@toa.io/extensions.exposition 1.0.0-alpha.288 → 1.0.0-alpha.289

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.
Files changed (120) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
  3. package/components/identity.clients/tsconfig.tsbuildinfo +1 -1
  4. package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
  5. package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
  6. package/components/identity.grants/tsconfig.tsbuildinfo +1 -1
  7. package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
  8. package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
  9. package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
  10. package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
  11. package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
  12. package/documentation/access.md +3 -2
  13. package/documentation/discovery.md +8 -8
  14. package/documentation/help.md +16 -3
  15. package/documentation/introspection.md +10 -2
  16. package/documentation/octets.md +20 -0
  17. package/features/discovery.feature +4 -2
  18. package/features/discovery.ui.feature +19 -3
  19. package/features/help.feature +46 -0
  20. package/features/octets.feature +59 -0
  21. package/features/steps/Components.ts +4 -2
  22. package/features/steps/Database.ts +7 -1
  23. package/features/steps/Gateway.ts +23 -21
  24. package/features/steps/Parameters.ts +25 -16
  25. package/features/steps/Realtime.ts +3 -3
  26. package/package.json +5 -5
  27. package/source/Directive.ts +0 -11
  28. package/source/Discovery/trunk.ts +13 -3
  29. package/source/Factory.ts +3 -86
  30. package/source/HTTP/Probe.ts +0 -11
  31. package/source/HTTP/Server.ts +2 -3
  32. package/source/HTTP/messages.ts +4 -2
  33. package/source/Introspection.ts +28 -0
  34. package/source/const.ts +18 -0
  35. package/source/deployment.ts +2 -2
  36. package/source/directives/auth/Anonymous.ts +10 -0
  37. package/source/directives/help/Family.ts +5 -1
  38. package/source/directives/help/Help.ts +21 -0
  39. package/source/directives/octets/Directive.ts +7 -0
  40. package/source/directives/octets/Octets.ts +8 -0
  41. package/source/directives/octets/Put.ts +10 -0
  42. package/source/manifest.ts +1 -1
  43. package/source/root.ts +2 -2
  44. package/source/service.ts +94 -0
  45. package/source/shortcuts.ts +14 -0
  46. package/transpiled/Directive.d.ts +0 -1
  47. package/transpiled/Directive.js +0 -11
  48. package/transpiled/Directive.js.map +1 -1
  49. package/transpiled/Discovery/trunk.d.ts +5 -2
  50. package/transpiled/Discovery/trunk.js +12 -4
  51. package/transpiled/Discovery/trunk.js.map +1 -1
  52. package/transpiled/Factory.js +3 -61
  53. package/transpiled/Factory.js.map +1 -1
  54. package/transpiled/HTTP/Probe.d.ts +0 -10
  55. package/transpiled/HTTP/Probe.js +0 -10
  56. package/transpiled/HTTP/Probe.js.map +1 -1
  57. package/transpiled/HTTP/Server.d.ts +0 -1
  58. package/transpiled/HTTP/Server.js +2 -2
  59. package/transpiled/HTTP/Server.js.map +1 -1
  60. package/transpiled/HTTP/messages.js +4 -2
  61. package/transpiled/HTTP/messages.js.map +1 -1
  62. package/transpiled/Introspection.d.ts +21 -0
  63. package/transpiled/Introspection.js +2 -0
  64. package/transpiled/Introspection.js.map +1 -1
  65. package/transpiled/const.d.ts +14 -0
  66. package/transpiled/const.js +14 -0
  67. package/transpiled/const.js.map +1 -1
  68. package/transpiled/deployment.js +2 -2
  69. package/transpiled/deployment.js.map +1 -1
  70. package/transpiled/directives/auth/Anonymous.d.ts +7 -0
  71. package/transpiled/directives/auth/Anonymous.js +8 -0
  72. package/transpiled/directives/auth/Anonymous.js.map +1 -1
  73. package/transpiled/directives/help/Family.d.ts +1 -1
  74. package/transpiled/directives/help/Family.js +4 -0
  75. package/transpiled/directives/help/Family.js.map +1 -1
  76. package/transpiled/directives/help/Help.d.ts +6 -0
  77. package/transpiled/directives/help/Help.js +14 -0
  78. package/transpiled/directives/help/Help.js.map +1 -1
  79. package/transpiled/directives/octets/Directive.d.ts +6 -0
  80. package/transpiled/directives/octets/Directive.js.map +1 -1
  81. package/transpiled/directives/octets/Octets.d.ts +3 -0
  82. package/transpiled/directives/octets/Octets.js +6 -0
  83. package/transpiled/directives/octets/Octets.js.map +1 -1
  84. package/transpiled/directives/octets/Put.d.ts +3 -0
  85. package/transpiled/directives/octets/Put.js +8 -0
  86. package/transpiled/directives/octets/Put.js.map +1 -1
  87. package/transpiled/manifest.js +1 -1
  88. package/transpiled/root.js +2 -2
  89. package/transpiled/root.js.map +1 -1
  90. package/transpiled/service.d.ts +7 -0
  91. package/transpiled/service.js +74 -0
  92. package/transpiled/service.js.map +1 -0
  93. package/transpiled/shortcuts.d.ts +3 -0
  94. package/transpiled/shortcuts.js +13 -0
  95. package/transpiled/shortcuts.js.map +1 -0
  96. package/tsconfig.tsbuildinfo +1 -1
  97. package/ui/dist/_app/immutable/assets/0.Bl7vLmIK.css +2 -0
  98. package/ui/dist/_app/immutable/chunks/{CWJaG9mK.js → CDMRPWCx.js} +2 -2
  99. package/ui/dist/_app/immutable/chunks/DJTKIUs8.js +1 -0
  100. package/ui/dist/_app/immutable/chunks/{yDQhitF9.js → z-DVUbQC.js} +1 -1
  101. package/ui/dist/_app/immutable/entry/{app.WZSMBcGf.js → app.5S2Jtv46.js} +2 -2
  102. package/ui/dist/_app/immutable/entry/start.Bqi6IBmH.js +1 -0
  103. package/ui/dist/_app/immutable/nodes/{0.Dl2DqINo.js → 0.D4LwNilH.js} +1 -1
  104. package/ui/dist/_app/immutable/nodes/{1.CSSnGhVD.js → 1.CSRya9So.js} +1 -1
  105. package/ui/dist/_app/immutable/nodes/{2.DmolQyNG.js → 2.BtiGmR6I.js} +4 -4
  106. package/ui/dist/_app/version.json +1 -1
  107. package/ui/dist/index.html +11 -11
  108. package/components/identity.bans/migrations/0001-system-properties.yaml +0 -14
  109. package/components/identity.basic/migrations/0001-system-properties.yaml +0 -14
  110. package/components/identity.clients/migrations/0001-system-properties.yaml +0 -14
  111. package/components/identity.federation/migrations/0001-system-properties.yaml +0 -14
  112. package/components/identity.grants/migrations/0001-system-properties.yaml +0 -14
  113. package/components/identity.keys/migrations/0001-system-properties.yaml +0 -14
  114. package/components/identity.otp/migrations/0001-system-properties.yaml +0 -14
  115. package/components/identity.passkeys/migrations/0001-system-properties.yaml +0 -14
  116. package/components/identity.roles/migrations/0001-system-properties.yaml +0 -14
  117. package/components/identity.tokens/migrations/0001-system-properties.yaml +0 -14
  118. package/ui/dist/_app/immutable/assets/0.BcIGtTfw.css +0 -2
  119. package/ui/dist/_app/immutable/chunks/BgTrzN6u.js +0 -1
  120. package/ui/dist/_app/immutable/entry/start._LkoC6Lz.js +0 -1
