@powerhousedao/reactor 6.2.2-dev.6 → 6.2.2-dev.60

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
@@ -1,4 +1,4 @@
1
- import { Action, CreateDocumentActionInput, DocumentModelModule, ISigner, Operation, OperationContext, OperationWithContext, OperationWithContext as OperationWithContext$1, PHDocument, PHDocumentState, SignatureVerificationHandler, UpgradeDocumentActionInput, UpgradeManifest, UpgradeReducer, UpgradeTransition, actions as documentActions } from "@powerhousedao/shared/document-model";
1
+ import { Action, AuthRequest, AuthSubject, CreateDocumentActionInput, DocumentModelModule, ISigner, Operation, OperationContext, OperationWithContext, OperationWithContext as OperationWithContext$1, PHAuthState, PHDocument, PHDocumentState, SignatureVerificationHandler, UpgradeDocumentActionInput, UpgradeManifest, UpgradeReducer, UpgradeTransition, actions as documentActions } from "@powerhousedao/shared/document-model";
2
2
  import { DocumentDriveDocument, DriveInput, FolderNode, Node } from "@powerhousedao/shared/document-drive";
3
3
  import { ILogger } from "document-model";
4
4
  import * as kysely from "kysely";
@@ -57,7 +57,14 @@ declare function updateRelationshipAction(sourceId: string, targetId: string, re
57
57
  declare function removeRelationshipAction(sourceId: string, targetId: string, relationshipType?: string): Action;
58
58
  //#endregion
59
59
  //#region src/queue/types.d.ts
60
- type JobKind = "mutation" | "load";
60
+ /**
61
+ * What a job carries: a mutation applies actions, a load imports synced
62
+ * operations, and a reevaluation re-judges a document's stored operations
63
+ * because a read-set stream elsewhere (a group document) changed. A
64
+ * reevaluation job carries no actions or operations; its work is derived
65
+ * from what is already stored.
66
+ */
67
+ type JobKind = "mutation" | "load" | "reevaluation";
61
68
  /**
62
69
  * State of a job in the queue
63
70
  */
@@ -69,6 +76,17 @@ declare enum JobQueueState {
69
76
  RUNNING = 3,
70
77
  RESOLVED = 4
71
78
  }
79
+ /**
80
+ * How a retry is accounted against the job's retry limit.
81
+ * - `CountAgainstLimit` (default): a fault; the job eventually exhausts its
82
+ * retries and fails terminally.
83
+ * - `ExemptFromLimit`: not a fault, so the attempt is not charged to the job.
84
+ * Used for concurrency conflicts, where the retry does new work.
85
+ */
86
+ declare enum RetryAccounting {
87
+ CountAgainstLimit = "count-against-limit",
88
+ ExemptFromLimit = "exempt-from-limit"
89
+ }
72
90
  /**
73
91
  * Interface for a job execution handle
74
92
  */
@@ -127,9 +145,14 @@ type JobAvailableEvent = {
127
145
  //#endregion
128
146
  //#region src/shared/types.d.ts
129
147
  /**
130
- * Information about an error including message and stack trace.
148
+ * Information about an error including its name, message and stack trace.
149
+ *
150
+ * The name is what survives the crossing out of the executor: a consumer that
151
+ * has to tell a terminal failure from a retryable one, or classify a dead
152
+ * letter, only ever sees this record rather than the thrown error.
131
153
  */
132
154
  type ErrorInfo$1 = {
155
+ name: string;
133
156
  message: string;
134
157
  stack: string;
135
158
  };
@@ -230,6 +253,11 @@ type ViewFilter = {
230
253
  branch?: string;
231
254
  scopes?: string[];
232
255
  revision?: number;
256
+ /**
257
+ * Read subject for the IReactorClient read gate; defaults to the client's
258
+ * signer. Set per request when serving many principals. Ignored by IReactor.
259
+ */
260
+ subject?: AuthSubject;
233
261
  };
234
262
  /**
235
263
  * Describes filter options for searching documents.
@@ -328,6 +356,16 @@ interface IReadModelCoordinator {
328
356
  */
329
357
  getChainDepth(): number;
330
358
  }
359
+ type ReadModelRegistrationStage = "pre_ready" | "post_ready";
360
+ /**
361
+ * Optional capability exposed by coordinators that support adding read models
362
+ * after construction. Custom and remote coordinators are not required to
363
+ * implement it.
364
+ */
365
+ interface ILiveReadModelCoordinator extends IReadModelCoordinator {
366
+ addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void;
367
+ }
368
+ declare function supportsLiveReadModelRegistration(coordinator: IReadModelCoordinator): coordinator is ILiveReadModelCoordinator;
331
369
  //#endregion
332
370
  //#region src/sync/types.d.ts
333
371
  declare enum ChannelScheme {
@@ -361,6 +399,16 @@ type RemoteOptions = {
361
399
  * Polling cadence for this remote. Defaults to `PollBehavior.Auto` when omitted.
362
400
  */
363
401
  pollBehavior?: PollBehavior;
402
+ /**
403
+ * The address this channel belongs to, once one has claimed it.
404
+ *
405
+ * Undefined is unbound and adoptable: a channel created anonymously, or one
406
+ * that predates binding. The first authenticated subject to poll or touch it
407
+ * takes it, and from then on it answers to that address alone. There is no
408
+ * sentinel for "anonymous" -- an anonymous holder is exactly one that has not
409
+ * claimed the channel.
410
+ */
411
+ boundAddress?: string;
364
412
  };
365
413
  type RemoteFilter = {
366
414
  documentId: string[];
@@ -398,7 +446,13 @@ declare enum ChannelErrorSource {
398
446
  Inbox = "inbox",
399
447
  Outbox = "outbox"
400
448
  }
401
- type SyncOperationErrorType = "SIGNATURE_INVALID" | "HASH_MISMATCH" | "LIBRARY_ERROR" | "MISSING_OPERATIONS" | "EXCESSIVE_SHUFFLE" | "GRACEFUL_ABORT";
449
+ type SyncOperationErrorType = "SIGNATURE_INVALID" | "HASH_MISMATCH" | "LIBRARY_ERROR" | "MISSING_OPERATIONS" | "EXCESSIVE_SHUFFLE" | "GRACEFUL_ABORT"
450
+ /**
451
+ * An arriving auth operation did not exceed the local auth head. Exempt from
452
+ * quarantine, because reconciling the two policies needs the traffic a
453
+ * quarantine would stop.
454
+ */
455
+ | "AUTH_TIMESTAMP_NOT_MONOTONIC" /** An arriving operation carried a timestamp that is not an ISO-8601 instant. */ | "INVALID_TIMESTAMP" /** No classification applies, including rows written before the field. */ | "UNCLASSIFIED";
402
456
  type ChannelHealth = {
403
457
  state: "idle" | "running" | "error";
404
458
  lastSuccessUtcMs?: number;
@@ -491,6 +545,7 @@ type DeadLetterAddedEvent = {
491
545
  remoteName: string;
492
546
  documentId: string;
493
547
  errorSource: ChannelErrorSource;
548
+ errorType: SyncOperationErrorType;
494
549
  };
495
550
  /**
496
551
  * Status of a sync operation result.
@@ -537,6 +592,36 @@ declare class OptimisticLockError extends Error {
537
592
  declare class RevisionMismatchError extends Error {
538
593
  constructor(expected: number, actual: number);
539
594
  }
595
+ /**
596
+ * One read-set stream and the highest operation index observed on it, or -1
597
+ * if it was observed empty.
598
+ */
599
+ type AppendConditionStream = {
600
+ documentId: string;
601
+ scope: string;
602
+ branch: string;
603
+ revision: number;
604
+ };
605
+ /**
606
+ * A read-set enforced by {@link IOperationStore.apply}: the append fails if
607
+ * any stream has operations past its recorded revision.
608
+ */
609
+ type AppendCondition = {
610
+ streams: AppendConditionStream[];
611
+ };
612
+ /** Error history keeps messages, not classes, so failures match by prefix. */
613
+ declare const APPEND_CONDITION_FAILED_PREFIX = "Append condition failed: ";
614
+ /**
615
+ * A read-set stream grew before the append committed. A concurrency
616
+ * conflict, not a fault: the caller retries against the new stream heads.
617
+ */
618
+ declare class AppendConditionFailedError extends Error {
619
+ readonly condition: AppendCondition;
620
+ constructor(condition: AppendCondition);
621
+ static isError(error: unknown): error is AppendConditionFailedError;
622
+ /** True when a recorded error message is an append-condition failure. */
623
+ static isFailureMessage(message: string): boolean;
624
+ }
540
625
  /**
541
626
  * A write transaction passed to {@link IOperationStore.apply}. Accumulates
542
627
  * operations that are committed atomically when the callback returns.
@@ -550,7 +635,7 @@ interface AtomicTxn {
550
635
  * revision field and lastModified timestamp.
551
636
  */
552
637
  type DocumentRevisions = {
553
- /** Map of scope to operation index for that scope */revision: Record<string, number>; /** Latest timestamp across revisions */
638
+ /** Map of scope to operation index for that scope */revision: Record<string, number>; /** The largest operation timestamp in the document, across every scope. */
554
639
  latestTimestamp: string;
555
640
  };
556
641
  /**
@@ -571,6 +656,12 @@ interface IOperationStore {
571
656
  * returned instead of throwing. If no matching stored row is found, the
572
657
  * original error is propagated unchanged.
573
658
  *
659
+ * With an {@link AppendCondition}, the append additionally fails with
660
+ * {@link AppendConditionFailedError} — writing nothing — if any read-set
661
+ * stream has operations past its recorded revision. The written and
662
+ * read-set streams are advisory-locked in sorted key order, so concurrent
663
+ * conditional appends on overlapping streams serialize.
664
+ *
574
665
  * @param documentId - The document id
575
666
  * @param documentType - The document type identifier
576
667
  * @param scope - The operation scope (e.g. "global", "local")
@@ -578,9 +669,10 @@ interface IOperationStore {
578
669
  * @param revision - Expected current revision (optimistic lock)
579
670
  * @param fn - Callback that stages operations via {@link AtomicTxn}
580
671
  * @param signal - Optional abort signal to cancel the request
672
+ * @param condition - Optional read-set to enforce at write time
581
673
  * @returns The stored operations; empty array when no operations were staged
582
674
  */
583
- apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal): Promise<Operation[]>;
675
+ apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal, condition?: AppendCondition): Promise<Operation[]>;
584
676
  /**
585
677
  * Returns operations for a document/scope/branch whose index is greater
586
678
  * than the given revision.
@@ -627,6 +719,16 @@ interface IOperationStore {
627
719
  * @returns Object containing revision map and latest timestamp
628
720
  */
629
721
  getRevisions(documentId: string, branch: string, signal?: AbortSignal): Promise<DocumentRevisions>;
722
+ /**
723
+ * The largest operation timestamp in one stream, or undefined when it is empty.
724
+ * Distinct from {@link DocumentRevisions.latestTimestamp}, which maxes over
725
+ * every scope.
726
+ *
727
+ * Must be a real maximum, not the last-indexed operation's timestamp: a
728
+ * re-evaluation pass re-appends at a fresh index while keeping the original
729
+ * timestamp, so a later timestamp can sit behind the last row.
730
+ */
731
+ getStreamLatestTimestamp(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<string | undefined>;
630
732
  }
631
733
  /**
632
734
  * Stores periodic document snapshots (keyframes) so that document state
@@ -1045,7 +1147,8 @@ type DeadLetterRecord = {
1045
1147
  branch: string;
1046
1148
  operations: OperationWithContext$1[];
1047
1149
  errorSource: ChannelErrorSource;
1048
- errorMessage: string;
1150
+ errorMessage: string; /** Why it failed, in the closed set sync classifies failures into. */
1151
+ errorType: SyncOperationErrorType;
1049
1152
  };
1050
1153
  /**
1051
1154
  * Persists dead-lettered sync operations so they survive reactor restarts.
@@ -1103,6 +1206,15 @@ interface IOperationIndexTxn {
1103
1206
  createCollection(collectionId: string): void;
1104
1207
  addToCollection(collectionId: string, documentId: string): void;
1105
1208
  removeFromCollection(collectionId: string, documentId: string): void;
1209
+ /**
1210
+ * Records the group documents an auth operation's input names, tied to the
1211
+ * last written operation like addToCollection. At commit each reference is
1212
+ * remembered permanently and the group joins every collection the
1213
+ * referencing document belongs to, keeping the earliest join and reopening
1214
+ * a closed membership, so sync serves the group's history to every remote
1215
+ * that can observe the referencing grant.
1216
+ */
1217
+ recordGroupReferences(documentId: string, groupIds: string[]): void;
1106
1218
  write(operations: OperationIndexEntry[]): void;
1107
1219
  }
1108
1220
  /**
@@ -1124,6 +1236,13 @@ interface IOperationIndex {
1124
1236
  * Returns a map of documentId to array of collection IDs.
1125
1237
  */
1126
1238
  getCollectionsForDocuments(documentIds: string[]): Promise<Record<string, string[]>>;
1239
+ /**
1240
+ * The documents whose auth history has ever referenced the group, from the
1241
+ * group-reference relation. This is the set a group-stream change owes a
1242
+ * re-evaluation pass to; it is complete because a group's auth scope cannot
1243
+ * reference other groups.
1244
+ */
1245
+ getGroupReferencers(groupId: string, signal?: AbortSignal): Promise<string[]>;
1127
1246
  }
1128
1247
  /**
1129
1248
  * Identifies the collection a remote synchronizes. Collections are drive-level
@@ -1152,6 +1271,51 @@ declare class DriveCollectionId {
1152
1271
  equals(other: DriveCollectionId): boolean;
1153
1272
  }
1154
1273
  //#endregion
1274
+ //#region src/cache/write-cache-types.d.ts
1275
+ /**
1276
+ * Configuration options for the write cache
1277
+ */
1278
+ type WriteCacheConfig = {
1279
+ /** Maximum number of document streams to cache (LRU eviction). Default: 1000 */maxDocuments: number; /** Number of snapshots to keep in each document's ring buffer. Default: 10 */
1280
+ ringBufferSize: number; /** Persist a keyframe snapshot every N revisions. Default: 10 */
1281
+ keyframeInterval: number;
1282
+ };
1283
+ /**
1284
+ * Unique identifier for a document stream
1285
+ */
1286
+ type DocumentStreamKey = {
1287
+ /** Document identifier */documentId: string; /** Operation scope */
1288
+ scope: string; /** Branch name */
1289
+ branch: string;
1290
+ };
1291
+ /**
1292
+ * Where a snapshot sits in its stream.
1293
+ *
1294
+ * - `Head`: the newest revision of the stream when it was stored. Only these
1295
+ * can answer a read that asks for the head.
1296
+ * - `Historical`: state at an earlier revision. Usable as a starting point to
1297
+ * replay forward from, and as an answer to a read for that same revision.
1298
+ */
1299
+ declare enum SnapshotPosition {
1300
+ Head = "head",
1301
+ Historical = "historical"
1302
+ }
1303
+ /**
1304
+ * A cached document snapshot at a specific revision
1305
+ */
1306
+ type CachedSnapshot = {
1307
+ /** The revision number of this snapshot */revision: number; /** The document state at this revision */
1308
+ document: PHDocument; /** Where this snapshot sat in the stream when it was stored */
1309
+ position: SnapshotPosition;
1310
+ };
1311
+ /**
1312
+ * Serialized keyframe snapshot for K/V store persistence
1313
+ */
1314
+ type KeyframeSnapshot = {
1315
+ /** The revision number of this keyframe */revision: number; /** Serialized document state */
1316
+ document: string;
1317
+ };
1318
+ //#endregion
1155
1319
  //#region src/cache/write/interfaces.d.ts
1156
1320
  /**
1157
1321
  * IWriteCache is a write-side projection that optimizes document state retrieval
@@ -1166,7 +1330,8 @@ interface IWriteCache {
1166
1330
  * @param documentId - The document identifier
1167
1331
  * @param scope - Operation scope
1168
1332
  * @param branch - Branch name
1169
- * @param targetRevision - The exact revision to retrieve (optional, defaults to latest)
1333
+ * @param targetRevision - Index of the last operation to apply, defaulting
1334
+ * to latest. An operation index, never `header.revision[scope]`.
1170
1335
  * @param signal - Optional abort signal to cancel the operation
1171
1336
  * @returns The complete document at the specified revision
1172
1337
  *
@@ -1192,15 +1357,19 @@ interface IWriteCache {
1192
1357
  * @param documentId - The document identifier
1193
1358
  * @param scope - Operation scope
1194
1359
  * @param branch - Branch name
1195
- * @param revision - The revision this document represents
1360
+ * @param revision - Index of the last operation this document reflects, so
1361
+ * `header.revision[scope]` is one greater. -1 for an empty scope.
1196
1362
  * @param document - The document to cache
1363
+ * @param position - Whether `revision` is the stream's head. Nothing checks
1364
+ * it: claiming `Head` for an earlier revision makes a getState() with no
1365
+ * target return stale state.
1197
1366
  *
1198
1367
  * @example
1199
1368
  * ```typescript
1200
- * cache.putState(docId, 'global', 'main', 42, document);
1369
+ * cache.putState(docId, 'global', 'main', 42, document, SnapshotPosition.Head);
1201
1370
  * ```
1202
1371
  */
1203
- putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument): void;
1372
+ putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, position: SnapshotPosition): void;
1204
1373
  /**
1205
1374
  * Invalidates (removes) cached entries for a document stream.
1206
1375
  *
@@ -1500,6 +1669,104 @@ interface IReactorSubscriptionManager {
1500
1669
  onRelationshipChanged(callback: (parentId: string, childId: string, changeType: RelationshipChangeType) => void, search?: SearchFilter): () => void;
1501
1670
  }
1502
1671
  //#endregion
1672
+ //#region src/decision/types.d.ts
1673
+ /** One operation stream. */
1674
+ type StreamQuery = {
1675
+ documentId: string;
1676
+ branch: string;
1677
+ scope: string;
1678
+ };
1679
+ /**
1680
+ * What building a decision model reads a stream's state through.
1681
+ *
1682
+ * `IWriteCache` satisfies this and is what the write paths pass. The read path
1683
+ * cannot: the write cache is a write-side projection invalidated by the process
1684
+ * that runs the executor, so a reactor whose executors live in worker processes
1685
+ * holds state in its parent that no commit ever invalidates. A read there would
1686
+ * decide against a policy arbitrarily far behind the one the write paths
1687
+ * enforce. Reads therefore pass a reader backed by the read side.
1688
+ */
1689
+ interface IStreamStateReader {
1690
+ getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
1691
+ }
1692
+ /** The document and branch a decision model is built for. */
1693
+ type DecisionTarget = {
1694
+ documentId: string;
1695
+ branch: string;
1696
+ };
1697
+ /**
1698
+ * What a decision's conditions may read beyond the projections: the executing
1699
+ * scope's own state and the attempted action's input. Populated only while
1700
+ * authConditions is on; otherwise both stay undefined and conditional grants
1701
+ * never apply.
1702
+ */
1703
+ type DecisionContext = {
1704
+ scopeState: unknown;
1705
+ actionInput?: unknown;
1706
+ };
1707
+ /** A statically-queried stream's operations, named after its projection. */
1708
+ type StreamHistory = {
1709
+ name: string;
1710
+ operations: Operation[];
1711
+ };
1712
+ /**
1713
+ * A named stream whose value in the model is that scope's state from the
1714
+ * document rebuild the reactor already performs. A derived query may read
1715
+ * only statically-queried projections, so composition is one layer deep.
1716
+ */
1717
+ type Projection<M> = {
1718
+ query: StreamQuery | ((model: Partial<M>) => StreamQuery[]);
1719
+ /**
1720
+ * For a derived projection, the streams it may read anywhere in an
1721
+ * evaluated range, derived from the statically-queried streams' operations
1722
+ * (including the operations under evaluation). A positional walk cannot use
1723
+ * `query`, because the folded state it depends on changes over the range;
1724
+ * this over-approximates by design, since a stream referenced at any
1725
+ * position stays readable when the earlier range is re-evaluated even if a
1726
+ * later operation removes the reference. Ignored on static projections.
1727
+ */
1728
+ queryOverHistory?: (reads: StreamHistory[]) => StreamQuery[];
1729
+ /**
1730
+ * Action types in this stream that can change an evaluation. Reads of the stream
1731
+ * are filtered to these, so anything left out is invisible to a decision.
1732
+ */
1733
+ decidingActions: string[]; /** Applies one of this stream's operations while deciding. */
1734
+ apply: (document: PHDocument, operation: Operation) => PHDocument;
1735
+ };
1736
+ /**
1737
+ * The outcome of evaluating one operation. A refusal carries the reason it is
1738
+ * recorded with, because a model has more than one way to refuse.
1739
+ */
1740
+ type Evaluation = {
1741
+ decision: "allow";
1742
+ } | {
1743
+ decision: "deny";
1744
+ reason: string;
1745
+ };
1746
+ /** Projections plus a decision function over the built model. */
1747
+ type DecisionModel<M> = {
1748
+ projections: { [K in keyof M]: Projection<M> };
1749
+ /**
1750
+ * Present when decide reads the executing scope's state through the
1751
+ * decision context. A positional walk then folds the evaluated stream with
1752
+ * this, from its base state through every effective operation, so
1753
+ * conditions read the state as it stood at each operation's position
1754
+ * rather than at the head.
1755
+ */
1756
+ foldEvaluatedScope?: (document: PHDocument, operation: Operation) => PHDocument;
1757
+ /**
1758
+ * Whether or not this model decides about operations in a given scope. That
1759
+ * is, a scope it reads is not necessarily one it evaluates, and vise-versa.
1760
+ */
1761
+ evaluatesScope(scope: string): boolean;
1762
+ decide(model: M, subject: AuthSubject, request: AuthRequest, ctx: DecisionContext): Evaluation;
1763
+ };
1764
+ /** A built model plus the read-set condition recording what the build read. */
1765
+ type BuiltDecisionModel<M> = {
1766
+ model: M;
1767
+ appendCondition: AppendCondition;
1768
+ };
1769
+ //#endregion
1503
1770
  //#region src/client/types.d.ts
1504
1771
  /**
1505
1772
  * Describes the types of document changes that can occur.
@@ -1531,6 +1798,17 @@ type CreateDocumentOptions = {
1531
1798
  /** Optional "id" or "slug" of parent document */parentIdentifier?: string; /** Optional version of the document model to use (defaults to latest) */
1532
1799
  documentModelVersion?: number;
1533
1800
  };
1801
+ /**
1802
+ * Options for upgrading a document.
1803
+ */
1804
+ type UpgradeDocumentOptions = {
1805
+ /**
1806
+ * How many times to retry with a fresh read when the executor rejects the
1807
+ * upgrade because the document changed after it was read. Defaults to
1808
+ * {@link DEFAULT_UPGRADE_CONFLICT_RETRIES}.
1809
+ */
1810
+ maxConflictRetries?: number;
1811
+ };
1534
1812
  /**
1535
1813
  * Drive-aware operations grouped under `client.drives`.
1536
1814
  *
@@ -1596,6 +1874,33 @@ interface IDriveClient {
1596
1874
  */
1597
1875
  listNodes(driveIdentifier: string, parentFolder?: string | null, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Node>>;
1598
1876
  }
1877
+ /**
1878
+ * One operation an authorization preflight predicts a verdict for. The input is
1879
+ * what a conditional grant reads, so a candidate standing for a filled-in form
1880
+ * carries that form's input.
1881
+ */
1882
+ type ActionCandidate = {
1883
+ scope: string;
1884
+ type: string;
1885
+ input?: unknown;
1886
+ };
1887
+ /**
1888
+ * The predicted verdicts for a set of candidates, in the order they were given,
1889
+ * with the aggregates a UI branches on.
1890
+ *
1891
+ * The aggregates are redundant -- a verdict is binary, so `allDenied` is
1892
+ * `!anyAllowed` and `anyDenied` is `!allAllowed` -- and all four are returned
1893
+ * so that a caller reads the one its question is phrased in rather than
1894
+ * negating another. Over no candidates every aggregate is false: nothing is
1895
+ * allowed and nothing is denied.
1896
+ */
1897
+ type ActionEvaluations = {
1898
+ evaluations: Evaluation[];
1899
+ allAllowed: boolean;
1900
+ anyAllowed: boolean;
1901
+ allDenied: boolean;
1902
+ anyDenied: boolean;
1903
+ };
1599
1904
  /**
1600
1905
  * The ReactorClient interface that wraps lower-level APIs to provide
1601
1906
  * a simpler interface for document operations.
@@ -1646,7 +1951,7 @@ interface IReactorClient {
1646
1951
  * @param signal - Optional abort signal to cancel the request
1647
1952
  * @returns The canonical document id
1648
1953
  */
1649
- resolveIdOrSlug(identifier: string, signal?: AbortSignal): Promise<string>;
1954
+ resolveIdOrSlug(identifier: string, view?: ViewFilter, signal?: AbortSignal): Promise<string>;
1650
1955
  /**
1651
1956
  * Retrieves operations for a document.
1652
1957
  *
@@ -1690,6 +1995,44 @@ interface IReactorClient {
1690
1995
  * @returns List of documents matching criteria and pagination cursor
1691
1996
  */
1692
1997
  find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
1998
+ /**
1999
+ * Predicts whether the subject would be admitted to execute each of a set of
2000
+ * candidate operations, without submitting any of them. A UI asks this to
2001
+ * disable a control rather than offer an action that fails on submit.
2002
+ *
2003
+ * The answer is a prediction, not a promise. Three caveats hold:
2004
+ *
2005
+ * - Real admission compiles an append condition over everything it read and
2006
+ * the store enforces it at write time. A preflight reads no future, so a
2007
+ * policy change landing between this answer and the submit changes the
2008
+ * verdict. The submit path stays the only authority.
2009
+ * - The verdict is evaluated at the stream heads. It is therefore correct for
2010
+ * a candidate that will be stamped at or after every timestamp the
2011
+ * evaluation read, which is the normal case for a control the user is about
2012
+ * to click. A backdated submission is out of contract: the reactor decides
2013
+ * that one by position, against the policy as it stood there.
2014
+ * - A candidate whose input decides the verdict needs that input supplied.
2015
+ * With `authConditions` on, a conditional grant reads `action.input`, so
2016
+ * omitting the input predicts the denial an empty input would earn rather
2017
+ * than the verdict the filled-in form will get.
2018
+ *
2019
+ * Document-scope candidates are decided against the policy of the document
2020
+ * their input names, not the one passed here: delete and upgrade name it in
2021
+ * `input.documentId`, and the relationship actions in `input.sourceId`. This
2022
+ * follows the executor's own gate, which decides against the document
2023
+ * guarding the write. `CREATE_DOCUMENT` follows the gate's exemption: it runs
2024
+ * before its document exists, so the executor never decides it against a
2025
+ * policy and the preflight predicts allow.
2026
+ *
2027
+ * @param documentIdentifier - Document "id" or "slug" the candidates target
2028
+ * @param branch - Branch to evaluate against
2029
+ * @param candidates - Operations to predict a verdict for, each with the scope it would execute in
2030
+ * @param subject - Optional subject to decide for, defaulting to the client's own signer. A plain subject rather than a ViewFilter: the evaluation reads no view, so a filter's branch or scopes would be silently ignored here
2031
+ * @param signal - Optional abort signal to cancel the request
2032
+ * @returns One evaluation per candidate, in the order given, with the aggregates over them
2033
+ * @throws AuthEnforcementDisabledError if the reactor's authEnforcement flag is off, in which case it holds no decision model and the legacy host-table permission system cannot answer for one
2034
+ */
2035
+ evaluateActions(documentIdentifier: string, branch: string, candidates: ActionCandidate[], subject?: AuthSubject, signal?: AbortSignal): Promise<ActionEvaluations>;
1693
2036
  /**
1694
2037
  * Creates a document and waits for completion
1695
2038
  *
@@ -1707,6 +2050,38 @@ interface IReactorClient {
1707
2050
  * @param signal - Optional abort signal to cancel the request
1708
2051
  */
