@spine-event-engine/server 2.0.0-snapshot.25 → 2.0.0-snapshot.28
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 +104 -16
- package/REFERENCE.md +486 -57
- package/dist/agent/agent-history.d.ts +19 -3
- package/dist/agent/agent-history.d.ts.map +1 -1
- package/dist/agent/agent-history.js +41 -10
- package/dist/agent/agent-history.js.map +1 -1
- 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 +13 -2
- package/dist/agent/agent-live-read-runtime.d.ts.map +1 -1
- package/dist/agent/agent-live-read-runtime.js +6 -5
- package/dist/agent/agent-live-read-runtime.js.map +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 +171 -1
- package/dist/browser/browser-server.d.ts.map +1 -1
- package/dist/browser/browser-server.js +1027 -303
- package/dist/browser/browser-server.js.map +1 -1
- package/dist/browser/durable-subscription-bindings.d.ts +5 -0
- package/dist/browser/durable-subscription-bindings.d.ts.map +1 -1
- package/dist/browser/durable-subscription-bindings.js +226 -38
- package/dist/browser/durable-subscription-bindings.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/options.d.ts +53 -2
- package/dist/browser/options.d.ts.map +1 -1
- package/dist/bus/command-bus.d.ts +9 -1
- package/dist/bus/command-bus.d.ts.map +1 -1
- package/dist/bus/command-bus.js +75 -10
- package/dist/bus/command-bus.js.map +1 -1
- package/dist/bus/command-errors.d.ts +20 -4
- package/dist/bus/command-errors.d.ts.map +1 -1
- package/dist/bus/command-errors.js +47 -7
- package/dist/bus/command-errors.js.map +1 -1
- package/dist/bus/event-bus.d.ts +9 -1
- package/dist/bus/event-bus.d.ts.map +1 -1
- package/dist/bus/event-bus.js +136 -9
- package/dist/bus/event-bus.js.map +1 -1
- package/dist/context/bounded-context.d.ts +59 -39
- package/dist/context/bounded-context.d.ts.map +1 -1
- package/dist/context/bounded-context.js +277 -262
- 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 -119
- 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 +9 -9
- package/dist/context/projection-handoff.d.ts.map +1 -1
- package/dist/context/projection-handoff.js +31 -23
- 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-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 +82 -34
- package/dist/delivery/delivery.js.map +1 -1
- package/dist/delivery/reception-observation.d.ts +3 -3
- package/dist/delivery/reception-observation.js +2 -2
- package/dist/entity/entity.d.ts +59 -0
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +60 -0
- package/dist/entity/entity.js.map +1 -1
- package/dist/handler/app-query-handler.d.ts +60 -0
- package/dist/handler/app-query-handler.d.ts.map +1 -0
- package/dist/handler/app-query-handler.js +23 -0
- package/dist/handler/app-query-handler.js.map +1 -0
- package/dist/handler/generated-registry-discovery.d.ts +6 -0
- package/dist/handler/generated-registry-discovery.d.ts.map +1 -1
- package/dist/handler/generated-registry-discovery.js +71 -2
- package/dist/handler/generated-registry-discovery.js.map +1 -1
- package/dist/handler/handler-decorators.d.ts +8 -29
- package/dist/handler/handler-decorators.d.ts.map +1 -1
- package/dist/handler/handler-decorators.js +33 -210
- package/dist/handler/handler-decorators.js.map +1 -1
- package/dist/handler/spine-metadata-inventory.d.ts +99 -0
- package/dist/handler/spine-metadata-inventory.d.ts.map +1 -0
- package/dist/handler/spine-metadata-inventory.js +162 -0
- package/dist/handler/spine-metadata-inventory.js.map +1 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- 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 +61 -26
- package/dist/repository/repository.d.ts.map +1 -1
- package/dist/repository/repository.js +612 -306
- 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/managed-server-application.d.ts +12 -11
- package/dist/server/managed-server-application.d.ts.map +1 -1
- package/dist/server/managed-server-application.js +41 -25
- package/dist/server/managed-server-application.js.map +1 -1
- package/dist/server/node-coordinator.d.ts +18 -5
- package/dist/server/node-coordinator.d.ts.map +1 -1
- package/dist/server/node-coordinator.js +744 -108
- package/dist/server/node-coordinator.js.map +1 -1
- package/dist/server/reserved-spine-rpc-paths.json +1 -0
- package/dist/server/server-environment.d.ts +23 -5
- package/dist/server/server-environment.d.ts.map +1 -1
- package/dist/server/server-environment.js +89 -24
- package/dist/server/server-environment.js.map +1 -1
- package/dist/server/server.d.ts +45 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +112 -3
- package/dist/server/server.js.map +1 -1
- package/dist/services/app-query-execution.d.ts +53 -0
- package/dist/services/app-query-execution.d.ts.map +1 -0
- package/dist/services/app-query-execution.js +408 -0
- package/dist/services/app-query-execution.js.map +1 -0
- package/dist/services/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/query-reader.d.ts +32 -7
- package/dist/services/query-reader.d.ts.map +1 -1
- package/dist/services/query-reader.js +22 -2
- package/dist/services/query-reader.js.map +1 -1
- package/dist/services/read-request-status.d.ts +65 -0
- package/dist/services/read-request-status.d.ts.map +1 -0
- package/dist/services/read-request-status.js +151 -0
- package/dist/services/read-request-status.js.map +1 -0
- package/dist/services/spine-services.d.ts +42 -7
- package/dist/services/spine-services.d.ts.map +1 -1
- package/dist/services/spine-services.js +1005 -175
- package/dist/services/spine-services.js.map +1 -1
- package/dist/services/trusted-reader-ingress.d.ts +110 -0
- package/dist/services/trusted-reader-ingress.d.ts.map +1 -0
- package/dist/services/trusted-reader-ingress.js +102 -0
- package/dist/services/trusted-reader-ingress.js.map +1 -0
- package/dist/spi/handler-registry.d.ts +2 -0
- package/dist/spi/handler-registry.d.ts.map +1 -1
- package/dist/spi/handler-registry.js +1 -1
- package/dist/spi/handler-registry.js.map +1 -1
- package/dist/stand/read-side-filter-engine.d.ts +46 -0
- package/dist/stand/read-side-filter-engine.d.ts.map +1 -0
- package/dist/stand/read-side-filter-engine.js +356 -0
- package/dist/stand/read-side-filter-engine.js.map +1 -0
- package/dist/stand/read-side-filter.d.ts +247 -0
- package/dist/stand/read-side-filter.d.ts.map +1 -0
- package/dist/stand/read-side-filter.js +39 -0
- package/dist/stand/read-side-filter.js.map +1 -0
- package/dist/stand/read-side-message.d.ts +66 -0
- package/dist/stand/read-side-message.d.ts.map +1 -0
- package/dist/stand/read-side-message.js +167 -0
- package/dist/stand/read-side-message.js.map +1 -0
- package/dist/stand/stand-direct-delivery.d.ts +161 -0
- package/dist/stand/stand-direct-delivery.d.ts.map +1 -0
- package/dist/stand/stand-direct-delivery.js +210 -0
- package/dist/stand/stand-direct-delivery.js.map +1 -0
- package/dist/stand/stand.d.ts +81 -43
- package/dist/stand/stand.d.ts.map +1 -1
- package/dist/stand/stand.js +411 -55
- package/dist/stand/stand.js.map +1 -1
- package/dist/stand/subscription-observer.d.ts.map +1 -1
- package/dist/stand/subscription-observer.js +103 -0
- package/dist/stand/subscription-observer.js.map +1 -1
- package/dist/stand/subscription-read-filters.d.ts +40 -0
- package/dist/stand/subscription-read-filters.d.ts.map +1 -0
- package/dist/stand/subscription-read-filters.js +91 -0
- package/dist/stand/subscription-read-filters.js.map +1 -0
- package/dist/stand/subscription-reader-binding.d.ts +20 -0
- package/dist/stand/subscription-reader-binding.d.ts.map +1 -0
- package/dist/stand/subscription-reader-binding.js +56 -0
- package/dist/stand/subscription-reader-binding.js.map +1 -0
- package/dist/stand/subscription-records.d.ts +30 -3
- package/dist/stand/subscription-records.d.ts.map +1 -1
- package/dist/stand/subscription-records.js +61 -16
- package/dist/stand/subscription-records.js.map +1 -1
- package/dist/stand/subscription-registry.d.ts +49 -14
- package/dist/stand/subscription-registry.d.ts.map +1 -1
- package/dist/stand/subscription-registry.js +150 -31
- package/dist/stand/subscription-registry.js.map +1 -1
- package/dist/stand/subscription-runtime.d.ts +7 -2
- package/dist/stand/subscription-runtime.d.ts.map +1 -1
- package/dist/stand/subscription-runtime.js +131 -25
- package/dist/stand/subscription-runtime.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -14
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* the License.
|
|
13
13
|
*/
|
|
14
14
|
import { clone, create, equals, ScalarType, toBinary, } from "@bufbuild/protobuf";
|
|
15
|
-
import { AnySchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, } from "@bufbuild/protobuf/wkt";
|
|
15
|
+
import { AnySchema, Int32ValueSchema, Int64ValueSchema, StringValueSchema, TimestampSchema, } from "@bufbuild/protobuf/wkt";
|
|
16
16
|
import { ValidationException, RejectionThrowable, Validate, TypeUrls, AnyPacker, Identifiers, StringifierRegistry, Time, } from "@spine-event-engine/core";
|
|
17
17
|
import { CommandContextSchema, ActorContextSchema, CommandIdSchema, CommandSchema, EventContextSchema, EventIdSchema, EventSchema, MessageIdSchema, TenantIdSchema, RejectionEventContextSchema, VersionSchema, EntityOption_Kind, } from "@spine-event-engine/proto";
|
|
18
18
|
import * as EntityLog from "@spine-event-engine/proto/generated/spine/system/server/entity_log_events_pb.js";
|
|
@@ -22,7 +22,7 @@ import { EventStore, StorageQueryPolicy, } from "@spine-event-engine/storage";
|
|
|
22
22
|
import { TenantBoundary, eventStoreAccess, StoragePreparations, } from "@spine-event-engine/storage/provider";
|
|
23
23
|
import { EntityCommitStorageFactories } from "@spine-event-engine/storage/provider";
|
|
24
24
|
import { AgentHistoryStorageFactories, } from "@spine-event-engine/storage/provider";
|
|
25
|
-
import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, ModelRefSchema, ModelPreferenceSchema, ConversationIdSchema, } from "@spine-event-engine/proto/agent";
|
|
25
|
+
import { AgentHistoryEntrySchema, AgentInvocationTerminatedSchema, AgentModelSelectionChangedSchema as SelectionChangedSchema, AiModelKind, ModelRefSchema, ModelPreferenceSchema, ConversationRecordSchema, ConversationIdSchema, AgentHistoryCursorSchema, } from "@spine-event-engine/proto/agent";
|
|
26
26
|
import { registryOptions } from "@spine-event-engine/ai/spi/runtime";
|
|
27
27
|
import { CommandValidationError } from "../bus/command-errors.js";
|
|
28
28
|
import { SignalPublisher } from "../runtime/signal-publisher.js";
|
|
@@ -32,10 +32,12 @@ import { CommandRoutingInternals, } from "./command-routing.js";
|
|
|
32
32
|
import { EventRoutingInternals } from "./event-routing.js";
|
|
33
33
|
import { StateUpdateRoutingInternals, } from "./state-update-routing.js";
|
|
34
34
|
import { RoutingDeclarations } from "./routing-declarations.js";
|
|
35
|
+
import { snapshotTrustedReader, } from "@spine-event-engine/core/spi/trusted-reader";
|
|
36
|
+
import { Code, ConnectError } from "@connectrpc/connect";
|
|
35
37
|
import { EventDispatcherOriginSchemas } from "../bus/event-dispatcher-origin-schemas.js";
|
|
36
38
|
import { receptionObservation } from "../delivery/reception-observation.js";
|
|
37
|
-
import { commitFenced, withDeliveryCommitFence } from "./commit-fence.js";
|
|
38
|
-
import { AgentHistoryReads } from "../agent/agent-history.js";
|
|
39
|
+
import { commitFenced, withDeliveryCommitFence, withoutDeliveryCommitFence, } from "./commit-fence.js";
|
|
40
|
+
import { AgentHistoryReads, } from "../agent/agent-history.js";
|
|
39
41
|
import { AgentLiveAiRuntime } from "../agent/agent-live-ai-runtime.js";
|
|
40
42
|
import { AgentLiveAudit } from "../agent/agent-live-audit.js";
|
|
41
43
|
import { AgentLiveBudget } from "../agent/agent-live-budget.js";
|
|
@@ -258,7 +260,14 @@ const RepositoryAgentReads = {
|
|
|
258
260
|
: validated;
|
|
259
261
|
const tenant = scope.tenantId === undefined ? undefined : RepositoryTenants.require(scope.tenantId);
|
|
260
262
|
const context = RepositoryTenants.storageContextForTenant(runtime.context, tenant);
|
|
261
|
-
|
|
263
|
+
const readSide = {
|
|
264
|
+
...(scope.actorContext === undefined
|
|
265
|
+
? {}
|
|
266
|
+
: { actorContext: clone(ActorContextSchema, scope.actorContext) }),
|
|
267
|
+
...(scope.reader === undefined ? {} : snapshotTrustedReader(scope.reader)),
|
|
268
|
+
...(scope.signal === undefined ? {} : { signal: scope.signal }),
|
|
269
|
+
};
|
|
270
|
+
return { runtime, context, id: copiedId, readSide };
|
|
262
271
|
},
|
|
263
272
|
/**
|
|
264
273
|
* Returns a detached public history request before provider access.
|
|
@@ -269,7 +278,9 @@ const RepositoryAgentReads = {
|
|
|
269
278
|
request(request) {
|
|
270
279
|
return {
|
|
271
280
|
pageSize: request.pageSize,
|
|
272
|
-
...(request.cursor === undefined
|
|
281
|
+
...(request.cursor === undefined
|
|
282
|
+
? {}
|
|
283
|
+
: { cursor: clone(AgentHistoryCursorSchema, request.cursor) }),
|
|
273
284
|
};
|
|
274
285
|
},
|
|
275
286
|
/**
|
|
@@ -313,22 +324,95 @@ const RepositoryAgentReads = {
|
|
|
313
324
|
* @returns Detached history entries and opaque continuation.
|
|
314
325
|
*/
|
|
315
326
|
async historyPage(repository, captured, view, request) {
|
|
327
|
+
this.checkCanceled(captured.readSide.signal);
|
|
316
328
|
if (repositoryRuntimes.get(repository) !== captured.runtime)
|
|
317
329
|
throw new Error("Agent read requires an active repository.");
|
|
318
330
|
await captured.runtime.onAdmit(captured.context.tenantId);
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
331
|
+
this.checkCanceled(captured.readSide.signal);
|
|
332
|
+
const page = await RepositoryStorage.track(repository, () => this.loadHistoryPage(repository, captured, view, request));
|
|
333
|
+
this.checkCanceled(captured.readSide.signal);
|
|
334
|
+
return page;
|
|
335
|
+
},
|
|
336
|
+
/**
|
|
337
|
+
* Rejects canceled public history reads even when no policy callback runs.
|
|
338
|
+
* @param signal Current caller cancellation, if supplied.
|
|
339
|
+
*/
|
|
340
|
+
checkCanceled(signal) {
|
|
341
|
+
if (signal?.aborted)
|
|
342
|
+
throw new ConnectError("Read canceled.", Code.Canceled);
|
|
343
|
+
},
|
|
344
|
+
/**
|
|
345
|
+
* Reads a retained Agent page and transforms only its outward domain values.
|
|
346
|
+
*
|
|
347
|
+
* @param repository Registered Agent repository.
|
|
348
|
+
* @param captured Stable Entity, tenant, and reader facts.
|
|
349
|
+
* @param view Indexed history category.
|
|
350
|
+
* @param request Detached page request.
|
|
351
|
+
* @returns Visible entries and original continuation.
|
|
352
|
+
*/
|
|
353
|
+
async loadHistoryPage(repository, captured, view, request) {
|
|
354
|
+
const input = RepositoryStorage.entityStorageInput(repository, captured.context);
|
|
355
|
+
const history = RepositoryStorage.openRepositoryAgentHistory(repository, captured.runtime.storageFactory, input);
|
|
356
|
+
const page = await AgentHistoryReads.readBound({
|
|
357
|
+
storage: history,
|
|
358
|
+
entityId: captured.id,
|
|
359
|
+
scope: repositoryAccess.agentHistoryScope(captured.context, input, captured.id),
|
|
360
|
+
}, view, request);
|
|
361
|
+
return this.visibleHistory(captured.runtime.stand, page, captured.id, view, request, captured.context.tenantId, captured.readSide);
|
|
362
|
+
},
|
|
363
|
+
/**
|
|
364
|
+
* Transforms returned Agent domain roots while retaining the original page cursor.
|
|
365
|
+
*
|
|
366
|
+
* @param stand Producing context's read policy.
|
|
367
|
+
* @param page Captured history page before outward transformation.
|
|
368
|
+
* @param entityId Agent identifier used for this call.
|
|
369
|
+
* @param view Requested history category.
|
|
370
|
+
* @param request Original page request.
|
|
371
|
+
* @param tenantId Effective tenant of this Agent.
|
|
372
|
+
* @param readSide Trusted in-process caller facts captured with the reader.
|
|
373
|
+
* @returns Visible entries with the provider's original continuation.
|
|
374
|
+
*/
|
|
375
|
+
async visibleHistory(stand, page, entityId, view, request, tenantId, readSide = {}) {
|
|
376
|
+
const facts = {
|
|
377
|
+
operation: { kind: "history", history: { source: "agent", entityId, view, request } },
|
|
378
|
+
...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
|
|
379
|
+
...readSide,
|
|
380
|
+
};
|
|
381
|
+
const items = [];
|
|
382
|
+
for (const entry of page.items) {
|
|
383
|
+
const visible = await this.visibleEntry(stand, entry, facts);
|
|
384
|
+
if (visible !== undefined)
|
|
385
|
+
items.push(visible);
|
|
386
|
+
}
|
|
387
|
+
return { items, ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }) };
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* Transforms one retained Agent entry without changing its wrapper or cursor.
|
|
391
|
+
*
|
|
392
|
+
* @param stand Producing context's outward read policy.
|
|
393
|
+
* @param entry Captured indexed history entry.
|
|
394
|
+
* @param facts Actual read operation and trusted invocation facts.
|
|
395
|
+
* @returns Visible entry, or undefined when policy removed its root.
|
|
396
|
+
*/
|
|
397
|
+
async visibleEntry(stand, entry, facts) {
|
|
398
|
+
const item = entry.item;
|
|
399
|
+
let visible;
|
|
400
|
+
if (item.case === "conversationRecord") {
|
|
401
|
+
const value = await standAccess.filterState(stand, ConversationRecordSchema, item.value, facts);
|
|
402
|
+
if (value !== null)
|
|
403
|
+
visible = { case: "conversationRecord", value };
|
|
404
|
+
}
|
|
405
|
+
else if (item.case === "systemEvent" || item.case === "domainEvent") {
|
|
406
|
+
const value = await standAccess.filterEvent(stand, item.value, facts);
|
|
407
|
+
if (value !== null)
|
|
408
|
+
visible =
|
|
409
|
+
item.case === "systemEvent"
|
|
410
|
+
? { case: "systemEvent", value }
|
|
411
|
+
: { case: "domainEvent", value };
|
|
412
|
+
}
|
|
413
|
+
return visible === undefined
|
|
414
|
+
? undefined
|
|
415
|
+
: create(AgentHistoryEntrySchema, { ...entry, item: visible });
|
|
332
416
|
},
|
|
333
417
|
/**
|
|
334
418
|
* Returns one typed category from the full Proto history oneof.
|
|
@@ -483,6 +567,9 @@ export const repositoryAccess = Object.freeze({
|
|
|
483
567
|
}
|
|
484
568
|
return schemas;
|
|
485
569
|
},
|
|
570
|
+
/**
|
|
571
|
+
* Returns the AI registry configured for this repository, if present.
|
|
572
|
+
*/
|
|
486
573
|
agentConfiguration(repository) {
|
|
487
574
|
const ai = repositoryAiConfigurations.get(repository);
|
|
488
575
|
return ai === undefined ? {} : { ai };
|
|
@@ -512,6 +599,9 @@ export const repositoryAccess = Object.freeze({
|
|
|
512
599
|
...read,
|
|
513
600
|
}));
|
|
514
601
|
},
|
|
602
|
+
/**
|
|
603
|
+
* Reads one tenant-admitted Agent history page through retained history storage.
|
|
604
|
+
*/
|
|
515
605
|
async agentHistoryPage(repository, entityId, request, tenantId) {
|
|
516
606
|
if (repository.entityFamily !== "agent")
|
|
517
607
|
throw new TypeError("Agent history requires an Agent repository.");
|
|
@@ -531,7 +621,7 @@ export const repositoryAccess = Object.freeze({
|
|
|
531
621
|
});
|
|
532
622
|
},
|
|
533
623
|
/**
|
|
534
|
-
*
|
|
624
|
+
* Returns the opaque cursor scope for a registered Agent history read.
|
|
535
625
|
*/
|
|
536
626
|
agentHistoryScope(context, input, entityId) {
|
|
537
627
|
return {
|
|
@@ -600,19 +690,19 @@ export const repositoryAccess = Object.freeze({
|
|
|
600
690
|
});
|
|
601
691
|
},
|
|
602
692
|
/**
|
|
603
|
-
* Looks up the Aggregate or Process Manager
|
|
693
|
+
* Looks up the Aggregate or Process Manager Inbox delivery target.
|
|
604
694
|
*
|
|
605
695
|
* @param repository Repository to inspect.
|
|
606
|
-
* @returns Its
|
|
696
|
+
* @returns Its delivery target, when registered.
|
|
607
697
|
*/
|
|
608
698
|
entityInboxTarget(repository) {
|
|
609
699
|
return repositoryEntityInboxTargets.get(repository);
|
|
610
700
|
},
|
|
611
701
|
/**
|
|
612
|
-
* Looks up the Projection
|
|
702
|
+
* Looks up the Projection Inbox delivery target.
|
|
613
703
|
*
|
|
614
704
|
* @param repository Repository to inspect.
|
|
615
|
-
* @returns Its
|
|
705
|
+
* @returns Its delivery target, when registered.
|
|
616
706
|
*/
|
|
617
707
|
projectionInboxTarget(repository) {
|
|
618
708
|
return repositoryProjectionInboxTargets.get(repository);
|
|
@@ -977,7 +1067,8 @@ class AggregateExecutionSupport {
|
|
|
977
1067
|
return Object.freeze([produced]);
|
|
978
1068
|
}
|
|
979
1069
|
/**
|
|
980
|
-
*
|
|
1070
|
+
* Writes Aggregate state, diagnostic history, and produced Events through the configured
|
|
1071
|
+
* commit storage. Atomicity depends on that storage implementation.
|
|
981
1072
|
*
|
|
982
1073
|
* @param loaded Aggregate and commit storage to update.
|
|
983
1074
|
* @param entityId Aggregate identifier.
|
|
@@ -1051,7 +1142,7 @@ class AggregateExecutionSupport {
|
|
|
1051
1142
|
await loaded.events.append(clone(EventSchema, event));
|
|
1052
1143
|
}
|
|
1053
1144
|
/**
|
|
1054
|
-
* Completes persistence
|
|
1145
|
+
* Completes persistence and one handoff attempt for each produced Event.
|
|
1055
1146
|
*
|
|
1056
1147
|
* @param loaded Aggregate and commit storage to update.
|
|
1057
1148
|
* @param entityId Aggregate identifier.
|
|
@@ -1059,14 +1150,14 @@ class AggregateExecutionSupport {
|
|
|
1059
1150
|
* @param dispatch Publishes one committed Event.
|
|
1060
1151
|
* @param onPersisted Runs after durable persistence.
|
|
1061
1152
|
* @param diagnostics Events to retain in diagnostic history with this commit.
|
|
1062
|
-
* @returns
|
|
1153
|
+
* @returns Completion after the Event handoff attempts settle.
|
|
1063
1154
|
*/
|
|
1064
1155
|
async persistAggregateAndDispatch(loaded, entityId, events, dispatch, onPersisted, diagnostics = events) {
|
|
1065
1156
|
const committed = await this.persistAggregateUpdate(loaded, entityId, events, diagnostics);
|
|
1066
1157
|
if (!committed)
|
|
1067
|
-
return
|
|
1158
|
+
return;
|
|
1068
1159
|
await onPersisted();
|
|
1069
|
-
|
|
1160
|
+
await withoutDeliveryCommitFence(async () => {
|
|
1070
1161
|
await Promise.all(events.map(async (event) => {
|
|
1071
1162
|
try {
|
|
1072
1163
|
await dispatch(event);
|
|
@@ -1075,7 +1166,7 @@ class AggregateExecutionSupport {
|
|
|
1075
1166
|
this.#runtime.publisher.reportFailure("event", event, error);
|
|
1076
1167
|
}
|
|
1077
1168
|
}));
|
|
1078
|
-
};
|
|
1169
|
+
});
|
|
1079
1170
|
}
|
|
1080
1171
|
/**
|
|
1081
1172
|
* Restores an Aggregate or creates one with initial state and version zero.
|
|
@@ -1135,27 +1226,27 @@ class AggregateCommandExecution {
|
|
|
1135
1226
|
/**
|
|
1136
1227
|
* Validates the Command and executes its assignee when registered.
|
|
1137
1228
|
*
|
|
1138
|
-
* @param
|
|
1139
|
-
* @returns
|
|
1229
|
+
* @param deliveredRoute Accepted route from durable inbox delivery, when present.
|
|
1230
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
1140
1231
|
*/
|
|
1141
|
-
async run(
|
|
1232
|
+
async run(deliveredRoute) {
|
|
1142
1233
|
void RepositorySignals.requireCommandId(this.#command);
|
|
1143
|
-
const intake = await this.#readIntake(
|
|
1234
|
+
const intake = await this.#readIntake(deliveredRoute);
|
|
1144
1235
|
if (intake === undefined)
|
|
1145
1236
|
return undefined;
|
|
1146
|
-
|
|
1237
|
+
await this.#runAssignee(intake);
|
|
1147
1238
|
}
|
|
1148
1239
|
/**
|
|
1149
1240
|
* Decodes the Command and selects its registered assignee.
|
|
1150
1241
|
*
|
|
1151
|
-
* @param
|
|
1242
|
+
* @param deliveredRoute Accepted inbox route, when present.
|
|
1152
1243
|
* @returns Decoded message and route with an assignee, or `undefined`.
|
|
1153
1244
|
*/
|
|
1154
|
-
async #readIntake(
|
|
1245
|
+
async #readIntake(deliveredRoute) {
|
|
1155
1246
|
const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
|
|
1156
1247
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
1157
1248
|
const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
|
|
1158
|
-
const route =
|
|
1249
|
+
const route = deliveredRoute ?? (await this.#repository.routeCommand(this.#command));
|
|
1159
1250
|
const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
1160
1251
|
return assignee === undefined ? undefined : Object.freeze({ message, route, assignee });
|
|
1161
1252
|
}
|
|
@@ -1163,33 +1254,34 @@ class AggregateCommandExecution {
|
|
|
1163
1254
|
* Invokes the assignee, persists its Events, or publishes a declared rejection.
|
|
1164
1255
|
*
|
|
1165
1256
|
* @param intake Decoded Command, target route, and assignee.
|
|
1166
|
-
* @returns
|
|
1257
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
1167
1258
|
*/
|
|
1168
1259
|
async #runAssignee(intake) {
|
|
1169
1260
|
const loaded = await this.#support.loadAggregate(intake.route.entityId);
|
|
1170
1261
|
this.#publishCommandDispatch(intake.route.entityId);
|
|
1171
1262
|
let produced;
|
|
1172
1263
|
try {
|
|
1173
|
-
produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler, intake.message, EntityInvocation.commandHandlerContext(this.#command));
|
|
1264
|
+
produced = await this.#invokeAssignee(loaded.entity, intake.assignee.handler, intake.message, EntityInvocation.commandHandlerContext(this.#command), this.#command.context?.actorContext);
|
|
1174
1265
|
}
|
|
1175
1266
|
catch (error) {
|
|
1176
|
-
|
|
1267
|
+
await this.#postAssigneeRejection(intake, error);
|
|
1268
|
+
return undefined;
|
|
1177
1269
|
}
|
|
1178
1270
|
const events = this.#bindProducedEvents(this.#support.normalizeProducedSignals(produced), intake.route.entityId, RepositoryEntities.priorVersion(loaded.current));
|
|
1179
|
-
|
|
1271
|
+
await this.#persistAssigneeResult(loaded, intake.route.entityId, events);
|
|
1180
1272
|
}
|
|
1181
1273
|
/**
|
|
1182
1274
|
* Publishes a declared rejection from the invoked Aggregate assignee.
|
|
1183
1275
|
*
|
|
1184
1276
|
* @param intake Assignee and target route for the rejected Command.
|
|
1185
1277
|
* @param error Handler failure to classify and publish.
|
|
1186
|
-
* @returns
|
|
1278
|
+
* @returns Completion after the rejection Event handoff attempt.
|
|
1187
1279
|
*/
|
|
1188
|
-
#postAssigneeRejection(intake, error) {
|
|
1280
|
+
async #postAssigneeRejection(intake, error) {
|
|
1189
1281
|
if (!RejectionThrowable.is(error))
|
|
1190
1282
|
throw error;
|
|
1191
1283
|
RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
|
|
1192
|
-
|
|
1284
|
+
await RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
|
|
1193
1285
|
}
|
|
1194
1286
|
/**
|
|
1195
1287
|
* Publishes a best-effort Command dispatch diagnostic.
|
|
@@ -1205,11 +1297,11 @@ class AggregateCommandExecution {
|
|
|
1205
1297
|
* @param loaded Aggregate and commit storage.
|
|
1206
1298
|
* @param entityId Target Aggregate identifier.
|
|
1207
1299
|
* @param events Bound Events from the assignee.
|
|
1208
|
-
* @returns
|
|
1300
|
+
* @returns Completion after committed Event handoff attempts.
|
|
1209
1301
|
*/
|
|
1210
|
-
#persistAssigneeResult(loaded, entityId, events) {
|
|
1302
|
+
async #persistAssigneeResult(loaded, entityId, events) {
|
|
1211
1303
|
const committedVersion = loaded.version + 1n;
|
|
1212
|
-
|
|
1304
|
+
await this.#support.persistAggregateAndDispatch(loaded, entityId, events, (event) => this.#runtime.publisher.publishCommittedEvent(event), () => {
|
|
1213
1305
|
if (!RepositoryEntities.repositoryChanged(loaded.entity))
|
|
1214
1306
|
return;
|
|
1215
1307
|
EntityStateChangePublisher.command(this.#runtime, this.#repository, this.#command, entityId, loaded.oldState, loaded.current === undefined
|
|
@@ -1227,26 +1319,45 @@ class AggregateCommandExecution {
|
|
|
1227
1319
|
* @param handler Assignee declaration to invoke.
|
|
1228
1320
|
* @param message Decoded Command message.
|
|
1229
1321
|
* @param context Copied Command context for the handler.
|
|
1322
|
+
* @param actorContext Actor metadata of the Command signal.
|
|
1230
1323
|
* @returns The handler's raw result after a successful commit.
|
|
1231
1324
|
*/
|
|
1232
|
-
async #invokeAssignee(entity, handler, message, context) {
|
|
1233
|
-
|
|
1325
|
+
async #invokeAssignee(entity, handler, message, context, actorContext) {
|
|
1326
|
+
const releaseActor = entityHistoryAccess.bindActor(entity, actorContext);
|
|
1327
|
+
let started = false;
|
|
1234
1328
|
try {
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
throw receptionObservation.recordPostHandlerFailure(new Error("Repository aggregate command handlers must return at least one event."));
|
|
1239
|
-
RepositoryHandlers.requireDeclaredOutputs(handler, signals);
|
|
1240
|
-
const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
|
|
1241
|
-
if (commit.status === "rejected") {
|
|
1242
|
-
throw EntityInvocation.rejectedTransition(commit.validation.error);
|
|
1243
|
-
}
|
|
1244
|
-
return produced;
|
|
1329
|
+
transactionalEntityAccess.start(entity);
|
|
1330
|
+
started = true;
|
|
1331
|
+
return await this.#invokeAndCommitAssignee(entity, handler, message, context);
|
|
1245
1332
|
}
|
|
1246
1333
|
catch (error) {
|
|
1247
|
-
|
|
1334
|
+
if (started)
|
|
1335
|
+
transactionalEntityAccess.rollback(entity);
|
|
1248
1336
|
throw error;
|
|
1249
1337
|
}
|
|
1338
|
+
finally {
|
|
1339
|
+
releaseActor();
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Checks a declared Aggregate result and commits its accepted transition.
|
|
1344
|
+
*
|
|
1345
|
+
* @param entity Current Aggregate draft.
|
|
1346
|
+
* @param handler Declared Command handler.
|
|
1347
|
+
* @param message Decoded domain Command.
|
|
1348
|
+
* @param context Detached signal context supplied to the handler.
|
|
1349
|
+
* @returns Raw handler result after commit.
|
|
1350
|
+
*/
|
|
1351
|
+
async #invokeAndCommitAssignee(entity, handler, message, context) {
|
|
1352
|
+
const produced = await EntityInvocation.invokeEntityMethod(entity, handler.methodName, message, handler.parameterCount, context);
|
|
1353
|
+
const signals = this.#support.normalizeProducedSignals(produced);
|
|
1354
|
+
if (signals.length === 0)
|
|
1355
|
+
throw receptionObservation.recordPostHandlerFailure(new Error("Repository aggregate command handlers must return at least one event."));
|
|
1356
|
+
RepositoryHandlers.requireDeclaredOutputs(handler, signals);
|
|
1357
|
+
const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current, true));
|
|
1358
|
+
if (commit.status === "rejected")
|
|
1359
|
+
throw EntityInvocation.rejectedTransition(commit.validation.error);
|
|
1360
|
+
return produced;
|
|
1250
1361
|
}
|
|
1251
1362
|
/**
|
|
1252
1363
|
* Binds each produced result to the target and pre-dispatch version.
|
|
@@ -1380,10 +1491,9 @@ class AggregateEventExecution {
|
|
|
1380
1491
|
await this.#support.appendDiagnosticEvent(loaded, entityId, source);
|
|
1381
1492
|
return;
|
|
1382
1493
|
}
|
|
1383
|
-
|
|
1494
|
+
await this.#support.persistAggregateAndDispatch(loaded, entityId, produced, (event) => this.#runtime.publisher.publishReactorEvent(event), () => {
|
|
1384
1495
|
this.#publishStateChange(loaded, entityId);
|
|
1385
1496
|
}, [source, ...produced]);
|
|
1386
|
-
void dispatch();
|
|
1387
1497
|
}
|
|
1388
1498
|
/**
|
|
1389
1499
|
* Publishes an Entity state-change System Event when the Aggregate changed.
|
|
@@ -1421,16 +1531,23 @@ class AggregateEventExecution {
|
|
|
1421
1531
|
async #invokeHandlers(entityId, loaded, intake) {
|
|
1422
1532
|
const eventContext = EntityInvocation.eventHandlerContext(this.#event);
|
|
1423
1533
|
const events = [];
|
|
1424
|
-
|
|
1534
|
+
const releaseActor = entityHistoryAccess.bindActor(loaded.entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
|
|
1535
|
+
let started = false;
|
|
1425
1536
|
try {
|
|
1537
|
+
transactionalEntityAccess.start(loaded.entity);
|
|
1538
|
+
started = true;
|
|
1426
1539
|
await this.#invokeReactors(entityId, loaded, intake, eventContext, events);
|
|
1427
1540
|
await this.#commitEntity(loaded.entity, events.length > 0);
|
|
1428
1541
|
return Object.freeze(events);
|
|
1429
1542
|
}
|
|
1430
1543
|
catch (error) {
|
|
1431
|
-
|
|
1544
|
+
if (started)
|
|
1545
|
+
transactionalEntityAccess.rollback(loaded.entity);
|
|
1432
1546
|
throw error;
|
|
1433
1547
|
}
|
|
1548
|
+
finally {
|
|
1549
|
+
releaseActor();
|
|
1550
|
+
}
|
|
1434
1551
|
}
|
|
1435
1552
|
/**
|
|
1436
1553
|
* Invokes each reactor, checks its declared output, and collects bound Events.
|
|
@@ -1559,7 +1676,7 @@ class ProjectionEventExecution {
|
|
|
1559
1676
|
* Delivers an accepted Event for one Projection target.
|
|
1560
1677
|
*
|
|
1561
1678
|
* @param entityId Target Projection identifier.
|
|
1562
|
-
* @param acceptedRoute Route accepted before
|
|
1679
|
+
* @param acceptedRoute Route accepted before deliver.
|
|
1563
1680
|
* @returns Completion after target delivery.
|
|
1564
1681
|
*/
|
|
1565
1682
|
async runTarget(entityId, acceptedRoute) {
|
|
@@ -1734,20 +1851,26 @@ class ProjectionEventExecution {
|
|
|
1734
1851
|
* @returns Completion after the subscriber transaction commits.
|
|
1735
1852
|
*/
|
|
1736
1853
|
async #invokeSubscribers(entity, subscribers, packedMessage) {
|
|
1737
|
-
|
|
1854
|
+
const releaseActor = entityHistoryAccess.bindActor(entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
|
|
1855
|
+
let started = false;
|
|
1738
1856
|
try {
|
|
1857
|
+
transactionalEntityAccess.start(entity);
|
|
1858
|
+
started = true;
|
|
1739
1859
|
for (const subscriber of subscribers) {
|
|
1740
1860
|
await this.#invokeSubscriber(entity, subscriber, packedMessage);
|
|
1741
1861
|
}
|
|
1742
1862
|
const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
|
|
1743
|
-
if (commit.status === "rejected")
|
|
1863
|
+
if (commit.status === "rejected")
|
|
1744
1864
|
throw EntityInvocation.rejectedTransition(commit.validation.error);
|
|
1745
|
-
}
|
|
1746
1865
|
}
|
|
1747
1866
|
catch (error) {
|
|
1748
|
-
|
|
1867
|
+
if (started)
|
|
1868
|
+
transactionalEntityAccess.rollback(entity);
|
|
1749
1869
|
throw error;
|
|
1750
1870
|
}
|
|
1871
|
+
finally {
|
|
1872
|
+
releaseActor();
|
|
1873
|
+
}
|
|
1751
1874
|
}
|
|
1752
1875
|
/**
|
|
1753
1876
|
* Invokes one Event subscriber and checks its raw return value.
|
|
@@ -1773,22 +1896,40 @@ class ProjectionEventExecution {
|
|
|
1773
1896
|
* @returns Completion after the subscriber transaction commits.
|
|
1774
1897
|
*/
|
|
1775
1898
|
async #invokeStateSubscribers(entity, subscribers, state) {
|
|
1776
|
-
|
|
1899
|
+
const releaseActor = entityHistoryAccess.bindActor(entity, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext);
|
|
1900
|
+
let started = false;
|
|
1777
1901
|
try {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
if (result !== undefined)
|
|
1782
|
-
throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
|
|
1783
|
-
}
|
|
1902
|
+
transactionalEntityAccess.start(entity);
|
|
1903
|
+
started = true;
|
|
1904
|
+
await this.#runStateSubscribers(entity, subscribers, state);
|
|
1784
1905
|
const commit = await commitFenced(entity, (current) => transactionalEntityAccess.commit(current));
|
|
1785
1906
|
if (commit.status === "rejected")
|
|
1786
1907
|
throw EntityInvocation.rejectedTransition(commit.validation.error);
|
|
1787
1908
|
}
|
|
1788
1909
|
catch (error) {
|
|
1789
|
-
|
|
1910
|
+
if (started)
|
|
1911
|
+
transactionalEntityAccess.rollback(entity);
|
|
1790
1912
|
throw error;
|
|
1791
1913
|
}
|
|
1914
|
+
finally {
|
|
1915
|
+
releaseActor();
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Delivers one state update to each declared Projection subscriber.
|
|
1920
|
+
*
|
|
1921
|
+
* @param entity Projection currently in its transaction.
|
|
1922
|
+
* @param subscribers Subscribers selected for the source state.
|
|
1923
|
+
* @param state Decoded source Entity state.
|
|
1924
|
+
* @returns Completion after all subscribers accept the state.
|
|
1925
|
+
*/
|
|
1926
|
+
async #runStateSubscribers(entity, subscribers, state) {
|
|
1927
|
+
for (const subscriber of subscribers) {
|
|
1928
|
+
const context = EntityInvocation.eventHandlerContext(this.#event);
|
|
1929
|
+
const result = await EntityInvocation.invokeEntityMethod(entity, subscriber.handler.methodName, state, subscriber.handler.parameterCount, context);
|
|
1930
|
+
if (result !== undefined)
|
|
1931
|
+
throw receptionObservation.recordPostHandlerFailure(new Error(`Projection subscriber "${subscriber.handler.methodName}" must not return signals.`));
|
|
1932
|
+
}
|
|
1792
1933
|
}
|
|
1793
1934
|
/**
|
|
1794
1935
|
* Restores or creates a Projection and binds its storage history.
|
|
@@ -1867,7 +2008,11 @@ const ProcessManagerQueries = Object.freeze({
|
|
|
1867
2008
|
* @returns Detached Projection state snapshots.
|
|
1868
2009
|
*/
|
|
1869
2010
|
async load(stand, registeredSchema, plan, destination, query, schema) {
|
|
1870
|
-
const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query
|
|
2011
|
+
const results = await QueryReader.read(stand, registeredSchema, plan, destination, 10_000, query, {
|
|
2012
|
+
operation: { kind: "query", query },
|
|
2013
|
+
...(query.context === undefined ? {} : { actorContext: query.context }),
|
|
2014
|
+
...(destination === undefined ? {} : { effectiveTenant: destination }),
|
|
2015
|
+
});
|
|
1871
2016
|
return Object.freeze(results.map((result) => clone(schema, result.state)));
|
|
1872
2017
|
},
|
|
1873
2018
|
});
|
|
@@ -2503,7 +2648,7 @@ class AgentLiveRunner {
|
|
|
2503
2648
|
* @returns The handler bound to current protected history and AI capabilities.
|
|
2504
2649
|
*/
|
|
2505
2650
|
bind(entity, facade, reads, actor) {
|
|
2506
|
-
const history = AgentHistoryReads.withReadInterceptor(entity, (scope, request, live, maxBytes) => reads.history(scope, request, live, maxBytes));
|
|
2651
|
+
const history = AgentHistoryReads.withReadInterceptor(entity, (scope, request, live, maxBytes, present) => reads.history(scope, request, live, maxBytes, present));
|
|
2507
2652
|
let query;
|
|
2508
2653
|
try {
|
|
2509
2654
|
query = ProcessManagerQueries.bind(entity, this.runtime, actor, this.tenantId, (schema, request, live) => reads.query(schema, request, live));
|
|
@@ -2767,27 +2912,29 @@ class ProcessManagerCommandExecution {
|
|
|
2767
2912
|
/**
|
|
2768
2913
|
* Invokes the registered Command handler and commits its results.
|
|
2769
2914
|
*
|
|
2770
|
-
* @param
|
|
2771
|
-
* @returns
|
|
2915
|
+
* @param deliveredRoute Accepted route from durable inbox delivery, when present.
|
|
2916
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
2772
2917
|
*/
|
|
2773
|
-
async run(
|
|
2918
|
+
async run(deliveredRoute) {
|
|
2774
2919
|
RepositorySignals.requireCommandId(this.#command);
|
|
2775
|
-
const intake = await this.#readIntake(
|
|
2920
|
+
const intake = await this.#readIntake(deliveredRoute);
|
|
2776
2921
|
if (intake === undefined)
|
|
2777
2922
|
return undefined;
|
|
2778
2923
|
const tenantOptions = RepositoryTenants.commandStandOptions(this.#runtime.context, this.#command);
|
|
2779
2924
|
const loaded = await this.#support.load(intake.route.entityId, tenantOptions);
|
|
2780
2925
|
await this.#publishDispatch(loaded, intake.route.entityId);
|
|
2926
|
+
let produced;
|
|
2781
2927
|
try {
|
|
2782
|
-
|
|
2783
|
-
return await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
|
|
2928
|
+
produced = await this.#invoke(loaded.entity, intake.assignee, intake.message, tenantOptions.tenantId);
|
|
2784
2929
|
}
|
|
2785
2930
|
catch (error) {
|
|
2786
2931
|
if (!RejectionThrowable.is(error))
|
|
2787
2932
|
throw error;
|
|
2788
2933
|
RepositorySignals.requireDeclaredRejection(intake.assignee.handler, error);
|
|
2789
|
-
|
|
2934
|
+
await RepositorySignals.postRejectionEvent(this.#runtime, this.#repository, this.#command, intake.route.entityId, error);
|
|
2935
|
+
return undefined;
|
|
2790
2936
|
}
|
|
2937
|
+
await this.#commitAndPublish(loaded, tenantOptions, intake, produced);
|
|
2791
2938
|
}
|
|
2792
2939
|
/**
|
|
2793
2940
|
* Persists required Agent dispatch history before the handler runs.
|
|
@@ -2817,14 +2964,14 @@ class ProcessManagerCommandExecution {
|
|
|
2817
2964
|
/**
|
|
2818
2965
|
* Decodes a registered Command and finds its assignee.
|
|
2819
2966
|
*
|
|
2820
|
-
* @param
|
|
2967
|
+
* @param deliveredRoute Accepted inbox route, when present.
|
|
2821
2968
|
* @returns Decoded Command, route, and assignee, or `undefined`.
|
|
2822
2969
|
*/
|
|
2823
|
-
async #readIntake(
|
|
2970
|
+
async #readIntake(deliveredRoute) {
|
|
2824
2971
|
const commandMessage = EntityInvocation.requireSignalMessage(this.#command.message, "command");
|
|
2825
2972
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(this.#routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
2826
2973
|
const message = EntityInvocation.unpackRequired(commandMessage, commandSchema, "command");
|
|
2827
|
-
const route =
|
|
2974
|
+
const route = deliveredRoute ?? (await this.#repository.routeCommand(this.#command));
|
|
2828
2975
|
const assignee = this.#routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
2829
2976
|
return assignee === undefined ? undefined : Object.freeze({ assignee, message, route });
|
|
2830
2977
|
}
|
|
@@ -2835,7 +2982,7 @@ class ProcessManagerCommandExecution {
|
|
|
2835
2982
|
* @param tenantOptions Stand tenant selection.
|
|
2836
2983
|
* @param intake Assignee and target route.
|
|
2837
2984
|
* @param producedSignals Validated handler results.
|
|
2838
|
-
* @returns
|
|
2985
|
+
* @returns Completion after produced-signal handoff attempts.
|
|
2839
2986
|
*/
|
|
2840
2987
|
async #commitAndPublish(loaded, tenantOptions, intake, producedSignals) {
|
|
2841
2988
|
const commands = this.#commandOutputs(intake.assignee, producedSignals);
|
|
@@ -2844,12 +2991,10 @@ class ProcessManagerCommandExecution {
|
|
|
2844
2991
|
if (!committed)
|
|
2845
2992
|
return undefined;
|
|
2846
2993
|
this.#publishChangedState(loaded, intake.route.entityId);
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
await this.#postCommands(commands, this.#command);
|
|
2852
|
-
};
|
|
2994
|
+
await withoutDeliveryCommitFence(async () => {
|
|
2995
|
+
await this.#postEvents(events);
|
|
2996
|
+
await this.#postCommands(commands, this.#command);
|
|
2997
|
+
});
|
|
2853
2998
|
}
|
|
2854
2999
|
/**
|
|
2855
3000
|
* Binds substitution results as Commands and requires nonempty output.
|
|
@@ -3012,15 +3157,15 @@ class ProcessManagerCommandExecution {
|
|
|
3012
3157
|
}
|
|
3013
3158
|
}
|
|
3014
3159
|
/**
|
|
3015
|
-
*
|
|
3160
|
+
* Publishes the Process Manager Command's produced Events after its commit.
|
|
3016
3161
|
*
|
|
3017
3162
|
* @param events Bound Events to publish.
|
|
3163
|
+
* @returns Completion after each Event handoff attempt.
|
|
3018
3164
|
*/
|
|
3019
|
-
#postEvents(events) {
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
}
|
|
3165
|
+
async #postEvents(events) {
|
|
3166
|
+
await Promise.all(events.map((event) => {
|
|
3167
|
+
return this.#runtime.publisher.publishReactorEvent(event);
|
|
3168
|
+
}));
|
|
3024
3169
|
}
|
|
3025
3170
|
}
|
|
3026
3171
|
/**
|
|
@@ -3060,16 +3205,16 @@ class ProcessManagerEventExecution {
|
|
|
3060
3205
|
}
|
|
3061
3206
|
this.#validateSourceEventIdForFollowUps(intake);
|
|
3062
3207
|
if (intake.route.entityIds.length === 1) {
|
|
3063
|
-
await InboxHandoff.
|
|
3208
|
+
await InboxHandoff.handoffEntityEvent(this.#repository, this.#runtime, this.#event, intake.route.entityIds[0]);
|
|
3064
3209
|
return;
|
|
3065
3210
|
}
|
|
3066
|
-
await InboxHandoff.
|
|
3211
|
+
await InboxHandoff.handoffEntityEvents(this.#repository, this.#runtime, this.#event, intake.route.entityIds);
|
|
3067
3212
|
}
|
|
3068
3213
|
/**
|
|
3069
3214
|
* Delivers a routed Event for one target under the duplicate-dispatch guard.
|
|
3070
3215
|
*
|
|
3071
3216
|
* @param entityId Target Entity identifier.
|
|
3072
|
-
* @param acceptedRoute Route accepted before
|
|
3217
|
+
* @param acceptedRoute Route accepted before deliver.
|
|
3073
3218
|
* @returns Completion after guarded target execution.
|
|
3074
3219
|
*/
|
|
3075
3220
|
async runTarget(entityId, acceptedRoute) {
|
|
@@ -3123,8 +3268,10 @@ class ProcessManagerEventExecution {
|
|
|
3123
3268
|
if (!committed)
|
|
3124
3269
|
return;
|
|
3125
3270
|
this.#publishChangedState(loaded, entityId);
|
|
3126
|
-
|
|
3127
|
-
|
|
3271
|
+
await withoutDeliveryCommitFence(async () => {
|
|
3272
|
+
await this.#postEvents(events);
|
|
3273
|
+
await this.#postCommands(commands);
|
|
3274
|
+
});
|
|
3128
3275
|
}
|
|
3129
3276
|
/**
|
|
3130
3277
|
* Publishes a state-change System Event when the Entity changed.
|
|
@@ -3170,17 +3317,18 @@ class ProcessManagerEventExecution {
|
|
|
3170
3317
|
const commands = [];
|
|
3171
3318
|
const events = [];
|
|
3172
3319
|
const releaseQuery = ProcessManagerQueries.bind(entity, this.#runtime, this.#runtime.signalMetadata.originFromEvent(this.#event).actorContext, tenantId);
|
|
3173
|
-
|
|
3320
|
+
let started = false;
|
|
3174
3321
|
try {
|
|
3322
|
+
transactionalEntityAccess.start(entity);
|
|
3323
|
+
started = true;
|
|
3175
3324
|
await this.#invokeEventHandlers(entityId, entity, intake, eventContext, events, commands);
|
|
3176
3325
|
await this.#commitEntity(entity, events.length > 0);
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
events: Object.freeze(events),
|
|
3180
|
-
});
|
|
3326
|
+
const emitted = { commands: Object.freeze(commands), events: Object.freeze(events) };
|
|
3327
|
+
return Object.freeze(emitted);
|
|
3181
3328
|
}
|
|
3182
3329
|
catch (error) {
|
|
3183
|
-
|
|
3330
|
+
if (started)
|
|
3331
|
+
transactionalEntityAccess.rollback(entity);
|
|
3184
3332
|
throw error;
|
|
3185
3333
|
}
|
|
3186
3334
|
finally {
|
|
@@ -3301,23 +3449,24 @@ class ProcessManagerEventExecution {
|
|
|
3301
3449
|
* Publishes each produced Command in handler order.
|
|
3302
3450
|
*
|
|
3303
3451
|
* @param commands Bound Commands to publish.
|
|
3452
|
+
* @returns Completion after each Command handoff attempt.
|
|
3304
3453
|
*/
|
|
3305
|
-
#postCommands(commands) {
|
|
3454
|
+
async #postCommands(commands) {
|
|
3306
3455
|
for (const command of commands) {
|
|
3307
3456
|
// spine-log-boundary: server.process_manager_command_follow_up
|
|
3308
|
-
|
|
3457
|
+
await this.#runtime.publisher.publishCommand(command);
|
|
3309
3458
|
}
|
|
3310
3459
|
}
|
|
3311
3460
|
/**
|
|
3312
|
-
*
|
|
3461
|
+
* Publishes the Process Manager Event's produced Events after its commit.
|
|
3313
3462
|
*
|
|
3314
3463
|
* @param events Bound Events to publish.
|
|
3464
|
+
* @returns Completion after each Event handoff attempt.
|
|
3315
3465
|
*/
|
|
3316
|
-
#postEvents(events) {
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
}
|
|
3466
|
+
async #postEvents(events) {
|
|
3467
|
+
await Promise.all(events.map((event) => {
|
|
3468
|
+
return this.#runtime.publisher.publishReactorEvent(event);
|
|
3469
|
+
}));
|
|
3321
3470
|
}
|
|
3322
3471
|
}
|
|
3323
3472
|
/**
|
|
@@ -3758,22 +3907,50 @@ const RepositoryEntities = {
|
|
|
3758
3907
|
throw new Error("Agent history storage is required.");
|
|
3759
3908
|
const lifetime = RepositoryStorage.lifetime(repository);
|
|
3760
3909
|
const runtime = repositoryRuntimes.get(repository);
|
|
3761
|
-
|
|
3910
|
+
if (runtime === undefined)
|
|
3911
|
+
throw new Error("Agent history requires its active runtime.");
|
|
3912
|
+
AgentHistoryReads.bind(entity, this.agentHistoryBinding(repository, entity, entityId, input, history, runtime, lifetime));
|
|
3913
|
+
},
|
|
3914
|
+
/**
|
|
3915
|
+
* Captures retained Agent history admission and outward filtering in one binding.
|
|
3916
|
+
*
|
|
3917
|
+
* @param repository Repository tracking the active runtime.
|
|
3918
|
+
* @param entity Restored Agent instance.
|
|
3919
|
+
* @param entityId Typed Agent identifier.
|
|
3920
|
+
* @param input Retained storage scope.
|
|
3921
|
+
* @param history Indexed Agent history handle.
|
|
3922
|
+
* @param runtime Current context services.
|
|
3923
|
+
* @param lifetime Repository storage lifetime.
|
|
3924
|
+
* @returns History binding with an invocation-time actor snapshot.
|
|
3925
|
+
*/
|
|
3926
|
+
agentHistoryBinding(repository, entity, entityId, input, history, runtime, lifetime) {
|
|
3927
|
+
return {
|
|
3762
3928
|
storage: history,
|
|
3763
3929
|
entityId,
|
|
3764
3930
|
onAdmit: async (onRead) => {
|
|
3765
|
-
if (
|
|
3931
|
+
if (repositoryRuntimes.get(repository) !== runtime)
|
|
3766
3932
|
throw new Error("Agent history requires its active runtime.");
|
|
3767
3933
|
await runtime.onAdmit(input.context.tenantId);
|
|
3768
3934
|
return RepositoryStorage.track(repository, onRead, lifetime);
|
|
3769
3935
|
},
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3936
|
+
captureActor: () => entityHistoryAccess.currentActor(entity),
|
|
3937
|
+
filterPage: (page, view, request, actorContext) => RepositoryAgentReads.visibleHistory(runtime.stand, page, entityId, view, request, input.context.tenantId, actorContext === undefined ? {} : { actorContext }),
|
|
3938
|
+
scope: this.agentHistoryScope(input, entityId),
|
|
3939
|
+
};
|
|
3940
|
+
},
|
|
3941
|
+
/**
|
|
3942
|
+
* Returns the exact retained Agent repository and typed ID for cursor validation.
|
|
3943
|
+
* @param input Captured Agent storage scope.
|
|
3944
|
+
* @param entityId Typed Agent identifier.
|
|
3945
|
+
* @returns Stable scope encoded in history cursors.
|
|
3946
|
+
*/
|
|
3947
|
+
agentHistoryScope(input, entityId) {
|
|
3948
|
+
return {
|
|
3949
|
+
context: input.context.name,
|
|
3950
|
+
tenant: JSON.stringify(input.context.tenantId ?? null),
|
|
3951
|
+
repository: input.sourceType.typeName,
|
|
3952
|
+
entity: input.id.key(entityId),
|
|
3953
|
+
};
|
|
3777
3954
|
},
|
|
3778
3955
|
/**
|
|
3779
3956
|
* Restores an Entity from Stand or creates a fresh instance at Version zero.
|
|
@@ -3959,18 +4136,18 @@ const RepositorySignals = {
|
|
|
3959
4136
|
});
|
|
3960
4137
|
},
|
|
3961
4138
|
/**
|
|
3962
|
-
* Builds a rejection Event and
|
|
4139
|
+
* Builds a rejection Event and awaits one publication attempt.
|
|
3963
4140
|
*
|
|
3964
4141
|
* @param runtime Context signal and publication services.
|
|
3965
4142
|
* @param repository Repository of the rejected Command.
|
|
3966
4143
|
* @param command Command rejected by its handler.
|
|
3967
4144
|
* @param entityId Target Entity identifier.
|
|
3968
4145
|
* @param rejection Declared handler rejection.
|
|
3969
|
-
* @returns
|
|
4146
|
+
* @returns Completion after the rejection handoff attempt settles.
|
|
3970
4147
|
*/
|
|
3971
|
-
postRejectionEvent(runtime, repository, command, entityId, rejection) {
|
|
4148
|
+
async postRejectionEvent(runtime, repository, command, entityId, rejection) {
|
|
3972
4149
|
const event = this.rejectionEvent(runtime, repository, command, entityId, rejection);
|
|
3973
|
-
|
|
4150
|
+
await withoutDeliveryCommitFence(async () => {
|
|
3974
4151
|
try {
|
|
3975
4152
|
// spine-log-boundary: server.repository_rejection_follow_up
|
|
3976
4153
|
await runtime.publisher.publishRejectionEvent(event);
|
|
@@ -3978,7 +4155,7 @@ const RepositorySignals = {
|
|
|
3978
4155
|
catch (error) {
|
|
3979
4156
|
runtime.publisher.reportFailure("event", event, error);
|
|
3980
4157
|
}
|
|
3981
|
-
};
|
|
4158
|
+
});
|
|
3982
4159
|
},
|
|
3983
4160
|
/**
|
|
3984
4161
|
* Creates the original declared rejection envelope without publishing it.
|
|
@@ -4565,13 +4742,13 @@ const RepositoryTenants = {
|
|
|
4565
4742
|
return RepositoryTenants.require(tenantId);
|
|
4566
4743
|
},
|
|
4567
4744
|
/**
|
|
4568
|
-
* Validates an Event tenant before
|
|
4745
|
+
* Validates an Event tenant before Entity Inbox handoff.
|
|
4569
4746
|
*
|
|
4570
4747
|
* @param context Bounded context storage mode.
|
|
4571
|
-
* @param event Event entering
|
|
4748
|
+
* @param event Event entering Entity Inbox delivery.
|
|
4572
4749
|
* @returns Validated tenant, or `undefined` for single-tenant contexts.
|
|
4573
4750
|
*/
|
|
4574
|
-
|
|
4751
|
+
requireEntityEventTenant(context, event) {
|
|
4575
4752
|
if (!context.multitenant) {
|
|
4576
4753
|
return undefined;
|
|
4577
4754
|
}
|
|
@@ -6310,7 +6487,7 @@ const RepositoryStorage = {
|
|
|
6310
6487
|
};
|
|
6311
6488
|
Object.freeze(RepositoryStorage);
|
|
6312
6489
|
/**
|
|
6313
|
-
*
|
|
6490
|
+
* Provides internal repository history operations.
|
|
6314
6491
|
*/
|
|
6315
6492
|
const RepositoryHistoryInternals = {
|
|
6316
6493
|
/**
|
|
@@ -6326,30 +6503,89 @@ const RepositoryHistoryInternals = {
|
|
|
6326
6503
|
bindEntityHistory(repository, entity, storage, entityId, schema, tenantId) {
|
|
6327
6504
|
const lifetime = RepositoryStorage.lifetime(repository);
|
|
6328
6505
|
const runtime = repositoryRuntimes.get(repository);
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
await runtime.onAdmit(tenantId);
|
|
6333
|
-
};
|
|
6506
|
+
if (runtime === undefined)
|
|
6507
|
+
throw new Error("Repository history requires its active runtime.");
|
|
6508
|
+
const onAdmit = () => this.admitHistory(repository, runtime, tenantId);
|
|
6334
6509
|
const { stateCache, eventCache } = this.boundHistoryCaches(storage, entityId, schema);
|
|
6335
6510
|
entityStateHistoryCaches.set(entity, stateCache);
|
|
6336
6511
|
entityHistoryAccess.bind(entity, {
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6512
|
+
...this.historyReaders({
|
|
6513
|
+
repository,
|
|
6514
|
+
lifetime,
|
|
6515
|
+
stand: runtime.stand,
|
|
6516
|
+
storage,
|
|
6517
|
+
schema,
|
|
6518
|
+
entityId,
|
|
6519
|
+
tenantId,
|
|
6520
|
+
entity,
|
|
6521
|
+
onAdmit,
|
|
6522
|
+
stateCache,
|
|
6523
|
+
eventCache,
|
|
6524
|
+
}),
|
|
6349
6525
|
stateMaintenance: this.boundStateMaintenance(repository, lifetime, storage.states, onAdmit),
|
|
6350
6526
|
eventMaintenance: this.boundEventMaintenance(repository, lifetime, storage.events, onAdmit),
|
|
6351
6527
|
});
|
|
6352
6528
|
},
|
|
6529
|
+
/**
|
|
6530
|
+
* Admits retained history provider work only while repository membership remains current.
|
|
6531
|
+
*
|
|
6532
|
+
* @param repository Repository whose runtime must remain current.
|
|
6533
|
+
* @param runtime Captured context runtime.
|
|
6534
|
+
* @param tenantId Bound tenant when multitenant.
|
|
6535
|
+
* @returns Completion after membership admission.
|
|
6536
|
+
*/
|
|
6537
|
+
async admitHistory(repository, runtime, tenantId) {
|
|
6538
|
+
if (repositoryRuntimes.get(repository) !== runtime)
|
|
6539
|
+
throw new Error("Repository history requires its active runtime.");
|
|
6540
|
+
await runtime.onAdmit(tenantId);
|
|
6541
|
+
},
|
|
6542
|
+
/**
|
|
6543
|
+
* Captures each protected history call's actor and request before admission awaits.
|
|
6544
|
+
*
|
|
6545
|
+
* @param scope Bound repository, caches, and admission work.
|
|
6546
|
+
* @returns State and Event read callbacks for the Entity.
|
|
6547
|
+
*/
|
|
6548
|
+
historyReaders(scope) {
|
|
6549
|
+
return {
|
|
6550
|
+
stateAt: (time) => this.historyStateAt(scope, time),
|
|
6551
|
+
states: (depth) => this.historyStates(scope, depth),
|
|
6552
|
+
events: (depth) => this.historyEvents(scope, depth),
|
|
6553
|
+
};
|
|
6554
|
+
},
|
|
6555
|
+
/**
|
|
6556
|
+
* Captures an Entity's state-at request and actor before membership awaits.
|
|
6557
|
+
* @param scope Bound history resources.
|
|
6558
|
+
* @param time Caller-selected historical time.
|
|
6559
|
+
* @returns Historical state, if retained and visible.
|
|
6560
|
+
*/
|
|
6561
|
+
async historyStateAt(scope, time) {
|
|
6562
|
+
const actor = entityHistoryAccess.currentActor(scope.entity);
|
|
6563
|
+
const captured = clone(TimestampSchema, time);
|
|
6564
|
+
await scope.onAdmit();
|
|
6565
|
+
return this.readBoundStateAt(scope.repository, scope.lifetime, scope.stand, scope.storage, scope.schema, scope.entityId, captured, scope.tenantId, actor);
|
|
6566
|
+
},
|
|
6567
|
+
/**
|
|
6568
|
+
* Captures an Entity state-history depth and actor before membership awaits.
|
|
6569
|
+
* @param scope Bound history resources.
|
|
6570
|
+
* @param depth Number of prior states requested.
|
|
6571
|
+
* @returns Visible historical states in retained order.
|
|
6572
|
+
*/
|
|
6573
|
+
async historyStates(scope, depth) {
|
|
6574
|
+
const actor = entityHistoryAccess.currentActor(scope.entity);
|
|
6575
|
+
await scope.onAdmit();
|
|
6576
|
+
return this.readBoundStates(scope.repository, scope.lifetime, scope.stand, scope.schema, scope.stateCache, scope.entityId, depth, scope.tenantId, actor);
|
|
6577
|
+
},
|
|
6578
|
+
/**
|
|
6579
|
+
* Captures an Entity Event-history depth and actor before membership awaits.
|
|
6580
|
+
* @param scope Bound history resources.
|
|
6581
|
+
* @param depth Number of prior Events requested.
|
|
6582
|
+
* @returns Visible retained Event envelopes.
|
|
6583
|
+
*/
|
|
6584
|
+
async historyEvents(scope, depth) {
|
|
6585
|
+
const actor = entityHistoryAccess.currentActor(scope.entity);
|
|
6586
|
+
await scope.onAdmit();
|
|
6587
|
+
return this.readBoundEvents(scope.repository, scope.lifetime, scope.stand, scope.eventCache, scope.entityId, depth, scope.tenantId, actor);
|
|
6588
|
+
},
|
|
6353
6589
|
/**
|
|
6354
6590
|
* Creates state and Event caches over retained history ports.
|
|
6355
6591
|
*
|
|
@@ -6406,38 +6642,86 @@ const RepositoryHistoryInternals = {
|
|
|
6406
6642
|
*
|
|
6407
6643
|
* @param repository Registered repository.
|
|
6408
6644
|
* @param lifetime Captured repository storage lifetime.
|
|
6645
|
+
* @param stand Producing context's outward read policy.
|
|
6409
6646
|
* @param storage Retained Entity read port.
|
|
6647
|
+
* @param schema Historical state schema.
|
|
6410
6648
|
* @param entityId Captured Entity identifier.
|
|
6411
6649
|
* @param time Historical instant.
|
|
6650
|
+
* @param tenantId Effective tenant of the bound Entity.
|
|
6651
|
+
* @param actorContext Actor metadata of an active handler, when present.
|
|
6412
6652
|
* @returns Historical state, if present.
|
|
6413
6653
|
*/
|
|
6414
|
-
readBoundStateAt(repository, lifetime, storage, entityId, time) {
|
|
6415
|
-
return RepositoryStorage.track(repository, async () =>
|
|
6654
|
+
readBoundStateAt(repository, lifetime, stand, storage, schema, entityId, time, tenantId, actorContext) {
|
|
6655
|
+
return RepositoryStorage.track(repository, async () => {
|
|
6656
|
+
const state = this.cloneHistoryState(await storage.states.stateAt(entityId, time));
|
|
6657
|
+
if (state === undefined)
|
|
6658
|
+
return undefined;
|
|
6659
|
+
const visible = await standAccess.filterState(stand, schema, state, {
|
|
6660
|
+
operation: { kind: "history", history: { source: "entity-state-at", entityId, time } },
|
|
6661
|
+
...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
|
|
6662
|
+
...(actorContext === undefined ? {} : { actorContext }),
|
|
6663
|
+
});
|
|
6664
|
+
return visible ?? undefined;
|
|
6665
|
+
}, lifetime);
|
|
6416
6666
|
},
|
|
6417
6667
|
/**
|
|
6418
6668
|
* Reads a complete bound state-history page within repository admission.
|
|
6419
6669
|
*
|
|
6420
6670
|
* @param repository Registered repository.
|
|
6421
6671
|
* @param lifetime Captured repository storage lifetime.
|
|
6672
|
+
* @param stand Producing context's outward read policy.
|
|
6422
6673
|
* @param schema State schema used to unpack rows.
|
|
6423
6674
|
* @param cache Bound state-history cache.
|
|
6675
|
+
* @param entityId Captured Entity identifier.
|
|
6424
6676
|
* @param depth Requested history depth.
|
|
6677
|
+
* @param tenantId Effective tenant of the bound Entity.
|
|
6678
|
+
* @param actorContext Actor metadata of an active handler, when present.
|
|
6425
6679
|
* @returns Frozen historical states.
|
|
6426
6680
|
*/
|
|
6427
|
-
readBoundStates(repository, lifetime, schema, cache, depth) {
|
|
6428
|
-
return RepositoryStorage.track(repository, async () =>
|
|
6681
|
+
readBoundStates(repository, lifetime, stand, schema, cache, entityId, depth, tenantId, actorContext) {
|
|
6682
|
+
return RepositoryStorage.track(repository, async () => {
|
|
6683
|
+
const states = this.freezeHistoryStates((await cache.read(depth)).map((record) => EntityRecords.unpack(schema, record).state));
|
|
6684
|
+
const visible = [];
|
|
6685
|
+
for (const state of states) {
|
|
6686
|
+
const next = await standAccess.filterState(stand, schema, state, {
|
|
6687
|
+
operation: { kind: "history", history: { source: "entity-states", entityId, depth } },
|
|
6688
|
+
...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
|
|
6689
|
+
...(actorContext === undefined ? {} : { actorContext }),
|
|
6690
|
+
});
|
|
6691
|
+
if (next !== null)
|
|
6692
|
+
visible.push(Object.freeze(next));
|
|
6693
|
+
}
|
|
6694
|
+
return Object.freeze(visible);
|
|
6695
|
+
}, lifetime);
|
|
6429
6696
|
},
|
|
6430
6697
|
/**
|
|
6431
6698
|
* Reads a complete bound Event-history page within repository admission.
|
|
6432
6699
|
*
|
|
6433
6700
|
* @param repository Registered repository.
|
|
6434
6701
|
* @param lifetime Captured repository storage lifetime.
|
|
6702
|
+
* @param stand Producing context's outward read policy.
|
|
6435
6703
|
* @param cache Bound Event-history cache.
|
|
6704
|
+
* @param entityId Captured Entity identifier.
|
|
6436
6705
|
* @param depth Requested history depth.
|
|
6706
|
+
* @param tenantId Effective tenant of the bound Entity.
|
|
6707
|
+
* @param actorContext Actor metadata of an active handler, when present.
|
|
6437
6708
|
* @returns Frozen historical Events.
|
|
6438
6709
|
*/
|
|
6439
|
-
readBoundEvents(repository, lifetime, cache, depth) {
|
|
6440
|
-
return RepositoryStorage.track(repository, async () =>
|
|
6710
|
+
readBoundEvents(repository, lifetime, stand, cache, entityId, depth, tenantId, actorContext) {
|
|
6711
|
+
return RepositoryStorage.track(repository, async () => {
|
|
6712
|
+
const events = this.freezeHistoryEvents(await cache.read(depth));
|
|
6713
|
+
const visible = [];
|
|
6714
|
+
for (const event of events) {
|
|
6715
|
+
const next = await standAccess.filterEvent(stand, event, {
|
|
6716
|
+
operation: { kind: "history", history: { source: "entity-events", entityId, depth } },
|
|
6717
|
+
...(tenantId === undefined ? {} : { effectiveTenant: tenantId }),
|
|
6718
|
+
...(actorContext === undefined ? {} : { actorContext }),
|
|
6719
|
+
});
|
|
6720
|
+
if (next !== null)
|
|
6721
|
+
visible.push(Object.freeze(next));
|
|
6722
|
+
}
|
|
6723
|
+
return Object.freeze(visible);
|
|
6724
|
+
}, lifetime);
|
|
6441
6725
|
},
|
|
6442
6726
|
/**
|
|
6443
6727
|
* Creates a history cache that serializes reads and extends prior results.
|
|
@@ -6974,7 +7258,7 @@ const InboxMessages = {
|
|
|
6974
7258
|
}
|
|
6975
7259
|
}
|
|
6976
7260
|
if (entityId === undefined) {
|
|
6977
|
-
throw new Error("Entity Inbox
|
|
7261
|
+
throw new Error("Entity Inbox delivery stored target ID is incompatible with the repository.");
|
|
6978
7262
|
}
|
|
6979
7263
|
return RepositoryRoutes.readRouteId(entityId, idField, "command");
|
|
6980
7264
|
},
|
|
@@ -6986,7 +7270,7 @@ const InboxMessages = {
|
|
|
6986
7270
|
*/
|
|
6987
7271
|
readInboxCommand(message) {
|
|
6988
7272
|
if (message.label !== "HANDLE_COMMAND") {
|
|
6989
|
-
throw new Error(`Entity Inbox
|
|
7273
|
+
throw new Error(`Entity Inbox delivery does not handle "${message.label}" messages.`);
|
|
6990
7274
|
}
|
|
6991
7275
|
const command = message.signal === undefined ? undefined : AnyPacker.tryUnpack(message.signal, CommandSchema);
|
|
6992
7276
|
if (command === undefined) {
|
|
@@ -6995,13 +7279,13 @@ const InboxMessages = {
|
|
|
6995
7279
|
return command;
|
|
6996
7280
|
},
|
|
6997
7281
|
/**
|
|
6998
|
-
* Reads an Event labeled for
|
|
7282
|
+
* Reads an Event labeled for an Entity reaction.
|
|
6999
7283
|
*
|
|
7000
7284
|
* @param message Stored Inbox message to validate and unpack.
|
|
7001
7285
|
* @returns The stored Event envelope.
|
|
7002
7286
|
*/
|
|
7003
|
-
|
|
7004
|
-
return InboxMessages.readStoredEvent(message, "REACT_UPON_EVENT", "Entity Inbox
|
|
7287
|
+
readEntityInboxEvent(message) {
|
|
7288
|
+
return InboxMessages.readStoredEvent(message, "REACT_UPON_EVENT", "Entity Inbox delivery", "Entity Inbox delivery requires a readable stored event.");
|
|
7005
7289
|
},
|
|
7006
7290
|
/**
|
|
7007
7291
|
* Reads an Event labeled for a Projection update.
|
|
@@ -7010,20 +7294,20 @@ const InboxMessages = {
|
|
|
7010
7294
|
* @returns The stored Event envelope.
|
|
7011
7295
|
*/
|
|
7012
7296
|
readProjectionInboxEvent(message) {
|
|
7013
|
-
return InboxMessages.readStoredEvent(message, "UPDATE_SUBSCRIBER", "Projection inbox
|
|
7297
|
+
return InboxMessages.readStoredEvent(message, "UPDATE_SUBSCRIBER", "Projection inbox delivery", "Projection inbox delivery requires a readable stored event.");
|
|
7014
7298
|
},
|
|
7015
7299
|
/**
|
|
7016
7300
|
* Validates an Inbox operation label and decodes its stored Event bytes.
|
|
7017
7301
|
*
|
|
7018
7302
|
* @param message Stored Inbox message to inspect.
|
|
7019
7303
|
* @param expectedLabel Operation label required by the caller.
|
|
7020
|
-
* @param
|
|
7304
|
+
* @param deliverName Human-readable operation name used in label errors.
|
|
7021
7305
|
* @param unreadableMessage Error text used when Event decoding fails.
|
|
7022
7306
|
* @returns The stored Event envelope.
|
|
7023
7307
|
*/
|
|
7024
|
-
readStoredEvent(message, expectedLabel,
|
|
7308
|
+
readStoredEvent(message, expectedLabel, deliverName, unreadableMessage) {
|
|
7025
7309
|
if (message.label !== expectedLabel) {
|
|
7026
|
-
throw new Error(`${
|
|
7310
|
+
throw new Error(`${deliverName} does not handle "${message.label}" messages.`);
|
|
7027
7311
|
}
|
|
7028
7312
|
const signal = message.signal === undefined
|
|
7029
7313
|
? undefined
|
|
@@ -7042,7 +7326,7 @@ Object.freeze(InboxMessages);
|
|
|
7042
7326
|
/**
|
|
7043
7327
|
* Validates and dispatches stored Inbox signals to their recorded Entity targets.
|
|
7044
7328
|
*/
|
|
7045
|
-
const
|
|
7329
|
+
const InboxDelivery = {
|
|
7046
7330
|
/**
|
|
7047
7331
|
* Dispatches a stored Aggregate Command after checking its tenant and payload.
|
|
7048
7332
|
*
|
|
@@ -7050,44 +7334,66 @@ const InboxReplay = {
|
|
|
7050
7334
|
* @param routing Registered handlers and message schemas.
|
|
7051
7335
|
* @param message Stored Inbox message with the recorded target.
|
|
7052
7336
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7053
|
-
* @returns
|
|
7337
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7054
7338
|
*/
|
|
7055
|
-
async
|
|
7339
|
+
async deliverAggregateCommand(repository, routing, message, deliveryTenantId) {
|
|
7056
7340
|
const runtime = repositoryRuntimes.get(repository);
|
|
7057
7341
|
if (runtime === undefined) {
|
|
7058
|
-
throw new Error("Aggregate inbox
|
|
7342
|
+
throw new Error("Aggregate inbox delivery requires a bound repository runtime.");
|
|
7059
7343
|
}
|
|
7060
7344
|
if (message.label !== "HANDLE_COMMAND") {
|
|
7061
|
-
throw new Error(`Aggregate inbox
|
|
7345
|
+
throw new Error(`Aggregate inbox delivery does not handle "${message.label}" messages.`);
|
|
7062
7346
|
}
|
|
7063
7347
|
const command = InboxMessages.readInboxCommand(message);
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
const route =
|
|
7067
|
-
|
|
7348
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, deliveryTenantId, command);
|
|
7349
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7350
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7351
|
+
await new AggregateCommandExecution(repository, routing, runtime, command).run(route);
|
|
7068
7352
|
},
|
|
7069
7353
|
/**
|
|
7070
|
-
*
|
|
7354
|
+
* Delivers a stored Event to the recorded Aggregate target.
|
|
7355
|
+
*
|
|
7356
|
+
* @param repository Aggregate repository receiving the Event.
|
|
7357
|
+
* @param routing Registered Event handlers and message schemas.
|
|
7358
|
+
* @param message Stored Inbox row with the recorded target.
|
|
7359
|
+
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7360
|
+
* @returns Completion after the guarded reactor and outgoing handoff attempts.
|
|
7361
|
+
*/
|
|
7362
|
+
async deliverAggregateEvent(repository, routing, message, deliveryTenantId) {
|
|
7363
|
+
const runtime = repositoryRuntimes.get(repository);
|
|
7364
|
+
if (runtime === undefined) {
|
|
7365
|
+
throw new Error("Aggregate inbox delivery requires a bound repository runtime.");
|
|
7366
|
+
}
|
|
7367
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7368
|
+
InboxDelivery.validateEventTenant(runtime.context, deliveryTenantId, event);
|
|
7369
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Aggregate inbox delivery requires a readable event payload.");
|
|
7370
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Aggregate inbox delivery");
|
|
7371
|
+
const [entityId] = route.entityIds;
|
|
7372
|
+
await DispatchGuards.guardedEntityEventDispatch(repository, runtime, event, entityId, () => new AggregateEventExecution(repository, routing, runtime, event).runTarget(entityId, route));
|
|
7373
|
+
},
|
|
7374
|
+
/**
|
|
7375
|
+
* Routes Process Manager Command or Event deliver by its stored operation label.
|
|
7071
7376
|
*
|
|
7072
7377
|
* @param repository Process Manager repository receiving the signal.
|
|
7073
7378
|
* @param routing Registered handlers and message schemas.
|
|
7074
7379
|
* @param message Stored Inbox message with the recorded target.
|
|
7075
7380
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7076
|
-
* @returns
|
|
7381
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7077
7382
|
*/
|
|
7078
|
-
async
|
|
7383
|
+
async deliverPmInbox(repository, routing, message, deliveryTenantId) {
|
|
7079
7384
|
if (repository.entityFamily === "agent") {
|
|
7080
|
-
await
|
|
7385
|
+
await InboxDelivery.deliverAgentInbox(repository, routing, message, deliveryTenantId);
|
|
7081
7386
|
return undefined;
|
|
7082
7387
|
}
|
|
7083
7388
|
if (message.label === "HANDLE_COMMAND") {
|
|
7084
|
-
|
|
7389
|
+
await InboxDelivery.deliverProcessManagerCommand(repository, routing, message, deliveryTenantId);
|
|
7390
|
+
return;
|
|
7085
7391
|
}
|
|
7086
7392
|
if (message.label === "REACT_UPON_EVENT") {
|
|
7087
|
-
await
|
|
7393
|
+
await InboxDelivery.deliverProcessManagerEvent(repository, routing, message, deliveryTenantId);
|
|
7088
7394
|
return undefined;
|
|
7089
7395
|
}
|
|
7090
|
-
throw new Error(`Entity Inbox
|
|
7396
|
+
throw new Error(`Entity Inbox delivery does not handle "${message.label}" messages.`);
|
|
7091
7397
|
},
|
|
7092
7398
|
/**
|
|
7093
7399
|
* Persists selected Agent work before the normal Inbox acknowledges delivery.
|
|
@@ -7098,17 +7404,17 @@ const InboxReplay = {
|
|
|
7098
7404
|
* @param deliveryTenantId Tenant selected by delivery.
|
|
7099
7405
|
* @returns When the fenced repository operation completes.
|
|
7100
7406
|
*/
|
|
7101
|
-
async
|
|
7407
|
+
async deliverAgentInbox(repository, routing, message, deliveryTenantId) {
|
|
7102
7408
|
const runtime = repositoryRuntimes.get(repository);
|
|
7103
7409
|
if (runtime === undefined)
|
|
7104
|
-
throw new Error("Agent Inbox
|
|
7410
|
+
throw new Error("Agent Inbox delivery requires a bound runtime.");
|
|
7105
7411
|
if (message.label === "HANDLE_COMMAND") {
|
|
7106
|
-
await
|
|
7412
|
+
await InboxDelivery.deliverAgentCommand(repository, routing, runtime, message, deliveryTenantId);
|
|
7107
7413
|
return;
|
|
7108
7414
|
}
|
|
7109
7415
|
if (message.label !== "REACT_UPON_EVENT")
|
|
7110
|
-
throw new Error(`Agent Inbox
|
|
7111
|
-
await
|
|
7416
|
+
throw new Error(`Agent Inbox delivery does not handle "${message.label}" messages.`);
|
|
7417
|
+
await InboxDelivery.deliverAgentEvent(repository, routing, runtime, message, deliveryTenantId);
|
|
7112
7418
|
},
|
|
7113
7419
|
/**
|
|
7114
7420
|
* Admits a Command using its stored target and registered assignee.
|
|
@@ -7120,15 +7426,15 @@ const InboxReplay = {
|
|
|
7120
7426
|
* @param tenantId Delivery tenant.
|
|
7121
7427
|
* @returns When the fenced repository operation completes.
|
|
7122
7428
|
*/
|
|
7123
|
-
async
|
|
7429
|
+
async deliverAgentCommand(repository, routing, runtime, message, tenantId) {
|
|
7124
7430
|
const command = InboxMessages.readInboxCommand(message);
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
const route =
|
|
7431
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, tenantId, command);
|
|
7432
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7433
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7128
7434
|
const assignee = routing.commandReadiness?.findCommandAssignee(route.messageFullTypeName);
|
|
7129
7435
|
if (assignee === undefined)
|
|
7130
7436
|
throw new Error("Agent Command has no selected assignee.");
|
|
7131
|
-
await
|
|
7437
|
+
await InboxDelivery.admitAgent(repository, runtime, message, tenantId, route.entityId, { case: "command", value: command }, [assignee.registeredHandler]);
|
|
7132
7438
|
},
|
|
7133
7439
|
/**
|
|
7134
7440
|
* Admits matching Event handlers from the stored target without rerouting.
|
|
@@ -7140,15 +7446,15 @@ const InboxReplay = {
|
|
|
7140
7446
|
* @param tenantId Delivery tenant.
|
|
7141
7447
|
* @returns When the fenced repository operation completes.
|
|
7142
7448
|
*/
|
|
7143
|
-
async
|
|
7144
|
-
const event = InboxMessages.
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
const route =
|
|
7148
|
-
const handlers =
|
|
7449
|
+
async deliverAgentEvent(repository, routing, runtime, message, tenantId) {
|
|
7450
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7451
|
+
InboxDelivery.validateEventTenant(runtime.context, tenantId, event);
|
|
7452
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Agent Inbox delivery requires an Event payload.");
|
|
7453
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Agent Inbox delivery");
|
|
7454
|
+
const handlers = InboxDelivery.agentEventHandlers(routing, route.messageFullTypeName, event);
|
|
7149
7455
|
if (handlers.length === 0)
|
|
7150
7456
|
return;
|
|
7151
|
-
await
|
|
7457
|
+
await InboxDelivery.admitAgent(repository, runtime, message, tenantId, route.entityIds[0], { case: "event", value: event }, handlers);
|
|
7152
7458
|
},
|
|
7153
7459
|
/**
|
|
7154
7460
|
* Restores selected Event and Command reactions from the original payload.
|
|
@@ -7187,14 +7493,14 @@ const InboxReplay = {
|
|
|
7187
7493
|
if (deliverySignal === undefined)
|
|
7188
7494
|
throw new Error("Agent admission requires delivery scope.");
|
|
7189
7495
|
const cancellation = AbortSignal.any([deliverySignal, runtime.agentSignal]);
|
|
7190
|
-
const key =
|
|
7496
|
+
const key = InboxDelivery.capacityKeyFor(repository, input.context, input.id.key(entityId));
|
|
7191
7497
|
const reservation = runtime.reserveAgent(key, cancellation);
|
|
7192
7498
|
if (reservation === undefined) {
|
|
7193
7499
|
runtime.deferAgent(message, tenantId);
|
|
7194
7500
|
AgentDeliveryScope.record({ kind: "deferred" });
|
|
7195
7501
|
return;
|
|
7196
7502
|
}
|
|
7197
|
-
await
|
|
7503
|
+
await InboxDelivery.admitReservedAgent({
|
|
7198
7504
|
repository,
|
|
7199
7505
|
runtime,
|
|
7200
7506
|
context: input.context,
|
|
@@ -7236,13 +7542,13 @@ const InboxReplay = {
|
|
|
7236
7542
|
async admitReservedAgent(input) {
|
|
7237
7543
|
const { repository, runtime, context, message, signal, reservation } = input;
|
|
7238
7544
|
try {
|
|
7239
|
-
const accepted = await
|
|
7545
|
+
const accepted = await InboxDelivery.acceptAgentReceipt(runtime, context, repository, message, signal);
|
|
7240
7546
|
if (!accepted) {
|
|
7241
7547
|
reservation.release();
|
|
7242
7548
|
AgentDeliveryScope.record({ kind: "duplicate" });
|
|
7243
7549
|
return;
|
|
7244
7550
|
}
|
|
7245
|
-
|
|
7551
|
+
InboxDelivery.recordAcceptedAgent(input);
|
|
7246
7552
|
}
|
|
7247
7553
|
catch (error) {
|
|
7248
7554
|
reservation.release();
|
|
@@ -7267,7 +7573,7 @@ const InboxReplay = {
|
|
|
7267
7573
|
},
|
|
7268
7574
|
run: (fence, checkSession) => {
|
|
7269
7575
|
const cancellation = AbortSignal.any([fence, runtime.agentSignal]);
|
|
7270
|
-
const checkAgentSession =
|
|
7576
|
+
const checkAgentSession = InboxDelivery.agentSessionGuard(cancellation, checkSession);
|
|
7271
7577
|
return reservation.run(() => withDeliveryCommitFence(checkAgentSession, () => new AgentLiveRunner(repository, runtime, tenantId, entityId, invocation, handlers, cancellation, checkAgentSession).run()));
|
|
7272
7578
|
},
|
|
7273
7579
|
});
|
|
@@ -7296,8 +7602,8 @@ const InboxReplay = {
|
|
|
7296
7602
|
*/
|
|
7297
7603
|
acceptAgentReceipt(runtime, context, repository, message, signal) {
|
|
7298
7604
|
const source = signal.case === "command"
|
|
7299
|
-
? { kind: "command", id:
|
|
7300
|
-
: { kind: "event", id:
|
|
7605
|
+
? { kind: "command", id: InboxDelivery.requireAgentCommandId(signal.value) }
|
|
7606
|
+
: { kind: "event", id: InboxDelivery.requireAgentEventId(signal.value) };
|
|
7301
7607
|
return runtime.agentReceipts.accept(context, {
|
|
7302
7608
|
stateType: repository.stateSchema.typeName,
|
|
7303
7609
|
recipientId: message.inboxId.targetId,
|
|
@@ -7332,18 +7638,18 @@ const InboxReplay = {
|
|
|
7332
7638
|
* @param routing Registered handlers and message schemas.
|
|
7333
7639
|
* @param message Stored Inbox message with the recorded target.
|
|
7334
7640
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7335
|
-
* @returns
|
|
7641
|
+
* @returns Completion after processing and outgoing handoff attempts.
|
|
7336
7642
|
*/
|
|
7337
|
-
async
|
|
7643
|
+
async deliverProcessManagerCommand(repository, routing, message, deliveryTenantId) {
|
|
7338
7644
|
const runtime = repositoryRuntimes.get(repository);
|
|
7339
7645
|
if (runtime === undefined) {
|
|
7340
|
-
throw new Error("Entity Inbox
|
|
7646
|
+
throw new Error("Entity Inbox delivery requires a bound repository runtime.");
|
|
7341
7647
|
}
|
|
7342
7648
|
const command = InboxMessages.readInboxCommand(message);
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
const route =
|
|
7346
|
-
|
|
7649
|
+
InboxDelivery.validateDeliveryTenant(runtime.context, deliveryTenantId, command);
|
|
7650
|
+
InboxDelivery.validateDeliveredCommandPayload(routing, command);
|
|
7651
|
+
const route = InboxDelivery.deliverCommandRoute(repository, routing, message, command);
|
|
7652
|
+
await new ProcessManagerCommandExecution(repository, routing, runtime, command).run(route);
|
|
7347
7653
|
},
|
|
7348
7654
|
/**
|
|
7349
7655
|
* Dispatches a validated Event to the Process Manager target recorded in its Inbox.
|
|
@@ -7354,15 +7660,15 @@ const InboxReplay = {
|
|
|
7354
7660
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7355
7661
|
* @returns Completion of dispatch to the recorded target.
|
|
7356
7662
|
*/
|
|
7357
|
-
async
|
|
7663
|
+
async deliverProcessManagerEvent(repository, routing, message, deliveryTenantId) {
|
|
7358
7664
|
const runtime = repositoryRuntimes.get(repository);
|
|
7359
7665
|
if (runtime === undefined) {
|
|
7360
|
-
throw new Error("Entity Inbox
|
|
7666
|
+
throw new Error("Entity Inbox delivery requires a bound repository runtime.");
|
|
7361
7667
|
}
|
|
7362
|
-
const event = InboxMessages.
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
const route =
|
|
7668
|
+
const event = InboxMessages.readEntityInboxEvent(message);
|
|
7669
|
+
InboxDelivery.validateEventTenant(runtime.context, deliveryTenantId, event);
|
|
7670
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event, "Entity Inbox delivery requires a readable event payload.");
|
|
7671
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Entity Inbox delivery");
|
|
7366
7672
|
const [entityId] = route.entityIds;
|
|
7367
7673
|
await new ProcessManagerEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
|
|
7368
7674
|
},
|
|
@@ -7375,34 +7681,34 @@ const InboxReplay = {
|
|
|
7375
7681
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7376
7682
|
* @returns Completion of dispatch to the recorded target.
|
|
7377
7683
|
*/
|
|
7378
|
-
async
|
|
7684
|
+
async deliverProjectionEvent(repository, routing, message, deliveryTenantId) {
|
|
7379
7685
|
const runtime = repositoryRuntimes.get(repository);
|
|
7380
7686
|
if (runtime === undefined) {
|
|
7381
|
-
throw new Error("Projection inbox
|
|
7687
|
+
throw new Error("Projection inbox delivery requires a bound repository runtime.");
|
|
7382
7688
|
}
|
|
7383
7689
|
const event = InboxMessages.readProjectionInboxEvent(message);
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
const route =
|
|
7690
|
+
InboxDelivery.validateProjectionDeliveryTenant(runtime.context, deliveryTenantId, event);
|
|
7691
|
+
InboxDelivery.validateDeliveredEventPayload(routing, event);
|
|
7692
|
+
const route = InboxDelivery.deliverEventRoute(repository, routing, message, event, "Projection inbox delivery");
|
|
7387
7693
|
const [entityId] = route.entityIds;
|
|
7388
7694
|
await new ProjectionEventExecution(repository, routing, runtime, event).runTarget(entityId, route);
|
|
7389
7695
|
},
|
|
7390
7696
|
/**
|
|
7391
|
-
* Routes a Projection Inbox message to domain Event or Entity-state update
|
|
7697
|
+
* Routes a Projection Inbox message to domain Event or Entity-state update deliver.
|
|
7392
7698
|
*
|
|
7393
7699
|
* @param repository Projection repository receiving the update.
|
|
7394
7700
|
* @param routing Registered subscribers and message schemas.
|
|
7395
7701
|
* @param message Stored Projection Inbox message.
|
|
7396
7702
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7397
|
-
* @returns Completion of the selected
|
|
7703
|
+
* @returns Completion of the selected deliver operation.
|
|
7398
7704
|
*/
|
|
7399
|
-
async
|
|
7705
|
+
async deliverProjectionMessage(repository, routing, message, deliveryTenantId) {
|
|
7400
7706
|
const event = InboxMessages.readProjectionInboxEvent(message);
|
|
7401
7707
|
if (event.message?.typeUrl === TypeUrls.derive(EntityLog.EntityStateChangedSchema)) {
|
|
7402
|
-
await
|
|
7708
|
+
await InboxDelivery.deliverProjectionStateUpdate(repository, routing, message, event, deliveryTenantId);
|
|
7403
7709
|
return;
|
|
7404
7710
|
}
|
|
7405
|
-
await
|
|
7711
|
+
await InboxDelivery.deliverProjectionEvent(repository, routing, message, deliveryTenantId);
|
|
7406
7712
|
},
|
|
7407
7713
|
/**
|
|
7408
7714
|
* Dispatches an Entity-state update to the Projection target recorded in its Inbox.
|
|
@@ -7414,12 +7720,12 @@ const InboxReplay = {
|
|
|
7414
7720
|
* @param deliveryTenantId Tenant selected by delivery in a multitenant context.
|
|
7415
7721
|
* @returns Completion of state-subscriber dispatch to the recorded target.
|
|
7416
7722
|
*/
|
|
7417
|
-
async
|
|
7723
|
+
async deliverProjectionStateUpdate(repository, routing, message, event, deliveryTenantId) {
|
|
7418
7724
|
const runtime = repositoryRuntimes.get(repository);
|
|
7419
7725
|
if (runtime === undefined)
|
|
7420
|
-
throw new Error("Projection inbox
|
|
7421
|
-
|
|
7422
|
-
const route =
|
|
7726
|
+
throw new Error("Projection inbox delivery requires a bound repository runtime.");
|
|
7727
|
+
InboxDelivery.validateProjectionDeliveryTenant(runtime.context, deliveryTenantId, event);
|
|
7728
|
+
const route = InboxDelivery.deliverStateUpdateRoute(repository, routing, message, event);
|
|
7423
7729
|
const [entityId] = route.entityIds;
|
|
7424
7730
|
const subscribers = route.subscribers;
|
|
7425
7731
|
await ProjectionEventExecution.runStateTarget(repository, routing, runtime, event, entityId, route, subscribers);
|
|
@@ -7428,9 +7734,9 @@ const InboxReplay = {
|
|
|
7428
7734
|
* Validates a stored Command against its schema and implicit identifier requirements.
|
|
7429
7735
|
*
|
|
7430
7736
|
* @param routing Registered Command schemas.
|
|
7431
|
-
* @param command Envelope whose payload must remain valid on
|
|
7737
|
+
* @param command Envelope whose payload must remain valid on deliver.
|
|
7432
7738
|
*/
|
|
7433
|
-
|
|
7739
|
+
validateDeliveredCommandPayload(routing, command) {
|
|
7434
7740
|
const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
|
|
7435
7741
|
const commandSchema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
7436
7742
|
const payload = AnyPacker.tryUnpack(commandMessage, commandSchema);
|
|
@@ -7455,10 +7761,10 @@ const InboxReplay = {
|
|
|
7455
7761
|
* Unpacks and validates a stored Event against its registered schema.
|
|
7456
7762
|
*
|
|
7457
7763
|
* @param routing Registered Event schemas.
|
|
7458
|
-
* @param event Envelope whose payload must remain valid on
|
|
7764
|
+
* @param event Envelope whose payload must remain valid on deliver.
|
|
7459
7765
|
* @param invalidPayloadMessage Error text used when the payload cannot be unpacked.
|
|
7460
7766
|
*/
|
|
7461
|
-
|
|
7767
|
+
validateDeliveredEventPayload(routing, event, invalidPayloadMessage = "Projection inbox delivery requires a readable event payload.") {
|
|
7462
7768
|
const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
|
|
7463
7769
|
const eventSchema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
|
|
7464
7770
|
const payload = AnyPacker.tryUnpack(eventMessage, eventSchema);
|
|
@@ -7468,69 +7774,69 @@ const InboxReplay = {
|
|
|
7468
7774
|
Validate.check(eventSchema, payload);
|
|
7469
7775
|
},
|
|
7470
7776
|
/**
|
|
7471
|
-
* Checks that a
|
|
7777
|
+
* Checks that a delivered Command's tenant matches its delivery tenant.
|
|
7472
7778
|
*
|
|
7473
7779
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7474
7780
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7475
7781
|
* @param command Stored Command containing the original actor context.
|
|
7476
7782
|
*/
|
|
7477
|
-
|
|
7783
|
+
validateDeliveryTenant(context, deliveryTenantId, command) {
|
|
7478
7784
|
if (!context.multitenant) {
|
|
7479
7785
|
return;
|
|
7480
7786
|
}
|
|
7481
7787
|
if (deliveryTenantId === undefined) {
|
|
7482
|
-
throw new Error(`Multitenant Entity Inbox
|
|
7788
|
+
throw new Error(`Multitenant Entity Inbox delivery for "${context.name}" requires tenantId.`);
|
|
7483
7789
|
}
|
|
7484
7790
|
const envelopeTenantId = RepositoryTenants.readCommandTenant(command);
|
|
7485
7791
|
if (envelopeTenantId === undefined) {
|
|
7486
|
-
throw new Error("Entity Inbox
|
|
7792
|
+
throw new Error("Entity Inbox delivery requires stored command tenant metadata.");
|
|
7487
7793
|
}
|
|
7488
7794
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7489
|
-
throw new Error("Entity Inbox
|
|
7795
|
+
throw new Error("Entity Inbox delivery stored command tenant does not match.");
|
|
7490
7796
|
}
|
|
7491
7797
|
},
|
|
7492
7798
|
/**
|
|
7493
|
-
* Checks that a
|
|
7799
|
+
* Checks that a delivered Projection Event's tenant matches its delivery tenant.
|
|
7494
7800
|
*
|
|
7495
7801
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7496
7802
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7497
7803
|
* @param event Stored Event containing the original tenant metadata.
|
|
7498
7804
|
*/
|
|
7499
|
-
|
|
7805
|
+
validateProjectionDeliveryTenant(context, deliveryTenantId, event) {
|
|
7500
7806
|
if (!context.multitenant) {
|
|
7501
7807
|
return;
|
|
7502
7808
|
}
|
|
7503
7809
|
if (deliveryTenantId === undefined) {
|
|
7504
|
-
throw new Error(`Multitenant projection inbox
|
|
7810
|
+
throw new Error(`Multitenant projection inbox delivery for "${context.name}" requires tenantId.`);
|
|
7505
7811
|
}
|
|
7506
7812
|
const envelopeTenantId = RepositoryTenants.readEventTenant(event);
|
|
7507
7813
|
if (envelopeTenantId === undefined) {
|
|
7508
|
-
throw new Error("Projection inbox
|
|
7814
|
+
throw new Error("Projection inbox delivery requires stored event tenant metadata.");
|
|
7509
7815
|
}
|
|
7510
7816
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7511
|
-
throw new Error("Projection inbox
|
|
7817
|
+
throw new Error("Projection inbox delivery stored event tenant does not match.");
|
|
7512
7818
|
}
|
|
7513
7819
|
},
|
|
7514
7820
|
/**
|
|
7515
|
-
* Checks that a
|
|
7821
|
+
* Checks that a delivered Entity Event's tenant matches its delivery tenant.
|
|
7516
7822
|
*
|
|
7517
7823
|
* @param context Context name and tenant mode; single-tenant contexts need no check.
|
|
7518
7824
|
* @param deliveryTenantId Tenant selected by Inbox delivery.
|
|
7519
7825
|
* @param event Stored Event containing the original tenant metadata.
|
|
7520
7826
|
*/
|
|
7521
|
-
|
|
7827
|
+
validateEventTenant(context, deliveryTenantId, event) {
|
|
7522
7828
|
if (!context.multitenant) {
|
|
7523
7829
|
return;
|
|
7524
7830
|
}
|
|
7525
7831
|
if (deliveryTenantId === undefined) {
|
|
7526
|
-
throw new Error(`Multitenant Entity Inbox
|
|
7832
|
+
throw new Error(`Multitenant Entity Inbox delivery for "${context.name}" requires tenantId.`);
|
|
7527
7833
|
}
|
|
7528
7834
|
const envelopeTenantId = RepositoryTenants.readEventTenant(event);
|
|
7529
7835
|
if (envelopeTenantId === undefined) {
|
|
7530
|
-
throw new Error("Entity Inbox
|
|
7836
|
+
throw new Error("Entity Inbox delivery requires stored event tenant metadata.");
|
|
7531
7837
|
}
|
|
7532
7838
|
if (!RepositoryTenants.equal(envelopeTenantId, deliveryTenantId)) {
|
|
7533
|
-
throw new Error("Entity Inbox
|
|
7839
|
+
throw new Error("Entity Inbox delivery stored event tenant does not match.");
|
|
7534
7840
|
}
|
|
7535
7841
|
},
|
|
7536
7842
|
/**
|
|
@@ -7542,10 +7848,10 @@ const InboxReplay = {
|
|
|
7542
7848
|
* @param command Stored Command used to identify the registered handler schema.
|
|
7543
7849
|
* @returns A deferred route to the recorded Entity identifier.
|
|
7544
7850
|
*/
|
|
7545
|
-
|
|
7851
|
+
deliverCommandRoute(repository, routing, message, command) {
|
|
7546
7852
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7547
7853
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7548
|
-
throw new Error("Entity Inbox
|
|
7854
|
+
throw new Error("Entity Inbox delivery stored target type does not match the routed repository.");
|
|
7549
7855
|
}
|
|
7550
7856
|
const commandMessage = EntityInvocation.requireSignalMessage(command.message, "command");
|
|
7551
7857
|
const schema = RepositoryRoutes.schemaForTypeUrl(routing.commandSchemas, commandMessage.typeUrl, "command");
|
|
@@ -7562,13 +7868,13 @@ const InboxReplay = {
|
|
|
7562
7868
|
* @param routing Registered Event schemas.
|
|
7563
7869
|
* @param message Inbox message recording the target type and identifier.
|
|
7564
7870
|
* @param event Stored Event used to identify the registered handler schema.
|
|
7565
|
-
* @param
|
|
7871
|
+
* @param deliverName Operation name used when reporting a target-type mismatch.
|
|
7566
7872
|
* @returns A deferred route containing exactly the recorded target identifier.
|
|
7567
7873
|
*/
|
|
7568
|
-
|
|
7874
|
+
deliverEventRoute(repository, routing, message, event, deliverName) {
|
|
7569
7875
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7570
7876
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7571
|
-
throw new Error(`${
|
|
7877
|
+
throw new Error(`${deliverName} stored target type does not match the routed repository.`);
|
|
7572
7878
|
}
|
|
7573
7879
|
const eventMessage = EntityInvocation.requireSignalMessage(event.message, "event");
|
|
7574
7880
|
const schema = RepositoryRoutes.schemaForTypeUrl(routing.eventSchemas, eventMessage.typeUrl, "event");
|
|
@@ -7589,15 +7895,15 @@ const InboxReplay = {
|
|
|
7589
7895
|
* @param event Stored Entity-state update envelope.
|
|
7590
7896
|
* @returns A deferred route with the unpacked state and matching subscribers.
|
|
7591
7897
|
*/
|
|
7592
|
-
|
|
7898
|
+
deliverStateUpdateRoute(repository, routing, message, event) {
|
|
7593
7899
|
const expectedTargetTypeUrl = TypeUrls.derive(repository.stateSchema);
|
|
7594
7900
|
if (message.inboxId.targetTypeUrl !== expectedTargetTypeUrl) {
|
|
7595
|
-
throw new Error("Projection inbox
|
|
7901
|
+
throw new Error("Projection inbox delivery stored target type does not match the routed repository.");
|
|
7596
7902
|
}
|
|
7597
|
-
const update = RepositoryRoutes.decodeStateUpdate(event, routing.stateSchemas, "Projection inbox
|
|
7903
|
+
const update = RepositoryRoutes.decodeStateUpdate(event, routing.stateSchemas, "Projection inbox delivery");
|
|
7598
7904
|
if (update === undefined ||
|
|
7599
7905
|
(routing.stateSubscriptions.get(update.schema.typeName)?.length ?? 0) === 0) {
|
|
7600
|
-
throw new Error("Projection inbox
|
|
7906
|
+
throw new Error("Projection inbox delivery requires a readable stored Entity state update.");
|
|
7601
7907
|
}
|
|
7602
7908
|
const { schema, state } = update;
|
|
7603
7909
|
const entityId = InboxMessages.targetEntityId(message.inboxId.targetId, repository.idField);
|
|
@@ -7606,7 +7912,7 @@ const InboxReplay = {
|
|
|
7606
7912
|
entityIds,
|
|
7607
7913
|
messageFullTypeName: schema.typeName,
|
|
7608
7914
|
state,
|
|
7609
|
-
subscribers:
|
|
7915
|
+
subscribers: InboxDelivery.stateSubscribers(routing, schema, event),
|
|
7610
7916
|
invocation: "deferred",
|
|
7611
7917
|
});
|
|
7612
7918
|
},
|
|
@@ -7623,7 +7929,7 @@ const InboxReplay = {
|
|
|
7623
7929
|
return Object.freeze(candidates.filter((subscriber) => (subscriber.handler.origin === "external") === (event.context?.external === true)));
|
|
7624
7930
|
},
|
|
7625
7931
|
};
|
|
7626
|
-
Object.freeze(
|
|
7932
|
+
Object.freeze(InboxDelivery);
|
|
7627
7933
|
/**
|
|
7628
7934
|
* Records routed signals in the appropriate Entity or Projection Inbox.
|
|
7629
7935
|
*/
|
|
@@ -7634,7 +7940,7 @@ const InboxHandoff = {
|
|
|
7634
7940
|
* @param repository Repository supplying the Command route and Entity type.
|
|
7635
7941
|
* @param runtime Inbox, storage, tenant mode, and delivery strategy.
|
|
7636
7942
|
* @param command Command envelope to retain and deliver.
|
|
7637
|
-
* @returns Completion
|
|
7943
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7638
7944
|
* @param acceptedRoute Recipient route saved when the signal was accepted.
|
|
7639
7945
|
*/
|
|
7640
7946
|
async handoffEntityCommand(repository, runtime, command, acceptedRoute) {
|
|
@@ -7662,7 +7968,7 @@ const InboxHandoff = {
|
|
|
7662
7968
|
* @param runtime Projection Inbox, storage, and tenant mode.
|
|
7663
7969
|
* @param event Event envelope to retain and deliver.
|
|
7664
7970
|
* @param entityId Routed Projection identifier.
|
|
7665
|
-
* @returns Completion
|
|
7971
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7666
7972
|
*/
|
|
7667
7973
|
async handoffProjectionEvent(repository, runtime, event, entityId) {
|
|
7668
7974
|
const eventId = RepositorySignals.requireEventId(event);
|
|
@@ -7683,53 +7989,53 @@ const InboxHandoff = {
|
|
|
7683
7989
|
}, deliveryTenantId);
|
|
7684
7990
|
},
|
|
7685
7991
|
/**
|
|
7686
|
-
* Delivers an Event to one
|
|
7992
|
+
* Delivers an Event to one Entity target using its Inbox.
|
|
7687
7993
|
*
|
|
7688
|
-
* @param repository Repository supplying the
|
|
7994
|
+
* @param repository Repository supplying the Entity type and ID descriptor.
|
|
7689
7995
|
* @param runtime Entity Inbox, storage, tenant mode, and delivery strategy.
|
|
7690
7996
|
* @param event Event envelope to retain and deliver.
|
|
7691
|
-
* @param entityId Routed
|
|
7692
|
-
* @returns Completion
|
|
7997
|
+
* @param entityId Routed Entity identifier.
|
|
7998
|
+
* @returns Completion after Inbox receipt and eligible delivery scheduling.
|
|
7693
7999
|
*/
|
|
7694
|
-
async
|
|
8000
|
+
async handoffEntityEvent(repository, runtime, event, entityId) {
|
|
7695
8001
|
const eventId = RepositorySignals.requireEventId(event);
|
|
7696
8002
|
const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
|
|
7697
|
-
const deliveryTenantId = RepositoryTenants.
|
|
8003
|
+
const deliveryTenantId = RepositoryTenants.requireEntityEventTenant(runtime.context, event);
|
|
7698
8004
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7699
|
-
await runtime.entityInbox.receive(delivery, InboxHandoff.
|
|
8005
|
+
await runtime.entityInbox.receive(delivery, InboxHandoff.entityEventInboxInput(repository, eventId.value, event, entityId, keepUntil), deliveryTenantId);
|
|
7700
8006
|
},
|
|
7701
8007
|
/**
|
|
7702
|
-
* Delivers an Event to all routed
|
|
8008
|
+
* Delivers an Event to all routed Entity targets through batch receipt.
|
|
7703
8009
|
*
|
|
7704
|
-
* @param repository Repository supplying the
|
|
8010
|
+
* @param repository Repository supplying the Entity type and ID descriptor.
|
|
7705
8011
|
* @param runtime Entity Inbox, storage, tenant mode, and delivery strategy.
|
|
7706
8012
|
* @param event Event envelope shared by the target deliveries.
|
|
7707
|
-
* @param entityIds Routed
|
|
7708
|
-
* @returns Completion
|
|
8013
|
+
* @param entityIds Routed Entity identifiers.
|
|
8014
|
+
* @returns Completion after batch Inbox receipts and eligible delivery scheduling.
|
|
7709
8015
|
*/
|
|
7710
|
-
async
|
|
8016
|
+
async handoffEntityEvents(repository, runtime, event, entityIds) {
|
|
7711
8017
|
const eventId = RepositorySignals.requireEventId(event);
|
|
7712
8018
|
const keepUntil = new Date(Time.currentTimeMillis() + inboxDedupMs);
|
|
7713
|
-
const deliveryTenantId = RepositoryTenants.
|
|
8019
|
+
const deliveryTenantId = RepositoryTenants.requireEntityEventTenant(runtime.context, event);
|
|
7714
8020
|
const delivery = serverEnvironmentAccess.coreDelivery(ServerEnvironment.instance());
|
|
7715
8021
|
for (let start = 0; start < entityIds.length; start += 1_000) {
|
|
7716
8022
|
const inputs = entityIds
|
|
7717
8023
|
.slice(start, start + 1_000)
|
|
7718
|
-
.map((entityId) => InboxHandoff.
|
|
8024
|
+
.map((entityId) => InboxHandoff.entityEventInboxInput(repository, eventId.value, event, entityId, keepUntil));
|
|
7719
8025
|
await runtime.entityInbox.receiveAll(delivery, inputs, deliveryTenantId);
|
|
7720
8026
|
}
|
|
7721
8027
|
},
|
|
7722
8028
|
/**
|
|
7723
|
-
* Builds a pending
|
|
8029
|
+
* Builds a pending Entity Event delivery for a single target.
|
|
7724
8030
|
*
|
|
7725
8031
|
* @param repository Repository supplying the target type and ID descriptor.
|
|
7726
8032
|
* @param signalId Identifier of the original Event.
|
|
7727
8033
|
* @param event Event envelope to pack without repeating validation.
|
|
7728
|
-
* @param entityId Routed
|
|
8034
|
+
* @param entityId Routed Entity identifier.
|
|
7729
8035
|
* @param keepUntil Duplicate-retention deadline for this receipt.
|
|
7730
8036
|
* @returns Input for Entity Inbox receipt.
|
|
7731
8037
|
*/
|
|
7732
|
-
|
|
8038
|
+
entityEventInboxInput(repository, signalId, event, entityId, keepUntil) {
|
|
7733
8039
|
return {
|
|
7734
8040
|
inboxId: {
|
|
7735
8041
|
targetId: InboxMessages.inboxTargetId(entityId, repository.idField),
|
|
@@ -7745,7 +8051,7 @@ const InboxHandoff = {
|
|
|
7745
8051
|
};
|
|
7746
8052
|
Object.freeze(InboxHandoff);
|
|
7747
8053
|
/**
|
|
7748
|
-
* Connects bus dispatchers and Inbox
|
|
8054
|
+
* Connects bus dispatchers and Inbox delivery to repository execution.
|
|
7749
8055
|
*/
|
|
7750
8056
|
const RepositoryDispatch = {
|
|
7751
8057
|
/**
|
|
@@ -7846,7 +8152,7 @@ const RepositoryDispatch = {
|
|
|
7846
8152
|
: RepositoryDispatch.dispatchRepositoryStateUpdate(repository, routing, event, route);
|
|
7847
8153
|
},
|
|
7848
8154
|
/**
|
|
7849
|
-
* Creates
|
|
8155
|
+
* Creates deliver access for an Aggregate or Process Manager with signal handlers.
|
|
7850
8156
|
*
|
|
7851
8157
|
* @param repository Repository providing signal routes and Entity identity.
|
|
7852
8158
|
* @param routing Registered handlers and message schemas.
|
|
@@ -7863,19 +8169,17 @@ const RepositoryDispatch = {
|
|
|
7863
8169
|
targetTypeUrl: TypeUrls.derive(repository.stateSchema),
|
|
7864
8170
|
labels: Object.freeze([
|
|
7865
8171
|
...(routing.commandSchemas.length === 0 ? [] : ["HANDLE_COMMAND"]),
|
|
7866
|
-
...(
|
|
7867
|
-
repository.entityFamily === "agent") &&
|
|
7868
|
-
routing.eventSchemas.length > 0
|
|
7869
|
-
? ["REACT_UPON_EVENT"]
|
|
7870
|
-
: []),
|
|
8172
|
+
...(routing.eventSchemas.length > 0 ? ["REACT_UPON_EVENT"] : []),
|
|
7871
8173
|
]),
|
|
7872
|
-
|
|
7873
|
-
?
|
|
7874
|
-
|
|
8174
|
+
deliver: (message, deliveryTenantId) => repository.entityFamily === "aggregate"
|
|
8175
|
+
? message.label === "REACT_UPON_EVENT"
|
|
8176
|
+
? InboxDelivery.deliverAggregateEvent(repository, routing, message, deliveryTenantId)
|
|
8177
|
+
: InboxDelivery.deliverAggregateCommand(repository, routing, message, deliveryTenantId)
|
|
8178
|
+
: InboxDelivery.deliverPmInbox(repository, routing, message, deliveryTenantId),
|
|
7875
8179
|
});
|
|
7876
8180
|
},
|
|
7877
8181
|
/**
|
|
7878
|
-
* Creates
|
|
8182
|
+
* Creates deliver access for a Projection with Event or state subscribers.
|
|
7879
8183
|
*
|
|
7880
8184
|
* @param repository Repository providing Projection identity and Event routes.
|
|
7881
8185
|
* @param routing Registered Event and state subscribers.
|
|
@@ -7888,7 +8192,7 @@ const RepositoryDispatch = {
|
|
|
7888
8192
|
}
|
|
7889
8193
|
return Object.freeze({
|
|
7890
8194
|
targetTypeUrl: TypeUrls.derive(repository.stateSchema),
|
|
7891
|
-
|
|
8195
|
+
deliver: (message, deliveryTenantId) => InboxDelivery.deliverProjectionMessage(repository, routing, message, deliveryTenantId),
|
|
7892
8196
|
});
|
|
7893
8197
|
},
|
|
7894
8198
|
/**
|
|
@@ -7926,7 +8230,7 @@ const RepositoryDispatch = {
|
|
|
7926
8230
|
const route = acceptedRoute ?? (await repository.routeEvent(event));
|
|
7927
8231
|
switch (repository.entityFamily) {
|
|
7928
8232
|
case "aggregate":
|
|
7929
|
-
await RepositoryDispatch.dispatchAggregateEvent(repository,
|
|
8233
|
+
await RepositoryDispatch.dispatchAggregateEvent(repository, runtime, event, route);
|
|
7930
8234
|
return;
|
|
7931
8235
|
case "process-manager":
|
|
7932
8236
|
case "agent":
|
|
@@ -7941,17 +8245,19 @@ const RepositoryDispatch = {
|
|
|
7941
8245
|
* Dispatches an Event to each routed Aggregate with its configured duplicate guard.
|
|
7942
8246
|
*
|
|
7943
8247
|
* @param repository Aggregate repository receiving the Event.
|
|
7944
|
-
* @param routing Registered Aggregate Event handlers and schemas.
|
|
7945
8248
|
* @param runtime Bound storage, publisher, and tenant context.
|
|
7946
8249
|
* @param event Event envelope to dispatch.
|
|
7947
8250
|
* @param route Accepted route listing target Aggregate identifiers.
|
|
7948
8251
|
* @returns Completion of all target dispatches in route order.
|
|
7949
8252
|
*/
|
|
7950
|
-
async dispatchAggregateEvent(repository,
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
8253
|
+
async dispatchAggregateEvent(repository, runtime, event, route) {
|
|
8254
|
+
if (route.entityIds.length === 0)
|
|
8255
|
+
return;
|
|
8256
|
+
if (route.entityIds.length === 1) {
|
|
8257
|
+
await InboxHandoff.handoffEntityEvent(repository, runtime, event, route.entityIds[0]);
|
|
8258
|
+
return;
|
|
7954
8259
|
}
|
|
8260
|
+
await InboxHandoff.handoffEntityEvents(repository, runtime, event, route.entityIds);
|
|
7955
8261
|
},
|
|
7956
8262
|
/**
|
|
7957
8263
|
* Delivers a routed Entity-state update to each target Projection Inbox.
|