@ai-matrx/records 0.99.31 → 0.99.33

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 (106) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/{client-Dthx26hr.d.ts → client-CSfkP7Xf.d.ts} +11 -80
  3. package/dist/{client-B7KEs9hi.d.cts → client-jKf1Z-kp.d.cts} +11 -80
  4. package/dist/core/index.cjs +149 -105
  5. package/dist/core/index.cjs.map +1 -1
  6. package/dist/core/index.d.cts +18 -359
  7. package/dist/core/index.d.ts +18 -359
  8. package/dist/core/index.js +149 -105
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/data/index.cjs +280 -236
  11. package/dist/data/index.cjs.map +1 -1
  12. package/dist/data/index.d.cts +12 -11
  13. package/dist/data/index.d.ts +12 -11
  14. package/dist/data/index.js +280 -236
  15. package/dist/data/index.js.map +1 -1
  16. package/dist/{declareTable-CvBu5dTJ.d.ts → declareTable-0emxEewn.d.ts} +3 -3
  17. package/dist/{declareTable-enQs2N0E.d.cts → declareTable-800bwUn3.d.cts} +3 -3
  18. package/dist/door-call/index.cjs +1287 -0
  19. package/dist/door-call/index.cjs.map +1 -0
  20. package/dist/door-call/index.d.cts +20 -0
  21. package/dist/door-call/index.d.ts +20 -0
  22. package/dist/door-call/index.js +1264 -0
  23. package/dist/door-call/index.js.map +1 -0
  24. package/dist/{doors-KbjyhBW-.d.cts → doors-BgjRUdxw.d.cts} +14 -2
  25. package/dist/{doors-C2-sFvAk.d.ts → doors-D7_jEKtg.d.ts} +14 -2
  26. package/dist/entity/index.d.cts +7 -7
  27. package/dist/entity/index.d.ts +7 -7
  28. package/dist/{field-ppX29ddi.d.cts → field-B8loYt-A.d.cts} +1 -1
  29. package/dist/{field-BHlhq24W.d.ts → field-DaE90BZ7.d.ts} +1 -1
  30. package/dist/forms/index.d.cts +3 -3
  31. package/dist/forms/index.d.ts +3 -3
  32. package/dist/index.d.cts +14 -13
  33. package/dist/index.d.ts +14 -13
  34. package/dist/memory/index.d.cts +7 -7
  35. package/dist/memory/index.d.ts +7 -7
  36. package/dist/{objectOrganization-Cu73SBxH.d.cts → objectOrganization-B5YFGVUB.d.cts} +2 -2
  37. package/dist/{objectOrganization-DtR3lWG2.d.ts → objectOrganization-DozoVIxS.d.ts} +2 -2
  38. package/dist/pickListDoors-C-_eAEGL.d.cts +135 -0
  39. package/dist/pickListDoors-DgWVnNRI.d.ts +135 -0
  40. package/dist/{port-Bk7TG0aE.d.ts → port-D6wepPvr.d.ts} +3 -3
  41. package/dist/{port-rqQEGhew.d.cts → port-O-UdH1HS.d.cts} +3 -3
  42. package/dist/{portal-DmxuUr2f.d.ts → portal-CAssJHRZ.d.ts} +1 -1
  43. package/dist/{portal-ffH13fsE.d.cts → portal-L_mN8l9P.d.cts} +1 -1
  44. package/dist/{ports-BCNNghmf.d.ts → ports-CmmvR8OI.d.ts} +2 -2
  45. package/dist/{ports-iATV-A2v.d.cts → ports-alv3bgKY.d.cts} +2 -2
  46. package/dist/questions/index.d.cts +10 -9
  47. package/dist/questions/index.d.ts +10 -9
  48. package/dist/react/index.cjs +363 -319
  49. package/dist/react/index.cjs.map +1 -1
  50. package/dist/react/index.d.cts +37 -22
  51. package/dist/react/index.d.ts +37 -22
  52. package/dist/react/index.js +363 -319
  53. package/dist/react/index.js.map +1 -1
  54. package/dist/realtime/index.d.cts +3 -3
  55. package/dist/realtime/index.d.ts +3 -3
  56. package/dist/{record-C8q21jjB.d.cts → record-CTgSgWe3.d.cts} +1 -1
  57. package/dist/{record-C8q21jjB.d.ts → record-CTgSgWe3.d.ts} +1 -1
  58. package/dist/{relation-Bwp8iuei.d.cts → relation-BjjjXyqn.d.cts} +2 -2
  59. package/dist/{relation-Bsjwkn6N.d.ts → relation-T4biacnz.d.ts} +2 -2
  60. package/dist/results/index.cjs +245 -0
  61. package/dist/results/index.cjs.map +1 -0
  62. package/dist/results/index.d.cts +24 -0
  63. package/dist/results/index.d.ts +24 -0
  64. package/dist/results/index.js +222 -0
  65. package/dist/results/index.js.map +1 -0
  66. package/dist/{rule-BQwUtKk5.d.cts → rule-49jO7lN-.d.cts} +1 -1
  67. package/dist/{rule-cGqclk_0.d.ts → rule-DzNj0QvN.d.ts} +1 -1
  68. package/dist/scopes/index.cjs +7 -0
  69. package/dist/scopes/index.cjs.map +1 -1
  70. package/dist/scopes/index.d.cts +3 -3
  71. package/dist/scopes/index.d.ts +3 -3
  72. package/dist/scopes/index.js +7 -0
  73. package/dist/scopes/index.js.map +1 -1
  74. package/dist/search/index.d.cts +1 -1
  75. package/dist/search/index.d.ts +1 -1
  76. package/dist/setup/index.cjs +289 -245
  77. package/dist/setup/index.cjs.map +1 -1
  78. package/dist/setup/index.d.cts +13 -12
  79. package/dist/setup/index.d.ts +13 -12
  80. package/dist/setup/index.js +289 -245
  81. package/dist/setup/index.js.map +1 -1
  82. package/dist/{subscription-eNzL9scc.d.ts → subscription-Bp1RSb6p.d.ts} +2 -2
  83. package/dist/{subscription-Ba8RKtPA.d.cts → subscription-CuaGi3lC.d.cts} +2 -2
  84. package/dist/table-logic-BPquabNN.d.ts +328 -0
  85. package/dist/table-logic-CTh6lwaq.d.cts +328 -0
  86. package/dist/templates/index.d.cts +10 -10
  87. package/dist/templates/index.d.ts +10 -10
  88. package/dist/typed-table/index.cjs +280 -236
  89. package/dist/typed-table/index.cjs.map +1 -1
  90. package/dist/typed-table/index.d.cts +11 -10
  91. package/dist/typed-table/index.d.ts +11 -10
  92. package/dist/typed-table/index.js +280 -236
  93. package/dist/typed-table/index.js.map +1 -1
  94. package/dist/{types-C4XCuv8O.d.cts → types-B54BeQzf.d.cts} +2 -2
  95. package/dist/{types-BlzdL4Vd.d.cts → types-BWfL38XJ.d.cts} +2 -2
  96. package/dist/{types-BBR2NGqj.d.ts → types-BZcYXFK3.d.ts} +2 -2
  97. package/dist/{types-EKwXJfxY.d.cts → types-Bztv0-pH.d.cts} +2 -2
  98. package/dist/{types-CmkHpvll.d.ts → types-CjGuR5ks.d.ts} +2 -2
  99. package/dist/{types-CrkzINrb.d.ts → types-bN7dG2ji.d.ts} +2 -2
  100. package/dist/use-cases/index.d.cts +5 -5
  101. package/dist/use-cases/index.d.ts +5 -5
  102. package/dist/{validate-BKeBAVSi.d.cts → validate-8VBa2xIU.d.cts} +1 -1
  103. package/dist/{validate-BB6DWgCP.d.ts → validate-xBnply7J.d.ts} +1 -1
  104. package/dist/views/index.d.cts +10 -9
  105. package/dist/views/index.d.ts +10 -9
  106. package/package.json +24 -4
