@powerhousedao/reactor 6.2.3-dev.24 → 6.2.3-dev.26

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 (39) hide show
  1. package/dist/{build-worker-executor-DxmDJ4QQ.js → build-worker-executor-Bvoba-AV.js} +4 -4
  2. package/dist/{build-worker-executor-DxmDJ4QQ.js.map → build-worker-executor-Bvoba-AV.js.map} +1 -1
  3. package/dist/{document-indexer-Dn9RB0sg.js → document-indexer-BVTGbEB0.js} +22 -21
  4. package/dist/document-indexer-BVTGbEB0.js.map +1 -0
  5. package/dist/entry.d.ts +2 -1
  6. package/dist/entry.js +2 -3
  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-Be5-EY9W.js → forwarding-logger-CnuVDVze.js} +303 -53
  11. package/dist/forwarding-logger-CnuVDVze.js.map +1 -0
  12. package/dist/index.d.ts +474 -307
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +700 -366
  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-mQhfVipO.js → projection-shard-manager-BpcqDtnQ.js} +2 -3
  20. package/dist/{projection-shard-manager-mQhfVipO.js.map → projection-shard-manager-BpcqDtnQ.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-B3p-nuqG.js → worker-handle-C2rLUHwv.js} +9 -8
  28. package/dist/{worker-handle-B3p-nuqG.js.map → worker-handle-C2rLUHwv.js.map} +1 -1
  29. package/package.json +14 -7
  30. package/dist/document-indexer-Dn9RB0sg.js.map +0 -1
  31. package/dist/drive-container-types-Be5-EY9W.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-DxDCmtId.js +0 -34
  37. package/dist/types-DxDCmtId.js.map +0 -1
  38. package/dist/worker-BDVzCtuf.js +0 -22
  39. package/dist/worker-BDVzCtuf.js.map +0 -1
@@ -1,4 +1,3 @@
1
- import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DxDCmtId.js";
2
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";
@@ -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.
@@ -329,6 +359,7 @@ const APPEND_CONDITION_FAILED_PREFIX = "Append condition failed: ";
329
359
  * conflict, not a fault: the caller retries against the new stream heads.
330
360
  */