1709
2052
  createEmpty<TDocument extends PHDocument>(documentModelType: string, options?: CreateDocumentOptions, signal?: AbortSignal): Promise<TDocument>;
2053
+ /**
2054
+ * Retrieves the document model module matching the version a document is
2055
+ * stamped with. Use this instead of {@link getDocumentModelModule}
2056
+ * whenever a specific document is in hand: the latest-wins lookup feeds
2057
+ * not-yet-upgraded documents the wrong reducer, diverging from replay.
2058
+ *
2059
+ * @param document - The document whose stamped version selects the module
2060
+ * @returns The document model module registered for that version
2061
+ * @throws UnsupportedDocumentModelVersionError if no module is registered for the stamped version
2062
+ */
2063
+ getDocumentModelModuleForDocument(document: PHDocument): Promise<DocumentModelModule<any>>;
2064
+ /**
2065
+ * Upgrades a document to a newer document model version by dispatching an
2066
+ * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the
2067
+ * latest registered module version for the document's type. Returns the
2068
+ * document unchanged when it is already at the target version.
2069
+ *
2070
+ * The action carries a snapshot of the document's version and per-scope
2071
+ * revisions, which the executor validates before persisting. When an edit
2072
+ * lands between the read and the upgrade executing, the upgrade is
2073
+ * rejected and retried with a fresh read up to
2074
+ * {@link UpgradeDocumentOptions.maxConflictRetries} times before the
2075
+ * conflict is surfaced.
2076
+ *
2077
+ * @param documentIdentifier - Target document id or slug
2078
+ * @param toVersion - Optional target document model version; defaults to latest
2079
+ * @param options - Optional upgrade options (maxConflictRetries)
2080
+ * @param signal - Optional abort signal to cancel the request
2081
+ * @returns The upgraded document
2082
+ * @throws DowngradeNotSupportedError if toVersion is less than the document's current version
2083
+ */
2084
+ upgradeDocument<TDocument extends PHDocument = PHDocument>(documentIdentifier: string, toVersion?: number, options?: UpgradeDocumentOptions, signal?: AbortSignal): Promise<TDocument>;
1710
2085
  /**
1711
2086
  * Creates an empty document in a drive as a single batched operation.
1712
2087
  * This is more efficient than createEmpty + addFile as it batches all
@@ -1863,136 +2238,1043 @@ interface IReactorClient {
1863
2238
  subscribe(search: SearchFilter, callback: (event: DocumentChangeEvent) => void, view?: ViewFilter): () => void;
1864
2239
  }
1865
2240
  //#endregion
1866
- //#region src/client/reactor-client.d.ts
2241
+ //#region src/cache/collection-membership-cache.d.ts
2242
+ interface ICollectionMembershipCache {
2243
+ getCollectionsForDocuments(documentIds: string[]): Promise<Record<string, string[]>>;
2244
+ invalidate(documentId: string): void;
2245
+ }
2246
+ //#endregion
2247
+ //#region src/cache/document-meta-cache-types.d.ts
1867
2248
  /**
1868
- * ReactorClient implementation that wraps lower-level APIs to provide
1869
- * a simpler interface for document operations.
2249
+ * Cached document metadata from the "document" scope.
1870
2250
  *
1871
- * Features:
1872
- * - Wraps Jobs with Promises for easier async handling
1873
- * - Manages signing of submitted Action objects
1874
- * - Provides quality-of-life functions for common tasks
1875
- * - Wraps subscription interface with ViewFilters
2251
+ * This lightweight structure holds essential document information needed by
2252
+ * the job executor without fetching full scope state. It provides an explicit
2253
+ * cross-scope contract for accessing document scope metadata.
1876
2254
  */
