@opendatalabs/personal-server-ts-server 1.32.0 → 1.33.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.
@@ -43409,10 +43409,10 @@ function concatBytes(...arrays) {
43409
43409
  sum += a10.length;
43410
43410
  }
43411
43411
  const res = new Uint8Array(sum);
43412
- for (let i10 = 0, pad2 = 0; i10 < arrays.length; i10++) {
43412
+ for (let i10 = 0, pad3 = 0; i10 < arrays.length; i10++) {
43413
43413
  const a10 = arrays[i10];
43414
- res.set(a10, pad2);
43415
- pad2 += a10.length;
43414
+ res.set(a10, pad3);
43415
+ pad3 += a10.length;
43416
43416
  }
43417
43417
  return res;
43418
43418
  }
@@ -44910,16 +44910,16 @@ var init_hmac = __esm({
44910
44910
  this.blockLen = this.iHash.blockLen;
44911
44911
  this.outputLen = this.iHash.outputLen;
44912
44912
  const blockLen = this.blockLen;
44913
- const pad2 = new Uint8Array(blockLen);
44914
- pad2.set(key.length > blockLen ? hash2.create().update(key).digest() : key);
44915
- for (let i10 = 0; i10 < pad2.length; i10++)
44916
- pad2[i10] ^= 54;
44917
- this.iHash.update(pad2);
44913
+ const pad3 = new Uint8Array(blockLen);
44914
+ pad3.set(key.length > blockLen ? hash2.create().update(key).digest() : key);
44915
+ for (let i10 = 0; i10 < pad3.length; i10++)
44916
+ pad3[i10] ^= 54;
44917
+ this.iHash.update(pad3);
44918
44918
  this.oHash = hash2.create();
44919
- for (let i10 = 0; i10 < pad2.length; i10++)
44920
- pad2[i10] ^= 54 ^ 92;
44921
- this.oHash.update(pad2);
44922
- clean(pad2);
44919
+ for (let i10 = 0; i10 < pad3.length; i10++)
44920
+ pad3[i10] ^= 54 ^ 92;
44921
+ this.oHash.update(pad3);
44922
+ clean(pad3);
44923
44923
  }
44924
44924
  update(buf) {
44925
44925
  aexists(this);
@@ -45066,10 +45066,10 @@ function concatBytes3(...arrays) {
45066
45066
  sum += a10.length;
45067
45067
  }
45068
45068
  const res = new Uint8Array(sum);
45069
- for (let i10 = 0, pad2 = 0; i10 < arrays.length; i10++) {
45069
+ for (let i10 = 0, pad3 = 0; i10 < arrays.length; i10++) {
45070
45070
  const a10 = arrays[i10];
45071
- res.set(a10, pad2);
45072
- pad2 += a10.length;
45071
+ res.set(a10, pad3);
45072
+ pad3 += a10.length;
45073
45073
  }
45074
45074
  return res;
45075
45075
  }
@@ -71384,13 +71384,13 @@ var require_cipher = __commonJS({
71384
71384
  }
71385
71385
  this._input.compact();
71386
71386
  };
71387
- BlockCipher.prototype.finish = function(pad2) {
71388
- if (pad2 && (this.mode.name === "ECB" || this.mode.name === "CBC")) {
71387
+ BlockCipher.prototype.finish = function(pad3) {
71388
+ if (pad3 && (this.mode.name === "ECB" || this.mode.name === "CBC")) {
71389
71389
  this.mode.pad = function(input) {
71390
- return pad2(this.blockSize, input, false);
71390
+ return pad3(this.blockSize, input, false);
71391
71391
  };
71392
71392
  this.mode.unpad = function(output) {
71393
- return pad2(this.blockSize, output, true);
71393
+ return pad3(this.blockSize, output, true);
71394
71394
  };
71395
71395
  }
71396
71396
  var options = {};
@@ -75188,11 +75188,11 @@ var require_rc2 = __commonJS({
75188
75188
  *
75189
75189
  * @return true if successful, false on error.
75190
75190
  */
75191
- finish: function(pad2) {
75191
+ finish: function(pad3) {
75192
75192
  var rval = true;
75193
75193
  if (encrypt) {
75194
- if (pad2) {
75195
- rval = pad2(8, _input, !encrypt);
75194
+ if (pad3) {
75195
+ rval = pad3(8, _input, !encrypt);
75196
75196
  } else {
75197
75197
  var padding = _input.length() === 8 ? 8 : 8 - _input.length();
75198
75198
  _input.fillWithByte(padding, padding);
@@ -75205,8 +75205,8 @@ var require_rc2 = __commonJS({
75205
75205
  if (!encrypt) {
75206
75206
  rval = _input.length() === 0;
75207
75207
  if (rval) {
75208
- if (pad2) {
75209
- rval = pad2(8, _output, !encrypt);
75208
+ if (pad3) {
75209
+ rval = pad3(8, _output, !encrypt);
75210
75210
  } else {
75211
75211
  var len = _output.length();
75212
75212
  var count = _output.at(len - 1);
@@ -75579,7 +75579,7 @@ var require_jsbn = __commonJS({
75579
75579
  return;
75580
75580
  }
75581
75581
  if (r10 == null) r10 = nbi();
75582
- var y10 = nbi(), ts3 = this.s, ms3 = m10.s;
75582
+ var y10 = nbi(), ts3 = this.s, ms4 = m10.s;
75583
75583
  var nsh = this.DB - nbits(pm2.data[pm2.t - 1]);
75584
75584
  if (nsh > 0) {
75585
75585
  pm2.lShiftTo(nsh, y10);
@@ -75612,7 +75612,7 @@ var require_jsbn = __commonJS({
75612
75612
  }
75613
75613
  if (q10 != null) {
75614
75614
  r10.drShiftTo(ys3, q10);
75615
- if (ts3 != ms3) BigInteger.ZERO.subTo(q10, q10);
75615
+ if (ts3 != ms4) BigInteger.ZERO.subTo(q10, q10);
75616
75616
  }
75617
75617
  r10.t = ys3;
75618
75618
  r10.clamp();
@@ -92919,12 +92919,12 @@ function field(value, key) {
92919
92919
  function arrayLength(value) {
92920
92920
  return Array.isArray(value) ? value.length : 0;
92921
92921
  }
92922
- function nestedShortfall(scope, legacyItems, projectedItems) {
92922
+ function nestedShortfall(scope, legacyItems2, projectedItems) {
92923
92923
  const nested = NESTED_ITEMS[scope];
92924
92924
  if (!nested)
92925
92925
  return void 0;
92926
92926
  const projectedById = new Map(projectedItems.map((item) => [itemId(item), nested(item).held]));
92927
- const short = legacyItems.filter((item) => (projectedById.get(itemId(item)) ?? 0) < nested(item).held);
92927
+ const short = legacyItems2.filter((item) => (projectedById.get(itemId(item)) ?? 0) < nested(item).held);
92928
92928
  const incomplete = projectedItems.filter((item) => nested(item).selfShort);
92929
92929
  const count = short.length + incomplete.length;
92930
92930
  return count > 0 ? `the projection holds fewer nested rows than the source or the newest legacy version for ${count} of the ${legacyCollectionKey(scope)}` : void 0;
@@ -93984,7 +93984,7 @@ var isBytes3 = (a10) => a10 instanceof Uint8Array || ArrayBuffer.isView(a10) &&
93984
93984
  var abytes3 = (a10, l10) => !isBytes3(a10) || typeof l10 === "number" && l10 > 0 && a10.length !== l10 ? err("Uint8Array expected") : a10;
93985
93985
  var u8n2 = (len) => new Uint8Array(len);
93986
93986
  var u8fr2 = (buf) => Uint8Array.from(buf);
93987
- var padh = (n10, pad2) => n10.toString(16).padStart(pad2, "0");
93987
+ var padh = (n10, pad3) => n10.toString(16).padStart(pad3, "0");
93988
93988
  var bytesToHex4 = (b10) => Array.from(abytes3(b10)).map((e10) => padh(e10, 2)).join("");
93989
93989
  var C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
93990
93990
  var _ch = (ch3) => {
@@ -94018,10 +94018,10 @@ var toU8 = (a10, len) => abytes3(isStr(a10) ? hexToBytes3(a10) : u8fr2(abytes3(a
94018
94018
  var cr2 = () => globalThis?.crypto;
94019
94019
  var concatBytes4 = (...arrs) => {
94020
94020
  const r10 = u8n2(arrs.reduce((sum, a10) => sum + abytes3(a10).length, 0));
94021
- let pad2 = 0;
94021
+ let pad3 = 0;
94022
94022
  arrs.forEach((a10) => {
94023
- r10.set(a10, pad2);
94024
- pad2 += a10.length;
94023
+ r10.set(a10, pad3);
94024
+ pad3 += a10.length;
94025
94025
  });
94026
94026
  return r10;
94027
94027
  };
@@ -111036,8 +111036,8 @@ function parseRetryAfterBodyMs(responseText) {
111036
111036
  function clampRateLimitDelay(delayMs) {
111037
111037
  return Math.min(Math.max(0, delayMs), MAX_RATE_LIMIT_DELAY_MS);
111038
111038
  }
111039
- function sleep(ms3) {
111040
- return new Promise((resolve) => setTimeout(resolve, ms3));
111039
+ function sleep(ms4) {
111040
+ return new Promise((resolve) => setTimeout(resolve, ms4));
111041
111041
  }
111042
111042
  async function safeText(response) {
111043
111043
  try {
@@ -112982,6 +112982,952 @@ function assertCanonicalJsonBody(bodyBytes) {
112982
112982
  }
112983
112983
  }
112984
112984
 
112985
+ // ../core/dist/additions/record-rules.js
112986
+ var singleton = () => ({
112987
+ collection: "profile",
112988
+ idFields: [],
112989
+ rowIdFields: [],
112990
+ singleton: true
112991
+ });
112992
+ var untracked = (collection, aliases) => ({
112993
+ collection,
112994
+ ...aliases ? { aliases } : {},
112995
+ idFields: [],
112996
+ rowIdFields: []
112997
+ });
112998
+ var sameId = (collection, fields, aliases) => ({
112999
+ collection,
113000
+ ...aliases ? { aliases } : {},
113001
+ idFields: fields,
113002
+ rowIdFields: fields
113003
+ });
113004
+ var MEMORY_RECORD_RULES = {
113005
+ // --- Empty: the owner app counts 0 records for these. -------------------
113006
+ "chatgpt.memories": [],
113007
+ "chatgpt.messages": [],
113008
+ "github.profile": [],
113009
+ "instagram.profile": [],
113010
+ // --- Tracked: an id that is equal in both stored forms. -----------------
113011
+ "chatgpt.conversations": [sameId("conversations", ["id"])],
113012
+ // Claude's conversations and projects keep the row id in both forms.
113013
+ "claude.conversations": [sameId("conversations", ["id"])],
113014
+ "claude.projects": [sameId("projects", ["id"])],
113015
+ // The binding projects the row's `html_url` into the legacy `url`.
113016
+ "github.repositories": [
113017
+ {
113018
+ collection: "repositories",
113019
+ idFields: ["url"],
113020
+ rowIdFields: ["html_url"]
113021
+ }
113022
+ ],
113023
+ "github.starred": [
113024
+ { collection: "starred", idFields: ["url"], rowIdFields: ["html_url"] }
113025
+ ],
113026
+ "github.events": [sameId("events", ["id"])],
113027
+ // The PDPP streams behind this legacy scope are stored as their own scopes
113028
+ // (`github.issues`, `github.pull_requests`, `github.user`), never as one
113029
+ // `github.history` rows scope, so these rules serve the legacy body only.
113030
+ "github.history": [
113031
+ sameId("issues", ["id"]),
113032
+ sameId("pullRequests", ["id"], ["pull_requests"])
113033
+ ],
113034
+ "github.issues": [sameId("issues", ["id"])],
113035
+ "github.pull_requests": [sameId("pullRequests", ["id"], ["pull_requests"])],
113036
+ // The PDPP `contributions` stream's records become the legacy `days` array.
113037
+ "github.contributions": [sameId("days", ["date"], ["contributions"])],
113038
+ // The binding projects the row `id` into the legacy `recordName`.
113039
+ "icloud_notes.notes": [
113040
+ { collection: "notes", idFields: ["recordName"], rowIdFields: ["id"] }
113041
+ ],
113042
+ "icloud_notes.folders": [
113043
+ { collection: "folders", idFields: ["recordName"], rowIdFields: ["id"] }
113044
+ ],
113045
+ // The legacy post has no id; `taken_at` is copied unchanged.
113046
+ "instagram.posts": [sameId("posts", ["taken_at"])],
113047
+ // The PDPP `following` stream's records become the legacy `accounts` array;
113048
+ // the binding projects the row `id` into the legacy `pk`.
113049
+ "instagram.following": [
113050
+ {
113051
+ collection: "accounts",
113052
+ aliases: ["following"],
113053
+ idFields: ["pk"],
113054
+ rowIdFields: ["id"]
113055
+ }
113056
+ ],
113057
+ // The PDPP `ads` stream carries all kinds; `streamFilter` keeps each
113058
+ // collection to the rows the projection assigns to it. The owner app counts
113059
+ // the category rows too, so they are counted (untracked) here.
113060
+ "instagram.ads": [
113061
+ {
113062
+ ...sameId("ad_topics", ["name"], ["ads"]),
113063
+ streamFilter: { field: "kind", equals: "ad_topic" }
113064
+ },
113065
+ {
113066
+ ...sameId("advertisers", ["name"], ["ads"]),
113067
+ streamFilter: { field: "kind", equals: "advertiser" }
113068
+ },
113069
+ {
113070
+ ...untracked("categories", ["ads"]),
113071
+ streamFilter: { field: "kind", equals: "ad_category" }
113072
+ }
113073
+ ],
113074
+ "discord.servers": [sameId("servers", ["id"])],
113075
+ "discord.messages": [sameId("messages", ["id"])],
113076
+ "discord.connections": [sameId("connections", ["id"])],
113077
+ // These store a PDPP `{ records }` body, whose rows are the collection. The
113078
+ // dataset-named alias keeps a keyed body (`{ posts: [...] }`) on the same
113079
+ // `records:` keys.
113080
+ "x.posts": [sameId("records", ["id"], ["posts"])],
113081
+ "x.likes": [sameId("records", ["id"], ["likes"])],
113082
+ "x.bookmarks": [sameId("records", ["id"], ["bookmarks"])],
113083
+ // The binding projects the row `uri` unchanged.
113084
+ "spotify.playlists": [
113085
+ sameId("playlists", ["uri"], ["public_playlists", "items"])
113086
+ ],
113087
+ "spotify.savedTracks": [
113088
+ sameId("savedTracks", ["uri"], ["tracks", "items", "saved_tracks"])
113089
+ ],
113090
+ // The PDPP stream is `saved_tracks`, so the rows are stored under this
113091
+ // scope; its keys equal the legacy `spotify.savedTracks` keys.
113092
+ "spotify.saved_tracks": [
113093
+ sameId("savedTracks", ["uri"], ["saved_tracks", "tracks", "items"])
113094
+ ],
113095
+ // The legacy body keeps the order number as `orderId`.
113096
+ "amazon.orders": [
113097
+ { collection: "orders", idFields: ["orderId"], rowIdFields: ["id"] }
113098
+ ],
113099
+ "shop.orders": [sameId("orders", ["id"])],
113100
+ "oura.activity": [sameId("days", ["id"], ["activity"])],
113101
+ "oura.readiness": [sameId("days", ["id"], ["readiness"])],
113102
+ "youtube.playlists": [sameId("playlists", ["url"])],
113103
+ // --- Counted, never tracked: the owner app counts them, but no field is --
113104
+ // --- provably equal in both stored forms. They report a total and no -----
113105
+ // --- additions, and a null trackedSince. ---------------------------------
113106
+ // The legacy body drops the row id.
113107
+ "linkedin.experience": [untracked("items", ["experience", "experiences"])],
113108
+ "linkedin.education": [untracked("items", ["education"])],
113109
+ "linkedin.connections": [untracked("connections")],
113110
+ "linkedin.skills": [untracked("skills")],
113111
+ "linkedin.languages": [untracked("languages")],
113112
+ // One `sleep` stream feeds two legacy arrays, so both are counted.
113113
+ "oura.sleep": [
113114
+ untracked("sleepPeriods", ["sleep"]),
113115
+ untracked("dailyScores")
113116
+ ],
113117
+ // No binding fixture proves an id equal in both forms.
113118
+ "heb.orders": [untracked("orders")],
113119
+ "wholefoods.orders": [untracked("orders")],
113120
+ "heb.nutrition": [untracked("nutrition")],
113121
+ "wholefoods.nutrition": [untracked("nutrition")],
113122
+ "youtube.subscriptions": [untracked("subscriptions")],
113123
+ "youtube.likes": [untracked("likedVideos", ["likes"])],
113124
+ "youtube.watchLater": [untracked("watchLater")],
113125
+ "youtube.watch_later": [untracked("watchLater", ["watch_later"])],
113126
+ "youtube.history": [untracked("history")],
113127
+ "youtube.watch_history": [untracked("history", ["watch_history"])],
113128
+ "youtube.playlistItems": [untracked("playlists", ["playlistItems"])],
113129
+ "youtube.playlist_items": [untracked("playlists", ["playlist_items"])],
113130
+ // Single-record scopes.
113131
+ "linkedin.profile": [singleton()],
113132
+ "spotify.profile": [singleton()],
113133
+ "youtube.profile": [singleton()],
113134
+ "heb.profile": [singleton()],
113135
+ "wholefoods.profile": [singleton()],
113136
+ // Streams that only join into another scope's legacy body, which the owner
113137
+ // app counts when they are stored as their own scopes.
113138
+ "amazon.order_items": [untracked("order_items")],
113139
+ "heb.order_items": [untracked("order_items")],
113140
+ "wholefoods.order_items": [untracked("order_items")],
113141
+ "claude.messages": [untracked("messages")],
113142
+ "claude.account_profile": [untracked("account_profile")],
113143
+ "claude.project_documents": [untracked("project_documents")],
113144
+ "github.user": [untracked("user")],
113145
+ "github.user_stats": [untracked("user_stats")],
113146
+ "github.pinned_repositories": [untracked("pinned_repositories")],
113147
+ "github.organizations": [untracked("organizations")],
113148
+ "instagram.post_likes": [untracked("post_likes")],
113149
+ "spotify.playlist_items": [untracked("playlist_items")]
113150
+ };
113151
+ function memoryRecordRulesFor(scope) {
113152
+ if (!Object.prototype.hasOwnProperty.call(MEMORY_RECORD_RULES, scope)) {
113153
+ return null;
113154
+ }
113155
+ return MEMORY_RECORD_RULES[scope] ?? null;
113156
+ }
113157
+
113158
+ // ../core/dist/additions/first-added.js
113159
+ var MAX_TRACKED_KEYS = 2e5;
113160
+ var MAX_ID_LENGTH = 64;
113161
+ var MAX_COLLECTION_LENGTH = 64;
113162
+ var PRUNE_AFTER_MS = 90 * 24 * 60 * 60 * 1e3;
113163
+ var RETRY_UNREADABLE_MS = 60 * 60 * 1e3;
113164
+ var MAX_RETRY_VERSIONS = 20;
113165
+ var YIELD_EVERY = 2e3;
113166
+ function yieldToEventLoop() {
113167
+ return new Promise((resolve) => setTimeout(resolve, 0));
113168
+ }
113169
+ var ID_PROPERTIES = ["id", "uuid", "key", "uri", "url"];
113170
+ var SERVER_STAMP_KEYS2 = /* @__PURE__ */ new Set([
113171
+ "$writtenBy",
113172
+ "$lineage"
113173
+ ]);
113174
+ function isRecord3(value) {
113175
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113176
+ }
113177
+ function hasOwn(data, key) {
113178
+ return Object.prototype.hasOwnProperty.call(data, key);
113179
+ }
113180
+ function isRowsBody(data) {
113181
+ const keys = Object.keys(data).filter((key) => !SERVER_STAMP_KEYS2.has(key));
113182
+ return keys.length === 1 && keys[0] === "records" && Array.isArray(data.records);
113183
+ }
113184
+ async function sha256Hex2(input) {
113185
+ const bytes2 = new TextEncoder().encode(input);
113186
+ const digest2 = await globalThis.crypto.subtle.digest("SHA-256", bytes2);
113187
+ let hex3 = "";
113188
+ for (const byte of new Uint8Array(digest2)) {
113189
+ hex3 += byte.toString(16).padStart(2, "0");
113190
+ }
113191
+ return hex3;
113192
+ }
113193
+ function valueAtPath(obj, path) {
113194
+ let current = obj;
113195
+ for (const segment of path.split(".")) {
113196
+ if (!isRecord3(current) || !hasOwn(current, segment))
113197
+ return void 0;
113198
+ current = current[segment];
113199
+ }
113200
+ return current;
113201
+ }
113202
+ async function idToken(id2) {
113203
+ if (typeof id2 === "number")
113204
+ return `n:${String(id2)}`;
113205
+ return id2.length > MAX_ID_LENGTH ? `h:${(await sha256Hex2(id2)).slice(0, 32)}` : `i:${id2}`;
113206
+ }
113207
+ async function collectionToken(name) {
113208
+ if (name.length > MAX_COLLECTION_LENGTH) {
113209
+ return `#${(await sha256Hex2(name)).slice(0, 32)}`;
113210
+ }
113211
+ return name.replace(/[\\:#]/g, (character) => `\\${character}`);
113212
+ }
113213
+ async function idPart(obj, fields) {
113214
+ for (const field2 of fields) {
113215
+ const value = valueAtPath(obj, field2);
113216
+ if (typeof value === "string" && value.length > 0)
113217
+ return idToken(value);
113218
+ if (typeof value === "number" && Number.isFinite(value)) {
113219
+ return idToken(value);
113220
+ }
113221
+ }
113222
+ return null;
113223
+ }
113224
+ var KeyCollector = class {
113225
+ keys = /* @__PURE__ */ new Set();
113226
+ total = 0;
113227
+ tooMany = false;
113228
+ /**
113229
+ * Count `items`, and track those with an id. Once the tracked keys pass the
113230
+ * cap it stops collecting (and frees them) but still counts, so a hostile
113231
+ * body costs a bounded amount of work and memory.
113232
+ */
113233
+ async addItems(collection, items, idFields) {
113234
+ this.total += items.length;
113235
+ if (this.tooMany || idFields.length === 0)
113236
+ return;
113237
+ const prefix = await collectionToken(collection);
113238
+ for (let index = 0; index < items.length; index += 1) {
113239
+ if (index % YIELD_EVERY === YIELD_EVERY - 1)
113240
+ await yieldToEventLoop();
113241
+ const item = items[index];
113242
+ if (!isRecord3(item))
113243
+ continue;
113244
+ const id2 = await idPart(item, idFields);
113245
+ if (id2 === null)
113246
+ continue;
113247
+ this.keys.add(`${prefix}:${id2}`);
113248
+ if (this.keys.size > MAX_TRACKED_KEYS) {
113249
+ this.tooMany = true;
113250
+ this.keys.clear();
113251
+ return;
113252
+ }
113253
+ }
113254
+ }
113255
+ result() {
113256
+ return {
113257
+ keys: Array.from(this.keys),
113258
+ total: this.total,
113259
+ ...this.tooMany ? { tooMany: true } : {}
113260
+ };
113261
+ }
113262
+ };
113263
+ function datasetOf(scope) {
113264
+ const dot = scope.indexOf(".");
113265
+ return dot < 0 ? scope : scope.slice(dot + 1);
113266
+ }
113267
+ function passesStreamFilter(row, rule) {
113268
+ return !rule.streamFilter || row[rule.streamFilter.field] === rule.streamFilter.equals;
113269
+ }
113270
+ function legacyItems(data, rule) {
113271
+ for (const name of [rule.collection, ...rule.aliases ?? []]) {
113272
+ if (!hasOwn(data, name))
113273
+ continue;
113274
+ const value = data[name];
113275
+ if (!Array.isArray(value))
113276
+ continue;
113277
+ return value.filter(isRecord3);
113278
+ }
113279
+ return [];
113280
+ }
113281
+ async function extractRecordKeys(scope, data) {
113282
+ if (!isRecord3(data))
113283
+ return null;
113284
+ const rules = memoryRecordRulesFor(scope);
113285
+ if (rules !== null && rules.length === 0)
113286
+ return { keys: [], total: 0 };
113287
+ if (hasOwn(data, "$binary"))
113288
+ return null;
113289
+ const collector = new KeyCollector();
113290
+ const rows = isRowsBody(data) ? data.records : null;
113291
+ if (rules !== null) {
113292
+ if (rows !== null) {
113293
+ const dataset = datasetOf(scope);
113294
+ const named = rules.filter((rule) => rule.collection === dataset || (rule.aliases ?? []).includes(dataset));
113295
+ const chosen = named.length > 0 ? named : rules.length === 1 ? rules : [];
113296
+ if (chosen.length === 0 && rules.length > 0) {
113297
+ collector.total += rows.length;
113298
+ }
113299
+ for (const rule of chosen) {
113300
+ if (rule.singleton) {
113301
+ collector.total += rows.filter(isRecord3).length;
113302
+ continue;
113303
+ }
113304
+ const items = rows.filter(isRecord3).filter((row) => passesStreamFilter(row, rule));
113305
+ await collector.addItems(rule.collection, items, rule.rowIdFields);
113306
+ }
113307
+ } else {
113308
+ for (const rule of rules) {
113309
+ if (rule.singleton) {
113310
+ if (Object.keys(data).some((key) => !key.startsWith("$"))) {
113311
+ collector.total += 1;
113312
+ }
113313
+ continue;
113314
+ }
113315
+ await collector.addItems(rule.collection, legacyItems(data, rule), rule.idFields);
113316
+ }
113317
+ }
113318
+ return collector.result();
113319
+ }
113320
+ if (rows !== null) {
113321
+ await collector.addItems(datasetOf(scope), rows, ID_PROPERTIES);
113322
+ return collector.result();
113323
+ }
113324
+ let sawArray = false;
113325
+ for (const key of Object.keys(data)) {
113326
+ if (key.startsWith("$"))
113327
+ continue;
113328
+ const value = data[key];
113329
+ if (!Array.isArray(value))
113330
+ continue;
113331
+ sawArray = true;
113332
+ await collector.addItems(key, value, ID_PROPERTIES);
113333
+ }
113334
+ if (!sawArray)
113335
+ collector.total = 1;
113336
+ return collector.result();
113337
+ }
113338
+ function ms3(value) {
113339
+ return value === null ? Number.NaN : Date.parse(value);
113340
+ }
113341
+ function isNewer(a10, b10) {
113342
+ if (b10 === null)
113343
+ return true;
113344
+ const difference = Date.parse(a10) - Date.parse(b10);
113345
+ return difference > 0 || difference === 0 && a10 >= b10;
113346
+ }
113347
+ function isEarlier(a10, b10) {
113348
+ const difference = Date.parse(a10) - Date.parse(b10);
113349
+ return difference < 0 || difference === 0 && a10 < b10;
113350
+ }
113351
+ function isTrackable(extraction) {
113352
+ return extraction !== null && !extraction.tooMany && (extraction.keys.length > 0 || extraction.total === 0);
113353
+ }
113354
+ var LedgerFold = class {
113355
+ scope;
113356
+ baseline = null;
113357
+ current = null;
113358
+ latest = null;
113359
+ through = null;
113360
+ retry;
113361
+ skipped;
113362
+ partial = false;
113363
+ // A Map keeps arbitrary keys (including "__proto__") out of the prototype
113364
+ // chain; Object.fromEntries defines each one as an own property again.
113365
+ records = /* @__PURE__ */ new Map();
113366
+ constructor(ledger, scope) {
113367
+ this.scope = ledger?.scope ?? scope ?? null;
113368
+ if (!ledger)
113369
+ return;
113370
+ this.baseline = ledger.baseline;
113371
+ this.current = ledger.current;
113372
+ this.latest = { ...ledger.latest };
113373
+ this.through = ledger.through;
113374
+ this.retry = ledger.retry ? {
113375
+ versions: [...ledger.retry.versions],
113376
+ attemptedAt: ledger.retry.attemptedAt
113377
+ } : void 0;
113378
+ this.skipped = ledger.skipped;
113379
+ this.partial = ledger.partial === true;
113380
+ for (const [key, pair] of Object.entries(ledger.records)) {
113381
+ this.records.set(key, [pair[0], pair[1]]);
113382
+ }
113383
+ }
113384
+ async add(version4) {
113385
+ const at3 = Date.parse(version4.collectedAt);
113386
+ if (Number.isNaN(at3))
113387
+ return;
113388
+ if (this.scope === null)
113389
+ this.scope = version4.scope;
113390
+ if (this.scope !== version4.scope)
113391
+ return;
113392
+ const extraction = await extractRecordKeys(version4.scope, version4.data);
113393
+ const total = extraction?.total ?? 1;
113394
+ if (this.latest === null || isNewer(version4.collectedAt, this.latest.collectedAt)) {
113395
+ this.latest = { collectedAt: version4.collectedAt, total };
113396
+ }
113397
+ if (isNewer(version4.collectedAt, this.through)) {
113398
+ this.through = version4.collectedAt;
113399
+ }
113400
+ if (this.skipped !== void 0)
113401
+ return;
113402
+ if (!isTrackable(extraction))
113403
+ return;
113404
+ if (this.baseline === null) {
113405
+ this.baseline = version4.collectedAt;
113406
+ } else if (isEarlier(version4.collectedAt, this.baseline) && !this.partial) {
113407
+ this.baseline = version4.collectedAt;
113408
+ }
113409
+ if (isNewer(version4.collectedAt, this.current)) {
113410
+ this.current = version4.collectedAt;
113411
+ }
113412
+ const keys = extraction.keys;
113413
+ const fresh = [];
113414
+ for (let index = 0; index < keys.length; index += 1) {
113415
+ if (index % YIELD_EVERY === YIELD_EVERY - 1)
113416
+ await yieldToEventLoop();
113417
+ const key = keys[index];
113418
+ const existing = this.records.get(key);
113419
+ if (!existing) {
113420
+ fresh.push(key);
113421
+ continue;
113422
+ }
113423
+ if (isEarlier(version4.collectedAt, existing[0])) {
113424
+ existing[0] = version4.collectedAt;
113425
+ }
113426
+ if (isNewer(version4.collectedAt, existing[1])) {
113427
+ existing[1] = version4.collectedAt;
113428
+ }
113429
+ }
113430
+ if (fresh.length > 0)
113431
+ await this.insertFresh(fresh, keys, version4.collectedAt);
113432
+ }
113433
+ /**
113434
+ * Insert the records this version brings that the ledger does not know.
113435
+ * Capacity is decided ONCE: when they do not fit, absent records are evicted
113436
+ * in a single pass (see `evict`), and whatever still does not fit is left
113437
+ * out, smallest keys first, so the result never depends on the order of the
113438
+ * ids in the version.
113439
+ */
113440
+ async insertFresh(fresh, versionKeys, collectedAt) {
113441
+ fresh.sort();
113442
+ let room = MAX_TRACKED_KEYS - this.records.size;
113443
+ if (fresh.length > room) {
113444
+ room += await this.evict(fresh.length - room, new Set(versionKeys));
113445
+ }
113446
+ const take = Math.min(fresh.length, Math.max(0, room));
113447
+ for (let index = 0; index < take; index += 1) {
113448
+ if (index % YIELD_EVERY === YIELD_EVERY - 1)
113449
+ await yieldToEventLoop();
113450
+ this.records.set(fresh[index], [collectedAt, collectedAt]);
113451
+ }
113452
+ }
113453
+ /**
113454
+ * Make room for `need` records by dropping some that are absent: not in the
113455
+ * newest tracked version, not in the version being folded, and not
113456
+ * pre-tracking (first seen at or before the baseline). Pre-tracking records
113457
+ * are never dropped. Dated records that are absent CAN be dropped (and are
113458
+ * dated as new if they return): that needs a scope near the cap, and
113459
+ * whoever can write the scope can provoke it. Among the evictable ones the
113460
+ * most recently first seen go first. Returns how many were dropped.
113461
+ */
113462
+ async evict(need, inVersion) {
113463
+ const currentMs = ms3(this.current);
113464
+ const baselineMs = ms3(this.baseline);
113465
+ const candidates = [];
113466
+ let seen = 0;
113467
+ for (const [key, pair] of this.records) {
113468
+ seen += 1;
113469
+ if (seen % (YIELD_EVERY * 5) === 0)
113470
+ await yieldToEventLoop();
113471
+ if (inVersion.has(key))
113472
+ continue;
113473
+ if (Date.parse(pair[1]) === currentMs)
113474
+ continue;
113475
+ const firstMs = Date.parse(pair[0]);
113476
+ if (firstMs <= baselineMs)
113477
+ continue;
113478
+ candidates.push([firstMs, key]);
113479
+ }
113480
+ candidates.sort((a10, b10) => b10[0] - a10[0] || (a10[1] < b10[1] ? -1 : a10[1] > b10[1] ? 1 : 0));
113481
+ const count = Math.min(need, candidates.length);
113482
+ for (let index = 0; index < count; index += 1) {
113483
+ this.records.delete(candidates[index][1]);
113484
+ }
113485
+ return count;
113486
+ }
113487
+ /** Drop records that are not present and were last seen over 90 days before `current`. */
113488
+ async prune() {
113489
+ if (this.current === null || this.skipped !== void 0)
113490
+ return;
113491
+ const currentMs = ms3(this.current);
113492
+ const cutoff = currentMs - PRUNE_AFTER_MS;
113493
+ let seen = 0;
113494
+ for (const [key, pair] of this.records) {
113495
+ seen += 1;
113496
+ if (seen % (YIELD_EVERY * 5) === 0)
113497
+ await yieldToEventLoop();
113498
+ const lastMs = Date.parse(pair[1]);
113499
+ if (lastMs !== currentMs && lastMs < cutoff)
113500
+ this.records.delete(key);
113501
+ }
113502
+ }
113503
+ /**
113504
+ * Prune and return the ledger, or null when nothing was ever folded.
113505
+ * `partial` marks a ledger rebuilt from only the newest retained versions;
113506
+ * `through` overrides the completeness marker (see `ScopeFirstSeenLedger`).
113507
+ */
113508
+ async finish(options = {}) {
113509
+ if (this.scope === null || this.latest === null)
113510
+ return null;
113511
+ await this.prune();
113512
+ const partial2 = this.partial || options.partial === true;
113513
+ const retry = options.retry === void 0 ? this.retry : options.retry;
113514
+ return {
113515
+ version: 3,
113516
+ scope: this.scope,
113517
+ baseline: this.baseline,
113518
+ current: this.current,
113519
+ latest: this.latest,
113520
+ through: options.through ?? this.through ?? this.latest.collectedAt,
113521
+ ...retry ? { retry } : {},
113522
+ ...this.skipped ? { skipped: this.skipped } : {},
113523
+ ...partial2 ? { partial: true } : {},
113524
+ records: Object.fromEntries(this.records)
113525
+ };
113526
+ }
113527
+ };
113528
+ function listAddedTimestamps(ledger) {
113529
+ if (ledger.skipped !== void 0 || ledger.baseline === null || ledger.current === null || Date.parse(ledger.current) !== Date.parse(ledger.latest.collectedAt)) {
113530
+ return [];
113531
+ }
113532
+ const baselineMs = Date.parse(ledger.baseline);
113533
+ const currentMs = Date.parse(ledger.current);
113534
+ const added = [];
113535
+ for (const [first, last2] of Object.values(ledger.records)) {
113536
+ if (Date.parse(last2) !== currentMs)
113537
+ continue;
113538
+ if (Date.parse(first) <= baselineMs)
113539
+ continue;
113540
+ added.push(first);
113541
+ }
113542
+ if (added.length > ledger.latest.total) {
113543
+ added.sort();
113544
+ added.length = ledger.latest.total;
113545
+ }
113546
+ return added;
113547
+ }
113548
+ function parseableString(value) {
113549
+ return typeof value === "string" && !Number.isNaN(Date.parse(value));
113550
+ }
113551
+ function nullableParseable(value) {
113552
+ return value === null || parseableString(value);
113553
+ }
113554
+ var SKIP_REASONS = /* @__PURE__ */ new Set(["too_large", "unreadable"]);
113555
+ function readScopeFirstSeenLedger(value) {
113556
+ try {
113557
+ if (!isRecord3(value) || value.version !== 3)
113558
+ return null;
113559
+ const { scope, baseline, current, latest, through, records, skipped, partial: partial2 } = value;
113560
+ if (typeof scope !== "string" || scope.length === 0)
113561
+ return null;
113562
+ if (!nullableParseable(baseline) || !nullableParseable(current)) {
113563
+ return null;
113564
+ }
113565
+ if (!isRecord3(latest) || !parseableString(latest.collectedAt))
113566
+ return null;
113567
+ if (!parseableString(through))
113568
+ return null;
113569
+ let retryValue;
113570
+ if (value.retry !== void 0) {
113571
+ const retry = value.retry;
113572
+ if (!isRecord3(retry) || !Array.isArray(retry.versions) || retry.versions.length === 0 || retry.versions.length > MAX_RETRY_VERSIONS || !retry.versions.every(parseableString) || !parseableString(retry.attemptedAt)) {
113573
+ return null;
113574
+ }
113575
+ retryValue = {
113576
+ versions: [...retry.versions],
113577
+ attemptedAt: retry.attemptedAt
113578
+ };
113579
+ }
113580
+ const newestKnown = Math.max(Date.parse(latest.collectedAt), ...retryValue?.versions.map((version4) => Date.parse(version4)) ?? []);
113581
+ if (Date.parse(through) > newestKnown)
113582
+ return null;
113583
+ const total = latest.total;
113584
+ if (typeof total !== "number" || !Number.isInteger(total) || total < 0) {
113585
+ return null;
113586
+ }
113587
+ if (skipped !== void 0 && !SKIP_REASONS.has(skipped))
113588
+ return null;
113589
+ if (partial2 !== void 0 && partial2 !== true)
113590
+ return null;
113591
+ if (!isRecord3(records))
113592
+ return null;
113593
+ const entries = Object.entries(records);
113594
+ if (entries.length > MAX_TRACKED_KEYS)
113595
+ return null;
113596
+ const copied = /* @__PURE__ */ new Map();
113597
+ for (const [key, pair] of entries) {
113598
+ if (!Array.isArray(pair) || pair.length !== 2)
113599
+ return null;
113600
+ if (!parseableString(pair[0]) || !parseableString(pair[1]))
113601
+ return null;
113602
+ copied.set(key, [pair[0], pair[1]]);
113603
+ }
113604
+ return {
113605
+ version: 3,
113606
+ scope,
113607
+ baseline,
113608
+ current,
113609
+ latest: { collectedAt: latest.collectedAt, total },
113610
+ through,
113611
+ ...retryValue ? { retry: retryValue } : {},
113612
+ ...skipped ? { skipped } : {},
113613
+ ...partial2 ? { partial: true } : {},
113614
+ records: Object.fromEntries(copied)
113615
+ };
113616
+ } catch {
113617
+ return null;
113618
+ }
113619
+ }
113620
+
113621
+ // ../core/dist/additions/scope-lock.js
113622
+ var tails = /* @__PURE__ */ new Map();
113623
+ async function withScopeLock(scope, fn3) {
113624
+ const previous = tails.get(scope) ?? Promise.resolve();
113625
+ let release;
113626
+ const mine = new Promise((resolve) => {
113627
+ release = resolve;
113628
+ });
113629
+ const tail = previous.then(() => mine);
113630
+ tails.set(scope, tail);
113631
+ await previous;
113632
+ try {
113633
+ return await fn3();
113634
+ } finally {
113635
+ release();
113636
+ if (tails.get(scope) === tail)
113637
+ tails.delete(scope);
113638
+ }
113639
+ }
113640
+
113641
+ // ../core/dist/additions/ledger-store.js
113642
+ var REBUILD_VERSION_LIMIT = 200;
113643
+ var REBUILD_BYTE_BUDGET = 256 * 1024 * 1024;
113644
+ function isRecord4(value) {
113645
+ return value !== null && typeof value === "object" && !Array.isArray(value);
113646
+ }
113647
+ function readsNothing(scope) {
113648
+ return memoryRecordRulesFor(scope)?.length === 0;
113649
+ }
113650
+ async function readVersionData(storage, scope, collectedAt) {
113651
+ if (readsNothing(scope))
113652
+ return { scope, collectedAt, data: {} };
113653
+ try {
113654
+ const envelope = storage.readStoredEnvelope ? await storage.readStoredEnvelope(scope, collectedAt) : await storage.readEnvelope(scope, collectedAt);
113655
+ if (!isRecord4(envelope.data))
113656
+ return null;
113657
+ return { scope, collectedAt, data: envelope.data };
113658
+ } catch {
113659
+ return null;
113660
+ }
113661
+ }
113662
+ function retainedVersions(storage, scope) {
113663
+ return storage.listVersions(scope, { limit: REBUILD_VERSION_LIMIT }).filter((entry) => !Number.isNaN(Date.parse(entry.collectedAt))).sort((a10, b10) => Date.parse(b10.collectedAt) - Date.parse(a10.collectedAt) || (a10.collectedAt < b10.collectedAt ? 1 : a10.collectedAt > b10.collectedAt ? -1 : 0));
113664
+ }
113665
+ function withinBudget(scope, entries, budget) {
113666
+ if (readsNothing(scope))
113667
+ return [...entries];
113668
+ const selected = [];
113669
+ let used = 0;
113670
+ for (const entry of entries) {
113671
+ used += Math.max(0, entry.sizeBytes ?? 0);
113672
+ if (used > budget)
113673
+ break;
113674
+ selected.push(entry);
113675
+ }
113676
+ return selected;
113677
+ }
113678
+ async function readStoredLedger(storage, scope) {
113679
+ if (!storage.readFirstSeenLedger)
113680
+ return null;
113681
+ try {
113682
+ const ledger = readScopeFirstSeenLedger(await storage.readFirstSeenLedger(scope));
113683
+ return ledger !== null && ledger.scope === scope ? ledger : null;
113684
+ } catch {
113685
+ return null;
113686
+ }
113687
+ }
113688
+ async function dropIfScopeEmpty(storage, scope) {
113689
+ if (storage.findEntry({ scope }))
113690
+ return false;
113691
+ await storage.deleteFirstSeenLedger?.(scope);
113692
+ return true;
113693
+ }
113694
+ function latestStillStored(storage, ledger) {
113695
+ return storage.findEntry({ scope: ledger.scope, at: ledger.latest.collectedAt })?.collectedAt === ledger.latest.collectedAt;
113696
+ }
113697
+ function isMarker(ledger) {
113698
+ return ledger.skipped === "too_large" || ledger.skipped === "unreadable";
113699
+ }
113700
+ function newerThan(retained, ledger) {
113701
+ const throughMs = Date.parse(ledger.through);
113702
+ return retained.filter((entry) => Date.parse(entry.collectedAt) > throughMs);
113703
+ }
113704
+ async function foldEntries(storage, scope, entries, fold) {
113705
+ let folded = 0;
113706
+ const failed = [];
113707
+ for (const entry of [...entries].reverse()) {
113708
+ const version4 = await readVersionData(storage, scope, entry.collectedAt);
113709
+ if (version4) {
113710
+ await fold.add(version4);
113711
+ folded += 1;
113712
+ } else {
113713
+ failed.push(entry);
113714
+ }
113715
+ await yieldToEventLoop();
113716
+ }
113717
+ return { folded, failed };
113718
+ }
113719
+ function retryOf(failed, now) {
113720
+ if (failed.length === 0)
113721
+ return null;
113722
+ return {
113723
+ versions: failed.slice(0, MAX_RETRY_VERSIONS).map((entry) => entry.collectedAt),
113724
+ attemptedAt: now.toISOString()
113725
+ };
113726
+ }
113727
+ async function rebuild(storage, scope, retained, budget, now) {
113728
+ if (retained.length === 0)
113729
+ return null;
113730
+ const selected = withinBudget(scope, retained, budget);
113731
+ const fold = new LedgerFold(null, scope);
113732
+ const { folded, failed } = await foldEntries(storage, scope, selected, fold);
113733
+ if (folded === 0) {
113734
+ return {
113735
+ version: 3,
113736
+ scope,
113737
+ baseline: null,
113738
+ current: null,
113739
+ latest: { collectedAt: retained[0].collectedAt, total: 0 },
113740
+ through: retained[0].collectedAt,
113741
+ skipped: selected.length === 0 ? "too_large" : "unreadable",
113742
+ records: {}
113743
+ };
113744
+ }
113745
+ const total = storage.countVersions(scope);
113746
+ return fold.finish({
113747
+ partial: folded < total,
113748
+ through: retained[0].collectedAt,
113749
+ retry: retryOf(failed, now)
113750
+ });
113751
+ }
113752
+ async function catchUp(storage, ledger, retained, budget, now) {
113753
+ const newer = newerThan(retained, ledger);
113754
+ const selected = withinBudget(ledger.scope, newer, budget);
113755
+ const pending = (ledger.retry?.versions ?? []).filter((version4) => retained.some((entry) => entry.collectedAt === version4));
113756
+ const due = ledger.retry !== void 0 && pending.length > 0 && now.getTime() - Date.parse(ledger.retry.attemptedAt) >= RETRY_UNREADABLE_MS;
113757
+ const retryEntries = due ? withinBudget(ledger.scope, retained.filter((entry) => pending.includes(entry.collectedAt)), budget) : [];
113758
+ const forgotten = pending.length !== (ledger.retry?.versions.length ?? 0);
113759
+ if (selected.length === 0 && retryEntries.length === 0 && !forgotten) {
113760
+ return ledger;
113761
+ }
113762
+ const fold = new LedgerFold(ledger);
113763
+ const failedNew = selected.length > 0 ? (await foldEntries(storage, ledger.scope, selected, fold)).failed : [];
113764
+ const failedRetry = retryEntries.length > 0 ? (await foldEntries(storage, ledger.scope, retryEntries, fold)).failed : [];
113765
+ const stillFailing = due ? failedRetry.map((entry) => entry.collectedAt) : pending;
113766
+ const versions = [
113767
+ ...stillFailing,
113768
+ ...failedNew.map((entry) => entry.collectedAt)
113769
+ ].slice(0, MAX_RETRY_VERSIONS);
113770
+ const attempted = due || failedNew.length > 0;
113771
+ const retry = versions.length === 0 ? null : {
113772
+ versions,
113773
+ attemptedAt: attempted ? now.toISOString() : ledger.retry.attemptedAt
113774
+ };
113775
+ const beyond = storage.listVersions(ledger.scope, {
113776
+ limit: 1,
113777
+ offset: retained.length
113778
+ })[0];
113779
+ const windowCut = beyond !== void 0 && Date.parse(beyond.collectedAt) > Date.parse(ledger.through);
113780
+ return await fold.finish({
113781
+ partial: windowCut || selected.length < newer.length,
113782
+ through: selected.length > 0 ? retained[0].collectedAt : ledger.through,
113783
+ retry
113784
+ }) ?? ledger;
113785
+ }
113786
+ async function recordStoredVersion(storage, version4) {
113787
+ if (!storage.readFirstSeenLedger || !storage.writeFirstSeenLedger)
113788
+ return;
113789
+ try {
113790
+ await withScopeLock(version4.scope, async () => {
113791
+ const retained = retainedVersions(storage, version4.scope);
113792
+ let existing = await readStoredLedger(storage, version4.scope);
113793
+ if (existing && !latestStillStored(storage, existing)) {
113794
+ await storage.deleteFirstSeenLedger?.(version4.scope);
113795
+ existing = null;
113796
+ }
113797
+ const others = retained.filter((entry) => entry.collectedAt !== version4.collectedAt);
113798
+ let through = version4.collectedAt;
113799
+ if (existing) {
113800
+ if (isMarker(existing))
113801
+ return;
113802
+ const throughMs = Date.parse(existing.through);
113803
+ const unfolded = others.some((entry) => Date.parse(entry.collectedAt) > throughMs);
113804
+ through = unfolded || Date.parse(version4.collectedAt) < throughMs ? existing.through : version4.collectedAt;
113805
+ } else if (others.length > 0) {
113806
+ return;
113807
+ }
113808
+ const fold = new LedgerFold(existing, version4.scope);
113809
+ await fold.add(version4);
113810
+ const next = await fold.finish({ through });
113811
+ if (!next)
113812
+ return;
113813
+ await storage.writeFirstSeenLedger(version4.scope, next);
113814
+ await dropIfScopeEmpty(storage, version4.scope);
113815
+ });
113816
+ } catch {
113817
+ }
113818
+ }
113819
+ async function ensureScopeLedger(storage, scope, options = {}) {
113820
+ const budget = options.byteBudget ?? REBUILD_BYTE_BUDGET;
113821
+ const now = options.now ?? /* @__PURE__ */ new Date();
113822
+ return withScopeLock(scope, async () => {
113823
+ try {
113824
+ const retained = retainedVersions(storage, scope);
113825
+ if (retained.length === 0) {
113826
+ await storage.deleteFirstSeenLedger?.(scope);
113827
+ return null;
113828
+ }
113829
+ let existing = await readStoredLedger(storage, scope);
113830
+ if (existing && (!latestStillStored(storage, existing) || isMarker(existing) && newerThan(retained, existing).length > 0)) {
113831
+ existing = null;
113832
+ }
113833
+ const ledger = existing ? await catchUp(storage, existing, retained, budget, now) : await rebuild(storage, scope, retained, budget, now);
113834
+ if (ledger && ledger !== existing && storage.writeFirstSeenLedger) {
113835
+ try {
113836
+ await storage.writeFirstSeenLedger(scope, ledger);
113837
+ if (await dropIfScopeEmpty(storage, scope))
113838
+ return null;
113839
+ } catch {
113840
+ }
113841
+ }
113842
+ return ledger;
113843
+ } catch {
113844
+ return null;
113845
+ }
113846
+ });
113847
+ }
113848
+
113849
+ // ../core/dist/additions/summary.js
113850
+ var InvalidTimezoneError = class extends Error {
113851
+ };
113852
+ function createFormatter(timezone) {
113853
+ try {
113854
+ return new Intl.DateTimeFormat("en-CA", {
113855
+ timeZone: timezone,
113856
+ year: "numeric",
113857
+ month: "2-digit",
113858
+ day: "2-digit"
113859
+ });
113860
+ } catch {
113861
+ throw new InvalidTimezoneError(`Unknown timezone: ${timezone}`);
113862
+ }
113863
+ }
113864
+ function localDate(formatter, instant) {
113865
+ let year = "";
113866
+ let month = "";
113867
+ let day = "";
113868
+ for (const part of formatter.formatToParts(instant)) {
113869
+ if (part.type === "year")
113870
+ year = part.value;
113871
+ else if (part.type === "month")
113872
+ month = part.value;
113873
+ else if (part.type === "day")
113874
+ day = part.value;
113875
+ }
113876
+ return `${year}-${month}-${day}`;
113877
+ }
113878
+ function pad2(value) {
113879
+ return String(value).padStart(2, "0");
113880
+ }
113881
+ function buildDayRows(formatter, now, days) {
113882
+ const [year, month, day] = localDate(formatter, now).split("-").map(Number);
113883
+ const rows = [];
113884
+ for (let offset = days - 1; offset >= 0; offset -= 1) {
113885
+ const date5 = new Date(Date.UTC(year, month - 1, day - offset));
113886
+ rows.push({
113887
+ date: `${date5.getUTCFullYear()}-${pad2(date5.getUTCMonth() + 1)}-${pad2(date5.getUTCDate())}`,
113888
+ added: 0
113889
+ });
113890
+ }
113891
+ return rows;
113892
+ }
113893
+ function validateTimezone(timezone) {
113894
+ createFormatter(timezone);
113895
+ }
113896
+ async function summarizeAdditions(input) {
113897
+ const { timezone, days, now } = input;
113898
+ const formatter = createFormatter(timezone);
113899
+ const dayRows = buildDayRows(formatter, now, days);
113900
+ const dayIndex = new Map(dayRows.map((row, index) => [row.date, index]));
113901
+ const scopes = [];
113902
+ let total = 0;
113903
+ let trackedSince = null;
113904
+ let trackedSinceAt = Infinity;
113905
+ for await (const ledger of input.ledgers) {
113906
+ for (const firstSeen of listAddedTimestamps(ledger)) {
113907
+ const date5 = localDate(formatter, new Date(Date.parse(firstSeen)));
113908
+ const index = dayIndex.get(date5);
113909
+ if (index !== void 0)
113910
+ dayRows[index].added += 1;
113911
+ }
113912
+ total += ledger.latest.total;
113913
+ const scopeTrackedSince = ledger.skipped ? null : ledger.baseline;
113914
+ if (scopeTrackedSince !== null) {
113915
+ const baselineAt = Date.parse(scopeTrackedSince);
113916
+ if (baselineAt < trackedSinceAt) {
113917
+ trackedSinceAt = baselineAt;
113918
+ trackedSince = scopeTrackedSince;
113919
+ }
113920
+ }
113921
+ scopes.push({
113922
+ scope: ledger.scope,
113923
+ total: ledger.latest.total,
113924
+ trackedSince: scopeTrackedSince
113925
+ });
113926
+ }
113927
+ scopes.sort((a10, b10) => a10.scope.localeCompare(b10.scope));
113928
+ return { timezone, total, days: dayRows, trackedSince, scopes };
113929
+ }
113930
+
112985
113931
  // ../core/dist/contracts/data.js
112986
113932
  var IngestPersistedError = class extends Error {
112987
113933
  relativePath;
@@ -113023,35 +113969,43 @@ function normalizeLimit(value) {
113023
113969
  function normalizeOffset(value) {
113024
113970
  return value ?? 0;
113025
113971
  }
113026
- function isRecord3(value) {
113972
+ function isRecord5(value) {
113027
113973
  return value !== null && typeof value === "object" && !Array.isArray(value);
113028
113974
  }
113029
113975
  var VISIBILITY_PAGE_SIZE = 200;
113976
+ async function collectScopes(storage, scopePrefix, isVisible) {
113977
+ const scopes = [];
113978
+ for (let scan = 0; ; scan += VISIBILITY_PAGE_SIZE) {
113979
+ const batch = storage.listScopes({
113980
+ scopePrefix,
113981
+ limit: VISIBILITY_PAGE_SIZE,
113982
+ offset: scan
113983
+ });
113984
+ for (const summary of batch.scopes) {
113985
+ if (!isVisible) {
113986
+ scopes.push(summary);
113987
+ continue;
113988
+ }
113989
+ const latest = storage.findEntry({
113990
+ scope: summary.scope,
113991
+ at: summary.latestCollectedAt
113992
+ });
113993
+ if (!latest || await isVisible(summary.scope, latest)) {
113994
+ scopes.push(summary);
113995
+ }
113996
+ }
113997
+ if (batch.scopes.length < VISIBILITY_PAGE_SIZE)
113998
+ break;
113999
+ }
114000
+ return scopes;
114001
+ }
113030
114002
  async function listDataScopesContract(input) {
113031
114003
  const limit = normalizeLimit(input.limit);
113032
114004
  const offset = normalizeOffset(input.offset);
113033
114005
  let page;
113034
114006
  let total;
113035
114007
  if (input.isVisible) {
113036
- const visible = [];
113037
- for (let scan = 0; ; scan += VISIBILITY_PAGE_SIZE) {
113038
- const batch = input.storage.listScopes({
113039
- scopePrefix: input.scopePrefix,
113040
- limit: VISIBILITY_PAGE_SIZE,
113041
- offset: scan
113042
- });
113043
- for (const summary of batch.scopes) {
113044
- const latest = input.storage.findEntry({
113045
- scope: summary.scope,
113046
- at: summary.latestCollectedAt
113047
- });
113048
- if (!latest || await input.isVisible(summary.scope, latest)) {
113049
- visible.push(summary);
113050
- }
113051
- }
113052
- if (batch.scopes.length < VISIBILITY_PAGE_SIZE)
113053
- break;
113054
- }
114008
+ const visible = await collectScopes(input.storage, input.scopePrefix, input.isVisible);
113055
114009
  total = visible.length;
113056
114010
  page = visible.slice(offset, offset + limit);
113057
114011
  } else {
@@ -113133,6 +114087,42 @@ async function listDataVersionsContract(input) {
113133
114087
  }
113134
114088
  };
113135
114089
  }
114090
+ function invalidQuery(message) {
114091
+ return {
114092
+ ok: false,
114093
+ status: 400,
114094
+ body: { error: "INVALID_QUERY", message }
114095
+ };
114096
+ }
114097
+ async function summarizeDataAdditionsContract(input) {
114098
+ if (!Number.isInteger(input.days) || input.days < 1 || input.days > 31) {
114099
+ return invalidQuery("days must be an integer between 1 and 31");
114100
+ }
114101
+ try {
114102
+ validateTimezone(input.timezone);
114103
+ } catch (err2) {
114104
+ if (err2 instanceof InvalidTimezoneError) {
114105
+ return invalidQuery("Unknown timezone");
114106
+ }
114107
+ throw err2;
114108
+ }
114109
+ const scopes = await collectScopes(input.storage, void 0, input.isVisible);
114110
+ async function* ledgers() {
114111
+ for (const summary of scopes) {
114112
+ const ledger = await ensureScopeLedger(input.storage, summary.scope, {
114113
+ now: input.now
114114
+ });
114115
+ if (ledger)
114116
+ yield ledger;
114117
+ }
114118
+ }
114119
+ return summarizeAdditions({
114120
+ ledgers: ledgers(),
114121
+ timezone: input.timezone,
114122
+ days: input.days,
114123
+ now: input.now
114124
+ });
114125
+ }
113136
114126
  async function readDataContract(input) {
113137
114127
  const scopeResult = parseDataScopeContract(input.scopeParam);
113138
114128
  if (!scopeResult.ok)
@@ -113162,7 +114152,7 @@ async function ingestDataContract(input) {
113162
114152
  const scopeResult = parseDataScopeContract(input.scopeParam);
113163
114153
  if (!scopeResult.ok)
113164
114154
  return scopeResult;
113165
- if (!isRecord3(input.body)) {
114155
+ if (!isRecord5(input.body)) {
113166
114156
  return {
113167
114157
  ok: false,
113168
114158
  status: 400,
@@ -113207,6 +114197,11 @@ async function ingestDataContract(input) {
113207
114197
  sizeBytes: writeResult.sizeBytes,
113208
114198
  afterTombstoneVersion: input.afterTombstoneVersion ?? null
113209
114199
  });
114200
+ await recordStoredVersion(input.storage, {
114201
+ scope: scopeResult.scope,
114202
+ collectedAt: input.collectedAt,
114203
+ data: envelope.data
114204
+ });
113210
114205
  return {
113211
114206
  ok: true,
113212
114207
  scope: scopeResult.scope,
@@ -113246,7 +114241,7 @@ async function ingestBinaryDataContract(input) {
113246
114241
  }
113247
114242
  };
113248
114243
  }
113249
- if (isRecord3(input.metadata) && (hasReservedWriterKey(input.metadata) || hasReservedLineageKey(input.metadata))) {
114244
+ if (isRecord5(input.metadata) && (hasReservedWriterKey(input.metadata) || hasReservedLineageKey(input.metadata))) {
113250
114245
  return {
113251
114246
  ok: false,
113252
114247
  status: 400,
@@ -113279,6 +114274,11 @@ async function ingestBinaryDataContract(input) {
113279
114274
  sizeBytes: input.bytes.length,
113280
114275
  afterTombstoneVersion: input.afterTombstoneVersion ?? null
113281
114276
  });
114277
+ await recordStoredVersion(input.storage, {
114278
+ scope: scopeResult.scope,
114279
+ collectedAt: input.collectedAt,
114280
+ data: envelope.data
114281
+ });
113282
114282
  return {
113283
114283
  ok: true,
113284
114284
  scope: scopeResult.scope,
@@ -114527,8 +115527,8 @@ function parseExpirySeconds(value) {
114527
115527
  const seconds = Number(value);
114528
115528
  return seconds > 0 ? seconds : void 0;
114529
115529
  }
114530
- const ms3 = Date.parse(value);
114531
- return Number.isFinite(ms3) ? Math.floor(ms3 / 1e3) : void 0;
115530
+ const ms4 = Date.parse(value);
115531
+ return Number.isFinite(ms4) ? Math.floor(ms4 / 1e3) : void 0;
114532
115532
  }
114533
115533
  function sortedUnique(values) {
114534
115534
  return Array.from(new Set(values)).sort();
@@ -115576,8 +116576,8 @@ async function buildChallenge(input) {
115576
116576
  }
115577
116577
  function base64decode(input) {
115578
116578
  let s10 = input.replace(/-/g, "+").replace(/_/g, "/");
115579
- const pad2 = (4 - s10.length % 4) % 4;
115580
- s10 += "=".repeat(pad2);
116579
+ const pad3 = (4 - s10.length % 4) % 4;
116580
+ s10 += "=".repeat(pad3);
115581
116581
  const binString = atob(s10);
115582
116582
  const bytes2 = Uint8Array.from(binString, (c10) => c10.codePointAt(0) ?? 0);
115583
116583
  return new TextDecoder().decode(bytes2);
@@ -116198,6 +117198,23 @@ async function handlePersonalServerDataRequest(request2, deps, options = {}) {
116198
117198
  });
116199
117199
  return jsonResponse(result.response);
116200
117200
  }
117201
+ if (pathname === "/additions") {
117202
+ if (request2.method !== "GET")
117203
+ return methodNotAllowed();
117204
+ await deps.auth.authorizeOwner(request2);
117205
+ const daysRaw = url2.searchParams.get("days");
117206
+ const result = await summarizeDataAdditionsContract({
117207
+ storage: deps.storage,
117208
+ isVisible: discoveryVisibility(deps),
117209
+ timezone: url2.searchParams.get("tz") ?? "UTC",
117210
+ // Digits only: Number() would also accept "", "1e1", "0x7" and " 7".
117211
+ days: daysRaw === null ? 7 : /^\d{1,3}$/.test(daysRaw) ? Number(daysRaw) : Number.NaN,
117212
+ now: (deps.now ?? (() => /* @__PURE__ */ new Date()))()
117213
+ });
117214
+ if ("ok" in result)
117215
+ return contractErrorResponse(result);
117216
+ return jsonResponse(result);
117217
+ }
116201
117218
  const parts = pathname.split("/").filter(Boolean);
116202
117219
  if (parts.length === 2 && parts[1] === "versions") {
116203
117220
  if (request2.method !== "GET")
@@ -117359,7 +118376,8 @@ function normalizeState(state) {
117359
118376
  })),
117360
118377
  envelopes: state.envelopes,
117361
118378
  blockManifests: state.blockManifests ?? [],
117362
- blockPayloads: state.blockPayloads ?? []
118379
+ blockPayloads: state.blockPayloads ?? [],
118380
+ ...state.pendingLedgerDeletes?.length ? { pendingLedgerDeletes: state.pendingLedgerDeletes } : {}
117363
118381
  };
117364
118382
  }
117365
118383
  function createIndexedDbFallbackDataFileStore(envelopes, blockManifests = /* @__PURE__ */ new Map(), blockPayloads = /* @__PURE__ */ new Map()) {
@@ -117584,6 +118602,18 @@ function createIndexedDbPsLitePersistence(options = {}) {
117584
118602
  },
117585
118603
  async write(state) {
117586
118604
  await runIndexedDbTransaction(resolved, "readwrite", (store) => store.put(state, resolved.key));
118605
+ },
118606
+ // Side records live in the same object store under their own keys, which
118607
+ // a build that only knows `resolved.key` never reads.
118608
+ async readAux(name) {
118609
+ const value = await runIndexedDbTransaction(resolved, "readonly", (store) => store.get(`${resolved.key}:${name}`));
118610
+ return value ?? null;
118611
+ },
118612
+ async writeAux(name, value) {
118613
+ await runIndexedDbTransaction(resolved, "readwrite", (store) => store.put(value, `${resolved.key}:${name}`));
118614
+ },
118615
+ async deleteAux(name) {
118616
+ await runIndexedDbTransaction(resolved, "readwrite", (store) => store.delete(`${resolved.key}:${name}`));
117587
118617
  }
117588
118618
  };
117589
118619
  }
@@ -117595,6 +118625,9 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117595
118625
  ]));
117596
118626
  const fallbackBlockManifests = new Map((state.blockManifests ?? []).map(({ path, manifest }) => [path, manifest]));
117597
118627
  const fallbackBlockPayloads = new Map((state.blockPayloads ?? []).map(({ path, block }) => [path, block]));
118628
+ const ledgerAux = (scope) => `first-seen:${scope}`;
118629
+ const firstSeenLedgers = /* @__PURE__ */ new Map();
118630
+ const pendingLedgerDeletes = new Set(state.pendingLedgerDeletes ?? []);
117598
118631
  const fallbackStore = createIndexedDbFallbackDataFileStore(fallbackEnvelopes, fallbackBlockManifests, fallbackBlockPayloads);
117599
118632
  const fileStore = dataFileStore ?? (adapter.kind !== "custom" && await isOpfsAvailable() ? await createOpfsPsLiteDataFileStore() : fallbackStore);
117600
118633
  const capabilities = {
@@ -117608,7 +118641,8 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117608
118641
  ...state,
117609
118642
  envelopes: fileStore.kind === "indexeddb" ? Array.from(fallbackEnvelopes.values()) : [],
117610
118643
  blockManifests: Array.from(fallbackBlockManifests.entries()).map(([path, manifest]) => ({ path, manifest })),
117611
- blockPayloads: Array.from(fallbackBlockPayloads.entries()).map(([path, block]) => ({ path, block }))
118644
+ blockPayloads: Array.from(fallbackBlockPayloads.entries()).map(([path, block]) => ({ path, block })),
118645
+ pendingLedgerDeletes: pendingLedgerDeletes.size > 0 ? Array.from(pendingLedgerDeletes) : void 0
117612
118646
  };
117613
118647
  const write = persistQueue.then(() => persistence.write(snapshot));
117614
118648
  persistQueue = write.catch(() => void 0);
@@ -117886,33 +118920,55 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117886
118920
  }
117887
118921
  return updated;
117888
118922
  },
117889
- async deleteScope(scope) {
117890
- let deleted = 0;
117891
- const deletedPaths = [];
117892
- const deletedBlockTrees = [];
117893
- state = {
117894
- ...state,
117895
- entries: state.entries.filter((entry) => {
117896
- if (entry.scope !== scope)
117897
- return true;
117898
- deleted += 1;
117899
- deletedPaths.push(envelopePath(entry.scope, entry.collectedAt));
117900
- deletedBlockTrees.push(blockTreePath(entry.scope, entry.collectedAt));
117901
- return false;
117902
- })
117903
- };
117904
- await Promise.all([
117905
- ...deletedPaths.flatMap((path) => [
117906
- fileStore.deleteEnvelope(path),
117907
- fallbackStore.deleteEnvelope(path)
117908
- ]),
117909
- ...deletedBlockTrees.flatMap((path) => [
117910
- fileStore.deleteBlockTree?.(path),
117911
- fallbackStore.deleteBlockTree?.(path)
117912
- ])
117913
- ].filter((promise2) => promise2 !== void 0));
117914
- await persist();
117915
- return deleted;
118923
+ async readFirstSeenLedger(scope) {
118924
+ if (!firstSeenLedgers.has(scope)) {
118925
+ const stored = await persistence.readAux?.(ledgerAux(scope)) ?? null;
118926
+ if (stored !== null)
118927
+ firstSeenLedgers.set(scope, stored);
118928
+ }
118929
+ return structuredClone(firstSeenLedgers.get(scope) ?? null);
118930
+ },
118931
+ async writeFirstSeenLedger(scope, ledger) {
118932
+ firstSeenLedgers.set(scope, structuredClone(ledger));
118933
+ await persistence.writeAux?.(ledgerAux(scope), ledger);
118934
+ },
118935
+ async deleteFirstSeenLedger(scope) {
118936
+ firstSeenLedgers.delete(scope);
118937
+ await persistence.deleteAux?.(ledgerAux(scope));
118938
+ },
118939
+ // Deletions run under the scope's first-seen sidecar lock (a rebuild in
118940
+ // flight cannot write it back), and the sidecar goes with the scope's last
118941
+ // version, so no delete path leaves a stale ledger behind.
118942
+ deleteScope(scope) {
118943
+ return withScopeLock(scope, async () => {
118944
+ await dropLedger(scope);
118945
+ let deleted = 0;
118946
+ const deletedPaths = [];
118947
+ const deletedBlockTrees = [];
118948
+ state = {
118949
+ ...state,
118950
+ entries: state.entries.filter((entry) => {
118951
+ if (entry.scope !== scope)
118952
+ return true;
118953
+ deleted += 1;
118954
+ deletedPaths.push(envelopePath(entry.scope, entry.collectedAt));
118955
+ deletedBlockTrees.push(blockTreePath(entry.scope, entry.collectedAt));
118956
+ return false;
118957
+ })
118958
+ };
118959
+ await Promise.all([
118960
+ ...deletedPaths.flatMap((path) => [
118961
+ fileStore.deleteEnvelope(path),
118962
+ fallbackStore.deleteEnvelope(path)
118963
+ ]),
118964
+ ...deletedBlockTrees.flatMap((path) => [
118965
+ fileStore.deleteBlockTree?.(path),
118966
+ fallbackStore.deleteBlockTree?.(path)
118967
+ ])
118968
+ ].filter((promise2) => promise2 !== void 0));
118969
+ await persist();
118970
+ return deleted;
118971
+ });
117916
118972
  },
117917
118973
  async deleteVersion(scope, collectedAt) {
117918
118974
  const entry = state.entries.find((e10) => e10.scope === scope && e10.collectedAt === collectedAt);
@@ -117927,7 +118983,35 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117927
118983
  return removeEntry(entry);
117928
118984
  }
117929
118985
  };
118986
+ async function dropLedger(scope) {
118987
+ const attempt = async (name) => {
118988
+ try {
118989
+ await storagePort.deleteFirstSeenLedger(name);
118990
+ return true;
118991
+ } catch {
118992
+ return false;
118993
+ }
118994
+ };
118995
+ const before = pendingLedgerDeletes.size;
118996
+ if (await attempt(scope))
118997
+ pendingLedgerDeletes.delete(scope);
118998
+ else
118999
+ pendingLedgerDeletes.add(scope);
119000
+ for (const other of [...pendingLedgerDeletes]) {
119001
+ if (other === scope)
119002
+ continue;
119003
+ const stillStored = state.entries.some((e10) => e10.scope === other);
119004
+ if (stillStored || await attempt(other))
119005
+ pendingLedgerDeletes.delete(other);
119006
+ }
119007
+ if (pendingLedgerDeletes.size !== before) {
119008
+ await persist().catch(() => void 0);
119009
+ }
119010
+ }
117930
119011
  async function removeEntry(entry) {
119012
+ return withScopeLock(entry.scope, () => removeEntryLocked(entry));
119013
+ }
119014
+ async function removeEntryLocked(entry) {
117931
119015
  const blobPath = envelopePath(entry.scope, entry.collectedAt);
117932
119016
  await Promise.all([
117933
119017
  fileStore.deleteEnvelope(blobPath),
@@ -117940,6 +119024,9 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117940
119024
  entries: state.entries.filter((e10) => e10 !== entry)
117941
119025
  };
117942
119026
  await persist();
119027
+ if (!state.entries.some((e10) => e10.scope === entry.scope)) {
119028
+ await dropLedger(entry.scope);
119029
+ }
117943
119030
  return true;
117944
119031
  }
117945
119032
  if (fileStore.readEnvelopePreview) {
@@ -117953,6 +119040,20 @@ async function createPersistentPsLiteStorage(adapter, persistence = createIndexe
117953
119040
  return preview;
117954
119041
  };
117955
119042
  }
119043
+ if (pendingLedgerDeletes.size > 0) {
119044
+ for (const scope of [...pendingLedgerDeletes]) {
119045
+ if (state.entries.some((e10) => e10.scope === scope)) {
119046
+ pendingLedgerDeletes.delete(scope);
119047
+ continue;
119048
+ }
119049
+ try {
119050
+ await storagePort.deleteFirstSeenLedger(scope);
119051
+ pendingLedgerDeletes.delete(scope);
119052
+ } catch {
119053
+ }
119054
+ }
119055
+ await persist().catch(() => void 0);
119056
+ }
117956
119057
  return storagePort;
117957
119058
  }
117958
119059
  async function readPreviewFromFileStore(store, path, maxBytes) {
@@ -118341,7 +119442,7 @@ var KEYS_BY_TYPE = {
118341
119442
  boolean: ["name", "type", "required"],
118342
119443
  enum: ["name", "type", "required", "values"]
118343
119444
  };
118344
- function isRecord4(value) {
119445
+ function isRecord6(value) {
118345
119446
  return value !== null && typeof value === "object" && !Array.isArray(value);
118346
119447
  }
118347
119448
  function invalid(message, details) {
@@ -118402,7 +119503,7 @@ function parseEnumValues(value, index) {
118402
119503
  return values;
118403
119504
  }
118404
119505
  function parseField(value, index) {
118405
- if (!isRecord4(value)) {
119506
+ if (!isRecord6(value)) {
118406
119507
  invalid(`answerShape.fields[${index}] must be an object`, { index });
118407
119508
  }
118408
119509
  const name = parseFieldName(value.name, index);
@@ -118458,7 +119559,7 @@ function parseField(value, index) {
118458
119559
  function parseAnswerShapeInput(value) {
118459
119560
  if (value === void 0 || value === null)
118460
119561
  return null;
118461
- if (!isRecord4(value)) {
119562
+ if (!isRecord6(value)) {
118462
119563
  invalid("answerShape must be an object with a fields array", {});
118463
119564
  }
118464
119565
  for (const key of Object.keys(value)) {
@@ -118671,7 +119772,7 @@ var MAX_QUESTION_CHARS = 8e3;
118671
119772
  var MAX_MODEL_CHARS = 128;
118672
119773
  var MAX_ECHOED_SCOPE_CHARS = 128;
118673
119774
  var MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._:/-]*$/;
118674
- function isRecord5(value) {
119775
+ function isRecord7(value) {
118675
119776
  return value !== null && typeof value === "object" && !Array.isArray(value);
118676
119777
  }
118677
119778
  function parseScope(value, field2) {
@@ -118687,7 +119788,7 @@ function parseScope(value, field2) {
118687
119788
  return parsed.scope;
118688
119789
  }
118689
119790
  function parseQuestionInput(body) {
118690
- if (!isRecord5(body)) {
119791
+ if (!isRecord7(body)) {
118691
119792
  throw new DerivativeQuestionInvalidError("Body must be a JSON object");
118692
119793
  }
118693
119794
  const derivedScope = parseScope(body.derivedScope, "derivedScope");
@@ -118823,11 +119924,11 @@ var TIMESTAMP_KEYS = [
118823
119924
  "published_at"
118824
119925
  ];
118825
119926
  var RESERVED_KEYS = /* @__PURE__ */ new Set(["$lineage", "$writtenBy", "$binary"]);
118826
- function isRecord6(value) {
119927
+ function isRecord8(value) {
118827
119928
  return value !== null && typeof value === "object" && !Array.isArray(value);
118828
119929
  }
118829
119930
  function timestampOf(item) {
118830
- if (!isRecord6(item))
119931
+ if (!isRecord8(item))
118831
119932
  return null;
118832
119933
  for (const key of TIMESTAMP_KEYS) {
118833
119934
  const value = item[key];
@@ -118868,7 +119969,7 @@ function trimSourceData(data, options = {}) {
118868
119969
  total = 0;
118869
119970
  if (Array.isArray(data))
118870
119971
  return trimArray(data, limit2);
118871
- if (isRecord6(data)) {
119972
+ if (isRecord8(data)) {
118872
119973
  const stamped = "$lineage" in data;
118873
119974
  const out = {};
118874
119975
  for (const [key, value] of Object.entries(data)) {
@@ -118982,7 +120083,7 @@ function parseAnswer(content) {
118982
120083
  for (const candidate of jsonFramings(content)) {
118983
120084
  try {
118984
120085
  const parsed = JSON.parse(candidate);
118985
- if (isRecord6(parsed) && typeof parsed.answer === "string") {
120086
+ if (isRecord8(parsed) && typeof parsed.answer === "string") {
118986
120087
  return {
118987
120088
  answer: parsed.answer,
118988
120089
  evidence: typeof parsed.evidence === "string" ? parsed.evidence : null
@@ -119002,10 +120103,10 @@ function parseShapedAnswer(content) {
119002
120103
  } catch {
119003
120104
  continue;
119004
120105
  }
119005
- if (!isRecord6(parsed))
120106
+ if (!isRecord8(parsed))
119006
120107
  continue;
119007
120108
  const evidence = typeof parsed.evidence === "string" ? parsed.evidence : null;
119008
- if (isRecord6(parsed.answer)) {
120109
+ if (isRecord8(parsed.answer)) {
119009
120110
  found.push({ answer: parsed.answer, evidence });
119010
120111
  }
119011
120112
  found.push({ answer: parsed, evidence });
@@ -119038,11 +120139,11 @@ var InferenceRequestError = class extends Error {
119038
120139
  this.retryable = options.retryable;
119039
120140
  }
119040
120141
  };
119041
- function isRecord7(value) {
120142
+ function isRecord9(value) {
119042
120143
  return value !== null && typeof value === "object" && !Array.isArray(value);
119043
120144
  }
119044
120145
  function readUsage(value) {
119045
- if (!isRecord7(value))
120146
+ if (!isRecord9(value))
119046
120147
  return void 0;
119047
120148
  const num2 = (v10) => typeof v10 === "number" ? v10 : void 0;
119048
120149
  const usage = {
@@ -119053,10 +120154,10 @@ function readUsage(value) {
119053
120154
  return usage;
119054
120155
  }
119055
120156
  function readContent(body) {
119056
- if (!isRecord7(body) || !Array.isArray(body.choices))
120157
+ if (!isRecord9(body) || !Array.isArray(body.choices))
119057
120158
  return null;
119058
120159
  const first = body.choices[0];
119059
- if (!isRecord7(first) || !isRecord7(first.message))
120160
+ if (!isRecord9(first) || !isRecord9(first.message))
119060
120161
  return null;
119061
120162
  const index = typeof first.index === "number" ? first.index : 0;
119062
120163
  const field2 = `choices.${index}.message.content`;
@@ -119065,7 +120166,7 @@ function readContent(body) {
119065
120166
  if (typeof content === "string")
119066
120167
  return { content, field: field2, id: id2 };
119067
120168
  if (Array.isArray(content)) {
119068
- const text = content.map((part) => isRecord7(part) && typeof part.text === "string" ? part.text : "").join("");
120169
+ const text = content.map((part) => isRecord9(part) && typeof part.text === "string" ? part.text : "").join("");
119069
120170
  return { content: text, field: field2, id: id2 };
119070
120171
  }
119071
120172
  return null;
@@ -119073,7 +120174,7 @@ function readContent(body) {
119073
120174
  async function readErrorType(response) {
119074
120175
  try {
119075
120176
  const body = await response.json();
119076
- if (isRecord7(body) && isRecord7(body.error)) {
120177
+ if (isRecord9(body) && isRecord9(body.error)) {
119077
120178
  return typeof body.error.type === "string" ? body.error.type : null;
119078
120179
  }
119079
120180
  } catch {
@@ -119181,7 +120282,7 @@ function createOpenAiCompatibleInferenceProvider(options = {}) {
119181
120282
  ok: true,
119182
120283
  result: {
119183
120284
  content,
119184
- usage: readUsage(isRecord7(parsed) ? parsed.usage : void 0),
120285
+ usage: readUsage(isRecord9(parsed) ? parsed.usage : void 0),
119185
120286
  ...receiptId ? { receiptId } : {},
119186
120287
  ...aciIdentity ? { aciIdentity } : {}
119187
120288
  }
@@ -119214,7 +120315,7 @@ function isRetryableInferenceError(err2) {
119214
120315
  }
119215
120316
  async function withRetries(deps, attempt, retryable) {
119216
120317
  const delays = deps.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;
119217
- const sleep2 = deps.sleep ?? ((ms3) => new Promise((r10) => setTimeout(r10, ms3)));
120318
+ const sleep2 = deps.sleep ?? ((ms4) => new Promise((r10) => setTimeout(r10, ms4)));
119218
120319
  for (let index = 0; ; index += 1) {
119219
120320
  try {
119220
120321
  return await attempt();
@@ -119592,7 +120693,7 @@ function isTransientFailureOutcome(outcome) {
119592
120693
  return shaped.status === "failed" && shaped.registration?.errorCode === "inference_unavailable";
119593
120694
  }
119594
120695
  var defaultTimers = {
119595
- setTimeout: (callback, ms3) => setTimeout(callback, ms3),
120696
+ setTimeout: (callback, ms4) => setTimeout(callback, ms4),
119596
120697
  clearTimeout: (handle) => clearTimeout(handle)
119597
120698
  };
119598
120699
  function createRecomputeScheduler(options) {
@@ -120287,25 +121388,25 @@ var E2eeAttestationError = class extends Error {
120287
121388
  this.name = "E2eeAttestationError";
120288
121389
  }
120289
121390
  };
120290
- function isRecord8(value) {
121391
+ function isRecord10(value) {
120291
121392
  return value !== null && typeof value === "object" && !Array.isArray(value);
120292
121393
  }
120293
- async function sha256Hex2(bytes2) {
121394
+ async function sha256Hex3(bytes2) {
120294
121395
  const buffer = new ArrayBuffer(bytes2.byteLength);
120295
121396
  new Uint8Array(buffer).set(bytes2);
120296
121397
  const digest2 = await globalThis.crypto.subtle.digest("SHA-256", buffer);
120297
121398
  return bytesToHex6(new Uint8Array(digest2));
120298
121399
  }
120299
121400
  async function workloadKeysetDigest(keyset) {
120300
- return `sha256:${await sha256Hex2(canonicalJsonBytes(keyset))}`;
121401
+ return `sha256:${await sha256Hex3(canonicalJsonBytes(keyset))}`;
120301
121402
  }
120302
121403
  async function reportDataFor(keysetDigest, nonce) {
120303
121404
  const nonceJson = nonce === null ? "null" : `"${nonce}"`;
120304
121405
  const statement = `{"keyset_digest":"${keysetDigest}","nonce":${nonceJson},"purpose":"${ACI_REPORT_DATA_PURPOSE}"}`;
120305
- return sha256Hex2(new TextEncoder().encode(statement));
121406
+ return sha256Hex3(new TextEncoder().encode(statement));
120306
121407
  }
120307
121408
  function parseReport(body) {
120308
- if (!isRecord8(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord8(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord8(body.attestation.workload_keyset)) {
121409
+ if (!isRecord10(body) || typeof body.api_version !== "string" || typeof body.workload_keyset_digest !== "string" || !isRecord10(body.attestation) || typeof body.attestation.tee_type !== "string" || typeof body.attestation.report_data !== "string" || !isRecord10(body.attestation.workload_keyset)) {
120309
121410
  throw new E2eeAttestationError("malformed_report", "attestation report is missing required fields");
120310
121411
  }
120311
121412
  const keyset = body.attestation.workload_keyset;
@@ -120352,7 +121453,7 @@ async function verifyAciReportBinding(report, nonce, nowSeconds) {
120352
121453
  }
120353
121454
  function selectX25519Key(keyset, expectedKeyId) {
120354
121455
  for (const entry of keyset.e2ee_public_keys) {
120355
- if (!isRecord8(entry) || entry.algo !== E2EE_ALGO_X25519)
121456
+ if (!isRecord10(entry) || entry.algo !== E2EE_ALGO_X25519)
120356
121457
  continue;
120357
121458
  if (typeof entry.public_key !== "string")
120358
121459
  continue;
@@ -122912,7 +124013,7 @@ var objectToNumericMapAsync = async (object3) => {
122912
124013
  }
122913
124014
  return map2;
122914
124015
  };
122915
- var wait = (ms3) => new Promise((resolve) => setTimeout(resolve, ms3));
124016
+ var wait = (ms4) => new Promise((resolve) => setTimeout(resolve, ms4));
122916
124017
  var SPACE_OR_PUNCTUATION = /[\n\r\p{Z}\p{P}]+/u;
122917
124018
 
122918
124019
  // ../core/dist/mcp/search/index.js
@@ -137148,6 +138249,11 @@ async function downloadOne(deps, record2) {
137148
138249
  throw err2;
137149
138250
  }
137150
138251
  diagnostics?.onIndexEnd(record2.id, envelope.scope);
138252
+ await recordStoredVersion(storage, {
138253
+ scope: envelope.scope,
138254
+ collectedAt: envelope.collectedAt,
138255
+ data: envelope.data
138256
+ });
137151
138257
  logger.info({ dataPointId: record2.id, scope: envelope.scope, path: relativePath }, "Downloaded and indexed data point");
137152
138258
  if (deps.onDataPointIndexed) {
137153
138259
  try {
@@ -137318,6 +138424,12 @@ async function reconcileDeletedDataPoint(deps, record2, deletedAt) {
137318
138424
  if (orphanKeys.length > 0 && deps.pendingBlobDeletions) {
137319
138425
  await deps.pendingBlobDeletions.add(orphanKeys);
137320
138426
  }
138427
+ if (removed > 0 || kept === 0) {
138428
+ try {
138429
+ await storage.deleteFirstSeenLedger?.(record2.scope);
138430
+ } catch {
138431
+ }
138432
+ }
137321
138433
  if (removed > 0 || kept > 0) {
137322
138434
  logger.info({ dataPointId: record2.id, scope: record2.scope, deletedAt, removed, kept }, "Reconciled gateway deletion against local index");
137323
138435
  }