@tensorix/metahub 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli.js +2311 -1633
  2. package/dist/cli.js.map +26 -22
  3. package/dist/core/audit.d.ts +97 -0
  4. package/dist/core/audit.d.ts.map +1 -0
  5. package/dist/core/crdt.d.ts +13 -1
  6. package/dist/core/crdt.d.ts.map +1 -1
  7. package/dist/core/databases.d.ts.map +1 -1
  8. package/dist/core/documents.d.ts +6 -0
  9. package/dist/core/documents.d.ts.map +1 -1
  10. package/dist/core/grants-core.d.ts.map +1 -1
  11. package/dist/core/history.d.ts +31 -0
  12. package/dist/core/history.d.ts.map +1 -1
  13. package/dist/core/indexing.d.ts +1 -1
  14. package/dist/core/integrity.d.ts +1 -1
  15. package/dist/core/integrity.d.ts.map +1 -1
  16. package/dist/core/properties.d.ts +1 -1
  17. package/dist/core/properties.d.ts.map +1 -1
  18. package/dist/core/records.d.ts +5 -0
  19. package/dist/core/records.d.ts.map +1 -1
  20. package/dist/core/relation-cells.d.ts +23 -0
  21. package/dist/core/relation-cells.d.ts.map +1 -0
  22. package/dist/core/resolve.d.ts +4 -0
  23. package/dist/core/resolve.d.ts.map +1 -1
  24. package/dist/core/sync/files.d.ts.map +1 -1
  25. package/dist/core/sync/server.d.ts.map +1 -1
  26. package/dist/core/sync/share-serve.d.ts.map +1 -1
  27. package/dist/db-worker.js +100 -98
  28. package/dist/edge-worker.js +74 -26
  29. package/dist/index.js +2267 -1423
  30. package/dist/index.js.map +18 -16
  31. package/dist/metahub-sdk.js +7 -7
  32. package/dist/mh-runtime.js +1 -1
  33. package/dist/sw.js +7 -7
  34. package/dist/webui/server/assets.d.ts +3 -1
  35. package/dist/webui/server/assets.d.ts.map +1 -1
  36. package/dist/webui/server/changes-route.d.ts +7 -0
  37. package/dist/webui/server/changes-route.d.ts.map +1 -0
  38. package/dist/webui/server/routes.d.ts.map +1 -1
  39. package/dist/webui-fmt-clang.js +1 -1
  40. package/dist/webui-fmt-gofmt.js +1 -1
  41. package/dist/webui-fmt-lua.js +2 -2
  42. package/dist/webui-fmt-ruff.js +2 -2
  43. package/dist/webui-fmt-sh.js +8 -8
  44. package/dist/webui-fmt-taplo.js +2 -2
  45. package/dist/webui-fmt.js +346 -346
  46. package/dist/webui.js +178 -174
  47. package/package.json +1 -1