1877
- declare class ReactorClient implements IReactorClient {
1878
- private logger;
1879
- private reactor;
1880
- private signer;
1881
- private subscriptionManager;
1882
- private jobAwaiter;
1883
- private documentIndexer;
1884
- private documentView;
1885
- readonly drives: IDriveClient;
1886
- constructor(logger: ILogger, reactor: IReactor, signer: ISigner, subscriptionManager: IReactorSubscriptionManager, jobAwaiter: IJobAwaiter, documentIndexer: IDocumentIndexer, documentView: IDocumentView);
2255
+ type CachedDocumentMeta = {
1887
2256
  /**
1888
- * Retrieves a list of document model modules.
2257
+ * The full PHDocumentState from document.state.document.
2258
+ * Contains version, hash, isDeleted, deletedAtUtcIso, etc.
1889
2259
  */
1890
- getDocumentModelModules(namespace?: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<DocumentModelModule>>;
2260
+ state: PHDocumentState;
1891
2261
  /**
1892
- * Retrieves a specific document model module by document type.
1893
- *
1894
- * @param documentType - The document type identifier
1895
- * @returns The document model module
2262
+ * The document type (from header), cached for convenience.
1896
2263
  */
1897
- getDocumentModelModule(documentType: string): Promise<DocumentModelModule<any>>;
2264
+ documentType: string;
1898
2265
  /**
1899
- * Retrieves a specific PHDocument
2266
+ * The revision of the document scope when this metadata was captured.
2267
+ * Used for cache invalidation and consistency checks.
1900
2268
  */
1901
- get<TDocument extends PHDocument>(identifier: string, view?: ViewFilter, signal?: AbortSignal): Promise<TDocument>;
2269
+ documentScopeRevision: number;
2270
+ };
2271
+ /**
2272
+ * Interface for the document metadata cache.
2273
+ *
2274
+ * This cache provides an explicit cross-scope contract for accessing document
2275
+ * scope metadata. It solves the problem where job execution in one scope (e.g.,
2276
+ * "global") needs access to document scope state (version, isDeleted, etc.)
2277
+ * which may be stale in scope-specific caches or keyframes.
2278
+ *
2279
+ * The cache supports:
2280
+ * - Latest metadata retrieval with LRU caching
2281
+ * - Historical metadata reconstruction for reshuffling scenarios
2282
+ * - Eager updates after document scope operations
2283
+ */
2284
+ interface IDocumentMetaCache {
1902
2285
  /**
1903
- * Resolves an identifier (id or slug) to the canonical document id, using the
1904
- * same lookup as the data path. Resolves against the "main" branch. Throws if
1905
- * the identifier cannot be resolved or is ambiguous.
2286
+ * Retrieves the LATEST document metadata from cache or rebuilds from operations.
2287
+ *
2288
+ * On cache miss, fetches all document scope operations and reconstructs the
2289
+ * current PHDocumentState by applying UPGRADE_DOCUMENT and DELETE_DOCUMENT
2290
+ * operations.
2291
+ *
2292
+ * @param documentId - The document identifier
2293
+ * @param branch - Branch name
2294
+ * @param signal - Optional abort signal to cancel the operation
2295
+ * @returns The cached or rebuilt document metadata
2296
+ * @throws {Error} "Operation aborted" if signal is aborted
2297
+ * @throws {Error} If document not found (no CREATE_DOCUMENT operation)
1906
2298
  */
1907
- resolveIdOrSlug(identifier: string, signal?: AbortSignal): Promise<string>;
2299
+ getDocumentMeta(documentId: string, branch: string, signal?: AbortSignal): Promise<CachedDocumentMeta>;
1908
2300
  /**
1909
- * Retrieves operations for a document
2301
+ * Rebuilds document metadata at a SPECIFIC revision (always rebuilds, no caching).
2302
+ *
2303
+ * Used during reshuffling when operations need to be inserted at a previous
2304
+ * revision and we need the document scope state as of that point in time.
2305
+ *
2306
+ * @param documentId - The document identifier
2307
+ * @param branch - Branch name
2308
+ * @param targetRevision - The document scope revision to reconstruct up to
2309
+ * @param signal - Optional abort signal to cancel the operation
2310
+ * @returns Document metadata as of the target revision
2311
+ * @throws {Error} "Operation aborted" if signal is aborted
2312
+ * @throws {Error} If document not found
1910
2313
  */
1911
- getOperations(documentIdentifier: string, view?: ViewFilter, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
1912
- private getOperationsWithCompositeCursor;
2314
+ rebuildAtRevision(documentId: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<CachedDocumentMeta>;
1913
2315
  /**
1914
- * Retrieves outgoing relationships of a given type from a source document.
2316
+ * Eagerly updates cached metadata after document scope operations.
2317
+ *
2318
+ * Called by the job executor after CREATE_DOCUMENT, UPGRADE_DOCUMENT, or
2319
+ * DELETE_DOCUMENT operations to keep the cache current.
2320
+ *
2321
+ * @param documentId - The document identifier
2322
+ * @param branch - Branch name
2323
+ * @param meta - The new metadata to cache
1915
2324
  */
1916
- getOutgoingRelationships(sourceIdentifier: string, relationshipType: string, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
2325
+ putDocumentMeta(documentId: string, branch: string, meta: CachedDocumentMeta): void;
1917
2326
  /**
1918
- * Retrieves incoming relationships of a given type to a target document.
2327
+ * Invalidates cached document metadata.
2328
+ *
2329
+ * Call before reshuffling operations that modify the document scope, or
2330
+ * when document state may have changed externally.
2331
+ *
2332
+ * @param documentId - The document identifier
2333
+ * @param branch - Optional branch to narrow invalidation (if omitted, all branches)
2334
+ * @returns Number of entries invalidated
1919
2335
  */
1920
- getIncomingRelationships(targetIdentifier: string, relationshipType: string, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
2336
+ invalidate(documentId: string, branch?: string): number;
1921
2337
  /**
1922
- * Filters documents by criteria and returns a list of them
2338
+ * Clears all cached document metadata.
1923
2339
  */
1924
- find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
2340
+ clear(): void;
1925
2341
  /**
1926
- * Creates a document and waits for completion
2342
+ * Performs startup initialization.
1927
2343
  */
1928
- create<TDocument extends PHDocument = PHDocument>(document: PHDocument, parentIdentifier?: string, signal?: AbortSignal): Promise<TDocument>;
2344
+ startup(): Promise<void>;
2345
+ /**
2346
+ * Performs graceful shutdown.
2347
+ */
2348
+ shutdown(): Promise<void>;
2349
+ }
2350
+ //#endregion
2351
+ //#region src/storage/kysely/types.d.ts
2352
+ interface OperationTable {
2353
+ id: Generated<number>;
2354
+ jobId: string;
2355
+ opId: string;
2356
+ prevOpId: string;
2357
+ writeTimestampUtcMs: Generated<Date>;
2358
+ documentId: string;
2359
+ documentType: string;
2360
+ scope: string;
2361
+ branch: string;
2362
+ timestampUtcMs: Date;
2363
+ index: number;
2364
+ action: unknown;
2365
+ skip: number;
2366
+ error?: string | null;
2367
+ deniedReason?: string | null;
2368
+ hash: string;
2369
+ }
2370
+ interface KeyframeTable {
2371
+ id: Generated<number>;
2372
+ documentId: string;
2373
+ documentType: string;
2374
+ scope: string;
2375
+ branch: string;
2376
+ revision: number;
2377
+ document: unknown;
2378
+ createdAt: Generated<Date>;
2379
+ }
2380
+ interface DocumentCollectionTable {
2381
+ documentId: string;
2382
+ collectionId: string;
2383
+ joinedOrdinal: bigint;
2384
+ leftOrdinal: bigint | null;
2385
+ }
2386
+ interface OperationIndexOperationTable {
2387
+ ordinal: Generated<number>;
2388
+ opId: string;
2389
+ documentId: string;
2390
+ documentType: string;
2391
+ scope: string;
2392
+ branch: string;
2393
+ timestampUtcMs: string;
2394
+ writeTimestampUtcMs: Generated<Date>;
2395
+ index: number;
2396
+ skip: number;
2397
+ hash: string;
2398
+ action: unknown;
2399
+ deniedReason?: string | null;
2400
+ sourceRemote: Generated<string>;
2401
+ }
2402
+ interface SyncRemoteTable {
2403
+ name: string;
2404
+ collection_id: string;
2405
+ channel_type: string;
2406
+ channel_id: string;
2407
+ remote_name: string;
2408
+ channel_parameters: unknown;
2409
+ filter_document_ids: unknown;
2410
+ filter_scopes: unknown;
2411
+ filter_branch: string;
2412
+ push_state: string;
2413
+ push_last_success_utc_ms: string | null;
2414
+ push_last_failure_utc_ms: string | null;
2415
+ push_failure_count: number;
2416
+ pull_state: string;
2417
+ pull_last_success_utc_ms: string | null;
2418
+ pull_last_failure_utc_ms: string | null;
2419
+ pull_failure_count: number;
2420
+ bound_address: string | null;
2421
+ created_at: Generated<Date>;
2422
+ updated_at: Generated<Date>;
2423
+ }
2424
+ interface SyncCursorTable {
2425
+ remote_name: string;
2426
+ cursor_type: string;
2427
+ cursor_ordinal: bigint;
2428
+ last_synced_at_utc_ms: string | null;
2429
+ updated_at: Generated<Date>;
2430
+ }
2431
+ /**
2432
+ * Kysely table definition for the `sync_dead_letters` table.
2433
+ */
2434
+ interface SyncDeadLetterTable {
2435
+ ordinal: Generated<number>;
2436
+ id: string;
2437
+ job_id: string;
2438
+ job_dependencies: unknown;
2439
+ remote_name: string;
2440
+ document_id: string;
2441
+ scopes: unknown;
2442
+ branch: string;
2443
+ operations: unknown;
2444
+ error_source: string;
2445
+ error_message: string;
2446
+ error_type: Generated<string>;
2447
+ created_at: Generated<Date>;
2448
+ }
2449
+ /**
2450
+ * One (document, group) reference ever discovered from an auth operation's
2451
+ * input. Rows are never updated or deleted (see migration 017).
2452
+ */
2453
+ interface GroupReferenceTable {
2454
+ documentId: string;
2455
+ groupId: string;
2456
+ }
2457
+ interface Database$1 {
2458
+ Operation: OperationTable;
2459
+ Keyframe: KeyframeTable;
2460
+ document_collections: DocumentCollectionTable;
2461
+ operation_index_operations: OperationIndexOperationTable;
2462
+ group_references: GroupReferenceTable;
2463
+ sync_remotes: SyncRemoteTable;
2464
+ sync_cursors: SyncCursorTable;
2465
+ sync_dead_letters: SyncDeadLetterTable;
2466
+ }
2467
+ interface DocumentTable {
2468
+ id: string;
2469
+ createdAt: Generated<Date>;
2470
+ updatedAt: Generated<Date>;
2471
+ }
2472
+ interface DocumentRelationshipTable {
2473
+ id: Generated<string>;
2474
+ sourceId: string;
2475
+ targetId: string;
2476
+ relationshipType: string;
2477
+ metadata: unknown;
2478
+ createdAt: Generated<Date>;
2479
+ updatedAt: Generated<Date>;
2480
+ }
2481
+ interface IndexerStateTable {
2482
+ id: Generated<number>;
2483
+ lastOperationId: number;
2484
+ lastOperationTimestamp: Generated<Date>;
2485
+ }
2486
+ interface DocumentIndexerDatabase {
2487
+ Document: DocumentTable;
2488
+ DocumentRelationship: DocumentRelationshipTable;
2489
+ IndexerState: IndexerStateTable;
2490
+ }
2491
+ //#endregion
2492
+ //#region src/executor/worker/protocol.d.ts
2493
+ /**
2494
+ * A JSON-clonable value safe to send across the worker IPC boundary.
2495
+ *
2496
+ * The shape mirrors the structured-clone subset used by the parent's
2497
+ * sanitizer: primitives, arrays, plain objects, plus the explicit
2498
+ * {@link ErrorInfo} shape for marshalled Errors.
2499
+ *
2500
+ * @see Wire Protocol Reference wiki page
2501
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2502
+ */
2503
+ type SanitizedArg = null | boolean | number | string | ErrorInfo | SanitizedArg[] | {
2504
+ [key: string]: SanitizedArg;
2505
+ };
2506
+ /**
2507
+ * Structured representation of an Error for IPC transport.
2508
+ *
2509
+ * Class instances cannot be structured-cloned across worker boundaries,
2510
+ * so Errors are flattened into this shape on the worker side and
2511
+ * reconstructed on the parent side.
2512
+ *
2513
+ * @see Wire Protocol Reference wiki page
2514
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2515
+ */
2516
+ type ErrorInfo = {
2517
+ name: string;
2518
+ message: string;
2519
+ stack?: string;
2520
+ cause?: ErrorInfo;
2521
+ };
2522
+ /**
2523
+ * Reference to a module that the worker should `import()` at runtime,
2524
+ * along with the named export to pluck out as the factory.
2525
+ *
2526
+ * Exactly one of `packageName` or `filePath` is provided.
2527
+ *
2528
+ * @see Wire Protocol Reference wiki page
2529
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2530
+ */
2531
+ type ModuleRef = {
2532
+ /** Bare-specifier package name (resolved by the worker's module loader). */packageName: string; /** Named export within the module to invoke as the factory. */
2533
+ exportName: string;
2534
+ } | {
2535
+ /** Absolute or worker-resolvable file path to import. */filePath: string; /** Named export within the module to invoke as the factory. */
2536
+ exportName: string;
2537
+ };
2538
+ /**
2539
+ * Factory specification shared by the signature verifier and document
2540
+ * model spec channels. The worker imports `module.exportName` and invokes
2541
+ * it with `initArgs` to obtain the actual instance.
2542
+ *
2543
+ * `initArgs` must be JSON-clonable.
2544
+ *
2545
+ * @see Wire Protocol Reference wiki page
2546
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2547
+ */
2548
+ type FactorySpec = {
2549
+ module: ModuleRef;
2550
+ initArgs?: SanitizedArg;
2551
+ };
2552
+ /**
2553
+ * Factory spec for the signature verifier the worker should instantiate.
2554
+ *
2555
+ * Structurally identical to {@link FactorySpec}; the alias exists so call
2556
+ * sites read intent-fully.
2557
+ *
2558
+ * @see Wire Protocol Reference wiki page
2559
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2560
+ */
2561
+ type SignatureVerifierSpec = FactorySpec;
2562
+ /**
2563
+ * Factory spec for a document model module the worker should instantiate.
2564
+ *
2565
+ * Structurally identical to {@link FactorySpec}; the alias exists so call
2566
+ * sites read intent-fully.
2567
+ *
2568
+ * @see Wire Protocol Reference wiki page
2569
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2570
+ */
2571
+ type DocumentModelSpec = FactorySpec;
2572
+ /**
2573
+ * One entry in the document model manifest the worker materializes on
2574
+ * startup (or extends lazily via `load-model`).
2575
+ *
2576
+ * @see Wire Protocol Reference wiki page
2577
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2578
+ */
2579
+ type ModelManifestEntry = {
2580
+ /** Document type identifier (e.g. "ph/account"). */documentType: string; /** Document model version this entry registers. */
2581
+ version: string; /** Factory spec the worker imports and invokes to obtain the model. */
2582
+ spec: DocumentModelSpec;
2583
+ };
2584
+ /**
2585
+ * JSON-clonable Postgres connection info passed to the worker so it can
2586
+ * open its own pool. Storage-specific wiring may extend this shape in
2587
+ * later phases.
2588
+ *
2589
+ * @see Wire Protocol Reference wiki page
2590
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2591
+ */
2592
+ type DbConfig = {
2593
+ host: string;
2594
+ port: number;
2595
+ database: string;
2596
+ user: string;
2597
+ password: string;
2598
+ ssl?: boolean;
2599
+ applicationName?: string;
2600
+ poolSize?: number;
2601
+ /**
2602
+ * Maximum time (ms) a caller will wait to acquire a connection from the
2603
+ * pool before pg.Pool throws. When omitted, pg defaults to 0 (unlimited
2604
+ * wait), which hides acquire-starvation as silent latency.
2605
+ */
2606
+ connectionTimeoutMillis?: number;
2607
+ /**
2608
+ * How long (ms) an idle connection stays open before pg closes it. When
2609
+ * omitted, pg defaults to 10000.
2610
+ */
2611
+ idleTimeoutMillis?: number;
2612
+ };
2613
+ /**
2614
+ * Configuration for the executor worker pool.
2615
+ *
2616
+ * Mirrors the `workerPool` sub-config on {@link JobExecutorConfig};
2617
+ * a later card wires this into the executor config.
2618
+ *
2619
+ * @see Wire Protocol Reference wiki page
2620
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2621
+ */
2622
+ type WorkerPoolConfig = {
2623
+ /** Whether the worker pool is active; when false the executor runs in-process. */enabled: boolean; /** Number of worker instances to spawn. */
2624
+ numWorkers: number; /** Worker isolation mode. */
2625
+ workerType: "thread" | "process"; /** Optional heartbeat interval in milliseconds. */
2626
+ heartbeatMs?: number; /** Optional per-worker Postgres pool size override. */
2627
+ workerPgPoolSize?: number;
2628
+ };
2629
+ /**
2630
+ * Payload the worker reports back when a job's write phase is complete.
2631
+ *
2632
+ * Parent fills `collectionMemberships` at emission time, so it is
2633
+ * intentionally absent from the worker -> parent message.
2634
+ *
2635
+ * @see Wire Protocol Reference wiki page
2636
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2637
+ */
2638
+ type JobWriteReadyPayload = {
2639
+ operations: OperationWithContext$1[];
2640
+ jobMeta: JobMeta;
2641
+ };
2642
+ /**
2643
+ * Initializes a freshly spawned worker with the configuration and
2644
+ * factories it needs to start executing jobs.
2645
+ *
2646
+ * @see Wire Protocol Reference wiki page
2647
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2648
+ */
2649
+ type InitMessage = {
2650
+ type: "init";
2651
+ correlationId: string;
2652
+ workerId: string;
2653
+ poolConfig: WorkerPoolConfig;
2654
+ db: DbConfig; /** Omitted = the worker performs no executor-side signature verification. */
2655
+ signatureVerifier?: SignatureVerifierSpec;
2656
+ models: ModelManifestEntry[]; /** Omitted = the worker builds its executor with the built-in defaults. */
2657
+ executorConfig?: JobExecutorConfig;
2658
+ };
2659
+ /**
2660
+ * Dispatches a job to the worker for execution.
2661
+ *
2662
+ * @see Wire Protocol Reference wiki page
2663
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2664
+ */
2665
+ type ExecuteMessage = {
2666
+ type: "execute";
2667
+ correlationId: string;
2668
+ job: Job;
2669
+ };
2670
+ /**
2671
+ * Requests cancellation of an in-flight job.
2672
+ *
2673
+ * @see Wire Protocol Reference wiki page
2674
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2675
+ */
2676
+ type AbortMessage = {
2677
+ type: "abort";
2678
+ correlationId: string; /** correlationId of the `execute` message being aborted. */
2679
+ targetCorrelationId: string;
2680
+ reason?: string;
2681
+ };
2682
+ /**
2683
+ * Asks the worker to drain in-flight work and exit.
2684
+ *
2685
+ * @see Wire Protocol Reference wiki page
2686
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2687
+ */
2688
+ type ShutdownMessage = {
2689
+ type: "shutdown";
2690
+ correlationId: string; /** Optional grace period before the parent force-terminates the worker. */
2691
+ graceMs?: number;
2692
+ };
2693
+ /**
2694
+ * Lazily registers an additional document model on a running worker.
2695
+ *
2696
+ * @see Wire Protocol Reference wiki page
2697
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2698
+ */
2699
+ type LoadModelMessage = {
2700
+ type: "load-model";
2701
+ correlationId: string;
2702
+ model: ModelManifestEntry;
2703
+ };
2704
+ /**
2705
+ * Union of all messages the parent may send to a worker.
2706
+ *
2707
+ * @see Wire Protocol Reference wiki page
2708
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2709
+ */
2710
+ type ParentMessage = InitMessage | ExecuteMessage | AbortMessage | ShutdownMessage | LoadModelMessage;
2711
+ /**
2712
+ * Announces that the worker has finished `init` and is ready to accept
2713
+ * `execute` messages.
2714
+ *
2715
+ * @see Wire Protocol Reference wiki page
2716
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2717
+ */
2718
+ type ReadyMessage = {
2719
+ type: "ready"; /** correlationId of the originating `init` message. */
2720
+ correlationId: string;
2721
+ workerId: string;
2722
+ };
2723
+ /**
2724
+ * Final result for an `execute` job.
2725
+ *
2726
+ * On success, `writeReady` carries the operations and job meta the
2727
+ * parent needs to emit `JOB_WRITE_READY`. On failure, `error` is set
2728
+ * and `result.success` is false.
2729
+ *
2730
+ * @see Wire Protocol Reference wiki page
2731
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2732
+ */
2733
+ type ResultMessage = {
2734
+ type: "result"; /** correlationId of the originating `execute` message. */
2735
+ correlationId: string;
2736
+ result: JobResult;
2737
+ writeReady?: JobWriteReadyPayload;
2738
+ error?: ErrorInfo;
2739
+ };
2740
+ /**
2741
+ * Acknowledges that a `load-model` request succeeded.
2742
+ *
2743
+ * @see Wire Protocol Reference wiki page
2744
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2745
+ */
2746
+ type ModelLoadedMessage = {
2747
+ type: "model-loaded"; /** correlationId of the originating `load-model` message. */
2748
+ correlationId: string;
2749
+ documentType: string;
2750
+ version: string;
2751
+ };
2752
+ /**
2753
+ * Reports that a `load-model` request failed.
2754
+ *
2755
+ * @see Wire Protocol Reference wiki page
2756
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2757
+ */
2758
+ type ModelLoadFailedMessage = {
2759
+ type: "model-load-failed"; /** correlationId of the originating `load-model` message. */
2760
+ correlationId: string;
2761
+ documentType: string;
2762
+ version: string;
2763
+ error: ErrorInfo;
2764
+ };
2765
+ /**
2766
+ * Forwarded log line from the worker. `args` is constrained to
2767
+ * {@link SanitizedArg} so callers cannot accidentally ship non-clonable
2768
+ * values across the boundary.
2769
+ *
2770
+ * The sanitizer in `./sanitize.ts` enforces the {@link SanitizedArg}
2771
+ * invariant on the producer side before each message is posted.
2772
+ *
2773
+ * @see Wire Protocol Reference wiki page
2774
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2775
+ */
2776
+ type LogMessage = {
2777
+ type: "log";
2778
+ level: "debug" | "info" | "warn" | "error";
2779
+ message: string;
2780
+ args: SanitizedArg[]; /** Epoch milliseconds at which the worker generated the log line. */
2781
+ timestamp: number;
2782
+ };
2783
+ /**
2784
+ * Periodic liveness signal. Included now to unblock Phase-3 scaffolding;
2785
+ * the wiki marks heartbeats as a Phase-5 future extension.
2786
+ *
2787
+ * @see Wire Protocol Reference wiki page
2788
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2789
+ */
2790
+ type HeartbeatMessage = {
2791
+ type: "heartbeat";
2792
+ workerId: string; /** Epoch milliseconds the worker generated the heartbeat. */
2793
+ timestamp: number; /** Optional snapshot of in-flight job correlation ids. */
2794
+ inFlightCorrelationIds?: string[];
2795
+ };
2796
+ /**
2797
+ * Periodic counters / gauges the worker reports for observability.
2798
+ *
2799
+ * @see Wire Protocol Reference wiki page
2800
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2801
+ */
2802
+ type MetricsMessage = {
2803
+ type: "metrics";
2804
+ workerId: string; /** Epoch milliseconds the worker generated the metrics snapshot. */
2805
+ timestamp: number;
2806
+ counters: {
2807
+ [name: string]: number;
2808
+ };
2809
+ gauges: {
2810
+ [name: string]: number;
2811
+ };
2812
+ };
2813
+ /**
2814
+ * Snapshot of one worker pool's acquire-wait samples and pool-stat counters,
2815
+ * forwarded periodically so the host can re-record into the shared
2816
+ * pg.Pool histogram and observable gauges. The worker owns the real
2817
+ * pg.Pool; the host's {@link PoolInstrumentation} is a forwarder driven
2818
+ * by these messages.
2819
+ */
2820
+ type PoolAcquireSamplesMessage = {
2821
+ type: "pool-acquire-samples";
2822
+ workerId: string; /** Stable identifier matching the host-side instrumentation name (e.g. "worker-0"). */
2823
+ poolName: string; /** Epoch milliseconds the worker generated the batch. */
2824
+ timestamp: number; /** Acquire-wait durations (ms) accumulated since the previous batch. */
2825
+ durations: number[]; /** Most recent pg.Pool counter snapshot at batch send time. */
2826
+ size: number;
2827
+ idle: number;
2828
+ waiting: number;
2829
+ };
2830
+ /**
2831
+ * Union of all messages a worker may send to the parent.
2832
+ *
2833
+ * @see Wire Protocol Reference wiki page
2834
+ * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2835
+ */
2836
+ type WorkerMessage = ReadyMessage | ResultMessage | ModelLoadedMessage | ModelLoadFailedMessage | LogMessage | HeartbeatMessage | MetricsMessage | PoolAcquireSamplesMessage;
2837
+ //#endregion
2838
+ //#region src/core/model-sources.d.ts
2839
+ /** An importable file holding one or more document-model exports. */
2840
+ type FileModelSource = {
2841
+ filePath: string;
2842
+ exportName?: string;
2843
+ };
2844
+ /** An importable package specifier holding one or more document-model exports. */
2845
+ type PackageModelSource = {
2846
+ packageName: string;
2847
+ subpath?: string;
2848
+ exportName?: string;
2849
+ };
2850
+ /**
2851
+ * A source of document models: a live module, an importable file, or an
2852
+ * importable package. File and package sources can cross a worker-thread
2853
+ * boundary (workers re-import them); a live module cannot.
2854
+ */
2855
+ type DocumentModelSource = DocumentModelModule<any> | FileModelSource | PackageModelSource;
2856
+ //#endregion
2857
+ //#region src/registry/interfaces.d.ts
2858
+ type RegistrationResult<T> = {
2859
+ status: "success";
2860
+ item: T;
2861
+ } | {
2862
+ status: "error";
2863
+ item: T;
2864
+ error: Error;
2865
+ };
2866
+ /**
2867
+ * Loader that asynchronously resolves a document type to a
2868
+ * {@link DocumentModelSource}. Used by the queue to gate CREATE_DOCUMENT
2869
+ * jobs until the required model is available in the registry.
2870
+ *
2871
+ * Return an importable source ({ filePath } or { packageName }) whenever
2872
+ * possible: the resolver registers the resolved models on the host registry
2873
+ * and broadcasts importable sources to executor workers. A live
2874
+ * DocumentModelModule is also valid but host-only — it cannot cross a
2875
+ * worker-thread boundary, so worker pools will not receive it.
2876
+ */
2877
+ interface IDocumentModelLoader {
2878
+ load(documentType: string): Promise<DocumentModelSource>;
2879
+ }
2880
+ /**
2881
+ * Registry for managing document model modules.
2882
+ * Provides centralized access to document models' reducers, utils, and specifications.
2883
+ * Supports version-aware module storage and upgrade manifest management.
2884
+ */
2885
+ interface IDocumentModelRegistry {
2886
+ /**
2887
+ * Register multiple modules at once.
2888
+ * Modules without a version field default to version 1.
2889
+ * Invalid or duplicate modules are skipped without breaking registration of the remaining modules.
2890
+ *
2891
+ * @param modules Document model modules to register
2892
+ * @returns Array of results, one per module, indicating success or failure
2893
+ */
2894
+ registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
2895
+ /**
2896
+ * Unregister all versions of the specified document types.
2897
+ *
2898
+ * @param documentTypes The document types to unregister
2899
+ * @returns true if all modules were unregistered, false if any were not found
2900
+ */
2901
+ unregisterModules(...documentTypes: string[]): boolean;
2902
+ /**
2903
+ * Get a specific document model module by document type and optional version.
2904
+ * If version is not specified, returns the latest version.
2905
+ *
2906
+ * @param documentType The document type identifier
2907
+ * @param version Optional version number to retrieve
2908
+ * @returns The document model module
2909
+ * @throws ModuleNotFoundError if the document type or version is not registered
2910
+ */
2911
+ getModule(documentType: string, version?: number): DocumentModelModule<any>;
2912
+ /**
2913
+ * Get all registered document model modules.
2914
+ *
2915
+ * @returns Array of all registered modules
2916
+ */
2917
+ getAllModules(): DocumentModelModule<any>[];
2918
+ /**
2919
+ * Clear all registered modules and upgrade manifests.
2920
+ */
2921
+ clear(): void;
2922
+ /**
2923
+ * Get all supported versions for a document type, sorted in ascending order.
2924
+ *
2925
+ * @param documentType The document type identifier
2926
+ * @returns Array of version numbers sorted ascending
2927
+ * @throws ModuleNotFoundError if no modules are registered for the document type
2928
+ */
2929
+ getSupportedVersions(documentType: string): number[];
2930
+ /**
2931
+ * Get the latest (highest) version number for a document type.
2932
+ *
2933
+ * @param documentType The document type identifier
2934
+ * @returns The highest version number registered for this document type
2935
+ * @throws ModuleNotFoundError if no modules are registered for the document type
2936
+ */
2937
+ getLatestVersion(documentType: string): number;
1929
2938
  /**
1930
- * Creates an empty document and waits for completion
2939
+ * Register upgrade manifests that define upgrade paths between versions.
2940
+ * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
2941
+ *
2942
+ * @param manifests Upgrade manifests to register
2943
+ * @returns Array of results, one per manifest, indicating success or failure
1931
2944
  */
1932
- createEmpty<TDocument extends PHDocument>(documentModelType: string, options?: CreateDocumentOptions, signal?: AbortSignal): Promise<TDocument>;
2945
+ registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
1933
2946
  /**
1934
- * Creates an empty document in a drive as a single batched operation.
1935
- * Delegates to {@link IDriveClient.addFile}.
2947
+ * Unregister upgrade manifests for the specified document types.
2948
+ * @param documentTypes The document types whose upgrade manifests should be unregistered
2949
+ * @returns true if all modules were unregistered, false if any were not found
2950
+ **/
2951
+ unregisterUpgradeManifests(...documentTypes: string[]): boolean;
2952
+ /**
2953
+ * Get the upgrade manifest for a document type.
1936
2954
  *
1937
- * @deprecated Use `client.drives.addFile` instead. This method will be
1938
- * removed in a future release.
2955
+ * @param documentType The document type identifier
2956
+ * @returns The upgrade manifest
2957
+ * @throws ManifestNotFoundError if no manifest is registered for the document type
1939
2958
  */
1940
- createDocumentInDrive<TDocument extends PHDocument>(driveId: string, document: PHDocument, parentFolder?: string, signal?: AbortSignal): Promise<TDocument>;
2959
+ getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
1941
2960
  /**
1942
- * Applies a list of actions to a document and waits for completion
2961
+ * Compute the upgrade path from one version to another.
2962
+ * Returns the sequence of upgrade transitions needed.
2963
+ *
2964
+ * @param documentType The document type identifier
2965
+ * @param fromVersion The starting version
2966
+ * @param toVersion The target version
2967
+ * @returns Array of upgrade transitions in order
2968
+ * @throws DowngradeNotSupportedError if toVersion is less than fromVersion
2969
+ * @throws ManifestNotFoundError if no upgrade manifest is registered
2970
+ * @throws MissingUpgradeTransitionError if any transition in the path is missing
1943
2971
  */
1944
- execute<TDocument extends PHDocument>(documentIdentifier: string, branch: string, actions: Action[], signal?: AbortSignal): Promise<TDocument>;
2972
+ computeUpgradePath(documentType: string, fromVersion: number, toVersion: number): UpgradeTransition[];
1945
2973
  /**
1946
- * Submits a list of actions to a document
2974
+ * Get the upgrade reducer for a single-step version transition.
2975
+ *
2976
+ * @param documentType The document type identifier
2977
+ * @param fromVersion The starting version
2978
+ * @param toVersion The target version (must be fromVersion + 1)
2979
+ * @returns The upgrade reducer function
2980
+ * @throws InvalidUpgradeStepError if toVersion is not fromVersion + 1
2981
+ * @throws ManifestNotFoundError if no upgrade manifest is registered
2982
+ * @throws MissingUpgradeTransitionError if the transition is not found
1947
2983
  */
1948
- executeAsync(documentIdentifier: string, branch: string, actions: Action[], signal?: AbortSignal): Promise<JobInfo>;
1949
- executeBatch(request: BatchExecutionRequest, signal?: AbortSignal): Promise<BatchExecutionResult>;
2984
+ getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
2985
+ }
2986
+ //#endregion
2987
+ //#region src/cache/buffer/ring-buffer.d.ts
2988
+ /**
2989
+ * RingBuffer is a generic circular buffer implementation that stores a fixed number
2990
+ * of items. When the buffer is full, new items overwrite the oldest items.
2991
+ *
2992
+ * This implementation maintains O(1) time complexity for push operations and provides
2993
+ * items in chronological order (oldest to newest) via getAll().
2994
+ *
2995
+ * @template T - The type of items stored in the buffer
2996
+ */
2997
+ declare class RingBuffer<T> {
2998
+ private buffer;
2999
+ private head;
3000
+ private size;
3001
+ private capacity;
3002
+ constructor(capacity: number);
1950
3003
  /**
1951
- * Renames a document and waits for completion
3004
+ * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
3005
+ *
3006
+ * @param item - The item to add
1952
3007
  */
1953
- rename(documentIdentifier: string, name: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3008
+ push(item: T): void;
1954
3009
  /**
1955
- * Updates the preferred editor recorded in the document header meta.
1956
- * Pass `null` to clear it.
3010
+ * Returns all items in the buffer in chronological order (oldest to newest).
3011
+ *
3012
+ * @returns Array of items in insertion order
1957
3013
  */
1958
- setPreferredEditor(documentIdentifier: string, preferredEditor: string | null, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3014
+ getAll(): T[];
1959
3015
  /**
1960
- * Adds multiple documents as children to another and waits for completion
3016
+ * Clears all items from the buffer.
1961
3017
  */
1962
- addRelationship(sourceIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3018
+ clear(): void;
1963
3019
  /**
1964
- * Removes a relationship between two documents and waits for completion.
3020
+ * Gets the current number of items in the buffer.
1965
3021
  */
1966
- removeRelationship(sourceIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3022
+ get length(): number;
3023
+ }
3024
+ //#endregion
3025
+ //#region src/cache/kysely-write-cache.d.ts
3026
+ type DocumentStream = {
3027
+ key: string;
3028
+ ringBuffer: RingBuffer<CachedSnapshot>;
3029
+ };
3030
+ /**
3031
+ * In-memory write cache with keyframe persistence for PHDocuments.
3032
+ *
3033
+ * Caches document snapshots in ring buffers with LRU eviction. On cache miss,
3034
+ * rebuilds documents from nearest keyframe or full operation history.
3035
+ *
3036
+ * **Performance Characteristics:**
3037
+ * - Cache hit: O(1) lookup in ring buffer
3038
+ * - Cold miss: O(n) where n is total operation count, or O(k) where k is operations since keyframe
3039
+ * - Warm miss: O(m) where m is operations since cached revision
3040
+ * - Eviction: O(1) for LRU tracking and removal
3041
+ *
3042
+ * **Thread Safety:**
3043
+ * Not thread-safe. Designed for single-threaded job executor environment.
3044
+ * External synchronization required for concurrent access across multiple executors.
3045
+ *
3046
+ * **Example:**
3047
+ * ```typescript
3048
+ * const cache = new KyselyWriteCache(
3049
+ * keyframeStore,
3050
+ * operationStore,
3051
+ * registry,
3052
+ * { maxDocuments: 1000, ringBufferSize: 10, keyframeInterval: 10 }
3053
+ * );
3054
+ *
3055
+ * await cache.startup();
3056
+ *
3057
+ * // Retrieve or rebuild document
3058
+ * const doc = await cache.getState(docId, docType, scope, branch, revision);
3059
+ *
3060
+ * // Cache result after job execution
3061
+ * cache.putState(docId, docType, scope, branch, newRevision, updatedDoc);
3062
+ *
3063
+ * await cache.shutdown();
3064
+ * ```
3065
+ */
3066
+ declare class KyselyWriteCache implements IWriteCache {
3067
+ private streams;
3068
+ private lruTracker;
3069
+ private keyframeStore;
3070
+ private operationStore;
3071
+ private registry;
3072
+ private config;
3073
+ constructor(keyframeStore: IKeyframeStore, operationStore: IOperationStore, registry: IDocumentModelRegistry, config: WriteCacheConfig);
3074
+ withScopedStores(operationStore: IOperationStore, keyframeStore: IKeyframeStore): KyselyWriteCache;
1967
3075
  /**
1968
- * Moves a relationship from one source document to another and waits for completion.
3076
+ * Initializes the write cache.
3077
+ * Currently a no-op as keyframe store lifecycle is managed externally.
1969
3078
  */
1970
- moveRelationship(sourceParentIdentifier: string, targetParentIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<{
1971
- source: PHDocument;
1972
- target: PHDocument;
1973
- }>;
1974
- loadBatch(request: BatchLoadRequest, signal?: AbortSignal): Promise<BatchLoadResult>;
3079
+ startup(): Promise<void>;
1975
3080
  /**
1976
- * Deletes a document and waits for completion
3081
+ * Shuts down the write cache.
3082
+ * Currently a no-op as keyframe store lifecycle is managed externally.
1977
3083
  */
1978
- deleteDocument(identifier: string, propagate?: PropagationMode, signal?: AbortSignal): Promise<void>;
3084
+ shutdown(): Promise<void>;
1979
3085
  /**
1980
- * Deletes documents and waits for completion
3086
+ * Retrieves document state at a specific revision from cache or rebuilds it.
3087
+ *
3088
+ * Note: this returns a _shallow_ copy of the document.
3089
+ *
3090
+ * Cache hit path: Returns cached snapshot if available (O(1))
3091
+ * Warm miss path: Rebuilds from cached base revision + incremental ops
3092
+ * Cold miss path: Rebuilds from keyframe or from scratch using all operations
3093
+ *
3094
+ * @param documentId - The document identifier
3095
+ * @param scope - The operation scope
3096
+ * @param branch - The operation branch
3097
+ * @param targetRevision - The target revision, or undefined for newest
3098
+ * @param signal - Optional abort signal to cancel the operation
3099
+ * @returns The document at the target revision
3100
+ * @throws {Error} "Operation aborted" if signal is aborted
3101
+ * @throws {ModuleNotFoundError} If document type not registered in registry
3102
+ * @throws {Error} "Failed to rebuild document" if operation store fails
3103
+ * @throws {Error} If reducer throws during operation application
3104
+ * @throws {Error} If document serialization fails
1981
3105
  */
1982
- deleteDocuments(identifiers: string[], propagate?: PropagationMode, signal?: AbortSignal): Promise<void>;
3106
+ getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
1983
3107
  /**
1984
- * Retrieves the status of a job
3108
+ * Stores a document snapshot in the cache at a specific revision.
3109
+ *
3110
+ * The cached document is a shallow copy of the input with its operation history
3111
+ * truncated to the last operation per scope and its clipboard cleared. This keeps
3112
+ * memory use and copy costs constant regardless of operation count. Consumers of
3113
+ * getState() must not rely on the full operation history being present; the only
3114
+ * guaranteed invariant is that operations[scope].at(-1) reflects the latest
3115
+ * operation index for each scope.
3116
+ *
3117
+ * Updates LRU tracker and may evict least recently used stream if at capacity.
3118
+ * Asynchronously persists keyframes at configured intervals (fire-and-forget).
3119
+ *
3120
+ * @param documentId - The document identifier
3121
+ * @param scope - The operation scope
3122
+ * @param branch - The operation branch
3123
+ * @param revision - The revision number
3124
+ * @param document - The document to cache
3125
+ * @throws {Error} If document serialization fails
1985
3126
  */
1986
- getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;
3127
+ putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, position: SnapshotPosition): void;
3128
+ private store;
3129
+ /**
3130
+ * Invalidates cached document streams.
3131
+ *
3132
+ * Supports three invalidation scopes:
3133
+ * - Document-level: invalidate(documentId) - removes all streams for document
3134
+ * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
3135
+ * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
3136
+ *
3137
+ * @param documentId - The document identifier
3138
+ * @param scope - Optional scope to narrow invalidation
3139
+ * @param branch - Optional branch to narrow invalidation (requires scope)
3140
+ * @returns The number of streams evicted
3141
+ */
3142
+ invalidate(documentId: string, scope?: string, branch?: string): number;
3143
+ /**
3144
+ * Clears the entire cache, removing all cached document streams.
3145
+ * Resets LRU tracking state. This operation always succeeds.
3146
+ */
3147
+ clear(): void;
3148
+ /**
3149
+ * Retrieves a specific stream for a document. Exposed on the implementation
3150
+ * for testing, but not on the interface.
3151
+ *
3152
+ * @internal
3153
+ */
3154
+ getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
3155
+ private findNearestKeyframe;
3156
+ /**
3157
+ * Rebuilds a scope from a keyframe or from the whole operation history.
3158
+ *
3159
+ * The document scope is always rebuilt first, because it carries the type,
3160
+ * the upgrades and the deletion marker. Its version-changing upgrades are not
3161
+ * applied there though: an upgrade reducer must see the state the requested
3162
+ * scope has reached at that upgrade's boundary, so each one is held back and
3163
+ * applied when the replay below crosses the boundary that
3164
+ * resolveModuleVersionForOp derives from it. Upgrades whose boundary lies past
3165
+ * the last replayed operation are applied at the end. Creation-time 0->N seed
3166
+ * upgrades carry the initial state, so they still apply immediately.
3167
+ */
3168
+ private coldMissRebuild;
3169
+ /**
3170
+ * Applies and removes every held-back upgrade whose target version is at or
3171
+ * below `throughVersion`, in the order the document scope recorded them.
3172
+ */
3173
+ private applyPendingUpgrades;
3174
+ /**
3175
+ * Applies the remaining held-back upgrades after the requested scope's
3176
+ * replay has finished. A head read applies them all. A positional read
3177
+ * applies only those whose boundary for this scope lies at or before the
3178
+ * target position: applying a later one would label migrated state with a
3179
+ * pre-upgrade revision, and a keyframe stored from that poisons every
3180
+ * rebuild that resumes from it. Boundaries come from the upgrade's revision
3181
+ * snapshot; an upgrade without one records no position for this scope, and
3182
+ * the replay loop not having crossed it already places it past the target.
3183
+ */
3184
+ private applyTailPendingUpgrades;
3185
+ /**
3186
+ * Applies one held-back upgrade, then re-applies the deletes the document
3187
+ * scope recorded after it so the hold-back cannot invert their order.
3188
+ */
3189
+ private applyPendingUpgrade;
3190
+ /**
3191
+ * Copies the current document revisions onto the document. Overwrites the
3192
+ * requested scope revision with the target revision, if provided.
3193
+ */
3194
+ private stampRevisions;
3195
+ /** The stored operation at `index`, or undefined if it is no longer there. */
3196
+ private operationAt;
3197
+ /**
3198
+ * Resolves which module version to use for a given operation in phase 2.
3199
+ *
3200
+ * Uses the validated-upgrade boundary rules from D7:
3201
+ * - If `input.revision` is present: op.index < revision[scope] → before the upgrade boundary
3202
+ * - Otherwise: timestamp fallback
3203
+ * - Falls back to final module version when neither is decidable
3204
+ */
3205
+ private resolveModuleVersionForOp;
3206
+ private warmMissRebuild;
3207
+ private findNearestOlderSnapshot;
3208
+ private makeStreamKey;
3209
+ private getOrCreateStream;
3210
+ private isKeyframeRevision;
3211
+ }
3212
+ //#endregion
3213
+ //#region src/storage/kysely/store.d.ts
3214
+ declare class KyselyOperationStore implements IOperationStore {
3215
+ private db;
3216
+ private trx?;
3217
+ constructor(db: Kysely<Database$1>);
3218
+ private get queryExecutor();
3219
+ withTransaction(trx: Transaction<Database$1>): KyselyOperationStore;
3220
+ apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal, condition?: AppendCondition): Promise<Operation[]>;
3221
+ private resolveUniqueConstraint;
3222
+ private executeApply;
1987
3223
  /**
1988
- * Waits for a job to complete
3224
+ * Locks the written stream and every read-set stream, in sorted key order
3225
+ * so that overlapping concurrent appends serialize rather than deadlock.
3226
+ * The locks are still taken one row at a time, so the query preserves that
3227
+ * order. It must stay separate from the guarded insert, which would
3228
+ * otherwise read a snapshot taken before the locks were held.
1989
3229
  */
1990
- waitForJob(jobId: string | JobInfo, signal?: AbortSignal): Promise<JobInfo>;
3230
+ private acquireStreamLocks;
1991
3231
  /**
1992
- * Subscribes to changes for documents matching specified filters
3232
+ * Inserts the staged operations with the condition compiled in as a WHERE
3233
+ * NOT EXISTS guard, making the check and the append one statement. Returns
3234
+ * the rows inserted; zero means the guard failed and nothing was written.
1993
3235
  */
1994
- subscribe(search: SearchFilter, callback: (event: DocumentChangeEvent) => void, view?: ViewFilter): () => void;
1995
- private removeAllIncomingRelationships;
3236
+ private insertGuarded;
3237
+ private findIdempotentReplay;
3238
+ getSince(documentId: string, scope: string, branch: string, revision: number, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
3239
+ getSinceId(id: number, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<OperationWithContext$1>>;
3240
+ getConflicting(documentId: string, scope: string, branch: string, minTimestamp: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
3241
+ getRevisions(documentId: string, branch: string, signal?: AbortSignal): Promise<DocumentRevisions>;
3242
+ getStreamLatestTimestamp(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<string | undefined>;
3243
+ private rowToOperation;
3244
+ private rowToOperationWithContext;
3245
+ }
3246
+ //#endregion
3247
+ //#region src/storage/kysely/keyframe-store.d.ts
3248
+ declare class KyselyKeyframeStore implements IKeyframeStore {
3249
+ private db;
3250
+ private trx?;
3251
+ constructor(db: Kysely<Database$1>);
3252
+ private get queryExecutor();
3253
+ withTransaction(trx: Transaction<Database$1>): KyselyKeyframeStore;
3254
+ putKeyframe(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, signal?: AbortSignal): Promise<void>;
3255
+ findNearestKeyframe(documentId: string, scope: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<{
3256
+ revision: number;
3257
+ document: PHDocument;
3258
+ } | undefined>;
3259
+ listKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<Array<{
3260
+ scope: string;
3261
+ branch: string;
3262
+ revision: number;
3263
+ document: PHDocument;
3264
+ }>>;
3265
+ deleteKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<number>;
3266
+ }
3267
+ //#endregion
3268
+ //#region src/executor/execution-scope.d.ts
3269
+ interface ExecutionStores {
3270
+ operationStore: IOperationStore;
3271
+ operationIndex: IOperationIndex;
3272
+ writeCache: IWriteCache;
3273
+ documentMetaCache: IDocumentMetaCache;
3274
+ collectionMembershipCache: ICollectionMembershipCache;
3275
+ }
3276
+ interface IExecutionScope {
3277
+ run<T>(fn: (stores: ExecutionStores) => Promise<T>, signal?: AbortSignal): Promise<T>;
1996
3278
  }
1997
3279
  //#endregion
1998
3280
  //#region src/executor/types.d.ts
@@ -2013,13 +3295,48 @@ type JobResult = {
2013
3295
  duration?: number; /** Any additional metadata from the execution */
2014
3296
  metadata?: Record<string, any>;
2015
3297
  };
3298
+ /**
3299
+ * Enforcement the reactor performs, each off by default.
3300
+ *
3301
+ * An evaluation made while replaying is part of the document's history, so two
3302
+ * reactors that share documents and disagree on these diverge. A flag is turned
3303
+ * on for a set of reactors that sync with each other, not for one node.
3304
+ */
3305
+ type ReactorFeatureFlags = {
3306
+ /**
3307
+ * Decide whether an operation may be admitted by building a decision model
3308
+ * over the document stream, rather than reading the deleted flag from the
3309
+ * document meta cache. Deletion then takes effect from the deleting
3310
+ * operation's position rather than for the whole document.
3311
+ */
3312
+ documentDecisions: boolean;
3313
+ /**
3314
+ * Evaluate the auth policy by reading the auth scope as a second projection.
3315
+ * Requires documentDecisions.
3316
+ */
3317
+ authEnforcement: boolean;
3318
+ /**
3319
+ * Match { group } principals by folding the referenced PHGroup documents as
3320
+ * derived projections. Requires authEnforcement.
3321
+ */
3322
+ authGroups: boolean;
3323
+ /**
3324
+ * Evaluate `where` clauses and { match } principals against the executing
3325
+ * scope's state, the subject, and the action input. Requires authGroups.
3326
+ */
3327
+ authConditions: boolean;
3328
+ };
2016
3329
  /**
2017
3330
  * Configuration options for the job executor
2018
3331
  */
2019
3332
  type JobExecutorConfig = {
2020
- /** Maximum number of conflicting operations to skip when reshuffling. */maxSkipThreshold?: number; /** Maximum number of concurrent jobs to execute */
3333
+ /** Feature flags; anything unset is off. */featureFlags?: Partial<ReactorFeatureFlags>; /** Maximum number of conflicting operations to skip when reshuffling. */
3334
+ maxSkipThreshold?: number; /** Maximum number of concurrent jobs to execute */
2021
3335
  maxConcurrency?: number; /** Maximum time in milliseconds a job can run before being considered timed out */
2022
- jobTimeoutMs?: number; /** Base delay in milliseconds for exponential backoff retries */
3336
+ jobTimeoutMs?: number;
3337
+ /** How long a job whose document is missing waits for it before failing.
3338
+ * Unbounded deferral never resolves the caller awaiting the job. */
3339
+ deferredJobTtlMs?: number; /** Base delay in milliseconds for exponential backoff retries */
2023
3340
  retryBaseDelayMs?: number; /** Maximum delay in milliseconds for exponential backoff retries */
2024
3341
  retryMaxDelayMs?: number;
2025
3342
  /** Maximum elapsed milliseconds before yielding to the main thread between actions.
@@ -2074,356 +3391,448 @@ type ExecutorStoppedEvent = {
2074
3391
  * Status information for the job executor manager
2075
3392
  */
2076
3393
  type ExecutorManagerStatus = {
2077
- /** Whether the manager is currently running */isRunning: boolean; /** Number of executor instances managed */
2078
- numExecutors: number; /** Number of jobs currently being processed */
2079
- activeJobs: number; /** Total number of jobs processed since start */
2080
- totalJobsProcessed: number;
2081
- };
2082
- //#endregion
2083
- //#region src/executor/worker/protocol.d.ts
2084
- /**
2085
- * A JSON-clonable value safe to send across the worker IPC boundary.
2086
- *
2087
- * The shape mirrors the structured-clone subset used by the parent's
2088
- * sanitizer: primitives, arrays, plain objects, plus the explicit
2089
- * {@link ErrorInfo} shape for marshalled Errors.
2090
- *
2091
- * @see Wire Protocol Reference wiki page
2092
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2093
- */
2094
- type SanitizedArg = null | boolean | number | string | ErrorInfo | SanitizedArg[] | {
2095
- [key: string]: SanitizedArg;
2096
- };
2097
- /**
2098
- * Structured representation of an Error for IPC transport.
2099
- *
2100
- * Class instances cannot be structured-cloned across worker boundaries,
2101
- * so Errors are flattened into this shape on the worker side and
2102
- * reconstructed on the parent side.
2103
- *
2104
- * @see Wire Protocol Reference wiki page
2105
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2106
- */
2107
- type ErrorInfo = {
2108
- name: string;
2109
- message: string;
2110
- stack?: string;
2111
- cause?: ErrorInfo;
2112
- };
2113
- /**
2114
- * Reference to a module that the worker should `import()` at runtime,
2115
- * along with the named export to pluck out as the factory.
2116
- *
2117
- * Exactly one of `packageName` or `filePath` is provided.
2118
- *
2119
- * @see Wire Protocol Reference wiki page
2120
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2121
- */
2122
- type ModuleRef = {
2123
- /** Bare-specifier package name (resolved by the worker's module loader). */packageName: string; /** Named export within the module to invoke as the factory. */
2124
- exportName: string;
2125
- } | {
2126
- /** Absolute or worker-resolvable file path to import. */filePath: string; /** Named export within the module to invoke as the factory. */
2127
- exportName: string;
2128
- };
2129
- /**
2130
- * Factory specification shared by the signature verifier and document
2131
- * model spec channels. The worker imports `module.exportName` and invokes
2132
- * it with `initArgs` to obtain the actual instance.
2133
- *
2134
- * `initArgs` must be JSON-clonable.
2135
- *
2136
- * @see Wire Protocol Reference wiki page
2137
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2138
- */
2139
- type FactorySpec = {
2140
- module: ModuleRef;
2141
- initArgs?: SanitizedArg;
2142
- };
2143
- /**
2144
- * Factory spec for the signature verifier the worker should instantiate.
2145
- *
2146
- * Structurally identical to {@link FactorySpec}; the alias exists so call
2147
- * sites read intent-fully.
2148
- *
2149
- * @see Wire Protocol Reference wiki page
2150
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2151
- */
2152
- type SignatureVerifierSpec = FactorySpec;
2153
- /**
2154
- * Factory spec for a document model module the worker should instantiate.
2155
- *
2156
- * Structurally identical to {@link FactorySpec}; the alias exists so call
2157
- * sites read intent-fully.
2158
- *
2159
- * @see Wire Protocol Reference wiki page
2160
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2161
- */
2162
- type DocumentModelSpec = FactorySpec;
2163
- /**
2164
- * One entry in the document model manifest the worker materializes on
2165
- * startup (or extends lazily via `load-model`).
2166
- *
2167
- * @see Wire Protocol Reference wiki page
2168
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2169
- */
2170
- type ModelManifestEntry = {
2171
- /** Document type identifier (e.g. "ph/account"). */documentType: string; /** Document model version this entry registers. */
2172
- version: string; /** Factory spec the worker imports and invokes to obtain the model. */
2173
- spec: DocumentModelSpec;
2174
- };
2175
- /**
2176
- * JSON-clonable Postgres connection info passed to the worker so it can
2177
- * open its own pool. Storage-specific wiring may extend this shape in
2178
- * later phases.
2179
- *
2180
- * @see Wire Protocol Reference wiki page
2181
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2182
- */
2183
- type DbConfig = {
2184
- host: string;
2185
- port: number;
2186
- database: string;
2187
- user: string;
2188
- password: string;
2189
- ssl?: boolean;
2190
- applicationName?: string;
2191
- poolSize?: number;
2192
- /**
2193
- * Maximum time (ms) a caller will wait to acquire a connection from the
2194
- * pool before pg.Pool throws. When omitted, pg defaults to 0 (unlimited
2195
- * wait), which hides acquire-starvation as silent latency.
2196
- */
2197
- connectionTimeoutMillis?: number;
2198
- /**
2199
- * How long (ms) an idle connection stays open before pg closes it. When
2200
- * omitted, pg defaults to 10000.
2201
- */
2202
- idleTimeoutMillis?: number;
2203
- };
2204
- /**
2205
- * Configuration for the executor worker pool.
2206
- *
2207
- * Mirrors the `workerPool` sub-config on {@link JobExecutorConfig};
2208
- * a later card wires this into the executor config.
2209
- *
2210
- * @see Wire Protocol Reference wiki page
2211
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2212
- */
2213
- type WorkerPoolConfig = {
2214
- /** Whether the worker pool is active; when false the executor runs in-process. */enabled: boolean; /** Number of worker instances to spawn. */
2215
- numWorkers: number; /** Worker isolation mode. */
2216
- workerType: "thread" | "process"; /** Optional heartbeat interval in milliseconds. */
2217
- heartbeatMs?: number; /** Optional per-worker Postgres pool size override. */
2218
- workerPgPoolSize?: number;
2219
- };
2220
- /**
2221
- * Payload the worker reports back when a job's write phase is complete.
2222
- *
2223
- * Parent fills `collectionMemberships` at emission time, so it is
2224
- * intentionally absent from the worker -> parent message.
2225
- *
2226
- * @see Wire Protocol Reference wiki page
2227
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2228
- */
2229
- type JobWriteReadyPayload = {
2230
- operations: OperationWithContext$1[];
2231
- jobMeta: JobMeta;
2232
- };
2233
- /**
2234
- * Initializes a freshly spawned worker with the configuration and
2235
- * factories it needs to start executing jobs.
2236
- *
2237
- * @see Wire Protocol Reference wiki page
2238
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2239
- */
2240
- type InitMessage = {
2241
- type: "init";
2242
- correlationId: string;
2243
- workerId: string;
2244
- poolConfig: WorkerPoolConfig;
2245
- db: DbConfig; /** Omitted = the worker performs no executor-side signature verification. */
2246
- signatureVerifier?: SignatureVerifierSpec;
2247
- models: ModelManifestEntry[];
2248
- };
2249
- /**
2250
- * Dispatches a job to the worker for execution.
2251
- *
2252
- * @see Wire Protocol Reference wiki page
2253
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2254
- */
2255
- type ExecuteMessage = {
2256
- type: "execute";
2257
- correlationId: string;
2258
- job: Job;
2259
- };
2260
- /**
2261
- * Requests cancellation of an in-flight job.
2262
- *
2263
- * @see Wire Protocol Reference wiki page
2264
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
2265
- */
2266
- type AbortMessage = {
2267
- type: "abort";
2268
- correlationId: string; /** correlationId of the `execute` message being aborted. */
2269
- targetCorrelationId: string;
2270
- reason?: string;
3394
+ /** Whether the manager is currently running */isRunning: boolean; /** Number of executor instances managed */
3395
+ numExecutors: number; /** Number of jobs currently being processed */
3396
+ activeJobs: number; /** Total number of jobs processed since start */
3397
+ totalJobsProcessed: number;
3398
+ };
3399
+ //#endregion
3400
+ //#region src/decision/document-decision-model.d.ts
3401
+ /** What the document decision model reads: the target's document scope. */
3402
+ type DocumentDecisionModel = {
3403
+ document: PHDocumentState;
2271
3404
  };
2272
3405
  /**
2273
- * Asks the worker to drain in-flight work and exit.
2274
- *
2275
- * @see Wire Protocol Reference wiki page
2276
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3406
+ * The simplest decision model: one projection over the document scope, which
3407
+ * rejects on a deleted document.
2277
3408
  */
2278
- type ShutdownMessage = {
2279
- type: "shutdown";
2280
- correlationId: string; /** Optional grace period before the parent force-terminates the worker. */
2281
- graceMs?: number;
3409
+ declare function documentDecisionModel(target: DecisionTarget): DecisionModel<DocumentDecisionModel>;
3410
+ //#endregion
3411
+ //#region src/decision/registered-model.d.ts
3412
+ /**
3413
+ * A model this reactor can register. Every one carries the document projection,
3414
+ * because admission reads the version and the deletion timestamp off it; a model
3415
+ * with more projections than that is still assignable here.
3416
+ */
3417
+ type RegisteredDecisionModel = (target: DecisionTarget) => DecisionModel<DocumentDecisionModel>;
3418
+ /** What admission needs out of a model built at the stream heads. */
3419
+ type AdmissionDecision = {
3420
+ evaluation: Evaluation;
3421
+ appendCondition: AppendCondition;
3422
+ documentVersion: number;
3423
+ deletedAtUtcIso: string | null;
2282
3424
  };
2283
3425
  /**
2284
- * Lazily registers an additional document model on a running worker.
2285
- *
2286
- * @see Wire Protocol Reference wiki page
2287
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3426
+ * What decideAtHead resolves a condition context from: the action's input,
3427
+ * with the executing scope's state read at the head. Supplied only while
3428
+ * authConditions is on.
2288
3429
  */
2289
- type LoadModelMessage = {
2290
- type: "load-model";
2291
- correlationId: string;
2292
- model: ModelManifestEntry;
3430
+ type AdmissionConditions = {
3431
+ actionInput?: unknown;
2293
3432
  };
2294
3433
  /**
2295
- * Union of all messages the parent may send to a worker.
3434
+ * Builds the model at the stream heads and decides one request against it. The
3435
+ * append condition it returns is the read-set the store enforces at write time.
2296
3436
  *
2297
- * @see Wire Protocol Reference wiki page
2298
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3437
+ * With `conditions` supplied, the executing scope's state is read at the head
3438
+ * for `doc.<scope>.*` paths. That read carries no append-condition entry of
3439
+ * its own: the written stream's expected-revision check already refuses a
3440
+ * write whose scope grew between the read and the append.
2299
3441
  */
2300
- type ParentMessage = InitMessage | ExecuteMessage | AbortMessage | ShutdownMessage | LoadModelMessage;
3442
+ declare function decideAtHead(model: RegisteredDecisionModel, cache: IWriteCache, target: DecisionTarget, subject: AuthSubject, request: AuthRequest, signal?: AbortSignal, conditions?: AdmissionConditions): Promise<AdmissionDecision>;
2301
3443
  /**
2302
- * Announces that the worker has finished `init` and is ready to accept
2303
- * `execute` messages.
2304
- *
2305
- * @see Wire Protocol Reference wiki page
2306
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3444
+ * The model this reactor enforces. With `authEnforcement` off the auth scope is
3445
+ * absent from every append condition and no load walks it; with `authGroups`
3446
+ * on, the group documents the grant list names join the read-set and the
3447
+ * registry supplies the reducer that folds them.
2307
3448
  */
2308
- type ReadyMessage = {
2309
- type: "ready"; /** correlationId of the originating `init` message. */
2310
- correlationId: string;
2311
- workerId: string;
2312
- };
3449
+ declare function selectDecisionModel(flags: ReactorFeatureFlags, registry: IDocumentModelRegistry): RegisteredDecisionModel;
3450
+ //#endregion
3451
+ //#region src/decision/read-gate.d.ts
2313
3452
  /**
2314
- * Final result for an `execute` job.
2315
- *
2316
- * On success, `writeReady` carries the operations and job meta the
2317
- * parent needs to emit `JOB_WRITE_READY`. On failure, `error` is set
2318
- * and `result.success` is false.
2319
- *
2320
- * @see Wire Protocol Reference wiki page
2321
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3453
+ * Scopes every holder of a document may read, whatever the grants say. Denying
3454
+ * the policy itself would let a replica sync a document without it, read the
3455
+ * auth scope as uninitialized, and allow every operation it holds, so replicas
3456
+ * would diverge permanently. The document scope carries the metadata the same
3457
+ * argument covers. Grants gate domain-scope reads only.
2322
3458
  */
2323
- type ResultMessage = {
2324
- type: "result"; /** correlationId of the originating `execute` message. */
2325
- correlationId: string;
2326
- result: JobResult;
2327
- writeReady?: JobWriteReadyPayload;
2328
- error?: ErrorInfo;
2329
- };
3459
+ declare const ALWAYS_READABLE_SCOPES: ReadonlySet<string>;
2330
3460
  /**
2331
- * Acknowledges that a `load-model` request succeeded.
3461
+ * How a gate treats a document nobody has written a policy onto.
2332
3462
  *
2333
- * @see Wire Protocol Reference wiki page
2334
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3463
+ * A host that closes by default owes an uninitialized document the same silence
3464
+ * it owes a denied one, because "no policy yet" and "a policy that allows this"
3465
+ * are indistinguishable to a replica that never receives the scope. The option
3466
+ * exists because that answer belongs to the host serving the read, not to the
3467
+ * document: replay must keep reading an uninitialized document in full, or a
3468
+ * replica would refuse to rebuild state it already holds.
2335
3469
  */
2336
- type ModelLoadedMessage = {
2337
- type: "model-loaded"; /** correlationId of the originating `load-model` message. */
2338
- correlationId: string;
2339
- documentType: string;
2340
- version: string;
3470
+ type ReadGateOptions = {
3471
+ withholdUninitialized: boolean;
2341
3472
  };
3473
+ /** Whether a subject may read each scope of one document. */
3474
+ interface IReadGate {
3475
+ /**
3476
+ * Resolves, for one document, which of its scopes the subject may read.
3477
+ *
3478
+ * The predicate is resolved up front rather than asked per scope so that the
3479
+ * filtering itself stays synchronous, and so that a model backing the answer
3480
+ * is built once per document instead of once per scope.
3481
+ */
3482
+ scopePredicate(document: PHDocument, subject: AuthSubject, branch: string, signal?: AbortSignal): Promise<(scope: string) => boolean>;
3483
+ }
2342
3484
  /**
2343
- * Reports that a `load-model` request failed.
2344
- *
2345
- * @see Wire Protocol Reference wiki page
2346
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3485
+ * The model reads enforce. Below `authEnforcement` there is no model to
3486
+ * enforce: the document-only model ignores the auth scope entirely, so reading
3487
+ * through it would serve every domain scope of a policied document to anyone.
3488
+ * Undefined therefore means "evaluate the policy alone", which is what the read
3489
+ * surface did before the model existed.
2347
3490
  */
2348
- type ModelLoadFailedMessage = {
2349
- type: "model-load-failed"; /** correlationId of the originating `load-model` message. */
2350
- correlationId: string;
2351
- documentType: string;
2352
- version: string;
2353
- error: ErrorInfo;
2354
- };
3491
+ declare function readDecisionModel(flags: ReactorFeatureFlags, registry: IDocumentModelRegistry): RegisteredDecisionModel | undefined;
2355
3492
  /**
2356
- * Forwarded log line from the worker. `args` is constrained to
2357
- * {@link SanitizedArg} so callers cannot accidentally ship non-clonable
2358
- * values across the boundary.
2359
- *
2360
- * The sanitizer in `./sanitize.ts` enforces the {@link SanitizedArg}
2361
- * invariant on the producer side before each message is posted.
2362
- *
2363
- * @see Wire Protocol Reference wiki page
2364
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3493
+ * Evaluates the policy on its own, with no groups map and no condition context.
3494
+ * A `{ group }` or conditional grant therefore never applies: an allow that
3495
+ * does not apply withholds access, so this cannot widen a policy, but a policy
3496
+ * relying on a conditional deny is weaker here than it is written.
2365
3497
  */
2366
- type LogMessage = {
2367
- type: "log";
2368
- level: "debug" | "info" | "warn" | "error";
2369
- message: string;
2370
- args: SanitizedArg[]; /** Epoch milliseconds at which the worker generated the log line. */
2371
- timestamp: number;
2372
- };
3498
+ declare class BareReadGate implements IReadGate {
3499
+ scopePredicate(document: PHDocument, subject: AuthSubject): Promise<(scope: string) => boolean>;
3500
+ }
2373
3501
  /**
2374
- * Periodic liveness signal. Included now to unblock Phase-3 scaffolding;
2375
- * the wiki marks heartbeats as a Phase-5 future extension.
3502
+ * Answers a stream read from the document already fetched, and anything else
3503
+ * through the read side.
2376
3504
  *
2377
- * @see Wire Protocol Reference wiki page
2378
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3505
+ * The seed is why routing reads through a decision model costs no extra I/O for
3506
+ * the document being read: its `document` and `auth` scopes are the two static
3507
+ * projections, and the caller has both in hand. Only a group stream the grant
3508
+ * list names is fetched.
2379
3509
  */
2380
- type HeartbeatMessage = {
2381
- type: "heartbeat";
2382
- workerId: string; /** Epoch milliseconds the worker generated the heartbeat. */
2383
- timestamp: number; /** Optional snapshot of in-flight job correlation ids. */
2384
- inFlightCorrelationIds?: string[];
2385
- };
3510
+ declare class SeededStateReader implements IStreamStateReader {
3511
+ private readonly documentView;
3512
+ private readonly seed;
3513
+ private readonly branch;
3514
+ constructor(documentView: IDocumentView, seed: PHDocument, branch: string);
3515
+ /**
3516
+ * A stream this replica does not hold has to reach buildDecisionModel as the
3517
+ * absence it recognises, or the whole read fails instead of leaving the group
3518
+ * out of the model, where its principal does not match and the policy fails
3519
+ * closed. The read side reports absence as a plain Error, so the absence is
3520
+ * confirmed rather than inferred from the message: a transient failure must
3521
+ * surface, not silently deny.
3522
+ */
3523
+ getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
3524
+ private assertAbsent;
3525
+ }
2386
3526
  /**
2387
- * Periodic counters / gauges the worker reports for observability.
3527
+ * Evaluates a read against the registered decision model, built at the stream
3528
+ * heads. This is what makes `{ group }` principals and conditional grants apply
3529
+ * to a read: the model supplies the groups map and the scope's own state, the
3530
+ * same two things admission supplies.
2388
3531
  *
2389
- * @see Wire Protocol Reference wiki page
2390
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3532
+ * A read has no action, so a condition on `action.input.*` never holds for one.
3533
+ *
3534
+ * State is read through the read side rather than the write cache. The write
3535
+ * cache is invalidated by whichever process runs the executor, so a reactor
3536
+ * running its executors in worker processes would answer reads in the parent
3537
+ * from state no commit ever invalidates.
2391
3538
  */
2392
- type MetricsMessage = {
2393
- type: "metrics";
2394
- workerId: string; /** Epoch milliseconds the worker generated the metrics snapshot. */
2395
- timestamp: number;
2396
- counters: {
2397
- [name: string]: number;
2398
- };
2399
- gauges: {
2400
- [name: string]: number;
2401
- };
2402
- };
3539
+ declare class ModelReadGate implements IReadGate {
3540
+ private readonly model;
3541
+ private readonly documentView;
3542
+ /**
3543
+ * Whether a group a policy names is served to that policy's audience. Only
3544
+ * meaningful with `authGroups`, which is what makes a `{ group }` grant
3545
+ * match at all; below it the grant fails closed, so serving the roster
3546
+ * would publish a member list no read grant can use.
3547
+ */
3548
+ private readonly servesGroups;
3549
+ private readonly operationIndex?;
3550
+ private readonly logger?;
3551
+ private readonly options;
3552
+ constructor(model: RegisteredDecisionModel, documentView: IDocumentView,
3553
+ /**
3554
+ * Whether a group a policy names is served to that policy's audience. Only
3555
+ * meaningful with `authGroups`, which is what makes a `{ group }` grant
3556
+ * match at all; below it the grant fails closed, so serving the roster
3557
+ * would publish a member list no read grant can use.
3558
+ */
3559
+
3560
+ servesGroups: boolean, operationIndex?: IOperationIndex | undefined, logger?: ILogger | undefined, options?: ReadGateOptions);
3561
+ /**
3562
+ * A served group yields its member list and nothing else. What the audience
3563
+ * is owed is the state it must fold to evaluate auth with the group; a
3564
+ * group's other scopes are its own business and stay behind its own grants.
3565
+ */
3566
+ scopePredicate(document: PHDocument, subject: AuthSubject, branch: string, signal?: AbortSignal): Promise<(scope: string) => boolean>;
3567
+ /**
3568
+ * Whether the subject is served this group because a policy names it.
3569
+ *
3570
+ * A replica must fold a group's membership to evaluate auth with it, so a
3571
+ * group a grant names is served to the audience of the document that names
3572
+ * it, whatever the group's own read grants say. Naming a group in a policy
3573
+ * publishes its roster to that policy's audience; a group whose membership
3574
+ * must stay confidential does not belong in a grant.
3575
+ *
3576
+ * The referencing document's own domain scopes are the test. Its `auth` and
3577
+ * `document` scopes are readable by every holder, so testing those would
3578
+ * serve every referenced group to everybody.
3579
+ *
3580
+ * One level only. A referencer that is itself a group is skipped, and a
3581
+ * referencer's own readability is decided from its policy alone, so a
3582
+ * reference cycle terminates. Cycles are reachable: the reference relation
3583
+ * is recorded from an operation's input, including one later stored denied,
3584
+ * so a refused grant naming a group from inside another group leaves a row
3585
+ * behind that validation never saw.
3586
+ *
3587
+ * The referencers are probed a few at a time and the walk stops at the first
3588
+ * that serves, because a subject outside the audience is the case that runs to
3589
+ * the bound, and it is the common one. A probe that failed decides only when
3590
+ * nothing served: serving rests on a real allow, so this cannot widen, and it
3591
+ * stops one unreachable referencer from turning an allow already in hand into
3592
+ * a denial. A read records no operation, so replicas differing over a
3593
+ * transient failure has no consensus consequence.
3594
+ *
3595
+ * The probes are awaited together rather than raced, so none is ever left
3596
+ * running with nobody awaiting it, which is where unhandled rejections come
3597
+ * from.
3598
+ */
3599
+ private servesGroupTo;
3600
+ /**
3601
+ * Whether one referencing document serves the subject any domain scope. A
3602
+ * referencer this replica does not hold serves nothing, which fails closed
3603
+ * the same way a group it does not hold does.
3604
+ */
3605
+ private servesThrough;
3606
+ private servesGroup;
3607
+ /**
3608
+ * What this document's own policy says, with no group serving applied.
3609
+ *
3610
+ * An unpoliced document is readable in full unless the host closes by default,
3611
+ * and either way it is the common case and the one worth answering without
3612
+ * building anything. The test is the one `evaluate` makes: a legacy `{}` auth
3613
+ * scope and version 0 both mean uninitialized, and "no grants" does not,
3614
+ * because a policy with a version and an empty grant list denies everything.
3615
+ *
3616
+ * Closing here rather than around the gate is what keeps a policy able to
3617
+ * publish an unpoliced group it names: the referencer walk asks this question
3618
+ * of the referencing document, whose real policy answers it.
3619
+ */
3620
+ private ownPolicyPredicate;
3621
+ }
3622
+ //#endregion
3623
+ //#region src/client/reactor-client.d.ts
2403
3624
  /**
2404
- * Snapshot of one worker pool's acquire-wait samples and pool-stat counters,
2405
- * forwarded periodically so the host can re-record into the shared
2406
- * pg.Pool histogram and observable gauges. The worker owns the real
2407
- * pg.Pool; the host's {@link PoolInstrumentation} is a forwarder driven
2408
- * by these messages.
3625
+ * What {@link IReactorClient.evaluateActions} decides against: the decision
3626
+ * model this reactor enforces, and the flags that selected it.
3627
+ *
3628
+ * Both, because neither alone is enough. The flags say which of the model's
3629
+ * inputs a decision may read, and the model itself cannot be derived from them
3630
+ * here: selecting one needs the document model registry, which a client does not
3631
+ * hold. Absent, this client answers no preflight at all -- which is the whole of
3632
+ * the non-coexistence guarantee, since a client built without a reactor holding
3633
+ * a decision model has nothing to answer from.
2409
3634
  */
2410
- type PoolAcquireSamplesMessage = {
2411
- type: "pool-acquire-samples";
2412
- workerId: string; /** Stable identifier matching the host-side instrumentation name (e.g. "worker-0"). */
2413
- poolName: string; /** Epoch milliseconds the worker generated the batch. */
2414
- timestamp: number; /** Acquire-wait durations (ms) accumulated since the previous batch. */
2415
- durations: number[]; /** Most recent pg.Pool counter snapshot at batch send time. */
2416
- size: number;
2417
- idle: number;
2418
- waiting: number;
3635
+ type ActionEvaluationConfig = {
3636
+ model: RegisteredDecisionModel;
3637
+ flags: ReactorFeatureFlags;
2419
3638
  };
2420
3639
  /**
2421
- * Union of all messages a worker may send to the parent.
3640
+ * ReactorClient implementation that wraps lower-level APIs to provide
3641
+ * a simpler interface for document operations.
2422
3642
  *
2423
- * @see Wire Protocol Reference wiki page
2424
- * (Powerhouse board wiki id: 64c03e51-1aa4-4fa9-93d8-daa45642484d)
3643
+ * Features:
3644
+ * - Wraps Jobs with Promises for easier async handling
3645
+ * - Manages signing of submitted Action objects
3646
+ * - Provides quality-of-life functions for common tasks
3647
+ * - Wraps subscription interface with ViewFilters
2425
3648
  */
2426
- type WorkerMessage = ReadyMessage | ResultMessage | ModelLoadedMessage | ModelLoadFailedMessage | LogMessage | HeartbeatMessage | MetricsMessage | PoolAcquireSamplesMessage;
3649
+ declare class ReactorClient implements IReactorClient {
3650
+ private logger;
3651
+ private reactor;
3652
+ private signer;
3653
+ private subscriptionManager;
3654
+ private jobAwaiter;
3655
+ private documentIndexer;
3656
+ private documentView;
3657
+ private readGate;
3658
+ private actionEvaluation;
3659
+ readonly drives: IDriveClient;
3660
+ constructor(logger: ILogger, reactor: IReactor, signer: ISigner, subscriptionManager: IReactorSubscriptionManager, jobAwaiter: IJobAwaiter, documentIndexer: IDocumentIndexer, documentView: IDocumentView, readGate?: IReadGate, actionEvaluation?: ActionEvaluationConfig);
3661
+ private readSubject;
3662
+ /**
3663
+ * Which scopes of one document the subject may read. Resolved once per
3664
+ * document, so the gate builds its model once however many scopes are then
3665
+ * tested, and the filtering itself stays synchronous.
3666
+ */
3667
+ private readableScopes;
3668
+ /**
3669
+ * One document, filtered to the scopes the subject may read. Every method
3670
+ * that hands a document back goes through here, including the ones that
3671
+ * follow a write: a document returned from a mutation is a read like any
3672
+ * other, and returning it whole served scopes the same subject would be
3673
+ * refused by `get`. Its author still sees what it wrote, because an allow on
3674
+ * execute confers read of that scope.
3675
+ */
3676
+ private gateDocument;
3677
+ /**
3678
+ * Retrieves a list of document model modules.
3679
+ */
3680
+ getDocumentModelModules(namespace?: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<DocumentModelModule>>;
3681
+ /**
3682
+ * Retrieves a specific document model module by document type.
3683
+ *
3684
+ * @param documentType - The document type identifier
3685
+ * @returns The document model module
3686
+ */
3687
+ getDocumentModelModule(documentType: string): Promise<DocumentModelModule<any>>;
3688
+ /**
3689
+ * Retrieves the document model module matching the version the document is
3690
+ * stamped with, so not-yet-upgraded documents get the reducer their
3691
+ * history was written with rather than the latest.
3692
+ */
3693
+ getDocumentModelModuleForDocument(document: PHDocument): Promise<DocumentModelModule<any>>;
3694
+ /**
3695
+ * Retrieves a specific PHDocument
3696
+ */
3697
+ get<TDocument extends PHDocument>(identifier: string, view?: ViewFilter, signal?: AbortSignal): Promise<TDocument>;
3698
+ /**
3699
+ * Resolves an identifier (id or slug) to the canonical document id, using the
3700
+ * same lookup as the data path. Resolves against the "main" branch. Throws if
3701
+ * the identifier cannot be resolved or is ambiguous.
3702
+ */
3703
+ resolveIdOrSlug(identifier: string, view?: ViewFilter, signal?: AbortSignal): Promise<string>;
3704
+ /**
3705
+ * Retrieves operations for a document
3706
+ */
3707
+ getOperations(documentIdentifier: string, view?: ViewFilter, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
3708
+ private getOperationsWithCompositeCursor;
3709
+ /**
3710
+ * Retrieves outgoing relationships of a given type from a source document.
3711
+ */
3712
+ getOutgoingRelationships(sourceIdentifier: string, relationshipType: string, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
3713
+ /**
3714
+ * Retrieves incoming relationships of a given type to a target document.
3715
+ */
3716
+ getIncomingRelationships(targetIdentifier: string, relationshipType: string, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
3717
+ /**
3718
+ * Filters documents by criteria and returns a list of them
3719
+ */
3720
+ find(search: SearchFilter, view?: ViewFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<PHDocument>>;
3721
+ /**
3722
+ * Predicts the admission verdict for each candidate. See
3723
+ * {@link IReactorClient.evaluateActions} for the contract and its caveats.
3724
+ *
3725
+ * Read-only throughout, and never through the write cache: that cache is
3726
+ * invalidated by whichever process runs the executor, so a reactor running
3727
+ * its executors in worker processes would answer here from state no commit
3728
+ * ever invalidates.
3729
+ */
3730
+ evaluateActions(documentIdentifier: string, branch: string, candidates: ActionCandidate[], subject?: AuthSubject, signal?: AbortSignal): Promise<ActionEvaluations>;
3731
+ /**
3732
+ * The decision model for one target document, built at its stream heads.
3733
+ *
3734
+ * The document is fetched unfiltered, because the policy is what decides:
3735
+ * reading it through the read gate would withhold the very scopes the
3736
+ * decision is about. A deleted document is served at its deletion boundary,
3737
+ * which is what lets the model refuse an execute against it -- authEnforcement
3738
+ * requires documentDecisions, so that read is available whenever this runs.
3739
+ *
3740
+ * Reading past the gate discloses nothing a submit does not. The `auth` and
3741
+ * `document` scopes are readable by every holder, so a verdict resting on the
3742
+ * policy alone is one the caller could compute unaided; and a verdict resting
3743
+ * on a conditional grant reads the executing scope's state exactly as
3744
+ * admission reads it, so the answer here is what submitting and being refused
3745
+ * would have revealed anyway.
3746
+ *
3747
+ * The append condition the build records is dropped. It guards a write, and
3748
+ * this makes none; reproducing it is also what the preflight cannot do, which
3749
+ * is why the answer is a prediction.
3750
+ */
3751
+ private buildEvaluationTarget;
3752
+ /**
3753
+ * Creates a document and waits for completion
3754
+ */
3755
+ create<TDocument extends PHDocument = PHDocument>(document: PHDocument, parentIdentifier?: string, signal?: AbortSignal): Promise<TDocument>;
3756
+ /**
3757
+ * Creates an empty document and waits for completion
3758
+ */
3759
+ createEmpty<TDocument extends PHDocument>(documentModelType: string, options?: CreateDocumentOptions, signal?: AbortSignal): Promise<TDocument>;
3760
+ /**
3761
+ * Upgrades a document to a newer document model version by dispatching an
3762
+ * UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the
3763
+ * latest registered module version for the document's type. Returns the
3764
+ * document unchanged when it is already at the target version.
3765
+ *
3766
+ * The executor validates the action's version and revision snapshot against
3767
+ * the state the migration actually runs on. When a concurrent edit
3768
+ * invalidates the snapshot, the upgrade is rebuilt from a fresh read and
3769
+ * retried up to maxConflictRetries times before the conflict is surfaced.
3770
+ */
3771
+ upgradeDocument<TDocument extends PHDocument = PHDocument>(documentIdentifier: string, toVersion?: number, options?: UpgradeDocumentOptions, signal?: AbortSignal): Promise<TDocument>;
3772
+ /**
3773
+ * Creates an empty document in a drive as a single batched operation.
3774
+ * Delegates to {@link IDriveClient.addFile}.
3775
+ *
3776
+ * @deprecated Use `client.drives.addFile` instead. This method will be
3777
+ * removed in a future release.
3778
+ */
3779
+ createDocumentInDrive<TDocument extends PHDocument>(driveId: string, document: PHDocument, parentFolder?: string, signal?: AbortSignal): Promise<TDocument>;
3780
+ /**
3781
+ * Applies a list of actions to a document and waits for completion
3782
+ */
3783
+ execute<TDocument extends PHDocument>(documentIdentifier: string, branch: string, actions: Action[], signal?: AbortSignal): Promise<TDocument>;
3784
+ /**
3785
+ * Submits a list of actions to a document
3786
+ */
3787
+ executeAsync(documentIdentifier: string, branch: string, actions: Action[], signal?: AbortSignal): Promise<JobInfo>;
3788
+ executeBatch(request: BatchExecutionRequest, signal?: AbortSignal): Promise<BatchExecutionResult>;
3789
+ /**
3790
+ * Renames a document and waits for completion
3791
+ */
3792
+ rename(documentIdentifier: string, name: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3793
+ /**
3794
+ * Updates the preferred editor recorded in the document header meta.
3795
+ * Pass `null` to clear it.
3796
+ */
3797
+ setPreferredEditor(documentIdentifier: string, preferredEditor: string | null, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3798
+ /**
3799
+ * Adds multiple documents as children to another and waits for completion
3800
+ */
3801
+ addRelationship(sourceIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3802
+ /**
3803
+ * Removes a relationship between two documents and waits for completion.
3804
+ */
3805
+ removeRelationship(sourceIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<PHDocument>;
3806
+ /**
3807
+ * Moves a relationship from one source document to another and waits for completion.
3808
+ */
3809
+ moveRelationship(sourceParentIdentifier: string, targetParentIdentifier: string, targetIdentifier: string, relationshipType: string, branch?: string, signal?: AbortSignal): Promise<{
3810
+ source: PHDocument;
3811
+ target: PHDocument;
3812
+ }>;
3813
+ loadBatch(request: BatchLoadRequest, signal?: AbortSignal): Promise<BatchLoadResult>;
3814
+ /**
3815
+ * Deletes a document and waits for completion
3816
+ */
3817
+ deleteDocument(identifier: string, propagate?: PropagationMode, signal?: AbortSignal): Promise<void>;
3818
+ /**
3819
+ * Deletes documents and waits for completion
3820
+ */
3821
+ deleteDocuments(identifiers: string[], propagate?: PropagationMode, signal?: AbortSignal): Promise<void>;
3822
+ /**
3823
+ * Retrieves the status of a job
3824
+ */
3825
+ getJobStatus(jobId: string, signal?: AbortSignal): Promise<JobInfo>;
3826
+ /**
3827
+ * Waits for a job to complete
3828
+ */
3829
+ waitForJob(jobId: string | JobInfo, signal?: AbortSignal): Promise<JobInfo>;
3830
+ /**
3831
+ * Subscribes to changes for documents matching specified filters
3832
+ */
3833
+ subscribe(search: SearchFilter, callback: (event: DocumentChangeEvent) => void, view?: ViewFilter): () => void;
3834
+ private removeAllIncomingRelationships;
3835
+ }
2427
3836
  //#endregion
2428
3837
  //#region src/executor/interfaces.d.ts
2429
3838
  /**
@@ -2679,9 +4088,11 @@ interface IQueue {
2679
4088
  * Retry a failed job.
2680
4089
  * @param jobId - The ID of the job to retry
2681
4090
  * @param error - Optional error information from the failure
4091
+ * @param accounting - Whether the attempt counts against the job's retry
4092
+ * limit; defaults to {@link RetryAccounting.CountAgainstLimit}
2682
4093
  * @returns Promise that resolves when the job is requeued for retry
2683
4094
  */
2684
- retryJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
4095
+ retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
2685
4096
  /**
2686
4097
  * Returns true if and only if all jobs have been resolved.
2687
4098
  */
@@ -2692,211 +4103,86 @@ interface IQueue {
2692
4103
  */
2693
4104
  block(onDrained?: () => void): void;
2694
4105
  /**
2695
- * Unblocks the queue from accepting new jobs.
2696
- */
2697
- unblock(): void;
2698
- }
2699
- //#endregion
2700
- //#region src/read-models/types.d.ts
2701
- interface ViewStateTable {
2702
- readModelId: string;
2703
- lastOrdinal: number;
2704
- lastOperationTimestamp: Generated<Date>;
2705
- }
2706
- interface DocumentSnapshotTable {
2707
- id: Generated<string>;
2708
- documentId: string;
2709
- slug: string | null;
2710
- name: string | null;
2711
- scope: string;
2712
- branch: string;
2713
- content: unknown;
2714
- documentType: string;
2715
- lastOperationIndex: number;
2716
- lastOperationHash: string;
2717
- lastUpdatedAt: Generated<Date>;
2718
- snapshotVersion: Generated<number>;
2719
- identifiers: unknown;
2720
- metadata: unknown;
2721
- isDeleted: Generated<boolean>;
2722
- deletedAt: Date | null;
2723
- }
2724
- interface SlugMappingTable {
2725
- slug: string;
2726
- documentId: string;
2727
- scope: string;
2728
- branch: string;
2729
- createdAt: Generated<Date>;
2730
- updatedAt: Generated<Date>;
2731
- }
2732
- interface ProcessorCursorTable {
2733
- processorId: string;
2734
- factoryId: string;
2735
- driveId: string;
2736
- processorIndex: number;
2737
- lastOrdinal: Generated<number>;
2738
- status: Generated<string>;
2739
- lastError: string | null;
2740
- lastErrorTimestamp: Date | null;
2741
- createdAt: Generated<Date>;
2742
- updatedAt: Generated<Date>;
2743
- }
2744
- interface DocumentViewDatabase {
2745
- ViewState: ViewStateTable;
2746
- DocumentSnapshot: DocumentSnapshotTable;
2747
- SlugMapping: SlugMappingTable;
2748
- ProcessorCursor: ProcessorCursorTable;
2749
- }
2750
- type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;
2751
- //#endregion
2752
- //#region src/core/model-sources.d.ts
2753
- /** An importable file holding one or more document-model exports. */
2754
- type FileModelSource = {
2755
- filePath: string;
2756
- exportName?: string;
2757
- };
2758
- /** An importable package specifier holding one or more document-model exports. */
2759
- type PackageModelSource = {
2760
- packageName: string;
2761
- subpath?: string;
2762
- exportName?: string;
2763
- };
2764
- /**
2765
- * A source of document models: a live module, an importable file, or an
2766
- * importable package. File and package sources can cross a worker-thread
2767
- * boundary (workers re-import them); a live module cannot.
2768
- */
2769
- type DocumentModelSource = DocumentModelModule<any> | FileModelSource | PackageModelSource;
2770
- //#endregion
2771
- //#region src/registry/interfaces.d.ts
2772
- type RegistrationResult<T> = {
2773
- status: "success";
2774
- item: T;
2775
- } | {
2776
- status: "error";
2777
- item: T;
2778
- error: Error;
2779
- };
2780
- /**
2781
- * Loader that asynchronously resolves a document type to a
2782
- * {@link DocumentModelSource}. Used by the queue to gate CREATE_DOCUMENT
2783
- * jobs until the required model is available in the registry.
2784
- *
2785
- * Return an importable source ({ filePath } or { packageName }) whenever
2786
- * possible: the resolver registers the resolved models on the host registry
2787
- * and broadcasts importable sources to executor workers. A live
2788
- * DocumentModelModule is also valid but host-only — it cannot cross a
2789
- * worker-thread boundary, so worker pools will not receive it.
2790
- */
2791
- interface IDocumentModelLoader {
2792
- load(documentType: string): Promise<DocumentModelSource>;
2793
- }
2794
- /**
2795
- * Registry for managing document model modules.
2796
- * Provides centralized access to document models' reducers, utils, and specifications.
2797
- * Supports version-aware module storage and upgrade manifest management.
2798
- */
2799
- interface IDocumentModelRegistry {
2800
- /**
2801
- * Register multiple modules at once.
2802
- * Modules without a version field default to version 1.
2803
- * Invalid or duplicate modules are skipped without breaking registration of the remaining modules.
2804
- *
2805
- * @param modules Document model modules to register
2806
- * @returns Array of results, one per module, indicating success or failure
2807
- */
2808
- registerModules(...modules: DocumentModelModule<any>[]): RegistrationResult<DocumentModelModule<any>>[];
2809
- /**
2810
- * Unregister all versions of the specified document types.
2811
- *
2812
- * @param documentTypes The document types to unregister
2813
- * @returns true if all modules were unregistered, false if any were not found
2814
- */
2815
- unregisterModules(...documentTypes: string[]): boolean;
2816
- /**
2817
- * Get a specific document model module by document type and optional version.
2818
- * If version is not specified, returns the latest version.
2819
- *
2820
- * @param documentType The document type identifier
2821
- * @param version Optional version number to retrieve
2822
- * @returns The document model module
2823
- * @throws ModuleNotFoundError if the document type or version is not registered
2824
- */
2825
- getModule(documentType: string, version?: number): DocumentModelModule<any>;
2826
- /**
2827
- * Get all registered document model modules.
2828
- *
2829
- * @returns Array of all registered modules
2830
- */
2831
- getAllModules(): DocumentModelModule<any>[];
2832
- /**
2833
- * Clear all registered modules and upgrade manifests.
2834
- */
2835
- clear(): void;
2836
- /**
2837
- * Get all supported versions for a document type, sorted in ascending order.
2838
- *
2839
- * @param documentType The document type identifier
2840
- * @returns Array of version numbers sorted ascending
2841
- * @throws ModuleNotFoundError if no modules are registered for the document type
2842
- */
2843
- getSupportedVersions(documentType: string): number[];
2844
- /**
2845
- * Get the latest (highest) version number for a document type.
2846
- *
2847
- * @param documentType The document type identifier
2848
- * @returns The highest version number registered for this document type
2849
- * @throws ModuleNotFoundError if no modules are registered for the document type
2850
- */
2851
- getLatestVersion(documentType: string): number;
2852
- /**
2853
- * Register upgrade manifests that define upgrade paths between versions.
2854
- * Invalid or duplicate manifests are skipped without breaking registration of the remaining manifests.
2855
- *
2856
- * @param manifests Upgrade manifests to register
2857
- * @returns Array of results, one per manifest, indicating success or failure
2858
- */
2859
- registerUpgradeManifests(...manifests: UpgradeManifest<readonly number[]>[]): RegistrationResult<UpgradeManifest<readonly number[]>>[];
2860
- /**
2861
- * Unregister upgrade manifests for the specified document types.
2862
- * @param documentTypes The document types whose upgrade manifests should be unregistered
2863
- * @returns true if all modules were unregistered, false if any were not found
2864
- **/
2865
- unregisterUpgradeManifests(...documentTypes: string[]): boolean;
2866
- /**
2867
- * Get the upgrade manifest for a document type.
2868
- *
2869
- * @param documentType The document type identifier
2870
- * @returns The upgrade manifest
2871
- * @throws ManifestNotFoundError if no manifest is registered for the document type
2872
- */
2873
- getUpgradeManifest(documentType: string): UpgradeManifest<readonly number[]>;
2874
- /**
2875
- * Compute the upgrade path from one version to another.
2876
- * Returns the sequence of upgrade transitions needed.
2877
- *
2878
- * @param documentType The document type identifier
2879
- * @param fromVersion The starting version
2880
- * @param toVersion The target version
2881
- * @returns Array of upgrade transitions in order
2882
- * @throws DowngradeNotSupportedError if toVersion is less than fromVersion
2883
- * @throws ManifestNotFoundError if no upgrade manifest is registered
2884
- * @throws MissingUpgradeTransitionError if any transition in the path is missing
2885
- */
2886
- computeUpgradePath(documentType: string, fromVersion: number, toVersion: number): UpgradeTransition[];
2887
- /**
2888
- * Get the upgrade reducer for a single-step version transition.
2889
- *
2890
- * @param documentType The document type identifier
2891
- * @param fromVersion The starting version
2892
- * @param toVersion The target version (must be fromVersion + 1)
2893
- * @returns The upgrade reducer function
2894
- * @throws InvalidUpgradeStepError if toVersion is not fromVersion + 1
2895
- * @throws ManifestNotFoundError if no upgrade manifest is registered
2896
- * @throws MissingUpgradeTransitionError if the transition is not found
4106
+ * Unblocks the queue from accepting new jobs.
2897
4107
  */
2898
- getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
4108
+ unblock(): void;
4109
+ }
4110
+ //#endregion
4111
+ //#region src/core/group-reevaluation-trigger.d.ts
4112
+ /**
4113
+ * Watches committed writes for group membership changes and enqueues a
4114
+ * re-evaluation job for every document whose auth history references the
4115
+ * changed group, found through the reverse direction of the group-reference
4116
+ * relation. Each affected document is re-judged in its own job, so the work
4117
+ * runs under that document's execution slot rather than the group's.
4118
+ *
4119
+ * The job carries the earliest changed membership timestamp; the executor
4120
+ * skips the pass when everything the document holds sorts before it, which
4121
+ * keeps the common case (a membership write later than all history) free.
4122
+ */
4123
+ declare class GroupReevaluationTrigger {
4124
+ private logger;
4125
+ private eventBus;
4126
+ private queue;
4127
+ private operationIndex;
4128
+ private unsubscribe?;
4129
+ constructor(logger: ILogger, eventBus: IEventBus, queue: IQueue, operationIndex: IOperationIndex);
4130
+ startup(): void;
4131
+ shutdown(): void;
4132
+ private onWriteReady;
4133
+ }
4134
+ //#endregion
4135
+ //#region src/read-models/types.d.ts
4136
+ interface ViewStateTable {
4137
+ readModelId: string;
4138
+ lastOrdinal: number;
4139
+ lastOperationTimestamp: Generated<Date>;
4140
+ }
4141
+ interface DocumentSnapshotTable {
4142
+ id: Generated<string>;
4143
+ documentId: string;
4144
+ slug: string | null;
4145
+ name: string | null;
4146
+ scope: string;
4147
+ branch: string;
4148
+ content: unknown;
4149
+ documentType: string;
4150
+ lastOperationIndex: number;
4151
+ lastOperationHash: string;
4152
+ lastUpdatedAt: Generated<Date>;
4153
+ snapshotVersion: Generated<number>;
4154
+ identifiers: unknown;
4155
+ metadata: unknown;
4156
+ isDeleted: Generated<boolean>;
4157
+ deletedAt: Date | null;
4158
+ }
4159
+ interface SlugMappingTable {
4160
+ slug: string;
4161
+ documentId: string;
4162
+ scope: string;
4163
+ branch: string;
4164
+ createdAt: Generated<Date>;
4165
+ updatedAt: Generated<Date>;
4166
+ }
4167
+ interface ProcessorCursorTable {
4168
+ processorId: string;
4169
+ factoryId: string;
4170
+ driveId: string;
4171
+ processorIndex: number;
4172
+ lastOrdinal: Generated<number>;
4173
+ status: Generated<string>;
4174
+ lastError: string | null;
4175
+ lastErrorTimestamp: Date | null;
4176
+ createdAt: Generated<Date>;
4177
+ updatedAt: Generated<Date>;
4178
+ }
4179
+ interface DocumentViewDatabase {
4180
+ ViewState: ViewStateTable;
4181
+ DocumentSnapshot: DocumentSnapshotTable;
4182
+ SlugMapping: SlugMappingTable;
4183
+ ProcessorCursor: ProcessorCursorTable;
2899
4184
  }
4185
+ type InsertableDocumentSnapshot = Insertable<DocumentSnapshotTable>;
2900
4186
  //#endregion
2901
4187
  //#region src/shared/consistency-tracker.d.ts
2902
4188
  interface IConsistencyTracker {
@@ -2946,134 +4232,6 @@ declare class ConsistencyTracker implements IConsistencyTracker {
2946
4232
  private removeWaiter;
2947
4233
  }
2948
4234
  //#endregion
2949
- //#region src/storage/kysely/types.d.ts
2950
- interface OperationTable {
2951
- id: Generated<number>;
2952
- jobId: string;
2953
- opId: string;
2954
- prevOpId: string;
2955
- writeTimestampUtcMs: Generated<Date>;
2956
- documentId: string;
2957
- documentType: string;
2958
- scope: string;
2959
- branch: string;
2960
- timestampUtcMs: Date;
2961
- index: number;
2962
- action: unknown;
2963
- skip: number;
2964
- error?: string | null;
2965
- hash: string;
2966
- }
2967
- interface KeyframeTable {
2968
- id: Generated<number>;
2969
- documentId: string;
2970
- documentType: string;
2971
- scope: string;
2972
- branch: string;
2973
- revision: number;
2974
- document: unknown;
2975
- createdAt: Generated<Date>;
2976
- }
2977
- interface DocumentCollectionTable {
2978
- documentId: string;
2979
- collectionId: string;
2980
- joinedOrdinal: bigint;
2981
- leftOrdinal: bigint | null;
2982
- }
2983
- interface OperationIndexOperationTable {
2984
- ordinal: Generated<number>;
2985
- opId: string;
2986
- documentId: string;
2987
- documentType: string;
2988
- scope: string;
2989
- branch: string;
2990
- timestampUtcMs: string;
2991
- writeTimestampUtcMs: Generated<Date>;
2992
- index: number;
2993
- skip: number;
2994
- hash: string;
2995
- action: unknown;
2996
- sourceRemote: Generated<string>;
2997
- }
2998
- interface SyncRemoteTable {
2999
- name: string;
3000
- collection_id: string;
3001
- channel_type: string;
3002
- channel_id: string;
3003
- remote_name: string;
3004
- channel_parameters: unknown;
3005
- filter_document_ids: unknown;
3006
- filter_scopes: unknown;
3007
- filter_branch: string;
3008
- push_state: string;
3009
- push_last_success_utc_ms: string | null;
3010
- push_last_failure_utc_ms: string | null;
3011
- push_failure_count: number;
3012
- pull_state: string;
3013
- pull_last_success_utc_ms: string | null;
3014
- pull_last_failure_utc_ms: string | null;
3015
- pull_failure_count: number;
3016
- created_at: Generated<Date>;
3017
- updated_at: Generated<Date>;
3018
- }
3019
- interface SyncCursorTable {
3020
- remote_name: string;
3021
- cursor_type: string;
3022
- cursor_ordinal: bigint;
3023
- last_synced_at_utc_ms: string | null;
3024
- updated_at: Generated<Date>;
3025
- }
3026
- /**
3027
- * Kysely table definition for the `sync_dead_letters` table.
3028
- */
3029
- interface SyncDeadLetterTable {
3030
- ordinal: Generated<number>;
3031
- id: string;
3032
- job_id: string;
3033
- job_dependencies: unknown;
3034
- remote_name: string;
3035
- document_id: string;
3036
- scopes: unknown;
3037
- branch: string;
3038
- operations: unknown;
3039
- error_source: string;
3040
- error_message: string;
3041
- created_at: Generated<Date>;
3042
- }
3043
- interface Database$1 {
3044
- Operation: OperationTable;
3045
- Keyframe: KeyframeTable;
3046
- document_collections: DocumentCollectionTable;
3047
- operation_index_operations: OperationIndexOperationTable;
3048
- sync_remotes: SyncRemoteTable;
3049
- sync_cursors: SyncCursorTable;
3050
- sync_dead_letters: SyncDeadLetterTable;
3051
- }
3052
- interface DocumentTable {
3053
- id: string;
3054
- createdAt: Generated<Date>;
3055
- updatedAt: Generated<Date>;
3056
- }
3057
- interface DocumentRelationshipTable {
3058
- id: Generated<string>;
3059
- sourceId: string;
3060
- targetId: string;
3061
- relationshipType: string;
3062
- metadata: unknown;
3063
- createdAt: Generated<Date>;
3064
- updatedAt: Generated<Date>;
3065
- }
3066
- interface IndexerStateTable {
3067
- id: Generated<number>;
3068
- lastOperationId: number;
3069
- lastOperationTimestamp: Generated<Date>;
3070
- }
3071
- interface DocumentIndexerDatabase {
3072
- Document: DocumentTable;
3073
- DocumentRelationship: DocumentRelationshipTable;
3074
- IndexerState: IndexerStateTable;
3075
- }
3076
- //#endregion
3077
4235
  //#region src/storage/pool-instrumentation.d.ts
3078
4236
  /**
3079
4237
  * Snapshot of a pg.Pool's internal counters at a point in time.
@@ -3122,6 +4280,45 @@ type ForwardingPoolInstrumentation = PoolInstrumentation & {
3122
4280
  };
3123
4281
  //#endregion
3124
4282
  //#region src/sync/errors.d.ts
4283
+ type GraphQLRequestErrorCategory = "network" | "http" | "parse" | "graphql" | "missing-data";
4284
+ declare class GraphQLRequestError extends Error {
4285
+ readonly statusCode: number | undefined;
4286
+ readonly category: GraphQLRequestErrorCategory;
4287
+ /**
4288
+ * One entry per error the response carried, in order, holding its
4289
+ * `extensions.code` - undefined where it declared none. Kept per error rather
4290
+ * than as a set, because a response that mixes a classified error with an
4291
+ * unclassified one must not be read as if only the classified one arrived.
4292
+ */
4293
+ readonly codes: readonly (string | undefined)[];
4294
+ constructor(message: string, category: GraphQLRequestErrorCategory, statusCode?: number, codes?: readonly (string | undefined)[]);
4295
+ }
4296
+ /**
4297
+ * Extension codes a remote uses to say a failure is worth polling through.
4298
+ *
4299
+ * Shared with reactor-api so the server throws what this check reads and the two
4300
+ * cannot drift. A `graphql` category error is otherwise permanent: it stops the
4301
+ * poll timer, and nothing restarts it, so a code that lands here is the
4302
+ * difference between a channel that recovers and one that is dead for the
4303
+ * process lifetime.
4304
+ */
4305
+ declare const RECOVERABLE_GRAPHQL_ERROR_CODES: {
4306
+ /**
4307
+ * A stored operation cannot be represented in the schema - an action with no
4308
+ * id, say. The document holding it needs repairing, but the channel serves
4309
+ * every other document, and a peer that stopped polling would stop receiving
4310
+ * those too.
4311
+ */
4312
+ readonly malformedStoredOperation: "MALFORMED_STORED_OPERATION";
4313
+ };
4314
+ /**
4315
+ * True when every error the response carried named a recoverable code.
4316
+ *
4317
+ * Unanimity is the requirement: one unclassified error alongside a recoverable
4318
+ * one means something else also went wrong, and polling through that would be
4319
+ * guessing.
4320
+ */
4321
+ declare function isRecoverableGraphQLError(error: GraphQLRequestError): boolean;
3125
4322
  /** Auth-rejection message fragments the switchboard emits. Shared with
3126
4323
  * reactor-api so server throws and this client check can't drift. */
3127
4324
  declare const DRIVE_AUTH_ERROR_MESSAGES: {
@@ -3137,7 +4334,13 @@ declare class PollingChannelError extends Error {
3137
4334
  declare class ChannelError extends Error {
3138
4335
  source: ChannelErrorSource;
3139
4336
  error: Error;
3140
- constructor(source: ChannelErrorSource, error: Error);
4337
+ /**
4338
+ * The classification when something other than the error carries it. Absent
4339
+ * means derive it from `error.name`; a dead letter mirrored from a peer sets it,
4340
+ * because only the message crosses the wire.
4341
+ */
4342
+ readonly errorType?: SyncOperationErrorType;
4343
+ constructor(source: ChannelErrorSource, error: Error, errorType?: SyncOperationErrorType);
3141
4344
  }
3142
4345
  //#endregion
3143
4346
  //#region src/sync/sync-operation.d.ts
@@ -3404,6 +4607,19 @@ interface ISyncManager {
3404
4607
  * @throws Error if a remote with this name already exists
3405
4608
  */
3406
4609
  add(name: string, collectionId: DriveCollectionId, channelConfig: ChannelConfig, filter?: RemoteFilter, options?: RemoteOptions, id?: string): Promise<Remote>;
4610
+ /**
4611
+ * Binds a remote to an address, so only that address may poll it.
4612
+ *
4613
+ * This is adoption, not configuration: a channel created anonymously is
4614
+ * unbound and serves whatever an anonymous subject may read, and the first
4615
+ * authenticated subject to poll it claims it. Binding an already-bound remote
4616
+ * to a different address is refused rather than allowed to steal it.
4617
+ *
4618
+ * @param id - The id of the remote to bind
4619
+ * @param boundAddress - The address that henceforth owns the channel
4620
+ * @throws Error if the remote does not exist, or is bound to another address
4621
+ */
4622
+ bindRemote(id: string, boundAddress: string): Promise<void>;
3407
4623
  /**
3408
4624
  * Triggers a one-shot pull for the named remote. Useful for Manual poll-behavior
3409
4625
  * remotes, where the channel is registered but does not poll on a schedule.
@@ -3736,6 +4952,12 @@ interface ReactorModule {
3736
4952
  * integration scenarios.
3737
4953
  */
3738
4954
  interface InProcessReactorModule extends ReactorModule {
4955
+ /**
4956
+ * The enforcement flags this reactor resolved, as plain booleans. Held on the
4957
+ * module because they select what a read enforces as well as what a write
4958
+ * does, and the read surface is composed outside the reactor.
4959
+ */
4960
+ featureFlags: ReactorFeatureFlags;
3739
4961
  queue: IQueue;
3740
4962
  jobTracker: IJobTracker;
3741
4963
  executorManager: IJobExecutorManager;
@@ -3754,6 +4976,12 @@ interface InProcessReactorModule extends ReactorModule {
3754
4976
  processorManagerConsistencyTracker: IConsistencyTracker;
3755
4977
  reactor: IReactor;
3756
4978
  syncModule: InProcessSyncModule | undefined;
4979
+ /**
4980
+ * Present when authGroups is on: enqueues re-evaluation jobs for the
4981
+ * documents a group membership change affects. Started by the builder;
4982
+ * hosts shut it down alongside the sync manager.
4983
+ */
4984
+ groupReevaluationTrigger: GroupReevaluationTrigger | undefined;
3757
4985
  /**
3758
4986
  * Instrumented pg.Pool handles registered with the builder, either by
3759
4987
  * createPostgresDatabase or by withInstrumentedPool. Empty when no pg
@@ -3815,12 +5043,6 @@ declare class DriveClient implements IDriveClient {
3815
5043
  private removeFileNode;
3816
5044
  }
3817
5045
  //#endregion
3818
- //#region src/cache/collection-membership-cache.d.ts
3819
- interface ICollectionMembershipCache {
3820
- getCollectionsForDocuments(documentIds: string[]): Promise<Record<string, string[]>>;
3821
- invalidate(documentId: string): void;
3822
- }
3823
- //#endregion
3824
5046
  //#region src/registry/document-model-resolver.d.ts
3825
5047
  interface IDocumentModelResolver {
3826
5048
  ensureModelLoaded(documentType: string): Promise<void>;
@@ -3866,51 +5088,19 @@ declare class DocumentModelResolver implements IDocumentModelResolver {
3866
5088
  * Checks the registry for the model and returns if found; throws if not.
3867
5089
  * Since there is no loader, missing models cannot be recovered.
3868
5090
  */
3869
- declare class NullDocumentModelResolver implements IDocumentModelResolver {
3870
- private registry?;
3871
- constructor(registry?: IDocumentModelRegistry | undefined);
3872
- ensureModelLoaded(documentType: string): Promise<void>;
3873
- }
3874
- //#endregion
3875
- //#region src/executor/worker-pool-job-executor-manager.d.ts
3876
- /**
3877
- * Factory invoked once per worker at `start()` time. The index is the
3878
- * worker's position in the pool and the same value the manager will use
3879
- * for sticky routing (`bucketFor(documentId) === index`).
3880
- */
3881
- type WorkerFactory = (index: number) => IExecutorWorker;
3882
- //#endregion
3883
- //#region src/cache/write-cache-types.d.ts
3884
- /**
3885
- * Configuration options for the write cache
3886
- */
3887
- type WriteCacheConfig = {
3888
- /** Maximum number of document streams to cache (LRU eviction). Default: 1000 */maxDocuments: number; /** Number of snapshots to keep in each document's ring buffer. Default: 10 */
3889
- ringBufferSize: number; /** Persist a keyframe snapshot every N revisions. Default: 10 */
3890
- keyframeInterval: number;
3891
- };
3892
- /**
3893
- * Unique identifier for a document stream
3894
- */
3895
- type DocumentStreamKey = {
3896
- /** Document identifier */documentId: string; /** Operation scope */
3897
- scope: string; /** Branch name */
3898
- branch: string;
3899
- };
3900
- /**
3901
- * A cached document snapshot at a specific revision
3902
- */
3903
- type CachedSnapshot = {
3904
- /** The revision number of this snapshot */revision: number; /** The document state at this revision */
3905
- document: PHDocument;
3906
- };
5091
+ declare class NullDocumentModelResolver implements IDocumentModelResolver {
5092
+ private registry?;
5093
+ constructor(registry?: IDocumentModelRegistry | undefined);
5094
+ ensureModelLoaded(documentType: string): Promise<void>;
5095
+ }
5096
+ //#endregion
5097
+ //#region src/executor/worker-pool-job-executor-manager.d.ts
3907
5098
  /**
3908
- * Serialized keyframe snapshot for K/V store persistence
5099
+ * Factory invoked once per worker at `start()` time. The index is the
5100
+ * worker's position in the pool and the same value the manager will use
5101
+ * for sticky routing (`bucketFor(documentId) === index`).
3909
5102
  */
3910
- type KeyframeSnapshot = {
3911
- /** The revision number of this keyframe */revision: number; /** Serialized document state */
3912
- document: string;
3913
- };
5103
+ type WorkerFactory = (index: number) => IExecutorWorker;
3914
5104
  //#endregion
3915
5105
  //#region src/projection/protocol.d.ts
3916
5106
  /**
@@ -4150,6 +5340,7 @@ declare class SyncBuilder {
4150
5340
  withDeadLetterStorage(storage: ISyncDeadLetterStorage): this;
4151
5341
  withMaxDeadLettersPerRemote(limit: number): this;
4152
5342
  withMaxInboxBatchSize(limit: number): this;
5343
+ withMaxHeldOperationsPerRemote(limit: number): this;
4153
5344
  build(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): ISyncManager;
4154
5345
  buildModule(reactor: IReactor, logger: ILogger, operationIndex: IOperationIndex, eventBus: IEventBus, db: Kysely<Database$1>, driveContainerTypes: ReadonlySet<string>): InProcessSyncModule;
4155
5346
  }
@@ -4162,6 +5353,7 @@ declare class SyncBuilder {
4162
5353
  * them (`BaseReadModel` subclasses, in particular).
4163
5354
  */
4164
5355
  interface ReadModelFactoryDeps {
5356
+ documentModelRegistry: IDocumentModelRegistry;
4165
5357
  operationIndex: IOperationIndex;
4166
5358
  writeCache: IWriteCache;
4167
5359
  processorManagerConsistencyTracker: IConsistencyTracker;
@@ -4383,6 +5575,7 @@ declare class ReactorClientBuilder {
4383
5575
  private subscriptionManager?;
4384
5576
  private jobAwaiter?;
4385
5577
  private documentModelLoader?;
5578
+ private readGate?;
4386
5579
  /**
4387
5580
  * Sets the logger for the ReactorClient.
4388
5581
  * @param logger - The logger to use.
@@ -4406,6 +5599,36 @@ declare class ReactorClientBuilder {
4406
5599
  withSubscriptionManager(subscriptionManager: IReactorSubscriptionManager): this;
4407
5600
  withJobAwaiter(jobAwaiter: IJobAwaiter): this;
4408
5601
  withDocumentModelLoader(loader: IDocumentModelLoader): this;
5602
+ /**
5603
+ * Overrides how reads are gated. A client built from a ReactorBuilder derives
5604
+ * this from that reactor's flags; one built from `withReactor` cannot, because
5605
+ * it is handed no flags and no registry, so it gates on the policy alone
5606
+ * unless a gate is supplied here.
5607
+ */
5608
+ withReadGate(readGate: IReadGate): this;
5609
+ /**
5610
+ * The gate the resolved model calls for. Below authEnforcement there is no
5611
+ * model to enforce -- the registered one ignores the auth scope -- so the
5612
+ * policy is evaluated on its own, which is what reads did before the model
5613
+ * existed. Group serving turns on with authGroups, because below it a
5614
+ * `{ group }` grant does not match, so a served roster is one no grant can
5615
+ * use.
5616
+ */
5617
+ private resolveReadGate;
5618
+ /**
5619
+ * What the client answers an authorization preflight from, or undefined when
5620
+ * it answers none.
5621
+ *
5622
+ * Resolved from the same model reads enforce, so a preflight and a read can
5623
+ * never decide against different models. Undefined below authEnforcement, and
5624
+ * undefined on the `withReactor` path, where there are no flags and no
5625
+ * registry to select a model with -- a client with no model refuses the
5626
+ * preflight rather than answering it from the legacy host-side permission
5627
+ * tables. Deliberately not derived from the read gate: `withReadGate`
5628
+ * overrides that, so sniffing the gate's type would report enforcement from a
5629
+ * caller's substitution.
5630
+ */
5631
+ private resolveActionEvaluation;
4409
5632
  build(): Promise<ReactorClient>;
4410
5633
  buildModule(): Promise<InProcessReactorClientModule>;
4411
5634
  }
@@ -4478,6 +5701,28 @@ declare function parseDriveUrl(url: string): ParsedDriveUrl;
4478
5701
  */
4479
5702
  declare function driveIdFromUrl(url: string): string;
4480
5703
  //#endregion
5704
+ //#region src/shared/errors.d.ts
5705
+ /**
5706
+ * An authorization preflight was asked for while the reactor's decision model
5707
+ * is off, so there is no model to answer from.
5708
+ *
5709
+ * Thrown rather than answered from the legacy host-side permission tables. The
5710
+ * two systems do not compose: the tables record which addresses a host lets
5711
+ * near a drive, the policy records what a document's own grants permit, and an
5712
+ * answer stitched from both would report an admission verdict neither system
5713
+ * would reach. A caller that cannot get a prediction disables nothing, which
5714
+ * leaves the submit path -- and its real gate -- as the only authority.
5715
+ *
5716
+ * Detection is by `name`, not `instanceof`: the SharedWorker RPC boundary
5717
+ * rebuilds a thrown error from `{ name, message, stack, cause }` alone
5718
+ * (`reactor-browser/src/rpc/error-info.ts`), so the class identity and any
5719
+ * custom field are lost in transit. This error therefore carries no fields.
5720
+ */
5721
+ declare class AuthEnforcementDisabledError extends Error {
5722
+ constructor();
5723
+ static isError(error: unknown): error is AuthEnforcementDisabledError;
5724
+ }
5725
+ //#endregion
4481
5726
  //#region src/shared/factories.d.ts
4482
5727
  /**
4483
5728
  * Factory method to create a ShutdownStatus that can be updated
@@ -4508,560 +5753,220 @@ declare function parsePagingOptions(paging: PagingOptions | undefined, defaultLi
4508
5753
  declare class DefaultSubscriptionErrorHandler implements ISubscriptionErrorHandler {
4509
5754
  handleError(error: unknown, context: SubscriptionErrorContext): void;
4510
5755
  }
4511
- //#endregion
4512
- //#region src/subs/react-subscription-manager.d.ts
4513
- type DocumentCreatedCallback = (result: PagedResults<string>) => void;
4514
- type DocumentDeletedCallback = (documentIds: string[]) => void;
4515
- type DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;
4516
- type RelationshipChangedCallback = (parentId: string, childId: string, changeType: RelationshipChangeType) => void;
4517
- declare class ReactorSubscriptionManager implements IReactorSubscriptionManager {
4518
- private createdSubscriptions;
4519
- private deletedSubscriptions;
4520
- private updatedSubscriptions;
4521
- private relationshipSubscriptions;
4522
- private subscriptionCounter;
4523
- private errorHandler;
4524
- constructor(errorHandler: ISubscriptionErrorHandler);
4525
- onDocumentCreated(callback: DocumentCreatedCallback, search?: SearchFilter): () => void;
4526
- onDocumentDeleted(callback: DocumentDeletedCallback, search?: SearchFilter): () => void;
4527
- onDocumentStateUpdated(callback: DocumentStateUpdatedCallback, search?: SearchFilter, view?: ViewFilter): () => void;
4528
- onRelationshipChanged(callback: RelationshipChangedCallback, search?: SearchFilter): () => void;
4529
- /**
4530
- * Notify subscribers about created documents
4531
- */
4532
- notifyDocumentsCreated(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4533
- /**
4534
- * Notify subscribers about deleted documents
4535
- */
4536
- notifyDocumentsDeleted(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4537
- /**
4538
- * Notify subscribers about updated documents
4539
- */
4540
- notifyDocumentsUpdated(documents: PHDocument[]): void;
4541
- /**
4542
- * Notify subscribers about relationship changes
4543
- */
4544
- notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
4545
- /**
4546
- * Clear all subscriptions
4547
- */
4548
- clearAll(): void;
4549
- private filterDocumentIds;
4550
- private filterDocuments;
4551
- private matchesRelationshipFilter;
4552
- }
4553
- //#endregion
4554
- //#region src/events/event-bus.d.ts
4555
- declare class EventBus implements IEventBus {
4556
- readonly eventTypeToSubscribers: Map<number, Subscriber[]>;
4557
- subscribe<K>(type: number, subscriber: (type: number, event: K) => void | Promise<void>): Unsubscribe;
4558
- emit(type: number, data: any): Promise<void>;
4559
- }
4560
- //#endregion
4561
- //#region src/queue/queue.d.ts
4562
- /**
4563
- * In-memory implementation of the IQueue interface.
4564
- * Organizes jobs by documentId, scope, and branch to ensure proper ordering.
4565
- * Ensures serial execution per document by tracking executing jobs.
4566
- * Implements dependency management through queue hints.
4567
- */
4568
- declare class InMemoryQueue implements IQueue {
4569
- private eventBus;
4570
- private resolver;
4571
- private queues;
4572
- private jobIdToQueueKey;
4573
- private docIdToJobId;
4574
- private jobIdToDocId;
4575
- private completedJobs;
4576
- private jobIndex;
4577
- private isBlocked;
4578
- private onDrainedCallback?;
4579
- private isPausedFlag;
4580
- constructor(eventBus: IEventBus, resolver: IDocumentModelResolver);
4581
- private toErrorInfo;
4582
- /**
4583
- * Creates a unique key for a document/scope/branch combination
4584
- */
4585
- private createQueueKey;
4586
- /**
4587
- * Gets or creates a queue for the given key
4588
- */
4589
- private getQueue;
4590
- /**
4591
- * Check if a document has any jobs currently executing
4592
- */
4593
- private isDocumentExecuting;
4594
- /**
4595
- * Mark a job as executing for its document
4596
- */
4597
- private markJobExecuting;
4598
- /**
4599
- * Mark a job as no longer executing for its document
4600
- */
4601
- private markJobComplete;
4602
- /**
4603
- * Check if all dependencies for a job have been completed
4604
- */
4605
- private areDependenciesMet;
4606
- /**
4607
- * Returns the head of the sub-queue if its dependencies are met, or null.
4608
- *
4609
- * The dispatcher only ever considers the head — a dep-blocked head holds
4610
- * the rest of its sub-queue. This preserves per-(documentId, scope, branch)
4611
- * FIFO regardless of how dependencies are authored, and makes the queue's
4612
- * documented "serialized per document" invariant hold even when callers
4613
- * omit queueHint dependencies on jobs that share a sub-queue.
4614
- */
4615
- private getNextJobWithMetDependencies;
4616
- private getCreateDocumentType;
4617
- enqueue(job: Job): Promise<void>;
4618
- dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4619
- dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4620
- dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
4621
- size(documentId: string, scope: string, branch: string): Promise<number>;
4622
- totalSize(): Promise<number>;
4623
- remove(jobId: string): Promise<boolean>;
4624
- clear(documentId: string, scope: string, branch: string): Promise<void>;
4625
- clearAll(): Promise<void>;
4626
- hasJobs(): Promise<boolean>;
4627
- completeJob(jobId: string): Promise<void>;
4628
- failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
4629
- deferJob(jobId: string): void;
4630
- retryJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
4631
- /**
4632
- * Check if the queue is drained and call the callback if it is
4633
- */
4634
- private checkDrained;
4635
- /**
4636
- * Returns true if and only if all jobs have been resolved.
4637
- */
4638
- get isDrained(): boolean;
4639
- /**
4640
- * Blocks the queue from accepting new jobs.
4641
- * @param onDrained - Optional callback to call when the queue is drained
4642
- */
4643
- block(onDrained?: () => void): void;
4644
- /**
4645
- * Unblocks the queue from accepting new jobs.
4646
- */
4647
- unblock(): void;
4648
- /**
4649
- * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.
4650
- */
4651
- pause(): void;
4652
- /**
4653
- * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.
4654
- */
4655
- resume(): Promise<void>;
4656
- /**
4657
- * Returns whether job dequeuing is paused.
4658
- */
4659
- get paused(): boolean;
4660
- /**
4661
- * Returns all pending jobs across all queues.
4662
- */
4663
- getPendingJobs(): Job[];
4664
- /**
4665
- * Returns a map of document IDs to sets of executing job IDs.
4666
- */
4667
- getExecutingJobIds(): Map<string, Set<string>>;
4668
- /**
4669
- * Returns a job by ID from the job index.
4670
- */
4671
- getJob(jobId: string): Job | undefined;
4672
- }
4673
- //#endregion
4674
- //#region src/job-tracker/in-memory-job-tracker.d.ts
4675
- /**
4676
- * In-memory implementation of IJobTracker.
4677
- * Maintains job status in a Map for synchronous access.
4678
- * Subscribes to operation events to update job states.
4679
- */
4680
- declare class InMemoryJobTracker implements IJobTracker {
4681
- private eventBus;
4682
- private jobs;
4683
- private unsubscribers;
4684
- constructor(eventBus: IEventBus);
4685
- private subscribeToEvents;
4686
- private handleWriteReady;
4687
- private handleReadReady;
4688
- private handleJobFailed;
4689
- shutdown(): void;
4690
- registerJob(jobInfo: JobInfo): void;
4691
- markRunning(jobId: string): void;
4692
- markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
4693
- getJobStatus(jobId: string): JobInfo | null;
4694
- }
4695
- //#endregion
4696
- //#region src/executor/simple-job-executor-manager.d.ts
4697
- type JobExecutorFactory = () => IJobExecutor;
4698
- /**
4699
- * Manages multiple job executors and coordinates job distribution.
4700
- * Listens for job available events and dispatches jobs to executors.
4701
- */
4702
- declare class SimpleJobExecutorManager implements IJobExecutorManager {
4703
- private executorFactory;
4704
- private eventBus;
4705
- private queue;
4706
- private jobTracker;
4707
- private logger;
4708
- private resolver;
4709
- private executors;
4710
- private isRunning;
4711
- private activeJobs;
4712
- private totalJobsProcessed;
4713
- private unsubscribe?;
4714
- private deferredJobs;
4715
- private resultHandler;
4716
- private jobTimeoutMs;
4717
- constructor(executorFactory: JobExecutorFactory, eventBus: IEventBus, queue: IQueue, jobTracker: IJobTracker, logger: ILogger, resolver: IDocumentModelResolver, jobTimeoutMs?: number);
4718
- start(numExecutors: number): Promise<void>;
4719
- stop(graceful?: boolean): Promise<void>;
4720
- getExecutors(): IJobExecutor[];
4721
- getStatus(): ExecutorManagerStatus;
4722
- private processNextJob;
4723
- private checkForMoreJobs;
4724
- private processExistingJobs;
4725
- private flushDeferredJobs;
4726
- }
4727
- //#endregion
4728
- //#region src/cache/document-meta-cache-types.d.ts
4729
- /**
4730
- * Cached document metadata from the "document" scope.
4731
- *
4732
- * This lightweight structure holds essential document information needed by
4733
- * the job executor without fetching full scope state. It provides an explicit
4734
- * cross-scope contract for accessing document scope metadata.
4735
- */
4736
- type CachedDocumentMeta = {
5756
+ //#endregion
5757
+ //#region src/subs/react-subscription-manager.d.ts
5758
+ type DocumentCreatedCallback = (result: PagedResults<string>) => void;
5759
+ type DocumentDeletedCallback = (documentIds: string[]) => void;
5760
+ type DocumentStateUpdatedCallback = (result: PagedResults<PHDocument>) => void;
5761
+ type RelationshipChangedCallback = (parentId: string, childId: string, changeType: RelationshipChangeType) => void;
5762
+ declare class ReactorSubscriptionManager implements IReactorSubscriptionManager {
5763
+ private createdSubscriptions;
5764
+ private deletedSubscriptions;
5765
+ private updatedSubscriptions;
5766
+ private relationshipSubscriptions;
5767
+ private subscriptionCounter;
5768
+ private errorHandler;
5769
+ constructor(errorHandler: ISubscriptionErrorHandler);
5770
+ onDocumentCreated(callback: DocumentCreatedCallback, search?: SearchFilter): () => void;
5771
+ onDocumentDeleted(callback: DocumentDeletedCallback, search?: SearchFilter): () => void;
5772
+ onDocumentStateUpdated(callback: DocumentStateUpdatedCallback, search?: SearchFilter, view?: ViewFilter): () => void;
5773
+ onRelationshipChanged(callback: RelationshipChangedCallback, search?: SearchFilter): () => void;
4737
5774
  /**
4738
- * The full PHDocumentState from document.state.document.
4739
- * Contains version, hash, isDeleted, deletedAtUtcIso, etc.
5775
+ * Notify subscribers about created documents
4740
5776
  */
4741
- state: PHDocumentState;
5777
+ notifyDocumentsCreated(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4742
5778
  /**
4743
- * The document type (from header), cached for convenience.
5779
+ * Notify subscribers about deleted documents
4744
5780
  */
4745
- documentType: string;
5781
+ notifyDocumentsDeleted(documentIds: string[], documentTypes?: Map<string, string>, parentIds?: Map<string, string | null>): void;
4746
5782
  /**
4747
- * The revision of the document scope when this metadata was captured.
4748
- * Used for cache invalidation and consistency checks.
5783
+ * Notify subscribers about updated documents
4749
5784
  */
4750
- documentScopeRevision: number;
4751
- };
4752
- /**
4753
- * Interface for the document metadata cache.
4754
- *
4755
- * This cache provides an explicit cross-scope contract for accessing document
4756
- * scope metadata. It solves the problem where job execution in one scope (e.g.,
4757
- * "global") needs access to document scope state (version, isDeleted, etc.)
4758
- * which may be stale in scope-specific caches or keyframes.
4759
- *
4760
- * The cache supports:
4761
- * - Latest metadata retrieval with LRU caching
4762
- * - Historical metadata reconstruction for reshuffling scenarios
4763
- * - Eager updates after document scope operations
4764
- */
4765
- interface IDocumentMetaCache {
5785
+ notifyDocumentsUpdated(documents: PHDocument[]): void;
4766
5786
  /**
4767
- * Retrieves the LATEST document metadata from cache or rebuilds from operations.
4768
- *
4769
- * On cache miss, fetches all document scope operations and reconstructs the
4770
- * current PHDocumentState by applying UPGRADE_DOCUMENT and DELETE_DOCUMENT
4771
- * operations.
4772
- *
4773
- * @param documentId - The document identifier
4774
- * @param branch - Branch name
4775
- * @param signal - Optional abort signal to cancel the operation
4776
- * @returns The cached or rebuilt document metadata
4777
- * @throws {Error} "Operation aborted" if signal is aborted
4778
- * @throws {Error} If document not found (no CREATE_DOCUMENT operation)
5787
+ * Notify subscribers about relationship changes
4779
5788
  */
4780
- getDocumentMeta(documentId: string, branch: string, signal?: AbortSignal): Promise<CachedDocumentMeta>;
5789
+ notifyRelationshipChanged(parentId: string, childId: string, changeType: RelationshipChangeType, childType?: string): void;
4781
5790
  /**
4782
- * Rebuilds document metadata at a SPECIFIC revision (always rebuilds, no caching).
4783
- *
4784
- * Used during reshuffling when operations need to be inserted at a previous
4785
- * revision and we need the document scope state as of that point in time.
4786
- *
4787
- * @param documentId - The document identifier
4788
- * @param branch - Branch name
4789
- * @param targetRevision - The document scope revision to reconstruct up to
4790
- * @param signal - Optional abort signal to cancel the operation
4791
- * @returns Document metadata as of the target revision
4792
- * @throws {Error} "Operation aborted" if signal is aborted
4793
- * @throws {Error} If document not found
5791
+ * Clear all subscriptions
4794
5792
  */
4795
- rebuildAtRevision(documentId: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<CachedDocumentMeta>;
5793
+ clearAll(): void;
5794
+ private filterDocumentIds;
5795
+ private filterDocuments;
5796
+ private matchesRelationshipFilter;
5797
+ }
5798
+ //#endregion
5799
+ //#region src/events/event-bus.d.ts
5800
+ declare class EventBus implements IEventBus {
5801
+ readonly eventTypeToSubscribers: Map<number, Subscriber[]>;
5802
+ subscribe<K>(type: number, subscriber: (type: number, event: K) => void | Promise<void>): Unsubscribe;
5803
+ emit(type: number, data: any): Promise<void>;
5804
+ }
5805
+ //#endregion
5806
+ //#region src/queue/queue.d.ts
5807
+ /**
5808
+ * In-memory implementation of the IQueue interface.
5809
+ * Organizes jobs by documentId, scope, and branch to ensure proper ordering.
5810
+ * Ensures serial execution per document by tracking executing jobs.
5811
+ * Implements dependency management through queue hints.
5812
+ */
5813
+ declare class InMemoryQueue implements IQueue {
5814
+ private eventBus;
5815
+ private resolver;
5816
+ private queues;
5817
+ private jobIdToQueueKey;
5818
+ private docIdToJobId;
5819
+ private jobIdToDocId;
5820
+ private completedJobs;
5821
+ private jobIndex;
5822
+ private isBlocked;
5823
+ private onDrainedCallback?;
5824
+ private isPausedFlag;
5825
+ constructor(eventBus: IEventBus, resolver: IDocumentModelResolver);
5826
+ private toErrorInfo;
4796
5827
  /**
4797
- * Eagerly updates cached metadata after document scope operations.
4798
- *
4799
- * Called by the job executor after CREATE_DOCUMENT, UPGRADE_DOCUMENT, or
4800
- * DELETE_DOCUMENT operations to keep the cache current.
4801
- *
4802
- * @param documentId - The document identifier
4803
- * @param branch - Branch name
4804
- * @param meta - The new metadata to cache
5828
+ * Creates a unique key for a document/scope/branch combination
4805
5829
  */
4806
- putDocumentMeta(documentId: string, branch: string, meta: CachedDocumentMeta): void;
5830
+ private createQueueKey;
4807
5831
  /**
4808
- * Invalidates cached document metadata.
4809
- *
4810
- * Call before reshuffling operations that modify the document scope, or
4811
- * when document state may have changed externally.
4812
- *
4813
- * @param documentId - The document identifier
4814
- * @param branch - Optional branch to narrow invalidation (if omitted, all branches)
4815
- * @returns Number of entries invalidated
5832
+ * Gets or creates a queue for the given key
4816
5833
  */
4817
- invalidate(documentId: string, branch?: string): number;
5834
+ private getQueue;
4818
5835
  /**
4819
- * Clears all cached document metadata.
5836
+ * Check if a document has any jobs currently executing
4820
5837
  */
4821
- clear(): void;
5838
+ private isDocumentExecuting;
4822
5839
  /**
4823
- * Performs startup initialization.
5840
+ * Mark a job as executing for its document
4824
5841
  */
4825
- startup(): Promise<void>;
5842
+ private markJobExecuting;
4826
5843
  /**
4827
- * Performs graceful shutdown.
5844
+ * Mark a job as no longer executing for its document
4828
5845
  */
4829
- shutdown(): Promise<void>;
4830
- }
4831
- //#endregion
4832
- //#region src/cache/buffer/ring-buffer.d.ts
4833
- /**
4834
- * RingBuffer is a generic circular buffer implementation that stores a fixed number
4835
- * of items. When the buffer is full, new items overwrite the oldest items.
4836
- *
4837
- * This implementation maintains O(1) time complexity for push operations and provides
4838
- * items in chronological order (oldest to newest) via getAll().
4839
- *
4840
- * @template T - The type of items stored in the buffer
4841
- */
4842
- declare class RingBuffer<T> {
4843
- private buffer;
4844
- private head;
4845
- private size;
4846
- private capacity;
4847
- constructor(capacity: number);
5846
+ private markJobComplete;
4848
5847
  /**
4849
- * Adds an item to the buffer. If the buffer is full, overwrites the oldest item.
4850
- *
4851
- * @param item - The item to add
5848
+ * Check if all dependencies for a job have been completed
4852
5849
  */
4853
- push(item: T): void;
5850
+ private areDependenciesMet;
4854
5851
  /**
4855
- * Returns all items in the buffer in chronological order (oldest to newest).
5852
+ * Returns the head of the sub-queue if its dependencies are met, or null.
4856
5853
  *
4857
- * @returns Array of items in insertion order
5854
+ * The dispatcher only ever considers the head — a dep-blocked head holds
5855
+ * the rest of its sub-queue. This preserves per-(documentId, scope, branch)
5856
+ * FIFO regardless of how dependencies are authored, and makes the queue's
5857
+ * documented "serialized per document" invariant hold even when callers
5858
+ * omit queueHint dependencies on jobs that share a sub-queue.
4858
5859
  */
4859
- getAll(): T[];
5860
+ private getNextJobWithMetDependencies;
5861
+ private getCreateDocumentType;
5862
+ enqueue(job: Job): Promise<void>;
5863
+ dequeue(documentId: string, scope: string, branch: string, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5864
+ dequeueNext(signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5865
+ dequeueNextMatching(predicate: (meta: JobRoutingMeta) => boolean, signal?: AbortSignal): Promise<IJobExecutionHandle | null>;
5866
+ size(documentId: string, scope: string, branch: string): Promise<number>;
5867
+ totalSize(): Promise<number>;
5868
+ remove(jobId: string): Promise<boolean>;
5869
+ clear(documentId: string, scope: string, branch: string): Promise<void>;
5870
+ clearAll(): Promise<void>;
5871
+ hasJobs(): Promise<boolean>;
5872
+ completeJob(jobId: string): Promise<void>;
5873
+ failJob(jobId: string, error?: ErrorInfo$1): Promise<void>;
5874
+ deferJob(jobId: string): void;
5875
+ retryJob(jobId: string, error?: ErrorInfo$1, accounting?: RetryAccounting): Promise<void>;
4860
5876
  /**
4861
- * Clears all items from the buffer.
5877
+ * Check if the queue is drained and call the callback if it is
4862
5878
  */
4863
- clear(): void;
5879
+ private checkDrained;
4864
5880
  /**
4865
- * Gets the current number of items in the buffer.
5881
+ * Returns true if and only if all jobs have been resolved.
4866
5882
  */
4867
- get length(): number;
4868
- }
4869
- //#endregion
4870
- //#region src/cache/kysely-write-cache.d.ts
4871
- type DocumentStream = {
4872
- key: string;
4873
- ringBuffer: RingBuffer<CachedSnapshot>;
4874
- };
4875
- /**
4876
- * In-memory write cache with keyframe persistence for PHDocuments.
4877
- *
4878
- * Caches document snapshots in ring buffers with LRU eviction. On cache miss,
4879
- * rebuilds documents from nearest keyframe or full operation history.
4880
- *
4881
- * **Performance Characteristics:**
4882
- * - Cache hit: O(1) lookup in ring buffer
4883
- * - Cold miss: O(n) where n is total operation count, or O(k) where k is operations since keyframe
4884
- * - Warm miss: O(m) where m is operations since cached revision
4885
- * - Eviction: O(1) for LRU tracking and removal
4886
- *
4887
- * **Thread Safety:**
4888
- * Not thread-safe. Designed for single-threaded job executor environment.
4889
- * External synchronization required for concurrent access across multiple executors.
4890
- *
4891
- * **Example:**
4892
- * ```typescript
4893
- * const cache = new KyselyWriteCache(
4894
- * keyframeStore,
4895
- * operationStore,
4896
- * registry,
4897
- * { maxDocuments: 1000, ringBufferSize: 10, keyframeInterval: 10 }
4898
- * );
4899
- *
4900
- * await cache.startup();
4901
- *
4902
- * // Retrieve or rebuild document
4903
- * const doc = await cache.getState(docId, docType, scope, branch, revision);
4904
- *
4905
- * // Cache result after job execution
4906
- * cache.putState(docId, docType, scope, branch, newRevision, updatedDoc);
4907
- *
4908
- * await cache.shutdown();
4909
- * ```
4910
- */
4911
- declare class KyselyWriteCache implements IWriteCache {
4912
- private streams;
4913
- private lruTracker;
4914
- private keyframeStore;
4915
- private operationStore;
4916
- private registry;
4917
- private config;
4918
- constructor(keyframeStore: IKeyframeStore, operationStore: IOperationStore, registry: IDocumentModelRegistry, config: WriteCacheConfig);
4919
- withScopedStores(operationStore: IOperationStore, keyframeStore: IKeyframeStore): KyselyWriteCache;
5883
+ get isDrained(): boolean;
4920
5884
  /**
4921
- * Initializes the write cache.
4922
- * Currently a no-op as keyframe store lifecycle is managed externally.
5885
+ * Blocks the queue from accepting new jobs.
5886
+ * @param onDrained - Optional callback to call when the queue is drained
4923
5887
  */
4924
- startup(): Promise<void>;
5888
+ block(onDrained?: () => void): void;
4925
5889
  /**
4926
- * Shuts down the write cache.
4927
- * Currently a no-op as keyframe store lifecycle is managed externally.
5890
+ * Unblocks the queue from accepting new jobs.
4928
5891
  */
4929
- shutdown(): Promise<void>;
5892
+ unblock(): void;
4930
5893
  /**
4931
- * Retrieves document state at a specific revision from cache or rebuilds it.
4932
- *
4933
- * Cache hit path: Returns cached snapshot if available (O(1))
4934
- * Warm miss path: Rebuilds from cached base revision + incremental ops
4935
- * Cold miss path: Rebuilds from keyframe or from scratch using all operations
4936
- *
4937
- * @param documentId - The document identifier
4938
- * @param scope - The operation scope
4939
- * @param branch - The operation branch
4940
- * @param targetRevision - The target revision, or undefined for newest
4941
- * @param signal - Optional abort signal to cancel the operation
4942
- * @returns The document at the target revision
4943
- * @throws {Error} "Operation aborted" if signal is aborted
4944
- * @throws {ModuleNotFoundError} If document type not registered in registry
4945
- * @throws {Error} "Failed to rebuild document" if operation store fails
4946
- * @throws {Error} If reducer throws during operation application
4947
- * @throws {Error} If document serialization fails
5894
+ * Pauses job dequeuing. Jobs can still be enqueued but dequeueNext() will return null.
4948
5895
  */
4949
- getState(documentId: string, scope: string, branch: string, targetRevision?: number, signal?: AbortSignal): Promise<PHDocument>;
5896
+ pause(): void;
4950
5897
  /**
4951
- * Stores a document snapshot in the cache at a specific revision.
4952
- *
4953
- * The cached document is a shallow copy of the input with its operation history
4954
- * truncated to the last operation per scope and its clipboard cleared. This keeps
4955
- * memory use and copy costs constant regardless of operation count. Consumers of
4956
- * getState() must not rely on the full operation history being present; the only
4957
- * guaranteed invariant is that operations[scope].at(-1) reflects the latest
4958
- * operation index for each scope.
4959
- *
4960
- * Updates LRU tracker and may evict least recently used stream if at capacity.
4961
- * Asynchronously persists keyframes at configured intervals (fire-and-forget).
4962
- *
4963
- * @param documentId - The document identifier
4964
- * @param scope - The operation scope
4965
- * @param branch - The operation branch
4966
- * @param revision - The revision number
4967
- * @param document - The document to cache
4968
- * @throws {Error} If document serialization fails
5898
+ * Resumes job dequeuing and emits JOB_AVAILABLE events for pending jobs to wake up executors.
4969
5899
  */
4970
- putState(documentId: string, scope: string, branch: string, revision: number, document: PHDocument): void;
5900
+ resume(): Promise<void>;
4971
5901
  /**
4972
- * Invalidates cached document streams.
4973
- *
4974
- * Supports three invalidation scopes:
4975
- * - Document-level: invalidate(documentId) - removes all streams for document
4976
- * - Scope-level: invalidate(documentId, scope) - removes all branches for scope
4977
- * - Stream-level: invalidate(documentId, scope, branch) - removes specific stream
4978
- *
4979
- * @param documentId - The document identifier
4980
- * @param scope - Optional scope to narrow invalidation
4981
- * @param branch - Optional branch to narrow invalidation (requires scope)
4982
- * @returns The number of streams evicted
5902
+ * Returns whether job dequeuing is paused.
4983
5903
  */
4984
- invalidate(documentId: string, scope?: string, branch?: string): number;
5904
+ get paused(): boolean;
4985
5905
  /**
4986
- * Clears the entire cache, removing all cached document streams.
4987
- * Resets LRU tracking state. This operation always succeeds.
5906
+ * Returns all pending jobs across all queues.
4988
5907
  */
4989
- clear(): void;
5908
+ getPendingJobs(): Job[];
4990
5909
  /**
4991
- * Retrieves a specific stream for a document. Exposed on the implementation
4992
- * for testing, but not on the interface.
4993
- *
4994
- * @internal
5910
+ * Returns a map of document IDs to sets of executing job IDs.
4995
5911
  */
4996
- getStream(documentId: string, scope: string, branch: string): DocumentStream | undefined;
4997
- private findNearestKeyframe;
4998
- private coldMissRebuild;
5912
+ getExecutingJobIds(): Map<string, Set<string>>;
4999
5913
  /**
5000
- * Resolves which module version to use for a given operation in phase 2.
5001
- *
5002
- * Uses the validated-upgrade boundary rules from D7:
5003
- * - If `input.revision` is present: op.index < revision[scope] → before the upgrade boundary
5004
- * - Otherwise: timestamp fallback
5005
- * - Falls back to final module version when neither is decidable
5914
+ * Returns a job by ID from the job index.
5006
5915
  */
5007
- private resolveModuleVersionForOp;
5008
- private warmMissRebuild;
5009
- private findNearestOlderSnapshot;
5010
- private makeStreamKey;
5011
- private getOrCreateStream;
5012
- private isKeyframeRevision;
5013
- }
5014
- //#endregion
5015
- //#region src/storage/kysely/store.d.ts
5016
- declare class KyselyOperationStore implements IOperationStore {
5017
- private db;
5018
- private trx?;
5019
- constructor(db: Kysely<Database$1>);
5020
- private get queryExecutor();
5021
- withTransaction(trx: Transaction<Database$1>): KyselyOperationStore;
5022
- apply(documentId: string, documentType: string, scope: string, branch: string, revision: number, fn: (txn: AtomicTxn) => void | Promise<void>, signal?: AbortSignal): Promise<Operation[]>;
5023
- private resolveUniqueConstraint;
5024
- private executeApply;
5025
- private findIdempotentReplay;
5026
- getSince(documentId: string, scope: string, branch: string, revision: number, filter?: OperationFilter, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
5027
- getSinceId(id: number, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<OperationWithContext$1>>;
5028
- getConflicting(documentId: string, scope: string, branch: string, minTimestamp: string, paging?: PagingOptions, signal?: AbortSignal): Promise<PagedResults<Operation>>;
5029
- getRevisions(documentId: string, branch: string, signal?: AbortSignal): Promise<DocumentRevisions>;
5030
- private rowToOperation;
5031
- private rowToOperationWithContext;
5916
+ getJob(jobId: string): Job | undefined;
5032
5917
  }
5033
5918
  //#endregion
5034
- //#region src/storage/kysely/keyframe-store.d.ts
5035
- declare class KyselyKeyframeStore implements IKeyframeStore {
5036
- private db;
5037
- private trx?;
5038
- constructor(db: Kysely<Database$1>);
5039
- private get queryExecutor();
5040
- withTransaction(trx: Transaction<Database$1>): KyselyKeyframeStore;
5041
- putKeyframe(documentId: string, scope: string, branch: string, revision: number, document: PHDocument, signal?: AbortSignal): Promise<void>;
5042
- findNearestKeyframe(documentId: string, scope: string, branch: string, targetRevision: number, signal?: AbortSignal): Promise<{
5043
- revision: number;
5044
- document: PHDocument;
5045
- } | undefined>;
5046
- listKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<Array<{
5047
- scope: string;
5048
- branch: string;
5049
- revision: number;
5050
- document: PHDocument;
5051
- }>>;
5052
- deleteKeyframes(documentId: string, scope?: string, branch?: string, signal?: AbortSignal): Promise<number>;
5919
+ //#region src/job-tracker/in-memory-job-tracker.d.ts
5920
+ /**
5921
+ * In-memory implementation of IJobTracker.
5922
+ * Maintains job status in a Map for synchronous access.
5923
+ * Subscribes to operation events to update job states.
5924
+ */
5925
+ declare class InMemoryJobTracker implements IJobTracker {
5926
+ private eventBus;
5927
+ private jobs;
5928
+ private unsubscribers;
5929
+ constructor(eventBus: IEventBus);
5930
+ private subscribeToEvents;
5931
+ private handleWriteReady;
5932
+ private handleReadReady;
5933
+ private handleJobFailed;
5934
+ shutdown(): void;
5935
+ registerJob(jobInfo: JobInfo): void;
5936
+ markRunning(jobId: string): void;
5937
+ markFailed(jobId: string, error: ErrorInfo$1, job?: Job): void;
5938
+ getJobStatus(jobId: string): JobInfo | null;
5053
5939
  }
5054
5940
  //#endregion
5055
- //#region src/executor/execution-scope.d.ts
5056
- interface ExecutionStores {
5057
- operationStore: IOperationStore;
5058
- operationIndex: IOperationIndex;
5059
- writeCache: IWriteCache;
5060
- documentMetaCache: IDocumentMetaCache;
5061
- collectionMembershipCache: ICollectionMembershipCache;
5062
- }
5063
- interface IExecutionScope {
5064
- run<T>(fn: (stores: ExecutionStores) => Promise<T>, signal?: AbortSignal): Promise<T>;
5941
+ //#region src/executor/simple-job-executor-manager.d.ts
5942
+ type JobExecutorFactory = () => IJobExecutor;
5943
+ /**
5944
+ * Manages multiple job executors and coordinates job distribution.
5945
+ * Listens for job available events and dispatches jobs to executors.
5946
+ */
5947
+ declare class SimpleJobExecutorManager implements IJobExecutorManager {
5948
+ private executorFactory;
5949
+ private eventBus;
5950
+ private queue;
5951
+ private jobTracker;
5952
+ private logger;
5953
+ private resolver;
5954
+ private executors;
5955
+ private isRunning;
5956
+ private activeJobs;
5957
+ private totalJobsProcessed;
5958
+ private unsubscribe?;
5959
+ private deferredJobs;
5960
+ private resultHandler;
5961
+ private jobTimeoutMs;
5962
+ constructor(executorFactory: JobExecutorFactory, eventBus: IEventBus, queue: IQueue, jobTracker: IJobTracker, logger: ILogger, resolver: IDocumentModelResolver, jobTimeoutMs?: number, deferredJobTtlMs?: number);
5963
+ start(numExecutors: number): Promise<void>;
5964
+ stop(graceful?: boolean): Promise<void>;
5965
+ getExecutors(): IJobExecutor[];
5966
+ getStatus(): ExecutorManagerStatus;
5967
+ private processNextJob;
5968
+ private checkForMoreJobs;
5969
+ private processExistingJobs;
5065
5970
  }
5066
5971
  //#endregion
5067
5972
  //#region src/executor/simple-job-executor.d.ts
@@ -5079,6 +5984,8 @@ declare class SimpleJobExecutor implements IJobExecutor {
5079
5984
  private collectionMembershipCache;
5080
5985
  private driveContainerTypes;
5081
5986
  private config;
5987
+ private featureFlags;
5988
+ private decisionModel;
5082
5989
  private signatureVerifierModule;
5083
5990
  private documentActionHandler;
5084
5991
  private executionScope;
@@ -5091,6 +5998,69 @@ declare class SimpleJobExecutor implements IJobExecutor {
5091
5998
  private getCollectionMembershipsForOperations;
5092
5999
  private processActions;
5093
6000
  private executeRegularAction;
6001
+ /**
6002
+ * Orders a write by timestamp and decides it where it lands. The caller
6003
+ * supplies the timestamp, so a write can belong before operations already
6004
+ * stored; those are re-appended alongside it, the way a load reshuffles.
6005
+ *
6006
+ * Deciding a backdated write at the stream heads instead of at its position
6007
+ * would overwrite the verdict every other replica computes for it.
6008
+ */
6009
+ private positionByTimestamp;
6010
+ /**
6011
+ * Decides each operation where it lands and carries the verdict on it. A
6012
+ * refused submitted action is reported to the caller and nothing is stored; a
6013
+ * refused operation the reshuffle merely moved keeps its verdict, because it
6014
+ * already holds a position.
6015
+ *
6016
+ * The operations carry the indexes and skips they will be stored at, because
6017
+ * the walk resolves skips before it orders them.
6018
+ */
6019
+ private evaluatePositioned;
6020
+ /**
6021
+ * The scopes a re-evaluation pass visits, in a fixed order.
6022
+ *
6023
+ * The revisions map comes from a query with no ORDER BY, and the order is
6024
+ * load-bearing: each scope's pass re-reads the auth stream, and the walk skips
6025
+ * an operation by its stored denial, so a denial this pass just wrote is
6026
+ * visible to a later-visited scope and invisible to an earlier one. The model's
6027
+ * own projection order leads, then the rest sorted, so the pass is reproducible
6028
+ * across replicas and across runs.
6029
+ */
6030
+ private evaluationOrder;
6031
+ /**
6032
+ * The first timestamp in the batch that does not strictly exceed everything
6033
+ * ahead of it, or undefined when the whole batch is monotonic.
6034
+ *
6035
+ * The bound is carried forward rather than compared against one stored maximum,
6036
+ * because a single execute can carry several auth actions stamped in the same
6037
+ * millisecond. Letting a tie through would store a stream the position walk
6038
+ * then refuses to read, with no repair path.
6039
+ */
6040
+ private firstNonMonotonicTimestamp;
6041
+ /** The operations a batch of submitted actions appends at the scope's tail. */
6042
+ private appendedOperations;
6043
+ /**
6044
+ * Re-evaluates the document when a write meets both criteria: it was written
6045
+ * to a stream the model reads, and it is timestamped before an operation
6046
+ * already stored. The caller supplies the timestamp and the reactor does not replace
6047
+ * it, so a mutation job can write such an operation just as a load job can,
6048
+ * which is why both executeJob and executeLoadJob call this.
6049
+ */
6050
+ private reevaluateIfCriteriaMet;
6051
+ /**
6052
+ * Re-evaluates every scope the model evaluates. Where an operation's
6053
+ * evaluation differs from what is stored, the tail from that operation is
6054
+ * re-appended, carrying a skip that spans the indices it supersedes.
6055
+ */
6056
+ private reevaluateDocument;
6057
+ /**
6058
+ * Re-judges a document's stored operations because a read-set stream in
6059
+ * another document (a group) gained an operation. The trigger timestamp
6060
+ * bounds the work: an operation later than everything this document holds
6061
+ * cannot change any evaluation, so the pass is skipped.
6062
+ */
6063
+ private executeReevaluationJob;
5094
6064
  private executeLoadJob;
5095
6065
  private accumulateResultOrReturnError;
5096
6066
  }
@@ -5159,6 +6129,78 @@ declare class DocumentModelRegistry implements IDocumentModelRegistry {
5159
6129
  getUpgradeReducer(documentType: string, fromVersion: number, toVersion: number): UpgradeReducer<any, any>;
5160
6130
  }
5161
6131
  //#endregion
6132
+ //#region src/decision/build-decision-model.d.ts
6133
+ /**
6134
+ * Reads each projection's stream through the supplied reader, recording the
6135
+ * revision observed. Static projections resolve first; derived projections
6136
+ * see only those and contribute a map from document id to state. Each
6137
+ * distinct stream is read once and yields one append condition entry.
6138
+ */
6139
+ declare function buildDecisionModel<M>(reader: IStreamStateReader, definition: (target: DecisionTarget) => DecisionModel<M>, target: DecisionTarget, signal?: AbortSignal): Promise<BuiltDecisionModel<M>>;
6140
+ //#endregion
6141
+ //#region src/decision/auth-decision-model.d.ts
6142
+ type AuthDecisionModel = {
6143
+ document: PHDocumentState;
6144
+ auth: PHAuthState;
6145
+ };
6146
+ /** This decision model uses both the document and the auth streams. */
6147
+ declare function authDecisionModel(target: DecisionTarget): DecisionModel<AuthDecisionModel>;
6148
+ //#endregion
6149
+ //#region src/decision/sync-scope-gate.d.ts
6150
+ /**
6151
+ * A read gate asked by document id rather than by document.
6152
+ *
6153
+ * Serving works from an id: an outbox entry names a document, a branch and a
6154
+ * scope, and carries no state. Fetching the document is therefore the serving
6155
+ * path's own job, and it is the only thing this adds to the gate it wraps.
6156
+ */
6157
+ declare class SyncScopeGate {
6158
+ private readonly gate;
6159
+ private readonly documentView;
6160
+ private readonly logger?;
6161
+ constructor(gate: IReadGate, documentView: IDocumentView, logger?: ILogger | undefined);
6162
+ /**
6163
+ * Which scopes of one document the subject may be served.
6164
+ *
6165
+ * A document this replica cannot produce yields the metadata scopes and
6166
+ * nothing else. That is the fail-closed direction, and it is safe to fail
6167
+ * closed here precisely because serving withholds rather than consumes: the
6168
+ * entry stays in the outbox and the next poll asks again, so a document that
6169
+ * is merely not indexed yet is delayed rather than lost.
6170
+ *
6171
+ * Any other failure is rethrown. A read side that is down must not read as a
6172
+ * silent, universal denial, because a denial that looks like a policy is one
6173
+ * nobody investigates.
6174
+ */
6175
+ scopePredicateById(documentId: string, subject: AuthSubject, branch: string, signal?: AbortSignal): Promise<(scope: string) => boolean>;
6176
+ }
6177
+ //#endregion
6178
+ //#region src/decision/stream-order.d.ts
6179
+ /** Where a stream's stored order contradicts its timestamps. */
6180
+ type OutOfOrderPair = {
6181
+ previous: Operation;
6182
+ current: Operation;
6183
+ /**
6184
+ * `descending` cannot be walked at all. `tied` walks fine — the intra-stream
6185
+ * rule breaks the tie by index — but violates the monotonic auth rule, so a
6186
+ * stream holding one can never be replicated to a peer that lacks it.
6187
+ */
6188
+ kind: "descending" | "tied";
6189
+ };
6190
+ /**
6191
+ * The first pair of effective operations whose stored order contradicts their
6192
+ * timestamps, or undefined when the stream is in position order.
6193
+ *
6194
+ * Such a stream cannot be walked, and the auth stream is never reshuffled once
6195
+ * the monotonic rule is on, so run this before enabling enforcement on a fleet.
6196
+ *
6197
+ * `requireStrict` additionally rejects a tie, which is what the auth stream's
6198
+ * monotonic rule requires and what the walk alone does not care about.
6199
+ */
6200
+ declare function firstOutOfOrderPair(operations: Operation[], options?: {
6201
+ requireStrict?: boolean;
6202
+ }): OutOfOrderPair | undefined;
6203
+ //#endregion
5162
6204
  //#region src/read-models/base-read-model.d.ts
5163
6205
  type BaseReadModelConfig = {
5164
6206
  readModelId: string;
@@ -5243,7 +6285,7 @@ declare class KyselyDocumentIndexer extends BaseReadModel implements IDocumentIn
5243
6285
  * serialized so the executor can return to dispatch without holding ordering
5244
6286
  * implicitly.
5245
6287
  */
5246
- declare class ReadModelCoordinator implements IReadModelCoordinator {
6288
+ declare class ReadModelCoordinator implements ILiveReadModelCoordinator {
5247
6289
  private eventBus;
5248
6290
  readonly preReady: IReadModel[];
5249
6291
  readonly postReady: IReadModel[];
@@ -5262,6 +6304,7 @@ declare class ReadModelCoordinator implements IReadModelCoordinator {
5262
6304
  */
5263
6305
  drain(): Promise<void>;
5264
6306
  getChainDepth(): number;
6307
+ addReadModel(readModel: IReadModel, stage: ReadModelRegistrationStage): void;
5265
6308
  private handleWriteReady;
5266
6309
  private emitEmptyReadReady;
5267
6310
  private runChain;
@@ -5275,8 +6318,31 @@ declare class ReadModelCoordinator implements IReadModelCoordinator {
5275
6318
  type Database$2 = Database$1 & DocumentViewDatabase;
5276
6319
  declare class KyselyDocumentView extends BaseReadModel implements IDocumentView {
5277
6320
  private operationStore;
6321
+ /**
6322
+ * Whether a single-document read serves a deleted document's state as of the
6323
+ * deletion rather than hiding it. Only meaningful with `documentDecisions`,
6324
+ * which is what makes deletion positional. Listings omit it either way.
6325
+ */
6326
+ private readonly servesDeletionBoundary;
5278
6327
  private _db;
5279
- constructor(db: Kysely<Database$2>, operationStore: IOperationStore, operationIndex: IOperationIndex, writeCache: IWriteCache, consistencyTracker: IConsistencyTracker);
6328
+ constructor(db: Kysely<Database$2>, operationStore: IOperationStore, operationIndex: IOperationIndex, writeCache: IWriteCache, consistencyTracker: IConsistencyTracker,
6329
+ /**
6330
+ * Whether a single-document read serves a deleted document's state as of the
6331
+ * deletion rather than hiding it. Only meaningful with `documentDecisions`,
6332
+ * which is what makes deletion positional. Listings omit it either way.
6333
+ */
6334
+
6335
+ servesDeletionBoundary: boolean);
6336
+ /**
6337
+ * Indexes committed operations into DocumentSnapshot rows. CREATE_DOCUMENT
6338
+ * only seeds header/document/auth. UPGRADE_DOCUMENT reindexes every scope
6339
+ * present in resultingState when the operation vouches for them — a seed
6340
+ * carrying initialState or a migration stamped with the __migrated marker
6341
+ * — since the upgrade reducer may have reshaped any of them; upgrades
6342
+ * without either fall back to header/document/auth, because their sibling
6343
+ * echoes may be stale. All other action types index only header and their
6344
+ * own scope.
6345
+ */
5280
6346
  protected commitOperations(items: OperationWithContext$1[]): Promise<void>;
5281
6347
  exists(documentIds: string[], consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<boolean[]>;
5282
6348
  get<TDocument extends PHDocument>(documentId: string, view?: ViewFilter$1, consistencyToken?: ConsistencyToken, signal?: AbortSignal): Promise<TDocument>;
@@ -5290,7 +6356,13 @@ declare class KyselyDocumentView extends BaseReadModel implements IDocumentView
5290
6356
  //#endregion
5291
6357
  //#region src/storage/migrations/migrator.d.ts
5292
6358
  declare const REACTOR_SCHEMA = "reactor";
5293
- declare function runMigrations(db: Kysely<any>, schema?: string): Promise<MigrationResult>;
6359
+ /**
6360
+ * Applies every pending migration, or every one up to and including `upTo`.
6361
+ *
6362
+ * The bound exists so a test can reach the schema a data migration is written
6363
+ * against, populate it, and then migrate across the migration under test.
6364
+ */
6365
+ declare function runMigrations(db: Kysely<any>, schema?: string, upTo?: string): Promise<MigrationResult>;
5294
6366
  declare function getMigrationStatus(db: Kysely<any>, schema?: string): Promise<readonly kysely.MigrationInfo[]>;
5295
6367
  //#endregion
5296
6368
  //#region src/storage/kysely/sync-cursor-storage.d.ts
@@ -5408,6 +6480,8 @@ declare class GqlRequestChannel implements IChannel {
5408
6480
  private isPushing;
5409
6481
  private pendingDrain;
5410
6482
  private receivingPages;
6483
+ /** Cleared for good the first time the remote rejects {@link DECISION_FIELDS}. */
6484
+ private peerServesDecisionFields;
5411
6485
  private isRecovering;
5412
6486
  private connectionState;
5413
6487
  /** Latest unrecoverable error was an auth rejection; cleared on connect. */
@@ -5454,6 +6528,18 @@ declare class GqlRequestChannel implements IChannel {
5454
6528
  * Queries the remote GraphQL endpoint for sync envelopes.
5455
6529
  */
5456
6530
  private pollSyncEnvelopes;
6531
+ /**
6532
+ * True when the remote rejected the query for naming a field it does not
6533
+ * have. Selecting an unknown field fails validation for the whole query, so
6534
+ * an unhandled one takes the channel's polling down until the process
6535
+ * restarts rather than degrading.
6536
+ */
6537
+ private rejectsDecisionFields;
6538
+ /**
6539
+ * The poll query. `withDecisionFields` selects the two fields added with the
6540
+ * auth projection; a remote on the previous schema is polled without them.
6541
+ */
6542
+ private pollQuery;
5457
6543
  /**
5458
6544
  * Registers or updates this channel on the remote server via GraphQL mutation.
5459
6545
  * Returns the remote's ack ordinal so the client can trim its outbox.
@@ -5514,6 +6600,8 @@ declare class GqlResponseChannel implements IChannel {
5514
6600
  private isShutdown;
5515
6601
  private lastPersistedInboxOrdinal;
5516
6602
  private lastPersistedOutboxOrdinal;
6603
+ private evictedOutboxFloor;
6604
+ private appliedOutboxOrdinal;
5517
6605
  private connectionState;
5518
6606
  private readonly connectionStateCallbacks;
5519
6607
  constructor(logger: ILogger, channelId: string, remoteName: string, cursorStorage: ISyncCursorStorage);
@@ -5522,8 +6610,35 @@ declare class GqlResponseChannel implements IChannel {
5522
6610
  onConnectionStateChange(callback: ConnectionStateChangeCallback): () => void;
5523
6611
  /** Response channels are push-driven; resolvers populate mailboxes directly. */
5524
6612
  triggerPull(): void;
5525
- private transitionConnectionState;
5526
6613
  init(): Promise<void>;
6614
+ private transitionConnectionState;
6615
+ /**
6616
+ * Records the ordinals of entries that left the outbox without being served,
6617
+ * so the cursor cannot advance past them.
6618
+ *
6619
+ * An entry can leave unserved because a bound evicted it, and an evicted entry
6620
+ * is exactly one this channel intends to re-derive: it is still owed to the
6621
+ * remote. Remembering the floor across the whole run rather than only while
6622
+ * the entry is present is what makes that true after a later ack would
6623
+ * otherwise have swept the cursor past it.
6624
+ */
6625
+ private rememberUnserved;
6626
+ /**
6627
+ * Persists the outbox cursor, never past an operation this remote has not
6628
+ * been served.
6629
+ *
6630
+ * The cursor is where a restart resumes deriving the outbox from, so an
6631
+ * ordinal persisted past an unserved entry loses that entry for good: the
6632
+ * rebuild starts beyond it and nothing else remembers it was owed. Acks
6633
+ * arrive out of order with respect to what is withheld -- a later entry can
6634
+ * be acknowledged while an earlier one is still being withheld from this
6635
+ * subject -- so the applied high-water mark alone is not a safe cursor.
6636
+ */
6637
+ private persistOutboxCursor;
6638
+ /** Drops the evicted floor once the entries it stood for are queued again. */
6639
+ private forgetEvictedBelow;
6640
+ /** The lowest ordinal still owed to this remote, evicted or still queued. */
6641
+ private unservedFloor;
5527
6642
  }
5528
6643
  //#endregion
5529
6644
  //#region src/sync/channels/interval-poll-timer.d.ts
@@ -5614,8 +6729,27 @@ declare function batchOperationsByDocument(operations: OperationWithContext$1[])
5614
6729
  * a single SyncOperation per group. Within each group, operations are sorted
5615
6730
  * by context.ordinal. The merged SyncOperation keeps the first group member's
5616
6731
  * jobId; all other jobIds are remapped so external dependencies still resolve.
6732
+ *
6733
+ * Only CONTIGUOUS runs merge. A document whose scopes interleave arrives as an
6734
+ * alternating chain (document -> auth -> document -> auth) where each entry
6735
+ * depends on the one before it. Merging every occurrence of a
6736
+ * (documentId, scope, branch) collapses that chain into two nodes that each
6737
+ * depend on the other, which validateBatchStructure rejects as a dependency
6738
+ * cycle. Merging only adjacent entries keeps the chain linear.
5617
6739
  */
5618
6740
  declare function consolidateSyncOperations(syncOps: SyncOperation[]): SyncOperation[];
6741
+ /**
6742
+ * Classifies a failure by error name rather than `instanceof`, because a failure
6743
+ * that crossed the pooled-worker boundary arrives as plain data.
6744
+ */
6745
+ declare function classifyJobFailure(errorName: string): SyncOperationErrorType;
6746
+ /** The explicit type when something else carried it, else derived by name. */
6747
+ declare function syncOperationErrorType(error: ChannelError | undefined): SyncOperationErrorType;
6748
+ /**
6749
+ * A held auth operation must not quarantine: reconciling the two policies needs
6750
+ * the traffic a quarantine would stop.
6751
+ */
6752
+ declare function quarantinesDocument(errorType: SyncOperationErrorType): boolean;
5619
6753
  //#endregion
5620
6754
  //#region src/admin/types.d.ts
5621
6755
  type KeyframeValidationIssue = {
@@ -5631,11 +6765,20 @@ type SnapshotValidationIssue = {
5631
6765
  snapshotHash: string;
5632
6766
  replayedHash: string;
5633
6767
  };
6768
+ /** Effective operations whose stored order contradicts their timestamps. */
6769
+ type StreamOrderIssue = {
6770
+ scope: string;
6771
+ branch: string;
6772
+ previous: Operation;
6773
+ current: Operation;
6774
+ kind: OutOfOrderPair["kind"];
6775
+ };
5634
6776
  type ValidationResult = {
5635
6777
  documentId: string;
5636
6778
  isConsistent: boolean;
5637
6779
  keyframeIssues: KeyframeValidationIssue[];
5638
6780
  snapshotIssues: SnapshotValidationIssue[];
6781
+ streamOrderIssues: StreamOrderIssue[];
5639
6782
  };
5640
6783
  type RebuildResult = {
5641
6784
  documentId: string;
@@ -5659,10 +6802,14 @@ declare class DocumentIntegrityService implements IDocumentIntegrityService {
5659
6802
  validateDocument(documentId: string, branch?: string, signal?: AbortSignal): Promise<ValidationResult>;
5660
6803
  rebuildKeyframes(documentId: string, branch?: string, signal?: AbortSignal): Promise<RebuildResult>;
5661
6804
  rebuildSnapshots(documentId: string, branch?: string, signal?: AbortSignal): Promise<RebuildResult>;
6805
+ private findStreamOrderIssues;
5662
6806
  private discoverScopes;
5663
6807
  }
5664
6808
  //#endregion
5665
6809
  //#region src/processors/processor-manager.d.ts
6810
+ type ProcessorManagerOptions = {
6811
+ legacyProcessorIds?: boolean;
6812
+ };
5666
6813
  /**
5667
6814
  * Manages processor lifecycle based on operations.
5668
6815
  * Extends BaseReadModel to receive operations from ReadModelCoordinator.
@@ -5682,7 +6829,8 @@ declare class ProcessorManager extends BaseReadModel implements IProcessorManage
5682
6829
  private cursorCache;
5683
6830
  private logger;
5684
6831
  private driveContainerTypes;
5685
- constructor(db: Kysely<DocumentViewDatabase>, operationIndex: IOperationIndex, writeCache: IWriteCache, consistencyTracker: IConsistencyTracker, logger: ILogger, driveContainerTypes: ReadonlySet<string>);
6832
+ private legacyProcessorIds;
6833
+ constructor(db: Kysely<DocumentViewDatabase>, operationIndex: IOperationIndex, writeCache: IWriteCache, consistencyTracker: IConsistencyTracker, logger: ILogger, driveContainerTypes: ReadonlySet<string>, options?: ProcessorManagerOptions);
5686
6834
  init(): Promise<void>;
5687
6835
  protected commitOperations(items: OperationWithContext$1[]): Promise<void>;
5688
6836
  registerFactory(identifier: string, factory: ProcessorFactory$1): Promise<void>;
@@ -5707,5 +6855,5 @@ declare class ProcessorManager extends BaseReadModel implements IProcessorManage
5707
6855
  private deleteProcessorCursors;
5708
6856
  }
5709
6857
  //#endregion
5710
- export { type AbortMessage, type AtomicTxn, type AttachmentHash, type AttachmentRef, BaseReadModel, type BatchExecutionRequest, type BatchExecutionResult, type BatchLoadRequest, type BatchLoadResult, type BuiltInReadModelKind, type CachedSnapshot, type ChannelConfig, ChannelError, ChannelErrorSource, type ChannelHealth, type ChannelMeta, ChannelScheme, type ConnectionState, type ConnectionStateChangeCallback, type ConnectionStateChangedEvent, type ConnectionStateSnapshot, type ConsistencyCoordinate, type ConsistencyKey, type ConsistencyToken, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, type Database, type DbConfig, type DeadLetterAddedEvent, DefaultSubscriptionErrorHandler, type DocumentChangeEvent, DocumentChangeType, type DocumentGraphEdge, type DocumentIndexerDatabase, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, type DocumentModelSource, type DocumentModelSpec, type DocumentRelationship, type DocumentRevisions, type DocumentStreamKey, type DocumentViewDatabase, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, type ExecuteMessage, type ExecutionJobPlan, type ExecutorStartedEvent, type ExecutorStoppedEvent, type FactorySpec, type FileModelSource, type GqlChannelConfig, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, type HeartbeatMessage, type IChannel, type IChannelFactory, type IConsistencyTracker, type IDocumentGraph, type IDocumentIndexer, type IDocumentIntegrityService, type IDocumentModelLoader, type IDocumentModelRegistry, type IDocumentModelResolver, type IDocumentView, type IDriveClient, type IEventBus, type IJobAwaiter, type IJobExecutor, type IJobExecutorManager, type IJobTracker, type IKeyframeStore, type IMailbox, type IOperationIndex, type IOperationStore, type IPollTimer, type IProcessor, type IProcessorHostModule, type IProcessorManager, type IProjectionTransport, type IQueue, type IReactor, type IReactorClient, type IReactorSubscriptionManager, type IReadModel, type IReadModelCoordinator, type IRelationalDb, type ISubscriptionErrorHandler, type ISyncCursorStorage, type ISyncManager, type ISyncRemoteStorage, type ISyncStatusTracker, type IWriteCache, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, type InProcessReactorClientModule, type InProcessReactorModule, type InProcessSyncModule, type InitMessage, type InsertableDocumentSnapshot, IntervalPollTimer, InvalidModuleError, type Job, type JobAvailableEvent, JobAwaiter, type JobCompletedEvent, type JobExecutorConfig, JobExecutorEventTypes, type JobExecutorFactory, type JobFailedEvent, type JobInfo, type JobPendingEvent, type JobReadReadyEvent, type JobResult, type JobRunningEvent, type JobStartedEvent, JobStatus, type JobWriteReadyEvent, type JobWriteReadyPayload, type JwtHandler, type KeyframeSnapshot, type KeyframeValidationIssue, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, type LoadJobPlan, type LoadModelMessage, type LogMessage, Mailbox, type MetricsMessage, type ModelLoadFailedMessage, type ModelLoadedEvent, type ModelLoadedMessage, type ModelManifestEntry, ModuleNotFoundError, type ModuleRef, NullDocumentModelResolver, type OperationBatch, type OperationContext, type OperationFilter, type OperationIndexEntry, type OperationTable, type OperationWithContext, OptimisticLockError, type PackageModelSource, type PagedResults, type PagingOptions, type ParentMessage, type ParsedDriveUrl, type ParsedPaging, PollBehavior, PollingChannelError, type PoolInstrumentation, type PoolStats, type ProcessorApp, type ProcessorFactory, type ProcessorFactoryBuilder, type ProcessorFilter, ProcessorManager, type ProcessorRecord, type ProcessorStatus, type ProjectionShardBuilderConfig, type ProjectionShardManagerConfig, type ProjectionWorkerFactory, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, type ReactorClientModule, ReactorEventTypes, type ReactorFeatures, type JobFailedEvent$1 as ReactorJobFailedEvent, type ReactorModule, ReactorSubscriptionManager, type ReadModelBatchCompletedEvent, ReadModelCoordinator, type ReadModelFactory, type ReadModelFactoryDeps, type ReadModelIndexedEvent, type ReadModelIndexingStage, type ReadModelStage, type ReadyMessage, type RebuildResult, RelationalDbProcessor, RelationshipChangeType, type Remote, type RemoteCursor, type RemoteFilter, type RemoteMeta, type RemoteOptions, type RemoteRecord, type RemoteStatus, type ResultMessage, RevisionMismatchError, type SanitizedArg, type SearchFilter, type ShutdownMessage, type ShutdownStatus, type SignatureVerificationHandler, type SignatureVerifierSpec, type SignerConfig, SimpleJobExecutorManager, type SnapshotValidationIssue, type Database$1 as StorageDatabase, type SubscriptionErrorContext, SyncBuilder, type SyncEnvelope, type SyncEnvelopeType, SyncEventTypes, type SyncFailedEvent, type SyncModule, SyncOperation, SyncOperationAggregateError, type SyncOperationErrorType, SyncOperationStatus, type SyncPendingEvent, SyncStatus, type SyncStatusChangeCallback, SyncStatusTracker, type SyncSucceededEvent, type TrackedProcessor, type Unsubscribe, type ValidationResult, type ViewFilter, type ErrorInfo as WorkerErrorInfo, type WorkerMessage, type WorkerPoolConfig, type WorkerPoolOptions, type WriteCacheConfig, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, getMigrationStatus, instrumentPgPool, isDriveAuthError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, removeRelationshipAction, runMigrations, sanitizeArg, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
6858
+ export { ALWAYS_READABLE_SCOPES, APPEND_CONDITION_FAILED_PREFIX, type AbortMessage, type ActionCandidate, type ActionEvaluationConfig, type ActionEvaluations, type AdmissionDecision, type AppendCondition, AppendConditionFailedError, type AppendConditionStream, type AtomicTxn, type AttachmentHash, type AttachmentRef, type AuthDecisionModel, AuthEnforcementDisabledError, BareReadGate, BaseReadModel, type BatchExecutionRequest, type BatchExecutionResult, type BatchLoadRequest, type BatchLoadResult, type BuiltDecisionModel, type BuiltInReadModelKind, type CachedSnapshot, type ChannelConfig, ChannelError, ChannelErrorSource, type ChannelHealth, type ChannelMeta, ChannelScheme, type ConnectionState, type ConnectionStateChangeCallback, type ConnectionStateChangedEvent, type ConnectionStateSnapshot, type ConsistencyCoordinate, type ConsistencyKey, type ConsistencyToken, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, type Database, type DbConfig, type DeadLetterAddedEvent, type DecisionContext, type DecisionModel, type DecisionTarget, DefaultSubscriptionErrorHandler, type DocumentChangeEvent, DocumentChangeType, type DocumentDecisionModel, type DocumentGraphEdge, type DocumentIndexerDatabase, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, type DocumentModelSource, type DocumentModelSpec, type DocumentRelationship, type DocumentRevisions, type DocumentStreamKey, type DocumentViewDatabase, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, type Evaluation, EventBus, EventBusAggregateError, type ExecuteMessage, type ExecutionJobPlan, type ExecutorStartedEvent, type ExecutorStoppedEvent, type FactorySpec, type FileModelSource, type GqlChannelConfig, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, type HeartbeatMessage, type IChannel, type IChannelFactory, type IConsistencyTracker, type IDocumentGraph, type IDocumentIndexer, type IDocumentIntegrityService, type IDocumentModelLoader, type IDocumentModelRegistry, type IDocumentModelResolver, type IDocumentView, type IDriveClient, type IEventBus, type IJobAwaiter, type IJobExecutor, type IJobExecutorManager, type IJobTracker, type IKeyframeStore, type ILiveReadModelCoordinator, type IMailbox, type IOperationIndex, type IOperationStore, type IPollTimer, type IProcessor, type IProcessorHostModule, type IProcessorManager, type IProjectionTransport, type IQueue, type IReactor, type IReactorClient, type IReactorSubscriptionManager, type IReadGate, type IReadModel, type IReadModelCoordinator, type IRelationalDb, type IStreamStateReader, type ISubscriptionErrorHandler, type ISyncCursorStorage, type ISyncManager, type ISyncRemoteStorage, type ISyncStatusTracker, type IWriteCache, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, type InProcessReactorClientModule, type InProcessReactorModule, type InProcessSyncModule, type InitMessage, type InsertableDocumentSnapshot, IntervalPollTimer, InvalidModuleError, type Job, type JobAvailableEvent, JobAwaiter, type JobCompletedEvent, type JobExecutorConfig, JobExecutorEventTypes, type JobExecutorFactory, type JobFailedEvent, type JobInfo, type JobPendingEvent, type JobReadReadyEvent, type JobResult, type JobRunningEvent, type JobStartedEvent, JobStatus, type JobWriteReadyEvent, type JobWriteReadyPayload, type JwtHandler, type KeyframeSnapshot, type KeyframeValidationIssue, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, type LoadJobPlan, type LoadModelMessage, type LogMessage, Mailbox, type MetricsMessage, type ModelLoadFailedMessage, type ModelLoadedEvent, type ModelLoadedMessage, type ModelManifestEntry, ModelReadGate, ModuleNotFoundError, type ModuleRef, NullDocumentModelResolver, type OperationBatch, type OperationContext, type OperationFilter, type OperationIndexEntry, type OperationTable, type OperationWithContext, OptimisticLockError, type OutOfOrderPair, type PackageModelSource, type PagedResults, type PagingOptions, type ParentMessage, type ParsedDriveUrl, type ParsedPaging, PollBehavior, PollingChannelError, type PoolInstrumentation, type PoolStats, type ProcessorApp, type ProcessorFactory, type ProcessorFactoryBuilder, type ProcessorFilter, ProcessorManager, type ProcessorRecord, type ProcessorStatus, type Projection, type ProjectionShardBuilderConfig, type ProjectionShardManagerConfig, type ProjectionWorkerFactory, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, RECOVERABLE_GRAPHQL_ERROR_CODES, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, type ReactorClientModule, ReactorEventTypes, type ReactorFeatureFlags, type ReactorFeatures, type JobFailedEvent$1 as ReactorJobFailedEvent, type ReactorModule, ReactorSubscriptionManager, type ReadGateOptions, type ReadModelBatchCompletedEvent, ReadModelCoordinator, type ReadModelFactory, type ReadModelFactoryDeps, type ReadModelIndexedEvent, type ReadModelIndexingStage, type ReadModelRegistrationStage, type ReadModelStage, type ReadyMessage, type RebuildResult, type RegisteredDecisionModel, RelationalDbProcessor, RelationshipChangeType, type Remote, type RemoteCursor, type RemoteFilter, type RemoteMeta, type RemoteOptions, type RemoteRecord, type RemoteStatus, type ResultMessage, RetryAccounting, RevisionMismatchError, type SanitizedArg, type SearchFilter, SeededStateReader, type ShutdownMessage, type ShutdownStatus, type SignatureVerificationHandler, type SignatureVerifierSpec, type SignerConfig, SimpleJobExecutorManager, type SnapshotValidationIssue, type Database$1 as StorageDatabase, type StreamOrderIssue, type StreamQuery, type SubscriptionErrorContext, SyncBuilder, type SyncEnvelope, type SyncEnvelopeType, SyncEventTypes, type SyncFailedEvent, type SyncModule, SyncOperation, SyncOperationAggregateError, type SyncOperationErrorType, SyncOperationStatus, type SyncPendingEvent, SyncScopeGate, SyncStatus, type SyncStatusChangeCallback, SyncStatusTracker, type SyncSucceededEvent, type TrackedProcessor, type Unsubscribe, type ValidationResult, type ViewFilter, type ErrorInfo as WorkerErrorInfo, type WorkerMessage, type WorkerPoolConfig, type WorkerPoolOptions, type WriteCacheConfig, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, decideAtHead, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, isRecoverableGraphQLError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, readDecisionModel, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
5711
6859
  //# sourceMappingURL=index.d.ts.map