@powerhousedao/reactor 6.2.2-dev.31 → 6.2.2-dev.32

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.
@@ -1,4 +1,4 @@
1
- import { _ as DocumentMetaCache, b as CollectionMembershipCache, c as KyselyKeyframeStore, g as KyselyOperationIndex, h as KyselyWriteCache, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, p as KyselyExecutionScope, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor } from "./drive-container-types-BNwEHEXP.js";
1
+ import { _ as DocumentMetaCache, b as CollectionMembershipCache, c as KyselyKeyframeStore, g as KyselyOperationIndex, h as KyselyWriteCache, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, p as KyselyExecutionScope, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor } from "./drive-container-types-CPQDOggp.js";
2
2
  import { n as ReactorEventTypes } from "./types-DMKLa0Ok.js";
3
3
  //#region src/executor/worker/build-worker-executor.ts
4
4
  async function defaultLoadFactory(spec) {
@@ -80,4 +80,4 @@ async function buildWorkerExecutor(options) {
80
80
  //#endregion
81
81
  export { defaultLoadFactory as n, buildWorkerExecutor as t };
82
82
 
83
- //# sourceMappingURL=build-worker-executor-CO2_-cnA.js.map
83
+ //# sourceMappingURL=build-worker-executor-DisavYJh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-worker-executor-CO2_-cnA.js","names":[],"sources":["../src/executor/worker/build-worker-executor.ts"],"sourcesContent":["import type {\n DocumentModelModule,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport { CollectionMembershipCache } from \"../../cache/collection-membership-cache.js\";\nimport { DocumentMetaCache } from \"../../cache/document-meta-cache.js\";\nimport { KyselyOperationIndex } from \"../../cache/kysely-operation-index.js\";\nimport { KyselyWriteCache } from \"../../cache/kysely-write-cache.js\";\nimport type { WriteCacheConfig } from \"../../cache/write-cache-types.js\";\nimport { DEFAULT_DRIVE_CONTAINER_TYPES } from \"../../core/drive-container-types.js\";\nimport type { Database } from \"../../core/types.js\";\nimport { EventBus } from \"../../events/event-bus.js\";\nimport {\n ReactorEventTypes,\n type JobWriteReadyEvent,\n} from \"../../events/types.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport type { JobMeta } from \"../../shared/types.js\";\nimport type { SignatureVerificationHandler } from \"../../signer/types.js\";\nimport { KyselyKeyframeStore } from \"../../storage/kysely/keyframe-store.js\";\nimport { KyselyOperationStore } from \"../../storage/kysely/store.js\";\nimport type { Database as StorageDatabase } from \"../../storage/kysely/types.js\";\nimport { REACTOR_SCHEMA } from \"../../storage/migrations/migrator.js\";\nimport { KyselyExecutionScope } from \"../execution-scope.js\";\nimport { SimpleJobExecutor } from \"../simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../types.js\";\nimport type {\n FactorySpec,\n InitMessage,\n ModelManifestEntry,\n} from \"./protocol.js\";\n\n/**\n * In-worker capture of the JOB_WRITE_READY event emitted by the executor.\n * The worker forwards `operations` and `jobMeta` back to the parent; the\n * parent re-enriches `collectionMemberships` at emit time.\n */\nexport type WorkerWriteReadyCapture = {\n operations: OperationWithContext[];\n jobMeta: JobMeta;\n};\n\nexport type WorkerExecutorStack = {\n executor: SimpleJobExecutor;\n registry: DocumentModelRegistry;\n /**\n * Synchronously pops the most-recent JOB_WRITE_READY captured on this\n * worker's local event bus and clears it. Returns null if the executor\n * did not produce one for this job.\n */\n takeLastWriteReady(): WorkerWriteReadyCapture | null;\n};\n\nexport type BuildWorkerExecutorOptions = {\n init: InitMessage;\n database: Kysely<Database>;\n logger: ILogger;\n executorConfig?: JobExecutorConfig;\n driveContainerTypes?: ReadonlySet<string>;\n /**\n * Override the module loader used to materialize factory specs. Tests\n * can inject a deterministic resolver instead of touching the real\n * Node module loader.\n */\n loadFactory?: (spec: FactorySpec) => Promise<unknown>;\n};\n\nexport async function defaultLoadFactory(spec: FactorySpec): Promise<unknown> {\n const ref = spec.module;\n const specifier =\n \"filePath\" in ref\n ? new URL(`file://${ref.filePath}`).href\n : ref.packageName;\n const mod = (await import(specifier)) as Record<string, unknown>;\n const exported = mod[ref.exportName];\n if (typeof exported === \"function\") {\n return (exported as (args: unknown) => unknown)(spec.initArgs);\n }\n return exported;\n}\n\nasync function loadModelManifest(\n entries: ModelManifestEntry[],\n loadFactory: (spec: FactorySpec) => Promise<unknown>,\n registry: DocumentModelRegistry,\n logger: ILogger,\n): Promise<void> {\n for (const entry of entries) {\n let module: DocumentModelModule;\n try {\n module = (await loadFactory(entry.spec)) as DocumentModelModule;\n } catch (error) {\n logger.error(\n \"worker failed to load document model: @entry @error\",\n entry,\n error,\n );\n throw error;\n }\n const [result] = registry.registerModules(module);\n if (result.status === \"error\") {\n logger.error(\n \"worker failed to register document model: @entry @error\",\n entry,\n result.error,\n );\n throw result.error;\n }\n }\n}\n\n/**\n * Assembles the in-worker storage stack plus a {@link SimpleJobExecutor}\n * bound to a pre-built Kysely instance. The parent owns the wire protocol\n * and routing; the worker owns everything below `SimpleJobExecutor`.\n *\n * The local event bus exists only to satisfy the executor's contract: its\n * JOB_WRITE_READY emissions are captured here and shipped to the parent\n * via {@link WorkerExecutorStack.takeLastWriteReady}.\n */\nexport async function buildWorkerExecutor(\n options: BuildWorkerExecutorOptions,\n): Promise<WorkerExecutorStack> {\n const { init, database: baseDatabase, logger } = options;\n const driveContainerTypes =\n options.driveContainerTypes ?? DEFAULT_DRIVE_CONTAINER_TYPES;\n const loadFactory = options.loadFactory ?? defaultLoadFactory;\n\n const registry = new DocumentModelRegistry();\n await loadModelManifest(init.models, loadFactory, registry, logger);\n\n let signatureVerifier: SignatureVerificationHandler | undefined;\n if (init.signatureVerifier) {\n try {\n signatureVerifier = (await loadFactory(\n init.signatureVerifier,\n )) as SignatureVerificationHandler;\n } catch (error) {\n logger.error(\n \"worker failed to load signature verifier: @spec @error\",\n init.signatureVerifier,\n error,\n );\n throw error;\n }\n }\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n const operationStore = new KyselyOperationStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n const keyframeStore = new KyselyKeyframeStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: 100,\n ringBufferSize: 10,\n keyframeInterval: 10,\n };\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n registry,\n cacheConfig,\n );\n await writeCache.startup();\n\n const operationIndex = new KyselyOperationIndex(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const documentMetaCache = new DocumentMetaCache(operationStore, {\n maxDocuments: 1000,\n });\n await documentMetaCache.startup();\n\n const collectionMembershipCache = new CollectionMembershipCache(\n operationIndex,\n );\n\n const executionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n const eventBus = new EventBus();\n let lastWriteReady: WorkerWriteReadyCapture | null = null;\n eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_t: number, event: JobWriteReadyEvent) => {\n lastWriteReady = {\n operations: event.operations,\n jobMeta: event.jobMeta,\n };\n },\n );\n\n const executorConfig = options.executorConfig ?? {};\n const executor = new SimpleJobExecutor(\n logger,\n registry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n driveContainerTypes,\n executorConfig,\n signatureVerifier,\n executionScope,\n );\n\n return {\n executor,\n registry,\n takeLastWriteReady(): WorkerWriteReadyCapture | null {\n const captured = lastWriteReady;\n lastWriteReady = null;\n return captured;\n },\n };\n}\n"],"mappings":";;;AAqEA,eAAsB,mBAAmB,MAAqC;CAC5E,MAAM,MAAM,KAAK;CAMjB,MAAM,YADO,OAHX,cAAc,MAAA,OACV,IAAI,IAAI,UAAU,IAAI,WAAW,CAAC,QAAA,OAClC,IAAI,eAEW,IAAI;AACzB,KAAI,OAAO,aAAa,WACtB,QAAQ,SAAwC,KAAK,SAAS;AAEhE,QAAO;;AAGT,eAAe,kBACb,SACA,aACA,UACA,QACe;AACf,MAAK,MAAM,SAAS,SAAS;EAC3B,IAAI;AACJ,MAAI;AACF,YAAU,MAAM,YAAY,MAAM,KAAK;WAChC,OAAO;AACd,UAAO,MACL,uDACA,OACA,MACD;AACD,SAAM;;EAER,MAAM,CAAC,UAAU,SAAS,gBAAgB,OAAO;AACjD,MAAI,OAAO,WAAW,SAAS;AAC7B,UAAO,MACL,2DACA,OACA,OAAO,MACR;AACD,SAAM,OAAO;;;;;;;;;;;;;AAcnB,eAAsB,oBACpB,SAC8B;CAC9B,MAAM,EAAE,MAAM,UAAU,cAAc,WAAW;CACjD,MAAM,sBACJ,QAAQ,uBAAuB;CACjC,MAAM,cAAc,QAAQ,eAAe;CAE3C,MAAM,WAAW,IAAI,uBAAuB;AAC5C,OAAM,kBAAkB,KAAK,QAAQ,aAAa,UAAU,OAAO;CAEnE,IAAI;AACJ,KAAI,KAAK,kBACP,KAAI;AACF,sBAAqB,MAAM,YACzB,KAAK,kBACN;UACM,OAAO;AACd,SAAO,MACL,0DACA,KAAK,mBACL,MACD;AACD,QAAM;;CAIV,MAAM,WAAW,aAAa,WAAW,eAAe;CACxD,MAAM,iBAAiB,IAAI,qBACzB,SACD;CACD,MAAM,gBAAgB,IAAI,oBACxB,SACD;CAOD,MAAM,aAAa,IAAI,iBACrB,eACA,gBACA,UARoC;EACpC,cAAc;EACd,gBAAgB;EAChB,kBAAkB;EACnB,CAMA;AACD,OAAM,WAAW,SAAS;CAE1B,MAAM,iBAAiB,IAAI,qBACzB,SACD;CAED,MAAM,oBAAoB,IAAI,kBAAkB,gBAAgB,EAC9D,cAAc,KACf,CAAC;AACF,OAAM,kBAAkB,SAAS;CAEjC,MAAM,4BAA4B,IAAI,0BACpC,eACD;CAED,MAAM,iBAAiB,IAAI,qBACzB,UACA,gBACA,gBACA,eACA,YACA,mBACA,0BACD;CAED,MAAM,WAAW,IAAI,UAAU;CAC/B,IAAI,iBAAiD;AACrD,UAAS,UACP,kBAAkB,kBACjB,IAAY,UAA8B;AACzC,mBAAiB;GACf,YAAY,MAAM;GAClB,SAAS,MAAM;GAChB;GAEJ;AAkBD,QAAO;EACL,UAhBe,IAAI,kBACnB,QACA,UACA,gBACA,UACA,YACA,gBACA,mBACA,2BACA,qBAVqB,QAAQ,kBAAkB,EAAE,EAYjD,mBACA,eACD;EAIC;EACA,qBAAqD;GACnD,MAAM,WAAW;AACjB,oBAAiB;AACjB,UAAO;;EAEV"}
1
+ {"version":3,"file":"build-worker-executor-DisavYJh.js","names":[],"sources":["../src/executor/worker/build-worker-executor.ts"],"sourcesContent":["import type {\n DocumentModelModule,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport { CollectionMembershipCache } from \"../../cache/collection-membership-cache.js\";\nimport { DocumentMetaCache } from \"../../cache/document-meta-cache.js\";\nimport { KyselyOperationIndex } from \"../../cache/kysely-operation-index.js\";\nimport { KyselyWriteCache } from \"../../cache/kysely-write-cache.js\";\nimport type { WriteCacheConfig } from \"../../cache/write-cache-types.js\";\nimport { DEFAULT_DRIVE_CONTAINER_TYPES } from \"../../core/drive-container-types.js\";\nimport type { Database } from \"../../core/types.js\";\nimport { EventBus } from \"../../events/event-bus.js\";\nimport {\n ReactorEventTypes,\n type JobWriteReadyEvent,\n} from \"../../events/types.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport type { JobMeta } from \"../../shared/types.js\";\nimport type { SignatureVerificationHandler } from \"../../signer/types.js\";\nimport { KyselyKeyframeStore } from \"../../storage/kysely/keyframe-store.js\";\nimport { KyselyOperationStore } from \"../../storage/kysely/store.js\";\nimport type { Database as StorageDatabase } from \"../../storage/kysely/types.js\";\nimport { REACTOR_SCHEMA } from \"../../storage/migrations/migrator.js\";\nimport { KyselyExecutionScope } from \"../execution-scope.js\";\nimport { SimpleJobExecutor } from \"../simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../types.js\";\nimport type {\n FactorySpec,\n InitMessage,\n ModelManifestEntry,\n} from \"./protocol.js\";\n\n/**\n * In-worker capture of the JOB_WRITE_READY event emitted by the executor.\n * The worker forwards `operations` and `jobMeta` back to the parent; the\n * parent re-enriches `collectionMemberships` at emit time.\n */\nexport type WorkerWriteReadyCapture = {\n operations: OperationWithContext[];\n jobMeta: JobMeta;\n};\n\nexport type WorkerExecutorStack = {\n executor: SimpleJobExecutor;\n registry: DocumentModelRegistry;\n /**\n * Synchronously pops the most-recent JOB_WRITE_READY captured on this\n * worker's local event bus and clears it. Returns null if the executor\n * did not produce one for this job.\n */\n takeLastWriteReady(): WorkerWriteReadyCapture | null;\n};\n\nexport type BuildWorkerExecutorOptions = {\n init: InitMessage;\n database: Kysely<Database>;\n logger: ILogger;\n executorConfig?: JobExecutorConfig;\n driveContainerTypes?: ReadonlySet<string>;\n /**\n * Override the module loader used to materialize factory specs. Tests\n * can inject a deterministic resolver instead of touching the real\n * Node module loader.\n */\n loadFactory?: (spec: FactorySpec) => Promise<unknown>;\n};\n\nexport async function defaultLoadFactory(spec: FactorySpec): Promise<unknown> {\n const ref = spec.module;\n const specifier =\n \"filePath\" in ref\n ? new URL(`file://${ref.filePath}`).href\n : ref.packageName;\n const mod = (await import(specifier)) as Record<string, unknown>;\n const exported = mod[ref.exportName];\n if (typeof exported === \"function\") {\n return (exported as (args: unknown) => unknown)(spec.initArgs);\n }\n return exported;\n}\n\nasync function loadModelManifest(\n entries: ModelManifestEntry[],\n loadFactory: (spec: FactorySpec) => Promise<unknown>,\n registry: DocumentModelRegistry,\n logger: ILogger,\n): Promise<void> {\n for (const entry of entries) {\n let module: DocumentModelModule;\n try {\n module = (await loadFactory(entry.spec)) as DocumentModelModule;\n } catch (error) {\n logger.error(\n \"worker failed to load document model: @entry @error\",\n entry,\n error,\n );\n throw error;\n }\n const [result] = registry.registerModules(module);\n if (result.status === \"error\") {\n logger.error(\n \"worker failed to register document model: @entry @error\",\n entry,\n result.error,\n );\n throw result.error;\n }\n }\n}\n\n/**\n * Assembles the in-worker storage stack plus a {@link SimpleJobExecutor}\n * bound to a pre-built Kysely instance. The parent owns the wire protocol\n * and routing; the worker owns everything below `SimpleJobExecutor`.\n *\n * The local event bus exists only to satisfy the executor's contract: its\n * JOB_WRITE_READY emissions are captured here and shipped to the parent\n * via {@link WorkerExecutorStack.takeLastWriteReady}.\n */\nexport async function buildWorkerExecutor(\n options: BuildWorkerExecutorOptions,\n): Promise<WorkerExecutorStack> {\n const { init, database: baseDatabase, logger } = options;\n const driveContainerTypes =\n options.driveContainerTypes ?? DEFAULT_DRIVE_CONTAINER_TYPES;\n const loadFactory = options.loadFactory ?? defaultLoadFactory;\n\n const registry = new DocumentModelRegistry();\n await loadModelManifest(init.models, loadFactory, registry, logger);\n\n let signatureVerifier: SignatureVerificationHandler | undefined;\n if (init.signatureVerifier) {\n try {\n signatureVerifier = (await loadFactory(\n init.signatureVerifier,\n )) as SignatureVerificationHandler;\n } catch (error) {\n logger.error(\n \"worker failed to load signature verifier: @spec @error\",\n init.signatureVerifier,\n error,\n );\n throw error;\n }\n }\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n const operationStore = new KyselyOperationStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n const keyframeStore = new KyselyKeyframeStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: 100,\n ringBufferSize: 10,\n keyframeInterval: 10,\n };\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n registry,\n cacheConfig,\n );\n await writeCache.startup();\n\n const operationIndex = new KyselyOperationIndex(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const documentMetaCache = new DocumentMetaCache(operationStore, {\n maxDocuments: 1000,\n });\n await documentMetaCache.startup();\n\n const collectionMembershipCache = new CollectionMembershipCache(\n operationIndex,\n );\n\n const executionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n const eventBus = new EventBus();\n let lastWriteReady: WorkerWriteReadyCapture | null = null;\n eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_t: number, event: JobWriteReadyEvent) => {\n lastWriteReady = {\n operations: event.operations,\n jobMeta: event.jobMeta,\n };\n },\n );\n\n const executorConfig = options.executorConfig ?? {};\n const executor = new SimpleJobExecutor(\n logger,\n registry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n driveContainerTypes,\n executorConfig,\n signatureVerifier,\n executionScope,\n );\n\n return {\n executor,\n registry,\n takeLastWriteReady(): WorkerWriteReadyCapture | null {\n const captured = lastWriteReady;\n lastWriteReady = null;\n return captured;\n },\n };\n}\n"],"mappings":";;;AAqEA,eAAsB,mBAAmB,MAAqC;CAC5E,MAAM,MAAM,KAAK;CAMjB,MAAM,YADO,OAHX,cAAc,MAAA,OACV,IAAI,IAAI,UAAU,IAAI,WAAW,CAAC,QAAA,OAClC,IAAI,eAEW,IAAI;AACzB,KAAI,OAAO,aAAa,WACtB,QAAQ,SAAwC,KAAK,SAAS;AAEhE,QAAO;;AAGT,eAAe,kBACb,SACA,aACA,UACA,QACe;AACf,MAAK,MAAM,SAAS,SAAS;EAC3B,IAAI;AACJ,MAAI;AACF,YAAU,MAAM,YAAY,MAAM,KAAK;WAChC,OAAO;AACd,UAAO,MACL,uDACA,OACA,MACD;AACD,SAAM;;EAER,MAAM,CAAC,UAAU,SAAS,gBAAgB,OAAO;AACjD,MAAI,OAAO,WAAW,SAAS;AAC7B,UAAO,MACL,2DACA,OACA,OAAO,MACR;AACD,SAAM,OAAO;;;;;;;;;;;;;AAcnB,eAAsB,oBACpB,SAC8B;CAC9B,MAAM,EAAE,MAAM,UAAU,cAAc,WAAW;CACjD,MAAM,sBACJ,QAAQ,uBAAuB;CACjC,MAAM,cAAc,QAAQ,eAAe;CAE3C,MAAM,WAAW,IAAI,uBAAuB;AAC5C,OAAM,kBAAkB,KAAK,QAAQ,aAAa,UAAU,OAAO;CAEnE,IAAI;AACJ,KAAI,KAAK,kBACP,KAAI;AACF,sBAAqB,MAAM,YACzB,KAAK,kBACN;UACM,OAAO;AACd,SAAO,MACL,0DACA,KAAK,mBACL,MACD;AACD,QAAM;;CAIV,MAAM,WAAW,aAAa,WAAW,eAAe;CACxD,MAAM,iBAAiB,IAAI,qBACzB,SACD;CACD,MAAM,gBAAgB,IAAI,oBACxB,SACD;CAOD,MAAM,aAAa,IAAI,iBACrB,eACA,gBACA,UARoC;EACpC,cAAc;EACd,gBAAgB;EAChB,kBAAkB;EACnB,CAMA;AACD,OAAM,WAAW,SAAS;CAE1B,MAAM,iBAAiB,IAAI,qBACzB,SACD;CAED,MAAM,oBAAoB,IAAI,kBAAkB,gBAAgB,EAC9D,cAAc,KACf,CAAC;AACF,OAAM,kBAAkB,SAAS;CAEjC,MAAM,4BAA4B,IAAI,0BACpC,eACD;CAED,MAAM,iBAAiB,IAAI,qBACzB,UACA,gBACA,gBACA,eACA,YACA,mBACA,0BACD;CAED,MAAM,WAAW,IAAI,UAAU;CAC/B,IAAI,iBAAiD;AACrD,UAAS,UACP,kBAAkB,kBACjB,IAAY,UAA8B;AACzC,mBAAiB;GACf,YAAY,MAAM;GAClB,SAAS,MAAM;GAChB;GAEJ;AAkBD,QAAO;EACL,UAhBe,IAAI,kBACnB,QACA,UACA,gBACA,UACA,YACA,gBACA,mBACA,2BACA,qBAVqB,QAAQ,kBAAkB,EAAE,EAYjD,mBACA,eACD;EAIC;EACA,qBAAqD;GACnD,MAAM,WAAW;AACjB,oBAAiB;AACjB,UAAO;;EAEV"}
@@ -1743,7 +1743,7 @@ function documentDecisionModel(target) {
1743
1743
  scope: "document"
1744
1744
  }
1745
1745
  } },