package/source/Factory.ts CHANGED
@@ -1,20 +1,6 @@
1
- import assert from 'node:assert'
2
1
  import { createHash } from 'node:crypto'
3
- import { console, traces, type LevelName, type TracesOptions } from 'openspan'
4
2
  import { Tenant } from './Tenant.js'
5
- import { Gateway } from './Gateway.js'
6
- import { Remotes } from './Remotes.js'
7
- import { Tree } from './RTD/index.js'
8
- import { EndpointsFactory } from './Endpoint.js'
9
- import { families, interceptors } from './directives/index.js'
10
- import { DirectivesFactory } from './Directive.js'
11
- import { Composition } from './Composition.js'
12
- import * as root from './root.js'
13
- import { ATOM_GROUP } from './const.js'
14
- import { Interception } from './Interception.js'
15
- import { Dispatcher } from './RPC/index.js'
16
- import { Server as Model } from './MCP/index.js'
17
- import * as http from './HTTP/index.js'
3
+ import { CHANNEL } from './const.js'
18
4
  import type { Branch } from './Branch.js'
19
5
  import type { syntax } from './RTD/index.js'
20
6
  import type { Broadcast } from './Gateway.js'
@@ -46,80 +32,11 @@ export class Factory implements extensions.Factory {
46
32
  }
47
33
 
48
34
  public async service(): Promise<Connector | null> {
49
- assert.ok(
50
- process.env.TOA_EXPOSITION_PROPERTIES,
51
- 'TOA_EXPOSITION_PROPERTIES is undefined'
52
- )
35
+ const { service } = await import('./service.js')
53
36
 
54
- configureLogs()
55
-
56
- const options = JSON.parse(process.env.TOA_EXPOSITION_PROPERTIES) as http.Options
57
- const broadcast: Broadcast = await this.host.broadcast(CHANNEL)
58
- const server = http.Server.create({ ...options })
59
- const remotes = new Remotes(this.host)
60
- const node = root.resolve()
61
- const methods = new EndpointsFactory(remotes)
62
- const directives = new DirectivesFactory(families, remotes, this.host, options)
63
- const interception = new Interception(interceptors, options)
64
- const tree = new Tree(node, methods, directives)
65
-
66
- const composition = new Composition(this.host)
67
- const dispatcher = options.rpc === undefined ? null : new Dispatcher(options.rpc)
68
- const mcp = options.mcp === undefined ? null : new Model(options.mcp, tree)
69
- const gateway = new Gateway(
70
- broadcast,
71
- tree,
72
- interception,
73
- directives,
74
- dispatcher,
75
- mcp
76
- )
77
-
78
- gateway.depends(remotes)
79
- gateway.depends(composition)
80
- // what the directives meter through; one atom per process, connected once
81
- gateway.depends(this.host.atom(ATOM_GROUP))
82
-
83
- server.attach(gateway.process.bind(gateway))
84
- server.depends(gateway)
85
-
86
- return server
37
+ return await service(this.host)
87
38
  }
