@opendatalabs/personal-server-ts-lite 1.34.2 → 1.35.0

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 (97) hide show
  1. package/dist/additions-crypto.d.ts +1 -1
  2. package/dist/additions-crypto.d.ts.map +1 -1
  3. package/dist/additions-crypto.js +16 -6
  4. package/dist/additions-crypto.js.map +1 -1
  5. package/dist/additions-protocol.d.ts +9 -1
  6. package/dist/additions-protocol.d.ts.map +1 -1
  7. package/dist/additions-protocol.js.map +1 -1
  8. package/dist/additions-session.d.ts +2 -0
  9. package/dist/additions-session.d.ts.map +1 -1
  10. package/dist/additions-session.js +29 -4
  11. package/dist/additions-session.js.map +1 -1
  12. package/dist/additions-transport.d.ts +10 -2
  13. package/dist/additions-transport.d.ts.map +1 -1
  14. package/dist/additions-transport.js +143 -48
  15. package/dist/additions-transport.js.map +1 -1
  16. package/dist/additions-worker.bundle.js +538 -101
  17. package/dist/additions-worker.js +45 -24
  18. package/dist/additions-worker.js.map +1 -1
  19. package/dist/browser-runtime.d.ts +23 -0
  20. package/dist/browser-runtime.d.ts.map +1 -1
  21. package/dist/browser-runtime.js +38 -1
  22. package/dist/browser-runtime.js.map +1 -1
  23. package/dist/canonical-persistence.d.ts +53 -0
  24. package/dist/canonical-persistence.d.ts.map +1 -0
  25. package/dist/canonical-persistence.js +13 -0
  26. package/dist/canonical-persistence.js.map +1 -0
  27. package/dist/canonical-store.d.ts +395 -0
  28. package/dist/canonical-store.d.ts.map +1 -0
  29. package/dist/canonical-store.js +1807 -0
  30. package/dist/canonical-store.js.map +1 -0
  31. package/dist/client.d.ts.map +1 -1
  32. package/dist/client.js +154 -1
  33. package/dist/client.js.map +1 -1
  34. package/dist/diagnostics.d.ts +3 -0
  35. package/dist/diagnostics.d.ts.map +1 -1
  36. package/dist/diagnostics.js +9 -1
  37. package/dist/diagnostics.js.map +1 -1
  38. package/dist/history-probe-worker.bundle.js +31216 -0
  39. package/dist/history-probe.worker.d.ts +35 -0
  40. package/dist/history-probe.worker.d.ts.map +1 -0
  41. package/dist/history-probe.worker.js +160 -0
  42. package/dist/history-probe.worker.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +10 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/mcp-store.d.ts.map +1 -1
  48. package/dist/mcp-store.js +109 -19
  49. package/dist/mcp-store.js.map +1 -1
  50. package/dist/oura-import.d.ts +2 -0
  51. package/dist/oura-import.d.ts.map +1 -0
  52. package/dist/oura-import.js +3 -0
  53. package/dist/oura-import.js.map +1 -0
  54. package/dist/replication-coordinator-state.d.ts +19 -0
  55. package/dist/replication-coordinator-state.d.ts.map +1 -0
  56. package/dist/replication-coordinator-state.js +20 -0
  57. package/dist/replication-coordinator-state.js.map +1 -0
  58. package/dist/replication-door.d.ts +17 -0
  59. package/dist/replication-door.d.ts.map +1 -0
  60. package/dist/replication-door.js +23 -0
  61. package/dist/replication-door.js.map +1 -0
  62. package/dist/replication-engine.d.ts +49 -0
  63. package/dist/replication-engine.d.ts.map +1 -0
  64. package/dist/replication-engine.js +589 -0
  65. package/dist/replication-engine.js.map +1 -0
  66. package/dist/replication-host.d.ts +92 -0
  67. package/dist/replication-host.d.ts.map +1 -0
  68. package/dist/replication-host.js +135 -0
  69. package/dist/replication-host.js.map +1 -0
  70. package/dist/replication-ports.d.ts +194 -0
  71. package/dist/replication-ports.d.ts.map +1 -0
  72. package/dist/replication-ports.js +58 -0
  73. package/dist/replication-ports.js.map +1 -0
  74. package/dist/replication-runtime.d.ts +64 -0
  75. package/dist/replication-runtime.d.ts.map +1 -0
  76. package/dist/replication-runtime.js +79 -0
  77. package/dist/replication-runtime.js.map +1 -0
  78. package/dist/replication-storage.d.ts +42 -0
  79. package/dist/replication-storage.d.ts.map +1 -0
  80. package/dist/replication-storage.js +173 -0
  81. package/dist/replication-storage.js.map +1 -0
  82. package/dist/runtime.d.ts +20 -2
  83. package/dist/runtime.d.ts.map +1 -1
  84. package/dist/runtime.js +80 -1
  85. package/dist/runtime.js.map +1 -1
  86. package/dist/state.d.ts +1 -1
  87. package/dist/state.d.ts.map +1 -1
  88. package/dist/state.js.map +1 -1
  89. package/dist/storage.d.ts +15 -0
  90. package/dist/storage.d.ts.map +1 -1
  91. package/dist/storage.js +352 -146
  92. package/dist/storage.js.map +1 -1
  93. package/dist/sync.d.ts +8 -0
  94. package/dist/sync.d.ts.map +1 -1
  95. package/dist/sync.js +25 -7
  96. package/dist/sync.js.map +1 -1
  97. package/package.json +8 -4
@@ -24051,12 +24051,12 @@ function describe(description) {
24051
24051
  return ch2;
24052
24052
  }
24053
24053
  // @__NO_SIDE_EFFECTS__
