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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -606,7 +606,7 @@ interface AtomicTxn {
606
606
  * revision field and lastModified timestamp.
607
607
  */
608
608
  type DocumentRevisions = {
609
- /** Map of scope to operation index for that scope */revision: Record<string, number>; /** Latest timestamp across revisions */
609
+ /** Map of scope to operation index for that scope */revision: Record<string, number>; /** The largest operation timestamp in the document, across every scope. */
610
610
  latestTimestamp: string;
611
611
  };
612
612
  /**
@@ -2109,298 +2109,436 @@ declare class ReactorClient implements IReactorClient {
2109
2109
  private removeAllIncomingRelationships;
2110
2110
  }
2111
2111
  //#endregion
2112
- //#region src/executor/types.d.ts
2112
+ //#region src/cache/collection-membership-cache.d.ts
2113
+ interface ICollectionMembershipCache {
2114
+ getCollectionsForDocuments(documentIds: string[]): Promise<Record<string, string[]>>;
2115
+ invalidate(documentId: string): void;
2116
+ }
2117
+ //#endregion
2118
+ //#region src/cache/document-meta-cache-types.d.ts
2113
2119
  /**
2114
- * Represents the result of a job execution
2120
+ * Cached document metadata from the "document" scope.
2121
+ *
2122
+ * This lightweight structure holds essential document information needed by
2123
+ * the job executor without fetching full scope state. It provides an explicit
2124
+ * cross-scope contract for accessing document scope metadata.
2115
2125
  */
2116
- type JobResult = {
2117
- /** The job that was executed */job: Job; /** Whether the job executed successfully */
2118
- success: boolean; /** Error if the job failed */
2119
- error?: Error; /** The operations generated from the actions (if successful) */
2120
- operations?: Operation[];
2126
+ type CachedDocumentMeta = {
2121
2127
  /**
2122
- * Operations with context (includes ephemeral resultingState).
2123
- * Used for emitting to IDocumentView via event bus.
2128
+ * The full PHDocumentState from document.state.document.
2129
+ * Contains version, hash, isDeleted, deletedAtUtcIso, etc.
2124
2130
  */
2125
- operationsWithContext?: OperationWithContext$1[]; /** Timestamp when the job execution completed */
2126
- completedAt?: string; /** Duration of job execution in milliseconds */
2127
- duration?: number; /** Any additional metadata from the execution */
2128
- metadata?: Record<string, any>;
2129
- };
2130
- /**
2131
- * Enforcement the reactor performs, each off by default.
2132
- *
2133
- * An evaluation made while replaying is part of the document's history, so two
2134
- * reactors that share documents and disagree on these diverge. A flag is turned
2135
- * on for a set of reactors that sync with each other, not for one node.
2136
- */
2137
- type ReactorFeatureFlags = {
2131
+ state: PHDocumentState;
2138
2132
  /**
2139
- * Decide whether an operation may be admitted by building a decision model
2140
- * over the document stream, rather than reading the deleted flag from the
2141
- * document meta cache. Deletion then takes effect from the deleting
2142
- * operation's position rather than for the whole document.
2133
+ * The document type (from header), cached for convenience.
2143
2134
  */
2144
- documentDecisions: boolean;
2145
- };
2146
- /**
2147
- * Configuration options for the job executor
2148
- */
2149
- type JobExecutorConfig = {
2150
- /** Feature flags; anything unset is off. */featureFlags?: Partial<ReactorFeatureFlags>; /** Maximum number of conflicting operations to skip when reshuffling. */
2151
- maxSkipThreshold?: number; /** Maximum number of concurrent jobs to execute */
2152
- maxConcurrency?: number; /** Maximum time in milliseconds a job can run before being considered timed out */
2153
- jobTimeoutMs?: number; /** Base delay in milliseconds for exponential backoff retries */
2154
- retryBaseDelayMs?: number; /** Maximum delay in milliseconds for exponential backoff retries */
2155
- retryMaxDelayMs?: number;
2156
- /** Maximum elapsed milliseconds before yielding to the main thread between actions.
2157
- * Keeps the UI responsive when processing large batches. */
2158
- yieldDeadlineMs?: number;
2159
- };
2160
- /**
2161
- * Event types for the job executor
2162
- */
2163
- declare const JobExecutorEventTypes: {
2164
- readonly JOB_STARTED: 20000;
2165
- readonly JOB_COMPLETED: 20001;
2166
- readonly JOB_FAILED: 20002;
2167
- readonly EXECUTOR_STARTED: 20003;
2168
- readonly EXECUTOR_STOPPED: 20004;
2169
- };
2170
- /**
2171
- * Event data for job execution events
2172
- */
2173
- type JobStartedEvent = {
2174
- job: Job;
2175
- startedAt: string;
2135
+ documentType: string;
2176
2136
  /**
2177
- * Identifier of the executor that took the job. For the worker pool this is
2178
- * the thread-worker id (e.g. "reactor-worker-3"); for the in-process simple
2179
- * manager it is "in-process-<index>". Optional for backwards compatibility
2180
- * with consumers built before the field was added.
2137
+ * The revision of the document scope when this metadata was captured.
2138
+ * Used for cache invalidation and consistency checks.
2181
2139
  */
2182
- workerId?: string;
2183
- };
2184
- type JobCompletedEvent = {
2185
- job: Job;
2186
- result: JobResult; /** See {@link JobStartedEvent.workerId}. */
2187
- workerId?: string;
2188
- };
2189
- type JobFailedEvent = {
2190
- job: Job;
2191
- error: string;
2192
- willRetry: boolean;
2193
- retryCount: number; /** See {@link JobStartedEvent.workerId}. */
2194
- workerId?: string;
2195
- };
2196
- type ExecutorStartedEvent = {
2197
- config: JobExecutorConfig;
2198
- startedAt: string;
2199
- };
2200
- type ExecutorStoppedEvent = {
2201
- stoppedAt: string;
2202
- graceful: boolean;
2203
- };
2204
- /**
2205
- * Status information for the job executor manager
2206
- */
2207
- type ExecutorManagerStatus = {
2208
- /** Whether the manager is currently running */isRunning: boolean; /** Number of executor instances managed */
2209
- numExecutors: number; /** Number of jobs currently being processed */
2210
- activeJobs: number; /** Total number of jobs processed since start */
2211
- totalJobsProcessed: number;
2212
- };
2213
- //#endregion
2214
- //#region src/executor/worker/protocol.d.ts
2215
- /**
2216
- * A JSON-clonable value safe to send across the worker IPC boundary.
2217
- *
2218
- * The shape mirrors the structured-clone subset used by the parent's
2219
- * sanitizer: primitives, arrays, plain objects, plus the explicit
2220
- * {@link ErrorInfo} shape for marshalled Errors.
2221
- *
2222
- * @see Wire Protocol Reference wiki page
2223
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2224
- */
2225
- type SanitizedArg = null | boolean | number | string | ErrorInfo | SanitizedArg[] | {
2226
- [key: string]: SanitizedArg;
2227
- };
2228
- /**
2229
- * Structured representation of an Error for IPC transport.
2230
- *
2231
- * Class instances cannot be structured-cloned across worker boundaries,
2232
- * so Errors are flattened into this shape on the worker side and
2233
- * reconstructed on the parent side.
2234
- *
2235
- * @see Wire Protocol Reference wiki page
2236
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2237
- */
2238
- type ErrorInfo = {
2239
- name: string;
2240
- message: string;
2241
- stack?: string;
2242
- cause?: ErrorInfo;
2243
- };
2244
- /**
2245
- * Reference to a module that the worker should `import()` at runtime,
2246
- * along with the named export to pluck out as the factory.
2247
- *
2248
- * Exactly one of `packageName` or `filePath` is provided.
2249
- *
2250
- * @see Wire Protocol Reference wiki page
2251
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2252
- */
2253
- type ModuleRef = {
2254
- /** Bare-specifier package name (resolved by the worker's module loader). */packageName: string; /** Named export within the module to invoke as the factory. */
2255
- exportName: string;
2256
- } | {
2257
- /** Absolute or worker-resolvable file path to import. */filePath: string; /** Named export within the module to invoke as the factory. */
2258
- exportName: string;
2259
- };
2260
- /**
2261
- * Factory specification shared by the signature verifier and document
2262
- * model spec channels. The worker imports `module.exportName` and invokes
2263
- * it with `initArgs` to obtain the actual instance.
2264
- *
2265
- * `initArgs` must be JSON-clonable.
2266
- *
2267
- * @see Wire Protocol Reference wiki page
2268
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2269
- */
2270
- type FactorySpec = {
2271
- module: ModuleRef;
2272
- initArgs?: SanitizedArg;
2140
+ documentScopeRevision: number;
2273
2141
  };
2274
2142
  /**
2275
- * Factory spec for the signature verifier the worker should instantiate.
2276
- *
2277
- * Structurally identical to {@link FactorySpec}; the alias exists so call
2278
- * sites read intent-fully.
2279
- *
2280
- * @see Wire Protocol Reference wiki page
2281
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2282
- */
2283
- type SignatureVerifierSpec = FactorySpec;
2284
- /**
2285
- * Factory spec for a document model module the worker should instantiate.
2286
- *
2287
- * Structurally identical to {@link FactorySpec}; the alias exists so call
2288
- * sites read intent-fully.
2289
- *
2290
- * @see Wire Protocol Reference wiki page
2291
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2292
- */
2293
- type DocumentModelSpec = FactorySpec;
2294
- /**
2295
- * One entry in the document model manifest the worker materializes on
2296
- * startup (or extends lazily via `load-model`).
2143
+ * Interface for the document metadata cache.
2297
2144
  *
2298
- * @see Wire Protocol Reference wiki page
2299
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2300
- */
2301
- type ModelManifestEntry = {
2302
- /** Document type identifier (e.g. "ph/account"). */documentType: string; /** Document model version this entry registers. */
2303
- version: string; /** Factory spec the worker imports and invokes to obtain the model. */
2304
- spec: DocumentModelSpec;
2305
- };
2306
- /**
2307
- * JSON-clonable Postgres connection info passed to the worker so it can
2308
- * open its own pool. Storage-specific wiring may extend this shape in
2309
- * later phases.
2145
+ * This cache provides an explicit cross-scope contract for accessing document
2146
+ * scope metadata. It solves the problem where job execution in one scope (e.g.,
2147
+ * "global") needs access to document scope state (version, isDeleted, etc.)
2148
+ * which may be stale in scope-specific caches or keyframes.
2310
2149
  *
2311
- * @see Wire Protocol Reference wiki page
2312
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2150
+ * The cache supports:
2151
+ * - Latest metadata retrieval with LRU caching
2152
+ * - Historical metadata reconstruction for reshuffling scenarios
2153
+ * - Eager updates after document scope operations
2313
2154
  */
2314
- type DbConfig = {
2315
- host: string;
2316
- port: number;
2317
- database: string;
2318
- user: string;
2319
- password: string;
2320
- ssl?: boolean;
2321
- applicationName?: string;
2322
- poolSize?: number;
2155
+ interface IDocumentMetaCache {
2323
2156
  /**
2324
- * Maximum time (ms) a caller will wait to acquire a connection from the
2325
- * pool before pg.Pool throws. When omitted, pg defaults to 0 (unlimited
2326
- * wait), which hides acquire-starvation as silent latency.
2157
+ * Retrieves the LATEST document metadata from cache or rebuilds from operations.
2158
+ *
2159
+ * On cache miss, fetches all document scope operations and reconstructs the
2160
+ * current PHDocumentState by applying UPGRADE_DOCUMENT and DELETE_DOCUMENT
2161
+ * operations.
2162
+ *
2163
+ * @param documentId - The document identifier
2164
+ * @param branch - Branch name
2165
+ * @param signal - Optional abort signal to cancel the operation
2166
+ * @returns The cached or rebuilt document metadata
2167
+ * @throws {Error} "Operation aborted" if signal is aborted
2168
+ * @throws {Error} If document not found (no CREATE_DOCUMENT operation)
2327
2169
  */
2328
- connectionTimeoutMillis?: number;
2170
+ getDocumentMeta(documentId: string, branch: string, signal?: AbortSignal): Promise<CachedDocumentMeta>;
2329
2171
  /**
2330
- * How long (ms) an idle connection stays open before pg closes it. When
2331
- * omitted, pg defaults to 10000.
2332
- */
2333
- idleTimeoutMillis?: number;
2334
- };
2335
- /**
2336
- * Configuration for the executor worker pool.
2337
- *
2338
- * Mirrors the `workerPool` sub-config on {@link JobExecutorConfig};
2339
- * a later card wires this into the executor config.
2172
+ * Rebuilds document metadata at a SPECIFIC revision (always rebuilds, no caching).
2173
+ *
2174
+ * Used during reshuffling when operations need to be inserted at a previous
2175
+ * revision and we need the document scope state as of that point in time.
2176
+ *
2177
+ * @param documentId - The document identifier
2178
+ * @param branch - Branch name
2179
+ * @param targetRevision - The document scope revision to reconstruct up to
2180
+ * @param signal - Optional abort signal to cancel the operation
2181
+ * @returns Document metadata as of the target revision
2182
+ * @throws {Error} "Operation aborted" if signal is aborted
2183
+ * @throws {Error} If document not found
2184
+ */
2185
+ rebuildAtRevision(documentId: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<CachedDocumentMeta>;
2186
+ /**
2187
+ * Eagerly updates cached metadata after document scope operations.
2188
+ *
2189
+ * Called by the job executor after CREATE_DOCUMENT, UPGRADE_DOCUMENT, or
2190
+ * DELETE_DOCUMENT operations to keep the cache current.
2191
+ *
2192
+ * @param documentId - The document identifier
2193
+ * @param branch - Branch name
2194
+ * @param meta - The new metadata to cache
2195
+ */
2196
+ putDocumentMeta(documentId: string, branch: string, meta: CachedDocumentMeta): void;
2197
+ /**
2198
+ * Invalidates cached document metadata.
2199
+ *
2200
+ * Call before reshuffling operations that modify the document scope, or
2201
+ * when document state may have changed externally.
2202
+ *
2203
+ * @param documentId - The document identifier
2204
+ * @param branch - Optional branch to narrow invalidation (if omitted, all branches)
2205
+ * @returns Number of entries invalidated
2206
+ */
2207
+ invalidate(documentId: string, branch?: string): number;
2208
+ /**
2209
+ * Clears all cached document metadata.
2210
+ */
2211
+ clear(): void;
2212
+ /**
2213
+ * Performs startup initialization.
2214
+ */
2215
+ startup(): Promise<void>;
2216
+ /**
2217
+ * Performs graceful shutdown.
2218
+ */
2219
+ shutdown(): Promise<void>;
2220
+ }
2221
+ //#endregion
2222
+ //#region src/storage/kysely/types.d.ts
2223
+ interface OperationTable {
2224
+ id: Generated<number>;
2225
+ jobId: string;
2226
+ opId: string;
2227
+ prevOpId: string;
2228
+ writeTimestampUtcMs: Generated<Date>;
2229
+ documentId: string;
2230
+ documentType: string;
2231
+ scope: string;
2232
+ branch: string;
2233
+ timestampUtcMs: Date;
2234
+ index: number;
2235
+ action: unknown;
2236
+ skip: number;
2237
+ error?: string | null;
2238
+ deniedReason?: string | null;
2239
+ hash: string;
2240
+ }
2241
+ interface KeyframeTable {
2242
+ id: Generated<number>;
2243
+ documentId: string;
2244
+ documentType: string;
2245
+ scope: string;
2246
+ branch: string;
2247
+ revision: number;
2248
+ document: unknown;
2249
+ createdAt: Generated<Date>;
2250
+ }
2251
+ interface DocumentCollectionTable {
2252
+ documentId: string;
2253
+ collectionId: string;
2254
+ joinedOrdinal: bigint;
2255
+ leftOrdinal: bigint | null;
2256
+ }
2257
+ interface OperationIndexOperationTable {
2258
+ ordinal: Generated<number>;
2259
+ opId: string;
2260
+ documentId: string;
2261
+ documentType: string;
2262
+ scope: string;
2263
+ branch: string;
2264
+ timestampUtcMs: string;
2265
+ writeTimestampUtcMs: Generated<Date>;
2266
+ index: number;
2267
+ skip: number;
2268
+ hash: string;
2269
+ action: unknown;
2270
+ deniedReason?: string | null;
2271
+ sourceRemote: Generated<string>;
2272
+ }
2273
+ interface SyncRemoteTable {
2274
+ name: string;
2275
+ collection_id: string;
2276
+ channel_type: string;
2277
+ channel_id: string;
2278
+ remote_name: string;
2279
+ channel_parameters: unknown;
2280
+ filter_document_ids: unknown;
2281
+ filter_scopes: unknown;
2282
+ filter_branch: string;
2283
+ push_state: string;
2284
+ push_last_success_utc_ms: string | null;
2285
+ push_last_failure_utc_ms: string | null;
2286
+ push_failure_count: number;
2287
+ pull_state: string;
2288
+ pull_last_success_utc_ms: string | null;
2289
+ pull_last_failure_utc_ms: string | null;
2290
+ pull_failure_count: number;
2291
+ created_at: Generated<Date>;
2292
+ updated_at: Generated<Date>;
2293
+ }
2294
+ interface SyncCursorTable {
2295
+ remote_name: string;
2296
+ cursor_type: string;
2297
+ cursor_ordinal: bigint;
2298
+ last_synced_at_utc_ms: string | null;
2299
+ updated_at: Generated<Date>;
2300
+ }
2301
+ /**
2302
+ * Kysely table definition for the `sync_dead_letters` table.
2303
+ */
2304
+ interface SyncDeadLetterTable {
2305
+ ordinal: Generated<number>;
2306
+ id: string;
2307
+ job_id: string;
2308
+ job_dependencies: unknown;
2309
+ remote_name: string;
2310
+ document_id: string;
2311
+ scopes: unknown;
2312
+ branch: string;
2313
+ operations: unknown;
2314
+ error_source: string;
2315
+ error_message: string;
2316
+ created_at: Generated<Date>;
2317
+ }
2318
+ interface Database$1 {
2319
+ Operation: OperationTable;
2320
+ Keyframe: KeyframeTable;
2321
+ document_collections: DocumentCollectionTable;
2322
+ operation_index_operations: OperationIndexOperationTable;
2323
+ sync_remotes: SyncRemoteTable;
2324
+ sync_cursors: SyncCursorTable;
2325
+ sync_dead_letters: SyncDeadLetterTable;
2326
+ }
2327
+ interface DocumentTable {
2328
+ id: string;
2329
+ createdAt: Generated<Date>;
2330
+ updatedAt: Generated<Date>;
2331
+ }
2332
+ interface DocumentRelationshipTable {
2333
+ id: Generated<string>;
2334
+ sourceId: string;
2335
+ targetId: string;
2336
+ relationshipType: string;
2337
+ metadata: unknown;
2338
+ createdAt: Generated<Date>;
2339
+ updatedAt: Generated<Date>;
2340
+ }
2341
+ interface IndexerStateTable {
2342
+ id: Generated<number>;
2343
+ lastOperationId: number;
2344
+ lastOperationTimestamp: Generated<Date>;
2345
+ }
2346
+ interface DocumentIndexerDatabase {
2347
+ Document: DocumentTable;
2348
+ DocumentRelationship: DocumentRelationshipTable;
2349
+ IndexerState: IndexerStateTable;
2350
+ }
2351
+ //#endregion
2352
+ //#region src/executor/worker/protocol.d.ts
2353
+ /**
2354
+ * A JSON-clonable value safe to send across the worker IPC boundary.
2355
+ *
2356
+ * The shape mirrors the structured-clone subset used by the parent's
2357
+ * sanitizer: primitives, arrays, plain objects, plus the explicit
2358
+ * {@link ErrorInfo} shape for marshalled Errors.
2340
2359
  *
2341
2360
  * @see Wire Protocol Reference wiki page
2342
2361
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2343
2362
  */
2344
- type WorkerPoolConfig = {
2345
- /** Whether the worker pool is active; when false the executor runs in-process. */enabled: boolean; /** Number of worker instances to spawn. */
2346
- numWorkers: number; /** Worker isolation mode. */
2347
- workerType: "thread" | "process"; /** Optional heartbeat interval in milliseconds. */
2348
- heartbeatMs?: number; /** Optional per-worker Postgres pool size override. */
2349
- workerPgPoolSize?: number;
2363
+ type SanitizedArg = null | boolean | number | string | ErrorInfo | SanitizedArg[] | {
2364
+ [key: string]: SanitizedArg;
2350
2365
  };
2351
2366
  /**
2352
- * Payload the worker reports back when a job's write phase is complete.
2367
+ * Structured representation of an Error for IPC transport.
2353
2368
  *
2354
- * Parent fills `collectionMemberships` at emission time, so it is
2355
- * intentionally absent from the worker -> parent message.
2369
+ * Class instances cannot be structured-cloned across worker boundaries,
2370
+ * so Errors are flattened into this shape on the worker side and
2371
+ * reconstructed on the parent side.
2356
2372
  *
2357
2373
  * @see Wire Protocol Reference wiki page
2358
2374
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2359
2375
  */
2360
- type JobWriteReadyPayload = {
2361
- operations: OperationWithContext$1[];
2362
- jobMeta: JobMeta;
2376
+ type ErrorInfo = {
2377
+ name: string;
2378
+ message: string;
2379
+ stack?: string;
2380
+ cause?: ErrorInfo;
2363
2381
  };
2364
2382
  /**
2365
- * Initializes a freshly spawned worker with the configuration and
2366
- * factories it needs to start executing jobs.
2383
+ * Reference to a module that the worker should `import()` at runtime,
2384
+ * along with the named export to pluck out as the factory.
2385
+ *
2386
+ * Exactly one of `packageName` or `filePath` is provided.
2367
2387
  *
2368
2388
  * @see Wire Protocol Reference wiki page
2369
2389
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2370
2390
  */
2371
- type InitMessage = {
2372
- type: "init";
2373
- correlationId: string;
2374
- workerId: string;
2375
- poolConfig: WorkerPoolConfig;
2376
- db: DbConfig; /** Omitted = the worker performs no executor-side signature verification. */
2377
- signatureVerifier?: SignatureVerifierSpec;
2378
- models: ModelManifestEntry[]; /** Omitted = the worker builds its executor with the built-in defaults. */
2379
- executorConfig?: JobExecutorConfig;
2391
+ type ModuleRef = {
2392
+ /** Bare-specifier package name (resolved by the worker's module loader). */packageName: string; /** Named export within the module to invoke as the factory. */
2393
+ exportName: string;
2394
+ } | {
2395
+ /** Absolute or worker-resolvable file path to import. */filePath: string; /** Named export within the module to invoke as the factory. */
2396
+ exportName: string;
2380
2397
  };
2381
2398
  /**
2382
- * Dispatches a job to the worker for execution.
2399
+ * Factory specification shared by the signature verifier and document
2400
+ * model spec channels. The worker imports `module.exportName` and invokes
2401
+ * it with `initArgs` to obtain the actual instance.
2402
+ *
2403
+ * `initArgs` must be JSON-clonable.
2383
2404
  *
2384
2405
  * @see Wire Protocol Reference wiki page
2385
2406
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2386
2407
  */
2387
- type ExecuteMessage = {
2388
- type: "execute";
2389
- correlationId: string;
2390
- job: Job;
2408
+ type FactorySpec = {
2409
+ module: ModuleRef;
2410
+ initArgs?: SanitizedArg;
2391
2411
  };
2392
2412
  /**
2393
- * Requests cancellation of an in-flight job.
2413
+ * Factory spec for the signature verifier the worker should instantiate.
2414
+ *
2415
+ * Structurally identical to {@link FactorySpec}; the alias exists so call
2416
+ * sites read intent-fully.
2394
2417
  *
2395
2418
  * @see Wire Protocol Reference wiki page
2396
2419
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2397
2420
  */
2398
- type AbortMessage = {
2399
- type: "abort";
2400
- correlationId: string; /** correlationId of the `execute` message being aborted. */
2401
- targetCorrelationId: string;
2402
- reason?: string;
2403
- };
2421
+ type SignatureVerifierSpec = FactorySpec;
2422
+ /**
2423
+ * Factory spec for a document model module the worker should instantiate.
2424
+ *
2425
+ * Structurally identical to {@link FactorySpec}; the alias exists so call
2426
+ * sites read intent-fully.
2427
+ *
2428
+ * @see Wire Protocol Reference wiki page
2429
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2430
+ */
2431
+ type DocumentModelSpec = FactorySpec;
2432
+ /**
2433
+ * One entry in the document model manifest the worker materializes on
2434
+ * startup (or extends lazily via `load-model`).
2435
+ *
2436
+ * @see Wire Protocol Reference wiki page
2437
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2438
+ */
2439
+ type ModelManifestEntry = {
2440
+ /** Document type identifier (e.g. "ph/account"). */documentType: string; /** Document model version this entry registers. */
2441
+ version: string; /** Factory spec the worker imports and invokes to obtain the model. */
2442
+ spec: DocumentModelSpec;
2443
+ };
2444
+ /**
2445
+ * JSON-clonable Postgres connection info passed to the worker so it can
2446
+ * open its own pool. Storage-specific wiring may extend this shape in
2447
+ * later phases.
2448
+ *
2449
+ * @see Wire Protocol Reference wiki page
2450
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2451
+ */
2452
+ type DbConfig = {
2453
+ host: string;
2454
+ port: number;
2455
+ database: string;
2456
+ user: string;
2457
+ password: string;
2458
+ ssl?: boolean;
2459
+ applicationName?: string;
2460
+ poolSize?: number;
2461
+ /**
2462
+ * Maximum time (ms) a caller will wait to acquire a connection from the
2463
+ * pool before pg.Pool throws. When omitted, pg defaults to 0 (unlimited
2464
+ * wait), which hides acquire-starvation as silent latency.
2465
+ */
2466
+ connectionTimeoutMillis?: number;
2467
+ /**
2468
+ * How long (ms) an idle connection stays open before pg closes it. When
2469
+ * omitted, pg defaults to 10000.
2470
+ */
2471
+ idleTimeoutMillis?: number;
2472
+ };
2473
+ /**
2474
+ * Configuration for the executor worker pool.
2475
+ *
2476
+ * Mirrors the `workerPool` sub-config on {@link JobExecutorConfig};
2477
+ * a later card wires this into the executor config.
2478
+ *
2479
+ * @see Wire Protocol Reference wiki page
2480
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2481
+ */
2482
+ type WorkerPoolConfig = {
2483
+ /** Whether the worker pool is active; when false the executor runs in-process. */enabled: boolean; /** Number of worker instances to spawn. */
2484
+ numWorkers: number; /** Worker isolation mode. */
2485
+ workerType: "thread" | "process"; /** Optional heartbeat interval in milliseconds. */
2486
+ heartbeatMs?: number; /** Optional per-worker Postgres pool size override. */
2487
+ workerPgPoolSize?: number;
2488
+ };
2489
+ /**
2490
+ * Payload the worker reports back when a job's write phase is complete.
2491
+ *
2492
+ * Parent fills `collectionMemberships` at emission time, so it is
2493
+ * intentionally absent from the worker -> parent message.
2494
+ *
2495
+ * @see Wire Protocol Reference wiki page
2496
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2497
+ */
2498
+ type JobWriteReadyPayload = {
2499
+ operations: OperationWithContext$1[];
2500
+ jobMeta: JobMeta;
2501
+ };
2502
+ /**
2503
+ * Initializes a freshly spawned worker with the configuration and
2504
+ * factories it needs to start executing jobs.
2505
+ *
2506
+ * @see Wire Protocol Reference wiki page
2507
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2508
+ */
2509
+ type InitMessage = {
2510
+ type: "init";
2511
+ correlationId: string;
2512
+ workerId: string;
2513
+ poolConfig: WorkerPoolConfig;
2514
+ db: DbConfig; /** Omitted = the worker performs no executor-side signature verification. */
2515
+ signatureVerifier?: SignatureVerifierSpec;
2516
+ models: ModelManifestEntry[]; /** Omitted = the worker builds its executor with the built-in defaults. */
2517
+ executorConfig?: JobExecutorConfig;
2518
+ };
2519
+ /**
2520
+ * Dispatches a job to the worker for execution.
2521
+ *
2522
+ * @see Wire Protocol Reference wiki page
2523
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2524
+ */
2525
+ type ExecuteMessage = {
2526
+ type: "execute";
2527
+ correlationId: string;
2528
+ job: Job;
2529
+ };
2530
+ /**
2531
+ * Requests cancellation of an in-flight job.
2532
+ *
2533
+ * @see Wire Protocol Reference wiki page
2534
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2535
+ */
2536
+ type AbortMessage = {
2537
+ type: "abort";
2538
+ correlationId: string; /** correlationId of the `execute` message being aborted. */
2539
+ targetCorrelationId: string;
2540
+ reason?: string;
2541
+ };
2404
2542
  /**
2405
2543
  * Asks the worker to drain in-flight work and exit.
2406
2544
  *
@@ -2557,498 +2695,859 @@ type PoolAcquireSamplesMessage = {
2557
2695
  */
2558
2696
  type WorkerMessage = ReadyMessage | ResultMessage | ModelLoadedMessage | ModelLoadFailedMessage | LogMessage | HeartbeatMessage | MetricsMessage | PoolAcquireSamplesMessage;
2559
2697
  //#endregion
2560
- //#region src/executor/interfaces.d.ts
2698
+ //#region src/core/model-sources.d.ts
2699
+ /** An importable file holding one or more document-model exports. */
2700
+ type FileModelSource = {
2701
+ filePath: string;
2702
+ exportName?: string;
2703
+ };
2704
+ /** An importable package specifier holding one or more document-model exports. */
2705
+ type PackageModelSource = {
2706
+ packageName: string;
2707
+ subpath?: string;
2708
+ exportName?: string;
2709
+ };
2561
2710
  /**
2562
- * Snapshot of the single in-flight slot maintained by an {@link IExecutorWorker}.
2711
+ * A source of document models: a live module, an importable file, or an
2712
+ * importable package. File and package sources can cross a worker-thread
2713
+ * boundary (workers re-import them); a live module cannot.
2563
2714
  */
2564
- type WorkerInFlightSnapshot = {
2565
- correlationId: string;
2566
- jobId: string;
2715
+ type DocumentModelSource = DocumentModelModule<any> | FileModelSource | PackageModelSource;
2716
+ //#endregion
2717
+ //#region src/registry/interfaces.d.ts
2718
+ type RegistrationResult<T> = {
2719
+ status: "success";
2720
+ item: T;
2721
+ } | {
2722
+ status: "error";
2723
+ item: T;
2724
+ error: Error;
2567
2725
  };
2568
2726
  /**
2569
- * Outcome of a worker-side job execution.
2727
+ * Loader that asynchronously resolves a document type to a
2728
+ * {@link DocumentModelSource}. Used by the queue to gate CREATE_DOCUMENT
2729
+ * jobs until the required model is available in the registry.
2570
2730
  *
2571
- * `result` mirrors the in-process `JobResult` exactly. `writeReady` carries
2572
- * the operations + jobMeta the parent needs to emit `JOB_WRITE_READY`, and is
2573
- * present only when the worker produced operations. It is absent on failure
2574
- * and on success-with-no-operations.
2731
+ * Return an importable source ({ filePath } or { packageName }) whenever
2732
+ * possible: the resolver registers the resolved models on the host registry
2733
+ * and broadcasts importable sources to executor workers. A live
2734
+ * DocumentModelModule is also valid but host-only — it cannot cross a
2735
+ * worker-thread boundary, so worker pools will not receive it.
2575
2736
  */
2576
- type WorkerExecutionOutcome = {
2577
- result: JobResult;
2578
- writeReady?: JobWriteReadyPayload;
2579
- };
2737
+ interface IDocumentModelLoader {
2738
+ load(documentType: string): Promise<DocumentModelSource>;
2739
+ }
2580
2740
  /**
2581
- * Parent-side handle for a single executor worker.
2582
- *
2583
- * Implementations wrap an IPC transport (worker_threads, child_process, or a
2584
- * test fake) and expose a transport-agnostic surface that the worker-pool
2585
- * manager uses to dispatch jobs. The handle owns one worker's lifecycle
2586
- * (`start` -> `execute`* -> `shutdown`) and bounds its in-flight map to a
2587
- * single entry; `SimpleJobExecutor` is single-threaded inside the worker, so
2588
- * concurrent dispatches would race its caches.
2741
+ * Registry for managing document model modules.
2742
+ * Provides centralized access to document models' reducers, utils, and specifications.
2743
+ * Supports version-aware module storage and upgrade manifest management.
2589
2744
  */
2590
- interface IExecutorWorker {
2591
- /** Stable identifier of the worker (mirrors `InitMessage.workerId`). */
2592
- readonly workerId: string;
2593
- /** Zero-based index within the pool, used for sticky routing. */
2594
- readonly index: number;
2745
+ interface IDocumentModelRegistry {
2595
2746
  /**
2596
- * Spawn the worker (if not already started), send the `init` payload and
2597
- * resolve when the worker replies with `ready`.
2747
+ * Register multiple modules at once.
2748
+ * Modules without a version field default to version 1.
2749
+ * Invalid or duplicate modules are skipped without breaking registration of the remaining modules.
2750
+ *
2751
+ * @param modules Document model modules to register
2752
+ * @returns Array of results, one per module, indicating success or failure
2598
2753
  */
2599
- start(): Promise<void>;
2754
+ registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
2600
2755
  /**
2601
- * Dispatch a job to the worker and resolve with its outcome — the
2602
- * `JobResult` and, on success-with-operations, a `writeReady` payload
2603
- * the parent will enrich and re-emit. Rejects with a transport-level
2604
- * error if the worker exits, aborts, or times out before producing a
2605
- * result.
2756
+ * Unregister all versions of the specified document types.
2757
+ *
2758
+ * @param documentTypes The document types to unregister
2759
+ * @returns true if all modules were unregistered, false if any were not found
2606
2760
  */
2607
- execute(job: Job, signal?: AbortSignal): Promise<WorkerExecutionOutcome>;
2761
+ unregisterModules(...documentTypes: string[]): boolean;
2608
2762
  /**
2609
- * Request cancellation of the in-flight job (if any). The handle posts an
2610
- * `abort` message; if the worker fails to reply within its grace window it
2611
- * is force-terminated.
2763
+ * Get a specific document model module by document type and optional version.
2764
+ * If version is not specified, returns the latest version.
2765
+ *
2766
+ * @param documentType The document type identifier
2767
+ * @param version Optional version number to retrieve
2768
+ * @returns The document model module
2769
+ * @throws ModuleNotFoundError if the document type or version is not registered
2612
2770
  */
2613
- abort(correlationId: string, reason?: string): void;
2771
+ getModule(documentType: string, version?: number): DocumentModelModule<any>;
2614
2772
  /**
2615
- * Stop the worker. When `graceful` is true the handle waits for the
2616
- * in-flight job to settle (up to `graceMs`) before terminating; otherwise
2617
- * the worker is terminated immediately.
2773
+ * Get all registered document model modules.
2774
+ *
2775
+ * @returns Array of all registered modules
2618
2776
  */
2619
- shutdown(graceful: boolean, graceMs?: number): Promise<void>;
2777
+ getAllModules(): DocumentModelModule<any>[];
2620
2778
  /**
2621
- * Register an additional document model on the running worker. Resolves
2622
- * when the worker replies with `model-loaded`; rejects when it replies
2623
- * with `model-load-failed` or the worker exits before answering.
2779
+ * Clear all registered modules and upgrade manifests.
2624
2780
  */
2625
- loadModel(entry: ModelManifestEntry, signal?: AbortSignal): Promise<void>;
2626
- /** True when no job is currently in flight. */
2627
- isIdle(): boolean;
2628
- /** Snapshot of the in-flight slot, or null when idle. */
2629
- getInFlight(): WorkerInFlightSnapshot | null;
2630
- }
2631
- /**
2632
- * Simple interface for executing a job.
2633
- * A JobExecutor simply takes a job and executes it - nothing more.
2634
- */
2635
- interface IJobExecutor {
2781
+ clear(): void;
2636
2782
  /**
2637
- * Execute a single job.
2638
- * @param job - The job to execute
2639
- * @returns Promise that resolves to the job result
2783
+ * Get all supported versions for a document type, sorted in ascending order.
2784
+ *
2785
+ * @param documentType The document type identifier
2786
+ * @returns Array of version numbers sorted ascending
2787
+ * @throws ModuleNotFoundError if no modules are registered for the document type
2640
2788
  */
2641
- executeJob(job: Job, signal?: AbortSignal): Promise<JobResult>;
2642
- }
2643
- /**
2644
- * Interface for managing multiple job executors.
2645
- * Listens for 'jobAvailable' events from the event bus, pulls jobs from the queue,
2646
- * and coordinates the distribution of jobs across multiple executor instances.
2647
- */
2648
- interface IJobExecutorManager {
2789
+ getSupportedVersions(documentType: string): number[];
2649
2790
  /**
2650
- * Start the executor manager.
2651
- * Begins listening for 'jobAvailable' events and dispatching to executors.
2791
+ * Get the latest (highest) version number for a document type.
2652
2792
  *
2653
- * @param numExecutors - Number of executor instances to create
2654
- * @returns Promise that resolves when the manager is started
2793
+ * @param documentType The document type identifier
2794
+ * @returns The highest version number registered for this document type
2795
+ * @throws ModuleNotFoundError if no modules are registered for the document type
2655
2796
  */
2656
- start(numExecutors: number): Promise<void>;
2797
+ getLatestVersion(documentType: string): number;
2657
2798
  /**
2658
- * Stop the executor manager.
2799
+ * Register upgrade manifests that define upgrade paths between versions.
2800
+ * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
2659
2801
  *
2660
- * @param graceful - Whether to wait for current jobs to complete
2661
- * @returns Promise that resolves when the manager is stopped
2802
+ * @param manifests Upgrade manifests to register
2803
+ * @returns Array of results, one per manifest, indicating success or failure
2662
2804
  */
2663
- stop(graceful?: boolean): Promise<void>;
2805
+ registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
2664
2806
  /**
2665
- * Get all managed executor instances.
2807
+ * Unregister upgrade manifests for the specified document types.
2808
+ * @param documentTypes The document types whose upgrade manifests should be unregistered
2809
+ * @returns true if all modules were unregistered, false if any were not found
2810
+ **/
2811
+ unregisterUpgradeManifests(...documentTypes: string[]): boolean;
2812
+ /**
2813
+ * Get the upgrade manifest for a document type.
2666
2814
  *
2667
- * @returns Array of executor instances
2815
+ * @param documentType The document type identifier
2816
+ * @returns The upgrade manifest
2817
+ * @throws ManifestNotFoundError if no manifest is registered for the document type
2668
2818
  */
2669
- getExecutors(): IJobExecutor[];
2819
+ getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
2670
2820
  /**
2671
- * Get the current status of the manager.
2821
+ * Compute the upgrade path from one version to another.
2822
+ * Returns the sequence of upgrade transitions needed.
2672
2823
  *
2673
- * @returns The current manager status
2824
+ * @param documentType The document type identifier
2825
+ * @param fromVersion The starting version
2826
+ * @param toVersion The target version
2827
+ * @returns Array of upgrade transitions in order
2828
+ * @throws DowngradeNotSupportedError if toVersion is less than fromVersion
2829
+ * @throws ManifestNotFoundError if no upgrade manifest is registered
2830
+ * @throws MissingUpgradeTransitionError if any transition in the path is missing
2674
2831
  */
2675
- getStatus(): ExecutorManagerStatus;
2832
+ computeUpgradePath(documentType: string, fromVersion: number, toVersion: number): UpgradeTransition[];
2833
+ /**
2834
+ * Get the upgrade reducer for a single-step version transition.
2835
+ *
2836
+ * @param documentType The document type identifier
2837
+ * @param fromVersion The starting version
2838
+ * @param toVersion The target version (must be fromVersion + 1)
2839
+ * @returns The upgrade reducer function
2840
+ * @throws InvalidUpgradeStepError if toVersion is not fromVersion + 1
2841
+ * @throws ManifestNotFoundError if no upgrade manifest is registered
2842
+ * @throws MissingUpgradeTransitionError if the transition is not found
2843
+ */
2844
+ getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
2676
2845
  }
2677
2846
  //#endregion
2678
- //#region src/job-tracker/interfaces.d.ts
2847
+ //#region src/cache/buffer/ring-buffer.d.ts
2679
2848
  /**
2680
- * Interface for tracking job lifecycle status.
2681
- * Maintains job state throughout execution: PENDING RUNNING COMPLETED/FAILED.
2849
+ * RingBuffer is a generic circular buffer implementation that stores a fixed number
2850
+ * of items. When the buffer is full, new items overwrite the oldest items.
2851
+ *
2852
+ * This implementation maintains O(1) time complexity for push operations and provides
2853
+ * items in chronological order (oldest to newest) via getAll().
2854
+ *
2855
+ * @template T - The type of items stored in the buffer
2682
2856
  */
2683
- interface IJobTracker {
2684
- /**
2685
- * Register a new job with PENDING status.
2686
- *
2687
- * @param jobInfo - The job information to register
2688
- */
2689
- registerJob(jobInfo: JobInfo): void;
2857
+ declare class RingBuffer<T> {
2858
+ private buffer;
2859
+ private head;
2860
+ private size;
2861
+ private capacity;
2862
+ constructor(capacity: number);
2690
2863
  /**
2691
- * Update a job's status to RUNNING.
2864
+ * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
2692
2865
  *
2693
- * @param jobId - The job ID to mark as running
2866
+ * @param item - The item to add
2694
2867
  */
2695
- markRunning(jobId: string): void;
2868
+ push(item: T): void;
2696
2869
  /**
2697
- * Mark a job as failed.
2870
+ * Returns all items in the buffer in chronological order (oldest to newest).
2698
2871
  *
2699
- * @param jobId - The job ID to mark as failed
2700
- * @param error - Error information including message and stack trace
2701
- * @param job - Optional full job object for debugging purposes
2872
+ * @returns Array of items in insertion order
2702
2873
  */
2703
- markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
2874
+ getAll(): T[];
2704
2875
  /**
2705
- * Retrieve the current status of a job.
2706
- *
2707
- * @param jobId - The job ID to query
2708
- * @returns The job information, or null if the job is not found
2876
+ * Clears all items from the buffer.
2709
2877
  */
2710
- getJobStatus(jobId: string): JobInfo | null;
2878
+ clear(): void;
2711
2879
  /**
2712
- * Shutdown the job tracker and clean up resources.
2713
- * Unsubscribes from all event bus subscriptions.
2880
+ * Gets the current number of items in the buffer.
2714
2881
  */
2715
- shutdown(): void;
2882
+ get length(): number;
2716
2883
  }
2717
2884
  //#endregion
2718
- //#region src/queue/interfaces.d.ts
2885
+ //#region src/cache/kysely-write-cache.d.ts
2886
+ type DocumentStream = {
2887
+ key: string;
2888
+ ringBuffer: RingBuffer<CachedSnapshot>;
2889
+ };
2719
2890
  /**
2720
- * Interface for a job queue that manages write operations.
2721
- * Internally organizes jobs by documentId, scope, and branch to ensure proper ordering.
2722
- * Emits events to the event bus when new jobs are available for consumption.
2891
+ * In-memory write cache with keyframe persistence for PHDocuments.
2892
+ *
2893
+ * Caches document snapshots in ring buffers with LRU eviction. On cache miss,
2894
+ * rebuilds documents from nearest keyframe or full operation history.
2895
+ *
2896
+ * **Performance Characteristics:**
2897
+ * - Cache hit: O(1) lookup in ring buffer
2898
+ * - Cold miss: O(n) where n is total operation count, or O(k) where k is operations since keyframe
2899
+ * - Warm miss: O(m) where m is operations since cached revision
2900
+ * - Eviction: O(1) for LRU tracking and removal
2901
+ *
2902
+ * **Thread Safety:**
2903
+ * Not thread-safe. Designed for single-threaded job executor environment.
2904
+ * External synchronization required for concurrent access across multiple executors.
2905
+ *
2906
+ * **Example:**
2907
+ * ```typescript
2908
+ * const cache = new KyselyWriteCache(
2909
+ * keyframeStore,
2910
+ * operationStore,
2911
+ * registry,
2912
+ * { maxDocuments: 1000, ringBufferSize: 10, keyframeInterval: 10 }
2913
+ * );
2914
+ *
2915
+ * await cache.startup();
2916
+ *
2917
+ * // Retrieve or rebuild document
2918
+ * const doc = await cache.getState(docId, docType, scope, branch, revision);
2919
+ *
2920
+ * // Cache result after job execution
2921
+ * cache.putState(docId, docType, scope, branch, newRevision, updatedDoc);
2922
+ *
2923
+ * await cache.shutdown();
2924
+ * ```
2723
2925
  */
2724
- interface IQueue {
2926
+ declare class KyselyWriteCache implements IWriteCache {
2927
+ private streams;
2928
+ private lruTracker;
2929
+ private keyframeStore;
2930
+ private operationStore;
2931
+ private registry;
2932
+ private config;
2933
+ constructor(keyframeStore: IKeyframeStore, operationStore: IOperationStore, registry: IDocumentModelRegistry, config: WriteCacheConfig);
2934
+ withScopedStores(operationStore: IOperationStore, keyframeStore: IKeyframeStore): KyselyWriteCache;
2725
2935
  /**
2726
- * Add a new job to the queue.
2727
- * Jobs are automatically organized by documentId, scope, and branch internally.
2728
- * Emits a 'jobAvailable' event to the event bus when the job is queued.
2729
- * @param job - The job to add to the queue
2730
- * @returns Promise that resolves when the job is queued
2936
+ * Initializes the write cache.
2937
+ * Currently a no-op as keyframe store lifecycle is managed externally.
2731
2938
  */
2732
- enqueue(job: Job): Promise<void>;
2939
+ startup(): Promise<void>;
2733
2940
  /**
2734
- * Get the next job to execute for a specific document/scope/branch combination.
2735
- * @param documentId - The document ID to get jobs for
2736
- * @param scope - The scope to get jobs for
2737
- * @param branch - The branch to get jobs for
2738
- * @param signal - Optional abort signal to cancel the request
2739
- * @returns Promise that resolves to the next job execution handle or null if no jobs available
2941
+ * Shuts down the write cache.
2942
+ * Currently a no-op as keyframe store lifecycle is managed externally.
2740
2943
  */
2741
- dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
2944
+ shutdown(): Promise<void>;
2742
2945
  /**
2743
- * Get the next available job from any queue.
2744
- * @param signal - Optional abort signal to cancel the request
2745
- * @returns Promise that resolves to the next job execution handle or null if no jobs available
2946
+ * Retrieves document state at a specific revision from cache or rebuilds it.
2947
+ *
2948
+ * Note: this returns a _shallow_ copy of the document.
2949
+ *
2950
+ * Cache hit path: Returns cached snapshot if available (O(1))
2951
+ * Warm miss path: Rebuilds from cached base revision + incremental ops
2952
+ * Cold miss path: Rebuilds from keyframe or from scratch using all operations
2953
+ *
2954
+ * @param documentId - The document identifier
2955
+ * @param scope - The operation scope
2956
+ * @param branch - The operation branch
2957
+ * @param targetRevision - The target revision, or undefined for newest
2958
+ * @param signal - Optional abort signal to cancel the operation
2959
+ * @returns The document at the target revision
2960
+ * @throws {Error} "Operation aborted" if signal is aborted
2961
+ * @throws {ModuleNotFoundError} If document type not registered in registry
2962
+ * @throws {Error} "Failed to rebuild document" if operation store fails
2963
+ * @throws {Error} If reducer throws during operation application
2964
+ * @throws {Error} If document serialization fails
2746
2965
  */
2747
- dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
2966
+ getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
2748
2967
  /**
2749
- * Get the next available job whose routing metadata satisfies the predicate.
2750
- * Walks ready sub-queue heads in queue insertion order, skips heads whose document
2751
- * is currently executing (same isDocumentExecuting gate as dequeueNext), and returns
2752
- * the first head for which predicate returns true.
2753
- * Returns null when paused, when nothing matches, or when the queue is empty.
2754
- * Rejects if signal is already aborted.
2755
- * @param predicate - Filter applied to JobRoutingMeta of each candidate head
2756
- * @param signal - Optional abort signal to cancel the request
2757
- * @returns Promise that resolves to the first matching job execution handle or null
2968
+ * Stores a document snapshot in the cache at a specific revision.
2969
+ *
2970
+ * The cached document is a shallow copy of the input with its operation history
2971
+ * truncated to the last operation per scope and its clipboard cleared. This keeps
2972
+ * memory use and copy costs constant regardless of operation count. Consumers of
2973
+ * getState() must not rely on the full operation history being present; the only
2974
+ * guaranteed invariant is that operations[scope].at(-1) reflects the latest
2975
+ * operation index for each scope.
2976
+ *
2977
+ * Updates LRU tracker and may evict least recently used stream if at capacity.
2978
+ * Asynchronously persists keyframes at configured intervals (fire-and-forget).
2979
+ *
2980
+ * @param documentId - The document identifier
2981
+ * @param scope - The operation scope
2982
+ * @param branch - The operation branch
2983
+ * @param revision - The revision number
2984
+ * @param document - The document to cache
2985
+ * @throws {Error} If document serialization fails
2758
2986
  */
2759
- dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
2987
+ putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, position: SnapshotPosition): void;
2988
+ private store;
2760
2989
  /**
2761
- * Get the current size of the queue for a specific document/scope/branch.
2762
- * @param documentId - The document ID
2763
- * @param scope - The scope
2764
- * @param branch - The branch
2765
- * @returns Promise that resolves to the number of jobs in the queue
2990
+ * Invalidates cached document streams.
2991
+ *
2992
+ * Supports three invalidation scopes:
2993
+ * - Document-level: invalidate(documentId) - removes all streams for document
2994
+ * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
2995
+ * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
2996
+ *
2997
+ * @param documentId - The document identifier
2998
+ * @param scope - Optional scope to narrow invalidation
2999
+ * @param branch - Optional branch to narrow invalidation (requires scope)
3000
+ * @returns The number of streams evicted
2766
3001
  */
2767
- size(documentId: string, scope: string, branch: string): Promise<number>;
2768
- /**
2769
- * Get the total size of all queues.
2770
- * @returns Promise that resolves to the total number of jobs across all queues
2771
- */
2772
- totalSize(): Promise<number>;
2773
- /**
2774
- * Remove a specific job from the queue.
2775
- * @param jobId - The ID of the job to remove
2776
- * @returns Promise that resolves to true if job was removed, false if not found
2777
- */
2778
- remove(jobId: string): Promise<boolean>;
2779
- /**
2780
- * Clear all jobs for a specific document/scope/branch combination.
2781
- * @param documentId - The document ID
2782
- * @param scope - The scope
2783
- * @param branch - The branch
2784
- * @returns Promise that resolves when the queue is cleared
2785
- */
2786
- clear(documentId: string, scope: string, branch: string): Promise<void>;
2787
- /**
2788
- * Clear all jobs from all queues.
2789
- * @returns Promise that resolves when all queues are cleared
2790
- */
2791
- clearAll(): Promise<void>;
2792
- /**
2793
- * Check if there are any jobs in the queue.
2794
- * @returns Promise that resolves to true if there are jobs, false otherwise
2795
- */
2796
- hasJobs(): Promise<boolean>;
3002
+ invalidate(documentId: string, scope?: string, branch?: string): number;
2797
3003
  /**
2798
- * Mark a job as completed.
2799
- * @param jobId - The ID of the job to mark as completed
2800
- * @returns Promise that resolves when the job is marked as completed
3004
+ * Clears the entire cache, removing all cached document streams.
3005
+ * Resets LRU tracking state. This operation always succeeds.
2801
3006
  */
2802
- completeJob(jobId: string): Promise<void>;
3007
+ clear(): void;
2803
3008
  /**
2804
- * Mark a job as failed.
2805
- * @param jobId - The ID of the job to mark as failed
2806
- * @param error - Optional error information
2807
- * @returns Promise that resolves when the job is marked as failed
3009
+ * Retrieves a specific stream for a document. Exposed on the implementation
3010
+ * for testing, but not on the interface.
3011
+ *
3012
+ * @internal
2808
3013
  */
2809
- failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
3014
+ getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
3015
+ private findNearestKeyframe;
3016
+ private coldMissRebuild;
2810
3017
  /**
2811
- * Retry a failed job.
2812
- * @param jobId - The ID of the job to retry
2813
- * @param error - Optional error information from the failure
2814
- * @param accounting - Whether the attempt counts against the job's retry
2815
- * limit; defaults to {@link RetryAccounting.CountAgainstLimit}
2816
- * @returns Promise that resolves when the job is requeued for retry
3018
+ * Copies the current document revisions onto the document. Overwrites the
3019
+ * requested scope revision with the target revision, if provided.
2817
3020
  */
2818
- retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
3021
+ private stampRevisions;
3022
+ /** The stored operation at `index`, or undefined if it is no longer there. */
3023
+ private operationAt;
2819
3024
  /**
2820
- * Returns true if and only if all jobs have been resolved.
3025
+ * Resolves which module version to use for a given operation in phase 2.
3026
+ *
3027
+ * Uses the validated-upgrade boundary rules from D7:
3028
+ * - If `input.revision` is present: op.index < revision[scope] → before the upgrade boundary
3029
+ * - Otherwise: timestamp fallback
3030
+ * - Falls back to final module version when neither is decidable
2821
3031
  */
2822
- get isDrained(): boolean;
3032
+ private resolveModuleVersionForOp;
3033
+ private warmMissRebuild;
3034
+ private findNearestOlderSnapshot;
3035
+ private makeStreamKey;
3036
+ private getOrCreateStream;
3037
+ private isKeyframeRevision;
3038
+ }
3039
+ //#endregion
3040
+ //#region src/storage/kysely/store.d.ts
3041
+ declare class KyselyOperationStore implements IOperationStore {
3042
+ private db;
3043
+ private trx?;
3044
+ constructor(db: Kysely<Database$1>);
3045
+ private get queryExecutor();
3046
+ withTransaction(trx: Transaction<Database$1>): KyselyOperationStore;
3047
+ apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal, condition?: AppendCondition): Promise<Operation[]>;
3048
+ private resolveUniqueConstraint;
3049
+ private executeApply;
2823
3050
  /**
2824
- * Blocks the queue from accepting new jobs.
2825
- * @param onDrained - Optional callback to call when the queue is drained
3051
+ * Locks the written stream and every read-set stream, in sorted key order
3052
+ * so that overlapping concurrent appends serialize rather than deadlock.
3053
+ * The locks are still taken one row at a time, so the query preserves that
3054
+ * order. It must stay separate from the guarded insert, which would
3055
+ * otherwise read a snapshot taken before the locks were held.
2826
3056
  */
2827
- block(onDrained?: () => void): void;
3057
+ private acquireStreamLocks;
2828
3058
  /**
2829
- * Unblocks the queue from accepting new jobs.
3059
+ * Inserts the staged operations with the condition compiled in as a WHERE
3060
+ * NOT EXISTS guard, making the check and the append one statement. Returns
3061
+ * the rows inserted; zero means the guard failed and nothing was written.
2830
3062
  */
2831
- unblock(): void;
3063
+ private insertGuarded;
3064
+ private findIdempotentReplay;
3065
+ getSince(documentId: string, scope: string, branch: string, revision: number, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
3066
+ getSinceId(id: number, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<OperationWithContext$1>>;
3067
+ getConflicting(documentId: string, scope: string, branch: string, minTimestamp: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
3068
+ getRevisions(documentId: string, branch: string, signal?: AbortSignal): Promise<DocumentRevisions>;
3069
+ private rowToOperation;
3070
+ private rowToOperationWithContext;
2832
3071
  }
2833
3072
  //#endregion
2834
- //#region src/read-models/types.d.ts
2835
- interface ViewStateTable {
2836
- readModelId: string;
2837
- lastOrdinal: number;
2838
- lastOperationTimestamp: Generated<Date>;
2839
- }
2840
- interface DocumentSnapshotTable {
2841
- id: Generated<string>;
2842
- documentId: string;
2843
- slug: string | null;
2844
- name: string | null;
2845
- scope: string;
2846
- branch: string;
2847
- content: unknown;
2848
- documentType: string;
2849
- lastOperationIndex: number;
2850
- lastOperationHash: string;
2851
- lastUpdatedAt: Generated<Date>;
2852
- snapshotVersion: Generated<number>;
2853
- identifiers: unknown;
2854
- metadata: unknown;
2855
- isDeleted: Generated<boolean>;
2856
- deletedAt: Date | null;
2857
- }
2858
- interface SlugMappingTable {
2859
- slug: string;
2860
- documentId: string;
2861
- scope: string;
2862
- branch: string;
2863
- createdAt: Generated<Date>;
2864
- updatedAt: Generated<Date>;
3073
+ //#region src/storage/kysely/keyframe-store.d.ts
3074
+ declare class KyselyKeyframeStore implements IKeyframeStore {
3075
+ private db;
3076
+ private trx?;
3077
+ constructor(db: Kysely<Database$1>);
3078
+ private get queryExecutor();
3079
+ withTransaction(trx: Transaction<Database$1>): KyselyKeyframeStore;
3080
+ putKeyframe(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, signal?: AbortSignal): Promise<void>;
3081
+ findNearestKeyframe(documentId: string, scope: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<{
3082
+ revision: number;
3083
+ document: PHDocument;
3084
+ } | undefined>;
3085
+ listKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<Array<{
3086
+ scope: string;
3087
+ branch: string;
3088
+ revision: number;
3089
+ document: PHDocument;
3090
+ }>>;
3091
+ deleteKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<number>;
2865
3092
  }
2866
- interface ProcessorCursorTable {
2867
- processorId: string;
2868
- factoryId: string;
2869
- driveId: string;
2870
- processorIndex: number;
2871
- lastOrdinal: Generated<number>;
2872
- status: Generated<string>;
2873
- lastError: string | null;
2874
- lastErrorTimestamp: Date | null;
2875
- createdAt: Generated<Date>;
2876
- updatedAt: Generated<Date>;
3093
+ //#endregion
3094
+ //#region src/executor/execution-scope.d.ts
3095
+ interface ExecutionStores {
3096
+ operationStore: IOperationStore;
3097
+ operationIndex: IOperationIndex;
3098
+ writeCache: IWriteCache;
3099
+ documentMetaCache: IDocumentMetaCache;
3100
+ collectionMembershipCache: ICollectionMembershipCache;
2877
3101
  }
2878
- interface DocumentViewDatabase {
2879
- ViewState: ViewStateTable;
2880
- DocumentSnapshot: DocumentSnapshotTable;
2881
- SlugMapping: SlugMappingTable;
2882
- ProcessorCursor: ProcessorCursorTable;
3102
+ interface IExecutionScope {
3103
+ run<T>(fn: (stores: ExecutionStores) => Promise<T>, signal?: AbortSignal): Promise<T>;
2883
3104
  }
2884
- type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;
2885
3105
  //#endregion
2886
- //#region src/core/model-sources.d.ts
2887
- /** An importable file holding one or more document-model exports. */
2888
- type FileModelSource = {
2889
- filePath: string;
2890
- exportName?: string;
2891
- };
2892
- /** An importable package specifier holding one or more document-model exports. */
2893
- type PackageModelSource = {
2894
- packageName: string;
2895
- subpath?: string;
2896
- exportName?: string;
2897
- };
3106
+ //#region src/executor/types.d.ts
2898
3107
  /**
2899
- * A source of document models: a live module, an importable file, or an
2900
- * importable package. File and package sources can cross a worker-thread
2901
- * boundary (workers re-import them); a live module cannot.
3108
+ * Represents the result of a job execution
2902
3109
  */
2903
- type DocumentModelSource = DocumentModelModule<any> | FileModelSource | PackageModelSource;
2904
- //#endregion
2905
- //#region src/registry/interfaces.d.ts
2906
- type RegistrationResult<T> = {
2907
- status: "success";
2908
- item: T;
2909
- } | {
2910
- status: "error";
2911
- item: T;
2912
- error: Error;
3110
+ type JobResult = {
3111
+ /** The job that was executed */job: Job; /** Whether the job executed successfully */
3112
+ success: boolean; /** Error if the job failed */
3113
+ error?: Error; /** The operations generated from the actions (if successful) */
3114
+ operations?: Operation[];
3115
+ /**
3116
+ * Operations with context (includes ephemeral resultingState).
3117
+ * Used for emitting to IDocumentView via event bus.
3118
+ */
3119
+ operationsWithContext?: OperationWithContext$1[]; /** Timestamp when the job execution completed */
3120
+ completedAt?: string; /** Duration of job execution in milliseconds */
3121
+ duration?: number; /** Any additional metadata from the execution */
3122
+ metadata?: Record<string, any>;
2913
3123
  };
2914
3124
  /**
2915
- * Loader that asynchronously resolves a document type to a
2916
- * {@link DocumentModelSource}. Used by the queue to gate CREATE_DOCUMENT
2917
- * jobs until the required model is available in the registry.
3125
+ * Enforcement the reactor performs, each off by default.
2918
3126
  *
2919
- * Return an importable source ({ filePath } or { packageName }) whenever
2920
- * possible: the resolver registers the resolved models on the host registry
2921
- * and broadcasts importable sources to executor workers. A live
2922
- * DocumentModelModule is also valid but host-only — it cannot cross a
2923
- * worker-thread boundary, so worker pools will not receive it.
3127
+ * An evaluation made while replaying is part of the document's history, so two
3128
+ * reactors that share documents and disagree on these diverge. A flag is turned
3129
+ * on for a set of reactors that sync with each other, not for one node.
2924
3130
  */
2925
- interface IDocumentModelLoader {
2926
- load(documentType: string): Promise<DocumentModelSource>;
2927
- }
3131
+ type ReactorFeatureFlags = {
3132
+ /**
3133
+ * Decide whether an operation may be admitted by building a decision model
3134
+ * over the document stream, rather than reading the deleted flag from the
3135
+ * document meta cache. Deletion then takes effect from the deleting
3136
+ * operation's position rather than for the whole document.
3137
+ */
3138
+ documentDecisions: boolean;
3139
+ };
2928
3140
  /**
2929
- * Registry for managing document model modules.
2930
- * Provides centralized access to document models' reducers, utils, and specifications.
2931
- * Supports version-aware module storage and upgrade manifest management.
3141
+ * Configuration options for the job executor
2932
3142
  */
2933
- interface IDocumentModelRegistry {
2934
- /**
2935
- * Register multiple modules at once.
2936
- * Modules without a version field default to version 1.
2937
- * Invalid or duplicate modules are skipped without breaking registration of the remaining modules.
2938
- *
2939
- * @param modules Document model modules to register
2940
- * @returns Array of results, one per module, indicating success or failure
3143
+ type JobExecutorConfig = {
3144
+ /** Feature flags; anything unset is off. */featureFlags?: Partial<ReactorFeatureFlags>; /** Maximum number of conflicting operations to skip when reshuffling. */
3145
+ maxSkipThreshold?: number; /** Maximum number of concurrent jobs to execute */
3146
+ maxConcurrency?: number; /** Maximum time in milliseconds a job can run before being considered timed out */
3147
+ jobTimeoutMs?: number; /** Base delay in milliseconds for exponential backoff retries */
3148
+ retryBaseDelayMs?: number; /** Maximum delay in milliseconds for exponential backoff retries */
3149
+ retryMaxDelayMs?: number;
3150
+ /** Maximum elapsed milliseconds before yielding to the main thread between actions.
3151
+ * Keeps the UI responsive when processing large batches. */
3152
+ yieldDeadlineMs?: number;
3153
+ };
3154
+ /**
3155
+ * Event types for the job executor
3156
+ */
3157
+ declare const JobExecutorEventTypes: {
3158
+ readonly JOB_STARTED: 20000;
3159
+ readonly JOB_COMPLETED: 20001;
3160
+ readonly JOB_FAILED: 20002;
3161
+ readonly EXECUTOR_STARTED: 20003;
3162
+ readonly EXECUTOR_STOPPED: 20004;
3163
+ };
3164
+ /**
3165
+ * Event data for job execution events
3166
+ */
3167
+ type JobStartedEvent = {
3168
+ job: Job;
3169
+ startedAt: string;
3170
+ /**
3171
+ * Identifier of the executor that took the job. For the worker pool this is
3172
+ * the thread-worker id (e.g. "reactor-worker-3"); for the in-process simple
3173
+ * manager it is "in-process-<index>". Optional for backwards compatibility
3174
+ * with consumers built before the field was added.
2941
3175
  */
2942
- registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
3176
+ workerId?: string;
3177
+ };
3178
+ type JobCompletedEvent = {
3179
+ job: Job;
3180
+ result: JobResult; /** See {@link JobStartedEvent.workerId}. */
3181
+ workerId?: string;
3182
+ };
3183
+ type JobFailedEvent = {
3184
+ job: Job;
3185
+ error: string;
3186
+ willRetry: boolean;
3187
+ retryCount: number; /** See {@link JobStartedEvent.workerId}. */
3188
+ workerId?: string;
3189
+ };
3190
+ type ExecutorStartedEvent = {
3191
+ config: JobExecutorConfig;
3192
+ startedAt: string;
3193
+ };
3194
+ type ExecutorStoppedEvent = {
3195
+ stoppedAt: string;
3196
+ graceful: boolean;
3197
+ };
3198
+ /**
3199
+ * Status information for the job executor manager
3200
+ */
3201
+ type ExecutorManagerStatus = {
3202
+ /** Whether the manager is currently running */isRunning: boolean; /** Number of executor instances managed */
3203
+ numExecutors: number; /** Number of jobs currently being processed */
3204
+ activeJobs: number; /** Total number of jobs processed since start */
3205
+ totalJobsProcessed: number;
3206
+ };
3207
+ //#endregion
3208
+ //#region src/executor/interfaces.d.ts
3209
+ /**
3210
+ * Snapshot of the single in-flight slot maintained by an {@link IExecutorWorker}.
3211
+ */
3212
+ type WorkerInFlightSnapshot = {
3213
+ correlationId: string;
3214
+ jobId: string;
3215
+ };
3216
+ /**
3217
+ * Outcome of a worker-side job execution.
3218
+ *
3219
+ * `result` mirrors the in-process `JobResult` exactly. `writeReady` carries
3220
+ * the operations + jobMeta the parent needs to emit `JOB_WRITE_READY`, and is
3221
+ * present only when the worker produced operations. It is absent on failure
3222
+ * and on success-with-no-operations.
3223
+ */
3224
+ type WorkerExecutionOutcome = {
3225
+ result: JobResult;
3226
+ writeReady?: JobWriteReadyPayload;
3227
+ };
3228
+ /**
3229
+ * Parent-side handle for a single executor worker.
3230
+ *
3231
+ * Implementations wrap an IPC transport (worker_threads, child_process, or a
3232
+ * test fake) and expose a transport-agnostic surface that the worker-pool
3233
+ * manager uses to dispatch jobs. The handle owns one worker's lifecycle
3234
+ * (`start` -> `execute`* -> `shutdown`) and bounds its in-flight map to a
3235
+ * single entry; `SimpleJobExecutor` is single-threaded inside the worker, so
3236
+ * concurrent dispatches would race its caches.
3237
+ */
3238
+ interface IExecutorWorker {
3239
+ /** Stable identifier of the worker (mirrors `InitMessage.workerId`). */
3240
+ readonly workerId: string;
3241
+ /** Zero-based index within the pool, used for sticky routing. */
3242
+ readonly index: number;
2943
3243
  /**
2944
- * Unregister all versions of the specified document types.
2945
- *
2946
- * @param documentTypes The document types to unregister
2947
- * @returns true if all modules were unregistered, false if any were not found
3244
+ * Spawn the worker (if not already started), send the `init` payload and
3245
+ * resolve when the worker replies with `ready`.
2948
3246
  */
2949
- unregisterModules(...documentTypes: string[]): boolean;
3247
+ start(): Promise<void>;
2950
3248
  /**
2951
- * Get a specific document model module by document type and optional version.
2952
- * If version is not specified, returns the latest version.
2953
- *
2954
- * @param documentType The document type identifier
2955
- * @param version Optional version number to retrieve
2956
- * @returns The document model module
2957
- * @throws ModuleNotFoundError if the document type or version is not registered
3249
+ * Dispatch a job to the worker and resolve with its outcome — the
3250
+ * `JobResult` and, on success-with-operations, a `writeReady` payload
3251
+ * the parent will enrich and re-emit. Rejects with a transport-level
3252
+ * error if the worker exits, aborts, or times out before producing a
3253
+ * result.
2958
3254
  */
2959
- getModule(documentType: string, version?: number): DocumentModelModule<any>;
3255
+ execute(job: Job, signal?: AbortSignal): Promise<WorkerExecutionOutcome>;
2960
3256
  /**
2961
- * Get all registered document model modules.
2962
- *
2963
- * @returns Array of all registered modules
3257
+ * Request cancellation of the in-flight job (if any). The handle posts an
3258
+ * `abort` message; if the worker fails to reply within its grace window it
3259
+ * is force-terminated.
2964
3260
  */
2965
- getAllModules(): DocumentModelModule<any>[];
3261
+ abort(correlationId: string, reason?: string): void;
2966
3262
  /**
2967
- * Clear all registered modules and upgrade manifests.
3263
+ * Stop the worker. When `graceful` is true the handle waits for the
3264
+ * in-flight job to settle (up to `graceMs`) before terminating; otherwise
3265
+ * the worker is terminated immediately.
2968
3266
  */
2969
- clear(): void;
3267
+ shutdown(graceful: boolean, graceMs?: number): Promise<void>;
2970
3268
  /**
2971
- * Get all supported versions for a document type, sorted in ascending order.
2972
- *
2973
- * @param documentType The document type identifier
2974
- * @returns Array of version numbers sorted ascending
2975
- * @throws ModuleNotFoundError if no modules are registered for the document type
3269
+ * Register an additional document model on the running worker. Resolves
3270
+ * when the worker replies with `model-loaded`; rejects when it replies
3271
+ * with `model-load-failed` or the worker exits before answering.
2976
3272
  */
2977
- getSupportedVersions(documentType: string): number[];
3273
+ loadModel(entry: ModelManifestEntry, signal?: AbortSignal): Promise<void>;
3274
+ /** True when no job is currently in flight. */
3275
+ isIdle(): boolean;
3276
+ /** Snapshot of the in-flight slot, or null when idle. */
3277
+ getInFlight(): WorkerInFlightSnapshot | null;
3278
+ }
3279
+ /**
3280
+ * Simple interface for executing a job.
3281
+ * A JobExecutor simply takes a job and executes it - nothing more.
3282
+ */
3283
+ interface IJobExecutor {
2978
3284
  /**
2979
- * Get the latest (highest) version number for a document type.
2980
- *
2981
- * @param documentType The document type identifier
2982
- * @returns The highest version number registered for this document type
2983
- * @throws ModuleNotFoundError if no modules are registered for the document type
3285
+ * Execute a single job.
3286
+ * @param job - The job to execute
3287
+ * @returns Promise that resolves to the job result
2984
3288
  */
2985
- getLatestVersion(documentType: string): number;
3289
+ executeJob(job: Job, signal?: AbortSignal): Promise<JobResult>;
3290
+ }
3291
+ /**
3292
+ * Interface for managing multiple job executors.
3293
+ * Listens for 'jobAvailable' events from the event bus, pulls jobs from the queue,
3294
+ * and coordinates the distribution of jobs across multiple executor instances.
3295
+ */
3296
+ interface IJobExecutorManager {
2986
3297
  /**
2987
- * Register upgrade manifests that define upgrade paths between versions.
2988
- * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
3298
+ * Start the executor manager.
3299
+ * Begins listening for 'jobAvailable' events and dispatching to executors.
2989
3300
  *
2990
- * @param manifests Upgrade manifests to register
2991
- * @returns Array of results, one per manifest, indicating success or failure
3301
+ * @param numExecutors - Number of executor instances to create
3302
+ * @returns Promise that resolves when the manager is started
2992
3303
  */
2993
- registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
2994
- /**
2995
- * Unregister upgrade manifests for the specified document types.
2996
- * @param documentTypes The document types whose upgrade manifests should be unregistered
2997
- * @returns true if all modules were unregistered, false if any were not found
2998
- **/
2999
- unregisterUpgradeManifests(...documentTypes: string[]): boolean;
3304
+ start(numExecutors: number): Promise<void>;
3000
3305
  /**
3001
- * Get the upgrade manifest for a document type.
3306
+ * Stop the executor manager.
3002
3307
  *
3003
- * @param documentType The document type identifier
3004
- * @returns The upgrade manifest
3005
- * @throws ManifestNotFoundError if no manifest is registered for the document type
3308
+ * @param graceful - Whether to wait for current jobs to complete
3309
+ * @returns Promise that resolves when the manager is stopped
3006
3310
  */
3007
- getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
3311
+ stop(graceful?: boolean): Promise<void>;
3008
3312
  /**
3009
- * Compute the upgrade path from one version to another.
3010
- * Returns the sequence of upgrade transitions needed.
3313
+ * Get all managed executor instances.
3011
3314
  *
3012
- * @param documentType The document type identifier
3013
- * @param fromVersion The starting version
3014
- * @param toVersion The target version
3015
- * @returns Array of upgrade transitions in order
3016
- * @throws DowngradeNotSupportedError if toVersion is less than fromVersion
3017
- * @throws ManifestNotFoundError if no upgrade manifest is registered
3018
- * @throws MissingUpgradeTransitionError if any transition in the path is missing
3315
+ * @returns Array of executor instances
3019
3316
  */
3020
- computeUpgradePath(documentType: string, fromVersion: number, toVersion: number): UpgradeTransition[];
3317
+ getExecutors(): IJobExecutor[];
3021
3318
  /**
3022
- * Get the upgrade reducer for a single-step version transition.
3319
+ * Get the current status of the manager.
3023
3320
  *
3024
- * @param documentType The document type identifier
3025
- * @param fromVersion The starting version
3026
- * @param toVersion The target version (must be fromVersion + 1)
3027
- * @returns The upgrade reducer function
3028
- * @throws InvalidUpgradeStepError if toVersion is not fromVersion + 1
3029
- * @throws ManifestNotFoundError if no upgrade manifest is registered
3030
- * @throws MissingUpgradeTransitionError if the transition is not found
3321
+ * @returns The current manager status
3031
3322
  */
3032
- getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
3323
+ getStatus(): ExecutorManagerStatus;
3033
3324
  }
3034
3325
  //#endregion
3035
- //#region src/shared/consistency-tracker.d.ts
3036
- interface IConsistencyTracker {
3037
- /**
3038
- * Updates the tracker with new operation indexes.
3039
- * When multiple coordinates have the same key, keeps the highest operationIndex.
3040
- * Resolves any pending waiters whose coordinates are now satisfied.
3041
- */
3042
- update(coordinates: ConsistencyCoordinate[]): void;
3326
+ //#region src/job-tracker/interfaces.d.ts
3327
+ /**
3328
+ * Interface for tracking job lifecycle status.
3329
+ * Maintains job state throughout execution: PENDING → RUNNING → COMPLETED/FAILED.
3330
+ */
3331
+ interface IJobTracker {
3043
3332
  /**
3044
- * Returns the latest operation index for a given key, or undefined if not tracked.
3333
+ * Register a new job with PENDING status.
3334
+ *
3335
+ * @param jobInfo - The job information to register
3045
3336
  */
3046
- getLatest(key: ConsistencyKey): number | undefined;
3337
+ registerJob(jobInfo: JobInfo): void;
3047
3338
  /**
3048
- * Returns a promise that resolves when all coordinates are satisfied.
3049
- * Rejects if the timeout is reached or the signal is aborted.
3339
+ * Update a job's status to RUNNING.
3340
+ *
3341
+ * @param jobId - The job ID to mark as running
3050
3342
  */
3051
- waitFor(coordinates: ConsistencyCoordinate[], timeoutMs?: number, signal?: AbortSignal): Promise<void>;
3343
+ markRunning(jobId: string): void;
3344
+ /**
3345
+ * Mark a job as failed.
3346
+ *
3347
+ * @param jobId - The job ID to mark as failed
3348
+ * @param error - Error information including message and stack trace
3349
+ * @param job - Optional full job object for debugging purposes
3350
+ */
3351
+ markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
3352
+ /**
3353
+ * Retrieve the current status of a job.
3354
+ *
3355
+ * @param jobId - The job ID to query
3356
+ * @returns The job information, or null if the job is not found
3357
+ */
3358
+ getJobStatus(jobId: string): JobInfo | null;
3359
+ /**
3360
+ * Shutdown the job tracker and clean up resources.
3361
+ * Unsubscribes from all event bus subscriptions.
3362
+ */
3363
+ shutdown(): void;
3364
+ }
3365
+ //#endregion
3366
+ //#region src/queue/interfaces.d.ts
3367
+ /**
3368
+ * Interface for a job queue that manages write operations.
3369
+ * Internally organizes jobs by documentId, scope, and branch to ensure proper ordering.
3370
+ * Emits events to the event bus when new jobs are available for consumption.
3371
+ */
3372
+ interface IQueue {
3373
+ /**
3374
+ * Add a new job to the queue.
3375
+ * Jobs are automatically organized by documentId, scope, and branch internally.
3376
+ * Emits a 'jobAvailable' event to the event bus when the job is queued.
3377
+ * @param job - The job to add to the queue
3378
+ * @returns Promise that resolves when the job is queued
3379
+ */
3380
+ enqueue(job: Job): Promise<void>;
3381
+ /**
3382
+ * Get the next job to execute for a specific document/scope/branch combination.
3383
+ * @param documentId - The document ID to get jobs for
3384
+ * @param scope - The scope to get jobs for
3385
+ * @param branch - The branch to get jobs for
3386
+ * @param signal - Optional abort signal to cancel the request
3387
+ * @returns Promise that resolves to the next job execution handle or null if no jobs available
3388
+ */
3389
+ dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
3390
+ /**
3391
+ * Get the next available job from any queue.
3392
+ * @param signal - Optional abort signal to cancel the request
3393
+ * @returns Promise that resolves to the next job execution handle or null if no jobs available
3394
+ */
3395
+ dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
3396
+ /**
3397
+ * Get the next available job whose routing metadata satisfies the predicate.
3398
+ * Walks ready sub-queue heads in queue insertion order, skips heads whose document
3399
+ * is currently executing (same isDocumentExecuting gate as dequeueNext), and returns
3400
+ * the first head for which predicate returns true.
3401
+ * Returns null when paused, when nothing matches, or when the queue is empty.
3402
+ * Rejects if signal is already aborted.
3403
+ * @param predicate - Filter applied to JobRoutingMeta of each candidate head
3404
+ * @param signal - Optional abort signal to cancel the request
3405
+ * @returns Promise that resolves to the first matching job execution handle or null
3406
+ */
3407
+ dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
3408
+ /**
3409
+ * Get the current size of the queue for a specific document/scope/branch.
3410
+ * @param documentId - The document ID
3411
+ * @param scope - The scope
3412
+ * @param branch - The branch
3413
+ * @returns Promise that resolves to the number of jobs in the queue
3414
+ */
3415
+ size(documentId: string, scope: string, branch: string): Promise<number>;
3416
+ /**
3417
+ * Get the total size of all queues.
3418
+ * @returns Promise that resolves to the total number of jobs across all queues
3419
+ */
3420
+ totalSize(): Promise<number>;
3421
+ /**
3422
+ * Remove a specific job from the queue.
3423
+ * @param jobId - The ID of the job to remove
3424
+ * @returns Promise that resolves to true if job was removed, false if not found
3425
+ */
3426
+ remove(jobId: string): Promise<boolean>;
3427
+ /**
3428
+ * Clear all jobs for a specific document/scope/branch combination.
3429
+ * @param documentId - The document ID
3430
+ * @param scope - The scope
3431
+ * @param branch - The branch
3432
+ * @returns Promise that resolves when the queue is cleared
3433
+ */
3434
+ clear(documentId: string, scope: string, branch: string): Promise<void>;
3435
+ /**
3436
+ * Clear all jobs from all queues.
3437
+ * @returns Promise that resolves when all queues are cleared
3438
+ */
3439
+ clearAll(): Promise<void>;
3440
+ /**
3441
+ * Check if there are any jobs in the queue.
3442
+ * @returns Promise that resolves to true if there are jobs, false otherwise
3443
+ */
3444
+ hasJobs(): Promise<boolean>;
3445
+ /**
3446
+ * Mark a job as completed.
3447
+ * @param jobId - The ID of the job to mark as completed
3448
+ * @returns Promise that resolves when the job is marked as completed
3449
+ */
3450
+ completeJob(jobId: string): Promise<void>;
3451
+ /**
3452
+ * Mark a job as failed.
3453
+ * @param jobId - The ID of the job to mark as failed
3454
+ * @param error - Optional error information
3455
+ * @returns Promise that resolves when the job is marked as failed
3456
+ */
3457
+ failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
3458
+ /**
3459
+ * Retry a failed job.
3460
+ * @param jobId - The ID of the job to retry
3461
+ * @param error - Optional error information from the failure
3462
+ * @param accounting - Whether the attempt counts against the job's retry
3463
+ * limit; defaults to {@link RetryAccounting.CountAgainstLimit}
3464
+ * @returns Promise that resolves when the job is requeued for retry
3465
+ */
3466
+ retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
3467
+ /**
3468
+ * Returns true if and only if all jobs have been resolved.
3469
+ */
3470
+ get isDrained(): boolean;
3471
+ /**
3472
+ * Blocks the queue from accepting new jobs.
3473
+ * @param onDrained - Optional callback to call when the queue is drained
3474
+ */
3475
+ block(onDrained?: () => void): void;
3476
+ /**
3477
+ * Unblocks the queue from accepting new jobs.
3478
+ */
3479
+ unblock(): void;
3480
+ }
3481
+ //#endregion
3482
+ //#region src/read-models/types.d.ts
3483
+ interface ViewStateTable {
3484
+ readModelId: string;
3485
+ lastOrdinal: number;
3486
+ lastOperationTimestamp: Generated<Date>;
3487
+ }
3488
+ interface DocumentSnapshotTable {
3489
+ id: Generated<string>;
3490
+ documentId: string;
3491
+ slug: string | null;
3492
+ name: string | null;
3493
+ scope: string;
3494
+ branch: string;
3495
+ content: unknown;
3496
+ documentType: string;
3497
+ lastOperationIndex: number;
3498
+ lastOperationHash: string;
3499
+ lastUpdatedAt: Generated<Date>;
3500
+ snapshotVersion: Generated<number>;
3501
+ identifiers: unknown;
3502
+ metadata: unknown;
3503
+ isDeleted: Generated<boolean>;
3504
+ deletedAt: Date | null;
3505
+ }
3506
+ interface SlugMappingTable {
3507
+ slug: string;
3508
+ documentId: string;
3509
+ scope: string;
3510
+ branch: string;
3511
+ createdAt: Generated<Date>;
3512
+ updatedAt: Generated<Date>;
3513
+ }
3514
+ interface ProcessorCursorTable {
3515
+ processorId: string;
3516
+ factoryId: string;
3517
+ driveId: string;
3518
+ processorIndex: number;
3519
+ lastOrdinal: Generated<number>;
3520
+ status: Generated<string>;
3521
+ lastError: string | null;
3522
+ lastErrorTimestamp: Date | null;
3523
+ createdAt: Generated<Date>;
3524
+ updatedAt: Generated<Date>;
3525
+ }
3526
+ interface DocumentViewDatabase {
3527
+ ViewState: ViewStateTable;
3528
+ DocumentSnapshot: DocumentSnapshotTable;
3529
+ SlugMapping: SlugMappingTable;
3530
+ ProcessorCursor: ProcessorCursorTable;
3531
+ }
3532
+ type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;
3533
+ //#endregion
3534
+ //#region src/shared/consistency-tracker.d.ts
3535
+ interface IConsistencyTracker {
3536
+ /**
3537
+ * Updates the tracker with new operation indexes.
3538
+ * When multiple coordinates have the same key, keeps the highest operationIndex.
3539
+ * Resolves any pending waiters whose coordinates are now satisfied.
3540
+ */
3541
+ update(coordinates: ConsistencyCoordinate[]): void;
3542
+ /**
3543
+ * Returns the latest operation index for a given key, or undefined if not tracked.
3544
+ */
3545
+ getLatest(key: ConsistencyKey): number | undefined;
3546
+ /**
3547
+ * Returns a promise that resolves when all coordinates are satisfied.
3548
+ * Rejects if the timeout is reached or the signal is aborted.
3549
+ */
3550
+ waitFor(coordinates: ConsistencyCoordinate[], timeoutMs?: number, signal?: AbortSignal): Promise<void>;
3052
3551
  /**
3053
3552
  * Returns a serializable snapshot of the current state.
3054
3553
  */
@@ -3080,136 +3579,6 @@ declare class ConsistencyTracker implements IConsistencyTracker {
3080
3579
  private removeWaiter;
3081
3580
  }
3082
3581
  //#endregion
3083
- //#region src/storage/kysely/types.d.ts
3084
- interface OperationTable {
3085
- id: Generated<number>;
3086
- jobId: string;
3087
- opId: string;
3088
- prevOpId: string;
3089
- writeTimestampUtcMs: Generated<Date>;
3090
- documentId: string;
3091
- documentType: string;
3092
- scope: string;
3093
- branch: string;
3094
- timestampUtcMs: Date;
3095
- index: number;
3096
- action: unknown;
3097
- skip: number;
3098
- error?: string | null;
3099
- deniedReason?: string | null;
3100
- hash: string;
3101
- }
3102
- interface KeyframeTable {
3103
- id: Generated<number>;
3104
- documentId: string;
3105
- documentType: string;
3106
- scope: string;
3107
- branch: string;
3108
- revision: number;
3109
- document: unknown;
3110
- createdAt: Generated<Date>;
3111
- }
3112
- interface DocumentCollectionTable {
3113
- documentId: string;
3114
- collectionId: string;
3115
- joinedOrdinal: bigint;
3116
- leftOrdinal: bigint | null;
3117
- }
3118
- interface OperationIndexOperationTable {
3119
- ordinal: Generated<number>;
3120
- opId: string;
3121
- documentId: string;
3122
- documentType: string;
3123
- scope: string;
3124
- branch: string;
3125
- timestampUtcMs: string;
3126
- writeTimestampUtcMs: Generated<Date>;
3127
- index: number;
3128
- skip: number;
3129
- hash: string;
3130
- action: unknown;
3131
- deniedReason?: string | null;
3132
- sourceRemote: Generated<string>;
3133
- }
3134
- interface SyncRemoteTable {
3135
- name: string;
3136
- collection_id: string;
3137
- channel_type: string;
3138
- channel_id: string;
3139
- remote_name: string;
3140
- channel_parameters: unknown;
3141
- filter_document_ids: unknown;
3142
- filter_scopes: unknown;
3143
- filter_branch: string;
3144
- push_state: string;
3145
- push_last_success_utc_ms: string | null;
3146
- push_last_failure_utc_ms: string | null;
3147
- push_failure_count: number;
3148
- pull_state: string;
3149
- pull_last_success_utc_ms: string | null;
3150
- pull_last_failure_utc_ms: string | null;
3151
- pull_failure_count: number;
3152
- created_at: Generated<Date>;
3153
- updated_at: Generated<Date>;
3154
- }
3155
- interface SyncCursorTable {
3156
- remote_name: string;
3157
- cursor_type: string;
3158
- cursor_ordinal: bigint;
3159
- last_synced_at_utc_ms: string | null;
3160
- updated_at: Generated<Date>;
3161
- }
3162
- /**
3163
- * Kysely table definition for the `sync_dead_letters` table.
3164
- */
3165
- interface SyncDeadLetterTable {
3166
- ordinal: Generated<number>;
3167
- id: string;
3168
- job_id: string;
3169
- job_dependencies: unknown;
3170
- remote_name: string;
3171
- document_id: string;
3172
- scopes: unknown;
3173
- branch: string;
3174
- operations: unknown;
3175
- error_source: string;
3176
- error_message: string;
3177
- created_at: Generated<Date>;
3178
- }
3179
- interface Database$1 {
3180
- Operation: OperationTable;
3181
- Keyframe: KeyframeTable;
3182
- document_collections: DocumentCollectionTable;
3183
- operation_index_operations: OperationIndexOperationTable;
3184
- sync_remotes: SyncRemoteTable;
3185
- sync_cursors: SyncCursorTable;
3186
- sync_dead_letters: SyncDeadLetterTable;
3187
- }
3188
- interface DocumentTable {
3189
- id: string;
3190
- createdAt: Generated<Date>;
3191
- updatedAt: Generated<Date>;
3192
- }
3193
- interface DocumentRelationshipTable {
3194
- id: Generated<string>;
3195
- sourceId: string;
3196
- targetId: string;
3197
- relationshipType: string;
3198
- metadata: unknown;
3199
- createdAt: Generated<Date>;
3200
- updatedAt: Generated<Date>;
3201
- }
3202
- interface IndexerStateTable {
3203
- id: Generated<number>;
3204
- lastOperationId: number;
3205
- lastOperationTimestamp: Generated<Date>;
3206
- }
3207
- interface DocumentIndexerDatabase {
3208
- Document: DocumentTable;
3209
- DocumentRelationship: DocumentRelationshipTable;
3210
- IndexerState: IndexerStateTable;
3211
- }
3212
- //#endregion
3213
3582
  //#region src/storage/pool-instrumentation.d.ts
3214
3583
  /**
3215
3584
  * Snapshot of a pg.Pool's internal counters at a point in time.
@@ -3951,12 +4320,6 @@ declare class DriveClient implements IDriveClient {
3951
4320
  private removeFileNode;
3952
4321
  }
3953
4322
  //#endregion
3954
- //#region src/cache/collection-membership-cache.d.ts
3955
- interface ICollectionMembershipCache {
3956
- getCollectionsForDocuments(documentIds: string[]): Promise<Record<string, string[]>>;
3957
- invalidate(documentId: string): void;
3958
- }
3959
- //#endregion
3960
4323
  //#region src/registry/document-model-resolver.d.ts
3961
4324
  interface IDocumentModelResolver {
3962
4325
  ensureModelLoaded(documentType: string): Promise<void>;
@@ -4213,984 +4576,621 @@ type ProjectionShardManagerConfig = {
4213
4576
  * manager routes each shard's `pool-acquire-samples` message to the
4214
4577
  * matching forwarder so the host's OpenTelemetry instrumentation records
4215
4578
  * acquire-wait latencies as if each shard's pg.Pool were local.
4216
- */
4217
- poolInstrumentations?: ForwardingPoolInstrumentation[];
4218
- };
4219
- //#endregion
4220
- //#region src/signer/types.d.ts
4221
- /**
4222
- * Configuration for signing and verification.
4223
- */
4224
- type SignerConfig = {
4225
- /**
4226
- * The signer used to sign actions before submission.
4227
- */
4228
- signer: ISigner;
4229
- /**
4230
- * Optional handler for verifying signatures on incoming operations.
4231
- * If not provided, signature verification will be skipped.
4232
- */
4233
- verifier?: SignatureVerificationHandler;
4234
- };
4235
- //#endregion
4236
- //#region src/storage/migrations/types.d.ts
4237
- type MigrationStrategy = "auto" | "manual" | "none";
4238
- interface MigrationResult {
4239
- success: boolean;
4240
- migrationsExecuted: string[];
4241
- error?: Error;
4242
- }
4243
- //#endregion
4244
- //#region src/sync/sync-builder.d.ts
4245
- declare class SyncBuilder {
4246
- private channelFactory?;
4247
- private remoteStorage?;
4248
- private cursorStorage?;
4249
- private deadLetterStorage?;
4250
- private config;
4251
- withChannelFactory(factory: IChannelFactory): this;
4252
- withRemoteStorage(storage: ISyncRemoteStorage): this;
4253
- withCursorStorage(storage: ISyncCursorStorage): this;
4254
- withDeadLetterStorage(storage: ISyncDeadLetterStorage): this;
4255
- withMaxDeadLettersPerRemote(limit: number): this;
4256
- withMaxInboxBatchSize(limit: number): this;
4257
- build(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): ISyncManager;
4258
- buildModule(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): InProcessSyncModule;
4259
- }
4260
- //#endregion
4261
- //#region src/core/reactor-builder.d.ts
4262
- /**
4263
- * Dependencies provided to read-model factories registered via
4264
- * `withReadModelFactory`. These are constructed inside `buildModule()`, which
4265
- * is why factory-based registration is needed for read models that depend on
4266
- * them (`BaseReadModel` subclasses, in particular).
4267
- */
4268
- interface ReadModelFactoryDeps {
4269
- documentModelRegistry: IDocumentModelRegistry;
4270
- operationIndex: IOperationIndex;
4271
- writeCache: IWriteCache;
4272
- processorManagerConsistencyTracker: IConsistencyTracker;
4273
- }
4274
- /**
4275
- * Factory that builds a pre-ready read model from internal reactor
4276
- * dependencies once they are available. Awaited during `buildModule()`.
4277
- */
4278
- type ReadModelFactory = (deps: ReadModelFactoryDeps) => IReadModel | Promise<IReadModel>;
4279
- type WorkerPoolBase = {
4280
- /** Number of worker threads to spawn; also the sticky-routing modulus. */numWorkers: number;
4281
- /**
4282
- * Factory spec the default transport's workers import to instantiate
4283
- * their signature verifier. Omitted = no executor-side verification,
4284
- * parity with the in-process executor's default.
4285
- */
4286
- verifier?: SignatureVerifierSpec;
4287
- };
4288
- /**
4289
- * Executor worker-pool configuration. Either `db` (default thread
4290
- * transport; each worker opens its own Postgres pool) or a custom
4291
- * `factory` transport is required by construction — an enabled pool
4292
- * without connection info is unrepresentable.
4293
- */
4294
- type WorkerPoolOptions = (WorkerPoolBase & {
4295
- db: DbConfig;
4296
- factory?: WorkerFactory;
4297
- }) | (WorkerPoolBase & {
4298
- db?: DbConfig;
4299
- factory: WorkerFactory;
4300
- });
4301
- /**
4302
- * Caller-facing config for {@link ReactorBuilder.withProjectionShards}.
4303
- * When set, the builder replaces the in-process
4304
- * {@link ReadModelCoordinator} with a {@link ProjectionShardManager} that
4305
- * fans JOB_WRITE_READY events to N projection workers sharded by
4306
- * documentId.
4307
- *
4308
- * @see Sharded projection workers sub-feature brief
4309
- * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)
4310
- */
4311
- type ProjectionShardBuilderConfig = {
4312
- shardCount: number;
4313
- preReadyKinds: BuiltInReadModelKind[];
4314
- postReadyKinds: BuiltInReadModelKind[];
4315
- /**
4316
- * Connection info for the projection workers' own pools. Falls back to
4317
- * the executor worker pool's `db` when {@link ReactorBuilder.withWorkerPool}
4318
- * is configured with one.
4319
- */
4320
- db?: DbConfig;
4321
- poolSize?: number;
4322
- initTimeoutMs?: number;
4323
- shutdownGraceMs?: number;
4324
- drainTimeoutMs?: number;
4325
- chainDepthReportIntervalMs?: number;
4326
- };
4327
- declare class ReactorBuilder {
4328
- private logger?;
4329
- private documentModelSources;
4330
- private upgradeManifests;
4331
- private features;
4332
- private readModels;
4333
- private readModelFactories;
4334
- private executorManager;
4335
- private executorConfig;
4336
- private writeCacheConfig?;
4337
- private migrationStrategy;
4338
- private syncBuilder?;
4339
- private eventBus?;
4340
- private readModelCoordinator?;
4341
- private signatureVerifier?;
4342
- private kyselyInstance?;
4343
- private signalHandlersEnabled;
4344
- private queueInstance?;
4345
- private channelScheme?;
4346
- private jwtHandler?;
4347
- private documentModelLoader?;
4348
- private shutdownHooks;
4349
- private driveContainerTypes;
4350
- private workerPool?;
4351
- private resolvedModelManifest?;
4352
- private projectionShardConfig?;
4353
- private projectionWorkerFactory?;
4354
- private instrumentedPools;
4355
- withLogger(logger: ILogger): this;
4356
- /**
4357
- * Register document-model sources: live modules, importable files, or
4358
- * importable packages. Appends across calls. At `buildModule()` every
4359
- * source is resolved host-side and registered on the registry; file and
4360
- * package sources additionally form the worker manifest when the worker
4361
- * pool is enabled (live modules cannot cross a thread boundary).
4362
- */
4363
- withDocumentModelSources(sources: DocumentModelSource[]): this;
4364
- withUpgradeManifests(manifests: UpgradeManifest<readonly number[]>[]): this;
4365
- withFeatures(features: ReactorFeatures): this;
4366
- withReadModel(readModel: IReadModel): this;
4367
- /**
4368
- * Register a factory that builds a pre-ready read model after the reactor's
4369
- * internal `operationIndex`, `writeCache`, and processor-manager consistency
4370
- * tracker are constructed. Use this for read models (e.g. `BaseReadModel`
4371
- * subclasses) that need those dependencies and therefore cannot be built
4372
- * before calling `buildModule()`.
4373
- */
4374
- withReadModelFactory(factory: ReadModelFactory): this;
4375
- withReadModelCoordinator(readModelCoordinator: IReadModelCoordinator): this;
4376
- withExecutor(executor: IJobExecutorManager): this;
4377
- withExecutorConfig(config: Partial<JobExecutorConfig>): this;
4378
- withWriteCacheConfig(config: Partial<WriteCacheConfig>): this;
4379
- withDriveContainerTypes(types: string[]): this;
4380
- withMigrationStrategy(strategy: MigrationStrategy): this;
4381
- withSync(syncBuilder: SyncBuilder): this;
4382
- withEventBus(eventBus: IEventBus): this;
4383
- withSignatureVerifier(verifier: SignatureVerificationHandler): this;
4384
- withKysely(kysely: Kysely<Database>): this;
4385
- /**
4386
- * Register an externally-constructed pg.Pool's {@link PoolInstrumentation}
4387
- * so it surfaces through {@link ReactorModule.pools}. Use this when the
4388
- * caller built the pool itself (e.g. the in-process bench host wiring) so
4389
- * pool acquire-wait and pool-stat metrics still emit. The builder also
4390
- * registers any pool it constructs internally via {@link createPostgresDatabase}.
4391
- */
4392
- withInstrumentedPool(instrumentation: PoolInstrumentation): this;
4393
- withQueue(queue: IQueue): this;
4394
- withChannelScheme(scheme: ChannelScheme): this;
4395
- withJwtHandler(handler: JwtHandler): this;
4396
- withDocumentModelLoader(loader: IDocumentModelLoader): this;
4397
- withSignalHandlers(): this;
4398
- /**
4399
- * Register an async cleanup hook to run during graceful shutdown. Hooks fire
4400
- * after `reactor.kill()` resolves and before `database.destroy()`, so callers
4401
- * that depend on the reactor (e.g. an HTTP API layered on top) can drain
4402
- * cleanly before the underlying kysely instance is torn down. Hook errors are
4403
- * logged and otherwise ignored — one bad hook cannot strand the rest of the
4404
- * shutdown chain.
4405
- */
4406
- withShutdownHook(hook: () => Promise<void>): this;
4407
- /**
4408
- * Enable the executor worker pool: N `node:worker_threads` workers with
4409
- * sticky per-document routing, replacing the in-process executor. Calling
4410
- * this enables the pool — there is no `enabled` flag. Provide `db`
4411
- * (each worker opens its own Postgres pool; the parent database is built
4412
- * from it too unless {@link withKysely} is set) or a custom `factory`
4413
- * transport. `verifier` is imported by the default transport's workers;
4414
- * omitted = no executor-side signature verification.
4415
- */
4416
- withWorkerPool(options: WorkerPoolOptions): this;
4417
- /**
4418
- * Configure N sharded projection workers. When set, the builder replaces
4419
- * the default in-process {@link ReadModelCoordinator} with a
4420
- * {@link ProjectionShardManager}.
4421
- *
4422
- * Projection workers open their own Postgres pools from `config.db`,
4423
- * falling back to the executor worker pool's `db` when
4424
- * {@link withWorkerPool} is configured with one; only the `poolSize` is
4425
- * overridden by {@link ProjectionShardBuilderConfig.poolSize}. The same
4426
- * model manifest resolved from {@link withDocumentModelSources} is
4427
- * forwarded.
4428
- */
4429
- withProjectionShards(config: ProjectionShardBuilderConfig): this;
4430
- /**
4431
- * Inject a custom {@link ProjectionWorkerFactory}. When set, the builder
4432
- * skips default thread-transport wiring for the projection shards and
4433
- * hands the factory directly to {@link ProjectionShardManager}.
4434
- */
4435
- withProjectionWorkerFactory(factory: ProjectionWorkerFactory): this;
4436
- getResolvedModelManifest(): ModelManifestEntry[] | undefined;
4437
- build(): Promise<IReactor>;
4438
- buildModule(): Promise<InProcessReactorModule>;
4439
- /**
4440
- * The single Postgres config for the parent, executor workers, and
4441
- * projection shards. They must share one physical database (the parent
4442
- * writes operations; workers and shards read them), so divergent
4443
- * worker/shard targets throw. `withKysely` overrides the parent and is not
4444
- * validated against a worker/shard `db`.
4445
- */
4446
- private resolveReactorDbConfig;
4447
- /**
4448
- * Constructs a {@link ProjectionShardManager} bound to the host event
4449
- * bus. Builds the default thread-transport factory unless one was
4450
- * injected via {@link withProjectionWorkerFactory}. Calls
4451
- * `manager.startup()` so all N workers reach READY before the reactor
4452
- * is returned to the caller.
4453
- */
4454
- private createProjectionShardManager;
4455
- private createDefaultProjectionWorkerFactory;
4456
- /**
4457
- * Default {@link WorkerFactory} used when the pool options carry no
4458
- * custom `factory`. Each worker spawns a real `node:worker_threads`
4459
- * Worker pointing at the compiled `worker/entry.js`.
4460
- */
4461
- private createDefaultWorkerFactory;
4462
- /**
4463
- * Builds the parent Kysely instance against a real Postgres server using
4464
- * the same {@link DbConfig} the workers receive at init. Used in the
4465
- * worker-pool path so the parent reactor and each worker thread share
4466
- * storage; PGlite cannot be shared across threads. The constructed pool
4467
- * is wrapped with {@link instrumentPgPool} and the resulting
4468
- * {@link PoolInstrumentation} is pushed onto {@link instrumentedPools} so
4469
- * the reactor module exposes acquire-wait and pool-stat surfaces.
4470
- */
4471
- private createPostgresDatabase;
4472
- private attachSignalHandlers;
4473
- }
4474
- //#endregion
4475
- //#region src/core/reactor-client-builder.d.ts
4476
- /**
4477
- * Builder class for constructing ReactorClient instances with proper configuration
4478
- */
4479
- declare class ReactorClientBuilder {
4480
- private logger?;
4481
- private reactorBuilder?;
4482
- private reactor?;
4483
- private eventBus?;
4484
- private documentIndexer?;
4485
- private documentView?;
4486
- private signer?;
4487
- private signatureVerifier?;
4488
- private subscriptionManager?;
4489
- private jobAwaiter?;
4490
- private documentModelLoader?;
4491
- /**
4492
- * Sets the logger for the ReactorClient.
4493
- * @param logger - The logger to use.
4494
- * @returns The ReactorClientBuilder instance.
4495
- */
4496
- withLogger(logger: ILogger): this;
4497
- /**
4498
- * Either this or withReactor must be set.
4499
- */
4500
- withReactorBuilder(reactorBuilder: ReactorBuilder): this;
4501
- /**
4502
- * Either this or withReactorBuilder must be set.
4503
- */
4504
- withReactor(reactor: IReactor, eventBus: IEventBus, documentIndexer: IDocumentIndexer, documentView: IDocumentView): this;
4505
- /**
4506
- * Sets the signer configuration for signing and verifying actions.
4507
- *
4508
- * @param config - Either an ISigner for signing only, or a SignerConfig for both signing and verification
4509
- */
4510
- withSigner(config: ISigner | SignerConfig): this;
4511
- withSubscriptionManager(subscriptionManager: IReactorSubscriptionManager): this;
4512
- withJobAwaiter(jobAwaiter: IJobAwaiter): this;
4513
- withDocumentModelLoader(loader: IDocumentModelLoader): this;
4514
- build(): Promise<ReactorClient>;
4515
- buildModule(): Promise<InProcessReactorClientModule>;
4516
- }
4517
- //#endregion
4518
- //#region src/core/drive-container-types.d.ts
4519
- declare const DEFAULT_DRIVE_CONTAINER_TYPES: ReadonlySet<string>;
4520
- //#endregion
4521
- //#region src/core/reactor.d.ts
4522
- /**
4523
- * This class implements the IReactor interface and serves as the main entry point
4524
- * for the new Reactor architecture.
4525
- */
4526
- declare class Reactor implements IReactor {
4527
- private logger;
4528
- private documentModelRegistry;
4529
- private shutdownStatus;
4530
- private setShutdown;
4531
- private setCompleted;
4532
- private queue;
4533
- private jobTracker;
4534
- private readModelCoordinator;
4535
- private features;
4536
- private documentView;
4537
- private documentIndexer;
4538
- private operationStore;
4539
- private eventBus;
4540
- private executorManager;
4541
- constructor(logger: ILogger, documentModelRegistry: IDocumentModelRegistry, queue: IQueue, jobTracker: IJobTracker, readModelCoordinator: IReadModelCoordinator, features: ReactorFeatures, documentView: IDocumentView, documentIndexer: IDocumentIndexer, operationStore: IOperationStore, eventBus: IEventBus, executorManager: IJobExecutorManager);
4542
- kill(): ShutdownStatus;
4543
- getDocumentModels(namespace?: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<DocumentModelModule>>;
4544
- get<TDocument extends PHDocument>(id: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4545
- getBySlug<TDocument extends PHDocument>(slug: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4546
- getByIdOrSlug<TDocument extends PHDocument>(identifier: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4547
- getOutgoingRelationships(sourceId: string, relationshipType: string, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<string[]>;
4548
- getIncomingRelationships(targetId: string, relationshipType: string, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<string[]>;
4549
- getOperations(documentId: string, view?: ViewFilter, filter?: OperationFilter, paging?: PagingOptions, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<Record<string, PagedResults<Operation>>>;
4550
- find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
4551
- create(document: PHDocument, signer?: ISigner, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4552
- deleteDocument(id: string, signer?: ISigner, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4553
- execute(docId: string, branch: string, actions: Action[], signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4554
- load(docId: string, branch: string, operations: Operation[], signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4555
- executeBatch(request: BatchExecutionRequest, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<BatchExecutionResult>;
4556
- loadBatch(request: BatchLoadRequest, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<BatchLoadResult>;
4557
- addRelationship(sourceId: string, targetId: string, relationshipType: string, branch?: string, signer?: ISigner, signal?: AbortSignal): Promise<JobInfo>;
4558
- removeRelationship(sourceId: string, targetId: string, relationshipType: string, branch?: string, signer?: ISigner, signal?: AbortSignal): Promise<JobInfo>;
4559
- getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;
4560
- private findByIds;
4561
- private findBySlugs;
4562
- private findByParentId;
4563
- private findByType;
4564
- private emitJobPending;
4565
- }
4566
- //#endregion
4567
- //#region src/shared/drive-url.d.ts
4568
- interface ParsedDriveUrl {
4569
- url: string;
4570
- driveId: string;
4571
- graphqlEndpoint: string;
4572
- }
4573
- /**
4574
- * Parse a drive URL to extract drive ID and construct GraphQL endpoint.
4575
- * Preserves any subpath prefix so the result is correct when the reactor is
4576
- * served behind a proxy at a non-root path.
4577
- * e.g., "http://localhost:4001/d/abc123" -> { driveId: "abc123", graphqlEndpoint: "http://localhost:4001/graphql/r" }
4578
- * e.g., "https://example.com/api/reactor/d/abc123" -> { ..., graphqlEndpoint: "https://example.com/api/reactor/graphql/r" }
4579
- */
4580
- declare function parseDriveUrl(url: string): ParsedDriveUrl;
4581
- /**
4582
- * Extract drive ID from a drive URL.
4583
- */
4584
- declare function driveIdFromUrl(url: string): string;
4585
- //#endregion
4586
- //#region src/shared/factories.d.ts
4587
- /**
4588
- * Factory method to create a ShutdownStatus that can be updated
4589
- *
4590
- * @param initialState - Initial shutdown state (default: false)
4591
- * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]
4592
- */
4593
- declare function createMutableShutdownStatus(initialState?: boolean): [ShutdownStatus, (value: boolean) => void, (completed: Promise<void>) => void];
4594
- //#endregion
4595
- //#region src/shared/utils.d.ts
4596
- type ParsedPaging = {
4597
- offset: number;
4598
- limit: number;
4579
+ */
4580
+ poolInstrumentations?: ForwardingPoolInstrumentation[];
4599
4581
  };
4600
- /**
4601
- * Validates PagingOptions and returns a normalized offset and limit.
4602
- * Throws if the cursor is not empty and not a non-negative integer, or if
4603
- * limit is less than 1. When `paging` is undefined, returns offset 0 and
4604
- * the caller-supplied `defaultLimit`.
4605
- */
4606
- declare function parsePagingOptions(paging: PagingOptions | undefined, defaultLimit: number): ParsedPaging;
4607
4582
  //#endregion
4608
- //#region src/subs/default-error-handler.d.ts
4583
+ //#region src/signer/types.d.ts
4609
4584
  /**
4610
- * Default error handler that re-throws subscription errors.
4611
- * This ensures that errors are not silently swallowed.
4585
+ * Configuration for signing and verification.
4612
4586
  */
4613
- declare class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {
4614
- handleError(error: unknown, context: SubscriptionErrorContext): void;
4615
- }
4616
- //#endregion
4617
- //#region src/subs/react-subscription-manager.d.ts
4618
- type DocumentCreatedCallback = (result: PagedResults<string>) => void;
4619
- type DocumentDeletedCallback = (documentIds: string[]) => void;
4620
- type DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;
4621
- type RelationshipChangedCallback = (parentId: string, childId: string, changeType: RelationshipChangeType) => void;
4622
- declare class ReactorSubscriptionManager implements IReactorSubscriptionManager {
4623
- private createdSubscriptions;
4624
- private deletedSubscriptions;
4625
- private updatedSubscriptions;
4626
- private relationshipSubscriptions;
4627
- private subscriptionCounter;
4628
- private errorHandler;
4629
- constructor(errorHandler: ISubscriptionErrorHandler);
4630
- onDocumentCreated(callback: DocumentCreatedCallback, search?: SearchFilter): () => void;
4631
- onDocumentDeleted(callback: DocumentDeletedCallback, search?: SearchFilter): () => void;
4632
- onDocumentStateUpdated(callback: DocumentStateUpdatedCallback, search?: SearchFilter, view?: ViewFilter): () => void;
4633
- onRelationshipChanged(callback: RelationshipChangedCallback, search?: SearchFilter): () => void;
4634
- /**
4635
- * Notify subscribers about created documents
4636
- */
4637
- notifyDocumentsCreated(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4638
- /**
4639
- * Notify subscribers about deleted documents
4640
- */
4641
- notifyDocumentsDeleted(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4642
- /**
4643
- * Notify subscribers about updated documents
4644
- */
4645
- notifyDocumentsUpdated(documents: PHDocument[]): void;
4587
+ type SignerConfig = {
4646
4588
  /**
4647
- * Notify subscribers about relationship changes
4589
+ * The signer used to sign actions before submission.
4648
4590
  */
4649
- notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
4591
+ signer: ISigner;
4650
4592
  /**
4651
- * Clear all subscriptions
4593
+ * Optional handler for verifying signatures on incoming operations.
4594
+ * If not provided, signature verification will be skipped.
4652
4595
  */
4653
- clearAll(): void;
4654
- private filterDocumentIds;
4655
- private filterDocuments;
4656
- private matchesRelationshipFilter;
4596
+ verifier?: SignatureVerificationHandler;
4597
+ };
4598
+ //#endregion
4599
+ //#region src/storage/migrations/types.d.ts
4600
+ type MigrationStrategy = "auto" | "manual" | "none";
4601
+ interface MigrationResult {
4602
+ success: boolean;
4603
+ migrationsExecuted: string[];
4604
+ error?: Error;
4657
4605
  }
4658
4606
  //#endregion
4659
- //#region src/events/event-bus.d.ts
4660
- declare class EventBus implements IEventBus {
4661
- readonly eventTypeToSubscribers: Map<number, Subscriber[]>;
4662
- subscribe<K>(type: number, subscriber: (type: number, event: K) => void | Promise<void>): Unsubscribe;
4663
- emit(type: number, data: any): Promise<void>;
4607
+ //#region src/sync/sync-builder.d.ts
4608
+ declare class SyncBuilder {
4609
+ private channelFactory?;
4610
+ private remoteStorage?;
4611
+ private cursorStorage?;
4612
+ private deadLetterStorage?;
4613
+ private config;
4614
+ withChannelFactory(factory: IChannelFactory): this;
4615
+ withRemoteStorage(storage: ISyncRemoteStorage): this;
4616
+ withCursorStorage(storage: ISyncCursorStorage): this;
4617
+ withDeadLetterStorage(storage: ISyncDeadLetterStorage): this;
4618
+ withMaxDeadLettersPerRemote(limit: number): this;
4619
+ withMaxInboxBatchSize(limit: number): this;
4620
+ build(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): ISyncManager;
4621
+ buildModule(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): InProcessSyncModule;
4664
4622
  }
4665
4623
  //#endregion
4666
- //#region src/queue/queue.d.ts
4624
+ //#region src/core/reactor-builder.d.ts
4667
4625
  /**
4668
- * In-memory implementation of the IQueue interface.
4669
- * Organizes jobs by documentId, scope, and branch to ensure proper ordering.
4670
- * Ensures serial execution per document by tracking executing jobs.
4671
- * Implements dependency management through queue hints.
4626
+ * Dependencies provided to read-model factories registered via
4627
+ * `withReadModelFactory`. These are constructed inside `buildModule()`, which
4628
+ * is why factory-based registration is needed for read models that depend on
4629
+ * them (`BaseReadModel` subclasses, in particular).
4672
4630
  */
4673
- declare class InMemoryQueue implements IQueue {
4674
- private eventBus;
4675
- private resolver;
4676
- private queues;
4677
- private jobIdToQueueKey;
4678
- private docIdToJobId;
4679
- private jobIdToDocId;
4680
- private completedJobs;
4681
- private jobIndex;
4682
- private isBlocked;
4683
- private onDrainedCallback?;
4684
- private isPausedFlag;
4685
- constructor(eventBus: IEventBus, resolver: IDocumentModelResolver);
4686
- private toErrorInfo;
4687
- /**
4688
- * Creates a unique key for a document/scope/branch combination
4689
- */
4690
- private createQueueKey;
4691
- /**
4692
- * Gets or creates a queue for the given key
4693
- */
4694
- private getQueue;
4695
- /**
4696
- * Check if a document has any jobs currently executing
4697
- */
4698
- private isDocumentExecuting;
4699
- /**
4700
- * Mark a job as executing for its document
4701
- */
4702
- private markJobExecuting;
4703
- /**
4704
- * Mark a job as no longer executing for its document
4705
- */
4706
- private markJobComplete;
4707
- /**
4708
- * Check if all dependencies for a job have been completed
4709
- */
4710
- private areDependenciesMet;
4711
- /**
4712
- * Returns the head of the sub-queue if its dependencies are met, or null.
4713
- *
4714
- * The dispatcher only ever considers the head — a dep-blocked head holds
4715
- * the rest of its sub-queue. This preserves per-(documentId, scope, branch)
4716
- * FIFO regardless of how dependencies are authored, and makes the queue's
4717
- * documented "serialized per document" invariant hold even when callers
4718
- * omit queueHint dependencies on jobs that share a sub-queue.
4719
- */
4720
- private getNextJobWithMetDependencies;
4721
- private getCreateDocumentType;
4722
- enqueue(job: Job): Promise<void>;
4723
- dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4724
- dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4725
- dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4726
- size(documentId: string, scope: string, branch: string): Promise<number>;
4727
- totalSize(): Promise<number>;
4728
- remove(jobId: string): Promise<boolean>;
4729
- clear(documentId: string, scope: string, branch: string): Promise<void>;
4730
- clearAll(): Promise<void>;
4731
- hasJobs(): Promise<boolean>;
4732
- completeJob(jobId: string): Promise<void>;
4733
- failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
4734
- deferJob(jobId: string): void;
4735
- retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
4631
+ interface ReadModelFactoryDeps {
4632
+ documentModelRegistry: IDocumentModelRegistry;
4633
+ operationIndex: IOperationIndex;
4634
+ writeCache: IWriteCache;
4635
+ processorManagerConsistencyTracker: IConsistencyTracker;
4636
+ }
4637
+ /**
4638
+ * Factory that builds a pre-ready read model from internal reactor
4639
+ * dependencies once they are available. Awaited during `buildModule()`.
4640
+ */
4641
+ type ReadModelFactory = (deps: ReadModelFactoryDeps) => IReadModel | Promise<IReadModel>;
4642
+ type WorkerPoolBase = {
4643
+ /** Number of worker threads to spawn; also the sticky-routing modulus. */numWorkers: number;
4736
4644
  /**
4737
- * Check if the queue is drained and call the callback if it is
4645
+ * Factory spec the default transport's workers import to instantiate
4646
+ * their signature verifier. Omitted = no executor-side verification,
4647
+ * parity with the in-process executor's default.
4738
4648
  */
4739
- private checkDrained;
4649
+ verifier?: SignatureVerifierSpec;
4650
+ };
4651
+ /**
4652
+ * Executor worker-pool configuration. Either `db` (default thread
4653
+ * transport; each worker opens its own Postgres pool) or a custom
4654
+ * `factory` transport is required by construction — an enabled pool
4655
+ * without connection info is unrepresentable.
4656
+ */
4657
+ type WorkerPoolOptions = (WorkerPoolBase & {
4658
+ db: DbConfig;
4659
+ factory?: WorkerFactory;
4660
+ }) | (WorkerPoolBase & {
4661
+ db?: DbConfig;
4662
+ factory: WorkerFactory;
4663
+ });
4664
+ /**
4665
+ * Caller-facing config for {@link ReactorBuilder.withProjectionShards}.
4666
+ * When set, the builder replaces the in-process
4667
+ * {@link ReadModelCoordinator} with a {@link ProjectionShardManager} that
4668
+ * fans JOB_WRITE_READY events to N projection workers sharded by
4669
+ * documentId.
4670
+ *
4671
+ * @see Sharded projection workers sub-feature brief
4672
+ * (Powerhouse board wiki id: eb26f01f-8f68-4918-a6f6-ac7a4679b533)
4673
+ */
4674
+ type ProjectionShardBuilderConfig = {
4675
+ shardCount: number;
4676
+ preReadyKinds: BuiltInReadModelKind[];
4677
+ postReadyKinds: BuiltInReadModelKind[];
4740
4678
  /**
4741
- * Returns true if and only if all jobs have been resolved.
4679
+ * Connection info for the projection workers' own pools. Falls back to
4680
+ * the executor worker pool's `db` when {@link ReactorBuilder.withWorkerPool}
4681
+ * is configured with one.
4742
4682
  */
4743
- get isDrained(): boolean;
4683
+ db?: DbConfig;
4684
+ poolSize?: number;
4685
+ initTimeoutMs?: number;
4686
+ shutdownGraceMs?: number;
4687
+ drainTimeoutMs?: number;
4688
+ chainDepthReportIntervalMs?: number;
4689
+ };
4690
+ declare class ReactorBuilder {
4691
+ private logger?;
4692
+ private documentModelSources;
4693
+ private upgradeManifests;
4694
+ private features;
4695
+ private readModels;
4696
+ private readModelFactories;
4697
+ private executorManager;
4698
+ private executorConfig;
4699
+ private writeCacheConfig?;
4700
+ private migrationStrategy;
4701
+ private syncBuilder?;
4702
+ private eventBus?;
4703
+ private readModelCoordinator?;
4704
+ private signatureVerifier?;
4705
+ private kyselyInstance?;
4706
+ private signalHandlersEnabled;
4707
+ private queueInstance?;
4708
+ private channelScheme?;
4709
+ private jwtHandler?;
4710
+ private documentModelLoader?;
4711
+ private shutdownHooks;
4712
+ private driveContainerTypes;
4713
+ private workerPool?;
4714
+ private resolvedModelManifest?;
4715
+ private projectionShardConfig?;
4716
+ private projectionWorkerFactory?;
4717
+ private instrumentedPools;
4718
+ withLogger(logger: ILogger): this;
4744
4719
  /**
4745
- * Blocks the queue from accepting new jobs.
4746
- * @param onDrained - Optional callback to call when the queue is drained
4720
+ * Register document-model sources: live modules, importable files, or
4721
+ * importable packages. Appends across calls. At `buildModule()` every
4722
+ * source is resolved host-side and registered on the registry; file and
4723
+ * package sources additionally form the worker manifest when the worker
4724
+ * pool is enabled (live modules cannot cross a thread boundary).
4747
4725
  */
4748
- block(onDrained?: () => void): void;
4726
+ withDocumentModelSources(sources: DocumentModelSource[]): this;
4727
+ withUpgradeManifests(manifests: UpgradeManifest<readonly number[]>[]): this;
4728
+ withFeatures(features: ReactorFeatures): this;
4729
+ withReadModel(readModel: IReadModel): this;
4749
4730
  /**
4750
- * Unblocks the queue from accepting new jobs.
4731
+ * Register a factory that builds a pre-ready read model after the reactor's
4732
+ * internal `operationIndex`, `writeCache`, and processor-manager consistency
4733
+ * tracker are constructed. Use this for read models (e.g. `BaseReadModel`
4734
+ * subclasses) that need those dependencies and therefore cannot be built
4735
+ * before calling `buildModule()`.
4751
4736
  */
4752
- unblock(): void;
4737
+ withReadModelFactory(factory: ReadModelFactory): this;
4738
+ withReadModelCoordinator(readModelCoordinator: IReadModelCoordinator): this;
4739
+ withExecutor(executor: IJobExecutorManager): this;
4740
+ withExecutorConfig(config: Partial<JobExecutorConfig>): this;
4741
+ withWriteCacheConfig(config: Partial<WriteCacheConfig>): this;
4742
+ withDriveContainerTypes(types: string[]): this;
4743
+ withMigrationStrategy(strategy: MigrationStrategy): this;
4744
+ withSync(syncBuilder: SyncBuilder): this;
4745
+ withEventBus(eventBus: IEventBus): this;
4746
+ withSignatureVerifier(verifier: SignatureVerificationHandler): this;
4747
+ withKysely(kysely: Kysely<Database>): this;
4753
4748
  /**
4754
- * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.
4749
+ * Register an externally-constructed pg.Pool's {@link PoolInstrumentation}
4750
+ * so it surfaces through {@link ReactorModule.pools}. Use this when the
4751
+ * caller built the pool itself (e.g. the in-process bench host wiring) so
4752
+ * pool acquire-wait and pool-stat metrics still emit. The builder also
4753
+ * registers any pool it constructs internally via {@link createPostgresDatabase}.
4755
4754
  */
4756
- pause(): void;
4755
+ withInstrumentedPool(instrumentation: PoolInstrumentation): this;
4756
+ withQueue(queue: IQueue): this;
4757
+ withChannelScheme(scheme: ChannelScheme): this;
4758
+ withJwtHandler(handler: JwtHandler): this;
4759
+ withDocumentModelLoader(loader: IDocumentModelLoader): this;
4760
+ withSignalHandlers(): this;
4757
4761
  /**
4758
- * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.
4762
+ * Register an async cleanup hook to run during graceful shutdown. Hooks fire
4763
+ * after `reactor.kill()` resolves and before `database.destroy()`, so callers
4764
+ * that depend on the reactor (e.g. an HTTP API layered on top) can drain
4765
+ * cleanly before the underlying kysely instance is torn down. Hook errors are
4766
+ * logged and otherwise ignored — one bad hook cannot strand the rest of the
4767
+ * shutdown chain.
4759
4768
  */
4760
- resume(): Promise<void>;
4769
+ withShutdownHook(hook: () => Promise<void>): this;
4761
4770
  /**
4762
- * Returns whether job dequeuing is paused.
4771
+ * Enable the executor worker pool: N `node:worker_threads` workers with
4772
+ * sticky per-document routing, replacing the in-process executor. Calling
4773
+ * this enables the pool — there is no `enabled` flag. Provide `db`
4774
+ * (each worker opens its own Postgres pool; the parent database is built
4775
+ * from it too unless {@link withKysely} is set) or a custom `factory`
4776
+ * transport. `verifier` is imported by the default transport's workers;
4777
+ * omitted = no executor-side signature verification.
4763
4778
  */
4764
- get paused(): boolean;
4779
+ withWorkerPool(options: WorkerPoolOptions): this;
4765
4780
  /**
4766
- * Returns all pending jobs across all queues.
4781
+ * Configure N sharded projection workers. When set, the builder replaces
4782
+ * the default in-process {@link ReadModelCoordinator} with a
4783
+ * {@link ProjectionShardManager}.
4784
+ *
4785
+ * Projection workers open their own Postgres pools from `config.db`,
4786
+ * falling back to the executor worker pool's `db` when
4787
+ * {@link withWorkerPool} is configured with one; only the `poolSize` is
4788
+ * overridden by {@link ProjectionShardBuilderConfig.poolSize}. The same
4789
+ * model manifest resolved from {@link withDocumentModelSources} is
4790
+ * forwarded.
4767
4791
  */
4768
- getPendingJobs(): Job[];
4792
+ withProjectionShards(config: ProjectionShardBuilderConfig): this;
4769
4793
  /**
4770
- * Returns a map of document IDs to sets of executing job IDs.
4794
+ * Inject a custom {@link ProjectionWorkerFactory}. When set, the builder
4795
+ * skips default thread-transport wiring for the projection shards and
4796
+ * hands the factory directly to {@link ProjectionShardManager}.
4771
4797
  */
4772
- getExecutingJobIds(): Map<string, Set<string>>;
4798
+ withProjectionWorkerFactory(factory: ProjectionWorkerFactory): this;
4799
+ getResolvedModelManifest(): ModelManifestEntry[] | undefined;
4800
+ build(): Promise<IReactor>;
4801
+ buildModule(): Promise<InProcessReactorModule>;
4773
4802
  /**
4774
- * Returns a job by ID from the job index.
4803
+ * The single Postgres config for the parent, executor workers, and
4804
+ * projection shards. They must share one physical database (the parent
4805
+ * writes operations; workers and shards read them), so divergent
4806
+ * worker/shard targets throw. `withKysely` overrides the parent and is not
4807
+ * validated against a worker/shard `db`.
4775
4808
  */
4776
- getJob(jobId: string): Job | undefined;
4777
- }
4778
- //#endregion
4779
- //#region src/job-tracker/in-memory-job-tracker.d.ts
4780
- /**
4781
- * In-memory implementation of IJobTracker.
4782
- * Maintains job status in a Map for synchronous access.
4783
- * Subscribes to operation events to update job states.
4784
- */
4785
- declare class InMemoryJobTracker implements IJobTracker {
4786
- private eventBus;
4787
- private jobs;
4788
- private unsubscribers;
4789
- constructor(eventBus: IEventBus);
4790
- private subscribeToEvents;
4791
- private handleWriteReady;
4792
- private handleReadReady;
4793
- private handleJobFailed;
4794
- shutdown(): void;
4795
- registerJob(jobInfo: JobInfo): void;
4796
- markRunning(jobId: string): void;
4797
- markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
4798
- getJobStatus(jobId: string): JobInfo | null;
4799
- }
4800
- //#endregion
4801
- //#region src/executor/simple-job-executor-manager.d.ts
4802
- type JobExecutorFactory = () => IJobExecutor;
4803
- /**
4804
- * Manages multiple job executors and coordinates job distribution.
4805
- * Listens for job available events and dispatches jobs to executors.
4806
- */
4807
- declare class SimpleJobExecutorManager implements IJobExecutorManager {
4808
- private executorFactory;
4809
- private eventBus;
4810
- private queue;
4811
- private jobTracker;
4812
- private logger;
4813
- private resolver;
4814
- private executors;
4815
- private isRunning;
4816
- private activeJobs;
4817
- private totalJobsProcessed;
4818
- private unsubscribe?;
4819
- private deferredJobs;
4820
- private resultHandler;
4821
- private jobTimeoutMs;
4822
- constructor(executorFactory: JobExecutorFactory, eventBus: IEventBus, queue: IQueue, jobTracker: IJobTracker, logger: ILogger, resolver: IDocumentModelResolver, jobTimeoutMs?: number);
4823
- start(numExecutors: number): Promise<void>;
4824
- stop(graceful?: boolean): Promise<void>;
4825
- getExecutors(): IJobExecutor[];
4826
- getStatus(): ExecutorManagerStatus;
4827
- private processNextJob;
4828
- private checkForMoreJobs;
4829
- private processExistingJobs;
4830
- private flushDeferredJobs;
4831
- }
4832
- //#endregion
4833
- //#region src/cache/document-meta-cache-types.d.ts
4834
- /**
4835
- * Cached document metadata from the "document" scope.
4836
- *
4837
- * This lightweight structure holds essential document information needed by
4838
- * the job executor without fetching full scope state. It provides an explicit
4839
- * cross-scope contract for accessing document scope metadata.
4840
- */
4841
- type CachedDocumentMeta = {
4809
+ private resolveReactorDbConfig;
4842
4810
  /**
4843
- * The full PHDocumentState from document.state.document.
4844
- * Contains version, hash, isDeleted, deletedAtUtcIso, etc.
4811
+ * Constructs a {@link ProjectionShardManager} bound to the host event
4812
+ * bus. Builds the default thread-transport factory unless one was
4813
+ * injected via {@link withProjectionWorkerFactory}. Calls
4814
+ * `manager.startup()` so all N workers reach READY before the reactor
4815
+ * is returned to the caller.
4845
4816
  */
4846
- state: PHDocumentState;
4817
+ private createProjectionShardManager;
4818
+ private createDefaultProjectionWorkerFactory;
4847
4819
  /**
4848
- * The document type (from header), cached for convenience.
4820
+ * Default {@link WorkerFactory} used when the pool options carry no
4821
+ * custom `factory`. Each worker spawns a real `node:worker_threads`
4822
+ * Worker pointing at the compiled `worker/entry.js`.
4849
4823
  */
4850
- documentType: string;
4824
+ private createDefaultWorkerFactory;
4851
4825
  /**
4852
- * The revision of the document scope when this metadata was captured.
4853
- * Used for cache invalidation and consistency checks.
4826
+ * Builds the parent Kysely instance against a real Postgres server using
4827
+ * the same {@link DbConfig} the workers receive at init. Used in the
4828
+ * worker-pool path so the parent reactor and each worker thread share
4829
+ * storage; PGlite cannot be shared across threads. The constructed pool
4830
+ * is wrapped with {@link instrumentPgPool} and the resulting
4831
+ * {@link PoolInstrumentation} is pushed onto {@link instrumentedPools} so
4832
+ * the reactor module exposes acquire-wait and pool-stat surfaces.
4854
4833
  */
4855
- documentScopeRevision: number;
4856
- };
4834
+ private createPostgresDatabase;
4835
+ private attachSignalHandlers;
4836
+ }
4837
+ //#endregion
4838
+ //#region src/core/reactor-client-builder.d.ts
4857
4839
  /**
4858
- * Interface for the document metadata cache.
4859
- *
4860
- * This cache provides an explicit cross-scope contract for accessing document
4861
- * scope metadata. It solves the problem where job execution in one scope (e.g.,
4862
- * "global") needs access to document scope state (version, isDeleted, etc.)
4863
- * which may be stale in scope-specific caches or keyframes.
4864
- *
4865
- * The cache supports:
4866
- * - Latest metadata retrieval with LRU caching
4867
- * - Historical metadata reconstruction for reshuffling scenarios
4868
- * - Eager updates after document scope operations
4840
+ * Builder class for constructing ReactorClient instances with proper configuration
4869
4841
  */
4870
- interface IDocumentMetaCache {
4871
- /**
4872
- * Retrieves the LATEST document metadata from cache or rebuilds from operations.
4873
- *
4874
- * On cache miss, fetches all document scope operations and reconstructs the
4875
- * current PHDocumentState by applying UPGRADE_DOCUMENT and DELETE_DOCUMENT
4876
- * operations.
4877
- *
4878
- * @param documentId - The document identifier
4879
- * @param branch - Branch name
4880
- * @param signal - Optional abort signal to cancel the operation
4881
- * @returns The cached or rebuilt document metadata
4882
- * @throws {Error} "Operation aborted" if signal is aborted
4883
- * @throws {Error} If document not found (no CREATE_DOCUMENT operation)
4884
- */
4885
- getDocumentMeta(documentId: string, branch: string, signal?: AbortSignal): Promise<CachedDocumentMeta>;
4886
- /**
4887
- * Rebuilds document metadata at a SPECIFIC revision (always rebuilds, no caching).
4888
- *
4889
- * Used during reshuffling when operations need to be inserted at a previous
4890
- * revision and we need the document scope state as of that point in time.
4891
- *
4892
- * @param documentId - The document identifier
4893
- * @param branch - Branch name
4894
- * @param targetRevision - The document scope revision to reconstruct up to
4895
- * @param signal - Optional abort signal to cancel the operation
4896
- * @returns Document metadata as of the target revision
4897
- * @throws {Error} "Operation aborted" if signal is aborted
4898
- * @throws {Error} If document not found
4899
- */
4900
- rebuildAtRevision(documentId: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<CachedDocumentMeta>;
4901
- /**
4902
- * Eagerly updates cached metadata after document scope operations.
4903
- *
4904
- * Called by the job executor after CREATE_DOCUMENT, UPGRADE_DOCUMENT, or
4905
- * DELETE_DOCUMENT operations to keep the cache current.
4906
- *
4907
- * @param documentId - The document identifier
4908
- * @param branch - Branch name
4909
- * @param meta - The new metadata to cache
4910
- */
4911
- putDocumentMeta(documentId: string, branch: string, meta: CachedDocumentMeta): void;
4842
+ declare class ReactorClientBuilder {
4843
+ private logger?;
4844
+ private reactorBuilder?;
4845
+ private reactor?;
4846
+ private eventBus?;
4847
+ private documentIndexer?;
4848
+ private documentView?;
4849
+ private signer?;
4850
+ private signatureVerifier?;
4851
+ private subscriptionManager?;
4852
+ private jobAwaiter?;
4853
+ private documentModelLoader?;
4912
4854
  /**
4913
- * Invalidates cached document metadata.
4914
- *
4915
- * Call before reshuffling operations that modify the document scope, or
4916
- * when document state may have changed externally.
4917
- *
4918
- * @param documentId - The document identifier
4919
- * @param branch - Optional branch to narrow invalidation (if omitted, all branches)
4920
- * @returns Number of entries invalidated
4855
+ * Sets the logger for the ReactorClient.
4856
+ * @param logger - The logger to use.
4857
+ * @returns The ReactorClientBuilder instance.
4921
4858
  */
4922
- invalidate(documentId: string, branch?: string): number;
4859
+ withLogger(logger: ILogger): this;
4923
4860
  /**
4924
- * Clears all cached document metadata.
4861
+ * Either this or withReactor must be set.
4925
4862
  */
4926
- clear(): void;
4863
+ withReactorBuilder(reactorBuilder: ReactorBuilder): this;
4927
4864
  /**
4928
- * Performs startup initialization.
4865
+ * Either this or withReactorBuilder must be set.
4929
4866
  */
4930
- startup(): Promise<void>;
4867
+ withReactor(reactor: IReactor, eventBus: IEventBus, documentIndexer: IDocumentIndexer, documentView: IDocumentView): this;
4931
4868
  /**
4932
- * Performs graceful shutdown.
4869
+ * Sets the signer configuration for signing and verifying actions.
4870
+ *
4871
+ * @param config - Either an ISigner for signing only, or a SignerConfig for both signing and verification
4933
4872
  */
4934
- shutdown(): Promise<void>;
4873
+ withSigner(config: ISigner | SignerConfig): this;
4874
+ withSubscriptionManager(subscriptionManager: IReactorSubscriptionManager): this;
4875
+ withJobAwaiter(jobAwaiter: IJobAwaiter): this;
4876
+ withDocumentModelLoader(loader: IDocumentModelLoader): this;
4877
+ build(): Promise<ReactorClient>;
4878
+ buildModule(): Promise<InProcessReactorClientModule>;
4935
4879
  }
4936
4880
  //#endregion
4937
- //#region src/cache/buffer/ring-buffer.d.ts
4881
+ //#region src/core/drive-container-types.d.ts
4882
+ declare const DEFAULT_DRIVE_CONTAINER_TYPES: ReadonlySet<string>;
4883
+ //#endregion
4884
+ //#region src/core/reactor.d.ts
4938
4885
  /**
4939
- * RingBuffer is a generic circular buffer implementation that stores a fixed number
4940
- * of items. When the buffer is full, new items overwrite the oldest items.
4941
- *
4942
- * This implementation maintains O(1) time complexity for push operations and provides
4943
- * items in chronological order (oldest to newest) via getAll().
4886
+ * This class implements the IReactor interface and serves as the main entry point
4887
+ * for the new Reactor architecture.
4888
+ */
4889
+ declare class Reactor implements IReactor {
4890
+ private logger;
4891
+ private documentModelRegistry;
4892
+ private shutdownStatus;
4893
+ private setShutdown;
4894
+ private setCompleted;
4895
+ private queue;
4896
+ private jobTracker;
4897
+ private readModelCoordinator;
4898
+ private features;
4899
+ private documentView;
4900
+ private documentIndexer;
4901
+ private operationStore;
4902
+ private eventBus;
4903
+ private executorManager;
4904
+ constructor(logger: ILogger, documentModelRegistry: IDocumentModelRegistry, queue: IQueue, jobTracker: IJobTracker, readModelCoordinator: IReadModelCoordinator, features: ReactorFeatures, documentView: IDocumentView, documentIndexer: IDocumentIndexer, operationStore: IOperationStore, eventBus: IEventBus, executorManager: IJobExecutorManager);
4905
+ kill(): ShutdownStatus;
4906
+ getDocumentModels(namespace?: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<DocumentModelModule>>;
4907
+ get<TDocument extends PHDocument>(id: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4908
+ getBySlug<TDocument extends PHDocument>(slug: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4909
+ getByIdOrSlug<TDocument extends PHDocument>(identifier: string, view?: ViewFilter, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
4910
+ getOutgoingRelationships(sourceId: string, relationshipType: string, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<string[]>;
4911
+ getIncomingRelationships(targetId: string, relationshipType: string, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<string[]>;
4912
+ getOperations(documentId: string, view?: ViewFilter, filter?: OperationFilter, paging?: PagingOptions, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<Record<string, PagedResults<Operation>>>;
4913
+ find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
4914
+ create(document: PHDocument, signer?: ISigner, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4915
+ deleteDocument(id: string, signer?: ISigner, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4916
+ execute(docId: string, branch: string, actions: Action[], signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4917
+ load(docId: string, branch: string, operations: Operation[], signal?: AbortSignal, meta?: Record<string, unknown>): Promise<JobInfo>;
4918
+ executeBatch(request: BatchExecutionRequest, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<BatchExecutionResult>;
4919
+ loadBatch(request: BatchLoadRequest, signal?: AbortSignal, meta?: Record<string, unknown>): Promise<BatchLoadResult>;
4920
+ addRelationship(sourceId: string, targetId: string, relationshipType: string, branch?: string, signer?: ISigner, signal?: AbortSignal): Promise<JobInfo>;
4921
+ removeRelationship(sourceId: string, targetId: string, relationshipType: string, branch?: string, signer?: ISigner, signal?: AbortSignal): Promise<JobInfo>;
4922
+ getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;
4923
+ private findByIds;
4924
+ private findBySlugs;
4925
+ private findByParentId;
4926
+ private findByType;
4927
+ private emitJobPending;
4928
+ }
4929
+ //#endregion
4930
+ //#region src/shared/drive-url.d.ts
4931
+ interface ParsedDriveUrl {
4932
+ url: string;
4933
+ driveId: string;
4934
+ graphqlEndpoint: string;
4935
+ }
4936
+ /**
4937
+ * Parse a drive URL to extract drive ID and construct GraphQL endpoint.
4938
+ * Preserves any subpath prefix so the result is correct when the reactor is
4939
+ * served behind a proxy at a non-root path.
4940
+ * e.g., "http://localhost:4001/d/abc123" -> { driveId: "abc123", graphqlEndpoint: "http://localhost:4001/graphql/r" }
4941
+ * e.g., "https://example.com/api/reactor/d/abc123" -> { ..., graphqlEndpoint: "https://example.com/api/reactor/graphql/r" }
4942
+ */
4943
+ declare function parseDriveUrl(url: string): ParsedDriveUrl;
4944
+ /**
4945
+ * Extract drive ID from a drive URL.
4946
+ */
4947
+ declare function driveIdFromUrl(url: string): string;
4948
+ //#endregion
4949
+ //#region src/shared/factories.d.ts
4950
+ /**
4951
+ * Factory method to create a ShutdownStatus that can be updated
4944
4952
  *
4945
- * @template T - The type of items stored in the buffer
4953
+ * @param initialState - Initial shutdown state (default: false)
4954
+ * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]
4946
4955
  */
4947
- declare class RingBuffer<T> {
4948
- private buffer;
4949
- private head;
4950
- private size;
4951
- private capacity;
4952
- constructor(capacity: number);
4956
+ declare function createMutableShutdownStatus(initialState?: boolean): [ShutdownStatus, (value: boolean) => void, (completed: Promise<void>) => void];
4957
+ //#endregion
4958
+ //#region src/shared/utils.d.ts
4959
+ type ParsedPaging = {
4960
+ offset: number;
4961
+ limit: number;
4962
+ };
4963
+ /**
4964
+ * Validates PagingOptions and returns a normalized offset and limit.
4965
+ * Throws if the cursor is not empty and not a non-negative integer, or if
4966
+ * limit is less than 1. When `paging` is undefined, returns offset 0 and
4967
+ * the caller-supplied `defaultLimit`.
4968
+ */
4969
+ declare function parsePagingOptions(paging: PagingOptions | undefined, defaultLimit: number): ParsedPaging;
4970
+ //#endregion
4971
+ //#region src/subs/default-error-handler.d.ts
4972
+ /**
4973
+ * Default error handler that re-throws subscription errors.
4974
+ * This ensures that errors are not silently swallowed.
4975
+ */
4976
+ declare class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {
4977
+ handleError(error: unknown, context: SubscriptionErrorContext): void;
4978
+ }
4979
+ //#endregion
4980
+ //#region src/subs/react-subscription-manager.d.ts
4981
+ type DocumentCreatedCallback = (result: PagedResults<string>) => void;
4982
+ type DocumentDeletedCallback = (documentIds: string[]) => void;
4983
+ type DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;
4984
+ type RelationshipChangedCallback = (parentId: string, childId: string, changeType: RelationshipChangeType) => void;
4985
+ declare class ReactorSubscriptionManager implements IReactorSubscriptionManager {
4986
+ private createdSubscriptions;
4987
+ private deletedSubscriptions;
4988
+ private updatedSubscriptions;
4989
+ private relationshipSubscriptions;
4990
+ private subscriptionCounter;
4991
+ private errorHandler;
4992
+ constructor(errorHandler: ISubscriptionErrorHandler);
4993
+ onDocumentCreated(callback: DocumentCreatedCallback, search?: SearchFilter): () => void;
4994
+ onDocumentDeleted(callback: DocumentDeletedCallback, search?: SearchFilter): () => void;
4995
+ onDocumentStateUpdated(callback: DocumentStateUpdatedCallback, search?: SearchFilter, view?: ViewFilter): () => void;
4996
+ onRelationshipChanged(callback: RelationshipChangedCallback, search?: SearchFilter): () => void;
4953
4997
  /**
4954
- * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
4955
- *
4956
- * @param item - The item to add
4998
+ * Notify subscribers about created documents
4957
4999
  */
4958
- push(item: T): void;
5000
+ notifyDocumentsCreated(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4959
5001
  /**
4960
- * Returns all items in the buffer in chronological order (oldest to newest).
4961
- *
4962
- * @returns Array of items in insertion order
5002
+ * Notify subscribers about deleted documents
4963
5003
  */
4964
- getAll(): T[];
5004
+ notifyDocumentsDeleted(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4965
5005
  /**
4966
- * Clears all items from the buffer.
5006
+ * Notify subscribers about updated documents
4967
5007
  */
4968
- clear(): void;
5008
+ notifyDocumentsUpdated(documents: PHDocument[]): void;
4969
5009
  /**
4970
- * Gets the current number of items in the buffer.
5010
+ * Notify subscribers about relationship changes
4971
5011
  */
4972
- get length(): number;
5012
+ notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
5013
+ /**
5014
+ * Clear all subscriptions
5015
+ */
5016
+ clearAll(): void;
5017
+ private filterDocumentIds;
5018
+ private filterDocuments;
5019
+ private matchesRelationshipFilter;
4973
5020
  }
4974
5021
  //#endregion
4975
- //#region src/cache/kysely-write-cache.d.ts
4976
- type DocumentStream = {
4977
- key: string;
4978
- ringBuffer: RingBuffer<CachedSnapshot>;
4979
- };
5022
+ //#region src/events/event-bus.d.ts
5023
+ declare class EventBus implements IEventBus {
5024
+ readonly eventTypeToSubscribers: Map<number, Subscriber[]>;
5025
+ subscribe<K>(type: number, subscriber: (type: number, event: K) => void | Promise<void>): Unsubscribe;
5026
+ emit(type: number, data: any): Promise<void>;
5027
+ }
5028
+ //#endregion
5029
+ //#region src/queue/queue.d.ts
4980
5030
  /**
4981
- * In-memory write cache with keyframe persistence for PHDocuments.
4982
- *
4983
- * Caches document snapshots in ring buffers with LRU eviction. On cache miss,
4984
- * rebuilds documents from nearest keyframe or full operation history.
4985
- *
4986
- * **Performance Characteristics:**
4987
- * - Cache hit: O(1) lookup in ring buffer
4988
- * - Cold miss: O(n) where n is total operation count, or O(k) where k is operations since keyframe
4989
- * - Warm miss: O(m) where m is operations since cached revision
4990
- * - Eviction: O(1) for LRU tracking and removal
4991
- *
4992
- * **Thread Safety:**
4993
- * Not thread-safe. Designed for single-threaded job executor environment.
4994
- * External synchronization required for concurrent access across multiple executors.
4995
- *
4996
- * **Example:**
4997
- * ```typescript
4998
- * const cache = new KyselyWriteCache(
4999
- * keyframeStore,
5000
- * operationStore,
5001
- * registry,
5002
- * { maxDocuments: 1000, ringBufferSize: 10, keyframeInterval: 10 }
5003
- * );
5004
- *
5005
- * await cache.startup();
5006
- *
5007
- * // Retrieve or rebuild document
5008
- * const doc = await cache.getState(docId, docType, scope, branch, revision);
5009
- *
5010
- * // Cache result after job execution
5011
- * cache.putState(docId, docType, scope, branch, newRevision, updatedDoc);
5012
- *
5013
- * await cache.shutdown();
5014
- * ```
5031
+ * In-memory implementation of the IQueue interface.
5032
+ * Organizes jobs by documentId, scope, and branch to ensure proper ordering.
5033
+ * Ensures serial execution per document by tracking executing jobs.
5034
+ * Implements dependency management through queue hints.
5015
5035
  */
5016
- declare class KyselyWriteCache implements IWriteCache {
5017
- private streams;
5018
- private lruTracker;
5019
- private keyframeStore;
5020
- private operationStore;
5021
- private registry;
5022
- private config;
5023
- constructor(keyframeStore: IKeyframeStore, operationStore: IOperationStore, registry: IDocumentModelRegistry, config: WriteCacheConfig);
5024
- withScopedStores(operationStore: IOperationStore, keyframeStore: IKeyframeStore): KyselyWriteCache;
5036
+ declare class InMemoryQueue implements IQueue {
5037
+ private eventBus;
5038
+ private resolver;
5039
+ private queues;
5040
+ private jobIdToQueueKey;
5041
+ private docIdToJobId;
5042
+ private jobIdToDocId;
5043
+ private completedJobs;
5044
+ private jobIndex;
5045
+ private isBlocked;
5046
+ private onDrainedCallback?;
5047
+ private isPausedFlag;
5048
+ constructor(eventBus: IEventBus, resolver: IDocumentModelResolver);
5049
+ private toErrorInfo;
5050
+ /**
5051
+ * Creates a unique key for a document/scope/branch combination
5052
+ */
5053
+ private createQueueKey;
5054
+ /**
5055
+ * Gets or creates a queue for the given key
5056
+ */
5057
+ private getQueue;
5025
5058
  /**
5026
- * Initializes the write cache.
5027
- * Currently a no-op as keyframe store lifecycle is managed externally.
5059
+ * Check if a document has any jobs currently executing
5028
5060
  */
5029
- startup(): Promise<void>;
5061
+ private isDocumentExecuting;
5030
5062
  /**
5031
- * Shuts down the write cache.
5032
- * Currently a no-op as keyframe store lifecycle is managed externally.
5063
+ * Mark a job as executing for its document
5033
5064
  */
5034
- shutdown(): Promise<void>;
5065
+ private markJobExecuting;
5035
5066
  /**
5036
- * Retrieves document state at a specific revision from cache or rebuilds it.
5037
- *
5038
- * Note: this returns a _shallow_ copy of the document.
5039
- *
5040
- * Cache hit path: Returns cached snapshot if available (O(1))
5041
- * Warm miss path: Rebuilds from cached base revision + incremental ops
5042
- * Cold miss path: Rebuilds from keyframe or from scratch using all operations
5043
- *
5044
- * @param documentId - The document identifier
5045
- * @param scope - The operation scope
5046
- * @param branch - The operation branch
5047
- * @param targetRevision - The target revision, or undefined for newest
5048
- * @param signal - Optional abort signal to cancel the operation
5049
- * @returns The document at the target revision
5050
- * @throws {Error} "Operation aborted" if signal is aborted
5051
- * @throws {ModuleNotFoundError} If document type not registered in registry
5052
- * @throws {Error} "Failed to rebuild document" if operation store fails
5053
- * @throws {Error} If reducer throws during operation application
5054
- * @throws {Error} If document serialization fails
5067
+ * Mark a job as no longer executing for its document
5055
5068
  */
5056
- getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
5069
+ private markJobComplete;
5057
5070
  /**
5058
- * Stores a document snapshot in the cache at a specific revision.
5059
- *
5060
- * The cached document is a shallow copy of the input with its operation history
5061
- * truncated to the last operation per scope and its clipboard cleared. This keeps
5062
- * memory use and copy costs constant regardless of operation count. Consumers of
5063
- * getState() must not rely on the full operation history being present; the only
5064
- * guaranteed invariant is that operations[scope].at(-1) reflects the latest
5065
- * operation index for each scope.
5066
- *
5067
- * Updates LRU tracker and may evict least recently used stream if at capacity.
5068
- * Asynchronously persists keyframes at configured intervals (fire-and-forget).
5069
- *
5070
- * @param documentId - The document identifier
5071
- * @param scope - The operation scope
5072
- * @param branch - The operation branch
5073
- * @param revision - The revision number
5074
- * @param document - The document to cache
5075
- * @throws {Error} If document serialization fails
5071
+ * Check if all dependencies for a job have been completed
5076
5072
  */
5077
- putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, position: SnapshotPosition): void;
5078
- private store;
5073
+ private areDependenciesMet;
5079
5074
  /**
5080
- * Invalidates cached document streams.
5081
- *
5082
- * Supports three invalidation scopes:
5083
- * - Document-level: invalidate(documentId) - removes all streams for document
5084
- * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
5085
- * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
5075
+ * Returns the head of the sub-queue if its dependencies are met, or null.
5086
5076
  *
5087
- * @param documentId - The document identifier
5088
- * @param scope - Optional scope to narrow invalidation
5089
- * @param branch - Optional branch to narrow invalidation (requires scope)
5090
- * @returns The number of streams evicted
5077
+ * The dispatcher only ever considers the head — a dep-blocked head holds
5078
+ * the rest of its sub-queue. This preserves per-(documentId, scope, branch)
5079
+ * FIFO regardless of how dependencies are authored, and makes the queue's
5080
+ * documented "serialized per document" invariant hold even when callers
5081
+ * omit queueHint dependencies on jobs that share a sub-queue.
5091
5082
  */
5092
- invalidate(documentId: string, scope?: string, branch?: string): number;
5083
+ private getNextJobWithMetDependencies;
5084
+ private getCreateDocumentType;
5085
+ enqueue(job: Job): Promise<void>;
5086
+ dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5087
+ dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5088
+ dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5089
+ size(documentId: string, scope: string, branch: string): Promise<number>;
5090
+ totalSize(): Promise<number>;
5091
+ remove(jobId: string): Promise<boolean>;
5092
+ clear(documentId: string, scope: string, branch: string): Promise<void>;
5093
+ clearAll(): Promise<void>;
5094
+ hasJobs(): Promise<boolean>;
5095
+ completeJob(jobId: string): Promise<void>;
5096
+ failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
5097
+ deferJob(jobId: string): void;
5098
+ retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
5093
5099
  /**
5094
- * Clears the entire cache, removing all cached document streams.
5095
- * Resets LRU tracking state. This operation always succeeds.
5100
+ * Check if the queue is drained and call the callback if it is
5096
5101
  */
5097
- clear(): void;
5102
+ private checkDrained;
5098
5103
  /**
5099
- * Retrieves a specific stream for a document. Exposed on the implementation
5100
- * for testing, but not on the interface.
5101
- *
5102
- * @internal
5104
+ * Returns true if and only if all jobs have been resolved.
5103
5105
  */
5104
- getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
5105
- private findNearestKeyframe;
5106
- private coldMissRebuild;
5106
+ get isDrained(): boolean;
5107
5107
  /**
5108
- * Copies the current document revisions onto the document. Overwrites the
5109
- * requested scope revision with the target revision, if provided.
5108
+ * Blocks the queue from accepting new jobs.
5109
+ * @param onDrained - Optional callback to call when the queue is drained
5110
5110
  */
5111
- private stampRevisions;
5112
- /** The stored operation at `index`, or undefined if it is no longer there. */
5113
- private operationAt;
5111
+ block(onDrained?: () => void): void;
5114
5112
  /**
5115
- * Resolves which module version to use for a given operation in phase 2.
5116
- *
5117
- * Uses the validated-upgrade boundary rules from D7:
5118
- * - If `input.revision` is present: op.index < revision[scope] → before the upgrade boundary
5119
- * - Otherwise: timestamp fallback
5120
- * - Falls back to final module version when neither is decidable
5113
+ * Unblocks the queue from accepting new jobs.
5121
5114
  */
5122
- private resolveModuleVersionForOp;
5123
- private warmMissRebuild;
5124
- private findNearestOlderSnapshot;
5125
- private makeStreamKey;
5126
- private getOrCreateStream;
5127
- private isKeyframeRevision;
5128
- }
5129
- //#endregion
5130
- //#region src/storage/kysely/store.d.ts
5131
- declare class KyselyOperationStore implements IOperationStore {
5132
- private db;
5133
- private trx?;
5134
- constructor(db: Kysely<Database$1>);
5135
- private get queryExecutor();
5136
- withTransaction(trx: Transaction<Database$1>): KyselyOperationStore;
5137
- apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal, condition?: AppendCondition): Promise<Operation[]>;
5138
- private resolveUniqueConstraint;
5139
- private executeApply;
5115
+ unblock(): void;
5140
5116
  /**
5141
- * Locks the written stream and every read-set stream, in sorted key order
5142
- * so that overlapping concurrent appends serialize rather than deadlock.
5143
- * The locks are still taken one row at a time, so the query preserves that
5144
- * order. It must stay separate from the guarded insert, which would
5145
- * otherwise read a snapshot taken before the locks were held.
5117
+ * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.
5146
5118
  */
5147
- private acquireStreamLocks;
5119
+ pause(): void;
5148
5120
  /**
5149
- * Inserts the staged operations with the condition compiled in as a WHERE
5150
- * NOT EXISTS guard, making the check and the append one statement. Returns
5151
- * the rows inserted; zero means the guard failed and nothing was written.
5121
+ * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.
5152
5122
  */
5153
- private insertGuarded;
5154
- private findIdempotentReplay;
5155
- getSince(documentId: string, scope: string, branch: string, revision: number, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
5156
- getSinceId(id: number, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<OperationWithContext$1>>;
5157
- getConflicting(documentId: string, scope: string, branch: string, minTimestamp: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
5158
- getRevisions(documentId: string, branch: string, signal?: AbortSignal): Promise<DocumentRevisions>;
5159
- private rowToOperation;
5160
- private rowToOperationWithContext;
5123
+ resume(): Promise<void>;
5124
+ /**
5125
+ * Returns whether job dequeuing is paused.
5126
+ */
5127
+ get paused(): boolean;
5128
+ /**
5129
+ * Returns all pending jobs across all queues.
5130
+ */
5131
+ getPendingJobs(): Job[];
5132
+ /**
5133
+ * Returns a map of document IDs to sets of executing job IDs.
5134
+ */
5135
+ getExecutingJobIds(): Map<string, Set<string>>;
5136
+ /**
5137
+ * Returns a job by ID from the job index.
5138
+ */
5139
+ getJob(jobId: string): Job | undefined;
5161
5140
  }
5162
5141
  //#endregion
5163
- //#region src/storage/kysely/keyframe-store.d.ts
5164
- declare class KyselyKeyframeStore implements IKeyframeStore {
5165
- private db;
5166
- private trx?;
5167
- constructor(db: Kysely<Database$1>);
5168
- private get queryExecutor();
5169
- withTransaction(trx: Transaction<Database$1>): KyselyKeyframeStore;
5170
- putKeyframe(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, signal?: AbortSignal): Promise<void>;
5171
- findNearestKeyframe(documentId: string, scope: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<{
5172
- revision: number;
5173
- document: PHDocument;
5174
- } | undefined>;
5175
- listKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<Array<{
5176
- scope: string;
5177
- branch: string;
5178
- revision: number;
5179
- document: PHDocument;
5180
- }>>;
5181
- deleteKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<number>;
5142
+ //#region src/job-tracker/in-memory-job-tracker.d.ts
5143
+ /**
5144
+ * In-memory implementation of IJobTracker.
5145
+ * Maintains job status in a Map for synchronous access.
5146
+ * Subscribes to operation events to update job states.
5147
+ */
5148
+ declare class InMemoryJobTracker implements IJobTracker {
5149
+ private eventBus;
5150
+ private jobs;
5151
+ private unsubscribers;
5152
+ constructor(eventBus: IEventBus);
5153
+ private subscribeToEvents;
5154
+ private handleWriteReady;
5155
+ private handleReadReady;
5156
+ private handleJobFailed;
5157
+ shutdown(): void;
5158
+ registerJob(jobInfo: JobInfo): void;
5159
+ markRunning(jobId: string): void;
5160
+ markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
5161
+ getJobStatus(jobId: string): JobInfo | null;
5182
5162
  }
5183
5163
  //#endregion
5184
- //#region src/executor/execution-scope.d.ts
5185
- interface ExecutionStores {
5186
- operationStore: IOperationStore;
5187
- operationIndex: IOperationIndex;
5188
- writeCache: IWriteCache;
5189
- documentMetaCache: IDocumentMetaCache;
5190
- collectionMembershipCache: ICollectionMembershipCache;
5191
- }
5192
- interface IExecutionScope {
5193
- run<T>(fn: (stores: ExecutionStores) => Promise<T>, signal?: AbortSignal): Promise<T>;
5164
+ //#region src/executor/simple-job-executor-manager.d.ts
5165
+ type JobExecutorFactory = () => IJobExecutor;
5166
+ /**
5167
+ * Manages multiple job executors and coordinates job distribution.
5168
+ * Listens for job available events and dispatches jobs to executors.
5169
+ */
5170
+ declare class SimpleJobExecutorManager implements IJobExecutorManager {
5171
+ private executorFactory;
5172
+ private eventBus;
5173
+ private queue;
5174
+ private jobTracker;
5175
+ private logger;
5176
+ private resolver;
5177
+ private executors;
5178
+ private isRunning;
5179
+ private activeJobs;
5180
+ private totalJobsProcessed;
5181
+ private unsubscribe?;
5182
+ private deferredJobs;
5183
+ private resultHandler;
5184
+ private jobTimeoutMs;
5185
+ constructor(executorFactory: JobExecutorFactory, eventBus: IEventBus, queue: IQueue, jobTracker: IJobTracker, logger: ILogger, resolver: IDocumentModelResolver, jobTimeoutMs?: number);
5186
+ start(numExecutors: number): Promise<void>;
5187
+ stop(graceful?: boolean): Promise<void>;
5188
+ getExecutors(): IJobExecutor[];
5189
+ getStatus(): ExecutorManagerStatus;
5190
+ private processNextJob;
5191
+ private checkForMoreJobs;
5192
+ private processExistingJobs;
5193
+ private flushDeferredJobs;
5194
5194
  }
5195
5195
  //#endregion
5196
5196
  //#region src/executor/simple-job-executor.d.ts
@@ -5221,6 +5221,13 @@ declare class SimpleJobExecutor implements IJobExecutor {
5221
5221
  private getCollectionMembershipsForOperations;
5222
5222
  private processActions;
5223
5223
  private executeRegularAction;
5224
+ /**
5225
+ * Orders a write by timestamp. The caller supplies the timestamp, so a write
5226
+ * can belong before operations already stored, and appending it at the tail
5227
+ * would leave the scope out of order. The operations it belongs before are
5228
+ * re-appended alongside it, the way a load reshuffles.
5229
+ */
5230
+ private positionByTimestamp;
5224
5231
  /**
5225
5232
  * Re-evaluates the document when a write meets both criteria: it was written
5226
5233
  * to a stream the model reads, and it is timestamped before an operation