@spine-event-engine/server 2.0.0-snapshot.11
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/LICENSE +201 -0
- package/README.md +209 -0
- package/REFERENCE.md +760 -0
- package/dist/browser/browser-server.d.ts +95 -0
- package/dist/browser/browser-server.d.ts.map +1 -0
- package/dist/browser/browser-server.js +719 -0
- package/dist/browser/browser-server.js.map +1 -0
- package/dist/browser/durable-subscription-bindings.d.ts +122 -0
- package/dist/browser/durable-subscription-bindings.d.ts.map +1 -0
- package/dist/browser/durable-subscription-bindings.js +524 -0
- package/dist/browser/durable-subscription-bindings.js.map +1 -0
- package/dist/browser/index.d.ts +12 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +24 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/options.d.ts +183 -0
- package/dist/browser/options.d.ts.map +1 -0
- package/dist/browser/options.js +15 -0
- package/dist/browser/options.js.map +1 -0
- package/dist/bus/command-bus.d.ts +63 -0
- package/dist/bus/command-bus.d.ts.map +1 -0
- package/dist/bus/command-bus.js +236 -0
- package/dist/bus/command-bus.js.map +1 -0
- package/dist/bus/command-dispatcher-registry.d.ts +33 -0
- package/dist/bus/command-dispatcher-registry.d.ts.map +1 -0
- package/dist/bus/command-dispatcher-registry.js +75 -0
- package/dist/bus/command-dispatcher-registry.js.map +1 -0
- package/dist/bus/command-dispatcher.d.ts +25 -0
- package/dist/bus/command-dispatcher.d.ts.map +1 -0
- package/dist/bus/command-dispatcher.js +15 -0
- package/dist/bus/command-dispatcher.js.map +1 -0
- package/dist/bus/command-errors.d.ts +23 -0
- package/dist/bus/command-errors.d.ts.map +1 -0
- package/dist/bus/command-errors.js +52 -0
- package/dist/bus/command-errors.js.map +1 -0
- package/dist/bus/event-bus.d.ts +113 -0
- package/dist/bus/event-bus.d.ts.map +1 -0
- package/dist/bus/event-bus.js +518 -0
- package/dist/bus/event-bus.js.map +1 -0
- package/dist/bus/event-dispatcher-origin-schemas.d.ts +17 -0
- package/dist/bus/event-dispatcher-origin-schemas.d.ts.map +1 -0
- package/dist/bus/event-dispatcher-origin-schemas.js +32 -0
- package/dist/bus/event-dispatcher-origin-schemas.js.map +1 -0
- package/dist/bus/event-dispatcher-registry.d.ts +48 -0
- package/dist/bus/event-dispatcher-registry.d.ts.map +1 -0
- package/dist/bus/event-dispatcher-registry.js +176 -0
- package/dist/bus/event-dispatcher-registry.js.map +1 -0
- package/dist/bus/event-dispatcher.d.ts +48 -0
- package/dist/bus/event-dispatcher.d.ts.map +1 -0
- package/dist/bus/event-dispatcher.js +15 -0
- package/dist/bus/event-dispatcher.js.map +1 -0
- package/dist/context/bounded-context.d.ts +592 -0
- package/dist/context/bounded-context.d.ts.map +1 -0
- package/dist/context/bounded-context.js +1897 -0
- package/dist/context/bounded-context.js.map +1 -0
- package/dist/context/entity-inbox.d.ts +63 -0
- package/dist/context/entity-inbox.d.ts.map +1 -0
- package/dist/context/entity-inbox.js +387 -0
- package/dist/context/entity-inbox.js.map +1 -0
- package/dist/context/local-inbox-handoff.d.ts +248 -0
- package/dist/context/local-inbox-handoff.d.ts.map +1 -0
- package/dist/context/local-inbox-handoff.js +340 -0
- package/dist/context/local-inbox-handoff.js.map +1 -0
- package/dist/context/projection-handoff.d.ts +46 -0
- package/dist/context/projection-handoff.d.ts.map +1 -0
- package/dist/context/projection-handoff.js +177 -0
- package/dist/context/projection-handoff.js.map +1 -0
- package/dist/context/tenant-index.d.ts +41 -0
- package/dist/context/tenant-index.d.ts.map +1 -0
- package/dist/context/tenant-index.js +93 -0
- package/dist/context/tenant-index.js.map +1 -0
- package/dist/delivery/conditional-pickup.d.ts +25 -0
- package/dist/delivery/conditional-pickup.d.ts.map +1 -0
- package/dist/delivery/conditional-pickup.js +26 -0
- package/dist/delivery/conditional-pickup.js.map +1 -0
- package/dist/delivery/delivery-builder.d.ts +216 -0
- package/dist/delivery/delivery-builder.d.ts.map +1 -0
- package/dist/delivery/delivery-builder.js +323 -0
- package/dist/delivery/delivery-builder.js.map +1 -0
- package/dist/delivery/delivery-lease.d.ts +15 -0
- package/dist/delivery/delivery-lease.d.ts.map +1 -0
- package/dist/delivery/delivery-lease.js +44 -0
- package/dist/delivery/delivery-lease.js.map +1 -0
- package/dist/delivery/delivery-loop.d.ts +113 -0
- package/dist/delivery/delivery-loop.d.ts.map +1 -0
- package/dist/delivery/delivery-loop.js +119 -0
- package/dist/delivery/delivery-loop.js.map +1 -0
- package/dist/delivery/delivery-monitor.d.ts +165 -0
- package/dist/delivery/delivery-monitor.d.ts.map +1 -0
- package/dist/delivery/delivery-monitor.js +168 -0
- package/dist/delivery/delivery-monitor.js.map +1 -0
- package/dist/delivery/delivery-ports.d.ts +151 -0
- package/dist/delivery/delivery-ports.d.ts.map +1 -0
- package/dist/delivery/delivery-ports.js +15 -0
- package/dist/delivery/delivery-ports.js.map +1 -0
- package/dist/delivery/delivery-run-control.d.ts +50 -0
- package/dist/delivery/delivery-run-control.d.ts.map +1 -0
- package/dist/delivery/delivery-run-control.js +66 -0
- package/dist/delivery/delivery-run-control.js.map +1 -0
- package/dist/delivery/delivery-run-coordinator.d.ts +218 -0
- package/dist/delivery/delivery-run-coordinator.d.ts.map +1 -0
- package/dist/delivery/delivery-run-coordinator.js +609 -0
- package/dist/delivery/delivery-run-coordinator.js.map +1 -0
- package/dist/delivery/delivery-storage-error.d.ts +13 -0
- package/dist/delivery/delivery-storage-error.d.ts.map +1 -0
- package/dist/delivery/delivery-storage-error.js +30 -0
- package/dist/delivery/delivery-storage-error.js.map +1 -0
- package/dist/delivery/delivery-supervisor.d.ts +175 -0
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -0
- package/dist/delivery/delivery-supervisor.js +469 -0
- package/dist/delivery/delivery-supervisor.js.map +1 -0
- package/dist/delivery/delivery-worker.d.ts +180 -0
- package/dist/delivery/delivery-worker.d.ts.map +1 -0
- package/dist/delivery/delivery-worker.js +232 -0
- package/dist/delivery/delivery-worker.js.map +1 -0
- package/dist/delivery/delivery.d.ts +236 -0
- package/dist/delivery/delivery.d.ts.map +1 -0
- package/dist/delivery/delivery.js +539 -0
- package/dist/delivery/delivery.js.map +1 -0
- package/dist/delivery/inbox-records.d.ts +19 -0
- package/dist/delivery/inbox-records.d.ts.map +1 -0
- package/dist/delivery/inbox-records.js +291 -0
- package/dist/delivery/inbox-records.js.map +1 -0
- package/dist/delivery/inbox-storage.d.ts +83 -0
- package/dist/delivery/inbox-storage.d.ts.map +1 -0
- package/dist/delivery/inbox-storage.js +336 -0
- package/dist/delivery/inbox-storage.js.map +1 -0
- package/dist/delivery/inbox.d.ts +272 -0
- package/dist/delivery/inbox.d.ts.map +1 -0
- package/dist/delivery/inbox.js +183 -0
- package/dist/delivery/inbox.js.map +1 -0
- package/dist/delivery/parked-delivery-obligations.d.ts +182 -0
- package/dist/delivery/parked-delivery-obligations.d.ts.map +1 -0
- package/dist/delivery/parked-delivery-obligations.js +390 -0
- package/dist/delivery/parked-delivery-obligations.js.map +1 -0
- package/dist/delivery/shard-index.d.ts +41 -0
- package/dist/delivery/shard-index.d.ts.map +1 -0
- package/dist/delivery/shard-index.js +67 -0
- package/dist/delivery/shard-index.js.map +1 -0
- package/dist/delivery/sharded-work-registry.d.ts +114 -0
- package/dist/delivery/sharded-work-registry.d.ts.map +1 -0
- package/dist/delivery/sharded-work-registry.js +361 -0
- package/dist/delivery/sharded-work-registry.js.map +1 -0
- package/dist/entity/entity-metadata.d.ts +173 -0
- package/dist/entity/entity-metadata.d.ts.map +1 -0
- package/dist/entity/entity-metadata.js +187 -0
- package/dist/entity/entity-metadata.js.map +1 -0
- package/dist/entity/entity-storage-descriptor.d.ts +64 -0
- package/dist/entity/entity-storage-descriptor.d.ts.map +1 -0
- package/dist/entity/entity-storage-descriptor.js +227 -0
- package/dist/entity/entity-storage-descriptor.js.map +1 -0
- package/dist/entity/entity-transaction.d.ts +372 -0
- package/dist/entity/entity-transaction.d.ts.map +1 -0
- package/dist/entity/entity-transaction.js +413 -0
- package/dist/entity/entity-transaction.js.map +1 -0
- package/dist/entity/entity-transition-validation.d.ts +47 -0
- package/dist/entity/entity-transition-validation.d.ts.map +1 -0
- package/dist/entity/entity-transition-validation.js +327 -0
- package/dist/entity/entity-transition-validation.js.map +1 -0
- package/dist/entity/entity.d.ts +605 -0
- package/dist/entity/entity.d.ts.map +1 -0
- package/dist/entity/entity.js +981 -0
- package/dist/entity/entity.js.map +1 -0
- package/dist/entity/implicit-required-id.d.ts +15 -0
- package/dist/entity/implicit-required-id.d.ts.map +1 -0
- package/dist/entity/implicit-required-id.js +123 -0
- package/dist/entity/implicit-required-id.js.map +1 -0
- package/dist/entity/spec-scanner.d.ts +14 -0
- package/dist/entity/spec-scanner.d.ts.map +1 -0
- package/dist/entity/spec-scanner.js +37 -0
- package/dist/entity/spec-scanner.js.map +1 -0
- package/dist/handler/command-registration-readiness.d.ts +99 -0
- package/dist/handler/command-registration-readiness.d.ts.map +1 -0
- package/dist/handler/command-registration-readiness.js +120 -0
- package/dist/handler/command-registration-readiness.js.map +1 -0
- package/dist/handler/declared-rejections.d.ts +11 -0
- package/dist/handler/declared-rejections.d.ts.map +1 -0
- package/dist/handler/declared-rejections.js +34 -0
- package/dist/handler/declared-rejections.js.map +1 -0
- package/dist/handler/event-handler-filter.d.ts +43 -0
- package/dist/handler/event-handler-filter.d.ts.map +1 -0
- package/dist/handler/event-handler-filter.js +128 -0
- package/dist/handler/event-handler-filter.js.map +1 -0
- package/dist/handler/event-registration-readiness.d.ts +196 -0
- package/dist/handler/event-registration-readiness.d.ts.map +1 -0
- package/dist/handler/event-registration-readiness.js +187 -0
- package/dist/handler/event-registration-readiness.js.map +1 -0
- package/dist/handler/external.d.ts +10 -0
- package/dist/handler/external.d.ts.map +1 -0
- package/dist/handler/external.js +15 -0
- package/dist/handler/external.js.map +1 -0
- package/dist/handler/generated-handler-registry.d.ts +185 -0
- package/dist/handler/generated-handler-registry.d.ts.map +1 -0
- package/dist/handler/generated-handler-registry.js +439 -0
- package/dist/handler/generated-handler-registry.js.map +1 -0
- package/dist/handler/generated-registry-discovery.d.ts +94 -0
- package/dist/handler/generated-registry-discovery.d.ts.map +1 -0
- package/dist/handler/generated-registry-discovery.js +226 -0
- package/dist/handler/generated-registry-discovery.js.map +1 -0
- package/dist/handler/handler-decorators.d.ts +151 -0
- package/dist/handler/handler-decorators.d.ts.map +1 -0
- package/dist/handler/handler-decorators.js +275 -0
- package/dist/handler/handler-decorators.js.map +1 -0
- package/dist/handler/handler-metadata.d.ts +553 -0
- package/dist/handler/handler-metadata.d.ts.map +1 -0
- package/dist/handler/handler-metadata.js +437 -0
- package/dist/handler/handler-metadata.js.map +1 -0
- package/dist/handler/registration-readiness-metadata.d.ts +68 -0
- package/dist/handler/registration-readiness-metadata.d.ts.map +1 -0
- package/dist/handler/registration-readiness-metadata.js +171 -0
- package/dist/handler/registration-readiness-metadata.js.map +1 -0
- package/dist/handler/rejection-source.d.ts +11 -0
- package/dist/handler/rejection-source.d.ts.map +1 -0
- package/dist/handler/rejection-source.js +25 -0
- package/dist/handler/rejection-source.js.map +1 -0
- package/dist/handler/standalone.d.ts +25 -0
- package/dist/handler/standalone.d.ts.map +1 -0
- package/dist/handler/standalone.js +38 -0
- package/dist/handler/standalone.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/external-messages.d.ts +49 -0
- package/dist/integration/external-messages.d.ts.map +1 -0
- package/dist/integration/external-messages.js +112 -0
- package/dist/integration/external-messages.js.map +1 -0
- package/dist/integration/integration-broker.d.ts +74 -0
- package/dist/integration/integration-broker.d.ts.map +1 -0
- package/dist/integration/integration-broker.js +444 -0
- package/dist/integration/integration-broker.js.map +1 -0
- package/dist/integration/third-party-context.d.ts +56 -0
- package/dist/integration/third-party-context.d.ts.map +1 -0
- package/dist/integration/third-party-context.js +130 -0
- package/dist/integration/third-party-context.js.map +1 -0
- package/dist/repository/command-errors.d.ts +25 -0
- package/dist/repository/command-errors.d.ts.map +1 -0
- package/dist/repository/command-errors.js +43 -0
- package/dist/repository/command-errors.js.map +1 -0
- package/dist/repository/command-routing.d.ts +72 -0
- package/dist/repository/command-routing.d.ts.map +1 -0
- package/dist/repository/command-routing.js +98 -0
- package/dist/repository/command-routing.js.map +1 -0
- package/dist/repository/commit-fence.d.ts +22 -0
- package/dist/repository/commit-fence.d.ts.map +1 -0
- package/dist/repository/commit-fence.js +37 -0
- package/dist/repository/commit-fence.js.map +1 -0
- package/dist/repository/event-routing.d.ts +71 -0
- package/dist/repository/event-routing.d.ts.map +1 -0
- package/dist/repository/event-routing.js +96 -0
- package/dist/repository/event-routing.js.map +1 -0
- package/dist/repository/primitive-id.d.ts +75 -0
- package/dist/repository/primitive-id.d.ts.map +1 -0
- package/dist/repository/primitive-id.js +85 -0
- package/dist/repository/primitive-id.js.map +1 -0
- package/dist/repository/repository.d.ts +667 -0
- package/dist/repository/repository.d.ts.map +1 -0
- package/dist/repository/repository.js +3510 -0
- package/dist/repository/repository.js.map +1 -0
- package/dist/repository/routing-declarations.d.ts +75 -0
- package/dist/repository/routing-declarations.d.ts.map +1 -0
- package/dist/repository/routing-declarations.js +101 -0
- package/dist/repository/routing-declarations.js.map +1 -0
- package/dist/repository/state-update-routing.d.ts +66 -0
- package/dist/repository/state-update-routing.d.ts.map +1 -0
- package/dist/repository/state-update-routing.js +91 -0
- package/dist/repository/state-update-routing.js.map +1 -0
- package/dist/runtime/runtime.d.ts +156 -0
- package/dist/runtime/runtime.d.ts.map +1 -0
- package/dist/runtime/runtime.js +215 -0
- package/dist/runtime/runtime.js.map +1 -0
- package/dist/runtime/signal-intake.d.ts +84 -0
- package/dist/runtime/signal-intake.d.ts.map +1 -0
- package/dist/runtime/signal-intake.js +94 -0
- package/dist/runtime/signal-intake.js.map +1 -0
- package/dist/runtime/signal-metadata.d.ts +217 -0
- package/dist/runtime/signal-metadata.d.ts.map +1 -0
- package/dist/runtime/signal-metadata.js +333 -0
- package/dist/runtime/signal-metadata.js.map +1 -0
- package/dist/runtime/signal-publisher.d.ts +144 -0
- package/dist/runtime/signal-publisher.d.ts.map +1 -0
- package/dist/runtime/signal-publisher.js +248 -0
- package/dist/runtime/signal-publisher.js.map +1 -0
- package/dist/runtime/standalone-handler-runtime.d.ts +114 -0
- package/dist/runtime/standalone-handler-runtime.d.ts.map +1 -0
- package/dist/runtime/standalone-handler-runtime.js +307 -0
- package/dist/runtime/standalone-handler-runtime.js.map +1 -0
- package/dist/server/environment-attachment.d.ts +356 -0
- package/dist/server/environment-attachment.d.ts.map +1 -0
- package/dist/server/environment-attachment.js +2062 -0
- package/dist/server/environment-attachment.js.map +1 -0
- package/dist/server/environment-delivery-records.d.ts +77 -0
- package/dist/server/environment-delivery-records.d.ts.map +1 -0
- package/dist/server/environment-delivery-records.js +282 -0
- package/dist/server/environment-delivery-records.js.map +1 -0
- package/dist/server/environment-delivery-worker.d.ts +161 -0
- package/dist/server/environment-delivery-worker.d.ts.map +1 -0
- package/dist/server/environment-delivery-worker.js +673 -0
- package/dist/server/environment-delivery-worker.js.map +1 -0
- package/dist/server/environment.d.ts +43 -0
- package/dist/server/environment.d.ts.map +1 -0
- package/dist/server/environment.js +90 -0
- package/dist/server/environment.js.map +1 -0
- package/dist/server/managed-child-subscription.d.ts +11 -0
- package/dist/server/managed-child-subscription.d.ts.map +1 -0
- package/dist/server/managed-child-subscription.js +44 -0
- package/dist/server/managed-child-subscription.js.map +1 -0
- package/dist/server/managed-server-application.d.ts +327 -0
- package/dist/server/managed-server-application.d.ts.map +1 -0
- package/dist/server/managed-server-application.js +937 -0
- package/dist/server/managed-server-application.js.map +1 -0
- package/dist/server/node-coordinator.d.ts +191 -0
- package/dist/server/node-coordinator.d.ts.map +1 -0
- package/dist/server/node-coordinator.js +552 -0
- package/dist/server/node-coordinator.js.map +1 -0
- package/dist/server/process-server-coordinator.d.ts +15 -0
- package/dist/server/process-server-coordinator.d.ts.map +1 -0
- package/dist/server/process-server-coordinator.js +112 -0
- package/dist/server/process-server-coordinator.js.map +1 -0
- package/dist/server/reserved-spine-rpc-paths.json +8 -0
- package/dist/server/retryable-close.d.ts +28 -0
- package/dist/server/retryable-close.d.ts.map +1 -0
- package/dist/server/retryable-close.js +129 -0
- package/dist/server/retryable-close.js.map +1 -0
- package/dist/server/server-environment.d.ts +180 -0
- package/dist/server/server-environment.d.ts.map +1 -0
- package/dist/server/server-environment.js +418 -0
- package/dist/server/server-environment.js.map +1 -0
- package/dist/server/server-log.d.ts +18 -0
- package/dist/server/server-log.d.ts.map +1 -0
- package/dist/server/server-log.js +100 -0
- package/dist/server/server-log.js.map +1 -0
- package/dist/server/server.d.ts +221 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +704 -0
- package/dist/server/server.js.map +1 -0
- package/dist/services/query-reader.d.ts +19 -0
- package/dist/services/query-reader.d.ts.map +1 -0
- package/dist/services/query-reader.js +43 -0
- package/dist/services/query-reader.js.map +1 -0
- package/dist/services/spine-services.d.ts +88 -0
- package/dist/services/spine-services.d.ts.map +1 -0
- package/dist/services/spine-services.js +1582 -0
- package/dist/services/spine-services.js.map +1 -0
- package/dist/spi/delivery.d.ts +5 -0
- package/dist/spi/delivery.d.ts.map +1 -0
- package/dist/spi/delivery.js +18 -0
- package/dist/spi/delivery.js.map +1 -0
- package/dist/spi/handler-registry.d.ts +8 -0
- package/dist/spi/handler-registry.d.ts.map +1 -0
- package/dist/spi/handler-registry.js +15 -0
- package/dist/spi/handler-registry.js.map +1 -0
- package/dist/stand/stand.d.ts +294 -0
- package/dist/stand/stand.d.ts.map +1 -0
- package/dist/stand/stand.js +681 -0
- package/dist/stand/stand.js.map +1 -0
- package/dist/stand/subscription-observer.d.ts +53 -0
- package/dist/stand/subscription-observer.d.ts.map +1 -0
- package/dist/stand/subscription-observer.js +444 -0
- package/dist/stand/subscription-observer.js.map +1 -0
- package/dist/stand/subscription-records.d.ts +12 -0
- package/dist/stand/subscription-records.d.ts.map +1 -0
- package/dist/stand/subscription-records.js +99 -0
- package/dist/stand/subscription-records.js.map +1 -0
- package/dist/stand/subscription-registry.d.ts +285 -0
- package/dist/stand/subscription-registry.d.ts.map +1 -0
- package/dist/stand/subscription-registry.js +403 -0
- package/dist/stand/subscription-registry.js.map +1 -0
- package/dist/stand/subscription-runtime.d.ts +102 -0
- package/dist/stand/subscription-runtime.d.ts.map +1 -0
- package/dist/stand/subscription-runtime.js +380 -0
- package/dist/stand/subscription-runtime.js.map +1 -0
- package/dist/testing/index.d.ts +36 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +54 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +76 -0
package/REFERENCE.md
ADDED
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
# @spine-event-engine/server reference
|
|
2
|
+
|
|
3
|
+
This reference describes the public server contracts for coding agents.
|
|
4
|
+
|
|
5
|
+
## Testing entry point
|
|
6
|
+
|
|
7
|
+
`@spine-event-engine/server/testing` is for framework-package tests and controlled
|
|
8
|
+
in-process fixtures. It exposes test reset and integration-frame helpers; managed-host
|
|
9
|
+
internals remain repository-only fixture code. This entrypoint is deliberately outside
|
|
10
|
+
the end-user root API and may change with test infrastructure rather than normal server
|
|
11
|
+
compatibility guarantees.
|
|
12
|
+
|
|
13
|
+
## Delivery SPI
|
|
14
|
+
|
|
15
|
+
Delivery adapters import `conditionalPickUp` from
|
|
16
|
+
`@spine-event-engine/server/spi/delivery`. It registers or looks up an optional
|
|
17
|
+
conditional-pickup capability for a `DeliveryWorkRegistry`; it is not part of
|
|
18
|
+
the server root API and does not independently create delivery work.
|
|
19
|
+
|
|
20
|
+
## Generated handler-registry SPI
|
|
21
|
+
|
|
22
|
+
Generated model modules import only their registry data types from
|
|
23
|
+
`@spine-event-engine/server/spi/handler-registry`. The stable contract is the
|
|
24
|
+
unversioned `GeneratedHandlerRegistry` receiver collection, receiver records,
|
|
25
|
+
handler records, kinds, arity, and optional `where` data. The subpath has no
|
|
26
|
+
runtime values. Server-side ingestion, validation errors, and their error codes
|
|
27
|
+
are implementation details; generated code must not depend on them. Retired
|
|
28
|
+
versioned registries are rejected and must be regenerated.
|
|
29
|
+
|
|
30
|
+
## Standalone handlers
|
|
31
|
+
|
|
32
|
+
Generated standalone handlers extend `AbstractAssignee`, `AbstractCommander`,
|
|
33
|
+
`AbstractEventReactor`, or `AbstractEventSubscriber`. Register instances with
|
|
34
|
+
`addAssignee()`, `addCommandDispatcher()`, or `addEventDispatcher()` as their
|
|
35
|
+
role requires, then use `buildAsync()` to load generated receiver metadata and
|
|
36
|
+
match the exact constructor. Assignees consume Commands and produce Events;
|
|
37
|
+
commanders consume Commands, Events, or rejections and produce Commands;
|
|
38
|
+
reactors consume Events or rejections and may produce Events; subscribers
|
|
39
|
+
consume Events, rejections, or Entity states and produce no signal. State
|
|
40
|
+
subscribers use the System Event Bus. Produced signals are published in-process
|
|
41
|
+
on a best-effort basis; this is not an exactly-once contract.
|
|
42
|
+
|
|
43
|
+
## Integration broker and event origin
|
|
44
|
+
|
|
45
|
+
Each `BoundedContext` creates and closes exactly one private integration broker.
|
|
46
|
+
The broker has distinct status, configuration, and event exchanges. Event
|
|
47
|
+
export is requested-only: a context installs a domestic publisher only for
|
|
48
|
+
event types requested by another context, and withdrawal removes only that
|
|
49
|
+
requester's interest. The broker does not enforce ownership; the supported
|
|
50
|
+
shape is many consumers and one domain producer per event type at a time.
|
|
51
|
+
|
|
52
|
+
The status exchange announces `BoundedContextOnline`; observing a foreign
|
|
53
|
+
announcement resends the local complete wanted-event document. The
|
|
54
|
+
configuration exchange treats each `ExternalEventsWanted` as a complete
|
|
55
|
+
replacement, installs producer publication on the first requester, avoids
|
|
56
|
+
duplicate unchanged broadcasts, and removes publication after the last
|
|
57
|
+
withdrawal. The event exchange uses one logical channel per event message type
|
|
58
|
+
and retains the original Event ID and complete Event. Self-origin and paired
|
|
59
|
+
domain/System Context messages are ignored.
|
|
60
|
+
|
|
61
|
+
`External<T>` is exported as a transparent type alias (`type External<T> = T`)
|
|
62
|
+
and has no runtime value or brand. The build-time handler analyzer recognizes
|
|
63
|
+
the canonical server declaration only when it is the direct first receptor
|
|
64
|
+
parameter. Missing markers mean `domestic`; a valid marker means `external`.
|
|
65
|
+
External commands are rejected, while external event/rejection handlers and
|
|
66
|
+
the supported state-subscription path are filtered by `EventContext.external`.
|
|
67
|
+
Repository selection applies the filter per handler, including mixed-origin
|
|
68
|
+
repositories. This is also the loop rule: domestic publication and origin
|
|
69
|
+
filtering prevent self-origin and paired-context traffic; no hop count, origin
|
|
70
|
+
ledger, Inbox, retry, replay, deduplication, cursor, fencing, or election is
|
|
71
|
+
added.
|
|
72
|
+
|
|
73
|
+
Incoming broker frames must contain a valid `ExternalMessage` wrapper and a
|
|
74
|
+
complete `Event`. The server unpacks and validates the event, obtains the
|
|
75
|
+
tenant from its explicit event origin, validates that tenant at the existing
|
|
76
|
+
boundary, copies only `EventContext.external = true`, and posts the ordinary
|
|
77
|
+
domain `EventBus`, whose admitted schemas are the destination's authority for
|
|
78
|
+
incoming payload decoding. Unknown or malformed received payloads are logged safely,
|
|
79
|
+
dropped, and do not stop the broker loop. Zero-byte control messages are valid
|
|
80
|
+
where the Proto contract permits an empty payload.
|
|
81
|
+
|
|
82
|
+
Malformed, unknown-schema, or undecodable received events emit the fixed ERROR
|
|
83
|
+
message `Dropped corrupt external event.` with safe `contextName`, `eventType`,
|
|
84
|
+
`operation: external-event-intake`, and
|
|
85
|
+
`reasonCode: corrupt_external_event`; the callback resolves, the event is
|
|
86
|
+
dropped, and later valid events continue. Application intake failures after a
|
|
87
|
+
valid decode remain observable.
|
|
88
|
+
|
|
89
|
+
Context readiness includes broker open. Close gates new EventBus intake before
|
|
90
|
+
awaiting transport work, broadcasts an empty wanted document, detaches domestic
|
|
91
|
+
publishers and external subscribers, drains accepted work, and closes acquired
|
|
92
|
+
channels. Failed cleanup remains retryable and concurrent close callers share
|
|
93
|
+
one attempt.
|
|
94
|
+
|
|
95
|
+
`ThirdPartyContext` is the sole public third-party import API. Its
|
|
96
|
+
`singleTenant()` form forbids an actor tenant; `multitenant()` requires one.
|
|
97
|
+
`emittedEvent(event, actor)` accepts a registered generated Protobuf message used as an event
|
|
98
|
+
payload and `ActorContext` (or a
|
|
99
|
+
`UserId` in the single-tenant form), preserves actor identity and timestamp,
|
|
100
|
+
and publishes through its hidden context. An unknown local schema throws; a
|
|
101
|
+
valid event with no interested external receptor is simply not delivered.
|
|
102
|
+
|
|
103
|
+
## Server environment facilities
|
|
104
|
+
|
|
105
|
+
`ServerEnvironment.instance()` is a process-wide singleton. Production
|
|
106
|
+
resolution requires storage and the complete application schema registry:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { EnvironmentType, ServerEnvironment } from "@spine-event-engine/server";
|
|
110
|
+
import type { TypeRegistryLookup } from "@spine-event-engine/core";
|
|
111
|
+
import type { StorageFactory } from "@spine-event-engine/storage";
|
|
112
|
+
|
|
113
|
+
declare const storageFactory: StorageFactory;
|
|
114
|
+
declare const typeRegistry: TypeRegistryLookup;
|
|
115
|
+
|
|
116
|
+
ServerEnvironment.when(EnvironmentType.Production).use({
|
|
117
|
+
storageFactory,
|
|
118
|
+
typeRegistry,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`storageFactory` supplies records and events. `typeRegistry` is the complete
|
|
123
|
+
application schema universe used by `ThirdPartyContext` to encode outgoing
|
|
124
|
+
imported events. An optional `integrationChannelFactory` overrides the
|
|
125
|
+
process-wide private
|
|
126
|
+
integration message channel factory. When it is absent, all local bounded
|
|
127
|
+
contexts share one in-memory factory, including in production. Local and test
|
|
128
|
+
environments default storage in memory and use the core registry only as a
|
|
129
|
+
fallback; those defaults are not production configuration.
|
|
130
|
+
|
|
131
|
+
To override the default channel factory, add
|
|
132
|
+
`integrationChannelFactory: customFactory` to the same settings object.
|
|
133
|
+
|
|
134
|
+
## Routing lifecycle
|
|
135
|
+
|
|
136
|
+
Event, command, and state-update routing accepts exact schemas and nominal
|
|
137
|
+
message-interface tokens. It selects exact, first registered matching token,
|
|
138
|
+
then replacement/default. The route runs once at accepted admission; its
|
|
139
|
+
validated typed targets are stored and reused for retries. The legacy-named
|
|
140
|
+
local `catchUpReadSide()` helper is unrelated to retry: it resets and replays
|
|
141
|
+
the whole local read side, and is not Projection catch-up.
|
|
142
|
+
|
|
143
|
+
## Message-valued Entity IDs
|
|
144
|
+
|
|
145
|
+
The first state field determines the Entity ID type. It can be a primitive or
|
|
146
|
+
a generated Protobuf message; for a message ID, all declared fields form the
|
|
147
|
+
identifier. Nested and composite IDs are supported.
|
|
148
|
+
|
|
149
|
+
```proto
|
|
150
|
+
message ShipmentId {
|
|
151
|
+
spine.core.UserId owner = 1;
|
|
152
|
+
int32 number = 2;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
message Shipment {
|
|
156
|
+
ShipmentId id = 1;
|
|
157
|
+
string status = 2;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The root `MessageId` type accepts generated messages such as `CommandId`:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { create } from "@bufbuild/protobuf";
|
|
165
|
+
import { type MessageId } from "@spine-event-engine/server";
|
|
166
|
+
import { CommandIdSchema } from "@spine-event-engine/proto";
|
|
167
|
+
|
|
168
|
+
const id: MessageId = create(CommandIdSchema, { uuid: "shipment-7" });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Admission validates a message ID against the schema declared by the state
|
|
172
|
+
field, including generated validation rules, before it is stored or replayed.
|
|
173
|
+
When default routing finds a message but the Entity ID type is primitive, a
|
|
174
|
+
custom route returns the intended primitive ID.
|
|
175
|
+
|
|
176
|
+
Without a custom route, Command routing uses the Command's declared first
|
|
177
|
+
field. Event routing uses a compatible producer ID when available and otherwise
|
|
178
|
+
uses the Event's declared first field. State-update routing selects the first
|
|
179
|
+
state field compatible with the Entity ID.
|
|
180
|
+
|
|
181
|
+
## Handler routing and operations
|
|
182
|
+
|
|
183
|
+
Generated handler metadata routes a command, event, or state update by its
|
|
184
|
+
exact generated message type. A declared default route is considered only when
|
|
185
|
+
there is no exact route. This is TypeScript routing; it does not consume
|
|
186
|
+
`(is).java_type` or `(every_is).java_type`, and it has no decorator-based
|
|
187
|
+
route registration API.
|
|
188
|
+
|
|
189
|
+
Handlers may return their established result directly or through exactly one built-in `Promise<T>`.
|
|
190
|
+
Nested promises and structural or imported thenable lookalikes are rejected during handler analysis.
|
|
191
|
+
The entity transaction remains open until that promise settles; rejection rolls
|
|
192
|
+
back framework state and suppresses produced output, but cannot undo external
|
|
193
|
+
side effects. Process Managers may use their protected read-only query surface
|
|
194
|
+
for eventually consistent projection state during a handler. Aggregates must
|
|
195
|
+
not use projection reads for invariants.
|
|
196
|
+
|
|
197
|
+
`select(schema, columns)` supports `byId`, typed `where`, `mask`, `orderBy`,
|
|
198
|
+
`limit`, `read`, `findById`, and `all`. A Process Manager query returns at most
|
|
199
|
+
1,000 states. `all()` is a convenience and can be costly on a large Projection;
|
|
200
|
+
prefer an ID-targeted or ordered bounded query.
|
|
201
|
+
|
|
202
|
+
Use `EntityQuery.all(...)` to require every predicate and `EntityQuery.either(...)`
|
|
203
|
+
to accept any branch. Query execution inherits the active handler's actor and
|
|
204
|
+
tenant; the protected read-only facade has no tenant override.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import type { Message } from "@bufbuild/protobuf";
|
|
208
|
+
import type { GenMessage } from "@bufbuild/protobuf/codegenv2";
|
|
209
|
+
import { EntityQuery, type EntityColumn } from "@spine-event-engine/core";
|
|
210
|
+
import { ProcessManager } from "@spine-event-engine/server";
|
|
211
|
+
|
|
212
|
+
type RequestView = Message<"RequestView"> & {
|
|
213
|
+
id: string;
|
|
214
|
+
status: string;
|
|
215
|
+
createdAt: number;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
declare const RequestViewSchema: GenMessage<RequestView>;
|
|
219
|
+
declare const RequestViewColumns: {
|
|
220
|
+
readonly status: EntityColumn<typeof RequestViewSchema, "status", string, "equal">;
|
|
221
|
+
readonly createdAt: EntityColumn<
|
|
222
|
+
typeof RequestViewSchema,
|
|
223
|
+
"createdAt",
|
|
224
|
+
number,
|
|
225
|
+
"equal" | "greaterThan"
|
|
226
|
+
>;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
abstract class RequestCoordinator extends ProcessManager<string, typeof RequestViewSchema> {
|
|
230
|
+
protected async pendingRequests(requestId: string): Promise<void> {
|
|
231
|
+
const matching = await this.select(RequestViewSchema, RequestViewColumns)
|
|
232
|
+
.where(EntityQuery.eq(RequestViewColumns.status, "pending"))
|
|
233
|
+
.orderBy(RequestViewColumns.createdAt, "asc")
|
|
234
|
+
.limit(10)
|
|
235
|
+
.read();
|
|
236
|
+
const one = await this.select(RequestViewSchema, RequestViewColumns).findById(requestId);
|
|
237
|
+
const everyVisible = await this.select(RequestViewSchema, RequestViewColumns).all();
|
|
238
|
+
void [matching, one, everyVisible];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`all()` remains capped at 1,000 states but may still be costly; use `findById()`
|
|
244
|
+
or an ordered bounded `read()` where possible.
|
|
245
|
+
|
|
246
|
+
A command-input `@Command` method is a command substitution receptor: it is
|
|
247
|
+
the one effective receptor for that Command type (instead of an `@Assign`),
|
|
248
|
+
commits its Entity state before its one-or-more returned Commands are detached
|
|
249
|
+
for in-process produced-command enqueue, and receives an optional `CommandContext`.
|
|
250
|
+
Only Process Manager repositories support `@Command` handlers. Aggregate and
|
|
251
|
+
Projection repositories reject command-input substitutions and event- or
|
|
252
|
+
rejection-input command reactions during generated metadata ingestion and
|
|
253
|
+
repository construction.
|
|
254
|
+
Event- and rejection-input `@Command` methods remain Event- or
|
|
255
|
+
rejection-to-command reactions on Event Bus.
|
|
256
|
+
Produced commands retain the source actor, tenant, origin, and causal lineage.
|
|
257
|
+
The enqueue is post-commit best effort, not an atomic outbox or exactly-once
|
|
258
|
+
delivery: a process crash between commit and enqueue can lose a child. Accepted
|
|
259
|
+
produced commands drain during context close. A contained child failure is diagnosed,
|
|
260
|
+
but does not retroactively fail an already accepted source command or durably
|
|
261
|
+
retry that child.
|
|
262
|
+
|
|
263
|
+
For a generated Process Manager, a command-input handler uses distinct domain
|
|
264
|
+
Command input and output types, and the generated registry supplies those
|
|
265
|
+
schemas. This self-contained example explicitly ingests the unversioned data that an
|
|
266
|
+
application build normally emits, then assembles a repository from it; it does
|
|
267
|
+
not claim that its fixture discovers a registry artifact automatically:
|
|
268
|
+
|
|
269
|
+
<!-- docs-snippet-path: packages/server-blackbox-tests/test/project-event-routing.test.ts -->
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { create } from "@bufbuild/protobuf";
|
|
273
|
+
import { SignalEnvelopes } from "@spine-event-engine/core";
|
|
274
|
+
import {
|
|
275
|
+
CommandContextSchema,
|
|
276
|
+
CommandIdSchema,
|
|
277
|
+
type CommandContext,
|
|
278
|
+
} from "@spine-event-engine/proto";
|
|
279
|
+
import {
|
|
280
|
+
BoundedContext,
|
|
281
|
+
Command,
|
|
282
|
+
type EntityHandlersMetadata,
|
|
283
|
+
HandlerRegistryIngestor,
|
|
284
|
+
ProcessManager,
|
|
285
|
+
Repository,
|
|
286
|
+
} from "@spine-event-engine/server";
|
|
287
|
+
import type { GeneratedHandlerRegistry } from "@spine-event-engine/server/spi/handler-registry";
|
|
288
|
+
|
|
289
|
+
import {
|
|
290
|
+
ApproveProjectSchema,
|
|
291
|
+
ScheduleProjectSchema,
|
|
292
|
+
type ApproveProject,
|
|
293
|
+
type ScheduleProject,
|
|
294
|
+
} from "../generated/spine/server/testing/project_commands_pb.js";
|
|
295
|
+
import {
|
|
296
|
+
CoordinationStateSchema,
|
|
297
|
+
OrganizationIdSchema,
|
|
298
|
+
ProjectIdSchema,
|
|
299
|
+
type ProjectId,
|
|
300
|
+
} from "../generated/spine/server/testing/project_workflow_pb.js";
|
|
301
|
+
|
|
302
|
+
class ApprovalCoordinator extends ProcessManager<
|
|
303
|
+
ProjectId,
|
|
304
|
+
typeof CoordinationStateSchema,
|
|
305
|
+
number
|
|
306
|
+
> {
|
|
307
|
+
@Command
|
|
308
|
+
approve(command: ApproveProject, context: CommandContext): ScheduleProject {
|
|
309
|
+
this.update((draft) => Object.assign(draft, { id: this.id, projectName: command.status }));
|
|
310
|
+
return create(ScheduleProjectSchema, { project: command.project, status: command.status });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const project = create(ProjectIdSchema, {
|
|
315
|
+
organization: create(OrganizationIdSchema, { code: "org-a" }),
|
|
316
|
+
number: 1,
|
|
317
|
+
});
|
|
318
|
+
const registry: GeneratedHandlerRegistry = {
|
|
319
|
+
receivers: [
|
|
320
|
+
{
|
|
321
|
+
receiverKind: "entity",
|
|
322
|
+
receiverType: ApprovalCoordinator,
|
|
323
|
+
stateSchema: CoordinationStateSchema,
|
|
324
|
+
handlers: [
|
|
325
|
+
{
|
|
326
|
+
kind: "command-substitution",
|
|
327
|
+
methodName: "approve",
|
|
328
|
+
input: { schema: ApproveProjectSchema, origin: "domestic" },
|
|
329
|
+
outcomes: { returned: [ScheduleProjectSchema], thrown: [] },
|
|
330
|
+
parameterCount: 2,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
const [handlers] = new HandlerRegistryIngestor().ingest(registry);
|
|
337
|
+
if (handlers === undefined) throw new Error("Generated Process Manager metadata is missing.");
|
|
338
|
+
const repository = new Repository({
|
|
339
|
+
entityType: ApprovalCoordinator,
|
|
340
|
+
schema: CoordinationStateSchema,
|
|
341
|
+
handlers: handlers as EntityHandlersMetadata<ApprovalCoordinator, typeof CoordinationStateSchema>,
|
|
342
|
+
});
|
|
343
|
+
const context = BoundedContext.singleTenant("Projects").add(repository).build();
|
|
344
|
+
await context.commandBus().post(
|
|
345
|
+
SignalEnvelopes.command({
|
|
346
|
+
context: create(CommandContextSchema),
|
|
347
|
+
schema: ApproveProjectSchema,
|
|
348
|
+
message: create(ApproveProjectSchema, { project, status: "approved" }),
|
|
349
|
+
}),
|
|
350
|
+
);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The generated record declares `ApproveProjectSchema` as the input and
|
|
354
|
+
`ScheduleProjectSchema` as the emitted Command schema; registry ingestion, not
|
|
355
|
+
manual decorator materialization, constructs the output-bearing metadata.
|
|
356
|
+
Application builds emit the record to
|
|
357
|
+
`generated/handler/generated-handler-registry.js`; those applications use
|
|
358
|
+
`buildAsync()` with their compiled package root.
|
|
359
|
+
|
|
360
|
+
One `@Where({ eventField, equals })` equality filter may narrow an event- or
|
|
361
|
+
rejection-consuming `@Subscribe`, `@React`, or `@Command` handler after type
|
|
362
|
+
routing. `eventField` and `equals` are typed string literals; invalid or
|
|
363
|
+
repeated declarations fail closed. It is not an alternative routing mechanism.
|
|
364
|
+
|
|
365
|
+
Configure logging through `ServerEnvironment` when assembling the server. The
|
|
366
|
+
server installs that logger into its runtime components, where warnings and
|
|
367
|
+
failures carry operation context. Durable facts belong in events or storage.
|
|
368
|
+
Entity Inbox replay sends persisted accepted work through the normal handler
|
|
369
|
+
path, so handler effects must be replay-safe.
|
|
370
|
+
|
|
371
|
+
## Context assembly and entities
|
|
372
|
+
|
|
373
|
+
Create a context with `BoundedContext.singleTenant(name)` or
|
|
374
|
+
`BoundedContext.multitenant(name)`. Names are immutable. Use
|
|
375
|
+
`withStorageFactory(factory)` to supply record/event storage.
|
|
376
|
+
`withGeneratedRegistryRoot(root)` enables framework discovery of the generated
|
|
377
|
+
handler registry for classes registered with `add(EntityClass)`;
|
|
378
|
+
`buildAsync()` performs that discovery and assembly. `build()` remains for
|
|
379
|
+
explicit `Repository` registration. A built context contains `CommandBus`,
|
|
380
|
+
`EventBus`, `Stand`, repositories, and its storage lifecycle.
|
|
381
|
+
|
|
382
|
+
Generated writer output is an unversioned receiver collection and records
|
|
383
|
+
command substitutions explicitly. The ingestor rejects retired versioned
|
|
384
|
+
registry shapes with an instruction to regenerate.
|
|
385
|
+
|
|
386
|
+
### Stand subscription registry
|
|
387
|
+
|
|
388
|
+
Each built context also has one `StandSubscriptionRegistry`. By default the
|
|
389
|
+
builder creates a storage-backed registry from the resolved context
|
|
390
|
+
`StorageFactory` (including the `ServerEnvironment` factory used for a builder
|
|
391
|
+
added to a server). It stores one `spine.client.SubscriptionRecord` under each
|
|
392
|
+
explicit `SubscriptionId`. A complete custom implementation can instead be
|
|
393
|
+
supplied with `withSubscriptionRegistry(registry)`; responsibility transfers to
|
|
394
|
+
the builder on the first build attempt.
|
|
395
|
+
|
|
396
|
+
`SubscriptionService` streams are live best-effort notifications, not replay,
|
|
397
|
+
ordering, or completeness contracts. A standalone Server defaults to the
|
|
398
|
+
persistent registry. In a managed complete-replica cohort, the framework starts
|
|
399
|
+
and closes each child Server and accepts only every context's actual
|
|
400
|
+
`InMemorySubscriptionRegistry`; a persistent or custom registry is rejected
|
|
401
|
+
and the assembled child is closed before READY. Durable logical bindings remain
|
|
402
|
+
Gateway-only. The Coordinator fans each logical definition to ready children,
|
|
403
|
+
merges updates, propagates cancellation, and carries no subscription payload on
|
|
404
|
+
parent/child IPC. A late or replacement child synchronizes retained definitions
|
|
405
|
+
before it is eligible for normal requests. Active definitions attach their
|
|
406
|
+
child streams before admission; subscribed but inactive definitions do not
|
|
407
|
+
block readiness. Each child observes application-configured remote/shared
|
|
408
|
+
Delivery directly. During DRAINING, new unary and shard admission stops while
|
|
409
|
+
active Delivery and its subscription updates finish before child close. The
|
|
410
|
+
application configures the Delivery facility and selects the shard strategy;
|
|
411
|
+
managed mode does not infer or certify configuration provenance.
|
|
412
|
+
|
|
413
|
+
Create records begin `pending` and expire after 30 seconds unless activated.
|
|
414
|
+
Active records have no framework TTL. Cancellation physically deletes the
|
|
415
|
+
definition; no tombstone remains. A stored definition is at most 1 MiB
|
|
416
|
+
(1,048,576 bytes). Its physical provider columns are `status` and
|
|
417
|
+
`when_activation_expires`. `cleanup()` asks the provider for 26 pending rows
|
|
418
|
+
ordered by `when_activation_expires` then ID, validates that finite result,
|
|
419
|
+
deletes at most 25 expired definitions, and reports `more` only when its
|
|
420
|
+
observed 26th row is expired. Cleanup can run idempotently on every node.
|
|
421
|
+
|
|
422
|
+
Context close drains and closes Stand before its registry, then closes tenant,
|
|
423
|
+
repository, and storage resources; it attempts every close and reports
|
|
424
|
+
aggregate failures. An in-memory registry reports `persistent === false`.
|
|
425
|
+
Attaching such a context to a production `ServerEnvironment` emits one
|
|
426
|
+
context-name-only warning without failing startup; Local environments do not
|
|
427
|
+
warn.
|
|
428
|
+
|
|
429
|
+
Creation writes the pending record only when that ID is absent. Activation
|
|
430
|
+
replaces that pending record only when it remains unchanged. Cancellation and
|
|
431
|
+
expiry cleanup delete the observed record only when it remains unchanged. Each
|
|
432
|
+
node reconciles its local snapshot immediately after context assembly and then
|
|
433
|
+
every ten seconds. Physical deletion detaches the local observer after its next
|
|
434
|
+
completed cycle. This is best-effort per-node convergence, not a
|
|
435
|
+
cluster-completeness, replay, ordering, gap-repair, or exactly-once guarantee.
|
|
436
|
+
|
|
437
|
+
The registry accepts only a generated `SubscriptionId` for activate, get, and
|
|
438
|
+
delete. `create(subscription)` returns `{ kind: "created", entry }` or
|
|
439
|
+
`{ kind: "existing", entry }`; the latter requires byte-equivalent canonical
|
|
440
|
+
content and a different definition with the same ID throws `StandConflictError`.
|
|
441
|
+
`activate(id)` returns `activated`, `active`, `missing`, or `expired`.
|
|
442
|
+
`delete(id)` returns `deleted` or `missing`. Blank IDs or topics and malformed
|
|
443
|
+
durable records fail closed.
|
|
444
|
+
|
|
445
|
+
Entries and nested subscriptions returned by create, activate, get, and
|
|
446
|
+
snapshot are clone-safe views. Protobuf byte arrays remain mutable runtime
|
|
447
|
+
views but are cloned so they never alias caller storage. Snapshots are ordered
|
|
448
|
+
by identifier. A durable provider must implement atomic compare-and-set for
|
|
449
|
+
`SubscriptionRecord`; construction rejects a provider that cannot. MySQL table
|
|
450
|
+
configuration and Datastore custom record storage registered for
|
|
451
|
+
`SubscriptionRecord` are used by the registry. The registry uses one
|
|
452
|
+
record-storage handle, while the context closes the registry.
|
|
453
|
+
|
|
454
|
+
`Entity` is the state base class. `Aggregate`, `Projection`, and
|
|
455
|
+
`ProcessManager` identify the three entity families. Handler decorators are
|
|
456
|
+
`@Assign`, `@Command`, `@React`, `@Subscribe`, and `@Apply`. A command-accepting
|
|
457
|
+
handler uses `@Throws(GeneratedRejection)` to declare its possible domain
|
|
458
|
+
rejections. Write the primary handler decorator first and `@Throws` immediately
|
|
459
|
+
below it; declaration order does not affect behavior. Generated metadata records
|
|
460
|
+
normal and rejection outcomes before context assembly, so clients may subscribe
|
|
461
|
+
to a declared rejection even when no server handler consumes it. Throwing an
|
|
462
|
+
undeclared generated rejection is a technical handler failure. In a transactional
|
|
463
|
+
handler, `update(mutator)` changes the active draft and returns the draft;
|
|
464
|
+
`tryUpdate(mutator)` validates a scratch draft and returns violations without
|
|
465
|
+
applying an invalid change. Entity lifecycle and version changes are committed
|
|
466
|
+
only after the transaction accepts.
|
|
467
|
+
|
|
468
|
+
## Signals, validation, and rejection behavior
|
|
469
|
+
|
|
470
|
+
`CommandBus` validates every accepted command from Proto validation options
|
|
471
|
+
before dispatcher or repository handler code runs. It raises the framework's
|
|
472
|
+
command-validation failure, which `SpineServices` maps to
|
|
473
|
+
`COMMAND_VALIDATION_ERROR` with a packed `spine.validation.ValidationError`.
|
|
474
|
+
Entity transition validation failures map to
|
|
475
|
+
`COMMAND_STATE_TRANSITION_VALIDATION_FAILED` with the same detail type.
|
|
476
|
+
Framework-created runtime command and event metadata uses fresh Node secure
|
|
477
|
+
UUIDs; fixed IDs belong only to existing source envelopes retained through the
|
|
478
|
+
normal origin chain.
|
|
479
|
+
|
|
480
|
+
An application handler throws a generated core `RejectionThrowable` for a
|
|
481
|
+
domain rejection declared with `@Throws`. The repository rolls back state,
|
|
482
|
+
version, lifecycle, and output; it schedules the typed rejection event independently. Command service
|
|
483
|
+
acknowledgement remains an accepted `Ack`. Rejection-event posting can be
|
|
484
|
+
unobserved by inactive, full, or closed subscriptions and a posting failure is
|
|
485
|
+
an internal diagnostic, not a retry guarantee.
|
|
486
|
+
|
|
487
|
+
Every domain context has an internal paired System Context. Domain events use
|
|
488
|
+
the domain `EventBus` and domain `EventStore`. System events use only the
|
|
489
|
+
System Context `EventBus`, so they never enter the domain EventStore. System
|
|
490
|
+
event persistence is optional: `persistSystemEvents()` enables the paired
|
|
491
|
+
System Context's separate storage; otherwise the bus validates, dispatches,
|
|
492
|
+
and notifies without appending. Schemas may come from an external dispatcher or
|
|
493
|
+
a registered internal repository producer. A producer-only schema is not
|
|
494
|
+
thereby an external dispatch route.
|
|
495
|
+
|
|
496
|
+
## Services, reads, and lifecycle
|
|
497
|
+
|
|
498
|
+
`SpineServices({ contexts }).register(router)` registers the JVM-compatible
|
|
499
|
+
CommandService, QueryService, and SubscriptionService contracts. `Server`
|
|
500
|
+
opens the local Node HTTP/2 listener; default host is `127.0.0.1`. Set
|
|
501
|
+
`readMaxBytes` and `writeMaxBytes` only to integers 1–4294967295; defaults are 4194304. `RunningServer.close()` stops intake, closes sessions, waits for active
|
|
502
|
+
work to stop using dependencies, then closes contexts. Caller-managed `start()`
|
|
503
|
+
does not close global process facilities; close `ServerEnvironment.instance()`
|
|
504
|
+
separately when used. The final run-managed `run()` close closes its environment.
|
|
505
|
+
|
|
506
|
+
`Stand` registers entity state for point/list reads, updates current state, and
|
|
507
|
+
offers in-process subscriptions. The legacy-named local reset/replay helper
|
|
508
|
+
`catchUpReadSide(options?)` clears every registered Projection state and replays
|
|
509
|
+
the whole stored event history through matching Projection subscribers; it does
|
|
510
|
+
not append events. Single-tenant contexts reject a tenant option; multitenant
|
|
511
|
+
contexts require one. It cannot target a Projection repository, entity IDs, or
|
|
512
|
+
a starting time, and has no durable operation identity, progress, Inbox
|
|
513
|
+
catch-up delivery, restart, resumption, or historical/live coordination. It is
|
|
514
|
+
not JVM-equivalent Projection catch-up.
|
|
515
|
+
|
|
516
|
+
Active subscription streams and queues are process-local. `Stand` serves
|
|
517
|
+
authoritative queries from current state and Entity subscriptions observe
|
|
518
|
+
committed `EntityStateChanged` messages on the paired System Context EventBus,
|
|
519
|
+
so a direct Stand write alone does not produce a subscription update. Consumer
|
|
520
|
+
callback failures are isolated from post-commit observation and other
|
|
521
|
+
consumers. The package does not guarantee cluster-complete observations,
|
|
522
|
+
subscription replay, event-gap repair, or exactly-once effects.
|
|
523
|
+
|
|
524
|
+
### Native and managed hosting
|
|
525
|
+
|
|
526
|
+
`Server.start()` installs no process signal handlers, never closes its
|
|
527
|
+
environment, and shares only a caller-managed active generation. `Server.run()`
|
|
528
|
+
coalesces concurrent calls on one builder, shares a run-managed generation with
|
|
529
|
+
other `run()` servers, and rejects mixed admission before listener open. The
|
|
530
|
+
coordinator uses one `SIGINT`/`SIGTERM` listener pair, closes servers in reverse
|
|
531
|
+
successful-start order, retains failed final environment closure for later
|
|
532
|
+
signal or explicit-close retry, and sets `process.exitCode` to `1` after a
|
|
533
|
+
signal-driven failure.
|
|
534
|
+
|
|
535
|
+
`ManagedServerApplication.run()` is the Node-only deployment entrypoint for a
|
|
536
|
+
complete-replica process cohort when the framework handles process shutdown.
|
|
537
|
+
`ManagedServerApplication.start()` starts the same cohort for an embedding host
|
|
538
|
+
and installs no process signal handlers; that host closes the returned handle.
|
|
539
|
+
Both require a positive safe-integer
|
|
540
|
+
`processCount` and an ESM `moduleUrl`; it never derives a process count from
|
|
541
|
+
the machine. Its optional `host` and required nonzero `port` bind the public
|
|
542
|
+
Coordinator listener. Every child executes that same module and calls the
|
|
543
|
+
module's `createServer({ host: "127.0.0.1", port: 0 })`. The child reports its
|
|
544
|
+
actual loopback endpoint only after its local readiness gates settle. The parent
|
|
545
|
+
initially waits until every configured child is ready. Afterwards it is ready
|
|
546
|
+
while at least one child is ready; it remains alive and keeps replacing while
|
|
547
|
+
degraded, and reports unready if no child is ready. It replaces an unexpected
|
|
548
|
+
child exit after a bounded exponential delay (250 ms initially, capped at 30 s;
|
|
549
|
+
reset after 60 s READY). It does not restart a child being closed. Restart
|
|
550
|
+
values are optional `restart` settings and must be positive safe integers;
|
|
551
|
+
concurrent starts default to `min(4, processCount)`. `run()` parent `SIGINT`
|
|
552
|
+
and `SIGTERM` use the same shared child-close path; `start()` leaves those
|
|
553
|
+
signals to its caller. This private lifecycle channel
|
|
554
|
+
does not transport application signals. The Coordinator forwards generated
|
|
555
|
+
Command and Query unary calls once to a READY child; process count does not
|
|
556
|
+
inspect CPU availability and does not constrain the application's independently
|
|
557
|
+
chosen `DeliveryStrategy` or shard count. Direct `Server.run()` and browser
|
|
558
|
+
hosting remain independent of managed Node deployment.
|
|
559
|
+
|
|
560
|
+
If no child is READY, Command and Query return gRPC `UNAVAILABLE`. The
|
|
561
|
+
Coordinator does not enter application intake and does not retry the selected
|
|
562
|
+
call on a sibling child. Subscription fan-out follows current READY members;
|
|
563
|
+
later and replacement members attach the retained subscription before managed
|
|
564
|
+
admission.
|
|
565
|
+
|
|
566
|
+
`ServerOptions` is native-only: it configures contexts, resources, services,
|
|
567
|
+
and the native listener. It has no `browser` option and the root package does
|
|
568
|
+
not load `@spine-event-engine/auth`. Browser hosting belongs to the explicit
|
|
569
|
+
`@spine-event-engine/server/browser` entry point; see the [browser/auth guide](../../docs/BROWSER_CLIENT_AUTH_EXTENSION_GUIDE.md).
|
|
570
|
+
|
|
571
|
+
Browser unary gateway admission has a fixed 1 MiB (1,048,576-byte) request
|
|
572
|
+
limit. A larger unary gateway request is rejected with `ResourceExhausted`.
|
|
573
|
+
This is an additional boundary: `readMaxBytes` still limits the Connect or
|
|
574
|
+
gRPC-Web transport message accepted by the public listener, so the effective
|
|
575
|
+
inbound limit is the stricter applicable limit. `writeMaxBytes` limits public
|
|
576
|
+
transport responses independently.
|
|
577
|
+
|
|
578
|
+
For `BrowserServer` from `@spine-event-engine/server/browser`, origins must be unique canonical HTTP(S) origins with no path, query,
|
|
579
|
+
fragment, or trailing slash. Requests without an allowed exact `Origin` receive
|
|
580
|
+
403 before RPC handling. Allowed responses include credentialed exact-origin
|
|
581
|
+
CORS, `Vary: Origin`, protocol request headers, and exposed gRPC status headers.
|
|
582
|
+
|
|
583
|
+
Supplying `browser.backend.baseUrl` selects standalone mode. It is one canonical
|
|
584
|
+
HTTP(S) origin for a Spine TS or JVM backend; the gateway never closes
|
|
585
|
+
that backend. Every standalone mode, including local development and tests,
|
|
586
|
+
requires explicit subscription bindings; production additionally requires an
|
|
587
|
+
application type registry and named durable bindings. `ResolveContext` stays in the gateway;
|
|
588
|
+
Post, Read, Subscribe, Activate, and Cancel use the same authenticated policy,
|
|
589
|
+
context-replacement, and native descriptors before reaching the backend.
|
|
590
|
+
|
|
591
|
+
`browser.backend.baseUrls` is exclusive with `baseUrl` and configures a
|
|
592
|
+
non-empty ordered unique static node set. Unary calls use bounded round-robin
|
|
593
|
+
without retry; native streams fan out best-effort. Durable bindings retain only
|
|
594
|
+
logical definitions, not topology. Clients
|
|
595
|
+
re-query authoritative state after duplicate updates or generic loss notices.
|
|
596
|
+
|
|
597
|
+
`browser.authRoutes` is deliberately a bounded callback seam, not a router.
|
|
598
|
+
Each registration has one exact `GET` or `POST` canonical path, one method per
|
|
599
|
+
path, exact canonical origins, finite request bytes, and a finite timeout. A
|
|
600
|
+
missing Origin is rejected unless that route explicitly permits it for an OAuth
|
|
601
|
+
callback. Unknown paths, method/origin failures, body overflow, timeout, and
|
|
602
|
+
handler failure return fixed 404, 405, 403, 413, 504, and 500 responses without
|
|
603
|
+
calling application code on rejected input. The 404 unknown-path result applies
|
|
604
|
+
only after framework Origin admission. Auth admission is bounded to 64 active
|
|
605
|
+
requests by default or a configured positive `maxActiveAuthRequests`; excess
|
|
606
|
+
requests receive 503 before handler invocation and capacity returns when work
|
|
607
|
+
settles. The route deadline covers body intake, handler work, and response
|
|
608
|
+
transfer. Response bodies use `writeMaxBytes`; overflow returns 413 before any
|
|
609
|
+
application status or headers are committed. Listener close ultimately aborts
|
|
610
|
+
an active request through its disconnect signal.
|
|
611
|
+
|
|
612
|
+
With `cookies`, `OpaqueSessionCookies` performs strict bearer-first or
|
|
613
|
+
CSRF-protected cookie extraction. A strict rejection becomes an unusable
|
|
614
|
+
credential and never falls back to permissive header parsing. Without cookies,
|
|
615
|
+
the host accepts exactly one non-whitespace bearer token. Credentials and raw
|
|
616
|
+
transport metadata do not reach the private backend. The application-supplied
|
|
617
|
+
context resolver replaces caller actor and tenant facts before forwarding.
|
|
618
|
+
|
|
619
|
+
Browser close stops new listener intake, closes subscription admission and
|
|
620
|
+
bindings so active streams settle, awaits listener closure, then closes the
|
|
621
|
+
native backend. Concurrent calls share an attempt; a failed unfinished phase
|
|
622
|
+
can be retried without repeating completed native cleanup.
|
|
623
|
+
|
|
624
|
+
Browser subscription bindings are separate from service subscription records.
|
|
625
|
+
`BrowserAdmission` selects their ownership. Authenticated mode supplies
|
|
626
|
+
`sessions` and may supply `BrowserServerOptions.bindings`; production then
|
|
627
|
+
requires `DurableSubscriptionBindings` from `@spine-event-engine/server/browser` and rejects a
|
|
628
|
+
missing or in-memory binding store before listener open. Public mode supplies
|
|
629
|
+
`publicAccess: true`, cannot supply bindings, and uses process-local bindings
|
|
630
|
+
managed by the framework. The durable registry receives an explicit application
|
|
631
|
+
namespace, storage factory, identifier source, and cleanup callback. It closes
|
|
632
|
+
only its independently opened record-storage handle, not the application
|
|
633
|
+
storage factory or a Spine JVM/TS backend. It stores canonical public
|
|
634
|
+
Subscription definitions, never backend envelopes or membership topology, and
|
|
635
|
+
never returns private native data through public subscription responses.
|
|
636
|
+
|
|
637
|
+
The durable registry preserves one approved `GatewayAuthenticatedSubscription`
|
|
638
|
+
per public subscription through a process restart. It validates the record ID,
|
|
639
|
+
full Subscription, and expiry before use. The trusted Actor and Tenant remain
|
|
640
|
+
inside the stored Topic and are checked for Activate and Cancel. Create assigns
|
|
641
|
+
the public ID directly; no quota, reservation, fingerprint, or lease is
|
|
642
|
+
persisted. Cleanup remains bounded and restart-safe at the record level. It
|
|
643
|
+
cleans the backend definition before deleting its record. It is not a cleaner
|
|
644
|
+
lease or fence: this direct store supports one Gateway process. Active streams do not resume,
|
|
645
|
+
updates are not replayed, and the registry provides neither exactly-once
|
|
646
|
+
delivery, global update ordering, nor cluster-complete notification delivery.
|
|
647
|
+
|
|
648
|
+
## Delivery and environment
|
|
649
|
+
|
|
650
|
+
`Inbox`, `InboxStorage`, `ShardIndex`, and `ShardedWorkRegistry` provide the
|
|
651
|
+
local durable-row abstraction. Read limits are 1–1000; work leases are
|
|
652
|
+
1000–2147483647 ms. Framework replay supports command handling, projection
|
|
653
|
+
updates, and process-manager reactions. Callbacks are at-least-once/replay-safe:
|
|
654
|
+
lost renewal can prevent stale finalization but cannot undo a callback already
|
|
655
|
+
run. The package exposes no general raw worker callback API.
|
|
656
|
+
|
|
657
|
+
Shard ownership is the only delivery exclusion mechanism and excludes
|
|
658
|
+
concurrent delivery within that shard. Pending and delivered `InboxMessage`
|
|
659
|
+
rows are direct records; a delivered row is the deduplication fact. A handler
|
|
660
|
+
effect and the exact pending-to-delivered compare-and-set are not one
|
|
661
|
+
transaction, so a lost acknowledgement can redeliver after restart. Downstream
|
|
662
|
+
signal handling must be idempotent. A customized `DeliveryWorkRegistry`
|
|
663
|
+
implements `validateOwnership()` so delivery can validate before dispatch, at
|
|
664
|
+
repository commit time, and before acknowledgement. Validation is a fence
|
|
665
|
+
against an observed takeover, not a distributed transaction with handler
|
|
666
|
+
storage.
|
|
667
|
+
|
|
668
|
+
`keepUntil` is the optional deduplication-protection deadline, not a second
|
|
669
|
+
retention setting. A delivered row is cleanup-eligible when that deadline is
|
|
670
|
+
absent or elapsed. Under current shard ownership, environment delivery performs
|
|
671
|
+
one bounded cleanup page, plus at most one continuation after a full protected
|
|
672
|
+
page makes no removal. Direct local storage atomically verifies ownership and
|
|
673
|
+
the exact delivered snapshot during removal. A remote adapter may instead read
|
|
674
|
+
and compare the snapshot before sending a separate best-effort removal request;
|
|
675
|
+
that sequence is not an atomic compare-and-delete guarantee. Pending, retryable,
|
|
676
|
+
non-delivered, and still-protected rows remain. There is no additional retention
|
|
677
|
+
configuration, timer, or scheduler.
|
|
678
|
+
|
|
679
|
+
`BoundedContextBuilder.withDeliveryStrategy(strategy)` snapshots a validated
|
|
680
|
+
immutable strategy for its Entity Inbox; the default is one shard. For example,
|
|
681
|
+
use the public builder chain:
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
import { BoundedContext, UniformAcrossAllShards } from "@spine-event-engine/server";
|
|
685
|
+
|
|
686
|
+
const context = BoundedContext.singleTenant("Tasks")
|
|
687
|
+
.withDeliveryStrategy(UniformAcrossAllShards.forNumber(3))
|
|
688
|
+
.build();
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Aggregate commands and Process Manager commands/events derive their target shard
|
|
692
|
+
internally and persist their envelope in the target Entity Inbox before any
|
|
693
|
+
handler runs. Delivery is never a post-request callback: local and remote
|
|
694
|
+
handler runs. Local intake can directly drain the persisted Inbox in the
|
|
695
|
+
current request path. Attached `ServerEnvironment` ports acknowledge admission
|
|
696
|
+
and delivery workers replay it through the same path. Projection rows remain
|
|
697
|
+
separate.
|
|
698
|
+
|
|
699
|
+
`DeliveryBuilder` constructs a controlled `Delivery`; `DeliverySupervisor`
|
|
700
|
+
receives `{ source, delivery, onMessage }`, manages bounded shard notifications
|
|
701
|
+
from that structural source, and must start after endpoints are ready and close
|
|
702
|
+
before source client/storage. `DeliverySource` is not configured on
|
|
703
|
+
`DeliveryBuilder`. The supervisor does not promise
|
|
704
|
+
durable supervisor state, topology failover, exactly-once effects, or automatic
|
|
705
|
+
retry of unknown remote mutations.
|
|
706
|
+
|
|
707
|
+
Each Inbox read is bounded to one page. That bound does not limit the total
|
|
708
|
+
pending work: an active drain can advance to another page while it retains the
|
|
709
|
+
shard. Delivery compares pending rows with delivered rows in the current page
|
|
710
|
+
and a process-local cache of the 1,000 most recent deliveries, then removes
|
|
711
|
+
duplicates. The 30-second Inbox deduplication window controls how long delivered
|
|
712
|
+
rows remain available as duplicate evidence; it is not a replay-retention
|
|
713
|
+
period. Persisted accepted rows follow their delivery lifecycle and may be
|
|
714
|
+
replayed according to that lifecycle after the deduplication window has elapsed.
|
|
715
|
+
|
|
716
|
+
`DeliveryMonitor` is an instantiable, customizable policy seam exposing
|
|
717
|
+
asynchronous continuation, start/completion, failed-reception, pickup-failure,
|
|
718
|
+
and already-picked hooks.
|
|
719
|
+
`FailedReception` supplies only `markDelivered()` and one immediate
|
|
720
|
+
`repeatDispatching()` action; the framework adds no attempt history, timer,
|
|
721
|
+
backoff, quarantine, dead-letter, receipt, marker, or scheduler policy. A failed
|
|
722
|
+
durable acknowledgement keeps that row pending, blocks later rows for its
|
|
723
|
+
target during the run, and does not prevent independent targets from running.
|
|
724
|
+
|
|
725
|
+
`Environment` resolves the Node `local` or `production` profile once from
|
|
726
|
+
`NODE_ENV`. `ServerEnvironment` resolves configured facilities once for that
|
|
727
|
+
profile and exposes its `storageFactory`, optional `delivery`, optional
|
|
728
|
+
`tracerFactory`, `nodeId`, and `close()` lifecycle. Applications do
|
|
729
|
+
not manage internal server attachments through this public API. Neither API
|
|
730
|
+
authenticates requests or supplies a production scheduler, remote topology,
|
|
731
|
+
storage adapter, or deployment policy.
|
|
732
|
+
|
|
733
|
+
`ServerEnvironmentDelivery` extends the existing closeable facility shape with
|
|
734
|
+
`open()` plus generic `inbox` and `workRegistry` ports. After readiness, the
|
|
735
|
+
environment supplies those ports to both existing delivery paths: direct finite
|
|
736
|
+
`Delivery` construction and the supervisor's `DeliveryBuilder`. The environment
|
|
737
|
+
invokes `open()` once before attachment admission and coalesces concurrent
|
|
738
|
+
callers; rejection admits no attachment and a later attempt retries open. A
|
|
739
|
+
close-only local delivery remains source-compatible. After attachments retire,
|
|
740
|
+
environment close runs delivery, tracer, then storage and preserves retry
|
|
741
|
+
checkpoints for failed phases.
|
|
742
|
+
|
|
743
|
+
`ServerEnvironmentDelivery.source` is optional. When present, its Admin source
|
|
744
|
+
is used by the environment supervisor for remote snapshots and
|
|
745
|
+
shard-update hints; when absent, the existing local source remains the fallback.
|
|
746
|
+
Applications configure and close only the delivery facility through the
|
|
747
|
+
environment: attachment supervisors and their source reads belong to that facility.
|
|
748
|
+
|
|
749
|
+
## Signal identity and Aggregate semantics
|
|
750
|
+
|
|
751
|
+
Projection handlers cannot use `@Assign`; explicit metadata rejects that unsupported
|
|
752
|
+
handler shape. Framework-created child Commands and Events receive fresh IDs. Existing
|
|
753
|
+
Command or Event envelopes retain their IDs through transport and storage, including
|
|
754
|
+
when returned or passed as envelopes. Aggregate domain Events use the loaded pre-dispatch
|
|
755
|
+
version, while persisted Aggregate state advances to the next version. History caching accepts descending retained
|
|
756
|
+
versions with gaps, while continuing to reject equal or newer continuations.
|
|
757
|
+
|
|
758
|
+
# Dynamic unary discovery
|
|
759
|
+
|
|
760
|
+
`BrowserServerOptions.discovery` from `@spine-event-engine/server/browser` may supply changing complete application-node snapshots alongside fixed `backend` configuration. Both unary routing and native subscription streams use the same current membership. Empty membership retains durable definitions and later nodes reactivate them; a new backend-dependent subscription is unavailable until a node exists.
|