@powerhousedao/reactor 6.2.3-dev.23 → 6.2.3-dev.25

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.
Files changed (40) hide show
  1. package/dist/{build-worker-executor-DUjyF4NM.js → build-worker-executor-DPGjh9Uc.js} +24 -8
  2. package/dist/build-worker-executor-DPGjh9Uc.js.map +1 -0
  3. package/dist/{document-indexer-CQzTEE1o.js → document-indexer-zbdzqZ3K.js} +18 -9
  4. package/dist/{document-indexer-CQzTEE1o.js.map → document-indexer-zbdzqZ3K.js.map} +1 -1
  5. package/dist/entry.d.ts +2 -1
  6. package/dist/entry.js +10 -6
  7. package/dist/entry.js.map +1 -1
  8. package/dist/{errors-D3S6Eysd.js → errors-BM_ULVWd.js} +9 -1
  9. package/dist/{errors-D3S6Eysd.js.map → errors-BM_ULVWd.js.map} +1 -1
  10. package/dist/{drive-container-types-CS5IxDiA.js → forwarding-logger-CSsCJ38f.js} +1081 -174
  11. package/dist/forwarding-logger-CSsCJ38f.js.map +1 -0
  12. package/dist/index.d.ts +2248 -1978
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +438 -258
  15. package/dist/index.js.map +1 -1
  16. package/dist/projection-entry.d.ts +2 -1
  17. package/dist/projection-entry.js +7 -13
  18. package/dist/projection-entry.js.map +1 -1
  19. package/dist/{projection-shard-manager-CUo4bF4O.js → projection-shard-manager-B-j8VYzA.js} +2 -3
  20. package/dist/{projection-shard-manager-CUo4bF4O.js.map → projection-shard-manager-B-j8VYzA.js.map} +1 -1
  21. package/dist/{projection-worker-wI4PwcV2.js → projection-worker-D8uNFdwX.js} +1 -1
  22. package/dist/{projection-worker-wI4PwcV2.js.map → projection-worker-D8uNFdwX.js.map} +1 -1
  23. package/dist/{transport-CuogVKN_.js → transport-B3G50KVd.js} +1 -1
  24. package/dist/{transport-CuogVKN_.js.map → transport-B3G50KVd.js.map} +1 -1
  25. package/dist/{transport-ByGviWdZ.js → transport-CZDHzfvy.js} +1 -1
  26. package/dist/{transport-ByGviWdZ.js.map → transport-CZDHzfvy.js.map} +1 -1
  27. package/dist/{worker-handle-CrERzl8s.js → worker-handle-CwWNTpUg.js} +22 -16
  28. package/dist/worker-handle-CwWNTpUg.js.map +1 -0
  29. package/package.json +7 -8
  30. package/dist/build-worker-executor-DUjyF4NM.js.map +0 -1
  31. package/dist/drive-container-types-CS5IxDiA.js.map +0 -1
  32. package/dist/error-info-Cpu4OY3o.js +0 -62
  33. package/dist/error-info-Cpu4OY3o.js.map +0 -1
  34. package/dist/forwarding-logger-BBkMSxuJ.js +0 -85
  35. package/dist/forwarding-logger-BBkMSxuJ.js.map +0 -1
  36. package/dist/types-DMKLa0Ok.js +0 -33
  37. package/dist/types-DMKLa0Ok.js.map +0 -1
  38. package/dist/worker-g03lNM42.js +0 -22
  39. package/dist/worker-g03lNM42.js.map +0 -1
  40. package/dist/worker-handle-CrERzl8s.js.map +0 -1
@@ -1,5 +1,4 @@
1
- import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
2
- import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DowngradeNotSupportedError as DowngradeNotSupportedError$1, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createPresignedHeader, decide, defaultBaseState, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, isDenied, isUndoRedo, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, referencedGroupIds, sortOperations } from "@powerhousedao/shared/document-model";
1
+ import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DOCUMENT_SCOPE_ACTION_TYPES, DowngradeNotSupportedError as DowngradeNotSupportedError$1, actionSignerIdentity, actionSigningTarget, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, base58Decode, baseReducerVersion, buildOperationSignatureMessage, buildOperationSignatureParams, canonicalJson, createPresignedHeader, decide, defaultBaseState, deriveDocumentId, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashActionV2, hashBrowser, hashDocumentStateForScope, isDenied, isDerivedDocumentId, isUndoRedo, isV2ActionHash, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, referencedGroupIds, signaturePolicyOf, sortOperations, targetDocumentId, v2TupleProblem, withProtocolVersions } from "@powerhousedao/shared/document-model";
3
2
  import { v4 } from "uuid";
4
3
  import { Migrator, sql } from "kysely";
5
4
  //#region \0rolldown/runtime.js
@@ -52,6 +51,37 @@ function parsePagingOptions(paging, defaultLimit) {
52
51
  };
53
52
  }
54
53
  //#endregion
54
+ //#region src/events/types.ts
55
+ /**
56
+ * Custom error class that aggregates multiple errors from event subscribers.
57
+ */
58
+ var EventBusAggregateError = class extends Error {
59
+ errors;
60
+ constructor(errors) {
61
+ const message = `EventBus emit failed with ${errors.length} error(s): ${errors.map((e) => {
62
+ if (e && typeof e === "object" && "message" in e) return e.message;
63
+ return String(e);
64
+ }).join("; ")}`;
65
+ super(message);
66
+ this.name = "EventBusAggregateError";
67
+ this.errors = errors;
68
+ }
69
+ };
70
+ /**
71
+ * Event types for reactor lifecycle events.
72
+ */
73
+ const ReactorEventTypes = {
74
+ JOB_PENDING: 10001,
75
+ JOB_RUNNING: 10002,
76
+ JOB_WRITE_READY: 10003,
77
+ JOB_READ_READY: 10004,
78
+ JOB_FAILED: 10005,
79
+ READMODEL_BATCH_COMPLETED: 10006,
80
+ READMODEL_INDEXED: 10007,
81
+ MODEL_LOADED: 10008,
82
+ SIGNATURE_REFUSED: 10009
83
+ };
84
+ //#endregion
55
85
  //#region src/shared/errors.ts
