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

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,300 +2109,438 @@ 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
- * A verdict reached 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.
2340
- *
2341
- * @see Wire Protocol Reference wiki page
2342
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
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.
2343
2303
  */
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;
2350
- };
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
2351
2353
  /**
2352
- * Payload the worker reports back when a job's write phase is complete.
2354
+ * A JSON-clonable value safe to send across the worker IPC boundary.
2353
2355
  *
2354
- * Parent fills `collectionMemberships` at emission time, so it is
2355
- * intentionally absent from the worker -> parent message.
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.
2356
2359
  *
2357
2360
  * @see Wire Protocol Reference wiki page
2358
2361
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2359
2362
  */
2360
- type JobWriteReadyPayload = {
2361
- operations: OperationWithContext$1[];
2362
- jobMeta: JobMeta;
2363
+ type SanitizedArg = null | boolean | number | string | ErrorInfo | SanitizedArg[] | {
2364
+ [key: string]: SanitizedArg;
2363
2365
  };
2364
2366
  /**
2365
- * Initializes a freshly spawned worker with the configuration and
2366
- * factories it needs to start executing jobs.
2367
+ * Structured representation of an Error for IPC transport.
2368
+ *
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.
2367
2372
  *
2368
2373
  * @see Wire Protocol Reference wiki page
2369
2374
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2370
2375
  */
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;
2376
+ type ErrorInfo = {
2377
+ name: string;
2378
+ message: string;
2379
+ stack?: string;
2380
+ cause?: ErrorInfo;
2380
2381
  };
2381
2382
  /**
2382
- * Dispatches a job to the worker for execution.
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.
2383
2387
  *
2384
2388
  * @see Wire Protocol Reference wiki page
2385
2389
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2386
2390
  */
2387
- type ExecuteMessage = {
2388
- type: "execute";
2389
- correlationId: string;
2390
- job: Job;
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;
2391
2397
  };
2392
2398
  /**
2393
- * Requests cancellation of an in-flight job.
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.
2394
2404
  *
2395
2405
  * @see Wire Protocol Reference wiki page
2396
2406
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2397
2407
  */
2398
- type AbortMessage = {
2399
- type: "abort";
2400
- correlationId: string; /** correlationId of the `execute` message being aborted. */
2401
- targetCorrelationId: string;
2402
- reason?: string;
2408
+ type FactorySpec = {
2409
+ module: ModuleRef;
2410
+ initArgs?: SanitizedArg;
2403
2411
  };
2404
2412
  /**
2405
- * Asks the worker to drain in-flight work and exit.
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.
2417
+ *
2418
+ * @see Wire Protocol Reference wiki page
2419
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2420
+ */
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
+ };
2542
+ /**
2543
+ * Asks the worker to drain in-flight work and exit.
2406
2544
  *
2407
2545
  * @see Wire Protocol Reference wiki page
2408
2546
  * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
@@ -2533,29 +2671,539 @@ type MetricsMessage = {
2533
2671
  };
2534
2672
  };
2535
2673
  /**
2536
- * Snapshot of one worker pool's acquire-wait samples and pool-stat counters,
2537
- * forwarded periodically so the host can re-record into the shared
2538
- * pg.Pool histogram and observable gauges. The worker owns the real
2539
- * pg.Pool; the host's {@link PoolInstrumentation} is a forwarder driven
2540
- * by these messages.
2674
+ * Snapshot of one worker pool's acquire-wait samples and pool-stat counters,
2675
+ * forwarded periodically so the host can re-record into the shared
2676
+ * pg.Pool histogram and observable gauges. The worker owns the real
2677
+ * pg.Pool; the host's {@link PoolInstrumentation} is a forwarder driven
2678
+ * by these messages.
2679
+ */
2680
+ type PoolAcquireSamplesMessage = {
2681
+ type: "pool-acquire-samples";
2682
+ workerId: string; /** Stable identifier matching the host-side instrumentation name (e.g. "worker-0"). */
2683
+ poolName: string; /** Epoch milliseconds the worker generated the batch. */
2684
+ timestamp: number; /** Acquire-wait durations (ms) accumulated since the previous batch. */
2685
+ durations: number[]; /** Most recent pg.Pool counter snapshot at batch send time. */
2686
+ size: number;
2687
+ idle: number;
2688
+ waiting: number;
2689
+ };
2690
+ /**
2691
+ * Union of all messages a worker may send to the parent.
2692
+ *
2693
+ * @see Wire Protocol Reference wiki page
2694
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2695
+ */
2696
+ type WorkerMessage = ReadyMessage | ResultMessage | ModelLoadedMessage | ModelLoadFailedMessage | LogMessage | HeartbeatMessage | MetricsMessage | PoolAcquireSamplesMessage;
2697
+ //#endregion
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
+ };
2710
+ /**
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.
2714
+ */
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;
2725
+ };
2726
+ /**
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.
2730
+ *
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.
2736
+ */
2737
+ interface IDocumentModelLoader {
2738
+ load(documentType: string): Promise<DocumentModelSource>;
2739
+ }
2740
+ /**
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.
2744
+ */
2745
+ interface IDocumentModelRegistry {
2746
+ /**
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
2753
+ */
2754
+ registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
2755
+ /**
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
2760
+ */
2761
+ unregisterModules(...documentTypes: string[]): boolean;
2762
+ /**
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
2770
+ */
2771
+ getModule(documentType: string, version?: number): DocumentModelModule<any>;
2772
+ /**
2773
+ * Get all registered document model modules.
2774
+ *
2775
+ * @returns Array of all registered modules
2776
+ */
2777
+ getAllModules(): DocumentModelModule<any>[];
2778
+ /**
2779
+ * Clear all registered modules and upgrade manifests.
2780
+ */
2781
+ clear(): void;
2782
+ /**
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
2788
+ */
2789
+ getSupportedVersions(documentType: string): number[];
2790
+ /**
2791
+ * Get the latest (highest) version number for a document type.
2792
+ *
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
2796
+ */
2797
+ getLatestVersion(documentType: string): number;
2798
+ /**
2799
+ * Register upgrade manifests that define upgrade paths between versions.
2800
+ * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
2801
+ *
2802
+ * @param manifests Upgrade manifests to register
2803
+ * @returns Array of results, one per manifest, indicating success or failure
2804
+ */
2805
+ registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
2806
+ /**
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.
2814
+ *
2815
+ * @param documentType The document type identifier
2816
+ * @returns The upgrade manifest
2817
+ * @throws ManifestNotFoundError if no manifest is registered for the document type
2818
+ */
2819
+ getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
2820
+ /**
2821
+ * Compute the upgrade path from one version to another.
2822
+ * Returns the sequence of upgrade transitions needed.
2823
+ *
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
2831
+ */
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>;
2845
+ }
2846
+ //#endregion
2847
+ //#region src/cache/buffer/ring-buffer.d.ts
2848
+ /**
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
2856
+ */
2857
+ declare class RingBuffer<T> {
2858
+ private buffer;
2859
+ private head;
2860
+ private size;
2861
+ private capacity;
2862
+ constructor(capacity: number);
2863
+ /**
2864
+ * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
2865
+ *
2866
+ * @param item - The item to add
2867
+ */
2868
+ push(item: T): void;
2869
+ /**
2870
+ * Returns all items in the buffer in chronological order (oldest to newest).
2871
+ *
2872
+ * @returns Array of items in insertion order
2873
+ */
2874
+ getAll(): T[];
2875
+ /**
2876
+ * Clears all items from the buffer.
2877
+ */
2878
+ clear(): void;
2879
+ /**
2880
+ * Gets the current number of items in the buffer.
2881
+ */
2882
+ get length(): number;
2883
+ }
2884
+ //#endregion
2885
+ //#region src/cache/kysely-write-cache.d.ts
2886
+ type DocumentStream = {
2887
+ key: string;
2888
+ ringBuffer: RingBuffer<CachedSnapshot>;
2889
+ };
2890
+ /**
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
+ * ```
2925
+ */
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;
2935
+ /**
2936
+ * Initializes the write cache.
2937
+ * Currently a no-op as keyframe store lifecycle is managed externally.
2938
+ */
2939
+ startup(): Promise<void>;
2940
+ /**
2941
+ * Shuts down the write cache.
2942
+ * Currently a no-op as keyframe store lifecycle is managed externally.
2943
+ */
2944
+ shutdown(): Promise<void>;
2945
+ /**
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
2965
+ */
2966
+ getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
2967
+ /**
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
2986
+ */
2987
+ putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, position: SnapshotPosition): void;
2988
+ private store;
2989
+ /**
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
3001
+ */
3002
+ invalidate(documentId: string, scope?: string, branch?: string): number;
3003
+ /**
3004
+ * Clears the entire cache, removing all cached document streams.
3005
+ * Resets LRU tracking state. This operation always succeeds.
3006
+ */
3007
+ clear(): void;
3008
+ /**
3009
+ * Retrieves a specific stream for a document. Exposed on the implementation
3010
+ * for testing, but not on the interface.
3011
+ *
3012
+ * @internal
3013
+ */
3014
+ getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
3015
+ private findNearestKeyframe;
3016
+ private coldMissRebuild;
3017
+ /**
3018
+ * Copies the current document revisions onto the document. Overwrites the
3019
+ * requested scope revision with the target revision, if provided.
3020
+ */
3021
+ private stampRevisions;
3022
+ /** The stored operation at `index`, or undefined if it is no longer there. */
3023
+ private operationAt;
3024
+ /**
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
3031
+ */
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;
3050
+ /**
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.
3056
+ */
3057
+ private acquireStreamLocks;
3058
+ /**
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.
3062
+ */
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;
3071
+ }
3072
+ //#endregion
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>;
3092
+ }
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;
3101
+ }
3102
+ interface IExecutionScope {
3103
+ run<T>(fn: (stores: ExecutionStores) => Promise<T>, signal?: AbortSignal): Promise<T>;
3104
+ }
3105
+ //#endregion
3106
+ //#region src/executor/types.d.ts
3107
+ /**
3108
+ * Represents the result of a job execution
3109
+ */
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>;
3123
+ };
3124
+ /**
3125
+ * Enforcement the reactor performs, each off by default.
3126
+ *
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.
3130
+ */
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
+ };
3140
+ /**
3141
+ * Configuration options for the job executor
3142
+ */
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
2541
3156
  */
