@spine-event-engine/server 2.0.0-snapshot.23 → 2.0.0-snapshot.25
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 +38 -11
- package/REFERENCE.md +278 -141
- 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 +15 -10
- package/dist/agent/agent-history.d.ts.map +1 -1
- package/dist/agent/agent-history.js +26 -14
- 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 +61 -0
- package/dist/agent/agent-live-read-runtime.d.ts.map +1 -0
- package/dist/agent/agent-live-read-runtime.js +121 -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/bus/command-bus.d.ts +14 -18
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +31 -48
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/event-bus.d.ts +82 -104
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +87 -127
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +143 -91
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +753 -359
- 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 +43 -20
- package/dist/context/entity-inbox.js.map +1 -1
- package/dist/context/local-inbox-handoff.d.ts +35 -34
- package/dist/context/local-inbox-handoff.d.ts.map +1 -1
- package/dist/context/local-inbox-handoff.js +123 -135
- 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 +12 -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 +12 -9
- package/dist/delivery/delivery-monitor.d.ts.map +1 -1
- package/dist/delivery/delivery-monitor.js +15 -9
- package/dist/delivery/delivery-monitor.js.map +1 -1
- package/dist/delivery/delivery-ports.d.ts +6 -13
- package/dist/delivery/delivery-ports.d.ts.map +1 -1
- package/dist/delivery/delivery-run-control.d.ts +21 -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 +33 -5
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
- package/dist/delivery/delivery-supervisor.js +178 -26
- 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 +803 -89
- 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 +14 -11
- package/dist/delivery/inbox-storage.d.ts.map +1 -1
- package/dist/delivery/inbox-storage.js +72 -78
- package/dist/delivery/inbox-storage.js.map +1 -1
- package/dist/delivery/inbox.d.ts +9 -12
- package/dist/delivery/inbox.d.ts.map +1 -1
- package/dist/delivery/inbox.js +13 -15
- package/dist/delivery/inbox.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +64 -0
- package/dist/delivery/reception-observation.d.ts.map +1 -0
- package/dist/delivery/reception-observation.js +78 -0
- package/dist/delivery/reception-observation.js.map +1 -0
- 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/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- 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 +33 -62
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +1475 -1827
- package/dist/repository/repository.js.map +1 -1
- package/dist/runtime/runtime.d.ts +24 -0
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +31 -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 +8 -15
- package/dist/runtime/signal-metadata.js.map +1 -1
- package/dist/runtime/signal-publisher.d.ts +8 -36
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +68 -61
- 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 +14 -130
- 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 +2 -2
- package/dist/server/managed-server-application.d.ts.map +1 -1
- package/dist/server/managed-server-application.js +471 -178
- package/dist/server/managed-server-application.js.map +1 -1
- package/dist/server/node-coordinator.d.ts +14 -0
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +266 -45
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/server-environment.d.ts +203 -25
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +661 -133
- 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 +18 -1
- package/dist/server/server-log.js.map +1 -1
- package/dist/server/server.d.ts +18 -32
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +62 -203
- package/dist/server/server.js.map +1 -1
- package/dist/services/spine-services.d.ts +1 -1
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +399 -370
- package/dist/services/spine-services.js.map +1 -1
- package/dist/stand/event-origin-filter.d.ts +16 -0
- package/dist/stand/event-origin-filter.d.ts.map +1 -0
- package/dist/stand/event-origin-filter.js +124 -0
- package/dist/stand/event-origin-filter.js.map +1 -0
- package/dist/stand/stand.d.ts +14 -0
- package/dist/stand/stand.d.ts.map +1 -1
- package/dist/stand/stand.js +53 -9
- package/dist/stand/stand.js.map +1 -1
- package/dist/stand/subscription-observer.d.ts.map +1 -1
- package/dist/stand/subscription-observer.js +61 -40
- package/dist/stand/subscription-observer.js.map +1 -1
- package/dist/stand/subscription-registry.d.ts +6 -2
- package/dist/stand/subscription-registry.d.ts.map +1 -1
- package/dist/stand/subscription-registry.js +15 -11
- package/dist/stand/subscription-registry.js.map +1 -1
- package/dist/stand/subscription-runtime.d.ts +23 -4
- package/dist/stand/subscription-runtime.d.ts.map +1 -1
- package/dist/stand/subscription-runtime.js +84 -13
- package/dist/stand/subscription-runtime.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +3 -2
- package/dist/testing/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- 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 -96
- package/dist/bus/saved-dispatcher-binding.d.ts.map +0 -1
- package/dist/bus/saved-dispatcher-binding.js +0 -35
- 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 -673
- package/dist/server/environment-delivery-worker.js.map +0 -1
|
@@ -12,14 +12,20 @@
|
|
|
12
12
|
* the License.
|
|
13
13
|
*/
|
|
14
14
|
import { InMemoryStorageFactory } from "@spine-event-engine/storage";
|
|
15
|
+
import { TenantBoundary } from "@spine-event-engine/storage/provider";
|
|
15
16
|
import { LogLayer, StructuredTransport } from "loglayer";
|
|
16
17
|
import { InMemoryTransportFactory } from "@spine-event-engine/transport";
|
|
17
18
|
import { spineCoreRegistry } from "@spine-event-engine/core";
|
|
18
|
-
import { RetryableCloseGroup } from "./retryable-close.js";
|
|
19
|
-
import {
|
|
19
|
+
import { CloseErrors, RetryableCloseGroup } from "./retryable-close.js";
|
|
20
|
+
import { Delivery as CoreDelivery } from "../delivery/delivery.js";
|
|
21
|
+
import { deliveryControls, deliveryStrategyAccess } from "../delivery/delivery-builder.js";
|
|
22
|
+
import { DeliverySupervisor, deliverySupervisorAccess } from "../delivery/delivery-supervisor.js";
|
|
23
|
+
import { deliveryTenant } from "../delivery/delivery-tenant.js";
|
|
24
|
+
import { emitServerInfo, emitServerWarning } from "./server-log.js";
|
|
25
|
+
import { managedDeliveryGate } from "./managed-delivery-gate.js";
|
|
20
26
|
import { boundedContextAccess } from "../context/bounded-context.js";
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
27
|
+
import { TenantIndexes } from "../context/tenant-index.js";
|
|
28
|
+
import { TenantUnknownError } from "../context/tenant-unknown-error.js";
|
|
23
29
|
import { Environment, EnvironmentTests, EnvironmentType } from "./environment.js";
|
|
24
30
|
const configuredSettings = new Map();
|
|
25
31
|
let resolvedEnvironment;
|
|
@@ -41,6 +47,14 @@ export class ServerEnvironment {
|
|
|
41
47
|
* Storage facility selected for server assembly, including server-added context builders.
|
|
42
48
|
*/
|
|
43
49
|
storageFactory;
|
|
50
|
+
/**
|
|
51
|
+
* Live default tenant membership index.
|
|
52
|
+
*/
|
|
53
|
+
tenantIndex;
|
|
54
|
+
/**
|
|
55
|
+
* Default AI registry selected for Bounded Context assembly, when configured.
|
|
56
|
+
*/
|
|
57
|
+
ai;
|
|
44
58
|
/**
|
|
45
59
|
* Message-channel factory selected for private bounded-context integration.
|
|
46
60
|
*
|
|
@@ -54,45 +68,91 @@ export class ServerEnvironment {
|
|
|
54
68
|
*/
|
|
55
69
|
typeRegistry;
|
|
56
70
|
/**
|
|
57
|
-
* Optional closeable delivery
|
|
71
|
+
* Optional closeable remote delivery facility selected for this environment.
|
|
72
|
+
*/
|
|
73
|
+
#remoteDelivery;
|
|
74
|
+
/**
|
|
75
|
+
* Returns the actual shared Delivery after explicit environment setup.
|
|
76
|
+
* @returns The one public Delivery facade for this environment.
|
|
58
77
|
*/
|
|
59
|
-
delivery
|
|
78
|
+
get delivery() {
|
|
79
|
+
if (this.#publicDelivery === undefined)
|
|
80
|
+
throw new Error("ServerEnvironment.completeSetup() must finish before Delivery use.");
|
|
81
|
+
return this.#publicDelivery;
|
|
82
|
+
}
|
|
60
83
|
/**
|
|
61
84
|
* Optional tracing factory selected for this environment.
|
|
62
85
|
*/
|
|
63
86
|
tracerFactory;
|
|
64
87
|
#ownedCloseables;
|
|
65
88
|
#closeGroup;
|
|
89
|
+
#deliveryCloseGroup;
|
|
90
|
+
#deliveryStrategy;
|
|
91
|
+
#delivery;
|
|
92
|
+
#publicDelivery;
|
|
93
|
+
#supervisor;
|
|
94
|
+
#supervisorStart;
|
|
66
95
|
#close;
|
|
67
96
|
#deliveryOpen;
|
|
68
97
|
#deliveryOpened = false;
|
|
98
|
+
#closing = false;
|
|
99
|
+
#fatal;
|
|
100
|
+
/**
|
|
101
|
+
* Initializes resolved environment facilities.
|
|
102
|
+
*
|
|
103
|
+
* @param environment Selected process environment.
|
|
104
|
+
* @param settings Resolved facilities and settings.
|
|
105
|
+
*/
|
|
69
106
|
constructor(environment, settings) {
|
|
70
107
|
this.environment = environment;
|
|
71
108
|
this.nodeId = crypto.randomUUID();
|
|
72
109
|
this.storageFactory = settings.storageFactory;
|
|
110
|
+
this.tenantIndex = settings.tenantIndex ?? TenantIndexes.fromFactory(settings.storageFactory);
|
|
111
|
+
this.ai = settings.ai;
|
|
73
112
|
this.integrationChannelFactory = settings.integrationChannelFactory;
|
|
74
113
|
this.typeRegistry = settings.typeRegistry;
|
|
75
|
-
this
|
|
114
|
+
this.#remoteDelivery = settings.delivery;
|
|
115
|
+
this.#deliveryStrategy = settings.deliveryStrategy;
|
|
76
116
|
this.tracerFactory = settings.tracerFactory;
|
|
77
|
-
|
|
78
|
-
this.#
|
|
79
|
-
|
|
117
|
+
this.#registerRuntimeHooks(settings.logger);
|
|
118
|
+
this.#deliveryCloseGroup = new RetryableCloseGroup([
|
|
119
|
+
{ close: () => this.#supervisor?.close() },
|
|
120
|
+
{ close: () => this.#delivery?.close() },
|
|
121
|
+
this.#remoteDelivery,
|
|
122
|
+
], "Environment Delivery close failed.");
|
|
123
|
+
this.#ownedCloseables = ServerEnvironmentValues.facilitiesToClose(settings, this.#deliveryCloseGroup);
|
|
80
124
|
this.#closeGroup = new RetryableCloseGroup(this.#ownedCloseables, "ServerEnvironment close failed.");
|
|
81
|
-
environmentAttachments.set(this, new EnvironmentAttachments({
|
|
82
|
-
logger: ServerEnvironmentValues.loggerFor(this),
|
|
83
|
-
createWorker: () => {
|
|
84
|
-
const ports = ServerEnvironmentValues.ports(this.delivery);
|
|
85
|
-
return new EnvironmentDeliveryWorker({
|
|
86
|
-
...(ports === undefined ? {} : { ports }),
|
|
87
|
-
nodeId: this.nodeId,
|
|
88
|
-
});
|
|
89
|
-
},
|
|
90
|
-
deliveryPorts: () => ServerEnvironmentValues.ports(this.delivery),
|
|
91
|
-
}));
|
|
92
|
-
deliveryOpeners.set(this, () => this.#openDelivery());
|
|
93
|
-
testAttachmentsInstallable.add(this);
|
|
94
125
|
Object.freeze(this);
|
|
95
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Registers package-local callbacks for this environment's runtime.
|
|
129
|
+
* @param logger Receives diagnostics from these callbacks.
|
|
130
|
+
*/
|
|
131
|
+
#registerRuntimeHooks(logger) {
|
|
132
|
+
environmentLoggers.set(this, logger);
|
|
133
|
+
environmentContexts.set(this, new EnvironmentContextRegistrations());
|
|
134
|
+
startupControls.set(this, {
|
|
135
|
+
fail: (cause) => {
|
|
136
|
+
this.#failStartup(cause);
|
|
137
|
+
},
|
|
138
|
+
awaitFailure: () => this.#awaitStartupFailure(),
|
|
139
|
+
available: () => {
|
|
140
|
+
if (this.#closing)
|
|
141
|
+
throw new Error("ServerEnvironment is closed.");
|
|
142
|
+
},
|
|
143
|
+
failed: () => this.#fatal !== undefined,
|
|
144
|
+
recordCleanup: (error) => {
|
|
145
|
+
if (this.#fatal !== undefined)
|
|
146
|
+
CloseErrors.collect(error, this.#fatal.cleanupErrors);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
warnedVolatileRegistries.set(this, new WeakSet());
|
|
150
|
+
supervisorStarters.set(this, () => this.#startSupervisor());
|
|
151
|
+
deliveryNotifiers.set(this, (shard) => {
|
|
152
|
+
this.#notifyDelivery(shard);
|
|
153
|
+
});
|
|
154
|
+
deliveryIdlers.set(this, () => this.#whenDeliveryIdle());
|
|
155
|
+
}
|
|
96
156
|
/**
|
|
97
157
|
* Sets facilities for an environment type before first resolution.
|
|
98
158
|
*
|
|
@@ -131,19 +191,162 @@ export class ServerEnvironment {
|
|
|
131
191
|
return new ServerEnvironment(environment, facilities);
|
|
132
192
|
}
|
|
133
193
|
/**
|
|
134
|
-
*
|
|
194
|
+
* Checks shared storage and initializes transport and Delivery before context startup.
|
|
135
195
|
*
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
|
|
196
|
+
* @returns The coalesced setup attempt for this environment lifetime.
|
|
197
|
+
*/
|
|
198
|
+
completeSetup() {
|
|
199
|
+
if (this.#closing)
|
|
200
|
+
return Promise.reject(new Error("ServerEnvironment is closed."));
|
|
201
|
+
const current = environmentSetups.get(this);
|
|
202
|
+
if (current !== undefined)
|
|
203
|
+
return current;
|
|
204
|
+
const started = this.#completeSetup();
|
|
205
|
+
environmentSetups.set(this, started);
|
|
206
|
+
return started;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Creates declared structures for the completed open contexts in this environment.
|
|
210
|
+
* Explicit IDs select matching multitenant contexts through their effective indexes.
|
|
211
|
+
* Database-backed providers require an existing database; this method does not
|
|
212
|
+
* provision one or register tenants.
|
|
213
|
+
* @param tenantIds Optional complete tenant identities; absence includes single-tenant
|
|
214
|
+
* contexts and pages current multitenant indexes.
|
|
215
|
+
* @returns Completion after participating contexts are prepared.
|
|
216
|
+
*/
|
|
217
|
+
createStorageStructures(...tenantIds) {
|
|
218
|
+
return this.#prepareContexts("create", tenantIds);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Checks existing structures without authorizing creation.
|
|
222
|
+
* Explicit IDs select matching multitenant contexts through their effective indexes.
|
|
223
|
+
* The operation does not provision databases or register tenants.
|
|
224
|
+
* @param tenantIds Optional complete tenant identities; absence includes single-tenant
|
|
225
|
+
* contexts and pages current multitenant indexes.
|
|
226
|
+
* @returns Completion after participating contexts pass read-only checks.
|
|
227
|
+
*/
|
|
228
|
+
checkStructureCompatibility(...tenantIds) {
|
|
229
|
+
return this.#prepareContexts("check", tenantIds);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Prevalidates selected tenant membership before starting provider work.
|
|
233
|
+
* @param contexts Captured completed context references.
|
|
234
|
+
* @param tenantIds Explicit complete tenant identities.
|
|
235
|
+
* @returns Participating contexts and their selected tenants.
|
|
236
|
+
*/
|
|
237
|
+
async #selectedContexts(contexts, tenantIds) {
|
|
238
|
+
const selected = new Map();
|
|
239
|
+
const boundaries = new Map();
|
|
240
|
+
for (const id of tenantIds) {
|
|
241
|
+
const boundary = TenantBoundary.from(id);
|
|
242
|
+
boundaries.set(boundary.key, boundary);
|
|
243
|
+
}
|
|
244
|
+
for (const boundary of boundaries.values()) {
|
|
245
|
+
let found = false;
|
|
246
|
+
for (const context of contexts) {
|
|
247
|
+
serverEnvironmentAccess.requireAvailable(this);
|
|
248
|
+
if (!context.isMultitenant)
|
|
249
|
+
continue;
|
|
250
|
+
const present = await context.tenantIndex.contains(boundary.tenantId);
|
|
251
|
+
serverEnvironmentAccess.requireAvailable(this);
|
|
252
|
+
if (!present)
|
|
253
|
+
continue;
|
|
254
|
+
const ids = selected.get(context) ?? [];
|
|
255
|
+
ids.push(boundary.tenantId);
|
|
256
|
+
selected.set(context, ids);
|
|
257
|
+
found = true;
|
|
258
|
+
}
|
|
259
|
+
if (!found)
|
|
260
|
+
throw new TenantUnknownError(boundary.tenantId, "ServerEnvironment");
|
|
261
|
+
}
|
|
262
|
+
return selected;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Prepares captured contexts after complete explicit-ID validation.
|
|
266
|
+
* @param mode Creation or read-only check.
|
|
267
|
+
* @param tenantIds Optional complete tenant identities.
|
|
268
|
+
* @returns Completion after every participating context settles.
|
|
269
|
+
*/
|
|
270
|
+
async #prepareContexts(mode, tenantIds) {
|
|
271
|
+
serverEnvironmentAccess.requireAvailable(this);
|
|
272
|
+
const captured = tenantIds.map((id) => TenantBoundary.from(id).tenantId);
|
|
273
|
+
const contexts = serverEnvironmentAccess
|
|
274
|
+
.openContexts(this)
|
|
275
|
+
.filter((context) => !boundedContextAccess.isClosing(context));
|
|
276
|
+
if (contexts.length === 0)
|
|
277
|
+
throw new Error("ServerEnvironment has no completed Bounded Context to prepare.");
|
|
278
|
+
const selection = Promise.withResolvers();
|
|
279
|
+
const admitted = contexts.map((context) => boundedContextAccess.admitServiceRegistryOperation(context, async () => {
|
|
280
|
+
const selected = await selection.promise;
|
|
281
|
+
const ids = selected?.get(context);
|
|
282
|
+
if (selected !== undefined && ids === undefined)
|
|
283
|
+
return;
|
|
284
|
+
await boundedContextAccess.prepareExplicit(context, mode, ids ?? []);
|
|
285
|
+
}));
|
|
286
|
+
if (captured.length === 0)
|
|
287
|
+
selection.resolve(undefined);
|
|
288
|
+
else
|
|
289
|
+
void this.#selectedContexts(contexts, captured).then(selection.resolve, selection.reject);
|
|
290
|
+
const results = await Promise.allSettled(admitted);
|
|
291
|
+
if (serverEnvironmentAccess.hasStartupFailure(this))
|
|
292
|
+
return serverEnvironmentAccess.awaitStartupFailure(this);
|
|
293
|
+
const failure = results.find((result) => result.status === "rejected");
|
|
294
|
+
if (failure?.status === "rejected")
|
|
295
|
+
throw failure.reason;
|
|
296
|
+
}
|
|
297
|
+
async #completeSetup() {
|
|
298
|
+
try {
|
|
299
|
+
if (typeof this.integrationChannelFactory.initialize !== "function")
|
|
300
|
+
throw new TypeError("TransportFactory.initialize is required.");
|
|
301
|
+
await this.storageFactory.checkConnection();
|
|
302
|
+
emitServerInfo(ServerEnvironmentValues.loggerFor(this), "Environment storage connection checked.", { operation: "server.setup_storage" });
|
|
303
|
+
await this.integrationChannelFactory.initialize();
|
|
304
|
+
emitServerInfo(ServerEnvironmentValues.loggerFor(this), "Environment transport initialized.", { operation: "server.setup_transport" });
|
|
305
|
+
await this.#openDelivery();
|
|
306
|
+
if (this.#closing)
|
|
307
|
+
throw new Error("ServerEnvironment closed during setup.");
|
|
308
|
+
emitServerInfo(ServerEnvironmentValues.loggerFor(this), "Environment Delivery initialized.", {
|
|
309
|
+
operation: "server.setup_delivery",
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
this.#closing = true;
|
|
314
|
+
const cleanup = await Promise.allSettled([this.#closeGroup.close()]);
|
|
315
|
+
const failures = cleanup.flatMap((result) => {
|
|
316
|
+
if (result.status !== "rejected")
|
|
317
|
+
return [];
|
|
318
|
+
return result.reason instanceof AggregateError
|
|
319
|
+
? result.reason.errors
|
|
320
|
+
: [result.reason];
|
|
321
|
+
});
|
|
322
|
+
if (failures.length)
|
|
323
|
+
throw new AggregateError([error, ...failures], "Environment setup failed.");
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Closes this environment after all Bounded Contexts close.
|
|
139
329
|
*
|
|
140
|
-
*
|
|
330
|
+
* A healthy environment with active contexts rejects without teardown. After
|
|
331
|
+
* required startup failure, this joins automatic cleanup and rejects with the
|
|
332
|
+
* original cause and any cleanup failures. Successful closure prevents reuse;
|
|
333
|
+
* failed facility closes during an ordinary close remain retryable.
|
|
141
334
|
*
|
|
335
|
+
* @returns Completion after facilities close or a rejection reporting failures.
|
|
142
336
|
*/
|
|
143
337
|
close() {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
338
|
+
if (this.#fatal !== undefined)
|
|
339
|
+
return this.#awaitStartupFailure();
|
|
340
|
+
if (EnvironmentContextRegistrations.forEnvironment(this).active)
|
|
341
|
+
return Promise.reject(new Error("Cannot close ServerEnvironment with active Bounded Contexts."));
|
|
342
|
+
this.#closing = true;
|
|
343
|
+
this.#delivery?.beginClose();
|
|
344
|
+
this.#close ??= Promise.resolve()
|
|
345
|
+
.then(async () => {
|
|
346
|
+
const setup = environmentSetups.get(this);
|
|
347
|
+
if (setup !== undefined)
|
|
348
|
+
await Promise.allSettled([setup]);
|
|
349
|
+
})
|
|
147
350
|
.then(() => this.#closeGroup.close())
|
|
148
351
|
.catch((error) => {
|
|
149
352
|
this.#close = undefined;
|
|
@@ -151,16 +354,83 @@ export class ServerEnvironment {
|
|
|
151
354
|
});
|
|
152
355
|
return this.#close;
|
|
153
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Fences a failed required startup phase and starts one automatic cleanup.
|
|
359
|
+
* @param cause Original required-phase failure.
|
|
360
|
+
*/
|
|
361
|
+
#failStartup(cause) {
|
|
362
|
+
if (this.#fatal !== undefined) {
|
|
363
|
+
this.#fatal.originals.push(cause);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
this.#closing = true;
|
|
367
|
+
const contexts = EnvironmentContextRegistrations.forEnvironment(this);
|
|
368
|
+
const cleanupErrors = [];
|
|
369
|
+
const cleanup = Promise.resolve().then(() => this.#cleanupStartup(contexts, cleanupErrors));
|
|
370
|
+
this.#fatal = { originals: [cause], cleanupErrors, cleanup };
|
|
371
|
+
for (const context of contexts.allContexts()) {
|
|
372
|
+
try {
|
|
373
|
+
boundedContextAccess.beginClose(context);
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
CloseErrors.collect(error, cleanupErrors);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
this.#delivery?.beginStartupDrain();
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Waits for local rollbacks, then closes contexts and shared facilities.
|
|
383
|
+
* @param contexts Context registrations for this environment.
|
|
384
|
+
* @param cleanupErrors Mutable cleanup failures.
|
|
385
|
+
* @returns Completion after every cleanup attempt.
|
|
386
|
+
*/
|
|
387
|
+
async #cleanupStartup(contexts, cleanupErrors) {
|
|
388
|
+
const started = new Set(contexts.openContexts());
|
|
389
|
+
const observe = (context) => Promise.resolve()
|
|
390
|
+
.then(() => context.close())
|
|
391
|
+
.then(() => ({ status: "fulfilled", value: undefined }), (reason) => ({ status: "rejected", reason }));
|
|
392
|
+
const completed = [...started].map(observe);
|
|
393
|
+
await contexts.whenBuildsIdle();
|
|
394
|
+
const remaining = contexts
|
|
395
|
+
.allContexts()
|
|
396
|
+
.filter((context) => !started.has(context))
|
|
397
|
+
.map(observe);
|
|
398
|
+
for (const result of await Promise.all([...completed, ...remaining]))
|
|
399
|
+
if (result.status === "rejected")
|
|
400
|
+
CloseErrors.collect(result.reason, cleanupErrors);
|
|
401
|
+
this.#delivery?.beginClose();
|
|
402
|
+
try {
|
|
403
|
+
await this.#closeGroup.close();
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
CloseErrors.collect(error, cleanupErrors);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Reports all original and cleanup errors after automatic cleanup settles.
|
|
411
|
+
* @returns Rejection preserving the original failure when it is the only error.
|
|
412
|
+
*/
|
|
413
|
+
async #awaitStartupFailure() {
|
|
414
|
+
const fatal = this.#fatal;
|
|
415
|
+
if (fatal === undefined)
|
|
416
|
+
throw new Error("No startup failure was recorded.");
|
|
417
|
+
await fatal.cleanup;
|
|
418
|
+
const failures = [...fatal.originals, ...fatal.cleanupErrors];
|
|
419
|
+
if (failures.length === 1)
|
|
420
|
+
throw failures[0];
|
|
421
|
+
throw new AggregateError(failures, "Required environment initialization failed.");
|
|
422
|
+
}
|
|
154
423
|
#openDelivery() {
|
|
155
424
|
if (this.#deliveryOpened)
|
|
156
425
|
return Promise.resolve();
|
|
157
426
|
const opening = (this.#deliveryOpen ??= Promise.resolve()
|
|
158
|
-
.then(() => {
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
427
|
+
.then(async () => {
|
|
428
|
+
const remote = ServerEnvironmentValues.openableDelivery(this.#remoteDelivery);
|
|
429
|
+
if (remote !== undefined)
|
|
430
|
+
await remote.open();
|
|
431
|
+
if (this.#closing)
|
|
432
|
+
throw new Error("ServerEnvironment closed during Delivery setup.");
|
|
433
|
+
this.#constructDelivery(ServerEnvironmentValues.ports(this.#remoteDelivery));
|
|
164
434
|
})
|
|
165
435
|
.then(() => {
|
|
166
436
|
this.#deliveryOpened = true;
|
|
@@ -172,12 +442,79 @@ export class ServerEnvironment {
|
|
|
172
442
|
});
|
|
173
443
|
return opening;
|
|
174
444
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
445
|
+
/**
|
|
446
|
+
* Constructs the one environment Delivery after configured ports open.
|
|
447
|
+
* @param ports Open remote ports when configured.
|
|
448
|
+
*/
|
|
449
|
+
#constructDelivery(ports) {
|
|
450
|
+
const delivery = new CoreDelivery({
|
|
451
|
+
context: { name: "__System_Delivery__", multitenant: false },
|
|
452
|
+
storageFactory: this.storageFactory,
|
|
453
|
+
node: this.nodeId,
|
|
454
|
+
...(ports?.source === undefined ? { logger: ServerEnvironmentValues.loggerFor(this) } : {}),
|
|
455
|
+
...(this.#deliveryStrategy === undefined
|
|
456
|
+
? {}
|
|
457
|
+
: { strategy: deliveryStrategyAccess.snapshot(this.#deliveryStrategy) }),
|
|
458
|
+
...(ports ?? {}),
|
|
459
|
+
});
|
|
460
|
+
delivery.setReplay((message) => this.#replayMessage(message), (message) => this.#acceptsMessage(message));
|
|
461
|
+
this.#delivery = delivery;
|
|
462
|
+
environmentDeliveryCores.set(this, delivery);
|
|
463
|
+
this.#publicDelivery = deliveryControls.supervised(delivery);
|
|
464
|
+
if (ports?.source !== undefined)
|
|
465
|
+
this.#installSupervisor(this.#publicDelivery, ports.source);
|
|
466
|
+
}
|
|
467
|
+
#acceptsMessage(message) {
|
|
468
|
+
return (EnvironmentContextRegistrations.forEnvironment(this).recipient(message.inboxId.targetTypeUrl) !== undefined);
|
|
469
|
+
}
|
|
470
|
+
#installSupervisor(delivery, source) {
|
|
471
|
+
const supervisor = new DeliverySupervisor({
|
|
472
|
+
source,
|
|
473
|
+
delivery,
|
|
474
|
+
onMessage: (message) => this.#replayMessage(message),
|
|
475
|
+
});
|
|
476
|
+
deliverySupervisorAccess.installAdmission(supervisor, (message) => this.#acceptsMessage(message));
|
|
477
|
+
deliverySupervisorAccess.installLogger(supervisor, ServerEnvironmentValues.loggerFor(this));
|
|
478
|
+
this.#supervisor = supervisor;
|
|
479
|
+
remoteSupervisedEnvironments.add(this);
|
|
480
|
+
}
|
|
481
|
+
#startSupervisor() {
|
|
482
|
+
const supervisor = this.#supervisor;
|
|
483
|
+
return supervisor === undefined
|
|
484
|
+
? Promise.resolve()
|
|
485
|
+
: managedDeliveryGate.start(() => (this.#supervisorStart ??= supervisor.start()));
|
|
486
|
+
}
|
|
487
|
+
#notifyDelivery(shard) {
|
|
488
|
+
if (this.#supervisor === undefined) {
|
|
489
|
+
this.#delivery?.wake(shard);
|
|
490
|
+
return;
|
|
179
491
|
}
|
|
180
|
-
|
|
492
|
+
try {
|
|
493
|
+
this.#supervisor.notify(shard);
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
emitServerWarning(ServerEnvironmentValues.loggerFor(this), "Delivery notification failed.", {
|
|
497
|
+
operation: "delivery.notify",
|
|
498
|
+
reasonCode: "failed",
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async #whenDeliveryIdle() {
|
|
503
|
+
const settled = await Promise.allSettled([
|
|
504
|
+
this.#supervisor?.whenIdle(),
|
|
505
|
+
this.#delivery?.whenIdle(),
|
|
506
|
+
]);
|
|
507
|
+
const failures = settled.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
508
|
+
if (failures.length === 1)
|
|
509
|
+
throw failures[0];
|
|
510
|
+
if (failures.length)
|
|
511
|
+
throw new AggregateError(failures, "Environment Delivery idle wait failed.");
|
|
512
|
+
}
|
|
513
|
+
async #replayMessage(message) {
|
|
514
|
+
const context = EnvironmentContextRegistrations.forEnvironment(this).recipient(message.inboxId.targetTypeUrl);
|
|
515
|
+
if (context === undefined)
|
|
516
|
+
throw new Error("No open Bounded Context serves the Inbox target.");
|
|
517
|
+
await boundedContextAccess.trackReplay(context, () => boundedContextAccess.delivery(context).replay(message, deliveryTenant(message)));
|
|
181
518
|
}
|
|
182
519
|
}
|
|
183
520
|
/**
|
|
@@ -196,8 +533,19 @@ export const ServerEnvironmentLifecycle = Object.freeze({
|
|
|
196
533
|
return current;
|
|
197
534
|
}
|
|
198
535
|
const resolved = resolvedEnvironment;
|
|
536
|
+
if (resolved !== undefined && EnvironmentContextRegistrations.forEnvironment(resolved).active) {
|
|
537
|
+
return Promise.reject(new Error("Cannot reset ServerEnvironment with active Bounded Contexts."));
|
|
538
|
+
}
|
|
199
539
|
const reset = Promise.resolve()
|
|
200
|
-
.then(() =>
|
|
540
|
+
.then(() => {
|
|
541
|
+
if (resolved !== undefined && EnvironmentContextRegistrations.forEnvironment(resolved).active) {
|
|
542
|
+
throw new Error("Cannot reset ServerEnvironment with active Bounded Contexts.");
|
|
543
|
+
}
|
|
544
|
+
if (resolved !== undefined && startupControls.get(resolved)?.failed())
|
|
545
|
+
// spine-log-boundary: server.fatal_environment_test_reset
|
|
546
|
+
return resolved.close().catch(() => undefined);
|
|
547
|
+
return resolved?.close();
|
|
548
|
+
})
|
|
201
549
|
.then(() => {
|
|
202
550
|
resolvedEnvironment = undefined;
|
|
203
551
|
configuredSettings.clear();
|
|
@@ -210,109 +558,248 @@ export const ServerEnvironmentLifecycle = Object.freeze({
|
|
|
210
558
|
return reset;
|
|
211
559
|
},
|
|
212
560
|
});
|
|
213
|
-
const
|
|
561
|
+
const environmentSetups = new WeakMap();
|
|
562
|
+
const startupControls = new WeakMap();
|
|
563
|
+
const environmentDeliveryCores = new WeakMap();
|
|
564
|
+
const remoteSupervisedEnvironments = new WeakSet();
|
|
214
565
|
const environmentLoggers = new WeakMap();
|
|
215
|
-
const
|
|
216
|
-
const
|
|
566
|
+
const supervisorStarters = new WeakMap();
|
|
567
|
+
const deliveryNotifiers = new WeakMap();
|
|
568
|
+
const deliveryIdlers = new WeakMap();
|
|
217
569
|
const warnedVolatileRegistries = new WeakMap();
|
|
570
|
+
const environmentContexts = new WeakMap();
|
|
571
|
+
/**
|
|
572
|
+
* Records live domain contexts separately from build-phase reservations.
|
|
573
|
+
*/
|
|
574
|
+
class EnvironmentContextRegistrations {
|
|
575
|
+
#contexts = new Set();
|
|
576
|
+
#completed = new Set();
|
|
577
|
+
#states = new Map();
|
|
578
|
+
#recipients = new Map();
|
|
579
|
+
#builds = 0;
|
|
580
|
+
#buildsIdle;
|
|
581
|
+
/**
|
|
582
|
+
* Finds the registrations attached to one resolved environment.
|
|
583
|
+
*
|
|
584
|
+
* @param environment Resolved environment.
|
|
585
|
+
* @returns Its context registrations.
|
|
586
|
+
*/
|
|
587
|
+
static forEnvironment(environment) {
|
|
588
|
+
const contexts = environmentContexts.get(environment);
|
|
589
|
+
if (contexts === undefined)
|
|
590
|
+
throw new TypeError("Context registration requires an environment.");
|
|
591
|
+
return contexts;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Checks whether a full singleton reset would discard context state.
|
|
595
|
+
*
|
|
596
|
+
* @returns Whether a context or build remains active.
|
|
597
|
+
*/
|
|
598
|
+
get active() {
|
|
599
|
+
return this.#builds > 0 || this.#contexts.size > 0;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Records a build until asynchronous assembly settles.
|
|
603
|
+
*
|
|
604
|
+
* @returns A callback that finishes this build exactly once.
|
|
605
|
+
*/
|
|
606
|
+
beginBuild() {
|
|
607
|
+
if (this.#builds === 0)
|
|
608
|
+
this.#buildsIdle = Promise.withResolvers();
|
|
609
|
+
this.#builds++;
|
|
610
|
+
let finished = false;
|
|
611
|
+
return () => {
|
|
612
|
+
if (finished)
|
|
613
|
+
return;
|
|
614
|
+
finished = true;
|
|
615
|
+
this.#builds--;
|
|
616
|
+
if (this.#builds === 0)
|
|
617
|
+
this.#buildsIdle?.resolve(undefined);
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Waits until every admitted build releases its local assembly ticket.
|
|
622
|
+
* @returns Completion when no build remains.
|
|
623
|
+
*/
|
|
624
|
+
whenBuildsIdle() {
|
|
625
|
+
return this.#buildsIdle?.promise ?? Promise.resolve();
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Lists constructed contexts, including incomplete registrations.
|
|
629
|
+
* @returns Snapshot of all constructed contexts.
|
|
630
|
+
*/
|
|
631
|
+
allContexts() {
|
|
632
|
+
return [...this.#contexts];
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Records an open or partly constructed context.
|
|
636
|
+
*
|
|
637
|
+
* @param context Context to track.
|
|
638
|
+
*/
|
|
639
|
+
add(context) {
|
|
640
|
+
this.#contexts.add(context);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Marks a tracked context ready for public cleanup enumeration.
|
|
644
|
+
*
|
|
645
|
+
* @param context Context whose integration opened successfully.
|
|
646
|
+
*/
|
|
647
|
+
complete(context) {
|
|
648
|
+
if (!this.#contexts.has(context))
|
|
649
|
+
throw new Error("Context completed after cleanup.");
|
|
650
|
+
this.#completed.add(context);
|
|
651
|
+
for (const endpoint of boundedContextAccess.delivery(context).endpoints())
|
|
652
|
+
this.#recipients.set(endpoint.targetTypeUrl, context);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Finds the completed context registered for a delivery target.
|
|
656
|
+
* @param typeUrl Identifies the target message type.
|
|
657
|
+
* @returns The matching context, if one completed registration.
|
|
658
|
+
*/
|
|
659
|
+
recipient(typeUrl) {
|
|
660
|
+
return this.#recipients.get(typeUrl);
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Registers one state type without overwriting a different context.
|
|
664
|
+
*
|
|
665
|
+
* @param context Context registering the state.
|
|
666
|
+
* @param typeUrl Full wire type URL of the state.
|
|
667
|
+
* @returns The conflicting context, if another context registered it.
|
|
668
|
+
*/
|
|
669
|
+
reserve(context, typeUrl) {
|
|
670
|
+
const prior = this.#states.get(typeUrl);
|
|
671
|
+
if (prior !== undefined)
|
|
672
|
+
return prior === context ? undefined : prior;
|
|
673
|
+
this.#states.set(typeUrl, context);
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Removes only registrations made by the selected context.
|
|
678
|
+
*
|
|
679
|
+
* @param context Context that completed shutdown.
|
|
680
|
+
*/
|
|
681
|
+
remove(context) {
|
|
682
|
+
this.#contexts.delete(context);
|
|
683
|
+
this.#completed.delete(context);
|
|
684
|
+
this.stopRecipients(context);
|
|
685
|
+
for (const [typeUrl, registered] of this.#states) {
|
|
686
|
+
if (registered === context)
|
|
687
|
+
this.#states.delete(typeUrl);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Removes fresh delivery endpoints while retaining state reservations.
|
|
692
|
+
* @param context Context entering close.
|
|
693
|
+
*/
|
|
694
|
+
stopRecipients(context) {
|
|
695
|
+
for (const [typeUrl, recipient] of this.#recipients)
|
|
696
|
+
if (recipient === context)
|
|
697
|
+
this.#recipients.delete(typeUrl);
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Lists the context currently serving a state type.
|
|
701
|
+
*
|
|
702
|
+
* @param typeUrl Full wire type URL of the state.
|
|
703
|
+
* @returns Zero or one context names.
|
|
704
|
+
*/
|
|
705
|
+
serving(typeUrl) {
|
|
706
|
+
const context = this.#states.get(typeUrl);
|
|
707
|
+
return context === undefined ? [] : [context.name.value];
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Returns completed context references without releasing them.
|
|
711
|
+
*
|
|
712
|
+
* @returns References to integrated open contexts.
|
|
713
|
+
*/
|
|
714
|
+
openContexts() {
|
|
715
|
+
return [...this.#completed];
|
|
716
|
+
}
|
|
717
|
+
}
|
|
218
718
|
/**
|
|
219
719
|
* Provides package-only environment delivery attachment access for server lifecycle owners.
|
|
220
720
|
*
|
|
221
721
|
* @internal
|
|
222
722
|
*/
|
|
223
723
|
export const serverEnvironmentAccess = Object.freeze({
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
const openDelivery = deliveryOpeners.get(environment);
|
|
231
|
-
if (openDelivery === undefined) {
|
|
232
|
-
return Promise.reject(new TypeError("Attachment requires a ServerEnvironment instance."));
|
|
233
|
-
}
|
|
234
|
-
return openDelivery().then(() => attachments.attach(options));
|
|
724
|
+
coreDelivery(environment) {
|
|
725
|
+
const core = environmentDeliveryCores.get(environment);
|
|
726
|
+
if (core === undefined)
|
|
727
|
+
throw new Error("ServerEnvironment.completeSetup() must finish before Delivery use.");
|
|
728
|
+
return core;
|
|
235
729
|
},
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (attachments === undefined) {
|
|
239
|
-
throw new TypeError("Failed-start observation requires a ServerEnvironment instance.");
|
|
240
|
-
}
|
|
241
|
-
return attachments.failedStartPending;
|
|
730
|
+
remoteSupervised(environment) {
|
|
731
|
+
return remoteSupervisedEnvironments.has(environment);
|
|
242
732
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (attachments === undefined) {
|
|
246
|
-
throw new TypeError("Failed-start retry observation requires a ServerEnvironment instance.");
|
|
247
|
-
}
|
|
248
|
-
return attachments.failedStartRetryPending(error);
|
|
733
|
+
whenDeliveryIdle(environment) {
|
|
734
|
+
return deliveryIdlers.get(environment)?.() ?? Promise.resolve();
|
|
249
735
|
},
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const attachments = environmentAttachments.get(environment);
|
|
253
|
-
if (attachments === undefined) {
|
|
254
|
-
return Promise.reject(new TypeError("Rollback retry requires a ServerEnvironment instance."));
|
|
255
|
-
}
|
|
256
|
-
return attachments.retryFailedStart();
|
|
736
|
+
notifyDelivery(environment, shard) {
|
|
737
|
+
deliveryNotifiers.get(environment)?.(shard);
|
|
257
738
|
},
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
return attachments.detach(attachment);
|
|
739
|
+
awaitSetup(environment) {
|
|
740
|
+
this.requireAvailable(environment);
|
|
741
|
+
return (environmentSetups.get(environment) ??
|
|
742
|
+
Promise.reject(new Error("Await ServerEnvironment.completeSetup() before building a Bounded Context."))).then(() => {
|
|
743
|
+
this.requireAvailable(environment);
|
|
744
|
+
});
|
|
265
745
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (attachments === undefined) {
|
|
270
|
-
return Promise.reject(new TypeError("Detach retry requires a ServerEnvironment instance."));
|
|
271
|
-
}
|
|
272
|
-
return attachments.retryDetach(attachment);
|
|
746
|
+
beginContextBuild(environment) {
|
|
747
|
+
this.requireAvailable(environment);
|
|
748
|
+
return EnvironmentContextRegistrations.forEnvironment(environment).beginBuild();
|
|
273
749
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
750
|
+
/**
|
|
751
|
+
* Starts automatic cleanup after a required startup failure.
|
|
752
|
+
*
|
|
753
|
+
* @param environment Failed environment.
|
|
754
|
+
* @param cause Original required-phase error.
|
|
755
|
+
*/
|
|
756
|
+
failStartup(environment, cause) {
|
|
757
|
+
startupControls.get(environment)?.fail(cause);
|
|
280
758
|
},
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return attachments.endpointSafe(attachment);
|
|
759
|
+
awaitStartupFailure(environment) {
|
|
760
|
+
const control = startupControls.get(environment);
|
|
761
|
+
if (control === undefined)
|
|
762
|
+
return Promise.reject(new Error("Unknown ServerEnvironment."));
|
|
763
|
+
return control.awaitFailure();
|
|
287
764
|
},
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const attachments = environmentAttachments.get(environment);
|
|
291
|
-
if (attachments === undefined) {
|
|
292
|
-
return Promise.reject(new TypeError("Delivery stop requires a ServerEnvironment instance."));
|
|
293
|
-
}
|
|
294
|
-
return attachments.stopDelivery();
|
|
765
|
+
hasStartupFailure(environment) {
|
|
766
|
+
return startupControls.get(environment)?.failed() ?? false;
|
|
295
767
|
},
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
768
|
+
/**
|
|
769
|
+
* Adds an independent partial-context rollback failure to the fatal result.
|
|
770
|
+
*
|
|
771
|
+
* @param environment Failed environment.
|
|
772
|
+
* @param error Local rollback failure.
|
|
773
|
+
*/
|
|
774
|
+
recordStartupCleanup(environment, error) {
|
|
775
|
+
startupControls.get(environment)?.recordCleanup(error);
|
|
303
776
|
},
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
environmentAttachments.set(environment, new EnvironmentAttachments({
|
|
312
|
-
logger: ServerEnvironmentValues.loggerFor(environment),
|
|
313
|
-
createWorker,
|
|
314
|
-
}));
|
|
777
|
+
requireAvailable(environment) {
|
|
778
|
+
startupControls.get(environment)?.available();
|
|
779
|
+
},
|
|
780
|
+
addContext: (environment, context) => {
|
|
781
|
+
serverEnvironmentAccess.requireAvailable(environment);
|
|
782
|
+
EnvironmentContextRegistrations.forEnvironment(environment).add(context);
|
|
315
783
|
},
|
|
784
|
+
completeContext: async (environment, context) => {
|
|
785
|
+
serverEnvironmentAccess.requireAvailable(environment);
|
|
786
|
+
EnvironmentContextRegistrations.forEnvironment(environment).complete(context);
|
|
787
|
+
const endpoints = boundedContextAccess.delivery(context).endpoints();
|
|
788
|
+
if (endpoints.length > 0)
|
|
789
|
+
await supervisorStarters.get(environment)?.();
|
|
790
|
+
serverEnvironmentAccess.requireAvailable(environment);
|
|
791
|
+
for (const endpoint of endpoints)
|
|
792
|
+
deliveryNotifiers.get(environment)?.(endpoint.shard);
|
|
793
|
+
},
|
|
794
|
+
reserveState: (environment, context, typeUrl) => EnvironmentContextRegistrations.forEnvironment(environment).reserve(context, typeUrl),
|
|
795
|
+
stopRecipients: (environment, context) => {
|
|
796
|
+
EnvironmentContextRegistrations.forEnvironment(environment).stopRecipients(context);
|
|
797
|
+
},
|
|
798
|
+
removeContext: (environment, context) => {
|
|
799
|
+
EnvironmentContextRegistrations.forEnvironment(environment).remove(context);
|
|
800
|
+
},
|
|
801
|
+
contextsServing: (environment, typeUrl) => EnvironmentContextRegistrations.forEnvironment(environment).serving(typeUrl),
|
|
802
|
+
openContexts: (environment) => EnvironmentContextRegistrations.forEnvironment(environment).openContexts(),
|
|
316
803
|
loggerFor(environment) {
|
|
317
804
|
return ServerEnvironmentValues.loggerFor(environment);
|
|
318
805
|
},
|
|
@@ -337,12 +824,24 @@ export const serverEnvironmentAccess = Object.freeze({
|
|
|
337
824
|
* @internal Groups private facility-assembly operations for the environment singleton.
|
|
338
825
|
*/
|
|
339
826
|
const ServerEnvironmentValues = Object.freeze({
|
|
827
|
+
/**
|
|
828
|
+
* Returns the installed environment logger.
|
|
829
|
+
*
|
|
830
|
+
* @param environment Resolved server environment.
|
|
831
|
+
* @returns Its logger.
|
|
832
|
+
*/
|
|
340
833
|
loggerFor(environment) {
|
|
341
834
|
const logger = environmentLoggers.get(environment);
|
|
342
835
|
if (logger === undefined)
|
|
343
836
|
throw new TypeError("ServerEnvironment logger is not available.");
|
|
344
837
|
return logger;
|
|
345
838
|
},
|
|
839
|
+
/**
|
|
840
|
+
* Gets an openable delivery facility when configured.
|
|
841
|
+
*
|
|
842
|
+
* @param delivery Configured delivery facility.
|
|
843
|
+
* @returns Openable delivery or undefined.
|
|
844
|
+
*/
|
|
346
845
|
openableDelivery(delivery) {
|
|
347
846
|
if (delivery === undefined)
|
|
348
847
|
return undefined;
|
|
@@ -356,6 +855,12 @@ const ServerEnvironmentValues = Object.freeze({
|
|
|
356
855
|
}
|
|
357
856
|
return candidate;
|
|
358
857
|
},
|
|
858
|
+
/**
|
|
859
|
+
* Maps delivery to its environment ports.
|
|
860
|
+
*
|
|
861
|
+
* @param delivery Configured delivery facility.
|
|
862
|
+
* @returns Ports when delivery is openable.
|
|
863
|
+
*/
|
|
359
864
|
ports(delivery) {
|
|
360
865
|
const remote = ServerEnvironmentValues.openableDelivery(delivery);
|
|
361
866
|
if (remote === undefined)
|
|
@@ -366,14 +871,27 @@ const ServerEnvironmentValues = Object.freeze({
|
|
|
366
871
|
...(remote.source === undefined ? {} : { source: remote.source }),
|
|
367
872
|
};
|
|
368
873
|
},
|
|
369
|
-
|
|
874
|
+
/**
|
|
875
|
+
* Lists facilities closed by the environment.
|
|
876
|
+
*
|
|
877
|
+
* @param options Resolved facilities.
|
|
878
|
+
* @param deliveryCloseGroup Closes the shared Delivery before other facilities.
|
|
879
|
+
* @returns Facilities in shutdown order.
|
|
880
|
+
*/
|
|
881
|
+
facilitiesToClose(options, deliveryCloseGroup) {
|
|
370
882
|
return Object.freeze([
|
|
371
|
-
|
|
883
|
+
deliveryCloseGroup,
|
|
372
884
|
options.integrationChannelFactory,
|
|
373
885
|
...(options.tracerFactory === undefined ? [] : [options.tracerFactory]),
|
|
374
886
|
options.storageFactory,
|
|
375
887
|
]);
|
|
376
888
|
},
|
|
889
|
+
/**
|
|
890
|
+
* Creates a default logger when none was supplied.
|
|
891
|
+
*
|
|
892
|
+
* @param logger Optional application logger.
|
|
893
|
+
* @returns Logger child for this environment.
|
|
894
|
+
*/
|
|
377
895
|
logger(logger) {
|
|
378
896
|
return (logger ??
|
|
379
897
|
new LogLayer({
|
|
@@ -388,28 +906,38 @@ const ServerEnvironmentValues = Object.freeze({
|
|
|
388
906
|
}),
|
|
389
907
|
})).child();
|
|
390
908
|
},
|
|
909
|
+
/**
|
|
910
|
+
* Resolves configured facilities with local defaults where supported.
|
|
911
|
+
*
|
|
912
|
+
* @param type Selected environment type.
|
|
913
|
+
* @param settings Configured facilities.
|
|
914
|
+
* @returns Complete resolved facilities.
|
|
915
|
+
*/
|
|
391
916
|
resolveFacilities(type, settings) {
|
|
392
917
|
if (type === EnvironmentType.Production) {
|
|
393
|
-
if (settings.storageFactory === undefined) {
|
|
394
|
-
throw new Error("Production ServerEnvironment requires storageFactory.");
|
|
395
|
-
}
|
|
396
918
|
if (settings.typeRegistry === undefined) {
|
|
397
919
|
throw new Error("Production ServerEnvironment requires typeRegistry.");
|
|
398
920
|
}
|
|
399
921
|
return {
|
|
400
|
-
storageFactory: settings.storageFactory,
|
|
922
|
+
storageFactory: settings.storageFactory ?? new InMemoryStorageFactory(),
|
|
923
|
+
tenantIndex: settings.tenantIndex,
|
|
924
|
+
ai: settings.ai,
|
|
401
925
|
integrationChannelFactory: settings.integrationChannelFactory ?? new InMemoryTransportFactory(),
|
|
402
926
|
typeRegistry: settings.typeRegistry,
|
|
403
927
|
delivery: settings.delivery,
|
|
928
|
+
deliveryStrategy: settings.deliveryStrategy,
|
|
404
929
|
tracerFactory: settings.tracerFactory,
|
|
405
930
|
logger: ServerEnvironmentValues.logger(settings.logger),
|
|
406
931
|
};
|
|
407
932
|
}
|
|
408
933
|
return {
|
|
409
934
|
storageFactory: settings.storageFactory ?? new InMemoryStorageFactory(),
|
|
935
|
+
tenantIndex: settings.tenantIndex,
|
|
936
|
+
ai: settings.ai,
|
|
410
937
|
integrationChannelFactory: settings.integrationChannelFactory ?? new InMemoryTransportFactory(),
|
|
411
938
|
typeRegistry: settings.typeRegistry ?? spineCoreRegistry,
|
|
412
939
|
delivery: settings.delivery,
|
|
940
|
+
deliveryStrategy: settings.deliveryStrategy,
|
|
413
941
|
tracerFactory: settings.tracerFactory,
|
|
414
942
|
logger: ServerEnvironmentValues.logger(settings.logger),
|
|
415
943
|
};
|