@powerhousedao/reactor 6.2.3-dev.4 → 6.2.3-dev.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{build-worker-executor-DnU3ZU4H.js → build-worker-executor-B9IfcRij.js} +30 -8
- package/dist/build-worker-executor-B9IfcRij.js.map +1 -0
- package/dist/{document-indexer-C5Gsa1B3.js → document-indexer-CZqIpdz2.js} +981 -104
- package/dist/document-indexer-CZqIpdz2.js.map +1 -0
- package/dist/entry.d.ts +2 -1
- package/dist/entry.js +13 -6
- package/dist/entry.js.map +1 -1
- package/dist/{errors-D3S6Eysd.js → errors-BM_ULVWd.js} +9 -1
- package/dist/{errors-D3S6Eysd.js.map → errors-BM_ULVWd.js.map} +1 -1
- package/dist/{drive-container-types-yZrksiJR.js → forwarding-logger-DMqDs9tE.js} +2631 -369
- package/dist/forwarding-logger-DMqDs9tE.js.map +1 -0
- package/dist/index.d.ts +4811 -3193
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4103 -880
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.d.ts +2 -1
- package/dist/projection-entry.js +99 -21
- package/dist/projection-entry.js.map +1 -1
- package/dist/{projection-shard-manager-CPQc5XHL.js → projection-shard-manager-BSK-GEyP.js} +24 -4
- package/dist/projection-shard-manager-BSK-GEyP.js.map +1 -0
- package/dist/{projection-worker-wI4PwcV2.js → projection-worker-D8uNFdwX.js} +1 -1
- package/dist/{projection-worker-wI4PwcV2.js.map → projection-worker-D8uNFdwX.js.map} +1 -1
- package/dist/{transport-CuogVKN_.js → transport-B3G50KVd.js} +1 -1
- package/dist/{transport-CuogVKN_.js.map → transport-B3G50KVd.js.map} +1 -1
- package/dist/{transport-ByGviWdZ.js → transport-CZDHzfvy.js} +1 -1
- package/dist/{transport-ByGviWdZ.js.map → transport-CZDHzfvy.js.map} +1 -1
- package/dist/{worker-handle-CrERzl8s.js → worker-handle-D-19Jmpz.js} +30 -17
- package/dist/worker-handle-D-19Jmpz.js.map +1 -0
- package/package.json +20 -10
- package/dist/build-worker-executor-DnU3ZU4H.js.map +0 -1
- package/dist/document-indexer-C5Gsa1B3.js.map +0 -1
- package/dist/drive-container-types-yZrksiJR.js.map +0 -1
- package/dist/error-info-Cpu4OY3o.js +0 -62
- package/dist/error-info-Cpu4OY3o.js.map +0 -1
- package/dist/forwarding-logger-BBkMSxuJ.js +0 -85
- package/dist/forwarding-logger-BBkMSxuJ.js.map +0 -1
- package/dist/projection-shard-manager-CPQc5XHL.js.map +0 -1
- package/dist/types-DMKLa0Ok.js +0 -33
- package/dist/types-DMKLa0Ok.js.map +0 -1
- package/dist/worker-HPysBfhx.js +0 -22
- package/dist/worker-HPysBfhx.js.map +0 -1
- package/dist/worker-handle-CrERzl8s.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["toErrorInfo","uuidv4","addFileAction","addFolderAction","deleteNodeAction","updateNodeAction","moveNodeAction","copyNodeAction","#byDocumentId","#clearTimer","#expire","#timers","#markFailed","uuidv4","firstOrdinalOf","uuidv4","toErrorInfo"],"sources":["../src/actions/index.ts","../src/core/utils.ts","../src/shared/types.ts","../src/client/drive-client.ts","../src/shared/awaiter.ts","../src/client/cursor.ts","../src/client/types.ts","../src/decision/read-gate.ts","../src/client/util.ts","../src/client/reactor-client.ts","../src/queue/types.ts","../src/executor/job-result-handler.ts","../src/executor/deferred-jobs.ts","../src/executor/worker-pool-router.ts","../src/executor/worker-pool-job-executor-manager.ts","../src/executor/simple-job-executor-manager.ts","../src/job-tracker/in-memory-job-tracker.ts","../src/processors/utils.ts","../src/processors/processor-manager.ts","../src/queue/job-execution-handle.ts","../src/queue/queue.ts","../src/projection/read-model-kinds.ts","../src/core/model-sources.ts","../src/registry/document-model-resolver.ts","../src/subs/default-error-handler.ts","../src/subs/react-subscription-manager.ts","../src/subs/subscription-notification-read-model.ts","../src/core/group-reevaluation-trigger.ts","../src/sync/types.ts","../src/sync/mailbox.ts","../src/sync/buffered-mailbox.ts","../src/sync/errors.ts","../src/sync/sync-operation.ts","../src/sync/utils.ts","../src/sync/channels/interval-poll-timer.ts","../src/sync/channels/utils.ts","../src/sync/channels/gql-req-channel.ts","../src/sync/channels/gql-request-channel-factory.ts","../src/sync/channels/gql-res-channel.ts","../src/sync/channels/gql-response-channel-factory.ts","../src/storage/kysely/sync-cursor-storage.ts","../src/storage/kysely/sync-dead-letter-storage.ts","../src/storage/kysely/sync-remote-storage.ts","../src/sync/batch-aggregator.ts","../src/sync/sync-awaiter.ts","../src/sync/sync-status-tracker.ts","../src/sync/sync-manager.ts","../src/sync/sync-builder.ts","../src/core/create-default-database.ts","../src/shared/factories.ts","../src/core/types.ts","../src/core/reactor.ts","../src/core/reactor-builder.ts","../src/projection/hybrid-projection-coordinator.ts","../src/projection/create-hybrid-projection-coordinator.ts","../src/signer/passthrough-signer.ts","../src/core/reactor-client-builder.ts","../src/shared/drive-url.ts","../src/decision/sync-scope-gate.ts","../src/decision/stream-order.ts","../src/read-models/interfaces.ts","../src/processors/host-module.ts","../src/admin/passthrough-keyframe-store.ts","../src/admin/document-integrity-service.ts"],"sourcesContent":["import type {\n Action,\n AddRelationshipActionInput,\n CreateDocumentActionInput,\n DeleteDocumentActionInput,\n RemoveRelationshipActionInput,\n UpdateRelationshipActionInput,\n UpgradeDocumentActionInput,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actions as documentActions,\n generateId,\n} from \"@powerhousedao/shared/document-model\";\n\nexport { documentActions };\n\n/**\n * Creates a CREATE_DOCUMENT action for document creation.\n */\nexport function createDocumentAction(input: CreateDocumentActionInput): Action {\n return {\n id: generateId(),\n type: \"CREATE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an UPGRADE_DOCUMENT action to set initial document state.\n */\nexport function upgradeDocumentAction(\n input: UpgradeDocumentActionInput,\n): Action {\n return {\n id: generateId(),\n type: \"UPGRADE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a DELETE_DOCUMENT action for document deletion.\n */\nexport function deleteDocumentAction(documentId: string): Action {\n const input: DeleteDocumentActionInput = {\n documentId,\n };\n\n return {\n id: generateId(),\n type: \"DELETE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an ADD_RELATIONSHIP action that records a directed edge from\n * `sourceId` to `targetId` with an arbitrary `relationshipType` and optional\n * `metadata`. The edge is opaque to the reactor — consumers (e.g. reactor-drive)\n * define their own type strings such as `\"drive/child\"` and attach\n * domain-specific metadata.\n */\nexport function addRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n): Action {\n const input: AddRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n ...(metadata !== undefined ? { metadata } : {}),\n };\n\n return {\n id: generateId(),\n type: \"ADD_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an UPDATE_RELATIONSHIP action to replace a relationship's metadata\n * without losing its createdAt ordering.\n */\nexport function updateRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n): Action {\n const input: UpdateRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n metadata,\n };\n\n return {\n id: generateId(),\n type: \"UPDATE_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a REMOVE_RELATIONSHIP action to remove a parent-child relationship.\n */\nexport function removeRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string = \"child\",\n): Action {\n const input: RemoveRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n };\n\n return {\n id: generateId(),\n type: \"REMOVE_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n","import type {\n Action,\n ISigner,\n Operation,\n PHDocument,\n Signature,\n} from \"@powerhousedao/shared/document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { ErrorInfo, JobMeta, PagedResults } from \"../shared/types.js\";\n\n/**\n * Represents a minimal job plan for validation purposes\n */\nexport type JobPlanForValidation = {\n key: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Represents a minimal load job plan for validation purposes\n */\nexport type LoadJobPlanForValidation = {\n key: string;\n operations: Operation[];\n dependsOn: string[];\n};\n\n/**\n * Represents a job plan with scope information for action validation\n */\nexport type JobPlanWithScope = {\n key: string;\n scope: string;\n actions: Action[];\n};\n\n/**\n * Represents a load job plan with scope information for operation validation\n */\nexport type LoadJobPlanWithScope = {\n key: string;\n scope: string;\n operations: Operation[];\n};\n\n/**\n * Represents a job plan with dependencies for topological sorting\n */\nexport type JobPlanForSorting = {\n key: string;\n dependsOn: string[];\n};\n\n/**\n * Validates structural properties shared by all batch requests:\n * duplicate keys, missing dependencies, and dependency cycles.\n */\nexport function validateBatchStructure(jobs: JobPlanForSorting[]): void {\n const keys = new Set<string>();\n for (const job of jobs) {\n if (keys.has(job.key)) {\n throw new Error(`Duplicate plan key: ${job.key}`);\n }\n keys.add(job.key);\n }\n for (const job of jobs) {\n for (const depKey of job.dependsOn) {\n if (!keys.has(depKey)) {\n throw new Error(\n `Job '${job.key}' depends on non-existent key: ${depKey}`,\n );\n }\n }\n }\n const visited = new Set<string>();\n const recStack = new Set<string>();\n const detectCycle = (key: string): boolean => {\n visited.add(key);\n recStack.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n if (!visited.has(depKey)) {\n if (detectCycle(depKey)) {\n return true;\n }\n } else if (recStack.has(depKey)) {\n return true;\n }\n }\n }\n recStack.delete(key);\n return false;\n };\n for (const job of jobs) {\n if (!visited.has(job.key)) {\n if (detectCycle(job.key)) {\n throw new Error(`Dependency cycle detected involving key: ${job.key}`);\n }\n }\n }\n}\n\n/**\n * Validates a batch mutation request for common errors\n */\nexport function validateBatchRequest(jobs: JobPlanForValidation[]): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.actions.length === 0) {\n throw new Error(`Job '${job.key}' has empty actions array`);\n }\n }\n}\n\n/**\n * Validates a batch load request for common errors\n */\nexport function validateBatchLoadRequest(\n jobs: LoadJobPlanForValidation[],\n): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.operations.length === 0) {\n throw new Error(`Job '${job.key}' has empty operations array`);\n }\n }\n}\n\n/**\n * Validates that all actions in a job match the declared scope\n */\nexport function validateActionScopes(job: JobPlanWithScope): void {\n for (const action of job.actions) {\n const actionScope = action.scope || \"global\";\n if (actionScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but action has scope '${actionScope}'`,\n );\n }\n }\n}\n\n/**\n * Validates that all operations in a job match the declared scope\n */\nexport function validateOperationScopes(job: LoadJobPlanWithScope): void {\n for (const operation of job.operations) {\n const operationScope = operation.action.scope || \"global\";\n if (operationScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but operation has scope '${operationScope}'`,\n );\n }\n }\n}\n\n/**\n * Performs topological sort on jobs based on dependencies\n */\nexport function topologicalSort(jobs: JobPlanForSorting[]): string[] {\n const result: string[] = [];\n const visited = new Set<string>();\n const visit = (key: string): void => {\n if (visited.has(key)) {\n return;\n }\n visited.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n visit(depKey);\n }\n }\n result.push(key);\n };\n for (const job of jobs) {\n visit(job.key);\n }\n return result;\n}\n\n/**\n * Converts an Error or string to ErrorInfo\n */\nexport function toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n}\n\n/**\n * Filters paged results by document type\n */\nexport function filterByType(\n results: PagedResults<PHDocument>,\n type: string,\n): PagedResults<PHDocument> {\n // Filter documents by their document type from the header\n const filteredDocuments = results.results.filter(\n (document) => document.header.documentType === type,\n );\n\n // Create new paged results with filtered documents\n // Note: This maintains the same paging structure but with filtered results\n return {\n results: filteredDocuments,\n options: results.options,\n nextCursor: results.nextCursor,\n next: results.next\n ? async () => {\n // If there's a next function, apply the same filter to the next page\n const nextResults = await results.next!();\n return filterByType(nextResults, type);\n }\n : undefined,\n };\n}\n\n/**\n * Validates that all operations share the same scope.\n * Throws an error if any operation has a different scope.\n */\nexport function getSharedOperationScope(operations: Operation[]): string {\n if (operations.length === 0) {\n throw new Error(\"No operations provided\");\n }\n\n const baseScope = operations[0].action.scope;\n for (const [index, operation] of operations.entries()) {\n const scope = operation.action.scope;\n if (scope !== baseScope) {\n throw new Error(\n `All operations in load must share the same scope. Expected '${baseScope}', received '${scope}' at position ${index}`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Validates that all actions share the same scope.\n * Throws an error if any action has a different scope.\n */\nexport function getSharedActionScope(actions: Action[]): string {\n if (actions.length === 0) {\n throw new Error(\"No actions provided\");\n }\n\n const baseScope = actions[0].scope;\n for (const action of actions) {\n if (action.scope !== baseScope) {\n throw new Error(\n `All actions must share the same scope. Expected '${baseScope}', received '${action.scope}'`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Signs an action with the provided signer.\n * If the action already has valid signatures, it is returned unchanged.\n */\nexport const signAction = async (\n action: Action,\n signer: ISigner,\n signal?: AbortSignal,\n): Promise<Action> => {\n const existingSignatures = action.context?.signer?.signatures;\n if (existingSignatures && existingSignatures.length > 0) {\n return action;\n }\n\n const signature: Signature = await signer.signAction(action, signal);\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n user: {\n address: signer.user?.address || \"\",\n networkId: signer.user?.networkId || \"\",\n chainId: signer.user?.chainId || 0,\n },\n app: {\n name: signer.app?.name || \"\",\n key: signer.app?.key || \"\",\n },\n signatures: [signature],\n },\n },\n };\n};\n\n/**\n * Signs multiple actions with the provided signer\n */\nexport const signActions = async (\n actions: Action[],\n signer: ISigner,\n signal?: AbortSignal,\n): Promise<Action[]> => {\n return Promise.all(\n actions.map((action) => signAction(action, signer, signal)),\n );\n};\n\nexport function buildSingleJobMeta(\n jobId: string,\n callerMeta?: Record<string, unknown>,\n): JobMeta {\n return { ...callerMeta, batchId: uuidv4(), batchJobIds: [jobId] };\n}\n","import type { AuthSubject } from \"@powerhousedao/shared/document-model\";\n\n/**\n * The document ID used for system operations (CREATE_DOCUMENT, DELETE_DOCUMENT, etc.)\n * System operations use this special ID along with the \"system\" scope.\n */\nexport const SYSTEM_DOCUMENT_ID = \"00000000-0000-0000-0000-000000000000\";\n\n/**\n * Information about an error including its name, message and stack trace.\n *\n * The name is what survives the crossing out of the executor: a consumer that\n * has to tell a terminal failure from a retryable one, or classify a dead\n * letter, only ever sees this record rather than the thrown error.\n */\nexport type ErrorInfo = {\n name: string;\n message: string;\n stack: string;\n};\n\n/**\n * Describes the status of a shutdown operation.\n */\nexport type ShutdownStatus = {\n /**\n * True if and only if the system has been shutdown.\n *\n * This value is meant to be polled to determine if the system has been shutdown.\n *\n * In the case of a browser process, the `kill` method should be able to synchronously set this to true.\n *\n * In the case of a server process, a graceful shutdown period should be allowed for the system to finish its work.\n */\n get isShutdown(): boolean;\n\n /**\n * A promise that resolves when the shutdown process is complete.\n *\n * For server environments, await this promise to ensure all active jobs finish\n * before exiting the process.\n */\n completed: Promise<void>;\n};\n\n/**\n * Enum that determines deletion propagation.\n */\nexport enum PropagationMode {\n None = \"none\",\n Cascade = \"cascade\",\n}\n\n/**\n * Enum that describes the type of relationship change.\n */\nexport enum RelationshipChangeType {\n Added = \"added\",\n Removed = \"removed\",\n}\n\n/**\n * Batch-specific metadata always present on every job.\n * Single jobs get a unique batchId and batchJobIds of [jobId].\n */\nexport type BatchMeta = {\n batchId: string;\n batchJobIds: string[];\n};\n\n/**\n * Metadata that flows through the job lifecycle.\n * Always includes batch fields; callers may add additional properties.\n */\nexport type JobMeta = BatchMeta & Record<string, unknown>;\n\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * Describes the current state of a job.\n */\nexport type JobInfo = {\n id: string;\n\n /** The document this job operates on; empty string when the job is unknown. */\n documentId: string;\n status: JobStatus;\n createdAtUtcIso: string;\n completedAtUtcIso?: string;\n error?: ErrorInfo;\n errorHistory?: ErrorInfo[];\n result?: any;\n\n /**\n * A token for coordinating reads, only valid once a job reaches COMPLETED.\n */\n consistencyToken: ConsistencyToken;\n\n /**\n * Metadata that flows through the job lifecycle.\n */\n meta: JobMeta;\n\n /**\n * The full job object, populated on failure for debugging purposes.\n */\n job?: Job;\n};\n\n/**\n * Job execution statuses\n */\nexport enum JobStatus {\n /** Job is queued but not yet started */\n PENDING = \"PENDING\",\n /** Job is currently being executed */\n RUNNING = \"RUNNING\",\n /** Operations have been written to the operation store (JOB_WRITE_READY event) */\n WRITE_READY = \"WRITE_READY\",\n /** Read models have finished indexing operations (JOB_READ_READY event) */\n READ_READY = \"READ_READY\",\n /** Job failed (may be retried) */\n FAILED = \"FAILED\",\n}\n\n/**\n * Describe the view of a set of documents. That is, what pieces of the\n * documents are populated.\n */\nexport type ViewFilter = {\n branch?: string;\n scopes?: string[];\n revision?: number;\n /**\n * Read subject for the IReactorClient read gate; defaults to the client's\n * signer. Set per request when serving many principals. Ignored by IReactor.\n */\n subject?: AuthSubject;\n};\n\n/**\n * Describes filter options for searching documents.\n *\n * Each parameter is treated as an AND condition.\n */\nexport type SearchFilter = {\n type?: string;\n parentId?: string;\n ids?: string[];\n slugs?: string[];\n};\n\n/**\n * Describes the options for paging.\n */\nexport type PagingOptions = {\n cursor: string;\n limit: number;\n};\n\n/**\n * The paged result.\n */\nexport type PagedResults<T> = {\n results: T[];\n options: PagingOptions;\n\n next?: () => Promise<PagedResults<T>>;\n nextCursor?: string;\n totalCount?: number;\n};\n\n/**\n * A string key in the format `documentId:scope:branch` used to identify a consistency checkpoint.\n */\nexport type ConsistencyKey = `${string}:${string}:${string}`;\n\n/**\n * Describes a specific point in a document's operation history.\n */\nexport type ConsistencyCoordinate = {\n documentId: string;\n scope: string;\n branch: string;\n operationIndex: number;\n};\n\n/**\n * A token that captures the state of write operations at a point in time.\n * Can be used to ensure read-after-write consistency.\n */\nexport type ConsistencyToken = {\n version: 1;\n createdAtUtcIso: string;\n coordinates: ConsistencyCoordinate[];\n};\n","import {\n addFolder as addFolderAction,\n copyNode as copyNodeAction,\n deleteNode as deleteNodeAction,\n driveCreateDocument,\n generateNodesCopy,\n getDescendants,\n handleTargetNameCollisions,\n isFileNode,\n isFolderNode,\n moveNode as moveNodeAction,\n updateNode as updateNodeAction,\n type DocumentDriveDocument,\n type DriveInput,\n type FolderNode,\n type Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport { addFile as addFileAction } from \"@powerhousedao/shared/document-drive\";\nimport {\n actions,\n createPresignedHeader,\n generateId,\n normalizeDocumentModelVersion,\n type Action,\n type CreateDocumentActionInput,\n type ISigner,\n type PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type { ExecutionJobPlan, IReactor } from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport type { JobInfo, PagedResults, PagingOptions } from \"../shared/types.js\";\nimport { JobStatus } from \"../shared/types.js\";\nimport { parsePagingOptions } from \"../shared/utils.js\";\nimport type { IDriveClient, IReactorClient } from \"./types.js\";\n\n/**\n * Implementation of {@link IDriveClient}.\n *\n * Holds a back-reference to its parent {@link IReactorClient} for read and\n * single-document write primitives, plus direct access to {@link IReactor}\n * for batch execution. The back-reference is captured but never invoked\n * during construction, so the partial-`this` hazard does not apply.\n */\nexport class DriveClient implements IDriveClient {\n constructor(\n private readonly client: IReactorClient,\n private readonly logger: ILogger,\n private readonly reactor: IReactor,\n private readonly signer: ISigner,\n ) {}\n\n async create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\"drives.create(@input)\", input);\n const driveDoc = driveCreateDocument({\n global: {\n name: input.global.name || \"\",\n icon: input.global.icon ?? null,\n nodes: [],\n },\n });\n if (input.preferredEditor) {\n driveDoc.header.meta = {\n ...driveDoc.header.meta,\n preferredEditor: input.preferredEditor,\n };\n }\n return this.client.create<DocumentDriveDocument>(\n driveDoc,\n undefined,\n signal,\n );\n }\n\n async addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"drives.addFile(@driveIdentifier, @document, @parentFolder)\",\n driveIdentifier,\n document.header.id,\n parentFolder,\n );\n\n const documentId = document.header.id;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const documentActions: Action[] = await signActions(\n [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n // imported files may predate the document scope entirely\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document\n ?.version,\n ),\n initialState: document.state,\n }),\n addRelationshipAction(driveIdentifier, documentId, \"child\"),\n ],\n this.signer,\n signal,\n );\n\n const driveActions: Action[] = await signActions(\n [\n addFileAction({\n id: documentId,\n name: document.header.name || documentId,\n documentType: document.header.documentType,\n parentFolder,\n }),\n ],\n this.signer,\n signal,\n );\n\n // Two batches, not one with a dependsOn edge. A batch's edges are ordering\n // only: a failed job still releases its dependents (see IQueue.failJob), so\n // batching these would add the file node on top of a create that never\n // landed, pointing the drive at whoever already owns that id. Waiting for\n // the document to exist costs a round trip and makes that unrepresentable.\n await this.runJobs(\n [\n {\n key: \"document\",\n documentId,\n scope: getSharedActionScope(documentActions),\n branch: \"main\",\n actions: documentActions,\n dependsOn: [],\n },\n ],\n signal,\n );\n\n await this.runJobs(\n [\n {\n key: \"drive\",\n documentId: driveIdentifier,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [],\n },\n ],\n signal,\n );\n\n // Through the client, not the reactor: this one holds the read gate, and a\n // document handed back from a write is a read like any other.\n return this.client.get<TDocument>(documentId, undefined, signal);\n }\n\n async addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode> {\n this.logger.verbose(\n \"drives.addFolder(@driveIdentifier, @name, @parentFolder)\",\n driveIdentifier,\n name,\n parentFolder,\n );\n const folderId = generateId();\n const updated = await this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [addFolderAction({ id: folderId, name, parentFolder })],\n signal,\n );\n // A denied action still completes its job, so a caller with no grant gets\n // the document back without the scope it tried to write.\n const node = (\n updated.state as Partial<typeof updated.state>\n ).global?.nodes.find((n) => n.id === folderId);\n if (!node || !isFolderNode(node)) {\n throw new Error(\"Folder creation failed\");\n }\n return node;\n }\n\n async removeNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"drives.removeNode(@driveIdentifier, @nodeId)\",\n driveIdentifier,\n nodeId,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const node = drive.state.global.nodes.find((n) => n.id === nodeId);\n\n if (!node) {\n // No FileNode, but the document may still exist as an orphan (relationship\n // and document persisted, node never added — e.g. ADD_FILE rejected the\n // name). Clean it up if so; otherwise it is genuinely unknown.\n const exists = await this.documentExists(nodeId, signal);\n if (!exists) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n await this.removeFileNode(driveIdentifier, nodeId, signal);\n return;\n }\n\n if (isFolderNode(node)) {\n const fileDescendants = getDescendants(\n node,\n drive.state.global.nodes,\n ).filter(isFileNode);\n for (const file of fileDescendants) {\n await this.removeFileNode(driveIdentifier, file.id, signal);\n }\n await this.client.execute(\n driveIdentifier,\n \"main\",\n [deleteNodeAction({ id: nodeId })],\n signal,\n );\n return;\n }\n\n await this.removeFileNode(driveIdentifier, nodeId, signal);\n }\n\n async renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n this.logger.verbose(\n \"drives.renameNode(@driveIdentifier, @nodeId, @name)\",\n driveIdentifier,\n nodeId,\n name,\n );\n const renamed = await this.client.execute(\n nodeId,\n \"main\",\n [actions.setName({ name })],\n signal,\n );\n if (renamed.header.name !== name) {\n throw new Error(\"Document rename did not apply\");\n }\n const drive = await this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [updateNodeAction({ id: nodeId, name })],\n signal,\n );\n const node = (\n drive.state as Partial<typeof drive.state>\n ).global?.nodes.find((n) => n.id === nodeId);\n if (!node) {\n throw new Error(\"Node missing from drive after rename\");\n }\n return node;\n }\n\n async setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"drives.setPreferredEditorOnNode(@nodeId, @preferredEditor)\",\n nodeId,\n preferredEditor,\n );\n return this.client.setPreferredEditor(\n nodeId,\n preferredEditor,\n \"main\",\n signal,\n );\n }\n\n async moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\n \"drives.moveNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)\",\n driveIdentifier,\n srcNodeId,\n targetParentFolderId,\n );\n return this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [\n moveNodeAction({\n srcFolder: srcNodeId,\n targetParentFolder: targetParentFolderId,\n }),\n ],\n signal,\n );\n }\n\n async copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\n \"drives.copyNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)\",\n driveIdentifier,\n srcNodeId,\n targetParentFolderId,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const srcNode = drive.state.global.nodes.find((n) => n.id === srcNodeId);\n if (!srcNode) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n const copyPlan = generateNodesCopy(\n {\n srcId: srcNodeId,\n targetParentFolder: targetParentFolderId,\n targetName: srcNode.name,\n },\n () => generateId(),\n drive.state.global.nodes,\n );\n\n const resolvedNamesByTargetId = new Map<string, string>();\n for (const entry of copyPlan) {\n const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);\n if (!node) continue;\n const resolved = handleTargetNameCollisions({\n nodes: drive.state.global.nodes,\n srcName: entry.targetName || node.name,\n srcKind: isFileNode(node) ? \"file\" : \"folder\",\n targetParentFolder: entry.targetParentFolder ?? null,\n });\n resolvedNamesByTargetId.set(entry.targetId, resolved);\n }\n\n for (const entry of copyPlan) {\n const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);\n if (!node || !isFileNode(node)) continue;\n const srcDoc = await this.client.get(entry.srcId, undefined, signal);\n\n // Read snapshots carry no operations, so the copy is seeded from the\n // source's current state rather than replayed. Replaying here would also\n // pick a reducer by document type alone and apply it to state that is\n // already current.\n const duplicated: PHDocument = {\n ...srcDoc,\n header: createPresignedHeader(\n entry.targetId,\n srcDoc.header.documentType,\n ),\n initialState: srcDoc.state,\n operations: {},\n };\n\n const resolvedName = resolvedNamesByTargetId.get(entry.targetId);\n if (resolvedName) {\n duplicated.header.name = resolvedName;\n }\n await this.addFile(\n driveIdentifier,\n duplicated,\n entry.targetParentFolder ?? undefined,\n signal,\n );\n }\n\n return this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n copyPlan.map((entry) => copyNodeAction(entry)),\n signal,\n );\n }\n\n async getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n this.logger.verbose(\n \"drives.getNode(@driveIdentifier, @nodeId)\",\n driveIdentifier,\n nodeId,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const node = drive.state.global.nodes.find((n) => n.id === nodeId);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n return node;\n }\n\n async listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>> {\n this.logger.verbose(\n \"drives.listNodes(@driveIdentifier, @parentFolder, @paging)\",\n driveIdentifier,\n parentFolder,\n paging,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const allNodes = drive.state.global.nodes;\n const filtered =\n parentFolder === undefined\n ? [...allNodes]\n : allNodes.filter((n) => (n.parentFolder ?? null) === parentFolder);\n\n const { offset: startIndex, limit } = parsePagingOptions(\n paging,\n filtered.length,\n );\n const effective: PagingOptions = paging ?? { cursor: \"\", limit };\n const endIndex = startIndex + limit;\n const slice = filtered.slice(startIndex, endIndex);\n const hasMore = endIndex < filtered.length;\n\n return {\n results: slice,\n options: effective,\n ...(hasMore ? { nextCursor: String(endIndex) } : {}),\n totalCount: filtered.length,\n };\n }\n\n /**\n * Runs one batch to completion, throwing the first failure. The thrown error\n * carries the job error's name, which is what callers classify on: an id\n * collision has to be told apart from a transient failure.\n */\n private async runJobs(\n jobs: ExecutionJobPlan[],\n signal?: AbortSignal,\n ): Promise<JobInfo[]> {\n const batchResult = await this.reactor.executeBatch({ jobs }, signal);\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.client.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw this.toError(job);\n }\n }\n\n return completedJobs;\n }\n\n private toError(job: JobInfo): Error {\n const error = new Error(job.error?.message ?? \"Job failed\");\n if (job.error?.name) {\n error.name = job.error.name;\n }\n return error;\n }\n\n private async documentExists(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n try {\n await this.client.get(documentId, undefined, signal);\n return true;\n } catch {\n return false;\n }\n }\n\n private async removeFileNode(\n driveId: string,\n fileId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const relationshipActions: Action[] = await signActions(\n [removeRelationshipAction(driveId, fileId, \"child\")],\n this.signer,\n signal,\n );\n const driveActions: Action[] = await signActions(\n [deleteNodeAction({ id: fileId })],\n this.signer,\n signal,\n );\n\n const batchResult = await this.reactor.executeBatch(\n {\n jobs: [\n {\n key: \"relationship\",\n documentId: driveId,\n scope: getSharedActionScope(relationshipActions),\n branch: \"main\",\n actions: relationshipActions,\n dependsOn: [],\n },\n {\n key: \"drive\",\n documentId: driveId,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [\"relationship\"],\n },\n ],\n },\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.client.waitForJob(job, signal),\n ),\n );\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n const deleteJob = await this.reactor.deleteDocument(\n fileId,\n this.signer,\n signal,\n );\n const deleteCompleted = await this.client.waitForJob(deleteJob, signal);\n if (deleteCompleted.status === JobStatus.FAILED) {\n throw new Error(deleteCompleted.error?.message);\n }\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport { JobStatus, type JobInfo } from \"./types.js\";\n\nexport interface IJobAwaiter {\n /**\n * Waits for a job to complete: turns a job into a promise.\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Shuts down the job awaiter. This will synchronously reject all pending jobs.\n */\n shutdown(): void;\n}\n\ntype JobWaiter = {\n resolve: (value: JobInfo) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Checks if a job status is terminal (job has finished).\n * WRITE_READY is not terminal - it's an intermediate state.\n * Only READ_READY and FAILED are truly terminal.\n */\nfunction isTerminalStatus(status: JobStatus): boolean {\n return status === JobStatus.READ_READY || status === JobStatus.FAILED;\n}\n\n/**\n * Event-driven implementation of IJobAwaiter.\n * Subscribes to operation events to detect job completion without polling.\n */\nexport class JobAwaiter implements IJobAwaiter {\n private pendingJobs = new Map<string, JobWaiter[]>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(\n private eventBus: IEventBus,\n private getJobStatus: (\n jobId: string,\n signal?: AbortSignal,\n ) => Promise<JobInfo>,\n ) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event: JobWriteReadyEvent) => {\n await this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n async (_type, event: JobReadReadyEvent) => {\n await this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event: JobFailedEvent) => {\n await this.handleJobFailed(event);\n },\n ),\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n\n for (const [, waiters] of this.pendingJobs) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"JobAwaiter destroyed\"));\n }\n }\n this.pendingJobs.clear();\n }\n\n async waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const currentStatus = await this.getJobStatus(jobId, signal);\n if (isTerminalStatus(currentStatus.status)) {\n return currentStatus;\n }\n\n const promise = new Promise<JobInfo>((resolve, reject) => {\n const waiter: JobWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingJobs.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingJobs.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingJobs.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingJobs.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n\n return promise;\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleReadReady(event: JobReadReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleJobFailed(event: JobFailedEvent): Promise<void> {\n await this.checkAndResolveWaiters(event.jobId);\n }\n\n private async checkAndResolveWaiters(jobId: string): Promise<void> {\n const waiters = this.pendingJobs.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n try {\n const activeWaiters = waiters.filter((w) => !w.signal?.aborted);\n\n if (activeWaiters.length === 0) {\n this.pendingJobs.delete(jobId);\n return;\n }\n\n const jobInfo = await this.getJobStatus(jobId, activeWaiters[0].signal);\n\n if (isTerminalStatus(jobInfo.status)) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of activeWaiters) {\n waiter.resolve(jobInfo);\n }\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n }\n } catch (error) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of waiters) {\n waiter.reject(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }\n}\n","const COMPOSITE_PREFIX = \"c:\";\n\n/**\n * Returns true if the cursor is a composite multi-scope cursor.\n */\nexport function isCompositeCursor(cursor: string): boolean {\n return cursor.startsWith(COMPOSITE_PREFIX);\n}\n\n/**\n * Encodes per-scope cursors into a single composite cursor string.\n * Only scopes that still have more results should be included.\n */\nexport function encodeCompositeCursor(\n scopeCursors: Record<string, string>,\n): string {\n return COMPOSITE_PREFIX + JSON.stringify(scopeCursors);\n}\n\n/**\n * Decodes a composite cursor string into a map of scope to cursor.\n * Throws if the cursor is not a valid composite cursor.\n */\nexport function decodeCompositeCursor(cursor: string): Record<string, string> {\n if (!cursor.startsWith(COMPOSITE_PREFIX)) {\n throw new Error(\"Invalid composite cursor format\");\n }\n\n const json = cursor.slice(COMPOSITE_PREFIX.length);\n\n try {\n const parsed: unknown = JSON.parse(json);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n Array.isArray(parsed)\n ) {\n throw new Error(\"Invalid composite cursor format\");\n }\n return parsed as Record<string, string>;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\"Invalid composite cursor format\", { cause: error });\n }\n throw error;\n }\n}\n","import type {\n DocumentDriveDocument,\n DriveInput,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport type {\n Action,\n AuthSubject,\n DocumentModelModule,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\n\nimport type {\n BatchExecutionRequest,\n BatchExecutionResult,\n BatchLoadRequest,\n BatchLoadResult,\n} from \"../core/types.js\";\nimport type { Evaluation } from \"../decision/types.js\";\nimport type {\n JobInfo,\n PagedResults,\n PagingOptions,\n PropagationMode,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type { OperationFilter } from \"../storage/interfaces.js\";\n\n/**\n * Describes the types of document changes that can occur.\n */\nexport enum DocumentChangeType {\n Created = \"created\",\n Deleted = \"deleted\",\n Updated = \"updated\",\n ParentAdded = \"parent_added\",\n ParentRemoved = \"parent_removed\",\n ChildAdded = \"child_added\",\n ChildRemoved = \"child_removed\",\n}\n\n/**\n * Represents a change event for documents.\n */\nexport type DocumentChangeEvent = {\n type: DocumentChangeType;\n documents: PHDocument[];\n context?: {\n parentId?: string;\n childId?: string;\n };\n};\n\n/**\n * Options for creating an empty document.\n */\nexport type CreateDocumentOptions = {\n /** Optional \"id\" or \"slug\" of parent document */\n parentIdentifier?: string;\n /** Optional version of the document model to use (defaults to latest) */\n documentModelVersion?: number;\n};\n\n/** Retries taken when an upgrade conflicts with concurrent edits. */\nexport const DEFAULT_UPGRADE_CONFLICT_RETRIES = 3;\n\n/**\n * Options for upgrading a document.\n */\nexport type UpgradeDocumentOptions = {\n /**\n * How many times to retry with a fresh read when the executor rejects the\n * upgrade because the document changed after it was read. Defaults to\n * {@link DEFAULT_UPGRADE_CONFLICT_RETRIES}.\n */\n maxConflictRetries?: number;\n};\n\n/**\n * Drive-aware operations grouped under `client.drives`.\n *\n * These methods orchestrate the multi-action, multi-document choreography\n * required to keep a drive's `state.global.nodes` array consistent with the\n * relationship index and the underlying documents. Use the flat\n * `IReactorClient` primitives (`get`, `execute`, `find`) for everything that\n * is not drive-aware.\n */\nexport interface IDriveClient {\n /**\n * Creates a new drive document and waits for completion.\n */\n create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Adds a document to a drive as a single batched operation.\n *\n * Issues CREATE_DOCUMENT, UPGRADE_DOCUMENT, ADD_RELATIONSHIP on the new\n * document and ADD_FILE on the drive in a single dependent batch.\n */\n addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Adds a folder node to a drive.\n */\n addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode>;\n\n /**\n * Removes a node from a drive. Folder nodes cascade: descendant file\n * documents are deleted first, then the folder node entry itself.\n */\n removeNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Renames a node. Updates both the underlying document header and the\n * drive's node entry.\n */\n renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node>;\n\n /**\n * Updates the preferred editor recorded in the document header meta for\n * a node. Pass `null` to clear it.\n */\n setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Moves a node to a different parent folder within the same drive.\n * Pass `undefined` to move the node to the drive root.\n */\n moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Copies a node (and its subtree, if it is a folder) within a drive.\n * Each copied file gets a new id and a duplicated document.\n */\n copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Returns a single node from the drive's `state.global.nodes` array.\n */\n getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node>;\n\n /**\n * Returns nodes in the drive, optionally filtered by parent folder:\n * - omit `parentFolder` (or pass `undefined`) to list every node in the drive.\n * - pass `null` to list only root-level nodes.\n * - pass a folder id to list only the direct children of that folder.\n *\n * Returns a paged result so callers can stream through drives with very\n * large node counts without materialising the whole list in memory.\n */\n listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>>;\n}\n\n/**\n * One operation an authorization preflight predicts a verdict for. The input is\n * what a conditional grant reads, so a candidate standing for a filled-in form\n * carries that form's input.\n */\nexport type ActionCandidate = {\n scope: string;\n type: string;\n input?: unknown;\n};\n\n/**\n * The predicted verdicts for a set of candidates, in the order they were given,\n * with the aggregates a UI branches on.\n *\n * The aggregates are redundant -- a verdict is binary, so `allDenied` is\n * `!anyAllowed` and `anyDenied` is `!allAllowed` -- and all four are returned\n * so that a caller reads the one its question is phrased in rather than\n * negating another. Over no candidates every aggregate is false: nothing is\n * allowed and nothing is denied.\n */\nexport type ActionEvaluations = {\n evaluations: Evaluation[];\n allAllowed: boolean;\n anyAllowed: boolean;\n allDenied: boolean;\n anyDenied: boolean;\n};\n\n/**\n * The ReactorClient interface that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport interface IReactorClient {\n /**\n * Drive-aware operations. See {@link IDriveClient}.\n */\n readonly drives: IDriveClient;\n\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>>;\n\n /**\n * Retrieves a specific document by identifier (either id or slug).\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Resolves an identifier (either an id or a slug) to the canonical document\n * id, using the same lookup as the read/operation data paths. Resolves\n * against the \"main\" branch. Throws if the identifier cannot be resolved or\n * is ambiguous.\n *\n * @param identifier - Required, this is the document id or slug\n * @param signal - Optional abort signal to cancel the request\n * @returns The canonical document id\n */\n resolveIdOrSlug(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<string>;\n\n /** True when the id is taken; a soft-deleted document's id is still taken. */\n isDocumentIdTaken(documentId: string, signal?: AbortSignal): Promise<boolean>;\n\n /**\n * Retrieves operations for a document.\n *\n * @param documentIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns Paginated list of operations\n */\n getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>>;\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n *\n * @param sourceIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - The relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The target documents and paging cursor\n */\n getOutgoingRelationships(\n sourceIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n *\n * @param targetIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - The relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The source documents and paging cursor\n */\n getIncomingRelationships(\n targetIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Filters documents by criteria and returns a list of them\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Predicts whether the subject would be admitted to execute each of a set of\n * candidate operations, without submitting any of them. A UI asks this to\n * disable a control rather than offer an action that fails on submit.\n *\n * The answer is a prediction, not a promise. Three caveats hold:\n *\n * - Real admission compiles an append condition over everything it read and\n * the store enforces it at write time. A preflight reads no future, so a\n * policy change landing between this answer and the submit changes the\n * verdict. The submit path stays the only authority.\n * - The verdict is evaluated at the stream heads. It is therefore correct for\n * a candidate that will be stamped at or after every timestamp the\n * evaluation read, which is the normal case for a control the user is about\n * to click. A backdated submission is out of contract: the reactor decides\n * that one by position, against the policy as it stood there.\n * - A candidate whose input decides the verdict needs that input supplied.\n * With `authConditions` on, a conditional grant reads `action.input`, so\n * omitting the input predicts the denial an empty input would earn rather\n * than the verdict the filled-in form will get.\n *\n * Document-scope candidates are decided against the policy of the document\n * their input names, not the one passed here: delete and upgrade name it in\n * `input.documentId`, and the relationship actions in `input.sourceId`. This\n * follows the executor's own gate, which decides against the document\n * guarding the write. `CREATE_DOCUMENT` follows the gate's exemption: it runs\n * before its document exists, so the executor never decides it against a\n * policy and the preflight predicts allow.\n *\n * @param documentIdentifier - Document \"id\" or \"slug\" the candidates target\n * @param branch - Branch to evaluate against\n * @param candidates - Operations to predict a verdict for, each with the scope it would execute in\n * @param subject - Optional subject to decide for, defaulting to the client's own signer. A plain subject rather than a ViewFilter: the evaluation reads no view, so a filter's branch or scopes would be silently ignored here\n * @param signal - Optional abort signal to cancel the request\n * @returns One evaluation per candidate, in the order given, with the aggregates over them\n * @throws AuthEnforcementDisabledError if the reactor's authEnforcement flag is off, in which case it holds no decision model and the legacy host-table permission system cannot answer for one\n */\n evaluateActions(\n documentIdentifier: string,\n branch: string,\n candidates: ActionCandidate[],\n subject?: AuthSubject,\n signal?: AbortSignal,\n ): Promise<ActionEvaluations>;\n\n /**\n * Creates a document and waits for completion\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param parentIdentifier - Optional \"id\" or \"slug\" of parent document\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Creates an empty document and waits for completion\n *\n * @param documentModelType - Type of document to create\n * @param options - Optional creation options (parentIdentifier, documentModelVersion)\n * @param signal - Optional abort signal to cancel the request\n */\n createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves the document model module matching the version a document is\n * stamped with. Use this instead of {@link getDocumentModelModule}\n * whenever a specific document is in hand: the latest-wins lookup feeds\n * not-yet-upgraded documents the wrong reducer, diverging from replay.\n *\n * @param document - The document whose stamped version selects the module\n * @returns The document model module registered for that version\n * @throws UnsupportedDocumentModelVersionError if no module is registered for the stamped version\n */\n getDocumentModelModuleForDocument(\n document: PHDocument,\n ): Promise<DocumentModelModule<any>>;\n\n /**\n * Upgrades a document to a newer document model version by dispatching an\n * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the\n * latest registered module version for the document's type. Returns the\n * document unchanged when it is already at the target version.\n *\n * The action carries a snapshot of the document's version and per-scope\n * revisions, which the executor validates before persisting. When an edit\n * lands between the read and the upgrade executing, the upgrade is\n * rejected and retried with a fresh read up to\n * {@link UpgradeDocumentOptions.maxConflictRetries} times before the\n * conflict is surfaced.\n *\n * @param documentIdentifier - Target document id or slug\n * @param toVersion - Optional target document model version; defaults to latest\n * @param options - Optional upgrade options (maxConflictRetries)\n * @param signal - Optional abort signal to cancel the request\n * @returns The upgraded document\n * @throws DowngradeNotSupportedError if toVersion is less than the document's current version\n */\n upgradeDocument<TDocument extends PHDocument = PHDocument>(\n documentIdentifier: string,\n toVersion?: number,\n options?: UpgradeDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n * This is more efficient than createEmpty + addFile as it batches all\n * actions into dependent jobs and waits for them to complete together.\n *\n * @deprecated Use {@link IDriveClient.addFile} via `client.drives.addFile`\n * instead. This method will be removed in a future release.\n * @param driveId - The drive document id or slug\n * @param document - The document to create\n * @param parentFolder - Optional folder id within the drive\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Applies a list of actions to a document and waits for completion\n *\n * The document handed back is a read like any other, so it goes through the\n * read gate. `subject` names who that read is for, the same way\n * {@link ViewFilter.subject} does on `get` and `find`; a host that shares one\n * client across many callers has no meaningful signer to fall back to, and\n * without it the gate would strip the domain scopes of a policied document\n * and resolve with a husk whose `state.global` is absent — reading as a\n * rejected write when the write in fact succeeded.\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @param subject - Optional read subject for the returned document; defaults\n * to the client's signer\n * @returns The updated document\n */\n execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n subject?: AuthSubject,\n ): Promise<TDocument>;\n\n /**\n * Submits a list of actions to a document\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @returns The job\n */\n executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Applies multiple mutation jobs in dependency order and waits for all to\n * complete. Actions on each job are signed by the client signer before\n * dispatch. Throws on the first failed job; the others may still execute\n * because dispatch is fire-and-await-all.\n *\n * @param request - Batch mutation request with per-job actions and dependsOn keys\n * @param signal - Optional abort signal to cancel the request\n * @returns The completed batch result (job ids keyed by plan key)\n */\n executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Renames a document and waits for completion\n *\n * @param documentIdentifier - Target document id or slug\n * @param name - The new name of the document\n * @param branch - Optional branch to rename the document, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document.\n */\n rename(\n documentIdentifier: string,\n name: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Updates the preferred editor in the document header meta and waits for completion.\n *\n * @param documentIdentifier - Target document id or slug\n * @param preferredEditor - The new preferred editor, or `null` to clear it\n * @param branch - Optional branch, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document.\n */\n setPreferredEditor(\n documentIdentifier: string,\n preferredEditor: string | null,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Adds a relationship between two documents and waits for completion.\n *\n * @param sourceIdentifier - Source document id or slug\n * @param targetIdentifier - Target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param branch - Optional branch to add the relationship to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source document\n */\n addRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Removes a relationship between two documents and waits for completion.\n *\n * @param sourceIdentifier - Source document id or slug\n * @param targetIdentifier - Target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param branch - Optional branch to remove the relationship from, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source document\n */\n removeRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Moves a relationship from one source document to another and waits for completion.\n *\n * @param sourceParentIdentifier - Source parent document id or slug\n * @param targetParentIdentifier - Target parent document id or slug\n * @param targetIdentifier - The target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param branch - Optional branch to apply the move to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source and target documents\n */\n moveRelationship(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }>;\n\n /**\n * Deletes a document and waits for completion\n *\n * @param identifier - Document identifier (id or slug)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Deletes documents and waits for completion\n *\n * @param identifiers - Document identifiers (ids or slugs)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n * Waits for all jobs to complete.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @returns Map of job keys to completed job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @param signal - Optional abort signal to cancel the request\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Waits for a job to complete\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string | JobInfo, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Subscribes to changes for documents matching specified filters\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param callback - Function called when documents change with the change event details\n * @param view - Optional filter containing branch and scopes information\n * @returns A function that unsubscribes from the changes\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void;\n}\n","import type {\n AuthSubject,\n PHAuthState,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n decide,\n groupDocumentType,\n MAX_AUTH_GRANTS,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport {\n DocumentExistence,\n type IDocumentView,\n} from \"../storage/interfaces.js\";\nimport { buildDecisionModel } from \"./build-decision-model.js\";\nimport type { RegisteredDecisionModel } from \"./registered-model.js\";\nimport { selectDecisionModel } from \"./registered-model.js\";\nimport type { IStreamStateReader } from \"./types.js\";\n\n/**\n * Scopes every holder of a document may read, whatever the grants say. Denying\n * the policy itself would let a replica sync a document without it, read the\n * auth scope as uninitialized, and allow every operation it holds, so replicas\n * would diverge permanently. The document scope carries the metadata the same\n * argument covers. Grants gate domain-scope reads only.\n */\nexport const ALWAYS_READABLE_SCOPES: ReadonlySet<string> = new Set([\n \"auth\",\n \"document\",\n]);\n\n/**\n * How a gate treats a document nobody has written a policy onto.\n *\n * A host that closes by default owes an uninitialized document the same silence\n * it owes a denied one, because \"no policy yet\" and \"a policy that allows this\"\n * are indistinguishable to a replica that never receives the scope. The option\n * exists because that answer belongs to the host serving the read, not to the\n * document: replay must keep reading an uninitialized document in full, or a\n * replica would refuse to rebuild state it already holds.\n */\nexport type ReadGateOptions = {\n withholdUninitialized: boolean;\n};\n\n/** What a gate does with an uninitialized policy when told nothing. */\nconst OPEN_BY_DEFAULT: ReadGateOptions = { withholdUninitialized: false };\n\n/**\n * The branch a group is read on. A group's member list lives on its main\n * branch whatever branch the document naming it is on, and the reference\n * relation records no branch of its own.\n */\nconst GROUP_BRANCH = \"main\";\n\n/**\n * The scope of a group document that holds its member list. It is the only\n * scope group serving reaches, because it is the only one the audience must\n * fold in order to evaluate auth with the group.\n */\nconst GROUP_MEMBERSHIP_SCOPE = \"global\";\n\n/**\n * How many of a group's referencing documents are examined before a read of\n * that group gives up and withholds. Each one costs a model build, and the\n * relation is append-only, so an old and briefly-held reference still counts.\n * Truncating denies, which is the direction that withholds. It is the cap a\n * version-1 policy carries on its own grant list, taken from there rather than\n * restated: raising that cap raises the read fan-out here.\n */\nconst MAX_EXAMINED_REFERENCERS = MAX_AUTH_GRANTS;\n\n/**\n * How many referencers are probed at once. The reactor's Postgres pool size is\n * optional and defaults to ten, and a listing resolves one gate per result, so\n * per-gate fan-out multiplies: a handful keeps one group read from claiming the\n * whole pool while still cutting the walk several times over. On PGlite the\n * queries serialize regardless, so a larger number buys nothing in a browser.\n */\nconst REFERENCER_PROBE_CONCURRENCY = 4;\n\n/**\n * The shared cursor a set of referencer probes pulls from, and what they found.\n * One record rather than three variables, because a probe reads its siblings'\n * answers across an await.\n */\ntype ReferencerWalk = {\n next: number;\n served: boolean;\n failure: Error | undefined;\n};\n\n/**\n * The policy carried on a document, if it carries one. A document handed to the\n * gate with no state at all is not policied, so it is not gated.\n */\nfunction authOf(document: PHDocument): PHAuthState | undefined {\n const state = document.state as { auth?: PHAuthState } | undefined;\n return state?.auth;\n}\n\n/** Whether a subject may read each scope of one document. */\nexport interface IReadGate {\n /**\n * Resolves, for one document, which of its scopes the subject may read.\n *\n * The predicate is resolved up front rather than asked per scope so that the\n * filtering itself stays synchronous, and so that a model backing the answer\n * is built once per document instead of once per scope.\n */\n scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean>;\n}\n\n/**\n * The model reads enforce. Below `authEnforcement` there is no model to\n * enforce: the document-only model ignores the auth scope entirely, so reading\n * through it would serve every domain scope of a policied document to anyone.\n * Undefined therefore means \"evaluate the policy alone\", which is what the read\n * surface did before the model existed.\n */\nexport function readDecisionModel(\n flags: ReactorFeatureFlags,\n registry: IDocumentModelRegistry,\n): RegisteredDecisionModel | undefined {\n return flags.authEnforcement\n ? selectDecisionModel(flags, registry)\n : undefined;\n}\n\n/**\n * Evaluates the policy on its own, with no groups map and no condition context.\n * A `{ group }` or conditional grant therefore never applies: an allow that\n * does not apply withholds access, so this cannot widen a policy, but a policy\n * relying on a conditional deny is weaker here than it is written.\n */\nexport class BareReadGate implements IReadGate {\n scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n ): Promise<(scope: string) => boolean> {\n const auth = authOf(document);\n return Promise.resolve(\n (scope: string) =>\n ALWAYS_READABLE_SCOPES.has(scope) ||\n decide(auth, subject, { verb: \"read\", scope }) === \"allow\",\n );\n }\n}\n\n/**\n * Answers a stream read from the document already fetched, and anything else\n * through the read side.\n *\n * The seed is why routing reads through a decision model costs no extra I/O for\n * the document being read: its `document` and `auth` scopes are the two static\n * projections, and the caller has both in hand. Only a group stream the grant\n * list names is fetched.\n */\nexport class SeededStateReader implements IStreamStateReader {\n constructor(\n private readonly documentView: IDocumentView,\n private readonly seed: PHDocument,\n private readonly branch: string,\n ) {}\n\n /**\n * A stream this replica does not hold has to reach buildDecisionModel as the\n * absence it recognises, or the whole read fails instead of leaving the group\n * out of the model, where its principal does not match and the policy fails\n * closed. The read side reports absence as a plain Error, so the absence is\n * confirmed rather than inferred from the message: a transient failure must\n * surface, not silently deny.\n */\n async getState(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision?: number,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n if (\n targetRevision === undefined &&\n documentId === this.seed.header.id &&\n branch === this.branch &&\n scope in (this.seed.state as Record<string, unknown>)\n ) {\n return this.seed;\n }\n\n try {\n return await this.documentView.get(\n documentId,\n { branch, scopes: [scope] },\n undefined,\n signal,\n );\n } catch (error) {\n await this.assertAbsent(documentId, error, signal);\n throw new DocumentNotFoundError(documentId);\n }\n }\n\n private async assertAbsent(\n documentId: string,\n error: unknown,\n signal?: AbortSignal,\n ): Promise<void> {\n if (DocumentNotFoundError.isError(error)) {\n return;\n }\n\n let exists: boolean[];\n try {\n exists = await this.documentView.exists(\n [documentId],\n DocumentExistence.LiveOnly,\n undefined,\n signal,\n );\n } catch {\n throw error;\n }\n\n if (exists[0]) {\n throw error;\n }\n }\n}\n\n/**\n * Evaluates a read against the registered decision model, built at the stream\n * heads. This is what makes `{ group }` principals and conditional grants apply\n * to a read: the model supplies the groups map and the scope's own state, the\n * same two things admission supplies.\n *\n * A read has no action, so a condition on `action.input.*` never holds for one.\n *\n * State is read through the read side rather than the write cache. The write\n * cache is invalidated by whichever process runs the executor, so a reactor\n * running its executors in worker processes would answer reads in the parent\n * from state no commit ever invalidates.\n */\nexport class ModelReadGate implements IReadGate {\n constructor(\n private readonly model: RegisteredDecisionModel,\n private readonly documentView: IDocumentView,\n /**\n * Whether a group a policy names is served to that policy's audience. Only\n * meaningful with `authGroups`, which is what makes a `{ group }` grant\n * match at all; below it the grant fails closed, so serving the roster\n * would publish a member list no read grant can use.\n */\n private readonly servesGroups: boolean,\n private readonly operationIndex?: IOperationIndex,\n private readonly logger?: ILogger,\n private readonly options: ReadGateOptions = OPEN_BY_DEFAULT,\n ) {}\n\n /**\n * A served group yields its member list and nothing else. What the audience\n * is owed is the state it must fold to evaluate auth with the group; a\n * group's other scopes are its own business and stay behind its own grants.\n */\n async scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n const own = await this.ownPolicyPredicate(\n document,\n subject,\n branch,\n signal,\n );\n\n if (!this.servesGroup(document) || own(GROUP_MEMBERSHIP_SCOPE)) {\n return own;\n }\n\n const audience = await this.servesGroupTo(\n document.header.id,\n subject,\n signal,\n );\n\n return (scope: string) =>\n own(scope) || (audience && scope === GROUP_MEMBERSHIP_SCOPE);\n }\n\n /**\n * Whether the subject is served this group because a policy names it.\n *\n * A replica must fold a group's membership to evaluate auth with it, so a\n * group a grant names is served to the audience of the document that names\n * it, whatever the group's own read grants say. Naming a group in a policy\n * publishes its roster to that policy's audience; a group whose membership\n * must stay confidential does not belong in a grant.\n *\n * The referencing document's own domain scopes are the test. Its `auth` and\n * `document` scopes are readable by every holder, so testing those would\n * serve every referenced group to everybody.\n *\n * One level only. A referencer that is itself a group is skipped, and a\n * referencer's own readability is decided from its policy alone, so a\n * reference cycle terminates. Cycles are reachable: the reference relation\n * is recorded from an operation's input, including one later stored denied,\n * so a refused grant naming a group from inside another group leaves a row\n * behind that validation never saw.\n *\n * The referencers are probed a few at a time and the walk stops at the first\n * that serves, because a subject outside the audience is the case that runs to\n * the bound, and it is the common one. A probe that failed decides only when\n * nothing served: serving rests on a real allow, so this cannot widen, and it\n * stops one unreachable referencer from turning an allow already in hand into\n * a denial. A read records no operation, so replicas differing over a\n * transient failure has no consensus consequence.\n *\n * The probes are awaited together rather than raced, so none is ever left\n * running with nobody awaiting it, which is where unhandled rejections come\n * from.\n */\n private async servesGroupTo(\n groupId: string,\n subject: AuthSubject,\n signal?: AbortSignal,\n ): Promise<boolean> {\n if (!this.operationIndex) {\n return false;\n }\n\n const referencers = await this.operationIndex.getGroupReferencers(\n groupId,\n signal,\n );\n\n const examined = referencers.slice(0, MAX_EXAMINED_REFERENCERS);\n if (examined.length < referencers.length) {\n this.logger?.warn(\n `Group ${groupId} is referenced by ${referencers.length} documents; only the first ${MAX_EXAMINED_REFERENCERS} decide whether it is served`,\n );\n }\n\n const walk: ReferencerWalk = {\n next: 0,\n served: false,\n failure: undefined,\n };\n\n const probe = async (): Promise<void> => {\n while (!walk.served && walk.next < examined.length) {\n const referencerId = examined[walk.next++];\n\n // Held in a local rather than assigned straight to the walk: reading\n // that back after the await could overwrite another probe's answer.\n let serves = false;\n try {\n serves = await this.servesThrough(referencerId, subject, signal);\n } catch (error) {\n walk.failure ??= Error.isError(error)\n ? error\n : new Error(`Probing referencer ${referencerId} failed`);\n }\n\n if (serves) {\n walk.served = true;\n }\n }\n };\n\n await Promise.all(\n Array.from(\n { length: Math.min(REFERENCER_PROBE_CONCURRENCY, examined.length) },\n probe,\n ),\n );\n\n if (!walk.served && walk.failure) {\n throw walk.failure;\n }\n\n return walk.served;\n }\n\n /**\n * Whether one referencing document serves the subject any domain scope. A\n * referencer this replica does not hold serves nothing, which fails closed\n * the same way a group it does not hold does.\n */\n private async servesThrough(\n referencerId: string,\n subject: AuthSubject,\n signal?: AbortSignal,\n ): Promise<boolean> {\n let referencer: PHDocument;\n try {\n referencer = await this.documentView.get(\n referencerId,\n { branch: GROUP_BRANCH },\n undefined,\n signal,\n );\n } catch {\n return false;\n }\n\n if (this.servesGroup(referencer)) {\n return false;\n }\n\n const readable = await this.ownPolicyPredicate(\n referencer,\n subject,\n GROUP_BRANCH,\n signal,\n );\n\n return Object.keys((referencer.state ?? {}) as Record<string, unknown>)\n .filter((scope) => !ALWAYS_READABLE_SCOPES.has(scope))\n .some((scope) => readable(scope));\n }\n\n private servesGroup(document: PHDocument): boolean {\n return (\n this.servesGroups &&\n this.operationIndex !== undefined &&\n document.header.documentType === groupDocumentType\n );\n }\n\n /**\n * What this document's own policy says, with no group serving applied.\n *\n * An unpoliced document is readable in full unless the host closes by default,\n * and either way it is the common case and the one worth answering without\n * building anything. The test is the one `evaluate` makes: a legacy `{}` auth\n * scope and version 0 both mean uninitialized, and \"no grants\" does not,\n * because a policy with a version and an empty grant list denies everything.\n *\n * Closing here rather than around the gate is what keeps a policy able to\n * publish an unpoliced group it names: the referencer walk asks this question\n * of the referencing document, whose real policy answers it.\n */\n private async ownPolicyPredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n const auth = authOf(document);\n\n if (!auth || !auth.version) {\n return this.options.withholdUninitialized\n ? (scope: string) => ALWAYS_READABLE_SCOPES.has(scope)\n : () => true;\n }\n\n const target = { documentId: document.header.id, branch };\n const built = await buildDecisionModel(\n new SeededStateReader(this.documentView, document, branch),\n this.model,\n target,\n signal,\n );\n\n // The read-set the build recorded guards a write, and a read makes none.\n const definition = this.model(target);\n const scopeStates = (document.state ?? {}) as Record<string, unknown>;\n\n return (scope: string) =>\n ALWAYS_READABLE_SCOPES.has(scope) ||\n definition.decide(\n built.model,\n subject,\n { verb: \"read\", scope },\n { scopeState: scopeStates[scope], actionInput: undefined },\n ).decision === \"allow\";\n }\n}\n","import type {\n AuthSubject,\n ISigner,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ViewFilter } from \"../shared/types.js\";\n\nexport function authSubjectFromSigner(signer: ISigner): AuthSubject {\n return { address: signer.user?.address, key: signer.app?.key };\n}\n\n// Ensures a scoped read still fetches the auth scope, so the gate sees the policy.\nexport function withAuthScope(view?: ViewFilter): ViewFilter | undefined {\n if (view?.scopes && view.scopes.length > 0) {\n return { ...view, scopes: [...new Set([...view.scopes, \"auth\"])] };\n }\n return view;\n}\n\n/**\n * Drops the scopes the predicate refuses. The predicate is resolved by the read\n * gate, which decides against the whole policy once per document; this only\n * applies the answer, so it stays synchronous.\n */\nexport function filterReadableScopes<TDocument extends PHDocument>(\n document: TDocument,\n readable: (scope: string) => boolean,\n): TDocument {\n const state = document.state as Record<string, unknown> | undefined;\n if (!state) {\n return document;\n }\n\n // initialState carries the same scopes as state, so filtering one and\n // spreading the other hands back the contents just removed.\n return {\n ...document,\n state: keepReadableScopes(state, readable),\n initialState: keepReadableScopes(\n document.initialState as Record<string, unknown> | undefined,\n readable,\n ),\n } as TDocument;\n}\n\nfunction keepReadableScopes(\n scopes: Record<string, unknown> | undefined,\n readable: (scope: string) => boolean,\n): Record<string, unknown> | undefined {\n if (!scopes) {\n return scopes;\n }\n const kept: Record<string, unknown> = {};\n for (const scope of Object.keys(scopes)) {\n if (readable(scope)) {\n kept[scope] = scopes[scope];\n }\n }\n return kept;\n}\n","import type {\n Action,\n AuthSubject,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actions,\n DowngradeNotSupportedError,\n normalizeDocumentModelVersion,\n UnsupportedDocumentModelVersionError,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type {\n BatchExecutionRequest,\n BatchExecutionResult,\n BatchLoadRequest,\n BatchLoadResult,\n ExecutionJobPlan,\n IReactor,\n} from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport { type IJobAwaiter } from \"../shared/awaiter.js\";\nimport { AuthEnforcementDisabledError } from \"../shared/errors.js\";\nimport {\n JobStatus,\n PropagationMode,\n RelationshipChangeType,\n type JobInfo,\n type PagedResults,\n type PagingOptions,\n type SearchFilter,\n type ViewFilter,\n} from \"../shared/types.js\";\nimport { DocumentExistence } from \"../storage/interfaces.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport {\n decodeCompositeCursor,\n encodeCompositeCursor,\n isCompositeCursor,\n} from \"./cursor.js\";\nimport { DriveClient } from \"./drive-client.js\";\nimport {\n DEFAULT_UPGRADE_CONFLICT_RETRIES,\n DocumentChangeType,\n type ActionCandidate,\n type ActionEvaluations,\n type CreateDocumentOptions,\n type DocumentChangeEvent,\n type IDriveClient,\n type IReactorClient,\n type UpgradeDocumentOptions,\n} from \"./types.js\";\nimport { buildDecisionModel } from \"../decision/build-decision-model.js\";\nimport type { IReadGate } from \"../decision/read-gate.js\";\nimport { BareReadGate, SeededStateReader } from \"../decision/read-gate.js\";\nimport type { DocumentDecisionModel } from \"../decision/document-decision-model.js\";\nimport type { RegisteredDecisionModel } from \"../decision/registered-model.js\";\nimport type { DecisionModel, Evaluation } from \"../decision/types.js\";\nimport { GATED_DOCUMENT_ACTIONS, targetDocumentId } from \"../executor/util.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport {\n authSubjectFromSigner,\n filterReadableScopes,\n withAuthScope,\n} from \"./util.js\";\n\n/**\n * A decision model built for one target document, with what deciding a candidate\n * against it needs: the definition that decides, the built model it decides\n * over, and the scope states a condition reads.\n */\ntype EvaluationTarget = {\n definition: DecisionModel<DocumentDecisionModel>;\n model: DocumentDecisionModel;\n scopeStates: Record<string, unknown>;\n};\n\n/**\n * The document a candidate is decided against. Routed on the action type alone,\n * which is what the executor routes on: every action the reactor reduces onto\n * the document scope carries that scope already, and the gate follows the\n * action's own target rather than the document the request named.\n */\nfunction evaluationTargetId(\n candidate: ActionCandidate,\n fallback: string,\n): string {\n return GATED_DOCUMENT_ACTIONS.has(candidate.type)\n ? targetDocumentId(\n { type: candidate.type, input: candidate.input },\n fallback,\n )\n : fallback;\n}\n\n/**\n * One candidate's verdict, decided exactly as admission decides it: the same\n * request, and a condition context populated only while authConditions is on,\n * so a conditional grant applies here precisely when it would apply there.\n * Groups need nothing added, because the selected model carries them.\n */\nfunction decideCandidate(\n config: ActionEvaluationConfig,\n target: EvaluationTarget,\n subject: AuthSubject,\n candidate: ActionCandidate,\n): Evaluation {\n return target.definition.decide(\n target.model,\n subject,\n { verb: \"execute\", scope: candidate.scope, operation: candidate.type },\n config.flags.authConditions\n ? {\n scopeState: target.scopeStates[candidate.scope],\n actionInput: candidate.input,\n }\n : { scopeState: undefined, actionInput: undefined },\n );\n}\n\n/**\n * What {@link IReactorClient.evaluateActions} decides against: the decision\n * model this reactor enforces, and the flags that selected it.\n *\n * Both, because neither alone is enough. The flags say which of the model's\n * inputs a decision may read, and the model itself cannot be derived from them\n * here: selecting one needs the document model registry, which a client does not\n * hold. Absent, this client answers no preflight at all -- which is the whole of\n * the non-coexistence guarantee, since a client built without a reactor holding\n * a decision model has nothing to answer from.\n */\nexport type ActionEvaluationConfig = {\n model: RegisteredDecisionModel;\n flags: ReactorFeatureFlags;\n};\n\n/**\n * ReactorClient implementation that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport class ReactorClient implements IReactorClient {\n private logger: ILogger;\n private reactor: IReactor;\n private signer: ISigner;\n private subscriptionManager: IReactorSubscriptionManager;\n private jobAwaiter: IJobAwaiter;\n private documentIndexer: IDocumentIndexer;\n private documentView: IDocumentView;\n private readGate: IReadGate;\n private actionEvaluation: ActionEvaluationConfig | undefined;\n\n readonly drives: IDriveClient;\n\n constructor(\n logger: ILogger,\n reactor: IReactor,\n signer: ISigner,\n subscriptionManager: IReactorSubscriptionManager,\n jobAwaiter: IJobAwaiter,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n readGate: IReadGate = new BareReadGate(),\n actionEvaluation?: ActionEvaluationConfig,\n ) {\n this.logger = logger;\n this.reactor = reactor;\n this.signer = signer;\n this.subscriptionManager = subscriptionManager;\n this.jobAwaiter = jobAwaiter;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n this.readGate = readGate;\n this.actionEvaluation = actionEvaluation;\n this.drives = new DriveClient(this, logger, reactor, signer);\n this.logger.verbose(\"ReactorClient initialized\");\n }\n\n private readSubject(subject?: AuthSubject): AuthSubject {\n return subject ?? authSubjectFromSigner(this.signer);\n }\n\n /**\n * Which scopes of one document the subject may read. Resolved once per\n * document, so the gate builds its model once however many scopes are then\n * tested, and the filtering itself stays synchronous.\n */\n private readableScopes(\n document: PHDocument,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n return this.readGate.scopePredicate(\n document,\n this.readSubject(view?.subject),\n view?.branch ?? \"main\",\n signal,\n );\n }\n\n /**\n * One document, filtered to the scopes the subject may read. Every method\n * that hands a document back goes through here, including the ones that\n * follow a write: a document returned from a mutation is a read like any\n * other, and returning it whole served scopes the same subject would be\n * refused by `get`. Its author still sees what it wrote, because an allow on\n * execute confers read of that scope.\n */\n private async gateDocument<TDocument extends PHDocument>(\n document: TDocument,\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<TDocument> {\n const readable = await this.readableScopes(document, view, signal);\n return filterReadableScopes(document, readable);\n }\n\n /**\n * Retrieves a list of document model modules.\n */\n async getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n return this.reactor.getDocumentModels(namespace, paging, signal);\n }\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n async getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>> {\n const modules = await this.reactor.getDocumentModels();\n\n let latestModule: DocumentModelModule | undefined;\n let latestVersion = -1;\n for (const module of modules.results) {\n if (module.documentModel.global.id !== documentType) {\n continue;\n }\n const version = module.version ?? 1;\n if (version > latestVersion) {\n latestVersion = version;\n latestModule = module;\n }\n }\n\n if (!latestModule) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n\n return latestModule as DocumentModelModule<any>;\n }\n\n /**\n * Retrieves the document model module matching the version the document is\n * stamped with, so not-yet-upgraded documents get the reducer their\n * history was written with rather than the latest.\n */\n async getDocumentModelModuleForDocument(\n document: PHDocument,\n ): Promise<DocumentModelModule<any>> {\n const documentType = document.header.documentType;\n const version = normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n );\n\n const modules = await this.reactor.getDocumentModels();\n\n const availableVersions: number[] = [];\n for (const module of modules.results) {\n if (module.documentModel.global.id !== documentType) {\n continue;\n }\n const moduleVersion = normalizeDocumentModelVersion(module.version);\n if (moduleVersion === version) {\n return module as DocumentModelModule<any>;\n }\n availableVersions.push(moduleVersion);\n }\n\n throw new UnsupportedDocumentModelVersionError(\n documentType,\n version,\n availableVersions.sort((a, b) => a - b),\n );\n }\n\n /**\n * Retrieves a specific PHDocument\n */\n async get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@identifier, @view)\", identifier, view);\n const document = await this.reactor.getByIdOrSlug<TDocument>(\n identifier,\n withAuthScope(view),\n undefined,\n signal,\n );\n return this.gateDocument(document, view, signal);\n }\n\n /**\n * Resolves an identifier (id or slug) to the canonical document id, using the\n * same lookup as the data path. Resolves against the \"main\" branch. Throws if\n * the identifier cannot be resolved or is ambiguous.\n */\n async resolveIdOrSlug(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<string> {\n return this.documentView.resolveIdOrSlug(\n identifier,\n view,\n undefined,\n signal,\n );\n }\n\n /** True when the id is taken; a soft-deleted document's id is still taken. */\n async isDocumentIdTaken(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n this.logger.verbose(\"isDocumentIdTaken(@documentId)\", documentId);\n const taken = await this.documentView.exists(\n [documentId],\n DocumentExistence.IncludingDeleted,\n undefined,\n signal,\n );\n return taken[0];\n }\n\n /**\n * Retrieves operations for a document\n */\n async getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>> {\n this.logger.verbose(\n \"getOperations(@documentIdentifier, @view, @filter, @paging)\",\n documentIdentifier,\n view,\n filter,\n paging,\n );\n\n const documentId = await this.documentView.resolveIdOrSlug(\n documentIdentifier,\n view,\n undefined,\n signal,\n );\n\n // Read gate: exclude operations in scopes the subject may not read. The\n // fetch carries the scopes about to be paged, not the policy alone, because\n // a conditional read grant reads the state of the scope it gates; a\n // condition can name no other scope, so the scopes not paged are not owed.\n const gated = await this.reactor.getByIdOrSlug<PHDocument>(\n documentId,\n withAuthScope(view),\n undefined,\n signal,\n );\n const canRead = await this.readableScopes(gated, view, signal);\n\n if (paging?.cursor && isCompositeCursor(paging.cursor)) {\n return this.getOperationsWithCompositeCursor(\n documentId,\n view,\n filter,\n paging,\n signal,\n canRead,\n );\n }\n\n const operationsByScope = await this.reactor.getOperations(\n documentId,\n view,\n filter,\n paging,\n undefined,\n signal,\n );\n\n for (const scope of Object.keys(operationsByScope)) {\n if (!canRead(scope)) {\n delete operationsByScope[scope];\n }\n }\n\n const scopeEntries = Object.entries(operationsByScope);\n const effectivePaging = paging || { cursor: \"0\", limit: 100 };\n\n if (scopeEntries.length <= 1) {\n const allOperations =\n scopeEntries.length === 1 ? [...scopeEntries[0][1].results] : [];\n allOperations.sort((a, b) => a.index - b.index);\n const nextCursor =\n scopeEntries.length === 1 ? scopeEntries[0][1].nextCursor : undefined;\n return { results: allOperations, options: effectivePaging, nextCursor };\n }\n\n const allOperations: Operation[] = [];\n const activeCursors: Record<string, string> = {};\n\n for (const [scopeName, scopeResults] of scopeEntries) {\n allOperations.push(...scopeResults.results);\n if (scopeResults.nextCursor) {\n activeCursors[scopeName] = scopeResults.nextCursor;\n }\n }\n\n allOperations.sort((a, b) => a.index - b.index);\n\n const nextCursor =\n Object.keys(activeCursors).length > 0\n ? encodeCompositeCursor(activeCursors)\n : undefined;\n\n return { results: allOperations, options: effectivePaging, nextCursor };\n }\n\n private async getOperationsWithCompositeCursor(\n documentId: string,\n view: ViewFilter | undefined,\n filter: OperationFilter | undefined,\n paging: PagingOptions,\n signal: AbortSignal | undefined,\n canRead: (scope: string) => boolean,\n ): Promise<PagedResults<Operation>> {\n const scopeCursors = decodeCompositeCursor(paging.cursor);\n const allOperations: Operation[] = [];\n const activeCursors: Record<string, string> = {};\n\n for (const [scopeName, cursor] of Object.entries(scopeCursors)) {\n if (!canRead(scopeName)) {\n continue;\n }\n const scopeView: ViewFilter = { ...view, scopes: [scopeName] };\n const scopePaging: PagingOptions = { cursor, limit: paging.limit };\n\n const operationsByScope = await this.reactor.getOperations(\n documentId,\n scopeView,\n filter,\n scopePaging,\n undefined,\n signal,\n );\n\n const scopeResult = operationsByScope[scopeName];\n allOperations.push(...scopeResult.results);\n if (scopeResult.nextCursor) {\n activeCursors[scopeName] = scopeResult.nextCursor;\n }\n }\n\n allOperations.sort((a, b) => a.index - b.index);\n\n const nextCursor =\n Object.keys(activeCursors).length > 0\n ? encodeCompositeCursor(activeCursors)\n : undefined;\n\n return { results: allOperations, options: paging, nextCursor };\n }\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n */\n async getOutgoingRelationships(\n sourceIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getOutgoingRelationships(@sourceIdentifier, @relationshipType, @view, @paging)\",\n sourceIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const sourceId = await this.documentView.resolveIdOrSlug(\n sourceIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getOutgoing(\n sourceId,\n [relationshipType],\n undefined,\n undefined,\n signal,\n );\n\n const targetIds = relationships.results.map((rel) => rel.targetId);\n\n if (targetIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.find({ ids: targetIds }, view, paging, signal);\n }\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n */\n async getIncomingRelationships(\n targetIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getIncomingRelationships(@targetIdentifier, @relationshipType, @view, @paging)\",\n targetIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const targetId = await this.documentView.resolveIdOrSlug(\n targetIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getIncoming(\n targetId,\n [relationshipType],\n undefined,\n undefined,\n signal,\n );\n\n const sourceIds = relationships.results.map((rel) => rel.sourceId);\n\n if (sourceIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.find({ ids: sourceIds }, view, paging, signal);\n }\n\n /**\n * Filters documents by criteria and returns a list of them\n */\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n const results = await this.reactor.find(\n search,\n withAuthScope(view),\n paging,\n undefined,\n signal,\n );\n return {\n ...results,\n results: await Promise.all(\n results.results.map(async (doc) =>\n filterReadableScopes(\n doc,\n await this.readableScopes(doc, view, signal),\n ),\n ),\n ),\n };\n }\n\n /**\n * Predicts the admission verdict for each candidate. See\n * {@link IReactorClient.evaluateActions} for the contract and its caveats.\n *\n * Read-only throughout, and never through the write cache: that cache is\n * invalidated by whichever process runs the executor, so a reactor running\n * its executors in worker processes would answer here from state no commit\n * ever invalidates.\n */\n async evaluateActions(\n documentIdentifier: string,\n branch: string,\n candidates: ActionCandidate[],\n subject?: AuthSubject,\n signal?: AbortSignal,\n ): Promise<ActionEvaluations> {\n this.logger.verbose(\n \"evaluateActions(@documentIdentifier, @branch, @count candidates)\",\n documentIdentifier,\n branch,\n candidates.length,\n );\n\n // No model, no answer. The legacy host-table permission system is not\n // consulted and not composed with: see AuthEnforcementDisabledError.\n const config = this.actionEvaluation;\n if (config === undefined) {\n throw new AuthEnforcementDisabledError();\n }\n\n const decidedSubject = this.readSubject(subject);\n const resolvedId = await this.documentView.resolveIdOrSlug(\n documentIdentifier,\n { branch },\n undefined,\n signal,\n );\n\n // Built once per distinct target rather than once per candidate, so a batch\n // of candidates against one document reads its streams once.\n const targets = new Map<string, EvaluationTarget>();\n const evaluations: Evaluation[] = [];\n\n for (const candidate of candidates) {\n // CREATE_DOCUMENT is exempt from the executor's policy gate by necessity:\n // it runs before the document exists, so there is no policy to decide\n // against (see GATED_DOCUMENT_ACTIONS). Mirror the exemption rather than\n // deciding the candidate against the named document's policy, which would\n // predict a denial the submit path never issues.\n if (candidate.type === \"CREATE_DOCUMENT\") {\n evaluations.push({ decision: \"allow\" });\n continue;\n }\n\n const targetId = evaluationTargetId(candidate, resolvedId);\n\n let target = targets.get(targetId);\n if (target === undefined) {\n target = await this.buildEvaluationTarget(\n config,\n targetId,\n branch,\n signal,\n );\n targets.set(targetId, target);\n }\n\n evaluations.push(\n decideCandidate(config, target, decidedSubject, candidate),\n );\n }\n\n const allowed = evaluations.filter(\n (evaluation) => evaluation.decision === \"allow\",\n ).length;\n\n return {\n evaluations,\n allAllowed: candidates.length > 0 && allowed === candidates.length,\n anyAllowed: allowed > 0,\n allDenied: candidates.length > 0 && allowed === 0,\n anyDenied: allowed < candidates.length,\n };\n }\n\n /**\n * The decision model for one target document, built at its stream heads.\n *\n * The document is fetched unfiltered, because the policy is what decides:\n * reading it through the read gate would withhold the very scopes the\n * decision is about. A deleted document is served at its deletion boundary,\n * which is what lets the model refuse an execute against it -- authEnforcement\n * requires documentDecisions, so that read is available whenever this runs.\n *\n * Reading past the gate discloses nothing a submit does not. The `auth` and\n * `document` scopes are readable by every holder, so a verdict resting on the\n * policy alone is one the caller could compute unaided; and a verdict resting\n * on a conditional grant reads the executing scope's state exactly as\n * admission reads it, so the answer here is what submitting and being refused\n * would have revealed anyway.\n *\n * The append condition the build records is dropped. It guards a write, and\n * this makes none; reproducing it is also what the preflight cannot do, which\n * is why the answer is a prediction.\n */\n private async buildEvaluationTarget(\n config: ActionEvaluationConfig,\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<EvaluationTarget> {\n const document = await this.documentView.get(\n documentId,\n { branch },\n undefined,\n signal,\n );\n\n const target = { documentId, branch };\n const built = await buildDecisionModel(\n new SeededStateReader(this.documentView, document, branch),\n config.model,\n target,\n signal,\n );\n\n return {\n definition: config.model(target),\n model: built.model,\n scopeStates: (document.state ?? {}) as Record<string, unknown>,\n };\n }\n\n /**\n * Creates a document and waits for completion\n */\n async create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"create(@id, @parentIdentifier)\",\n document.header.id,\n parentIdentifier,\n );\n\n const documentId = document.header.id;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId,\n signing: {\n signature: documentId,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createActions: Action[] = await signActions(\n [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document\n ?.version,\n ),\n initialState: document.state,\n }),\n ],\n this.signer,\n signal,\n );\n\n const jobs: ExecutionJobPlan[] = [\n {\n key: \"create\",\n documentId,\n scope: getSharedActionScope(createActions),\n branch: \"main\",\n actions: createActions,\n dependsOn: [],\n },\n ];\n\n if (parentIdentifier) {\n const parentActions: Action[] = await signActions(\n [addRelationshipAction(parentIdentifier, documentId, \"child\")],\n this.signer,\n signal,\n );\n\n jobs.push({\n key: \"parent\",\n documentId: parentIdentifier,\n scope: getSharedActionScope(parentActions),\n branch: \"main\",\n actions: parentActions,\n dependsOn: [\"create\"],\n });\n }\n\n const batchResult = await this.reactor.executeBatch({ jobs }, signal);\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n const created = await this.reactor.get<TDocument>(documentId);\n return this.gateDocument(created, undefined, signal);\n }\n\n /**\n * Creates an empty document and waits for completion\n */\n async createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"createEmpty(@documentModelType, @options)\",\n documentModelType,\n options,\n );\n const modulesResult = await this.reactor.getDocumentModels(\n undefined,\n undefined,\n signal,\n );\n\n const matchingModules = modulesResult.results.filter(\n (m) => m.documentModel.global.id === documentModelType,\n );\n\n let module: DocumentModelModule | undefined;\n if (options?.documentModelVersion !== undefined) {\n const requestedVersion = normalizeDocumentModelVersion(\n options.documentModelVersion,\n );\n module = matchingModules.find(\n (m) => normalizeDocumentModelVersion(m.version) === requestedVersion,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType} with version: ${options.documentModelVersion}`,\n );\n }\n } else {\n module = matchingModules.reduce<DocumentModelModule | undefined>(\n (latest, current) => {\n if (latest === undefined) return current;\n const currentVersion = normalizeDocumentModelVersion(current.version);\n const latestVersion = normalizeDocumentModelVersion(latest.version);\n return currentVersion > latestVersion ? current : latest;\n },\n undefined,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType}`,\n );\n }\n }\n\n const document = module.utils.createDocument();\n document.state.document.version = normalizeDocumentModelVersion(\n module.version,\n );\n\n return this.create<TDocument>(document, options?.parentIdentifier, signal);\n }\n\n /**\n * Upgrades a document to a newer document model version by dispatching an\n * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the\n * latest registered module version for the document's type. Returns the\n * document unchanged when it is already at the target version.\n *\n * The executor validates the action's version and revision snapshot against\n * the state the migration actually runs on. When a concurrent edit\n * invalidates the snapshot, the upgrade is rebuilt from a fresh read and\n * retried up to maxConflictRetries times before the conflict is surfaced.\n */\n async upgradeDocument<TDocument extends PHDocument = PHDocument>(\n documentIdentifier: string,\n toVersion?: number,\n options?: UpgradeDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"upgradeDocument(@documentIdentifier, @toVersion)\",\n documentIdentifier,\n toVersion,\n );\n\n const maxConflictRetries =\n options?.maxConflictRetries ?? DEFAULT_UPGRADE_CONFLICT_RETRIES;\n\n let lastConflictMessage = \"\";\n for (let attempt = 0; attempt <= maxConflictRetries; attempt++) {\n const document = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n undefined,\n undefined,\n signal,\n );\n\n const documentId = document.header.id;\n const documentType = document.header.documentType;\n const branch = document.header.branch || \"main\";\n const fromVersion = normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n );\n\n let targetVersion = toVersion;\n if (targetVersion === undefined) {\n const module = await this.getDocumentModelModule(documentType);\n targetVersion = normalizeDocumentModelVersion(module.version);\n }\n\n if (targetVersion === fromVersion) {\n return this.gateDocument(document, { branch }, signal);\n }\n if (targetVersion < fromVersion) {\n throw new DowngradeNotSupportedError(\n documentType,\n fromVersion,\n targetVersion,\n );\n }\n\n const action = upgradeDocumentAction({\n documentId,\n model: documentType,\n fromVersion,\n toVersion: targetVersion,\n revision: { ...document.header.revision },\n });\n\n const signedActions = await signActions([action], this.signer, signal);\n const jobInfo = await this.reactor.execute(\n documentId,\n branch,\n signedActions,\n signal,\n );\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status !== JobStatus.FAILED) {\n const upgraded = await this.reactor.getByIdOrSlug<TDocument>(\n documentId,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(upgraded, { branch }, signal);\n }\n\n if (completedJob.error?.name !== \"UpgradePreconditionFailedError\") {\n throw new Error(completedJob.error?.message);\n }\n lastConflictMessage = completedJob.error.message;\n }\n\n throw new Error(\n `Upgrade of document ${documentIdentifier} conflicted with concurrent edits after ${maxConflictRetries + 1} attempts: ${lastConflictMessage}`,\n );\n }\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n * Delegates to {@link IDriveClient.addFile}.\n *\n * @deprecated Use `client.drives.addFile` instead. This method will be\n * removed in a future release.\n */\n async createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n return this.drives.addFile<TDocument>(\n driveId,\n document,\n parentFolder,\n signal,\n );\n }\n\n /**\n * Applies a list of actions to a document and waits for completion.\n *\n * `subject` names who the returned document is read as; see\n * {@link IReactorClient.execute}.\n */\n async execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n subject?: AuthSubject,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"execute(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const signedActions = await signActions(actions, this.signer, signal);\n\n const jobInfo = await this.reactor.execute(\n documentIdentifier,\n branch,\n signedActions,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const view: ViewFilter = { branch, subject };\n const result = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n view,\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, view, signal);\n }\n\n /**\n * Submits a list of actions to a document\n */\n async executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"executeAsync(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const signedActions = await signActions(actions, this.signer, signal);\n\n return this.reactor.execute(\n documentIdentifier,\n branch,\n signedActions,\n signal,\n );\n }\n\n async executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\"executeBatch(@count jobs)\", request.jobs.length);\n\n const signedJobs: ExecutionJobPlan[] = await Promise.all(\n request.jobs.map(async (job) => ({\n ...job,\n actions: await signActions(job.actions, this.signer, signal),\n })),\n );\n\n const batchResult = await this.reactor.executeBatch(\n { jobs: signedJobs },\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n return batchResult;\n }\n\n /**\n * Renames a document and waits for completion\n */\n async rename(\n documentIdentifier: string,\n name: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"rename(@documentIdentifier, @name, @branch)\",\n documentIdentifier,\n name,\n branch,\n );\n return this.execute(\n documentIdentifier,\n branch,\n [actions.setName(name)],\n signal,\n );\n }\n\n /**\n * Updates the preferred editor recorded in the document header meta.\n * Pass `null` to clear it.\n */\n async setPreferredEditor(\n documentIdentifier: string,\n preferredEditor: string | null,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"setPreferredEditor(@documentIdentifier, @preferredEditor, @branch)\",\n documentIdentifier,\n preferredEditor,\n branch,\n );\n return this.execute(\n documentIdentifier,\n branch,\n [actions.setPreferredEditor(preferredEditor)],\n signal,\n );\n }\n\n /**\n * Adds multiple documents as children to another and waits for completion\n */\n async addRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)\",\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n );\n const jobInfo = await this.reactor.addRelationship(\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceIdentifier,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, { branch }, signal);\n }\n\n /**\n * Removes a relationship between two documents and waits for completion.\n */\n async removeRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"removeRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)\",\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n );\n const jobInfo = await this.reactor.removeRelationship(\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceIdentifier,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, { branch }, signal);\n }\n\n /**\n * Moves a relationship from one source document to another and waits for completion.\n */\n async moveRelationship(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }> {\n this.logger.verbose(\n \"moveRelationship(@sourceParentIdentifier, @targetParentIdentifier, @targetIdentifier, @relationshipType, @branch)\",\n sourceParentIdentifier,\n targetParentIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n );\n const removeJobInfo = await this.reactor.removeRelationship(\n sourceParentIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);\n\n if (removeCompletedJob.status === JobStatus.FAILED) {\n throw new Error(removeCompletedJob.error?.message);\n }\n\n const addJobInfo = await this.reactor.addRelationship(\n targetParentIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const addCompletedJob = await this.waitForJob(addJobInfo, signal);\n\n if (addCompletedJob.status === JobStatus.FAILED) {\n throw new Error(addCompletedJob.error?.message);\n }\n\n const sourceResult = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceParentIdentifier,\n { branch },\n removeCompletedJob.consistencyToken,\n signal,\n );\n\n const targetResult = await this.reactor.getByIdOrSlug<PHDocument>(\n targetParentIdentifier,\n { branch },\n addCompletedJob.consistencyToken,\n signal,\n );\n\n return {\n source: await this.gateDocument(sourceResult, { branch }, signal),\n target: await this.gateDocument(targetResult, { branch }, signal),\n };\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n const result = await this.reactor.loadBatch(request, signal);\n\n const completedJobs = await Promise.all(\n Object.entries(result.jobs).map(async ([key, jobInfo]) => {\n const completed = await this.waitForJob(jobInfo, signal);\n return [key, completed] as const;\n }),\n );\n\n for (const [, completedJob] of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n\n return { jobs: Object.fromEntries(completedJobs) };\n }\n\n /**\n * Deletes a document and waits for completion\n */\n async deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocument(@identifier, @propagate)\",\n identifier,\n propagate,\n );\n const jobs: JobInfo[] = [];\n\n if (propagate === PropagationMode.Cascade) {\n const toDelete = new Set([identifier]);\n let changed = true;\n\n while (changed) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n changed = false;\n const orphans = await this.documentIndexer.getOrphanedChildren(\n [...toDelete],\n [\"child\"],\n signal,\n );\n for (const id of orphans) {\n if (!toDelete.has(id)) {\n toDelete.add(id);\n changed = true;\n }\n }\n }\n\n for (const descendantId of toDelete) {\n if (descendantId === identifier) {\n continue;\n }\n const removalJobs = await this.removeAllIncomingRelationships(\n descendantId,\n signal,\n );\n jobs.push(...removalJobs);\n\n const jobInfo = await this.reactor.deleteDocument(\n descendantId,\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n }\n }\n\n const removalJobs = await this.removeAllIncomingRelationships(\n identifier,\n signal,\n );\n jobs.push(...removalJobs);\n\n const jobInfo = await this.reactor.deleteDocument(\n identifier,\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n\n const completedJobs = await Promise.all(\n jobs.map((job) => this.waitForJob(job, signal)),\n );\n\n for (const completedJob of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n }\n\n /**\n * Deletes documents and waits for completion\n */\n async deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocuments(@count identifiers, @propagate)\",\n identifiers.length,\n propagate,\n );\n const deletePromises = identifiers.map((identifier) =>\n this.deleteDocument(identifier, propagate, signal),\n );\n\n await Promise.all(deletePromises);\n }\n\n /**\n * Retrieves the status of a job\n */\n async getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n return this.reactor.getJobStatus(jobId, signal);\n }\n\n /**\n * Waits for a job to complete\n */\n async waitForJob(\n jobId: string | JobInfo,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n const id = typeof jobId === \"string\" ? jobId : jobId.id;\n this.logger.verbose(\"waitForJob(@id)\", id);\n return this.jobAwaiter.waitForJob(id, signal);\n }\n\n /**\n * Subscribes to changes for documents matching specified filters\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void {\n this.logger.verbose(\"subscribe(@search, @view)\", search, view);\n\n // A subscription is a read. The filter lives here because the subscription\n // manager is a read model, which sees everything.\n const readable = async <TDocument extends PHDocument>(\n document: TDocument,\n ): Promise<TDocument> =>\n filterReadableScopes(document, await this.readableScopes(document, view));\n\n let disposed = false;\n let delivering: Promise<void> = Promise.resolve();\n\n /**\n * Queues one event behind those already queued. Gating an event resolves\n * asynchronously, so an event needing a group fetch would otherwise be\n * overtaken by the one behind it, and one still in flight would reach a\n * subscriber that has already unsubscribed. Only delivery is ordered, not\n * the gating: a single subscription can cover every document in the\n * reactor, so serializing that work would make its delivery rate the sum\n * of every gate build rather than the slowest. An event that cannot be\n * gated is withheld, because serving it unfiltered would leak the scopes\n * the gate did not clear -- but it is logged rather than swallowed, since\n * the gate rethrows a transient failure precisely so it is not read as a\n * denial.\n */\n const deliver = (\n event: DocumentChangeEvent | Promise<DocumentChangeEvent>,\n ): void => {\n // Attached now, or a rejection while the chain is parked on real I/O\n // waits a full turn with no handler, which Node reports as unhandled.\n void Promise.resolve(event).catch(() => undefined);\n\n delivering = delivering\n .then(async () => {\n const built = await event;\n if (disposed) {\n return;\n }\n callback(built);\n })\n .catch((error: unknown) => {\n this.logger.error(\n \"Subscription delivery failed for @search: @Error\",\n { search },\n error,\n );\n });\n };\n\n const unsubscribeCreated = this.subscriptionManager.onDocumentCreated(\n (result) => {\n deliver(\n (async () => {\n // withAuthScope, or a view that narrows scopes would leave the\n // policy out of the fetch and the gate would read an absent one as\n // uninitialized, which allows everything.\n const documents = await Promise.all(\n result.results.map((id) =>\n this.reactor.get(id, withAuthScope(view), undefined, undefined),\n ),\n );\n\n return {\n type: DocumentChangeType.Created,\n documents: await Promise.all(documents.map(readable)),\n };\n })(),\n );\n },\n search,\n );\n\n const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted(\n (documentIds) => {\n deliver({\n type: DocumentChangeType.Deleted,\n documents: [],\n context: { childId: documentIds[0] },\n });\n },\n search,\n );\n\n const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated(\n (result) => {\n deliver(\n (async () => ({\n type: DocumentChangeType.Updated,\n documents: await Promise.all(result.results.map(readable)),\n }))(),\n );\n },\n search,\n view,\n );\n\n const unsubscribeRelationship =\n this.subscriptionManager.onRelationshipChanged(\n (parentId, childId, changeType) => {\n deliver({\n type:\n changeType === RelationshipChangeType.Added\n ? DocumentChangeType.ChildAdded\n : DocumentChangeType.ChildRemoved,\n documents: [],\n context: {\n parentId,\n childId,\n },\n });\n },\n search,\n );\n\n return () => {\n disposed = true;\n unsubscribeCreated();\n unsubscribeDeleted();\n unsubscribeUpdated();\n unsubscribeRelationship();\n };\n }\n\n private async removeAllIncomingRelationships(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<JobInfo[]> {\n const incoming = await this.documentIndexer.getIncoming(\n documentId,\n undefined,\n undefined,\n undefined,\n signal,\n );\n\n const jobs: JobInfo[] = [];\n for (const rel of incoming.results) {\n const jobInfo = await this.reactor.removeRelationship(\n rel.sourceId,\n documentId,\n rel.relationshipType,\n \"main\",\n this.signer,\n signal,\n );\n jobs.push(jobInfo);\n }\n return jobs;\n }\n}\n","import type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport type { ErrorInfo, JobMeta } from \"../shared/types.js\";\n\n/**\n * What a job carries: a mutation applies actions, a load imports synced\n * operations, and a reevaluation re-judges a document's stored operations\n * because a read-set stream elsewhere (a group document) changed. A\n * reevaluation job carries no actions or operations; its work is derived\n * from what is already stored.\n */\nexport type JobKind = \"mutation\" | \"load\" | \"reevaluation\";\n\n/**\n * State of a job in the queue\n */\nexport enum JobQueueState {\n UNKNOWN = -1,\n PREPROCESSING = 0,\n PENDING = 1,\n READY = 2,\n RUNNING = 3,\n RESOLVED = 4,\n}\n\n/**\n * How a retry is accounted against the job's retry limit.\n * - `CountAgainstLimit` (default): a fault; the job eventually exhausts its\n * retries and fails terminally.\n * - `ExemptFromLimit`: not a fault, so the attempt is not charged to the job.\n * Used for concurrency conflicts, where the retry does new work.\n */\nexport enum RetryAccounting {\n CountAgainstLimit = \"count-against-limit\",\n ExemptFromLimit = \"exempt-from-limit\",\n}\n\n/**\n * Interface for a job execution handle\n */\nexport interface IJobExecutionHandle {\n readonly job: Job;\n readonly state: JobQueueState;\n\n start(): void;\n complete(): void;\n fail(error: ErrorInfo): void;\n defer(): void;\n}\n\n/**\n * Represents a job to be executed by the job executor\n */\nexport type Job = {\n /** Unique identifier for the job */\n id: string;\n\n /** Classification of the job so executors can switch behavior */\n kind: JobKind;\n\n /** The document ID this job operates on */\n documentId: string;\n\n /** The scope of the operations */\n scope: string;\n\n /** The branch of the operations */\n branch: string;\n\n /** The actions to be executed (processed sequentially) */\n actions: Action[];\n\n /** Pre-existing operations to import (used for load jobs) */\n operations: Operation[];\n\n /** Timestamp when the job was created */\n createdAt: string;\n\n /** The hint for the queue to use for ordering the job */\n queueHint: string[];\n\n /** Number of retry attempts */\n retryCount?: number;\n\n /** Maximum number of retries allowed */\n maxRetries?: number;\n\n /** Last error if job failed */\n lastError?: ErrorInfo;\n\n /** History of all errors from each attempt (ordered) */\n errorHistory: ErrorInfo[];\n\n /** Metadata that flows through the job lifecycle */\n meta: JobMeta;\n};\n\n/**\n * Event types for the queue system\n */\nexport const QueueEventTypes = {\n JOB_AVAILABLE: 10000,\n} as const;\n\n/**\n * Minimal projection of a Job used by IQueue.dequeueNextMatching predicates.\n * Exposes only the routing-relevant fields so routing logic (e.g. hash(documentId) % numWorkers)\n * can live outside the queue without leaking the full Job.\n */\nexport type JobRoutingMeta = {\n documentId: string;\n scope: string;\n branch: string;\n};\n\n/**\n * Event data for job available events\n */\nexport type JobAvailableEvent = {\n documentId: string;\n scope: string;\n branch: string;\n jobId: string;\n};\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport { RetryAccounting } from \"../queue/types.js\";\nimport type { IJobExecutionHandle, Job } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { ModuleNotFoundError } from \"../registry/errors.js\";\nimport {\n AuthorizationDeniedError,\n AuthTimestampNotMonotonicError,\n DocumentDeletedError,\n DocumentNotFoundError,\n ExcessiveReshuffleError,\n InvalidOperationTimestampError,\n UpgradePreconditionFailedError,\n} from \"../shared/errors.js\";\nimport {\n AppendConditionFailedError,\n DocumentAlreadyExistsError,\n} from \"../storage/interfaces.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { JobResult } from \"./types.js\";\n\n/** Conflict retries a job may take without charging its retry limit. */\nconst MAX_EXEMPT_CONFLICT_RETRIES = 20;\n\nexport type JobResultCallbacks = {\n deferJob(documentId: string, job: Job): void;\n flushDeferredFor(documentId: string): Promise<void>;\n};\n\nexport interface IJobResultHandler {\n handleResult(\n handle: IJobExecutionHandle,\n result: JobResult,\n callbacks: JobResultCallbacks,\n ): Promise<void>;\n}\n\nexport function toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n}\n\nexport class JobResultHandler implements IJobResultHandler {\n constructor(\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private eventBus: IEventBus,\n private resolver: IDocumentModelResolver,\n private logger: ILogger,\n ) {}\n\n async handleResult(\n handle: IJobExecutionHandle,\n result: JobResult,\n callbacks: JobResultCallbacks,\n ): Promise<void> {\n if (result.success) {\n handle.complete();\n\n if (this.hasCreateDocumentAction(handle.job)) {\n await callbacks.flushDeferredFor(handle.job.documentId);\n }\n return;\n }\n\n // Attempt model recovery before exhausting retries\n if (result.error && ModuleNotFoundError.isError(result.error)) {\n let modelLoaded = false;\n try {\n await this.resolver.ensureModelLoaded(result.error.documentType);\n modelLoaded = true;\n } catch {\n // Model could not be loaded, fall through to normal failure path\n }\n\n if (modelLoaded) {\n const errorInfo = toErrorInfo(result.error);\n try {\n await this.queue.retryJob(handle.job.id, errorInfo);\n return;\n } catch {\n // Fall through to normal failure path\n }\n }\n }\n\n // AppendConditionFailedError: a concurrency conflict, not a fault. Retry\n // exempt from the retry limit; the executor already dropped the stale\n // streams from the write cache.\n if (\n result.error &&\n AppendConditionFailedError.isError(result.error) &&\n this.countConflicts(handle.job) < MAX_EXEMPT_CONFLICT_RETRIES\n ) {\n const errorInfo = toErrorInfo(result.error);\n try {\n await this.queue.retryJob(\n handle.job.id,\n errorInfo,\n RetryAccounting.ExemptFromLimit,\n );\n return;\n } catch {\n // Fall through to normal failure path\n }\n }\n\n // DocumentNotFoundError on a load job: the operations came from a remote and\n // may have arrived ahead of the ones that create the document, so the job is\n // held for a CREATE_DOCUMENT that could still land. Held against the id the\n // error names, which is the document a flush has to create - not necessarily\n // the job's own, since a relationship reads its source.\n //\n // A mutation is not held. It names its document itself, and nothing in\n // flight will create one the caller got wrong, so holding it only delays the\n // refusal until the deferral deadline. It falls through to the deterministic\n // failures below.\n if (\n result.error &&\n DocumentNotFoundError.isError(result.error) &&\n handle.job.kind === \"load\"\n ) {\n handle.defer();\n callbacks.deferJob(result.error.documentId, handle.job);\n return;\n }\n\n if (\n result.error &&\n (DocumentDeletedError.isError(result.error) ||\n // The id is taken. A retry re-writes against the same stream and loses\n // the same way; only a new id resolves it.\n DocumentAlreadyExistsError.isError(result.error) ||\n // A document that is not there will not appear because a load ran again.\n DocumentNotFoundError.isError(result.error) ||\n AuthorizationDeniedError.isError(result.error) ||\n // All deterministic, so retrying only re-runs the load to fail the same.\n AuthTimestampNotMonotonicError.isError(result.error) ||\n InvalidOperationTimestampError.isError(result.error) ||\n ExcessiveReshuffleError.isError(result.error) ||\n // The action's snapshot stays stale; the client retries with a fresh read.\n UpgradePreconditionFailedError.isError(result.error))\n ) {\n const errorInfo = toErrorInfo(result.error);\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error,\n job: handle.job,\n })\n .catch(() => {});\n handle.fail(errorInfo);\n return;\n }\n\n const retryCount = handle.job.retryCount || 0;\n const maxRetries = handle.job.maxRetries || 0;\n\n if (retryCount < maxRetries) {\n const currentErrorInfo = result.error\n ? toErrorInfo(result.error)\n : toErrorInfo(\"Unknown error\");\n\n try {\n await this.queue.retryJob(handle.job.id, currentErrorInfo);\n } catch (error) {\n const retryErrorInfo = toErrorInfo(\n error instanceof Error ? error : \"Failed to retry job\",\n );\n\n this.jobTracker.markFailed(handle.job.id, retryErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(retryErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(retryErrorInfo);\n }\n } else {\n const currentErrorInfo = result.error\n ? toErrorInfo(result.error)\n : toErrorInfo(\"Unknown error\");\n\n const fullErrorInfo = this.formatErrorHistory(\n handle.job.errorHistory,\n currentErrorInfo,\n retryCount + 1,\n );\n\n this.jobTracker.markFailed(handle.job.id, fullErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(fullErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(fullErrorInfo);\n }\n }\n\n /** How many times this job has already lost an append-condition race. */\n private countConflicts(job: Job): number {\n let conflicts = 0;\n for (const error of job.errorHistory) {\n if (AppendConditionFailedError.isFailureMessage(error.message)) {\n conflicts++;\n }\n }\n return conflicts;\n }\n\n private hasCreateDocumentAction(job: Job): boolean {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n return false;\n }\n\n private formatErrorHistory(\n errorHistory: ErrorInfo[],\n currentError: ErrorInfo,\n totalAttempts: number,\n ): ErrorInfo {\n const allErrors = [...errorHistory, currentError];\n\n if (allErrors.length === 1) {\n return currentError;\n }\n\n const messageLines = [`Job failed after ${totalAttempts} attempts:`];\n const stackLines: string[] = [];\n\n allErrors.forEach((error, index) => {\n messageLines.push(`[Attempt ${index + 1}] ${error.message}`);\n stackLines.push(`[Attempt ${index + 1}] Stack trace:\\n${error.stack}`);\n });\n\n return {\n // The attempt that ended the job is the one a consumer classifies by.\n name: currentError.name,\n message: messageLines.join(\"\\n\"),\n stack: stackLines.join(\"\\n\\n\"),\n };\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport { toErrorInfo } from \"./job-result-handler.js\";\nimport { DEFAULT_DEFERRED_JOB_TTL_MS } from \"./types.js\";\n\n/**\n * The jobs held back because the document they write to was not there yet.\n *\n * A job whose document is missing is deferred rather than failed, because the\n * operations that create that document may still be on their way - out of order\n * from a peer, or a sibling job in the same batch. Deferring emits no event and\n * moves no job status, which is what makes it cheap: a job that gets flushed\n * runs as if it had merely waited its turn.\n *\n * That silence is also why the wait has to be bounded. A deferred job is only\n * released when a job carrying CREATE_DOCUMENT for that exact document id\n * completes, so a job deferred against an id nothing will ever create is\n * released by nothing. Its status stays RUNNING, which is not terminal, so a\n * caller awaiting it waits forever - there is no timeout on that side. The TTL\n * converts that silence into the failure the caller can act on.\n *\n * Failing through the queue rather than only the tracker matters: the queue\n * records the job as resolved, which is what unblocks anything that declared a\n * dependency on it. Deferring alone does not, so a dependent of a deferred job\n * is stranded at PENDING for as long as the deferral lasts.\n */\nexport class DeferredJobs {\n #byDocumentId = new Map<string, Job[]>();\n #timers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(\n private readonly queue: IQueue,\n private readonly jobTracker: IJobTracker,\n private readonly eventBus: IEventBus,\n private readonly logger: ILogger,\n private readonly ttlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n /**\n * Called once an expiry has resolved a job in the queue. Resolving one job\n * can make a dependent runnable, and an expiry fires outside the dispatch\n * loop, so without this nudge the dependent waits for unrelated traffic to\n * wake the manager up.\n */\n private readonly onReleased: () => Promise<void> = () => Promise.resolve(),\n ) {}\n\n /**\n * Holds a job until its document arrives, or until the TTL elapses.\n *\n * A job deferred again after a flush replaces its own timer rather than\n * accumulating one, so the wait is measured from the most recent attempt.\n */\n add(documentId: string, job: Job): void {\n const existing = this.#byDocumentId.get(documentId) ?? [];\n existing.push(job);\n this.#byDocumentId.set(documentId, existing);\n\n this.#clearTimer(job.id);\n const timer = setTimeout(() => {\n void this.#expire(documentId, job);\n }, this.ttlMs);\n // Node keeps the process alive for a pending timer; a deferral must not.\n // Browsers hand back a number, which has nothing to unref.\n if (typeof timer === \"object\" && \"unref\" in timer) {\n timer.unref();\n }\n this.#timers.set(job.id, timer);\n }\n\n /** Re-enqueues everything waiting on a document that has now arrived. */\n async flush(documentId: string): Promise<void> {\n const jobs = this.#byDocumentId.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.#byDocumentId.delete(documentId);\n\n for (const job of jobs) {\n this.#clearTimer(job.id);\n try {\n await this.queue.enqueue(job);\n } catch (error) {\n this.logger.error(\"Error re-enqueuing deferred job: @Error\", error);\n }\n }\n }\n\n /**\n * Fails everything still deferred, for a reactor that is shutting down.\n *\n * The queue is deliberately not told: it is going away too, and nothing is\n * left to unblock.\n */\n failAll(): void {\n for (const [, jobs] of this.#byDocumentId) {\n for (const job of jobs) {\n this.#clearTimer(job.id);\n this.#markFailed(job);\n }\n }\n this.#byDocumentId.clear();\n }\n\n /** The jobs still waiting on a document. Exposed for assertions. */\n waitingOn(documentId: string): readonly Job[] {\n return this.#byDocumentId.get(documentId) ?? [];\n }\n\n async #expire(documentId: string, job: Job): Promise<void> {\n const jobs = this.#byDocumentId.get(documentId);\n if (!jobs?.includes(job)) {\n return;\n }\n const remaining = jobs.filter((held) => held !== job);\n if (remaining.length === 0) {\n this.#byDocumentId.delete(documentId);\n } else {\n this.#byDocumentId.set(documentId, remaining);\n }\n this.#timers.delete(job.id);\n\n this.logger.error(\n \"Deferred job @jobId gave up waiting for document @documentId after @ttlMs ms\",\n job.id,\n documentId,\n this.ttlMs,\n );\n\n const errorInfo = this.#markFailed(job);\n\n try {\n // Resolves the job in the queue, which is what releases its dependents.\n await this.queue.failJob(job.id, errorInfo);\n } catch (error) {\n this.logger.error(\"Error failing an expired deferred job: @Error\", error);\n return;\n }\n\n // Deferring dropped the job from the queue's index, so the JOB_FAILED the\n // queue just emitted carried no job with it, and the tracker's own handler\n // overwrote the one recorded above with nothing. Re-asserted so the failed\n // job keeps the record it is meant to be debuggable from.\n this.jobTracker.markFailed(job.id, errorInfo, job);\n\n try {\n await this.onReleased();\n } catch (error) {\n this.logger.error(\n \"Error dispatching after a deferred job expired: @Error\",\n error,\n );\n }\n }\n\n #markFailed(job: Job) {\n const error = new DocumentNotFoundError(job.documentId);\n const errorInfo = toErrorInfo(error);\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error,\n job,\n })\n .catch(() => {});\n return errorInfo;\n }\n\n #clearTimer(jobId: string): void {\n const timer = this.#timers.get(jobId);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.#timers.delete(jobId);\n }\n }\n}\n","/**\n * Sticky-by-document routing for the executor worker pool.\n *\n * Hashes `documentId` to a stable bucket so every job on a given document\n * lands on the same worker. The queue already enforces \"at most one job\n * executing per document at a time\", so a sticky worker always sees a fresh\n * post-commit snapshot before its next job on that document — which keeps\n * the per-worker `IWriteCache` and `IDocumentMetaCache` coherent for free.\n *\n * The hash is FNV-1a 32-bit: deterministic, dependency-free, and stable\n * across processes (so the same routing decision can be reproduced anywhere\n * the documentId is known).\n *\n * @see Executor Worker Pool Design wiki page\n * (Powerhouse board wiki id: d400d711-f07e-4389-a226-4e9fdd4fa8ba)\n */\n\nconst FNV_OFFSET_BASIS = 0x811c9dc5;\nconst FNV_PRIME = 0x01000193;\n\nexport function hashDocumentId(documentId: string): number {\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < documentId.length; i++) {\n hash ^= documentId.charCodeAt(i);\n hash = Math.imul(hash, FNV_PRIME);\n }\n return hash >>> 0;\n}\n\nexport function bucketFor(documentId: string, numWorkers: number): number {\n if (numWorkers < 1) {\n throw new Error(`bucketFor: numWorkers must be >= 1 (got ${numWorkers})`);\n }\n return hashDocumentId(documentId) % numWorkers;\n}\n","import type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobRunningEvent,\n type JobWriteReadyEvent,\n} from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type {\n IJobExecutionHandle,\n Job,\n JobRoutingMeta,\n} from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type {\n IExecutorWorker,\n IJobExecutor,\n IJobExecutorManager,\n WorkerExecutionOutcome,\n} from \"./interfaces.js\";\nimport { DeferredJobs } from \"./deferred-jobs.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\n DEFAULT_DEFERRED_JOB_TTL_MS,\n JobExecutorEventTypes,\n type ExecutorManagerStatus,\n type JobCompletedEvent,\n type JobFailedEvent,\n type JobStartedEvent,\n} from \"./types.js\";\nimport {\n WorkerAbortTimeoutError,\n WorkerExitedError,\n WorkerInitFailedError,\n} from \"./worker/errors.js\";\nimport { bucketFor } from \"./worker-pool-router.js\";\nimport type {\n JobWriteReadyPayload,\n ModelManifestEntry,\n} from \"./worker/protocol.js\";\n\n/**\n * Factory invoked once per worker at `start()` time. The index is the\n * worker's position in the pool and the same value the manager will use\n * for sticky routing (`bucketFor(documentId) === index`).\n */\nexport type WorkerFactory = (index: number) => IExecutorWorker;\n\n/**\n * Manages a pool of executor workers and dispatches jobs across them with\n * sticky-by-documentId routing. Replaces `SimpleJobExecutorManager` when\n * the worker pool is enabled.\n *\n * Responsibilities that stay on the parent (not in the worker):\n * - Dequeueing from `IQueue` and routing to the matching worker bucket.\n * - Emitting `JOB_RUNNING` and `JOB_WRITE_READY` events; the worker's\n * local event bus is a no-op stub.\n * - Maintaining the deferred-jobs map for `DocumentNotFoundError`.\n * - Enriching the outgoing `JOB_WRITE_READY` with\n * `collectionMemberships`, read from the operation index after the\n * worker has committed. It takes the whole index rather than a\n * narrower read interface so that handing it a cache does not\n * compile: which documents a commit moves between collections is not\n * derivable from action shape, because joining a collection also\n * joins every group the joining document has referenced and that set\n * comes from selects run inside the commit. A parent-side cache\n * cannot learn it went stale, and a stale entry silently drops the\n * document's operations from the outbox of every remote subscribed to\n * the omitted collection. Costs one primary-key-prefix lookup on\n * `document_collections` per job that produced operations, on a\n * fire-and-forget path.\n *\n * @see Executor Worker Pool Design wiki page\n * (Powerhouse board wiki id: d400d711-f07e-4389-a226-4e9fdd4fa8ba)\n */\nexport class WorkerPoolJobExecutorManager implements IJobExecutorManager {\n private workers: IExecutorWorker[] = [];\n private isRunning = false;\n private activeJobs = 0;\n private totalJobsProcessed = 0;\n private unsubscribe?: () => void;\n private deferredJobs: DeferredJobs;\n private resultHandler: IJobResultHandler;\n private jobTimeoutMs: number;\n\n constructor(\n private workerFactory: WorkerFactory,\n private eventBus: IEventBus,\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private logger: ILogger,\n private resolver: IDocumentModelResolver,\n private operationIndex: IOperationIndex,\n jobTimeoutMs: number = 30_000,\n deferredJobTtlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\n this.deferredJobs = new DeferredJobs(\n queue,\n jobTracker,\n eventBus,\n logger,\n deferredJobTtlMs,\n () => this.tryDispatchAll(),\n );\n this.resultHandler = new JobResultHandler(\n queue,\n jobTracker,\n eventBus,\n resolver,\n logger,\n );\n }\n\n async start(numWorkers: number): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"WorkerPoolJobExecutorManager is already running\");\n }\n if (numWorkers < 1) {\n throw new Error(\"Number of workers must be at least 1\");\n }\n\n this.workers = Array.from({ length: numWorkers }, (_, i) =>\n this.workerFactory(i),\n );\n await Promise.all(this.workers.map((w) => w.start()));\n\n this.unsubscribe = this.eventBus.subscribe(\n QueueEventTypes.JOB_AVAILABLE,\n async () => {\n await this.tryDispatchAll();\n },\n );\n\n this.isRunning = true;\n await this.tryDispatchAll();\n }\n\n async stop(graceful = true): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n if (graceful) {\n while (this.activeJobs > 0) {\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n\n this.deferredJobs.failAll();\n\n await Promise.all(\n this.workers.map((w) =>\n w.shutdown(graceful).catch((err: unknown) => {\n this.logger.warn(\"worker shutdown failed: @Error\", err);\n }),\n ),\n );\n\n this.workers = [];\n this.isRunning = false;\n }\n\n /**\n * Worker-pool mode has no in-process `IJobExecutor` instances — the\n * executors live in worker threads behind `IExecutorWorker` handles.\n * Returns an empty array; callers that need pool-aware introspection\n * should use `getStatus()` instead.\n */\n getExecutors(): IJobExecutor[] {\n return [];\n }\n\n /**\n * Broadcasts a `load-model` request to every running worker in parallel.\n * Rejects with the first worker's failure if any worker rejects (after\n * waiting for all in-flight broadcasts to settle). Workers that already\n * have the model registered respond with a `DuplicateModuleError`-rooted\n * failure; those are treated as success on the broadcast level so that\n * a model registered on some workers but not others still converges.\n */\n async loadModel(entry: ModelManifestEntry): Promise<void> {\n if (this.workers.length === 0) {\n return;\n }\n const results = await Promise.allSettled(\n this.workers.map((w) => w.loadModel(entry)),\n );\n const failures = results\n .filter((r): r is PromiseRejectedResult => r.status === \"rejected\")\n .filter((r) => !isDuplicateModuleFailure(r.reason));\n if (failures.length === 0) {\n return;\n }\n for (const f of failures) {\n this.logger.error(\n \"worker failed to load model @entry: @error\",\n entry,\n f.reason,\n );\n }\n throw failures[0].reason instanceof Error\n ? failures[0].reason\n : new Error(String(failures[0].reason));\n }\n\n getStatus(): ExecutorManagerStatus {\n return {\n isRunning: this.isRunning,\n numExecutors: this.workers.length,\n activeJobs: this.activeJobs,\n totalJobsProcessed: this.totalJobsProcessed,\n };\n }\n\n private async tryDispatchAll(): Promise<void> {\n if (!this.isRunning && this.workers.length === 0) {\n return;\n }\n await Promise.all(\n this.workers.map((worker) => this.tryDispatchFor(worker)),\n );\n }\n\n private async tryDispatchFor(worker: IExecutorWorker): Promise<void> {\n if (!worker.isIdle()) {\n return;\n }\n\n const index = worker.index;\n const numWorkers = this.workers.length;\n const predicate = (meta: JobRoutingMeta): boolean =>\n bucketFor(meta.documentId, numWorkers) === index;\n\n let handle: IJobExecutionHandle | null;\n try {\n handle = await this.queue.dequeueNextMatching(predicate);\n } catch (error) {\n this.logger.error(\"Error dequeueing next job: @Error\", error);\n return;\n }\n\n if (!handle) {\n return;\n }\n\n handle.start();\n this.activeJobs++;\n this.jobTracker.markRunning(handle.job.id);\n\n const runningEvent: JobRunningEvent = {\n jobId: handle.job.id,\n jobMeta: handle.job.meta,\n };\n this.eventBus\n .emit(ReactorEventTypes.JOB_RUNNING, runningEvent)\n .catch(() => {});\n\n const workerId = worker.workerId;\n const startedEvent: JobStartedEvent = {\n job: handle.job,\n startedAt: new Date().toISOString(),\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_STARTED, startedEvent)\n .catch(() => {});\n\n const signal = AbortSignal.timeout(this.jobTimeoutMs);\n let outcome: WorkerExecutionOutcome;\n try {\n outcome = await worker.execute(handle.job, signal);\n } catch (error) {\n const errorInfo = toErrorInfo(\n error instanceof Error ? error : String(error),\n );\n if (isWorkerTransportError(error)) {\n await this.handleWorkerTransportFailure(worker, handle.job, errorInfo);\n return;\n }\n handle.fail(errorInfo);\n this.activeJobs--;\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: new Error(errorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: errorInfo.message,\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n await this.tryDispatchFor(worker);\n return;\n }\n\n if (outcome.result.success) {\n this.totalJobsProcessed++;\n const completedEvent: JobCompletedEvent = {\n job: handle.job,\n result: outcome.result,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_COMPLETED, completedEvent)\n .catch(() => {});\n } else {\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: outcome.result.error?.message ?? \"unknown\",\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n }\n\n if (outcome.result.success && outcome.writeReady) {\n void this.emitWriteReady(handle.job, outcome.writeReady).catch(\n (error) => {\n this.logger.error(\n \"emitWriteReady failed for job @jobId: @Error\",\n { jobId: handle.job.id },\n error,\n );\n },\n );\n }\n\n await this.resultHandler.handleResult(handle, outcome.result, {\n deferJob: (documentId, job) => this.deferredJobs.add(documentId, job),\n flushDeferredFor: (documentId) => this.deferredJobs.flush(documentId),\n });\n\n this.activeJobs--;\n await this.tryDispatchFor(worker);\n }\n\n private async emitWriteReady(\n job: Job,\n payload: JobWriteReadyPayload,\n ): Promise<void> {\n const documentIds = [\n ...new Set(payload.operations.map((op) => op.context.documentId)),\n ];\n let collectionMemberships: Record<string, string[]> = {};\n if (documentIds.length > 0) {\n try {\n const found =\n await this.operationIndex.getCollectionsForDocuments(documentIds);\n collectionMemberships = fillMissingMemberships(documentIds, found);\n } catch (error) {\n this.logger.error(\n \"Failed to load collection memberships for JOB_WRITE_READY: @Error\",\n error,\n );\n }\n }\n\n const event: JobWriteReadyEvent = {\n jobId: job.id,\n operations: payload.operations,\n jobMeta: payload.jobMeta,\n collectionMemberships,\n };\n try {\n await this.eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, event);\n } catch (error) {\n this.logger.error(\"Failed to emit JOB_WRITE_READY event: @Error\", error);\n }\n }\n\n /**\n * Handle a worker-transport failure (worker exited / init failed / abort\n * timed out) detected while `worker.execute` was in flight. Re-enqueues\n * the in-flight job via `queue.retryJob` so it is retried on a healthy\n * worker, then replaces the dead worker with a fresh handle and resumes\n * dispatch on the same bucket. Does NOT emit JOB_FAILED — the job is\n * not failed, only the worker is.\n */\n private async handleWorkerTransportFailure(\n dead: IExecutorWorker,\n job: Job,\n errorInfo: ReturnType<typeof toErrorInfo>,\n ): Promise<void> {\n this.logger.warn(\n \"worker transport error during execute; retrying job @jobId on a replacement worker: @error\",\n { jobId: job.id, workerId: dead.workerId },\n errorInfo.message,\n );\n\n this.activeJobs--;\n\n // Replace the dead worker BEFORE re-enqueuing the job. Otherwise\n // `queue.retryJob` emits JOB_AVAILABLE, the subscriber re-runs\n // `tryDispatchAll`, and the still-in-the-array dead worker picks up\n // the retried job — looping until heap exhaustion.\n await this.replaceWorker(dead);\n\n try {\n await this.queue.retryJob(job.id, errorInfo);\n } catch (error) {\n this.logger.error(\n \"failed to re-enqueue job after worker transport error: @Error\",\n error,\n );\n }\n }\n\n /**\n * Replace a dead worker at its existing pool index with a fresh handle\n * produced by `workerFactory`. Awaits `start()` on the replacement so it\n * is ready before dispatch resumes. On replacement failure the slot is\n * left empty (the index becomes a hole that subsequent retries will\n * route to no worker) and the error is logged — the manager keeps\n * running so other buckets continue to make progress.\n */\n private async replaceWorker(dead: IExecutorWorker): Promise<void> {\n const deadIndex = dead.index;\n if (this.workers[deadIndex] !== dead) {\n return;\n }\n\n let fresh: IExecutorWorker;\n try {\n fresh = this.workerFactory(deadIndex);\n } catch (error) {\n this.logger.error(\n \"workerFactory threw while replacing dead worker at index @index: @Error\",\n deadIndex,\n error,\n );\n return;\n }\n\n try {\n await fresh.start();\n } catch (error) {\n this.logger.error(\n \"replacement worker at index @index failed to start: @Error\",\n deadIndex,\n error,\n );\n return;\n }\n\n this.workers[deadIndex] = fresh;\n await this.tryDispatchFor(fresh);\n }\n}\n\nfunction isWorkerTransportError(error: unknown): boolean {\n return (\n error instanceof WorkerExitedError ||\n error instanceof WorkerInitFailedError ||\n error instanceof WorkerAbortTimeoutError\n );\n}\n\nfunction isDuplicateModuleFailure(reason: unknown): boolean {\n if (!(reason instanceof Error)) {\n return false;\n }\n if (reason.name === \"DuplicateModuleError\") {\n return true;\n }\n const cause = (reason as { cause?: unknown }).cause;\n return (\n cause instanceof Error && (cause as Error).name === \"DuplicateModuleError\"\n );\n}\n\n/**\n * Gives every requested document a key, empty when it belongs to no\n * collection. The operation index omits documents with no rows while the\n * membership cache defaulted them to `[]`, so this keeps the emitted\n * `JobWriteReadyEvent.collectionMemberships` shape unchanged.\n */\nfunction fillMissingMemberships(\n documentIds: string[],\n found: Record<string, string[]>,\n): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const documentId of documentIds) {\n result[documentId] = found[documentId] ?? [];\n }\n return result;\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobRunningEvent } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { IJobExecutionHandle } from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type { IJobExecutor, IJobExecutorManager } from \"./interfaces.js\";\nimport { DeferredJobs } from \"./deferred-jobs.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\n DEFAULT_DEFERRED_JOB_TTL_MS,\n JobExecutorEventTypes,\n type ExecutorManagerStatus,\n type JobCompletedEvent,\n type JobFailedEvent,\n type JobResult,\n type JobStartedEvent,\n} from \"./types.js\";\n\nexport type JobExecutorFactory = () => IJobExecutor;\n\n/**\n * Manages multiple job executors and coordinates job distribution.\n * Listens for job available events and dispatches jobs to executors.\n */\nexport class SimpleJobExecutorManager implements IJobExecutorManager {\n private executors: IJobExecutor[] = [];\n private isRunning = false;\n private activeJobs = 0;\n private totalJobsProcessed = 0;\n private unsubscribe?: () => void;\n private deferredJobs: DeferredJobs;\n private resultHandler: IJobResultHandler;\n\n private jobTimeoutMs: number;\n\n constructor(\n private executorFactory: JobExecutorFactory,\n private eventBus: IEventBus,\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private logger: ILogger,\n private resolver: IDocumentModelResolver,\n jobTimeoutMs: number = 30_000,\n deferredJobTtlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\n this.deferredJobs = new DeferredJobs(\n queue,\n jobTracker,\n eventBus,\n logger,\n deferredJobTtlMs,\n () => this.checkForMoreJobs(),\n );\n this.resultHandler = new JobResultHandler(\n queue,\n jobTracker,\n eventBus,\n resolver,\n logger,\n );\n }\n\n async start(numExecutors: number): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"JobExecutorManager is already running\");\n }\n\n if (numExecutors < 1) {\n throw new Error(\"Number of executors must be at least 1\");\n }\n\n // Create executors\n this.executors = [];\n for (let i = 0; i < numExecutors; i++) {\n this.executors.push(this.executorFactory());\n }\n\n // Start listening for job available events\n this.unsubscribe = this.eventBus.subscribe(\n QueueEventTypes.JOB_AVAILABLE,\n async () => {\n // Only process if we have capacity (simple round-robin for now)\n if (this.activeJobs < this.executors.length) {\n await this.processNextJob();\n }\n },\n );\n\n this.isRunning = true;\n\n // Process any existing jobs in the queue\n await this.processExistingJobs();\n }\n\n async stop(graceful = true): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n // Stop listening for new jobs\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n if (graceful) {\n // Wait for active jobs to complete\n while (this.activeJobs > 0) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n // Fail any deferred jobs that were never flushed\n this.deferredJobs.failAll();\n\n this.executors = [];\n this.isRunning = false;\n }\n\n getExecutors(): IJobExecutor[] {\n return [...this.executors];\n }\n\n getStatus(): ExecutorManagerStatus {\n return {\n isRunning: this.isRunning,\n numExecutors: this.executors.length,\n activeJobs: this.activeJobs,\n totalJobsProcessed: this.totalJobsProcessed,\n };\n }\n\n private async processNextJob(): Promise<void> {\n // dequeue next available job\n let handle: IJobExecutionHandle | null;\n try {\n handle = await this.queue.dequeueNext();\n } catch (error) {\n this.logger.error(\"Error dequeueing next job: @Error\", error);\n return;\n }\n\n if (!handle) {\n return;\n }\n\n // start the job execution\n handle.start();\n this.activeJobs++;\n this.jobTracker.markRunning(handle.job.id);\n\n // Emit JOB_RUNNING event\n const runningEvent: JobRunningEvent = {\n jobId: handle.job.id,\n jobMeta: handle.job.meta,\n };\n this.eventBus\n .emit(ReactorEventTypes.JOB_RUNNING, runningEvent)\n .catch(() => {\n // Ignore event emission errors\n });\n\n // Find an available executor (simple round-robin)\n const executorIndex = this.totalJobsProcessed % this.executors.length;\n const executor = this.executors[executorIndex];\n const workerId = `in-process-${executorIndex}`;\n\n const startedEvent: JobStartedEvent = {\n job: handle.job,\n startedAt: new Date().toISOString(),\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_STARTED, startedEvent)\n .catch(() => {});\n\n // execute the job with a timeout signal; race ensures the timeout fires\n // even if the executor hangs on a call that does not check the signal\n const signal = AbortSignal.timeout(this.jobTimeoutMs);\n const toError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new Error(String(reason));\n const abortPromise = new Promise<never>((_, reject) => {\n if (signal.aborted) {\n reject(toError(signal.reason));\n return;\n }\n signal.addEventListener(\"abort\", () => reject(toError(signal.reason)), {\n once: true,\n });\n });\n let result: JobResult;\n try {\n result = await Promise.race([\n executor.executeJob(handle.job, signal),\n abortPromise,\n ]);\n } catch (error) {\n const errorInfo = toErrorInfo(\n error instanceof Error ? error : String(error),\n );\n\n handle.fail(errorInfo);\n this.activeJobs--;\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: new Error(errorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: errorInfo.message,\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n\n await this.checkForMoreJobs();\n return;\n }\n\n // handle the result\n if (result.success) {\n this.totalJobsProcessed++;\n }\n\n if (result.success) {\n const completedEvent: JobCompletedEvent = {\n job: handle.job,\n result,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_COMPLETED, completedEvent)\n .catch(() => {});\n } else {\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: result.error?.message ?? \"unknown\",\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n }\n\n await this.resultHandler.handleResult(handle, result, {\n deferJob: (documentId, job) => this.deferredJobs.add(documentId, job),\n flushDeferredFor: (documentId) => this.deferredJobs.flush(documentId),\n });\n\n this.activeJobs--;\n await this.checkForMoreJobs();\n }\n\n private async checkForMoreJobs(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n let hasMore: boolean;\n try {\n hasMore = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for more jobs: @Error\", error);\n return;\n }\n\n if (hasMore) {\n await this.processNextJob();\n }\n }\n\n private async processExistingJobs(): Promise<void> {\n let hasJobs: boolean;\n try {\n hasJobs = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for existing jobs: @Error\", error);\n return;\n }\n\n if (hasJobs) {\n // Start processing up to the number of executors\n const promises: Promise<void>[] = [];\n for (let i = 0; i < Math.min(this.executors.length, 5); i++) {\n promises.push(this.processNextJob());\n }\n\n try {\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error processing existing jobs: @Error\", error);\n }\n }\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport {\n createConsistencyToken,\n createEmptyConsistencyToken,\n} from \"../executor/util.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport { JobStatus, type JobInfo } from \"../shared/types.js\";\nimport type { IJobTracker } from \"./interfaces.js\";\n\n/**\n * In-memory implementation of IJobTracker.\n * Maintains job status in a Map for synchronous access.\n * Subscribes to operation events to update job states.\n */\nexport class InMemoryJobTracker implements IJobTracker {\n private jobs = new Map<string, JobInfo>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(private eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_type, event: JobWriteReadyEvent) => {\n this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n (_type, event: JobReadReadyEvent) => {\n this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.handleJobFailed(event);\n },\n ),\n );\n }\n\n private handleWriteReady(event: JobWriteReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.RUNNING) {\n const consistencyToken = createConsistencyToken(event.operations);\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.WRITE_READY,\n consistencyToken,\n });\n }\n }\n\n private handleReadReady(event: JobReadReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.WRITE_READY) {\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.READ_READY,\n });\n }\n }\n\n private handleJobFailed(event: JobFailedEvent): void {\n this.markFailed(\n event.jobId,\n {\n name: event.error.name,\n message: event.error.message,\n stack: event.error.stack || \"\",\n },\n event.job,\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n }\n\n registerJob(jobInfo: JobInfo): void {\n this.jobs.set(jobInfo.id, { ...jobInfo });\n }\n\n markRunning(jobId: string): void {\n const job = this.jobs.get(jobId);\n if (!job) {\n // Job not found - might have been registered elsewhere\n // Create minimal job entry\n this.jobs.set(jobId, {\n id: jobId,\n documentId: \"\",\n status: JobStatus.RUNNING,\n createdAtUtcIso: new Date().toISOString(),\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.RUNNING,\n });\n }\n\n markFailed(jobId: string, error: ErrorInfo, job?: Job): void {\n const existing = this.jobs.get(jobId);\n if (!existing) {\n this.jobs.set(jobId, {\n id: jobId,\n documentId: job?.documentId ?? \"\",\n status: JobStatus.FAILED,\n createdAtUtcIso: new Date().toISOString(),\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n this.jobs.set(jobId, {\n ...existing,\n status: JobStatus.FAILED,\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n });\n }\n\n getJobStatus(jobId: string): JobInfo | null {\n const job = this.jobs.get(jobId);\n return job ? { ...job } : null;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type {\n ProcessorFilter,\n ProcessorRecord,\n} from \"@powerhousedao/shared/processors\";\nimport type { PHDocumentHeader } from \"@powerhousedao/shared/document-model\";\n\nexport function isDriveDeletion(op: OperationWithContext): boolean {\n return op.operation.action.type === \"DELETE_DOCUMENT\";\n}\n\nexport function extractDriveHeader(\n op: OperationWithContext,\n): PHDocumentHeader | undefined {\n if (!op.context.resultingState) return undefined;\n\n const state = JSON.parse(op.context.resultingState) as Record<\n string,\n unknown\n >;\n return state.header as PHDocumentHeader | undefined;\n}\n\nexport function extractDeletedDocumentId(\n op: OperationWithContext,\n): string | undefined {\n const input = op.operation.action.input as { documentId?: string };\n return input.documentId ?? op.context.documentId;\n}\n\nexport function createMinimalDriveHeader(\n driveId: string,\n documentType: string,\n): PHDocumentHeader {\n return {\n id: driveId,\n documentType,\n sig: {\n publicKey: {},\n nonce: \"\",\n },\n slug: \"\",\n name: \"\",\n branch: \"main\",\n revision: {},\n createdAtUtcIso: new Date().toISOString(),\n lastModifiedAtUtcIso: new Date().toISOString(),\n };\n}\n\nexport function matchesFilter(\n op: OperationWithContext,\n filter: ProcessorFilter,\n): boolean {\n if (filter.documentType && filter.documentType.length > 0) {\n if (!filter.documentType.includes(op.context.documentType)) {\n return false;\n }\n }\n\n if (filter.scope && filter.scope.length > 0) {\n if (!filter.scope.includes(op.context.scope)) {\n return false;\n }\n }\n\n if (filter.branch && filter.branch.length > 0) {\n if (!filter.branch.includes(op.context.branch)) {\n return false;\n }\n }\n\n if (filter.documentId && filter.documentId.length > 0) {\n const hasWildcard = filter.documentId.includes(\"*\");\n if (!hasWildcard && !filter.documentId.includes(op.context.documentId)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction namespaceOf(processor: object): string | undefined {\n return nonEmpty((processor as { namespace?: unknown }).namespace);\n}\n\nfunction classNameOf(processor: object): string | undefined {\n const name = processor.constructor.name;\n return name && name !== \"Object\" ? name : undefined;\n}\n\n// Cursor key of each record within its factory and drive. Legacy mode keeps\n// the array index; otherwise id, then namespace, then class name, then index.\nexport function resolveProcessorSlots(\n records: ProcessorRecord[],\n legacy: boolean,\n): string[] {\n if (legacy) return records.map((_, i) => String(i));\n\n const seen = new Map<string, number>();\n return records.map((record, i) => {\n const base =\n nonEmpty(record.id) ??\n namespaceOf(record.processor) ??\n classNameOf(record.processor) ??\n String(i);\n const count = seen.get(base) ?? 0;\n seen.set(base, count + 1);\n return count === 0 ? base : `${base}#${count}`;\n });\n}\n","import type {\n OperationWithContext,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type {\n IProcessor,\n IProcessorManager,\n ProcessorFactory,\n ProcessorRecord,\n TrackedProcessor,\n} from \"@powerhousedao/shared/processors\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { BaseReadModel } from \"../read-models/base-read-model.js\";\nimport type {\n DocumentViewDatabase,\n ProcessorCursorRow,\n} from \"../read-models/types.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport {\n createMinimalDriveHeader,\n extractDeletedDocumentId,\n extractDriveHeader,\n isDriveDeletion,\n matchesFilter,\n resolveProcessorSlots,\n} from \"./utils.js\";\n\nexport type ProcessorManagerOptions = {\n // Key cursors by array position (default). Off derives stable keys from\n // record id, namespace or class name, so reordering factories is safe.\n legacyProcessorIds?: boolean;\n};\n\n/**\n * Manages processor lifecycle based on operations.\n * Extends BaseReadModel to receive operations from ReadModelCoordinator.\n *\n * Responsibilities:\n * 1. Detect drive creation from CREATE_DOCUMENT operations\n * 2. Create processors for each drive using registered factories\n * 3. Route operations to matching processors based on filters\n * 4. Clean up processors when drives are deleted or factories are unregistered\n * 5. Track per-processor cursors for failure recovery and backfill\n */\nexport class ProcessorManager\n extends BaseReadModel\n implements IProcessorManager\n{\n private factoryRegistry: Map<string, ProcessorFactory> = new Map();\n private processorsByDrive: Map<string, TrackedProcessor[]> = new Map();\n private factoryToProcessors: Map<string, Map<string, TrackedProcessor[]>> =\n new Map();\n private knownDrives: Map<string, string> = new Map();\n private cursorCache: Map<string, ProcessorCursorRow> = new Map();\n private logger: ILogger;\n private driveContainerTypes: ReadonlySet<string>;\n private legacyProcessorIds: boolean;\n\n constructor(\n db: Kysely<DocumentViewDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n options: ProcessorManagerOptions = {},\n ) {\n super(db, operationIndex, writeCache, consistencyTracker, {\n readModelId: \"processor-manager\",\n rebuildStateOnInit: true,\n });\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\n this.legacyProcessorIds = options.legacyProcessorIds ?? true;\n }\n\n override async init(): Promise<void> {\n await super.init();\n await this.loadAllCursors();\n await this.discoverExistingDrives();\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n await this.detectAndRegisterNewDrives(items);\n await this.detectAndCleanupDeletedDrives(items);\n await this.routeOperationsToProcessors(items);\n }\n\n async registerFactory(\n identifier: string,\n factory: ProcessorFactory,\n ): Promise<void> {\n if (this.factoryRegistry.has(identifier)) {\n await this.unregisterFactory(identifier);\n }\n\n this.factoryRegistry.set(identifier, factory);\n this.factoryToProcessors.set(identifier, new Map());\n\n for (const [driveId, documentType] of this.knownDrives) {\n const driveHeader = createMinimalDriveHeader(driveId, documentType);\n await this.createProcessorsForDrive(\n driveId,\n identifier,\n factory,\n driveHeader,\n );\n }\n }\n\n async unregisterFactory(identifier: string): Promise<void> {\n const factoryProcessors = this.factoryToProcessors.get(identifier);\n if (!factoryProcessors) return;\n\n for (const [driveId, tracked] of factoryProcessors) {\n for (const t of tracked) {\n await this.safeDisconnect(t.record.processor);\n }\n\n const driveProcessors = this.processorsByDrive.get(driveId);\n if (driveProcessors) {\n const remaining = driveProcessors.filter((p) => !tracked.includes(p));\n if (remaining.length > 0) {\n this.processorsByDrive.set(driveId, remaining);\n } else {\n this.processorsByDrive.delete(driveId);\n }\n }\n }\n\n await this.deleteProcessorCursors({ factoryId: identifier });\n this.factoryToProcessors.delete(identifier);\n this.factoryRegistry.delete(identifier);\n }\n\n get(processorId: string): TrackedProcessor | undefined {\n for (const tracked of this.allTrackedProcessors()) {\n if (tracked.processorId === processorId) return tracked;\n }\n return undefined;\n }\n\n getAll(): TrackedProcessor[] {\n return Array.from(this.allTrackedProcessors());\n }\n\n private *allTrackedProcessors(): Iterable<TrackedProcessor> {\n for (const tracked of this.processorsByDrive.values()) {\n yield* tracked;\n }\n }\n\n private async detectAndRegisterNewDrives(\n operations: OperationWithContext[],\n ): Promise<void> {\n for (const op of operations) {\n if (!this.isDriveCreation(op)) continue;\n\n const driveId = op.context.documentId;\n if (this.knownDrives.has(driveId)) continue;\n\n this.knownDrives.set(driveId, op.context.documentType);\n\n const driveHeader = extractDriveHeader(op);\n if (!driveHeader) continue;\n\n for (const [identifier, factory] of this.factoryRegistry) {\n await this.createProcessorsForDrive(\n driveId,\n identifier,\n factory,\n driveHeader,\n );\n }\n }\n }\n\n private isDriveCreation(op: OperationWithContext): boolean {\n return (\n op.operation.action.type === \"CREATE_DOCUMENT\" &&\n this.driveContainerTypes.has(op.context.documentType)\n );\n }\n\n private async detectAndCleanupDeletedDrives(\n operations: OperationWithContext[],\n ): Promise<void> {\n for (const op of operations) {\n if (!isDriveDeletion(op)) continue;\n\n const driveId = extractDeletedDocumentId(op);\n if (!driveId || !this.knownDrives.has(driveId)) continue;\n\n if (!this.isDeletedDocumentADrive(driveId)) continue;\n\n await this.cleanupDriveProcessors(driveId);\n this.knownDrives.delete(driveId);\n }\n }\n\n private async discoverExistingDrives(): Promise<void> {\n const drives = await this.db\n .selectFrom(\"DocumentSnapshot\")\n .select([\"documentId\", \"documentType\"])\n .where(\"documentType\", \"in\", [...this.driveContainerTypes])\n .where(\"isDeleted\", \"=\", false)\n .execute();\n\n for (const drive of drives) {\n this.knownDrives.set(drive.documentId, drive.documentType);\n }\n }\n\n private isDeletedDocumentADrive(documentId: string): boolean {\n return this.knownDrives.has(documentId);\n }\n\n private async createProcessorsForDrive(\n driveId: string,\n identifier: string,\n factory: ProcessorFactory,\n driveHeader: PHDocumentHeader,\n ): Promise<void> {\n let records: ProcessorRecord[];\n\n try {\n records = await factory(driveHeader);\n } catch (error) {\n this.logger.error(\n \"Factory '@FactoryId' failed for drive '@DriveId': @Error\",\n identifier,\n driveId,\n error,\n );\n return;\n }\n\n if (records.length === 0) return;\n\n const trackedList: TrackedProcessor[] = [];\n const slots = resolveProcessorSlots(records, this.legacyProcessorIds);\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i]!;\n const processorId = `${identifier}:${driveId}:${slots[i]}`;\n\n const cached = this.cursorCache.get(processorId);\n let lastOrdinal: number;\n let status: \"active\" | \"errored\";\n let lastError: string | undefined;\n let lastErrorTimestamp: Date | undefined;\n\n if (cached) {\n lastOrdinal = cached.lastOrdinal;\n status = cached.status as \"active\" | \"errored\";\n lastError = cached.lastError ?? undefined;\n lastErrorTimestamp = cached.lastErrorTimestamp ?? undefined;\n } else {\n const startFrom = record.startFrom ?? \"beginning\";\n lastOrdinal = startFrom === \"current\" ? this.lastOrdinal : 0;\n status = \"active\";\n lastError = undefined;\n lastErrorTimestamp = undefined;\n }\n\n const tracked: TrackedProcessor = {\n processorId,\n factoryId: identifier,\n driveId,\n processorIndex: i,\n record,\n lastOrdinal,\n status,\n lastError,\n lastErrorTimestamp,\n retry: () => this.retryProcessor(tracked),\n };\n\n trackedList.push(tracked);\n\n await this.saveProcessorCursor(tracked);\n }\n\n // Cursors this factory no longer produces for the drive are orphans.\n const liveIds = new Set(trackedList.map((t) => t.processorId));\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", identifier)\n .where(\"driveId\", \"=\", driveId)\n .where(\"processorId\", \"not in\", [...liveIds])\n .execute();\n\n for (const [id, row] of this.cursorCache) {\n if (\n row.factoryId === identifier &&\n row.driveId === driveId &&\n !liveIds.has(id)\n ) {\n this.cursorCache.delete(id);\n }\n }\n\n const factoryProcessors = this.factoryToProcessors.get(identifier);\n if (factoryProcessors) {\n factoryProcessors.set(driveId, trackedList);\n }\n\n const existingDriveProcessors = this.processorsByDrive.get(driveId) ?? [];\n this.processorsByDrive.set(driveId, [\n ...existingDriveProcessors,\n ...trackedList,\n ]);\n\n for (const tracked of trackedList) {\n if (\n tracked.status === \"active\" &&\n tracked.lastOrdinal < this.lastOrdinal\n ) {\n await this.backfillProcessor(tracked);\n }\n }\n }\n\n private async backfillProcessor(tracked: TrackedProcessor): Promise<void> {\n let page = await this.operationIndex.getSinceOrdinal(tracked.lastOrdinal);\n\n while (page.results.length > 0) {\n const matching = page.results.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n\n if (matching.length > 0) {\n try {\n await tracked.record.processor.onOperations(matching);\n } catch (error) {\n tracked.status = \"errored\";\n tracked.lastError =\n error instanceof Error ? error.message : String(error);\n tracked.lastErrorTimestamp = new Date();\n await this.safeSaveProcessorCursor(tracked);\n this.logger.error(\n \"Processor '@ProcessorId' failed during backfill at ordinal @Ordinal: @Error\",\n tracked.processorId,\n tracked.lastOrdinal,\n error,\n );\n return;\n }\n }\n\n const lastResult = page.results[page.results.length - 1]!;\n tracked.lastOrdinal = lastResult.context.ordinal;\n await this.safeSaveProcessorCursor(tracked);\n\n if (!page.next) break;\n page = await page.next();\n }\n }\n\n private async retryProcessor(tracked: TrackedProcessor): Promise<void> {\n if (tracked.status !== \"errored\") return;\n tracked.status = \"active\";\n tracked.lastError = undefined;\n tracked.lastErrorTimestamp = undefined;\n await this.saveProcessorCursor(tracked);\n await this.backfillProcessor(tracked);\n }\n\n private async cleanupDriveProcessors(driveId: string): Promise<void> {\n const processors = this.processorsByDrive.get(driveId);\n if (!processors) return;\n\n for (const tracked of processors) {\n await this.safeDisconnect(tracked.record.processor);\n }\n\n this.processorsByDrive.delete(driveId);\n\n for (const factoryProcessors of this.factoryToProcessors.values()) {\n factoryProcessors.delete(driveId);\n }\n\n await this.deleteProcessorCursors({ driveId });\n }\n\n private async safeDisconnect(processor: IProcessor): Promise<void> {\n try {\n await processor.onDisconnect();\n } catch (error) {\n this.logger.error(\"Error disconnecting processor: @Error\", error);\n }\n }\n\n private async routeOperationsToProcessors(\n operations: OperationWithContext[],\n ): Promise<void> {\n let maxOrdinal = 0;\n for (const op of operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n const allTracked = Array.from(this.allTrackedProcessors());\n\n await Promise.all(\n allTracked.map(async (tracked) => {\n if (tracked.status !== \"active\") return;\n\n const unseen = operations.filter(\n (op) => op.context.ordinal > tracked.lastOrdinal,\n );\n const matching = unseen.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n\n if (matching.length > 0) {\n try {\n await tracked.record.processor.onOperations(matching);\n } catch (error) {\n tracked.status = \"errored\";\n tracked.lastError =\n error instanceof Error ? error.message : String(error);\n tracked.lastErrorTimestamp = new Date();\n await this.safeSaveProcessorCursor(tracked);\n this.logger.error(\n \"Processor '@ProcessorId' failed at ordinal @Ordinal: @Error\",\n tracked.processorId,\n tracked.lastOrdinal,\n error,\n );\n return;\n }\n }\n\n tracked.lastOrdinal = maxOrdinal;\n await this.safeSaveProcessorCursor(tracked);\n }),\n );\n }\n\n private async loadAllCursors(): Promise<void> {\n const rows = await this.db\n .selectFrom(\"ProcessorCursor\")\n .selectAll()\n .execute();\n\n for (const row of rows) {\n this.cursorCache.set(row.processorId, row);\n }\n }\n\n private async safeSaveProcessorCursor(\n tracked: TrackedProcessor,\n ): Promise<void> {\n try {\n await this.saveProcessorCursor(tracked);\n } catch (error) {\n this.logger.error(\n \"Failed to persist cursor for '@ProcessorId': @Error\",\n tracked.processorId,\n error,\n );\n }\n }\n\n private async saveProcessorCursor(tracked: TrackedProcessor): Promise<void> {\n await this.db\n .insertInto(\"ProcessorCursor\")\n .values({\n processorId: tracked.processorId,\n factoryId: tracked.factoryId,\n driveId: tracked.driveId,\n processorIndex: tracked.processorIndex,\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n updatedAt: new Date(),\n })\n .onConflict((oc) =>\n oc.column(\"processorId\").doUpdateSet({\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n updatedAt: new Date(),\n }),\n )\n .execute();\n\n this.cursorCache.set(tracked.processorId, {\n processorId: tracked.processorId,\n factoryId: tracked.factoryId,\n driveId: tracked.driveId,\n processorIndex: tracked.processorIndex,\n lastOrdinal: tracked.lastOrdinal,\n status: tracked.status,\n lastError: tracked.lastError ?? null,\n lastErrorTimestamp: tracked.lastErrorTimestamp ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n }\n\n private async deleteProcessorCursors(\n filter: { factoryId: string } | { driveId: string },\n ): Promise<void> {\n if (\"factoryId\" in filter) {\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", filter.factoryId)\n .execute();\n for (const [id, row] of this.cursorCache) {\n if (row.factoryId === filter.factoryId) this.cursorCache.delete(id);\n }\n } else {\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"driveId\", \"=\", filter.driveId)\n .execute();\n for (const [id, row] of this.cursorCache) {\n if (row.driveId === filter.driveId) this.cursorCache.delete(id);\n }\n }\n }\n}\n","import type { ErrorInfo } from \"../shared/types.js\";\nimport type { IJobExecutionHandle, Job } from \"./types.js\";\nimport { JobQueueState } from \"./types.js\";\n\n/**\n * Implementation of the IJobExecutionHandle interface\n */\nexport class JobExecutionHandle implements IJobExecutionHandle {\n private _state: JobQueueState;\n private _job: Job;\n private onStart?: () => void;\n private onComplete?: () => void;\n private onFail?: (error: ErrorInfo) => void;\n private onDefer?: () => void;\n\n private getStateName(state: JobQueueState): string {\n switch (state) {\n case JobQueueState.PREPROCESSING:\n return \"PREPROCESSING\";\n case JobQueueState.PENDING:\n return \"PENDING\";\n case JobQueueState.READY:\n return \"READY\";\n case JobQueueState.RUNNING:\n return \"RUNNING\";\n case JobQueueState.RESOLVED:\n return \"RESOLVED\";\n default:\n return `UNKNOWN`;\n }\n }\n\n constructor(\n job: Job,\n initialState: JobQueueState,\n callbacks?: {\n onStart?: () => void;\n onComplete?: () => void;\n onFail?: (error: ErrorInfo) => void;\n onDefer?: () => void;\n },\n ) {\n this._job = job;\n this._state = initialState;\n this.onStart = callbacks?.onStart;\n this.onComplete = callbacks?.onComplete;\n this.onFail = callbacks?.onFail;\n this.onDefer = callbacks?.onDefer;\n }\n\n get job(): Job {\n return this._job;\n }\n\n get state(): JobQueueState {\n return this._state;\n }\n\n start(): void {\n if (this._state !== JobQueueState.READY) {\n throw new Error(\n `Cannot start job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RUNNING;\n this.onStart?.();\n }\n\n complete(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot complete job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onComplete?.();\n }\n\n fail(error: ErrorInfo): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot fail job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onFail?.(error);\n }\n\n defer(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot defer job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onDefer?.();\n }\n}\n","import type { CreateDocumentActionInput } from \"@powerhousedao/shared/document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { IQueue } from \"./interfaces.js\";\nimport { JobExecutionHandle } from \"./job-execution-handle.js\";\nimport type {\n IJobExecutionHandle,\n Job,\n JobAvailableEvent,\n JobRoutingMeta,\n} from \"./types.js\";\nimport { JobQueueState, QueueEventTypes, RetryAccounting } from \"./types.js\";\n\n/**\n * In-memory implementation of the IQueue interface.\n * Organizes jobs by documentId, scope, and branch to ensure proper ordering.\n * Ensures serial execution per document by tracking executing jobs.\n * Implements dependency management through queue hints.\n */\nexport class InMemoryQueue implements IQueue {\n private queues = new Map<string, Job[]>();\n private jobIdToQueueKey = new Map<string, string>();\n private docIdToJobId = new Map<string, Set<string>>();\n private jobIdToDocId = new Map<string, string>();\n private completedJobs = new Set<string>();\n private jobIndex = new Map<string, Job>();\n private isBlocked = false;\n private onDrainedCallback?: () => void;\n private isPausedFlag = false;\n\n constructor(\n private eventBus: IEventBus,\n private resolver: IDocumentModelResolver,\n ) {}\n\n private toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n }\n\n /**\n * Creates a unique key for a document/scope/branch combination\n */\n private createQueueKey(\n documentId: string,\n scope: string,\n branch: string,\n ): string {\n return `${documentId}:${scope}:${branch}`;\n }\n\n /**\n * Gets or creates a queue for the given key\n */\n private getQueue(queueKey: string): Job[] {\n let queue = this.queues.get(queueKey);\n if (!queue) {\n queue = [];\n this.queues.set(queueKey, queue);\n }\n return queue;\n }\n\n /**\n * Check if a document has any jobs currently executing\n */\n private isDocumentExecuting(documentId: string): boolean {\n const executingSet = this.docIdToJobId.get(documentId);\n return executingSet ? executingSet.size > 0 : false;\n }\n\n /**\n * Mark a job as executing for its document\n */\n private markJobExecuting(job: Job): void {\n let executingSet = this.docIdToJobId.get(job.documentId);\n if (!executingSet) {\n executingSet = new Set();\n this.docIdToJobId.set(job.documentId, executingSet);\n }\n executingSet.add(job.id);\n this.jobIdToDocId.set(job.id, job.documentId);\n }\n\n /**\n * Mark a job as no longer executing for its document\n */\n private markJobComplete(jobId: string, documentId: string): void {\n const executingSet = this.docIdToJobId.get(documentId);\n if (executingSet) {\n executingSet.delete(jobId);\n if (executingSet.size === 0) {\n this.docIdToJobId.delete(documentId);\n }\n }\n this.jobIdToDocId.delete(jobId);\n }\n\n /**\n * Check if all dependencies for a job have been completed\n */\n private areDependenciesMet(job: Job): boolean {\n if (job.queueHint.length === 0) {\n return true;\n }\n return job.queueHint.every((depId) => this.completedJobs.has(depId));\n }\n\n /**\n * Returns the head of the sub-queue if its dependencies are met, or null.\n *\n * The dispatcher only ever considers the head — a dep-blocked head holds\n * the rest of its sub-queue. This preserves per-(documentId, scope, branch)\n * FIFO regardless of how dependencies are authored, and makes the queue's\n * documented \"serialized per document\" invariant hold even when callers\n * omit queueHint dependencies on jobs that share a sub-queue.\n */\n private getNextJobWithMetDependencies(queue: Job[]): Job | null {\n if (queue.length === 0) {\n return null;\n }\n const head = queue[0];\n return this.areDependenciesMet(head) ? head : null;\n }\n\n private getCreateDocumentType(job: Job): string | undefined {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return (action.input as CreateDocumentActionInput).model;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return (operation.action.input as CreateDocumentActionInput).model;\n }\n }\n return undefined;\n }\n\n async enqueue(job: Job): Promise<void> {\n if (this.isBlocked) {\n throw new Error(\"Queue is blocked\");\n }\n\n const queueKey = this.createQueueKey(job.documentId, job.scope, job.branch);\n const queue = this.getQueue(queueKey);\n\n queue.push(job);\n\n // Track job location for removal and dependency resolution\n this.jobIdToQueueKey.set(job.id, queueKey);\n this.jobIndex.set(job.id, job);\n\n // Gate CREATE_DOCUMENT jobs on model availability\n const documentType = this.getCreateDocumentType(job);\n if (documentType) {\n try {\n await this.resolver.ensureModelLoaded(documentType);\n } catch {\n await this.failJob(job.id, {\n name: \"Error\",\n message: `Failed to load document model for type: ${documentType}`,\n stack: new Error().stack || \"\",\n });\n return;\n }\n }\n\n // Emit job available event\n const eventData: JobAvailableEvent = {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n };\n\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, eventData);\n }\n\n dequeue(\n documentId: string,\n scope: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<IJobExecutionHandle | null> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (!queue || queue.length === 0) {\n return Promise.resolve(null);\n }\n\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n return Promise.resolve(null);\n }\n\n // Remove job from queue\n const jobIndex = queue.indexOf(job);\n queue.splice(jobIndex, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n\n dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isPausedFlag) {\n return Promise.resolve(null);\n }\n\n // Find the first non-empty queue for a document that's not currently executing\n for (const [queueKey, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n continue; // No job with met dependencies in this queue\n }\n\n // Only dequeue if the document is not currently executing jobs\n if (!this.isDocumentExecuting(job.documentId)) {\n // Remove job from queue\n const jobIdx = queue.indexOf(job);\n queue.splice(jobIdx, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n // Keep job in jobIndex so we can retry it if needed\n\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n }\n }\n\n return Promise.resolve(null);\n }\n\n dequeueNextMatching(\n predicate: (meta: JobRoutingMeta) => boolean,\n signal?: AbortSignal,\n ): Promise<IJobExecutionHandle | null> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isPausedFlag) {\n return Promise.resolve(null);\n }\n\n for (const [queueKey, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n continue;\n }\n\n if (this.isDocumentExecuting(job.documentId)) {\n continue;\n }\n\n const meta: JobRoutingMeta = {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n };\n\n if (!predicate(meta)) {\n continue;\n }\n\n const jobIdx = queue.indexOf(job);\n queue.splice(jobIdx, 1);\n\n this.jobIdToQueueKey.delete(job.id);\n\n this.markJobExecuting(job);\n\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n }\n\n return Promise.resolve(null);\n }\n\n size(documentId: string, scope: string, branch: string): Promise<number> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n return Promise.resolve(queue ? queue.length : 0);\n }\n\n totalSize(): Promise<number> {\n let total = 0;\n for (const queue of this.queues.values()) {\n total += queue.length;\n }\n return Promise.resolve(total);\n }\n\n remove(jobId: string): Promise<boolean> {\n const queueKey = this.jobIdToQueueKey.get(jobId);\n if (!queueKey) {\n return Promise.resolve(false);\n }\n\n const queue = this.queues.get(queueKey);\n if (!queue) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n const jobIdx = queue.findIndex((job) => job.id === jobId);\n if (jobIdx === -1) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n // Remove job from queue\n queue.splice(jobIdx, 1);\n\n // Remove from job index\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve(true);\n }\n\n clear(documentId: string, scope: string, branch: string): Promise<void> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (queue) {\n // Remove all jobs from the job index\n for (const job of queue) {\n this.jobIdToQueueKey.delete(job.id);\n this.jobIndex.delete(job.id);\n }\n\n // Remove the queue\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve();\n }\n\n clearAll(): Promise<void> {\n // Clear all job indices\n this.jobIdToQueueKey.clear();\n this.jobIndex.clear();\n this.completedJobs.clear();\n\n this.queues.clear();\n\n return Promise.resolve();\n }\n\n hasJobs(): Promise<boolean> {\n return Promise.resolve(\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0),\n );\n }\n\n async completeJob(jobId: string): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n // Track the job as completed for dependency resolution\n this.completedJobs.add(jobId);\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // For in-memory queue, completing just removes the job\n // In a persistent queue, this would update the job status\n await this.remove(jobId);\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n async failJob(jobId: string, error?: ErrorInfo): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n const job = this.jobIndex.get(jobId);\n if (job) {\n job.lastError = error;\n if (error) {\n job.errorHistory.push(error);\n }\n }\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // Completed here means finished, not succeeded: this is the same set\n // areDependenciesMet reads, and it cannot tell the two apart. A failed job\n // therefore releases its dependents, which go on to run against whatever\n // state the failure left behind. That is what a batch's dependsOn edges\n // mean today - ordering, with no guarantee the job before them worked.\n this.completedJobs.add(jobId);\n\n // For in-memory queue, failing just removes the job\n // In a persistent queue, this would update the job status and store the error\n await this.remove(jobId);\n\n // Emit JOB_FAILED so subscribers (sync manager, job tracker, etc.) can react.\n // The name is preserved because consumers classify failures by it.\n const emittedError = new Error(error?.message ?? \"Job failed\");\n if (error?.name) {\n emittedError.name = error.name;\n }\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId,\n error: emittedError,\n job,\n })\n .catch(() => {});\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n deferJob(jobId: string): void {\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n this.jobIndex.delete(jobId);\n }\n\n async retryJob(\n jobId: string,\n error?: ErrorInfo,\n accounting: RetryAccounting = RetryAccounting.CountAgainstLimit,\n ): Promise<void> {\n // Get the job from the index (it might be executing, not in queue)\n const job = this.jobIndex.get(jobId);\n if (!job) {\n return;\n }\n\n job.lastError = error;\n\n // Mark it as no longer executing if it was\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n\n // Remove from indices\n this.jobIndex.delete(jobId);\n this.jobIdToQueueKey.delete(jobId);\n\n // Add error to history\n if (error) {\n job.errorHistory.push(error);\n }\n\n const retryCount = job.retryCount || 0;\n const updatedJob: Job = {\n ...job,\n retryCount:\n accounting === RetryAccounting.CountAgainstLimit\n ? retryCount + 1\n : retryCount,\n lastError: error,\n };\n\n // Re-enqueue with updated retry count\n await this.enqueue(updatedJob);\n }\n\n /**\n * Check if the queue is drained and call the callback if it is\n */\n private checkDrained(): void {\n if (this.isDrained && this.onDrainedCallback) {\n const callback = this.onDrainedCallback;\n this.onDrainedCallback = undefined;\n callback();\n }\n }\n\n /**\n * Returns true if and only if all jobs have been resolved.\n */\n get isDrained(): boolean {\n // Queue is drained if there are no pending jobs and no executing jobs\n const hasPendingJobs =\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0);\n const hasExecutingJobs =\n this.docIdToJobId.size > 0 &&\n Array.from(this.docIdToJobId.values()).some((set) => set.size > 0);\n\n return !hasPendingJobs && !hasExecutingJobs;\n }\n\n /**\n * Blocks the queue from accepting new jobs.\n * @param onDrained - Optional callback to call when the queue is drained\n */\n block(onDrained?: () => void): void {\n this.isBlocked = true;\n this.onDrainedCallback = onDrained;\n\n // Check if already drained\n this.checkDrained();\n }\n\n /**\n * Unblocks the queue from accepting new jobs.\n */\n unblock(): void {\n this.isBlocked = false;\n this.onDrainedCallback = undefined;\n }\n\n /**\n * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.\n */\n pause(): void {\n this.isPausedFlag = true;\n }\n\n /**\n * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.\n */\n async resume(): Promise<void> {\n this.isPausedFlag = false;\n // Emit JOB_AVAILABLE for each queue with pending jobs to wake up the executor manager\n for (const [, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n const job = queue[0];\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n });\n }\n }\n }\n\n /**\n * Returns whether job dequeuing is paused.\n */\n get paused(): boolean {\n return this.isPausedFlag;\n }\n\n /**\n * Returns all pending jobs across all queues.\n */\n getPendingJobs(): Job[] {\n const jobs: Job[] = [];\n for (const queue of this.queues.values()) {\n jobs.push(...queue);\n }\n return jobs;\n }\n\n /**\n * Returns a map of document IDs to sets of executing job IDs.\n */\n getExecutingJobIds(): Map<string, Set<string>> {\n return new Map(\n Array.from(this.docIdToJobId.entries()).map(([k, v]) => [k, new Set(v)]),\n );\n }\n\n /**\n * Returns a job by ID from the job index.\n */\n getJob(jobId: string): Job | undefined {\n return this.jobIndex.get(jobId);\n }\n}\n","/**\n * Runtime companion to {@link BuiltInReadModelKind}, which lives in\n * `protocol.ts` — a types-only module that must not emit runtime code.\n *\n * @see Sharded projection workers sub-feature brief\n * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)\n */\n\nimport type { BuiltInReadModelKind } from \"./protocol.js\";\n\n/**\n * Every {@link BuiltInReadModelKind}, at runtime. Derived from a total\n * record so adding a kind to the union without adding it here is a compile\n * error: under projection sharding a kind missing from both `preReadyKinds`\n * and `postReadyKinds` is indexed by nobody — the host's own copies never\n * index an operation — so a gap here reads as silently stale data.\n */\nexport const BUILT_IN_READ_MODEL_KINDS = Object.keys({\n \"document-view\": true,\n \"document-indexer\": true,\n} satisfies Record<BuiltInReadModelKind, true>) as BuiltInReadModelKind[];\n","import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport type { ModelManifestEntry } from \"../executor/worker/protocol.js\";\n\n/** An importable file holding one or more document-model exports. */\nexport type FileModelSource = { filePath: string; exportName?: string };\n\n/** An importable package specifier holding one or more document-model exports. */\nexport type PackageModelSource = {\n packageName: string;\n subpath?: string;\n exportName?: string;\n};\n\n/**\n * A source of document models: a live module, an importable file, or an\n * importable package. File and package sources can cross a worker-thread\n * boundary (workers re-import them); a live module cannot.\n */\nexport type DocumentModelSource =\n | DocumentModelModule<any>\n | FileModelSource\n | PackageModelSource;\n\nexport type ResolvedModelSources = {\n /** Every resolved module, deduped by `documentType@version`, first wins. */\n modules: DocumentModelModule<any>[];\n /** One entry per model reachable through an importable source. */\n manifest: ModelManifestEntry[];\n /** `documentType@version` keys reachable only through live modules. */\n moduleOnlyKeys: string[];\n};\n\nexport function modelSourceKey(module: DocumentModelModule<any>): string {\n return `${module.documentModel.global.id}@${module.version ?? 1}`;\n}\n\nexport function isDocumentModelModule(\n value: unknown,\n): value is DocumentModelModule<any> {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as {\n documentModel?: { global?: { id?: unknown } };\n reducer?: unknown;\n };\n return (\n typeof candidate.reducer === \"function\" &&\n typeof candidate.documentModel?.global?.id === \"string\"\n );\n}\n\ntype ResolvedEntry = {\n module: DocumentModelModule<any>;\n spec: ModelManifestEntry[\"spec\"] | null;\n};\n\n/**\n * Resolves every source to live modules by importing file/package sources\n * host-side and scanning their exports, so the host registry and the worker\n * manifest are derived from one list and cannot diverge.\n */\nexport async function resolveModelSources(\n sources: DocumentModelSource[],\n): Promise<ResolvedModelSources> {\n const resolved = new Map<string, ResolvedEntry>();\n\n for (const source of sources) {\n if (isDocumentModelModule(source)) {\n addEntry(resolved, { module: source, spec: null });\n continue;\n }\n for (const entry of await resolveImportableSource(source)) {\n addEntry(resolved, entry);\n }\n }\n\n const modules: DocumentModelModule<any>[] = [];\n const manifest: ModelManifestEntry[] = [];\n const moduleOnlyKeys: string[] = [];\n for (const [key, entry] of resolved) {\n modules.push(entry.module);\n if (entry.spec) {\n manifest.push({\n documentType: entry.module.documentModel.global.id,\n version: String(entry.module.version ?? 1),\n spec: entry.spec,\n });\n } else {\n moduleOnlyKeys.push(key);\n }\n }\n return { modules, manifest, moduleOnlyKeys };\n}\n\n// First occurrence wins for the module instance; importability is an OR, so\n// a later importable occurrence backfills the spec of a live-module one.\nfunction addEntry(map: Map<string, ResolvedEntry>, entry: ResolvedEntry): void {\n const key = modelSourceKey(entry.module);\n const existing = map.get(key);\n if (!existing) {\n map.set(key, entry);\n return;\n }\n if (!existing.spec && entry.spec) {\n existing.spec = entry.spec;\n }\n}\n\nasync function resolveImportableSource(\n source: FileModelSource | PackageModelSource,\n): Promise<ResolvedEntry[]> {\n const isFile = \"filePath\" in source;\n const specifier = isFile\n ? new URL(`file://${source.filePath}`).href\n : source.subpath\n ? `${source.packageName}/${source.subpath}`\n : source.packageName;\n const moduleRef = isFile\n ? { filePath: source.filePath }\n : { packageName: specifier };\n\n let moduleNs: Record<string, unknown>;\n try {\n moduleNs = (await import(/* @vite-ignore */ specifier)) as Record<\n string,\n unknown\n >;\n } catch (error) {\n throw new Error(\n `Failed to import document-model source \"${describeSource(source)}\"`,\n { cause: error },\n );\n }\n\n if (source.exportName !== undefined) {\n const value = moduleNs[source.exportName];\n if (!isDocumentModelModule(value)) {\n throw new Error(\n `Export \"${source.exportName}\" of document-model source \"${describeSource(source)}\" is not a DocumentModelModule`,\n );\n }\n return [\n {\n module: value,\n spec: { module: { ...moduleRef, exportName: source.exportName } },\n },\n ];\n }\n\n const entries: ResolvedEntry[] = [];\n for (const [exportName, value] of Object.entries(moduleNs)) {\n if (isDocumentModelModule(value)) {\n entries.push({\n module: value,\n spec: { module: { ...moduleRef, exportName } },\n });\n }\n }\n if (entries.length === 0) {\n throw new Error(\n `Document-model source \"${describeSource(source)}\" has no DocumentModelModule exports`,\n );\n }\n return entries;\n}\n\nfunction describeSource(source: FileModelSource | PackageModelSource): string {\n return \"filePath\" in source\n ? source.filePath\n : source.subpath\n ? `${source.packageName}/${source.subpath}`\n : source.packageName;\n}\n","import { childLogger } from \"document-model\";\nimport { resolveModelSources } from \"../core/model-sources.js\";\nimport type { ModelManifestEntry } from \"../executor/worker/protocol.js\";\nimport { DuplicateModuleError, ModuleNotFoundError } from \"./errors.js\";\nimport type {\n IDocumentModelLoader,\n IDocumentModelRegistry,\n} from \"./interfaces.js\";\n\nexport interface IDocumentModelResolver {\n ensureModelLoaded(documentType: string): Promise<void>;\n}\n\n/**\n * Post-success hook called after the resolver registers a newly loaded\n * model on the parent's registry. Used to broadcast `load-model` to the\n * worker pool so workers can register the same model locally.\n */\nexport type ModelLoadedBroadcastHook = (\n entry: ModelManifestEntry,\n) => Promise<void>;\n\n/**\n * Encapsulates the logic for resolving document model modules on demand.\n * Shared between the queue (CREATE_DOCUMENT gate) and the executor manager\n * (post-failure recovery) so that both paths use the same deduplication\n * and failure-caching state.\n */\nexport class DocumentModelResolver implements IDocumentModelResolver {\n private loadingModels = new Map<string, Promise<void>>();\n private failedModelTypes = new Set<string>();\n private broadcastHook: ModelLoadedBroadcastHook | null = null;\n private modelLoadedHook: ((documentType: string) => Promise<void>) | null =\n null;\n private readonly logger = childLogger([\"reactor\", \"document-model-resolver\"]);\n\n constructor(\n private registry: IDocumentModelRegistry,\n private loader: IDocumentModelLoader,\n ) {}\n\n /**\n * Install a post-success hook called after the resolver registers a\n * newly loaded model. ReactorBuilder uses this to wire the worker-pool\n * `load-model` broadcast without touching the resolver's constructor.\n */\n setBroadcastHook(hook: ModelLoadedBroadcastHook): void {\n this.broadcastHook = hook;\n }\n\n /**\n * Post-success hook called with the document type after a model loads, so\n * peers (tabs) can load the same type via the event bus.\n */\n setModelLoadedHook(hook: (documentType: string) => Promise<void>): void {\n this.modelLoadedHook = hook;\n }\n\n async ensureModelLoaded(documentType: string): Promise<void> {\n try {\n this.registry.getModule(documentType);\n return;\n } catch (error) {\n if (!ModuleNotFoundError.isError(error)) {\n throw error;\n }\n }\n\n if (this.failedModelTypes.has(documentType)) {\n throw new Error(\n `Document model type previously failed to load: ${documentType}`,\n );\n }\n\n const existing = this.loadingModels.get(documentType);\n if (existing) {\n return existing;\n }\n\n const loadPromise = (async () => {\n try {\n await this.loadRegisterAndBroadcast(documentType);\n } catch (error) {\n this.failedModelTypes.add(documentType);\n throw error;\n } finally {\n this.loadingModels.delete(documentType);\n }\n await this.notifyPeersModelLoaded(documentType);\n })();\n\n this.loadingModels.set(documentType, loadPromise);\n return loadPromise;\n }\n\n private async loadRegisterAndBroadcast(documentType: string): Promise<void> {\n const source = await this.loader.load(documentType);\n const resolved = await resolveModelSources([source]);\n const hasRequested = resolved.modules.some(\n (module) => module.documentModel.global.id === documentType,\n );\n if (!hasRequested) {\n throw new Error(\n `Loader source resolved no module for document type: ${documentType}`,\n );\n }\n const results = this.registry.registerModules(...resolved.modules);\n for (const result of results) {\n if (\n result.status === \"error\" &&\n !DuplicateModuleError.isError(result.error)\n ) {\n throw result.error as Error;\n }\n }\n // Importable sources carry manifest entries; live modules do not, so a\n // module source registers host-side only and workers never hear of it.\n if (this.broadcastHook) {\n for (const entry of resolved.manifest) {\n await this.broadcastHook(entry);\n }\n }\n }\n\n // Best-effort peer notification; its failure must not fail the registered load.\n private async notifyPeersModelLoaded(documentType: string): Promise<void> {\n if (!this.modelLoadedHook) {\n return;\n }\n try {\n await this.modelLoadedHook(documentType);\n } catch (error) {\n this.logger.warn(`MODEL_LOADED hook failed: ${documentType}`, error);\n }\n }\n}\n\n/**\n * No-op resolver used when no document model loader is configured.\n * Checks the registry for the model and returns if found; throws if not.\n * Since there is no loader, missing models cannot be recovered.\n */\nexport class NullDocumentModelResolver implements IDocumentModelResolver {\n constructor(private registry?: IDocumentModelRegistry) {}\n\n ensureModelLoaded(documentType: string): Promise<void> {\n if (this.registry) {\n try {\n this.registry.getModule(documentType);\n return Promise.resolve();\n } catch {\n // fall through to throw below\n }\n }\n\n return Promise.reject(new ModuleNotFoundError(documentType));\n }\n}\n","import type {\n ISubscriptionErrorHandler,\n SubscriptionErrorContext,\n} from \"./types.js\";\n\n/**\n * Default error handler that re-throws subscription errors.\n * This ensures that errors are not silently swallowed.\n */\nexport class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {\n handleError(error: unknown, context: SubscriptionErrorContext): void {\n const errorMessage = `Subscription error in ${context.eventType} (${context.subscriptionId})`;\n\n if (error instanceof Error) {\n // Preserve the original error with additional context\n const enhancedError = new Error(`${errorMessage}: ${error.message}`);\n enhancedError.cause = error;\n enhancedError.stack = error.stack;\n throw enhancedError;\n } else {\n // Handle non-Error objects\n throw new Error(`${errorMessage}: ${String(error)}`);\n }\n }\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport type {\n PagedResults,\n RelationshipChangeType,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n IReactorSubscriptionManager,\n ISubscriptionErrorHandler,\n} from \"./types.js\";\n\ntype DocumentCreatedCallback = (result: PagedResults<string>) => void;\ntype DocumentDeletedCallback = (documentIds: string[]) => void;\ntype DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;\ntype RelationshipChangedCallback = (\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n) => void;\n\ntype Subscription<T> = {\n id: string;\n callback: T;\n search?: SearchFilter;\n view?: ViewFilter;\n};\n\nexport class ReactorSubscriptionManager implements IReactorSubscriptionManager {\n private createdSubscriptions = new Map<\n string,\n Subscription<DocumentCreatedCallback>\n >();\n private deletedSubscriptions = new Map<\n string,\n Subscription<DocumentDeletedCallback>\n >();\n private updatedSubscriptions = new Map<\n string,\n Subscription<DocumentStateUpdatedCallback>\n >();\n private relationshipSubscriptions = new Map<\n string,\n Subscription<RelationshipChangedCallback>\n >();\n\n private subscriptionCounter = 0;\n private errorHandler: ISubscriptionErrorHandler;\n\n constructor(errorHandler: ISubscriptionErrorHandler) {\n this.errorHandler = errorHandler;\n }\n\n onDocumentCreated(\n callback: DocumentCreatedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `created-${++this.subscriptionCounter}`;\n this.createdSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.createdSubscriptions.delete(id);\n };\n }\n\n onDocumentDeleted(\n callback: DocumentDeletedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `deleted-${++this.subscriptionCounter}`;\n this.deletedSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.deletedSubscriptions.delete(id);\n };\n }\n\n onDocumentStateUpdated(\n callback: DocumentStateUpdatedCallback,\n search?: SearchFilter,\n view?: ViewFilter,\n ): () => void {\n const id = `updated-${++this.subscriptionCounter}`;\n this.updatedSubscriptions.set(id, { id, callback, search, view });\n\n return () => {\n this.updatedSubscriptions.delete(id);\n };\n }\n\n onRelationshipChanged(\n callback: RelationshipChangedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `relationship-${++this.subscriptionCounter}`;\n this.relationshipSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.relationshipSubscriptions.delete(id);\n };\n }\n\n /**\n * Notify subscribers about created documents\n */\n notifyDocumentsCreated(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): void {\n const result: PagedResults<string> = {\n results: documentIds,\n options: { cursor: \"\", limit: documentIds.length },\n };\n\n for (const subscription of this.createdSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredIds,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"created\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about deleted documents\n */\n notifyDocumentsDeleted(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): void {\n for (const subscription of this.deletedSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n subscription.callback(filteredIds);\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"deleted\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about updated documents\n */\n notifyDocumentsUpdated(documents: PHDocument[]): void {\n const result: PagedResults<PHDocument> = {\n results: documents,\n options: { cursor: \"\", limit: documents.length },\n };\n\n for (const subscription of this.updatedSubscriptions.values()) {\n const filteredDocs = this.filterDocuments(documents, subscription.search);\n\n if (filteredDocs.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredDocs,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"updated\",\n subscriptionId: subscription.id,\n eventData: filteredDocs,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about relationship changes\n */\n notifyRelationshipChanged(\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n childType?: string,\n ): void {\n for (const subscription of this.relationshipSubscriptions.values()) {\n if (\n this.matchesRelationshipFilter(\n parentId,\n childId,\n childType,\n subscription.search,\n )\n ) {\n try {\n subscription.callback(parentId, childId, changeType);\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"relationshipChanged\",\n subscriptionId: subscription.id,\n eventData: { parentId, childId, changeType },\n });\n }\n }\n }\n }\n\n /**\n * Clear all subscriptions\n */\n clearAll(): void {\n this.createdSubscriptions.clear();\n this.deletedSubscriptions.clear();\n this.updatedSubscriptions.clear();\n this.relationshipSubscriptions.clear();\n }\n\n private filterDocumentIds(\n documentIds: string[],\n search?: SearchFilter,\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): string[] {\n if (!search) return documentIds;\n\n return documentIds.filter((id) => {\n if (search.ids && !search.ids.includes(id)) return false;\n\n if (search.type && documentTypes) {\n const docType = documentTypes.get(id);\n if (docType !== search.type) return false;\n }\n\n if (search.parentId && parentIds) {\n const parentId = parentIds.get(id);\n if (parentId !== search.parentId) return false;\n }\n\n return true;\n });\n }\n\n private filterDocuments(\n documents: PHDocument[],\n search?: SearchFilter,\n ): PHDocument[] {\n if (!search) return documents;\n\n return documents.filter((doc) => {\n if (search.ids && !search.ids.includes(doc.header.id)) return false;\n if (search.type && doc.header.documentType !== search.type) return false;\n if (search.slugs && !search.slugs.includes(doc.header.slug)) return false;\n\n return true;\n });\n }\n\n private matchesRelationshipFilter(\n parentId: string,\n childId: string,\n childType?: string,\n search?: SearchFilter,\n ): boolean {\n if (!search) return true;\n\n if (search.parentId && parentId !== search.parentId) return false;\n if (search.ids && !search.ids.includes(childId)) return false;\n if (search.type && childType && childType !== search.type) return false;\n\n return true;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { IReadModel } from \"../read-models/interfaces.js\";\nimport { RelationshipChangeType } from \"../shared/types.js\";\nimport type { IDocumentView } from \"../storage/interfaces.js\";\nimport type { ReactorSubscriptionManager } from \"./react-subscription-manager.js\";\n\n/**\n * A read model that notifies the subscription manager when operations are processed.\n * This bridges the gap between operation processing and subscription callbacks.\n *\n * Must be processed AFTER other read models have completed and AFTER READ_READY\n * is emitted, so that reactor.get() returns fresh data when callbacks fire.\n */\nexport class SubscriptionNotificationReadModel implements IReadModel {\n readonly name = \"subscription-notification\";\n\n constructor(\n private subscriptionManager: ReactorSubscriptionManager,\n private documentView?: IDocumentView,\n ) {}\n\n async indexOperations(operations: OperationWithContext[]): Promise<void> {\n if (operations.length === 0) return;\n\n const created: string[] = [];\n const deleted: string[] = [];\n const updatedIds = new Set<string>();\n const documentTypes = new Map<string, string>();\n const parentIds = new Map<string, string | null>();\n\n for (const item of operations) {\n const { operation, context } = item;\n const actionType = operation.action.type;\n\n documentTypes.set(context.documentId, context.documentType);\n\n if (actionType === \"CREATE_DOCUMENT\") {\n created.push(context.documentId);\n } else if (actionType === \"DELETE_DOCUMENT\") {\n const input = operation.action.input as { documentId?: string };\n const deletedId = input.documentId ?? context.documentId;\n deleted.push(deletedId);\n } else if (actionType === \"ADD_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Added,\n input.childType,\n );\n } else if (actionType === \"REMOVE_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Removed,\n input.childType,\n );\n } else {\n if (!created.includes(context.documentId)) {\n updatedIds.add(context.documentId);\n }\n }\n }\n\n if (created.length > 0) {\n this.subscriptionManager.notifyDocumentsCreated(\n created,\n documentTypes,\n parentIds,\n );\n }\n\n if (deleted.length > 0) {\n this.subscriptionManager.notifyDocumentsDeleted(\n deleted,\n documentTypes,\n parentIds,\n );\n }\n\n if (updatedIds.size > 0 && this.documentView) {\n const documents = await Promise.all(\n Array.from(updatedIds).map((id) => this.documentView!.get(id)),\n );\n this.subscriptionManager.notifyDocumentsUpdated(documents);\n }\n }\n}\n","import {\n groupDocumentType,\n groupMembershipActionTypes,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { JobWriteReadyEvent, Unsubscribe } from \"../events/types.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport { buildSingleJobMeta } from \"./utils.js\";\n\n/**\n * Watches committed writes for group membership changes and enqueues a\n * re-evaluation job for every document whose auth history references the\n * changed group, found through the reverse direction of the group-reference\n * relation. Each affected document is re-judged in its own job, so the work\n * runs under that document's execution slot rather than the group's.\n *\n * The job carries the earliest changed membership timestamp; the executor\n * skips the pass when everything the document holds sorts before it, which\n * keeps the common case (a membership write later than all history) free.\n */\nexport class GroupReevaluationTrigger {\n private unsubscribe?: Unsubscribe;\n\n constructor(\n private logger: ILogger,\n private eventBus: IEventBus,\n private queue: IQueue,\n private operationIndex: IOperationIndex,\n ) {}\n\n startup(): void {\n this.unsubscribe = this.eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event) => this.onWriteReady(event),\n );\n }\n\n shutdown(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n }\n\n private async onWriteReady(event: JobWriteReadyEvent): Promise<void> {\n // groupId -> earliest membership-change timestamp in this event\n const changed = new Map<string, string>();\n for (const { operation, context } of event.operations) {\n if (\n context.documentType !== groupDocumentType ||\n context.scope !== \"global\" ||\n !(groupMembershipActionTypes as readonly string[]).includes(\n operation.action.type,\n )\n ) {\n continue;\n }\n const existing = changed.get(context.documentId);\n if (\n existing === undefined ||\n Date.parse(operation.timestampUtcMs) < Date.parse(existing)\n ) {\n changed.set(context.documentId, operation.timestampUtcMs);\n }\n }\n if (changed.size === 0) {\n return;\n }\n\n // One job per affected document, at the earliest trigger among its groups.\n const affected = new Map<string, string>();\n for (const [groupId, timestamp] of changed) {\n let referencers: string[];\n try {\n referencers = await this.operationIndex.getGroupReferencers(groupId);\n } catch (error) {\n this.logger.error(\n \"Failed to resolve referencers of group @groupId: @error\",\n groupId,\n error,\n );\n continue;\n }\n for (const documentId of referencers) {\n const existing = affected.get(documentId);\n if (\n existing === undefined ||\n Date.parse(timestamp) < Date.parse(existing)\n ) {\n affected.set(documentId, timestamp);\n }\n }\n }\n\n for (const [documentId, timestamp] of affected) {\n const jobId = uuidv4();\n const job: Job = {\n id: jobId,\n kind: \"reevaluation\",\n documentId,\n scope: \"global\",\n branch: \"main\",\n actions: [],\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: {\n ...buildSingleJobMeta(jobId),\n triggerTimestampUtcMs: timestamp,\n },\n };\n try {\n await this.queue.enqueue(job);\n } catch (error) {\n this.logger.error(\n \"Failed to enqueue re-evaluation of @documentId: @error\",\n documentId,\n error,\n );\n }\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { DriveCollectionId } from \"../cache/operation-index-types.js\";\n\nexport enum ChannelScheme {\n CONNECT = \"connect\",\n SWITCHBOARD = \"switchboard\",\n}\n\n/**\n * Dynamic JWT token handler for generating authentication tokens per-request.\n * Called with the target URL to enable audience-specific tokens (aud claim).\n * Returns undefined if no authentication is available (e.g., user not logged in).\n */\nexport type JwtHandler = (url: string) => Promise<string | undefined>;\n\n/**\n * Controls how a remote drives its polling schedule.\n * - `Auto` (default): the channel runs the configured interval timer in the background.\n * - `Manual`: the channel registers and connects but never ticks on its own.\n * Callers must invoke ISyncManager.triggerPull(name) to fetch.\n */\nexport enum PollBehavior {\n Auto = \"auto\",\n Manual = \"manual\",\n}\n\nexport type RemoteOptions = {\n /**\n * Stringified UTC timestamp (ms). When set and not `\"0\"`, outbox operations older\n * than this value are excluded. `\"0\"` and `undefined` are equivalent and disable\n * the filter, syncing from the beginning of history.\n */\n sinceTimestampUtcMs?: string;\n /**\n * Polling cadence for this remote. Defaults to `PollBehavior.Auto` when omitted.\n */\n pollBehavior?: PollBehavior;\n /**\n * The address this channel belongs to, once one has claimed it.\n *\n * Undefined is unbound and adoptable: a channel created anonymously, or one\n * that predates binding. The first authenticated subject to poll or touch it\n * takes it, and from then on it answers to that address alone. There is no\n * sentinel for \"anonymous\" -- an anonymous holder is exactly one that has not\n * claimed the channel.\n */\n boundAddress?: string;\n};\n\nexport type RemoteFilter = {\n documentId: string[];\n scope: string[];\n branch: string;\n};\n\nexport type RemoteCursor = {\n remoteName: string;\n cursorType: string;\n cursorOrdinal: number;\n lastSyncedAtUtcMs?: number;\n};\n\nexport type ChannelMeta = {\n id: string;\n};\n\nexport type SyncEnvelopeType = \"operations\" | \"ack\";\n\nexport type SyncEnvelope = {\n type: SyncEnvelopeType;\n channelMeta: ChannelMeta;\n operations?: OperationWithContext[];\n cursor?: RemoteCursor;\n key?: string;\n dependsOn?: string[];\n};\n\nexport enum SyncOperationStatus {\n Unknown = -1,\n TransportPending = 0,\n ExecutionPending = 1,\n Applied = 2,\n Error = 3,\n}\n\nexport enum ChannelErrorSource {\n None = \"none\",\n Channel = \"channel\",\n Inbox = \"inbox\",\n Outbox = \"outbox\",\n}\n\nexport type SyncOperationErrorType =\n | \"SIGNATURE_INVALID\"\n | \"HASH_MISMATCH\"\n | \"LIBRARY_ERROR\"\n | \"MISSING_OPERATIONS\"\n | \"EXCESSIVE_SHUFFLE\"\n | \"GRACEFUL_ABORT\"\n /**\n * An arriving auth operation did not exceed the local auth head. Exempt from\n * quarantine, because reconciling the two policies needs the traffic a\n * quarantine would stop.\n */\n | \"AUTH_TIMESTAMP_NOT_MONOTONIC\"\n /** An arriving operation carried a timestamp that is not an ISO-8601 instant. */\n | \"INVALID_TIMESTAMP\"\n /** No classification applies, including rows written before the field. */\n | \"UNCLASSIFIED\";\n\nexport type ChannelHealth = {\n state: \"idle\" | \"running\" | \"error\";\n lastSuccessUtcMs?: number;\n lastFailureUtcMs?: number;\n failureCount: number;\n};\n\nexport type ConnectionState =\n | \"connecting\"\n | \"connected\"\n | \"disconnected\"\n | \"reconnecting\"\n | \"error\";\n\nexport type ConnectionStateSnapshot = {\n state: ConnectionState;\n failureCount: number;\n lastSuccessUtcMs: number;\n lastFailureUtcMs: number;\n pushBlocked: boolean;\n pushFailureCount: number;\n receivingPages: boolean;\n /** Latest error was the remote rejecting the caller as unauthenticated\n * (401/403 or Forbidden GraphQL error). Only meaningful while state=\"error\". */\n requiresAuth: boolean;\n};\n\nexport type ConnectionStateChangedEvent = {\n remoteName: string;\n remoteId: string;\n previous: ConnectionState;\n current: ConnectionState;\n snapshot: ConnectionStateSnapshot;\n};\n\nexport type RemoteStatus = {\n push: ChannelHealth;\n pull: ChannelHealth;\n};\n\nexport type ChannelConfig = {\n type: string;\n parameters: Record<string, unknown>;\n};\n\nexport type RemoteRecord = {\n id: string;\n name: string;\n collectionId: DriveCollectionId;\n channelConfig: ChannelConfig;\n filter: RemoteFilter;\n options: RemoteOptions;\n status: RemoteStatus;\n};\n\n/**\n * Event types for sync lifecycle events.\n * These events track the sync progress of a job's operations to remotes.\n * Uses a separate namespace (20000 range) from ReactorEventTypes (10000 range).\n */\nexport const SyncEventTypes = {\n SYNC_PENDING: 20001,\n SYNC_SUCCEEDED: 20002,\n SYNC_FAILED: 20003,\n DEAD_LETTER_ADDED: 20004,\n CONNECTION_STATE_CHANGED: 20005,\n} as const;\n\n/**\n * Event emitted when all SyncOperations for a job are queued in outboxes.\n */\nexport type SyncPendingEvent = {\n jobId: string;\n syncOperationCount: number;\n remoteNames: string[];\n};\n\n/**\n * Event emitted when all sync operations for a job succeed.\n */\nexport type SyncSucceededEvent = {\n jobId: string;\n syncOperationCount: number;\n};\n\n/**\n * Event emitted when at least one sync operation for a job fails.\n */\nexport type SyncFailedEvent = {\n jobId: string;\n successCount: number;\n failureCount: number;\n errors: Array<{\n remoteName: string;\n documentId: string;\n error: string;\n }>;\n};\n\n/**\n * Event emitted when a sync operation is moved to dead letter storage.\n */\nexport type DeadLetterAddedEvent = {\n id: string;\n jobId: string;\n remoteName: string;\n documentId: string;\n errorSource: ChannelErrorSource;\n errorType: SyncOperationErrorType;\n};\n\n/**\n * Status of a sync operation result.\n */\nexport type SyncResultStatus = \"succeeded\" | \"failed\";\n\n/**\n * Error information for a failed sync operation to a specific remote.\n */\nexport type SyncResultError = {\n remoteName: string;\n documentId: string;\n error: string;\n};\n\n/**\n * Result of waiting for sync operations to complete for a job.\n * Returned by ISyncManager.waitForSync().\n */\nexport type SyncResult = {\n jobId: string;\n status: SyncResultStatus;\n syncOperationCount: number;\n successCount: number;\n failureCount: number;\n errors: SyncResultError[];\n};\n","import type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport type MailboxCallback = (items: SyncOperation[]) => void;\n\n/**\n * The Mailbox interface is not intended to use any persistence. Instead, the\n * IChannel implementation is responsible for persisting cursors or other data.\n *\n * This means that ackOrdinal and latestOrdinal are in memory only.\n */\nexport interface IMailbox {\n get items(): ReadonlyArray<SyncOperation>;\n\n /**\n * The latest ordinal that has been acknowledged. Because acknowledged items\n * are removed from the mailbox, this is the last ordinal that has been removed.\n */\n get ackOrdinal(): number;\n\n /**\n * The latest ordinal of the items that are or have been added to the mailbox.\n * This may be greater than the ack ordinal if items have been added but not\n * yet acknowledged.\n */\n get latestOrdinal(): number;\n\n // sync op management\n init(ackOrdinal: number): void;\n advanceOrdinal(ordinal: number): void;\n get(id: string): SyncOperation | undefined;\n add(...items: SyncOperation[]): void;\n remove(...items: SyncOperation[]): void;\n\n // listeners\n onAdded(callback: MailboxCallback): void;\n onRemoved(callback: MailboxCallback): void;\n\n // these are mostly for debug use\n pause(): void;\n resume(): void;\n flush(): void;\n isPaused(): boolean;\n}\n\nexport class MailboxAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `Mailbox callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"MailboxAggregateError\";\n this.errors = errors;\n }\n}\n\nexport class Mailbox implements IMailbox {\n private itemsMap: Map<string, SyncOperation> = new Map();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private paused: boolean = false;\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n return this._ack;\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.set(item.id, item);\n\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n\n if (this.paused) {\n this.addedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n }\n\n if (this.paused) {\n this.removedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n this.flush();\n }\n\n flush(): void {\n if (this.addedBuffer.length > 0) {\n const items = this.addedBuffer.splice(0);\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n if (this.removedBuffer.length > 0) {\n const items = this.removedBuffer.splice(0);\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n}\n","import {\n type IMailbox,\n MailboxAggregateError,\n type MailboxCallback,\n} from \"./mailbox.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport class BufferedMailbox implements IMailbox {\n private itemsMap: Map<string, SyncOperation> = new Map();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n private addedTimer: ReturnType<typeof setTimeout> | null = null;\n private removedTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly milliseconds: number;\n private readonly maxQueued: number;\n private paused: boolean = false;\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n constructor(milliseconds: number, maxQueued: number) {\n this.milliseconds = milliseconds;\n this.maxQueued = maxQueued;\n }\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n return this._ack;\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.set(item.id, item);\n\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n this.addedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.addedBuffer.length >= this.maxQueued) {\n this.flushAdded();\n } else {\n this.scheduleAddedFlush();\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n }\n this.removedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.removedBuffer.length >= this.maxQueued) {\n this.flushRemoved();\n } else {\n this.scheduleRemovedFlush();\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.addedBuffer.length > 0) {\n this.scheduleAddedFlush();\n }\n if (this.removedBuffer.length > 0) {\n this.scheduleRemovedFlush();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n flush(): void {\n this.flushAdded();\n this.flushRemoved();\n }\n\n private scheduleAddedFlush(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n }\n this.addedTimer = setTimeout(() => {\n this.flushAdded();\n }, this.milliseconds);\n }\n\n private scheduleRemovedFlush(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n }\n this.removedTimer = setTimeout(() => {\n this.flushRemoved();\n }, this.milliseconds);\n }\n\n private flushAdded(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n\n const items = this.addedBuffer;\n this.addedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.addedCallbacks, items);\n }\n }\n\n private flushRemoved(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n\n const items = this.removedBuffer;\n this.removedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.removedCallbacks, items);\n }\n }\n\n private invokeCallbacks(\n callbacks: MailboxCallback[],\n items: SyncOperation[],\n ): void {\n const callbacksCopy = [...callbacks];\n const errors: Error[] = [];\n\n for (const callback of callbacksCopy) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n}\n","import type { ChannelErrorSource, SyncOperationErrorType } from \"./types.js\";\n\nexport type GraphQLRequestErrorCategory =\n | \"network\"\n | \"http\"\n | \"parse\"\n | \"graphql\"\n | \"missing-data\";\n\nexport class GraphQLRequestError extends Error {\n readonly statusCode: number | undefined;\n readonly category: GraphQLRequestErrorCategory;\n /**\n * One entry per error the response carried, in order, holding its\n * `extensions.code` - undefined where it declared none. Kept per error rather\n * than as a set, because a response that mixes a classified error with an\n * unclassified one must not be read as if only the classified one arrived.\n */\n readonly codes: readonly (string | undefined)[];\n\n constructor(\n message: string,\n category: GraphQLRequestErrorCategory,\n statusCode?: number,\n codes: readonly (string | undefined)[] = [],\n ) {\n super(message);\n this.name = \"GraphQLRequestError\";\n this.category = category;\n this.statusCode = statusCode;\n this.codes = codes;\n }\n}\n\n/**\n * Extension codes a remote uses to say a failure is worth polling through.\n *\n * Shared with reactor-api so the server throws what this check reads and the two\n * cannot drift. A `graphql` category error is otherwise permanent: it stops the\n * poll timer, and nothing restarts it, so a code that lands here is the\n * difference between a channel that recovers and one that is dead for the\n * process lifetime.\n */\nexport const RECOVERABLE_GRAPHQL_ERROR_CODES = {\n /**\n * A stored operation cannot be represented in the schema - an action with no\n * id, say. The document holding it needs repairing, but the channel serves\n * every other document, and a peer that stopped polling would stop receiving\n * those too.\n */\n malformedStoredOperation: \"MALFORMED_STORED_OPERATION\",\n} as const;\n\nconst RECOVERABLE_CODES: ReadonlySet<string> = new Set(\n Object.values(RECOVERABLE_GRAPHQL_ERROR_CODES),\n);\n\n/**\n * True when every error the response carried named a recoverable code.\n *\n * Unanimity is the requirement: one unclassified error alongside a recoverable\n * one means something else also went wrong, and polling through that would be\n * guessing.\n */\nexport function isRecoverableGraphQLError(error: GraphQLRequestError): boolean {\n return (\n error.codes.length > 0 &&\n error.codes.every(\n (code) => code !== undefined && RECOVERABLE_CODES.has(code),\n )\n );\n}\n\n/** Auth-rejection message fragments the switchboard emits. Shared with\n * reactor-api so server throws and this client check can't drift. */\nexport const DRIVE_AUTH_ERROR_MESSAGES = {\n forbidden: \"Forbidden: insufficient permissions\",\n authenticationRequired: \"Forbidden: authentication required\",\n} as const;\n\n/**\n * A non-GraphQL HTTP failure against a drive endpoint.\n *\n * Drive discovery (`GET <base>/d/:drive`) is REST, not GraphQL, so its\n * failures cannot be a `GraphQLRequestError` without the name lying about\n * what was called. It carries the status for the same reason that one does:\n * `isDriveAuthError` is what decides whether a failure prompts a login, and a\n * bare `Error` tells it nothing.\n */\nexport class DriveRequestError extends Error {\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DriveRequestError\";\n this.statusCode = statusCode;\n }\n}\n\n/** True when the remote rejected the caller as unauthenticated/unauthorized:\n * an HTTP 401/403, or a Forbidden/Unauthorized GraphQL error.\n *\n * 403 and 401 only — NOT 404. The drive info endpoint answers a drive the\n * caller may not read with the same 404 it gives a drive that does not exist,\n * so that an unauthorized caller cannot enumerate drives by probing slugs.\n * That is deliberate, and it costs exactly this: a protected drive is\n * indistinguishable from a typo, and prompting for a login on every 404 would\n * fire on every mistyped URL. See the `WWW-Authenticate` note on the endpoint\n * for the signal that would let a client tell the two apart. */\nexport function isDriveAuthError(error: unknown): boolean {\n if (error instanceof DriveRequestError) {\n return error.statusCode === 401 || error.statusCode === 403;\n }\n if (!(error instanceof GraphQLRequestError)) {\n return false;\n }\n if (error.category === \"http\") {\n return error.statusCode === 401 || error.statusCode === 403;\n }\n if (error.category === \"graphql\") {\n return Object.values(DRIVE_AUTH_ERROR_MESSAGES).some((m) =>\n error.message.includes(m),\n );\n }\n return false;\n}\n\nexport class PollingChannelError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PollingChannelError\";\n }\n}\n\nexport class ChannelError extends Error {\n source: ChannelErrorSource;\n error: Error;\n /**\n * The classification when something other than the error carries it. Absent\n * means derive it from `error.name`; a dead letter mirrored from a peer sets it,\n * because only the message crosses the wire.\n */\n readonly errorType?: SyncOperationErrorType;\n\n constructor(\n source: ChannelErrorSource,\n error: Error,\n errorType?: SyncOperationErrorType,\n ) {\n super(`ChannelError[${source}]: ${error.message}`);\n this.name = \"ChannelError\";\n this.source = source;\n this.error = error;\n this.errorType = errorType;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ChannelError } from \"./errors.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\ntype SyncOperationStatusCallback = (\n syncOp: SyncOperation,\n prev: SyncOperationStatus,\n next: SyncOperationStatus,\n) => void;\n\nexport class SyncOperationAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `SyncOperation callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"SyncOperationAggregateError\";\n this.errors = errors;\n }\n}\n\nexport class SyncOperation {\n readonly id: string;\n readonly jobId: string;\n jobDependencies: string[];\n readonly remoteName: string;\n readonly documentId: string;\n readonly scopes: string[];\n readonly branch: string;\n readonly operations: OperationWithContext[];\n status: SyncOperationStatus;\n error?: ChannelError;\n deliveredCount = 0;\n emittedCount = 0;\n\n callbacks: SyncOperationStatusCallback[] = [];\n\n constructor(\n id: string,\n jobId: string,\n jobDependencies: string[],\n remoteName: string,\n documentId: string,\n scopes: string[],\n branch: string,\n operations: OperationWithContext[],\n ) {\n this.id = id;\n this.jobId = jobId;\n this.jobDependencies = jobDependencies;\n this.remoteName = remoteName;\n this.documentId = documentId;\n this.scopes = scopes;\n this.branch = branch;\n this.operations = operations;\n this.status = SyncOperationStatus.Unknown;\n }\n\n on(callback: SyncOperationStatusCallback): void {\n this.callbacks.push(callback);\n }\n\n started(): void {\n this.transition(SyncOperationStatus.TransportPending);\n }\n\n transported(): void {\n this.transition(SyncOperationStatus.ExecutionPending);\n }\n\n executed(): void {\n this.transition(SyncOperationStatus.Applied);\n }\n\n failed(error: ChannelError): void {\n this.error = error;\n this.transition(SyncOperationStatus.Error);\n }\n\n transition(next: SyncOperationStatus): void {\n const prev = this.status;\n if (next <= prev) {\n return;\n }\n this.status = next;\n const errors: Error[] = [];\n for (const callback of this.callbacks) {\n try {\n callback(this, prev, next);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new SyncOperationAggregateError(errors);\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport { type OperationIndexEntry } from \"../cache/operation-index-types.js\";\nimport type { ChannelError } from \"./errors.js\";\nimport type { PreparedBatch } from \"./batch-aggregator.js\";\nimport type { IMailbox } from \"./mailbox.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncOperationStatus,\n type ChannelHealth,\n type RemoteFilter,\n type SyncOperationErrorType,\n} from \"./types.js\";\n\nexport type OperationBatch = {\n documentId: string;\n branch: string;\n scope: string;\n operations: OperationWithContext[];\n};\n\n/**\n * Trims a mailbox using the jobIds from a batch.\n */\nexport function trimMailboxFromBatch(\n mailbox: IMailbox,\n batch: PreparedBatch,\n): void {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the inbox when marked as executed\n // 2. we remove syncops as a batch after they have been executed\n for (const syncOp of batch.entries) {\n for (const item of mailbox.items) {\n if (syncOp.event.jobId === item.jobId) {\n toRemove.push(item);\n break;\n }\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Trims a mailbox using the ack ordinal.\n */\nexport function trimMailboxFromAckOrdinal(\n mailbox: IMailbox,\n ackOrdinal: number,\n) {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the mailbox when marked as applied\n // 2. we remove syncops as a single batch\n for (const syncOp of mailbox.items) {\n let maxOrdinal = 0;\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n\n if (maxOrdinal <= ackOrdinal) {\n toRemove.push(syncOp);\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Filters operations based on a remote's filter criteria.\n *\n * @param operations - The operations to filter\n * @param filter - The filter criteria to apply\n * @returns The filtered operations that match the criteria\n */\nexport function filterOperations(\n operations: OperationWithContext[],\n filter: RemoteFilter,\n): OperationWithContext[] {\n return operations.filter((op) => {\n if (filter.branch && op.context.branch !== filter.branch) {\n return false;\n }\n\n if (\n filter.documentId.length > 0 &&\n !filter.documentId.includes(op.context.documentId)\n ) {\n return false;\n }\n\n if (filter.scope.length > 0 && !filter.scope.includes(op.context.scope)) {\n return false;\n }\n\n return true;\n });\n}\n\n/**\n * Creates an idle channel health status.\n *\n * @returns A new idle channel health object\n */\nexport function createIdleHealth(): ChannelHealth {\n return {\n state: \"idle\",\n failureCount: 0,\n };\n}\n\n/**\n * Batches consecutive operations by documentId and scope, preserving ordering.\n *\n * For operations [a1_doc, a1_global, a2_doc, b1_global], this returns:\n * - Batch 1: [a1_doc] for doc-a, document scope\n * - Batch 2: [a1_global] for doc-a, global scope\n * - Batch 3: [a2_doc] for doc-a, document scope\n * - Batch 4: [b1_global] for doc-b, global scope\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents and scopes.\n */\nexport function batchOperationsByDocument(\n operations: OperationWithContext[],\n): OperationBatch[] {\n const batches: OperationBatch[] = [];\n\n let currentDocId: string | null = null;\n let currentScope: string | null = null;\n let currentBatch: OperationWithContext[] = [];\n\n const flushBatch = () => {\n if (\n currentBatch.length === 0 ||\n currentDocId === null ||\n currentScope === null\n ) {\n return;\n }\n\n batches.push({\n documentId: currentDocId,\n branch: currentBatch[0].context.branch,\n scope: currentScope,\n operations: currentBatch,\n });\n currentBatch = [];\n };\n\n for (const op of operations) {\n const docId = op.context.documentId;\n const scope = op.context.scope;\n if (docId !== currentDocId || scope !== currentScope) {\n flushBatch();\n currentDocId = docId;\n currentScope = scope;\n }\n currentBatch.push(op);\n }\n\n flushBatch();\n return batches;\n}\n\n/**\n * Splits a sorted page of operations into a safe-to-emit prefix and a\n * deferred tail containing the trailing run that shares the same\n * (documentId, branch, scope, timestampUtcMs) as the last operation.\n *\n * The page is assumed to be sorted by (documentId, scope, ordinal), so a\n * same-(docId, scope, ts) run is contiguous and lives at the end of any\n * page that contains its last member. Holding that tail back lets callers\n * prepend it to the next page so a single producer-side execute() call\n * never gets split across two outbound envelopes.\n */\nexport function splitTrailingSameTimestampRun(\n operations: OperationWithContext[],\n): { emit: OperationWithContext[]; carry: OperationWithContext[] } {\n if (operations.length === 0) {\n return { emit: [], carry: [] };\n }\n\n const last = operations[operations.length - 1];\n const lastDocId = last.context.documentId;\n const lastBranch = last.context.branch;\n const lastScope = last.context.scope;\n const lastTs = last.operation.timestampUtcMs;\n\n let carryStart = operations.length;\n for (let i = operations.length - 1; i >= 0; i--) {\n const op = operations[i];\n if (\n op.context.documentId === lastDocId &&\n op.context.branch === lastBranch &&\n op.context.scope === lastScope &&\n op.operation.timestampUtcMs === lastTs\n ) {\n carryStart = i;\n } else {\n break;\n }\n }\n\n return {\n emit: operations.slice(0, carryStart),\n carry: operations.slice(carryStart),\n };\n}\n\nexport function getMaxOrdinal(operations: OperationWithContext[]): number {\n return operations.reduce(\n (maxOrdinal, operation) => Math.max(maxOrdinal, operation.context.ordinal),\n 0,\n );\n}\n\nexport function filterByCollectionMembership(\n operations: OperationWithContext[],\n collectionId: string,\n collectionMemberships?: Record<string, string[]>,\n): OperationWithContext[] {\n if (!collectionMemberships) {\n return [];\n }\n\n return operations.filter((op) => {\n const documentId = op.context.documentId;\n if (!(documentId in collectionMemberships)) {\n return false;\n }\n return collectionMemberships[documentId].includes(collectionId);\n });\n}\n\nexport function toOperationWithContext(\n entry: OperationIndexEntry,\n): OperationWithContext {\n return {\n operation: {\n id: entry.id,\n index: entry.index,\n skip: entry.skip,\n hash: entry.hash,\n timestampUtcMs: entry.timestampUtcMs,\n deniedReason: entry.deniedReason,\n action: entry.action,\n } as Operation,\n context: {\n documentId: entry.documentId,\n documentType: entry.documentType,\n scope: entry.scope,\n branch: entry.branch,\n ordinal: entry.ordinal ?? 0,\n },\n };\n}\n\n/**\n * Merges SyncOperations that share the same (documentId, scope, branch) into\n * a single SyncOperation per group. Within each group, operations are sorted\n * by context.ordinal. The merged SyncOperation keeps the first group member's\n * jobId; all other jobIds are remapped so external dependencies still resolve.\n *\n * Only CONTIGUOUS runs merge. A document whose scopes interleave arrives as an\n * alternating chain (document -> auth -> document -> auth) where each entry\n * depends on the one before it. Merging every occurrence of a\n * (documentId, scope, branch) collapses that chain into two nodes that each\n * depend on the other, which validateBatchStructure rejects as a dependency\n * cycle. Merging only adjacent entries keeps the chain linear.\n */\nexport function consolidateSyncOperations(\n syncOps: SyncOperation[],\n): SyncOperation[] {\n if (syncOps.length <= 1) {\n return syncOps;\n }\n\n const groups: Array<{ ops: SyncOperation[]; canonicalJobId: string }> = [];\n const jobIdRemap = new Map<string, string>();\n let prevKey: string | null = null;\n\n for (const syncOp of syncOps) {\n const key = `${syncOp.documentId}|${syncOp.scopes.slice().sort().join(\",\")}|${syncOp.branch}`;\n\n const current = key === prevKey ? groups[groups.length - 1] : undefined;\n if (current) {\n current.ops.push(syncOp);\n if (syncOp.jobId && syncOp.jobId !== current.canonicalJobId) {\n jobIdRemap.set(syncOp.jobId, current.canonicalJobId);\n }\n } else {\n groups.push({ ops: [syncOp], canonicalJobId: syncOp.jobId });\n prevKey = key;\n }\n }\n\n const result: SyncOperation[] = [];\n\n for (const group of groups) {\n const allOperations = group.ops\n .flatMap((op) => op.operations)\n .sort((a, b) => a.context.ordinal - b.context.ordinal);\n\n const allDeps = new Set<string>();\n for (const op of group.ops) {\n for (const dep of op.jobDependencies) {\n allDeps.add(dep);\n }\n }\n allDeps.delete(group.canonicalJobId);\n for (const op of group.ops) {\n allDeps.delete(op.jobId);\n }\n\n const remappedDeps: string[] = [];\n for (const dep of allDeps) {\n const mapped = jobIdRemap.get(dep) ?? dep;\n if (!remappedDeps.includes(mapped) && mapped !== group.canonicalJobId) {\n remappedDeps.push(mapped);\n }\n }\n\n const first = group.ops[0];\n const merged = new SyncOperation(\n first.id,\n first.jobId,\n remappedDeps,\n first.remoteName,\n first.documentId,\n first.scopes,\n first.branch,\n allOperations,\n );\n\n if (first.status > SyncOperationStatus.TransportPending) {\n if (first.status >= SyncOperationStatus.Error) {\n merged.executed();\n } else if (first.status >= SyncOperationStatus.Applied) {\n merged.transported();\n } else {\n merged.started();\n }\n }\n\n result.push(merged);\n }\n\n return result;\n}\n\ntype ChunkableItem = {\n syncOp: { jobId: string; jobDependencies: string[] };\n};\n\n/**\n * Chunks sync operations into batches that respect dependency-connected\n * components. SyncOps linked by jobDependencies are kept in the same chunk.\n * If a connected component exceeds maxSize, it is split by topological order.\n */\nexport function chunkSyncOperations<T extends ChunkableItem>(\n items: T[],\n maxSize: number,\n): T[][] {\n if (items.length === 0) return [];\n if (items.length <= maxSize) return [items];\n\n // Union-Find\n const parent = items.map((_, i) => i);\n const rank = new Array<number>(items.length).fill(0);\n\n function find(x: number): number {\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]];\n x = parent[x];\n }\n return x;\n }\n\n function union(a: number, b: number): void {\n const ra = find(a);\n const rb = find(b);\n if (ra === rb) return;\n if (rank[ra] < rank[rb]) {\n parent[ra] = rb;\n } else if (rank[ra] > rank[rb]) {\n parent[rb] = ra;\n } else {\n parent[rb] = ra;\n rank[ra]++;\n }\n }\n\n const jobIdToIndex = new Map<string, number>();\n for (let i = 0; i < items.length; i++) {\n jobIdToIndex.set(items[i].syncOp.jobId, i);\n }\n\n for (let i = 0; i < items.length; i++) {\n for (const dep of items[i].syncOp.jobDependencies) {\n const depIdx = jobIdToIndex.get(dep);\n if (depIdx !== undefined) {\n union(i, depIdx);\n }\n }\n }\n\n // Group into components preserving insertion order\n const componentMap = new Map<number, T[]>();\n for (let i = 0; i < items.length; i++) {\n const root = find(i);\n let component = componentMap.get(root);\n if (!component) {\n component = [];\n componentMap.set(root, component);\n }\n component.push(items[i]);\n }\n\n const components = [...componentMap.values()];\n\n // Greedy bin-packing\n const chunks: T[][] = [];\n let currentChunk: T[] = [];\n\n for (const component of components) {\n if (component.length > maxSize) {\n // Flush current chunk first\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n }\n // Split oversized component by topological walk\n for (const subChunk of splitComponent(component, maxSize)) {\n chunks.push(subChunk);\n }\n continue;\n }\n\n if (currentChunk.length + component.length > maxSize) {\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n currentChunk = [...component];\n } else {\n currentChunk.push(...component);\n }\n }\n\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n}\n\n/**\n * Splits an oversized connected component into chunks by topological order.\n * Cross-chunk dependency references are handled by the caller's dep filter.\n */\nfunction splitComponent<T extends ChunkableItem>(\n items: T[],\n maxSize: number,\n): T[][] {\n // Build in-degree map for topological sort within the component\n const jobIdToItem = new Map<string, T>();\n const jobIds = new Set<string>();\n for (const item of items) {\n jobIdToItem.set(item.syncOp.jobId, item);\n jobIds.add(item.syncOp.jobId);\n }\n\n const inDegree = new Map<string, number>();\n const adjacency = new Map<string, string[]>();\n for (const item of items) {\n const key = item.syncOp.jobId;\n if (!inDegree.has(key)) inDegree.set(key, 0);\n if (!adjacency.has(key)) adjacency.set(key, []);\n for (const dep of item.syncOp.jobDependencies) {\n if (jobIds.has(dep)) {\n inDegree.set(key, (inDegree.get(key) ?? 0) + 1);\n if (!adjacency.has(dep)) adjacency.set(dep, []);\n adjacency.get(dep)!.push(key);\n }\n }\n }\n\n // Kahn's algorithm\n const queue: string[] = [];\n for (const [key, degree] of inDegree) {\n if (degree === 0) queue.push(key);\n }\n\n const sorted: T[] = [];\n while (queue.length > 0) {\n const key = queue.shift()!;\n sorted.push(jobIdToItem.get(key)!);\n for (const neighbor of adjacency.get(key) ?? []) {\n const newDegree = (inDegree.get(neighbor) ?? 1) - 1;\n inDegree.set(neighbor, newDegree);\n if (newDegree === 0) queue.push(neighbor);\n }\n }\n\n // Fall back to insertion order for any items not reached (cycle safety)\n if (sorted.length < items.length) {\n const sortedIds = new Set(sorted.map((item) => item.syncOp.jobId));\n for (const item of items) {\n if (!sortedIds.has(item.syncOp.jobId)) {\n sorted.push(item);\n }\n }\n }\n\n // Slice into chunks\n const chunks: T[][] = [];\n for (let i = 0; i < sorted.length; i += maxSize) {\n chunks.push(sorted.slice(i, i + maxSize));\n }\n return chunks;\n}\n\n/**\n * Classifies a failure by error name rather than `instanceof`, because a failure\n * that crossed the pooled-worker boundary arrives as plain data.\n */\nexport function classifyJobFailure(errorName: string): SyncOperationErrorType {\n switch (errorName) {\n case \"AuthTimestampNotMonotonicError\":\n return \"AUTH_TIMESTAMP_NOT_MONOTONIC\";\n case \"InvalidOperationTimestampError\":\n return \"INVALID_TIMESTAMP\";\n case \"ExcessiveReshuffleError\":\n return \"EXCESSIVE_SHUFFLE\";\n case \"InvalidSignatureError\":\n return \"SIGNATURE_INVALID\";\n case \"HashMismatchError\":\n return \"HASH_MISMATCH\";\n default:\n return \"UNCLASSIFIED\";\n }\n}\n\n/** The explicit type when something else carried it, else derived by name. */\nexport function syncOperationErrorType(\n error: ChannelError | undefined,\n): SyncOperationErrorType {\n return error?.errorType ?? classifyJobFailure(error?.error?.name ?? \"Error\");\n}\n\n/** Dead-letter types that must not stop the document syncing. */\nconst NON_QUARANTINING_ERROR_TYPES: ReadonlySet<SyncOperationErrorType> =\n new Set([\"AUTH_TIMESTAMP_NOT_MONOTONIC\"]);\n\n/**\n * A held auth operation must not quarantine: reconciling the two policies needs\n * the traffic a quarantine would stop.\n */\nexport function quarantinesDocument(\n errorType: SyncOperationErrorType,\n): boolean {\n return !NON_QUARANTINING_ERROR_TYPES.has(errorType);\n}\n","import type { IQueue } from \"../../queue/interfaces.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\n\nexport type PollTimerConfig = {\n intervalMs: number;\n maxQueueDepth: number;\n backpressureCheckIntervalMs: number;\n retryBaseDelayMs: number;\n retryMaxDelayMs: number;\n /**\n * If true, start() puts the timer into a paused state — running but not ticking.\n * Use resume() to begin scheduling ticks, or triggerNow() to fire a single tick\n * without leaving the paused state. Defaults to false (auto-tick on start).\n */\n startPaused: boolean;\n};\n\nconst DEFAULT_CONFIG: PollTimerConfig = {\n intervalMs: 2000,\n maxQueueDepth: 100,\n backpressureCheckIntervalMs: 500,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n startPaused: false,\n};\n\nexport function calculateBackoffDelay(\n consecutiveFailures: number,\n retryBaseDelayMs: number,\n retryMaxDelayMs: number,\n random: number,\n): number {\n const backoff = Math.min(\n retryMaxDelayMs,\n retryBaseDelayMs * Math.pow(2, consecutiveFailures - 1),\n );\n return backoff / 2 + random * (backoff / 2);\n}\n\n/**\n * Default poll timer using setTimeout.\n * Waits for delegate completion before scheduling next tick.\n * Checks queue depth and defers polling when backpressure is detected.\n */\nexport class IntervalPollTimer implements IPollTimer {\n private delegate: (() => Promise<void>) | undefined;\n private timer: NodeJS.Timeout | undefined;\n private running: boolean;\n private paused: boolean;\n private consecutiveFailures: number;\n private readonly queue: IQueue;\n private readonly config: PollTimerConfig;\n\n constructor(queue: IQueue, config: Partial<PollTimerConfig> = {}) {\n this.queue = queue;\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.running = false;\n this.paused = this.config.startPaused;\n this.consecutiveFailures = 0;\n }\n\n setDelegate(delegate: () => Promise<void>): void {\n this.delegate = delegate;\n }\n\n start(): void {\n this.running = true;\n this.consecutiveFailures = 0;\n if (!this.paused) {\n this.tick();\n }\n }\n\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private tick(): void {\n if (!this.delegate || !this.running) return;\n\n const delegate = this.delegate;\n\n void this.queue\n .totalSize()\n .then((size) => {\n if (!this.running) return;\n if (size > this.config.maxQueueDepth) {\n this.scheduleBackpressureRecheck();\n } else {\n void delegate()\n .then(() => {\n this.consecutiveFailures = 0;\n this.scheduleNext();\n })\n .catch(() => {\n this.consecutiveFailures++;\n this.scheduleRetry();\n });\n }\n })\n .catch(() => {\n // Fail-open: schedule next at normal interval when totalSize() throws\n this.scheduleNext();\n });\n }\n\n private scheduleNext(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(() => this.tick(), this.config.intervalMs);\n }\n\n private scheduleRetry(): void {\n if (!this.running || this.paused) return;\n const delay = calculateBackoffDelay(\n this.consecutiveFailures,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n this.timer = setTimeout(() => this.tick(), delay);\n }\n\n private scheduleBackpressureRecheck(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(\n () => this.tick(),\n this.config.backpressureCheckIntervalMs,\n );\n }\n\n pause(): void {\n this.paused = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.running) {\n this.scheduleNext();\n }\n }\n\n triggerNow(): void {\n if (this.running && this.delegate) {\n this.tick();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n getIntervalMs(): number {\n return this.config.intervalMs;\n }\n\n setIntervalMs(ms: number): void {\n this.config.intervalMs = ms;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport {\n deserializeSignature,\n toTransportAction,\n} from \"@powerhousedao/shared/document-model\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport { SyncOperationStatus, type SyncEnvelope } from \"../types.js\";\nimport { batchOperationsByDocument } from \"../utils.js\";\n\nlet syncOpCounter = 0;\n\n/**\n * Serializes an action for GraphQL transport.\n *\n * Projects onto exactly the fields `ActionInput` declares, which is what keeps a\n * stale runtime-only field - a legacy `attachments` array, say - from riding\n * along into the mutation variables, where the schema would reject the whole\n * request rather than the field.\n */\nexport function serializeAction(action: Action): unknown {\n return toTransportAction(action);\n}\n\n/**\n * Serializes a SyncEnvelope for GraphQL transport.\n *\n * Signatures are serialized as comma-separated strings since GraphQL schema\n * defines them as [String!]!. The resultingState context field is stripped\n * since it is not defined in OperationContextInput.\n */\nexport function serializeEnvelope(envelope: SyncEnvelope): unknown {\n return {\n type: envelope.type.toUpperCase(),\n channelMeta: envelope.channelMeta,\n operations: envelope.operations?.map((opWithContext) => ({\n operation: {\n index: opWithContext.operation.index,\n timestampUtcMs: opWithContext.operation.timestampUtcMs,\n hash: opWithContext.operation.hash,\n skip: opWithContext.operation.skip,\n error: opWithContext.operation.error,\n // Omitted while undefined, so a peer whose schema predates the field only\n // sees it on an operation that was actually denied.\n ...(opWithContext.operation.deniedReason !== undefined\n ? { deniedReason: opWithContext.operation.deniedReason }\n : {}),\n id: opWithContext.operation.id,\n action: serializeAction(opWithContext.operation.action),\n },\n context: {\n documentId: opWithContext.context.documentId,\n documentType: opWithContext.context.documentType,\n scope: opWithContext.context.scope,\n branch: opWithContext.context.branch,\n ordinal: opWithContext.context.ordinal,\n },\n })),\n cursor: envelope.cursor,\n key: envelope.key,\n dependsOn: envelope.dependsOn,\n };\n}\n\n/**\n * The nullable operation fields as GraphQL delivers them: a selected field\n * arrives as null rather than absent, which `Operation` does not admit.\n */\ntype WireNullableFields = {\n error?: string | null;\n deniedReason?: string | null;\n};\n\n/**\n * `isDenied` tests strictly against undefined, so a null left in place would mark\n * every synced operation denied. The key is removed rather than set to undefined,\n * so an operation that arrived without it stays identical to the one sent.\n */\nfunction normalizeAbsentFields(operation: Operation): Operation {\n const wire: WireNullableFields = operation;\n if (wire.error !== null && wire.deniedReason !== null) {\n return operation;\n }\n\n const normalized = { ...operation };\n if (wire.error === null) {\n delete normalized.error;\n }\n if (wire.deniedReason === null) {\n delete normalized.deniedReason;\n }\n\n return normalized;\n}\n\n/** Restores signature tuples and null-valued optional fields to undefined. */\nfunction deserializeOperation(\n opWithContext: OperationWithContext,\n): OperationWithContext {\n const operation = normalizeAbsentFields(opWithContext.operation);\n const signer = operation.action.context?.signer;\n\n if (!signer?.signatures || signer.signatures.length === 0) {\n return { ...opWithContext, operation };\n }\n\n const deserializedSignatures = signer.signatures.map(deserializeSignature);\n\n const deserializedOperation: Operation = {\n ...operation,\n action: {\n ...operation.action,\n context: {\n ...operation.action.context,\n signer: {\n ...signer,\n signatures: deserializedSignatures,\n },\n },\n },\n };\n\n return {\n ...opWithContext,\n operation: deserializedOperation,\n };\n}\n\n/**\n * Converts a SyncEnvelope containing operations into a SyncOperation.\n *\n * Extracts the necessary metadata from the envelope's operations to create\n * a sync operation that can be processed by the receiving channel. Also\n * deserializes any signatures from comma-separated strings back to tuples,\n * as GraphQL transport serializes Signature tuples for compatibility.\n *\n * @param envelope - The sync envelope containing operations\n * @param remoteName - The name of the remote this sync operation is associated with\n * @returns A new SyncOperation containing the envelope's operations with deserialized signatures\n * @throws Error if envelope has no operations or operations array is empty\n */\nexport function envelopeToSyncOperation(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation {\n if (!envelope.operations || envelope.operations.length === 0) {\n throw new Error(\n \"Cannot create SyncOperation from envelope without operations\",\n );\n }\n\n const deserializedOperations = envelope.operations.map(deserializeOperation);\n const firstOp = deserializedOperations[0];\n const documentId = firstOp.context.documentId;\n const branch = firstOp.context.branch;\n const scopes = [\n ...new Set(deserializedOperations.map((op) => op.context.scope)),\n ];\n\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n documentId,\n scopes,\n branch,\n deserializedOperations,\n );\n}\n\n/**\n * Converts a SyncEnvelope containing operations into multiple SyncOperations.\n *\n * This function batches operations by documentId, preserving cross-document ordering.\n * For operations [a1, a2, a3, b1, b2, a4], it returns:\n * - SyncOperation 1: [a1, a2, a3] for doc-a\n * - SyncOperation 2: [b1, b2] for doc-b\n * - SyncOperation 3: [a4] for doc-a\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents.\n */\nexport function envelopesToSyncOperations(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation[] {\n if (!envelope.operations || envelope.operations.length === 0) {\n return [];\n }\n\n const deserializedOps = envelope.operations.map(deserializeOperation);\n const batches = batchOperationsByDocument(deserializedOps);\n\n return batches.map((batch) => {\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n });\n}\n\nexport const getLatestAppliedOrdinal = (syncOps: SyncOperation[]): number => {\n let maxOrdinal = 0;\n for (const syncOp of syncOps) {\n if (syncOp.status === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n }\n }\n return maxOrdinal;\n};\n","import type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../../cache/operation-index-types.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport { BufferedMailbox } from \"../buffered-mailbox.js\";\nimport {\n ChannelError,\n GraphQLRequestError,\n isDriveAuthError,\n isRecoverableGraphQLError,\n} from \"../errors.js\";\nimport type { ConnectionStateChangeCallback, IChannel } from \"../interfaces.js\";\nimport { type IMailbox, Mailbox } from \"../mailbox.js\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport type {\n ConnectionState,\n ConnectionStateSnapshot,\n JwtHandler,\n RemoteFilter,\n SyncEnvelope,\n SyncOperationErrorType,\n} from \"../types.js\";\nimport { ChannelErrorSource } from \"../types.js\";\nimport {\n consolidateSyncOperations,\n trimMailboxFromAckOrdinal,\n} from \"../utils.js\";\nimport { calculateBackoffDelay } from \"./interval-poll-timer.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\nimport {\n envelopesToSyncOperations,\n getLatestAppliedOrdinal,\n serializeEnvelope,\n} from \"./utils.js\";\n\n/**\n * Configuration parameters for GqlChannel\n */\nexport type GqlChannelConfig = {\n /** The GraphQL endpoint URL */\n url: string;\n /** Dynamic JWT token handler for generating fresh tokens per-request */\n jwtHandler?: JwtHandler;\n /** Custom fetch function for testing (default: global fetch) */\n fetchFn?: typeof fetch;\n /** Collection to synchronize (drive-level abstraction) */\n collectionId: DriveCollectionId;\n /** Filter to apply to operations */\n filter: RemoteFilter;\n /** Base delay in ms for exponential backoff on push retries */\n retryBaseDelayMs: number;\n /** Maximum delay in ms for exponential backoff on push retries */\n retryMaxDelayMs: number;\n};\n\n/**\n * Fields the auth projection added to the sync schema. A remote that predates\n * them rejects the whole query for naming one, so they are selected only while\n * the remote is known to serve them.\n */\nconst DECISION_FIELDS = [\"deniedReason\", \"errorType\"] as const;\n\ntype PollSyncEnvelopesResult = {\n pollSyncEnvelopes: {\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters?: Array<{\n documentId: string;\n error: string;\n errorType?: string | null;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n };\n};\n\n/**\n * GraphQL-based synchronization channel for network communication between reactors.\n */\nexport class GqlRequestChannel implements IChannel {\n readonly inbox: IMailbox;\n readonly outbox: IMailbox;\n readonly deadLetter: IMailbox;\n readonly config: GqlChannelConfig;\n private readonly bufferedOutbox: BufferedMailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly operationIndex: IOperationIndex;\n private readonly pollTimer: IPollTimer;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private failureCount: number;\n private lastSuccessUtcMs?: number;\n private lastFailureUtcMs?: number;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private pushFailureCount: number = 0;\n private pushRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private pushBlocked: boolean = false;\n private isPushing: boolean = false;\n private pendingDrain: boolean = false;\n private receivingPages: boolean = false;\n /** Cleared for good the first time the remote rejects {@link DECISION_FIELDS}. */\n private peerServesDecisionFields: boolean = true;\n private isRecovering: boolean = false;\n private connectionState: ConnectionState = \"connecting\";\n /** Latest unrecoverable error was an auth rejection; cleared on connect. */\n private requiresAuth: boolean = false;\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n config: GqlChannelConfig,\n operationIndex: IOperationIndex,\n pollTimer: IPollTimer,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.operationIndex = operationIndex;\n this.pollTimer = pollTimer;\n this.config = {\n url: config.url,\n jwtHandler: config.jwtHandler,\n fetchFn: config.fetchFn,\n collectionId: config.collectionId,\n filter: config.filter,\n retryBaseDelayMs: config.retryBaseDelayMs,\n retryMaxDelayMs: config.retryMaxDelayMs,\n };\n this.isShutdown = false;\n this.failureCount = 0;\n\n this.inbox = new Mailbox();\n this.bufferedOutbox = new BufferedMailbox(500, 25);\n this.outbox = this.bufferedOutbox;\n this.deadLetter = new Mailbox();\n\n this.deadLetter.onAdded((syncOps) => {\n for (const syncOp of syncOps) {\n this.logger.warn(\n \"Dead letter added for document @DocumentId on channel @ChannelId\",\n syncOp.documentId,\n this.channelId,\n );\n }\n });\n\n // when sync ops are added to the outbox, push them to the remote\n this.outbox.onAdded((syncOps) => {\n if (this.isShutdown) return;\n if (this.isPushing) {\n this.pendingDrain = true;\n return;\n }\n if (this.pushBlocked) return; // ops stay in outbox, included in next retry\n if (this.receivingPages) {\n this.pendingDrain = true;\n return;\n }\n this.attemptPush(syncOps);\n });\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedOutboxOrdinal) {\n this.lastPersistedOutboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n\n this.inbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n /**\n * Shuts down the channel and prevents further operations.\n */\n shutdown(): Promise<void> {\n this.abortController.abort();\n this.bufferedOutbox.flush();\n this.isShutdown = true;\n this.pollTimer.stop();\n\n if (this.pushRetryTimer) {\n clearTimeout(this.pushRetryTimer);\n this.pushRetryTimer = null;\n }\n\n this.transitionConnectionState(\"disconnected\");\n\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: this.failureCount,\n lastSuccessUtcMs: this.lastSuccessUtcMs ?? 0,\n lastFailureUtcMs: this.lastFailureUtcMs ?? 0,\n pushBlocked: this.pushBlocked,\n pushFailureCount: this.pushFailureCount,\n receivingPages: this.receivingPages,\n requiresAuth: this.requiresAuth,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n triggerPull(): void {\n if (this.isShutdown) return;\n this.pollTimer.triggerNow();\n }\n\n /** This channel polls a remote itself; it has no holder to hear from. */\n notePoll(): void {}\n\n /** No holder, so nothing this channel reports may strand one. */\n lastHolderPollUtcMs(): number | undefined {\n return undefined;\n }\n\n /**\n * Initializes the channel by registering it on the remote server and starting polling.\n */\n async init(): Promise<void> {\n const { ackOrdinal } = await this.touchRemoteChannel();\n\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n\n this.pollTimer.setDelegate(() => this.poll());\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (next === \"connected\") this.requiresAuth = false;\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n /**\n * Polls the remote for new sync envelopes.\n */\n private async poll(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n let response;\n try {\n response = await this.pollSyncEnvelopes(\n this.inbox.ackOrdinal,\n this.inbox.latestOrdinal,\n );\n } catch (error) {\n if (!this.handlePollError(error)) {\n throw error;\n }\n return;\n }\n\n const { envelopes, ackOrdinal, deadLetters, hasMore } = response;\n\n // first: trim outbox\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n\n // convert the envelopes to sync operations\n const allSyncOps: SyncOperation[] = [];\n for (const envelope of envelopes) {\n if (envelope.type.toLowerCase() === \"operations\" && envelope.operations) {\n const syncOps = envelopesToSyncOperations(envelope, this.remoteName);\n for (const syncOp of syncOps) {\n syncOp.transported();\n }\n allSyncOps.push(...syncOps);\n }\n }\n\n // merge SyncOps sharing the same (documentId, scope, branch) so\n // multiple polled envelopes for one document become a single load job\n const consolidated =\n allSyncOps.length > 1\n ? consolidateSyncOperations(allSyncOps)\n : allSyncOps;\n\n if (consolidated.length > 0) {\n this.inbox.add(...consolidated);\n }\n\n // handle dead letters from the remote\n if (deadLetters.length > 0) {\n this.handleRemoteDeadLetters(deadLetters);\n }\n\n if (hasMore) {\n this.receivingPages = true;\n } else if (this.receivingPages) {\n this.receivingPages = false;\n this.drainOutbox();\n }\n\n this.lastSuccessUtcMs = Date.now();\n this.failureCount = 0;\n this.transitionConnectionState(\"connected\");\n }\n\n /**\n * Handles dead letters reported by the remote server.\n * Creates local dead letter SyncOperations so the channel quiesces.\n */\n private handleRemoteDeadLetters(\n deadLetters: Array<{\n documentId: string;\n error: string;\n errorType?: string | null;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>,\n ): void {\n for (const dl of deadLetters) {\n this.logger.error(\n \"Remote dead letter on @ChannelId: document @DocumentId failed with: @Error\",\n this.channelId,\n dl.documentId,\n dl.error,\n );\n }\n\n const syncOps: SyncOperation[] = [];\n for (const dl of deadLetters) {\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n dl.jobId,\n [],\n this.remoteName,\n dl.documentId,\n dl.scopes,\n dl.branch,\n [],\n );\n // Only the message crosses the wire, so the origin's classification is\n // carried explicitly. Without it this replica would freeze a document the\n // origin is deliberately still syncing.\n syncOp.failed(\n new ChannelError(\n ChannelErrorSource.Outbox,\n new Error(dl.error),\n (dl.errorType ?? undefined) as SyncOperationErrorType | undefined,\n ),\n );\n syncOps.push(syncOp);\n }\n this.deadLetter.add(...syncOps);\n }\n\n /**\n * Handles polling errors with error classification.\n * Returns true if the error was handled (caller should not rethrow).\n */\n private handlePollError(error: unknown): boolean {\n if (this.isShutdown) return true;\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (err.message.includes(\"Channel not found\")) {\n this.transitionConnectionState(\"reconnecting\");\n this.recoverFromChannelNotFound();\n return true;\n }\n\n const classification = this.classifyError(err);\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n\n this.logger.error(\n \"GqlChannel poll error (@FailureCount, @Classification): @Error\",\n this.failureCount,\n classification,\n channelError,\n );\n\n if (classification === \"unrecoverable\") {\n this.pollTimer.stop();\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n return true;\n }\n\n this.transitionConnectionState(\"error\");\n return false;\n }\n\n /**\n * Recovers from a \"Channel not found\" error by re-registering and restarting polling.\n * Self-retries with backoff instead of restarting the poll timer on failure.\n */\n private recoverFromChannelNotFound(): void {\n if (this.isRecovering) return;\n this.isRecovering = true;\n\n this.logger.info(\n \"GqlChannel @ChannelId not found on remote, re-registering...\",\n this.channelId,\n );\n\n this.pollTimer.stop();\n\n const attemptRecovery = (attempt: number): void => {\n if (this.isShutdown) {\n this.isRecovering = false;\n return;\n }\n\n void this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n this.logger.info(\n \"GqlChannel @ChannelId re-registered successfully\",\n this.channelId,\n );\n this.isRecovering = false;\n this.failureCount = 0;\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n this.resumePushAfterRecovery();\n })\n .catch((recoveryError: unknown) => {\n const err =\n recoveryError instanceof Error\n ? recoveryError\n : new Error(String(recoveryError));\n const classification = this.classifyError(err);\n\n this.logger.error(\n \"GqlChannel @ChannelId recovery attempt @Attempt failed (@Classification): @Error\",\n this.channelId,\n attempt,\n classification,\n recoveryError,\n );\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n if (classification === \"unrecoverable\") {\n this.isRecovering = false;\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n return;\n }\n\n this.transitionConnectionState(\"reconnecting\");\n const delay = calculateBackoffDelay(\n attempt,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n setTimeout(() => attemptRecovery(attempt + 1), delay);\n });\n };\n\n attemptRecovery(1);\n }\n\n /**\n * Resumes pushing outbox items that were blocked while the channel was being\n * recreated. Called after a successful re-registration.\n */\n private resumePushAfterRecovery(): void {\n if (this.isShutdown) return;\n if (!this.pushBlocked) return;\n\n if (this.pushRetryTimer) {\n clearTimeout(this.pushRetryTimer);\n this.pushRetryTimer = null;\n }\n\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n\n const items = this.outbox.items;\n if (items.length > 0) {\n this.attemptPush([...items]);\n }\n }\n\n /**\n * Queries the remote GraphQL endpoint for sync envelopes.\n */\n private async pollSyncEnvelopes(\n ackOrdinal: number,\n latestOrdinal: number,\n ): Promise<{\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters: Array<{\n documentId: string;\n error: string;\n errorType?: string | null;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n }> {\n const variables = {\n channelId: this.channelId,\n outboxAck: ackOrdinal,\n outboxLatest: latestOrdinal,\n };\n\n let response: PollSyncEnvelopesResult;\n try {\n response = await this.executeGraphQL<PollSyncEnvelopesResult>(\n this.pollQuery(this.peerServesDecisionFields),\n variables,\n );\n } catch (error) {\n if (!this.rejectsDecisionFields(error)) {\n throw error;\n }\n this.logger.warn(\n \"Remote @channelId does not serve deniedReason/errorType; polling without them. The remote is on an older schema, so it has neither to report.\",\n this.channelId,\n );\n this.peerServesDecisionFields = false;\n response = await this.executeGraphQL<PollSyncEnvelopesResult>(\n this.pollQuery(false),\n variables,\n );\n }\n\n return {\n envelopes: response.pollSyncEnvelopes.envelopes,\n ackOrdinal: response.pollSyncEnvelopes.ackOrdinal,\n deadLetters: response.pollSyncEnvelopes.deadLetters ?? [],\n hasMore: response.pollSyncEnvelopes.hasMore,\n };\n }\n\n /**\n * True when the remote rejected the query for naming a field it does not\n * have. Selecting an unknown field fails validation for the whole query, so\n * an unhandled one takes the channel's polling down until the process\n * restarts rather than degrading.\n */\n private rejectsDecisionFields(error: unknown): boolean {\n if (!this.peerServesDecisionFields) {\n return false;\n }\n if (\n !(error instanceof GraphQLRequestError) ||\n error.category !== \"graphql\"\n ) {\n return false;\n }\n return DECISION_FIELDS.some((field) => error.message.includes(field));\n }\n\n /**\n * The poll query. `withDecisionFields` selects the two fields added with the\n * auth projection; a remote on the previous schema is polled without them.\n */\n private pollQuery(withDecisionFields: boolean): string {\n const deniedReason = withDecisionFields ? \"deniedReason\" : \"\";\n const errorType = withDecisionFields ? \"errorType\" : \"\";\n\n return `\n query PollSyncEnvelopes($channelId: String!, $outboxAck: Int!, $outboxLatest: Int!) {\n pollSyncEnvelopes(channelId: $channelId, outboxAck: $outboxAck, outboxLatest: $outboxLatest) {\n envelopes {\n type\n channelMeta {\n id\n }\n operations {\n operation {\n index\n timestampUtcMs\n hash\n skip\n error\n ${deniedReason}\n id\n action {\n id\n type\n timestampUtcMs\n input\n scope\n context {\n signer {\n user {\n address\n networkId\n chainId\n }\n app {\n name\n key\n }\n signatures\n }\n }\n }\n }\n context {\n documentId\n documentType\n scope\n branch\n ordinal\n }\n }\n cursor {\n remoteName\n cursorOrdinal\n lastSyncedAtUtcMs\n }\n key\n dependsOn\n }\n ackOrdinal\n deadLetters {\n documentId\n error\n ${errorType}\n jobId\n branch\n scopes\n operationCount\n }\n hasMore\n }\n }\n `;\n }\n\n /**\n * Registers or updates this channel on the remote server via GraphQL mutation.\n * Returns the remote's ack ordinal so the client can trim its outbox.\n */\n private async touchRemoteChannel(): Promise<{ ackOrdinal: number }> {\n let sinceTimestampUtcMs = \"0\";\n try {\n const result = await this.operationIndex.getLatestTimestampForCollection(\n this.config.collectionId.key,\n );\n if (result) {\n sinceTimestampUtcMs = result;\n }\n } catch {\n // If query fails, use default \"0\" (sends all operations)\n }\n\n const mutation = `\n mutation TouchChannel($input: TouchChannelInput!) {\n touchChannel(input: $input) {\n success\n ackOrdinal\n }\n }\n `;\n\n const variables = {\n input: {\n id: this.channelId,\n name: this.channelId,\n collectionId: this.config.collectionId.key,\n filter: {\n documentId: this.config.filter.documentId,\n scope: this.config.filter.scope,\n branch: this.config.filter.branch,\n },\n sinceTimestampUtcMs,\n },\n };\n\n const data = await this.executeGraphQL<{\n touchChannel: { success: boolean; ackOrdinal: number };\n }>(mutation, variables);\n\n if (!data.touchChannel.success) {\n throw new GraphQLRequestError(\n \"touchChannel returned success=false\",\n \"graphql\",\n );\n }\n\n return { ackOrdinal: data.touchChannel.ackOrdinal };\n }\n\n /**\n * Fire-and-forget push with retry on recoverable errors.\n * On success, clears push blocked state. On recoverable error, blocks\n * further pushes and schedules a retry. On unrecoverable error, moves\n * ops to deadLetter.\n */\n private attemptPush(syncOps: SyncOperation[]): void {\n this.isPushing = true;\n this.pushSyncOperations(syncOps)\n .then(() => {\n this.isPushing = false;\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n if (\n this.connectionState === \"reconnecting\" ||\n this.connectionState === \"error\"\n ) {\n this.transitionConnectionState(\"connected\");\n }\n this.drainOutbox();\n })\n .catch((error) => {\n this.isPushing = false;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (err.message.includes(\"Channel not found\")) {\n this.pushBlocked = true; // keep ops in outbox; retried after recovery\n this.transitionConnectionState(\"reconnecting\");\n this.recoverFromChannelNotFound();\n return;\n }\n\n const classification = this.classifyError(err);\n\n if (classification === \"recoverable\") {\n this.pushFailureCount++;\n this.pushBlocked = true;\n this.logger.error(\n \"GqlChannel push failed (attempt @FailureCount), will retry: @Error\",\n this.pushFailureCount,\n err,\n );\n this.transitionConnectionState(\"reconnecting\");\n this.schedulePushRetry();\n } else {\n const channelError = new ChannelError(ChannelErrorSource.Outbox, err);\n for (const syncOp of syncOps) {\n syncOp.failed(channelError);\n }\n this.deadLetter.add(...syncOps);\n this.outbox.remove(...syncOps);\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n }\n });\n }\n\n /**\n * Schedules a retry of all current outbox items using exponential backoff.\n */\n private schedulePushRetry(): void {\n if (this.pushRetryTimer) return;\n\n const delay = calculateBackoffDelay(\n this.pushFailureCount,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n\n this.pushRetryTimer = setTimeout(() => {\n this.pushRetryTimer = null;\n\n if (this.isShutdown) return;\n\n const allItems = this.outbox.items;\n if (allItems.length === 0) {\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n return;\n }\n\n this.attemptPush([...allItems]);\n }, delay);\n }\n\n /**\n * Drains pending outbox items that arrived while a push was in-flight.\n * Server-side action.id dedup handles any overlap with the previous push.\n */\n private drainOutbox(): void {\n if (!this.pendingDrain) return;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n const items = this.outbox.items;\n if (items.length === 0) return;\n this.attemptPush([...items]);\n }\n\n /**\n * Classifies an error as recoverable or unrecoverable based on its type.\n * Recoverable errors are transient and worth retrying (network, 5xx, parse).\n * Unrecoverable errors will not self-heal (auth, client errors, GraphQL rejections).\n */\n private classifyError(error: Error): \"recoverable\" | \"unrecoverable\" {\n if (!(error instanceof GraphQLRequestError)) {\n return \"recoverable\";\n }\n\n switch (error.category) {\n case \"network\":\n return \"recoverable\";\n case \"http\": {\n if (error.statusCode !== undefined && error.statusCode >= 500) {\n return \"recoverable\";\n }\n return \"unrecoverable\";\n }\n case \"parse\":\n return \"recoverable\";\n case \"graphql\":\n // A remote that classified the failure as worth polling through is\n // taken at its word. Everything else stays permanent: it stops the poll\n // timer, and nothing restarts it.\n return isRecoverableGraphQLError(error)\n ? \"recoverable\"\n : \"unrecoverable\";\n case \"missing-data\":\n return \"unrecoverable\";\n }\n }\n\n /**\n * Pushes multiple sync operations to the remote via a single GraphQL mutation.\n * Creates one SyncEnvelope per SyncOperation with key/dependsOn for batch ordering.\n */\n private async pushSyncOperations(syncOps: SyncOperation[]): Promise<void> {\n for (const syncOp of syncOps) {\n syncOp.started();\n }\n\n const jobIdToKeys = new Map<string, string[]>();\n const envelopes: SyncEnvelope[] = [];\n\n for (let i = 0; i < syncOps.length; i++) {\n const syncOp = syncOps[i];\n const key = String(i);\n\n if (syncOp.jobId) {\n if (!jobIdToKeys.has(syncOp.jobId)) {\n jobIdToKeys.set(syncOp.jobId, []);\n }\n jobIdToKeys.get(syncOp.jobId)!.push(key);\n }\n\n const dependsOn: string[] = [];\n for (const dep of syncOp.jobDependencies) {\n const depKeys = jobIdToKeys.get(dep);\n if (depKeys) {\n dependsOn.push(...depKeys);\n }\n }\n\n this.logger.debug(\n \"[PUSH]: @Operations\",\n syncOp.operations.map(\n (op) =>\n `(${op.context.documentId}, ${op.context.branch}, ${op.context.scope}, ${op.operation.index})`,\n ),\n );\n\n envelopes.push({\n type: \"operations\",\n channelMeta: { id: this.channelId },\n operations: syncOp.operations,\n key,\n dependsOn,\n });\n }\n\n const mutation = `\n mutation PushSyncEnvelopes($envelopes: [SyncEnvelopeInput!]!) {\n pushSyncEnvelopes(envelopes: $envelopes)\n }\n `;\n\n const variables = {\n envelopes: envelopes.map((e) => serializeEnvelope(e)),\n };\n\n await this.executeGraphQL<{ pushSyncEnvelopes: boolean }>(\n mutation,\n variables,\n );\n }\n\n /**\n * Gets the authorization header value using jwtHandler.\n */\n private async getAuthorizationHeader(): Promise<string | undefined> {\n if (!this.config.jwtHandler) {\n return undefined;\n }\n\n try {\n const token = await this.config.jwtHandler(this.config.url);\n if (token) {\n return `Bearer ${token}`;\n }\n } catch (error) {\n this.logger.error(\"JWT handler failed: @Error\", error);\n }\n return undefined;\n }\n\n /**\n * Executes a GraphQL query or mutation against the remote endpoint.\n */\n private async executeGraphQL<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n const authHeader = await this.getAuthorizationHeader();\n if (authHeader) {\n headers[\"Authorization\"] = authHeader;\n }\n\n const operationMatch = query.match(/(?:query|mutation)\\s+(\\w+)/);\n const operationName = operationMatch?.[1] ?? \"unknown\";\n\n this.logger.verbose(\n \"GQL request @channelId @operation @url vars=@variables\",\n this.channelId,\n operationName,\n this.config.url,\n JSON.stringify(variables),\n );\n\n const fetchFn = this.config.fetchFn ?? fetch;\n let response;\n try {\n response = await fetchFn(this.config.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query,\n variables,\n }),\n signal: this.abortController.signal,\n });\n } catch (error) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${error instanceof Error ? error.message : String(error)}`,\n \"network\",\n );\n }\n\n if (!response.ok) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${response.status} ${response.statusText}`,\n \"http\",\n response.status,\n );\n }\n\n let result;\n try {\n result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string; extensions?: { code?: string } }>;\n };\n } catch (error) {\n throw new GraphQLRequestError(\n `Failed to parse GraphQL response: ${error instanceof Error ? error.message : String(error)}`,\n \"parse\",\n );\n }\n\n this.logger.verbose(\n \"GQL response @channelId @operation status=@status data=@data errors=@errors\",\n this.channelId,\n operationName,\n response.status,\n JSON.stringify(result.data),\n result.errors ? JSON.stringify(result.errors) : \"none\",\n );\n\n if (result.errors) {\n throw new GraphQLRequestError(\n `GraphQL errors: ${JSON.stringify(result.errors, null, 2)}`,\n \"graphql\",\n undefined,\n result.errors.map((error) => error.extensions?.code),\n );\n }\n\n if (!result.data) {\n throw new GraphQLRequestError(\n \"GraphQL response missing data field\",\n \"missing-data\",\n );\n }\n\n return result.data;\n }\n\n get poller(): IPollTimer {\n return this.pollTimer;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../../cache/operation-index-types.js\";\nimport type { IQueue } from \"../../queue/interfaces.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type {\n ChannelConfig,\n JwtHandler,\n RemoteFilter,\n RemoteOptions,\n} from \"../types.js\";\nimport { PollBehavior } from \"../types.js\";\nimport { GqlRequestChannel, type GqlChannelConfig } from \"./gql-req-channel.js\";\nimport { IntervalPollTimer } from \"./interval-poll-timer.js\";\n\n/**\n * Factory for creating GqlRequestChannel instances.\n *\n * Extracts GraphQL-specific configuration from ChannelConfig.parameters and\n * instantiates GqlRequestChannel instances for network-based synchronization.\n *\n * The optional jwtHandler enables dynamic JWT token generation per-request,\n * which is useful for short-lived tokens with audience-specific claims.\n */\nexport class GqlRequestChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n private readonly jwtHandler?: JwtHandler;\n private readonly queue: IQueue;\n\n constructor(\n logger: ILogger,\n jwtHandler: JwtHandler | undefined,\n queue: IQueue,\n ) {\n this.logger = logger;\n this.jwtHandler = jwtHandler;\n this.queue = queue;\n }\n\n /**\n * Creates a new GqlRequestChannel instance with the given configuration.\n * See GqlChannelConfig for the expected parameters.\n *\n * @param config - Channel configuration including type and parameters\n * @param cursorStorage - Storage for persisting synchronization cursors\n * @param operationIndex - Operation index for querying timestamps\n * @returns A new GqlRequestChannel instance\n */\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n collectionId: DriveCollectionId,\n filter: RemoteFilter,\n operationIndex: IOperationIndex,\n options?: RemoteOptions,\n ): IChannel {\n // Extract and validate required parameters\n const url = config.parameters.url;\n if (typeof url !== \"string\" || !url) {\n throw new Error(\n 'GqlRequestChannelFactory requires \"url\" parameter in config.parameters',\n );\n }\n\n // Extract optional parameters with validation\n const gqlConfig: GqlChannelConfig = {\n url,\n collectionId,\n filter,\n jwtHandler: this.jwtHandler,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n };\n\n let pollIntervalMs = 2000;\n if (config.parameters.pollIntervalMs !== undefined) {\n if (typeof config.parameters.pollIntervalMs !== \"number\") {\n throw new Error('\"pollIntervalMs\" parameter must be a number');\n }\n pollIntervalMs = config.parameters.pollIntervalMs;\n }\n\n let retryBaseDelayMs: number | undefined;\n if (config.parameters.retryBaseDelayMs !== undefined) {\n if (typeof config.parameters.retryBaseDelayMs !== \"number\") {\n throw new Error('\"retryBaseDelayMs\" parameter must be a number');\n }\n retryBaseDelayMs = config.parameters.retryBaseDelayMs;\n }\n\n let retryMaxDelayMs: number | undefined;\n if (config.parameters.retryMaxDelayMs !== undefined) {\n if (typeof config.parameters.retryMaxDelayMs !== \"number\") {\n throw new Error('\"retryMaxDelayMs\" parameter must be a number');\n }\n retryMaxDelayMs = config.parameters.retryMaxDelayMs;\n }\n\n if (config.parameters.fetchFn !== undefined) {\n if (typeof config.parameters.fetchFn !== \"function\") {\n throw new Error('\"fetchFn\" parameter must be a function');\n }\n gqlConfig.fetchFn = config.parameters.fetchFn as typeof fetch;\n }\n\n if (retryBaseDelayMs !== undefined) {\n gqlConfig.retryBaseDelayMs = retryBaseDelayMs;\n }\n if (retryMaxDelayMs !== undefined) {\n gqlConfig.retryMaxDelayMs = retryMaxDelayMs;\n }\n\n let maxQueueDepth: number | undefined;\n if (config.parameters.maxQueueDepth !== undefined) {\n if (typeof config.parameters.maxQueueDepth !== \"number\") {\n throw new Error('\"maxQueueDepth\" parameter must be a number');\n }\n maxQueueDepth = config.parameters.maxQueueDepth;\n }\n\n let backpressureCheckIntervalMs: number | undefined;\n if (config.parameters.backpressureCheckIntervalMs !== undefined) {\n if (typeof config.parameters.backpressureCheckIntervalMs !== \"number\") {\n throw new Error(\n '\"backpressureCheckIntervalMs\" parameter must be a number',\n );\n }\n backpressureCheckIntervalMs =\n config.parameters.backpressureCheckIntervalMs;\n }\n\n const pollTimer = new IntervalPollTimer(this.queue, {\n intervalMs: pollIntervalMs,\n ...(retryBaseDelayMs !== undefined && { retryBaseDelayMs }),\n ...(retryMaxDelayMs !== undefined && { retryMaxDelayMs }),\n ...(maxQueueDepth !== undefined && { maxQueueDepth }),\n ...(backpressureCheckIntervalMs !== undefined && {\n backpressureCheckIntervalMs,\n }),\n startPaused: options?.pollBehavior === PollBehavior.Manual,\n });\n\n return new GqlRequestChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n gqlConfig,\n operationIndex,\n pollTimer,\n );\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { ConnectionStateChangeCallback, IChannel } from \"../interfaces.js\";\nimport { Mailbox } from \"../mailbox.js\";\nimport type { SyncOperation } from \"../sync-operation.js\";\nimport type { ConnectionState, ConnectionStateSnapshot } from \"../types.js\";\nimport { SyncOperationStatus } from \"../types.js\";\nimport { getLatestAppliedOrdinal } from \"./utils.js\";\n\n/** Where a sync operation's run of ordinals begins. */\nfunction firstOrdinalOf(syncOp: SyncOperation): number {\n return syncOp.operations.length > 0\n ? syncOp.operations[0].context.ordinal\n : 0;\n}\n\n/**\n * This class is used server-side to accumulate inbox + outbox operations.\n *\n * In general, the resolvers are responsible for updating mailboxes.\n */\nexport class GqlResponseChannel implements IChannel {\n readonly inbox: Mailbox;\n readonly outbox: Mailbox;\n readonly deadLetter: Mailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private isShutdown: boolean;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private evictedOutboxFloor: number = Number.POSITIVE_INFINITY;\n private appliedOutboxOrdinal: number = 0;\n private lastPollUtcMs: number = Date.now();\n private connectionState: ConnectionState = \"connecting\";\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.isShutdown = false;\n\n this.inbox = new Mailbox();\n this.outbox = new Mailbox();\n this.deadLetter = new Mailbox();\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n this.rememberUnserved(syncOps);\n this.persistOutboxCursor(getLatestAppliedOrdinal(syncOps));\n });\n\n // An entry arriving at or below the evicted floor is the re-derivation of\n // what was evicted: it is queued again, so the queue itself now holds the\n // floor down and the remembered one would only pin the cursor for good.\n this.outbox.onAdded((syncOps) => {\n this.forgetEvictedBelow(syncOps);\n });\n\n this.inbox.onRemoved((syncOps) => {\n const maxOrdinal = getLatestAppliedOrdinal(syncOps);\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n shutdown(): Promise<void> {\n this.isShutdown = true;\n this.transitionConnectionState(\"disconnected\");\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: 0,\n lastSuccessUtcMs: this.lastPollUtcMs,\n lastFailureUtcMs: 0,\n pushBlocked: false,\n pushFailureCount: 0,\n receivingPages: false,\n requiresAuth: false,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n /** Response channels are push-driven; resolvers populate mailboxes directly. */\n triggerPull(): void {}\n\n notePoll(): void {\n this.lastPollUtcMs = Date.now();\n }\n\n /** This channel is served: its holder's polls are the liveness it reports. */\n lastHolderPollUtcMs(): number | undefined {\n return this.lastPollUtcMs;\n }\n\n async init(): Promise<void> {\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n this.appliedOutboxOrdinal = outboxOrdinal;\n this.transitionConnectionState(\"connected\");\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n /**\n * Records the ordinals of entries that left the outbox without being served,\n * so the cursor cannot advance past them.\n *\n * An entry can leave unserved because a bound evicted it, and an evicted entry\n * is exactly one this channel intends to re-derive: it is still owed to the\n * remote. Remembering the floor across the whole run rather than only while\n * the entry is present is what makes that true after a later ack would\n * otherwise have swept the cursor past it.\n */\n private rememberUnserved(syncOps: SyncOperation[]): void {\n for (const syncOp of syncOps) {\n if (syncOp.status === SyncOperationStatus.Applied) continue;\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first < this.evictedOutboxFloor) {\n this.evictedOutboxFloor = first;\n }\n }\n }\n\n /**\n * Persists the outbox cursor, never past an operation this remote has not\n * been served.\n *\n * The cursor is where a restart resumes deriving the outbox from, so an\n * ordinal persisted past an unserved entry loses that entry for good: the\n * rebuild starts beyond it and nothing else remembers it was owed. Acks\n * arrive out of order with respect to what is withheld -- a later entry can\n * be acknowledged while an earlier one is still being withheld from this\n * subject -- so the applied high-water mark alone is not a safe cursor.\n */\n private persistOutboxCursor(appliedOrdinal: number): void {\n this.appliedOutboxOrdinal = Math.max(\n this.appliedOutboxOrdinal,\n appliedOrdinal,\n );\n const ordinal = Math.min(\n this.appliedOutboxOrdinal,\n this.unservedFloor() - 1,\n );\n if (ordinal <= this.lastPersistedOutboxOrdinal) {\n return;\n }\n\n this.lastPersistedOutboxOrdinal = ordinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: ordinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n\n /** Drops the evicted floor once the entries it stood for are queued again. */\n private forgetEvictedBelow(syncOps: SyncOperation[]): void {\n if (this.evictedOutboxFloor === Number.POSITIVE_INFINITY) {\n return;\n }\n\n for (const syncOp of syncOps) {\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first <= this.evictedOutboxFloor) {\n this.evictedOutboxFloor = Number.POSITIVE_INFINITY;\n return;\n }\n }\n }\n\n /** The lowest ordinal still owed to this remote, evicted or still queued. */\n private unservedFloor(): number {\n let floor = this.evictedOutboxFloor;\n for (const syncOp of this.outbox.items) {\n if (syncOp.status === SyncOperationStatus.Applied) continue;\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first < floor) {\n floor = first;\n }\n }\n return floor;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type { ChannelConfig } from \"../types.js\";\nimport { GqlResponseChannel } from \"./gql-res-channel.js\";\n\n/**\n * Factory for creating GqlResponseChannel instances.\n */\nexport class GqlResponseChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n\n constructor(logger: ILogger) {\n this.logger = logger;\n }\n\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n ): IChannel {\n return new GqlResponseChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n );\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { RemoteCursor } from \"../../sync/types.js\";\nimport type { ISyncCursorStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncCursor, SyncCursorRow } from \"./types.js\";\n\nfunction rowToRemoteCursor(row: SyncCursorRow): RemoteCursor {\n return {\n remoteName: row.remote_name,\n cursorType: row.cursor_type as \"inbox\" | \"outbox\",\n cursorOrdinal: Number(row.cursor_ordinal),\n lastSyncedAtUtcMs: row.last_synced_at_utc_ms\n ? new Date(row.last_synced_at_utc_ms).getTime()\n : undefined,\n };\n}\n\nfunction remoteCursorToRow(cursor: RemoteCursor): InsertableSyncCursor {\n return {\n remote_name: cursor.remoteName,\n cursor_type: cursor.cursorType,\n cursor_ordinal: BigInt(cursor.cursorOrdinal),\n last_synced_at_utc_ms: cursor.lastSyncedAtUtcMs\n ? new Date(cursor.lastSyncedAtUtcMs).toISOString()\n : null,\n };\n}\n\nexport class KyselySyncCursorStorage implements ISyncCursorStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<RemoteCursor[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteCursor);\n }\n\n async get(\n remoteName: string,\n cursorType: \"inbox\" | \"outbox\",\n signal?: AbortSignal,\n ): Promise<RemoteCursor> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .where(\"cursor_type\", \"=\", cursorType)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n return {\n remoteName,\n cursorType,\n cursorOrdinal: 0,\n };\n }\n\n return rowToRemoteCursor(row);\n }\n\n async upsert(cursor: RemoteCursor, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteCursorToRow(cursor);\n\n await trx\n .insertInto(\"sync_cursors\")\n .values(insertable)\n .onConflict((oc) =>\n oc.columns([\"remote_name\", \"cursor_type\"]).doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(remoteName: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_cursors\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { PagingOptions, PagedResults } from \"../../shared/types.js\";\nimport type {\n ChannelErrorSource,\n SyncOperationErrorType,\n} from \"../../sync/types.js\";\nimport { quarantinesDocument } from \"../../sync/utils.js\";\nimport type { DeadLetterRecord } from \"../interfaces.js\";\nimport type { ISyncDeadLetterStorage } from \"../interfaces.js\";\nimport type {\n Database,\n InsertableSyncDeadLetter,\n SyncDeadLetterRow,\n} from \"./types.js\";\n\nfunction rowToDeadLetterRecord(row: SyncDeadLetterRow): DeadLetterRecord {\n return {\n id: row.id,\n jobId: row.job_id,\n jobDependencies: row.job_dependencies as string[],\n remoteName: row.remote_name,\n documentId: row.document_id,\n scopes: row.scopes as string[],\n branch: row.branch,\n operations: row.operations as OperationWithContext[],\n errorSource: row.error_source as ChannelErrorSource,\n errorMessage: row.error_message,\n // Coalesced as well as defaulted, so a pre-existing row rehydrates either way.\n errorType: (row.error_type ?? \"UNCLASSIFIED\") as SyncOperationErrorType,\n };\n}\n\nfunction deadLetterRecordToRow(\n record: DeadLetterRecord,\n): InsertableSyncDeadLetter {\n return {\n id: record.id,\n job_id: record.jobId,\n job_dependencies: JSON.stringify(record.jobDependencies),\n remote_name: record.remoteName,\n document_id: record.documentId,\n scopes: JSON.stringify(record.scopes),\n branch: record.branch,\n operations: JSON.stringify(record.operations),\n error_source: record.errorSource,\n error_message: record.errorMessage,\n error_type: record.errorType,\n };\n}\n\n/**\n * PGlite/Kysely-backed implementation of {@link ISyncDeadLetterStorage}.\n */\nexport class KyselySyncDeadLetterStorage implements ISyncDeadLetterStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DeadLetterRecord>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .orderBy(\"ordinal\", \"desc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n let hasMore = false;\n let items = rows;\n if (paging?.limit && rows.length > limit) {\n hasMore = true;\n items = rows.slice(0, limit);\n }\n\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n const cursor = paging?.cursor || \"0\";\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return {\n results: items.map(rowToDeadLetterRecord),\n options: { cursor, limit },\n nextCursor,\n };\n }\n\n async add(deadLetter: DeadLetterRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = deadLetterRecordToRow(deadLetter);\n\n await trx\n .insertInto(\"sync_dead_letters\")\n .values(insertable)\n .onConflict((oc) => oc.column(\"id\").doNothing())\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(id: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_dead_letters\").where(\"id\", \"=\", id).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async removeByRemote(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_dead_letters\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async listQuarantinedDocumentIds(signal?: AbortSignal): Promise<string[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .select([\"document_id\", \"error_type\"])\n .distinct()\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n // A document whose only dead letters are held auth operations keeps syncing\n // across a restart too.\n const quarantined = new Set<string>();\n for (const row of rows) {\n const errorType = (row.error_type ??\n \"UNCLASSIFIED\") as SyncOperationErrorType;\n if (quarantinesDocument(errorType)) {\n quarantined.add(row.document_id);\n }\n }\n\n return [...quarantined];\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport { DriveCollectionId } from \"../../cache/operation-index-types.js\";\nimport type { RemoteRecord } from \"../../sync/types.js\";\nimport type { ISyncRemoteStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncRemote, SyncRemoteRow } from \"./types.js\";\n\nfunction rowToRemoteRecord(row: SyncRemoteRow): RemoteRecord {\n return {\n id: row.channel_id,\n name: row.name,\n collectionId: DriveCollectionId.fromKey(row.collection_id),\n channelConfig: {\n type: row.channel_type,\n parameters: (row.channel_parameters ?? {}) as Record<string, unknown>,\n },\n filter: {\n documentId: (row.filter_document_ids ?? []) as string[],\n scope: (row.filter_scopes ?? []) as string[],\n branch: row.filter_branch,\n },\n options: {\n sinceTimestampUtcMs: \"0\",\n boundAddress: row.bound_address ?? undefined,\n },\n status: {\n push: {\n state: row.push_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.push_last_success_utc_ms\n ? new Date(row.push_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.push_last_failure_utc_ms\n ? new Date(row.push_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.push_failure_count,\n },\n pull: {\n state: row.pull_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.pull_last_success_utc_ms\n ? new Date(row.pull_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.pull_last_failure_utc_ms\n ? new Date(row.pull_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.pull_failure_count,\n },\n },\n };\n}\n\n/**\n * Maps a RemoteRecord to a row. jsonb array columns are JSON-encoded because the\n * pg driver would otherwise emit a Postgres array literal (\"{a,b}\"), which jsonb\n * rejects (matches sync-dead-letter-storage).\n */\nfunction remoteRecordToRow(remote: RemoteRecord): InsertableSyncRemote {\n return {\n name: remote.name,\n collection_id: remote.collectionId.key,\n channel_type: remote.channelConfig.type,\n channel_id: remote.id,\n remote_name: remote.name,\n channel_parameters: remote.channelConfig.parameters,\n filter_document_ids:\n remote.filter.documentId.length > 0\n ? JSON.stringify(remote.filter.documentId)\n : null,\n filter_scopes:\n remote.filter.scope.length > 0\n ? JSON.stringify(remote.filter.scope)\n : null,\n filter_branch: remote.filter.branch,\n push_state: remote.status.push.state,\n push_last_success_utc_ms: remote.status.push.lastSuccessUtcMs\n ? new Date(remote.status.push.lastSuccessUtcMs).toISOString()\n : null,\n push_last_failure_utc_ms: remote.status.push.lastFailureUtcMs\n ? new Date(remote.status.push.lastFailureUtcMs).toISOString()\n : null,\n push_failure_count: remote.status.push.failureCount,\n pull_state: remote.status.pull.state,\n pull_last_success_utc_ms: remote.status.pull.lastSuccessUtcMs\n ? new Date(remote.status.pull.lastSuccessUtcMs).toISOString()\n : null,\n pull_last_failure_utc_ms: remote.status.pull.lastFailureUtcMs\n ? new Date(remote.status.pull.lastFailureUtcMs).toISOString()\n : null,\n pull_failure_count: remote.status.pull.failureCount,\n bound_address: remote.options.boundAddress ?? null,\n };\n}\n\nexport class KyselySyncRemoteStorage implements ISyncRemoteStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(signal?: AbortSignal): Promise<RemoteRecord[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db.selectFrom(\"sync_remotes\").selectAll().execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteRecord);\n }\n\n async get(name: string, signal?: AbortSignal): Promise<RemoteRecord> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_remotes\")\n .selectAll()\n .where(\"name\", \"=\", name)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n throw new Error(`Remote not found: ${name}`);\n }\n\n return rowToRemoteRecord(row);\n }\n\n async upsert(remote: RemoteRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteRecordToRow(remote);\n\n await trx\n .insertInto(\"sync_remotes\")\n .values(insertable)\n .onConflict((oc) =>\n oc.column(\"name\").doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(name: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_remotes\").where(\"name\", \"=\", name).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { ILogger } from \"document-model\";\nimport { DriveCollectionId } from \"../cache/operation-index-types.js\";\nimport type { JobFailedEvent, JobWriteReadyEvent } from \"../events/types.js\";\n\nexport type PreparedBatch = {\n /** Document ID -> Collection IDs that they are a part of */\n collectionMemberships: Record<string, string[]>;\n entries: Array<{\n event: JobWriteReadyEvent;\n jobDependencies: string[];\n }>;\n};\n\ntype PendingBatch = {\n expectedJobIds: Set<string>;\n arrivedJobIds: Set<string>;\n events: JobWriteReadyEvent[];\n};\n\nexport class BatchAggregator {\n private readonly logger: ILogger;\n private readonly driveContainerTypes: ReadonlySet<string>;\n private readonly onBatchReady: (batch: PreparedBatch) => Promise<void>;\n private queue: JobWriteReadyEvent[] = [];\n private processing: boolean = false;\n private readonly pendingBatches: Map<string, PendingBatch> = new Map();\n\n constructor(\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n onBatchReady: (batch: PreparedBatch) => Promise<void>,\n ) {\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\n this.onBatchReady = onBatchReady;\n }\n\n async enqueueWriteReady(event: JobWriteReadyEvent): Promise<void> {\n this.queue.push(event);\n await this.processQueue();\n }\n\n async handleJobFailed(event: JobFailedEvent): Promise<void> {\n const batchId = event.job?.meta.batchId;\n if (!batchId) {\n return;\n }\n\n const pending = this.pendingBatches.get(batchId);\n if (!pending) {\n return;\n }\n\n this.pendingBatches.delete(batchId);\n if (pending.events.length > 0) {\n await this.onBatchReady(this.prepareBatch(pending.events));\n }\n }\n\n clear(): void {\n this.queue = [];\n this.pendingBatches.clear();\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing) {\n return;\n }\n this.processing = true;\n\n try {\n while (this.queue.length > 0) {\n const event = this.queue.shift()!;\n try {\n await this.handleWriteReady(event);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to process write-ready event (@jobId, @error)\",\n event.jobId,\n err.message,\n );\n }\n }\n } finally {\n this.processing = false;\n }\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const { batchId, batchJobIds } = event.jobMeta;\n\n if (batchJobIds.length <= 1) {\n await this.onBatchReady(this.prepareBatch([event]));\n return;\n }\n\n let pending = this.pendingBatches.get(batchId);\n if (!pending) {\n pending = {\n expectedJobIds: new Set(batchJobIds),\n arrivedJobIds: new Set(),\n events: [],\n };\n this.pendingBatches.set(batchId, pending);\n }\n\n pending.arrivedJobIds.add(event.jobId);\n pending.events.push(event);\n\n if (pending.arrivedJobIds.size >= pending.expectedJobIds.size) {\n this.pendingBatches.delete(batchId);\n await this.onBatchReady(this.prepareBatch(pending.events));\n }\n }\n\n private prepareBatch(events: JobWriteReadyEvent[]): PreparedBatch {\n const collectionMemberships = this.mergeCollectionMemberships(events);\n const isBatch = events.length > 1;\n const priorJobIds: string[] = [];\n const entries: PreparedBatch[\"entries\"] = [];\n\n for (const event of events) {\n entries.push({\n event,\n jobDependencies: isBatch ? [...priorJobIds] : [],\n });\n\n if (isBatch && event.jobId) {\n priorJobIds.push(event.jobId);\n }\n }\n\n return { collectionMemberships, entries };\n }\n\n private mergeCollectionMemberships(\n events: JobWriteReadyEvent[],\n ): Record<string, string[]> {\n const mergedMemberships: Record<string, string[]> = {};\n\n for (const event of events) {\n if (event.collectionMemberships) {\n for (const [docId, collections] of Object.entries(\n event.collectionMemberships,\n )) {\n if (!(docId in mergedMemberships)) {\n mergedMemberships[docId] = [];\n }\n for (const c of collections) {\n if (!mergedMemberships[docId].includes(c)) {\n mergedMemberships[docId].push(c);\n }\n }\n }\n }\n\n for (const op of event.operations) {\n const action = op.operation.action as {\n type: string;\n input?: { sourceId?: string; targetId?: string };\n };\n if (action.type !== \"ADD_RELATIONSHIP\") {\n continue;\n }\n if (!this.driveContainerTypes.has(op.context.documentType)) {\n continue;\n }\n const input = action.input;\n if (!input?.sourceId || !input.targetId) {\n continue;\n }\n\n const collectionId = DriveCollectionId.forDrive(\n input.sourceId,\n op.context.branch,\n ).key;\n if (!(input.targetId in mergedMemberships)) {\n mergedMemberships[input.targetId] = [];\n }\n if (!mergedMemberships[input.targetId].includes(collectionId)) {\n mergedMemberships[input.targetId].push(collectionId);\n }\n }\n }\n\n return mergedMemberships;\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport type { Unsubscribe } from \"../events/types.js\";\nimport {\n SyncEventTypes,\n type SyncFailedEvent,\n type SyncResult,\n type SyncSucceededEvent,\n} from \"./types.js\";\n\ntype SyncWaiter = {\n resolve: (value: SyncResult) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Provides a promise-based interface for waiting on sync completion.\n * Subscribes to sync events at construction and tracks completed sync results\n * to provide a fast path for jobs that have already synced.\n */\nexport class SyncAwaiter {\n private readonly completedResults = new Map<string, SyncResult>();\n private readonly pendingWaiters = new Map<string, SyncWaiter[]>();\n private readonly unsubscribers: Unsubscribe[] = [];\n private isShutdown = false;\n\n constructor(private readonly eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n /**\n * Waits for sync operations for a job to complete.\n * Resolves when SYNC_SUCCEEDED is emitted.\n * Rejects when SYNC_FAILED is emitted.\n *\n * @param jobId - The job id to wait for\n * @param signal - Optional abort signal\n * @returns The sync result\n */\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isShutdown) {\n return Promise.reject(new Error(\"SyncAwaiter is shutdown\"));\n }\n\n const completedResult = this.completedResults.get(jobId);\n if (completedResult) {\n return Promise.resolve(completedResult);\n }\n\n return new Promise<SyncResult>((resolve, reject) => {\n const waiter: SyncWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingWaiters.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingWaiters.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingWaiters.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingWaiters.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n }\n\n /**\n * Shuts down the sync awaiter. This will synchronously reject all pending waiters.\n */\n shutdown(): void {\n this.isShutdown = true;\n\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers.length = 0;\n\n for (const [, waiters] of this.pendingWaiters) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"SyncAwaiter shutdown\"));\n }\n }\n this.pendingWaiters.clear();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncSucceededEvent>(\n SyncEventTypes.SYNC_SUCCEEDED,\n (_type, event) => {\n this.handleSyncSucceeded(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncFailedEvent>(\n SyncEventTypes.SYNC_FAILED,\n (_type, event) => {\n this.handleSyncFailed(event);\n },\n ),\n );\n }\n\n private handleSyncSucceeded(event: SyncSucceededEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"succeeded\",\n syncOperationCount: event.syncOperationCount,\n successCount: event.syncOperationCount,\n failureCount: 0,\n errors: [],\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private handleSyncFailed(event: SyncFailedEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"failed\",\n syncOperationCount: event.successCount + event.failureCount,\n successCount: event.successCount,\n failureCount: event.failureCount,\n errors: event.errors,\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private resolveWaiters(jobId: string, result: SyncResult): void {\n const waiters = this.pendingWaiters.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n this.pendingWaiters.delete(jobId);\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n } else {\n waiter.resolve(result);\n }\n }\n }\n}\n","import type { IChannel } from \"./interfaces.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\n\nexport enum SyncStatus {\n Synced = \"SYNCED\",\n Outgoing = \"OUTGOING\",\n Incoming = \"INCOMING\",\n OutgoingAndIncoming = \"OUTGOING_AND_INCOMING\",\n Error = \"ERROR\",\n}\n\nexport type SyncStatusChangeCallback = (\n documentId: string,\n status: SyncStatus,\n) => void;\n\nexport interface ISyncStatusTracker {\n getStatus(documentId: string): SyncStatus | undefined;\n onChange(callback: SyncStatusChangeCallback): () => void;\n trackRemote(remoteName: string, channel: IChannel): void;\n untrackRemote(remoteName: string): void;\n clear(): void;\n}\n\ntype DocumentCounts = {\n inboxCount: number;\n outboxCount: number;\n errorCount: number;\n};\n\ntype MailboxType = \"inbox\" | \"outbox\" | \"deadLetter\";\n\nexport class SyncStatusTracker implements ISyncStatusTracker {\n private readonly remotes: Map<string, Map<string, DocumentCounts>> =\n new Map();\n private readonly seen: Set<string> = new Set();\n private readonly callbacks: Set<SyncStatusChangeCallback> = new Set();\n\n getStatus(documentId: string): SyncStatus | undefined {\n if (!this.seen.has(documentId)) {\n return undefined;\n }\n\n let totalInbox = 0;\n let totalOutbox = 0;\n let totalErrors = 0;\n\n for (const documents of this.remotes.values()) {\n const counts = documents.get(documentId);\n if (counts) {\n totalInbox += counts.inboxCount;\n totalOutbox += counts.outboxCount;\n totalErrors += counts.errorCount;\n }\n }\n\n return deriveStatus(totalInbox, totalOutbox, totalErrors);\n }\n\n onChange(callback: SyncStatusChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n trackRemote(remoteName: string, channel: IChannel): void {\n this.remotes.set(remoteName, new Map());\n\n channel.inbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"inbox\", syncOps),\n );\n channel.inbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"inbox\", syncOps),\n );\n channel.outbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"outbox\", syncOps),\n );\n channel.outbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"outbox\", syncOps),\n );\n channel.deadLetter.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"deadLetter\", syncOps),\n );\n }\n\n untrackRemote(remoteName: string): void {\n const documents = this.remotes.get(remoteName);\n if (!documents) {\n return;\n }\n\n const affectedDocumentIds = [...documents.keys()];\n this.remotes.delete(remoteName);\n\n for (const documentId of affectedDocumentIds) {\n this.notifyChange(documentId);\n }\n }\n\n clear(): void {\n this.remotes.clear();\n this.seen.clear();\n this.callbacks.clear();\n }\n\n private handleAdded(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n if (mailboxType === \"inbox\" && !syncOp.remoteName) {\n continue;\n }\n\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n this.seen.add(syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount++;\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount++;\n } else {\n counts.errorCount++;\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private handleRemoved(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount = Math.max(0, counts.inboxCount - 1);\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount = Math.max(0, counts.outboxCount - 1);\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private getOrCreateCounts(\n remoteName: string,\n documentId: string,\n ): DocumentCounts {\n let documents = this.remotes.get(remoteName);\n if (!documents) {\n documents = new Map();\n this.remotes.set(remoteName, documents);\n }\n\n let counts = documents.get(documentId);\n if (!counts) {\n counts = { inboxCount: 0, outboxCount: 0, errorCount: 0 };\n documents.set(documentId, counts);\n }\n\n return counts;\n }\n\n private notifyChange(documentId: string): void {\n const status = this.getStatus(documentId);\n if (status === undefined) {\n return;\n }\n\n for (const callback of [...this.callbacks]) {\n callback(documentId, status);\n }\n }\n}\n\nfunction deriveStatus(\n inboxCount: number,\n outboxCount: number,\n errorCount: number,\n): SyncStatus {\n if (errorCount > 0) {\n return SyncStatus.Error;\n }\n if (inboxCount > 0 && outboxCount > 0) {\n return SyncStatus.OutgoingAndIncoming;\n }\n if (inboxCount > 0) {\n return SyncStatus.Incoming;\n }\n if (outboxCount > 0) {\n return SyncStatus.Outgoing;\n }\n return SyncStatus.Synced;\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../cache/operation-index-types.js\";\nimport type {\n BatchLoadRequest,\n BatchLoadResult,\n IReactor,\n} from \"../core/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobWriteReadyEvent,\n} from \"../events/types.js\";\nimport { JobAwaiter } from \"../shared/awaiter.js\";\nimport {\n JobStatus,\n type ErrorInfo,\n type ShutdownStatus,\n} from \"../shared/types.js\";\nimport type {\n DeadLetterRecord,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n} from \"../storage/interfaces.js\";\nimport { BatchAggregator, type PreparedBatch } from \"./batch-aggregator.js\";\nimport {\n ChannelError,\n GraphQLRequestError,\n isDriveAuthError,\n} from \"./errors.js\";\nimport type { IChannelFactory, ISyncManager, Remote } from \"./interfaces.js\";\nimport { SyncAwaiter } from \"./sync-awaiter.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncStatusTracker,\n type SyncStatus,\n type SyncStatusChangeCallback,\n} from \"./sync-status-tracker.js\";\nimport type {\n ChannelConfig,\n ConnectionStateChangedEvent,\n DeadLetterAddedEvent,\n RemoteFilter,\n RemoteOptions,\n RemoteRecord,\n RemoteStatus,\n SyncResult,\n} from \"./types.js\";\nimport {\n ChannelErrorSource,\n SyncEventTypes,\n SyncOperationStatus,\n} from \"./types.js\";\nimport {\n batchOperationsByDocument,\n chunkSyncOperations,\n classifyJobFailure,\n createIdleHealth,\n filterOperations,\n quarantinesDocument,\n splitTrailingSameTimestampRun,\n syncOperationErrorType,\n toOperationWithContext,\n trimMailboxFromBatch,\n} from \"./utils.js\";\nimport type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\n\nexport type SyncManagerConfig = {\n maxDeadLettersPerRemote: number;\n maxInboxBatchSize: number;\n /**\n * How many operations may sit in one remote's outbox waiting to be taken.\n *\n * Serving can withhold an entry indefinitely -- a subject whose policy never\n * widens is served that entry never -- so without a bound the outbox is a leak\n * with a policy behind it. Past the cap the newest entries are evicted rather\n * than the oldest, which keeps what remains a contiguous run from the cursor,\n * and evicting is not dropping: the cursor is held below the evicted floor, so\n * the next refill derives them again from the operation index.\n */\n maxHeldOperationsPerRemote: number;\n /**\n * How long a serving channel may go unpolled before an outbox past its bound\n * is removed rather than evicted.\n *\n * Evicting a channel nobody polls derives the same operations again on every\n * batch, forever. Removing one that is merely behind is worse: a holder more\n * than the bound behind could never catch up, so it is removed only once its\n * holder has stopped asking.\n */\n staleRemotePollWindowMs: number;\n};\n\nenum OutboxMode {\n Backfill = \"backfill\",\n BatchTriggered = \"batch-triggered\",\n}\n\nconst defaultSyncManagerConfig: SyncManagerConfig = {\n maxDeadLettersPerRemote: 100,\n maxInboxBatchSize: 32,\n maxHeldOperationsPerRemote: 10000,\n staleRemotePollWindowMs: 5 * 60_000,\n};\n\nconst PLAN_KEY_TO_JOB_UUID_CAP = 10000;\n\n/**\n * Whether a channel failure says the caller could not authenticate or could not\n * reach the remote, rather than that the remote itself is misconfigured. The\n * remote record stays on disk for these so a retry after sign-in can re-add it.\n */\nfunction isCredentialOrNetworkError(error: unknown): boolean {\n if (isDriveAuthError(error)) {\n return true;\n }\n return error instanceof GraphQLRequestError && error.category === \"network\";\n}\n\n/** Where a sync operation's run of ordinals begins. */\nfunction firstOrdinalOf(syncOp: SyncOperation): number {\n return syncOp.operations.length > 0\n ? syncOp.operations[0].context.ordinal\n : 0;\n}\n\nexport class SyncManager implements ISyncManager {\n private readonly logger: ILogger;\n private readonly remoteStorage: ISyncRemoteStorage;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly deadLetterStorage: ISyncDeadLetterStorage;\n private readonly channelFactory: IChannelFactory;\n private readonly operationIndex: IOperationIndex;\n private readonly reactor: IReactor;\n private readonly eventBus: IEventBus;\n private readonly remotes: Map<string, Remote>;\n private readonly awaiter: JobAwaiter;\n private readonly syncAwaiter: SyncAwaiter;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private eventUnsubscribe?: () => void;\n private failedEventUnsubscribe?: () => void;\n private readonly batchAggregator: BatchAggregator;\n private readonly syncStatusTracker: SyncStatusTracker;\n private readonly config: SyncManagerConfig;\n private readonly connectionStateUnsubscribes: Map<string, () => void> =\n new Map();\n private readonly quarantinedDocumentIds = new Set<string>();\n private readonly backfillAbortControllers = new Map<\n string,\n AbortController\n >();\n private readonly planKeyToJobUuid = new Map<string, string>();\n private readonly evictedOutboxFloors = new Map<string, number>();\n private readonly prunePending = new Set<string>();\n private pruneChain: Promise<void> = Promise.resolve();\n private derivingOutboxes = 0;\n private pruneDrainDeferred = false;\n private readonly removing = new Set<string>();\n private readonly lastEnqueuedJobIdByKey = new Map<string, string>();\n private inboxChunkChain: Promise<void> = Promise.resolve();\n\n constructor(\n logger: ILogger,\n remoteStorage: ISyncRemoteStorage,\n cursorStorage: ISyncCursorStorage,\n deadLetterStorage: ISyncDeadLetterStorage,\n channelFactory: IChannelFactory,\n operationIndex: IOperationIndex,\n reactor: IReactor,\n eventBus: IEventBus,\n driveContainerTypes: ReadonlySet<string>,\n config: Partial<SyncManagerConfig> = {},\n ) {\n this.logger = logger;\n this.remoteStorage = remoteStorage;\n this.cursorStorage = cursorStorage;\n this.deadLetterStorage = deadLetterStorage;\n this.channelFactory = channelFactory;\n this.operationIndex = operationIndex;\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.config = { ...defaultSyncManagerConfig, ...config };\n this.remotes = new Map();\n this.awaiter = new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n this.syncAwaiter = new SyncAwaiter(eventBus);\n this.isShutdown = false;\n this.batchAggregator = new BatchAggregator(\n logger,\n driveContainerTypes,\n (batch) => this.processCompleteBatch(batch),\n );\n this.syncStatusTracker = new SyncStatusTracker();\n }\n\n async startup(): Promise<void> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is already shutdown and cannot be started\");\n }\n\n try {\n const quarantinedIds =\n await this.deadLetterStorage.listQuarantinedDocumentIds();\n for (const id of quarantinedIds) {\n this.quarantinedDocumentIds.add(id);\n }\n } catch (error) {\n this.logger.error(\n \"Failed to load quarantined document IDs (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n const remoteRecords = await this.remoteStorage.list();\n\n for (const record of remoteRecords) {\n const channel = this.channelFactory.instance(\n record.id,\n record.name,\n record.channelConfig,\n this.cursorStorage,\n record.collectionId,\n record.filter,\n this.operationIndex,\n record.options,\n );\n\n const remote: Remote = {\n meta: {\n id: record.id,\n name: record.name,\n collectionId: record.collectionId,\n channelConfig: record.channelConfig,\n filter: record.filter,\n options: record.options,\n },\n channel,\n };\n\n this.remotes.set(record.name, remote);\n await this.loadDeadLetters(remote);\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n this.logger.error(\n \"Error initializing channel for remote (@name, @error)\",\n record.name,\n error instanceof Error ? error.message : String(error),\n );\n await this.dropRemoteAfterFailedInit(remote, false);\n continue;\n }\n\n // backfill channels asynchronously -- don't block startup\n const outboxAckOrdinal = remote.channel.outbox.ackOrdinal;\n if (outboxAckOrdinal > 0) {\n const backfillController = new AbortController();\n this.backfillAbortControllers.set(record.name, backfillController);\n void this.updateOutbox(\n remote,\n outboxAckOrdinal,\n OutboxMode.Backfill,\n backfillController.signal,\n )\n .catch((error) => {\n if (backfillController.signal.aborted) return;\n this.logger.error(\n \"Backfill failed for remote @RemoteName: @Error\",\n remote.meta.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(record.name);\n void this.drainPrunes();\n });\n }\n }\n\n this.eventUnsubscribe = this.eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event) => this.batchAggregator.enqueueWriteReady(event),\n );\n\n this.failedEventUnsubscribe = this.eventBus.subscribe<JobFailedEvent>(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event) => this.batchAggregator.handleJobFailed(event),\n );\n }\n\n shutdown(): ShutdownStatus {\n this.isShutdown = true;\n this.abortController.abort();\n for (const controller of this.backfillAbortControllers.values()) {\n controller.abort();\n }\n this.backfillAbortControllers.clear();\n this.planKeyToJobUuid.clear();\n this.lastEnqueuedJobIdByKey.clear();\n this.prunePending.clear();\n this.pruneDrainDeferred = false;\n this.batchAggregator.clear();\n\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = undefined;\n }\n\n if (this.failedEventUnsubscribe) {\n this.failedEventUnsubscribe();\n this.failedEventUnsubscribe = undefined;\n }\n\n this.awaiter.shutdown();\n this.syncAwaiter.shutdown();\n this.syncStatusTracker.clear();\n\n for (const unsub of this.connectionStateUnsubscribes.values()) {\n unsub();\n }\n this.connectionStateUnsubscribes.clear();\n\n const promises: Promise<void>[] = [];\n for (const remote of this.remotes.values()) {\n promises.push(remote.channel.shutdown());\n }\n\n this.remotes.clear();\n\n return {\n isShutdown: true,\n completed: Promise.all(promises).then(() => undefined),\n };\n }\n\n getByName(name: string): Remote {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n return remote;\n }\n\n getById(id: string): Remote {\n for (const remote of this.remotes.values()) {\n if (remote.meta.id === id) {\n return remote;\n }\n }\n throw new Error(`Remote with id '${id}' does not exist`);\n }\n\n async bindRemote(id: string, boundAddress: string): Promise<void> {\n const remote = this.getById(id);\n const bound = remote.meta.options.boundAddress;\n\n if (bound === boundAddress) {\n return;\n }\n\n if (bound !== undefined) {\n throw new Error(\n `Remote with id '${id}' is already bound to another address`,\n );\n }\n\n remote.meta.options = { ...remote.meta.options, boundAddress };\n\n await this.remoteStorage.upsert({\n id: remote.meta.id,\n name: remote.meta.name,\n collectionId: remote.meta.collectionId,\n channelConfig: remote.meta.channelConfig,\n filter: remote.meta.filter,\n options: remote.meta.options,\n status: {\n push: createIdleHealth(),\n pull: createIdleHealth(),\n },\n });\n }\n\n async add(\n name: string,\n collectionId: DriveCollectionId,\n channelConfig: ChannelConfig,\n filter: RemoteFilter = { documentId: [], scope: [], branch: \"\" },\n options: RemoteOptions = { sinceTimestampUtcMs: \"0\" },\n id?: string,\n ): Promise<Remote> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is shutdown and cannot add remotes\");\n }\n\n if (this.remotes.has(name)) {\n throw new Error(`Remote with name '${name}' already exists`);\n }\n\n this.logger.debug(\n \"Adding remote (@name, @collectionId, @channelConfig, @filter, @options, @id)\",\n name,\n collectionId.key,\n channelConfig,\n filter,\n options,\n id,\n );\n\n const remoteId = id ?? crypto.randomUUID();\n\n const status: RemoteStatus = {\n push: createIdleHealth(),\n pull: createIdleHealth(),\n };\n\n const remoteRecord: RemoteRecord = {\n id: remoteId,\n name,\n collectionId,\n channelConfig,\n filter,\n options,\n status,\n };\n\n await this.remoteStorage.upsert(remoteRecord);\n\n const channel = this.channelFactory.instance(\n remoteId,\n name,\n channelConfig,\n this.cursorStorage,\n collectionId,\n filter,\n this.operationIndex,\n options,\n );\n\n const remote: Remote = {\n meta: {\n id: remoteId,\n name,\n collectionId,\n channelConfig,\n filter,\n options,\n },\n channel,\n };\n\n this.remotes.set(name, remote);\n await this.loadDeadLetters(remote);\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n // Only a failure that says the remote itself is unusable may drop its\n // record. A refused credential or an unreachable host says nothing about\n // the configuration, so the record stays and a retry after sign-in can\n // re-add it.\n await this.dropRemoteAfterFailedInit(\n remote,\n !isCredentialOrNetworkError(error),\n );\n\n throw error;\n }\n\n // backfill asynchronously -- don't block channel registration\n const backfillController = new AbortController();\n this.backfillAbortControllers.set(name, backfillController);\n void this.updateOutbox(\n remote,\n 0,\n OutboxMode.Backfill,\n backfillController.signal,\n )\n .catch((error) => {\n if (backfillController.signal.aborted) return;\n this.logger.error(\n \"Backfill failed for remote @RemoteName: @Error\",\n remote.meta.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(name);\n void this.drainPrunes();\n });\n\n return remote;\n }\n\n triggerPull(name: string): void {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n remote.channel.triggerPull();\n }\n\n async remove(name: string): Promise<void> {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n\n // The channel shuts down and the rows go several awaits before the map\n // entry does, and a batch landing in that gap would otherwise pick this\n // remote up and derive into mailboxes that are already being torn down.\n this.removing.add(name);\n try {\n await this.teardownRemoteResources(remote);\n\n // delete the remote's data\n await this.remoteStorage.remove(name);\n await this.cursorStorage.remove(name);\n } finally {\n // Released last: while the slot is held, a concurrent add of the same\n // name is refused, so it cannot race the storage deletes above. The\n // finally still guarantees the slot is freed if one of them throws.\n this.remotes.delete(name);\n this.removing.delete(name);\n }\n }\n\n /**\n * Drops a remote whose channel.init() rejected, optionally removing its\n * stored record. Ordered like remove(): the registry slot is released last,\n * so a concurrent add of the same name cannot slip in and have its record\n * deleted by the removal below. A failure to tear down must not replace the\n * init error the caller has to classify.\n */\n private async dropRemoteAfterFailedInit(\n remote: Remote,\n removeStorageRecord: boolean,\n ): Promise<void> {\n const name = remote.meta.name;\n // Same guard remove() takes: the remote is registered before init() runs,\n // so a batch could derive into mailboxes this teardown is closing.\n this.removing.add(name);\n try {\n await this.teardownRemoteResources(remote);\n if (removeStorageRecord) {\n await this.remoteStorage.remove(name);\n }\n } catch (error) {\n this.logger.error(\n \"Error tearing down remote after failed init (@name, @error)\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n this.remotes.delete(name);\n this.removing.delete(name);\n }\n }\n\n /**\n * Releases everything wiring a remote up holds: the in-flight backfill, the\n * channel, the status tracker entry and the connection-state subscription.\n * The registry slot is deliberately NOT released here -- the caller holds it\n * until any storage removal has finished, so a concurrent add of the same\n * name is refused for the whole teardown.\n */\n private async teardownRemoteResources(remote: Remote): Promise<void> {\n const name = remote.meta.name;\n\n const backfillController = this.backfillAbortControllers.get(name);\n if (backfillController) {\n backfillController.abort();\n this.backfillAbortControllers.delete(name);\n }\n\n try {\n await remote.channel.shutdown();\n } finally {\n this.syncStatusTracker.untrackRemote(name);\n const unsub = this.connectionStateUnsubscribes.get(name);\n if (unsub) {\n unsub();\n this.connectionStateUnsubscribes.delete(name);\n }\n this.evictedOutboxFloors.delete(name);\n this.prunePending.delete(name);\n }\n }\n\n list(): Remote[] {\n return Array.from(this.remotes.values());\n }\n\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n return this.syncAwaiter.waitForSync(jobId, signal);\n }\n\n getSyncStatus(documentId: string): SyncStatus | undefined {\n return this.syncStatusTracker.getStatus(documentId);\n }\n\n onSyncStatusChange(callback: SyncStatusChangeCallback): () => void {\n return this.syncStatusTracker.onChange(callback);\n }\n\n private recordPlanKeyMapping(planKey: string, jobId: string): void {\n if (\n !this.planKeyToJobUuid.has(planKey) &&\n this.planKeyToJobUuid.size >= PLAN_KEY_TO_JOB_UUID_CAP\n ) {\n const oldest = this.planKeyToJobUuid.keys().next().value;\n if (oldest !== undefined) {\n this.planKeyToJobUuid.delete(oldest);\n }\n }\n this.planKeyToJobUuid.set(planKey, jobId);\n }\n\n private wireChannelCallbacks(remote: Remote): void {\n remote.channel.inbox.onAdded((syncOps) =>\n this.handleInboxAdded(remote, syncOps),\n );\n\n this.syncStatusTracker.trackRemote(remote.meta.name, remote.channel);\n\n const unsubscribe = remote.channel.onConnectionStateChange((snapshot) => {\n void this.eventBus\n .emit(SyncEventTypes.CONNECTION_STATE_CHANGED, {\n remoteName: remote.meta.name,\n remoteId: remote.meta.id,\n previous: snapshot.state,\n current: snapshot.state,\n snapshot,\n } satisfies ConnectionStateChangedEvent)\n .catch(() => {});\n });\n this.connectionStateUnsubscribes.set(remote.meta.name, unsubscribe);\n\n remote.channel.deadLetter.onAdded((syncOps) => {\n for (const syncOp of syncOps) {\n this.logger.error(\n \"Dead letter (@remote, @documentId, @jobId, @error, @dependencies)\",\n remote.meta.name,\n syncOp.documentId,\n syncOp.jobId,\n syncOp.error?.message ?? \"unknown\",\n syncOp.jobDependencies,\n );\n\n const errorType = syncOperationErrorType(syncOp.error);\n\n // A held auth operation must keep syncing: reconciling the two policies\n // needs the traffic a quarantine would stop, in both directions.\n if (quarantinesDocument(errorType)) {\n this.quarantinedDocumentIds.add(syncOp.documentId);\n }\n\n const record: DeadLetterRecord = {\n id: syncOp.id,\n jobId: syncOp.jobId,\n jobDependencies: syncOp.jobDependencies,\n remoteName: syncOp.remoteName,\n documentId: syncOp.documentId,\n scopes: syncOp.scopes,\n branch: syncOp.branch,\n operations: syncOp.operations,\n errorSource: syncOp.error?.source ?? ChannelErrorSource.None,\n errorMessage: syncOp.error?.error.message ?? \"unknown\",\n errorType,\n };\n\n void this.deadLetterStorage.add(record).catch((err) => {\n this.logger.error(\n \"Failed to persist dead letter (@id, @error)\",\n record.id,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n void this.eventBus\n .emit(SyncEventTypes.DEAD_LETTER_ADDED, {\n id: record.id,\n jobId: record.jobId,\n remoteName: record.remoteName,\n documentId: record.documentId,\n errorSource: record.errorSource,\n errorType: record.errorType,\n } satisfies DeadLetterAddedEvent)\n .catch(() => {});\n }\n\n // Evict oldest dead letters from mailbox if over capacity\n const items = remote.channel.deadLetter.items;\n if (items.length > this.config.maxDeadLettersPerRemote) {\n const excessCount = items.length - this.config.maxDeadLettersPerRemote;\n const toEvict = items.slice(0, excessCount);\n remote.channel.deadLetter.remove(...toEvict);\n }\n });\n }\n\n private async loadDeadLetters(remote: Remote): Promise<void> {\n let records: DeadLetterRecord[];\n try {\n const page = await this.deadLetterStorage.list(remote.meta.name, {\n cursor: \"0\",\n limit: this.config.maxDeadLettersPerRemote,\n });\n records = page.results;\n } catch (error) {\n this.logger.error(\n \"Failed to load dead letters for remote (@name, @error)\",\n remote.meta.name,\n error instanceof Error ? error.message : String(error),\n );\n return;\n }\n\n if (records.length === 0) {\n return;\n }\n\n // Records come in ordinal DESC order (newest first).\n // Reverse so the Map maintains chronological insertion order (oldest first),\n // which makes eviction (slice from the front) straightforward.\n records.reverse();\n\n const syncOps: SyncOperation[] = [];\n for (const record of records) {\n const syncOp = new SyncOperation(\n record.id,\n record.jobId,\n record.jobDependencies,\n record.remoteName,\n record.documentId,\n record.scopes,\n record.branch,\n record.operations,\n );\n // The stored classification is carried rather than re-derived: only the\n // message was persisted, so a rebuilt Error has no name to classify by,\n // and a non-quarantining type would come back as quarantining.\n syncOp.failed(\n new ChannelError(\n record.errorSource,\n new Error(record.errorMessage),\n record.errorType,\n ),\n );\n syncOps.push(syncOp);\n }\n\n remote.channel.deadLetter.add(...syncOps);\n\n this.logger.debug(\n \"Loaded @count persisted dead letters for remote @name\",\n records.length,\n remote.meta.name,\n );\n }\n\n private getRemotesForCollection(collectionId: string): Remote[] {\n return Array.from(this.remotes.values()).filter(\n (remote) =>\n remote.meta.collectionId.key === collectionId &&\n !this.removing.has(remote.meta.name),\n );\n }\n\n private async processCompleteBatch(batch: PreparedBatch): Promise<void> {\n if (this.isShutdown) return;\n\n // get the unique set of collection ids\n const collectionIds = [\n ...new Set(\n Object.values(batch.collectionMemberships).flatMap(\n (collections) => collections,\n ),\n ),\n ];\n\n // get the unique set of affected remotes\n const affectedRemotes: Remote[] = [];\n for (const collectionId of collectionIds) {\n const remotes = this.getRemotesForCollection(collectionId);\n for (const remote of remotes) {\n if (!affectedRemotes.includes(remote)) {\n affectedRemotes.push(remote);\n }\n }\n }\n\n // ack matching inbox items\n for (const remote of affectedRemotes) {\n trimMailboxFromBatch(remote.channel.inbox, batch);\n }\n\n // finally, work through the affected remotes and backfill based on the last operation in the outbox\n for (const remote of affectedRemotes) {\n // A drain between two derivations can remove a remote this list was\n // built before, so membership is read again rather than assumed.\n if (\n !this.remotes.has(remote.meta.name) ||\n this.removing.has(remote.meta.name)\n ) {\n continue;\n }\n\n await this.updateOutbox(\n remote,\n remote.channel.outbox.latestOrdinal,\n OutboxMode.BatchTriggered,\n );\n }\n\n await this.drainPrunes();\n }\n\n private handleInboxAdded(remote: Remote, syncOps: SyncOperation[]): void {\n if (this.isShutdown) {\n return;\n }\n\n const eligible = syncOps.filter(\n (op) => !this.quarantinedDocumentIds.has(op.documentId),\n );\n if (eligible.length === 0) return;\n\n const keyed: SyncOperation[] = [];\n const nonKeyed: SyncOperation[] = [];\n\n for (const syncOp of eligible) {\n if (syncOp.jobId) {\n keyed.push(syncOp);\n } else {\n nonKeyed.push(syncOp);\n }\n }\n\n for (const syncOp of nonKeyed) {\n void this.applyInboxJob(remote, syncOp);\n }\n\n if (keyed.length > 0) {\n const allItems = keyed.map((syncOp) => ({ remote, syncOp }));\n const chunks = chunkSyncOperations(\n allItems,\n this.config.maxInboxBatchSize,\n );\n void this.processInboxChunks(chunks);\n }\n }\n\n private processInboxChunks(\n chunks: Array<Array<{ remote: Remote; syncOp: SyncOperation }>>,\n ): Promise<void> {\n const next = this.inboxChunkChain.then(async () => {\n for (const chunk of chunks) {\n if (this.isShutdown) return;\n await this.applyInboxBatch(chunk);\n }\n });\n this.inboxChunkChain = next.catch((err) => {\n this.logger.error(\n \"Inbox chunk processing failed (@error)\",\n err instanceof Error ? err.message : String(err),\n );\n });\n return next;\n }\n\n private async applyInboxJob(\n remote: Remote,\n syncOp: SyncOperation,\n ): Promise<void> {\n const operations: Operation[] = syncOp.operations.map((op) => op.operation);\n\n let jobInfo;\n try {\n jobInfo = await this.reactor.load(\n syncOp.documentId,\n syncOp.branch,\n operations,\n this.abortController.signal,\n { sourceRemote: remote.meta.name },\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to load operations from inbox (@remote, @documentId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n err.message,\n );\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to wait for job completion (@remote, @documentId, @jobId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n jobInfo.id,\n err.message,\n );\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n\n if (this.isShutdown) return;\n\n if (completedJobInfo.status === JobStatus.FAILED) {\n const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n this.logger.error(\n \"Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n completedJobInfo.id,\n errorMessage,\n );\n syncOp.failed(this.inboxFailure(completedJobInfo.error));\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n\n private async applyInboxBatch(\n items: Array<{ remote: Remote; syncOp: SyncOperation }>,\n ): Promise<void> {\n const sourceRemote = items[0].remote.meta.name;\n\n const chunkKeys = new Set(items.map(({ syncOp }) => syncOp.jobId));\n const jobs = items.map(({ syncOp }) => {\n const dependsOn: string[] = [];\n const externalDeps: string[] = [];\n for (const dep of syncOp.jobDependencies) {\n if (!dep) continue;\n if (chunkKeys.has(dep)) {\n dependsOn.push(dep);\n } else {\n const uuid = this.planKeyToJobUuid.get(dep);\n if (uuid !== undefined) externalDeps.push(uuid);\n }\n }\n const fifoKey = `${syncOp.documentId}:${syncOp.scopes[0]}:${syncOp.branch}`;\n const prevUuid = this.lastEnqueuedJobIdByKey.get(fifoKey);\n if (prevUuid !== undefined) externalDeps.push(prevUuid);\n return {\n key: syncOp.jobId,\n documentId: syncOp.documentId,\n scope: syncOp.scopes[0],\n branch: syncOp.branch,\n operations: syncOp.operations.map((op) => op.operation),\n dependsOn,\n externalDeps,\n };\n });\n\n const request: BatchLoadRequest = { jobs };\n\n let result: BatchLoadResult;\n try {\n result = await this.reactor.loadBatch(\n request,\n this.abortController.signal,\n { sourceRemote },\n );\n } catch (error) {\n if (this.isShutdown) return;\n for (const { remote, syncOp } of items) {\n const err = error instanceof Error ? error : new Error(String(error));\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n }\n return;\n }\n\n if (this.isShutdown) return;\n\n for (const plan of jobs) {\n if (!(plan.key in result.jobs)) continue;\n const info = result.jobs[plan.key];\n this.recordPlanKeyMapping(plan.key, info.id);\n const fifoKey = `${plan.documentId}:${plan.scope}:${plan.branch}`;\n this.lastEnqueuedJobIdByKey.set(fifoKey, info.id);\n }\n\n for (const { remote, syncOp } of items) {\n if (!(syncOp.jobId in result.jobs)) {\n this.logger.error(\n \"Job key missing from batch load result (@remote, @documentId, @jobId)\",\n remote.meta.name,\n syncOp.documentId,\n syncOp.jobId,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Job key '${syncOp.jobId}' missing from batch load result`),\n );\n syncOp.failed(error);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n const jobInfo = result.jobs[syncOp.jobId];\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) continue;\n const err = error instanceof Error ? error : new Error(String(error));\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) return;\n\n if (completedJobInfo.status === JobStatus.FAILED) {\n syncOp.failed(this.inboxFailure(completedJobInfo.error));\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n }\n\n /**\n * The dead letter for a load job the executor failed.\n *\n * The classification is passed explicitly because it cannot be recovered\n * downstream: the wrapper carries the failure's message, not the failure, so\n * deriving it from the wrapper's own name would classify every one of these\n * as unclassified and quarantine the document. A held auth operation must\n * keep syncing, because reconciling the two policies needs the traffic a\n * quarantine would stop.\n */\n private inboxFailure(error: ErrorInfo | undefined): ChannelError {\n const message = error?.message || \"Unknown error\";\n return new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${message}`),\n classifyJobFailure(error?.name ?? \"Error\"),\n );\n }\n\n /**\n * Where the next outbox derivation starts: at the evicted floor when there is\n * room to take those entries back, and at the caller's cursor otherwise.\n *\n * The floor is forgotten as it is folded in. If the refill overflows again the\n * eviction records a new one, and if it does not, the entries are queued and\n * the outbox itself holds the cursor down.\n */\n private refillOrdinal(remote: Remote, ackOrdinal: number): number {\n const floor = this.evictedOutboxFloors.get(remote.meta.name);\n if (floor === undefined) {\n return ackOrdinal;\n }\n\n if (\n this.outboxOperationCount(remote) >=\n this.config.maxHeldOperationsPerRemote\n ) {\n return ackOrdinal;\n }\n\n this.evictedOutboxFloors.delete(remote.meta.name);\n return Math.min(ackOrdinal, floor - 1);\n }\n\n /**\n * Evicts the newest entries of an outbox that has grown past its bound.\n *\n * Only entries that have never been taken up for transport are eligible, which\n * is what makes this a bound on withheld work rather than a way to drop\n * something already in flight. One entry is always kept, so an outbox whose\n * first entry is larger than the whole cap still makes progress.\n */\n private evictPastOutboxBound(remote: Remote): void {\n const cap = this.config.maxHeldOperationsPerRemote;\n const queued = remote.channel.outbox.items\n .filter((syncOp) => syncOp.status === SyncOperationStatus.Unknown)\n .sort((a, b) => firstOrdinalOf(a) - firstOrdinalOf(b));\n\n let kept = 0;\n let cut = queued.length;\n for (let i = 0; i < queued.length; i++) {\n const size = queued[i].operations.length;\n if (i > 0 && kept + size > cap) {\n cut = i;\n break;\n }\n kept += size;\n }\n\n const evicted = queued.slice(cut);\n if (evicted.length === 0) {\n return;\n }\n\n const floor = firstOrdinalOf(evicted[0]);\n const known = this.evictedOutboxFloors.get(remote.meta.name);\n this.evictedOutboxFloors.set(\n remote.meta.name,\n known === undefined ? floor : Math.min(known, floor),\n );\n\n const staleMs = this.stalePollAgeMs(remote);\n if (staleMs !== undefined) {\n let held = kept;\n for (const syncOp of evicted) {\n held += syncOp.operations.length;\n }\n\n // emitBatches evicts once per page, so a large backfill reaches here\n // repeatedly; the decision and its log belong to the first.\n if (!this.prunePending.has(remote.meta.name)) {\n this.prunePending.add(remote.meta.name);\n this.logger.warn(\n \"Outbox for @RemoteName (@Collection) is past its bound of @Cap operations holding @Held, and it has not been polled for @StaleMs ms; marking the channel for removal once this derivation ends\",\n remote.meta.name,\n remote.meta.collectionId.key,\n cap,\n held,\n staleMs,\n );\n }\n } else {\n this.logger.warn(\n \"Outbox for @RemoteName is past its bound of @Cap operations; evicting @Count entries from ordinal @Floor, to be derived again once it drains\",\n remote.meta.name,\n cap,\n evicted.length,\n floor,\n );\n }\n\n remote.channel.outbox.remove(...evicted);\n }\n\n /**\n * How long a served remote's holder has been silent, if past the window.\n *\n * The channel is asked, rather than its config inspected: SyncManager serves\n * and subscribes with the same interface, both kinds report lastSuccessUtcMs,\n * and the caller-supplied channelConfig.type is a free-form string the\n * factories do not read -- so neither could tell a dead served channel from a\n * client whose switchboard is merely unreachable. Only a channel that claims\n * a holder by reporting when it last heard from one can be removed for that\n * holder's silence; a channel that reports nothing (or 0) is never pruned.\n */\n private stalePollAgeMs(remote: Remote): number | undefined {\n const last = remote.channel.lastHolderPollUtcMs();\n if (last === undefined || last <= 0) {\n return undefined;\n }\n\n const age = Date.now() - last;\n return age >= this.config.staleRemotePollWindowMs ? age : undefined;\n }\n\n /**\n * Removes the remotes marked stale during eviction.\n *\n * Removing a remote that a derivation is still iterating would pull its\n * mailboxes out from under it, so a drain that arrives during one is deferred\n * rather than run; the last derivation to finish re-arms it.\n */\n private drainPrunes(): Promise<void> {\n if (this.derivingOutboxes > 0) {\n this.pruneDrainDeferred = true;\n return Promise.resolve();\n }\n\n const next = this.pruneChain.then(async () => {\n if (this.isShutdown) return;\n for (const name of [...this.prunePending]) {\n // Taken again per remote rather than once on the way in: this body\n // runs behind the chain and across the awaits of each removal, so a\n // derivation can have started since. From here to the `removing` mark\n // inside remove() nothing yields, which is what closes the window.\n if (this.derivingOutboxes > 0) {\n this.pruneDrainDeferred = true;\n return;\n }\n\n this.prunePending.delete(name);\n const remote = this.remotes.get(name);\n if (!remote) continue;\n\n // The mark was made mid-derivation and is acted on after it. A poll in\n // between is the holder saying it is still there -- and a first poll is\n // slow precisely when the outbox is huge, which is the state that\n // marked it -- so the decision is taken again here, against now.\n if (this.stalePollAgeMs(remote) === undefined) {\n this.logger.info(\n \"Stale removal of @name revoked: its holder polled while the outbox was being derived\",\n name,\n );\n continue;\n }\n\n try {\n await this.remove(name);\n } catch (error) {\n this.logger.error(\n \"Failed to remove stale remote (@name, @error)\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n });\n this.pruneChain = next.catch(() => {});\n return next;\n }\n\n private outboxOperationCount(remote: Remote): number {\n let count = 0;\n for (const syncOp of remote.channel.outbox.items) {\n count += syncOp.operations.length;\n }\n return count;\n }\n\n /**\n * Derives this remote's outbox, holding off prunes for the duration.\n *\n * A backfill elsewhere can finish at any await in here and drain the prunes\n * it marked; the count is what keeps that drain from removing the remote this\n * call is still adding to.\n */\n private async updateOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode = OutboxMode.Backfill,\n signal?: AbortSignal,\n ): Promise<void> {\n this.derivingOutboxes++;\n try {\n await this.deriveOutbox(remote, ackOrdinal, mode, signal);\n } finally {\n this.derivingOutboxes--;\n if (this.derivingOutboxes === 0 && this.pruneDrainDeferred) {\n this.pruneDrainDeferred = false;\n void this.drainPrunes();\n }\n }\n }\n\n private async deriveOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode,\n signal?: AbortSignal,\n ): Promise<void> {\n const composedSignal = signal\n ? AbortSignal.any([signal, this.abortController.signal])\n : this.abortController.signal;\n\n const startOrdinal = this.refillOrdinal(remote, ackOrdinal);\n let maxOrdinal = startOrdinal;\n const lastJobByDoc = new Map<string, string>();\n let prevChainJobId: string | undefined;\n const sinceTimestamp = remote.meta.options.sinceTimestampUtcMs;\n\n const emitBatches = (operations: OperationWithContext[]): void => {\n if (operations.length === 0) {\n return;\n }\n\n const batches = batchOperationsByDocument(operations);\n\n const syncOps: SyncOperation[] = [];\n for (const batch of batches) {\n const jobId = crypto.randomUUID();\n const prevJobId = lastJobByDoc.get(batch.documentId);\n\n const deps: string[] = [];\n if (prevJobId) deps.push(prevJobId);\n if (\n mode === OutboxMode.BatchTriggered &&\n prevChainJobId &&\n prevChainJobId !== prevJobId\n ) {\n deps.push(prevChainJobId);\n }\n\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n jobId,\n deps,\n remote.meta.name,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n\n syncOps.push(syncOp);\n lastJobByDoc.set(batch.documentId, jobId);\n if (mode === OutboxMode.BatchTriggered) prevChainJobId = jobId;\n }\n\n remote.channel.outbox.add(...syncOps);\n this.evictPastOutboxBound(remote);\n };\n\n let page = await this.operationIndex.find(\n remote.meta.collectionId.key,\n startOrdinal,\n { excludeSourceRemote: remote.meta.name },\n undefined,\n composedSignal,\n );\n\n let carry: OperationWithContext[] = [];\n\n let hasMore: boolean;\n do {\n if (composedSignal.aborted) {\n return;\n }\n for (const entry of page.results) {\n maxOrdinal = Math.max(maxOrdinal, entry.ordinal ?? 0);\n }\n\n let operations = page.results.map((entry) =>\n toOperationWithContext(entry),\n );\n\n if (carry.length > 0) {\n operations = [...carry, ...operations];\n carry = [];\n }\n\n if (sinceTimestamp && sinceTimestamp !== \"0\") {\n operations = operations.filter(\n (op) => op.operation.timestampUtcMs >= sinceTimestamp,\n );\n }\n operations = filterOperations(operations, remote.meta.filter);\n operations = operations.filter(\n (op) => !this.quarantinedDocumentIds.has(op.context.documentId),\n );\n\n hasMore = !!page.next;\n\n if (operations.length > 0) {\n // Which documents this page creates. A creation is always in the\n // `document` scope (document-action-handler.ts:346), and every other\n // scope's run for that document needs it to exist first.\n const created = new Set<string>();\n for (const entry of operations) {\n if (entry.operation.action.type === \"CREATE_DOCUMENT\") {\n created.add(entry.context.documentId);\n }\n }\n\n operations.sort((a, b) => {\n if (a.context.documentId !== b.context.documentId) {\n return a.context.documentId < b.context.documentId ? -1 : 1;\n }\n if (a.context.scope !== b.context.scope) {\n // Scope is otherwise compared as a raw string, so `auth` sorted\n // ahead of the `document` run that creates it -- and emitBatches\n // turns emission order into a dependency through lastJobByDoc, so\n // the creation was emitted depending on an operation for a\n // document that did not exist yet. The receiver defers that\n // operation until a CREATE_DOCUMENT for the id lands, which is the\n // job queued behind it, and neither side moves.\n //\n // Only the creating run is hoisted. Ordering the scopes against\n // each other generally is a separate question with its own\n // answers elsewhere (comparePositions gives `auth` a timestamp\n // tie), and grouping by scope at all is what keeps a page's\n // batches few -- serving a document's operations in index order\n // splits it into a batch per scope change.\n if (created.has(a.context.documentId)) {\n if (a.context.scope === \"document\") return -1;\n if (b.context.scope === \"document\") return 1;\n }\n return a.context.scope < b.context.scope ? -1 : 1;\n }\n return a.context.ordinal - b.context.ordinal;\n });\n\n if (hasMore) {\n const split = splitTrailingSameTimestampRun(operations);\n carry = split.carry;\n emitBatches(split.emit);\n } else {\n emitBatches(operations);\n }\n }\n\n if (hasMore) {\n page = await page.next!();\n }\n } while (hasMore);\n\n if (carry.length > 0) {\n emitBatches(carry);\n }\n\n remote.channel.outbox.advanceOrdinal(maxOrdinal);\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IReactor, InProcessSyncModule } from \"../core/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type {\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n} from \"../storage/interfaces.js\";\nimport { KyselySyncCursorStorage } from \"../storage/kysely/sync-cursor-storage.js\";\nimport { KyselySyncDeadLetterStorage } from \"../storage/kysely/sync-dead-letter-storage.js\";\nimport { KyselySyncRemoteStorage } from \"../storage/kysely/sync-remote-storage.js\";\nimport type { Database } from \"../storage/kysely/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"./interfaces.js\";\nimport { SyncManager, type SyncManagerConfig } from \"./sync-manager.js\";\n\nexport class SyncBuilder {\n private channelFactory?: IChannelFactory;\n private remoteStorage?: ISyncRemoteStorage;\n private cursorStorage?: ISyncCursorStorage;\n private deadLetterStorage?: ISyncDeadLetterStorage;\n private config: Partial<SyncManagerConfig> = {};\n\n withChannelFactory(factory: IChannelFactory): this {\n this.channelFactory = factory;\n return this;\n }\n\n withRemoteStorage(storage: ISyncRemoteStorage): this {\n this.remoteStorage = storage;\n return this;\n }\n\n withCursorStorage(storage: ISyncCursorStorage): this {\n this.cursorStorage = storage;\n return this;\n }\n\n withDeadLetterStorage(storage: ISyncDeadLetterStorage): this {\n this.deadLetterStorage = storage;\n return this;\n }\n\n withMaxDeadLettersPerRemote(limit: number): this {\n this.config.maxDeadLettersPerRemote = limit;\n return this;\n }\n\n withMaxInboxBatchSize(limit: number): this {\n this.config.maxInboxBatchSize = limit;\n return this;\n }\n\n withMaxHeldOperationsPerRemote(limit: number): this {\n this.config.maxHeldOperationsPerRemote = limit;\n return this;\n }\n\n withStaleRemotePollWindowMs(windowMs: number): this {\n this.config.staleRemotePollWindowMs = windowMs;\n return this;\n }\n\n build(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n driveContainerTypes: ReadonlySet<string>,\n ): ISyncManager {\n const module = this.buildModule(\n reactor,\n logger,\n operationIndex,\n eventBus,\n db,\n driveContainerTypes,\n );\n return module.syncManager;\n }\n\n buildModule(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n driveContainerTypes: ReadonlySet<string>,\n ): InProcessSyncModule {\n if (!this.channelFactory) {\n throw new Error(\"Channel factory is required\");\n }\n\n const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);\n const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);\n const deadLetterStorage =\n this.deadLetterStorage ?? new KyselySyncDeadLetterStorage(db);\n\n const syncManager = new SyncManager(\n logger,\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n this.channelFactory,\n operationIndex,\n reactor,\n eventBus,\n driveContainerTypes,\n this.config,\n );\n\n return {\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n channelFactory: this.channelFactory,\n syncManager,\n };\n }\n}\n","import type { Kysely } from \"kysely\";\nimport type { Database } from \"./types.js\";\n\nexport async function createDefaultDatabase(): Promise<Kysely<Database>> {\n const { Kysely } = await import(\"kysely\");\n const { PGlite } = await import(\"@electric-sql/pglite\");\n const { PGliteDialect } = await import(\"kysely-pglite-dialect\");\n return new Kysely<Database>({\n dialect: new PGliteDialect(new PGlite()),\n });\n}\n","import type { ShutdownStatus } from \"./types.js\";\n\n/**\n * Factory method to create a ShutdownStatus object\n *\n * @param isShutdown - Initial shutdown state\n * @returns A ShutdownStatus object with a getter for the shutdown state\n */\nexport function createShutdownStatus(isShutdown: boolean): ShutdownStatus {\n const shutdownState = isShutdown;\n\n return {\n get isShutdown() {\n return shutdownState;\n },\n completed: Promise.resolve(),\n };\n}\n\n/**\n * Factory method to create a ShutdownStatus that can be updated\n *\n * @param initialState - Initial shutdown state (default: false)\n * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]\n */\nexport function createMutableShutdownStatus(\n initialState = false,\n): [\n ShutdownStatus,\n (value: boolean) => void,\n (completed: Promise<void>) => void,\n] {\n let shutdownState = initialState;\n let completedPromise: Promise<void> = Promise.resolve();\n\n const status: ShutdownStatus = {\n get isShutdown() {\n return shutdownState;\n },\n get completed() {\n return completedPromise;\n },\n };\n\n const setShutdown = (value: boolean) => {\n shutdownState = value;\n };\n\n const setCompleted = (promise: Promise<void>) => {\n completedPromise = promise;\n };\n\n return [status, setShutdown, setCompleted];\n}\n","import type {\n Action,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\n\nimport type { IProcessorManager } from \"@powerhousedao/shared/processors\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { ReactorClient } from \"../client/reactor-client.js\";\nimport type { IReactorClient } from \"../client/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { GroupReevaluationTrigger } from \"./group-reevaluation-trigger.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { DocumentViewDatabase } from \"../read-models/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport type { IJobAwaiter } from \"../shared/awaiter.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncRemoteStorage,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type {\n DocumentIndexerDatabase,\n Database as StorageDatabase,\n} from \"../storage/kysely/types.js\";\nimport type { PoolInstrumentation } from \"../storage/pool-instrumentation.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"../sync/interfaces.js\";\n\nexport class AbortError extends Error {\n constructor(message?: string) {\n super(message || \"Aborted\");\n\n this.name = \"AbortError\";\n }\n}\n\nexport const isAbortError = (error: unknown): boolean => {\n return error instanceof AbortError;\n};\n\n/**\n * A single mutation job within a batch request.\n */\nexport type ExecutionJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Request for batch mutation operation.\n */\nexport type BatchExecutionRequest = {\n jobs: ExecutionJobPlan[];\n};\n\n/**\n * Result from batch mutation operation.\n */\nexport type BatchExecutionResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * A single load job within a batch request.\n *\n * `dependsOn` lists intra-batch plan keys (resolved to UUIDs by `loadBatch`).\n * `externalDeps` lists pre-resolved job UUIDs from prior batches; these are\n * appended to the queue hint without going through plan-key resolution.\n */\nexport type LoadJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n operations: Operation[];\n dependsOn: string[];\n externalDeps: string[];\n};\n\n/**\n * Request for batch load operation.\n */\nexport type BatchLoadRequest = {\n jobs: LoadJobPlan[];\n};\n\n/**\n * Result from batch load operation.\n */\nexport type BatchLoadResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * The main Reactor interface that serves as a facade for document operations.\n * This interface provides a unified API for document management, including\n * creation, retrieval, mutation, and deletion operations.\n */\nexport interface IReactor {\n /**\n * Signals that the reactor should shutdown.\n */\n kill(): ShutdownStatus;\n\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional options for paging data in large queries.\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific PHDocument by id\n *\n * @param id - Required, this is the document id\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument\n */\n get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by slug\n *\n * @param slug - Required, this is the document slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by identifier (either id or slug).\n * Throws an error if the identifier matches both an id and a slug that refer to different documents.\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n * @throws {Error} If identifier matches both an ID and slug referring to different documents\n */\n getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n *\n * @param sourceId - The source document id\n * @param relationshipType - The relationship type to filter by\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of target document ids\n */\n getOutgoingRelationships(\n sourceId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n *\n * @param targetId - The target document id\n * @param relationshipType - The relationship type to filter by\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of source document ids\n */\n getIncomingRelationships(\n targetId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves the operations for a document\n *\n * @param documentId - The document id\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of operations\n */\n getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>>;\n\n /**\n * Filters documents by criteria and returns a list of them\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Creates a document\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job status\n */\n create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Deletes a document\n *\n * @param id - Document id\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies a list of actions to a document.\n *\n * @param docId - Document id\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Loads existing operations generated elsewhere into this reactor.\n *\n * @param docId - Document id\n * @param branch - Branch to load operations to\n * @param operations - List of operations to load\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies multiple mutations across documents with dependency management.\n *\n * Ordering, and only ordering. A batch is neither atomic nor transactional:\n * it is submitted as one job per plan entry, each commits on its own, and\n * there is no batch-level rollback, status or consistency token.\n *\n * A dependency waits for the job before it to finish, not to succeed. A\n * failed job leaves the queue the same way a successful one does, so its\n * dependents still run, against whatever state the failure left behind.\n * Jobs that committed stay committed.\n *\n * Re-submitting after a partial failure re-applies the entries that already\n * succeeded, because a job plan carries no idempotency key. Compensation\n * belongs to the caller.\n *\n * @param request - Batch mutation request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @returns A receipt, not a result: every JobInfo is a PENDING snapshot\n * carrying a placeholder consistency token. Poll getJobStatus per job id, or\n * subscribe to JOB_FAILED and correlate on meta.batchId, to find out what\n * each one did.\n */\n executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns Map of job keys to job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult>;\n\n /**\n * Adds a relationship between two documents.\n *\n * @param sourceId - Source document id\n * @param targetId - Target document id\n * @param relationshipType - Relationship type identifier\n * @param branch - Branch to add the relationship to, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n addRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Removes a relationship between two documents.\n *\n * @param sourceId - Source document id\n * @param targetId - Target document id\n * @param relationshipType - Relationship type identifier\n * @param branch - Branch to remove the relationship from, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n removeRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n}\n\n/**\n * Feature flags for reactor configuration\n */\nexport type ReactorFeatures = { [key: string]: boolean };\n\n/**\n * Combined database type that includes all schemas\n */\nexport type Database = StorageDatabase &\n DocumentViewDatabase &\n DocumentIndexerDatabase;\n\n/**\n * Base sync-manager contract resolvable in every environment (in-process or\n * proxied over a worker boundary): just the sync manager.\n */\nexport interface SyncModule {\n syncManager: ISyncManager;\n}\n\n/**\n * Sync module composed in-process, with the full set of sync dependencies.\n */\nexport interface InProcessSyncModule extends SyncModule {\n remoteStorage: ISyncRemoteStorage;\n cursorStorage: ISyncCursorStorage;\n deadLetterStorage: ISyncDeadLetterStorage;\n channelFactory: IChannelFactory;\n}\n\n/**\n * Base reactor module contract — the dependencies a client resolves regardless\n * of where the reactor graph lives. In-process hosts compose\n * {@link InProcessReactorModule}; a worker-backed tab resolves only this base\n * (the full graph lives in the worker).\n */\nexport interface ReactorModule {\n documentModelRegistry: IDocumentModelRegistry;\n syncModule: SyncModule | undefined;\n eventBus: IEventBus;\n}\n\n/**\n * Reactor module composed in-process: the full dependency graph. Provides\n * direct access to internal components for advanced use cases, testing, or\n * integration scenarios.\n */\n/**\n * A component that failed to build or catch up, and was started anyway.\n *\n * The reactor does not take itself down for one of these: a read model that\n * cannot reach its database should not stop the writes. But it does serve\n * reads that are silently incomplete, so the failure is recorded rather than\n * only logged -- a host can refuse readiness on a non-empty list instead of\n * reporting healthy while answering from an index that stopped at boot.\n */\nexport interface DegradedComponent {\n /** Names the component that failed, e.g. `read model 0 (attachments)`. */\n component: string;\n error: Error;\n}\n\nexport interface InProcessReactorModule extends ReactorModule {\n /**\n * The enforcement flags this reactor resolved, as plain booleans. Held on the\n * module because they select what a read enforces as well as what a write\n * does, and the read surface is composed outside the reactor.\n */\n featureFlags: ReactorFeatureFlags;\n queue: IQueue;\n jobTracker: IJobTracker;\n executorManager: IJobExecutorManager;\n database: Kysely<Database>;\n operationStore: IOperationStore;\n keyframeStore: IKeyframeStore;\n writeCache: IWriteCache;\n operationIndex: IOperationIndex;\n documentView: IDocumentView;\n documentViewConsistencyTracker: IConsistencyTracker;\n documentIndexer: IDocumentIndexer;\n documentIndexerConsistencyTracker: IConsistencyTracker;\n readModelCoordinator: IReadModelCoordinator;\n subscriptionManager: IReactorSubscriptionManager;\n processorManager: IProcessorManager;\n processorManagerConsistencyTracker: IConsistencyTracker;\n reactor: IReactor;\n syncModule: InProcessSyncModule | undefined;\n /**\n * Present when authGroups is on: enqueues re-evaluation jobs for the\n * documents a group membership change affects. Started by the builder;\n * hosts shut it down alongside the sync manager.\n */\n groupReevaluationTrigger: GroupReevaluationTrigger | undefined;\n /**\n * Instrumented pg.Pool handles registered with the builder, either by\n * createPostgresDatabase or by withInstrumentedPool. Empty when no pg\n * pool is in use (e.g. PGlite tests). Observers iterate the array to\n * record per-pool acquire-wait and stat metrics.\n */\n pools: PoolInstrumentation[];\n /**\n * Components that failed to initialize and were started degraded. Empty on\n * a clean boot. See {@link DegradedComponent}.\n */\n degradedComponents: DegradedComponent[];\n}\n\n/**\n * Base reactor client contract — the client plus its reactor module. Satisfied\n * by every host; in-process hosts extend it with {@link InProcessReactorClientModule}.\n */\nexport interface ReactorClientModule {\n client: IReactorClient;\n reactorModule: ReactorModule | undefined;\n}\n\n/**\n * Reactor client composed in-process: the concrete client plus direct refs to\n * the in-process graph internals.\n */\nexport interface InProcessReactorClientModule extends ReactorClientModule {\n client: ReactorClient;\n reactor: IReactor;\n eventBus: IEventBus;\n documentIndexer: IDocumentIndexer;\n documentView: IDocumentView;\n signer: ISigner;\n subscriptionManager: IReactorSubscriptionManager;\n jobAwaiter: IJobAwaiter;\n reactorModule: InProcessReactorModule | undefined;\n}\n","import type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { normalizeDocumentModelVersion } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n deleteDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobPendingEvent,\n} from \"../events/types.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { createMutableShutdownStatus } from \"../shared/factories.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n JobMeta,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport { JobStatus } from \"../shared/types.js\";\nimport { matchesScope, throwIfAborted } from \"../shared/utils.js\";\nimport type {\n IDocumentIndexer,\n IDocumentView,\n IOperationStore,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport {\n AbortError,\n type BatchExecutionRequest,\n type BatchExecutionResult,\n type BatchLoadRequest,\n type BatchLoadResult,\n type IReactor,\n type ReactorFeatures,\n} from \"./types.js\";\nimport {\n buildSingleJobMeta,\n filterByType,\n getSharedActionScope,\n getSharedOperationScope,\n signAction,\n signActions,\n toErrorInfo,\n topologicalSort,\n validateActionScopes,\n validateBatchLoadRequest,\n validateBatchRequest,\n validateOperationScopes,\n} from \"./utils.js\";\n\n/**\n * This class implements the IReactor interface and serves as the main entry point\n * for the new Reactor architecture.\n */\nexport class Reactor implements IReactor {\n private logger: ILogger;\n private documentModelRegistry: IDocumentModelRegistry;\n private shutdownStatus: ShutdownStatus;\n private setShutdown: (value: boolean) => void;\n private setCompleted: (completed: Promise<void>) => void;\n private queue: IQueue;\n private jobTracker: IJobTracker;\n private readModelCoordinator: IReadModelCoordinator;\n private features: ReactorFeatures;\n private documentView: IDocumentView;\n private documentIndexer: IDocumentIndexer;\n private operationStore: IOperationStore;\n private eventBus: IEventBus;\n private executorManager: IJobExecutorManager;\n\n constructor(\n logger: ILogger,\n documentModelRegistry: IDocumentModelRegistry,\n queue: IQueue,\n jobTracker: IJobTracker,\n readModelCoordinator: IReadModelCoordinator,\n features: ReactorFeatures,\n documentView: IDocumentView,\n documentIndexer: IDocumentIndexer,\n operationStore: IOperationStore,\n eventBus: IEventBus,\n executorManager: IJobExecutorManager,\n ) {\n this.logger = logger;\n this.documentModelRegistry = documentModelRegistry;\n this.queue = queue;\n this.jobTracker = jobTracker;\n this.readModelCoordinator = readModelCoordinator;\n this.features = features;\n this.documentView = documentView;\n this.documentIndexer = documentIndexer;\n this.operationStore = operationStore;\n this.eventBus = eventBus;\n this.executorManager = executorManager;\n\n const [status, setShutdown, setCompleted] =\n createMutableShutdownStatus(false);\n this.shutdownStatus = status;\n this.setShutdown = setShutdown;\n this.setCompleted = setCompleted;\n\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.logger.error(\n \"Job @JobId failed with @Message: @Job\",\n event.jobId,\n event.error.message,\n event.job,\n );\n },\n );\n\n this.readModelCoordinator.start();\n }\n\n kill(): ShutdownStatus {\n this.logger.verbose(\"kill()\");\n\n if (this.shutdownStatus.isShutdown) {\n return this.shutdownStatus;\n }\n\n this.setShutdown(true);\n\n const shutdownAsync = async () => {\n await this.executorManager.stop(true);\n\n this.readModelCoordinator.stop();\n this.jobTracker.shutdown();\n };\n\n this.setCompleted(shutdownAsync());\n\n return this.shutdownStatus;\n }\n\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const modules = this.documentModelRegistry.getAllModules();\n const filteredModels = modules.filter(\n (module: DocumentModelModule) =>\n !namespace || module.documentModel.global.id.startsWith(namespace),\n );\n\n const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || filteredModels.length;\n const pagedModels = filteredModels.slice(startIndex, startIndex + limit);\n\n const hasMore = startIndex + limit < filteredModels.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return Promise.resolve({\n results: pagedModels,\n options: paging || { cursor: \"0\", limit: filteredModels.length },\n nextCursor,\n next: hasMore\n ? () =>\n this.getDocumentModels(\n namespace,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n });\n }\n\n async get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@id, @view)\", id, view);\n\n return await this.documentView.get<TDocument>(\n id,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getBySlug(@slug, @view)\", slug, view);\n\n const documentId = await this.documentView.resolveSlug(\n slug,\n view,\n consistencyToken,\n signal,\n );\n\n if (!documentId) {\n throw new Error(`Document not found with slug: ${slug}`);\n }\n\n return await this.get<TDocument>(\n documentId,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getByIdOrSlug(@identifier, @view)\", identifier, view);\n\n return await this.documentView.getByIdOrSlug<TDocument>(\n identifier,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getOutgoingRelationships(\n sourceId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getOutgoing(\n sourceId,\n [relationshipType],\n undefined,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships.results.map((rel) => rel.targetId);\n }\n\n async getIncomingRelationships(\n targetId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getIncoming(\n targetId,\n [relationshipType],\n undefined,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships.results.map((rel) => rel.sourceId);\n }\n\n async getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>> {\n this.logger.verbose(\n \"getOperations(@documentId, @view, @filter, @paging)\",\n documentId,\n view,\n filter,\n paging,\n );\n\n const branch = view?.branch || \"main\";\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const allScopes = Object.keys(revisions.revision);\n const result: Record<string, PagedResults<Operation>> = {};\n\n for (const scope of allScopes) {\n if (!matchesScope(view, scope)) {\n continue;\n }\n\n throwIfAborted(signal, () => new AbortError());\n\n const scopeResult = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n -1,\n filter,\n paging,\n signal,\n );\n\n result[scope] = {\n results: scopeResult.results,\n options: scopeResult.options,\n nextCursor: scopeResult.nextCursor,\n next: scopeResult.next\n ? async () => {\n const nextPage = await this.getOperations(\n documentId,\n view,\n filter,\n {\n cursor: scopeResult.nextCursor!,\n limit: scopeResult.options.limit,\n },\n consistencyToken,\n signal,\n );\n return nextPage[scope];\n }\n : undefined,\n };\n }\n\n return result;\n }\n\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n\n let results: PagedResults<PHDocument>;\n if (search.ids) {\n if (search.slugs && search.slugs.length > 0) {\n throw new Error(\"Cannot use both ids and slugs in the same search\");\n }\n\n results = await this.findByIds(\n search.ids,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.slugs) {\n results = await this.findBySlugs(\n search.slugs,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.parentId) {\n results = await this.findByParentId(\n search.parentId,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.type) {\n results = await this.findByType(\n search.type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n } else {\n throw new Error(\"No search criteria provided\");\n }\n\n throwIfAborted(signal, () => new AbortError());\n\n return results;\n }\n\n async create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"create(@id, @type, @slug)\",\n document.header.id,\n document.header.documentType,\n document.header.slug,\n );\n const createdAtUtcIso = new Date().toISOString();\n\n throwIfAborted(signal, () => new AbortError());\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createAction = createDocumentAction(createInput);\n const upgradeAction = upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n // imported documents may predate the document scope entirely\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n ),\n initialState: document.state,\n });\n\n let actions: Action[] = [createAction, upgradeAction];\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: document.header.id,\n scope: \"document\",\n branch: \"main\",\n actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\"deleteDocument(@id)\", id);\n const createdAtUtcIso = new Date().toISOString();\n\n throwIfAborted(signal, () => new AbortError());\n\n let action = deleteDocumentAction(id);\n\n if (signer) {\n action = await signAction(action, signer, signal);\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: id,\n scope: \"document\",\n branch: \"main\",\n actions: [action],\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"execute(@docId, @branch, @actions)\",\n docId,\n branch,\n actions,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const createdAtUtcIso = new Date().toISOString();\n const scope = getSharedActionScope(actions);\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: docId,\n scope: scope,\n branch: branch,\n actions: actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n throwIfAborted(signal, () => new AbortError());\n\n return jobInfo;\n }\n\n async load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"load(@docId, @branch, @count, @operations)\",\n docId,\n branch,\n operations.length,\n operations,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n if (operations.length === 0) {\n throw new Error(\"load requires at least one operation\");\n }\n\n const scope = getSharedOperationScope(operations);\n const createdAtUtcIso = new Date().toISOString();\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: docId,\n scope,\n branch,\n actions: [],\n operations,\n createdAt: createdAtUtcIso,\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n throwIfAborted(signal, () => new AbortError());\n\n return jobInfo;\n }\n\n async executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\"executeBatch(@count jobs)\", request.jobs.length);\n\n throwIfAborted(signal, () => new AbortError());\n validateBatchRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateActionScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: jobPlan.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n throwIfAborted(signal, () => new AbortError());\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = jobPlan.dependsOn.map(\n (depKey) => planKeyToJobId.get(depKey)!,\n );\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: jobPlan.actions,\n operations: [],\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchExecutionResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n\n throwIfAborted(signal, () => new AbortError());\n validateBatchLoadRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateOperationScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: jobPlan.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n throwIfAborted(signal, () => new AbortError());\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = [\n ...jobPlan.dependsOn.map((depKey) => planKeyToJobId.get(depKey)!),\n ...jobPlan.externalDeps,\n ];\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: [],\n operations: jobPlan.operations,\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchLoadResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async addRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"addRelationship(@sourceId, @targetId, @relationshipType, @branch)\",\n sourceId,\n targetId,\n relationshipType,\n branch,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n let actions: Action[] = [\n addRelationshipAction(sourceId, targetId, relationshipType),\n ];\n\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n return await this.execute(sourceId, branch, actions, signal);\n }\n\n async removeRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"removeRelationship(@sourceId, @targetId, @relationshipType, @branch)\",\n sourceId,\n targetId,\n relationshipType,\n branch,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n let actions: Action[] = [\n removeRelationshipAction(sourceId, targetId, relationshipType),\n ];\n\n if (signer) {\n actions = await signActions(actions, signer, signal);\n }\n\n return await this.execute(sourceId, branch, actions, signal);\n }\n\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n\n throwIfAborted(signal, () => new AbortError());\n\n const jobInfo = this.jobTracker.getJobStatus(jobId);\n\n if (!jobInfo) {\n const now = new Date().toISOString();\n return Promise.resolve({\n id: jobId,\n documentId: \"\",\n status: JobStatus.FAILED,\n createdAtUtcIso: now,\n completedAtUtcIso: now,\n error: toErrorInfo(\"Job not found\"),\n consistencyToken: {\n version: 1,\n createdAtUtcIso: now,\n coordinates: [],\n },\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n }\n\n return Promise.resolve(jobInfo);\n }\n\n private async findByIds(\n ids: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByIds(@count ids)\", ids.length);\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || ids.length;\n const pagedIds = ids.slice(startIndex, startIndex + limit);\n\n const results = await this.documentView.getMany<PHDocument>(\n pagedIds,\n view,\n consistencyToken,\n signal,\n );\n\n const hasMore = startIndex + limit < ids.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results,\n options: paging || { cursor: \"0\", limit: ids.length },\n nextCursor,\n };\n }\n\n private async findBySlugs(\n slugs: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findBySlugs(@count slugs)\", slugs.length);\n\n const ids = await this.documentView.resolveSlugs(\n slugs,\n view,\n consistencyToken,\n signal,\n );\n\n return await this.findByIds(ids, view, paging, consistencyToken, signal);\n }\n\n private async findByParentId(\n parentId: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByParentId(@parentId)\", parentId);\n\n const relationships = await this.documentIndexer.getOutgoing(\n parentId,\n [\"child\"],\n paging,\n consistencyToken,\n signal,\n );\n\n const ids = relationships.results.map((rel) => rel.targetId);\n return await this.findByIds(ids, view, paging, undefined, signal);\n }\n\n private async findByType(\n type: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByType(@type)\", type);\n\n return await this.documentView.findByType(\n type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n }\n\n private emitJobPending(jobId: string, meta: JobMeta): void {\n const event: JobPendingEvent = {\n jobId,\n jobMeta: meta,\n };\n this.eventBus.emit(ReactorEventTypes.JOB_PENDING, event).catch(() => {\n // Ignore event emission errors\n });\n }\n}\n","import type { UpgradeManifest } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type {\n DbConfig,\n ModelManifestEntry,\n SignatureVerifierSpec,\n WorkerPoolConfig,\n} from \"../executor/worker/protocol.js\";\nimport { WorkerPoolJobExecutorManager } from \"../executor/worker-pool-job-executor-manager.js\";\nimport type { WorkerFactory } from \"../executor/worker-pool-job-executor-manager.js\";\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 { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { WriteCacheConfig } from \"../cache/write-cache-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { EventBus } from \"../events/event-bus.js\";\nimport { resolveFeatureFlags } from \"./feature-flags.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport {\n KyselyExecutionScope,\n type IExecutionScope,\n} from \"../executor/execution-scope.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport { SimpleJobExecutorManager } from \"../executor/simple-job-executor-manager.js\";\nimport { SimpleJobExecutor } from \"../executor/simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../executor/types.js\";\nimport { InMemoryJobTracker } from \"../job-tracker/in-memory-job-tracker.js\";\nimport { ProcessorManager } from \"../processors/processor-manager.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport { InMemoryQueue } from \"../queue/queue.js\";\nimport type {\n BuiltInReadModelKind,\n ProjectionWorkerFactory,\n} from \"../projection/index.js\";\n// Imported from the leaf module, not the projection barrel: the barrel pulls\n// in worker_threads transports that this file otherwise only reaches through\n// a dynamic import.\nimport { BUILT_IN_READ_MODEL_KINDS } from \"../projection/read-model-kinds.js\";\nimport type {\n ProjectionShardHooks,\n ProjectionShardManager,\n} from \"../projection/projection-shard-manager.js\";\nimport { ReadModelCoordinator } from \"../read-models/coordinator.js\";\nimport { KyselyDocumentView } from \"../read-models/document-view.js\";\nimport type {\n IReadModel,\n IReadModelCoordinator,\n} from \"../read-models/interfaces.js\";\nimport {\n DocumentModelResolver,\n NullDocumentModelResolver,\n} from \"../registry/document-model-resolver.js\";\nimport { DocumentModelRegistry } from \"../registry/implementation.js\";\nimport type {\n IDocumentModelLoader,\n IDocumentModelRegistry,\n} from \"../registry/interfaces.js\";\nimport {\n ConsistencyTracker,\n type IConsistencyTracker,\n} from \"../shared/consistency-tracker.js\";\nimport type { SignatureVerificationHandler } from \"../signer/types.js\";\nimport {\n KyselyDocumentIndexer,\n type IndexerDatabase,\n} from \"../storage/kysely/document-indexer.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 {\n createForwardingPoolInstrumentation,\n instrumentPgPool,\n type ForwardingPoolInstrumentation,\n type PoolInstrumentation,\n} from \"../storage/pool-instrumentation.js\";\nimport {\n REACTOR_SCHEMA,\n runMigrations,\n} from \"../storage/migrations/migrator.js\";\nimport type { MigrationStrategy } from \"../storage/migrations/types.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport { SubscriptionNotificationReadModel } from \"../subs/subscription-notification-read-model.js\";\nimport { GroupReevaluationTrigger } from \"./group-reevaluation-trigger.js\";\nimport { GqlRequestChannelFactory } from \"../sync/channels/gql-request-channel-factory.js\";\nimport { GqlResponseChannelFactory } from \"../sync/channels/gql-response-channel-factory.js\";\nimport { SyncBuilder } from \"../sync/sync-builder.js\";\nimport type { JwtHandler } from \"../sync/types.js\";\nimport { ChannelScheme } from \"../sync/types.js\";\nimport { createDefaultDatabase } from \"./create-default-database.js\";\nimport { DEFAULT_DRIVE_CONTAINER_TYPES } from \"./drive-container-types.js\";\nimport { resolveModelSources } from \"./model-sources.js\";\nimport type { DocumentModelSource } from \"./model-sources.js\";\nimport { Reactor } from \"./reactor.js\";\nimport type {\n Database,\n DegradedComponent,\n InProcessReactorModule,\n InProcessSyncModule,\n IReactor,\n ReactorFeatures,\n} from \"./types.js\";\n\n/**\n * Dependencies provided to read-model factories registered via\n * `withReadModelFactory`. These are constructed inside `buildModule()`, which\n * is why factory-based registration is needed for read models that depend on\n * them (`BaseReadModel` subclasses, in particular).\n */\nexport interface ReadModelFactoryDeps {\n documentModelRegistry: IDocumentModelRegistry;\n operationIndex: IOperationIndex;\n writeCache: IWriteCache;\n processorManagerConsistencyTracker: IConsistencyTracker;\n}\n\n/**\n * Factory that builds a pre-ready read model from internal reactor\n * dependencies once they are available. Awaited during `buildModule()`.\n */\nexport type ReadModelFactory = (\n deps: ReadModelFactoryDeps,\n) => IReadModel | Promise<IReadModel>;\n\n/**\n * Dependencies handed to a coordinator factory registered via\n * `withReadModelCoordinatorFactory`. All constructed inside `buildModule()`.\n */\nexport interface ReadModelCoordinatorFactoryDeps {\n /** Host bus: subscribe to JOB_WRITE_READY, emit JOB_READ_READY. */\n eventBus: IEventBus;\n logger: ILogger;\n /**\n * `withReadModel` + `withReadModelFactory` models, in registration order.\n * Excludes documentView/documentIndexer so a worker owner cannot\n * double-index them.\n */\n readModels: IReadModel[];\n /** Post-ready: runs after JOB_READ_READY so callbacks see fresh reads. */\n subscriptionNotificationReadModel: IReadModel;\n /** Post-ready: every package-installed processor. */\n processorManager: IReadModel;\n /** Host document view; lookup surface only, never indexed by the factory. */\n documentView: IReadModel;\n /** Host document indexer; lookup surface only, never indexed by the factory. */\n documentIndexer: IReadModel;\n /**\n * Builds and starts a projection shard manager on this event bus with the\n * host consistency trackers wired. Registers no shutdown hook: the factory\n * owns the manager's lifecycle through `registerShutdownHook`.\n */\n createProjectionShardManager: (\n config: ProjectionShardBuilderConfig & ProjectionShardHooks,\n ) => Promise<ProjectionShardManager>;\n /** Appends to the list `withShutdownHook` uses; hooks run in order. */\n registerShutdownHook: (hook: () => Promise<void>) => void;\n}\n\n/**\n * Factory that builds the read-model coordinator from reactor internals.\n * Awaited during `buildModule()`.\n */\nexport type ReadModelCoordinatorFactory = (\n deps: ReadModelCoordinatorFactoryDeps,\n) => IReadModelCoordinator | Promise<IReadModelCoordinator>;\n\nexport type {\n DocumentModelSource,\n FileModelSource,\n PackageModelSource,\n ResolvedModelSources,\n} from \"./model-sources.js\";\n\ntype WorkerPoolBase = {\n /** Number of worker threads to spawn; also the sticky-routing modulus. */\n numWorkers: number;\n /**\n * Factory spec the default transport's workers import to instantiate\n * their signature verifier. Omitted = no executor-side verification,\n * parity with the in-process executor's default.\n */\n verifier?: SignatureVerifierSpec;\n};\n\n/**\n * Executor worker-pool configuration. Either `db` (default thread\n * transport; each worker opens its own Postgres pool) or a custom\n * `factory` transport is required by construction — an enabled pool\n * without connection info is unrepresentable.\n */\nexport type WorkerPoolOptions =\n | (WorkerPoolBase & { db: DbConfig; factory?: WorkerFactory })\n | (WorkerPoolBase & { db?: DbConfig; factory: WorkerFactory });\n\n/**\n * Caller-facing config for {@link ReactorBuilder.withProjectionShards}.\n * When set, the builder replaces the in-process\n * {@link ReadModelCoordinator} with a {@link ProjectionShardManager} that\n * fans JOB_WRITE_READY events to N projection workers sharded by\n * documentId.\n *\n * @see Sharded projection workers sub-feature brief\n * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)\n */\nexport type ProjectionShardBuilderConfig = {\n shardCount: number;\n /**\n * Kinds each shard indexes before its job reaches READ_READY.\n *\n * Together with {@link postReadyKinds} these must name every\n * {@link BuiltInReadModelKind} exactly once — the host's own copies of\n * those models never index under sharding, so a kind named in neither\n * list is indexed by nobody, and one named in both is indexed twice.\n * `buildModule` rejects any other combination.\n */\n preReadyKinds: BuiltInReadModelKind[];\n /** Kinds each shard indexes after READ_READY. See {@link preReadyKinds}. */\n postReadyKinds: BuiltInReadModelKind[];\n /**\n * Connection info for the projection workers' own pools. Falls back to\n * the executor worker pool's `db` when {@link ReactorBuilder.withWorkerPool}\n * is configured with one.\n */\n db?: DbConfig;\n poolSize?: number;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\nfunction sameDatabaseTarget(a: DbConfig, b: DbConfig): boolean {\n return a.host === b.host && a.port === b.port && a.database === b.database;\n}\n\n/**\n * Requires each built-in kind exactly once across both lists. A kind in\n * neither list is indexed by nobody (host copies never index under a\n * worker), so its reads go stale and its consistency-token waits never\n * resolve; a kind in both is indexed twice per operation.\n */\nfunction validateBuiltInKindCoverage(\n preReadyKinds: BuiltInReadModelKind[],\n postReadyKinds: BuiltInReadModelKind[],\n): void {\n const named = [...preReadyKinds, ...postReadyKinds];\n const missing = BUILT_IN_READ_MODEL_KINDS.filter(\n (kind) => !named.includes(kind),\n );\n const duplicated = BUILT_IN_READ_MODEL_KINDS.filter(\n (kind) => named.filter((entry) => entry === kind).length > 1,\n );\n if (missing.length === 0 && duplicated.length === 0) {\n return;\n }\n const problems = [\n missing.length > 0\n ? `never named: ${missing.join(\", \")} (would be indexed by no shard, leaving those reads permanently stale and their consistency-token waits unresolvable)`\n : undefined,\n duplicated.length > 0\n ? `named more than once: ${duplicated.join(\", \")} (would be indexed twice per operation)`\n : undefined,\n ].filter((problem) => problem !== undefined);\n throw new Error(\n `withProjectionShards requires preReadyKinds and postReadyKinds to name each built-in read model (${BUILT_IN_READ_MODEL_KINDS.join(\", \")}) exactly once between them; ${problems.join(\"; \")}`,\n );\n}\n\nexport class ReactorBuilder {\n private logger?: ILogger;\n private documentModelSources: DocumentModelSource[] = [];\n private upgradeManifests: UpgradeManifest<readonly number[]>[] = [];\n private features: ReactorFeatures = {\n legacyStorageEnabled: false,\n legacyProcessorIds: true,\n };\n private readModels: IReadModel[] = [];\n private readModelFactories: ReadModelFactory[] = [];\n private executorManager: IJobExecutorManager | undefined;\n private executorConfig: JobExecutorConfig = {};\n private writeCacheConfig?: Partial<WriteCacheConfig>;\n private migrationStrategy: MigrationStrategy = \"auto\";\n private syncBuilder?: SyncBuilder;\n private eventBus?: IEventBus;\n private readModelCoordinator?: IReadModelCoordinator;\n private readModelCoordinatorFactory?: ReadModelCoordinatorFactory;\n private signatureVerifier?: SignatureVerificationHandler;\n private kyselyInstance?: Kysely<Database>;\n private signalHandlersEnabled = false;\n private queueInstance?: IQueue;\n private channelScheme?: ChannelScheme;\n private jwtHandler?: JwtHandler;\n private documentModelLoader?: IDocumentModelLoader;\n private shutdownHooks: Array<() => Promise<void>> = [];\n private driveContainerTypes: ReadonlySet<string> =\n DEFAULT_DRIVE_CONTAINER_TYPES;\n private workerPool?: WorkerPoolOptions;\n private resolvedModelManifest?: ModelManifestEntry[];\n private moduleOnlyModelKeys: string[] = [];\n private projectionShardConfig?: ProjectionShardBuilderConfig;\n private projectionWorkerFactory?: ProjectionWorkerFactory;\n private instrumentedPools: PoolInstrumentation[] = [];\n\n withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n /**\n * Register document-model sources: live modules, importable files, or\n * importable packages. Appends across calls. At `buildModule()` every\n * source is resolved host-side and registered on the registry; file and\n * package sources additionally form the worker manifest when the worker\n * pool is enabled (live modules cannot cross a thread boundary).\n */\n withDocumentModelSources(sources: DocumentModelSource[]): this {\n this.documentModelSources.push(...sources);\n return this;\n }\n\n withUpgradeManifests(manifests: UpgradeManifest<readonly number[]>[]): this {\n this.upgradeManifests = manifests;\n return this;\n }\n\n withFeatures(features: ReactorFeatures): this {\n this.features = { ...this.features, ...features };\n return this;\n }\n\n withReadModel(readModel: IReadModel): this {\n this.readModels.push(readModel);\n return this;\n }\n\n /**\n * Register a factory that builds a pre-ready read model after the reactor's\n * internal `operationIndex`, `writeCache`, and processor-manager consistency\n * tracker are constructed. Use this for read models (e.g. `BaseReadModel`\n * subclasses) that need those dependencies and therefore cannot be built\n * before calling `buildModule()`.\n */\n withReadModelFactory(factory: ReadModelFactory): this {\n this.readModelFactories.push(factory);\n return this;\n }\n\n withReadModelCoordinator(readModelCoordinator: IReadModelCoordinator): this {\n this.readModelCoordinator = readModelCoordinator;\n return this;\n }\n\n /**\n * Register a factory that builds the coordinator once the subscription\n * read model, processor manager and host read models exist. Use this, not\n * `withReadModelCoordinator`, for coordinators that compose those internals.\n */\n withReadModelCoordinatorFactory(factory: ReadModelCoordinatorFactory): this {\n this.readModelCoordinatorFactory = factory;\n return this;\n }\n\n withExecutor(executor: IJobExecutorManager): this {\n this.executorManager = executor;\n return this;\n }\n\n withExecutorConfig(config: Partial<JobExecutorConfig>): this {\n this.executorConfig = { ...this.executorConfig, ...config };\n return this;\n }\n\n withWriteCacheConfig(config: Partial<WriteCacheConfig>): this {\n this.writeCacheConfig = config;\n return this;\n }\n\n withDriveContainerTypes(types: string[]): this {\n this.driveContainerTypes = new Set(types);\n return this;\n }\n\n withMigrationStrategy(strategy: MigrationStrategy): this {\n this.migrationStrategy = strategy;\n return this;\n }\n\n withSync(syncBuilder: SyncBuilder): this {\n this.syncBuilder = syncBuilder;\n return this;\n }\n\n withEventBus(eventBus: IEventBus): this {\n this.eventBus = eventBus;\n return this;\n }\n\n withSignatureVerifier(verifier: SignatureVerificationHandler): this {\n this.signatureVerifier = verifier;\n return this;\n }\n\n withKysely(kysely: Kysely<Database>): this {\n this.kyselyInstance = kysely;\n return this;\n }\n\n /**\n * Register an externally-constructed pg.Pool's {@link PoolInstrumentation}\n * so it surfaces through {@link ReactorModule.pools}. Use this when the\n * caller built the pool itself (e.g. the in-process bench host wiring) so\n * pool acquire-wait and pool-stat metrics still emit. The builder also\n * registers any pool it constructs internally via {@link createPostgresDatabase}.\n */\n withInstrumentedPool(instrumentation: PoolInstrumentation): this {\n this.instrumentedPools.push(instrumentation);\n return this;\n }\n\n withQueue(queue: IQueue): this {\n this.queueInstance = queue;\n return this;\n }\n\n withChannelScheme(scheme: ChannelScheme): this {\n this.channelScheme = scheme;\n return this;\n }\n\n withJwtHandler(handler: JwtHandler): this {\n this.jwtHandler = handler;\n return this;\n }\n\n withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n withSignalHandlers(): this {\n this.signalHandlersEnabled = true;\n return this;\n }\n\n /**\n * Register an async cleanup hook to run during graceful shutdown. Hooks fire\n * after `reactor.kill()` resolves and before `database.destroy()`, so callers\n * that depend on the reactor (e.g. an HTTP API layered on top) can drain\n * cleanly before the underlying kysely instance is torn down. Hook errors are\n * logged and otherwise ignored — one bad hook cannot strand the rest of the\n * shutdown chain.\n */\n withShutdownHook(hook: () => Promise<void>): this {\n this.shutdownHooks.push(hook);\n return this;\n }\n\n /**\n * Enable the executor worker pool: N `node:worker_threads` workers with\n * sticky per-document routing, replacing the in-process executor. Calling\n * this enables the pool — there is no `enabled` flag. Provide `db`\n * (each worker opens its own Postgres pool; the parent database is built\n * from it too unless {@link withKysely} is set) or a custom `factory`\n * transport. `verifier` is imported by the default transport's workers;\n * omitted = no executor-side signature verification.\n */\n withWorkerPool(options: WorkerPoolOptions): this {\n this.workerPool = options;\n return this;\n }\n\n /**\n * Configure N sharded projection workers. When set, the builder replaces\n * the default in-process {@link ReadModelCoordinator} with a\n * {@link ProjectionShardManager}.\n *\n * Projection workers open their own Postgres pools from `config.db`,\n * falling back to the executor worker pool's `db` when\n * {@link withWorkerPool} is configured with one; only the `poolSize` is\n * overridden by {@link ProjectionShardBuilderConfig.poolSize}. The same\n * model manifest resolved from {@link withDocumentModelSources} is\n * forwarded.\n */\n withProjectionShards(config: ProjectionShardBuilderConfig): this {\n this.projectionShardConfig = config;\n return this;\n }\n\n /**\n * Inject a custom {@link ProjectionWorkerFactory}. When set, the builder\n * skips default thread-transport wiring for the projection shards and\n * hands the factory directly to {@link ProjectionShardManager}.\n */\n withProjectionWorkerFactory(factory: ProjectionWorkerFactory): this {\n this.projectionWorkerFactory = factory;\n return this;\n }\n\n getResolvedModelManifest(): ModelManifestEntry[] | undefined {\n return this.resolvedModelManifest;\n }\n\n async build(): Promise<IReactor> {\n const module = await this.buildModule();\n return module.reactor;\n }\n\n async buildModule(): Promise<InProcessReactorModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor\"]);\n }\n\n const featureFlags = resolveFeatureFlags(this.executorConfig.featureFlags);\n\n if (\n this.readModelCoordinator !== undefined &&\n this.readModelCoordinatorFactory !== undefined\n ) {\n throw new Error(\n \"withReadModelCoordinator and withReadModelCoordinatorFactory are mutually exclusive; register one coordinator source\",\n );\n }\n\n if (\n this.projectionShardConfig !== undefined &&\n this.readModelCoordinatorFactory !== undefined\n ) {\n throw new Error(\n \"withProjectionShards and withReadModelCoordinatorFactory are mutually exclusive; the factory owns the projection worker through its createProjectionShardManager dependency\",\n );\n }\n\n if (\n this.projectionShardConfig !== undefined &&\n this.readModelFactories.length > 0\n ) {\n throw new Error(\n \"withProjectionShards does not support read models registered through withReadModelFactory; projection workers cannot receive host-only factory dependencies\",\n );\n }\n\n // Same class of bug, quieter: `readModelInstances` is handed to the\n // in-process ReadModelCoordinator only, so anything registered through\n // withReadModel is dropped on the floor under sharding rather than\n // rejected. Fail here instead.\n //\n // `withProjectionShards` replaces the coordinator wholesale, so it also\n // orphans subscriptionNotificationReadModel and processorManager — both\n // built unconditionally below, with no caller registration to guard on.\n // Under bare sharding they are built, initialized, and never fed an\n // operation, which silently disables GraphQL subscriptions and every\n // package-installed processor. (Consistency-token reads are fine: the\n // manager advances the host trackers from the shards' readmodel-indexed\n // reports.) Hosts that need subscriptions, processors or host read\n // models must use `withReadModelCoordinatorFactory` with the hybrid\n // coordinator (`createHybridProjectionCoordinatorFactory`) instead.\n if (\n this.projectionShardConfig !== undefined &&\n this.readModels.length > 0\n ) {\n throw new Error(\n \"withProjectionShards does not support read models registered through withReadModel; projection workers build their own read models from the shard config and would silently omit these\",\n );\n }\n\n // Third instance of the same bug, and the quietest: a built-in kind\n // absent from both lists is indexed by nobody. Checked here, before any\n // pool opens, and again in createProjectionShardManager for the\n // coordinator-factory path.\n if (this.projectionShardConfig !== undefined) {\n validateBuiltInKindCoverage(\n this.projectionShardConfig.preReadyKinds,\n this.projectionShardConfig.postReadyKinds,\n );\n }\n\n // One resolution pass feeds both sides: the host registry gets every\n // resolved module (in both executor modes), and importable sources form\n // the worker manifest.\n const resolvedSources = await resolveModelSources(\n this.documentModelSources,\n );\n if (this.workerPool) {\n if (resolvedSources.manifest.length === 0) {\n throw new Error(\n \"withWorkerPool requires at least one worker-importable document-model source ({ filePath } or { packageName }).\",\n );\n }\n if (resolvedSources.moduleOnlyKeys.length > 0) {\n throw new Error(\n `withWorkerPool requires worker-importable sources, but these models were registered only as live modules: ${resolvedSources.moduleOnlyKeys.join(\", \")}. Provide a { filePath } or { packageName } source for each.`,\n );\n }\n }\n this.resolvedModelManifest =\n resolvedSources.manifest.length > 0\n ? resolvedSources.manifest\n : undefined;\n this.moduleOnlyModelKeys = resolvedSources.moduleOnlyKeys;\n\n const documentModelRegistry = new DocumentModelRegistry();\n if (this.upgradeManifests.length > 0) {\n const results = documentModelRegistry.registerUpgradeManifests(\n ...this.upgradeManifests,\n );\n for (const result of results) {\n if (result.status === \"error\") {\n this.logger.error(\n \"Failed to register upgrade manifest: @error\",\n result.error.message,\n );\n }\n }\n }\n if (resolvedSources.modules.length > 0) {\n const results = documentModelRegistry.registerModules(\n ...resolvedSources.modules,\n );\n for (const result of results) {\n if (result.status === \"error\") {\n this.logger.error(\n \"Failed to register document model: @error\",\n result.error.message,\n );\n }\n }\n }\n\n const reactorDbConfig = this.resolveReactorDbConfig();\n const baseDatabase =\n this.kyselyInstance ??\n (reactorDbConfig\n ? await this.createPostgresDatabase(reactorDbConfig)\n : await createDefaultDatabase());\n\n if (this.migrationStrategy === \"auto\") {\n const result = await runMigrations(baseDatabase, REACTOR_SCHEMA);\n if (!result.success && result.error) {\n throw new Error(`Database migration failed: ${result.error.message}`);\n }\n }\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n\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 eventBus = this.eventBus || new EventBus();\n const resolver = this.documentModelLoader\n ? new DocumentModelResolver(\n documentModelRegistry,\n this.documentModelLoader,\n )\n : new NullDocumentModelResolver(documentModelRegistry);\n if (resolver instanceof DocumentModelResolver) {\n resolver.setModelLoadedHook((documentType) =>\n eventBus.emit(ReactorEventTypes.MODEL_LOADED, { documentType }),\n );\n }\n const queue = this.queueInstance ?? new InMemoryQueue(eventBus, resolver);\n const jobTracker = new InMemoryJobTracker(eventBus);\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: this.writeCacheConfig?.maxDocuments ?? 100,\n ringBufferSize: this.writeCacheConfig?.ringBufferSize ?? 10,\n keyframeInterval: this.writeCacheConfig?.keyframeInterval ?? 10,\n };\n\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n documentModelRegistry,\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: IExecutionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n let executorManager = this.executorManager;\n let executorStartCount = this.executorConfig.maxConcurrency ?? 1;\n if (!executorManager) {\n if (this.workerPool) {\n const pool = this.workerPool;\n let factory = pool.factory;\n if (!factory) {\n if (pool.db === undefined) {\n throw new Error(\n \"unreachable: worker pool configured without db or factory\",\n );\n }\n factory = await this.createDefaultWorkerFactory(\n pool.numWorkers,\n pool.db,\n pool.verifier,\n );\n }\n const poolManager = new WorkerPoolJobExecutorManager(\n factory,\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n operationIndex,\n this.executorConfig.jobTimeoutMs,\n this.executorConfig.deferredJobTtlMs,\n );\n executorManager = poolManager;\n executorStartCount = pool.numWorkers;\n if (resolver instanceof DocumentModelResolver) {\n resolver.setBroadcastHook((entry) => poolManager.loadModel(entry));\n }\n } else {\n executorManager = new SimpleJobExecutorManager(\n () =>\n new SimpleJobExecutor(\n this.logger!,\n documentModelRegistry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n this.driveContainerTypes,\n this.executorConfig,\n this.signatureVerifier,\n executionScope,\n ),\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n this.executorConfig.jobTimeoutMs,\n this.executorConfig.deferredJobTtlMs,\n );\n }\n }\n\n await executorManager.start(executorStartCount);\n\n // withReadModel + withReadModelFactory models only. Excludes\n // documentView/documentIndexer so a caller-supplied coordinator cannot\n // double-index them alongside a projection worker.\n const callerReadModels: IReadModel[] = Array.from(\n new Set([...this.readModels]),\n );\n\n // Initializing degraded must not take the reactor down -- a read model that\n // cannot reach its database should not stop the writes. But a bare\n // `console.error` and an absent component is how an operator finds out from\n // a user report instead of from the server: the process reports healthy\n // while answering from an index that stopped at boot. Each failure is now\n // logged against the component that failed and recorded on the module, so a\n // host can refuse readiness on a non-empty list.\n const degradedComponents: DegradedComponent[] = [];\n const startDegraded = (component: string, error: unknown): void => {\n degradedComponents.push({\n component,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n this.logger?.error(\n \"Reactor component started degraded: @component\",\n component,\n error,\n );\n };\n\n const documentViewConsistencyTracker = new ConsistencyTracker();\n const documentView = new KyselyDocumentView(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationStore,\n operationIndex,\n writeCache,\n documentViewConsistencyTracker,\n featureFlags.documentDecisions,\n );\n\n try {\n await documentView.init();\n } catch (error) {\n startDegraded(\"document view\", error);\n }\n\n const documentIndexerConsistencyTracker = new ConsistencyTracker();\n const documentIndexer = new KyselyDocumentIndexer(\n database as unknown as Kysely<IndexerDatabase>,\n operationIndex,\n writeCache,\n documentIndexerConsistencyTracker,\n );\n\n try {\n await documentIndexer.init();\n } catch (error) {\n startDegraded(\"document indexer\", error);\n }\n\n const subscriptionManager = new ReactorSubscriptionManager(\n new DefaultSubscriptionErrorHandler(),\n );\n\n const subscriptionNotificationReadModel =\n new SubscriptionNotificationReadModel(subscriptionManager, documentView);\n\n const processorManagerConsistencyTracker = new ConsistencyTracker();\n const processorManager = new ProcessorManager(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n this.logger!,\n this.driveContainerTypes,\n { legacyProcessorIds: this.features.legacyProcessorIds !== false },\n );\n\n try {\n await processorManager.init();\n } catch (error) {\n startDegraded(\"processor manager\", error);\n }\n\n for (const [index, factory] of this.readModelFactories.entries()) {\n // A read model that cannot build or catch up must not take the reactor\n // down with it: log and start degraded, as the indexers above do.\n //\n // A factory carries no id, and a failure has no instance to ask for one,\n // so it is named by registration order plus the function's own name when\n // it has one -- otherwise the log cannot say which of them failed.\n try {\n const readModel = await factory({\n documentModelRegistry,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n });\n callerReadModels.push(readModel);\n } catch (error) {\n startDegraded(\n `read model ${index}${factory.name ? ` (${factory.name})` : \"\"}`,\n error,\n );\n }\n }\n\n const readModelInstances: IReadModel[] = [\n ...callerReadModels,\n documentView,\n documentIndexer,\n ];\n\n const hostTrackers = {\n \"document-view\": documentViewConsistencyTracker,\n \"document-indexer\": documentIndexerConsistencyTracker,\n };\n const readModelCoordinator = this.readModelCoordinator\n ? this.readModelCoordinator\n : this.readModelCoordinatorFactory\n ? await this.readModelCoordinatorFactory({\n eventBus,\n logger: this.logger,\n readModels: callerReadModels,\n subscriptionNotificationReadModel,\n processorManager,\n documentView,\n documentIndexer,\n createProjectionShardManager: (config) =>\n this.createProjectionShardManager(\n config,\n eventBus,\n hostTrackers,\n false,\n ),\n registerShutdownHook: (hook) => this.shutdownHooks.push(hook),\n })\n : this.projectionShardConfig\n ? await this.createProjectionShardManager(\n this.projectionShardConfig,\n eventBus,\n hostTrackers,\n true,\n )\n : new ReadModelCoordinator(eventBus, readModelInstances, [\n subscriptionNotificationReadModel,\n processorManager,\n ]);\n\n const reactor = new Reactor(\n this.logger,\n documentModelRegistry,\n queue,\n jobTracker,\n readModelCoordinator,\n this.features,\n documentView,\n documentIndexer,\n operationStore,\n eventBus,\n executorManager,\n );\n\n let syncModule: InProcessSyncModule | undefined = undefined;\n if (this.channelScheme) {\n const factory =\n this.channelScheme === ChannelScheme.CONNECT\n ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue)\n : new GqlResponseChannelFactory(this.logger);\n\n const syncBuilder = new SyncBuilder().withChannelFactory(factory);\n syncModule = syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n this.driveContainerTypes,\n );\n await syncModule.syncManager.startup();\n } else if (this.syncBuilder) {\n syncModule = this.syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n this.driveContainerTypes,\n );\n await syncModule.syncManager.startup();\n }\n\n let groupReevaluationTrigger: GroupReevaluationTrigger | undefined;\n if (featureFlags.authGroups) {\n groupReevaluationTrigger = new GroupReevaluationTrigger(\n this.logger,\n eventBus,\n queue,\n operationIndex,\n );\n groupReevaluationTrigger.startup();\n }\n\n const module: InProcessReactorModule = {\n eventBus,\n documentModelRegistry,\n featureFlags,\n queue,\n jobTracker,\n executorManager,\n database,\n operationStore,\n keyframeStore,\n writeCache,\n operationIndex,\n documentView,\n documentViewConsistencyTracker,\n documentIndexer,\n documentIndexerConsistencyTracker,\n readModelCoordinator,\n subscriptionManager,\n processorManager,\n processorManagerConsistencyTracker,\n syncModule,\n reactor,\n groupReevaluationTrigger,\n pools: this.instrumentedPools,\n degradedComponents,\n };\n\n if (degradedComponents.length > 0) {\n // buildModule assigns a default logger before anything here runs.\n this.logger.warn(\n \"Reactor started with @count degraded component(s): @components\",\n degradedComponents.length,\n degradedComponents.map(({ component }) => component).join(\", \"),\n );\n }\n\n if (this.signalHandlersEnabled) {\n this.attachSignalHandlers(module);\n }\n\n return module;\n }\n\n /**\n * The single Postgres config for the parent, executor workers, and\n * projection shards. They must share one physical database (the parent\n * writes operations; workers and shards read them), so divergent\n * worker/shard targets throw. `withKysely` overrides the parent and is not\n * validated against a worker/shard `db`.\n */\n private resolveReactorDbConfig(): DbConfig | undefined {\n const workerDb = this.workerPool?.db;\n const projectionDb = this.projectionShardConfig?.db;\n if (\n workerDb &&\n projectionDb &&\n !sameDatabaseTarget(workerDb, projectionDb)\n ) {\n throw new Error(\n \"withWorkerPool({ db }) and withProjectionShards({ db }) must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.\",\n );\n }\n return workerDb ?? projectionDb;\n }\n\n /**\n * Constructs a {@link ProjectionShardManager} bound to the host event\n * bus. Builds the default thread-transport factory unless one was\n * injected via {@link withProjectionWorkerFactory}. Calls\n * `manager.startup()` so all N workers reach READY before the reactor\n * is returned to the caller.\n *\n * @param consistencyTrackers The host's trackers for the built-in read\n * models, keyed by kind. Under sharding the host's own copies of those\n * models never index an operation, so the manager advances these from the\n * shards' relayed indexing reports; without them every read carrying a\n * consistency token waits forever.\n * @param registerShutdownHook Whether the builder owns `manager.shutdown()`\n * at signal time. False for the coordinator-factory path, whose factory\n * registers its own hook so host chains drain before the worker stops.\n */\n private async createProjectionShardManager(\n config: ProjectionShardBuilderConfig & ProjectionShardHooks,\n eventBus: IEventBus,\n consistencyTrackers: Partial<\n Record<BuiltInReadModelKind, IConsistencyTracker>\n >,\n registerShutdownHook: boolean,\n ): Promise<ProjectionShardManager> {\n const parentDb = this.resolveReactorDbConfig();\n const baseDb = config.db ?? parentDb;\n // withProjectionShards and withReadModelCoordinatorFactory are mutually\n // exclusive, so the configured one names the caller in every message here.\n const caller =\n this.projectionShardConfig !== undefined\n ? \"withProjectionShards\"\n : \"withReadModelCoordinatorFactory\";\n if (!baseDb) {\n throw new Error(\n `${caller} requires a db (or an executor worker pool configured with one); projection workers need connection info to open their own pools.`,\n );\n }\n const workerDb = this.workerPool?.db;\n if (config.db && workerDb && !sameDatabaseTarget(workerDb, config.db)) {\n throw new Error(\n \"withWorkerPool({ db }) and the projection worker db must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.\",\n );\n }\n // The parent serves reads from the tables the worker writes, so the\n // worker's db must name the parent's database too. A `db` that reached\n // this method through the coordinator factory contributed nothing to\n // `resolveReactorDbConfig`: the factory does not run until long after the\n // parent database is built, so without this guard the parent silently\n // falls back to the default embedded database while the worker projects\n // into Postgres, and the parent's read models never see a projected row.\n // `withKysely` owns the parent connection outright and is not validated.\n if (config.db && this.kyselyInstance === undefined) {\n if (!parentDb) {\n throw new Error(\n `The projection worker db passed to ${caller} must also be the parent reactor's database, but nothing configures the parent: it would fall back to the default embedded database while the worker projects into Postgres. Pass the same { db } to withWorkerPool, or give the parent its connection with withKysely.`,\n );\n }\n if (!sameDatabaseTarget(config.db, parentDb)) {\n throw new Error(\n `The projection worker db passed to ${caller} and the parent reactor database must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.`,\n );\n }\n }\n validateBuiltInKindCoverage(config.preReadyKinds, config.postReadyKinds);\n // The executor pool guard in buildModule only runs with a worker pool.\n if (this.moduleOnlyModelKeys.length > 0) {\n throw new Error(\n `projection workers require worker-importable sources, but these models were registered only as live modules: ${this.moduleOnlyModelKeys.join(\", \")}. Provide a { filePath } or { packageName } source for each.`,\n );\n }\n const models = this.resolvedModelManifest ?? [];\n const db: DbConfig = {\n ...baseDb,\n poolSize: config.poolSize ?? baseDb.poolSize,\n applicationName: config.db?.applicationName ?? \"reactor-projection-shard\",\n };\n const factory =\n this.projectionWorkerFactory ??\n (await this.createDefaultProjectionWorkerFactory());\n const poolInstrumentations: ForwardingPoolInstrumentation[] = [];\n for (let i = 0; i < config.shardCount; i++) {\n const forwarder = createForwardingPoolInstrumentation(\n `projection-shard-${i}`,\n );\n poolInstrumentations.push(forwarder);\n this.instrumentedPools.push(forwarder);\n }\n const { ProjectionShardManager } =\n await import(\"../projection/projection-shard-manager.js\");\n const manager = new ProjectionShardManager({\n shardCount: config.shardCount,\n db,\n models,\n preReadyKinds: config.preReadyKinds,\n postReadyKinds: config.postReadyKinds,\n factory,\n logger: this.logger!,\n hostBus: eventBus,\n initTimeoutMs: config.initTimeoutMs,\n shutdownGraceMs: config.shutdownGraceMs,\n drainTimeoutMs: config.drainTimeoutMs,\n chainDepthReportIntervalMs: config.chainDepthReportIntervalMs,\n poolInstrumentations,\n consistencyTrackers,\n onReadReady: config.onReadReady,\n onShardFatal: config.onShardFatal,\n });\n try {\n await manager.startup();\n } catch (error) {\n // A shard that never reached READY leaves a live worker thread behind,\n // which keeps the process alive after the boot error propagates.\n try {\n await manager.shutdown();\n } catch (shutdownError) {\n this.logger!.warn(\n \"projection shard manager shutdown after a failed startup also failed: @error\",\n shutdownError,\n );\n }\n throw error;\n }\n if (registerShutdownHook) {\n this.shutdownHooks.push(() => manager.shutdown());\n }\n return manager;\n }\n\n private async createDefaultProjectionWorkerFactory(): Promise<ProjectionWorkerFactory> {\n const [{ createProjectionThreadTransport }, { projectionWorkerEntryPath }] =\n await Promise.all([\n import(\"../projection/transport.js\"),\n import(\"../projection/projection-worker/index.js\"),\n ]);\n return () => createProjectionThreadTransport(projectionWorkerEntryPath);\n }\n\n /**\n * Default {@link WorkerFactory} used when the pool options carry no\n * custom `factory`. Each worker spawns a real `node:worker_threads`\n * Worker pointing at the compiled `worker/entry.js`.\n */\n private async createDefaultWorkerFactory(\n numWorkers: number,\n db: DbConfig,\n signatureVerifier: SignatureVerifierSpec | undefined,\n ): Promise<WorkerFactory> {\n const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] =\n await Promise.all([\n import(\"../executor/worker/worker-handle.js\"),\n import(\"../executor/worker/transport.js\"),\n import(\"../executor/worker/index.js\"),\n ]);\n // The wire protocol keeps its full shape; only \"thread\" is implemented.\n const poolConfig: WorkerPoolConfig = {\n enabled: true,\n numWorkers,\n workerType: \"thread\",\n };\n const models = this.resolvedModelManifest ?? [];\n const logger = this.logger!;\n return (index: number) => {\n const workerId = `reactor-worker-${index}`;\n const poolInstrumentation = createForwardingPoolInstrumentation(workerId);\n this.instrumentedPools.push(poolInstrumentation);\n return new WorkerHandle({\n workerId,\n index,\n transport: createThreadTransport(workerEntryPath),\n initPayload: {\n poolConfig,\n db,\n signatureVerifier,\n models,\n executorConfig: this.executorConfig,\n },\n logger,\n poolInstrumentation,\n });\n };\n }\n\n /**\n * Builds the parent Kysely instance against a real Postgres server using\n * the same {@link DbConfig} the workers receive at init. Used in the\n * worker-pool path so the parent reactor and each worker thread share\n * storage; PGlite cannot be shared across threads. The constructed pool\n * is wrapped with {@link instrumentPgPool} and the resulting\n * {@link PoolInstrumentation} is pushed onto {@link instrumentedPools} so\n * the reactor module exposes acquire-wait and pool-stat surfaces.\n */\n private async createPostgresDatabase(\n config: DbConfig,\n ): Promise<Kysely<Database>> {\n const { Kysely, PostgresDialect } = await import(\"kysely\");\n const pgModule = await import(\"pg\");\n const Pool = pgModule.default.Pool;\n const pool = new Pool({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n ssl: config.ssl ? { rejectUnauthorized: false } : undefined,\n application_name: config.applicationName,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n this.instrumentedPools.push(\n instrumentPgPool(pool, config.applicationName ?? \"reactor-host\"),\n );\n return new Kysely<Database>({\n dialect: new PostgresDialect({ pool }),\n });\n }\n\n private attachSignalHandlers(module: InProcessReactorModule): void {\n if (\n typeof globalThis === \"undefined\" ||\n !(\"process\" in globalThis) ||\n typeof globalThis.process.on !== \"function\"\n ) {\n return;\n }\n\n const nodeProcess = globalThis.process;\n const realExit = nodeProcess.exit.bind(nodeProcess);\n let shutdownInProgress = false;\n let pendingExitCode: number | undefined;\n\n // While our async cleanup runs, swap process.exit for a recording shim so\n // peer SIGINT handlers (notably vite's bindCLIShortcuts handler, default\n // enabled) cannot terminate the process before reactor.kill() and\n // database.destroy() finish. Without this guard the process exits during\n // the first microtask of our async chain, leaving e.g. PGlite's WAL dirty\n // and the data-dir lock held.\n const handler = async (signal: string) => {\n if (shutdownInProgress) {\n this.logger!.warn(\n `Received ${signal} again, continuing graceful shutdown...`,\n );\n return;\n }\n\n shutdownInProgress = true;\n nodeProcess.exit = ((code?: number) => {\n pendingExitCode ??= code ?? 0;\n return undefined as never;\n }) as typeof nodeProcess.exit;\n\n this.logger!.info(`Received ${signal}, starting graceful shutdown...`);\n\n const status = module.reactor.kill();\n\n try {\n await status.completed;\n } catch (error) {\n this.logger!.error(\"Shutdown failed waiting for reactor:\", error);\n nodeProcess.exit = realExit;\n realExit(1);\n return;\n }\n\n for (const hook of this.shutdownHooks) {\n try {\n await hook();\n } catch (error) {\n this.logger!.error(\"Shutdown hook failed:\", error);\n }\n }\n\n try {\n await module.database.destroy();\n } catch (error) {\n this.logger!.error(\"Shutdown failed destroying database:\", error);\n nodeProcess.exit = realExit;\n realExit(1);\n return;\n }\n\n this.logger!.info(\"Shutdown complete\");\n nodeProcess.exit = realExit;\n realExit(pendingExitCode ?? 0);\n };\n\n nodeProcess.prependListener(\"SIGINT\", () => void handler(\"SIGINT\"));\n nodeProcess.prependListener(\"SIGTERM\", () => void handler(\"SIGTERM\"));\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobReadReadyEvent,\n type ReadModelBatchCompletedEvent,\n type ReadModelIndexedEvent,\n type ReadModelIndexingStage,\n} from \"../events/types.js\";\nimport type {\n ILiveReadModelCoordinator,\n IReadModel,\n ReadModelRegistrationStage,\n} from \"../read-models/interfaces.js\";\nimport type { ProjectionShardManager } from \"./projection-shard-manager.js\";\n\nexport type HybridProjectionCoordinatorOptions = {\n eventBus: IEventBus;\n logger: ILogger;\n manager: ProjectionShardManager;\n /** Caller and factory registered models. Never documentView/documentIndexer. */\n preReady: IReadModel[];\n /** subscriptionNotificationReadModel, processorManager. */\n postReady: IReadModel[];\n /** Included in `readModels` for getReadModel() lookup; never indexed here. */\n lookupOnly: IReadModel[];\n};\n\n/** Host-side stages on a per-queueKey chain driven by the worker's read-ready. */\nexport class HybridProjectionCoordinator implements ILiveReadModelCoordinator {\n /** One array, mutated in place: reactor-api captures it by reference once. */\n readonly readModels: IReadModel[];\n\n private readonly eventBus: IEventBus;\n private readonly logger: ILogger;\n private readonly manager: ProjectionShardManager;\n private readonly preReady: IReadModel[];\n private readonly postReady: IReadModel[];\n private readonly chains = new Map<string, Promise<void>>();\n\n constructor(options: HybridProjectionCoordinatorOptions) {\n this.eventBus = options.eventBus;\n this.logger = options.logger;\n this.manager = options.manager;\n this.preReady = options.preReady;\n this.postReady = options.postReady;\n this.readModels = [\n ...options.preReady,\n ...options.postReady,\n ...options.lookupOnly,\n ];\n }\n\n start(): void {\n this.manager.start();\n }\n\n stop(): void {\n this.manager.stop();\n }\n\n /** Wired as `onReadReady`; host trackers are already advanced (port FIFO). */\n acceptReadReady(event: JobReadReadyEvent): void {\n if (event.operations.length === 0) {\n void this.manager\n .emitReadReady(event)\n .catch((err: unknown) =>\n this.logger.error(\n \"JOB_READ_READY emit failed for job @jobId: @Error\",\n { jobId: event.jobId },\n err,\n ),\n );\n return;\n }\n\n const enqueuedAt = performance.now();\n const key = this.queueKeyFor(event);\n const previous = this.chains.get(key) ?? Promise.resolve();\n const current = previous.then(() => this.runHostChain(event, enqueuedAt));\n\n this.chains.set(key, current);\n void current.finally(() => {\n if (this.chains.get(key) === current) {\n this.chains.delete(key);\n }\n });\n }\n\n addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void {\n if (this.readModels.some(({ name }) => name === readModel.name)) {\n throw new Error(`Read model \"${readModel.name}\" is already registered`);\n }\n\n if (stage === \"pre_ready\") {\n this.preReady.push(readModel);\n } else {\n this.postReady.push(readModel);\n }\n this.readModels.push(readModel);\n }\n\n getChainDepth(): number {\n return this.manager.getChainDepth() + this.chains.size;\n }\n\n /** Worker chains flush first, so every relayed read-ready is in `chains`. */\n async drain(): Promise<void> {\n await this.manager.drain();\n while (this.chains.size > 0) {\n const pending = Array.from(this.chains.values());\n await Promise.allSettled(pending);\n }\n }\n\n /** Builder shutdown hook; reaches `manager.shutdown()` even when drain fails. */\n async shutdown(): Promise<void> {\n try {\n await this.drain();\n } catch (error) {\n this.logger.warn(\n \"hybrid coordinator drain failed during shutdown: @Error\",\n error,\n );\n }\n await this.manager.shutdown();\n }\n\n private async runHostChain(\n event: JobReadReadyEvent,\n enqueuedAt: number,\n ): Promise<void> {\n const chainWaitDurationMs = performance.now() - enqueuedAt;\n const preReadyStart = performance.now();\n // A failing host read model must not withhold JOB_READ_READY from awaiters.\n try {\n await Promise.all(\n this.preReady.map((readModel) =>\n this.indexWithTiming(readModel, \"pre_ready\", event),\n ),\n );\n } catch (error) {\n this.logger.error(\n \"Host pre-ready read model indexing failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n\n const preReadyDurationMs = performance.now() - preReadyStart;\n\n // Awaited: the subscription read model must run after READ_READY is emitted.\n const emitStart = performance.now();\n try {\n await this.manager.emitReadReady(event);\n } catch (error) {\n this.logger.error(\n \"JOB_READ_READY emit failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n const emitDurationMs = performance.now() - emitStart;\n\n const postReadyStart = performance.now();\n try {\n await Promise.all(\n this.postReady.map((readModel) =>\n this.indexWithTiming(readModel, \"post_ready\", event),\n ),\n );\n } catch (error) {\n this.logger.error(\n \"Host post-ready read model indexing failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n const postReadyDurationMs = performance.now() - postReadyStart;\n\n // The worker reports its own batch for this job, covering the built-in\n // read models only. Without this second report the host's stages — every\n // read model the hybrid coordinator exists to keep on the host — are\n // absent from the batch metrics, which is exactly what a rollout measures.\n this.emitBatchCompleted({\n jobId: event.jobId,\n batchSize: event.operations.length,\n chainWaitDurationMs,\n preReadyDurationMs,\n emitDurationMs,\n postReadyDurationMs,\n });\n }\n\n private async indexWithTiming(\n readModel: IReadModel,\n stage: ReadModelIndexingStage,\n event: JobReadReadyEvent,\n ): Promise<void> {\n const start = performance.now();\n let success = false;\n try {\n await readModel.indexOperations(event.operations);\n success = true;\n } finally {\n this.emitReadModelIndexed({\n jobId: event.jobId,\n readModelName: readModel.name,\n stage,\n durationMs: performance.now() - start,\n operationCount: event.operations.length,\n success,\n });\n }\n }\n\n private emitBatchCompleted(payload: ReadModelBatchCompletedEvent): void {\n void this.eventBus\n .emit(ReactorEventTypes.READMODEL_BATCH_COMPLETED, payload)\n .catch((err: unknown) =>\n this.logger.error(\n \"READMODEL_BATCH_COMPLETED emit failed for job @jobId: @Error\",\n { jobId: payload.jobId },\n err,\n ),\n );\n }\n\n private emitReadModelIndexed(payload: ReadModelIndexedEvent): void {\n void this.eventBus\n .emit(ReactorEventTypes.READMODEL_INDEXED, payload)\n .catch((err: unknown) =>\n this.logger.error(\n \"READMODEL_INDEXED emit failed for job @jobId: @Error\",\n { jobId: payload.jobId },\n err,\n ),\n );\n }\n\n private queueKeyFor(event: JobReadReadyEvent): string {\n const ctx = event.operations[0]!.context;\n return `${ctx.documentId}:${ctx.scope}:${ctx.branch}`;\n }\n}\n","import type {\n ReadModelCoordinatorFactory,\n ReadModelCoordinatorFactoryDeps,\n} from \"../core/reactor-builder.js\";\nimport { HybridProjectionCoordinator } from \"./hybrid-projection-coordinator.js\";\nimport type { DbConfig } from \"./protocol.js\";\n\nexport type HybridProjectionOptions = {\n /** Defaults to 1: one worker captures the whole win (Run 11). */\n shardCount?: number;\n poolSize?: number;\n db?: DbConfig;\n onFatal?: (shardId: string, reason: Error) => void;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\ntype CoordinatorRef = { current: HybridProjectionCoordinator | undefined };\n\n/** Built-ins in one projection worker; all other read models stay on the host. */\nexport function createHybridProjectionCoordinatorFactory(\n options: HybridProjectionOptions = {},\n): ReadModelCoordinatorFactory {\n return async (deps: ReadModelCoordinatorFactoryDeps) => {\n // Set before start(): onReadReady cannot fire until a write-ready is routed.\n const ref: CoordinatorRef = { current: undefined };\n const manager = await deps.createProjectionShardManager({\n shardCount: options.shardCount ?? 1,\n preReadyKinds: [\"document-view\", \"document-indexer\"],\n postReadyKinds: [],\n db: options.db,\n poolSize: options.poolSize,\n initTimeoutMs: options.initTimeoutMs,\n shutdownGraceMs: options.shutdownGraceMs,\n drainTimeoutMs: options.drainTimeoutMs,\n chainDepthReportIntervalMs: options.chainDepthReportIntervalMs,\n onReadReady: (event) => ref.current!.acceptReadReady(event),\n onShardFatal: options.onFatal,\n });\n const coordinator = new HybridProjectionCoordinator({\n eventBus: deps.eventBus,\n logger: deps.logger,\n manager,\n preReady: deps.readModels,\n postReady: [\n deps.subscriptionNotificationReadModel,\n deps.processorManager,\n ],\n lookupOnly: [deps.documentView, deps.documentIndexer],\n });\n ref.current = coordinator;\n deps.registerShutdownHook(() => coordinator.shutdown());\n return coordinator;\n };\n}\n","import type { ISigner, Signature } from \"@powerhousedao/shared/document-model\";\n\n/**\n * A no-op signer that returns empty values for all methods.\n * Used when signing is not required.\n */\nexport class PassthroughSigner implements ISigner {\n publicKey = {} as unknown as CryptoKey;\n\n sign(): Promise<Uint8Array> {\n return Promise.resolve(new Uint8Array(0));\n }\n\n verify(): Promise<void> {\n return Promise.resolve();\n }\n\n signAction(): Promise<Signature> {\n return Promise.resolve([\"\", \"\", \"\", \"\", \"\"]);\n }\n}\n","import type { ISigner } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { ActionEvaluationConfig } from \"../client/reactor-client.js\";\nimport { ReactorClient } from \"../client/reactor-client.js\";\nimport type { IReadGate } from \"../decision/read-gate.js\";\nimport {\n BareReadGate,\n ModelReadGate,\n readDecisionModel,\n} from \"../decision/read-gate.js\";\nimport type { RegisteredDecisionModel } from \"../decision/registered-model.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IDocumentModelLoader } from \"../registry/interfaces.js\";\nimport { JobAwaiter, type IJobAwaiter } from \"../shared/awaiter.js\";\nimport { PassthroughSigner } from \"../signer/passthrough-signer.js\";\nimport type {\n SignatureVerificationHandler,\n SignerConfig,\n} from \"../signer/types.js\";\nimport type { IDocumentIndexer, IDocumentView } from \"../storage/interfaces.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { ReactorBuilder } from \"./reactor-builder.js\";\nimport type {\n InProcessReactorClientModule,\n InProcessReactorModule,\n IReactor,\n} from \"./types.js\";\n\n/**\n * Builder class for constructing ReactorClient instances with proper configuration\n */\nexport class ReactorClientBuilder {\n private logger?: ILogger;\n private reactorBuilder?: ReactorBuilder;\n private reactor?: IReactor;\n private eventBus?: IEventBus;\n private documentIndexer?: IDocumentIndexer;\n private documentView?: IDocumentView;\n private signer?: ISigner;\n private signatureVerifier?: SignatureVerificationHandler;\n private subscriptionManager?: IReactorSubscriptionManager;\n private jobAwaiter?: IJobAwaiter;\n private documentModelLoader?: IDocumentModelLoader;\n private readGate?: IReadGate;\n\n /**\n * Sets the logger for the ReactorClient.\n * @param logger - The logger to use.\n * @returns The ReactorClientBuilder instance.\n */\n public withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n /**\n * Either this or withReactor must be set.\n */\n public withReactorBuilder(reactorBuilder: ReactorBuilder): this {\n if (this.reactor) {\n throw new Error(\"Reactor is already set\");\n }\n\n this.reactorBuilder = reactorBuilder;\n return this;\n }\n\n /**\n * Either this or withReactorBuilder must be set.\n */\n public withReactor(\n reactor: IReactor,\n eventBus: IEventBus,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n ): this {\n if (this.reactorBuilder) {\n throw new Error(\"ReactorBuilder is already set\");\n }\n\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n return this;\n }\n\n /**\n * Sets the signer configuration for signing and verifying actions.\n *\n * @param config - Either an ISigner for signing only, or a SignerConfig for both signing and verification\n */\n public withSigner(config: ISigner | SignerConfig): this {\n if (\"signer\" in config) {\n this.signer = config.signer;\n this.signatureVerifier = config.verifier;\n } else {\n this.signer = config;\n }\n return this;\n }\n\n public withSubscriptionManager(\n subscriptionManager: IReactorSubscriptionManager,\n ): this {\n this.subscriptionManager = subscriptionManager;\n return this;\n }\n\n public withJobAwaiter(jobAwaiter: IJobAwaiter): this {\n this.jobAwaiter = jobAwaiter;\n return this;\n }\n\n public withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n /**\n * Overrides how reads are gated. A client built from a ReactorBuilder derives\n * this from that reactor's flags; one built from `withReactor` cannot, because\n * it is handed no flags and no registry, so it gates on the policy alone\n * unless a gate is supplied here.\n */\n public withReadGate(readGate: IReadGate): this {\n this.readGate = readGate;\n return this;\n }\n\n /**\n * The gate the resolved model calls for. Below authEnforcement there is no\n * model to enforce -- the registered one ignores the auth scope -- so the\n * policy is evaluated on its own, which is what reads did before the model\n * existed. Group serving turns on with authGroups, because below it a\n * `{ group }` grant does not match, so a served roster is one no grant can\n * use.\n */\n private resolveReadGate(\n reactorModule: InProcessReactorModule | undefined,\n documentView: IDocumentView,\n model: RegisteredDecisionModel | undefined,\n ): IReadGate {\n if (!reactorModule || model === undefined) {\n return new BareReadGate();\n }\n\n return new ModelReadGate(\n model,\n documentView,\n reactorModule.featureFlags.authGroups,\n reactorModule.operationIndex,\n this.logger,\n );\n }\n\n /**\n * What the client answers an authorization preflight from, or undefined when\n * it answers none.\n *\n * Resolved from the same model reads enforce, so a preflight and a read can\n * never decide against different models. Undefined below authEnforcement, and\n * undefined on the `withReactor` path, where there are no flags and no\n * registry to select a model with -- a client with no model refuses the\n * preflight rather than answering it from the legacy host-side permission\n * tables. Deliberately not derived from the read gate: `withReadGate`\n * overrides that, so sniffing the gate's type would report enforcement from a\n * caller's substitution.\n */\n private resolveActionEvaluation(\n reactorModule: InProcessReactorModule | undefined,\n model: RegisteredDecisionModel | undefined,\n ): ActionEvaluationConfig | undefined {\n return reactorModule && model\n ? { model, flags: reactorModule.featureFlags }\n : undefined;\n }\n\n public async build(): Promise<ReactorClient> {\n const module = await this.buildModule();\n return module.client;\n }\n\n public async buildModule(): Promise<InProcessReactorClientModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor-client\"]);\n }\n\n let reactor: IReactor;\n let eventBus: IEventBus;\n let documentIndexer: IDocumentIndexer;\n let documentView: IDocumentView;\n let reactorModule: InProcessReactorModule | undefined;\n\n if (this.reactorBuilder) {\n if (this.signatureVerifier) {\n this.reactorBuilder.withSignatureVerifier(this.signatureVerifier);\n }\n if (this.documentModelLoader) {\n this.reactorBuilder.withDocumentModelLoader(this.documentModelLoader);\n }\n reactorModule = await this.reactorBuilder.buildModule();\n reactor = reactorModule.reactor;\n eventBus = reactorModule.eventBus;\n documentIndexer = reactorModule.documentIndexer;\n documentView = reactorModule.documentView;\n } else if (\n this.reactor &&\n this.eventBus &&\n this.documentIndexer &&\n this.documentView\n ) {\n reactor = this.reactor;\n eventBus = this.eventBus;\n documentIndexer = this.documentIndexer;\n documentView = this.documentView;\n reactorModule = undefined;\n } else {\n throw new Error(\n \"Either ReactorBuilder or (Reactor + EventBus + DocumentIndexer + DocumentView) is required\",\n );\n }\n\n const signer = this.signer ?? new PassthroughSigner();\n\n const subscriptionManager =\n this.subscriptionManager ??\n reactorModule?.subscriptionManager ??\n new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());\n\n const jobAwaiter =\n this.jobAwaiter ??\n new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n\n const decisionModel = reactorModule\n ? readDecisionModel(\n reactorModule.featureFlags,\n reactorModule.documentModelRegistry,\n )\n : undefined;\n\n const client = new ReactorClient(\n this.logger,\n reactor,\n signer,\n subscriptionManager,\n jobAwaiter,\n documentIndexer,\n documentView,\n this.readGate ??\n this.resolveReadGate(reactorModule, documentView, decisionModel),\n this.resolveActionEvaluation(reactorModule, decisionModel),\n );\n\n return {\n client,\n reactor,\n eventBus,\n documentIndexer,\n documentView,\n signer,\n subscriptionManager,\n jobAwaiter,\n reactorModule,\n };\n }\n}\n","export interface ParsedDriveUrl {\n url: string;\n driveId: string;\n graphqlEndpoint: string;\n}\n\n/**\n * Parse a drive URL to extract drive ID and construct GraphQL endpoint.\n * Preserves any subpath prefix so the result is correct when the reactor is\n * served behind a proxy at a non-root path.\n * e.g., \"http://localhost:4001/d/abc123\" -> { driveId: \"abc123\", graphqlEndpoint: \"http://localhost:4001/graphql/r\" }\n * e.g., \"https://example.com/api/reactor/d/abc123\" -> { ..., graphqlEndpoint: \"https://example.com/api/reactor/graphql/r\" }\n */\nexport function parseDriveUrl(url: string): ParsedDriveUrl {\n const parsedUrl = new URL(url);\n const driveId = url.split(\"/\").pop() ?? \"\";\n const basePath = parsedUrl.pathname.replace(/\\/d\\/[^/]+\\/?$/, \"\");\n const graphqlEndpoint = `${parsedUrl.protocol}//${parsedUrl.host}${basePath}/graphql/r`;\n return { url, driveId, graphqlEndpoint };\n}\n\n/**\n * Extract drive ID from a drive URL.\n */\nexport function driveIdFromUrl(url: string): string {\n return url.split(\"/\").pop() ?? \"\";\n}\n","import type {\n AuthSubject,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type { IDocumentView } from \"../storage/interfaces.js\";\nimport type { IReadGate } from \"./read-gate.js\";\nimport { ALWAYS_READABLE_SCOPES } from \"./read-gate.js\";\n\n/** What a subject may read of a document that is not there to be read. */\nconst META_ONLY = (scope: string): boolean => ALWAYS_READABLE_SCOPES.has(scope);\n\n/**\n * A read gate asked by document id rather than by document.\n *\n * Serving works from an id: an outbox entry names a document, a branch and a\n * scope, and carries no state. Fetching the document is therefore the serving\n * path's own job, and it is the only thing this adds to the gate it wraps.\n */\nexport class SyncScopeGate {\n constructor(\n private readonly gate: IReadGate,\n private readonly documentView: IDocumentView,\n private readonly logger?: ILogger,\n ) {}\n\n /**\n * Which scopes of one document the subject may be served.\n *\n * A document this replica cannot produce yields the metadata scopes and\n * nothing else. That is the fail-closed direction, and it is safe to fail\n * closed here precisely because serving withholds rather than consumes: the\n * entry stays in the outbox and the next poll asks again, so a document that\n * is merely not indexed yet is delayed rather than lost.\n *\n * Any other failure is rethrown. A read side that is down must not read as a\n * silent, universal denial, because a denial that looks like a policy is one\n * nobody investigates.\n */\n async scopePredicateById(\n documentId: string,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n let document: PHDocument;\n try {\n document = await this.documentView.get(\n documentId,\n { branch },\n undefined,\n signal,\n );\n } catch (error) {\n if (!DocumentNotFoundError.isError(error)) {\n throw error;\n }\n this.logger?.verbose(\n `Withholding ${documentId} on branch ${branch}: the read side does not hold it`,\n );\n return META_ONLY;\n }\n\n return this.gate.scopePredicate(document, subject, branch, signal);\n }\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\nimport {\n garbageCollect,\n sortOperations,\n} from \"@powerhousedao/shared/document-model\";\n\n/** Where a stream's stored order contradicts its timestamps. */\nexport type OutOfOrderPair = {\n previous: Operation;\n current: Operation;\n /**\n * `descending` cannot be walked at all. `tied` walks fine — the intra-stream\n * rule breaks the tie by index — but violates the monotonic auth rule, so a\n * stream holding one can never be replicated to a peer that lacks it.\n */\n kind: \"descending\" | \"tied\";\n};\n\n/**\n * The first pair of effective operations whose stored order contradicts their\n * timestamps, or undefined when the stream is in position order.\n *\n * Such a stream cannot be walked, and the auth stream is never reshuffled once\n * the monotonic rule is on, so run this before enabling enforcement on a fleet.\n *\n * `requireStrict` additionally rejects a tie, which is what the auth stream's\n * monotonic rule requires and what the walk alone does not care about.\n */\nexport function firstOutOfOrderPair(\n operations: Operation[],\n options?: { requireStrict?: boolean },\n): OutOfOrderPair | undefined {\n const requireStrict = options?.requireStrict ?? false;\n const effective = garbageCollect(sortOperations([...operations]));\n\n for (let i = 1; i < effective.length; i++) {\n const previous = effective[i - 1];\n const current = effective[i];\n\n // Parsed rather than compared through comparePositions: one stream makes the\n // cross-stream rules inert, and a tie has to be visible here rather than\n // resolved by index.\n const previousAt = Date.parse(previous.timestampUtcMs);\n const currentAt = Date.parse(current.timestampUtcMs);\n\n if (currentAt < previousAt) {\n return { previous, current, kind: \"descending\" };\n }\n if (requireStrict && currentAt === previousAt) {\n return { previous, current, kind: \"tied\" };\n }\n }\n\n return undefined;\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\n\n/**\n * Generic interface for any read model that can index operations.\n * Implementations include IDocumentView, search indices, caches, etc.\n */\nexport interface IReadModel {\n /**\n * Unique name identifying this read model, used for lookup via getReadModel.\n */\n readonly name: string;\n\n /**\n * Indexes a list of operations into the read model.\n * This method is called asynchronously when operations are written to the operation store.\n *\n * @param operations - The operations with their context to index\n */\n indexOperations(operations: OperationWithContext[]): Promise<void>;\n}\n\n/**\n * Coordinates read model synchronization with operation writes.\n * Listens to operation events from the event bus and updates all registered read models.\n */\nexport interface IReadModelCoordinator {\n /**\n * All registered read models (pre-ready and post-ready).\n */\n readonly readModels: IReadModel[];\n\n /**\n * Start listening for operation events and updating read models.\n */\n start(): void;\n\n /**\n * Stop listening and clean up subscriptions.\n */\n stop(): void;\n\n /**\n * Resolves when every per-queueKey projection chain has flushed.\n * Intended for test fixtures and explicit shutdown.\n */\n drain(): Promise<void>;\n\n /**\n * Current number of in-flight per-queueKey projection chains.\n * Used as a backpressure signal by observability gauges.\n */\n getChainDepth(): number;\n}\n\nexport type ReadModelRegistrationStage = \"pre_ready\" | \"post_ready\";\n\n/**\n * Optional capability exposed by coordinators that support adding read models\n * after construction. Custom and remote coordinators are not required to\n * implement it.\n */\nexport interface ILiveReadModelCoordinator extends IReadModelCoordinator {\n addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void;\n}\n\nexport function supportsLiveReadModelRegistration(\n coordinator: IReadModelCoordinator,\n): coordinator is ILiveReadModelCoordinator {\n return (\n \"addReadModel\" in coordinator &&\n typeof coordinator.addReadModel === \"function\"\n );\n}\n","import type { IProcessorHostModuleBase } from \"@powerhousedao/shared/processors\";\nimport type { IReactorClient } from \"../client/types.js\";\nimport type { IReadModel } from \"../read-models/interfaces.js\";\nimport type { ReactorReadModels } from \"../read-models/names.js\";\n\n/**\n * Host module fields every reactor host provides: the shared core plus the\n * reactor client and typed read-model lookup. Hosts extend it with what only\n * they can name (the attachment client) as `IProcessorHostModule`.\n */\nexport interface IReactorProcessorHostModuleBase extends IProcessorHostModuleBase {\n client: IReactorClient;\n /**\n * Retrieves a registered read model by name.\n *\n * Reactor-registered names are typed via `ReactorReadModels` — hover a key\n * there for what each model holds:\n * - `\"document-view\"` (materialized document state, `IDocumentView`)\n * - `\"document-indexer\"` (document relationship graph, `IDocumentIndexer`).\n *\n * Other names return the caller-supplied type.\n * Throws if no read model with that name is registered.\n */\n getReadModel<K extends keyof ReactorReadModels>(\n name: K,\n ): ReactorReadModels[K];\n getReadModel<T>(name: string): T;\n}\n\nexport type ReactorHostModuleBaseOptions = Pick<\n IProcessorHostModuleBase,\n \"analyticsStore\" | \"relationalDb\" | \"processorApp\" | \"config\"\n> & {\n client: IReactorClient;\n /** Registered read models, typically `readModelCoordinator.readModels`. */\n readModels: ReadonlyArray<Pick<IReadModel, \"name\">>;\n};\n\n/** Builds the reactor-level host module; hosts spread it and add their own fields. */\nexport function createReactorHostModuleBase(\n options: ReactorHostModuleBaseOptions,\n): IReactorProcessorHostModuleBase {\n const { client, readModels, ...core } = options;\n return {\n ...core,\n client,\n dispatch: {\n async execute(docId, branch, actions, signal) {\n const jobInfo = await client.executeAsync(\n docId,\n branch,\n actions,\n signal,\n );\n return { id: jobInfo.id, status: jobInfo.status };\n },\n },\n getReadModel<T>(name: string): T {\n const model = readModels.find((m) => m.name === name);\n if (!model) {\n throw new Error(`Read model \"${name}\" not found`);\n }\n return model as unknown as T;\n },\n };\n}\n","import type { IKeyframeStore } from \"../storage/interfaces.js\";\n\nexport const passthroughKeyframeStore: IKeyframeStore = {\n putKeyframe: () => Promise.resolve(),\n findNearestKeyframe: () => Promise.resolve(undefined),\n listKeyframes: () => Promise.resolve([]),\n deleteKeyframes: () => Promise.resolve(0),\n};\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { hashDocumentStateForScope } from \"@powerhousedao/shared/document-model\";\nimport { KyselyWriteCache } from \"../cache/kysely-write-cache.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { firstOutOfOrderPair } from \"../decision/stream-order.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { throwIfAborted } from \"../shared/utils.js\";\nimport type {\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n} from \"../storage/interfaces.js\";\nimport { passthroughKeyframeStore } from \"./passthrough-keyframe-store.js\";\nimport type {\n IDocumentIntegrityService,\n KeyframeValidationIssue,\n RebuildResult,\n SnapshotValidationIssue,\n StreamOrderIssue,\n ValidationResult,\n} from \"./types.js\";\n\nexport class DocumentIntegrityService implements IDocumentIntegrityService {\n private readonly keyframeStore: IKeyframeStore;\n private readonly operationStore: IOperationStore;\n private readonly writeCache: IWriteCache;\n private readonly documentView: IDocumentView;\n private readonly documentModelRegistry: IDocumentModelRegistry;\n\n constructor(\n keyframeStore: IKeyframeStore,\n operationStore: IOperationStore,\n writeCache: IWriteCache,\n documentView: IDocumentView,\n documentModelRegistry: IDocumentModelRegistry,\n ) {\n this.keyframeStore = keyframeStore;\n this.operationStore = operationStore;\n this.writeCache = writeCache;\n this.documentView = documentView;\n this.documentModelRegistry = documentModelRegistry;\n }\n\n async validateDocument(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<ValidationResult> {\n const keyframeIssues: KeyframeValidationIssue[] = [];\n const snapshotIssues: SnapshotValidationIssue[] = [];\n const streamOrderIssues = await this.findStreamOrderIssues(\n documentId,\n branch,\n signal,\n );\n\n const replayCache = new KyselyWriteCache(\n passthroughKeyframeStore,\n this.operationStore,\n this.documentModelRegistry,\n {\n maxDocuments: 1,\n ringBufferSize: 1,\n keyframeInterval: Number.MAX_SAFE_INTEGER,\n },\n );\n\n const keyframes = await this.keyframeStore.listKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n for (const keyframe of keyframes) {\n throwIfAborted(signal);\n\n replayCache.invalidate(documentId, keyframe.scope, branch);\n const replayedDoc = await replayCache.getState(\n documentId,\n keyframe.scope,\n branch,\n keyframe.revision,\n signal,\n );\n\n const kfHash = hashDocumentStateForScope(\n keyframe.document,\n keyframe.scope,\n );\n const replayHash = hashDocumentStateForScope(replayedDoc, keyframe.scope);\n\n if (kfHash !== replayHash) {\n keyframeIssues.push({\n scope: keyframe.scope,\n branch,\n revision: keyframe.revision,\n keyframeHash: kfHash,\n replayedHash: replayHash,\n });\n }\n }\n\n let currentDoc: PHDocument;\n try {\n currentDoc = await this.documentView.get(documentId);\n } catch {\n return {\n documentId,\n isConsistent:\n keyframeIssues.length === 0 && streamOrderIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n streamOrderIssues,\n };\n }\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n const allScopes = Object.keys(revisions.revision);\n\n for (const scope of allScopes) {\n if (scope === \"document\") continue;\n\n replayCache.invalidate(documentId, scope, branch);\n\n let replayedDoc: PHDocument;\n try {\n replayedDoc = await replayCache.getState(\n documentId,\n scope,\n branch,\n undefined,\n signal,\n );\n } catch {\n throwIfAborted(signal);\n continue;\n }\n\n const snapshotHash = hashDocumentStateForScope(currentDoc, scope);\n const replayHash = hashDocumentStateForScope(replayedDoc, scope);\n if (snapshotHash !== replayHash) {\n snapshotIssues.push({\n scope,\n branch,\n snapshotHash,\n replayedHash: replayHash,\n });\n }\n }\n\n return {\n documentId,\n isConsistent:\n keyframeIssues.length === 0 &&\n snapshotIssues.length === 0 &&\n streamOrderIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n streamOrderIssues,\n };\n }\n\n async rebuildKeyframes(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const deleted = await this.keyframeStore.deleteKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n return {\n documentId,\n keyframesDeleted: deleted,\n scopesInvalidated: 0,\n };\n }\n\n async rebuildSnapshots(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const scopes = await this.discoverScopes(documentId, branch, signal);\n\n for (const scope of scopes) {\n throwIfAborted(signal);\n this.writeCache.invalidate(documentId, scope, branch);\n }\n\n return {\n documentId,\n keyframesDeleted: 0,\n scopesInvalidated: scopes.length,\n };\n }\n\n private async findStreamOrderIssues(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<StreamOrderIssue[]> {\n const scopes = await this.discoverScopes(documentId, branch, signal);\n const issues: StreamOrderIssue[] = [];\n\n for (const scope of scopes) {\n throwIfAborted(signal);\n\n const stored = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n -1,\n undefined,\n undefined,\n signal,\n );\n\n const pair = firstOutOfOrderPair(stored.results, {\n requireStrict: scope === \"auth\",\n });\n if (pair !== undefined) {\n issues.push({ scope, branch, ...pair });\n }\n }\n\n return issues;\n }\n\n private async discoverScopes(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n return Object.keys(revisions.revision);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["toErrorInfo","uuidv4","addFileAction","addFolderAction","deleteNodeAction","updateNodeAction","moveNodeAction","copyNodeAction","isPurgeMarker","uuidv4","isPurgeMarker","isPurgeMarker","isPurgeMarker","uuidv4","isPurgeMarker","isPurgeMarker","isPurgeMarker","firstOrdinalOf","keyOf","keyOf","isPurgeMarker","uuidv4","toErrorInfo","isPurgeMarker"],"sources":["../src/actions/index.ts","../src/core/utils.ts","../src/shared/types.ts","../src/client/drive-client.ts","../src/shared/awaiter.ts","../src/client/cursor.ts","../src/client/types.ts","../src/decision/read-gate.ts","../src/client/event-reads.ts","../src/client/util.ts","../src/client/reactor-client.ts","../src/queue/types.ts","../src/executor/job-result-handler.ts","../src/executor/deferred-jobs.ts","../src/executor/worker-pool-router.ts","../src/executor/worker-pool-job-executor-manager.ts","../src/admin/document-purge-service.ts","../src/storage/kysely/stored-protocol-versions.ts","../src/core/stored-protocol-check.ts","../src/executor/simple-job-executor-manager.ts","../src/job-tracker/in-memory-job-tracker.ts","../src/read-models/types.ts","../src/processors/utils.ts","../src/processors/processor-queue.ts","../src/processors/processor-manager.ts","../src/queue/job-execution-handle.ts","../src/queue/queue.ts","../src/projection/read-model-kinds.ts","../src/core/model-sources.ts","../src/registry/document-model-resolver.ts","../src/subs/default-error-handler.ts","../src/subs/react-subscription-manager.ts","../src/subs/subscription-notification-read-model.ts","../src/core/group-reevaluation-trigger.ts","../src/sync/types.ts","../src/sync/mailbox.ts","../src/sync/buffered-mailbox.ts","../src/sync/errors.ts","../src/sync/sync-operation.ts","../src/sync/utils.ts","../src/sync/channels/interval-poll-timer.ts","../src/sync/purge-refusals.ts","../src/sync/channels/utils.ts","../src/sync/channels/gql-req-channel.ts","../src/sync/channels/gql-request-channel-factory.ts","../src/sync/channels/gql-res-channel.ts","../src/sync/channels/gql-response-channel-factory.ts","../src/storage/kysely/delivery-lookup.ts","../src/storage/kysely/sync-cursor-storage.ts","../src/storage/kysely/sync-dead-letter-storage.ts","../src/storage/kysely/sync-hold-storage.ts","../src/storage/kysely/sync-purge-refusal-storage.ts","../src/storage/kysely/sync-received-marker-storage.ts","../src/storage/kysely/sync-remote-storage.ts","../src/sync/batch-aggregator.ts","../src/sync/delivery-tracking.ts","../src/sync/memory-hold-storage.ts","../src/sync/memory-purge-refusal-storage.ts","../src/sync/memory-received-marker-storage.ts","../src/sync/peer-agreement.ts","../src/sync/sync-awaiter.ts","../src/sync/sync-status-tracker.ts","../src/sync/sync-manager.ts","../src/sync/sync-builder.ts","../src/core/create-default-database.ts","../src/shared/factories.ts","../src/core/types.ts","../src/core/reactor.ts","../src/core/reactor-builder.ts","../src/projection/hybrid-projection-coordinator.ts","../src/projection/create-hybrid-projection-coordinator.ts","../src/core/reactor-client-builder.ts","../src/shared/drive-url.ts","../src/signer/types.ts","../src/executor/worker/index.ts","../src/decision/sync-scope-gate.ts","../src/decision/stream-order.ts","../src/read-models/interfaces.ts","../src/processors/host-module.ts","../src/admin/passthrough-keyframe-store.ts","../src/admin/document-integrity-service.ts"],"sourcesContent":["import type {\n Action,\n AddRelationshipActionInput,\n CreateDocumentActionInput,\n DeleteDocumentActionInput,\n RemoveRelationshipActionInput,\n UpdateRelationshipActionInput,\n UpgradeDocumentActionInput,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actions as documentActions,\n generateId,\n} from \"@powerhousedao/shared/document-model\";\n\nexport { documentActions };\n\n/**\n * Creates a CREATE_DOCUMENT action for document creation.\n */\nexport function createDocumentAction(input: CreateDocumentActionInput): Action {\n return {\n id: generateId(),\n type: \"CREATE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an UPGRADE_DOCUMENT action to set initial document state.\n */\nexport function upgradeDocumentAction(\n input: UpgradeDocumentActionInput,\n): Action {\n return {\n id: generateId(),\n type: \"UPGRADE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a DELETE_DOCUMENT action for document deletion.\n */\nexport function deleteDocumentAction(documentId: string): Action {\n const input: DeleteDocumentActionInput = {\n documentId,\n };\n\n return {\n id: generateId(),\n type: \"DELETE_DOCUMENT\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an ADD_RELATIONSHIP action that records a directed edge from\n * `sourceId` to `targetId` with an arbitrary `relationshipType` and optional\n * `metadata`. The edge is opaque to the reactor — consumers (e.g. reactor-drive)\n * define their own type strings such as `\"drive/child\"` and attach\n * domain-specific metadata.\n */\nexport function addRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n): Action {\n const input: AddRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n ...(metadata !== undefined ? { metadata } : {}),\n };\n\n return {\n id: generateId(),\n type: \"ADD_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates an UPDATE_RELATIONSHIP action to replace a relationship's metadata\n * without losing its createdAt ordering.\n */\nexport function updateRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n): Action {\n const input: UpdateRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n metadata,\n };\n\n return {\n id: generateId(),\n type: \"UPDATE_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n\n/**\n * Creates a REMOVE_RELATIONSHIP action to remove a parent-child relationship.\n */\nexport function removeRelationshipAction(\n sourceId: string,\n targetId: string,\n relationshipType: string = \"child\",\n): Action {\n const input: RemoveRelationshipActionInput = {\n sourceId,\n targetId,\n relationshipType,\n };\n\n return {\n id: generateId(),\n type: \"REMOVE_RELATIONSHIP\",\n scope: \"document\",\n timestampUtcMs: new Date().toISOString(),\n input,\n };\n}\n","import type {\n Action,\n ActionSigningTarget,\n ISigner,\n Operation,\n PHDocument,\n Signature,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actionSignerIdentity,\n actionSigningTarget,\n} from \"@powerhousedao/shared/document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { ErrorInfo, JobMeta, PagedResults } from \"../shared/types.js\";\n\n/**\n * Represents a minimal job plan for validation purposes\n */\nexport type JobPlanForValidation = {\n key: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Represents a minimal load job plan for validation purposes\n */\nexport type LoadJobPlanForValidation = {\n key: string;\n operations: Operation[];\n dependsOn: string[];\n};\n\n/**\n * Represents a job plan with scope information for action validation\n */\nexport type JobPlanWithScope = {\n key: string;\n scope: string;\n actions: Action[];\n};\n\n/**\n * Represents a load job plan with scope information for operation validation\n */\nexport type LoadJobPlanWithScope = {\n key: string;\n scope: string;\n operations: Operation[];\n};\n\n/**\n * Represents a job plan with dependencies for topological sorting\n */\nexport type JobPlanForSorting = {\n key: string;\n dependsOn: string[];\n};\n\n/**\n * Validates structural properties shared by all batch requests:\n * duplicate keys, missing dependencies, and dependency cycles.\n */\nexport function validateBatchStructure(jobs: JobPlanForSorting[]): void {\n const keys = new Set<string>();\n for (const job of jobs) {\n if (keys.has(job.key)) {\n throw new Error(`Duplicate plan key: ${job.key}`);\n }\n keys.add(job.key);\n }\n for (const job of jobs) {\n for (const depKey of job.dependsOn) {\n if (!keys.has(depKey)) {\n throw new Error(\n `Job '${job.key}' depends on non-existent key: ${depKey}`,\n );\n }\n }\n }\n const visited = new Set<string>();\n const recStack = new Set<string>();\n const detectCycle = (key: string): boolean => {\n visited.add(key);\n recStack.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n if (!visited.has(depKey)) {\n if (detectCycle(depKey)) {\n return true;\n }\n } else if (recStack.has(depKey)) {\n return true;\n }\n }\n }\n recStack.delete(key);\n return false;\n };\n for (const job of jobs) {\n if (!visited.has(job.key)) {\n if (detectCycle(job.key)) {\n throw new Error(`Dependency cycle detected involving key: ${job.key}`);\n }\n }\n }\n}\n\n/**\n * Validates a batch mutation request for common errors\n */\nexport function validateBatchRequest(jobs: JobPlanForValidation[]): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.actions.length === 0) {\n throw new Error(`Job '${job.key}' has empty actions array`);\n }\n }\n}\n\n/**\n * Validates a batch load request for common errors\n */\nexport function validateBatchLoadRequest(\n jobs: LoadJobPlanForValidation[],\n): void {\n validateBatchStructure(jobs);\n for (const job of jobs) {\n if (job.operations.length === 0) {\n throw new Error(`Job '${job.key}' has empty operations array`);\n }\n }\n}\n\n/**\n * Validates that all actions in a job match the declared scope\n */\nexport function validateActionScopes(job: JobPlanWithScope): void {\n for (const action of job.actions) {\n const actionScope = action.scope || \"global\";\n if (actionScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but action has scope '${actionScope}'`,\n );\n }\n }\n}\n\n/**\n * Validates that all operations in a job match the declared scope\n */\nexport function validateOperationScopes(job: LoadJobPlanWithScope): void {\n for (const operation of job.operations) {\n const operationScope = operation.action.scope || \"global\";\n if (operationScope !== job.scope) {\n throw new Error(\n `Job '${job.key}' declares scope '${job.scope}' but operation has scope '${operationScope}'`,\n );\n }\n }\n}\n\n/**\n * Performs topological sort on jobs based on dependencies\n */\nexport function topologicalSort(jobs: JobPlanForSorting[]): string[] {\n const result: string[] = [];\n const visited = new Set<string>();\n const visit = (key: string): void => {\n if (visited.has(key)) {\n return;\n }\n visited.add(key);\n const job = jobs.find((j) => j.key === key);\n if (job) {\n for (const depKey of job.dependsOn) {\n visit(depKey);\n }\n }\n result.push(key);\n };\n for (const job of jobs) {\n visit(job.key);\n }\n return result;\n}\n\n/**\n * Converts an Error or string to ErrorInfo\n */\nexport function toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n}\n\n/**\n * Filters paged results by document type\n */\nexport function filterByType(\n results: PagedResults<PHDocument>,\n type: string,\n): PagedResults<PHDocument> {\n // Filter documents by their document type from the header\n const filteredDocuments = results.results.filter(\n (document) => document.header.documentType === type,\n );\n\n // Create new paged results with filtered documents\n // Note: This maintains the same paging structure but with filtered results\n return {\n results: filteredDocuments,\n options: results.options,\n nextCursor: results.nextCursor,\n next: results.next\n ? async () => {\n // If there's a next function, apply the same filter to the next page\n const nextResults = await results.next!();\n return filterByType(nextResults, type);\n }\n : undefined,\n };\n}\n\n/**\n * Validates that all operations share the same scope.\n * Throws an error if any operation has a different scope.\n */\nexport function getSharedOperationScope(operations: Operation[]): string {\n if (operations.length === 0) {\n throw new Error(\"No operations provided\");\n }\n\n const baseScope = operations[0].action.scope;\n for (const [index, operation] of operations.entries()) {\n const scope = operation.action.scope;\n if (scope !== baseScope) {\n throw new Error(\n `All operations in load must share the same scope. Expected '${baseScope}', received '${scope}' at position ${index}`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Validates that all actions share the same scope.\n * Throws an error if any action has a different scope.\n */\nexport function getSharedActionScope(actions: Action[]): string {\n if (actions.length === 0) {\n throw new Error(\"No actions provided\");\n }\n\n const baseScope = actions[0].scope;\n for (const action of actions) {\n if (action.scope !== baseScope) {\n throw new Error(\n `All actions must share the same scope. Expected '${baseScope}', received '${action.scope}'`,\n );\n }\n }\n\n return baseScope;\n}\n\n/**\n * Signs an action for `target`, the log it is written to. An action already\n * signed under a key is returned unchanged; one whose signer or last tuple has\n * no key is unsigned, and is signed like any other.\n */\nexport const signAction = async (\n action: Action,\n signer: ISigner,\n target: ActionSigningTarget,\n signal?: AbortSignal,\n): Promise<Action> => {\n const existing = action.context?.signer;\n if (existing?.app?.key && existing.signatures.at(-1)?.[1]) {\n return action;\n }\n\n const signature: Signature = await signer.signAction(action, target, signal);\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: { ...actionSignerIdentity(signer), signatures: [signature] },\n },\n };\n};\n\n/**\n * Signs the actions of a job on `job.documentId`, each for the log it is\n * written to: a relationship action lands in its source document.\n */\nexport const signActions = async (\n actions: Action[],\n signer: ISigner,\n job: ActionSigningTarget,\n signal?: AbortSignal,\n): Promise<Action[]> => {\n return Promise.all(\n actions.map((action) =>\n signAction(\n action,\n signer,\n actionSigningTarget(action, job.documentId, job.branch),\n signal,\n ),\n ),\n );\n};\n\nexport function buildSingleJobMeta(\n jobId: string,\n callerMeta?: Record<string, unknown>,\n): JobMeta {\n return { ...callerMeta, batchId: uuidv4(), batchJobIds: [jobId] };\n}\n","import type {\n AuthSubject,\n OperationOutcome,\n} from \"@powerhousedao/shared/document-model\";\n\n/**\n * The document ID used for system operations (CREATE_DOCUMENT, DELETE_DOCUMENT, etc.)\n * System operations use this special ID along with the \"system\" scope.\n */\nexport const SYSTEM_DOCUMENT_ID = \"00000000-0000-0000-0000-000000000000\";\n\n/**\n * Information about an error including its name, message and stack trace.\n *\n * The name is what survives the crossing out of the executor: a consumer that\n * has to tell a terminal failure from a retryable one, or classify a dead\n * letter, only ever sees this record rather than the thrown error.\n */\nexport type ErrorInfo = {\n name: string;\n message: string;\n stack: string;\n /** The document the error names, when it carries one. */\n documentId?: string;\n};\n\n/**\n * Describes the status of a shutdown operation.\n */\nexport type ShutdownStatus = {\n /**\n * True if and only if the system has been shutdown.\n *\n * This value is meant to be polled to determine if the system has been shutdown.\n *\n * In the case of a browser process, the `kill` method should be able to synchronously set this to true.\n *\n * In the case of a server process, a graceful shutdown period should be allowed for the system to finish its work.\n */\n get isShutdown(): boolean;\n\n /**\n * A promise that resolves when the shutdown process is complete.\n *\n * For server environments, await this promise to ensure all active jobs finish\n * before exiting the process.\n */\n completed: Promise<void>;\n};\n\n/**\n * Enum that determines deletion propagation.\n */\nexport enum PropagationMode {\n None = \"none\",\n Cascade = \"cascade\",\n}\n\n/**\n * Enum that describes the type of relationship change.\n */\nexport enum RelationshipChangeType {\n Added = \"added\",\n Removed = \"removed\",\n}\n\n/**\n * Batch-specific metadata always present on every job.\n * Single jobs get a unique batchId and batchJobIds of [jobId].\n */\nexport type BatchMeta = {\n batchId: string;\n batchJobIds: string[];\n};\n\n/**\n * Metadata that flows through the job lifecycle.\n * Always includes batch fields; callers may add additional properties.\n */\nexport type JobMeta = BatchMeta & Record<string, unknown>;\n\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * What became of one action the caller submitted, at the position the\n * operation carrying it was written to.\n */\nexport type SubmittedActionResult = {\n actionId: string;\n scope: string;\n\n /** The index the operation occupies in its scope. */\n index: number;\n} & OperationOutcome;\n\n/**\n * The outcome of every action a job was given.\n *\n * A reducer error or a denial does not fail the job: the operation is still\n * written at its index, the rest of the batch still applies, and the job still\n * reaches READ_READY. This is the only place a caller can see that one of its\n * actions was rejected.\n */\nexport type JobResultSummary = {\n /** One entry per submitted action that produced an operation, in write order. */\n actions: SubmittedActionResult[];\n\n /** False when any submitted action hit a reducer error or was denied. */\n allApplied: boolean;\n};\n\n/**\n * Describes the current state of a job.\n */\nexport type JobInfo = {\n id: string;\n\n /** The document this job operates on; empty string when the job is unknown. */\n documentId: string;\n status: JobStatus;\n createdAtUtcIso: string;\n completedAtUtcIso?: string;\n error?: ErrorInfo;\n errorHistory?: ErrorInfo[];\n\n /**\n * What the job produced for its caller, from the moment its operations are\n * durable. Undefined until then, and on jobs that carry no submitted\n * actions.\n */\n result?: JobResultSummary;\n\n /**\n * A token for coordinating reads, only valid once a job reaches COMPLETED.\n */\n consistencyToken: ConsistencyToken;\n\n /**\n * Metadata that flows through the job lifecycle.\n */\n meta: JobMeta;\n\n /**\n * The full job object, populated on failure for debugging purposes.\n */\n job?: Job;\n};\n\n/**\n * Job execution statuses\n */\nexport enum JobStatus {\n /** Job is queued but not yet started */\n PENDING = \"PENDING\",\n /** Job is currently being executed */\n RUNNING = \"RUNNING\",\n /** Operations have been written to the operation store (JOB_WRITE_READY event) */\n WRITE_READY = \"WRITE_READY\",\n /** Read models have finished indexing operations (JOB_READ_READY event) */\n READ_READY = \"READ_READY\",\n /** Job failed (may be retried) */\n FAILED = \"FAILED\",\n}\n\n/**\n * Describe the view of a set of documents. That is, what pieces of the\n * documents are populated.\n */\nexport type ViewFilter = {\n branch?: string;\n scopes?: string[];\n revision?: number;\n /**\n * Read subject for the IReactorClient read gate; defaults to the client's\n * signer. Set per request when serving many principals. Ignored by IReactor.\n */\n subject?: AuthSubject;\n};\n\n/**\n * Describes filter options for searching documents.\n *\n * Each parameter is treated as an AND condition.\n */\nexport type SearchFilter = {\n type?: string;\n parentId?: string;\n ids?: string[];\n slugs?: string[];\n};\n\n/**\n * Describes the options for paging.\n */\nexport type PagingOptions = {\n cursor: string;\n limit: number;\n};\n\n/**\n * The paged result.\n */\nexport type PagedResults<T> = {\n results: T[];\n options: PagingOptions;\n\n next?: () => Promise<PagedResults<T>>;\n nextCursor?: string;\n};\n\n/**\n * A string key in the format `documentId:scope:branch` used to identify a consistency checkpoint.\n */\nexport type ConsistencyKey = `${string}:${string}:${string}`;\n\n/**\n * Describes a specific point in a document's operation history.\n */\nexport type ConsistencyCoordinate = {\n documentId: string;\n scope: string;\n branch: string;\n operationIndex: number;\n};\n\n/**\n * A token that captures the state of write operations at a point in time.\n * Can be used to ensure read-after-write consistency.\n */\nexport type ConsistencyToken = {\n version: 1;\n createdAtUtcIso: string;\n coordinates: ConsistencyCoordinate[];\n};\n","import {\n addFolder as addFolderAction,\n copyNode as copyNodeAction,\n deleteNode as deleteNodeAction,\n driveCreateDocument,\n generateNodesCopy,\n getDescendants,\n handleTargetNameCollisions,\n isFileNode,\n isFolderNode,\n moveNode as moveNodeAction,\n updateNode as updateNodeAction,\n type DocumentDriveDocument,\n type DriveInput,\n type FolderNode,\n type Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport { addFile as addFileAction } from \"@powerhousedao/shared/document-drive\";\nimport {\n actions,\n createCopyHeader,\n generateId,\n normalizeDocumentModelVersion,\n requestedSignaturePolicy,\n withSignaturePolicy,\n type Action,\n type CreateDocumentActionInput,\n type ISigner,\n type PHDocument,\n type PHDocumentHeader,\n type ProtocolVersions,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n deleteDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type { ExecutionJobPlan, IReactor } from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport type { JobInfo, PagedResults, PagingOptions } from \"../shared/types.js\";\nimport { JobStatus } from \"../shared/types.js\";\nimport { parsePagingOptions } from \"../shared/utils.js\";\nimport type { IDriveClient, IReactorClient } from \"./types.js\";\n\n/**\n * Implementation of {@link IDriveClient}.\n *\n * Holds a back-reference to its parent {@link IReactorClient} for read and\n * single-document write primitives, plus direct access to {@link IReactor}\n * for batch execution. The back-reference is captured but never invoked\n * during construction, so the partial-`this` hazard does not apply.\n */\n/** A copy keeps its source's versions; selection fills only the keys it lacks. */\nfunction withSelectedVersions(\n header: PHDocumentHeader,\n selected: ProtocolVersions,\n): Pick<PHDocumentHeader, \"documentType\" | \"protocolVersions\"> {\n return {\n documentType: header.documentType,\n protocolVersions: header.protocolVersions\n ? { ...selected, ...header.protocolVersions }\n : undefined,\n };\n}\n\nexport class DriveClient implements IDriveClient {\n constructor(\n private readonly client: IReactorClient,\n private readonly logger: ILogger,\n private readonly reactor: IReactor,\n private readonly signer: ISigner,\n private readonly resolveReference: (\n identifier: string,\n signal?: AbortSignal,\n ) => Promise<string>,\n ) {}\n\n async create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\"drives.create(@input)\", input);\n const driveDoc = withSignaturePolicy(\n driveCreateDocument({\n global: {\n name: input.global.name || \"\",\n icon: input.global.icon ?? null,\n nodes: [],\n },\n }),\n requestedSignaturePolicy(\n input,\n await this.client.getCreateSignaturePolicy(),\n ),\n {\n protocolVersions: {\n ...(await this.client.getCreateProtocolVersions(undefined, signal)),\n ...input.protocolVersions,\n },\n },\n );\n if (input.preferredEditor) {\n driveDoc.header.meta = {\n ...driveDoc.header.meta,\n preferredEditor: input.preferredEditor,\n };\n }\n return this.client.create<DocumentDriveDocument>(\n driveDoc,\n undefined,\n signal,\n );\n }\n\n async addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"drives.addFile(@driveIdentifier, @document, @parentFolder)\",\n driveIdentifier,\n document.header.id,\n parentFolder,\n );\n\n const documentId = document.header.id;\n const driveId = await this.resolveReference(driveIdentifier, signal);\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const documentActions: Action[] = await signActions(\n [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n // imported files may predate the document scope entirely\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document\n ?.version,\n ),\n initialState: document.state,\n }),\n addRelationshipAction(driveId, documentId, \"child\"),\n ],\n this.signer,\n { documentId, branch: \"main\" },\n signal,\n );\n\n const driveActions: Action[] = await signActions(\n [\n addFileAction({\n id: documentId,\n name: document.header.name || documentId,\n documentType: document.header.documentType,\n parentFolder,\n }),\n ],\n this.signer,\n { documentId: driveId, branch: \"main\" },\n signal,\n );\n\n // Two batches, not one with a dependsOn edge. A batch's edges are ordering\n // only: a failed job still releases its dependents (see IQueue.failJob), so\n // batching these would add the file node on top of a create that never\n // landed, pointing the drive at whoever already owns that id. Waiting for\n // the document to exist costs a round trip and makes that unrepresentable.\n await this.runJobs(\n [\n {\n key: \"document\",\n documentId,\n scope: getSharedActionScope(documentActions),\n branch: \"main\",\n actions: documentActions,\n dependsOn: [],\n },\n ],\n signal,\n );\n\n await this.runJobs(\n [\n {\n key: \"drive\",\n documentId: driveId,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [],\n },\n ],\n signal,\n );\n\n // Through the client, not the reactor: this one holds the read gate, and a\n // document handed back from a write is a read like any other.\n return this.client.get<TDocument>(documentId, undefined, signal);\n }\n\n async addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode> {\n this.logger.verbose(\n \"drives.addFolder(@driveIdentifier, @name, @parentFolder)\",\n driveIdentifier,\n name,\n parentFolder,\n );\n const folderId = generateId();\n const updated = await this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [addFolderAction({ id: folderId, name, parentFolder })],\n signal,\n );\n // A denied action still completes its job, so a caller with no grant gets\n // the document back without the scope it tried to write.\n const node = (\n updated.state as Partial<typeof updated.state>\n ).global?.nodes.find((n) => n.id === folderId);\n if (!node || !isFolderNode(node)) {\n throw new Error(\"Folder creation failed\");\n }\n return node;\n }\n\n async removeNode(\n driveIdentifier: string,\n nodeIdentifier: string,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"drives.removeNode(@driveIdentifier, @nodeIdentifier)\",\n driveIdentifier,\n nodeIdentifier,\n );\n const driveId = await this.resolveReference(driveIdentifier, signal);\n const nodeId = await this.resolveReference(nodeIdentifier, signal);\n const drive = await this.client.get<DocumentDriveDocument>(\n driveId,\n undefined,\n signal,\n );\n const node = drive.state.global.nodes.find((n) => n.id === nodeId);\n\n if (!node) {\n // No FileNode, but the document may still exist as an orphan (relationship\n // and document persisted, node never added — e.g. ADD_FILE rejected the\n // name). Clean it up if so; otherwise it is genuinely unknown.\n const exists = await this.documentExists(nodeId, signal);\n if (!exists) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n await this.removeFileNode(driveId, nodeId, signal);\n return;\n }\n\n if (isFolderNode(node)) {\n const fileDescendants = getDescendants(\n node,\n drive.state.global.nodes,\n ).filter(isFileNode);\n for (const file of fileDescendants) {\n await this.removeFileNode(driveId, file.id, signal);\n }\n await this.client.execute(\n driveId,\n \"main\",\n [deleteNodeAction({ id: nodeId })],\n signal,\n );\n return;\n }\n\n await this.removeFileNode(driveId, nodeId, signal);\n }\n\n async renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n this.logger.verbose(\n \"drives.renameNode(@driveIdentifier, @nodeId, @name)\",\n driveIdentifier,\n nodeId,\n name,\n );\n const renamed = await this.client.execute(\n nodeId,\n \"main\",\n [actions.setName({ name })],\n signal,\n );\n if (renamed.header.name !== name) {\n throw new Error(\"Document rename did not apply\");\n }\n const drive = await this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [updateNodeAction({ id: nodeId, name })],\n signal,\n );\n const node = (\n drive.state as Partial<typeof drive.state>\n ).global?.nodes.find((n) => n.id === nodeId);\n if (!node) {\n throw new Error(\"Node missing from drive after rename\");\n }\n return node;\n }\n\n async setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"drives.setPreferredEditorOnNode(@nodeId, @preferredEditor)\",\n nodeId,\n preferredEditor,\n );\n return this.client.setPreferredEditor(\n nodeId,\n preferredEditor,\n \"main\",\n signal,\n );\n }\n\n async moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\n \"drives.moveNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)\",\n driveIdentifier,\n srcNodeId,\n targetParentFolderId,\n );\n return this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n [\n moveNodeAction({\n srcFolder: srcNodeId,\n targetParentFolder: targetParentFolderId,\n }),\n ],\n signal,\n );\n }\n\n async copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument> {\n this.logger.verbose(\n \"drives.copyNode(@driveIdentifier, @srcNodeId, @targetParentFolderId)\",\n driveIdentifier,\n srcNodeId,\n targetParentFolderId,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const srcNode = drive.state.global.nodes.find((n) => n.id === srcNodeId);\n if (!srcNode) {\n throw new Error(\n `Node ${srcNodeId} not found in drive ${driveIdentifier}`,\n );\n }\n\n const copyPlan = generateNodesCopy(\n {\n srcId: srcNodeId,\n targetParentFolder: targetParentFolderId,\n targetName: srcNode.name,\n },\n () => generateId(),\n drive.state.global.nodes,\n );\n\n const resolvedNamesByTargetId = new Map<string, string>();\n for (const entry of copyPlan) {\n const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);\n if (!node) continue;\n const resolved = handleTargetNameCollisions({\n nodes: drive.state.global.nodes,\n srcName: entry.targetName || node.name,\n srcKind: isFileNode(node) ? \"file\" : \"folder\",\n targetParentFolder: entry.targetParentFolder ?? null,\n });\n resolvedNamesByTargetId.set(entry.targetId, resolved);\n }\n\n const policy = await this.client.getCreateSignaturePolicy();\n const base = await this.client.getCreateProtocolVersions(\n drive.header.id,\n signal,\n );\n for (const entry of copyPlan) {\n const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);\n if (!node || !isFileNode(node)) continue;\n const srcDoc = await this.client.get(entry.srcId, undefined, signal);\n\n // Read snapshots carry no operations, so the copy is seeded from the\n // source's current state rather than replayed. Replaying here would also\n // pick a reducer by document type alone and apply it to state that is\n // already current.\n const duplicated: PHDocument = {\n ...srcDoc,\n header: createCopyHeader(\n withSelectedVersions(srcDoc.header, base),\n entry.targetId,\n policy,\n ),\n initialState: srcDoc.state,\n operations: {},\n };\n\n const resolvedName = resolvedNamesByTargetId.get(entry.targetId);\n if (resolvedName) {\n duplicated.header.name = resolvedName;\n }\n // A v2-required copy takes a derived id, which the drive's node must name.\n entry.targetId = duplicated.header.id;\n await this.addFile(\n driveIdentifier,\n duplicated,\n entry.targetParentFolder ?? undefined,\n signal,\n );\n }\n\n return this.client.execute<DocumentDriveDocument>(\n driveIdentifier,\n \"main\",\n copyPlan.map((entry) => copyNodeAction(entry)),\n signal,\n );\n }\n\n async getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node> {\n this.logger.verbose(\n \"drives.getNode(@driveIdentifier, @nodeId)\",\n driveIdentifier,\n nodeId,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const node = drive.state.global.nodes.find((n) => n.id === nodeId);\n if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\n }\n return node;\n }\n\n async listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>> {\n this.logger.verbose(\n \"drives.listNodes(@driveIdentifier, @parentFolder, @paging)\",\n driveIdentifier,\n parentFolder,\n paging,\n );\n const drive = await this.client.get<DocumentDriveDocument>(\n driveIdentifier,\n undefined,\n signal,\n );\n const allNodes = drive.state.global.nodes;\n const filtered =\n parentFolder === undefined\n ? [...allNodes]\n : allNodes.filter((n) => (n.parentFolder ?? null) === parentFolder);\n\n const { offset: startIndex, limit } = parsePagingOptions(\n paging,\n filtered.length,\n );\n const effective: PagingOptions = paging ?? { cursor: \"\", limit };\n const endIndex = startIndex + limit;\n const slice = filtered.slice(startIndex, endIndex);\n const hasMore = endIndex < filtered.length;\n\n return {\n results: slice,\n options: effective,\n ...(hasMore ? { nextCursor: String(endIndex) } : {}),\n };\n }\n\n /**\n * Runs one batch to completion, throwing the first failure. The thrown error\n * carries the job error's name, which is what callers classify on: an id\n * collision has to be told apart from a transient failure.\n */\n private async runJobs(\n jobs: ExecutionJobPlan[],\n signal?: AbortSignal,\n ): Promise<JobInfo[]> {\n const batchResult = await this.reactor.executeBatch({ jobs }, signal);\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.client.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw this.toError(job);\n }\n }\n\n return completedJobs;\n }\n\n private toError(job: JobInfo): Error {\n const error = new Error(job.error?.message ?? \"Job failed\");\n if (job.error?.name) {\n error.name = job.error.name;\n }\n return error;\n }\n\n private async documentExists(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n try {\n await this.client.get(documentId, undefined, signal);\n return true;\n } catch {\n return false;\n }\n }\n\n private async removeFileNode(\n driveId: string,\n fileId: string,\n signal?: AbortSignal,\n ): Promise<void> {\n const driveActions: Action[] = await signActions(\n [deleteNodeAction({ id: fileId })],\n this.signer,\n { documentId: driveId, branch: \"main\" },\n signal,\n );\n // Alone, so a drive that refuses the removal leaves the file untouched.\n await this.runJobs(\n [\n {\n key: \"drive\",\n documentId: driveId,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [],\n },\n ],\n signal,\n );\n\n const deleteActions: Action[] = await signActions(\n [deleteDocumentAction(fileId)],\n this.signer,\n { documentId: fileId, branch: \"main\" },\n signal,\n );\n const relationshipActions: Action[] = await signActions(\n [removeRelationshipAction(driveId, fileId, \"child\")],\n this.signer,\n { documentId: driveId, branch: \"main\" },\n signal,\n );\n // The drive's remotes are served the delete while the file is a member.\n await this.runJobs(\n [\n {\n key: \"delete\",\n documentId: fileId,\n scope: getSharedActionScope(deleteActions),\n branch: \"main\",\n actions: deleteActions,\n dependsOn: [],\n },\n {\n key: \"relationship\",\n documentId: driveId,\n scope: getSharedActionScope(relationshipActions),\n branch: \"main\",\n actions: relationshipActions,\n dependsOn: [\"delete\"],\n },\n ],\n signal,\n );\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport { JobStatus, type JobInfo } from \"./types.js\";\n\nexport interface IJobAwaiter {\n /**\n * Waits for a job to complete: turns a job into a promise.\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Shuts down the job awaiter. This will synchronously reject all pending jobs.\n */\n shutdown(): void;\n}\n\ntype JobWaiter = {\n resolve: (value: JobInfo) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Checks if a job status is terminal (job has finished).\n * WRITE_READY is not terminal - it's an intermediate state.\n * Only READ_READY and FAILED are truly terminal.\n */\nfunction isTerminalStatus(status: JobStatus): boolean {\n return status === JobStatus.READ_READY || status === JobStatus.FAILED;\n}\n\n/**\n * Event-driven implementation of IJobAwaiter.\n * Subscribes to operation events to detect job completion without polling.\n */\nexport class JobAwaiter implements IJobAwaiter {\n private pendingJobs = new Map<string, JobWaiter[]>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(\n private eventBus: IEventBus,\n private getJobStatus: (\n jobId: string,\n signal?: AbortSignal,\n ) => Promise<JobInfo>,\n ) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event: JobWriteReadyEvent) => {\n await this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n async (_type, event: JobReadReadyEvent) => {\n await this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event: JobFailedEvent) => {\n await this.handleJobFailed(event);\n },\n ),\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n\n for (const [, waiters] of this.pendingJobs) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"JobAwaiter destroyed\"));\n }\n }\n this.pendingJobs.clear();\n }\n\n async waitForJob(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const currentStatus = await this.getJobStatus(jobId, signal);\n if (isTerminalStatus(currentStatus.status)) {\n return currentStatus;\n }\n\n const promise = new Promise<JobInfo>((resolve, reject) => {\n const waiter: JobWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingJobs.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingJobs.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingJobs.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingJobs.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n\n return promise;\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleReadReady(event: JobReadReadyEvent): Promise<void> {\n const jobId = event.jobId;\n await this.checkAndResolveWaiters(jobId);\n }\n\n private async handleJobFailed(event: JobFailedEvent): Promise<void> {\n await this.checkAndResolveWaiters(event.jobId);\n }\n\n private async checkAndResolveWaiters(jobId: string): Promise<void> {\n const waiters = this.pendingJobs.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n try {\n const activeWaiters = waiters.filter((w) => !w.signal?.aborted);\n\n if (activeWaiters.length === 0) {\n this.pendingJobs.delete(jobId);\n return;\n }\n\n const jobInfo = await this.getJobStatus(jobId, activeWaiters[0].signal);\n\n if (isTerminalStatus(jobInfo.status)) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of activeWaiters) {\n waiter.resolve(jobInfo);\n }\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n }\n } catch (error) {\n this.pendingJobs.delete(jobId);\n\n for (const waiter of waiters) {\n waiter.reject(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }\n}\n","const COMPOSITE_PREFIX = \"c:\";\n\n/**\n * Returns true if the cursor is a composite multi-scope cursor.\n */\nexport function isCompositeCursor(cursor: string): boolean {\n return cursor.startsWith(COMPOSITE_PREFIX);\n}\n\n/**\n * Encodes per-scope cursors into a single composite cursor string.\n * Only scopes that still have more results should be included.\n */\nexport function encodeCompositeCursor(\n scopeCursors: Record<string, string>,\n): string {\n return COMPOSITE_PREFIX + JSON.stringify(scopeCursors);\n}\n\n/**\n * Decodes a composite cursor string into a map of scope to cursor.\n * Throws if the cursor is not a valid composite cursor.\n */\nexport function decodeCompositeCursor(cursor: string): Record<string, string> {\n if (!cursor.startsWith(COMPOSITE_PREFIX)) {\n throw new Error(\"Invalid composite cursor format\");\n }\n\n const json = cursor.slice(COMPOSITE_PREFIX.length);\n\n try {\n const parsed: unknown = JSON.parse(json);\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n Array.isArray(parsed)\n ) {\n throw new Error(\"Invalid composite cursor format\");\n }\n return parsed as Record<string, string>;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new Error(\"Invalid composite cursor format\", { cause: error });\n }\n throw error;\n }\n}\n","import type {\n DocumentDriveDocument,\n DriveInput,\n FolderNode,\n Node,\n} from \"@powerhousedao/shared/document-drive\";\nimport type {\n Action,\n AuthSubject,\n DocumentModelModule,\n Operation,\n PeerCapability,\n PeerCapabilityFlags,\n PHDocument,\n ProtocolVersions,\n SignaturePolicy,\n} from \"@powerhousedao/shared/document-model\";\nimport type { IPeerAgreement } from \"../sync/peer-agreement.js\";\n\n/** What creation selects protocol versions from. */\nexport type ProtocolSelection = {\n capabilities: readonly PeerCapability[];\n flags: PeerCapabilityFlags;\n /** Absent without sync: no members, so the local preference. */\n agreement?: () => IPeerAgreement;\n collectionsOf: (documentIds: string[]) => Promise<Record<string, string[]>>;\n};\n\nimport type {\n BatchExecutionRequest,\n BatchExecutionResult,\n BatchLoadRequest,\n BatchLoadResult,\n} from \"../core/types.js\";\nimport type { Evaluation } from \"../decision/types.js\";\nimport type {\n JobInfo,\n PagedResults,\n PagingOptions,\n PropagationMode,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n DocumentRelationship,\n OperationFilter,\n} from \"../storage/interfaces.js\";\n\n/**\n * Describes the types of document changes that can occur.\n */\nexport enum DocumentChangeType {\n Created = \"created\",\n Deleted = \"deleted\",\n Updated = \"updated\",\n ParentAdded = \"parent_added\",\n ParentRemoved = \"parent_removed\",\n ChildAdded = \"child_added\",\n ChildRemoved = \"child_removed\",\n}\n\n/**\n * Represents a change event for documents.\n */\nexport type DocumentChangeEvent = {\n type: DocumentChangeType;\n documents: PHDocument[];\n context?: {\n parentId?: string;\n childId?: string;\n /** On a Deleted event: a purge marker applied the deletion. */\n purged?: true;\n };\n};\n\n/**\n * Options for creating an empty document.\n */\nexport type CreateDocumentOptions = {\n /** Optional \"id\" or \"slug\" of parent document */\n parentIdentifier?: string;\n /** Optional version of the document model to use (defaults to latest) */\n documentModelVersion?: number;\n /** Merged over the model's defaults; `signature: 2` makes it v2-required. */\n protocolVersions?: { [protocol: string]: number };\n /** Overrides the client's creation default for this document. */\n signaturePolicy?: SignaturePolicy;\n};\n\n/** Retries taken when an upgrade conflicts with concurrent edits. */\nexport const DEFAULT_UPGRADE_CONFLICT_RETRIES = 3;\n\n/**\n * Options for upgrading a document.\n */\nexport type UpgradeDocumentOptions = {\n /**\n * How many times to retry with a fresh read when the executor rejects the\n * upgrade because the document changed after it was read. Defaults to\n * {@link DEFAULT_UPGRADE_CONFLICT_RETRIES}.\n */\n maxConflictRetries?: number;\n};\n\n/**\n * Drive-aware operations grouped under `client.drives`.\n *\n * These methods orchestrate the multi-action, multi-document choreography\n * required to keep a drive's `state.global.nodes` array consistent with the\n * relationship index and the underlying documents. Use the flat\n * `IReactorClient` primitives (`get`, `execute`, `find`) for everything that\n * is not drive-aware.\n */\nexport interface IDriveClient {\n /**\n * Creates a new drive document and waits for completion.\n */\n create(\n input: DriveInput,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Adds a document to a drive as a single batched operation.\n *\n * Issues CREATE_DOCUMENT, UPGRADE_DOCUMENT, ADD_RELATIONSHIP on the new\n * document and ADD_FILE on the drive in a single dependent batch.\n */\n addFile<TDocument extends PHDocument = PHDocument>(\n driveIdentifier: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Adds a folder node to a drive.\n */\n addFolder(\n driveIdentifier: string,\n name: string,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<FolderNode>;\n\n /**\n * Removes a node from a drive. Folder nodes cascade: descendant file\n * documents are deleted first, then the folder node entry itself.\n */\n removeNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Renames a node. Updates both the underlying document header and the\n * drive's node entry.\n */\n renameNode(\n driveIdentifier: string,\n nodeId: string,\n name: string,\n signal?: AbortSignal,\n ): Promise<Node>;\n\n /**\n * Updates the preferred editor recorded in the document header meta for\n * a node. Pass `null` to clear it.\n */\n setPreferredEditorOnNode(\n nodeId: string,\n preferredEditor: string | null,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Moves a node to a different parent folder within the same drive.\n * Pass `undefined` to move the node to the drive root.\n */\n moveNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Copies a node (and its subtree, if it is a folder) within a drive.\n * Each copied file gets a new id and a duplicated document.\n */\n copyNode(\n driveIdentifier: string,\n srcNodeId: string,\n targetParentFolderId: string | undefined,\n signal?: AbortSignal,\n ): Promise<DocumentDriveDocument>;\n\n /**\n * Returns a single node from the drive's `state.global.nodes` array.\n */\n getNode(\n driveIdentifier: string,\n nodeId: string,\n signal?: AbortSignal,\n ): Promise<Node>;\n\n /**\n * Returns nodes in the drive, optionally filtered by parent folder:\n * - omit `parentFolder` (or pass `undefined`) to list every node in the drive.\n * - pass `null` to list only root-level nodes.\n * - pass a folder id to list only the direct children of that folder.\n *\n * Returns a paged result so callers can stream through drives with very\n * large node counts without materialising the whole list in memory.\n */\n listNodes(\n driveIdentifier: string,\n parentFolder?: string | null,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Node>>;\n}\n\n/**\n * One operation an authorization preflight predicts a verdict for. The input is\n * what a conditional grant reads, so a candidate standing for a filled-in form\n * carries that form's input.\n */\nexport type ActionCandidate = {\n scope: string;\n type: string;\n input?: unknown;\n};\n\n/**\n * The predicted verdicts for a set of candidates, in the order they were given,\n * with the aggregates a UI branches on.\n *\n * The aggregates are redundant -- a verdict is binary, so `allDenied` is\n * `!anyAllowed` and `anyDenied` is `!allAllowed` -- and all four are returned\n * so that a caller reads the one its question is phrased in rather than\n * negating another. Over no candidates every aggregate is false: nothing is\n * allowed and nothing is denied.\n */\nexport type ActionEvaluations = {\n evaluations: Evaluation[];\n allAllowed: boolean;\n anyAllowed: boolean;\n allDenied: boolean;\n anyDenied: boolean;\n};\n\n/**\n * The ReactorClient interface that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport interface IReactorClient {\n /**\n * Drive-aware operations. See {@link IDriveClient}.\n */\n readonly drives: IDriveClient;\n\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>>;\n\n /**\n * Retrieves a specific document by identifier (either id or slug).\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Resolves an identifier (either an id or a slug) to the canonical document\n * id, using the same lookup as the read/operation data paths. Resolves\n * against the \"main\" branch. Throws if the identifier cannot be resolved or\n * is ambiguous.\n *\n * @param identifier - Required, this is the document id or slug\n * @param signal - Optional abort signal to cancel the request\n * @returns The canonical document id\n */\n resolveIdOrSlug(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<string>;\n\n /** True when the id is taken; a soft-deleted document's id is still taken. */\n isDocumentIdTaken(documentId: string, signal?: AbortSignal): Promise<boolean>;\n\n /**\n * Retrieves operations for a document.\n *\n * @param documentIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns Paginated list of operations\n */\n getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>>;\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n *\n * @param sourceIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - The relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The target documents and paging cursor\n */\n getOutgoingRelationships(\n sourceIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n *\n * @param targetIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - The relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The source documents and paging cursor\n */\n getIncomingRelationships(\n targetIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Retrieves the outgoing relationship edges of a source document.\n *\n * Unlike {@link IReactorClient.getOutgoingRelationships}, which returns the\n * documents at the far end, this returns the edges themselves, carrying the\n * metadata and timestamps recorded against each relationship.\n *\n * @param sourceIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - Optional relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The matching relationship edges and paging cursor\n */\n getOutgoingRelationshipEdges(\n sourceIdentifier: string,\n relationshipType?: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Retrieves the incoming relationship edges of a target document.\n *\n * Unlike {@link IReactorClient.getIncomingRelationships}, which returns the\n * documents at the far end, this returns the edges themselves, carrying the\n * metadata and timestamps recorded against each relationship.\n *\n * @param targetIdentifier - Required, this is either a document \"id\" field or a \"slug\"\n * @param relationshipType - Optional relationship type to filter by\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns The matching relationship edges and paging cursor\n */\n getIncomingRelationshipEdges(\n targetIdentifier: string,\n relationshipType?: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Filters documents by criteria and returns a list of them. A document the\n * view's subject may read no domain scope of is withheld, not served as its\n * header and always-readable scopes.\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /** Whether {@link find} would serve the document; false when it is absent. */\n isServed(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Predicts whether the subject would be admitted to execute each of a set of\n * candidate operations, without submitting any of them. A UI asks this to\n * disable a control rather than offer an action that fails on submit.\n *\n * The answer is a prediction, not a promise. Three caveats hold:\n *\n * - Real admission compiles an append condition over everything it read and\n * the store enforces it at write time. A preflight reads no future, so a\n * policy change landing between this answer and the submit changes the\n * verdict. The submit path stays the only authority.\n * - The verdict is evaluated at the stream heads. It is therefore correct for\n * a candidate that will be stamped at or after every timestamp the\n * evaluation read, which is the normal case for a control the user is about\n * to click. A backdated submission is out of contract: the reactor decides\n * that one by position, against the policy as it stood there.\n * - A candidate whose input decides the verdict needs that input supplied.\n * With `authConditions` on, a conditional grant reads `action.input`, so\n * omitting the input predicts the denial an empty input would earn rather\n * than the verdict the filled-in form will get.\n *\n * Document-scope candidates are decided against the policy of the document\n * their input names, not the one passed here: delete and upgrade name it in\n * `input.documentId`, and the relationship actions in `input.sourceId`. This\n * follows the executor's own gate, which decides against the document\n * guarding the write. `CREATE_DOCUMENT` follows the gate's exemption: it runs\n * before its document exists, so the executor never decides it against a\n * policy and the preflight predicts allow.\n *\n * @param documentIdentifier - Document \"id\" or \"slug\" the candidates target\n * @param branch - Branch to evaluate against\n * @param candidates - Operations to predict a verdict for, each with the scope it would execute in\n * @param subject - Optional subject to decide for, defaulting to the client's own signer. A plain subject rather than a ViewFilter: the evaluation reads no view, so a filter's branch or scopes would be silently ignored here\n * @param signal - Optional abort signal to cancel the request\n * @returns One evaluation per candidate, in the order given, with the aggregates over them\n * @throws AuthEnforcementDisabledError if the reactor's authEnforcement flag is off, in which case it holds no decision model and the legacy host-table permission system cannot answer for one\n */\n evaluateActions(\n documentIdentifier: string,\n branch: string,\n candidates: ActionCandidate[],\n subject?: AuthSubject,\n signal?: AbortSignal,\n ): Promise<ActionEvaluations>;\n\n /**\n * The signature policy this client gives the documents it creates when the\n * caller does not choose one. It never changes an existing document.\n */\n getCreateSignaturePolicy(): Promise<SignaturePolicy>;\n\n /**\n * protocolVersions for a new document under `parentIdentifier`, before the\n * signature policy: what this reactor and the direct peers of the parent's\n * collections agree on. Without a parent, the local preference.\n */\n getCreateProtocolVersions(\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<ProtocolVersions>;\n\n /**\n * Creates a document and waits for completion. The document keeps the\n * signature policy and id its header carries.\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param parentIdentifier - Optional \"id\" or \"slug\" of parent document\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Submits what {@link create} submits and returns without waiting. The\n * batch holds a `create` job and, with a parent, a `parent` job that\n * depends on it; a failed `create` still releases `parent`.\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param parentIdentifier - Optional \"id\" or \"slug\" of parent document\n * @param signal - Optional abort signal to cancel the request\n * @returns The submitted jobs, keyed `create` and `parent`\n */\n createAsync(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Creates an empty document and waits for completion\n *\n * @param documentModelType - Type of document to create\n * @param options - Optional creation options (parentIdentifier, documentModelVersion)\n * @param signal - Optional abort signal to cancel the request\n */\n createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Submits what {@link createEmpty} submits and returns without waiting;\n * see {@link createAsync}. The new document's id is `jobs.create.documentId`.\n *\n * @param documentModelType - Type of document to create\n * @param options - Optional creation options (parentIdentifier, documentModelVersion)\n * @param signal - Optional abort signal to cancel the request\n * @returns The submitted jobs, keyed `create` and `parent`\n */\n createEmptyAsync(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Retrieves the document model module matching the version a document is\n * stamped with. Use this instead of {@link getDocumentModelModule}\n * whenever a specific document is in hand: the latest-wins lookup feeds\n * not-yet-upgraded documents the wrong reducer, diverging from replay.\n *\n * @param document - The document whose stamped version selects the module\n * @returns The document model module registered for that version\n * @throws UnsupportedDocumentModelVersionError if no module is registered for the stamped version\n */\n getDocumentModelModuleForDocument(\n document: PHDocument,\n ): Promise<DocumentModelModule<any>>;\n\n /**\n * Upgrades a document to a newer document model version by dispatching an\n * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the\n * latest registered module version for the document's type. Returns the\n * document unchanged when it is already at the target version.\n *\n * The action carries a snapshot of the document's version and per-scope\n * revisions, which the executor validates before persisting. When an edit\n * lands between the read and the upgrade executing, the upgrade is\n * rejected and retried with a fresh read up to\n * {@link UpgradeDocumentOptions.maxConflictRetries} times before the\n * conflict is surfaced.\n *\n * @param documentIdentifier - Target document id or slug\n * @param toVersion - Optional target document model version; defaults to latest\n * @param options - Optional upgrade options (maxConflictRetries)\n * @param signal - Optional abort signal to cancel the request\n * @returns The upgraded document\n * @throws DowngradeNotSupportedError if toVersion is less than the document's current version\n */\n upgradeDocument<TDocument extends PHDocument = PHDocument>(\n documentIdentifier: string,\n toVersion?: number,\n options?: UpgradeDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n * This is more efficient than createEmpty + addFile as it batches all\n * actions into dependent jobs and waits for them to complete together.\n *\n * @deprecated Use {@link IDriveClient.addFile} via `client.drives.addFile`\n * instead. This method will be removed in a future release.\n * @param driveId - The drive document id or slug\n * @param document - The document to create\n * @param parentFolder - Optional folder id within the drive\n * @param signal - Optional abort signal to cancel the request\n * @returns The created document\n */\n createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Applies a list of actions to a document and waits for completion\n *\n * The document handed back is a read like any other, so it goes through the\n * read gate. `subject` names who that read is for, the same way\n * {@link ViewFilter.subject} does on `get` and `find`; a host that shares one\n * client across many callers has no meaningful signer to fall back to, and\n * without it the gate would strip the domain scopes of a policied document\n * and resolve with a husk whose `state.global` is absent — reading as a\n * rejected write when the write in fact succeeded.\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @param subject - Optional read subject for the returned document; defaults\n * to the client's signer\n * @returns The updated document\n */\n execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n subject?: AuthSubject,\n ): Promise<TDocument>;\n\n /**\n * Submits a list of actions to a document\n *\n * @param documentIdentifier - Target document id or slug\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @returns The job\n */\n executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Applies multiple mutation jobs in dependency order and waits for all to\n * complete. Actions on each job are signed by the client signer before\n * dispatch. Throws on the first failed job; the others may still execute\n * because dispatch is fire-and-await-all.\n *\n * @param request - Batch mutation request with per-job actions and dependsOn keys\n * @param signal - Optional abort signal to cancel the request\n * @returns The completed batch result (job ids keyed by plan key)\n */\n executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Renames a document and waits for completion\n *\n * @param documentIdentifier - Target document id or slug\n * @param name - The new name of the document\n * @param branch - Optional branch to rename the document, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document.\n */\n rename(\n documentIdentifier: string,\n name: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Updates the preferred editor in the document header meta and waits for completion.\n *\n * @param documentIdentifier - Target document id or slug\n * @param preferredEditor - The new preferred editor, or `null` to clear it\n * @param branch - Optional branch, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated document.\n */\n setPreferredEditor(\n documentIdentifier: string,\n preferredEditor: string | null,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Adds a relationship between two documents and waits for completion.\n *\n * Adding a relationship that already exists is a no-op, metadata included.\n * Use {@link IReactorClient.updateRelationship} to change an existing edge.\n *\n * @param sourceIdentifier - Source document id or slug\n * @param targetIdentifier - Target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param metadata - Optional metadata to attach to the relationship\n * @param branch - Optional branch to add the relationship to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source document\n */\n addRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Replaces the metadata of an existing relationship and waits for completion.\n * The relationship's createdAt is preserved.\n *\n * @param sourceIdentifier - Source document id or slug\n * @param targetIdentifier - Target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param metadata - The metadata to store; null clears it\n * @param branch - Optional branch holding the relationship, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source document\n */\n updateRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Removes a relationship between two documents and waits for completion.\n *\n * @param sourceIdentifier - Source document id or slug\n * @param targetIdentifier - Target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param branch - Optional branch to remove the relationship from, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source document\n */\n removeRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<PHDocument>;\n\n /**\n * Moves a relationship from one source document to another and waits for completion.\n *\n * @param sourceParentIdentifier - Source parent document id or slug\n * @param targetParentIdentifier - Target parent document id or slug\n * @param targetIdentifier - The target document id or slug\n * @param relationshipType - Relationship type identifier\n * @param branch - Optional branch to apply the move to, defaults to \"main\"\n * @param signal - Optional abort signal to cancel the request\n * @returns The updated source and target documents\n */\n moveRelationship(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch?: string,\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }>;\n\n /**\n * Deletes a document and waits for completion\n *\n * @param identifier - Document identifier (id or slug)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Deletes documents and waits for completion\n *\n * @param identifiers - Document identifiers (ids or slugs)\n * @param propagate - Optional mode for handling children, CASCADE deletes child documents\n * @param signal - Optional abort signal to cancel the request\n * @returns a promise, resolving on deletion confirmation\n */\n deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n * Waits for all jobs to complete.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @returns Map of job keys to completed job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @param signal - Optional abort signal to cancel the request\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Waits for a job to complete\n *\n * @param jobId - The job id or job object\n * @param signal - Optional abort signal to cancel the request\n * @returns The result of the job\n */\n waitForJob(jobId: string | JobInfo, signal?: AbortSignal): Promise<JobInfo>;\n\n /**\n * Subscribes to changes for documents matching specified filters, gated as\n * `find` is: an event naming a document the view's subject may read no domain\n * scope of is withheld.\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param callback - Function called when documents change with the change event details\n * @param view - Optional filter containing branch and scopes information\n * @returns A function that unsubscribes from the changes\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void;\n}\n","import type {\n AuthSubject,\n PHAuthState,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n decide,\n groupDocumentType,\n MAX_AUTH_GRANTS,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport {\n DocumentExistence,\n type IDocumentView,\n} from \"../storage/interfaces.js\";\nimport { buildDecisionModel } from \"./build-decision-model.js\";\nimport type { RegisteredDecisionModel } from \"./registered-model.js\";\nimport { selectDecisionModel } from \"./registered-model.js\";\nimport type { IStreamStateReader } from \"./types.js\";\n\n/**\n * Scopes every holder of a document may read, whatever the grants say. Denying\n * the policy itself would let a replica sync a document without it, read the\n * auth scope as uninitialized, and allow every operation it holds, so replicas\n * would diverge permanently. The document scope carries the metadata the same\n * argument covers. Grants gate domain-scope reads only.\n */\nexport const ALWAYS_READABLE_SCOPES: ReadonlySet<string> = new Set([\n \"auth\",\n \"document\",\n]);\n\n// Every model declares these; the read model holds a scope only once written.\nconst DECLARED_DOMAIN_SCOPES: readonly string[] = [\"global\", \"local\"];\n\n/**\n * The scopes grants gate: the declared ones, and any other the document holds.\n * A document fetched for a narrowed view holds only that view's scopes.\n */\nexport function domainScopesOf(document: PHDocument): string[] {\n const state = document.state as Record<string, unknown> | undefined;\n const held = Object.keys(state ?? {}).filter(\n (scope) => !ALWAYS_READABLE_SCOPES.has(scope),\n );\n return [...new Set([...DECLARED_DOMAIN_SCOPES, ...held])];\n}\n\nexport function allowsSomeDomainScope(\n document: PHDocument,\n readable: (scope: string) => boolean,\n): boolean {\n return domainScopesOf(document).some(readable);\n}\n\n/** A document with no state at all is not policied, so nothing withholds it. */\nexport function refusesEveryDomainScope(\n document: PHDocument,\n readable: (scope: string) => boolean,\n): boolean {\n const state = document.state as PHDocument[\"state\"] | undefined;\n return state !== undefined && !allowsSomeDomainScope(document, readable);\n}\n\n/**\n * The domain scopes the document does not hold whose read decision reads their\n * own state. A condition's `doc.<scope>` path resolves only for that scope, so\n * every other scope is decided on its name and the policy alone.\n */\nexport function unheldScopesReadOnState(document: PHDocument): string[] {\n const grants = authOf(document)?.grants ?? [];\n const onState = new Set<string>();\n for (const grant of grants) {\n collectStateScopes(grant.where, onState);\n collectStateScopes(grant.principal, onState);\n }\n if (onState.size === 0) {\n return [];\n }\n const held = (document.state as Record<string, unknown> | undefined) ?? {};\n return domainScopesOf(document).filter(\n (scope) => onState.has(scope) && !(scope in held),\n );\n}\n\nfunction collectStateScopes(node: unknown, into: Set<string>): void {\n if (Array.isArray(node)) {\n for (const child of node) {\n collectStateScopes(child, into);\n }\n return;\n }\n if (typeof node !== \"object\" || node === null) {\n return;\n }\n const { attr } = node as { attr?: unknown };\n if (typeof attr === \"string\") {\n const [root, scope] = attr.split(\".\");\n if (root === \"doc\" && scope) {\n into.add(scope);\n }\n return;\n }\n for (const child of Object.values(node)) {\n collectStateScopes(child, into);\n }\n}\n\n/**\n * How a gate treats a document nobody has written a policy onto.\n *\n * A host that closes by default owes an uninitialized document the same silence\n * it owes a denied one, because \"no policy yet\" and \"a policy that allows this\"\n * are indistinguishable to a replica that never receives the scope. The option\n * exists because that answer belongs to the host serving the read, not to the\n * document: replay must keep reading an uninitialized document in full, or a\n * replica would refuse to rebuild state it already holds.\n */\nexport type ReadGateOptions = {\n withholdUninitialized: boolean;\n};\n\n/** What a gate does with an uninitialized policy when told nothing. */\nconst OPEN_BY_DEFAULT: ReadGateOptions = { withholdUninitialized: false };\n\n/**\n * The branch a group is read on. A group's member list lives on its main\n * branch whatever branch the document naming it is on, and the reference\n * relation records no branch of its own.\n */\nconst GROUP_BRANCH = \"main\";\n\n/**\n * The scope of a group document that holds its member list. It is the only\n * scope group serving reaches, because it is the only one the audience must\n * fold in order to evaluate auth with the group.\n */\nconst GROUP_MEMBERSHIP_SCOPE = \"global\";\n\n/**\n * How many of a group's referencing documents are examined before a read of\n * that group gives up and withholds. Each one costs a model build, and the\n * relation is append-only, so an old and briefly-held reference still counts.\n * Truncating denies, which is the direction that withholds. It is the cap a\n * version-1 policy carries on its own grant list, taken from there rather than\n * restated: raising that cap raises the read fan-out here.\n */\nconst MAX_EXAMINED_REFERENCERS = MAX_AUTH_GRANTS;\n\n/**\n * How many referencers are probed at once. The reactor's Postgres pool size is\n * optional and defaults to ten, and a listing resolves one gate per result, so\n * per-gate fan-out multiplies: a handful keeps one group read from claiming the\n * whole pool while still cutting the walk several times over. On PGlite the\n * queries serialize regardless, so a larger number buys nothing in a browser.\n */\nconst REFERENCER_PROBE_CONCURRENCY = 4;\n\n/**\n * The shared cursor a set of referencer probes pulls from, and what they found.\n * One record rather than three variables, because a probe reads its siblings'\n * answers across an await.\n */\ntype ReferencerWalk = {\n next: number;\n served: boolean;\n failure: Error | undefined;\n};\n\n/**\n * The policy carried on a document, if it carries one. A document handed to the\n * gate with no state at all is not policied, so it is not gated.\n */\nfunction authOf(document: PHDocument): PHAuthState | undefined {\n const state = document.state as { auth?: PHAuthState } | undefined;\n return state?.auth;\n}\n\n/** Whether a subject may read each scope of one document. */\nexport interface IReadGate {\n /**\n * Resolves, for one document, which of its scopes the subject may read.\n *\n * The predicate is resolved up front rather than asked per scope so that the\n * filtering itself stays synchronous, and so that a model backing the answer\n * is built once per document instead of once per scope.\n */\n scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean>;\n\n /** Reads what one document's decision needs once, then decides per subject. */\n prepare?(\n document: PHDocument,\n branch: string,\n signal?: AbortSignal,\n ): Promise<SubjectScopePredicate>;\n}\n\n/** Which scopes of an already-prepared document one subject may read. */\nexport type SubjectScopePredicate = (\n subject: AuthSubject,\n) => Promise<(scope: string) => boolean>;\n\n/** A group's referencer reads, shared by every subject it is decided for. */\ntype ReferencerReads = {\n examined: () => Promise<string[]>;\n serves: (referencerId: string) => Promise<(subject: AuthSubject) => boolean>;\n};\n\n/**\n * The model reads enforce. Below `authEnforcement` there is no model to\n * enforce: the document-only model ignores the auth scope entirely, so reading\n * through it would serve every domain scope of a policied document to anyone.\n * Undefined therefore means \"evaluate the policy alone\", which is what the read\n * surface did before the model existed.\n */\nexport function readDecisionModel(\n flags: ReactorFeatureFlags,\n registry: IDocumentModelRegistry,\n): RegisteredDecisionModel | undefined {\n return flags.authEnforcement\n ? selectDecisionModel(flags, registry)\n : undefined;\n}\n\n/**\n * Evaluates the policy on its own, with no groups map and no condition context.\n * A `{ group }` or conditional grant therefore never applies: an allow that\n * does not apply withholds access, so this cannot widen a policy, but a policy\n * relying on a conditional deny is weaker here than it is written.\n */\nexport class BareReadGate implements IReadGate {\n scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n ): Promise<(scope: string) => boolean> {\n const auth = authOf(document);\n return Promise.resolve(\n (scope: string) =>\n ALWAYS_READABLE_SCOPES.has(scope) ||\n decide(auth, subject, { verb: \"read\", scope }) === \"allow\",\n );\n }\n\n prepare(document: PHDocument): Promise<SubjectScopePredicate> {\n return Promise.resolve((subject) => this.scopePredicate(document, subject));\n }\n}\n\n/**\n * Rethrows a failed read unless the document is confirmed not live. Any error\n * other than DocumentNotFoundError is checked against `exists` rather than\n * inferred from the message, and a failed check surfaces the read's own error.\n */\nexport async function assertAbsent(\n documentView: Pick<IDocumentView, \"exists\">,\n documentId: string,\n error: unknown,\n signal?: AbortSignal,\n): Promise<void> {\n if (DocumentNotFoundError.isError(error)) {\n return;\n }\n\n let exists: boolean[];\n try {\n exists = await documentView.exists(\n [documentId],\n DocumentExistence.LiveOnly,\n undefined,\n signal,\n );\n } catch {\n throw error;\n }\n\n if (exists[0]) {\n throw error;\n }\n}\n\n/**\n * Answers a stream read from the document already fetched, and anything else\n * through the read side.\n *\n * The seed is why routing reads through a decision model costs no extra I/O for\n * the document being read: its `document` and `auth` scopes are the two static\n * projections, and the caller has both in hand. Only a group stream the grant\n * list names is fetched.\n */\nexport class SeededStateReader implements IStreamStateReader {\n constructor(\n private readonly documentView: IDocumentView,\n private readonly seed: PHDocument,\n private readonly branch: string,\n ) {}\n\n /**\n * A stream this replica does not hold has to reach buildDecisionModel as the\n * absence it recognises, or the whole read fails instead of leaving the group\n * out of the model, where its principal does not match and the policy fails\n * closed. An error other than DocumentNotFoundError has its absence\n * confirmed rather than inferred from the message: a transient failure must\n * surface, not silently deny.\n */\n async getState(\n documentId: string,\n scope: string,\n branch: string,\n targetRevision?: number,\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n if (\n targetRevision === undefined &&\n documentId === this.seed.header.id &&\n branch === this.branch &&\n scope in (this.seed.state as Record<string, unknown>)\n ) {\n return this.seed;\n }\n\n try {\n return await this.documentView.get(\n documentId,\n { branch, scopes: [scope] },\n undefined,\n signal,\n );\n } catch (error) {\n await assertAbsent(this.documentView, documentId, error, signal);\n throw new DocumentNotFoundError(documentId);\n }\n }\n}\n\n/**\n * Evaluates a read against the registered decision model, built at the stream\n * heads. This is what makes `{ group }` principals and conditional grants apply\n * to a read: the model supplies the groups map and the scope's own state, the\n * same two things admission supplies.\n *\n * A read has no action, so a condition on `action.input.*` never holds for one.\n *\n * State is read through the read side rather than the write cache. The write\n * cache is invalidated by whichever process runs the executor, so a reactor\n * running its executors in worker processes would answer reads in the parent\n * from state no commit ever invalidates.\n */\nexport class ModelReadGate implements IReadGate {\n constructor(\n private readonly model: RegisteredDecisionModel,\n private readonly documentView: IDocumentView,\n /**\n * Whether a group a policy names is served to that policy's audience. Only\n * meaningful with `authGroups`, which is what makes a `{ group }` grant\n * match at all; below it the grant fails closed, so serving the roster\n * would publish a member list no read grant can use.\n */\n private readonly servesGroups: boolean,\n private readonly operationIndex?: IOperationIndex,\n private readonly logger?: ILogger,\n private readonly options: ReadGateOptions = OPEN_BY_DEFAULT,\n ) {}\n\n /**\n * A served group yields its member list and nothing else. What the audience\n * is owed is the state it must fold to evaluate auth with the group; a\n * group's other scopes are its own business and stay behind its own grants.\n */\n async scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n const decide = await this.prepare(document, branch, signal);\n return decide(subject);\n }\n\n async prepare(\n document: PHDocument,\n branch: string,\n signal?: AbortSignal,\n ): Promise<SubjectScopePredicate> {\n const policy = await this.ownPolicy(document, branch, signal);\n\n if (!this.servesGroup(document)) {\n return (subject) => Promise.resolve(policy(subject));\n }\n\n const referencers = this.referencerReads(document.header.id, signal);\n\n return async (subject) => {\n const own = policy(subject);\n if (own(GROUP_MEMBERSHIP_SCOPE)) {\n return own;\n }\n\n const audience = await this.servesGroupTo(subject, referencers);\n\n return (scope: string) =>\n own(scope) || (audience && scope === GROUP_MEMBERSHIP_SCOPE);\n };\n }\n\n /**\n * Whether the subject is served this group because a policy names it.\n *\n * A replica must fold a group's membership to evaluate auth with it, so a\n * group a grant names is served to the audience of the document that names\n * it, whatever the group's own read grants say. Naming a group in a policy\n * publishes its roster to that policy's audience; a group whose membership\n * must stay confidential does not belong in a grant.\n *\n * The referencing document's own domain scopes are the test. Its `auth` and\n * `document` scopes are readable by every holder, so testing those would\n * serve every referenced group to everybody.\n *\n * One level only. A referencer that is itself a group is skipped, and a\n * referencer's own readability is decided from its policy alone, so a\n * reference cycle terminates. Cycles are reachable: the reference relation\n * is recorded from an operation's input, including one later stored denied,\n * so a refused grant naming a group from inside another group leaves a row\n * behind that validation never saw.\n *\n * The referencers are probed a few at a time and the walk stops at the first\n * that serves, because a subject outside the audience is the case that runs to\n * the bound, and it is the common one. A probe that failed decides only when\n * nothing served: serving rests on a real allow, so this cannot widen, and it\n * stops one unreachable referencer from turning an allow already in hand into\n * a denial. A read records no operation, so replicas differing over a\n * transient failure has no consensus consequence.\n *\n * The probes are awaited together rather than raced, so none is ever left\n * running with nobody awaiting it, which is where unhandled rejections come\n * from.\n */\n private async servesGroupTo(\n subject: AuthSubject,\n referencers: ReferencerReads,\n ): Promise<boolean> {\n const examined = await referencers.examined();\n\n const walk: ReferencerWalk = {\n next: 0,\n served: false,\n failure: undefined,\n };\n\n const probe = async (): Promise<void> => {\n while (!walk.served && walk.next < examined.length) {\n const referencerId = examined[walk.next++];\n\n // Held in a local rather than assigned straight to the walk: reading\n // that back after the await could overwrite another probe's answer.\n let serves = false;\n try {\n serves = (await referencers.serves(referencerId))(subject);\n } catch (error) {\n walk.failure ??= Error.isError(error)\n ? error\n : new Error(`Probing referencer ${referencerId} failed`);\n }\n\n if (serves) {\n walk.served = true;\n }\n }\n };\n\n await Promise.all(\n Array.from(\n { length: Math.min(REFERENCER_PROBE_CONCURRENCY, examined.length) },\n probe,\n ),\n );\n\n if (!walk.served && walk.failure) {\n throw walk.failure;\n }\n\n return walk.served;\n }\n\n /** Each read made once, however many subjects the walk runs for. */\n private referencerReads(\n groupId: string,\n signal?: AbortSignal,\n ): ReferencerReads {\n let examined: Promise<string[]> | undefined;\n const serves = new Map<\n string,\n Promise<(subject: AuthSubject) => boolean>\n >();\n\n return {\n examined: () => (examined ??= this.examinedReferencers(groupId, signal)),\n serves: (referencerId) => {\n let read = serves.get(referencerId);\n if (!read) {\n read = this.servesThrough(referencerId, signal);\n serves.set(referencerId, read);\n }\n return read;\n },\n };\n }\n\n private async examinedReferencers(\n groupId: string,\n signal?: AbortSignal,\n ): Promise<string[]> {\n if (!this.operationIndex) {\n return [];\n }\n\n const referencers = await this.operationIndex.getGroupReferencers(\n groupId,\n signal,\n );\n\n const examined = referencers.slice(0, MAX_EXAMINED_REFERENCERS);\n if (examined.length < referencers.length) {\n this.logger?.warn(\n `Group ${groupId} is referenced by ${referencers.length} documents; only the first ${MAX_EXAMINED_REFERENCERS} decide whether it is served`,\n );\n }\n return examined;\n }\n\n /**\n * Whether one referencing document serves a subject any domain scope. A\n * referencer this replica does not hold serves nothing, which fails closed\n * the same way a group it does not hold does.\n */\n private async servesThrough(\n referencerId: string,\n signal?: AbortSignal,\n ): Promise<(subject: AuthSubject) => boolean> {\n let referencer: PHDocument;\n try {\n referencer = await this.documentView.get(\n referencerId,\n { branch: GROUP_BRANCH },\n undefined,\n signal,\n );\n } catch {\n return () => false;\n }\n\n if (this.servesGroup(referencer)) {\n return () => false;\n }\n\n const policy = await this.ownPolicy(referencer, GROUP_BRANCH, signal);\n\n return (subject) => allowsSomeDomainScope(referencer, policy(subject));\n }\n\n private servesGroup(document: PHDocument): boolean {\n return (\n this.servesGroups &&\n this.operationIndex !== undefined &&\n document.header.documentType === groupDocumentType\n );\n }\n\n /**\n * What this document's own policy says, with no group serving applied.\n *\n * An unpoliced document is readable in full unless the host closes by default,\n * and either way it is the common case and the one worth answering without\n * building anything. The test is the one `evaluate` makes: a legacy `{}` auth\n * scope and version 0 both mean uninitialized, and \"no grants\" does not,\n * because a policy with a version and an empty grant list denies everything.\n *\n * Closing here rather than around the gate is what keeps a policy able to\n * publish an unpoliced group it names: the referencer walk asks this question\n * of the referencing document, whose real policy answers it.\n */\n private async ownPolicy(\n document: PHDocument,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(subject: AuthSubject) => (scope: string) => boolean> {\n const auth = authOf(document);\n\n if (!auth || !auth.version) {\n const readable = this.options.withholdUninitialized\n ? (scope: string) => ALWAYS_READABLE_SCOPES.has(scope)\n : () => true;\n return () => readable;\n }\n\n const target = { documentId: document.header.id, branch };\n const built = await buildDecisionModel(\n new SeededStateReader(this.documentView, document, branch),\n this.model,\n target,\n signal,\n );\n\n // The read-set the build recorded guards a write, and a read makes none.\n const definition = this.model(target);\n const scopeStates = (document.state ?? {}) as Record<string, unknown>;\n\n return (subject) => (scope: string) =>\n ALWAYS_READABLE_SCOPES.has(scope) ||\n definition.decide(\n built.model,\n subject,\n { verb: \"read\", scope },\n { scopeState: scopeStates[scope], actionInput: undefined },\n ).decision === \"allow\";\n }\n}\n","import type {\n AuthSubject,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { IReactor } from \"../core/types.js\";\nimport type {\n IReadGate,\n SubjectScopePredicate,\n} from \"../decision/read-gate.js\";\nimport type { ViewFilter } from \"../shared/types.js\";\nimport type {\n DocumentExistence,\n IDocumentView,\n} from \"../storage/interfaces.js\";\n\n/** One change event's reads, shared by every subscription it reaches. */\nexport class EventReads {\n private readonly documents = new Map<string, Promise<PHDocument>>();\n private readonly existence = new Map<string, Promise<boolean[]>>();\n private readonly prepared = new WeakMap<\n PHDocument,\n Map<string, Promise<SubjectScopePredicate>>\n >();\n\n constructor(\n private readonly reactor: IReactor,\n private readonly documentView: IDocumentView,\n private readonly readGate: IReadGate,\n ) {}\n\n get(id: string, view: ViewFilter | undefined): Promise<PHDocument> {\n const key = JSON.stringify([id, view?.branch, view?.scopes]);\n let read = this.documents.get(key);\n if (!read) {\n read = this.reactor.get(id, view);\n this.documents.set(key, read);\n }\n return read;\n }\n\n exists(ids: string[], existence: DocumentExistence): Promise<boolean[]> {\n const key = JSON.stringify([ids, existence]);\n let read = this.existence.get(key);\n if (!read) {\n read = this.documentView.exists(ids, existence);\n this.existence.set(key, read);\n }\n return read;\n }\n\n async scopePredicate(\n document: PHDocument,\n subject: AuthSubject,\n branch: string,\n ): Promise<(scope: string) => boolean> {\n if (!this.readGate.prepare) {\n return this.readGate.scopePredicate(document, subject, branch);\n }\n\n let byBranch = this.prepared.get(document);\n if (!byBranch) {\n byBranch = new Map();\n this.prepared.set(document, byBranch);\n }\n let prepared = byBranch.get(branch);\n if (!prepared) {\n prepared = this.readGate.prepare(document, branch);\n byBranch.set(branch, prepared);\n }\n return (await prepared)(subject);\n }\n}\n\n// The subscription manager notifies every subscription in one synchronous pass.\nexport class EventReadsSource {\n private current: EventReads | undefined;\n\n constructor(\n private readonly reactor: IReactor,\n private readonly documentView: IDocumentView,\n private readonly readGate: IReadGate,\n ) {}\n\n forEvent(): EventReads {\n if (!this.current) {\n this.current = new EventReads(\n this.reactor,\n this.documentView,\n this.readGate,\n );\n queueMicrotask(() => {\n this.current = undefined;\n });\n }\n return this.current;\n }\n}\n","import type {\n AuthSubject,\n ISigner,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ViewFilter } from \"../shared/types.js\";\n\nexport function authSubjectFromSigner(signer: ISigner): AuthSubject {\n return { address: signer.user?.address, key: signer.app?.key };\n}\n\n// Ensures a scoped read still fetches the auth scope, so the gate sees the policy.\nexport function withAuthScope(view?: ViewFilter): ViewFilter | undefined {\n if (view?.scopes && view.scopes.length > 0) {\n return { ...view, scopes: [...new Set([...view.scopes, \"auth\"])] };\n }\n return view;\n}\n\n// Unnarrowed, so withholding is decided on every scope the document holds.\nexport function withAllScopes(view?: ViewFilter): ViewFilter | undefined {\n if (!view?.scopes) {\n return view;\n }\n const { scopes: _narrowed, ...whole } = view;\n return whole;\n}\n\n// The scopes a narrowed fetch returns, or undefined when the view does not narrow.\nexport function narrowedScopes(view?: ViewFilter): Set<string> | undefined {\n if (!view?.scopes || view.scopes.length === 0) {\n return undefined;\n }\n return new Set([\"document\", \"auth\", ...view.scopes]);\n}\n\n/**\n * Drops the scopes the predicate refuses. The predicate is resolved by the read\n * gate, which decides against the whole policy once per document; this only\n * applies the answer, so it stays synchronous.\n */\nexport function filterReadableScopes<TDocument extends PHDocument>(\n document: TDocument,\n readable: (scope: string) => boolean,\n): TDocument {\n const state = document.state as Record<string, unknown> | undefined;\n if (!state) {\n return document;\n }\n\n // initialState carries the same scopes as state, so filtering one and\n // spreading the other hands back the contents just removed.\n return {\n ...document,\n state: keepReadableScopes(state, readable),\n initialState: keepReadableScopes(\n document.initialState as Record<string, unknown> | undefined,\n readable,\n ),\n } as TDocument;\n}\n\nfunction keepReadableScopes(\n scopes: Record<string, unknown> | undefined,\n readable: (scope: string) => boolean,\n): Record<string, unknown> | undefined {\n if (!scopes) {\n return scopes;\n }\n const kept: Record<string, unknown> = {};\n for (const scope of Object.keys(scopes)) {\n if (readable(scope)) {\n kept[scope] = scopes[scope];\n }\n }\n return kept;\n}\n","import type {\n Action,\n AuthSubject,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n ProtocolVersions,\n SignaturePolicy,\n Supports,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n actions,\n DEFAULT_SIGNATURE_POLICY,\n PEER_CAPABILITIES,\n selectProtocolVersions,\n DowngradeNotSupportedError,\n normalizeDocumentModelVersion,\n requestedSignaturePolicy,\n UnsupportedDocumentModelVersionError,\n withSignaturePolicy,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n deleteDocumentAction,\n removeRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type {\n BatchExecutionRequest,\n BatchExecutionResult,\n BatchLoadRequest,\n BatchLoadResult,\n ExecutionJobPlan,\n IReactor,\n} from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport { type IJobAwaiter } from \"../shared/awaiter.js\";\nimport {\n AuthEnforcementDisabledError,\n RelationshipNotFoundError,\n} from \"../shared/errors.js\";\nimport {\n JobStatus,\n PropagationMode,\n RelationshipChangeType,\n type JobInfo,\n type PagedResults,\n type PagingOptions,\n type SearchFilter,\n type ViewFilter,\n} from \"../shared/types.js\";\nimport { DocumentExistence } from \"../storage/interfaces.js\";\nimport type {\n DocumentRelationship,\n IDocumentIndexer,\n IDocumentView,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport {\n decodeCompositeCursor,\n encodeCompositeCursor,\n isCompositeCursor,\n} from \"./cursor.js\";\nimport { DriveClient } from \"./drive-client.js\";\nimport {\n DEFAULT_UPGRADE_CONFLICT_RETRIES,\n DocumentChangeType,\n type ActionCandidate,\n type ActionEvaluations,\n type CreateDocumentOptions,\n type DocumentChangeEvent,\n type IDriveClient,\n type IReactorClient,\n type ProtocolSelection,\n type UpgradeDocumentOptions,\n} from \"./types.js\";\nimport { DriveCollectionId } from \"../cache/operation-index-types.js\";\nimport { buildDecisionModel } from \"../decision/build-decision-model.js\";\nimport type { IReadGate } from \"../decision/read-gate.js\";\nimport {\n assertAbsent,\n BareReadGate,\n refusesEveryDomainScope,\n SeededStateReader,\n unheldScopesReadOnState,\n} from \"../decision/read-gate.js\";\nimport type { DocumentDecisionModel } from \"../decision/document-decision-model.js\";\nimport type { RegisteredDecisionModel } from \"../decision/registered-model.js\";\nimport type { DecisionModel, Evaluation } from \"../decision/types.js\";\nimport { GATED_DOCUMENT_ACTIONS, targetDocumentId } from \"../executor/util.js\";\nimport { type EventReads, EventReadsSource } from \"./event-reads.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport {\n authSubjectFromSigner,\n filterReadableScopes,\n narrowedScopes,\n withAllScopes,\n withAuthScope,\n} from \"./util.js\";\n\n/**\n * A decision model built for one target document, with what deciding a candidate\n * against it needs: the definition that decides, the built model it decides\n * over, and the scope states a condition reads.\n */\ntype EvaluationTarget = {\n definition: DecisionModel<DocumentDecisionModel>;\n model: DocumentDecisionModel;\n scopeStates: Record<string, unknown>;\n};\n\nfunction deletePlanKey(documentId: string): string {\n return `delete:${documentId}`;\n}\n\n/**\n * The document a candidate is decided against. Routed on the action type alone,\n * which is what the executor routes on: every action the reactor reduces onto\n * the document scope carries that scope already, and the gate follows the\n * action's own target rather than the document the request named.\n */\nfunction evaluationTargetId(\n candidate: ActionCandidate,\n fallback: string,\n): string {\n return GATED_DOCUMENT_ACTIONS.has(candidate.type)\n ? targetDocumentId(\n { type: candidate.type, input: candidate.input },\n fallback,\n )\n : fallback;\n}\n\n/**\n * One candidate's verdict, decided exactly as admission decides it: the same\n * request, and a condition context populated only while authConditions is on,\n * so a conditional grant applies here precisely when it would apply there.\n * Groups need nothing added, because the selected model carries them.\n */\nfunction decideCandidate(\n config: ActionEvaluationConfig,\n target: EvaluationTarget,\n subject: AuthSubject,\n candidate: ActionCandidate,\n): Evaluation {\n return target.definition.decide(\n target.model,\n subject,\n { verb: \"execute\", scope: candidate.scope, operation: candidate.type },\n config.flags.authConditions\n ? {\n scopeState: target.scopeStates[candidate.scope],\n actionInput: candidate.input,\n }\n : { scopeState: undefined, actionInput: undefined },\n );\n}\n\n/**\n * What {@link IReactorClient.evaluateActions} decides against: the decision\n * model this reactor enforces, and the flags that selected it.\n *\n * Both, because neither alone is enough. The flags say which of the model's\n * inputs a decision may read, and the model itself cannot be derived from them\n * here: selecting one needs the document model registry, which a client does not\n * hold. Absent, this client answers no preflight at all -- which is the whole of\n * the non-coexistence guarantee, since a client built without a reactor holding\n * a decision model has nothing to answer from.\n */\nexport type ActionEvaluationConfig = {\n model: RegisteredDecisionModel;\n flags: ReactorFeatureFlags;\n};\n\n/**\n * ReactorClient implementation that wraps lower-level APIs to provide\n * a simpler interface for document operations.\n *\n * Features:\n * - Wraps Jobs with Promises for easier async handling\n * - Manages signing of submitted Action objects\n * - Provides quality-of-life functions for common tasks\n * - Wraps subscription interface with ViewFilters\n */\nexport class ReactorClient implements IReactorClient {\n private logger: ILogger;\n private reactor: IReactor;\n private signer: ISigner;\n private subscriptionManager: IReactorSubscriptionManager;\n private jobAwaiter: IJobAwaiter;\n private documentIndexer: IDocumentIndexer;\n private documentView: IDocumentView;\n private readGate: IReadGate;\n private eventReads: EventReadsSource;\n private actionEvaluation: ActionEvaluationConfig | undefined;\n private readonly createSignaturePolicy: SignaturePolicy;\n private readonly protocolSelection: ProtocolSelection;\n\n readonly drives: IDriveClient;\n\n constructor(\n logger: ILogger,\n reactor: IReactor,\n signer: ISigner,\n subscriptionManager: IReactorSubscriptionManager,\n jobAwaiter: IJobAwaiter,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n readGate: IReadGate = new BareReadGate(),\n actionEvaluation?: ActionEvaluationConfig,\n createSignaturePolicy: SignaturePolicy = DEFAULT_SIGNATURE_POLICY,\n protocolSelection: ProtocolSelection = {\n capabilities: PEER_CAPABILITIES,\n flags: {},\n collectionsOf: () => Promise.resolve({}),\n },\n ) {\n this.protocolSelection = protocolSelection;\n this.logger = logger;\n this.reactor = reactor;\n this.signer = signer;\n this.subscriptionManager = subscriptionManager;\n this.jobAwaiter = jobAwaiter;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n this.readGate = readGate;\n this.eventReads = new EventReadsSource(reactor, documentView, readGate);\n this.actionEvaluation = actionEvaluation;\n this.createSignaturePolicy = createSignaturePolicy;\n this.drives = new DriveClient(this, logger, reactor, signer, (id, signal) =>\n this.resolveReference(id, \"main\", signal),\n );\n this.logger.verbose(\"ReactorClient initialized\");\n }\n\n /**\n * Retrieves a list of document model modules.\n */\n async getDocumentModelModules(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n return this.reactor.getDocumentModels(namespace, paging, signal);\n }\n\n /**\n * Retrieves a specific document model module by document type.\n *\n * @param documentType - The document type identifier\n * @returns The document model module\n */\n async getDocumentModelModule(\n documentType: string,\n ): Promise<DocumentModelModule<any>> {\n const modules = await this.reactor.getDocumentModels();\n\n let latestModule: DocumentModelModule | undefined;\n let latestVersion = -1;\n for (const module of modules.results) {\n if (module.documentModel.global.id !== documentType) {\n continue;\n }\n const version = module.version ?? 1;\n if (version > latestVersion) {\n latestVersion = version;\n latestModule = module;\n }\n }\n\n if (!latestModule) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n\n return latestModule as DocumentModelModule<any>;\n }\n\n /**\n * Retrieves the document model module matching the version the document is\n * stamped with, so not-yet-upgraded documents get the reducer their\n * history was written with rather than the latest.\n */\n async getDocumentModelModuleForDocument(\n document: PHDocument,\n ): Promise<DocumentModelModule<any>> {\n const documentType = document.header.documentType;\n const version = normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n );\n\n const modules = await this.reactor.getDocumentModels();\n\n const availableVersions: number[] = [];\n for (const module of modules.results) {\n if (module.documentModel.global.id !== documentType) {\n continue;\n }\n const moduleVersion = normalizeDocumentModelVersion(module.version);\n if (moduleVersion === version) {\n return module as DocumentModelModule<any>;\n }\n availableVersions.push(moduleVersion);\n }\n\n throw new UnsupportedDocumentModelVersionError(\n documentType,\n version,\n availableVersions.sort((a, b) => a - b),\n );\n }\n\n /**\n * Retrieves a specific PHDocument\n */\n async get<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@identifier, @view)\", identifier, view);\n const document = await this.reactor.getByIdOrSlug<TDocument>(\n identifier,\n withAuthScope(view),\n undefined,\n signal,\n );\n return this.gateDocument(document, view, signal);\n }\n\n /**\n * Resolves an identifier (id or slug) to the canonical document id, using the\n * same lookup as the data path. Resolves against the \"main\" branch. Throws if\n * the identifier cannot be resolved or is ambiguous.\n */\n async resolveIdOrSlug(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<string> {\n return this.documentView.resolveIdOrSlug(\n identifier,\n view,\n undefined,\n signal,\n );\n }\n\n /** True when the id is taken; a soft-deleted document's id is still taken. */\n async isDocumentIdTaken(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n this.logger.verbose(\"isDocumentIdTaken(@documentId)\", documentId);\n const taken = await this.documentView.exists(\n [documentId],\n DocumentExistence.IncludingDeleted,\n undefined,\n signal,\n );\n return taken[0];\n }\n\n /**\n * Retrieves operations for a document\n */\n async getOperations(\n documentIdentifier: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<Operation>> {\n this.logger.verbose(\n \"getOperations(@documentIdentifier, @view, @filter, @paging)\",\n documentIdentifier,\n view,\n filter,\n paging,\n );\n\n const documentId = await this.documentView.resolveIdOrSlug(\n documentIdentifier,\n view,\n undefined,\n signal,\n );\n\n // Read gate: exclude operations in scopes the subject may not read. The\n // fetch carries the scopes about to be paged, not the policy alone, because\n // a conditional read grant reads the state of the scope it gates; a\n // condition can name no other scope, so the scopes not paged are not owed.\n const gated = await this.reactor.getByIdOrSlug<PHDocument>(\n documentId,\n withAuthScope(view),\n undefined,\n signal,\n );\n const canRead = await this.readableScopes(gated, view, signal);\n\n if (paging?.cursor && isCompositeCursor(paging.cursor)) {\n return this.getOperationsWithCompositeCursor(\n documentId,\n view,\n filter,\n paging,\n signal,\n canRead,\n );\n }\n\n const operationsByScope = await this.reactor.getOperations(\n documentId,\n view,\n filter,\n paging,\n undefined,\n signal,\n );\n\n for (const scope of Object.keys(operationsByScope)) {\n if (!canRead(scope)) {\n delete operationsByScope[scope];\n }\n }\n\n const scopeEntries = Object.entries(operationsByScope);\n const effectivePaging = paging || { cursor: \"0\", limit: 100 };\n\n if (scopeEntries.length <= 1) {\n const allOperations =\n scopeEntries.length === 1 ? [...scopeEntries[0][1].results] : [];\n allOperations.sort((a, b) => a.index - b.index);\n const nextCursor =\n scopeEntries.length === 1 ? scopeEntries[0][1].nextCursor : undefined;\n return { results: allOperations, options: effectivePaging, nextCursor };\n }\n\n const allOperations: Operation[] = [];\n const activeCursors: Record<string, string> = {};\n\n for (const [scopeName, scopeResults] of scopeEntries) {\n allOperations.push(...scopeResults.results);\n if (scopeResults.nextCursor) {\n activeCursors[scopeName] = scopeResults.nextCursor;\n }\n }\n\n allOperations.sort((a, b) => a.index - b.index);\n\n const nextCursor =\n Object.keys(activeCursors).length > 0\n ? encodeCompositeCursor(activeCursors)\n : undefined;\n\n return { results: allOperations, options: effectivePaging, nextCursor };\n }\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n */\n async getOutgoingRelationships(\n sourceIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getOutgoingRelationships(@sourceIdentifier, @relationshipType, @view, @paging)\",\n sourceIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const sourceId = await this.documentView.resolveIdOrSlug(\n sourceIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getOutgoing(\n sourceId,\n [relationshipType],\n undefined,\n undefined,\n signal,\n );\n\n const targetIds = relationships.results.map((rel) => rel.targetId);\n\n if (targetIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.find({ ids: targetIds }, view, paging, signal);\n }\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n */\n async getIncomingRelationships(\n targetIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\n \"getIncomingRelationships(@targetIdentifier, @relationshipType, @view, @paging)\",\n targetIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const targetId = await this.documentView.resolveIdOrSlug(\n targetIdentifier,\n view,\n undefined,\n signal,\n );\n\n const relationships = await this.documentIndexer.getIncoming(\n targetId,\n [relationshipType],\n undefined,\n undefined,\n signal,\n );\n\n const sourceIds = relationships.results.map((rel) => rel.sourceId);\n\n if (sourceIds.length === 0) {\n return {\n results: [],\n options: paging || { cursor: \"0\", limit: 0 },\n };\n }\n\n return this.find({ ids: sourceIds }, view, paging, signal);\n }\n\n /**\n * Retrieves the outgoing relationship edges of a source document, carrying the\n * metadata and timestamps the far-end documents do not.\n */\n async getOutgoingRelationshipEdges(\n sourceIdentifier: string,\n relationshipType?: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n this.logger.verbose(\n \"getOutgoingRelationshipEdges(@sourceIdentifier, @relationshipType, @view, @paging)\",\n sourceIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const sourceId = await this.documentView.resolveIdOrSlug(\n sourceIdentifier,\n view,\n undefined,\n signal,\n );\n\n const edges = await this.reactor.getOutgoingRelationshipEdges(\n sourceId,\n relationshipType,\n paging,\n undefined,\n signal,\n );\n\n return this.gateEdges(edges, \"targetId\", view, signal);\n }\n\n /**\n * Retrieves the incoming relationship edges of a target document, carrying the\n * metadata and timestamps the far-end documents do not.\n */\n async getIncomingRelationshipEdges(\n targetIdentifier: string,\n relationshipType?: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n this.logger.verbose(\n \"getIncomingRelationshipEdges(@targetIdentifier, @relationshipType, @view, @paging)\",\n targetIdentifier,\n relationshipType,\n view,\n paging,\n );\n\n const targetId = await this.documentView.resolveIdOrSlug(\n targetIdentifier,\n view,\n undefined,\n signal,\n );\n\n const edges = await this.reactor.getIncomingRelationshipEdges(\n targetId,\n relationshipType,\n paging,\n undefined,\n signal,\n );\n\n return this.gateEdges(edges, \"sourceId\", view, signal);\n }\n\n /**\n * Filters documents by criteria and returns a list of them\n */\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n const results = await this.reactor.find(\n search,\n withAuthScope(view),\n paging,\n undefined,\n signal,\n );\n return this.gateListing(results, view, signal);\n }\n\n /** Whether `find` would serve the document; false when it is absent. */\n async isServed(\n identifier: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<boolean> {\n this.logger.verbose(\"isServed(@identifier, @view)\", identifier, view);\n let document: PHDocument;\n try {\n document = await this.reactor.getByIdOrSlug(\n identifier,\n withAuthScope(view),\n undefined,\n signal,\n );\n } catch (error) {\n await assertAbsent(this.documentView, identifier, error, signal);\n return false;\n }\n return (await this.gateListed(document, view, signal)) !== undefined;\n }\n\n /**\n * Predicts the admission verdict for each candidate. See\n * {@link IReactorClient.evaluateActions} for the contract and its caveats.\n *\n * Read-only throughout, and never through the write cache: that cache is\n * invalidated by whichever process runs the executor, so a reactor running\n * its executors in worker processes would answer here from state no commit\n * ever invalidates.\n */\n async evaluateActions(\n documentIdentifier: string,\n branch: string,\n candidates: ActionCandidate[],\n subject?: AuthSubject,\n signal?: AbortSignal,\n ): Promise<ActionEvaluations> {\n this.logger.verbose(\n \"evaluateActions(@documentIdentifier, @branch, @count candidates)\",\n documentIdentifier,\n branch,\n candidates.length,\n );\n\n // No model, no answer. The legacy host-table permission system is not\n // consulted and not composed with: see AuthEnforcementDisabledError.\n const config = this.actionEvaluation;\n if (config === undefined) {\n throw new AuthEnforcementDisabledError();\n }\n\n const decidedSubject = this.readSubject(subject);\n const resolvedId = await this.documentView.resolveIdOrSlug(\n documentIdentifier,\n { branch },\n undefined,\n signal,\n );\n\n // Built once per distinct target rather than once per candidate, so a batch\n // of candidates against one document reads its streams once.\n const targets = new Map<string, EvaluationTarget>();\n const evaluations: Evaluation[] = [];\n\n for (const candidate of candidates) {\n // CREATE_DOCUMENT is exempt from the executor's policy gate by necessity:\n // it runs before the document exists, so there is no policy to decide\n // against (see GATED_DOCUMENT_ACTIONS). Mirror the exemption rather than\n // deciding the candidate against the named document's policy, which would\n // predict a denial the submit path never issues.\n if (candidate.type === \"CREATE_DOCUMENT\") {\n evaluations.push({ decision: \"allow\" });\n continue;\n }\n\n const targetId = evaluationTargetId(candidate, resolvedId);\n\n let target = targets.get(targetId);\n if (target === undefined) {\n target = await this.buildEvaluationTarget(\n config,\n targetId,\n branch,\n signal,\n );\n targets.set(targetId, target);\n }\n\n evaluations.push(\n decideCandidate(config, target, decidedSubject, candidate),\n );\n }\n\n const allowed = evaluations.filter(\n (evaluation) => evaluation.decision === \"allow\",\n ).length;\n\n return {\n evaluations,\n allAllowed: candidates.length > 0 && allowed === candidates.length,\n anyAllowed: allowed > 0,\n allDenied: candidates.length > 0 && allowed === 0,\n anyDenied: allowed < candidates.length,\n };\n }\n\n getCreateSignaturePolicy(): Promise<SignaturePolicy> {\n return Promise.resolve(this.createSignaturePolicy);\n }\n\n async getCreateProtocolVersions(\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<ProtocolVersions> {\n const { capabilities, flags, agreement, collectionsOf } =\n this.protocolSelection;\n let members: Supports[] = [];\n if (parentIdentifier !== undefined && agreement) {\n let parentId: string | undefined;\n try {\n parentId = await this.resolveIdOrSlug(\n parentIdentifier,\n undefined,\n signal,\n );\n } catch {\n parentId = undefined;\n }\n if (parentId !== undefined) {\n const memberships = await collectionsOf([parentId]);\n const collectionIds = [\n ...(memberships[parentId] ?? []),\n DriveCollectionId.forDrive(parentId).key,\n ];\n members = [...agreement().members(collectionIds).values()];\n }\n }\n return selectProtocolVersions({ capabilities, flags, members });\n }\n\n /**\n * Creates a document and waits for completion\n */\n async create<TDocument extends PHDocument = PHDocument>(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"create(@id, @parentIdentifier)\",\n document.header.id,\n parentIdentifier,\n );\n\n const batchResult = await this.submitCreate(\n document,\n parentIdentifier,\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n const created = await this.reactor.get<TDocument>(document.header.id);\n return this.gateDocument(created, undefined, signal);\n }\n\n /**\n * Submits a document's create batch without waiting for it\n */\n async createAsync(\n document: PHDocument,\n parentIdentifier?: string,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\n \"createAsync(@id, @parentIdentifier)\",\n document.header.id,\n parentIdentifier,\n );\n return this.submitCreate(document, parentIdentifier, signal);\n }\n\n private async submitCreate(\n document: PHDocument,\n parentIdentifier: string | undefined,\n signal: AbortSignal | undefined,\n ): Promise<BatchExecutionResult> {\n const documentId = document.header.id;\n const branch = document.header.branch || \"main\";\n const parentId = parentIdentifier\n ? await this.resolveReference(parentIdentifier, \"main\", signal)\n : undefined;\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId,\n signing: {\n signature: documentId,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createActions: Action[] = await signActions(\n [\n createDocumentAction(createInput),\n upgradeDocumentAction({\n documentId,\n model: document.header.documentType,\n fromVersion: 0,\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document\n ?.version,\n ),\n initialState: document.state,\n }),\n ],\n this.signer,\n { documentId, branch },\n signal,\n );\n\n const jobs: ExecutionJobPlan[] = [\n {\n key: \"create\",\n documentId,\n scope: getSharedActionScope(createActions),\n branch,\n actions: createActions,\n dependsOn: [],\n },\n ];\n\n if (parentId) {\n const parentActions: Action[] = await signActions(\n [addRelationshipAction(parentId, documentId, \"child\")],\n this.signer,\n { documentId: parentId, branch: \"main\" },\n signal,\n );\n\n jobs.push({\n key: \"parent\",\n documentId: parentId,\n scope: getSharedActionScope(parentActions),\n branch: \"main\",\n actions: parentActions,\n dependsOn: [\"create\"],\n });\n }\n\n return this.reactor.executeBatch({ jobs }, signal);\n }\n\n /**\n * Creates an empty document and waits for completion\n */\n async createEmpty<TDocument extends PHDocument>(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"createEmpty(@documentModelType, @options)\",\n documentModelType,\n options,\n );\n const document = await this.emptyDocument(\n documentModelType,\n options,\n signal,\n );\n return this.create<TDocument>(document, options?.parentIdentifier, signal);\n }\n\n /**\n * Submits an empty document's create batch without waiting for it\n */\n async createEmptyAsync(\n documentModelType: string,\n options?: CreateDocumentOptions,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\n \"createEmptyAsync(@documentModelType, @options)\",\n documentModelType,\n options,\n );\n const document = await this.emptyDocument(\n documentModelType,\n options,\n signal,\n );\n return this.submitCreate(document, options?.parentIdentifier, signal);\n }\n\n private async emptyDocument(\n documentModelType: string,\n options: CreateDocumentOptions | undefined,\n signal: AbortSignal | undefined,\n ): Promise<PHDocument> {\n const modulesResult = await this.reactor.getDocumentModels(\n undefined,\n undefined,\n signal,\n );\n\n const matchingModules = modulesResult.results.filter(\n (m) => m.documentModel.global.id === documentModelType,\n );\n\n let module: DocumentModelModule | undefined;\n if (options?.documentModelVersion !== undefined) {\n const requestedVersion = normalizeDocumentModelVersion(\n options.documentModelVersion,\n );\n module = matchingModules.find(\n (m) => normalizeDocumentModelVersion(m.version) === requestedVersion,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType} with version: ${options.documentModelVersion}`,\n );\n }\n } else {\n module = matchingModules.reduce<DocumentModelModule | undefined>(\n (latest, current) => {\n if (latest === undefined) return current;\n const currentVersion = normalizeDocumentModelVersion(current.version);\n const latestVersion = normalizeDocumentModelVersion(latest.version);\n return currentVersion > latestVersion ? current : latest;\n },\n undefined,\n );\n if (!module) {\n throw new Error(\n `Document model not found for type: ${documentModelType}`,\n );\n }\n }\n\n const base = await this.getCreateProtocolVersions(\n options?.parentIdentifier,\n signal,\n );\n const document = withSignaturePolicy(\n module.utils.createDocument(),\n requestedSignaturePolicy(options, this.createSignaturePolicy),\n { protocolVersions: { ...base, ...options?.protocolVersions } },\n );\n document.state.document.version = normalizeDocumentModelVersion(\n module.version,\n );\n return document;\n }\n\n /**\n * Upgrades a document to a newer document model version by dispatching an\n * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the\n * latest registered module version for the document's type. Returns the\n * document unchanged when it is already at the target version.\n *\n * The executor validates the action's version and revision snapshot against\n * the state the migration actually runs on. When a concurrent edit\n * invalidates the snapshot, the upgrade is rebuilt from a fresh read and\n * retried up to maxConflictRetries times before the conflict is surfaced.\n */\n async upgradeDocument<TDocument extends PHDocument = PHDocument>(\n documentIdentifier: string,\n toVersion?: number,\n options?: UpgradeDocumentOptions,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"upgradeDocument(@documentIdentifier, @toVersion)\",\n documentIdentifier,\n toVersion,\n );\n\n const maxConflictRetries =\n options?.maxConflictRetries ?? DEFAULT_UPGRADE_CONFLICT_RETRIES;\n\n let lastConflictMessage = \"\";\n for (let attempt = 0; attempt <= maxConflictRetries; attempt++) {\n const document = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n undefined,\n undefined,\n signal,\n );\n\n const documentId = document.header.id;\n const documentType = document.header.documentType;\n const branch = document.header.branch || \"main\";\n const fromVersion = normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n );\n\n let targetVersion = toVersion;\n if (targetVersion === undefined) {\n const module = await this.getDocumentModelModule(documentType);\n targetVersion = normalizeDocumentModelVersion(module.version);\n }\n\n if (targetVersion === fromVersion) {\n return this.gateDocument(document, { branch }, signal);\n }\n if (targetVersion < fromVersion) {\n throw new DowngradeNotSupportedError(\n documentType,\n fromVersion,\n targetVersion,\n );\n }\n\n const action = upgradeDocumentAction({\n documentId,\n model: documentType,\n fromVersion,\n toVersion: targetVersion,\n revision: { ...document.header.revision },\n });\n\n const signedActions = await signActions(\n [action],\n this.signer,\n { documentId, branch },\n signal,\n );\n const jobInfo = await this.reactor.execute(\n documentId,\n branch,\n signedActions,\n signal,\n );\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status !== JobStatus.FAILED) {\n const upgraded = await this.reactor.getByIdOrSlug<TDocument>(\n documentId,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(upgraded, { branch }, signal);\n }\n\n if (completedJob.error?.name !== \"UpgradePreconditionFailedError\") {\n throw new Error(completedJob.error?.message);\n }\n lastConflictMessage = completedJob.error.message;\n }\n\n throw new Error(\n `Upgrade of document ${documentIdentifier} conflicted with concurrent edits after ${maxConflictRetries + 1} attempts: ${lastConflictMessage}`,\n );\n }\n\n /**\n * Creates an empty document in a drive as a single batched operation.\n * Delegates to {@link IDriveClient.addFile}.\n *\n * @deprecated Use `client.drives.addFile` instead. This method will be\n * removed in a future release.\n */\n async createDocumentInDrive<TDocument extends PHDocument>(\n driveId: string,\n document: PHDocument,\n parentFolder?: string,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n return this.drives.addFile<TDocument>(\n driveId,\n document,\n parentFolder,\n signal,\n );\n }\n\n /**\n * Applies a list of actions to a document and waits for completion.\n *\n * `subject` names who the returned document is read as; see\n * {@link IReactorClient.execute}.\n */\n async execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n subject?: AuthSubject,\n ): Promise<TDocument> {\n this.logger.verbose(\n \"execute(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const documentId = await this.resolveWriteTarget(\n documentIdentifier,\n branch,\n actions,\n signal,\n );\n const signedActions = await signActions(\n actions,\n this.signer,\n { documentId, branch },\n signal,\n );\n\n const jobInfo = await this.reactor.execute(\n documentId,\n branch,\n signedActions,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const view: ViewFilter = { branch, subject };\n const result = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n view,\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, view, signal);\n }\n\n /**\n * Submits a list of actions to a document\n */\n async executeAsync(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"executeAsync(@documentIdentifier, @branch, @count actions)\",\n documentIdentifier,\n branch,\n actions.length,\n );\n const documentId = await this.resolveWriteTarget(\n documentIdentifier,\n branch,\n actions,\n signal,\n );\n const signedActions = await signActions(\n actions,\n this.signer,\n { documentId, branch },\n signal,\n );\n\n return this.reactor.execute(documentId, branch, signedActions, signal);\n }\n\n async executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\"executeBatch(@count jobs)\", request.jobs.length);\n\n const signedJobs: ExecutionJobPlan[] = await Promise.all(\n request.jobs.map(async (job) => {\n const documentId = await this.resolveWriteTarget(\n job.documentId,\n job.branch,\n job.actions,\n signal,\n );\n return {\n ...job,\n documentId,\n actions: await signActions(\n job.actions,\n this.signer,\n { documentId, branch: job.branch },\n signal,\n ),\n };\n }),\n );\n\n const batchResult = await this.reactor.executeBatch(\n { jobs: signedJobs },\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n return batchResult;\n }\n\n /**\n * Renames a document and waits for completion\n */\n async rename(\n documentIdentifier: string,\n name: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"rename(@documentIdentifier, @name, @branch)\",\n documentIdentifier,\n name,\n branch,\n );\n return this.execute(\n documentIdentifier,\n branch,\n [actions.setName(name)],\n signal,\n );\n }\n\n /**\n * Updates the preferred editor recorded in the document header meta.\n * Pass `null` to clear it.\n */\n async setPreferredEditor(\n documentIdentifier: string,\n preferredEditor: string | null,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"setPreferredEditor(@documentIdentifier, @preferredEditor, @branch)\",\n documentIdentifier,\n preferredEditor,\n branch,\n );\n return this.execute(\n documentIdentifier,\n branch,\n [actions.setPreferredEditor(preferredEditor)],\n signal,\n );\n }\n\n /**\n * Adds multiple documents as children to another and waits for completion\n */\n async addRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @metadata, @branch)\",\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n metadata,\n branch,\n );\n const sourceId = await this.resolveReference(\n sourceIdentifier,\n branch,\n signal,\n );\n const targetId = await this.resolveReference(\n targetIdentifier,\n branch,\n signal,\n );\n const jobInfo = await this.reactor.addRelationship(\n sourceId,\n targetId,\n relationshipType,\n metadata,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceId,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, { branch }, signal);\n }\n\n /**\n * Replaces the metadata of an existing relationship and waits for completion.\n */\n async updateRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"updateRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @metadata, @branch)\",\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n metadata,\n branch,\n );\n const sourceId = await this.resolveReference(\n sourceIdentifier,\n branch,\n signal,\n );\n const targetId = await this.resolveReference(\n targetIdentifier,\n branch,\n signal,\n );\n\n // The write matches the edge in SQL and reports no row count, so an update\n // of an edge that is not there reaches READ_READY having stored nothing.\n const existing = await this.readRelationshipEdge(\n sourceId,\n targetId,\n relationshipType,\n { branch },\n signal,\n );\n if (!existing) {\n throw new RelationshipNotFoundError(\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n );\n }\n\n const jobInfo = await this.reactor.updateRelationship(\n sourceId,\n targetId,\n relationshipType,\n metadata,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceId,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, { branch }, signal);\n }\n\n /**\n * Removes a relationship between two documents and waits for completion.\n */\n async removeRelationship(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<PHDocument> {\n this.logger.verbose(\n \"removeRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)\",\n sourceIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n );\n const sourceId = await this.resolveReference(\n sourceIdentifier,\n branch,\n signal,\n );\n const targetId = await this.resolveReference(\n targetIdentifier,\n branch,\n signal,\n );\n const jobInfo = await this.reactor.removeRelationship(\n sourceId,\n targetId,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const completedJob = await this.waitForJob(jobInfo, signal);\n\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n\n const result = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceId,\n { branch },\n completedJob.consistencyToken,\n signal,\n );\n return this.gateDocument(result, { branch }, signal);\n }\n\n /**\n * Moves a relationship from one source document to another and waits for completion.\n */\n async moveRelationship(\n sourceParentIdentifier: string,\n targetParentIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n branch: string = \"main\",\n signal?: AbortSignal,\n ): Promise<{\n source: PHDocument;\n target: PHDocument;\n }> {\n this.logger.verbose(\n \"moveRelationship(@sourceParentIdentifier, @targetParentIdentifier, @targetIdentifier, @relationshipType, @branch)\",\n sourceParentIdentifier,\n targetParentIdentifier,\n targetIdentifier,\n relationshipType,\n branch,\n );\n const sourceParentId = await this.resolveReference(\n sourceParentIdentifier,\n branch,\n signal,\n );\n const targetParentId = await this.resolveReference(\n targetParentIdentifier,\n branch,\n signal,\n );\n const targetId = await this.resolveReference(\n targetIdentifier,\n branch,\n signal,\n );\n\n // A move is a remove followed by an add, and the add would otherwise write a\n // fresh edge with no metadata. Read the edge first so the move carries it.\n // A failed read aborts the move: once the remove has run, \"the edge carried\n // no metadata\" and \"the read did not answer\" are indistinguishable, and\n // treating the second as the first rewrites the edge with no metadata for\n // good.\n const edge = await this.readRelationshipEdge(\n sourceParentId,\n targetId,\n relationshipType,\n { branch },\n signal,\n );\n const metadata = edge?.metadata;\n\n const removeJobInfo = await this.reactor.removeRelationship(\n sourceParentId,\n targetId,\n relationshipType,\n branch,\n this.signer,\n signal,\n );\n\n const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);\n\n if (removeCompletedJob.status === JobStatus.FAILED) {\n throw new Error(removeCompletedJob.error?.message);\n }\n\n const addJobInfo = await this.reactor.addRelationship(\n targetParentId,\n targetId,\n relationshipType,\n metadata,\n branch,\n this.signer,\n signal,\n );\n\n const addCompletedJob = await this.waitForJob(addJobInfo, signal);\n\n if (addCompletedJob.status === JobStatus.FAILED) {\n throw new Error(addCompletedJob.error?.message);\n }\n\n const sourceResult = await this.reactor.getByIdOrSlug<PHDocument>(\n sourceParentId,\n { branch },\n removeCompletedJob.consistencyToken,\n signal,\n );\n\n const targetResult = await this.reactor.getByIdOrSlug<PHDocument>(\n targetParentId,\n { branch },\n addCompletedJob.consistencyToken,\n signal,\n );\n\n return {\n source: await this.gateDocument(sourceResult, { branch }, signal),\n target: await this.gateDocument(targetResult, { branch }, signal),\n };\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n const result = await this.reactor.loadBatch(request, signal);\n\n const completedJobs = await Promise.all(\n Object.entries(result.jobs).map(async ([key, jobInfo]) => {\n const completed = await this.waitForJob(jobInfo, signal);\n return [key, completed] as const;\n }),\n );\n\n for (const [, completedJob] of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n\n return { jobs: Object.fromEntries(completedJobs) };\n }\n\n /**\n * Deletes a document and waits for completion\n */\n async deleteDocument(\n identifier: string,\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocument(@identifier, @propagate)\",\n identifier,\n propagate,\n );\n const rootId = await this.resolveReference(identifier, \"main\", signal);\n const toDelete = new Set([rootId]);\n\n if (propagate === PropagationMode.Cascade) {\n let changed = true;\n\n while (changed) {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n changed = false;\n const orphans = await this.documentIndexer.getOrphanedChildren(\n [...toDelete],\n [\"child\"],\n signal,\n );\n for (const id of orphans) {\n if (!toDelete.has(id)) {\n toDelete.add(id);\n changed = true;\n }\n }\n }\n }\n\n // Discovery puts sources before targets; reversed, children go first.\n const order = [...toDelete].reverse();\n const position = new Map(order.map((id, index) => [id, index]));\n const incoming = new Map<string, DocumentRelationship[]>();\n for (const documentId of order) {\n const page = await this.documentIndexer.getIncoming(\n documentId,\n undefined,\n undefined,\n undefined,\n signal,\n );\n incoming.set(documentId, page.results);\n }\n // An edge whose source is deleted no later than its target cannot wait.\n const waitsForTarget = (rel: DocumentRelationship): boolean => {\n const source = position.get(rel.sourceId);\n return source === undefined || source > position.get(rel.targetId)!;\n };\n const leadingBySource = new Map<string, DocumentRelationship[]>();\n for (const rels of incoming.values()) {\n for (const rel of rels) {\n if (waitsForTarget(rel)) continue;\n const leading = leadingBySource.get(rel.sourceId) ?? [];\n leading.push(rel);\n leadingBySource.set(rel.sourceId, leading);\n }\n }\n\n const plans: ExecutionJobPlan[] = [];\n for (const documentId of order) {\n for (const rel of leadingBySource.get(documentId) ?? []) {\n plans.push(await this.planRemoval(rel, [], plans.length, signal));\n }\n plans.push(await this.planDelete(documentId, signal));\n for (const rel of incoming.get(documentId)!) {\n if (waitsForTarget(rel)) {\n plans.push(\n await this.planRemoval(\n rel,\n [deletePlanKey(documentId)],\n plans.length,\n signal,\n ),\n );\n }\n }\n }\n\n const batchResult = await this.reactor.executeBatch(\n { jobs: plans },\n signal,\n );\n\n const completedJobs = await Promise.all(\n Object.values(batchResult.jobs).map((job) =>\n this.waitForJob(job, signal),\n ),\n );\n\n for (const completedJob of completedJobs) {\n if (completedJob.status === JobStatus.FAILED) {\n throw new Error(completedJob.error?.message);\n }\n }\n }\n\n /**\n * Deletes documents and waits for completion\n */\n async deleteDocuments(\n identifiers: string[],\n propagate?: PropagationMode,\n signal?: AbortSignal,\n ): Promise<void> {\n this.logger.verbose(\n \"deleteDocuments(@count identifiers, @propagate)\",\n identifiers.length,\n propagate,\n );\n const deletePromises = identifiers.map((identifier) =>\n this.deleteDocument(identifier, propagate, signal),\n );\n\n await Promise.all(deletePromises);\n }\n\n /**\n * Retrieves the status of a job\n */\n async getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n return this.reactor.getJobStatus(jobId, signal);\n }\n\n /**\n * Waits for a job to complete\n */\n async waitForJob(\n jobId: string | JobInfo,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n const id = typeof jobId === \"string\" ? jobId : jobId.id;\n this.logger.verbose(\"waitForJob(@id)\", id);\n return this.jobAwaiter.waitForJob(id, signal);\n }\n\n /**\n * Subscribes to changes for documents matching specified filters\n */\n subscribe(\n search: SearchFilter,\n callback: (event: DocumentChangeEvent) => void,\n view?: ViewFilter,\n ): () => void {\n this.logger.verbose(\"subscribe(@search, @view)\", search, view);\n\n // A subscription is a read, gated here because the subscription manager is\n // a read model, which sees everything. It is gated as a listing is: an\n // event for a document the subject can read no domain scope of is withheld.\n const served = async (\n type: DocumentChangeType,\n documents: PHDocument[],\n reads: EventReads,\n ): Promise<DocumentChangeEvent | undefined> => {\n const documentsServed = await this.gateServedAll(\n documents,\n view,\n undefined,\n reads,\n );\n return documentsServed.length > 0\n ? { type, documents: documentsServed }\n : undefined;\n };\n\n let disposed = false;\n let delivering: Promise<void> = Promise.resolve();\n\n /**\n * Queues one event behind those already queued. Gating an event resolves\n * asynchronously, so an event needing a group fetch would otherwise be\n * overtaken by the one behind it, and one still in flight would reach a\n * subscriber that has already unsubscribed. Only delivery is ordered, not\n * the gating: a single subscription can cover every document in the\n * reactor, so serializing that work would make its delivery rate the sum\n * of every gate build rather than the slowest. An event that cannot be\n * gated is withheld, because serving it unfiltered would leak the scopes\n * the gate did not clear -- but it is logged rather than swallowed, since\n * the gate rethrows a transient failure precisely so it is not read as a\n * denial. An event that resolves to undefined was withheld.\n */\n const deliver = (event: Promise<DocumentChangeEvent | undefined>): void => {\n // Attached now, or a rejection while the chain is parked on real I/O\n // waits a full turn with no handler, which Node reports as unhandled.\n void Promise.resolve(event).catch(() => undefined);\n\n delivering = delivering\n .then(async () => {\n const built = await event;\n if (disposed || !built) {\n return;\n }\n callback(built);\n })\n .catch((error: unknown) => {\n this.logger.error(\n \"Subscription delivery failed for @search: @Error\",\n { search },\n error,\n );\n });\n };\n\n const unsubscribeCreated = this.subscriptionManager.onDocumentCreated(\n (result) => {\n const reads = this.eventReads.forEvent();\n deliver(\n (async () => {\n // Unnarrowed: a narrowed fetch could omit the policy, which the\n // gate reads as uninitialized, or every domain scope.\n const documents = await Promise.all(\n result.results.map((id) => reads.get(id, withAllScopes(view))),\n );\n return served(DocumentChangeType.Created, documents, reads);\n })(),\n );\n },\n search,\n );\n\n const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted(\n (documentIds, info) => {\n const reads = this.eventReads.forEvent();\n const purged = info?.purged ? { purged: true as const } : {};\n for (const childId of documentIds) {\n deliver(\n (async () =>\n (await this.servesEvery([childId], view, reads))\n ? {\n type: DocumentChangeType.Deleted,\n documents: [],\n context: { childId, ...purged },\n }\n : undefined)(),\n );\n }\n },\n search,\n );\n\n const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated(\n (result) => {\n deliver(\n served(\n DocumentChangeType.Updated,\n result.results,\n this.eventReads.forEvent(),\n ),\n );\n },\n search,\n view,\n );\n\n const unsubscribeRelationship =\n this.subscriptionManager.onRelationshipChanged(\n (parentId, childId, changeType) => {\n const reads = this.eventReads.forEvent();\n deliver(\n (async () =>\n (await this.servesEvery([parentId, childId], view, reads))\n ? {\n type:\n changeType === RelationshipChangeType.Added\n ? DocumentChangeType.ChildAdded\n : DocumentChangeType.ChildRemoved,\n documents: [],\n context: { parentId, childId },\n }\n : undefined)(),\n );\n },\n search,\n );\n\n return () => {\n disposed = true;\n unsubscribeCreated();\n unsubscribeDeleted();\n unsubscribeUpdated();\n unsubscribeRelationship();\n };\n }\n\n private readSubject(subject?: AuthSubject): AuthSubject {\n return subject ?? authSubjectFromSigner(this.signer);\n }\n\n /**\n * Which scopes of one document the subject may read. Resolved once per\n * document, so the gate builds its model once however many scopes are then\n * tested, and the filtering itself stays synchronous.\n */\n private readableScopes(\n document: PHDocument,\n view?: ViewFilter,\n signal?: AbortSignal,\n reads?: EventReads,\n ): Promise<(scope: string) => boolean> {\n const subject = this.readSubject(view?.subject);\n const branch = view?.branch ?? \"main\";\n return reads\n ? reads.scopePredicate(document, subject, branch)\n : this.readGate.scopePredicate(document, subject, branch, signal);\n }\n\n /**\n * One document, filtered to the scopes the subject may read. Every method\n * that hands a document back goes through here, including the ones that\n * follow a write: a document returned from a mutation is a read like any\n * other, and returning it whole served scopes the same subject would be\n * refused by `get`. Its author still sees what it wrote, because an allow on\n * execute confers read of that scope.\n */\n private async gateDocument<TDocument extends PHDocument>(\n document: TDocument,\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<TDocument> {\n const readable = await this.readableScopes(document, view, signal);\n return filterReadableScopes(document, readable);\n }\n\n /**\n * One document as a listing or a feed serves it, or undefined when withheld.\n * The document must hold every scope, or withholding is decided on a subset;\n * a narrowing view is applied here instead.\n */\n private async gateServed<TDocument extends PHDocument>(\n document: TDocument,\n view: ViewFilter | undefined,\n signal?: AbortSignal,\n reads?: EventReads,\n ): Promise<TDocument | undefined> {\n const readable = await this.readableScopes(document, view, signal, reads);\n if (refusesEveryDomainScope(document, readable)) {\n return undefined;\n }\n const narrowed = narrowedScopes(view);\n return filterReadableScopes(\n document,\n narrowed ? (scope) => readable(scope) && narrowed.has(scope) : readable,\n );\n }\n\n private async gateServedAll(\n documents: PHDocument[],\n view: ViewFilter | undefined,\n signal?: AbortSignal,\n reads?: EventReads,\n ): Promise<PHDocument[]> {\n const served = await Promise.all(\n documents.map((document) =>\n this.gateServed(document, view, signal, reads),\n ),\n );\n return served.filter((document) => document !== undefined);\n }\n\n /**\n * Gates a page and every page after it. `nextCursor` is left as the stream\n * reported it.\n */\n private async gateListing(\n page: PagedResults<PHDocument>,\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<PagedResults<PHDocument>> {\n const listed = await Promise.all(\n page.results.map((document) => this.gateListed(document, view, signal)),\n );\n const results = listed.filter((document) => document !== undefined);\n const next = page.next;\n return {\n ...page,\n results,\n next: next\n ? async () => this.gateListing(await next(), view, signal)\n : undefined,\n };\n }\n\n /**\n * One document as a listing serves it, or undefined when withheld. It holds\n * only the view's scopes; a domain scope it lacks is decided on its name, or\n * on its state read now when a condition needs that.\n */\n private async gateListed(\n document: PHDocument,\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<PHDocument | undefined> {\n const readable = await this.readableScopes(document, view, signal);\n const unheld = unheldScopesReadOnState(document);\n const decided =\n unheld.length === 0\n ? readable\n : (scope: string) => !unheld.includes(scope) && readable(scope);\n if (\n refusesEveryDomainScope(document, decided) &&\n !(await this.servesUnheld(document, unheld, view, signal))\n ) {\n return undefined;\n }\n return filterReadableScopes(document, readable);\n }\n\n private async servesUnheld(\n document: PHDocument,\n scopes: string[],\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<boolean> {\n if (scopes.length === 0) {\n return false;\n }\n const id = document.header.id;\n let held: PHDocument;\n try {\n held = await this.reactor.get(\n id,\n withAuthScope({ ...view, scopes }),\n undefined,\n signal,\n );\n } catch (error) {\n await assertAbsent(this.documentView, id, error, signal);\n return false;\n }\n const readable = await this.readableScopes(held, view, signal);\n return scopes.some(readable);\n }\n\n /**\n * Whether an event naming only these ids may reach the subject. An id with no\n * live document behind it has no content left to protect, so it does not\n * withhold; a deleted document is read at its deletion boundary when\n * deletion is positional, and gated like any other.\n */\n private async servesEvery(\n ids: string[],\n view: ViewFilter | undefined,\n reads: EventReads,\n ): Promise<boolean> {\n const served = await Promise.all(\n ids.map((id) => this.servesId(id, view, reads)),\n );\n return served.every(Boolean);\n }\n\n private async servesId(\n id: string,\n view: ViewFilter | undefined,\n reads: EventReads,\n ): Promise<boolean> {\n let document: PHDocument;\n try {\n document = await reads.get(id, withAllScopes(view));\n } catch (error) {\n await assertAbsent(reads, id, error);\n return true;\n }\n return !refusesEveryDomainScope(\n document,\n await this.readableScopes(document, view, undefined, reads),\n );\n }\n\n private async getOperationsWithCompositeCursor(\n documentId: string,\n view: ViewFilter | undefined,\n filter: OperationFilter | undefined,\n paging: PagingOptions,\n signal: AbortSignal | undefined,\n canRead: (scope: string) => boolean,\n ): Promise<PagedResults<Operation>> {\n const scopeCursors = decodeCompositeCursor(paging.cursor);\n const allOperations: Operation[] = [];\n const activeCursors: Record<string, string> = {};\n\n for (const [scopeName, cursor] of Object.entries(scopeCursors)) {\n if (!canRead(scopeName)) {\n continue;\n }\n const scopeView: ViewFilter = { ...view, scopes: [scopeName] };\n const scopePaging: PagingOptions = { cursor, limit: paging.limit };\n\n const operationsByScope = await this.reactor.getOperations(\n documentId,\n scopeView,\n filter,\n scopePaging,\n undefined,\n signal,\n );\n\n const scopeResult = operationsByScope[scopeName];\n allOperations.push(...scopeResult.results);\n if (scopeResult.nextCursor) {\n activeCursors[scopeName] = scopeResult.nextCursor;\n }\n }\n\n allOperations.sort((a, b) => a.index - b.index);\n\n const nextCursor =\n Object.keys(activeCursors).length > 0\n ? encodeCompositeCursor(activeCursors)\n : undefined;\n\n return { results: allOperations, options: paging, nextCursor };\n }\n\n /**\n * Drops the edges whose far-end document the subject may read no domain scope\n * of. An edge is withheld whole rather than stripped of its metadata: the\n * document-shaped relationship reads already answer with the far end stripped\n * to the scopes the gate allows, so the far end's existence is disclosed\n * either way, but an edge's metadata is content about the pair that the far\n * end's own reads would refuse. An edge to a far end stripped to nothing\n * therefore carries content past a refusal, and there is no useful shell to\n * hand back in its place.\n *\n * `nextCursor` and `options` are left as the underlying stream reported them,\n * because a caller must feed them back to resume from the right position. A\n * gated page can therefore be shorter than the limit it asked for.\n */\n private async gateEdges(\n page: PagedResults<DocumentRelationship>,\n farEnd: \"sourceId\" | \"targetId\",\n view: ViewFilter | undefined,\n signal: AbortSignal | undefined,\n ): Promise<PagedResults<DocumentRelationship>> {\n const ids = [...new Set(page.results.map((edge) => edge[farEnd]))];\n if (ids.length === 0) {\n return page;\n }\n\n // No scopes: the gate must see every scope the document holds. Honouring a\n // caller-supplied narrowing would let `scopes: [\"auth\"]` leave a document\n // with no visible domain scope and switch the gate off. The explicit limit\n // keeps the tail of a large page from paging out of sight and reading as\n // absent.\n const farEndView: ViewFilter = {\n subject: view?.subject,\n branch: view?.branch,\n };\n const documents = await this.reactor.find(\n { ids },\n farEndView,\n { cursor: \"0\", limit: ids.length },\n undefined,\n signal,\n );\n\n const readable = new Map(\n await Promise.all(\n documents.results.map(async (doc) => {\n const allows = await this.readableScopes(doc, view, signal);\n return [\n doc.header.id,\n !refusesEveryDomainScope(doc, allows),\n ] as const;\n }),\n ),\n );\n\n // A far end `find` did not return is readable. `addRelationship` tolerates a\n // missing target, so a dangling edge is legitimate and failing closed would\n // hide it; and `find` never drops a document for authorization, so absence\n // here means genuinely absent and there is no content to protect.\n const results = page.results.filter(\n (edge) => readable.get(edge[farEnd]) !== false,\n );\n if (results.length === page.results.length) {\n return page;\n }\n\n const nextPage = page.next;\n return {\n ...page,\n results,\n next: nextPage\n ? async () => this.gateEdges(await nextPage(), farEnd, view, signal)\n : undefined,\n };\n }\n\n /**\n * One relationship edge, or undefined when it does not exist. A point lookup:\n * the pair is filtered in SQL rather than scanned out of the source's edge\n * list, which on a drive with thousands of children is the difference between\n * one query and dozens.\n */\n private async readRelationshipEdge(\n sourceIdentifier: string,\n targetIdentifier: string,\n relationshipType: string,\n view?: ViewFilter,\n signal?: AbortSignal,\n ): Promise<DocumentRelationship | undefined> {\n const sourceId = await this.documentView.resolveIdOrSlug(\n sourceIdentifier,\n view,\n undefined,\n signal,\n );\n const targetId = await this.documentView.resolveIdOrSlug(\n targetIdentifier,\n view,\n undefined,\n signal,\n );\n\n const edges = await this.documentIndexer.getDirectedRelationships(\n sourceId,\n targetId,\n [relationshipType],\n { cursor: \"0\", limit: 1 },\n undefined,\n signal,\n );\n\n return edges.results[0];\n }\n\n /**\n * The id a write on `identifier` is stored under, which its signatures bind.\n * A create names its own id.\n */\n private async resolveWriteTarget(\n identifier: string,\n branch: string,\n actions: readonly Action[],\n signal?: AbortSignal,\n ): Promise<string> {\n if (actions.some((action) => action.type === \"CREATE_DOCUMENT\")) {\n return identifier;\n }\n return this.resolveReference(identifier, branch, signal);\n }\n\n /**\n * The id a document reference names. An id no slug maps to is taken as\n * given, so a document still in flight resolves to itself; an id that is\n * another document's slug is refused as ambiguous.\n */\n private async resolveReference(\n identifier: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<string> {\n const view = { branch };\n const bySlug = await this.documentView.resolveSlug(\n identifier,\n view,\n undefined,\n signal,\n );\n if (bySlug === undefined || bySlug === identifier) {\n return identifier;\n }\n return this.documentView.resolveIdOrSlug(\n identifier,\n view,\n undefined,\n signal,\n );\n }\n\n /**\n * The decision model for one target document, built at its stream heads.\n *\n * The document is fetched unfiltered, because the policy is what decides:\n * reading it through the read gate would withhold the very scopes the\n * decision is about. A deleted document is served at its deletion boundary,\n * which is what lets the model refuse an execute against it -- authEnforcement\n * requires documentDecisions, so that read is available whenever this runs.\n *\n * Reading past the gate discloses nothing a submit does not. The `auth` and\n * `document` scopes are readable by every holder, so a verdict resting on the\n * policy alone is one the caller could compute unaided; and a verdict resting\n * on a conditional grant reads the executing scope's state exactly as\n * admission reads it, so the answer here is what submitting and being refused\n * would have revealed anyway.\n *\n * The append condition the build records is dropped. It guards a write, and\n * this makes none; reproducing it is also what the preflight cannot do, which\n * is why the answer is a prediction.\n */\n private async buildEvaluationTarget(\n config: ActionEvaluationConfig,\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<EvaluationTarget> {\n const document = await this.documentView.get(\n documentId,\n { branch },\n undefined,\n signal,\n );\n\n const target = { documentId, branch };\n const built = await buildDecisionModel(\n new SeededStateReader(this.documentView, document, branch),\n config.model,\n target,\n signal,\n );\n\n return {\n definition: config.model(target),\n model: built.model,\n scopeStates: (document.state ?? {}) as Record<string, unknown>,\n };\n }\n\n private async planDelete(\n documentId: string,\n signal?: AbortSignal,\n ): Promise<ExecutionJobPlan> {\n const signed = await signActions(\n [deleteDocumentAction(documentId)],\n this.signer,\n { documentId, branch: \"main\" },\n signal,\n );\n return {\n key: deletePlanKey(documentId),\n documentId,\n scope: \"document\",\n branch: \"main\",\n actions: signed,\n dependsOn: [],\n };\n }\n\n private async planRemoval(\n rel: DocumentRelationship,\n dependsOn: string[],\n index: number,\n signal?: AbortSignal,\n ): Promise<ExecutionJobPlan> {\n const signed = await signActions(\n [\n removeRelationshipAction(\n rel.sourceId,\n rel.targetId,\n rel.relationshipType,\n ),\n ],\n this.signer,\n { documentId: rel.sourceId, branch: \"main\" },\n signal,\n );\n return {\n key: `remove:${index}`,\n documentId: rel.sourceId,\n scope: \"document\",\n branch: \"main\",\n actions: signed,\n dependsOn,\n };\n }\n}\n","import type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport type { ErrorInfo, JobMeta } from \"../shared/types.js\";\n\n/**\n * What a job carries: a mutation applies actions, a load imports synced\n * operations, and a reevaluation re-judges a document's stored operations\n * because a read-set stream elsewhere (a group document) changed. A\n * reevaluation job carries no actions or operations; its work is derived\n * from what is already stored. A purge job erases a deleted document and\n * writes its PURGE_DOCUMENT marker; only DocumentPurgeService enqueues one.\n */\nexport type JobKind = \"mutation\" | \"load\" | \"reevaluation\" | \"purge\";\n\n/** What a purge job carries beyond its document id. */\nexport type PurgeJobOptions = {\n /** The erasure request that ordered the purge; the marker's requestId. */\n requestId: string;\n /** Lifts the maxPurgeOperations cap for this document. */\n allowLarge: boolean;\n};\n\n/**\n * State of a job in the queue\n */\nexport enum JobQueueState {\n UNKNOWN = -1,\n PREPROCESSING = 0,\n PENDING = 1,\n READY = 2,\n RUNNING = 3,\n RESOLVED = 4,\n}\n\n/**\n * How a retry is accounted against the job's retry limit.\n * - `CountAgainstLimit` (default): a fault; the job eventually exhausts its\n * retries and fails terminally.\n * - `ExemptFromLimit`: not a fault, so the attempt is not charged to the job.\n * Used for concurrency conflicts, where the retry does new work.\n */\nexport enum RetryAccounting {\n CountAgainstLimit = \"count-against-limit\",\n ExemptFromLimit = \"exempt-from-limit\",\n}\n\n/**\n * Interface for a job execution handle\n */\nexport interface IJobExecutionHandle {\n readonly job: Job;\n readonly state: JobQueueState;\n\n start(): void;\n complete(): void;\n fail(error: ErrorInfo): void;\n defer(): void;\n}\n\n/**\n * Represents a job to be executed by the job executor\n */\nexport type Job = {\n /** Unique identifier for the job */\n id: string;\n\n /** Classification of the job so executors can switch behavior */\n kind: JobKind;\n\n /** The document ID this job operates on */\n documentId: string;\n\n /** The scope of the operations */\n scope: string;\n\n /** The branch of the operations */\n branch: string;\n\n /** The actions to be executed (processed sequentially) */\n actions: Action[];\n\n /** Pre-existing operations to import (used for load jobs) */\n operations: Operation[];\n\n /** Timestamp when the job was created */\n createdAt: string;\n\n /** The hint for the queue to use for ordering the job */\n queueHint: string[];\n\n /** Number of retry attempts */\n retryCount?: number;\n\n /** Maximum number of retries allowed */\n maxRetries?: number;\n\n /** Last error if job failed */\n lastError?: ErrorInfo;\n\n /** History of all errors from each attempt (ordered) */\n errorHistory: ErrorInfo[];\n\n /** Metadata that flows through the job lifecycle */\n meta: JobMeta;\n\n /** Set on a purge job only. */\n purge?: PurgeJobOptions;\n};\n\n/**\n * Event types for the queue system\n */\nexport const QueueEventTypes = {\n JOB_AVAILABLE: 10000,\n} as const;\n\n/**\n * Minimal projection of a Job used by IQueue.dequeueNextMatching predicates.\n * Exposes only the routing-relevant fields so routing logic (e.g. hash(documentId) % numWorkers)\n * can live outside the queue without leaking the full Job.\n */\nexport type JobRoutingMeta = {\n documentId: string;\n scope: string;\n branch: string;\n};\n\n/**\n * Event data for job available events\n */\nexport type JobAvailableEvent = {\n documentId: string;\n scope: string;\n branch: string;\n jobId: string;\n};\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport { RetryAccounting } from \"../queue/types.js\";\nimport type { IJobExecutionHandle, Job } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { ModuleNotFoundError } from \"../registry/errors.js\";\nimport {\n AuthorizationDeniedError,\n AuthTimestampNotMonotonicError,\n DocumentDeletedError,\n DocumentNotDeletedError,\n DocumentNotFoundError,\n DocumentPurgedError,\n ExcessiveReshuffleError,\n GroupInUseError,\n InvalidOperationTimestampError,\n InvalidSignatureError,\n PurgeTooLargeError,\n ReservedActionError,\n UnsupportedProtocolVersionError,\n UpgradePreconditionFailedError,\n} from \"../shared/errors.js\";\nimport {\n AppendConditionFailedError,\n DocumentAlreadyExistsError,\n} from \"../storage/interfaces.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { JobResult } from \"./types.js\";\n\n/** Conflict retries a job may take without charging its retry limit. */\nconst MAX_EXEMPT_CONFLICT_RETRIES = 20;\n\nexport type JobResultCallbacks = {\n deferJob(documentId: string, job: Job): void;\n flushDeferredFor(documentId: string): Promise<void>;\n};\n\nexport interface IJobResultHandler {\n handleResult(\n handle: IJobExecutionHandle,\n result: JobResult,\n callbacks: JobResultCallbacks,\n ): Promise<void>;\n}\n\nexport function toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n const documentId = (error as { documentId?: unknown }).documentId;\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n ...(typeof documentId === \"string\" ? { documentId } : {}),\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n}\n\nexport class JobResultHandler implements IJobResultHandler {\n constructor(\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private eventBus: IEventBus,\n private resolver: IDocumentModelResolver,\n private logger: ILogger,\n ) {}\n\n async handleResult(\n handle: IJobExecutionHandle,\n result: JobResult,\n callbacks: JobResultCallbacks,\n ): Promise<void> {\n if (result.success) {\n handle.complete();\n\n if (this.hasCreateDocumentAction(handle.job)) {\n await callbacks.flushDeferredFor(handle.job.documentId);\n }\n return;\n }\n\n // Attempt model recovery before exhausting retries\n if (result.error && ModuleNotFoundError.isError(result.error)) {\n let modelLoaded = false;\n try {\n await this.resolver.ensureModelLoaded(result.error.documentType);\n modelLoaded = true;\n } catch {\n // Model could not be loaded, fall through to normal failure path\n }\n\n if (modelLoaded) {\n const errorInfo = toErrorInfo(result.error);\n try {\n await this.queue.retryJob(handle.job.id, errorInfo);\n return;\n } catch {\n // Fall through to normal failure path\n }\n }\n }\n\n // AppendConditionFailedError: a concurrency conflict, not a fault. Retry\n // exempt from the retry limit; the executor already dropped the stale\n // streams from the write cache.\n if (\n result.error &&\n AppendConditionFailedError.isError(result.error) &&\n this.countConflicts(handle.job) < MAX_EXEMPT_CONFLICT_RETRIES\n ) {\n const errorInfo = toErrorInfo(result.error);\n try {\n await this.queue.retryJob(\n handle.job.id,\n errorInfo,\n RetryAccounting.ExemptFromLimit,\n );\n return;\n } catch {\n // Fall through to normal failure path\n }\n }\n\n // DocumentNotFoundError on a load job: the operations came from a remote and\n // may have arrived ahead of the ones that create the document, so the job is\n // held for a CREATE_DOCUMENT that could still land. Held against the id the\n // error names, which is the document a flush has to create - not necessarily\n // the job's own, since a relationship reads its source.\n //\n // A mutation is not held. It names its document itself, and nothing in\n // flight will create one the caller got wrong, so holding it only delays the\n // refusal until the deferral deadline. It falls through to the deterministic\n // failures below.\n if (\n result.error &&\n DocumentNotFoundError.isError(result.error) &&\n !DocumentPurgedError.isError(result.error) &&\n handle.job.kind === \"load\"\n ) {\n handle.defer();\n callbacks.deferJob(result.error.documentId, handle.job);\n return;\n }\n\n if (\n result.error &&\n (DocumentDeletedError.isError(result.error) ||\n // The id is taken. A retry re-writes against the same stream and loses\n // the same way; only a new id resolves it.\n DocumentAlreadyExistsError.isError(result.error) ||\n // A document that is not there will not appear because a load ran again.\n DocumentNotFoundError.isError(result.error) ||\n AuthorizationDeniedError.isError(result.error) ||\n // All deterministic, so retrying only re-runs the load to fail the same.\n AuthTimestampNotMonotonicError.isError(result.error) ||\n InvalidOperationTimestampError.isError(result.error) ||\n ExcessiveReshuffleError.isError(result.error) ||\n // A refusal is a function of the write's content.\n InvalidSignatureError.isError(result.error) ||\n // The action's snapshot stays stale; the client retries with a fresh read.\n UpgradePreconditionFailedError.isError(result.error) ||\n UnsupportedProtocolVersionError.isError(result.error) ||\n DocumentPurgedError.isError(result.error) ||\n DocumentNotDeletedError.isError(result.error) ||\n GroupInUseError.isError(result.error) ||\n PurgeTooLargeError.isError(result.error) ||\n ReservedActionError.isError(result.error))\n ) {\n const errorInfo = toErrorInfo(result.error);\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error,\n job: handle.job,\n })\n .catch(() => {});\n handle.fail(errorInfo);\n return;\n }\n\n const retryCount = handle.job.retryCount || 0;\n const maxRetries = handle.job.maxRetries || 0;\n\n if (retryCount < maxRetries) {\n const currentErrorInfo = result.error\n ? toErrorInfo(result.error)\n : toErrorInfo(\"Unknown error\");\n\n try {\n await this.queue.retryJob(handle.job.id, currentErrorInfo);\n } catch (error) {\n const retryErrorInfo = toErrorInfo(\n error instanceof Error ? error : \"Failed to retry job\",\n );\n\n this.jobTracker.markFailed(handle.job.id, retryErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(retryErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(retryErrorInfo);\n }\n } else {\n const currentErrorInfo = result.error\n ? toErrorInfo(result.error)\n : toErrorInfo(\"Unknown error\");\n\n const fullErrorInfo = this.formatErrorHistory(\n handle.job.errorHistory,\n currentErrorInfo,\n retryCount + 1,\n );\n\n this.jobTracker.markFailed(handle.job.id, fullErrorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: result.error ?? new Error(fullErrorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n handle.fail(fullErrorInfo);\n }\n }\n\n /** How many times this job has already lost an append-condition race. */\n private countConflicts(job: Job): number {\n let conflicts = 0;\n for (const error of job.errorHistory) {\n if (AppendConditionFailedError.isFailureMessage(error.message)) {\n conflicts++;\n }\n }\n return conflicts;\n }\n\n private hasCreateDocumentAction(job: Job): boolean {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return true;\n }\n }\n return false;\n }\n\n private formatErrorHistory(\n errorHistory: ErrorInfo[],\n currentError: ErrorInfo,\n totalAttempts: number,\n ): ErrorInfo {\n const allErrors = [...errorHistory, currentError];\n\n if (allErrors.length === 1) {\n return currentError;\n }\n\n const messageLines = [`Job failed after ${totalAttempts} attempts:`];\n const stackLines: string[] = [];\n\n allErrors.forEach((error, index) => {\n messageLines.push(`[Attempt ${index + 1}] ${error.message}`);\n stackLines.push(`[Attempt ${index + 1}] Stack trace:\\n${error.stack}`);\n });\n\n return {\n // The attempt that ended the job is the one a consumer classifies by.\n name: currentError.name,\n message: messageLines.join(\"\\n\"),\n stack: stackLines.join(\"\\n\\n\"),\n };\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport { toErrorInfo } from \"./job-result-handler.js\";\nimport { DEFAULT_DEFERRED_JOB_TTL_MS } from \"./types.js\";\n\n/**\n * The jobs held back because the document they write to was not there yet.\n *\n * A job whose document is missing is deferred rather than failed, because the\n * operations that create that document may still be on their way - out of order\n * from a peer, or a sibling job in the same batch. Deferring emits no event and\n * moves no job status, which is what makes it cheap: a job that gets flushed\n * runs as if it had merely waited its turn.\n *\n * That silence is also why the wait has to be bounded. A deferred job is only\n * released when a job carrying CREATE_DOCUMENT for that exact document id\n * completes, so a job deferred against an id nothing will ever create is\n * released by nothing. Its status stays RUNNING, which is not terminal, so a\n * caller awaiting it waits forever - there is no timeout on that side. The TTL\n * converts that silence into the failure the caller can act on.\n *\n * Failing through the queue rather than only the tracker matters: the queue\n * records the job as resolved, which is what unblocks anything that declared a\n * dependency on it. Deferring alone does not, so a dependent of a deferred job\n * is stranded at PENDING for as long as the deferral lasts.\n */\nexport class DeferredJobs {\n #byDocumentId = new Map<string, Job[]>();\n #timers = new Map<string, ReturnType<typeof setTimeout>>();\n\n constructor(\n private readonly queue: IQueue,\n private readonly jobTracker: IJobTracker,\n private readonly eventBus: IEventBus,\n private readonly logger: ILogger,\n private readonly ttlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n /**\n * Called once an expiry has resolved a job in the queue. Resolving one job\n * can make a dependent runnable, and an expiry fires outside the dispatch\n * loop, so without this nudge the dependent waits for unrelated traffic to\n * wake the manager up.\n */\n private readonly onReleased: () => Promise<void> = () => Promise.resolve(),\n ) {}\n\n /**\n * Holds a job until its document arrives, or until the TTL elapses.\n *\n * A job deferred again after a flush replaces its own timer rather than\n * accumulating one, so the wait is measured from the most recent attempt.\n */\n add(documentId: string, job: Job): void {\n const existing = this.#byDocumentId.get(documentId) ?? [];\n existing.push(job);\n this.#byDocumentId.set(documentId, existing);\n\n this.#clearTimer(job.id);\n const timer = setTimeout(() => {\n void this.#expire(documentId, job);\n }, this.ttlMs);\n // Node keeps the process alive for a pending timer; a deferral must not.\n // Browsers hand back a number, which has nothing to unref.\n if (typeof timer === \"object\" && \"unref\" in timer) {\n timer.unref();\n }\n this.#timers.set(job.id, timer);\n }\n\n /** Re-enqueues everything waiting on a document that has now arrived. */\n async flush(documentId: string): Promise<void> {\n const jobs = this.#byDocumentId.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.#byDocumentId.delete(documentId);\n\n for (const job of jobs) {\n this.#clearTimer(job.id);\n try {\n await this.queue.enqueue(job);\n } catch (error) {\n this.logger.error(\"Error re-enqueuing deferred job: @Error\", error);\n }\n }\n }\n\n /**\n * Fails everything still deferred, for a reactor that is shutting down.\n *\n * The queue is deliberately not told: it is going away too, and nothing is\n * left to unblock.\n */\n failAll(): void {\n for (const [, jobs] of this.#byDocumentId) {\n for (const job of jobs) {\n this.#clearTimer(job.id);\n this.#markFailed(job);\n }\n }\n this.#byDocumentId.clear();\n }\n\n /** The jobs still waiting on a document. Exposed for assertions. */\n waitingOn(documentId: string): readonly Job[] {\n return this.#byDocumentId.get(documentId) ?? [];\n }\n\n async #expire(documentId: string, job: Job): Promise<void> {\n const jobs = this.#byDocumentId.get(documentId);\n if (!jobs?.includes(job)) {\n return;\n }\n const remaining = jobs.filter((held) => held !== job);\n if (remaining.length === 0) {\n this.#byDocumentId.delete(documentId);\n } else {\n this.#byDocumentId.set(documentId, remaining);\n }\n this.#timers.delete(job.id);\n\n this.logger.error(\n \"Deferred job @jobId gave up waiting for document @documentId after @ttlMs ms\",\n job.id,\n documentId,\n this.ttlMs,\n );\n\n const errorInfo = this.#markFailed(job);\n\n try {\n // Resolves the job in the queue, which is what releases its dependents.\n await this.queue.failJob(job.id, errorInfo);\n } catch (error) {\n this.logger.error(\"Error failing an expired deferred job: @Error\", error);\n return;\n }\n\n // Deferring dropped the job from the queue's index, so the JOB_FAILED the\n // queue just emitted carried no job with it, and the tracker's own handler\n // overwrote the one recorded above with nothing. Re-asserted so the failed\n // job keeps the record it is meant to be debuggable from.\n this.jobTracker.markFailed(job.id, errorInfo, job);\n\n try {\n await this.onReleased();\n } catch (error) {\n this.logger.error(\n \"Error dispatching after a deferred job expired: @Error\",\n error,\n );\n }\n }\n\n #markFailed(job: Job) {\n const error = new DocumentNotFoundError(job.documentId);\n const errorInfo = toErrorInfo(error);\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error,\n job,\n })\n .catch(() => {});\n return errorInfo;\n }\n\n #clearTimer(jobId: string): void {\n const timer = this.#timers.get(jobId);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.#timers.delete(jobId);\n }\n }\n}\n","/**\n * Sticky-by-document routing for the executor worker pool.\n *\n * Hashes `documentId` to a stable bucket so every job on a given document\n * lands on the same worker. The queue already enforces \"at most one job\n * executing per document at a time\", so a sticky worker always sees a fresh\n * post-commit snapshot before its next job on that document — which keeps\n * the per-worker `IWriteCache` and `IDocumentMetaCache` coherent for free.\n *\n * The hash is FNV-1a 32-bit: deterministic, dependency-free, and stable\n * across processes (so the same routing decision can be reproduced anywhere\n * the documentId is known).\n *\n * @see Executor Worker Pool Design wiki page\n * (Powerhouse board wiki id: d400d711-f07e-4389-a226-4e9fdd4fa8ba)\n */\n\nconst FNV_OFFSET_BASIS = 0x811c9dc5;\nconst FNV_PRIME = 0x01000193;\n\nexport function hashDocumentId(documentId: string): number {\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < documentId.length; i++) {\n hash ^= documentId.charCodeAt(i);\n hash = Math.imul(hash, FNV_PRIME);\n }\n return hash >>> 0;\n}\n\nexport function bucketFor(documentId: string, numWorkers: number): number {\n if (numWorkers < 1) {\n throw new Error(`bucketFor: numWorkers must be >= 1 (got ${numWorkers})`);\n }\n return hashDocumentId(documentId) % numWorkers;\n}\n","import { isPurgeMarker } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobRunningEvent,\n type JobWriteReadyEvent,\n} from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type {\n IJobExecutionHandle,\n Job,\n JobRoutingMeta,\n} from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type {\n IExecutorWorker,\n IJobExecutor,\n IJobExecutorManager,\n WorkerExecutionOutcome,\n} from \"./interfaces.js\";\nimport { DeferredJobs } from \"./deferred-jobs.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\n DEFAULT_DEFERRED_JOB_TTL_MS,\n JobExecutorEventTypes,\n type ExecutorManagerStatus,\n type JobCompletedEvent,\n type JobFailedEvent,\n type JobStartedEvent,\n} from \"./types.js\";\nimport {\n WorkerAbortTimeoutError,\n WorkerExitedError,\n WorkerInitFailedError,\n} from \"./worker/errors.js\";\nimport { submittedActionIds } from \"./util.js\";\nimport { bucketFor } from \"./worker-pool-router.js\";\nimport type {\n JobWriteReadyPayload,\n ModelManifestEntry,\n} from \"./worker/protocol.js\";\n\n/**\n * Factory invoked once per worker at `start()` time. The index is the\n * worker's position in the pool and the same value the manager will use\n * for sticky routing (`bucketFor(documentId) === index`).\n */\nexport type WorkerFactory = (index: number) => IExecutorWorker;\n\n/**\n * Manages a pool of executor workers and dispatches jobs across them with\n * sticky-by-documentId routing. Replaces `SimpleJobExecutorManager` when\n * the worker pool is enabled.\n *\n * Responsibilities that stay on the parent (not in the worker):\n * - Dequeueing from `IQueue` and routing to the matching worker bucket.\n * - Emitting `JOB_RUNNING` and `JOB_WRITE_READY` events; the worker's\n * local event bus is a no-op stub.\n * - Maintaining the deferred-jobs map for `DocumentNotFoundError`.\n * - Enriching the outgoing `JOB_WRITE_READY` with\n * `collectionMemberships`, read from the operation index after the\n * worker has committed. It takes the whole index rather than a\n * narrower read interface so that handing it a cache does not\n * compile: which documents a commit moves between collections is not\n * derivable from action shape, because joining a collection also\n * joins every group the joining document has referenced and that set\n * comes from selects run inside the commit. A parent-side cache\n * cannot learn it went stale, and a stale entry silently drops the\n * document's operations from the outbox of every remote subscribed to\n * the omitted collection. Costs one primary-key-prefix lookup on\n * `document_collections` per job that produced operations, on a\n * fire-and-forget path.\n *\n * @see Executor Worker Pool Design wiki page\n * (Powerhouse board wiki id: d400d711-f07e-4389-a226-4e9fdd4fa8ba)\n */\nexport class WorkerPoolJobExecutorManager implements IJobExecutorManager {\n private workers: IExecutorWorker[] = [];\n private isRunning = false;\n private activeJobs = 0;\n private totalJobsProcessed = 0;\n private unsubscribe?: () => void;\n private deferredJobs: DeferredJobs;\n private resultHandler: IJobResultHandler;\n private jobTimeoutMs: number;\n\n constructor(\n private workerFactory: WorkerFactory,\n private eventBus: IEventBus,\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private logger: ILogger,\n private resolver: IDocumentModelResolver,\n private operationIndex: IOperationIndex,\n jobTimeoutMs: number = 30_000,\n deferredJobTtlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\n this.deferredJobs = new DeferredJobs(\n queue,\n jobTracker,\n eventBus,\n logger,\n deferredJobTtlMs,\n () => this.tryDispatchAll(),\n );\n this.resultHandler = new JobResultHandler(\n queue,\n jobTracker,\n eventBus,\n resolver,\n logger,\n );\n }\n\n async start(numWorkers: number): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"WorkerPoolJobExecutorManager is already running\");\n }\n if (numWorkers < 1) {\n throw new Error(\"Number of workers must be at least 1\");\n }\n\n this.workers = Array.from({ length: numWorkers }, (_, i) =>\n this.workerFactory(i),\n );\n await Promise.all(this.workers.map((w) => w.start()));\n\n this.unsubscribe = this.eventBus.subscribe(\n QueueEventTypes.JOB_AVAILABLE,\n async () => {\n await this.tryDispatchAll();\n },\n );\n\n this.isRunning = true;\n await this.tryDispatchAll();\n }\n\n async stop(graceful = true): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n if (graceful) {\n while (this.activeJobs > 0) {\n await new Promise((resolve) => setTimeout(resolve, 10));\n }\n }\n\n this.deferredJobs.failAll();\n\n await Promise.all(\n this.workers.map((w) =>\n w.shutdown(graceful).catch((err: unknown) => {\n this.logger.warn(\"worker shutdown failed: @Error\", err);\n }),\n ),\n );\n\n this.workers = [];\n this.isRunning = false;\n }\n\n /**\n * Worker-pool mode has no in-process `IJobExecutor` instances — the\n * executors live in worker threads behind `IExecutorWorker` handles.\n * Returns an empty array; callers that need pool-aware introspection\n * should use `getStatus()` instead.\n */\n getExecutors(): IJobExecutor[] {\n return [];\n }\n\n /**\n * Broadcasts a `load-model` request to every running worker in parallel.\n * Rejects with the first worker's failure if any worker rejects (after\n * waiting for all in-flight broadcasts to settle). Workers that already\n * have the model registered respond with a `DuplicateModuleError`-rooted\n * failure; those are treated as success on the broadcast level so that\n * a model registered on some workers but not others still converges.\n */\n async loadModel(entry: ModelManifestEntry): Promise<void> {\n if (this.workers.length === 0) {\n return;\n }\n const results = await Promise.allSettled(\n this.workers.map((w) => w.loadModel(entry)),\n );\n const failures = results\n .filter((r): r is PromiseRejectedResult => r.status === \"rejected\")\n .filter((r) => !isDuplicateModuleFailure(r.reason));\n if (failures.length === 0) {\n return;\n }\n for (const f of failures) {\n this.logger.error(\n \"worker failed to load model @entry: @error\",\n entry,\n f.reason,\n );\n }\n throw failures[0].reason instanceof Error\n ? failures[0].reason\n : new Error(String(failures[0].reason));\n }\n\n getStatus(): ExecutorManagerStatus {\n return {\n isRunning: this.isRunning,\n numExecutors: this.workers.length,\n activeJobs: this.activeJobs,\n totalJobsProcessed: this.totalJobsProcessed,\n };\n }\n\n private async tryDispatchAll(): Promise<void> {\n if (!this.isRunning && this.workers.length === 0) {\n return;\n }\n await Promise.all(\n this.workers.map((worker) => this.tryDispatchFor(worker)),\n );\n }\n\n private async tryDispatchFor(worker: IExecutorWorker): Promise<void> {\n if (!worker.isIdle()) {\n return;\n }\n\n const index = worker.index;\n const numWorkers = this.workers.length;\n const predicate = (meta: JobRoutingMeta): boolean =>\n bucketFor(meta.documentId, numWorkers) === index;\n\n let handle: IJobExecutionHandle | null;\n try {\n handle = await this.queue.dequeueNextMatching(predicate);\n } catch (error) {\n this.logger.error(\"Error dequeueing next job: @Error\", error);\n return;\n }\n\n if (!handle) {\n return;\n }\n\n handle.start();\n this.activeJobs++;\n this.jobTracker.markRunning(handle.job.id);\n\n const runningEvent: JobRunningEvent = {\n jobId: handle.job.id,\n jobMeta: handle.job.meta,\n };\n this.eventBus\n .emit(ReactorEventTypes.JOB_RUNNING, runningEvent)\n .catch(() => {});\n\n const workerId = worker.workerId;\n const startedEvent: JobStartedEvent = {\n job: handle.job,\n startedAt: new Date().toISOString(),\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_STARTED, startedEvent)\n .catch(() => {});\n\n const signal = AbortSignal.timeout(this.jobTimeoutMs);\n let outcome: WorkerExecutionOutcome;\n try {\n outcome = await worker.execute(handle.job, signal);\n } catch (error) {\n const errorInfo = toErrorInfo(\n error instanceof Error ? error : String(error),\n );\n if (isWorkerTransportError(error)) {\n await this.handleWorkerTransportFailure(worker, handle.job, errorInfo);\n return;\n }\n handle.fail(errorInfo);\n this.activeJobs--;\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: new Error(errorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: errorInfo.message,\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n await this.tryDispatchFor(worker);\n return;\n }\n\n for (const refusal of outcome.signatureRefusals ?? []) {\n this.eventBus\n .emit(ReactorEventTypes.SIGNATURE_REFUSED, refusal)\n .catch(() => {});\n }\n\n if (outcome.result.success) {\n this.totalJobsProcessed++;\n const completedEvent: JobCompletedEvent = {\n job: handle.job,\n result: outcome.result,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_COMPLETED, completedEvent)\n .catch(() => {});\n } else {\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: outcome.result.error?.message ?? \"unknown\",\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n }\n\n if (outcome.result.success && outcome.writeReady) {\n this.evictPurged(outcome.writeReady);\n void this.emitWriteReady(handle.job, outcome.writeReady).catch(\n (error) => {\n this.logger.error(\n \"emitWriteReady failed for job @jobId: @Error\",\n { jobId: handle.job.id },\n error,\n );\n },\n );\n }\n\n await this.resultHandler.handleResult(handle, outcome.result, {\n deferJob: (documentId, job) => this.deferredJobs.add(documentId, job),\n flushDeferredFor: (documentId) => this.deferredJobs.flush(documentId),\n });\n\n this.activeJobs--;\n await this.tryDispatchFor(worker);\n }\n\n /** Every worker drops a committed purge's id; routing covers only its own. */\n private evictPurged(payload: JobWriteReadyPayload): void {\n const documentIds = [\n ...new Set(\n payload.operations\n .filter((operation) => isPurgeMarker(operation))\n .map((operation) => operation.context.documentId),\n ),\n ];\n if (documentIds.length === 0) {\n return;\n }\n for (const worker of this.workers) {\n worker.evictPurged(documentIds);\n }\n }\n\n private async emitWriteReady(\n job: Job,\n payload: JobWriteReadyPayload,\n ): Promise<void> {\n const documentIds = [\n ...new Set(payload.operations.map((op) => op.context.documentId)),\n ];\n let collectionMemberships: Record<string, string[]> = {};\n if (documentIds.length > 0) {\n try {\n const found =\n await this.operationIndex.getCollectionsForDocuments(documentIds);\n collectionMemberships = fillMissingMemberships(documentIds, found);\n } catch (error) {\n this.logger.error(\n \"Failed to load collection memberships for JOB_WRITE_READY: @Error\",\n error,\n );\n }\n }\n\n const event: JobWriteReadyEvent = {\n jobId: job.id,\n operations: payload.operations,\n jobMeta: payload.jobMeta,\n submittedActionIds: submittedActionIds(job),\n collectionMemberships,\n };\n try {\n await this.eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, event);\n } catch (error) {\n this.logger.error(\"Failed to emit JOB_WRITE_READY event: @Error\", error);\n }\n }\n\n /**\n * Handle a worker-transport failure (worker exited / init failed / abort\n * timed out) detected while `worker.execute` was in flight. Re-enqueues\n * the in-flight job via `queue.retryJob` so it is retried on a healthy\n * worker, then replaces the dead worker with a fresh handle and resumes\n * dispatch on the same bucket. Does NOT emit JOB_FAILED — the job is\n * not failed, only the worker is.\n */\n private async handleWorkerTransportFailure(\n dead: IExecutorWorker,\n job: Job,\n errorInfo: ReturnType<typeof toErrorInfo>,\n ): Promise<void> {\n this.logger.warn(\n \"worker transport error during execute; retrying job @jobId on a replacement worker: @error\",\n { jobId: job.id, workerId: dead.workerId },\n errorInfo.message,\n );\n\n this.activeJobs--;\n\n // Replace the dead worker BEFORE re-enqueuing the job. Otherwise\n // `queue.retryJob` emits JOB_AVAILABLE, the subscriber re-runs\n // `tryDispatchAll`, and the still-in-the-array dead worker picks up\n // the retried job — looping until heap exhaustion.\n await this.replaceWorker(dead);\n\n try {\n await this.queue.retryJob(job.id, errorInfo);\n } catch (error) {\n this.logger.error(\n \"failed to re-enqueue job after worker transport error: @Error\",\n error,\n );\n }\n }\n\n /**\n * Replace a dead worker at its existing pool index with a fresh handle\n * produced by `workerFactory`. Awaits `start()` on the replacement so it\n * is ready before dispatch resumes. On replacement failure the slot is\n * left empty (the index becomes a hole that subsequent retries will\n * route to no worker) and the error is logged — the manager keeps\n * running so other buckets continue to make progress.\n */\n private async replaceWorker(dead: IExecutorWorker): Promise<void> {\n const deadIndex = dead.index;\n if (this.workers[deadIndex] !== dead) {\n return;\n }\n\n let fresh: IExecutorWorker;\n try {\n fresh = this.workerFactory(deadIndex);\n } catch (error) {\n this.logger.error(\n \"workerFactory threw while replacing dead worker at index @index: @Error\",\n deadIndex,\n error,\n );\n return;\n }\n\n try {\n await fresh.start();\n } catch (error) {\n this.logger.error(\n \"replacement worker at index @index failed to start: @Error\",\n deadIndex,\n error,\n );\n return;\n }\n\n this.workers[deadIndex] = fresh;\n await this.tryDispatchFor(fresh);\n }\n}\n\nfunction isWorkerTransportError(error: unknown): boolean {\n return (\n error instanceof WorkerExitedError ||\n error instanceof WorkerInitFailedError ||\n error instanceof WorkerAbortTimeoutError\n );\n}\n\nfunction isDuplicateModuleFailure(reason: unknown): boolean {\n if (!(reason instanceof Error)) {\n return false;\n }\n if (reason.name === \"DuplicateModuleError\") {\n return true;\n }\n const cause = (reason as { cause?: unknown }).cause;\n return (\n cause instanceof Error && (cause as Error).name === \"DuplicateModuleError\"\n );\n}\n\n/**\n * Gives every requested document a key, empty when it belongs to no\n * collection. The operation index omits documents with no rows while the\n * membership cache defaulted them to `[]`, so this keeps the emitted\n * `JobWriteReadyEvent.collectionMemberships` shape unchanged.\n */\nfunction fillMissingMemberships(\n documentIds: string[],\n found: Record<string, string[]>,\n): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const documentId of documentIds) {\n result[documentId] = found[documentId] ?? [];\n }\n return result;\n}\n","import { v4 as uuidv4 } from \"uuid\";\nimport type { InProcessReactorModule } from \"../core/types.js\";\nimport { buildSingleJobMeta } from \"../core/utils.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobPendingEvent } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport { JobStatus, type JobInfo } from \"../shared/types.js\";\n\nexport { DEFAULT_MAX_PURGE_OPERATIONS } from \"../executor/types.js\";\n\nexport type EnqueuePurgeOptions = {\n /** Lifts the executor's maxPurgeOperations cap for every id. */\n allowLarge?: boolean;\n};\n\n/** The only local origin of a purge: one purge job per erased document. */\nexport class DocumentPurgeService {\n constructor(\n private readonly queue: IQueue,\n private readonly jobTracker: IJobTracker,\n private readonly eventBus: IEventBus,\n ) {}\n\n static fromModule(module: InProcessReactorModule): DocumentPurgeService {\n return new DocumentPurgeService(\n module.queue,\n module.jobTracker,\n module.eventBus,\n );\n }\n\n /** One purge job per id; resolves once enqueued, not once complete. */\n async enqueuePurge(\n ids: string[],\n requestId: string,\n opts: EnqueuePurgeOptions = {},\n ): Promise<JobInfo[]> {\n const requestDocumentIds = [...new Set(ids)];\n const infos: JobInfo[] = [];\n for (const documentId of requestDocumentIds) {\n const jobId = uuidv4();\n const createdAtUtcIso = new Date().toISOString();\n const meta = buildSingleJobMeta(jobId, {\n purgeRequestId: requestId,\n purgeRequestDocumentIds: requestDocumentIds,\n });\n const job: Job = {\n id: jobId,\n kind: \"purge\",\n documentId,\n scope: \"document\",\n branch: \"main\",\n actions: [],\n operations: [],\n createdAt: createdAtUtcIso,\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta,\n purge: { requestId, allowLarge: opts.allowLarge ?? false },\n };\n const info: JobInfo = {\n id: jobId,\n documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta,\n };\n this.jobTracker.registerJob(info);\n const pending: JobPendingEvent = { jobId, jobMeta: meta };\n this.eventBus.emit(ReactorEventTypes.JOB_PENDING, pending).catch(() => {\n // Ignored, as the reactor ignores it for every other job.\n });\n // enqueue settles only after the job runs; its synchronous part queues it.\n const dispatched = this.queue.enqueue(job);\n dispatched.catch(() => {\n // A dispatch failure surfaces on the job, as for every other job.\n });\n await Promise.race([dispatched, Promise.resolve()]);\n infos.push(info);\n }\n return infos;\n }\n}\n","import { sql, type Kysely } from \"kysely\";\n\ntype ProtocolVersions = { [protocol: string]: number };\n\nexport type StoredProtocolVersions = {\n hash: string;\n versions: ProtocolVersions;\n};\n\n// Reads idx_operation_created_protocol_versions; a marker at index 0 never counts.\nconst created = sql.raw(\n `scope = 'document' and \"index\" = 0 and (action->'input'->'protocolVersions') is not null and action->>'type' <> 'PURGE_DOCUMENT'`,\n);\nconst key = sql.raw(`md5((action->'input'->'protocolVersions')::text)`);\n\n/** Distinct creation protocolVersions, one index probe per value. */\nexport async function storedProtocolVersions(\n db: Kysely<any>,\n schema: string,\n): Promise<StoredProtocolVersions[]> {\n const operation = sql.id(schema, \"Operation\");\n const result = await sql<{ hash: string; versions: ProtocolVersions }>`\n with recursive hashes(hash) as (\n select (select ${key} from ${operation} where ${created} order by 1 limit 1)\n union all\n select (\n select ${key} from ${operation}\n where ${created} and ${key} > hashes.hash\n order by 1 limit 1\n )\n from hashes where hashes.hash is not null\n )\n select hash, (\n select action->'input'->'protocolVersions' from ${operation}\n where ${created} and ${key} = hashes.hash limit 1\n ) as versions\n from hashes where hash is not null\n `.execute(db);\n return result.rows;\n}\n\n/** Documents created with any of the protocolVersions `hashes` name. */\nexport async function countDocumentsCreatedWith(\n db: Kysely<any>,\n schema: string,\n hashes: readonly string[],\n): Promise<number> {\n if (hashes.length === 0) return 0;\n const operation = sql.id(schema, \"Operation\");\n const result = await sql<{ documents: string | number }>`\n select count(distinct \"documentId\") as documents from ${operation}\n where ${created} and ${key} in (${sql.join(hashes)})\n `.execute(db);\n return Number(result.rows[0]?.documents ?? 0);\n}\n","import type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { ProtocolSupport } from \"../executor/types.js\";\nimport { UnsupportedStoredProtocolError } from \"../shared/errors.js\";\nimport {\n countDocumentsCreatedWith,\n storedProtocolVersions,\n} from \"../storage/kysely/stored-protocol-versions.js\";\n\nexport type UnsupportedStoredDocuments = \"refuse\" | \"read-only\";\n\n/** Refuses to run below a version the store holds, unless told read-only. */\nexport async function checkStoredProtocols(\n db: Kysely<any>,\n schema: string,\n support: ProtocolSupport,\n mode: UnsupportedStoredDocuments,\n logger: ILogger,\n): Promise<void> {\n const unsupported = new Map<string, { protocol: string; version: number }>();\n const hashes: string[] = [];\n for (const { hash, versions } of await storedProtocolVersions(db, schema)) {\n let refused = false;\n for (const [protocol, version] of Object.entries(versions)) {\n const supported = support[protocol] as readonly number[] | undefined;\n if (supported === undefined || supported.includes(version)) continue;\n unsupported.set(`${protocol}:${version}`, { protocol, version });\n refused = true;\n }\n if (refused) hashes.push(hash);\n }\n if (hashes.length === 0) return;\n\n const error = new UnsupportedStoredProtocolError(\n [...unsupported.values()],\n await countDocumentsCreatedWith(db, schema, hashes),\n );\n if (mode === \"refuse\") throw error;\n logger.warn(\"Starting with documents read-only: @message\", error.message);\n}\n","import type { ILogger } from \"document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobRunningEvent } from \"../events/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { IJobExecutionHandle } from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type { IJobExecutor, IJobExecutorManager } from \"./interfaces.js\";\nimport { DeferredJobs } from \"./deferred-jobs.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\n DEFAULT_DEFERRED_JOB_TTL_MS,\n JobExecutorEventTypes,\n type ExecutorManagerStatus,\n type JobCompletedEvent,\n type JobFailedEvent,\n type JobResult,\n type JobStartedEvent,\n} from \"./types.js\";\n\nexport type JobExecutorFactory = () => IJobExecutor;\n\n/**\n * Manages multiple job executors and coordinates job distribution.\n * Listens for job available events and dispatches jobs to executors.\n */\nexport class SimpleJobExecutorManager implements IJobExecutorManager {\n private executors: IJobExecutor[] = [];\n private isRunning = false;\n private activeJobs = 0;\n private totalJobsProcessed = 0;\n private unsubscribe?: () => void;\n private deferredJobs: DeferredJobs;\n private resultHandler: IJobResultHandler;\n\n private jobTimeoutMs: number;\n\n constructor(\n private executorFactory: JobExecutorFactory,\n private eventBus: IEventBus,\n private queue: IQueue,\n private jobTracker: IJobTracker,\n private logger: ILogger,\n private resolver: IDocumentModelResolver,\n jobTimeoutMs: number = 30_000,\n deferredJobTtlMs: number = DEFAULT_DEFERRED_JOB_TTL_MS,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\n this.deferredJobs = new DeferredJobs(\n queue,\n jobTracker,\n eventBus,\n logger,\n deferredJobTtlMs,\n () => this.checkForMoreJobs(),\n );\n this.resultHandler = new JobResultHandler(\n queue,\n jobTracker,\n eventBus,\n resolver,\n logger,\n );\n }\n\n async start(numExecutors: number): Promise<void> {\n if (this.isRunning) {\n throw new Error(\"JobExecutorManager is already running\");\n }\n\n if (numExecutors < 1) {\n throw new Error(\"Number of executors must be at least 1\");\n }\n\n // Create executors\n this.executors = [];\n for (let i = 0; i < numExecutors; i++) {\n this.executors.push(this.executorFactory());\n }\n\n // Start listening for job available events\n this.unsubscribe = this.eventBus.subscribe(\n QueueEventTypes.JOB_AVAILABLE,\n async () => {\n // Only process if we have capacity (simple round-robin for now)\n if (this.activeJobs < this.executors.length) {\n await this.processNextJob();\n }\n },\n );\n\n this.isRunning = true;\n\n // Process any existing jobs in the queue\n await this.processExistingJobs();\n }\n\n async stop(graceful = true): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n // Stop listening for new jobs\n if (this.unsubscribe) {\n this.unsubscribe();\n this.unsubscribe = undefined;\n }\n\n if (graceful) {\n // Wait for active jobs to complete\n while (this.activeJobs > 0) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n\n // Fail any deferred jobs that were never flushed\n this.deferredJobs.failAll();\n\n this.executors = [];\n this.isRunning = false;\n }\n\n getExecutors(): IJobExecutor[] {\n return [...this.executors];\n }\n\n getStatus(): ExecutorManagerStatus {\n return {\n isRunning: this.isRunning,\n numExecutors: this.executors.length,\n activeJobs: this.activeJobs,\n totalJobsProcessed: this.totalJobsProcessed,\n };\n }\n\n private async processNextJob(): Promise<void> {\n // dequeue next available job\n let handle: IJobExecutionHandle | null;\n try {\n handle = await this.queue.dequeueNext();\n } catch (error) {\n this.logger.error(\"Error dequeueing next job: @Error\", error);\n return;\n }\n\n if (!handle) {\n return;\n }\n\n // start the job execution\n handle.start();\n this.activeJobs++;\n this.jobTracker.markRunning(handle.job.id);\n\n // Emit JOB_RUNNING event\n const runningEvent: JobRunningEvent = {\n jobId: handle.job.id,\n jobMeta: handle.job.meta,\n };\n this.eventBus\n .emit(ReactorEventTypes.JOB_RUNNING, runningEvent)\n .catch(() => {\n // Ignore event emission errors\n });\n\n // Find an available executor (simple round-robin)\n const executorIndex = this.totalJobsProcessed % this.executors.length;\n const executor = this.executors[executorIndex];\n const workerId = `in-process-${executorIndex}`;\n\n const startedEvent: JobStartedEvent = {\n job: handle.job,\n startedAt: new Date().toISOString(),\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_STARTED, startedEvent)\n .catch(() => {});\n\n // execute the job with a timeout signal; race ensures the timeout fires\n // even if the executor hangs on a call that does not check the signal\n const signal = AbortSignal.timeout(this.jobTimeoutMs);\n const toError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new Error(String(reason));\n const abortPromise = new Promise<never>((_, reject) => {\n if (signal.aborted) {\n reject(toError(signal.reason));\n return;\n }\n signal.addEventListener(\"abort\", () => reject(toError(signal.reason)), {\n once: true,\n });\n });\n let result: JobResult;\n try {\n result = await Promise.race([\n executor.executeJob(handle.job, signal),\n abortPromise,\n ]);\n } catch (error) {\n const errorInfo = toErrorInfo(\n error instanceof Error ? error : String(error),\n );\n\n handle.fail(errorInfo);\n this.activeJobs--;\n this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);\n\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: handle.job.id,\n error: new Error(errorInfo.message),\n job: handle.job,\n })\n .catch(() => {});\n\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: errorInfo.message,\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n\n await this.checkForMoreJobs();\n return;\n }\n\n // handle the result\n if (result.success) {\n this.totalJobsProcessed++;\n }\n\n if (result.success) {\n const completedEvent: JobCompletedEvent = {\n job: handle.job,\n result,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_COMPLETED, completedEvent)\n .catch(() => {});\n } else {\n const failedEvent: JobFailedEvent = {\n job: handle.job,\n error: result.error?.message ?? \"unknown\",\n willRetry: false,\n retryCount: 0,\n workerId,\n };\n this.eventBus\n .emit(JobExecutorEventTypes.JOB_FAILED, failedEvent)\n .catch(() => {});\n }\n\n await this.resultHandler.handleResult(handle, result, {\n deferJob: (documentId, job) => this.deferredJobs.add(documentId, job),\n flushDeferredFor: (documentId) => this.deferredJobs.flush(documentId),\n });\n\n this.activeJobs--;\n await this.checkForMoreJobs();\n }\n\n private async checkForMoreJobs(): Promise<void> {\n if (!this.isRunning) {\n return;\n }\n\n let hasMore: boolean;\n try {\n hasMore = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for more jobs: @Error\", error);\n return;\n }\n\n if (hasMore) {\n await this.processNextJob();\n }\n }\n\n private async processExistingJobs(): Promise<void> {\n let hasJobs: boolean;\n try {\n hasJobs = await this.queue.hasJobs();\n } catch (error) {\n this.logger.error(\"Error checking for existing jobs: @Error\", error);\n return;\n }\n\n if (hasJobs) {\n // Start processing up to the number of executors\n const promises: Promise<void>[] = [];\n for (let i = 0; i < Math.min(this.executors.length, 5); i++) {\n promises.push(this.processNextJob());\n }\n\n try {\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error processing existing jobs: @Error\", error);\n }\n }\n }\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport {\n createConsistencyToken,\n createEmptyConsistencyToken,\n summarizeSubmittedActions,\n} from \"../executor/util.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport { JobStatus, type JobInfo } from \"../shared/types.js\";\nimport type { IJobTracker } from \"./interfaces.js\";\n\n/**\n * In-memory implementation of IJobTracker.\n * Maintains job status in a Map for synchronous access.\n * Subscribes to operation events to update job states.\n */\nexport class InMemoryJobTracker implements IJobTracker {\n private jobs = new Map<string, JobInfo>();\n private unsubscribers: Unsubscribe[] = [];\n\n constructor(private eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_type, event: JobWriteReadyEvent) => {\n this.handleWriteReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n (_type, event: JobReadReadyEvent) => {\n this.handleReadReady(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.handleJobFailed(event);\n },\n ),\n );\n }\n\n private handleWriteReady(event: JobWriteReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.RUNNING) {\n const consistencyToken = createConsistencyToken(event.operations);\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.WRITE_READY,\n consistencyToken,\n // A reducer error or a denial leaves the job on its way to READ_READY,\n // so the status alone cannot tell the caller one of its actions was\n // rejected. This is where it finds out.\n result: summarizeSubmittedActions(\n event.operations,\n event.submittedActionIds,\n ),\n });\n }\n }\n\n private handleReadReady(event: JobReadReadyEvent): void {\n const jobId = event.jobId;\n const job = this.jobs.get(jobId);\n if (job && job.status === JobStatus.WRITE_READY) {\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.READ_READY,\n });\n }\n }\n\n private handleJobFailed(event: JobFailedEvent): void {\n this.markFailed(\n event.jobId,\n {\n name: event.error.name,\n message: event.error.message,\n stack: event.error.stack || \"\",\n },\n event.job,\n );\n }\n\n shutdown(): void {\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers = [];\n }\n\n registerJob(jobInfo: JobInfo): void {\n this.jobs.set(jobInfo.id, { ...jobInfo });\n }\n\n markRunning(jobId: string): void {\n const job = this.jobs.get(jobId);\n if (!job) {\n // Job not found - might have been registered elsewhere\n // Create minimal job entry\n this.jobs.set(jobId, {\n id: jobId,\n documentId: \"\",\n status: JobStatus.RUNNING,\n createdAtUtcIso: new Date().toISOString(),\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n this.jobs.set(jobId, {\n ...job,\n status: JobStatus.RUNNING,\n });\n }\n\n markFailed(jobId: string, error: ErrorInfo, job?: Job): void {\n const existing = this.jobs.get(jobId);\n if (!existing) {\n this.jobs.set(jobId, {\n id: jobId,\n documentId: job?.documentId ?? \"\",\n status: JobStatus.FAILED,\n createdAtUtcIso: new Date().toISOString(),\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n this.jobs.set(jobId, {\n ...existing,\n status: JobStatus.FAILED,\n completedAtUtcIso: new Date().toISOString(),\n error,\n job,\n consistencyToken: createEmptyConsistencyToken(),\n });\n }\n\n getJobStatus(jobId: string): JobInfo | null {\n const job = this.jobs.get(jobId);\n return job ? { ...job } : null;\n }\n}\n","import type { Generated, Insertable, Selectable, Updateable } from \"kysely\";\n\nexport interface ViewStateTable {\n readModelId: string;\n lastOrdinal: number;\n lastOperationTimestamp: Generated<Date>;\n}\n\nexport interface DocumentSnapshotTable {\n id: Generated<string>;\n documentId: string;\n slug: string | null;\n name: string | null;\n scope: string;\n branch: string;\n content: unknown; // JSONB type - stored as object\n documentType: string;\n lastOperationIndex: number;\n lastOperationHash: string;\n lastOperationOrdinal: Generated<number>;\n lastUpdatedAt: Generated<Date>;\n snapshotVersion: Generated<number>;\n identifiers: unknown; // JSONB type - stored as object\n metadata: unknown; // JSONB type - stored as object\n isDeleted: Generated<boolean>;\n deletedAt: Date | null;\n}\n\nexport interface SlugMappingTable {\n slug: string;\n documentId: string;\n scope: string;\n branch: string;\n createdAt: Generated<Date>;\n updatedAt: Generated<Date>;\n}\n\n/** A cursor row whose factory is unregistered; it only records a drive's data. */\nexport const RELEASED_CURSOR_STATUS = \"released\";\n\nexport interface ProcessorCursorTable {\n processorId: string;\n factoryId: string;\n driveId: string;\n processorIndex: number;\n lastOrdinal: Generated<number>;\n status: Generated<string>;\n lastError: string | null;\n lastErrorTimestamp: Date | null;\n createdAt: Generated<Date>;\n updatedAt: Generated<Date>;\n}\n\nexport interface DocumentViewDatabase {\n ViewState: ViewStateTable;\n DocumentSnapshot: DocumentSnapshotTable;\n SlugMapping: SlugMappingTable;\n ProcessorCursor: ProcessorCursorTable;\n}\n\nexport type ViewStateRow = Selectable<ViewStateTable>;\nexport type InsertableViewState = Insertable<ViewStateTable>;\nexport type UpdateableViewState = Updateable<ViewStateTable>;\n\nexport type DocumentSnapshotRow = Selectable<DocumentSnapshotTable>;\nexport type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;\nexport type UpdateableDocumentSnapshot = Updateable<DocumentSnapshotTable>;\n\nexport type SlugMappingRow = Selectable<SlugMappingTable>;\nexport type InsertableSlugMapping = Insertable<SlugMappingTable>;\nexport type UpdateableSlugMapping = Updateable<SlugMappingTable>;\n\nexport type ProcessorCursorRow = Selectable<ProcessorCursorTable>;\nexport type InsertableProcessorCursor = Insertable<ProcessorCursorTable>;\nexport type UpdateableProcessorCursor = Updateable<ProcessorCursorTable>;\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type {\n ProcessorFilter,\n ProcessorRecord,\n} from \"@powerhousedao/shared/processors\";\nimport type {\n CreateDocumentAction,\n PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport { createDocumentFromAction } from \"../executor/util.js\";\n\n// The marker counts: a peer can receive a drive's marker without its delete.\nexport function isDriveDeletion(op: OperationWithContext): boolean {\n const type = op.operation.action.type;\n return type === \"DELETE_DOCUMENT\" || type === \"PURGE_DOCUMENT\";\n}\n\nexport function extractDriveHeader(\n op: OperationWithContext,\n): PHDocumentHeader | undefined {\n if (!op.context.resultingState) return undefined;\n\n const state = JSON.parse(op.context.resultingState) as Record<\n string,\n unknown\n >;\n return state.header as PHDocumentHeader | undefined;\n}\n\n// The index keeps no resultingState, so its rows rebuild it from the input.\nexport function extractCreationHeader(\n op: OperationWithContext,\n): PHDocumentHeader | undefined {\n if (op.operation.action.type !== \"CREATE_DOCUMENT\") return undefined;\n return (\n extractDriveHeader(op) ??\n createDocumentFromAction(op.operation.action as CreateDocumentAction).header\n );\n}\n\nexport function extractDeletedDocumentId(\n op: OperationWithContext,\n): string | undefined {\n const input = op.operation.action.input as { documentId?: string };\n return input.documentId ?? op.context.documentId;\n}\n\nexport function createMinimalDriveHeader(\n driveId: string,\n documentType: string,\n): PHDocumentHeader {\n return {\n id: driveId,\n documentType,\n sig: {\n publicKey: {},\n nonce: \"\",\n },\n slug: \"\",\n name: \"\",\n branch: \"main\",\n revision: {},\n createdAtUtcIso: new Date().toISOString(),\n lastModifiedAtUtcIso: new Date().toISOString(),\n };\n}\n\nexport function matchesFilter(\n op: OperationWithContext,\n filter: ProcessorFilter,\n): boolean {\n if (filter.documentType && filter.documentType.length > 0) {\n if (!filter.documentType.includes(op.context.documentType)) {\n return false;\n }\n }\n\n if (filter.scope && filter.scope.length > 0) {\n if (!filter.scope.includes(op.context.scope)) {\n return false;\n }\n }\n\n if (filter.branch && filter.branch.length > 0) {\n if (!filter.branch.includes(op.context.branch)) {\n return false;\n }\n }\n\n if (filter.documentId && filter.documentId.length > 0) {\n const hasWildcard = filter.documentId.includes(\"*\");\n if (!hasWildcard && !filter.documentId.includes(op.context.documentId)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction nonEmpty(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction namespaceOf(processor: object): string | undefined {\n return nonEmpty((processor as { namespace?: unknown }).namespace);\n}\n\nfunction classNameOf(processor: object): string | undefined {\n const name = processor.constructor.name;\n return name && name !== \"Object\" ? name : undefined;\n}\n\n// Cursor key of each record within its factory and drive. Legacy mode keeps\n// the array index; otherwise id, then namespace, then class name, then index.\nexport function resolveProcessorSlots(\n records: ProcessorRecord[],\n legacy: boolean,\n): string[] {\n if (legacy) return records.map((_, i) => String(i));\n\n const seen = new Map<string, number>();\n return records.map((record, i) => {\n const base =\n nonEmpty(record.id) ??\n namespaceOf(record.processor) ??\n classNameOf(record.processor) ??\n String(i);\n const count = seen.get(base) ?? 0;\n seen.set(base, count + 1);\n return count === 0 ? base : `${base}#${count}`;\n });\n}\n","import {\n isPurgeMarker,\n type OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type {\n IProcessor,\n ProcessorFilter,\n TrackedProcessor,\n} from \"@powerhousedao/shared/processors\";\nimport type { ILogger } from \"document-model\";\nimport type { PagedResults } from \"../shared/types.js\";\nimport { matchesFilter } from \"./utils.js\";\n\nexport type ProcessorCursorState = Pick<\n TrackedProcessor,\n \"lastOrdinal\" | \"status\" | \"lastError\" | \"lastErrorTimestamp\"\n>;\n\nexport type ProcessorQueueOptions = {\n processorId: string;\n processor: IProcessor;\n filter: ProcessorFilter;\n /** Mutated in place: the tracked processor the manager exposes. */\n cursor: ProcessorCursorState;\n /** Ordinals at or below this are never delivered. */\n floor: number;\n readSince: (ordinal: number) => Promise<PagedResults<OperationWithContext>>;\n /** Highest ordinal routed so far; anything above it has yet to arrive live. */\n routedThrough: () => number;\n /** The manager's appliedThrough; the cursor never passes it. */\n confirmedThrough: () => number;\n persist: (cursor: ProcessorCursorState) => Promise<void>;\n /** Tombstoned ids among `ids`; takes no purge lock. */\n purged: (ids: string[]) => Promise<ReadonlySet<string>>;\n /** First wait before retrying a failed tombstone lookup; doubles per try. */\n lookupRetryMs?: number;\n /** Retries of a failed tombstone lookup before the processor parks. */\n lookupRetries?: number;\n logger: ILogger;\n};\n\n/** Tombstoned ids a live batch was checked against, as routing started it. */\nexport type LiveCheck = {\n purged: Promise<ReadonlySet<string>>;\n /** Ordinals delivered even when tombstoned. */\n keep?: ReadonlySet<number>;\n};\n\ntype LiveDelivery = {\n kind: \"live\";\n ops: OperationWithContext[];\n check: LiveCheck | undefined;\n done: () => void;\n};\n\ntype Delivery =\n | LiveDelivery\n | { kind: \"advance\"; through: number; done: () => void };\n\ntype EraseTask = {\n kind: \"erase\";\n ops: OperationWithContext[];\n deletion: OperationWithContext | undefined;\n check: LiveCheck;\n run: () => Promise<void>;\n done: () => void;\n};\n\ntype Task =\n | Delivery\n | EraseTask\n | {\n kind: \"backfill\" | \"retry\" | \"disconnect\";\n run: () => Promise<void>;\n done: () => void;\n };\n\nfunction isDelivery(task: Task | undefined): task is Delivery {\n return task?.kind === \"live\" || task?.kind === \"advance\";\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction lowestOf(ops: OperationWithContext[]): number {\n let lowest = ops[0]!.context.ordinal;\n for (const op of ops) lowest = Math.min(lowest, op.context.ordinal);\n return lowest;\n}\n\n/** Drops operations of tombstoned ids; the marker always passes. */\nexport function withoutPurged(\n ops: OperationWithContext[],\n purged: ReadonlySet<string>,\n keep?: ReadonlySet<number>,\n): OperationWithContext[] {\n if (purged.size === 0) return ops;\n return ops.filter(\n (op) =>\n isPurgeMarker(op.operation) ||\n !purged.has(op.context.documentId) ||\n keep?.has(op.context.ordinal) === true,\n );\n}\n\n/** The ids whose tombstone would drop an operation of `ops`. */\nexport function purgeCandidates(ops: OperationWithContext[]): string[] {\n const ids = new Set<string>();\n for (const op of ops) {\n if (!isPurgeMarker(op.operation)) ids.add(op.context.documentId);\n }\n return [...ids];\n}\n\nfunction highestOf(ops: OperationWithContext[]): number {\n let highest = 0;\n for (const op of ops) highest = Math.max(highest, op.context.ordinal);\n return highest;\n}\n\n/** Largest merged live call, matching the operation index's page size. */\nexport const MAX_MERGED_OPERATIONS = 500;\n\nconst LOOKUP_RETRY_MS = 100;\nconst MAX_LOOKUP_RETRY_MS = 30_000;\nconst LOOKUP_RETRIES = 10;\n\n// One task at a time per processor; task promises never reject.\nexport class ProcessorQueue {\n private readonly tasks: Task[] = [];\n private running = false;\n private closed = false;\n private replaysAhead = 0;\n // Delivered by backfill before routing reached them; dropped once, live.\n private readonly unrouted = new Set<number>();\n // Ends a lookup retry's wait early; set while one waits.\n private wake: (() => void) | undefined;\n // Set once a lookup exhausts its retries; nothing moves until one answers.\n private parked = false;\n\n constructor(private readonly options: ProcessorQueueOptions) {}\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n /** True while a backfill or retry is queued or running. */\n get replaying(): boolean {\n return this.replaysAhead > 0;\n }\n\n /** Resolves at once behind a replay, so a pass never waits out a backfill. */\n live(ops: OperationWithContext[], check?: LiveCheck): Promise<void> {\n const delivered = this.push((done) => ({ kind: \"live\", ops, check, done }));\n return this.replaysAhead > 0 ? Promise.resolve() : delivered;\n }\n\n /** Raises the cursor past a batch with nothing for this processor. */\n advance(through: number): Promise<void> {\n const last = this.tasks.at(-1);\n if (last?.kind === \"advance\") {\n last.through = Math.max(last.through, through);\n return Promise.resolve();\n }\n return this.push((done) => ({ kind: \"advance\", through, done }));\n }\n\n /** `share` if active, then `deletion` whatever the status; true once delivered. */\n erase(\n share: OperationWithContext[],\n deletion: OperationWithContext,\n check: LiveCheck,\n ): Promise<boolean> {\n if (this.closed) return Promise.resolve(false);\n let delivered = false;\n const task: Omit<EraseTask, \"done\"> = {\n kind: \"erase\",\n ops: share,\n deletion,\n check,\n run: async () => {\n delivered = await this.runErase(task);\n },\n };\n return this.push((done) => Object.assign(task, { done })).then(\n () => delivered,\n );\n }\n\n /** Replays from the cursor as it stands when the task runs. */\n backfill(): Promise<void> {\n return this.replay(\"backfill\", () => this.runBackfill());\n }\n\n /** Clears an error and replays from the cursor. No-op when active. */\n retry(): Promise<void> {\n return this.replay(\"retry\", async () => {\n const { cursor } = this.options;\n if (cursor.status !== \"errored\") return;\n cursor.status = \"active\";\n cursor.lastError = undefined;\n cursor.lastErrorTimestamp = undefined;\n await this.persist();\n await this.runBackfill();\n });\n }\n\n /** Stops cursor writes now; queued tasks still run before the disconnect. */\n close(): Promise<void> {\n if (this.closed) return Promise.resolve();\n const disconnect = this.enqueue(\"disconnect\", async () => {\n try {\n await this.options.processor.onDisconnect();\n } catch (error) {\n this.options.logger.error(\n \"Error disconnecting processor '@ProcessorId': @Error\",\n this.options.processorId,\n error,\n );\n }\n });\n this.closed = true;\n this.wake?.();\n return disconnect;\n }\n\n private replay(\n kind: \"backfill\" | \"retry\",\n run: () => Promise<void>,\n ): Promise<void> {\n if (this.closed) return Promise.resolve();\n this.replaysAhead++;\n return this.enqueue(kind, async () => {\n try {\n await run();\n } finally {\n this.replaysAhead--;\n }\n });\n }\n\n private enqueue(\n kind: \"backfill\" | \"retry\" | \"disconnect\",\n run: () => Promise<void>,\n ): Promise<void> {\n return this.push((done) => ({ kind, run, done }));\n }\n\n private push(task: (done: () => void) => Task): Promise<void> {\n if (this.closed) return Promise.resolve();\n\n return new Promise<void>((resolve) => {\n this.tasks.push(task(resolve));\n if (this.running) return;\n this.running = true;\n // Never start user code on the caller's stack.\n queueMicrotask(() => void this.drain());\n });\n }\n\n private async drain(): Promise<void> {\n while (this.tasks.length > 0) {\n const task = this.tasks.shift()!;\n // Consecutive deliveries go to the processor as one call.\n const batch: Delivery[] = [];\n if (isDelivery(task)) {\n batch.push(task);\n let size = task.kind === \"live\" ? task.ops.length : 0;\n let next = this.tasks[0];\n while (isDelivery(next) && size < MAX_MERGED_OPERATIONS) {\n if (next.kind === \"live\") size += next.ops.length;\n batch.push(next);\n this.tasks.shift();\n next = this.tasks[0];\n }\n }\n try {\n await (isDelivery(task) ? this.deliverLive(batch) : task.run());\n } catch (error) {\n this.options.logger.error(\n \"Processor '@ProcessorId' task '@Kind' failed: @Error\",\n this.options.processorId,\n task.kind,\n error,\n );\n } finally {\n task.done();\n for (const merged of batch.slice(1)) merged.done();\n }\n }\n this.running = false;\n }\n\n private async deliverLive(batch: Delivery[]): Promise<void> {\n const { cursor, floor } = this.options;\n const ops: OperationWithContext[] = [];\n let through = 0;\n for (const task of batch) {\n if (task.kind === \"live\") ops.push(...task.ops);\n else through = Math.max(through, task.through);\n }\n const fresh = ops.filter((op) => {\n const ordinal = op.context.ordinal;\n if (ordinal <= floor) return false;\n return !this.unrouted.delete(ordinal);\n });\n if (fresh.length === 0) {\n await this.raiseCursor(through);\n return;\n }\n\n if (cursor.status !== \"active\" || this.parked) {\n await this.parkBelow(fresh);\n return;\n }\n\n const live = await this.dropPurgedLive(batch, fresh);\n if (live === undefined) {\n await this.parkBelow(fresh);\n return;\n }\n\n if (live.length > 0 && !(await this.deliver(live))) {\n await this.parkBelow(fresh);\n return;\n }\n\n await this.raiseCursor(Math.max(highestOf(fresh), through));\n }\n\n /** Undefined once closed, or parked after the lookup's last retry. */\n private async dropPurgedLive(\n batch: Delivery[],\n fresh: OperationWithContext[],\n ): Promise<OperationWithContext[] | undefined> {\n const keep = new Set<number>();\n const checks: Promise<ReadonlySet<string>>[] = [];\n for (const task of batch) {\n if (task.kind !== \"live\" || !task.check) continue;\n checks.push(task.check.purged);\n for (const ordinal of task.check.keep ?? []) keep.add(ordinal);\n }\n let purged: ReadonlySet<string> | undefined;\n try {\n const sets = await Promise.all(checks);\n purged = new Set(sets.flatMap((set) => [...set]));\n } catch (error) {\n purged = await this.retryLookup(fresh, error);\n }\n return purged && withoutPurged(fresh, purged, keep);\n }\n\n // The cursor holds meanwhile: a transient failure costs a delay, not an error.\n private async retryLookup(\n ops: OperationWithContext[],\n error: unknown,\n ): Promise<ReadonlySet<string> | undefined> {\n const ids = purgeCandidates(ops);\n let wait = this.options.lookupRetryMs ?? LOOKUP_RETRY_MS;\n const retries = this.options.lookupRetries ?? LOOKUP_RETRIES;\n for (let attempt = 0; attempt < retries; attempt++) {\n this.options.logger.error(\n \"Processor '@ProcessorId' failed checking tombstones, retrying in @Wait ms: @Error\",\n this.options.processorId,\n wait,\n error,\n );\n await this.sleep(wait);\n if (this.closed) return undefined;\n try {\n return await this.options.purged(ids);\n } catch (retryError) {\n error = retryError;\n wait = Math.min(wait * 2, MAX_LOOKUP_RETRY_MS);\n }\n }\n await this.park(ops, ids, wait, error);\n return undefined;\n }\n\n // Active but held below `ops`; a replay retries every `wait` ms, then resumes.\n private async park(\n ops: OperationWithContext[],\n ids: string[],\n wait: number,\n error: unknown,\n ): Promise<void> {\n this.parked = true;\n this.noteLookupError(error);\n this.options.logger.error(\n \"Processor '@ProcessorId' parked: tombstones unread after @Retries retries, retrying every @Wait ms: @Error\",\n this.options.processorId,\n this.options.lookupRetries ?? LOOKUP_RETRIES,\n wait,\n error,\n );\n void this.replay(\"backfill\", () => this.resume(ids, wait));\n await this.parkBelow(ops);\n }\n\n private async resume(ids: string[], wait: number): Promise<void> {\n for (;;) {\n if (this.closed) return;\n await this.sleep(wait);\n if (this.closed) return;\n try {\n await this.options.purged(ids);\n break;\n } catch (error) {\n this.noteLookupError(error);\n this.options.logger.error(\n \"Processor '@ProcessorId' still parked, tombstones unread; retrying in @Wait ms: @Error\",\n this.options.processorId,\n wait,\n error,\n );\n }\n }\n this.parked = false;\n const { cursor } = this.options;\n cursor.lastError = undefined;\n cursor.lastErrorTimestamp = undefined;\n await this.persist();\n this.options.logger.info(\n \"Processor '@ProcessorId' resumed from ordinal @Ordinal\",\n this.options.processorId,\n cursor.lastOrdinal,\n );\n await this.runBackfill();\n }\n\n private noteLookupError(error: unknown): void {\n const { cursor } = this.options;\n cursor.lastError = errorMessage(error);\n cursor.lastErrorTimestamp = new Date();\n }\n\n // Unref'd, and cut short by close(), so a retry never outlives the queue.\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const done = () => {\n clearTimeout(timer);\n this.wake = undefined;\n resolve();\n };\n const timer = setTimeout(done, ms);\n (timer as { unref?: () => void }).unref?.();\n this.wake = done;\n });\n }\n\n private async runErase(task: Omit<EraseTask, \"done\">): Promise<boolean> {\n const { cursor, floor } = this.options;\n const fresh = task.ops.filter((op) => {\n const ordinal = op.context.ordinal;\n if (ordinal <= floor) return false;\n return !this.unrouted.delete(ordinal);\n });\n const deletion =\n task.deletion && !this.unrouted.delete(task.deletion.context.ordinal)\n ? task.deletion\n : undefined;\n\n let ops: OperationWithContext[] = [];\n if (cursor.status === \"active\" && !this.parked && fresh.length > 0) {\n try {\n ops = withoutPurged(fresh, await task.check.purged);\n } catch (error) {\n this.options.logger.error(\n \"Processor '@ProcessorId' skipped its drive's last batch, tombstones unread: @Error\",\n this.options.processorId,\n error,\n );\n }\n }\n if (deletion) ops.push(deletion);\n return ops.length === 0 || (await this.deliver(ops));\n }\n\n private async runBackfill(): Promise<void> {\n const { cursor, filter, floor } = this.options;\n if (cursor.status !== \"active\" || this.parked) return;\n\n let page: PagedResults<OperationWithContext>;\n try {\n page = await this.options.readSince(cursor.lastOrdinal);\n } catch (error) {\n await this.fail(error, \"reading backfill\");\n return;\n }\n\n while (page.results.length > 0) {\n if (this.closed) return;\n\n const matching = page.results.filter(\n (op) => op.context.ordinal > floor && matchesFilter(op, filter),\n );\n let live: OperationWithContext[] = matching;\n if (matching.length > 0) {\n const ids = purgeCandidates(matching);\n let purged: ReadonlySet<string> | undefined;\n try {\n purged = await this.options.purged(ids);\n } catch (error) {\n purged = await this.retryLookup(matching, error);\n }\n if (purged === undefined) {\n await this.persist();\n return;\n }\n live = withoutPurged(matching, purged);\n }\n if (live.length > 0) {\n if (!(await this.deliver(live))) {\n await this.persist();\n return;\n }\n }\n if (matching.length > 0) this.dedupeQueued(matching);\n\n await this.raiseCursor(highestOf(page.results));\n\n if (!page.next) break;\n try {\n page = await page.next();\n } catch (error) {\n await this.fail(error, \"reading backfill\");\n return;\n }\n }\n }\n\n // Routing is synchronous: a routed ordinal is queued now, an unrouted one comes later.\n private dedupeQueued(delivered: OperationWithContext[]): void {\n const routed = this.options.routedThrough();\n const queued = new Set<number>();\n for (const op of delivered) {\n const ordinal = op.context.ordinal;\n if (ordinal > routed) this.unrouted.add(ordinal);\n else queued.add(ordinal);\n }\n if (queued.size === 0) return;\n for (const task of this.tasks) {\n if (task.kind === \"erase\") {\n const { deletion } = task;\n if (deletion && queued.has(deletion.context.ordinal)) {\n task.deletion = undefined;\n }\n } else if (task.kind !== \"live\") {\n continue;\n }\n task.ops = task.ops.filter((op) => !queued.has(op.context.ordinal));\n }\n }\n\n private async deliver(ops: OperationWithContext[]): Promise<boolean> {\n try {\n await this.options.processor.onOperations(ops);\n return true;\n } catch (error) {\n this.markErrored(error);\n this.options.logger.error(\n \"Processor '@ProcessorId' failed at ordinal @Ordinal: @Error\",\n this.options.processorId,\n lowestOf(ops),\n error,\n );\n return false;\n }\n }\n\n private async fail(error: unknown, during: string): Promise<void> {\n this.markErrored(error);\n await this.persist();\n this.options.logger.error(\n \"Processor '@ProcessorId' failed @During: @Error\",\n this.options.processorId,\n during,\n error,\n );\n }\n\n private markErrored(error: unknown): void {\n const { cursor } = this.options;\n cursor.status = \"errored\";\n cursor.lastError = errorMessage(error);\n cursor.lastErrorTimestamp = new Date();\n }\n\n private async raiseCursor(through: number): Promise<void> {\n const { cursor } = this.options;\n const capped = Math.min(through, this.options.confirmedThrough());\n if (cursor.status !== \"active\" || this.parked) return;\n if (capped <= cursor.lastOrdinal) return;\n cursor.lastOrdinal = capped;\n await this.persist();\n }\n\n // Retry and restart replay from the cursor, so it must stay below a miss.\n private async parkBelow(missed: OperationWithContext[]): Promise<void> {\n const { cursor } = this.options;\n cursor.lastOrdinal = Math.min(cursor.lastOrdinal, lowestOf(missed) - 1);\n await this.persist();\n }\n\n private async persist(): Promise<void> {\n if (this.closed) return;\n const { cursor } = this.options;\n try {\n await this.options.persist({\n lastOrdinal: cursor.lastOrdinal,\n status: cursor.status,\n lastError: cursor.lastError,\n lastErrorTimestamp: cursor.lastErrorTimestamp,\n });\n } catch (error) {\n this.options.logger.error(\n \"Failed to persist cursor for '@ProcessorId': @Error\",\n this.options.processorId,\n error,\n );\n }\n }\n}\n","import {\n isPurgeMarker,\n type OperationWithContext,\n type PHDocumentHeader,\n} from \"@powerhousedao/shared/document-model\";\nimport type {\n IProcessorManager,\n ProcessorFactory,\n ProcessorRecord,\n TrackedProcessor,\n} from \"@powerhousedao/shared/processors\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport {\n BaseReadModel,\n unchunkedReadModelIndexingConfig,\n} from \"../read-models/base-read-model.js\";\nimport {\n RELEASED_CURSOR_STATUS,\n type DocumentViewDatabase,\n type ProcessorCursorRow,\n} from \"../read-models/types.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport { findPurged } from \"../storage/kysely/document-purges.js\";\nimport {\n ProcessorQueue,\n purgeCandidates,\n type LiveCheck,\n type ProcessorCursorState,\n} from \"./processor-queue.js\";\nimport {\n createMinimalDriveHeader,\n extractCreationHeader,\n extractDeletedDocumentId,\n extractDriveHeader,\n isDriveDeletion,\n matchesFilter,\n resolveProcessorSlots,\n} from \"./utils.js\";\n\n// A drive header read in flight; a purge marker routed meanwhile flags it.\ntype HeaderRead = { purged: boolean; header?: Promise<PHDocumentHeader> };\n\n// A factory run in progress; batches routed meanwhile are left to backfill.\ntype PendingSlot = {\n factoryId: string;\n driveId: string;\n // Highest ordinal routed before the slot existed.\n reservedAt: number;\n lowestRoutedOrdinal: number | undefined;\n // Resolves once the factory call is bound or its records discarded.\n settled: Promise<void> | undefined;\n};\n\ntype Bound = { tracked: TrackedProcessor; queue: ProcessorQueue };\n\ntype FactoryRun = () => Promise<void>;\n\n/** What a cursor row write needs of its processor. */\ntype CursorOwner = Pick<\n TrackedProcessor,\n \"processorId\" | \"factoryId\" | \"driveId\" | \"processorIndex\" | \"lastOrdinal\"\n>;\n\n/** Drives a batch deletes, each with its first deletion in the batch. */\ntype DriveDeletions = ReadonlyMap<string, OperationWithContext>;\n\nconst DRIVE_STREAM_PAGE = 500;\n/** Drive header reads the manager runs at once, as after a restart. */\nexport const HEADER_READ_CONCURRENCY = 8;\n// How long shutdown waits for processors to finish and disconnect.\nconst SHUTDOWN_GRACE_MS = 5_000;\n\nexport type ProcessorManagerOptions = {\n // Key cursors by array position (default). Off derives stable keys from\n // record id, namespace or class name, so reordering factories is safe.\n legacyProcessorIds?: boolean;\n};\n\n// Routing tables are only read or written synchronously; everything that\n// touches a processor runs on that processor's queue.\nexport class ProcessorManager\n extends BaseReadModel\n implements IProcessorManager\n{\n private factoryRegistry: Map<string, ProcessorFactory> = new Map();\n private processorsByDrive: Map<string, Bound[]> = new Map();\n private pendingSlots: Set<PendingSlot> = new Set();\n private knownDrives: Map<string, string> = new Map();\n private highestRoutedOrdinal = 0;\n private cursorCache: Map<string, ProcessorCursorRow> = new Map();\n // lastOrdinal each processor's row holds, as this process last read or wrote it.\n private persistedOrdinals: Map<string, number> = new Map();\n private advancing: Promise<void> = Promise.resolve();\n // Serializes every cursor row write per processor id.\n private cursorWrites: Map<string, Promise<void>> = new Map();\n // Removed processors per factory id, until each has disconnected.\n private draining: Map<string, Promise<void>> = new Map();\n // Deleted drives some cursor row still owes the deletion, with that deletion.\n private deletedDrives: Map<string, OperationWithContext> = new Map();\n // factoryId:driveId pairs whose deletion is being delivered, until it settles.\n private erasing: Map<string, Promise<void>> = new Map();\n // Creation headers of known and owed drives; a purge drops a drive's.\n private driveHeaders: Map<string, PHDocumentHeader> = new Map();\n private headerReads: Map<string, HeaderRead> = new Map();\n private headerSlots = HEADER_READ_CONCURRENCY;\n private headerWaiters: (() => void)[] = [];\n private stopped = false;\n // Rows whose drive's deletion threw, kept visible to getAll() with a retry.\n private owedFailures: Map<string, TrackedProcessor> = new Map();\n private logger: ILogger;\n private driveContainerTypes: ReadonlySet<string>;\n private legacyProcessorIds: boolean;\n\n constructor(\n db: Kysely<DocumentViewDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n options: ProcessorManagerOptions = {},\n ) {\n super(db, operationIndex, writeCache, consistencyTracker, {\n readModelId: \"processor-manager\",\n rebuildStateOnInit: true,\n indexing: unchunkedReadModelIndexingConfig,\n // A lock held across processor delivery would block purges behind it.\n purgeFence: \"none\",\n });\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\n this.legacyProcessorIds = options.legacyProcessorIds ?? true;\n }\n\n override async init(): Promise<void> {\n await this.loadAllCursors();\n await super.init();\n await this.discoverExistingDrives();\n await this.discoverDeletedDrives();\n // Not awaited: a hung processor must not hold the reactor's start.\n for (const [identifier, factory] of this.factoryRegistry) {\n this.eraseOwed(identifier, factory);\n }\n }\n\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n const { runs, reserved } = this.detectNewDrives(items);\n const deletions = this.findDriveDeletions(items);\n const deliveries = this.enqueueRouted(items, reserved, deletions);\n this.detectDeletedDrives(items);\n this.forgetPurgedHeaders(items);\n\n await Promise.all([...runs.map((run) => run()), ...deliveries]);\n }\n\n async registerFactory(\n identifier: string,\n factory: ProcessorFactory,\n ): Promise<void> {\n if (this.stopped) return;\n const removals = this.removeFactory(identifier);\n this.factoryRegistry.set(identifier, factory);\n const previous = this.draining.get(identifier);\n\n // A late registration has no creation batch to anchor to: \"current\"\n // means from here on.\n const creationOrdinal = this.highWater() + 1;\n const runs: FactoryRun[] = [];\n for (const [driveId, documentType] of this.knownDrives) {\n runs.push(\n this.reserveSlot(\n identifier,\n factory,\n driveId,\n this.headerOf(driveId, documentType),\n creationOrdinal,\n undefined,\n false,\n previous,\n ).run,\n );\n }\n\n // Not awaited, like a live deletion; nor does a later registration wait.\n this.eraseOwed(identifier, factory, previous);\n await Promise.all([...removals, ...runs.map((run) => run())]);\n }\n\n async unregisterFactory(identifier: string): Promise<void> {\n await Promise.all(this.removeFactory(identifier));\n }\n\n /** Closes every processor queue; the reactor's kill() calls it. */\n async shutdown(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n this.pendingSlots.clear();\n this.owedFailures.clear();\n const closing = [...this.allBound()].map(({ queue }) => queue.close());\n this.processorsByDrive.clear();\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const grace = new Promise<boolean>((resolve) => {\n timer = setTimeout(() => resolve(false), SHUTDOWN_GRACE_MS);\n (timer as { unref?: () => void }).unref?.();\n });\n const closed = await Promise.race([\n Promise.all(closing).then(() => true),\n grace,\n ]);\n clearTimeout(timer);\n if (!closed) {\n this.logger.warn(\n \"Processors still busy @Ms ms into shutdown; not waiting for them\",\n SHUTDOWN_GRACE_MS,\n );\n }\n }\n\n get(processorId: string): TrackedProcessor | undefined {\n for (const tracked of this.allTrackedProcessors()) {\n if (tracked.processorId === processorId) return tracked;\n }\n return undefined;\n }\n\n getAll(): TrackedProcessor[] {\n return Array.from(this.allTrackedProcessors());\n }\n\n /** Synchronous: reserves a slot per registered factory for each new drive. */\n protected detectNewDrives(items: OperationWithContext[]): {\n runs: FactoryRun[];\n reserved: Set<PendingSlot>;\n } {\n const runs: FactoryRun[] = [];\n const reserved = new Set<PendingSlot>();\n\n for (const op of items) {\n if (!this.isDriveCreation(op)) continue;\n\n const driveId = op.context.documentId;\n if (this.knownDrives.has(driveId)) continue;\n this.knownDrives.set(driveId, op.context.documentType);\n\n const driveHeader = extractDriveHeader(op);\n if (!driveHeader) continue;\n this.driveHeaders.set(driveId, driveHeader);\n\n for (const [identifier, factory] of this.factoryRegistry) {\n const { slot, run } = this.reserveSlot(\n identifier,\n factory,\n driveId,\n driveHeader,\n op.context.ordinal,\n items,\n true,\n );\n reserved.add(slot);\n runs.push(run);\n }\n }\n\n return { runs, reserved };\n }\n\n /** Synchronous: drops a deleted drive's processors; their rows stay owed. */\n protected detectDeletedDrives(items: OperationWithContext[]): void {\n for (const op of items) {\n if (!isDriveDeletion(op)) continue;\n\n const driveId = extractDeletedDocumentId(op);\n if (!driveId || !this.knownDrives.has(driveId)) continue;\n this.knownDrives.delete(driveId);\n // Rows without a live processor are paid when their factory next runs.\n this.deletedDrives.set(driveId, op);\n\n for (const slot of this.pendingSlots) {\n if (slot.driveId === driveId) this.pendingSlots.delete(slot);\n }\n\n for (const { queue } of this.processorsByDrive.get(driveId) ?? []) {\n // Not awaited: the pass must not wait out the drive's queues.\n void queue.close();\n }\n this.processorsByDrive.delete(driveId);\n this.pruneDeletedDrive(driveId);\n }\n }\n\n /** Synchronous: a purged drive's header is erased data. */\n private forgetPurgedHeaders(items: OperationWithContext[]): void {\n for (const op of items) {\n if (isPurgeMarker(op.operation)) {\n this.driveHeaders.delete(op.context.documentId);\n const read = this.headerReads.get(op.context.documentId);\n if (read) read.purged = true;\n }\n }\n }\n\n /** A drive's creation header; minimal once the drive's stream is purged. */\n private headerOf(\n driveId: string,\n documentType: string,\n ): Promise<PHDocumentHeader> {\n const cached = this.driveHeaders.get(driveId);\n if (cached) return Promise.resolve(cached);\n const inFlight = this.headerReads.get(driveId)?.header;\n if (inFlight) return inFlight;\n const read: HeaderRead = { purged: false };\n this.headerReads.set(driveId, read);\n read.header = this.readHeader(driveId, documentType, read);\n return read.header;\n }\n\n private async readHeader(\n driveId: string,\n documentType: string,\n read: HeaderRead,\n ): Promise<PHDocumentHeader> {\n let first: OperationWithContext | undefined;\n await this.takeHeaderSlot();\n try {\n [first] = await this.operationIndex.getStreamAfter(\n { documentId: driveId, scope: \"document\", branch: \"main\" },\n 0,\n undefined,\n 1,\n );\n } catch (error) {\n this.logger.error(\n \"Failed reading drive '@DriveId' header: @Error\",\n driveId,\n error,\n );\n } finally {\n this.releaseHeaderSlot();\n if (this.headerReads.get(driveId) === read)\n this.headerReads.delete(driveId);\n }\n const header = first && extractCreationHeader(first);\n // A purge routed during the read erased what it returned.\n if (!header || read.purged) {\n return createMinimalDriveHeader(driveId, documentType);\n }\n if (this.knownDrives.has(driveId) || this.deletedDrives.has(driveId)) {\n this.driveHeaders.set(driveId, header);\n }\n return header;\n }\n\n private async takeHeaderSlot(): Promise<void> {\n if (this.headerSlots > 0) {\n this.headerSlots--;\n return;\n }\n await new Promise<void>((resolve) => this.headerWaiters.push(resolve));\n }\n\n private releaseHeaderSlot(): void {\n const next = this.headerWaiters.shift();\n if (next) next();\n else this.headerSlots++;\n }\n\n /** Synchronous: the known drives this batch deletes. */\n protected findDriveDeletions(items: OperationWithContext[]): DriveDeletions {\n const deletions = new Map<string, OperationWithContext>();\n for (const op of items) {\n if (!isDriveDeletion(op)) continue;\n const driveId = extractDeletedDocumentId(op);\n if (!driveId || !this.knownDrives.has(driveId)) continue;\n const first = deletions.get(driveId);\n if (!first || op.context.ordinal < first.context.ordinal) {\n deletions.set(driveId, op);\n }\n }\n return deletions;\n }\n\n /** Synchronous: puts each processor's share of the batch on its queue. */\n protected enqueueRouted(\n items: OperationWithContext[],\n reserved: ReadonlySet<PendingSlot>,\n deletions: DriveDeletions = new Map(),\n ): Promise<void>[] {\n if (items.length === 0) return [];\n\n let lowest = items[0]!.context.ordinal;\n let highest = 0;\n for (const item of items) {\n lowest = Math.min(lowest, item.context.ordinal);\n highest = Math.max(highest, item.context.ordinal);\n }\n this.highestRoutedOrdinal = Math.max(this.highestRoutedOrdinal, highest);\n\n for (const slot of this.pendingSlots) {\n if (reserved.has(slot)) continue;\n slot.lowestRoutedOrdinal = Math.min(\n slot.lowestRoutedOrdinal ?? lowest,\n lowest,\n );\n }\n\n let check: LiveCheck | undefined;\n const checkOf = () => (check ??= this.checkPurged(items));\n\n const deliveries: Promise<void>[] = [];\n for (const { tracked, queue } of this.allBound()) {\n const deletion = deletions.get(tracked.driveId);\n if (deletion) {\n deliveries.push(\n this.deliverDeletion(tracked, queue, items, deletion, checkOf()),\n );\n continue;\n }\n const matching = items.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n if (matching.length > 0) {\n deliveries.push(queue.live(matching, checkOf()));\n } else {\n // Not awaited: the pass waits only on processors it delivers to.\n void queue.advance(highest);\n }\n }\n return deliveries;\n }\n\n // The batch through the deletion, then the deletion whatever the filter.\n private deliverDeletion(\n tracked: TrackedProcessor,\n queue: ProcessorQueue,\n items: OperationWithContext[],\n deletion: OperationWithContext,\n check: LiveCheck,\n ): Promise<void> {\n const through = deletion.context.ordinal;\n const share = items.filter(\n (op) =>\n op.context.ordinal < through &&\n matchesFilter(op, tracked.record.filter),\n );\n // Like live(): a pass never waits out a backfill; the row holds meanwhile.\n const behindReplay = queue.replaying;\n const settled = queue\n .erase(share, deletion, check)\n .then((delivered) => this.settleErased(tracked, delivered, deletion));\n this.holdErasure(tracked.factoryId, tracked.driveId, settled);\n return behindReplay ? Promise.resolve() : settled;\n }\n\n /** A delivered deletion deletes the row; a failed one keeps it, errored. */\n private settleErased(\n tracked: TrackedProcessor,\n delivered: boolean,\n deletion: OperationWithContext,\n ): Promise<void> {\n if (!delivered) {\n this.deletedDrives.set(tracked.driveId, deletion);\n this.trackOwedFailure(tracked, tracked.record, tracked);\n return this.writeCursor(tracked, tracked);\n }\n const deleted = this.deleteCursors(\n (row) => row.processorId === tracked.processorId,\n );\n this.pruneDeletedDrive(tracked.driveId);\n return Promise.all(deleted).then(() => undefined);\n }\n\n /** Keeps a failed deletion in getAll(); retry() delivers it again. */\n private trackOwedFailure(\n owner: CursorOwner,\n record: ProcessorRecord,\n state: ProcessorCursorState,\n ): void {\n const { processorId, factoryId, driveId, processorIndex } = owner;\n this.owedFailures.set(processorId, {\n processorId,\n factoryId,\n driveId,\n processorIndex,\n record,\n lastOrdinal: state.lastOrdinal,\n status: \"errored\",\n lastError: state.lastError,\n lastErrorTimestamp: state.lastErrorTimestamp,\n retry: () => this.retryOwed(factoryId, driveId),\n });\n }\n\n private async retryOwed(factoryId: string, driveId: string): Promise<void> {\n const factory = this.factoryRegistry.get(factoryId);\n const deletion = this.deletedDrives.get(driveId);\n if (!factory || !deletion || this.stopped) return;\n await this.eraseDrive(factoryId, factory, driveId, deletion);\n }\n\n /** Forgets a deleted drive once no cursor row owes its deletion. */\n private pruneDeletedDrive(driveId: string): void {\n for (const row of this.cursorCache.values()) {\n if (row.driveId === driveId) return;\n }\n this.deletedDrives.delete(driveId);\n if (!this.knownDrives.has(driveId)) this.driveHeaders.delete(driveId);\n }\n\n /** Delivers each deleted drive's deletion its rows of `factoryId` still owe. */\n private eraseOwed(\n factoryId: string,\n factory: ProcessorFactory,\n previous?: Promise<void>,\n ): void {\n const owesAny = [...this.cursorCache.values()].some(\n (row) =>\n row.factoryId === factoryId && this.deletedDrives.has(row.driveId),\n );\n if (!owesAny || this.stopped) return;\n\n void (async () => {\n // A re-registered factory starts once its previous instance is gone.\n await previous;\n if (this.factoryRegistry.get(factoryId) !== factory) return;\n const owed = new Set<string>();\n for (const row of this.cursorCache.values()) {\n if (\n row.factoryId === factoryId &&\n this.deletedDrives.has(row.driveId)\n ) {\n owed.add(row.driveId);\n }\n }\n await Promise.all(\n [...owed].map((driveId) =>\n this.eraseOwedDrive(factoryId, factory, driveId),\n ),\n );\n })().catch((error: unknown) => {\n this.logger.error(\n \"Owed deletions of '@FactoryId' failed: @Error\",\n factoryId,\n error,\n );\n });\n }\n\n /** After any delivery of the pair's deletion in flight, if still owed. */\n private async eraseOwedDrive(\n factoryId: string,\n factory: ProcessorFactory,\n driveId: string,\n ): Promise<void> {\n const key = `${factoryId}:${driveId}`;\n for (let held = this.erasing.get(key); held; held = this.erasing.get(key)) {\n await held;\n }\n if (this.stopped || this.factoryRegistry.get(factoryId) !== factory) return;\n const owes = [...this.cursorCache.values()].some(\n (row) => row.factoryId === factoryId && row.driveId === driveId,\n );\n const deletion = this.deletedDrives.get(driveId);\n if (!owes || !deletion) return;\n await this.eraseDrive(factoryId, factory, driveId, deletion);\n }\n\n /** Marks the pair's deletion as being delivered until `erasure` settles. */\n private holdErasure(\n factoryId: string,\n driveId: string,\n erasure: Promise<unknown>,\n ): void {\n const key = `${factoryId}:${driveId}`;\n const held = Promise.allSettled([this.erasing.get(key), erasure]).then(\n () => undefined,\n );\n this.erasing.set(key, held);\n void held.then(() => {\n if (this.erasing.get(key) === held) this.erasing.delete(key);\n });\n }\n\n /** Runs `erase` unless the pair's deletion is already being delivered. */\n private async claimErasure(\n factoryId: string,\n driveId: string,\n erase: () => Promise<void>,\n ): Promise<boolean> {\n if (this.erasing.has(`${factoryId}:${driveId}`)) return false;\n let release!: () => void;\n this.holdErasure(\n factoryId,\n driveId,\n new Promise<void>((resolve) => {\n release = resolve;\n }),\n );\n try {\n await erase();\n } finally {\n release();\n }\n return true;\n }\n\n private async eraseDrive(\n factoryId: string,\n factory: ProcessorFactory,\n driveId: string,\n deletion: OperationWithContext,\n ): Promise<void> {\n await this.claimErasure(factoryId, driveId, async () => {\n const header = await this.headerOf(\n driveId,\n deletion.context.documentType,\n );\n const slot = { factoryId, driveId };\n const records = await this.runFactory(slot, factory, header);\n // A failed run leaves the rows owed until the factory runs again.\n if (!records) return;\n await this.eraseRecords(factoryId, driveId, deletion, records);\n });\n }\n\n /** The deletion to each record a row owes it to, then every disconnect. */\n private async eraseRecords(\n factoryId: string,\n driveId: string,\n deletion: OperationWithContext,\n records: ProcessorRecord[],\n ): Promise<void> {\n const ids = resolveProcessorSlots(records, this.legacyProcessorIds);\n const delivered = new Set<string>();\n const failed = new Map<string, unknown>();\n const recordOf = new Map<string, ProcessorRecord>();\n await Promise.all(\n records.map(async (record, i) => {\n const processorId = `${factoryId}:${driveId}:${ids[i]}`;\n if (this.cursorCache.has(processorId)) {\n recordOf.set(processorId, record);\n try {\n await record.processor.onOperations([deletion]);\n delivered.add(processorId);\n } catch (error) {\n failed.set(processorId, error);\n this.logger.error(\n \"Processor '@ProcessorId' failed on its drive's deletion: @Error\",\n processorId,\n error,\n );\n }\n }\n await this.discard(\n { factoryId, driveId },\n record.processor.onDisconnect.bind(record.processor),\n );\n }),\n );\n\n const writes: Promise<void>[] = [];\n for (const row of this.cursorCache.values()) {\n if (row.factoryId !== factoryId || row.driveId !== driveId) continue;\n if (delivered.has(row.processorId)) continue;\n if (!failed.has(row.processorId)) {\n // A run that threw inside a wrapper reads as no records: stay owed.\n this.logger.warn(\n \"Factory '@FactoryId' made no processor '@ProcessorId' for deleted drive '@DriveId'; its deletion stays owed\",\n factoryId,\n row.processorId,\n driveId,\n );\n continue;\n }\n const error = failed.get(row.processorId);\n const state: ProcessorCursorState = {\n lastOrdinal: row.lastOrdinal,\n status: \"errored\",\n lastError: error instanceof Error ? error.message : String(error),\n lastErrorTimestamp: new Date(),\n };\n this.trackOwedFailure(row, recordOf.get(row.processorId)!, state);\n writes.push(this.writeCursor(row, state));\n }\n for (const processorId of delivered) this.owedFailures.delete(processorId);\n writes.push(...this.deleteCursors((row) => delivered.has(row.processorId)));\n this.pruneDeletedDrive(driveId);\n await Promise.all(writes);\n }\n\n /** Started at routing; takes no purge lock, since processors hold none. */\n private checkPurged(items: OperationWithContext[]): LiveCheck {\n const ids = purgeCandidates(items);\n const purged =\n ids.length === 0\n ? Promise.resolve<ReadonlySet<string>>(new Set())\n : findPurged(this.db, ids);\n // Observed here: a closed queue drops the task that would await it.\n purged.catch(() => undefined);\n return { purged };\n }\n\n /** Synchronous: binds records, or discards them if the slot was cancelled. */\n protected bind(\n slot: PendingSlot,\n records: ProcessorRecord[] | undefined,\n creationOrdinal: number,\n creationItems: OperationWithContext[] | undefined,\n ): { persisted: Promise<void>; delivered: Promise<void> } {\n const released = this.pendingSlots.delete(slot);\n // A failed or empty run leaves the factory's cursors for the drive alone.\n if (!records || records.length === 0) {\n const settled = Promise.resolve();\n return { persisted: settled, delivered: settled };\n }\n // A run that settles after shutdown binds nothing: no queue would close.\n if (!released || this.stopped) {\n const settled = this.discardRecords(slot, records);\n return { persisted: settled, delivered: settled };\n }\n\n const { factoryId, driveId, lowestRoutedOrdinal } = slot;\n const ids = resolveProcessorSlots(records, this.legacyProcessorIds);\n const bound: Bound[] = records.map((record, i) =>\n this.track(\n slot,\n record,\n i,\n `${factoryId}:${driveId}:${ids[i]}`,\n creationOrdinal,\n lowestRoutedOrdinal,\n ),\n );\n\n const drive = this.processorsByDrive.get(driveId) ?? [];\n this.processorsByDrive.set(driveId, [...drive, ...bound]);\n\n // Cursors this factory no longer produces for the drive are orphans.\n const liveIds = new Set(bound.map((b) => b.tracked.processorId));\n const writes = this.deleteCursors(\n (row) =>\n row.factoryId === factoryId &&\n row.driveId === driveId &&\n !liveIds.has(row.processorId),\n );\n for (const { tracked } of bound) {\n writes.push(this.writeCursor(tracked, tracked));\n }\n\n const deliveries: Promise<void>[] = [];\n for (const { tracked, queue } of bound) {\n const missed =\n tracked.lastOrdinal < slot.reservedAt ||\n lowestRoutedOrdinal !== undefined;\n if (tracked.status === \"active\" && missed) {\n deliveries.push(queue.backfill());\n }\n const matching = creationItems?.filter((op) =>\n matchesFilter(op, tracked.record.filter),\n );\n if (matching && matching.length > 0) {\n deliveries.push(queue.live(matching, this.checkPurged(matching)));\n }\n }\n\n return {\n persisted: Promise.all(writes).then(() => undefined),\n delivered: Promise.all(deliveries).then(() => undefined),\n };\n }\n\n /** A cancelled slot's records; on a deleted drive they pay what rows owe. */\n private async discardRecords(\n slot: PendingSlot,\n records: ProcessorRecord[],\n ): Promise<void> {\n const { factoryId, driveId } = slot;\n const deletion = this.stopped ? undefined : this.deletedDrives.get(driveId);\n const erased =\n deletion !== undefined &&\n (await this.claimErasure(factoryId, driveId, () =>\n this.eraseRecords(factoryId, driveId, deletion, records),\n ));\n if (erased) return;\n await Promise.all(\n records.map((record) =>\n this.discard(\n slot,\n record.processor.onDisconnect.bind(record.processor),\n ),\n ),\n );\n }\n\n /** Synchronous: removes a factory's slots and processors; releases its rows. */\n protected removeFactory(identifier: string): Promise<void>[] {\n if (!this.factoryRegistry.delete(identifier)) return [];\n for (const [processorId, failure] of this.owedFailures) {\n if (failure.factoryId === identifier)\n this.owedFailures.delete(processorId);\n }\n\n // Deletions in delivery are not waited on; their pairs stay claimed.\n const closing: Promise<void>[] = [];\n for (const slot of this.pendingSlots) {\n if (slot.factoryId !== identifier) continue;\n this.pendingSlots.delete(slot);\n if (slot.settled) closing.push(slot.settled);\n }\n\n for (const [driveId, drive] of this.processorsByDrive) {\n const remaining: Bound[] = [];\n for (const b of drive) {\n if (b.tracked.factoryId === identifier) {\n closing.push(b.queue.close());\n } else {\n remaining.push(b);\n }\n }\n if (remaining.length > 0) {\n this.processorsByDrive.set(driveId, remaining);\n } else {\n this.processorsByDrive.delete(driveId);\n }\n }\n\n if (closing.length > 0) {\n const drained = Promise.all([\n this.draining.get(identifier),\n ...closing,\n ]).then(() => undefined);\n this.draining.set(identifier, drained);\n void drained.then(() => {\n if (this.draining.get(identifier) === drained) {\n this.draining.delete(identifier);\n }\n });\n }\n\n return this.releaseCursors(identifier);\n }\n\n // A drive deleted before the factory's next registration still owes its rows.\n private releaseCursors(factoryId: string): Promise<void>[] {\n const writes: Promise<void>[] = [];\n for (const row of this.cursorCache.values()) {\n if (row.factoryId !== factoryId) continue;\n if (row.status === RELEASED_CURSOR_STATUS) continue;\n if (this.deletedDrives.has(row.driveId)) continue;\n const released = { ...row, status: RELEASED_CURSOR_STATUS };\n this.cursorCache.set(row.processorId, released);\n writes.push(\n this.lane(row.processorId, () =>\n this.db\n .updateTable(\"ProcessorCursor\")\n .set({ status: RELEASED_CURSOR_STATUS, updatedAt: new Date() })\n .where(\"processorId\", \"=\", row.processorId)\n .execute(),\n ),\n );\n }\n return writes;\n }\n\n private reserveSlot(\n factoryId: string,\n factory: ProcessorFactory,\n driveId: string,\n driveHeader: PHDocumentHeader | Promise<PHDocumentHeader>,\n creationOrdinal: number,\n creationItems: OperationWithContext[] | undefined,\n awaitDelivery: boolean,\n previous?: Promise<void>,\n ): { slot: PendingSlot; run: FactoryRun } {\n const slot: PendingSlot = {\n factoryId,\n driveId,\n reservedAt: this.highWater(),\n lowestRoutedOrdinal: undefined,\n settled: undefined,\n };\n this.pendingSlots.add(slot);\n\n const run = async () => {\n const bound = (async () => {\n // A re-registered factory starts once its previous instance is gone.\n await previous;\n const header = await driveHeader;\n const records = await this.runFactory(slot, factory, header);\n return this.bind(slot, records, creationOrdinal, creationItems);\n })();\n slot.settled = bound.then(({ persisted }) => persisted);\n const { persisted, delivered } = await bound;\n await (awaitDelivery ? Promise.all([persisted, delivered]) : persisted);\n };\n\n return { slot, run };\n }\n\n private async runFactory(\n slot: Pick<PendingSlot, \"factoryId\" | \"driveId\">,\n factory: ProcessorFactory,\n driveHeader: PHDocumentHeader,\n ): Promise<ProcessorRecord[] | undefined> {\n try {\n return await factory(driveHeader);\n } catch (error) {\n this.logger.error(\n \"Factory '@FactoryId' failed for drive '@DriveId': @Error\",\n slot.factoryId,\n slot.driveId,\n error,\n );\n return undefined;\n }\n }\n\n private track(\n slot: PendingSlot,\n record: ProcessorRecord,\n processorIndex: number,\n processorId: string,\n creationOrdinal: number,\n lowestRoutedOrdinal: number | undefined,\n ): Bound {\n const cached = this.cursorCache.get(processorId);\n let floor = 0;\n let cursor: ProcessorCursorState;\n // A released row starts the processor afresh, as if it had none.\n if (cached && cached.status !== RELEASED_CURSOR_STATUS) {\n // An active row's error is from a lookup outage; this run starts unparked.\n const errored = cached.status !== \"active\";\n cursor = {\n lastOrdinal: cached.lastOrdinal,\n status: cached.status as ProcessorCursorState[\"status\"],\n lastError: errored ? (cached.lastError ?? undefined) : undefined,\n lastErrorTimestamp: errored\n ? (cached.lastErrorTimestamp ?? undefined)\n : undefined,\n };\n } else {\n if (record.startFrom === \"current\") floor = creationOrdinal - 1;\n cursor = {\n lastOrdinal: floor,\n status: \"active\",\n lastError: undefined,\n lastErrorTimestamp: undefined,\n };\n }\n if (lowestRoutedOrdinal !== undefined) {\n cursor.lastOrdinal = Math.max(\n floor,\n Math.min(cursor.lastOrdinal, lowestRoutedOrdinal - 1),\n );\n }\n\n const tracked: TrackedProcessor = {\n processorId,\n factoryId: slot.factoryId,\n driveId: slot.driveId,\n processorIndex,\n record,\n ...cursor,\n retry: () => queue.retry(),\n };\n const queue = new ProcessorQueue({\n processorId,\n processor: record.processor,\n filter: record.filter,\n cursor: tracked,\n floor,\n readSince: (ordinal) => this.operationIndex.getSinceOrdinal(ordinal),\n routedThrough: () => this.highWater(),\n confirmedThrough: () => this.lastOrdinal,\n persist: (state) => this.writeCursor(tracked, state),\n purged: (ids) => findPurged(this.db, ids),\n logger: this.logger,\n });\n return { tracked, queue };\n }\n\n /** Resolves once every queue has taken the latest cursor advance. */\n whenCursorsAdvanced(): Promise<void> {\n return this.advancing;\n }\n\n /** Every bound processor may now move up to the manager's cursor. */\n protected override onCursorAdvanced(appliedThrough: number): void {\n const advances: Promise<void>[] = [];\n for (const { queue } of this.allBound()) {\n advances.push(queue.advance(appliedThrough));\n }\n this.advancing = Promise.all(advances).then(() => undefined);\n }\n\n private async discard(\n slot: Pick<PendingSlot, \"factoryId\" | \"driveId\">,\n disconnect: () => Promise<void>,\n ): Promise<void> {\n try {\n await disconnect();\n } catch (error) {\n this.logger.error(\n \"Error disconnecting discarded processor for '@FactoryId' on '@DriveId': @Error\",\n slot.factoryId,\n slot.driveId,\n error,\n );\n }\n }\n\n private highWater(): number {\n return Math.max(this.lastOrdinal, this.highestRoutedOrdinal);\n }\n\n private *allBound(): Iterable<Bound> {\n for (const drive of this.processorsByDrive.values()) {\n yield* drive;\n }\n }\n\n private *allTrackedProcessors(): Iterable<TrackedProcessor> {\n for (const { tracked } of this.allBound()) {\n yield tracked;\n }\n yield* this.owedFailures.values();\n }\n\n private isDriveCreation(op: OperationWithContext): boolean {\n return (\n op.operation.action.type === \"CREATE_DOCUMENT\" &&\n this.driveContainerTypes.has(op.context.documentType)\n );\n }\n\n private async discoverExistingDrives(): Promise<void> {\n const drives = await this.db\n .selectFrom(\"DocumentSnapshot\")\n .select([\"documentId\", \"documentType\"])\n .where(\"documentType\", \"in\", [...this.driveContainerTypes])\n .where(\"isDeleted\", \"=\", false)\n .execute();\n\n for (const drive of drives) {\n this.knownDrives.set(drive.documentId, drive.documentType);\n }\n }\n\n /** Cursor rows of a drive that is gone: find the deletion they still owe. */\n private async discoverDeletedDrives(): Promise<void> {\n const driveIds = new Set<string>();\n for (const row of this.cursorCache.values()) {\n if (!this.knownDrives.has(row.driveId)) driveIds.add(row.driveId);\n }\n for (const driveId of driveIds) {\n try {\n await this.discoverDeletedDrive(driveId);\n } catch (error) {\n this.logger.error(\n \"Failed reading deleted drive '@DriveId': @Error\",\n driveId,\n error,\n );\n }\n }\n }\n\n private async discoverDeletedDrive(driveId: string): Promise<void> {\n const key = { documentId: driveId, scope: \"document\", branch: \"main\" };\n let header: PHDocumentHeader | undefined;\n let deletion: OperationWithContext | undefined;\n let after = 0;\n for (;;) {\n const page = await this.operationIndex.getStreamAfter(\n key,\n after,\n undefined,\n DRIVE_STREAM_PAGE,\n );\n for (const op of page) {\n header ??= extractCreationHeader(op);\n if (\n !deletion &&\n isDriveDeletion(op) &&\n extractDeletedDocumentId(op) === driveId\n ) {\n deletion = op;\n }\n }\n if (page.length < DRIVE_STREAM_PAGE) break;\n after = page.at(-1)!.context.ordinal;\n }\n if (!deletion) return;\n this.deletedDrives.set(driveId, deletion);\n if (header) this.driveHeaders.set(driveId, header);\n }\n\n private async loadAllCursors(): Promise<void> {\n const rows = await this.db\n .selectFrom(\"ProcessorCursor\")\n .selectAll()\n .execute();\n\n for (const row of rows) {\n this.cursorCache.set(row.processorId, row);\n this.persistedOrdinals.set(row.processorId, row.lastOrdinal);\n }\n }\n\n private lane(\n processorId: string,\n write: () => Promise<unknown>,\n ): Promise<void> {\n const previous = this.cursorWrites.get(processorId) ?? Promise.resolve();\n const next = previous.then(write).then(\n () => undefined,\n (error: unknown) => {\n this.logger.error(\n \"Failed to write cursor for '@ProcessorId': @Error\",\n processorId,\n error,\n );\n },\n );\n this.cursorWrites.set(processorId, next);\n void next.then(() => {\n if (this.cursorWrites.get(processorId) === next) {\n this.cursorWrites.delete(processorId);\n }\n });\n return next;\n }\n\n private writeCursor(\n tracked: CursorOwner,\n state: ProcessorCursorState,\n ): Promise<void> {\n const now = new Date();\n const row: ProcessorCursorRow = {\n processorId: tracked.processorId,\n factoryId: tracked.factoryId,\n driveId: tracked.driveId,\n processorIndex: tracked.processorIndex,\n lastOrdinal: state.lastOrdinal,\n status: state.status,\n lastError: state.lastError ?? null,\n lastErrorTimestamp: state.lastErrorTimestamp ?? null,\n createdAt: now,\n updatedAt: now,\n };\n this.cursorCache.set(row.processorId, row);\n\n return this.lane(row.processorId, async () => {\n const expected = this.persistedOrdinals.get(row.processorId);\n if (expected === undefined) {\n await this.insertCursor(row, now);\n return;\n }\n const result = await this.db\n .updateTable(\"ProcessorCursor\")\n .set({\n lastOrdinal: row.lastOrdinal,\n status: row.status,\n lastError: row.lastError,\n lastErrorTimestamp: row.lastErrorTimestamp,\n updatedAt: now,\n })\n .where(\"processorId\", \"=\", row.processorId)\n .where(\"lastOrdinal\", \"=\", expected)\n .executeTakeFirst();\n if (Number(result.numUpdatedRows) > 0) {\n this.persistedOrdinals.set(row.processorId, row.lastOrdinal);\n return;\n }\n await this.reconcileCursor(tracked, row, now);\n });\n }\n\n private async insertCursor(\n row: ProcessorCursorRow,\n now: Date,\n ): Promise<void> {\n await this.db\n .insertInto(\"ProcessorCursor\")\n .values({\n processorId: row.processorId,\n factoryId: row.factoryId,\n driveId: row.driveId,\n processorIndex: row.processorIndex,\n lastOrdinal: row.lastOrdinal,\n status: row.status,\n lastError: row.lastError,\n lastErrorTimestamp: row.lastErrorTimestamp,\n updatedAt: now,\n })\n .onConflict((oc) =>\n oc.column(\"processorId\").doUpdateSet({\n lastOrdinal: row.lastOrdinal,\n status: row.status,\n lastError: row.lastError,\n lastErrorTimestamp: row.lastErrorTimestamp,\n updatedAt: now,\n }),\n )\n .execute();\n this.persistedOrdinals.set(row.processorId, row.lastOrdinal);\n }\n\n /** A failed compare-and-set: a lowered row resets the cursor and backfills. */\n private async reconcileCursor(\n tracked: CursorOwner,\n row: ProcessorCursorRow,\n now: Date,\n ): Promise<void> {\n const stored = await this.db\n .selectFrom(\"ProcessorCursor\")\n .select(\"lastOrdinal\")\n .where(\"processorId\", \"=\", row.processorId)\n .executeTakeFirst();\n if (stored === undefined) {\n await this.insertCursor(row, now);\n return;\n }\n this.persistedOrdinals.set(row.processorId, stored.lastOrdinal);\n if (stored.lastOrdinal >= row.lastOrdinal) return;\n\n this.logger.info(\n \"Processor '@ProcessorId' cursor lowered externally from @Old to @New; replaying\",\n row.processorId,\n row.lastOrdinal,\n stored.lastOrdinal,\n );\n tracked.lastOrdinal = stored.lastOrdinal;\n row.lastOrdinal = stored.lastOrdinal;\n for (const bound of this.allBound()) {\n if (bound.tracked.processorId === row.processorId) {\n void bound.queue.backfill();\n }\n }\n }\n\n private deleteCursors(\n matches: (row: ProcessorCursorRow) => boolean,\n ): Promise<void>[] {\n const deletes: Promise<void>[] = [];\n for (const [processorId, row] of this.cursorCache) {\n if (!matches(row)) continue;\n this.cursorCache.delete(processorId);\n this.persistedOrdinals.delete(processorId);\n deletes.push(\n this.lane(processorId, () =>\n this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"processorId\", \"=\", processorId)\n .execute(),\n ),\n );\n }\n return deletes;\n }\n}\n","import type { ErrorInfo } from \"../shared/types.js\";\nimport type { IJobExecutionHandle, Job } from \"./types.js\";\nimport { JobQueueState } from \"./types.js\";\n\n/**\n * Implementation of the IJobExecutionHandle interface\n */\nexport class JobExecutionHandle implements IJobExecutionHandle {\n private _state: JobQueueState;\n private _job: Job;\n private onStart?: () => void;\n private onComplete?: () => void;\n private onFail?: (error: ErrorInfo) => void;\n private onDefer?: () => void;\n\n private getStateName(state: JobQueueState): string {\n switch (state) {\n case JobQueueState.PREPROCESSING:\n return \"PREPROCESSING\";\n case JobQueueState.PENDING:\n return \"PENDING\";\n case JobQueueState.READY:\n return \"READY\";\n case JobQueueState.RUNNING:\n return \"RUNNING\";\n case JobQueueState.RESOLVED:\n return \"RESOLVED\";\n default:\n return `UNKNOWN`;\n }\n }\n\n constructor(\n job: Job,\n initialState: JobQueueState,\n callbacks?: {\n onStart?: () => void;\n onComplete?: () => void;\n onFail?: (error: ErrorInfo) => void;\n onDefer?: () => void;\n },\n ) {\n this._job = job;\n this._state = initialState;\n this.onStart = callbacks?.onStart;\n this.onComplete = callbacks?.onComplete;\n this.onFail = callbacks?.onFail;\n this.onDefer = callbacks?.onDefer;\n }\n\n get job(): Job {\n return this._job;\n }\n\n get state(): JobQueueState {\n return this._state;\n }\n\n start(): void {\n if (this._state !== JobQueueState.READY) {\n throw new Error(\n `Cannot start job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RUNNING;\n this.onStart?.();\n }\n\n complete(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot complete job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onComplete?.();\n }\n\n fail(error: ErrorInfo): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot fail job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onFail?.(error);\n }\n\n defer(): void {\n if (this._state !== JobQueueState.RUNNING) {\n throw new Error(\n `Cannot defer job in state ${this.getStateName(this._state)}`,\n );\n }\n this._state = JobQueueState.RESOLVED;\n this.onDefer?.();\n }\n}\n","import type { CreateDocumentActionInput } from \"@powerhousedao/shared/document-model\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { IQueue } from \"./interfaces.js\";\nimport { JobExecutionHandle } from \"./job-execution-handle.js\";\nimport type {\n IJobExecutionHandle,\n Job,\n JobAvailableEvent,\n JobRoutingMeta,\n} from \"./types.js\";\nimport { JobQueueState, QueueEventTypes, RetryAccounting } from \"./types.js\";\n\n/**\n * In-memory implementation of the IQueue interface.\n * Organizes jobs by documentId, scope, and branch to ensure proper ordering.\n * Ensures serial execution per document by tracking executing jobs.\n * Implements dependency management through queue hints.\n */\nexport class InMemoryQueue implements IQueue {\n private queues = new Map<string, Job[]>();\n private jobIdToQueueKey = new Map<string, string>();\n private docIdToJobId = new Map<string, Set<string>>();\n private jobIdToDocId = new Map<string, string>();\n private completedJobs = new Set<string>();\n private jobIndex = new Map<string, Job>();\n private isBlocked = false;\n private onDrainedCallback?: () => void;\n private isPausedFlag = false;\n\n constructor(\n private eventBus: IEventBus,\n private resolver: IDocumentModelResolver,\n ) {}\n\n private toErrorInfo(error: Error | string): ErrorInfo {\n if (error instanceof Error) {\n return {\n name: error.name,\n message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\n name: \"Error\",\n message: error,\n stack: new Error().stack || \"\",\n };\n }\n\n /**\n * Creates a unique key for a document/scope/branch combination\n */\n private createQueueKey(\n documentId: string,\n scope: string,\n branch: string,\n ): string {\n return `${documentId}:${scope}:${branch}`;\n }\n\n /**\n * Gets or creates a queue for the given key\n */\n private getQueue(queueKey: string): Job[] {\n let queue = this.queues.get(queueKey);\n if (!queue) {\n queue = [];\n this.queues.set(queueKey, queue);\n }\n return queue;\n }\n\n /**\n * Check if a document has any jobs currently executing\n */\n private isDocumentExecuting(documentId: string): boolean {\n const executingSet = this.docIdToJobId.get(documentId);\n return executingSet ? executingSet.size > 0 : false;\n }\n\n /**\n * Mark a job as executing for its document\n */\n private markJobExecuting(job: Job): void {\n let executingSet = this.docIdToJobId.get(job.documentId);\n if (!executingSet) {\n executingSet = new Set();\n this.docIdToJobId.set(job.documentId, executingSet);\n }\n executingSet.add(job.id);\n this.jobIdToDocId.set(job.id, job.documentId);\n }\n\n /**\n * Mark a job as no longer executing for its document\n */\n private markJobComplete(jobId: string, documentId: string): void {\n const executingSet = this.docIdToJobId.get(documentId);\n if (executingSet) {\n executingSet.delete(jobId);\n if (executingSet.size === 0) {\n this.docIdToJobId.delete(documentId);\n }\n }\n this.jobIdToDocId.delete(jobId);\n }\n\n /**\n * Check if all dependencies for a job have been completed\n */\n private areDependenciesMet(job: Job): boolean {\n if (job.queueHint.length === 0) {\n return true;\n }\n return job.queueHint.every((depId) => this.completedJobs.has(depId));\n }\n\n /**\n * Returns the head of the sub-queue if its dependencies are met, or null.\n *\n * The dispatcher only ever considers the head — a dep-blocked head holds\n * the rest of its sub-queue. This preserves per-(documentId, scope, branch)\n * FIFO regardless of how dependencies are authored, and makes the queue's\n * documented \"serialized per document\" invariant hold even when callers\n * omit queueHint dependencies on jobs that share a sub-queue.\n */\n private getNextJobWithMetDependencies(queue: Job[]): Job | null {\n if (queue.length === 0) {\n return null;\n }\n const head = queue[0];\n return this.areDependenciesMet(head) ? head : null;\n }\n\n private getCreateDocumentType(job: Job): string | undefined {\n for (const action of job.actions) {\n if (action.type === \"CREATE_DOCUMENT\") {\n return (action.input as CreateDocumentActionInput).model;\n }\n }\n for (const operation of job.operations) {\n if (operation.action.type === \"CREATE_DOCUMENT\") {\n return (operation.action.input as CreateDocumentActionInput).model;\n }\n }\n return undefined;\n }\n\n async enqueue(job: Job): Promise<void> {\n if (this.isBlocked) {\n throw new Error(\"Queue is blocked\");\n }\n\n const queueKey = this.createQueueKey(job.documentId, job.scope, job.branch);\n const queue = this.getQueue(queueKey);\n\n queue.push(job);\n\n // Track job location for removal and dependency resolution\n this.jobIdToQueueKey.set(job.id, queueKey);\n this.jobIndex.set(job.id, job);\n\n // Gate CREATE_DOCUMENT jobs on model availability\n const documentType = this.getCreateDocumentType(job);\n if (documentType) {\n try {\n await this.resolver.ensureModelLoaded(documentType);\n } catch {\n await this.failJob(job.id, {\n name: \"Error\",\n message: `Failed to load document model for type: ${documentType}`,\n stack: new Error().stack || \"\",\n });\n return;\n }\n }\n\n // Emit job available event\n const eventData: JobAvailableEvent = {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n };\n\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, eventData);\n }\n\n dequeue(\n documentId: string,\n scope: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<IJobExecutionHandle | null> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (!queue || queue.length === 0) {\n return Promise.resolve(null);\n }\n\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n return Promise.resolve(null);\n }\n\n // Remove job from queue\n const jobIndex = queue.indexOf(job);\n queue.splice(jobIndex, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n\n dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isPausedFlag) {\n return Promise.resolve(null);\n }\n\n // Find the first non-empty queue for a document that's not currently executing\n for (const [queueKey, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n // Find the first job with met dependencies\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n continue; // No job with met dependencies in this queue\n }\n\n // Only dequeue if the document is not currently executing jobs\n if (!this.isDocumentExecuting(job.documentId)) {\n // Remove job from queue\n const jobIdx = queue.indexOf(job);\n queue.splice(jobIdx, 1);\n\n // Remove from queue tracking but keep in job index for retry\n this.jobIdToQueueKey.delete(job.id);\n // Keep job in jobIndex so we can retry it if needed\n\n this.markJobExecuting(job);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n // Create and return the execution handle\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n }\n }\n\n return Promise.resolve(null);\n }\n\n dequeueNextMatching(\n predicate: (meta: JobRoutingMeta) => boolean,\n signal?: AbortSignal,\n ): Promise<IJobExecutionHandle | null> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isPausedFlag) {\n return Promise.resolve(null);\n }\n\n for (const [queueKey, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n const job = this.getNextJobWithMetDependencies(queue);\n if (!job) {\n continue;\n }\n\n if (this.isDocumentExecuting(job.documentId)) {\n continue;\n }\n\n const meta: JobRoutingMeta = {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n };\n\n if (!predicate(meta)) {\n continue;\n }\n\n const jobIdx = queue.indexOf(job);\n queue.splice(jobIdx, 1);\n\n this.jobIdToQueueKey.delete(job.id);\n\n this.markJobExecuting(job);\n\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n const handle = new JobExecutionHandle(job, JobQueueState.READY, {\n onStart: () => {\n // Job is now running\n },\n onComplete: () => {\n void this.completeJob(job.id);\n },\n onFail: (error: ErrorInfo) => {\n void this.failJob(job.id, error);\n },\n onDefer: () => {\n void this.deferJob(job.id);\n },\n });\n\n return Promise.resolve(handle);\n }\n }\n\n return Promise.resolve(null);\n }\n\n size(documentId: string, scope: string, branch: string): Promise<number> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n return Promise.resolve(queue ? queue.length : 0);\n }\n\n totalSize(): Promise<number> {\n let total = 0;\n for (const queue of this.queues.values()) {\n total += queue.length;\n }\n return Promise.resolve(total);\n }\n\n remove(jobId: string): Promise<boolean> {\n const queueKey = this.jobIdToQueueKey.get(jobId);\n if (!queueKey) {\n return Promise.resolve(false);\n }\n\n const queue = this.queues.get(queueKey);\n if (!queue) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n const jobIdx = queue.findIndex((job) => job.id === jobId);\n if (jobIdx === -1) {\n // Clean up orphaned index entry\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n return Promise.resolve(false);\n }\n\n // Remove job from queue\n queue.splice(jobIdx, 1);\n\n // Remove from job index\n this.jobIdToQueueKey.delete(jobId);\n this.jobIndex.delete(jobId);\n\n // Clean up empty queue\n if (queue.length === 0) {\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve(true);\n }\n\n clear(documentId: string, scope: string, branch: string): Promise<void> {\n const queueKey = this.createQueueKey(documentId, scope, branch);\n const queue = this.queues.get(queueKey);\n\n if (queue) {\n // Remove all jobs from the job index\n for (const job of queue) {\n this.jobIdToQueueKey.delete(job.id);\n this.jobIndex.delete(job.id);\n }\n\n // Remove the queue\n this.queues.delete(queueKey);\n }\n\n return Promise.resolve();\n }\n\n clearAll(): Promise<void> {\n // Clear all job indices\n this.jobIdToQueueKey.clear();\n this.jobIndex.clear();\n this.completedJobs.clear();\n\n this.queues.clear();\n\n return Promise.resolve();\n }\n\n hasJobs(): Promise<boolean> {\n return Promise.resolve(\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0),\n );\n }\n\n async completeJob(jobId: string): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n // Track the job as completed for dependency resolution\n this.completedJobs.add(jobId);\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // For in-memory queue, completing just removes the job\n // In a persistent queue, this would update the job status\n await this.remove(jobId);\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n async failJob(jobId: string, error?: ErrorInfo): Promise<void> {\n // Get the documentId for the executing job\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n // Mark the job as no longer executing\n this.markJobComplete(jobId, documentId);\n }\n\n const job = this.jobIndex.get(jobId);\n if (job) {\n job.lastError = error;\n if (error) {\n job.errorHistory.push(error);\n }\n }\n\n // Remove from job index\n this.jobIndex.delete(jobId);\n\n // Completed here means finished, not succeeded: this is the same set\n // areDependenciesMet reads, and it cannot tell the two apart. A failed job\n // therefore releases its dependents, which go on to run against whatever\n // state the failure left behind. That is what a batch's dependsOn edges\n // mean today - ordering, with no guarantee the job before them worked.\n this.completedJobs.add(jobId);\n\n // For in-memory queue, failing just removes the job\n // In a persistent queue, this would update the job status and store the error\n await this.remove(jobId);\n\n // Emit JOB_FAILED so subscribers (sync manager, job tracker, etc.) can react.\n // The name is preserved because consumers classify failures by it.\n const emittedError = new Error(error?.message ?? \"Job failed\");\n if (error?.name) {\n emittedError.name = error.name;\n }\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId,\n error: emittedError,\n job,\n })\n .catch(() => {});\n\n // Check if queue is now drained\n this.checkDrained();\n }\n\n deferJob(jobId: string): void {\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n this.jobIndex.delete(jobId);\n }\n\n async retryJob(\n jobId: string,\n error?: ErrorInfo,\n accounting: RetryAccounting = RetryAccounting.CountAgainstLimit,\n ): Promise<void> {\n // Get the job from the index (it might be executing, not in queue)\n const job = this.jobIndex.get(jobId);\n if (!job) {\n return;\n }\n\n job.lastError = error;\n\n // Mark it as no longer executing if it was\n const documentId = this.jobIdToDocId.get(jobId);\n if (documentId) {\n this.markJobComplete(jobId, documentId);\n }\n\n // Remove from indices\n this.jobIndex.delete(jobId);\n this.jobIdToQueueKey.delete(jobId);\n\n // Add error to history\n if (error) {\n job.errorHistory.push(error);\n }\n\n const retryCount = job.retryCount || 0;\n const updatedJob: Job = {\n ...job,\n retryCount:\n accounting === RetryAccounting.CountAgainstLimit\n ? retryCount + 1\n : retryCount,\n lastError: error,\n };\n\n // Re-enqueue with updated retry count\n await this.enqueue(updatedJob);\n }\n\n /**\n * Check if the queue is drained and call the callback if it is\n */\n private checkDrained(): void {\n if (this.isDrained && this.onDrainedCallback) {\n const callback = this.onDrainedCallback;\n this.onDrainedCallback = undefined;\n callback();\n }\n }\n\n /**\n * Returns true if and only if all jobs have been resolved.\n */\n get isDrained(): boolean {\n // Queue is drained if there are no pending jobs and no executing jobs\n const hasPendingJobs =\n this.queues.size > 0 &&\n Array.from(this.queues.values()).some((q) => q.length > 0);\n const hasExecutingJobs =\n this.docIdToJobId.size > 0 &&\n Array.from(this.docIdToJobId.values()).some((set) => set.size > 0);\n\n return !hasPendingJobs && !hasExecutingJobs;\n }\n\n /**\n * Blocks the queue from accepting new jobs.\n * @param onDrained - Optional callback to call when the queue is drained\n */\n block(onDrained?: () => void): void {\n this.isBlocked = true;\n this.onDrainedCallback = onDrained;\n\n // Check if already drained\n this.checkDrained();\n }\n\n /**\n * Unblocks the queue from accepting new jobs.\n */\n unblock(): void {\n this.isBlocked = false;\n this.onDrainedCallback = undefined;\n }\n\n /**\n * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.\n */\n pause(): void {\n this.isPausedFlag = true;\n }\n\n /**\n * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.\n */\n async resume(): Promise<void> {\n this.isPausedFlag = false;\n // Emit JOB_AVAILABLE for each queue with pending jobs to wake up the executor manager\n for (const [, queue] of this.queues.entries()) {\n if (queue.length > 0) {\n const job = queue[0];\n await this.eventBus.emit(QueueEventTypes.JOB_AVAILABLE, {\n documentId: job.documentId,\n scope: job.scope,\n branch: job.branch,\n jobId: job.id,\n });\n }\n }\n }\n\n /**\n * Returns whether job dequeuing is paused.\n */\n get paused(): boolean {\n return this.isPausedFlag;\n }\n\n /**\n * Returns all pending jobs across all queues.\n */\n getPendingJobs(): Job[] {\n const jobs: Job[] = [];\n for (const queue of this.queues.values()) {\n jobs.push(...queue);\n }\n return jobs;\n }\n\n /**\n * Returns a map of document IDs to sets of executing job IDs.\n */\n getExecutingJobIds(): Map<string, Set<string>> {\n return new Map(\n Array.from(this.docIdToJobId.entries()).map(([k, v]) => [k, new Set(v)]),\n );\n }\n\n /**\n * Returns a job by ID from the job index.\n */\n getJob(jobId: string): Job | undefined {\n return this.jobIndex.get(jobId);\n }\n}\n","/**\n * Runtime companion to {@link BuiltInReadModelKind}, which lives in\n * `protocol.ts` — a types-only module that must not emit runtime code.\n *\n * @see Sharded projection workers sub-feature brief\n * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)\n */\n\nimport type { BuiltInReadModelKind } from \"./protocol.js\";\n\n/**\n * Every {@link BuiltInReadModelKind}, at runtime. Derived from a total\n * record so adding a kind to the union without adding it here is a compile\n * error: under projection sharding a kind missing from both `preReadyKinds`\n * and `postReadyKinds` is indexed by nobody — the host's own copies never\n * index an operation — so a gap here reads as silently stale data.\n */\nexport const BUILT_IN_READ_MODEL_KINDS = Object.keys({\n \"document-view\": true,\n \"document-indexer\": true,\n} satisfies Record<BuiltInReadModelKind, true>) as BuiltInReadModelKind[];\n","import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport type { ModelManifestEntry } from \"../executor/worker/protocol.js\";\n\n/** An importable file holding one or more document-model exports. */\nexport type FileModelSource = { filePath: string; exportName?: string };\n\n/** An importable package specifier holding one or more document-model exports. */\nexport type PackageModelSource = {\n packageName: string;\n subpath?: string;\n exportName?: string;\n};\n\n/**\n * A source of document models: a live module, an importable file, or an\n * importable package. File and package sources can cross a worker-thread\n * boundary (workers re-import them); a live module cannot.\n */\nexport type DocumentModelSource =\n | DocumentModelModule<any>\n | FileModelSource\n | PackageModelSource;\n\nexport type ResolvedModelSources = {\n /** Every resolved module, deduped by `documentType@version`, first wins. */\n modules: DocumentModelModule<any>[];\n /** One entry per model reachable through an importable source. */\n manifest: ModelManifestEntry[];\n /** `documentType@version` keys reachable only through live modules. */\n moduleOnlyKeys: string[];\n};\n\nexport function modelSourceKey(module: DocumentModelModule<any>): string {\n return `${module.documentModel.global.id}@${module.version ?? 1}`;\n}\n\nexport function isDocumentModelModule(\n value: unknown,\n): value is DocumentModelModule<any> {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n const candidate = value as {\n documentModel?: { global?: { id?: unknown } };\n reducer?: unknown;\n };\n return (\n typeof candidate.reducer === \"function\" &&\n typeof candidate.documentModel?.global?.id === \"string\"\n );\n}\n\ntype ResolvedEntry = {\n module: DocumentModelModule<any>;\n spec: ModelManifestEntry[\"spec\"] | null;\n};\n\n/**\n * Resolves every source to live modules by importing file/package sources\n * host-side and scanning their exports, so the host registry and the worker\n * manifest are derived from one list and cannot diverge.\n */\nexport async function resolveModelSources(\n sources: DocumentModelSource[],\n): Promise<ResolvedModelSources> {\n const resolved = new Map<string, ResolvedEntry>();\n\n for (const source of sources) {\n if (isDocumentModelModule(source)) {\n addEntry(resolved, { module: source, spec: null });\n continue;\n }\n for (const entry of await resolveImportableSource(source)) {\n addEntry(resolved, entry);\n }\n }\n\n const modules: DocumentModelModule<any>[] = [];\n const manifest: ModelManifestEntry[] = [];\n const moduleOnlyKeys: string[] = [];\n for (const [key, entry] of resolved) {\n modules.push(entry.module);\n if (entry.spec) {\n manifest.push({\n documentType: entry.module.documentModel.global.id,\n version: String(entry.module.version ?? 1),\n spec: entry.spec,\n });\n } else {\n moduleOnlyKeys.push(key);\n }\n }\n return { modules, manifest, moduleOnlyKeys };\n}\n\n// First occurrence wins for the module instance; importability is an OR, so\n// a later importable occurrence backfills the spec of a live-module one.\nfunction addEntry(map: Map<string, ResolvedEntry>, entry: ResolvedEntry): void {\n const key = modelSourceKey(entry.module);\n const existing = map.get(key);\n if (!existing) {\n map.set(key, entry);\n return;\n }\n if (!existing.spec && entry.spec) {\n existing.spec = entry.spec;\n }\n}\n\nasync function resolveImportableSource(\n source: FileModelSource | PackageModelSource,\n): Promise<ResolvedEntry[]> {\n const isFile = \"filePath\" in source;\n const specifier = isFile\n ? new URL(`file://${source.filePath}`).href\n : source.subpath\n ? `${source.packageName}/${source.subpath}`\n : source.packageName;\n const moduleRef = isFile\n ? { filePath: source.filePath }\n : { packageName: specifier };\n\n let moduleNs: Record<string, unknown>;\n try {\n moduleNs = (await import(/* @vite-ignore */ specifier)) as Record<\n string,\n unknown\n >;\n } catch (error) {\n throw new Error(\n `Failed to import document-model source \"${describeSource(source)}\"`,\n { cause: error },\n );\n }\n\n if (source.exportName !== undefined) {\n const value = moduleNs[source.exportName];\n if (!isDocumentModelModule(value)) {\n throw new Error(\n `Export \"${source.exportName}\" of document-model source \"${describeSource(source)}\" is not a DocumentModelModule`,\n );\n }\n return [\n {\n module: value,\n spec: { module: { ...moduleRef, exportName: source.exportName } },\n },\n ];\n }\n\n const entries: ResolvedEntry[] = [];\n for (const [exportName, value] of Object.entries(moduleNs)) {\n if (isDocumentModelModule(value)) {\n entries.push({\n module: value,\n spec: { module: { ...moduleRef, exportName } },\n });\n }\n }\n if (entries.length === 0) {\n throw new Error(\n `Document-model source \"${describeSource(source)}\" has no DocumentModelModule exports`,\n );\n }\n return entries;\n}\n\nfunction describeSource(source: FileModelSource | PackageModelSource): string {\n return \"filePath\" in source\n ? source.filePath\n : source.subpath\n ? `${source.packageName}/${source.subpath}`\n : source.packageName;\n}\n","import { childLogger } from \"document-model\";\nimport { resolveModelSources } from \"../core/model-sources.js\";\nimport type { ModelManifestEntry } from \"../executor/worker/protocol.js\";\nimport { DuplicateModuleError, ModuleNotFoundError } from \"./errors.js\";\nimport type {\n IDocumentModelLoader,\n IDocumentModelRegistry,\n} from \"./interfaces.js\";\n\nexport interface IDocumentModelResolver {\n ensureModelLoaded(documentType: string): Promise<void>;\n}\n\n/**\n * Post-success hook called after the resolver registers a newly loaded\n * model on the parent's registry. Used to broadcast `load-model` to the\n * worker pool so workers can register the same model locally.\n */\nexport type ModelLoadedBroadcastHook = (\n entry: ModelManifestEntry,\n) => Promise<void>;\n\n/**\n * Encapsulates the logic for resolving document model modules on demand.\n * Shared between the queue (CREATE_DOCUMENT gate) and the executor manager\n * (post-failure recovery) so that both paths use the same deduplication\n * and failure-caching state.\n */\nexport class DocumentModelResolver implements IDocumentModelResolver {\n private loadingModels = new Map<string, Promise<void>>();\n private failedModelTypes = new Set<string>();\n private broadcastHook: ModelLoadedBroadcastHook | null = null;\n private modelLoadedHook: ((documentType: string) => Promise<void>) | null =\n null;\n private readonly logger = childLogger([\"reactor\", \"document-model-resolver\"]);\n\n constructor(\n private registry: IDocumentModelRegistry,\n private loader: IDocumentModelLoader,\n ) {}\n\n /**\n * Install a post-success hook called after the resolver registers a\n * newly loaded model. ReactorBuilder uses this to wire the worker-pool\n * `load-model` broadcast without touching the resolver's constructor.\n */\n setBroadcastHook(hook: ModelLoadedBroadcastHook): void {\n this.broadcastHook = hook;\n }\n\n /**\n * Post-success hook called with the document type after a model loads, so\n * peers (tabs) can load the same type via the event bus.\n */\n setModelLoadedHook(hook: (documentType: string) => Promise<void>): void {\n this.modelLoadedHook = hook;\n }\n\n async ensureModelLoaded(documentType: string): Promise<void> {\n try {\n this.registry.getModule(documentType);\n return;\n } catch (error) {\n if (!ModuleNotFoundError.isError(error)) {\n throw error;\n }\n }\n\n if (this.failedModelTypes.has(documentType)) {\n throw new Error(\n `Document model type previously failed to load: ${documentType}`,\n );\n }\n\n const existing = this.loadingModels.get(documentType);\n if (existing) {\n return existing;\n }\n\n const loadPromise = (async () => {\n try {\n await this.loadRegisterAndBroadcast(documentType);\n } catch (error) {\n this.failedModelTypes.add(documentType);\n throw error;\n } finally {\n this.loadingModels.delete(documentType);\n }\n await this.notifyPeersModelLoaded(documentType);\n })();\n\n this.loadingModels.set(documentType, loadPromise);\n return loadPromise;\n }\n\n private async loadRegisterAndBroadcast(documentType: string): Promise<void> {\n const source = await this.loader.load(documentType);\n const resolved = await resolveModelSources([source]);\n const hasRequested = resolved.modules.some(\n (module) => module.documentModel.global.id === documentType,\n );\n if (!hasRequested) {\n throw new Error(\n `Loader source resolved no module for document type: ${documentType}`,\n );\n }\n const results = this.registry.registerModules(...resolved.modules);\n for (const result of results) {\n if (\n result.status === \"error\" &&\n !DuplicateModuleError.isError(result.error)\n ) {\n throw result.error as Error;\n }\n }\n // Importable sources carry manifest entries; live modules do not, so a\n // module source registers host-side only and workers never hear of it.\n if (this.broadcastHook) {\n for (const entry of resolved.manifest) {\n await this.broadcastHook(entry);\n }\n }\n }\n\n // Best-effort peer notification; its failure must not fail the registered load.\n private async notifyPeersModelLoaded(documentType: string): Promise<void> {\n if (!this.modelLoadedHook) {\n return;\n }\n try {\n await this.modelLoadedHook(documentType);\n } catch (error) {\n this.logger.warn(\n \"MODEL_LOADED hook failed: @documentType: @error\",\n documentType,\n error,\n );\n }\n }\n}\n\n/**\n * No-op resolver used when no document model loader is configured.\n * Checks the registry for the model and returns if found; throws if not.\n * Since there is no loader, missing models cannot be recovered.\n */\nexport class NullDocumentModelResolver implements IDocumentModelResolver {\n constructor(private registry?: IDocumentModelRegistry) {}\n\n ensureModelLoaded(documentType: string): Promise<void> {\n if (this.registry) {\n try {\n this.registry.getModule(documentType);\n return Promise.resolve();\n } catch {\n // fall through to throw below\n }\n }\n\n return Promise.reject(new ModuleNotFoundError(documentType));\n }\n}\n","import type {\n ISubscriptionErrorHandler,\n SubscriptionErrorContext,\n} from \"./types.js\";\n\n/**\n * Default error handler that re-throws subscription errors.\n * This ensures that errors are not silently swallowed.\n */\nexport class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {\n handleError(error: unknown, context: SubscriptionErrorContext): void {\n const errorMessage = `Subscription error in ${context.eventType} (${context.subscriptionId})`;\n\n if (error instanceof Error) {\n // Preserve the original error with additional context\n const enhancedError = new Error(`${errorMessage}: ${error.message}`);\n enhancedError.cause = error;\n enhancedError.stack = error.stack;\n throw enhancedError;\n } else {\n // Handle non-Error objects\n throw new Error(`${errorMessage}: ${String(error)}`);\n }\n }\n}\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport type {\n PagedResults,\n RelationshipChangeType,\n SearchFilter,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n DocumentDeletedInfo,\n IReactorSubscriptionManager,\n ISubscriptionErrorHandler,\n} from \"./types.js\";\n\ntype DocumentCreatedCallback = (result: PagedResults<string>) => void;\ntype DocumentDeletedCallback = (\n documentIds: string[],\n info?: DocumentDeletedInfo,\n) => void;\ntype DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;\ntype RelationshipChangedCallback = (\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n) => void;\n\ntype Subscription<T> = {\n id: string;\n callback: T;\n search?: SearchFilter;\n view?: ViewFilter;\n};\n\nexport class ReactorSubscriptionManager implements IReactorSubscriptionManager {\n private createdSubscriptions = new Map<\n string,\n Subscription<DocumentCreatedCallback>\n >();\n private deletedSubscriptions = new Map<\n string,\n Subscription<DocumentDeletedCallback>\n >();\n private updatedSubscriptions = new Map<\n string,\n Subscription<DocumentStateUpdatedCallback>\n >();\n private relationshipSubscriptions = new Map<\n string,\n Subscription<RelationshipChangedCallback>\n >();\n\n private subscriptionCounter = 0;\n private errorHandler: ISubscriptionErrorHandler;\n\n constructor(errorHandler: ISubscriptionErrorHandler) {\n this.errorHandler = errorHandler;\n }\n\n onDocumentCreated(\n callback: DocumentCreatedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `created-${++this.subscriptionCounter}`;\n this.createdSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.createdSubscriptions.delete(id);\n };\n }\n\n onDocumentDeleted(\n callback: DocumentDeletedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `deleted-${++this.subscriptionCounter}`;\n this.deletedSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.deletedSubscriptions.delete(id);\n };\n }\n\n onDocumentStateUpdated(\n callback: DocumentStateUpdatedCallback,\n search?: SearchFilter,\n view?: ViewFilter,\n ): () => void {\n const id = `updated-${++this.subscriptionCounter}`;\n this.updatedSubscriptions.set(id, { id, callback, search, view });\n\n return () => {\n this.updatedSubscriptions.delete(id);\n };\n }\n\n onRelationshipChanged(\n callback: RelationshipChangedCallback,\n search?: SearchFilter,\n ): () => void {\n const id = `relationship-${++this.subscriptionCounter}`;\n this.relationshipSubscriptions.set(id, { id, callback, search });\n\n return () => {\n this.relationshipSubscriptions.delete(id);\n };\n }\n\n /**\n * Notify subscribers about created documents\n */\n notifyDocumentsCreated(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): void {\n const result: PagedResults<string> = {\n results: documentIds,\n options: { cursor: \"\", limit: documentIds.length },\n };\n\n for (const subscription of this.createdSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredIds,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"created\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about deleted documents\n */\n notifyDocumentsDeleted(\n documentIds: string[],\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n info?: DocumentDeletedInfo,\n ): void {\n for (const subscription of this.deletedSubscriptions.values()) {\n const filteredIds = this.filterDocumentIds(\n documentIds,\n subscription.search,\n documentTypes,\n parentIds,\n );\n\n if (filteredIds.length > 0) {\n try {\n if (info) {\n subscription.callback(filteredIds, info);\n } else {\n subscription.callback(filteredIds);\n }\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"deleted\",\n subscriptionId: subscription.id,\n eventData: filteredIds,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about updated documents\n */\n notifyDocumentsUpdated(documents: PHDocument[]): void {\n const result: PagedResults<PHDocument> = {\n results: documents,\n options: { cursor: \"\", limit: documents.length },\n };\n\n for (const subscription of this.updatedSubscriptions.values()) {\n const filteredDocs = this.filterDocuments(documents, subscription.search);\n\n if (filteredDocs.length > 0) {\n try {\n subscription.callback({\n ...result,\n results: filteredDocs,\n });\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"updated\",\n subscriptionId: subscription.id,\n eventData: filteredDocs,\n });\n }\n }\n }\n }\n\n /**\n * Notify subscribers about relationship changes\n */\n notifyRelationshipChanged(\n parentId: string,\n childId: string,\n changeType: RelationshipChangeType,\n childType?: string,\n ): void {\n for (const subscription of this.relationshipSubscriptions.values()) {\n if (\n this.matchesRelationshipFilter(\n parentId,\n childId,\n childType,\n subscription.search,\n )\n ) {\n try {\n subscription.callback(parentId, childId, changeType);\n } catch (error) {\n this.errorHandler.handleError(error, {\n eventType: \"relationshipChanged\",\n subscriptionId: subscription.id,\n eventData: { parentId, childId, changeType },\n });\n }\n }\n }\n }\n\n /**\n * Clear all subscriptions\n */\n clearAll(): void {\n this.createdSubscriptions.clear();\n this.deletedSubscriptions.clear();\n this.updatedSubscriptions.clear();\n this.relationshipSubscriptions.clear();\n }\n\n private filterDocumentIds(\n documentIds: string[],\n search?: SearchFilter,\n documentTypes?: Map<string, string>,\n parentIds?: Map<string, string | null>,\n ): string[] {\n if (!search) return documentIds;\n\n return documentIds.filter((id) => {\n if (search.ids && !search.ids.includes(id)) return false;\n\n if (search.type && documentTypes) {\n const docType = documentTypes.get(id);\n if (docType !== search.type) return false;\n }\n\n if (search.parentId && parentIds) {\n const parentId = parentIds.get(id);\n if (parentId !== search.parentId) return false;\n }\n\n return true;\n });\n }\n\n private filterDocuments(\n documents: PHDocument[],\n search?: SearchFilter,\n ): PHDocument[] {\n if (!search) return documents;\n\n return documents.filter((doc) => {\n if (search.ids && !search.ids.includes(doc.header.id)) return false;\n if (search.type && doc.header.documentType !== search.type) return false;\n if (search.slugs && !search.slugs.includes(doc.header.slug)) return false;\n\n return true;\n });\n }\n\n private matchesRelationshipFilter(\n parentId: string,\n childId: string,\n childType?: string,\n search?: SearchFilter,\n ): boolean {\n if (!search) return true;\n\n if (search.parentId && parentId !== search.parentId) return false;\n if (search.ids && !search.ids.includes(childId)) return false;\n if (search.type && childType && childType !== search.type) return false;\n\n return true;\n }\n}\n","import {\n isPurgeMarker,\n type OperationWithContext,\n type PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { PurgeMarkerContext } from \"../events/types.js\";\nimport type { IReadModel } from \"../read-models/interfaces.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport { RelationshipChangeType } from \"../shared/types.js\";\nimport type { IDocumentView } from \"../storage/interfaces.js\";\nimport type { ReactorSubscriptionManager } from \"./react-subscription-manager.js\";\n\n/**\n * A read model that notifies the subscription manager when operations are processed.\n * This bridges the gap between operation processing and subscription callbacks.\n *\n * Must be processed AFTER other read models have completed and AFTER READ_READY\n * is emitted, so that reactor.get() returns fresh data when callbacks fire.\n */\nexport class SubscriptionNotificationReadModel implements IReadModel {\n readonly name = \"subscription-notification\";\n\n constructor(\n private subscriptionManager: ReactorSubscriptionManager,\n private documentView?: IDocumentView,\n ) {}\n\n async indexOperations(operations: OperationWithContext[]): Promise<void> {\n if (operations.length === 0) return;\n\n const created: string[] = [];\n const deleted: string[] = [];\n const purgeDeleted: string[] = [];\n const updatedIds = new Set<string>();\n const purgedIds = new Set<string>();\n const documentTypes = new Map<string, string>();\n const parentIds = new Map<string, string | null>();\n\n for (const item of operations) {\n const { operation, context } = item;\n const actionType = operation.action.type;\n\n documentTypes.set(context.documentId, context.documentType);\n\n if (isPurgeMarker(operation)) {\n purgedIds.add(context.documentId);\n // An already-deleted document had its Deleted notice at the deletion.\n if ((context as PurgeMarkerContext).appliedDeletion) {\n purgeDeleted.push(context.documentId);\n }\n } else if (actionType === \"CREATE_DOCUMENT\") {\n created.push(context.documentId);\n } else if (actionType === \"DELETE_DOCUMENT\") {\n const input = operation.action.input as { documentId?: string };\n const deletedId = input.documentId ?? context.documentId;\n deleted.push(deletedId);\n } else if (actionType === \"ADD_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Added,\n input.childType,\n );\n } else if (actionType === \"REMOVE_RELATIONSHIP\") {\n const input = operation.action.input as {\n sourceId: string;\n targetId: string;\n childType?: string;\n };\n this.subscriptionManager.notifyRelationshipChanged(\n input.sourceId,\n input.targetId,\n RelationshipChangeType.Removed,\n input.childType,\n );\n } else {\n if (!created.includes(context.documentId)) {\n updatedIds.add(context.documentId);\n }\n }\n }\n\n if (created.length > 0) {\n this.subscriptionManager.notifyDocumentsCreated(\n created,\n documentTypes,\n parentIds,\n );\n }\n\n if (deleted.length > 0) {\n this.subscriptionManager.notifyDocumentsDeleted(\n deleted,\n documentTypes,\n parentIds,\n );\n }\n\n if (purgeDeleted.length > 0) {\n this.subscriptionManager.notifyDocumentsDeleted(\n purgeDeleted,\n documentTypes,\n parentIds,\n { purged: true },\n );\n }\n\n for (const id of purgedIds) updatedIds.delete(id);\n\n if (updatedIds.size > 0 && this.documentView) {\n const documentView = this.documentView;\n const documents = await Promise.all(\n Array.from(updatedIds).map((id) => this.readUpdated(documentView, id)),\n );\n const present = documents.filter(\n (document): document is PHDocument => document !== undefined,\n );\n if (present.length > 0) {\n this.subscriptionManager.notifyDocumentsUpdated(present);\n }\n }\n }\n\n /** Undefined for a document gone since its update; others still notify. */\n private async readUpdated(\n documentView: IDocumentView,\n id: string,\n ): Promise<PHDocument | undefined> {\n try {\n return await documentView.get(id);\n } catch (error) {\n if (DocumentNotFoundError.isError(error)) return undefined;\n throw error;\n }\n }\n}\n","import {\n groupDocumentType,\n groupMembershipActionTypes,\n type OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { sql, type Kysely } from \"kysely\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { SweepResult } from \"../catch-up/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { JobWriteReadyEvent, Unsubscribe } from \"../events/types.js\";\nimport { ReactorEventTypes } from \"../events/types.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport {\n BaseReadModel,\n unchunkedReadModelIndexingConfig,\n} from \"../read-models/base-read-model.js\";\nimport type { DocumentViewDatabase } from \"../read-models/types.js\";\nimport { ConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type { Database as StorageDatabase } from \"../storage/kysely/types.js\";\nimport { buildSingleJobMeta } from \"./utils.js\";\n\nexport const GROUP_REEVALUATION_TRIGGER = \"group-reevaluation-trigger\";\n\nfunction isMembershipChange({\n operation,\n context,\n}: OperationWithContext): boolean {\n return (\n context.documentType === groupDocumentType &&\n context.scope === \"global\" &&\n (groupMembershipActionTypes as readonly string[]).includes(\n operation.action.type,\n )\n );\n}\n\n/**\n * Watches committed writes for group membership changes and enqueues a\n * re-evaluation job for every document whose auth history references the\n * changed group, found through the reverse direction of the group-reference\n * relation. Each affected document is re-judged in its own job, so the work\n * runs under that document's execution slot rather than the group's.\n *\n * The job carries the earliest changed membership timestamp; the executor\n * skips the pass when everything the document holds sorts before it, which\n * keeps the common case (a membership write later than all history) free.\n */\nexport class GroupReevaluationTrigger extends BaseReadModel {\n private unsubscribe?: Unsubscribe;\n\n constructor(\n private readonly logger: ILogger,\n private readonly eventBus: IEventBus,\n private readonly queue: IQueue,\n operationIndex: IOperationIndex,\n db: Kysely<DocumentViewDatabase>,\n ) {\n super(db, operationIndex, {} as IWriteCache, new ConsistencyTracker(), {\n readModelId: GROUP_REEVALUATION_TRIGGER,\n rebuildStateOnInit: false,\n indexing: unchunkedReadModelIndexingConfig,\n startFrom: \"head\",\n replayStreamSuffix: false,\n // Writes no rows; its lookups and enqueues need the connection.\n purgeFence: \"none\",\n });\n }\n\n /** A first start begins at the watermark: it has no history to trust. */\n async startup(): Promise<void> {\n await this.init();\n this.unsubscribe = this.eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event) => this.onWriteReady(event),\n );\n }\n\n shutdown(): void {\n this.unsubscribe?.();\n this.unsubscribe = undefined;\n }\n\n /** Sweeps membership changes only; other operations never hold the cursor. */\n override async sweep(\n settledThrough: number,\n _present: readonly number[],\n signal?: AbortSignal,\n ): Promise<SweepResult> {\n signal?.throwIfAborted();\n const rows = await (this.db as unknown as Kysely<StorageDatabase>)\n .selectFrom(\"operation_index_operations\")\n .select(\"ordinal\")\n .where(\"ordinal\", \">\", this.appliedThrough)\n .where(\"ordinal\", \"<=\", settledThrough)\n .where(\"documentType\", \"=\", groupDocumentType)\n .where(\"scope\", \"=\", \"global\")\n .where(sql<string>`action->>'type'`, \"in\", [\n ...groupMembershipActionTypes,\n ])\n .orderBy(\"ordinal\", \"asc\")\n .execute();\n return super.sweep(\n settledThrough,\n rows.map((row) => Number(row.ordinal)),\n signal,\n );\n }\n\n /** Throws when any lookup or enqueue failed, so the batch is retried. */\n protected override async commitOperations(\n items: OperationWithContext[],\n ): Promise<void> {\n // groupId -> earliest membership-change timestamp in this batch\n const changed = new Map<string, string>();\n for (const item of items) {\n if (!isMembershipChange(item)) continue;\n const { operation, context } = item;\n const existing = changed.get(context.documentId);\n if (\n existing === undefined ||\n Date.parse(operation.timestampUtcMs) < Date.parse(existing)\n ) {\n changed.set(context.documentId, operation.timestampUtcMs);\n }\n }\n if (changed.size === 0) {\n return;\n }\n\n const failures: unknown[] = [];\n\n // One job per affected document, at the earliest trigger among its groups.\n const affected = new Map<string, string>();\n for (const [groupId, timestamp] of changed) {\n let referencers: string[];\n try {\n referencers = await this.operationIndex.getGroupReferencers(groupId);\n } catch (error) {\n this.logger.error(\n \"Failed to resolve referencers of group @groupId: @error\",\n groupId,\n error,\n );\n failures.push(error);\n continue;\n }\n for (const documentId of referencers) {\n const existing = affected.get(documentId);\n if (\n existing === undefined ||\n Date.parse(timestamp) < Date.parse(existing)\n ) {\n affected.set(documentId, timestamp);\n }\n }\n }\n\n for (const [documentId, timestamp] of affected) {\n const jobId = uuidv4();\n const job: Job = {\n id: jobId,\n kind: \"reevaluation\",\n documentId,\n scope: \"global\",\n branch: \"main\",\n actions: [],\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: {\n ...buildSingleJobMeta(jobId),\n triggerTimestampUtcMs: timestamp,\n },\n };\n try {\n await this.queue.enqueue(job);\n } catch (error) {\n this.logger.error(\n \"Failed to enqueue re-evaluation of @documentId: @error\",\n documentId,\n error,\n );\n failures.push(error);\n }\n }\n\n if (failures.length > 0) {\n throw new AggregateError(\n failures,\n \"group re-evaluation was not enqueued for every affected document\",\n );\n }\n }\n\n private async onWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const changes = event.operations.filter(isMembershipChange);\n if (changes.length === 0) {\n return;\n }\n try {\n await this.indexOperations(changes);\n } catch {\n // Logged per failure above; the claims are released for the next sweep.\n }\n }\n}\n","import type {\n HoldReason,\n OperationWithContext,\n PeerCapability,\n PeerCapabilityFlags,\n PeerManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport type { DriveCollectionId } from \"../cache/operation-index-types.js\";\n\nexport enum ChannelScheme {\n CONNECT = \"connect\",\n SWITCHBOARD = \"switchboard\",\n}\n\n/**\n * Dynamic JWT token handler for generating authentication tokens per-request.\n * Called with the target URL to enable audience-specific tokens (aud claim).\n * Returns undefined if no authentication is available (e.g., user not logged in).\n */\nexport type JwtHandler = (url: string) => Promise<string | undefined>;\n\n/**\n * Controls how a remote drives its polling schedule.\n * - `Auto` (default): the channel runs the configured interval timer in the background.\n * - `Manual`: the channel registers and connects but never ticks on its own.\n * Callers must invoke ISyncManager.triggerPull(name) to fetch.\n */\nexport enum PollBehavior {\n Auto = \"auto\",\n Manual = \"manual\",\n}\n\nexport type RemoteOptions = {\n /**\n * Stringified UTC timestamp (ms). When set and not `\"0\"`, outbox operations older\n * than this value are excluded. `\"0\"` and `undefined` are equivalent and disable\n * the filter, syncing from the beginning of history.\n */\n sinceTimestampUtcMs?: string;\n /**\n * Polling cadence for this remote. Defaults to `PollBehavior.Auto` when omitted.\n */\n pollBehavior?: PollBehavior;\n /**\n * The address this channel belongs to, once one has claimed it.\n *\n * Undefined is unbound and adoptable: a channel created anonymously, or one\n * that predates binding. The first authenticated subject to poll or touch it\n * takes it, and from then on it answers to that address alone. There is no\n * sentinel for \"anonymous\" -- an anonymous holder is exactly one that has not\n * claimed the channel.\n */\n boundAddress?: string;\n};\n\nexport type RemoteFilter = {\n documentId: string[];\n scope: string[];\n branch: string;\n};\n\nexport type RemoteCursor = {\n remoteName: string;\n cursorType: string;\n cursorOrdinal: number;\n lastSyncedAtUtcMs?: number;\n};\n\nexport type ChannelMeta = {\n id: string;\n};\n\nexport type SyncEnvelopeType = \"operations\" | \"ack\";\n\nexport type SyncEnvelope = {\n type: SyncEnvelopeType;\n channelMeta: ChannelMeta;\n operations?: OperationWithContext[];\n cursor?: RemoteCursor;\n key?: string;\n dependsOn?: string[];\n};\n\nexport enum SyncOperationStatus {\n Unknown = -1,\n TransportPending = 0,\n ExecutionPending = 1,\n Applied = 2,\n Error = 3,\n}\n\nexport enum ChannelErrorSource {\n None = \"none\",\n Channel = \"channel\",\n Inbox = \"inbox\",\n Outbox = \"outbox\",\n}\n\nexport type SyncOperationErrorType =\n | \"SIGNATURE_INVALID\"\n | \"HASH_MISMATCH\"\n | \"LIBRARY_ERROR\"\n | \"MISSING_OPERATIONS\"\n | \"EXCESSIVE_SHUFFLE\"\n | \"GRACEFUL_ABORT\"\n /**\n * An arriving auth operation did not exceed the local auth head. Exempt from\n * quarantine, because reconciling the two policies needs the traffic a\n * quarantine would stop.\n */\n | \"AUTH_TIMESTAMP_NOT_MONOTONIC\"\n /** The document requires a protocol version this reactor does not run. */\n | \"UNSUPPORTED_PROTOCOL\"\n /** A peer wrote into a document at a version it does not announce. */\n | \"PEER_PROTOCOL_UNSUPPORTED\"\n /** An arriving operation carried a timestamp that is not an ISO-8601 instant. */\n | \"INVALID_TIMESTAMP\"\n /** The document was purged here; its operations are dropped, not kept. */\n | \"DOCUMENT_PURGED\"\n /** A purge's precondition failed; the document itself is unaffected. */\n | \"PURGE_PRECONDITION\"\n /** A peer sent a reserved action type as a regular write. */\n | \"RESERVED_ACTION\"\n /** A peer's purge marker was refused; the document keeps syncing. */\n | \"MARKER_REFUSED\"\n /** No classification applies, including rows written before the field. */\n | \"UNCLASSIFIED\";\n\nexport type ChannelHealth = {\n state: \"idle\" | \"running\" | \"error\";\n lastSuccessUtcMs?: number;\n lastFailureUtcMs?: number;\n failureCount: number;\n};\n\nexport type ConnectionState =\n | \"connecting\"\n | \"connected\"\n | \"disconnected\"\n | \"reconnecting\"\n | \"error\";\n\nexport type ConnectionStateSnapshot = {\n state: ConnectionState;\n failureCount: number;\n lastSuccessUtcMs: number;\n lastFailureUtcMs: number;\n pushBlocked: boolean;\n pushFailureCount: number;\n receivingPages: boolean;\n /** Latest error was the remote rejecting the caller as unauthenticated\n * (401/403 or Forbidden GraphQL error). Only meaningful while state=\"error\". */\n requiresAuth: boolean;\n};\n\nexport type ConnectionStateChangedEvent = {\n remoteName: string;\n remoteId: string;\n previous: ConnectionState;\n current: ConnectionState;\n snapshot: ConnectionStateSnapshot;\n};\n\nexport type RemoteStatus = {\n push: ChannelHealth;\n pull: ChannelHealth;\n};\n\nexport type ChannelConfig = {\n type: string;\n parameters: Record<string, unknown>;\n};\n\n/** What this reactor announces, and the flags its support is a function of. */\nexport type LocalPeer = {\n capabilities: readonly PeerCapability[];\n flags: PeerCapabilityFlags;\n /** The signer's did:key, when configured. */\n appKey?: string;\n /** The manifest's start sequence; defaults to the process start time. */\n sequence?: number;\n /** A document's protocolVersions; undefined when it is not stored here. */\n protocolVersionsOf?: (\n documentId: string,\n branch: string,\n ) => Promise<{ [protocol: string]: number } | undefined>;\n /** Drops a purged document from the host cache behind protocolVersionsOf. */\n forgetDocument?: (documentId: string) => void;\n};\n\n/** The tombstone index, as the sync manager reads it at startup. */\nexport type PurgeLookup = {\n listPurged(): Promise<string[]>;\n};\n\n/** What the peer announced, and when; a null manifest is a silent peer. */\nexport type RemotePeer = {\n manifest: PeerManifest | null;\n receivedAtUtcMs: number;\n};\n\nexport type RemoteRecord = {\n id: string;\n name: string;\n collectionId: DriveCollectionId;\n channelConfig: ChannelConfig;\n filter: RemoteFilter;\n options: RemoteOptions;\n status: RemoteStatus;\n /** Undefined: the peer has not been heard from. */\n peer?: RemotePeer;\n};\n\n/**\n * Event types for sync lifecycle events.\n * These events track the sync progress of a job's operations to remotes.\n * Uses a separate namespace (20000 range) from ReactorEventTypes (10000 range).\n */\nexport const SyncEventTypes = {\n SYNC_PENDING: 20001,\n SYNC_SUCCEEDED: 20002,\n SYNC_FAILED: 20003,\n DEAD_LETTER_ADDED: 20004,\n CONNECTION_STATE_CHANGED: 20005,\n SYNC_HELD: 20006,\n SYNC_RELEASED: 20007,\n PURGE_REFUSED: 20008,\n} as const;\n\n/** A document held back from one remote because its peer cannot run it. */\nexport type SyncHold = {\n remoteName: string;\n documentId: string;\n branch: string;\n reason: HoldReason;\n heldAtUtcMs: number;\n};\n\nexport type SyncHeldEvent = {\n remoteName: string;\n documentId: string;\n branch: string;\n reason: HoldReason;\n};\n\n/** A remote reported it could not apply a purged document's marker. */\nexport type SyncPurgeRefusedEvent = {\n remoteName: string;\n documentId: string;\n branch: string;\n errorMessage: string;\n};\n\nexport type SyncReleasedEvent = {\n remoteName: string;\n documentId: string;\n branch: string;\n};\n\n/**\n * Event emitted when all SyncOperations for a job are queued in outboxes.\n */\nexport type SyncPendingEvent = {\n jobId: string;\n syncOperationCount: number;\n remoteNames: string[];\n};\n\n/**\n * Event emitted when all sync operations for a job succeed.\n */\nexport type SyncSucceededEvent = {\n jobId: string;\n syncOperationCount: number;\n};\n\n/**\n * Event emitted when at least one sync operation for a job fails.\n */\nexport type SyncFailedEvent = {\n jobId: string;\n successCount: number;\n failureCount: number;\n errors: Array<{\n remoteName: string;\n documentId: string;\n error: string;\n }>;\n};\n\n/**\n * Event emitted when a sync operation is moved to dead letter storage.\n */\nexport type DeadLetterAddedEvent = {\n id: string;\n jobId: string;\n remoteName: string;\n documentId: string;\n errorSource: ChannelErrorSource;\n errorType: SyncOperationErrorType;\n};\n\n/**\n * Status of a sync operation result.\n */\nexport type SyncResultStatus = \"succeeded\" | \"failed\";\n\n/**\n * Error information for a failed sync operation to a specific remote.\n */\nexport type SyncResultError = {\n remoteName: string;\n documentId: string;\n error: string;\n};\n\n/**\n * Result of waiting for sync operations to complete for a job.\n * Returned by ISyncManager.waitForSync().\n */\nexport type SyncResult = {\n jobId: string;\n status: SyncResultStatus;\n syncOperationCount: number;\n successCount: number;\n failureCount: number;\n errors: SyncResultError[];\n};\n","import { isPurgeMarker } from \"@powerhousedao/shared/document-model\";\nimport type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport type MailboxCallback = (items: SyncOperation[]) => void;\n\n/**\n * The Mailbox interface is not intended to use any persistence. Instead, the\n * IChannel implementation is responsible for persisting cursors or other data.\n *\n * This means that ackOrdinal and latestOrdinal are in memory only.\n */\nexport interface IMailbox {\n get items(): ReadonlyArray<SyncOperation>;\n\n /**\n * The latest ordinal that has been acknowledged. Because acknowledged items\n * are removed from the mailbox, this is the last ordinal that has been removed.\n * With holdAckBelowMarkers, never at or past an unapplied marker entry.\n */\n get ackOrdinal(): number;\n\n /**\n * The latest ordinal of the items that are or have been added to the mailbox.\n * This may be greater than the ack ordinal if items have been added but not\n * yet acknowledged.\n */\n get latestOrdinal(): number;\n\n // sync op management\n init(ackOrdinal: number): void;\n advanceOrdinal(ordinal: number): void;\n get(id: string): SyncOperation | undefined;\n add(...items: SyncOperation[]): void;\n remove(...items: SyncOperation[]): void;\n\n // listeners\n onAdded(callback: MailboxCallback): void;\n onRemoved(callback: MailboxCallback): void;\n\n // these are mostly for debug use\n pause(): void;\n resume(): void;\n flush(): void;\n isPaused(): boolean;\n}\n\nexport class MailboxAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `Mailbox callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"MailboxAggregateError\";\n this.errors = errors;\n }\n}\n\nexport type MailboxOptions = {\n /** An unapplied item carrying a purge marker keeps ackOrdinal below it. */\n holdAckBelowMarkers?: boolean;\n};\n\nexport class Mailbox implements IMailbox {\n private readonly holdAckBelowMarkers: boolean;\n private itemsMap: Map<string, SyncOperation> = new Map();\n /** Unapplied items carrying a marker, so the held ack reads only these. */\n private readonly heldMarkers = new Set<SyncOperation>();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private paused: boolean = false;\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n constructor(options: MailboxOptions = {}) {\n this.holdAckBelowMarkers = options.holdAckBelowMarkers ?? false;\n }\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n if (!this.holdAckBelowMarkers) return this._ack;\n let floor = Number.POSITIVE_INFINITY;\n for (const item of this.heldMarkers) {\n for (const op of item.operations) {\n const ordinal = op.context.ordinal;\n if (ordinal > 0 && ordinal < floor) floor = ordinal;\n }\n }\n return Math.min(this._ack, floor - 1);\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n const replaced = this.itemsMap.get(item.id);\n if (replaced !== undefined) this.heldMarkers.delete(replaced);\n this.itemsMap.set(item.id, item);\n\n let marker = false;\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n if (isPurgeMarker(op)) marker = true;\n }\n if (\n this.holdAckBelowMarkers &&\n marker &&\n item.status !== SyncOperationStatus.Applied\n ) {\n this.heldMarkers.add(item);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n this.heldMarkers.delete(syncOp);\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n\n if (this.paused) {\n this.addedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n this.heldMarkers.delete(item);\n }\n\n if (this.paused) {\n this.removedBuffer.push(...items);\n return;\n }\n\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n }\n\n resume(): void {\n this.paused = false;\n this.flush();\n }\n\n flush(): void {\n if (this.addedBuffer.length > 0) {\n const items = this.addedBuffer.splice(0);\n const callbacks = [...this.addedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n\n if (this.removedBuffer.length > 0) {\n const items = this.removedBuffer.splice(0);\n const callbacks = [...this.removedCallbacks];\n const errors: Error[] = [];\n for (const callback of callbacks) {\n try {\n callback(items);\n } catch (error) {\n errors.push(\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n}\n","import {\n type IMailbox,\n MailboxAggregateError,\n type MailboxCallback,\n} from \"./mailbox.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\nexport class BufferedMailbox implements IMailbox {\n private itemsMap: Map<string, SyncOperation> = new Map();\n private addedCallbacks: MailboxCallback[] = [];\n private removedCallbacks: MailboxCallback[] = [];\n private addedBuffer: SyncOperation[] = [];\n private removedBuffer: SyncOperation[] = [];\n private addedTimer: ReturnType<typeof setTimeout> | null = null;\n private removedTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly milliseconds: number;\n private readonly maxQueued: number;\n private paused: boolean = false;\n\n private _ack: number = 0;\n private _latestOrdinal: number = 0;\n\n constructor(milliseconds: number, maxQueued: number) {\n this.milliseconds = milliseconds;\n this.maxQueued = maxQueued;\n }\n\n init(ackOrdinal: number) {\n this._ack = this._latestOrdinal = ackOrdinal;\n }\n\n advanceOrdinal(ordinal: number): void {\n this._latestOrdinal = Math.max(this._latestOrdinal, ordinal);\n }\n\n get items(): ReadonlyArray<SyncOperation> {\n return Array.from(this.itemsMap.values());\n }\n\n get ackOrdinal(): number {\n return this._ack;\n }\n\n get latestOrdinal(): number {\n return this._latestOrdinal;\n }\n\n get(id: string): SyncOperation | undefined {\n return this.itemsMap.get(id);\n }\n\n add(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.set(item.id, item);\n\n for (const op of item.operations) {\n this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);\n }\n\n // listen for updates to the syncop status\n item.on((syncOp, _, next) => {\n if (next === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n this._ack = Math.max(this._ack, op.context.ordinal);\n }\n }\n });\n }\n this.addedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.addedBuffer.length >= this.maxQueued) {\n this.flushAdded();\n } else {\n this.scheduleAddedFlush();\n }\n }\n\n remove(...items: SyncOperation[]): void {\n for (const item of items) {\n this.itemsMap.delete(item.id);\n }\n this.removedBuffer.push(...items);\n\n if (this.paused) {\n return;\n }\n\n if (this.removedBuffer.length >= this.maxQueued) {\n this.flushRemoved();\n } else {\n this.scheduleRemovedFlush();\n }\n }\n\n onAdded(callback: MailboxCallback): void {\n this.addedCallbacks.push(callback);\n }\n\n onRemoved(callback: MailboxCallback): void {\n this.removedCallbacks.push(callback);\n }\n\n pause(): void {\n this.paused = true;\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.addedBuffer.length > 0) {\n this.scheduleAddedFlush();\n }\n if (this.removedBuffer.length > 0) {\n this.scheduleRemovedFlush();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n flush(): void {\n this.flushAdded();\n this.flushRemoved();\n }\n\n private scheduleAddedFlush(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n }\n this.addedTimer = setTimeout(() => {\n this.flushAdded();\n }, this.milliseconds);\n }\n\n private scheduleRemovedFlush(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n }\n this.removedTimer = setTimeout(() => {\n this.flushRemoved();\n }, this.milliseconds);\n }\n\n private flushAdded(): void {\n if (this.addedTimer !== null) {\n clearTimeout(this.addedTimer);\n this.addedTimer = null;\n }\n\n const items = this.addedBuffer;\n this.addedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.addedCallbacks, items);\n }\n }\n\n private flushRemoved(): void {\n if (this.removedTimer !== null) {\n clearTimeout(this.removedTimer);\n this.removedTimer = null;\n }\n\n const items = this.removedBuffer;\n this.removedBuffer = [];\n\n if (items.length > 0) {\n this.invokeCallbacks(this.removedCallbacks, items);\n }\n }\n\n private invokeCallbacks(\n callbacks: MailboxCallback[],\n items: SyncOperation[],\n ): void {\n const callbacksCopy = [...callbacks];\n const errors: Error[] = [];\n\n for (const callback of callbacksCopy) {\n try {\n callback(items);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n\n if (errors.length > 0) {\n throw new MailboxAggregateError(errors);\n }\n }\n}\n","import type { ChannelErrorSource, SyncOperationErrorType } from \"./types.js\";\n\nexport type GraphQLRequestErrorCategory =\n | \"network\"\n | \"http\"\n | \"parse\"\n | \"graphql\"\n | \"missing-data\";\n\nexport class GraphQLRequestError extends Error {\n readonly statusCode: number | undefined;\n readonly category: GraphQLRequestErrorCategory;\n /**\n * One entry per error the response carried, in order, holding its\n * `extensions.code` - undefined where it declared none. Kept per error rather\n * than as a set, because a response that mixes a classified error with an\n * unclassified one must not be read as if only the classified one arrived.\n */\n readonly codes: readonly (string | undefined)[];\n\n constructor(\n message: string,\n category: GraphQLRequestErrorCategory,\n statusCode?: number,\n codes: readonly (string | undefined)[] = [],\n ) {\n super(message);\n this.name = \"GraphQLRequestError\";\n this.category = category;\n this.statusCode = statusCode;\n this.codes = codes;\n }\n}\n\n/**\n * Extension codes a remote uses to say a failure is worth polling through.\n *\n * Shared with reactor-api so the server throws what this check reads and the two\n * cannot drift. A `graphql` category error is otherwise permanent: it stops the\n * poll timer, and nothing restarts it, so a code that lands here is the\n * difference between a channel that recovers and one that is dead for the\n * process lifetime.\n */\nexport const RECOVERABLE_GRAPHQL_ERROR_CODES = {\n /**\n * A stored operation cannot be represented in the schema - an action with no\n * id, say. The document holding it needs repairing, but the channel serves\n * every other document, and a peer that stopped polling would stop receiving\n * those too.\n */\n malformedStoredOperation: \"MALFORMED_STORED_OPERATION\",\n /**\n * The poll's marker refusals could not be recorded, so nothing was served.\n * The client keeps them pending and reports them on its next poll.\n */\n refusalNotRecorded: \"REFUSAL_NOT_RECORDED\",\n} as const;\n\nconst RECOVERABLE_CODES: ReadonlySet<string> = new Set(\n Object.values(RECOVERABLE_GRAPHQL_ERROR_CODES),\n);\n\n/**\n * True when every error the response carried named a recoverable code.\n *\n * Unanimity is the requirement: one unclassified error alongside a recoverable\n * one means something else also went wrong, and polling through that would be\n * guessing.\n */\nexport function isRecoverableGraphQLError(error: GraphQLRequestError): boolean {\n return (\n error.codes.length > 0 &&\n error.codes.every(\n (code) => code !== undefined && RECOVERABLE_CODES.has(code),\n )\n );\n}\n\n/** Auth-rejection message fragments the switchboard emits. Shared with\n * reactor-api so server throws and this client check can't drift. */\nexport const DRIVE_AUTH_ERROR_MESSAGES = {\n forbidden: \"Forbidden: insufficient permissions\",\n authenticationRequired: \"Forbidden: authentication required\",\n} as const;\n\n/**\n * A non-GraphQL HTTP failure against a drive endpoint.\n *\n * Drive discovery (`GET <base>/d/:drive`) is REST, not GraphQL, so its\n * failures cannot be a `GraphQLRequestError` without the name lying about\n * what was called. It carries the status for the same reason that one does:\n * `isDriveAuthError` is what decides whether a failure prompts a login, and a\n * bare `Error` tells it nothing.\n */\nexport class DriveRequestError extends Error {\n readonly statusCode: number | undefined;\n\n constructor(message: string, statusCode?: number) {\n super(message);\n this.name = \"DriveRequestError\";\n this.statusCode = statusCode;\n }\n}\n\n/** True when the remote rejected the caller as unauthenticated/unauthorized:\n * an HTTP 401/403, or a Forbidden/Unauthorized GraphQL error.\n *\n * 403 and 401 only — NOT 404. The drive info endpoint answers a drive the\n * caller may not read with the same 404 it gives a drive that does not exist,\n * so that an unauthorized caller cannot enumerate drives by probing slugs.\n * That is deliberate, and it costs exactly this: a protected drive is\n * indistinguishable from a typo, and prompting for a login on every 404 would\n * fire on every mistyped URL. See the `WWW-Authenticate` note on the endpoint\n * for the signal that would let a client tell the two apart. */\nexport function isDriveAuthError(error: unknown): boolean {\n if (error instanceof DriveRequestError) {\n return error.statusCode === 401 || error.statusCode === 403;\n }\n if (!(error instanceof GraphQLRequestError)) {\n return false;\n }\n if (error.category === \"http\") {\n return error.statusCode === 401 || error.statusCode === 403;\n }\n if (error.category === \"graphql\") {\n return Object.values(DRIVE_AUTH_ERROR_MESSAGES).some((m) =>\n error.message.includes(m),\n );\n }\n return false;\n}\n\nexport class PollingChannelError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PollingChannelError\";\n }\n}\n\nexport class ChannelError extends Error {\n source: ChannelErrorSource;\n error: Error;\n /**\n * The classification when something other than the error carries it. Absent\n * means derive it from `error.name`; a dead letter mirrored from a peer sets it,\n * because only the message crosses the wire.\n */\n readonly errorType?: SyncOperationErrorType;\n\n constructor(\n source: ChannelErrorSource,\n error: Error,\n errorType?: SyncOperationErrorType,\n ) {\n super(`ChannelError[${source}]: ${error.message}`);\n this.name = \"ChannelError\";\n this.source = source;\n this.error = error;\n this.errorType = errorType;\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ChannelError } from \"./errors.js\";\nimport { SyncOperationStatus } from \"./types.js\";\n\ntype SyncOperationStatusCallback = (\n syncOp: SyncOperation,\n prev: SyncOperationStatus,\n next: SyncOperationStatus,\n) => void;\n\nexport class SyncOperationAggregateError extends Error {\n errors: Error[];\n\n constructor(errors: Error[]) {\n const messages = errors.map((e) => e.message).join(\"; \");\n super(\n `SyncOperation callback failed with ${errors.length} error(s): ${messages}`,\n );\n this.name = \"SyncOperationAggregateError\";\n this.errors = errors;\n }\n}\n\nexport class SyncOperation {\n readonly id: string;\n readonly jobId: string;\n jobDependencies: string[];\n readonly remoteName: string;\n readonly documentId: string;\n readonly scopes: string[];\n readonly branch: string;\n readonly operations: OperationWithContext[];\n status: SyncOperationStatus;\n error?: ChannelError;\n deliveredCount = 0;\n emittedCount = 0;\n\n callbacks: SyncOperationStatusCallback[] = [];\n\n constructor(\n id: string,\n jobId: string,\n jobDependencies: string[],\n remoteName: string,\n documentId: string,\n scopes: string[],\n branch: string,\n operations: OperationWithContext[],\n ) {\n this.id = id;\n this.jobId = jobId;\n this.jobDependencies = jobDependencies;\n this.remoteName = remoteName;\n this.documentId = documentId;\n this.scopes = scopes;\n this.branch = branch;\n this.operations = operations;\n this.status = SyncOperationStatus.Unknown;\n }\n\n on(callback: SyncOperationStatusCallback): void {\n this.callbacks.push(callback);\n }\n\n started(): void {\n this.transition(SyncOperationStatus.TransportPending);\n }\n\n transported(): void {\n this.transition(SyncOperationStatus.ExecutionPending);\n }\n\n executed(): void {\n this.transition(SyncOperationStatus.Applied);\n }\n\n failed(error: ChannelError): void {\n this.error = error;\n this.transition(SyncOperationStatus.Error);\n }\n\n transition(next: SyncOperationStatus): void {\n const prev = this.status;\n if (next <= prev) {\n return;\n }\n this.status = next;\n const errors: Error[] = [];\n for (const callback of this.callbacks) {\n try {\n callback(this, prev, next);\n } catch (error) {\n errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n if (errors.length > 0) {\n throw new SyncOperationAggregateError(errors);\n }\n }\n}\n","import {\n isPurgeMarker,\n type Operation,\n type OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport { type OperationIndexEntry } from \"../cache/operation-index-types.js\";\nimport type { ChannelError } from \"./errors.js\";\nimport type { PreparedBatch } from \"./batch-aggregator.js\";\nimport type { IMailbox } from \"./mailbox.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncOperationStatus,\n type ChannelHealth,\n type RemoteFilter,\n type SyncOperationErrorType,\n} from \"./types.js\";\n\nexport type OperationBatch = {\n documentId: string;\n branch: string;\n scope: string;\n operations: OperationWithContext[];\n};\n\n/**\n * Trims a mailbox using the jobIds from a batch.\n */\nexport function trimMailboxFromBatch(\n mailbox: IMailbox,\n batch: PreparedBatch,\n): void {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the inbox when marked as executed\n // 2. we remove syncops as a batch after they have been executed\n for (const syncOp of batch.entries) {\n for (const item of mailbox.items) {\n if (syncOp.event.jobId === item.jobId) {\n toRemove.push(item);\n break;\n }\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Trims a mailbox using the ack ordinal.\n */\nexport function trimMailboxFromAckOrdinal(\n mailbox: IMailbox,\n ackOrdinal: number,\n) {\n const toRemove: SyncOperation[] = [];\n\n // we want to guarantee:\n //\n // 1. sync ops are still in the mailbox when marked as applied\n // 2. we remove syncops as a single batch\n for (const syncOp of mailbox.items) {\n let maxOrdinal = 0;\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n\n if (maxOrdinal <= ackOrdinal) {\n toRemove.push(syncOp);\n }\n }\n\n if (toRemove.length > 0) {\n for (const syncOp of toRemove) {\n syncOp.executed();\n }\n\n mailbox.remove(...toRemove);\n }\n}\n\n/**\n * Filters operations based on a remote's filter criteria.\n *\n * @param operations - The operations to filter\n * @param filter - The filter criteria to apply\n * @returns The filtered operations that match the criteria\n */\nexport function filterOperations(\n operations: OperationWithContext[],\n filter: RemoteFilter,\n): OperationWithContext[] {\n return operations.filter((op) => {\n if (filter.branch && op.context.branch !== filter.branch) {\n return false;\n }\n\n if (\n filter.documentId.length > 0 &&\n !filter.documentId.includes(op.context.documentId)\n ) {\n return false;\n }\n\n if (filter.scope.length > 0 && !filter.scope.includes(op.context.scope)) {\n return false;\n }\n\n return true;\n });\n}\n\n/** Markers pass every remote filter, as they pass sinceTimestamp. */\nexport function filterForRemote(\n operations: OperationWithContext[],\n filter: RemoteFilter,\n): OperationWithContext[] {\n const kept = new Set(filterOperations(operations, filter));\n return operations.filter((op) => kept.has(op) || isPurgeMarker(op));\n}\n\n/**\n * Creates an idle channel health status.\n *\n * @returns A new idle channel health object\n */\nexport function createIdleHealth(): ChannelHealth {\n return {\n state: \"idle\",\n failureCount: 0,\n };\n}\n\n/**\n * Batches consecutive operations by documentId and scope, preserving ordering.\n *\n * For operations [a1_doc, a1_global, a2_doc, b1_global], this returns:\n * - Batch 1: [a1_doc] for doc-a, document scope\n * - Batch 2: [a1_global] for doc-a, global scope\n * - Batch 3: [a2_doc] for doc-a, document scope\n * - Batch 4: [b1_global] for doc-b, global scope\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents and scopes.\n */\nexport function batchOperationsByDocument(\n operations: OperationWithContext[],\n): OperationBatch[] {\n const batches: OperationBatch[] = [];\n\n let currentDocId: string | null = null;\n let currentScope: string | null = null;\n let currentBatch: OperationWithContext[] = [];\n\n const flushBatch = () => {\n if (\n currentBatch.length === 0 ||\n currentDocId === null ||\n currentScope === null\n ) {\n return;\n }\n\n batches.push({\n documentId: currentDocId,\n branch: currentBatch[0].context.branch,\n scope: currentScope,\n operations: currentBatch,\n });\n currentBatch = [];\n };\n\n for (const op of operations) {\n const docId = op.context.documentId;\n const scope = op.context.scope;\n if (docId !== currentDocId || scope !== currentScope) {\n flushBatch();\n currentDocId = docId;\n currentScope = scope;\n }\n currentBatch.push(op);\n }\n\n flushBatch();\n return batches;\n}\n\n/**\n * Splits a sorted page of operations into a safe-to-emit prefix and a\n * deferred tail containing the trailing run that shares the same\n * (documentId, branch, scope, timestampUtcMs) as the last operation.\n *\n * The page is assumed to be sorted by (documentId, scope, ordinal), so a\n * same-(docId, scope, ts) run is contiguous and lives at the end of any\n * page that contains its last member. Holding that tail back lets callers\n * prepend it to the next page so a single producer-side execute() call\n * never gets split across two outbound envelopes.\n */\nexport function splitTrailingSameTimestampRun(\n operations: OperationWithContext[],\n): { emit: OperationWithContext[]; carry: OperationWithContext[] } {\n if (operations.length === 0) {\n return { emit: [], carry: [] };\n }\n\n const last = operations[operations.length - 1];\n const lastDocId = last.context.documentId;\n const lastBranch = last.context.branch;\n const lastScope = last.context.scope;\n const lastTs = last.operation.timestampUtcMs;\n\n let carryStart = operations.length;\n for (let i = operations.length - 1; i >= 0; i--) {\n const op = operations[i];\n if (\n op.context.documentId === lastDocId &&\n op.context.branch === lastBranch &&\n op.context.scope === lastScope &&\n op.operation.timestampUtcMs === lastTs\n ) {\n carryStart = i;\n } else {\n break;\n }\n }\n\n return {\n emit: operations.slice(0, carryStart),\n carry: operations.slice(carryStart),\n };\n}\n\nexport function getMaxOrdinal(operations: OperationWithContext[]): number {\n return operations.reduce(\n (maxOrdinal, operation) => Math.max(maxOrdinal, operation.context.ordinal),\n 0,\n );\n}\n\nexport function filterByCollectionMembership(\n operations: OperationWithContext[],\n collectionId: string,\n collectionMemberships?: Record<string, string[]>,\n): OperationWithContext[] {\n if (!collectionMemberships) {\n return [];\n }\n\n return operations.filter((op) => {\n const documentId = op.context.documentId;\n if (!(documentId in collectionMemberships)) {\n return false;\n }\n return collectionMemberships[documentId].includes(collectionId);\n });\n}\n\nexport function toOperationWithContext(\n entry: OperationIndexEntry,\n): OperationWithContext {\n return {\n operation: {\n id: entry.id,\n index: entry.index,\n skip: entry.skip,\n hash: entry.hash,\n timestampUtcMs: entry.timestampUtcMs,\n deniedReason: entry.deniedReason,\n action: entry.action,\n } as Operation,\n context: {\n documentId: entry.documentId,\n documentType: entry.documentType,\n scope: entry.scope,\n branch: entry.branch,\n ordinal: entry.ordinal ?? 0,\n },\n };\n}\n\n/**\n * Merges SyncOperations that share the same (documentId, scope, branch) into\n * a single SyncOperation per group. Within each group, operations are sorted\n * by context.ordinal. The merged SyncOperation keeps the first group member's\n * jobId; all other jobIds are remapped so external dependencies still resolve.\n *\n * Only CONTIGUOUS runs merge. A document whose scopes interleave arrives as an\n * alternating chain (document -> auth -> document -> auth) where each entry\n * depends on the one before it. Merging every occurrence of a\n * (documentId, scope, branch) collapses that chain into two nodes that each\n * depend on the other, which validateBatchStructure rejects as a dependency\n * cycle. Merging only adjacent entries keeps the chain linear.\n */\nexport function consolidateSyncOperations(\n syncOps: SyncOperation[],\n): SyncOperation[] {\n if (syncOps.length <= 1) {\n return syncOps;\n }\n\n const groups: Array<{ ops: SyncOperation[]; canonicalJobId: string }> = [];\n const jobIdRemap = new Map<string, string>();\n let prevKey: string | null = null;\n\n for (const syncOp of syncOps) {\n const key = `${syncOp.documentId}|${syncOp.scopes.slice().sort().join(\",\")}|${syncOp.branch}`;\n\n const current = key === prevKey ? groups[groups.length - 1] : undefined;\n if (current) {\n current.ops.push(syncOp);\n if (syncOp.jobId && syncOp.jobId !== current.canonicalJobId) {\n jobIdRemap.set(syncOp.jobId, current.canonicalJobId);\n }\n } else {\n groups.push({ ops: [syncOp], canonicalJobId: syncOp.jobId });\n prevKey = key;\n }\n }\n\n const result: SyncOperation[] = [];\n\n for (const group of groups) {\n const allOperations = group.ops\n .flatMap((op) => op.operations)\n .sort((a, b) => a.context.ordinal - b.context.ordinal);\n\n const allDeps = new Set<string>();\n for (const op of group.ops) {\n for (const dep of op.jobDependencies) {\n allDeps.add(dep);\n }\n }\n allDeps.delete(group.canonicalJobId);\n for (const op of group.ops) {\n allDeps.delete(op.jobId);\n }\n\n const remappedDeps: string[] = [];\n for (const dep of allDeps) {\n const mapped = jobIdRemap.get(dep) ?? dep;\n if (!remappedDeps.includes(mapped) && mapped !== group.canonicalJobId) {\n remappedDeps.push(mapped);\n }\n }\n\n const first = group.ops[0];\n const merged = new SyncOperation(\n first.id,\n first.jobId,\n remappedDeps,\n first.remoteName,\n first.documentId,\n first.scopes,\n first.branch,\n allOperations,\n );\n\n if (first.status > SyncOperationStatus.TransportPending) {\n if (first.status >= SyncOperationStatus.Error) {\n merged.executed();\n } else if (first.status >= SyncOperationStatus.Applied) {\n merged.transported();\n } else {\n merged.started();\n }\n }\n\n result.push(merged);\n }\n\n return result;\n}\n\ntype ChunkableItem = {\n syncOp: { jobId: string; jobDependencies: string[] };\n};\n\n/**\n * Chunks sync operations into batches that respect dependency-connected\n * components. SyncOps linked by jobDependencies are kept in the same chunk.\n * If a connected component exceeds maxSize, it is split by topological order.\n */\nexport function chunkSyncOperations<T extends ChunkableItem>(\n items: T[],\n maxSize: number,\n): T[][] {\n if (items.length === 0) return [];\n if (items.length <= maxSize) return [items];\n\n // Union-Find\n const parent = items.map((_, i) => i);\n const rank = new Array<number>(items.length).fill(0);\n\n function find(x: number): number {\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]];\n x = parent[x];\n }\n return x;\n }\n\n function union(a: number, b: number): void {\n const ra = find(a);\n const rb = find(b);\n if (ra === rb) return;\n if (rank[ra] < rank[rb]) {\n parent[ra] = rb;\n } else if (rank[ra] > rank[rb]) {\n parent[rb] = ra;\n } else {\n parent[rb] = ra;\n rank[ra]++;\n }\n }\n\n const jobIdToIndex = new Map<string, number>();\n for (let i = 0; i < items.length; i++) {\n jobIdToIndex.set(items[i].syncOp.jobId, i);\n }\n\n for (let i = 0; i < items.length; i++) {\n for (const dep of items[i].syncOp.jobDependencies) {\n const depIdx = jobIdToIndex.get(dep);\n if (depIdx !== undefined) {\n union(i, depIdx);\n }\n }\n }\n\n // Group into components preserving insertion order\n const componentMap = new Map<number, T[]>();\n for (let i = 0; i < items.length; i++) {\n const root = find(i);\n let component = componentMap.get(root);\n if (!component) {\n component = [];\n componentMap.set(root, component);\n }\n component.push(items[i]);\n }\n\n const components = [...componentMap.values()];\n\n // Greedy bin-packing\n const chunks: T[][] = [];\n let currentChunk: T[] = [];\n\n for (const component of components) {\n if (component.length > maxSize) {\n // Flush current chunk first\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n }\n // Split oversized component by topological walk\n for (const subChunk of splitComponent(component, maxSize)) {\n chunks.push(subChunk);\n }\n continue;\n }\n\n if (currentChunk.length + component.length > maxSize) {\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n currentChunk = [...component];\n } else {\n currentChunk.push(...component);\n }\n }\n\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n}\n\n/**\n * Splits an oversized connected component into chunks by topological order.\n * Cross-chunk dependency references are handled by the caller's dep filter.\n */\nfunction splitComponent<T extends ChunkableItem>(\n items: T[],\n maxSize: number,\n): T[][] {\n // Build in-degree map for topological sort within the component\n const jobIdToItem = new Map<string, T>();\n const jobIds = new Set<string>();\n for (const item of items) {\n jobIdToItem.set(item.syncOp.jobId, item);\n jobIds.add(item.syncOp.jobId);\n }\n\n const inDegree = new Map<string, number>();\n const adjacency = new Map<string, string[]>();\n for (const item of items) {\n const key = item.syncOp.jobId;\n if (!inDegree.has(key)) inDegree.set(key, 0);\n if (!adjacency.has(key)) adjacency.set(key, []);\n for (const dep of item.syncOp.jobDependencies) {\n if (jobIds.has(dep)) {\n inDegree.set(key, (inDegree.get(key) ?? 0) + 1);\n if (!adjacency.has(dep)) adjacency.set(dep, []);\n adjacency.get(dep)!.push(key);\n }\n }\n }\n\n // Kahn's algorithm\n const queue: string[] = [];\n for (const [key, degree] of inDegree) {\n if (degree === 0) queue.push(key);\n }\n\n const sorted: T[] = [];\n while (queue.length > 0) {\n const key = queue.shift()!;\n sorted.push(jobIdToItem.get(key)!);\n for (const neighbor of adjacency.get(key) ?? []) {\n const newDegree = (inDegree.get(neighbor) ?? 1) - 1;\n inDegree.set(neighbor, newDegree);\n if (newDegree === 0) queue.push(neighbor);\n }\n }\n\n // Fall back to insertion order for any items not reached (cycle safety)\n if (sorted.length < items.length) {\n const sortedIds = new Set(sorted.map((item) => item.syncOp.jobId));\n for (const item of items) {\n if (!sortedIds.has(item.syncOp.jobId)) {\n sorted.push(item);\n }\n }\n }\n\n // Slice into chunks\n const chunks: T[][] = [];\n for (let i = 0; i < sorted.length; i += maxSize) {\n chunks.push(sorted.slice(i, i + maxSize));\n }\n return chunks;\n}\n\n/**\n * Classifies a failure by error name rather than `instanceof`, because a failure\n * that crossed the pooled-worker boundary arrives as plain data.\n */\nexport function classifyJobFailure(errorName: string): SyncOperationErrorType {\n switch (errorName) {\n case \"AuthTimestampNotMonotonicError\":\n return \"AUTH_TIMESTAMP_NOT_MONOTONIC\";\n case \"InvalidOperationTimestampError\":\n return \"INVALID_TIMESTAMP\";\n case \"ExcessiveReshuffleError\":\n return \"EXCESSIVE_SHUFFLE\";\n case \"InvalidSignatureError\":\n return \"SIGNATURE_INVALID\";\n case \"HashMismatchError\":\n return \"HASH_MISMATCH\";\n case \"UnsupportedProtocolVersionError\":\n return \"UNSUPPORTED_PROTOCOL\";\n case \"DocumentPurgedError\":\n return \"DOCUMENT_PURGED\";\n case \"DocumentNotDeletedError\":\n case \"GroupInUseError\":\n case \"PurgeTooLargeError\":\n return \"PURGE_PRECONDITION\";\n case \"ReservedActionError\":\n return \"RESERVED_ACTION\";\n default:\n return \"UNCLASSIFIED\";\n }\n}\n\n/** The explicit type when something else carried it, else derived by name. */\nexport function syncOperationErrorType(\n error: ChannelError | undefined,\n): SyncOperationErrorType {\n return error?.errorType ?? classifyJobFailure(error?.error?.name ?? \"Error\");\n}\n\n/** Dead-letter types that must not stop the document syncing. */\nconst NON_QUARANTINING_ERROR_TYPES: ReadonlySet<SyncOperationErrorType> =\n new Set([\n \"AUTH_TIMESTAMP_NOT_MONOTONIC\",\n \"UNSUPPORTED_PROTOCOL\",\n \"PEER_PROTOCOL_UNSUPPORTED\",\n \"DOCUMENT_PURGED\",\n \"PURGE_PRECONDITION\",\n \"MARKER_REFUSED\",\n ]);\n\n/**\n * A held auth operation must not quarantine: reconciling the two policies needs\n * the traffic a quarantine would stop. A protocol refusal concerns one peer,\n * and quarantine is global to the document.\n */\nexport function quarantinesDocument(\n errorType: SyncOperationErrorType,\n): boolean {\n return !NON_QUARANTINING_ERROR_TYPES.has(errorType);\n}\n","import type { IQueue } from \"../../queue/interfaces.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\n\nexport type PollTimerConfig = {\n intervalMs: number;\n maxQueueDepth: number;\n backpressureCheckIntervalMs: number;\n retryBaseDelayMs: number;\n retryMaxDelayMs: number;\n /**\n * If true, start() puts the timer into a paused state — running but not ticking.\n * Use resume() to begin scheduling ticks, or triggerNow() to fire a single tick\n * without leaving the paused state. Defaults to false (auto-tick on start).\n */\n startPaused: boolean;\n};\n\nconst DEFAULT_CONFIG: PollTimerConfig = {\n intervalMs: 2000,\n maxQueueDepth: 100,\n backpressureCheckIntervalMs: 500,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n startPaused: false,\n};\n\nexport function calculateBackoffDelay(\n consecutiveFailures: number,\n retryBaseDelayMs: number,\n retryMaxDelayMs: number,\n random: number,\n): number {\n const backoff = Math.min(\n retryMaxDelayMs,\n retryBaseDelayMs * Math.pow(2, consecutiveFailures - 1),\n );\n return backoff / 2 + random * (backoff / 2);\n}\n\n/**\n * Default poll timer using setTimeout.\n * Waits for delegate completion before scheduling next tick.\n * Checks queue depth and defers polling when backpressure is detected.\n */\nexport class IntervalPollTimer implements IPollTimer {\n private delegate: (() => Promise<void>) | undefined;\n private timer: NodeJS.Timeout | undefined;\n private running: boolean;\n private paused: boolean;\n private consecutiveFailures: number;\n private readonly queue: IQueue;\n private readonly config: PollTimerConfig;\n\n constructor(queue: IQueue, config: Partial<PollTimerConfig> = {}) {\n this.queue = queue;\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.running = false;\n this.paused = this.config.startPaused;\n this.consecutiveFailures = 0;\n }\n\n setDelegate(delegate: () => Promise<void>): void {\n this.delegate = delegate;\n }\n\n start(): void {\n this.running = true;\n this.consecutiveFailures = 0;\n if (!this.paused) {\n this.tick();\n }\n }\n\n stop(): void {\n this.running = false;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n private tick(): void {\n if (!this.delegate || !this.running) return;\n\n const delegate = this.delegate;\n\n void this.queue\n .totalSize()\n .then((size) => {\n if (!this.running) return;\n if (size > this.config.maxQueueDepth) {\n this.scheduleBackpressureRecheck();\n } else {\n void delegate()\n .then(() => {\n this.consecutiveFailures = 0;\n this.scheduleNext();\n })\n .catch(() => {\n this.consecutiveFailures++;\n this.scheduleRetry();\n });\n }\n })\n .catch(() => {\n // Fail-open: schedule next at normal interval when totalSize() throws\n this.scheduleNext();\n });\n }\n\n private scheduleNext(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(() => this.tick(), this.config.intervalMs);\n }\n\n private scheduleRetry(): void {\n if (!this.running || this.paused) return;\n const delay = calculateBackoffDelay(\n this.consecutiveFailures,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n this.timer = setTimeout(() => this.tick(), delay);\n }\n\n private scheduleBackpressureRecheck(): void {\n if (!this.running || this.paused) return;\n this.timer = setTimeout(\n () => this.tick(),\n this.config.backpressureCheckIntervalMs,\n );\n }\n\n pause(): void {\n this.paused = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = undefined;\n }\n }\n\n resume(): void {\n this.paused = false;\n if (this.running) {\n this.scheduleNext();\n }\n }\n\n triggerNow(): void {\n if (this.running && this.delegate) {\n this.tick();\n }\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n getIntervalMs(): number {\n return this.config.intervalMs;\n }\n\n setIntervalMs(ms: number): void {\n this.config.intervalMs = ms;\n }\n}\n","import type { SyncPurgeRefusedEvent } from \"./types.js\";\n\n/** The most refusals a poll reports; the rest wait for the next poll. */\nexport const MAX_POLLED_REFUSALS = 100;\n\nexport type PolledMarkerRefusal = { documentId: string; branch: string };\n\n/** Refusals are persisted before PURGE_REFUSED fires, so a restart keeps them. */\nexport interface IPurgeRefusalRecorder {\n /** Rejects when the refusal was not persisted; the caller reports it again. */\n recordPurgeRefusal(refusal: SyncPurgeRefusedEvent): Promise<void>;\n /**\n * A poller's marker refusals, at most {@link MAX_POLLED_REFUSALS}. Only a\n * tombstoned document in the remote's collection was owed its marker; any\n * other is dropped. Rejects when the refusals were not persisted.\n */\n recordPolledMarkerRefusals(\n remoteName: string,\n refusals: readonly PolledMarkerRefusal[],\n ): Promise<void>;\n}\n\nexport function supportsPurgeRefusals(x: unknown): x is IPurgeRefusalRecorder {\n return (\n typeof x === \"object\" &&\n x !== null &&\n typeof (x as { recordPurgeRefusal?: unknown }).recordPurgeRefusal ===\n \"function\" &&\n typeof (x as { recordPolledMarkerRefusals?: unknown })\n .recordPolledMarkerRefusals === \"function\"\n );\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Action, Operation } from \"@powerhousedao/shared/document-model\";\nimport {\n deserializeSignature,\n toTransportAction,\n} from \"@powerhousedao/shared/document-model\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport { SyncOperationStatus, type SyncEnvelope } from \"../types.js\";\nimport { batchOperationsByDocument } from \"../utils.js\";\n\nlet syncOpCounter = 0;\n\n/**\n * Serializes an action for GraphQL transport.\n *\n * Projects onto exactly the fields `ActionInput` declares, which is what keeps a\n * stale runtime-only field - a legacy `attachments` array, say - from riding\n * along into the mutation variables, where the schema would reject the whole\n * request rather than the field.\n */\nexport function serializeAction(action: Action): unknown {\n return toTransportAction(action);\n}\n\n/**\n * Serializes a SyncEnvelope for GraphQL transport.\n *\n * Signatures are serialized as comma-separated strings since GraphQL schema\n * defines them as [String!]!. The resultingState context field is stripped\n * since it is not defined in OperationContextInput.\n */\nexport function serializeEnvelope(envelope: SyncEnvelope): unknown {\n return {\n type: envelope.type.toUpperCase(),\n channelMeta: envelope.channelMeta,\n operations: envelope.operations?.map((opWithContext) => ({\n operation: {\n index: opWithContext.operation.index,\n timestampUtcMs: opWithContext.operation.timestampUtcMs,\n hash: opWithContext.operation.hash,\n skip: opWithContext.operation.skip,\n error: opWithContext.operation.error,\n // Omitted while undefined, so a peer whose schema predates the field only\n // sees it on an operation that was actually denied.\n ...(opWithContext.operation.deniedReason !== undefined\n ? { deniedReason: opWithContext.operation.deniedReason }\n : {}),\n id: opWithContext.operation.id,\n action: serializeAction(opWithContext.operation.action),\n },\n context: {\n documentId: opWithContext.context.documentId,\n documentType: opWithContext.context.documentType,\n scope: opWithContext.context.scope,\n branch: opWithContext.context.branch,\n ordinal: opWithContext.context.ordinal,\n },\n })),\n cursor: envelope.cursor,\n key: envelope.key,\n dependsOn: envelope.dependsOn,\n };\n}\n\n/**\n * The nullable operation fields as GraphQL delivers them: a selected field\n * arrives as null rather than absent, which `Operation` does not admit.\n */\ntype WireNullableFields = {\n error?: string | null;\n deniedReason?: string | null;\n};\n\n/**\n * `isDenied` tests strictly against undefined, so a null left in place would mark\n * every synced operation denied. The key is removed rather than set to undefined,\n * so an operation that arrived without it stays identical to the one sent.\n */\nfunction normalizeAbsentFields(operation: Operation): Operation {\n const wire: WireNullableFields = operation;\n if (wire.error !== null && wire.deniedReason !== null) {\n return operation;\n }\n\n const normalized = { ...operation };\n if (wire.error === null) {\n delete normalized.error;\n }\n if (wire.deniedReason === null) {\n delete normalized.deniedReason;\n }\n\n return normalized;\n}\n\n/** Restores signature tuples and null-valued optional fields to undefined. */\nfunction deserializeOperation(\n opWithContext: OperationWithContext,\n): OperationWithContext {\n const operation = normalizeAbsentFields(opWithContext.operation);\n const signer = operation.action.context?.signer;\n\n if (!signer?.signatures || signer.signatures.length === 0) {\n return { ...opWithContext, operation };\n }\n\n const deserializedSignatures = signer.signatures.map(deserializeSignature);\n\n const deserializedOperation: Operation = {\n ...operation,\n action: {\n ...operation.action,\n context: {\n ...operation.action.context,\n signer: {\n ...signer,\n signatures: deserializedSignatures,\n },\n },\n },\n };\n\n return {\n ...opWithContext,\n operation: deserializedOperation,\n };\n}\n\n/**\n * Converts a SyncEnvelope containing operations into a SyncOperation.\n *\n * Extracts the necessary metadata from the envelope's operations to create\n * a sync operation that can be processed by the receiving channel. Also\n * deserializes any signatures from comma-separated strings back to tuples,\n * as GraphQL transport serializes Signature tuples for compatibility.\n *\n * @param envelope - The sync envelope containing operations\n * @param remoteName - The name of the remote this sync operation is associated with\n * @returns A new SyncOperation containing the envelope's operations with deserialized signatures\n * @throws Error if envelope has no operations or operations array is empty\n */\nexport function envelopeToSyncOperation(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation {\n if (!envelope.operations || envelope.operations.length === 0) {\n throw new Error(\n \"Cannot create SyncOperation from envelope without operations\",\n );\n }\n\n const deserializedOperations = envelope.operations.map(deserializeOperation);\n const firstOp = deserializedOperations[0];\n const documentId = firstOp.context.documentId;\n const branch = firstOp.context.branch;\n const scopes = [\n ...new Set(deserializedOperations.map((op) => op.context.scope)),\n ];\n\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n documentId,\n scopes,\n branch,\n deserializedOperations,\n );\n}\n\n/**\n * Converts a SyncEnvelope containing operations into multiple SyncOperations.\n *\n * This function batches operations by documentId, preserving cross-document ordering.\n * For operations [a1, a2, a3, b1, b2, a4], it returns:\n * - SyncOperation 1: [a1, a2, a3] for doc-a\n * - SyncOperation 2: [b1, b2] for doc-b\n * - SyncOperation 3: [a4] for doc-a\n *\n * This ensures operations are grouped for efficient processing while maintaining\n * causality across documents.\n */\nexport function envelopesToSyncOperations(\n envelope: SyncEnvelope,\n remoteName: string,\n): SyncOperation[] {\n if (!envelope.operations || envelope.operations.length === 0) {\n return [];\n }\n\n const deserializedOps = envelope.operations.map(deserializeOperation);\n const batches = batchOperationsByDocument(deserializedOps);\n\n return batches.map((batch) => {\n const syncOpId = `syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`;\n return new SyncOperation(\n syncOpId,\n envelope.key ?? \"\",\n (envelope.dependsOn ?? []).filter(Boolean),\n remoteName,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n });\n}\n\nexport const getLatestAppliedOrdinal = (syncOps: SyncOperation[]): number => {\n let maxOrdinal = 0;\n for (const syncOp of syncOps) {\n if (syncOp.status === SyncOperationStatus.Applied) {\n for (const op of syncOp.operations) {\n maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);\n }\n }\n }\n return maxOrdinal;\n};\n","import {\n isOlderManifest,\n isPurgeMarker,\n MARKER_REFUSAL_FEATURE,\n readPeerManifest,\n type PeerManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../../cache/operation-index-types.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport { BufferedMailbox } from \"../buffered-mailbox.js\";\nimport {\n ChannelError,\n GraphQLRequestError,\n isDriveAuthError,\n isRecoverableGraphQLError,\n} from \"../errors.js\";\nimport type {\n ConnectionStateChangeCallback,\n IChannel,\n PeerManifestListener,\n} from \"../interfaces.js\";\nimport { type IMailbox, Mailbox } from \"../mailbox.js\";\nimport { SyncOperation } from \"../sync-operation.js\";\nimport type {\n ConnectionState,\n ConnectionStateSnapshot,\n JwtHandler,\n RemoteFilter,\n SyncEnvelope,\n SyncOperationErrorType,\n} from \"../types.js\";\nimport { ChannelErrorSource, SyncOperationStatus } from \"../types.js\";\nimport {\n consolidateSyncOperations,\n syncOperationErrorType,\n trimMailboxFromAckOrdinal,\n} from \"../utils.js\";\nimport { calculateBackoffDelay } from \"./interval-poll-timer.js\";\nimport { MAX_POLLED_REFUSALS } from \"../purge-refusals.js\";\nimport type { IPollTimer } from \"./poll-timer.js\";\nimport {\n envelopesToSyncOperations,\n getLatestAppliedOrdinal,\n serializeEnvelope,\n} from \"./utils.js\";\n\n/**\n * Configuration parameters for GqlChannel\n */\nexport type GqlChannelConfig = {\n /** The GraphQL endpoint URL */\n url: string;\n /** Dynamic JWT token handler for generating fresh tokens per-request */\n jwtHandler?: JwtHandler;\n /** Custom fetch function for testing (default: global fetch) */\n fetchFn?: typeof fetch;\n /** Collection to synchronize (drive-level abstraction) */\n collectionId: DriveCollectionId;\n /** Filter to apply to operations */\n filter: RemoteFilter;\n /** Base delay in ms for exponential backoff on push retries */\n retryBaseDelayMs: number;\n /** Maximum delay in ms for exponential backoff on push retries */\n retryMaxDelayMs: number;\n};\n\n/**\n * Fields the auth projection added to the sync schema. A remote that predates\n * them rejects the whole query for naming one, so they are selected only while\n * the remote is known to serve them.\n */\nconst DECISION_FIELDS = [\"deniedReason\", \"errorType\"] as const;\n\n/** Fields peer agreement added; a remote without them is a silent peer. */\nconst AGREEMENT_FIELDS = [\n \"manifest\",\n \"manifestRevision\",\n \"peerManifestRevision\",\n \"refusals\",\n \"SyncRefusalInput\",\n] as const;\n\n/** `kind` only on a marker refusal, sent to a peer announcing the feature. */\ntype RefusalWire = { documentId: string; branch: string; kind?: \"marker\" };\n\nfunction refusalKey(refusal: RefusalWire): string {\n return `${refusal.documentId}\\u0000${refusal.branch}\\u0000${refusal.kind ?? \"\"}`;\n}\n\n/** How often a channel whose remote went silent asks again whether it serves agreement. */\nconst AGREEMENT_PROBE_INTERVAL_MS = 5 * 60_000;\n\nfunction pushMutation(withGatedUnder: boolean): string {\n return withGatedUnder\n ? `\n mutation PushSyncEnvelopes($envelopes: [SyncEnvelopeInput!]!, $peerManifestRevision: String) {\n pushSyncEnvelopes(envelopes: $envelopes, peerManifestRevision: $peerManifestRevision)\n }\n `\n : `\n mutation PushSyncEnvelopes($envelopes: [SyncEnvelopeInput!]!) {\n pushSyncEnvelopes(envelopes: $envelopes)\n }\n `;\n}\n\ntype DeadLetterWire = {\n documentId: string;\n error: string;\n errorType?: string | null;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n};\n\ntype PollSyncEnvelopesResult = {\n pollSyncEnvelopes: {\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters?: DeadLetterWire[];\n hasMore: boolean;\n manifestRevision?: string | null;\n peerManifestRevision?: string | null;\n };\n};\n\n/**\n * GraphQL-based synchronization channel for network communication between reactors.\n */\nexport class GqlRequestChannel implements IChannel {\n readonly inbox: IMailbox;\n readonly outbox: IMailbox;\n readonly deadLetter: IMailbox;\n readonly config: GqlChannelConfig;\n private readonly bufferedOutbox: BufferedMailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly operationIndex: IOperationIndex;\n private readonly pollTimer: IPollTimer;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private failureCount: number;\n private lastSuccessUtcMs?: number;\n private lastFailureUtcMs?: number;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private pushFailureCount: number = 0;\n private pushRetryTimer: ReturnType<typeof setTimeout> | null = null;\n private pushBlocked: boolean = false;\n private isPushing: boolean = false;\n private pendingDrain: boolean = false;\n private receivingPages: boolean = false;\n /** Cleared for good the first time the remote rejects {@link DECISION_FIELDS}. */\n private peerServesDecisionFields: boolean = true;\n /** Cleared when the remote rejects {@link AGREEMENT_FIELDS}; set again by a touch that carries them. */\n private peerServesAgreement: boolean = true;\n private agreementStoppedUtcMs = 0;\n private localManifestProvider?: () => PeerManifest;\n /** Undefined until the first handshake; null for a silent peer. */\n private peerManifest: PeerManifest | null | undefined = undefined;\n private readonly peerManifestCallbacks = new Set<PeerManifestListener>();\n private manifestRefresh: Promise<void> | undefined;\n /** Polled rows this reactor could not run, reported on the next poll. */\n private readonly pendingRefusals = new Map<string, RefusalWire>();\n /** When each pushed, unacknowledged marker entry was last pushed. */\n private readonly markerPushedAt = new Map<string, number>();\n /** Documents whose marker the remote refused; its report repeats per poll. */\n private readonly refusedMarkers = new Set<string>();\n private isRecovering: boolean = false;\n private connectionState: ConnectionState = \"connecting\";\n /** Latest unrecoverable error was an auth rejection; cleared on connect. */\n private requiresAuth: boolean = false;\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n config: GqlChannelConfig,\n operationIndex: IOperationIndex,\n pollTimer: IPollTimer,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.operationIndex = operationIndex;\n this.pollTimer = pollTimer;\n this.config = {\n url: config.url,\n jwtHandler: config.jwtHandler,\n fetchFn: config.fetchFn,\n collectionId: config.collectionId,\n filter: config.filter,\n retryBaseDelayMs: config.retryBaseDelayMs,\n retryMaxDelayMs: config.retryMaxDelayMs,\n };\n this.isShutdown = false;\n this.failureCount = 0;\n\n this.inbox = new Mailbox({ holdAckBelowMarkers: true });\n this.bufferedOutbox = new BufferedMailbox(500, 25);\n this.outbox = this.bufferedOutbox;\n this.deadLetter = new Mailbox();\n\n this.deadLetter.onAdded((syncOps) => {\n for (const syncOp of syncOps) {\n this.logger.warn(\n \"Dead letter added for document @DocumentId on channel @ChannelId\",\n syncOp.documentId,\n this.channelId,\n );\n if (syncOp.error?.source !== ChannelErrorSource.Inbox) continue;\n const errorType = syncOperationErrorType(syncOp.error);\n const refusal: RefusalWire | undefined =\n errorType === \"UNSUPPORTED_PROTOCOL\"\n ? { documentId: syncOp.documentId, branch: syncOp.branch }\n : errorType === \"MARKER_REFUSED\"\n ? {\n documentId: syncOp.documentId,\n branch: syncOp.branch,\n kind: \"marker\",\n }\n : undefined;\n if (refusal) this.pendingRefusals.set(refusalKey(refusal), refusal);\n }\n });\n\n // when sync ops are added to the outbox, push them to the remote\n this.outbox.onAdded((added) => {\n if (this.isShutdown) return;\n // The buffer hands over entries removed since, e.g. a purged id's.\n const syncOps = added.filter((op) => this.outbox.get(op.id) === op);\n if (syncOps.length === 0) return;\n if (this.isPushing) {\n this.pendingDrain = true;\n return;\n }\n if (this.pushBlocked) return; // ops stay in outbox, included in next retry\n if (this.receivingPages) {\n this.pendingDrain = true;\n return;\n }\n this.attemptPush(syncOps);\n });\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n for (const syncOp of syncOps) this.markerPushedAt.delete(syncOp.id);\n // Items for different documents apply out of order, so the highest\n // applied ordinal can pass one still in flight; a restart would skip it.\n const ordinal = Math.min(\n getLatestAppliedOrdinal(syncOps),\n this.unappliedFloor() - 1,\n );\n if (ordinal > this.lastPersistedOutboxOrdinal) {\n this.lastPersistedOutboxOrdinal = ordinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: ordinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n\n // The inbox ack, which never passes a marker still awaiting its load.\n this.inbox.onRemoved(() => {\n const maxOrdinal = this.inbox.ackOrdinal;\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n /**\n * Shuts down the channel and prevents further operations.\n */\n shutdown(): Promise<void> {\n this.abortController.abort();\n this.bufferedOutbox.flush();\n this.isShutdown = true;\n this.pollTimer.stop();\n\n if (this.pushRetryTimer) {\n clearTimeout(this.pushRetryTimer);\n this.pushRetryTimer = null;\n }\n\n this.transitionConnectionState(\"disconnected\");\n\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: this.failureCount,\n lastSuccessUtcMs: this.lastSuccessUtcMs ?? 0,\n lastFailureUtcMs: this.lastFailureUtcMs ?? 0,\n pushBlocked: this.pushBlocked,\n pushFailureCount: this.pushFailureCount,\n receivingPages: this.receivingPages,\n requiresAuth: this.requiresAuth,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n triggerPull(): void {\n if (this.isShutdown) return;\n this.pollTimer.triggerNow();\n }\n\n /** This channel polls a remote itself; it has no holder to hear from. */\n notePoll(): void {}\n\n /** No holder, so nothing this channel reports may strand one. */\n lastHolderPollUtcMs(): number | undefined {\n return undefined;\n }\n\n setLocalManifest(provider: () => PeerManifest): void {\n this.localManifestProvider = provider;\n }\n\n onPeerManifest(callback: PeerManifestListener): () => void {\n this.peerManifestCallbacks.add(callback);\n return () => {\n this.peerManifestCallbacks.delete(callback);\n };\n }\n\n forgetMarkerRefusal(documentId: string, branch: string): void {\n this.refusedMarkers.delete(`${documentId}\\u0000${branch}`);\n }\n\n private async hearPeer(\n manifest: PeerManifest | null,\n undelivered?: readonly SyncOperation[],\n ): Promise<void> {\n if (\n (this.peerManifest !== undefined &&\n (this.peerManifest?.revision ?? null) ===\n (manifest?.revision ?? null)) ||\n isOlderManifest(manifest, this.peerManifest)\n ) {\n return;\n }\n this.peerManifest = manifest;\n await Promise.all(\n [...this.peerManifestCallbacks].map(async (callback) => {\n try {\n await callback(manifest, undelivered);\n } catch (error) {\n this.logger.error(\"Peer manifest callback error: @Error\", error);\n }\n }),\n );\n }\n\n /**\n * Re-touches once when either side's manifest moved; touching is idempotent.\n * False when the refresh failed, so polled rows must not be judged yet.\n */\n private async refreshManifestsIfStale(\n manifestRevision: string | null | undefined,\n peerManifestRevision: string | null | undefined,\n ): Promise<boolean> {\n if (!this.peerServesAgreement || typeof manifestRevision !== \"string\") {\n return true;\n }\n const local = this.localManifestProvider?.().revision ?? null;\n if (\n manifestRevision === (this.peerManifest?.revision ?? null) &&\n (peerManifestRevision ?? null) === local\n ) {\n return true;\n }\n if (this.isShutdown) {\n return false;\n }\n this.manifestRefresh ??= this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n })\n .finally(() => {\n this.manifestRefresh = undefined;\n });\n try {\n await this.manifestRefresh;\n return true;\n } catch (error) {\n this.logger.error(\n \"GqlChannel @ChannelId manifest refresh failed: @Error\",\n this.channelId,\n error,\n );\n return false;\n }\n }\n\n /** Re-touches a silent remote in case it was upgraded. */\n private async probeAgreement(): Promise<void> {\n this.agreementStoppedUtcMs = Date.now();\n try {\n const { ackOrdinal } = await this.touchRemoteChannel();\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n } catch (error) {\n this.logger.error(\n \"GqlChannel @ChannelId agreement probe failed: @Error\",\n this.channelId,\n error,\n );\n }\n }\n\n /**\n * Initializes the channel by registering it on the remote server and starting polling.\n */\n async init(): Promise<void> {\n const { ackOrdinal } = await this.touchRemoteChannel();\n\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n\n this.pollTimer.setDelegate(() => this.poll());\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (next === \"connected\") this.requiresAuth = false;\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n /**\n * Polls the remote for new sync envelopes.\n */\n private async poll(): Promise<void> {\n if (this.isShutdown) {\n return;\n }\n\n if (\n !this.peerServesAgreement &&\n Date.now() - this.agreementStoppedUtcMs >= AGREEMENT_PROBE_INTERVAL_MS\n ) {\n await this.probeAgreement();\n }\n\n let response;\n try {\n response = await this.pollSyncEnvelopes(\n this.inbox.ackOrdinal,\n this.inbox.latestOrdinal,\n );\n } catch (error) {\n if (!this.handlePollError(error)) {\n throw error;\n }\n return;\n }\n\n const {\n envelopes,\n ackOrdinal,\n deadLetters,\n hasMore,\n manifestRevision,\n peerManifestRevision,\n } = response;\n\n // first: trim outbox\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.retireRefusedMarkers(deadLetters);\n this.repushUnackedMarkers();\n\n // Rows are judged against the sender's record, so a stale one is refreshed\n // first. Unadmitted rows stay unacked and are served again.\n if (\n !(await this.refreshManifestsIfStale(\n manifestRevision,\n peerManifestRevision,\n ))\n ) {\n return;\n }\n\n // convert the envelopes to sync operations\n const allSyncOps: SyncOperation[] = [];\n for (const envelope of envelopes) {\n if (envelope.type.toLowerCase() === \"operations\" && envelope.operations) {\n const syncOps = envelopesToSyncOperations(envelope, this.remoteName);\n for (const syncOp of syncOps) {\n syncOp.transported();\n }\n allSyncOps.push(...syncOps);\n }\n }\n\n // merge SyncOps sharing the same (documentId, scope, branch) so\n // multiple polled envelopes for one document become a single load job\n const consolidated =\n allSyncOps.length > 1\n ? consolidateSyncOperations(allSyncOps)\n : allSyncOps;\n\n if (consolidated.length > 0) {\n this.inbox.add(...consolidated);\n }\n\n // handle dead letters from the remote\n if (deadLetters.length > 0) {\n this.handleRemoteDeadLetters(deadLetters);\n }\n\n if (hasMore) {\n this.receivingPages = true;\n } else if (this.receivingPages) {\n this.receivingPages = false;\n this.drainOutbox();\n }\n\n this.lastSuccessUtcMs = Date.now();\n this.failureCount = 0;\n this.transitionConnectionState(\"connected\");\n }\n\n /**\n * Handles dead letters reported by the remote server.\n * Creates local dead letter SyncOperations so the channel quiesces.\n */\n private handleRemoteDeadLetters(wire: DeadLetterWire[]): void {\n const deadLetters = wire.filter((dl) => {\n if (dl.errorType !== \"MARKER_REFUSED\") return true;\n const key = `${dl.documentId}\\u0000${dl.branch}`;\n if (this.refusedMarkers.has(key)) return false;\n this.refusedMarkers.add(key);\n return true;\n });\n if (deadLetters.length === 0) return;\n for (const dl of deadLetters) {\n this.logger.error(\n \"Remote dead letter on @ChannelId: document @DocumentId failed with: @Error\",\n this.channelId,\n dl.documentId,\n dl.error,\n );\n }\n\n const syncOps: SyncOperation[] = [];\n for (const dl of deadLetters) {\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n dl.jobId,\n [],\n this.remoteName,\n dl.documentId,\n dl.scopes,\n dl.branch,\n [],\n );\n // Only the message crosses the wire, so the origin's classification is\n // carried explicitly. Without it this replica would freeze a document the\n // origin is deliberately still syncing.\n syncOp.failed(\n new ChannelError(\n ChannelErrorSource.Outbox,\n new Error(dl.error),\n (dl.errorType ?? undefined) as SyncOperationErrorType | undefined,\n ),\n );\n syncOps.push(syncOp);\n }\n this.deadLetter.add(...syncOps);\n }\n\n /**\n * Handles polling errors with error classification.\n * Returns true if the error was handled (caller should not rethrow).\n */\n private handlePollError(error: unknown): boolean {\n if (this.isShutdown) return true;\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (err.message.includes(\"Channel not found\")) {\n this.transitionConnectionState(\"reconnecting\");\n this.recoverFromChannelNotFound();\n return true;\n }\n\n const classification = this.classifyError(err);\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n\n this.logger.error(\n \"GqlChannel poll error (@FailureCount, @Classification): @Error\",\n this.failureCount,\n classification,\n channelError,\n );\n\n if (classification === \"unrecoverable\") {\n this.pollTimer.stop();\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n return true;\n }\n\n this.transitionConnectionState(\"error\");\n return false;\n }\n\n /**\n * Recovers from a \"Channel not found\" error by re-registering and restarting polling.\n * Self-retries with backoff instead of restarting the poll timer on failure.\n */\n private recoverFromChannelNotFound(): void {\n if (this.isRecovering) return;\n this.isRecovering = true;\n\n this.logger.info(\n \"GqlChannel @ChannelId not found on remote, re-registering...\",\n this.channelId,\n );\n\n this.pollTimer.stop();\n\n const attemptRecovery = (attempt: number): void => {\n if (this.isShutdown) {\n this.isRecovering = false;\n return;\n }\n\n void this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n this.logger.info(\n \"GqlChannel @ChannelId re-registered successfully\",\n this.channelId,\n );\n this.isRecovering = false;\n this.failureCount = 0;\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\n this.resumePushAfterRecovery();\n })\n .catch((recoveryError: unknown) => {\n const err =\n recoveryError instanceof Error\n ? recoveryError\n : new Error(String(recoveryError));\n const classification = this.classifyError(err);\n\n this.logger.error(\n \"GqlChannel @ChannelId recovery attempt @Attempt failed (@Classification): @Error\",\n this.channelId,\n attempt,\n classification,\n recoveryError,\n );\n\n this.failureCount++;\n this.lastFailureUtcMs = Date.now();\n\n if (classification === \"unrecoverable\") {\n this.isRecovering = false;\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n return;\n }\n\n this.transitionConnectionState(\"reconnecting\");\n const delay = calculateBackoffDelay(\n attempt,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n setTimeout(() => attemptRecovery(attempt + 1), delay);\n });\n };\n\n attemptRecovery(1);\n }\n\n /**\n * Resumes pushing outbox items that were blocked while the channel was being\n * recreated. Called after a successful re-registration.\n */\n private resumePushAfterRecovery(): void {\n if (this.isShutdown) return;\n if (!this.pushBlocked) return;\n\n if (this.pushRetryTimer) {\n clearTimeout(this.pushRetryTimer);\n this.pushRetryTimer = null;\n }\n\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n\n const items = this.outbox.items;\n if (items.length > 0) {\n this.attemptPush([...items]);\n }\n }\n\n /**\n * Queries the remote GraphQL endpoint for sync envelopes.\n */\n private async pollSyncEnvelopes(\n ackOrdinal: number,\n latestOrdinal: number,\n ): Promise<{\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters: DeadLetterWire[];\n hasMore: boolean;\n manifestRevision?: string | null;\n peerManifestRevision?: string | null;\n }> {\n const variables = {\n channelId: this.channelId,\n outboxAck: ackOrdinal,\n outboxLatest: latestOrdinal,\n };\n\n // Each flag only ever clears, so this settles within three attempts.\n let response: PollSyncEnvelopesResult;\n let refusals: RefusalWire[] = [];\n for (;;) {\n const revision = this.peerServesAgreement\n ? this.localManifestProvider?.().revision\n : undefined;\n refusals = this.peerServesAgreement\n ? [...this.pendingRefusals.values()]\n .filter(\n (refusal) =>\n refusal.kind === undefined || this.peerTakesMarkerRefusals(),\n )\n .slice(0, MAX_POLLED_REFUSALS)\n : [];\n try {\n response = await this.executeGraphQL<PollSyncEnvelopesResult>(\n this.pollQuery(\n this.peerServesDecisionFields,\n this.peerServesAgreement,\n ),\n this.peerServesAgreement\n ? { ...variables, manifestRevision: revision, refusals }\n : variables,\n );\n break;\n } catch (error) {\n if (this.rejectsAgreementFields(error)) {\n await this.stopAgreement();\n continue;\n }\n if (!this.rejectsDecisionFields(error)) {\n throw error;\n }\n this.logger.warn(\n \"Remote @channelId does not serve deniedReason/errorType; polling without them. The remote is on an older schema, so it has neither to report.\",\n this.channelId,\n );\n this.peerServesDecisionFields = false;\n }\n }\n\n // The server holds what was reported; a silent one keeps them pending.\n for (const refusal of refusals) {\n this.pendingRefusals.delete(refusalKey(refusal));\n }\n\n return {\n envelopes: response.pollSyncEnvelopes.envelopes,\n ackOrdinal: response.pollSyncEnvelopes.ackOrdinal,\n deadLetters: response.pollSyncEnvelopes.deadLetters ?? [],\n hasMore: response.pollSyncEnvelopes.hasMore,\n manifestRevision: response.pollSyncEnvelopes.manifestRevision,\n peerManifestRevision: response.pollSyncEnvelopes.peerManifestRevision,\n };\n }\n\n /** A peer before the feature would reject the field; its refusals wait. */\n private peerTakesMarkerRefusals(): boolean {\n return (\n this.peerManifest?.features[MARKER_REFUSAL_FEATURE]?.includes(1) === true\n );\n }\n\n private rejectsAgreementFields(error: unknown): boolean {\n return this.peerServesAgreement && this.isAgreementRejection(error);\n }\n\n private isAgreementRejection(error: unknown): boolean {\n if (\n !(error instanceof GraphQLRequestError) ||\n error.category !== \"graphql\" ||\n isRecoverableGraphQLError(error)\n ) {\n return false;\n }\n return AGREEMENT_FIELDS.some((field) => error.message.includes(field));\n }\n\n /** Resolves once what the silent peer cannot run is held. */\n private async stopAgreement(\n undelivered?: readonly SyncOperation[],\n ): Promise<void> {\n this.logger.warn(\n \"Remote @channelId does not serve peer manifests; treating it as a silent peer.\",\n this.channelId,\n );\n this.peerServesAgreement = false;\n this.agreementStoppedUtcMs = Date.now();\n await this.hearPeer(null, undelivered);\n }\n\n /**\n * True when the remote rejected the query for naming a field it does not\n * have. Selecting an unknown field fails validation for the whole query, so\n * an unhandled one takes the channel's polling down until the process\n * restarts rather than degrading.\n */\n private rejectsDecisionFields(error: unknown): boolean {\n if (!this.peerServesDecisionFields) {\n return false;\n }\n if (\n !(error instanceof GraphQLRequestError) ||\n error.category !== \"graphql\"\n ) {\n return false;\n }\n return DECISION_FIELDS.some((field) => error.message.includes(field));\n }\n\n /**\n * The poll query. `withDecisionFields` selects the two fields added with the\n * auth projection; a remote on the previous schema is polled without them.\n */\n private pollQuery(\n withDecisionFields: boolean,\n withAgreementFields: boolean,\n ): string {\n const deniedReason = withDecisionFields ? \"deniedReason\" : \"\";\n const errorType = withDecisionFields ? \"errorType\" : \"\";\n const revisions = withAgreementFields\n ? \"manifestRevision\\n peerManifestRevision\"\n : \"\";\n\n const revisionVariable = withAgreementFields\n ? \", $manifestRevision: String, $refusals: [SyncRefusalInput!]\"\n : \"\";\n const revisionArgument = withAgreementFields\n ? \", manifestRevision: $manifestRevision, refusals: $refusals\"\n : \"\";\n\n return `\n query PollSyncEnvelopes($channelId: String!, $outboxAck: Int!, $outboxLatest: Int!${revisionVariable}) {\n pollSyncEnvelopes(channelId: $channelId, outboxAck: $outboxAck, outboxLatest: $outboxLatest${revisionArgument}) {\n envelopes {\n type\n channelMeta {\n id\n }\n operations {\n operation {\n index\n timestampUtcMs\n hash\n skip\n error\n ${deniedReason}\n id\n action {\n id\n type\n timestampUtcMs\n input\n scope\n context {\n signer {\n user {\n address\n networkId\n chainId\n }\n app {\n name\n key\n }\n signatures\n }\n }\n }\n }\n context {\n documentId\n documentType\n scope\n branch\n ordinal\n }\n }\n cursor {\n remoteName\n cursorOrdinal\n lastSyncedAtUtcMs\n }\n key\n dependsOn\n }\n ackOrdinal\n deadLetters {\n documentId\n error\n ${errorType}\n jobId\n branch\n scopes\n operationCount\n }\n hasMore\n ${revisions}\n }\n }\n `;\n }\n\n /**\n * Registers or updates this channel on the remote server via GraphQL mutation.\n * Returns the remote's ack ordinal so the client can trim its outbox.\n */\n private async touchRemoteChannel(): Promise<{ ackOrdinal: number }> {\n let sinceTimestampUtcMs = \"0\";\n try {\n const result = await this.operationIndex.getLatestTimestampForCollection(\n this.config.collectionId.key,\n );\n if (result) {\n sinceTimestampUtcMs = result;\n }\n } catch {\n // If query fails, use default \"0\" (sends all operations)\n }\n\n const touch = (withAgreement: boolean) => {\n const manifest = withAgreement\n ? this.localManifestProvider?.()\n : undefined;\n return this.executeGraphQL<{\n touchChannel: {\n success: boolean;\n ackOrdinal: number;\n manifest?: unknown;\n };\n }>(\n `\n mutation TouchChannel($input: TouchChannelInput!) {\n touchChannel(input: $input) {\n success\n ackOrdinal\n ${withAgreement ? \"manifest\" : \"\"}\n }\n }\n `,\n {\n input: {\n id: this.channelId,\n name: this.channelId,\n collectionId: this.config.collectionId.key,\n filter: {\n documentId: this.config.filter.documentId,\n scope: this.config.filter.scope,\n branch: this.config.filter.branch,\n },\n sinceTimestampUtcMs,\n ...(manifest ? { manifest } : {}),\n },\n },\n );\n };\n\n // Every touch asks for agreement, so an upgraded remote is heard again.\n let data;\n try {\n data = await touch(true);\n this.peerServesAgreement = true;\n } catch (error) {\n if (!this.isAgreementRejection(error)) {\n throw error;\n }\n await this.stopAgreement();\n data = await touch(false);\n }\n\n if (!data.touchChannel.success) {\n throw new GraphQLRequestError(\n \"touchChannel returned success=false\",\n \"graphql\",\n );\n }\n\n await this.hearPeer(\n this.peerServesAgreement\n ? readPeerManifest(data.touchChannel.manifest)\n : null,\n );\n\n return { ackOrdinal: data.touchChannel.ackOrdinal };\n }\n\n /**\n * Fire-and-forget push with retry on recoverable errors.\n * On success, clears push blocked state. On recoverable error, blocks\n * further pushes and schedules a retry. On unrecoverable error, moves\n * ops to deadLetter.\n */\n private attemptPush(syncOps: SyncOperation[]): void {\n this.isPushing = true;\n this.pushSyncOperations(syncOps)\n .then(() => {\n this.isPushing = false;\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n if (\n this.connectionState === \"reconnecting\" ||\n this.connectionState === \"error\"\n ) {\n this.transitionConnectionState(\"connected\");\n }\n this.drainOutbox();\n })\n .catch((error) => {\n this.isPushing = false;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (err.message.includes(\"Channel not found\")) {\n this.pushBlocked = true; // keep ops in outbox; retried after recovery\n this.transitionConnectionState(\"reconnecting\");\n this.recoverFromChannelNotFound();\n return;\n }\n\n const classification = this.classifyError(err);\n\n if (classification === \"recoverable\") {\n this.pushFailureCount++;\n this.pushBlocked = true;\n this.logger.error(\n \"GqlChannel push failed (attempt @FailureCount), will retry: @Error\",\n this.pushFailureCount,\n err,\n );\n this.transitionConnectionState(\"reconnecting\");\n this.schedulePushRetry();\n } else {\n const channelError = new ChannelError(ChannelErrorSource.Outbox, err);\n for (const syncOp of syncOps) {\n syncOp.failed(channelError);\n }\n this.deadLetter.add(...syncOps);\n this.outbox.remove(...syncOps);\n this.requiresAuth = isDriveAuthError(err);\n this.transitionConnectionState(\"error\");\n }\n });\n }\n\n /** Stops pushing a marker entry the remote refused; its wire has no op id. */\n private retireRefusedMarkers(deadLetters: DeadLetterWire[]): void {\n const refused = deadLetters.filter(\n (dl) => dl.errorType === \"MARKER_REFUSED\",\n );\n if (refused.length === 0) return;\n const retired = this.outbox.items.filter(\n (syncOp) =>\n this.markerPushedAt.has(syncOp.id) &&\n syncOp.status !== SyncOperationStatus.Applied &&\n refused.some(\n (dl) =>\n dl.documentId === syncOp.documentId && dl.branch === syncOp.branch,\n ),\n );\n if (retired.length === 0) return;\n for (const syncOp of retired) {\n syncOp.failed(\n new ChannelError(\n ChannelErrorSource.Outbox,\n new Error(`Remote refused the purge marker of ${syncOp.documentId}`),\n \"MARKER_REFUSED\",\n ),\n );\n }\n this.outbox.remove(...retired);\n }\n\n /** Re-pushes markers unacked for retryMaxDelayMs: a restarted remote lost them. */\n private repushUnackedMarkers(): void {\n if (this.isPushing || this.pushBlocked || this.receivingPages) return;\n const due = Date.now() - this.config.retryMaxDelayMs;\n const stale = this.outbox.items.filter((syncOp) => {\n const pushedAt = this.markerPushedAt.get(syncOp.id);\n return (\n pushedAt !== undefined &&\n pushedAt <= due &&\n syncOp.status !== SyncOperationStatus.Applied\n );\n });\n if (stale.length > 0) this.attemptPush(stale);\n }\n\n /**\n * Schedules a retry of all current outbox items using exponential backoff.\n */\n private schedulePushRetry(): void {\n if (this.pushRetryTimer) return;\n\n const delay = calculateBackoffDelay(\n this.pushFailureCount,\n this.config.retryBaseDelayMs,\n this.config.retryMaxDelayMs,\n Math.random(),\n );\n\n this.pushRetryTimer = setTimeout(() => {\n this.pushRetryTimer = null;\n\n if (this.isShutdown) return;\n\n const allItems = this.outbox.items;\n if (allItems.length === 0) {\n this.pushBlocked = false;\n this.pushFailureCount = 0;\n return;\n }\n\n this.attemptPush([...allItems]);\n }, delay);\n }\n\n /**\n * Drains pending outbox items that arrived while a push was in-flight.\n * Server-side action.id dedup handles any overlap with the previous push.\n */\n private drainOutbox(): void {\n if (!this.pendingDrain) return;\n this.pendingDrain = false;\n if (this.isShutdown) return;\n const items = this.outbox.items;\n if (items.length === 0) return;\n this.attemptPush([...items]);\n }\n\n /**\n * Classifies an error as recoverable or unrecoverable based on its type.\n * Recoverable errors are transient and worth retrying (network, 5xx, parse).\n * Unrecoverable errors will not self-heal (auth, client errors, GraphQL rejections).\n */\n private classifyError(error: Error): \"recoverable\" | \"unrecoverable\" {\n if (!(error instanceof GraphQLRequestError)) {\n return \"recoverable\";\n }\n\n switch (error.category) {\n case \"network\":\n return \"recoverable\";\n case \"http\": {\n if (error.statusCode !== undefined && error.statusCode >= 500) {\n return \"recoverable\";\n }\n return \"unrecoverable\";\n }\n case \"parse\":\n return \"recoverable\";\n case \"graphql\":\n // A remote that classified the failure as worth polling through is\n // taken at its word. Everything else stays permanent: it stops the poll\n // timer, and nothing restarts it.\n return isRecoverableGraphQLError(error)\n ? \"recoverable\"\n : \"unrecoverable\";\n case \"missing-data\":\n return \"unrecoverable\";\n }\n }\n\n /**\n * Pushes multiple sync operations to the remote via a single GraphQL mutation.\n * Creates one SyncEnvelope per SyncOperation with key/dependsOn for batch ordering.\n */\n private async pushSyncOperations(syncOps: SyncOperation[]): Promise<void> {\n const now = Date.now();\n for (const syncOp of syncOps) {\n syncOp.started();\n if (syncOp.operations.some((op) => isPurgeMarker(op))) {\n this.markerPushedAt.set(syncOp.id, now);\n }\n }\n\n // The server revision this push was gated under; a pre-feature server\n // rejects it, which is how a rollback of the server is noticed.\n const gatedUnder = this.peerServesAgreement\n ? this.peerManifest?.revision\n : undefined;\n try {\n await this.executeGraphQL<{ pushSyncEnvelopes: boolean }>(\n pushMutation(gatedUnder !== undefined),\n {\n envelopes: this.envelopesFor(syncOps),\n ...(gatedUnder !== undefined\n ? { peerManifestRevision: gatedUnder }\n : {}),\n },\n );\n return;\n } catch (error) {\n if (gatedUnder === undefined || !this.rejectsAgreementFields(error)) {\n throw error;\n }\n }\n\n // Resending the same envelopes would deliver what the silent peer cannot\n // run, so those are held first.\n await this.stopAgreement(syncOps);\n const unsent = new Set(this.outbox.items);\n const remaining = syncOps.filter((syncOp) => unsent.has(syncOp));\n if (remaining.length === 0) return;\n await this.executeGraphQL<{ pushSyncEnvelopes: boolean }>(\n pushMutation(false),\n { envelopes: this.envelopesFor(remaining) },\n );\n }\n\n /** One envelope per SyncOperation, with key/dependsOn for batch ordering. */\n private envelopesFor(syncOps: SyncOperation[]): unknown[] {\n const jobIdToKeys = new Map<string, string[]>();\n const envelopes: SyncEnvelope[] = [];\n\n for (let i = 0; i < syncOps.length; i++) {\n const syncOp = syncOps[i];\n const key = String(i);\n\n if (syncOp.jobId) {\n if (!jobIdToKeys.has(syncOp.jobId)) {\n jobIdToKeys.set(syncOp.jobId, []);\n }\n jobIdToKeys.get(syncOp.jobId)!.push(key);\n }\n\n const dependsOn: string[] = [];\n for (const dep of syncOp.jobDependencies) {\n const depKeys = jobIdToKeys.get(dep);\n if (depKeys) {\n dependsOn.push(...depKeys);\n }\n }\n\n this.logger.debug(\n \"[PUSH]: @Operations\",\n syncOp.operations.map(\n (op) =>\n `(${op.context.documentId}, ${op.context.branch}, ${op.context.scope}, ${op.operation.index})`,\n ),\n );\n\n envelopes.push({\n type: \"operations\",\n channelMeta: { id: this.channelId },\n operations: syncOp.operations,\n key,\n dependsOn,\n });\n }\n\n return envelopes.map((e) => serializeEnvelope(e));\n }\n\n /**\n * Gets the authorization header value using jwtHandler.\n */\n private async getAuthorizationHeader(): Promise<string | undefined> {\n if (!this.config.jwtHandler) {\n return undefined;\n }\n\n try {\n const token = await this.config.jwtHandler(this.config.url);\n if (token) {\n return `Bearer ${token}`;\n }\n } catch (error) {\n this.logger.error(\"JWT handler failed: @Error\", error);\n }\n return undefined;\n }\n\n /**\n * Executes a GraphQL query or mutation against the remote endpoint.\n */\n private async executeGraphQL<T>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n const authHeader = await this.getAuthorizationHeader();\n if (authHeader) {\n headers[\"Authorization\"] = authHeader;\n }\n\n const operationMatch = query.match(/(?:query|mutation)\\s+(\\w+)/);\n const operationName = operationMatch?.[1] ?? \"unknown\";\n\n this.logger.verbose(\n \"GQL request @channelId @operation @url vars=@variables\",\n this.channelId,\n operationName,\n this.config.url,\n JSON.stringify(variables),\n );\n\n const fetchFn = this.config.fetchFn ?? fetch;\n let response;\n try {\n response = await fetchFn(this.config.url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query,\n variables,\n }),\n signal: this.abortController.signal,\n });\n } catch (error) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${error instanceof Error ? error.message : String(error)}`,\n \"network\",\n );\n }\n\n if (!response.ok) {\n throw new GraphQLRequestError(\n `GraphQL request failed: ${response.status} ${response.statusText}`,\n \"http\",\n response.status,\n );\n }\n\n let result;\n try {\n result = (await response.json()) as {\n data?: T;\n errors?: Array<{ message: string; extensions?: { code?: string } }>;\n };\n } catch (error) {\n throw new GraphQLRequestError(\n `Failed to parse GraphQL response: ${error instanceof Error ? error.message : String(error)}`,\n \"parse\",\n );\n }\n\n this.logger.verbose(\n \"GQL response @channelId @operation status=@status data=@data errors=@errors\",\n this.channelId,\n operationName,\n response.status,\n JSON.stringify(result.data),\n result.errors ? JSON.stringify(result.errors) : \"none\",\n );\n\n if (result.errors) {\n throw new GraphQLRequestError(\n `GraphQL errors: ${JSON.stringify(result.errors, null, 2)}`,\n \"graphql\",\n undefined,\n result.errors.map((error) => error.extensions?.code),\n );\n }\n\n if (!result.data) {\n throw new GraphQLRequestError(\n \"GraphQL response missing data field\",\n \"missing-data\",\n );\n }\n\n return result.data;\n }\n\n get poller(): IPollTimer {\n return this.pollTimer;\n }\n\n /** The lowest ordinal of an outbox item the remote has not applied. */\n private unappliedFloor(): number {\n let floor = Number.POSITIVE_INFINITY;\n for (const syncOp of this.outbox.items) {\n if (syncOp.status === SyncOperationStatus.Applied) continue;\n for (const op of syncOp.operations) {\n const ordinal = op.context.ordinal;\n if (ordinal > 0 && ordinal < floor) floor = ordinal;\n }\n }\n return floor;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../../cache/operation-index-types.js\";\nimport type { IQueue } from \"../../queue/interfaces.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type {\n ChannelConfig,\n JwtHandler,\n RemoteFilter,\n RemoteOptions,\n} from \"../types.js\";\nimport { PollBehavior } from \"../types.js\";\nimport { GqlRequestChannel, type GqlChannelConfig } from \"./gql-req-channel.js\";\nimport { IntervalPollTimer } from \"./interval-poll-timer.js\";\n\n/**\n * Factory for creating GqlRequestChannel instances.\n *\n * Extracts GraphQL-specific configuration from ChannelConfig.parameters and\n * instantiates GqlRequestChannel instances for network-based synchronization.\n *\n * The optional jwtHandler enables dynamic JWT token generation per-request,\n * which is useful for short-lived tokens with audience-specific claims.\n */\nexport class GqlRequestChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n private readonly jwtHandler?: JwtHandler;\n private readonly queue: IQueue;\n\n constructor(\n logger: ILogger,\n jwtHandler: JwtHandler | undefined,\n queue: IQueue,\n ) {\n this.logger = logger;\n this.jwtHandler = jwtHandler;\n this.queue = queue;\n }\n\n /**\n * Creates a new GqlRequestChannel instance with the given configuration.\n * See GqlChannelConfig for the expected parameters.\n *\n * @param config - Channel configuration including type and parameters\n * @param cursorStorage - Storage for persisting synchronization cursors\n * @param operationIndex - Operation index for querying timestamps\n * @returns A new GqlRequestChannel instance\n */\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n collectionId: DriveCollectionId,\n filter: RemoteFilter,\n operationIndex: IOperationIndex,\n options?: RemoteOptions,\n ): IChannel {\n // Extract and validate required parameters\n const url = config.parameters.url;\n if (typeof url !== \"string\" || !url) {\n throw new Error(\n 'GqlRequestChannelFactory requires \"url\" parameter in config.parameters',\n );\n }\n\n // Extract optional parameters with validation\n const gqlConfig: GqlChannelConfig = {\n url,\n collectionId,\n filter,\n jwtHandler: this.jwtHandler,\n retryBaseDelayMs: 1000,\n retryMaxDelayMs: 300000,\n };\n\n let pollIntervalMs = 2000;\n if (config.parameters.pollIntervalMs !== undefined) {\n if (typeof config.parameters.pollIntervalMs !== \"number\") {\n throw new Error('\"pollIntervalMs\" parameter must be a number');\n }\n pollIntervalMs = config.parameters.pollIntervalMs;\n }\n\n let retryBaseDelayMs: number | undefined;\n if (config.parameters.retryBaseDelayMs !== undefined) {\n if (typeof config.parameters.retryBaseDelayMs !== \"number\") {\n throw new Error('\"retryBaseDelayMs\" parameter must be a number');\n }\n retryBaseDelayMs = config.parameters.retryBaseDelayMs;\n }\n\n let retryMaxDelayMs: number | undefined;\n if (config.parameters.retryMaxDelayMs !== undefined) {\n if (typeof config.parameters.retryMaxDelayMs !== \"number\") {\n throw new Error('\"retryMaxDelayMs\" parameter must be a number');\n }\n retryMaxDelayMs = config.parameters.retryMaxDelayMs;\n }\n\n if (config.parameters.fetchFn !== undefined) {\n if (typeof config.parameters.fetchFn !== \"function\") {\n throw new Error('\"fetchFn\" parameter must be a function');\n }\n gqlConfig.fetchFn = config.parameters.fetchFn as typeof fetch;\n }\n\n if (retryBaseDelayMs !== undefined) {\n gqlConfig.retryBaseDelayMs = retryBaseDelayMs;\n }\n if (retryMaxDelayMs !== undefined) {\n gqlConfig.retryMaxDelayMs = retryMaxDelayMs;\n }\n\n let maxQueueDepth: number | undefined;\n if (config.parameters.maxQueueDepth !== undefined) {\n if (typeof config.parameters.maxQueueDepth !== \"number\") {\n throw new Error('\"maxQueueDepth\" parameter must be a number');\n }\n maxQueueDepth = config.parameters.maxQueueDepth;\n }\n\n let backpressureCheckIntervalMs: number | undefined;\n if (config.parameters.backpressureCheckIntervalMs !== undefined) {\n if (typeof config.parameters.backpressureCheckIntervalMs !== \"number\") {\n throw new Error(\n '\"backpressureCheckIntervalMs\" parameter must be a number',\n );\n }\n backpressureCheckIntervalMs =\n config.parameters.backpressureCheckIntervalMs;\n }\n\n const pollTimer = new IntervalPollTimer(this.queue, {\n intervalMs: pollIntervalMs,\n ...(retryBaseDelayMs !== undefined && { retryBaseDelayMs }),\n ...(retryMaxDelayMs !== undefined && { retryMaxDelayMs }),\n ...(maxQueueDepth !== undefined && { maxQueueDepth }),\n ...(backpressureCheckIntervalMs !== undefined && {\n backpressureCheckIntervalMs,\n }),\n startPaused: options?.pollBehavior === PollBehavior.Manual,\n });\n\n return new GqlRequestChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n gqlConfig,\n operationIndex,\n pollTimer,\n );\n }\n}\n","import type { PeerManifest } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { ConnectionStateChangeCallback, IChannel } from \"../interfaces.js\";\nimport { Mailbox } from \"../mailbox.js\";\nimport type { SyncOperation } from \"../sync-operation.js\";\nimport type { ConnectionState, ConnectionStateSnapshot } from \"../types.js\";\nimport { SyncOperationStatus } from \"../types.js\";\nimport { getLatestAppliedOrdinal } from \"./utils.js\";\n\n/** Where a sync operation's run of ordinals begins. */\nfunction firstOrdinalOf(syncOp: SyncOperation): number {\n return syncOp.operations.length > 0\n ? syncOp.operations[0].context.ordinal\n : 0;\n}\n\n/**\n * This class is used server-side to accumulate inbox + outbox operations.\n *\n * In general, the resolvers are responsible for updating mailboxes.\n */\nexport class GqlResponseChannel implements IChannel {\n readonly inbox: Mailbox;\n readonly outbox: Mailbox;\n readonly deadLetter: Mailbox;\n\n private readonly channelId: string;\n private readonly remoteName: string;\n private readonly cursorStorage: ISyncCursorStorage;\n private isShutdown: boolean;\n private lastPersistedInboxOrdinal: number = 0;\n private lastPersistedOutboxOrdinal: number = 0;\n private evictedOutboxFloor: number = Number.POSITIVE_INFINITY;\n private appliedOutboxOrdinal: number = 0;\n private lastPollUtcMs: number = Date.now();\n private connectionState: ConnectionState = \"connecting\";\n private readonly connectionStateCallbacks: Set<ConnectionStateChangeCallback> =\n new Set();\n\n constructor(\n private readonly logger: ILogger,\n channelId: string,\n remoteName: string,\n cursorStorage: ISyncCursorStorage,\n ) {\n this.channelId = channelId;\n this.remoteName = remoteName;\n this.cursorStorage = cursorStorage;\n this.isShutdown = false;\n\n this.inbox = new Mailbox({ holdAckBelowMarkers: true });\n this.outbox = new Mailbox();\n this.deadLetter = new Mailbox();\n\n // Instead of listening to syncops directly for cursor updates, we listen\n // to the mailbox. This is for efficiency: many syncops may fire on a trim,\n // but only one onRemoved callback will be fired for the batch.\n this.outbox.onRemoved((syncOps) => {\n this.rememberUnserved(syncOps);\n this.persistOutboxCursor(getLatestAppliedOrdinal(syncOps));\n });\n\n // An entry arriving at or below the evicted floor is the re-derivation of\n // what was evicted: it is queued again, so the queue itself now holds the\n // floor down and the remembered one would only pin the cursor for good.\n this.outbox.onAdded((syncOps) => {\n this.forgetEvictedBelow(syncOps);\n });\n\n // The inbox ack, which never passes a marker still awaiting its load.\n this.inbox.onRemoved(() => {\n const maxOrdinal = this.inbox.ackOrdinal;\n if (maxOrdinal > this.lastPersistedInboxOrdinal) {\n this.lastPersistedInboxOrdinal = maxOrdinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"inbox\",\n cursorOrdinal: maxOrdinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update inbox cursor for @ChannelId! This is unlikely to cause a problem, but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n });\n }\n\n shutdown(): Promise<void> {\n this.isShutdown = true;\n this.transitionConnectionState(\"disconnected\");\n return Promise.resolve();\n }\n\n getConnectionState(): ConnectionStateSnapshot {\n return {\n state: this.connectionState,\n failureCount: 0,\n lastSuccessUtcMs: this.lastPollUtcMs,\n lastFailureUtcMs: 0,\n pushBlocked: false,\n pushFailureCount: 0,\n receivingPages: false,\n requiresAuth: false,\n };\n }\n\n onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void {\n this.connectionStateCallbacks.add(callback);\n return () => {\n this.connectionStateCallbacks.delete(callback);\n };\n }\n\n /** Response channels are push-driven; resolvers populate mailboxes directly. */\n triggerPull(): void {}\n\n notePoll(): void {\n this.lastPollUtcMs = Date.now();\n }\n\n /** This channel is served: its holder's polls are the liveness it reports. */\n lastHolderPollUtcMs(): number | undefined {\n return this.lastPollUtcMs;\n }\n\n /** The client announces through touchChannel, which the sync manager serves. */\n setLocalManifest(_provider: () => PeerManifest): void {}\n\n onPeerManifest(\n _callback: (manifest: PeerManifest | null) => void,\n ): () => void {\n return () => {};\n }\n\n async init(): Promise<void> {\n // get cursors -- these are the last acknowledged ordinals for the inbox and outbox\n const cursors = await this.cursorStorage.list(this.remoteName);\n const inboxOrdinal =\n cursors.find((c) => c.cursorType === \"inbox\")?.cursorOrdinal ?? 0;\n const outboxOrdinal =\n cursors.find((c) => c.cursorType === \"outbox\")?.cursorOrdinal ?? 0;\n this.inbox.init(inboxOrdinal);\n this.outbox.init(outboxOrdinal);\n this.lastPersistedInboxOrdinal = inboxOrdinal;\n this.lastPersistedOutboxOrdinal = outboxOrdinal;\n this.appliedOutboxOrdinal = outboxOrdinal;\n this.transitionConnectionState(\"connected\");\n }\n\n private transitionConnectionState(next: ConnectionState): void {\n if (this.connectionState === next) return;\n this.connectionState = next;\n const snapshot = this.getConnectionState();\n for (const callback of this.connectionStateCallbacks) {\n try {\n callback(snapshot);\n } catch (error) {\n this.logger.error(\n \"Connection state change callback error: @Error\",\n error,\n );\n }\n }\n }\n\n /**\n * Records the ordinals of entries that left the outbox without being served,\n * so the cursor cannot advance past them.\n *\n * An entry can leave unserved because a bound evicted it, and an evicted entry\n * is exactly one this channel intends to re-derive: it is still owed to the\n * remote. Remembering the floor across the whole run rather than only while\n * the entry is present is what makes that true after a later ack would\n * otherwise have swept the cursor past it.\n */\n private rememberUnserved(syncOps: SyncOperation[]): void {\n for (const syncOp of syncOps) {\n if (syncOp.status === SyncOperationStatus.Applied) continue;\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first < this.evictedOutboxFloor) {\n this.evictedOutboxFloor = first;\n }\n }\n }\n\n /**\n * Persists the outbox cursor, never past an operation this remote has not\n * been served.\n *\n * The cursor is where a restart resumes deriving the outbox from, so an\n * ordinal persisted past an unserved entry loses that entry for good: the\n * rebuild starts beyond it and nothing else remembers it was owed. Acks\n * arrive out of order with respect to what is withheld -- a later entry can\n * be acknowledged while an earlier one is still being withheld from this\n * subject -- so the applied high-water mark alone is not a safe cursor.\n */\n private persistOutboxCursor(appliedOrdinal: number): void {\n this.appliedOutboxOrdinal = Math.max(\n this.appliedOutboxOrdinal,\n appliedOrdinal,\n );\n const ordinal = Math.min(\n this.appliedOutboxOrdinal,\n this.unservedFloor() - 1,\n );\n if (ordinal <= this.lastPersistedOutboxOrdinal) {\n return;\n }\n\n this.lastPersistedOutboxOrdinal = ordinal;\n this.cursorStorage\n .upsert({\n remoteName: this.remoteName,\n cursorType: \"outbox\",\n cursorOrdinal: ordinal,\n lastSyncedAtUtcMs: Date.now(),\n })\n .catch((error) => {\n this.logger.error(\n \"Failed to update outbox cursor for @ChannelId! This means that future application runs may resend duplicate operations. This is recoverable (with deduplication protection), but not-optimal: @Error\",\n this.channelId,\n error,\n );\n });\n }\n\n /** Drops the evicted floor once the entries it stood for are queued again. */\n private forgetEvictedBelow(syncOps: SyncOperation[]): void {\n if (this.evictedOutboxFloor === Number.POSITIVE_INFINITY) {\n return;\n }\n\n for (const syncOp of syncOps) {\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first <= this.evictedOutboxFloor) {\n this.evictedOutboxFloor = Number.POSITIVE_INFINITY;\n return;\n }\n }\n }\n\n /** The lowest ordinal still owed to this remote, evicted or still queued. */\n private unservedFloor(): number {\n let floor = this.evictedOutboxFloor;\n for (const syncOp of this.outbox.items) {\n if (syncOp.status === SyncOperationStatus.Applied) continue;\n const first = firstOrdinalOf(syncOp);\n if (first > 0 && first < floor) {\n floor = first;\n }\n }\n return floor;\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport type { IChannel, IChannelFactory } from \"../interfaces.js\";\nimport type { ChannelConfig } from \"../types.js\";\nimport { GqlResponseChannel } from \"./gql-res-channel.js\";\n\n/**\n * Factory for creating GqlResponseChannel instances.\n */\nexport class GqlResponseChannelFactory implements IChannelFactory {\n private readonly logger: ILogger;\n\n constructor(logger: ILogger) {\n this.logger = logger;\n }\n\n instance(\n remoteId: string,\n remoteName: string,\n config: ChannelConfig,\n cursorStorage: ISyncCursorStorage,\n ): IChannel {\n return new GqlResponseChannel(\n this.logger,\n remoteId,\n remoteName,\n cursorStorage,\n );\n }\n}\n","import type { Kysely } from \"kysely\";\nimport type { OperationIndexEntry } from \"../../cache/operation-index-types.js\";\nimport type { DeliveryRow } from \"../../sync/delivery-tracking.js\";\nimport type { Database } from \"./types.js\";\n\n/** The index row at `ordinal` and every membership of its document. */\nexport async function deliveryAt(\n db: Kysely<Database>,\n documentId: string,\n ordinal: number,\n): Promise<DeliveryRow | undefined> {\n const row = await db\n .selectFrom(\"operation_index_operations\")\n .selectAll()\n .where(\"ordinal\", \"=\", ordinal)\n .where(\"documentId\", \"=\", documentId)\n .executeTakeFirst();\n if (!row) return undefined;\n\n const memberships = await db\n .selectFrom(\"document_collections\")\n .select([\"collectionId\", \"joinedOrdinal\", \"leftOrdinal\"])\n .where(\"documentId\", \"=\", documentId)\n .execute();\n\n const entry: OperationIndexEntry = {\n ordinal: row.ordinal,\n documentId: row.documentId,\n documentType: row.documentType,\n branch: row.branch,\n scope: row.scope,\n index: row.index,\n timestampUtcMs: row.timestampUtcMs,\n hash: row.hash,\n skip: row.skip,\n action: row.action as OperationIndexEntry[\"action\"],\n deniedReason: row.deniedReason ?? undefined,\n id: row.opId,\n sourceRemote: row.sourceRemote,\n };\n return {\n entry,\n memberships: memberships.map((membership) => ({\n collectionId: membership.collectionId,\n joinedOrdinal: Number(membership.joinedOrdinal),\n leftOrdinal:\n membership.leftOrdinal === null ? null : Number(membership.leftOrdinal),\n })),\n };\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { RemoteCursor } from \"../../sync/types.js\";\nimport type { ISyncCursorStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncCursor, SyncCursorRow } from \"./types.js\";\n\nfunction rowToRemoteCursor(row: SyncCursorRow): RemoteCursor {\n return {\n remoteName: row.remote_name,\n cursorType: row.cursor_type as \"inbox\" | \"outbox\",\n cursorOrdinal: Number(row.cursor_ordinal),\n lastSyncedAtUtcMs: row.last_synced_at_utc_ms\n ? new Date(row.last_synced_at_utc_ms).getTime()\n : undefined,\n };\n}\n\nfunction remoteCursorToRow(cursor: RemoteCursor): InsertableSyncCursor {\n return {\n remote_name: cursor.remoteName,\n cursor_type: cursor.cursorType,\n cursor_ordinal: BigInt(cursor.cursorOrdinal),\n last_synced_at_utc_ms: cursor.lastSyncedAtUtcMs\n ? new Date(cursor.lastSyncedAtUtcMs).toISOString()\n : null,\n };\n}\n\nexport class KyselySyncCursorStorage implements ISyncCursorStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<RemoteCursor[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteCursor);\n }\n\n async get(\n remoteName: string,\n cursorType: \"inbox\" | \"outbox\",\n signal?: AbortSignal,\n ): Promise<RemoteCursor> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_cursors\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .where(\"cursor_type\", \"=\", cursorType)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n return {\n remoteName,\n cursorType,\n cursorOrdinal: 0,\n };\n }\n\n return rowToRemoteCursor(row);\n }\n\n async upsert(cursor: RemoteCursor, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteCursorToRow(cursor);\n\n await trx\n .insertInto(\"sync_cursors\")\n .values(insertable)\n .onConflict((oc) =>\n oc.columns([\"remote_name\", \"cursor_type\"]).doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(remoteName: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_cursors\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport { DocumentPurgedError } from \"../../shared/errors.js\";\nimport type { PagingOptions, PagedResults } from \"../../shared/types.js\";\nimport type {\n ChannelErrorSource,\n SyncOperationErrorType,\n} from \"../../sync/types.js\";\nimport { quarantinesDocument } from \"../../sync/utils.js\";\nimport type { DeadLetterRecord } from \"../interfaces.js\";\nimport type { ISyncDeadLetterStorage } from \"../interfaces.js\";\nimport { acquirePurgeLocks, findPurged } from \"./document-purges.js\";\nimport type {\n Database,\n InsertableSyncDeadLetter,\n SyncDeadLetterRow,\n} from \"./types.js\";\n\nfunction rowToDeadLetterRecord(row: SyncDeadLetterRow): DeadLetterRecord {\n return {\n id: row.id,\n jobId: row.job_id,\n jobDependencies: row.job_dependencies as string[],\n remoteName: row.remote_name,\n documentId: row.document_id,\n scopes: row.scopes as string[],\n branch: row.branch,\n operations: row.operations as OperationWithContext[],\n errorSource: row.error_source as ChannelErrorSource,\n errorMessage: row.error_message,\n // Coalesced as well as defaulted, so a pre-existing row rehydrates either way.\n errorType: (row.error_type ?? \"UNCLASSIFIED\") as SyncOperationErrorType,\n };\n}\n\nfunction deadLetterRecordToRow(\n record: DeadLetterRecord,\n): InsertableSyncDeadLetter {\n return {\n id: record.id,\n job_id: record.jobId,\n job_dependencies: JSON.stringify(record.jobDependencies),\n remote_name: record.remoteName,\n document_id: record.documentId,\n scopes: JSON.stringify(record.scopes),\n branch: record.branch,\n operations: JSON.stringify(record.operations),\n error_source: record.errorSource,\n error_message: record.errorMessage,\n error_type: record.errorType,\n };\n}\n\n/**\n * PGlite/Kysely-backed implementation of {@link ISyncDeadLetterStorage}.\n */\nexport class KyselySyncDeadLetterStorage implements ISyncDeadLetterStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n remoteName: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DeadLetterRecord>> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) : 0;\n const limit = paging?.limit || 100;\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .orderBy(\"ordinal\", \"desc\")\n .offset(startIndex)\n .limit(limit + 1)\n .execute();\n\n let hasMore = false;\n let items = rows;\n if (paging?.limit && rows.length > limit) {\n hasMore = true;\n items = rows.slice(0, limit);\n }\n\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n const cursor = paging?.cursor || \"0\";\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return {\n results: items.map(rowToDeadLetterRecord),\n options: { cursor, limit },\n nextCursor,\n };\n }\n\n async add(deadLetter: DeadLetterRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n // A purged id's dead letter would write its operations back.\n await this.db.transaction().execute(async (trx) => {\n await acquirePurgeLocks(trx, [deadLetter.documentId], \"shared\");\n const purged = await findPurged(trx, [deadLetter.documentId]);\n if (purged.size > 0) {\n throw new DocumentPurgedError(deadLetter.documentId);\n }\n\n const insertable = deadLetterRecordToRow(deadLetter);\n\n await trx\n .insertInto(\"sync_dead_letters\")\n .values(insertable)\n .onConflict((oc) => oc.column(\"id\").doNothing())\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(id: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_dead_letters\").where(\"id\", \"=\", id).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async removeByRemote(\n remoteName: string,\n signal?: AbortSignal,\n ): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx\n .deleteFrom(\"sync_dead_letters\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async listQuarantinedDocumentIds(signal?: AbortSignal): Promise<string[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db\n .selectFrom(\"sync_dead_letters\")\n .select([\"document_id\", \"error_type\"])\n .distinct()\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n // A document whose only dead letters are held auth operations keeps syncing\n // across a restart too.\n const quarantined = new Set<string>();\n for (const row of rows) {\n const errorType = (row.error_type ??\n \"UNCLASSIFIED\") as SyncOperationErrorType;\n if (quarantinesDocument(errorType)) {\n quarantined.add(row.document_id);\n }\n }\n\n return [...quarantined];\n }\n}\n","import { DOCUMENT_PURGE_PROTOCOL } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport { DocumentPurgedError } from \"../../shared/errors.js\";\nimport type { ISyncHoldStorage, SyncHoldRecord } from \"../interfaces.js\";\nimport { acquirePurgeLocks, findPurged } from \"./document-purges.js\";\nimport type { Database } from \"./types.js\";\n\nexport class KyselySyncHoldStorage implements ISyncHoldStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(\n filter: { remoteName?: string; documentId?: string } = {},\n ): Promise<SyncHoldRecord[]> {\n let query = this.db.selectFrom(\"sync_holds\").selectAll();\n if (filter.remoteName !== undefined) {\n query = query.where(\"remote_name\", \"=\", filter.remoteName);\n }\n if (filter.documentId !== undefined) {\n query = query.where(\"document_id\", \"=\", filter.documentId);\n }\n const rows = await query.orderBy(\"held_at_utc_ms\").execute();\n return rows.map((row) => ({\n remoteName: row.remote_name,\n documentId: row.document_id,\n branch: row.branch,\n protocol: row.protocol,\n version: row.version,\n heldAtUtcMs: Number(row.held_at_utc_ms),\n }));\n }\n\n async upsert(hold: SyncHoldRecord): Promise<void> {\n const row = {\n remote_name: hold.remoteName,\n document_id: hold.documentId,\n branch: hold.branch,\n protocol: hold.protocol,\n version: hold.version,\n held_at_utc_ms: hold.heldAtUtcMs,\n };\n // A purge deleted the id's holds; only its marker may earn one after.\n await this.db.transaction().execute(async (trx) => {\n await acquirePurgeLocks(trx, [hold.documentId], \"shared\");\n if (hold.protocol !== DOCUMENT_PURGE_PROTOCOL) {\n const purged = await findPurged(trx, [hold.documentId]);\n if (purged.size > 0) throw new DocumentPurgedError(hold.documentId);\n }\n await trx\n .insertInto(\"sync_holds\")\n .values(row)\n .onConflict((oc) =>\n oc\n .columns([\"remote_name\", \"document_id\", \"branch\"])\n .doUpdateSet({ protocol: row.protocol, version: row.version }),\n )\n .execute();\n });\n }\n\n async remove(\n remoteName: string,\n documentId: string,\n branch: string,\n ): Promise<void> {\n await this.db\n .deleteFrom(\"sync_holds\")\n .where(\"remote_name\", \"=\", remoteName)\n .where(\"document_id\", \"=\", documentId)\n .where(\"branch\", \"=\", branch)\n .execute();\n }\n\n async removeRemote(remoteName: string): Promise<void> {\n await this.db\n .deleteFrom(\"sync_holds\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n }\n}\n","import type { Kysely } from \"kysely\";\nimport type {\n ISyncPurgeRefusalStorage,\n PurgeRefusalRecord,\n} from \"../interfaces.js\";\nimport type { Database } from \"./types.js\";\n\nexport class KyselySyncPurgeRefusalStorage implements ISyncPurgeRefusalStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(documentId: string): Promise<PurgeRefusalRecord[]> {\n const rows = await this.db\n .selectFrom(\"sync_purge_refusals\")\n .selectAll()\n .where(\"document_id\", \"=\", documentId)\n .orderBy(\"refused_at_utc_ms\")\n .orderBy(\"remote_name\")\n .execute();\n return rows.map((row) => ({\n remoteName: row.remote_name,\n documentId: row.document_id,\n branch: row.branch,\n refusedAtUtcMs: Number(row.refused_at_utc_ms),\n }));\n }\n\n async record(refusals: readonly PurgeRefusalRecord[]): Promise<void> {\n if (refusals.length === 0) return;\n await this.db\n .insertInto(\"sync_purge_refusals\")\n .values(\n refusals.map((refusal) => ({\n remote_name: refusal.remoteName,\n document_id: refusal.documentId,\n branch: refusal.branch,\n refused_at_utc_ms: refusal.refusedAtUtcMs,\n })),\n )\n .onConflict((oc) =>\n oc.columns([\"remote_name\", \"document_id\", \"branch\"]).doNothing(),\n )\n .execute();\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport type {\n ISyncReceivedMarkerStorage,\n ReceivedMarkerRecord,\n} from \"../interfaces.js\";\nimport type { Database } from \"./types.js\";\n\nexport class KyselySyncReceivedMarkerStorage implements ISyncReceivedMarkerStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(remoteName: string): Promise<ReceivedMarkerRecord[]> {\n const rows = await this.db\n .selectFrom(\"sync_received_markers\")\n .selectAll()\n .where(\"remote_name\", \"=\", remoteName)\n .orderBy(\"received_at_utc_ms\")\n .execute();\n return rows.map((row) => ({\n remoteName: row.remote_name,\n markerId: row.marker_id,\n documentId: row.document_id,\n branch: row.branch,\n operation: row.operation as OperationWithContext,\n receivedAtUtcMs: Number(row.received_at_utc_ms),\n }));\n }\n\n async upsert(record: ReceivedMarkerRecord): Promise<void> {\n await this.db\n .insertInto(\"sync_received_markers\")\n .values({\n remote_name: record.remoteName,\n marker_id: record.markerId,\n document_id: record.documentId,\n branch: record.branch,\n operation: JSON.stringify(record.operation),\n received_at_utc_ms: record.receivedAtUtcMs,\n })\n .onConflict((oc) => oc.columns([\"remote_name\", \"marker_id\"]).doNothing())\n .execute();\n }\n\n async remove(remoteName: string, markerId: string): Promise<void> {\n await this.db\n .deleteFrom(\"sync_received_markers\")\n .where(\"remote_name\", \"=\", remoteName)\n .where(\"marker_id\", \"=\", markerId)\n .execute();\n }\n\n async removeRemote(remoteName: string): Promise<void> {\n await this.db\n .deleteFrom(\"sync_received_markers\")\n .where(\"remote_name\", \"=\", remoteName)\n .execute();\n }\n}\n","import {\n canonicalJson,\n readPeerManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\nimport { DriveCollectionId } from \"../../cache/operation-index-types.js\";\nimport type { RemotePeer, RemoteRecord } from \"../../sync/types.js\";\nimport type { ISyncRemoteStorage } from \"../interfaces.js\";\nimport type { Database, InsertableSyncRemote, SyncRemoteRow } from \"./types.js\";\n\nfunction rowToPeer(row: SyncRemoteRow): RemotePeer | undefined {\n if (row.peer_manifest_at_utc_ms === null) {\n return undefined;\n }\n let manifest = null;\n if (row.peer_manifest !== null) {\n try {\n manifest = readPeerManifest(JSON.parse(row.peer_manifest));\n } catch {\n // unreadable: treated as silent\n }\n }\n return {\n manifest,\n receivedAtUtcMs: Number(row.peer_manifest_at_utc_ms),\n };\n}\n\nfunction rowToRemoteRecord(row: SyncRemoteRow): RemoteRecord {\n const peer = rowToPeer(row);\n return {\n ...(peer ? { peer } : {}),\n id: row.channel_id,\n name: row.name,\n collectionId: DriveCollectionId.fromKey(row.collection_id),\n channelConfig: {\n type: row.channel_type,\n parameters: (row.channel_parameters ?? {}) as Record<string, unknown>,\n },\n filter: {\n documentId: (row.filter_document_ids ?? []) as string[],\n scope: (row.filter_scopes ?? []) as string[],\n branch: row.filter_branch,\n },\n options: {\n sinceTimestampUtcMs: \"0\",\n boundAddress: row.bound_address ?? undefined,\n },\n status: {\n push: {\n state: row.push_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.push_last_success_utc_ms\n ? new Date(row.push_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.push_last_failure_utc_ms\n ? new Date(row.push_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.push_failure_count,\n },\n pull: {\n state: row.pull_state as \"idle\" | \"running\" | \"error\",\n lastSuccessUtcMs: row.pull_last_success_utc_ms\n ? new Date(row.pull_last_success_utc_ms).getTime()\n : undefined,\n lastFailureUtcMs: row.pull_last_failure_utc_ms\n ? new Date(row.pull_last_failure_utc_ms).getTime()\n : undefined,\n failureCount: row.pull_failure_count,\n },\n },\n };\n}\n\n/**\n * Maps a RemoteRecord to a row. jsonb array columns are JSON-encoded because the\n * pg driver would otherwise emit a Postgres array literal (\"{a,b}\"), which jsonb\n * rejects (matches sync-dead-letter-storage).\n */\nfunction remoteRecordToRow(remote: RemoteRecord): InsertableSyncRemote {\n return {\n name: remote.name,\n collection_id: remote.collectionId.key,\n channel_type: remote.channelConfig.type,\n channel_id: remote.id,\n remote_name: remote.name,\n channel_parameters: remote.channelConfig.parameters,\n filter_document_ids:\n remote.filter.documentId.length > 0\n ? JSON.stringify(remote.filter.documentId)\n : null,\n filter_scopes:\n remote.filter.scope.length > 0\n ? JSON.stringify(remote.filter.scope)\n : null,\n filter_branch: remote.filter.branch,\n push_state: remote.status.push.state,\n push_last_success_utc_ms: remote.status.push.lastSuccessUtcMs\n ? new Date(remote.status.push.lastSuccessUtcMs).toISOString()\n : null,\n push_last_failure_utc_ms: remote.status.push.lastFailureUtcMs\n ? new Date(remote.status.push.lastFailureUtcMs).toISOString()\n : null,\n push_failure_count: remote.status.push.failureCount,\n pull_state: remote.status.pull.state,\n pull_last_success_utc_ms: remote.status.pull.lastSuccessUtcMs\n ? new Date(remote.status.pull.lastSuccessUtcMs).toISOString()\n : null,\n pull_last_failure_utc_ms: remote.status.pull.lastFailureUtcMs\n ? new Date(remote.status.pull.lastFailureUtcMs).toISOString()\n : null,\n pull_failure_count: remote.status.pull.failureCount,\n bound_address: remote.options.boundAddress ?? null,\n peer_manifest: remote.peer?.manifest\n ? canonicalJson(remote.peer.manifest, \"peer manifest\")\n : null,\n peer_manifest_at_utc_ms: remote.peer?.receivedAtUtcMs ?? null,\n };\n}\n\nexport class KyselySyncRemoteStorage implements ISyncRemoteStorage {\n constructor(private readonly db: Kysely<Database>) {}\n\n async list(signal?: AbortSignal): Promise<RemoteRecord[]> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const rows = await this.db.selectFrom(\"sync_remotes\").selectAll().execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map(rowToRemoteRecord);\n }\n\n async get(name: string, signal?: AbortSignal): Promise<RemoteRecord> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n const row = await this.db\n .selectFrom(\"sync_remotes\")\n .selectAll()\n .where(\"name\", \"=\", name)\n .executeTakeFirst();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!row) {\n throw new Error(`Remote not found: ${name}`);\n }\n\n return rowToRemoteRecord(row);\n }\n\n async upsert(remote: RemoteRecord, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n const insertable = remoteRecordToRow(remote);\n\n await trx\n .insertInto(\"sync_remotes\")\n .values(insertable)\n .onConflict((oc) =>\n oc.column(\"name\").doUpdateSet({\n ...insertable,\n updated_at: sql`NOW()`,\n }),\n )\n .execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n\n async remove(name: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom(\"sync_remotes\").where(\"name\", \"=\", name).execute();\n });\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n }\n}\n","import type { ILogger } from \"document-model\";\nimport { DriveCollectionId } from \"../cache/operation-index-types.js\";\nimport type { JobFailedEvent, JobWriteReadyEvent } from \"../events/types.js\";\n\nexport type PreparedBatch = {\n /** Document ID -> Collection IDs that they are a part of */\n collectionMemberships: Record<string, string[]>;\n entries: Array<{\n event: JobWriteReadyEvent;\n jobDependencies: string[];\n }>;\n};\n\n/** Queued between write-ready events when the settled watermark advances. */\nconst SETTLED = Symbol(\"settled\");\n\n/** Batches remembered as finished, so a repeated JOB_FAILED is ignored. */\nconst FINISHED_BATCH_MEMORY = 1024;\n\ntype QueueItem =\n | JobWriteReadyEvent\n | typeof SETTLED\n | { failed: JobFailedEvent };\n\ntype PendingBatch = {\n expectedJobIds: Set<string>;\n /** Jobs that arrived or failed. */\n resolvedJobIds: Set<string>;\n events: JobWriteReadyEvent[];\n};\n\nexport class BatchAggregator {\n private readonly logger: ILogger;\n private readonly driveContainerTypes: ReadonlySet<string>;\n private readonly onBatchReady: (batch: PreparedBatch) => Promise<void>;\n private readonly onSettled: () => Promise<void>;\n private queue: QueueItem[] = [];\n private processing: boolean = false;\n private readonly pendingBatches: Map<string, PendingBatch> = new Map();\n private readonly finishedBatches: Set<string> = new Set();\n\n constructor(\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n onBatchReady: (batch: PreparedBatch) => Promise<void>,\n onSettled: () => Promise<void>,\n ) {\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\n this.onBatchReady = onBatchReady;\n this.onSettled = onSettled;\n }\n\n async enqueueWriteReady(event: JobWriteReadyEvent): Promise<void> {\n this.queue.push(event);\n await this.processQueue();\n }\n\n /** Runs onSettled on the serial queue; one queued run covers many. */\n async enqueueSettled(): Promise<void> {\n if (!this.queue.includes(SETTLED)) {\n this.queue.push(SETTLED);\n }\n await this.processQueue();\n }\n\n async handleJobFailed(event: JobFailedEvent): Promise<void> {\n this.queue.push({ failed: event });\n await this.processQueue();\n }\n\n clear(): void {\n this.queue = [];\n this.pendingBatches.clear();\n this.finishedBatches.clear();\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing) {\n return;\n }\n this.processing = true;\n\n try {\n while (this.queue.length > 0) {\n const item = this.queue.shift()!;\n if (item === SETTLED) {\n try {\n await this.onSettled();\n } catch (error) {\n this.logger.error(\n \"Failed to derive settled outboxes (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n continue;\n }\n if (\"failed\" in item) {\n try {\n await this.handleFailed(item.failed);\n } catch (error) {\n this.logger.error(\n \"Failed to process job-failed event (@jobId, @error)\",\n item.failed.jobId,\n error instanceof Error ? error.message : String(error),\n );\n }\n continue;\n }\n try {\n await this.handleWriteReady(item);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to process write-ready event (@jobId, @error)\",\n item.jobId,\n err.message,\n );\n }\n }\n } finally {\n this.processing = false;\n }\n }\n\n private async handleWriteReady(event: JobWriteReadyEvent): Promise<void> {\n const { batchId, batchJobIds } = event.jobMeta;\n\n const pending =\n batchJobIds.length > 1\n ? this.pendingFor(batchId, batchJobIds)\n : undefined;\n if (!pending) {\n await this.onBatchReady(this.prepareBatch([event]));\n return;\n }\n pending.resolvedJobIds.add(event.jobId);\n pending.events.push(event);\n\n if (this.finishIfResolved(batchId, pending)) {\n await this.onBatchReady(this.prepareBatch(pending.events));\n }\n }\n\n /** A failed job never arrives, so what its batch holds goes now. */\n private async handleFailed(event: JobFailedEvent): Promise<void> {\n const meta = event.job?.meta;\n if (!meta?.batchId || meta.batchJobIds.length <= 1) {\n return;\n }\n const pending = this.pendingFor(meta.batchId, meta.batchJobIds);\n if (!pending) {\n return;\n }\n pending.resolvedJobIds.add(event.jobId);\n this.finishIfResolved(meta.batchId, pending);\n\n const events = pending.events;\n pending.events = [];\n if (events.length > 0) {\n await this.onBatchReady(this.prepareBatch(events));\n }\n }\n\n private pendingFor(\n batchId: string,\n batchJobIds: string[],\n ): PendingBatch | undefined {\n if (this.finishedBatches.has(batchId)) {\n return undefined;\n }\n let pending = this.pendingBatches.get(batchId);\n if (!pending) {\n pending = {\n expectedJobIds: new Set(batchJobIds),\n resolvedJobIds: new Set(),\n events: [],\n };\n this.pendingBatches.set(batchId, pending);\n }\n return pending;\n }\n\n private finishIfResolved(batchId: string, pending: PendingBatch): boolean {\n for (const id of pending.expectedJobIds) {\n if (!pending.resolvedJobIds.has(id)) {\n return false;\n }\n }\n this.pendingBatches.delete(batchId);\n this.finishedBatches.add(batchId);\n if (this.finishedBatches.size > FINISHED_BATCH_MEMORY) {\n const oldest = this.finishedBatches.values().next().value!;\n this.finishedBatches.delete(oldest);\n }\n return true;\n }\n\n private prepareBatch(events: JobWriteReadyEvent[]): PreparedBatch {\n const collectionMemberships = this.mergeCollectionMemberships(events);\n const isBatch = events.length > 1;\n const priorJobIds: string[] = [];\n const entries: PreparedBatch[\"entries\"] = [];\n\n for (const event of events) {\n entries.push({\n event,\n jobDependencies: isBatch ? [...priorJobIds] : [],\n });\n\n if (isBatch && event.jobId) {\n priorJobIds.push(event.jobId);\n }\n }\n\n return { collectionMemberships, entries };\n }\n\n private mergeCollectionMemberships(\n events: JobWriteReadyEvent[],\n ): Record<string, string[]> {\n const mergedMemberships: Record<string, string[]> = {};\n\n for (const event of events) {\n if (event.collectionMemberships) {\n for (const [docId, collections] of Object.entries(\n event.collectionMemberships,\n )) {\n if (!(docId in mergedMemberships)) {\n mergedMemberships[docId] = [];\n }\n for (const c of collections) {\n if (!mergedMemberships[docId].includes(c)) {\n mergedMemberships[docId].push(c);\n }\n }\n }\n }\n\n for (const op of event.operations) {\n const action = op.operation.action as {\n type: string;\n input?: { sourceId?: string; targetId?: string };\n };\n if (action.type !== \"ADD_RELATIONSHIP\") {\n continue;\n }\n if (!this.driveContainerTypes.has(op.context.documentType)) {\n continue;\n }\n const input = action.input;\n if (!input?.sourceId || !input.targetId) {\n continue;\n }\n\n const collectionId = DriveCollectionId.forDrive(\n input.sourceId,\n op.context.branch,\n ).key;\n if (!(input.targetId in mergedMemberships)) {\n mergedMemberships[input.targetId] = [];\n }\n if (!mergedMemberships[input.targetId].includes(collectionId)) {\n mergedMemberships[input.targetId].push(collectionId);\n }\n }\n }\n\n return mergedMemberships;\n }\n}\n","import type { OperationIndexEntry } from \"../cache/operation-index-types.js\";\nimport type {\n ISyncCursorStorage,\n ISyncHoldStorage,\n} from \"../storage/interfaces.js\";\nimport type { Remote } from \"./interfaces.js\";\nimport type { ConnectionState } from \"./types.js\";\nimport { filterForRemote, toOperationWithContext } from \"./utils.js\";\n\nexport type PendingDelivery = {\n remote: string;\n state: ConnectionState | \"held\";\n};\n\n/** No sync manager while sync_remotes has rows means not converged. */\nexport interface IDeliveryTracking {\n /** Remotes that have not acknowledged ordinal for documentId's collections. */\n pendingDelivery(\n documentId: string,\n ordinal: number,\n ): Promise<PendingDelivery[]>;\n}\n\nexport function supportsDeliveryTracking(x: unknown): x is IDeliveryTracking {\n return (\n typeof x === \"object\" &&\n x !== null &&\n typeof (x as { pendingDelivery?: unknown }).pendingDelivery === \"function\"\n );\n}\n\nexport type DeliveryMembership = {\n collectionId: string;\n joinedOrdinal: number;\n leftOrdinal: number | null;\n};\n\nexport type DeliveryRow = {\n entry: OperationIndexEntry;\n memberships: DeliveryMembership[];\n};\n\n/** The index row at an ordinal and every membership of its document. */\nexport type DeliveryLookup = {\n at(documentId: string, ordinal: number): Promise<DeliveryRow | undefined>;\n};\n\nexport type DeliveryDeps = {\n remotes: readonly Remote[];\n cursors: ISyncCursorStorage;\n holds: ISyncHoldStorage;\n lookup: DeliveryLookup;\n};\n\n/** The outbox's rule: remotes it would serve the row to, not yet acknowledged. */\nexport async function pendingDelivery(\n deps: DeliveryDeps,\n documentId: string,\n ordinal: number,\n): Promise<PendingDelivery[]> {\n const row = await deps.lookup.at(documentId, ordinal);\n if (!row) {\n throw new Error(\n `No operation of document ${documentId} at ordinal ${ordinal}`,\n );\n }\n const { entry, memberships } = row;\n const operation = toOperationWithContext(entry);\n const holds = await deps.holds.list({ documentId });\n\n const pending: PendingDelivery[] = [];\n for (const remote of deps.remotes) {\n const name = remote.meta.name;\n const membership = memberships.find(\n (candidate) =>\n candidate.collectionId === remote.meta.collectionId.key &&\n (candidate.leftOrdinal === null || ordinal < candidate.leftOrdinal),\n );\n if (!membership) continue;\n if (entry.sourceRemote === name) continue;\n if (filterForRemote([operation], remote.meta.filter).length === 0) {\n continue;\n }\n\n const held = holds.some(\n (hold) => hold.remoteName === name && hold.branch === entry.branch,\n );\n if (held) {\n pending.push({ remote: name, state: \"held\" });\n continue;\n }\n\n // A later join re-serves rows at or below the cursor (the joiner branch).\n const owedThrough = Math.max(ordinal, membership.joinedOrdinal);\n const cursor = await deps.cursors.get(name, \"outbox\");\n if (cursor.cursorOrdinal < owedThrough) {\n pending.push({\n remote: name,\n state: remote.channel.getConnectionState().state,\n });\n }\n }\n return pending;\n}\n","import type {\n ISyncHoldStorage,\n SyncHoldRecord,\n} from \"../storage/interfaces.js\";\n\nconst keyOf = (remoteName: string, documentId: string, branch: string) =>\n `${remoteName}\\u0000${documentId}\\u0000${branch}`;\n\n/** For a sync manager built without a database. */\nexport class InMemorySyncHoldStorage implements ISyncHoldStorage {\n private readonly holds = new Map<string, SyncHoldRecord>();\n\n list(\n filter: { remoteName?: string; documentId?: string } = {},\n ): Promise<SyncHoldRecord[]> {\n return Promise.resolve(\n [...this.holds.values()].filter(\n (hold) =>\n (filter.remoteName === undefined ||\n hold.remoteName === filter.remoteName) &&\n (filter.documentId === undefined ||\n hold.documentId === filter.documentId),\n ),\n );\n }\n\n upsert(hold: SyncHoldRecord): Promise<void> {\n const key = keyOf(hold.remoteName, hold.documentId, hold.branch);\n const known = this.holds.get(key);\n this.holds.set(key, {\n ...hold,\n heldAtUtcMs: known?.heldAtUtcMs ?? hold.heldAtUtcMs,\n });\n return Promise.resolve();\n }\n\n remove(\n remoteName: string,\n documentId: string,\n branch: string,\n ): Promise<void> {\n this.holds.delete(keyOf(remoteName, documentId, branch));\n return Promise.resolve();\n }\n\n removeRemote(remoteName: string): Promise<void> {\n for (const [key, hold] of this.holds) {\n if (hold.remoteName === remoteName) this.holds.delete(key);\n }\n return Promise.resolve();\n }\n}\n","import type {\n ISyncPurgeRefusalStorage,\n PurgeRefusalRecord,\n} from \"../storage/interfaces.js\";\n\nconst keyOf = (refusal: PurgeRefusalRecord) =>\n `${refusal.remoteName}\\u0000${refusal.documentId}\\u0000${refusal.branch}`;\n\n/** For a sync manager built without a database. */\nexport class InMemorySyncPurgeRefusalStorage implements ISyncPurgeRefusalStorage {\n private readonly records = new Map<string, PurgeRefusalRecord>();\n\n list(documentId: string): Promise<PurgeRefusalRecord[]> {\n return Promise.resolve(\n [...this.records.values()].filter(\n (record) => record.documentId === documentId,\n ),\n );\n }\n\n record(refusals: readonly PurgeRefusalRecord[]): Promise<void> {\n for (const refusal of refusals) {\n const key = keyOf(refusal);\n if (!this.records.has(key)) this.records.set(key, refusal);\n }\n return Promise.resolve();\n }\n}\n","import type {\n ISyncReceivedMarkerStorage,\n ReceivedMarkerRecord,\n} from \"../storage/interfaces.js\";\n\nconst keyOf = (remoteName: string, markerId: string) =>\n `${remoteName}\\u0000${markerId}`;\n\n/** For a sync manager built without a database. */\nexport class InMemorySyncReceivedMarkerStorage implements ISyncReceivedMarkerStorage {\n private readonly records = new Map<string, ReceivedMarkerRecord>();\n\n list(remoteName: string): Promise<ReceivedMarkerRecord[]> {\n return Promise.resolve(\n [...this.records.values()].filter(\n (record) => record.remoteName === remoteName,\n ),\n );\n }\n\n upsert(record: ReceivedMarkerRecord): Promise<void> {\n const key = keyOf(record.remoteName, record.markerId);\n if (!this.records.has(key)) this.records.set(key, record);\n return Promise.resolve();\n }\n\n remove(remoteName: string, markerId: string): Promise<void> {\n this.records.delete(keyOf(remoteName, markerId));\n return Promise.resolve();\n }\n\n removeRemote(remoteName: string): Promise<void> {\n for (const [key, record] of this.records) {\n if (record.remoteName === remoteName) this.records.delete(key);\n }\n return Promise.resolve();\n }\n}\n","import type {\n PeerManifest,\n Supports,\n} from \"@powerhousedao/shared/document-model\";\nimport type { RemoteMeta } from \"./interfaces.js\";\n\nexport interface IPeerAgreement {\n local(): PeerManifest;\n /** Cloneable, so agreement can be rebuilt across a worker boundary. */\n basis(): PeerAgreementBasis;\n /** What `remoteName`'s peer supports, from its manifest or the baselines. */\n peer(remoteName: string): Supports;\n /** The direct peers of the remotes in `collectionIds`, by remote name. */\n members(collectionIds: readonly string[]): Map<string, Supports>;\n /** The remotes in a collection whose peers lack the version creation wants. */\n limitedBy(collectionId: string, protocol: string): string[];\n}\n\nexport type PeerAgreementBasis = {\n local: PeerManifest;\n /** What a silent peer supports. */\n legacy: Supports;\n /** The version per protocol that new documents would take locally. */\n wanted: { [protocol: string]: number };\n};\n\n/** Pure over the remote records it is given, live or not. */\nexport function createPeerAgreement(\n basis: PeerAgreementBasis,\n remotes: readonly Pick<RemoteMeta, \"name\" | \"collectionId\" | \"peer\">[],\n): IPeerAgreement {\n const supportsOf = (remote: Pick<RemoteMeta, \"peer\">): Supports =>\n remote.peer?.manifest ?? basis.legacy;\n\n const members = (collectionIds: readonly string[]) => {\n const wanted = new Set(collectionIds);\n const found = new Map<string, Supports>();\n for (const remote of remotes) {\n if (wanted.has(remote.collectionId.key)) {\n found.set(remote.name, supportsOf(remote));\n }\n }\n return found;\n };\n\n return {\n local: () => basis.local,\n basis: () => basis,\n peer: (remoteName) => {\n const remote = remotes.find((candidate) => candidate.name === remoteName);\n return remote ? supportsOf(remote) : basis.legacy;\n },\n members,\n limitedBy: (collectionId, protocol) => {\n const version = basis.wanted[protocol] as number | undefined;\n if (version === undefined) return [];\n return [...members([collectionId])]\n .filter(\n ([, supports]) => !supports.protocols[protocol]?.includes(version),\n )\n .map(([name]) => name);\n },\n };\n}\n","import type { IEventBus } from \"../events/interfaces.js\";\nimport type { Unsubscribe } from \"../events/types.js\";\nimport {\n SyncEventTypes,\n type SyncFailedEvent,\n type SyncResult,\n type SyncSucceededEvent,\n} from \"./types.js\";\n\ntype SyncWaiter = {\n resolve: (value: SyncResult) => void;\n reject: (reason: Error) => void;\n signal?: AbortSignal;\n};\n\n/**\n * Provides a promise-based interface for waiting on sync completion.\n * Subscribes to sync events at construction and tracks completed sync results\n * to provide a fast path for jobs that have already synced.\n */\nexport class SyncAwaiter {\n private readonly completedResults = new Map<string, SyncResult>();\n private readonly pendingWaiters = new Map<string, SyncWaiter[]>();\n private readonly unsubscribers: Unsubscribe[] = [];\n private isShutdown = false;\n\n constructor(private readonly eventBus: IEventBus) {\n this.subscribeToEvents();\n }\n\n /**\n * Waits for sync operations for a job to complete.\n * Resolves when SYNC_SUCCEEDED is emitted.\n * Rejects when SYNC_FAILED is emitted.\n *\n * @param jobId - The job id to wait for\n * @param signal - Optional abort signal\n * @returns The sync result\n */\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n if (signal?.aborted) {\n return Promise.reject(new Error(\"Operation aborted\"));\n }\n\n if (this.isShutdown) {\n return Promise.reject(new Error(\"SyncAwaiter is shutdown\"));\n }\n\n const completedResult = this.completedResults.get(jobId);\n if (completedResult) {\n return Promise.resolve(completedResult);\n }\n\n return new Promise<SyncResult>((resolve, reject) => {\n const waiter: SyncWaiter = { resolve, reject, signal };\n\n const existingWaiters = this.pendingWaiters.get(jobId) || [];\n existingWaiters.push(waiter);\n this.pendingWaiters.set(jobId, existingWaiters);\n\n if (signal) {\n const abortHandler = () => {\n const waiters = this.pendingWaiters.get(jobId);\n if (waiters) {\n const index = waiters.indexOf(waiter);\n if (index !== -1) {\n waiters.splice(index, 1);\n if (waiters.length === 0) {\n this.pendingWaiters.delete(jobId);\n }\n waiter.reject(new Error(\"Operation aborted\"));\n }\n }\n };\n\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n });\n }\n\n /**\n * Shuts down the sync awaiter. This will synchronously reject all pending waiters.\n */\n shutdown(): void {\n this.isShutdown = true;\n\n for (const unsubscribe of this.unsubscribers) {\n unsubscribe();\n }\n this.unsubscribers.length = 0;\n\n for (const [, waiters] of this.pendingWaiters) {\n for (const waiter of waiters) {\n waiter.reject(new Error(\"SyncAwaiter shutdown\"));\n }\n }\n this.pendingWaiters.clear();\n }\n\n private subscribeToEvents(): void {\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncSucceededEvent>(\n SyncEventTypes.SYNC_SUCCEEDED,\n (_type, event) => {\n this.handleSyncSucceeded(event);\n },\n ),\n );\n\n this.unsubscribers.push(\n this.eventBus.subscribe<SyncFailedEvent>(\n SyncEventTypes.SYNC_FAILED,\n (_type, event) => {\n this.handleSyncFailed(event);\n },\n ),\n );\n }\n\n private handleSyncSucceeded(event: SyncSucceededEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"succeeded\",\n syncOperationCount: event.syncOperationCount,\n successCount: event.syncOperationCount,\n failureCount: 0,\n errors: [],\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private handleSyncFailed(event: SyncFailedEvent): void {\n const result: SyncResult = {\n jobId: event.jobId,\n status: \"failed\",\n syncOperationCount: event.successCount + event.failureCount,\n successCount: event.successCount,\n failureCount: event.failureCount,\n errors: event.errors,\n };\n\n this.completedResults.set(event.jobId, result);\n this.resolveWaiters(event.jobId, result);\n }\n\n private resolveWaiters(jobId: string, result: SyncResult): void {\n const waiters = this.pendingWaiters.get(jobId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n this.pendingWaiters.delete(jobId);\n\n for (const waiter of waiters) {\n if (waiter.signal?.aborted) {\n waiter.reject(new Error(\"Operation aborted\"));\n } else {\n waiter.resolve(result);\n }\n }\n }\n}\n","import type { IChannel } from \"./interfaces.js\";\nimport type { SyncOperation } from \"./sync-operation.js\";\n\nexport enum SyncStatus {\n Synced = \"SYNCED\",\n Outgoing = \"OUTGOING\",\n Incoming = \"INCOMING\",\n OutgoingAndIncoming = \"OUTGOING_AND_INCOMING\",\n Error = \"ERROR\",\n}\n\nexport type SyncStatusChangeCallback = (\n documentId: string,\n status: SyncStatus,\n) => void;\n\nexport interface ISyncStatusTracker {\n getStatus(documentId: string): SyncStatus | undefined;\n onChange(callback: SyncStatusChangeCallback): () => void;\n trackRemote(remoteName: string, channel: IChannel): void;\n untrackRemote(remoteName: string): void;\n clear(): void;\n}\n\ntype DocumentCounts = {\n inboxCount: number;\n outboxCount: number;\n errorCount: number;\n};\n\ntype MailboxType = \"inbox\" | \"outbox\" | \"deadLetter\";\n\nexport class SyncStatusTracker implements ISyncStatusTracker {\n private readonly remotes: Map<string, Map<string, DocumentCounts>> =\n new Map();\n private readonly seen: Set<string> = new Set();\n private readonly callbacks: Set<SyncStatusChangeCallback> = new Set();\n\n getStatus(documentId: string): SyncStatus | undefined {\n if (!this.seen.has(documentId)) {\n return undefined;\n }\n\n let totalInbox = 0;\n let totalOutbox = 0;\n let totalErrors = 0;\n\n for (const documents of this.remotes.values()) {\n const counts = documents.get(documentId);\n if (counts) {\n totalInbox += counts.inboxCount;\n totalOutbox += counts.outboxCount;\n totalErrors += counts.errorCount;\n }\n }\n\n return deriveStatus(totalInbox, totalOutbox, totalErrors);\n }\n\n onChange(callback: SyncStatusChangeCallback): () => void {\n this.callbacks.add(callback);\n return () => {\n this.callbacks.delete(callback);\n };\n }\n\n trackRemote(remoteName: string, channel: IChannel): void {\n this.remotes.set(remoteName, new Map());\n\n channel.inbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"inbox\", syncOps),\n );\n channel.inbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"inbox\", syncOps),\n );\n channel.outbox.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"outbox\", syncOps),\n );\n channel.outbox.onRemoved((syncOps) =>\n this.handleRemoved(remoteName, \"outbox\", syncOps),\n );\n channel.deadLetter.onAdded((syncOps) =>\n this.handleAdded(remoteName, \"deadLetter\", syncOps),\n );\n }\n\n untrackRemote(remoteName: string): void {\n const documents = this.remotes.get(remoteName);\n if (!documents) {\n return;\n }\n\n const affectedDocumentIds = [...documents.keys()];\n this.remotes.delete(remoteName);\n\n for (const documentId of affectedDocumentIds) {\n this.notifyChange(documentId);\n }\n }\n\n clear(): void {\n this.remotes.clear();\n this.seen.clear();\n this.callbacks.clear();\n }\n\n private handleAdded(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n if (mailboxType === \"inbox\" && !syncOp.remoteName) {\n continue;\n }\n\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n this.seen.add(syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount++;\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount++;\n } else {\n counts.errorCount++;\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private handleRemoved(\n remoteName: string,\n mailboxType: MailboxType,\n syncOps: SyncOperation[],\n ): void {\n const changedDocuments = new Set<string>();\n\n for (const syncOp of syncOps) {\n const counts = this.getOrCreateCounts(remoteName, syncOp.documentId);\n\n if (mailboxType === \"inbox\") {\n counts.inboxCount = Math.max(0, counts.inboxCount - 1);\n } else if (mailboxType === \"outbox\") {\n counts.outboxCount = Math.max(0, counts.outboxCount - 1);\n }\n\n changedDocuments.add(syncOp.documentId);\n }\n\n for (const documentId of changedDocuments) {\n this.notifyChange(documentId);\n }\n }\n\n private getOrCreateCounts(\n remoteName: string,\n documentId: string,\n ): DocumentCounts {\n let documents = this.remotes.get(remoteName);\n if (!documents) {\n documents = new Map();\n this.remotes.set(remoteName, documents);\n }\n\n let counts = documents.get(documentId);\n if (!counts) {\n counts = { inboxCount: 0, outboxCount: 0, errorCount: 0 };\n documents.set(documentId, counts);\n }\n\n return counts;\n }\n\n private notifyChange(documentId: string): void {\n const status = this.getStatus(documentId);\n if (status === undefined) {\n return;\n }\n\n for (const callback of [...this.callbacks]) {\n callback(documentId, status);\n }\n }\n}\n\nfunction deriveStatus(\n inboxCount: number,\n outboxCount: number,\n errorCount: number,\n): SyncStatus {\n if (errorCount > 0) {\n return SyncStatus.Error;\n }\n if (inboxCount > 0 && outboxCount > 0) {\n return SyncStatus.OutgoingAndIncoming;\n }\n if (inboxCount > 0) {\n return SyncStatus.Incoming;\n }\n if (outboxCount > 0) {\n return SyncStatus.Outgoing;\n }\n return SyncStatus.Synced;\n}\n","import type {\n HoldReason,\n Operation,\n PeerCapability,\n PeerManifest,\n ProtocolVersions,\n Supports,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n coversLocal,\n DOCUMENT_PURGE_PROTOCOL,\n isOlderManifest,\n isPurgeMarker,\n holdReason,\n legacySupports,\n localPeerManifest,\n localSupports,\n PEER_CAPABILITIES,\n peerSupports,\n purgedProtocolVersions,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type {\n DriveCollectionId,\n IOperationIndex,\n} from \"../cache/operation-index-types.js\";\nimport type {\n BatchLoadRequest,\n BatchLoadResult,\n IReactor,\n} from \"../core/types.js\";\nimport type { ISettledWatermark } from \"../catch-up/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobWriteReadyEvent,\n} from \"../events/types.js\";\nimport { JobAwaiter } from \"../shared/awaiter.js\";\nimport { DocumentPurgedError } from \"../shared/errors.js\";\nimport {\n JobStatus,\n type ErrorInfo,\n type ShutdownStatus,\n} from \"../shared/types.js\";\nimport type {\n DeadLetterRecord,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncHoldStorage,\n ISyncPurgeRefusalStorage,\n ISyncReceivedMarkerStorage,\n ISyncRemoteStorage,\n SyncHoldRecord,\n} from \"../storage/interfaces.js\";\nimport { BatchAggregator, type PreparedBatch } from \"./batch-aggregator.js\";\nimport {\n pendingDelivery,\n type DeliveryLookup,\n type IDeliveryTracking,\n type PendingDelivery,\n} from \"./delivery-tracking.js\";\nimport {\n ChannelError,\n GraphQLRequestError,\n isDriveAuthError,\n} from \"./errors.js\";\nimport type {\n IChannelFactory,\n ISyncManager,\n Remote,\n RemoteMeta,\n} from \"./interfaces.js\";\nimport { calculateBackoffDelay } from \"./channels/interval-poll-timer.js\";\nimport { InMemorySyncHoldStorage } from \"./memory-hold-storage.js\";\nimport { InMemorySyncPurgeRefusalStorage } from \"./memory-purge-refusal-storage.js\";\nimport { InMemorySyncReceivedMarkerStorage } from \"./memory-received-marker-storage.js\";\nimport {\n MAX_POLLED_REFUSALS,\n type IPurgeRefusalRecorder,\n type PolledMarkerRefusal,\n} from \"./purge-refusals.js\";\nimport { createPeerAgreement, type IPeerAgreement } from \"./peer-agreement.js\";\nimport { SyncAwaiter } from \"./sync-awaiter.js\";\nimport { SyncOperation } from \"./sync-operation.js\";\nimport {\n SyncStatusTracker,\n type SyncStatus,\n type SyncStatusChangeCallback,\n} from \"./sync-status-tracker.js\";\nimport type {\n ChannelConfig,\n ConnectionStateChangedEvent,\n DeadLetterAddedEvent,\n LocalPeer,\n PurgeLookup,\n RemoteFilter,\n RemoteOptions,\n RemoteRecord,\n SyncHeldEvent,\n SyncHold,\n SyncOperationErrorType,\n SyncPurgeRefusedEvent,\n SyncReleasedEvent,\n SyncResult,\n} from \"./types.js\";\nimport {\n ChannelErrorSource,\n SyncEventTypes,\n SyncOperationStatus,\n} from \"./types.js\";\nimport {\n batchOperationsByDocument,\n chunkSyncOperations,\n classifyJobFailure,\n createIdleHealth,\n filterForRemote,\n quarantinesDocument,\n splitTrailingSameTimestampRun,\n syncOperationErrorType,\n toOperationWithContext,\n trimMailboxFromBatch,\n} from \"./utils.js\";\nimport type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\n\nexport type SyncManagerConfig = {\n maxDeadLettersPerRemote: number;\n maxInboxBatchSize: number;\n /**\n * How many operations may sit in one remote's outbox waiting to be taken.\n *\n * Serving can withhold an entry indefinitely -- a subject whose policy never\n * widens is served that entry never -- so without a bound the outbox is a leak\n * with a policy behind it. Past the cap the newest entries are evicted rather\n * than the oldest, which keeps what remains a contiguous run from the cursor,\n * and evicting is not dropping: the cursor is held below the evicted floor, so\n * the next refill derives them again from the operation index.\n */\n maxHeldOperationsPerRemote: number;\n /**\n * How long a serving channel may go unpolled before an outbox past its bound\n * is removed rather than evicted.\n *\n * Evicting a channel nobody polls derives the same operations again on every\n * batch, forever. Removing one that is merely behind is worse: a holder more\n * than the bound behind could never catch up, so it is removed only once its\n * holder has stopped asking.\n */\n staleRemotePollWindowMs: number;\n /** Backoff for reloading a received marker whose load failed transiently. */\n markerRetryBaseDelayMs: number;\n markerRetryMaxDelayMs: number;\n};\n\nenum OutboxMode {\n Backfill = \"backfill\",\n BatchTriggered = \"batch-triggered\",\n}\n\nconst defaultSyncManagerConfig: SyncManagerConfig = {\n maxDeadLettersPerRemote: 100,\n maxInboxBatchSize: 32,\n maxHeldOperationsPerRemote: 10000,\n staleRemotePollWindowMs: 5 * 60_000,\n markerRetryBaseDelayMs: 1_000,\n markerRetryMaxDelayMs: 60_000,\n};\n\nconst PLAN_KEY_TO_JOB_UUID_CAP = 10000;\n\n/**\n * Whether a channel failure says the caller could not authenticate or could not\n * reach the remote, rather than that the remote itself is misconfigured. The\n * remote record stays on disk for these so a retry after sign-in can re-add it.\n */\nfunction isCredentialOrNetworkError(error: unknown): boolean {\n if (isDriveAuthError(error)) {\n return true;\n }\n return error instanceof GraphQLRequestError && error.category === \"network\";\n}\n\nconst holdKey = (documentId: string, branch: string): string =>\n `${documentId}\\u0000${branch}`;\n\nconst VERSION_CACHE_CAP = 10000;\n\n/** The versions a CREATE_DOCUMENT among `operations` fixes for the document. */\nfunction createdVersions(\n operations: readonly OperationWithContext[],\n documentId: string,\n): ProtocolVersions | undefined {\n for (const { operation, context } of operations) {\n if (\n context.documentId === documentId &&\n operation.action.type === \"CREATE_DOCUMENT\"\n ) {\n const input = operation.action.input as {\n protocolVersions?: ProtocolVersions;\n };\n return input.protocolVersions ?? {};\n }\n }\n return undefined;\n}\n\n/** The load was refused because this very id is purged here: a drop. */\nfunction isPurgedFailure(\n error: ErrorInfo | undefined,\n documentId: string,\n): boolean {\n return (\n error?.name === \"DocumentPurgedError\" && error.documentId === documentId\n );\n}\n\nfunction carriesMarker(syncOp: SyncOperation): boolean {\n return syncOp.operations.some((op) => isPurgeMarker(op));\n}\n\nfunction markerIdsOf(syncOp: SyncOperation): string[] {\n return syncOp.operations\n .filter((op) => isPurgeMarker(op))\n .map((op) => op.operation.id);\n}\n\n/** Refused, or purged under another id: final, unlike an outage. */\nfunction isRefusedMarker(error: ErrorInfo | undefined): boolean {\n return (\n error?.name === \"InvalidSignatureError\" ||\n error?.name === \"DocumentPurgedError\"\n );\n}\n\n/** A job's dependency chain through one derivation's emitted batches. */\ntype EmitChain = {\n lastJobByDoc: Map<string, string>;\n prevChainJobId?: string;\n};\n\n/** Where a sync operation's run of ordinals begins. */\nfunction firstOrdinalOf(syncOp: SyncOperation): number {\n return syncOp.operations.length > 0\n ? syncOp.operations[0].context.ordinal\n : 0;\n}\n\nexport class SyncManager\n implements ISyncManager, IDeliveryTracking, IPurgeRefusalRecorder\n{\n private readonly logger: ILogger;\n private readonly remoteStorage: ISyncRemoteStorage;\n private readonly cursorStorage: ISyncCursorStorage;\n private readonly deadLetterStorage: ISyncDeadLetterStorage;\n private readonly channelFactory: IChannelFactory;\n private readonly operationIndex: IOperationIndex;\n private readonly reactor: IReactor;\n private readonly eventBus: IEventBus;\n private readonly remotes: Map<string, Remote>;\n private readonly awaiter: JobAwaiter;\n private readonly syncAwaiter: SyncAwaiter;\n private readonly abortController = new AbortController();\n private isShutdown: boolean;\n private eventUnsubscribe?: () => void;\n private failedEventUnsubscribe?: () => void;\n private readonly batchAggregator: BatchAggregator;\n private readonly syncStatusTracker: SyncStatusTracker;\n private readonly config: SyncManagerConfig;\n private readonly connectionStateUnsubscribes: Map<string, () => void> =\n new Map();\n private readonly quarantinedDocumentIds = new Set<string>();\n private readonly purgedDocumentIds = new Set<string>();\n private readonly purges?: PurgeLookup;\n private readonly delivery?: DeliveryLookup;\n private readonly forgetDocument?: (documentId: string) => void;\n private readonly backfillAbortControllers = new Map<\n string,\n AbortController\n >();\n private readonly planKeyToJobUuid = new Map<string, string>();\n private readonly evictedOutboxFloors = new Map<string, number>();\n private readonly prunePending = new Set<string>();\n private pruneChain: Promise<void> = Promise.resolve();\n private derivingOutboxes = 0;\n private pruneDrainDeferred = false;\n private readonly removing = new Set<string>();\n private readonly lastEnqueuedJobIdByKey = new Map<string, string>();\n private readonly watermark: ISettledWatermark;\n // remote name -> ordinal its outbox is owed through\n private readonly owed = new Map<string, number>();\n // remote name -> settled ordinal its last complete derivation read through\n private readonly derivedThrough = new Map<string, number>();\n // settled ordinals at or below this have owed their collections' remotes\n private sweptThrough = 0;\n private settledUnsubscribe?: () => void;\n private inboxChunkChain: Promise<void> = Promise.resolve();\n private readonly capabilities: readonly PeerCapability[];\n private readonly manifest: PeerManifest;\n private readonly localSupport: Supports;\n private readonly legacy: Supports;\n private readonly wanted: { [protocol: string]: number } = {};\n private readonly holds: ISyncHoldStorage;\n private readonly heldKeys = new Map<string, Map<string, SyncHoldRecord>>();\n private readonly records = new Map<string, RemoteMeta>();\n private readonly versionCache = new Map<string, ProtocolVersions>();\n private readonly protocolVersionsOf: (\n documentId: string,\n branch: string,\n ) => Promise<ProtocolVersions | undefined>;\n private readonly peerUpdates = new Map<string, Promise<void>>();\n private readonly peerUnsubscribes = new Map<string, () => void>();\n // remote name -> marker op id -> the inbox item loading it\n private readonly receivedMarkers = new Map<\n string,\n Map<string, SyncOperation>\n >();\n private readonly markerStorage: ISyncReceivedMarkerStorage;\n private readonly refusalStorage: ISyncPurgeRefusalStorage;\n // remote name + marker id -> its storage writes, applied in order\n private readonly markerWrites = new Map<string, Promise<void>>();\n // reloaded from storage, so already stored\n private readonly restoredMarkers = new WeakSet<SyncOperation>();\n // inbox sync op id -> retry of its failed marker load\n private readonly markerRetries = new Map<\n string,\n {\n remoteName: string;\n attempt: number;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n constructor(\n logger: ILogger,\n remoteStorage: ISyncRemoteStorage,\n cursorStorage: ISyncCursorStorage,\n deadLetterStorage: ISyncDeadLetterStorage,\n channelFactory: IChannelFactory,\n operationIndex: IOperationIndex,\n reactor: IReactor,\n eventBus: IEventBus,\n driveContainerTypes: ReadonlySet<string>,\n watermark: ISettledWatermark,\n config: Partial<SyncManagerConfig> = {},\n localPeer: LocalPeer = { capabilities: PEER_CAPABILITIES, flags: {} },\n holds: ISyncHoldStorage = new InMemorySyncHoldStorage(),\n purges?: PurgeLookup,\n receivedMarkers: ISyncReceivedMarkerStorage = new InMemorySyncReceivedMarkerStorage(),\n delivery?: DeliveryLookup,\n refusals: ISyncPurgeRefusalStorage = new InMemorySyncPurgeRefusalStorage(),\n ) {\n this.markerStorage = receivedMarkers;\n this.refusalStorage = refusals;\n this.watermark = watermark;\n this.purges = purges;\n this.delivery = delivery;\n this.forgetDocument = localPeer.forgetDocument;\n this.capabilities = localPeer.capabilities;\n // A restart always moves the start time forward, which is all ordering needs.\n this.manifest = localPeerManifest(\n localPeer.capabilities,\n localPeer.flags,\n localPeer.appKey,\n localPeer.sequence ?? Date.now(),\n );\n this.localSupport = localSupports(localPeer.capabilities, localPeer.flags);\n this.legacy = legacySupports(localPeer.capabilities);\n for (const capability of localPeer.capabilities) {\n if (capability.kind !== \"protocol\") continue;\n // Only purged documents carry it; no new document takes it.\n if (capability.name === DOCUMENT_PURGE_PROTOCOL) continue;\n const supported = capability.supported(localPeer.flags);\n const wanted =\n capability.preferred?.(localPeer.flags) ??\n (supported.length > 0 ? Math.max(...supported) : undefined);\n if (wanted !== undefined) this.wanted[capability.name] = wanted;\n }\n this.holds = holds;\n this.protocolVersionsOf =\n localPeer.protocolVersionsOf ??\n ((documentId, branch) => this.indexedVersions(documentId, branch));\n this.logger = logger;\n this.remoteStorage = remoteStorage;\n this.cursorStorage = cursorStorage;\n this.deadLetterStorage = deadLetterStorage;\n this.channelFactory = channelFactory;\n this.operationIndex = operationIndex;\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.config = { ...defaultSyncManagerConfig, ...config };\n this.remotes = new Map();\n this.awaiter = new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n this.syncAwaiter = new SyncAwaiter(eventBus);\n this.isShutdown = false;\n this.batchAggregator = new BatchAggregator(\n logger,\n driveContainerTypes,\n (batch) => this.processCompleteBatch(batch),\n () => this.deriveSettled(),\n );\n this.syncStatusTracker = new SyncStatusTracker();\n }\n\n async startup(): Promise<void> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is already shutdown and cannot be started\");\n }\n\n try {\n const quarantinedIds =\n await this.deadLetterStorage.listQuarantinedDocumentIds();\n for (const id of quarantinedIds) {\n this.quarantinedDocumentIds.add(id);\n }\n } catch (error) {\n this.logger.error(\n \"Failed to load quarantined document IDs (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n try {\n for (const id of (await this.purges?.listPurged()) ?? []) {\n this.purgedDocumentIds.add(id);\n }\n } catch (error) {\n this.logger.error(\n \"Failed to load purged document IDs (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n try {\n for (const hold of await this.holds.list()) {\n this.heldFor(hold.remoteName).set(\n holdKey(hold.documentId, hold.branch),\n hold,\n );\n }\n } catch (error) {\n this.logger.error(\n \"Failed to load sync holds (@error)\",\n error instanceof Error ? error.message : String(error),\n );\n }\n\n const remoteRecords = await this.remoteStorage.list();\n const head = await this.watermarkHead();\n this.sweptThrough = Math.max(this.sweptThrough, head);\n\n for (const record of remoteRecords) {\n const channel = this.channelFactory.instance(\n record.id,\n record.name,\n record.channelConfig,\n this.cursorStorage,\n record.collectionId,\n record.filter,\n this.operationIndex,\n record.options,\n );\n\n const remote: Remote = {\n meta: {\n id: record.id,\n name: record.name,\n collectionId: record.collectionId,\n channelConfig: record.channelConfig,\n filter: record.filter,\n options: record.options,\n peer: record.peer,\n },\n channel,\n };\n\n this.remotes.set(record.name, remote);\n this.owe(record.name, head);\n this.records.set(record.name, remote.meta);\n await this.loadDeadLetters(remote);\n await this.restoreReceivedMarkers(remote);\n // Restored, or pushed while the remote was reachable but unwired.\n const unheard = [...remote.channel.inbox.items];\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n this.logger.error(\n \"Error initializing channel for remote (@name, @error)\",\n record.name,\n error instanceof Error ? error.message : String(error),\n );\n await this.dropRemoteAfterFailedInit(remote, false);\n continue;\n }\n if (unheard.length > 0) this.handleInboxAdded(remote, unheard);\n await this.peerUpdates.get(record.name);\n\n // backfill channels asynchronously -- don't block startup\n const outboxAckOrdinal = remote.channel.outbox.ackOrdinal;\n if (outboxAckOrdinal > 0) {\n const backfillController = new AbortController();\n this.backfillAbortControllers.set(record.name, backfillController);\n void this.updateOutbox(\n remote,\n outboxAckOrdinal,\n OutboxMode.Backfill,\n backfillController.signal,\n )\n .catch((error) => {\n if (backfillController.signal.aborted) return;\n this.logger.error(\n \"Backfill failed for remote @RemoteName: @Error\",\n remote.meta.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(record.name);\n void this.drainPrunes();\n });\n }\n }\n\n this.settledUnsubscribe = this.watermark.onAdvance(\n () => void this.batchAggregator.enqueueSettled(),\n );\n\n this.eventUnsubscribe = this.eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n async (_type, event) => this.batchAggregator.enqueueWriteReady(event),\n );\n\n this.failedEventUnsubscribe = this.eventBus.subscribe<JobFailedEvent>(\n ReactorEventTypes.JOB_FAILED,\n async (_type, event) => this.batchAggregator.handleJobFailed(event),\n );\n }\n\n shutdown(): ShutdownStatus {\n this.isShutdown = true;\n this.abortController.abort();\n for (const controller of this.backfillAbortControllers.values()) {\n controller.abort();\n }\n this.backfillAbortControllers.clear();\n this.planKeyToJobUuid.clear();\n this.lastEnqueuedJobIdByKey.clear();\n this.prunePending.clear();\n this.pruneDrainDeferred = false;\n this.batchAggregator.clear();\n this.owed.clear();\n for (const retry of this.markerRetries.values()) clearTimeout(retry.timer);\n this.markerRetries.clear();\n this.settledUnsubscribe?.();\n this.settledUnsubscribe = undefined;\n\n if (this.eventUnsubscribe) {\n this.eventUnsubscribe();\n this.eventUnsubscribe = undefined;\n }\n\n if (this.failedEventUnsubscribe) {\n this.failedEventUnsubscribe();\n this.failedEventUnsubscribe = undefined;\n }\n\n this.awaiter.shutdown();\n this.syncAwaiter.shutdown();\n this.syncStatusTracker.clear();\n\n for (const unsub of this.connectionStateUnsubscribes.values()) {\n unsub();\n }\n this.connectionStateUnsubscribes.clear();\n for (const unsub of this.peerUnsubscribes.values()) {\n unsub();\n }\n this.peerUnsubscribes.clear();\n this.peerUpdates.clear();\n\n const promises: Promise<void>[] = [];\n for (const remote of this.remotes.values()) {\n promises.push(remote.channel.shutdown());\n }\n\n this.remotes.clear();\n\n return {\n isShutdown: true,\n completed: Promise.all(promises).then(() => undefined),\n };\n }\n\n getByName(name: string): Remote {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n return remote;\n }\n\n getById(id: string): Remote {\n for (const remote of this.remotes.values()) {\n if (remote.meta.id === id) {\n return remote;\n }\n }\n throw new Error(`Remote with id '${id}' does not exist`);\n }\n\n async bindRemote(id: string, boundAddress: string): Promise<void> {\n const remote = this.getById(id);\n const bound = remote.meta.options.boundAddress;\n\n if (bound === boundAddress) {\n return;\n }\n\n if (bound !== undefined) {\n throw new Error(\n `Remote with id '${id}' is already bound to another address`,\n );\n }\n\n remote.meta.options = { ...remote.meta.options, boundAddress };\n\n await this.remoteStorage.upsert(this.recordOf(remote.meta));\n }\n\n localManifest(): PeerManifest {\n return this.manifest;\n }\n\n async setPeerManifest(\n id: string,\n manifest: PeerManifest | null,\n ): Promise<void> {\n await this.queuePeerManifest(this.getById(id), manifest);\n }\n\n /** Applied in arrival order per remote; the handshake awaits the tail. */\n private queuePeerManifest(\n remote: Remote,\n manifest: PeerManifest | null,\n undelivered?: readonly SyncOperation[],\n ): Promise<void> {\n const name = remote.meta.name;\n const next = (this.peerUpdates.get(name) ?? Promise.resolve()).then(() =>\n this.applyPeerManifest(remote, manifest, undelivered),\n );\n const settled = next.catch((error: unknown) => {\n this.logger.error(\n \"Failed to record the peer manifest of @name: @error\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n });\n this.peerUpdates.set(name, settled);\n return next;\n }\n\n private async applyPeerManifest(\n remote: Remote,\n manifest: PeerManifest | null,\n undelivered?: readonly SyncOperation[],\n ): Promise<void> {\n const name = remote.meta.name;\n if (this.remotes.get(name) !== remote || this.removing.has(name)) {\n return;\n }\n const known = remote.meta.peer;\n if (isOlderManifest(manifest, known?.manifest)) {\n return;\n }\n if (\n known !== undefined &&\n (known.manifest?.revision ?? null) === (manifest?.revision ?? null)\n ) {\n // A release interrupted before its hold was removed is retried here.\n if (this.heldKeys.get(name)?.size) {\n await this.releaseSupported(remote);\n }\n return;\n }\n remote.meta.peer = { manifest, receivedAtUtcMs: Date.now() };\n await this.remoteStorage.upsert(this.recordOf(remote.meta));\n await this.holdUnsupported(remote, undelivered);\n await this.releaseSupported(remote);\n }\n\n async listHolds(\n filter: { remoteName?: string; documentId?: string } = {},\n ): Promise<SyncHold[]> {\n const records = await this.holds.list(filter);\n return records.map((record) => ({\n remoteName: record.remoteName,\n documentId: record.documentId,\n branch: record.branch,\n reason: {\n protocol: record.protocol,\n version: record.version,\n peerSupports:\n this.agreement().peer(record.remoteName).protocols[record.protocol] ??\n [],\n },\n heldAtUtcMs: record.heldAtUtcMs,\n }));\n }\n\n async pendingDelivery(\n documentId: string,\n ordinal: number,\n ): Promise<PendingDelivery[]> {\n if (!this.delivery) {\n throw new Error(\"Delivery tracking needs a delivery lookup\");\n }\n return pendingDelivery(\n {\n remotes: [...this.remotes.values()].filter(\n (remote) => !this.removing.has(remote.meta.name),\n ),\n cursors: this.cursorStorage,\n holds: this.holds,\n lookup: this.delivery,\n },\n documentId,\n ordinal,\n );\n }\n\n recordPurgeRefusal(refusal: SyncPurgeRefusedEvent): Promise<void> {\n return this.persistRefusals([refusal]);\n }\n\n async recordPolledMarkerRefusals(\n remoteName: string,\n refusals: readonly PolledMarkerRefusal[],\n ): Promise<void> {\n const remote = this.getByName(remoteName);\n const tombstoned = refusals\n .slice(0, MAX_POLLED_REFUSALS)\n .filter((refusal) => this.purgedDocumentIds.has(refusal.documentId));\n if (tombstoned.length === 0) return;\n const collections = await this.operationIndex.getCollectionsForDocuments([\n ...new Set(tombstoned.map((refusal) => refusal.documentId)),\n ]);\n const key = remote.meta.collectionId.key;\n const owed = new Map<string, SyncPurgeRefusedEvent>();\n for (const refusal of tombstoned) {\n if (!collections[refusal.documentId]?.includes(key)) continue;\n owed.set(`${refusal.documentId}\\u0000${refusal.branch}`, {\n remoteName,\n documentId: refusal.documentId,\n branch: refusal.branch,\n errorMessage: `Marker refused by ${remoteName}`,\n });\n }\n await this.persistRefusals([...owed.values()]);\n }\n\n private async persistRefusals(\n refusals: readonly SyncPurgeRefusedEvent[],\n ): Promise<void> {\n if (refusals.length === 0) return;\n const refusedAtUtcMs = Date.now();\n await this.refusalStorage.record(\n refusals.map((refusal) => ({\n remoteName: refusal.remoteName,\n documentId: refusal.documentId,\n branch: refusal.branch,\n refusedAtUtcMs,\n })),\n );\n for (const refusal of refusals) {\n await this.eventBus\n .emit(SyncEventTypes.PURGE_REFUSED, refusal)\n .catch(() => {});\n }\n }\n\n agreement(): IPeerAgreement {\n return createPeerAgreement(\n { local: this.manifest, legacy: this.legacy, wanted: this.wanted },\n [...this.records.values()],\n );\n }\n\n private heldFor(remoteName: string): Map<string, SyncHoldRecord> {\n let held = this.heldKeys.get(remoteName);\n if (!held) {\n held = new Map();\n this.heldKeys.set(remoteName, held);\n }\n return held;\n }\n\n /** Once per id: a second pass would drop the holds its marker earned. */\n private tombstone(documentId: string): void {\n if (this.purgedDocumentIds.has(documentId)) return;\n this.purgedDocumentIds.add(documentId);\n this.quarantinedDocumentIds.delete(documentId);\n const prefix = holdKey(documentId, \"\");\n for (const key of [...this.versionCache.keys()]) {\n if (key.startsWith(prefix)) this.versionCache.delete(key);\n }\n this.forgetDocument?.(documentId);\n for (const held of this.heldKeys.values()) {\n for (const [key, record] of [...held]) {\n if (record.documentId === documentId) held.delete(key);\n }\n }\n for (const remote of this.remotes.values()) {\n // Sent or in flight too: a lost response or a push retry would resend it.\n const stale = remote.channel.outbox.items.filter(\n (item) =>\n item.documentId === documentId &&\n item.status < SyncOperationStatus.Applied &&\n !carriesMarker(item),\n );\n // Applied, so a served cursor is not pinned below what is gone.\n for (const item of stale) item.executed();\n if (stale.length > 0) remote.channel.outbox.remove(...stale);\n const dead = remote.channel.deadLetter.items.filter(\n (item) => item.documentId === documentId,\n );\n if (dead.length > 0) remote.channel.deadLetter.remove(...dead);\n }\n }\n\n private async forgetRemote(name: string): Promise<void> {\n this.records.delete(name);\n this.heldKeys.delete(name);\n await this.holds.removeRemote(name);\n await Promise.allSettled(this.markerWritesOf([name]));\n await this.markerStorage.removeRemote(name);\n }\n\n private peerSupportsOf(remote: Remote): Supports {\n return peerSupports(remote.meta.peer?.manifest ?? null, this.capabilities);\n }\n\n /** Cached once found: a document's versions change only by a purge. */\n private async versionsOf(\n documentId: string,\n branch: string,\n ): Promise<ProtocolVersions | undefined> {\n if (this.purgedDocumentIds.has(documentId)) {\n return purgedProtocolVersions();\n }\n const key = holdKey(documentId, branch);\n const cached = this.versionCache.get(key);\n if (cached) return cached;\n let versions: ProtocolVersions | undefined;\n try {\n versions = await this.protocolVersionsOf(documentId, branch);\n } catch {\n versions = undefined;\n }\n if (versions) {\n if (this.versionCache.size >= VERSION_CACHE_CAP) {\n const oldest = this.versionCache.keys().next().value;\n if (oldest !== undefined) this.versionCache.delete(oldest);\n }\n this.versionCache.set(key, versions);\n }\n return versions;\n }\n\n /** The fallback without a meta cache: the document's indexed creation. */\n private async indexedVersions(\n documentId: string,\n branch: string,\n ): Promise<ProtocolVersions | undefined> {\n const page = await this.operationIndex.get(\n documentId,\n { branch, scopes: [\"document\"] },\n { cursor: \"0\", limit: 10 },\n );\n return createdVersions(\n page.results.map((entry) => toOperationWithContext(entry)),\n documentId,\n );\n }\n\n private async hold(\n remote: Remote,\n documentId: string,\n branch: string,\n reason: HoldReason,\n ): Promise<void> {\n const held = this.heldFor(remote.meta.name);\n const key = holdKey(documentId, branch);\n if (held.has(key)) return;\n const record: SyncHoldRecord = {\n remoteName: remote.meta.name,\n documentId,\n branch,\n protocol: reason.protocol,\n version: reason.version,\n heldAtUtcMs: Date.now(),\n };\n held.set(key, record);\n try {\n await this.holds.upsert(record);\n } catch (error) {\n if (DocumentPurgedError.isError(error)) {\n held.delete(key);\n this.tombstone(documentId);\n return;\n }\n this.logger.error(\n \"Failed to persist a sync hold (@remote, @documentId): @error\",\n remote.meta.name,\n documentId,\n error instanceof Error ? error.message : String(error),\n );\n }\n void this.eventBus\n .emit(SyncEventTypes.SYNC_HELD, {\n remoteName: remote.meta.name,\n documentId,\n branch,\n reason,\n } satisfies SyncHeldEvent)\n .catch(() => {});\n }\n\n /** Narrowed: unsent outbox items for documents the peer cannot run. */\n private async holdUnsupported(\n remote: Remote,\n undelivered: readonly SyncOperation[] = [],\n ): Promise<void> {\n const support = this.peerSupportsOf(remote);\n if (coversLocal(support, this.localSupport, this.capabilities)) return;\n const verdicts = new Map<string, HoldReason | null>();\n const held: SyncOperation[] = [];\n const unsent = new Set(undelivered);\n for (const item of remote.channel.outbox.items) {\n if (\n (item.status !== SyncOperationStatus.Unknown ||\n item.emittedCount > 0) &&\n !unsent.has(item)\n ) {\n continue;\n }\n const key = holdKey(item.documentId, item.branch);\n let reason = verdicts.get(key);\n if (reason === undefined) {\n const versions =\n createdVersions(item.operations, item.documentId) ??\n (await this.versionsOf(item.documentId, item.branch));\n reason =\n (versions && holdReason(support, versions, this.capabilities)) ??\n null;\n verdicts.set(key, reason);\n }\n if (reason) {\n held.push(item);\n await this.hold(remote, item.documentId, item.branch, reason);\n }\n }\n if (held.length > 0) {\n remote.channel.outbox.remove(...held);\n }\n }\n\n /** Widened: held documents the peer can now run go out whole. */\n private async releaseSupported(remote: Remote): Promise<void> {\n const held = this.heldKeys.get(remote.meta.name);\n if (!held?.size) return;\n const support = this.peerSupportsOf(remote);\n for (const [key, record] of [...held]) {\n const versions = await this.versionsOf(record.documentId, record.branch);\n if (versions && holdReason(support, versions, this.capabilities)) {\n continue;\n }\n held.delete(key);\n await this.backfillDocument(remote, record.documentId, record.branch);\n await this.holds.remove(\n record.remoteName,\n record.documentId,\n record.branch,\n );\n void this.eventBus\n .emit(SyncEventTypes.SYNC_RELEASED, {\n remoteName: record.remoteName,\n documentId: record.documentId,\n branch: record.branch,\n } satisfies SyncReleasedEvent)\n .catch(() => {});\n }\n }\n\n /**\n * Queues a released document's whole history. The peer never received it\n * from this remote, so sinceTimestampUtcMs does not apply; the receiver\n * dedups by action id.\n */\n private async backfillDocument(\n remote: Remote,\n documentId: string,\n branch: string,\n ): Promise<void> {\n const entries = [];\n let page = await this.operationIndex.get(\n documentId,\n { branch },\n undefined,\n this.abortController.signal,\n );\n for (;;) {\n entries.push(...page.results);\n if (!page.next) break;\n page = await page.next();\n }\n let operations = entries\n .filter((entry) => entry.sourceRemote !== remote.meta.name)\n .map((entry) => toOperationWithContext(entry));\n operations = filterForRemote(operations, remote.meta.filter);\n if (operations.length === 0) return;\n operations.sort((a, b) => {\n if (a.context.scope !== b.context.scope) {\n if (a.context.scope === \"document\") return -1;\n if (b.context.scope === \"document\") return 1;\n return a.context.scope < b.context.scope ? -1 : 1;\n }\n return a.context.ordinal - b.context.ordinal;\n });\n this.emitBatches(remote, operations, OutboxMode.Backfill, {\n lastJobByDoc: new Map(),\n });\n }\n\n /** A peer that announced support refused the document: hold it from that peer. */\n private async holdRefused(\n remote: Remote,\n syncOp: SyncOperation,\n ): Promise<void> {\n const versions =\n (await this.versionsOf(syncOp.documentId, syncOp.branch)) ?? {};\n const support = this.peerSupportsOf(remote);\n const protocols = this.capabilities.filter(\n (capability) =>\n capability.kind === \"protocol\" &&\n versions[capability.name] !== undefined,\n );\n const capability =\n protocols.find(\n (candidate) => !candidate.baseline.includes(versions[candidate.name]),\n ) ?? protocols.at(0);\n const protocol = capability?.name ?? \"unknown\";\n await this.hold(remote, syncOp.documentId, syncOp.branch, {\n protocol,\n version: versions[protocol] ?? 0,\n peerSupports: support.protocols[protocol] ?? [],\n });\n }\n\n /**\n * Refuses, before load, what this reactor cannot run and what the sending\n * peer does not announce. Neither quarantines the document.\n */\n private async refuseOnReceipt(\n remote: Remote,\n syncOps: readonly SyncOperation[],\n ): Promise<Set<SyncOperation>> {\n // A manifest the channel heard before handing these over is applied first.\n await this.peerUpdates.get(remote.meta.name);\n const peer = this.peerSupportsOf(remote);\n const refused = new Set<SyncOperation>();\n for (const syncOp of syncOps) {\n // A marker runs no reducer, so neither side's versions bear on it.\n if (carriesMarker(syncOp)) continue;\n const versions =\n createdVersions(syncOp.operations, syncOp.documentId) ??\n (await this.versionsOf(syncOp.documentId, syncOp.branch));\n if (versions && this.refuse(remote, syncOp, versions, peer)) {\n refused.add(syncOp);\n }\n }\n return refused;\n }\n\n /** Dead-letters a refused sync operation. */\n private refuse(\n remote: Remote,\n syncOp: SyncOperation,\n versions: ProtocolVersions,\n peer: Supports,\n ): boolean {\n let errorType: SyncOperationErrorType;\n let reason = holdReason(this.localSupport, versions, this.capabilities);\n if (reason) {\n errorType = \"UNSUPPORTED_PROTOCOL\";\n } else {\n reason = holdReason(peer, versions, this.capabilities);\n if (!reason) return false;\n errorType = \"PEER_PROTOCOL_UNSUPPORTED\";\n }\n\n const message =\n errorType === \"UNSUPPORTED_PROTOCOL\"\n ? `Document ${syncOp.documentId} requires ${reason.protocol} ${reason.version}, which this reactor does not support`\n : `Remote ${remote.meta.name} wrote into document ${syncOp.documentId} at ${reason.protocol} ${reason.version}, which its peer does not announce`;\n syncOp.failed(\n new ChannelError(ChannelErrorSource.Inbox, new Error(message), errorType),\n );\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return true;\n }\n\n /** Whether anything could be held from this remote; synchronous when not. */\n private gates(remote: Remote): boolean {\n return (\n (this.heldKeys.get(remote.meta.name)?.size ?? 0) > 0 ||\n !coversLocal(\n this.peerSupportsOf(remote),\n this.localSupport,\n this.capabilities,\n )\n );\n }\n\n /** Drops rows of documents the remote's peer cannot run, recording holds. */\n private async gateOutbound(\n remote: Remote,\n operations: OperationWithContext[],\n ): Promise<OperationWithContext[]> {\n if (operations.length === 0) return operations;\n const held = this.heldKeys.get(remote.meta.name);\n const support = this.peerSupportsOf(remote);\n\n const verdicts = new Map<string, boolean>();\n const kept: OperationWithContext[] = [];\n for (const entry of operations) {\n const { documentId, branch } = entry.context;\n const key = holdKey(documentId, branch);\n let isHeld = verdicts.get(key);\n if (isHeld === undefined) {\n isHeld = held?.has(key) ?? false;\n if (!isHeld) {\n const versions =\n createdVersions(operations, documentId) ??\n (await this.versionsOf(documentId, branch));\n const reason =\n versions && holdReason(support, versions, this.capabilities);\n if (reason) {\n await this.hold(remote, documentId, branch, reason);\n isHeld = true;\n }\n }\n verdicts.set(key, isHeld);\n }\n if (!isHeld) kept.push(entry);\n }\n return kept;\n }\n\n private recordOf(meta: RemoteMeta): RemoteRecord {\n return {\n id: meta.id,\n name: meta.name,\n collectionId: meta.collectionId,\n channelConfig: meta.channelConfig,\n filter: meta.filter,\n options: meta.options,\n status: {\n push: createIdleHealth(),\n pull: createIdleHealth(),\n },\n peer: meta.peer,\n };\n }\n\n async add(\n name: string,\n collectionId: DriveCollectionId,\n channelConfig: ChannelConfig,\n filter: RemoteFilter = { documentId: [], scope: [], branch: \"\" },\n options: RemoteOptions = { sinceTimestampUtcMs: \"0\" },\n id?: string,\n peer?: PeerManifest | null,\n ): Promise<Remote> {\n if (this.isShutdown) {\n throw new Error(\"SyncManager is shutdown and cannot add remotes\");\n }\n\n if (this.remotes.has(name)) {\n throw new Error(`Remote with name '${name}' already exists`);\n }\n\n this.logger.debug(\n \"Adding remote (@name, @collectionId, @channelConfig, @filter, @options, @id)\",\n name,\n collectionId.key,\n channelConfig,\n filter,\n options,\n id,\n );\n\n const remoteId = id ?? crypto.randomUUID();\n\n // Written before the first backfill, so its derivation is already gated.\n const meta: RemoteMeta = {\n id: remoteId,\n name,\n collectionId,\n channelConfig,\n filter,\n options,\n peer:\n peer === undefined\n ? undefined\n : { manifest: peer, receivedAtUtcMs: Date.now() },\n };\n\n await this.remoteStorage.upsert(this.recordOf(meta));\n\n const channel = this.channelFactory.instance(\n remoteId,\n name,\n channelConfig,\n this.cursorStorage,\n collectionId,\n filter,\n this.operationIndex,\n options,\n );\n\n const remote: Remote = { meta, channel };\n\n this.remotes.set(name, remote);\n this.records.set(name, meta);\n await this.loadDeadLetters(remote);\n await this.restoreReceivedMarkers(remote);\n // Restored, or pushed while the remote was reachable but unwired.\n const unheard = [...remote.channel.inbox.items];\n this.wireChannelCallbacks(remote);\n\n try {\n await channel.init();\n } catch (error) {\n // Only a failure that says the remote itself is unusable may drop its\n // record. A refused credential or an unreachable host says nothing about\n // the configuration, so the record stays and a retry after sign-in can\n // re-add it.\n await this.dropRemoteAfterFailedInit(\n remote,\n !isCredentialOrNetworkError(error),\n );\n\n throw error;\n }\n if (unheard.length > 0) this.handleInboxAdded(remote, unheard);\n await this.peerUpdates.get(name);\n\n this.owe(name, await this.watermarkHead());\n\n // backfill asynchronously -- don't block channel registration\n const backfillController = new AbortController();\n this.backfillAbortControllers.set(name, backfillController);\n void this.updateOutbox(\n remote,\n 0,\n OutboxMode.Backfill,\n backfillController.signal,\n )\n .catch((error) => {\n if (backfillController.signal.aborted) return;\n this.logger.error(\n \"Backfill failed for remote @RemoteName: @Error\",\n remote.meta.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(name);\n void this.drainPrunes();\n });\n\n return remote;\n }\n\n triggerPull(name: string): void {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n remote.channel.triggerPull();\n }\n\n async remove(name: string): Promise<void> {\n const remote = this.remotes.get(name);\n if (!remote) {\n throw new Error(`Remote with name '${name}' does not exist`);\n }\n\n // The channel shuts down and the rows go several awaits before the map\n // entry does, and a batch landing in that gap would otherwise pick this\n // remote up and derive into mailboxes that are already being torn down.\n this.removing.add(name);\n this.owed.delete(name);\n this.derivedThrough.delete(name);\n try {\n await this.teardownRemoteResources(remote);\n\n // delete the remote's data\n await this.remoteStorage.remove(name);\n await this.cursorStorage.remove(name);\n await this.forgetRemote(name);\n } finally {\n // Released last: while the slot is held, a concurrent add of the same\n // name is refused, so it cannot race the storage deletes above. The\n // finally still guarantees the slot is freed if one of them throws.\n this.remotes.delete(name);\n this.removing.delete(name);\n }\n }\n\n /**\n * Drops a remote whose channel.init() rejected, optionally removing its\n * stored record. Ordered like remove(): the registry slot is released last,\n * so a concurrent add of the same name cannot slip in and have its record\n * deleted by the removal below. A failure to tear down must not replace the\n * init error the caller has to classify.\n */\n private async dropRemoteAfterFailedInit(\n remote: Remote,\n removeStorageRecord: boolean,\n ): Promise<void> {\n const name = remote.meta.name;\n // Same guard remove() takes: the remote is registered before init() runs,\n // so a batch could derive into mailboxes this teardown is closing.\n this.removing.add(name);\n try {\n await this.teardownRemoteResources(remote);\n if (removeStorageRecord) {\n await this.remoteStorage.remove(name);\n await this.forgetRemote(name);\n }\n } catch (error) {\n this.logger.error(\n \"Error tearing down remote after failed init (@name, @error)\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n } finally {\n this.remotes.delete(name);\n this.removing.delete(name);\n }\n }\n\n /**\n * Releases everything wiring a remote up holds: the in-flight backfill, the\n * channel, the status tracker entry and the connection-state subscription.\n * The registry slot is deliberately NOT released here -- the caller holds it\n * until any storage removal has finished, so a concurrent add of the same\n * name is refused for the whole teardown.\n */\n private async teardownRemoteResources(remote: Remote): Promise<void> {\n const name = remote.meta.name;\n\n const backfillController = this.backfillAbortControllers.get(name);\n if (backfillController) {\n backfillController.abort();\n this.backfillAbortControllers.delete(name);\n }\n\n try {\n await remote.channel.shutdown();\n } finally {\n this.syncStatusTracker.untrackRemote(name);\n const unsub = this.connectionStateUnsubscribes.get(name);\n if (unsub) {\n unsub();\n this.connectionStateUnsubscribes.delete(name);\n }\n this.evictedOutboxFloors.delete(name);\n this.derivedThrough.delete(name);\n this.prunePending.delete(name);\n this.receivedMarkers.delete(name);\n for (const [id, retry] of [...this.markerRetries]) {\n if (retry.remoteName !== name) continue;\n clearTimeout(retry.timer);\n this.markerRetries.delete(id);\n }\n this.peerUnsubscribes.get(name)?.();\n this.peerUnsubscribes.delete(name);\n this.peerUpdates.delete(name);\n }\n }\n\n list(): Remote[] {\n return Array.from(this.remotes.values());\n }\n\n waitForSync(jobId: string, signal?: AbortSignal): Promise<SyncResult> {\n return this.syncAwaiter.waitForSync(jobId, signal);\n }\n\n getSyncStatus(documentId: string): SyncStatus | undefined {\n return this.syncStatusTracker.getStatus(documentId);\n }\n\n onSyncStatusChange(callback: SyncStatusChangeCallback): () => void {\n return this.syncStatusTracker.onChange(callback);\n }\n\n /** Settles once the remotes' received markers are stored; rejects if one failed. */\n async receiptsStored(remoteNames?: Iterable<string>): Promise<void> {\n await Promise.all(this.markerWritesOf(remoteNames));\n }\n\n private recordPlanKeyMapping(planKey: string, jobId: string): void {\n if (\n !this.planKeyToJobUuid.has(planKey) &&\n this.planKeyToJobUuid.size >= PLAN_KEY_TO_JOB_UUID_CAP\n ) {\n const oldest = this.planKeyToJobUuid.keys().next().value;\n if (oldest !== undefined) {\n this.planKeyToJobUuid.delete(oldest);\n }\n }\n this.planKeyToJobUuid.set(planKey, jobId);\n }\n\n private wireChannelCallbacks(remote: Remote): void {\n remote.channel.inbox.onAdded((syncOps) =>\n this.handleInboxAdded(remote, syncOps),\n );\n remote.channel.inbox.onRemoved((syncOps) =>\n this.handleInboxRemoved(remote, syncOps),\n );\n\n remote.channel.setLocalManifest?.(() => this.manifest);\n const unsubscribePeer = remote.channel.onPeerManifest?.(\n (manifest, undelivered) =>\n this.queuePeerManifest(remote, manifest, undelivered).catch(() => {}),\n );\n if (unsubscribePeer) {\n this.peerUnsubscribes.set(remote.meta.name, unsubscribePeer);\n }\n\n this.syncStatusTracker.trackRemote(remote.meta.name, remote.channel);\n\n const unsubscribe = remote.channel.onConnectionStateChange((snapshot) => {\n void this.eventBus\n .emit(SyncEventTypes.CONNECTION_STATE_CHANGED, {\n remoteName: remote.meta.name,\n remoteId: remote.meta.id,\n previous: snapshot.state,\n current: snapshot.state,\n snapshot,\n } satisfies ConnectionStateChangedEvent)\n .catch(() => {});\n });\n this.connectionStateUnsubscribes.set(remote.meta.name, unsubscribe);\n\n remote.channel.deadLetter.onAdded((added) => {\n // A peer that refused a document it announced: a hold, not a failure.\n const refusals = added.filter(\n (syncOp) =>\n syncOp.error?.source === ChannelErrorSource.Outbox &&\n syncOperationErrorType(syncOp.error) === \"UNSUPPORTED_PROTOCOL\",\n );\n if (refusals.length > 0) {\n remote.channel.deadLetter.remove(...refusals);\n for (const syncOp of refusals) {\n void this.holdRefused(remote, syncOp).catch(() => {});\n }\n }\n const remaining = added.filter((syncOp) => !refusals.includes(syncOp));\n // Quarantine would withhold the marker; persisting would keep a payload.\n const purged = remaining.filter((syncOp) =>\n this.purgedDocumentIds.has(syncOp.documentId),\n );\n for (const syncOp of purged) {\n this.logger.warn(\n \"Dead letter for purged document (@remote, @documentId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n syncOp.error?.message ?? \"unknown\",\n );\n if (syncOp.error?.source !== ChannelErrorSource.Outbox) continue;\n if (syncOperationErrorType(syncOp.error) !== \"MARKER_REFUSED\") continue;\n const refusal = {\n remoteName: remote.meta.name,\n documentId: syncOp.documentId,\n branch: syncOp.branch,\n errorMessage: syncOp.error.error.message,\n };\n void this.recordPurgeRefusal(refusal).catch((error: unknown) => {\n this.logger.error(\n \"Recording a refused marker failed (@remote, @documentId, @error)\",\n refusal.remoteName,\n refusal.documentId,\n error instanceof Error ? error.message : String(error),\n );\n // The remote repeats its report each poll; hear the next one.\n remote.channel.forgetMarkerRefusal?.(\n refusal.documentId,\n refusal.branch,\n );\n });\n }\n if (purged.length > 0) remote.channel.deadLetter.remove(...purged);\n const syncOps = remaining.filter((syncOp) => !purged.includes(syncOp));\n\n for (const syncOp of syncOps) {\n this.logger.error(\n \"Dead letter (@remote, @documentId, @jobId, @error, @dependencies)\",\n remote.meta.name,\n syncOp.documentId,\n syncOp.jobId,\n syncOp.error?.message ?? \"unknown\",\n syncOp.jobDependencies,\n );\n\n const errorType = syncOperationErrorType(syncOp.error);\n\n // A held auth operation must keep syncing: reconciling the two policies\n // needs the traffic a quarantine would stop, in both directions.\n if (quarantinesDocument(errorType)) {\n this.quarantinedDocumentIds.add(syncOp.documentId);\n }\n\n const record: DeadLetterRecord = {\n id: syncOp.id,\n jobId: syncOp.jobId,\n jobDependencies: syncOp.jobDependencies,\n remoteName: syncOp.remoteName,\n documentId: syncOp.documentId,\n scopes: syncOp.scopes,\n branch: syncOp.branch,\n // A refused marker's job dropped the rest; they are not the record.\n operations: carriesMarker(syncOp)\n ? syncOp.operations.filter((op) => isPurgeMarker(op))\n : syncOp.operations,\n errorSource: syncOp.error?.source ?? ChannelErrorSource.None,\n errorMessage: syncOp.error?.error.message ?? \"unknown\",\n errorType,\n };\n\n void this.deadLetterStorage.add(record).catch((err) => {\n if (DocumentPurgedError.isError(err)) {\n this.tombstone(record.documentId);\n return;\n }\n this.logger.error(\n \"Failed to persist dead letter (@id, @error)\",\n record.id,\n err instanceof Error ? err.message : String(err),\n );\n });\n\n void this.eventBus\n .emit(SyncEventTypes.DEAD_LETTER_ADDED, {\n id: record.id,\n jobId: record.jobId,\n remoteName: record.remoteName,\n documentId: record.documentId,\n errorSource: record.errorSource,\n errorType: record.errorType,\n } satisfies DeadLetterAddedEvent)\n .catch(() => {});\n }\n\n // Evict oldest dead letters from mailbox if over capacity\n const items = remote.channel.deadLetter.items;\n if (items.length > this.config.maxDeadLettersPerRemote) {\n const excessCount = items.length - this.config.maxDeadLettersPerRemote;\n const toEvict = items.slice(0, excessCount);\n remote.channel.deadLetter.remove(...toEvict);\n }\n });\n }\n\n private async loadDeadLetters(remote: Remote): Promise<void> {\n let records: DeadLetterRecord[];\n try {\n const page = await this.deadLetterStorage.list(remote.meta.name, {\n cursor: \"0\",\n limit: this.config.maxDeadLettersPerRemote,\n });\n records = page.results;\n } catch (error) {\n this.logger.error(\n \"Failed to load dead letters for remote (@name, @error)\",\n remote.meta.name,\n error instanceof Error ? error.message : String(error),\n );\n return;\n }\n\n if (records.length === 0) {\n return;\n }\n\n // Records come in ordinal DESC order (newest first).\n // Reverse so the Map maintains chronological insertion order (oldest first),\n // which makes eviction (slice from the front) straightforward.\n records.reverse();\n\n const syncOps: SyncOperation[] = [];\n for (const record of records) {\n const syncOp = new SyncOperation(\n record.id,\n record.jobId,\n record.jobDependencies,\n record.remoteName,\n record.documentId,\n record.scopes,\n record.branch,\n record.operations,\n );\n // The stored classification is carried rather than re-derived: only the\n // message was persisted, so a rebuilt Error has no name to classify by,\n // and a non-quarantining type would come back as quarantining.\n syncOp.failed(\n new ChannelError(\n record.errorSource,\n new Error(record.errorMessage),\n record.errorType,\n ),\n );\n syncOps.push(syncOp);\n }\n\n remote.channel.deadLetter.add(...syncOps);\n\n this.logger.debug(\n \"Loaded @count persisted dead letters for remote @name\",\n records.length,\n remote.meta.name,\n );\n }\n\n private getRemotesForCollection(collectionId: string): Remote[] {\n return Array.from(this.remotes.values()).filter(\n (remote) =>\n remote.meta.collectionId.key === collectionId &&\n !this.removing.has(remote.meta.name),\n );\n }\n\n private async processCompleteBatch(batch: PreparedBatch): Promise<void> {\n if (this.isShutdown) return;\n\n for (const { event } of batch.entries) {\n for (const op of event.operations) {\n if (isPurgeMarker(op)) this.tombstone(op.context.documentId);\n }\n }\n\n // get the unique set of collection ids\n const collectionIds = [\n ...new Set(\n Object.values(batch.collectionMemberships).flatMap(\n (collections) => collections,\n ),\n ),\n ];\n\n // get the unique set of affected remotes\n const affectedRemotes: Remote[] = [];\n for (const collectionId of collectionIds) {\n const remotes = this.getRemotesForCollection(collectionId);\n for (const remote of remotes) {\n if (!affectedRemotes.includes(remote)) {\n affectedRemotes.push(remote);\n }\n }\n }\n\n // ack matching inbox items\n for (const remote of affectedRemotes) {\n trimMailboxFromBatch(remote.channel.inbox, batch);\n }\n\n // With no other write open, this batch's own operations are settled now.\n const through = await this.watermark.refresh();\n let batchMax = 0;\n for (const { event } of batch.entries) {\n for (const op of event.operations) {\n batchMax = Math.max(batchMax, op.context.ordinal);\n }\n }\n\n // finally, work through the affected remotes and backfill based on the last operation in the outbox\n for (const remote of affectedRemotes) {\n // A drain between two derivations can remove a remote this list was\n // built before, so membership is read again rather than assumed.\n if (\n !this.remotes.has(remote.meta.name) ||\n this.removing.has(remote.meta.name)\n ) {\n continue;\n }\n\n await this.updateOutbox(\n remote,\n remote.channel.outbox.latestOrdinal,\n OutboxMode.BatchTriggered,\n );\n if (batchMax > through) {\n this.owe(remote.meta.name, batchMax);\n }\n }\n\n await this.drainPrunes();\n }\n\n /** Derives every owed remote up to the watermark. */\n private async deriveSettled(): Promise<void> {\n if (this.isShutdown) return;\n const through = this.watermark.settledThrough;\n await this.oweSettledRange(through);\n for (const [name, upTo] of [...this.owed]) {\n const remote = this.remotes.get(name);\n if (!remote || this.removing.has(name)) {\n this.owed.delete(name);\n continue;\n }\n await this.updateOutbox(\n remote,\n remote.channel.outbox.latestOrdinal,\n OutboxMode.BatchTriggered,\n );\n if (through >= upTo) this.owed.delete(name);\n }\n await this.drainPrunes();\n }\n\n /** Owes the remotes of every collection that moved, event or not. */\n private async oweSettledRange(through: number): Promise<void> {\n if (through <= this.sweptThrough) return;\n const among = [\n ...new Set(\n [...this.remotes.values()]\n .filter(\n (remote) =>\n !this.removing.has(remote.meta.name) &&\n (this.derivedThrough.get(remote.meta.name) ?? -1) < through,\n )\n .map((remote) => remote.meta.collectionId.key),\n ),\n ];\n // A remote added later is owed the head on add(); one derived through\n // `through` has read the range already.\n if (among.length === 0) {\n this.sweptThrough = through;\n return;\n }\n let collectionIds: string[];\n try {\n collectionIds = await this.operationIndex.getCollectionsInRange(\n this.sweptThrough,\n through,\n among,\n this.abortController.signal,\n );\n } catch (error) {\n this.logger.warn(\n \"Settled range sweep failed; the next advance retries it: @error\",\n error,\n );\n return;\n }\n for (const collectionId of collectionIds) {\n for (const remote of this.getRemotesForCollection(collectionId)) {\n const name = remote.meta.name;\n if ((this.derivedThrough.get(name) ?? -1) < through) {\n this.owe(name, through);\n }\n }\n }\n this.sweptThrough = through;\n }\n\n private owe(name: string, upTo: number): void {\n this.owed.set(name, Math.max(this.owed.get(name) ?? 0, upTo));\n }\n\n /** The sequence head after a fresh probe; a remote is owed through it. */\n private async watermarkHead(): Promise<number> {\n try {\n await this.watermark.refresh();\n } catch (error) {\n this.logger.warn(\n \"Settled watermark probe failed; owed remotes wait for the next one: @error\",\n error,\n );\n }\n return this.watermark.status().head;\n }\n\n private handleInboxAdded(remote: Remote, syncOps: SyncOperation[]): void {\n if (this.isShutdown) {\n return;\n }\n\n const eligible: SyncOperation[] = [];\n const dropped: SyncOperation[] = [];\n const received = this.receivedMarkersOf(remote.meta.name);\n // A resent marker whose first copy is still loading or awaiting a retry.\n const loading = (id: string, syncOp: SyncOperation): boolean => {\n const item = received.get(id);\n return (\n item !== undefined &&\n item !== syncOp &&\n item.status !== SyncOperationStatus.Applied &&\n remote.channel.inbox.get(item.id) === item\n );\n };\n for (const syncOp of syncOps) {\n if (carriesMarker(syncOp)) {\n const ids = markerIdsOf(syncOp);\n if (ids.every((id) => loading(id, syncOp))) {\n // Stored again: the pusher resends when its earlier ack failed.\n this.storeReceivedMarker(remote, syncOp);\n dropped.push(syncOp);\n } else {\n for (const id of ids) received.set(id, syncOp);\n this.storeReceivedMarker(remote, syncOp);\n eligible.push(syncOp);\n }\n } else if (this.purgedDocumentIds.has(syncOp.documentId)) {\n dropped.push(syncOp);\n } else if (!this.quarantinedDocumentIds.has(syncOp.documentId)) {\n eligible.push(syncOp);\n }\n }\n // A purged id's history is gone here; a job or a dead letter would restore it.\n for (const syncOp of dropped) {\n this.logger.debug(\n \"Dropping received operations of a purged or already-loading document (@remote, @documentId)\",\n remote.meta.name,\n syncOp.documentId,\n );\n syncOp.executed();\n }\n if (dropped.length > 0) remote.channel.inbox.remove(...dropped);\n if (eligible.length === 0) return;\n\n const keyed: SyncOperation[] = [];\n const nonKeyed: SyncOperation[] = [];\n\n for (const syncOp of eligible) {\n if (syncOp.jobId) {\n keyed.push(syncOp);\n } else {\n nonKeyed.push(syncOp);\n }\n }\n\n for (const syncOp of nonKeyed) {\n void this.applyInboxJob(remote, syncOp);\n }\n\n if (keyed.length > 0) {\n const allItems = keyed.map((syncOp) => ({ remote, syncOp }));\n const chunks = chunkSyncOperations(\n allItems,\n this.config.maxInboxBatchSize,\n );\n void this.processInboxChunks(chunks);\n }\n }\n\n /** Forgets a marker once the item loading it leaves the inbox. */\n private handleInboxRemoved(remote: Remote, syncOps: SyncOperation[]): void {\n const received = this.receivedMarkers.get(remote.meta.name);\n if (received === undefined || received.size === 0) return;\n const name = remote.meta.name;\n for (const syncOp of syncOps) {\n for (const id of markerIdsOf(syncOp)) {\n if (received.get(id) !== syncOp) continue;\n received.delete(id);\n this.writeMarker(name, id, () => this.markerStorage.remove(name, id));\n }\n }\n }\n\n private markerWritesOf(remoteNames?: Iterable<string>): Promise<void>[] {\n if (remoteNames === undefined) return [...this.markerWrites.values()];\n const prefixes = [...remoteNames].map((name) => `${name}\\u0000`);\n return [...this.markerWrites]\n .filter(([key]) => prefixes.some((prefix) => key.startsWith(prefix)))\n .map(([, write]) => write);\n }\n\n /** Kept until its outcome, so a restart does not ack past it unapplied. */\n private storeReceivedMarker(remote: Remote, syncOp: SyncOperation): void {\n if (this.restoredMarkers.has(syncOp)) return;\n if (this.purgedDocumentIds.has(syncOp.documentId)) return;\n const name = remote.meta.name;\n for (const operation of syncOp.operations) {\n if (!isPurgeMarker(operation)) continue;\n const markerId = operation.operation.id;\n this.writeMarker(name, markerId, () =>\n this.markerStorage.upsert({\n remoteName: name,\n markerId,\n documentId: syncOp.documentId,\n branch: syncOp.branch,\n operation,\n receivedAtUtcMs: Date.now(),\n }),\n );\n }\n }\n\n private writeMarker(\n remoteName: string,\n markerId: string,\n write: () => Promise<void>,\n ): void {\n const key = `${remoteName}\\u0000${markerId}`;\n const next = (this.markerWrites.get(key) ?? Promise.resolve())\n .catch(() => undefined)\n .then(write);\n this.markerWrites.set(key, next);\n const settle = () => {\n if (this.markerWrites.get(key) === next) this.markerWrites.delete(key);\n };\n next.then(settle, (error: unknown) => {\n this.logger.error(\n \"Failed to store received marker (@remote, @markerId, @error)\",\n remoteName,\n markerId,\n error instanceof Error ? error.message : String(error),\n );\n settle();\n });\n }\n\n /** Queued before init resets latestOrdinal, so a puller is re-served above it. */\n private async restoreReceivedMarkers(remote: Remote): Promise<void> {\n const name = remote.meta.name;\n let records;\n try {\n records = await this.markerStorage.list(name);\n } catch (error) {\n this.logger.error(\n \"Failed to load received markers for remote (@name, @error)\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n return;\n }\n if (records.length === 0) return;\n const syncOps = records.map((record) => {\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n \"\",\n [],\n name,\n record.documentId,\n [record.operation.context.scope],\n record.branch,\n [record.operation],\n );\n syncOp.transported();\n this.restoredMarkers.add(syncOp);\n return syncOp;\n });\n // Loaded only once init has run, via handleInboxAdded.\n remote.channel.inbox.add(...syncOps);\n }\n\n private receivedMarkersOf(name: string): Map<string, SyncOperation> {\n let received = this.receivedMarkers.get(name);\n if (received === undefined) {\n received = new Map();\n this.receivedMarkers.set(name, received);\n }\n return received;\n }\n\n private processInboxChunks(\n chunks: Array<Array<{ remote: Remote; syncOp: SyncOperation }>>,\n ): Promise<void> {\n const next = this.inboxChunkChain.then(async () => {\n for (const chunk of chunks) {\n if (this.isShutdown) return;\n await this.applyInboxBatch(chunk);\n }\n });\n this.inboxChunkChain = next.catch((err) => {\n this.logger.error(\n \"Inbox chunk processing failed (@error)\",\n err instanceof Error ? err.message : String(err),\n );\n });\n return next;\n }\n\n private async applyInboxJob(\n remote: Remote,\n syncOp: SyncOperation,\n ): Promise<void> {\n const refused = await this.refuseOnReceipt(remote, [syncOp]);\n if (refused.size > 0) {\n return;\n }\n\n const operations: Operation[] = syncOp.operations.map((op) => op.operation);\n\n let jobInfo;\n try {\n jobInfo = await this.reactor.load(\n syncOp.documentId,\n syncOp.branch,\n operations,\n this.abortController.signal,\n { sourceRemote: remote.meta.name },\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to load operations from inbox (@remote, @documentId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n err.message,\n );\n if (carriesMarker(syncOp)) {\n this.retryMarker(remote, syncOp, err.message);\n return;\n }\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n if (syncOp.jobId) this.recordPlanKeyMapping(syncOp.jobId, jobInfo.id);\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n if (this.isShutdown) return;\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error(\n \"Failed to wait for job completion (@remote, @documentId, @jobId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n jobInfo.id,\n err.message,\n );\n if (carriesMarker(syncOp)) {\n this.retryMarker(remote, syncOp, err.message);\n return;\n }\n const channelError = new ChannelError(ChannelErrorSource.Inbox, err);\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n return;\n }\n\n if (this.isShutdown) return;\n\n if (completedJobInfo.status !== JobStatus.FAILED) {\n syncOp.executed();\n if (carriesMarker(syncOp)) this.tombstone(syncOp.documentId);\n } else if (isPurgedFailure(completedJobInfo.error, syncOp.documentId)) {\n this.tombstone(syncOp.documentId);\n syncOp.executed();\n } else {\n const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n this.logger.error(\n \"Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n completedJobInfo.id,\n errorMessage,\n );\n if (carriesMarker(syncOp) && !isRefusedMarker(completedJobInfo.error)) {\n this.retryMarker(remote, syncOp, errorMessage);\n return;\n }\n syncOp.failed(this.inboxFailure(syncOp, completedJobInfo.error));\n remote.channel.deadLetter.add(syncOp);\n }\n\n this.markerRetries.delete(syncOp.id);\n remote.channel.inbox.remove(syncOp);\n }\n\n /** Reloads a marker with backoff; it stays in the inbox, not dead-lettered. */\n private retryMarker(\n remote: Remote,\n syncOp: SyncOperation,\n reason: string,\n ): void {\n if (this.isShutdown) return;\n const attempt = (this.markerRetries.get(syncOp.id)?.attempt ?? 0) + 1;\n const delay = calculateBackoffDelay(\n attempt,\n this.config.markerRetryBaseDelayMs,\n this.config.markerRetryMaxDelayMs,\n Math.random(),\n );\n this.logger.warn(\n \"Purge marker load failed; retrying (@remote, @documentId, @attempt, @delayMs, @error)\",\n remote.meta.name,\n syncOp.documentId,\n attempt,\n Math.round(delay),\n reason,\n );\n const timer = setTimeout(() => {\n void this.redeliverMarker(remote, syncOp).catch((error: unknown) => {\n this.logger.error(\n \"Purge marker retry failed (@remote, @documentId, @error)\",\n remote.meta.name,\n syncOp.documentId,\n error instanceof Error ? error.message : String(error),\n );\n });\n }, delay);\n if (typeof timer === \"object\" && \"unref\" in timer) timer.unref();\n this.markerRetries.set(syncOp.id, {\n remoteName: remote.meta.name,\n attempt,\n timer,\n });\n }\n\n private async redeliverMarker(\n remote: Remote,\n syncOp: SyncOperation,\n ): Promise<void> {\n const name = remote.meta.name;\n if (\n this.isShutdown ||\n this.remotes.get(name) !== remote ||\n this.removing.has(name) ||\n remote.channel.inbox.get(syncOp.id) !== syncOp\n ) {\n this.markerRetries.delete(syncOp.id);\n return;\n }\n if (this.purgedDocumentIds.has(syncOp.documentId)) {\n this.markerRetries.delete(syncOp.id);\n syncOp.executed();\n remote.channel.inbox.remove(syncOp);\n return;\n }\n await this.applyInboxJob(remote, syncOp);\n }\n\n private async applyInboxBatch(\n received: Array<{ remote: Remote; syncOp: SyncOperation }>,\n ): Promise<void> {\n const refused = new Set<SyncOperation>();\n for (const { remote, syncOp } of received) {\n const refusals = await this.refuseOnReceipt(remote, [syncOp]);\n for (const refusal of refusals) {\n refused.add(refusal);\n }\n }\n const items = received.filter(({ syncOp }) => !refused.has(syncOp));\n if (items.length === 0) return;\n const sourceRemote = items[0].remote.meta.name;\n\n const chunkKeys = new Set(items.map(({ syncOp }) => syncOp.jobId));\n const jobs = items.map(({ syncOp }) => {\n const dependsOn: string[] = [];\n const externalDeps: string[] = [];\n for (const dep of syncOp.jobDependencies) {\n if (!dep) continue;\n if (chunkKeys.has(dep)) {\n dependsOn.push(dep);\n } else {\n const uuid = this.planKeyToJobUuid.get(dep);\n if (uuid !== undefined) externalDeps.push(uuid);\n }\n }\n const fifoKey = `${syncOp.documentId}:${syncOp.scopes[0]}:${syncOp.branch}`;\n const prevUuid = this.lastEnqueuedJobIdByKey.get(fifoKey);\n if (prevUuid !== undefined) externalDeps.push(prevUuid);\n return {\n key: syncOp.jobId,\n documentId: syncOp.documentId,\n scope: syncOp.scopes[0],\n branch: syncOp.branch,\n operations: syncOp.operations.map((op) => op.operation),\n dependsOn,\n externalDeps,\n };\n });\n\n const request: BatchLoadRequest = { jobs };\n\n let result: BatchLoadResult;\n try {\n result = await this.reactor.loadBatch(\n request,\n this.abortController.signal,\n { sourceRemote },\n );\n } catch (error) {\n if (this.isShutdown) return;\n for (const { remote, syncOp } of items) {\n const err = error instanceof Error ? error : new Error(String(error));\n if (carriesMarker(syncOp)) {\n this.retryMarker(remote, syncOp, err.message);\n continue;\n }\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n }\n return;\n }\n\n if (this.isShutdown) return;\n\n for (const plan of jobs) {\n if (!(plan.key in result.jobs)) continue;\n const info = result.jobs[plan.key];\n this.recordPlanKeyMapping(plan.key, info.id);\n const fifoKey = `${plan.documentId}:${plan.scope}:${plan.branch}`;\n this.lastEnqueuedJobIdByKey.set(fifoKey, info.id);\n }\n\n for (const { remote, syncOp } of items) {\n if (!(syncOp.jobId in result.jobs)) {\n this.logger.error(\n \"Job key missing from batch load result (@remote, @documentId, @jobId)\",\n remote.meta.name,\n syncOp.documentId,\n syncOp.jobId,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Job key '${syncOp.jobId}' missing from batch load result`),\n );\n syncOp.failed(error);\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n const jobInfo = result.jobs[syncOp.jobId];\n\n let completedJobInfo;\n try {\n completedJobInfo = await this.awaiter.waitForJob(\n jobInfo.id,\n this.abortController.signal,\n );\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) continue;\n const err = error instanceof Error ? error : new Error(String(error));\n if (carriesMarker(syncOp)) {\n this.retryMarker(remote, syncOp, err.message);\n continue;\n }\n syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));\n remote.channel.deadLetter.add(syncOp);\n remote.channel.inbox.remove(syncOp);\n continue;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- isShutdown may change during await\n if (this.isShutdown) return;\n\n if (completedJobInfo.status !== JobStatus.FAILED) {\n syncOp.executed();\n if (carriesMarker(syncOp)) this.tombstone(syncOp.documentId);\n } else if (isPurgedFailure(completedJobInfo.error, syncOp.documentId)) {\n this.tombstone(syncOp.documentId);\n syncOp.executed();\n } else if (\n carriesMarker(syncOp) &&\n !isRefusedMarker(completedJobInfo.error)\n ) {\n this.retryMarker(\n remote,\n syncOp,\n completedJobInfo.error?.message || \"Unknown error\",\n );\n continue;\n } else {\n syncOp.failed(this.inboxFailure(syncOp, completedJobInfo.error));\n remote.channel.deadLetter.add(syncOp);\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n }\n\n /**\n * The dead letter for a load job the executor failed.\n *\n * The classification is passed explicitly because it cannot be recovered\n * downstream: the wrapper carries the failure's message, not the failure, so\n * deriving it from the wrapper's own name would classify every one of these\n * as unclassified and quarantine the document. A held auth operation must\n * keep syncing, because reconciling the two policies needs the traffic a\n * quarantine would stop.\n */\n private inboxFailure(\n syncOp: SyncOperation,\n error: ErrorInfo | undefined,\n ): ChannelError {\n const message = error?.message || \"Unknown error\";\n return new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${message}`),\n carriesMarker(syncOp)\n ? \"MARKER_REFUSED\"\n : classifyJobFailure(error?.name ?? \"Error\"),\n );\n }\n\n /**\n * Where the next outbox derivation starts: at the evicted floor when there is\n * room to take those entries back, and at the caller's cursor otherwise.\n *\n * The floor is forgotten as it is folded in. If the refill overflows again the\n * eviction records a new one, and if it does not, the entries are queued and\n * the outbox itself holds the cursor down.\n */\n private refillOrdinal(remote: Remote, ackOrdinal: number): number {\n const floor = this.evictedOutboxFloors.get(remote.meta.name);\n if (floor === undefined) {\n return ackOrdinal;\n }\n\n if (\n this.outboxOperationCount(remote) >=\n this.config.maxHeldOperationsPerRemote\n ) {\n return ackOrdinal;\n }\n\n this.evictedOutboxFloors.delete(remote.meta.name);\n return Math.min(ackOrdinal, floor - 1);\n }\n\n /**\n * Evicts the newest entries of an outbox that has grown past its bound.\n *\n * Only entries that have never been taken up for transport are eligible, which\n * is what makes this a bound on withheld work rather than a way to drop\n * something already in flight. One entry is always kept, so an outbox whose\n * first entry is larger than the whole cap still makes progress.\n */\n private evictPastOutboxBound(remote: Remote): void {\n const cap = this.config.maxHeldOperationsPerRemote;\n const queued = remote.channel.outbox.items\n .filter((syncOp) => syncOp.status === SyncOperationStatus.Unknown)\n .sort((a, b) => firstOrdinalOf(a) - firstOrdinalOf(b));\n\n let kept = 0;\n let cut = queued.length;\n for (let i = 0; i < queued.length; i++) {\n const size = queued[i].operations.length;\n if (i > 0 && kept + size > cap) {\n cut = i;\n break;\n }\n kept += size;\n }\n\n const evicted = queued.slice(cut);\n if (evicted.length === 0) {\n return;\n }\n\n const floor = firstOrdinalOf(evicted[0]);\n const known = this.evictedOutboxFloors.get(remote.meta.name);\n this.evictedOutboxFloors.set(\n remote.meta.name,\n known === undefined ? floor : Math.min(known, floor),\n );\n\n const staleMs = this.stalePollAgeMs(remote);\n if (staleMs !== undefined) {\n let held = kept;\n for (const syncOp of evicted) {\n held += syncOp.operations.length;\n }\n\n // emitBatches evicts once per page, so a large backfill reaches here\n // repeatedly; the decision and its log belong to the first.\n if (!this.prunePending.has(remote.meta.name)) {\n this.prunePending.add(remote.meta.name);\n this.logger.warn(\n \"Outbox for @RemoteName (@Collection) is past its bound of @Cap operations holding @Held, and it has not been polled for @StaleMs ms; marking the channel for removal once this derivation ends\",\n remote.meta.name,\n remote.meta.collectionId.key,\n cap,\n held,\n staleMs,\n );\n }\n } else {\n this.logger.warn(\n \"Outbox for @RemoteName is past its bound of @Cap operations; evicting @Count entries from ordinal @Floor, to be derived again once it drains\",\n remote.meta.name,\n cap,\n evicted.length,\n floor,\n );\n }\n\n remote.channel.outbox.remove(...evicted);\n }\n\n /**\n * How long a served remote's holder has been silent, if past the window.\n *\n * The channel is asked, rather than its config inspected: SyncManager serves\n * and subscribes with the same interface, both kinds report lastSuccessUtcMs,\n * and the caller-supplied channelConfig.type is a free-form string the\n * factories do not read -- so neither could tell a dead served channel from a\n * client whose switchboard is merely unreachable. Only a channel that claims\n * a holder by reporting when it last heard from one can be removed for that\n * holder's silence; a channel that reports nothing (or 0) is never pruned.\n */\n private stalePollAgeMs(remote: Remote): number | undefined {\n const last = remote.channel.lastHolderPollUtcMs();\n if (last === undefined || last <= 0) {\n return undefined;\n }\n\n const age = Date.now() - last;\n return age >= this.config.staleRemotePollWindowMs ? age : undefined;\n }\n\n /**\n * Removes the remotes marked stale during eviction.\n *\n * Removing a remote that a derivation is still iterating would pull its\n * mailboxes out from under it, so a drain that arrives during one is deferred\n * rather than run; the last derivation to finish re-arms it.\n */\n private drainPrunes(): Promise<void> {\n if (this.derivingOutboxes > 0) {\n this.pruneDrainDeferred = true;\n return Promise.resolve();\n }\n\n const next = this.pruneChain.then(async () => {\n if (this.isShutdown) return;\n for (const name of [...this.prunePending]) {\n // Taken again per remote rather than once on the way in: this body\n // runs behind the chain and across the awaits of each removal, so a\n // derivation can have started since. From here to the `removing` mark\n // inside remove() nothing yields, which is what closes the window.\n if (this.derivingOutboxes > 0) {\n this.pruneDrainDeferred = true;\n return;\n }\n\n this.prunePending.delete(name);\n const remote = this.remotes.get(name);\n if (!remote) continue;\n\n // The mark was made mid-derivation and is acted on after it. A poll in\n // between is the holder saying it is still there -- and a first poll is\n // slow precisely when the outbox is huge, which is the state that\n // marked it -- so the decision is taken again here, against now.\n if (this.stalePollAgeMs(remote) === undefined) {\n this.logger.info(\n \"Stale removal of @name revoked: its holder polled while the outbox was being derived\",\n name,\n );\n continue;\n }\n\n try {\n await this.remove(name);\n } catch (error) {\n this.logger.error(\n \"Failed to remove stale remote (@name, @error)\",\n name,\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n });\n this.pruneChain = next.catch(() => {});\n return next;\n }\n\n private outboxOperationCount(remote: Remote): number {\n let count = 0;\n for (const syncOp of remote.channel.outbox.items) {\n count += syncOp.operations.length;\n }\n return count;\n }\n\n /**\n * Derives this remote's outbox, holding off prunes for the duration.\n *\n * A backfill elsewhere can finish at any await in here and drain the prunes\n * it marked; the count is what keeps that drain from removing the remote this\n * call is still adding to.\n */\n private async updateOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode = OutboxMode.Backfill,\n signal?: AbortSignal,\n ): Promise<void> {\n this.derivingOutboxes++;\n try {\n await this.deriveOutbox(remote, ackOrdinal, mode, signal);\n } finally {\n this.derivingOutboxes--;\n if (this.derivingOutboxes === 0 && this.pruneDrainDeferred) {\n this.pruneDrainDeferred = false;\n void this.drainPrunes();\n }\n }\n }\n\n private emitBatches(\n remote: Remote,\n operations: OperationWithContext[],\n mode: OutboxMode,\n chain: EmitChain,\n ): void {\n operations = operations.filter(\n (op) =>\n isPurgeMarker(op) || !this.purgedDocumentIds.has(op.context.documentId),\n );\n if (operations.length === 0) {\n return;\n }\n\n const batches = batchOperationsByDocument(operations);\n\n const syncOps: SyncOperation[] = [];\n for (const batch of batches) {\n const jobId = crypto.randomUUID();\n const prevJobId = chain.lastJobByDoc.get(batch.documentId);\n\n const deps: string[] = [];\n if (prevJobId) deps.push(prevJobId);\n if (\n mode === OutboxMode.BatchTriggered &&\n chain.prevChainJobId &&\n chain.prevChainJobId !== prevJobId\n ) {\n deps.push(chain.prevChainJobId);\n }\n\n const syncOp = new SyncOperation(\n crypto.randomUUID(),\n jobId,\n deps,\n remote.meta.name,\n batch.documentId,\n [batch.scope],\n batch.branch,\n batch.operations,\n );\n\n syncOps.push(syncOp);\n chain.lastJobByDoc.set(batch.documentId, jobId);\n if (mode === OutboxMode.BatchTriggered) chain.prevChainJobId = jobId;\n }\n\n remote.channel.outbox.add(...syncOps);\n this.evictPastOutboxBound(remote);\n }\n\n private async deriveOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode,\n signal?: AbortSignal,\n ): Promise<void> {\n const composedSignal = signal\n ? AbortSignal.any([signal, this.abortController.signal])\n : this.abortController.signal;\n\n const startOrdinal = this.refillOrdinal(remote, ackOrdinal);\n const throughOrdinal = this.watermark.settledThrough;\n let maxOrdinal = startOrdinal;\n const chain: EmitChain = { lastJobByDoc: new Map() };\n const sinceTimestamp = remote.meta.options.sinceTimestampUtcMs;\n\n const emitBatches = (operations: OperationWithContext[]): void =>\n this.emitBatches(remote, operations, mode, chain);\n\n let page = await this.operationIndex.find(\n remote.meta.collectionId.key,\n startOrdinal,\n {\n excludeSourceRemote: remote.meta.name,\n throughOrdinal,\n },\n undefined,\n composedSignal,\n );\n\n let carry: OperationWithContext[] = [];\n\n let hasMore: boolean;\n do {\n if (composedSignal.aborted) {\n return;\n }\n for (const entry of page.results) {\n maxOrdinal = Math.max(maxOrdinal, entry.ordinal ?? 0);\n }\n\n let operations = page.results.map((entry) =>\n toOperationWithContext(entry),\n );\n\n if (carry.length > 0) {\n operations = [...carry, ...operations];\n carry = [];\n }\n\n // Before any filter judges it, so each judges the id as purged.\n for (const op of operations) {\n if (isPurgeMarker(op)) this.tombstone(op.context.documentId);\n }\n operations = operations.filter(\n (op) =>\n isPurgeMarker(op) ||\n !this.purgedDocumentIds.has(op.context.documentId),\n );\n\n if (sinceTimestamp && sinceTimestamp !== \"0\") {\n operations = operations.filter(\n (op) =>\n isPurgeMarker(op) || op.operation.timestampUtcMs >= sinceTimestamp,\n );\n }\n operations = filterForRemote(operations, remote.meta.filter);\n operations = operations.filter(\n (op) =>\n isPurgeMarker(op) ||\n !this.quarantinedDocumentIds.has(op.context.documentId),\n );\n if (this.gates(remote)) {\n operations = await this.gateOutbound(remote, operations);\n }\n\n hasMore = !!page.next;\n\n if (operations.length > 0) {\n // Which documents this page creates. A creation is always in the\n // `document` scope (document-action-handler.ts:346), and every other\n // scope's run for that document needs it to exist first.\n const created = new Set<string>();\n for (const entry of operations) {\n if (entry.operation.action.type === \"CREATE_DOCUMENT\") {\n created.add(entry.context.documentId);\n }\n }\n\n operations.sort((a, b) => {\n if (a.context.documentId !== b.context.documentId) {\n return a.context.documentId < b.context.documentId ? -1 : 1;\n }\n if (a.context.scope !== b.context.scope) {\n // Scope is otherwise compared as a raw string, so `auth` sorted\n // ahead of the `document` run that creates it -- and emitBatches\n // turns emission order into a dependency through lastJobByDoc, so\n // the creation was emitted depending on an operation for a\n // document that did not exist yet. The receiver defers that\n // operation until a CREATE_DOCUMENT for the id lands, which is the\n // job queued behind it, and neither side moves.\n //\n // Only the creating run is hoisted. Ordering the scopes against\n // each other generally is a separate question with its own\n // answers elsewhere (comparePositions gives `auth` a timestamp\n // tie), and grouping by scope at all is what keeps a page's\n // batches few -- serving a document's operations in index order\n // splits it into a batch per scope change.\n if (created.has(a.context.documentId)) {\n if (a.context.scope === \"document\") return -1;\n if (b.context.scope === \"document\") return 1;\n }\n return a.context.scope < b.context.scope ? -1 : 1;\n }\n return a.context.ordinal - b.context.ordinal;\n });\n\n if (hasMore) {\n const split = splitTrailingSameTimestampRun(operations);\n carry = split.carry;\n emitBatches(split.emit);\n } else {\n emitBatches(operations);\n }\n }\n\n if (hasMore) {\n page = await page.next!();\n }\n } while (hasMore);\n\n if (carry.length > 0) {\n emitBatches(carry);\n }\n\n remote.channel.outbox.advanceOrdinal(maxOrdinal);\n const name = remote.meta.name;\n if (this.evictedOutboxFloors.has(name)) {\n this.derivedThrough.delete(name);\n } else {\n this.derivedThrough.set(\n name,\n Math.max(this.derivedThrough.get(name) ?? -1, throughOrdinal),\n );\n }\n }\n}\n","import type { ISettledWatermark } from \"../catch-up/types.js\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IReactor, InProcessSyncModule } from \"../core/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type {\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncHoldStorage,\n ISyncPurgeRefusalStorage,\n ISyncReceivedMarkerStorage,\n ISyncRemoteStorage,\n} from \"../storage/interfaces.js\";\nimport { deliveryAt } from \"../storage/kysely/delivery-lookup.js\";\nimport { listPurged } from \"../storage/kysely/document-purges.js\";\nimport { KyselySyncCursorStorage } from \"../storage/kysely/sync-cursor-storage.js\";\nimport { KyselySyncDeadLetterStorage } from \"../storage/kysely/sync-dead-letter-storage.js\";\nimport { KyselySyncHoldStorage } from \"../storage/kysely/sync-hold-storage.js\";\nimport { KyselySyncPurgeRefusalStorage } from \"../storage/kysely/sync-purge-refusal-storage.js\";\nimport { KyselySyncReceivedMarkerStorage } from \"../storage/kysely/sync-received-marker-storage.js\";\nimport { KyselySyncRemoteStorage } from \"../storage/kysely/sync-remote-storage.js\";\nimport type { Database } from \"../storage/kysely/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"./interfaces.js\";\nimport type { LocalPeer } from \"./types.js\";\nimport { SyncManager, type SyncManagerConfig } from \"./sync-manager.js\";\n\nexport class SyncBuilder {\n private channelFactory?: IChannelFactory;\n private remoteStorage?: ISyncRemoteStorage;\n private cursorStorage?: ISyncCursorStorage;\n private deadLetterStorage?: ISyncDeadLetterStorage;\n private holdStorage?: ISyncHoldStorage;\n private receivedMarkerStorage?: ISyncReceivedMarkerStorage;\n private purgeRefusalStorage?: ISyncPurgeRefusalStorage;\n private config: Partial<SyncManagerConfig> = {};\n\n withChannelFactory(factory: IChannelFactory): this {\n this.channelFactory = factory;\n return this;\n }\n\n withRemoteStorage(storage: ISyncRemoteStorage): this {\n this.remoteStorage = storage;\n return this;\n }\n\n withCursorStorage(storage: ISyncCursorStorage): this {\n this.cursorStorage = storage;\n return this;\n }\n\n withDeadLetterStorage(storage: ISyncDeadLetterStorage): this {\n this.deadLetterStorage = storage;\n return this;\n }\n\n withHoldStorage(storage: ISyncHoldStorage): this {\n this.holdStorage = storage;\n return this;\n }\n\n withReceivedMarkerStorage(storage: ISyncReceivedMarkerStorage): this {\n this.receivedMarkerStorage = storage;\n return this;\n }\n\n withPurgeRefusalStorage(storage: ISyncPurgeRefusalStorage): this {\n this.purgeRefusalStorage = storage;\n return this;\n }\n\n withMaxDeadLettersPerRemote(limit: number): this {\n this.config.maxDeadLettersPerRemote = limit;\n return this;\n }\n\n withMaxInboxBatchSize(limit: number): this {\n this.config.maxInboxBatchSize = limit;\n return this;\n }\n\n withMaxHeldOperationsPerRemote(limit: number): this {\n this.config.maxHeldOperationsPerRemote = limit;\n return this;\n }\n\n withStaleRemotePollWindowMs(windowMs: number): this {\n this.config.staleRemotePollWindowMs = windowMs;\n return this;\n }\n\n build(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n driveContainerTypes: ReadonlySet<string>,\n watermark: ISettledWatermark,\n localPeer?: LocalPeer,\n ): ISyncManager {\n const module = this.buildModule(\n reactor,\n logger,\n operationIndex,\n eventBus,\n db,\n driveContainerTypes,\n watermark,\n localPeer,\n );\n return module.syncManager;\n }\n\n buildModule(\n reactor: IReactor,\n logger: ILogger,\n operationIndex: IOperationIndex,\n eventBus: IEventBus,\n db: Kysely<Database>,\n driveContainerTypes: ReadonlySet<string>,\n watermark: ISettledWatermark,\n localPeer?: LocalPeer,\n ): InProcessSyncModule {\n if (!this.channelFactory) {\n throw new Error(\"Channel factory is required\");\n }\n\n const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);\n const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);\n const deadLetterStorage =\n this.deadLetterStorage ?? new KyselySyncDeadLetterStorage(db);\n const holdStorage = this.holdStorage ?? new KyselySyncHoldStorage(db);\n const receivedMarkerStorage =\n this.receivedMarkerStorage ?? new KyselySyncReceivedMarkerStorage(db);\n const purgeRefusalStorage =\n this.purgeRefusalStorage ?? new KyselySyncPurgeRefusalStorage(db);\n\n const syncManager = new SyncManager(\n logger,\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n this.channelFactory,\n operationIndex,\n reactor,\n eventBus,\n driveContainerTypes,\n watermark,\n this.config,\n localPeer,\n holdStorage,\n { listPurged: () => listPurged(db) },\n receivedMarkerStorage,\n { at: (documentId, ordinal) => deliveryAt(db, documentId, ordinal) },\n purgeRefusalStorage,\n );\n\n return {\n remoteStorage,\n cursorStorage,\n deadLetterStorage,\n holdStorage,\n channelFactory: this.channelFactory,\n syncManager,\n };\n }\n}\n","import type { Kysely } from \"kysely\";\nimport type { Database } from \"./types.js\";\n\nexport async function createDefaultDatabase(): Promise<Kysely<Database>> {\n const { Kysely } = await import(\"kysely\");\n const { PGlite } = await import(\"@electric-sql/pglite\");\n const { PGliteDialect } = await import(\"kysely-pglite-dialect\");\n return new Kysely<Database>({\n dialect: new PGliteDialect(new PGlite()),\n });\n}\n","import type { ShutdownStatus } from \"./types.js\";\n\n/**\n * Factory method to create a ShutdownStatus object\n *\n * @param isShutdown - Initial shutdown state\n * @returns A ShutdownStatus object with a getter for the shutdown state\n */\nexport function createShutdownStatus(isShutdown: boolean): ShutdownStatus {\n const shutdownState = isShutdown;\n\n return {\n get isShutdown() {\n return shutdownState;\n },\n completed: Promise.resolve(),\n };\n}\n\n/**\n * Factory method to create a ShutdownStatus that can be updated\n *\n * @param initialState - Initial shutdown state (default: false)\n * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]\n */\nexport function createMutableShutdownStatus(\n initialState = false,\n): [\n ShutdownStatus,\n (value: boolean) => void,\n (completed: Promise<void>) => void,\n] {\n let shutdownState = initialState;\n let completedPromise: Promise<void> = Promise.resolve();\n\n const status: ShutdownStatus = {\n get isShutdown() {\n return shutdownState;\n },\n get completed() {\n return completedPromise;\n },\n };\n\n const setShutdown = (value: boolean) => {\n shutdownState = value;\n };\n\n const setCompleted = (promise: Promise<void>) => {\n completedPromise = promise;\n };\n\n return [status, setShutdown, setCompleted];\n}\n","import type {\n Action,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { Kysely } from \"kysely\";\n\nimport type { IProcessorManager } from \"@powerhousedao/shared/processors\";\nimport type { DocumentPurgeService } from \"../admin/document-purge-service.js\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport type { ICatchUp, ISettledWatermark } from \"../catch-up/types.js\";\nimport type { ReactorClient } from \"../client/reactor-client.js\";\nimport type { IReactorClient } from \"../client/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { ReactorFeatureFlags } from \"../executor/types.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { GroupReevaluationTrigger } from \"./group-reevaluation-trigger.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { DocumentViewDatabase } from \"../read-models/types.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport type { IJobAwaiter } from \"../shared/awaiter.js\";\nimport type { IConsistencyTracker } from \"../shared/consistency-tracker.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport type {\n DocumentRelationship,\n IDocumentIndexer,\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n ISyncCursorStorage,\n ISyncDeadLetterStorage,\n ISyncHoldStorage,\n ISyncRemoteStorage,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport type {\n DocumentIndexerDatabase,\n Database as StorageDatabase,\n} from \"../storage/kysely/types.js\";\nimport type { PoolInstrumentation } from \"../storage/pool-instrumentation.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { IChannelFactory, ISyncManager } from \"../sync/interfaces.js\";\n\nexport class AbortError extends Error {\n constructor(message?: string) {\n super(message || \"Aborted\");\n\n this.name = \"AbortError\";\n }\n}\n\nexport const isAbortError = (error: unknown): boolean => {\n return error instanceof AbortError;\n};\n\n/**\n * A single mutation job within a batch request.\n */\nexport type ExecutionJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n actions: Action[];\n dependsOn: string[];\n};\n\n/**\n * Request for batch mutation operation.\n */\nexport type BatchExecutionRequest = {\n jobs: ExecutionJobPlan[];\n};\n\n/**\n * Result from batch mutation operation.\n */\nexport type BatchExecutionResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * A single load job within a batch request.\n *\n * `dependsOn` lists intra-batch plan keys (resolved to UUIDs by `loadBatch`).\n * `externalDeps` lists pre-resolved job UUIDs from prior batches; these are\n * appended to the queue hint without going through plan-key resolution.\n */\nexport type LoadJobPlan = {\n key: string;\n documentId: string;\n scope: string;\n branch: string;\n operations: Operation[];\n dependsOn: string[];\n externalDeps: string[];\n};\n\n/**\n * Request for batch load operation.\n */\nexport type BatchLoadRequest = {\n jobs: LoadJobPlan[];\n};\n\n/**\n * Result from batch load operation.\n */\nexport type BatchLoadResult = {\n jobs: Record<string, JobInfo>;\n};\n\n/**\n * The main Reactor interface that serves as a facade for document operations.\n * This interface provides a unified API for document management, including\n * creation, retrieval, mutation, and deletion operations.\n */\nexport interface IReactor {\n /**\n * Signals that the reactor should shutdown.\n */\n kill(): ShutdownStatus;\n\n /**\n * Retrieves a list of document model modules.\n *\n * @param namespace - Optional namespace like \"powerhouse\" or \"sky\", defaults to \"\"\n * @param paging - Optional options for paging data in large queries.\n * @param signal - Optional abort signal to cancel the request\n * @returns List of document model modules\n */\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>>;\n\n /**\n * Retrieves a specific PHDocument by id\n *\n * @param id - Required, this is the document id\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument\n */\n get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by slug\n *\n * @param slug - Required, this is the document slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n */\n getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves a specific PHDocument by identifier (either id or slug).\n * Throws an error if the identifier matches both an id and a slug that refer to different documents.\n *\n * @param identifier - Required, this is the document id or slug\n * @param view - Optional filter containing branch and scopes information\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The up-to-date PHDocument with scopes and list of child document ids\n * @throws {Error} If identifier matches both an ID and slug referring to different documents\n */\n getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument>;\n\n /**\n * Retrieves outgoing relationships of a given type from a source document.\n *\n * @param sourceId - The source document id\n * @param relationshipType - The relationship type to filter by\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of target document ids\n */\n getOutgoingRelationships(\n sourceId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves incoming relationships of a given type to a target document.\n *\n * @param targetId - The target document id\n * @param relationshipType - The relationship type to filter by\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of source document ids\n */\n getIncomingRelationships(\n targetId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]>;\n\n /**\n * Retrieves outgoing relationship edges from a source document, carrying the\n * metadata and timestamps the document ids alone do not.\n *\n * @param sourceId - The source document id\n * @param relationshipType - Optional relationship type to filter by\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The matching relationship edges\n */\n getOutgoingRelationshipEdges(\n sourceId: string,\n relationshipType?: string,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Retrieves incoming relationship edges to a target document, carrying the\n * metadata and timestamps the document ids alone do not.\n *\n * @param targetId - The target document id\n * @param relationshipType - Optional relationship type to filter by\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The matching relationship edges\n */\n getIncomingRelationshipEdges(\n targetId: string,\n relationshipType?: string,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>>;\n\n /**\n * Retrieves the operations for a document\n *\n * @param documentId - The document id\n * @param view - Optional filter containing branch and scopes information\n * @param filter - Optional filter for actionTypes, timestamps, and revision\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns The list of operations\n */\n getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>>;\n\n /**\n * Filters documents by criteria and returns a list of them\n *\n * @param search - Search filter options (type, parentId, identifiers)\n * @param view - Optional filter containing branch and scopes information\n * @param paging - Optional pagination options\n * @param consistencyToken - Optional token for read-after-write consistency\n * @param signal - Optional abort signal to cancel the request\n * @returns List of documents matching criteria and pagination cursor\n */\n find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>>;\n\n /**\n * Creates a document\n *\n * @param document - Document with optional id, slug, parent, model type, and initial state\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job status\n */\n create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Deletes a document\n *\n * @param id - Document id\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies a list of actions to a document.\n *\n * @param docId - Document id\n * @param branch - Branch to apply actions to\n * @param actions - List of actions to apply\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Loads existing operations generated elsewhere into this reactor.\n *\n * @param docId - Document id\n * @param branch - Branch to load operations to\n * @param operations - List of operations to load\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns The job id and status\n */\n load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo>;\n\n /**\n * Applies multiple mutations across documents with dependency management.\n *\n * Ordering, and only ordering. A batch is neither atomic nor transactional:\n * it is submitted as one job per plan entry, each commits on its own, and\n * there is no batch-level rollback, status or consistency token.\n *\n * A dependency waits for the job before it to finish, not to succeed. A\n * failed job leaves the queue the same way a successful one does, so its\n * dependents still run, against whatever state the failure left behind.\n * Jobs that committed stay committed.\n *\n * Re-submitting after a partial failure re-applies the entries that already\n * succeeded, because a job plan carries no idempotency key. Compensation\n * belongs to the caller.\n *\n * @param request - Batch mutation request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @returns A receipt, not a result: every JobInfo is a PENDING snapshot\n * carrying a placeholder consistency token. Poll getJobStatus per job id, or\n * subscribe to JOB_FAILED and correlate on meta.batchId, to find out what\n * each one did.\n */\n executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult>;\n\n /**\n * Loads multiple batches of pre-existing operations across documents with dependency management.\n *\n * @param request - Batch load request containing jobs with dependencies\n * @param signal - Optional abort signal to cancel the request\n * @param meta - Optional metadata that flows through the job lifecycle\n * @returns Map of job keys to job information\n */\n loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult>;\n\n /**\n * Adds a relationship between two documents.\n *\n * Adding a relationship that already exists is a no-op, metadata included.\n * Use {@link IReactor.updateRelationship} to change an existing edge.\n *\n * @param sourceId - Source document id\n * @param targetId - Target document id\n * @param relationshipType - Relationship type identifier\n * @param metadata - Optional metadata to attach to the relationship\n * @param branch - Branch to add the relationship to, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n addRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Replaces the metadata of an existing relationship, preserving its createdAt.\n *\n * @param sourceId - Source document id\n * @param targetId - Target document id\n * @param relationshipType - Relationship type identifier\n * @param metadata - The metadata to store; null clears it\n * @param branch - Branch holding the relationship, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n updateRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Removes a relationship between two documents.\n *\n * @param sourceId - Source document id\n * @param targetId - Target document id\n * @param relationshipType - Relationship type identifier\n * @param branch - Branch to remove the relationship from, defaults to \"main\"\n * @param signer - Optional signer to sign the actions\n * @param signal - Optional abort signal to cancel the request\n * @returns The job id and status\n */\n removeRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch?: string,\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo>;\n\n /**\n * Retrieves the status of a job\n *\n * @param jobId - The job id\n * @returns The job status\n */\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;\n}\n\n/**\n * Feature flags for reactor configuration\n */\nexport type ReactorFeatures = { [key: string]: boolean };\n\n/**\n * Combined database type that includes all schemas\n */\nexport type Database = StorageDatabase &\n DocumentViewDatabase &\n DocumentIndexerDatabase;\n\n/**\n * Base sync-manager contract resolvable in every environment (in-process or\n * proxied over a worker boundary): just the sync manager.\n */\nexport interface SyncModule {\n syncManager: ISyncManager;\n}\n\n/**\n * Sync module composed in-process, with the full set of sync dependencies.\n */\nexport interface InProcessSyncModule extends SyncModule {\n remoteStorage: ISyncRemoteStorage;\n cursorStorage: ISyncCursorStorage;\n deadLetterStorage: ISyncDeadLetterStorage;\n holdStorage: ISyncHoldStorage;\n channelFactory: IChannelFactory;\n}\n\n/**\n * Base reactor module contract — the dependencies a client resolves regardless\n * of where the reactor graph lives. In-process hosts compose\n * {@link InProcessReactorModule}; a worker-backed tab resolves only this base\n * (the full graph lives in the worker).\n */\nexport interface ReactorModule {\n documentModelRegistry: IDocumentModelRegistry;\n syncModule: SyncModule | undefined;\n eventBus: IEventBus;\n}\n\n/**\n * Reactor module composed in-process: the full dependency graph. Provides\n * direct access to internal components for advanced use cases, testing, or\n * integration scenarios.\n */\n/**\n * A component that failed to build or catch up, and was started anyway.\n *\n * The reactor does not take itself down for one of these: a read model that\n * cannot reach its database should not stop the writes. But it does serve\n * reads that are silently incomplete, so the failure is recorded rather than\n * only logged -- a host can refuse readiness on a non-empty list instead of\n * reporting healthy while answering from an index that stopped at boot.\n */\nexport interface DegradedComponent {\n /** Names the component that failed, e.g. `read model 0 (attachments)`. */\n component: string;\n error: Error;\n}\n\nexport interface InProcessReactorModule extends ReactorModule {\n /**\n * The enforcement flags this reactor resolved, as plain booleans. Held on the\n * module because they select what a read enforces as well as what a write\n * does, and the read surface is composed outside the reactor.\n */\n featureFlags: ReactorFeatureFlags;\n queue: IQueue;\n jobTracker: IJobTracker;\n executorManager: IJobExecutorManager;\n database: Kysely<Database>;\n operationStore: IOperationStore;\n keyframeStore: IKeyframeStore;\n writeCache: IWriteCache;\n operationIndex: IOperationIndex;\n documentView: IDocumentView;\n documentViewConsistencyTracker: IConsistencyTracker;\n documentIndexer: IDocumentIndexer;\n documentIndexerConsistencyTracker: IConsistencyTracker;\n readModelCoordinator: IReadModelCoordinator;\n subscriptionManager: IReactorSubscriptionManager;\n processorManager: IProcessorManager;\n processorManagerConsistencyTracker: IConsistencyTracker;\n reactor: IReactor;\n syncModule: InProcessSyncModule | undefined;\n /**\n * Present when authGroups is on: enqueues re-evaluation jobs for the\n * documents a group membership change affects. Started by the builder;\n * hosts shut it down alongside the sync manager.\n */\n groupReevaluationTrigger: GroupReevaluationTrigger | undefined;\n /**\n * Instrumented pg.Pool handles registered with the builder, either by\n * createPostgresDatabase or by withInstrumentedPool. Empty when no pg\n * pool is in use (e.g. PGlite tests). Observers iterate the array to\n * record per-pool acquire-wait and stat metrics.\n */\n pools: PoolInstrumentation[];\n /**\n * Components that failed to initialize and were started degraded. Empty on\n * a clean boot. See {@link DegradedComponent}.\n */\n degradedComponents: DegradedComponent[];\n /** Read-side catch-up: status and an on-demand sweep. */\n catchUp: ICatchUp;\n /** The settled watermark this thread's catch-up sweeps up to. */\n settledWatermark: ISettledWatermark;\n /** Enqueues purge jobs; the erasure scheduler is its only caller. */\n documentPurgeService: DocumentPurgeService;\n}\n\n/**\n * Base reactor client contract — the client plus its reactor module. Satisfied\n * by every host; in-process hosts extend it with {@link InProcessReactorClientModule}.\n */\nexport interface ReactorClientModule {\n client: IReactorClient;\n reactorModule: ReactorModule | undefined;\n}\n\n/**\n * Reactor client composed in-process: the concrete client plus direct refs to\n * the in-process graph internals.\n */\nexport interface InProcessReactorClientModule extends ReactorClientModule {\n client: ReactorClient;\n reactor: IReactor;\n eventBus: IEventBus;\n documentIndexer: IDocumentIndexer;\n documentView: IDocumentView;\n signer: ISigner;\n subscriptionManager: IReactorSubscriptionManager;\n jobAwaiter: IJobAwaiter;\n reactorModule: InProcessReactorModule | undefined;\n}\n","import type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { normalizeDocumentModelVersion } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { CatchUpScheduler } from \"../catch-up/scheduler.js\";\nimport {\n addRelationshipAction,\n createDocumentAction,\n deleteDocumentAction,\n removeRelationshipAction,\n updateRelationshipAction,\n upgradeDocumentAction,\n} from \"../actions/index.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobFailedEvent,\n type JobPendingEvent,\n type Unsubscribe,\n} from \"../events/types.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport type { IJobTracker } from \"../job-tracker/interfaces.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport type { Job } from \"../queue/types.js\";\nimport type { IReadModelCoordinator } from \"../read-models/interfaces.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { createMutableShutdownStatus } from \"../shared/factories.js\";\nimport type {\n ConsistencyToken,\n JobInfo,\n JobMeta,\n PagedResults,\n PagingOptions,\n SearchFilter,\n ShutdownStatus,\n ViewFilter,\n} from \"../shared/types.js\";\nimport { JobStatus } from \"../shared/types.js\";\nimport { matchesScope, throwIfAborted } from \"../shared/utils.js\";\nimport type {\n DocumentRelationship,\n IDocumentIndexer,\n IDocumentView,\n IOperationStore,\n OperationFilter,\n} from \"../storage/interfaces.js\";\nimport {\n AbortError,\n type BatchExecutionRequest,\n type BatchExecutionResult,\n type BatchLoadRequest,\n type BatchLoadResult,\n type IReactor,\n type ReactorFeatures,\n} from \"./types.js\";\nimport {\n buildSingleJobMeta,\n filterByType,\n getSharedActionScope,\n getSharedOperationScope,\n signAction,\n signActions,\n toErrorInfo,\n topologicalSort,\n validateActionScopes,\n validateBatchLoadRequest,\n validateBatchRequest,\n validateOperationScopes,\n} from \"./utils.js\";\n\n/**\n * This class implements the IReactor interface and serves as the main entry point\n * for the new Reactor architecture.\n */\nexport class Reactor implements IReactor {\n private logger: ILogger;\n private documentModelRegistry: IDocumentModelRegistry;\n private shutdownStatus: ShutdownStatus;\n private setShutdown: (value: boolean) => void;\n private setCompleted: (completed: Promise<void>) => void;\n private queue: IQueue;\n private jobTracker: IJobTracker;\n private readModelCoordinator: IReadModelCoordinator;\n private features: ReactorFeatures;\n private documentView: IDocumentView;\n private documentIndexer: IDocumentIndexer;\n private operationStore: IOperationStore;\n private eventBus: IEventBus;\n private executorManager: IJobExecutorManager;\n private catchUp: CatchUpScheduler | undefined;\n\n constructor(\n logger: ILogger,\n documentModelRegistry: IDocumentModelRegistry,\n queue: IQueue,\n jobTracker: IJobTracker,\n readModelCoordinator: IReadModelCoordinator,\n features: ReactorFeatures,\n documentView: IDocumentView,\n documentIndexer: IDocumentIndexer,\n operationStore: IOperationStore,\n eventBus: IEventBus,\n executorManager: IJobExecutorManager,\n catchUp?: CatchUpScheduler,\n private readonly disposers: Unsubscribe[] = [],\n // Run last on kill(), once nothing routes to what they close.\n private readonly closers: Array<() => Promise<void>> = [],\n ) {\n this.catchUp = catchUp;\n this.logger = logger;\n this.documentModelRegistry = documentModelRegistry;\n this.queue = queue;\n this.jobTracker = jobTracker;\n this.readModelCoordinator = readModelCoordinator;\n this.features = features;\n this.documentView = documentView;\n this.documentIndexer = documentIndexer;\n this.operationStore = operationStore;\n this.eventBus = eventBus;\n this.executorManager = executorManager;\n\n const [status, setShutdown, setCompleted] =\n createMutableShutdownStatus(false);\n this.shutdownStatus = status;\n this.setShutdown = setShutdown;\n this.setCompleted = setCompleted;\n\n this.eventBus.subscribe(\n ReactorEventTypes.JOB_FAILED,\n (_type, event: JobFailedEvent) => {\n this.logger.error(\n \"Job @JobId failed with @Message: @Job\",\n event.jobId,\n event.error.message,\n event.job,\n );\n },\n );\n\n this.readModelCoordinator.start();\n }\n\n kill(): ShutdownStatus {\n this.logger.verbose(\"kill()\");\n\n if (this.shutdownStatus.isShutdown) {\n return this.shutdownStatus;\n }\n\n this.setShutdown(true);\n\n const shutdownAsync = async () => {\n await this.executorManager.stop(true);\n for (const dispose of this.disposers) dispose();\n\n await this.catchUp?.stop();\n this.readModelCoordinator.stop();\n this.jobTracker.shutdown();\n for (const close of this.closers) {\n try {\n await close();\n } catch (error) {\n this.logger.error(\"Shutdown step failed: @Error\", error);\n }\n }\n };\n\n this.setCompleted(shutdownAsync());\n\n return this.shutdownStatus;\n }\n\n getDocumentModels(\n namespace?: string,\n paging?: PagingOptions,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentModelModule>> {\n this.logger.verbose(\n \"getDocumentModels(@namespace, @paging)\",\n namespace,\n paging,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const modules = this.documentModelRegistry.getAllModules();\n const filteredModels = modules.filter(\n (module: DocumentModelModule) =>\n !namespace || module.documentModel.global.id.startsWith(namespace),\n );\n\n const startIndex = paging ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || filteredModels.length;\n const pagedModels = filteredModels.slice(startIndex, startIndex + limit);\n\n const hasMore = startIndex + limit < filteredModels.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return Promise.resolve({\n results: pagedModels,\n options: paging || { cursor: \"0\", limit: filteredModels.length },\n nextCursor,\n next: hasMore\n ? () =>\n this.getDocumentModels(\n namespace,\n { cursor: nextCursor!, limit },\n signal,\n )\n : undefined,\n });\n }\n\n async get<TDocument extends PHDocument>(\n id: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"get(@id, @view)\", id, view);\n\n return await this.documentView.get<TDocument>(\n id,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getBySlug<TDocument extends PHDocument>(\n slug: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getBySlug(@slug, @view)\", slug, view);\n\n const documentId = await this.documentView.resolveSlug(\n slug,\n view,\n consistencyToken,\n signal,\n );\n\n if (!documentId) {\n throw new Error(`Document not found with slug: ${slug}`);\n }\n\n return await this.get<TDocument>(\n documentId,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getByIdOrSlug<TDocument extends PHDocument>(\n identifier: string,\n view?: ViewFilter,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<TDocument> {\n this.logger.verbose(\"getByIdOrSlug(@identifier, @view)\", identifier, view);\n\n return await this.documentView.getByIdOrSlug<TDocument>(\n identifier,\n view,\n consistencyToken,\n signal,\n );\n }\n\n async getOutgoingRelationships(\n sourceId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getOutgoing(\n sourceId,\n [relationshipType],\n undefined,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships.results.map((rel) => rel.targetId);\n }\n\n async getIncomingRelationships(\n targetId: string,\n relationshipType: string,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const relationships = await this.documentIndexer.getIncoming(\n targetId,\n [relationshipType],\n undefined,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships.results.map((rel) => rel.sourceId);\n }\n\n async getOutgoingRelationshipEdges(\n sourceId: string,\n relationshipType?: string,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n const relationships = await this.documentIndexer.getOutgoing(\n sourceId,\n relationshipType ? [relationshipType] : undefined,\n paging,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships;\n }\n\n async getIncomingRelationshipEdges(\n targetId: string,\n relationshipType?: string,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<DocumentRelationship>> {\n const relationships = await this.documentIndexer.getIncoming(\n targetId,\n relationshipType ? [relationshipType] : undefined,\n paging,\n consistencyToken,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n return relationships;\n }\n\n async getOperations(\n documentId: string,\n view?: ViewFilter,\n filter?: OperationFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<Record<string, PagedResults<Operation>>> {\n this.logger.verbose(\n \"getOperations(@documentId, @view, @filter, @paging)\",\n documentId,\n view,\n filter,\n paging,\n );\n\n const branch = view?.branch || \"main\";\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const allScopes = Object.keys(revisions.revision);\n const result: Record<string, PagedResults<Operation>> = {};\n\n for (const scope of allScopes) {\n if (!matchesScope(view, scope)) {\n continue;\n }\n\n throwIfAborted(signal, () => new AbortError());\n\n const scopeResult = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n -1,\n filter,\n paging,\n signal,\n );\n\n result[scope] = {\n results: scopeResult.results,\n options: scopeResult.options,\n nextCursor: scopeResult.nextCursor,\n next: scopeResult.next\n ? async () => {\n const nextPage = await this.getOperations(\n documentId,\n view,\n filter,\n {\n cursor: scopeResult.nextCursor!,\n limit: scopeResult.options.limit,\n },\n consistencyToken,\n signal,\n );\n return nextPage[scope];\n }\n : undefined,\n };\n }\n\n return result;\n }\n\n async find(\n search: SearchFilter,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"find(@search, @view, @paging)\", search, view, paging);\n\n let results: PagedResults<PHDocument>;\n if (search.ids) {\n if (search.slugs && search.slugs.length > 0) {\n throw new Error(\"Cannot use both ids and slugs in the same search\");\n }\n\n results = await this.findByIds(\n search.ids,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.slugs) {\n results = await this.findBySlugs(\n search.slugs,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.parentId) {\n results = await this.findByParentId(\n search.parentId,\n view,\n paging,\n consistencyToken,\n signal,\n );\n\n if (search.type) {\n results = filterByType(results, search.type);\n }\n } else if (search.type) {\n results = await this.findByType(\n search.type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n } else {\n throw new Error(\"No search criteria provided\");\n }\n\n throwIfAborted(signal, () => new AbortError());\n\n return results;\n }\n\n async create(\n document: PHDocument,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"create(@id, @type, @slug)\",\n document.header.id,\n document.header.documentType,\n document.header.slug,\n );\n const createdAtUtcIso = new Date().toISOString();\n\n throwIfAborted(signal, () => new AbortError());\n\n const createInput: CreateDocumentActionInput = {\n model: document.header.documentType,\n version: 0,\n documentId: document.header.id,\n signing: {\n signature: document.header.id,\n publicKey: document.header.sig.publicKey,\n nonce: document.header.sig.nonce,\n createdAtUtcIso: document.header.createdAtUtcIso,\n documentType: document.header.documentType,\n },\n slug: document.header.slug,\n name: document.header.name,\n branch: document.header.branch,\n meta: document.header.meta,\n protocolVersions: document.header.protocolVersions ?? {\n \"base-reducer\": 2,\n },\n };\n\n const createAction = createDocumentAction(createInput);\n const upgradeAction = upgradeDocumentAction({\n documentId: document.header.id,\n model: document.header.documentType,\n fromVersion: 0,\n // imported documents may predate the document scope entirely\n toVersion: normalizeDocumentModelVersion(\n (document.state as Partial<typeof document.state>).document?.version,\n ),\n initialState: document.state,\n });\n\n const branch = document.header.branch || \"main\";\n let actions: Action[] = [createAction, upgradeAction];\n if (signer) {\n actions = await signActions(\n actions,\n signer,\n { documentId: document.header.id, branch },\n signal,\n );\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: document.header.id,\n scope: \"document\",\n branch,\n actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async deleteDocument(\n id: string,\n signer?: ISigner,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\"deleteDocument(@id)\", id);\n const createdAtUtcIso = new Date().toISOString();\n\n throwIfAborted(signal, () => new AbortError());\n\n let action = deleteDocumentAction(id);\n\n if (signer) {\n action = await signAction(\n action,\n signer,\n { documentId: id, branch: \"main\" },\n signal,\n );\n }\n\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: id,\n scope: \"document\",\n branch: \"main\",\n actions: [action],\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n return jobInfo;\n }\n\n async execute(\n docId: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"execute(@docId, @branch, @actions)\",\n docId,\n branch,\n actions,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n const createdAtUtcIso = new Date().toISOString();\n const scope = getSharedActionScope(actions);\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: docId,\n scope: scope,\n branch: branch,\n actions: actions,\n operations: [],\n createdAt: new Date().toISOString(),\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n throwIfAborted(signal, () => new AbortError());\n\n return jobInfo;\n }\n\n async load(\n docId: string,\n branch: string,\n operations: Operation[],\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"load(@docId, @branch, @count, @operations)\",\n docId,\n branch,\n operations.length,\n operations,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n if (operations.length === 0) {\n throw new Error(\"load requires at least one operation\");\n }\n\n const scope = getSharedOperationScope(operations);\n const createdAtUtcIso = new Date().toISOString();\n const jobId = uuidv4();\n const jobMeta = buildSingleJobMeta(jobId, meta);\n\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: docId,\n scope,\n branch,\n actions: [],\n operations,\n createdAt: createdAtUtcIso,\n queueHint: [],\n maxRetries: 3,\n errorHistory: [],\n meta: jobMeta,\n };\n\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: job.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: jobMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, jobMeta);\n\n await this.queue.enqueue(job);\n\n throwIfAborted(signal, () => new AbortError());\n\n return jobInfo;\n }\n\n async executeBatch(\n request: BatchExecutionRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchExecutionResult> {\n this.logger.verbose(\"executeBatch(@count jobs)\", request.jobs.length);\n\n throwIfAborted(signal, () => new AbortError());\n validateBatchRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateActionScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: jobPlan.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n throwIfAborted(signal, () => new AbortError());\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = jobPlan.dependsOn.map((depKey) =>\n planKeyToJobId.get(depKey)!,\n );\n const job: Job = {\n id: jobId,\n kind: \"mutation\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: jobPlan.actions,\n operations: [],\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchExecutionResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async loadBatch(\n request: BatchLoadRequest,\n signal?: AbortSignal,\n meta?: Record<string, unknown>,\n ): Promise<BatchLoadResult> {\n this.logger.verbose(\"loadBatch(@count jobs)\", request.jobs.length);\n\n throwIfAborted(signal, () => new AbortError());\n validateBatchLoadRequest(request.jobs);\n for (const jobPlan of request.jobs) {\n validateOperationScopes(jobPlan);\n }\n const createdAtUtcIso = new Date().toISOString();\n const planKeyToJobId = new Map<string, string>();\n for (const jobPlan of request.jobs) {\n planKeyToJobId.set(jobPlan.key, uuidv4());\n }\n const batchId = uuidv4();\n const batchJobIds = [...planKeyToJobId.values()];\n const batchMeta: JobMeta = {\n ...meta,\n batchId,\n batchJobIds,\n };\n const jobInfos = new Map<string, JobInfo>();\n for (const jobPlan of request.jobs) {\n const jobId = planKeyToJobId.get(jobPlan.key)!;\n const jobInfo: JobInfo = {\n id: jobId,\n documentId: jobPlan.documentId,\n status: JobStatus.PENDING,\n createdAtUtcIso,\n consistencyToken: {\n version: 1,\n createdAtUtcIso,\n coordinates: [],\n },\n meta: batchMeta,\n };\n this.jobTracker.registerJob(jobInfo);\n this.emitJobPending(jobInfo.id, batchMeta);\n jobInfos.set(jobPlan.key, jobInfo);\n }\n const sortedKeys = topologicalSort(request.jobs);\n const enqueuedKeys: string[] = [];\n try {\n for (const key of sortedKeys) {\n throwIfAborted(signal, () => new AbortError());\n const jobPlan = request.jobs.find((j) => j.key === key)!;\n const jobId = planKeyToJobId.get(key)!;\n const queueHint = [\n ...jobPlan.dependsOn.map((depKey) => planKeyToJobId.get(depKey)!),\n ...jobPlan.externalDeps,\n ];\n const job: Job = {\n id: jobId,\n kind: \"load\",\n documentId: jobPlan.documentId,\n scope: jobPlan.scope,\n branch: jobPlan.branch,\n actions: [],\n operations: jobPlan.operations,\n createdAt: createdAtUtcIso,\n queueHint,\n maxRetries: 3,\n errorHistory: [],\n meta: batchMeta,\n };\n await this.queue.enqueue(job);\n enqueuedKeys.push(key);\n }\n } catch (error) {\n for (const key of enqueuedKeys) {\n const jobId = planKeyToJobId.get(key)!;\n try {\n await this.queue.remove(jobId);\n } catch {\n // Ignore removal errors during cleanup\n }\n }\n for (const jobInfo of jobInfos.values()) {\n this.jobTracker.markFailed(\n jobInfo.id,\n toErrorInfo(\"Batch enqueue failed\"),\n );\n }\n throw error;\n }\n const result: BatchLoadResult = {\n jobs: Object.fromEntries(jobInfos),\n };\n return result;\n }\n\n async addRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata?: Record<string, unknown>,\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"addRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)\",\n sourceId,\n targetId,\n relationshipType,\n metadata,\n branch,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n let actions: Action[] = [\n addRelationshipAction(sourceId, targetId, relationshipType, metadata),\n ];\n\n if (signer) {\n actions = await signActions(\n actions,\n signer,\n { documentId: sourceId, branch },\n signal,\n );\n }\n\n return await this.execute(sourceId, branch, actions, signal);\n }\n\n async updateRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n metadata: Record<string, unknown> | null,\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"updateRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)\",\n sourceId,\n targetId,\n relationshipType,\n metadata,\n branch,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n let actions: Action[] = [\n updateRelationshipAction(sourceId, targetId, relationshipType, metadata),\n ];\n\n if (signer) {\n actions = await signActions(\n actions,\n signer,\n { documentId: sourceId, branch },\n signal,\n );\n }\n\n return await this.execute(sourceId, branch, actions, signal);\n }\n\n async removeRelationship(\n sourceId: string,\n targetId: string,\n relationshipType: string,\n branch: string = \"main\",\n signer?: ISigner,\n signal?: AbortSignal,\n ): Promise<JobInfo> {\n this.logger.verbose(\n \"removeRelationship(@sourceId, @targetId, @relationshipType, @branch)\",\n sourceId,\n targetId,\n relationshipType,\n branch,\n );\n\n throwIfAborted(signal, () => new AbortError());\n\n let actions: Action[] = [\n removeRelationshipAction(sourceId, targetId, relationshipType),\n ];\n\n if (signer) {\n actions = await signActions(\n actions,\n signer,\n { documentId: sourceId, branch },\n signal,\n );\n }\n\n return await this.execute(sourceId, branch, actions, signal);\n }\n\n getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo> {\n this.logger.verbose(\"getJobStatus(@jobId)\", jobId);\n\n throwIfAborted(signal, () => new AbortError());\n\n const jobInfo = this.jobTracker.getJobStatus(jobId);\n\n if (!jobInfo) {\n const now = new Date().toISOString();\n return Promise.resolve({\n id: jobId,\n documentId: \"\",\n status: JobStatus.FAILED,\n createdAtUtcIso: now,\n completedAtUtcIso: now,\n error: toErrorInfo(\"Job not found\"),\n consistencyToken: {\n version: 1,\n createdAtUtcIso: now,\n coordinates: [],\n },\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n }\n\n return Promise.resolve(jobInfo);\n }\n\n private async findByIds(\n ids: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByIds(@count ids)\", ids.length);\n\n const startIndex = paging?.cursor ? parseInt(paging.cursor) || 0 : 0;\n const limit = paging?.limit || ids.length;\n const pagedIds = ids.slice(startIndex, startIndex + limit);\n\n const results = await this.documentView.getMany<PHDocument>(\n pagedIds,\n view,\n consistencyToken,\n signal,\n );\n\n const hasMore = startIndex + limit < ids.length;\n const nextCursor = hasMore ? String(startIndex + limit) : undefined;\n\n return {\n results,\n options: paging || { cursor: \"0\", limit: ids.length },\n nextCursor,\n };\n }\n\n private async findBySlugs(\n slugs: string[],\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findBySlugs(@count slugs)\", slugs.length);\n\n const ids = await this.documentView.resolveSlugs(\n slugs,\n view,\n consistencyToken,\n signal,\n );\n\n return await this.findByIds(ids, view, paging, consistencyToken, signal);\n }\n\n private async findByParentId(\n parentId: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByParentId(@parentId)\", parentId);\n\n const relationships = await this.documentIndexer.getOutgoing(\n parentId,\n [\"child\"],\n paging,\n consistencyToken,\n signal,\n );\n\n const ids = relationships.results.map((rel) => rel.targetId);\n return await this.findByIds(ids, view, paging, undefined, signal);\n }\n\n private async findByType(\n type: string,\n view?: ViewFilter,\n paging?: PagingOptions,\n consistencyToken?: ConsistencyToken,\n signal?: AbortSignal,\n ): Promise<PagedResults<PHDocument>> {\n this.logger.verbose(\"findByType(@type)\", type);\n\n return await this.documentView.findByType(\n type,\n view,\n paging,\n consistencyToken,\n signal,\n );\n }\n\n private emitJobPending(jobId: string, meta: JobMeta): void {\n const event: JobPendingEvent = {\n jobId,\n jobMeta: meta,\n };\n this.eventBus.emit(ReactorEventTypes.JOB_PENDING, event).catch(() => {\n // Ignore event emission errors\n });\n }\n}\n","import type {\n ISigner,\n PeerCapability,\n UpgradeManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport {\n isPurgeMarker,\n localSupports,\n mergePeerCapabilities,\n PEER_CAPABILITIES,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type {\n DbConfig,\n FactorySpec,\n ModelManifestEntry,\n WorkerPoolConfig,\n} from \"../executor/worker/protocol.js\";\nimport { WorkerPoolJobExecutorManager } from \"../executor/worker-pool-job-executor-manager.js\";\nimport { DocumentPurgeService } from \"../admin/document-purge-service.js\";\nimport type { WorkerFactory } from \"../executor/worker-pool-job-executor-manager.js\";\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 { CatchUpScheduler, isCatchUpConsumer } from \"../catch-up/scheduler.js\";\nimport {\n createKyselyWatermarkProbe,\n describeWaitingSessions,\n SettledWatermark,\n} from \"../catch-up/settled-watermark.js\";\nimport {\n defaultCatchUpConfig,\n type CatchUpConfig,\n type CatchUpStatus,\n type ICatchUpConsumer,\n} from \"../catch-up/types.js\";\nimport type { IOperationIndex } from \"../cache/operation-index-types.js\";\nimport type { WriteCacheConfig } from \"../cache/write-cache-types.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { EventBus } from \"../events/event-bus.js\";\nimport { resolveFeatureFlags } from \"./feature-flags.js\";\nimport {\n checkStoredProtocols,\n type UnsupportedStoredDocuments,\n} from \"./stored-protocol-check.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport { ReactorEventTypes, type JobWriteReadyEvent } from \"../events/types.js\";\nimport {\n KyselyExecutionScope,\n type IExecutionScope,\n} from \"../executor/execution-scope.js\";\nimport type { IJobExecutorManager } from \"../executor/interfaces.js\";\nimport { SimpleJobExecutorManager } from \"../executor/simple-job-executor-manager.js\";\nimport { SimpleJobExecutor } from \"../executor/simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../executor/types.js\";\nimport type { SignatureTrustPolicy } from \"../signer/types.js\";\nimport { InMemoryJobTracker } from \"../job-tracker/in-memory-job-tracker.js\";\nimport { ProcessorManager } from \"../processors/processor-manager.js\";\nimport type { IQueue } from \"../queue/interfaces.js\";\nimport { InMemoryQueue } from \"../queue/queue.js\";\nimport type {\n BuiltInReadModelKind,\n ProjectionWorkerFactory,\n} from \"../projection/index.js\";\n// Imported from the leaf module, not the projection barrel: the barrel pulls\n// in worker_threads transports that this file otherwise only reaches through\n// a dynamic import.\nimport { BUILT_IN_READ_MODEL_KINDS } from \"../projection/read-model-kinds.js\";\nimport type {\n ProjectionShardHooks,\n ProjectionShardManager,\n} from \"../projection/projection-shard-manager.js\";\nimport {\n DEFAULT_COMMIT_CHUNK_SIZE,\n DEFAULT_READ_MODEL_YIELD_DEADLINE_MS,\n type ReadModelIndexingConfig,\n} from \"../read-models/base-read-model.js\";\nimport { ReadModelCoordinator } from \"../read-models/coordinator.js\";\nimport type { DocumentViewDatabase } from \"../read-models/types.js\";\nimport {\n DeletedDocumentRead,\n KyselyDocumentView,\n} from \"../read-models/document-view.js\";\nimport type {\n IReadModel,\n IReadModelCoordinator,\n} from \"../read-models/interfaces.js\";\nimport {\n DocumentModelResolver,\n NullDocumentModelResolver,\n} from \"../registry/document-model-resolver.js\";\nimport { DocumentModelRegistry } from \"../registry/implementation.js\";\nimport type {\n IDocumentModelLoader,\n IDocumentModelRegistry,\n} from \"../registry/interfaces.js\";\nimport {\n ConsistencyTracker,\n type IConsistencyTracker,\n} from \"../shared/consistency-tracker.js\";\nimport {\n KyselyDocumentIndexer,\n type IndexerDatabase,\n} from \"../storage/kysely/document-indexer.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 {\n createForwardingPoolInstrumentation,\n instrumentPgPool,\n type ForwardingPoolInstrumentation,\n type PoolInstrumentation,\n} from \"../storage/pool-instrumentation.js\";\nimport {\n REACTOR_SCHEMA,\n runMigrations,\n} from \"../storage/migrations/migrator.js\";\nimport type { MigrationStrategy } from \"../storage/migrations/types.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport { SubscriptionNotificationReadModel } from \"../subs/subscription-notification-read-model.js\";\nimport { GroupReevaluationTrigger } from \"./group-reevaluation-trigger.js\";\nimport { GqlRequestChannelFactory } from \"../sync/channels/gql-request-channel-factory.js\";\nimport { GqlResponseChannelFactory } from \"../sync/channels/gql-response-channel-factory.js\";\nimport { SyncBuilder } from \"../sync/sync-builder.js\";\nimport type { JwtHandler, LocalPeer } from \"../sync/types.js\";\nimport { ChannelScheme } from \"../sync/types.js\";\nimport { createDefaultDatabase } from \"./create-default-database.js\";\nimport { DEFAULT_DRIVE_CONTAINER_TYPES } from \"./drive-container-types.js\";\nimport { resolveModelSources } from \"./model-sources.js\";\nimport type { DocumentModelSource } from \"./model-sources.js\";\nimport { Reactor } from \"./reactor.js\";\nimport type {\n Database,\n DegradedComponent,\n InProcessReactorModule,\n InProcessSyncModule,\n IReactor,\n ReactorFeatures,\n} from \"./types.js\";\n\n/**\n * Dependencies provided to read-model factories registered via\n * `withReadModelFactory`. These are constructed inside `buildModule()`, which\n * is why factory-based registration is needed for read models that depend on\n * them (`BaseReadModel` subclasses, in particular).\n */\nexport interface ReadModelFactoryDeps {\n documentModelRegistry: IDocumentModelRegistry;\n operationIndex: IOperationIndex;\n writeCache: IWriteCache;\n processorManagerConsistencyTracker: IConsistencyTracker;\n}\n\n/**\n * Factory that builds a pre-ready read model from internal reactor\n * dependencies once they are available. Awaited during `buildModule()`.\n */\nexport type ReadModelFactory = (\n deps: ReadModelFactoryDeps,\n) => IReadModel | Promise<IReadModel>;\n\n/**\n * Dependencies handed to a coordinator factory registered via\n * `withReadModelCoordinatorFactory`. All constructed inside `buildModule()`.\n */\nexport interface ReadModelCoordinatorFactoryDeps {\n /** Host bus: subscribe to JOB_WRITE_READY, emit JOB_READ_READY. */\n eventBus: IEventBus;\n logger: ILogger;\n /**\n * `withReadModel` + `withReadModelFactory` models, in registration order.\n * Excludes documentView/documentIndexer so a worker owner cannot\n * double-index them.\n */\n readModels: IReadModel[];\n /** Post-ready: runs after JOB_READ_READY so callbacks see fresh reads. */\n subscriptionNotificationReadModel: IReadModel;\n /** Post-ready: every package-installed processor. */\n processorManager: IReadModel;\n /** Host document view; lookup surface only, never indexed by the factory. */\n documentView: IReadModel;\n /** Host document indexer; lookup surface only, never indexed by the factory. */\n documentIndexer: IReadModel;\n /**\n * Builds and starts a projection shard manager on this event bus with the\n * host consistency trackers wired. Registers no shutdown hook: the factory\n * owns the manager's lifecycle through `registerShutdownHook`.\n */\n createProjectionShardManager: (\n config: ProjectionShardBuilderConfig & ProjectionShardHooks,\n ) => Promise<ProjectionShardManager>;\n /** Appends to the list `withShutdownHook` uses; hooks run in order. */\n registerShutdownHook: (hook: () => Promise<void>) => void;\n}\n\n/**\n * Factory that builds the read-model coordinator from reactor internals.\n * Awaited during `buildModule()`.\n */\nexport type ReadModelCoordinatorFactory = (\n deps: ReadModelCoordinatorFactoryDeps,\n) => IReadModelCoordinator | Promise<IReadModelCoordinator>;\n\nexport type {\n DocumentModelSource,\n FileModelSource,\n PackageModelSource,\n ResolvedModelSources,\n} from \"./model-sources.js\";\n\ntype WorkerPoolBase = {\n /** Number of worker threads to spawn; also the sticky-routing modulus. */\n numWorkers: number;\n};\n\n/**\n * Executor worker-pool configuration. Either `db` (default thread\n * transport; each worker opens its own Postgres pool) or a custom\n * `factory` transport is required by construction — an enabled pool\n * without connection info is unrepresentable.\n */\nexport type WorkerPoolOptions =\n | (WorkerPoolBase & { db: DbConfig; factory?: WorkerFactory })\n | (WorkerPoolBase & { db?: DbConfig; factory: WorkerFactory });\n\n/**\n * Caller-facing config for {@link ReactorBuilder.withProjectionShards}.\n * When set, the builder replaces the in-process\n * {@link ReadModelCoordinator} with a {@link ProjectionShardManager} that\n * fans JOB_WRITE_READY events to N projection workers sharded by\n * documentId.\n *\n * @see Sharded projection workers sub-feature brief\n * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)\n */\nexport type ProjectionShardBuilderConfig = {\n shardCount: number;\n /**\n * Kinds each shard indexes before its job reaches READ_READY.\n *\n * Together with {@link postReadyKinds} these must name every\n * {@link BuiltInReadModelKind} exactly once — the host's own copies of\n * those models never index under sharding, so a kind named in neither\n * list is indexed by nobody, and one named in both is indexed twice.\n * `buildModule` rejects any other combination.\n */\n preReadyKinds: BuiltInReadModelKind[];\n /** Kinds each shard indexes after READ_READY. See {@link preReadyKinds}. */\n postReadyKinds: BuiltInReadModelKind[];\n /**\n * Connection info for the projection workers' own pools. Falls back to\n * the executor worker pool's `db` when {@link ReactorBuilder.withWorkerPool}\n * is configured with one.\n */\n db?: DbConfig;\n poolSize?: number;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\n/** One contiguous cursor per read model cannot serve shards that each see part of the stream. */\nfunction validateShardCount(shardCount: number): void {\n if (shardCount !== 1) {\n throw new Error(\n `shardCount ${shardCount} is not supported: read-side catch-up keeps one cursor per read model, so projection runs in exactly one worker (shardCount: 1)`,\n );\n }\n}\n\n/** A coordinator whose models sweep in a worker reports that status. */\nfunction hasCatchUpStatuses(\n coordinator: IReadModelCoordinator,\n): coordinator is IReadModelCoordinator & {\n catchUpStatuses(): CatchUpStatus[];\n} {\n return (\n \"catchUpStatuses\" in coordinator &&\n typeof coordinator.catchUpStatuses === \"function\"\n );\n}\n\nfunction sameDatabaseTarget(a: DbConfig, b: DbConfig): boolean {\n return a.host === b.host && a.port === b.port && a.database === b.database;\n}\n\n/**\n * Requires each built-in kind exactly once across both lists. A kind in\n * neither list is indexed by nobody (host copies never index under a\n * worker), so its reads go stale and its consistency-token waits never\n * resolve; a kind in both is indexed twice per operation.\n */\nfunction validateBuiltInKindCoverage(\n preReadyKinds: BuiltInReadModelKind[],\n postReadyKinds: BuiltInReadModelKind[],\n): void {\n const named = [...preReadyKinds, ...postReadyKinds];\n const missing = BUILT_IN_READ_MODEL_KINDS.filter(\n (kind) => !named.includes(kind),\n );\n const duplicated = BUILT_IN_READ_MODEL_KINDS.filter(\n (kind) => named.filter((entry) => entry === kind).length > 1,\n );\n if (missing.length === 0 && duplicated.length === 0) {\n return;\n }\n const problems = [\n missing.length > 0\n ? `never named: ${missing.join(\", \")} (would be indexed by no shard, leaving those reads permanently stale and their consistency-token waits unresolvable)`\n : undefined,\n duplicated.length > 0\n ? `named more than once: ${duplicated.join(\", \")} (would be indexed twice per operation)`\n : undefined,\n ].filter((problem) => problem !== undefined);\n throw new Error(\n `withProjectionShards requires preReadyKinds and postReadyKinds to name each built-in read model (${BUILT_IN_READ_MODEL_KINDS.join(\", \")}) exactly once between them; ${problems.join(\"; \")}`,\n );\n}\n\nexport class ReactorBuilder {\n private logger?: ILogger;\n private documentModelSources: DocumentModelSource[] = [];\n private upgradeManifests: UpgradeManifest<readonly number[]>[] = [];\n private features: ReactorFeatures = {\n legacyStorageEnabled: false,\n legacyProcessorIds: true,\n };\n private readModels: IReadModel[] = [];\n private readModelFactories: ReadModelFactory[] = [];\n private executorManager: IJobExecutorManager | undefined;\n private executorConfig: JobExecutorConfig = {};\n private writeCacheConfig?: Partial<WriteCacheConfig>;\n private migrationStrategy: MigrationStrategy = \"auto\";\n private unsupportedStoredDocuments: UnsupportedStoredDocuments = \"refuse\";\n private syncBuilder?: SyncBuilder;\n private eventBus?: IEventBus;\n private readModelCoordinator?: IReadModelCoordinator;\n private readModelCoordinatorFactory?: ReadModelCoordinatorFactory;\n private kyselyInstance?: Kysely<Database>;\n private signer?: ISigner;\n private workerSigner?: FactorySpec;\n private trustPolicy?: SignatureTrustPolicy;\n private workerTrustPolicy?: FactorySpec;\n private signalHandlersEnabled = false;\n private queueInstance?: IQueue;\n private channelScheme?: ChannelScheme;\n private jwtHandler?: JwtHandler;\n private documentModelLoader?: IDocumentModelLoader;\n private shutdownHooks: Array<() => Promise<void>> = [];\n private driveContainerTypes: ReadonlySet<string> =\n DEFAULT_DRIVE_CONTAINER_TYPES;\n private workerPool?: WorkerPoolOptions;\n private resolvedModelManifest?: ModelManifestEntry[];\n private moduleOnlyModelKeys: string[] = [];\n private projectionShardConfig?: ProjectionShardBuilderConfig;\n private projectionWorkerFactory?: ProjectionWorkerFactory;\n private instrumentedPools: PoolInstrumentation[] = [];\n private catchUpConfig: CatchUpConfig = defaultCatchUpConfig;\n private extraPeerCapabilities: PeerCapability[] = [];\n\n withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n /**\n * Register document-model sources: live modules, importable files, or\n * importable packages. Appends across calls. At `buildModule()` every\n * source is resolved host-side and registered on the registry; file and\n * package sources additionally form the worker manifest when the worker\n * pool is enabled (live modules cannot cross a thread boundary).\n */\n withDocumentModelSources(sources: DocumentModelSource[]): this {\n this.documentModelSources.push(...sources);\n return this;\n }\n\n /** Capabilities beyond the registry, for tests and hosts that ship their own. */\n withPeerCapabilities(extra: readonly PeerCapability[]): this {\n this.extraPeerCapabilities = mergePeerCapabilities(\n this.extraPeerCapabilities,\n extra,\n );\n return this;\n }\n\n /** The registry plus any capabilities added with withPeerCapabilities. */\n getPeerCapabilities(): readonly PeerCapability[] {\n return mergePeerCapabilities(PEER_CAPABILITIES, this.extraPeerCapabilities);\n }\n\n withUpgradeManifests(manifests: UpgradeManifest<readonly number[]>[]): this {\n this.upgradeManifests = manifests;\n return this;\n }\n\n withFeatures(features: ReactorFeatures): this {\n this.features = { ...this.features, ...features };\n return this;\n }\n\n withReadModel(readModel: IReadModel): this {\n this.readModels.push(readModel);\n return this;\n }\n\n /**\n * Register a factory that builds a pre-ready read model after the reactor's\n * internal `operationIndex`, `writeCache`, and processor-manager consistency\n * tracker are constructed. Use this for read models (e.g. `BaseReadModel`\n * subclasses) that need those dependencies and therefore cannot be built\n * before calling `buildModule()`.\n */\n withReadModelFactory(factory: ReadModelFactory): this {\n this.readModelFactories.push(factory);\n return this;\n }\n\n withReadModelCoordinator(readModelCoordinator: IReadModelCoordinator): this {\n this.readModelCoordinator = readModelCoordinator;\n return this;\n }\n\n /**\n * Register a factory that builds the coordinator once the subscription\n * read model, processor manager and host read models exist. Use this, not\n * `withReadModelCoordinator`, for coordinators that compose those internals.\n */\n withReadModelCoordinatorFactory(factory: ReadModelCoordinatorFactory): this {\n this.readModelCoordinatorFactory = factory;\n return this;\n }\n\n withExecutor(executor: IJobExecutorManager): this {\n this.executorManager = executor;\n return this;\n }\n\n withExecutorConfig(config: Partial<JobExecutorConfig>): this {\n this.executorConfig = { ...this.executorConfig, ...config };\n return this;\n }\n\n withWriteCacheConfig(config: Partial<WriteCacheConfig>): this {\n this.writeCacheConfig = config;\n return this;\n }\n\n withDriveContainerTypes(types: string[]): this {\n this.driveContainerTypes = new Set(types);\n return this;\n }\n\n withMigrationStrategy(strategy: MigrationStrategy): this {\n this.migrationStrategy = strategy;\n return this;\n }\n\n /**\n * Stored documents at protocol versions this build does not run: \"refuse\"\n * (the default) fails buildModule; \"read-only\" starts with a warning, and\n * every job and received row into them is refused.\n */\n withUnsupportedStoredDocuments(mode: UnsupportedStoredDocuments): this {\n this.unsupportedStoredDocuments = mode;\n return this;\n }\n\n withSync(syncBuilder: SyncBuilder): this {\n this.syncBuilder = syncBuilder;\n return this;\n }\n\n /** Tunes the read-side catch-up sweep. */\n withCatchUp(config: Partial<CatchUpConfig>): this {\n this.catchUpConfig = { ...this.catchUpConfig, ...config };\n return this;\n }\n\n withEventBus(eventBus: IEventBus): this {\n this.eventBus = eventBus;\n return this;\n }\n\n /**\n * Signs the operations the executor synthesizes: the NOOP an UNDO becomes and\n * the action a REDO rebuilds. Without one they are stored unsigned. Pooled\n * workers import `workerSigner` to build the same signer.\n */\n withSigner(signer: ISigner, workerSigner?: FactorySpec): this {\n this.signer = signer;\n this.workerSigner = workerSigner;\n return this;\n }\n\n /** Whether {@link withSigner} was called. */\n hasSigner(): boolean {\n return this.signer !== undefined;\n }\n\n /**\n * Decides at admission whether a key may sign as the user it claims; see\n * {@link SignatureTrustPolicy} for the contract and the default. Pooled\n * workers import `workerTrustPolicy` to build the same policy.\n */\n withTrustPolicy(\n trustPolicy: SignatureTrustPolicy,\n workerTrustPolicy?: FactorySpec,\n ): this {\n this.trustPolicy = trustPolicy;\n this.workerTrustPolicy = workerTrustPolicy;\n return this;\n }\n\n /** Whether {@link withTrustPolicy} was called. */\n hasTrustPolicy(): boolean {\n return this.trustPolicy !== undefined;\n }\n\n withKysely(kysely: Kysely<Database>): this {\n this.kyselyInstance = kysely;\n return this;\n }\n\n /**\n * Register an externally-constructed pg.Pool's {@link PoolInstrumentation}\n * so it surfaces through {@link ReactorModule.pools}. Use this when the\n * caller built the pool itself (e.g. the in-process bench host wiring) so\n * pool acquire-wait and pool-stat metrics still emit. The builder also\n * registers any pool it constructs internally via {@link createPostgresDatabase}.\n */\n withInstrumentedPool(instrumentation: PoolInstrumentation): this {\n this.instrumentedPools.push(instrumentation);\n return this;\n }\n\n withQueue(queue: IQueue): this {\n this.queueInstance = queue;\n return this;\n }\n\n withChannelScheme(scheme: ChannelScheme): this {\n this.channelScheme = scheme;\n return this;\n }\n\n withJwtHandler(handler: JwtHandler): this {\n this.jwtHandler = handler;\n return this;\n }\n\n withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n withSignalHandlers(): this {\n this.signalHandlersEnabled = true;\n return this;\n }\n\n /**\n * Register an async cleanup hook to run during graceful shutdown. Hooks fire\n * after `reactor.kill()` resolves and before `database.destroy()`, so callers\n * that depend on the reactor (e.g. an HTTP API layered on top) can drain\n * cleanly before the underlying kysely instance is torn down. Hook errors are\n * logged and otherwise ignored — one bad hook cannot strand the rest of the\n * shutdown chain.\n */\n withShutdownHook(hook: () => Promise<void>): this {\n this.shutdownHooks.push(hook);\n return this;\n }\n\n /**\n * Enable the executor worker pool: N `node:worker_threads` workers with\n * sticky per-document routing, replacing the in-process executor. Calling\n * this enables the pool — there is no `enabled` flag. Provide `db`\n * (each worker opens its own Postgres pool; the parent database is built\n * from it too unless {@link withKysely} is set) or a custom `factory`\n * transport.\n */\n withWorkerPool(options: WorkerPoolOptions): this {\n this.workerPool = options;\n return this;\n }\n\n /**\n * Configure N sharded projection workers. When set, the builder replaces\n * the default in-process {@link ReadModelCoordinator} with a\n * {@link ProjectionShardManager}.\n *\n * Projection workers open their own Postgres pools from `config.db`,\n * falling back to the executor worker pool's `db` when\n * {@link withWorkerPool} is configured with one; only the `poolSize` is\n * overridden by {@link ProjectionShardBuilderConfig.poolSize}. The same\n * model manifest resolved from {@link withDocumentModelSources} is\n * forwarded.\n */\n withProjectionShards(config: ProjectionShardBuilderConfig): this {\n this.projectionShardConfig = config;\n return this;\n }\n\n /**\n * Inject a custom {@link ProjectionWorkerFactory}. When set, the builder\n * skips default thread-transport wiring for the projection shards and\n * hands the factory directly to {@link ProjectionShardManager}.\n */\n withProjectionWorkerFactory(factory: ProjectionWorkerFactory): this {\n this.projectionWorkerFactory = factory;\n return this;\n }\n\n getResolvedModelManifest(): ModelManifestEntry[] | undefined {\n return this.resolvedModelManifest;\n }\n\n async build(): Promise<IReactor> {\n const module = await this.buildModule();\n return module.reactor;\n }\n\n async buildModule(): Promise<InProcessReactorModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor\"]);\n }\n\n const featureFlags = resolveFeatureFlags(this.executorConfig.featureFlags);\n if (this.executorConfig.protocolSupport === undefined) {\n this.executorConfig = {\n ...this.executorConfig,\n protocolSupport: localSupports(this.getPeerCapabilities(), featureFlags)\n .protocols,\n };\n }\n\n if (\n this.readModelCoordinator !== undefined &&\n this.readModelCoordinatorFactory !== undefined\n ) {\n throw new Error(\n \"withReadModelCoordinator and withReadModelCoordinatorFactory are mutually exclusive; register one coordinator source\",\n );\n }\n\n if (\n this.projectionShardConfig !== undefined &&\n this.readModelCoordinatorFactory !== undefined\n ) {\n throw new Error(\n \"withProjectionShards and withReadModelCoordinatorFactory are mutually exclusive; the factory owns the projection worker through its createProjectionShardManager dependency\",\n );\n }\n\n if (\n this.projectionShardConfig !== undefined &&\n this.readModelFactories.length > 0\n ) {\n throw new Error(\n \"withProjectionShards does not support read models registered through withReadModelFactory; projection workers cannot receive host-only factory dependencies\",\n );\n }\n\n // Same class of bug, quieter: `readModelInstances` is handed to the\n // in-process ReadModelCoordinator only, so anything registered through\n // withReadModel is dropped on the floor under sharding rather than\n // rejected. Fail here instead.\n //\n // `withProjectionShards` replaces the coordinator wholesale, so it also\n // orphans subscriptionNotificationReadModel and processorManager — both\n // built unconditionally below, with no caller registration to guard on.\n // Under bare sharding they are built, initialized, and never fed an\n // operation, which silently disables GraphQL subscriptions and every\n // package-installed processor. (Consistency-token reads are fine: the\n // manager advances the host trackers from the shards' readmodel-indexed\n // reports.) Hosts that need subscriptions, processors or host read\n // models must use `withReadModelCoordinatorFactory` with the hybrid\n // coordinator (`createHybridProjectionCoordinatorFactory`) instead.\n if (\n this.projectionShardConfig !== undefined &&\n this.readModels.length > 0\n ) {\n throw new Error(\n \"withProjectionShards does not support read models registered through withReadModel; projection workers build their own read models from the shard config and would silently omit these\",\n );\n }\n\n // Third instance of the same bug, and the quietest: a built-in kind\n // absent from both lists is indexed by nobody. Checked here, before any\n // pool opens, and again in createProjectionShardManager for the\n // coordinator-factory path.\n if (this.projectionShardConfig !== undefined) {\n validateShardCount(this.projectionShardConfig.shardCount);\n validateBuiltInKindCoverage(\n this.projectionShardConfig.preReadyKinds,\n this.projectionShardConfig.postReadyKinds,\n );\n }\n\n // One resolution pass feeds both sides: the host registry gets every\n // resolved module (in both executor modes), and importable sources form\n // the worker manifest.\n const resolvedSources = await resolveModelSources(\n this.documentModelSources,\n );\n if (this.workerPool) {\n if (resolvedSources.manifest.length === 0) {\n throw new Error(\n \"withWorkerPool requires at least one worker-importable document-model source ({ filePath } or { packageName }).\",\n );\n }\n if (resolvedSources.moduleOnlyKeys.length > 0) {\n throw new Error(\n `withWorkerPool requires worker-importable sources, but these models were registered only as live modules: ${resolvedSources.moduleOnlyKeys.join(\", \")}. Provide a { filePath } or { packageName } source for each.`,\n );\n }\n }\n this.resolvedModelManifest =\n resolvedSources.manifest.length > 0\n ? resolvedSources.manifest\n : undefined;\n this.moduleOnlyModelKeys = resolvedSources.moduleOnlyKeys;\n\n const documentModelRegistry = new DocumentModelRegistry();\n if (this.upgradeManifests.length > 0) {\n const results = documentModelRegistry.registerUpgradeManifests(\n ...this.upgradeManifests,\n );\n for (const result of results) {\n if (result.status === \"error\") {\n this.logger.error(\n \"Failed to register upgrade manifest: @error\",\n result.error.message,\n );\n }\n }\n }\n if (resolvedSources.modules.length > 0) {\n const results = documentModelRegistry.registerModules(\n ...resolvedSources.modules,\n );\n for (const result of results) {\n if (result.status === \"error\") {\n this.logger.error(\n \"Failed to register document model: @error\",\n result.error.message,\n );\n }\n }\n }\n\n const reactorDbConfig = this.resolveReactorDbConfig();\n const baseDatabase =\n this.kyselyInstance ??\n (reactorDbConfig\n ? await this.createPostgresDatabase(reactorDbConfig)\n : await createDefaultDatabase());\n\n if (this.migrationStrategy === \"auto\") {\n const result = await runMigrations(baseDatabase, REACTOR_SCHEMA);\n if (!result.success && result.error) {\n throw new Error(`Database migration failed: ${result.error.message}`);\n }\n }\n\n await checkStoredProtocols(\n baseDatabase,\n REACTOR_SCHEMA,\n this.executorConfig.protocolSupport ?? {},\n this.unsupportedStoredDocuments,\n this.logger,\n );\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n\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 eventBus = this.eventBus || new EventBus();\n const resolver = this.documentModelLoader\n ? new DocumentModelResolver(\n documentModelRegistry,\n this.documentModelLoader,\n )\n : new NullDocumentModelResolver(documentModelRegistry);\n if (resolver instanceof DocumentModelResolver) {\n resolver.setModelLoadedHook((documentType) =>\n eventBus.emit(ReactorEventTypes.MODEL_LOADED, { documentType }),\n );\n }\n const queue = this.queueInstance ?? new InMemoryQueue(eventBus, resolver);\n const jobTracker = new InMemoryJobTracker(eventBus);\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: this.writeCacheConfig?.maxDocuments ?? 100,\n ringBufferSize: this.writeCacheConfig?.ringBufferSize ?? 10,\n keyframeInterval: this.writeCacheConfig?.keyframeInterval ?? 10,\n };\n\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n documentModelRegistry,\n cacheConfig,\n );\n await writeCache.startup();\n\n const operationIndex = new KyselyOperationIndex(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const settledWatermark = new SettledWatermark(\n createKyselyWatermarkProbe(\n database as unknown as Kysely<StorageDatabase>,\n ),\n this.logger,\n );\n const catchUp = new CatchUpScheduler(\n settledWatermark,\n operationIndex,\n this.catchUpConfig,\n this.logger,\n {\n onSwept: (result, thread) =>\n void eventBus\n .emit(ReactorEventTypes.CATCHUP_SWEPT, { ...result, thread })\n .catch(() => {}),\n describeSessions: (xids) => describeWaitingSessions(database, xids),\n },\n );\n\n const documentMetaCache = new DocumentMetaCache(operationStore, {\n maxDocuments: 1000,\n });\n await documentMetaCache.startup();\n\n // Worker executors evict only their own caches; these are the host's.\n const unsubscribeMarkerEviction = eventBus.subscribe<JobWriteReadyEvent>(\n ReactorEventTypes.JOB_WRITE_READY,\n (_type, event) => {\n for (const item of event.operations) {\n if (!isPurgeMarker(item)) continue;\n writeCache.invalidate(item.context.documentId);\n documentMetaCache.invalidate(item.context.documentId);\n }\n },\n );\n\n const collectionMembershipCache = new CollectionMembershipCache(\n operationIndex,\n );\n\n const executionScope: IExecutionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n let executorManager = this.executorManager;\n let executorStartCount = this.executorConfig.maxConcurrency ?? 1;\n if (!executorManager) {\n if (this.workerPool) {\n const pool = this.workerPool;\n let factory = pool.factory;\n if (!factory) {\n if (pool.db === undefined) {\n throw new Error(\n \"unreachable: worker pool configured without db or factory\",\n );\n }\n if (this.signer && !this.workerSigner) {\n this.logger!.warn(\n \"Worker pool has no signer spec; pooled workers store synthesized operations unsigned\",\n );\n }\n if (this.trustPolicy && !this.workerTrustPolicy) {\n this.logger!.warn(\n \"Worker pool has no trust policy spec; pooled workers apply the default trust policy\",\n );\n }\n factory = await this.createDefaultWorkerFactory(\n pool.numWorkers,\n pool.db,\n );\n }\n const poolManager = new WorkerPoolJobExecutorManager(\n factory,\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n operationIndex,\n this.executorConfig.jobTimeoutMs,\n this.executorConfig.deferredJobTtlMs,\n );\n executorManager = poolManager;\n executorStartCount = pool.numWorkers;\n if (resolver instanceof DocumentModelResolver) {\n resolver.setBroadcastHook((entry) => poolManager.loadModel(entry));\n }\n } else {\n executorManager = new SimpleJobExecutorManager(\n () =>\n new SimpleJobExecutor(\n this.logger!,\n documentModelRegistry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n this.driveContainerTypes,\n this.executorConfig,\n executionScope,\n this.signer,\n this.trustPolicy,\n ),\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n this.executorConfig.jobTimeoutMs,\n this.executorConfig.deferredJobTtlMs,\n );\n }\n }\n\n await executorManager.start(executorStartCount);\n\n // withReadModel + withReadModelFactory models only. Excludes\n // documentView/documentIndexer so a caller-supplied coordinator cannot\n // double-index them alongside a projection worker.\n const callerReadModels: IReadModel[] = Array.from(\n new Set([...this.readModels]),\n );\n\n // Initializing degraded must not take the reactor down -- a read model that\n // cannot reach its database should not stop the writes. But a bare\n // `console.error` and an absent component is how an operator finds out from\n // a user report instead of from the server: the process reports healthy\n // while answering from an index that stopped at boot. Each failure is now\n // logged against the component that failed and recorded on the module, so a\n // host can refuse readiness on a non-empty list.\n const degradedComponents: DegradedComponent[] = [];\n const startDegraded = (component: string, error: unknown): void => {\n degradedComponents.push({\n component,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n this.logger?.error(\n \"Reactor component started degraded: @component\",\n component,\n error,\n );\n };\n\n const readModelIndexing: ReadModelIndexingConfig = {\n commitChunkSize: DEFAULT_COMMIT_CHUNK_SIZE,\n yieldDeadlineMs:\n this.executorConfig.yieldDeadlineMs ??\n DEFAULT_READ_MODEL_YIELD_DEADLINE_MS,\n };\n\n const documentViewConsistencyTracker = new ConsistencyTracker();\n const documentView = new KyselyDocumentView(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationStore,\n operationIndex,\n writeCache,\n documentViewConsistencyTracker,\n featureFlags.documentDecisions\n ? DeletedDocumentRead.StateAtDeletion\n : DeletedDocumentRead.NotFound,\n readModelIndexing,\n );\n documentView.attachCatchUp(\n settledWatermark,\n this.catchUpConfig.maxTrackedAboveCursor,\n );\n\n try {\n await documentView.init();\n } catch (error) {\n startDegraded(\"document view\", error);\n }\n\n const documentIndexerConsistencyTracker = new ConsistencyTracker();\n const documentIndexer = new KyselyDocumentIndexer(\n database as unknown as Kysely<IndexerDatabase>,\n operationIndex,\n writeCache,\n documentIndexerConsistencyTracker,\n readModelIndexing,\n );\n documentIndexer.attachCatchUp(\n settledWatermark,\n this.catchUpConfig.maxTrackedAboveCursor,\n );\n\n try {\n await documentIndexer.init();\n } catch (error) {\n startDegraded(\"document indexer\", error);\n }\n\n const subscriptionManager = new ReactorSubscriptionManager(\n new DefaultSubscriptionErrorHandler(),\n );\n\n const subscriptionNotificationReadModel =\n new SubscriptionNotificationReadModel(subscriptionManager, documentView);\n\n const processorManagerConsistencyTracker = new ConsistencyTracker();\n const processorManager = new ProcessorManager(\n // @ts-expect-error - Database type is a superset that includes all required tables\n database,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n this.logger!,\n this.driveContainerTypes,\n { legacyProcessorIds: this.features.legacyProcessorIds !== false },\n );\n processorManager.attachCatchUp(\n settledWatermark,\n this.catchUpConfig.maxTrackedAboveCursor,\n );\n\n try {\n await processorManager.init();\n } catch (error) {\n startDegraded(\"processor manager\", error);\n }\n\n for (const [index, factory] of this.readModelFactories.entries()) {\n // A read model that cannot build or catch up must not take the reactor\n // down with it: log and start degraded, as the indexers above do.\n //\n // A factory carries no id, and a failure has no instance to ask for one,\n // so it is named by registration order plus the function's own name when\n // it has one -- otherwise the log cannot say which of them failed.\n try {\n const readModel = await factory({\n documentModelRegistry,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n });\n callerReadModels.push(readModel);\n } catch (error) {\n startDegraded(\n `read model ${index}${factory.name ? ` (${factory.name})` : \"\"}`,\n error,\n );\n }\n }\n\n const readModelInstances: IReadModel[] = [\n ...callerReadModels,\n documentView,\n documentIndexer,\n ];\n\n const hostTrackers = {\n \"document-view\": documentViewConsistencyTracker,\n \"document-indexer\": documentIndexerConsistencyTracker,\n };\n const readModelCoordinator = this.readModelCoordinator\n ? this.readModelCoordinator\n : this.readModelCoordinatorFactory\n ? await this.readModelCoordinatorFactory({\n eventBus,\n logger: this.logger,\n readModels: callerReadModels,\n subscriptionNotificationReadModel,\n processorManager,\n documentView,\n documentIndexer,\n createProjectionShardManager: (config) =>\n this.createProjectionShardManager(\n config,\n eventBus,\n hostTrackers,\n readModelIndexing,\n false,\n ),\n registerShutdownHook: (hook) => this.shutdownHooks.push(hook),\n })\n : this.projectionShardConfig\n ? await this.createProjectionShardManager(\n this.projectionShardConfig,\n eventBus,\n hostTrackers,\n readModelIndexing,\n true,\n )\n : new ReadModelCoordinator(eventBus, readModelInstances, [\n subscriptionNotificationReadModel,\n processorManager,\n ]);\n\n if (hasCatchUpStatuses(readModelCoordinator)) {\n catchUp.addStatusSource(() =>\n readModelCoordinator\n .catchUpStatuses()\n .flatMap((status) => status.consumers),\n );\n }\n const indexedReadModels =\n readModelCoordinator.indexedReadModels?.bind(readModelCoordinator);\n if (indexedReadModels) {\n catchUp.addSource(\n () =>\n indexedReadModels().filter(\n (model): model is IReadModel & ICatchUpConsumer =>\n isCatchUpConsumer(model),\n ),\n \"host\",\n );\n } else {\n this.logger.warn(\n \"The read model coordinator does not report indexedReadModels; the host runs no read-side catch-up sweep\",\n );\n }\n\n const reactor = new Reactor(\n this.logger,\n documentModelRegistry,\n queue,\n jobTracker,\n readModelCoordinator,\n this.features,\n documentView,\n documentIndexer,\n operationStore,\n eventBus,\n executorManager,\n catchUp,\n [unsubscribeMarkerEviction],\n [() => processorManager.shutdown()],\n );\n\n const localPeer: LocalPeer = {\n capabilities: this.getPeerCapabilities(),\n flags: featureFlags,\n appKey: this.signer?.app?.key,\n protocolVersionsOf: async (documentId, branch) => {\n try {\n const meta = await documentMetaCache.getDocumentMeta(\n documentId,\n branch,\n );\n return meta.protocolVersions;\n } catch {\n return undefined;\n }\n },\n forgetDocument: (documentId) => {\n documentMetaCache.invalidate(documentId);\n },\n };\n let syncModule: InProcessSyncModule | undefined = undefined;\n if (this.channelScheme) {\n const factory =\n this.channelScheme === ChannelScheme.CONNECT\n ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue)\n : new GqlResponseChannelFactory(this.logger);\n\n const syncBuilder = new SyncBuilder().withChannelFactory(factory);\n syncModule = syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n this.driveContainerTypes,\n settledWatermark,\n localPeer,\n );\n await syncModule.syncManager.startup();\n } else if (this.syncBuilder) {\n syncModule = this.syncBuilder.buildModule(\n reactor,\n this.logger,\n operationIndex,\n eventBus,\n database as unknown as Kysely<StorageDatabase>,\n this.driveContainerTypes,\n settledWatermark,\n localPeer,\n );\n await syncModule.syncManager.startup();\n }\n\n let groupReevaluationTrigger: GroupReevaluationTrigger | undefined;\n if (featureFlags.authGroups) {\n groupReevaluationTrigger = new GroupReevaluationTrigger(\n this.logger,\n eventBus,\n queue,\n operationIndex,\n database as unknown as Kysely<DocumentViewDatabase>,\n );\n groupReevaluationTrigger.attachCatchUp(\n settledWatermark,\n this.catchUpConfig.maxTrackedAboveCursor,\n );\n await groupReevaluationTrigger.startup();\n catchUp.addConsumer(groupReevaluationTrigger, \"host\");\n }\n\n const module: InProcessReactorModule = {\n eventBus,\n documentModelRegistry,\n featureFlags,\n queue,\n jobTracker,\n executorManager,\n database,\n operationStore,\n keyframeStore,\n writeCache,\n operationIndex,\n documentView,\n documentViewConsistencyTracker,\n documentIndexer,\n documentIndexerConsistencyTracker,\n readModelCoordinator,\n subscriptionManager,\n processorManager,\n processorManagerConsistencyTracker,\n syncModule,\n reactor,\n groupReevaluationTrigger,\n pools: this.instrumentedPools,\n degradedComponents,\n catchUp,\n settledWatermark,\n documentPurgeService: new DocumentPurgeService(\n queue,\n jobTracker,\n eventBus,\n ),\n };\n\n catchUp.start();\n\n if (degradedComponents.length > 0) {\n // buildModule assigns a default logger before anything here runs.\n this.logger.warn(\n \"Reactor started with @count degraded component(s): @components\",\n degradedComponents.length,\n degradedComponents.map(({ component }) => component).join(\", \"),\n );\n }\n\n if (this.signalHandlersEnabled) {\n this.attachSignalHandlers(module);\n }\n\n return module;\n }\n\n /**\n * The single Postgres config for the parent, executor workers, and\n * projection shards. They must share one physical database (the parent\n * writes operations; workers and shards read them), so divergent\n * worker/shard targets throw. `withKysely` overrides the parent and is not\n * validated against a worker/shard `db`.\n */\n private resolveReactorDbConfig(): DbConfig | undefined {\n const workerDb = this.workerPool?.db;\n const projectionDb = this.projectionShardConfig?.db;\n if (\n workerDb &&\n projectionDb &&\n !sameDatabaseTarget(workerDb, projectionDb)\n ) {\n throw new Error(\n \"withWorkerPool({ db }) and withProjectionShards({ db }) must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.\",\n );\n }\n return workerDb ?? projectionDb;\n }\n\n /**\n * Constructs a {@link ProjectionShardManager} bound to the host event\n * bus. Builds the default thread-transport factory unless one was\n * injected via {@link withProjectionWorkerFactory}. Calls\n * `manager.startup()` so all N workers reach READY before the reactor\n * is returned to the caller.\n *\n * @param consistencyTrackers The host's trackers for the built-in read\n * models, keyed by kind. Under sharding the host's own copies of those\n * models never index an operation, so the manager advances these from the\n * shards' relayed indexing reports; without them every read carrying a\n * consistency token waits forever.\n * @param indexing The chunking bounds the host's own read models index\n * under. The shards' read models are built inside the worker, so without\n * this they would fall back to the library default and a host that tuned\n * the cadence would silently get it on the in-process path only.\n * @param registerShutdownHook Whether the builder owns `manager.shutdown()`\n * at signal time. False for the coordinator-factory path, whose factory\n * registers its own hook so host chains drain before the worker stops.\n */\n private async createProjectionShardManager(\n config: ProjectionShardBuilderConfig & ProjectionShardHooks,\n eventBus: IEventBus,\n consistencyTrackers: Partial<\n Record<BuiltInReadModelKind, IConsistencyTracker>\n >,\n indexing: ReadModelIndexingConfig,\n registerShutdownHook: boolean,\n ): Promise<ProjectionShardManager> {\n const parentDb = this.resolveReactorDbConfig();\n const baseDb = config.db ?? parentDb;\n // withProjectionShards and withReadModelCoordinatorFactory are mutually\n // exclusive, so the configured one names the caller in every message here.\n const caller =\n this.projectionShardConfig !== undefined\n ? \"withProjectionShards\"\n : \"withReadModelCoordinatorFactory\";\n if (!baseDb) {\n throw new Error(\n `${caller} requires a db (or an executor worker pool configured with one); projection workers need connection info to open their own pools.`,\n );\n }\n const workerDb = this.workerPool?.db;\n if (config.db && workerDb && !sameDatabaseTarget(workerDb, config.db)) {\n throw new Error(\n \"withWorkerPool({ db }) and the projection worker db must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.\",\n );\n }\n // The parent serves reads from the tables the worker writes, so the\n // worker's db must name the parent's database too. A `db` that reached\n // this method through the coordinator factory contributed nothing to\n // `resolveReactorDbConfig`: the factory does not run until long after the\n // parent database is built, so without this guard the parent silently\n // falls back to the default embedded database while the worker projects\n // into Postgres, and the parent's read models never see a projected row.\n // `withKysely` owns the parent connection outright and is not validated.\n if (config.db && this.kyselyInstance === undefined) {\n if (!parentDb) {\n throw new Error(\n `The projection worker db passed to ${caller} must also be the parent reactor's database, but nothing configures the parent: it would fall back to the default embedded database while the worker projects into Postgres. Pass the same { db } to withWorkerPool, or give the parent its connection with withKysely.`,\n );\n }\n if (!sameDatabaseTarget(config.db, parentDb)) {\n throw new Error(\n `The projection worker db passed to ${caller} and the parent reactor database must address the same Postgres database (same host, port, and database); the parent writes operations there and the projection shards read them.`,\n );\n }\n }\n validateShardCount(config.shardCount);\n validateBuiltInKindCoverage(config.preReadyKinds, config.postReadyKinds);\n // The executor pool guard in buildModule only runs with a worker pool.\n if (this.moduleOnlyModelKeys.length > 0) {\n throw new Error(\n `projection workers require worker-importable sources, but these models were registered only as live modules: ${this.moduleOnlyModelKeys.join(\", \")}. Provide a { filePath } or { packageName } source for each.`,\n );\n }\n const models = this.resolvedModelManifest ?? [];\n const db: DbConfig = {\n ...baseDb,\n poolSize: config.poolSize ?? baseDb.poolSize,\n applicationName: config.db?.applicationName ?? \"reactor-projection-shard\",\n };\n const factory =\n this.projectionWorkerFactory ??\n (await this.createDefaultProjectionWorkerFactory());\n const poolInstrumentations: ForwardingPoolInstrumentation[] = [];\n for (let i = 0; i < config.shardCount; i++) {\n const forwarder = createForwardingPoolInstrumentation(\n `projection-shard-${i}`,\n );\n poolInstrumentations.push(forwarder);\n this.instrumentedPools.push(forwarder);\n }\n const { ProjectionShardManager } =\n await import(\"../projection/projection-shard-manager.js\");\n const manager = new ProjectionShardManager({\n shardCount: config.shardCount,\n db,\n models,\n preReadyKinds: config.preReadyKinds,\n postReadyKinds: config.postReadyKinds,\n indexing,\n catchUp: this.catchUpConfig,\n factory,\n logger: this.logger!,\n hostBus: eventBus,\n initTimeoutMs: config.initTimeoutMs,\n shutdownGraceMs: config.shutdownGraceMs,\n drainTimeoutMs: config.drainTimeoutMs,\n chainDepthReportIntervalMs: config.chainDepthReportIntervalMs,\n poolInstrumentations,\n consistencyTrackers,\n onReadReady: config.onReadReady,\n onShardFatal: config.onShardFatal,\n });\n try {\n await manager.startup();\n } catch (error) {\n // A shard that never reached READY leaves a live worker thread behind,\n // which keeps the process alive after the boot error propagates.\n try {\n await manager.shutdown();\n } catch (shutdownError) {\n this.logger!.warn(\n \"projection shard manager shutdown after a failed startup also failed: @error\",\n shutdownError,\n );\n }\n throw error;\n }\n if (registerShutdownHook) {\n this.shutdownHooks.push(() => manager.shutdown());\n }\n return manager;\n }\n\n private async createDefaultProjectionWorkerFactory(): Promise<ProjectionWorkerFactory> {\n const [{ createProjectionThreadTransport }, { projectionWorkerEntryPath }] =\n await Promise.all([\n import(\"../projection/transport.js\"),\n import(\"../projection/projection-worker/index.js\"),\n ]);\n return () => createProjectionThreadTransport(projectionWorkerEntryPath);\n }\n\n /**\n * Default {@link WorkerFactory} used when the pool options carry no\n * custom `factory`. Each worker spawns a real `node:worker_threads`\n * Worker pointing at the compiled `worker/entry.js`.\n */\n private async createDefaultWorkerFactory(\n numWorkers: number,\n db: DbConfig,\n ): Promise<WorkerFactory> {\n const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] =\n await Promise.all([\n import(\"../executor/worker/worker-handle.js\"),\n import(\"../executor/worker/transport.js\"),\n import(\"../executor/worker/index.js\"),\n ]);\n // The wire protocol keeps its full shape; only \"thread\" is implemented.\n const poolConfig: WorkerPoolConfig = {\n enabled: true,\n numWorkers,\n workerType: \"thread\",\n };\n const models = this.resolvedModelManifest ?? [];\n const logger = this.logger!;\n return (index: number) => {\n const workerId = `reactor-worker-${index}`;\n const poolInstrumentation = createForwardingPoolInstrumentation(workerId);\n this.instrumentedPools.push(poolInstrumentation);\n return new WorkerHandle({\n workerId,\n index,\n transport: createThreadTransport(workerEntryPath),\n initPayload: {\n poolConfig,\n db,\n models,\n executorConfig: this.executorConfig,\n signer: this.workerSigner,\n trustPolicy: this.workerTrustPolicy,\n },\n logger,\n poolInstrumentation,\n });\n };\n }\n\n /**\n * Builds the parent Kysely instance against a real Postgres server using\n * the same {@link DbConfig} the workers receive at init. Used in the\n * worker-pool path so the parent reactor and each worker thread share\n * storage; PGlite cannot be shared across threads. The constructed pool\n * is wrapped with {@link instrumentPgPool} and the resulting\n * {@link PoolInstrumentation} is pushed onto {@link instrumentedPools} so\n * the reactor module exposes acquire-wait and pool-stat surfaces.\n */\n private async createPostgresDatabase(\n config: DbConfig,\n ): Promise<Kysely<Database>> {\n const { Kysely, PostgresDialect } = await import(\"kysely\");\n const pgModule = await import(\"pg\");\n const Pool = pgModule.default.Pool;\n const pool = new Pool({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n ssl: config.ssl ? { rejectUnauthorized: false } : undefined,\n application_name: config.applicationName,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n this.instrumentedPools.push(\n instrumentPgPool(pool, config.applicationName ?? \"reactor-host\"),\n );\n return new Kysely<Database>({\n dialect: new PostgresDialect({ pool }),\n });\n }\n\n private attachSignalHandlers(module: InProcessReactorModule): void {\n if (\n typeof globalThis === \"undefined\" ||\n !(\"process\" in globalThis) ||\n typeof globalThis.process.on !== \"function\"\n ) {\n return;\n }\n\n const nodeProcess = globalThis.process;\n const realExit = nodeProcess.exit.bind(nodeProcess);\n let shutdownInProgress = false;\n let pendingExitCode: number | undefined;\n\n // While our async cleanup runs, swap process.exit for a recording shim so\n // peer SIGINT handlers (notably vite's bindCLIShortcuts handler, default\n // enabled) cannot terminate the process before reactor.kill() and\n // database.destroy() finish. Without this guard the process exits during\n // the first microtask of our async chain, leaving e.g. PGlite's WAL dirty\n // and the data-dir lock held.\n const handler = async (signal: string) => {\n if (shutdownInProgress) {\n this.logger!.warn(\n `Received ${signal} again, continuing graceful shutdown...`,\n );\n return;\n }\n\n shutdownInProgress = true;\n nodeProcess.exit = ((code?: number) => {\n pendingExitCode ??= code ?? 0;\n return undefined as never;\n }) as typeof nodeProcess.exit;\n\n this.logger!.info(`Received ${signal}, starting graceful shutdown...`);\n\n const status = module.reactor.kill();\n\n try {\n await status.completed;\n } catch (error) {\n this.logger!.error(\"Shutdown failed waiting for reactor:\", error);\n nodeProcess.exit = realExit;\n realExit(1);\n return;\n }\n\n for (const hook of this.shutdownHooks) {\n try {\n await hook();\n } catch (error) {\n this.logger!.error(\"Shutdown hook failed:\", error);\n }\n }\n\n try {\n await module.database.destroy();\n } catch (error) {\n this.logger!.error(\"Shutdown failed destroying database:\", error);\n nodeProcess.exit = realExit;\n realExit(1);\n return;\n }\n\n this.logger!.info(\"Shutdown complete\");\n nodeProcess.exit = realExit;\n // A failure code set before the signal (a fatal-error handler) wins\n // over a peer handler's exit(0).\n realExit(pendingExitCode || Number(nodeProcess.exitCode ?? 0));\n };\n\n nodeProcess.prependListener(\"SIGINT\", () => void handler(\"SIGINT\"));\n nodeProcess.prependListener(\"SIGTERM\", () => void handler(\"SIGTERM\"));\n }\n}\n","import type { ILogger } from \"document-model\";\nimport type { CatchUpStatus } from \"../catch-up/types.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport {\n ReactorEventTypes,\n type JobReadReadyEvent,\n type ReadModelBatchCompletedEvent,\n type ReadModelIndexedEvent,\n type ReadModelIndexingStage,\n} from \"../events/types.js\";\nimport {\n indexReserved,\n releaseBatch,\n reserveBatch,\n type BatchReservations,\n} from \"../read-models/coordinator.js\";\nimport type {\n ILiveReadModelCoordinator,\n IReadModel,\n ReadModelRegistrationStage,\n} from \"../read-models/interfaces.js\";\nimport type { ProjectionShardManager } from \"./projection-shard-manager.js\";\n\nexport type HybridProjectionCoordinatorOptions = {\n eventBus: IEventBus;\n logger: ILogger;\n manager: ProjectionShardManager;\n /** Caller and factory registered models. Never documentView/documentIndexer. */\n preReady: IReadModel[];\n /** subscriptionNotificationReadModel, processorManager. */\n postReady: IReadModel[];\n /** Included in `readModels` for getReadModel() lookup; never indexed here. */\n lookupOnly: IReadModel[];\n};\n\n/** Host-side stages on a per-queueKey chain driven by the worker's read-ready. */\nexport class HybridProjectionCoordinator implements ILiveReadModelCoordinator {\n /** One array, mutated in place: reactor-api captures it by reference once. */\n readonly readModels: IReadModel[];\n\n private readonly eventBus: IEventBus;\n private readonly logger: ILogger;\n private readonly manager: ProjectionShardManager;\n private readonly preReady: IReadModel[];\n private readonly postReady: IReadModel[];\n private readonly chains = new Map<string, Promise<void>>();\n\n constructor(options: HybridProjectionCoordinatorOptions) {\n this.eventBus = options.eventBus;\n this.logger = options.logger;\n this.manager = options.manager;\n this.preReady = options.preReady;\n this.postReady = options.postReady;\n this.readModels = [\n ...options.preReady,\n ...options.postReady,\n ...options.lookupOnly,\n ];\n }\n\n start(): void {\n this.manager.start();\n }\n\n stop(): void {\n this.manager.stop();\n }\n\n /** Wired as `onReadReady`; host trackers are already advanced (port FIFO). */\n acceptReadReady(event: JobReadReadyEvent): void {\n if (event.operations.length === 0) {\n void this.manager\n .emitReadReady(event)\n .catch((err: unknown) =>\n this.logger.error(\n \"JOB_READ_READY emit failed for job @jobId: @Error\",\n { jobId: event.jobId },\n err,\n ),\n );\n return;\n }\n\n const enqueuedAt = performance.now();\n const key = this.queueKeyFor(event);\n const reservations = reserveBatch(\n this.indexedReadModels(),\n event.operations,\n this.logger,\n );\n const run = () => this.runHostChain(event, enqueuedAt, reservations);\n const previous = this.chains.get(key) ?? Promise.resolve();\n const current = previous.then(run, run);\n\n this.chains.set(key, current);\n void current.finally(() => {\n if (this.chains.get(key) === current) {\n this.chains.delete(key);\n }\n });\n }\n\n addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void {\n if (this.readModels.some(({ name }) => name === readModel.name)) {\n throw new Error(`Read model \"${readModel.name}\" is already registered`);\n }\n\n if (stage === \"pre_ready\") {\n this.preReady.push(readModel);\n } else {\n this.postReady.push(readModel);\n }\n this.readModels.push(readModel);\n }\n\n getChainDepth(): number {\n return this.manager.getChainDepth() + this.chains.size;\n }\n\n indexedReadModels(): readonly IReadModel[] {\n return [...this.preReady, ...this.postReady];\n }\n\n catchUpStatuses(): CatchUpStatus[] {\n return this.manager.catchUpStatuses();\n }\n\n /** Worker chains flush first, so every relayed read-ready is in `chains`. */\n async drain(): Promise<void> {\n await this.manager.drain();\n while (this.chains.size > 0) {\n const pending = Array.from(this.chains.values());\n await Promise.allSettled(pending);\n }\n }\n\n /** Builder shutdown hook; reaches `manager.shutdown()` even when drain fails. */\n async shutdown(): Promise<void> {\n try {\n await this.drain();\n } catch (error) {\n this.logger.warn(\n \"hybrid coordinator drain failed during shutdown: @Error\",\n error,\n );\n }\n await this.manager.shutdown();\n }\n\n private async runHostChain(\n event: JobReadReadyEvent,\n enqueuedAt: number,\n reservations: BatchReservations,\n ): Promise<void> {\n try {\n await this.runHostBatch(event, enqueuedAt, reservations);\n } finally {\n releaseBatch(reservations);\n }\n }\n\n private async runHostBatch(\n event: JobReadReadyEvent,\n enqueuedAt: number,\n reservations: BatchReservations,\n ): Promise<void> {\n const chainWaitDurationMs = performance.now() - enqueuedAt;\n const preReadyStart = performance.now();\n // A failing host read model must not withhold JOB_READ_READY from awaiters.\n try {\n await Promise.all(\n this.preReady.map((readModel) =>\n this.indexWithTiming(readModel, \"pre_ready\", event, reservations),\n ),\n );\n } catch (error) {\n this.logger.error(\n \"Host pre-ready read model indexing failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n\n const preReadyDurationMs = performance.now() - preReadyStart;\n\n // Awaited: the subscription read model must run after READ_READY is emitted.\n const emitStart = performance.now();\n try {\n await this.manager.emitReadReady(event);\n } catch (error) {\n this.logger.error(\n \"JOB_READ_READY emit failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n const emitDurationMs = performance.now() - emitStart;\n\n const postReadyStart = performance.now();\n try {\n await Promise.all(\n this.postReady.map((readModel) =>\n this.indexWithTiming(readModel, \"post_ready\", event, reservations),\n ),\n );\n } catch (error) {\n this.logger.error(\n \"Host post-ready read model indexing failed for job @jobId: @Error\",\n { jobId: event.jobId },\n error,\n );\n }\n const postReadyDurationMs = performance.now() - postReadyStart;\n\n // The worker reports its own batch for this job, covering the built-in\n // read models only. Without this second report the host's stages — every\n // read model the hybrid coordinator exists to keep on the host — are\n // absent from the batch metrics, which is exactly what a rollout measures.\n this.emitBatchCompleted({\n jobId: event.jobId,\n batchSize: event.operations.length,\n chainWaitDurationMs,\n preReadyDurationMs,\n emitDurationMs,\n postReadyDurationMs,\n });\n }\n\n private async indexWithTiming(\n readModel: IReadModel,\n stage: ReadModelIndexingStage,\n event: JobReadReadyEvent,\n reservations: BatchReservations,\n ): Promise<void> {\n const start = performance.now();\n let success = false;\n try {\n await indexReserved(readModel, event.operations, reservations);\n success = true;\n } finally {\n this.emitReadModelIndexed({\n jobId: event.jobId,\n readModelName: readModel.name,\n stage,\n durationMs: performance.now() - start,\n operationCount: event.operations.length,\n success,\n });\n }\n }\n\n private emitBatchCompleted(payload: ReadModelBatchCompletedEvent): void {\n void this.eventBus\n .emit(ReactorEventTypes.READMODEL_BATCH_COMPLETED, payload)\n .catch((err: unknown) =>\n this.logger.error(\n \"READMODEL_BATCH_COMPLETED emit failed for job @jobId: @Error\",\n { jobId: payload.jobId },\n err,\n ),\n );\n }\n\n private emitReadModelIndexed(payload: ReadModelIndexedEvent): void {\n void this.eventBus\n .emit(ReactorEventTypes.READMODEL_INDEXED, payload)\n .catch((err: unknown) =>\n this.logger.error(\n \"READMODEL_INDEXED emit failed for job @jobId: @Error\",\n { jobId: payload.jobId },\n err,\n ),\n );\n }\n\n private queueKeyFor(event: JobReadReadyEvent): string {\n const ctx = event.operations[0]!.context;\n return `${ctx.documentId}:${ctx.scope}:${ctx.branch}`;\n }\n}\n","import type {\n ReadModelCoordinatorFactory,\n ReadModelCoordinatorFactoryDeps,\n} from \"../core/reactor-builder.js\";\nimport { HybridProjectionCoordinator } from \"./hybrid-projection-coordinator.js\";\nimport type { DbConfig } from \"./protocol.js\";\n\nexport type HybridProjectionOptions = {\n /** Defaults to 1: one worker captures the whole win (Run 11). */\n shardCount?: number;\n poolSize?: number;\n db?: DbConfig;\n onFatal?: (shardId: string, reason: Error) => void;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\ntype CoordinatorRef = { current: HybridProjectionCoordinator | undefined };\n\n/** Built-ins in one projection worker; all other read models stay on the host. */\nexport function createHybridProjectionCoordinatorFactory(\n options: HybridProjectionOptions = {},\n): ReadModelCoordinatorFactory {\n return async (deps: ReadModelCoordinatorFactoryDeps) => {\n // Set before start(): onReadReady cannot fire until a write-ready is routed.\n const ref: CoordinatorRef = { current: undefined };\n const manager = await deps.createProjectionShardManager({\n shardCount: options.shardCount ?? 1,\n preReadyKinds: [\"document-view\", \"document-indexer\"],\n postReadyKinds: [],\n db: options.db,\n poolSize: options.poolSize,\n initTimeoutMs: options.initTimeoutMs,\n shutdownGraceMs: options.shutdownGraceMs,\n drainTimeoutMs: options.drainTimeoutMs,\n chainDepthReportIntervalMs: options.chainDepthReportIntervalMs,\n onReadReady: (event) => ref.current!.acceptReadReady(event),\n onShardFatal: options.onFatal,\n });\n const coordinator = new HybridProjectionCoordinator({\n eventBus: deps.eventBus,\n logger: deps.logger,\n manager,\n preReady: deps.readModels,\n postReady: [\n deps.subscriptionNotificationReadModel,\n deps.processorManager,\n ],\n lookupOnly: [deps.documentView, deps.documentIndexer],\n });\n ref.current = coordinator;\n deps.registerShutdownHook(() => coordinator.shutdown());\n return coordinator;\n };\n}\n","import type {\n ISigner,\n SignaturePolicy,\n} from \"@powerhousedao/shared/document-model\";\nimport { DEFAULT_SIGNATURE_POLICY } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { ActionEvaluationConfig } from \"../client/reactor-client.js\";\nimport { ReactorClient } from \"../client/reactor-client.js\";\nimport type { ProtocolSelection } from \"../client/types.js\";\nimport type { IReadGate } from \"../decision/read-gate.js\";\nimport {\n BareReadGate,\n ModelReadGate,\n readDecisionModel,\n} from \"../decision/read-gate.js\";\nimport type { RegisteredDecisionModel } from \"../decision/registered-model.js\";\nimport type { IEventBus } from \"../events/interfaces.js\";\nimport type { IDocumentModelLoader } from \"../registry/interfaces.js\";\nimport { JobAwaiter, type IJobAwaiter } from \"../shared/awaiter.js\";\nimport { PassthroughSigner } from \"../signer/passthrough-signer.js\";\nimport type { SignerConfig } from \"../signer/types.js\";\nimport type { IDocumentIndexer, IDocumentView } from \"../storage/interfaces.js\";\nimport { DefaultSubscriptionErrorHandler } from \"../subs/default-error-handler.js\";\nimport { ReactorSubscriptionManager } from \"../subs/react-subscription-manager.js\";\nimport type { IReactorSubscriptionManager } from \"../subs/types.js\";\nimport type { ReactorBuilder } from \"./reactor-builder.js\";\nimport type {\n InProcessReactorClientModule,\n InProcessReactorModule,\n IReactor,\n} from \"./types.js\";\n\n/**\n * Builder class for constructing ReactorClient instances with proper configuration\n */\nexport class ReactorClientBuilder {\n private logger?: ILogger;\n private reactorBuilder?: ReactorBuilder;\n private reactor?: IReactor;\n private eventBus?: IEventBus;\n private documentIndexer?: IDocumentIndexer;\n private documentView?: IDocumentView;\n private signer?: ISigner;\n private workerSigner?: SignerConfig[\"workerSigner\"];\n private trustPolicy?: SignerConfig[\"trustPolicy\"];\n private workerTrustPolicy?: SignerConfig[\"workerTrustPolicy\"];\n private createSignaturePolicy: SignaturePolicy = DEFAULT_SIGNATURE_POLICY;\n private subscriptionManager?: IReactorSubscriptionManager;\n private jobAwaiter?: IJobAwaiter;\n private documentModelLoader?: IDocumentModelLoader;\n private readGate?: IReadGate;\n\n /**\n * Sets the logger for the ReactorClient.\n * @param logger - The logger to use.\n * @returns The ReactorClientBuilder instance.\n */\n public withLogger(logger: ILogger): this {\n this.logger = logger;\n return this;\n }\n\n /**\n * Either this or withReactor must be set.\n */\n public withReactorBuilder(reactorBuilder: ReactorBuilder): this {\n if (this.reactor) {\n throw new Error(\"Reactor is already set\");\n }\n\n this.reactorBuilder = reactorBuilder;\n return this;\n }\n\n /**\n * Either this or withReactorBuilder must be set.\n */\n public withReactor(\n reactor: IReactor,\n eventBus: IEventBus,\n documentIndexer: IDocumentIndexer,\n documentView: IDocumentView,\n ): this {\n if (this.reactorBuilder) {\n throw new Error(\"ReactorBuilder is already set\");\n }\n\n this.reactor = reactor;\n this.eventBus = eventBus;\n this.documentIndexer = documentIndexer;\n this.documentView = documentView;\n return this;\n }\n\n /**\n * Signs submitted actions, and the operations a reactor built from\n * `withReactorBuilder` synthesizes unless that builder has its own signer.\n * A `trustPolicy` reaches that builder unless it has its own.\n */\n public withSigner(config: ISigner | SignerConfig): this {\n if (\"signer\" in config) {\n this.signer = config.signer;\n this.workerSigner = config.workerSigner;\n this.trustPolicy = config.trustPolicy;\n this.workerTrustPolicy = config.workerTrustPolicy;\n } else {\n this.signer = config;\n }\n return this;\n }\n\n /**\n * What the client creates new documents as when a call does not say:\n * `v2-required` (the default) or `legacy`, for a fleet with peers that\n * predate v2-required documents. Existing documents keep their policy.\n */\n public withCreateSignaturePolicy(policy: SignaturePolicy): this {\n this.createSignaturePolicy = policy;\n return this;\n }\n\n public withSubscriptionManager(\n subscriptionManager: IReactorSubscriptionManager,\n ): this {\n this.subscriptionManager = subscriptionManager;\n return this;\n }\n\n public withJobAwaiter(jobAwaiter: IJobAwaiter): this {\n this.jobAwaiter = jobAwaiter;\n return this;\n }\n\n public withDocumentModelLoader(loader: IDocumentModelLoader): this {\n this.documentModelLoader = loader;\n return this;\n }\n\n /**\n * Overrides how reads are gated. A client built from a ReactorBuilder derives\n * this from that reactor's flags; one built from `withReactor` cannot, because\n * it is handed no flags and no registry, so it gates on the policy alone\n * unless a gate is supplied here.\n */\n public withReadGate(readGate: IReadGate): this {\n this.readGate = readGate;\n return this;\n }\n\n /**\n * The gate the resolved model calls for. Below authEnforcement there is no\n * model to enforce -- the registered one ignores the auth scope -- so the\n * policy is evaluated on its own, which is what reads did before the model\n * existed. Group serving turns on with authGroups, because below it a\n * `{ group }` grant does not match, so a served roster is one no grant can\n * use.\n */\n private resolveReadGate(\n reactorModule: InProcessReactorModule | undefined,\n documentView: IDocumentView,\n model: RegisteredDecisionModel | undefined,\n ): IReadGate {\n if (!reactorModule || model === undefined) {\n return new BareReadGate();\n }\n\n return new ModelReadGate(\n model,\n documentView,\n reactorModule.featureFlags.authGroups,\n reactorModule.operationIndex,\n this.logger,\n );\n }\n\n /**\n * What the client answers an authorization preflight from, or undefined when\n * it answers none.\n *\n * Resolved from the same model reads enforce, so a preflight and a read can\n * never decide against different models. Undefined below authEnforcement, and\n * undefined on the `withReactor` path, where there are no flags and no\n * registry to select a model with -- a client with no model refuses the\n * preflight rather than answering it from the legacy host-side permission\n * tables. Deliberately not derived from the read gate: `withReadGate`\n * overrides that, so sniffing the gate's type would report enforcement from a\n * caller's substitution.\n */\n private resolveActionEvaluation(\n reactorModule: InProcessReactorModule | undefined,\n model: RegisteredDecisionModel | undefined,\n ): ActionEvaluationConfig | undefined {\n return reactorModule && model\n ? { model, flags: reactorModule.featureFlags }\n : undefined;\n }\n\n /** Selection reads the sync manager's agreement; without a module, none. */\n private resolveProtocolSelection(\n reactorModule: InProcessReactorModule | undefined,\n ): ProtocolSelection | undefined {\n if (!reactorModule || !this.reactorBuilder) {\n return undefined;\n }\n const syncManager = reactorModule.syncModule?.syncManager;\n return {\n capabilities: this.reactorBuilder.getPeerCapabilities(),\n flags: reactorModule.featureFlags,\n agreement: syncManager ? () => syncManager.agreement() : undefined,\n collectionsOf: (documentIds) =>\n reactorModule.operationIndex.getCollectionsForDocuments(documentIds),\n };\n }\n\n public async build(): Promise<ReactorClient> {\n const module = await this.buildModule();\n return module.client;\n }\n\n public async buildModule(): Promise<InProcessReactorClientModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor-client\"]);\n }\n\n let reactor: IReactor;\n let eventBus: IEventBus;\n let documentIndexer: IDocumentIndexer;\n let documentView: IDocumentView;\n let reactorModule: InProcessReactorModule | undefined;\n\n if (this.reactorBuilder) {\n if (this.documentModelLoader) {\n this.reactorBuilder.withDocumentModelLoader(this.documentModelLoader);\n }\n if (this.signer && !this.reactorBuilder.hasSigner()) {\n this.reactorBuilder.withSigner(this.signer, this.workerSigner);\n }\n if (this.trustPolicy && !this.reactorBuilder.hasTrustPolicy()) {\n this.reactorBuilder.withTrustPolicy(\n this.trustPolicy,\n this.workerTrustPolicy,\n );\n }\n reactorModule = await this.reactorBuilder.buildModule();\n reactor = reactorModule.reactor;\n eventBus = reactorModule.eventBus;\n documentIndexer = reactorModule.documentIndexer;\n documentView = reactorModule.documentView;\n } else if (\n this.reactor &&\n this.eventBus &&\n this.documentIndexer &&\n this.documentView\n ) {\n reactor = this.reactor;\n eventBus = this.eventBus;\n documentIndexer = this.documentIndexer;\n documentView = this.documentView;\n reactorModule = undefined;\n } else {\n throw new Error(\n \"Either ReactorBuilder or (Reactor + EventBus + DocumentIndexer + DocumentView) is required\",\n );\n }\n\n const signer = this.signer ?? new PassthroughSigner();\n\n const subscriptionManager =\n this.subscriptionManager ??\n reactorModule?.subscriptionManager ??\n new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());\n\n const jobAwaiter =\n this.jobAwaiter ??\n new JobAwaiter(eventBus, (jobId, signal) =>\n reactor.getJobStatus(jobId, signal),\n );\n\n const decisionModel = reactorModule\n ? readDecisionModel(\n reactorModule.featureFlags,\n reactorModule.documentModelRegistry,\n )\n : undefined;\n\n const client = new ReactorClient(\n this.logger,\n reactor,\n signer,\n subscriptionManager,\n jobAwaiter,\n documentIndexer,\n documentView,\n this.readGate ??\n this.resolveReadGate(reactorModule, documentView, decisionModel),\n this.resolveActionEvaluation(reactorModule, decisionModel),\n this.createSignaturePolicy,\n this.resolveProtocolSelection(reactorModule),\n );\n\n return {\n client,\n reactor,\n eventBus,\n documentIndexer,\n documentView,\n signer,\n subscriptionManager,\n jobAwaiter,\n reactorModule,\n };\n }\n}\n","export interface ParsedDriveUrl {\n url: string;\n driveId: string;\n graphqlEndpoint: string;\n}\n\n/**\n * Parse a drive URL to extract drive ID and construct GraphQL endpoint.\n * Preserves any subpath prefix so the result is correct when the reactor is\n * served behind a proxy at a non-root path.\n * e.g., \"http://localhost:4001/d/abc123\" -> { driveId: \"abc123\", graphqlEndpoint: \"http://localhost:4001/graphql/r\" }\n * e.g., \"https://example.com/api/reactor/d/abc123\" -> { ..., graphqlEndpoint: \"https://example.com/api/reactor/graphql/r\" }\n */\nexport function parseDriveUrl(url: string): ParsedDriveUrl {\n const parsedUrl = new URL(url);\n const driveId = url.split(\"/\").pop() ?? \"\";\n const basePath = parsedUrl.pathname.replace(/\\/d\\/[^/]+\\/?$/, \"\");\n const graphqlEndpoint = `${parsedUrl.protocol}//${parsedUrl.host}${basePath}/graphql/r`;\n return { url, driveId, graphqlEndpoint };\n}\n\n/**\n * Extract drive ID from a drive URL.\n */\nexport function driveIdFromUrl(url: string): string {\n return url.split(\"/\").pop() ?? \"\";\n}\n","import type {\n ActionSigner,\n ISigner,\n SignatureVerificationHandler,\n} from \"@powerhousedao/shared/document-model\";\nimport type { FactorySpec } from \"../executor/worker/protocol.js\";\n\nexport type { SignatureVerificationHandler };\n\n/** The signer a client signs with and the executor signs synthesized operations with. */\nexport type SignerConfig = {\n /**\n * The signer used to sign actions before submission.\n */\n signer: ISigner;\n\n /**\n * What a pooled executor worker imports to build the same signer, which\n * cannot cross the thread boundary itself. Omitted = pooled workers store\n * synthesized operations unsigned.\n */\n workerSigner?: FactorySpec;\n\n /** Decides which keys may sign as which users; see {@link SignatureTrustPolicy}. */\n trustPolicy?: SignatureTrustPolicy;\n\n /**\n * What a pooled executor worker imports to build the same trust policy.\n * Omitted = pooled workers apply the default.\n */\n workerTrustPolicy?: FactorySpec;\n};\n\n/**\n * The host's answer to whether `key` may sign as `signer.user`. The reactor has\n * already proven that `key` made the signature over this action in this\n * document.\n *\n * Asked once per signed write at admission, never on a re-append, and never for\n * an unsigned action. `false` refuses the write as `SIGNER_UNAUTHORIZED`. A\n * throw is a job error: the job is retried and nothing is dropped, so a\n * transient failure must throw rather than answer `false`.\n *\n * The answer may not depend on when it is asked: replicas admit the same write\n * at different times and must reach the same verdict. Cache an acceptance and\n * never expire it.\n *\n * Key a cache on (`signer.user`, `key`) only. The reactor's own key signing as\n * its current user is accepted before the policy is asked, read live on every\n * ask, and never cached. Invalidate nothing when the local identity changes:\n * other users' verdicts do not depend on it. Without a policy, a signed write\n * is refused under `authEnforcement` and accepted otherwise.\n */\nexport type SignatureTrustPolicy = {\n authorizeSigner(\n signer: ActionSigner,\n key: string,\n documentId: string,\n ): Promise<boolean>;\n};\n\n/** `log` counts refusals and admits anyway; `enforce` refuses. */\nexport type SignatureVerificationMode = \"log\" | \"enforce\";\n\nexport const SIGNATURE_REFUSAL_CODES = [\n \"UNSIGNED_REQUIRED\",\n \"UNSIGNED_IDENTITY\",\n \"KEY_MISMATCH\",\n \"MALFORMED_TUPLE\",\n \"TIMESTAMP_MISMATCH\",\n \"HASH_MISMATCH\",\n \"BAD_SIGNATURE\",\n \"SCHEME_BELOW_POLICY\",\n \"ID_MISMATCH\",\n \"DUPLICATE_ACTION\",\n \"SIGNER_UNAUTHORIZED\",\n] as const;\n\nexport type SignatureRefusalCode = (typeof SIGNATURE_REFUSAL_CODES)[number];\n\n/** Read from the prefix or length of tuple[2]. */\nexport type SignatureScheme =\n | \"unsigned\"\n | \"v2\"\n | \"legacy-renown\"\n | \"legacy-shared\"\n | \"legacy-unknown\";\n\n/** Where this reactor first stores the write. */\nexport type AdmissionPath = \"mutation\" | \"load\";\n\nexport type SignatureVerdict =\n | { ok: true; scheme: SignatureScheme }\n | {\n ok: false;\n scheme: SignatureScheme;\n code: SignatureRefusalCode;\n reason: string;\n };\n","export { createForwardingLogger } from \"./forwarding-logger.js\";\nexport { errorToInfo, sanitizeArg } from \"./sanitize.js\";\n\nfunction resolveWorkerEntryPath(): string {\n const url = new URL(\"./entry.js\", import.meta.url);\n if (url.protocol !== \"file:\") {\n return url.href;\n }\n let pathname = decodeURIComponent(url.pathname);\n if (/^\\/[A-Za-z]:[/\\\\]/.test(pathname)) {\n pathname = pathname.slice(1);\n }\n return pathname;\n}\n\n/** Absolute path to the worker entry script for use with `new Worker(workerEntryPath)`. */\nexport const workerEntryPath = resolveWorkerEntryPath();\n","import type {\n AuthSubject,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type { IDocumentView } from \"../storage/interfaces.js\";\nimport type { IReadGate } from \"./read-gate.js\";\nimport { ALWAYS_READABLE_SCOPES } from \"./read-gate.js\";\n\n/** What a subject may read of a document that is not there to be read. */\nconst META_ONLY = (scope: string): boolean => ALWAYS_READABLE_SCOPES.has(scope);\n\n/**\n * A read gate asked by document id rather than by document.\n *\n * Serving works from an id: an outbox entry names a document, a branch and a\n * scope, and carries no state. Fetching the document is therefore the serving\n * path's own job, and it is the only thing this adds to the gate it wraps.\n */\nexport class SyncScopeGate {\n constructor(\n private readonly gate: IReadGate,\n private readonly documentView: IDocumentView,\n private readonly logger?: ILogger,\n ) {}\n\n /**\n * Which scopes of one document the subject may be served.\n *\n * A document this replica cannot produce yields the metadata scopes and\n * nothing else. That is the fail-closed direction, and it is safe to fail\n * closed here precisely because serving withholds rather than consumes: the\n * entry stays in the outbox and the next poll asks again, so a document that\n * is merely not indexed yet is delayed rather than lost.\n *\n * Any other failure is rethrown. A read side that is down must not read as a\n * silent, universal denial, because a denial that looks like a policy is one\n * nobody investigates.\n */\n async scopePredicateById(\n documentId: string,\n subject: AuthSubject,\n branch: string,\n signal?: AbortSignal,\n ): Promise<(scope: string) => boolean> {\n let document: PHDocument;\n try {\n document = await this.documentView.get(\n documentId,\n { branch },\n undefined,\n signal,\n );\n } catch (error) {\n if (!DocumentNotFoundError.isError(error)) {\n throw error;\n }\n this.logger?.verbose(\n `Withholding ${documentId} on branch ${branch}: the read side does not hold it`,\n );\n return META_ONLY;\n }\n\n return this.gate.scopePredicate(document, subject, branch, signal);\n }\n}\n","import type { Operation } from \"@powerhousedao/shared/document-model\";\nimport {\n garbageCollect,\n sortOperations,\n} from \"@powerhousedao/shared/document-model\";\n\n/** Where a stream's stored order contradicts its timestamps. */\nexport type OutOfOrderPair = {\n previous: Operation;\n current: Operation;\n /**\n * `descending` cannot be walked at all. `tied` walks fine — the intra-stream\n * rule breaks the tie by index — but violates the monotonic auth rule, so a\n * stream holding one can never be replicated to a peer that lacks it.\n */\n kind: \"descending\" | \"tied\";\n};\n\n/**\n * The first pair of effective operations whose stored order contradicts their\n * timestamps, or undefined when the stream is in position order.\n *\n * Such a stream cannot be walked, and the auth stream is never reshuffled once\n * the monotonic rule is on, so run this before enabling enforcement on a fleet.\n *\n * `requireStrict` additionally rejects a tie, which is what the auth stream's\n * monotonic rule requires and what the walk alone does not care about.\n */\nexport function firstOutOfOrderPair(\n operations: Operation[],\n options?: { requireStrict?: boolean },\n): OutOfOrderPair | undefined {\n const requireStrict = options?.requireStrict ?? false;\n const effective = garbageCollect(sortOperations([...operations]));\n\n for (let i = 1; i < effective.length; i++) {\n const previous = effective[i - 1];\n const current = effective[i];\n\n // Parsed rather than compared through comparePositions: one stream makes the\n // cross-stream rules inert, and a tie has to be visible here rather than\n // resolved by index.\n const previousAt = Date.parse(previous.timestampUtcMs);\n const currentAt = Date.parse(current.timestampUtcMs);\n\n if (currentAt < previousAt) {\n return { previous, current, kind: \"descending\" };\n }\n if (requireStrict && currentAt === previousAt) {\n return { previous, current, kind: \"tied\" };\n }\n }\n\n return undefined;\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\n\n/**\n * Generic interface for any read model that can index operations.\n * Implementations include IDocumentView, search indices, caches, etc.\n */\nexport interface IReadModel {\n /**\n * Unique name identifying this read model, used for lookup via getReadModel.\n */\n readonly name: string;\n\n /**\n * Indexes a list of operations into the read model.\n * This method is called asynchronously when operations are written to the operation store.\n *\n * @param operations - The operations with their context to index\n */\n indexOperations(operations: OperationWithContext[]): Promise<void>;\n\n /** Claims a batch as it queues; a sweep then leaves it to the live path. */\n reserveOperations?(operations: OperationWithContext[]): IReadModelReservation;\n}\n\n/** A batch a read model claimed while it waits on the coordinator's chain. */\nexport interface IReadModelReservation {\n /** Indexes the batch; at most once. */\n apply(): Promise<void>;\n /** Frees the claims of a batch that will not be applied. */\n release(): void;\n}\n\n/**\n * Coordinates read model synchronization with operation writes.\n * Listens to operation events from the event bus and updates all registered read models.\n */\nexport interface IReadModelCoordinator {\n /**\n * All registered read models (pre-ready and post-ready).\n */\n readonly readModels: IReadModel[];\n\n /**\n * Start listening for operation events and updating read models.\n */\n start(): void;\n\n /**\n * Stop listening and clean up subscriptions.\n */\n stop(): void;\n\n /**\n * Resolves when every per-queueKey projection chain has flushed.\n * Intended for test fixtures and explicit shutdown.\n */\n drain(): Promise<void>;\n\n /**\n * Current number of in-flight per-queueKey projection chains.\n * Used as a backpressure signal by observability gauges.\n */\n getChainDepth(): number;\n\n /** The models indexed on this thread; catch-up sweeps them. */\n indexedReadModels?(): readonly IReadModel[];\n}\n\nexport type ReadModelRegistrationStage = \"pre_ready\" | \"post_ready\";\n\n/**\n * Optional capability exposed by coordinators that support adding read models\n * after construction. Custom and remote coordinators are not required to\n * implement it.\n */\nexport interface ILiveReadModelCoordinator extends IReadModelCoordinator {\n addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void;\n}\n\nexport function supportsLiveReadModelRegistration(\n coordinator: IReadModelCoordinator,\n): coordinator is ILiveReadModelCoordinator {\n return (\n \"addReadModel\" in coordinator &&\n typeof coordinator.addReadModel === \"function\"\n );\n}\n","import type { IProcessorHostModuleBase } from \"@powerhousedao/shared/processors\";\nimport type { IReactorClient } from \"../client/types.js\";\nimport type { IReadModel } from \"../read-models/interfaces.js\";\nimport type { ReactorReadModels } from \"../read-models/names.js\";\n\n/**\n * Host module fields every reactor host provides: the shared core plus the\n * reactor client and typed read-model lookup. Hosts extend it with what only\n * they can name (the attachment client) as `IProcessorHostModule`.\n */\nexport interface IReactorProcessorHostModuleBase extends IProcessorHostModuleBase {\n client: IReactorClient;\n /**\n * Retrieves a registered read model by name.\n *\n * Reactor-registered names are typed via `ReactorReadModels` — hover a key\n * there for what each model holds:\n * - `\"document-view\"` (materialized document state, `IDocumentView`)\n * - `\"document-indexer\"` (document relationship graph, `IDocumentIndexer`).\n *\n * Other names return the caller-supplied type.\n * Throws if no read model with that name is registered.\n */\n getReadModel<K extends keyof ReactorReadModels>(\n name: K,\n ): ReactorReadModels[K];\n getReadModel<T>(name: string): T;\n}\n\nexport type ReactorHostModuleBaseOptions = Pick<\n IProcessorHostModuleBase,\n \"analyticsStore\" | \"relationalDb\" | \"processorApp\" | \"config\"\n> & {\n client: IReactorClient;\n /** Registered read models, typically `readModelCoordinator.readModels`. */\n readModels: ReadonlyArray<Pick<IReadModel, \"name\">>;\n};\n\n/** Builds the reactor-level host module; hosts spread it and add their own fields. */\nexport function createReactorHostModuleBase(\n options: ReactorHostModuleBaseOptions,\n): IReactorProcessorHostModuleBase {\n const { client, readModels, ...core } = options;\n return {\n ...core,\n client,\n dispatch: {\n async execute(docId, branch, actions, signal) {\n const jobInfo = await client.executeAsync(\n docId,\n branch,\n actions,\n signal,\n );\n return { id: jobInfo.id, status: jobInfo.status };\n },\n },\n getReadModel<T>(name: string): T {\n const model = readModels.find((m) => m.name === name);\n if (!model) {\n throw new Error(`Read model \"${name}\" not found`);\n }\n return model as unknown as T;\n },\n };\n}\n","import type { IKeyframeStore } from \"../storage/interfaces.js\";\n\nexport const passthroughKeyframeStore: IKeyframeStore = {\n putKeyframe: () => Promise.resolve(),\n findNearestKeyframe: () => Promise.resolve(undefined),\n listKeyframes: () => Promise.resolve([]),\n deleteKeyframes: () => Promise.resolve(0),\n};\n","import type { PHDocument } from \"@powerhousedao/shared/document-model\";\nimport { hashDocumentStateForScope } from \"@powerhousedao/shared/document-model\";\nimport { KyselyWriteCache } from \"../cache/kysely-write-cache.js\";\nimport type { IWriteCache } from \"../cache/write/interfaces.js\";\nimport { firstOutOfOrderPair } from \"../decision/stream-order.js\";\nimport type { IDocumentModelRegistry } from \"../registry/interfaces.js\";\nimport { throwIfAborted } from \"../shared/utils.js\";\nimport type {\n IDocumentView,\n IKeyframeStore,\n IOperationStore,\n} from \"../storage/interfaces.js\";\nimport { passthroughKeyframeStore } from \"./passthrough-keyframe-store.js\";\nimport type {\n IDocumentIntegrityService,\n KeyframeValidationIssue,\n RebuildResult,\n SnapshotValidationIssue,\n StreamOrderIssue,\n ValidationResult,\n} from \"./types.js\";\n\nexport class DocumentIntegrityService implements IDocumentIntegrityService {\n private readonly keyframeStore: IKeyframeStore;\n private readonly operationStore: IOperationStore;\n private readonly writeCache: IWriteCache;\n private readonly documentView: IDocumentView;\n private readonly documentModelRegistry: IDocumentModelRegistry;\n\n constructor(\n keyframeStore: IKeyframeStore,\n operationStore: IOperationStore,\n writeCache: IWriteCache,\n documentView: IDocumentView,\n documentModelRegistry: IDocumentModelRegistry,\n ) {\n this.keyframeStore = keyframeStore;\n this.operationStore = operationStore;\n this.writeCache = writeCache;\n this.documentView = documentView;\n this.documentModelRegistry = documentModelRegistry;\n }\n\n async validateDocument(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<ValidationResult> {\n const keyframeIssues: KeyframeValidationIssue[] = [];\n const snapshotIssues: SnapshotValidationIssue[] = [];\n const streamOrderIssues = await this.findStreamOrderIssues(\n documentId,\n branch,\n signal,\n );\n\n const replayCache = new KyselyWriteCache(\n passthroughKeyframeStore,\n this.operationStore,\n this.documentModelRegistry,\n {\n maxDocuments: 1,\n ringBufferSize: 1,\n keyframeInterval: Number.MAX_SAFE_INTEGER,\n },\n );\n\n const keyframes = await this.keyframeStore.listKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n for (const keyframe of keyframes) {\n throwIfAborted(signal);\n\n replayCache.invalidate(documentId, keyframe.scope, branch);\n const replayedDoc = await replayCache.getState(\n documentId,\n keyframe.scope,\n branch,\n keyframe.revision,\n signal,\n );\n\n const kfHash = hashDocumentStateForScope(\n keyframe.document,\n keyframe.scope,\n );\n const replayHash = hashDocumentStateForScope(replayedDoc, keyframe.scope);\n\n if (kfHash !== replayHash) {\n keyframeIssues.push({\n scope: keyframe.scope,\n branch,\n revision: keyframe.revision,\n keyframeHash: kfHash,\n replayedHash: replayHash,\n });\n }\n }\n\n let currentDoc: PHDocument;\n try {\n currentDoc = await this.documentView.get(documentId);\n } catch {\n return {\n documentId,\n isConsistent:\n keyframeIssues.length === 0 && streamOrderIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n streamOrderIssues,\n };\n }\n\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n const allScopes = Object.keys(revisions.revision);\n\n for (const scope of allScopes) {\n if (scope === \"document\") continue;\n\n replayCache.invalidate(documentId, scope, branch);\n\n let replayedDoc: PHDocument;\n try {\n replayedDoc = await replayCache.getState(\n documentId,\n scope,\n branch,\n undefined,\n signal,\n );\n } catch {\n throwIfAborted(signal);\n continue;\n }\n\n const snapshotHash = hashDocumentStateForScope(currentDoc, scope);\n const replayHash = hashDocumentStateForScope(replayedDoc, scope);\n if (snapshotHash !== replayHash) {\n snapshotIssues.push({\n scope,\n branch,\n snapshotHash,\n replayedHash: replayHash,\n });\n }\n }\n\n return {\n documentId,\n isConsistent:\n keyframeIssues.length === 0 &&\n snapshotIssues.length === 0 &&\n streamOrderIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\n streamOrderIssues,\n };\n }\n\n async rebuildKeyframes(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const deleted = await this.keyframeStore.deleteKeyframes(\n documentId,\n undefined,\n branch,\n signal,\n );\n\n return {\n documentId,\n keyframesDeleted: deleted,\n scopesInvalidated: 0,\n };\n }\n\n async rebuildSnapshots(\n documentId: string,\n branch = \"main\",\n signal?: AbortSignal,\n ): Promise<RebuildResult> {\n const scopes = await this.discoverScopes(documentId, branch, signal);\n\n for (const scope of scopes) {\n throwIfAborted(signal);\n this.writeCache.invalidate(documentId, scope, branch);\n }\n\n return {\n documentId,\n keyframesDeleted: 0,\n scopesInvalidated: scopes.length,\n };\n }\n\n private async findStreamOrderIssues(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<StreamOrderIssue[]> {\n const scopes = await this.discoverScopes(documentId, branch, signal);\n const issues: StreamOrderIssue[] = [];\n\n for (const scope of scopes) {\n throwIfAborted(signal);\n\n const stored = await this.operationStore.getSince(\n documentId,\n scope,\n branch,\n -1,\n undefined,\n undefined,\n signal,\n );\n\n const pair = firstOutOfOrderPair(stored.results, {\n requireStrict: scope === \"auth\",\n });\n if (pair !== undefined) {\n issues.push({ scope, branch, ...pair });\n }\n }\n\n return issues;\n }\n\n private async discoverScopes(\n documentId: string,\n branch: string,\n signal?: AbortSignal,\n ): Promise<string[]> {\n const revisions = await this.operationStore.getRevisions(\n documentId,\n branch,\n signal,\n );\n return Object.keys(revisions.revision);\n }\n}\n"],"mappings":"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"}
|