2542
- type PoolAcquireSamplesMessage = {
2543
- type: "pool-acquire-samples";
2544
- workerId: string; /** Stable identifier matching the host-side instrumentation name (e.g. "worker-0"). */
2545
- poolName: string; /** Epoch milliseconds the worker generated the batch. */
2546
- timestamp: number; /** Acquire-wait durations (ms) accumulated since the previous batch. */
2547
- durations: number[]; /** Most recent pg.Pool counter snapshot at batch send time. */
2548
- size: number;
2549
- idle: number;
2550
- waiting: number;
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;
2551
3163
  };
2552
3164
  /**
2553
- * Union of all messages a worker may send to the parent.
2554
- *
2555
- * @see Wire Protocol Reference wiki page
2556
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3165
+ * Event data for job execution events
2557
3166
  */
2558
- type WorkerMessage = ReadyMessage | ResultMessage | ModelLoadedMessage | ModelLoadFailedMessage | LogMessage | HeartbeatMessage | MetricsMessage | PoolAcquireSamplesMessage;
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.
3175
+ */
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
+ };
2559
3207
  //#endregion
2560
3208
  //#region src/executor/interfaces.d.ts
2561
3209
  /**
@@ -2795,242 +3443,93 @@ interface IQueue {
2795
3443
  */
