@spine-event-engine/server 2.0.0-snapshot.24 → 2.0.0-snapshot.26
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/README.md +86 -19
- package/REFERENCE.md +538 -150
- package/dist/agent/agent-acceptance-receipts.d.ts +69 -0
- package/dist/agent/agent-acceptance-receipts.d.ts.map +1 -0
- package/dist/agent/agent-acceptance-receipts.js +198 -0
- package/dist/agent/agent-acceptance-receipts.js.map +1 -0
- package/dist/agent/agent-execution-capacity.d.ts +37 -9
- package/dist/agent/agent-execution-capacity.d.ts.map +1 -1
- package/dist/agent/agent-execution-capacity.js +73 -15
- package/dist/agent/agent-execution-capacity.js.map +1 -1
- package/dist/agent/agent-history.d.ts +33 -12
- package/dist/agent/agent-history.d.ts.map +1 -1
- package/dist/agent/agent-history.js +61 -18
- package/dist/agent/agent-history.js.map +1 -1
- package/dist/agent/agent-live-ai-audit.d.ts +95 -0
- package/dist/agent/agent-live-ai-audit.d.ts.map +1 -0
- package/dist/agent/agent-live-ai-audit.js +290 -0
- package/dist/agent/agent-live-ai-audit.js.map +1 -0
- package/dist/agent/agent-live-ai-runtime.d.ts +300 -0
- package/dist/agent/agent-live-ai-runtime.d.ts.map +1 -0
- package/dist/agent/agent-live-ai-runtime.js +611 -0
- package/dist/agent/agent-live-ai-runtime.js.map +1 -0
- package/dist/agent/agent-live-audit.d.ts +67 -0
- package/dist/agent/agent-live-audit.d.ts.map +1 -0
- package/dist/agent/agent-live-audit.js +121 -0
- package/dist/agent/agent-live-audit.js.map +1 -0
- package/dist/agent/agent-live-budget.d.ts +84 -0
- package/dist/agent/agent-live-budget.d.ts.map +1 -0
- package/dist/agent/agent-live-budget.js +161 -0
- package/dist/agent/agent-live-budget.js.map +1 -0
- package/dist/agent/agent-live-fault.d.ts +18 -0
- package/dist/agent/agent-live-fault.d.ts.map +1 -0
- package/dist/agent/{agent-execution-fault.js → agent-live-fault.js} +7 -7
- package/dist/agent/agent-live-fault.js.map +1 -0
- package/dist/agent/agent-live-inputs.d.ts +74 -0
- package/dist/agent/agent-live-inputs.d.ts.map +1 -0
- package/dist/agent/agent-live-inputs.js +15 -0
- package/dist/agent/agent-live-inputs.js.map +1 -0
- package/dist/agent/agent-live-interaction-events.d.ts +46 -0
- package/dist/agent/agent-live-interaction-events.d.ts.map +1 -0
- package/dist/agent/agent-live-interaction-events.js +104 -0
- package/dist/agent/agent-live-interaction-events.js.map +1 -0
- package/dist/agent/agent-live-mcp-controls.d.ts +129 -0
- package/dist/agent/agent-live-mcp-controls.d.ts.map +1 -0
- package/dist/agent/agent-live-mcp-controls.js +228 -0
- package/dist/agent/agent-live-mcp-controls.js.map +1 -0
- package/dist/agent/agent-live-mcp-host.d.ts +88 -0
- package/dist/agent/agent-live-mcp-host.d.ts.map +1 -0
- package/dist/agent/{agent-mcp-host.js → agent-live-mcp-host.js} +1 -1
- package/dist/agent/agent-live-mcp-host.js.map +1 -0
- package/dist/agent/{agent-mcp-runtime.d.ts → agent-live-mcp-runtime.d.ts} +6 -6
- package/dist/agent/agent-live-mcp-runtime.d.ts.map +1 -0
- package/dist/agent/{agent-mcp-runtime.js → agent-live-mcp-runtime.js} +13 -42
- package/dist/agent/agent-live-mcp-runtime.js.map +1 -0
- package/dist/agent/agent-live-read-runtime.d.ts +72 -0
- package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
- package/dist/agent/agent-live-read-runtime.js +122 -0
- package/dist/agent/agent-live-read-runtime.js.map +1 -0
- package/dist/agent/agent-model-selection.d.ts +21 -34
- package/dist/agent/agent-model-selection.d.ts.map +1 -1
- package/dist/agent/agent-model-selection.js +61 -78
- package/dist/agent/agent-model-selection.js.map +1 -1
- package/dist/browser/browser-server.d.ts +163 -0
- package/dist/browser/browser-server.d.ts.map +1 -1
- package/dist/browser/browser-server.js +579 -188
- package/dist/browser/browser-server.js.map +1 -1
- package/dist/browser/durable-subscription-bindings.d.ts +5 -0
- package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
- package/dist/browser/durable-subscription-bindings.js +226 -38
- package/dist/browser/durable-subscription-bindings.js.map +1 -1
- package/dist/browser/options.d.ts +5 -0
- package/dist/browser/options.d.ts.map +1 -1
- package/dist/bus/command-bus.d.ts +17 -18
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +97 -54
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/command-errors.d.ts +20 -4
- package/dist/bus/command-errors.d.ts.map +1 -1
- package/dist/bus/command-errors.js +47 -7
- package/dist/bus/command-errors.js.map +1 -1
- package/dist/bus/event-bus.d.ts +20 -4
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +166 -14
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +169 -98
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +837 -503
- package/dist/context/bounded-context.js.map +1 -1
- package/dist/context/entity-inbox.d.ts +7 -2
- package/dist/context/entity-inbox.d.ts.map +1 -1
- package/dist/context/entity-inbox.js +22 -13
- package/dist/context/entity-inbox.js.map +1 -1
- package/dist/context/local-inbox-handoff.d.ts +34 -34
- package/dist/context/local-inbox-handoff.d.ts.map +1 -1
- package/dist/context/local-inbox-handoff.js +108 -150
- package/dist/context/local-inbox-handoff.js.map +1 -1
- package/dist/context/projection-handoff.d.ts +2 -2
- package/dist/context/projection-handoff.d.ts.map +1 -1
- package/dist/context/projection-handoff.js +10 -8
- package/dist/context/projection-handoff.js.map +1 -1
- package/dist/context/tenant-index.d.ts +36 -31
- package/dist/context/tenant-index.d.ts.map +1 -1
- package/dist/context/tenant-index.js +123 -125
- package/dist/context/tenant-index.js.map +1 -1
- package/dist/context/tenant-unknown-error.d.ts +33 -0
- package/dist/context/tenant-unknown-error.d.ts.map +1 -0
- package/dist/context/tenant-unknown-error.js +75 -0
- package/dist/context/tenant-unknown-error.js.map +1 -0
- package/dist/delivery/agent-delivery-scope.d.ts +66 -0
- package/dist/delivery/agent-delivery-scope.d.ts.map +1 -0
- package/dist/delivery/agent-delivery-scope.js +44 -0
- package/dist/delivery/agent-delivery-scope.js.map +1 -0
- package/dist/delivery/delivery-builder.d.ts +25 -1
- package/dist/delivery/delivery-builder.d.ts.map +1 -1
- package/dist/delivery/delivery-builder.js +89 -0
- package/dist/delivery/delivery-builder.js.map +1 -1
- package/dist/delivery/delivery-monitor.d.ts +1 -1
- package/dist/delivery/delivery-monitor.js +1 -1
- package/dist/delivery/delivery-run-control.d.ts +6 -0
- package/dist/delivery/delivery-run-control.d.ts.map +1 -1
- package/dist/delivery/delivery-run-control.js +10 -0
- package/dist/delivery/delivery-run-control.js.map +1 -1
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
- package/dist/delivery/delivery-supervisor.js +28 -4
- package/dist/delivery/delivery-supervisor.js.map +1 -1
- package/dist/delivery/delivery-tenant.d.ts +17 -0
- package/dist/delivery/delivery-tenant.d.ts.map +1 -0
- package/dist/delivery/delivery-tenant.js +50 -0
- package/dist/delivery/delivery-tenant.js.map +1 -0
- package/dist/delivery/delivery.d.ts +96 -2
- package/dist/delivery/delivery.d.ts.map +1 -1
- package/dist/delivery/delivery.js +693 -53
- package/dist/delivery/delivery.js.map +1 -1
- package/dist/delivery/inbox-records.d.ts.map +1 -1
- package/dist/delivery/inbox-records.js +9 -16
- package/dist/delivery/inbox-records.js.map +1 -1
- package/dist/delivery/inbox-storage.d.ts +6 -0
- package/dist/delivery/inbox-storage.d.ts.map +1 -1
- package/dist/delivery/inbox-storage.js +43 -6
- package/dist/delivery/inbox-storage.js.map +1 -1
- package/dist/delivery/inbox.d.ts.map +1 -1
- package/dist/delivery/inbox.js +3 -2
- package/dist/delivery/inbox.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +12 -4
- package/dist/delivery/reception-observation.d.ts.map +1 -1
- package/dist/delivery/reception-observation.js +14 -3
- package/dist/delivery/reception-observation.js.map +1 -1
- package/dist/delivery/sharded-work-registry.d.ts +6 -0
- package/dist/delivery/sharded-work-registry.d.ts.map +1 -1
- package/dist/delivery/sharded-work-registry.js +41 -4
- package/dist/delivery/sharded-work-registry.js.map +1 -1
- package/dist/entity/entity-storage-descriptor.d.ts.map +1 -1
- package/dist/entity/entity-storage-descriptor.js +7 -4
- package/dist/entity/entity-storage-descriptor.js.map +1 -1
- package/dist/entity/entity.d.ts +59 -0
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +60 -0
- package/dist/entity/entity.js.map +1 -1
- package/dist/handler/app-query-handler.d.ts +60 -0
- package/dist/handler/app-query-handler.d.ts.map +1 -0
- package/dist/{bus/saved-dispatcher-binding.js → handler/app-query-handler.js} +8 -12
- package/dist/handler/app-query-handler.js.map +1 -0
- package/dist/handler/generated-registry-discovery.d.ts +6 -0
- package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
- package/dist/handler/generated-registry-discovery.js +71 -2
- package/dist/handler/generated-registry-discovery.js.map +1 -1
- package/dist/handler/handler-decorators.d.ts +8 -29
- package/dist/handler/handler-decorators.d.ts.map +1 -1
- package/dist/handler/handler-decorators.js +33 -210
- package/dist/handler/handler-decorators.js.map +1 -1
- package/dist/handler/spine-metadata-inventory.d.ts +99 -0
- package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
- package/dist/handler/spine-metadata-inventory.js +162 -0
- package/dist/handler/spine-metadata-inventory.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/integration/external-messages.d.ts.map +1 -1
- package/dist/integration/external-messages.js +9 -15
- package/dist/integration/external-messages.js.map +1 -1
- package/dist/integration/integration-broker.d.ts.map +1 -1
- package/dist/integration/integration-broker.js +112 -6
- package/dist/integration/integration-broker.js.map +1 -1
- package/dist/integration/third-party-context.d.ts.map +1 -1
- package/dist/integration/third-party-context.js +7 -10
- package/dist/integration/third-party-context.js.map +1 -1
- package/dist/repository/primitive-id.js +26 -11
- package/dist/repository/primitive-id.js.map +1 -1
- package/dist/repository/repository.d.ts +75 -60
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +1757 -1818
- package/dist/repository/repository.js.map +1 -1
- package/dist/runtime/runtime.d.ts +13 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +18 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/signal-metadata.d.ts.map +1 -1
- package/dist/runtime/signal-metadata.js +6 -6
- package/dist/runtime/signal-metadata.js.map +1 -1
- package/dist/runtime/signal-publisher.d.ts +0 -6
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +1 -9
- package/dist/runtime/signal-publisher.js.map +1 -1
- package/dist/runtime/standalone-handler-runtime.d.ts +0 -7
- package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -1
- package/dist/runtime/standalone-handler-runtime.js +13 -99
- package/dist/runtime/standalone-handler-runtime.js.map +1 -1
- package/dist/server/managed-delivery-gate.d.ts +34 -0
- package/dist/server/managed-delivery-gate.d.ts.map +1 -0
- package/dist/server/managed-delivery-gate.js +70 -0
- package/dist/server/managed-delivery-gate.js.map +1 -0
- package/dist/server/managed-server-application.d.ts +12 -11
- package/dist/server/managed-server-application.d.ts.map +1 -1
- package/dist/server/managed-server-application.js +48 -29
- package/dist/server/managed-server-application.js.map +1 -1
- package/dist/server/node-coordinator.d.ts +18 -5
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +554 -84
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/reserved-spine-rpc-paths.json +1 -0
- package/dist/server/server-environment.d.ts +220 -25
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +734 -145
- package/dist/server/server-environment.js.map +1 -1
- package/dist/server/server-log.d.ts +7 -0
- package/dist/server/server-log.d.ts.map +1 -1
- package/dist/server/server-log.js +14 -1
- package/dist/server/server-log.js.map +1 -1
- package/dist/server/server.d.ts +60 -29
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +155 -201
- package/dist/server/server.js.map +1 -1
- package/dist/services/app-query-execution.d.ts +53 -0
- package/dist/services/app-query-execution.d.ts.map +1 -0
- package/dist/services/app-query-execution.js +408 -0
- package/dist/services/app-query-execution.js.map +1 -0
- package/dist/services/query-reader.d.ts +32 -7
- package/dist/services/query-reader.d.ts.map +1 -1
- package/dist/services/query-reader.js +22 -2
- package/dist/services/query-reader.js.map +1 -1
- package/dist/services/read-request-status.d.ts +65 -0
- package/dist/services/read-request-status.d.ts.map +1 -0
- package/dist/services/read-request-status.js +151 -0
- package/dist/services/read-request-status.js.map +1 -0
- package/dist/services/spine-services.d.ts +37 -8
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +1103 -470
- package/dist/services/spine-services.js.map +1 -1
- package/dist/services/trusted-reader-ingress.d.ts +92 -0
- package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
- package/dist/services/trusted-reader-ingress.js +89 -0
- package/dist/services/trusted-reader-ingress.js.map +1 -0
- package/dist/spi/handler-registry.d.ts +2 -0
- package/dist/spi/handler-registry.d.ts.map +1 -1
- package/dist/spi/handler-registry.js +1 -1
- package/dist/spi/handler-registry.js.map +1 -1
- package/dist/stand/event-origin-filter.js +3 -3
- package/dist/stand/event-origin-filter.js.map +1 -1
- package/dist/stand/read-side-filter-engine.d.ts +46 -0
- package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
- package/dist/stand/read-side-filter-engine.js +356 -0
- package/dist/stand/read-side-filter-engine.js.map +1 -0
- package/dist/stand/read-side-filter.d.ts +247 -0
- package/dist/stand/read-side-filter.d.ts.map +1 -0
- package/dist/stand/read-side-filter.js +39 -0
- package/dist/stand/read-side-filter.js.map +1 -0
- package/dist/stand/read-side-message.d.ts +66 -0
- package/dist/stand/read-side-message.d.ts.map +1 -0
- package/dist/stand/read-side-message.js +167 -0
- package/dist/stand/read-side-message.js.map +1 -0
- package/dist/stand/stand-direct-delivery.d.ts +161 -0
- package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
- package/dist/stand/stand-direct-delivery.js +210 -0
- package/dist/stand/stand-direct-delivery.js.map +1 -0
- package/dist/stand/stand.d.ts +94 -42
- package/dist/stand/stand.d.ts.map +1 -1
- package/dist/stand/stand.js +460 -60
- package/dist/stand/stand.js.map +1 -1
- package/dist/stand/subscription-observer.d.ts.map +1 -1
- package/dist/stand/subscription-observer.js +157 -40
- package/dist/stand/subscription-observer.js.map +1 -1
- package/dist/stand/subscription-read-filters.d.ts +40 -0
- package/dist/stand/subscription-read-filters.d.ts.map +1 -0
- package/dist/stand/subscription-read-filters.js +91 -0
- package/dist/stand/subscription-read-filters.js.map +1 -0
- package/dist/stand/subscription-reader-binding.d.ts +20 -0
- package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
- package/dist/stand/subscription-reader-binding.js +56 -0
- package/dist/stand/subscription-reader-binding.js.map +1 -0
- package/dist/stand/subscription-records.d.ts +30 -3
- package/dist/stand/subscription-records.d.ts.map +1 -1
- package/dist/stand/subscription-records.js +61 -16
- package/dist/stand/subscription-records.js.map +1 -1
- package/dist/stand/subscription-registry.d.ts +54 -15
- package/dist/stand/subscription-registry.d.ts.map +1 -1
- package/dist/stand/subscription-registry.js +164 -41
- package/dist/stand/subscription-registry.js.map +1 -1
- package/dist/stand/subscription-runtime.d.ts +12 -5
- package/dist/stand/subscription-runtime.d.ts.map +1 -1
- package/dist/stand/subscription-runtime.js +183 -34
- package/dist/stand/subscription-runtime.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -3
- package/dist/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -14
- package/dist/agent/agent-admission.d.ts +0 -80
- package/dist/agent/agent-admission.d.ts.map +0 -1
- package/dist/agent/agent-admission.js +0 -127
- package/dist/agent/agent-admission.js.map +0 -1
- package/dist/agent/agent-ai-runtime.d.ts +0 -76
- package/dist/agent/agent-ai-runtime.d.ts.map +0 -1
- package/dist/agent/agent-ai-runtime.js +0 -1269
- package/dist/agent/agent-ai-runtime.js.map +0 -1
- package/dist/agent/agent-execution-fault.d.ts +0 -18
- package/dist/agent/agent-execution-fault.d.ts.map +0 -1
- package/dist/agent/agent-execution-fault.js.map +0 -1
- package/dist/agent/agent-execution-session.d.ts +0 -103
- package/dist/agent/agent-execution-session.d.ts.map +0 -1
- package/dist/agent/agent-execution-session.js +0 -238
- package/dist/agent/agent-execution-session.js.map +0 -1
- package/dist/agent/agent-interaction-audit.d.ts +0 -58
- package/dist/agent/agent-interaction-audit.d.ts.map +0 -1
- package/dist/agent/agent-interaction-audit.js +0 -114
- package/dist/agent/agent-interaction-audit.js.map +0 -1
- package/dist/agent/agent-interaction-events.d.ts +0 -40
- package/dist/agent/agent-interaction-events.d.ts.map +0 -1
- package/dist/agent/agent-interaction-events.js +0 -129
- package/dist/agent/agent-interaction-events.js.map +0 -1
- package/dist/agent/agent-mcp-budget.d.ts +0 -125
- package/dist/agent/agent-mcp-budget.d.ts.map +0 -1
- package/dist/agent/agent-mcp-budget.js +0 -566
- package/dist/agent/agent-mcp-budget.js.map +0 -1
- package/dist/agent/agent-mcp-host.d.ts +0 -125
- package/dist/agent/agent-mcp-host.d.ts.map +0 -1
- package/dist/agent/agent-mcp-host.js.map +0 -1
- package/dist/agent/agent-mcp-runtime.d.ts.map +0 -1
- package/dist/agent/agent-mcp-runtime.js.map +0 -1
- package/dist/agent/agent-read-runtime.d.ts +0 -70
- package/dist/agent/agent-read-runtime.d.ts.map +0 -1
- package/dist/agent/agent-read-runtime.js +0 -320
- package/dist/agent/agent-read-runtime.js.map +0 -1
- package/dist/agent/agent-revisions.d.ts +0 -35
- package/dist/agent/agent-revisions.d.ts.map +0 -1
- package/dist/agent/agent-revisions.js +0 -133
- package/dist/agent/agent-revisions.js.map +0 -1
- package/dist/agent/agent-scheduler.d.ts +0 -99
- package/dist/agent/agent-scheduler.d.ts.map +0 -1
- package/dist/agent/agent-scheduler.js +0 -398
- package/dist/agent/agent-scheduler.js.map +0 -1
- package/dist/bus/saved-dispatcher-binding.d.ts +0 -53
- package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
- package/dist/bus/saved-dispatcher-binding.js.map +0 -1
- package/dist/delivery/delivery-run-coordinator.d.ts +0 -218
- package/dist/delivery/delivery-run-coordinator.d.ts.map +0 -1
- package/dist/delivery/delivery-run-coordinator.js +0 -609
- package/dist/delivery/delivery-run-coordinator.js.map +0 -1
- package/dist/delivery/parked-delivery-obligations.d.ts +0 -182
- package/dist/delivery/parked-delivery-obligations.d.ts.map +0 -1
- package/dist/delivery/parked-delivery-obligations.js +0 -390
- package/dist/delivery/parked-delivery-obligations.js.map +0 -1
- package/dist/server/environment-attachment.d.ts +0 -356
- package/dist/server/environment-attachment.d.ts.map +0 -1
- package/dist/server/environment-attachment.js +0 -2062
- package/dist/server/environment-attachment.js.map +0 -1
- package/dist/server/environment-delivery-records.d.ts +0 -77
- package/dist/server/environment-delivery-records.d.ts.map +0 -1
- package/dist/server/environment-delivery-records.js +0 -282
- package/dist/server/environment-delivery-records.js.map +0 -1
- package/dist/server/environment-delivery-worker.d.ts +0 -161
- package/dist/server/environment-delivery-worker.d.ts.map +0 -1
- package/dist/server/environment-delivery-worker.js +0 -973
- package/dist/server/environment-delivery-worker.js.map +0 -1
|
@@ -15,17 +15,22 @@ import * as http2 from "node:http2";
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
import { clone, create, equals, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
17
17
|
import { EmptySchema } from "@bufbuild/protobuf/wkt";
|
|
18
|
-
import { Code, ConnectError, createClient } from "@connectrpc/connect";
|
|
18
|
+
import { Code, ConnectError, createClient, } from "@connectrpc/connect";
|
|
19
19
|
import { connectNodeAdapter, createGrpcTransport, Http2SessionManager, } from "@connectrpc/connect-node";
|
|
20
20
|
import { BackendMembershipKernel, } from "@spine-event-engine/deployment/spi/backend-membership";
|
|
21
21
|
import { SUBSCRIPTION_ACTIVATION_HANDSHAKE_MS } from "@spine-event-engine/core/spi/subscription-lifecycle";
|
|
22
|
+
import { Validate } from "@spine-event-engine/core";
|
|
23
|
+
import { ReaderForwarding, } from "@spine-event-engine/core/spi/reader-forwarding";
|
|
24
|
+
import { ReaderIdentities, snapshotTrustedReader, } from "@spine-event-engine/core/spi/trusted-reader";
|
|
22
25
|
import { AckSchema, ResponseSchema, StatusSchema, } from "@spine-event-engine/proto";
|
|
23
|
-
import { QueryResponseSchema, CommandService, QueryService, SubscriptionIdSchema, SubscriptionSchema, SubscriptionService, SubscriptionUpdateSchema, TopicSchema, } from "@spine-event-engine/proto/client";
|
|
26
|
+
import { AppQueryResponseSchema, AppQuerySchema, QueryResponseSchema, QuerySchema, CommandService, QueryService, SubscriptionIdSchema, SubscriptionSchema, SubscriptionService, SubscriptionUpdateSchema, TopicSchema, } from "@spine-event-engine/proto/client";
|
|
27
|
+
import { ReadRequestStatus } from "../services/read-request-status.js";
|
|
28
|
+
import { TrustedReaderIngress, } from "../services/trusted-reader-ingress.js";
|
|
29
|
+
import { ServerEnvironment } from "./server-environment.js";
|
|
24
30
|
const defaultHost = "127.0.0.1";
|
|
25
31
|
const defaultPort = 0;
|
|
26
32
|
const defaultMessageMaxBytes = 4_194_304;
|
|
27
33
|
const gracefulSessionDrainMs = 100;
|
|
28
|
-
const subscriptionQueueLimit = 100;
|
|
29
34
|
/**
|
|
30
35
|
* Hosts generated unary services in front of managed complete replicas.
|
|
31
36
|
*
|
|
@@ -34,6 +39,13 @@ const subscriptionQueueLimit = 100;
|
|
|
34
39
|
export class NodeCoordinator {
|
|
35
40
|
// prettier-ignore
|
|
36
41
|
#members;
|
|
42
|
+
#readerAdmission;
|
|
43
|
+
#forwardingProof;
|
|
44
|
+
#readers = new Map();
|
|
45
|
+
#children = new Map();
|
|
46
|
+
#logicalForChild = new Map();
|
|
47
|
+
#readMaxBytes;
|
|
48
|
+
#subscriptionQueueCapacity;
|
|
37
49
|
#unaryKernel;
|
|
38
50
|
#subscriptionKernel;
|
|
39
51
|
#server;
|
|
@@ -67,26 +79,48 @@ export class NodeCoordinator {
|
|
|
67
79
|
* @param server The HTTP/2 server.
|
|
68
80
|
* @param address The listening address.
|
|
69
81
|
* @param sessions Tracked HTTP/2 sessions.
|
|
82
|
+
* @param subscriptionQueueCapacity Resolved bound for each managed subscriber.
|
|
70
83
|
*/
|
|
71
|
-
constructor(options, server, address, sessions) {
|
|
84
|
+
constructor(options, server, address, sessions, subscriptionQueueCapacity) {
|
|
72
85
|
this.#members = options.members;
|
|
73
|
-
this.#
|
|
74
|
-
this.#
|
|
86
|
+
this.#readerAdmission = options.readerAdmission;
|
|
87
|
+
this.#forwardingProof = options.forwardingProof;
|
|
88
|
+
this.#readMaxBytes = options.readMaxBytes ?? defaultMessageMaxBytes;
|
|
89
|
+
this.#subscriptionQueueCapacity = subscriptionQueueCapacity;
|
|
90
|
+
this.#unaryKernel = new BackendMembershipKernel(NodeCoordinatorValues.unaryKernelOptions(undefined, this.#forwardingProof, this.#readMaxBytes, (bytes) => this.#readerFor(bytes)));
|
|
91
|
+
this.#subscriptionKernel = this.#makeSubscriptionKernel(options.members);
|
|
75
92
|
this.#server = server;
|
|
76
93
|
this.#sessions = sessions;
|
|
77
94
|
this.host = address.address;
|
|
78
95
|
this.port = address.port;
|
|
79
96
|
this.baseUrl = `http://${NodeCoordinatorValues.formatHostForUrl(this.host)}:${this.port.toString()}`;
|
|
80
97
|
const reconcile = () => {
|
|
81
|
-
this.#
|
|
82
|
-
.then(() => this.#reconcile(), () => this.#reconcile())
|
|
83
|
-
.catch(() => undefined);
|
|
98
|
+
this.#scheduleReconciliation();
|
|
84
99
|
};
|
|
85
100
|
this.#stopMembers = [
|
|
86
101
|
options.members.onReadyMembersChange(reconcile),
|
|
87
102
|
options.members.onRelayMembersChange(reconcile),
|
|
88
103
|
];
|
|
89
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Builds the subscription kernel with reader provenance for each child.
|
|
107
|
+
*
|
|
108
|
+
* @param members The available backend member source.
|
|
109
|
+
* @returns The native subscription kernel.
|
|
110
|
+
*/
|
|
111
|
+
#makeSubscriptionKernel(members) {
|
|
112
|
+
return new BackendMembershipKernel(NodeCoordinatorValues.unaryKernelOptions(members, this.#forwardingProof, this.#readMaxBytes, (bytes) => this.#readerFor(bytes), (definition, child) => {
|
|
113
|
+
this.#rememberChild(definition, child);
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Serializes membership refresh after readiness or relay changes.
|
|
118
|
+
*/
|
|
119
|
+
#scheduleReconciliation() {
|
|
120
|
+
this.#membershipReconciliation = this.#membershipReconciliation
|
|
121
|
+
.then(() => this.#reconcile(), () => this.#reconcile())
|
|
122
|
+
.catch(() => undefined);
|
|
123
|
+
}
|
|
90
124
|
/**
|
|
91
125
|
* Opens one generated unary-service listener.
|
|
92
126
|
*
|
|
@@ -94,37 +128,44 @@ export class NodeCoordinator {
|
|
|
94
128
|
* @returns The running private Coordinator.
|
|
95
129
|
*/
|
|
96
130
|
static async open(options) {
|
|
131
|
+
const subscriptionQueueCapacity = ServerEnvironment.instance().subscriptionQueueCapacity;
|
|
97
132
|
const { host, port, readMaxBytes, writeMaxBytes } = this.#listenerOptions(options);
|
|
98
133
|
// eslint-disable-next-line prefer-const -- routes capture the Coordinator before the listener exists.
|
|
99
134
|
let coordinator;
|
|
100
135
|
const sessions = new Set();
|
|
101
136
|
const server = http2.createServer(connectNodeAdapter({
|
|
102
137
|
routes: (router) => {
|
|
103
|
-
router
|
|
104
|
-
post: async (command, context) => {
|
|
105
|
-
return coordinator.#post(command, context);
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
router.service(QueryService, {
|
|
109
|
-
read: async (query, context) => {
|
|
110
|
-
return coordinator.#read(query, context);
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
router.service(SubscriptionService, {
|
|
114
|
-
subscribe: (topic, context) => coordinator.#subscribe(topic, context),
|
|
115
|
-
activate: (subscription, context) => coordinator.#activate(subscription, context),
|
|
116
|
-
cancel: (subscription, context) => coordinator.#cancel(subscription, context),
|
|
117
|
-
});
|
|
138
|
+
this.#routes(router, () => coordinator);
|
|
118
139
|
},
|
|
119
140
|
readMaxBytes,
|
|
120
141
|
writeMaxBytes,
|
|
121
142
|
}));
|
|
122
143
|
this.#trackSessions(server, sessions);
|
|
123
144
|
const address = await NodeCoordinatorValues.listen(server, host, port);
|
|
124
|
-
coordinator = new NodeCoordinator(options, server, address, sessions);
|
|
145
|
+
coordinator = new NodeCoordinator(options, server, address, sessions, subscriptionQueueCapacity);
|
|
125
146
|
await coordinator.#reconcile();
|
|
126
147
|
return coordinator;
|
|
127
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Registers native service routes after the listener receives its Coordinator instance.
|
|
151
|
+
*
|
|
152
|
+
* @param router Connect router for the private listener.
|
|
153
|
+
* @param current Returns the initialized Coordinator at request time.
|
|
154
|
+
*/
|
|
155
|
+
static #routes(router, current) {
|
|
156
|
+
router.service(CommandService, {
|
|
157
|
+
post: (command, context) => current().#post(command, context),
|
|
158
|
+
});
|
|
159
|
+
router.service(QueryService, {
|
|
160
|
+
read: (query, context) => current().#read(query, context),
|
|
161
|
+
execute: (query, context) => current().#execute(query, context),
|
|
162
|
+
});
|
|
163
|
+
router.service(SubscriptionService, {
|
|
164
|
+
subscribe: (topic, context) => current().#subscribe(topic, context),
|
|
165
|
+
activate: (subscription, context) => current().#activate(subscription, context),
|
|
166
|
+
cancel: (subscription, context) => current().#cancel(subscription, context),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
128
169
|
/**
|
|
129
170
|
* Validates private listener and message limits before opening the server.
|
|
130
171
|
*
|
|
@@ -181,37 +222,110 @@ export class NodeCoordinator {
|
|
|
181
222
|
await this.#membershipReconciliation;
|
|
182
223
|
await this.#subscriptionKernel.close();
|
|
183
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Admits a command reader and forwards the command to a managed child.
|
|
227
|
+
*
|
|
228
|
+
* @param command Command envelope from the native client.
|
|
229
|
+
* @param context The trusted context for this operation.
|
|
230
|
+
* @returns Child acknowledgment after forwarding.
|
|
231
|
+
*/
|
|
184
232
|
async #post(command, context) {
|
|
233
|
+
const reader = await this.#admit("spine.client.CommandService", "Post", toBinary(CommandService.method.post.input, command), context);
|
|
185
234
|
const request = {
|
|
186
235
|
kind: "command",
|
|
187
236
|
command,
|
|
188
237
|
context,
|
|
238
|
+
reader,
|
|
189
239
|
response: create(AckSchema),
|
|
190
240
|
};
|
|
191
241
|
await this.#forward(request);
|
|
192
242
|
return request.response;
|
|
193
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Admits a read reader and forwards the query to a managed child.
|
|
246
|
+
*
|
|
247
|
+
* @param query Query envelope from the native client.
|
|
248
|
+
* @param context The trusted context for this operation.
|
|
249
|
+
* @returns Child read response after forwarding.
|
|
250
|
+
*/
|
|
194
251
|
async #read(query, context) {
|
|
252
|
+
const reader = await this.#admit("spine.client.QueryService", "Read", toBinary(QuerySchema, query), context);
|
|
253
|
+
ReadRequestStatus.check("query", () => Validate.check(QuerySchema, query));
|
|
195
254
|
const request = {
|
|
196
255
|
kind: "query",
|
|
197
256
|
query,
|
|
198
257
|
context,
|
|
258
|
+
reader,
|
|
199
259
|
response: create(QueryResponseSchema),
|
|
200
260
|
};
|
|
201
261
|
await this.#forward(request);
|
|
202
262
|
return request.response;
|
|
203
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Admits an App Query and forwards it to a ready application replica.
|
|
266
|
+
*
|
|
267
|
+
* @param query App Query envelope.
|
|
268
|
+
* @param context Native RPC context.
|
|
269
|
+
* @returns The application replica's response.
|
|
270
|
+
*/
|
|
271
|
+
async #execute(query, context) {
|
|
272
|
+
const reader = await this.#admit("spine.client.QueryService", "Execute", toBinary(QueryService.method.execute.input, query), context);
|
|
273
|
+
ReadRequestStatus.check("query", () => Validate.check(AppQuerySchema, query));
|
|
274
|
+
const request = {
|
|
275
|
+
kind: "app-query",
|
|
276
|
+
query,
|
|
277
|
+
context,
|
|
278
|
+
reader,
|
|
279
|
+
response: create(AppQueryResponseSchema),
|
|
280
|
+
};
|
|
281
|
+
await this.#forward(request);
|
|
282
|
+
return request.response;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Verifies forwarded or direct reader facts at the managed ingress.
|
|
286
|
+
*
|
|
287
|
+
* @param service Canonical RPC service.
|
|
288
|
+
* @param method Canonical RPC method.
|
|
289
|
+
* @param request Canonical request bytes.
|
|
290
|
+
* @param context Native transport context.
|
|
291
|
+
* @returns Detached trusted reader facts.
|
|
292
|
+
*/
|
|
293
|
+
#admit(service, method, request, context) {
|
|
294
|
+
return TrustedReaderIngress.admit({
|
|
295
|
+
service,
|
|
296
|
+
method,
|
|
297
|
+
request,
|
|
298
|
+
headers: context.requestHeader,
|
|
299
|
+
peer: context,
|
|
300
|
+
maxBytes: this.#readMaxBytes,
|
|
301
|
+
...this.#readerAdmission,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Validates and retains a reader-bound subscription definition.
|
|
306
|
+
*
|
|
307
|
+
* @param topic Framework subscription Topic.
|
|
308
|
+
* @param context The trusted context for this operation.
|
|
309
|
+
* @returns The new subscription definition.
|
|
310
|
+
*/
|
|
204
311
|
async #subscribe(topic, context) {
|
|
312
|
+
const reader = await this.#admit("spine.client.SubscriptionService", "Subscribe", toBinary(TopicSchema, topic), context);
|
|
313
|
+
ReadRequestStatus.check("topic", () => Validate.check(TopicSchema, topic));
|
|
205
314
|
if (this.#draining)
|
|
206
315
|
throw new ConnectError("Managed application is closing.", Code.Unavailable);
|
|
207
316
|
const subscription = create(SubscriptionSchema, {
|
|
208
317
|
id: create(SubscriptionIdSchema, { value: `s-${randomUUID()}` }),
|
|
209
318
|
topic,
|
|
210
319
|
});
|
|
320
|
+
const id = subscription.id?.value;
|
|
321
|
+
if (id === undefined)
|
|
322
|
+
throw new Error("Created subscription has no ID.");
|
|
323
|
+
this.#readers.set(id, reader);
|
|
211
324
|
try {
|
|
212
325
|
await this.#subscriptionKernel.subscribe(toBinary(SubscriptionSchema, subscription), context.signal);
|
|
213
326
|
}
|
|
214
327
|
catch (error) {
|
|
328
|
+
this.#forgetReader(id);
|
|
215
329
|
throw this.#availabilityError(error);
|
|
216
330
|
}
|
|
217
331
|
if (this.#isDraining()) {
|
|
@@ -229,7 +343,15 @@ export class NodeCoordinator {
|
|
|
229
343
|
#isDraining() {
|
|
230
344
|
return this.#draining;
|
|
231
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Verifies membership and activates a retained subscription.
|
|
348
|
+
*
|
|
349
|
+
* @param subscription The subscription definition being processed.
|
|
350
|
+
* @param context The trusted context for this operation.
|
|
351
|
+
* @returns The update stream after admission.
|
|
352
|
+
*/
|
|
232
353
|
async *#activate(subscription, context) {
|
|
354
|
+
await this.#admitActivation(subscription, context);
|
|
233
355
|
if (this.#draining)
|
|
234
356
|
throw new ConnectError("Managed application is closing.", Code.Unavailable);
|
|
235
357
|
const definition = toBinary(SubscriptionSchema, subscription);
|
|
@@ -241,10 +363,14 @@ export class NodeCoordinator {
|
|
|
241
363
|
context.signal.addEventListener("abort", abort, { once: true });
|
|
242
364
|
if (context.signal.aborted)
|
|
243
365
|
abort();
|
|
244
|
-
const updates = new SubscriptionUpdateQueue(
|
|
245
|
-
const
|
|
366
|
+
const updates = new SubscriptionUpdateQueue(this.#subscriptionQueueCapacity, abort);
|
|
367
|
+
const ack = Promise.withResolvers();
|
|
368
|
+
const activation = this.#activateChild(definition, subscription, updates, ack, overflow.signal);
|
|
246
369
|
let failure;
|
|
247
370
|
try {
|
|
371
|
+
const ready = await ack.promise;
|
|
372
|
+
if (ready !== undefined)
|
|
373
|
+
yield ready;
|
|
248
374
|
for await (const update of updates)
|
|
249
375
|
yield update;
|
|
250
376
|
}
|
|
@@ -258,26 +384,53 @@ export class NodeCoordinator {
|
|
|
258
384
|
await this.#finishActivation(definition, activation, failure);
|
|
259
385
|
}
|
|
260
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Admits and validates a reader-bound activation before changing pending state.
|
|
389
|
+
*
|
|
390
|
+
* @param subscription The subscription definition being processed.
|
|
391
|
+
* @param context The trusted context for this operation.
|
|
392
|
+
* @returns Completion after the operation settles.
|
|
393
|
+
*/
|
|
394
|
+
async #admitActivation(subscription, context) {
|
|
395
|
+
const reader = await this.#admit("spine.client.SubscriptionService", "Activate", toBinary(SubscriptionSchema, subscription), context);
|
|
396
|
+
ReadRequestStatus.check("subscription", () => Validate.check(SubscriptionSchema, subscription));
|
|
397
|
+
ReadRequestStatus.checkLifecycleCriterion(subscription);
|
|
398
|
+
this.#requireReader(subscription, reader);
|
|
399
|
+
}
|
|
261
400
|
/**
|
|
262
401
|
* Relays accepted child updates through the bounded logical stream.
|
|
263
402
|
*
|
|
264
403
|
* @param definition The validated child subscription bytes.
|
|
265
404
|
* @param subscription The logical subscription exposed to callers.
|
|
266
405
|
* @param updates The bounded caller stream queue.
|
|
406
|
+
* @param ack The separate activation acknowledgment gate.
|
|
267
407
|
* @param signal Cancels the active child relay.
|
|
268
408
|
* @returns Completion after the backend activation ends.
|
|
269
409
|
*/
|
|
270
|
-
#activateChild(definition, subscription, updates, signal) {
|
|
410
|
+
#activateChild(definition, subscription, updates, ack, signal) {
|
|
271
411
|
return this.#subscriptionKernel
|
|
272
412
|
.activate(definition, async (childUpdate) => {
|
|
273
413
|
const update = fromBinary(SubscriptionUpdateSchema, childUpdate);
|
|
274
414
|
update.subscription = clone(SubscriptionSchema, subscription);
|
|
415
|
+
if (update.response?.status?.status.case === "ok" && update.update.case === undefined) {
|
|
416
|
+
ack.resolve(update);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
275
419
|
await updates.push(update);
|
|
276
420
|
}, signal)
|
|
277
421
|
.finally(() => {
|
|
422
|
+
ack.resolve(undefined);
|
|
278
423
|
updates.close();
|
|
279
424
|
});
|
|
280
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* Finishes one activated stream and clears pending state.
|
|
428
|
+
*
|
|
429
|
+
* @param definition The serialized subscription definition.
|
|
430
|
+
* @param activation Activation task and its stream state.
|
|
431
|
+
* @param previousFailure Earlier lifecycle failure.
|
|
432
|
+
* @returns Completion after the operation settles.
|
|
433
|
+
*/
|
|
281
434
|
async #finishActivation(definition, activation, previousFailure) {
|
|
282
435
|
const failures = previousFailure === undefined ? [] : [previousFailure];
|
|
283
436
|
try {
|
|
@@ -298,13 +451,36 @@ export class NodeCoordinator {
|
|
|
298
451
|
if (failures.length > 1)
|
|
299
452
|
throw new AggregateError(failures, "Coordinator activation cleanup failed.");
|
|
300
453
|
}
|
|
454
|
+
/**
|
|
455
|
+
* Verifies reader membership and removes a retained subscription.
|
|
456
|
+
*
|
|
457
|
+
* @param subscription The subscription definition being processed.
|
|
458
|
+
* @param context The trusted context for this operation.
|
|
459
|
+
* @returns The cancellation acknowledgment.
|
|
460
|
+
*/
|
|
301
461
|
async #cancel(subscription, context) {
|
|
462
|
+
const reader = await this.#admit("spine.client.SubscriptionService", "Cancel", toBinary(SubscriptionSchema, subscription), context);
|
|
463
|
+
ReadRequestStatus.check("subscription", () => Validate.check(SubscriptionSchema, subscription));
|
|
464
|
+
ReadRequestStatus.checkLifecycleCriterion(subscription);
|
|
465
|
+
const id = subscription.id?.value;
|
|
466
|
+
if (id !== undefined && this.#readers.has(id))
|
|
467
|
+
this.#requireReader(subscription, reader);
|
|
468
|
+
else
|
|
469
|
+
return create(ResponseSchema, {
|
|
470
|
+
status: create(StatusSchema, { status: { case: "ok", value: create(EmptySchema) } }),
|
|
471
|
+
});
|
|
302
472
|
this.#clearPendingActivation(subscription);
|
|
303
473
|
await this.#cancelDefinition(toBinary(SubscriptionSchema, subscription), context.signal);
|
|
304
474
|
return create(ResponseSchema, {
|
|
305
475
|
status: create(StatusSchema, { status: { case: "ok", value: create(EmptySchema) } }),
|
|
306
476
|
});
|
|
307
477
|
}
|
|
478
|
+
/**
|
|
479
|
+
* Forwards a verified request to the selected managed child.
|
|
480
|
+
*
|
|
481
|
+
* @param request The request being processed.
|
|
482
|
+
* @returns Completion after the operation settles.
|
|
483
|
+
*/
|
|
308
484
|
async #forward(request) {
|
|
309
485
|
if (this.#draining)
|
|
310
486
|
throw new ConnectError("No ready application replica is available.", Code.Unavailable);
|
|
@@ -315,11 +491,22 @@ export class NodeCoordinator {
|
|
|
315
491
|
throw this.#availabilityError(error);
|
|
316
492
|
}
|
|
317
493
|
}
|
|
494
|
+
/**
|
|
495
|
+
* Maps child transport failure to the public availability status.
|
|
496
|
+
*
|
|
497
|
+
* @param error The failure being classified or propagated.
|
|
498
|
+
* @returns The public transport error.
|
|
499
|
+
*/
|
|
318
500
|
#availabilityError(error) {
|
|
319
501
|
return error instanceof Error && error.message === "backend membership is unavailable."
|
|
320
502
|
? new ConnectError("No ready application replica is available.", Code.Unavailable)
|
|
321
503
|
: error;
|
|
322
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* Starts the activation handshake expiry for a new subscription.
|
|
507
|
+
*
|
|
508
|
+
* @param subscription The subscription definition being processed.
|
|
509
|
+
*/
|
|
323
510
|
#schedulePendingActivation(subscription) {
|
|
324
511
|
const id = NodeCoordinatorValues.requiredValue(subscription.id?.value, "Coordinator subscription definition is missing an ID.");
|
|
325
512
|
this.#clearPendingActivation(subscription);
|
|
@@ -332,18 +519,98 @@ export class NodeCoordinator {
|
|
|
332
519
|
pending.timer.unref();
|
|
333
520
|
this.#pendingActivations.set(id, pending);
|
|
334
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* Forwards cancellation of a native subscription definition.
|
|
524
|
+
*
|
|
525
|
+
* @param definition The serialized subscription definition.
|
|
526
|
+
* @param signal The cancellation signal for this operation.
|
|
527
|
+
* @returns Completion after the operation settles.
|
|
528
|
+
*/
|
|
335
529
|
#cancelDefinition(definition, signal) {
|
|
336
530
|
const id = NodeCoordinatorValues.requiredValue(fromBinary(SubscriptionSchema, definition).id?.value, "Coordinator subscription definition is missing an ID.");
|
|
337
531
|
const active = this.#cancellations.get(id);
|
|
338
532
|
if (active !== undefined)
|
|
339
533
|
return active;
|
|
340
|
-
const cancellation = this.#subscriptionKernel
|
|
534
|
+
const cancellation = this.#subscriptionKernel
|
|
535
|
+
.cancel(definition, signal)
|
|
536
|
+
.then(() => {
|
|
537
|
+
this.#forgetReader(id);
|
|
538
|
+
})
|
|
539
|
+
.finally(() => {
|
|
341
540
|
if (this.#cancellations.get(id) === cancellation)
|
|
342
541
|
this.#cancellations.delete(id);
|
|
343
542
|
});
|
|
344
543
|
this.#cancellations.set(id, cancellation);
|
|
345
544
|
return cancellation;
|
|
346
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* Verifies and refreshes the reader before subscription effects.
|
|
548
|
+
*
|
|
549
|
+
* @param subscription The subscription definition being processed.
|
|
550
|
+
* @param reader The verified reader facts for this operation.
|
|
551
|
+
*/
|
|
552
|
+
#requireReader(subscription, reader) {
|
|
553
|
+
const id = subscription.id?.value;
|
|
554
|
+
const previous = id === undefined ? undefined : this.#readers.get(id);
|
|
555
|
+
if (id === undefined || previous === undefined)
|
|
556
|
+
throw ReadRequestStatus.unknownSubscription();
|
|
557
|
+
if (!ReaderIdentities.matches(ReaderIdentities.from(previous), ReaderIdentities.from(reader)))
|
|
558
|
+
throw new ConnectError("Subscription reader is not authorized.", Code.PermissionDenied);
|
|
559
|
+
this.#readers.set(id, reader);
|
|
560
|
+
for (const child of this.#children.get(id) ?? [])
|
|
561
|
+
this.#readers.set(child, reader);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Associates one native child with the admitted logical reader.
|
|
565
|
+
*
|
|
566
|
+
* @param definition The serialized subscription definition.
|
|
567
|
+
* @param child The native child subscription definition.
|
|
568
|
+
*/
|
|
569
|
+
#rememberChild(definition, child) {
|
|
570
|
+
const id = fromBinary(SubscriptionSchema, definition).id?.value;
|
|
571
|
+
const childId = fromBinary(SubscriptionSchema, child).id?.value;
|
|
572
|
+
if (id === undefined || childId === undefined)
|
|
573
|
+
return;
|
|
574
|
+
const logicalId = this.#logicalForChild.get(id) ?? (this.#readers.has(id) ? id : undefined);
|
|
575
|
+
if (logicalId === undefined)
|
|
576
|
+
return;
|
|
577
|
+
const children = this.#children.get(logicalId) ?? new Set();
|
|
578
|
+
children.add(childId);
|
|
579
|
+
this.#children.set(logicalId, children);
|
|
580
|
+
this.#logicalForChild.set(childId, logicalId);
|
|
581
|
+
const reader = this.#readers.get(id);
|
|
582
|
+
if (reader !== undefined)
|
|
583
|
+
this.#readers.set(childId, reader);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Returns current detached facts for a logical or child subscription.
|
|
587
|
+
*
|
|
588
|
+
* @param bytes The serialized message bytes.
|
|
589
|
+
* @returns The verified reader for this subscription.
|
|
590
|
+
*/
|
|
591
|
+
#readerFor(bytes) {
|
|
592
|
+
const id = fromBinary(SubscriptionSchema, bytes).id?.value;
|
|
593
|
+
const reader = id === undefined ? undefined : this.#readers.get(id);
|
|
594
|
+
return reader === undefined ? undefined : snapshotTrustedReader(reader);
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Removes a finished logical definition and its current child mappings.
|
|
598
|
+
*
|
|
599
|
+
* @param id The stable identifier for this record.
|
|
600
|
+
*/
|
|
601
|
+
#forgetReader(id) {
|
|
602
|
+
this.#readers.delete(id);
|
|
603
|
+
for (const child of this.#children.get(id) ?? []) {
|
|
604
|
+
this.#readers.delete(child);
|
|
605
|
+
this.#logicalForChild.delete(child);
|
|
606
|
+
}
|
|
607
|
+
this.#children.delete(id);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Removes a pending activation timer and its tracking entry.
|
|
611
|
+
*
|
|
612
|
+
* @param subscription The subscription definition being processed.
|
|
613
|
+
*/
|
|
347
614
|
#clearPendingActivation(subscription) {
|
|
348
615
|
const id = subscription.id?.value;
|
|
349
616
|
if (id === undefined)
|
|
@@ -354,6 +621,13 @@ export class NodeCoordinator {
|
|
|
354
621
|
clearTimeout(pending.timer);
|
|
355
622
|
this.#pendingActivations.delete(id);
|
|
356
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Expires an unactivated subscription after its handshake window.
|
|
626
|
+
*
|
|
627
|
+
* @param id The stable identifier for this record.
|
|
628
|
+
* @param pending Pending activation or retry state.
|
|
629
|
+
* @returns Completion after the operation settles.
|
|
630
|
+
*/
|
|
357
631
|
async #expirePendingActivation(id, pending) {
|
|
358
632
|
if (this.#pendingActivations.get(id) !== pending)
|
|
359
633
|
return;
|
|
@@ -367,6 +641,11 @@ export class NodeCoordinator {
|
|
|
367
641
|
this.#schedulePendingActivation(fromBinary(SubscriptionSchema, pending.definition));
|
|
368
642
|
}
|
|
369
643
|
}
|
|
644
|
+
/**
|
|
645
|
+
* Reconciles managed child subscriptions after topology changes.
|
|
646
|
+
*
|
|
647
|
+
* @returns Completion after the operation settles.
|
|
648
|
+
*/
|
|
370
649
|
#reconcile() {
|
|
371
650
|
if (this.#members.onRelaySynchronized !== undefined)
|
|
372
651
|
return this.#subscriptionKernel
|
|
@@ -377,6 +656,11 @@ export class NodeCoordinator {
|
|
|
377
656
|
this.#subscriptionKernel.reconcile(this.#members.relayMembers()),
|
|
378
657
|
]).then(() => undefined);
|
|
379
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Runs shutdown once while concurrent callers await the same completion.
|
|
661
|
+
*
|
|
662
|
+
* @returns Completion after the operation settles.
|
|
663
|
+
*/
|
|
380
664
|
async #closeOnce() {
|
|
381
665
|
this.#stopMembers.forEach((stop) => {
|
|
382
666
|
stop();
|
|
@@ -395,16 +679,16 @@ export class NodeCoordinator {
|
|
|
395
679
|
* @internal
|
|
396
680
|
*/
|
|
397
681
|
export class SubscriptionUpdateQueue {
|
|
682
|
+
#active;
|
|
398
683
|
#updates = [];
|
|
399
684
|
#waiters = [];
|
|
400
|
-
#delivered = [];
|
|
401
685
|
#limit;
|
|
402
686
|
#onOverflow;
|
|
403
687
|
#closed = false;
|
|
404
688
|
/**
|
|
405
689
|
* Creates one terminal bounded update queue.
|
|
406
690
|
*
|
|
407
|
-
* @param limit
|
|
691
|
+
* @param limit Maximum waiting updates beyond one active update.
|
|
408
692
|
* @param onOverflow Aborts the public relay when the queue reaches its bound.
|
|
409
693
|
*/
|
|
410
694
|
constructor(limit, onOverflow = () => undefined) {
|
|
@@ -420,18 +704,22 @@ export class SubscriptionUpdateQueue {
|
|
|
420
704
|
push(update) {
|
|
421
705
|
if (this.#closed)
|
|
422
706
|
return Promise.resolve();
|
|
423
|
-
|
|
424
|
-
if (waiter !== undefined) {
|
|
425
|
-
waiter({ value: update, done: false });
|
|
426
|
-
return Promise.resolve();
|
|
427
|
-
}
|
|
428
|
-
if (this.#updates.length >= this.#limit) {
|
|
707
|
+
if (this.#active !== undefined && this.#updates.length >= this.#limit) {
|
|
429
708
|
this.#onOverflow();
|
|
430
709
|
this.close();
|
|
431
710
|
return Promise.resolve();
|
|
432
711
|
}
|
|
433
|
-
|
|
434
|
-
|
|
712
|
+
return new Promise((resolve) => {
|
|
713
|
+
const entry = { update, delivered: false, resolve };
|
|
714
|
+
if (this.#active === undefined) {
|
|
715
|
+
this.#active = entry;
|
|
716
|
+
const waiter = this.#waiters.shift();
|
|
717
|
+
if (waiter !== undefined)
|
|
718
|
+
waiter(this.#deliver(entry));
|
|
719
|
+
}
|
|
720
|
+
else
|
|
721
|
+
this.#updates.push(entry);
|
|
722
|
+
});
|
|
435
723
|
}
|
|
436
724
|
/**
|
|
437
725
|
* Closes the queue and releases all waiting producers and consumers.
|
|
@@ -440,11 +728,20 @@ export class SubscriptionUpdateQueue {
|
|
|
440
728
|
if (this.#closed)
|
|
441
729
|
return;
|
|
442
730
|
this.#closed = true;
|
|
443
|
-
this.#
|
|
731
|
+
this.#active?.resolve();
|
|
732
|
+
this.#active = undefined;
|
|
733
|
+
for (const update of this.#updates.splice(0))
|
|
734
|
+
update.resolve();
|
|
444
735
|
for (const waiter of this.#waiters.splice(0))
|
|
445
736
|
waiter({ value: undefined, done: true });
|
|
446
|
-
|
|
447
|
-
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Resolves one waiting consumer with the next queued subscription update.
|
|
740
|
+
*/
|
|
741
|
+
#deliver(entry) {
|
|
742
|
+
entry.delivered = true;
|
|
743
|
+
entry.resolve();
|
|
744
|
+
return { value: entry.update, done: false };
|
|
448
745
|
}
|
|
449
746
|
/**
|
|
450
747
|
* Returns queued updates until the terminal close.
|
|
@@ -454,13 +751,12 @@ export class SubscriptionUpdateQueue {
|
|
|
454
751
|
[Symbol.asyncIterator]() {
|
|
455
752
|
return {
|
|
456
753
|
next: () => {
|
|
457
|
-
const update = this.#updates.shift();
|
|
458
|
-
if (update !== undefined) {
|
|
459
|
-
this.#delivered.shift()?.();
|
|
460
|
-
return Promise.resolve({ value: update, done: false });
|
|
461
|
-
}
|
|
462
754
|
if (this.#closed)
|
|
463
755
|
return Promise.resolve({ value: undefined, done: true });
|
|
756
|
+
if (this.#active?.delivered)
|
|
757
|
+
this.#active = this.#updates.shift();
|
|
758
|
+
if (this.#active !== undefined)
|
|
759
|
+
return Promise.resolve(this.#deliver(this.#active));
|
|
464
760
|
return new Promise((resolve) => this.#waiters.push(resolve));
|
|
465
761
|
},
|
|
466
762
|
};
|
|
@@ -472,20 +768,35 @@ const NodeCoordinatorValues = Object.freeze({
|
|
|
472
768
|
*
|
|
473
769
|
* @param members The ready member source when available.
|
|
474
770
|
* @returns The configured membership kernel options.
|
|
771
|
+
* @param issuer The configured forwarding proof producer.
|
|
772
|
+
* @param maxBytes The existing native message-size bound.
|
|
773
|
+
* @param readerFor Reader lookup for a retained subscription.
|
|
774
|
+
* @param onRememberChild Records a native child ID for its logical subscription.
|
|
475
775
|
*/
|
|
476
|
-
unaryKernelOptions(members) {
|
|
477
|
-
// The Coordinator deliberately uses only the kernel's member lifecycle and
|
|
478
|
-
// one-shot forwarding operations. Subscription fan-out is owned by T-0208.
|
|
776
|
+
unaryKernelOptions(members, issuer, maxBytes = defaultMessageMaxBytes, readerFor, onRememberChild) {
|
|
479
777
|
return {
|
|
480
|
-
create: (member) => Promise.resolve(NodeCoordinatorValues.client(member, members)),
|
|
778
|
+
create: (member) => Promise.resolve(NodeCoordinatorValues.client(member, members, issuer, maxBytes, readerFor, onRememberChild)),
|
|
481
779
|
memberKey: (member) => `${member.slot.toString()}/${member.incarnation}`,
|
|
482
780
|
sameMember: (left, right) => left.endpoint === right.endpoint,
|
|
483
781
|
definitionKey: (definition) => fromBinary(SubscriptionSchema, definition).id?.value,
|
|
484
|
-
childDefinition: NodeCoordinatorValues.
|
|
782
|
+
childDefinition: (definition, member) => NodeCoordinatorValues.rememberChildDefinition(definition, member, onRememberChild),
|
|
485
783
|
childSize: (child) => child.byteLength,
|
|
486
784
|
...NodeCoordinatorValues.activationAckOptions(members),
|
|
487
785
|
};
|
|
488
786
|
},
|
|
787
|
+
/**
|
|
788
|
+
* Builds and records a child definition before member subscription.
|
|
789
|
+
*
|
|
790
|
+
* @param definition The serialized subscription definition.
|
|
791
|
+
* @param member The selected backend member.
|
|
792
|
+
* @param onRememberChild Records a native child ID for its logical subscription.
|
|
793
|
+
* @returns The retained managed-child definition.
|
|
794
|
+
*/
|
|
795
|
+
rememberChildDefinition(definition, member, onRememberChild) {
|
|
796
|
+
const child = NodeCoordinatorValues.childDefinition(definition, member);
|
|
797
|
+
onRememberChild?.(definition, child);
|
|
798
|
+
return child;
|
|
799
|
+
},
|
|
489
800
|
/**
|
|
490
801
|
* Builds strict child ACK validation for the subscription membership kernel.
|
|
491
802
|
*
|
|
@@ -526,49 +837,172 @@ const NodeCoordinatorValues = Object.freeze({
|
|
|
526
837
|
* @param member The discovered backend member.
|
|
527
838
|
* @param members The ready member source when available.
|
|
528
839
|
* @returns The member transport client.
|
|
840
|
+
* @param issuer The configured forwarding proof producer.
|
|
841
|
+
* @param maxBytes The existing native message-size bound.
|
|
842
|
+
* @param readerFor Reader lookup for a retained subscription.
|
|
843
|
+
* @param onRememberChild Records a native child ID for its logical subscription.
|
|
529
844
|
*/
|
|
530
|
-
client(member, members) {
|
|
845
|
+
client(member, members, issuer, maxBytes = defaultMessageMaxBytes, readerFor, onRememberChild) {
|
|
531
846
|
const manager = new Http2SessionManager(member.endpoint);
|
|
532
847
|
const transport = createGrpcTransport({ baseUrl: member.endpoint, sessionManager: manager });
|
|
533
848
|
return {
|
|
534
|
-
forward: (request) => NodeCoordinatorValues.forward(transport, request),
|
|
535
|
-
close: () =>
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
},
|
|
539
|
-
subscribe: async (definition, signal) => {
|
|
540
|
-
const subscription = fromBinary(SubscriptionSchema, definition);
|
|
541
|
-
const created = await createClient(SubscriptionService, transport).subscribe(NodeCoordinatorValues.requiredValue(subscription.topic, "Coordinator subscription definition is missing a Topic."), { signal });
|
|
542
|
-
return toBinary(SubscriptionSchema, created);
|
|
543
|
-
},
|
|
544
|
-
activate: (child, updates, signal) => NodeCoordinatorValues.activateChild(transport, member, members, child, updates, signal),
|
|
545
|
-
dispose: async (child, signal) => {
|
|
546
|
-
const subscription = fromBinary(SubscriptionSchema, child);
|
|
547
|
-
members?.onChildSubscriptionCancelled?.(member, subscription);
|
|
548
|
-
await createClient(SubscriptionService, transport).cancel(subscription, { signal });
|
|
549
|
-
},
|
|
849
|
+
forward: (request) => NodeCoordinatorValues.forward(transport, request, issuer, maxBytes),
|
|
850
|
+
close: () => NodeCoordinatorValues.closeManager(manager),
|
|
851
|
+
subscribe: (definition, signal) => NodeCoordinatorValues.subscribeChild(transport, definition, signal, readerFor?.(definition), issuer, maxBytes, onRememberChild),
|
|
852
|
+
...NodeCoordinatorValues.childOperations(transport, member, members, issuer, maxBytes, readerFor),
|
|
550
853
|
};
|
|
551
854
|
},
|
|
552
855
|
/**
|
|
553
|
-
*
|
|
856
|
+
* Closes the transport manager for a departed native member.
|
|
857
|
+
*
|
|
858
|
+
* @param manager Native session manager.
|
|
859
|
+
* @returns Completion after the operation settles.
|
|
860
|
+
*/
|
|
861
|
+
closeManager(manager) {
|
|
862
|
+
manager.abort();
|
|
863
|
+
return Promise.resolve();
|
|
864
|
+
},
|
|
865
|
+
/**
|
|
866
|
+
* Returns streaming and cancellation operations for a native member.
|
|
867
|
+
*
|
|
868
|
+
* @param transport The connected native transport.
|
|
869
|
+
* @param member The selected backend member.
|
|
870
|
+
* @param members The available backend member source.
|
|
871
|
+
* @param issuer The configured forwarding proof producer.
|
|
872
|
+
* @param maxBytes The existing native message-size bound.
|
|
873
|
+
* @param readerFor Reader lookup for a retained subscription.
|
|
874
|
+
* @returns The managed-child operations.
|
|
875
|
+
*/
|
|
876
|
+
childOperations(transport, member, members, issuer, maxBytes, readerFor) {
|
|
877
|
+
return {
|
|
878
|
+
activate: NodeCoordinatorValues.activation(transport, member, members, issuer, maxBytes, readerFor),
|
|
879
|
+
dispose: (child, signal) => NodeCoordinatorValues.cancelChild(transport, member, members, child, signal, readerFor?.(child), issuer, maxBytes),
|
|
880
|
+
};
|
|
881
|
+
},
|
|
882
|
+
/**
|
|
883
|
+
* Captures native member context for one child stream.
|
|
884
|
+
*
|
|
885
|
+
* @param transport The connected native transport.
|
|
886
|
+
* @param member The selected backend member.
|
|
887
|
+
* @param members The available backend member source.
|
|
888
|
+
* @param issuer The configured forwarding proof producer.
|
|
889
|
+
* @param maxBytes The existing native message-size bound.
|
|
890
|
+
* @param readerFor Reader lookup for a retained subscription.
|
|
891
|
+
* @returns The activation stream completion.
|
|
892
|
+
*/
|
|
893
|
+
activation(transport, member, members, issuer, maxBytes, readerFor) {
|
|
894
|
+
return (child, updates, signal) => NodeCoordinatorValues.activateChild(transport, member, members, child, updates, signal, readerFor?.(child), issuer, maxBytes);
|
|
895
|
+
},
|
|
896
|
+
/**
|
|
897
|
+
* Creates one native child and records any backend-assigned ID.
|
|
898
|
+
*
|
|
899
|
+
* @param transport The connected native transport.
|
|
900
|
+
* @param definition The serialized subscription definition.
|
|
901
|
+
* @param signal The cancellation signal for this operation.
|
|
902
|
+
* @param reader The verified reader facts for this operation.
|
|
903
|
+
* @param issuer The configured forwarding proof producer.
|
|
904
|
+
* @param maxBytes The existing native message-size bound.
|
|
905
|
+
* @param onRememberChild Records a native child ID for its logical subscription.
|
|
906
|
+
* @returns The managed-child subscription.
|
|
907
|
+
*/
|
|
908
|
+
async subscribeChild(transport, definition, signal, reader, issuer, maxBytes, onRememberChild) {
|
|
909
|
+
const subscription = fromBinary(SubscriptionSchema, definition);
|
|
910
|
+
const topic = NodeCoordinatorValues.requiredValue(subscription.topic, "Coordinator subscription definition is missing a Topic.");
|
|
911
|
+
const headers = await NodeCoordinatorValues.readerHeaders("Subscribe", toBinary(TopicSchema, topic), reader, issuer, maxBytes);
|
|
912
|
+
const created = await createClient(SubscriptionService, transport).subscribe(topic, {
|
|
913
|
+
signal,
|
|
914
|
+
...(headers === undefined ? {} : { headers }),
|
|
915
|
+
});
|
|
916
|
+
const bytes = toBinary(SubscriptionSchema, created);
|
|
917
|
+
onRememberChild?.(definition, bytes);
|
|
918
|
+
return bytes;
|
|
919
|
+
},
|
|
920
|
+
/**
|
|
921
|
+
* Cancels one native child with the same verified reader as its creation.
|
|
922
|
+
*
|
|
923
|
+
* @param transport The connected native transport.
|
|
924
|
+
* @param member The selected backend member.
|
|
925
|
+
* @param members The available backend member source.
|
|
926
|
+
* @param child The native child subscription definition.
|
|
927
|
+
* @param signal The cancellation signal for this operation.
|
|
928
|
+
* @param reader The verified reader facts for this operation.
|
|
929
|
+
* @param issuer The configured forwarding proof producer.
|
|
930
|
+
* @param maxBytes The existing native message-size bound.
|
|
931
|
+
* @returns Completion after the operation settles.
|
|
932
|
+
*/
|
|
933
|
+
async cancelChild(transport, member, members, child, signal, reader, issuer, maxBytes) {
|
|
934
|
+
const subscription = fromBinary(SubscriptionSchema, child);
|
|
935
|
+
members?.onChildSubscriptionCancelled?.(member, subscription);
|
|
936
|
+
const headers = await NodeCoordinatorValues.readerHeaders("Cancel", child, reader, issuer, maxBytes);
|
|
937
|
+
await createClient(SubscriptionService, transport).cancel(subscription, {
|
|
938
|
+
signal,
|
|
939
|
+
...(headers === undefined ? {} : { headers }),
|
|
940
|
+
});
|
|
941
|
+
},
|
|
942
|
+
/**
|
|
943
|
+
* Sends one native Command, entity read, or App Query to a ready member.
|
|
554
944
|
*
|
|
555
945
|
* @param transport The connected native transport.
|
|
556
946
|
* @param request The request to forward.
|
|
557
947
|
* @returns Empty routing bytes after the response is assigned.
|
|
948
|
+
* @param issuer The configured forwarding proof producer.
|
|
949
|
+
* @param maxBytes The existing native message-size bound.
|
|
558
950
|
*/
|
|
559
|
-
async forward(transport, request) {
|
|
951
|
+
async forward(transport, request, issuer, maxBytes = defaultMessageMaxBytes) {
|
|
952
|
+
const headers = await NodeCoordinatorValues.forwardHeaders(request, issuer, maxBytes);
|
|
560
953
|
if (request.kind === "command") {
|
|
561
|
-
|
|
954
|
+
await NodeCoordinatorValues.forwardCommand(transport, request, headers);
|
|
955
|
+
}
|
|
956
|
+
else if (request.kind === "query") {
|
|
957
|
+
const response = await transport.unary(QueryService.method.read, request.context.signal, request.context.timeoutMs(), headers, request.query);
|
|
562
958
|
NodeCoordinatorValues.responseMetadata(response.header, response.trailer, request.context);
|
|
563
959
|
request.response = response.message;
|
|
564
960
|
}
|
|
565
961
|
else {
|
|
566
|
-
const response = await transport.unary(QueryService.method.
|
|
962
|
+
const response = await transport.unary(QueryService.method.execute, request.context.signal, request.context.timeoutMs(), headers, request.query);
|
|
567
963
|
NodeCoordinatorValues.responseMetadata(response.header, response.trailer, request.context);
|
|
568
964
|
request.response = response.message;
|
|
569
965
|
}
|
|
570
966
|
return new Uint8Array();
|
|
571
967
|
},
|
|
968
|
+
/**
|
|
969
|
+
* Invokes the Command branch and retains its native response metadata.
|
|
970
|
+
*
|
|
971
|
+
* @param transport The selected native child transport.
|
|
972
|
+
* @param request The admitted Command and response slot.
|
|
973
|
+
* @param headers Operation-bound verified-reader metadata.
|
|
974
|
+
* @returns Completion after assigning the native response.
|
|
975
|
+
*/
|
|
976
|
+
async forwardCommand(transport, request, headers) {
|
|
977
|
+
const response = await transport.unary(CommandService.method.post, request.context.signal, request.context.timeoutMs(), headers, request.command);
|
|
978
|
+
NodeCoordinatorValues.responseMetadata(response.header, response.trailer, request.context);
|
|
979
|
+
request.response = response.message;
|
|
980
|
+
},
|
|
981
|
+
/**
|
|
982
|
+
* Encodes verified reader facts into one exact native unary operation.
|
|
983
|
+
*
|
|
984
|
+
* @param request The request being processed.
|
|
985
|
+
* @param issuer The configured forwarding proof producer.
|
|
986
|
+
* @param maxBytes The existing native message-size bound.
|
|
987
|
+
* @returns The operation-bound forwarding headers.
|
|
988
|
+
*/
|
|
989
|
+
async forwardHeaders(request, issuer, maxBytes) {
|
|
990
|
+
const service = request.kind === "command" ? "spine.client.CommandService" : "spine.client.QueryService";
|
|
991
|
+
const method = request.kind === "command" ? "Post" : request.kind === "query" ? "Read" : "Execute";
|
|
992
|
+
const value = request.kind === "command"
|
|
993
|
+
? toBinary(CommandService.method.post.input, request.command)
|
|
994
|
+
: request.kind === "query"
|
|
995
|
+
? toBinary(QuerySchema, request.query)
|
|
996
|
+
: toBinary(AppQuerySchema, request.query);
|
|
997
|
+
const headers = NodeCoordinatorValues.applicationHeaders(request.context.requestHeader);
|
|
998
|
+
if (issuer === undefined &&
|
|
999
|
+
(request.reader.principal !== undefined || request.reader.authorityFacts !== undefined))
|
|
1000
|
+
throw new ConnectError("Managed reader forwarding is unavailable.", Code.Internal);
|
|
1001
|
+
if (issuer !== undefined)
|
|
1002
|
+
for (const [name, entry] of Object.entries(await ReaderForwarding.seal(service, method, value, request.reader, issuer, maxBytes)))
|
|
1003
|
+
headers.set(name, entry);
|
|
1004
|
+
return headers;
|
|
1005
|
+
},
|
|
572
1006
|
/**
|
|
573
1007
|
* Streams validated updates from one native child subscription.
|
|
574
1008
|
*
|
|
@@ -579,25 +1013,59 @@ const NodeCoordinatorValues = Object.freeze({
|
|
|
579
1013
|
* @param updates The logical update sink.
|
|
580
1014
|
* @param signal Cancels child activation.
|
|
581
1015
|
* @returns Completion after the child stream closes.
|
|
1016
|
+
* @param reader The verified reader facts for this operation.
|
|
1017
|
+
* @param issuer The configured forwarding proof producer.
|
|
1018
|
+
* @param maxBytes The existing native message-size bound.
|
|
582
1019
|
*/
|
|
583
|
-
async activateChild(transport, member, members, child, updates, signal) {
|
|
1020
|
+
async activateChild(transport, member, members, child, updates, signal, reader, issuer, maxBytes = defaultMessageMaxBytes) {
|
|
584
1021
|
const subscription = fromBinary(SubscriptionSchema, child);
|
|
585
1022
|
members?.onChildSubscriptionActivated?.(member, subscription);
|
|
1023
|
+
const headers = await NodeCoordinatorValues.readerHeaders("Activate", child, reader, issuer, maxBytes);
|
|
586
1024
|
for await (const update of createClient(SubscriptionService, transport).activate(subscription, {
|
|
587
1025
|
signal,
|
|
1026
|
+
...(headers === undefined ? {} : { headers }),
|
|
588
1027
|
})) {
|
|
589
|
-
|
|
590
|
-
const updateTopic = update.subscription?.topic;
|
|
591
|
-
if (update.subscription?.id?.value !== subscription.id?.value ||
|
|
592
|
-
topic === undefined ||
|
|
593
|
-
updateTopic === undefined ||
|
|
594
|
-
!equals(TopicSchema, updateTopic, topic))
|
|
595
|
-
throw new Error("Backend child update does not match its subscription.");
|
|
1028
|
+
NodeCoordinatorValues.checkChildUpdate(subscription, update);
|
|
596
1029
|
await updates(toBinary(SubscriptionUpdateSchema, update));
|
|
597
1030
|
}
|
|
598
1031
|
},
|
|
599
1032
|
/**
|
|
600
|
-
*
|
|
1033
|
+
* Rejects a native update for a different child ID or Topic.
|
|
1034
|
+
*
|
|
1035
|
+
* @param subscription The subscription definition being processed.
|
|
1036
|
+
* @param update Native subscription update.
|
|
1037
|
+
*/
|
|
1038
|
+
checkChildUpdate(subscription, update) {
|
|
1039
|
+
const topic = subscription.topic;
|
|
1040
|
+
const updateTopic = update.subscription?.topic;
|
|
1041
|
+
if (update.subscription?.id?.value !== subscription.id?.value ||
|
|
1042
|
+
topic === undefined ||
|
|
1043
|
+
updateTopic === undefined ||
|
|
1044
|
+
!equals(TopicSchema, updateTopic, topic))
|
|
1045
|
+
throw new Error("Backend child update does not match its subscription.");
|
|
1046
|
+
},
|
|
1047
|
+
/**
|
|
1048
|
+
* Encodes one verified reader against the exact child subscription RPC.
|
|
1049
|
+
*
|
|
1050
|
+
* @param method The canonical RPC method name.
|
|
1051
|
+
* @param value Canonical child subscription RPC bytes.
|
|
1052
|
+
* @param reader The verified reader facts for this operation.
|
|
1053
|
+
* @param issuer The configured forwarding proof producer.
|
|
1054
|
+
* @param maxBytes The existing native message-size bound.
|
|
1055
|
+
* @returns The canonical reader assertion headers.
|
|
1056
|
+
*/
|
|
1057
|
+
async readerHeaders(method, value, reader, issuer, maxBytes) {
|
|
1058
|
+
if (reader === undefined)
|
|
1059
|
+
return undefined;
|
|
1060
|
+
if (issuer === undefined) {
|
|
1061
|
+
if (reader.principal !== undefined || reader.authorityFacts !== undefined)
|
|
1062
|
+
throw new ConnectError("Managed reader forwarding is unavailable.", Code.Internal);
|
|
1063
|
+
return undefined;
|
|
1064
|
+
}
|
|
1065
|
+
return new Headers(await ReaderForwarding.seal("spine.client.SubscriptionService", method, value, reader, issuer, maxBytes));
|
|
1066
|
+
},
|
|
1067
|
+
/**
|
|
1068
|
+
* Snapshots backend response metadata into the public context.
|
|
601
1069
|
*
|
|
602
1070
|
* @param headers Headers received from the backend.
|
|
603
1071
|
* @param trailers Trailers received from the backend.
|
|
@@ -610,7 +1078,7 @@ const NodeCoordinatorValues = Object.freeze({
|
|
|
610
1078
|
context.responseTrailer.append(name, value);
|
|
611
1079
|
},
|
|
612
1080
|
/**
|
|
613
|
-
*
|
|
1081
|
+
* Snapshots application headers for forwarding.
|
|
614
1082
|
*
|
|
615
1083
|
* @param headers Headers received from the backend.
|
|
616
1084
|
* @returns Forwardable application headers.
|
|
@@ -622,7 +1090,9 @@ const NodeCoordinatorValues = Object.freeze({
|
|
|
622
1090
|
name === "te" ||
|
|
623
1091
|
name === "user-agent" ||
|
|
624
1092
|
name.startsWith("grpc-") ||
|
|
625
|
-
name.startsWith("connect-")
|
|
1093
|
+
name.startsWith("connect-") ||
|
|
1094
|
+
name === "spine-internal-reader" ||
|
|
1095
|
+
name === "spine-internal-reader-proof")
|
|
626
1096
|
forwarded.delete(name);
|
|
627
1097
|
}
|
|
628
1098
|
return forwarded;
|