24054
- function meta(metadata) {
24054
+ function meta(metadata2) {
24055
24055
  const ch2 = new $ZodCheck({ check: "meta" });
24056
24056
  ch2._zod.onattach = [
24057
24057
  (inst) => {
24058
24058
  const existing = globalRegistry.get(inst) ?? {};
24059
- globalRegistry.add(inst, { ...existing, ...metadata });
24059
+ globalRegistry.add(inst, { ...existing, ...metadata2 });
24060
24060
  }
24061
24061
  ];
24062
24062
  ch2._zod.check = () => {
@@ -29642,6 +29642,288 @@ var DerivativeStatusSchema = external_exports.object({
29642
29642
  retryAfterSeconds: nullable2(external_exports.number())
29643
29643
  });
29644
29644
 
29645
+ // ../core/dist/sync/tombstone.js
29646
+ var TOMBSTONE_DATA_HASH_LABEL = "vana.data-point.tombstone.v1";
29647
+ var TOMBSTONE_METADATA_HASH_LABEL = "vana.data-point.tombstone.metadata.v1";
29648
+ var TOMBSTONE_DATA_HASH = keccak256(stringToHex(TOMBSTONE_DATA_HASH_LABEL));
29649
+ var TOMBSTONE_METADATA_HASH = keccak256(stringToHex(TOMBSTONE_METADATA_HASH_LABEL));
29650
+ function isTombstoneRecord(record2) {
29651
+ return record2.dataHash.toLowerCase() === TOMBSTONE_DATA_HASH && record2.metadataHash.toLowerCase() === TOMBSTONE_METADATA_HASH;
29652
+ }
29653
+
29654
+ // ../core/dist/sync/data-point-id.js
29655
+ function computeDataPointId(ownerAddress, scope) {
29656
+ return keccak256(encodeAbiParameters([
29657
+ { name: "ownerAddress", type: "address" },
29658
+ { name: "scope", type: "string" }
29659
+ ], [ownerAddress.toLowerCase(), scope]));
29660
+ }
29661
+
29662
+ // ../core/dist/lineage/lineage.js
29663
+ var LINEAGE_KEY = "$lineage";
29664
+ var BYTES32_HEX = /^0x[0-9a-fA-F]{64}$/;
29665
+ function isRecord2(value) {
29666
+ return value !== null && typeof value === "object" && !Array.isArray(value);
29667
+ }
29668
+ var StoredLineageMalformedError = class extends Error {
29669
+ constructor(detail) {
29670
+ super(`Stored $lineage is malformed: ${detail}`);
29671
+ this.name = "StoredLineageMalformedError";
29672
+ }
29673
+ };
29674
+ function readStoredLineage(data) {
29675
+ if (!isRecord2(data) || !(LINEAGE_KEY in data))
29676
+ return null;
29677
+ const value = data[LINEAGE_KEY];
29678
+ if (!isRecord2(value)) {
29679
+ throw new StoredLineageMalformedError("not an object");
29680
+ }
29681
+ const sources = value.sources;
29682
+ if (!Array.isArray(sources) || !sources.every((id) => typeof id === "string" && BYTES32_HEX.test(id))) {
29683
+ throw new StoredLineageMalformedError("sources is not a list of bytes32 ids");
29684
+ }
29685
+ if (typeof value.writtenAt !== "string") {
29686
+ throw new StoredLineageMalformedError("writtenAt is not a string");
29687
+ }
29688
+ return {
29689
+ sources: sources.map((id) => id.toLowerCase()),
29690
+ writtenAt: value.writtenAt
29691
+ };
29692
+ }
29693
+
29694
+ // ../core/dist/storage/adapters/interface.js
29695
+ var StoredBlobConflictError = class extends Error {
29696
+ key;
29697
+ constructor(key) {
29698
+ super(`Storage key ${key} already holds different bytes`);
29699
+ this.key = key;
29700
+ this.name = "StoredBlobConflictError";
29701
+ }
29702
+ };
29703
+
29704
+ // ../core/dist/publication/publish.js
29705
+ var NeedsFencedPublicationError = class extends Error {
29706
+ scope;
29707
+ reason;
29708
+ retryable;
29709
+ code = "NEEDS_FENCED_PUBLICATION";
29710
+ constructor(scope, reason, retryable = true) {
29711
+ super(`Needs fenced publication for ${scope}: ${reason}`);
29712
+ this.scope = scope;
29713
+ this.reason = reason;
29714
+ this.retryable = retryable;
29715
+ this.name = "NeedsFencedPublicationError";
29716
+ }
29717
+ };
29718
+ var sameHash = (a2, b2) => a2.toLowerCase() === b2.toLowerCase();
29719
+ function metadata(scope, collectedAt, sizeBytes) {
29720
+ return keccak256(stringToHex(JSON.stringify({ scope, collectedAt, sizeBytes })));
29721
+ }
29722
+ async function publishVersion(options) {
29723
+ const { envelope, storageAdapter } = options;
29724
+ const { scope } = envelope;
29725
+ const password = Array.from(deriveScopeKey(options.masterKey, scope), (byte) => byte.toString(16).padStart(2, "0")).join("");
29726
+ const plaintext = new TextEncoder().encode(JSON.stringify(envelope));
29727
+ const lineage = readStoredLineage(envelope.data);
29728
+ function candidate(env, bytes, hash3) {
29729
+ return {
29730
+ dataHash: hash3,
29731
+ metadataHash: metadata(scope, env.collectedAt, bytes.byteLength),
29732
+ sizeBytes: bytes.byteLength,
29733
+ lineage: readStoredLineage(env.data)
29734
+ };
29735
+ }
29736
+ return publishPreparedVersion({
29737
+ ...options,
29738
+ scope,
29739
+ storageAdapter,
29740
+ async prepare() {
29741
+ const encrypted = await encryptWithPassword(plaintext, password);
29742
+ return {
29743
+ encrypted,
29744
+ wanted: {
29745
+ ...candidate(envelope, encrypted, keccak256(plaintext)),
29746
+ lineage
29747
+ },
29748
+ async verifyStored(bytes) {
29749
+ const clear = await decryptWithPassword(bytes, password);
29750
+ const env = DataFileEnvelopeSchema.parse(JSON.parse(new TextDecoder().decode(clear)));
29751
+ if (env.scope !== scope || !Number.isFinite(Date.parse(env.collectedAt)))
29752
+ throw new Error("Publication envelope scope/timestamp mismatch");
29753
+ return candidate(env, bytes, keccak256(clear));
29754
+ }
29755
+ };
29756
+ }
29757
+ });
29758
+ }
29759
+ async function publishPreparedVersion(options) {
29760
+ const { scope, storageAdapter, fencedPublication, signer } = options;
29761
+ const isDeleted = (row) => Boolean(row.deletedAt) || isTombstoneRecord(row);
29762
+ let fence = await fencedPublication?.read({
29763
+ ownerAddress: options.ownerAddress,
29764
+ scope
29765
+ });
29766
+ let head = fence ? fence.head : options.head;
29767
+ await options.checkHead?.(head ?? null);
29768
+ function requireFence(reason) {
29769
+ if (!fence)
29770
+ throw new NeedsFencedPublicationError(scope, reason);
29771
+ return fence;
29772
+ }
29773
+ function checkDeletion() {
29774
+ if (!head || !isDeleted(head))
29775
+ return;
29776
+ requireFence("publication crosses a deletion");
29777
+ if (options.acknowledgedDeletionVersion !== BigInt(head.expectedVersion))
29778
+ throw new NeedsFencedPublicationError(scope, "tombstone was not acknowledged by this publication");
29779
+ }
29780
+ checkDeletion();
29781
+ const { encrypted, wanted, verifyStored } = await options.prepare();
29782
+ const wantedHash = wanted.dataHash;
29783
+ const keyUrl = (version5) => storageAdapter.urlForKey(`${scope}/${version5}`);
29784
+ async function readCandidate(version5) {
29785
+ const bytes = await storageAdapter.download(keyUrl(version5));
29786
+ return verifyStored(bytes);
29787
+ }
29788
+ async function commit(version5, value, mode) {
29789
+ const request = {
29790
+ ownerAddress: options.ownerAddress,
29791
+ scope,
29792
+ dataHash: value.dataHash,
29793
+ metadataHash: value.metadataHash,
29794
+ expectedVersion: String(version5),
29795
+ signature: await signer.signAddData({
29796
+ ownerAddress: options.ownerAddress,
29797
+ scope,
29798
+ dataHash: value.dataHash,
29799
+ metadataHash: value.metadataHash,
29800
+ expectedVersion: version5
29801
+ })
29802
+ };
29803
+ const lineage = value.lineage;
29804
+ let lineageFields;
29805
+ if (lineage) {
29806
+ if (!signer.signLineageAttestation || !fencedPublication && !options.lineageGateway)
29807
+ throw new Error(`Cannot register derivative ${scope}: lineage registration needs a gateway URL (lineageGateway is not configured)`);
29808
+ lineageFields = {
29809
+ lineage: lineage.sources,
29810
+ lineageSignature: await signer.signLineageAttestation({
29811
+ ownerAddress: options.ownerAddress,
29812
+ scope,
29813
+ expectedVersion: version5,
29814
+ dataHash: value.dataHash,
29815
+ sources: lineage.sources
29816
+ })
29817
+ };
29818
+ }
29819
+ let id;
29820
+ if (fencedPublication) {
29821
+ const previous = requireFence("registration/adoption");
29822
+ const result = await fencedPublication.commit({
29823
+ ...request,
29824
+ ...lineageFields,
29825
+ mode,
29826
+ fence: previous
29827
+ });
29828
+ const registered = result.fence.head;
29829
+ if (result.fence.generation !== previous.generation || !registered || isDeleted(registered) || registered.expectedVersion !== String(version5) || !sameHash(registered.dataHash, value.dataHash) || !sameHash(registered.metadataHash, value.metadataHash) || registered.id !== result.dataPointId)
29830
+ throw new Error("Gateway returned inconsistent publication evidence");
29831
+ fence = result.fence;
29832
+ head = registered;
29833
+ id = result.dataPointId;
29834
+ } else if (mode === "adopt") {
29835
+ id = head?.id;
29836
+ } else {
29837
+ const result = lineageFields ? await options.lineageGateway.registerDataPoint({
29838
+ ...request,
29839
+ ...lineageFields
29840
+ }) : await options.gateway.registerDataPoint(request);
29841
+ id = result.dataPointId;
29842
+ }
29843
+ if (!id)
29844
+ throw new Error(`Gateway registerDataPoint did not return a dataPointId for ${scope} version ${version5}`);
29845
+ return {
29846
+ version: version5,
29847
+ url: keyUrl(version5),
29848
+ dataPointId: id,
29849
+ sizeBytes: value.sizeBytes,
29850
+ dataHash: value.dataHash,
29851
+ metadataHash: value.metadataHash
29852
+ };
29853
+ }
29854
+ let version4 = head === void 0 ? options.preferredVersion : BigInt(head?.expectedVersion ?? "0") + 1n;
29855
+ let rebased = false;
29856
+ for (let step = 0; step < 4; step++) {
29857
+ checkDeletion();
29858
+ if (head && !isDeleted(head) && sameHash(head.dataHash, wantedHash)) {
29859
+ version4 = BigInt(head.expectedVersion);
29860
+ const url2 = keyUrl(version4);
29861
+ if (!await storageAdapter.exists(url2)) {
29862
+ requireFence("healing a committed version's missing bytes");
29863
+ if (!sameHash(wanted.metadataHash, head.metadataHash))
29864
+ throw new Error("Missing committed blob cannot be healed: metadata commitment mismatch");
29865
+ try {
29866
+ await storageAdapter.upload(`${scope}/${version4}`, encrypted);
29867
+ } catch (error51) {
29868
+ if (!(error51 instanceof StoredBlobConflictError))
29869
+ throw error51;
29870
+ }
29871
+ }
29872
+ const value2 = await readCandidate(version4);
29873
+ if (!sameHash(value2.dataHash, head.dataHash) || !sameHash(value2.metadataHash, head.metadataHash))
29874
+ throw new Error("Adopted publication commitment mismatch");
29875
+ return commit(version4, value2, "adopt");
29876
+ }
29877
+ if (fence)
29878
+ version4 = BigInt(head?.expectedVersion ?? "0") + 1n;
29879
+ if (head && BigInt(head.expectedVersion) >= version4)
29880
+ version4 = BigInt(head.expectedVersion) + 1n;
29881
+ let value = wanted;
29882
+ try {
29883
+ await storageAdapter.upload(`${scope}/${version4}`, encrypted);
29884
+ } catch (error51) {
29885
+ if (!(error51 instanceof StoredBlobConflictError))
29886
+ throw error51;
29887
+ value = await readCandidate(version4);
29888
+ if (!sameHash(value.dataHash, wantedHash))
29889
+ requireFence("resuming an occupied unregistered version");
29890
+ }
29891
+ try {
29892
+ const result = await commit(version4, value, "register");
29893
+ if (sameHash(value.dataHash, wantedHash))
29894
+ return result;
29895
+ version4++;
29896
+ } catch (error51) {
29897
+ if (fencedPublication || !(error51 instanceof Error) || !/Gateway error: 409\b/.test(error51.message))
29898
+ throw error51;
29899
+ if (rebased)
29900
+ throw error51;
29901
+ rebased = true;
29902
+ head = await options.readHead();
29903
+ await options.checkHead?.(head);
29904
+ checkDeletion();
29905
+ const next = parseGatewayNextVersion(error51.message);
29906
+ if (!head && next === null)
29907
+ throw error51;
29908
+ const observedNext = BigInt(head?.expectedVersion ?? "0") + 1n;
29909
+ if (next !== null && BigInt(next) > observedNext)
29910
+ requireFence("gateway version hint advances beyond the observed registry head");
29911
+ version4 = observedNext;
29912
+ }
29913
+ }
29914
+ throw new Error(`Publication contention for ${scope}; retry next cycle`);
29915
+ }
29916
+ function parseGatewayNextVersion(message) {
29917
+ const nextValid = message.match(/next valid version is (\d+)/i);
29918
+ if (nextValid)
29919
+ return Number(nextValid[1]);
29920
+ const effectiveMax = message.match(/effective max \((\d+)/i);
29921
+ if (effectiveMax)
29922
+ return Number(effectiveMax[1]) + 1;
29923
+ const storedValue = message.match(/strictly greater than the stored value (\d+)/i);
29924
+ return storedValue ? Number(storedValue[1]) + 1 : null;
29925
+ }
29926
+
29645
29927
  // ../core/dist/additions/version-inventory.js
29646
29928
  var uint256 = external_exports.string().regex(/^(0|[1-9][0-9]{0,77})$/).refine((value) => BigInt(value) < 2n ** 256n);
29647
29929
  var hash2 = external_exports.string().regex(/^0x[0-9a-fA-F]{64}$/).transform((value) => value.toLowerCase());
@@ -29763,7 +30045,7 @@ function createVersionInventoryClient(options) {
29763
30045
  async function list(scope, readOptions = {}) {
29764
30046
  const maxPages = external_exports.number().int().positive().parse(readOptions.maxPages ?? 1e3);
29765
30047
  let after = "0";
29766
- let head = null;
30048
+ let head2 = null;
29767
30049
  const versions = [];
29768
30050
  for (let count = 0; count < maxPages; count++) {
29769
30051
  const result = await page(scope, {
@@ -29771,12 +30053,12 @@ function createVersionInventoryClient(options) {
29771
30053
  signal: readOptions.signal,
29772
30054
  budget: readOptions.budget
29773
30055
  });
29774
- if (count > 0 && JSON.stringify(head) !== JSON.stringify(result.data.head))
30056
+ if (count > 0 && JSON.stringify(head2) !== JSON.stringify(result.data.head))
29775
30057
  throw new Error("Gateway inventory head changed during traversal");
29776
- head = result.data.head;
30058
+ head2 = result.data.head;
29777
30059
  versions.push(...result.data.versions);
29778
30060
  if (!result.pagination.hasMore)
29779
- return { head, versions };
30061
+ return { head: head2, versions };
29780
30062
  const next = result.pagination.nextAfter;
29781
30063
  if (next === null)
29782
30064
  throw new Error("Gateway inventory missing next cursor");
@@ -29818,12 +30100,25 @@ function createVersionInventoryClient(options) {
29818
30100
  }
29819
30101
  throw new Error("Gateway scope page budget exceeded");
29820
30102
  }
29821
- return { page, list, listScopes };
30103
+ async function head(scope, signal) {
30104
+ const current = (await page(scope, { limit: 1, signal })).data.head;
30105
+ return current ? {
30106
+ expectedVersion: current.expectedVersion,
30107
+ deletedAt: current.deletedAt
30108
+ } : null;
30109
+ }
30110
+ return { page, list, listScopes, head };
30111
+ }
30112
+
30113
+ // ../core/dist/replication/namespace.js
30114
+ var REPLICA_SCOPE_PREFIX = "pdpp_replica_v1";
30115
+ function isReplicaScope(scope) {
30116
+ return scope === REPLICA_SCOPE_PREFIX || scope.startsWith(`${REPLICA_SCOPE_PREFIX}.`);
29822
30117
  }
29823
30118
 
29824
30119
  // ../core/dist/policy/reserved-scopes.js
29825
30120
  function isSystemScope(scope) {
29826
- return scope.startsWith("vana.system.");
30121
+ return scope.startsWith("vana.system.") || isReplicaScope(scope);
29827
30122
  }
29828
30123
 
29829
30124
  // ../core/dist/additions/durable.js
@@ -29851,14 +30146,27 @@ function additionsLedgerScope(scope) {
29851
30146
  const literal2 = `${ADDITIONS_SUMMARY_SCOPE}.${scope}`;
29852
30147
  return new TextEncoder().encode(literal2).byteLength <= 256 ? literal2 : `vana.system.additions-hash.${keccak256(stringToHex(scope)).slice(2)}`;
29853
30148
  }
30149
+ var AdditionsRecoveryError = class extends Error {
30150
+ code;
30151
+ constructor(code, message = code) {
30152
+ super(message);
30153
+ this.code = code;
30154
+ this.name = "AdditionsRecoveryError";
30155
+ }
30156
+ };
29854
30157
  var InaccessibleLedger = class extends Error {
29855
30158
  };
30159
+ var CheckpointSearchExceeded = class extends AdditionsBudgetExceeded {
30160
+ };
30161
+ var CHECKPOINT_READ_LIMIT = 256;
29856
30162
  var BoundedWorkUnavailable = class extends Error {
29857
30163
  };
29858
30164
  var ReadBudget = class _ReadBudget extends AdditionsBudget {
29859
30165
  limited;
30166
+ checkpoints;
29860
30167
  constructor(options, parent) {
29861
30168
  super(options.byteBudget, options.timeBudgetMs, options.signal, parent);
30169
+ this.checkpoints = parent?.checkpoints ?? { reads: 0, rejected: /* @__PURE__ */ new Map() };
29862
30170
  this.limited = options.byteBudget !== void 0 || options.timeBudgetMs !== void 0;
29863
30171
  }
29864
30172
  /** A failed source unit cannot spend the bytes held for its durable prefix. */
@@ -29891,6 +30199,27 @@ function sameInventory(a2, b2) {
29891
30199
  function settled(slot) {
29892
30200
  return slot.status === "confirmed" || slot.status === "finalized";
29893
30201
  }
30202
+ function eligible(slot) {
30203
+ return !slot.isTombstone && ["confirmed", "finalized", "pending", "submitting"].includes(slot.status);
30204
+ }
30205
+ function sourceWindow(snapshot) {
30206
+ const end = BigInt(snapshot.head?.expectedVersion ?? "0");
30207
+ const rows = snapshot.versions.filter((slot) => BigInt(slot.expectedVersion) <= end);
30208
+ const after = BigInt(rows.filter((slot) => slot.isTombstone && settled(slot)).at(-1)?.expectedVersion ?? "0");
30209
+ return {
30210
+ end,
30211
+ after,
30212
+ relevant: rows.filter((slot) => BigInt(slot.expectedVersion) > after)
30213
+ };
30214
+ }
30215
+ function findCheckpointRegistration(snapshot, version4, commitment) {
30216
+ if (!snapshot.head || snapshot.head.deletedAt !== null)
30217
+ return void 0;
30218
+ const { end, after } = sourceWindow(snapshot);
30219
+ if (BigInt(version4) > end || BigInt(version4) <= after)
30220
+ return void 0;
30221
+ return snapshot.versions.find((row) => row.expectedVersion === version4 && eligible(row) && row.dataHash.toLowerCase() === commitment.dataHash.toLowerCase() && row.metadataHash.toLowerCase() === commitment.metadataHash.toLowerCase());
30222
+ }
29894
30223
  function sourceData(data) {
29895
30224
  if (data === null || typeof data !== "object" || Array.isArray(data))
29896
30225
  return null;
@@ -29908,19 +30237,31 @@ function createDurableAdditions(options) {
29908
30237
  if (!budget.limited)
29909
30238
  budget.check(encrypted.byteLength);
29910
30239
  const password = hex3(deriveScopeKey(options.masterKey, scope));
29911
- const plaintext = budget.limited && options.boundedWork ? await options.boundedWork.decrypt(encrypted, password, budget) : await decryptWithPassword(encrypted, password);
30240
+ let plaintext;
30241
+ try {
30242
+ plaintext = budget.limited && options.boundedWork ? await options.boundedWork.decrypt(encrypted, password, budget) : await decryptWithPassword(encrypted, password);
30243
+ } catch (error51) {
30244
+ if (error51 instanceof AdditionsRecoveryError || error51 instanceof AdditionsBudgetExceeded || budget.signal?.aborted)
30245
+ throw error51;
30246
+ throw new AdditionsRecoveryError("decrypt_failed");
30247
+ }
29912
30248
  budget.check(plaintext.byteLength);
29913
30249
  if (keccak256(plaintext).toLowerCase() !== slot.dataHash.toLowerCase())
29914
- throw new Error("Additions content commitment mismatch");
30250
+ throw new AdditionsRecoveryError("data_hash_mismatch", "Additions content commitment mismatch");
29915
30251
  budget.check(plaintext.byteLength * 3);
29916
- const envelope = DataFileEnvelopeSchema.parse(JSON.parse(new TextDecoder().decode(plaintext)));
30252
+ let envelope;
30253
+ try {
30254
+ envelope = DataFileEnvelopeSchema.parse(JSON.parse(new TextDecoder().decode(plaintext)));
30255
+ } catch {
30256
+ throw new AdditionsRecoveryError("invalid_envelope");
30257
+ }
29917
30258
  const metadataHash = keccak256(stringToHex(JSON.stringify({
29918
30259
  scope,
29919
30260
  collectedAt: envelope.collectedAt,
29920
30261
  sizeBytes: encrypted.byteLength
29921
30262
  })));
29922
30263
  if (envelope.scope !== scope || !Number.isFinite(Date.parse(envelope.collectedAt)) || metadataHash.toLowerCase() !== slot.metadataHash.toLowerCase())
29923
- throw new Error("Additions metadata commitment mismatch");
30264
+ throw new AdditionsRecoveryError("metadata_hash_mismatch", "Additions metadata commitment mismatch");
29924
30265
  budget.check();
29925
30266
  return envelope;
29926
30267
  }
@@ -29931,21 +30272,50 @@ function createDurableAdditions(options) {
29931
30272
  const head = snapshot.head;
29932
30273
  if (!head || head.deletedAt !== null)
29933
30274
  return null;
29934
- const slot = snapshot.versions.find((row) => row.expectedVersion === head.expectedVersion && !row.isTombstone && row.dataHash === head.dataHash && row.metadataHash === head.metadataHash);
30275
+ const slot = findCheckpointRegistration(snapshot, head.expectedVersion, head);
29935
30276
  if (!slot)
29936
30277
  throw new Error("Additions head missing from inventory");
29937
30278
  return { snapshot, envelope: await readEnvelope(scope, slot, budget) };
29938
30279
  }
29939
- async function readDocument(scope, budget) {
30280
+ async function readDocument(scope, budget, source, search) {
30281
+ const { relevant, after } = sourceWindow(source);
29940
30282
  const systemScope = additionsLedgerScope(scope);
29941
30283
  const snapshot = await inventory.list(systemScope, budget.readOptions);
29942
30284
  if (!snapshot.head || snapshot.head.deletedAt !== null)
29943
30285
  return null;
29944
- const boundary = snapshot.versions.filter((row) => row.isTombstone).at(-1)?.expectedVersion ?? "0";
30286
+ const boundary = snapshot.versions.filter((row) => row.isTombstone && settled(row) && BigInt(row.expectedVersion) <= BigInt(snapshot.head.expectedVersion)).at(-1)?.expectedVersion ?? "0";
29945
30287
  let inaccessible = false;
29946
- for (const slot of [...snapshot.versions].reverse()) {
29947
- if (BigInt(slot.expectedVersion) <= BigInt(boundary) || BigInt(slot.expectedVersion) > BigInt(snapshot.head.expectedVersion) || slot.isTombstone)
30288
+ const sourceFingerprint = fingerprintVersionInventory(source);
30289
+ for (let index = snapshot.versions.length - 1; index >= 0; index--) {
30290
+ budget.check();
30291
+ const slot = snapshot.versions[index];
30292
+ if (BigInt(slot.expectedVersion) <= BigInt(boundary) || BigInt(slot.expectedVersion) > BigInt(snapshot.head.expectedVersion) || search && BigInt(slot.expectedVersion) >= BigInt(search.before) || !eligible(slot))
29948
30293
  continue;
30294
+ const identity = JSON.stringify([
30295
+ systemScope,
30296
+ slot.expectedVersion,
30297
+ slot.dataHash.toLowerCase(),
30298
+ slot.metadataHash.toLowerCase(),
30299
+ sourceFingerprint
30300
+ ]);
30301
+ const rejected = budget.checkpoints.rejected.get(identity);
30302
+ if (rejected !== void 0) {
30303
+ inaccessible ||= rejected === "inaccessible";
30304
+ continue;
30305
+ }
30306
+ if (budget.checkpoints.reads >= CHECKPOINT_READ_LIMIT) {
30307
+ try {
30308
+ options.onRecoveryDiagnostic?.({
30309
+ scope,
30310
+ version: slot.expectedVersion,
30311
+ code: "checkpoint_search_exhausted"
30312
+ });
30313
+ } catch {
30314
+ }
30315
+ throw new CheckpointSearchExceeded("Retained checkpoint search exhausted");
30316
+ }
30317
+ budget.check(identity.length * 2 + 64);
30318
+ budget.checkpoints.reads++;
29949
30319
  let envelope;
29950
30320
  try {
29951
30321
  envelope = await readEnvelope(systemScope, slot, budget);
@@ -29953,18 +30323,30 @@ function createDurableAdditions(options) {
29953
30323
  if (error51 instanceof AdditionsBudgetExceeded || budget.signal?.aborted)
29954
30324
  throw error51;
29955
30325
  inaccessible = true;
30326
+ budget.checkpoints.rejected.set(identity, "inaccessible");
29956
30327
  continue;
29957
30328
  }
29958
30329
  const parsed = documentSchema.safeParse(envelope.data);
29959
- if (!parsed.success || parsed.data.owner !== owner || parsed.data.network !== options.network || parsed.data.scope !== scope)
30330
+ if (!parsed.success || parsed.data.owner !== owner || parsed.data.network !== options.network || parsed.data.scope !== scope) {
30331
+ budget.checkpoints.rejected.set(identity, "invalid");
29960
30332
  continue;
30333
+ }
29961
30334
  const ledger = parsed.data.ledger === null ? null : readScopeFirstSeenLedger(parsed.data.ledger);
29962
- if (parsed.data.ledger !== null && (!ledger || ledger.scope !== scope))
30335
+ if (parsed.data.ledger !== null && (!ledger || ledger.scope !== scope)) {
30336
+ budget.checkpoints.rejected.set(identity, "invalid");
30337
+ continue;
30338
+ }
30339
+ const candidate = { ...parsed.data, ledger };
30340
+ if (candidate.slots.length > relevant.length || !candidate.slots.every((row, i2) => eligible(relevant[i2]) && sameCommitment(row, relevant[i2])) || candidate.through !== (candidate.slots.at(-1)?.expectedVersion ?? String(after))) {
30341
+ budget.checkpoints.rejected.set(identity, "invalid");
30342
+ continue;
30343
+ }
30344
+ if (search && candidate.slots.length <= search.longerThan)
29963
30345
  continue;
29964
30346
  return {
29965
- ...parsed.data,
29966
- ledger,
29967
- ...slot.expectedVersion !== snapshot.head.expectedVersion ? { repairHead: true } : {}
30347
+ document: candidate,
30348
+ publicationVersion: slot.expectedVersion,
30349
+ repairHead: slot.expectedVersion !== snapshot.head.expectedVersion
29968
30350
  };
29969
30351
  }
29970
30352
  if (inaccessible)
@@ -29976,48 +30358,44 @@ function createDurableAdditions(options) {
29976
30358
  if (budget.limited) {
29977
30359
  if (!options.boundedWork)
29978
30360
  throw new BoundedWorkUnavailable("Bounded checkpoint publication unavailable");
29979
- const proof = await options.boundedWork.publish(scope, data, budget);
30361
+ const proof2 = await options.boundedWork.publish(scope, data, budget);
29980
30362
  budget.check();
29981
30363
  const check3 = await inventory.list(scope, budget.readOptions);
29982
- const slot = check3.versions.find((row) => row.expectedVersion === proof.expectedVersion && row.dataHash === proof.dataHash && row.metadataHash === proof.metadataHash);
30364
+ const slot = findCheckpointRegistration(check3, proof2.expectedVersion, proof2);
29983
30365
  if (!slot)
29984
30366
  throw new Error("Bounded checkpoint registration was replaced");
29985
- const envelope2 = await readEnvelope(scope, slot, budget);
29986
- if (JSON.stringify(envelope2.data) !== JSON.stringify(data))
30367
+ const envelope = await readEnvelope(scope, slot, budget);
30368
+ if (JSON.stringify(envelope.data) !== JSON.stringify(data))
29987
30369
  throw new Error("Bounded checkpoint content was replaced");
29988
30370
  budget.check();
29989
30371
  await options.boundedWork.confirmed?.(scope, budget);
29990
30372
  return;
29991
30373
  }
29992
30374
  const snapshot = await inventory.list(scope, budget.readOptions);
29993
- const version4 = BigInt(snapshot.versions.at(-1)?.expectedVersion ?? "0") + 1n;
29994
- const envelope = createDataFileEnvelope(scope, (/* @__PURE__ */ new Date()).toISOString(), data);
29995
- const plaintext = new TextEncoder().encode(JSON.stringify(envelope));
29996
- const encrypted = await encryptWithPassword(plaintext, hex3(deriveScopeKey(options.masterKey, scope)));
29997
- const dataHash = keccak256(plaintext);
29998
- const metadataHash = keccak256(stringToHex(JSON.stringify({
29999
- scope,
30000
- collectedAt: envelope.collectedAt,
30001
- sizeBytes: encrypted.byteLength
30002
- })));
30003
- await storageAdapter.upload(`${scope}/${version4}`, encrypted);
30004
- const signature = await options.signer.signAddData({
30005
- ownerAddress: getAddress(owner),
30006
- scope,
30007
- dataHash,
30008
- metadataHash,
30009
- expectedVersion: version4
30010
- });
30011
- await options.gateway.registerDataPoint({
30375
+ const toHead = (state) => state.head ? {
30376
+ ...state.head,
30377
+ id: computeDataPointId(owner, scope)
30378
+ } : null;
30379
+ const proof = await publishVersion({
30012
30380
  ownerAddress: owner,
30013
- scope,
30014
- dataHash,
30015
- metadataHash,
30016
- expectedVersion: String(version4),
30017
- signature
30381
+ envelope: createDataFileEnvelope(scope, (/* @__PURE__ */ new Date()).toISOString(), data),
30382
+ preferredVersion: BigInt(snapshot.versions.at(-1)?.expectedVersion ?? "0") + 1n,
30383
+ masterKey: options.masterKey,
30384
+ storageAdapter,
30385
+ signer: options.signer,
30386
+ gateway: options.gateway,
30387
+ fencedPublication: options.fencedPublication,
30388
+ head: toHead(snapshot),
30389
+ readHead: async () => toHead(await inventory.list(scope, budget.readOptions)),
30390
+ // Rebuilding an explicitly deleted system document is a deliberate re-add.
30391
+ acknowledgedDeletionVersion: snapshot.head?.deletedAt ? BigInt(snapshot.head.expectedVersion) : void 0
30392
+ }).catch((error51) => {
30393
+ if (error51 instanceof NeedsFencedPublicationError)
30394
+ options.onPublicationBlocked?.(error51);
30395
+ throw error51;
30018
30396
  });
30019
30397
  const check2 = await inventory.list(scope, budget.readOptions);
30020
- const registered = check2.versions.find((slot) => slot.expectedVersion === String(version4) && slot.dataHash === dataHash && slot.metadataHash === metadataHash);
30398
+ const registered = findCheckpointRegistration(check2, String(proof.version), proof);
30021
30399
  if (!registered)
30022
30400
  throw new Error("Concurrent additions publication replaced the registration");
30023
30401
  await readEnvelope(scope, registered, budget);
@@ -30038,21 +30416,18 @@ function createDurableAdditions(options) {
30038
30416
  coverage: !sameInventory(snapshot, check2) ? "behind" : head ? settled(head) && tombstone ? "complete" : "partial" : snapshot.versions.length ? "partial" : "complete"
30039
30417
  };
30040
30418
  }
30041
- const end = BigInt(head.expectedVersion);
30042
- const rows = snapshot.versions.filter((slot) => BigInt(slot.expectedVersion) <= end);
30043
- const boundary = rows.filter((slot) => slot.isTombstone && settled(slot)).at(-1);
30044
- const after = BigInt(boundary?.expectedVersion ?? "0");
30045
- const relevant = rows.filter((slot) => BigInt(slot.expectedVersion) > after);
30046
- let existing = null;
30419
+ const { end, after, relevant } = sourceWindow(snapshot);
30420
+ let checkpoint = null;
30047
30421
  let inaccessibleExisting = false;
30048
30422
  try {
30049
- existing = await readDocument(scope, budget);
30423
+ checkpoint = await readDocument(scope, budget, snapshot);
30050
30424
  } catch (error51) {
30051
30425
  if (error51 instanceof AdditionsBudgetExceeded || budget.signal?.aborted)
30052
30426
  throw error51;
30053
30427
  inaccessibleExisting = true;
30054
30428
  }
30055
- const prefixValid = existing !== null && existing.slots.length <= relevant.length && existing.slots.every((slot, i2) => sameCommitment(slot, relevant[i2])) && existing.through === (existing.slots.at(-1)?.expectedVersion ?? String(after));
30429
+ let existing = checkpoint?.document ?? null;
30430
+ const prefixValid = existing !== null;
30056
30431
  let ledger = prefixValid ? existing.ledger : null;
30057
30432
  const folded = prefixValid ? relevant.slice(0, existing.slots.length) : [];
30058
30433
  let currentTotal = prefixValid ? existing.currentTotal : 0;
@@ -30065,6 +30440,7 @@ function createDurableAdditions(options) {
30065
30440
  coverage = "partial";
30066
30441
  expected = BigInt(slot.expectedVersion) + 1n;
30067
30442
  }
30443
+ const sourceCoverage = coverage;
30068
30444
  const checkpointBytes = (candidate, count) => 128 * 1024 + 128 * new TextEncoder().encode(JSON.stringify({
30069
30445
  ledger: candidate,
30070
30446
  slots: relevant.slice(0, count),
@@ -30074,17 +30450,15 @@ function createDurableAdditions(options) {
30074
30450
  through,
30075
30451
  currentTotal
30076
30452
  })).byteLength;
30077
- for (const slot of relevant.slice(folded.length)) {
30453
+ while (folded.length < relevant.length) {
30454
+ const slot = relevant[folded.length];
30455
+ let unitBudget = budget;
30078
30456
  try {
30079
- const unitBudget = budget.limited ? budget.excluding(checkpointBytes(ledger, folded.length)) : budget;
30457
+ unitBudget = budget.limited ? budget.excluding(checkpointBytes(ledger, folded.length)) : budget;
30080
30458
  unitBudget.check();
30081
30459
  if (BigInt(slot.expectedVersion) !== BigInt(through) + 1n)
30082
30460
  coverage = "partial";
30083
- if (slot.status === "failed" || !["confirmed", "finalized", "pending", "submitting"].includes(slot.status)) {
30084
- coverage = "partial";
30085
- break;
30086
- }
30087
- if (slot.isTombstone) {
30461
+ if (!eligible(slot)) {
30088
30462
  coverage = "partial";
30089
30463
  break;
30090
30464
  }
@@ -30092,21 +30466,21 @@ function createDurableAdditions(options) {
30092
30466
  const proof = uploaded?.proof;
30093
30467
  const inMemory = proof ? DataFileEnvelopeSchema.safeParse(proof.envelope) : null;
30094
30468
  const bytes = proof ? new TextEncoder().encode(JSON.stringify(proof.envelope)) : null;
30095
- const metadata = inMemory?.success && proof ? keccak256(stringToHex(JSON.stringify({
30469
+ const metadata2 = inMemory?.success && proof ? keccak256(stringToHex(JSON.stringify({
30096
30470
  scope,
30097
30471
  collectedAt: inMemory.data.collectedAt,
30098
30472
  sizeBytes: proof.encryptedSize
30099
30473
  }))) : null;
30100
- if (inMemory?.success && bytes && inMemory.data.scope === scope && keccak256(bytes) === slot.dataHash && metadata === slot.metadataHash) {
30474
+ if (inMemory?.success && bytes && inMemory.data.scope === scope && keccak256(bytes) === slot.dataHash && metadata2 === slot.metadataHash) {
30101
30475
  const data = sourceData(inMemory.data.data);
30102
30476
  if (!data)
30103
- throw new Error("Unsupported source envelope");
30477
+ throw new AdditionsRecoveryError("unsupported_source_data");
30104
30478
  version4 = { scope, collectedAt: inMemory.data.collectedAt, data };
30105
30479
  } else {
30106
30480
  const envelope = await readEnvelope(scope, slot, unitBudget);
30107
30481
  const data = sourceData(envelope.data);
30108
30482
  if (!data)
30109
- throw new Error("Unsupported source envelope");
30483
+ throw new AdditionsRecoveryError("unsupported_source_data");
30110
30484
  version4 = { scope, collectedAt: envelope.collectedAt, data };
30111
30485
  }
30112
30486
  const hadBaseline = ledger !== null && ledger.baseline !== null;
@@ -30124,7 +30498,39 @@ function createDurableAdditions(options) {
30124
30498
  folded.push(slot);
30125
30499
  through = slot.expectedVersion;
30126
30500
  } catch (error51) {
30127
- coverage = error51 instanceof AdditionsBudgetExceeded || budget.signal?.aborted ? "behind" : "partial";
30501
+ if (checkpoint && !budget.signal?.aborted) {
30502
+ const searchBudget = budget.limited ? budget.excluding(checkpointBytes(ledger, folded.length)) : budget;
30503
+ try {
30504
+ const fallback = await readDocument(scope, searchBudget, snapshot, {
30505
+ before: checkpoint.publicationVersion,
30506
+ longerThan: folded.length
30507
+ });
30508
+ if (fallback) {
30509
+ checkpoint = fallback;
30510
+ existing = fallback.document;
30511
+ ledger = existing.ledger;
30512
+ currentTotal = existing.currentTotal;
30513
+ through = existing.through;
30514
+ folded.splice(0, folded.length, ...relevant.slice(0, existing.slots.length));
30515
+ coverage = sourceCoverage;
30516
+ continue;
30517
+ }
30518
+ } catch (failure) {
30519
+ if (failure instanceof AdditionsBudgetExceeded)
30520
+ error51 = failure;
30521
+ else if (searchBudget.signal?.aborted)
30522
+ error51 = new AdditionsBudgetExceeded("Checkpoint fallback allowance exhausted");
30523
+ }
30524
+ }
30525
+ try {
30526
+ options.onRecoveryDiagnostic?.({
30527
+ scope,
30528
+ version: slot.expectedVersion,
30529
+ code: error51 instanceof AdditionsBudgetExceeded || unitBudget.signal?.aborted && unitBudget.signal.reason instanceof DOMException && unitBudget.signal.reason.name === "TimeoutError" ? "budget_exceeded" : budget.signal?.aborted ? "cancelled" : error51 instanceof AdditionsRecoveryError ? error51.code : "replay_failed"
30530
+ });
30531
+ } catch {
30532
+ }
30533
+ coverage = error51 instanceof AdditionsBudgetExceeded || budget.signal?.aborted || unitBudget.signal?.aborted ? "behind" : "partial";
30128
30534
  break;
30129
30535
  }
30130
30536
  }
@@ -30153,7 +30559,7 @@ function createDurableAdditions(options) {
30153
30559
  slots: folded,
30154
30560
  ledger
30155
30561
  };
30156
- if ((!existing && !inaccessibleExisting || prefixValid || coverage === "complete") && JSON.stringify(document2) !== JSON.stringify(existing)) {
30562
+ if ((!existing && !inaccessibleExisting || prefixValid || coverage === "complete") && (checkpoint?.repairHead || JSON.stringify(document2) !== JSON.stringify(existing))) {
30157
30563
  try {
30158
30564
  await publish(additionsLedgerScope(scope), { ...document2 }, budget);
30159
30565
  } catch {
@@ -30261,9 +30667,9 @@ function createDurableAdditions(options) {
30261
30667
  return;
30262
30668
  if (proof && keccak256(new TextEncoder().encode(JSON.stringify(proof.envelope))) !== head.dataHash)
30263
30669
  return;
30264
- const previous = await readDocument(version4.scope, budget);
30265
- const boundary = snapshot.versions.filter((slot) => slot.isTombstone && settled(slot)).at(-1)?.expectedVersion ?? "0";
30266
- const startsFresh = BigInt(head.expectedVersion) === BigInt(boundary) + 1n;
30670
+ const previous = (await readDocument(version4.scope, budget, snapshot))?.document;
30671
+ const { after } = sourceWindow(snapshot);
30672
+ const startsFresh = BigInt(head.expectedVersion) === after + 1n;
30267
30673
  if (!startsFresh && (!previous || BigInt(previous.through) + 1n !== BigInt(head.expectedVersion)))
30268
30674
  return;
30269
30675
  const uploaded = !budget.limited && "collectedAt" in version4 && "data" in version4 ? { version: version4, proof } : void 0;
@@ -30271,9 +30677,13 @@ function createDurableAdditions(options) {
30271
30677
  } catch {
30272
30678
  }
30273
30679
  },
30274
- async deleteScope(scope) {
30680
+ async deleteScope(scope, deletion) {
30681
+ deletion?.signal?.throwIfAborted();
30682
+ options.signal?.throwIfAborted();
30275
30683
  if (isSystemScope(scope))
30276
30684
  return;
30685
+ if (deletion?.durableOnly)
30686
+ throw new Error("Bounded additions deletion is unavailable");
30277
30687
  if (!options.deleteSystemScope)
30278
30688
  throw new Error("Durable additions deletion is unavailable");
30279
30689
  await options.deleteSystemScope(additionsLedgerScope(scope));
@@ -30300,12 +30710,24 @@ async function decryptBoundedAdditions(encrypted, password, budget) {
30300
30710
  ignoreMalformedPackets: false
30301
30711
  };
30302
30712
  const message = await no({ binaryMessage: encrypted, config: config2 });
30303
- const result = await po({
30304
- message,
30305
- passwords: [password],
30306
- format: "binary",
30307
- config: config2
30308
- });
30713
+ let result;
30714
+ try {
30715
+ result = await po({
30716
+ message,
30717
+ passwords: [password],
30718
+ format: "binary",
30719
+ config: config2
30720
+ });
30721
+ } catch (error51) {
30722
+ if (error51 instanceof Error && error51.message.includes(
30723
+ "Compressed OpenPGP packets are unsupported in bounded recovery"
30724
+ ))
30725
+ throw new AdditionsRecoveryError(
30726
+ "compressed_packet",
30727
+ "Compressed OpenPGP packets are unsupported in bounded recovery"
30728
+ );
30729
+ throw error51;
30730
+ }
30309
30731
  budget.charge();
30310
30732
  if (!(result.data instanceof Uint8Array))
30311
30733
  throw new Error("Unexpected OpenPGP stream");
@@ -30388,11 +30810,12 @@ function createService(init) {
30388
30810
  const reconciledScopes = /* @__PURE__ */ new Set();
30389
30811
  async function reconcilePending(scope, budget) {
30390
30812
  if (reconciledScopes.has(scope)) return null;
30391
- const prior = await transport("readPending", [scope], budget);
30392
- if (!prior) {
30813
+ const pending2 = await transport("readPending", [scope], budget);
30814
+ if (!pending2) {
30393
30815
  reconciledScopes.add(scope);
30394
30816
  return null;
30395
30817
  }
30818
+ let prior = pending2;
30396
30819
  let snapshot = await inventory.list(scope, {
30397
30820
  budget,
30398
30821
  signal: budget.signal
@@ -30404,7 +30827,8 @@ function createService(init) {
30404
30827
  if (slot && replaced && !["confirmed", "finalized"].includes(slot.status))
30405
30828
  throw new Error("Pending checkpoint replacement is not settled");
30406
30829
  if (!slot) {
30407
- await transport("publish", [prior], budget);
30830
+ const published = await transport("publish", [prior], budget);
30831
+ prior = { ...prior, expectedVersion: published.expectedVersion };
30408
30832
  snapshot = await inventory.list(scope, {
30409
30833
  budget,
30410
30834
  signal: budget.signal
@@ -30413,7 +30837,7 @@ function createService(init) {
30413
30837
  (row) => row.expectedVersion === prior.expectedVersion
30414
30838
  );
30415
30839
  }
30416
- if (!slot || !replaced && (slot.dataHash !== prior.dataHash || slot.metadataHash !== prior.metadataHash))
30840
+ if (!slot || !replaced && !findCheckpointRegistration(snapshot, prior.expectedVersion, prior))
30417
30841
  throw new Error("Interrupted checkpoint registration remains ambiguous");
30418
30842
  const proof = replaced ? slot : prior;
30419
30843
  let data;
@@ -30445,18 +30869,17 @@ function createService(init) {
30445
30869
  throw new Error("Interrupted checkpoint commitment mismatch");
30446
30870
  data = envelope.data;
30447
30871
  }
30448
- if (replaced) {
30449
- const latest = await inventory.list(scope, {
30450
- budget,
30451
- signal: budget.signal
30452
- });
30453
- if (!latest.versions.some(
30454
- (row) => row.expectedVersion === proof.expectedVersion && row.dataHash === proof.dataHash && row.metadataHash === proof.metadataHash && ["confirmed", "finalized"].includes(row.status)
30455
- ))
30456
- throw new Error(
30457
- "Pending checkpoint replacement changed during verification"
30458
- );
30459
- }
30872
+ const latest = await inventory.list(scope, {
30873
+ budget,
30874
+ signal: budget.signal
30875
+ });
30876
+ const visible = slot.isTombstone ? latest.head && BigInt(proof.expectedVersion) <= BigInt(latest.head.expectedVersion) && latest.versions.find(
30877
+ (row) => row.expectedVersion === proof.expectedVersion && row.dataHash === proof.dataHash && row.metadataHash === proof.metadataHash && row.isTombstone
30878
+ ) : findCheckpointRegistration(latest, proof.expectedVersion, proof);
30879
+ if (!visible || replaced && !["confirmed", "finalized"].includes(visible.status))
30880
+ throw new Error(
30881
+ "Interrupted checkpoint registration changed during verification"
30882
+ );
30460
30883
  await transport("clearPending", [scope], budget);
30461
30884
  reconciledScopes.add(scope);
30462
30885
  return replaced ? null : { proof: prior, data };
@@ -30475,10 +30898,21 @@ function createService(init) {
30475
30898
  listScopes: async (options) => {
30476
30899
  activeBudget = options?.budget;
30477
30900
  return inventory.listScopes(options);
30901
+ },
30902
+ head: (scope, signal) => {
30903
+ activeBudget = void 0;
30904
+ return inventory.head(scope, signal);
30478
30905
  }
30479
30906
  };
30907
+ const reported = /* @__PURE__ */ new Set();
30480
30908
  return createDurableAdditions({
30481
30909
  ...init,
30910
+ onRecoveryDiagnostic: init.diagnostics ? (event) => {
30911
+ const key = JSON.stringify(event);
30912
+ if (reported.size >= 32 || reported.has(key)) return;
30913
+ reported.add(key);
30914
+ worker.postMessage({ kind: "diagnostic", event });
30915
+ } : void 0,
30482
30916
  inventory: boundedInventory,
30483
30917
  storageAdapter: {
30484
30918
  urlForKey: (key) => key,
@@ -30550,8 +30984,7 @@ function createService(init) {
30550
30984
  )
30551
30985
  )
30552
30986
  };
30553
- await transport("publish", [prepared], budget);
30554
- return prepared;
30987
+ return transport("publish", [prepared], budget);
30555
30988
  }
30556
30989
  }
30557
30990
  });
@@ -30566,7 +30999,11 @@ worker.onmessage = (event) => {
30566
30999
  request?.budget?.charge();
30567
31000
  if (message.error)
30568
31001
  request?.reject(
30569
- message.budgetExceeded ? new AdditionsBudgetExceeded(message.error) : new Error(message.error)
31002
+ message.budgetExceeded ? new AdditionsBudgetExceeded(message.error) : message.publicationBlocked ? new NeedsFencedPublicationError(
31003
+ message.publicationBlocked.scope,
31004
+ message.publicationBlocked.reason,
31005
+ false
31006
+ ) : new Error(message.error)
30570
31007
  );
30571
31008
  else request?.resolve(message.result);
30572
31009
  } catch (error51) {