@toa.io/definitions 1.0.0-alpha.292 → 1.0.0-alpha.294
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/digest/extensions.configuration.json +1 -1
- package/digest/extensions.exposition.json +1 -1
- package/package.json +24 -7
- package/readme.md +1 -0
- package/transpiled/bridges.node/exports.js +25 -2
- package/transpiled/bridges.node/exports.js.map +1 -1
- package/transpiled/digest/generate.js +23 -3
- package/transpiled/digest/generate.js.map +1 -1
- package/transpiled/digest/read.d.ts +8 -0
- package/transpiled/digest/read.js +11 -0
- package/transpiled/digest/read.js.map +1 -1
- package/transpiled/extensions.configuration/deployment.js +4 -2
- package/transpiled/extensions.configuration/deployment.js.map +1 -1
- package/transpiled/extensions.configuration/index.d.ts +1 -0
- package/transpiled/extensions.configuration/index.js +1 -0
- package/transpiled/extensions.configuration/index.js.map +1 -1
- package/transpiled/extensions.configuration/secrets.d.ts +4 -0
- package/transpiled/extensions.configuration/secrets.js +42 -0
- package/transpiled/extensions.configuration/secrets.js.map +1 -0
- package/transpiled/extensions.exposition/index.d.ts +1 -0
- package/transpiled/extensions.exposition/index.js +1 -0
- package/transpiled/extensions.exposition/index.js.map +1 -1
- package/transpiled/extensions.exposition/packages.d.ts +11 -0
- package/transpiled/extensions.exposition/packages.js +17 -0
- package/transpiled/extensions.exposition/packages.js.map +1 -0
- package/transpiled/extensions.storages/index.d.ts +1 -0
- package/transpiled/extensions.storages/index.js +1 -0
- package/transpiled/extensions.storages/index.js.map +1 -1
- package/transpiled/extensions.storages/packages.d.ts +19 -0
- package/transpiled/extensions.storages/packages.js +41 -0
- package/transpiled/extensions.storages/packages.js.map +1 -0
- package/CHANGELOG.md +0 -13
- package/source/bindings.amqp/annotation.ts +0 -26
- package/source/bindings.amqp/context.ts +0 -68
- package/source/bindings.amqp/deployment.ts +0 -14
- package/source/bindings.amqp/index.ts +0 -9
- package/source/bindings.amqp/instance.ts +0 -3
- package/source/bindings.amqp/sources.ts +0 -39
- package/source/bindings.loop/index.ts +0 -3
- package/source/bridges.bash/const.ts +0 -2
- package/source/bridges.bash/define.ts +0 -38
- package/source/bridges.bash/index.ts +0 -2
- package/source/bridges.node/define.ts +0 -69
- package/source/bridges.node/exports.ts +0 -175
- package/source/bridges.node/index.ts +0 -6
- package/source/bridges.node/operations.ts +0 -127
- package/source/bridges.node/sources.ts +0 -44
- package/source/definition.ts +0 -38
- package/source/digest/generate.ts +0 -77
- package/source/digest/read.ts +0 -39
- package/source/extensions.cadence/components.ts +0 -6
- package/source/extensions.cadence/const.ts +0 -31
- package/source/extensions.cadence/context.ts +0 -10
- package/source/extensions.cadence/deployment.ts +0 -37
- package/source/extensions.cadence/index.ts +0 -8
- package/source/extensions.cadence/manifest.ts +0 -64
- package/source/extensions.cadence/schemas.ts +0 -10
- package/source/extensions.cadence/types.ts +0 -41
- package/source/extensions.configuration/components.ts +0 -6
- package/source/extensions.configuration/const.ts +0 -30
- package/source/extensions.configuration/context.ts +0 -13
- package/source/extensions.configuration/deployment.ts +0 -159
- package/source/extensions.configuration/epoch.ts +0 -22
- package/source/extensions.configuration/index.ts +0 -10
- package/source/extensions.configuration/manifest.ts +0 -12
- package/source/extensions.configuration/schemas.ts +0 -9
- package/source/extensions.exposition/Annotation.ts +0 -91
- package/source/extensions.exposition/components.ts +0 -6
- package/source/extensions.exposition/const.ts +0 -45
- package/source/extensions.exposition/deployment.ts +0 -95
- package/source/extensions.exposition/index.ts +0 -16
- package/source/extensions.exposition/manifest.ts +0 -56
- package/source/extensions.exposition/schemas.ts +0 -16
- package/source/extensions.exposition/shortcuts.ts +0 -14
- package/source/extensions.exposition/syntax/index.ts +0 -2
- package/source/extensions.exposition/syntax/parse.ts +0 -171
- package/source/extensions.exposition/syntax/types.ts +0 -69
- package/source/extensions.fetch/context.ts +0 -10
- package/source/extensions.fetch/index.ts +0 -1
- package/source/extensions.introspection/annotation.ts +0 -87
- package/source/extensions.introspection/components.ts +0 -13
- package/source/extensions.introspection/const.ts +0 -46
- package/source/extensions.introspection/deployment.ts +0 -48
- package/source/extensions.introspection/index.ts +0 -6
- package/source/extensions.introspection/manifest.ts +0 -18
- package/source/extensions.introspection/schemas.ts +0 -12
- package/source/extensions.realtime/components.ts +0 -5
- package/source/extensions.realtime/deployment.ts +0 -89
- package/source/extensions.realtime/index.ts +0 -4
- package/source/extensions.stash/context.ts +0 -10
- package/source/extensions.stash/deployment.ts +0 -22
- package/source/extensions.stash/index.ts +0 -2
- package/source/extensions.state/context.ts +0 -9
- package/source/extensions.state/index.ts +0 -1
- package/source/extensions.storages/Annotation.ts +0 -57
- package/source/extensions.storages/context.ts +0 -14
- package/source/extensions.storages/deployment.ts +0 -91
- package/source/extensions.storages/index.ts +0 -9
- package/source/extensions.storages/manifest.ts +0 -7
- package/source/extensions.storages/schemas.ts +0 -15
- package/source/extensions.storages/secrets.ts +0 -22
- package/source/extensions.telemetry/const.ts +0 -6
- package/source/extensions.telemetry/context.ts +0 -11
- package/source/extensions.telemetry/deployment.ts +0 -91
- package/source/extensions.telemetry/index.ts +0 -5
- package/source/extensions.telemetry/ready.ts +0 -33
- package/source/index.ts +0 -2
- package/source/storages.mongodb/deployment.ts +0 -22
- package/source/storages.mongodb/index.ts +0 -1
- package/source/version.ts +0 -10
- package/transpiled/bindings.amqp/annotation.test.d.ts +0 -1
- package/transpiled/bindings.amqp/annotation.test.js +0 -36
- package/transpiled/bindings.amqp/annotation.test.js.map +0 -1
- package/transpiled/bindings.amqp/properties.test.d.ts +0 -1
- package/transpiled/bindings.amqp/properties.test.js +0 -7
- package/transpiled/bindings.amqp/properties.test.js.map +0 -1
- package/transpiled/bindings.loop/properties.test.d.ts +0 -1
- package/transpiled/bindings.loop/properties.test.js +0 -7
- package/transpiled/bindings.loop/properties.test.js.map +0 -1
- package/transpiled/bridges.node/exports.test.d.ts +0 -1
- package/transpiled/bridges.node/exports.test.js +0 -88
- package/transpiled/bridges.node/exports.test.js.map +0 -1
- package/transpiled/bridges.node/operations.test.d.ts +0 -1
- package/transpiled/bridges.node/operations.test.js +0 -108
- package/transpiled/bridges.node/operations.test.js.map +0 -1
- package/transpiled/extensions.cadence/manifest.test.d.ts +0 -1
- package/transpiled/extensions.cadence/manifest.test.js +0 -41
- package/transpiled/extensions.cadence/manifest.test.js.map +0 -1
- package/transpiled/extensions.configuration/deployment.test.d.ts +0 -1
- package/transpiled/extensions.configuration/deployment.test.js +0 -74
- package/transpiled/extensions.configuration/deployment.test.js.map +0 -1
- package/transpiled/extensions.configuration/manifest.test.d.ts +0 -1
- package/transpiled/extensions.configuration/manifest.test.js +0 -14
- package/transpiled/extensions.configuration/manifest.test.js.map +0 -1
- package/transpiled/extensions.exposition/manifest.test.d.ts +0 -1
- package/transpiled/extensions.exposition/manifest.test.js +0 -38
- package/transpiled/extensions.exposition/manifest.test.js.map +0 -1
- package/transpiled/extensions.exposition/schemas.test.d.ts +0 -1
- package/transpiled/extensions.exposition/schemas.test.js +0 -22
- package/transpiled/extensions.exposition/schemas.test.js.map +0 -1
- package/transpiled/extensions.exposition/syntax/parse.test.d.ts +0 -1
- package/transpiled/extensions.exposition/syntax/parse.test.js +0 -156
- package/transpiled/extensions.exposition/syntax/parse.test.js.map +0 -1
- package/transpiled/extensions.storages/schemas.test.d.ts +0 -1
- package/transpiled/extensions.storages/schemas.test.js +0 -59
- package/transpiled/extensions.storages/schemas.test.js.map +0 -1
- package/tsconfig.json +0 -10
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { type Dependency, type Service } from '@toa.io/operations'
|
|
3
|
-
import { type Annotation } from './Annotation.js'
|
|
4
|
-
import * as schemas from './schemas.js'
|
|
5
|
-
import { shortcuts } from './shortcuts.js'
|
|
6
|
-
import { components } from './components.js'
|
|
7
|
-
import { parse } from './syntax/index.js'
|
|
8
|
-
import { DELAY, PORT, PROBE } from './const.js'
|
|
9
|
-
import { version } from '../version.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,
|
|
27
|
-
variables: [],
|
|
28
|
-
components: labels,
|
|
29
|
-
resources: annotation.resources,
|
|
30
|
-
ingress: { path: '/', hosts: [] },
|
|
31
|
-
probe: {
|
|
32
|
-
path: '/.ready',
|
|
33
|
-
port: PROBE,
|
|
34
|
-
delay: DELAY
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (annotation?.['/'] !== undefined) {
|
|
39
|
-
const tree = parse(annotation['/'], shortcuts)
|
|
40
|
-
|
|
41
|
-
service.variables!.push({
|
|
42
|
-
name: 'TOA_EXPOSITION',
|
|
43
|
-
value: JSON.stringify(tree)
|
|
44
|
-
})
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const { debug, authorities } = annotation
|
|
48
|
-
|
|
49
|
-
service.ingress!.hosts = Object.values(authorities)
|
|
50
|
-
|
|
51
|
-
// leaving these undefined lets the context's own ingress section supply them
|
|
52
|
-
if (annotation.class !== undefined) service.ingress!.class = annotation.class
|
|
53
|
-
|
|
54
|
-
if (annotation.annotations !== undefined)
|
|
55
|
-
service.ingress!.annotations = annotation.annotations
|
|
56
|
-
|
|
57
|
-
if (annotation.service?.annotations !== undefined)
|
|
58
|
-
service.annotations = annotation.service.annotations
|
|
59
|
-
|
|
60
|
-
const properties: Properties = { authorities }
|
|
61
|
-
|
|
62
|
-
if (debug === true) properties.debug = true
|
|
63
|
-
|
|
64
|
-
if (annotation.protocol !== undefined) properties.protocol = annotation.protocol
|
|
65
|
-
|
|
66
|
-
if (annotation.ip !== undefined) properties.ip = annotation.ip
|
|
67
|
-
|
|
68
|
-
if (annotation.bouncer !== undefined) properties.bouncer = annotation.bouncer
|
|
69
|
-
|
|
70
|
-
if (annotation.oauth !== undefined) properties.oauth = annotation.oauth
|
|
71
|
-
|
|
72
|
-
if (annotation.rpc !== undefined) properties.rpc = annotation.rpc
|
|
73
|
-
|
|
74
|
-
if (annotation.mcp !== undefined) properties.mcp = annotation.mcp
|
|
75
|
-
|
|
76
|
-
service.variables!.push({
|
|
77
|
-
name: 'TOA_EXPOSITION_PROPERTIES',
|
|
78
|
-
value: JSON.stringify(properties)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// The identity composition nested in this process connects before route discovery settles,
|
|
82
|
-
// so telemetry's probe — which tracks that composition — would report ready too early.
|
|
83
|
-
// The gateway answers for itself, on the same port telemetry would have used.
|
|
84
|
-
service.variables!.push({
|
|
85
|
-
name: 'TOA_TELEMETRY_READY',
|
|
86
|
-
value: JSON.stringify(false)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
return { services: [service] }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
type Properties = Pick<
|
|
93
|
-
Annotation,
|
|
94
|
-
'authorities' | 'debug' | 'protocol' | 'bouncer' | 'ip' | 'oauth' | 'rpc' | 'mcp'
|
|
95
|
-
>
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export { manifest } from './manifest.js'
|
|
2
|
-
export { components } from './components.js'
|
|
3
|
-
export { deployment, image } from './deployment.js'
|
|
4
|
-
export { shortcuts } from './shortcuts.js'
|
|
5
|
-
export * from './const.js'
|
|
6
|
-
export * as schemas from './schemas.js'
|
|
7
|
-
export * as syntax from './syntax/index.js'
|
|
8
|
-
|
|
9
|
-
export type {
|
|
10
|
-
Annotation,
|
|
11
|
-
Bouncer,
|
|
12
|
-
MCP as MCPAnnotation,
|
|
13
|
-
OAuth,
|
|
14
|
-
Protocol,
|
|
15
|
-
RPC as RPCAnnotation
|
|
16
|
-
} from './Annotation.js'
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { parse, type Node, type Method, type Query } from './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
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import * as schemas from '@toa.io/schemas'
|
|
3
|
-
import type { Schema } from '@toa.io/schemas'
|
|
4
|
-
import type { Node } from './syntax/types.js'
|
|
5
|
-
import type { Annotation } from './Annotation.js'
|
|
6
|
-
|
|
7
|
-
const path = resolve(import.meta.dirname, '../../schemas/extensions.exposition')
|
|
8
|
-
const namespace = schemas.namespace(path)
|
|
9
|
-
|
|
10
|
-
// what the gateway validates a request by, kept beside what a manifest is validated by
|
|
11
|
-
export const querystring: Schema<Record<string, unknown>> =
|
|
12
|
-
namespace.schema('querystring')
|
|
13
|
-
export const call: Schema<Record<string, unknown>> = namespace.schema('call')
|
|
14
|
-
|
|
15
|
-
export const annotation: Schema<Annotation> = namespace.schema('annotation')
|
|
16
|
-
export const node: Schema<Node> = namespace.schema('node')
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Shortcuts } from './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,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
|
-
])
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Contribution } from '@toa.io/core/types'
|
|
2
|
-
|
|
3
|
-
/** What this extension puts on the context of every component, being declared for all. */
|
|
4
|
-
export function context(): Contribution {
|
|
5
|
-
return {
|
|
6
|
-
name: 'fetch',
|
|
7
|
-
type: '(input: string | URL | Request, init?: FetchInit) => Promise<Response>',
|
|
8
|
-
imports: { '@toa.io/extensions.fetch': ['FetchInit'] }
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { context } from './context.js'
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_INTERVAL, DEFAULT_THRESHOLD, DENIED, ENV } from './const.js'
|
|
2
|
-
import { environment as variables } from '@toa.io/generic'
|
|
3
|
-
import type { Resources } from '@toa.io/operations'
|
|
4
|
-
|
|
5
|
-
/** `context.toa.yaml` */
|
|
6
|
-
export type Annotation =
|
|
7
|
-
| false
|
|
8
|
-
| {
|
|
9
|
-
/** Capture real payloads. Off by default: this is production data. */
|
|
10
|
-
samples?: boolean
|
|
11
|
-
/** Flush period, seconds. */
|
|
12
|
-
interval?: number
|
|
13
|
-
/** Flush as soon as this many distinct edges are buffered. */
|
|
14
|
-
threshold?: number
|
|
15
|
-
/** Publish the UI. On by default. */
|
|
16
|
-
ui?: boolean
|
|
17
|
-
resources?: Resources
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** `manifest.toa.yaml` */
|
|
21
|
-
export type Declaration =
|
|
22
|
-
| false
|
|
23
|
-
| {
|
|
24
|
-
samples?: boolean
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** What `deployment()` encodes into the environment. */
|
|
28
|
-
export interface Options {
|
|
29
|
-
samples: boolean
|
|
30
|
-
interval: number
|
|
31
|
-
threshold: number
|
|
32
|
-
ui: boolean
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** The effective per-component decision. */
|
|
36
|
-
export interface Settings {
|
|
37
|
-
enabled: boolean
|
|
38
|
-
samples: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const DISABLED: Settings = { enabled: false, samples: false }
|
|
42
|
-
|
|
43
|
-
export function options(annotation?: Annotation): Options {
|
|
44
|
-
const declaration = annotation === undefined || annotation === false ? {} : annotation
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
samples: declaration.samples === true,
|
|
48
|
-
interval: declaration.interval ?? DEFAULT_INTERVAL,
|
|
49
|
-
threshold: declaration.threshold ?? DEFAULT_THRESHOLD,
|
|
50
|
-
ui: declaration.ui !== false
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Reads what `deployment()` has put into the environment. */
|
|
55
|
-
export function environment(): Options | null {
|
|
56
|
-
const value = variables.get(ENV)
|
|
57
|
-
|
|
58
|
-
if (value === undefined) return null
|
|
59
|
-
|
|
60
|
-
return JSON.parse(value) as Options
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function component(declaration: Declaration | null | undefined): Declaration {
|
|
64
|
-
if (declaration === false) return false
|
|
65
|
-
|
|
66
|
-
// predefined extensions arrive as null for components that say nothing
|
|
67
|
-
if (declaration === null || declaration === undefined) return {}
|
|
68
|
-
|
|
69
|
-
return declaration.samples === undefined ? {} : { samples: declaration.samples }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Both levels must agree, and either can veto: the context is the environment
|
|
74
|
-
* ceiling, the manifest is the component's own call. A component handling
|
|
75
|
-
* personal data opts out for good, and no context flag overrides that.
|
|
76
|
-
*/
|
|
77
|
-
export function settings(
|
|
78
|
-
namespace: string,
|
|
79
|
-
declaration: Declaration,
|
|
80
|
-
opts: Options | null
|
|
81
|
-
): Settings {
|
|
82
|
-
if (opts === null || declaration === false) return DISABLED
|
|
83
|
-
|
|
84
|
-
const samples = opts.samples && declaration.samples !== false && !DENIED.has(namespace)
|
|
85
|
-
|
|
86
|
-
return { enabled: true, samples }
|
|
87
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { components as digest, type Components } from '../digest/read.js'
|
|
2
|
-
import type { Annotation } from './annotation.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The extension is predefined, so an application that turns introspection off
|
|
6
|
-
* must not end up with the explorer components — nor with the exposition
|
|
7
|
-
* dependency they bring in.
|
|
8
|
-
*/
|
|
9
|
-
export function components(annotation?: Annotation): Components {
|
|
10
|
-
if (annotation === false) return { labels: [], manifests: [] }
|
|
11
|
-
|
|
12
|
-
return digest('extensions.introspection')
|
|
13
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { environment } from '@toa.io/generic'
|
|
2
|
-
|
|
3
|
-
export const ID = 'introspection'
|
|
4
|
-
export const NAMESPACE = 'introspection'
|
|
5
|
-
|
|
6
|
-
export const ENV = 'TOA_INTROSPECTION'
|
|
7
|
-
|
|
8
|
-
export const NODES = 'nodes'
|
|
9
|
-
export const EDGES = 'edges'
|
|
10
|
-
|
|
11
|
-
/** Where the UI is mounted; `/introspection/*` belongs to the components' own API. */
|
|
12
|
-
export const UI_PATH = '/.introspection'
|
|
13
|
-
export const UI_PORT = 8002
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* The port the UI is served on. `UI_PORT` is what a deployment publishes and what the chart
|
|
17
|
-
* renders; the variable is for a machine that runs more than one Toa process — a Toa checkout
|
|
18
|
-
* beside an application, or two applications — where one of them has to give it up.
|
|
19
|
-
*/
|
|
20
|
-
export const UI_PORT_ENV = 'TOA_INTROSPECTION_UI_PORT'
|
|
21
|
-
export function uiPort(): number {
|
|
22
|
-
const value = environment.get(UI_PORT_ENV)
|
|
23
|
-
|
|
24
|
-
return value === undefined ? UI_PORT : Number(value)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const DEFAULT_INTERVAL = 300
|
|
28
|
-
export const DEFAULT_THRESHOLD = 1024
|
|
29
|
-
|
|
30
|
-
/** How often a component re-announces its description, so that removed components fade out. */
|
|
31
|
-
export const ANNOUNCE_INTERVAL = 1_800_000
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* `source` arrives over the wire, so the number of distinct edges a process
|
|
35
|
-
* can hold must be bounded regardless of what peers send.
|
|
36
|
-
*/
|
|
37
|
-
export const MAX_EDGES = 4096
|
|
38
|
-
|
|
39
|
-
/** Never capture samples for these namespaces, whatever the annotation says. */
|
|
40
|
-
export const DENIED = new Set(['identity', 'introspection'])
|
|
41
|
-
|
|
42
|
-
/** Keys never stored in a sample, even when sampling is on. */
|
|
43
|
-
export const REDACTED = /^(password|secret|token|credentials?|key|authorization|cookie)$/i
|
|
44
|
-
|
|
45
|
-
/** Serialized size cap of a single sample. */
|
|
46
|
-
export const SAMPLE_LIMIT = 4096
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { options } from './annotation.js'
|
|
2
|
-
import { components } from './components.js'
|
|
3
|
-
import { version } from '../version.js'
|
|
4
|
-
import { ENV, UI_PATH, UI_PORT } from './const.js'
|
|
5
|
-
import * as schemas from './schemas.js'
|
|
6
|
-
import type { Annotation } from './annotation.js'
|
|
7
|
-
import type { Dependency, Instances, Service } from '@toa.io/operations'
|
|
8
|
-
|
|
9
|
-
/** Where Toa's release publishes this service's image. An application takes it
|
|
10
|
-
* instead of building one when its context says `registry.services: published`. */
|
|
11
|
-
export const image = 'ghcr.io/toa-io/extension-introspection-explorer'
|
|
12
|
-
|
|
13
|
-
export const standalone = true
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* The explorer hosts the introspection components, exactly as the exposition
|
|
17
|
-
* gateway hosts the identity ones. Collection is on unless the context says
|
|
18
|
-
* `introspection: false`, and the environment variable is emitted together
|
|
19
|
-
* with the service — never on its own, or tasks would pile up in a queue
|
|
20
|
-
* nothing consumes.
|
|
21
|
-
*/
|
|
22
|
-
export function deployment(_: Instances<unknown>, annotation?: Annotation): Dependency {
|
|
23
|
-
if (annotation === false) return {}
|
|
24
|
-
|
|
25
|
-
if (annotation !== undefined) schemas.annotation.validate(annotation)
|
|
26
|
-
|
|
27
|
-
const opts = options(annotation)
|
|
28
|
-
|
|
29
|
-
const service: Service = {
|
|
30
|
-
group: 'introspection',
|
|
31
|
-
name: 'explorer',
|
|
32
|
-
image,
|
|
33
|
-
version,
|
|
34
|
-
components: components().labels,
|
|
35
|
-
resources: annotation?.resources,
|
|
36
|
-
variables: []
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (opts.ui) {
|
|
40
|
-
service.port = UI_PORT
|
|
41
|
-
service.ingress = { path: UI_PATH }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
services: [service],
|
|
46
|
-
variables: { global: [{ name: ENV, value: JSON.stringify(opts) }] }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { component } from './annotation.js'
|
|
2
|
-
import * as schemas from './schemas.js'
|
|
3
|
-
import type { Declaration } from './annotation.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* The component level of the annotation.
|
|
7
|
-
*
|
|
8
|
-
* The extension is predefined, so most components say nothing and the
|
|
9
|
-
* declaration arrives as `null` — which still has to produce a value,
|
|
10
|
-
* or norm rejects the extension.
|
|
11
|
-
*/
|
|
12
|
-
export function manifest(declaration: Declaration | null | undefined): Declaration {
|
|
13
|
-
const normalized = component(declaration)
|
|
14
|
-
|
|
15
|
-
if (normalized !== false) schemas.declaration.validate(normalized)
|
|
16
|
-
|
|
17
|
-
return normalized
|
|
18
|
-
}
|