56
86
  /**
57
87
  * Error thrown when attempting to access a deleted document.
@@ -167,19 +197,22 @@ var ExcessiveReshuffleError = class ExcessiveReshuffleError extends Error {
167
197
  return Error.isError(error) && error.name === "ExcessiveReshuffleError";
168
198
  }
169
199
  };
170
- /**
171
- * Error thrown when an operation has an invalid signature.
172
- */
200
+ /** The message carries the code: only name and message cross the queue. */
173
201
  var InvalidSignatureError = class InvalidSignatureError extends Error {
174
202
  documentId;
203
+ code;
175
204
  reason;
176
- constructor(documentId, reason) {
177
- super(`Invalid signature in document ${documentId}: ${reason}`);
205
+ constructor(documentId, code, reason) {
206
+ super(`Invalid signature in document ${documentId} [${code}]: ${reason}`);
178
207
  this.name = "InvalidSignatureError";
179
208
  this.documentId = documentId;
209
+ this.code = code;
180
210
  this.reason = reason;
181
211
  Error.captureStackTrace(this, InvalidSignatureError);
182
212
  }
213
+ static isError(error) {
214
+ return Error.isError(error) && error.name === "InvalidSignatureError";
215
+ }
183
216
  };
184
217
  /**
185
218
  * An UPGRADE_DOCUMENT action's preconditions (fromVersion and the per-scope
@@ -326,6 +359,7 @@ const APPEND_CONDITION_FAILED_PREFIX = "Append condition failed: ";
326
359
  * conflict, not a fault: the caller retries against the new stream heads.
327
360
  */
328
361
  var AppendConditionFailedError = class extends Error {
362
+ condition;
329
363
  constructor(condition) {
330
364
  const streams = condition.streams.map((s) => `${s.documentId}:${s.scope}:${s.branch}@${s.revision}`).join(", ");
331
365
  super(`${APPEND_CONDITION_FAILED_PREFIX}a read-set stream advanced [${streams}]`);
@@ -714,34 +748,13 @@ function selectDecisionModel(flags, registry) {
714
748
  //#endregion
715
749
  //#region src/executor/util.ts
716
750
  /** Actions the reactor reduces itself, onto the document scope. */
717
- const DOCUMENT_SCOPE_ACTIONS = new Set([
718
- "CREATE_DOCUMENT",
719
- "DELETE_DOCUMENT",
720
- "UPGRADE_DOCUMENT",
721
- "ADD_RELATIONSHIP",
722
- "REMOVE_RELATIONSHIP",
723
- "UPDATE_RELATIONSHIP"
724
- ]);
751
+ const DOCUMENT_SCOPE_ACTIONS = DOCUMENT_SCOPE_ACTION_TYPES;
725
752
  /**
726
753
  * `CREATE_DOCUMENT` is exempt by necessity: it runs before the document exists,
727
754
  * so building a decision model would throw and defer the job forever.
728
755
  */
729
756
  const GATED_DOCUMENT_ACTIONS = new Set([...DOCUMENT_SCOPE_ACTIONS].filter((type) => type !== "CREATE_DOCUMENT"));
730
757
  /**
731
- * The document a document-scope action writes to, which is not always the job's
732
- * own document: delete and upgrade name it in `input.documentId`, and the
733
- * relationship actions in `input.sourceId`. `execute` only checks that a batch
734
- * shares one scope, so a caller can submit an action whose target is a document
735
- * other than the one the job is keyed by. The policy gate has to follow the
736
- * action rather than the job, or it decides against a policy the caller may
737
- * control instead of the one guarding the write.
738
- */
739
- function targetDocumentId(action, fallback) {
740
- const input = action.input;
741
- if (action.type === "ADD_RELATIONSHIP" || action.type === "REMOVE_RELATIONSHIP" || action.type === "UPDATE_RELATIONSHIP") return typeof input?.sourceId === "string" && input.sourceId.length > 0 ? input.sourceId : fallback;
742
- return typeof input?.documentId === "string" && input.documentId.length > 0 ? input.documentId : fallback;
743
- }
744
- /**
745
758
  * Creates a PHDocument from a CREATE_DOCUMENT action input.
746
759
  * Reconstructs the document header and initializes the base state.
747
760
  *
@@ -1051,6 +1064,7 @@ const JobExecutorEventTypes = {
1051
1064
  //#endregion
1052
1065
  //#region src/cache/collection-membership-cache.ts
1053
1066
  var CollectionMembershipCache = class CollectionMembershipCache {
1067
+ operationIndex;
1054
1068
  cache = /* @__PURE__ */ new Map();
1055
1069
  constructor(operationIndex) {
1056
1070
  this.operationIndex = operationIndex;
@@ -1264,6 +1278,7 @@ var DocumentMetaCache = class DocumentMetaCache {
1264
1278
  return {
1265
1279
  state: document.state.document,
1266
1280
  documentType,
1281
+ protocolVersions: createAction.input.protocolVersions,
1267
1282
  documentScopeRevision: documentScopeRevision + 1
1268
1283
  };
1269
1284
  }
@@ -1345,6 +1360,7 @@ var KyselyOperationIndexTxn = class {
1345
1360
  }
1346
1361
  };
1347
1362
  var KyselyOperationIndex = class KyselyOperationIndex {
1363
+ db;
1348
1364
  trx;
1349
1365
  constructor(db) {
1350
1366
  this.db = db;
@@ -1459,6 +1475,12 @@ var KyselyOperationIndex = class KyselyOperationIndex {
1459
1475
  if (signal?.aborted) throw new Error("Operation aborted");
1460
1476
  return (await this.queryExecutor.selectFrom("group_references").select("documentId").where("groupId", "=", groupId).orderBy("documentId").execute()).map((row) => row.documentId);
1461
1477
  }
1478
+ async getOrdinalsByOpIds(documentId, scope, branch, opIds, signal) {
1479
+ signal?.throwIfAborted();
1480
+ if (opIds.length === 0) return /* @__PURE__ */ new Map();
1481
+ const rows = await this.queryExecutor.selectFrom("operation_index_operations").select("opId").select((eb) => eb.fn.max("ordinal").as("ordinal")).where("documentId", "=", documentId).where("branch", "=", branch).where("scope", "=", scope).where("opId", "in", opIds).groupBy("opId").execute();
1482
+ return new Map(rows.map((row) => [row.opId, Number(row.ordinal)]));
1483
+ }
1462
1484
  async find(collectionId, cursor, view, paging, signal) {
1463
1485
  if (signal?.aborted) throw new Error("Operation aborted");
1464
1486
  const outerCursor = cursor ?? -1;
@@ -1678,21 +1700,6 @@ var RingBuffer = class {
1678
1700
  }
1679
1701
  };
1680
1702
  //#endregion
1681
- //#region src/cache/write-cache-types.ts
1682
- /**
1683
- * Where a snapshot sits in its stream.
1684
- *
1685
- * - `Head`: the newest revision of the stream when it was stored. Only these
1686
- * can answer a read that asks for the head.
1687
- * - `Historical`: state at an earlier revision. Usable as a starting point to
1688
- * replay forward from, and as an answer to a read for that same revision.
1689
- */
1690
- let SnapshotPosition = /* @__PURE__ */ function(SnapshotPosition) {
1691
- SnapshotPosition["Head"] = "head";
1692
- SnapshotPosition["Historical"] = "historical";
1693
- return SnapshotPosition;
1694
- }({});
1695
- //#endregion
1696
1703
  //#region src/cache/kysely-write-cache.ts
1697
1704
  /**
1698
1705
  * The last operation index a keyframe's document reflects for the scope. A
@@ -1831,13 +1838,13 @@ var KyselyWriteCache = class KyselyWriteCache {
1831
1838
  const snapshots = stream.ringBuffer.getAll();
1832
1839
  if (targetRevision === void 0) {
1833
1840
  const newest = highestRevision(snapshots);
1834
- if (newest?.position === SnapshotPosition.Head) {
1841
+ if (newest?.position === "head") {
1835
1842
  this.lruTracker.touch(streamKey);
1836
1843
  return copyDocument(newest.document);
1837
1844
  }
1838
1845
  if (newest) {
1839
1846
  const document = await this.warmMissRebuild(newest.document, newest.revision, documentId, scope, branch, void 0, signal);
1840
- this.store(documentId, scope, branch, (document.header.revision[scope] ?? 0) - 1, document, SnapshotPosition.Head);
1847
+ this.store(documentId, scope, branch, (document.header.revision[scope] ?? 0) - 1, document, "head");
1841
1848
  this.lruTracker.touch(streamKey);
1842
1849
  return document;
1843
1850
  }
@@ -1850,7 +1857,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1850
1857
  const newestOlder = this.findNearestOlderSnapshot(snapshots, targetRevision);
1851
1858
  if (newestOlder) {
1852
1859
  const document = await this.warmMissRebuild(newestOlder.document, newestOlder.revision, documentId, scope, branch, targetRevision, signal);
1853
- this.store(documentId, scope, branch, targetRevision, document, SnapshotPosition.Historical);
1860
+ this.store(documentId, scope, branch, targetRevision, document, "historical");
1854
1861
  this.lruTracker.touch(streamKey);
1855
1862
  return document;
1856
1863
  }
@@ -1858,7 +1865,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1858
1865
  }
1859
1866
  const document = await this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
1860
1867
  const revision = targetRevision ?? (document.header.revision[scope] ?? 0) - 1;
1861
- this.store(documentId, scope, branch, revision, document, targetRevision === void 0 ? SnapshotPosition.Head : SnapshotPosition.Historical);
1868
+ this.store(documentId, scope, branch, revision, document, targetRevision === void 0 ? "head" : "historical");
1862
1869
  return document;
1863
1870
  }
1864
1871
  /**
@@ -1893,7 +1900,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1893
1900
  if (run.length === 0) return;
1894
1901
  for (const entry of run.slice(0, -1)) this.persistKeyframe(documentId, scope, branch, entry.revision, entry.document);
1895
1902
  const head = run[run.length - 1];
1896
- this.store(documentId, scope, branch, head.revision, head.document, SnapshotPosition.Head);
1903
+ this.store(documentId, scope, branch, head.revision, head.document, "head");
1897
1904
  }
1898
1905
  store(documentId, scope, branch, revision, document, position) {
1899
1906
  const streamKey = this.makeStreamKey(documentId, scope, branch);
@@ -2099,10 +2106,10 @@ var KyselyWriteCache = class KyselyWriteCache {
2099
2106
  for (const pending of pendingUpgrades) pending.subsequentDeletes.push(operation.action);
2100
2107
  } else {
2101
2108
  const protocolVersion = baseReducerVersion(document.header);
2102
- document = docModule.reducer(document, operation.action, void 0, {
2109
+ document = withProtocolVersions(docModule.reducer(document, operation.action, void 0, {
2103
2110
  skip: operation.skip,
2104
2111
  protocolVersion
2105
- });
2112
+ }), document.header.protocolVersions);
2106
2113
  }
2107
2114
  }
2108
2115
  }
@@ -2151,12 +2158,12 @@ var KyselyWriteCache = class KyselyWriteCache {
2151
2158
  if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
2152
2159
  else {
2153
2160
  const protocolVersion = baseReducerVersion(document.header);
2154
- document = getModuleCached(moduleVersion).reducer(document, operation.action, void 0, {
2161
+ document = withProtocolVersions(getModuleCached(moduleVersion).reducer(document, operation.action, void 0, {
2155
2162
  skip: operation.skip,
2156
2163
  protocolVersion,
2157
2164
  replayOptions: { operation },
2158
2165
  skipIndexValidation: true
2159
- });
2166
+ }), document.header.protocolVersions);
2160
2167
  }
2161
2168
  }
2162
2169
  const reachedTarget = targetRevision !== void 0 && result.results.some((op) => op.index >= targetRevision);
@@ -2280,12 +2287,12 @@ var KyselyWriteCache = class KyselyWriteCache {
2280
2287
  if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
2281
2288
  else {
2282
2289
  const protocolVersion = baseReducerVersion(document.header);
2283
- document = module.reducer(document, operation.action, void 0, {
2290
+ document = withProtocolVersions(module.reducer(document, operation.action, void 0, {
2284
2291
  skip: operation.skip,
2285
2292
  protocolVersion,
2286
2293
  replayOptions: { operation },
2287
2294
  skipIndexValidation: true
2288
- });
2295
+ }), document.header.protocolVersions);
2289
2296
  }
2290
2297
  if (targetRevision !== void 0 && operation.index === targetRevision) break;
2291
2298
  }
@@ -2412,6 +2419,11 @@ function validateFeatureFlags(flags, prerequisites) {
2412
2419
  //#endregion
2413
2420
  //#region src/executor/execution-scope.ts
2414
2421
  var DefaultExecutionScope = class {
2422
+ operationStore;
2423
+ operationIndex;
2424
+ writeCache;
2425
+ documentMetaCache;
2426
+ collectionMembershipCache;
2415
2427
  constructor(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache) {
2416
2428
  this.operationStore = operationStore;
2417
2429
  this.operationIndex = operationIndex;
@@ -2431,6 +2443,13 @@ var DefaultExecutionScope = class {
2431
2443
  }
2432
2444
  };
2433
2445
  var KyselyExecutionScope = class {
2446
+ db;
2447
+ operationStore;
2448
+ operationIndex;
2449
+ keyframeStore;
2450
+ writeCache;
2451
+ documentMetaCache;
2452
+ collectionMembershipCache;
2434
2453
  constructor(db, operationStore, operationIndex, keyframeStore, writeCache, documentMetaCache, collectionMembershipCache) {
2435
2454
  this.db = db;
2436
2455
  this.operationStore = operationStore;
@@ -2458,7 +2477,7 @@ var KyselyExecutionScope = class {
2458
2477
  };
2459
2478
  //#endregion
2460
2479
  //#region src/utils/reshuffle.ts
2461
- const STRICT_ORDER_ACTION_TYPES = new Set([
2480
+ const STRICT_ORDER_ACTION_TYPES = /* @__PURE__ */ new Set([
2462
2481
  "CREATE_DOCUMENT",
2463
2482
  "DELETE_DOCUMENT",
2464
2483
  "UPGRADE_DOCUMENT",
@@ -2789,6 +2808,8 @@ const DRIVE_COLLECTION_PREFIX = "drive.";
2789
2808
  * rows and persisted remotes remain valid without migration.
2790
2809
  */
2791
2810
  var DriveCollectionId = class DriveCollectionId {
2811
+ driveId;
2812
+ branch;
2792
2813
  constructor(driveId, branch) {
2793
2814
  this.driveId = driveId;
2794
2815
  this.branch = branch;
@@ -2821,6 +2842,11 @@ var DriveCollectionId = class DriveCollectionId {
2821
2842
  //#endregion
2822
2843
  //#region src/executor/document-action-handler.ts
2823
2844
  var DocumentActionHandler = class {
2845
+ registry;
2846
+ logger;
2847
+ driveContainerTypes;
2848
+ featureFlags;
2849
+ decisionModel;
2824
2850
  constructor(registry, logger, driveContainerTypes, featureFlags, decisionModel) {
2825
2851
  this.registry = registry;
2826
2852
  this.logger = logger;
@@ -2913,7 +2939,7 @@ var DocumentActionHandler = class {
2913
2939
  [job.scope]: [...standing.operations[job.scope] ?? [], operation]
2914
2940
  };
2915
2941
  executing.touchedStreams.add(job.documentId, job.scope, job.branch);
2916
- stores.writeCache.putState(job.documentId, job.scope, job.branch, operation.index, standing, SnapshotPosition.Head);
2942
+ stores.writeCache.putState(job.documentId, job.scope, job.branch, operation.index, standing, "head");
2917
2943
  indexTxn.write([{
2918
2944
  ...operation,
2919
2945
  documentId: job.documentId,
@@ -2925,6 +2951,7 @@ var DocumentActionHandler = class {
2925
2951
  stores.documentMetaCache.putDocumentMeta(job.documentId, job.branch, {
2926
2952
  state: standing.state.document,
2927
2953
  documentType: standing.header.documentType,
2954
+ protocolVersions: standing.header.protocolVersions,
2928
2955
  documentScopeRevision: operation.index + 1
2929
2956
  });
2930
2957
  return buildSuccessResult(job, operation, job.documentId, standing.header.documentType, JSON.stringify({
@@ -2948,8 +2975,8 @@ var DocumentActionHandler = class {
2948
2975
  branch: job.branch
2949
2976
  });
2950
2977
  const resultingStateObj = {
2951
- header: document.header,
2952
- ...document.state
2978
+ ...document.state,
2979
+ header: document.header
2953
2980
  };
2954
2981
  const resultingState = JSON.stringify(resultingStateObj);
2955
2982
  const writeResult = await this.writeOperationToStore({
@@ -2966,7 +2993,7 @@ var DocumentActionHandler = class {
2966
2993
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
2967
2994
  };
2968
2995
  executing.touchedStreams.add(document.header.id, job.scope, job.branch);
2969
- stores.writeCache.putState(document.header.id, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
2996
+ stores.writeCache.putState(document.header.id, job.scope, job.branch, operation.index, document, "head");
2970
2997
  indexTxn.write([{
2971
2998
  ...operation,
2972
2999
  documentId: document.header.id,
@@ -2983,6 +3010,7 @@ var DocumentActionHandler = class {
2983
3010
  stores.documentMetaCache.putDocumentMeta(document.header.id, job.branch, {
2984
3011
  state: document.state.document,
2985
3012
  documentType: document.header.documentType,
3013
+ protocolVersions: document.header.protocolVersions,
2986
3014
  documentScopeRevision: 1
2987
3015
  });
2988
3016
  return buildSuccessResult(job, operation, document.header.id, document.header.documentType, resultingState, startTime);
@@ -3026,7 +3054,7 @@ var DocumentActionHandler = class {
3026
3054
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
3027
3055
  };
3028
3056
  executing.touchedStreams.add(documentId, job.scope, job.branch);
3029
- stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
3057
+ stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, "head");
3030
3058
  indexTxn.write([{
3031
3059
  ...operation,
3032
3060
  documentId,
@@ -3038,6 +3066,7 @@ var DocumentActionHandler = class {
3038
3066
  stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {
3039
3067
  state: document.state.document,
3040
3068
  documentType: document.header.documentType,
3069
+ protocolVersions: document.header.protocolVersions,
3041
3070
  documentScopeRevision: operation.index + 1
3042
3071
  });
3043
3072
  return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
@@ -3076,7 +3105,7 @@ var DocumentActionHandler = class {
3076
3105
  } catch (error) {
3077
3106
  return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch revisions for upgrade: ${error instanceof Error ? error.message : String(error)}`), startTime);
3078
3107
  }
3079
- const revisionScopes = new Set([...Object.keys(input.revision), ...Object.keys(actualRevisions)]);
3108
+ const revisionScopes = /* @__PURE__ */ new Set([...Object.keys(input.revision), ...Object.keys(actualRevisions)]);
3080
3109
  for (const revisionScope of revisionScopes) {
3081
3110
  const snapshot = input.revision[revisionScope] ?? 0;
3082
3111
  const actual = actualRevisions[revisionScope] ?? 0;
@@ -3118,8 +3147,8 @@ var DocumentActionHandler = class {
3118
3147
  branch: job.branch
3119
3148
  });
3120
3149
  const resultingStateObj = {
3121
- header: document.header,
3122
- ...document.state
3150
+ ...document.state,
3151
+ header: document.header
3123
3152
  };
3124
3153
  if (fromVersion > 0) resultingStateObj.__migrated = true;
3125
3154
  const resultingState = JSON.stringify(resultingStateObj);
@@ -3137,7 +3166,7 @@ var DocumentActionHandler = class {
3137
3166
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
3138
3167
  };
3139
3168
  executing.touchedStreams.add(documentId, job.scope, job.branch);
3140
- stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
3169
+ stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, "head");
3141
3170
  for (const scope of otherScopes) executing.postCommitInvalidations.push({
3142
3171
  documentId,
3143
3172
  scope,
@@ -3154,6 +3183,7 @@ var DocumentActionHandler = class {
3154
3183
  stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {
3155
3184
  state: document.state.document,
3156
3185
  documentType: document.header.documentType,
3186
+ protocolVersions: document.header.protocolVersions,
3157
3187
  documentScopeRevision: operation.index + 1
3158
3188
  });
3159
3189
  return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
@@ -3223,7 +3253,7 @@ var DocumentActionHandler = class {
3223
3253
  const resultingState = JSON.stringify(resultingStateObj);
3224
3254
  executing.touchedStreams.add(input.sourceId, job.scope, job.branch);
3225
3255
  executing.touchedStreams.add(input.targetId, job.scope, job.branch);
3226
- stores.writeCache.putState(input.sourceId, job.scope, job.branch, operation.index, sourceDoc, SnapshotPosition.Head);
3256
+ stores.writeCache.putState(input.sourceId, job.scope, job.branch, operation.index, sourceDoc, "head");
3227
3257
  indexTxn.write([{
3228
3258
  ...operation,
3229
3259
  documentId: input.sourceId,
@@ -3242,6 +3272,7 @@ var DocumentActionHandler = class {
3242
3272
  stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {
3243
3273
  state: sourceDoc.state.document,
3244
3274
  documentType: sourceDoc.header.documentType,
3275
+ protocolVersions: sourceDoc.header.protocolVersions,
3245
3276
  documentScopeRevision: operation.index + 1
3246
3277
  });
3247
3278
  return buildSuccessResult(job, operation, input.sourceId, sourceDoc.header.documentType, resultingState, startTime);
@@ -3270,55 +3301,632 @@ var DocumentActionHandler = class {
3270
3301
  }
3271
3302
  };
3272
3303
  //#endregion
3273
- //#region src/executor/signature-verifier.ts
3274
- var SignatureVerifier = class {
3275
- constructor(verifier) {
3276
- this.verifier = verifier;
3277
- }
3278
- async verifyActions(documentId, branch, actions) {
3279
- if (!this.verifier) return;
3280
- for (const action of actions) {
3281
- const signer = action.context?.signer;
3282
- if (!signer) continue;
3283
- if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Action ${action.id} has signer but no signatures`);
3284
- const publicKey = signer.app.key;
3285
- let isValid;
3286
- try {
3287
- const tempOperation = {
3288
- id: deriveOperationId(documentId, action.scope, branch, action.id),
3289
- index: 0,
3290
- timestampUtcMs: action.timestampUtcMs || (/* @__PURE__ */ new Date()).toISOString(),
3291
- hash: "",
3292
- skip: 0,
3293
- action
3294
- };
3295
- isValid = await this.verifier(tempOperation, publicKey);
3296
- } catch (error) {
3297
- const errorMessage = error instanceof Error ? error.message : String(error);
3298
- throw new InvalidSignatureError(documentId, `Action ${action.id} verification failed: ${errorMessage}`);
3304
+ //#region src/signer/passthrough-signer.ts
3305
+ /**
3306
+ * A no-op signer that returns empty values for all methods.
3307
+ * Used when signing is not required.
3308
+ */
3309
+ var PassthroughSigner = class {
3310
+ publicKey = {};
3311
+ sign() {
3312
+ return Promise.resolve(/* @__PURE__ */ new Uint8Array(0));
3313
+ }
3314
+ verify() {
3315
+ return Promise.resolve();
3316
+ }
3317
+ signAction() {
3318
+ return Promise.resolve([
3319
+ "",
3320
+ "",
3321
+ "",
3322
+ "",
3323
+ ""
3324
+ ]);
3325
+ }
3326
+ };
3327
+ //#endregion
3328
+ //#region src/signer/trust-policy.ts
3329
+ /**
3330
+ * The policy admission asks: the reactor's own key signing as its own user is
3331
+ * accepted, then the host's policy decides, else the default does. `own` is
3332
+ * read on every ask, since its user changes on login.
3333
+ */
3334
+ function admissionTrustPolicy(own, authEnforcement, policy) {
3335
+ return { authorizeSigner(signer, key, documentId) {
3336
+ const identity = actionSignerIdentity(own);
3337
+ if (identity.app.key !== "" && key === identity.app.key) {
3338
+ if (sameUser(actionSignerIdentity(signer).user, identity.user)) return Promise.resolve(true);
3339
+ }
3340
+ if (policy) return policy.authorizeSigner(signer, key, documentId);
3341
+ return Promise.resolve(!authEnforcement);
3342
+ } };
3343
+ }
3344
+ function sameUser(a, b) {
3345
+ return a.address.toLowerCase() === b.address.toLowerCase() && a.networkId === b.networkId && a.chainId === b.chainId;
3346
+ }
3347
+ //#endregion
3348
+ //#region src/signer/did-key.ts
3349
+ const DID_KEY_PREFIX = "did:key:z";
3350
+ const P256_MULTICODEC = [128, 36];
3351
+ const MAX_CACHED_KEYS = 1024;
3352
+ const P = BigInt("0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff");
3353
+ const A = BigInt("0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc");
3354
+ const B = BigInt("0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b");
3355
+ /** A did:key that is not a compressed P-256 public key. */
3356
+ var MalformedDidKeyError = class extends Error {
3357
+ constructor(did, detail) {
3358
+ super(`Malformed did:key ${did}: ${detail}`);
3359
+ this.name = "MalformedDidKeyError";
3360
+ }
3361
+ };
3362
+ const keyCache = /* @__PURE__ */ new Map();
3363
+ /** Cached per process: decompressing the point costs a BigInt modPow. */
3364
+ async function importDidKey(did) {
3365
+ const cached = keyCache.get(did);
3366
+ if (cached) {
3367
+ keyCache.delete(did);
3368
+ keyCache.set(did, cached);
3369
+ return cached;
3370
+ }
3371
+ const raw = decompressP256(compressedPointFromDid(did), did);
3372
+ const key = await crypto.subtle.importKey("raw", raw.buffer, {
3373
+ name: "ECDSA",
3374
+ namedCurve: "P-256"
3375
+ }, false, ["verify"]);
3376
+ keyCache.set(did, key);
3377
+ if (keyCache.size > MAX_CACHED_KEYS) {
3378
+ const oldest = keyCache.keys().next().value;
3379
+ if (oldest !== void 0) keyCache.delete(oldest);
3380
+ }
3381
+ return key;
3382
+ }
3383
+ function compressedPointFromDid(did) {
3384
+ if (!did.startsWith(DID_KEY_PREFIX)) throw new MalformedDidKeyError(did, "not a base58btc did:key");
3385
+ const decoded = base58Decode(did.slice(9));
3386
+ if (!decoded || decoded.length !== 35) throw new MalformedDidKeyError(did, "not a compressed P-256 key");
3387
+ if (decoded[0] !== P256_MULTICODEC[0] || decoded[1] !== P256_MULTICODEC[1]) throw new MalformedDidKeyError(did, "not a P-256 multicodec");
3388
+ return decoded.subarray(2);
3389
+ }
3390
+ function decompressP256(compressed, did) {
3391
+ const prefix = compressed[0];
3392
+ if (prefix !== 2 && prefix !== 3) throw new MalformedDidKeyError(did, `compression prefix ${prefix}`);
3393
+ let x = BigInt(0);
3394
+ for (let i = 1; i < compressed.length; i++) x = x << BigInt(8) | BigInt(compressed[i]);
3395
+ if (x >= P) throw new MalformedDidKeyError(did, "x outside the field");
3396
+ const ySquared = (modPow(x, BigInt(3), P) + A * x + B) % P;
3397
+ let y = modPow(ySquared, (P + BigInt(1)) / BigInt(4), P);
3398
+ if (y * y % P !== ySquared) throw new MalformedDidKeyError(did, "x is not on the curve");
3399
+ if (y % BigInt(2) === BigInt(0) !== (prefix === 2)) y = P - y;
3400
+ const uncompressed = /* @__PURE__ */ new Uint8Array(65);
3401
+ uncompressed[0] = 4;
3402
+ uncompressed.set(bigIntToBytes(x), 1);
3403
+ uncompressed.set(bigIntToBytes(y), 33);
3404
+ return uncompressed;
3405
+ }
3406
+ function modPow(base, exp, mod) {
3407
+ let result = BigInt(1);
3408
+ let b = base % mod;
3409
+ let e = exp;
3410
+ while (e > BigInt(0)) {
3411
+ if (e & BigInt(1)) result = result * b % mod;
3412
+ e >>= BigInt(1);
3413
+ b = b * b % mod;
3414
+ }
3415
+ return result;
3416
+ }
3417
+ function bigIntToBytes(value) {
3418
+ const bytes = /* @__PURE__ */ new Uint8Array(32);
3419
+ let v = value;
3420
+ for (let i = 31; i >= 0; i--) {
3421
+ bytes[i] = Number(v & BigInt(255));
3422
+ v >>= BigInt(8);
3423
+ }
3424
+ return bytes;
3425
+ }
3426
+ //#endregion
3427
+ //#region src/signer/verify-action-signature.ts
3428
+ const RENOWN_HASH_LENGTH = 44;
3429
+ const SHARED_HASH_LENGTH = 28;
3430
+ const HEX_SIGNATURE = /^(0x)?([0-9a-fA-F]{2})+$/;
3431
+ /**
3432
+ * Integrity only: identity binding and the live-id check sit with the caller.
3433
+ * `operation` is the incoming operation at load admission.
3434
+ */
3435
+ async function verifyActionSignature(action, target, path, operation) {
3436
+ const policy = target.policy ?? "legacy";
3437
+ const refusal = policyRefusal(action, policy);
3438
+ if (refusal) return refusal;
3439
+ const shape = signerShape(action.context?.signer);
3440
+ if (shape.kind === "unsigned") return policy === "v2-required" ? refuse("unsigned", "UNSIGNED_REQUIRED", `action ${action.id} is unsigned but ${target.documentId} requires v2 signatures`) : {
3441
+ ok: true,
3442
+ scheme: "unsigned"
3443
+ };
3444
+ if (shape.kind === "malformed") return refuse("legacy-unknown", "MALFORMED_TUPLE", `action ${action.id} ${shape.reason}`);
3445
+ const { signer, tuple } = shape;
3446
+ const scheme = schemeOf(tuple[2]);
3447
+ if (tuple[1] !== signer.app.key) return refuse(scheme, "KEY_MISMATCH", `action ${action.id} tuple key does not match signer.app.key`);
3448
+ if (scheme !== "v2" && policy === "v2-required") return refuse(scheme, "SCHEME_BELOW_POLICY", `action ${action.id} carries a legacy tuple but ${target.documentId} requires v2 signatures`);
3449
+ if (scheme === "legacy-unknown" && path === "mutation") return refuse(scheme, "MALFORMED_TUPLE", `action ${action.id} carries a hash of unknown length ${tuple[2].length}`);
3450
+ if (scheme === "v2") {
3451
+ const refusal = await checkV2(action, tuple, target, operation);
3452
+ if (refusal) return refusal;
3453
+ } else if (path === "mutation") {
3454
+ if (await legacyHash(scheme, action, target) !== tuple[2]) return refuse(scheme, "HASH_MISMATCH", `action ${action.id} does not match the hash its signature covers`);
3455
+ }
3456
+ return verifyEcdsa(tuple, scheme, action.id);
3457
+ }
3458
+ /** Refusals a document's policy makes before any tuple is read. */
3459
+ function policyRefusal(action, policy) {
3460
+ if (action.type !== "CREATE_DOCUMENT") return;
3461
+ const scheme = schemeOfAction(action);
3462
+ const input = action.input;
3463
+ const documentId = input?.documentId ?? "";
3464
+ if (policy === "legacy") return isDerivedDocumentId(documentId) ? refuse(scheme, "ID_MISMATCH", `action ${action.id}: a legacy document cannot take the content-addressed id ${documentId}`) : void 0;
3465
+ const expected = derivedCreateId(input);
3466
+ if (expected === documentId) return;
3467
+ return refuse(scheme, "ID_MISMATCH", expected === void 0 ? `action ${action.id} creates v2-required ${documentId} without the header params its id is derived from` : `action ${action.id} creates v2-required ${documentId}, but its header params derive ${expected}`);
3468
+ }
3469
+ function derivedCreateId(input) {
3470
+ if (!input?.signing || !input.protocolVersions) return;
3471
+ try {
3472
+ return deriveDocumentId({
3473
+ documentType: input.model,
3474
+ createdAtUtcIso: input.signing.createdAtUtcIso,
3475
+ nonce: input.signing.nonce,
3476
+ protocolVersions: input.protocolVersions
3477
+ });
3478
+ } catch {
3479
+ return;
3480
+ }
3481
+ }
3482
+ function schemeOfAction(action) {
3483
+ const shape = signerShape(action.context?.signer);
3484
+ if (shape.kind === "unsigned") return "unsigned";
3485
+ return shape.kind === "signed" ? schemeOf(shape.tuple[2]) : "legacy-unknown";
3486
+ }
3487
+ /** Peers send arbitrary JSON, so nothing past `context` is assumed. */
3488
+ function signerShape(value) {
3489
+ if (value === void 0 || value === null) return { kind: "unsigned" };
3490
+ if (!isRecord(value)) return {
3491
+ kind: "malformed",
3492
+ reason: "has a signer that is not an object"
3493
+ };
3494
+ const { app, user, signatures } = value;
3495
+ if (app !== void 0 && app !== null && !isRecord(app)) return {
3496
+ kind: "malformed",
3497
+ reason: "has a signer.app that is not an object"
3498
+ };
3499
+ const key = isRecord(app) ? app.key : void 0;
3500
+ if (key !== void 0 && key !== null && typeof key !== "string") return {
3501
+ kind: "malformed",
3502
+ reason: "has a signer.app.key that is not a string"
3503
+ };
3504
+ if (!key) return { kind: "unsigned" };
3505
+ if (!isRecord(user) || typeof user.address !== "string") return {
3506
+ kind: "malformed",
3507
+ reason: "has a signer without a user address"
3508
+ };
3509
+ if (!Array.isArray(signatures)) return {
3510
+ kind: "malformed",
3511
+ reason: "has a signer whose signatures are not a list"
3512
+ };
3513
+ const tuple = signatures.at(-1);
3514
+ if (!isTuple(tuple)) return {
3515
+ kind: "malformed",
3516
+ reason: signatures.length === 0 ? "has a signer but no signatures" : "carries a malformed signature tuple"
3517
+ };
3518
+ return {
3519
+ kind: "signed",
3520
+ signer: value,
3521
+ tuple
3522
+ };
3523
+ }
3524
+ function isRecord(value) {
3525
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3526
+ }
3527
+ async function checkV2(action, tuple, target, operation) {
3528
+ const problem = v2TupleProblem(tuple);
3529
+ if (problem) return refuse("v2", "MALFORMED_TUPLE", `action ${action.id}: ${problem}`);
3530
+ if (operation && !sameInstant(operation.timestampUtcMs, action.timestampUtcMs)) return refuse("v2", "TIMESTAMP_MISMATCH", `action ${action.id} is stamped ${action.timestampUtcMs} but its operation ${operation.timestampUtcMs}`);
3531
+ let expected;
3532
+ try {
3533
+ expected = await hashActionV2(action, target, action.context.signer);
3534
+ } catch (error) {
3535
+ return refuse("v2", "HASH_MISMATCH", `action ${action.id} has no v2 hash: ${error instanceof Error ? error.message : String(error)}`);
3536
+ }
3537
+ if (expected !== tuple[2]) return refuse("v2", "HASH_MISMATCH", `action ${action.id} does not match the hash its signature covers`);
3538
+ }
3539
+ function sameInstant(a, b) {
3540
+ const left = Date.parse(a);
3541
+ return !Number.isNaN(left) && left === Date.parse(b);
3542
+ }
3543
+ function schemeOf(hash) {
3544
+ if (isV2ActionHash(hash)) return "v2";
3545
+ if (hash.length === RENOWN_HASH_LENGTH) return "legacy-renown";
3546
+ if (hash.length === SHARED_HASH_LENGTH) return "legacy-shared";
3547
+ return "legacy-unknown";
3548
+ }
3549
+ async function legacyHash(scheme, action, target) {
3550
+ if (scheme === "legacy-renown") {
3551
+ const payload = [
3552
+ action.scope,
3553
+ action.type,
3554
+ JSON.stringify(action.input)
3555
+ ].join("");
3556
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
3557
+ return bytesToBase64(new Uint8Array(digest));
3558
+ }
3559
+ const signer = action.context.signer;
3560
+ return buildOperationSignatureParams({
3561
+ documentId: target.documentId,
3562
+ signer,
3563
+ action,
3564
+ previousStateHash: ""
3565
+ })[2];
3566
+ }
3567
+ async function verifyEcdsa(tuple, scheme, actionId) {
3568
+ const [timestamp, key, hash, prevStateHash, signatureHex] = tuple;
3569
+ if (!HEX_SIGNATURE.test(signatureHex)) return refuse(scheme, "MALFORMED_TUPLE", `action ${actionId} signature is not hex`);
3570
+ let cryptoKey;
3571
+ try {
3572
+ cryptoKey = await importDidKey(key);
3573
+ } catch (error) {
3574
+ return refuse(scheme, "MALFORMED_TUPLE", `action ${actionId}: ${error instanceof Error ? error.message : String(error)}`);
3575
+ }
3576
+ const message = buildOperationSignatureMessage([
3577
+ timestamp,
3578
+ key,
3579
+ hash,
3580
+ prevStateHash
3581
+ ]);
3582
+ let valid;
3583
+ try {
3584
+ valid = await crypto.subtle.verify({
3585
+ name: "ECDSA",
3586
+ hash: "SHA-256"
3587
+ }, cryptoKey, hexToBytes(signatureHex).buffer, message.buffer);
3588
+ } catch {
3589
+ valid = false;
3590
+ }
3591
+ if (!valid) return refuse(scheme, "BAD_SIGNATURE", `action ${actionId} signature does not verify under its key`);
3592
+ return {
3593
+ ok: true,
3594
+ scheme
3595
+ };
3596
+ }
3597
+ function isTuple(value) {
3598
+ return Array.isArray(value) && value.length === 5 && value.every((element) => typeof element === "string");
3599
+ }
3600
+ function refuse(scheme, code, reason) {
3601
+ return {
3602
+ ok: false,
3603
+ scheme,
3604
+ code,
3605
+ reason
3606
+ };
3607
+ }
3608
+ function hexToBytes(hex) {
3609
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
3610
+ const bytes = new Uint8Array(clean.length / 2);
3611
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
3612
+ return bytes;
3613
+ }
3614
+ function bytesToBase64(bytes) {
3615
+ let binary = "";
3616
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
3617
+ return btoa(binary);
3618
+ }
3619
+ //#endregion
3620
+ //#region src/executor/synthesized-signing.ts
3621
+ /** Submitted types whose stored action the reducer writes in their place. */
3622
+ const SYNTHESIZING_TYPES = /* @__PURE__ */ new Set(["UNDO", "REDO"]);
3623
+ /**
3624
+ * The id of the action synthesized from `submittedId`. Derived, so the stored
3625
+ * operation answers for the submitted id at the live-id check.
3626
+ */
3627
+ function synthesizedActionId(submittedId) {
3628
+ return hashBrowser(`synthesized:${submittedId}`, "sha1", "hex").slice(0, 32);
3629
+ }
3630
+ /**
3631
+ * Whether `operation` stores an action the reducer made from `submitted`
3632
+ * rather than `submitted` itself or an operation already in `history`.
3633
+ */
3634
+ function isSynthesized(submitted, operation, history) {
3635
+ if (!SYNTHESIZING_TYPES.has(submitted.type)) return false;
3636
+ const id = operation.action.id;
3637
+ if (!id) return true;
3638
+ if (id === submitted.id) return false;
3639
+ return !history.some((existing) => existing.action.id === id);
3640
+ }
3641
+ /**
3642
+ * Signs the synthesized action of `operation` in place as `signer`, under the
3643
+ * id {@link synthesizedActionId} derives from the submitted one. A REDO's
3644
+ * rebuilt action has no timestamp, so it takes the submitted one; the
3645
+ * operation timestamp is set to the action's, which a peer requires of a v2
3646
+ * tuple.
3647
+ */
3648
+ async function signSynthesized(operation, submitted, target, signer, signal) {
3649
+ const synthesized = operation.action;
3650
+ const id = synthesizedActionId(submitted.id);
3651
+ const timestampUtcMs = operation.timestampUtcMs || synthesized.timestampUtcMs || submitted.timestampUtcMs;
3652
+ const action = {
3653
+ id,
3654
+ timestampUtcMs,
3655
+ type: synthesized.type,
3656
+ input: synthesized.input,
3657
+ scope: synthesized.scope
3658
+ };
3659
+ const signature = await signer.signAction(action, {
3660
+ documentId: target.documentId,
3661
+ branch: target.branch
3662
+ }, signal);
3663
+ operation.action = {
3664
+ ...action,
3665
+ context: { signer: {
3666
+ ...actionSignerIdentity(signer),
3667
+ signatures: [signature]
3668
+ } }
3669
+ };
3670
+ operation.id = deriveOperationId(target.documentId, target.scope, target.branch, id);
3671
+ operation.timestampUtcMs = timestampUtcMs;
3672
+ }
3673
+ //#endregion
3674
+ //#region src/executor/signature-admission.ts
3675
+ const DEFAULT_TRUST_TIMEOUT_MS = 1e4;
3676
+ /** Runs once per write, when this reactor first stores it; never on re-appends. */
3677
+ var SignatureAdmission = class {
3678
+ mode;
3679
+ logger;
3680
+ eventBus;
3681
+ policySource;
3682
+ trustPolicy;
3683
+ trustTimeoutMs;
3684
+ constructor(mode, logger, eventBus, policySource = "meta", trust = {
3685
+ authEnforcement: false,
3686
+ timeoutMs: DEFAULT_TRUST_TIMEOUT_MS
3687
+ }) {
3688
+ this.mode = mode;
3689
+ this.logger = logger;
3690
+ this.eventBus = eventBus;
3691
+ this.policySource = policySource;
3692
+ this.trustPolicy = admissionTrustPolicy(trust.signer ?? new PassthroughSigner(), trust.authEnforcement, trust.policy);
3693
+ this.trustTimeoutMs = trust.timeoutMs;
3694
+ }
3695
+ /** The first refusal of a mutation's submitted actions, when enforcing. */
3696
+ async admitMutation(job, stores, signal) {
3697
+ const { operationStore } = stores;
3698
+ const candidates = job.actions.map((action) => candidate(action, mutationStream(action, job)));
3699
+ const live = await this.liveOperationIds(candidates, operationStore, signal);
3700
+ const committed = await this.storedAsSubmitted(candidates, live, isRetry(job), operationStore, signal);
3701
+ await resolvePolicies(candidates, job.actions, (documentId, branch) => storedPolicy(stores, this.policySource, documentId, branch, signal));
3702
+ const submitted = /* @__PURE__ */ new Set();
3703
+ for (const entry of candidates) {
3704
+ if (committed.has(entry)) continue;
3705
+ const verdict = await this.verdict(entry, live, submitted, "mutation", signal);
3706
+ submitted.add(entry.opId);
3707
+ if (verdict.ok) continue;
3708
+ const refusal = this.record(job, entry, verdict, "mutation");
3709
+ if (refusal) return {
3710
+ kind: "refused",
3711
+ error: refusal
3712
+ };
3713
+ }
3714
+ return {
3715
+ kind: "admitted",
3716
+ committed: [...committed.values()]
3717
+ };
3718
+ }
3719
+ /** The incoming operations a load drops, empty unless enforcing. */
3720
+ async admitLoad(job, operations, stores, signal) {
3721
+ const { operationStore } = stores;
3722
+ const stream = {
3723
+ documentId: job.documentId,
3724
+ scope: job.scope,
3725
+ branch: job.branch
3726
+ };
3727
+ const candidates = operations.map((operation) => ({
3728
+ ...candidate(operation.action, stream),
3729
+ operation
3730
+ }));
3731
+ const live = await this.liveOperationIds(candidates, operationStore, signal);
3732
+ await resolvePolicies(candidates, job.operations.map((operation) => operation.action), (documentId, branch) => storedPolicy(stores, this.policySource, documentId, branch, signal));
3733
+ const dropped = /* @__PURE__ */ new Set();
3734
+ for (let i = 0; i < candidates.length; i++) {
3735
+ const verdict = await this.verdict(candidates[i], live, void 0, "load", signal);
3736
+ if (verdict.ok) continue;
3737
+ if (this.record(job, candidates[i], verdict, "load")) dropped.add(operations[i]);
3738
+ }
3739
+ return dropped;
3740
+ }
3741
+ async verdict(entry, live, submitted, path, signal) {
3742
+ const verdict = await verifyActionSignature(entry.action, {
3743
+ documentId: entry.stream.documentId,
3744
+ branch: entry.stream.branch,
3745
+ policy: entry.policy
3746
+ }, path, entry.operation);
3747
+ if (!verdict.ok) return verdict;
3748
+ if (isLive(entry, live) || submitted?.has(entry.opId)) return {
3749
+ ok: false,
3750
+ scheme: verdict.scheme,
3751
+ code: "DUPLICATE_ACTION",
3752
+ reason: `action ${entry.action.id} is already in the stream`
3753
+ };
3754
+ if (verdict.scheme === "unsigned") {
3755
+ const address = entry.action.context?.signer?.user?.address;
3756
+ return address ? {
3757
+ ok: false,
3758
+ scheme: "unsigned",
3759
+ code: "UNSIGNED_IDENTITY",
3760
+ reason: `action ${entry.action.id} is unsigned but claims to act as ${address}`
3761
+ } : verdict;
3762
+ }
3763
+ const signer = entry.action.context.signer;
3764
+ if (!await this.authorize(signer, entry.stream.documentId, signal)) return {
3765
+ ok: false,
3766
+ scheme: verdict.scheme,
3767
+ code: "SIGNER_UNAUTHORIZED",
3768
+ reason: `action ${entry.action.id}: key ${signer.app.key} may not sign as ${signer.user.address}`
3769
+ };
3770
+ return verdict;
3771
+ }
3772
+ /** Throws when the policy does, times out or the job is aborted. */
3773
+ async authorize(signer, documentId, signal) {
3774
+ signal?.throwIfAborted();
3775
+ let timer;
3776
+ let onAbort;
3777
+ const stop = new Promise((_, reject) => {
3778
+ timer = setTimeout(() => {
3779
+ reject(/* @__PURE__ */ new Error(`signature trust policy did not answer for ${signer.app.key} within ${this.trustTimeoutMs}ms`));
3780
+ }, this.trustTimeoutMs);
3781
+ if (signal) {
3782
+ onAbort = () => reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
3783
+ signal.addEventListener("abort", onAbort, { once: true });
3299
3784
  }
3300
- if (!isValid) throw new InvalidSignatureError(documentId, `Action ${action.id} signature verification returned false`);
3785
+ });
3786
+ try {
3787
+ return await Promise.race([this.trustPolicy.authorizeSigner(signer, signer.app.key, documentId), stop]);
3788
+ } finally {
3789
+ clearTimeout(timer);
3790
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
3301
3791
  }
3302
3792
  }
3303
- async verifyOperations(documentId, operations) {
3304
- if (!this.verifier) return;
3305
- for (let i = 0; i < operations.length; i++) {
3306
- const operation = operations[i];
3307
- const signer = operation.action.context?.signer;
3308
- if (!signer) continue;
3309
- if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} has signer but no signatures`);
3310
- const publicKey = signer.app.key;
3311
- let isValid;
3312
- try {
3313
- isValid = await this.verifier(operation, publicKey);
3314
- } catch (error) {
3315
- const errorMessage = error instanceof Error ? error.message : String(error);
3316
- throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} verification failed: ${errorMessage}`);
3793
+ /**
3794
+ * The live candidates stored exactly as submitted. A synthesized operation
3795
+ * holds another action, so it cannot be compared; only a retry, whose first
3796
+ * attempt this job was, takes it as committed.
3797
+ */
3798
+ async storedAsSubmitted(candidates, live, retry, operationStore, signal) {
3799
+ const committed = /* @__PURE__ */ new Map();
3800
+ const toCompare = [];
3801
+ for (const entry of candidates) if (entry.synthesizedOpId && live.has(entry.synthesizedOpId)) {
3802
+ if (retry) committed.set(entry, committedWrite(entry, entry.synthesizedOpId));
3803
+ } else if (live.has(entry.opId)) toCompare.push(entry);
3804
+ for (const [stream, entries] of byStream(toCompare)) {
3805
+ const stored = await operationStore.getOperationsByIds(stream.documentId, stream.scope, stream.branch, entries.map((entry) => entry.opId), signal);
3806
+ const byId = new Map(stored.map((operation) => [operation.id, operation]));
3807
+ for (const entry of entries) {
3808
+ const operation = byId.get(entry.opId);
3809
+ if (operation && sameContent(operation.action, entry.action)) committed.set(entry, committedWrite(entry, entry.opId));
3317
3810
  }
3318
- if (!isValid) throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} signature verification returned false`);
3319
3811
  }
3812
+ return committed;
3813
+ }
3814
+ async liveOperationIds(candidates, operationStore, signal) {
3815
+ const live = /* @__PURE__ */ new Set();
3816
+ for (const [stream, entries] of byStream(candidates)) {
3817
+ const found = await operationStore.findOperationIds(stream.documentId, stream.scope, stream.branch, entries.flatMap((entry) => entry.synthesizedOpId ? [entry.opId, entry.synthesizedOpId] : [entry.opId]), signal);
3818
+ for (const opId of found) live.add(opId);
3819
+ }
3820
+ return live;
3821
+ }
3822
+ /** Logs and counts a refusal; returns the error only when enforcing. */
3823
+ record(job, entry, refusal, path) {
3824
+ const enforced = this.mode === "enforce";
3825
+ const event = {
3826
+ jobId: job.id,
3827
+ documentId: entry.stream.documentId,
3828
+ scope: entry.stream.scope,
3829
+ branch: entry.stream.branch,
3830
+ actionId: entry.action.id,
3831
+ code: refusal.code,
3832
+ scheme: refusal.scheme,
3833
+ path,
3834
+ enforced,
3835
+ reason: refusal.reason
3836
+ };
3837
+ this.logger.warn(enforced ? "Signature refused: @Refusal" : "Signature refusal logged, write admitted: @Refusal", event);
3838
+ this.eventBus.emit(ReactorEventTypes.SIGNATURE_REFUSED, event).catch((error) => {
3839
+ this.logger.error("Failed to emit SIGNATURE_REFUSED: @Error", error);
3840
+ });
3841
+ return enforced ? new InvalidSignatureError(entry.stream.documentId, refusal.code, refusal.reason) : void 0;
3320
3842
  }
3321
3843
  };
3844
+ /**
3845
+ * A CREATE_DOCUMENT is verified under its own input. Anything else takes the
3846
+ * stored document's policy, or, for a document this job creates, that CREATE's.
3847
+ */
3848
+ async function resolvePolicies(candidates, jobActions, stored) {
3849
+ const resolved = /* @__PURE__ */ new Map();
3850
+ for (const entry of candidates) {
3851
+ if (entry.action.type === "CREATE_DOCUMENT") {
3852
+ entry.policy = createPolicy(entry.action);
3853
+ continue;
3854
+ }
3855
+ const { documentId, branch } = entry.stream;
3856
+ const key = `${documentId}\u0000${branch}`;
3857
+ let policy = resolved.get(key);
3858
+ if (policy === void 0) {
3859
+ policy = await stored(documentId, branch) ?? createdPolicy(jobActions, documentId) ?? "legacy";
3860
+ resolved.set(key, policy);
3861
+ }
3862
+ entry.policy = policy;
3863
+ }
3864
+ }
3865
+ async function storedPolicy(stores, source, documentId, branch, signal) {
3866
+ try {
3867
+ if (source === "write-cache") {
3868
+ const document = await stores.writeCache.getState(documentId, "document", branch, void 0, signal);
3869
+ return signaturePolicyOf(document.header);
3870
+ }
3871
+ const meta = await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal);
3872
+ return signaturePolicyOf(meta.protocolVersions);
3873
+ } catch (error) {
3874
+ if (DocumentNotFoundError.isError(error)) return;
3875
+ throw error;
3876
+ }
3877
+ }
3878
+ function createdPolicy(actions, documentId) {
3879
+ const create = actions.find((action) => action.type === "CREATE_DOCUMENT" && action.input?.documentId === documentId);
3880
+ return create ? createPolicy(create) : void 0;
3881
+ }
3882
+ function createPolicy(action) {
3883
+ return signaturePolicyOf(action.input);
3884
+ }
3885
+ function candidate(action, stream) {
3886
+ const opId = (actionId) => deriveOperationId(stream.documentId, stream.scope, stream.branch, actionId);
3887
+ return {
3888
+ action,
3889
+ stream,
3890
+ opId: opId(action.id),
3891
+ synthesizedOpId: SYNTHESIZING_TYPES.has(action.type) ? opId(synthesizedActionId(action.id)) : void 0
3892
+ };
3893
+ }
3894
+ function isLive(entry, live) {
3895
+ return live.has(entry.opId) || entry.synthesizedOpId !== void 0 && live.has(entry.synthesizedOpId);
3896
+ }
3897
+ function mutationStream(action, job) {
3898
+ return {
3899
+ ...actionSigningTarget(action, job.documentId, job.branch),
3900
+ scope: job.scope
3901
+ };
3902
+ }
3903
+ function byStream(candidates) {
3904
+ const groups = /* @__PURE__ */ new Map();
3905
+ for (const entry of candidates) {
3906
+ const key = `${entry.stream.documentId}\u0000${entry.stream.scope}\u0000${entry.stream.branch}`;
3907
+ const group = groups.get(key) ?? [entry.stream, []];
3908
+ group[1].push(entry);
3909
+ groups.set(key, group);
3910
+ }
3911
+ return [...groups.values()];
3912
+ }
3913
+ function isRetry(job) {
3914
+ return job.errorHistory.length > 0 || (job.retryCount ?? 0) > 0;
3915
+ }
3916
+ function committedWrite(entry, opId) {
3917
+ return {
3918
+ ...entry.stream,
3919
+ actionId: entry.action.id,
3920
+ opId
3921
+ };
3922
+ }
3923
+ function sameContent(stored, submitted) {
3924
+ try {
3925
+ return canonicalJson(stored) === canonicalJson(submitted);
3926
+ } catch {
3927
+ return false;
3928
+ }
3929
+ }
3322
3930
  //#endregion
3323
3931
  //#region src/executor/simple-job-executor.ts
3324
3932
  const MAX_SKIP_THRESHOLD = 1e3;
@@ -3336,6 +3944,7 @@ function isValidISOTimestamp(value) {
3336
3944
  * every test expect a failed job to look like.
3337
3945
  */
3338
3946
  var JobRollbackSignal = class extends Error {
3947
+ result;
3339
3948
  constructor(result) {
3340
3949
  super("job rolled back");
3341
3950
  this.result = result;
@@ -3343,13 +3952,27 @@ var JobRollbackSignal = class extends Error {
3343
3952
  }
3344
3953
  };
3345
3954
  var SimpleJobExecutor = class {
3955
+ logger;
3956
+ registry;
3957
+ operationStore;
3958
+ eventBus;
3959
+ writeCache;
3960
+ operationIndex;
3961
+ documentMetaCache;
3962
+ collectionMembershipCache;
3963
+ driveContainerTypes;
3346
3964
  config;
3347
3965
  featureFlags;
3348
3966
  decisionModel;
3349
- signatureVerifierModule;
3967
+ signatureAdmission;
3350
3968
  documentActionHandler;
3351
3969
  executionScope;
3352
- constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, signatureVerifier, executionScope) {
3970
+ signer;
3971
+ /**
3972
+ * `signer` signs the operations the reducer synthesizes; unsigned if omitted.
3973
+ * `trustPolicy` decides which keys may sign as which users at admission.
3974
+ */
3975
+ constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, executionScope, signer, trustPolicy) {
3353
3976
  this.logger = logger;
3354
3977
  this.registry = registry;
3355
3978
  this.operationStore = operationStore;
@@ -3359,6 +3982,7 @@ var SimpleJobExecutor = class {
3359
3982
  this.documentMetaCache = documentMetaCache;
3360
3983
  this.collectionMembershipCache = collectionMembershipCache;
3361
3984
  this.driveContainerTypes = driveContainerTypes;
3985
+ this.signer = signer ?? new PassthroughSigner();
3362
3986
  this.config = {
3363
3987
  featureFlags: config.featureFlags ?? {},
3364
3988
  maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,
@@ -3368,11 +3992,17 @@ var SimpleJobExecutor = class {
3368
3992
  retryBaseDelayMs: config.retryBaseDelayMs ?? 100,
3369
3993
  retryMaxDelayMs: config.retryMaxDelayMs ?? 5e3,
3370
3994
  yieldDeadlineMs: config.yieldDeadlineMs ?? 50,
3371
- batchApplies: config.batchApplies ?? true
3995
+ batchApplies: config.batchApplies ?? true,
3996
+ signatureVerification: config.signatureVerification ?? "enforce"
3372
3997
  };
3373
3998
  this.featureFlags = resolveFeatureFlags(config.featureFlags);
3374
3999
  this.decisionModel = selectDecisionModel(this.featureFlags, registry);
3375
- this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
4000
+ this.signatureAdmission = new SignatureAdmission(this.config.signatureVerification, logger, eventBus, this.featureFlags.documentDecisions ? "write-cache" : "meta", {
4001
+ signer: this.signer,
4002
+ authEnforcement: this.featureFlags.authEnforcement,
4003
+ policy: trustPolicy,
4004
+ timeoutMs: Math.min(DEFAULT_TRUST_TIMEOUT_MS, this.config.jobTimeoutMs / 2)
4005
+ });
3376
4006
  this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags, this.decisionModel);
3377
4007
  this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
3378
4008
  }
@@ -3502,10 +4132,29 @@ var SimpleJobExecutor = class {
3502
4132
  pendingEvent
3503
4133
  };
3504
4134
  }
3505
- const positioned = await this.positionByTimestamp(job, stores, signal);
4135
+ let admission;
4136
+ try {
4137
+ admission = await this.signatureAdmission.admitMutation(job, stores, signal);
4138
+ } catch (error) {
4139
+ return { result: buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime) };
4140
+ }
4141
+ if (admission.kind === "refused") return { result: buildErrorResult(job, admission.error, startTime) };
4142
+ let committed;
4143
+ try {
4144
+ committed = await this.reloadCommitted(admission.committed, stores, signal);
4145
+ } catch (error) {
4146
+ return { result: buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime) };
4147
+ }
4148
+ const committedIds = new Set(admission.committed.map((write) => write.actionId));
4149
+ const fresh = committedIds.size === 0 ? job : {
4150
+ ...job,
4151
+ actions: job.actions.filter((action) => !committedIds.has(action.id))
4152
+ };
4153
+ if (fresh.actions.length === 0) return this.committedOutcome(job, committed, [], [], stores, startTime);
4154
+ const positioned = await this.positionByTimestamp(fresh, stores, signal);
3506
4155
  if (positioned.error) return { result: buildErrorResult(job, positioned.error, startTime) };
3507
4156
  const executing = {
3508
- job,
4157
+ job: fresh,
3509
4158
  startTime,
3510
4159
  indexTxn,
3511
4160
  stores,
@@ -3535,12 +4184,21 @@ var SimpleJobExecutor = class {
3535
4184
  } };
3536
4185
  const ordinals = await stores.operationIndex.commit(indexTxn, signal);
3537
4186
  postCommitMembershipInvalidations.push(...indexTxn.getMembershipInvalidations());
3538
- if (actionResult.operationsWithContext.length > 0) {
3539
- for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
3540
- const collectionMemberships = await this.getCollectionMembershipsForOperations(actionResult.operationsWithContext, stores);
4187
+ for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
4188
+ return this.committedOutcome(job, committed, actionResult.operationsWithContext, actionResult.generatedOperations, stores, startTime);
4189
+ }
4190
+ /**
4191
+ * A successful mutation's result and the write-ready event it owes, carrying
4192
+ * what earlier attempts committed ahead of what this one wrote.
4193
+ */
4194
+ async committedOutcome(job, reloaded, written, generated, stores, startTime) {
4195
+ const operationsWithContext = [...reloaded, ...written];
4196
+ let pendingEvent;
4197
+ if (operationsWithContext.length > 0) {
4198
+ const collectionMemberships = await this.getCollectionMembershipsForOperations(operationsWithContext, stores);
3541
4199
  pendingEvent = {
3542
4200
  jobId: job.id,
3543
- operations: actionResult.operationsWithContext,
4201
+ operations: operationsWithContext,
3544
4202
  jobMeta: job.meta,
3545
4203
  submittedActionIds: submittedActionIds(job),
3546
4204
  collectionMemberships
@@ -3550,14 +4208,62 @@ var SimpleJobExecutor = class {
3550
4208
  result: {
3551
4209
  job,
3552
4210
  success: true,
3553
- operations: actionResult.generatedOperations,
3554
- operationsWithContext: actionResult.operationsWithContext,
4211
+ operations: [...reloaded.map((entry) => entry.operation), ...generated],
4212
+ operationsWithContext,
3555
4213
  duration: Date.now() - startTime
3556
4214
  },
3557
4215
  pendingEvent
3558
4216
  };
3559
4217
  }
3560
4218
  /**
4219
+ * The stored operations of writes an earlier attempt committed, as a fresh
4220
+ * write would report them. State is rebuilt from the write cache, and left
4221
+ * out when it cannot be.
4222
+ */
4223
+ async reloadCommitted(writes, stores, signal) {
4224
+ const streams = /* @__PURE__ */ new Map();
4225
+ for (const write of writes) {
4226
+ const key = `${write.documentId}\u0000${write.scope}\u0000${write.branch}`;
4227
+ streams.set(key, [...streams.get(key) ?? [], write]);
4228
+ }
4229
+ const reloaded = [];
4230
+ for (const group of streams.values()) {
4231
+ const { documentId, scope, branch } = group[0];
4232
+ const opIds = [...new Set(group.map((write) => write.opId))];
4233
+ const operations = await stores.operationStore.getOperationsByIds(documentId, scope, branch, opIds, signal);
4234
+ const ordinals = await stores.operationIndex.getOrdinalsByOpIds(documentId, scope, branch, opIds, signal);
4235
+ const { documentType } = await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal);
4236
+ for (const operation of operations.sort((a, b) => a.index - b.index)) reloaded.push({
4237
+ operation,
4238
+ context: {
4239
+ documentId,
4240
+ scope,
4241
+ branch,
4242
+ documentType,
4243
+ resultingState: await this.rebuiltState(stores, {
4244
+ documentId,
4245
+ scope,
4246
+ branch
4247
+ }, operation, signal),
4248
+ ordinal: ordinals.get(operation.id) ?? 0
4249
+ }
4250
+ });
4251
+ }
4252
+ return reloaded;
4253
+ }
4254
+ async rebuiltState(stores, stream, operation, signal) {
4255
+ try {
4256
+ const document = await stores.writeCache.getState(stream.documentId, stream.scope, stream.branch, operation.index, signal);
4257
+ return JSON.stringify({
4258
+ ...document.state,
4259
+ header: document.header
4260
+ });
4261
+ } catch (error) {
4262
+ this.logger.warn("No state for committed operation @OperationId in @Stream: @Error", operation.id, stream, error);
4263
+ return;
4264
+ }
4265
+ }
4266
+ /**
3561
4267
  * Drops the cached state of every stream a job wrote, after its transaction
3562
4268
  * did not commit.
3563
4269
  *
@@ -3586,16 +4292,6 @@ var SimpleJobExecutor = class {
3586
4292
  const actions = writes.map((write) => write.action);
3587
4293
  const generatedOperations = [];
3588
4294
  const operationsWithContext = [];
3589
- try {
3590
- await this.signatureVerifierModule.verifyActions(job.documentId, job.branch, actions);
3591
- } catch (error) {
3592
- return {
3593
- success: false,
3594
- generatedOperations,
3595
- operationsWithContext,
3596
- error: error instanceof Error ? error : new Error(String(error))
3597
- };
3598
- }
3599
4295
  for (const action of actions) if (action.timestampUtcMs && !isValidISOTimestamp(action.timestampUtcMs)) return {
3600
4296
  success: false,
3601
4297
  generatedOperations,
@@ -3693,7 +4389,7 @@ var SimpleJobExecutor = class {
3693
4389
  if (docMeta.state.isDeleted) return buildErrorResult(job, new DocumentDeletedError(job.documentId, docMeta.state.deletedAtUtcIso), startTime);
3694
4390
  documentVersion = docMeta.state.version;
3695
4391
  }
3696
- if (isUndoRedo(action) || action.type === "PRUNE" || skip > 0) stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
4392
+ if (isUndoRedo(action) || skip > 0) stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
3697
4393
  let document;
3698
4394
  if (baseDocument !== void 0) document = baseDocument;
3699
4395
  else try {
@@ -3753,7 +4449,7 @@ var SimpleJobExecutor = class {
3753
4449
  branch: job.branch,
3754
4450
  protocolVersion
3755
4451
  };
3756
- updatedDocument = module.reducer(document, action, void 0, reducerOptions);
4452
+ updatedDocument = withProtocolVersions(module.reducer(document, action, void 0, reducerOptions), document.header.protocolVersions);
3757
4453
  } catch (error) {
3758
4454
  const contextMessage = `Failed to apply action to document:\n Action type: ${action.type}\n Document ID: ${job.documentId}\n Document type: ${document.header.documentType}\n Scope: ${job.scope}\n Original error: ${error instanceof Error ? error.message : String(error)}`;
3759
4455
  const enhancedError = new Error(contextMessage);
@@ -3765,6 +4461,15 @@ var SimpleJobExecutor = class {
3765
4461
  if (operations.length === 0) return buildErrorResult(job, /* @__PURE__ */ new Error("No operation generated from action"), startTime);
3766
4462
  const newOperation = operations[operations.length - 1];
3767
4463
  if (!isUndoRedo(action)) newOperation.skip = skip;
4464
+ if (deniedReason === void 0 && sourceOperation === void 0 && !executing.replayingAcceptedHistory && isSynthesized(action, newOperation, document.operations[scope] ?? [])) try {
4465
+ await signSynthesized(newOperation, action, {
4466
+ documentId: job.documentId,
4467
+ scope,
4468
+ branch: job.branch
4469
+ }, this.signer, signal);
4470
+ } catch (error) {
4471
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
4472
+ }
3768
4473
  const resultingState = JSON.stringify({
3769
4474
  ...updatedDocument.state,
3770
4475
  header: updatedDocument.header
@@ -3865,7 +4570,7 @@ var SimpleJobExecutor = class {
3865
4570
  * apply and its own reasons for it.
3866
4571
  * - A positional or replayed run carries skips and re-appended operations,
3867
4572
  * whose indices are not a simple ascending run from the head.
3868
- * - UNDO, REDO, PRUNE and NOOP-with-skip each invalidate the write cache to
4573
+ * - UNDO, REDO and NOOP-with-skip each invalidate the write cache to
3869
4574
  * force a full-history rebuild, so they cannot be reduced against state
3870
4575
  * threaded from the write before them.
3871
4576
  * - The auth scope decides later writes against the policy earlier ones
@@ -3883,7 +4588,7 @@ var SimpleJobExecutor = class {
3883
4588
  if (scope === "auth" || scope === "document") return false;
3884
4589
  return writes.every((write) => {
3885
4590
  const type = write.action.type;
3886
- return write.skip === 0 && write.deniedReason === void 0 && write.sourceOperation === void 0 && !DOCUMENT_SCOPE_ACTIONS.has(type) && !isUndoRedo(write.action) && type !== "PRUNE" && type !== "NOOP";
4591
+ return write.skip === 0 && write.deniedReason === void 0 && write.sourceOperation === void 0 && !DOCUMENT_SCOPE_ACTIONS.has(type) && !isUndoRedo(write.action) && type !== "NOOP";
3887
4592
  });
3888
4593
  }
3889
4594
  /**
@@ -4270,36 +4975,32 @@ var SimpleJobExecutor = class {
4270
4975
  error: new InvalidOperationTimestampError(job.documentId, scope, operation.timestampUtcMs, `operation (index: ${operation.index})`),
4271
4976
  duration: Date.now() - startTime
4272
4977
  };
4273
- let minIncomingIndex = Number.POSITIVE_INFINITY;
4274
- let minIncomingTimestamp = job.operations[0]?.timestampUtcMs || "";
4275
- for (const operation of job.operations) {
4276
- minIncomingIndex = Math.min(minIncomingIndex, operation.index);
4277
- const ts = operation.timestampUtcMs || "";
4278
- if (Date.parse(ts) < Date.parse(minIncomingTimestamp)) minIncomingTimestamp = ts;
4279
- }
4280
- let conflictingOps;
4281
- try {
4282
- conflictingOps = (await stores.operationStore.getConflicting(job.documentId, scope, job.branch, minIncomingTimestamp, void 0, signal)).results;
4283
- } catch {
4284
- conflictingOps = [];
4285
- }
4286
- let allOpsFromMinConflictingIndex = conflictingOps;
4287
- if (conflictingOps.length > 0) {
4288
- const minConflictingIndex = Math.min(...conflictingOps.map((op) => op.index));
4978
+ const dropped = /* @__PURE__ */ new Set();
4979
+ const admitted = /* @__PURE__ */ new Set();
4980
+ let selection = await this.selectLoadWrites(job, job.operations, stores, signal);
4981
+ for (;;) {
4982
+ const unadmitted = selection.incomingOpsToApply.filter((operation) => !admitted.has(operation));
4983
+ if (unadmitted.length === 0) break;
4984
+ let refused;
4289
4985
  try {
4290
- allOpsFromMinConflictingIndex = (await stores.operationStore.getSince(job.documentId, scope, job.branch, minConflictingIndex - 1, void 0, void 0, signal)).results;
4291
- } catch {
4292
- allOpsFromMinConflictingIndex = conflictingOps;
4986
+ refused = await this.signatureAdmission.admitLoad(job, unadmitted, stores, signal);
4987
+ } catch (error) {
4988
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
4293
4989
  }
4990
+ for (const operation of unadmitted) if (refused.has(operation)) dropped.add(operation);
4991
+ else admitted.add(operation);
4992
+ if (refused.size === 0) break;
4993
+ const remaining = job.operations.filter((operation) => !dropped.has(operation));
4994
+ if (remaining.length === 0) return {
4995
+ job,
4996
+ success: true,
4997
+ operations: [],
4998
+ operationsWithContext: [],
4999
+ duration: Date.now() - startTime
5000
+ };
5001
+ selection = await this.selectLoadWrites(job, remaining, stores, signal);
4294
5002
  }
4295
- const incomingActionIds = new Set(job.operations.map((op) => op.action.id));
4296
- const nonSupersededOps = conflictingOps.filter((op) => {
4297
- if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
4298
- for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
4299
- if (laterOp.index - laterOp.skip <= op.index) return false;
4300
- }
4301
- return true;
4302
- });
5003
+ const { nonSupersededOps, allOpsFromMinConflictingIndex, incomingOpsToApply } = selection;
4303
5004
  const existingOpsToReshuffle = monotonicAuthStream ? [] : nonSupersededOps.filter((operation) => !isGenesisOperation(operation));
4304
5005
  const actionIdCounts = /* @__PURE__ */ new Map();
4305
5006
  for (const operation of allOpsFromMinConflictingIndex) actionIdCounts.set(operation.action.id, (actionIdCounts.get(operation.action.id) ?? 0) + 1);
@@ -4320,14 +5021,6 @@ var SimpleJobExecutor = class {
4320
5021
  const logicalSkip = latestRevision - minLogicalIndex;
4321
5022
  if (logicalSkip > skipCount) skipCount = logicalSkip;
4322
5023
  }
4323
- const existingActionIds = new Set(nonSupersededOps.map((op) => op.action.id));
4324
- const seenIncomingActionIds = /* @__PURE__ */ new Set();
4325
- const incomingOpsToApply = job.operations.filter((op) => {
4326
- if (existingActionIds.has(op.action.id)) return false;
4327
- if (seenIncomingActionIds.has(op.action.id)) return false;
4328
- seenIncomingActionIds.add(op.action.id);
4329
- return true;
4330
- });
4331
5024
  if (incomingOpsToApply.length === 0) return {
4332
5025
  job,
4333
5026
  success: true,
@@ -4407,6 +5100,53 @@ var SimpleJobExecutor = class {
4407
5100
  duration: Date.now() - startTime
4408
5101
  };
4409
5102
  }
5103
+ /** The conflicting window a load opens, and which of its operations are new. */
5104
+ async selectLoadWrites(job, operations, stores, signal) {
5105
+ const scope = job.scope;
5106
+ let minIncomingIndex = Number.POSITIVE_INFINITY;
5107
+ let minIncomingTimestamp = operations[0]?.timestampUtcMs || "";
5108
+ for (const operation of operations) {
5109
+ minIncomingIndex = Math.min(minIncomingIndex, operation.index);
5110
+ const ts = operation.timestampUtcMs || "";
5111
+ if (Date.parse(ts) < Date.parse(minIncomingTimestamp)) minIncomingTimestamp = ts;
5112
+ }
5113
+ let conflictingOps;
5114
+ try {
5115
+ conflictingOps = (await stores.operationStore.getConflicting(job.documentId, scope, job.branch, minIncomingTimestamp, void 0, signal)).results;
5116
+ } catch {
5117
+ conflictingOps = [];
5118
+ }
5119
+ let allOpsFromMinConflictingIndex = conflictingOps;
5120
+ if (conflictingOps.length > 0) {
5121
+ const minConflictingIndex = Math.min(...conflictingOps.map((op) => op.index));
5122
+ try {
5123
+ allOpsFromMinConflictingIndex = (await stores.operationStore.getSince(job.documentId, scope, job.branch, minConflictingIndex - 1, void 0, void 0, signal)).results;
5124
+ } catch {
5125
+ allOpsFromMinConflictingIndex = conflictingOps;
5126
+ }
5127
+ }
5128
+ const incomingActionIds = new Set(operations.map((op) => op.action.id));
5129
+ const nonSupersededOps = conflictingOps.filter((op) => {
5130
+ if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
5131
+ for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
5132
+ if (laterOp.index - laterOp.skip <= op.index) return false;
5133
+ }
5134
+ return true;
5135
+ });
5136
+ const existingActionIds = new Set(nonSupersededOps.map((op) => op.action.id));
5137
+ const seenIncomingActionIds = /* @__PURE__ */ new Set();
5138
+ const incomingOpsToApply = operations.filter((op) => {
5139
+ if (existingActionIds.has(op.action.id)) return false;
5140
+ if (seenIncomingActionIds.has(op.action.id)) return false;
5141
+ seenIncomingActionIds.add(op.action.id);
5142
+ return true;
5143
+ });
5144
+ return {
5145
+ nonSupersededOps,
5146
+ allOpsFromMinConflictingIndex,
5147
+ incomingOpsToApply
5148
+ };
5149
+ }
4410
5150
  accumulateResultOrReturnError(result, generatedOperations, operationsWithContext) {
4411
5151
  if (!result.success) return result;
4412
5152
  if (result.operations && result.operations.length > 0) generatedOperations.push(...result.operations);
@@ -4553,6 +5293,7 @@ var DocumentModelRegistry = class {
4553
5293
  //#endregion
4554
5294
  //#region src/storage/kysely/keyframe-store.ts
4555
5295
  var KyselyKeyframeStore = class KyselyKeyframeStore {
5296
+ db;
4556
5297
  trx;
4557
5298
  constructor(db) {
4558
5299
  this.db = db;
@@ -4637,6 +5378,11 @@ function paginateRows(rows, paging, cursorOf, toItem, refetch) {
4637
5378
  //#endregion
4638
5379
  //#region src/storage/txn.ts
4639
5380
  var AtomicTransaction = class {
5381
+ documentId;
5382
+ documentType;
5383
+ scope;
5384
+ branch;
5385
+ baseRevision;
4640
5386
  operations = [];
4641
5387
  constructor(documentId, documentType, scope, branch, baseRevision) {
4642
5388
  this.documentId = documentId;
@@ -4670,6 +5416,11 @@ var AtomicTransaction = class {
4670
5416
  //#endregion
4671
5417
  //#region src/storage/kysely/store.ts
4672
5418
  var _UniqueConstraintContext = class extends Error {
5419
+ documentId;
5420
+ scope;
5421
+ branch;
5422
+ revision;
5423
+ stagedOps;
4673
5424
  constructor(documentId, scope, branch, revision, stagedOps) {
4674
5425
  super("unique constraint");
4675
5426
  this.documentId = documentId;
@@ -4681,6 +5432,7 @@ var _UniqueConstraintContext = class extends Error {
4681
5432
  }
4682
5433
  };
4683
5434
  var KyselyOperationStore = class KyselyOperationStore {
5435
+ db;
4684
5436
  trx;
4685
5437
  constructor(db) {
4686
5438
  this.db = db;
@@ -4780,12 +5532,13 @@ var KyselyOperationStore = class KyselyOperationStore {
4780
5532
  * otherwise read a snapshot taken before the locks were held.
4781
5533
  */
4782
5534
  async acquireStreamLocks(trx, documentId, scope, branch, condition) {
4783
- const keys = new Set([`${documentId}:${scope}:${branch}`]);
5535
+ const keys = /* @__PURE__ */ new Set([`${documentId}:${scope}:${branch}`]);
4784
5536
  for (const stream of condition.streams) keys.add(`${stream.documentId}:${stream.scope}:${stream.branch}`);
5537
+ const sortedKeys = sql.join([...keys].sort());
4785
5538
  await sql`
4786
5539
  with ordered as materialized (
4787
5540
  select key
4788
- from unnest(array[${sql.join([...keys].sort())}]::text[]) with ordinality as t(key, ord)
5541
+ from unnest(array[${sortedKeys}]::text[]) with ordinality as t(key, ord)
4789
5542
  order by ord
4790
5543
  )
4791
5544
  select pg_advisory_xact_lock(hashtext(key)) from ordered
@@ -4939,6 +5692,20 @@ var KyselyOperationStore = class KyselyOperationStore {
4939
5692
  const latest = await this.queryExecutor.selectFrom("Operation").select((eb) => eb.fn.max("timestampUtcMs").as("latestTimestamp")).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).executeTakeFirst();
4940
5693
  return latest?.latestTimestamp ? new Date(latest.latestTimestamp).toISOString() : void 0;
4941
5694
  }
5695
+ async findOperationIds(documentId, scope, branch, opIds, signal) {
5696
+ throwIfAborted(signal);
5697
+ if (opIds.length === 0) return /* @__PURE__ */ new Set();
5698
+ const rows = await this.queryExecutor.selectFrom("Operation").select("opId").distinct().where("opId", "in", opIds).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).execute();
5699
+ return new Set(rows.map((row) => row.opId));
5700
+ }
5701
+ async getOperationsByIds(documentId, scope, branch, opIds, signal) {
5702
+ throwIfAborted(signal);
5703
+ if (opIds.length === 0) return [];
5704
+ const rows = await this.queryExecutor.selectFrom("Operation").selectAll().where("opId", "in", opIds).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).orderBy("index", "asc").execute();
5705
+ const latest = /* @__PURE__ */ new Map();
5706
+ for (const row of rows) latest.set(row.opId, this.rowToOperation(row));
5707
+ return [...latest.values()];
5708
+ }
4942
5709
  rowToOperation(row) {
4943
5710
  return {
4944
5711
  index: row.index,
@@ -5410,8 +6177,148 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
5410
6177
  }
5411
6178
  //#endregion
5412
6179
  //#region src/core/drive-container-types.ts
5413
- const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
6180
+ const DEFAULT_DRIVE_CONTAINER_TYPES = /* @__PURE__ */ new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
6181
+ //#endregion
6182
+ //#region src/executor/worker/error-info.ts
6183
+ const MAX_STACK_LENGTH = 8192;
6184
+ const MAX_CAUSE_DEPTH = 8;
6185
+ function toErrorInfo(err, depth = 0) {
6186
+ if (depth > MAX_CAUSE_DEPTH) return {
6187
+ name: "Error",
6188
+ message: "cause chain truncated"
6189
+ };
6190
+ if (err instanceof Error) {
6191
+ const info = {
6192
+ name: err.name || "Error",
6193
+ message: err.message
6194
+ };
6195
+ if (typeof err.stack === "string") info.stack = err.stack.length > MAX_STACK_LENGTH ? err.stack.slice(0, MAX_STACK_LENGTH) : err.stack;
6196
+ if (err.cause !== void 0) info.cause = toErrorInfo(err.cause, depth + 1);
6197
+ return info;
6198
+ }
6199
+ if (typeof err === "string") return {
6200
+ name: "Error",
6201
+ message: err
6202
+ };
6203
+ if (err && typeof err === "object") {
6204
+ const record = err;
6205
+ return {
6206
+ name: typeof record.name === "string" ? record.name : "Error",
6207
+ message: typeof record.message === "string" ? record.message : safeStringify(err)
6208
+ };
6209
+ }
6210
+ return {
6211
+ name: "Error",
6212
+ message: safeStringify(err)
6213
+ };
6214
+ }
6215
+ function safeStringify(value) {
6216
+ if (value === null || value === void 0) return String(value);
6217
+ if (typeof value === "string") return value;
6218
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
6219
+ try {
6220
+ return JSON.stringify(value);
6221
+ } catch {
6222
+ return "[unserializable]";
6223
+ }
6224
+ }
6225
+ function fromErrorInfo(info) {
6226
+ const err = new Error(info.message);
6227
+ Object.defineProperty(err, "name", {
6228
+ value: info.name,
6229
+ configurable: true,
6230
+ writable: true
6231
+ });
6232
+ if (info.stack !== void 0) err.stack = info.stack;
6233
+ if (info.cause !== void 0) Object.defineProperty(err, "cause", {
6234
+ value: fromErrorInfo(info.cause),
6235
+ configurable: true,
6236
+ writable: true
6237
+ });
6238
+ return err;
6239
+ }
6240
+ //#endregion
6241
+ //#region src/executor/worker/sanitize.ts
6242
+ const MAX_DEPTH = 8;
6243
+ function errorToInfo(err) {
6244
+ return toErrorInfo(err);
6245
+ }
6246
+ function sanitizeArg(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
6247
+ if (depth > MAX_DEPTH) return "[Truncated]";
6248
+ if (value === null || value === void 0) return null;
6249
+ if (typeof value === "boolean") return value;
6250
+ if (typeof value === "number") return value;
6251
+ if (typeof value === "string") return value;
6252
+ if (typeof value === "bigint") return value.toString();
6253
+ if (typeof value === "function" || typeof value === "symbol") return null;
6254
+ if (value instanceof Error) return toErrorInfo(value);
6255
+ if (value instanceof Date) return value.toISOString();
6256
+ if (value instanceof Map) {
6257
+ const obj = {};
6258
+ for (const [k, v] of value.entries()) obj[String(k)] = sanitizeArg(v, depth + 1, seen);
6259
+ return obj;
6260
+ }
6261
+ if (value instanceof Set) {
6262
+ const arr = [];
6263
+ for (const v of value.values()) arr.push(sanitizeArg(v, depth + 1, seen));
6264
+ return arr;
6265
+ }
6266
+ if (Array.isArray(value)) {
6267
+ if (seen.has(value)) return "[Circular]";
6268
+ seen.add(value);
6269
+ const result = value.map((v) => sanitizeArg(v, depth + 1, seen));
6270
+ seen.delete(value);
6271
+ return result;
6272
+ }
6273
+ if (typeof value === "object") {
6274
+ const proto = Object.getPrototypeOf(value);
6275
+ if (proto !== Object.prototype && proto !== null) return { __nonClonable: value.constructor?.name || "Unknown" };
6276
+ if (seen.has(value)) return "[Circular]";
6277
+ seen.add(value);
6278
+ const result = {};
6279
+ for (const [k, v] of Object.entries(value)) result[k] = sanitizeArg(v, depth + 1, seen);
6280
+ seen.delete(value);
6281
+ return result;
6282
+ }
6283
+ return null;
6284
+ }
6285
+ //#endregion
6286
+ //#region src/executor/worker/forwarding-logger.ts
6287
+ function createForwardingLogger(post, level = "info", tags = []) {
6288
+ function safePost(msg) {
6289
+ try {
6290
+ post(msg);
6291
+ } catch {
6292
+ try {
6293
+ process.stderr.write(`[forwarding-logger] postMessage failed: ${msg.level} ${msg.message}\n`);
6294
+ } catch {}
6295
+ }
6296
+ }
6297
+ function emit(wireLevel, message, args) {
6298
+ const sanitizedArgs = args.map((a) => sanitizeArg(a));
6299
+ safePost({
6300
+ type: "log",
6301
+ level: wireLevel,
6302
+ message,
6303
+ args: tags.length > 0 ? [sanitizeArg({ tags }), ...sanitizedArgs] : sanitizedArgs,
6304
+ timestamp: Date.now()
6305
+ });
6306
+ }
6307
+ return {
6308
+ level,
6309
+ verbose: (message, ...args) => emit("debug", message, args),
6310
+ debug: (message, ...args) => emit("debug", message, args),
6311
+ info: (message, ...args) => emit("info", message, args),
6312
+ warn: (message, ...args) => emit("warn", message, args),
6313
+ error: (message, ...args) => emit("error", message, args),
6314
+ errorHandler: ((...data) => {
6315
+ const first = data[0];
6316
+ emit("error", typeof first === "string" ? first : "", data.slice(1));
6317
+ }),
6318
+ child: (childTags) => createForwardingLogger(post, level, [...tags, ...childTags])
6319
+ };
6320
+ }
5414
6321
  //#endregion
5415
- export { parsePagingOptions as $, targetDocumentId as A, DuplicateOperationError as B, InvalidModuleError as C, createEmptyConsistencyToken as D, createConsistencyToken as E, buildDecisionModel as F, AuthorizationDeniedError as G, RevisionMismatchError as H, APPEND_CONDITION_FAILED_PREFIX as I, ExcessiveReshuffleError as J, DocumentDeletedError as K, AppendConditionFailedError as L, selectDecisionModel as M, documentDecisionModel as N, submittedActionIds as O, authDecisionModel as P, matchesScope as Q, DocumentAlreadyExistsError as R, DuplicateModuleError as S, GATED_DOCUMENT_ACTIONS as T, AuthEnforcementDisabledError as U, OptimisticLockError as V, AuthTimestampNotMonotonicError as W, RelationshipNotFoundError as X, InvalidOperationTimestampError as Y, UpgradePreconditionFailedError as Z, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, JobExecutorEventTypes as b, KyselyKeyframeStore as c, DriveCollectionId as d, throwIfAborted as et, KyselyExecutionScope as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, decideAtHead as j, summarizeSubmittedActions as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, __exportAll as nt, instrumentPgPool as o, resolveFeatureFlags as p, DocumentNotFoundError as q, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, yieldToMain as tt, SimpleJobExecutor as u, CollectionMembershipCache as v, ModuleNotFoundError as w, DuplicateManifestError as x, DEFAULT_DEFERRED_JOB_TTL_MS as y, DocumentExistence as z };
6322
+ export { DocumentNotFoundError as $, ModuleNotFoundError as A, authDecisionModel as B, DocumentMetaCache as C, DuplicateManifestError as D, JobExecutorEventTypes as E, summarizeSubmittedActions as F, DocumentExistence as G, APPEND_CONDITION_FAILED_PREFIX as H, targetDocumentId as I, RevisionMismatchError as J, DuplicateOperationError as K, decideAtHead as L, createConsistencyToken as M, createEmptyConsistencyToken as N, DuplicateModuleError as O, submittedActionIds as P, DocumentDeletedError as Q, selectDecisionModel as R, KyselyOperationIndex as S, DEFAULT_DEFERRED_JOB_TTL_MS as T, AppendConditionFailedError as U, buildDecisionModel as V, DocumentAlreadyExistsError as W, AuthTimestampNotMonotonicError as X, AuthEnforcementDisabledError as Y, AuthorizationDeniedError as Z, DriveCollectionId as _, DEFAULT_DRIVE_CONTAINER_TYPES as a, EventBusAggregateError as at, EventBus as b, runMigrations as c, parsePagingOptions as ct, KyselyOperationStore as d, __exportAll as dt, ExcessiveReshuffleError as et, KyselyKeyframeStore as f, PassthroughSigner as g, verifyActionSignature as h, fromErrorInfo as i, UpgradePreconditionFailedError as it, GATED_DOCUMENT_ACTIONS as j, InvalidModuleError as k, createForwardingPoolInstrumentation as l, throwIfAborted as lt, SimpleJobExecutor as m, errorToInfo as n, InvalidSignatureError as nt, REACTOR_SCHEMA as o, ReactorEventTypes as ot, DocumentModelRegistry as p, OptimisticLockError as q, sanitizeArg as r, RelationshipNotFoundError as rt, getMigrationStatus as s, matchesScope as st, createForwardingLogger as t, InvalidOperationTimestampError as tt, instrumentPgPool as u, yieldToMain as ut, KyselyExecutionScope as v, CollectionMembershipCache as w, KyselyWriteCache as x, resolveFeatureFlags as y, documentDecisionModel as z };
5416
6323
 
5417
- //# sourceMappingURL=drive-container-types-CS5IxDiA.js.map
6324
+ //# sourceMappingURL=forwarding-logger-CSsCJ38f.js.map