@toa.io/extensions.exposition 1.0.0-alpha.292 → 1.0.0-alpha.293
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/components/identity.basic/package.json +8 -0
- package/components/identity.federation/package.json +8 -0
- package/components/identity.passkeys/package.json +8 -0
- package/components/identity.tokens/package.json +10 -0
- package/package.json +24 -13
- package/transpiled/Endpoint.js +3 -0
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Introspection.d.ts +6 -0
- package/transpiled/Introspection.js +1 -0
- package/transpiled/Introspection.js.map +1 -1
- package/transpiled/MCP/schema.js +2 -2
- package/transpiled/Query.d.ts +2 -12
- package/transpiled/Query.js +2 -12
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RTD/Endpoint.d.ts +2 -3
- package/ui/dist/_app/immutable/assets/0.Cu0AnR_8.css +2 -0
- package/ui/dist/_app/immutable/assets/2.BghF56l_.css +1 -0
- package/ui/dist/_app/immutable/assets/ui.C8aD6zzF.css +1 -0
- package/ui/dist/_app/immutable/chunks/B8GvXrmq.js +1 -0
- package/ui/dist/_app/immutable/chunks/BVDJTrf4.js +3 -0
- package/ui/dist/_app/immutable/chunks/D6q28_Ok.js +86 -0
- package/ui/dist/_app/immutable/chunks/{aLpTDmOc.js → DVlaUfVU.js} +1 -1
- package/ui/dist/_app/immutable/chunks/DpB73ZB6.js +6 -0
- package/ui/dist/_app/immutable/entry/app.CTava8yn.js +2 -0
- package/ui/dist/_app/immutable/entry/start.yWN0gSLA.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.HKRDBYrC.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.CLa0ydOi.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.C9Kaym8C.js +8 -0
- package/ui/dist/_app/immutable/nodes/3.C1X4_rXZ.js +1 -0
- package/ui/dist/_app/immutable/nodes/4.DdzDIi9E.js +1 -0
- package/ui/dist/_app/immutable/nodes/5.DpbmCMdw.js +1 -0
- package/ui/dist/_app/version.json +1 -1
- package/ui/dist/index.html +12 -12
- package/CHANGELOG.md +0 -42
- package/components/identity.bans/tsconfig.tsbuildinfo +0 -1
- package/components/identity.basic/source/lib/credentials.test.ts +0 -18
- package/components/identity.basic/tsconfig.tsbuildinfo +0 -1
- package/components/identity.clients/source/lib/canonical.test.ts +0 -43
- package/components/identity.clients/source/lib/cimd.test.ts +0 -122
- package/components/identity.clients/source/lib/redirect.test.ts +0 -47
- package/components/identity.clients/tsconfig.tsbuildinfo +0 -1
- package/components/identity.credentials/source/list.test.ts +0 -70
- package/components/identity.credentials/tsconfig.tsbuildinfo +0 -1
- package/components/identity.federation/source/lib/discovery.test.ts +0 -60
- package/components/identity.federation/source/list.test.ts +0 -41
- package/components/identity.federation/tsconfig.tsbuildinfo +0 -1
- package/components/identity.grants/tsconfig.tsbuildinfo +0 -1
- package/components/identity.keys/tsconfig.tsbuildinfo +0 -1
- package/components/identity.otp/tsconfig.tsbuildinfo +0 -1
- package/components/identity.passkeys/source/errors.test.ts +0 -34
- package/components/identity.passkeys/source/webauthn.test.ts +0 -26
- package/components/identity.passkeys/tsconfig.tsbuildinfo +0 -1
- package/components/identity.roles/tsconfig.tsbuildinfo +0 -1
- package/components/identity.tokens/source/authenticate.test.ts +0 -112
- package/components/identity.tokens/source/decrypt.test.ts +0 -216
- package/components/identity.tokens/source/encrypt.test.ts +0 -150
- package/components/identity.tokens/source/issue.test.ts +0 -73
- package/components/identity.tokens/source/lib/form.test.ts +0 -25
- package/components/identity.tokens/source/lib/paseto.test.ts +0 -62
- package/components/identity.tokens/tsconfig.tsbuildinfo +0 -1
- package/context.toa.yaml +0 -31
- package/cucumber.mjs +0 -7
- package/documentation/.assets/ia3-dark.jpg +0 -0
- package/documentation/.assets/ia3-light.jpg +0 -0
- package/documentation/.assets/overview-dark.jpg +0 -0
- package/documentation/.assets/overview-light.jpg +0 -0
- package/documentation/.assets/role-scopes-dark.jpg +0 -0
- package/documentation/.assets/role-scopes-light.jpg +0 -0
- package/documentation/.assets/rtd-dark.jpg +0 -0
- package/documentation/.assets/rtd-light.jpg +0 -0
- package/documentation/access.md +0 -350
- package/documentation/authorities.md +0 -51
- package/documentation/cache.md +0 -72
- package/documentation/components.md +0 -472
- package/documentation/consent.md +0 -101
- package/documentation/credentials.md +0 -88
- package/documentation/dev.md +0 -30
- package/documentation/discovery.md +0 -81
- package/documentation/flow.md +0 -44
- package/documentation/help.md +0 -122
- package/documentation/identity.md +0 -304
- package/documentation/introspection.md +0 -123
- package/documentation/io.md +0 -167
- package/documentation/ip.md +0 -24
- package/documentation/map.md +0 -123
- package/documentation/mcp.md +0 -183
- package/documentation/notes/desync.jpg +0 -0
- package/documentation/notes/peers.md +0 -59
- package/documentation/notes/sse.md +0 -71
- package/documentation/notes/throttling.md +0 -122
- package/documentation/oauth.md +0 -180
- package/documentation/octets.md +0 -334
- package/documentation/passkeys.md +0 -11
- package/documentation/protocol.md +0 -120
- package/documentation/query.md +0 -324
- package/documentation/require.md +0 -15
- package/documentation/rpc.md +0 -154
- package/documentation/tracing.md +0 -70
- package/documentation/tree.md +0 -199
- package/features/access.feature +0 -596
- package/features/annotation.feature +0 -32
- package/features/auth.assert.feature +0 -55
- package/features/auth.bearer.feature +0 -120
- package/features/auth.claims.feature +0 -171
- package/features/auth.incept.feature +0 -166
- package/features/auth.input.feature +0 -59
- package/features/authorities.basic.feature +0 -141
- package/features/authorities.feature +0 -71
- package/features/authorities.federation.feature +0 -100
- package/features/authorities.tokens.feature +0 -117
- package/features/body.feature +0 -76
- package/features/cache.feature +0 -368
- package/features/configuration.feature +0 -206
- package/features/cors.feature +0 -127
- package/features/credentials.feature +0 -106
- package/features/debug.feature +0 -34
- package/features/dev.feature +0 -90
- package/features/directives.feature +0 -61
- package/features/discovery.feature +0 -213
- package/features/discovery.ui.feature +0 -213
- package/features/dynamic.feature +0 -192
- package/features/errors.feature +0 -258
- package/features/etag.feature +0 -239
- package/features/flow.feature +0 -148
- package/features/help.feature +0 -512
- package/features/identity.bans.feature +0 -200
- package/features/identity.basic.feature +0 -666
- package/features/identity.feature +0 -240
- package/features/identity.federation.feature +0 -396
- package/features/identity.otp.feature +0 -142
- package/features/identity.roles.feature +0 -568
- package/features/identity.tokens.custom.feature +0 -430
- package/features/identity.tokens.feature +0 -131
- package/features/interruptions.feature +0 -20
- package/features/introspection.feature +0 -565
- package/features/introspection.map.feature +0 -108
- package/features/io.feature +0 -204
- package/features/io.throttle.feature +0 -166
- package/features/map.feature +0 -360
- package/features/mcp.feature +0 -753
- package/features/methods.feature +0 -48
- package/features/oauth.clients.feature +0 -195
- package/features/oauth.discovery.feature +0 -164
- package/features/oauth.grants.feature +0 -301
- package/features/octets.cloudinary.feature +0 -233
- package/features/octets.download.feature +0 -194
- package/features/octets.entries.feature +0 -83
- package/features/octets.feature +0 -306
- package/features/octets.head.feature +0 -40
- package/features/octets.location.feature +0 -83
- package/features/octets.meta.feature +0 -117
- package/features/octets.workflows.feature +0 -565
- package/features/passkeys.feature +0 -66
- package/features/probes.feature +0 -18
- package/features/query.feature +0 -389
- package/features/realtime.feature +0 -34
- package/features/require.feature +0 -66
- package/features/response.feature +0 -103
- package/features/routes.feature +0 -224
- package/features/rpc.feature +0 -429
- package/features/server.feature +0 -21
- package/features/site/_app/immutable/asset.js +0 -1
- package/features/site/favicon.ico +0 -0
- package/features/site/index.html +0 -10
- package/features/steps/.env.example +0 -5
- package/features/steps/Captures.ts +0 -20
- package/features/steps/Cloudinary.ts +0 -12
- package/features/steps/Common.ts +0 -38
- package/features/steps/Components.ts +0 -115
- package/features/steps/Database.ts +0 -106
- package/features/steps/Gateway.ts +0 -239
- package/features/steps/HTTP.ts +0 -96
- package/features/steps/IDP.ts +0 -361
- package/features/steps/Identity.ts +0 -54
- package/features/steps/OTP.ts +0 -42
- package/features/steps/Parameters.ts +0 -162
- package/features/steps/Probe.ts +0 -38
- package/features/steps/Realtime.ts +0 -175
- package/features/steps/Workspace.ts +0 -48
- package/features/steps/components/audit/manifest.toa.yaml +0 -20
- package/features/steps/components/audit/operations/record.js +0 -5
- package/features/steps/components/echo/manifest.toa.yaml +0 -59
- package/features/steps/components/echo/operations/affect.js +0 -3
- package/features/steps/components/echo/operations/compute.js +0 -3
- package/features/steps/components/echo/operations/echo.js +0 -3
- package/features/steps/components/echo/operations/error.js +0 -7
- package/features/steps/components/echo/operations/identity.js +0 -3
- package/features/steps/components/echo/operations/parameters.js +0 -3
- package/features/steps/components/echo/operations/ping.js +0 -3
- package/features/steps/components/echo/operations/unwrap.js +0 -3
- package/features/steps/components/echo.beacon/manifest.toa.yaml +0 -2
- package/features/steps/components/echo.beacon/operations/hello.js +0 -3
- package/features/steps/components/greeter/manifest.toa.yaml +0 -5
- package/features/steps/components/greeter/operations/greet.js +0 -3
- package/features/steps/components/notify/manifest.toa.yaml +0 -20
- package/features/steps/components/notify/operations/send.js +0 -5
- package/features/steps/components/octets.tester/manifest.toa.yaml +0 -73
- package/features/steps/components/octets.tester/operations/authority.js +0 -3
- package/features/steps/components/octets.tester/operations/bar.js +0 -9
- package/features/steps/components/octets.tester/operations/baz.js +0 -9
- package/features/steps/components/octets.tester/operations/concat.js +0 -5
- package/features/steps/components/octets.tester/operations/echo.js +0 -5
- package/features/steps/components/octets.tester/operations/err.js +0 -14
- package/features/steps/components/octets.tester/operations/foo.js +0 -5
- package/features/steps/components/octets.tester/operations/id.js +0 -5
- package/features/steps/components/octets.tester/operations/identity.js +0 -5
- package/features/steps/components/octets.tester/operations/lenna.png +0 -0
- package/features/steps/components/octets.tester/operations/redirect.js +0 -17
- package/features/steps/components/octets.tester/operations/yex.js +0 -14
- package/features/steps/components/octets.tester/operations/yield.js +0 -11
- package/features/steps/components/orders/manifest.toa.yaml +0 -30
- package/features/steps/components/orders/operations/create.js +0 -7
- package/features/steps/components/pots/manifest.toa.yaml +0 -91
- package/features/steps/components/pots/migrations/0002-indexes.yaml +0 -4
- package/features/steps/components/pricing/manifest.toa.yaml +0 -13
- package/features/steps/components/pricing/operations/quote.js +0 -3
- package/features/steps/components/sequences/manifest.toa.yaml +0 -11
- package/features/steps/components/sequences/operations/numbers.js +0 -5
- package/features/steps/components/sequences/operations/tokens.js +0 -14
- package/features/steps/components/users/manifest.toa.yaml +0 -30
- package/features/steps/components/users/operations/create.js +0 -11
- package/features/steps/components/users.properties/manifest.toa.yaml +0 -22
- package/features/steps/map.ts +0 -30
- package/features/steps/tsconfig.json +0 -9
- package/features/streams.feature +0 -35
- package/features/timestamps.feature +0 -41
- package/features/timing.feature +0 -49
- package/features/tracing.feature +0 -123
- package/source/Branch.test.ts +0 -40
- package/source/Branch.ts +0 -44
- package/source/Composition.ts +0 -51
- package/source/Context.ts +0 -11
- package/source/Directive.test.ts +0 -199
- package/source/Directive.ts +0 -248
- package/source/Discovery/Explorer.ts +0 -34
- package/source/Discovery/Site.test.ts +0 -147
- package/source/Discovery/Site.ts +0 -202
- package/source/Discovery/index.ts +0 -3
- package/source/Discovery/tree.test.ts +0 -175
- package/source/Discovery/tree.ts +0 -77
- package/source/Discovery/trunk.ts +0 -33
- package/source/Endpoint.test.ts +0 -67
- package/source/Endpoint.ts +0 -157
- package/source/Factory.ts +0 -42
- package/source/Gateway.ts +0 -390
- package/source/HTTP/Context.ts +0 -143
- package/source/HTTP/Probe.ts +0 -99
- package/source/HTTP/Server.ts +0 -435
- package/source/HTTP/Timing.ts +0 -14
- package/source/HTTP/address.test.ts +0 -28
- package/source/HTTP/address.ts +0 -16
- package/source/HTTP/exceptions.ts +0 -99
- package/source/HTTP/formats/form.ts +0 -21
- package/source/HTTP/formats/index.ts +0 -36
- package/source/HTTP/formats/json.ts +0 -14
- package/source/HTTP/formats/msgpack.ts +0 -14
- package/source/HTTP/formats/text.ts +0 -10
- package/source/HTTP/formats/yaml.test.ts +0 -22
- package/source/HTTP/formats/yaml.ts +0 -40
- package/source/HTTP/index.ts +0 -6
- package/source/HTTP/messages.test.ts +0 -196
- package/source/HTTP/messages.ts +0 -186
- package/source/HTTP/types.ts +0 -39
- package/source/Interception.ts +0 -37
- package/source/Introspection.ts +0 -179
- package/source/MCP/Server.ts +0 -335
- package/source/MCP/discover.ts +0 -51
- package/source/MCP/errors.ts +0 -80
- package/source/MCP/index.ts +0 -1
- package/source/MCP/schema.test.ts +0 -91
- package/source/MCP/schema.ts +0 -94
- package/source/MCP/tools.ts +0 -144
- package/source/MCP/types.ts +0 -84
- package/source/Mapping.ts +0 -101
- package/source/Query.test.ts +0 -54
- package/source/Query.ts +0 -277
- package/source/RPC/Context.ts +0 -75
- package/source/RPC/Dispatcher.ts +0 -150
- package/source/RPC/errors.test.ts +0 -77
- package/source/RPC/errors.ts +0 -82
- package/source/RPC/index.ts +0 -1
- package/source/RPC/names.test.ts +0 -141
- package/source/RPC/names.ts +0 -176
- package/source/RPC/types.ts +0 -34
- package/source/RTD/Context.ts +0 -16
- package/source/RTD/Directives.ts +0 -126
- package/source/RTD/Endpoint.ts +0 -27
- package/source/RTD/Match.ts +0 -11
- package/source/RTD/Method.ts +0 -39
- package/source/RTD/Node.ts +0 -169
- package/source/RTD/Route.ts +0 -93
- package/source/RTD/Tree.ts +0 -137
- package/source/RTD/expiration.test.ts +0 -81
- package/source/RTD/factory.ts +0 -105
- package/source/RTD/index.ts +0 -8
- package/source/RTD/segment.test.ts +0 -35
- package/source/RTD/segment.ts +0 -51
- package/source/RTD/syntax/index.ts +0 -1
- package/source/RTD/walk.test.ts +0 -147
- package/source/Remotes.test.ts +0 -79
- package/source/Remotes.ts +0 -39
- package/source/Tenant.ts +0 -73
- package/source/directives/auth/Anonymous.test.ts +0 -70
- package/source/directives/auth/Anonymous.ts +0 -36
- package/source/directives/auth/Anyone.ts +0 -23
- package/source/directives/auth/Assert.ts +0 -43
- package/source/directives/auth/Authorization.test.ts +0 -24
- package/source/directives/auth/Authorization.ts +0 -388
- package/source/directives/auth/Delegate.test.ts +0 -36
- package/source/directives/auth/Delegate.ts +0 -54
- package/source/directives/auth/Echo.ts +0 -24
- package/source/directives/auth/Federation.ts +0 -104
- package/source/directives/auth/Id.ts +0 -37
- package/source/directives/auth/Incept.ts +0 -117
- package/source/directives/auth/Input.ts +0 -92
- package/source/directives/auth/Role.test.ts +0 -147
- package/source/directives/auth/Role.ts +0 -99
- package/source/directives/auth/Rule.ts +0 -59
- package/source/directives/auth/Scheme.ts +0 -31
- package/source/directives/auth/create.ts +0 -11
- package/source/directives/auth/index.ts +0 -3
- package/source/directives/auth/schemes.test.ts +0 -18
- package/source/directives/auth/schemes.ts +0 -39
- package/source/directives/auth/split.ts +0 -14
- package/source/directives/auth/types.ts +0 -75
- package/source/directives/cache/Cache.ts +0 -92
- package/source/directives/cache/Control.ts +0 -81
- package/source/directives/cache/Exact.ts +0 -7
- package/source/directives/cache/etag.ts +0 -38
- package/source/directives/cache/index.ts +0 -3
- package/source/directives/cache/types.ts +0 -9
- package/source/directives/cors/CORS.test.ts +0 -75
- package/source/directives/cors/CORS.ts +0 -88
- package/source/directives/cors/index.ts +0 -3
- package/source/directives/dev/Development.ts +0 -41
- package/source/directives/dev/Faulty.ts +0 -31
- package/source/directives/dev/Sleep.ts +0 -53
- package/source/directives/dev/Stub.ts +0 -14
- package/source/directives/dev/Throw.ts +0 -14
- package/source/directives/dev/index.ts +0 -3
- package/source/directives/dev/types.ts +0 -5
- package/source/directives/flow/Compose.ts +0 -98
- package/source/directives/flow/Fetch.ts +0 -86
- package/source/directives/flow/Flow.ts +0 -43
- package/source/directives/flow/index.ts +0 -3
- package/source/directives/flow/types.ts +0 -7
- package/source/directives/help/Family.ts +0 -122
- package/source/directives/help/Help.test.ts +0 -227
- package/source/directives/help/Help.ts +0 -47
- package/source/directives/help/Parameters.ts +0 -47
- package/source/directives/help/described.ts +0 -57
- package/source/directives/help/index.ts +0 -45
- package/source/directives/index.ts +0 -33
- package/source/directives/io/Directive.ts +0 -17
- package/source/directives/io/IO.ts +0 -118
- package/source/directives/io/Input.ts +0 -55
- package/source/directives/io/Message.ts +0 -1
- package/source/directives/io/Output.ts +0 -81
- package/source/directives/io/Status.test.ts +0 -88
- package/source/directives/io/Status.ts +0 -47
- package/source/directives/io/Throttle.ts +0 -30
- package/source/directives/io/index.ts +0 -3
- package/source/directives/io/lib/throttle/Configuration.test.ts +0 -72
- package/source/directives/io/lib/throttle/Configuration.ts +0 -68
- package/source/directives/io/lib/throttle/Keys.ts +0 -53
- package/source/directives/io/lib/throttle/Quotas.test.ts +0 -411
- package/source/directives/io/lib/throttle/Quotas.ts +0 -190
- package/source/directives/io/lib/throttle/Sync.test.ts +0 -163
- package/source/directives/io/lib/throttle/Sync.ts +0 -93
- package/source/directives/io/lib/throttle/Warning.test.ts +0 -32
- package/source/directives/io/lib/throttle/Warning.ts +0 -29
- package/source/directives/io/lib/throttle/components/Component.ts +0 -7
- package/source/directives/io/lib/throttle/components/IP.ts +0 -30
- package/source/directives/io/lib/throttle/components/Identity.ts +0 -19
- package/source/directives/io/lib/throttle/components/Path.ts +0 -8
- package/source/directives/io/lib/throttle/components/Route.ts +0 -19
- package/source/directives/io/lib/throttle/components/Segment.ts +0 -19
- package/source/directives/io/lib/throttle/components/index.ts +0 -19
- package/source/directives/io/lib/throttle/conditions/Condition.ts +0 -5
- package/source/directives/io/lib/throttle/conditions/Status.ts +0 -17
- package/source/directives/io/lib/throttle/conditions/index.ts +0 -11
- package/source/directives/io/lib/throttle/index.ts +0 -4
- package/source/directives/io/schemas.test.ts +0 -40
- package/source/directives/io/schemas.ts +0 -16
- package/source/directives/map/Authority.ts +0 -15
- package/source/directives/map/Buffer.ts +0 -15
- package/source/directives/map/Claims.ts +0 -63
- package/source/directives/map/Directive.ts +0 -4
- package/source/directives/map/Headers.ts +0 -60
- package/source/directives/map/Language.ts +0 -45
- package/source/directives/map/Languages.ts +0 -17
- package/source/directives/map/Map.ts +0 -85
- package/source/directives/map/Mapping.ts +0 -45
- package/source/directives/map/Properties.ts +0 -15
- package/source/directives/map/Segments.ts +0 -52
- package/source/directives/map/index.ts +0 -3
- package/source/directives/mcp/MCP.ts +0 -33
- package/source/directives/mcp/Tool.test.ts +0 -56
- package/source/directives/mcp/Tool.ts +0 -32
- package/source/directives/mcp/index.ts +0 -6
- package/source/directives/oauth/Discovery.ts +0 -66
- package/source/directives/oauth/documents.ts +0 -110
- package/source/directives/oauth/index.ts +0 -3
- package/source/directives/octets/Context.ts +0 -23
- package/source/directives/octets/Delete.ts +0 -96
- package/source/directives/octets/Directive.ts +0 -21
- package/source/directives/octets/Get.ts +0 -88
- package/source/directives/octets/Octets.ts +0 -82
- package/source/directives/octets/Put.ts +0 -186
- package/source/directives/octets/Workflow.ts +0 -60
- package/source/directives/octets/bytes.test.ts +0 -33
- package/source/directives/octets/bytes.ts +0 -18
- package/source/directives/octets/index.ts +0 -3
- package/source/directives/octets/schemas.test.ts +0 -20
- package/source/directives/octets/schemas.ts +0 -15
- package/source/directives/octets/types.ts +0 -9
- package/source/directives/octets/workflows/Execution.ts +0 -130
- package/source/directives/octets/workflows/Workflow.ts +0 -51
- package/source/directives/octets/workflows/index.ts +0 -1
- package/source/directives/require/Directive.ts +0 -5
- package/source/directives/require/Headers.ts +0 -19
- package/source/directives/require/Require.ts +0 -27
- package/source/directives/require/index.ts +0 -3
- package/source/discovery.ts +0 -1
- package/source/exceptions.ts +0 -47
- package/source/index.ts +0 -10
- package/source/io.ts +0 -4
- package/source/octets.d.ts +0 -17
- package/source/root.ts +0 -59
- package/source/service.ts +0 -95
- package/source/userland.ts +0 -47
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
- package/ui/dist/_app/immutable/assets/0.Bl7vLmIK.css +0 -2
- package/ui/dist/_app/immutable/assets/2.BC0-Jhqu.css +0 -1
- package/ui/dist/_app/immutable/chunks/BaWe3aOV.js +0 -1
- package/ui/dist/_app/immutable/chunks/DjIYL2zC.js +0 -3
- package/ui/dist/_app/immutable/chunks/GZTfV0ks.js +0 -6
- package/ui/dist/_app/immutable/entry/app.D5v4JoUx.js +0 -2
- package/ui/dist/_app/immutable/entry/start.CWxFoIyZ.js +0 -1
- package/ui/dist/_app/immutable/nodes/0.DIh5T_5-.js +0 -1
- package/ui/dist/_app/immutable/nodes/1.CY-bFzHC.js +0 -1
- package/ui/dist/_app/immutable/nodes/2.6Ubd1ItY.js +0 -14
package/source/Discovery/Site.ts
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs'
|
|
2
|
-
import * as path from 'node:path'
|
|
3
|
-
import { Readable } from 'node:stream'
|
|
4
|
-
import { console } from 'openspan'
|
|
5
|
-
import * as http from '../HTTP/index.js'
|
|
6
|
-
import { DISCOVERY } from '@toa.io/definitions/extensions.exposition'
|
|
7
|
-
import type { Input, Output } from '../io.js'
|
|
8
|
-
import type { Interceptor } from '../Interception.js'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The page that reads the tree: the directory `ui` builds, and nothing else.
|
|
12
|
-
*
|
|
13
|
-
* An interceptor rather than an endpoint, for the reason the OAuth documents are one — it
|
|
14
|
-
* runs before a credential is resolved. A page is public, and one served after `auth` would
|
|
15
|
-
* refuse the client holding a stale token, who is the client most likely to have opened it.
|
|
16
|
-
* It is also what keeps `depart` off these replies: a re-issued credential sets `no-store`,
|
|
17
|
-
* which would take the build hash off every asset that carries one.
|
|
18
|
-
*
|
|
19
|
-
* It claims the whole prefix, so that nothing under it reaches the router — a crawler
|
|
20
|
-
* asking for assets that are not there would otherwise have every one of them counted as a
|
|
21
|
-
* route that has gone missing, and ping discovery about it.
|
|
22
|
-
*
|
|
23
|
-
* `OPTIONS` on the prefix itself is the exception it lets through: the tree is what answers
|
|
24
|
-
* there, and that needs the identity this stage does not have yet.
|
|
25
|
-
*/
|
|
26
|
-
export class Site implements Interceptor {
|
|
27
|
-
public readonly name = 'discovery'
|
|
28
|
-
|
|
29
|
-
/** What a test points at a fixture; the build otherwise. */
|
|
30
|
-
private readonly override: string | undefined
|
|
31
|
-
private root: string
|
|
32
|
-
|
|
33
|
-
public constructor(root?: string) {
|
|
34
|
-
this.override = root
|
|
35
|
-
this.root = root ?? site()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Resolved here rather than at construction because this is a module singleton, built
|
|
40
|
-
* when the module loads and before anything has said where the page is. Said once, too,
|
|
41
|
-
* because a page that is not built is otherwise a `503` per request and nothing else.
|
|
42
|
-
*/
|
|
43
|
-
public mount(): void {
|
|
44
|
-
this.root = this.override ?? site()
|
|
45
|
-
|
|
46
|
-
if (!isFile(path.join(this.root, 'index.html')))
|
|
47
|
-
console.warn('Discovery UI is not built', { root: this.root })
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public intercept(input: Input): Output {
|
|
51
|
-
const { pathname } = input.url
|
|
52
|
-
|
|
53
|
-
if (pathname !== DISCOVERY && !pathname.startsWith(DISCOVERY + '/')) return null
|
|
54
|
-
|
|
55
|
-
// the prefix itself, as opposed to something under it
|
|
56
|
-
const bare = pathname === DISCOVERY || pathname === DISCOVERY + '/'
|
|
57
|
-
const method = input.request.method
|
|
58
|
-
|
|
59
|
-
if (method !== 'GET' && method !== 'HEAD') {
|
|
60
|
-
// the tree answers here, and it needs the identity this stage does not have yet
|
|
61
|
-
if (bare && method === 'OPTIONS') return null
|
|
62
|
-
|
|
63
|
-
throw new http.MethodNotAllowed(
|
|
64
|
-
new Headers({ allow: bare ? 'GET, HEAD, OPTIONS' : 'GET, HEAD' })
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// the page is a directory, and every asset it names is relative to it
|
|
69
|
-
if (pathname === DISCOVERY)
|
|
70
|
-
return {
|
|
71
|
-
status: 302,
|
|
72
|
-
headers: new Headers({ location: DISCOVERY + '/' + input.url.search })
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const file = this.resolve(pathname)
|
|
76
|
-
|
|
77
|
-
if (file === null) throw new http.NotFound()
|
|
78
|
-
|
|
79
|
-
return this.send(file, method)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The file a request lands on, or `null` when nothing does. A path that exists is served
|
|
84
|
-
* as it is; anything else that could be a route falls back to the page, because the
|
|
85
|
-
* client router — not this server — knows what routes there are.
|
|
86
|
-
*/
|
|
87
|
-
private resolve(pathname: string): string | null {
|
|
88
|
-
/*
|
|
89
|
-
* Decoded after the prefix is off, so that an encoded separator cannot smuggle a
|
|
90
|
-
* segment past the test that put us here. A dot segment never arrives — `..` and
|
|
91
|
-
* `%2e%2e` alike are collapsed while the URL is parsed — but `%2f` survives that and
|
|
92
|
-
* decodes to a separator here, which is what the guard below is for.
|
|
93
|
-
*/
|
|
94
|
-
const relative = decode(pathname.slice(DISCOVERY.length))
|
|
95
|
-
|
|
96
|
-
if (relative === null || relative.includes('\0')) return null
|
|
97
|
-
|
|
98
|
-
const file = path.join(this.root, relative)
|
|
99
|
-
|
|
100
|
-
if (file !== this.root && !file.startsWith(this.root + path.sep)) return null
|
|
101
|
-
|
|
102
|
-
if (isFile(file)) return file
|
|
103
|
-
|
|
104
|
-
/*
|
|
105
|
-
* A missing asset is missing, but a route can look like one. What this server would
|
|
106
|
-
* have served is what it knows how to serve, so anything else is a route and falls
|
|
107
|
-
* back to the page — as does anything ending in a slash, which is no name for a file.
|
|
108
|
-
*/
|
|
109
|
-
const asset = !relative.endsWith('/') && path.extname(relative) in TYPES
|
|
110
|
-
|
|
111
|
-
return asset ? null : path.join(this.root, 'index.html')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Always with a `content-type` of its own: a stream without one is framed as
|
|
116
|
-
* `multipart/*` and the page arrives as an envelope of JSON-encoded buffers.
|
|
117
|
-
*/
|
|
118
|
-
private send(file: string, method: string): Output {
|
|
119
|
-
const stats = fs.statSync(file, { throwIfNoEntry: false })
|
|
120
|
-
|
|
121
|
-
if (stats === undefined)
|
|
122
|
-
return {
|
|
123
|
-
status: 503,
|
|
124
|
-
headers: new Headers({ 'content-type': 'text/plain; charset=utf-8' }),
|
|
125
|
-
body: Readable.from([Buffer.from(NOT_BUILT)])
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const headers = new Headers({
|
|
129
|
-
'content-type': TYPES[path.extname(file)] ?? 'application/octet-stream',
|
|
130
|
-
'content-length': String(stats.size),
|
|
131
|
-
'cache-control': caching(path.relative(this.root, file))
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
// a HEAD reply carries no body but reports the length a GET would have returned; the
|
|
135
|
-
// HTTP/2 layer does not drop one written to it, so none is opened
|
|
136
|
-
if (method === 'HEAD') return { status: 200, headers }
|
|
137
|
-
|
|
138
|
-
return { status: 200, headers, body: fs.createReadStream(file) }
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const NOT_BUILT = 'The discovery UI is not built. Run `npm run build:ui`.\n'
|
|
143
|
-
|
|
144
|
-
function decode(pathname: string): string | null {
|
|
145
|
-
try {
|
|
146
|
-
return decodeURIComponent(pathname)
|
|
147
|
-
} catch {
|
|
148
|
-
return null
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function isFile(file: string): boolean {
|
|
153
|
-
return fs.statSync(file, { throwIfNoEntry: false })?.isFile() === true
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Where `npm run build:ui` puts the page. Two levels up because this module is a directory
|
|
158
|
-
* deeper than `source` itself, and `transpiled` mirrors that — so it resolves the same from
|
|
159
|
-
* either. The variable is the features', which run against a fixture rather than a build.
|
|
160
|
-
*/
|
|
161
|
-
function site(): string {
|
|
162
|
-
return (
|
|
163
|
-
process.env.__TESTING_EXPOSITION_DISCOVERY_ROOT ??
|
|
164
|
-
path.resolve(import.meta.dirname, '..', '..', 'ui', 'dist')
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/** Assets under this prefix carry their build hash in the name. */
|
|
169
|
-
const IMMUTABLE = path.join('_app', 'immutable')
|
|
170
|
-
const FOREVER = 'public, max-age=31536000, immutable'
|
|
171
|
-
|
|
172
|
-
/** The icons: named without a hash, so they are re-read, but rarely. */
|
|
173
|
-
const ICONS = new Set(['favicon.ico', 'favicon-96x96.png', 'apple-touch-icon.png'])
|
|
174
|
-
const DAY = 'public, max-age=86400'
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* How long what is served may be held. The page is never cached — it names the assets, and
|
|
178
|
-
* their names carry the build. An icon is named for what it is rather than for its content,
|
|
179
|
-
* so it is asked about again, but not on every page load.
|
|
180
|
-
*/
|
|
181
|
-
function caching(relative: string): string {
|
|
182
|
-
if (relative.startsWith(IMMUTABLE)) return FOREVER
|
|
183
|
-
|
|
184
|
-
return ICONS.has(relative) ? DAY : 'no-cache'
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const TYPES: Record<string, string> = {
|
|
188
|
-
'.css': 'text/css; charset=utf-8',
|
|
189
|
-
'.html': 'text/html; charset=utf-8',
|
|
190
|
-
'.ico': 'image/x-icon',
|
|
191
|
-
'.jpg': 'image/jpeg',
|
|
192
|
-
'.js': 'text/javascript; charset=utf-8',
|
|
193
|
-
'.json': 'application/json; charset=utf-8',
|
|
194
|
-
'.map': 'application/json; charset=utf-8',
|
|
195
|
-
'.png': 'image/png',
|
|
196
|
-
'.svg': 'image/svg+xml',
|
|
197
|
-
'.txt': 'text/plain; charset=utf-8',
|
|
198
|
-
'.webmanifest': 'application/manifest+json',
|
|
199
|
-
'.webp': 'image/webp',
|
|
200
|
-
'.woff': 'font/woff',
|
|
201
|
-
'.woff2': 'font/woff2'
|
|
202
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { describe as suite, it, afterEach } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import { describe } from './tree.js'
|
|
5
|
-
import { Tree } from '../RTD/Tree.js'
|
|
6
|
-
import type * as http from '../HTTP/index.js'
|
|
7
|
-
import type { Introspection } from '../Introspection.js'
|
|
8
|
-
import type { EndpointsFactory } from '../Endpoint.js'
|
|
9
|
-
import type { DirectiveFactory } from '../RTD/Directives.js'
|
|
10
|
-
import type * as syntax from '../RTD/syntax/index.js'
|
|
11
|
-
|
|
12
|
-
/** A method with no mapping has no endpoint, so what it says is its directives' alone. */
|
|
13
|
-
const endpoints = {} as unknown as EndpointsFactory
|
|
14
|
-
|
|
15
|
-
/** What a method marked with this describes as, standing for a directive that refuses. */
|
|
16
|
-
const REFUSE = 'refuse'
|
|
17
|
-
|
|
18
|
-
const directives = {
|
|
19
|
-
create: (stack: syntax.Directive[]) => ({
|
|
20
|
-
declared: () => undefined,
|
|
21
|
-
precall: async () => null,
|
|
22
|
-
settle: async () => undefined,
|
|
23
|
-
dispose: () => undefined,
|
|
24
|
-
explain: async (_: unknown, introspection: Introspection) => {
|
|
25
|
-
const mark = stack.find((directive) => directive.family === 'test')?.value
|
|
26
|
-
|
|
27
|
-
if (mark === REFUSE) return null
|
|
28
|
-
|
|
29
|
-
return mark === undefined ? introspection : { ...introspection, description: mark }
|
|
30
|
-
}
|
|
31
|
-
}),
|
|
32
|
-
preflight: async () => undefined,
|
|
33
|
-
depart: async () => undefined,
|
|
34
|
-
dispose: () => undefined
|
|
35
|
-
} as unknown as DirectiveFactory
|
|
36
|
-
|
|
37
|
-
const method = (verb: string, mark?: string): syntax.Method => ({
|
|
38
|
-
verb,
|
|
39
|
-
directives: mark === undefined ? [] : [{ family: 'test', name: 'mark', value: mark }]
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
const node = (
|
|
43
|
-
path: string,
|
|
44
|
-
methods: syntax.Method[] = [],
|
|
45
|
-
routes: syntax.Route[] = []
|
|
46
|
-
): syntax.Route => ({ path, node: { routes, methods, directives: [] } })
|
|
47
|
-
|
|
48
|
-
const trunk = (routes: syntax.Route[], methods: syntax.Method[] = []): syntax.Node => ({
|
|
49
|
-
routes,
|
|
50
|
-
methods,
|
|
51
|
-
directives: []
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const request = {} as unknown as http.Context
|
|
55
|
-
|
|
56
|
-
afterEach(() => {
|
|
57
|
-
delete process.env.__TESTING_EXPOSITION_BRANCH_TTL
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
suite('discovery tree', () => {
|
|
61
|
-
it('should key the trunk by the root', async () => {
|
|
62
|
-
const tree = new Tree(trunk([], [method('GET')]), endpoints, directives)
|
|
63
|
-
|
|
64
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), ['/'])
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should key every route by the template it answers at', async () => {
|
|
68
|
-
const tree = new Tree(
|
|
69
|
-
trunk([
|
|
70
|
-
node('/pots', [method('GET'), method('POST')], [node('/:id', [method('GET')])])
|
|
71
|
-
]),
|
|
72
|
-
endpoints,
|
|
73
|
-
directives
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
const { routes: resources } = await describe(tree, request)
|
|
77
|
-
|
|
78
|
-
assert.deepEqual(Object.keys(resources), ['/pots', '/pots/:id'])
|
|
79
|
-
assert.deepEqual(Object.keys(resources['/pots']), ['GET', 'POST'])
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it('should include a route no name can spell', async () => {
|
|
83
|
-
// `/.rpc` and `/.mcp` leave these out; HTTP addresses a route by its path
|
|
84
|
-
const tree = new Tree(
|
|
85
|
-
trunk([
|
|
86
|
-
node('/v1.0', [method('GET')]),
|
|
87
|
-
node('/files', [], [node('/**', [method('GET')])]),
|
|
88
|
-
node('/any', [], [node('/*', [method('GET')])])
|
|
89
|
-
]),
|
|
90
|
-
endpoints,
|
|
91
|
-
directives
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), [
|
|
95
|
-
'/any/*',
|
|
96
|
-
'/files/**',
|
|
97
|
-
'/v1.0'
|
|
98
|
-
])
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('should skip an intermediate node, whose route answers in its place', async () => {
|
|
102
|
-
const tree = new Tree(
|
|
103
|
-
trunk([node('/posts', [method('PATCH')], [node('/', [method('PUT')])])]),
|
|
104
|
-
endpoints,
|
|
105
|
-
directives
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), ['/posts'])
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it('should omit a resource this caller may reach no method of', async () => {
|
|
112
|
-
const tree = new Tree(
|
|
113
|
-
trunk([
|
|
114
|
-
node('/open', [method('GET')]),
|
|
115
|
-
node('/closed', [method('GET', REFUSE), method('POST', REFUSE)])
|
|
116
|
-
]),
|
|
117
|
-
endpoints,
|
|
118
|
-
directives
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), ['/open'])
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('should carry only the verbs this caller may reach', async () => {
|
|
125
|
-
const tree = new Tree(
|
|
126
|
-
trunk([node('/pots', [method('GET'), method('POST', REFUSE)])]),
|
|
127
|
-
endpoints,
|
|
128
|
-
directives
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
const { routes: resources } = await describe(tree, request)
|
|
132
|
-
|
|
133
|
-
assert.deepEqual(Object.keys(resources['/pots']), ['GET'])
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should answer the declaration a request would reach where two share a template', async () => {
|
|
137
|
-
// `/a/b` and `/a: { /b: }` are two routes and one template; the more specific matches
|
|
138
|
-
const tree = new Tree(
|
|
139
|
-
trunk([
|
|
140
|
-
node('/a/b', [method('GET', 'nested')]),
|
|
141
|
-
node('/a', [], [node('/b', [method('GET', 'adjacent')])])
|
|
142
|
-
]),
|
|
143
|
-
endpoints,
|
|
144
|
-
directives
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
const { routes: resources } = await describe(tree, request)
|
|
148
|
-
|
|
149
|
-
assert.deepEqual(Object.keys(resources), ['/a/b'])
|
|
150
|
-
assert.equal(resources['/a/b'].GET.description, 'nested')
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('should drop a branch that has expired', async () => {
|
|
154
|
-
process.env.__TESTING_EXPOSITION_BRANCH_TTL = '-1'
|
|
155
|
-
|
|
156
|
-
const tree = new Tree(trunk([]), endpoints, directives)
|
|
157
|
-
|
|
158
|
-
tree.merge(trunk([node('/pots', [method('GET')])]), {
|
|
159
|
-
namespace: 'default',
|
|
160
|
-
component: 'pots'
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), [])
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('should sort, because branches merge in the order their tenants answer', async () => {
|
|
167
|
-
const tree = new Tree(
|
|
168
|
-
trunk([node('/zed', [method('GET')]), node('/alpha', [method('GET')])]),
|
|
169
|
-
endpoints,
|
|
170
|
-
directives
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
assert.deepEqual(Object.keys((await describe(tree, request)).routes), ['/alpha', '/zed'])
|
|
174
|
-
})
|
|
175
|
-
})
|
package/source/Discovery/tree.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describing } from '../Introspection.js'
|
|
2
|
-
import { template } from '../RPC/names.js'
|
|
3
|
-
import { variables } from '../RTD/segment.js'
|
|
4
|
-
import { guarded, resource, type Described } from '../directives/help/index.js'
|
|
5
|
-
import type * as http from '../HTTP/index.js'
|
|
6
|
-
import type { Tree } from '../RTD/index.js'
|
|
7
|
-
import type { Introspection } from '../Introspection.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Every route in the tree this caller may reach, keyed by the template it answers at, and
|
|
11
|
-
* each described exactly as `OPTIONS` on that path describes it — so a key can be taken
|
|
12
|
-
* from the answer and sent back as a request.
|
|
13
|
-
*
|
|
14
|
-
* A route a name cannot spell is here, unlike at `/.rpc` and `/.mcp`: HTTP addresses a
|
|
15
|
-
* route by its path, so leaving one out would make this disagree with the router. A
|
|
16
|
-
* resource no method of which this caller may reach is not here at all — `OPTIONS` answers
|
|
17
|
-
* `403` to one, and an empty entry would still say it exists.
|
|
18
|
-
*
|
|
19
|
-
* Sorted, because a branch is merged whenever its tenant answers and two replicas would
|
|
20
|
-
* otherwise order the same tree differently. Verbs keep the order they were declared in,
|
|
21
|
-
* which is the order `OPTIONS` answers them in.
|
|
22
|
-
*/
|
|
23
|
-
export async function describe(tree: Tree, request: http.Context): Promise<Discovered> {
|
|
24
|
-
const context = describing(request)
|
|
25
|
-
const routes = new Map<string, Mounted>()
|
|
26
|
-
|
|
27
|
-
/*
|
|
28
|
-
* Sequentially: there is no I/O to overlap — an endpoint's own description is read once
|
|
29
|
-
* and memoized — so awaiting the whole tree at once would only allocate every clone of
|
|
30
|
-
* it at the same moment.
|
|
31
|
-
*/
|
|
32
|
-
for (const { segments, verb, method } of tree.walk()) {
|
|
33
|
-
const introspection = await method.explain(context, variables(segments))
|
|
34
|
-
|
|
35
|
-
if (introspection === null) continue
|
|
36
|
-
|
|
37
|
-
const route = template(segments)
|
|
38
|
-
let mounted = routes.get(route)
|
|
39
|
-
|
|
40
|
-
if (mounted === undefined) {
|
|
41
|
-
mounted = { described: resource(method.directives), methods: {} }
|
|
42
|
-
routes.set(route, mounted)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// two declarations can walk to one template — `/a/b` and `/a: { /b: }` — and the walk
|
|
46
|
-
// is in the order `match` tries them, so the first is the one a request would reach
|
|
47
|
-
mounted.methods[verb] ??= introspection
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const described: Record<string, Resource> = {}
|
|
51
|
-
|
|
52
|
-
for (const route of Array.from(routes.keys()).sort()) {
|
|
53
|
-
const { described: stated, methods } = routes.get(route)!
|
|
54
|
-
|
|
55
|
-
// what the resource is, beside the methods it serves; a verb is upper case and cannot
|
|
56
|
-
// collide with either key
|
|
57
|
-
described[route] = { ...stated, ...guarded(methods), ...methods }
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return { routes: described }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** What the tree answers. An object, so that what is said of the whole of it has somewhere to go. */
|
|
64
|
-
export interface Discovered {
|
|
65
|
-
routes: Record<string, Resource>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** What one resource is, and what it serves; a verb is upper case, and nothing else here is. */
|
|
69
|
-
export interface Resource extends Described {
|
|
70
|
-
[verb: string]: unknown
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** One route template as the walk found it, before the two are answered as one. */
|
|
74
|
-
interface Mounted {
|
|
75
|
-
described: Described | null
|
|
76
|
-
methods: Record<string, Introspection>
|
|
77
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import Negotiator from 'negotiator'
|
|
2
|
-
import { types } from '../HTTP/formats/index.js'
|
|
3
|
-
import { DISCOVERY } from '@toa.io/definitions/extensions.exposition'
|
|
4
|
-
import type { Context, OutgoingMessage } from '../HTTP/index.js'
|
|
5
|
-
|
|
6
|
-
/** What a browser asks for, and what nothing else here answers with. */
|
|
7
|
-
const HTML = 'text/html'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Where a browser that asked for the trunk is sent. An application serves what it declares,
|
|
11
|
-
* and `/` is usually not one of those — a person who typed the address into a browser is
|
|
12
|
-
* looking for something to look at, and the page is the only thing here that is one.
|
|
13
|
-
*
|
|
14
|
-
* A client that asked for something in particular gets what it asked for: only an `accept`
|
|
15
|
-
* that prefers a page, or one that names no preference at all, is sent here. The second is
|
|
16
|
-
* what an unfurler sends — several ask for anything, and a link to an application would
|
|
17
|
-
* otherwise show nothing — and what `curl` sends, which the page serves no worse than the
|
|
18
|
-
* `405` it used to get.
|
|
19
|
-
*/
|
|
20
|
-
export function looking(context: Context): OutgoingMessage | null {
|
|
21
|
-
if (context.request.method !== 'GET' || context.procedural) return null
|
|
22
|
-
|
|
23
|
-
const accept = context.request.headers.accept
|
|
24
|
-
|
|
25
|
-
if (accept !== undefined && accept.trim() !== ANYTHING) {
|
|
26
|
-
if (new Negotiator(context.request).mediaType([...types, HTML]) !== HTML) return null
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return { status: 302, headers: new Headers({ location: DISCOVERY + '/' }) }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** An `accept` that names nothing, which is the same as naming none. */
|
|
33
|
-
const ANYTHING = '*/*'
|
package/source/Endpoint.test.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import { Endpoint } from './Endpoint.js'
|
|
5
|
-
import { Mapping } from './Mapping.js'
|
|
6
|
-
import type { Remote } from '@toa.io/core'
|
|
7
|
-
import type { Parameter } from './RTD/index.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* What `Remote.explain` answers is the contract's own object, the same one every time and
|
|
11
|
-
* on every route that mounts the endpoint — which is what this stands in for.
|
|
12
|
-
*/
|
|
13
|
-
function remote(): Promise<Remote> {
|
|
14
|
-
const explanation = {
|
|
15
|
-
input: {
|
|
16
|
-
type: 'object',
|
|
17
|
-
properties: {
|
|
18
|
-
a: { type: 'string' },
|
|
19
|
-
b: { type: 'string' }
|
|
20
|
-
},
|
|
21
|
-
required: ['a', 'b']
|
|
22
|
-
},
|
|
23
|
-
output: null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return Promise.resolve({
|
|
27
|
-
explain: async () => explanation
|
|
28
|
-
} as unknown as Remote)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const endpoint = (discovery: Promise<Remote>): Endpoint =>
|
|
32
|
-
new Endpoint('parameters', Mapping.create(), discovery)
|
|
33
|
-
|
|
34
|
-
const variable: Parameter[] = [{ name: 'a', value: 'a1b2' }]
|
|
35
|
-
|
|
36
|
-
describe('explain', () => {
|
|
37
|
-
it('should state a route variable as the route, not as the input', async () => {
|
|
38
|
-
const introspection = await endpoint(remote()).explain(variable)
|
|
39
|
-
|
|
40
|
-
assert.deepEqual(Object.keys(introspection.route ?? {}), ['a'])
|
|
41
|
-
assert.deepEqual(Object.keys((introspection.input as any).properties), ['b'])
|
|
42
|
-
assert.deepEqual((introspection.input as any).required, ['b'])
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should leave the operation for the next route that mounts it', async () => {
|
|
46
|
-
const discovery = remote()
|
|
47
|
-
|
|
48
|
-
await endpoint(discovery).explain(variable)
|
|
49
|
-
|
|
50
|
-
// a second route on the same endpoint, taking no variable of its own
|
|
51
|
-
const introspection = await endpoint(discovery).explain([])
|
|
52
|
-
|
|
53
|
-
assert.deepEqual(Object.keys((introspection.input as any).properties), ['a', 'b'])
|
|
54
|
-
assert.deepEqual((introspection.input as any).required, ['a', 'b'])
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('should answer a copy, so what one caller narrows the next still sees', async () => {
|
|
58
|
-
const one = endpoint(remote())
|
|
59
|
-
const first = await one.explain(variable)
|
|
60
|
-
|
|
61
|
-
delete (first.input as any).properties.b
|
|
62
|
-
|
|
63
|
-
const second = await one.explain(variable)
|
|
64
|
-
|
|
65
|
-
assert.deepEqual(Object.keys((second.input as any).properties), ['b'])
|
|
66
|
-
})
|
|
67
|
-
})
|