@fabricorg/platform-host 2.0.0 → 2.0.1

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/dist/index.d.cts CHANGED
@@ -382,6 +382,8 @@ interface PlatformHostOptions<TDb> {
382
382
  producerModuleVersion(actionId: ActionId, actionVersion: number): string;
383
383
  classifyPayload(event: AssetEventEnvelope): EventPayloadClassification;
384
384
  traceContext?(event: AssetEventEnvelope): Record<string, string> | undefined;
385
+ /** Host lifecycle events are excluded by default; opt in only after making their payload audit-safe. */
386
+ shouldPublish?(event: AssetEventEnvelope): boolean;
385
387
  };
386
388
  /** Optional vertical-owned evaluator. When absent, agent behavior is unchanged. */
387
389
  hitlEvaluator?: AgentActionPolicyEvaluator;
@@ -433,8 +435,15 @@ interface GovernedActionHost {
433
435
 
434
436
  declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost;
435
437
 
438
+ interface MemoryPlatformHostTransactionProvider<TDb> {
439
+ snapshot(db: TDb): TDb;
440
+ restore(db: TDb, snapshot: TDb): void;
441
+ }
436
442
  declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb> {
437
443
  readonly db: TDb;
444
+ readonly transactionalOutbox: boolean;
445
+ transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
446
+ private transactionTail;
438
447
  readonly invocations: ActionInvocationRecord[];
439
448
  readonly policyEvaluations: PolicyEvaluationRecord[];
440
449
  readonly adapterInvocations: AdapterInvocationRecord[];
@@ -443,8 +452,9 @@ declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostSto
443
452
  readonly executionAttestations: ExecutionAttestationRecord[];
444
453
  readonly externalReconciliations: ExternalReconciliationRecord[];
445
454
  readonly outbox: OutboxRecord[];
446
- constructor(db: TDb);
455
+ constructor(db: TDb, transactionProvider?: MemoryPlatformHostTransactionProvider<TDb>);
447
456
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
457
+ private runTransactionWithEvents;
448
458
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
449
459
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
450
460
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: Partial<Pick<ActionInvocationRecord, "status" | "result" | "error">>): Promise<void>;
@@ -593,4 +603,4 @@ declare function runPlatformActionWorkerCycle<TDb>(options: PlatformActionWorker
593
603
  /** Poll until aborted. Each iteration is bounded and waits without busy-spinning. */
594
604
  declare function runPlatformActionWorker<TDb>(options: PlatformActionWorkerOptions<TDb>): Promise<void>;
595
605
 
596
- export { type ActionApprovalDecision, type ActionAuthorizationInput, type ActionExecutionAuthorizationInput, type ActionExecutionReason, type ActionInvocationRecord, type AdapterInvocationRecord, type AdapterInvocationStatus, type ApprovalDecisionTransitionResult, type ApprovalPlatformHostStore, type AtomicMutationPlatformHostStore, type BeginApprovalDecisionInput, type ClaimActionInvocationsInput, type ClaimOutboxInput, type CreateActionInvocationInput, type DispatchActionInput, type DispatchActionResult, type EnterpriseEventEnvelope, type EnterpriseEventPublisher, type EventPayloadClassification, type ExecuteActionResult, type ExecuteInvocationOptions, type ExecutionAttestationRecord, type ExternalReconciliationRecord, type GovernancePlatformHostStore, type GovernedActionHost, type HitlDecisionEvidence, type ListActionInvocationsInput, MemoryPlatformHostStore, type OutboxEventMetadata, type OutboxPlatformHostStore, type OutboxRecord, type OutboxRelayOptions, type OutboxRelayResult, type OutboxStatus, PLATFORM_HOST_CONTRACT_VERSION, type PendingAssetEvent, type PersistedActionApprovalDecision, type PlatformActionDispatcher, type PlatformActionWorkerCycleResult, type PlatformActionWorkerOptions, type PlatformHostAuthorization, type PlatformHostMutationTransaction, type PlatformHostOptions, type PlatformHostSqlClient, type PlatformHostSqlResult, type PlatformHostStore, type PolicyEvaluationRecord, type PolicyObligationRecord, PostgresPlatformHostStore, type PostgresPlatformHostTransactionContext, type PostgresPlatformHostTransactionProvider, type RecoverablePlatformHostStore, type SubmitActionInput, type SubmitActionResult, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
606
+ export { type ActionApprovalDecision, type ActionAuthorizationInput, type ActionExecutionAuthorizationInput, type ActionExecutionReason, type ActionInvocationRecord, type AdapterInvocationRecord, type AdapterInvocationStatus, type ApprovalDecisionTransitionResult, type ApprovalPlatformHostStore, type AtomicMutationPlatformHostStore, type BeginApprovalDecisionInput, type ClaimActionInvocationsInput, type ClaimOutboxInput, type CreateActionInvocationInput, type DispatchActionInput, type DispatchActionResult, type EnterpriseEventEnvelope, type EnterpriseEventPublisher, type EventPayloadClassification, type ExecuteActionResult, type ExecuteInvocationOptions, type ExecutionAttestationRecord, type ExternalReconciliationRecord, type GovernancePlatformHostStore, type GovernedActionHost, type HitlDecisionEvidence, type ListActionInvocationsInput, MemoryPlatformHostStore, type MemoryPlatformHostTransactionProvider, type OutboxEventMetadata, type OutboxPlatformHostStore, type OutboxRecord, type OutboxRelayOptions, type OutboxRelayResult, type OutboxStatus, PLATFORM_HOST_CONTRACT_VERSION, type PendingAssetEvent, type PersistedActionApprovalDecision, type PlatformActionDispatcher, type PlatformActionWorkerCycleResult, type PlatformActionWorkerOptions, type PlatformHostAuthorization, type PlatformHostMutationTransaction, type PlatformHostOptions, type PlatformHostSqlClient, type PlatformHostSqlResult, type PlatformHostStore, type PolicyEvaluationRecord, type PolicyObligationRecord, PostgresPlatformHostStore, type PostgresPlatformHostTransactionContext, type PostgresPlatformHostTransactionProvider, type RecoverablePlatformHostStore, type SubmitActionInput, type SubmitActionResult, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
package/dist/index.d.ts CHANGED
@@ -382,6 +382,8 @@ interface PlatformHostOptions<TDb> {
382
382
  producerModuleVersion(actionId: ActionId, actionVersion: number): string;
383
383
  classifyPayload(event: AssetEventEnvelope): EventPayloadClassification;
384
384
  traceContext?(event: AssetEventEnvelope): Record<string, string> | undefined;
385
+ /** Host lifecycle events are excluded by default; opt in only after making their payload audit-safe. */
386
+ shouldPublish?(event: AssetEventEnvelope): boolean;
385
387
  };
386
388
  /** Optional vertical-owned evaluator. When absent, agent behavior is unchanged. */
387
389
  hitlEvaluator?: AgentActionPolicyEvaluator;
@@ -433,8 +435,15 @@ interface GovernedActionHost {
433
435
 
434
436
  declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost;
435
437
 
438
+ interface MemoryPlatformHostTransactionProvider<TDb> {
439
+ snapshot(db: TDb): TDb;
440
+ restore(db: TDb, snapshot: TDb): void;
441
+ }
436
442
  declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb> {
437
443
  readonly db: TDb;
444
+ readonly transactionalOutbox: boolean;
445
+ transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
446
+ private transactionTail;
438
447
  readonly invocations: ActionInvocationRecord[];
439
448
  readonly policyEvaluations: PolicyEvaluationRecord[];
440
449
  readonly adapterInvocations: AdapterInvocationRecord[];
@@ -443,8 +452,9 @@ declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostSto
443
452
  readonly executionAttestations: ExecutionAttestationRecord[];
444
453
  readonly externalReconciliations: ExternalReconciliationRecord[];
445
454
  readonly outbox: OutboxRecord[];
446
- constructor(db: TDb);
455
+ constructor(db: TDb, transactionProvider?: MemoryPlatformHostTransactionProvider<TDb>);
447
456
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
457
+ private runTransactionWithEvents;
448
458
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
449
459
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
450
460
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: Partial<Pick<ActionInvocationRecord, "status" | "result" | "error">>): Promise<void>;
@@ -593,4 +603,4 @@ declare function runPlatformActionWorkerCycle<TDb>(options: PlatformActionWorker
593
603
  /** Poll until aborted. Each iteration is bounded and waits without busy-spinning. */
594
604
  declare function runPlatformActionWorker<TDb>(options: PlatformActionWorkerOptions<TDb>): Promise<void>;
595
605
 
596
- export { type ActionApprovalDecision, type ActionAuthorizationInput, type ActionExecutionAuthorizationInput, type ActionExecutionReason, type ActionInvocationRecord, type AdapterInvocationRecord, type AdapterInvocationStatus, type ApprovalDecisionTransitionResult, type ApprovalPlatformHostStore, type AtomicMutationPlatformHostStore, type BeginApprovalDecisionInput, type ClaimActionInvocationsInput, type ClaimOutboxInput, type CreateActionInvocationInput, type DispatchActionInput, type DispatchActionResult, type EnterpriseEventEnvelope, type EnterpriseEventPublisher, type EventPayloadClassification, type ExecuteActionResult, type ExecuteInvocationOptions, type ExecutionAttestationRecord, type ExternalReconciliationRecord, type GovernancePlatformHostStore, type GovernedActionHost, type HitlDecisionEvidence, type ListActionInvocationsInput, MemoryPlatformHostStore, type OutboxEventMetadata, type OutboxPlatformHostStore, type OutboxRecord, type OutboxRelayOptions, type OutboxRelayResult, type OutboxStatus, PLATFORM_HOST_CONTRACT_VERSION, type PendingAssetEvent, type PersistedActionApprovalDecision, type PlatformActionDispatcher, type PlatformActionWorkerCycleResult, type PlatformActionWorkerOptions, type PlatformHostAuthorization, type PlatformHostMutationTransaction, type PlatformHostOptions, type PlatformHostSqlClient, type PlatformHostSqlResult, type PlatformHostStore, type PolicyEvaluationRecord, type PolicyObligationRecord, PostgresPlatformHostStore, type PostgresPlatformHostTransactionContext, type PostgresPlatformHostTransactionProvider, type RecoverablePlatformHostStore, type SubmitActionInput, type SubmitActionResult, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
606
+ export { type ActionApprovalDecision, type ActionAuthorizationInput, type ActionExecutionAuthorizationInput, type ActionExecutionReason, type ActionInvocationRecord, type AdapterInvocationRecord, type AdapterInvocationStatus, type ApprovalDecisionTransitionResult, type ApprovalPlatformHostStore, type AtomicMutationPlatformHostStore, type BeginApprovalDecisionInput, type ClaimActionInvocationsInput, type ClaimOutboxInput, type CreateActionInvocationInput, type DispatchActionInput, type DispatchActionResult, type EnterpriseEventEnvelope, type EnterpriseEventPublisher, type EventPayloadClassification, type ExecuteActionResult, type ExecuteInvocationOptions, type ExecutionAttestationRecord, type ExternalReconciliationRecord, type GovernancePlatformHostStore, type GovernedActionHost, type HitlDecisionEvidence, type ListActionInvocationsInput, MemoryPlatformHostStore, type MemoryPlatformHostTransactionProvider, type OutboxEventMetadata, type OutboxPlatformHostStore, type OutboxRecord, type OutboxRelayOptions, type OutboxRelayResult, type OutboxStatus, PLATFORM_HOST_CONTRACT_VERSION, type PendingAssetEvent, type PersistedActionApprovalDecision, type PlatformActionDispatcher, type PlatformActionWorkerCycleResult, type PlatformActionWorkerOptions, type PlatformHostAuthorization, type PlatformHostMutationTransaction, type PlatformHostOptions, type PlatformHostSqlClient, type PlatformHostSqlResult, type PlatformHostStore, type PolicyEvaluationRecord, type PolicyObligationRecord, PostgresPlatformHostStore, type PostgresPlatformHostTransactionContext, type PostgresPlatformHostTransactionProvider, type RecoverablePlatformHostStore, type SubmitActionInput, type SubmitActionResult, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
package/dist/index.js CHANGED
@@ -326,6 +326,9 @@ function createGovernedActionHost(options) {
326
326
  let domainEvents = [];
327
327
  try {
328
328
  const runHandler = async (db, transaction) => {
329
+ if (options.outbox && !transaction?.appendEventWithOutbox) {
330
+ throw new Error("Outbox egress requires transactionWithEvents to provide appendEventWithOutbox before handler execution.");
331
+ }
329
332
  const handlerResult = action.handler ? await action.handler(
330
333
  {
331
334
  actionInvocationId,
@@ -692,6 +695,12 @@ function createGovernedActionHost(options) {
692
695
  ...invocation.causationId ? { causationId: invocation.causationId } : {}
693
696
  };
694
697
  if (options.outbox) {
698
+ const hostLifecycleEvent = (/* @__PURE__ */ new Set(["AdapterInvocationStarted", "AdapterInvocationSucceeded", "AdapterInvocationFailed", "ComplianceBlocked"])).has(envelope.eventType);
699
+ const shouldPublish = options.outbox.shouldPublish?.(envelope) ?? !hostLifecycleEvent;
700
+ if (!shouldPublish) {
701
+ await (transaction ?? options.store).appendEvent(envelope);
702
+ return;
703
+ }
695
704
  const traceContext = options.outbox.traceContext?.(envelope);
696
705
  const metadata = {
697
706
  producerModuleVersion: options.outbox.producerModuleVersion(invocation.actionId, invocation.actionVersion),
@@ -847,13 +856,13 @@ async function runOutboxRelayCycle(options) {
847
856
  continue;
848
857
  }
849
858
  result.published += 1;
850
- } catch (error) {
859
+ } catch {
851
860
  const deadLetter = record.attemptCount >= maxAttempts;
852
861
  try {
853
862
  await options.store.markOutboxFailed({
854
863
  id: record.id,
855
864
  workerId: options.workerId,
856
- error: error instanceof Error ? error.message : String(error),
865
+ error: "Event publisher failed",
857
866
  availableAt: new Date(now().getTime() + (options.retryDelayMs?.(record.attemptCount) ?? 1e3)),
858
867
  deadLetter
859
868
  });
@@ -874,10 +883,14 @@ function cloneOutboxRecord(record) {
874
883
 
875
884
  // src/memory-store.ts
876
885
  var MemoryPlatformHostStore = class {
877
- constructor(db) {
886
+ constructor(db, transactionProvider) {
878
887
  this.db = db;
888
+ this.transactionalOutbox = transactionProvider !== void 0;
889
+ if (transactionProvider) this.transactionWithEvents = (run) => this.runTransactionWithEvents(run, transactionProvider);
879
890
  }
880
891
  db;
892
+ transactionalOutbox;
893
+ transactionTail = Promise.resolve();
881
894
  invocations = [];
882
895
  policyEvaluations = [];
883
896
  adapterInvocations = [];
@@ -889,6 +902,50 @@ var MemoryPlatformHostStore = class {
889
902
  async transaction(run) {
890
903
  return run(this.db);
891
904
  }
905
+ async runTransactionWithEvents(run, transactionProvider) {
906
+ let release;
907
+ const previous = this.transactionTail;
908
+ this.transactionTail = new Promise((resolve) => {
909
+ release = resolve;
910
+ });
911
+ await previous;
912
+ let domainSnapshot;
913
+ let snapshotCreated = false;
914
+ try {
915
+ domainSnapshot = transactionProvider.snapshot(this.db);
916
+ snapshotCreated = true;
917
+ const pendingEvents = [];
918
+ const pendingUpdates = [];
919
+ const appendPending = async (event, metadata) => {
920
+ if (this.events.some((candidate) => candidate.id === event.id) || pendingEvents.some((candidate) => candidate.event.id === event.id)) return;
921
+ pendingEvents.push({ event, ...metadata ? { metadata } : {} });
922
+ };
923
+ const result = await run({
924
+ db: this.db,
925
+ appendEvent: (event) => appendPending(event),
926
+ appendEventWithOutbox: (event, metadata) => appendPending(event, metadata),
927
+ nextEventSequence: async (tenantId, spaceId) => (await this.listEvents(tenantId, spaceId)).length + pendingEvents.filter((candidate) => candidate.event.tenantId === tenantId && candidate.event.spaceId === spaceId).length + 1,
928
+ listEvents: async (tenantId, spaceId) => [...await this.listEvents(tenantId, spaceId), ...pendingEvents.map((candidate) => candidate.event).filter((event) => event.tenantId === tenantId && event.spaceId === spaceId)],
929
+ updateActionInvocation: async (id, tenantId, spaceId, patch) => {
930
+ pendingUpdates.push({ id, tenantId, spaceId, patch });
931
+ }
932
+ });
933
+ for (const update of pendingUpdates) {
934
+ if (!await this.getActionInvocation(update.id, update.tenantId, update.spaceId)) throw new Error(`ActionInvocation not found: ${update.id}`);
935
+ }
936
+ for (const pending of pendingEvents) {
937
+ if (pending.metadata) await this.appendEventWithOutbox(pending.event, pending.metadata);
938
+ else await this.appendEvent(pending.event);
939
+ }
940
+ for (const update of pendingUpdates) await this.updateActionInvocation(update.id, update.tenantId, update.spaceId, update.patch);
941
+ return result;
942
+ } catch (error) {
943
+ if (snapshotCreated) transactionProvider.restore(this.db, domainSnapshot);
944
+ throw error;
945
+ } finally {
946
+ release();
947
+ }
948
+ }
892
949
  async createActionInvocation(input) {
893
950
  if (input.idempotencyKey) {
894
951
  const existing = this.invocations.find(