@powerhousedao/reactor 6.1.0-dev.9 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["toErrorInfo","uuidv4","addFileAction","addFolderAction","deleteNodeAction","updateNodeAction","moveNodeAction","copyNodeAction","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/client/reactor-client.ts","../src/queue/types.ts","../src/executor/job-result-handler.ts","../src/executor/types.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/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/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/signer/passthrough-signer.ts","../src/core/reactor-client-builder.ts","../src/shared/drive-url.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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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","/**\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 message and stack trace.\n */\nexport type ErrorInfo = {\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 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\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 replayDocument,\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 { IReactor } from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport type { 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 toVersion: 1,\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 const batchResult = await this.reactor.executeBatch(\n {\n jobs: [\n {\n key: \"document\",\n documentId,\n scope: getSharedActionScope(documentActions),\n branch: \"main\",\n actions: documentActions,\n dependsOn: [],\n },\n {\n key: \"drive\",\n documentId: driveIdentifier,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [\"document\"],\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\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n return this.reactor.get<TDocument>(documentId);\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 const node = updated.state.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 if (!node) {\n throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);\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 = drive.state.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 const module = await this.client.getDocumentModelModule(\n srcDoc.header.documentType,\n );\n const duplicated = replayDocument(\n srcDoc.initialState,\n srcDoc.operations,\n module.reducer,\n createPresignedHeader(entry.targetId, srcDoc.header.documentType),\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 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 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 {\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/**\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 * 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 * 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 * 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 * 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 * @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 updated document\n */\n execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\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 Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { actions } 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 {\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 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 DocumentChangeType,\n type CreateDocumentOptions,\n type DocumentChangeEvent,\n type IDriveClient,\n type IReactorClient,\n} from \"./types.js\";\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\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 ) {\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.drives = new DriveClient(this, logger, reactor, signer);\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 const module = modules.results.find(\n (m) => m.documentModel.global.id === documentType,\n );\n\n if (!module) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n\n return module as DocumentModelModule<any>;\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 return await this.reactor.getByIdOrSlug<TDocument>(\n identifier,\n view,\n undefined,\n signal,\n );\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 if (paging?.cursor && isCompositeCursor(paging.cursor)) {\n return this.getOperationsWithCompositeCursor(\n documentId,\n view,\n filter,\n paging,\n signal,\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 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 ): 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 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.reactor.find(\n { ids: targetIds },\n view,\n paging,\n undefined,\n signal,\n );\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.reactor.find(\n { ids: sourceIds },\n view,\n paging,\n undefined,\n signal,\n );\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 return this.reactor.find(search, view, paging, undefined, signal);\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: document.state.document.version,\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 return await this.reactor.get<TDocument>(documentId);\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 module = matchingModules.find(\n (m) => m.version === options.documentModelVersion,\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 = current.version ?? 0;\n const latestVersion = latest.version ?? 0;\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 = module.version ?? 1;\n\n return this.create<TDocument>(document, options?.parentIdentifier, signal);\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 async execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\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 };\n const result = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n view,\n completedJob.consistencyToken,\n signal,\n );\n return result;\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 result;\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 result;\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: sourceResult,\n target: targetResult,\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 const unsubscribeCreated = this.subscriptionManager.onDocumentCreated(\n (result) => {\n void (async () => {\n try {\n const documents = await Promise.all(\n result.results.map((id) =>\n this.reactor.get(id, view, undefined, undefined),\n ),\n );\n\n callback({\n type: DocumentChangeType.Created,\n documents,\n });\n } catch {\n // Silently ignore errors when fetching created documents\n }\n })();\n },\n search,\n );\n\n const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted(\n (documentIds) => {\n callback({\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 callback({\n type: DocumentChangeType.Updated,\n documents: result.results,\n });\n },\n search,\n view,\n );\n\n const unsubscribeRelationship =\n this.subscriptionManager.onRelationshipChanged(\n (parentId, childId, changeType) => {\n callback({\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 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\nexport type JobKind = \"mutation\" | \"load\";\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 * 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 type { IJobExecutionHandle, Job } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { ModuleNotFoundError } from \"../registry/errors.js\";\nimport {\n DocumentDeletedError,\n DocumentNotFoundError,\n} from \"../shared/errors.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { JobResult } from \"./types.js\";\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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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 // DocumentNotFoundError: defer the job instead of failing immediately.\n // A CREATE_DOCUMENT job may arrive later and unblock it.\n if (result.error && DocumentNotFoundError.isError(result.error)) {\n handle.defer();\n callbacks.deferJob(handle.job.documentId, handle.job);\n return;\n }\n\n if (result.error && DocumentDeletedError.isError(result.error)) {\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 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 message: messageLines.join(\"\\n\"),\n stack: stackLines.join(\"\\n\\n\"),\n };\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * Represents the result of a job execution\n */\nexport type JobResult = {\n /** The job that was executed */\n job: Job;\n\n /** Whether the job executed successfully */\n success: boolean;\n\n /** Error if the job failed */\n error?: Error;\n\n /** The operations generated from the actions (if successful) */\n operations?: Operation[];\n\n /**\n * Operations with context (includes ephemeral resultingState).\n * Used for emitting to IDocumentView via event bus.\n */\n operationsWithContext?: OperationWithContext[];\n\n /** Timestamp when the job execution completed */\n completedAt?: string;\n\n /** Duration of job execution in milliseconds */\n duration?: number;\n\n /** Any additional metadata from the execution */\n metadata?: Record<string, any>;\n};\n\n/**\n * Configuration options for the job executor\n */\nexport type JobExecutorConfig = {\n /** Maximum number of conflicting operations to skip when reshuffling. */\n maxSkipThreshold?: number;\n\n /** Maximum number of concurrent jobs to execute */\n maxConcurrency?: number;\n\n /** Maximum time in milliseconds a job can run before being considered timed out */\n jobTimeoutMs?: number;\n\n /** Base delay in milliseconds for exponential backoff retries */\n retryBaseDelayMs?: number;\n\n /** Maximum delay in milliseconds for exponential backoff retries */\n retryMaxDelayMs?: number;\n\n /** Maximum elapsed milliseconds before yielding to the main thread between actions.\n * Keeps the UI responsive when processing large batches. */\n yieldDeadlineMs?: number;\n};\n\n/**\n * Event types for the job executor\n */\nexport const JobExecutorEventTypes = {\n JOB_STARTED: 20000,\n JOB_COMPLETED: 20001,\n JOB_FAILED: 20002,\n EXECUTOR_STARTED: 20003,\n EXECUTOR_STOPPED: 20004,\n} as const;\n\n/**\n * Event data for job execution events\n */\nexport type JobStartedEvent = {\n job: Job;\n startedAt: string;\n /**\n * Identifier of the executor that took the job. For the worker pool this is\n * the thread-worker id (e.g. \"reactor-worker-3\"); for the in-process simple\n * manager it is \"in-process-<index>\". Optional for backwards compatibility\n * with consumers built before the field was added.\n */\n workerId?: string;\n};\n\nexport type JobCompletedEvent = {\n job: Job;\n result: JobResult;\n /** See {@link JobStartedEvent.workerId}. */\n workerId?: string;\n};\n\nexport type JobFailedEvent = {\n job: Job;\n error: string;\n willRetry: boolean;\n retryCount: number;\n /** See {@link JobStartedEvent.workerId}. */\n workerId?: string;\n};\n\nexport type ExecutorStartedEvent = {\n config: JobExecutorConfig;\n startedAt: string;\n};\n\nexport type ExecutorStoppedEvent = {\n stoppedAt: string;\n graceful: boolean;\n};\n\n/**\n * Status information for the job executor manager\n */\nexport type ExecutorManagerStatus = {\n /** Whether the manager is currently running */\n isRunning: boolean;\n\n /** Number of executor instances managed */\n numExecutors: number;\n\n /** Number of jobs currently being processed */\n activeJobs: number;\n\n /** Total number of jobs processed since start */\n totalJobsProcessed: number;\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 { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { ICollectionMembershipCache } from \"../cache/collection-membership-cache.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 { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type {\n IExecutorWorker,\n IJobExecutor,\n IJobExecutorManager,\n WorkerExecutionOutcome,\n} from \"./interfaces.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\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 * Action types whose application invalidates the parent's collection\n * membership cache. Mirrors the in-process invalidation pattern in\n * `document-action-handler.ts` (the worker pool relocates that work to\n * the parent because the cache lives there).\n */\nconst MEMBERSHIP_INVALIDATING_ACTIONS = new Set([\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n \"UPDATE_RELATIONSHIP\",\n \"DELETE_DOCUMENT\",\n]);\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 * - Owning the authoritative `ICollectionMembershipCache` — workers do\n * not query it. Each result enriches the outgoing `JOB_WRITE_READY`\n * with `collectionMemberships` and invalidates targets named by\n * relationship/delete operations before the lookup.\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 = new Map<string, Job[]>();\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 collectionMembershipCache: ICollectionMembershipCache,\n jobTimeoutMs: number = 30_000,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\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 for (const [, jobs] of this.deferredJobs) {\n for (const job of jobs) {\n const errorInfo = toErrorInfo(\n new DocumentNotFoundError(job.documentId),\n );\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error: new DocumentNotFoundError(job.documentId),\n job,\n })\n .catch(() => {});\n }\n }\n this.deferredJobs.clear();\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) => {\n const existing = this.deferredJobs.get(documentId) ?? [];\n existing.push(job);\n this.deferredJobs.set(documentId, existing);\n },\n flushDeferredFor: (documentId) => this.flushDeferredJobs(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 this.invalidateMembershipsFor(payload.operations);\n\n const documentIds = [\n ...new Set(payload.operations.map((op) => op.context.documentId)),\n ];\n let collectionMemberships: Record<string, string[]> = {};\n try {\n collectionMemberships =\n await this.collectionMembershipCache.getCollectionsForDocuments(\n documentIds,\n );\n } catch (error) {\n this.logger.error(\n \"Failed to load collection memberships for JOB_WRITE_READY: @Error\",\n error,\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 private invalidateMembershipsFor(operations: OperationWithContext[]): void {\n for (const op of operations) {\n const actionType = op.operation.action.type;\n if (!MEMBERSHIP_INVALIDATING_ACTIONS.has(actionType)) {\n continue;\n }\n const target = extractMembershipTarget(op);\n if (target) {\n this.collectionMembershipCache.invalidate(target);\n }\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 private async flushDeferredJobs(documentId: string): Promise<void> {\n const jobs = this.deferredJobs.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.deferredJobs.delete(documentId);\n\n for (const job of jobs) {\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\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\nfunction extractMembershipTarget(op: OperationWithContext): string | undefined {\n const actionType = op.operation.action.type;\n const input = op.operation.action.input as\n | { targetId?: string; documentId?: string }\n | undefined;\n if (\n actionType === \"ADD_RELATIONSHIP\" ||\n actionType === \"REMOVE_RELATIONSHIP\" ||\n actionType === \"UPDATE_RELATIONSHIP\"\n ) {\n return input?.targetId;\n }\n if (actionType === \"DELETE_DOCUMENT\") {\n return input?.documentId ?? op.context.documentId;\n }\n return undefined;\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, Job } from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type { IJobExecutor, IJobExecutorManager } from \"./interfaces.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\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 = new Map<string, Job[]>();\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 ) {\n this.jobTimeoutMs = jobTimeoutMs;\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 for (const [, jobs] of this.deferredJobs) {\n for (const job of jobs) {\n const errorInfo = toErrorInfo(\n new DocumentNotFoundError(job.documentId),\n );\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error: new DocumentNotFoundError(job.documentId),\n job,\n })\n .catch(() => {});\n }\n }\n this.deferredJobs.clear();\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) => {\n const existing = this.deferredJobs.get(documentId) ?? [];\n existing.push(job);\n this.deferredJobs.set(documentId, existing);\n },\n flushDeferredFor: (documentId) => this.flushDeferredJobs(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 private async flushDeferredJobs(documentId: string): Promise<void> {\n const jobs = this.deferredJobs.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.deferredJobs.delete(documentId);\n\n for (const job of jobs) {\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","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 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 status: JobStatus.RUNNING,\n createdAtUtcIso: new Date().toISOString(),\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n // Update existing job\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 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 { ProcessorFilter } 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","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} from \"./utils.js\";\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\n constructor(\n db: Kysely<DocumentViewDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n ) {\n super(db, operationIndex, writeCache, consistencyTracker, {\n readModelId: \"processor-manager\",\n rebuildStateOnInit: true,\n });\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\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\n for (let i = 0; i < records.length; i++) {\n const record = records[i]!;\n const processorId = `${identifier}:${driveId}:${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 // Clean up orphaned cursor rows from previous runs with more processors\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", identifier)\n .where(\"driveId\", \"=\", driveId)\n .where(\"processorIndex\", \">=\", records.length)\n .execute();\n\n for (const [id, row] of this.cursorCache) {\n if (\n row.factoryId === identifier &&\n row.driveId === driveId &&\n row.processorIndex >= records.length\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 maxOrdinal = Math.max(\n ...page.results.map((op) => op.context.ordinal),\n );\n tracked.lastOrdinal = maxOrdinal;\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 const maxOrdinal = Math.max(...operations.map((op) => op.context.ordinal));\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 } 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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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 // Throw error if queue is blocked\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 // Add job to the end of the queue (FIFO)\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 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 // Mark this job as executing for its document\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 // Mark this job as executing for its document\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 // Clear all queues\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 // update the job lastError and errorHistory\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 // Track as completed so dependent jobs are unblocked\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 this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId,\n error: new Error(error?.message ?? \"Job failed\"),\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(jobId: string, error?: ErrorInfo): 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 // update the job lastError\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 // Update retry count\n const updatedJob: Job = {\n ...job,\n retryCount: (job.retryCount || 0) + 1,\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","import 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\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 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 const module = await this.loader.load(documentType);\n const [result] = this.registry.registerModules(module);\n if (\n result.status === \"error\" &&\n !DuplicateModuleError.isError(result.error)\n ) {\n throw result.error as Error;\n }\n await this.broadcastIfPossible(documentType);\n } catch (error) {\n this.failedModelTypes.add(documentType);\n throw error;\n } finally {\n this.loadingModels.delete(documentType);\n }\n })();\n\n this.loadingModels.set(documentType, loadPromise);\n return loadPromise;\n }\n\n private async broadcastIfPossible(documentType: string): Promise<void> {\n if (!this.broadcastHook || !this.loader.resolveSpec) {\n return;\n }\n const entry = await this.loader.resolveSpec(documentType);\n if (!entry) {\n return;\n }\n await this.broadcastHook(entry);\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 type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\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\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\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};\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: string;\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};\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 // update latest ordinal\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 // update latest ordinal\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 } 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 constructor(\n message: string,\n category: GraphQLRequestErrorCategory,\n statusCode?: number,\n ) {\n super(message);\n this.name = \"GraphQLRequestError\";\n this.category = category;\n this.statusCode = statusCode;\n }\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 constructor(source: ChannelErrorSource, error: Error) {\n super(`ChannelError[${source}]: ${error.message}`);\n this.name = \"ChannelError\";\n this.source = source;\n this.error = error;\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 { 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} 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 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 */\nexport function consolidateSyncOperations(\n syncOps: SyncOperation[],\n): SyncOperation[] {\n if (syncOps.length <= 1) {\n return syncOps;\n }\n\n type GroupKey = string;\n const groups = new Map<\n GroupKey,\n { ops: SyncOperation[]; canonicalJobId: string }\n >();\n const jobIdRemap = new Map<string, string>();\n const insertionOrder: GroupKey[] = [];\n\n for (const syncOp of syncOps) {\n const key: GroupKey = `${syncOp.documentId}|${syncOp.scopes.slice().sort().join(\",\")}|${syncOp.branch}`;\n\n const existing = groups.get(key);\n if (existing) {\n existing.ops.push(syncOp);\n if (syncOp.jobId && syncOp.jobId !== existing.canonicalJobId) {\n jobIdRemap.set(syncOp.jobId, existing.canonicalJobId);\n }\n } else {\n groups.set(key, { ops: [syncOp], canonicalJobId: syncOp.jobId });\n insertionOrder.push(key);\n }\n }\n\n const result: SyncOperation[] = [];\n\n for (const key of insertionOrder) {\n const group = groups.get(key)!;\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","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 {\n Action,\n Operation,\n Signature,\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, converting signature tuples to strings.\n */\nexport function serializeAction(action: Action): unknown {\n const signer = action.context?.signer;\n if (!signer?.signatures) {\n return action;\n }\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n ...signer,\n signatures: signer.signatures.map((sig: Signature | string) =>\n Array.isArray(sig) ? sig.join(\", \") : sig,\n ),\n },\n },\n };\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 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 * Deserializes a signature from a comma-separated string back to a tuple.\n *\n * GraphQL serializes Signature tuples as comma-separated strings for transport.\n * This function converts them back to the expected [string, string, string, string, string] format.\n */\nfunction deserializeSignature(sig: Signature | string): Signature {\n if (Array.isArray(sig)) {\n return sig;\n }\n return sig.split(\", \") as Signature;\n}\n\n/**\n * Deserializes signatures in an operation's signer context from strings back to tuples.\n *\n * When operations are transported via GraphQL, signatures are serialized as comma-separated\n * strings. This function restores them to the Signature tuple format required for verification.\n */\nfunction deserializeOperationSignatures(\n opWithContext: OperationWithContext,\n): OperationWithContext {\n const signer = opWithContext.operation.action.context?.signer;\n if (!signer?.signatures || signer.signatures.length === 0) {\n return opWithContext;\n }\n\n const deserializedSignatures = signer.signatures.map(deserializeSignature);\n\n const deserializedOperation: Operation = {\n ...opWithContext.operation,\n action: {\n ...opWithContext.operation.action,\n context: {\n ...opWithContext.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(\n deserializeOperationSignatures,\n );\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(\n deserializeOperationSignatures,\n );\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 { IOperationIndex } from \"../../cache/operation-index-types.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport { BufferedMailbox } from \"../buffered-mailbox.js\";\nimport { ChannelError, GraphQLRequestError } 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} 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 ID to synchronize */\n collectionId: string;\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 * 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 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 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 };\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 /**\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 (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 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 syncOp.failed(\n new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error)),\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.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 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) return;\n\n void this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n this.logger.info(\n \"GqlChannel @ChannelId re-registered successfully\",\n this.channelId,\n );\n this.failureCount = 0;\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\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.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 * 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 jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n }> {\n const query = `\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 id\n action {\n id\n type\n timestampUtcMs\n input\n scope\n attachments {\n data\n mimeType\n hash\n extension\n fileName\n }\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 jobId\n branch\n scopes\n operationCount\n }\n hasMore\n }\n }\n `;\n\n const variables = {\n channelId: this.channelId,\n outboxAck: ackOrdinal,\n outboxLatest: latestOrdinal,\n };\n\n const response = await this.executeGraphQL<{\n pollSyncEnvelopes: {\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters?: Array<{\n documentId: string;\n error: string;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n };\n }>(query, variables);\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 * 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,\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,\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 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.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 return \"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 }>;\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 );\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 { IOperationIndex } 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: string,\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 { ConnectionState, ConnectionStateSnapshot } from \"../types.js\";\nimport { getLatestAppliedOrdinal } from \"./utils.js\";\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 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 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 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: 0,\n lastFailureUtcMs: 0,\n pushBlocked: false,\n pushFailureCount: 0,\n receivingPages: 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 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 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.transitionConnectionState(\"connected\");\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 { ChannelErrorSource } from \"../../sync/types.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 };\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 };\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\")\n .distinct()\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map((row) => row.document_id);\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\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: 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: { sinceTimestampUtcMs: \"0\" },\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\nfunction remoteRecordToRow(remote: RemoteRecord): InsertableSyncRemote {\n return {\n name: remote.name,\n collection_id: remote.collectionId,\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 ? remote.filter.documentId : null,\n filter_scopes: remote.filter.scope.length > 0 ? remote.filter.scope : 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 };\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(\n op.context.branch,\n input.sourceId,\n );\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 { IOperationIndex } 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 { JobStatus, type ShutdownStatus } 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 { ChannelError } 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 { ChannelErrorSource, SyncEventTypes } from \"./types.js\";\nimport {\n batchOperationsByDocument,\n chunkSyncOperations,\n createIdleHealth,\n filterOperations,\n splitTrailingSameTimestampRun,\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\nenum OutboxMode {\n Backfill = \"backfill\",\n BatchTriggered = \"batch-triggered\",\n}\n\nconst defaultSyncManagerConfig: SyncManagerConfig = {\n maxDeadLettersPerRemote: 100,\n maxInboxBatchSize: 32,\n};\n\nconst PLAN_KEY_TO_JOB_UUID_CAP = 10000;\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 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 id: record.id,\n name: record.name,\n collectionId: record.collectionId,\n filter: record.filter,\n options: record.options,\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 this.remotes.delete(record.name);\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.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(record.name);\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.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.id === id) {\n return remote;\n }\n }\n throw new Error(`Remote with id '${id}' does not exist`);\n }\n\n async add(\n name: string,\n collectionId: string,\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,\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 id: remoteId,\n name,\n collectionId,\n filter,\n options,\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 this.remotes.delete(name);\n await this.remoteStorage.remove(name);\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.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(name);\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 // cancel any in-flight backfill for this remote\n const backfillController = this.backfillAbortControllers.get(name);\n if (backfillController) {\n backfillController.abort();\n this.backfillAbortControllers.delete(name);\n }\n\n // shutdown the channel\n await remote.channel.shutdown();\n\n // delete the remote's data\n await this.remoteStorage.remove(name);\n await this.cursorStorage.remove(name);\n\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.remotes.delete(name);\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.name, remote.channel);\n\n const unsubscribe = remote.channel.onConnectionStateChange((snapshot) => {\n void this.eventBus\n .emit(SyncEventTypes.CONNECTION_STATE_CHANGED, {\n remoteName: remote.name,\n remoteId: remote.id,\n previous: snapshot.state,\n current: snapshot.state,\n snapshot,\n } satisfies ConnectionStateChangedEvent)\n .catch(() => {});\n });\n this.connectionStateUnsubscribes.set(remote.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.name,\n syncOp.documentId,\n syncOp.jobId,\n syncOp.error?.message ?? \"unknown\",\n syncOp.jobDependencies,\n );\n\n this.quarantinedDocumentIds.add(syncOp.documentId);\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 };\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 } 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.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.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 syncOp.failed(\n new ChannelError(record.errorSource, new Error(record.errorMessage)),\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.name,\n );\n }\n\n private getRemotesForCollection(collectionId: string): Remote[] {\n return Array.from(this.remotes.values()).filter(\n (remote) => remote.collectionId === collectionId,\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 await this.updateOutbox(\n remote,\n remote.channel.outbox.latestOrdinal,\n OutboxMode.BatchTriggered,\n );\n }\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.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.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.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.name,\n syncOp.documentId,\n completedJobInfo.id,\n errorMessage,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(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.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.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 const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n const channelError = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n }\n\n private async updateOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode = OutboxMode.Backfill,\n signal?: AbortSignal,\n ): Promise<void> {\n const composedSignal = signal\n ? AbortSignal.any([signal, this.abortController.signal])\n : this.abortController.signal;\n\n let maxOrdinal = ackOrdinal;\n const lastJobByDoc = new Map<string, string>();\n let prevChainJobId: string | undefined;\n const sinceTimestamp = remote.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.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 };\n\n let page = await this.operationIndex.find(\n remote.collectionId,\n ackOrdinal,\n { excludeSourceRemote: remote.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.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 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 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, SyncModule } 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 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 ): SyncModule {\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 { IEventBus } from \"../events/interfaces.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 { 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 * @param request - Batch mutation 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 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 * Container for all sync manager dependencies created during the build process.\n */\nexport interface SyncModule {\n remoteStorage: ISyncRemoteStorage;\n cursorStorage: ISyncCursorStorage;\n deadLetterStorage: ISyncDeadLetterStorage;\n channelFactory: IChannelFactory;\n syncManager: ISyncManager;\n}\n\n/**\n * Container for all reactor dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface ReactorModule {\n eventBus: IEventBus;\n documentModelRegistry: IDocumentModelRegistry;\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 syncModule: SyncModule | undefined;\n reactor: IReactor;\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\n/**\n * Container for all reactor client dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface 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: ReactorModule | undefined;\n}\n","import type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} 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 toVersion: document.state.document.version,\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 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 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 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 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 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 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 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 DocumentModelModule,\n UpgradeManifest,\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 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 type { IEventBus } from \"../events/interfaces.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\";\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 { IDocumentModelLoader } 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 { 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 { Reactor } from \"./reactor.js\";\nimport type {\n Database,\n IReactor,\n ReactorFeatures,\n ReactorModule,\n SyncModule,\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 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 * Describes a document-model package the worker should import at runtime.\n * Either a bare-specifier package or an absolute file path.\n */\nexport type DocumentModelSpecInput =\n | { packageName: string; version: string }\n | { filePath: string };\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 preReadyKinds: BuiltInReadModelKind[];\n postReadyKinds: BuiltInReadModelKind[];\n poolSize?: number;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\nexport class ReactorBuilder {\n private logger?: ILogger;\n private documentModels: DocumentModelModule<any>[] = [];\n private upgradeManifests: UpgradeManifest<readonly number[]>[] = [];\n private features: ReactorFeatures = { legacyStorageEnabled: false };\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 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 documentModelSpecs: DocumentModelSpecInput[] = [];\n private workerPoolConfig?: WorkerPoolConfig;\n private resolvedModelManifest?: ModelManifestEntry[];\n private workerDbConfig?: DbConfig;\n private workerSignatureVerifierSpec?: SignatureVerifierSpec;\n private workerFactory?: WorkerFactory;\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 withDocumentModels(models: DocumentModelModule<any>[]): this {\n this.documentModels = models;\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 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 withDocumentModelSpecs(specs: DocumentModelSpecInput[]): this {\n this.documentModelSpecs = specs;\n return this;\n }\n\n /**\n * Stores the worker-pool configuration. When `config.enabled === true` the\n * builder constructs a {@link WorkerPoolJobExecutorManager} in place of the\n * in-process {@link SimpleJobExecutorManager}.\n */\n withWorkerPool(config: WorkerPoolConfig): this {\n this.workerPoolConfig = config;\n return this;\n }\n\n /**\n * Postgres connection info forwarded to each worker so it can open its own\n * pool. Required when `workerPool.enabled === true` unless a custom\n * `withWorkerFactory` or `withExecutor` is provided.\n */\n withWorkerDbConfig(db: DbConfig): this {\n this.workerDbConfig = db;\n return this;\n }\n\n /**\n * Factory spec the worker imports to instantiate its signature verifier.\n * Required when `workerPool.enabled === true` unless a custom\n * `withWorkerFactory` or `withExecutor` is provided.\n */\n withWorkerSignatureVerifierSpec(spec: SignatureVerifierSpec): this {\n this.workerSignatureVerifierSpec = spec;\n return this;\n }\n\n /**\n * Inject a custom {@link WorkerFactory}. When set, the builder skips\n * default thread-transport wiring and hands the factory directly to the\n * pool manager. Use this in tests or to plug in a different transport\n * (e.g. a child-process adapter).\n */\n withWorkerFactory(factory: WorkerFactory): this {\n this.workerFactory = factory;\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}. The same `DbConfig` registered via\n * {@link withWorkerDbConfig} is reused for the projection workers'\n * connection info; only the `poolSize` is overridden by\n * {@link ProjectionShardBuilderConfig.poolSize}.\n *\n * Requires {@link withWorkerDbConfig} (the projection workers need\n * connection info to open their own pools). The same model manifest\n * resolved from {@link withDocumentModelSpecs} is 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<ReactorModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor\"]);\n }\n\n if (this.workerPoolConfig?.enabled) {\n if (this.documentModels.length > 0) {\n throw new Error(\n \"workerPool.enabled requires withDocumentModelSpecs; remove withDocumentModels() in worker-pool mode.\",\n );\n }\n if (this.documentModelSpecs.length === 0) {\n throw new Error(\n \"workerPool.enabled requires at least one spec registered via withDocumentModelSpecs.\",\n );\n }\n const needsDefaultFactory = !this.executorManager && !this.workerFactory;\n if (needsDefaultFactory && !this.workerDbConfig) {\n throw new Error(\n \"workerPool.enabled requires withWorkerDbConfig (or a custom withWorkerFactory / withExecutor).\",\n );\n }\n if (needsDefaultFactory && !this.workerSignatureVerifierSpec) {\n throw new Error(\n \"workerPool.enabled requires withWorkerSignatureVerifierSpec (or a custom withWorkerFactory / withExecutor).\",\n );\n }\n }\n\n if (this.documentModelSpecs.length > 0) {\n this.resolvedModelManifest = this.documentModelSpecs.map((input) => {\n if (\"filePath\" in input) {\n const entry: ModelManifestEntry = {\n documentType: \"<unresolved>\",\n version: \"<unresolved>\",\n spec: {\n module: { filePath: input.filePath, exportName: \"documentModel\" },\n },\n };\n return entry;\n }\n const entry: ModelManifestEntry = {\n documentType: \"<unresolved>\",\n version: input.version,\n spec: {\n module: {\n packageName: input.packageName,\n exportName: \"documentModel\",\n },\n },\n };\n return entry;\n });\n }\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 (this.documentModels.length > 0) {\n const results = documentModelRegistry.registerModules(\n ...this.documentModels,\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 baseDatabase =\n this.kyselyInstance ??\n (this.workerPoolConfig?.enabled && this.workerDbConfig\n ? await this.createPostgresDatabase(this.workerDbConfig)\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 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.workerPoolConfig?.enabled) {\n const factory =\n this.workerFactory ??\n (await this.createDefaultWorkerFactory(this.workerPoolConfig));\n const poolManager = new WorkerPoolJobExecutorManager(\n factory,\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n collectionMembershipCache,\n this.executorConfig.jobTimeoutMs,\n );\n executorManager = poolManager;\n executorStartCount = this.workerPoolConfig.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 );\n }\n }\n\n await executorManager.start(executorStartCount);\n\n const readModelInstances: IReadModel[] = Array.from(\n new Set([...this.readModels]),\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 );\n\n try {\n await documentView.init();\n } catch (error) {\n console.error(\"Error initializing document view\", error);\n }\n\n readModelInstances.push(documentView);\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 console.error(\"Error initializing document indexer\", error);\n }\n\n readModelInstances.push(documentIndexer);\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 );\n\n try {\n await processorManager.init();\n } catch (error) {\n console.error(\"Error initializing processor manager\", error);\n }\n\n for (const factory of this.readModelFactories) {\n const readModel = await factory({\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n });\n readModelInstances.push(readModel);\n }\n\n const readModelCoordinator = this.readModelCoordinator\n ? this.readModelCoordinator\n : this.projectionShardConfig\n ? await this.createProjectionShardManager(\n this.projectionShardConfig,\n eventBus,\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: SyncModule | 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 const module: ReactorModule = {\n eventBus,\n documentModelRegistry,\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 pools: this.instrumentedPools,\n };\n\n if (this.signalHandlersEnabled) {\n this.attachSignalHandlers(module);\n }\n\n return module;\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 private async createProjectionShardManager(\n config: ProjectionShardBuilderConfig,\n eventBus: IEventBus,\n ): Promise<IReadModelCoordinator> {\n if (!this.workerDbConfig) {\n throw new Error(\n \"withProjectionShards requires withWorkerDbConfig; projection workers need connection info to open their own pools.\",\n );\n }\n const models = this.resolvedModelManifest ?? [];\n const db: DbConfig = {\n ...this.workerDbConfig,\n poolSize: config.poolSize ?? this.workerDbConfig.poolSize,\n 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 });\n await manager.startup();\n this.shutdownHooks.push(() => manager.shutdown());\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 `workerPool.enabled` is set and\n * the caller did not inject `withWorkerFactory`. Each worker spawns a real\n * `node:worker_threads` Worker pointing at the compiled `worker/entry.js`.\n */\n private async createDefaultWorkerFactory(\n poolConfig: WorkerPoolConfig,\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 const db = this.workerDbConfig!;\n const signatureVerifier = this.workerSignatureVerifierSpec!;\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 },\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: ReactorModule): 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 { 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 { ReactorClient } from \"../client/reactor-client.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 { IReactor, ReactorClientModule, ReactorModule } 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\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 public async build(): Promise<ReactorClient> {\n const module = await this.buildModule();\n return module.client;\n }\n\n public async buildModule(): Promise<ReactorClientModule> {\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: ReactorModule | 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 client = new ReactorClient(\n this.logger,\n reactor,\n signer,\n subscriptionManager,\n jobAwaiter,\n documentIndexer,\n documentView,\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 { 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 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 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\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: keyframeIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\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: keyframeIssues.length === 0 && snapshotIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\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 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":";;;;;;;;;;;;;;;;AAmBA,SAAgB,qBAAqB,OAA0C;AAC7E,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;AAMH,SAAgB,sBACd,OACQ;AACR,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;AAMH,SAAgB,qBAAqB,YAA4B;CAC/D,MAAM,QAAmC,EACvC,YACD;AAED,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;;;;;AAUH,SAAgB,sBACd,UACA,UACA,kBACA,UACQ;CACR,MAAM,QAAoC;EACxC;EACA;EACA;EACA,GAAI,aAAa,KAAA,IAAY,EAAE,UAAU,GAAG,EAAE;EAC/C;AAED,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;;AAOH,SAAgB,yBACd,UACA,UACA,kBACA,UACQ;CACR,MAAM,QAAuC;EAC3C;EACA;EACA;EACA;EACD;AAED,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;AAMH,SAAgB,yBACd,UACA,UACA,mBAA2B,SACnB;CACR,MAAM,QAAuC;EAC3C;EACA;EACA;EACD;AAED,QAAO;EACL,IAAI,YAAY;EAChB,MAAM;EACN,OAAO;EACP,iCAAgB,IAAI,MAAM,EAAC,aAAa;EACxC;EACD;;;;;;;;AC9EH,SAAgB,uBAAuB,MAAiC;CACtE,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,KAAK,IAAI,IAAI,IAAI,CACnB,OAAM,IAAI,MAAM,uBAAuB,IAAI,MAAM;AAEnD,OAAK,IAAI,IAAI,IAAI;;AAEnB,MAAK,MAAM,OAAO,KAChB,MAAK,MAAM,UAAU,IAAI,UACvB,KAAI,CAAC,KAAK,IAAI,OAAO,CACnB,OAAM,IAAI,MACR,QAAQ,IAAI,IAAI,iCAAiC,SAClD;CAIP,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,2BAAW,IAAI,KAAa;CAClC,MAAM,eAAe,QAAyB;AAC5C,UAAQ,IAAI,IAAI;AAChB,WAAS,IAAI,IAAI;EACjB,MAAM,MAAM,KAAK,MAAM,MAAM,EAAE,QAAQ,IAAI;AAC3C,MAAI;QACG,MAAM,UAAU,IAAI,UACvB,KAAI,CAAC,QAAQ,IAAI,OAAO;QAClB,YAAY,OAAO,CACrB,QAAO;cAEA,SAAS,IAAI,OAAO,CAC7B,QAAO;;AAIb,WAAS,OAAO,IAAI;AACpB,SAAO;;AAET,MAAK,MAAM,OAAO,KAChB,KAAI,CAAC,QAAQ,IAAI,IAAI,IAAI;MACnB,YAAY,IAAI,IAAI,CACtB,OAAM,IAAI,MAAM,4CAA4C,IAAI,MAAM;;;;;;AAS9E,SAAgB,qBAAqB,MAAoC;AACvE,wBAAuB,KAAK;AAC5B,MAAK,MAAM,OAAO,KAChB,KAAI,IAAI,QAAQ,WAAW,EACzB,OAAM,IAAI,MAAM,QAAQ,IAAI,IAAI,2BAA2B;;;;;AAQjE,SAAgB,yBACd,MACM;AACN,wBAAuB,KAAK;AAC5B,MAAK,MAAM,OAAO,KAChB,KAAI,IAAI,WAAW,WAAW,EAC5B,OAAM,IAAI,MAAM,QAAQ,IAAI,IAAI,8BAA8B;;;;;AAQpE,SAAgB,qBAAqB,KAA6B;AAChE,MAAK,MAAM,UAAU,IAAI,SAAS;EAChC,MAAM,cAAc,OAAO,SAAS;AACpC,MAAI,gBAAgB,IAAI,MACtB,OAAM,IAAI,MACR,QAAQ,IAAI,IAAI,oBAAoB,IAAI,MAAM,0BAA0B,YAAY,GACrF;;;;;;AAQP,SAAgB,wBAAwB,KAAiC;AACvE,MAAK,MAAM,aAAa,IAAI,YAAY;EACtC,MAAM,iBAAiB,UAAU,OAAO,SAAS;AACjD,MAAI,mBAAmB,IAAI,MACzB,OAAM,IAAI,MACR,QAAQ,IAAI,IAAI,oBAAoB,IAAI,MAAM,6BAA6B,eAAe,GAC3F;;;;;;AAQP,SAAgB,gBAAgB,MAAqC;CACnE,MAAM,SAAmB,EAAE;CAC3B,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,SAAS,QAAsB;AACnC,MAAI,QAAQ,IAAI,IAAI,CAClB;AAEF,UAAQ,IAAI,IAAI;EAChB,MAAM,MAAM,KAAK,MAAM,MAAM,EAAE,QAAQ,IAAI;AAC3C,MAAI,IACF,MAAK,MAAM,UAAU,IAAI,UACvB,OAAM,OAAO;AAGjB,SAAO,KAAK,IAAI;;AAElB,MAAK,MAAM,OAAO,KAChB,OAAM,IAAI,IAAI;AAEhB,QAAO;;;;;AAMT,SAAgBA,cAAY,OAAkC;AAC5D,KAAI,iBAAiB,MACnB,QAAO;EACL,SAAS,MAAM;EACf,OAAO,MAAM,0BAAS,IAAI,OAAO,EAAC,SAAS;EAC5C;AAEH,QAAO;EACL,SAAS;EACT,wBAAO,IAAI,OAAO,EAAC,SAAS;EAC7B;;;;;AAMH,SAAgB,aACd,SACA,MAC0B;AAQ1B,QAAO;EACL,SAPwB,QAAQ,QAAQ,QACvC,aAAa,SAAS,OAAO,iBAAiB,KAChD;EAMC,SAAS,QAAQ;EACjB,YAAY,QAAQ;EACpB,MAAM,QAAQ,OACV,YAAY;AAGV,UAAO,aADa,MAAM,QAAQ,MAAO,EACR,KAAK;MAExC,KAAA;EACL;;;;;;AAOH,SAAgB,wBAAwB,YAAiC;AACvE,KAAI,WAAW,WAAW,EACxB,OAAM,IAAI,MAAM,yBAAyB;CAG3C,MAAM,YAAY,WAAW,GAAG,OAAO;AACvC,MAAK,MAAM,CAAC,OAAO,cAAc,WAAW,SAAS,EAAE;EACrD,MAAM,QAAQ,UAAU,OAAO;AAC/B,MAAI,UAAU,UACZ,OAAM,IAAI,MACR,+DAA+D,UAAU,eAAe,MAAM,gBAAgB,QAC/G;;AAIL,QAAO;;;;;;AAOT,SAAgB,qBAAqB,SAA2B;AAC9D,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,sBAAsB;CAGxC,MAAM,YAAY,QAAQ,GAAG;AAC7B,MAAK,MAAM,UAAU,QACnB,KAAI,OAAO,UAAU,UACnB,OAAM,IAAI,MACR,oDAAoD,UAAU,eAAe,OAAO,MAAM,GAC3F;AAIL,QAAO;;;;;;AAOT,MAAa,aAAa,OACxB,QACA,QACA,WACoB;CACpB,MAAM,qBAAqB,OAAO,SAAS,QAAQ;AACnD,KAAI,sBAAsB,mBAAmB,SAAS,EACpD,QAAO;CAGT,MAAM,YAAuB,MAAM,OAAO,WAAW,QAAQ,OAAO;AAEpE,QAAO;EACL,GAAG;EACH,SAAS;GACP,GAAG,OAAO;GACV,QAAQ;IACN,MAAM;KACJ,SAAS,OAAO,MAAM,WAAW;KACjC,WAAW,OAAO,MAAM,aAAa;KACrC,SAAS,OAAO,MAAM,WAAW;KAClC;IACD,KAAK;KACH,MAAM,OAAO,KAAK,QAAQ;KAC1B,KAAK,OAAO,KAAK,OAAO;KACzB;IACD,YAAY,CAAC,UAAU;IACxB;GACF;EACF;;;;;AAMH,MAAa,cAAc,OACzB,SACA,QACA,WACsB;AACtB,QAAO,QAAQ,IACb,QAAQ,KAAK,WAAW,WAAW,QAAQ,QAAQ,OAAO,CAAC,CAC5D;;AAGH,SAAgB,mBACd,OACA,YACS;AACT,QAAO;EAAE,GAAG;EAAY,SAASC,IAAQ;EAAE,aAAa,CAAC,MAAM;EAAE;;;;;;;AC1RnE,IAAY,kBAAL,yBAAA,iBAAA;AACL,iBAAA,UAAA;AACA,iBAAA,aAAA;;KACD;;;;AAKD,IAAY,yBAAL,yBAAA,wBAAA;AACL,wBAAA,WAAA;AACA,wBAAA,aAAA;;KACD;;;;AAkDD,IAAY,YAAL,yBAAA,WAAA;;AAEL,WAAA,aAAA;;AAEA,WAAA,aAAA;;AAEA,WAAA,iBAAA;;AAEA,WAAA,gBAAA;;AAEA,WAAA,YAAA;;KACD;;;;;;;;;;;AC/DD,IAAa,cAAb,MAAiD;CAC/C,YACE,QACA,QACA,SACA,QACA;AAJiB,OAAA,SAAA;AACA,OAAA,SAAA;AACA,OAAA,UAAA;AACA,OAAA,SAAA;;CAGnB,MAAM,OACJ,OACA,QACgC;AAChC,OAAK,OAAO,QAAQ,yBAAyB,MAAM;EACnD,MAAM,WAAW,oBAAoB,EACnC,QAAQ;GACN,MAAM,MAAM,OAAO,QAAQ;GAC3B,MAAM,MAAM,OAAO,QAAQ;GAC3B,OAAO,EAAE;GACV,EACF,CAAC;AACF,MAAI,MAAM,gBACR,UAAS,OAAO,OAAO;GACrB,GAAG,SAAS,OAAO;GACnB,iBAAiB,MAAM;GACxB;AAEH,SAAO,KAAK,OAAO,OACjB,UACA,KAAA,GACA,OACD;;CAGH,MAAM,QACJ,iBACA,UACA,cACA,QACoB;AACpB,OAAK,OAAO,QACV,8DACA,iBACA,SAAS,OAAO,IAChB,aACD;EAED,MAAM,aAAa,SAAS,OAAO;EAsBnC,MAAM,kBAA4B,MAAM,YACtC;GACE,qBAtB2C;IAC7C,OAAO,SAAS,OAAO;IACvB,SAAS;IACT,YAAY,SAAS,OAAO;IAC5B,SAAS;KACP,WAAW,SAAS,OAAO;KAC3B,WAAW,SAAS,OAAO,IAAI;KAC/B,OAAO,SAAS,OAAO,IAAI;KAC3B,iBAAiB,SAAS,OAAO;KACjC,cAAc,SAAS,OAAO;KAC/B;IACD,MAAM,SAAS,OAAO;IACtB,MAAM,SAAS,OAAO;IACtB,QAAQ,SAAS,OAAO;IACxB,MAAM,SAAS,OAAO;IACtB,kBAAkB,SAAS,OAAO,oBAAoB,EACpD,gBAAgB,GACjB;IACF,CAIoC;GACjC,sBAAsB;IACpB,YAAY,SAAS,OAAO;IAC5B,OAAO,SAAS,OAAO;IACvB,aAAa;IACb,WAAW;IACX,cAAc,SAAS;IACxB,CAAC;GACF,sBAAsB,iBAAiB,YAAY,QAAQ;GAC5D,EACD,KAAK,QACL,OACD;EAED,MAAM,eAAyB,MAAM,YACnC,CACEC,QAAc;GACZ,IAAI;GACJ,MAAM,SAAS,OAAO,QAAQ;GAC9B,cAAc,SAAS,OAAO;GAC9B;GACD,CAAC,CACH,EACD,KAAK,QACL,OACD;EAED,MAAM,cAAc,MAAM,KAAK,QAAQ,aACrC,EACE,MAAM,CACJ;GACE,KAAK;GACL;GACA,OAAO,qBAAqB,gBAAgB;GAC5C,QAAQ;GACR,SAAS;GACT,WAAW,EAAE;GACd,EACD;GACE,KAAK;GACL,YAAY;GACZ,OAAO,qBAAqB,aAAa;GACzC,QAAQ;GACR,SAAS;GACT,WAAW,CAAC,WAAW;GACxB,CACF,EACF,EACD,OACD;EAED,MAAM,gBAAgB,MAAM,QAAQ,IAClC,OAAO,OAAO,YAAY,KAAK,CAAC,KAAK,QACnC,KAAK,OAAO,WAAW,KAAK,OAAO,CACpC,CACF;AAED,OAAK,MAAM,OAAO,cAChB,KAAI,IAAI,WAAW,UAAU,OAC3B,OAAM,IAAI,MAAM,IAAI,OAAO,QAAQ;AAIvC,SAAO,KAAK,QAAQ,IAAe,WAAW;;CAGhD,MAAM,UACJ,iBACA,MACA,cACA,QACqB;AACrB,OAAK,OAAO,QACV,4DACA,iBACA,MACA,aACD;EACD,MAAM,WAAW,YAAY;EAO7B,MAAM,QANU,MAAM,KAAK,OAAO,QAChC,iBACA,QACA,CAACC,UAAgB;GAAE,IAAI;GAAU;GAAM;GAAc,CAAC,CAAC,EACvD,OACD,EACoB,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,SAAS;AACtE,MAAI,CAAC,QAAQ,CAAC,aAAa,KAAK,CAC9B,OAAM,IAAI,MAAM,yBAAyB;AAE3C,SAAO;;CAGT,MAAM,WACJ,iBACA,QACA,QACe;AACf,OAAK,OAAO,QACV,gDACA,iBACA,OACD;EACD,MAAM,QAAQ,MAAM,KAAK,OAAO,IAC9B,iBACA,KAAA,GACA,OACD;EACD,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,OAAO;AAClE,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,QAAQ,OAAO,sBAAsB,kBAAkB;AAGzE,MAAI,aAAa,KAAK,EAAE;GACtB,MAAM,kBAAkB,eACtB,MACA,MAAM,MAAM,OAAO,MACpB,CAAC,OAAO,WAAW;AACpB,QAAK,MAAM,QAAQ,gBACjB,OAAM,KAAK,eAAe,iBAAiB,KAAK,IAAI,OAAO;AAE7D,SAAM,KAAK,OAAO,QAChB,iBACA,QACA,CAACC,WAAiB,EAAE,IAAI,QAAQ,CAAC,CAAC,EAClC,OACD;AACD;;AAGF,QAAM,KAAK,eAAe,iBAAiB,QAAQ,OAAO;;CAG5D,MAAM,WACJ,iBACA,QACA,MACA,QACe;AACf,OAAK,OAAO,QACV,uDACA,iBACA,QACA,KACD;AAOD,OANgB,MAAM,KAAK,OAAO,QAChC,QACA,QACA,CAAC,QAAQ,QAAQ,EAAE,MAAM,CAAC,CAAC,EAC3B,OACD,EACW,OAAO,SAAS,KAC1B,OAAM,IAAI,MAAM,gCAAgC;EAQlD,MAAM,QANQ,MAAM,KAAK,OAAO,QAC9B,iBACA,QACA,CAACC,WAAiB;GAAE,IAAI;GAAQ;GAAM,CAAC,CAAC,EACxC,OACD,EACkB,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,OAAO;AAClE,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,uCAAuC;AAEzD,SAAO;;CAGT,MAAM,yBACJ,QACA,iBACA,QACqB;AACrB,OAAK,OAAO,QACV,8DACA,QACA,gBACD;AACD,SAAO,KAAK,OAAO,mBACjB,QACA,iBACA,QACA,OACD;;CAGH,MAAM,SACJ,iBACA,WACA,sBACA,QACgC;AAChC,OAAK,OAAO,QACV,wEACA,iBACA,WACA,qBACD;AACD,SAAO,KAAK,OAAO,QACjB,iBACA,QACA,CACEC,SAAe;GACb,WAAW;GACX,oBAAoB;GACrB,CAAC,CACH,EACD,OACD;;CAGH,MAAM,SACJ,iBACA,WACA,sBACA,QACgC;AAChC,OAAK,OAAO,QACV,wEACA,iBACA,WACA,qBACD;EACD,MAAM,QAAQ,MAAM,KAAK,OAAO,IAC9B,iBACA,KAAA,GACA,OACD;EACD,MAAM,UAAU,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,UAAU;AACxE,MAAI,CAAC,QACH,OAAM,IAAI,MACR,QAAQ,UAAU,sBAAsB,kBACzC;EAGH,MAAM,WAAW,kBACf;GACE,OAAO;GACP,oBAAoB;GACpB,YAAY,QAAQ;GACrB,QACK,YAAY,EAClB,MAAM,MAAM,OAAO,MACpB;EAED,MAAM,0CAA0B,IAAI,KAAqB;AACzD,OAAK,MAAM,SAAS,UAAU;GAC5B,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,MAAM;AACvE,OAAI,CAAC,KAAM;GACX,MAAM,WAAW,2BAA2B;IAC1C,OAAO,MAAM,MAAM,OAAO;IAC1B,SAAS,MAAM,cAAc,KAAK;IAClC,SAAS,WAAW,KAAK,GAAG,SAAS;IACrC,oBAAoB,MAAM,sBAAsB;IACjD,CAAC;AACF,2BAAwB,IAAI,MAAM,UAAU,SAAS;;AAGvD,OAAK,MAAM,SAAS,UAAU;GAC5B,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,MAAM;AACvE,OAAI,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAE;GAChC,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI,MAAM,OAAO,KAAA,GAAW,OAAO;GACpE,MAAM,SAAS,MAAM,KAAK,OAAO,uBAC/B,OAAO,OAAO,aACf;GACD,MAAM,aAAa,eACjB,OAAO,cACP,OAAO,YACP,OAAO,SACP,sBAAsB,MAAM,UAAU,OAAO,OAAO,aAAa,CAClE;GACD,MAAM,eAAe,wBAAwB,IAAI,MAAM,SAAS;AAChE,OAAI,aACF,YAAW,OAAO,OAAO;AAE3B,SAAM,KAAK,QACT,iBACA,YACA,MAAM,sBAAsB,KAAA,GAC5B,OACD;;AAGH,SAAO,KAAK,OAAO,QACjB,iBACA,QACA,SAAS,KAAK,UAAUC,SAAe,MAAM,CAAC,EAC9C,OACD;;CAGH,MAAM,QACJ,iBACA,QACA,QACe;AACf,OAAK,OAAO,QACV,6CACA,iBACA,OACD;EAMD,MAAM,QALQ,MAAM,KAAK,OAAO,IAC9B,iBACA,KAAA,GACA,OACD,EACkB,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,OAAO;AAClE,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,QAAQ,OAAO,sBAAsB,kBAAkB;AAEzE,SAAO;;CAGT,MAAM,UACJ,iBACA,cACA,QACA,QAC6B;AAC7B,OAAK,OAAO,QACV,8DACA,iBACA,cACA,OACD;EAMD,MAAM,YALQ,MAAM,KAAK,OAAO,IAC9B,iBACA,KAAA,GACA,OACD,EACsB,MAAM,OAAO;EACpC,MAAM,WACJ,iBAAiB,KAAA,IACb,CAAC,GAAG,SAAS,GACb,SAAS,QAAQ,OAAO,EAAE,gBAAgB,UAAU,aAAa;EAEvE,MAAM,EAAE,QAAQ,YAAY,UAAU,mBACpC,QACA,SAAS,OACV;EACD,MAAM,YAA2B,UAAU;GAAE,QAAQ;GAAI;GAAO;EAChE,MAAM,WAAW,aAAa;AAI9B,SAAO;GACL,SAJY,SAAS,MAAM,YAAY,SAAS;GAKhD,SAAS;GACT,GALc,WAAW,SAAS,SAKpB,EAAE,YAAY,OAAO,SAAS,EAAE,GAAG,EAAE;GACnD,YAAY,SAAS;GACtB;;CAGH,MAAc,eACZ,SACA,QACA,QACe;EACf,MAAM,sBAAgC,MAAM,YAC1C,CAAC,yBAAyB,SAAS,QAAQ,QAAQ,CAAC,EACpD,KAAK,QACL,OACD;EACD,MAAM,eAAyB,MAAM,YACnC,CAACH,WAAiB,EAAE,IAAI,QAAQ,CAAC,CAAC,EAClC,KAAK,QACL,OACD;EAED,MAAM,cAAc,MAAM,KAAK,QAAQ,aACrC,EACE,MAAM,CACJ;GACE,KAAK;GACL,YAAY;GACZ,OAAO,qBAAqB,oBAAoB;GAChD,QAAQ;GACR,SAAS;GACT,WAAW,EAAE;GACd,EACD;GACE,KAAK;GACL,YAAY;GACZ,OAAO,qBAAqB,aAAa;GACzC,QAAQ;GACR,SAAS;GACT,WAAW,CAAC,eAAe;GAC5B,CACF,EACF,EACD,OACD;EAED,MAAM,gBAAgB,MAAM,QAAQ,IAClC,OAAO,OAAO,YAAY,KAAK,CAAC,KAAK,QACnC,KAAK,OAAO,WAAW,KAAK,OAAO,CACpC,CACF;AACD,OAAK,MAAM,OAAO,cAChB,KAAI,IAAI,WAAW,UAAU,OAC3B,OAAM,IAAI,MAAM,IAAI,OAAO,QAAQ;EAIvC,MAAM,YAAY,MAAM,KAAK,QAAQ,eACnC,QACA,KAAK,QACL,OACD;EACD,MAAM,kBAAkB,MAAM,KAAK,OAAO,WAAW,WAAW,OAAO;AACvE,MAAI,gBAAgB,WAAW,UAAU,OACvC,OAAM,IAAI,MAAM,gBAAgB,OAAO,QAAQ;;;;;;;;;;AC5erD,SAAS,iBAAiB,QAA4B;AACpD,QAAO,WAAW,UAAU,cAAc,WAAW,UAAU;;;;;;AAOjE,IAAa,aAAb,MAA+C;CAC7C,8BAAsB,IAAI,KAA0B;CACpD,gBAAuC,EAAE;CAEzC,YACE,UACA,cAIA;AALQ,OAAA,WAAA;AACA,OAAA,eAAA;AAKR,OAAK,mBAAmB;;CAG1B,oBAAkC;AAChC,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,iBAClB,OAAO,OAAO,UAA8B;AAC1C,SAAM,KAAK,iBAAiB,MAAM;IAErC,CACF;AAED,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,gBAClB,OAAO,OAAO,UAA6B;AACzC,SAAM,KAAK,gBAAgB,MAAM;IAEpC,CACF;AAED,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,YAClB,OAAO,OAAO,UAA0B;AACtC,SAAM,KAAK,gBAAgB,MAAM;IAEpC,CACF;;CAGH,WAAiB;AACf,OAAK,MAAM,eAAe,KAAK,cAC7B,cAAa;AAEf,OAAK,gBAAgB,EAAE;AAEvB,OAAK,MAAM,GAAG,YAAY,KAAK,YAC7B,MAAK,MAAM,UAAU,QACnB,QAAO,uBAAO,IAAI,MAAM,uBAAuB,CAAC;AAGpD,OAAK,YAAY,OAAO;;CAG1B,MAAM,WAAW,OAAe,QAAwC;AACtE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,gBAAgB,MAAM,KAAK,aAAa,OAAO,OAAO;AAC5D,MAAI,iBAAiB,cAAc,OAAO,CACxC,QAAO;AA6BT,SA1BgB,IAAI,SAAkB,SAAS,WAAW;GACxD,MAAM,SAAoB;IAAE;IAAS;IAAQ;IAAQ;GAErD,MAAM,kBAAkB,KAAK,YAAY,IAAI,MAAM,IAAI,EAAE;AACzD,mBAAgB,KAAK,OAAO;AAC5B,QAAK,YAAY,IAAI,OAAO,gBAAgB;AAE5C,OAAI,QAAQ;IACV,MAAM,qBAAqB;KACzB,MAAM,UAAU,KAAK,YAAY,IAAI,MAAM;AAC3C,SAAI,SAAS;MACX,MAAM,QAAQ,QAAQ,QAAQ,OAAO;AACrC,UAAI,UAAU,IAAI;AAChB,eAAQ,OAAO,OAAO,EAAE;AACxB,WAAI,QAAQ,WAAW,EACrB,MAAK,YAAY,OAAO,MAAM;AAEhC,cAAO,uBAAO,IAAI,MAAM,oBAAoB,CAAC;;;;AAKnD,WAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,MAAM,CAAC;;IAEhE;;CAKJ,MAAc,iBAAiB,OAA0C;EACvE,MAAM,QAAQ,MAAM;AACpB,QAAM,KAAK,uBAAuB,MAAM;;CAG1C,MAAc,gBAAgB,OAAyC;EACrE,MAAM,QAAQ,MAAM;AACpB,QAAM,KAAK,uBAAuB,MAAM;;CAG1C,MAAc,gBAAgB,OAAsC;AAClE,QAAM,KAAK,uBAAuB,MAAM,MAAM;;CAGhD,MAAc,uBAAuB,OAA8B;EACjE,MAAM,UAAU,KAAK,YAAY,IAAI,MAAM;AAC3C,MAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;AAGF,MAAI;GACF,MAAM,gBAAgB,QAAQ,QAAQ,MAAM,CAAC,EAAE,QAAQ,QAAQ;AAE/D,OAAI,cAAc,WAAW,GAAG;AAC9B,SAAK,YAAY,OAAO,MAAM;AAC9B;;GAGF,MAAM,UAAU,MAAM,KAAK,aAAa,OAAO,cAAc,GAAG,OAAO;AAEvE,OAAI,iBAAiB,QAAQ,OAAO,EAAE;AACpC,SAAK,YAAY,OAAO,MAAM;AAE9B,SAAK,MAAM,UAAU,cACnB,QAAO,QAAQ,QAAQ;AAGzB,SAAK,MAAM,UAAU,QACnB,KAAI,OAAO,QAAQ,QACjB,QAAO,uBAAO,IAAI,MAAM,oBAAoB,CAAC;;WAI5C,OAAO;AACd,QAAK,YAAY,OAAO,MAAM;AAE9B,QAAK,MAAM,UAAU,QACnB,QAAO,OACL,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAC1D;;;;;;AC9LT,MAAM,mBAAmB;;;;AAKzB,SAAgB,kBAAkB,QAAyB;AACzD,QAAO,OAAO,WAAW,iBAAiB;;;;;;AAO5C,SAAgB,sBACd,cACQ;AACR,QAAO,mBAAmB,KAAK,UAAU,aAAa;;;;;;AAOxD,SAAgB,sBAAsB,QAAwC;AAC5E,KAAI,CAAC,OAAO,WAAW,iBAAiB,CACtC,OAAM,IAAI,MAAM,kCAAkC;CAGpD,MAAM,OAAO,OAAO,MAAM,EAAwB;AAElD,KAAI;EACF,MAAM,SAAkB,KAAK,MAAM,KAAK;AACxC,MACE,OAAO,WAAW,YAClB,WAAW,QACX,MAAM,QAAQ,OAAO,CAErB,OAAM,IAAI,MAAM,kCAAkC;AAEpD,SAAO;UACA,OAAO;AACd,MAAI,iBAAiB,YACnB,OAAM,IAAI,MAAM,mCAAmC,EAAE,OAAO,OAAO,CAAC;AAEtE,QAAM;;;;;;;;ACZV,IAAY,qBAAL,yBAAA,oBAAA;AACL,oBAAA,aAAA;AACA,oBAAA,aAAA;AACA,oBAAA,aAAA;AACA,oBAAA,iBAAA;AACA,oBAAA,mBAAA;AACA,oBAAA,gBAAA;AACA,oBAAA,kBAAA;;KACD;;;;;;;;;;;;;ACyBD,IAAa,gBAAb,MAAqD;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA,YACE,QACA,SACA,QACA,qBACA,YACA,iBACA,cACA;AACA,OAAK,SAAS;AACd,OAAK,UAAU;AACf,OAAK,SAAS;AACd,OAAK,sBAAsB;AAC3B,OAAK,aAAa;AAClB,OAAK,kBAAkB;AACvB,OAAK,eAAe;AACpB,OAAK,SAAS,IAAI,YAAY,MAAM,QAAQ,SAAS,OAAO;AAC5D,OAAK,OAAO,QAAQ,4BAA4B;;;;;CAMlD,MAAM,wBACJ,WACA,QACA,QAC4C;AAC5C,OAAK,OAAO,QACV,0CACA,WACA,OACD;AACD,SAAO,KAAK,QAAQ,kBAAkB,WAAW,QAAQ,OAAO;;;;;;;;CASlE,MAAM,uBACJ,cACmC;EAEnC,MAAM,UADU,MAAM,KAAK,QAAQ,mBAAmB,EAC/B,QAAQ,MAC5B,MAAM,EAAE,cAAc,OAAO,OAAO,aACtC;AAED,MAAI,CAAC,OACH,OAAM,IAAI,MACR,6CAA6C,eAC9C;AAGH,SAAO;;;;;CAMT,MAAM,IACJ,YACA,MACA,QACoB;AACpB,OAAK,OAAO,QAAQ,2BAA2B,YAAY,KAAK;AAChE,SAAO,MAAM,KAAK,QAAQ,cACxB,YACA,MACA,KAAA,GACA,OACD;;;;;CAMH,MAAM,cACJ,oBACA,MACA,QACA,QACA,QACkC;AAClC,OAAK,OAAO,QACV,+DACA,oBACA,MACA,QACA,OACD;EAED,MAAM,aAAa,MAAM,KAAK,aAAa,gBACzC,oBACA,MACA,KAAA,GACA,OACD;AAED,MAAI,QAAQ,UAAU,kBAAkB,OAAO,OAAO,CACpD,QAAO,KAAK,iCACV,YACA,MACA,QACA,QACA,OACD;EAGH,MAAM,oBAAoB,MAAM,KAAK,QAAQ,cAC3C,YACA,MACA,QACA,QACA,KAAA,GACA,OACD;EAED,MAAM,eAAe,OAAO,QAAQ,kBAAkB;EACtD,MAAM,kBAAkB,UAAU;GAAE,QAAQ;GAAK,OAAO;GAAK;AAE7D,MAAI,aAAa,UAAU,GAAG;GAC5B,MAAM,gBACJ,aAAa,WAAW,IAAI,CAAC,GAAG,aAAa,GAAG,GAAG,QAAQ,GAAG,EAAE;AAClE,iBAAc,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AAG/C,UAAO;IAAE,SAAS;IAAe,SAAS;IAAiB,YADzD,aAAa,WAAW,IAAI,aAAa,GAAG,GAAG,aAAa,KAAA;IACS;;EAGzE,MAAM,gBAA6B,EAAE;EACrC,MAAM,gBAAwC,EAAE;AAEhD,OAAK,MAAM,CAAC,WAAW,iBAAiB,cAAc;AACpD,iBAAc,KAAK,GAAG,aAAa,QAAQ;AAC3C,OAAI,aAAa,WACf,eAAc,aAAa,aAAa;;AAI5C,gBAAc,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AAO/C,SAAO;GAAE,SAAS;GAAe,SAAS;GAAiB,YAJzD,OAAO,KAAK,cAAc,CAAC,SAAS,IAChC,sBAAsB,cAAc,GACpC,KAAA;GAEiE;;CAGzE,MAAc,iCACZ,YACA,MACA,QACA,QACA,QACkC;EAClC,MAAM,eAAe,sBAAsB,OAAO,OAAO;EACzD,MAAM,gBAA6B,EAAE;EACrC,MAAM,gBAAwC,EAAE;AAEhD,OAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,aAAa,EAAE;GAC9D,MAAM,YAAwB;IAAE,GAAG;IAAM,QAAQ,CAAC,UAAU;IAAE;GAC9D,MAAM,cAA6B;IAAE;IAAQ,OAAO,OAAO;IAAO;GAWlE,MAAM,eAToB,MAAM,KAAK,QAAQ,cAC3C,YACA,WACA,QACA,aACA,KAAA,GACA,OACD,EAEqC;AACtC,iBAAc,KAAK,GAAG,YAAY,QAAQ;AAC1C,OAAI,YAAY,WACd,eAAc,aAAa,YAAY;;AAI3C,gBAAc,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;AAO/C,SAAO;GAAE,SAAS;GAAe,SAAS;GAAQ,YAJhD,OAAO,KAAK,cAAc,CAAC,SAAS,IAChC,sBAAsB,cAAc,GACpC,KAAA;GAEwD;;;;;CAMhE,MAAM,yBACJ,kBACA,kBACA,MACA,QACA,QACmC;AACnC,OAAK,OAAO,QACV,kFACA,kBACA,kBACA,MACA,OACD;EAED,MAAM,WAAW,MAAM,KAAK,aAAa,gBACvC,kBACA,MACA,KAAA,GACA,OACD;EAUD,MAAM,aARgB,MAAM,KAAK,gBAAgB,YAC/C,UACA,CAAC,iBAAiB,EAClB,KAAA,GACA,KAAA,GACA,OACD,EAE+B,QAAQ,KAAK,QAAQ,IAAI,SAAS;AAElE,MAAI,UAAU,WAAW,EACvB,QAAO;GACL,SAAS,EAAE;GACX,SAAS,UAAU;IAAE,QAAQ;IAAK,OAAO;IAAG;GAC7C;AAGH,SAAO,KAAK,QAAQ,KAClB,EAAE,KAAK,WAAW,EAClB,MACA,QACA,KAAA,GACA,OACD;;;;;CAMH,MAAM,yBACJ,kBACA,kBACA,MACA,QACA,QACmC;AACnC,OAAK,OAAO,QACV,kFACA,kBACA,kBACA,MACA,OACD;EAED,MAAM,WAAW,MAAM,KAAK,aAAa,gBACvC,kBACA,MACA,KAAA,GACA,OACD;EAUD,MAAM,aARgB,MAAM,KAAK,gBAAgB,YAC/C,UACA,CAAC,iBAAiB,EAClB,KAAA,GACA,KAAA,GACA,OACD,EAE+B,QAAQ,KAAK,QAAQ,IAAI,SAAS;AAElE,MAAI,UAAU,WAAW,EACvB,QAAO;GACL,SAAS,EAAE;GACX,SAAS,UAAU;IAAE,QAAQ;IAAK,OAAO;IAAG;GAC7C;AAGH,SAAO,KAAK,QAAQ,KAClB,EAAE,KAAK,WAAW,EAClB,MACA,QACA,KAAA,GACA,OACD;;;;;CAMH,MAAM,KACJ,QACA,MACA,QACA,QACmC;AACnC,OAAK,OAAO,QAAQ,iCAAiC,QAAQ,MAAM,OAAO;AAC1E,SAAO,KAAK,QAAQ,KAAK,QAAQ,MAAM,QAAQ,KAAA,GAAW,OAAO;;;;;CAMnE,MAAM,OACJ,UACA,kBACA,QACoB;AACpB,OAAK,OAAO,QACV,kCACA,SAAS,OAAO,IAChB,iBACD;EAED,MAAM,aAAa,SAAS,OAAO;EAsBnC,MAAM,gBAA0B,MAAM,YACpC,CACE,qBAtB2C;GAC7C,OAAO,SAAS,OAAO;GACvB,SAAS;GACT;GACA,SAAS;IACP,WAAW;IACX,WAAW,SAAS,OAAO,IAAI;IAC/B,OAAO,SAAS,OAAO,IAAI;IAC3B,iBAAiB,SAAS,OAAO;IACjC,cAAc,SAAS,OAAO;IAC/B;GACD,MAAM,SAAS,OAAO;GACtB,MAAM,SAAS,OAAO;GACtB,QAAQ,SAAS,OAAO;GACxB,MAAM,SAAS,OAAO;GACtB,kBAAkB,SAAS,OAAO,oBAAoB,EACpD,gBAAgB,GACjB;GACF,CAIoC,EACjC,sBAAsB;GACpB;GACA,OAAO,SAAS,OAAO;GACvB,aAAa;GACb,WAAW,SAAS,MAAM,SAAS;GACnC,cAAc,SAAS;GACxB,CAAC,CACH,EACD,KAAK,QACL,OACD;EAED,MAAM,OAA2B,CAC/B;GACE,KAAK;GACL;GACA,OAAO,qBAAqB,cAAc;GAC1C,QAAQ;GACR,SAAS;GACT,WAAW,EAAE;GACd,CACF;AAED,MAAI,kBAAkB;GACpB,MAAM,gBAA0B,MAAM,YACpC,CAAC,sBAAsB,kBAAkB,YAAY,QAAQ,CAAC,EAC9D,KAAK,QACL,OACD;AAED,QAAK,KAAK;IACR,KAAK;IACL,YAAY;IACZ,OAAO,qBAAqB,cAAc;IAC1C,QAAQ;IACR,SAAS;IACT,WAAW,CAAC,SAAS;IACtB,CAAC;;EAGJ,MAAM,cAAc,MAAM,KAAK,QAAQ,aAAa,EAAE,MAAM,EAAE,OAAO;EAErE,MAAM,gBAAgB,MAAM,QAAQ,IAClC,OAAO,OAAO,YAAY,KAAK,CAAC,KAAK,QACnC,KAAK,WAAW,KAAK,OAAO,CAC7B,CACF;AAED,OAAK,MAAM,OAAO,cAChB,KAAI,IAAI,WAAW,UAAU,OAC3B,OAAM,IAAI,MAAM,IAAI,OAAO,QAAQ;AAIvC,SAAO,MAAM,KAAK,QAAQ,IAAe,WAAW;;;;;CAMtD,MAAM,YACJ,mBACA,SACA,QACoB;AACpB,OAAK,OAAO,QACV,6CACA,mBACA,QACD;EAOD,MAAM,mBANgB,MAAM,KAAK,QAAQ,kBACvC,KAAA,GACA,KAAA,GACA,OACD,EAEqC,QAAQ,QAC3C,MAAM,EAAE,cAAc,OAAO,OAAO,kBACtC;EAED,IAAI;AACJ,MAAI,SAAS,yBAAyB,KAAA,GAAW;AAC/C,YAAS,gBAAgB,MACtB,MAAM,EAAE,YAAY,QAAQ,qBAC9B;AACD,OAAI,CAAC,OACH,OAAM,IAAI,MACR,sCAAsC,kBAAkB,iBAAiB,QAAQ,uBAClF;SAEE;AACL,YAAS,gBAAgB,QACtB,QAAQ,YAAY;AACnB,QAAI,WAAW,KAAA,EAAW,QAAO;AAGjC,YAFuB,QAAQ,WAAW,MACpB,OAAO,WAAW,KACA,UAAU;MAEpD,KAAA,EACD;AACD,OAAI,CAAC,OACH,OAAM,IAAI,MACR,sCAAsC,oBACvC;;EAIL,MAAM,WAAW,OAAO,MAAM,gBAAgB;AAC9C,WAAS,MAAM,SAAS,UAAU,OAAO,WAAW;AAEpD,SAAO,KAAK,OAAkB,UAAU,SAAS,kBAAkB,OAAO;;;;;;;;;CAU5E,MAAM,sBACJ,SACA,UACA,cACA,QACoB;AACpB,SAAO,KAAK,OAAO,QACjB,SACA,UACA,cACA,OACD;;;;;CAMH,MAAM,QACJ,oBACA,QACA,SACA,QACoB;AACpB,OAAK,OAAO,QACV,yDACA,oBACA,QACA,QAAQ,OACT;EACD,MAAM,gBAAgB,MAAM,YAAY,SAAS,KAAK,QAAQ,OAAO;EAErE,MAAM,UAAU,MAAM,KAAK,QAAQ,QACjC,oBACA,QACA,eACA,OACD;EAED,MAAM,eAAe,MAAM,KAAK,WAAW,SAAS,OAAO;AAE3D,MAAI,aAAa,WAAW,UAAU,OACpC,OAAM,IAAI,MAAM,aAAa,OAAO,QAAQ;EAG9C,MAAM,OAAmB,EAAE,QAAQ;AAOnC,SANe,MAAM,KAAK,QAAQ,cAChC,oBACA,MACA,aAAa,kBACb,OACD;;;;;CAOH,MAAM,aACJ,oBACA,QACA,SACA,QACkB;AAClB,OAAK,OAAO,QACV,8DACA,oBACA,QACA,QAAQ,OACT;EACD,MAAM,gBAAgB,MAAM,YAAY,SAAS,KAAK,QAAQ,OAAO;AAErE,SAAO,KAAK,QAAQ,QAClB,oBACA,QACA,eACA,OACD;;CAGH,MAAM,aACJ,SACA,QAC+B;AAC/B,OAAK,OAAO,QAAQ,6BAA6B,QAAQ,KAAK,OAAO;EAErE,MAAM,aAAiC,MAAM,QAAQ,IACnD,QAAQ,KAAK,IAAI,OAAO,SAAS;GAC/B,GAAG;GACH,SAAS,MAAM,YAAY,IAAI,SAAS,KAAK,QAAQ,OAAO;GAC7D,EAAE,CACJ;EAED,MAAM,cAAc,MAAM,KAAK,QAAQ,aACrC,EAAE,MAAM,YAAY,EACpB,OACD;EAED,MAAM,gBAAgB,MAAM,QAAQ,IAClC,OAAO,OAAO,YAAY,KAAK,CAAC,KAAK,QACnC,KAAK,WAAW,KAAK,OAAO,CAC7B,CACF;AAED,OAAK,MAAM,OAAO,cAChB,KAAI,IAAI,WAAW,UAAU,OAC3B,OAAM,IAAI,MAAM,IAAI,OAAO,QAAQ;AAIvC,SAAO;;;;;CAMT,MAAM,OACJ,oBACA,MACA,SAAiB,QACjB,QACqB;AACrB,OAAK,OAAO,QACV,+CACA,oBACA,MACA,OACD;AACD,SAAO,KAAK,QACV,oBACA,QACA,CAAC,QAAQ,QAAQ,KAAK,CAAC,EACvB,OACD;;;;;;CAOH,MAAM,mBACJ,oBACA,iBACA,SAAiB,QACjB,QACqB;AACrB,OAAK,OAAO,QACV,sEACA,oBACA,iBACA,OACD;AACD,SAAO,KAAK,QACV,oBACA,QACA,CAAC,QAAQ,mBAAmB,gBAAgB,CAAC,EAC7C,OACD;;;;;CAMH,MAAM,gBACJ,kBACA,kBACA,kBACA,SAAiB,QACjB,QACqB;AACrB,OAAK,OAAO,QACV,qFACA,kBACA,kBACA,kBACA,OACD;EACD,MAAM,UAAU,MAAM,KAAK,QAAQ,gBACjC,kBACA,kBACA,kBACA,QACA,KAAK,QACL,OACD;EAED,MAAM,eAAe,MAAM,KAAK,WAAW,SAAS,OAAO;AAE3D,MAAI,aAAa,WAAW,UAAU,OACpC,OAAM,IAAI,MAAM,aAAa,OAAO,QAAQ;AAS9C,SANe,MAAM,KAAK,QAAQ,cAChC,kBACA,EAAE,QAAQ,EACV,aAAa,kBACb,OACD;;;;;CAOH,MAAM,mBACJ,kBACA,kBACA,kBACA,SAAiB,QACjB,QACqB;AACrB,OAAK,OAAO,QACV,wFACA,kBACA,kBACA,kBACA,OACD;EACD,MAAM,UAAU,MAAM,KAAK,QAAQ,mBACjC,kBACA,kBACA,kBACA,QACA,KAAK,QACL,OACD;EAED,MAAM,eAAe,MAAM,KAAK,WAAW,SAAS,OAAO;AAE3D,MAAI,aAAa,WAAW,UAAU,OACpC,OAAM,IAAI,MAAM,aAAa,OAAO,QAAQ;AAS9C,SANe,MAAM,KAAK,QAAQ,cAChC,kBACA,EAAE,QAAQ,EACV,aAAa,kBACb,OACD;;;;;CAOH,MAAM,iBACJ,wBACA,wBACA,kBACA,kBACA,SAAiB,QACjB,QAIC;AACD,OAAK,OAAO,QACV,qHACA,wBACA,wBACA,kBACA,kBACA,OACD;EACD,MAAM,gBAAgB,MAAM,KAAK,QAAQ,mBACvC,wBACA,kBACA,kBACA,QACA,KAAK,QACL,OACD;EAED,MAAM,qBAAqB,MAAM,KAAK,WAAW,eAAe,OAAO;AAEvE,MAAI,mBAAmB,WAAW,UAAU,OAC1C,OAAM,IAAI,MAAM,mBAAmB,OAAO,QAAQ;EAGpD,MAAM,aAAa,MAAM,KAAK,QAAQ,gBACpC,wBACA,kBACA,kBACA,QACA,KAAK,QACL,OACD;EAED,MAAM,kBAAkB,MAAM,KAAK,WAAW,YAAY,OAAO;AAEjE,MAAI,gBAAgB,WAAW,UAAU,OACvC,OAAM,IAAI,MAAM,gBAAgB,OAAO,QAAQ;AAiBjD,SAAO;GACL,QAfmB,MAAM,KAAK,QAAQ,cACtC,wBACA,EAAE,QAAQ,EACV,mBAAmB,kBACnB,OACD;GAWC,QATmB,MAAM,KAAK,QAAQ,cACtC,wBACA,EAAE,QAAQ,EACV,gBAAgB,kBAChB,OACD;GAKA;;CAGH,MAAM,UACJ,SACA,QAC0B;AAC1B,OAAK,OAAO,QAAQ,0BAA0B,QAAQ,KAAK,OAAO;EAClE,MAAM,SAAS,MAAM,KAAK,QAAQ,UAAU,SAAS,OAAO;EAE5D,MAAM,gBAAgB,MAAM,QAAQ,IAClC,OAAO,QAAQ,OAAO,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,aAAa;AAExD,UAAO,CAAC,KADU,MAAM,KAAK,WAAW,SAAS,OAAO,CACjC;IACvB,CACH;AAED,OAAK,MAAM,GAAG,iBAAiB,cAC7B,KAAI,aAAa,WAAW,UAAU,OACpC,OAAM,IAAI,MAAM,aAAa,OAAO,QAAQ;AAIhD,SAAO,EAAE,MAAM,OAAO,YAAY,cAAc,EAAE;;;;;CAMpD,MAAM,eACJ,YACA,WACA,QACe;AACf,OAAK,OAAO,QACV,2CACA,YACA,UACD;EACD,MAAM,OAAkB,EAAE;AAE1B,MAAI,cAAc,gBAAgB,SAAS;GACzC,MAAM,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC;GACtC,IAAI,UAAU;AAEd,UAAO,SAAS;AACd,QAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAEtC,cAAU;IACV,MAAM,UAAU,MAAM,KAAK,gBAAgB,oBACzC,CAAC,GAAG,SAAS,EACb,CAAC,QAAQ,EACT,OACD;AACD,SAAK,MAAM,MAAM,QACf,KAAI,CAAC,SAAS,IAAI,GAAG,EAAE;AACrB,cAAS,IAAI,GAAG;AAChB,eAAU;;;AAKhB,QAAK,MAAM,gBAAgB,UAAU;AACnC,QAAI,iBAAiB,WACnB;IAEF,MAAM,cAAc,MAAM,KAAK,+BAC7B,cACA,OACD;AACD,SAAK,KAAK,GAAG,YAAY;IAEzB,MAAM,UAAU,MAAM,KAAK,QAAQ,eACjC,cACA,KAAK,QACL,OACD;AACD,SAAK,KAAK,QAAQ;;;EAItB,MAAM,cAAc,MAAM,KAAK,+BAC7B,YACA,OACD;AACD,OAAK,KAAK,GAAG,YAAY;EAEzB,MAAM,UAAU,MAAM,KAAK,QAAQ,eACjC,YACA,KAAK,QACL,OACD;AACD,OAAK,KAAK,QAAQ;EAElB,MAAM,gBAAgB,MAAM,QAAQ,IAClC,KAAK,KAAK,QAAQ,KAAK,WAAW,KAAK,OAAO,CAAC,CAChD;AAED,OAAK,MAAM,gBAAgB,cACzB,KAAI,aAAa,WAAW,UAAU,OACpC,OAAM,IAAI,MAAM,aAAa,OAAO,QAAQ;;;;;CAQlD,MAAM,gBACJ,aACA,WACA,QACe;AACf,OAAK,OAAO,QACV,mDACA,YAAY,QACZ,UACD;EACD,MAAM,iBAAiB,YAAY,KAAK,eACtC,KAAK,eAAe,YAAY,WAAW,OAAO,CACnD;AAED,QAAM,QAAQ,IAAI,eAAe;;;;;CAMnC,MAAM,aAAa,OAAe,QAAwC;AACxE,OAAK,OAAO,QAAQ,wBAAwB,MAAM;AAClD,SAAO,KAAK,QAAQ,aAAa,OAAO,OAAO;;;;;CAMjD,MAAM,WACJ,OACA,QACkB;EAClB,MAAM,KAAK,OAAO,UAAU,WAAW,QAAQ,MAAM;AACrD,OAAK,OAAO,QAAQ,mBAAmB,GAAG;AAC1C,SAAO,KAAK,WAAW,WAAW,IAAI,OAAO;;;;;CAM/C,UACE,QACA,UACA,MACY;AACZ,OAAK,OAAO,QAAQ,6BAA6B,QAAQ,KAAK;EAC9D,MAAM,qBAAqB,KAAK,oBAAoB,mBACjD,WAAW;AACV,IAAM,YAAY;AAChB,QAAI;KACF,MAAM,YAAY,MAAM,QAAQ,IAC9B,OAAO,QAAQ,KAAK,OAClB,KAAK,QAAQ,IAAI,IAAI,MAAM,KAAA,GAAW,KAAA,EAAU,CACjD,CACF;AAED,cAAS;MACP,MAAM,mBAAmB;MACzB;MACD,CAAC;YACI;OAGN;KAEN,OACD;EAED,MAAM,qBAAqB,KAAK,oBAAoB,mBACjD,gBAAgB;AACf,YAAS;IACP,MAAM,mBAAmB;IACzB,WAAW,EAAE;IACb,SAAS,EAAE,SAAS,YAAY,IAAI;IACrC,CAAC;KAEJ,OACD;EAED,MAAM,qBAAqB,KAAK,oBAAoB,wBACjD,WAAW;AACV,YAAS;IACP,MAAM,mBAAmB;IACzB,WAAW,OAAO;IACnB,CAAC;KAEJ,QACA,KACD;EAED,MAAM,0BACJ,KAAK,oBAAoB,uBACtB,UAAU,SAAS,eAAe;AACjC,YAAS;IACP,MACE,eAAe,uBAAuB,QAClC,mBAAmB,aACnB,mBAAmB;IACzB,WAAW,EAAE;IACb,SAAS;KACP;KACA;KACD;IACF,CAAC;KAEJ,OACD;AAEH,eAAa;AACX,uBAAoB;AACpB,uBAAoB;AACpB,uBAAoB;AACpB,4BAAyB;;;CAI7B,MAAc,+BACZ,YACA,QACoB;EACpB,MAAM,WAAW,MAAM,KAAK,gBAAgB,YAC1C,YACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,OACD;EAED,MAAM,OAAkB,EAAE;AAC1B,OAAK,MAAM,OAAO,SAAS,SAAS;GAClC,MAAM,UAAU,MAAM,KAAK,QAAQ,mBACjC,IAAI,UACJ,YACA,IAAI,kBACJ,QACA,KAAK,QACL,OACD;AACD,QAAK,KAAK,QAAQ;;AAEpB,SAAO;;;;;;;;ACtkCX,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,cAAA,aAAA,MAAA;AACA,eAAA,cAAA,mBAAA,KAAA;AACA,eAAA,cAAA,aAAA,KAAA;AACA,eAAA,cAAA,WAAA,KAAA;AACA,eAAA,cAAA,aAAA,KAAA;AACA,eAAA,cAAA,cAAA,KAAA;;KACD;;;;AAiED,MAAa,kBAAkB,EAC7B,eAAe,KAChB;;;ACtDD,SAAgB,YAAY,OAAkC;AAC5D,KAAI,iBAAiB,MACnB,QAAO;EACL,SAAS,MAAM;EACf,OAAO,MAAM,0BAAS,IAAI,OAAO,EAAC,SAAS;EAC5C;AAEH,QAAO;EACL,SAAS;EACT,wBAAO,IAAI,OAAO,EAAC,SAAS;EAC7B;;AAGH,IAAa,mBAAb,MAA2D;CACzD,YACE,OACA,YACA,UACA,UACA,QACA;AALQ,OAAA,QAAA;AACA,OAAA,aAAA;AACA,OAAA,WAAA;AACA,OAAA,WAAA;AACA,OAAA,SAAA;;CAGV,MAAM,aACJ,QACA,QACA,WACe;AACf,MAAI,OAAO,SAAS;AAClB,UAAO,UAAU;AAEjB,OAAI,KAAK,wBAAwB,OAAO,IAAI,CAC1C,OAAM,UAAU,iBAAiB,OAAO,IAAI,WAAW;AAEzD;;AAIF,MAAI,OAAO,SAAS,oBAAoB,QAAQ,OAAO,MAAM,EAAE;GAC7D,IAAI,cAAc;AAClB,OAAI;AACF,UAAM,KAAK,SAAS,kBAAkB,OAAO,MAAM,aAAa;AAChE,kBAAc;WACR;AAIR,OAAI,aAAa;IACf,MAAM,YAAY,YAAY,OAAO,MAAM;AAC3C,QAAI;AACF,WAAM,KAAK,MAAM,SAAS,OAAO,IAAI,IAAI,UAAU;AACnD;YACM;;;AAQZ,MAAI,OAAO,SAAS,sBAAsB,QAAQ,OAAO,MAAM,EAAE;AAC/D,UAAO,OAAO;AACd,aAAU,SAAS,OAAO,IAAI,YAAY,OAAO,IAAI;AACrD;;AAGF,MAAI,OAAO,SAAS,qBAAqB,QAAQ,OAAO,MAAM,EAAE;GAC9D,MAAM,YAAY,YAAY,OAAO,MAAM;AAC3C,QAAK,WAAW,WAAW,OAAO,IAAI,IAAI,WAAW,OAAO,IAAI;AAChE,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,OAAO,IAAI;IAClB,OAAO,OAAO;IACd,KAAK,OAAO;IACb,CAAC,CACD,YAAY,GAAG;AAClB,UAAO,KAAK,UAAU;AACtB;;EAGF,MAAM,aAAa,OAAO,IAAI,cAAc;AAG5C,MAAI,cAFe,OAAO,IAAI,cAAc,IAEf;GAC3B,MAAM,mBAAmB,OAAO,QAC5B,YAAY,OAAO,MAAM,GACzB,YAAY,gBAAgB;AAEhC,OAAI;AACF,UAAM,KAAK,MAAM,SAAS,OAAO,IAAI,IAAI,iBAAiB;YACnD,OAAO;IACd,MAAM,iBAAiB,YACrB,iBAAiB,QAAQ,QAAQ,sBAClC;AAED,SAAK,WAAW,WAAW,OAAO,IAAI,IAAI,gBAAgB,OAAO,IAAI;AAErE,SAAK,SACF,KAAK,kBAAkB,YAAY;KAClC,OAAO,OAAO,IAAI;KAClB,OAAO,OAAO,SAAS,IAAI,MAAM,eAAe,QAAQ;KACxD,KAAK,OAAO;KACb,CAAC,CACD,YAAY,GAAG;AAElB,WAAO,KAAK,eAAe;;SAExB;GACL,MAAM,mBAAmB,OAAO,QAC5B,YAAY,OAAO,MAAM,GACzB,YAAY,gBAAgB;GAEhC,MAAM,gBAAgB,KAAK,mBACzB,OAAO,IAAI,cACX,kBACA,aAAa,EACd;AAED,QAAK,WAAW,WAAW,OAAO,IAAI,IAAI,eAAe,OAAO,IAAI;AAEpE,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,OAAO,IAAI;IAClB,OAAO,OAAO,SAAS,IAAI,MAAM,cAAc,QAAQ;IACvD,KAAK,OAAO;IACb,CAAC,CACD,YAAY,GAAG;AAElB,UAAO,KAAK,cAAc;;;CAI9B,wBAAgC,KAAmB;AACjD,OAAK,MAAM,UAAU,IAAI,QACvB,KAAI,OAAO,SAAS,kBAClB,QAAO;AAGX,OAAK,MAAM,aAAa,IAAI,WAC1B,KAAI,UAAU,OAAO,SAAS,kBAC5B,QAAO;AAGX,SAAO;;CAGT,mBACE,cACA,cACA,eACW;EACX,MAAM,YAAY,CAAC,GAAG,cAAc,aAAa;AAEjD,MAAI,UAAU,WAAW,EACvB,QAAO;EAGT,MAAM,eAAe,CAAC,oBAAoB,cAAc,YAAY;EACpE,MAAM,aAAuB,EAAE;AAE/B,YAAU,SAAS,OAAO,UAAU;AAClC,gBAAa,KAAK,YAAY,QAAQ,EAAE,IAAI,MAAM,UAAU;AAC5D,cAAW,KAAK,YAAY,QAAQ,EAAE,kBAAkB,MAAM,QAAQ;IACtE;AAEF,SAAO;GACL,SAAS,aAAa,KAAK,KAAK;GAChC,OAAO,WAAW,KAAK,OAAO;GAC/B;;;;;;;;ACpIL,MAAa,wBAAwB;CACnC,aAAa;CACb,eAAe;CACf,YAAY;CACZ,kBAAkB;CAClB,kBAAkB;CACnB;;;;;;;;;;;;;;;;;;;ACpDD,MAAM,mBAAmB;AACzB,MAAM,YAAY;AAElB,SAAgB,eAAe,YAA4B;CACzD,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAQ,WAAW,WAAW,EAAE;AAChC,SAAO,KAAK,KAAK,MAAM,UAAU;;AAEnC,QAAO,SAAS;;AAGlB,SAAgB,UAAU,YAAoB,YAA4B;AACxE,KAAI,aAAa,EACf,OAAM,IAAI,MAAM,2CAA2C,WAAW,GAAG;AAE3E,QAAO,eAAe,WAAW,GAAG;;;;;;;;;;AC4BtC,MAAM,kCAAkC,IAAI,IAAI;CAC9C;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;;;AAoBF,IAAa,+BAAb,MAAyE;CACvE,UAAqC,EAAE;CACvC,YAAoB;CACpB,aAAqB;CACrB,qBAA6B;CAC7B;CACA,+BAAuB,IAAI,KAAoB;CAC/C;CACA;CAEA,YACE,eACA,UACA,OACA,YACA,QACA,UACA,2BACA,eAAuB,KACvB;AARQ,OAAA,gBAAA;AACA,OAAA,WAAA;AACA,OAAA,QAAA;AACA,OAAA,aAAA;AACA,OAAA,SAAA;AACA,OAAA,WAAA;AACA,OAAA,4BAAA;AAGR,OAAK,eAAe;AACpB,OAAK,gBAAgB,IAAI,iBACvB,OACA,YACA,UACA,UACA,OACD;;CAGH,MAAM,MAAM,YAAmC;AAC7C,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,kDAAkD;AAEpE,MAAI,aAAa,EACf,OAAM,IAAI,MAAM,uCAAuC;AAGzD,OAAK,UAAU,MAAM,KAAK,EAAE,QAAQ,YAAY,GAAG,GAAG,MACpD,KAAK,cAAc,EAAE,CACtB;AACD,QAAM,QAAQ,IAAI,KAAK,QAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,CAAC;AAErD,OAAK,cAAc,KAAK,SAAS,UAC/B,gBAAgB,eAChB,YAAY;AACV,SAAM,KAAK,gBAAgB;IAE9B;AAED,OAAK,YAAY;AACjB,QAAM,KAAK,gBAAgB;;CAG7B,MAAM,KAAK,WAAW,MAAqB;AACzC,MAAI,CAAC,KAAK,UACR;AAGF,MAAI,KAAK,aAAa;AACpB,QAAK,aAAa;AAClB,QAAK,cAAc,KAAA;;AAGrB,MAAI,SACF,QAAO,KAAK,aAAa,EACvB,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;AAI3D,OAAK,MAAM,GAAG,SAAS,KAAK,aAC1B,MAAK,MAAM,OAAO,MAAM;GACtB,MAAM,YAAY,YAChB,IAAI,sBAAsB,IAAI,WAAW,CAC1C;AACD,QAAK,WAAW,WAAW,IAAI,IAAI,WAAW,IAAI;AAClD,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,IAAI;IACX,OAAO,IAAI,sBAAsB,IAAI,WAAW;IAChD;IACD,CAAC,CACD,YAAY,GAAG;;AAGtB,OAAK,aAAa,OAAO;AAEzB,QAAM,QAAQ,IACZ,KAAK,QAAQ,KAAK,MAChB,EAAE,SAAS,SAAS,CAAC,OAAO,QAAiB;AAC3C,QAAK,OAAO,KAAK,kCAAkC,IAAI;IACvD,CACH,CACF;AAED,OAAK,UAAU,EAAE;AACjB,OAAK,YAAY;;;;;;;;CASnB,eAA+B;AAC7B,SAAO,EAAE;;;;;;;;;;CAWX,MAAM,UAAU,OAA0C;AACxD,MAAI,KAAK,QAAQ,WAAW,EAC1B;EAKF,MAAM,YAHU,MAAM,QAAQ,WAC5B,KAAK,QAAQ,KAAK,MAAM,EAAE,UAAU,MAAM,CAAC,CAC5C,EAEE,QAAQ,MAAkC,EAAE,WAAW,WAAW,CAClE,QAAQ,MAAM,CAAC,yBAAyB,EAAE,OAAO,CAAC;AACrD,MAAI,SAAS,WAAW,EACtB;AAEF,OAAK,MAAM,KAAK,SACd,MAAK,OAAO,MACV,8CACA,OACA,EAAE,OACH;AAEH,QAAM,SAAS,GAAG,kBAAkB,QAChC,SAAS,GAAG,SACZ,IAAI,MAAM,OAAO,SAAS,GAAG,OAAO,CAAC;;CAG3C,YAAmC;AACjC,SAAO;GACL,WAAW,KAAK;GAChB,cAAc,KAAK,QAAQ;GAC3B,YAAY,KAAK;GACjB,oBAAoB,KAAK;GAC1B;;CAGH,MAAc,iBAAgC;AAC5C,MAAI,CAAC,KAAK,aAAa,KAAK,QAAQ,WAAW,EAC7C;AAEF,QAAM,QAAQ,IACZ,KAAK,QAAQ,KAAK,WAAW,KAAK,eAAe,OAAO,CAAC,CAC1D;;CAGH,MAAc,eAAe,QAAwC;AACnE,MAAI,CAAC,OAAO,QAAQ,CAClB;EAGF,MAAM,QAAQ,OAAO;EACrB,MAAM,aAAa,KAAK,QAAQ;EAChC,MAAM,aAAa,SACjB,UAAU,KAAK,YAAY,WAAW,KAAK;EAE7C,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,KAAK,MAAM,oBAAoB,UAAU;WACjD,OAAO;AACd,QAAK,OAAO,MAAM,qCAAqC,MAAM;AAC7D;;AAGF,MAAI,CAAC,OACH;AAGF,SAAO,OAAO;AACd,OAAK;AACL,OAAK,WAAW,YAAY,OAAO,IAAI,GAAG;EAE1C,MAAM,eAAgC;GACpC,OAAO,OAAO,IAAI;GAClB,SAAS,OAAO,IAAI;GACrB;AACD,OAAK,SACF,KAAK,kBAAkB,aAAa,aAAa,CACjD,YAAY,GAAG;EAElB,MAAM,WAAW,OAAO;EACxB,MAAM,eAAgC;GACpC,KAAK,OAAO;GACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACD;AACD,OAAK,SACF,KAAK,sBAAsB,aAAa,aAAa,CACrD,YAAY,GAAG;EAElB,MAAM,SAAS,YAAY,QAAQ,KAAK,aAAa;EACrD,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,OAAO,QAAQ,OAAO,KAAK,OAAO;WAC3C,OAAO;GACd,MAAM,YAAY,YAChB,iBAAiB,QAAQ,QAAQ,OAAO,MAAM,CAC/C;AACD,OAAI,uBAAuB,MAAM,EAAE;AACjC,UAAM,KAAK,6BAA6B,QAAQ,OAAO,KAAK,UAAU;AACtE;;AAEF,UAAO,KAAK,UAAU;AACtB,QAAK;AACL,QAAK,WAAW,WAAW,OAAO,IAAI,IAAI,WAAW,OAAO,IAAI;AAChE,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,OAAO,IAAI;IAClB,OAAO,IAAI,MAAM,UAAU,QAAQ;IACnC,KAAK,OAAO;IACb,CAAC,CACD,YAAY,GAAG;GAClB,MAAM,cAA8B;IAClC,KAAK,OAAO;IACZ,OAAO,UAAU;IACjB,WAAW;IACX,YAAY;IACZ;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,YAAY,YAAY,CACnD,YAAY,GAAG;AAClB,SAAM,KAAK,eAAe,OAAO;AACjC;;AAGF,MAAI,QAAQ,OAAO,SAAS;AAC1B,QAAK;GACL,MAAM,iBAAoC;IACxC,KAAK,OAAO;IACZ,QAAQ,QAAQ;IAChB;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,eAAe,eAAe,CACzD,YAAY,GAAG;SACb;GACL,MAAM,cAA8B;IAClC,KAAK,OAAO;IACZ,OAAO,QAAQ,OAAO,OAAO,WAAW;IACxC,WAAW;IACX,YAAY;IACZ;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,YAAY,YAAY,CACnD,YAAY,GAAG;;AAGpB,MAAI,QAAQ,OAAO,WAAW,QAAQ,WAC/B,MAAK,eAAe,OAAO,KAAK,QAAQ,WAAW,CAAC,OACtD,UAAU;AACT,QAAK,OAAO,MACV,gDACA,EAAE,OAAO,OAAO,IAAI,IAAI,EACxB,MACD;IAEJ;AAGH,QAAM,KAAK,cAAc,aAAa,QAAQ,QAAQ,QAAQ;GAC5D,WAAW,YAAY,QAAQ;IAC7B,MAAM,WAAW,KAAK,aAAa,IAAI,WAAW,IAAI,EAAE;AACxD,aAAS,KAAK,IAAI;AAClB,SAAK,aAAa,IAAI,YAAY,SAAS;;GAE7C,mBAAmB,eAAe,KAAK,kBAAkB,WAAW;GACrE,CAAC;AAEF,OAAK;AACL,QAAM,KAAK,eAAe,OAAO;;CAGnC,MAAc,eACZ,KACA,SACe;AACf,OAAK,yBAAyB,QAAQ,WAAW;EAEjD,MAAM,cAAc,CAClB,GAAG,IAAI,IAAI,QAAQ,WAAW,KAAK,OAAO,GAAG,QAAQ,WAAW,CAAC,CAClE;EACD,IAAI,wBAAkD,EAAE;AACxD,MAAI;AACF,2BACE,MAAM,KAAK,0BAA0B,2BACnC,YACD;WACI,OAAO;AACd,QAAK,OAAO,MACV,qEACA,MACD;;EAGH,MAAM,QAA4B;GAChC,OAAO,IAAI;GACX,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB;GACD;AACD,MAAI;AACF,SAAM,KAAK,SAAS,KAAK,kBAAkB,iBAAiB,MAAM;WAC3D,OAAO;AACd,QAAK,OAAO,MAAM,gDAAgD,MAAM;;;CAI5E,yBAAiC,YAA0C;AACzE,OAAK,MAAM,MAAM,YAAY;GAC3B,MAAM,aAAa,GAAG,UAAU,OAAO;AACvC,OAAI,CAAC,gCAAgC,IAAI,WAAW,CAClD;GAEF,MAAM,SAAS,wBAAwB,GAAG;AAC1C,OAAI,OACF,MAAK,0BAA0B,WAAW,OAAO;;;;;;;;;;;CAavD,MAAc,6BACZ,MACA,KACA,WACe;AACf,OAAK,OAAO,KACV,8FACA;GAAE,OAAO,IAAI;GAAI,UAAU,KAAK;GAAU,EAC1C,UAAU,QACX;AAED,OAAK;AAML,QAAM,KAAK,cAAc,KAAK;AAE9B,MAAI;AACF,SAAM,KAAK,MAAM,SAAS,IAAI,IAAI,UAAU;WACrC,OAAO;AACd,QAAK,OAAO,MACV,iEACA,MACD;;;;;;;;;;;CAYL,MAAc,cAAc,MAAsC;EAChE,MAAM,YAAY,KAAK;AACvB,MAAI,KAAK,QAAQ,eAAe,KAC9B;EAGF,IAAI;AACJ,MAAI;AACF,WAAQ,KAAK,cAAc,UAAU;WAC9B,OAAO;AACd,QAAK,OAAO,MACV,2EACA,WACA,MACD;AACD;;AAGF,MAAI;AACF,SAAM,MAAM,OAAO;WACZ,OAAO;AACd,QAAK,OAAO,MACV,8DACA,WACA,MACD;AACD;;AAGF,OAAK,QAAQ,aAAa;AAC1B,QAAM,KAAK,eAAe,MAAM;;CAGlC,MAAc,kBAAkB,YAAmC;EACjE,MAAM,OAAO,KAAK,aAAa,IAAI,WAAW;AAC9C,MAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B;AAEF,OAAK,aAAa,OAAO,WAAW;AAEpC,OAAK,MAAM,OAAO,KAChB,KAAI;AACF,SAAM,KAAK,MAAM,QAAQ,IAAI;WACtB,OAAO;AACd,QAAK,OAAO,MAAM,2CAA2C,MAAM;;;;AAM3E,SAAS,uBAAuB,OAAyB;AACvD,QACE,iBAAiB,qBACjB,iBAAiB,yBACjB,iBAAiB;;AAIrB,SAAS,yBAAyB,QAA0B;AAC1D,KAAI,EAAE,kBAAkB,OACtB,QAAO;AAET,KAAI,OAAO,SAAS,uBAClB,QAAO;CAET,MAAM,QAAS,OAA+B;AAC9C,QACE,iBAAiB,SAAU,MAAgB,SAAS;;AAIxD,SAAS,wBAAwB,IAA8C;CAC7E,MAAM,aAAa,GAAG,UAAU,OAAO;CACvC,MAAM,QAAQ,GAAG,UAAU,OAAO;AAGlC,KACE,eAAe,sBACf,eAAe,yBACf,eAAe,sBAEf,QAAO,OAAO;AAEhB,KAAI,eAAe,kBACjB,QAAO,OAAO,cAAc,GAAG,QAAQ;;;;;;;;ACzgB3C,IAAa,2BAAb,MAAqE;CACnE,YAAoC,EAAE;CACtC,YAAoB;CACpB,aAAqB;CACrB,qBAA6B;CAC7B;CACA,+BAAuB,IAAI,KAAoB;CAC/C;CAEA;CAEA,YACE,iBACA,UACA,OACA,YACA,QACA,UACA,eAAuB,KACvB;AAPQ,OAAA,kBAAA;AACA,OAAA,WAAA;AACA,OAAA,QAAA;AACA,OAAA,aAAA;AACA,OAAA,SAAA;AACA,OAAA,WAAA;AAGR,OAAK,eAAe;AACpB,OAAK,gBAAgB,IAAI,iBACvB,OACA,YACA,UACA,UACA,OACD;;CAGH,MAAM,MAAM,cAAqC;AAC/C,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,wCAAwC;AAG1D,MAAI,eAAe,EACjB,OAAM,IAAI,MAAM,yCAAyC;AAI3D,OAAK,YAAY,EAAE;AACnB,OAAK,IAAI,IAAI,GAAG,IAAI,cAAc,IAChC,MAAK,UAAU,KAAK,KAAK,iBAAiB,CAAC;AAI7C,OAAK,cAAc,KAAK,SAAS,UAC/B,gBAAgB,eAChB,YAAY;AAEV,OAAI,KAAK,aAAa,KAAK,UAAU,OACnC,OAAM,KAAK,gBAAgB;IAGhC;AAED,OAAK,YAAY;AAGjB,QAAM,KAAK,qBAAqB;;CAGlC,MAAM,KAAK,WAAW,MAAqB;AACzC,MAAI,CAAC,KAAK,UACR;AAIF,MAAI,KAAK,aAAa;AACpB,QAAK,aAAa;AAClB,QAAK,cAAc,KAAA;;AAGrB,MAAI,SAEF,QAAO,KAAK,aAAa,EACvB,OAAM,IAAI,SAAS,YAAY,WAAW,SAAS,IAAI,CAAC;AAK5D,OAAK,MAAM,GAAG,SAAS,KAAK,aAC1B,MAAK,MAAM,OAAO,MAAM;GACtB,MAAM,YAAY,YAChB,IAAI,sBAAsB,IAAI,WAAW,CAC1C;AACD,QAAK,WAAW,WAAW,IAAI,IAAI,WAAW,IAAI;AAClD,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,IAAI;IACX,OAAO,IAAI,sBAAsB,IAAI,WAAW;IAChD;IACD,CAAC,CACD,YAAY,GAAG;;AAGtB,OAAK,aAAa,OAAO;AAEzB,OAAK,YAAY,EAAE;AACnB,OAAK,YAAY;;CAGnB,eAA+B;AAC7B,SAAO,CAAC,GAAG,KAAK,UAAU;;CAG5B,YAAmC;AACjC,SAAO;GACL,WAAW,KAAK;GAChB,cAAc,KAAK,UAAU;GAC7B,YAAY,KAAK;GACjB,oBAAoB,KAAK;GAC1B;;CAGH,MAAc,iBAAgC;EAE5C,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,KAAK,MAAM,aAAa;WAChC,OAAO;AACd,QAAK,OAAO,MAAM,qCAAqC,MAAM;AAC7D;;AAGF,MAAI,CAAC,OACH;AAIF,SAAO,OAAO;AACd,OAAK;AACL,OAAK,WAAW,YAAY,OAAO,IAAI,GAAG;EAG1C,MAAM,eAAgC;GACpC,OAAO,OAAO,IAAI;GAClB,SAAS,OAAO,IAAI;GACrB;AACD,OAAK,SACF,KAAK,kBAAkB,aAAa,aAAa,CACjD,YAAY,GAEX;EAGJ,MAAM,gBAAgB,KAAK,qBAAqB,KAAK,UAAU;EAC/D,MAAM,WAAW,KAAK,UAAU;EAChC,MAAM,WAAW,cAAc;EAE/B,MAAM,eAAgC;GACpC,KAAK,OAAO;GACZ,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC;GACD;AACD,OAAK,SACF,KAAK,sBAAsB,aAAa,aAAa,CACrD,YAAY,GAAG;EAIlB,MAAM,SAAS,YAAY,QAAQ,KAAK,aAAa;EACrD,MAAM,WAAW,WACf,kBAAkB,QAAQ,SAAS,IAAI,MAAM,OAAO,OAAO,CAAC;EAC9D,MAAM,eAAe,IAAI,SAAgB,GAAG,WAAW;AACrD,OAAI,OAAO,SAAS;AAClB,WAAO,QAAQ,OAAO,OAAO,CAAC;AAC9B;;AAEF,UAAO,iBAAiB,eAAe,OAAO,QAAQ,OAAO,OAAO,CAAC,EAAE,EACrE,MAAM,MACP,CAAC;IACF;EACF,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,QAAQ,KAAK,CAC1B,SAAS,WAAW,OAAO,KAAK,OAAO,EACvC,aACD,CAAC;WACK,OAAO;GACd,MAAM,YAAY,YAChB,iBAAiB,QAAQ,QAAQ,OAAO,MAAM,CAC/C;AAED,UAAO,KAAK,UAAU;AACtB,QAAK;AACL,QAAK,WAAW,WAAW,OAAO,IAAI,IAAI,WAAW,OAAO,IAAI;AAEhE,QAAK,SACF,KAAK,kBAAkB,YAAY;IAClC,OAAO,OAAO,IAAI;IAClB,OAAO,IAAI,MAAM,UAAU,QAAQ;IACnC,KAAK,OAAO;IACb,CAAC,CACD,YAAY,GAAG;GAElB,MAAM,cAA8B;IAClC,KAAK,OAAO;IACZ,OAAO,UAAU;IACjB,WAAW;IACX,YAAY;IACZ;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,YAAY,YAAY,CACnD,YAAY,GAAG;AAElB,SAAM,KAAK,kBAAkB;AAC7B;;AAIF,MAAI,OAAO,QACT,MAAK;AAGP,MAAI,OAAO,SAAS;GAClB,MAAM,iBAAoC;IACxC,KAAK,OAAO;IACZ;IACA;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,eAAe,eAAe,CACzD,YAAY,GAAG;SACb;GACL,MAAM,cAA8B;IAClC,KAAK,OAAO;IACZ,OAAO,OAAO,OAAO,WAAW;IAChC,WAAW;IACX,YAAY;IACZ;IACD;AACD,QAAK,SACF,KAAK,sBAAsB,YAAY,YAAY,CACnD,YAAY,GAAG;;AAGpB,QAAM,KAAK,cAAc,aAAa,QAAQ,QAAQ;GACpD,WAAW,YAAY,QAAQ;IAC7B,MAAM,WAAW,KAAK,aAAa,IAAI,WAAW,IAAI,EAAE;AACxD,aAAS,KAAK,IAAI;AAClB,SAAK,aAAa,IAAI,YAAY,SAAS;;GAE7C,mBAAmB,eAAe,KAAK,kBAAkB,WAAW;GACrE,CAAC;AAEF,OAAK;AACL,QAAM,KAAK,kBAAkB;;CAG/B,MAAc,mBAAkC;AAC9C,MAAI,CAAC,KAAK,UACR;EAGF,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,KAAK,MAAM,SAAS;WAC7B,OAAO;AACd,QAAK,OAAO,MAAM,wCAAwC,MAAM;AAChE;;AAGF,MAAI,QACF,OAAM,KAAK,gBAAgB;;CAI/B,MAAc,sBAAqC;EACjD,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,KAAK,MAAM,SAAS;WAC7B,OAAO;AACd,QAAK,OAAO,MAAM,4CAA4C,MAAM;AACpE;;AAGF,MAAI,SAAS;GAEX,MAAM,WAA4B,EAAE;AACpC,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK,UAAU,QAAQ,EAAE,EAAE,IACtD,UAAS,KAAK,KAAK,gBAAgB,CAAC;AAGtC,OAAI;AACF,UAAM,QAAQ,IAAI,SAAS;YACpB,OAAO;AACd,SAAK,OAAO,MAAM,0CAA0C,MAAM;;;;CAKxE,MAAc,kBAAkB,YAAmC;EACjE,MAAM,OAAO,KAAK,aAAa,IAAI,WAAW;AAC9C,MAAI,CAAC,QAAQ,KAAK,WAAW,EAC3B;AAEF,OAAK,aAAa,OAAO,WAAW;AAEpC,OAAK,MAAM,OAAO,KAChB,KAAI;AACF,SAAM,KAAK,MAAM,QAAQ,IAAI;WACtB,OAAO;AACd,QAAK,OAAO,MAAM,2CAA2C,MAAM;;;;;;;;;;;ACxT3E,IAAa,qBAAb,MAAuD;CACrD,uBAAe,IAAI,KAAsB;CACzC,gBAAuC,EAAE;CAEzC,YAAY,UAA6B;AAArB,OAAA,WAAA;AAClB,OAAK,mBAAmB;;CAG1B,oBAAkC;AAChC,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,kBACjB,OAAO,UAA8B;AACpC,QAAK,iBAAiB,MAAM;IAE/B,CACF;AAED,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,iBACjB,OAAO,UAA6B;AACnC,QAAK,gBAAgB,MAAM;IAE9B,CACF;AAED,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,kBAAkB,aACjB,OAAO,UAA0B;AAChC,QAAK,gBAAgB,MAAM;IAE9B,CACF;;CAGH,iBAAyB,OAAiC;EACxD,MAAM,QAAQ,MAAM;EACpB,MAAM,MAAM,KAAK,KAAK,IAAI,MAAM;AAChC,MAAI,OAAO,IAAI,WAAW,UAAU,SAAS;GAC3C,MAAM,mBAAmB,uBAAuB,MAAM,WAAW;AACjE,QAAK,KAAK,IAAI,OAAO;IACnB,GAAG;IACH,QAAQ,UAAU;IAClB;IACD,CAAC;;;CAIN,gBAAwB,OAAgC;EACtD,MAAM,QAAQ,MAAM;EACpB,MAAM,MAAM,KAAK,KAAK,IAAI,MAAM;AAChC,MAAI,OAAO,IAAI,WAAW,UAAU,YAClC,MAAK,KAAK,IAAI,OAAO;GACnB,GAAG;GACH,QAAQ,UAAU;GACnB,CAAC;;CAIN,gBAAwB,OAA6B;AACnD,OAAK,WACH,MAAM,OACN;GACE,SAAS,MAAM,MAAM;GACrB,OAAO,MAAM,MAAM,SAAS;GAC7B,EACD,MAAM,IACP;;CAGH,WAAiB;AACf,OAAK,MAAM,eAAe,KAAK,cAC7B,cAAa;AAEf,OAAK,gBAAgB,EAAE;;CAGzB,YAAY,SAAwB;AAClC,OAAK,KAAK,IAAI,QAAQ,IAAI,EAAE,GAAG,SAAS,CAAC;;CAG3C,YAAY,OAAqB;EAC/B,MAAM,MAAM,KAAK,KAAK,IAAI,MAAM;AAChC,MAAI,CAAC,KAAK;AAGR,QAAK,KAAK,IAAI,OAAO;IACnB,IAAI;IACJ,QAAQ,UAAU;IAClB,kCAAiB,IAAI,MAAM,EAAC,aAAa;IACzC,kBAAkB,6BAA6B;IAC/C,MAAM;KAAE,SAAS;KAAO,aAAa,CAAC,MAAM;KAAE;IAC/C,CAAC;AACF;;AAIF,OAAK,KAAK,IAAI,OAAO;GACnB,GAAG;GACH,QAAQ,UAAU;GACnB,CAAC;;CAGJ,WAAW,OAAe,OAAkB,KAAiB;EAC3D,MAAM,WAAW,KAAK,KAAK,IAAI,MAAM;AACrC,MAAI,CAAC,UAAU;AACb,QAAK,KAAK,IAAI,OAAO;IACnB,IAAI;IACJ,QAAQ,UAAU;IAClB,kCAAiB,IAAI,MAAM,EAAC,aAAa;IACzC,oCAAmB,IAAI,MAAM,EAAC,aAAa;IAC3C;IACA;IACA,kBAAkB,6BAA6B;IAC/C,MAAM;KAAE,SAAS;KAAO,aAAa,CAAC,MAAM;KAAE;IAC/C,CAAC;AACF;;AAGF,OAAK,KAAK,IAAI,OAAO;GACnB,GAAG;GACH,QAAQ,UAAU;GAClB,oCAAmB,IAAI,MAAM,EAAC,aAAa;GAC3C;GACA;GACA,kBAAkB,6BAA6B;GAChD,CAAC;;CAGJ,aAAa,OAA+B;EAC1C,MAAM,MAAM,KAAK,KAAK,IAAI,MAAM;AAChC,SAAO,MAAM,EAAE,GAAG,KAAK,GAAG;;;;;ACvJ9B,SAAgB,gBAAgB,IAAmC;AACjE,QAAO,GAAG,UAAU,OAAO,SAAS;;AAGtC,SAAgB,mBACd,IAC8B;AAC9B,KAAI,CAAC,GAAG,QAAQ,eAAgB,QAAO,KAAA;AAMvC,QAJc,KAAK,MAAM,GAAG,QAAQ,eAAe,CAItC;;AAGf,SAAgB,yBACd,IACoB;AAEpB,QADc,GAAG,UAAU,OAAO,MACrB,cAAc,GAAG,QAAQ;;AAGxC,SAAgB,yBACd,SACA,cACkB;AAClB,QAAO;EACL,IAAI;EACJ;EACA,KAAK;GACH,WAAW,EAAE;GACb,OAAO;GACR;EACD,MAAM;EACN,MAAM;EACN,QAAQ;EACR,UAAU,EAAE;EACZ,kCAAiB,IAAI,MAAM,EAAC,aAAa;EACzC,uCAAsB,IAAI,MAAM,EAAC,aAAa;EAC/C;;AAGH,SAAgB,cACd,IACA,QACS;AACT,KAAI,OAAO,gBAAgB,OAAO,aAAa,SAAS;MAClD,CAAC,OAAO,aAAa,SAAS,GAAG,QAAQ,aAAa,CACxD,QAAO;;AAIX,KAAI,OAAO,SAAS,OAAO,MAAM,SAAS;MACpC,CAAC,OAAO,MAAM,SAAS,GAAG,QAAQ,MAAM,CAC1C,QAAO;;AAIX,KAAI,OAAO,UAAU,OAAO,OAAO,SAAS;MACtC,CAAC,OAAO,OAAO,SAAS,GAAG,QAAQ,OAAO,CAC5C,QAAO;;AAIX,KAAI,OAAO,cAAc,OAAO,WAAW,SAAS;MAE9C,CADgB,OAAO,WAAW,SAAS,IAAI,IAC/B,CAAC,OAAO,WAAW,SAAS,GAAG,QAAQ,WAAW,CACpE,QAAO;;AAIX,QAAO;;;;;;;;;;;;;;;ACpCT,IAAa,mBAAb,cACU,cAEV;CACE,kCAAyD,IAAI,KAAK;CAClE,oCAA6D,IAAI,KAAK;CACtE,sCACE,IAAI,KAAK;CACX,8BAA2C,IAAI,KAAK;CACpD,8BAAuD,IAAI,KAAK;CAChE;CACA;CAEA,YACE,IACA,gBACA,YACA,oBACA,QACA,qBACA;AACA,QAAM,IAAI,gBAAgB,YAAY,oBAAoB;GACxD,aAAa;GACb,oBAAoB;GACrB,CAAC;AACF,OAAK,SAAS;AACd,OAAK,sBAAsB;;CAG7B,MAAe,OAAsB;AACnC,QAAM,MAAM,MAAM;AAClB,QAAM,KAAK,gBAAgB;AAC3B,QAAM,KAAK,wBAAwB;;CAGrC,MAAyB,iBACvB,OACe;AACf,QAAM,KAAK,2BAA2B,MAAM;AAC5C,QAAM,KAAK,8BAA8B,MAAM;AAC/C,QAAM,KAAK,4BAA4B,MAAM;;CAG/C,MAAM,gBACJ,YACA,SACe;AACf,MAAI,KAAK,gBAAgB,IAAI,WAAW,CACtC,OAAM,KAAK,kBAAkB,WAAW;AAG1C,OAAK,gBAAgB,IAAI,YAAY,QAAQ;AAC7C,OAAK,oBAAoB,IAAI,4BAAY,IAAI,KAAK,CAAC;AAEnD,OAAK,MAAM,CAAC,SAAS,iBAAiB,KAAK,aAAa;GACtD,MAAM,cAAc,yBAAyB,SAAS,aAAa;AACnE,SAAM,KAAK,yBACT,SACA,YACA,SACA,YACD;;;CAIL,MAAM,kBAAkB,YAAmC;EACzD,MAAM,oBAAoB,KAAK,oBAAoB,IAAI,WAAW;AAClE,MAAI,CAAC,kBAAmB;AAExB,OAAK,MAAM,CAAC,SAAS,YAAY,mBAAmB;AAClD,QAAK,MAAM,KAAK,QACd,OAAM,KAAK,eAAe,EAAE,OAAO,UAAU;GAG/C,MAAM,kBAAkB,KAAK,kBAAkB,IAAI,QAAQ;AAC3D,OAAI,iBAAiB;IACnB,MAAM,YAAY,gBAAgB,QAAQ,MAAM,CAAC,QAAQ,SAAS,EAAE,CAAC;AACrE,QAAI,UAAU,SAAS,EACrB,MAAK,kBAAkB,IAAI,SAAS,UAAU;QAE9C,MAAK,kBAAkB,OAAO,QAAQ;;;AAK5C,QAAM,KAAK,uBAAuB,EAAE,WAAW,YAAY,CAAC;AAC5D,OAAK,oBAAoB,OAAO,WAAW;AAC3C,OAAK,gBAAgB,OAAO,WAAW;;CAGzC,IAAI,aAAmD;AACrD,OAAK,MAAM,WAAW,KAAK,sBAAsB,CAC/C,KAAI,QAAQ,gBAAgB,YAAa,QAAO;;CAKpD,SAA6B;AAC3B,SAAO,MAAM,KAAK,KAAK,sBAAsB,CAAC;;CAGhD,CAAS,uBAAmD;AAC1D,OAAK,MAAM,WAAW,KAAK,kBAAkB,QAAQ,CACnD,QAAO;;CAIX,MAAc,2BACZ,YACe;AACf,OAAK,MAAM,MAAM,YAAY;AAC3B,OAAI,CAAC,KAAK,gBAAgB,GAAG,CAAE;GAE/B,MAAM,UAAU,GAAG,QAAQ;AAC3B,OAAI,KAAK,YAAY,IAAI,QAAQ,CAAE;AAEnC,QAAK,YAAY,IAAI,SAAS,GAAG,QAAQ,aAAa;GAEtD,MAAM,cAAc,mBAAmB,GAAG;AAC1C,OAAI,CAAC,YAAa;AAElB,QAAK,MAAM,CAAC,YAAY,YAAY,KAAK,gBACvC,OAAM,KAAK,yBACT,SACA,YACA,SACA,YACD;;;CAKP,gBAAwB,IAAmC;AACzD,SACE,GAAG,UAAU,OAAO,SAAS,qBAC7B,KAAK,oBAAoB,IAAI,GAAG,QAAQ,aAAa;;CAIzD,MAAc,8BACZ,YACe;AACf,OAAK,MAAM,MAAM,YAAY;AAC3B,OAAI,CAAC,gBAAgB,GAAG,CAAE;GAE1B,MAAM,UAAU,yBAAyB,GAAG;AAC5C,OAAI,CAAC,WAAW,CAAC,KAAK,YAAY,IAAI,QAAQ,CAAE;AAEhD,OAAI,CAAC,KAAK,wBAAwB,QAAQ,CAAE;AAE5C,SAAM,KAAK,uBAAuB,QAAQ;AAC1C,QAAK,YAAY,OAAO,QAAQ;;;CAIpC,MAAc,yBAAwC;EACpD,MAAM,SAAS,MAAM,KAAK,GACvB,WAAW,mBAAmB,CAC9B,OAAO,CAAC,cAAc,eAAe,CAAC,CACtC,MAAM,gBAAgB,MAAM,CAAC,GAAG,KAAK,oBAAoB,CAAC,CAC1D,MAAM,aAAa,KAAK,MAAM,CAC9B,SAAS;AAEZ,OAAK,MAAM,SAAS,OAClB,MAAK,YAAY,IAAI,MAAM,YAAY,MAAM,aAAa;;CAI9D,wBAAgC,YAA6B;AAC3D,SAAO,KAAK,YAAY,IAAI,WAAW;;CAGzC,MAAc,yBACZ,SACA,YACA,SACA,aACe;EACf,IAAI;AAEJ,MAAI;AACF,aAAU,MAAM,QAAQ,YAAY;WAC7B,OAAO;AACd,QAAK,OAAO,MACV,4DACA,YACA,SACA,MACD;AACD;;AAGF,MAAI,QAAQ,WAAW,EAAG;EAE1B,MAAM,cAAkC,EAAE;AAE1C,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,SAAS,QAAQ;GACvB,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG;GAEhD,MAAM,SAAS,KAAK,YAAY,IAAI,YAAY;GAChD,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;AAEJ,OAAI,QAAQ;AACV,kBAAc,OAAO;AACrB,aAAS,OAAO;AAChB,gBAAY,OAAO,aAAa,KAAA;AAChC,yBAAqB,OAAO,sBAAsB,KAAA;UAC7C;AAEL,mBADkB,OAAO,aAAa,iBACV,YAAY,KAAK,cAAc;AAC3D,aAAS;AACT,gBAAY,KAAA;AACZ,yBAAqB,KAAA;;GAGvB,MAAM,UAA4B;IAChC;IACA,WAAW;IACX;IACA,gBAAgB;IAChB;IACA;IACA;IACA;IACA;IACA,aAAa,KAAK,eAAe,QAAQ;IAC1C;AAED,eAAY,KAAK,QAAQ;AAEzB,SAAM,KAAK,oBAAoB,QAAQ;;AAIzC,QAAM,KAAK,GACR,WAAW,kBAAkB,CAC7B,MAAM,aAAa,KAAK,WAAW,CACnC,MAAM,WAAW,KAAK,QAAQ,CAC9B,MAAM,kBAAkB,MAAM,QAAQ,OAAO,CAC7C,SAAS;AAEZ,OAAK,MAAM,CAAC,IAAI,QAAQ,KAAK,YAC3B,KACE,IAAI,cAAc,cAClB,IAAI,YAAY,WAChB,IAAI,kBAAkB,QAAQ,OAE9B,MAAK,YAAY,OAAO,GAAG;EAI/B,MAAM,oBAAoB,KAAK,oBAAoB,IAAI,WAAW;AAClE,MAAI,kBACF,mBAAkB,IAAI,SAAS,YAAY;EAG7C,MAAM,0BAA0B,KAAK,kBAAkB,IAAI,QAAQ,IAAI,EAAE;AACzE,OAAK,kBAAkB,IAAI,SAAS,CAClC,GAAG,yBACH,GAAG,YACJ,CAAC;AAEF,OAAK,MAAM,WAAW,YACpB,KACE,QAAQ,WAAW,YACnB,QAAQ,cAAc,KAAK,YAE3B,OAAM,KAAK,kBAAkB,QAAQ;;CAK3C,MAAc,kBAAkB,SAA0C;EACxE,IAAI,OAAO,MAAM,KAAK,eAAe,gBAAgB,QAAQ,YAAY;AAEzE,SAAO,KAAK,QAAQ,SAAS,GAAG;GAC9B,MAAM,WAAW,KAAK,QAAQ,QAAQ,OACpC,cAAc,IAAI,QAAQ,OAAO,OAAO,CACzC;AAED,OAAI,SAAS,SAAS,EACpB,KAAI;AACF,UAAM,QAAQ,OAAO,UAAU,aAAa,SAAS;YAC9C,OAAO;AACd,YAAQ,SAAS;AACjB,YAAQ,YACN,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACxD,YAAQ,qCAAqB,IAAI,MAAM;AACvC,UAAM,KAAK,wBAAwB,QAAQ;AAC3C,SAAK,OAAO,MACV,+EACA,QAAQ,aACR,QAAQ,aACR,MACD;AACD;;AAOJ,WAAQ,cAHW,KAAK,IACtB,GAAG,KAAK,QAAQ,KAAK,OAAO,GAAG,QAAQ,QAAQ,CAChD;AAED,SAAM,KAAK,wBAAwB,QAAQ;AAE3C,OAAI,CAAC,KAAK,KAAM;AAChB,UAAO,MAAM,KAAK,MAAM;;;CAI5B,MAAc,eAAe,SAA0C;AACrE,MAAI,QAAQ,WAAW,UAAW;AAClC,UAAQ,SAAS;AACjB,UAAQ,YAAY,KAAA;AACpB,UAAQ,qBAAqB,KAAA;AAC7B,QAAM,KAAK,oBAAoB,QAAQ;AACvC,QAAM,KAAK,kBAAkB,QAAQ;;CAGvC,MAAc,uBAAuB,SAAgC;EACnE,MAAM,aAAa,KAAK,kBAAkB,IAAI,QAAQ;AACtD,MAAI,CAAC,WAAY;AAEjB,OAAK,MAAM,WAAW,WACpB,OAAM,KAAK,eAAe,QAAQ,OAAO,UAAU;AAGrD,OAAK,kBAAkB,OAAO,QAAQ;AAEtC,OAAK,MAAM,qBAAqB,KAAK,oBAAoB,QAAQ,CAC/D,mBAAkB,OAAO,QAAQ;AAGnC,QAAM,KAAK,uBAAuB,EAAE,SAAS,CAAC;;CAGhD,MAAc,eAAe,WAAsC;AACjE,MAAI;AACF,SAAM,UAAU,cAAc;WACvB,OAAO;AACd,QAAK,OAAO,MAAM,yCAAyC,MAAM;;;CAIrE,MAAc,4BACZ,YACe;EACf,MAAM,aAAa,KAAK,IAAI,GAAG,WAAW,KAAK,OAAO,GAAG,QAAQ,QAAQ,CAAC;EAC1E,MAAM,aAAa,MAAM,KAAK,KAAK,sBAAsB,CAAC;AAE1D,QAAM,QAAQ,IACZ,WAAW,IAAI,OAAO,YAAY;AAChC,OAAI,QAAQ,WAAW,SAAU;GAKjC,MAAM,WAHS,WAAW,QACvB,OAAO,GAAG,QAAQ,UAAU,QAAQ,YACtC,CACuB,QAAQ,OAC9B,cAAc,IAAI,QAAQ,OAAO,OAAO,CACzC;AAED,OAAI,SAAS,SAAS,EACpB,KAAI;AACF,UAAM,QAAQ,OAAO,UAAU,aAAa,SAAS;YAC9C,OAAO;AACd,YAAQ,SAAS;AACjB,YAAQ,YACN,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACxD,YAAQ,qCAAqB,IAAI,MAAM;AACvC,UAAM,KAAK,wBAAwB,QAAQ;AAC3C,SAAK,OAAO,MACV,+DACA,QAAQ,aACR,QAAQ,aACR,MACD;AACD;;AAIJ,WAAQ,cAAc;AACtB,SAAM,KAAK,wBAAwB,QAAQ;IAC3C,CACH;;CAGH,MAAc,iBAAgC;EAC5C,MAAM,OAAO,MAAM,KAAK,GACrB,WAAW,kBAAkB,CAC7B,WAAW,CACX,SAAS;AAEZ,OAAK,MAAM,OAAO,KAChB,MAAK,YAAY,IAAI,IAAI,aAAa,IAAI;;CAI9C,MAAc,wBACZ,SACe;AACf,MAAI;AACF,SAAM,KAAK,oBAAoB,QAAQ;WAChC,OAAO;AACd,QAAK,OAAO,MACV,uDACA,QAAQ,aACR,MACD;;;CAIL,MAAc,oBAAoB,SAA0C;AAC1E,QAAM,KAAK,GACR,WAAW,kBAAkB,CAC7B,OAAO;GACN,aAAa,QAAQ;GACrB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,gBAAgB,QAAQ;GACxB,aAAa,QAAQ;GACrB,QAAQ,QAAQ;GAChB,WAAW,QAAQ,aAAa;GAChC,oBAAoB,QAAQ,sBAAsB;GAClD,2BAAW,IAAI,MAAM;GACtB,CAAC,CACD,YAAY,OACX,GAAG,OAAO,cAAc,CAAC,YAAY;GACnC,aAAa,QAAQ;GACrB,QAAQ,QAAQ;GAChB,WAAW,QAAQ,aAAa;GAChC,oBAAoB,QAAQ,sBAAsB;GAClD,2BAAW,IAAI,MAAM;GACtB,CAAC,CACH,CACA,SAAS;AAEZ,OAAK,YAAY,IAAI,QAAQ,aAAa;GACxC,aAAa,QAAQ;GACrB,WAAW,QAAQ;GACnB,SAAS,QAAQ;GACjB,gBAAgB,QAAQ;GACxB,aAAa,QAAQ;GACrB,QAAQ,QAAQ;GAChB,WAAW,QAAQ,aAAa;GAChC,oBAAoB,QAAQ,sBAAsB;GAClD,2BAAW,IAAI,MAAM;GACrB,2BAAW,IAAI,MAAM;GACtB,CAAC;;CAGJ,MAAc,uBACZ,QACe;AACf,MAAI,eAAe,QAAQ;AACzB,SAAM,KAAK,GACR,WAAW,kBAAkB,CAC7B,MAAM,aAAa,KAAK,OAAO,UAAU,CACzC,SAAS;AACZ,QAAK,MAAM,CAAC,IAAI,QAAQ,KAAK,YAC3B,KAAI,IAAI,cAAc,OAAO,UAAW,MAAK,YAAY,OAAO,GAAG;SAEhE;AACL,SAAM,KAAK,GACR,WAAW,kBAAkB,CAC7B,MAAM,WAAW,KAAK,OAAO,QAAQ,CACrC,SAAS;AACZ,QAAK,MAAM,CAAC,IAAI,QAAQ,KAAK,YAC3B,KAAI,IAAI,YAAY,OAAO,QAAS,MAAK,YAAY,OAAO,GAAG;;;;;;;;;ACxfvE,IAAa,qBAAb,MAA+D;CAC7D;CACA;CACA;CACA;CACA;CACA;CAEA,aAAqB,OAA8B;AACjD,UAAQ,OAAR;GACE,KAAK,cAAc,cACjB,QAAO;GACT,KAAK,cAAc,QACjB,QAAO;GACT,KAAK,cAAc,MACjB,QAAO;GACT,KAAK,cAAc,QACjB,QAAO;GACT,KAAK,cAAc,SACjB,QAAO;GACT,QACE,QAAO;;;CAIb,YACE,KACA,cACA,WAMA;AACA,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,UAAU,WAAW;AAC1B,OAAK,aAAa,WAAW;AAC7B,OAAK,SAAS,WAAW;AACzB,OAAK,UAAU,WAAW;;CAG5B,IAAI,MAAW;AACb,SAAO,KAAK;;CAGd,IAAI,QAAuB;AACzB,SAAO,KAAK;;CAGd,QAAc;AACZ,MAAI,KAAK,WAAW,cAAc,MAChC,OAAM,IAAI,MACR,6BAA6B,KAAK,aAAa,KAAK,OAAO,GAC5D;AAEH,OAAK,SAAS,cAAc;AAC5B,OAAK,WAAW;;CAGlB,WAAiB;AACf,MAAI,KAAK,WAAW,cAAc,QAChC,OAAM,IAAI,MACR,gCAAgC,KAAK,aAAa,KAAK,OAAO,GAC/D;AAEH,OAAK,SAAS,cAAc;AAC5B,OAAK,cAAc;;CAGrB,KAAK,OAAwB;AAC3B,MAAI,KAAK,WAAW,cAAc,QAChC,OAAM,IAAI,MACR,4BAA4B,KAAK,aAAa,KAAK,OAAO,GAC3D;AAEH,OAAK,SAAS,cAAc;AAC5B,OAAK,SAAS,MAAM;;CAGtB,QAAc;AACZ,MAAI,KAAK,WAAW,cAAc,QAChC,OAAM,IAAI,MACR,6BAA6B,KAAK,aAAa,KAAK,OAAO,GAC5D;AAEH,OAAK,SAAS,cAAc;AAC5B,OAAK,WAAW;;;;;;;;;;;AC1EpB,IAAa,gBAAb,MAA6C;CAC3C,yBAAiB,IAAI,KAAoB;CACzC,kCAA0B,IAAI,KAAqB;CACnD,+BAAuB,IAAI,KAA0B;CACrD,+BAAuB,IAAI,KAAqB;CAChD,gCAAwB,IAAI,KAAa;CACzC,2BAAmB,IAAI,KAAkB;CACzC,YAAoB;CACpB;CACA,eAAuB;CAEvB,YACE,UACA,UACA;AAFQ,OAAA,WAAA;AACA,OAAA,WAAA;;CAGV,YAAoB,OAAkC;AACpD,MAAI,iBAAiB,MACnB,QAAO;GACL,SAAS,MAAM;GACf,OAAO,MAAM,0BAAS,IAAI,OAAO,EAAC,SAAS;GAC5C;AAEH,SAAO;GACL,SAAS;GACT,wBAAO,IAAI,OAAO,EAAC,SAAS;GAC7B;;;;;CAMH,eACE,YACA,OACA,QACQ;AACR,SAAO,GAAG,WAAW,GAAG,MAAM,GAAG;;;;;CAMnC,SAAiB,UAAyB;EACxC,IAAI,QAAQ,KAAK,OAAO,IAAI,SAAS;AACrC,MAAI,CAAC,OAAO;AACV,WAAQ,EAAE;AACV,QAAK,OAAO,IAAI,UAAU,MAAM;;AAElC,SAAO;;;;;CAMT,oBAA4B,YAA6B;EACvD,MAAM,eAAe,KAAK,aAAa,IAAI,WAAW;AACtD,SAAO,eAAe,aAAa,OAAO,IAAI;;;;;CAMhD,iBAAyB,KAAgB;EACvC,IAAI,eAAe,KAAK,aAAa,IAAI,IAAI,WAAW;AACxD,MAAI,CAAC,cAAc;AACjB,kCAAe,IAAI,KAAK;AACxB,QAAK,aAAa,IAAI,IAAI,YAAY,aAAa;;AAErD,eAAa,IAAI,IAAI,GAAG;AACxB,OAAK,aAAa,IAAI,IAAI,IAAI,IAAI,WAAW;;;;;CAM/C,gBAAwB,OAAe,YAA0B;EAC/D,MAAM,eAAe,KAAK,aAAa,IAAI,WAAW;AACtD,MAAI,cAAc;AAChB,gBAAa,OAAO,MAAM;AAC1B,OAAI,aAAa,SAAS,EACxB,MAAK,aAAa,OAAO,WAAW;;AAGxC,OAAK,aAAa,OAAO,MAAM;;;;;CAMjC,mBAA2B,KAAmB;AAC5C,MAAI,IAAI,UAAU,WAAW,EAC3B,QAAO;AAET,SAAO,IAAI,UAAU,OAAO,UAAU,KAAK,cAAc,IAAI,MAAM,CAAC;;;;;;;;;;;CAYtE,8BAAsC,OAA0B;AAC9D,MAAI,MAAM,WAAW,EACnB,QAAO;EAET,MAAM,OAAO,MAAM;AACnB,SAAO,KAAK,mBAAmB,KAAK,GAAG,OAAO;;CAGhD,sBAA8B,KAA8B;AAC1D,OAAK,MAAM,UAAU,IAAI,QACvB,KAAI,OAAO,SAAS,kBAClB,QAAQ,OAAO,MAAoC;AAGvD,OAAK,MAAM,aAAa,IAAI,WAC1B,KAAI,UAAU,OAAO,SAAS,kBAC5B,QAAQ,UAAU,OAAO,MAAoC;;CAMnE,MAAM,QAAQ,KAAyB;AAErC,MAAI,KAAK,UACP,OAAM,IAAI,MAAM,mBAAmB;EAGrC,MAAM,WAAW,KAAK,eAAe,IAAI,YAAY,IAAI,OAAO,IAAI,OAAO;AAC7D,OAAK,SAAS,SAAS,CAG/B,KAAK,IAAI;AAGf,OAAK,gBAAgB,IAAI,IAAI,IAAI,SAAS;AAC1C,OAAK,SAAS,IAAI,IAAI,IAAI,IAAI;EAG9B,MAAM,eAAe,KAAK,sBAAsB,IAAI;AACpD,MAAI,aACF,KAAI;AACF,SAAM,KAAK,SAAS,kBAAkB,aAAa;UAC7C;AACN,SAAM,KAAK,QAAQ,IAAI,IAAI;IACzB,SAAS,2CAA2C;IACpD,wBAAO,IAAI,OAAO,EAAC,SAAS;IAC7B,CAAC;AACF;;EAKJ,MAAM,YAA+B;GACnC,YAAY,IAAI;GAChB,OAAO,IAAI;GACX,QAAQ,IAAI;GACZ,OAAO,IAAI;GACZ;AAED,QAAM,KAAK,SAAS,KAAK,gBAAgB,eAAe,UAAU;;CAGpE,QACE,YACA,OACA,QACA,QACqC;EACrC,MAAM,WAAW,KAAK,eAAe,YAAY,OAAO,OAAO;EAC/D,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;AAEvC,MAAI,QAAQ,QACV,QAAO,QAAQ,uBAAO,IAAI,MAAM,oBAAoB,CAAC;AAGvD,MAAI,CAAC,SAAS,MAAM,WAAW,EAC7B,QAAO,QAAQ,QAAQ,KAAK;EAI9B,MAAM,MAAM,KAAK,8BAA8B,MAAM;AACrD,MAAI,CAAC,IACH,QAAO,QAAQ,QAAQ,KAAK;EAI9B,MAAM,WAAW,MAAM,QAAQ,IAAI;AACnC,QAAM,OAAO,UAAU,EAAE;AAGzB,OAAK,gBAAgB,OAAO,IAAI,GAAG;AAGnC,OAAK,iBAAiB,IAAI;AAG1B,MAAI,MAAM,WAAW,EACnB,MAAK,OAAO,OAAO,SAAS;EAI9B,MAAM,SAAS,IAAI,mBAAmB,KAAK,cAAc,OAAO;GAC9D,eAAe;GAGf,kBAAkB;AACX,SAAK,YAAY,IAAI,GAAG;;GAE/B,SAAS,UAAqB;AACvB,SAAK,QAAQ,IAAI,IAAI,MAAM;;GAElC,eAAe;AACR,SAAK,SAAS,IAAI,GAAG;;GAE7B,CAAC;AAEF,SAAO,QAAQ,QAAQ,OAAO;;CAGhC,YAAY,QAA2D;AACrE,MAAI,QAAQ,QACV,QAAO,QAAQ,uBAAO,IAAI,MAAM,oBAAoB,CAAC;AAGvD,MAAI,KAAK,aACP,QAAO,QAAQ,QAAQ,KAAK;AAI9B,OAAK,MAAM,CAAC,UAAU,UAAU,KAAK,OAAO,SAAS,CACnD,KAAI,MAAM,SAAS,GAAG;GAEpB,MAAM,MAAM,KAAK,8BAA8B,MAAM;AACrD,OAAI,CAAC,IACH;AAIF,OAAI,CAAC,KAAK,oBAAoB,IAAI,WAAW,EAAE;IAE7C,MAAM,SAAS,MAAM,QAAQ,IAAI;AACjC,UAAM,OAAO,QAAQ,EAAE;AAGvB,SAAK,gBAAgB,OAAO,IAAI,GAAG;AAInC,SAAK,iBAAiB,IAAI;AAG1B,QAAI,MAAM,WAAW,EACnB,MAAK,OAAO,OAAO,SAAS;IAI9B,MAAM,SAAS,IAAI,mBAAmB,KAAK,cAAc,OAAO;KAC9D,eAAe;KAGf,kBAAkB;AACX,WAAK,YAAY,IAAI,GAAG;;KAE/B,SAAS,UAAqB;AACvB,WAAK,QAAQ,IAAI,IAAI,MAAM;;KAElC,eAAe;AACR,WAAK,SAAS,IAAI,GAAG;;KAE7B,CAAC;AAEF,WAAO,QAAQ,QAAQ,OAAO;;;AAKpC,SAAO,QAAQ,QAAQ,KAAK;;CAG9B,oBACE,WACA,QACqC;AACrC,MAAI,QAAQ,QACV,QAAO,QAAQ,uBAAO,IAAI,MAAM,oBAAoB,CAAC;AAGvD,MAAI,KAAK,aACP,QAAO,QAAQ,QAAQ,KAAK;AAG9B,OAAK,MAAM,CAAC,UAAU,UAAU,KAAK,OAAO,SAAS,CACnD,KAAI,MAAM,SAAS,GAAG;GACpB,MAAM,MAAM,KAAK,8BAA8B,MAAM;AACrD,OAAI,CAAC,IACH;AAGF,OAAI,KAAK,oBAAoB,IAAI,WAAW,CAC1C;AASF,OAAI,CAAC,UANwB;IAC3B,YAAY,IAAI;IAChB,OAAO,IAAI;IACX,QAAQ,IAAI;IACb,CAEmB,CAClB;GAGF,MAAM,SAAS,MAAM,QAAQ,IAAI;AACjC,SAAM,OAAO,QAAQ,EAAE;AAEvB,QAAK,gBAAgB,OAAO,IAAI,GAAG;AAEnC,QAAK,iBAAiB,IAAI;AAE1B,OAAI,MAAM,WAAW,EACnB,MAAK,OAAO,OAAO,SAAS;GAG9B,MAAM,SAAS,IAAI,mBAAmB,KAAK,cAAc,OAAO;IAC9D,eAAe;IAGf,kBAAkB;AACX,UAAK,YAAY,IAAI,GAAG;;IAE/B,SAAS,UAAqB;AACvB,UAAK,QAAQ,IAAI,IAAI,MAAM;;IAElC,eAAe;AACR,UAAK,SAAS,IAAI,GAAG;;IAE7B,CAAC;AAEF,UAAO,QAAQ,QAAQ,OAAO;;AAIlC,SAAO,QAAQ,QAAQ,KAAK;;CAG9B,KAAK,YAAoB,OAAe,QAAiC;EACvE,MAAM,WAAW,KAAK,eAAe,YAAY,OAAO,OAAO;EAC/D,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;AACvC,SAAO,QAAQ,QAAQ,QAAQ,MAAM,SAAS,EAAE;;CAGlD,YAA6B;EAC3B,IAAI,QAAQ;AACZ,OAAK,MAAM,SAAS,KAAK,OAAO,QAAQ,CACtC,UAAS,MAAM;AAEjB,SAAO,QAAQ,QAAQ,MAAM;;CAG/B,OAAO,OAAiC;EACtC,MAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM;AAChD,MAAI,CAAC,SACH,QAAO,QAAQ,QAAQ,MAAM;EAG/B,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;AACvC,MAAI,CAAC,OAAO;AAEV,QAAK,gBAAgB,OAAO,MAAM;AAClC,QAAK,SAAS,OAAO,MAAM;AAC3B,UAAO,QAAQ,QAAQ,MAAM;;EAG/B,MAAM,SAAS,MAAM,WAAW,QAAQ,IAAI,OAAO,MAAM;AACzD,MAAI,WAAW,IAAI;AAEjB,QAAK,gBAAgB,OAAO,MAAM;AAClC,QAAK,SAAS,OAAO,MAAM;AAC3B,UAAO,QAAQ,QAAQ,MAAM;;AAI/B,QAAM,OAAO,QAAQ,EAAE;AAGvB,OAAK,gBAAgB,OAAO,MAAM;AAClC,OAAK,SAAS,OAAO,MAAM;AAG3B,MAAI,MAAM,WAAW,EACnB,MAAK,OAAO,OAAO,SAAS;AAG9B,SAAO,QAAQ,QAAQ,KAAK;;CAG9B,MAAM,YAAoB,OAAe,QAA+B;EACtE,MAAM,WAAW,KAAK,eAAe,YAAY,OAAO,OAAO;EAC/D,MAAM,QAAQ,KAAK,OAAO,IAAI,SAAS;AAEvC,MAAI,OAAO;AAET,QAAK,MAAM,OAAO,OAAO;AACvB,SAAK,gBAAgB,OAAO,IAAI,GAAG;AACnC,SAAK,SAAS,OAAO,IAAI,GAAG;;AAI9B,QAAK,OAAO,OAAO,SAAS;;AAG9B,SAAO,QAAQ,SAAS;;CAG1B,WAA0B;AAExB,OAAK,gBAAgB,OAAO;AAC5B,OAAK,SAAS,OAAO;AACrB,OAAK,cAAc,OAAO;AAG1B,OAAK,OAAO,OAAO;AAEnB,SAAO,QAAQ,SAAS;;CAG1B,UAA4B;AAC1B,SAAO,QAAQ,QACb,KAAK,OAAO,OAAO,KACjB,MAAM,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,MAAM,MAAM,EAAE,SAAS,EAAE,CAC7D;;CAGH,MAAM,YAAY,OAA8B;EAE9C,MAAM,aAAa,KAAK,aAAa,IAAI,MAAM;AAC/C,MAAI,WAEF,MAAK,gBAAgB,OAAO,WAAW;AAIzC,OAAK,cAAc,IAAI,MAAM;AAG7B,OAAK,SAAS,OAAO,MAAM;AAI3B,QAAM,KAAK,OAAO,MAAM;AAGxB,OAAK,cAAc;;CAGrB,MAAM,QAAQ,OAAe,OAAkC;EAE7D,MAAM,aAAa,KAAK,aAAa,IAAI,MAAM;AAC/C,MAAI,WAEF,MAAK,gBAAgB,OAAO,WAAW;EAIzC,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM;AACpC,MAAI,KAAK;AACP,OAAI,YAAY;AAChB,OAAI,MACF,KAAI,aAAa,KAAK,MAAM;;AAKhC,OAAK,SAAS,OAAO,MAAM;AAG3B,OAAK,cAAc,IAAI,MAAM;AAI7B,QAAM,KAAK,OAAO,MAAM;AAGxB,OAAK,SACF,KAAK,kBAAkB,YAAY;GAClC;GACA,OAAO,IAAI,MAAM,OAAO,WAAW,aAAa;GAChD;GACD,CAAC,CACD,YAAY,GAAG;AAGlB,OAAK,cAAc;;CAGrB,SAAS,OAAqB;EAC5B,MAAM,aAAa,KAAK,aAAa,IAAI,MAAM;AAC/C,MAAI,WACF,MAAK,gBAAgB,OAAO,WAAW;AAEzC,OAAK,SAAS,OAAO,MAAM;;CAG7B,MAAM,SAAS,OAAe,OAAkC;EAE9D,MAAM,MAAM,KAAK,SAAS,IAAI,MAAM;AACpC,MAAI,CAAC,IACH;AAIF,MAAI,YAAY;EAGhB,MAAM,aAAa,KAAK,aAAa,IAAI,MAAM;AAC/C,MAAI,WACF,MAAK,gBAAgB,OAAO,WAAW;AAIzC,OAAK,SAAS,OAAO,MAAM;AAC3B,OAAK,gBAAgB,OAAO,MAAM;AAGlC,MAAI,MACF,KAAI,aAAa,KAAK,MAAM;EAI9B,MAAM,aAAkB;GACtB,GAAG;GACH,aAAa,IAAI,cAAc,KAAK;GACpC,WAAW;GACZ;AAGD,QAAM,KAAK,QAAQ,WAAW;;;;;CAMhC,eAA6B;AAC3B,MAAI,KAAK,aAAa,KAAK,mBAAmB;GAC5C,MAAM,WAAW,KAAK;AACtB,QAAK,oBAAoB,KAAA;AACzB,aAAU;;;;;;CAOd,IAAI,YAAqB;EAEvB,MAAM,iBACJ,KAAK,OAAO,OAAO,KACnB,MAAM,KAAK,KAAK,OAAO,QAAQ,CAAC,CAAC,MAAM,MAAM,EAAE,SAAS,EAAE;EAC5D,MAAM,mBACJ,KAAK,aAAa,OAAO,KACzB,MAAM,KAAK,KAAK,aAAa,QAAQ,CAAC,CAAC,MAAM,QAAQ,IAAI,OAAO,EAAE;AAEpE,SAAO,CAAC,kBAAkB,CAAC;;;;;;CAO7B,MAAM,WAA8B;AAClC,OAAK,YAAY;AACjB,OAAK,oBAAoB;AAGzB,OAAK,cAAc;;;;;CAMrB,UAAgB;AACd,OAAK,YAAY;AACjB,OAAK,oBAAoB,KAAA;;;;;CAM3B,QAAc;AACZ,OAAK,eAAe;;;;;CAMtB,MAAM,SAAwB;AAC5B,OAAK,eAAe;AAEpB,OAAK,MAAM,GAAG,UAAU,KAAK,OAAO,SAAS,CAC3C,KAAI,MAAM,SAAS,GAAG;GACpB,MAAM,MAAM,MAAM;AAClB,SAAM,KAAK,SAAS,KAAK,gBAAgB,eAAe;IACtD,YAAY,IAAI;IAChB,OAAO,IAAI;IACX,QAAQ,IAAI;IACZ,OAAO,IAAI;IACZ,CAAC;;;;;;CAQR,IAAI,SAAkB;AACpB,SAAO,KAAK;;;;;CAMd,iBAAwB;EACtB,MAAM,OAAc,EAAE;AACtB,OAAK,MAAM,SAAS,KAAK,OAAO,QAAQ,CACtC,MAAK,KAAK,GAAG,MAAM;AAErB,SAAO;;;;;CAMT,qBAA+C;AAC7C,SAAO,IAAI,IACT,MAAM,KAAK,KAAK,aAAa,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CACzE;;;;;CAMH,OAAO,OAAgC;AACrC,SAAO,KAAK,SAAS,IAAI,MAAM;;;;;;;;;;;ACpoBnC,IAAa,wBAAb,MAAqE;CACnE,gCAAwB,IAAI,KAA4B;CACxD,mCAA2B,IAAI,KAAa;CAC5C,gBAAyD;CAEzD,YACE,UACA,QACA;AAFQ,OAAA,WAAA;AACA,OAAA,SAAA;;;;;;;CAQV,iBAAiB,MAAsC;AACrD,OAAK,gBAAgB;;CAGvB,MAAM,kBAAkB,cAAqC;AAC3D,MAAI;AACF,QAAK,SAAS,UAAU,aAAa;AACrC;WACO,OAAO;AACd,OAAI,CAAC,oBAAoB,QAAQ,MAAM,CACrC,OAAM;;AAIV,MAAI,KAAK,iBAAiB,IAAI,aAAa,CACzC,OAAM,IAAI,MACR,kDAAkD,eACnD;EAGH,MAAM,WAAW,KAAK,cAAc,IAAI,aAAa;AACrD,MAAI,SACF,QAAO;EAGT,MAAM,eAAe,YAAY;AAC/B,OAAI;IACF,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,aAAa;IACnD,MAAM,CAAC,UAAU,KAAK,SAAS,gBAAgB,OAAO;AACtD,QACE,OAAO,WAAW,WAClB,CAAC,qBAAqB,QAAQ,OAAO,MAAM,CAE3C,OAAM,OAAO;AAEf,UAAM,KAAK,oBAAoB,aAAa;YACrC,OAAO;AACd,SAAK,iBAAiB,IAAI,aAAa;AACvC,UAAM;aACE;AACR,SAAK,cAAc,OAAO,aAAa;;MAEvC;AAEJ,OAAK,cAAc,IAAI,cAAc,YAAY;AACjD,SAAO;;CAGT,MAAc,oBAAoB,cAAqC;AACrE,MAAI,CAAC,KAAK,iBAAiB,CAAC,KAAK,OAAO,YACtC;EAEF,MAAM,QAAQ,MAAM,KAAK,OAAO,YAAY,aAAa;AACzD,MAAI,CAAC,MACH;AAEF,QAAM,KAAK,cAAc,MAAM;;;;;;;;AASnC,IAAa,4BAAb,MAAyE;CACvE,YAAY,UAA2C;AAAnC,OAAA,WAAA;;CAEpB,kBAAkB,cAAqC;AACrD,MAAI,KAAK,SACP,KAAI;AACF,QAAK,SAAS,UAAU,aAAa;AACrC,UAAO,QAAQ,SAAS;UAClB;AAKV,SAAO,QAAQ,OAAO,IAAI,oBAAoB,aAAa,CAAC;;;;;;;;;AC9GhE,IAAa,kCAAb,MAAkF;CAChF,YAAY,OAAgB,SAAyC;EACnE,MAAM,eAAe,yBAAyB,QAAQ,UAAU,IAAI,QAAQ,eAAe;AAE3F,MAAI,iBAAiB,OAAO;GAE1B,MAAM,gCAAgB,IAAI,MAAM,GAAG,aAAa,IAAI,MAAM,UAAU;AACpE,iBAAc,QAAQ;AACtB,iBAAc,QAAQ,MAAM;AAC5B,SAAM;QAGN,OAAM,IAAI,MAAM,GAAG,aAAa,IAAI,OAAO,MAAM,GAAG;;;;;ACO1D,IAAa,6BAAb,MAA+E;CAC7E,uCAA+B,IAAI,KAGhC;CACH,uCAA+B,IAAI,KAGhC;CACH,uCAA+B,IAAI,KAGhC;CACH,4CAAoC,IAAI,KAGrC;CAEH,sBAA8B;CAC9B;CAEA,YAAY,cAAyC;AACnD,OAAK,eAAe;;CAGtB,kBACE,UACA,QACY;EACZ,MAAM,KAAK,WAAW,EAAE,KAAK;AAC7B,OAAK,qBAAqB,IAAI,IAAI;GAAE;GAAI;GAAU;GAAQ,CAAC;AAE3D,eAAa;AACX,QAAK,qBAAqB,OAAO,GAAG;;;CAIxC,kBACE,UACA,QACY;EACZ,MAAM,KAAK,WAAW,EAAE,KAAK;AAC7B,OAAK,qBAAqB,IAAI,IAAI;GAAE;GAAI;GAAU;GAAQ,CAAC;AAE3D,eAAa;AACX,QAAK,qBAAqB,OAAO,GAAG;;;CAIxC,uBACE,UACA,QACA,MACY;EACZ,MAAM,KAAK,WAAW,EAAE,KAAK;AAC7B,OAAK,qBAAqB,IAAI,IAAI;GAAE;GAAI;GAAU;GAAQ;GAAM,CAAC;AAEjE,eAAa;AACX,QAAK,qBAAqB,OAAO,GAAG;;;CAIxC,sBACE,UACA,QACY;EACZ,MAAM,KAAK,gBAAgB,EAAE,KAAK;AAClC,OAAK,0BAA0B,IAAI,IAAI;GAAE;GAAI;GAAU;GAAQ,CAAC;AAEhE,eAAa;AACX,QAAK,0BAA0B,OAAO,GAAG;;;;;;CAO7C,uBACE,aACA,eACA,WACM;EACN,MAAM,SAA+B;GACnC,SAAS;GACT,SAAS;IAAE,QAAQ;IAAI,OAAO,YAAY;IAAQ;GACnD;AAED,OAAK,MAAM,gBAAgB,KAAK,qBAAqB,QAAQ,EAAE;GAC7D,MAAM,cAAc,KAAK,kBACvB,aACA,aAAa,QACb,eACA,UACD;AAED,OAAI,YAAY,SAAS,EACvB,KAAI;AACF,iBAAa,SAAS;KACpB,GAAG;KACH,SAAS;KACV,CAAC;YACK,OAAO;AACd,SAAK,aAAa,YAAY,OAAO;KACnC,WAAW;KACX,gBAAgB,aAAa;KAC7B,WAAW;KACZ,CAAC;;;;;;;CASV,uBACE,aACA,eACA,WACM;AACN,OAAK,MAAM,gBAAgB,KAAK,qBAAqB,QAAQ,EAAE;GAC7D,MAAM,cAAc,KAAK,kBACvB,aACA,aAAa,QACb,eACA,UACD;AAED,OAAI,YAAY,SAAS,EACvB,KAAI;AACF,iBAAa,SAAS,YAAY;YAC3B,OAAO;AACd,SAAK,aAAa,YAAY,OAAO;KACnC,WAAW;KACX,gBAAgB,aAAa;KAC7B,WAAW;KACZ,CAAC;;;;;;;CASV,uBAAuB,WAA+B;EACpD,MAAM,SAAmC;GACvC,SAAS;GACT,SAAS;IAAE,QAAQ;IAAI,OAAO,UAAU;IAAQ;GACjD;AAED,OAAK,MAAM,gBAAgB,KAAK,qBAAqB,QAAQ,EAAE;GAC7D,MAAM,eAAe,KAAK,gBAAgB,WAAW,aAAa,OAAO;AAEzE,OAAI,aAAa,SAAS,EACxB,KAAI;AACF,iBAAa,SAAS;KACpB,GAAG;KACH,SAAS;KACV,CAAC;YACK,OAAO;AACd,SAAK,aAAa,YAAY,OAAO;KACnC,WAAW;KACX,gBAAgB,aAAa;KAC7B,WAAW;KACZ,CAAC;;;;;;;CASV,0BACE,UACA,SACA,YACA,WACM;AACN,OAAK,MAAM,gBAAgB,KAAK,0BAA0B,QAAQ,CAChE,KACE,KAAK,0BACH,UACA,SACA,WACA,aAAa,OACd,CAED,KAAI;AACF,gBAAa,SAAS,UAAU,SAAS,WAAW;WAC7C,OAAO;AACd,QAAK,aAAa,YAAY,OAAO;IACnC,WAAW;IACX,gBAAgB,aAAa;IAC7B,WAAW;KAAE;KAAU;KAAS;KAAY;IAC7C,CAAC;;;;;;CASV,WAAiB;AACf,OAAK,qBAAqB,OAAO;AACjC,OAAK,qBAAqB,OAAO;AACjC,OAAK,qBAAqB,OAAO;AACjC,OAAK,0BAA0B,OAAO;;CAGxC,kBACE,aACA,QACA,eACA,WACU;AACV,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,YAAY,QAAQ,OAAO;AAChC,OAAI,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,GAAG,CAAE,QAAO;AAEnD,OAAI,OAAO,QAAQ;QACD,cAAc,IAAI,GAAG,KACrB,OAAO,KAAM,QAAO;;AAGtC,OAAI,OAAO,YAAY;QACJ,UAAU,IAAI,GAAG,KACjB,OAAO,SAAU,QAAO;;AAG3C,UAAO;IACP;;CAGJ,gBACE,WACA,QACc;AACd,MAAI,CAAC,OAAQ,QAAO;AAEpB,SAAO,UAAU,QAAQ,QAAQ;AAC/B,OAAI,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,IAAI,OAAO,GAAG,CAAE,QAAO;AAC9D,OAAI,OAAO,QAAQ,IAAI,OAAO,iBAAiB,OAAO,KAAM,QAAO;AACnE,OAAI,OAAO,SAAS,CAAC,OAAO,MAAM,SAAS,IAAI,OAAO,KAAK,CAAE,QAAO;AAEpE,UAAO;IACP;;CAGJ,0BACE,UACA,SACA,WACA,QACS;AACT,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI,OAAO,YAAY,aAAa,OAAO,SAAU,QAAO;AAC5D,MAAI,OAAO,OAAO,CAAC,OAAO,IAAI,SAAS,QAAQ,CAAE,QAAO;AACxD,MAAI,OAAO,QAAQ,aAAa,cAAc,OAAO,KAAM,QAAO;AAElE,SAAO;;;;;;;;;;;;ACvRX,IAAa,oCAAb,MAAqE;CACnE,OAAgB;CAEhB,YACE,qBACA,cACA;AAFQ,OAAA,sBAAA;AACA,OAAA,eAAA;;CAGV,MAAM,gBAAgB,YAAmD;AACvE,MAAI,WAAW,WAAW,EAAG;EAE7B,MAAM,UAAoB,EAAE;EAC5B,MAAM,UAAoB,EAAE;EAC5B,MAAM,6BAAa,IAAI,KAAa;EACpC,MAAM,gCAAgB,IAAI,KAAqB;EAC/C,MAAM,4BAAY,IAAI,KAA4B;AAElD,OAAK,MAAM,QAAQ,YAAY;GAC7B,MAAM,EAAE,WAAW,YAAY;GAC/B,MAAM,aAAa,UAAU,OAAO;AAEpC,iBAAc,IAAI,QAAQ,YAAY,QAAQ,aAAa;AAE3D,OAAI,eAAe,kBACjB,SAAQ,KAAK,QAAQ,WAAW;YACvB,eAAe,mBAAmB;IAE3C,MAAM,YADQ,UAAU,OAAO,MACP,cAAc,QAAQ;AAC9C,YAAQ,KAAK,UAAU;cACd,eAAe,oBAAoB;IAC5C,MAAM,QAAQ,UAAU,OAAO;AAK/B,SAAK,oBAAoB,0BACvB,MAAM,UACN,MAAM,UACN,uBAAuB,OACvB,MAAM,UACP;cACQ,eAAe,uBAAuB;IAC/C,MAAM,QAAQ,UAAU,OAAO;AAK/B,SAAK,oBAAoB,0BACvB,MAAM,UACN,MAAM,UACN,uBAAuB,SACvB,MAAM,UACP;cAEG,CAAC,QAAQ,SAAS,QAAQ,WAAW,CACvC,YAAW,IAAI,QAAQ,WAAW;;AAKxC,MAAI,QAAQ,SAAS,EACnB,MAAK,oBAAoB,uBACvB,SACA,eACA,UACD;AAGH,MAAI,QAAQ,SAAS,EACnB,MAAK,oBAAoB,uBACvB,SACA,eACA,UACD;AAGH,MAAI,WAAW,OAAO,KAAK,KAAK,cAAc;GAC5C,MAAM,YAAY,MAAM,QAAQ,IAC9B,MAAM,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,aAAc,IAAI,GAAG,CAAC,CAC/D;AACD,QAAK,oBAAoB,uBAAuB,UAAU;;;;;;AC3FhE,IAAY,gBAAL,yBAAA,eAAA;AACL,eAAA,aAAA;AACA,eAAA,iBAAA;;KACD;;;;;;;AAeD,IAAY,eAAL,yBAAA,cAAA;AACL,cAAA,UAAA;AACA,cAAA,YAAA;;KACD;AA2CD,IAAY,sBAAL,yBAAA,qBAAA;AACL,qBAAA,oBAAA,aAAA,MAAA;AACA,qBAAA,oBAAA,sBAAA,KAAA;AACA,qBAAA,oBAAA,sBAAA,KAAA;AACA,qBAAA,oBAAA,aAAA,KAAA;AACA,qBAAA,oBAAA,WAAA,KAAA;;KACD;AAED,IAAY,qBAAL,yBAAA,oBAAA;AACL,oBAAA,UAAA;AACA,oBAAA,aAAA;AACA,oBAAA,WAAA;AACA,oBAAA,YAAA;;KACD;;;;;;AAmED,MAAa,iBAAiB;CAC5B,cAAc;CACd,gBAAgB;CAChB,aAAa;CACb,mBAAmB;CACnB,0BAA0B;CAC3B;;;AC3GD,IAAa,wBAAb,cAA2C,MAAM;CAC/C;CAEA,YAAY,QAAiB;EAC3B,MAAM,WAAW,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;AACxD,QACE,gCAAgC,OAAO,OAAO,aAAa,WAC5D;AACD,OAAK,OAAO;AACZ,OAAK,SAAS;;;AAIlB,IAAa,UAAb,MAAyC;CACvC,2BAA+C,IAAI,KAAK;CACxD,iBAA4C,EAAE;CAC9C,mBAA8C,EAAE;CAChD,SAA0B;CAC1B,cAAuC,EAAE;CACzC,gBAAyC,EAAE;CAE3C,OAAuB;CACvB,iBAAiC;CAEjC,KAAK,YAAoB;AACvB,OAAK,OAAO,KAAK,iBAAiB;;CAGpC,eAAe,SAAuB;AACpC,OAAK,iBAAiB,KAAK,IAAI,KAAK,gBAAgB,QAAQ;;CAG9D,IAAI,QAAsC;AACxC,SAAO,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC;;CAG3C,IAAI,aAAqB;AACvB,SAAO,KAAK;;CAGd,IAAI,gBAAwB;AAC1B,SAAO,KAAK;;CAGd,IAAI,IAAuC;AACzC,SAAO,KAAK,SAAS,IAAI,GAAG;;CAG9B,IAAI,GAAG,OAA8B;AACnC,OAAK,MAAM,QAAQ,OAAO;AACxB,QAAK,SAAS,IAAI,KAAK,IAAI,KAAK;AAGhC,QAAK,MAAM,MAAM,KAAK,WACpB,MAAK,iBAAiB,KAAK,IAAI,KAAK,gBAAgB,GAAG,QAAQ,QAAQ;AAIzE,QAAK,IAAI,QAAQ,GAAG,SAAS;AAC3B,QAAI,SAAS,oBAAoB,QAC/B,MAAK,MAAM,MAAM,OAAO,WACtB,MAAK,OAAO,KAAK,IAAI,KAAK,MAAM,GAAG,QAAQ,QAAQ;KAGvD;;AAGJ,MAAI,KAAK,QAAQ;AACf,QAAK,YAAY,KAAK,GAAG,MAAM;AAC/B;;EAGF,MAAM,YAAY,CAAC,GAAG,KAAK,eAAe;EAC1C,MAAM,SAAkB,EAAE;AAC1B,OAAK,MAAM,YAAY,UACrB,KAAI;AACF,YAAS,MAAM;WACR,OAAO;AACd,UAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAAC;;AAG1E,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,sBAAsB,OAAO;;CAI3C,OAAO,GAAG,OAA8B;AACtC,OAAK,MAAM,QAAQ,MACjB,MAAK,SAAS,OAAO,KAAK,GAAG;AAG/B,MAAI,KAAK,QAAQ;AACf,QAAK,cAAc,KAAK,GAAG,MAAM;AACjC;;EAGF,MAAM,YAAY,CAAC,GAAG,KAAK,iBAAiB;EAC5C,MAAM,SAAkB,EAAE;AAC1B,OAAK,MAAM,YAAY,UACrB,KAAI;AACF,YAAS,MAAM;WACR,OAAO;AACd,UAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAAC;;AAG1E,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,sBAAsB,OAAO;;CAI3C,QAAQ,UAAiC;AACvC,OAAK,eAAe,KAAK,SAAS;;CAGpC,UAAU,UAAiC;AACzC,OAAK,iBAAiB,KAAK,SAAS;;CAGtC,QAAc;AACZ,OAAK,SAAS;;CAGhB,SAAe;AACb,OAAK,SAAS;AACd,OAAK,OAAO;;CAGd,QAAc;AACZ,MAAI,KAAK,YAAY,SAAS,GAAG;GAC/B,MAAM,QAAQ,KAAK,YAAY,OAAO,EAAE;GACxC,MAAM,YAAY,CAAC,GAAG,KAAK,eAAe;GAC1C,MAAM,SAAkB,EAAE;AAC1B,QAAK,MAAM,YAAY,UACrB,KAAI;AACF,aAAS,MAAM;YACR,OAAO;AACd,WAAO,KACL,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAC1D;;AAGL,OAAI,OAAO,SAAS,EAClB,OAAM,IAAI,sBAAsB,OAAO;;AAI3C,MAAI,KAAK,cAAc,SAAS,GAAG;GACjC,MAAM,QAAQ,KAAK,cAAc,OAAO,EAAE;GAC1C,MAAM,YAAY,CAAC,GAAG,KAAK,iBAAiB;GAC5C,MAAM,SAAkB,EAAE;AAC1B,QAAK,MAAM,YAAY,UACrB,KAAI;AACF,aAAS,MAAM;YACR,OAAO;AACd,WAAO,KACL,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAC1D;;AAGL,OAAI,OAAO,SAAS,EAClB,OAAM,IAAI,sBAAsB,OAAO;;;CAK7C,WAAoB;AAClB,SAAO,KAAK;;;;;AC3MhB,IAAa,kBAAb,MAAiD;CAC/C,2BAA+C,IAAI,KAAK;CACxD,iBAA4C,EAAE;CAC9C,mBAA8C,EAAE;CAChD,cAAuC,EAAE;CACzC,gBAAyC,EAAE;CAC3C,aAA2D;CAC3D,eAA6D;CAC7D;CACA;CACA,SAA0B;CAE1B,OAAuB;CACvB,iBAAiC;CAEjC,YAAY,cAAsB,WAAmB;AACnD,OAAK,eAAe;AACpB,OAAK,YAAY;;CAGnB,KAAK,YAAoB;AACvB,OAAK,OAAO,KAAK,iBAAiB;;CAGpC,eAAe,SAAuB;AACpC,OAAK,iBAAiB,KAAK,IAAI,KAAK,gBAAgB,QAAQ;;CAG9D,IAAI,QAAsC;AACxC,SAAO,MAAM,KAAK,KAAK,SAAS,QAAQ,CAAC;;CAG3C,IAAI,aAAqB;AACvB,SAAO,KAAK;;CAGd,IAAI,gBAAwB;AAC1B,SAAO,KAAK;;CAGd,IAAI,IAAuC;AACzC,SAAO,KAAK,SAAS,IAAI,GAAG;;CAG9B,IAAI,GAAG,OAA8B;AACnC,OAAK,MAAM,QAAQ,OAAO;AACxB,QAAK,SAAS,IAAI,KAAK,IAAI,KAAK;AAGhC,QAAK,MAAM,MAAM,KAAK,WACpB,MAAK,iBAAiB,KAAK,IAAI,KAAK,gBAAgB,GAAG,QAAQ,QAAQ;AAIzE,QAAK,IAAI,QAAQ,GAAG,SAAS;AAC3B,QAAI,SAAS,oBAAoB,QAC/B,MAAK,MAAM,MAAM,OAAO,WACtB,MAAK,OAAO,KAAK,IAAI,KAAK,MAAM,GAAG,QAAQ,QAAQ;KAGvD;;AAEJ,OAAK,YAAY,KAAK,GAAG,MAAM;AAE/B,MAAI,KAAK,OACP;AAGF,MAAI,KAAK,YAAY,UAAU,KAAK,UAClC,MAAK,YAAY;MAEjB,MAAK,oBAAoB;;CAI7B,OAAO,GAAG,OAA8B;AACtC,OAAK,MAAM,QAAQ,MACjB,MAAK,SAAS,OAAO,KAAK,GAAG;AAE/B,OAAK,cAAc,KAAK,GAAG,MAAM;AAEjC,MAAI,KAAK,OACP;AAGF,MAAI,KAAK,cAAc,UAAU,KAAK,UACpC,MAAK,cAAc;MAEnB,MAAK,sBAAsB;;CAI/B,QAAQ,UAAiC;AACvC,OAAK,eAAe,KAAK,SAAS;;CAGpC,UAAU,UAAiC;AACzC,OAAK,iBAAiB,KAAK,SAAS;;CAGtC,QAAc;AACZ,OAAK,SAAS;AACd,MAAI,KAAK,eAAe,MAAM;AAC5B,gBAAa,KAAK,WAAW;AAC7B,QAAK,aAAa;;AAEpB,MAAI,KAAK,iBAAiB,MAAM;AAC9B,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;;CAIxB,SAAe;AACb,OAAK,SAAS;AACd,MAAI,KAAK,YAAY,SAAS,EAC5B,MAAK,oBAAoB;AAE3B,MAAI,KAAK,cAAc,SAAS,EAC9B,MAAK,sBAAsB;;CAI/B,WAAoB;AAClB,SAAO,KAAK;;CAGd,QAAc;AACZ,OAAK,YAAY;AACjB,OAAK,cAAc;;CAGrB,qBAAmC;AACjC,MAAI,KAAK,eAAe,KACtB,cAAa,KAAK,WAAW;AAE/B,OAAK,aAAa,iBAAiB;AACjC,QAAK,YAAY;KAChB,KAAK,aAAa;;CAGvB,uBAAqC;AACnC,MAAI,KAAK,iBAAiB,KACxB,cAAa,KAAK,aAAa;AAEjC,OAAK,eAAe,iBAAiB;AACnC,QAAK,cAAc;KAClB,KAAK,aAAa;;CAGvB,aAA2B;AACzB,MAAI,KAAK,eAAe,MAAM;AAC5B,gBAAa,KAAK,WAAW;AAC7B,QAAK,aAAa;;EAGpB,MAAM,QAAQ,KAAK;AACnB,OAAK,cAAc,EAAE;AAErB,MAAI,MAAM,SAAS,EACjB,MAAK,gBAAgB,KAAK,gBAAgB,MAAM;;CAIpD,eAA6B;AAC3B,MAAI,KAAK,iBAAiB,MAAM;AAC9B,gBAAa,KAAK,aAAa;AAC/B,QAAK,eAAe;;EAGtB,MAAM,QAAQ,KAAK;AACnB,OAAK,gBAAgB,EAAE;AAEvB,MAAI,MAAM,SAAS,EACjB,MAAK,gBAAgB,KAAK,kBAAkB,MAAM;;CAItD,gBACE,WACA,OACM;EACN,MAAM,gBAAgB,CAAC,GAAG,UAAU;EACpC,MAAM,SAAkB,EAAE;AAE1B,OAAK,MAAM,YAAY,cACrB,KAAI;AACF,YAAS,MAAM;WACR,OAAO;AACd,UAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAAC;;AAI1E,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,sBAAsB,OAAO;;;;;AChM7C,IAAa,sBAAb,cAAyC,MAAM;CAC7C;CACA;CAEA,YACE,SACA,UACA,YACA;AACA,QAAM,QAAQ;AACd,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,aAAa;;;AAItB,IAAa,sBAAb,cAAyC,MAAM;CAC7C,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;AAIhB,IAAa,eAAb,cAAkC,MAAM;CACtC;CACA;CAEA,YAAY,QAA4B,OAAc;AACpD,QAAM,gBAAgB,OAAO,KAAK,MAAM,UAAU;AAClD,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,QAAQ;;;;;AC9BjB,IAAa,8BAAb,cAAiD,MAAM;CACrD;CAEA,YAAY,QAAiB;EAC3B,MAAM,WAAW,OAAO,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK;AACxD,QACE,sCAAsC,OAAO,OAAO,aAAa,WAClE;AACD,OAAK,OAAO;AACZ,OAAK,SAAS;;;AAIlB,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,iBAAiB;CACjB,eAAe;CAEf,YAA2C,EAAE;CAE7C,YACE,IACA,OACA,iBACA,YACA,YACA,QACA,QACA,YACA;AACA,OAAK,KAAK;AACV,OAAK,QAAQ;AACb,OAAK,kBAAkB;AACvB,OAAK,aAAa;AAClB,OAAK,aAAa;AAClB,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,aAAa;AAClB,OAAK,SAAS,oBAAoB;;CAGpC,GAAG,UAA6C;AAC9C,OAAK,UAAU,KAAK,SAAS;;CAG/B,UAAgB;AACd,OAAK,WAAW,oBAAoB,iBAAiB;;CAGvD,cAAoB;AAClB,OAAK,WAAW,oBAAoB,iBAAiB;;CAGvD,WAAiB;AACf,OAAK,WAAW,oBAAoB,QAAQ;;CAG9C,OAAO,OAA2B;AAChC,OAAK,QAAQ;AACb,OAAK,WAAW,oBAAoB,MAAM;;CAG5C,WAAW,MAAiC;EAC1C,MAAM,OAAO,KAAK;AAClB,MAAI,QAAQ,KACV;AAEF,OAAK,SAAS;EACd,MAAM,SAAkB,EAAE;AAC1B,OAAK,MAAM,YAAY,KAAK,UAC1B,KAAI;AACF,YAAS,MAAM,MAAM,KAAK;WACnB,OAAO;AACd,UAAO,KAAK,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAAC;;AAG1E,MAAI,OAAO,SAAS,EAClB,OAAM,IAAI,4BAA4B,OAAO;;;;;;;;AC1EnD,SAAgB,qBACd,SACA,OACM;CACN,MAAM,WAA4B,EAAE;AAMpC,MAAK,MAAM,UAAU,MAAM,QACzB,MAAK,MAAM,QAAQ,QAAQ,MACzB,KAAI,OAAO,MAAM,UAAU,KAAK,OAAO;AACrC,WAAS,KAAK,KAAK;AACnB;;AAKN,KAAI,SAAS,SAAS,GAAG;AACvB,OAAK,MAAM,UAAU,SACnB,QAAO,UAAU;AAGnB,UAAQ,OAAO,GAAG,SAAS;;;;;;AAO/B,SAAgB,0BACd,SACA,YACA;CACA,MAAM,WAA4B,EAAE;AAMpC,MAAK,MAAM,UAAU,QAAQ,OAAO;EAClC,IAAI,aAAa;AACjB,OAAK,MAAM,MAAM,OAAO,WACtB,cAAa,KAAK,IAAI,YAAY,GAAG,QAAQ,QAAQ;AAGvD,MAAI,cAAc,WAChB,UAAS,KAAK,OAAO;;AAIzB,KAAI,SAAS,SAAS,GAAG;AACvB,OAAK,MAAM,UAAU,SACnB,QAAO,UAAU;AAGnB,UAAQ,OAAO,GAAG,SAAS;;;;;;;;;;AAW/B,SAAgB,iBACd,YACA,QACwB;AACxB,QAAO,WAAW,QAAQ,OAAO;AAC/B,MAAI,OAAO,UAAU,GAAG,QAAQ,WAAW,OAAO,OAChD,QAAO;AAGT,MACE,OAAO,WAAW,SAAS,KAC3B,CAAC,OAAO,WAAW,SAAS,GAAG,QAAQ,WAAW,CAElD,QAAO;AAGT,MAAI,OAAO,MAAM,SAAS,KAAK,CAAC,OAAO,MAAM,SAAS,GAAG,QAAQ,MAAM,CACrE,QAAO;AAGT,SAAO;GACP;;;;;;;AAQJ,SAAgB,mBAAkC;AAChD,QAAO;EACL,OAAO;EACP,cAAc;EACf;;;;;;;;;;;;;;AAeH,SAAgB,0BACd,YACkB;CAClB,MAAM,UAA4B,EAAE;CAEpC,IAAI,eAA8B;CAClC,IAAI,eAA8B;CAClC,IAAI,eAAuC,EAAE;CAE7C,MAAM,mBAAmB;AACvB,MACE,aAAa,WAAW,KACxB,iBAAiB,QACjB,iBAAiB,KAEjB;AAGF,UAAQ,KAAK;GACX,YAAY;GACZ,QAAQ,aAAa,GAAG,QAAQ;GAChC,OAAO;GACP,YAAY;GACb,CAAC;AACF,iBAAe,EAAE;;AAGnB,MAAK,MAAM,MAAM,YAAY;EAC3B,MAAM,QAAQ,GAAG,QAAQ;EACzB,MAAM,QAAQ,GAAG,QAAQ;AACzB,MAAI,UAAU,gBAAgB,UAAU,cAAc;AACpD,eAAY;AACZ,kBAAe;AACf,kBAAe;;AAEjB,eAAa,KAAK,GAAG;;AAGvB,aAAY;AACZ,QAAO;;;;;;;;;;;;;AAcT,SAAgB,8BACd,YACiE;AACjE,KAAI,WAAW,WAAW,EACxB,QAAO;EAAE,MAAM,EAAE;EAAE,OAAO,EAAE;EAAE;CAGhC,MAAM,OAAO,WAAW,WAAW,SAAS;CAC5C,MAAM,YAAY,KAAK,QAAQ;CAC/B,MAAM,aAAa,KAAK,QAAQ;CAChC,MAAM,YAAY,KAAK,QAAQ;CAC/B,MAAM,SAAS,KAAK,UAAU;CAE9B,IAAI,aAAa,WAAW;AAC5B,MAAK,IAAI,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;EAC/C,MAAM,KAAK,WAAW;AACtB,MACE,GAAG,QAAQ,eAAe,aAC1B,GAAG,QAAQ,WAAW,cACtB,GAAG,QAAQ,UAAU,aACrB,GAAG,UAAU,mBAAmB,OAEhC,cAAa;MAEb;;AAIJ,QAAO;EACL,MAAM,WAAW,MAAM,GAAG,WAAW;EACrC,OAAO,WAAW,MAAM,WAAW;EACpC;;AA4BH,SAAgB,uBACd,OACsB;AACtB,QAAO;EACL,WAAW;GACT,IAAI,MAAM;GACV,OAAO,MAAM;GACb,MAAM,MAAM;GACZ,MAAM,MAAM;GACZ,gBAAgB,MAAM;GACtB,QAAQ,MAAM;GACf;EACD,SAAS;GACP,YAAY,MAAM;GAClB,cAAc,MAAM;GACpB,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,SAAS,MAAM,WAAW;GAC3B;EACF;;;;;;;;AASH,SAAgB,0BACd,SACiB;AACjB,KAAI,QAAQ,UAAU,EACpB,QAAO;CAIT,MAAM,yBAAS,IAAI,KAGhB;CACH,MAAM,6BAAa,IAAI,KAAqB;CAC5C,MAAM,iBAA6B,EAAE;AAErC,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,MAAgB,GAAG,OAAO,WAAW,GAAG,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,GAAG,OAAO;EAE/F,MAAM,WAAW,OAAO,IAAI,IAAI;AAChC,MAAI,UAAU;AACZ,YAAS,IAAI,KAAK,OAAO;AACzB,OAAI,OAAO,SAAS,OAAO,UAAU,SAAS,eAC5C,YAAW,IAAI,OAAO,OAAO,SAAS,eAAe;SAElD;AACL,UAAO,IAAI,KAAK;IAAE,KAAK,CAAC,OAAO;IAAE,gBAAgB,OAAO;IAAO,CAAC;AAChE,kBAAe,KAAK,IAAI;;;CAI5B,MAAM,SAA0B,EAAE;AAElC,MAAK,MAAM,OAAO,gBAAgB;EAChC,MAAM,QAAQ,OAAO,IAAI,IAAI;EAC7B,MAAM,gBAAgB,MAAM,IACzB,SAAS,OAAO,GAAG,WAAW,CAC9B,MAAM,GAAG,MAAM,EAAE,QAAQ,UAAU,EAAE,QAAQ,QAAQ;EAExD,MAAM,0BAAU,IAAI,KAAa;AACjC,OAAK,MAAM,MAAM,MAAM,IACrB,MAAK,MAAM,OAAO,GAAG,gBACnB,SAAQ,IAAI,IAAI;AAGpB,UAAQ,OAAO,MAAM,eAAe;AACpC,OAAK,MAAM,MAAM,MAAM,IACrB,SAAQ,OAAO,GAAG,MAAM;EAG1B,MAAM,eAAyB,EAAE;AACjC,OAAK,MAAM,OAAO,SAAS;GACzB,MAAM,SAAS,WAAW,IAAI,IAAI,IAAI;AACtC,OAAI,CAAC,aAAa,SAAS,OAAO,IAAI,WAAW,MAAM,eACrD,cAAa,KAAK,OAAO;;EAI7B,MAAM,QAAQ,MAAM,IAAI;EACxB,MAAM,SAAS,IAAI,cACjB,MAAM,IACN,MAAM,OACN,cACA,MAAM,YACN,MAAM,YACN,MAAM,QACN,MAAM,QACN,cACD;AAED,MAAI,MAAM,SAAS,oBAAoB,iBACrC,KAAI,MAAM,UAAU,oBAAoB,MACtC,QAAO,UAAU;WACR,MAAM,UAAU,oBAAoB,QAC7C,QAAO,aAAa;MAEpB,QAAO,SAAS;AAIpB,SAAO,KAAK,OAAO;;AAGrB,QAAO;;;;;;;AAYT,SAAgB,oBACd,OACA,SACO;AACP,KAAI,MAAM,WAAW,EAAG,QAAO,EAAE;AACjC,KAAI,MAAM,UAAU,QAAS,QAAO,CAAC,MAAM;CAG3C,MAAM,SAAS,MAAM,KAAK,GAAG,MAAM,EAAE;CACrC,MAAM,OAAO,IAAI,MAAc,MAAM,OAAO,CAAC,KAAK,EAAE;CAEpD,SAAS,KAAK,GAAmB;AAC/B,SAAO,OAAO,OAAO,GAAG;AACtB,UAAO,KAAK,OAAO,OAAO;AAC1B,OAAI,OAAO;;AAEb,SAAO;;CAGT,SAAS,MAAM,GAAW,GAAiB;EACzC,MAAM,KAAK,KAAK,EAAE;EAClB,MAAM,KAAK,KAAK,EAAE;AAClB,MAAI,OAAO,GAAI;AACf,MAAI,KAAK,MAAM,KAAK,IAClB,QAAO,MAAM;WACJ,KAAK,MAAM,KAAK,IACzB,QAAO,MAAM;OACR;AACL,UAAO,MAAM;AACb,QAAK;;;CAIT,MAAM,+BAAe,IAAI,KAAqB;AAC9C,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,cAAa,IAAI,MAAM,GAAG,OAAO,OAAO,EAAE;AAG5C,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,MAAK,MAAM,OAAO,MAAM,GAAG,OAAO,iBAAiB;EACjD,MAAM,SAAS,aAAa,IAAI,IAAI;AACpC,MAAI,WAAW,KAAA,EACb,OAAM,GAAG,OAAO;;CAMtB,MAAM,+BAAe,IAAI,KAAkB;AAC3C,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,KAAK,EAAE;EACpB,IAAI,YAAY,aAAa,IAAI,KAAK;AACtC,MAAI,CAAC,WAAW;AACd,eAAY,EAAE;AACd,gBAAa,IAAI,MAAM,UAAU;;AAEnC,YAAU,KAAK,MAAM,GAAG;;CAG1B,MAAM,aAAa,CAAC,GAAG,aAAa,QAAQ,CAAC;CAG7C,MAAM,SAAgB,EAAE;CACxB,IAAI,eAAoB,EAAE;AAE1B,MAAK,MAAM,aAAa,YAAY;AAClC,MAAI,UAAU,SAAS,SAAS;AAE9B,OAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,KAAK,aAAa;AACzB,mBAAe,EAAE;;AAGnB,QAAK,MAAM,YAAY,eAAe,WAAW,QAAQ,CACvD,QAAO,KAAK,SAAS;AAEvB;;AAGF,MAAI,aAAa,SAAS,UAAU,SAAS,SAAS;AACpD,OAAI,aAAa,SAAS,EACxB,QAAO,KAAK,aAAa;AAE3B,kBAAe,CAAC,GAAG,UAAU;QAE7B,cAAa,KAAK,GAAG,UAAU;;AAInC,KAAI,aAAa,SAAS,EACxB,QAAO,KAAK,aAAa;AAG3B,QAAO;;;;;;AAOT,SAAS,eACP,OACA,SACO;CAEP,MAAM,8BAAc,IAAI,KAAgB;CACxC,MAAM,yBAAS,IAAI,KAAa;AAChC,MAAK,MAAM,QAAQ,OAAO;AACxB,cAAY,IAAI,KAAK,OAAO,OAAO,KAAK;AACxC,SAAO,IAAI,KAAK,OAAO,MAAM;;CAG/B,MAAM,2BAAW,IAAI,KAAqB;CAC1C,MAAM,4BAAY,IAAI,KAAuB;AAC7C,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,KAAK,OAAO;AACxB,MAAI,CAAC,SAAS,IAAI,IAAI,CAAE,UAAS,IAAI,KAAK,EAAE;AAC5C,MAAI,CAAC,UAAU,IAAI,IAAI,CAAE,WAAU,IAAI,KAAK,EAAE,CAAC;AAC/C,OAAK,MAAM,OAAO,KAAK,OAAO,gBAC5B,KAAI,OAAO,IAAI,IAAI,EAAE;AACnB,YAAS,IAAI,MAAM,SAAS,IAAI,IAAI,IAAI,KAAK,EAAE;AAC/C,OAAI,CAAC,UAAU,IAAI,IAAI,CAAE,WAAU,IAAI,KAAK,EAAE,CAAC;AAC/C,aAAU,IAAI,IAAI,CAAE,KAAK,IAAI;;;CAMnC,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,KAAK,WAAW,SAC1B,KAAI,WAAW,EAAG,OAAM,KAAK,IAAI;CAGnC,MAAM,SAAc,EAAE;AACtB,QAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,OAAO;AACzB,SAAO,KAAK,YAAY,IAAI,IAAI,CAAE;AAClC,OAAK,MAAM,YAAY,UAAU,IAAI,IAAI,IAAI,EAAE,EAAE;GAC/C,MAAM,aAAa,SAAS,IAAI,SAAS,IAAI,KAAK;AAClD,YAAS,IAAI,UAAU,UAAU;AACjC,OAAI,cAAc,EAAG,OAAM,KAAK,SAAS;;;AAK7C,KAAI,OAAO,SAAS,MAAM,QAAQ;EAChC,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,OAAO,MAAM,CAAC;AAClE,OAAK,MAAM,QAAQ,MACjB,KAAI,CAAC,UAAU,IAAI,KAAK,OAAO,MAAM,CACnC,QAAO,KAAK,KAAK;;CAMvB,MAAM,SAAgB,EAAE;AACxB,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,QACtC,QAAO,KAAK,OAAO,MAAM,GAAG,IAAI,QAAQ,CAAC;AAE3C,QAAO;;;;AClgBT,MAAM,iBAAkC;CACtC,YAAY;CACZ,eAAe;CACf,6BAA6B;CAC7B,kBAAkB;CAClB,iBAAiB;CACjB,aAAa;CACd;AAED,SAAgB,sBACd,qBACA,kBACA,iBACA,QACQ;CACR,MAAM,UAAU,KAAK,IACnB,iBACA,mBAAmB,KAAK,IAAI,GAAG,sBAAsB,EAAE,CACxD;AACD,QAAO,UAAU,IAAI,UAAU,UAAU;;;;;;;AAQ3C,IAAa,oBAAb,MAAqD;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,OAAe,SAAmC,EAAE,EAAE;AAChE,OAAK,QAAQ;AACb,OAAK,SAAS;GAAE,GAAG;GAAgB,GAAG;GAAQ;AAC9C,OAAK,UAAU;AACf,OAAK,SAAS,KAAK,OAAO;AAC1B,OAAK,sBAAsB;;CAG7B,YAAY,UAAqC;AAC/C,OAAK,WAAW;;CAGlB,QAAc;AACZ,OAAK,UAAU;AACf,OAAK,sBAAsB;AAC3B,MAAI,CAAC,KAAK,OACR,MAAK,MAAM;;CAIf,OAAa;AACX,OAAK,UAAU;AACf,MAAI,KAAK,OAAO;AACd,gBAAa,KAAK,MAAM;AACxB,QAAK,QAAQ,KAAA;;;CAIjB,OAAqB;AACnB,MAAI,CAAC,KAAK,YAAY,CAAC,KAAK,QAAS;EAErC,MAAM,WAAW,KAAK;AAEjB,OAAK,MACP,WAAW,CACX,MAAM,SAAS;AACd,OAAI,CAAC,KAAK,QAAS;AACnB,OAAI,OAAO,KAAK,OAAO,cACrB,MAAK,6BAA6B;OAE7B,WAAU,CACZ,WAAW;AACV,SAAK,sBAAsB;AAC3B,SAAK,cAAc;KACnB,CACD,YAAY;AACX,SAAK;AACL,SAAK,eAAe;KACpB;IAEN,CACD,YAAY;AAEX,QAAK,cAAc;IACnB;;CAGN,eAA6B;AAC3B,MAAI,CAAC,KAAK,WAAW,KAAK,OAAQ;AAClC,OAAK,QAAQ,iBAAiB,KAAK,MAAM,EAAE,KAAK,OAAO,WAAW;;CAGpE,gBAA8B;AAC5B,MAAI,CAAC,KAAK,WAAW,KAAK,OAAQ;EAClC,MAAM,QAAQ,sBACZ,KAAK,qBACL,KAAK,OAAO,kBACZ,KAAK,OAAO,iBACZ,KAAK,QAAQ,CACd;AACD,OAAK,QAAQ,iBAAiB,KAAK,MAAM,EAAE,MAAM;;CAGnD,8BAA4C;AAC1C,MAAI,CAAC,KAAK,WAAW,KAAK,OAAQ;AAClC,OAAK,QAAQ,iBACL,KAAK,MAAM,EACjB,KAAK,OAAO,4BACb;;CAGH,QAAc;AACZ,OAAK,SAAS;AACd,MAAI,KAAK,OAAO;AACd,gBAAa,KAAK,MAAM;AACxB,QAAK,QAAQ,KAAA;;;CAIjB,SAAe;AACb,OAAK,SAAS;AACd,MAAI,KAAK,QACP,MAAK,cAAc;;CAIvB,aAAmB;AACjB,MAAI,KAAK,WAAW,KAAK,SACvB,MAAK,MAAM;;CAIf,WAAoB;AAClB,SAAO,KAAK;;CAGd,YAAqB;AACnB,SAAO,KAAK;;CAGd,gBAAwB;AACtB,SAAO,KAAK,OAAO;;CAGrB,cAAc,IAAkB;AAC9B,OAAK,OAAO,aAAa;;;;;AC9J7B,IAAI,gBAAgB;;;;AAKpB,SAAgB,gBAAgB,QAAyB;CACvD,MAAM,SAAS,OAAO,SAAS;AAC/B,KAAI,CAAC,QAAQ,WACX,QAAO;AAGT,QAAO;EACL,GAAG;EACH,SAAS;GACP,GAAG,OAAO;GACV,QAAQ;IACN,GAAG;IACH,YAAY,OAAO,WAAW,KAAK,QACjC,MAAM,QAAQ,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG,IACvC;IACF;GACF;EACF;;;;;;;;;AAUH,SAAgB,kBAAkB,UAAiC;AACjE,QAAO;EACL,MAAM,SAAS,KAAK,aAAa;EACjC,aAAa,SAAS;EACtB,YAAY,SAAS,YAAY,KAAK,mBAAmB;GACvD,WAAW;IACT,OAAO,cAAc,UAAU;IAC/B,gBAAgB,cAAc,UAAU;IACxC,MAAM,cAAc,UAAU;IAC9B,MAAM,cAAc,UAAU;IAC9B,OAAO,cAAc,UAAU;IAC/B,IAAI,cAAc,UAAU;IAC5B,QAAQ,gBAAgB,cAAc,UAAU,OAAO;IACxD;GACD,SAAS;IACP,YAAY,cAAc,QAAQ;IAClC,cAAc,cAAc,QAAQ;IACpC,OAAO,cAAc,QAAQ;IAC7B,QAAQ,cAAc,QAAQ;IAC9B,SAAS,cAAc,QAAQ;IAChC;GACF,EAAE;EACH,QAAQ,SAAS;EACjB,KAAK,SAAS;EACd,WAAW,SAAS;EACrB;;;;;;;;AASH,SAAS,qBAAqB,KAAoC;AAChE,KAAI,MAAM,QAAQ,IAAI,CACpB,QAAO;AAET,QAAO,IAAI,MAAM,KAAK;;;;;;;;AASxB,SAAS,+BACP,eACsB;CACtB,MAAM,SAAS,cAAc,UAAU,OAAO,SAAS;AACvD,KAAI,CAAC,QAAQ,cAAc,OAAO,WAAW,WAAW,EACtD,QAAO;CAGT,MAAM,yBAAyB,OAAO,WAAW,IAAI,qBAAqB;CAE1E,MAAM,wBAAmC;EACvC,GAAG,cAAc;EACjB,QAAQ;GACN,GAAG,cAAc,UAAU;GAC3B,SAAS;IACP,GAAG,cAAc,UAAU,OAAO;IAClC,QAAQ;KACN,GAAG;KACH,YAAY;KACb;IACF;GACF;EACF;AAED,QAAO;EACL,GAAG;EACH,WAAW;EACZ;;;;;;;;;;;;;;AA8DH,SAAgB,0BACd,UACA,YACiB;AACjB,KAAI,CAAC,SAAS,cAAc,SAAS,WAAW,WAAW,EACzD,QAAO,EAAE;AAQX,QAFgB,0BAHQ,SAAS,WAAW,IAC1C,+BACD,CACyD,CAE3C,KAAK,UAAU;AAE5B,SAAO,IAAI,cADM,UAAU,SAAS,YAAY,GAAG,GAAG,KAAK,KAAK,CAAC,GAAG,mBAGlE,SAAS,OAAO,KACf,SAAS,aAAa,EAAE,EAAE,OAAO,QAAQ,EAC1C,YACA,MAAM,YACN,CAAC,MAAM,MAAM,EACb,MAAM,QACN,MAAM,WACP;GACD;;AAGJ,MAAa,2BAA2B,YAAqC;CAC3E,IAAI,aAAa;AACjB,MAAK,MAAM,UAAU,QACnB,KAAI,OAAO,WAAW,oBAAoB,QACxC,MAAK,MAAM,MAAM,OAAO,WACtB,cAAa,KAAK,IAAI,YAAY,GAAG,QAAQ,QAAQ;AAI3D,QAAO;;;;;;;ACpKT,IAAa,oBAAb,MAAmD;CACjD;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA,kBAAmC,IAAI,iBAAiB;CACxD;CACA;CACA;CACA;CACA,4BAA4C;CAC5C,6BAA6C;CAC7C,mBAAmC;CACnC,iBAA+D;CAC/D,cAA+B;CAC/B,YAA6B;CAC7B,eAAgC;CAChC,iBAAkC;CAClC,kBAA2C;CAC3C,2CACE,IAAI,KAAK;CAEX,YACE,QACA,WACA,YACA,eACA,QACA,gBACA,WACA;AAPiB,OAAA,SAAA;AAQjB,OAAK,YAAY;AACjB,OAAK,aAAa;AAClB,OAAK,gBAAgB;AACrB,OAAK,iBAAiB;AACtB,OAAK,YAAY;AACjB,OAAK,SAAS;GACZ,KAAK,OAAO;GACZ,YAAY,OAAO;GACnB,SAAS,OAAO;GAChB,cAAc,OAAO;GACrB,QAAQ,OAAO;GACf,kBAAkB,OAAO;GACzB,iBAAiB,OAAO;GACzB;AACD,OAAK,aAAa;AAClB,OAAK,eAAe;AAEpB,OAAK,QAAQ,IAAI,SAAS;AAC1B,OAAK,iBAAiB,IAAI,gBAAgB,KAAK,GAAG;AAClD,OAAK,SAAS,KAAK;AACnB,OAAK,aAAa,IAAI,SAAS;AAE/B,OAAK,WAAW,SAAS,YAAY;AACnC,QAAK,MAAM,UAAU,QACnB,MAAK,OAAO,KACV,oEACA,OAAO,YACP,KAAK,UACN;IAEH;AAGF,OAAK,OAAO,SAAS,YAAY;AAC/B,OAAI,KAAK,WAAY;AACrB,OAAI,KAAK,WAAW;AAClB,SAAK,eAAe;AACpB;;AAEF,OAAI,KAAK,YAAa;AACtB,OAAI,KAAK,gBAAgB;AACvB,SAAK,eAAe;AACpB;;AAEF,QAAK,YAAY,QAAQ;IACzB;AAKF,OAAK,OAAO,WAAW,YAAY;GACjC,MAAM,aAAa,wBAAwB,QAAQ;AACnD,OAAI,aAAa,KAAK,4BAA4B;AAChD,SAAK,6BAA6B;AAClC,SAAK,cACF,OAAO;KACN,YAAY,KAAK;KACjB,YAAY;KACZ,eAAe;KACf,mBAAmB,KAAK,KAAK;KAC9B,CAAC,CACD,OAAO,UAAU;AAChB,UAAK,OAAO,MACV,wMACA,KAAK,WACL,MACD;MACD;;IAEN;AAEF,OAAK,MAAM,WAAW,YAAY;GAChC,MAAM,aAAa,wBAAwB,QAAQ;AACnD,OAAI,aAAa,KAAK,2BAA2B;AAC/C,SAAK,4BAA4B;AACjC,SAAK,cACF,OAAO;KACN,YAAY,KAAK;KACjB,YAAY;KACZ,eAAe;KACf,mBAAmB,KAAK,KAAK;KAC9B,CAAC,CACD,OAAO,UAAU;AAChB,UAAK,OAAO,MACV,8GACA,KAAK,WACL,MACD;MACD;;IAEN;;;;;CAMJ,WAA0B;AACxB,OAAK,gBAAgB,OAAO;AAC5B,OAAK,eAAe,OAAO;AAC3B,OAAK,aAAa;AAClB,OAAK,UAAU,MAAM;AAErB,MAAI,KAAK,gBAAgB;AACvB,gBAAa,KAAK,eAAe;AACjC,QAAK,iBAAiB;;AAGxB,OAAK,0BAA0B,eAAe;AAE9C,SAAO,QAAQ,SAAS;;CAG1B,qBAA8C;AAC5C,SAAO;GACL,OAAO,KAAK;GACZ,cAAc,KAAK;GACnB,kBAAkB,KAAK,oBAAoB;GAC3C,kBAAkB,KAAK,oBAAoB;GAC3C,aAAa,KAAK;GAClB,kBAAkB,KAAK;GACvB,gBAAgB,KAAK;GACtB;;CAGH,wBAAwB,UAAqD;AAC3E,OAAK,yBAAyB,IAAI,SAAS;AAC3C,eAAa;AACX,QAAK,yBAAyB,OAAO,SAAS;;;CAIlD,cAAoB;AAClB,MAAI,KAAK,WAAY;AACrB,OAAK,UAAU,YAAY;;;;;CAM7B,MAAM,OAAsB;EAC1B,MAAM,EAAE,eAAe,MAAM,KAAK,oBAAoB;EAGtD,MAAM,UAAU,MAAM,KAAK,cAAc,KAAK,KAAK,WAAW;EAC9D,MAAM,eACJ,QAAQ,MAAM,MAAM,EAAE,eAAe,QAAQ,EAAE,iBAAiB;EAClE,MAAM,gBACJ,QAAQ,MAAM,MAAM,EAAE,eAAe,SAAS,EAAE,iBAAiB;AACnE,OAAK,MAAM,KAAK,aAAa;AAC7B,OAAK,OAAO,KAAK,cAAc;AAC/B,OAAK,4BAA4B;AACjC,OAAK,6BAA6B;AAElC,MAAI,aAAa,EACf,2BAA0B,KAAK,QAAQ,WAAW;AAGpD,OAAK,UAAU,kBAAkB,KAAK,MAAM,CAAC;AAC7C,OAAK,UAAU,OAAO;AACtB,OAAK,0BAA0B,YAAY;;CAG7C,0BAAkC,MAA6B;AAC7D,MAAI,KAAK,oBAAoB,KAAM;AACnC,OAAK,kBAAkB;EACvB,MAAM,WAAW,KAAK,oBAAoB;AAC1C,OAAK,MAAM,YAAY,KAAK,yBAC1B,KAAI;AACF,YAAS,SAAS;WACX,OAAO;AACd,QAAK,OAAO,MACV,kDACA,MACD;;;;;;CAQP,MAAc,OAAsB;AAClC,MAAI,KAAK,WACP;EAGF,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,KAAK,kBACpB,KAAK,MAAM,YACX,KAAK,MAAM,cACZ;WACM,OAAO;AACd,OAAI,CAAC,KAAK,gBAAgB,MAAM,CAC9B,OAAM;AAER;;EAGF,MAAM,EAAE,WAAW,YAAY,aAAa,YAAY;AAGxD,MAAI,aAAa,EACf,2BAA0B,KAAK,QAAQ,WAAW;EAIpD,MAAM,aAA8B,EAAE;AACtC,OAAK,MAAM,YAAY,UACrB,KAAI,SAAS,KAAK,aAAa,KAAK,gBAAgB,SAAS,YAAY;GACvE,MAAM,UAAU,0BAA0B,UAAU,KAAK,WAAW;AACpE,QAAK,MAAM,UAAU,QACnB,QAAO,aAAa;AAEtB,cAAW,KAAK,GAAG,QAAQ;;EAM/B,MAAM,eACJ,WAAW,SAAS,IAChB,0BAA0B,WAAW,GACrC;AAEN,MAAI,aAAa,SAAS,EACxB,MAAK,MAAM,IAAI,GAAG,aAAa;AAIjC,MAAI,YAAY,SAAS,EACvB,MAAK,wBAAwB,YAAY;AAG3C,MAAI,QACF,MAAK,iBAAiB;WACb,KAAK,gBAAgB;AAC9B,QAAK,iBAAiB;AACtB,QAAK,aAAa;;AAGpB,OAAK,mBAAmB,KAAK,KAAK;AAClC,OAAK,eAAe;AACpB,OAAK,0BAA0B,YAAY;;;;;;CAO7C,wBACE,aAQM;AACN,OAAK,MAAM,MAAM,YACf,MAAK,OAAO,MACV,8EACA,KAAK,WACL,GAAG,YACH,GAAG,MACJ;EAGH,MAAM,UAA2B,EAAE;AACnC,OAAK,MAAM,MAAM,aAAa;GAC5B,MAAM,SAAS,IAAI,cACjB,OAAO,YAAY,EACnB,GAAG,OACH,EAAE,EACF,KAAK,YACL,GAAG,YACH,GAAG,QACH,GAAG,QACH,EAAE,CACH;AACD,UAAO,OACL,IAAI,aAAa,mBAAmB,QAAQ,IAAI,MAAM,GAAG,MAAM,CAAC,CACjE;AACD,WAAQ,KAAK,OAAO;;AAEtB,OAAK,WAAW,IAAI,GAAG,QAAQ;;;;;;CAOjC,gBAAwB,OAAyB;AAC/C,MAAI,KAAK,WAAY,QAAO;EAE5B,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAErE,MAAI,IAAI,QAAQ,SAAS,oBAAoB,EAAE;AAC7C,QAAK,0BAA0B,eAAe;AAC9C,QAAK,4BAA4B;AACjC,UAAO;;EAGT,MAAM,iBAAiB,KAAK,cAAc,IAAI;AAE9C,OAAK;AACL,OAAK,mBAAmB,KAAK,KAAK;EAElC,MAAM,eAAe,IAAI,aAAa,mBAAmB,OAAO,IAAI;AAEpE,OAAK,OAAO,MACV,kEACA,KAAK,cACL,gBACA,aACD;AAED,MAAI,mBAAmB,iBAAiB;AACtC,QAAK,UAAU,MAAM;AACrB,QAAK,0BAA0B,QAAQ;AACvC,UAAO;;AAGT,OAAK,0BAA0B,QAAQ;AACvC,SAAO;;;;;;CAOT,6BAA2C;AACzC,OAAK,OAAO,KACV,gEACA,KAAK,UACN;AAED,OAAK,UAAU,MAAM;EAErB,MAAM,mBAAmB,YAA0B;AACjD,OAAI,KAAK,WAAY;AAEhB,QAAK,oBAAoB,CAC3B,MAAM,EAAE,iBAAiB;AACxB,SAAK,OAAO,KACV,oDACA,KAAK,UACN;AACD,SAAK,eAAe;AACpB,QAAI,aAAa,EACf,2BAA0B,KAAK,QAAQ,WAAW;AAEpD,SAAK,UAAU,OAAO;AACtB,SAAK,0BAA0B,YAAY;KAC3C,CACD,OAAO,kBAA2B;IACjC,MAAM,MACJ,yBAAyB,QACrB,gBACA,IAAI,MAAM,OAAO,cAAc,CAAC;IACtC,MAAM,iBAAiB,KAAK,cAAc,IAAI;AAE9C,SAAK,OAAO,MACV,oFACA,KAAK,WACL,SACA,gBACA,cACD;AAED,SAAK;AACL,SAAK,mBAAmB,KAAK,KAAK;AAElC,QAAI,mBAAmB,iBAAiB;AACtC,UAAK,0BAA0B,QAAQ;AACvC;;AAGF,SAAK,0BAA0B,eAAe;IAC9C,MAAM,QAAQ,sBACZ,SACA,KAAK,OAAO,kBACZ,KAAK,OAAO,iBACZ,KAAK,QAAQ,CACd;AACD,qBAAiB,gBAAgB,UAAU,EAAE,EAAE,MAAM;KACrD;;AAGN,kBAAgB,EAAE;;;;;CAMpB,MAAc,kBACZ,YACA,eAaC;EACD,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2Ed,MAAM,YAAY;GAChB,WAAW,KAAK;GAChB,WAAW;GACX,cAAc;GACf;EAED,MAAM,WAAW,MAAM,KAAK,eAczB,OAAO,UAAU;AAEpB,SAAO;GACL,WAAW,SAAS,kBAAkB;GACtC,YAAY,SAAS,kBAAkB;GACvC,aAAa,SAAS,kBAAkB,eAAe,EAAE;GACzD,SAAS,SAAS,kBAAkB;GACrC;;;;;;CAOH,MAAc,qBAAsD;EAClE,IAAI,sBAAsB;AAC1B,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,eAAe,gCACvC,KAAK,OAAO,aACb;AACD,OAAI,OACF,uBAAsB;UAElB;EAIR,MAAM,WAAW;;;;;;;;EASjB,MAAM,YAAY,EAChB,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,cAAc,KAAK,OAAO;GAC1B,QAAQ;IACN,YAAY,KAAK,OAAO,OAAO;IAC/B,OAAO,KAAK,OAAO,OAAO;IAC1B,QAAQ,KAAK,OAAO,OAAO;IAC5B;GACD;GACD,EACF;EAED,MAAM,OAAO,MAAM,KAAK,eAErB,UAAU,UAAU;AAEvB,MAAI,CAAC,KAAK,aAAa,QACrB,OAAM,IAAI,oBACR,uCACA,UACD;AAGH,SAAO,EAAE,YAAY,KAAK,aAAa,YAAY;;;;;;;;CASrD,YAAoB,SAAgC;AAClD,OAAK,YAAY;AACjB,OAAK,mBAAmB,QAAQ,CAC7B,WAAW;AACV,QAAK,YAAY;AACjB,QAAK,cAAc;AACnB,QAAK,mBAAmB;AACxB,OACE,KAAK,oBAAoB,kBACzB,KAAK,oBAAoB,QAEzB,MAAK,0BAA0B,YAAY;AAE7C,QAAK,aAAa;IAClB,CACD,OAAO,UAAU;AAChB,QAAK,YAAY;AACjB,QAAK,eAAe;AACpB,OAAI,KAAK,WAAY;GAErB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AAGrE,OAFuB,KAAK,cAAc,IAAI,KAEvB,eAAe;AACpC,SAAK;AACL,SAAK,cAAc;AACnB,SAAK,OAAO,MACV,sEACA,KAAK,kBACL,IACD;AACD,SAAK,0BAA0B,eAAe;AAC9C,SAAK,mBAAmB;UACnB;IACL,MAAM,eAAe,IAAI,aAAa,mBAAmB,QAAQ,IAAI;AACrE,SAAK,MAAM,UAAU,QACnB,QAAO,OAAO,aAAa;AAE7B,SAAK,WAAW,IAAI,GAAG,QAAQ;AAC/B,SAAK,OAAO,OAAO,GAAG,QAAQ;AAC9B,SAAK,0BAA0B,QAAQ;;IAEzC;;;;;CAMN,oBAAkC;AAChC,MAAI,KAAK,eAAgB;EAEzB,MAAM,QAAQ,sBACZ,KAAK,kBACL,KAAK,OAAO,kBACZ,KAAK,OAAO,iBACZ,KAAK,QAAQ,CACd;AAED,OAAK,iBAAiB,iBAAiB;AACrC,QAAK,iBAAiB;AAEtB,OAAI,KAAK,WAAY;GAErB,MAAM,WAAW,KAAK,OAAO;AAC7B,OAAI,SAAS,WAAW,GAAG;AACzB,SAAK,cAAc;AACnB,SAAK,mBAAmB;AACxB;;AAGF,QAAK,YAAY,CAAC,GAAG,SAAS,CAAC;KAC9B,MAAM;;;;;;CAOX,cAA4B;AAC1B,MAAI,CAAC,KAAK,aAAc;AACxB,OAAK,eAAe;AACpB,MAAI,KAAK,WAAY;EACrB,MAAM,QAAQ,KAAK,OAAO;AAC1B,MAAI,MAAM,WAAW,EAAG;AACxB,OAAK,YAAY,CAAC,GAAG,MAAM,CAAC;;;;;;;CAQ9B,cAAsB,OAA+C;AACnE,MAAI,EAAE,iBAAiB,qBACrB,QAAO;AAGT,UAAQ,MAAM,UAAd;GACE,KAAK,UACH,QAAO;GACT,KAAK;AACH,QAAI,MAAM,eAAe,KAAA,KAAa,MAAM,cAAc,IACxD,QAAO;AAET,WAAO;GAET,KAAK,QACH,QAAO;GACT,KAAK,UACH,QAAO;GACT,KAAK,eACH,QAAO;;;;;;;CAQb,MAAc,mBAAmB,SAAyC;AACxE,OAAK,MAAM,UAAU,QACnB,QAAO,SAAS;EAGlB,MAAM,8BAAc,IAAI,KAAuB;EAC/C,MAAM,YAA4B,EAAE;AAEpC,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,SAAS,QAAQ;GACvB,MAAM,MAAM,OAAO,EAAE;AAErB,OAAI,OAAO,OAAO;AAChB,QAAI,CAAC,YAAY,IAAI,OAAO,MAAM,CAChC,aAAY,IAAI,OAAO,OAAO,EAAE,CAAC;AAEnC,gBAAY,IAAI,OAAO,MAAM,CAAE,KAAK,IAAI;;GAG1C,MAAM,YAAsB,EAAE;AAC9B,QAAK,MAAM,OAAO,OAAO,iBAAiB;IACxC,MAAM,UAAU,YAAY,IAAI,IAAI;AACpC,QAAI,QACF,WAAU,KAAK,GAAG,QAAQ;;AAI9B,QAAK,OAAO,MACV,uBACA,OAAO,WAAW,KACf,OACC,IAAI,GAAG,QAAQ,WAAW,IAAI,GAAG,QAAQ,OAAO,IAAI,GAAG,QAAQ,MAAM,IAAI,GAAG,UAAU,MAAM,GAC/F,CACF;AAED,aAAU,KAAK;IACb,MAAM;IACN,aAAa,EAAE,IAAI,KAAK,WAAW;IACnC,YAAY,OAAO;IACnB;IACA;IACD,CAAC;;EAGJ,MAAM,WAAW;;;;;EAMjB,MAAM,YAAY,EAChB,WAAW,UAAU,KAAK,MAAM,kBAAkB,EAAE,CAAC,EACtD;AAED,QAAM,KAAK,eACT,UACA,UACD;;;;;CAMH,MAAc,yBAAsD;AAClE,MAAI,CAAC,KAAK,OAAO,WACf;AAGF,MAAI;GACF,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,KAAK,OAAO,IAAI;AAC3D,OAAI,MACF,QAAO,UAAU;WAEZ,OAAO;AACd,QAAK,OAAO,MAAM,8BAA8B,MAAM;;;;;;CAQ1D,MAAc,eACZ,OACA,WACY;EACZ,MAAM,UAAkC,EACtC,gBAAgB,oBACjB;EAED,MAAM,aAAa,MAAM,KAAK,wBAAwB;AACtD,MAAI,WACF,SAAQ,mBAAmB;EAI7B,MAAM,gBADiB,MAAM,MAAM,6BAA6B,GACzB,MAAM;AAE7C,OAAK,OAAO,QACV,0DACA,KAAK,WACL,eACA,KAAK,OAAO,KACZ,KAAK,UAAU,UAAU,CAC1B;EAED,MAAM,UAAU,KAAK,OAAO,WAAW;EACvC,IAAI;AACJ,MAAI;AACF,cAAW,MAAM,QAAQ,KAAK,OAAO,KAAK;IACxC,QAAQ;IACR;IACA,MAAM,KAAK,UAAU;KACnB;KACA;KACD,CAAC;IACF,QAAQ,KAAK,gBAAgB;IAC9B,CAAC;WACK,OAAO;AACd,SAAM,IAAI,oBACR,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IACjF,UACD;;AAGH,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,oBACR,2BAA2B,SAAS,OAAO,GAAG,SAAS,cACvD,QACA,SAAS,OACV;EAGH,IAAI;AACJ,MAAI;AACF,YAAU,MAAM,SAAS,MAAM;WAIxB,OAAO;AACd,SAAM,IAAI,oBACR,qCAAqC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC3F,QACD;;AAGH,OAAK,OAAO,QACV,+EACA,KAAK,WACL,eACA,SAAS,QACT,KAAK,UAAU,OAAO,KAAK,EAC3B,OAAO,SAAS,KAAK,UAAU,OAAO,OAAO,GAAG,OACjD;AAED,MAAI,OAAO,OACT,OAAM,IAAI,oBACR,mBAAmB,KAAK,UAAU,OAAO,QAAQ,MAAM,EAAE,IACzD,UACD;AAGH,MAAI,CAAC,OAAO,KACV,OAAM,IAAI,oBACR,uCACA,eACD;AAGH,SAAO,OAAO;;CAGhB,IAAI,SAAqB;AACvB,SAAO,KAAK;;;;;;;;;;;;;;ACt6BhB,IAAa,2BAAb,MAAiE;CAC/D;CACA;CACA;CAEA,YACE,QACA,YACA,OACA;AACA,OAAK,SAAS;AACd,OAAK,aAAa;AAClB,OAAK,QAAQ;;;;;;;;;;;CAYf,SACE,UACA,YACA,QACA,eACA,cACA,QACA,gBACA,SACU;EAEV,MAAM,MAAM,OAAO,WAAW;AAC9B,MAAI,OAAO,QAAQ,YAAY,CAAC,IAC9B,OAAM,IAAI,MACR,2EACD;EAIH,MAAM,YAA8B;GAClC;GACA;GACA;GACA,YAAY,KAAK;GACjB,kBAAkB;GAClB,iBAAiB;GAClB;EAED,IAAI,iBAAiB;AACrB,MAAI,OAAO,WAAW,mBAAmB,KAAA,GAAW;AAClD,OAAI,OAAO,OAAO,WAAW,mBAAmB,SAC9C,OAAM,IAAI,MAAM,gDAA8C;AAEhE,oBAAiB,OAAO,WAAW;;EAGrC,IAAI;AACJ,MAAI,OAAO,WAAW,qBAAqB,KAAA,GAAW;AACpD,OAAI,OAAO,OAAO,WAAW,qBAAqB,SAChD,OAAM,IAAI,MAAM,kDAAgD;AAElE,sBAAmB,OAAO,WAAW;;EAGvC,IAAI;AACJ,MAAI,OAAO,WAAW,oBAAoB,KAAA,GAAW;AACnD,OAAI,OAAO,OAAO,WAAW,oBAAoB,SAC/C,OAAM,IAAI,MAAM,iDAA+C;AAEjE,qBAAkB,OAAO,WAAW;;AAGtC,MAAI,OAAO,WAAW,YAAY,KAAA,GAAW;AAC3C,OAAI,OAAO,OAAO,WAAW,YAAY,WACvC,OAAM,IAAI,MAAM,2CAAyC;AAE3D,aAAU,UAAU,OAAO,WAAW;;AAGxC,MAAI,qBAAqB,KAAA,EACvB,WAAU,mBAAmB;AAE/B,MAAI,oBAAoB,KAAA,EACtB,WAAU,kBAAkB;EAG9B,IAAI;AACJ,MAAI,OAAO,WAAW,kBAAkB,KAAA,GAAW;AACjD,OAAI,OAAO,OAAO,WAAW,kBAAkB,SAC7C,OAAM,IAAI,MAAM,+CAA6C;AAE/D,mBAAgB,OAAO,WAAW;;EAGpC,IAAI;AACJ,MAAI,OAAO,WAAW,gCAAgC,KAAA,GAAW;AAC/D,OAAI,OAAO,OAAO,WAAW,gCAAgC,SAC3D,OAAM,IAAI,MACR,6DACD;AAEH,iCACE,OAAO,WAAW;;EAGtB,MAAM,YAAY,IAAI,kBAAkB,KAAK,OAAO;GAClD,YAAY;GACZ,GAAI,qBAAqB,KAAA,KAAa,EAAE,kBAAkB;GAC1D,GAAI,oBAAoB,KAAA,KAAa,EAAE,iBAAiB;GACxD,GAAI,kBAAkB,KAAA,KAAa,EAAE,eAAe;GACpD,GAAI,gCAAgC,KAAA,KAAa,EAC/C,6BACD;GACD,aAAa,SAAS,iBAAiB,aAAa;GACrD,CAAC;AAEF,SAAO,IAAI,kBACT,KAAK,QACL,UACA,YACA,eACA,WACA,gBACA,UACD;;;;;;;;;;AC5IL,IAAa,qBAAb,MAAoD;CAClD;CACA;CACA;CAEA;CACA;CACA;CACA;CACA,4BAA4C;CAC5C,6BAA6C;CAC7C,kBAA2C;CAC3C,2CACE,IAAI,KAAK;CAEX,YACE,QACA,WACA,YACA,eACA;AAJiB,OAAA,SAAA;AAKjB,OAAK,YAAY;AACjB,OAAK,aAAa;AAClB,OAAK,gBAAgB;AACrB,OAAK,aAAa;AAElB,OAAK,QAAQ,IAAI,SAAS;AAC1B,OAAK,SAAS,IAAI,SAAS;AAC3B,OAAK,aAAa,IAAI,SAAS;AAK/B,OAAK,OAAO,WAAW,YAAY;GACjC,MAAM,aAAa,wBAAwB,QAAQ;AACnD,OAAI,aAAa,KAAK,4BAA4B;AAChD,SAAK,6BAA6B;AAClC,SAAK,cACF,OAAO;KACN,YAAY,KAAK;KACjB,YAAY;KACZ,eAAe;KACf,mBAAmB,KAAK,KAAK;KAC9B,CAAC,CACD,OAAO,UAAU;AAChB,UAAK,OAAO,MACV,wMACA,KAAK,WACL,MACD;MACD;;IAEN;AAEF,OAAK,MAAM,WAAW,YAAY;GAChC,MAAM,aAAa,wBAAwB,QAAQ;AACnD,OAAI,aAAa,KAAK,2BAA2B;AAC/C,SAAK,4BAA4B;AACjC,SAAK,cACF,OAAO;KACN,YAAY,KAAK;KACjB,YAAY;KACZ,eAAe;KACf,mBAAmB,KAAK,KAAK;KAC9B,CAAC,CACD,OAAO,UAAU;AAChB,UAAK,OAAO,MACV,8GACA,KAAK,WACL,MACD;MACD;;IAEN;;CAGJ,WAA0B;AACxB,OAAK,aAAa;AAClB,OAAK,0BAA0B,eAAe;AAC9C,SAAO,QAAQ,SAAS;;CAG1B,qBAA8C;AAC5C,SAAO;GACL,OAAO,KAAK;GACZ,cAAc;GACd,kBAAkB;GAClB,kBAAkB;GAClB,aAAa;GACb,kBAAkB;GAClB,gBAAgB;GACjB;;CAGH,wBAAwB,UAAqD;AAC3E,OAAK,yBAAyB,IAAI,SAAS;AAC3C,eAAa;AACX,QAAK,yBAAyB,OAAO,SAAS;;;;CAKlD,cAAoB;CAEpB,0BAAkC,MAA6B;AAC7D,MAAI,KAAK,oBAAoB,KAAM;AACnC,OAAK,kBAAkB;EACvB,MAAM,WAAW,KAAK,oBAAoB;AAC1C,OAAK,MAAM,YAAY,KAAK,yBAC1B,KAAI;AACF,YAAS,SAAS;WACX,OAAO;AACd,QAAK,OAAO,MACV,kDACA,MACD;;;CAKP,MAAM,OAAsB;EAE1B,MAAM,UAAU,MAAM,KAAK,cAAc,KAAK,KAAK,WAAW;EAC9D,MAAM,eACJ,QAAQ,MAAM,MAAM,EAAE,eAAe,QAAQ,EAAE,iBAAiB;EAClE,MAAM,gBACJ,QAAQ,MAAM,MAAM,EAAE,eAAe,SAAS,EAAE,iBAAiB;AACnE,OAAK,MAAM,KAAK,aAAa;AAC7B,OAAK,OAAO,KAAK,cAAc;AAC/B,OAAK,4BAA4B;AACjC,OAAK,6BAA6B;AAClC,OAAK,0BAA0B,YAAY;;;;;;;;ACtI/C,IAAa,4BAAb,MAAkE;CAChE;CAEA,YAAY,QAAiB;AAC3B,OAAK,SAAS;;CAGhB,SACE,UACA,YACA,QACA,eACU;AACV,SAAO,IAAI,mBACT,KAAK,QACL,UACA,YACA,cACD;;;;;ACrBL,SAAS,kBAAkB,KAAkC;AAC3D,QAAO;EACL,YAAY,IAAI;EAChB,YAAY,IAAI;EAChB,eAAe,OAAO,IAAI,eAAe;EACzC,mBAAmB,IAAI,wBACnB,IAAI,KAAK,IAAI,sBAAsB,CAAC,SAAS,GAC7C,KAAA;EACL;;AAGH,SAAS,kBAAkB,QAA4C;AACrE,QAAO;EACL,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,gBAAgB,OAAO,OAAO,cAAc;EAC5C,uBAAuB,OAAO,oBAC1B,IAAI,KAAK,OAAO,kBAAkB,CAAC,aAAa,GAChD;EACL;;AAGH,IAAa,0BAAb,MAAmE;CACjE,YAAY,IAAuC;AAAtB,OAAA,KAAA;;CAE7B,MAAM,KACJ,YACA,QACyB;AACzB,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,OAAO,MAAM,KAAK,GACrB,WAAW,eAAe,CAC1B,WAAW,CACX,MAAM,eAAe,KAAK,WAAW,CACrC,SAAS;AAEZ,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,SAAO,KAAK,IAAI,kBAAkB;;CAGpC,MAAM,IACJ,YACA,YACA,QACuB;AACvB,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,MAAM,MAAM,KAAK,GACpB,WAAW,eAAe,CAC1B,WAAW,CACX,MAAM,eAAe,KAAK,WAAW,CACrC,MAAM,eAAe,KAAK,WAAW,CACrC,kBAAkB;AAErB,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,MAAI,CAAC,IACH,QAAO;GACL;GACA;GACA,eAAe;GAChB;AAGH,SAAO,kBAAkB,IAAI;;CAG/B,MAAM,OAAO,QAAsB,QAAqC;AACtE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;GACjD,MAAM,aAAa,kBAAkB,OAAO;AAE5C,SAAM,IACH,WAAW,eAAe,CAC1B,OAAO,WAAW,CAClB,YAAY,OACX,GAAG,QAAQ,CAAC,eAAe,cAAc,CAAC,CAAC,YAAY;IACrD,GAAG;IACH,YAAY,GAAG;IAChB,CAAC,CACH,CACA,SAAS;IACZ;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;CAIxC,MAAM,OAAO,YAAoB,QAAqC;AACpE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;AACjD,SAAM,IACH,WAAW,eAAe,CAC1B,MAAM,eAAe,KAAK,WAAW,CACrC,SAAS;IACZ;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;;;;AC7G1C,SAAS,sBAAsB,KAA0C;AACvE,QAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,iBAAiB,IAAI;EACrB,YAAY,IAAI;EAChB,YAAY,IAAI;EAChB,QAAQ,IAAI;EACZ,QAAQ,IAAI;EACZ,YAAY,IAAI;EAChB,aAAa,IAAI;EACjB,cAAc,IAAI;EACnB;;AAGH,SAAS,sBACP,QAC0B;AAC1B,QAAO;EACL,IAAI,OAAO;EACX,QAAQ,OAAO;EACf,kBAAkB,KAAK,UAAU,OAAO,gBAAgB;EACxD,aAAa,OAAO;EACpB,aAAa,OAAO;EACpB,QAAQ,KAAK,UAAU,OAAO,OAAO;EACrC,QAAQ,OAAO;EACf,YAAY,KAAK,UAAU,OAAO,WAAW;EAC7C,cAAc,OAAO;EACrB,eAAe,OAAO;EACvB;;;;;AAMH,IAAa,8BAAb,MAA2E;CACzE,YAAY,IAAuC;AAAtB,OAAA,KAAA;;CAE7B,MAAM,KACJ,YACA,QACA,QACyC;AACzC,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,aAAa,QAAQ,SAAS,SAAS,OAAO,OAAO,GAAG;EAC9D,MAAM,QAAQ,QAAQ,SAAS;EAE/B,MAAM,OAAO,MAAM,KAAK,GACrB,WAAW,oBAAoB,CAC/B,WAAW,CACX,MAAM,eAAe,KAAK,WAAW,CACrC,QAAQ,WAAW,OAAO,CAC1B,OAAO,WAAW,CAClB,MAAM,QAAQ,EAAE,CAChB,SAAS;EAEZ,IAAI,UAAU;EACd,IAAI,QAAQ;AACZ,MAAI,QAAQ,SAAS,KAAK,SAAS,OAAO;AACxC,aAAU;AACV,WAAQ,KAAK,MAAM,GAAG,MAAM;;EAG9B,MAAM,aAAa,UAAU,OAAO,aAAa,MAAM,GAAG,KAAA;EAC1D,MAAM,SAAS,QAAQ,UAAU;AAEjC,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,SAAO;GACL,SAAS,MAAM,IAAI,sBAAsB;GACzC,SAAS;IAAE;IAAQ;IAAO;GAC1B;GACD;;CAGH,MAAM,IAAI,YAA8B,QAAqC;AAC3E,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;GACjD,MAAM,aAAa,sBAAsB,WAAW;AAEpD,SAAM,IACH,WAAW,oBAAoB,CAC/B,OAAO,WAAW,CAClB,YAAY,OAAO,GAAG,OAAO,KAAK,CAAC,WAAW,CAAC,CAC/C,SAAS;IACZ;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;CAIxC,MAAM,OAAO,IAAY,QAAqC;AAC5D,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;AACjD,SAAM,IAAI,WAAW,oBAAoB,CAAC,MAAM,MAAM,KAAK,GAAG,CAAC,SAAS;IACxE;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;CAIxC,MAAM,eACJ,YACA,QACe;AACf,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;AACjD,SAAM,IACH,WAAW,oBAAoB,CAC/B,MAAM,eAAe,KAAK,WAAW,CACrC,SAAS;IACZ;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;CAIxC,MAAM,2BAA2B,QAAyC;AACxE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,OAAO,MAAM,KAAK,GACrB,WAAW,oBAAoB,CAC/B,OAAO,cAAc,CACrB,UAAU,CACV,SAAS;AAEZ,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,SAAO,KAAK,KAAK,QAAQ,IAAI,YAAY;;;;;AC3J7C,SAAS,kBAAkB,KAAkC;AAC3D,QAAO;EACL,IAAI,IAAI;EACR,MAAM,IAAI;EACV,cAAc,IAAI;EAClB,eAAe;GACb,MAAM,IAAI;GACV,YAAa,IAAI,sBAAsB,EAAE;GAC1C;EACD,QAAQ;GACN,YAAa,IAAI,uBAAuB,EAAE;GAC1C,OAAQ,IAAI,iBAAiB,EAAE;GAC/B,QAAQ,IAAI;GACb;EACD,SAAS,EAAE,qBAAqB,KAAK;EACrC,QAAQ;GACN,MAAM;IACJ,OAAO,IAAI;IACX,kBAAkB,IAAI,2BAClB,IAAI,KAAK,IAAI,yBAAyB,CAAC,SAAS,GAChD,KAAA;IACJ,kBAAkB,IAAI,2BAClB,IAAI,KAAK,IAAI,yBAAyB,CAAC,SAAS,GAChD,KAAA;IACJ,cAAc,IAAI;IACnB;GACD,MAAM;IACJ,OAAO,IAAI;IACX,kBAAkB,IAAI,2BAClB,IAAI,KAAK,IAAI,yBAAyB,CAAC,SAAS,GAChD,KAAA;IACJ,kBAAkB,IAAI,2BAClB,IAAI,KAAK,IAAI,yBAAyB,CAAC,SAAS,GAChD,KAAA;IACJ,cAAc,IAAI;IACnB;GACF;EACF;;AAGH,SAAS,kBAAkB,QAA4C;AACrE,QAAO;EACL,MAAM,OAAO;EACb,eAAe,OAAO;EACtB,cAAc,OAAO,cAAc;EACnC,YAAY,OAAO;EACnB,aAAa,OAAO;EACpB,oBAAoB,OAAO,cAAc;EACzC,qBACE,OAAO,OAAO,WAAW,SAAS,IAAI,OAAO,OAAO,aAAa;EACnE,eAAe,OAAO,OAAO,MAAM,SAAS,IAAI,OAAO,OAAO,QAAQ;EACtE,eAAe,OAAO,OAAO;EAC7B,YAAY,OAAO,OAAO,KAAK;EAC/B,0BAA0B,OAAO,OAAO,KAAK,mBACzC,IAAI,KAAK,OAAO,OAAO,KAAK,iBAAiB,CAAC,aAAa,GAC3D;EACJ,0BAA0B,OAAO,OAAO,KAAK,mBACzC,IAAI,KAAK,OAAO,OAAO,KAAK,iBAAiB,CAAC,aAAa,GAC3D;EACJ,oBAAoB,OAAO,OAAO,KAAK;EACvC,YAAY,OAAO,OAAO,KAAK;EAC/B,0BAA0B,OAAO,OAAO,KAAK,mBACzC,IAAI,KAAK,OAAO,OAAO,KAAK,iBAAiB,CAAC,aAAa,GAC3D;EACJ,0BAA0B,OAAO,OAAO,KAAK,mBACzC,IAAI,KAAK,OAAO,OAAO,KAAK,iBAAiB,CAAC,aAAa,GAC3D;EACJ,oBAAoB,OAAO,OAAO,KAAK;EACxC;;AAGH,IAAa,0BAAb,MAAmE;CACjE,YAAY,IAAuC;AAAtB,OAAA,KAAA;;CAE7B,MAAM,KAAK,QAA+C;AACxD,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,OAAO,MAAM,KAAK,GAAG,WAAW,eAAe,CAAC,WAAW,CAAC,SAAS;AAE3E,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,SAAO,KAAK,IAAI,kBAAkB;;CAGpC,MAAM,IAAI,MAAc,QAA6C;AACnE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,MAAM,MAAM,KAAK,GACpB,WAAW,eAAe,CAC1B,WAAW,CACX,MAAM,QAAQ,KAAK,KAAK,CACxB,kBAAkB;AAErB,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,qBAAqB,OAAO;AAG9C,SAAO,kBAAkB,IAAI;;CAG/B,MAAM,OAAO,QAAsB,QAAqC;AACtE,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;GACjD,MAAM,aAAa,kBAAkB,OAAO;AAE5C,SAAM,IACH,WAAW,eAAe,CAC1B,OAAO,WAAW,CAClB,YAAY,OACX,GAAG,OAAO,OAAO,CAAC,YAAY;IAC5B,GAAG;IACH,YAAY,GAAG;IAChB,CAAC,CACH,CACA,SAAS;IACZ;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;CAIxC,MAAM,OAAO,MAAc,QAAqC;AAC9D,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;AAGtC,QAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,OAAO,QAAQ;AACjD,SAAM,IAAI,WAAW,eAAe,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC,SAAS;IACvE;AAEF,MAAI,QAAQ,QACV,OAAM,IAAI,MAAM,oBAAoB;;;;;ACpI1C,IAAa,kBAAb,MAA6B;CAC3B;CACA;CACA;CACA,QAAsC,EAAE;CACxC,aAA8B;CAC9B,iCAA6D,IAAI,KAAK;CAEtE,YACE,QACA,qBACA,cACA;AACA,OAAK,SAAS;AACd,OAAK,sBAAsB;AAC3B,OAAK,eAAe;;CAGtB,MAAM,kBAAkB,OAA0C;AAChE,OAAK,MAAM,KAAK,MAAM;AACtB,QAAM,KAAK,cAAc;;CAG3B,MAAM,gBAAgB,OAAsC;EAC1D,MAAM,UAAU,MAAM,KAAK,KAAK;AAChC,MAAI,CAAC,QACH;EAGF,MAAM,UAAU,KAAK,eAAe,IAAI,QAAQ;AAChD,MAAI,CAAC,QACH;AAGF,OAAK,eAAe,OAAO,QAAQ;AACnC,MAAI,QAAQ,OAAO,SAAS,EAC1B,OAAM,KAAK,aAAa,KAAK,aAAa,QAAQ,OAAO,CAAC;;CAI9D,QAAc;AACZ,OAAK,QAAQ,EAAE;AACf,OAAK,eAAe,OAAO;;CAG7B,MAAc,eAA8B;AAC1C,MAAI,KAAK,WACP;AAEF,OAAK,aAAa;AAElB,MAAI;AACF,UAAO,KAAK,MAAM,SAAS,GAAG;IAC5B,MAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,QAAI;AACF,WAAM,KAAK,iBAAiB,MAAM;aAC3B,OAAO;KACd,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,UAAK,OAAO,MACV,wDACA,MAAM,OACN,IAAI,QACL;;;YAGG;AACR,QAAK,aAAa;;;CAItB,MAAc,iBAAiB,OAA0C;EACvE,MAAM,EAAE,SAAS,gBAAgB,MAAM;AAEvC,MAAI,YAAY,UAAU,GAAG;AAC3B,SAAM,KAAK,aAAa,KAAK,aAAa,CAAC,MAAM,CAAC,CAAC;AACnD;;EAGF,IAAI,UAAU,KAAK,eAAe,IAAI,QAAQ;AAC9C,MAAI,CAAC,SAAS;AACZ,aAAU;IACR,gBAAgB,IAAI,IAAI,YAAY;IACpC,+BAAe,IAAI,KAAK;IACxB,QAAQ,EAAE;IACX;AACD,QAAK,eAAe,IAAI,SAAS,QAAQ;;AAG3C,UAAQ,cAAc,IAAI,MAAM,MAAM;AACtC,UAAQ,OAAO,KAAK,MAAM;AAE1B,MAAI,QAAQ,cAAc,QAAQ,QAAQ,eAAe,MAAM;AAC7D,QAAK,eAAe,OAAO,QAAQ;AACnC,SAAM,KAAK,aAAa,KAAK,aAAa,QAAQ,OAAO,CAAC;;;CAI9D,aAAqB,QAA6C;EAChE,MAAM,wBAAwB,KAAK,2BAA2B,OAAO;EACrE,MAAM,UAAU,OAAO,SAAS;EAChC,MAAM,cAAwB,EAAE;EAChC,MAAM,UAAoC,EAAE;AAE5C,OAAK,MAAM,SAAS,QAAQ;AAC1B,WAAQ,KAAK;IACX;IACA,iBAAiB,UAAU,CAAC,GAAG,YAAY,GAAG,EAAE;IACjD,CAAC;AAEF,OAAI,WAAW,MAAM,MACnB,aAAY,KAAK,MAAM,MAAM;;AAIjC,SAAO;GAAE;GAAuB;GAAS;;CAG3C,2BACE,QAC0B;EAC1B,MAAM,oBAA8C,EAAE;AAEtD,OAAK,MAAM,SAAS,QAAQ;AAC1B,OAAI,MAAM,sBACR,MAAK,MAAM,CAAC,OAAO,gBAAgB,OAAO,QACxC,MAAM,sBACP,EAAE;AACD,QAAI,EAAE,SAAS,mBACb,mBAAkB,SAAS,EAAE;AAE/B,SAAK,MAAM,KAAK,YACd,KAAI,CAAC,kBAAkB,OAAO,SAAS,EAAE,CACvC,mBAAkB,OAAO,KAAK,EAAE;;AAMxC,QAAK,MAAM,MAAM,MAAM,YAAY;IACjC,MAAM,SAAS,GAAG,UAAU;AAI5B,QAAI,OAAO,SAAS,mBAClB;AAEF,QAAI,CAAC,KAAK,oBAAoB,IAAI,GAAG,QAAQ,aAAa,CACxD;IAEF,MAAM,QAAQ,OAAO;AACrB,QAAI,CAAC,OAAO,YAAY,CAAC,MAAM,SAC7B;IAGF,MAAM,eAAe,kBACnB,GAAG,QAAQ,QACX,MAAM,SACP;AACD,QAAI,EAAE,MAAM,YAAY,mBACtB,mBAAkB,MAAM,YAAY,EAAE;AAExC,QAAI,CAAC,kBAAkB,MAAM,UAAU,SAAS,aAAa,CAC3D,mBAAkB,MAAM,UAAU,KAAK,aAAa;;;AAK1D,SAAO;;;;;;;;;;ACtKX,IAAa,cAAb,MAAyB;CACvB,mCAAoC,IAAI,KAAyB;CACjE,iCAAkC,IAAI,KAA2B;CACjE,gBAAgD,EAAE;CAClD,aAAqB;CAErB,YAAY,UAAsC;AAArB,OAAA,WAAA;AAC3B,OAAK,mBAAmB;;;;;;;;;;;CAY1B,YAAY,OAAe,QAA2C;AACpE,MAAI,QAAQ,QACV,QAAO,QAAQ,uBAAO,IAAI,MAAM,oBAAoB,CAAC;AAGvD,MAAI,KAAK,WACP,QAAO,QAAQ,uBAAO,IAAI,MAAM,0BAA0B,CAAC;EAG7D,MAAM,kBAAkB,KAAK,iBAAiB,IAAI,MAAM;AACxD,MAAI,gBACF,QAAO,QAAQ,QAAQ,gBAAgB;AAGzC,SAAO,IAAI,SAAqB,SAAS,WAAW;GAClD,MAAM,SAAqB;IAAE;IAAS;IAAQ;IAAQ;GAEtD,MAAM,kBAAkB,KAAK,eAAe,IAAI,MAAM,IAAI,EAAE;AAC5D,mBAAgB,KAAK,OAAO;AAC5B,QAAK,eAAe,IAAI,OAAO,gBAAgB;AAE/C,OAAI,QAAQ;IACV,MAAM,qBAAqB;KACzB,MAAM,UAAU,KAAK,eAAe,IAAI,MAAM;AAC9C,SAAI,SAAS;MACX,MAAM,QAAQ,QAAQ,QAAQ,OAAO;AACrC,UAAI,UAAU,IAAI;AAChB,eAAQ,OAAO,OAAO,EAAE;AACxB,WAAI,QAAQ,WAAW,EACrB,MAAK,eAAe,OAAO,MAAM;AAEnC,cAAO,uBAAO,IAAI,MAAM,oBAAoB,CAAC;;;;AAKnD,WAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,MAAM,CAAC;;IAEhE;;;;;CAMJ,WAAiB;AACf,OAAK,aAAa;AAElB,OAAK,MAAM,eAAe,KAAK,cAC7B,cAAa;AAEf,OAAK,cAAc,SAAS;AAE5B,OAAK,MAAM,GAAG,YAAY,KAAK,eAC7B,MAAK,MAAM,UAAU,QACnB,QAAO,uBAAO,IAAI,MAAM,uBAAuB,CAAC;AAGpD,OAAK,eAAe,OAAO;;CAG7B,oBAAkC;AAChC,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,eAAe,iBACd,OAAO,UAAU;AAChB,QAAK,oBAAoB,MAAM;IAElC,CACF;AAED,OAAK,cAAc,KACjB,KAAK,SAAS,UACZ,eAAe,cACd,OAAO,UAAU;AAChB,QAAK,iBAAiB,MAAM;IAE/B,CACF;;CAGH,oBAA4B,OAAiC;EAC3D,MAAM,SAAqB;GACzB,OAAO,MAAM;GACb,QAAQ;GACR,oBAAoB,MAAM;GAC1B,cAAc,MAAM;GACpB,cAAc;GACd,QAAQ,EAAE;GACX;AAED,OAAK,iBAAiB,IAAI,MAAM,OAAO,OAAO;AAC9C,OAAK,eAAe,MAAM,OAAO,OAAO;;CAG1C,iBAAyB,OAA8B;EACrD,MAAM,SAAqB;GACzB,OAAO,MAAM;GACb,QAAQ;GACR,oBAAoB,MAAM,eAAe,MAAM;GAC/C,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,QAAQ,MAAM;GACf;AAED,OAAK,iBAAiB,IAAI,MAAM,OAAO,OAAO;AAC9C,OAAK,eAAe,MAAM,OAAO,OAAO;;CAG1C,eAAuB,OAAe,QAA0B;EAC9D,MAAM,UAAU,KAAK,eAAe,IAAI,MAAM;AAC9C,MAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;AAGF,OAAK,eAAe,OAAO,MAAM;AAEjC,OAAK,MAAM,UAAU,QACnB,KAAI,OAAO,QAAQ,QACjB,QAAO,uBAAO,IAAI,MAAM,oBAAoB,CAAC;MAE7C,QAAO,QAAQ,OAAO;;;;;AC5J9B,IAAY,aAAL,yBAAA,YAAA;AACL,YAAA,YAAA;AACA,YAAA,cAAA;AACA,YAAA,cAAA;AACA,YAAA,yBAAA;AACA,YAAA,WAAA;;KACD;AAuBD,IAAa,oBAAb,MAA6D;CAC3D,0BACE,IAAI,KAAK;CACX,uBAAqC,IAAI,KAAK;CAC9C,4BAA4D,IAAI,KAAK;CAErE,UAAU,YAA4C;AACpD,MAAI,CAAC,KAAK,KAAK,IAAI,WAAW,CAC5B;EAGF,IAAI,aAAa;EACjB,IAAI,cAAc;EAClB,IAAI,cAAc;AAElB,OAAK,MAAM,aAAa,KAAK,QAAQ,QAAQ,EAAE;GAC7C,MAAM,SAAS,UAAU,IAAI,WAAW;AACxC,OAAI,QAAQ;AACV,kBAAc,OAAO;AACrB,mBAAe,OAAO;AACtB,mBAAe,OAAO;;;AAI1B,SAAO,aAAa,YAAY,aAAa,YAAY;;CAG3D,SAAS,UAAgD;AACvD,OAAK,UAAU,IAAI,SAAS;AAC5B,eAAa;AACX,QAAK,UAAU,OAAO,SAAS;;;CAInC,YAAY,YAAoB,SAAyB;AACvD,OAAK,QAAQ,IAAI,4BAAY,IAAI,KAAK,CAAC;AAEvC,UAAQ,MAAM,SAAS,YACrB,KAAK,YAAY,YAAY,SAAS,QAAQ,CAC/C;AACD,UAAQ,MAAM,WAAW,YACvB,KAAK,cAAc,YAAY,SAAS,QAAQ,CACjD;AACD,UAAQ,OAAO,SAAS,YACtB,KAAK,YAAY,YAAY,UAAU,QAAQ,CAChD;AACD,UAAQ,OAAO,WAAW,YACxB,KAAK,cAAc,YAAY,UAAU,QAAQ,CAClD;AACD,UAAQ,WAAW,SAAS,YAC1B,KAAK,YAAY,YAAY,cAAc,QAAQ,CACpD;;CAGH,cAAc,YAA0B;EACtC,MAAM,YAAY,KAAK,QAAQ,IAAI,WAAW;AAC9C,MAAI,CAAC,UACH;EAGF,MAAM,sBAAsB,CAAC,GAAG,UAAU,MAAM,CAAC;AACjD,OAAK,QAAQ,OAAO,WAAW;AAE/B,OAAK,MAAM,cAAc,oBACvB,MAAK,aAAa,WAAW;;CAIjC,QAAc;AACZ,OAAK,QAAQ,OAAO;AACpB,OAAK,KAAK,OAAO;AACjB,OAAK,UAAU,OAAO;;CAGxB,YACE,YACA,aACA,SACM;EACN,MAAM,mCAAmB,IAAI,KAAa;AAE1C,OAAK,MAAM,UAAU,SAAS;AAC5B,OAAI,gBAAgB,WAAW,CAAC,OAAO,WACrC;GAGF,MAAM,SAAS,KAAK,kBAAkB,YAAY,OAAO,WAAW;AACpE,QAAK,KAAK,IAAI,OAAO,WAAW;AAEhC,OAAI,gBAAgB,QAClB,QAAO;YACE,gBAAgB,SACzB,QAAO;OAEP,QAAO;AAGT,oBAAiB,IAAI,OAAO,WAAW;;AAGzC,OAAK,MAAM,cAAc,iBACvB,MAAK,aAAa,WAAW;;CAIjC,cACE,YACA,aACA,SACM;EACN,MAAM,mCAAmB,IAAI,KAAa;AAE1C,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,KAAK,kBAAkB,YAAY,OAAO,WAAW;AAEpE,OAAI,gBAAgB,QAClB,QAAO,aAAa,KAAK,IAAI,GAAG,OAAO,aAAa,EAAE;YAC7C,gBAAgB,SACzB,QAAO,cAAc,KAAK,IAAI,GAAG,OAAO,cAAc,EAAE;AAG1D,oBAAiB,IAAI,OAAO,WAAW;;AAGzC,OAAK,MAAM,cAAc,iBACvB,MAAK,aAAa,WAAW;;CAIjC,kBACE,YACA,YACgB;EAChB,IAAI,YAAY,KAAK,QAAQ,IAAI,WAAW;AAC5C,MAAI,CAAC,WAAW;AACd,+BAAY,IAAI,KAAK;AACrB,QAAK,QAAQ,IAAI,YAAY,UAAU;;EAGzC,IAAI,SAAS,UAAU,IAAI,WAAW;AACtC,MAAI,CAAC,QAAQ;AACX,YAAS;IAAE,YAAY;IAAG,aAAa;IAAG,YAAY;IAAG;AACzD,aAAU,IAAI,YAAY,OAAO;;AAGnC,SAAO;;CAGT,aAAqB,YAA0B;EAC7C,MAAM,SAAS,KAAK,UAAU,WAAW;AACzC,MAAI,WAAW,KAAA,EACb;AAGF,OAAK,MAAM,YAAY,CAAC,GAAG,KAAK,UAAU,CACxC,UAAS,YAAY,OAAO;;;AAKlC,SAAS,aACP,YACA,aACA,YACY;AACZ,KAAI,aAAa,EACf,QAAO,WAAW;AAEpB,KAAI,aAAa,KAAK,cAAc,EAClC,QAAO,WAAW;AAEpB,KAAI,aAAa,EACf,QAAO,WAAW;AAEpB,KAAI,cAAc,EAChB,QAAO,WAAW;AAEpB,QAAO,WAAW;;;;ACtJpB,IAAK,aAAL,yBAAA,YAAA;AACE,YAAA,cAAA;AACA,YAAA,oBAAA;;EAFG,cAAA,EAAA,CAGJ;AAED,MAAM,2BAA8C;CAClD,yBAAyB;CACzB,mBAAmB;CACpB;AAED,MAAM,2BAA2B;AAEjC,IAAa,cAAb,MAAiD;CAC/C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,kBAAmC,IAAI,iBAAiB;CACxD;CACA;CACA;CACA;CACA;CACA;CACA,8CACE,IAAI,KAAK;CACX,yCAA0C,IAAI,KAAa;CAC3D,2CAA4C,IAAI,KAG7C;CACH,mCAAoC,IAAI,KAAqB;CAC7D,yCAA0C,IAAI,KAAqB;CACnE,kBAAyC,QAAQ,SAAS;CAE1D,YACE,QACA,eACA,eACA,mBACA,gBACA,gBACA,SACA,UACA,qBACA,SAAqC,EAAE,EACvC;AACA,OAAK,SAAS;AACd,OAAK,gBAAgB;AACrB,OAAK,gBAAgB;AACrB,OAAK,oBAAoB;AACzB,OAAK,iBAAiB;AACtB,OAAK,iBAAiB;AACtB,OAAK,UAAU;AACf,OAAK,WAAW;AAChB,OAAK,SAAS;GAAE,GAAG;GAA0B,GAAG;GAAQ;AACxD,OAAK,0BAAU,IAAI,KAAK;AACxB,OAAK,UAAU,IAAI,WAAW,WAAW,OAAO,WAC9C,QAAQ,aAAa,OAAO,OAAO,CACpC;AACD,OAAK,cAAc,IAAI,YAAY,SAAS;AAC5C,OAAK,aAAa;AAClB,OAAK,kBAAkB,IAAI,gBACzB,QACA,sBACC,UAAU,KAAK,qBAAqB,MAAM,CAC5C;AACD,OAAK,oBAAoB,IAAI,mBAAmB;;CAGlD,MAAM,UAAyB;AAC7B,MAAI,KAAK,WACP,OAAM,IAAI,MAAM,wDAAwD;AAG1E,MAAI;GACF,MAAM,iBACJ,MAAM,KAAK,kBAAkB,4BAA4B;AAC3D,QAAK,MAAM,MAAM,eACf,MAAK,uBAAuB,IAAI,GAAG;WAE9B,OAAO;AACd,QAAK,OAAO,MACV,oDACA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;;EAGH,MAAM,gBAAgB,MAAM,KAAK,cAAc,MAAM;AAErD,OAAK,MAAM,UAAU,eAAe;GAClC,MAAM,UAAU,KAAK,eAAe,SAClC,OAAO,IACP,OAAO,MACP,OAAO,eACP,KAAK,eACL,OAAO,cACP,OAAO,QACP,KAAK,gBACL,OAAO,QACR;GAED,MAAM,SAAiB;IACrB,IAAI,OAAO;IACX,MAAM,OAAO;IACb,cAAc,OAAO;IACrB,QAAQ,OAAO;IACf,SAAS,OAAO;IAChB;IACD;AAED,QAAK,QAAQ,IAAI,OAAO,MAAM,OAAO;AACrC,SAAM,KAAK,gBAAgB,OAAO;AAClC,QAAK,qBAAqB,OAAO;AAEjC,OAAI;AACF,UAAM,QAAQ,MAAM;YACb,OAAO;AACd,SAAK,OAAO,MACV,yDACA,OAAO,MACP,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;AACD,SAAK,QAAQ,OAAO,OAAO,KAAK;AAChC;;GAIF,MAAM,mBAAmB,OAAO,QAAQ,OAAO;AAC/C,OAAI,mBAAmB,GAAG;IACxB,MAAM,qBAAqB,IAAI,iBAAiB;AAChD,SAAK,yBAAyB,IAAI,OAAO,MAAM,mBAAmB;AAC7D,SAAK,aACR,QACA,kBACA,WAAW,UACX,mBAAmB,OACpB,CACE,OAAO,UAAU;AAChB,SAAI,mBAAmB,OAAO,QAAS;AACvC,UAAK,OAAO,MACV,kDACA,OAAO,MACP,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAC1D;MACD,CACD,cAAc;AACb,UAAK,yBAAyB,OAAO,OAAO,KAAK;MACjD;;;AAIR,OAAK,mBAAmB,KAAK,SAAS,UACpC,kBAAkB,iBAClB,OAAO,OAAO,UAAU,KAAK,gBAAgB,kBAAkB,MAAM,CACtE;AAED,OAAK,yBAAyB,KAAK,SAAS,UAC1C,kBAAkB,YAClB,OAAO,OAAO,UAAU,KAAK,gBAAgB,gBAAgB,MAAM,CACpE;;CAGH,WAA2B;AACzB,OAAK,aAAa;AAClB,OAAK,gBAAgB,OAAO;AAC5B,OAAK,MAAM,cAAc,KAAK,yBAAyB,QAAQ,CAC7D,YAAW,OAAO;AAEpB,OAAK,yBAAyB,OAAO;AACrC,OAAK,iBAAiB,OAAO;AAC7B,OAAK,uBAAuB,OAAO;AACnC,OAAK,gBAAgB,OAAO;AAE5B,MAAI,KAAK,kBAAkB;AACzB,QAAK,kBAAkB;AACvB,QAAK,mBAAmB,KAAA;;AAG1B,MAAI,KAAK,wBAAwB;AAC/B,QAAK,wBAAwB;AAC7B,QAAK,yBAAyB,KAAA;;AAGhC,OAAK,QAAQ,UAAU;AACvB,OAAK,YAAY,UAAU;AAC3B,OAAK,kBAAkB,OAAO;AAE9B,OAAK,MAAM,SAAS,KAAK,4BAA4B,QAAQ,CAC3D,QAAO;AAET,OAAK,4BAA4B,OAAO;EAExC,MAAM,WAA4B,EAAE;AACpC,OAAK,MAAM,UAAU,KAAK,QAAQ,QAAQ,CACxC,UAAS,KAAK,OAAO,QAAQ,UAAU,CAAC;AAG1C,OAAK,QAAQ,OAAO;AAEpB,SAAO;GACL,YAAY;GACZ,WAAW,QAAQ,IAAI,SAAS,CAAC,WAAW,KAAA,EAAU;GACvD;;CAGH,UAAU,MAAsB;EAC9B,MAAM,SAAS,KAAK,QAAQ,IAAI,KAAK;AACrC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,qBAAqB,KAAK,kBAAkB;AAE9D,SAAO;;CAGT,QAAQ,IAAoB;AAC1B,OAAK,MAAM,UAAU,KAAK,QAAQ,QAAQ,CACxC,KAAI,OAAO,OAAO,GAChB,QAAO;AAGX,QAAM,IAAI,MAAM,mBAAmB,GAAG,kBAAkB;;CAG1D,MAAM,IACJ,MACA,cACA,eACA,SAAuB;EAAE,YAAY,EAAE;EAAE,OAAO,EAAE;EAAE,QAAQ;EAAI,EAChE,UAAyB,EAAE,qBAAqB,KAAK,EACrD,IACiB;AACjB,MAAI,KAAK,WACP,OAAM,IAAI,MAAM,iDAAiD;AAGnE,MAAI,KAAK,QAAQ,IAAI,KAAK,CACxB,OAAM,IAAI,MAAM,qBAAqB,KAAK,kBAAkB;AAG9D,OAAK,OAAO,MACV,gFACA,MACA,cACA,eACA,QACA,SACA,GACD;EAED,MAAM,WAAW,MAAM,OAAO,YAAY;EAO1C,MAAM,eAA6B;GACjC,IAAI;GACJ;GACA;GACA;GACA;GACA;GACA,QAZ2B;IAC3B,MAAM,kBAAkB;IACxB,MAAM,kBAAkB;IACzB;GAUA;AAED,QAAM,KAAK,cAAc,OAAO,aAAa;EAE7C,MAAM,UAAU,KAAK,eAAe,SAClC,UACA,MACA,eACA,KAAK,eACL,cACA,QACA,KAAK,gBACL,QACD;EAED,MAAM,SAAiB;GACrB,IAAI;GACJ;GACA;GACA;GACA;GACA;GACD;AAED,OAAK,QAAQ,IAAI,MAAM,OAAO;AAC9B,QAAM,KAAK,gBAAgB,OAAO;AAClC,OAAK,qBAAqB,OAAO;AAEjC,MAAI;AACF,SAAM,QAAQ,MAAM;WACb,OAAO;AACd,QAAK,QAAQ,OAAO,KAAK;AACzB,SAAM,KAAK,cAAc,OAAO,KAAK;AACrC,SAAM;;EAIR,MAAM,qBAAqB,IAAI,iBAAiB;AAChD,OAAK,yBAAyB,IAAI,MAAM,mBAAmB;AACtD,OAAK,aACR,QACA,GACA,WAAW,UACX,mBAAmB,OACpB,CACE,OAAO,UAAU;AAChB,OAAI,mBAAmB,OAAO,QAAS;AACvC,QAAK,OAAO,MACV,kDACA,OAAO,MACP,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAC1D;IACD,CACD,cAAc;AACb,QAAK,yBAAyB,OAAO,KAAK;IAC1C;AAEJ,SAAO;;CAGT,YAAY,MAAoB;EAC9B,MAAM,SAAS,KAAK,QAAQ,IAAI,KAAK;AACrC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,qBAAqB,KAAK,kBAAkB;AAE9D,SAAO,QAAQ,aAAa;;CAG9B,MAAM,OAAO,MAA6B;EACxC,MAAM,SAAS,KAAK,QAAQ,IAAI,KAAK;AACrC,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,qBAAqB,KAAK,kBAAkB;EAI9D,MAAM,qBAAqB,KAAK,yBAAyB,IAAI,KAAK;AAClE,MAAI,oBAAoB;AACtB,sBAAmB,OAAO;AAC1B,QAAK,yBAAyB,OAAO,KAAK;;AAI5C,QAAM,OAAO,QAAQ,UAAU;AAG/B,QAAM,KAAK,cAAc,OAAO,KAAK;AACrC,QAAM,KAAK,cAAc,OAAO,KAAK;AAErC,OAAK,kBAAkB,cAAc,KAAK;EAC1C,MAAM,QAAQ,KAAK,4BAA4B,IAAI,KAAK;AACxD,MAAI,OAAO;AACT,UAAO;AACP,QAAK,4BAA4B,OAAO,KAAK;;AAE/C,OAAK,QAAQ,OAAO,KAAK;;CAG3B,OAAiB;AACf,SAAO,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC;;CAG1C,YAAY,OAAe,QAA2C;AACpE,SAAO,KAAK,YAAY,YAAY,OAAO,OAAO;;CAGpD,cAAc,YAA4C;AACxD,SAAO,KAAK,kBAAkB,UAAU,WAAW;;CAGrD,mBAAmB,UAAgD;AACjE,SAAO,KAAK,kBAAkB,SAAS,SAAS;;CAGlD,qBAA6B,SAAiB,OAAqB;AACjE,MACE,CAAC,KAAK,iBAAiB,IAAI,QAAQ,IACnC,KAAK,iBAAiB,QAAQ,0BAC9B;GACA,MAAM,SAAS,KAAK,iBAAiB,MAAM,CAAC,MAAM,CAAC;AACnD,OAAI,WAAW,KAAA,EACb,MAAK,iBAAiB,OAAO,OAAO;;AAGxC,OAAK,iBAAiB,IAAI,SAAS,MAAM;;CAG3C,qBAA6B,QAAsB;AACjD,SAAO,QAAQ,MAAM,SAAS,YAC5B,KAAK,iBAAiB,QAAQ,QAAQ,CACvC;AAED,OAAK,kBAAkB,YAAY,OAAO,MAAM,OAAO,QAAQ;EAE/D,MAAM,cAAc,OAAO,QAAQ,yBAAyB,aAAa;AAClE,QAAK,SACP,KAAK,eAAe,0BAA0B;IAC7C,YAAY,OAAO;IACnB,UAAU,OAAO;IACjB,UAAU,SAAS;IACnB,SAAS,SAAS;IAClB;IACD,CAAuC,CACvC,YAAY,GAAG;IAClB;AACF,OAAK,4BAA4B,IAAI,OAAO,MAAM,YAAY;AAE9D,SAAO,QAAQ,WAAW,SAAS,YAAY;AAC7C,QAAK,MAAM,UAAU,SAAS;AAC5B,SAAK,OAAO,MACV,qEACA,OAAO,MACP,OAAO,YACP,OAAO,OACP,OAAO,OAAO,WAAW,WACzB,OAAO,gBACR;AAED,SAAK,uBAAuB,IAAI,OAAO,WAAW;IAElD,MAAM,SAA2B;KAC/B,IAAI,OAAO;KACX,OAAO,OAAO;KACd,iBAAiB,OAAO;KACxB,YAAY,OAAO;KACnB,YAAY,OAAO;KACnB,QAAQ,OAAO;KACf,QAAQ,OAAO;KACf,YAAY,OAAO;KACnB,aAAa,OAAO,OAAO,UAAU,mBAAmB;KACxD,cAAc,OAAO,OAAO,MAAM,WAAW;KAC9C;AAEI,SAAK,kBAAkB,IAAI,OAAO,CAAC,OAAO,QAAQ;AACrD,UAAK,OAAO,MACV,+CACA,OAAO,IACP,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD;MACD;AAEG,SAAK,SACP,KAAK,eAAe,mBAAmB;KACtC,IAAI,OAAO;KACX,OAAO,OAAO;KACd,YAAY,OAAO;KACnB,YAAY,OAAO;KACnB,aAAa,OAAO;KACrB,CAAgC,CAChC,YAAY,GAAG;;GAIpB,MAAM,QAAQ,OAAO,QAAQ,WAAW;AACxC,OAAI,MAAM,SAAS,KAAK,OAAO,yBAAyB;IACtD,MAAM,cAAc,MAAM,SAAS,KAAK,OAAO;IAC/C,MAAM,UAAU,MAAM,MAAM,GAAG,YAAY;AAC3C,WAAO,QAAQ,WAAW,OAAO,GAAG,QAAQ;;IAE9C;;CAGJ,MAAc,gBAAgB,QAA+B;EAC3D,IAAI;AACJ,MAAI;AAKF,cAJa,MAAM,KAAK,kBAAkB,KAAK,OAAO,MAAM;IAC1D,QAAQ;IACR,OAAO,KAAK,OAAO;IACpB,CAAC,EACa;WACR,OAAO;AACd,QAAK,OAAO,MACV,0DACA,OAAO,MACP,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CACvD;AACD;;AAGF,MAAI,QAAQ,WAAW,EACrB;AAMF,UAAQ,SAAS;EAEjB,MAAM,UAA2B,EAAE;AACnC,OAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,SAAS,IAAI,cACjB,OAAO,IACP,OAAO,OACP,OAAO,iBACP,OAAO,YACP,OAAO,YACP,OAAO,QACP,OAAO,QACP,OAAO,WACR;AACD,UAAO,OACL,IAAI,aAAa,OAAO,aAAa,IAAI,MAAM,OAAO,aAAa,CAAC,CACrE;AACD,WAAQ,KAAK,OAAO;;AAGtB,SAAO,QAAQ,WAAW,IAAI,GAAG,QAAQ;AAEzC,OAAK,OAAO,MACV,yDACA,QAAQ,QACR,OAAO,KACR;;CAGH,wBAAgC,cAAgC;AAC9D,SAAO,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC,CAAC,QACtC,WAAW,OAAO,iBAAiB,aACrC;;CAGH,MAAc,qBAAqB,OAAqC;AACtE,MAAI,KAAK,WAAY;EAGrB,MAAM,gBAAgB,CACpB,GAAG,IAAI,IACL,OAAO,OAAO,MAAM,sBAAsB,CAAC,SACxC,gBAAgB,YAClB,CACF,CACF;EAGD,MAAM,kBAA4B,EAAE;AACpC,OAAK,MAAM,gBAAgB,eAAe;GACxC,MAAM,UAAU,KAAK,wBAAwB,aAAa;AAC1D,QAAK,MAAM,UAAU,QACnB,KAAI,CAAC,gBAAgB,SAAS,OAAO,CACnC,iBAAgB,KAAK,OAAO;;AAMlC,OAAK,MAAM,UAAU,gBACnB,sBAAqB,OAAO,QAAQ,OAAO,MAAM;AAInD,OAAK,MAAM,UAAU,gBACnB,OAAM,KAAK,aACT,QACA,OAAO,QAAQ,OAAO,eACtB,WAAW,eACZ;;CAIL,iBAAyB,QAAgB,SAAgC;AACvE,MAAI,KAAK,WACP;EAGF,MAAM,WAAW,QAAQ,QACtB,OAAO,CAAC,KAAK,uBAAuB,IAAI,GAAG,WAAW,CACxD;AACD,MAAI,SAAS,WAAW,EAAG;EAE3B,MAAM,QAAyB,EAAE;EACjC,MAAM,WAA4B,EAAE;AAEpC,OAAK,MAAM,UAAU,SACnB,KAAI,OAAO,MACT,OAAM,KAAK,OAAO;MAElB,UAAS,KAAK,OAAO;AAIzB,OAAK,MAAM,UAAU,SACd,MAAK,cAAc,QAAQ,OAAO;AAGzC,MAAI,MAAM,SAAS,GAAG;GAEpB,MAAM,SAAS,oBADE,MAAM,KAAK,YAAY;IAAE;IAAQ;IAAQ,EAAE,EAG1D,KAAK,OAAO,kBACb;AACI,QAAK,mBAAmB,OAAO;;;CAIxC,mBACE,QACe;EACf,MAAM,OAAO,KAAK,gBAAgB,KAAK,YAAY;AACjD,QAAK,MAAM,SAAS,QAAQ;AAC1B,QAAI,KAAK,WAAY;AACrB,UAAM,KAAK,gBAAgB,MAAM;;IAEnC;AACF,OAAK,kBAAkB,KAAK,OAAO,QAAQ;AACzC,QAAK,OAAO,MACV,0CACA,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD;IACD;AACF,SAAO;;CAGT,MAAc,cACZ,QACA,QACe;EACf,MAAM,aAA0B,OAAO,WAAW,KAAK,OAAO,GAAG,UAAU;EAE3E,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,KAAK,QAAQ,KAC3B,OAAO,YACP,OAAO,QACP,YACA,KAAK,gBAAgB,QACrB,EAAE,cAAc,OAAO,MAAM,CAC9B;WACM,OAAO;AACd,OAAI,KAAK,WAAY;GACrB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,QAAK,OAAO,MACV,uEACA,OAAO,MACP,OAAO,YACP,IAAI,QACL;GACD,MAAM,eAAe,IAAI,aAAa,mBAAmB,OAAO,IAAI;AACpE,UAAO,OAAO,aAAa;AAC3B,UAAO,QAAQ,WAAW,IAAI,OAAO;AACrC,UAAO,QAAQ,MAAM,OAAO,OAAO;AACnC;;EAGF,IAAI;AACJ,MAAI;AACF,sBAAmB,MAAM,KAAK,QAAQ,WACpC,QAAQ,IACR,KAAK,gBAAgB,OACtB;WACM,OAAO;AACd,OAAI,KAAK,WAAY;GACrB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,QAAK,OAAO,MACV,4EACA,OAAO,MACP,OAAO,YACP,QAAQ,IACR,IAAI,QACL;GACD,MAAM,eAAe,IAAI,aAAa,mBAAmB,OAAO,IAAI;AACpE,UAAO,OAAO,aAAa;AAC3B,UAAO,QAAQ,WAAW,IAAI,OAAO;AACrC,UAAO,QAAQ,MAAM,OAAO,OAAO;AACnC;;AAGF,MAAI,KAAK,WAAY;AAErB,MAAI,iBAAiB,WAAW,UAAU,QAAQ;GAChD,MAAM,eAAe,iBAAiB,OAAO,WAAW;AACxD,QAAK,OAAO,MACV,gFACA,OAAO,MACP,OAAO,YACP,iBAAiB,IACjB,aACD;GACD,MAAM,QAAQ,IAAI,aAChB,mBAAmB,uBACnB,IAAI,MAAM,+BAA+B,eAAe,CACzD;AACD,UAAO,OAAO,MAAM;AACpB,UAAO,QAAQ,WAAW,IAAI,OAAO;QAErC,QAAO,UAAU;AAGnB,SAAO,QAAQ,MAAM,OAAO,OAAO;;CAGrC,MAAc,gBACZ,OACe;EACf,MAAM,eAAe,MAAM,GAAG,OAAO;EAErC,MAAM,YAAY,IAAI,IAAI,MAAM,KAAK,EAAE,aAAa,OAAO,MAAM,CAAC;EAClE,MAAM,OAAO,MAAM,KAAK,EAAE,aAAa;GACrC,MAAM,YAAsB,EAAE;GAC9B,MAAM,eAAyB,EAAE;AACjC,QAAK,MAAM,OAAO,OAAO,iBAAiB;AACxC,QAAI,CAAC,IAAK;AACV,QAAI,UAAU,IAAI,IAAI,CACpB,WAAU,KAAK,IAAI;SACd;KACL,MAAM,OAAO,KAAK,iBAAiB,IAAI,IAAI;AAC3C,SAAI,SAAS,KAAA,EAAW,cAAa,KAAK,KAAK;;;GAGnD,MAAM,UAAU,GAAG,OAAO,WAAW,GAAG,OAAO,OAAO,GAAG,GAAG,OAAO;GACnE,MAAM,WAAW,KAAK,uBAAuB,IAAI,QAAQ;AACzD,OAAI,aAAa,KAAA,EAAW,cAAa,KAAK,SAAS;AACvD,UAAO;IACL,KAAK,OAAO;IACZ,YAAY,OAAO;IACnB,OAAO,OAAO,OAAO;IACrB,QAAQ,OAAO;IACf,YAAY,OAAO,WAAW,KAAK,OAAO,GAAG,UAAU;IACvD;IACA;IACD;IACD;EAEF,MAAM,UAA4B,EAAE,MAAM;EAE1C,IAAI;AACJ,MAAI;AACF,YAAS,MAAM,KAAK,QAAQ,UAC1B,SACA,KAAK,gBAAgB,QACrB,EAAE,cAAc,CACjB;WACM,OAAO;AACd,OAAI,KAAK,WAAY;AACrB,QAAK,MAAM,EAAE,QAAQ,YAAY,OAAO;IACtC,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,WAAO,OAAO,IAAI,aAAa,mBAAmB,OAAO,IAAI,CAAC;AAC9D,WAAO,QAAQ,WAAW,IAAI,OAAO;AACrC,WAAO,QAAQ,MAAM,OAAO,OAAO;;AAErC;;AAGF,MAAI,KAAK,WAAY;AAErB,OAAK,MAAM,QAAQ,MAAM;AACvB,OAAI,EAAE,KAAK,OAAO,OAAO,MAAO;GAChC,MAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,QAAK,qBAAqB,KAAK,KAAK,KAAK,GAAG;GAC5C,MAAM,UAAU,GAAG,KAAK,WAAW,GAAG,KAAK,MAAM,GAAG,KAAK;AACzD,QAAK,uBAAuB,IAAI,SAAS,KAAK,GAAG;;AAGnD,OAAK,MAAM,EAAE,QAAQ,YAAY,OAAO;AACtC,OAAI,EAAE,OAAO,SAAS,OAAO,OAAO;AAClC,SAAK,OAAO,MACV,yEACA,OAAO,MACP,OAAO,YACP,OAAO,MACR;IACD,MAAM,QAAQ,IAAI,aAChB,mBAAmB,uBACnB,IAAI,MAAM,YAAY,OAAO,MAAM,kCAAkC,CACtE;AACD,WAAO,OAAO,MAAM;AACpB,WAAO,QAAQ,WAAW,IAAI,OAAO;AACrC,WAAO,QAAQ,MAAM,OAAO,OAAO;AACnC;;GAEF,MAAM,UAAU,OAAO,KAAK,OAAO;GAEnC,IAAI;AACJ,OAAI;AACF,uBAAmB,MAAM,KAAK,QAAQ,WACpC,QAAQ,IACR,KAAK,gBAAgB,OACtB;YACM,OAAO;AAEd,QAAI,KAAK,WAAY;IACrB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;AACrE,WAAO,OAAO,IAAI,aAAa,mBAAmB,OAAO,IAAI,CAAC;AAC9D,WAAO,QAAQ,WAAW,IAAI,OAAO;AACrC,WAAO,QAAQ,MAAM,OAAO,OAAO;AACnC;;AAIF,OAAI,KAAK,WAAY;AAErB,OAAI,iBAAiB,WAAW,UAAU,QAAQ;IAChD,MAAM,eAAe,iBAAiB,OAAO,WAAW;IACxD,MAAM,eAAe,IAAI,aACvB,mBAAmB,uBACnB,IAAI,MAAM,+BAA+B,eAAe,CACzD;AACD,WAAO,OAAO,aAAa;AAC3B,WAAO,QAAQ,WAAW,IAAI,OAAO;SAErC,QAAO,UAAU;AAGnB,UAAO,QAAQ,MAAM,OAAO,OAAO;;;CAIvC,MAAc,aACZ,QACA,YACA,OAAmB,WAAW,UAC9B,QACe;EACf,MAAM,iBAAiB,SACnB,YAAY,IAAI,CAAC,QAAQ,KAAK,gBAAgB,OAAO,CAAC,GACtD,KAAK,gBAAgB;EAEzB,IAAI,aAAa;EACjB,MAAM,+BAAe,IAAI,KAAqB;EAC9C,IAAI;EACJ,MAAM,iBAAiB,OAAO,QAAQ;EAEtC,MAAM,eAAe,eAA6C;AAChE,OAAI,WAAW,WAAW,EACxB;GAGF,MAAM,UAAU,0BAA0B,WAAW;GAErD,MAAM,UAA2B,EAAE;AACnC,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,QAAQ,OAAO,YAAY;IACjC,MAAM,YAAY,aAAa,IAAI,MAAM,WAAW;IAEpD,MAAM,OAAiB,EAAE;AACzB,QAAI,UAAW,MAAK,KAAK,UAAU;AACnC,QACE,SAAS,WAAW,kBACpB,kBACA,mBAAmB,UAEnB,MAAK,KAAK,eAAe;IAG3B,MAAM,SAAS,IAAI,cACjB,OAAO,YAAY,EACnB,OACA,MACA,OAAO,MACP,MAAM,YACN,CAAC,MAAM,MAAM,EACb,MAAM,QACN,MAAM,WACP;AAED,YAAQ,KAAK,OAAO;AACpB,iBAAa,IAAI,MAAM,YAAY,MAAM;AACzC,QAAI,SAAS,WAAW,eAAgB,kBAAiB;;AAG3D,UAAO,QAAQ,OAAO,IAAI,GAAG,QAAQ;;EAGvC,IAAI,OAAO,MAAM,KAAK,eAAe,KACnC,OAAO,cACP,YACA,EAAE,qBAAqB,OAAO,MAAM,EACpC,KAAA,GACA,eACD;EAED,IAAI,QAAgC,EAAE;EAEtC,IAAI;AACJ,KAAG;AACD,OAAI,eAAe,QACjB;AAEF,QAAK,MAAM,SAAS,KAAK,QACvB,cAAa,KAAK,IAAI,YAAY,MAAM,WAAW,EAAE;GAGvD,IAAI,aAAa,KAAK,QAAQ,KAAK,UACjC,uBAAuB,MAAM,CAC9B;AAED,OAAI,MAAM,SAAS,GAAG;AACpB,iBAAa,CAAC,GAAG,OAAO,GAAG,WAAW;AACtC,YAAQ,EAAE;;AAGZ,OAAI,kBAAkB,mBAAmB,IACvC,cAAa,WAAW,QACrB,OAAO,GAAG,UAAU,kBAAkB,eACxC;AAEH,gBAAa,iBAAiB,YAAY,OAAO,OAAO;AACxD,gBAAa,WAAW,QACrB,OAAO,CAAC,KAAK,uBAAuB,IAAI,GAAG,QAAQ,WAAW,CAChE;AAED,aAAU,CAAC,CAAC,KAAK;AAEjB,OAAI,WAAW,SAAS,GAAG;AACzB,eAAW,MAAM,GAAG,MAAM;AACxB,SAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,WACrC,QAAO,EAAE,QAAQ,aAAa,EAAE,QAAQ,aAAa,KAAK;AAE5D,SAAI,EAAE,QAAQ,UAAU,EAAE,QAAQ,MAChC,QAAO,EAAE,QAAQ,QAAQ,EAAE,QAAQ,QAAQ,KAAK;AAElD,YAAO,EAAE,QAAQ,UAAU,EAAE,QAAQ;MACrC;AAEF,QAAI,SAAS;KACX,MAAM,QAAQ,8BAA8B,WAAW;AACvD,aAAQ,MAAM;AACd,iBAAY,MAAM,KAAK;UAEvB,aAAY,WAAW;;AAI3B,OAAI,QACF,QAAO,MAAM,KAAK,MAAO;WAEpB;AAET,MAAI,MAAM,SAAS,EACjB,aAAY,MAAM;AAGpB,SAAO,QAAQ,OAAO,eAAe,WAAW;;;;;ACv9BpD,IAAa,cAAb,MAAyB;CACvB;CACA;CACA;CACA;CACA,SAA6C,EAAE;CAE/C,mBAAmB,SAAgC;AACjD,OAAK,iBAAiB;AACtB,SAAO;;CAGT,kBAAkB,SAAmC;AACnD,OAAK,gBAAgB;AACrB,SAAO;;CAGT,kBAAkB,SAAmC;AACnD,OAAK,gBAAgB;AACrB,SAAO;;CAGT,sBAAsB,SAAuC;AAC3D,OAAK,oBAAoB;AACzB,SAAO;;CAGT,4BAA4B,OAAqB;AAC/C,OAAK,OAAO,0BAA0B;AACtC,SAAO;;CAGT,sBAAsB,OAAqB;AACzC,OAAK,OAAO,oBAAoB;AAChC,SAAO;;CAGT,MACE,SACA,QACA,gBACA,UACA,IACA,qBACc;AASd,SARe,KAAK,YAClB,SACA,QACA,gBACA,UACA,IACA,oBACD,CACa;;CAGhB,YACE,SACA,QACA,gBACA,UACA,IACA,qBACY;AACZ,MAAI,CAAC,KAAK,eACR,OAAM,IAAI,MAAM,8BAA8B;EAGhD,MAAM,gBAAgB,KAAK,iBAAiB,IAAI,wBAAwB,GAAG;EAC3E,MAAM,gBAAgB,KAAK,iBAAiB,IAAI,wBAAwB,GAAG;EAC3E,MAAM,oBACJ,KAAK,qBAAqB,IAAI,4BAA4B,GAAG;EAE/D,MAAM,cAAc,IAAI,YACtB,QACA,eACA,eACA,mBACA,KAAK,gBACL,gBACA,SACA,UACA,qBACA,KAAK,OACN;AAED,SAAO;GACL;GACA;GACA;GACA,gBAAgB,KAAK;GACrB;GACD;;;;;AC1GL,eAAsB,wBAAmD;CACvE,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,EAAE,kBAAkB,MAAM,OAAO;AACvC,QAAO,IAAI,OAAiB,EAC1B,SAAS,IAAI,cAAc,IAAI,QAAQ,CAAC,EACzC,CAAC;;;;;;;;;;ACgBJ,SAAgB,4BACd,eAAe,OAKf;CACA,IAAI,gBAAgB;CACpB,IAAI,mBAAkC,QAAQ,SAAS;CAEvD,MAAM,SAAyB;EAC7B,IAAI,aAAa;AACf,UAAO;;EAET,IAAI,YAAY;AACd,UAAO;;EAEV;CAED,MAAM,eAAe,UAAmB;AACtC,kBAAgB;;CAGlB,MAAM,gBAAgB,YAA2B;AAC/C,qBAAmB;;AAGrB,QAAO;EAAC;EAAQ;EAAa;EAAa;;;;ACH5C,IAAa,aAAb,cAAgC,MAAM;CACpC,YAAY,SAAkB;AAC5B,QAAM,WAAW,UAAU;AAE3B,OAAK,OAAO;;;;;;;;;ACuBhB,IAAa,UAAb,MAAyC;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,QACA,uBACA,OACA,YACA,sBACA,UACA,cACA,iBACA,gBACA,UACA,iBACA;AACA,OAAK,SAAS;AACd,OAAK,wBAAwB;AAC7B,OAAK,QAAQ;AACb,OAAK,aAAa;AAClB,OAAK,uBAAuB;AAC5B,OAAK,WAAW;AAChB,OAAK,eAAe;AACpB,OAAK,kBAAkB;AACvB,OAAK,iBAAiB;AACtB,OAAK,WAAW;AAChB,OAAK,kBAAkB;EAEvB,MAAM,CAAC,QAAQ,aAAa,gBAC1B,4BAA4B,MAAM;AACpC,OAAK,iBAAiB;AACtB,OAAK,cAAc;AACnB,OAAK,eAAe;AAEpB,OAAK,SAAS,UACZ,kBAAkB,aACjB,OAAO,UAA0B;AAChC,QAAK,OAAO,MACV,yCACA,MAAM,OACN,MAAM,MAAM,SACZ,MAAM,IACP;IAEJ;AAED,OAAK,qBAAqB,OAAO;;CAGnC,OAAuB;AACrB,OAAK,OAAO,QAAQ,SAAS;AAE7B,MAAI,KAAK,eAAe,WACtB,QAAO,KAAK;AAGd,OAAK,YAAY,KAAK;EAEtB,MAAM,gBAAgB,YAAY;AAChC,SAAM,KAAK,gBAAgB,KAAK,KAAK;AAErC,QAAK,qBAAqB,MAAM;AAChC,QAAK,WAAW,UAAU;;AAG5B,OAAK,aAAa,eAAe,CAAC;AAElC,SAAO,KAAK;;CAGd,kBACE,WACA,QACA,QAC4C;AAC5C,OAAK,OAAO,QACV,0CACA,WACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;EAG9C,MAAM,iBADU,KAAK,sBAAsB,eAAe,CAC3B,QAC5B,WACC,CAAC,aAAa,OAAO,cAAc,OAAO,GAAG,WAAW,UAAU,CACrE;EAED,MAAM,aAAa,SAAS,SAAS,OAAO,OAAO,IAAI,IAAI;EAC3D,MAAM,QAAQ,QAAQ,SAAS,eAAe;EAC9C,MAAM,cAAc,eAAe,MAAM,YAAY,aAAa,MAAM;EAExE,MAAM,UAAU,aAAa,QAAQ,eAAe;EACpD,MAAM,aAAa,UAAU,OAAO,aAAa,MAAM,GAAG,KAAA;AAE1D,SAAO,QAAQ,QAAQ;GACrB,SAAS;GACT,SAAS,UAAU;IAAE,QAAQ;IAAK,OAAO,eAAe;IAAQ;GAChE;GACA,MAAM,gBAEA,KAAK,kBACH,WACA;IAAE,QAAQ;IAAa;IAAO,EAC9B,OACD,GACH,KAAA;GACL,CAAC;;CAGJ,MAAM,IACJ,IACA,MACA,kBACA,QACoB;AACpB,OAAK,OAAO,QAAQ,mBAAmB,IAAI,KAAK;AAEhD,SAAO,MAAM,KAAK,aAAa,IAC7B,IACA,MACA,kBACA,OACD;;CAGH,MAAM,UACJ,MACA,MACA,kBACA,QACoB;AACpB,OAAK,OAAO,QAAQ,2BAA2B,MAAM,KAAK;EAE1D,MAAM,aAAa,MAAM,KAAK,aAAa,YACzC,MACA,MACA,kBACA,OACD;AAED,MAAI,CAAC,WACH,OAAM,IAAI,MAAM,iCAAiC,OAAO;AAG1D,SAAO,MAAM,KAAK,IAChB,YACA,MACA,kBACA,OACD;;CAGH,MAAM,cACJ,YACA,MACA,kBACA,QACoB;AACpB,OAAK,OAAO,QAAQ,qCAAqC,YAAY,KAAK;AAE1E,SAAO,MAAM,KAAK,aAAa,cAC7B,YACA,MACA,kBACA,OACD;;CAGH,MAAM,yBACJ,UACA,kBACA,kBACA,QACmB;EACnB,MAAM,gBAAgB,MAAM,KAAK,gBAAgB,YAC/C,UACA,CAAC,iBAAiB,EAClB,KAAA,GACA,kBACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,SAAO,cAAc,QAAQ,KAAK,QAAQ,IAAI,SAAS;;CAGzD,MAAM,yBACJ,UACA,kBACA,kBACA,QACmB;EACnB,MAAM,gBAAgB,MAAM,KAAK,gBAAgB,YAC/C,UACA,CAAC,iBAAiB,EAClB,KAAA,GACA,kBACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,SAAO,cAAc,QAAQ,KAAK,QAAQ,IAAI,SAAS;;CAGzD,MAAM,cACJ,YACA,MACA,QACA,QACA,kBACA,QACkD;AAClD,OAAK,OAAO,QACV,uDACA,YACA,MACA,QACA,OACD;EAED,MAAM,SAAS,MAAM,UAAU;EAE/B,MAAM,YAAY,MAAM,KAAK,eAAe,aAC1C,YACA,QACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,MAAM,YAAY,OAAO,KAAK,UAAU,SAAS;EACjD,MAAM,SAAkD,EAAE;AAE1D,OAAK,MAAM,SAAS,WAAW;AAC7B,OAAI,CAAC,aAAa,MAAM,MAAM,CAC5B;AAGF,kBAAe,cAAc,IAAI,YAAY,CAAC;GAE9C,MAAM,cAAc,MAAM,KAAK,eAAe,SAC5C,YACA,OACA,QACA,IACA,QACA,QACA,OACD;AAED,UAAO,SAAS;IACd,SAAS,YAAY;IACrB,SAAS,YAAY;IACrB,YAAY,YAAY;IACxB,MAAM,YAAY,OACd,YAAY;AAYV,aAXiB,MAAM,KAAK,cAC1B,YACA,MACA,QACA;MACE,QAAQ,YAAY;MACpB,OAAO,YAAY,QAAQ;MAC5B,EACD,kBACA,OACD,EACe;QAElB,KAAA;IACL;;AAGH,SAAO;;CAGT,MAAM,KACJ,QACA,MACA,QACA,kBACA,QACmC;AACnC,OAAK,OAAO,QAAQ,iCAAiC,QAAQ,MAAM,OAAO;EAE1E,IAAI;AACJ,MAAI,OAAO,KAAK;AACd,OAAI,OAAO,SAAS,OAAO,MAAM,SAAS,EACxC,OAAM,IAAI,MAAM,mDAAmD;AAGrE,aAAU,MAAM,KAAK,UACnB,OAAO,KACP,MACA,QACA,kBACA,OACD;AAED,OAAI,OAAO,KACT,WAAU,aAAa,SAAS,OAAO,KAAK;aAErC,OAAO,OAAO;AACvB,aAAU,MAAM,KAAK,YACnB,OAAO,OACP,MACA,QACA,kBACA,OACD;AAED,OAAI,OAAO,KACT,WAAU,aAAa,SAAS,OAAO,KAAK;aAErC,OAAO,UAAU;AAC1B,aAAU,MAAM,KAAK,eACnB,OAAO,UACP,MACA,QACA,kBACA,OACD;AAED,OAAI,OAAO,KACT,WAAU,aAAa,SAAS,OAAO,KAAK;aAErC,OAAO,KAChB,WAAU,MAAM,KAAK,WACnB,OAAO,MACP,MACA,QACA,kBACA,OACD;MAED,OAAM,IAAI,MAAM,8BAA8B;AAGhD,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,SAAO;;CAGT,MAAM,OACJ,UACA,QACA,QACA,MACkB;AAClB,OAAK,OAAO,QACV,6BACA,SAAS,OAAO,IAChB,SAAS,OAAO,cAChB,SAAS,OAAO,KACjB;EACD,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;AAEhD,iBAAe,cAAc,IAAI,YAAY,CAAC;EA+B9C,IAAI,UAAoB,CATH,qBApB0B;GAC7C,OAAO,SAAS,OAAO;GACvB,SAAS;GACT,YAAY,SAAS,OAAO;GAC5B,SAAS;IACP,WAAW,SAAS,OAAO;IAC3B,WAAW,SAAS,OAAO,IAAI;IAC/B,OAAO,SAAS,OAAO,IAAI;IAC3B,iBAAiB,SAAS,OAAO;IACjC,cAAc,SAAS,OAAO;IAC/B;GACD,MAAM,SAAS,OAAO;GACtB,MAAM,SAAS,OAAO;GACtB,QAAQ,SAAS,OAAO;GACxB,MAAM,SAAS,OAAO;GACtB,kBAAkB,SAAS,OAAO,oBAAoB,EACpD,gBAAgB,GACjB;GACF,CAEqD,EAChC,sBAAsB;GAC1C,YAAY,SAAS,OAAO;GAC5B,OAAO,SAAS,OAAO;GACvB,aAAa;GACb,WAAW,SAAS,MAAM,SAAS;GACnC,cAAc,SAAS;GACxB,CAAC,CAEmD;AACrD,MAAI,OACF,WAAU,MAAM,YAAY,SAAS,QAAQ,OAAO;EAGtD,MAAM,QAAQI,IAAQ;EACtB,MAAM,UAAU,mBAAmB,OAAO,KAAK;EAE/C,MAAM,MAAW;GACf,IAAI;GACJ,MAAM;GACN,YAAY,SAAS,OAAO;GAC5B,OAAO;GACP,QAAQ;GACR;GACA,YAAY,EAAE;GACd,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,WAAW,EAAE;GACb,YAAY;GACZ,cAAc,EAAE;GAChB,MAAM;GACP;EAED,MAAM,UAAmB;GACvB,IAAI;GACJ,QAAQ,UAAU;GAClB;GACA,kBAAkB;IAChB,SAAS;IACT;IACA,aAAa,EAAE;IAChB;GACD,MAAM;GACP;AACD,OAAK,WAAW,YAAY,QAAQ;AACpC,OAAK,eAAe,QAAQ,IAAI,QAAQ;AAExC,QAAM,KAAK,MAAM,QAAQ,IAAI;AAE7B,SAAO;;CAGT,MAAM,eACJ,IACA,QACA,QACA,MACkB;AAClB,OAAK,OAAO,QAAQ,uBAAuB,GAAG;EAC9C,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;AAEhD,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,IAAI,SAAS,qBAAqB,GAAG;AAErC,MAAI,OACF,UAAS,MAAM,WAAW,QAAQ,QAAQ,OAAO;EAGnD,MAAM,QAAQA,IAAQ;EACtB,MAAM,UAAU,mBAAmB,OAAO,KAAK;EAE/C,MAAM,MAAW;GACf,IAAI;GACJ,MAAM;GACN,YAAY;GACZ,OAAO;GACP,QAAQ;GACR,SAAS,CAAC,OAAO;GACjB,YAAY,EAAE;GACd,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,WAAW,EAAE;GACb,YAAY;GACZ,cAAc,EAAE;GAChB,MAAM;GACP;EAED,MAAM,UAAmB;GACvB,IAAI;GACJ,QAAQ,UAAU;GAClB;GACA,kBAAkB;IAChB,SAAS;IACT;IACA,aAAa,EAAE;IAChB;GACD,MAAM;GACP;AACD,OAAK,WAAW,YAAY,QAAQ;AACpC,OAAK,eAAe,QAAQ,IAAI,QAAQ;AAExC,QAAM,KAAK,MAAM,QAAQ,IAAI;AAE7B,SAAO;;CAGT,MAAM,QACJ,OACA,QACA,SACA,QACA,MACkB;AAClB,OAAK,OAAO,QACV,sCACA,OACA,QACA,QACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;EAChD,MAAM,QAAQ,qBAAqB,QAAQ;EAC3C,MAAM,QAAQA,IAAQ;EACtB,MAAM,UAAU,mBAAmB,OAAO,KAAK;EAE/C,MAAM,MAAW;GACf,IAAI;GACJ,MAAM;GACN,YAAY;GACL;GACC;GACC;GACT,YAAY,EAAE;GACd,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,WAAW,EAAE;GACb,YAAY;GACZ,cAAc,EAAE;GAChB,MAAM;GACP;EAED,MAAM,UAAmB;GACvB,IAAI;GACJ,QAAQ,UAAU;GAClB;GACA,kBAAkB;IAChB,SAAS;IACT;IACA,aAAa,EAAE;IAChB;GACD,MAAM;GACP;AACD,OAAK,WAAW,YAAY,QAAQ;AACpC,OAAK,eAAe,QAAQ,IAAI,QAAQ;AAExC,QAAM,KAAK,MAAM,QAAQ,IAAI;AAE7B,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,SAAO;;CAGT,MAAM,KACJ,OACA,QACA,YACA,QACA,MACkB;AAClB,OAAK,OAAO,QACV,8CACA,OACA,QACA,WAAW,QACX,WACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,MAAI,WAAW,WAAW,EACxB,OAAM,IAAI,MAAM,uCAAuC;EAGzD,MAAM,QAAQ,wBAAwB,WAAW;EACjD,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;EAChD,MAAM,QAAQA,IAAQ;EACtB,MAAM,UAAU,mBAAmB,OAAO,KAAK;EAE/C,MAAM,MAAW;GACf,IAAI;GACJ,MAAM;GACN,YAAY;GACZ;GACA;GACA,SAAS,EAAE;GACX;GACA,WAAW;GACX,WAAW,EAAE;GACb,YAAY;GACZ,cAAc,EAAE;GAChB,MAAM;GACP;EAED,MAAM,UAAmB;GACvB,IAAI;GACJ,QAAQ,UAAU;GAClB;GACA,kBAAkB;IAChB,SAAS;IACT;IACA,aAAa,EAAE;IAChB;GACD,MAAM;GACP;AACD,OAAK,WAAW,YAAY,QAAQ;AACpC,OAAK,eAAe,QAAQ,IAAI,QAAQ;AAExC,QAAM,KAAK,MAAM,QAAQ,IAAI;AAE7B,iBAAe,cAAc,IAAI,YAAY,CAAC;AAE9C,SAAO;;CAGT,MAAM,aACJ,SACA,QACA,MAC+B;AAC/B,OAAK,OAAO,QAAQ,6BAA6B,QAAQ,KAAK,OAAO;AAErE,iBAAe,cAAc,IAAI,YAAY,CAAC;AAC9C,uBAAqB,QAAQ,KAAK;AAClC,OAAK,MAAM,WAAW,QAAQ,KAC5B,sBAAqB,QAAQ;EAE/B,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;EAChD,MAAM,iCAAiB,IAAI,KAAqB;AAChD,OAAK,MAAM,WAAW,QAAQ,KAC5B,gBAAe,IAAI,QAAQ,KAAKA,IAAQ,CAAC;EAE3C,MAAM,UAAUA,IAAQ;EACxB,MAAM,cAAc,CAAC,GAAG,eAAe,QAAQ,CAAC;EAChD,MAAM,YAAqB;GACzB,GAAG;GACH;GACA;GACD;EACD,MAAM,2BAAW,IAAI,KAAsB;AAC3C,OAAK,MAAM,WAAW,QAAQ,MAAM;GAElC,MAAM,UAAmB;IACvB,IAFY,eAAe,IAAI,QAAQ,IAAI;IAG3C,QAAQ,UAAU;IAClB;IACA,kBAAkB;KAChB,SAAS;KACT;KACA,aAAa,EAAE;KAChB;IACD,MAAM;IACP;AACD,QAAK,WAAW,YAAY,QAAQ;AACpC,QAAK,eAAe,QAAQ,IAAI,UAAU;AAC1C,YAAS,IAAI,QAAQ,KAAK,QAAQ;;EAEpC,MAAM,aAAa,gBAAgB,QAAQ,KAAK;EAChD,MAAM,eAAyB,EAAE;AACjC,MAAI;AACF,QAAK,MAAM,OAAO,YAAY;AAC5B,mBAAe,cAAc,IAAI,YAAY,CAAC;IAC9C,MAAM,UAAU,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,IAAI;IACvD,MAAM,QAAQ,eAAe,IAAI,IAAI;IACrC,MAAM,YAAY,QAAQ,UAAU,KACjC,WAAW,eAAe,IAAI,OAAO,CACvC;IACD,MAAM,MAAW;KACf,IAAI;KACJ,MAAM;KACN,YAAY,QAAQ;KACpB,OAAO,QAAQ;KACf,QAAQ,QAAQ;KAChB,SAAS,QAAQ;KACjB,YAAY,EAAE;KACd,WAAW;KACX;KACA,YAAY;KACZ,cAAc,EAAE;KAChB,MAAM;KACP;AACD,UAAM,KAAK,MAAM,QAAQ,IAAI;AAC7B,iBAAa,KAAK,IAAI;;WAEjB,OAAO;AACd,QAAK,MAAM,OAAO,cAAc;IAC9B,MAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,QAAI;AACF,WAAM,KAAK,MAAM,OAAO,MAAM;YACxB;;AAIV,QAAK,MAAM,WAAW,SAAS,QAAQ,CACrC,MAAK,WAAW,WACd,QAAQ,IACRC,cAAY,uBAAuB,CACpC;AAEH,SAAM;;AAKR,SAHqC,EACnC,MAAM,OAAO,YAAY,SAAS,EACnC;;CAIH,MAAM,UACJ,SACA,QACA,MAC0B;AAC1B,OAAK,OAAO,QAAQ,0BAA0B,QAAQ,KAAK,OAAO;AAElE,iBAAe,cAAc,IAAI,YAAY,CAAC;AAC9C,2BAAyB,QAAQ,KAAK;AACtC,OAAK,MAAM,WAAW,QAAQ,KAC5B,yBAAwB,QAAQ;EAElC,MAAM,mCAAkB,IAAI,MAAM,EAAC,aAAa;EAChD,MAAM,iCAAiB,IAAI,KAAqB;AAChD,OAAK,MAAM,WAAW,QAAQ,KAC5B,gBAAe,IAAI,QAAQ,KAAKD,IAAQ,CAAC;EAE3C,MAAM,UAAUA,IAAQ;EACxB,MAAM,cAAc,CAAC,GAAG,eAAe,QAAQ,CAAC;EAChD,MAAM,YAAqB;GACzB,GAAG;GACH;GACA;GACD;EACD,MAAM,2BAAW,IAAI,KAAsB;AAC3C,OAAK,MAAM,WAAW,QAAQ,MAAM;GAElC,MAAM,UAAmB;IACvB,IAFY,eAAe,IAAI,QAAQ,IAAI;IAG3C,QAAQ,UAAU;IAClB;IACA,kBAAkB;KAChB,SAAS;KACT;KACA,aAAa,EAAE;KAChB;IACD,MAAM;IACP;AACD,QAAK,WAAW,YAAY,QAAQ;AACpC,QAAK,eAAe,QAAQ,IAAI,UAAU;AAC1C,YAAS,IAAI,QAAQ,KAAK,QAAQ;;EAEpC,MAAM,aAAa,gBAAgB,QAAQ,KAAK;EAChD,MAAM,eAAyB,EAAE;AACjC,MAAI;AACF,QAAK,MAAM,OAAO,YAAY;AAC5B,mBAAe,cAAc,IAAI,YAAY,CAAC;IAC9C,MAAM,UAAU,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,IAAI;IACvD,MAAM,QAAQ,eAAe,IAAI,IAAI;IACrC,MAAM,YAAY,CAChB,GAAG,QAAQ,UAAU,KAAK,WAAW,eAAe,IAAI,OAAO,CAAE,EACjE,GAAG,QAAQ,aACZ;IACD,MAAM,MAAW;KACf,IAAI;KACJ,MAAM;KACN,YAAY,QAAQ;KACpB,OAAO,QAAQ;KACf,QAAQ,QAAQ;KAChB,SAAS,EAAE;KACX,YAAY,QAAQ;KACpB,WAAW;KACX;KACA,YAAY;KACZ,cAAc,EAAE;KAChB,MAAM;KACP;AACD,UAAM,KAAK,MAAM,QAAQ,IAAI;AAC7B,iBAAa,KAAK,IAAI;;WAEjB,OAAO;AACd,QAAK,MAAM,OAAO,cAAc;IAC9B,MAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,QAAI;AACF,WAAM,KAAK,MAAM,OAAO,MAAM;YACxB;;AAIV,QAAK,MAAM,WAAW,SAAS,QAAQ,CACrC,MAAK,WAAW,WACd,QAAQ,IACRC,cAAY,uBAAuB,CACpC;AAEH,SAAM;;AAKR,SAHgC,EAC9B,MAAM,OAAO,YAAY,SAAS,EACnC;;CAIH,MAAM,gBACJ,UACA,UACA,kBACA,SAAiB,QACjB,QACA,QACkB;AAClB,OAAK,OAAO,QACV,qEACA,UACA,UACA,kBACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,IAAI,UAAoB,CACtB,sBAAsB,UAAU,UAAU,iBAAiB,CAC5D;AAED,MAAI,OACF,WAAU,MAAM,YAAY,SAAS,QAAQ,OAAO;AAGtD,SAAO,MAAM,KAAK,QAAQ,UAAU,QAAQ,SAAS,OAAO;;CAG9D,MAAM,mBACJ,UACA,UACA,kBACA,SAAiB,QACjB,QACA,QACkB;AAClB,OAAK,OAAO,QACV,wEACA,UACA,UACA,kBACA,OACD;AAED,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,IAAI,UAAoB,CACtB,yBAAyB,UAAU,UAAU,iBAAiB,CAC/D;AAED,MAAI,OACF,WAAU,MAAM,YAAY,SAAS,QAAQ,OAAO;AAGtD,SAAO,MAAM,KAAK,QAAQ,UAAU,QAAQ,SAAS,OAAO;;CAG9D,aAAa,OAAe,QAAwC;AAClE,OAAK,OAAO,QAAQ,wBAAwB,MAAM;AAElD,iBAAe,cAAc,IAAI,YAAY,CAAC;EAE9C,MAAM,UAAU,KAAK,WAAW,aAAa,MAAM;AAEnD,MAAI,CAAC,SAAS;GACZ,MAAM,uBAAM,IAAI,MAAM,EAAC,aAAa;AACpC,UAAO,QAAQ,QAAQ;IACrB,IAAI;IACJ,QAAQ,UAAU;IAClB,iBAAiB;IACjB,mBAAmB;IACnB,OAAOA,cAAY,gBAAgB;IACnC,kBAAkB;KAChB,SAAS;KACT,iBAAiB;KACjB,aAAa,EAAE;KAChB;IACD,MAAM;KAAE,SAAS;KAAO,aAAa,CAAC,MAAM;KAAE;IAC/C,CAAC;;AAGJ,SAAO,QAAQ,QAAQ,QAAQ;;CAGjC,MAAc,UACZ,KACA,MACA,QACA,kBACA,QACmC;AACnC,OAAK,OAAO,QAAQ,yBAAyB,IAAI,OAAO;EAExD,MAAM,aAAa,QAAQ,SAAS,SAAS,OAAO,OAAO,IAAI,IAAI;EACnE,MAAM,QAAQ,QAAQ,SAAS,IAAI;EACnC,MAAM,WAAW,IAAI,MAAM,YAAY,aAAa,MAAM;EAE1D,MAAM,UAAU,MAAM,KAAK,aAAa,QACtC,UACA,MACA,kBACA,OACD;EAGD,MAAM,aADU,aAAa,QAAQ,IAAI,SACZ,OAAO,aAAa,MAAM,GAAG,KAAA;AAE1D,SAAO;GACL;GACA,SAAS,UAAU;IAAE,QAAQ;IAAK,OAAO,IAAI;IAAQ;GACrD;GACD;;CAGH,MAAc,YACZ,OACA,MACA,QACA,kBACA,QACmC;AACnC,OAAK,OAAO,QAAQ,6BAA6B,MAAM,OAAO;EAE9D,MAAM,MAAM,MAAM,KAAK,aAAa,aAClC,OACA,MACA,kBACA,OACD;AAED,SAAO,MAAM,KAAK,UAAU,KAAK,MAAM,QAAQ,kBAAkB,OAAO;;CAG1E,MAAc,eACZ,UACA,MACA,QACA,kBACA,QACmC;AACnC,OAAK,OAAO,QAAQ,6BAA6B,SAAS;EAU1D,MAAM,OARgB,MAAM,KAAK,gBAAgB,YAC/C,UACA,CAAC,QAAQ,EACT,QACA,kBACA,OACD,EAEyB,QAAQ,KAAK,QAAQ,IAAI,SAAS;AAC5D,SAAO,MAAM,KAAK,UAAU,KAAK,MAAM,QAAQ,KAAA,GAAW,OAAO;;CAGnE,MAAc,WACZ,MACA,MACA,QACA,kBACA,QACmC;AACnC,OAAK,OAAO,QAAQ,qBAAqB,KAAK;AAE9C,SAAO,MAAM,KAAK,aAAa,WAC7B,MACA,MACA,QACA,kBACA,OACD;;CAGH,eAAuB,OAAe,MAAqB;EACzD,MAAM,QAAyB;GAC7B;GACA,SAAS;GACV;AACD,OAAK,SAAS,KAAK,kBAAkB,aAAa,MAAM,CAAC,YAAY,GAEnE;;;;;ACt5BN,IAAa,iBAAb,MAA4B;CAC1B;CACA,iBAAqD,EAAE;CACvD,mBAAiE,EAAE;CACnE,WAAoC,EAAE,sBAAsB,OAAO;CACnE,aAAmC,EAAE;CACrC,qBAAiD,EAAE;CACnD;CACA,iBAA4C,EAAE;CAC9C;CACA,oBAA+C;CAC/C;CACA;CACA;CACA;CACA;CACA,wBAAgC;CAChC;CACA;CACA;CACA;CACA,gBAAoD,EAAE;CACtD,sBACE;CACF,qBAAuD,EAAE;CACzD;CACA;CACA;CACA;CACA;CACA;CACA;CACA,oBAAmD,EAAE;CAErD,WAAW,QAAuB;AAChC,OAAK,SAAS;AACd,SAAO;;CAGT,mBAAmB,QAA0C;AAC3D,OAAK,iBAAiB;AACtB,SAAO;;CAGT,qBAAqB,WAAuD;AAC1E,OAAK,mBAAmB;AACxB,SAAO;;CAGT,aAAa,UAAiC;AAC5C,OAAK,WAAW;GAAE,GAAG,KAAK;GAAU,GAAG;GAAU;AACjD,SAAO;;CAGT,cAAc,WAA6B;AACzC,OAAK,WAAW,KAAK,UAAU;AAC/B,SAAO;;;;;;;;;CAUT,qBAAqB,SAAiC;AACpD,OAAK,mBAAmB,KAAK,QAAQ;AACrC,SAAO;;CAGT,yBAAyB,sBAAmD;AAC1E,OAAK,uBAAuB;AAC5B,SAAO;;CAGT,aAAa,UAAqC;AAChD,OAAK,kBAAkB;AACvB,SAAO;;CAGT,mBAAmB,QAA0C;AAC3D,OAAK,iBAAiB;GAAE,GAAG,KAAK;GAAgB,GAAG;GAAQ;AAC3D,SAAO;;CAGT,qBAAqB,QAAyC;AAC5D,OAAK,mBAAmB;AACxB,SAAO;;CAGT,wBAAwB,OAAuB;AAC7C,OAAK,sBAAsB,IAAI,IAAI,MAAM;AACzC,SAAO;;CAGT,sBAAsB,UAAmC;AACvD,OAAK,oBAAoB;AACzB,SAAO;;CAGT,SAAS,aAAgC;AACvC,OAAK,cAAc;AACnB,SAAO;;CAGT,aAAa,UAA2B;AACtC,OAAK,WAAW;AAChB,SAAO;;CAGT,sBAAsB,UAA8C;AAClE,OAAK,oBAAoB;AACzB,SAAO;;CAGT,WAAW,QAAgC;AACzC,OAAK,iBAAiB;AACtB,SAAO;;;;;;;;;CAUT,qBAAqB,iBAA4C;AAC/D,OAAK,kBAAkB,KAAK,gBAAgB;AAC5C,SAAO;;CAGT,UAAU,OAAqB;AAC7B,OAAK,gBAAgB;AACrB,SAAO;;CAGT,kBAAkB,QAA6B;AAC7C,OAAK,gBAAgB;AACrB,SAAO;;CAGT,eAAe,SAA2B;AACxC,OAAK,aAAa;AAClB,SAAO;;CAGT,wBAAwB,QAAoC;AAC1D,OAAK,sBAAsB;AAC3B,SAAO;;CAGT,qBAA2B;AACzB,OAAK,wBAAwB;AAC7B,SAAO;;;;;;;;;;CAWT,iBAAiB,MAAiC;AAChD,OAAK,cAAc,KAAK,KAAK;AAC7B,SAAO;;CAGT,uBAAuB,OAAuC;AAC5D,OAAK,qBAAqB;AAC1B,SAAO;;;;;;;CAQT,eAAe,QAAgC;AAC7C,OAAK,mBAAmB;AACxB,SAAO;;;;;;;CAQT,mBAAmB,IAAoB;AACrC,OAAK,iBAAiB;AACtB,SAAO;;;;;;;CAQT,gCAAgC,MAAmC;AACjE,OAAK,8BAA8B;AACnC,SAAO;;;;;;;;CAST,kBAAkB,SAA8B;AAC9C,OAAK,gBAAgB;AACrB,SAAO;;;;;;;;;;;;;;CAeT,qBAAqB,QAA4C;AAC/D,OAAK,wBAAwB;AAC7B,SAAO;;;;;;;CAQT,4BAA4B,SAAwC;AAClE,OAAK,0BAA0B;AAC/B,SAAO;;CAGT,2BAA6D;AAC3D,SAAO,KAAK;;CAGd,MAAM,QAA2B;AAE/B,UADe,MAAM,KAAK,aAAa,EACzB;;CAGhB,MAAM,cAAsC;AAC1C,MAAI,CAAC,KAAK,OACR,MAAK,SAAS,IAAI,cAAc,CAAC,UAAU,CAAC;AAG9C,MAAI,KAAK,kBAAkB,SAAS;AAClC,OAAI,KAAK,eAAe,SAAS,EAC/B,OAAM,IAAI,MACR,uGACD;AAEH,OAAI,KAAK,mBAAmB,WAAW,EACrC,OAAM,IAAI,MACR,uFACD;GAEH,MAAM,sBAAsB,CAAC,KAAK,mBAAmB,CAAC,KAAK;AAC3D,OAAI,uBAAuB,CAAC,KAAK,eAC/B,OAAM,IAAI,MACR,iGACD;AAEH,OAAI,uBAAuB,CAAC,KAAK,4BAC/B,OAAM,IAAI,MACR,8GACD;;AAIL,MAAI,KAAK,mBAAmB,SAAS,EACnC,MAAK,wBAAwB,KAAK,mBAAmB,KAAK,UAAU;AAClE,OAAI,cAAc,MAQhB,QAPkC;IAChC,cAAc;IACd,SAAS;IACT,MAAM,EACJ,QAAQ;KAAE,UAAU,MAAM;KAAU,YAAY;KAAiB,EAClE;IACF;AAaH,UAVkC;IAChC,cAAc;IACd,SAAS,MAAM;IACf,MAAM,EACJ,QAAQ;KACN,aAAa,MAAM;KACnB,YAAY;KACb,EACF;IACF;IAED;EAGJ,MAAM,wBAAwB,IAAI,uBAAuB;AACzD,MAAI,KAAK,iBAAiB,SAAS,GAAG;GACpC,MAAM,UAAU,sBAAsB,yBACpC,GAAG,KAAK,iBACT;AACD,QAAK,MAAM,UAAU,QACnB,KAAI,OAAO,WAAW,QACpB,MAAK,OAAO,MACV,+CACA,OAAO,MAAM,QACd;;AAIP,MAAI,KAAK,eAAe,SAAS,GAAG;GAClC,MAAM,UAAU,sBAAsB,gBACpC,GAAG,KAAK,eACT;AACD,QAAK,MAAM,UAAU,QACnB,KAAI,OAAO,WAAW,QACpB,MAAK,OAAO,MACV,6CACA,OAAO,MAAM,QACd;;EAKP,MAAM,eACJ,KAAK,mBACJ,KAAK,kBAAkB,WAAW,KAAK,iBACpC,MAAM,KAAK,uBAAuB,KAAK,eAAe,GACtD,MAAM,uBAAuB;AAEnC,MAAI,KAAK,sBAAsB,QAAQ;GACrC,MAAM,SAAS,MAAM,cAAc,cAAc,eAAe;AAChE,OAAI,CAAC,OAAO,WAAW,OAAO,MAC5B,OAAM,IAAI,MAAM,8BAA8B,OAAO,MAAM,UAAU;;EAIzE,MAAM,WAAW,aAAa,WAAW,eAAe;EAExD,MAAM,iBAAiB,IAAI,qBACzB,SACD;EACD,MAAM,gBAAgB,IAAI,oBACxB,SACD;EAED,MAAM,WAAW,KAAK,YAAY,IAAI,UAAU;EAChD,MAAM,WAAW,KAAK,sBAClB,IAAI,sBACF,uBACA,KAAK,oBACN,GACD,IAAI,0BAA0B,sBAAsB;EACxD,MAAM,QAAQ,KAAK,iBAAiB,IAAI,cAAc,UAAU,SAAS;EACzE,MAAM,aAAa,IAAI,mBAAmB,SAAS;EAQnD,MAAM,aAAa,IAAI,iBACrB,eACA,gBACA,uBAToC;GACpC,cAAc,KAAK,kBAAkB,gBAAgB;GACrD,gBAAgB,KAAK,kBAAkB,kBAAkB;GACzD,kBAAkB,KAAK,kBAAkB,oBAAoB;GAC9D,CAOA;AACD,QAAM,WAAW,SAAS;EAE1B,MAAM,iBAAiB,IAAI,qBACzB,SACD;EAED,MAAM,oBAAoB,IAAI,kBAAkB,gBAAgB,EAC9D,cAAc,KACf,CAAC;AACF,QAAM,kBAAkB,SAAS;EAEjC,MAAM,4BAA4B,IAAI,0BACpC,eACD;EAED,MAAM,iBAAkC,IAAI,qBAC1C,UACA,gBACA,gBACA,eACA,YACA,mBACA,0BACD;EAED,IAAI,kBAAkB,KAAK;EAC3B,IAAI,qBAAqB,KAAK,eAAe,kBAAkB;AAC/D,MAAI,CAAC,gBACH,KAAI,KAAK,kBAAkB,SAAS;GAIlC,MAAM,cAAc,IAAI,6BAFtB,KAAK,iBACJ,MAAM,KAAK,2BAA2B,KAAK,iBAAiB,EAG7D,UACA,OACA,YACA,KAAK,QACL,UACA,2BACA,KAAK,eAAe,aACrB;AACD,qBAAkB;AAClB,wBAAqB,KAAK,iBAAiB;AAC3C,OAAI,oBAAoB,sBACtB,UAAS,kBAAkB,UAAU,YAAY,UAAU,MAAM,CAAC;QAGpE,mBAAkB,IAAI,+BAElB,IAAI,kBACF,KAAK,QACL,uBACA,gBACA,UACA,YACA,gBACA,mBACA,2BACA,KAAK,qBACL,KAAK,gBACL,KAAK,mBACL,eACD,EACH,UACA,OACA,YACA,KAAK,QACL,UACA,KAAK,eAAe,aACrB;AAIL,QAAM,gBAAgB,MAAM,mBAAmB;EAE/C,MAAM,qBAAmC,MAAM,KAC7C,IAAI,IAAI,CAAC,GAAG,KAAK,WAAW,CAAC,CAC9B;EAED,MAAM,iCAAiC,IAAI,oBAAoB;EAC/D,MAAM,eAAe,IAAI,mBAEvB,UACA,gBACA,gBACA,YACA,+BACD;AAED,MAAI;AACF,SAAM,aAAa,MAAM;WAClB,OAAO;AACd,WAAQ,MAAM,oCAAoC,MAAM;;AAG1D,qBAAmB,KAAK,aAAa;EAErC,MAAM,oCAAoC,IAAI,oBAAoB;EAClE,MAAM,kBAAkB,IAAI,sBAC1B,UACA,gBACA,YACA,kCACD;AAED,MAAI;AACF,SAAM,gBAAgB,MAAM;WACrB,OAAO;AACd,WAAQ,MAAM,uCAAuC,MAAM;;AAG7D,qBAAmB,KAAK,gBAAgB;EAExC,MAAM,sBAAsB,IAAI,2BAC9B,IAAI,iCAAiC,CACtC;EAED,MAAM,oCACJ,IAAI,kCAAkC,qBAAqB,aAAa;EAE1E,MAAM,qCAAqC,IAAI,oBAAoB;EACnE,MAAM,mBAAmB,IAAI,iBAE3B,UACA,gBACA,YACA,oCACA,KAAK,QACL,KAAK,oBACN;AAED,MAAI;AACF,SAAM,iBAAiB,MAAM;WACtB,OAAO;AACd,WAAQ,MAAM,wCAAwC,MAAM;;AAG9D,OAAK,MAAM,WAAW,KAAK,oBAAoB;GAC7C,MAAM,YAAY,MAAM,QAAQ;IAC9B;IACA;IACA;IACD,CAAC;AACF,sBAAmB,KAAK,UAAU;;EAGpC,MAAM,uBAAuB,KAAK,uBAC9B,KAAK,uBACL,KAAK,wBACH,MAAM,KAAK,6BACT,KAAK,uBACL,SACD,GACD,IAAI,qBAAqB,UAAU,oBAAoB,CACrD,mCACA,iBACD,CAAC;EAER,MAAM,UAAU,IAAI,QAClB,KAAK,QACL,uBACA,OACA,YACA,sBACA,KAAK,UACL,cACA,iBACA,gBACA,UACA,gBACD;EAED,IAAI,aAAqC,KAAA;AACzC,MAAI,KAAK,eAAe;GACtB,MAAM,UACJ,KAAK,kBAAkB,cAAc,UACjC,IAAI,yBAAyB,KAAK,QAAQ,KAAK,YAAY,MAAM,GACjE,IAAI,0BAA0B,KAAK,OAAO;AAGhD,gBADoB,IAAI,aAAa,CAAC,mBAAmB,QAAQ,CACxC,YACvB,SACA,KAAK,QACL,gBACA,UACA,UACA,KAAK,oBACN;AACD,SAAM,WAAW,YAAY,SAAS;aAC7B,KAAK,aAAa;AAC3B,gBAAa,KAAK,YAAY,YAC5B,SACA,KAAK,QACL,gBACA,UACA,UACA,KAAK,oBACN;AACD,SAAM,WAAW,YAAY,SAAS;;EAGxC,MAAM,SAAwB;GAC5B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,OAAO,KAAK;GACb;AAED,MAAI,KAAK,sBACP,MAAK,qBAAqB,OAAO;AAGnC,SAAO;;;;;;;;;CAUT,MAAc,6BACZ,QACA,UACgC;AAChC,MAAI,CAAC,KAAK,eACR,OAAM,IAAI,MACR,qHACD;EAEH,MAAM,SAAS,KAAK,yBAAyB,EAAE;EAC/C,MAAM,KAAe;GACnB,GAAG,KAAK;GACR,UAAU,OAAO,YAAY,KAAK,eAAe;GACjD,iBAAiB;GAClB;EACD,MAAM,UACJ,KAAK,2BACJ,MAAM,KAAK,sCAAsC;EACpD,MAAM,uBAAwD,EAAE;AAChE,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,YAAY,KAAK;GAC1C,MAAM,YAAY,oCAChB,oBAAoB,IACrB;AACD,wBAAqB,KAAK,UAAU;AACpC,QAAK,kBAAkB,KAAK,UAAU;;EAExC,MAAM,EAAE,2BACN,MAAM,OAAO;EACf,MAAM,UAAU,IAAI,uBAAuB;GACzC,YAAY,OAAO;GACnB;GACA;GACA,eAAe,OAAO;GACtB,gBAAgB,OAAO;GACvB;GACA,QAAQ,KAAK;GACb,SAAS;GACT,eAAe,OAAO;GACtB,iBAAiB,OAAO;GACxB,gBAAgB,OAAO;GACvB,4BAA4B,OAAO;GACnC;GACD,CAAC;AACF,QAAM,QAAQ,SAAS;AACvB,OAAK,cAAc,WAAW,QAAQ,UAAU,CAAC;AACjD,SAAO;;CAGT,MAAc,uCAAyE;EACrF,MAAM,CAAC,EAAE,mCAAmC,EAAE,+BAC5C,MAAM,QAAQ,IAAI,CAChB,OAAO,4BACP,OAAO,mCACR,CAAC;AACJ,eAAa,gCAAgC,0BAA0B;;;;;;;CAQzE,MAAc,2BACZ,YACwB;EACxB,MAAM,CAAC,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,qBACpD,MAAM,QAAQ,IAAI;GAChB,OAAO;GACP,OAAO;GACP,OAAO,wBAAA,MAAA,MAAA,EAAA,EAAA;GACR,CAAC;EACJ,MAAM,KAAK,KAAK;EAChB,MAAM,oBAAoB,KAAK;EAC/B,MAAM,SAAS,KAAK,yBAAyB,EAAE;EAC/C,MAAM,SAAS,KAAK;AACpB,UAAQ,UAAkB;GACxB,MAAM,WAAW,kBAAkB;GACnC,MAAM,sBAAsB,oCAAoC,SAAS;AACzE,QAAK,kBAAkB,KAAK,oBAAoB;AAChD,UAAO,IAAI,aAAa;IACtB;IACA;IACA,WAAW,sBAAsB,gBAAgB;IACjD,aAAa;KACX;KACA;KACA;KACA;KACD;IACD;IACA;IACD,CAAC;;;;;;;;;;;;CAaN,MAAc,uBACZ,QAC2B;EAC3B,MAAM,EAAE,QAAQ,oBAAoB,MAAM,OAAO;EAEjD,MAAM,QADW,MAAM,OAAO,OACR,QAAQ;EAC9B,MAAM,OAAO,IAAI,KAAK;GACpB,MAAM,OAAO;GACb,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,KAAK,OAAO,MAAM,EAAE,oBAAoB,OAAO,GAAG,KAAA;GAClD,kBAAkB,OAAO;GACzB,KAAK,OAAO;GACZ,yBAAyB,OAAO;GAChC,mBAAmB,OAAO;GAC3B,CAAC;AACF,OAAK,kBAAkB,KACrB,iBAAiB,MAAM,OAAO,mBAAmB,eAAe,CACjE;AACD,SAAO,IAAI,OAAiB,EAC1B,SAAS,IAAI,gBAAgB,EAAE,MAAM,CAAC,EACvC,CAAC;;CAGJ,qBAA6B,QAA6B;AACxD,MACE,OAAO,eAAe,eACtB,EAAE,aAAa,eACf,OAAO,WAAW,QAAQ,OAAO,WAEjC;EAGF,MAAM,cAAc,WAAW;EAC/B,MAAM,WAAW,YAAY,KAAK,KAAK,YAAY;EACnD,IAAI,qBAAqB;EACzB,IAAI;EAQJ,MAAM,UAAU,OAAO,WAAmB;AACxC,OAAI,oBAAoB;AACtB,SAAK,OAAQ,KACX,YAAY,OAAO,yCACpB;AACD;;AAGF,wBAAqB;AACrB,eAAY,SAAS,SAAkB;AACrC,wBAAoB,QAAQ;;AAI9B,QAAK,OAAQ,KAAK,YAAY,OAAO,iCAAiC;GAEtE,MAAM,SAAS,OAAO,QAAQ,MAAM;AAEpC,OAAI;AACF,UAAM,OAAO;YACN,OAAO;AACd,SAAK,OAAQ,MAAM,wCAAwC,MAAM;AACjE,gBAAY,OAAO;AACnB,aAAS,EAAE;AACX;;AAGF,QAAK,MAAM,QAAQ,KAAK,cACtB,KAAI;AACF,UAAM,MAAM;YACL,OAAO;AACd,SAAK,OAAQ,MAAM,yBAAyB,MAAM;;AAItD,OAAI;AACF,UAAM,OAAO,SAAS,SAAS;YACxB,OAAO;AACd,SAAK,OAAQ,MAAM,wCAAwC,MAAM;AACjE,gBAAY,OAAO;AACnB,aAAS,EAAE;AACX;;AAGF,QAAK,OAAQ,KAAK,oBAAoB;AACtC,eAAY,OAAO;AACnB,YAAS,mBAAmB,EAAE;;AAGhC,cAAY,gBAAgB,gBAAgB,KAAK,QAAQ,SAAS,CAAC;AACnE,cAAY,gBAAgB,iBAAiB,KAAK,QAAQ,UAAU,CAAC;;;;;;;;;AC77BzE,IAAa,oBAAb,MAAkD;CAChD,YAAY,EAAE;CAEd,OAA4B;AAC1B,SAAO,QAAQ,QAAQ,IAAI,WAAW,EAAE,CAAC;;CAG3C,SAAwB;AACtB,SAAO,QAAQ,SAAS;;CAG1B,aAAiC;AAC/B,SAAO,QAAQ,QAAQ;GAAC;GAAI;GAAI;GAAI;GAAI;GAAG,CAAC;;;;;;;;ACIhD,IAAa,uBAAb,MAAkC;CAChC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;;;;;CAOA,WAAkB,QAAuB;AACvC,OAAK,SAAS;AACd,SAAO;;;;;CAMT,mBAA0B,gBAAsC;AAC9D,MAAI,KAAK,QACP,OAAM,IAAI,MAAM,yBAAyB;AAG3C,OAAK,iBAAiB;AACtB,SAAO;;;;;CAMT,YACE,SACA,UACA,iBACA,cACM;AACN,MAAI,KAAK,eACP,OAAM,IAAI,MAAM,gCAAgC;AAGlD,OAAK,UAAU;AACf,OAAK,WAAW;AAChB,OAAK,kBAAkB;AACvB,OAAK,eAAe;AACpB,SAAO;;;;;;;CAQT,WAAkB,QAAsC;AACtD,MAAI,YAAY,QAAQ;AACtB,QAAK,SAAS,OAAO;AACrB,QAAK,oBAAoB,OAAO;QAEhC,MAAK,SAAS;AAEhB,SAAO;;CAGT,wBACE,qBACM;AACN,OAAK,sBAAsB;AAC3B,SAAO;;CAGT,eAAsB,YAA+B;AACnD,OAAK,aAAa;AAClB,SAAO;;CAGT,wBAA+B,QAAoC;AACjE,OAAK,sBAAsB;AAC3B,SAAO;;CAGT,MAAa,QAAgC;AAE3C,UADe,MAAM,KAAK,aAAa,EACzB;;CAGhB,MAAa,cAA4C;AACvD,MAAI,CAAC,KAAK,OACR,MAAK,SAAS,IAAI,cAAc,CAAC,iBAAiB,CAAC;EAGrD,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;AAEJ,MAAI,KAAK,gBAAgB;AACvB,OAAI,KAAK,kBACP,MAAK,eAAe,sBAAsB,KAAK,kBAAkB;AAEnE,OAAI,KAAK,oBACP,MAAK,eAAe,wBAAwB,KAAK,oBAAoB;AAEvE,mBAAgB,MAAM,KAAK,eAAe,aAAa;AACvD,aAAU,cAAc;AACxB,cAAW,cAAc;AACzB,qBAAkB,cAAc;AAChC,kBAAe,cAAc;aAE7B,KAAK,WACL,KAAK,YACL,KAAK,mBACL,KAAK,cACL;AACA,aAAU,KAAK;AACf,cAAW,KAAK;AAChB,qBAAkB,KAAK;AACvB,kBAAe,KAAK;AACpB,mBAAgB,KAAA;QAEhB,OAAM,IAAI,MACR,6FACD;EAGH,MAAM,SAAS,KAAK,UAAU,IAAI,mBAAmB;EAErD,MAAM,sBACJ,KAAK,uBACL,eAAe,uBACf,IAAI,2BAA2B,IAAI,iCAAiC,CAAC;EAEvE,MAAM,aACJ,KAAK,cACL,IAAI,WAAW,WAAW,OAAO,WAC/B,QAAQ,aAAa,OAAO,OAAO,CACpC;AAYH,SAAO;GACL,QAXa,IAAI,cACjB,KAAK,QACL,SACA,QACA,qBACA,YACA,iBACA,aACD;GAIC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;;;;;;;;;;;;AC9KL,SAAgB,cAAc,KAA6B;CACzD,MAAM,YAAY,IAAI,IAAI,IAAI;CAC9B,MAAM,UAAU,IAAI,MAAM,IAAI,CAAC,KAAK,IAAI;CACxC,MAAM,WAAW,UAAU,SAAS,QAAQ,kBAAkB,GAAG;AAEjE,QAAO;EAAE;EAAK;EAAS,iBADC,GAAG,UAAU,SAAS,IAAI,UAAU,OAAO,SAAS;EACpC;;;;;AAM1C,SAAgB,eAAe,KAAqB;AAClD,QAAO,IAAI,MAAM,IAAI,CAAC,KAAK,IAAI;;;;ACvBjC,MAAa,2BAA2C;CACtD,mBAAmB,QAAQ,SAAS;CACpC,2BAA2B,QAAQ,QAAQ,KAAA,EAAU;CACrD,qBAAqB,QAAQ,QAAQ,EAAE,CAAC;CACxC,uBAAuB,QAAQ,QAAQ,EAAE;CAC1C;;;ACaD,IAAa,2BAAb,MAA2E;CACzE;CACA;CACA;CACA;CACA;CAEA,YACE,eACA,gBACA,YACA,cACA,uBACA;AACA,OAAK,gBAAgB;AACrB,OAAK,iBAAiB;AACtB,OAAK,aAAa;AAClB,OAAK,eAAe;AACpB,OAAK,wBAAwB;;CAG/B,MAAM,iBACJ,YACA,SAAS,QACT,QAC2B;EAC3B,MAAM,iBAA4C,EAAE;EACpD,MAAM,iBAA4C,EAAE;EAEpD,MAAM,cAAc,IAAI,iBACtB,0BACA,KAAK,gBACL,KAAK,uBACL;GACE,cAAc;GACd,gBAAgB;GAChB,kBAAkB,OAAO;GAC1B,CACF;EAED,MAAM,YAAY,MAAM,KAAK,cAAc,cACzC,YACA,KAAA,GACA,QACA,OACD;AAED,OAAK,MAAM,YAAY,WAAW;AAChC,kBAAe,OAAO;AAEtB,eAAY,WAAW,YAAY,SAAS,OAAO,OAAO;GAC1D,MAAM,cAAc,MAAM,YAAY,SACpC,YACA,SAAS,OACT,QACA,SAAS,UACT,OACD;GAED,MAAM,SAAS,0BACb,SAAS,UACT,SAAS,MACV;GACD,MAAM,aAAa,0BAA0B,aAAa,SAAS,MAAM;AAEzE,OAAI,WAAW,WACb,gBAAe,KAAK;IAClB,OAAO,SAAS;IAChB;IACA,UAAU,SAAS;IACnB,cAAc;IACd,cAAc;IACf,CAAC;;EAIN,IAAI;AACJ,MAAI;AACF,gBAAa,MAAM,KAAK,aAAa,IAAI,WAAW;UAC9C;AACN,UAAO;IACL;IACA,cAAc,eAAe,WAAW;IACxC;IACA;IACD;;EAGH,MAAM,YAAY,MAAM,KAAK,eAAe,aAC1C,YACA,QACA,OACD;EACD,MAAM,YAAY,OAAO,KAAK,UAAU,SAAS;AAEjD,OAAK,MAAM,SAAS,WAAW;AAC7B,OAAI,UAAU,WAAY;AAE1B,eAAY,WAAW,YAAY,OAAO,OAAO;GAEjD,IAAI;AACJ,OAAI;AACF,kBAAc,MAAM,YAAY,SAC9B,YACA,OACA,QACA,KAAA,GACA,OACD;WACK;AACN,mBAAe,OAAO;AACtB;;GAGF,MAAM,eAAe,0BAA0B,YAAY,MAAM;GACjE,MAAM,aAAa,0BAA0B,aAAa,MAAM;AAChE,OAAI,iBAAiB,WACnB,gBAAe,KAAK;IAClB;IACA;IACA;IACA,cAAc;IACf,CAAC;;AAIN,SAAO;GACL;GACA,cAAc,eAAe,WAAW,KAAK,eAAe,WAAW;GACvE;GACA;GACD;;CAGH,MAAM,iBACJ,YACA,SAAS,QACT,QACwB;AAQxB,SAAO;GACL;GACA,kBATc,MAAM,KAAK,cAAc,gBACvC,YACA,KAAA,GACA,QACA,OACD;GAKC,mBAAmB;GACpB;;CAGH,MAAM,iBACJ,YACA,SAAS,QACT,QACwB;EACxB,MAAM,SAAS,MAAM,KAAK,eAAe,YAAY,QAAQ,OAAO;AAEpE,OAAK,MAAM,SAAS,QAAQ;AAC1B,kBAAe,OAAO;AACtB,QAAK,WAAW,WAAW,YAAY,OAAO,OAAO;;AAGvD,SAAO;GACL;GACA,kBAAkB;GAClB,mBAAmB,OAAO;GAC3B;;CAGH,MAAc,eACZ,YACA,QACA,QACmB;EACnB,MAAM,YAAY,MAAM,KAAK,eAAe,aAC1C,YACA,QACA,OACD;AACD,SAAO,OAAO,KAAK,UAAU,SAAS"}
1
+ {"version":3,"file":"index.js","names":["toErrorInfo","uuidv4","addFileAction","addFolderAction","deleteNodeAction","updateNodeAction","moveNodeAction","copyNodeAction","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/client/reactor-client.ts","../src/queue/types.ts","../src/executor/job-result-handler.ts","../src/executor/types.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/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/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/signer/passthrough-signer.ts","../src/core/reactor-client-builder.ts","../src/shared/drive-url.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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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","/**\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 message and stack trace.\n */\nexport type ErrorInfo = {\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 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\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 replayDocument,\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 { IReactor } from \"../core/types.js\";\nimport { getSharedActionScope, signActions } from \"../core/utils.js\";\nimport type { 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 toVersion: 1,\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 const batchResult = await this.reactor.executeBatch(\n {\n jobs: [\n {\n key: \"document\",\n documentId,\n scope: getSharedActionScope(documentActions),\n branch: \"main\",\n actions: documentActions,\n dependsOn: [],\n },\n {\n key: \"drive\",\n documentId: driveIdentifier,\n scope: getSharedActionScope(driveActions),\n branch: \"main\",\n actions: driveActions,\n dependsOn: [\"document\"],\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\n for (const job of completedJobs) {\n if (job.status === JobStatus.FAILED) {\n throw new Error(job.error?.message);\n }\n }\n\n return this.reactor.get<TDocument>(documentId);\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 const node = updated.state.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 = drive.state.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 const module = await this.client.getDocumentModelModule(\n srcDoc.header.documentType,\n );\n const duplicated = replayDocument(\n srcDoc.initialState,\n srcDoc.operations,\n module.reducer,\n createPresignedHeader(entry.targetId, srcDoc.header.documentType),\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 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 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 {\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/**\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 * 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 * 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 * 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 * 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 * @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 updated document\n */\n execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\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 Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} from \"@powerhousedao/shared/document-model\";\nimport { actions } 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 {\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 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 DocumentChangeType,\n type CreateDocumentOptions,\n type DocumentChangeEvent,\n type IDriveClient,\n type IReactorClient,\n} from \"./types.js\";\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\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 ) {\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.drives = new DriveClient(this, logger, reactor, signer);\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 const module = modules.results.find(\n (m) => m.documentModel.global.id === documentType,\n );\n\n if (!module) {\n throw new Error(\n `Document model module not found for type: ${documentType}`,\n );\n }\n\n return module as DocumentModelModule<any>;\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 return await this.reactor.getByIdOrSlug<TDocument>(\n identifier,\n view,\n undefined,\n signal,\n );\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 if (paging?.cursor && isCompositeCursor(paging.cursor)) {\n return this.getOperationsWithCompositeCursor(\n documentId,\n view,\n filter,\n paging,\n signal,\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 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 ): 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 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.reactor.find(\n { ids: targetIds },\n view,\n paging,\n undefined,\n signal,\n );\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.reactor.find(\n { ids: sourceIds },\n view,\n paging,\n undefined,\n signal,\n );\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 return this.reactor.find(search, view, paging, undefined, signal);\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: document.state.document.version,\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 return await this.reactor.get<TDocument>(documentId);\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 module = matchingModules.find(\n (m) => m.version === options.documentModelVersion,\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 = current.version ?? 0;\n const latestVersion = latest.version ?? 0;\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 = module.version ?? 1;\n\n return this.create<TDocument>(document, options?.parentIdentifier, signal);\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 async execute<TDocument extends PHDocument>(\n documentIdentifier: string,\n branch: string,\n actions: Action[],\n signal?: AbortSignal,\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 };\n const result = await this.reactor.getByIdOrSlug<TDocument>(\n documentIdentifier,\n view,\n completedJob.consistencyToken,\n signal,\n );\n return result;\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 result;\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 result;\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: sourceResult,\n target: targetResult,\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 const unsubscribeCreated = this.subscriptionManager.onDocumentCreated(\n (result) => {\n void (async () => {\n try {\n const documents = await Promise.all(\n result.results.map((id) =>\n this.reactor.get(id, view, undefined, undefined),\n ),\n );\n\n callback({\n type: DocumentChangeType.Created,\n documents,\n });\n } catch {\n // Silently ignore errors when fetching created documents\n }\n })();\n },\n search,\n );\n\n const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted(\n (documentIds) => {\n callback({\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 callback({\n type: DocumentChangeType.Updated,\n documents: result.results,\n });\n },\n search,\n view,\n );\n\n const unsubscribeRelationship =\n this.subscriptionManager.onRelationshipChanged(\n (parentId, childId, changeType) => {\n callback({\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 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\nexport type JobKind = \"mutation\" | \"load\";\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 * 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 type { IJobExecutionHandle, Job } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { ModuleNotFoundError } from \"../registry/errors.js\";\nimport {\n DocumentDeletedError,\n DocumentNotFoundError,\n} from \"../shared/errors.js\";\nimport type { ErrorInfo } from \"../shared/types.js\";\nimport type { JobResult } from \"./types.js\";\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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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 // DocumentNotFoundError: defer the job instead of failing immediately.\n // A CREATE_DOCUMENT job may arrive later and unblock it.\n if (result.error && DocumentNotFoundError.isError(result.error)) {\n handle.defer();\n callbacks.deferJob(handle.job.documentId, handle.job);\n return;\n }\n\n if (result.error && DocumentDeletedError.isError(result.error)) {\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 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 message: messageLines.join(\"\\n\"),\n stack: stackLines.join(\"\\n\\n\"),\n };\n }\n}\n","import type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { Operation } from \"@powerhousedao/shared/document-model\";\nimport type { Job } from \"../queue/types.js\";\n\n/**\n * Represents the result of a job execution\n */\nexport type JobResult = {\n /** The job that was executed */\n job: Job;\n\n /** Whether the job executed successfully */\n success: boolean;\n\n /** Error if the job failed */\n error?: Error;\n\n /** The operations generated from the actions (if successful) */\n operations?: Operation[];\n\n /**\n * Operations with context (includes ephemeral resultingState).\n * Used for emitting to IDocumentView via event bus.\n */\n operationsWithContext?: OperationWithContext[];\n\n /** Timestamp when the job execution completed */\n completedAt?: string;\n\n /** Duration of job execution in milliseconds */\n duration?: number;\n\n /** Any additional metadata from the execution */\n metadata?: Record<string, any>;\n};\n\n/**\n * Configuration options for the job executor\n */\nexport type JobExecutorConfig = {\n /** Maximum number of conflicting operations to skip when reshuffling. */\n maxSkipThreshold?: number;\n\n /** Maximum number of concurrent jobs to execute */\n maxConcurrency?: number;\n\n /** Maximum time in milliseconds a job can run before being considered timed out */\n jobTimeoutMs?: number;\n\n /** Base delay in milliseconds for exponential backoff retries */\n retryBaseDelayMs?: number;\n\n /** Maximum delay in milliseconds for exponential backoff retries */\n retryMaxDelayMs?: number;\n\n /** Maximum elapsed milliseconds before yielding to the main thread between actions.\n * Keeps the UI responsive when processing large batches. */\n yieldDeadlineMs?: number;\n};\n\n/**\n * Event types for the job executor\n */\nexport const JobExecutorEventTypes = {\n JOB_STARTED: 20000,\n JOB_COMPLETED: 20001,\n JOB_FAILED: 20002,\n EXECUTOR_STARTED: 20003,\n EXECUTOR_STOPPED: 20004,\n} as const;\n\n/**\n * Event data for job execution events\n */\nexport type JobStartedEvent = {\n job: Job;\n startedAt: string;\n /**\n * Identifier of the executor that took the job. For the worker pool this is\n * the thread-worker id (e.g. \"reactor-worker-3\"); for the in-process simple\n * manager it is \"in-process-<index>\". Optional for backwards compatibility\n * with consumers built before the field was added.\n */\n workerId?: string;\n};\n\nexport type JobCompletedEvent = {\n job: Job;\n result: JobResult;\n /** See {@link JobStartedEvent.workerId}. */\n workerId?: string;\n};\n\nexport type JobFailedEvent = {\n job: Job;\n error: string;\n willRetry: boolean;\n retryCount: number;\n /** See {@link JobStartedEvent.workerId}. */\n workerId?: string;\n};\n\nexport type ExecutorStartedEvent = {\n config: JobExecutorConfig;\n startedAt: string;\n};\n\nexport type ExecutorStoppedEvent = {\n stoppedAt: string;\n graceful: boolean;\n};\n\n/**\n * Status information for the job executor manager\n */\nexport type ExecutorManagerStatus = {\n /** Whether the manager is currently running */\n isRunning: boolean;\n\n /** Number of executor instances managed */\n numExecutors: number;\n\n /** Number of jobs currently being processed */\n activeJobs: number;\n\n /** Total number of jobs processed since start */\n totalJobsProcessed: number;\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 { OperationWithContext } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { ICollectionMembershipCache } from \"../cache/collection-membership-cache.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 { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type {\n IExecutorWorker,\n IJobExecutor,\n IJobExecutorManager,\n WorkerExecutionOutcome,\n} from \"./interfaces.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\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 * Action types whose application invalidates the parent's collection\n * membership cache. Mirrors the in-process invalidation pattern in\n * `document-action-handler.ts` (the worker pool relocates that work to\n * the parent because the cache lives there).\n */\nconst MEMBERSHIP_INVALIDATING_ACTIONS = new Set([\n \"ADD_RELATIONSHIP\",\n \"REMOVE_RELATIONSHIP\",\n \"UPDATE_RELATIONSHIP\",\n \"DELETE_DOCUMENT\",\n]);\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 * - Owning the authoritative `ICollectionMembershipCache` — workers do\n * not query it. Each result enriches the outgoing `JOB_WRITE_READY`\n * with `collectionMemberships` and invalidates targets named by\n * relationship/delete operations before the lookup.\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 = new Map<string, Job[]>();\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 collectionMembershipCache: ICollectionMembershipCache,\n jobTimeoutMs: number = 30_000,\n ) {\n this.jobTimeoutMs = jobTimeoutMs;\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 for (const [, jobs] of this.deferredJobs) {\n for (const job of jobs) {\n const errorInfo = toErrorInfo(\n new DocumentNotFoundError(job.documentId),\n );\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error: new DocumentNotFoundError(job.documentId),\n job,\n })\n .catch(() => {});\n }\n }\n this.deferredJobs.clear();\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) => {\n const existing = this.deferredJobs.get(documentId) ?? [];\n existing.push(job);\n this.deferredJobs.set(documentId, existing);\n },\n flushDeferredFor: (documentId) => this.flushDeferredJobs(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 this.invalidateMembershipsFor(payload.operations);\n\n const documentIds = [\n ...new Set(payload.operations.map((op) => op.context.documentId)),\n ];\n let collectionMemberships: Record<string, string[]> = {};\n try {\n collectionMemberships =\n await this.collectionMembershipCache.getCollectionsForDocuments(\n documentIds,\n );\n } catch (error) {\n this.logger.error(\n \"Failed to load collection memberships for JOB_WRITE_READY: @Error\",\n error,\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 private invalidateMembershipsFor(operations: OperationWithContext[]): void {\n for (const op of operations) {\n const actionType = op.operation.action.type;\n if (!MEMBERSHIP_INVALIDATING_ACTIONS.has(actionType)) {\n continue;\n }\n const target = extractMembershipTarget(op);\n if (target) {\n this.collectionMembershipCache.invalidate(target);\n }\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 private async flushDeferredJobs(documentId: string): Promise<void> {\n const jobs = this.deferredJobs.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.deferredJobs.delete(documentId);\n\n for (const job of jobs) {\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\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\nfunction extractMembershipTarget(op: OperationWithContext): string | undefined {\n const actionType = op.operation.action.type;\n const input = op.operation.action.input as\n | { targetId?: string; documentId?: string }\n | undefined;\n if (\n actionType === \"ADD_RELATIONSHIP\" ||\n actionType === \"REMOVE_RELATIONSHIP\" ||\n actionType === \"UPDATE_RELATIONSHIP\"\n ) {\n return input?.targetId;\n }\n if (actionType === \"DELETE_DOCUMENT\") {\n return input?.documentId ?? op.context.documentId;\n }\n return undefined;\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, Job } from \"../queue/types.js\";\nimport { QueueEventTypes } from \"../queue/types.js\";\nimport type { IDocumentModelResolver } from \"../registry/document-model-resolver.js\";\nimport { DocumentNotFoundError } from \"../shared/errors.js\";\nimport type { IJobExecutor, IJobExecutorManager } from \"./interfaces.js\";\nimport {\n JobResultHandler,\n toErrorInfo,\n type IJobResultHandler,\n} from \"./job-result-handler.js\";\nimport {\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 = new Map<string, Job[]>();\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 ) {\n this.jobTimeoutMs = jobTimeoutMs;\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 for (const [, jobs] of this.deferredJobs) {\n for (const job of jobs) {\n const errorInfo = toErrorInfo(\n new DocumentNotFoundError(job.documentId),\n );\n this.jobTracker.markFailed(job.id, errorInfo, job);\n this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId: job.id,\n error: new DocumentNotFoundError(job.documentId),\n job,\n })\n .catch(() => {});\n }\n }\n this.deferredJobs.clear();\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) => {\n const existing = this.deferredJobs.get(documentId) ?? [];\n existing.push(job);\n this.deferredJobs.set(documentId, existing);\n },\n flushDeferredFor: (documentId) => this.flushDeferredJobs(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 private async flushDeferredJobs(documentId: string): Promise<void> {\n const jobs = this.deferredJobs.get(documentId);\n if (!jobs || jobs.length === 0) {\n return;\n }\n this.deferredJobs.delete(documentId);\n\n for (const job of jobs) {\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","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 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 status: JobStatus.RUNNING,\n createdAtUtcIso: new Date().toISOString(),\n consistencyToken: createEmptyConsistencyToken(),\n meta: { batchId: jobId, batchJobIds: [jobId] },\n });\n return;\n }\n\n // Update existing job\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 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 { ProcessorFilter } 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","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} from \"./utils.js\";\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\n constructor(\n db: Kysely<DocumentViewDatabase>,\n operationIndex: IOperationIndex,\n writeCache: IWriteCache,\n consistencyTracker: IConsistencyTracker,\n logger: ILogger,\n driveContainerTypes: ReadonlySet<string>,\n ) {\n super(db, operationIndex, writeCache, consistencyTracker, {\n readModelId: \"processor-manager\",\n rebuildStateOnInit: true,\n });\n this.logger = logger;\n this.driveContainerTypes = driveContainerTypes;\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\n for (let i = 0; i < records.length; i++) {\n const record = records[i]!;\n const processorId = `${identifier}:${driveId}:${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 // Clean up orphaned cursor rows from previous runs with more processors\n await this.db\n .deleteFrom(\"ProcessorCursor\")\n .where(\"factoryId\", \"=\", identifier)\n .where(\"driveId\", \"=\", driveId)\n .where(\"processorIndex\", \">=\", records.length)\n .execute();\n\n for (const [id, row] of this.cursorCache) {\n if (\n row.factoryId === identifier &&\n row.driveId === driveId &&\n row.processorIndex >= records.length\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 maxOrdinal = Math.max(\n ...page.results.map((op) => op.context.ordinal),\n );\n tracked.lastOrdinal = maxOrdinal;\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 const maxOrdinal = Math.max(...operations.map((op) => op.context.ordinal));\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 } 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 message: error.message,\n stack: error.stack || new Error().stack || \"\",\n };\n }\n return {\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 // Throw error if queue is blocked\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 // Add job to the end of the queue (FIFO)\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 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 // Mark this job as executing for its document\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 // Mark this job as executing for its document\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 // Clear all queues\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 // update the job lastError and errorHistory\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 // Track as completed so dependent jobs are unblocked\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 this.eventBus\n .emit(ReactorEventTypes.JOB_FAILED, {\n jobId,\n error: new Error(error?.message ?? \"Job failed\"),\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(jobId: string, error?: ErrorInfo): 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 // update the job lastError\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 // Update retry count\n const updatedJob: Job = {\n ...job,\n retryCount: (job.retryCount || 0) + 1,\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","import 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\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 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 const module = await this.loader.load(documentType);\n const [result] = this.registry.registerModules(module);\n if (\n result.status === \"error\" &&\n !DuplicateModuleError.isError(result.error)\n ) {\n throw result.error as Error;\n }\n await this.broadcastIfPossible(documentType);\n } catch (error) {\n this.failedModelTypes.add(documentType);\n throw error;\n } finally {\n this.loadingModels.delete(documentType);\n }\n })();\n\n this.loadingModels.set(documentType, loadPromise);\n return loadPromise;\n }\n\n private async broadcastIfPossible(documentType: string): Promise<void> {\n if (!this.broadcastHook || !this.loader.resolveSpec) {\n return;\n }\n const entry = await this.loader.resolveSpec(documentType);\n if (!entry) {\n return;\n }\n await this.broadcastHook(entry);\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 type { OperationWithContext } from \"@powerhousedao/shared/document-model\";\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\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\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};\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: string;\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};\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 // update latest ordinal\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 // update latest ordinal\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 } 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 constructor(\n message: string,\n category: GraphQLRequestErrorCategory,\n statusCode?: number,\n ) {\n super(message);\n this.name = \"GraphQLRequestError\";\n this.category = category;\n this.statusCode = statusCode;\n }\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 constructor(source: ChannelErrorSource, error: Error) {\n super(`ChannelError[${source}]: ${error.message}`);\n this.name = \"ChannelError\";\n this.source = source;\n this.error = error;\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 { 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} 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 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 */\nexport function consolidateSyncOperations(\n syncOps: SyncOperation[],\n): SyncOperation[] {\n if (syncOps.length <= 1) {\n return syncOps;\n }\n\n type GroupKey = string;\n const groups = new Map<\n GroupKey,\n { ops: SyncOperation[]; canonicalJobId: string }\n >();\n const jobIdRemap = new Map<string, string>();\n const insertionOrder: GroupKey[] = [];\n\n for (const syncOp of syncOps) {\n const key: GroupKey = `${syncOp.documentId}|${syncOp.scopes.slice().sort().join(\",\")}|${syncOp.branch}`;\n\n const existing = groups.get(key);\n if (existing) {\n existing.ops.push(syncOp);\n if (syncOp.jobId && syncOp.jobId !== existing.canonicalJobId) {\n jobIdRemap.set(syncOp.jobId, existing.canonicalJobId);\n }\n } else {\n groups.set(key, { ops: [syncOp], canonicalJobId: syncOp.jobId });\n insertionOrder.push(key);\n }\n }\n\n const result: SyncOperation[] = [];\n\n for (const key of insertionOrder) {\n const group = groups.get(key)!;\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","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 {\n Action,\n Operation,\n Signature,\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, converting signature tuples to strings.\n */\nexport function serializeAction(action: Action): unknown {\n const signer = action.context?.signer;\n if (!signer?.signatures) {\n return action;\n }\n\n return {\n ...action,\n context: {\n ...action.context,\n signer: {\n ...signer,\n signatures: signer.signatures.map((sig: Signature | string) =>\n Array.isArray(sig) ? sig.join(\", \") : sig,\n ),\n },\n },\n };\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 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 * Deserializes a signature from a comma-separated string back to a tuple.\n *\n * GraphQL serializes Signature tuples as comma-separated strings for transport.\n * This function converts them back to the expected [string, string, string, string, string] format.\n */\nfunction deserializeSignature(sig: Signature | string): Signature {\n if (Array.isArray(sig)) {\n return sig;\n }\n return sig.split(\", \") as Signature;\n}\n\n/**\n * Deserializes signatures in an operation's signer context from strings back to tuples.\n *\n * When operations are transported via GraphQL, signatures are serialized as comma-separated\n * strings. This function restores them to the Signature tuple format required for verification.\n */\nfunction deserializeOperationSignatures(\n opWithContext: OperationWithContext,\n): OperationWithContext {\n const signer = opWithContext.operation.action.context?.signer;\n if (!signer?.signatures || signer.signatures.length === 0) {\n return opWithContext;\n }\n\n const deserializedSignatures = signer.signatures.map(deserializeSignature);\n\n const deserializedOperation: Operation = {\n ...opWithContext.operation,\n action: {\n ...opWithContext.operation.action,\n context: {\n ...opWithContext.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(\n deserializeOperationSignatures,\n );\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(\n deserializeOperationSignatures,\n );\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 { IOperationIndex } from \"../../cache/operation-index-types.js\";\nimport type { ISyncCursorStorage } from \"../../storage/interfaces.js\";\nimport { BufferedMailbox } from \"../buffered-mailbox.js\";\nimport { ChannelError, GraphQLRequestError } 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} 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 ID to synchronize */\n collectionId: string;\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 * 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 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 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 };\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 /**\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 (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 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 syncOp.failed(\n new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error)),\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.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 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) return;\n\n void this.touchRemoteChannel()\n .then(({ ackOrdinal }) => {\n this.logger.info(\n \"GqlChannel @ChannelId re-registered successfully\",\n this.channelId,\n );\n this.failureCount = 0;\n if (ackOrdinal > 0) {\n trimMailboxFromAckOrdinal(this.outbox, ackOrdinal);\n }\n this.pollTimer.start();\n this.transitionConnectionState(\"connected\");\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.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 * 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 jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n }> {\n const query = `\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 id\n action {\n id\n type\n timestampUtcMs\n input\n scope\n attachments {\n data\n mimeType\n hash\n extension\n fileName\n }\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 jobId\n branch\n scopes\n operationCount\n }\n hasMore\n }\n }\n `;\n\n const variables = {\n channelId: this.channelId,\n outboxAck: ackOrdinal,\n outboxLatest: latestOrdinal,\n };\n\n const response = await this.executeGraphQL<{\n pollSyncEnvelopes: {\n envelopes: SyncEnvelope[];\n ackOrdinal: number;\n deadLetters?: Array<{\n documentId: string;\n error: string;\n jobId: string;\n branch: string;\n scopes: string[];\n operationCount: number;\n }>;\n hasMore: boolean;\n };\n }>(query, variables);\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 * 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,\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,\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 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.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 return \"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 }>;\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 );\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 { IOperationIndex } 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: string,\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 { ConnectionState, ConnectionStateSnapshot } from \"../types.js\";\nimport { getLatestAppliedOrdinal } from \"./utils.js\";\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 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 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 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: 0,\n lastFailureUtcMs: 0,\n pushBlocked: false,\n pushFailureCount: 0,\n receivingPages: 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 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 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.transitionConnectionState(\"connected\");\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 { ChannelErrorSource } from \"../../sync/types.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 };\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 };\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\")\n .distinct()\n .execute();\n\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n return rows.map((row) => row.document_id);\n }\n}\n","import type { Kysely } from \"kysely\";\nimport { sql } from \"kysely\";\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: 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: { sinceTimestampUtcMs: \"0\" },\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\nfunction remoteRecordToRow(remote: RemoteRecord): InsertableSyncRemote {\n return {\n name: remote.name,\n collection_id: remote.collectionId,\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 ? remote.filter.documentId : null,\n filter_scopes: remote.filter.scope.length > 0 ? remote.filter.scope : 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 };\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(\n op.context.branch,\n input.sourceId,\n );\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 { IOperationIndex } 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 { JobStatus, type ShutdownStatus } 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 { ChannelError } 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 { ChannelErrorSource, SyncEventTypes } from \"./types.js\";\nimport {\n batchOperationsByDocument,\n chunkSyncOperations,\n createIdleHealth,\n filterOperations,\n splitTrailingSameTimestampRun,\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\nenum OutboxMode {\n Backfill = \"backfill\",\n BatchTriggered = \"batch-triggered\",\n}\n\nconst defaultSyncManagerConfig: SyncManagerConfig = {\n maxDeadLettersPerRemote: 100,\n maxInboxBatchSize: 32,\n};\n\nconst PLAN_KEY_TO_JOB_UUID_CAP = 10000;\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 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 id: record.id,\n name: record.name,\n collectionId: record.collectionId,\n filter: record.filter,\n options: record.options,\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 this.remotes.delete(record.name);\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.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(record.name);\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.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.id === id) {\n return remote;\n }\n }\n throw new Error(`Remote with id '${id}' does not exist`);\n }\n\n async add(\n name: string,\n collectionId: string,\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,\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 id: remoteId,\n name,\n collectionId,\n filter,\n options,\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 this.remotes.delete(name);\n await this.remoteStorage.remove(name);\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.name,\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this.backfillAbortControllers.delete(name);\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 // cancel any in-flight backfill for this remote\n const backfillController = this.backfillAbortControllers.get(name);\n if (backfillController) {\n backfillController.abort();\n this.backfillAbortControllers.delete(name);\n }\n\n // shutdown the channel\n await remote.channel.shutdown();\n\n // delete the remote's data\n await this.remoteStorage.remove(name);\n await this.cursorStorage.remove(name);\n\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.remotes.delete(name);\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.name, remote.channel);\n\n const unsubscribe = remote.channel.onConnectionStateChange((snapshot) => {\n void this.eventBus\n .emit(SyncEventTypes.CONNECTION_STATE_CHANGED, {\n remoteName: remote.name,\n remoteId: remote.id,\n previous: snapshot.state,\n current: snapshot.state,\n snapshot,\n } satisfies ConnectionStateChangedEvent)\n .catch(() => {});\n });\n this.connectionStateUnsubscribes.set(remote.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.name,\n syncOp.documentId,\n syncOp.jobId,\n syncOp.error?.message ?? \"unknown\",\n syncOp.jobDependencies,\n );\n\n this.quarantinedDocumentIds.add(syncOp.documentId);\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 };\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 } 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.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.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 syncOp.failed(\n new ChannelError(record.errorSource, new Error(record.errorMessage)),\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.name,\n );\n }\n\n private getRemotesForCollection(collectionId: string): Remote[] {\n return Array.from(this.remotes.values()).filter(\n (remote) => remote.collectionId === collectionId,\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 await this.updateOutbox(\n remote,\n remote.channel.outbox.latestOrdinal,\n OutboxMode.BatchTriggered,\n );\n }\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.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.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.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.name,\n syncOp.documentId,\n completedJobInfo.id,\n errorMessage,\n );\n const error = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(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.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.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 const errorMessage = completedJobInfo.error?.message || \"Unknown error\";\n const channelError = new ChannelError(\n ChannelErrorSource.Inbox,\n new Error(`Failed to apply operations: ${errorMessage}`),\n );\n syncOp.failed(channelError);\n remote.channel.deadLetter.add(syncOp);\n } else {\n syncOp.executed();\n }\n\n remote.channel.inbox.remove(syncOp);\n }\n }\n\n private async updateOutbox(\n remote: Remote,\n ackOrdinal: number,\n mode: OutboxMode = OutboxMode.Backfill,\n signal?: AbortSignal,\n ): Promise<void> {\n const composedSignal = signal\n ? AbortSignal.any([signal, this.abortController.signal])\n : this.abortController.signal;\n\n let maxOrdinal = ackOrdinal;\n const lastJobByDoc = new Map<string, string>();\n let prevChainJobId: string | undefined;\n const sinceTimestamp = remote.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.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 };\n\n let page = await this.operationIndex.find(\n remote.collectionId,\n ackOrdinal,\n { excludeSourceRemote: remote.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.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 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 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, SyncModule } 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 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 ): SyncModule {\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 { IEventBus } from \"../events/interfaces.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 { 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 * @param request - Batch mutation 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 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 * Container for all sync manager dependencies created during the build process.\n */\nexport interface SyncModule {\n remoteStorage: ISyncRemoteStorage;\n cursorStorage: ISyncCursorStorage;\n deadLetterStorage: ISyncDeadLetterStorage;\n channelFactory: IChannelFactory;\n syncManager: ISyncManager;\n}\n\n/**\n * Container for all reactor dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface ReactorModule {\n eventBus: IEventBus;\n documentModelRegistry: IDocumentModelRegistry;\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 syncModule: SyncModule | undefined;\n reactor: IReactor;\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\n/**\n * Container for all reactor client dependencies created during the build process.\n * Provides direct access to internal components for advanced use cases,\n * testing, or integration scenarios.\n */\nexport interface 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: ReactorModule | undefined;\n}\n","import type {\n Action,\n CreateDocumentActionInput,\n DocumentModelModule,\n ISigner,\n Operation,\n PHDocument,\n} 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 toVersion: document.state.document.version,\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 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 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 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 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 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 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 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 DocumentModelModule,\n UpgradeManifest,\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 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 type { IEventBus } from \"../events/interfaces.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\";\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 { IDocumentModelLoader } 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 { 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 { Reactor } from \"./reactor.js\";\nimport type {\n Database,\n IReactor,\n ReactorFeatures,\n ReactorModule,\n SyncModule,\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 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 * Describes a document-model package the worker should import at runtime.\n * Either a bare-specifier package or an absolute file path.\n */\nexport type DocumentModelSpecInput =\n | { packageName: string; version: string }\n | { filePath: string };\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 preReadyKinds: BuiltInReadModelKind[];\n postReadyKinds: BuiltInReadModelKind[];\n poolSize?: number;\n initTimeoutMs?: number;\n shutdownGraceMs?: number;\n drainTimeoutMs?: number;\n chainDepthReportIntervalMs?: number;\n};\n\nexport class ReactorBuilder {\n private logger?: ILogger;\n private documentModels: DocumentModelModule<any>[] = [];\n private upgradeManifests: UpgradeManifest<readonly number[]>[] = [];\n private features: ReactorFeatures = { legacyStorageEnabled: false };\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 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 documentModelSpecs: DocumentModelSpecInput[] = [];\n private workerPoolConfig?: WorkerPoolConfig;\n private resolvedModelManifest?: ModelManifestEntry[];\n private workerDbConfig?: DbConfig;\n private workerSignatureVerifierSpec?: SignatureVerifierSpec;\n private workerFactory?: WorkerFactory;\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 withDocumentModels(models: DocumentModelModule<any>[]): this {\n this.documentModels = models;\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 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 withDocumentModelSpecs(specs: DocumentModelSpecInput[]): this {\n this.documentModelSpecs = specs;\n return this;\n }\n\n /**\n * Stores the worker-pool configuration. When `config.enabled === true` the\n * builder constructs a {@link WorkerPoolJobExecutorManager} in place of the\n * in-process {@link SimpleJobExecutorManager}.\n */\n withWorkerPool(config: WorkerPoolConfig): this {\n this.workerPoolConfig = config;\n return this;\n }\n\n /**\n * Postgres connection info forwarded to each worker so it can open its own\n * pool. Required when `workerPool.enabled === true` unless a custom\n * `withWorkerFactory` or `withExecutor` is provided.\n */\n withWorkerDbConfig(db: DbConfig): this {\n this.workerDbConfig = db;\n return this;\n }\n\n /**\n * Factory spec the worker imports to instantiate its signature verifier.\n * Required when `workerPool.enabled === true` unless a custom\n * `withWorkerFactory` or `withExecutor` is provided.\n */\n withWorkerSignatureVerifierSpec(spec: SignatureVerifierSpec): this {\n this.workerSignatureVerifierSpec = spec;\n return this;\n }\n\n /**\n * Inject a custom {@link WorkerFactory}. When set, the builder skips\n * default thread-transport wiring and hands the factory directly to the\n * pool manager. Use this in tests or to plug in a different transport\n * (e.g. a child-process adapter).\n */\n withWorkerFactory(factory: WorkerFactory): this {\n this.workerFactory = factory;\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}. The same `DbConfig` registered via\n * {@link withWorkerDbConfig} is reused for the projection workers'\n * connection info; only the `poolSize` is overridden by\n * {@link ProjectionShardBuilderConfig.poolSize}.\n *\n * Requires {@link withWorkerDbConfig} (the projection workers need\n * connection info to open their own pools). The same model manifest\n * resolved from {@link withDocumentModelSpecs} is 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<ReactorModule> {\n if (!this.logger) {\n this.logger = new ConsoleLogger([\"reactor\"]);\n }\n\n if (this.workerPoolConfig?.enabled) {\n if (this.documentModels.length > 0) {\n throw new Error(\n \"workerPool.enabled requires withDocumentModelSpecs; remove withDocumentModels() in worker-pool mode.\",\n );\n }\n if (this.documentModelSpecs.length === 0) {\n throw new Error(\n \"workerPool.enabled requires at least one spec registered via withDocumentModelSpecs.\",\n );\n }\n const needsDefaultFactory = !this.executorManager && !this.workerFactory;\n if (needsDefaultFactory && !this.workerDbConfig) {\n throw new Error(\n \"workerPool.enabled requires withWorkerDbConfig (or a custom withWorkerFactory / withExecutor).\",\n );\n }\n if (needsDefaultFactory && !this.workerSignatureVerifierSpec) {\n throw new Error(\n \"workerPool.enabled requires withWorkerSignatureVerifierSpec (or a custom withWorkerFactory / withExecutor).\",\n );\n }\n }\n\n if (this.documentModelSpecs.length > 0) {\n this.resolvedModelManifest = this.documentModelSpecs.map((input) => {\n if (\"filePath\" in input) {\n const entry: ModelManifestEntry = {\n documentType: \"<unresolved>\",\n version: \"<unresolved>\",\n spec: {\n module: { filePath: input.filePath, exportName: \"documentModel\" },\n },\n };\n return entry;\n }\n const entry: ModelManifestEntry = {\n documentType: \"<unresolved>\",\n version: input.version,\n spec: {\n module: {\n packageName: input.packageName,\n exportName: \"documentModel\",\n },\n },\n };\n return entry;\n });\n }\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 (this.documentModels.length > 0) {\n const results = documentModelRegistry.registerModules(\n ...this.documentModels,\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 baseDatabase =\n this.kyselyInstance ??\n (this.workerPoolConfig?.enabled && this.workerDbConfig\n ? await this.createPostgresDatabase(this.workerDbConfig)\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 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.workerPoolConfig?.enabled) {\n const factory =\n this.workerFactory ??\n (await this.createDefaultWorkerFactory(this.workerPoolConfig));\n const poolManager = new WorkerPoolJobExecutorManager(\n factory,\n eventBus,\n queue,\n jobTracker,\n this.logger,\n resolver,\n collectionMembershipCache,\n this.executorConfig.jobTimeoutMs,\n );\n executorManager = poolManager;\n executorStartCount = this.workerPoolConfig.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 );\n }\n }\n\n await executorManager.start(executorStartCount);\n\n const readModelInstances: IReadModel[] = Array.from(\n new Set([...this.readModels]),\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 );\n\n try {\n await documentView.init();\n } catch (error) {\n console.error(\"Error initializing document view\", error);\n }\n\n readModelInstances.push(documentView);\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 console.error(\"Error initializing document indexer\", error);\n }\n\n readModelInstances.push(documentIndexer);\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 );\n\n try {\n await processorManager.init();\n } catch (error) {\n console.error(\"Error initializing processor manager\", error);\n }\n\n for (const factory of this.readModelFactories) {\n const readModel = await factory({\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n });\n readModelInstances.push(readModel);\n }\n\n const readModelCoordinator = this.readModelCoordinator\n ? this.readModelCoordinator\n : this.projectionShardConfig\n ? await this.createProjectionShardManager(\n this.projectionShardConfig,\n eventBus,\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: SyncModule | 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 const module: ReactorModule = {\n eventBus,\n documentModelRegistry,\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 pools: this.instrumentedPools,\n };\n\n if (this.signalHandlersEnabled) {\n this.attachSignalHandlers(module);\n }\n\n return module;\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 private async createProjectionShardManager(\n config: ProjectionShardBuilderConfig,\n eventBus: IEventBus,\n ): Promise<IReadModelCoordinator> {\n if (!this.workerDbConfig) {\n throw new Error(\n \"withProjectionShards requires withWorkerDbConfig; projection workers need connection info to open their own pools.\",\n );\n }\n const models = this.resolvedModelManifest ?? [];\n const db: DbConfig = {\n ...this.workerDbConfig,\n poolSize: config.poolSize ?? this.workerDbConfig.poolSize,\n 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 });\n await manager.startup();\n this.shutdownHooks.push(() => manager.shutdown());\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 `workerPool.enabled` is set and\n * the caller did not inject `withWorkerFactory`. Each worker spawns a real\n * `node:worker_threads` Worker pointing at the compiled `worker/entry.js`.\n */\n private async createDefaultWorkerFactory(\n poolConfig: WorkerPoolConfig,\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 const db = this.workerDbConfig!;\n const signatureVerifier = this.workerSignatureVerifierSpec!;\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 },\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: ReactorModule): 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 { 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 { ReactorClient } from \"../client/reactor-client.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 { IReactor, ReactorClientModule, ReactorModule } 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\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 public async build(): Promise<ReactorClient> {\n const module = await this.buildModule();\n return module.client;\n }\n\n public async buildModule(): Promise<ReactorClientModule> {\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: ReactorModule | 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 client = new ReactorClient(\n this.logger,\n reactor,\n signer,\n subscriptionManager,\n jobAwaiter,\n documentIndexer,\n documentView,\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 { 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 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 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\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: keyframeIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\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: keyframeIssues.length === 0 && snapshotIssues.length === 0,\n keyframeIssues,\n snapshotIssues,\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 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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"}