88
39
  }
89
40
 
90
- const CHANNEL = 'exposition'
91
- const LOGS_PREFIX = 'TOA_TELEMETRY_LOGS'
92
- const TRACES_ENV = 'TOA_TELEMETRY_TRACES'
93
-
94
- function configureLogs(): void {
95
- const globEnv = process.env[LOGS_PREFIX]
96
- const level: LevelName = process.env.TOA_DEV === '1' ? 'trace' : 'info'
97
- const options =
98
- globEnv === undefined ? { level } : (JSON.parse(globEnv) as { level?: LevelName })
99
-
100
- console.configure({ level: options.level ?? level })
101
-
102
- const tracesEnv = process.env[TRACES_ENV]
103
-
104
- traces(
105
- tracesEnv === undefined ? development() : (JSON.parse(tracesEnv) as TracesOptions)
106
- )
107
- }
108
-
109
- /**
110
- * Tracing is off unless it is configured. The console exporter is a local development
111
- * mechanism, so it is turned on for `toa dev` and for a boot trace the CLI has already
112
- * asked for (`runtime/boot/src/span.js`), and nowhere else — a deployment that wants
113
- * traces annotates `telemetry.traces.exporters`.
114
- *
115
- * The gateway boots without the telemetry extension, hence the copy of
116
- * `extensions/telemetry/source/extension.ts`.
117
- */
118
- function development(): TracesOptions {
119
- const local = process.env.TOA_DEV === '1' || process.env.TOA_BOOT_TRACE === '1'
120
-
121
- return local ? { exporters: { console: {} } } : {}
122
- }
123
-
124
41
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
125
42
  export type Host = extensions.Host
@@ -96,15 +96,4 @@ export class Probe {
96
96
  }
97
97
  }
98
98
 
99
- /**
100
- * Reserved for this probe. `8001` is the Telemetry readiness probe's, and `toa export` refuses a
101
- * port claimed twice — `toa mono` and a local run put every service in one process.
102
- */
103
- export const PROBE = 8004
104
99
  export const PATH = '/.ready'
105
-
106
- /**
107
- * The initial delay of the readiness probe. The server does not sleep for it: whoever
108
- * probes is the one that waits, and doing it here as well only delayed the process twice.
109
- */
110
- export const DELAY = 3 // seconds
@@ -10,7 +10,8 @@ import { Connector } from '@toa.io/core'
10
10
  import { type OutgoingMessage, write } from './messages.js'
11
11
  import { ClientError, Exception } from './exceptions.js'
12
12
  import { Context } from './Context.js'
13
- import { PROBE, Probe } from './Probe.js'
13
+ import { Probe } from './Probe.js'
14
+ import { PORT, PROBE } from '../const.js'
14
15
  import type { IncomingMessage, Protocol, ServerResponse } from './types.js'
15
16
  import type { Bouncer, MCP, OAuth, RPC } from '../Annotation.js'
16
17
 
@@ -331,8 +332,6 @@ function errorAttributes(
331
332
  return attributes
332
333
  }
333
334
 
334
- export const PORT = 8000
335
-
336
335
  export const DRAIN = 10 // seconds
337
336
 
338
337
  /** Megabytes a single HTTP/2 session may hold, over Node's default of 10. */
@@ -5,13 +5,15 @@ import * as contentType from 'content-type'
5
5
  import { console } from 'openspan'
6
6
  import { type Format, decoders } from './formats/index.js'
7
7
  import { BadRequest, NotAcceptable, UnsupportedMediaType } from './exceptions.js'
8
+ import { environment } from '@toa.io/generic'
8
9
  import type { Context } from './Context.js'
9
10
  import type { ServerResponse } from './types.js'
10
11
 
12
+ const context = environment.get('TOA_CONTEXT')
13
+ const env = environment.get('TOA_ENV')
11
14
  const server =
12
15
  `Exposition/${JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version}` +
13
- ((process.env.TOA_CONTEXT === undefined ? '' : ` ${process.env.TOA_CONTEXT}`) +
14
- (process.env.TOA_ENV === undefined ? '' : `/${process.env.TOA_ENV}`))
16
+ ((context === undefined ? '' : ` ${context}`) + (env === undefined ? '' : `/${env}`))
15
17
 
16
18
  /**
17
19
  * Applies what the request accumulated in `pipelines.response` — an `io:output` restriction,
@@ -13,6 +13,13 @@ export interface Introspection {
13
13
  /** what a person is shown where a client lists this method, which a name is not */
14
14
  title?: string
15
15
 