@@ -402,184 +402,6 @@ function isWritingDoor(door) {
402
402
  return /(_declare|_set(_|$)|_update|_write|_delete|_restore|_archive|_upsert|_reparent|_change|_duplicate|_move|_decide|_run$|_submit|_publish|_import_|_start$|_complete|_sign$|_add$|_retire|_propose|_promote|_land$|_pin$|_mute|_snooze|_unsnooze|_clear|_unclear|_revoke|_issue|_bind|_unbind|_apply|_backfill|_sync$|_decorate|_designate|_ensure|^migrate_|^set_table_|^home_add|^capture_|^claim_|^record_stage|^anon_|^io_import_|^io_comment_resolve|^enrich_(declare|land|pin)|^field_(declare|duplicate|propose|restore|retire|update)|^table_(declare|ensure|from_example|move|sync)|^value_restore|^relation_own)/.test(door);
403
403
  }
404
404
 
405
- // src/core/recordChanges.ts
406
- var import_global_slot = require("@ai-matrx/kit/global-slot");
407
- var listeners = (0, import_global_slot.globalSlot)("ai-matrx.records.record-change-listeners", () => /* @__PURE__ */ new Set());
408
- function announceRecordChanged(recordId) {
409
- for (const listener of [...listeners]) {
410
- try {
411
- listener(recordId);
412
- } catch (thrown) {
413
- console.error("[@ai-matrx/records] a record-change listener threw", thrown);
414
- }
415
- }
416
- }
417
- var removed = (0, import_global_slot.globalSlot)("ai-matrx.records.removed-records", () => /* @__PURE__ */ new Set());
418
- function markRecordRemoved(recordId) {
419
- removed.add(recordId);
420
- }
421
- function markRecordRestored(recordId) {
422
- removed.delete(recordId);
423
- }
424
- function isRecordRemoved(recordId) {
425
- return removed.has(recordId);
426
- }
427
- function onRecordChanged(listener) {
428
- listeners.add(listener);
429
- return () => listeners.delete(listener);
430
- }
431
- var tableListeners = (0, import_global_slot.globalSlot)("ai-matrx.records.table-structure-listeners", () => /* @__PURE__ */ new Set());
432
- function announceTableStructureChanged(tableId) {
433
- for (const listener of [...tableListeners]) {
434
- try {
435
- listener(tableId);
436
- } catch (thrown) {
437
- console.error("[@ai-matrx/records] a table-structure listener threw", thrown);
438
- }
439
- }
440
- }
441
-
442
- // src/archive.ts
443
- var ARCHIVE_LANES = ["mine", "org"];
444
- var DEFAULT_ARCHIVE_LANE = "org";
445
- var ARCHIVE_LANE_LABEL = {
446
- mine: "Archived by me",
447
- org: "Archived by anyone"
448
- };
449
- var ARCHIVE_LANE_MEANS = {
450
- mine: "The records you archived yourself.",
451
- org: "Everything anybody in this organization archived, including you."
452
- };
453
- function isArchiveLane(word) {
454
- return typeof word === "string" && ARCHIVE_LANES.includes(word);
455
- }
456
- function archivedWhen(at, locale = "en-GB") {
457
- const when = new Date(at);
458
- if (Number.isNaN(when.getTime())) return "a moment nobody recorded";
459
- return new Intl.DateTimeFormat(locale, {
460
- day: "numeric",
461
- month: "long",
462
- year: "numeric",
463
- hour: "2-digit",
464
- minute: "2-digit"
465
- }).format(when);
466
- }
467
- function archivedByLine(row, locale = "en-GB") {
468
- const when = archivedWhen(row.archivedAt, locale);
469
- const who = (row.archivedByName ?? "").trim();
470
- if (who === "") {
471
- return `Archived on ${when}. The store does not know who archived it \u2014 whoever did is no longer in this organization, or it happened before this table kept that.`;
472
- }
473
- return `Archived by ${who} on ${when}.`;
474
- }
475
- function emptyArchiveLine(lane) {
476
- return lane === "mine" ? "You have not archived anything in this table. Switch to \u201CArchived by anyone\u201D to see what the rest of the organization has." : "Nothing in this table has been archived. Records you remove from the grid arrive here, and nothing is ever destroyed.";
477
- }
478
- function sameDocument(a, b) {
479
- return JSON.stringify(a) === JSON.stringify(b);
480
- }
481
-
482
- // src/aggregate.ts
483
- function isRuleFilter(filter) {
484
- if (!filter || typeof filter !== "object" || Array.isArray(filter)) return false;
485
- const node = filter;
486
- return typeof node.op === "string" && Array.isArray(node.args);
487
- }
488
- function measureKey(measure) {
489
- return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
490
- }
491
- function stableFilterKey(filter) {
492
- if (!filter) return "{}";
493
- if (isRuleFilter(filter)) return JSON.stringify(filter);
494
- const keys = Object.keys(filter).sort();
495
- if (keys.length === 0) return "{}";
496
- const parts = keys.map((key) => {
497
- const value = filter[key];
498
- if (value !== null && typeof value === "object") {
499
- const window2 = value;
500
- return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
501
- }
502
- return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
503
- });
504
- return `{${parts.join(",")}}`;
505
- }
506
- function liftWithheld(rows) {
507
- let shared;
508
- for (const row of rows) {
509
- const m = (row.measures ?? {})["_withheld"];
510
- if (m !== void 0 && m !== null && typeof m === "object") {
511
- shared = m;
512
- break;
513
- }
514
- }
515
- return rows.map((row) => {
516
- const measures = row.measures ?? {};
517
- const own = measures["_withheld"];
518
- const marker = own !== void 0 && own !== null && typeof own === "object" ? own : shared;
519
- const rowCount = row.row_count ?? 0;
520
- if (marker === void 0 || marker === null || typeof marker !== "object") {
521
- return rowCount === row.row_count ? row : { ...row, row_count: rowCount };
522
- }
523
- const { _withheld: _drop, ...rest } = measures;
524
- const withheld = {};
525
- for (const [name, raw] of Object.entries(marker)) {
526
- const m = raw ?? {};
527
- withheld[name] = {
528
- reason: m.withheld?.reason ?? null,
529
- needs: m.withheld?.needs ?? null,
530
- or: m.withheld?.or ?? null,
531
- ...m.says ? { says: m.says } : {},
532
- ...m.label ? { label: m.label } : {},
533
- ...m.field_key ? { field_key: m.field_key } : {}
534
- };
535
- }
536
- return { ...row, measures: rest, withheld, row_count: rowCount };
537
- });
538
- }
539
-
540
- // src/record.ts
541
- function isLockedStub(row) {
542
- if (!row) return false;
543
- if (row.level !== null && row.level !== void 0) return false;
544
- const doc = row.document;
545
- if (!doc || typeof doc !== "object") return false;
546
- const keys = Object.keys(doc);
547
- if (keys.length === 0 || keys.length > 3) return false;
548
- const d = doc;
549
- return d["exists"] === true && keys.every((k) => k === "id" || k === "exists" || k === "submitted_at");
550
- }
551
- function splitLockedRows(rows) {
552
- const open = [];
553
- let locked = 0;
554
- for (const row of rows) {
555
- if (isLockedStub(row)) locked += 1;
556
- else open.push(row);
557
- }
558
- return { rows: open, locked };
559
- }
560
-
561
- // src/subscription.ts
562
- function hasQuietHours(quiet2) {
563
- return Boolean(quiet2?.start && quiet2?.end);
564
- }
565
- function callersTimeZone() {
566
- try {
567
- return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
568
- } catch {
569
- return null;
570
- }
571
- }
572
- function withCallersClock(spec, zone = callersTimeZone()) {
573
- if (!zone || spec.quiet_hours?.tz) return spec;
574
- return { ...spec, quiet_hours: { ...spec.quiet_hours ?? {}, tz: zone } };
575
- }
576
-
577
- // src/rowLabel.ts
578
- var ROW_LABEL_FORMULA_MAX_LENGTH = 2e3;
579
- function clearsRowLabel(formula) {
580
- return formula == null || formula.trim() === "";
581
- }
582
-
583
405
  // src/results.ts