1746
- judgesScope() {
1746
+ evaluatesScope() {
1747
1747
  return true;
1748
1748
  },
1749
1749
  decide(model) {
@@ -1767,6 +1767,7 @@ function comparePositions(a, b) {
1767
1767
  const aTime = Date.parse(a.operation.timestampUtcMs);
1768
1768
  const bTime = Date.parse(b.operation.timestampUtcMs);
1769
1769
  if (aTime !== bTime) return aTime - bTime;
1770
+ if (a.streamKey === b.streamKey) return a.operation.index - b.operation.index;
1770
1771
  const actionIds = (a.operation.action.id ?? "").localeCompare(b.operation.action.id ?? "");
1771
1772
  if (actionIds !== 0) return actionIds;
1772
1773
  return (a.operation.id ?? "").localeCompare(b.operation.id ?? "");
@@ -1799,6 +1800,23 @@ function retractionSkip(nextIndex, firstRetractedIndex) {
1799
1800
  //#endregion
1800
1801
  //#region src/decision/walk.ts
1801
1802
  /**
1803
+ * A single forward pass is only correct while a stream's effective operations
1804
+ * are ordered.
1805
+ */
1806
+ function assertPositionOrder(streamKey, operations) {
1807
+ for (let i = 1; i < operations.length; i++) {
1808
+ const previous = operations[i - 1];
1809
+ const current = operations[i];
1810
+ if (comparePositions({
1811
+ streamKey,
1812
+ operation: previous
1813
+ }, {
1814
+ streamKey,
1815
+ operation: current
1816
+ }) > 0) throw new Error(`Stream ${streamKey} is out of position order: index ${previous.index} at ${previous.timestampUtcMs} precedes index ${current.index} at ${current.timestampUtcMs}`);
1817
+ }
1818
+ }
1819
+ /**
1802
1820
  * Visits every operation in the read-set once, in the order their positions
1803
1821
  * fall, and hands back the state each stream held just before it. That state is
1804
1822
  * what a decision at that operation reads: everything ahead of it has been
@@ -1810,10 +1828,14 @@ function retractionSkip(nextIndex, firstRetractedIndex) {
1810
1828
  * A denied operation is visited but not applied.
1811
1829
  */
1812
1830
  function* walkByPosition(streams) {
1813
- const merged = mergeByPosition(streams.map((stream) => ({
1814
- streamKey: stream.streamKey,
1815
- operations: garbageCollect(sortOperations([...stream.operations]))
1816
- })));
1831
+ const merged = mergeByPosition(streams.map((stream) => {
1832
+ const operations = garbageCollect(sortOperations([...stream.operations]));
1833
+ assertPositionOrder(stream.streamKey, operations);
1834
+ return {
1835
+ streamKey: stream.streamKey,
1836
+ operations
1837
+ };
1838
+ }));
1817
1839
  const states = new Map(streams.map((stream) => [stream.streamKey, stream.document]));
1818
1840
  for (const { streamKey, operation } of merged) {
1819
1841
  yield {
@@ -1829,9 +1851,9 @@ function* walkByPosition(streams) {
1829
1851
  }
1830
1852
  }
1831
1853
  //#endregion
1832
- //#region src/decision/deletion-verdicts.ts
1854
+ //#region src/decision/deletion-evaluation.ts
1833
1855
  const WRITTEN = "written";
1834
- /** Whether a read stream counts this action as one that changes a verdict. */
1856
+ /** Whether a read stream counts this action as one that changes an evaluation. */
1835
1857
  function canRefuseOthers(operation, readSet) {
1836
1858
  return readSet.some((stream) => stream.decidingActions.includes(operation.action.type));
1837
1859
  }
@@ -1849,16 +1871,17 @@ function firstDeleted(candidates) {
1849
1871
  * and leaves the earlier ones alone. A deletion among the ones passed in does
1850
1872
  * the same to those after it.
1851
1873
  */
1852
- async function deletionVerdictsByPosition(documentId, scope, branch, operations, writeCache, operationStore, signal) {
1874
+ async function evaluateDeletionsByPosition(documentId, scope, branch, operations, writeCache, operationStore, signal) {
1853
1875
  const definition = documentDecisionModel({
1854
1876
  documentId,
1855
1877
  branch
1856
1878
  });
1857
1879
  const readSet = staticReadSet(definition);
1858
- if (!definition.judgesScope(scope)) return operations.map(() => void 0);
1880
+ if (!definition.evaluatesScope(scope)) return operations.map(() => void 0);
1881
+ const evaluating = new Set(operations.map((operation) => operation.id));
1859
1882
  const readStreams = await Promise.all(readSet.map(async (stream) => ({
1860
1883
  stream,
1861
- operations: (await operationStore.getSince(stream.query.documentId, stream.query.scope, stream.query.branch, 0, { actionTypes: stream.decidingActions }, void 0, signal)).results
1884
+ operations: (await operationStore.getSince(stream.query.documentId, stream.query.scope, stream.query.branch, 0, { actionTypes: stream.decidingActions }, void 0, signal)).results.filter((operation) => !evaluating.has(operation.id))
1862
1885
  })));
1863
1886
  const decidingWritten = operations.filter((operation) => canRefuseOthers(operation, readSet));
1864
1887
  if (readStreams.every((read) => read.operations.length === 0) && decidingWritten.length === 0) return operations.map(() => void 0);
@@ -1947,17 +1970,60 @@ var DocumentActionHandler = class {
1947
1970
  this.logger = logger;
1948
1971
  this.driveContainerTypes = driveContainerTypes;
1949
1972
  }
1950
- async execute(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal, verdictAlreadyDecided = false) {
1973
+ async execute(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal, alreadyEvaluated = false, deniedReason) {
1974
+ if (deniedReason !== void 0) return this.writeDenied(job, action, startTime, indexTxn, stores, skip, sourceRemote, deniedReason, signal);
1951
1975
  switch (action.type) {
1952
1976
  case "CREATE_DOCUMENT": return this.executeCreate(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal);
1953
- case "DELETE_DOCUMENT": return this.executeDelete(job, action, startTime, indexTxn, stores, sourceRemote, signal, verdictAlreadyDecided);
1954
- case "UPGRADE_DOCUMENT": return this.executeUpgrade(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, verdictAlreadyDecided);
1977
+ case "DELETE_DOCUMENT": return this.executeDelete(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, alreadyEvaluated);
1978
+ case "UPGRADE_DOCUMENT": return this.executeUpgrade(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, alreadyEvaluated);
1955
1979
  case "ADD_RELATIONSHIP": return this.executeAddRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
1956
1980
  case "REMOVE_RELATIONSHIP": return this.executeRemoveRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
1957
1981
  case "UPDATE_RELATIONSHIP": return this.executeUpdateRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
1958
1982
  default: return buildErrorResult(job, /* @__PURE__ */ new Error(`Unknown document action type: ${action.type}`), startTime);
1959
1983
  }
1960
1984
  }
1985
+ /** A refused operation holds a position in the stream but changes nothing. */
1986
+ async writeDenied(job, action, startTime, indexTxn, stores, skip, sourceRemote, deniedReason, signal) {
1987
+ let document;
1988
+ try {
1989
+ document = await stores.writeCache.getState(job.documentId, job.scope, job.branch, void 0, signal);
1990
+ } catch (error) {
1991
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
1992
+ }
1993
+ let operation = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
1994
+ documentId: job.documentId,
1995
+ scope: job.scope,
1996
+ branch: job.branch
1997
+ });
1998
+ operation.deniedReason = deniedReason;
1999
+ operation.hash = hashDocumentStateForScope(document, job.scope);
2000
+ const writeResult = await this.writeOperationToStore(job.documentId, document.header.documentType, job.scope, job.branch, operation, job, startTime, stores, signal);
2001
+ if (!Array.isArray(writeResult)) return writeResult;
2002
+ operation = writeResult[0];
2003
+ updateDocumentRevision(document, job.scope, operation.index);
2004
+ document.operations = {
2005
+ ...document.operations,
2006
+ [job.scope]: [...document.operations[job.scope] ?? [], operation]
2007
+ };
2008
+ stores.writeCache.putState(job.documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
2009
+ indexTxn.write([{
2010
+ ...operation,
2011
+ documentId: job.documentId,
2012
+ documentType: document.header.documentType,
2013
+ branch: job.branch,
2014
+ scope: job.scope,
2015
+ sourceRemote
2016
+ }]);
2017
+ stores.documentMetaCache.putDocumentMeta(job.documentId, job.branch, {
2018
+ state: document.state.document,
2019
+ documentType: document.header.documentType,
2020
+ documentScopeRevision: operation.index + 1
2021
+ });
2022
+ return buildSuccessResult(job, operation, job.documentId, document.header.documentType, JSON.stringify({
2023
+ header: document.header,
2024
+ document: document.state.document
2025
+ }), startTime);
2026
+ }
1961
2027
  async executeCreate(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal) {
1962
2028
  if (job.scope !== "document") return {
1963
2029
  job,
@@ -2005,7 +2071,7 @@ var DocumentActionHandler = class {
2005
2071
  });
2006
2072
  return buildSuccessResult(job, operation, document.header.id, document.header.documentType, resultingState, startTime);
2007
2073
  }
2008
- async executeDelete(job, action, startTime, indexTxn, stores, sourceRemote = "", signal, verdictAlreadyDecided = false) {
2074
+ async executeDelete(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal, alreadyEvaluated = false) {
2009
2075
  const input = action.input;
2010
2076
  if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("DELETE_DOCUMENT action requires a documentId in input"), startTime);
2011
2077
  const documentId = input.documentId;
@@ -2016,8 +2082,8 @@ var DocumentActionHandler = class {
2016
2082
  return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document before deletion: ${error instanceof Error ? error.message : String(error)}`), startTime);
2017
2083
  }
2018
2084
  const documentState = document.state.document;
2019
- if (documentState.isDeleted && !verdictAlreadyDecided) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
2020
- let operation = createOperation(action, getNextIndexForScope(document, job.scope), 0, {
2085
+ if (documentState.isDeleted && !alreadyEvaluated) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
2086
+ let operation = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
2021
2087
  documentId,
2022
2088
  scope: job.scope,
2023
2089
  branch: job.branch
@@ -2052,7 +2118,7 @@ var DocumentActionHandler = class {
2052
2118
  });
2053
2119
  return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
2054
2120
  }
2055
- async executeUpgrade(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal, verdictAlreadyDecided = false) {
2121
+ async executeUpgrade(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal, alreadyEvaluated = false) {
2056
2122
  const input = action.input;
2057
2123
  if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("UPGRADE_DOCUMENT action requires a documentId in input"), startTime);
2058
2124
  const documentId = input.documentId;
@@ -2065,7 +2131,7 @@ var DocumentActionHandler = class {
2065
2131
  return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document for upgrade: ${error instanceof Error ? error.message : String(error)}`), startTime);
2066
2132
  }
2067
2133
  const documentState = document.state.document;
2068
- if (documentState.isDeleted && !verdictAlreadyDecided) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
2134
+ if (documentState.isDeleted && !alreadyEvaluated) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
2069
2135
  const nextIndex = getNextIndexForScope(document, job.scope);
2070
2136
  let upgradePath;
2071
2137
  if (fromVersion > 0 && fromVersion < toVersion) try {
@@ -2360,6 +2426,22 @@ var SimpleJobExecutor = class {
2360
2426
  scope: owc.context.scope,
2361
2427
  branch: owc.context.branch
2362
2428
  });
2429
+ const reevaluationError = await this.reevaluateIfCriteriaMet({
2430
+ scope: job.scope,
2431
+ operations: actionResult.generatedOperations
2432
+ }, {
2433
+ job,
2434
+ startTime,
2435
+ indexTxn,
2436
+ stores,
2437
+ signal
2438
+ });
2439
+ if (reevaluationError) return {
2440
+ job,
2441
+ success: false,
2442
+ error: reevaluationError,
2443
+ duration: Date.now() - startTime
2444
+ };
2363
2445
  const ordinals = await stores.operationIndex.commit(indexTxn, signal);
2364
2446
  if (actionResult.operationsWithContext.length > 0) {
2365
2447
  for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
@@ -2415,12 +2497,13 @@ var SimpleJobExecutor = class {
2415
2497
  error: /* @__PURE__ */ new Error(`Invalid timestamp "${action.timestampUtcMs}" on action ${action.type} (id: ${action.id})`)
2416
2498
  };
2417
2499
  let lastYield = performance.now();
2500
+ const replayingAcceptedHistory = job.kind === "load" || deniedReasons !== void 0;
2418
2501
  for (let actionIndex = 0; actionIndex < actions.length; actionIndex++) {
2419
2502
  const action = actions[actionIndex];
2420
2503
  const skip = skipValues?.[actionIndex] ?? 0;
2421
2504
  const sourceOperation = sourceOperations?.[actionIndex];
2422
2505
  const deniedReason = deniedReasons?.[actionIndex];
2423
- const result = documentScopeActions.includes(action.type) ? await this.documentActionHandler.execute(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, this.featureFlags.documentDecisions && job.kind === "load") : await this.executeRegularAction(job, action, startTime, indexTxn, stores, skip, sourceOperation, sourceRemote, signal, deniedReason);
2506
+ const result = documentScopeActions.includes(action.type) ? await this.documentActionHandler.execute(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, this.featureFlags.documentDecisions && replayingAcceptedHistory, deniedReason) : await this.executeRegularAction(job, action, startTime, indexTxn, stores, skip, sourceOperation, sourceRemote, signal, deniedReason, replayingAcceptedHistory);
2424
2507
  const error = this.accumulateResultOrReturnError(result, generatedOperations, operationsWithContext);
2425
2508
  if (error !== null) return {
2426
2509
  success: false,
@@ -2445,11 +2528,11 @@ var SimpleJobExecutor = class {
2445
2528
  operationsWithContext
2446
2529
  };
2447
2530
  }
2448
- async executeRegularAction(job, action, startTime, indexTxn, stores, skip = 0, sourceOperation, sourceRemote = "", signal, deniedReason) {
2531
+ async executeRegularAction(job, action, startTime, indexTxn, stores, skip = 0, sourceOperation, sourceRemote = "", signal, deniedReason, replayingAcceptedHistory = false) {
2449
2532
  let appendCondition;
2450
2533
  let documentVersion;
2451
- const verdictAlreadyDecided = this.featureFlags.documentDecisions && job.kind === "load";
2452
- if (this.featureFlags.documentDecisions && !verdictAlreadyDecided) {
2534
+ const alreadyEvaluated = this.featureFlags.documentDecisions && replayingAcceptedHistory;
2535
+ if (this.featureFlags.documentDecisions && !alreadyEvaluated) {
2453
2536
  const target = {
2454
2537
  documentId: job.documentId,
2455
2538
  branch: job.branch
@@ -2471,7 +2554,7 @@ var SimpleJobExecutor = class {
2471
2554
  }, { scopeState: void 0 }) === "deny") return buildErrorResult(job, new DocumentDeletedError(job.documentId, built.model.document.deletedAtUtcIso), startTime);
2472
2555
  appendCondition = built.appendCondition;
2473
2556
  documentVersion = built.model.document.version;
2474
- } else if (verdictAlreadyDecided) documentVersion = (await stores.writeCache.getState(job.documentId, "document", job.branch, void 0, signal)).state.document.version;
2557
+ } else if (alreadyEvaluated) documentVersion = (await stores.writeCache.getState(job.documentId, "document", job.branch, void 0, signal)).state.document.version;
2475
2558
  else {
2476
2559
  let docMeta;
2477
2560
  try {
@@ -2489,7 +2572,7 @@ var SimpleJobExecutor = class {
2489
2572
  } catch (error) {
2490
2573
  return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
2491
2574
  }
2492
- if (job.kind !== "load") {
2575
+ if (!replayingAcceptedHistory) {
2493
2576
  const subject = {
2494
2577
  address: action.context?.signer?.user.address,
2495
2578
  key: action.context?.signer?.app.key
@@ -2509,18 +2592,25 @@ var SimpleJobExecutor = class {
2509
2592
  }
2510
2593
  let updatedDocument;
2511
2594
  if (deniedReason !== void 0) {
2512
- const denied = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
2595
+ const index = getNextIndexForScope(document, job.scope);
2596
+ const denied = createOperation(action, index, skip, {
2513
2597
  documentId: job.documentId,
2514
2598
  scope: job.scope,
2515
2599
  branch: job.branch
2516
2600
  });
2517
2601
  denied.deniedReason = deniedReason;
2518
- denied.hash = hashDocumentStateForScope(document, job.scope);
2602
+ let standing = document;
2603
+ if (skip > 0) try {
2604
+ standing = await stores.writeCache.getState(job.documentId, job.scope, job.branch, index - skip - 1, signal);
2605
+ } catch (error) {
2606
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
2607
+ }
2608
+ denied.hash = hashDocumentStateForScope(standing, job.scope);
2519
2609
  updatedDocument = {
2520
- ...document,
2610
+ ...standing,
2521
2611
  operations: {
2522
- ...document.operations,
2523
- [job.scope]: [...document.operations[job.scope] ?? [], denied]
2612
+ ...standing.operations,
2613
+ [job.scope]: [...standing.operations[job.scope] ?? [], denied]
2524
2614
  }
2525
2615
  };
2526
2616
  } else try {
@@ -2600,24 +2690,43 @@ var SimpleJobExecutor = class {
2600
2690
  };
2601
2691
  }
2602
2692
  /**
2603
- * If an operation happened that could change a decision model verdict, we
2604
- * need to re-evaluate across the streams that could be affected. Previously
2605
- * approved operations may need to be denied, which will apply new denied
2606
- * operations with appropriate skip.
2693
+ * Re-evaluates the document when a write meets both criteria: it was written
2694
+ * to a stream the model reads, and it is timestamped before an operation
2695
+ * already stored. The caller supplies the timestamp and the reactor does not replace
2696
+ * it, so a mutation job can write such an operation just as a load job can,
2697
+ * which is why both executeJob and executeLoadJob call this.
2607
2698
  */
2608
- async reevaluateReadingScopes(job, startTime, indexTxn, stores, signal) {
2699
+ async reevaluateIfCriteriaMet(criteria, executing) {
2700
+ if (!this.featureFlags.documentDecisions) return;
2701
+ const { job, stores, signal } = executing;
2702
+ if (!staticReadSet(documentDecisionModel({
2703
+ documentId: job.documentId,
2704
+ branch: job.branch
2705
+ })).some((stream) => stream.query.documentId === job.documentId && stream.query.scope === criteria.scope && stream.query.branch === job.branch)) return;
2706
+ const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
2707
+ const latest = Date.parse(revisions.latestTimestamp);
2708
+ if (!criteria.operations.some((operation) => Date.parse(operation.timestampUtcMs) < latest)) return;
2709
+ return this.reevaluateDocument(executing);
2710
+ }
2711
+ /**
2712
+ * Re-evaluates every scope the model evaluates. Where an operation's
2713
+ * evaluation differs from what is stored, the tail from that operation is
2714
+ * re-appended, carrying a skip that spans the indices it supersedes.
2715
+ */
2716
+ async reevaluateDocument(executing) {
2717
+ const { job, startTime, indexTxn, stores, signal } = executing;
2609
2718
  const definition = documentDecisionModel({
2610
2719
  documentId: job.documentId,
2611
2720
  branch: job.branch
2612
2721
  });
2613
2722
  const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
2614
2723
  for (const scope of Object.keys(revisions.revision)) {
2615
- if (!definition.judgesScope(scope)) continue;
2724
+ if (!definition.evaluatesScope(scope)) continue;
2616
2725
  const stored = (await stores.operationStore.getSince(job.documentId, scope, job.branch, -1, void 0, void 0, signal)).results;
2617
2726
  const effective = garbageCollect(sortOperations([...stored]));
2618
2727
  if (effective.length === 0) continue;
2619
- const recomputed = await deletionVerdictsByPosition(job.documentId, scope, job.branch, effective, stores.writeCache, stores.operationStore, signal);
2620
- const firstChange = effective.findIndex((operation, i) => operation.deniedReason !== recomputed[i]);
2728
+ const reevaluated = await evaluateDeletionsByPosition(job.documentId, scope, job.branch, effective, stores.writeCache, stores.operationStore, signal);
2729
+ const firstChange = effective.findIndex((operation, i) => operation.deniedReason !== reevaluated[i]);
2621
2730
  if (firstChange === -1) continue;
2622
2731
  const tail = effective.slice(firstChange);
2623
2732
  const nextIndex = revisions.revision[scope];
@@ -2625,7 +2734,7 @@ var SimpleJobExecutor = class {
2625
2734
  const result = await this.processActions({
2626
2735
  ...job,
2627
2736
  scope
2628
- }, tail.map((operation) => operation.action), startTime, indexTxn, stores, tail.map((_, i) => i === 0 ? retractionSkip(nextIndex, tail[0].index) : 0), void 0, "", signal, recomputed.slice(firstChange));
2737
+ }, tail.map((operation) => operation.action), startTime, indexTxn, stores, tail.map((_, i) => i === 0 ? retractionSkip(nextIndex, tail[0].index) : 0), void 0, "", signal, reevaluated.slice(firstChange));
2629
2738
  if (!result.success) return result.error ?? /* @__PURE__ */ new Error(`Re-evaluation of ${job.documentId} ${scope} failed`);
2630
2739
  }
2631
2740
  }
@@ -2726,7 +2835,7 @@ var SimpleJobExecutor = class {
2726
2835
  const skipValues = reshuffledOperations.map((operation) => operation.skip);
2727
2836
  let deniedReasons;
2728
2837
  if (this.featureFlags.documentDecisions) try {
2729
- deniedReasons = await deletionVerdictsByPosition(job.documentId, scope, job.branch, reshuffledOperations, stores.writeCache, stores.operationStore, signal);
2838
+ deniedReasons = await evaluateDeletionsByPosition(job.documentId, scope, job.branch, reshuffledOperations, stores.writeCache, stores.operationStore, signal);
2730
2839
  } catch (error) {
2731
2840
  return {
2732
2841
  job,
@@ -2745,23 +2854,22 @@ var SimpleJobExecutor = class {
2745
2854
  };
2746
2855
  stores.writeCache.invalidate(job.documentId, scope, job.branch);
2747
2856
  if (scope === "document") stores.documentMetaCache.invalidate(job.documentId, job.branch);
2748
- const readsThisStream = staticReadSet(documentDecisionModel({
2749
- documentId: job.documentId,
2750
- branch: job.branch
2751
- })).some((stream) => stream.query.documentId === job.documentId && stream.query.scope === scope && stream.query.branch === job.branch);
2752
- if (this.featureFlags.documentDecisions && readsThisStream) {
2753
- const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
2754
- const latest = Date.parse(revisions.latestTimestamp);
2755
- if (result.generatedOperations.some((operation) => Date.parse(operation.timestampUtcMs) < latest)) {
2756
- const error = await this.reevaluateReadingScopes(job, startTime, indexTxn, stores, signal);
2757
- if (error) return {
2758
- job,
2759
- success: false,
2760
- error,
2761
- duration: Date.now() - startTime
2762
- };
2763
- }
2764
- }
2857
+ const reevaluationError = await this.reevaluateIfCriteriaMet({
2858
+ scope,
2859
+ operations: result.generatedOperations
2860
+ }, {
2861
+ job,
2862
+ startTime,
2863
+ indexTxn,
2864
+ stores,
2865
+ signal
2866
+ });
2867
+ if (reevaluationError) return {
2868
+ job,
2869
+ success: false,
2870
+ error: reevaluationError,
2871
+ duration: Date.now() - startTime
2872
+ };
2765
2873
  return {
2766
2874
  job,
2767
2875
  success: true,
@@ -3622,4 +3730,4 @@ const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "pow
3622
3730
  //#endregion
3623
3731
  export { AuthorizationDeniedError as A, DuplicateOperationError as C, DuplicateModuleError as D, DuplicateManifestError as E, throwIfAborted as F, __exportAll as I, DocumentNotFoundError as M, matchesScope as N, InvalidModuleError as O, parsePagingOptions as P, AppendConditionFailedError as S, RevisionMismatchError as T, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, CollectionMembershipCache as b, KyselyKeyframeStore as c, DriveCollectionId as d, buildDecisionModel as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, DocumentDeletedError as j, ModuleNotFoundError as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, instrumentPgPool as o, KyselyExecutionScope as p, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, createConsistencyToken as v, OptimisticLockError as w, APPEND_CONDITION_FAILED_PREFIX as x, createEmptyConsistencyToken as y };
3624
3732
 
3625
- //# sourceMappingURL=drive-container-types-BNwEHEXP.js.map
3733
+ //# sourceMappingURL=drive-container-types-CPQDOggp.js.map