@toa.io/extensions.exposition 1.0.0-alpha.291 → 1.0.0-alpha.292
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 +7 -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/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/features/steps/Gateway.ts +1 -2
- package/package.json +6 -5
- package/source/Discovery/Explorer.ts +1 -1
- package/source/Discovery/Site.ts +1 -1
- package/source/Discovery/trunk.ts +1 -1
- package/source/Factory.ts +1 -1
- package/source/Gateway.ts +1 -1
- package/source/HTTP/Server.ts +7 -2
- package/source/MCP/Server.ts +2 -2
- package/source/MCP/discover.ts +1 -1
- package/source/Query.ts +1 -1
- package/source/RPC/Dispatcher.ts +3 -3
- package/source/RTD/Tree.ts +1 -1
- package/source/RTD/factory.ts +1 -1
- package/source/RTD/syntax/index.ts +1 -2
- package/source/Tenant.ts +1 -1
- package/source/directives/auth/Authorization.ts +1 -1
- package/source/directives/io/IO.ts +1 -1
- package/source/directives/oauth/documents.ts +1 -1
- package/source/index.ts +0 -2
- package/source/service.ts +3 -2
- package/transpiled/Discovery/Explorer.js +1 -1
- package/transpiled/Discovery/Explorer.js.map +1 -1
- package/transpiled/Discovery/Site.js +1 -1
- package/transpiled/Discovery/Site.js.map +1 -1
- package/transpiled/Discovery/trunk.js +1 -1
- package/transpiled/Discovery/trunk.js.map +1 -1
- package/transpiled/Factory.js +1 -1
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Gateway.js +1 -1
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Server.d.ts +1 -1
- package/transpiled/HTTP/Server.js +1 -1
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/MCP/Server.d.ts +1 -1
- package/transpiled/MCP/Server.js +1 -1
- package/transpiled/MCP/Server.js.map +1 -1
- package/transpiled/MCP/discover.d.ts +1 -1
- package/transpiled/Query.js +1 -1
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RPC/Dispatcher.d.ts +1 -1
- package/transpiled/RPC/Dispatcher.js +2 -2
- package/transpiled/RPC/Dispatcher.js.map +1 -1
- package/transpiled/RTD/Tree.js +1 -1
- package/transpiled/RTD/Tree.js.map +1 -1
- package/transpiled/RTD/factory.js +1 -1
- package/transpiled/RTD/factory.js.map +1 -1
- package/transpiled/RTD/syntax/index.d.ts +1 -2
- package/transpiled/RTD/syntax/index.js +1 -2
- package/transpiled/RTD/syntax/index.js.map +1 -1
- package/transpiled/Tenant.js +1 -1
- package/transpiled/Tenant.js.map +1 -1
- package/transpiled/directives/auth/Authorization.js +1 -1
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/io/IO.js +1 -1
- package/transpiled/directives/io/IO.js.map +1 -1
- package/transpiled/directives/oauth/documents.d.ts +1 -1
- package/transpiled/index.d.ts +0 -2
- package/transpiled/index.js +0 -2
- package/transpiled/index.js.map +1 -1
- package/transpiled/service.js +1 -1
- package/transpiled/service.js.map +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/ui/dist/_app/immutable/chunks/BaWe3aOV.js +1 -0
- package/ui/dist/_app/immutable/chunks/{B_pK3U1C.js → GZTfV0ks.js} +2 -2
- package/ui/dist/_app/immutable/chunks/{D9tE1yyO.js → aLpTDmOc.js} +1 -1
- package/ui/dist/_app/immutable/entry/{app.DMqmL4NY.js → app.D5v4JoUx.js} +2 -2
- package/ui/dist/_app/immutable/entry/start.CWxFoIyZ.js +1 -0
- package/ui/dist/_app/immutable/nodes/{0.D9m-6cbM.js → 0.DIh5T_5-.js} +1 -1
- package/ui/dist/_app/immutable/nodes/{1.Cgi_RcSO.js → 1.CY-bFzHC.js} +1 -1
- package/ui/dist/_app/immutable/nodes/{2.D_Cbxgz4.js → 2.6Ubd1ItY.js} +1 -1
- package/ui/dist/_app/version.json +1 -1
- package/ui/dist/index.html +10 -10
- package/schemas/annotation.cos.yaml +0 -125
- package/schemas/call.cos.yaml +0 -18
- package/schemas/directive.cos.yaml +0 -7
- package/schemas/method.cos.yaml +0 -23
- package/schemas/node.cos.yaml +0 -22
- package/schemas/query.cos.yaml +0 -31
- package/schemas/querystring.cos.yaml +0 -19
- package/schemas/range.cos.yaml +0 -10
- package/schemas/route.cos.yaml +0 -6
- package/source/Annotation.ts +0 -89
- package/source/RTD/syntax/parse.test.ts +0 -196
- package/source/RTD/syntax/parse.ts +0 -171
- package/source/RTD/syntax/types.ts +0 -69
- package/source/const.ts +0 -45
- package/source/deployment.ts +0 -96
- package/source/manifest.test.ts +0 -56
- package/source/manifest.ts +0 -56
- package/source/schemas.test.ts +0 -31
- package/source/schemas.ts +0 -15
- package/source/shortcuts.ts +0 -14
- package/transpiled/Annotation.d.ts +0 -70
- package/transpiled/Annotation.js +0 -2
- package/transpiled/Annotation.js.map +0 -1
- package/transpiled/RTD/syntax/parse.d.ts +0 -4
- package/transpiled/RTD/syntax/parse.js +0 -112
- package/transpiled/RTD/syntax/parse.js.map +0 -1
- package/transpiled/RTD/syntax/types.d.ts +0 -52
- package/transpiled/RTD/syntax/types.js +0 -11
- package/transpiled/RTD/syntax/types.js.map +0 -1
- package/transpiled/const.d.ts +0 -36
- package/transpiled/const.js +0 -37
- package/transpiled/const.js.map +0 -1
- package/transpiled/deployment.d.ts +0 -6
- package/transpiled/deployment.js +0 -76
- package/transpiled/deployment.js.map +0 -1
- package/transpiled/manifest.d.ts +0 -3
- package/transpiled/manifest.js +0 -39
- package/transpiled/manifest.js.map +0 -1
- package/transpiled/schemas.d.ts +0 -9
- package/transpiled/schemas.js +0 -9
- package/transpiled/schemas.js.map +0 -1
- package/transpiled/shortcuts.d.ts +0 -3
- package/transpiled/shortcuts.js +0 -13
- package/transpiled/shortcuts.js.map +0 -1
- package/ui/dist/_app/immutable/chunks/CiKbPLyY.js +0 -1
- package/ui/dist/_app/immutable/entry/start.CRe7HWBI.js +0 -1
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import * as schemas from '../../schemas.js'
|
|
2
|
-
import {
|
|
3
|
-
verbs,
|
|
4
|
-
type Node,
|
|
5
|
-
type Route,
|
|
6
|
-
type Method,
|
|
7
|
-
type Mapping,
|
|
8
|
-
type Directive,
|
|
9
|
-
type Range
|
|
10
|
-
} from './types.js'
|
|
11
|
-
|
|
12
|
-
export function parse(input: object, shortcuts?: Shortcuts): Node {
|
|
13
|
-
const node = parseNode(input, shortcuts)
|
|
14
|
-
|
|
15
|
-
schemas.node.validate(node)
|
|
16
|
-
|
|
17
|
-
return node
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function parseNode(input: object | string, shortcuts?: Shortcuts): Node {
|
|
21
|
-
const node = createNode()
|
|
22
|
-
|
|
23
|
-
if (typeof input === 'string') {
|
|
24
|
-
node.forward = input
|
|
25
|
-
|
|
26
|
-
return node
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
for (const [key, value] of Object.entries(input) as Array<[keyof Node, unknown]>)
|
|
30
|
-
switch (key) {
|
|
31
|
-
case 'protected':
|
|
32
|
-
case 'isolated':
|
|
33
|
-
node[key] = value as boolean
|
|
34
|
-
break
|
|
35
|
-
case 'forward':
|
|
36
|
-
node[key] = value as string
|
|
37
|
-
break
|
|
38
|
-
|
|
39
|
-
default:
|
|
40
|
-
// eslint-disable-next-line max-depth
|
|
41
|
-
if (key[0] === '/') {
|
|
42
|
-
const route = parseRoute(key, value as Node, shortcuts)
|
|
43
|
-
|
|
44
|
-
node.routes.push(route)
|
|
45
|
-
|
|
46
|
-
continue
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// eslint-disable-next-line max-depth
|
|
50
|
-
if (verbs.has(key)) {
|
|
51
|
-
const method = parseMethod(key, value as Mapping, shortcuts)
|
|
52
|
-
|
|
53
|
-
node.methods.push(method)
|
|
54
|
-
|
|
55
|
-
continue
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// eslint-disable-next-line no-case-declarations
|
|
59
|
-
const directive = parseDirective(key, value, shortcuts)
|
|
60
|
-
|
|
61
|
-
// eslint-disable-next-line max-depth
|
|
62
|
-
if (directive !== null) {
|
|
63
|
-
node.directives.push(directive)
|
|
64
|
-
|
|
65
|
-
continue
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
throw new Error(`RTD parse error: unknown key '${key}'`)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return node
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function createNode(): Node {
|
|
75
|
-
return {
|
|
76
|
-
routes: [],
|
|
77
|
-
methods: [],
|
|
78
|
-
directives: []
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function parseRoute(path: string, value: Node, shortcuts?: Shortcuts): Route {
|
|
83
|
-
const node = parse(value, shortcuts)
|
|
84
|
-
|
|
85
|
-
return createRoute(path, node)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function createRoute(path: string, node: Node): Route {
|
|
89
|
-
return { path, node }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function parseMethod(
|
|
93
|
-
verb: string,
|
|
94
|
-
value: Mapping | string,
|
|
95
|
-
shortcuts?: Shortcuts
|
|
96
|
-
): Method {
|
|
97
|
-
const mapping = typeof value === 'string' ? { endpoint: value } : value
|
|
98
|
-
|
|
99
|
-
parseEndpoint(mapping)
|
|
100
|
-
parseQuery(mapping)
|
|
101
|
-
|
|
102
|
-
const directives = parseDirectives(mapping, shortcuts)
|
|
103
|
-
|
|
104
|
-
return { verb, mapping, directives }
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function parseEndpoint(mapping: Mapping): void {
|
|
108
|
-
if (mapping.endpoint === undefined) return
|
|
109
|
-
|
|
110
|
-
const [endpoiont, component, namespace] = mapping.endpoint.split('.').reverse()
|
|
111
|
-
|
|
112
|
-
if (component !== undefined) {
|
|
113
|
-
mapping.component = component
|
|
114
|
-
mapping.namespace = namespace ?? mapping.namespace ?? 'default'
|
|
115
|
-
mapping.endpoint = endpoiont
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function parseQuery(mapping: any): void {
|
|
120
|
-
const query = mapping.query
|
|
121
|
-
|
|
122
|
-
if (query === undefined || query === null) return
|
|
123
|
-
|
|
124
|
-
if (typeof query.limit === 'number') query.limit = expandRange(query.limit as number)
|
|
125
|
-
|
|
126
|
-
if (typeof query.omit === 'number') query.omit = expandRange(query.omit as number)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function parseDirectives(
|
|
130
|
-
mapping: Record<string, any>,
|
|
131
|
-
shortcuts?: Shortcuts
|
|
132
|
-
): Directive[] {
|
|
133
|
-
const directives: Directive[] = []
|
|
134
|
-
|
|
135
|
-
for (const [key, value] of Object.entries(mapping)) {
|
|
136
|
-
const directive = parseDirective(key, value, shortcuts)
|
|
137
|
-
|
|
138
|
-
if (directive === null) continue
|
|
139
|
-
|
|
140
|
-
directives.push(directive)
|
|
141
|
-
|
|
142
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
143
|
-
delete mapping[key]
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return directives
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function parseDirective(
|
|
150
|
-
key: string,
|
|
151
|
-
value: any,
|
|
152
|
-
shortcuts?: Shortcuts
|
|
153
|
-
): Directive | null {
|
|
154
|
-
if (shortcuts?.has(key) === true) key = shortcuts.get(key)! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
|
155
|
-
|
|
156
|
-
const match = key.match(DIRECTIVE_RX)
|
|
157
|
-
|
|
158
|
-
if (match === null) return null
|
|
159
|
-
|
|
160
|
-
const { family, name } = match.groups as { family: string; name: string }
|
|
161
|
-
|
|
162
|
-
return { family, name, value }
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function expandRange(range: number): Range {
|
|
166
|
-
return { value: range, range: [range, range] }
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const DIRECTIVE_RX = /^(?<family>\w{1,32}):(?<name>\w{1,32})$/
|
|
170
|
-
|
|
171
|
-
export type Shortcuts = Map<string, string>
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
export interface Node {
|
|
2
|
-
protected?: boolean
|
|
3
|
-
isolated?: boolean
|
|
4
|
-
forward?: string
|
|
5
|
-
routes: Route[]
|
|
6
|
-
methods: Method[]
|
|
7
|
-
directives: Directive[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface Route {
|
|
11
|
-
path: string
|
|
12
|
-
node: Node
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface Method {
|
|
16
|
-
verb: string
|
|
17
|
-
mapping?: Mapping
|
|
18
|
-
directives: Directive[]
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Directive {
|
|
22
|
-
family: string
|
|
23
|
-
name: string
|
|
24
|
-
value: any
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface Mapping {
|
|
28
|
-
namespace?: string
|
|
29
|
-
component?: string
|
|
30
|
-
endpoint: string
|
|
31
|
-
query?: Query | null
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Whether the operation this maps to answers a page of a collection, and so takes the
|
|
35
|
-
* `omit` and `limit` a page is taken by. Written where the component's manifest is read,
|
|
36
|
-
* beside `namespace` and `component`, and not declared.
|
|
37
|
-
*/
|
|
38
|
-
paged?: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface Query {
|
|
42
|
-
id?: string
|
|
43
|
-
criteria?: string
|
|
44
|
-
search?: boolean
|
|
45
|
-
sample?: number
|
|
46
|
-
sort?: string
|
|
47
|
-
omit?: Range
|
|
48
|
-
limit?: Range
|
|
49
|
-
selectors?: string[]
|
|
50
|
-
projection?: string[]
|
|
51
|
-
parameters?: string[]
|
|
52
|
-
deleted?: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface Range {
|
|
56
|
-
value?: number
|
|
57
|
-
range: [number, number]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const verbs = new Set<string>([
|
|
61
|
-
'GET',
|
|
62
|
-
'HEAD',
|
|
63
|
-
'POST',
|
|
64
|
-
'PUT',
|
|
65
|
-
'PATCH',
|
|
66
|
-
'DELETE',
|
|
67
|
-
'LOCK',
|
|
68
|
-
'UNLOCK'
|
|
69
|
-
])
|
package/source/const.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export const BRANCH_TTL = 1_800_000
|
|
2
|
-
|
|
3
|
-
/** The broadcast the gateway and its tenants share. */
|
|
4
|
-
export const CHANNEL = 'exposition'
|
|
5
|
-
|
|
6
|
-
/** The replicas of the gateway decide together, whatever context they serve. */
|
|
7
|
-
export const ATOM_GROUP = 'exposition'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Where JSON-RPC is served, for every procedure at once. Pinned rather than configured: a
|
|
11
|
-
* path an application could choose is a path it could collide with a route of its own.
|
|
12
|
-
*/
|
|
13
|
-
export const RPC = '/.rpc'
|
|
14
|
-
|
|
15
|
-
/** Calls one request may carry where the annotation does not say. */
|
|
16
|
-
export const BATCH = 32
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Where the Model Context Protocol is served. Pinned for the reason `/.rpc` is: a path an
|
|
20
|
-
* application could choose is a path it could collide with a route of its own.
|
|
21
|
-
*/
|
|
22
|
-
export const MCP = '/.mcp'
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Where the resource tree and the page that reads it are served. Pinned for the reason
|
|
26
|
-
* `/.rpc` is: a path an application could choose is a path it could collide with a route of
|
|
27
|
-
* its own. Unlike those two it is not annotated — every entry it carries is what `OPTIONS`
|
|
28
|
-
* on that path already answers to the same caller, and what it adds is the enumeration.
|
|
29
|
-
*/
|
|
30
|
-
export const DISCOVERY = '/.discovery'
|
|
31
|
-
|
|
32
|
-
/** Where the gateway serves. */
|
|
33
|
-
export const PORT = 8000
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Reserved for the readiness probe. `8001` is the Telemetry readiness probe's, and `toa export`
|
|
37
|
-
* refuses a port claimed twice — `toa mono` and a local run put every service in one process.
|
|
38
|
-
*/
|
|
39
|
-
export const PROBE = 8004
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The initial delay of the readiness probe. The server does not sleep for it: whoever
|
|
43
|
-
* probes is the one that waits, and doing it here as well only delayed the process twice.
|
|
44
|
-
*/
|
|
45
|
-
export const DELAY = 3 // seconds
|
package/source/deployment.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import assert from 'node:assert'
|
|
3
|
-
import { type Dependency, type Service } from '@toa.io/operations'
|
|
4
|
-
import { type Annotation } from './Annotation.js'
|
|
5
|
-
import * as schemas from './schemas.js'
|
|
6
|
-
import { shortcuts } from './shortcuts.js'
|
|
7
|
-
import { components } from './Composition.js'
|
|
8
|
-
import { parse } from './RTD/syntax/index.js'
|
|
9
|
-
import { DELAY, PORT, PROBE } from './const.js'
|
|
10
|
-
|
|
11
|
-
/** Where Toa's release publishes this service's image. An application takes it
|
|
12
|
-
* instead of building one when its context says `registry.services: published`. */
|
|
13
|
-
export const image = 'ghcr.io/toa-io/extension-exposition-gateway'
|
|
14
|
-
|
|
15
|
-
export function deployment(_: unknown, annotation?: Annotation): Dependency {
|
|
16
|
-
assert.ok(annotation !== undefined, 'Exposition context annotation is required')
|
|
17
|
-
schemas.annotation.validate(annotation)
|
|
18
|
-
|
|
19
|
-
const labels = components().labels
|
|
20
|
-
|
|
21
|
-
const service: Service = {
|
|
22
|
-
group: 'exposition',
|
|
23
|
-
name: 'gateway',
|
|
24
|
-
image,
|
|
25
|
-
port: PORT,
|
|
26
|
-
version: JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
27
|
-
.version,
|
|
28
|
-
variables: [],
|
|
29
|
-
components: labels,
|
|
30
|
-
resources: annotation.resources,
|
|
31
|
-
ingress: { path: '/', hosts: [] },
|
|
32
|
-
probe: {
|
|
33
|
-
path: '/.ready',
|
|
34
|
-
port: PROBE,
|
|
35
|
-
delay: DELAY
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (annotation?.['/'] !== undefined) {
|
|
40
|
-
const tree = parse(annotation['/'], shortcuts)
|
|
41
|
-
|
|
42
|
-
service.variables!.push({
|
|
43
|
-
name: 'TOA_EXPOSITION',
|
|
44
|
-
value: JSON.stringify(tree)
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const { debug, authorities } = annotation
|
|
49
|
-
|
|
50
|
-
service.ingress!.hosts = Object.values(authorities)
|
|
51
|
-
|
|
52
|
-
// leaving these undefined lets the context's own ingress section supply them
|
|
53
|
-
if (annotation.class !== undefined) service.ingress!.class = annotation.class
|
|
54
|
-
|
|
55
|
-
if (annotation.annotations !== undefined)
|
|
56
|
-
service.ingress!.annotations = annotation.annotations
|
|
57
|
-
|
|
58
|
-
if (annotation.service?.annotations !== undefined)
|
|
59
|
-
service.annotations = annotation.service.annotations
|
|
60
|
-
|
|
61
|
-
const properties: Properties = { authorities }
|
|
62
|
-
|
|
63
|
-
if (debug === true) properties.debug = true
|
|
64
|
-
|
|
65
|
-
if (annotation.protocol !== undefined) properties.protocol = annotation.protocol
|
|
66
|
-
|
|
67
|
-
if (annotation.ip !== undefined) properties.ip = annotation.ip
|
|
68
|
-
|
|
69
|
-
if (annotation.bouncer !== undefined) properties.bouncer = annotation.bouncer
|
|
70
|
-
|
|
71
|
-
if (annotation.oauth !== undefined) properties.oauth = annotation.oauth
|
|
72
|
-
|
|
73
|
-
if (annotation.rpc !== undefined) properties.rpc = annotation.rpc
|
|
74
|
-
|
|
75
|
-
if (annotation.mcp !== undefined) properties.mcp = annotation.mcp
|
|
76
|
-
|
|
77
|
-
service.variables!.push({
|
|
78
|
-
name: 'TOA_EXPOSITION_PROPERTIES',
|
|
79
|
-
value: JSON.stringify(properties)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
// The identity composition nested in this process connects before route discovery settles,
|
|
83
|
-
// so telemetry's probe — which tracks that composition — would report ready too early.
|
|
84
|
-
// The gateway answers for itself, on the same port telemetry would have used.
|
|
85
|
-
service.variables!.push({
|
|
86
|
-
name: 'TOA_TELEMETRY_READY',
|
|
87
|
-
value: JSON.stringify(false)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
return { services: [service] }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
type Properties = Pick<
|
|
94
|
-
Annotation,
|
|
95
|
-
'authorities' | 'debug' | 'protocol' | 'bouncer' | 'ip' | 'oauth' | 'rpc' | 'mcp'
|
|
96
|
-
>
|
package/source/manifest.test.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { it, beforeEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import { type Manifest } from '@toa.io/norm'
|
|
5
|
-
import { generate } from 'randomstring'
|
|
6
|
-
import { manifest } from './manifest.js'
|
|
7
|
-
|
|
8
|
-
const name = 'cm-' + generate()
|
|
9
|
-
const namespace = 'ns' + generate()
|
|
10
|
-
|
|
11
|
-
let mf: Manifest
|
|
12
|
-
|
|
13
|
-
const operations = { observe: {} }
|
|
14
|
-
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
mf = { namespace, name, operations } as unknown as Manifest
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const declaration = {
|
|
20
|
-
'/': { GET: 'observe' }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
it('should create branch', async () => {
|
|
24
|
-
const node = manifest(declaration, mf)
|
|
25
|
-
|
|
26
|
-
assert.notStrictEqual(node, undefined)
|
|
27
|
-
assert.strictEqual(node.routes.length, 1)
|
|
28
|
-
assert.strictEqual(node.routes[0].path, '/' + namespace + '/' + name)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('should not create node for default namespace', async () => {
|
|
32
|
-
mf.namespace = 'default'
|
|
33
|
-
|
|
34
|
-
const node = manifest(declaration, mf)
|
|
35
|
-
|
|
36
|
-
assert.strictEqual(node.routes.length, 1)
|
|
37
|
-
assert.strictEqual(node.routes[0].path, '/' + name)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should throw on invalid declaration type', async () => {
|
|
41
|
-
assert.throws(
|
|
42
|
-
() => manifest('hello' as unknown as object, mf),
|
|
43
|
-
(error: any) => /Exposition declaration must be an object/.test(error.message)
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should set namespace and component', async () => {
|
|
48
|
-
const node = manifest(declaration, mf)
|
|
49
|
-
|
|
50
|
-
const root = node.routes[0].node
|
|
51
|
-
const intemediate = root.routes[0].node
|
|
52
|
-
const GET = intemediate.methods[0]
|
|
53
|
-
|
|
54
|
-
assert.strictEqual(GET.mapping?.namespace, namespace)
|
|
55
|
-
assert.strictEqual(GET.mapping?.component, name)
|
|
56
|
-
})
|
package/source/manifest.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { parse, type Node, type Method, type Query } from './RTD/syntax/index.js'
|
|
3
|
-
import { shortcuts } from './shortcuts.js'
|
|
4
|
-
import * as schemas from './schemas.js'
|
|
5
|
-
import type { Manifest } from '@toa.io/norm'
|
|
6
|
-
|
|
7
|
-
export function manifest(declaration: object, manifest: Manifest): Node {
|
|
8
|
-
assert.ok(
|
|
9
|
-
typeof declaration === 'object' && declaration !== null,
|
|
10
|
-
'Exposition declaration must be an object'
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
declaration = wrap(declaration, manifest.namespace, manifest.name)
|
|
14
|
-
|
|
15
|
-
const node = parse(declaration, shortcuts)
|
|
16
|
-
|
|
17
|
-
specify(node, manifest)
|
|
18
|
-
schemas.node.validate(node)
|
|
19
|
-
|
|
20
|
-
return node
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function wrap(declaration: object, namespace: string, name: string): object {
|
|
24
|
-
const path =
|
|
25
|
-
(namespace === undefined || namespace === 'default' ? '' : '/' + namespace) +
|
|
26
|
-
'/' +
|
|
27
|
-
name
|
|
28
|
-
|
|
29
|
-
return { [path]: declaration }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function specify(node: Node, manifest: Manifest): void {
|
|
33
|
-
for (const route of node.routes) {
|
|
34
|
-
for (const method of route.node.methods) specifyMethod(method, manifest)
|
|
35
|
-
|
|
36
|
-
specify(route.node, manifest)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function specifyMethod(method: Method, manifest: Manifest): void {
|
|
41
|
-
if (method.mapping?.endpoint === undefined) return
|
|
42
|
-
|
|
43
|
-
const operation = manifest.operations[method.mapping.endpoint]
|
|
44
|
-
|
|
45
|
-
assert.ok(operation !== undefined, `Operation '${method.mapping.endpoint}' not found`)
|
|
46
|
-
|
|
47
|
-
if (method.mapping.query === undefined)
|
|
48
|
-
method.mapping.query = operation.query === false ? null : ({} as unknown as Query)
|
|
49
|
-
|
|
50
|
-
// a page is taken of a collection, and of nothing else: an operation that answers one object
|
|
51
|
-
// takes no `omit` and no `limit`, and refuses a request that carries them
|
|
52
|
-
method.mapping.paged = operation.type === 'observation' && operation.scope === 'objects'
|
|
53
|
-
|
|
54
|
-
method.mapping.namespace = manifest.namespace
|
|
55
|
-
method.mapping.component = manifest.name
|
|
56
|
-
}
|
package/source/schemas.test.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import * as schemas from './schemas.js'
|
|
5
|
-
|
|
6
|
-
const authorities = { nex: 'nex.toa.io' }
|
|
7
|
-
|
|
8
|
-
it('should require ip with bouncer', () => {
|
|
9
|
-
assert.throws(() => schemas.annotation.validate({ authorities, bouncer: {} }))
|
|
10
|
-
assert.doesNotThrow(() =>
|
|
11
|
-
schemas.annotation.validate({ authorities, bouncer: {}, ip: 'x-real-ip' })
|
|
12
|
-
)
|
|
13
|
-
assert.doesNotThrow(() => schemas.annotation.validate({ authorities, ip: 'x-real-ip' }))
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('should default nothing in bouncer', () => {
|
|
17
|
-
assert.doesNotThrow(() =>
|
|
18
|
-
schemas.annotation.validate({
|
|
19
|
-
authorities,
|
|
20
|
-
ip: 'x-real-ip',
|
|
21
|
-
bouncer: { attempts: 5 }
|
|
22
|
-
})
|
|
23
|
-
)
|
|
24
|
-
assert.throws(() =>
|
|
25
|
-
schemas.annotation.validate({
|
|
26
|
-
authorities,
|
|
27
|
-
ip: 'x-real-ip',
|
|
28
|
-
bouncer: { attempts: 0 }
|
|
29
|
-
})
|
|
30
|
-
)
|
|
31
|
-
})
|
package/source/schemas.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import * as schemas from '@toa.io/schemas'
|
|
3
|
-
import type { Query } from './HTTP/index.js'
|
|
4
|
-
import type { Node } from './RTD/index.js'
|
|
5
|
-
import type { Call } from './RPC/types.js'
|
|
6
|
-
import type { Schema } from '@toa.io/schemas'
|
|
7
|
-
import type { Annotation } from './Annotation.js'
|
|
8
|
-
|
|
9
|
-
const path = resolve(import.meta.dirname, '../schemas')
|
|
10
|
-
const namespace = schemas.namespace(path)
|
|
11
|
-
|
|
12
|
-
export const querystring: Schema<Query> = namespace.schema('querystring')
|
|
13
|
-
export const annotation: Schema<Annotation> = namespace.schema('annotation')
|
|
14
|
-
export const node: Schema<Node> = namespace.schema('node')
|
|
15
|
-
export const call: Schema<Call> = namespace.schema('call')
|
package/source/shortcuts.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Shortcuts } from './RTD/syntax/index.js'
|
|
2
|
-
|
|
3
|
-
/** What a directive may be written as, where the family is implied. */
|
|
4
|
-
export const shortcuts: Shortcuts = new Map([
|
|
5
|
-
['anonymous', 'auth:anonymous'],
|
|
6
|
-
['anyone', 'auth:anyone'],
|
|
7
|
-
['id', 'auth:id'],
|
|
8
|
-
['role', 'auth:role'],
|
|
9
|
-
['rule', 'auth:rule'],
|
|
10
|
-
['incept', 'auth:incept'],
|
|
11
|
-
['input', 'io:input'],
|
|
12
|
-
['output', 'io:output'],
|
|
13
|
-
['languages', 'map:languages']
|
|
14
|
-
])
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { Resources } from '@toa.io/operations';
|
|
2
|
-
import type { Protocol } from './HTTP/index.js';
|
|
3
|
-
export interface Annotation {
|
|
4
|
-
authorities: Record<string, string>;
|
|
5
|
-
/**
|
|
6
|
-
* `h2c` requires an ingress controller that proxies cleartext HTTP/2 upstream.
|
|
7
|
-
* See `documentation/protocol.md`.
|
|
8
|
-
*/
|
|
9
|
-
protocol?: Protocol;
|
|
10
|
-
class?: string;
|
|
11
|
-
resources?: Resources;
|
|
12
|
-
annotations?: Record<string, string>;
|
|
13
|
-
/** The Service, as opposed to the Ingress that `annotations` above describes. */
|
|
14
|
-
service?: {
|
|
15
|
-
annotations?: Record<string, string>;
|
|
16
|
-
};
|
|
17
|
-
debug?: boolean;
|
|
18
|
-
/** The header the client address is read from; the connection's without one. See `ip.md`. */
|
|
19
|
-
ip?: string;
|
|
20
|
-
/** Failed authentications an address may make; none are metered unless set. See `identity.md`. */
|
|
21
|
-
bouncer?: Bouncer;
|
|
22
|
-
/** The authorization server this context exposes; none is exposed without it. */
|
|
23
|
-
oauth?: OAuth;
|
|
24
|
-
/** JSON-RPC at `/.rpc`; none is served without it. See `documentation/rpc.md`. */
|
|
25
|
-
rpc?: RPC;
|
|
26
|
-
/** MCP at `/.mcp`; none is served without it. See `documentation/mcp.md`. */
|
|
27
|
-
mcp?: MCP;
|
|
28
|
-
'/'?: object;
|
|
29
|
-
}
|
|
30
|
-
export interface OAuth {
|
|
31
|
-
/**
|
|
32
|
-
* Where a client sends the user to consent. The one endpoint of the flow that needs a
|
|
33
|
-
* person, and so the one this extension does not serve: an application builds it.
|
|
34
|
-
*/
|
|
35
|
-
authorize: string;
|
|
36
|
-
/** Paths a token may be restricted to, each advertised as a protected resource. */
|
|
37
|
-
resources?: string[];
|
|
38
|
-
/** What a client may ask for. Each is a role, or a scope within one. */
|
|
39
|
-
scopes?: string[];
|
|
40
|
-
/** Origins whose Client ID Metadata Documents may be read. None are without it. */
|
|
41
|
-
trust?: string[];
|
|
42
|
-
/** Dynamic Client Registration, off unless opened. */
|
|
43
|
-
registration?: 'open' | 'closed';
|
|
44
|
-
}
|
|
45
|
-
export interface RPC {
|
|
46
|
-
/**
|
|
47
|
-
* Calls one request may carry, 32 by default. One request is one authentication and one
|
|
48
|
-
* reply however many calls it makes, so what a caller may ask for at once is bounded.
|
|
49
|
-
*/
|
|
50
|
-
batch?: number;
|
|
51
|
-
}
|
|
52
|
-
export interface MCP {
|
|
53
|
-
/** What the server calls itself, which a client shows a user. */
|
|
54
|
-
name: string;
|
|
55
|
-
/** What a model is told before it calls anything here. */
|
|
56
|
-
instructions?: string;
|
|
57
|
-
/**
|
|
58
|
-
* Browser origins the endpoint answers, which the revision requires against DNS rebinding.
|
|
59
|
-
* None are admitted without it; a request carrying no `Origin` is not a browser's.
|
|
60
|
-
*/
|
|
61
|
-
origins?: string[];
|
|
62
|
-
/** Whether the endpoint answers without a credential. It does not unless it says so. */
|
|
63
|
-
anonymous?: boolean;
|
|
64
|
-
}
|
|
65
|
-
export interface Bouncer {
|
|
66
|
-
/** what an address may fail at once, 20 by default */
|
|
67
|
-
attempts?: number;
|
|
68
|
-
/** seconds it takes to earn them back, 60 by default */
|
|
69
|
-
interval?: number;
|
|
70
|
-
}
|
package/transpiled/Annotation.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Annotation.js","sourceRoot":"","sources":["../source/Annotation.ts"],"names":[],"mappings":""}
|