2796
3444
  hasJobs(): Promise<boolean>;
2797
3445
  /**
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
2801
- */
2802
- completeJob(jobId: string): Promise<void>;
2803
- /**
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
2808
- */
2809
- failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
2810
- /**
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
2817
- */
2818
- retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
2819
- /**
2820
- * Returns true if and only if all jobs have been resolved.
2821
- */
2822
- get isDrained(): boolean;
2823
- /**
2824
- * Blocks the queue from accepting new jobs.
2825
- * @param onDrained - Optional callback to call when the queue is drained
2826
- */
2827
- block(onDrained?: () => void): void;
2828
- /**
2829
- * Unblocks the queue from accepting new jobs.
2830
- */
2831
- unblock(): void;
2832
- }
2833
- //#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>;
2865
- }
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>;
2877
- }
2878
- interface DocumentViewDatabase {
2879
- ViewState: ViewStateTable;
2880
- DocumentSnapshot: DocumentSnapshotTable;
2881
- SlugMapping: SlugMappingTable;
2882
- ProcessorCursor: ProcessorCursorTable;
2883
- }
2884
- type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;
2885
- //#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
- };
2898
- /**
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.
2902
- */
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;
2913
- };
2914
- /**
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.
2918
- *
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.
2924
- */
2925
- interface IDocumentModelLoader {
2926
- load(documentType: string): Promise<DocumentModelSource>;
2927
- }
2928
- /**
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.
2932
- */
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
2941
- */
2942
- registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
2943
- /**
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
2948
- */
2949
- unregisterModules(...documentTypes: string[]): boolean;
2950
- /**
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
2958
- */
2959
- getModule(documentType: string, version?: number): DocumentModelModule<any>;
2960
- /**
2961
- * Get all registered document model modules.
2962
- *
2963
- * @returns Array of all registered modules
2964
- */
2965
- getAllModules(): DocumentModelModule<any>[];
2966
- /**
2967
- * Clear all registered modules and upgrade manifests.
2968
- */
2969
- clear(): void;
2970
- /**
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
2976
- */
2977
- getSupportedVersions(documentType: string): number[];
2978
- /**
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
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
2984
3449
  */