16
+ /**
17
+ * whether reaching it takes nothing at all — `auth:anonymous`. Said rather than left
18
+ * unsaid, because it is also what refuses a caller presenting a credential: a client
19
+ * cannot know that from an answer that says nothing.
20
+ */
21
+ anonymous?: boolean
22
+
16
23
  /** whether reaching it takes being someone, whoever — `auth:anyone` and its like */
17
24
  authenticated?: boolean
18
25
 
@@ -29,11 +36,30 @@ export interface Introspection {
29
36
  mcp?: boolean
30
37
  route?: Record<string, Schema>
31
38
  query?: Record<string, Schema>
39
+
40
+ /** what the body is, where it is a file rather than a value — [`octets:put`](octets.md) */
41
+ octets?: Octets
32
42
  input?: Schema
33
43
  output?: Schema
34
44
  errors?: string[]
35
45
  }
36
46
 
47
+ /** What sending a file to a resource takes, which is not something a schema states. */
48
+ export interface Octets {
49
+ /** what may be sent, in the syntax of an `accept` header; anything where unstated */
50
+ accept?: string
51
+
52
+ /** the largest body it takes, as it is written and as a refusal reports it */
53
+ limit: string
54
+
55
+ /**
56
+ * Whether the reply arrives as a stream of parts rather than as one object: a workflow
57
+ * runs on what was stored, and each step answers as it finishes. The first part is the
58
+ * entry itself, which is what a caller that wants nothing else reads and stops.
59
+ */
60
+ stream?: boolean
61
+ }
62
+
37
63
  export type Schema = Awaited<ReturnType<Remote['explain']>>['input']
38
64
 
39
65
  /**
@@ -64,6 +90,7 @@ export function order(introspection: Introspection): Introspection {
64
90
  const KEYS = [
65
91
  'title',
66
92
  'description',
93
+ 'anonymous',
67
94
  'authenticated',
68
95
  'private',
69
96
  'protected',
@@ -71,6 +98,7 @@ const KEYS = [
71
98
  'mcp',
72
99
  'route',
73
100
  'query',
101
+ 'octets',
74
102
  'input',
75
103
  'output',
76
104
  'errors'
package/source/const.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export const BRANCH_TTL = 1_800_000
2
2
 
3
+ /** The broadcast the gateway and its tenants share. */
4
+ export const CHANNEL = 'exposition'
5
+
3
6
  /** The replicas of the gateway decide together, whatever context they serve. */
4
7
  export const ATOM_GROUP = 'exposition'
5
8
 
@@ -25,3 +28,18 @@ export const MCP = '/.mcp'
25
28
  * on that path already answers to the same caller, and what it adds is the enumeration.
26
29
  */
27
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
@@ -3,10 +3,10 @@ import assert from 'node:assert'
3
3
  import { type Dependency, type Service } from '@toa.io/operations'
4
4
  import { type Annotation } from './Annotation.js'
5
5
  import * as schemas from './schemas.js'
6
- import { shortcuts } from './Directive.js'
6
+ import { shortcuts } from './shortcuts.js'
7
7
  import { components } from './Composition.js'
8
8
  import { parse } from './RTD/syntax/index.js'
9
- import { DELAY, PORT, PROBE } from './HTTP/index.js'
9
+ import { DELAY, PORT, PROBE } from './const.js'
10
10
 
11
11
  /** Where Toa's release publishes this service's image. An application takes it
12
12
  * instead of building one when its context says `registry.services: published`. */
@@ -1,4 +1,5 @@
1
1
  import { type Directive, type Context } from './types.js'
2
+ import type { Introspection } from '../../Introspection.js'
2
3
 
