@tensorix/metahub 0.5.0 → 0.5.2

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.
@@ -279,9 +279,9 @@ function lcs(a, b) {
279
279
  const m = a.length;
280
280
  const n = b.length;
281
281
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
282
- for (let i2 = m - 1;i2 >= 0; i2--)
283
- for (let j2 = n - 1;j2 >= 0; j2--)
284
- dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
282
+ for (let i = m - 1;i >= 0; i--)
283
+ for (let j = n - 1;j >= 0; j--)
284
+ dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
285
285
  const pairs = [];
286
286
  let i = 0;
287
287
  let j = 0;
@@ -411,6 +411,10 @@ var DOMAIN = {
411
411
  blob_policy: {
412
412
  table: "blob_policy",
413
413
  cols: new Set(["full_nodes", "redundancy", "__deleted"])
414
+ },
415
+ nodes: {
416
+ table: "nodes",
417
+ cols: new Set(["label", "platform", "form", "app", "first_seen", "__deleted"])
414
418
  }
415
419
  };
416
420
  var RECORD_META = new Set(["database_id", "created_hlc", "order_key", "__deleted"]);
@@ -428,7 +432,8 @@ var NOT_NULL_ZERO_COLS = new Set([
428
432
  "sites:spa",
429
433
  "site_channels:__deleted",
430
434
  "site_files:__deleted",
431
- "blob_policy:__deleted"
435
+ "blob_policy:__deleted",
436
+ "nodes:__deleted"
432
437
  ]);
433
438
  function decodeMaterializeValue(dataset, col, valueJson) {
434
439
  if (valueJson !== null)
@@ -463,8 +468,8 @@ function materialize(db, dataset, rowId, col, valueJson) {
463
468
  if (dataset === "records") {
464
469
  if (RECORD_META.has(col)) {
465
470
  ensureRow(db, "records", rowId);
466
- const v2 = decodeMaterializeValue("records", col, valueJson);
467
- db.query(`UPDATE records SET "${col}" = ? WHERE id = ?`).run(encodeScalar(v2), rowId);
471
+ const v = decodeMaterializeValue("records", col, valueJson);
472
+ db.query(`UPDATE records SET "${col}" = ? WHERE id = ?`).run(encodeScalar(v), rowId);
468
473
  } else {
469
474
  ensureRow(db, "records", rowId);
470
475
  if (valueJson === null) {
@@ -603,7 +608,7 @@ function nextPosition(db, databaseId) {
603
608
  const row = db.query("SELECT MAX(position) AS m FROM properties WHERE database_id = ? AND __deleted = 0").get(databaseId);
604
609
  return (row.m ?? 0) + 1;
605
610
  }
606
- var addProperty = grouped(function addProperty2(db, databaseId, opts) {
611
+ var addProperty = grouped(function addProperty(db, databaseId, opts) {
607
612
  if (!getDatabase(db, databaseId))
608
613
  throw new MhError("not_found", `no such database: ${databaseId}`);
609
614
  if (!PROP_TYPES.has(opts.type))
@@ -633,7 +638,7 @@ function listProperties(db, databaseId) {
633
638
  config: r.config ? JSON.parse(r.config) : null
634
639
  }));
635
640
  }
636
- var updateProperty = grouped(function updateProperty2(db, id, fields) {
641
+ var updateProperty = grouped(function updateProperty(db, id, fields) {
637
642
  const cur = getProperty(db, id);
638
643
  if (!cur)
639
644
  throw new MhError("not_found", `no such property: ${id}`);
@@ -677,7 +682,7 @@ function getSelectProperty(db, id) {
677
682
  throw new MhError("invalid_input", `${prop.type} property has no options`);
678
683
  return { prop, options: prop.config?.options ?? [] };
679
684
  }
680
- var renameSelectOption = grouped(function renameSelectOption2(db, id, from, to) {
685
+ var renameSelectOption = grouped(function renameSelectOption(db, id, from, to) {
681
686
  const { prop, options } = getSelectProperty(db, id);
682
687
  to = to.trim();
683
688
  if (!to)
@@ -710,7 +715,7 @@ var renameSelectOption = grouped(function renameSelectOption2(db, id, from, to)
710
715
  }
711
716
  return { property: getProperty(db, id), renamed };
712
717
  });
713
- var removeSelectOption = grouped(function removeSelectOption2(db, id, name) {
718
+ var removeSelectOption = grouped(function removeSelectOption(db, id, name) {
714
719
  const { prop, options } = getSelectProperty(db, id);
715
720
  if (!options.includes(name))
716
721
  throw new MhError("not_found", `no such option: ${name}`);
@@ -738,7 +743,7 @@ var removeSelectOption = grouped(function removeSelectOption2(db, id, name) {
738
743
  }
739
744
  return { property: getProperty(db, id), cleared };
740
745
  });
741
- var removeProperty = grouped(function removeProperty2(db, id) {
746
+ var removeProperty = grouped(function removeProperty(db, id) {
742
747
  const prop = getProperty(db, id);
743
748
  if (!prop)
744
749
  return false;
@@ -1041,7 +1046,7 @@ function rebalanceRecordOrderKeys(db, databaseId) {
1041
1046
  emit(db, "records", row.id, "order_key", keys[i]);
1042
1047
  });
1043
1048
  }
1044
- var backfillRecordOrderKeys = grouped(function backfillRecordOrderKeys2(db, databaseId) {
1049
+ var backfillRecordOrderKeys = grouped(function backfillRecordOrderKeys(db, databaseId) {
1045
1050
  const emitChange = canEmitOrderKeys(db);
1046
1051
  const dbRows = databaseId ? [{ database_id: databaseId }] : db.query(`SELECT DISTINCT database_id FROM records
1047
1052
  WHERE database_id IS NOT NULL AND __deleted = 0 AND order_key IS NULL`).all();
@@ -1056,7 +1061,7 @@ var backfillRecordOrderKeys = grouped(function backfillRecordOrderKeys2(db, data
1056
1061
  missing.forEach((row, i) => writeRecordOrderKey(db, row.id, keys[i], emitChange));
1057
1062
  }
1058
1063
  });
1059
- var createRecordPrepared = grouped(function createRecordPrepared2(db, database, cells) {
1064
+ var createRecordPrepared = grouped(function createRecordPrepared(db, database, cells) {
1060
1065
  const id = deriveTitle(cells, slugify(database.name, "rec"));
1061
1066
  const orderKey = keyBetween(lastRecordOrderKey(db, database.id), null);
1062
1067
  const first = emit(db, "records", id, "database_id", database.id);
@@ -1066,7 +1071,7 @@ var createRecordPrepared = grouped(function createRecordPrepared2(db, database,
1066
1071
  emit(db, "records", id, prop.id, value);
1067
1072
  return getRecord(db, id);
1068
1073
  });
1069
- var createRecord = grouped(function createRecord2(db, databaseId, data) {
1074
+ var createRecord = grouped(function createRecord(db, databaseId, data) {
1070
1075
  const database = getDatabase(db, databaseId);
1071
1076
  if (!database)
1072
1077
  throw new MhError("not_found", `no such database: ${databaseId}`);
@@ -1098,7 +1103,7 @@ function listRecords(db, databaseId, opts = {}) {
1098
1103
  key = key.slice(1);
1099
1104
  }
1100
1105
  if (key !== "created" && key !== "created_hlc") {
1101
- let p = props.find((p2) => p2.id === key);
1106
+ let p = props.find((p) => p.id === key);
1102
1107
  if (!p) {
1103
1108
  const matches = props.filter((q) => q.name.toLowerCase() === key.toLowerCase());
1104
1109
  if (matches.length > 1)
@@ -1136,7 +1141,7 @@ function listRecords(db, databaseId, opts = {}) {
1136
1141
  }
1137
1142
  return rows;
1138
1143
  }
1139
- var moveRecord = grouped(function moveRecord2(db, id, targetId, where) {
1144
+ var moveRecord = grouped(function moveRecord(db, id, targetId, where) {
1140
1145
  if (where !== "before" && where !== "after")
1141
1146
  throw new MhError("invalid_input", `unknown move position: ${where}`);
1142
1147
  const src = db.query("SELECT id, database_id FROM records WHERE id = ? AND __deleted = 0").get(id);
@@ -1166,7 +1171,7 @@ var moveRecord = grouped(function moveRecord2(db, id, targetId, where) {
1166
1171
  emit(db, "records", id, "order_key", keyBetween(left, right));
1167
1172
  return getRecord(db, id);
1168
1173
  });
1169
- var updateRecordPrepared = grouped(function updateRecordPrepared2(db, id, cells) {
1174
+ var updateRecordPrepared = grouped(function updateRecordPrepared(db, id, cells) {
1170
1175
  const rec = db.query("SELECT id, database_id, data FROM records WHERE id = ? AND __deleted = 0").get(id);
1171
1176
  if (!rec)
1172
1177
  throw new MhError("not_found", `no such record: ${id}`);
@@ -1179,13 +1184,13 @@ var updateRecordPrepared = grouped(function updateRecordPrepared2(db, id, cells)
1179
1184
  }
1180
1185
  return getRecord(db, id);
1181
1186
  });
1182
- var updateRecord = grouped(function updateRecord2(db, id, data) {
1187
+ var updateRecord = grouped(function updateRecord(db, id, data) {
1183
1188
  const rec = db.query("SELECT id, database_id FROM records WHERE id = ? AND __deleted = 0").get(id);
1184
1189
  if (!rec)
1185
1190
  throw new MhError("not_found", `no such record: ${id}`);
1186
1191
  return updateRecordPrepared(db, id, prepareRecordCells(db, rec.database_id, data));
1187
1192
  });
1188
- var deleteRecord = grouped(function deleteRecord2(db, id) {
1193
+ var deleteRecord = grouped(function deleteRecord(db, id) {
1189
1194
  const rec = db.query("SELECT id FROM records WHERE id = ? AND __deleted = 0").get(id);
1190
1195
  if (!rec)
1191
1196
  return false;
@@ -1201,7 +1206,7 @@ function rowOut(r) {
1201
1206
  return null;
1202
1207
  return { ...r, meta: r.meta === null ? null : JSON.parse(r.meta) };
1203
1208
  }
1204
- var createDatabase = grouped(function createDatabase2(db, opts) {
1209
+ var createDatabase = grouped(function createDatabase(db, opts) {
1205
1210
  const id = newId("db", opts.name);
1206
1211
  const first = emit(db, "databases", id, "name", opts.name);
1207
1212
  emit(db, "databases", id, "created_hlc", first.hlc);
@@ -1209,7 +1214,7 @@ var createDatabase = grouped(function createDatabase2(db, opts) {
1209
1214
  emit(db, "databases", id, "icon", opts.icon);
1210
1215
  return getDatabase(db, id);
1211
1216
  });
1212
- var updateDatabase = grouped(function updateDatabase2(db, id, fields) {
1217
+ var updateDatabase = grouped(function updateDatabase(db, id, fields) {
1213
1218
  if (!getDatabase(db, id))
1214
1219
  throw new MhError("not_found", `no such database: ${id}`);
1215
1220
  if (fields.name !== undefined)
@@ -1223,7 +1228,7 @@ var updateDatabase = grouped(function updateDatabase2(db, id, fields) {
1223
1228
  }
1224
1229
  return getDatabase(db, id);
1225
1230
  });
1226
- var duplicateDatabase = grouped(function duplicateDatabase2(db, id, opts = {}) {
1231
+ var duplicateDatabase = grouped(function duplicateDatabase(db, id, opts = {}) {
1227
1232
  const src = getDatabase(db, id);
1228
1233
  if (!src)
1229
1234
  throw new MhError("not_found", `no such database: ${id}`);
@@ -1275,7 +1280,7 @@ var duplicateDatabase = grouped(function duplicateDatabase2(db, id, opts = {}) {
1275
1280
  function getDatabase(db, id) {
1276
1281
  return rowOut(db.query(`SELECT ${DATABASE_SELECT} FROM databases WHERE id = ? AND __deleted = 0`).get(id));
1277
1282
  }
1278
- var deleteDatabase = grouped(function deleteDatabase2(db, id) {
1283
+ var deleteDatabase = grouped(function deleteDatabase(db, id) {
1279
1284
  if (!getDatabase(db, id))
1280
1285
  return false;
1281
1286
  emit(db, "databases", id, "__deleted", 1);
@@ -1599,14 +1604,14 @@ function prepareIntent(db, policy, principal, intent, clock) {
1599
1604
  if (intent.action === "createRecord") {
1600
1605
  if (!intent.table || typeof intent.table !== "string")
1601
1606
  throw new MhError("invalid_input", "createRecord intent needs a table");
1602
- const database2 = authorizeDbRef(db, policy.grants, intent.table, "create");
1603
- const cells2 = assertGuestPayload(db, policy.grants, principal, database2, intent.payload, { limits: policy.limits });
1607
+ const database = authorizeDbRef(db, policy.grants, intent.table, "create");
1608
+ const cells = assertGuestPayload(db, policy.grants, principal, database, intent.payload, { limits: policy.limits });
1604
1609
  return {
1605
1610
  action: intent.action,
1606
- database: database2,
1607
- cells: cells2,
1611
+ database,
1612
+ cells,
1608
1613
  targetLive: true,
1609
- fingerprint: fingerprintIntent(intent.action, database2.id, clock === "submitted" ? intent.recordId ?? null : null, cells2)
1614
+ fingerprint: fingerprintIntent(intent.action, database.id, clock === "submitted" ? intent.recordId ?? null : null, cells)
1610
1615
  };
1611
1616
  }
1612
1617
  if (intent.action !== "updateRecord")
@@ -1752,10 +1757,10 @@ function isRecordRow(value) {
1752
1757
  return typeof row.id === "string" && typeof row.database_id === "string" && !!row.values && typeof row.values === "object" && !Array.isArray(row.values) && !!row.cells && typeof row.cells === "object" && !Array.isArray(row.cells);
1753
1758
  }
1754
1759
  function clampMillis(submittedAt, now) {
1755
- const safeNow2 = Math.max(0, Math.trunc(Number.isFinite(now) ? now : Date.now()));
1756
- const ceil = safeNow2 + HLC_SKEW_MS;
1760
+ const safeNow = Math.max(0, Math.trunc(Number.isFinite(now) ? now : Date.now()));
1761
+ const ceil = safeNow + HLC_SKEW_MS;
1757
1762
  if (!Number.isFinite(submittedAt))
1758
- return safeNow2;
1763
+ return safeNow;
1759
1764
  return Math.max(0, Math.min(Math.trunc(submittedAt), ceil));
1760
1765
  }
1761
1766
  function seedCounter(db, node, millis) {
@@ -1795,12 +1800,12 @@ function stableStringify(value) {
1795
1800
 
1796
1801
  // src/core/sync/anti-abuse.ts
1797
1802
  function timingSafeEq(a, b) {
1798
- const ab2 = new TextEncoder().encode(a);
1803
+ const ab = new TextEncoder().encode(a);
1799
1804
  const bb = new TextEncoder().encode(b);
1800
- let diff = ab2.length ^ bb.length;
1801
- const n = Math.max(ab2.length, bb.length);
1805
+ let diff = ab.length ^ bb.length;
1806
+ const n = Math.max(ab.length, bb.length);
1802
1807
  for (let i = 0;i < n; i++)
1803
- diff |= (ab2[i] ?? 0) ^ (bb[i] ?? 0);
1808
+ diff |= (ab[i] ?? 0) ^ (bb[i] ?? 0);
1804
1809
  return diff === 0;
1805
1810
  }
1806
1811
  var siteverify = async (secret, token, ip) => {
@@ -2081,7 +2086,7 @@ function assertVisibility(v) {
2081
2086
  if (v !== "public" && v !== "private")
2082
2087
  throw new MhError("invalid_input", `visibility must be "public" or "private", got ${JSON.stringify(v)}`);
2083
2088
  }
2084
- var createSite = grouped(function createSite2(db, opts) {
2089
+ var createSite = grouped(function createSite(db, opts) {
2085
2090
  const name = normalizeSiteName(opts.name);
2086
2091
  if (getSiteByName(db, name))
2087
2092
  throw new MhError("conflict", `site name already exists: ${name}`);
@@ -2096,7 +2101,7 @@ var createSite = grouped(function createSite2(db, opts) {
2096
2101
  emit(db, "sites", id, "visibility", opts.visibility);
2097
2102
  return getSite(db, id);
2098
2103
  });
2099
- var updateSite = grouped(function updateSite2(db, id, opts) {
2104
+ var updateSite = grouped(function updateSite(db, id, opts) {
2100
2105
  if (!getSite(db, id))
2101
2106
  throw new MhError("not_found", `no such site: ${id}`);
2102
2107
  if (opts.visibility !== undefined)
@@ -2116,14 +2121,14 @@ var updateSite = grouped(function updateSite2(db, id, opts) {
2116
2121
  emit(db, "sites", id, "spa", opts.spa ? 1 : 0);
2117
2122
  return getSite(db, id);
2118
2123
  });
2119
- var setSitePublicGrants = grouped(function setSitePublicGrants2(db, siteId, grants) {
2124
+ var setSitePublicGrants = grouped(function setSitePublicGrants(db, siteId, grants) {
2120
2125
  if (!getSite(db, siteId))
2121
2126
  throw new MhError("not_found", `no such site: ${siteId}`);
2122
2127
  const value = grants == null ? null : serializeGrantSet(validateGrantSetInput(grants));
2123
2128
  emit(db, "sites", siteId, "public_grants", value);
2124
2129
  return getSite(db, siteId);
2125
2130
  });
2126
- var deleteSite = grouped(function deleteSite2(db, id) {
2131
+ var deleteSite = grouped(function deleteSite(db, id) {
2127
2132
  if (!getSite(db, id))
2128
2133
  return false;
2129
2134
  const files = db.query("SELECT id FROM site_files WHERE site_id = ? AND __deleted = 0").all(id);
@@ -2136,7 +2141,7 @@ function fileIdFor(db, siteId, path) {
2136
2141
  const row = db.query("SELECT id FROM site_files WHERE site_id = ? AND path = ? ORDER BY created_hlc LIMIT 1").get(siteId, path);
2137
2142
  return row?.id ?? null;
2138
2143
  }
2139
- var deleteFile = grouped(function deleteFile2(db, siteId, path) {
2144
+ var deleteFile = grouped(function deleteFile(db, siteId, path) {
2140
2145
  const id = fileIdFor(db, siteId, normalizeSitePath(path));
2141
2146
  if (!id)
2142
2147
  return false;
@@ -2509,6 +2514,22 @@ CREATE TABLE IF NOT EXISTS blob_policy (
2509
2514
  redundancy TEXT, -- 'all' | 'any'
2510
2515
  __deleted INTEGER NOT NULL DEFAULT 0
2511
2516
  );
2517
+
2518
+ -- Synced device roster (system tier, see tables.ts): one row per node id, written
2519
+ -- by the device itself on open (describeSelf) and by anyone who renames it.
2520
+ -- Replicates so every device shows the same name and icon for a peer — the only
2521
+ -- record a purely bucket-joined device leaves besides its oplog stream. Each
2522
+ -- column is its own LWW register, so a rename and a platform refresh never
2523
+ -- clobber each other. meta.node_label survives as a node-local read fallback.
2524
+ CREATE TABLE IF NOT EXISTS nodes (
2525
+ id TEXT PRIMARY KEY, -- node_id
2526
+ label TEXT, -- shared display name; any device may set it
2527
+ platform TEXT, -- macos | windows | linux | ios | android | web
2528
+ form TEXT, -- laptop | desktop | phone | server | browser
2529
+ app TEXT, -- cli | server | desktop | web
2530
+ first_seen INTEGER, -- epoch ms of the device's first self-description
2531
+ __deleted INTEGER NOT NULL DEFAULT 0
2532
+ );
2512
2533
  `;
2513
2534
  var FTS_SCHEMA = `
2514
2535
  CREATE VIRTUAL TABLE IF NOT EXISTS search_fts USING fts5(
@@ -2600,7 +2621,7 @@ function documentVersion(db, id) {
2600
2621
  OR (dataset = 'doc_blocks' AND row_id IN (SELECT id FROM doc_blocks WHERE doc_id = ?))`).get(id, id);
2601
2622
  return row.h ?? "";
2602
2623
  }
2603
- var createDocument = grouped(function createDocument2(db, opts) {
2624
+ var createDocument = grouped(function createDocument(db, opts) {
2604
2625
  const id = newId("doc", opts.title);
2605
2626
  const first = emit(db, "documents", id, "title", opts.title);
2606
2627
  emit(db, "documents", id, "created_hlc", first.hlc);
@@ -2664,7 +2685,7 @@ function backfillSiblings(db, parentId) {
2664
2685
  db.query("UPDATE documents SET order_key = ? WHERE id = ?").run(keys[i], row.id);
2665
2686
  });
2666
2687
  }
2667
- var backfillDocumentOrderKeys = grouped(function backfillDocumentOrderKeys2(db) {
2688
+ var backfillDocumentOrderKeys = grouped(function backfillDocumentOrderKeys(db) {
2668
2689
  const parents = db.query("SELECT DISTINCT parent_id FROM documents WHERE __deleted = 0 AND order_key IS NULL").all();
2669
2690
  for (const p of parents)
2670
2691
  backfillSiblings(db, p.parent_id);
@@ -2700,7 +2721,7 @@ function placeInSiblings(db, id, parentId, anchor) {
2700
2721
  }
2701
2722
  emit(db, "documents", id, "order_key", key);
2702
2723
  }
2703
- var moveDocument = grouped(function moveDocument2(db, id, targetId, where) {
2724
+ var moveDocument = grouped(function moveDocument(db, id, targetId, where) {
2704
2725
  const src = getDocument(db, id);
2705
2726
  if (!src)
2706
2727
  throw new MhError("not_found", `no such document: ${id}`);
@@ -2715,7 +2736,7 @@ var moveDocument = grouped(function moveDocument2(db, id, targetId, where) {
2715
2736
  placeInSiblings(db, id, target.parent_id, { targetId, where });
2716
2737
  return getDocument(db, id);
2717
2738
  });
2718
- var duplicateDocument = grouped(function duplicateDocument2(db, id, opts = {}) {
2739
+ var duplicateDocument = grouped(function duplicateDocument(db, id, opts = {}) {
2719
2740
  const src = getDocument(db, id);
2720
2741
  if (!src)
2721
2742
  throw new MhError("not_found", `no such document: ${id}`);
@@ -2733,7 +2754,7 @@ var duplicateDocument = grouped(function duplicateDocument2(db, id, opts = {}) {
2733
2754
  moveDocument(db, dup.id, src.id, "after");
2734
2755
  return getDocument(db, dup.id);
2735
2756
  });
2736
- var updateDocument = grouped(function updateDocument2(db, id, fields, opts = {}) {
2757
+ var updateDocument = grouped(function updateDocument(db, id, fields, opts = {}) {
2737
2758
  const doc = getDocument(db, id);
2738
2759
  if (!doc)
2739
2760
  throw new MhError("not_found", `no such document: ${id}`);
@@ -2754,7 +2775,7 @@ var updateDocument = grouped(function updateDocument2(db, id, fields, opts = {})
2754
2775
  }
2755
2776
  return getDocument(db, id);
2756
2777
  });
2757
- var editDocument = grouped(function editDocument2(db, id, opts) {
2778
+ var editDocument = grouped(function editDocument(db, id, opts) {
2758
2779
  if (!getDocument(db, id))
2759
2780
  throw new MhError("not_found", `no such document: ${id}`);
2760
2781
  if (opts.ifMatch !== undefined && documentVersion(db, id) !== opts.ifMatch)
@@ -2792,7 +2813,7 @@ var editDocument = grouped(function editDocument2(db, id, opts) {
2792
2813
  reconcileBody(db, id, newBody);
2793
2814
  return { id, changed: newBody !== body, replaced: opts.replaceAll ? count : 1, version: documentVersion(db, id) };
2794
2815
  });
2795
- var editDocumentBatch = grouped(function editDocumentBatch2(db, id, opts) {
2816
+ var editDocumentBatch = grouped(function editDocumentBatch(db, id, opts) {
2796
2817
  if (!getDocument(db, id))
2797
2818
  throw new MhError("not_found", `no such document: ${id}`);
2798
2819
  if (opts.edits.length === 0)
@@ -2819,7 +2840,7 @@ var editDocumentBatch = grouped(function editDocumentBatch2(db, id, opts) {
2819
2840
  reconcileBody(db, id, body);
2820
2841
  return { id, changed: body !== orig, replaced: total, version: documentVersion(db, id) };
2821
2842
  });
2822
- var appendDocument = grouped(function appendDocument2(db, id, body) {
2843
+ var appendDocument = grouped(function appendDocument(db, id, body) {
2823
2844
  if (!getDocument(db, id))
2824
2845
  throw new MhError("not_found", `no such document: ${id}`);
2825
2846
  ensureBlocks(db, id);
@@ -2831,7 +2852,7 @@ var appendDocument = grouped(function appendDocument2(db, id, body) {
2831
2852
  }
2832
2853
  return { id, changed: next.length > 0, replaced: next.length, version: documentVersion(db, id) };
2833
2854
  });
2834
- var prependDocument = grouped(function prependDocument2(db, id, body) {
2855
+ var prependDocument = grouped(function prependDocument(db, id, body) {
2835
2856
  if (!getDocument(db, id))
2836
2857
  throw new MhError("not_found", `no such document: ${id}`);
2837
2858
  ensureBlocks(db, id);
@@ -2843,7 +2864,7 @@ var prependDocument = grouped(function prependDocument2(db, id, body) {
2843
2864
  }
2844
2865
  return { id, changed: next.length > 0, replaced: next.length, version: documentVersion(db, id) };
2845
2866
  });
2846
- var deleteDocument = grouped(function deleteDocument2(db, id) {
2867
+ var deleteDocument = grouped(function deleteDocument(db, id) {
2847
2868
  if (!getDocument(db, id))
2848
2869
  return false;
2849
2870
  emit(db, "documents", id, "__deleted", 1);
@@ -3125,22 +3146,28 @@ function migrateSitesAccess(db) {
3125
3146
  backfill("public_grants", "NULL");
3126
3147
  }
3127
3148
  }
3128
- var SITE_CHANNELS_REPLAY_KEY = "site_channels_replay_seq";
3129
- function migrateSiteChannels(db) {
3130
- if (!tableExists3(db, "site_channels"))
3149
+ function replayDataset(db, dataset) {
3150
+ if (!tableExists3(db, dataset))
3131
3151
  return;
3132
- const wm = db.query("SELECT value FROM meta WHERE key = ?").get(SITE_CHANNELS_REPLAY_KEY);
3152
+ const key = `${dataset}_replay_seq`;
3153
+ const wm = db.query("SELECT value FROM meta WHERE key = ?").get(key);
3133
3154
  const since = wm ? Number(wm.value) : 0;
3134
3155
  const changes = db.query(`SELECT seq, ${CHANGE_SELECT} FROM crdt_changes
3135
- WHERE dataset = 'site_channels' AND seq > ? ORDER BY seq`).all(since);
3156
+ WHERE dataset = ? AND seq > ? ORDER BY seq`).all(dataset, since);
3136
3157
  if (changes.length === 0)
3137
3158
  return;
3138
3159
  db.transaction((rows) => {
3139
3160
  for (const change of rows)
3140
3161
  applyChange(db, change);
3141
- db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(SITE_CHANNELS_REPLAY_KEY, String(rows[rows.length - 1].seq));
3162
+ db.query("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, String(rows[rows.length - 1].seq));
3142
3163
  })(changes);
3143
3164
  }
3165
+ function migrateSiteChannels(db) {
3166
+ replayDataset(db, "site_channels");
3167
+ }
3168
+ function migrateNodes(db) {
3169
+ replayDataset(db, "nodes");
3170
+ }
3144
3171
  function initSchema(db) {
3145
3172
  runSchema(db);
3146
3173
  migrateOplog(db);
@@ -3155,6 +3182,7 @@ function initSchema(db) {
3155
3182
  migrateShares(db);
3156
3183
  migrateSitesAccess(db);
3157
3184
  migrateSiteChannels(db);
3185
+ migrateNodes(db);
3158
3186
  }
3159
3187
 
3160
3188
  // src/core/sync/partition.ts
@@ -3543,7 +3571,7 @@ function createRoomFetch(deps) {
3543
3571
  if (!body || typeof body.guestBase !== "string" || body.guestBase === "")
3544
3572
  return json({ error: "provision needs a guestBase", code: "invalid_input" }, 400);
3545
3573
  const str = (v) => typeof v === "string" && v !== "" ? v : null;
3546
- const cfg3 = initRoomDb(db, {
3574
+ const cfg = initRoomDb(db, {
3547
3575
  slug: typeof body.slug === "string" && body.slug !== "" ? body.slug : pathSlug,
3548
3576
  guestBase: body.guestBase,
3549
3577
  grants: str(body.grants) ?? "",
@@ -3551,11 +3579,11 @@ function createRoomFetch(deps) {
3551
3579
  pwSalt: str(body.pwSalt),
3552
3580
  expiresAt: typeof body.expiresAt === "number" ? body.expiresAt : null
3553
3581
  });
3554
- await deps.setAlarm?.(cfg3.expiresAt ?? null);
3555
- return json({ slug: cfg3.slug, room_node: cfg3.roomNode, provisioned: true });
3582
+ await deps.setAlarm?.(cfg.expiresAt ?? null);
3583
+ return json({ slug: cfg.slug, room_node: cfg.roomNode, provisioned: true });
3556
3584
  }
3557
- const cfg2 = tryConfig(db);
3558
- if (!cfg2)
3585
+ const cfg = tryConfig(db);
3586
+ if (!cfg)
3559
3587
  return json({ error: "room is not provisioned", code: "not_found" }, 404);
3560
3588
  if (sub === "owner/sync") {
3561
3589
  const body = await req.json().catch(() => null);
@@ -3563,7 +3591,7 @@ function createRoomFetch(deps) {
3563
3591
  return json({ error: "malformed sync request", code: "invalid_input" }, 400);
3564
3592
  await hlcHeadroom(db, deps);
3565
3593
  try {
3566
- const resp = handleOwnerSync(db, body, cfg2);
3594
+ const resp = handleOwnerSync(db, body, cfg);
3567
3595
  if (resp.share_state === "active" && body.changes.length > 0)
3568
3596
  deps.poke?.(maxSeq(db));
3569
3597
  return json(resp);
@@ -3662,10 +3690,10 @@ function serveRoomFile(db, cfg, path) {
3662
3690
  if (row.encoding === "utf8")
3663
3691
  return new Response(row.content ?? "", { status, headers });
3664
3692
  if (row.encoding === "base64") {
3665
- const bytes2 = base64ToBytes(row.content ?? "");
3666
- const body2 = new Uint8Array(bytes2.byteLength);
3667
- body2.set(bytes2);
3668
- return new Response(body2.buffer, { status, headers });
3693
+ const bytes = base64ToBytes(row.content ?? "");
3694
+ const body = new Uint8Array(bytes.byteLength);
3695
+ body.set(bytes);
3696
+ return new Response(body.buffer, { status, headers });
3669
3697
  }
3670
3698
  const bytes = row.content ? roomBlobBytes(db, row.content) : null;
3671
3699
  if (!bytes)
@@ -4066,8 +4094,8 @@ var edge_worker_default = {
4066
4094
  if (slug === null)
4067
4095
  return new Response("bad request", { status: 400 });
4068
4096
  const id = env.ROOM.idFromName(slug);
4069
- const res2 = await env.ROOM.get(id).fetch(req);
4070
- return cors ? corsResponse(res2) : res2;
4097
+ const res = await env.ROOM.get(id).fetch(req);
4098
+ return cors ? corsResponse(res) : res;
4071
4099
  }
4072
4100
  const res = await createInboxFetch({ sql: d1Sql(env.DB), ownerToken: env.OWNER_TOKEN })(req);
4073
4101
  return cors ? corsResponse(res) : res;