2985
- getLatestVersion(documentType: string): number;
3450
+ completeJob(jobId: string): Promise<void>;
2986
3451
  /**
2987
- * Register upgrade manifests that define upgrade paths between versions.
2988
- * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
2989
- *
2990
- * @param manifests Upgrade manifests to register
2991
- * @returns Array of results, one per manifest, indicating success or failure
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
2992
3456
  */
2993
- registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
3457
+ failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
2994
3458
  /**
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;
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>;
3000
3467
  /**
3001
- * Get the upgrade manifest for a document type.
3002
- *
3003
- * @param documentType The document type identifier
3004
- * @returns The upgrade manifest
3005
- * @throws ManifestNotFoundError if no manifest is registered for the document type
3468
+ * Returns true if and only if all jobs have been resolved.
3006
3469
  */
3007
- getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
3470
+ get isDrained(): boolean;
3008
3471
  /**
3009
- * Compute the upgrade path from one version to another.
3010
- * Returns the sequence of upgrade transitions needed.
3011
- *
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
3472
+ * Blocks the queue from accepting new jobs.
3473
+ * @param onDrained - Optional callback to call when the queue is drained
3019
3474
  */
3020
- computeUpgradePath(documentType: string, fromVersion: number, toVersion: number): UpgradeTransition[];
3475
+ block(onDrained?: () => void): void;
3021
3476
  /**
3022
- * Get the upgrade reducer for a single-step version transition.
3023
- *
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
3477
+ * Unblocks the queue from accepting new jobs.
3031
3478
  */
3032
- getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
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>;
3033
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>;
3034
3533
  //#endregion
3035
3534
  //#region src/shared/consistency-tracker.d.ts