584
406
  function isRecordsErr(result) {
585
407
  return result.ok === false;
@@ -725,48 +547,319 @@ function parseDetail(details) {
725
547
  return details;
726
548
  }
727
549
  }
728
- function isNotASqlstate(code) {
729
- if (!code) return true;
730
- return /^[0-9]{3}$/.test(code);
550
+ function isNotASqlstate(code) {
551
+ if (!code) return true;
552
+ return /^[0-9]{3}$/.test(code);
553
+ }
554
+ function mapPgError(error, where) {
555
+ const raw = error.code ?? void 0;
556
+ const sqlstate = raw && raw !== "" ? raw : void 0;
557
+ const mapped = {
558
+ code: "internal",
559
+ message: error.message,
560
+ ...error.hint ? { hint: error.hint } : {},
561
+ ...sqlstate ? { sqlstate } : {}
562
+ };
563
+ if (isNotASqlstate(sqlstate)) {
564
+ mapped.code = "unreachable";
565
+ } else {
566
+ mapped.code = BY_SQLSTATE[sqlstate] ?? "internal";
567
+ }
568
+ const detail = parseDetail(error.details);
569
+ if (detail !== void 0) mapped.detail = detail;
570
+ if (mapped.code === "internal") {
571
+ mapped.unmapped = true;
572
+ mapped.diagnostic = `@ai-matrx/records did not recognise SQLSTATE ${sqlstate ?? "(none)"} from ${where}; the store's own message is the thing to act on.`;
573
+ } else if (mapped.code === "unreachable") {
574
+ mapped.unmapped = true;
575
+ mapped.diagnostic = `@ai-matrx/records never reached the store from ${where}: ${raw ? `the transport answered ${raw}` : "no response"} \u2014 ${error.message}`;
576
+ mapped.message = "We could not reach your data.";
577
+ delete mapped.hint;
578
+ }
579
+ return mapped;
580
+ }
581
+ function staleWriteDetail(error) {
582
+ if (error.code !== "stale_write") return null;
583
+ const detail = error.detail;
584
+ if (!detail || typeof detail.expected_version !== "number" || typeof detail.current_version !== "number") {
585
+ return null;
586
+ }
587
+ return {
588
+ expected_version: detail.expected_version,
589
+ current_version: detail.current_version,
590
+ contested_fields: detail.contested_fields ?? {}
591
+ };
592
+ }
593
+
594
+ // src/core/doorCall.ts
595
+ var DEFAULT_DOOR_SCHEMA = "custom";
596
+ function canonicalArgs(value) {
597
+ return JSON.stringify(
598
+ value,
599
+ (_key, held) => held && typeof held === "object" && !Array.isArray(held) ? Object.fromEntries(Object.keys(held).sort().map((k) => [k, held[k]])) : held
600
+ );
601
+ }
602
+ var A_PERSONS_REFUSAL = /* @__PURE__ */ new Set([
603
+ "refused_by_rule",
604
+ "stale_write",
605
+ "invalid_argument",
606
+ "already_exists",
607
+ "missing_reference"
608
+ ]);
609
+ function isAPersonsRefusal(code) {
610
+ return A_PERSONS_REFUSAL.has(code);
611
+ }
612
+ function recordsErrorSink(onError) {
613
+ return onError ?? ((e) => {
614
+ const line = `[@ai-matrx/records] ${e.where}: ${e.code} \u2014 ${e.message}${e.hint ? ` (${e.hint})` : ""}`;
615
+ if (isAPersonsRefusal(e.code)) console.info(line);
616
+ else console.error(line);
617
+ });
618
+ }
619
+ function doorAbsentRefusal(name, owedBy) {
620
+ return {
621
+ code: "door_absent",
622
+ message: `The record store has no door \`custom.${name}\` yet, so this call was not made. Nothing was read and nothing was written.`,
623
+ hint: owedBy ? `That door is ${owedBy}. Until it lands, this call refuses rather than guessing: an empty answer here would read like "there is nothing", which is a different sentence.` : `No lane in the build book claims this door. Either the name is wrong, or the door was removed from the store and this package has not been regenerated \u2014 run \`pnpm records:generate\`.`
624
+ };
625
+ }
626
+ function primedDoorAnswer(door, args) {
627
+ const look = LOOK_WRITES[door];
628
+ if (look) {
629
+ dropPrimedDoorAnswers(look);
630
+ return null;
631
+ }
632
+ if (isWritingDoor(door)) {
633
+ dropPrimedDoorAnswers();
634
+ return null;
635
+ }
636
+ return takePrimedDoorAnswer(`${door}|${canonicalArgs(args)}`);
637
+ }
638
+ function createDoorCall(options) {
639
+ const { dataSource, doorReady: doorReady2, held, wrote } = options;
640
+ const schema = options.schema ?? DEFAULT_DOOR_SCHEMA;
641
+ const scream = recordsErrorSink(options.onError);
642
+ const absent = options.absent ?? ((door) => doorAbsentRefusal(door));
643
+ const readsInFlight = /* @__PURE__ */ new Map();
644
+ const say = (refusal3, where) => scream({ code: refusal3.code, message: refusal3.message, ...refusal3.hint ? { hint: refusal3.hint } : {}, where });
645
+ return async function callDoor(door, args, where, opts) {
646
+ const primed = primedDoorAnswer(door, args);
647
+ if (primed) return ok(primed.data);
648
+ if (held) {
649
+ const bundled = await held(door, args);
650
+ if (bundled) return ok(bundled.data);
651
+ }
652
+ if (doorReady2 && !doorReady2(door)) {
653
+ const refusal3 = absent(door);
654
+ say(refusal3, where);
655
+ return err(refusal3);
656
+ }
657
+ const flightKey = isWritingDoor(door) ? null : `${door}|${canonicalArgs(args)}`;
658
+ const flying = flightKey ? readsInFlight.get(flightKey) : void 0;
659
+ const response = await (flying ?? (() => {
660
+ const call = Promise.resolve(dataSource.rpc(`${door}`, args, { schema }));
661
+ if (flightKey) {
662
+ readsInFlight.set(flightKey, call);
663
+ const forget = () => {
664
+ if (readsInFlight.get(flightKey) === call) readsInFlight.delete(flightKey);
665
+ };
666
+ call.then(forget, forget);
667
+ }
668
+ return call;
669
+ })());
670
+ if (response.error) {
671
+ const code = response.error.code;
672
+ const refusal3 = !doorReady2 && (code === "PGRST202" || code === "42883") ? absent(door) : mapPgError(response.error, where);
673
+ if (opts?.quiet) console.info(`[@ai-matrx/records] ${where}: ${refusal3.code} - ${refusal3.message}`);
674
+ else say(refusal3, where);
675
+ return err(refusal3);
676
+ }
677
+ if (isWritingDoor(door) || LOOK_WRITES[door]) {
678
+ const tableId = args["p_table_id"];
679
+ wrote?.(door, args);
680
+ forgetPrimedTablePageBundlesOf(typeof tableId === "string" ? tableId : null);
681
+ dropPrimedDoorAnswers(LOOK_WRITES[door]);
682
+ }
683
+ return ok(response.data);
684
+ };
685
+ }
686
+
687
+ // src/core/recordChanges.ts
688
+ var import_global_slot = require("@ai-matrx/kit/global-slot");
689
+ var listeners = (0, import_global_slot.globalSlot)("ai-matrx.records.record-change-listeners", () => /* @__PURE__ */ new Set());
690
+ function announceRecordChanged(recordId) {
691
+ for (const listener of [...listeners]) {
692
+ try {
693
+ listener(recordId);
694
+ } catch (thrown) {
695
+ console.error("[@ai-matrx/records] a record-change listener threw", thrown);
696
+ }
697
+ }
698
+ }
699
+ var removed = (0, import_global_slot.globalSlot)("ai-matrx.records.removed-records", () => /* @__PURE__ */ new Set());
700
+ function markRecordRemoved(recordId) {
701
+ removed.add(recordId);
702
+ }
703
+ function markRecordRestored(recordId) {
704
+ removed.delete(recordId);
705
+ }
706
+ function isRecordRemoved(recordId) {
707
+ return removed.has(recordId);
708
+ }
709
+ function onRecordChanged(listener) {
710
+ listeners.add(listener);
711
+ return () => listeners.delete(listener);
712
+ }
713
+ var tableListeners = (0, import_global_slot.globalSlot)("ai-matrx.records.table-structure-listeners", () => /* @__PURE__ */ new Set());
714
+ function announceTableStructureChanged(tableId) {
715
+ for (const listener of [...tableListeners]) {
716
+ try {
717
+ listener(tableId);
718
+ } catch (thrown) {
719
+ console.error("[@ai-matrx/records] a table-structure listener threw", thrown);
720
+ }
721
+ }
722
+ }
723
+
724
+ // src/archive.ts
725
+ var ARCHIVE_LANES = ["mine", "org"];
726
+ var DEFAULT_ARCHIVE_LANE = "org";
727
+ var ARCHIVE_LANE_LABEL = {
728
+ mine: "Archived by me",
729
+ org: "Archived by anyone"
730
+ };
731
+ var ARCHIVE_LANE_MEANS = {
732
+ mine: "The records you archived yourself.",
733
+ org: "Everything anybody in this organization archived, including you."
734
+ };
735
+ function isArchiveLane(word) {
736
+ return typeof word === "string" && ARCHIVE_LANES.includes(word);
737
+ }
738
+ function archivedWhen(at, locale = "en-GB") {
739
+ const when = new Date(at);
740
+ if (Number.isNaN(when.getTime())) return "a moment nobody recorded";
741
+ return new Intl.DateTimeFormat(locale, {
742
+ day: "numeric",
743
+ month: "long",
744
+ year: "numeric",
745
+ hour: "2-digit",
746
+ minute: "2-digit"
747
+ }).format(when);
748
+ }
749
+ function archivedByLine(row, locale = "en-GB") {
750
+ const when = archivedWhen(row.archivedAt, locale);
751
+ const who = (row.archivedByName ?? "").trim();
752
+ if (who === "") {
753
+ return `Archived on ${when}. The store does not know who archived it \u2014 whoever did is no longer in this organization, or it happened before this table kept that.`;
754
+ }
755
+ return `Archived by ${who} on ${when}.`;
756
+ }
757
+ function emptyArchiveLine(lane) {
758
+ return lane === "mine" ? "You have not archived anything in this table. Switch to \u201CArchived by anyone\u201D to see what the rest of the organization has." : "Nothing in this table has been archived. Records you remove from the grid arrive here, and nothing is ever destroyed.";
731
759
  }
732
- function mapPgError(error, where) {
733
- const raw = error.code ?? void 0;
734
- const sqlstate = raw && raw !== "" ? raw : void 0;
735
- const mapped = {
736
- code: "internal",
737
- message: error.message,
738
- ...error.hint ? { hint: error.hint } : {},
739
- ...sqlstate ? { sqlstate } : {}
740
- };
741
- if (isNotASqlstate(sqlstate)) {
742
- mapped.code = "unreachable";
743
- } else {
744
- mapped.code = BY_SQLSTATE[sqlstate] ?? "internal";
760
+ function sameDocument(a, b) {
761
+ return JSON.stringify(a) === JSON.stringify(b);
762
+ }
763
+
764
+ // src/aggregate.ts
765
+ function isRuleFilter(filter) {
766
+ if (!filter || typeof filter !== "object" || Array.isArray(filter)) return false;
767
+ const node = filter;
768
+ return typeof node.op === "string" && Array.isArray(node.args);
769
+ }
770
+ function measureKey(measure) {
771
+ return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
772
+ }
773
+ function stableFilterKey(filter) {
774
+ if (!filter) return "{}";
775
+ if (isRuleFilter(filter)) return JSON.stringify(filter);
776
+ const keys = Object.keys(filter).sort();
777
+ if (keys.length === 0) return "{}";
778
+ const parts = keys.map((key) => {
779
+ const value = filter[key];
780
+ if (value !== null && typeof value === "object") {
781
+ const window2 = value;
782
+ return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
783
+ }
784
+ return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
785
+ });
786
+ return `{${parts.join(",")}}`;
787
+ }
788
+ function liftWithheld(rows) {
789
+ let shared;
790
+ for (const row of rows) {
791
+ const m = (row.measures ?? {})["_withheld"];
792
+ if (m !== void 0 && m !== null && typeof m === "object") {
793
+ shared = m;
794
+ break;
795
+ }
745
796
  }
746
- const detail = parseDetail(error.details);
747
- if (detail !== void 0) mapped.detail = detail;
748
- if (mapped.code === "internal") {
749
- mapped.unmapped = true;
750
- mapped.diagnostic = `@ai-matrx/records did not recognise SQLSTATE ${sqlstate ?? "(none)"} from ${where}; the store's own message is the thing to act on.`;
751
- } else if (mapped.code === "unreachable") {
752
- mapped.unmapped = true;
753
- mapped.diagnostic = `@ai-matrx/records never reached the store from ${where}: ${raw ? `the transport answered ${raw}` : "no response"} \u2014 ${error.message}`;
754
- mapped.message = "We could not reach your data.";
755
- delete mapped.hint;
797
+ return rows.map((row) => {
798
+ const measures = row.measures ?? {};
799
+ const own = measures["_withheld"];
800
+ const marker = own !== void 0 && own !== null && typeof own === "object" ? own : shared;
801
+ const rowCount = row.row_count ?? 0;
802
+ if (marker === void 0 || marker === null || typeof marker !== "object") {
803
+ return rowCount === row.row_count ? row : { ...row, row_count: rowCount };
804
+ }
805
+ const { _withheld: _drop, ...rest } = measures;
806
+ const withheld = {};
807
+ for (const [name, raw] of Object.entries(marker)) {
808
+ const m = raw ?? {};
809
+ withheld[name] = {
810
+ reason: m.withheld?.reason ?? null,
811
+ needs: m.withheld?.needs ?? null,
812
+ or: m.withheld?.or ?? null,
813
+ ...m.says ? { says: m.says } : {},
814
+ ...m.label ? { label: m.label } : {},
815
+ ...m.field_key ? { field_key: m.field_key } : {}
816
+ };
817
+ }
818
+ return { ...row, measures: rest, withheld, row_count: rowCount };
819
+ });
820
+ }
821
+
822
+ // src/record.ts
823
+ function isLockedStub(row) {
824
+ if (!row) return false;
825
+ if (row.level !== null && row.level !== void 0) return false;
826
+ const doc = row.document;
827
+ if (!doc || typeof doc !== "object") return false;
828
+ const keys = Object.keys(doc);
829
+ if (keys.length === 0 || keys.length > 3) return false;
830
+ const d = doc;
831
+ return d["exists"] === true && keys.every((k) => k === "id" || k === "exists" || k === "submitted_at");
832
+ }
833
+ function splitLockedRows(rows) {
834
+ const open = [];
835
+ let locked = 0;
836
+ for (const row of rows) {
837
+ if (isLockedStub(row)) locked += 1;
838
+ else open.push(row);
756
839
  }
757
- return mapped;
840
+ return { rows: open, locked };
758
841
  }
759
- function staleWriteDetail(error) {
760
- if (error.code !== "stale_write") return null;
761
- const detail = error.detail;
762
- if (!detail || typeof detail.expected_version !== "number" || typeof detail.current_version !== "number") {
842
+
843
+ // src/subscription.ts
844
+ function hasQuietHours(quiet2) {
845
+ return Boolean(quiet2?.start && quiet2?.end);
846
+ }
847
+ function callersTimeZone() {
848
+ try {
849
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || null;
850
+ } catch {
763
851
  return null;
764
852
  }
765
- return {
766
- expected_version: detail.expected_version,
767
- current_version: detail.current_version,
768
- contested_fields: detail.contested_fields ?? {}
769
- };
853
+ }
854
+ function withCallersClock(spec, zone = callersTimeZone()) {
855
+ if (!zone || spec.quiet_hours?.tz) return spec;
856
+ return { ...spec, quiet_hours: { ...spec.quiet_hours ?? {}, tz: zone } };
857
+ }
858
+
859
+ // src/rowLabel.ts
860
+ var ROW_LABEL_FORMULA_MAX_LENGTH = 2e3;
861
+ function clearsRowLabel(formula) {
862
+ return formula == null || formula.trim() === "";
770
863
  }
771
864
 
772
865
  // src/relation.ts
@@ -1617,11 +1710,51 @@ function createScopeDoors(call) {
1617
1710
  source_type: String(envelope.data?.source_type ?? cell.source_type ?? "ai_enriched")
1618
1711
  }
1619
1712
  };
1713
+ },
1714
+ tableProvision({ organization_id, item_id, scope_id, home_id }) {
1715
+ return call(
1716
+ "scope_table_provision",
1717
+ { p_organization_id: organization_id, p_item_id: item_id, p_scope_id: scope_id, p_home_id: home_id ?? null },
1718
+ "scopeTableProvision"
1719
+ );
1620
1720
  }
1621
1721
  };
1622
1722
  return doors;
1623
1723
  }
1624
1724
 
1725
+ // src/core/pickListDoors.ts
1726
+ var PICK_LIST_DOORS = {
1727
+ pickListGet: "pick_list_get",
1728
+ pickListForSelection: "pick_list_for_selection",
1729
+ pickListUpdate: "pick_list_update"
1730
+ };
1731
+ function createPickListDoors(callDoor) {
1732
+ return {
1733
+ async pickListGet({ list_id }) {
1734
+ return callDoor(PICK_LIST_DOORS.pickListGet, { p_list_id: list_id }, "pickListGet");
1735
+ },
1736
+ async pickListForSelection({ list_id }) {
1737
+ return callDoor(
1738
+ PICK_LIST_DOORS.pickListForSelection,
1739
+ { p_list_id: list_id },
1740
+ "pickListForSelection"
1741
+ );
1742
+ },
1743
+ async pickListUpdate({ list_id, list_name, description, items }) {
1744
+ return callDoor(
1745
+ PICK_LIST_DOORS.pickListUpdate,
1746
+ {
1747
+ p_list_id: list_id,
1748
+ p_list_name: list_name ?? null,
1749
+ p_description: description ?? null,
1750
+ p_items: items ? [...items] : null
1751
+ },
1752
+ "pickListUpdate"
1753
+ );
1754
+ }
1755
+ };
1756
+ }
1757
+
1625
1758
  // src/choice.ts
1626
1759
  var import_uuid3 = require("@ai-matrx/kit/uuid");
1627
1760
  function optionKey(option) {
@@ -4824,12 +4957,7 @@ function doorReady(config, name) {
4824
4957
  return DOOR_NAMES.has(name) || Boolean(config?.knownDoors?.includes(name));
4825
4958
  }
4826
4959
  function doorAbsent(name) {
4827
- const owed = OWED_BY[name];
4828
- return {
4829
- code: "door_absent",
4830
- message: `The record store has no door \`custom.${name}\` yet, so this call was not made. Nothing was read and nothing was written.`,
4831
- hint: owed ? `That door is ${owed}. Until it lands, this call refuses rather than guessing: an empty answer here would read like "there is nothing", which is a different sentence.` : `No lane in the build book claims this door. Either the name is wrong, or the door was removed from the store and this package has not been regenerated \u2014 run \`pnpm records:generate\`.`
4832
- };
4960
+ return doorAbsentRefusal(name, OWED_BY[name]);
4833
4961
  }
4834
4962
  var DOORS = {
4835
4963
  // THE READ DOOR (DOOR-1). Every read of a record's values goes through these
@@ -5629,29 +5757,6 @@ function intersectIds(sets) {
5629
5757
 
5630
5758
  // src/core/client.ts
5631
5759
  var DEFAULT_SCHEMA = "custom";
5632
- function canonicalArgs(value) {
5633
- return JSON.stringify(
5634
- value,
5635
- (_key, held) => held && typeof held === "object" && !Array.isArray(held) ? Object.fromEntries(Object.keys(held).sort().map((k) => [k, held[k]])) : held
5636
- );
5637
- }
5638
- var A_PERSONS_REFUSAL = /* @__PURE__ */ new Set([
5639
- "refused_by_rule",
5640
- "stale_write",
5641
- "invalid_argument",
5642
- "already_exists",
5643
- "missing_reference"
5644
- ]);
5645
- function isAPersonsRefusal(code) {
5646
- return A_PERSONS_REFUSAL.has(code);
5647
- }
5648
- function sink(config) {
5649
- return config.onError ?? ((e) => {
5650
- const line = `[@ai-matrx/records] ${e.where}: ${e.code} \u2014 ${e.message}${e.hint ? ` (${e.hint})` : ""}`;
5651
- if (isAPersonsRefusal(e.code)) console.info(line);
5652
- else console.error(line);
5653
- });
5654
- }
5655
5760
  var TABLE_LIST_PAGE = 500;
5656
5761
  var TABLE_ARCHIVE_PASS = 20;
5657
5762
  var TABLE_RESTORE_PASS = 20;
@@ -5664,7 +5769,7 @@ function createRecordsClient(given) {
5664
5769
  const org = config.organizationId;
5665
5770
  const ledger = config.opLedger ?? defaultOpLedger;
5666
5771
  const mintOp = () => ledger.mint();
5667
- const scream = sink(config);
5772
+ const scream = recordsErrorSink(config.onError);
5668
5773
  const BUNDLE_TTL_MS = 1e4;
5669
5774
  const BUNDLE_OPENERS = /* @__PURE__ */ new Set(["applicable_fields", "views", "view_look_read", "table_decorations", "row_actions", "reverse_columns", "record_change_actions", "grid_layout", "io_imports"]);
5670
5775
  const bundleParts = /* @__PURE__ */ new Map();
@@ -5806,56 +5911,20 @@ function createRecordsClient(given) {
5806
5911
  bundleAsked.delete(tableId);
5807
5912
  for (const key of [...bundleParts.keys()]) if (key.includes(tableId)) bundleParts.delete(key);
5808
5913
  }
5809
- function primedFor(door, args) {
5810
- const look = LOOK_WRITES[door];
5811
- if (look) {
5812
- dropPrimedDoorAnswers(look);
5813
- return null;
5814
- }
5815
- if (isWritingDoor(door)) {
5816
- dropPrimedDoorAnswers();
5817
- return null;
5818
- }
5819
- return takePrimedDoorAnswer(`${door}|${canonicalArgs(args)}`);
5820
- }
5821
- const readsInFlight = /* @__PURE__ */ new Map();
5822
- async function callDoor(door, args, where, opts) {
5823
- const primed = primedFor(door, args);
5824
- if (primed) return ok(primed.data);
5825
- const bundled = await fromBundle(door, args);
5826
- if (bundled) return ok(bundled.data);
5827
- if (!doorReady(config, door)) {
5828
- const refusal3 = doorAbsent(door);
5829
- scream({ code: refusal3.code, message: refusal3.message, ...refusal3.hint ? { hint: refusal3.hint } : {}, where });
5830
- return err(refusal3);
5831
- }
5832
- const flightKey = isWritingDoor(door) ? null : `${door}|${canonicalArgs(args)}`;
5833
- const flying = flightKey ? readsInFlight.get(flightKey) : void 0;
5834
- const response = await (flying ?? (() => {
5835
- const call = Promise.resolve(config.dataSource.rpc(`${door}`, args, { schema }));
5836
- if (flightKey) {
5837
- readsInFlight.set(flightKey, call);
5838
- const forget = () => {
5839
- if (readsInFlight.get(flightKey) === call) readsInFlight.delete(flightKey);
5840
- };
5841
- call.then(forget, forget);
5842
- }
5843
- return call;
5844
- })());
5845
- if (response.error) {
5846
- const refusal3 = mapPgError(response.error, where);
5847
- if (opts?.quiet) console.info(`[@ai-matrx/records] ${where}: ${refusal3.code} - ${refusal3.message}`);
5848
- else scream({ code: refusal3.code, message: refusal3.message, ...refusal3.hint ? { hint: refusal3.hint } : {}, where });
5849
- return err(refusal3);
5850
- }
5851
- if (isWritingDoor(door) || LOOK_WRITES[door]) {
5914
+ const primedFor = primedDoorAnswer;
5915
+ const callDoor = createDoorCall({
5916
+ dataSource: config.dataSource,
5917
+ schema,
5918
+ onError: scream,
5919
+ doorReady: (door) => doorReady(config, door),
5920
+ absent: doorAbsent,
5921
+ held: fromBundle,
5922
+ wrote: (_door, args) => {
5852
5923
  const tableId = args["p_table_id"];
5853
5924
  bundleDrop(typeof tableId === "string" ? tableId : null);
5854
- forgetPrimedTablePageBundlesOf(typeof tableId === "string" ? tableId : null);
5855
- dropPrimedDoorAnswers(LOOK_WRITES[door]);
5856
5925
  }
5857
- return ok(response.data);
5858
- }
5926
+ });
5927
+ const scopes = createScopeDoors(callDoor);
5859
5928
  function structureMoved(tableId, result) {
5860
5929
  if (result.ok) {
5861
5930
  bundleDrop(tableId);
@@ -6054,7 +6123,7 @@ function createRecordsClient(given) {
6054
6123
  const unavailableReason = (error) => `${error.message}${error.hint ? ` ${error.hint}` : ""}`;
6055
6124
  const client = {
6056
6125
  config,
6057
- scopes: createScopeDoors(callDoor),
6126
+ scopes,
6058
6127
  async recordWrite({ table_id, data }) {
6059
6128
  const result = await callDoor(
6060
6129
  DOORS.recordWrite,
@@ -6753,28 +6822,7 @@ function createRecordsClient(given) {
6753
6822
  "fieldOptions"
6754
6823
  );
6755
6824
  },
6756
- async pickListGet({ list_id }) {
6757
- return callDoor(DOORS.pickListGet, { p_list_id: list_id }, "pickListGet");
6758
- },
6759
- async pickListForSelection({ list_id }) {
6760
- return callDoor(
6761
- DOORS.pickListForSelection,
6762
- { p_list_id: list_id },
6763
- "pickListForSelection"
6764
- );
6765
- },
6766
- async pickListUpdate({ list_id, list_name, description, items }) {
6767
- return callDoor(
6768
- DOORS.pickListUpdate,
6769
- {
6770
- p_list_id: list_id,
6771
- p_list_name: list_name ?? null,
6772
- p_description: description ?? null,
6773
- p_items: items ? [...items] : null
6774
- },
6775
- "pickListUpdate"
6776
- );
6777
- },
6825
+ ...createPickListDoors(callDoor),
6778
6826
  async fieldChoiceUsage({ field_id }) {
6779
6827
  const result = await callDoor(
6780
6828
  DOORS.fieldChoiceUsage,
@@ -9308,11 +9356,7 @@ function createRecordsClient(given) {
9308
9356
  );
9309
9357
  },
9310
9358
  async scopeTableProvision({ home_id, item_id, scope_id }) {
9311
- return callDoor(
9312
- DOORS.scopeTableProvision,
9313
- { p_organization_id: org, p_item_id: item_id, p_scope_id: scope_id, p_home_id: home_id ?? null },
9314
- "scopeTableProvision"
9315
- );
9359
+ return scopes.tableProvision({ organization_id: org, item_id, scope_id, home_id: home_id ?? null });
9316
9360
  },
9317
9361
  async tableRowCounts({ table_ids }) {
9318
9362
  return callDoor(