@spine-event-engine/server 2.0.0-snapshot.26 → 2.0.0-snapshot.29
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 +61 -8
- package/REFERENCE.md +267 -45
- package/dist/agent/agent-live-ai-runtime.d.ts +1 -1
- package/dist/agent/agent-live-ai-runtime.js +1 -1
- package/dist/agent/agent-live-budget.d.ts +1 -1
- package/dist/agent/agent-live-budget.js +1 -1
- package/dist/agent/agent-live-read-runtime.d.ts +1 -1
- package/dist/agent/agent-live-read-runtime.js +1 -1
- package/dist/browser/browser-admission.d.ts +47 -0
- package/dist/browser/browser-admission.d.ts.map +1 -0
- package/dist/browser/browser-admission.js +200 -0
- package/dist/browser/browser-admission.js.map +1 -0
- package/dist/browser/browser-server.d.ts +14 -7
- package/dist/browser/browser-server.d.ts.map +1 -1
- package/dist/browser/browser-server.js +462 -129
- package/dist/browser/browser-server.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/options.d.ts +48 -2
- package/dist/browser/options.d.ts.map +1 -1
- package/dist/bus/command-bus.d.ts +6 -1
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +6 -1
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/event-bus.d.ts +6 -1
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +6 -1
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +30 -28
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +137 -83
- package/dist/context/bounded-context.js.map +1 -1
- package/dist/context/entity-inbox.d.ts +12 -17
- package/dist/context/entity-inbox.d.ts.map +1 -1
- package/dist/context/entity-inbox.js +106 -120
- package/dist/context/entity-inbox.js.map +1 -1
- package/dist/context/local-inbox-handoff.d.ts +13 -13
- package/dist/context/local-inbox-handoff.d.ts.map +1 -1
- package/dist/context/local-inbox-handoff.js +60 -32
- package/dist/context/local-inbox-handoff.js.map +1 -1
- package/dist/context/persistent-tenant-index.d.ts +81 -0
- package/dist/context/persistent-tenant-index.d.ts.map +1 -0
- package/dist/context/persistent-tenant-index.js +270 -0
- package/dist/context/persistent-tenant-index.js.map +1 -0
- package/dist/context/projection-handoff.d.ts +13 -11
- package/dist/context/projection-handoff.d.ts.map +1 -1
- package/dist/context/projection-handoff.js +77 -34
- package/dist/context/projection-handoff.js.map +1 -1
- package/dist/context/tenant-index.d.ts +7 -6
- package/dist/context/tenant-index.d.ts.map +1 -1
- package/dist/context/tenant-index.js +2 -188
- package/dist/context/tenant-index.js.map +1 -1
- package/dist/context/tenant-unknown-error.d.ts +2 -2
- package/dist/context/tenant-unknown-error.d.ts.map +1 -1
- package/dist/context/tenant-unknown-error.js +4 -1
- package/dist/context/tenant-unknown-error.js.map +1 -1
- package/dist/delivery/delivery-builder.d.ts +8 -7
- package/dist/delivery/delivery-builder.d.ts.map +1 -1
- package/dist/delivery/delivery-builder.js +2 -2
- package/dist/delivery/delivery-builder.js.map +1 -1
- package/dist/delivery/delivery-ports.d.ts +2 -2
- package/dist/delivery/delivery-supervisor.d.ts.map +1 -1
- package/dist/delivery/delivery-supervisor.js +12 -3
- package/dist/delivery/delivery-supervisor.js.map +1 -1
- package/dist/delivery/delivery.d.ts +4 -3
- package/dist/delivery/delivery.d.ts.map +1 -1
- package/dist/delivery/delivery.js +87 -38
- package/dist/delivery/delivery.js.map +1 -1
- package/dist/delivery/inbox-retention.d.ts +7 -0
- package/dist/delivery/inbox-retention.d.ts.map +1 -0
- package/dist/delivery/inbox-retention.js +29 -0
- package/dist/delivery/inbox-retention.js.map +1 -0
- package/dist/delivery/inbox-storage.d.ts +2 -2
- package/dist/delivery/inbox-storage.d.ts.map +1 -1
- package/dist/delivery/inbox-storage.js +32 -8
- package/dist/delivery/inbox-storage.js.map +1 -1
- package/dist/delivery/inbox.d.ts +5 -9
- package/dist/delivery/inbox.d.ts.map +1 -1
- package/dist/delivery/inbox.js +4 -6
- package/dist/delivery/inbox.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +3 -3
- package/dist/delivery/reception-observation.js +2 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/repository/command-routing.d.ts +1 -1
- package/dist/repository/command-routing.js +1 -1
- package/dist/repository/event-routing.d.ts +1 -1
- package/dist/repository/event-routing.js +1 -1
- package/dist/repository/repository.d.ts +25 -33
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +245 -230
- package/dist/repository/repository.js.map +1 -1
- package/dist/repository/state-update-routing.d.ts +1 -1
- package/dist/repository/state-update-routing.js +1 -1
- package/dist/runtime/signal-publisher.d.ts +23 -8
- package/dist/runtime/signal-publisher.d.ts.map +1 -1
- package/dist/runtime/signal-publisher.js +11 -8
- package/dist/runtime/signal-publisher.js.map +1 -1
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +220 -54
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/server-environment.d.ts +6 -5
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +13 -9
- package/dist/server/server-environment.js.map +1 -1
- package/dist/server/server.js +1 -0
- package/dist/server/server.js.map +1 -1
- package/dist/services/app-query-execution.js +2 -2
- package/dist/services/app-query-execution.js.map +1 -1
- package/dist/services/native-operation.d.ts +34 -0
- package/dist/services/native-operation.d.ts.map +1 -0
- package/dist/services/native-operation.js +77 -0
- package/dist/services/native-operation.js.map +1 -0
- package/dist/services/spine-services.d.ts +6 -0
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +128 -30
- package/dist/services/spine-services.js.map +1 -1
- package/dist/services/trusted-reader-ingress.d.ts +23 -5
- package/dist/services/trusted-reader-ingress.d.ts.map +1 -1
- package/dist/services/trusted-reader-ingress.js +18 -5
- package/dist/services/trusted-reader-ingress.js.map +1 -1
- package/dist/spi/delivery.d.ts +1 -0
- package/dist/spi/delivery.d.ts.map +1 -1
- package/dist/spi/delivery.js +1 -0
- package/dist/spi/delivery.js.map +1 -1
- package/dist/stand/stand.d.ts +1 -1
- package/dist/stand/stand.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -14
|
@@ -36,7 +36,7 @@ import { snapshotTrustedReader, } from "@spine-event-engine/core/spi/trusted-rea
|
|
|
36
36
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
37
37
|
import { EventDispatcherOriginSchemas } from "../bus/event-dispatcher-origin-schemas.js";
|
|
38
38
|
import { receptionObservation } from "../delivery/reception-observation.js";
|
|
39
|
-
import { commitFenced, withDeliveryCommitFence } from "./commit-fence.js";
|
|
39
|
+
import { commitFenced, withDeliveryCommitFence, withoutDeliveryCommitFence, } from "./commit-fence.js";
|
|
40
40
|
import { AgentHistoryReads, } from "../agent/agent-history.js";
|
|
41
41
|
import { AgentLiveAiRuntime } from "../agent/agent-live-ai-runtime.js";
|
|
42
42
|
import { AgentLiveAudit } from "../agent/agent-live-audit.js";
|
|
@@ -48,7 +48,6 @@ import { AgentDeliveryScope } from "../delivery/agent-delivery-scope.js";
|
|
|
48
48
|
import { AgentModelSelection } from "../agent/agent-model-selection.js";
|
|
49
49
|
import { AgentAiBindings } from "../agent/agent-ai-binding.js";
|
|
50
50
|
import { InboxTargets } from "../delivery/inbox.js";
|
|
51
|
-
import { ShardIndex } from "../delivery/shard-index.js";
|
|
52
51
|
import { Aggregate, Agent, ProcessManager, Projection, entityHistoryAccess, processManagerQueryAccess, transactionalEntityAccess, } from "../entity/entity.js";
|
|
53
52
|
import { describeEntityMetadata, attachEntitySchema, } from "../entity/entity-metadata.js";
|
|
54
53
|
import { EntityIds, EntityRecords, entityStorageDescriptor, } from "../entity/entity-storage-descriptor.js";
|
|
@@ -690,19 +689,19 @@ export const repositoryAccess = Object.freeze({
|
|
|
690
689
|
});
|
|
691
690
|
},
|
|
692
691
|
/**
|
|
693
|
-
* Looks up the Aggregate or Process Manager
|
|
692
|
+
* Looks up the Aggregate or Process Manager Inbox delivery target.
|
|
694
693
|
*
|
|
695
694
|
* @param repository Repository to inspect.
|
|
696
|
-
* @returns Its
|
|
695
|
+
* @returns Its delivery target, when registered.
|
|
697
696
|
*/
|
|
698
697
|
entityInboxTarget(repository) {
|
|
699
698
|
return repositoryEntityInboxTargets.get(repository);
|
|
700
699
|
},
|
|
701
700
|
/**
|
|
702
|
-
* Looks up the Projection
|
|
701
|
+
* Looks up the Projection Inbox delivery target.
|
|
703
702
|
*
|
|
704
703
|
* @param repository Repository to inspect.
|
|
705
|
-
* @returns Its
|
|
704
|
+
* @returns Its delivery target, when registered.
|
|
706
705
|
*/
|
|
707
706
|
projectionInboxTarget(repository) {
|
|
708
707
|
return repositoryProjectionInboxTargets.get(repository);
|
|
@@ -754,7 +753,6 @@ export const repositoryAccess = Object.freeze({
|
|
|
754
753
|
RepositoryStorage.closeRuntimeIdle(repository);
|
|
755
754
|
},
|
|
756
755
|
});
|
|
757
|
-
const inboxDedupMs = 30_000;
|
|
758
756
|
/**
|
|
759
757
|
* Creates a repository Entity from framework options and an optional application constructor callback.
|
|
760
758
|
*/
|
|
@@ -1067,7 +1065,8 @@ class AggregateExecutionSupport {
|
|
|
1067
1065
|
return Object.freeze([produced]);
|
|
1068
1066
|
}
|
|
1069
1067
|
/**
|
|
1070
|
-
*
|
|
1068
|
+
* Writes Aggregate state, diagnostic history, and produced Events through the configured
|
|
1069
|
+
* commit storage. Atomicity depends on that storage implementation.
|
|
1071
1070
|
*
|
|
1072
1071
|
* @param loaded Aggregate and commit storage to update.
|
|
1073
1072
|
* @param entityId Aggregate identifier.
|
|
@@ -1141,7 +1140,7 @@ class AggregateExecutionSupport {
|
|
|
1141
1140
|
await loaded.events.append(clone(EventSchema, event));
|
|
1142
1141
|
}
|
|
1143
1142
|
/**
|
|
1144
|
-
* Completes persistence
|
|
1143
|
+
* Completes persistence and one handoff attempt for each produced Event.
|
|
1145
1144
|
*
|
|
1146
1145
|
* @param loaded Aggregate and commit storage to update.
|
|
1147
1146
|
* @param entityId Aggregate identifier.
|
|
@@ -1149,14 +1148,14 @@ class AggregateExecutionSupport {
|
|
|
1149
1148
|
* @param dispatch Publishes one committed Event.
|
|
1150
1149
|
* @param onPersisted Runs after durable persistence.
|
|
1151
1150
|
* @param diagnostics Events to retain in diagnostic history with this commit.
|
|
1152
|
-
* @returns
|
|
1151
|
+
* @returns Completion after the Event handoff attempts settle.
|
|
1153
1152
|
*/
|
|
1154
1153
|
async persistAggregateAndDispatch(loaded, entityId, events, dispatch, onPersisted, diagnostics = events) {
|
|
1155
1154
|
const committed = await this.persistAggregateUpdate(loaded, entityId, events, diagnostics);
|
|
1156
1155
|
if (!committed)
|
|
1157
|
-
return
|
|
1156
|
+
return;
|
|
1158
1157
|
await onPersisted();
|
|
1159
|
-
|
|
1158
|
+
await withoutDeliveryCommitFence(async () => {
|
|
1160
1159
|
await Promise.all(events.map(async (event) => {
|
|
1161
1160
|
try {
|
|
1162
1161
|
await dispatch(event);
|
|
@@ -1165,7 +1164,7 @@ class AggregateExecutionSupport {
|
|
|
1165
1164
|
this.#runtime.publisher.reportFailure("event", event, error);
|
|
1166
1165
|
}
|
|
1167
1166
|
}));
|
|
1168
|
-
};
|
|
1167
|
+
});
|
|
1169
1168
|
}
|
|
1170
1169
|
/**
|
|
1171
1170
|
* Restores an Aggregate or creates one with initial state and version zero.
|
|
@@ -1225,27 +1224,27 @@ class AggregateCommandExecution {
|
|
|
1225
1224
|
/**
|
|
1226
1225
|
* Validates the Command and executes its assignee when registered.
|
|
1227
1226
|
*
|
|
1228
|
-
* @param
|
|
1229
|
-
* @returns
|
|
1227
|
+
* @param deliveredRoute Accepted route from durable inbox delivery, when present.
|
|
1228
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
1230
1229
|
*/
|
|
1231
|
-
async run(
|
|
1230
|
+
async run(deliveredRoute) {
|
|
1232
1231
|
void RepositorySignals.requireCommandId(this.#command);
|
|
1233
|
-
const intake = await this.#readIntake(
|
|
1232
|
+
const intake = await this.#readIntake(deliveredRoute);
|
|
1234
1233
|
if (intake === undefined)
|
|
1235
1234
|
return undefined;
|
|
1236
|
-
|
|
1235
|
+
await this.#runAssignee(intake);
|
|
1237
1236
|
}
|
|
1238
1237
|
/**
|
|
1239
1238
|
* Decodes the Command and selects its registered assignee.
|
|
1240
1239
|
*
|
|
1241
|
-
* @param
|
|
1240
|
+
* @param deliveredRoute Accepted inbox route, when present.
|
|
1242
1241
|
* @returns Decoded message and route with an assignee, or `undefined`.
|
|
1243
1242
|
*/
|
|
1244
|
-
async #readIntake(
|
|
1243
|
+
async #readIntake(deliveredRoute) {
|
|
1245
1244
|
const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
|
|
1246
1245
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
1247
1246
|
const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
|
|
1248
|
-
const route =
|
|
1247
|
+
const route = deliveredRoute ?? (await this.#repository.routeCommand(this.#command));
|
|
1249
1248
|
const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
1250
1249
|
return assignee === undefined ? undefined : Object.freeze({ message, route, assignee });
|
|
1251
1250
|
}
|
|
@@ -1253,7 +1252,7 @@ class AggregateCommandExecution {
|
|
|
1253
1252
|
* Invokes the assignee, persists its Events, or publishes a declared rejection.
|
|
1254
1253
|
*
|
|
1255
1254
|
* @param intake Decoded Command, target route, and assignee.
|
|
1256
|
-
* @returns
|
|
1255
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
1257
1256
|
*/
|
|
1258
1257
|
async #runAssignee(intake) {
|
|
1259
1258
|
const loaded = await this.#support.loadAggregate(intake.route.entityId);
|
|
@@ -1263,23 +1262,24 @@ class AggregateCommandExecution {
|
|
|
1263
1262
|
produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler, intake.message, EntityInvocation.commandHandlerContext(this.#command), this.#command.context?.actorContext);
|
|
1264
1263
|
}
|
|
1265
1264
|
catch (error) {
|
|
1266
|
-
|
|
1265
|
+
await this.#postAssigneeRejection(intake, error);
|
|
1266
|
+
return undefined;
|
|
1267
1267
|
}
|
|
1268
1268
|
const events = this.#bindProducedEvents(this.#support.normalizeProducedSignals(produced), intake.route.entityId, RepositoryEntities.priorVersion(loaded.current));
|
|
1269
|
-
|
|
1269
|
+
await this.#persistAssigneeResult(loaded, intake.route.entityId, events);
|
|
1270
1270
|
}
|
|
1271
1271
|
/**
|
|
1272
1272
|
* Publishes a declared rejection from the invoked Aggregate assignee.
|
|
1273
1273
|
*
|
|
1274
1274
|
* @param intake Assignee and target route for the rejected Command.
|
|
1275
1275
|
* @param error Handler failure to classify and publish.
|
|
1276
|
-
* @returns
|
|
1276
|
+
* @returns Completion after the rejection Event handoff attempt.
|
|
1277
1277
|
*/
|
|
1278
|
-
#postAssigneeRejection(intake, error) {
|
|
1278
|
+
async #postAssigneeRejection(intake, error) {
|
|
1279
1279
|
if (!RejectionThrowable.is(error))
|
|
1280
1280
|
throw error;
|
|
1281
1281
|
RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
|
|
1282
|
-
|
|
1282
|
+
await RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
|
|
1283
1283
|
}
|
|
1284
1284
|
/**
|
|
1285
1285
|
* Publishes a best-effort Command dispatch diagnostic.
|
|
@@ -1295,11 +1295,11 @@ class AggregateCommandExecution {
|
|
|
1295
1295
|
* @param loaded Aggregate and commit storage.
|
|
1296
1296
|
* @param entityId Target Aggregate identifier.
|
|
1297
1297
|
* @param events Bound Events from the assignee.
|
|
1298
|
-
* @returns
|
|
1298
|
+
* @returns Completion after committed Event handoff attempts.
|
|
1299
1299
|
*/
|
|
1300
|
-
#persistAssigneeResult(loaded, entityId, events) {
|
|
1300
|
+
async #persistAssigneeResult(loaded, entityId, events) {
|
|
1301
1301
|
const committedVersion = loaded.version + 1n;
|
|
1302
|
-
|
|
1302
|
+
await this.#support.persistAggregateAndDispatch(loaded, entityId, events, (event) => this.#runtime.publisher.publishCommittedEvent(event), () => {
|
|
1303
1303
|
if (!RepositoryEntities.repositoryChanged(loaded.entity))
|
|
1304
1304
|
return;
|
|
1305
1305
|
EntityStateChangePublisher.command(this.#runtime, this.#repository, this.#command, entityId, loaded.oldState, loaded.current === undefined
|
|
@@ -1489,10 +1489,9 @@ class AggregateEventExecution {
|
|
|
1489
1489
|
await this.#support.appendDiagnosticEvent(loaded, entityId, source);
|
|
1490
1490
|
return;
|
|
1491
1491
|
}
|
|
1492
|
-
|
|
1492
|
+
await this.#support.persistAggregateAndDispatch(loaded, entityId, produced, (event) => this.#runtime.publisher.publishReactorEvent(event), () => {
|
|
1493
1493
|
this.#publishStateChange(loaded, entityId);
|
|
1494
1494
|
}, [source, ...produced]);
|
|
1495
|
-
void dispatch();
|
|
1496
1495
|
}
|
|
1497
1496
|
/**
|
|
1498
1497
|
* Publishes an Entity state-change System Event when the Aggregate changed.
|
|
@@ -1675,7 +1674,7 @@ class ProjectionEventExecution {
|
|
|
1675
1674
|
* Delivers an accepted Event for one Projection target.
|
|
1676
1675
|
*
|
|
1677
1676
|
* @param entityId Target Projection identifier.
|
|
1678
|
-
* @param acceptedRoute Route accepted before
|
|
1677
|
+
* @param acceptedRoute Route accepted before deliver.
|
|
1679
1678
|
* @returns Completion after target delivery.
|
|
1680
1679
|
*/
|
|
1681
1680
|
async runTarget(entityId, acceptedRoute) {
|
|
@@ -2911,27 +2910,29 @@ class ProcessManagerCommandExecution {
|
|
|
2911
2910
|
/**
|
|
2912
2911
|
* Invokes the registered Command handler and commits its results.
|
|
2913
2912
|
*
|
|
2914
|
-
* @param
|
|
2915
|
-
* @returns
|
|
2913
|
+
* @param deliveredRoute Accepted route from durable inbox delivery, when present.
|
|
2914
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
2916
2915
|
*/
|
|
2917
|
-
async run(
|
|
2916
|
+
async run(deliveredRoute) {
|
|
2918
2917
|
RepositorySignals.requireCommandId(this.#command);
|
|
2919
|
-
const intake = await this.#readIntake(
|
|
2918
|
+
const intake = await this.#readIntake(deliveredRoute);
|
|
2920
2919
|
if (intake === undefined)
|
|
2921
2920
|
return undefined;
|
|
2922
2921
|
const tenantOptions = RepositoryTenants.commandStandOptions(this.#runtime.context, this.#command);
|
|
2923
2922
|
const loaded = await this.#support.load(intake.route.entityId, tenantOptions);
|
|
2924
2923
|
await this.#publishDispatch(loaded, intake.route.entityId);
|
|
2924
|
+
let produced;
|
|
2925
2925
|
try {
|
|
2926
|
-
|
|
2927
|
-
return await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
|
|
2926
|
+
produced = await this.#invoke(loaded.entity, intake.assignee, intake.message, tenantOptions.tenantId);
|
|
2928
2927
|
}
|
|
2929
2928
|
catch (error) {
|
|
2930
2929
|
if (!RejectionThrowable.is(error))
|
|
2931
2930
|
throw error;
|
|
2932
2931
|
RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
|
|
2933
|
-
|
|
2932
|
+
await RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
|
|
2933
|
+
return undefined;
|
|
2934
2934
|
}
|
|
2935
|
+
await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
|
|
2935
2936
|
}
|
|
2936
2937
|
/**
|
|
2937
2938
|
* Persists required Agent dispatch history before the handler runs.
|
|
@@ -2961,14 +2962,14 @@ class ProcessManagerCommandExecution {
|
|
|
2961
2962
|
/**
|
|
2962
2963
|
* Decodes a registered Command and finds its assignee.
|
|
2963
2964
|
*
|
|
2964
|
-
* @param
|
|
2965
|
+
* @param deliveredRoute Accepted inbox route, when present.
|
|
2965
2966
|
* @returns Decoded Command, route, and assignee, or `undefined`.
|
|
2966
2967
|
*/
|
|
2967
|
-
async #readIntake(
|
|
2968
|
+
async #readIntake(deliveredRoute) {
|
|
2968
2969
|
const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
|
|
2969
2970
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
2970
2971
|
const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
|
|
2971
|
-
const route =
|
|
2972
|
+
const route = deliveredRoute ?? (await this.#repository.routeCommand(this.#command));
|
|
2972
2973
|
const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
2973
2974
|
return assignee === undefined ? undefined : Object.freeze({ assignee, message, route });
|
|
2974
2975
|
}
|
|
@@ -2979,7 +2980,7 @@ class ProcessManagerCommandExecution {
|
|
|
2979
2980
|
* @param tenantOptions Stand tenant selection.
|
|
2980
2981
|
* @param intake Assignee and target route.
|
|
2981
2982
|
* @param producedSignals Validated handler results.
|
|
2982
|
-
* @returns
|
|
2983
|
+
* @returns Completion after produced-signal handoff attempts.
|
|
2983
2984
|
*/
|
|
2984
2985
|
async #commitAndPublish(loaded, tenantOptions, intake, producedSignals) {
|
|
2985
2986
|
const commands = this.#commandOutputs(intake.assignee, producedSignals);
|
|
@@ -2988,12 +2989,10 @@ class ProcessManagerCommandExecution {
|
|
|
2988
2989
|
if (!committed)
|
|
2989
2990
|
return undefined;
|
|
2990
2991
|
this.#publishChangedState(loaded, intake.route.entityId);
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
await this.#postCommands(commands, this.#command);
|
|
2996
|
-
};
|
|
2992
|
+
await withoutDeliveryCommitFence(async () => {
|
|
2993
|
+
await this.#postEvents(events);
|
|
2994
|
+
await this.#postCommands(commands, this.#command);
|
|
2995
|
+
});
|
|
2997
2996
|
}
|
|
2998
2997
|
/**
|
|
2999
2998
|
* Binds substitution results as Commands and requires nonempty output.
|
|
@@ -3156,15 +3155,15 @@ class ProcessManagerCommandExecution {
|
|
|
3156
3155
|
}
|
|
3157
3156
|
}
|
|
3158
3157
|
/**
|
|
3159
|
-
*
|
|
3158
|
+
* Publishes the Process Manager Command's produced Events after its commit.
|
|
3160
3159
|
*
|
|
3161
3160
|
* @param events Bound Events to publish.
|
|
3161
|
+
* @returns Completion after each Event handoff attempt.
|
|
3162
3162
|
*/
|
|
3163
|
-
#postEvents(events) {
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
}
|
|
3163
|
+
async #postEvents(events) {
|
|
3164
|
+
await Promise.all(events.map((event) => {
|
|
3165
|
+
return this.#runtime.publisher.publishReactorEvent(event);
|
|
3166
|
+
}));
|
|
3168
3167
|
}
|
|
3169
3168
|
}
|
|
3170
3169
|
/**
|
|
@@ -3204,16 +3203,16 @@ class ProcessManagerEventExecution {
|
|
|
3204
3203
|
}
|
|
3205
3204
|
this.#validateSourceEventIdForFollowUps(intake);
|
|
3206
3205
|
if (intake.route.entityIds.length === 1) {
|
|
3207
|
-
await InboxHandoff.
|
|
3206
|
+
await InboxHandoff.handoffEntityEvent(this.#repository, this.#runtime, this.#event, intake.route.entityIds[0]);
|
|
3208
3207
|
return;
|
|
3209
3208
|
}
|
|
3210
|
-
await InboxHandoff.
|
|
3209
|
+
await InboxHandoff.handoffEntityEvents(this.#repository, this.#runtime, this.#event, intake.route.entityIds);
|
|
3211
3210
|
}
|
|
3212
3211
|
/**
|
|
3213
3212
|
* Delivers a routed Event for one target under the duplicate-dispatch guard.
|
|
3214
3213
|
*
|
|
3215
3214
|
* @param entityId Target Entity identifier.
|
|
3216
|
-
* @param acceptedRoute Route accepted before
|
|
3215
|
+
* @param acceptedRoute Route accepted before deliver.
|
|
3217
3216
|
* @returns Completion after guarded target execution.
|
|
3218
3217
|
*/
|
|
3219
3218
|
async runTarget(entityId, acceptedRoute) {
|
|
@@ -3267,8 +3266,10 @@ class ProcessManagerEventExecution {
|
|
|
3267
3266
|
if (!committed)
|
|
3268
3267
|
return;
|
|
3269
3268
|
this.#publishChangedState(loaded, entityId);
|
|
3270
|
-
|
|
3271
|
-
|
|
3269
|
+
await withoutDeliveryCommitFence(async () => {
|
|
3270
|
+
await this.#postEvents(events);
|
|
3271
|
+
await this.#postCommands(commands);
|
|
3272
|
+
});
|
|
3272
3273
|
}
|
|
3273
3274
|
/**
|
|
3274
3275
|
* Publishes a state-change System Event when the Entity changed.
|
|
@@ -3446,23 +3447,24 @@ class ProcessManagerEventExecution {
|
|
|
3446
3447
|
* Publishes each produced Command in handler order.
|
|
3447
3448
|
*
|
|
3448
3449
|
* @param commands Bound Commands to publish.
|
|
3450
|
+
* @returns Completion after each Command handoff attempt.
|
|
3449
3451
|
*/
|
|
3450
|
-
#postCommands(commands) {
|
|
3452
|
+
async #postCommands(commands) {
|
|
3451
3453
|
for (const command of commands) {
|
|
3452
3454
|
// spine-log-boundary: server.process_manager_command_follow_up
|
|
3453
|
-
|
|
3455
|
+
await this.#runtime.publisher.publishCommand(command);
|
|
3454
3456
|
}
|
|
3455
3457
|
}
|
|
3456
3458
|
/**
|
|
3457
|
-
*
|
|
3459
|
+
* Publishes the Process Manager Event's produced Events after its commit.
|
|
3458
3460
|
*
|
|
3459
3461
|
* @param events Bound Events to publish.
|
|
3462
|
+
* @returns Completion after each Event handoff attempt.
|
|
3460
3463
|
*/
|
|
3461
|
-
#postEvents(events) {
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
}
|
|
3464
|
+
async #postEvents(events) {
|
|
3465
|
+
await Promise.all(events.map((event) => {
|
|
3466
|
+
return this.#runtime.publisher.publishReactorEvent(event);
|
|
3467
|
+
}));
|
|
3466
3468
|
}
|
|
3467
3469
|
}
|
|
3468
3470
|
/**
|
|
@@ -4132,18 +4134,18 @@ const RepositorySignals = {
|
|
|
4132
4134
|
});
|
|
4133
4135
|
},
|
|
4134
4136
|
/**
|
|
4135
|
-
* Builds a rejection Event and
|
|
4137
|
+
* Builds a rejection Event and awaits one publication attempt.
|
|
4136
4138
|
*
|
|
4137
4139
|
* @param runtime Context signal and publication services.
|
|
4138
4140
|
* @param repository Repository of the rejected Command.
|
|
4139
4141
|
* @param command Command rejected by its handler.
|
|
4140
4142
|
* @param entityId Target Entity identifier.
|
|
4141
4143
|
* @param rejection Declared handler rejection.
|
|
4142
|
-
* @returns
|
|
4144
|
+
* @returns Completion after the rejection handoff attempt settles.
|
|
4143
4145
|
*/
|
|
4144
|
-
postRejectionEvent(runtime, repository, command, entityId, rejection) {
|
|
4146
|
+
async postRejectionEvent(runtime, repository, command, entityId, rejection) {
|
|
4145
4147
|
const event = this.rejectionEvent(runtime, repository, command, entityId, rejection);
|
|
4146
|
-
|
|
4148
|
+
await withoutDeliveryCommitFence(async () => {
|
|
4147
4149
|
try {
|
|
4148
4150
|
// spine-log-boundary: server.repository_rejection_follow_up
|
|
4149
4151
|
await runtime.publisher.publishRejectionEvent(event);
|
|
@@ -4151,7 +4153,7 @@ const RepositorySignals = {
|
|
|
4151
4153
|
catch (error) {
|
|
4152
4154
|
runtime.publisher.reportFailure("event", event, error);
|
|
4153
4155
|
}
|
|
4154
|
-
};
|
|
4156
|
+
});
|
|
4155
4157
|
},
|
|
4156
4158
|
/**
|
|
4157
4159
|
* Creates the original declared rejection envelope without publishing it.
|
|
@@ -4738,13 +4740,13 @@ const RepositoryTenants = {
|
|
|
4738
4740
|
return RepositoryTenants.require(tenantId);
|
|
4739
4741
|
},
|
|
4740
4742
|
/**
|
|
4741
|
-
* Validates an Event tenant before
|
|
4743
|
+
* Validates an Event tenant before Entity Inbox handoff.
|
|
4742
4744
|
*
|
|
4743
4745
|
* @param context Bounded context storage mode.
|
|
4744
|
-
* @param event Event entering
|
|
4746
|
+
* @param event Event entering Entity Inbox delivery.
|
|
4745
4747
|
* @returns Validated tenant, or `undefined` for single-tenant contexts.
|
|
4746
4748
|
*/
|
|
4747
|
-
|
|
4749
|
+
requireEntityEventTenant(context, event) {
|
|
4748
4750
|
if (!context.multitenant) {
|
|
4749
4751
|
return undefined;
|
|
4750
4752
|
}
|
|
@@ -7254,7 +7256,7 @@ const InboxMessages = {
|
|
|
7254
7256
|
}
|
|
7255
7257
|
}
|
|
7256
7258
|
if (entityId === undefined) {
|
|
7257
|
-
throw new Error("Entity Inbox
|
|
7259
|
+
throw new Error("Entity Inbox delivery stored target ID is incompatible with the repository.");
|
|
7258
7260
|
}
|
|
7259
7261
|
return RepositoryRoutes.readRouteId(entityId, idField, "command");
|
|
7260
7262
|
},
|
|
@@ -7266,7 +7268,7 @@ const InboxMessages = {
|
|
|
7266
7268
|
*/
|
|
7267
7269
|
readInboxCommand(message) {
|
|
7268
7270
|
if (message.label !== "HANDLE_COMMAND") {
|
|
7269
|
-
throw new Error(`Entity Inbox
|
|
7271
|
+
throw new Error(`Entity Inbox delivery does not handle "${message.label}" messages.`);
|
|
7270
7272
|
}
|
|
7271
7273
|
const command = message.signal === undefined ? undefined : AnyPacker.tryUnpack(message.signal, CommandSchema);
|
|
7272
7274
|
if (command === undefined) {
|
|
@@ -7275,13 +7277,13 @@ const InboxMessages = {
|
|
|
7275
7277
|
return command;
|
|
7276
7278
|
},
|
|
7277
7279
|
/**
|
|
7278
|
-
* Reads an Event labeled for
|
|
7280
|
+
* Reads an Event labeled for an Entity reaction.
|
|
7279
7281
|
*
|
|
7280
7282
|
* @param message Stored Inbox message to validate and unpack.
|
|
7281
7283
|
* @returns The stored Event envelope.
|
|
7282
7284
|
*/
|
|
7283
|
-
|
|
7284
|
-
return InboxMessages.readStoredEvent(message, "REACT_UPON_EVENT", "Entity Inbox
|
|
7285
|
+
readEntityInboxEvent(message) {
|
|
7286
|
+
return InboxMessages.readStoredEvent(message, "REACT_UPON_EVENT", "Entity Inbox delivery", "Entity Inbox delivery requires a readable stored event.");
|
|
7285
7287
|
},
|
|
7286
7288
|
/**
|
|
7287
7289
|
* Reads an Event labeled for a Projection update.
|
|
@@ -7290,20 +7292,20 @@ const InboxMessages = {
|
|
|
7290
7292
|
* @returns The stored Event envelope.
|
|
7291
7293
|
*/
|
|
7292
7294
|
readProjectionInboxEvent(message) {
|
|
7293
|
-
return InboxMessages.readStoredEvent(message, "UPDATE_SUBSCRIBER", "Projection inbox
|
|
7295
|
+
return InboxMessages.readStoredEvent(message, "UPDATE_SUBSCRIBER", "Projection inbox delivery", "Projection inbox delivery requires a readable stored event.");
|
|
7294
7296
|
},
|
|
7295
7297
|
/**
|
|
7296
7298
|
* Validates an Inbox operation label and decodes its stored Event bytes.
|
|
7297
7299
|
*
|
|
7298
7300
|
* @param message Stored Inbox message to inspect.
|
|
7299
7301
|
* @param expectedLabel Operation label required by the caller.
|
|
7300
|
-
* @param
|
|
7302
|
+
* @param deliverName Human-readable operation name used in label errors.
|
|
7301
7303
|
* @param unreadableMessage Error text used when Event decoding fails.
|
|
7302
7304
|
* @returns The stored Event envelope.
|
|
7303
7305
|
*/
|
|
7304
|
-
readStoredEvent(message, expectedLabel,
|
|
7306
|
+
readStoredEvent(message, expectedLabel, deliverName, unreadableMessage) {
|
|
7305
7307
|
if (message.label !== expectedLabel) {
|
|
7306
|
-
throw new Error(`${
|
|
7308
|
+
throw new Error(`${deliverName} does not handle "${message.label}" messages.`);
|
|
7307
7309
|
}
|
|
7308
7310
|
const signal = message.signal === undefined
|
|
7309
7311
|
? undefined
|
|
@@ -7322,7 +7324,7 @@ Object.freeze(InboxMessages);
|
|
|
7322
7324
|
/**
|
|
7323
7325
|
* Validates and dispatches stored Inbox signals to their recorded Entity targets.
|
|
7324
7326
|
*/
|
|
7325
|
-
const
|
|
7327
|
+
const InboxDelivery = {
|
|
7326
7328
|
/**
|
|
7327
7329
|
* Dispatches a stored Aggregate Command after checking its tenant and payload.
|
|
7328
7330
|
*
|
|
@@ -7330,44 +7332,66 @@ const InboxReplay = {
|
|
|
7330
7332
|
* @param routing Registered handlers and message schemas.
|
|
7331
7333
|
* @param message Stored Inbox message with the recorded target.
|
|
7332
7334
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7333
|
-
* @returns
|
|
7335
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7334
7336
|
*/
|
|
7335
|
-
async
|
|
7337
|
+
async deliverAggregateCommand(repository, routing, message, deliveryTenantId) {
|
|
7336
7338
|
const runtime = repositoryRuntimes.get(repository);
|
|
7337
7339
|
if (runtime === undefined) {
|
|
7338
|
-
throw new Error("Aggregate inbox
|
|
7340
|
+
throw new Error("Aggregate inbox delivery requires a bound repository runtime.");
|
|
7339
7341
|
}
|
|
7340
7342
|
if (message.label !== "HANDLE_COMMAND") {
|
|
7341
|
-
throw new Error(`Aggregate inbox
|
|
7343
|
+
throw new Error(`Aggregate inbox delivery does not handle "${message.label}" messages.`);
|
|
7342
7344
|
}
|
|
7343
7345
|
const command = InboxMessages.readInboxCommand(message);
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
const route =
|
|
7347
|
-
|
|
7346
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, deliveryTenantId, command);
|
|
7347
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7348
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7349
|
+
await new AggregateCommandExecution(repository, routing, runtime, command).run(route);
|
|
7348
7350
|
},
|
|
7349
7351
|
/**
|
|
7350
|
-
*
|
|
7352
|
+
* Delivers a stored Event to the recorded Aggregate target.
|
|
7353
|
+
*
|
|
7354
|
+
* @param repository Aggregate repository receiving the Event.
|
|
7355
|
+
* @param routing Registered Event handlers and message schemas.
|
|
7356
|
+
* @param message Stored Inbox row with the recorded target.
|
|
7357
|
+
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7358
|
+
* @returns Completion after the guarded reactor and outgoing handoff attempts.
|
|
7359
|
+
*/
|
|
7360
|
+
async deliverAggregateEvent(repository, routing, message, deliveryTenantId) {
|
|
7361
|
+
const runtime = repositoryRuntimes.get(repository);
|
|
7362
|
+
if (runtime === undefined) {
|
|
7363
|
+
throw new Error("Aggregate inbox delivery requires a bound repository runtime.");
|
|
7364
|
+
}
|
|
7365
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7366
|
+
InboxDelivery.validateEventTenant(runtime.context, deliveryTenantId, event);
|
|
7367
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Aggregate inbox delivery requires a readable event payload.");
|
|
7368
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Aggregate inbox delivery");
|
|
7369
|
+
const [entityId] = route.entityIds;
|
|
7370
|
+
await DispatchGuards.guardedEntityEventDispatch(repository, runtime, event, entityId, () => new AggregateEventExecution(repository, routing, runtime, event).runTarget(entityId, route));
|
|
7371
|
+
},
|
|
7372
|
+
/**
|
|
7373
|
+
* Routes Process Manager Command or Event deliver by its stored operation label.
|
|
7351
7374
|
*
|
|
7352
7375
|
* @param repository Process Manager repository receiving the signal.
|
|
7353
7376
|
* @param routing Registered handlers and message schemas.
|
|
7354
7377
|
* @param message Stored Inbox message with the recorded target.
|
|
7355
7378
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7356
|
-
* @returns
|
|
7379
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7357
7380
|
*/
|
|
7358
|
-
async
|
|
7381
|
+
async deliverPmInbox(repository, routing, message, deliveryTenantId) {
|
|
7359
7382
|
if (repository.entityFamily === "agent") {
|
|
7360
|
-
await
|
|
7383
|
+
await InboxDelivery.deliverAgentInbox(repository, routing, message, deliveryTenantId);
|
|
7361
7384
|
return undefined;
|
|
7362
7385
|
}
|
|
7363
7386
|
if (message.label === "HANDLE_COMMAND") {
|
|
7364
|
-
|
|
7387
|
+
await InboxDelivery.deliverProcessManagerCommand(repository, routing, message, deliveryTenantId);
|
|
7388
|
+
return;
|
|
7365
7389
|
}
|
|
7366
7390
|
if (message.label === "REACT_UPON_EVENT") {
|
|
7367
|
-
await
|
|
7391
|
+
await InboxDelivery.deliverProcessManagerEvent(repository, routing, message, deliveryTenantId);
|
|
7368
7392
|
return undefined;
|
|
7369
7393
|
}
|
|
7370
|
-
throw new Error(`Entity Inbox
|
|
7394
|
+
throw new Error(`Entity Inbox delivery does not handle "${message.label}" messages.`);
|
|
7371
7395
|
},
|
|
7372
7396
|
/**
|
|
7373
7397
|
* Persists selected Agent work before the normal Inbox acknowledges delivery.
|
|
@@ -7378,17 +7402,17 @@ const InboxReplay = {
|
|
|
7378
7402
|
* @param deliveryTenantId Tenant selected by delivery.
|
|
7379
7403
|
* @returns When the fenced repository operation completes.
|
|
7380
7404
|
*/
|
|
7381
|
-
async
|
|
7405
|
+
async deliverAgentInbox(repository, routing, message, deliveryTenantId) {
|
|
7382
7406
|
const runtime = repositoryRuntimes.get(repository);
|
|
7383
7407
|
if (runtime === undefined)
|
|
7384
|
-
throw new Error("Agent Inbox
|
|
7408
|
+
throw new Error("Agent Inbox delivery requires a bound runtime.");
|
|
7385
7409
|
if (message.label === "HANDLE_COMMAND") {
|
|
7386
|
-
await
|
|
7410
|
+
await InboxDelivery.deliverAgentCommand(repository, routing, runtime, message, deliveryTenantId);
|
|
7387
7411
|
return;
|
|
7388
7412
|
}
|
|
7389
7413
|
if (message.label !== "REACT_UPON_EVENT")
|
|
7390
|
-
throw new Error(`Agent Inbox
|
|
7391
|
-
await
|
|
7414
|
+
throw new Error(`Agent Inbox delivery does not handle "${message.label}" messages.`);
|
|
7415
|
+
await InboxDelivery.deliverAgentEvent(repository, routing, runtime, message, deliveryTenantId);
|
|
7392
7416
|
},
|
|
7393
7417
|
/**
|
|
7394
7418
|
* Admits a Command using its stored target and registered assignee.
|
|
@@ -7400,15 +7424,15 @@ const InboxReplay = {
|
|
|
7400
7424
|
* @param tenantId Delivery tenant.
|
|
7401
7425
|
* @returns When the fenced repository operation completes.
|
|
7402
7426
|
*/
|
|
7403
|
-
async
|
|
7427
|
+
async deliverAgentCommand(repository, routing, runtime, message, tenantId) {
|
|
7404
7428
|
const command = InboxMessages.readInboxCommand(message);
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
const route =
|
|
7429
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, tenantId, command);
|
|
7430
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7431
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7408
7432
|
const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
7409
7433
|
if (assignee === undefined)
|
|
7410
7434
|
throw new Error("Agent Command has no selected assignee.");
|
|
7411
|
-
await
|
|
7435
|
+
await InboxDelivery.admitAgent(repository, runtime, message, tenantId, route.entityId, { case: "command", value: command }, [assignee.registeredHandler]);
|
|
7412
7436
|
},
|
|
7413
7437
|
/**
|
|
7414
7438
|
* Admits matching Event handlers from the stored target without rerouting.
|
|
@@ -7420,15 +7444,15 @@ const InboxReplay = {
|
|
|
7420
7444
|
* @param tenantId Delivery tenant.
|
|
7421
7445
|
* @returns When the fenced repository operation completes.
|
|
7422
7446
|
*/
|
|
7423
|
-
async
|
|
7424
|
-
const event = InboxMessages.
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
const route =
|
|
7428
|
-
const handlers =
|
|
7447
|
+
async deliverAgentEvent(repository, routing, runtime, message, tenantId) {
|
|
7448
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7449
|
+
InboxDelivery.validateEventTenant(runtime.context, tenantId, event);
|
|
7450
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Agent Inbox delivery requires an Event payload.");
|
|
7451
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Agent Inbox delivery");
|
|
7452
|
+
const handlers = InboxDelivery.agentEventHandlers(routing, route.messageFullTypeName, event);
|
|
7429
7453
|
if (handlers.length === 0)
|
|
7430
7454
|
return;
|
|
7431
|
-
await
|
|
7455
|
+
await InboxDelivery.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { case: "event", value: event }, handlers);
|
|
7432
7456
|
},
|
|
7433
7457
|
/**
|
|
7434
7458
|
* Restores selected Event and Command reactions from the original payload.
|
|
@@ -7467,14 +7491,14 @@ const InboxReplay = {
|
|
|
7467
7491
|
if (deliverySignal === undefined)
|
|
7468
7492
|
throw new Error("Agent admission requires delivery scope.");
|
|
7469
7493
|
const cancellation = AbortSignal.any([deliverySignal, runtime.agentSignal]);
|
|
7470
|
-
const key =
|
|
7494
|
+
const key = InboxDelivery.capacityKeyFor(repository, input.context, input.id.key(entityId));
|
|
7471
7495
|
const reservation = runtime.reserveAgent(key, cancellation);
|
|
7472
7496
|
if (reservation === undefined) {
|
|
7473
7497
|
runtime.deferAgent(message, tenantId);
|
|
7474
7498
|
AgentDeliveryScope.record({ kind: "deferred" });
|
|
7475
7499
|
return;
|
|
7476
7500
|
}
|
|
7477
|
-
await
|
|
7501
|
+
await InboxDelivery.admitReservedAgent({
|
|
7478
7502
|
repository,
|
|
7479
7503
|
runtime,
|
|
7480
7504
|
context: input.context,
|
|
@@ -7516,13 +7540,13 @@ const InboxReplay = {
|
|
|
7516
7540
|
async admitReservedAgent(input) {
|
|
7517
7541
|
const { repository, runtime, context, message, signal, reservation } = input;
|
|
7518
7542
|
try {
|
|
7519
|
-
const accepted = await
|
|
7543
|
+
const accepted = await InboxDelivery.acceptAgentReceipt(runtime, context, repository, message, signal);
|
|
7520
7544
|
if (!accepted) {
|
|
7521
7545
|
reservation.release();
|
|
7522
7546
|
AgentDeliveryScope.record({ kind: "duplicate" });
|
|
7523
7547
|
return;
|
|
7524
7548
|
}
|
|
7525
|
-
|
|
7549
|
+
InboxDelivery.recordAcceptedAgent(input);
|
|
7526
7550
|
}
|
|
7527
7551
|
catch (error) {
|
|
7528
7552
|
reservation.release();
|
|
@@ -7547,7 +7571,7 @@ const InboxReplay = {
|
|
|
7547
7571
|
},
|
|
7548
7572
|
run: (fence, checkSession) => {
|
|
7549
7573
|
const cancellation = AbortSignal.any([fence, runtime.agentSignal]);
|
|
7550
|
-
const checkAgentSession =
|
|
7574
|
+
const checkAgentSession = InboxDelivery.agentSessionGuard(cancellation, checkSession);
|
|
7551
7575
|
return reservation.run(() => withDeliveryCommitFence(checkAgentSession, () => new AgentLiveRunner(repository, runtime, tenantId, entityId, invocation, handlers, cancellation, checkAgentSession).run()));
|
|
7552
7576
|
},
|
|
7553
7577
|
});
|
|
@@ -7576,8 +7600,8 @@ const InboxReplay = {
|
|
|
7576
7600
|
*/
|
|
7577
7601
|
acceptAgentReceipt(runtime, context, repository, message, signal) {
|
|
7578
7602
|
const source = signal.case === "command"
|
|
7579
|
-
? { kind: "command", id:
|
|
7580
|
-
: { kind: "event", id:
|
|
7603
|
+
? { kind: "command", id: InboxDelivery.requireAgentCommandId(signal.value) }
|
|
7604
|
+
: { kind: "event", id: InboxDelivery.requireAgentEventId(signal.value) };
|
|
7581
7605
|
return runtime.agentReceipts.accept(context, {
|
|
7582
7606
|
stateType: repository.stateSchema.typeName,
|
|
7583
7607
|
recipientId: message.inboxId.targetId,
|
|
@@ -7612,18 +7636,18 @@ const InboxReplay = {
|
|
|
7612
7636
|
* @param routing Registered handlers and message schemas.
|
|
7613
7637
|
* @param message Stored Inbox message with the recorded target.
|
|
7614
7638
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7615
|
-
* @returns
|
|
7639
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7616
7640
|
*/
|
|
7617
|
-
async
|
|
7641
|
+
async deliverProcessManagerCommand(repository, routing, message, deliveryTenantId) {
|
|
7618
7642
|
const runtime = repositoryRuntimes.get(repository);
|
|
7619
7643
|
if (runtime === undefined) {
|
|
7620
|
-
throw new Error("Entity Inbox
|
|
7644
|
+
throw new Error("Entity Inbox delivery requires a bound repository runtime.");
|
|
7621
7645
|
}
|
|
7622
7646
|
const command = InboxMessages.readInboxCommand(message);
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
const route =
|
|
7626
|
-
|
|
7647
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, deliveryTenantId, command);
|
|
7648
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7649
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7650
|
+
await new ProcessManagerCommandExecution(repository, routing, runtime, command).run(route);
|
|
7627
7651
|
},
|
|
7628
7652
|
/**
|
|
7629
7653
|
* Dispatches a validated Event to the Process Manager target recorded in its Inbox.
|
|
@@ -7634,15 +7658,15 @@ const InboxReplay = {
|
|
|
7634
7658
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7635
7659
|
* @returns Completion of dispatch to the recorded target.
|
|
7636
7660
|
*/
|
|
7637
|
-
async
|
|
7661
|
+
async deliverProcessManagerEvent(repository, routing, message, deliveryTenantId) {
|
|
7638
7662
|
const runtime = repositoryRuntimes.get(repository);
|
|
7639
7663
|
if (runtime === undefined) {
|
|
7640
|
-
throw new Error("Entity Inbox
|
|
7664
|
+
throw new Error("Entity Inbox delivery requires a bound repository runtime.");
|
|
7641
7665
|
}
|
|
7642
|
-
const event = InboxMessages.
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
const route =
|
|
7666
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7667
|
+
InboxDelivery.validateEventTenant(runtime.context, deliveryTenantId, event);
|
|
7668
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Entity Inbox delivery requires a readable event payload.");
|
|
7669
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Entity Inbox delivery");
|
|
7646
7670
|
const [entityId] = route.entityIds;
|
|
7647
7671
|
await new ProcessManagerEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
|
|
7648
7672
|
},
|
|
@@ -7655,34 +7679,34 @@ const InboxReplay = {
|
|
|
7655
7679
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7656
7680
|
* @returns Completion of dispatch to the recorded target.
|
|
7657
7681
|
*/
|
|
7658
|
-
async
|
|
7682
|
+
async deliverProjectionEvent(repository, routing, message, deliveryTenantId) {
|
|
7659
7683
|
const runtime = repositoryRuntimes.get(repository);
|
|
7660
7684
|
if (runtime === undefined) {
|
|
7661
|
-
throw new Error("Projection inbox
|
|
7685
|
+
throw new Error("Projection inbox delivery requires a bound repository runtime.");
|
|
7662
7686
|
}
|
|
7663
7687
|
const event = InboxMessages.readProjectionInboxEvent(message);
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
const route =
|
|
7688
|
+
InboxDelivery.validateProjectionDeliveryTenant(runtime.context, deliveryTenantId, event);
|
|
7689
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event);
|
|
7690
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Projection inbox delivery");
|
|
7667
7691
|
const [entityId] = route.entityIds;
|
|
7668
7692
|
await new ProjectionEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
|
|
7669
7693
|
},
|
|
7670
7694
|
/**
|
|
7671
|
-
* Routes a Projection Inbox message to domain Event or Entity-state update
|
|
7695
|
+
* Routes a Projection Inbox message to domain Event or Entity-state update deliver.
|
|
7672
7696
|
*
|
|
7673
7697
|
* @param repository Projection repository receiving the update.
|
|
7674
7698
|
* @param routing Registered subscribers and message schemas.
|
|
7675
7699
|
* @param message Stored Projection Inbox message.
|
|
7676
7700
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7677
|
-
* @returns Completion of the selected
|
|
7701
|
+
* @returns Completion of the selected deliver operation.
|
|
7678
7702
|
*/
|
|
7679
|
-
async
|
|
7703
|
+
async deliverProjectionMessage(repository, routing, message, deliveryTenantId) {
|
|
7680
7704
|
const event = InboxMessages.readProjectionInboxEvent(message);
|
|
7681
7705
|
if (event.message?.typeUrl === TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
|
|
7682
|
-
await
|
|
7706
|
+
await InboxDelivery.deliverProjectionStateUpdate(repository, routing, message, event, deliveryTenantId);
|
|
7683
7707
|
return;
|
|
7684
7708
|
}
|
|
7685
|
-
await
|
|
7709
|
+
await InboxDelivery.deliverProjectionEvent(repository, routing, message, deliveryTenantId);
|
|
7686
7710
|
},
|
|
7687
7711
|
/**
|
|
7688
7712
|
* Dispatches an Entity-state update to the Projection target recorded in its Inbox.
|
|
@@ -7694,12 +7718,12 @@ const InboxReplay = {
|
|
|
7694
7718
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7695
7719
|
* @returns Completion of state-subscriber dispatch to the recorded target.
|
|
7696
7720
|
*/
|
|
7697
|
-
async
|
|
7721
|
+
async deliverProjectionStateUpdate(repository, routing, message, event, deliveryTenantId) {
|
|
7698
7722
|
const runtime = repositoryRuntimes.get(repository);
|
|
7699
7723
|
if (runtime === undefined)
|
|
7700
|
-
throw new Error("Projection inbox
|
|
7701
|
-
|
|
7702
|
-
const route =
|
|
7724
|
+
throw new Error("Projection inbox delivery requires a bound repository runtime.");
|
|
7725
|
+
InboxDelivery.validateProjectionDeliveryTenant(runtime.context, deliveryTenantId, event);
|
|
7726
|
+
const route = InboxDelivery.deliverStateUpdateRoute(repository, routing, message, event);
|
|
7703
7727
|
const [entityId] = route.entityIds;
|
|
7704
7728
|
const subscribers = route.subscribers;
|
|
7705
7729
|
await ProjectionEventExecution.runStateTarget(repository, routing, runtime, event, entityId, route, subscribers);
|
|
@@ -7708,9 +7732,9 @@ const InboxReplay = {
|
|
|
7708
7732
|
* Validates a stored Command against its schema and implicit identifier requirements.
|
|
7709
7733
|
*
|
|
7710
7734
|
* @param routing Registered Command schemas.
|
|
7711
|
-
* @param command Envelope whose payload must remain valid on
|
|
7735
|
+
* @param command Envelope whose payload must remain valid on deliver.
|
|
7712
7736
|
*/
|
|
7713
|
-
|
|
7737
|
+
validateDeliveredCommandPayload(routing, command) {
|
|
7714
7738
|
const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
|
|
7715
7739
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
7716
7740
|
const payload = AnyPacker.tryUnpack(commandMessage, commandSchema);
|
|
@@ -7735,10 +7759,10 @@ const InboxReplay = {
|
|
|
7735
7759
|
* Unpacks and validates a stored Event against its registered schema.
|
|
7736
7760
|
*
|
|
7737
7761
|
* @param routing Registered Event schemas.
|
|
7738
|
-
* @param event Envelope whose payload must remain valid on
|
|
7762
|
+
* @param event Envelope whose payload must remain valid on deliver.
|
|
7739
7763
|
* @param invalidPayloadMessage Error text used when the payload cannot be unpacked.
|
|
7740
7764
|
*/
|
|
7741
|
-
|
|
7765
|
+
validateDeliveredEventPayload(routing, event, invalidPayloadMessage = "Projection inbox delivery requires a readable event payload.") {
|
|
7742
7766
|
const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
|
|
7743
7767
|
const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
|
|
7744
7768
|
const payload = AnyPacker.tryUnpack(eventMessage, eventSchema);
|
|
@@ -7748,69 +7772,69 @@ const InboxReplay = {
|
|
|
7748
7772
|
Validate.check(eventSchema, payload);
|
|
7749
7773
|
},
|
|
7750
7774
|
/**
|
|
7751
|
-
* Checks that a
|
|
7775
|
+
* Checks that a delivered Command's tenant matches its delivery tenant.
|
|
7752
7776
|
*
|
|
7753
7777
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7754
7778
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7755
7779
|
* @param command Stored Command containing the original actor context.
|
|
7756
7780
|
*/
|
|
7757
|
-
|
|
7781
|
+
validateDeliveryTenant(context, deliveryTenantId, command) {
|
|
7758
7782
|
if (!context.multitenant) {
|
|
7759
7783
|
return;
|
|
7760
7784
|
}
|
|
7761
7785
|
if (deliveryTenantId === undefined) {
|
|
7762
|
-
throw new Error(`Multitenant Entity Inbox
|
|
7786
|
+
throw new Error(`Multitenant Entity Inbox delivery for "${context.name}" requires tenantId.`);
|
|
7763
7787
|
}
|
|
7764
7788
|
const envelopeTenantId = RepositoryTenants.readCommandTenant(command);
|
|
7765
7789
|
if (envelopeTenantId === undefined) {
|
|
7766
|
-
throw new Error("Entity Inbox
|
|
7790
|
+
throw new Error("Entity Inbox delivery requires stored command tenant metadata.");
|
|
7767
7791
|
}
|
|
7768
7792
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7769
|
-
throw new Error("Entity Inbox
|
|
7793
|
+
throw new Error("Entity Inbox delivery stored command tenant does not match.");
|
|
7770
7794
|
}
|
|
7771
7795
|
},
|
|
7772
7796
|
/**
|
|
7773
|
-
* Checks that a
|
|
7797
|
+
* Checks that a delivered Projection Event's tenant matches its delivery tenant.
|
|
7774
7798
|
*
|
|
7775
7799
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7776
7800
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7777
7801
|
* @param event Stored Event containing the original tenant metadata.
|
|
7778
7802
|
*/
|
|
7779
|
-
|
|
7803
|
+
validateProjectionDeliveryTenant(context, deliveryTenantId, event) {
|
|
7780
7804
|
if (!context.multitenant) {
|
|
7781
7805
|
return;
|
|
7782
7806
|
}
|
|
7783
7807
|
if (deliveryTenantId === undefined) {
|
|
7784
|
-
throw new Error(`Multitenant projection inbox
|
|
7808
|
+
throw new Error(`Multitenant projection inbox delivery for "${context.name}" requires tenantId.`);
|
|
7785
7809
|
}
|
|
7786
7810
|
const envelopeTenantId = RepositoryTenants.readEventTenant(event);
|
|
7787
7811
|
if (envelopeTenantId === undefined) {
|
|
7788
|
-
throw new Error("Projection inbox
|
|
7812
|
+
throw new Error("Projection inbox delivery requires stored event tenant metadata.");
|
|
7789
7813
|
}
|
|
7790
7814
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7791
|
-
throw new Error("Projection inbox
|
|
7815
|
+
throw new Error("Projection inbox delivery stored event tenant does not match.");
|
|
7792
7816
|
}
|
|
7793
7817
|
},
|
|
7794
7818
|
/**
|
|
7795
|
-
* Checks that a
|
|
7819
|
+
* Checks that a delivered Entity Event's tenant matches its delivery tenant.
|
|
7796
7820
|
*
|
|
7797
7821
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7798
7822
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7799
7823
|
* @param event Stored Event containing the original tenant metadata.
|
|
7800
7824
|
*/
|
|
7801
|
-
|
|
7825
|
+
validateEventTenant(context, deliveryTenantId, event) {
|
|
7802
7826
|
if (!context.multitenant) {
|
|
7803
7827
|
return;
|
|
7804
7828
|
}
|
|
7805
7829
|
if (deliveryTenantId === undefined) {
|
|
7806
|
-
throw new Error(`Multitenant Entity Inbox
|
|
7830
|
+
throw new Error(`Multitenant Entity Inbox delivery for "${context.name}" requires tenantId.`);
|
|
7807
7831
|
}
|
|
7808
7832
|
const envelopeTenantId = RepositoryTenants.readEventTenant(event);
|
|
7809
7833
|
if (envelopeTenantId === undefined) {
|
|
7810
|
-
throw new Error("Entity Inbox
|
|
7834
|
+
throw new Error("Entity Inbox delivery requires stored event tenant metadata.");
|
|
7811
7835
|
}
|
|
7812
7836
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7813
|
-
throw new Error("Entity Inbox
|
|
7837
|
+
throw new Error("Entity Inbox delivery stored event tenant does not match.");
|
|
7814
7838
|
}
|
|
7815
7839
|
},
|
|
7816
7840
|
/**
|
|
@@ -7822,10 +7846,10 @@ const InboxReplay = {
|
|
|
7822
7846
|
* @param command Stored Command used to identify the registered handler schema.
|
|
7823
7847
|
* @returns A deferred route to the recorded Entity identifier.
|
|
7824
7848
|
*/
|
|
7825
|
-
|
|
7849
|
+
deliverCommandRoute(repository, routing, message, command) {
|
|
7826
7850
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7827
7851
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7828
|
-
throw new Error("Entity Inbox
|
|
7852
|
+
throw new Error("Entity Inbox delivery stored target type does not match the routed repository.");
|
|
7829
7853
|
}
|
|
7830
7854
|
const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
|
|
7831
7855
|
const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
@@ -7842,13 +7866,13 @@ const InboxReplay = {
|
|
|
7842
7866
|
* @param routing Registered Event schemas.
|
|
7843
7867
|
* @param message Inbox message recording the target type and identifier.
|
|
7844
7868
|
* @param event Stored Event used to identify the registered handler schema.
|
|
7845
|
-
* @param
|
|
7869
|
+
* @param deliverName Operation name used when reporting a target-type mismatch.
|
|
7846
7870
|
* @returns A deferred route containing exactly the recorded target identifier.
|
|
7847
7871
|
*/
|
|
7848
|
-
|
|
7872
|
+
deliverEventRoute(repository, routing, message, event, deliverName) {
|
|
7849
7873
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7850
7874
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7851
|
-
throw new Error(`${
|
|
7875
|
+
throw new Error(`${deliverName} stored target type does not match the routed repository.`);
|
|
7852
7876
|
}
|
|
7853
7877
|
const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
|
|
7854
7878
|
const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
|
|
@@ -7869,15 +7893,15 @@ const InboxReplay = {
|
|
|
7869
7893
|
* @param event Stored Entity-state update envelope.
|
|
7870
7894
|
* @returns A deferred route with the unpacked state and matching subscribers.
|
|
7871
7895
|
*/
|
|
7872
|
-
|
|
7896
|
+
deliverStateUpdateRoute(repository, routing, message, event) {
|
|
7873
7897
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7874
7898
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7875
|
-
throw new Error("Projection inbox
|
|
7899
|
+
throw new Error("Projection inbox delivery stored target type does not match the routed repository.");
|
|
7876
7900
|
}
|
|
7877
|
-
const update = RepositoryRoutes.decodeStateUpdate(event, routing.stateSchemas, "Projection inbox
|
|
7901
|
+
const update = RepositoryRoutes.decodeStateUpdate(event, routing.stateSchemas, "Projection inbox delivery");
|
|
7878
7902
|
if (update === undefined ||
|
|
7879
7903
|
(routing.stateSubscriptions.get(update.schema.typeName)?.length ?? 0) === 0) {
|
|
7880
|
-
throw new Error("Projection inbox
|
|
7904
|
+
throw new Error("Projection inbox delivery requires a readable stored Entity state update.");
|
|
7881
7905
|
}
|
|
7882
7906
|
const { schema, state } = update;
|
|
7883
7907
|
const entityId = InboxMessages.targetEntityId(message.inboxId.targetId, repository.idField);
|
|
@@ -7886,7 +7910,7 @@ const InboxReplay = {
|
|
|
7886
7910
|
entityIds,
|
|
7887
7911
|
messageFullTypeName: schema.typeName,
|
|
7888
7912
|
state,
|
|
7889
|
-
subscribers:
|
|
7913
|
+
subscribers: InboxDelivery.stateSubscribers(routing, schema, event),
|
|
7890
7914
|
invocation: "deferred",
|
|
7891
7915
|
});
|
|
7892
7916
|
},
|
|
@@ -7903,7 +7927,7 @@ const InboxReplay = {
|
|
|
7903
7927
|
return Object.freeze(candidates.filter((subscriber) => (subscriber.handler.origin === "external") === (event.context?.external === true)));
|
|
7904
7928
|
},
|
|
7905
7929
|
};
|
|
7906
|
-
Object.freeze(
|
|
7930
|
+
Object.freeze(InboxDelivery);
|
|
7907
7931
|
/**
|
|
7908
7932
|
* Records routed signals in the appropriate Entity or Projection Inbox.
|
|
7909
7933
|
*/
|
|
@@ -7914,13 +7938,12 @@ const InboxHandoff = {
|
|
|
7914
7938
|
* @param repository Repository supplying the Command route and Entity type.
|
|
7915
7939
|
* @param runtime Inbox, storage, tenant mode, and delivery strategy.
|
|
7916
7940
|
* @param command Command envelope to retain and deliver.
|
|
7917
|
-
* @returns Completion
|
|
7941
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7918
7942
|
* @param acceptedRoute Recipient route saved when the signal was accepted.
|
|
7919
7943
|
*/
|
|
7920
7944
|
async handoffEntityCommand(repository, runtime, command, acceptedRoute) {
|
|
7921
7945
|
const route = acceptedRoute ?? (await repository.routeCommand(command));
|
|
7922
7946
|
const commandId = RepositorySignals.requireCommandId(command);
|
|
7923
|
-
const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
|
|
7924
7947
|
const deliveryTenantId = RepositoryTenants.requireCommandTenant(runtime.context, command);
|
|
7925
7948
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7926
7949
|
await runtime.entityInbox.receive(delivery, {
|
|
@@ -7932,21 +7955,19 @@ const InboxHandoff = {
|
|
|
7932
7955
|
signal: AnyPacker.pack(CommandSchema, command),
|
|
7933
7956
|
label: "HANDLE_COMMAND",
|
|
7934
7957
|
status: "TO_DELIVER",
|
|
7935
|
-
keepUntil,
|
|
7936
7958
|
}, deliveryTenantId);
|
|
7937
7959
|
},
|
|
7938
7960
|
/**
|
|
7939
|
-
* Delivers an Event to one Projection target
|
|
7961
|
+
* Delivers an Event to one Projection target through its Inbox.
|
|
7940
7962
|
*
|
|
7941
7963
|
* @param repository Repository supplying the Projection type and ID descriptor.
|
|
7942
7964
|
* @param runtime Projection Inbox, storage, and tenant mode.
|
|
7943
7965
|
* @param event Event envelope to retain and deliver.
|
|
7944
7966
|
* @param entityId Routed Projection identifier.
|
|
7945
|
-
* @returns Completion
|
|
7967
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7946
7968
|
*/
|
|
7947
7969
|
async handoffProjectionEvent(repository, runtime, event, entityId) {
|
|
7948
7970
|
const eventId = RepositorySignals.requireEventId(event);
|
|
7949
|
-
const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
|
|
7950
7971
|
const deliveryTenantId = RepositoryTenants.requireProjectionTenant(runtime.context, event);
|
|
7951
7972
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7952
7973
|
await runtime.projectionInbox.receive(delivery, {
|
|
@@ -7958,58 +7979,53 @@ const InboxHandoff = {
|
|
|
7958
7979
|
signal: AnyPacker.pack(EventSchema, event),
|
|
7959
7980
|
label: "UPDATE_SUBSCRIBER",
|
|
7960
7981
|
status: "TO_DELIVER",
|
|
7961
|
-
shard: ShardIndex.single(),
|
|
7962
|
-
keepUntil,
|
|
7963
7982
|
}, deliveryTenantId);
|
|
7964
7983
|
},
|
|
7965
7984
|
/**
|
|
7966
|
-
* Delivers an Event to one
|
|
7985
|
+
* Delivers an Event to one Entity target using its Inbox.
|
|
7967
7986
|
*
|
|
7968
|
-
* @param repository Repository supplying the
|
|
7987
|
+
* @param repository Repository supplying the Entity type and ID descriptor.
|
|
7969
7988
|
* @param runtime Entity Inbox, storage, tenant mode, and delivery strategy.
|
|
7970
7989
|
* @param event Event envelope to retain and deliver.
|
|
7971
|
-
* @param entityId Routed
|
|
7972
|
-
* @returns Completion
|
|
7990
|
+
* @param entityId Routed Entity identifier.
|
|
7991
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7973
7992
|
*/
|
|
7974
|
-
async
|
|
7993
|
+
async handoffEntityEvent(repository, runtime, event, entityId) {
|
|
7975
7994
|
const eventId = RepositorySignals.requireEventId(event);
|
|
7976
|
-
const
|
|
7977
|
-
const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
|
|
7995
|
+
const deliveryTenantId = RepositoryTenants.requireEntityEventTenant(runtime.context, event);
|
|
7978
7996
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7979
|
-
await runtime.entityInbox.receive(delivery, InboxHandoff.
|
|
7997
|
+
await runtime.entityInbox.receive(delivery, InboxHandoff.entityEventInboxInput(repository, eventId.value, event, entityId), deliveryTenantId);
|
|
7980
7998
|
},
|
|
7981
7999
|
/**
|
|
7982
|
-
* Delivers an Event to all routed
|
|
8000
|
+
* Delivers an Event to all routed Entity targets through batch receipt.
|
|
7983
8001
|
*
|
|
7984
|
-
* @param repository Repository supplying the
|
|
8002
|
+
* @param repository Repository supplying the Entity type and ID descriptor.
|
|
7985
8003
|
* @param runtime Entity Inbox, storage, tenant mode, and delivery strategy.
|
|
7986
8004
|
* @param event Event envelope shared by the target deliveries.
|
|
7987
|
-
* @param entityIds Routed
|
|
7988
|
-
* @returns Completion
|
|
8005
|
+
* @param entityIds Routed Entity identifiers.
|
|
8006
|
+
* @returns Completion after batch Inbox receipts and eligible delivery scheduling.
|
|
7989
8007
|
*/
|
|
7990
|
-
async
|
|
8008
|
+
async handoffEntityEvents(repository, runtime, event, entityIds) {
|
|
7991
8009
|
const eventId = RepositorySignals.requireEventId(event);
|
|
7992
|
-
const
|
|
7993
|
-
const deliveryTenantId = RepositoryTenants.requirePmEventTenant(runtime.context, event);
|
|
8010
|
+
const deliveryTenantId = RepositoryTenants.requireEntityEventTenant(runtime.context, event);
|
|
7994
8011
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7995
8012
|
for (let start = 0; start < entityIds.length; start += 1_000) {
|
|
7996
8013
|
const inputs = entityIds
|
|
7997
8014
|
.slice(start, start + 1_000)
|
|
7998
|
-
.map((entityId) => InboxHandoff.
|
|
8015
|
+
.map((entityId) => InboxHandoff.entityEventInboxInput(repository, eventId.value, event, entityId));
|
|
7999
8016
|
await runtime.entityInbox.receiveAll(delivery, inputs, deliveryTenantId);
|
|
8000
8017
|
}
|
|
8001
8018
|
},
|
|
8002
8019
|
/**
|
|
8003
|
-
* Builds a pending
|
|
8020
|
+
* Builds a pending Entity Event delivery for a single target.
|
|
8004
8021
|
*
|
|
8005
8022
|
* @param repository Repository supplying the target type and ID descriptor.
|
|
8006
8023
|
* @param signalId Identifier of the original Event.
|
|
8007
8024
|
* @param event Event envelope to pack without repeating validation.
|
|
8008
|
-
* @param entityId Routed
|
|
8009
|
-
* @param keepUntil Duplicate-retention deadline for this receipt.
|
|
8025
|
+
* @param entityId Routed Entity identifier.
|
|
8010
8026
|
* @returns Input for Entity Inbox receipt.
|
|
8011
8027
|
*/
|
|
8012
|
-
|
|
8028
|
+
entityEventInboxInput(repository, signalId, event, entityId) {
|
|
8013
8029
|
return {
|
|
8014
8030
|
inboxId: {
|
|
8015
8031
|
targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
|
|
@@ -8019,13 +8035,12 @@ const InboxHandoff = {
|
|
|
8019
8035
|
signal: AnyPacker.pack(EventSchema, event),
|
|
8020
8036
|
label: "REACT_UPON_EVENT",
|
|
8021
8037
|
status: "TO_DELIVER",
|
|
8022
|
-
keepUntil,
|
|
8023
8038
|
};
|
|
8024
8039
|
},
|
|
8025
8040
|
};
|
|
8026
8041
|
Object.freeze(InboxHandoff);
|
|
8027
8042
|
/**
|
|
8028
|
-
* Connects bus dispatchers and Inbox
|
|
8043
|
+
* Connects bus dispatchers and Inbox delivery to repository execution.
|
|
8029
8044
|
*/
|
|
8030
8045
|
const RepositoryDispatch = {
|
|
8031
8046
|
/**
|
|
@@ -8126,7 +8141,7 @@ const RepositoryDispatch = {
|
|
|
8126
8141
|
: RepositoryDispatch.dispatchRepositoryStateUpdate(repository, routing, event, route);
|
|
8127
8142
|
},
|
|
8128
8143
|
/**
|
|
8129
|
-
* Creates
|
|
8144
|
+
* Creates deliver access for an Aggregate or Process Manager with signal handlers.
|
|
8130
8145
|
*
|
|
8131
8146
|
* @param repository Repository providing signal routes and Entity identity.
|
|
8132
8147
|
* @param routing Registered handlers and message schemas.
|
|
@@ -8143,19 +8158,17 @@ const RepositoryDispatch = {
|
|
|
8143
8158
|
targetTypeUrl: TypeUrls.derive(repository.stateSchema),
|
|
8144
8159
|
labels: Object.freeze([
|
|
8145
8160
|
...(routing.commandSchemas.length === 0 ? [] : ["HANDLE_COMMAND"]),
|
|
8146
|
-
...(
|
|
8147
|
-
repository.entityFamily === "agent") &&
|
|
8148
|
-
routing.eventSchemas.length > 0
|
|
8149
|
-
? ["REACT_UPON_EVENT"]
|
|
8150
|
-
: []),
|
|
8161
|
+
...(routing.eventSchemas.length > 0 ? ["REACT_UPON_EVENT"] : []),
|
|
8151
8162
|
]),
|
|
8152
|
-
|
|
8153
|
-
?
|
|
8154
|
-
|
|
8163
|
+
deliver: (message, deliveryTenantId) => repository.entityFamily === "aggregate"
|
|
8164
|
+
? message.label === "REACT_UPON_EVENT"
|
|
8165
|
+
? InboxDelivery.deliverAggregateEvent(repository, routing, message, deliveryTenantId)
|
|
8166
|
+
: InboxDelivery.deliverAggregateCommand(repository, routing, message, deliveryTenantId)
|
|
8167
|
+
: InboxDelivery.deliverPmInbox(repository, routing, message, deliveryTenantId),
|
|
8155
8168
|
});
|
|
8156
8169
|
},
|
|
8157
8170
|
/**
|
|
8158
|
-
* Creates
|
|
8171
|
+
* Creates deliver access for a Projection with Event or state subscribers.
|
|
8159
8172
|
*
|
|
8160
8173
|
* @param repository Repository providing Projection identity and Event routes.
|
|
8161
8174
|
* @param routing Registered Event and state subscribers.
|
|
@@ -8168,7 +8181,7 @@ const RepositoryDispatch = {
|
|
|
8168
8181
|
}
|
|
8169
8182
|
return Object.freeze({
|
|
8170
8183
|
targetTypeUrl: TypeUrls.derive(repository.stateSchema),
|
|
8171
|
-
|
|
8184
|
+
deliver: (message, deliveryTenantId) => InboxDelivery.deliverProjectionMessage(repository, routing, message, deliveryTenantId),
|
|
8172
8185
|
});
|
|
8173
8186
|
},
|
|
8174
8187
|
/**
|
|
@@ -8206,7 +8219,7 @@ const RepositoryDispatch = {
|
|
|
8206
8219
|
const route = acceptedRoute ?? (await repository.routeEvent(event));
|
|
8207
8220
|
switch (repository.entityFamily) {
|
|
8208
8221
|
case "aggregate":
|
|
8209
|
-
await RepositoryDispatch.dispatchAggregateEvent(repository,
|
|
8222
|
+
await RepositoryDispatch.dispatchAggregateEvent(repository, runtime, event, route);
|
|
8210
8223
|
return;
|
|
8211
8224
|
case "process-manager":
|
|
8212
8225
|
case "agent":
|
|
@@ -8221,17 +8234,19 @@ const RepositoryDispatch = {
|
|
|
8221
8234
|
* Dispatches an Event to each routed Aggregate with its configured duplicate guard.
|
|
8222
8235
|
*
|
|
8223
8236
|
* @param repository Aggregate repository receiving the Event.
|
|
8224
|
-
* @param routing Registered Aggregate Event handlers and schemas.
|
|
8225
8237
|
* @param runtime Bound storage, publisher, and tenant context.
|
|
8226
8238
|
* @param event Event envelope to dispatch.
|
|
8227
8239
|
* @param route Accepted route listing target Aggregate identifiers.
|
|
8228
8240
|
* @returns Completion of all target dispatches in route order.
|
|
8229
8241
|
*/
|
|
8230
|
-
async dispatchAggregateEvent(repository,
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8242
|
+
async dispatchAggregateEvent(repository, runtime, event, route) {
|
|
8243
|
+
if (route.entityIds.length === 0)
|
|
8244
|
+
return;
|
|
8245
|
+
if (route.entityIds.length === 1) {
|
|
8246
|
+
await InboxHandoff.handoffEntityEvent(repository, runtime, event, route.entityIds[0]);
|
|
8247
|
+
return;
|
|
8234
8248
|
}
|
|
8249
|
+
await InboxHandoff.handoffEntityEvents(repository, runtime, event, route.entityIds);
|
|
8235
8250
|
},
|
|
8236
8251
|
/**
|
|
8237
8252
|
* Delivers a routed Entity-state update to each target Projection Inbox.
|