3036
3535
  interface IConsistencyTracker {
@@ -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.
@@ -3944,17 +4313,11 @@ declare class DriveClient implements IDriveClient {
3944
4313
  renameNode(driveIdentifier: string, nodeId: string, name: string, signal?: AbortSignal): Promise<Node>;
3945
4314
  setPreferredEditorOnNode(nodeId: string, preferredEditor: string | null, signal?: AbortSignal): Promise<PHDocument>;
3946
4315
  moveNode(driveIdentifier: string, srcNodeId: string, targetParentFolderId: string | undefined, signal?: AbortSignal): Promise<DocumentDriveDocument>;
3947
- copyNode(driveIdentifier: string, srcNodeId: string, targetParentFolderId: string | undefined, signal?: AbortSignal): Promise<DocumentDriveDocument>;
3948
- getNode(driveIdentifier: string, nodeId: string, signal?: AbortSignal): Promise<Node>;
3949
- listNodes(driveIdentifier: string, parentFolder?: string | null, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Node>>;
3950
- private documentExists;
3951
- private removeFileNode;
3952
- }
3953
- //#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;
4316
+ copyNode(driveIdentifier: string, srcNodeId: string, targetParentFolderId: string | undefined, signal?: AbortSignal): Promise<DocumentDriveDocument>;
4317
+ getNode(driveIdentifier: string, nodeId: string, signal?: AbortSignal): Promise<Node>;
4318
+ listNodes(driveIdentifier: string, parentFolder?: string | null, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Node>>;
4319
+ private documentExists;
4320
+ private removeFileNode;
3958
4321
  }
3959
4322
  //#endregion
3960
4323
  //#region src/registry/document-model-resolver.d.ts
@@ -4578,619 +4941,256 @@ interface ParsedDriveUrl {
4578
4941
  * e.g., "https://example.com/api/reactor/d/abc123" -> { ..., graphqlEndpoint: "https://example.com/api/reactor/graphql/r" }
4579
4942
  */
4580
4943
  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;
4599
- };
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
- //#endregion
4608
- //#region src/subs/default-error-handler.d.ts
4609
- /**
4610
- * Default error handler that re-throws subscription errors.
4611
- * This ensures that errors are not silently swallowed.
4612
- */
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;
4646
- /**
4647
- * Notify subscribers about relationship changes
4648
- */
4649
- notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
4650
- /**
4651
- * Clear all subscriptions
4652
- */
4653
- clearAll(): void;
4654
- private filterDocumentIds;
4655
- private filterDocuments;
4656
- private matchesRelationshipFilter;
4657
- }
4658
- //#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>;
4664
- }
4665
- //#endregion
4666
- //#region src/queue/queue.d.ts
4667
- /**
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.
4672
- */
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>;
4736
- /**
4737
- * Check if the queue is drained and call the callback if it is
4738
- */
4739
- private checkDrained;
4740
- /**
4741
- * Returns true if and only if all jobs have been resolved.
4742
- */
4743
- get isDrained(): boolean;
4744
- /**
4745
- * Blocks the queue from accepting new jobs.
4746
- * @param onDrained - Optional callback to call when the queue is drained
4747
- */
4748
- block(onDrained?: () => void): void;
4749
- /**
4750
- * Unblocks the queue from accepting new jobs.
4751
- */
4752
- unblock(): void;
4753
- /**
4754
- * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.
4755
- */
4756
- pause(): void;
4757
- /**
4758
- * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.
4759
- */
4760
- resume(): Promise<void>;
4761
- /**
4762
- * Returns whether job dequeuing is paused.
4763
- */
4764
- get paused(): boolean;
4765
- /**
4766
- * Returns all pending jobs across all queues.
4767
- */
4768
- getPendingJobs(): Job[];
4769
- /**
4770
- * Returns a map of document IDs to sets of executing job IDs.
4771
- */
4772
- getExecutingJobIds(): Map<string, Set<string>>;
4773
- /**
4774
- * Returns a job by ID from the job index.
4775
- */
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
- }
4944
+ /**
4945
+ * Extract drive ID from a drive URL.
4946
+ */
4947
+ declare function driveIdFromUrl(url: string): string;
4832
4948
  //#endregion
4833
- //#region src/cache/document-meta-cache-types.d.ts
4949
+ //#region src/shared/factories.d.ts
4834
4950
  /**
4835
- * Cached document metadata from the "document" scope.
4951
+ * Factory method to create a ShutdownStatus that can be updated
4836
4952
  *
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.
4953
+ * @param initialState - Initial shutdown state (default: false)
4954
+ * @returns A tuple of [ShutdownStatus, setShutdown function, setCompleted function]
4840
4955
  */
4841
- type CachedDocumentMeta = {
4842
- /**
4843
- * The full PHDocumentState from document.state.document.
4844
- * Contains version, hash, isDeleted, deletedAtUtcIso, etc.
4845
- */
4846
- state: PHDocumentState;
4847
- /**
4848
- * The document type (from header), cached for convenience.
4849
- */
4850
- documentType: string;
4851
- /**
4852
- * The revision of the document scope when this metadata was captured.
4853
- * Used for cache invalidation and consistency checks.
4854
- */
4855
- documentScopeRevision: 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;
4856
4962
  };
4857
4963
  /**
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
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`.
4869
4968
  */
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>;
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;
4901
4997
  /**
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
4998
+ * Notify subscribers about created documents
4910
4999
  */