3
4
  export class Anonymous implements Directive {
4
5
  private readonly allow: boolean
@@ -23,4 +24,13 @@ export class Anonymous implements Directive {
23
24
  public admits(_: any, context: Context): boolean {
24
25
  return this.authorize(_, context)
25
26
  }
27
+
28
+ /**
29
+ * That nothing guards it is something said rather than something left unsaid: it is also
30
+ * what refuses a caller presenting a credential, and a client with one has to know which
31
+ * methods not to present it to.
32
+ */
33
+ public describe(introspection: Introspection): Introspection {
34
+ return this.allow ? { ...introspection, anonymous: true } : introspection
35
+ }
26
36
  }
@@ -47,9 +47,13 @@ export class Family implements DirectiveFamily<Directive> {
47
47
  directives: Directive[],
48
48
  _: Context,
49
49
  introspection: Introspection
50
- ): Introspection {
50
+ ): Introspection | null {
51
51
  const help = Family.method(directives)
52
52
 
53
+ // what is hidden is hidden from every answer at once, this being the one place they
54
+ // all read; the method itself is reached as it always was
55
+ if (help?.hidden === true) return null
56
+
53
57
  const described: Introspection = {
54
58
  ...introspection,
55
59
  ...(help?.title === undefined ? {} : { title: help.title }),
@@ -1,3 +1,4 @@
1
+ import assert from 'node:assert'
1
2
  import { described, type Described } from './described.js'
2
3
 
3
4
  /**
@@ -14,10 +15,30 @@ export class Help {
14
15
  public readonly title: string | undefined
15
16
  public readonly description: string | undefined
16
17
 
18
+ /**
19
+ * Whether the method is described at all. `help:method: null` takes it out of every
20
+ * answer — `OPTIONS`, discovery and the tools MCP publishes — and leaves it as callable
21
+ * as it was: a route something else reaches for, and nobody is meant to go looking for.
22
+ */
23
+ public readonly hidden: boolean
24
+
17
25
  public constructor(subject: Subject, value: unknown) {
26
+ assert.ok(
27
+ value !== null || subject === 'method',
28
+ `Directive help:${subject}: only a method is hidden`
29
+ )
30
+
31
+ if (value === null) {
32
+ this.subject = subject
33
+ this.hidden = true
34
+
35
+ return
36
+ }
37
+
18
38
  const stated: Described = described(`help:${subject}`, value)
19
39
 
20
40
  this.subject = subject
41
+ this.hidden = false
21
42
  this.title = stated.title
22
43
  this.description = stated.description
23
44
  }
@@ -1,5 +1,6 @@
1
1
  import type { Input } from './types.js'
2
2
  import type { Parameter } from '../../RTD/index.js'
3
+ import type { Introspection } from '../../Introspection.js'
3
4
  import type * as io from '../../io.js'
4
5
 
5
6
  export abstract class Directive {
@@ -11,4 +12,10 @@ export abstract class Directive {
11
12
  input: Input,
12
13
  parameters: Parameter[]
13
14
  ): Promise<io.Output>
15
+
16
+ /**
17
+ * What a caller has to know to make the request. Only sending a file takes anything a
18
+ * schema does not already say; reading one and deleting one take nothing.
19
+ */
20
+ public describe?(introspection: Introspection): void
14
21
  }
@@ -9,6 +9,7 @@ import type { Output } from '../../io.js'
9
9
  import type { Component } from '@toa.io/core'
10
10
  import type { Remotes } from '../../Remotes.js'
11
11
  import type { Parameter, DirectiveFamily } from '../../RTD/index.js'
12
+ import type { Introspection } from '../../Introspection.js'
12
13
  import type { Input } from './types.js'
13
14
 
14
15
  export class Octets implements DirectiveFamily<Directive> {
@@ -28,6 +29,13 @@ export class Octets implements DirectiveFamily<Directive> {
28
29
  return new Class(value, this.discovery, remotes)
29
30
  }
30
31
 
32
+ /** What the request takes, which only sending a file says anything about. */
33
+ public explain(directives: Directive[], _: Input, introspection: Introspection): Introspection {
34
+ for (const directive of directives) directive.describe?.(introspection)
35
+
36
+ return introspection
37
+ }
38
+
31
39
  public async precall(
32
40
  directives: Directive[],
33
41
  input: Input,
@@ -12,6 +12,7 @@ import type { Unit, Location } from './workflows/index.js'
12
12
  import type { Entry } from '@toa.io/extensions.storages'
13
13
  import type { Remotes } from '../../Remotes.js'
14
14
  import type { Component } from '@toa.io/core'
15
+ import type { Introspection } from '../../Introspection.js'
15
16
  import type { Output } from '../../io.js'
16
17
  import type { Input } from './types.js'
17
18
 
@@ -63,6 +64,15 @@ export class Put extends Directive {
63
64
  cors.allow('content-location')
64
65
  }
65
66
 
67
+ /** What sending a file here takes: what may be sent, how much of it, and what comes back. */
68
+ public override describe(introspection: Introspection): void {
69
+ introspection.octets = {
70
+ ...(this.accept === undefined ? {} : { accept: this.accept }),
71
+ limit: this.limitString,
72
+ ...(this.workflow === undefined ? {} : { stream: true })
73
+ }
74
+ }
75
+
66
76
  public async apply(
67
77
  storage: string,
68
78
  input: Input,
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert'
2
2
  import { parse, type Node, type Method, type Query } from './RTD/syntax/index.js'
3
- import { shortcuts } from './Directive.js'
3
+ import { shortcuts } from './shortcuts.js'
4
4
  import * as schemas from './schemas.js'
5
5
  import type { Manifest } from '@toa.io/norm'
6
6
 
package/source/root.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { merge } from '@toa.io/generic'
1
+ import { environment, merge } from '@toa.io/generic'
2
2
  import { syntax } from './RTD/index.js'
3
3
 
4
4
  export function resolve(): syntax.Node {
5
- const value = process.env.TOA_EXPOSITION
5
+ const value = environment.get('TOA_EXPOSITION')
6
6
  const root =
7
7
  value !== undefined ? (JSON.parse(value) as syntax.Node) : syntax.createNode()
8
8
 
@@ -0,0 +1,94 @@
1
+ import assert from 'node:assert'
2
+ import { console, traces, type LevelName, type TracesOptions } from 'openspan'
3
+ import { Gateway } from './Gateway.js'
4
+ import { Remotes } from './Remotes.js'
5
+ import { Tree } from './RTD/index.js'
6
+ import { EndpointsFactory } from './Endpoint.js'
7
+ import { families, interceptors } from './directives/index.js'
8
+ import { DirectivesFactory } from './Directive.js'
9
+ import { Composition } from './Composition.js'
10
+ import * as root from './root.js'
11
+ import { ATOM_GROUP, CHANNEL } from './const.js'
12
+ import { Interception } from './Interception.js'
13
+ import { Dispatcher } from './RPC/index.js'
14
+ import { Server as Model } from './MCP/index.js'
15
+ import * as http from './HTTP/index.js'
16
+ import { environment } from '@toa.io/generic'
17
+ import type { Broadcast } from './Gateway.js'
18
+ import type { Connector } from '@toa.io/core'
19
+ import type { Host } from './Factory.js'
20
+
21
+ /**
22
+ * The gateway. A composition loads this extension for its tenants alone, so what the
23
+ * gateway is made of is reached from here and nowhere the factory imports.
24
+ */
25
+ export async function service(host: Host): Promise<Connector | null> {
26
+ const properties = environment.get('TOA_EXPOSITION_PROPERTIES')
27
+
28
+ assert.ok(properties !== undefined, 'TOA_EXPOSITION_PROPERTIES is undefined')
29
+
30
+ configureLogs()
31
+
32
+ const options = JSON.parse(properties) as http.Options
33
+ const broadcast: Broadcast = await host.broadcast(CHANNEL)
34
+ const server = http.Server.create({ ...options })
35
+ const remotes = new Remotes(host)
36
+ const node = root.resolve()
37
+ const methods = new EndpointsFactory(remotes)
38
+ const directives = new DirectivesFactory(families, remotes, host, options)
39
+ const interception = new Interception(interceptors, options)
40
+ const tree = new Tree(node, methods, directives)
41
+
42
+ const composition = new Composition(host)
43
+ const dispatcher = options.rpc === undefined ? null : new Dispatcher(options.rpc)
44
+ const mcp = options.mcp === undefined ? null : new Model(options.mcp, tree)
45
+ const gateway = new Gateway(broadcast, tree, interception, directives, dispatcher, mcp)
46
+
47
+ gateway.depends(remotes)
48
+ gateway.depends(composition)
49
+ // what the directives meter through; one atom per process, connected once
50
+ gateway.depends(host.atom(ATOM_GROUP))
51
+
52
+ server.attach(gateway.process.bind(gateway))
53
+ server.depends(gateway)
54
+
55
+ return server
56
+ }
57
+
58
+ const LOGS_PREFIX = 'TOA_TELEMETRY_LOGS'
59
+ const TRACES_ENV = 'TOA_TELEMETRY_TRACES'
60
+
61
+ function configureLogs(): void {
62
+ const globEnv = environment.get(LOGS_PREFIX)
63
+ const level: LevelName = environment.get('TOA_DEV') === '1' ? 'trace' : 'info'
64
+ const options =
65
+ globEnv === undefined ? { level } : (JSON.parse(globEnv) as { level?: LevelName })
66
+
67
+ console.configure({ level: options.level ?? level })
68
+
69
+ const tracesEnv = environment.get(TRACES_ENV)
70
+ const tracing =
71
+ tracesEnv === undefined ? development() : (JSON.parse(tracesEnv) as TracesOptions)
72
+
73
+ // `openspan` is not Toa's and reads the environment, which no longer holds the context by
74
+ // the time the exporter is made — so the service is named here
75
+ if (tracing.exporters?.otlp !== undefined)
76
+ tracing.exporters.otlp.service ??= environment.get('TOA_CONTEXT')
77
+
78
+ traces(tracing)
79
+ }
80
+
81
+ /**
82
+ * Tracing is off unless it is configured. The console exporter is a local development
83
+ * mechanism, so it is turned on for `toa dev` and for a boot trace the CLI has already
84
+ * asked for (`runtime/boot/src/span.js`), and nowhere else — a deployment that wants
85
+ * traces annotates `telemetry.traces.exporters`.
86
+ *
87
+ * The gateway boots without the telemetry extension, hence the copy of
88
+ * `extensions/telemetry/source/extension.ts`.
89
+ */
90
+ function development(): TracesOptions {
91
+ const local = environment.get('TOA_DEV') === '1' || environment.get('TOA_BOOT_TRACE') === '1'
92
+
93
+ return local ? { exporters: { console: {} } } : {}
94
+ }
@@ -0,0 +1,14 @@
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
+ ])
@@ -41,4 +41,3 @@ export declare class DirectivesFactory implements RTD.DirectiveFactory {
41
41
  /** Mandatory families first, in their own order; everything else keeps its own. */
42
42
  private rank;
43
43
  }
44
- export declare const shortcuts: RTD.syntax.Shortcuts;
@@ -160,15 +160,4 @@ function options(set, stage) {
160
160
  options.attributes = { directives: Array.from(new Set(set.names)).join(' ') };
161
161
  return options;
162
162
  }
163
- export const shortcuts = new Map([
164
- ['anonymous', 'auth:anonymous'],
165
- ['anyone', 'auth:anyone'],
166
- ['id', 'auth:id'],
167
- ['role', 'auth:role'],
168
- ['rule', 'auth:rule'],
169
- ['incept', 'auth:incept'],
170
- ['input', 'io:input'],
171
- ['output', 'io:output'],
172
- ['languages', 'map:languages']
173
- ]);
174
163
  //# sourceMappingURL=Directive.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Directive.js","sourceRoot":"","sources":["../source/Directive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAoB,MAAM,UAAU,CAAA;AAQpD,MAAM,OAAO,UAAU;IACJ,IAAI,CAAoB;IAEzC,iFAAiF;IAChE,KAAK,CAAS;IAE/B,YAAmB,IAAwB;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC9B,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC;YAChC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC;YAC9B,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC;SACjC,CAAC,CAAC,CAAA;IACL,CAAC;IAEM,QAAQ,CAAI,MAAc;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,EAAE,UAE/C,CAAA;IACf,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,OAAgB,EAAE,UAA2B;QAChE,IAAI,MAAM,GAAG,IAAI,CAAA;QAEjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAExB,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS;gBAAE,SAAQ;YAE9C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAC5B,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EACrB,KAAK,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,OAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAC3E,CAAA;YAED,IAAI,GAAG,KAAK,IAAI;gBAAE,SAAQ;YAE1B,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;;gBACxE,MAAM,GAAG,GAAG,CAAA;QACnB,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAClB,OAAgB,EAChB,aAA4B;QAE5B,IAAI,SAAS,GAAkB,aAAa,CAAA;QAE5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAExB,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,KAAK,SAAS;gBAAE,SAAQ;YAE9C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAC7B,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EACrB,KAAK,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,OAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,CAC1E,CAAA;YAED,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAA;YAE9B,SAAS,GAAG,IAAI,CAAA;QAClB,CAAC;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,OAAgB,EAAE,QAAyB;QAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAExB,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;gBACjC,MAAM,OAAO,CAAC,IAAI,CAChB,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EACpB,KAAK,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,MAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CACxE,CAAA;QACL,CAAC;IACH,CAAC;IAEM,OAAO;QACZ,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI;YAAE,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACnE,CAAC;CACF;AAED,MAAM,OAAO,iBAAiB;IACX,OAAO,CAAS;IAChB,QAAQ,GAAwC,EAAE,CAAA;IAClD,SAAS,GAAa,EAAE,CAAA;IACxB,SAAS,GAAiB,EAAE,CAAA;IAE7C,oFAAoF;IACnE,MAAM,GAAY,EAAE,CAAA;IAErC,sCAAsC;IACtC,YACE,QAA+B,EAC/B,OAAgB,EAChB,IAAU,EACV,OAAgB;QAEhB,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC7B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAA;YAEnC,IAAI,MAAM,CAAC,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAEtD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACf,MAAM;gBACN,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,YAAY,EAAE;gBAC/C,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,SAAS,EAAE;aAC1C,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,SAAS,CAAC,OAAgB;QACrC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM;YAC7B,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS;gBACtC,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;oBAC7C,MAAM,KAAK,CAAC,MAAM,CAAC,SAAU,CAAC,OAAO,CAAC,CAAA;gBACxC,CAAC,CAAC,CAAA;IACR,CAAC;IAED,iDAAiD;IAC1C,KAAK,CAAC,MAAM,CAAC,OAAgB,EAAE,QAAyB;QAC7D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM;YAC7B,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;gBACnC,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;oBAC1C,MAAM,KAAK,CAAC,MAAM,CAAC,MAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;gBAC/C,CAAC,CAAC,CAAA;IACR,CAAC;IAEM,MAAM,CAAC,YAAoC,EAAE,QAAgB,EAAE;QACpE,MAAM,MAAM,GAAwB,EAAE,CAAA;QACtC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAEzC,MAAM,KAAK,GAA6B,EAAE,CAAA;QAE1C,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;YAEhD,IAAI,MAAM,KAAK,SAAS;gBACtB,MAAM,IAAI,KAAK,CAAC,qBAAqB,WAAW,CAAC,MAAM,gBAAgB,CAAC,CAAA;YAE1E,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAC7B,WAAW,CAAC,IAAI,EAChB,WAAW,CAAC,KAAK,EACjB,IAAI,CAAC,OAAO,EACZ,KAAK,CACN,CAAA;YAED,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;YAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACnC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;YACzB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,CAAC,CAAA;YACpE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;QAED,MAAM,IAAI,GAAuB,EAAE,CAAA;QAEnC,KAAK,MAAM,MAAM,IAAI,SAAS;YAC5B,IAAI,CAAC,IAAI,CAAC;gBACR,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC7B,UAAU,EAAE,EAAE;gBACd,KAAK,EAAE,EAAE;aACV,CAAC,CAAA;QAEJ,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;YACvD,IAAI,CAAC,IAAI,CAAC;gBACR,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC7B,UAAU;gBACV,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;aACrB,CAAC,CAAA;QAEJ,iFAAiF;QACjF,iFAAiF;QACjF,+EAA+E;QAC/E,wEAAwE;QACxE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;QAExE,wFAAwF;QACxF,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAE5D,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAA;QAEvC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAE/B,OAAO,UAAU,CAAA;IACnB,CAAC;IAEM,WAAW,CAAC,SAA+B;QAChD,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,KAAK,CAAA;IAC7D,CAAC;IAEM,OAAO;QACZ,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,SAAS;YAAE,UAAU,CAAC,OAAO,EAAE,CAAA;IAC/D,CAAC;IAED,mFAAmF;IAC3E,IAAI,CAAC,MAAc;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAE5C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;IACrD,CAAC;CACF;AAED,SAAS,OAAO,CACd,GAAqB,EACrB,KAAuC;IAEvC,MAAM,OAAO,GAAgB,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAA;IAEpE,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACjD,OAAO,CAAC,UAAU,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;IAE/E,OAAO,OAAO,CAAA;AAChB,CAAC;AAcD,MAAM,CAAC,MAAM,SAAS,GAAyB,IAAI,GAAG,CAAC;IACrD,CAAC,WAAW,EAAE,gBAAgB,CAAC;IAC/B,CAAC,QAAQ,EAAE,aAAa,CAAC;IACzB,CAAC,IAAI,EAAE,SAAS,CAAC;IACjB,CAAC,MAAM,EAAE,WAAW,CAAC;IACrB,CAAC,MAAM,EAAE,WAAW,CAAC;IACrB,CAAC,QAAQ,EAAE,aAAa,CAAC;IACzB,CAAC,OAAO,EAAE,UAAU,CAAC;IACrB,CAAC,QAAQ,EAAE,WAAW,CAAC;IACvB,CAAC,WAAW,EAAE,eAAe,CAAC;CAC/B,CAAC,CAAA"}
1
+ {"version":3,"file":"Directive.js","sourceRoot":"","sources":["../source/Directive.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,10 @@ import type { Context, OutgoingMessage } from '../HTTP/index.js';
4
4
  * and `/` is usually not one of those — a person who typed the address into a browser is
5
5
  * looking for something to look at, and the page is the only thing here that is one.
6
6
  *
7
- * A client that takes anything is not one: `accept` has to prefer a page over what the
8
- * gateway answers with, which is what a browser sends and an API client does not.
7
+ * A client that asked for something in particular gets what it asked for: only an `accept`
8
+ * that prefers a page, or one that names no preference at all, is sent here. The second is
9
+ * what an unfurler sends — several ask for anything, and a link to an application would
10
+ * otherwise show nothing — and what `curl` sends, which the page serves no worse than the
11
+ * `405` it used to get.
9
12
  */
10
13
  export declare function looking(context: Context): OutgoingMessage | null;
@@ -8,14 +8,22 @@ const HTML = 'text/html';
8
8
  * and `/` is usually not one of those — a person who typed the address into a browser is
9
9
  * looking for something to look at, and the page is the only thing here that is one.
10
10
  *
11
- * A client that takes anything is not one: `accept` has to prefer a page over what the
12
- * gateway answers with, which is what a browser sends and an API client does not.
11
+ * A client that asked for something in particular gets what it asked for: only an `accept`
12
+ * that prefers a page, or one that names no preference at all, is sent here. The second is
13
+ * what an unfurler sends — several ask for anything, and a link to an application would
14
+ * otherwise show nothing — and what `curl` sends, which the page serves no worse than the
15
+ * `405` it used to get.
13
16
  */
14
17
  export function looking(context) {
15
18
  if (context.request.method !== 'GET' || context.procedural)
16
19
  return null;
17
- if (new Negotiator(context.request).mediaType([...types, HTML]) !== HTML)
18
- return null;
20
+ const accept = context.request.headers.accept;
21
+ if (accept !== undefined && accept.trim() !== ANYTHING) {
22
+ if (new Negotiator(context.request).mediaType([...types, HTML]) !== HTML)
23
+ return null;
24
+ }
19
25
  return { status: 302, headers: new Headers({ location: DISCOVERY + '/' }) };
20
26
  }
27
+ /** An `accept` that names nothing, which is the same as naming none. */
28
+ const ANYTHING = '*/*';
21
29
  //# sourceMappingURL=trunk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"trunk.js","sourceRoot":"","sources":["../../source/Discovery/trunk.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGvC,wEAAwE;AACxE,MAAM,IAAI,GAAG,WAAW,CAAA;AAExB;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,OAAgB;IACtC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAEvE,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAErF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,EAAE,QAAQ,EAAE,SAAS,GAAG,GAAG,EAAE,CAAC,EAAE,CAAA;AAC7E,CAAC"}
1
+ {"version":3,"file":"trunk.js","sourceRoot":"","sources":["../../source/Discovery/trunk.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGvC,wEAAwE;AACxE,MAAM,IAAI,GAAG,WAAW,CAAA;AAExB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,OAAO,CAAC,OAAgB;IACtC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAEvE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAA;IAE7C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;QACvD,IAAI,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;IACvF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,EAAE,QAAQ,EAAE,SAAS,GAAG,GAAG,EAAE,CAAC,EAAE,CAAA;AAC7E,CAAC;AAED,wEAAwE;AACxE,MAAM,QAAQ,GAAG,KAAK,CAAA"}