@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/Endpoint.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { Readable } from 'node:stream'
|
|
2
|
-
import { Mapping } from './Mapping.js'
|
|
3
|
-
import { take } from './Introspection.js'
|
|
4
|
-
import { parse } from './directives/cache/etag.js'
|
|
5
|
-
import * as http from './HTTP/index.js'
|
|
6
|
-
import type { Introspection, Schema } from './Introspection.js'
|
|
7
|
-
import type { Remote } from '@toa.io/core'
|
|
8
|
-
import type { Remotes } from './Remotes.js'
|
|
9
|
-
import type { Context } from './Context.js'
|
|
10
|
-
import type * as RTD from './RTD/index.js'
|
|
11
|
-
|
|
12
|
-
export class Endpoint implements RTD.Endpoint {
|
|
13
|
-
private readonly endpoint: string
|
|
14
|
-
private readonly mapping: Mapping
|
|
15
|
-
private readonly discovery: Promise<Remote>
|
|
16
|
-
private remote: Remote | null = null
|
|
17
|
-
|
|
18
|
-
/** What the operation says, with what the route takes already split out of it. */
|
|
19
|
-
private introspection: Introspection | null = null
|
|
20
|
-
|
|
21
|
-
public constructor(endpoint: string, mapping: Mapping, discovery: Promise<Remote>) {
|
|
22
|
-
this.endpoint = endpoint
|
|
23
|
-
this.mapping = mapping
|
|
24
|
-
this.discovery = discovery
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public async call(
|
|
28
|
-
context: http.Context,
|
|
29
|
-
parameters: RTD.Parameter[]
|
|
30
|
-
): Promise<http.OutgoingMessage> {
|
|
31
|
-
const body = await context.body()
|
|
32
|
-
const query = this.query(context)
|
|
33
|
-
const request = this.mapping.fit(body, query, parameters)
|
|
34
|
-
|
|
35
|
-
this.remote ??= await this.discovery
|
|
36
|
-
|
|
37
|
-
const reply = await this.remote.invoke(this.endpoint, request)
|
|
38
|
-
|
|
39
|
-
if (reply instanceof Error) throw new http.UnprocessableEntity(reply)
|
|
40
|
-
|
|
41
|
-
const message: http.OutgoingMessage = { body: reply }
|
|
42
|
-
|
|
43
|
-
// what the reply carries for a cache to validate by; the `cache` family sets the headers
|
|
44
|
-
if (typeof reply === 'object' && reply !== null && !(reply instanceof Readable)) {
|
|
45
|
-
if ('VERSION' in reply) message.version = reply.VERSION
|
|
46
|
-
|
|
47
|
-
const modified = reply.UPDATED ?? reply.CREATED
|
|
48
|
-
|
|
49
|
-
if (modified !== undefined) message.modified = modified
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return message
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public selection(): Record<string, Schema> | null {
|
|
56
|
-
return this.mapping.selection()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public async explain(parameters: RTD.Parameter[]): Promise<Introspection> {
|
|
60
|
-
this.introspection ??= await this.introspect(parameters)
|
|
61
|
-
|
|
62
|
-
// what a directive narrows is this caller's answer, not the next caller's
|
|
63
|
-
return structuredClone(this.introspection)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* `Remote.explain` answers the contract's own object, whose `input` is the manifest's by
|
|
68
|
-
* reference. What follows takes properties out of it, and two routes mounting one endpoint
|
|
69
|
-
* share one remote — so the copy is what keeps the second from describing what the first
|
|
70
|
-
* took away.
|
|
71
|
-
*/
|
|
72
|
-
private async introspect(parameters: RTD.Parameter[]): Promise<Introspection> {
|
|
73
|
-
this.remote ??= await this.discovery
|
|
74
|
-
|
|
75
|
-
const operation = structuredClone(await this.remote.explain(this.endpoint))
|
|
76
|
-
|
|
77
|
-
// what the operation states it is, is the Introspection's to read and not a resource's:
|
|
78
|
-
// an operation is written without knowledge of any route, and the same one mounted twice
|
|
79
|
-
// is two methods. What a method states is what its route states, which `mcp:tool` gives.
|
|
80
|
-
delete operation.description
|
|
81
|
-
|
|
82
|
-
let route: Record<string, Schema> | null = null
|
|
83
|
-
|
|
84
|
-
// a variable the operation names is taken by the path, so it is not the body's to send
|
|
85
|
-
for (const parameter of parameters) {
|
|
86
|
-
const schema = take(operation, parameter.name)
|
|
87
|
-
|
|
88
|
-
if (schema === undefined) continue
|
|
89
|
-
|
|
90
|
-
route ??= {}
|
|
91
|
-
route[parameter.name] = schema
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const query = this.mapping.explain(operation)
|
|
95
|
-
const introspection: Introspection = {}
|
|
96
|
-
|
|
97
|
-
if (route !== null) introspection.route = route
|
|
98
|
-
|
|
99
|
-
if (query !== null) introspection.query = query
|
|
100
|
-
|
|
101
|
-
Object.assign(introspection, operation)
|
|
102
|
-
|
|
103
|
-
return introspection
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public async close(): Promise<void> {
|
|
107
|
-
this.remote ??= await this.discovery
|
|
108
|
-
|
|
109
|
-
await this.remote.disconnect(INTERRUPT)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private query(context: http.Context): http.Query {
|
|
113
|
-
const query: http.Query = Object.fromEntries(context.url.searchParams)
|
|
114
|
-
const etag = context.request.headers['if-match']
|
|
115
|
-
|
|
116
|
-
if (etag !== undefined && this.mapping.queryable) query.version = this.version(etag)
|
|
117
|
-
|
|
118
|
-
return query
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
private version(etag: string): number {
|
|
122
|
-
const version = parse(etag)
|
|
123
|
-
|
|
124
|
-
if (version === null) throw new http.BadRequest('Invalid ETag')
|
|
125
|
-
|
|
126
|
-
return version
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export class EndpointsFactory implements RTD.EndpointsFactory {
|
|
131
|
-
private readonly remotes: Remotes
|
|
132
|
-
|
|
133
|
-
public constructor(remotes: Remotes) {
|
|
134
|
-
this.remotes = remotes
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
public create(method: RTD.syntax.Method, context: Context): Endpoint {
|
|
138
|
-
if (method.mapping === undefined)
|
|
139
|
-
throw new Error('Cannot create Endpoint without mapping')
|
|
140
|
-
|
|
141
|
-
const mapping = Mapping.create(method.mapping.query, method.mapping.paged)
|
|
142
|
-
|
|
143
|
-
const branch = context.extension
|
|
144
|
-
|
|
145
|
-
const namespace = method.mapping.namespace ?? branch?.namespace
|
|
146
|
-
const component = method.mapping.component ?? branch?.component
|
|
147
|
-
|
|
148
|
-
if (namespace === undefined || component === undefined)
|
|
149
|
-
throw new Error('Annotation endpoints must be fully qualified')
|
|
150
|
-
|
|
151
|
-
const discovery = this.remotes.discover(namespace, component, branch?.version)
|
|
152
|
-
|
|
153
|
-
return new Endpoint(method.mapping.endpoint, mapping, discovery)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const INTERRUPT = true
|
package/source/Factory.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto'
|
|
2
|
-
import { Tenant } from './Tenant.js'
|
|
3
|
-
import { CHANNEL } from '@toa.io/definitions/extensions.exposition'
|
|
4
|
-
import type { Branch } from './Branch.js'
|
|
5
|
-
import type { syntax } from './RTD/index.js'
|
|
6
|
-
import type { Broadcast } from './Gateway.js'
|
|
7
|
-
import type { Connector, Locator } from '@toa.io/core'
|
|
8
|
-
import type { extensions } from '@toa.io/core/types'
|
|
9
|
-
|
|
10
|
-
export class Factory implements extensions.Factory {
|
|
11
|
-
private readonly host: Host
|
|
12
|
-
|
|
13
|
-
public constructor(host: Host) {
|
|
14
|
-
this.host = host
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
public async tenant(locator: Locator, node: syntax.Node): Promise<Connector> {
|
|
18
|
-
const broadcast: Broadcast = await this.host.broadcast(CHANNEL, locator.id)
|
|
19
|
-
const hash = createHash('sha256').update(JSON.stringify(node)).digest('hex')
|
|
20
|
-
|
|
21
|
-
// no timestamp: the tenant stamps each announcement with its own start time
|
|
22
|
-
const branch: Omit<Branch, 'timestamp'> = {
|
|
23
|
-
// boot only ever hands a tenant a component's locator, which always has a namespace
|
|
24
|
-
namespace: locator.namespace!,
|
|
25
|
-
component: locator.name,
|
|
26
|
-
isolated: locator.namespace === 'identity',
|
|
27
|
-
node,
|
|
28
|
-
version: hash
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return new Tenant(broadcast, branch)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public async service(): Promise<Connector | null> {
|
|
35
|
-
const { service } = await import('./service.js')
|
|
36
|
-
|
|
37
|
-
return await service(this.host)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
42
|
-
export type Host = extensions.Host
|
package/source/Gateway.ts
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { setTimeout } from 'node:timers/promises'
|
|
3
|
-
import { console } from 'openspan'
|
|
4
|
-
import { Connector } from '@toa.io/core'
|
|
5
|
-
import type { bindings } from '@toa.io/core/types'
|
|
6
|
-
import * as http from './HTTP/index.js'
|
|
7
|
-
import { DISCOVERY, MCP, RPC } from '@toa.io/definitions/extensions.exposition'
|
|
8
|
-
import { rethrow } from './exceptions.js'
|
|
9
|
-
import { decide } from './Branch.js'
|
|
10
|
-
import { describing } from './Introspection.js'
|
|
11
|
-
import { Explorer, looking } from './Discovery/index.js'
|
|
12
|
-
import type { Interception } from './Interception.js'
|
|
13
|
-
import type { Dispatcher } from './RPC/index.js'
|
|
14
|
-
import type { Server } from './MCP/index.js'
|
|
15
|
-
import type {
|
|
16
|
-
DirectiveFactory,
|
|
17
|
-
Method,
|
|
18
|
-
Node,
|
|
19
|
-
Parameter,
|
|
20
|
-
Tree,
|
|
21
|
-
Match
|
|
22
|
-
} from './RTD/index.js'
|
|
23
|
-
import type { Label } from './discovery.js'
|
|
24
|
-
import type { Branch, Exposed } from './Branch.js'
|
|
25
|
-
|
|
26
|
-
export class Gateway extends Connector {
|
|
27
|
-
private readonly broadcast: Broadcast
|
|
28
|
-
private readonly tree: Tree
|
|
29
|
-
private readonly interceptor: Interception
|
|
30
|
-
private readonly directives: DirectiveFactory
|
|
31
|
-
|
|
32
|
-
/** JSON-RPC is served only where the annotation asked for it. */
|
|
33
|
-
private readonly dispatcher: Dispatcher | null
|
|
34
|
-
|
|
35
|
-
/** And the same of MCP. */
|
|
36
|
-
private readonly mcp: Server | null
|
|
37
|
-
|
|
38
|
-
/** The tree, for every route at once. Served always, and so not nullable. */
|
|
39
|
-
private readonly explorer: Explorer
|
|
40
|
-
private readonly branches = new Map<string, Exposed>()
|
|
41
|
-
private lastMerge = 0
|
|
42
|
-
private widestGap = 0
|
|
43
|
-
private lastPing = 0
|
|
44
|
-
private stopped = false
|
|
45
|
-
private resolveFirstMerge: (() => void) | null = null
|
|
46
|
-
|
|
47
|
-
// eslint-disable-next-line max-params
|
|
48
|
-
public constructor(
|
|
49
|
-
broadcast: Broadcast,
|
|
50
|
-
tree: Tree,
|
|
51
|
-
interception: Interception,
|
|
52
|
-
directives: DirectiveFactory,
|
|
53
|
-
dispatcher: Dispatcher | null,
|
|
54
|
-
mcp: Server | null
|
|
55
|
-
) {
|
|
56
|
-
super()
|
|
57
|
-
|
|
58
|
-
this.broadcast = broadcast
|
|
59
|
-
this.tree = tree
|
|
60
|
-
this.interceptor = interception
|
|
61
|
-
this.directives = directives
|
|
62
|
-
this.dispatcher = dispatcher
|
|
63
|
-
this.mcp = mcp
|
|
64
|
-
this.explorer = new Explorer(tree)
|
|
65
|
-
|
|
66
|
-
this.depends(broadcast)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
public async process(context: http.Context): Promise<http.OutgoingMessage> {
|
|
70
|
-
const interception = await this.interceptor.intercept(context)
|
|
71
|
-
|
|
72
|
-
if (interception !== null) return interception
|
|
73
|
-
|
|
74
|
-
// request-scoped, before anything is routed: this is where a credential is read
|
|
75
|
-
await this.directives.preflight(context).catch(rethrow)
|
|
76
|
-
|
|
77
|
-
const response = await this.endpoint(context)
|
|
78
|
-
|
|
79
|
-
// request-scoped, on whatever is going back: this is where a credential is re-issued
|
|
80
|
-
await this.directives.depart(context, response).catch(rethrow)
|
|
81
|
-
|
|
82
|
-
return response
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* What answers the request: a pinned endpoint that makes calls of its own, or the route
|
|
87
|
-
* the path names.
|
|
88
|
-
*/
|
|
89
|
-
private async endpoint(context: http.Context): Promise<http.OutgoingMessage> {
|
|
90
|
-
const route: http.Processor = async (call) => await this.route(call)
|
|
91
|
-
|
|
92
|
-
if (this.dispatcher !== null && context.url.pathname === RPC)
|
|
93
|
-
return await this.dispatcher.dispatch(context, route)
|
|
94
|
-
|
|
95
|
-
if (this.mcp !== null && context.url.pathname === MCP)
|
|
96
|
-
return await this.mcp.process(context, route)
|
|
97
|
-
|
|
98
|
-
// the page under this prefix is an interceptor's, and has already answered
|
|
99
|
-
if (context.url.pathname === DISCOVERY || context.url.pathname === DISCOVERY + '/')
|
|
100
|
-
return await this.explorer.process(context)
|
|
101
|
-
|
|
102
|
-
// a browser that asked for the trunk of an API is looking for the page, and where the
|
|
103
|
-
// application answers there itself, that is what answers
|
|
104
|
-
if (context.url.pathname === '/' && !this.serves(context)) {
|
|
105
|
-
const page = looking(context)
|
|
106
|
-
|
|
107
|
-
if (page !== null) return page
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return await this.route(context)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** Whether the application serves the trunk with the verb the request was made with. */
|
|
114
|
-
private serves(context: http.Context): boolean {
|
|
115
|
-
const match = this.tree.match('/')
|
|
116
|
-
|
|
117
|
-
return match !== null && context.request.method in match.node.methods
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* One call: everything that needs a node. A request makes one of these, and a request
|
|
122
|
-
* that carries several calls makes one per call.
|
|
123
|
-
*/
|
|
124
|
-
private async route(context: http.Context): Promise<http.OutgoingMessage> {
|
|
125
|
-
const { node, parameters } = this.match(context)
|
|
126
|
-
|
|
127
|
-
if (context.request.method === 'OPTIONS')
|
|
128
|
-
return await this.explain(context, node, parameters)
|
|
129
|
-
|
|
130
|
-
let verb = context.request.method
|
|
131
|
-
|
|
132
|
-
if (!(verb in node.methods) && verb === 'HEAD' && 'GET' in node.methods) verb = 'GET'
|
|
133
|
-
|
|
134
|
-
if (!(verb in node.methods)) throw new http.MethodNotAllowed()
|
|
135
|
-
|
|
136
|
-
const method = node.methods[verb]
|
|
137
|
-
|
|
138
|
-
const interruption = await method.directives
|
|
139
|
-
.precall(context, parameters)
|
|
140
|
-
.catch(rethrow)
|
|
141
|
-
const response = interruption ?? (await this.call(method, context, parameters))
|
|
142
|
-
|
|
143
|
-
await method.directives.settle(context, response).catch(rethrow)
|
|
144
|
-
|
|
145
|
-
return response
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
protected override async open(): Promise<void> {
|
|
149
|
-
await this.discover()
|
|
150
|
-
|
|
151
|
-
// what is served besides the tree, so that turning one on can be seen to have worked
|
|
152
|
-
const pinned: string[] = []
|
|
153
|
-
|
|
154
|
-
if (this.dispatcher !== null) pinned.push(RPC)
|
|
155
|
-
|
|
156
|
-
if (this.mcp !== null) pinned.push(MCP)
|
|
157
|
-
|
|
158
|
-
console.info('Gateway started', {
|
|
159
|
-
endpoints: pinned.length === 0 ? 'none' : pinned.join(' ')
|
|
160
|
-
})
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Both of these reach into components, and a dependency is torn down only once this has
|
|
165
|
-
* returned — where `dispose` runs after every one of them already has. The throttling
|
|
166
|
-
* ticker firing in between calls an endpoint that has just been unbound, and reports the
|
|
167
|
-
* refusal as a failure to reconcile.
|
|
168
|
-
*/
|
|
169
|
-
protected override async close(): Promise<void> {
|
|
170
|
-
this.stopped = true
|
|
171
|
-
|
|
172
|
-
this.tree.dispose()
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
protected override dispose(): void {
|
|
176
|
-
console.info('Gateway is closed')
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
private match(context: http.Context): Match {
|
|
180
|
-
const match = this.tree.match(context.url.pathname)
|
|
181
|
-
|
|
182
|
-
if (match === null) {
|
|
183
|
-
// the route may be missing because an expose has been lost
|
|
184
|
-
this.reping()
|
|
185
|
-
|
|
186
|
-
throw new http.NotFound('Route not found')
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (match.node.forward === null) return match
|
|
190
|
-
|
|
191
|
-
const destination = match.node.forward.replace(/\/:([^/]+)/g, (_, name) => {
|
|
192
|
-
const value = match.parameters.find((parameter) => parameter.name === name)?.value
|
|
193
|
-
|
|
194
|
-
assert.ok(value !== undefined, `Forwarded parameter '${name}' not found`)
|
|
195
|
-
|
|
196
|
-
return `/${value}`
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
const forward = this.tree.match(destination)
|
|
200
|
-
|
|
201
|
-
assert.ok(forward !== null, 'Forwarded route not found')
|
|
202
|
-
|
|
203
|
-
return forward
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private async call(
|
|
207
|
-
method: Method,
|
|
208
|
-
context: http.Context,
|
|
209
|
-
parameters: Parameter[]
|
|
210
|
-
): Promise<http.OutgoingMessage> {
|
|
211
|
-
if (context.url.pathname[context.url.pathname.length - 1] !== '/')
|
|
212
|
-
throw new http.NotFound('Trailing slash is required')
|
|
213
|
-
|
|
214
|
-
if (context.encoder === null) throw new http.NotAcceptable()
|
|
215
|
-
|
|
216
|
-
if (method.endpoint === null) throw new http.MethodNotAllowed()
|
|
217
|
-
|
|
218
|
-
return (await method.endpoint
|
|
219
|
-
.call(context, parameters)
|
|
220
|
-
.catch(rethrow)) as http.OutgoingMessage
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
private async explain(
|
|
224
|
-
context: http.Context,
|
|
225
|
-
node: Node,
|
|
226
|
-
parameters: Parameter[]
|
|
227
|
-
): Promise<http.OutgoingMessage> {
|
|
228
|
-
const { described, methods } = await node.explain(describing(context), parameters)
|
|
229
|
-
const verbs = Object.keys(methods)
|
|
230
|
-
|
|
231
|
-
// what a caller may not use is not a resource to them, and describing it would be the leak
|
|
232
|
-
if (verbs.length === 0 && Object.keys(node.methods).length > 0)
|
|
233
|
-
throw new http.Forbidden()
|
|
234
|
-
|
|
235
|
-
// what the resource is, beside the methods it serves; a verb is upper case and cannot
|
|
236
|
-
// collide with either key
|
|
237
|
-
return {
|
|
238
|
-
body: { ...described, ...methods },
|
|
239
|
-
headers: new Headers({ allow: verbs.join(', ') })
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private async discover(): Promise<void> {
|
|
244
|
-
const first = new Promise<void>((resolve) => {
|
|
245
|
-
this.resolveFirstMerge = resolve
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
await this.broadcast.receive<Branch>('expose', this.merge.bind(this))
|
|
249
|
-
|
|
250
|
-
void this.knock()
|
|
251
|
-
|
|
252
|
-
await this.settled(first)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* A single ping is enough only if every tenant is listening by then, which is
|
|
257
|
-
* not the case while the deployment is still rolling out.
|
|
258
|
-
*/
|
|
259
|
-
private async knock(): Promise<void> {
|
|
260
|
-
for (const delay of KNOCK_DELAYS) {
|
|
261
|
-
if (this.stopped) return
|
|
262
|
-
|
|
263
|
-
if (delay > 0) await setTimeout(delay, undefined, { ref: false })
|
|
264
|
-
|
|
265
|
-
await this.ping()
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
private async ping(): Promise<void> {
|
|
270
|
-
if (this.stopped) return
|
|
271
|
-
|
|
272
|
-
this.lastPing = Date.now()
|
|
273
|
-
|
|
274
|
-
await this.broadcast
|
|
275
|
-
.transmit<null>('ping', null)
|
|
276
|
-
.catch((exception: Error) =>
|
|
277
|
-
console.error('Discovery ping failed', { message: exception.message })
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private reping(): void {
|
|
282
|
-
if (Date.now() - this.lastPing < PING_COOLDOWN) return
|
|
283
|
-
|
|
284
|
-
void this.ping()
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
private async settled(first: Promise<void>): Promise<void> {
|
|
288
|
-
const deadline = Date.now() + SETTLE_TIMEOUT
|
|
289
|
-
const abort = new AbortController()
|
|
290
|
-
|
|
291
|
-
// an uncancelled timer keeps the process alive long after the race is won
|
|
292
|
-
await Promise.race([
|
|
293
|
-
first,
|
|
294
|
-
setTimeout(SETTLE_TIMEOUT, undefined, { signal: abort.signal })
|
|
295
|
-
])
|
|
296
|
-
.finally(() => {
|
|
297
|
-
abort.abort()
|
|
298
|
-
})
|
|
299
|
-
.catch(() => {})
|
|
300
|
-
|
|
301
|
-
if (this.lastMerge === 0) {
|
|
302
|
-
console.warn('Discovery timed out waiting for the first expose')
|
|
303
|
-
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
while (Date.now() - this.lastMerge < this.quiet()) {
|
|
308
|
-
if (Date.now() >= deadline) break
|
|
309
|
-
|
|
310
|
-
await setTimeout(SETTLE_POLL)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* How long the branches must stay quiet before discovery counts as settled.
|
|
316
|
-
*
|
|
317
|
-
* Every tenant of a local composition answers the first ping at once, so a short window
|
|
318
|
-
* is enough; a rolling deployment brings them up seconds apart, and the window grows with
|
|
319
|
-
* the widest gap seen so far to keep waiting for the ones still starting.
|
|
320
|
-
*/
|
|
321
|
-
private quiet(): number {
|
|
322
|
-
const adaptive = this.widestGap * SETTLE_QUIET_FACTOR
|
|
323
|
-
|
|
324
|
-
return Math.min(Math.max(adaptive, SETTLE_QUIET_MIN), SETTLE_QUIET_MAX)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private merge(branch: Branch): void {
|
|
328
|
-
const id = branch.namespace + '.' + branch.component
|
|
329
|
-
|
|
330
|
-
const attributes = {
|
|
331
|
-
namespace: branch.namespace,
|
|
332
|
-
component: branch.component,
|
|
333
|
-
version: branch.version
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const exposed = this.branches.get(id)
|
|
337
|
-
|
|
338
|
-
if (exposed !== undefined)
|
|
339
|
-
switch (decide(exposed, branch)) {
|
|
340
|
-
// rebuilding an identical branch would only tear down its live endpoints
|
|
341
|
-
case 'refresh':
|
|
342
|
-
this.tree.refresh(exposed.nodes)
|
|
343
|
-
console.trace('Branch refreshed', attributes)
|
|
344
|
-
|
|
345
|
-
return
|
|
346
|
-
|
|
347
|
-
case 'superseded':
|
|
348
|
-
console.debug('Branch superseded', {
|
|
349
|
-
...attributes,
|
|
350
|
-
timestamp: branch.timestamp,
|
|
351
|
-
exposed: exposed.timestamp
|
|
352
|
-
})
|
|
353
|
-
|
|
354
|
-
return
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
let nodes: Node[]
|
|
358
|
-
|
|
359
|
-
try {
|
|
360
|
-
nodes = this.tree.merge(branch.node, branch)
|
|
361
|
-
} catch (exception: unknown) {
|
|
362
|
-
const message = exception instanceof Error ? exception.message : 'Unknown error'
|
|
363
|
-
|
|
364
|
-
console.error('Branch merge exception', { message, ...attributes })
|
|
365
|
-
|
|
366
|
-
return
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
this.branches.set(id, { version: branch.version, timestamp: branch.timestamp, nodes })
|
|
370
|
-
|
|
371
|
-
if (this.lastMerge !== 0)
|
|
372
|
-
this.widestGap = Math.max(this.widestGap, Date.now() - this.lastMerge)
|
|
373
|
-
|
|
374
|
-
this.lastMerge = Date.now()
|
|
375
|
-
this.resolveFirstMerge?.()
|
|
376
|
-
this.resolveFirstMerge = null
|
|
377
|
-
|
|
378
|
-
console.info('Branch merged', attributes)
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export type Broadcast = bindings.Broadcast<Label>
|
|
383
|
-
|
|
384
|
-
const SETTLE_QUIET_MIN = 500
|
|
385
|
-
const SETTLE_QUIET_MAX = 10_000
|
|
386
|
-
const SETTLE_QUIET_FACTOR = 2
|
|
387
|
-
const SETTLE_TIMEOUT = 30_000
|
|
388
|
-
const SETTLE_POLL = 50
|
|
389
|
-
const KNOCK_DELAYS = [0, 500, 1000, 1500]
|
|
390
|
-
const PING_COOLDOWN = 5_000
|