@@ -327,6 +327,7 @@ function intentSubmissionExpired(submittedAt, now = Date.now()) {
327
327
  var CHANGE_COLS = ["hlc", "node_id", "dataset", "row_id", "col", "value", "txn"];
328
328
  var CHANGE_SELECT = CHANGE_COLS.join(", ");
329
329
  var currentTxn = null;
330
+ var currentActor = null;
330
331
  var currentNodeOverride = null;
331
332
  function withNodeId(node, fn) {
332
333
  const prev = currentNodeOverride;
@@ -340,7 +341,7 @@ function withNodeId(node, fn) {
340
341
  function withChangeGroup(label, fn) {
341
342
  if (currentTxn !== null)
342
343
  return fn();
343
- currentTxn = (label ? label + ":" : "") + randomSuffix(8);
344
+ currentTxn = (currentActor ? currentActor + "/" : "") + (label ? label + ":" : "") + randomSuffix(8);
344
345
  try {
345
346
  return fn();
346
347
  } finally {
@@ -565,7 +566,7 @@ function ensurePropIndex(db, databaseId, propId) {
565
566
  function maybeAutoIndex(db, databaseId, prop) {
566
567
  if (hasIndex(db, databaseId, prop.id))
567
568
  return;
568
- if (prop.type !== "relation") {
569
+ if (prop.type !== "relation" && prop.type !== "doc") {
569
570
  const row = db.query("SELECT COUNT(*) AS c FROM records WHERE database_id = ? AND __deleted = 0").get(databaseId);
570
571
  if (row.c < AUTO_INDEX_ROW_THRESHOLD)
571
572
  return;
@@ -582,6 +583,7 @@ var PROP_TYPES = new Set([
582
583
  "multi_select",
583
584
  "date",
584
585
  "relation",
586
+ "doc",
585
587
  "url"
586
588
  ]);
587
589
  var PROPERTY_COLS = ["id", "database_id", "name", "type", "config", "position"];
@@ -614,7 +616,7 @@ var addProperty = grouped(function addProperty2(db, databaseId, opts) {
614
616
  emit(db, "properties", id, "type", opts.type);
615
617
  emit(db, "properties", id, "config", config);
616
618
  emit(db, "properties", id, "position", opts.position ?? nextPosition(db, databaseId));
617
- if (opts.type === "relation" || opts.indexed === true)
619
+ if (opts.type === "relation" || opts.type === "doc" || opts.indexed === true)
618
620
  ensurePropIndex(db, databaseId, id);
619
621
  return getProperty(db, id);
620
622
  });
@@ -886,6 +888,20 @@ function resolveRelation(db, prop, value) {
886
888
  }
887
889
  throw new MhError("ambiguous", `${prop.name}: ambiguous relation "${value}" (${cands.length} matches); use a full id`);
888
890
  }
891
+ function resolveDoc(db, prop, value) {
892
+ const cands = resolveCandidates(db, value, { kind: "doc" });
893
+ const exact = cands.find((c) => c.id === value);
894
+ if (exact)
895
+ return exact.id;
896
+ if (cands.length === 1)
897
+ return cands[0].id;
898
+ if (cands.length === 0) {
899
+ if (idKind(value) === "doc")
900
+ return value;
901
+ throw new MhError("not_found", `${prop.name}: no such document: ${value}`);
902
+ }
903
+ throw new MhError("ambiguous", `${prop.name}: ambiguous document "${value}" (${cands.length} matches); use a full id`);
904
+ }
889
905
  function coerce(db, prop, value) {
890
906
  switch (prop.type) {
891
907
  case "text":
@@ -934,6 +950,10 @@ function coerce(db, prop, value) {
934
950
  const arr = value === null ? [] : Array.isArray(value) ? value : [value];
935
951
  return arr.map((v) => resolveRelation(db, prop, String(v)));
936
952
  }
953
+ case "doc": {
954
+ const arr = value === null ? [] : Array.isArray(value) ? value : [value];
955
+ return arr.map((v) => resolveDoc(db, prop, String(v)));
956
+ }
937
957
  }
938
958
  }
939
959
  function propsByName(props) {
@@ -1147,11 +1167,16 @@ var moveRecord = grouped(function moveRecord2(db, id, targetId, where) {
1147
1167
  return getRecord(db, id);
1148
1168
  });
1149
1169
  var updateRecordPrepared = grouped(function updateRecordPrepared2(db, id, cells) {
1150
- const rec = db.query("SELECT id, database_id FROM records WHERE id = ? AND __deleted = 0").get(id);
1170
+ const rec = db.query("SELECT id, database_id, data FROM records WHERE id = ? AND __deleted = 0").get(id);
1151
1171
  if (!rec)
1152
1172
  throw new MhError("not_found", `no such record: ${id}`);
1153
- for (const { prop, value } of cells)
1154
- emit(db, "records", id, prop.id, value);
1173
+ const cur = JSON.parse(rec.data || "{}");
1174
+ for (const { prop, value } of cells) {
1175
+ const exists = Object.hasOwn(cur, prop.id);
1176
+ const same = value === undefined ? !exists : exists && JSON.stringify(value) === JSON.stringify(cur[prop.id]);
1177
+ if (!same)
1178
+ emit(db, "records", id, prop.id, value);
1179
+ }
1155
1180
  return getRecord(db, id);
1156
1181
  });
1157
1182
  var updateRecord = grouped(function updateRecord2(db, id, data) {
@@ -1212,19 +1237,38 @@ var duplicateDatabase = grouped(function duplicateDatabase2(db, id, opts = {}) {
1212
1237
  emit(db, "databases", dup.id, "meta", meta);
1213
1238
  }
1214
1239
  const propIdMap = new Map;
1240
+ const selfRelNew = new Set;
1215
1241
  for (const p of listProperties(db, id)) {
1216
- const config = p.config?.database === id ? { ...p.config, database: dup.id } : p.config ?? undefined;
1242
+ const self = p.type === "relation" && p.config?.database === id;
1243
+ const config = self ? { ...p.config, database: dup.id } : p.config ?? undefined;
1217
1244
  const np = addProperty(db, dup.id, { name: p.name, type: p.type, config, position: p.position });
1218
1245
  propIdMap.set(p.id, np.id);
1246
+ if (self)
1247
+ selfRelNew.add(np.id);
1219
1248
  }
1220
- for (const r of listRecords(db, id)) {
1249
+ const srcRecords = listRecords(db, id);
1250
+ const recIdMap = new Map;
1251
+ for (const r of srcRecords) {
1221
1252
  const data = {};
1222
1253
  for (const [pid, v] of Object.entries(r.cells)) {
1223
1254
  const nid = propIdMap.get(pid);
1224
- if (nid)
1225
- data[nid] = v;
1255
+ if (!nid)
1256
+ continue;
1257
+ if (selfRelNew.has(nid) && Array.isArray(v) && v.length > 0)
1258
+ continue;
1259
+ data[nid] = v;
1260
+ }
1261
+ recIdMap.set(r.id, createRecord(db, dup.id, data).id);
1262
+ }
1263
+ for (const r of srcRecords) {
1264
+ const patch = {};
1265
+ for (const [pid, v] of Object.entries(r.cells)) {
1266
+ const nid = propIdMap.get(pid);
1267
+ if (nid && selfRelNew.has(nid) && Array.isArray(v) && v.length > 0)
1268
+ patch[nid] = v.map((x) => recIdMap.get(String(x)) ?? x);
1226
1269
  }
1227
- createRecord(db, dup.id, data);
1270
+ if (Object.keys(patch).length)
1271
+ updateRecord(db, recIdMap.get(r.id), patch);
1228
1272
  }
1229
1273
  return getDatabase(db, dup.id);
1230
1274
  });
@@ -1363,29 +1407,33 @@ function liveRowCount(db, databaseId) {
1363
1407
  }
1364
1408
  function assertRelationAllowed(set, principalKind, prop) {
1365
1409
  if (principalKind === "public")
1366
- throw new MhError("invalid_input", `${prop.name}: relation properties are not writable anonymously`);
1410
+ throw new MhError("invalid_input", `${prop.name}: ${prop.type} properties are not writable anonymously`);
1411
+ if (prop.type === "doc")
1412
+ throw new MhError("invalid_input", `${prop.name}: doc properties are not writable via shares (grants cannot scope documents)`);
1367
1413
  const target = prop.config?.database;
1368
1414
  if (!target || !grantFor(set, target))
1369
1415
  throw new MhError("invalid_input", `${prop.name}: relation target database is not in this share's grants`);
1370
1416
  }
1417
+ function isLiveRefValue(prop, value) {
1418
+ return (prop.type === "relation" || prop.type === "doc") && value != null && !(Array.isArray(value) && value.length === 0);
1419
+ }
1371
1420
  function assertGuestPayload(db, set, principal, database, values, opts = {}) {
1372
1421
  const limits = opts.limits ?? GUEST_LIMITS;
1373
1422
  const entries = Object.entries(values);
1374
1423
  if (entries.length > limits.maxCells)
1375
1424
  throw new MhError("invalid_input", `too many cells (max ${limits.maxCells})`);
1376
1425
  const props = listProperties(db, database.id);
1377
- const resolved = resolveData(props, values).map(({ prop, value }) => ({
1378
- prop,
1379
- value: coerce(db, prop, value)
1380
- }));
1426
+ const resolved = resolveData(props, values).map(({ prop, value }) => {
1427
+ if (isLiveRefValue(prop, value))
1428
+ assertRelationAllowed(set, principal.kind, prop);
1429
+ return { prop, value: coerce(db, prop, value) };
1430
+ });
1381
1431
  let total = 0;
1382
1432
  for (const { prop, value } of resolved) {
1383
1433
  const size = utf8Len(JSON.stringify(value ?? null));
1384
1434
  if (size > limits.maxValueBytes)
1385
1435
  throw new MhError("invalid_input", `${prop.name}: value too large (max ${limits.maxValueBytes} bytes)`);
1386
1436
  total += size;
1387
- if (prop.type === "relation" && value != null && !(Array.isArray(value) && value.length === 0))
1388
- assertRelationAllowed(set, principal.kind, prop);
1389
1437
  }
1390
1438
  if (total > limits.maxBodyBytes)
1391
1439
  throw new MhError("invalid_input", `payload too large (max ${limits.maxBodyBytes} bytes)`);
@@ -4026,13 +4074,13 @@ var edge_worker_default = {
4026
4074
  }
4027
4075
  };
4028
4076
  export {
4029
- edge_worker_default as default,
4030
- createInboxFetch,
4031
- MhRoom,
4032
- EDGE_WORKER_VERSION,
4033
- EDGE_WORKER_MARKER,
4034
- EDGE_SCHEMA_SQL,
4035
- DROP_ENVELOPE_LIMIT_BYTES,
4077
+ DROP_DEFAULT_MAX_BYTES,
4036
4078
  DROP_DEFAULT_MAX_ENVELOPES,
4037
- DROP_DEFAULT_MAX_BYTES
4079
+ DROP_ENVELOPE_LIMIT_BYTES,
4080
+ EDGE_SCHEMA_SQL,
4081
+ EDGE_WORKER_MARKER,
4082
+ EDGE_WORKER_VERSION,
4083
+ MhRoom,
4084
+ createInboxFetch,
4085
+ edge_worker_default as default
4038
4086
  };