4911
- putDocumentMeta(documentId: string, branch: string, meta: CachedDocumentMeta): void;
5000
+ notifyDocumentsCreated(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4912
5001
  /**
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
5002
+ * Notify subscribers about deleted documents
4921
5003
  */
4922
- invalidate(documentId: string, branch?: string): number;
5004
+ notifyDocumentsDeleted(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4923
5005
  /**
4924
- * Clears all cached document metadata.
5006
+ * Notify subscribers about updated documents
4925
5007
  */
4926
- clear(): void;
5008
+ notifyDocumentsUpdated(documents: PHDocument[]): void;
4927
5009
  /**
4928
- * Performs startup initialization.
5010
+ * Notify subscribers about relationship changes
4929
5011
  */
4930
- startup(): Promise<void>;
5012
+ notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
4931
5013
  /**
4932
- * Performs graceful shutdown.
5014
+ * Clear all subscriptions
4933
5015
  */
4934
- shutdown(): Promise<void>;
5016
+ clearAll(): void;
5017
+ private filterDocumentIds;
5018
+ private filterDocuments;
5019
+ private matchesRelationshipFilter;
4935
5020
  }
4936
5021
  //#endregion
4937
- //#region src/cache/buffer/ring-buffer.d.ts
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
4938
5030
  /**
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().
4944
- *
4945
- * @template T - The type of items stored in the buffer
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.
4946
5035
  */
4947
- declare class RingBuffer<T> {
4948
- private buffer;
4949
- private head;
4950
- private size;
4951
- private capacity;
4952
- constructor(capacity: number);
4953
- /**
4954
- * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
4955
- *
4956
- * @param item - The item to add
4957
- */
4958
- push(item: T): void;
4959
- /**
4960
- * Returns all items in the buffer in chronological order (oldest to newest).
4961
- *
4962
- * @returns Array of items in insertion order
4963
- */
4964
- getAll(): T[];
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;
4965
5050
  /**
4966
- * Clears all items from the buffer.
5051
+ * Creates a unique key for a document/scope/branch combination
4967
5052
  */
4968
- clear(): void;
5053
+ private createQueueKey;
4969
5054
  /**
4970
- * Gets the current number of items in the buffer.
5055
+ * Gets or creates a queue for the given key
4971
5056
  */
4972
- get length(): number;
4973
- }
4974
- //#endregion
4975
- //#region src/cache/kysely-write-cache.d.ts
4976
- type DocumentStream = {
4977
- key: string;
4978
- ringBuffer: RingBuffer<CachedSnapshot>;
4979
- };
4980
- /**
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
- * ```
5015
- */
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;
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
@@ -5222,12 +5222,26 @@ declare class SimpleJobExecutor implements IJobExecutor {
5222
5222
  private processActions;
5223
5223
  private executeRegularAction;
5224
5224
  /**
5225
- * If an operation happened that could change a decision model verdict, we
5226
- * need to re-evaluate across the streams that could be affected. Previously
5227
- * approved operations may need to be denied, which will apply new denied
5228
- * operations with appropriate skip.
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;
5231
+ /**
5232
+ * Re-evaluates the document when a write meets both criteria: it was written
5233
+ * to a stream the model reads, and it is timestamped before an operation
5234
+ * already stored. The caller supplies the timestamp and the reactor does not replace
5235
+ * it, so a mutation job can write such an operation just as a load job can,
5236
+ * which is why both executeJob and executeLoadJob call this.
5237
+ */
5238
+ private reevaluateIfCriteriaMet;
5239
+ /**
5240
+ * Re-evaluates every scope the model evaluates. Where an operation's
5241
+ * evaluation differs from what is stored, the tail from that operation is
5242
+ * re-appended, carrying a skip that spans the indices it supersedes.
5229
5243
  */
5230
- private reevaluateReadingScopes;
5244
+ private reevaluateDocument;
5231
5245
  private executeLoadJob;
5232
5246
  private accumulateResultOrReturnError;
5233
5247
  }
@@ -5320,7 +5334,7 @@ type DecisionContext = {
5320
5334
  type Projection<M> = {
5321
5335
  query: StreamQuery | ((model: Partial<M>) => StreamQuery[]);
5322
5336
  /**
5323
- * Action types in this stream that can change a verdict. Reads of the stream
5337
+ * Action types in this stream that can change an evaluation. Reads of the stream
5324
5338
  * are filtered to these, so anything left out is invisible to a decision.
5325
5339
  */
5326
5340
  decidingActions: string[]; /** Applies one of this stream's operations while deciding. */
@@ -5331,9 +5345,9 @@ type DecisionModel<M> = {
5331
5345
  projections: { [K in keyof M]: Projection<M> };
5332
5346
  /**
5333
5347
  * Whether or not this model decides about operations in a given scope. That
5334
- * is, a scope it reads is not necessarily one it judges, and vise-versa.
5348
+ * is, a scope it reads is not necessarily one it evaluates, and vise-versa.
5335
5349
  */
5336
- judgesScope(scope: string): boolean;
5350
+ evaluatesScope(scope: string): boolean;
5337
5351
  decide(model: M, subject: AuthSubject, request: AuthRequest, ctx: DecisionContext): AuthDecision;
5338
5352
  };
5339
5353
  /** A built model plus the read-set condition recording what the build read. */