331
361
  var AppendConditionFailedError = class extends Error {
362
+ condition;
332
363
  constructor(condition) {
333
364
  const streams = condition.streams.map((s) => `${s.documentId}:${s.scope}:${s.branch}@${s.revision}`).join(", ");
334
365
  super(`${APPEND_CONDITION_FAILED_PREFIX}a read-set stream advanced [${streams}]`);
@@ -1033,6 +1064,7 @@ const JobExecutorEventTypes = {
1033
1064
  //#endregion
1034
1065
  //#region src/cache/collection-membership-cache.ts
1035
1066
  var CollectionMembershipCache = class CollectionMembershipCache {
1067
+ operationIndex;
1036
1068
  cache = /* @__PURE__ */ new Map();
1037
1069
  constructor(operationIndex) {
1038
1070
  this.operationIndex = operationIndex;
@@ -1328,6 +1360,7 @@ var KyselyOperationIndexTxn = class {
1328
1360
  }
1329
1361
  };
1330
1362
  var KyselyOperationIndex = class KyselyOperationIndex {
1363
+ db;
1331
1364
  trx;
1332
1365
  constructor(db) {
1333
1366
  this.db = db;
@@ -1667,21 +1700,6 @@ var RingBuffer = class {
1667
1700
  }
1668
1701
  };
1669
1702
  //#endregion
1670
- //#region src/cache/write-cache-types.ts
1671
- /**
1672
- * Where a snapshot sits in its stream.
1673
- *
1674
- * - `Head`: the newest revision of the stream when it was stored. Only these
1675
- * can answer a read that asks for the head.
1676
- * - `Historical`: state at an earlier revision. Usable as a starting point to
1677
- * replay forward from, and as an answer to a read for that same revision.
1678
- */
1679
- let SnapshotPosition = /* @__PURE__ */ function(SnapshotPosition) {
1680
- SnapshotPosition["Head"] = "head";
1681
- SnapshotPosition["Historical"] = "historical";
1682
- return SnapshotPosition;
1683
- }({});
1684
- //#endregion
1685
1703
  //#region src/cache/kysely-write-cache.ts
1686
1704
  /**
1687
1705
  * The last operation index a keyframe's document reflects for the scope. A
@@ -1820,13 +1838,13 @@ var KyselyWriteCache = class KyselyWriteCache {
1820
1838
  const snapshots = stream.ringBuffer.getAll();
1821
1839
  if (targetRevision === void 0) {
1822
1840
  const newest = highestRevision(snapshots);
1823
- if (newest?.position === SnapshotPosition.Head) {
1841
+ if (newest?.position === "head") {
1824
1842
  this.lruTracker.touch(streamKey);
1825
1843
  return copyDocument(newest.document);
1826
1844
  }
1827
1845
  if (newest) {
1828
1846
  const document = await this.warmMissRebuild(newest.document, newest.revision, documentId, scope, branch, void 0, signal);
1829
- 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");
1830
1848
  this.lruTracker.touch(streamKey);
1831
1849
  return document;
1832
1850
  }
@@ -1839,7 +1857,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1839
1857
  const newestOlder = this.findNearestOlderSnapshot(snapshots, targetRevision);
1840
1858
  if (newestOlder) {
1841
1859
  const document = await this.warmMissRebuild(newestOlder.document, newestOlder.revision, documentId, scope, branch, targetRevision, signal);
1842
- this.store(documentId, scope, branch, targetRevision, document, SnapshotPosition.Historical);
1860
+ this.store(documentId, scope, branch, targetRevision, document, "historical");
1843
1861
  this.lruTracker.touch(streamKey);
1844
1862
  return document;
1845
1863
  }
@@ -1847,7 +1865,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1847
1865
  }
1848
1866
  const document = await this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
1849
1867
  const revision = targetRevision ?? (document.header.revision[scope] ?? 0) - 1;
1850
- 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");
1851
1869
  return document;
1852
1870
  }
1853
1871
  /**
@@ -1882,7 +1900,7 @@ var KyselyWriteCache = class KyselyWriteCache {
1882
1900
  if (run.length === 0) return;
1883
1901
  for (const entry of run.slice(0, -1)) this.persistKeyframe(documentId, scope, branch, entry.revision, entry.document);
1884
1902
  const head = run[run.length - 1];
1885
- this.store(documentId, scope, branch, head.revision, head.document, SnapshotPosition.Head);
1903
+ this.store(documentId, scope, branch, head.revision, head.document, "head");
1886
1904
  }
1887
1905
  store(documentId, scope, branch, revision, document, position) {
1888
1906
  const streamKey = this.makeStreamKey(documentId, scope, branch);
@@ -2401,6 +2419,11 @@ function validateFeatureFlags(flags, prerequisites) {
2401
2419
  //#endregion
2402
2420
  //#region src/executor/execution-scope.ts
2403
2421
  var DefaultExecutionScope = class {
2422
+ operationStore;
2423
+ operationIndex;
2424
+ writeCache;
2425
+ documentMetaCache;
2426
+ collectionMembershipCache;
2404
2427
  constructor(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache) {
2405
2428
  this.operationStore = operationStore;
2406
2429
  this.operationIndex = operationIndex;
@@ -2420,6 +2443,13 @@ var DefaultExecutionScope = class {
2420
2443
  }
2421
2444
  };
2422
2445
  var KyselyExecutionScope = class {
2446
+ db;
2447
+ operationStore;
2448
+ operationIndex;
2449
+ keyframeStore;
2450
+ writeCache;
2451
+ documentMetaCache;
2452
+ collectionMembershipCache;
2423
2453
  constructor(db, operationStore, operationIndex, keyframeStore, writeCache, documentMetaCache, collectionMembershipCache) {
2424
2454
  this.db = db;
2425
2455
  this.operationStore = operationStore;
@@ -2447,7 +2477,7 @@ var KyselyExecutionScope = class {
2447
2477
  };
2448
2478
  //#endregion
2449
2479
  //#region src/utils/reshuffle.ts
2450
- const STRICT_ORDER_ACTION_TYPES = new Set([
2480
+ const STRICT_ORDER_ACTION_TYPES = /* @__PURE__ */ new Set([
2451
2481
  "CREATE_DOCUMENT",
2452
2482
  "DELETE_DOCUMENT",
2453
2483
  "UPGRADE_DOCUMENT",
@@ -2778,6 +2808,8 @@ const DRIVE_COLLECTION_PREFIX = "drive.";
2778
2808
  * rows and persisted remotes remain valid without migration.
2779
2809
  */
2780
2810
  var DriveCollectionId = class DriveCollectionId {
2811
+ driveId;
2812
+ branch;
2781
2813
  constructor(driveId, branch) {
2782
2814
  this.driveId = driveId;
2783
2815
  this.branch = branch;
@@ -2810,6 +2842,11 @@ var DriveCollectionId = class DriveCollectionId {
2810
2842
  //#endregion
2811
2843
  //#region src/executor/document-action-handler.ts
2812
2844
  var DocumentActionHandler = class {
2845
+ registry;
2846
+ logger;
2847
+ driveContainerTypes;
2848
+ featureFlags;
2849
+ decisionModel;
2813
2850
  constructor(registry, logger, driveContainerTypes, featureFlags, decisionModel) {
2814
2851
  this.registry = registry;
2815
2852
  this.logger = logger;
@@ -2902,7 +2939,7 @@ var DocumentActionHandler = class {
2902
2939
  [job.scope]: [...standing.operations[job.scope] ?? [], operation]
2903
2940
  };
2904
2941
  executing.touchedStreams.add(job.documentId, job.scope, job.branch);
2905
- 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");
2906
2943
  indexTxn.write([{
2907
2944
  ...operation,
2908
2945
  documentId: job.documentId,
@@ -2956,7 +2993,7 @@ var DocumentActionHandler = class {
2956
2993
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
2957
2994
  };
2958
2995
  executing.touchedStreams.add(document.header.id, job.scope, job.branch);
2959
- 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");
2960
2997
  indexTxn.write([{
2961
2998
  ...operation,
2962
2999
  documentId: document.header.id,
@@ -3017,7 +3054,7 @@ var DocumentActionHandler = class {
3017
3054
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
3018
3055
  };
3019
3056
  executing.touchedStreams.add(documentId, job.scope, job.branch);
3020
- 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");
3021
3058
  indexTxn.write([{
3022
3059
  ...operation,
3023
3060
  documentId,
@@ -3068,7 +3105,7 @@ var DocumentActionHandler = class {
3068
3105
  } catch (error) {
3069
3106
  return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch revisions for upgrade: ${error instanceof Error ? error.message : String(error)}`), startTime);
3070
3107
  }
3071
- const revisionScopes = new Set([...Object.keys(input.revision), ...Object.keys(actualRevisions)]);
3108
+ const revisionScopes = /* @__PURE__ */ new Set([...Object.keys(input.revision), ...Object.keys(actualRevisions)]);
3072
3109
  for (const revisionScope of revisionScopes) {
3073
3110
  const snapshot = input.revision[revisionScope] ?? 0;
3074
3111
  const actual = actualRevisions[revisionScope] ?? 0;
@@ -3129,7 +3166,7 @@ var DocumentActionHandler = class {
3129
3166
  [job.scope]: [...document.operations[job.scope] ?? [], operation]
3130
3167
  };
3131
3168
  executing.touchedStreams.add(documentId, job.scope, job.branch);
3132
- 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");
3133
3170
  for (const scope of otherScopes) executing.postCommitInvalidations.push({
3134
3171
  documentId,
3135
3172
  scope,
@@ -3216,7 +3253,7 @@ var DocumentActionHandler = class {
3216
3253
  const resultingState = JSON.stringify(resultingStateObj);
3217
3254
  executing.touchedStreams.add(input.sourceId, job.scope, job.branch);
3218
3255
  executing.touchedStreams.add(input.targetId, job.scope, job.branch);
3219
- 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");
3220
3257
  indexTxn.write([{
3221
3258
  ...operation,
3222
3259
  documentId: input.sourceId,
@@ -3272,7 +3309,7 @@ var DocumentActionHandler = class {
3272
3309
  var PassthroughSigner = class {
3273
3310
  publicKey = {};
3274
3311
  sign() {
3275
- return Promise.resolve(new Uint8Array(0));
3312
+ return Promise.resolve(/* @__PURE__ */ new Uint8Array(0));
3276
3313
  }
3277
3314
  verify() {
3278
3315
  return Promise.resolve();
@@ -3360,7 +3397,7 @@ function decompressP256(compressed, did) {
3360
3397
  let y = modPow(ySquared, (P + BigInt(1)) / BigInt(4), P);
3361
3398
  if (y * y % P !== ySquared) throw new MalformedDidKeyError(did, "x is not on the curve");
3362
3399
  if (y % BigInt(2) === BigInt(0) !== (prefix === 2)) y = P - y;
3363
- const uncompressed = new Uint8Array(65);
3400
+ const uncompressed = /* @__PURE__ */ new Uint8Array(65);
3364
3401
  uncompressed[0] = 4;
3365
3402
  uncompressed.set(bigIntToBytes(x), 1);
3366
3403
  uncompressed.set(bigIntToBytes(y), 33);
@@ -3378,7 +3415,7 @@ function modPow(base, exp, mod) {
3378
3415
  return result;
3379
3416
  }
3380
3417
  function bigIntToBytes(value) {
3381
- const bytes = new Uint8Array(32);
3418
+ const bytes = /* @__PURE__ */ new Uint8Array(32);
3382
3419
  let v = value;
3383
3420
  for (let i = 31; i >= 0; i--) {
3384
3421
  bytes[i] = Number(v & BigInt(255));
@@ -3582,7 +3619,7 @@ function bytesToBase64(bytes) {
3582
3619
  //#endregion
3583
3620
  //#region src/executor/synthesized-signing.ts
3584
3621
  /** Submitted types whose stored action the reducer writes in their place. */
3585
- const SYNTHESIZING_TYPES = new Set(["UNDO", "REDO"]);
3622
+ const SYNTHESIZING_TYPES = /* @__PURE__ */ new Set(["UNDO", "REDO"]);
3586
3623
  /**
3587
3624
  * The id of the action synthesized from `submittedId`. Derived, so the stored
3588
3625
  * operation answers for the submitted id at the live-id check.
@@ -3638,6 +3675,10 @@ async function signSynthesized(operation, submitted, target, signer, signal) {
3638
3675
  const DEFAULT_TRUST_TIMEOUT_MS = 1e4;
3639
3676
  /** Runs once per write, when this reactor first stores it; never on re-appends. */
3640
3677
  var SignatureAdmission = class {
3678
+ mode;
3679
+ logger;
3680
+ eventBus;
3681
+ policySource;
3641
3682
  trustPolicy;
3642
3683
  trustTimeoutMs;
3643
3684
  constructor(mode, logger, eventBus, policySource = "meta", trust = {
@@ -3688,23 +3729,24 @@ var SignatureAdmission = class {
3688
3729
  operation
3689
3730
  }));
3690
3731
  const live = await this.liveOperationIds(candidates, operationStore, signal);
3732
+ const redelivered = await this.redelivered(candidates, live, operationStore, signal);
3691
3733
  await resolvePolicies(candidates, job.operations.map((operation) => operation.action), (documentId, branch) => storedPolicy(stores, this.policySource, documentId, branch, signal));
3692
3734
  const dropped = /* @__PURE__ */ new Set();
3693
3735
  for (let i = 0; i < candidates.length; i++) {
3694
- const verdict = await this.verdict(candidates[i], live, void 0, "load", signal);
3736
+ const verdict = await this.verdict(candidates[i], live, void 0, "load", signal, redelivered.has(candidates[i]));
3695
3737
  if (verdict.ok) continue;
3696
3738
  if (this.record(job, candidates[i], verdict, "load")) dropped.add(operations[i]);
3697
3739
  }
3698
3740
  return dropped;
3699
3741
  }
3700
- async verdict(entry, live, submitted, path, signal) {
3742
+ async verdict(entry, live, submitted, path, signal, known = false) {
3701
3743
  const verdict = await verifyActionSignature(entry.action, {
3702
3744
  documentId: entry.stream.documentId,
3703
3745
  branch: entry.stream.branch,
3704
3746
  policy: entry.policy
3705
3747
  }, path, entry.operation);
3706
3748
  if (!verdict.ok) return verdict;
3707
- if (isLive(entry, live) || submitted?.has(entry.opId)) return {
3749
+ if (!known && (isLive(entry, live) || submitted?.has(entry.opId))) return {
3708
3750
  ok: false,
3709
3751
  scheme: verdict.scheme,
3710
3752
  code: "DUPLICATE_ACTION",
@@ -3719,6 +3761,7 @@ var SignatureAdmission = class {
3719
3761
  reason: `action ${entry.action.id} is unsigned but claims to act as ${address}`
3720
3762
  } : verdict;
3721
3763
  }
3764
+ if (known) return verdict;
3722
3765
  const signer = entry.action.context.signer;
3723
3766
  if (!await this.authorize(signer, entry.stream.documentId, signal)) return {
3724
3767
  ok: false,
@@ -3750,7 +3793,9 @@ var SignatureAdmission = class {
3750
3793
  }
3751
3794
  }
3752
3795
  /**
3753
- * The live candidates stored exactly as submitted. A synthesized operation
3796
+ * The live candidates stored exactly as submitted and still standing: one
3797
+ * an undo or a reshuffle has since superseded is refused, not re-emitted with
3798
+ * the state it once produced. A synthesized operation
3754
3799
  * holds another action, so it cannot be compared; only a retry, whose first
3755
3800
  * attempt this job was, takes it as committed.
3756
3801
  */
@@ -3763,13 +3808,29 @@ var SignatureAdmission = class {
3763
3808
  for (const [stream, entries] of byStream(toCompare)) {
3764
3809
  const stored = await operationStore.getOperationsByIds(stream.documentId, stream.scope, stream.branch, entries.map((entry) => entry.opId), signal);
3765
3810
  const byId = new Map(stored.map((operation) => [operation.id, operation]));
3766
- for (const entry of entries) {
3811
+ const matching = entries.filter((entry) => {
3767
3812
  const operation = byId.get(entry.opId);
3768
- if (operation && sameContent(operation.action, entry.action)) committed.set(entry, committedWrite(entry, entry.opId));
3769
- }
3813
+ return operation && sameContent(operation.action, entry.action);
3814
+ });
3815
+ if (matching.length === 0) continue;
3816
+ const later = await operationStore.getSince(stream.documentId, stream.scope, stream.branch, Math.min(...matching.map((entry) => byId.get(entry.opId).index)), void 0, void 0, signal);
3817
+ for (const entry of matching) if (!isSuperseded(byId.get(entry.opId), later.results)) committed.set(entry, committedWrite(entry, entry.opId));
3770
3818
  }
3771
3819
  return committed;
3772
3820
  }
3821
+ /**
3822
+ * The load candidates stored exactly as they arrive, timestamp included: a
3823
+ * peer re-sending what this stream holds, not a replay of its action.
3824
+ */
3825
+ async redelivered(candidates, live, operationStore, signal) {
3826
+ const found = /* @__PURE__ */ new Set();
3827
+ const toCompare = candidates.filter((entry) => live.has(entry.opId) && !(entry.synthesizedOpId && live.has(entry.synthesizedOpId)));
3828
+ for (const [stream, entries] of byStream(toCompare)) {
3829
+ const stored = await operationStore.getOperationsByIds(stream.documentId, stream.scope, stream.branch, entries.map((entry) => entry.opId), signal);
3830
+ for (const entry of entries) if (stored.some((operation) => operation.id === entry.opId && Date.parse(operation.timestampUtcMs) === Date.parse(entry.operation?.timestampUtcMs ?? "") && sameContent(operation.action, entry.action))) found.add(entry);
3831
+ }
3832
+ return found;
3833
+ }
3773
3834
  async liveOperationIds(candidates, operationStore, signal) {
3774
3835
  const live = /* @__PURE__ */ new Set();
3775
3836
  for (const [stream, entries] of byStream(candidates)) {
@@ -3823,8 +3884,12 @@ async function resolvePolicies(candidates, jobActions, stored) {
3823
3884
  }
3824
3885
  async function storedPolicy(stores, source, documentId, branch, signal) {
3825
3886
  try {
3826
- if (source === "write-cache") return signaturePolicyOf((await stores.writeCache.getState(documentId, "document", branch, void 0, signal)).header);
3827
- return signaturePolicyOf((await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal)).protocolVersions);
3887
+ if (source === "write-cache") {
3888
+ const document = await stores.writeCache.getState(documentId, "document", branch, void 0, signal);
3889
+ return signaturePolicyOf(document.header);
3890
+ }
3891
+ const meta = await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal);
3892
+ return signaturePolicyOf(meta.protocolVersions);
3828
3893
  } catch (error) {
3829
3894
  if (DocumentNotFoundError.isError(error)) return;
3830
3895
  throw error;
@@ -3875,6 +3940,9 @@ function committedWrite(entry, opId) {
3875
3940
  opId
3876
3941
  };
3877
3942
  }
3943
+ function isSuperseded(operation, later) {
3944
+ return later.some((next) => next.index > operation.index && next.skip > 0 && next.index - next.skip <= operation.index);
3945
+ }
3878
3946
  function sameContent(stored, submitted) {
3879
3947
  try {
3880
3948
  return canonicalJson(stored) === canonicalJson(submitted);
@@ -3899,6 +3967,7 @@ function isValidISOTimestamp(value) {
3899
3967
  * every test expect a failed job to look like.
3900
3968
  */
3901
3969
  var JobRollbackSignal = class extends Error {
3970
+ result;
3902
3971
  constructor(result) {
3903
3972
  super("job rolled back");
3904
3973
  this.result = result;
@@ -3906,6 +3975,15 @@ var JobRollbackSignal = class extends Error {
3906
3975
  }
3907
3976
  };
3908
3977
  var SimpleJobExecutor = class {
3978
+ logger;
3979
+ registry;
3980
+ operationStore;
3981
+ eventBus;
3982
+ writeCache;
3983
+ operationIndex;
3984
+ documentMetaCache;
3985
+ collectionMembershipCache;
3986
+ driveContainerTypes;
3909
3987
  config;
3910
3988
  featureFlags;
3911
3989
  decisionModel;
@@ -4831,7 +4909,7 @@ var SimpleJobExecutor = class {
4831
4909
  stores.writeCache.invalidate(job.documentId, scope, job.branch);
4832
4910
  const result = await this.processActions(tail.map((operation, i) => ({
4833
4911
  action: operation.action,
4834
- skip: i === 0 ? retractionSkip(nextIndex, tail[0].index) : 0,
4912
+ skip: i === 0 ? retractionSkip(nextIndex, tail[0].index - tail[0].skip) : 0,
4835
4913
  sourceRemote: "",
4836
4914
  deniedReason: reevaluated[firstChange + i]
4837
4915
  })), {
@@ -4993,7 +5071,8 @@ var SimpleJobExecutor = class {
4993
5071
  ...operation,
4994
5072
  id: operation.id
4995
5073
  })));
4996
- for (const operation of reshuffledOperations) if (operation.action.type === "NOOP" && operation.skip === 0) operation.skip = 1;
5074
+ const incomingIds = new Set(incomingOpsToApply.map((operation) => operation.action.id));
5075
+ for (const operation of reshuffledOperations) if (operation.action.type === "NOOP" && operation.skip === 0 && incomingIds.has(operation.action.id)) operation.skip = 1;
4997
5076
  let deniedReasons;
4998
5077
  if (this.featureFlags.documentDecisions) try {
4999
5078
  deniedReasons = await evaluateByPosition(this.decisionModel, {
@@ -5071,17 +5150,35 @@ var SimpleJobExecutor = class {
5071
5150
  }
5072
5151
  }
5073
5152
  const incomingActionIds = new Set(operations.map((op) => op.action.id));
5153
+ const supersededBy = (op) => {
5154
+ let nearest;
5155
+ for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0 && laterOp.index - laterOp.skip <= op.index && (nearest === void 0 || laterOp.index < nearest.index)) nearest = laterOp;
5156
+ return nearest;
5157
+ };
5158
+ const undoneActionIds = /* @__PURE__ */ new Set();
5159
+ let predecessorBound = minIncomingIndex;
5160
+ const rowsByActionId = /* @__PURE__ */ new Map();
5161
+ for (const op of conflictingOps) if (incomingActionIds.has(op.action.id)) rowsByActionId.set(op.action.id, [...rowsByActionId.get(op.action.id) ?? [], op]);
5162
+ const liveOps = /* @__PURE__ */ new Set();
5163
+ for (const op of conflictingOps) if (supersededBy(op) === void 0) liveOps.add(op);
5164
+ for (const [actionId, rows] of rowsByActionId) {
5165
+ if (rows.some((row) => liveOps.has(row))) continue;
5166
+ if (rows.some((row) => supersededBy(row)?.action.type === "NOOP")) undoneActionIds.add(actionId);
5167
+ else predecessorBound = Math.min(predecessorBound, Math.max(...rows.map((row) => row.index)));
5168
+ }
5074
5169
  const nonSupersededOps = conflictingOps.filter((op) => {
5075
- if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
5076
- for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
5077
- if (laterOp.index - laterOp.skip <= op.index) return false;
5078
- }
5079
- return true;
5170
+ if (op.index < predecessorBound && !incomingActionIds.has(op.action.id)) return false;
5171
+ return liveOps.has(op);
5080
5172
  });
5173
+ let rewoundFrom = Number.POSITIVE_INFINITY;
5174
+ for (const op of nonSupersededOps) if (!isGenesisOperation(op)) rewoundFrom = Math.min(rewoundFrom, op.index - op.skip);
5175
+ const reappended = new Set(nonSupersededOps.map((op) => op.index));
5176
+ for (const op of allOpsFromMinConflictingIndex) if (op.index >= rewoundFrom && !reappended.has(op.index) && supersededBy(op) === void 0) nonSupersededOps.push(op);
5081
5177
  const existingActionIds = new Set(nonSupersededOps.map((op) => op.action.id));
5082
5178
  const seenIncomingActionIds = /* @__PURE__ */ new Set();
5083
5179
  const incomingOpsToApply = operations.filter((op) => {
5084
5180
  if (existingActionIds.has(op.action.id)) return false;
5181
+ if (undoneActionIds.has(op.action.id)) return false;
5085
5182
  if (seenIncomingActionIds.has(op.action.id)) return false;
5086
5183
  seenIncomingActionIds.add(op.action.id);
5087
5184
  return true;
@@ -5238,6 +5335,7 @@ var DocumentModelRegistry = class {
5238
5335
  //#endregion
5239
5336
  //#region src/storage/kysely/keyframe-store.ts
5240
5337
  var KyselyKeyframeStore = class KyselyKeyframeStore {
5338
+ db;
5241
5339
  trx;
5242
5340
  constructor(db) {
5243
5341
  this.db = db;
@@ -5322,6 +5420,11 @@ function paginateRows(rows, paging, cursorOf, toItem, refetch) {
5322
5420
  //#endregion
5323
5421
  //#region src/storage/txn.ts
5324
5422
  var AtomicTransaction = class {
5423
+ documentId;
5424
+ documentType;
5425
+ scope;
5426
+ branch;
5427
+ baseRevision;
5325
5428
  operations = [];
5326
5429
  constructor(documentId, documentType, scope, branch, baseRevision) {
5327
5430
  this.documentId = documentId;
@@ -5355,6 +5458,11 @@ var AtomicTransaction = class {
5355
5458
  //#endregion
5356
5459
  //#region src/storage/kysely/store.ts
5357
5460
  var _UniqueConstraintContext = class extends Error {
5461
+ documentId;
5462
+ scope;
5463
+ branch;
5464
+ revision;
5465
+ stagedOps;
5358
5466
  constructor(documentId, scope, branch, revision, stagedOps) {
5359
5467
  super("unique constraint");
5360
5468
  this.documentId = documentId;
@@ -5366,6 +5474,7 @@ var _UniqueConstraintContext = class extends Error {
5366
5474
  }
5367
5475
  };
5368
5476
  var KyselyOperationStore = class KyselyOperationStore {
5477
+ db;
5369
5478
  trx;
5370
5479
  constructor(db) {
5371
5480
  this.db = db;
@@ -5465,12 +5574,13 @@ var KyselyOperationStore = class KyselyOperationStore {
5465
5574
  * otherwise read a snapshot taken before the locks were held.
5466
5575
  */
5467
5576
  async acquireStreamLocks(trx, documentId, scope, branch, condition) {
5468
- const keys = new Set([`${documentId}:${scope}:${branch}`]);
5577
+ const keys = /* @__PURE__ */ new Set([`${documentId}:${scope}:${branch}`]);
5469
5578
  for (const stream of condition.streams) keys.add(`${stream.documentId}:${stream.scope}:${stream.branch}`);
5579
+ const sortedKeys = sql.join([...keys].sort());
5470
5580
  await sql`
5471
5581
  with ordered as materialized (
5472
5582
  select key
5473
- from unnest(array[${sql.join([...keys].sort())}]::text[]) with ordinality as t(key, ord)
5583
+ from unnest(array[${sortedKeys}]::text[]) with ordinality as t(key, ord)
5474
5584
  order by ord
5475
5585
  )
5476
5586
  select pg_advisory_xact_lock(hashtext(key)) from ordered
@@ -6109,8 +6219,148 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
6109
6219
  }
6110
6220
  //#endregion
6111
6221
  //#region src/core/drive-container-types.ts
6112
- const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
6222
+ const DEFAULT_DRIVE_CONTAINER_TYPES = /* @__PURE__ */ new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
6223
+ //#endregion
6224
+ //#region src/executor/worker/error-info.ts
6225
+ const MAX_STACK_LENGTH = 8192;
6226
+ const MAX_CAUSE_DEPTH = 8;
6227
+ function toErrorInfo(err, depth = 0) {
6228
+ if (depth > MAX_CAUSE_DEPTH) return {
6229
+ name: "Error",
6230
+ message: "cause chain truncated"
6231
+ };
6232
+ if (err instanceof Error) {
6233
+ const info = {
6234
+ name: err.name || "Error",
6235
+ message: err.message
6236
+ };
6237
+ if (typeof err.stack === "string") info.stack = err.stack.length > MAX_STACK_LENGTH ? err.stack.slice(0, MAX_STACK_LENGTH) : err.stack;
6238
+ if (err.cause !== void 0) info.cause = toErrorInfo(err.cause, depth + 1);
6239
+ return info;
6240
+ }
6241
+ if (typeof err === "string") return {
6242
+ name: "Error",
6243
+ message: err
6244
+ };
6245
+ if (err && typeof err === "object") {
6246
+ const record = err;
6247
+ return {
6248
+ name: typeof record.name === "string" ? record.name : "Error",
6249
+ message: typeof record.message === "string" ? record.message : safeStringify(err)
6250
+ };
6251
+ }
6252
+ return {
6253
+ name: "Error",
6254
+ message: safeStringify(err)
6255
+ };
6256
+ }
6257
+ function safeStringify(value) {
6258
+ if (value === null || value === void 0) return String(value);
6259
+ if (typeof value === "string") return value;
6260
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
6261
+ try {
6262
+ return JSON.stringify(value);
6263
+ } catch {
6264
+ return "[unserializable]";
6265
+ }
6266
+ }
6267
+ function fromErrorInfo(info) {
6268
+ const err = new Error(info.message);
6269
+ Object.defineProperty(err, "name", {
6270
+ value: info.name,
6271
+ configurable: true,
6272
+ writable: true
6273
+ });
6274
+ if (info.stack !== void 0) err.stack = info.stack;
6275
+ if (info.cause !== void 0) Object.defineProperty(err, "cause", {
6276
+ value: fromErrorInfo(info.cause),
6277
+ configurable: true,
6278
+ writable: true
6279
+ });
6280
+ return err;
6281
+ }
6282
+ //#endregion
6283
+ //#region src/executor/worker/sanitize.ts
6284
+ const MAX_DEPTH = 8;
6285
+ function errorToInfo(err) {
6286
+ return toErrorInfo(err);
6287
+ }
6288
+ function sanitizeArg(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
6289
+ if (depth > MAX_DEPTH) return "[Truncated]";
6290
+ if (value === null || value === void 0) return null;
6291
+ if (typeof value === "boolean") return value;
6292
+ if (typeof value === "number") return value;
6293
+ if (typeof value === "string") return value;
6294
+ if (typeof value === "bigint") return value.toString();
6295
+ if (typeof value === "function" || typeof value === "symbol") return null;
6296
+ if (value instanceof Error) return toErrorInfo(value);
6297
+ if (value instanceof Date) return value.toISOString();
6298
+ if (value instanceof Map) {
6299
+ const obj = {};
6300
+ for (const [k, v] of value.entries()) obj[String(k)] = sanitizeArg(v, depth + 1, seen);
6301
+ return obj;
6302
+ }
6303
+ if (value instanceof Set) {
6304
+ const arr = [];
6305
+ for (const v of value.values()) arr.push(sanitizeArg(v, depth + 1, seen));
6306
+ return arr;
6307
+ }
6308
+ if (Array.isArray(value)) {
6309
+ if (seen.has(value)) return "[Circular]";
6310
+ seen.add(value);
6311
+ const result = value.map((v) => sanitizeArg(v, depth + 1, seen));
6312
+ seen.delete(value);
6313
+ return result;
6314
+ }
6315
+ if (typeof value === "object") {
6316
+ const proto = Object.getPrototypeOf(value);
6317
+ if (proto !== Object.prototype && proto !== null) return { __nonClonable: value.constructor?.name || "Unknown" };
6318
+ if (seen.has(value)) return "[Circular]";
6319
+ seen.add(value);
6320
+ const result = {};
6321
+ for (const [k, v] of Object.entries(value)) result[k] = sanitizeArg(v, depth + 1, seen);
6322
+ seen.delete(value);
6323
+ return result;
6324
+ }
6325
+ return null;
6326
+ }
6327
+ //#endregion
6328
+ //#region src/executor/worker/forwarding-logger.ts
6329
+ function createForwardingLogger(post, level = "info", tags = []) {
6330
+ function safePost(msg) {
6331
+ try {
6332
+ post(msg);
6333
+ } catch {
6334
+ try {
6335
+ process.stderr.write(`[forwarding-logger] postMessage failed: ${msg.level} ${msg.message}\n`);
6336
+ } catch {}
6337
+ }
6338
+ }
6339
+ function emit(wireLevel, message, args) {
6340
+ const sanitizedArgs = args.map((a) => sanitizeArg(a));
6341
+ safePost({
6342
+ type: "log",
6343
+ level: wireLevel,
6344
+ message,
6345
+ args: tags.length > 0 ? [sanitizeArg({ tags }), ...sanitizedArgs] : sanitizedArgs,
6346
+ timestamp: Date.now()
6347
+ });
6348
+ }
6349
+ return {
6350
+ level,
6351
+ verbose: (message, ...args) => emit("debug", message, args),
6352
+ debug: (message, ...args) => emit("debug", message, args),
6353
+ info: (message, ...args) => emit("info", message, args),
6354
+ warn: (message, ...args) => emit("warn", message, args),
6355
+ error: (message, ...args) => emit("error", message, args),
6356
+ errorHandler: ((...data) => {
6357
+ const first = data[0];
6358
+ emit("error", typeof first === "string" ? first : "", data.slice(1));
6359
+ }),
6360
+ child: (childTags) => createForwardingLogger(post, level, [...tags, ...childTags])
6361
+ };
6362
+ }
6113
6363
  //#endregion
6114
- export { RelationshipNotFoundError as $, submittedActionIds as A, DocumentAlreadyExistsError as B, DuplicateManifestError as C, GATED_DOCUMENT_ACTIONS as D, ModuleNotFoundError as E, documentDecisionModel as F, AuthEnforcementDisabledError as G, DuplicateOperationError as H, authDecisionModel as I, DocumentDeletedError as J, AuthTimestampNotMonotonicError as K, buildDecisionModel as L, targetDocumentId as M, decideAtHead as N, createConsistencyToken as O, selectDecisionModel as P, InvalidSignatureError as Q, APPEND_CONDITION_FAILED_PREFIX as R, JobExecutorEventTypes as S, InvalidModuleError as T, OptimisticLockError as U, DocumentExistence as V, RevisionMismatchError as W, ExcessiveReshuffleError as X, DocumentNotFoundError as Y, InvalidOperationTimestampError as Z, KyselyWriteCache as _, createForwardingPoolInstrumentation as a, __exportAll as at, CollectionMembershipCache as b, KyselyKeyframeStore as c, verifyActionSignature as d, UpgradePreconditionFailedError as et, PassthroughSigner as f, EventBus as g, resolveFeatureFlags as h, runMigrations as i, yieldToMain as it, summarizeSubmittedActions as j, createEmptyConsistencyToken as k, DocumentModelRegistry as l, KyselyExecutionScope as m, REACTOR_SCHEMA as n, parsePagingOptions as nt, instrumentPgPool as o, DriveCollectionId as p, AuthorizationDeniedError as q, getMigrationStatus as r, throwIfAborted as rt, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, matchesScope as tt, SimpleJobExecutor as u, KyselyOperationIndex as v, DuplicateModuleError as w, DEFAULT_DEFERRED_JOB_TTL_MS as x, DocumentMetaCache as y, AppendConditionFailedError as z };
6364
+ 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 };
6115
6365
 
6116
- //# sourceMappingURL=drive-container-types-Be5-EY9W.js.map
6366
+ //# sourceMappingURL=forwarding-logger-CnuVDVze.js.map