@ai-matrx/records 0.58.0 → 0.58.1

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.
@@ -2289,12 +2289,15 @@ __export(react_exports, {
2289
2289
  memoryStore: () => memoryStore,
2290
2290
  mintClientKey: () => mintClientKey,
2291
2291
  mintOpId: () => mintOpId,
2292
+ moveConsequence: () => moveConsequence,
2292
2293
  moveInOrder: () => moveInOrder,
2293
2294
  offeredAsContext: () => offeredAsContext,
2294
2295
  ok: () => ok,
2295
2296
  openCaptureQueue: () => openCaptureQueue,
2296
2297
  optionKey: () => optionKey,
2297
2298
  optionLabel: () => optionLabel,
2299
+ parseTableHome: () => parseTableHome,
2300
+ parseTableMoved: () => parseTableMoved,
2298
2301
  portalArchiveConsequence: () => portalArchiveConsequence,
2299
2302
  portalConfirmLine: () => portalConfirmLine,
2300
2303
  portalPath: () => portalPath,
@@ -2441,6 +2444,116 @@ function rememberedOpCount() {
2441
2444
  return ours.size;
2442
2445
  }
2443
2446
 
2447
+ // src/placement.ts
2448
+ function keptOutOfSight(placement) {
2449
+ return placement?.kept_by_the_app === true;
2450
+ }
2451
+ function offeredAsContext(placement) {
2452
+ return placement?.offered_as_context === true;
2453
+ }
2454
+ function withTablePlacement(tables, facts) {
2455
+ if (!facts) return [...tables];
2456
+ const byId = new Map(facts.map((f) => [f.table_id, f]));
2457
+ return tables.map((table) => {
2458
+ const fact = byId.get(table.id);
2459
+ if (!fact || fact.kept_by_the_app === void 0) return table;
2460
+ return {
2461
+ ...table,
2462
+ kept_by_the_app: fact.kept_by_the_app,
2463
+ kept_for: fact.kept_for ?? null,
2464
+ offered_as_context: fact.offered_as_context ?? false,
2465
+ keeper_group: fact.keeper_group ?? null,
2466
+ keeper_says: fact.keeper_says ?? null,
2467
+ used_in_kind: fact.used_in_kind ?? null,
2468
+ used_in_id: fact.used_in_id ?? null,
2469
+ used_in_table_id: fact.used_in_table_id ?? null
2470
+ };
2471
+ });
2472
+ }
2473
+ function keptGroups(tables) {
2474
+ const out = [];
2475
+ const at = /* @__PURE__ */ new Map();
2476
+ for (const table of tables) {
2477
+ if (!keptOutOfSight(table)) continue;
2478
+ const group = table.keeper_group?.trim() || "Kept by the app";
2479
+ let i = at.get(group);
2480
+ if (i === void 0) {
2481
+ i = out.length;
2482
+ at.set(group, i);
2483
+ out.push({ group, tables: [] });
2484
+ }
2485
+ out[i].tables.push(table);
2486
+ }
2487
+ return out;
2488
+ }
2489
+ function keptTableHref(fact, hrefs) {
2490
+ if (fact.used_in_kind === "scope" && fact.used_in_id) return hrefs.scope(fact.used_in_id);
2491
+ if (fact.used_in_kind === "table" && (fact.used_in_table_id || fact.used_in_id)) {
2492
+ return hrefs.table(fact.used_in_table_id || fact.used_in_id);
2493
+ }
2494
+ return hrefs.table(fact.table_id);
2495
+ }
2496
+ function countOf(value) {
2497
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
2498
+ }
2499
+ function wordsOf(value) {
2500
+ return typeof value === "string" && value.length > 0 ? value : null;
2501
+ }
2502
+ function parseTableHome(raw) {
2503
+ if (!raw || typeof raw !== "object") return null;
2504
+ const row = raw;
2505
+ const table = row.table;
2506
+ const org = row.organization;
2507
+ if (!table || !org || !wordsOf(table.id) || !wordsOf(org.id) || !wordsOf(org.name)) return null;
2508
+ const carries = row.carries ?? {};
2509
+ const destinations = Array.isArray(row.destinations) ? row.destinations : [];
2510
+ return {
2511
+ table: { id: String(table.id), name: wordsOf(table.name) ?? "This table", version: countOf(table.version) },
2512
+ organization: { id: String(org.id), name: String(org.name) },
2513
+ mayMove: row.may_move === true,
2514
+ whyNot: wordsOf(row.why_not),
2515
+ heldBy: Array.isArray(row.held_by) ? row.held_by.filter((s) => typeof s === "string") : [],
2516
+ destinations: destinations.filter((d) => !!d && typeof d === "object" && !!wordsOf(d.id)).map((d) => ({
2517
+ id: String(d.id),
2518
+ name: wordsOf(d.name) ?? "An organization",
2519
+ role: wordsOf(d.role) ?? "member",
2520
+ ok: d.ok === true,
2521
+ why: wordsOf(d.why)
2522
+ })),
2523
+ carries: {
2524
+ fields: countOf(carries.fields),
2525
+ records: countOf(carries.records),
2526
+ inTrash: countOf(carries.in_trash),
2527
+ choiceLists: countOf(carries.choice_lists),
2528
+ withIt: countOf(carries.with_it),
2529
+ tablesInside: countOf(carries.tables_inside),
2530
+ linksAcross: countOf(carries.links_across)
2531
+ }
2532
+ };
2533
+ }
2534
+ function parseTableMoved(raw) {
2535
+ const row = raw ?? {};
2536
+ const to = row.to ?? {};
2537
+ const from = row.from ?? {};
2538
+ const table = row.table ?? {};
2539
+ if (row.moved !== true || !wordsOf(to.id)) return null;
2540
+ return {
2541
+ to: { id: String(to.id), name: wordsOf(to.name) ?? "the new organization" },
2542
+ from: { id: wordsOf(from.id) ?? "", name: wordsOf(from.name) ?? "its old organization" },
2543
+ tableName: wordsOf(table.name) ?? "The table"
2544
+ };
2545
+ }
2546
+ function plural(n, one, many) {
2547
+ return `${n} ${n === 1 ? one : many}`;
2548
+ }
2549
+ function moveConsequence(home, to) {
2550
+ const c = home.carries;
2551
+ const parts = [plural(c.fields, "column", "columns"), plural(c.records, "row", "rows")];
2552
+ if (c.inTrash > 0) parts.push(`${c.inTrash} in the trash`);
2553
+ const extra = c.choiceLists + c.withIt;
2554
+ return `${home.table.name} moves to ${to.name} with its ${parts.join(", ")}` + (c.tablesInside > 0 ? `, the ${plural(c.tablesInside, "table", "tables")} that live${c.tablesInside === 1 ? "s" : ""} inside it or its rows (with their own rows)` : "") + (extra > 0 ? `, and ${plural(extra, "thing", "things")} built only on it (choice lists, rules, dashboards, templates)` : "") + `, its comments, forms, saved views and history. ` + (c.linksAcross > 0 ? `${plural(c.linksAcross, "link", "links")} to rows that stay in ${home.organization.name} keep${c.linksAcross === 1 ? "s" : ""} pointing there, across the two organizations. ` : "") + `People who see it only because they are in ${home.organization.name} stop seeing it; people it was shared with by name keep it. You can move it back from the same place.`;
2555
+ }
2556
+
2444
2557
  // src/level.ts
2445
2558
  var PERMISSION_LEVELS = [
2446
2559
  "viewer",
@@ -4449,6 +4562,8 @@ var ENTITY_TOKENS = [
4449
4562
 
4450
4563
  // src/core/doors.ts
4451
4564
  var OWED_BY = {
4565
+ table_home: "SC-1' \u2014 matrx-frontend migrations/campaign/sc1p_a_table_says_where_it_lives_and_its_owner_can_move_it.sql (and its grant file), applied by the chair",
4566
+ table_move: "SC-1' / SC-1-TAILS \u2014 sc1p_a_table_says_where_it_lives_and_its_owner_can_move_it.sql, then sc1t_a_table_moves_with_the_tables_inside_it.sql, applied by the chair",
4452
4567
  metadata_search: "W5-SRV / W5-AGENT \u2014 the agent surface's eight verbs",
4453
4568
  record_aggregate: "W5-SRV / W5-AGENT \u2014 `record_aggregate` is one of the eight verbs",
4454
4569
  field_propose: "W5-AGENT \u2014 AGT-4/AGT-8, the proposal and its approvers",
@@ -4774,6 +4889,12 @@ var DOORS = {
4774
4889
  // ask, under the same wall and visibility gate as `portals` itself.
4775
4890
  portalTables: "portal_tables",
4776
4891
  tableFacts: "table_facts",
4892
+ // WHERE A TABLE LIVES, AND MOVING IT (SC-1' / SC-1-TAILS). `table_home` answers only when the
4893
+ // caller may open the Table (custom.where_id_opens) and names its organization FROM THE TABLE;
4894
+ // `table_move` moves it into another organization the caller belongs to, with every Table
4895
+ // inside it, or refuses with one sentence.
4896
+ tableHome: "table_home",
4897
+ tableMove: "table_move",
4777
4898
  portalCard: "portal_card",
4778
4899
  portalDeclare: "portal_declare",
4779
4900
  portalInvite: "portal_invite",
@@ -5417,6 +5538,40 @@ function createRecordsClient(config) {
5417
5538
  if (!doorExists(DOORS.tableFacts)) return ok(null);
5418
5539
  return callDoor(DOORS.tableFacts, { p_organization_id: org }, "tableFacts");
5419
5540
  },
5541
+ async tableHome({ table_id }) {
5542
+ const answered = await callDoor(DOORS.tableHome, { p_table_id: table_id }, "tableHome");
5543
+ if (!answered.ok) return answered;
5544
+ if (answered.data === null || answered.data === void 0) return ok(null);
5545
+ const home = parseTableHome(answered.data);
5546
+ if (!home) {
5547
+ const refusal = {
5548
+ code: "not_supported",
5549
+ message: "The record store answered where this table lives in a shape this client cannot read, so nothing is claimed about it.",
5550
+ hint: "Regenerate @ai-matrx/records against the store (pnpm records:generate) \u2014 the door and this client disagree."
5551
+ };
5552
+ scream({ code: refusal.code, message: refusal.message, ...refusal.hint ? { hint: refusal.hint } : {}, where: "tableHome" });
5553
+ return err(refusal);
5554
+ }
5555
+ return ok(home);
5556
+ },
5557
+ async tableMove({ table_id, to_organization_id, expected_version }) {
5558
+ const answered = await callDoor(
5559
+ DOORS.tableMove,
5560
+ { p_table_id: table_id, p_to_organization_id: to_organization_id, p_expected_version: expected_version ?? null },
5561
+ "tableMove"
5562
+ );
5563
+ if (!answered.ok) return answered;
5564
+ const moved = parseTableMoved(answered.data);
5565
+ if (!moved) {
5566
+ const refusal = {
5567
+ code: "not_supported",
5568
+ message: "The record store answered the move in a shape this client cannot read. Look where the table lives before trying again."
5569
+ };
5570
+ scream({ code: refusal.code, message: refusal.message, where: "tableMove" });
5571
+ return err(refusal);
5572
+ }
5573
+ return ok(moved);
5574
+ },
5420
5575
  async tableArchived({ table_id }) {
5421
5576
  const kernel = await callDoor(DOORS.tableKernelId, {}, "tableArchived.kernel");
5422
5577
  if (!kernel.ok) return err(kernel.error);
@@ -8440,56 +8595,6 @@ function emptyPortalArchiveLine() {
8440
8595
  return "No portal of this organization has been archived. A portal you put away waits here until somebody restores it, and nothing is ever destroyed.";
8441
8596
  }
8442
8597
 
8443
- // src/placement.ts
8444
- function keptOutOfSight(placement) {
8445
- return placement?.kept_by_the_app === true;
8446
- }
8447
- function offeredAsContext(placement) {
8448
- return placement?.offered_as_context === true;
8449
- }
8450
- function withTablePlacement(tables, facts) {
8451
- if (!facts) return [...tables];
8452
- const byId = new Map(facts.map((f) => [f.table_id, f]));
8453
- return tables.map((table) => {
8454
- const fact = byId.get(table.id);
8455
- if (!fact || fact.kept_by_the_app === void 0) return table;
8456
- return {
8457
- ...table,
8458
- kept_by_the_app: fact.kept_by_the_app,
8459
- kept_for: fact.kept_for ?? null,
8460
- offered_as_context: fact.offered_as_context ?? false,
8461
- keeper_group: fact.keeper_group ?? null,
8462
- keeper_says: fact.keeper_says ?? null,
8463
- used_in_kind: fact.used_in_kind ?? null,
8464
- used_in_id: fact.used_in_id ?? null,
8465
- used_in_table_id: fact.used_in_table_id ?? null
8466
- };
8467
- });
8468
- }
8469
- function keptGroups(tables) {
8470
- const out = [];
8471
- const at = /* @__PURE__ */ new Map();
8472
- for (const table of tables) {
8473
- if (!keptOutOfSight(table)) continue;
8474
- const group = table.keeper_group?.trim() || "Kept by the app";
8475
- let i = at.get(group);
8476
- if (i === void 0) {
8477
- i = out.length;
8478
- at.set(group, i);
8479
- out.push({ group, tables: [] });
8480
- }
8481
- out[i].tables.push(table);
8482
- }
8483
- return out;
8484
- }
8485
- function keptTableHref(fact, hrefs) {
8486
- if (fact.used_in_kind === "scope" && fact.used_in_id) return hrefs.scope(fact.used_in_id);
8487
- if (fact.used_in_kind === "table" && (fact.used_in_table_id || fact.used_in_id)) {
8488
- return hrefs.table(fact.used_in_table_id || fact.used_in_id);
8489
- }
8490
- return hrefs.table(fact.table_id);
8491
- }
8492
-
8493
8598
  // src/capture-queue.ts
8494
8599
  var DB_NAME = "matrx-capture";
8495
8600
  var DB_VERSION = 1;
@@ -8558,7 +8663,7 @@ function memoryStore() {
8558
8663
  setMeta: async (key, value) => void meta.set(key, value)
8559
8664
  };
8560
8665
  }
8561
- function countOf(items) {
8666
+ function countOf2(items) {
8562
8667
  const c = { waiting: 0, sending: 0, refused: 0, landed: 0 };
8563
8668
  for (const i of items) c[i.state] += 1;
8564
8669
  return c;
@@ -8575,7 +8680,7 @@ function openCaptureQueue(opts) {
8575
8680
  const announce = async () => {
8576
8681
  if (!opts.onChange) return;
8577
8682
  const items = await store.all();
8578
- opts.onChange(countOf(items), items);
8683
+ opts.onChange(countOf2(items), items);
8579
8684
  };
8580
8685
  const oldestFirst = (items) => items.slice().sort((a, b) => a.captured_at.localeCompare(b.captured_at));
8581
8686
  const queue = {
@@ -8604,7 +8709,7 @@ function openCaptureQueue(opts) {
8604
8709
  return key;
8605
8710
  },
8606
8711
  list: () => store.all().then(oldestFirst),
8607
- counts: () => store.all().then(countOf),
8712
+ counts: () => store.all().then(countOf2),
8608
8713
  async sync() {
8609
8714
  if (syncing) return syncing;
8610
8715
  syncing = (async () => {