@toa.io/extensions.exposition 1.0.0-alpha.286 → 1.0.0-alpha.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
- package/components/identity.clients/tsconfig.tsbuildinfo +1 -1
- package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
- package/components/identity.grants/tsconfig.tsbuildinfo +1 -1
- package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/manifest.toa.yaml +2 -1
- package/components/identity.passkeys/source/errors.test.ts +34 -0
- package/components/identity.passkeys/source/webauthn.test.ts +26 -0
- package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/types/toa.d.ts +1 -1
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/documentation/access.md +22 -4
- package/documentation/discovery.md +81 -0
- package/documentation/help.md +109 -0
- package/documentation/introspection.md +34 -9
- package/documentation/mcp.md +13 -16
- package/features/cors.feature +31 -1
- package/features/dev.feature +2 -0
- package/features/discovery.feature +211 -0
- package/features/discovery.ui.feature +197 -0
- package/features/help.feature +466 -0
- package/features/introspection.feature +94 -8
- package/features/map.feature +1 -0
- package/features/mcp.feature +74 -12
- package/features/methods.feature +2 -1
- package/features/oauth.grants.feature +3 -1
- package/features/octets.download.feature +1 -0
- package/features/octets.meta.feature +1 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/favicon.ico +0 -0
- package/features/site/index.html +10 -0
- package/features/steps/Parameters.ts +12 -0
- package/package.json +6 -4
- package/readme.md +3 -0
- package/source/Directive.ts +4 -0
- package/source/Discovery/Explorer.ts +34 -0
- package/source/Discovery/Site.test.ts +147 -0
- package/source/Discovery/Site.ts +202 -0
- package/source/Discovery/index.ts +3 -0
- package/source/Discovery/tree.test.ts +175 -0
- package/source/Discovery/tree.ts +77 -0
- package/source/Discovery/trunk.ts +23 -0
- package/source/Endpoint.ts +4 -0
- package/source/Gateway.ts +34 -4
- package/source/HTTP/Context.ts +7 -0
- package/source/Introspection.ts +33 -10
- package/source/MCP/schema.ts +16 -5
- package/source/MCP/tools.ts +14 -32
- package/source/Mapping.ts +5 -0
- package/source/Query.ts +17 -2
- package/source/RTD/Directives.ts +10 -0
- package/source/RTD/Endpoint.ts +8 -1
- package/source/RTD/Node.ts +20 -6
- package/source/RTD/Tree.ts +19 -9
- package/source/RTD/factory.ts +37 -1
- package/source/RTD/segment.ts +21 -0
- package/source/const.ts +8 -0
- package/source/directives/auth/Anonymous.test.ts +31 -5
- package/source/directives/auth/Anonymous.ts +5 -3
- package/source/directives/auth/Anyone.ts +6 -0
- package/source/directives/auth/Assert.ts +5 -0
- package/source/directives/auth/Delegate.ts +5 -2
- package/source/directives/auth/Federation.ts +11 -0
- package/source/directives/auth/Id.ts +15 -0
- package/source/directives/auth/Input.ts +5 -0
- package/source/directives/auth/Role.ts +17 -0
- package/source/directives/cors/CORS.test.ts +75 -0
- package/source/directives/cors/CORS.ts +10 -3
- package/source/directives/help/Family.ts +118 -0
- package/source/directives/help/Help.test.ts +227 -0
- package/source/directives/help/Help.ts +26 -0
- package/source/directives/help/Parameters.ts +47 -0
- package/source/directives/help/described.ts +57 -0
- package/source/directives/help/index.ts +45 -0
- package/source/directives/index.ts +8 -1
- package/source/directives/map/Headers.ts +6 -6
- package/source/directives/mcp/MCP.ts +12 -21
- package/source/directives/mcp/Tool.test.ts +22 -68
- package/source/directives/mcp/Tool.ts +14 -41
- package/transpiled/Directive.d.ts +1 -0
- package/transpiled/Directive.js +3 -0
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Discovery/Explorer.d.ts +11 -0
- package/transpiled/Discovery/Explorer.js +29 -0
- package/transpiled/Discovery/Explorer.js.map +1 -0
- package/transpiled/Discovery/Site.d.ts +43 -0
- package/transpiled/Discovery/Site.js +171 -0
- package/transpiled/Discovery/Site.js.map +1 -0
- package/transpiled/Discovery/index.d.ts +3 -0
- package/transpiled/Discovery/index.js +4 -0
- package/transpiled/Discovery/index.js.map +1 -0
- package/transpiled/Discovery/tree.d.ts +26 -0
- package/transpiled/Discovery/tree.js +50 -0
- package/transpiled/Discovery/tree.js.map +1 -0
- package/transpiled/Discovery/trunk.d.ts +10 -0
- package/transpiled/Discovery/trunk.js +21 -0
- package/transpiled/Discovery/trunk.js.map +1 -0
- package/transpiled/Endpoint.d.ts +2 -1
- package/transpiled/Endpoint.js +3 -0
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Gateway.d.ts +4 -0
- package/transpiled/Gateway.js +29 -4
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +6 -0
- package/transpiled/HTTP/Context.js +6 -0
- package/transpiled/HTTP/Context.js.map +1 -1
- package/transpiled/Introspection.d.ts +18 -7
- package/transpiled/Introspection.js +16 -1
- package/transpiled/Introspection.js.map +1 -1
- package/transpiled/MCP/schema.d.ts +2 -2
- package/transpiled/MCP/schema.js +11 -5
- package/transpiled/MCP/schema.js.map +1 -1
- package/transpiled/MCP/tools.d.ts +1 -1
- package/transpiled/MCP/tools.js +12 -28
- package/transpiled/MCP/tools.js.map +1 -1
- package/transpiled/Mapping.d.ts +2 -0
- package/transpiled/Mapping.js +4 -0
- package/transpiled/Mapping.js.map +1 -1
- package/transpiled/Query.d.ts +12 -0
- package/transpiled/Query.js +16 -2
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RTD/Directives.d.ts +8 -0
- package/transpiled/RTD/Endpoint.d.ts +7 -1
- package/transpiled/RTD/Node.d.ts +11 -2
- package/transpiled/RTD/Node.js +12 -2
- package/transpiled/RTD/Node.js.map +1 -1
- package/transpiled/RTD/Tree.js +15 -8
- package/transpiled/RTD/Tree.js.map +1 -1
- package/transpiled/RTD/factory.js +27 -1
- package/transpiled/RTD/factory.js.map +1 -1
- package/transpiled/RTD/segment.d.ts +7 -0
- package/transpiled/RTD/segment.js +17 -0
- package/transpiled/RTD/segment.js.map +1 -1
- package/transpiled/const.d.ts +7 -0
- package/transpiled/const.js +7 -0
- package/transpiled/const.js.map +1 -1
- package/transpiled/directives/auth/Anonymous.d.ts +4 -2
- package/transpiled/directives/auth/Anonymous.js +5 -3
- package/transpiled/directives/auth/Anonymous.js.map +1 -1
- package/transpiled/directives/auth/Anyone.d.ts +3 -0
- package/transpiled/directives/auth/Anyone.js +4 -0
- package/transpiled/directives/auth/Anyone.js.map +1 -1
- package/transpiled/directives/auth/Assert.d.ts +2 -0
- package/transpiled/directives/auth/Assert.js +4 -0
- package/transpiled/directives/auth/Assert.js.map +1 -1
- package/transpiled/directives/auth/Delegate.d.ts +4 -1
- package/transpiled/directives/auth/Delegate.js +5 -2
- package/transpiled/directives/auth/Delegate.js.map +1 -1
- package/transpiled/directives/auth/Federation.d.ts +5 -0
- package/transpiled/directives/auth/Federation.js +8 -0
- package/transpiled/directives/auth/Federation.js.map +1 -1
- package/transpiled/directives/auth/Id.d.ts +9 -0
- package/transpiled/directives/auth/Id.js +12 -0
- package/transpiled/directives/auth/Id.js.map +1 -1
- package/transpiled/directives/auth/Input.d.ts +2 -0
- package/transpiled/directives/auth/Input.js +4 -0
- package/transpiled/directives/auth/Input.js.map +1 -1
- package/transpiled/directives/auth/Role.d.ts +7 -0
- package/transpiled/directives/auth/Role.js +11 -0
- package/transpiled/directives/auth/Role.js.map +1 -1
- package/transpiled/directives/cors/CORS.js +9 -2
- package/transpiled/directives/cors/CORS.js.map +1 -1
- package/transpiled/directives/help/Family.d.ts +28 -0
- package/transpiled/directives/help/Family.js +79 -0
- package/transpiled/directives/help/Family.js.map +1 -0
- package/transpiled/directives/help/Help.d.ts +16 -0
- package/transpiled/directives/help/Help.js +22 -0
- package/transpiled/directives/help/Help.js.map +1 -0
- package/transpiled/directives/help/Parameters.d.ts +18 -0
- package/transpiled/directives/help/Parameters.js +34 -0
- package/transpiled/directives/help/Parameters.js.map +1 -0
- package/transpiled/directives/help/described.d.ts +16 -0
- package/transpiled/directives/help/described.js +23 -0
- package/transpiled/directives/help/described.js.map +1 -0
- package/transpiled/directives/help/index.d.ts +22 -0
- package/transpiled/directives/help/index.js +34 -0
- package/transpiled/directives/help/index.js.map +1 -0
- package/transpiled/directives/index.d.ts +4 -0
- package/transpiled/directives/index.js +8 -1
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/map/Headers.d.ts +5 -0
- package/transpiled/directives/map/Headers.js +7 -5
- package/transpiled/directives/map/Headers.js.map +1 -1
- package/transpiled/directives/mcp/MCP.d.ts +8 -5
- package/transpiled/directives/mcp/MCP.js +9 -13
- package/transpiled/directives/mcp/MCP.js.map +1 -1
- package/transpiled/directives/mcp/Tool.d.ts +6 -12
- package/transpiled/directives/mcp/Tool.js +10 -23
- package/transpiled/directives/mcp/Tool.js.map +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/ui/dist/_app/env.js +1 -0
- package/ui/dist/_app/immutable/assets/0.BcIGtTfw.css +2 -0
- package/ui/dist/_app/immutable/assets/2.BC0-Jhqu.css +1 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
- package/ui/dist/_app/immutable/chunks/BgTrzN6u.js +1 -0
- package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
- package/ui/dist/_app/immutable/chunks/CWJaG9mK.js +6 -0
- package/ui/dist/_app/immutable/chunks/DjIYL2zC.js +3 -0
- package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/ui/dist/_app/immutable/chunks/yDQhitF9.js +1 -0
- package/ui/dist/_app/immutable/entry/app.WZSMBcGf.js +2 -0
- package/ui/dist/_app/immutable/entry/start._LkoC6Lz.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.Dl2DqINo.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.CSSnGhVD.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.DmolQyNG.js +14 -0
- package/ui/dist/_app/version.json +1 -0
- package/ui/dist/apple-touch-icon.png +0 -0
- package/ui/dist/favicon-96x96.png +0 -0
- package/ui/dist/favicon.ico +0 -0
- package/ui/dist/index.html +66 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Resource discovery
|
|
2
|
+
|
|
3
|
+
Everything an application serves, at one path, and a page to read it with.
|
|
4
|
+
|
|
5
|
+
```http
|
|
6
|
+
OPTIONS /.discovery HTTP/1.1
|
|
7
|
+
accept: application/yaml
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```http
|
|
11
|
+
200 OK
|
|
12
|
+
Allow: GET, HEAD, OPTIONS
|
|
13
|
+
cache-control: private, max-age=1800
|
|
14
|
+
|
|
15
|
+
routes:
|
|
16
|
+
/pots:
|
|
17
|
+
title: Pots
|
|
18
|
+
GET:
|
|
19
|
+
title: Every pot
|
|
20
|
+
description: Every pot there is, newest first.
|
|
21
|
+
output:
|
|
22
|
+
type: array
|
|
23
|
+
items:
|
|
24
|
+
type: object
|
|
25
|
+
properties:
|
|
26
|
+
title: { type: string, maxLength: 64 }
|
|
27
|
+
POST:
|
|
28
|
+
input:
|
|
29
|
+
type: object
|
|
30
|
+
properties:
|
|
31
|
+
title: { type: string, maxLength: 64 }
|
|
32
|
+
required: [title]
|
|
33
|
+
errors:
|
|
34
|
+
- NO_WAY
|
|
35
|
+
/pots/:id:
|
|
36
|
+
protected: true
|
|
37
|
+
GET:
|
|
38
|
+
protected: true
|
|
39
|
+
route:
|
|
40
|
+
id: { title: Which pot }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`routes` is keyed by route template, and an object so that what is said of the whole tree has
|
|
44
|
+
somewhere to go. What a key maps to is what [`OPTIONS`](introspection.md) on that path
|
|
45
|
+
answers — what the resource is, from [`help:node`](help.md), beside every method of it — so a key
|
|
46
|
+
can be read from here and sent straight back as a request. A method this identity may not reach is
|
|
47
|
+
not in either, and a resource they may reach no method of is not here at all — so what a reader
|
|
48
|
+
sees is what they may call, and signing in is what adds to it.
|
|
49
|
+
|
|
50
|
+
A key carries no trailing slash, and a request needs one: `OPTIONS /pots/:id` and
|
|
51
|
+
`OPTIONS /pots/:id/` both answer, but `GET /pots/:id` is `404` and `GET /pots/:id/` is the call.
|
|
52
|
+
Append it.
|
|
53
|
+
|
|
54
|
+
Held for half an hour, and `private`: what is in it is what the identity that asked may reach.
|
|
55
|
+
|
|
56
|
+
## The page
|
|
57
|
+
|
|
58
|
+
`GET /.discovery/` is a page that reads the tree, served from the same origin the API is. It
|
|
59
|
+
answers before a credential is read, so it opens for a client whose token has expired — which is
|
|
60
|
+
the client most likely to be looking.
|
|
61
|
+
|
|
62
|
+
A method is called from it: the verb is a button, what the method takes is a form, and what came
|
|
63
|
+
back is shown where the form was. The call is made as whoever is reading — the same request they
|
|
64
|
+
would send themselves, and with the same effect.
|
|
65
|
+
|
|
66
|
+
A `GET /` whose `accept` prefers a page is sent here, so the address of the application opens it
|
|
67
|
+
in a browser. A route declared at `/` answers `/` as it always did; this is only what stands where
|
|
68
|
+
an application serves nothing there.
|
|
69
|
+
|
|
70
|
+
## What it is not
|
|
71
|
+
|
|
72
|
+
Neither is annotated, and both answer wherever a gateway does. What each entry says is what
|
|
73
|
+
`OPTIONS` on that path already says to the same caller, but the enumeration is not otherwise
|
|
74
|
+
available: an application that does not publish its routes to everyone refuses `/.discovery` at
|
|
75
|
+
the ingress.
|
|
76
|
+
|
|
77
|
+
## References
|
|
78
|
+
|
|
79
|
+
- [Introspection](introspection.md), which is what an entry is
|
|
80
|
+
- [Help](help.md), which is what names one
|
|
81
|
+
- [Features](../features/discovery.feature)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Help
|
|
2
|
+
|
|
3
|
+
What a resource and its methods are, in the words an application chooses for them. One
|
|
4
|
+
declaration, read by everything that describes a resource: [`OPTIONS`](introspection.md),
|
|
5
|
+
[discovery](discovery.md), and the tools [MCP](mcp.md) publishes.
|
|
6
|
+
|
|
7
|
+
```yaml
|
|
8
|
+
/pots:
|
|
9
|
+
help:node: Pots
|
|
10
|
+
GET:
|
|
11
|
+
help:method: Every pot
|
|
12
|
+
help:query:
|
|
13
|
+
criteria:
|
|
14
|
+
title: What to match
|
|
15
|
+
description: An RSQL expression over a pot's fields.
|
|
16
|
+
endpoint: enumerate
|
|
17
|
+
/:id:
|
|
18
|
+
GET:
|
|
19
|
+
help:method:
|
|
20
|
+
title: One pot
|
|
21
|
+
description: The pot by its id, and what is in it.
|
|
22
|
+
help:route:
|
|
23
|
+
id: Which pot
|
|
24
|
+
endpoint: observe
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
<dl>
|
|
28
|
+
<dt><code>help:node</code></dt>
|
|
29
|
+
<dd>What the resource is.</dd>
|
|
30
|
+
<dt><code>help:method</code></dt>
|
|
31
|
+
<dd>What one of its methods is.</dd>
|
|
32
|
+
<dt><code>help:route</code></dt>
|
|
33
|
+
<dd>What a route variable is, by the name the template gives it.</dd>
|
|
34
|
+
<dt><code>help:query</code></dt>
|
|
35
|
+
<dd>What a querystring parameter is, by name.</dd>
|
|
36
|
+
</dl>
|
|
37
|
+
|
|
38
|
+
All four take the same value. A bare one is the <code>title</code> — the short thing, and what a
|
|
39
|
+
client has somewhere to put. A sentence is written as a <code>description</code> beside it, and
|
|
40
|
+
either may stand alone. The parameters name each one they describe.
|
|
41
|
+
|
|
42
|
+
A title is not a name. A name is an address — `apps._identity._id.repos.POST` — and reads as one;
|
|
43
|
+
this is what a person is shown instead.
|
|
44
|
+
|
|
45
|
+
An operation [states what it is](/documentation/component/declaration.md) as well, and that is not
|
|
46
|
+
this: it is written without knowledge of any route, and a method is an operation and a route
|
|
47
|
+
together. The same operation mounted twice is two methods, and one sentence is not true of both.
|
|
48
|
+
The operation's own is the Introspection's to read, and the gateway does not use it.
|
|
49
|
+
|
|
50
|
+
## Where it is answered
|
|
51
|
+
|
|
52
|
+
`help:node` is answered beside the methods, and `help:method` inside the method it is on. A verb
|
|
53
|
+
is upper case, so neither key can be mistaken for the other. A parameter's goes in its schema,
|
|
54
|
+
which is where a schema says these anyway:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
title: Pots
|
|
58
|
+
GET:
|
|
59
|
+
title: Every pot
|
|
60
|
+
query:
|
|
61
|
+
criteria:
|
|
62
|
+
type: string
|
|
63
|
+
title: What to match
|
|
64
|
+
description: An RSQL expression over a pot's fields.
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Describe a parameter by the name the answer carries it under. `map:segments` answers a variable
|
|
68
|
+
under the property it fills, so that is its name here and the template's is nowhere in the answer.
|
|
69
|
+
|
|
70
|
+
A route variable the template names is answered whether or not the operation declares it — an `:id`
|
|
71
|
+
an observation takes through the query is otherwise not in the answer at all, and describing it is
|
|
72
|
+
what puts it there. Anything else is described only where it is already answered. A name that is
|
|
73
|
+
nobody's is not answered, and nothing here checks one: the template does not say them all.
|
|
74
|
+
|
|
75
|
+
`help:query` describes the [parameters a resource declares](query.md#parameters). What selects
|
|
76
|
+
records — `criteria`, `sort` and the rest — is the same of every queryable resource and says so
|
|
77
|
+
itself, so it is neither answered here nor described.
|
|
78
|
+
|
|
79
|
+
## What it is not inherited by
|
|
80
|
+
|
|
81
|
+
Every other directive applies to everything below the node it is on. This one does not: carried
|
|
82
|
+
downward, it would say of every resource below that it is the one it was written for.
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
/pots:
|
|
86
|
+
help:node: Pots # `/pots`, and not `/pots/:id`
|
|
87
|
+
GET: enumerate
|
|
88
|
+
/:id:
|
|
89
|
+
GET: observe
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
A node whose `/` answers in its place is the exception, because the two are one resource:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
/pots:
|
|
96
|
+
help:node: Pots # `/pots/` is what answers, and it is described
|
|
97
|
+
/:
|
|
98
|
+
GET: enumerate
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A resource is described beside what it serves, so a node that serves nothing cannot be described.
|
|
102
|
+
Declaring it there is refused where it is written, rather than found missing from the answer.
|
|
103
|
+
|
|
104
|
+
## References
|
|
105
|
+
|
|
106
|
+
- [Resource introspection](introspection.md), which is where a method's own is answered
|
|
107
|
+
- [Resource discovery](discovery.md), which answers every resource at once
|
|
108
|
+
- [MCP](mcp.md), where a published method is a tool called this
|
|
109
|
+
- [Features](../features/help.feature)
|
|
@@ -1,21 +1,46 @@
|
|
|
1
1
|
# Resource introspection
|
|
2
2
|
|
|
3
3
|
Any resource can be introspected by sending an `OPTIONS` request to the resource's path.
|
|
4
|
+
[Discovery](discovery.md) answers the same of every resource at once.
|
|
4
5
|
|
|
5
6
|
What it answers is what the route's directives leave of what the operation declared, for each
|
|
6
|
-
method
|
|
7
|
-
every method
|
|
7
|
+
method this identity may reach. A method they may not is not there, nor in `Allow`; a resource
|
|
8
|
+
whose every method they may not reach is `403`.
|
|
9
|
+
|
|
10
|
+
A credential does not narrow it. What refuses one at an [`anonymous`](access.md#anonymous) route
|
|
11
|
+
is about a reply a cache would hold, and a description is not one — so a route that is public is
|
|
12
|
+
described to whoever asks, as it is at `/.rpc` and `/.mcp`.
|
|
13
|
+
|
|
14
|
+
What the resource itself is, from [`help:node`](help.md), is answered beside the methods rather
|
|
15
|
+
than inside one. A verb is upper case, so neither key can be mistaken for the other. A resource
|
|
16
|
+
carries `private`, `protected` and `system` where any of its methods does.
|
|
8
17
|
|
|
9
18
|
Introspection properties:
|
|
10
19
|
|
|
11
|
-
- `description` what the route states this method is, from
|
|
12
|
-
The operation states what it is too, and that is not this: it is
|
|
13
|
-
any route, and the same operation mounted twice is two methods
|
|
14
|
-
- `
|
|
15
|
-
|
|
16
|
-
- `
|
|
20
|
+
- `title` and `description` what the route states this method is, from
|
|
21
|
+
[`help:method`](help.md). The operation states what it is too, and that is not this: it is
|
|
22
|
+
written without knowledge of any route, and the same operation mounted twice is two methods
|
|
23
|
+
- `authenticated` reaching it takes being someone, whoever — [`auth:anyone`](access.md#anyone),
|
|
24
|
+
`auth:delegate` and `auth:claims`
|
|
25
|
+
- `private` reaching it is being the identity it is about — [`auth:id`](access.md#id)
|
|
26
|
+
- `protected` reaching it takes a role — [`auth:role`](access.md#role)
|
|
27
|
+
- `system` and that role is one of the `system` scope, which guards what an application runs
|
|
28
|
+
on rather than what it serves
|
|
29
|
+
- `route` route parameters, including what `map:segments` names differently, each as
|
|
30
|
+
[`help:route`](help.md) describes it
|
|
31
|
+
- `query` the [query parameters](query.md#parameters) this resource declares, each as
|
|
32
|
+
[`help:query`](help.md) describes it. What selects records — `criteria`, `sort`, `limit`,
|
|
33
|
+
`omit`, `search` — is not among them: it is the same of every queryable resource, and so
|
|
34
|
+
not something one says about itself. A [procedure](rpc.md) carries it, because there it is
|
|
35
|
+
something the caller sends
|
|
17
36
|
- `input` input schema, restricted by `io:input`, without what the gateway fills itself
|
|
18
|
-
- `output` output schema, restricted by `io:output`; absent where the reply is not sent at all
|
|
37
|
+
- `output` output schema, restricted by `io:output`; absent where the reply is not sent at all.
|
|
38
|
+
An operation that declares none is described by what its type answers: a transition, an
|
|
39
|
+
observation and an assignment work on the Entity Object, so that is what they are said to
|
|
40
|
+
answer — a set of them where the scope is a set. A computation neither uses the scope nor
|
|
41
|
+
produces one, and an effect answers whatever it computed, so neither is guessed at. It is a
|
|
42
|
+
description and nothing is held to it — an operation answering a projection says so by
|
|
43
|
+
declaring its own `output`
|
|
19
44
|
- `errors` error codes
|
|
20
45
|
|
|
21
46
|
```http
|
package/documentation/mcp.md
CHANGED
|
@@ -32,12 +32,14 @@ A tool is an RTD method that says it is one, named as the [procedure](rpc.md#the
|
|
|
32
32
|
```yaml
|
|
33
33
|
/pots:
|
|
34
34
|
GET:
|
|
35
|
-
mcp:tool:
|
|
35
|
+
mcp:tool: true
|
|
36
|
+
help:method: Every pot there is, newest first.
|
|
36
37
|
endpoint: enumerate
|
|
37
38
|
/hot:
|
|
38
39
|
GET:
|
|
39
40
|
query: { criteria: temperature=gt=80 }
|
|
40
|
-
mcp:tool:
|
|
41
|
+
mcp:tool: true
|
|
42
|
+
help:method:
|
|
41
43
|
title: Hot pots
|
|
42
44
|
description: The pots that are too hot to pour.
|
|
43
45
|
endpoint: enumerate
|
|
@@ -47,21 +49,12 @@ A default denies, and a tree holds everything an application serves — its iden
|
|
|
47
49
|
uploads, the machinery of the authorization flow the model already came through. Publishing all of
|
|
48
50
|
it would spend a model's context on what it has no business calling.
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
`mcp:tool` says only whether the method is published. What the tool is called and what it is for is
|
|
53
|
+
[`help:method`](help.md), which is what describes the method everywhere else as well — a tool a
|
|
54
|
+
model cannot read the purpose of is one it cannot choose, so declare one beside the other.
|
|
52
55
|
|
|
53
|
-
A
|
|
54
|
-
|
|
55
|
-
description alone is written as the value; a title beside it is written as a mapping.
|
|
56
|
-
|
|
57
|
-
An operation [states what it is](/documentation/component/declaration.md) as well, and that is not
|
|
58
|
-
this: it is written without knowledge of any route, and a tool is an operation and a route together.
|
|
59
|
-
The two routes above are one operation and two tools, and one sentence is not true of both. The
|
|
60
|
-
operation's own is the Introspection's to read, and the gateway does not use it.
|
|
61
|
-
|
|
62
|
-
A declaration is inherited by everything below it, as every directive is, and the nearer one wins.
|
|
63
|
-
Since what it carries is what one method is, it belongs on a method: a node stating one would say
|
|
64
|
-
the same thing of everything under it.
|
|
56
|
+
A declaration is inherited by everything below it, as every directive is, and the nearer one wins —
|
|
57
|
+
so a node publishes a subtree, and `mcp:tool: false` withdraws one method of it.
|
|
65
58
|
|
|
66
59
|
A route whose name a client [cannot spell](rpc.md#what-has-a-name) is refused where the directive
|
|
67
60
|
is built, rather than served as a tool that is quietly never listed.
|
|
@@ -81,6 +74,10 @@ application did not publish is not reachable by guessing what it would have been
|
|
|
81
74
|
querystring under `query`, and what is left is the body. `io:input` restricts it, and a property
|
|
82
75
|
`map` fills is not there — a call carries no headers of its own.
|
|
83
76
|
|
|
77
|
+
`query` carries what selects records — `criteria`, `sort`, `limit`, `omit`, `search` — beside the
|
|
78
|
+
parameters the resource declares. `OPTIONS` states only the latter, because the former is the same
|
|
79
|
+
of every queryable resource; here it is stated, because here it is something the caller sends.
|
|
80
|
+
|
|
84
81
|
```yaml
|
|
85
82
|
type: object
|
|
86
83
|
properties:
|
package/features/cors.feature
CHANGED
|
@@ -13,6 +13,7 @@ Feature: CORS Support
|
|
|
13
13
|
When the following request is received:
|
|
14
14
|
"""
|
|
15
15
|
OPTIONS / HTTP/1.1
|
|
16
|
+
access-control-request-method: GET
|
|
16
17
|
host: nex.toa.io
|
|
17
18
|
origin: https://hello.world
|
|
18
19
|
"""
|
|
@@ -21,7 +22,7 @@ Feature: CORS Support
|
|
|
21
22
|
204 No Content
|
|
22
23
|
access-control-allow-credentials: true
|
|
23
24
|
access-control-allow-headers: accept, authorization, content-type, if-match, if-none-match
|
|
24
|
-
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, LOCK, UNLOCK
|
|
25
|
+
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, LOCK, UNLOCK, OPTIONS
|
|
25
26
|
access-control-allow-origin: https://hello.world
|
|
26
27
|
access-control-max-age: 3600
|
|
27
28
|
cache-control: max-age=3600
|
|
@@ -42,6 +43,35 @@ Feature: CORS Support
|
|
|
42
43
|
vary: origin
|
|
43
44
|
"""
|
|
44
45
|
|
|
46
|
+
Scenario: An OPTIONS that is not a preflight
|
|
47
|
+
A preflight is `OPTIONS` carrying `Access-Control-Request-Method`, and only that. A
|
|
48
|
+
browser puts `Origin` on every request whose method is not `GET` or `HEAD`, its own
|
|
49
|
+
`OPTIONS` included — so one answered as a preflight is a resource no page can introspect.
|
|
50
|
+
|
|
51
|
+
Given the annotation:
|
|
52
|
+
"""yaml
|
|
53
|
+
/:
|
|
54
|
+
anonymous: true
|
|
55
|
+
/foo:
|
|
56
|
+
io:output: true
|
|
57
|
+
GET:
|
|
58
|
+
dev:stub: Hello
|
|
59
|
+
"""
|
|
60
|
+
When the following request is received:
|
|
61
|
+
"""
|
|
62
|
+
OPTIONS /foo/ HTTP/1.1
|
|
63
|
+
host: nex.toa.io
|
|
64
|
+
origin: https://hello.world
|
|
65
|
+
accept: application/yaml
|
|
66
|
+
"""
|
|
67
|
+
Then the following reply is sent:
|
|
68
|
+
"""
|
|
69
|
+
200 OK
|
|
70
|
+
access-control-allow-origin: https://hello.world
|
|
71
|
+
Allow: GET
|
|
72
|
+
vary: origin
|
|
73
|
+
"""
|
|
74
|
+
|
|
45
75
|
Scenario: Always vary CORS
|
|
46
76
|
Given the annotation:
|
|
47
77
|
"""yaml
|
package/features/dev.feature
CHANGED
|
@@ -18,6 +18,7 @@ Feature: Dev
|
|
|
18
18
|
When the following request is received:
|
|
19
19
|
"""
|
|
20
20
|
OPTIONS / HTTP/1.1
|
|
21
|
+
access-control-request-method: GET
|
|
21
22
|
origin: http://example.com
|
|
22
23
|
"""
|
|
23
24
|
Then the following reply is sent:
|
|
@@ -70,6 +71,7 @@ Feature: Dev
|
|
|
70
71
|
When the following request is received:
|
|
71
72
|
"""
|
|
72
73
|
OPTIONS / HTTP/1.1
|
|
74
|
+
access-control-request-method: GET
|
|
73
75
|
origin: http://example.com
|
|
74
76
|
"""
|
|
75
77
|
Then the following reply is sent:
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
Feature: Resource discovery
|
|
2
|
+
|
|
3
|
+
Every route an application serves, at one path, described as `OPTIONS` on each of them
|
|
4
|
+
describes it.
|
|
5
|
+
|
|
6
|
+
Scenario: What an application serves
|
|
7
|
+
Given the `pots` is running with the following manifest:
|
|
8
|
+
"""yaml
|
|
9
|
+
exposition:
|
|
10
|
+
/:
|
|
11
|
+
io:output: [id, title]
|
|
12
|
+
GET: enumerate
|
|
13
|
+
POST: create
|
|
14
|
+
/:id:
|
|
15
|
+
GET: observe
|
|
16
|
+
"""
|
|
17
|
+
When the following request is received:
|
|
18
|
+
"""
|
|
19
|
+
OPTIONS /.discovery HTTP/1.1
|
|
20
|
+
host: nex.toa.io
|
|
21
|
+
accept: application/yaml
|
|
22
|
+
"""
|
|
23
|
+
Then the following reply is sent:
|
|
24
|
+
"""
|
|
25
|
+
200 OK
|
|
26
|
+
Allow: GET, HEAD, OPTIONS
|
|
27
|
+
cache-control: private, max-age=1800
|
|
28
|
+
|
|
29
|
+
routes:
|
|
30
|
+
/pots:
|
|
31
|
+
GET:
|
|
32
|
+
output:
|
|
33
|
+
type: array
|
|
34
|
+
items:
|
|
35
|
+
properties:
|
|
36
|
+
id:
|
|
37
|
+
type: string
|
|
38
|
+
title:
|
|
39
|
+
type: string
|
|
40
|
+
POST:
|
|
41
|
+
input:
|
|
42
|
+
properties:
|
|
43
|
+
title:
|
|
44
|
+
type: string
|
|
45
|
+
/pots/:id:
|
|
46
|
+
GET:
|
|
47
|
+
output:
|
|
48
|
+
properties:
|
|
49
|
+
id:
|
|
50
|
+
type: string
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
Scenario: A key is a request that can be made
|
|
54
|
+
What each entry says is what `OPTIONS` on that key says, so a client reads the tree
|
|
55
|
+
once and addresses any of it by the key it was given.
|
|
56
|
+
|
|
57
|
+
Given the `echo` is running with the following manifest:
|
|
58
|
+
"""yaml
|
|
59
|
+
exposition:
|
|
60
|
+
/:a:
|
|
61
|
+
io:output: true
|
|
62
|
+
PATCH: parameters
|
|
63
|
+
"""
|
|
64
|
+
When the following request is received:
|
|
65
|
+
"""
|
|
66
|
+
OPTIONS /.discovery HTTP/1.1
|
|
67
|
+
host: nex.toa.io
|
|
68
|
+
accept: application/yaml
|
|
69
|
+
"""
|
|
70
|
+
Then the following reply is sent:
|
|
71
|
+
"""
|
|
72
|
+
200 OK
|
|
73
|
+
|
|
74
|
+
routes:
|
|
75
|
+
/echo/:a:
|
|
76
|
+
PATCH:
|
|
77
|
+
route:
|
|
78
|
+
a:
|
|
79
|
+
type: string
|
|
80
|
+
input:
|
|
81
|
+
type: object
|
|
82
|
+
properties:
|
|
83
|
+
b:
|
|
84
|
+
type: string
|
|
85
|
+
"""
|
|
86
|
+
When the following request is received:
|
|
87
|
+
"""
|
|
88
|
+
OPTIONS /echo/:a/ HTTP/1.1
|
|
89
|
+
host: nex.toa.io
|
|
90
|
+
accept: application/yaml
|
|
91
|
+
"""
|
|
92
|
+
Then the following reply is sent:
|
|
93
|
+
"""
|
|
94
|
+
200 OK
|
|
95
|
+
Allow: PATCH
|
|
96
|
+
|
|
97
|
+
PATCH:
|
|
98
|
+
route:
|
|
99
|
+
a:
|
|
100
|
+
type: string
|
|
101
|
+
input:
|
|
102
|
+
type: object
|
|
103
|
+
properties:
|
|
104
|
+
b:
|
|
105
|
+
type: string
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
Scenario: A resource this caller may reach no method of is not there
|
|
109
|
+
`OPTIONS` on one answers `403`, and a tree cannot refuse a single entry — so it is
|
|
110
|
+
omitted, rather than named as something that exists and is not for them.
|
|
111
|
+
|
|
112
|
+
Given the annotation:
|
|
113
|
+
"""yaml
|
|
114
|
+
/:
|
|
115
|
+
/open:
|
|
116
|
+
anonymous: true
|
|
117
|
+
GET:
|
|
118
|
+
dev:stub: hello
|
|
119
|
+
/closed:
|
|
120
|
+
auth:role: admin
|
|
121
|
+
GET:
|
|
122
|
+
dev:stub: secret
|
|
123
|
+
"""
|
|
124
|
+
When the following request is received:
|
|
125
|
+
"""
|
|
126
|
+
OPTIONS /.discovery HTTP/1.1
|
|
127
|
+
host: nex.toa.io
|
|
128
|
+
accept: application/yaml
|
|
129
|
+
"""
|
|
130
|
+
Then the following reply is sent:
|
|
131
|
+
"""
|
|
132
|
+
200 OK
|
|
133
|
+
|
|
134
|
+
routes:
|
|
135
|
+
/open:
|
|
136
|
+
GET: {}
|
|
137
|
+
"""
|
|
138
|
+
And the reply does not contain:
|
|
139
|
+
"""
|
|
140
|
+
/closed:
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
Scenario: Only the verbs this caller may reach
|
|
144
|
+
A resource is described by what is left of it, not dropped for the part that is not
|
|
145
|
+
theirs.
|
|
146
|
+
|
|
147
|
+
Given the `pots` is running with the following manifest:
|
|
148
|
+
"""yaml
|
|
149
|
+
exposition:
|
|
150
|
+
/:
|
|
151
|
+
isolated: true
|
|
152
|
+
io:output: [id, title]
|
|
153
|
+
GET:
|
|
154
|
+
anonymous: true
|
|
155
|
+
endpoint: enumerate
|
|
156
|
+
POST:
|
|
157
|
+
auth:role: admin
|
|
158
|
+
endpoint: create
|
|
159
|
+
"""
|
|
160
|
+
When the following request is received:
|
|
161
|
+
"""
|
|
162
|
+
OPTIONS /.discovery HTTP/1.1
|
|
163
|
+
host: nex.toa.io
|
|
164
|
+
accept: application/yaml
|
|
165
|
+
"""
|
|
166
|
+
Then the following reply is sent:
|
|
167
|
+
"""
|
|
168
|
+
200 OK
|
|
169
|
+
|
|
170
|
+
routes:
|
|
171
|
+
/pots:
|
|
172
|
+
GET:
|
|
173
|
+
"""
|
|
174
|
+
And the reply does not contain:
|
|
175
|
+
"""
|
|
176
|
+
temperature
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
Scenario: A credential does not hide what is anonymous
|
|
180
|
+
A credential refuses an `anonymous` route because it would make the reply uncacheable,
|
|
181
|
+
and a description is not that reply. Without this an identity would be shown less of
|
|
182
|
+
the tree than someone presenting nothing at all.
|
|
183
|
+
|
|
184
|
+
Given the `identity.basic` database contains:
|
|
185
|
+
# developer:secret
|
|
186
|
+
| _id | authority | username | password |
|
|
187
|
+
| efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
|
|
188
|
+
And the `identity.bans` database is empty
|
|
189
|
+
And the annotation:
|
|
190
|
+
"""yaml
|
|
191
|
+
/:
|
|
192
|
+
/pots:
|
|
193
|
+
anonymous: true
|
|
194
|
+
GET:
|
|
195
|
+
dev:stub: hello
|
|
196
|
+
"""
|
|
197
|
+
When the following request is received:
|
|
198
|
+
"""
|
|
199
|
+
OPTIONS /.discovery HTTP/1.1
|
|
200
|
+
host: nex.toa.io
|
|
201
|
+
authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
|
|
202
|
+
accept: application/yaml
|
|
203
|
+
"""
|
|
204
|
+
Then the following reply is sent:
|
|
205
|
+
"""
|
|
206
|
+
200 OK
|
|
207
|
+
|
|
208
|
+
routes:
|
|
209
|
+
/pots:
|
|
210
|
+
GET: {}
|
|
211
|
+
"""
|