@powerhousedao/reactor 6.2.2-dev.53 → 6.2.2-dev.55

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, c as KyselyKeyframeStore, f as KyselyExecutionScope, g as KyselyOperationIndex, h as KyselyWriteCache, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as CollectionMembershipCache } from "./drive-container-types-RZa1wukO.js";
1
+ import { _ as DocumentMetaCache, c as KyselyKeyframeStore, f as KyselyExecutionScope, g as KyselyOperationIndex, h as KyselyWriteCache, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as CollectionMembershipCache } from "./drive-container-types-bVQ_8YwX.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-NT9b3rNm.js.map
83
+ //# sourceMappingURL=build-worker-executor-DBHkoWBR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-worker-executor-NT9b3rNm.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-DBHkoWBR.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"}
@@ -208,8 +208,14 @@ var UpgradePreconditionFailedError = class UpgradePreconditionFailedError extend
208
208
  */
209
209
  var DocumentNotFoundError = class DocumentNotFoundError extends Error {
210
210
  documentId;
211
- constructor(documentId) {
212
- super(`Document ${documentId} not found`);
211
+ /**
212
+ * @param message Overrides the default text. A handler that knows which of
213
+ * several documents an action reads - a relationship's source, say - says so
214
+ * here rather than rewrapping in a bare Error, which would strip the name the
215
+ * executor classifies by.
216
+ */
217
+ constructor(documentId, message) {
218
+ super(message ?? `Document ${documentId} not found`);
213
219
  this.name = "DocumentNotFoundError";
214
220
  this.documentId = documentId;
215
221
  Error.captureStackTrace(this, DocumentNotFoundError);
@@ -912,6 +918,20 @@ var AppendConditionFailedError = class extends Error {
912
918
  }
913
919
  };
914
920
  //#endregion
921
+ //#region src/executor/types.ts
922
+ /** How long a deferred job waits for its document before it fails. */
923
+ const DEFAULT_DEFERRED_JOB_TTL_MS = 3e4;
924
+ /**
925
+ * Event types for the job executor
926
+ */
927
+ const JobExecutorEventTypes = {
928
+ JOB_STARTED: 2e4,
929
+ JOB_COMPLETED: 20001,
930
+ JOB_FAILED: 20002,
931
+ EXECUTOR_STARTED: 20003,
932
+ EXECUTOR_STOPPED: 20004
933
+ };
934
+ //#endregion
915
935
  //#region src/cache/collection-membership-cache.ts
916
936
  var CollectionMembershipCache = class CollectionMembershipCache {
917
937
  cache = /* @__PURE__ */ new Map();
@@ -3004,6 +3024,7 @@ var DocumentActionHandler = class {
3004
3024
  try {
3005
3025
  sourceDoc = await stores.writeCache.getState(input.sourceId, "document", job.branch, void 0, signal);
3006
3026
  } catch (error) {
3027
+ if (DocumentNotFoundError.isError(error)) return buildErrorResult(job, new DocumentNotFoundError(input.sourceId, `${actionTypeName}: source document ${input.sourceId} not found`), startTime);
3007
3028
  return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName}: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`), startTime);
3008
3029
  }
3009
3030
  let operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope), skip, {
@@ -3159,6 +3180,7 @@ var SimpleJobExecutor = class {
3159
3180
  maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,
3160
3181
  maxConcurrency: config.maxConcurrency ?? 1,
3161
3182
  jobTimeoutMs: config.jobTimeoutMs ?? 3e4,
3183
+ deferredJobTtlMs: config.deferredJobTtlMs ?? 3e4,
3162
3184
  retryBaseDelayMs: config.retryBaseDelayMs ?? 100,
3163
3185
  retryMaxDelayMs: config.retryMaxDelayMs ?? 5e3,
3164
3186
  yieldDeadlineMs: config.yieldDeadlineMs ?? 50
@@ -4557,8 +4579,8 @@ function createForwardingPoolInstrumentation(name) {
4557
4579
  }
4558
4580
  //#endregion
4559
4581
  //#region src/storage/migrations/001_create_operation_table.ts
4560
- var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
4561
- async function up$16(db) {
4582
+ var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$18 });
4583
+ async function up$18(db) {
4562
4584
  await db.schema.createTable("Operation").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("jobId", "text", (col) => col.notNull()).addColumn("opId", "text", (col) => col.notNull()).addColumn("prevOpId", "text", (col) => col.notNull()).addColumn("writeTimestampUtcMs", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("timestampUtcMs", "timestamptz", (col) => col.notNull()).addColumn("index", "integer", (col) => col.notNull()).addColumn("action", "jsonb", (col) => col.notNull()).addColumn("skip", "integer", (col) => col.notNull()).addColumn("error", "text").addColumn("hash", "text", (col) => col.notNull()).addUniqueConstraint("unique_revision", [
4563
4585
  "documentId",
4564
4586
  "scope",
@@ -4583,8 +4605,8 @@ async function up$16(db) {
4583
4605
  }
4584
4606
  //#endregion
4585
4607
  //#region src/storage/migrations/002_create_keyframe_table.ts
4586
- var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
4587
- async function up$15(db) {
4608
+ var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$17 });
4609
+ async function up$17(db) {
4588
4610
  await db.schema.createTable("Keyframe").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("revision", "integer", (col) => col.notNull()).addColumn("document", "jsonb", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_keyframe", [
4589
4611
  "documentId",
4590
4612
  "scope",
@@ -4600,14 +4622,14 @@ async function up$15(db) {
4600
4622
  }
4601
4623
  //#endregion
4602
4624
  //#region src/storage/migrations/003_create_document_table.ts
4603
- var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
4604
- async function up$14(db) {
4625
+ var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
4626
+ async function up$16(db) {
4605
4627
  await db.schema.createTable("Document").addColumn("id", "text", (col) => col.primaryKey()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4606
4628
  }
4607
4629
  //#endregion
4608
4630
  //#region src/storage/migrations/004_create_document_relationship_table.ts
4609
- var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
4610
- async function up$13(db) {
4631
+ var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
4632
+ async function up$15(db) {
4611
4633
  await db.schema.createTable("DocumentRelationship").addColumn("id", "text", (col) => col.primaryKey()).addColumn("sourceId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("targetId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("relationshipType", "text", (col) => col.notNull()).addColumn("metadata", "jsonb").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_source_target_type", [
4612
4634
  "sourceId",
4613
4635
  "targetId",
@@ -4619,14 +4641,14 @@ async function up$13(db) {
4619
4641
  }
4620
4642
  //#endregion
4621
4643
  //#region src/storage/migrations/005_create_indexer_state_table.ts
4622
- var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
4623
- async function up$12(db) {
4644
+ var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
4645
+ async function up$14(db) {
4624
4646
  await db.schema.createTable("IndexerState").addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()).addColumn("lastOperationId", "integer", (col) => col.notNull()).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4625
4647
  }
4626
4648
  //#endregion
4627
4649
  //#region src/storage/migrations/006_create_document_snapshot_table.ts
4628
- var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
4629
- async function up$11(db) {
4650
+ var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
4651
+ async function up$13(db) {
4630
4652
  await db.schema.createTable("DocumentSnapshot").addColumn("id", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("slug", "text").addColumn("name", "text").addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("content", "jsonb", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("lastOperationIndex", "integer", (col) => col.notNull()).addColumn("lastOperationHash", "text", (col) => col.notNull()).addColumn("lastUpdatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("snapshotVersion", "integer", (col) => col.notNull().defaultTo(1)).addColumn("identifiers", "jsonb").addColumn("metadata", "jsonb").addColumn("isDeleted", "boolean", (col) => col.notNull().defaultTo(false)).addColumn("deletedAt", "timestamptz").addUniqueConstraint("unique_doc_scope_branch", [
4631
4653
  "documentId",
4632
4654
  "scope",
@@ -4647,8 +4669,8 @@ async function up$11(db) {
4647
4669
  }
4648
4670
  //#endregion
4649
4671
  //#region src/storage/migrations/007_create_slug_mapping_table.ts
4650
- var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
4651
- async function up$10(db) {
4672
+ var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
4673
+ async function up$12(db) {
4652
4674
  await db.schema.createTable("SlugMapping").addColumn("slug", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_docid_scope_branch", [
4653
4675
  "documentId",
4654
4676
  "scope",
@@ -4658,14 +4680,14 @@ async function up$10(db) {
4658
4680
  }
4659
4681
  //#endregion
4660
4682
  //#region src/storage/migrations/008_create_view_state_table.ts
4661
- var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
4662
- async function up$9(db) {
4683
+ var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
4684
+ async function up$11(db) {
4663
4685
  await db.schema.createTable("ViewState").addColumn("readModelId", "text", (col) => col.primaryKey()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(0)).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4664
4686
  }
4665
4687
  //#endregion
4666
4688
  //#region src/storage/migrations/009_create_operation_index_tables.ts
4667
- var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$8 });
4668
- async function up$8(db) {
4689
+ var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
4690
+ async function up$10(db) {
4669
4691
  await db.schema.createTable("document_collections").addColumn("documentId", "text", (col) => col.notNull()).addColumn("collectionId", "text", (col) => col.notNull()).addColumn("joinedOrdinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("leftOrdinal", "bigint").addPrimaryKeyConstraint("document_collections_pkey", ["documentId", "collectionId"]).execute();
4670
4692
  await db.schema.createIndex("idx_document_collections_collectionId").on("document_collections").column("collectionId").execute();
4671
4693
  await db.schema.createIndex("idx_doc_collections_collection_range").on("document_collections").columns(["collectionId", "joinedOrdinal"]).execute();
@@ -4679,8 +4701,8 @@ async function up$8(db) {
4679
4701
  }
4680
4702
  //#endregion
4681
4703
  //#region src/storage/migrations/010_create_sync_tables.ts
4682
- var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$7 });
4683
- async function up$7(db) {
4704
+ var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
4705
+ async function up$9(db) {
4684
4706
  await db.schema.createTable("sync_remotes").addColumn("name", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("channel_type", "text", (col) => col.notNull()).addColumn("channel_id", "text", (col) => col.notNull().defaultTo("")).addColumn("remote_name", "text", (col) => col.notNull().defaultTo("")).addColumn("channel_parameters", "jsonb", (col) => col.notNull().defaultTo(sql`'{}'::jsonb`)).addColumn("filter_document_ids", "jsonb").addColumn("filter_scopes", "jsonb").addColumn("filter_branch", "text", (col) => col.notNull().defaultTo("main")).addColumn("push_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("push_last_success_utc_ms", "text").addColumn("push_last_failure_utc_ms", "text").addColumn("push_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("pull_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("pull_last_success_utc_ms", "text").addColumn("pull_last_failure_utc_ms", "text").addColumn("pull_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4685
4707
  await db.schema.createIndex("idx_sync_remotes_collection").on("sync_remotes").column("collection_id").execute();
4686
4708
  await db.schema.createTable("sync_cursors").addColumn("remote_name", "text", (col) => col.primaryKey().references("sync_remotes.name").onDelete("cascade")).addColumn("cursor_ordinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("last_synced_at_utc_ms", "text").addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
@@ -4688,8 +4710,8 @@ async function up$7(db) {
4688
4710
  }
4689
4711
  //#endregion
4690
4712
  //#region src/storage/migrations/011_add_cursor_type_column.ts
4691
- var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$6 });
4692
- async function up$6(db) {
4713
+ var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$8 });
4714
+ async function up$8(db) {
4693
4715
  await db.deleteFrom("sync_cursors").where("remote_name", "like", "outbox::%").execute();
4694
4716
  await db.deleteFrom("sync_remotes").where("name", "like", "outbox::%").execute();
4695
4717
  await db.schema.dropTable("sync_cursors").execute();
@@ -4698,64 +4720,64 @@ async function up$6(db) {
4698
4720
  }
4699
4721
  //#endregion
4700
4722
  //#region src/storage/migrations/012_add_source_remote_column.ts
4701
- var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$5 });
4702
- async function up$5(db) {
4723
+ var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$7 });
4724
+ async function up$7(db) {
4703
4725
  await db.schema.alterTable("operation_index_operations").addColumn("sourceRemote", "text", (col) => col.notNull().defaultTo("")).execute();
4704
4726
  }
4705
4727
  //#endregion
4706
4728
  //#region src/storage/migrations/013_create_sync_dead_letters_table.ts
4707
- var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$4 });
4708
- async function up$4(db) {
4729
+ var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$6 });
4730
+ async function up$6(db) {
4709
4731
  await db.schema.createTable("sync_dead_letters").addColumn("ordinal", "serial", (col) => col.primaryKey()).addColumn("id", "text", (col) => col.unique().notNull()).addColumn("job_id", "text", (col) => col.notNull()).addColumn("job_dependencies", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("remote_name", "text", (col) => col.notNull().references("sync_remotes.name").onDelete("cascade")).addColumn("document_id", "text", (col) => col.notNull()).addColumn("scopes", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("branch", "text", (col) => col.notNull()).addColumn("operations", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("error_source", "text", (col) => col.notNull()).addColumn("error_message", "text", (col) => col.notNull()).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4710
4732
  await db.schema.createIndex("idx_sync_dead_letters_remote").on("sync_dead_letters").column("remote_name").execute();
4711
4733
  }
4712
4734
  //#endregion
4713
4735
  //#region src/storage/migrations/014_create_processor_cursor_table.ts
4714
- var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$3 });
4715
- async function up$3(db) {
4736
+ var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$5 });
4737
+ async function up$5(db) {
4716
4738
  await db.schema.createTable("ProcessorCursor").addColumn("processorId", "text", (col) => col.primaryKey()).addColumn("factoryId", "text", (col) => col.notNull()).addColumn("driveId", "text", (col) => col.notNull()).addColumn("processorIndex", "integer", (col) => col.notNull()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(sql`0`)).addColumn("status", "text", (col) => col.notNull().defaultTo(sql`'active'`)).addColumn("lastError", "text").addColumn("lastErrorTimestamp", "timestamptz").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4717
4739
  }
4718
4740
  //#endregion
4719
4741
  //#region src/storage/migrations/015_add_operation_denied_reason.ts
4720
4742
  var _015_add_operation_denied_reason_exports = /* @__PURE__ */ __exportAll({
4721
- down: () => down$2,
4722
- up: () => up$2
4743
+ down: () => down$4,
4744
+ up: () => up$4
4723
4745
  });
4724
4746
  /**
4725
4747
  * Records why authorization refused an operation. Separate from `error` so a
4726
4748
  * denial is distinguishable from a reducer failure without matching on a
4727
4749
  * message. Null for every operation written before decisions were enforced.
4728
4750
  */
4729
- async function up$2(db) {
4751
+ async function up$4(db) {
4730
4752
  await db.schema.alterTable("Operation").addColumn("deniedReason", "text").execute();
4731
4753
  await db.schema.alterTable("operation_index_operations").addColumn("deniedReason", "text").execute();
4732
4754
  }
4733
- async function down$2(db) {
4755
+ async function down$4(db) {
4734
4756
  await db.schema.alterTable("operation_index_operations").dropColumn("deniedReason").execute();
4735
4757
  await db.schema.alterTable("Operation").dropColumn("deniedReason").execute();
4736
4758
  }
4737
4759
  //#endregion
4738
4760
  //#region src/storage/migrations/016_add_dead_letter_error_type.ts
4739
4761
  var _016_add_dead_letter_error_type_exports = /* @__PURE__ */ __exportAll({
4740
- down: () => down$1,
4741
- up: () => up$1
4762
+ down: () => down$3,
4763
+ up: () => up$3
4742
4764
  });
4743
4765
  /**
4744
4766
  * The classification a dead letter falls into, stored because it decides whether
4745
4767
  * the document stays quarantined and the in-memory error is gone after a restart.
4746
4768
  * Defaulted rather than nullable, so a pre-existing row rehydrates.
4747
4769
  */
4748
- async function up$1(db) {
4770
+ async function up$3(db) {
4749
4771
  await db.schema.alterTable("sync_dead_letters").addColumn("error_type", "text", (col) => col.notNull().defaultTo("UNCLASSIFIED")).execute();
4750
4772
  }
4751
- async function down$1(db) {
4773
+ async function down$3(db) {
4752
4774
  await db.schema.alterTable("sync_dead_letters").dropColumn("error_type").execute();
4753
4775
  }
4754
4776
  //#endregion
4755
4777
  //#region src/storage/migrations/017_create_group_references.ts
4756
4778
  var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
4757
- down: () => down,
4758
- up: () => up
4779
+ down: () => down$2,
4780
+ up: () => up$2
4759
4781
  });
4760
4782
  /**
4761
4783
  * One row per (document, group) reference ever discovered from an auth
@@ -4766,14 +4788,82 @@ var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
4766
4788
  * requires (sync), and by groupId for the documents a group change affects
4767
4789
  * (re-evaluation).
4768
4790
  */
4769
- async function up(db) {
4791
+ async function up$2(db) {
4770
4792
  await db.schema.createTable("group_references").addColumn("documentId", "text", (col) => col.notNull()).addColumn("groupId", "text", (col) => col.notNull()).addPrimaryKeyConstraint("group_references_pkey", ["documentId", "groupId"]).execute();
4771
4793
  await db.schema.createIndex("idx_group_references_groupId").on("group_references").column("groupId").execute();
4772
4794
  }
4773
- async function down(db) {
4795
+ async function down$2(db) {
4774
4796
  await db.schema.dropTable("group_references").execute();
4775
4797
  }
4776
4798
  //#endregion
4799
+ //#region src/storage/migrations/018_add_sync_remote_bound_address.ts
4800
+ var _018_add_sync_remote_bound_address_exports = /* @__PURE__ */ __exportAll({
4801
+ down: () => down$1,
4802
+ up: () => up$1
4803
+ });
4804
+ /**
4805
+ * The address a sync channel is bound to, so a channel created by one subject
4806
+ * cannot be polled by another.
4807
+ *
4808
+ * Nullable rather than defaulted: null is a channel nobody has claimed, which is
4809
+ * what every pre-existing row is and what an anonymously created channel stays
4810
+ * until its first authenticated poll adopts it. A default would claim them all
4811
+ * for one address.
4812
+ */
4813
+ async function up$1(db) {
4814
+ await db.schema.alterTable("sync_remotes").addColumn("bound_address", "text").execute();
4815
+ }
4816
+ async function down$1(db) {
4817
+ await db.schema.alterTable("sync_remotes").dropColumn("bound_address").execute();
4818
+ }
4819
+ //#endregion
4820
+ //#region src/storage/migrations/019_require_action_id.ts
4821
+ var _019_require_action_id_exports = /* @__PURE__ */ __exportAll({
4822
+ down: () => down,
4823
+ up: () => up
4824
+ });
4825
+ /**
4826
+ * Makes an operation whose action carries no id physically unstorable.
4827
+ *
4828
+ * The id is not decoration: `deriveOperationId` hashes it into the operation id
4829
+ * and replay dedupes incoming operations by it, so an action without one
4830
+ * collapses every id-less operation on a document/scope/branch onto a single
4831
+ * derived operation id. The API rejects such an action now, and this is the
4832
+ * last line of defense behind it.
4833
+ *
4834
+ * Both tables are constrained because sync reads operations from the index
4835
+ * rather than the operation table, so poison reaching only the index would
4836
+ * still be served to a replica.
4837
+ *
4838
+ * Pre-existing rows are backfilled rather than left behind a NOT VALID
4839
+ * constraint: a row the index and the operation table disagree about is worse
4840
+ * than a missing id, because dedup keys off the value each side serves. The
4841
+ * backfill therefore mints one id per operation and writes that same id to both
4842
+ * tables, joined on the identity they share. Rewriting the action is safe: the
4843
+ * operation hash is taken over the resulting state, not over the action, and a
4844
+ * signature is verified from the params carried in the signature tuple, which
4845
+ * do not include the action id.
4846
+ *
4847
+ * The empty string is rejected alongside null. It derives the same colliding
4848
+ * operation id as an absent id, so admitting it would leave the hole open.
4849
+ */
4850
+ async function up(db) {
4851
+ await db.updateTable("Operation").set({ action: sql`jsonb_set(action, '{id}', to_jsonb(gen_random_uuid()::text))` }).where(sql`jsonb_typeof(action) = 'object' and coalesce(action->>'id', '') = ''`).execute();
4852
+ await db.updateTable("operation_index_operations as oio").from("Operation as op").set({ action: sql`jsonb_set(oio.action, '{id}', to_jsonb(op.action->>'id'))` }).whereRef("oio.opId", "=", "op.opId").whereRef("oio.index", "=", "op.index").whereRef("oio.skip", "=", "op.skip").where(sql`jsonb_typeof(oio.action) = 'object' and coalesce(oio.action->>'id', '') = ''`).where(sql`coalesce(op.action->>'id', '') <> ''`).execute();
4853
+ await db.updateTable("operation_index_operations").set({ action: sql`jsonb_set(action, '{id}', to_jsonb(gen_random_uuid()::text))` }).where(sql`jsonb_typeof(action) = 'object' and coalesce(action->>'id', '') = ''`).execute();
4854
+ await db.schema.alterTable("Operation").addCheckConstraint("action_must_have_id", sql`action->>'id' is not null and action->>'id' <> ''`).execute();
4855
+ await db.schema.alterTable("operation_index_operations").addCheckConstraint("action_must_have_id", sql`action->>'id' is not null and action->>'id' <> ''`).execute();
4856
+ }
4857
+ /**
4858
+ * Only the constraints are dropped. The backfilled ids stay: they are the ids
4859
+ * their operations are now known by, and reverting them would reintroduce the
4860
+ * collision the migration removed.
4861
+ */
4862
+ async function down(db) {
4863
+ await db.schema.alterTable("operation_index_operations").dropConstraint("action_must_have_id").execute();
4864
+ await db.schema.alterTable("Operation").dropConstraint("action_must_have_id").execute();
4865
+ }
4866
+ //#endregion
4777
4867
  //#region src/storage/migrations/migrator.ts
4778
4868
  const REACTOR_SCHEMA = "reactor";
4779
4869
  const migrations = {
@@ -4793,14 +4883,22 @@ const migrations = {
4793
4883
  "014_create_processor_cursor_table": _014_create_processor_cursor_table_exports,
4794
4884
  "015_add_operation_denied_reason": _015_add_operation_denied_reason_exports,
4795
4885
  "016_add_dead_letter_error_type": _016_add_dead_letter_error_type_exports,
4796
- "017_create_group_references": _017_create_group_references_exports
4886
+ "017_create_group_references": _017_create_group_references_exports,
4887
+ "018_add_sync_remote_bound_address": _018_add_sync_remote_bound_address_exports,
4888
+ "019_require_action_id": _019_require_action_id_exports
4797
4889
  };
4798
4890
  var ProgrammaticMigrationProvider = class {
4799
4891
  getMigrations() {
4800
4892
  return Promise.resolve(migrations);
4801
4893
  }
4802
4894
  };
4803
- async function runMigrations(db, schema = REACTOR_SCHEMA) {
4895
+ /**
4896
+ * Applies every pending migration, or every one up to and including `upTo`.
4897
+ *
4898
+ * The bound exists so a test can reach the schema a data migration is written
4899
+ * against, populate it, and then migrate across the migration under test.
4900
+ */
4901
+ async function runMigrations(db, schema = REACTOR_SCHEMA, upTo) {
4804
4902
  try {
4805
4903
  await sql`CREATE SCHEMA IF NOT EXISTS ${sql.id(schema)}`.execute(db);
4806
4904
  } catch (error) {
@@ -4818,7 +4916,7 @@ async function runMigrations(db, schema = REACTOR_SCHEMA) {
4818
4916
  let error;
4819
4917
  let results;
4820
4918
  try {
4821
- const result = await migrator.migrateToLatest();
4919
+ const result = upTo ? await migrator.migrateTo(upTo) : await migrator.migrateToLatest();
4822
4920
  error = result.error;
4823
4921
  results = result.results;
4824
4922
  } catch (e) {
@@ -4847,6 +4945,6 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
4847
4945
  //#region src/core/drive-container-types.ts
4848
4946
  const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
4849
4947
  //#endregion
4850
- export { createEmptyConsistencyToken as A, DocumentDeletedError as B, RevisionMismatchError as C, ModuleNotFoundError as D, InvalidModuleError as E, authDecisionModel as F, matchesScope as G, ExcessiveReshuffleError as H, buildDecisionModel as I, __exportAll as J, parsePagingOptions as K, AuthEnforcementDisabledError as L, decideAtHead as M, selectDecisionModel as N, GATED_DOCUMENT_ACTIONS as O, documentDecisionModel as P, AuthTimestampNotMonotonicError as R, OptimisticLockError as S, DuplicateModuleError as T, InvalidOperationTimestampError as U, DocumentNotFoundError as V, UpgradePreconditionFailedError as W, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, AppendConditionFailedError as b, KyselyKeyframeStore as c, DriveCollectionId as d, KyselyExecutionScope as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, targetDocumentId as j, createConsistencyToken as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, instrumentPgPool as o, resolveFeatureFlags as p, throwIfAborted as q, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, CollectionMembershipCache as v, DuplicateManifestError as w, DuplicateOperationError as x, APPEND_CONDITION_FAILED_PREFIX as y, AuthorizationDeniedError as z };
4948
+ export { GATED_DOCUMENT_ACTIONS as A, AuthTimestampNotMonotonicError as B, DuplicateOperationError as C, DuplicateModuleError as D, DuplicateManifestError as E, selectDecisionModel as F, InvalidOperationTimestampError as G, DocumentDeletedError as H, documentDecisionModel as I, parsePagingOptions as J, UpgradePreconditionFailedError as K, authDecisionModel as L, createEmptyConsistencyToken as M, targetDocumentId as N, InvalidModuleError as O, decideAtHead as P, buildDecisionModel as R, AppendConditionFailedError as S, RevisionMismatchError as T, DocumentNotFoundError as U, AuthorizationDeniedError as V, ExcessiveReshuffleError as W, __exportAll as X, throwIfAborted as Y, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, JobExecutorEventTypes as b, KyselyKeyframeStore as c, DriveCollectionId as d, KyselyExecutionScope as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, createConsistencyToken as j, ModuleNotFoundError as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, instrumentPgPool as o, resolveFeatureFlags as p, matchesScope as q, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, CollectionMembershipCache as v, OptimisticLockError as w, APPEND_CONDITION_FAILED_PREFIX as x, DEFAULT_DEFERRED_JOB_TTL_MS as y, AuthEnforcementDisabledError as z };
4851
4949
 
4852
- //# sourceMappingURL=drive-container-types-RZa1wukO.js.map
4950
+ //# sourceMappingURL=drive-container-types-bVQ_8YwX.js.map