@ai-matrx/records 0.57.3 → 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.
package/dist/index.cjs CHANGED
@@ -56,6 +56,7 @@ __export(src_exports, {
56
56
  PERMISSION_LEVELS: () => PERMISSION_LEVELS,
57
57
  PER_VALUE_ACCESS_WORDS: () => PER_VALUE_ACCESS_WORDS,
58
58
  PIPELINE_RULE_KINDS: () => PIPELINE_RULE_KINDS,
59
+ PORTAL_ACCENTS: () => PORTAL_ACCENTS,
59
60
  PORTAL_ARCHIVE_FILTERS: () => PORTAL_ARCHIVE_FILTERS,
60
61
  REFUSALS_WORTH_REPEATING: () => REFUSALS_WORTH_REPEATING,
61
62
  RELATION_BINDINGS: () => RELATION_BINDINGS,
@@ -90,6 +91,11 @@ __export(src_exports, {
90
91
  coercionLabel: () => coercionLabel,
91
92
  emptyArchiveLine: () => emptyArchiveLine,
92
93
  emptyPortalArchiveLine: () => emptyPortalArchiveLine,
94
+ entityReferenceKinds: () => entityReferenceKinds,
95
+ entityReferenceKindsOf: () => entityReferenceKindsOf,
96
+ entityReferenceWords: () => entityReferenceWords,
97
+ entityRefsOf: () => entityRefsOf,
98
+ entityRefsToWrite: () => entityRefsToWrite,
93
99
  err: () => err,
94
100
  exampleSpec: () => exampleSpec,
95
101
  exportPageXlsx: () => exportPageXlsx,
@@ -100,20 +106,31 @@ __export(src_exports, {
100
106
  indexedDbStore: () => indexedDbStore,
101
107
  isArchiveLane: () => isArchiveLane,
102
108
  isDecided: () => isDecided,
109
+ isEntityReferenceConfig: () => isEntityReferenceConfig,
103
110
  isOwnOp: () => isOwnOp,
111
+ isPortalAccent: () => isPortalAccent,
104
112
  isPortalArchiveFilter: () => isPortalArchiveFilter,
105
113
  isRecordsErr: () => isRecordsErr,
114
+ isRuleFilter: () => isRuleFilter,
106
115
  isTheChosen: () => isTheChosen,
116
+ keptGroups: () => keptGroups,
117
+ keptOutOfSight: () => keptOutOfSight,
118
+ keptTableHref: () => keptTableHref,
107
119
  looksLikeAnId: () => looksLikeAnId,
108
120
  mayDrag: () => mayDrag,
109
121
  measureKey: () => measureKey,
110
122
  memoryStore: () => memoryStore,
111
123
  mintClientKey: () => mintClientKey,
112
124
  mintOpId: () => mintOpId,
125
+ moveConsequence: () => moveConsequence,
126
+ moveInOrder: () => moveInOrder,
127
+ offeredAsContext: () => offeredAsContext,
113
128
  ok: () => ok,
114
129
  openCaptureQueue: () => openCaptureQueue,
115
130
  optionKey: () => optionKey,
116
131
  optionLabel: () => optionLabel,
132
+ parseTableHome: () => parseTableHome,
133
+ parseTableMoved: () => parseTableMoved,
117
134
  portalArchiveConsequence: () => portalArchiveConsequence,
118
135
  portalConfirmLine: () => portalConfirmLine,
119
136
  portalPath: () => portalPath,
@@ -123,7 +140,10 @@ __export(src_exports, {
123
140
  rememberedOpCount: () => rememberedOpCount,
124
141
  sameDocument: () => sameDocument,
125
142
  scopeChip: () => scopeChip,
126
- stableFilterKey: () => stableFilterKey
143
+ sheetSyncSpec: () => sheetSyncSpec,
144
+ stableFilterKey: () => stableFilterKey,
145
+ withRowCounts: () => withRowCounts,
146
+ withTablePlacement: () => withTablePlacement
127
147
  });
128
148
  module.exports = __toCommonJS(src_exports);
129
149
 
@@ -305,6 +325,7 @@ var FIELD_KINDS = [
305
325
  "currency",
306
326
  "datetime",
307
327
  "email",
328
+ "entity_reference",
308
329
  "formula",
309
330
  "json",
310
331
  "long_text",
@@ -343,6 +364,7 @@ function fieldKindFor(field, kernel) {
343
364
  case "list":
344
365
  return field.multi ? "multi_select" : "select";
345
366
  case "relation": {
367
+ if (isEntityReferenceConfig(config)) return "entity_reference";
346
368
  const parity = String(config["parity"] ?? "");
347
369
  if (parity === "attachment" || parity === "member") return parity;
348
370
  if (kernel?.file && field.relation_target === kernel.file) return "attachment";
@@ -360,6 +382,14 @@ function fieldKindFor(field, kernel) {
360
382
  }
361
383
  }
362
384
  var UUID_TEXT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
385
+ function isEntityReferenceConfig(config) {
386
+ const allowed = config?.["allowed_types"];
387
+ return Array.isArray(allowed) && allowed.length > 0;
388
+ }
389
+ function entityReferenceKindsOf(field) {
390
+ const allowed = field.config?.["allowed_types"];
391
+ return Array.isArray(allowed) ? allowed.map((k) => String(k).trim().toLowerCase()).filter(Boolean) : [];
392
+ }
363
393
  var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
364
394
  var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
365
395
  function coercionLabel(field) {
@@ -475,6 +505,23 @@ function coerceTypedAnswer(field, raw, opts = {}) {
475
505
  return {
476
506
  refusal: `${label} points at another record, so it is chosen rather than typed. Open it and pick the record.`
477
507
  };
508
+ // SC-R / P12. An entity reference names a platform thing by kind and id.
509
+ // An import or an agent may hand over `note:<id>` — or a bare id when the
510
+ // column allows exactly one kind; the store then decides whether the thing
511
+ // is there and whether the writer may open it. Words are chosen, not typed.
512
+ case "entity_reference": {
513
+ const allowed = entityReferenceKindsOf(field);
514
+ const [maybeToken, maybeId] = text.includes(":") ? text.split(/:(.*)/s, 2) : ["", text];
515
+ const token = (maybeToken ?? "").trim().toLowerCase() || (allowed.length === 1 ? allowed[0] : "");
516
+ const id = (maybeId ?? "").trim();
517
+ if (token && UUID_TEXT.test(id)) {
518
+ const ref = { token, id: id.toLowerCase() };
519
+ return { value: field.multi ? [ref] : ref };
520
+ }
521
+ return {
522
+ refusal: `${label} points at something on the platform, so it is chosen rather than typed. Open it and pick it.`
523
+ };
524
+ }
478
525
  case "formula":
479
526
  case "lookup":
480
527
  case "rollup":
@@ -531,6 +578,10 @@ function publicFormPath(formId) {
531
578
  }
532
579
 
533
580
  // src/portal.ts
581
+ var PORTAL_ACCENTS = ["slate", "green", "amber", "red", "blue", "violet", "teal"];
582
+ function isPortalAccent(word) {
583
+ return typeof word === "string" && PORTAL_ACCENTS.includes(word);
584
+ }
534
585
  function portalPath(slug) {
535
586
  return `/portal/c/${slug}`;
536
587
  }
@@ -554,11 +605,17 @@ function emptyPortalArchiveLine() {
554
605
  }
555
606
 
556
607
  // src/aggregate.ts
608
+ function isRuleFilter(filter) {
609
+ if (!filter || typeof filter !== "object" || Array.isArray(filter)) return false;
610
+ const node = filter;
611
+ return typeof node.op === "string" && Array.isArray(node.args);
612
+ }
557
613
  function measureKey(measure) {
558
614
  return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
559
615
  }
560
616
  function stableFilterKey(filter) {
561
617
  if (!filter) return "{}";
618
+ if (isRuleFilter(filter)) return JSON.stringify(filter);
562
619
  const keys = Object.keys(filter).sort();
563
620
  if (keys.length === 0) return "{}";
564
621
  const parts = keys.map((key) => {
@@ -654,6 +711,161 @@ function recordChangeTrigger(tableId, options = {}) {
654
711
  ...options.changedFields && options.changedFields.length > 0 ? { changed_fields: options.changedFields } : {}
655
712
  };
656
713
  }
714
+ function sheetSyncSpec(args) {
715
+ const [header = [], ...body] = args.cells;
716
+ const columns = header.map((c, i) => {
717
+ const name = c == null ? "" : String(c).trim();
718
+ return name === "" ? `Column ${i + 1}` : name;
719
+ });
720
+ if (columns.length === 0) {
721
+ throw new Error("The sheet has no header row, so there are no columns to sync. Nothing was sent.");
722
+ }
723
+ const seenCol = /* @__PURE__ */ new Set();
724
+ for (const c of columns) {
725
+ if (seenCol.has(c)) throw new Error(`The sheet has two columns named "${c}". Rename one and sync again.`);
726
+ seenCol.add(c);
727
+ }
728
+ const refIdx = args.refColumn == null ? -1 : columns.indexOf(args.refColumn);
729
+ if (args.refColumn != null && refIdx < 0) {
730
+ throw new Error(`The id column "${args.refColumn}" is not in the sheet's header. Nothing was sent.`);
731
+ }
732
+ const seen = /* @__PURE__ */ new Set();
733
+ const rows = [];
734
+ body.forEach((cells, i) => {
735
+ if (cells.every((v) => v == null || String(v).trim() === "")) return;
736
+ const ref = refIdx >= 0 ? String(cells[refIdx] ?? "").trim() : `row-${i + 2}`;
737
+ if (ref === "") throw new Error(`Sheet row ${i + 2} has no value in the id column "${args.refColumn}".`);
738
+ if (seen.has(ref)) throw new Error(`Two sheet rows share the id "${ref}"; each row needs its own.`);
739
+ seen.add(ref);
740
+ const values = {};
741
+ columns.forEach((c, j) => {
742
+ const v = cells[j];
743
+ values[c] = v === void 0 || v === "" ? null : v;
744
+ });
745
+ rows.push({ ref, values });
746
+ });
747
+ return { source: args.source, table_name: args.tableName, columns, rows };
748
+ }
749
+ function withRowCounts(tables, counts) {
750
+ if (!counts) return [...tables];
751
+ const byId = new Map(counts.map((c) => [c.table_id, Number(c.visible_rows)]));
752
+ return tables.map((t) => byId.has(t.id) ? { ...t, row_count: byId.get(t.id) } : t);
753
+ }
754
+ function moveInOrder(order, moved, toIndex) {
755
+ const rest = order.filter((id) => id !== moved);
756
+ const at = Math.max(0, Math.min(Math.trunc(toIndex), rest.length));
757
+ return [...rest.slice(0, at), moved, ...rest.slice(at)];
758
+ }
759
+
760
+ // src/placement.ts
761
+ function keptOutOfSight(placement) {
762
+ return placement?.kept_by_the_app === true;
763
+ }
764
+ function offeredAsContext(placement) {
765
+ return placement?.offered_as_context === true;
766
+ }
767
+ function withTablePlacement(tables, facts) {
768
+ if (!facts) return [...tables];
769
+ const byId = new Map(facts.map((f) => [f.table_id, f]));
770
+ return tables.map((table) => {
771
+ const fact = byId.get(table.id);
772
+ if (!fact || fact.kept_by_the_app === void 0) return table;
773
+ return {
774
+ ...table,
775
+ kept_by_the_app: fact.kept_by_the_app,
776
+ kept_for: fact.kept_for ?? null,
777
+ offered_as_context: fact.offered_as_context ?? false,
778
+ keeper_group: fact.keeper_group ?? null,
779
+ keeper_says: fact.keeper_says ?? null,
780
+ used_in_kind: fact.used_in_kind ?? null,
781
+ used_in_id: fact.used_in_id ?? null,
782
+ used_in_table_id: fact.used_in_table_id ?? null
783
+ };
784
+ });
785
+ }
786
+ function keptGroups(tables) {
787
+ const out = [];
788
+ const at = /* @__PURE__ */ new Map();
789
+ for (const table of tables) {
790
+ if (!keptOutOfSight(table)) continue;
791
+ const group = table.keeper_group?.trim() || "Kept by the app";
792
+ let i = at.get(group);
793
+ if (i === void 0) {
794
+ i = out.length;
795
+ at.set(group, i);
796
+ out.push({ group, tables: [] });
797
+ }
798
+ out[i].tables.push(table);
799
+ }
800
+ return out;
801
+ }
802
+ function keptTableHref(fact, hrefs) {
803
+ if (fact.used_in_kind === "scope" && fact.used_in_id) return hrefs.scope(fact.used_in_id);
804
+ if (fact.used_in_kind === "table" && (fact.used_in_table_id || fact.used_in_id)) {
805
+ return hrefs.table(fact.used_in_table_id || fact.used_in_id);
806
+ }
807
+ return hrefs.table(fact.table_id);
808
+ }
809
+ function countOf(value) {
810
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
811
+ }
812
+ function wordsOf(value) {
813
+ return typeof value === "string" && value.length > 0 ? value : null;
814
+ }
815
+ function parseTableHome(raw) {
816
+ if (!raw || typeof raw !== "object") return null;
817
+ const row = raw;
818
+ const table = row.table;
819
+ const org = row.organization;
820
+ if (!table || !org || !wordsOf(table.id) || !wordsOf(org.id) || !wordsOf(org.name)) return null;
821
+ const carries = row.carries ?? {};
822
+ const destinations = Array.isArray(row.destinations) ? row.destinations : [];
823
+ return {
824
+ table: { id: String(table.id), name: wordsOf(table.name) ?? "This table", version: countOf(table.version) },
825
+ organization: { id: String(org.id), name: String(org.name) },
826
+ mayMove: row.may_move === true,
827
+ whyNot: wordsOf(row.why_not),
828
+ heldBy: Array.isArray(row.held_by) ? row.held_by.filter((s) => typeof s === "string") : [],
829
+ destinations: destinations.filter((d) => !!d && typeof d === "object" && !!wordsOf(d.id)).map((d) => ({
830
+ id: String(d.id),
831
+ name: wordsOf(d.name) ?? "An organization",
832
+ role: wordsOf(d.role) ?? "member",
833
+ ok: d.ok === true,
834
+ why: wordsOf(d.why)
835
+ })),
836
+ carries: {
837
+ fields: countOf(carries.fields),
838
+ records: countOf(carries.records),
839
+ inTrash: countOf(carries.in_trash),
840
+ choiceLists: countOf(carries.choice_lists),
841
+ withIt: countOf(carries.with_it),
842
+ tablesInside: countOf(carries.tables_inside),
843
+ linksAcross: countOf(carries.links_across)
844
+ }
845
+ };
846
+ }
847
+ function parseTableMoved(raw) {
848
+ const row = raw ?? {};
849
+ const to = row.to ?? {};
850
+ const from = row.from ?? {};
851
+ const table = row.table ?? {};
852
+ if (row.moved !== true || !wordsOf(to.id)) return null;
853
+ return {
854
+ to: { id: String(to.id), name: wordsOf(to.name) ?? "the new organization" },
855
+ from: { id: wordsOf(from.id) ?? "", name: wordsOf(from.name) ?? "its old organization" },
856
+ tableName: wordsOf(table.name) ?? "The table"
857
+ };
858
+ }
859
+ function plural(n, one, many) {
860
+ return `${n} ${n === 1 ? one : many}`;
861
+ }
862
+ function moveConsequence(home, to) {
863
+ const c = home.carries;
864
+ const parts = [plural(c.fields, "column", "columns"), plural(c.records, "row", "rows")];
865
+ if (c.inTrash > 0) parts.push(`${c.inTrash} in the trash`);
866
+ const extra = c.choiceLists + c.withIt;
867
+ 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.`;
868
+ }
657
869
 
658
870
  // src/capture-queue.ts
659
871
  var DB_NAME = "matrx-capture";
@@ -723,7 +935,7 @@ function memoryStore() {
723
935
  setMeta: async (key, value) => void meta.set(key, value)
724
936
  };
725
937
  }
726
- function countOf(items) {
938
+ function countOf2(items) {
727
939
  const c = { waiting: 0, sending: 0, refused: 0, landed: 0 };
728
940
  for (const i of items) c[i.state] += 1;
729
941
  return c;
@@ -740,7 +952,7 @@ function openCaptureQueue(opts) {
740
952
  const announce = async () => {
741
953
  if (!opts.onChange) return;
742
954
  const items = await store.all();
743
- opts.onChange(countOf(items), items);
955
+ opts.onChange(countOf2(items), items);
744
956
  };
745
957
  const oldestFirst = (items) => items.slice().sort((a, b) => a.captured_at.localeCompare(b.captured_at));
746
958
  const queue = {
@@ -769,7 +981,7 @@ function openCaptureQueue(opts) {
769
981
  return key;
770
982
  },
771
983
  list: () => store.all().then(oldestFirst),
772
- counts: () => store.all().then(countOf),
984
+ counts: () => store.all().then(countOf2),
773
985
  async sync() {
774
986
  if (syncing) return syncing;
775
987
  syncing = (async () => {
@@ -941,6 +1153,182 @@ function isRecordsErr(result) {
941
1153
  var ok = (data) => ({ ok: true, data });
942
1154
  var err = (error) => ({ ok: false, error });
943
1155
 
1156
+ // src/core/pgError.ts
1157
+ var BY_SQLSTATE = {
1158
+ PT409: "stale_write",
1159
+ "42501": "door",
1160
+ "23514": "refused_by_rule",
1161
+ "22004": "invalid_argument",
1162
+ "22023": "invalid_argument",
1163
+ // 22P02 — a value does not fit the type the store holds for it. The class is
1164
+ // real and was hit live: an organization whose copy of a package table grew
1165
+ // `status` as a uuid-valued Field was sent the text "open", and the store
1166
+ // answered `invalid input syntax for type uuid: "open"`. It is the CALL that
1167
+ // is wrong for the shape the store holds, which is what `invalid_argument`
1168
+ // already means — the store's own sentence names the value and the type.
1169
+ "22P02": "invalid_argument",
1170
+ // 42703 — a column that is not there. The store raises it from
1171
+ // `custom.entity_table`, which is asked for a column by NAME, so the class is
1172
+ // exactly "the call named something the shape does not have": the same thing
1173
+ // `invalid_argument` already means, and the store's own sentence names the
1174
+ // column. Added 2026-09-20 (lane FORMS) because the package's live-door suite
1175
+ // caught it raised by a door this package did not know about — which is the
1176
+ // suite working, and is why the map is a census rather than a guess.
1177
+ "42703": "invalid_argument",
1178
+ // 22007 — a date or a time the store could not read as one. It is raised by
1179
+ // `custom.dashboard_window_sql`, which is handed a window a caller wrote
1180
+ // ("last 30 days", a literal date), so the class is again "the call named
1181
+ // something the shape does not accept" and the store's own sentence names the
1182
+ // text it could not read. Added 2026-09-20 (lane SCREEN-BREAK) after the
1183
+ // live-door census caught it — the same way 42703 arrived, which is the
1184
+ // census working rather than a guess.
1185
+ "22007": "invalid_argument",
1186
+ // 22003 — a number the store could not hold at the size it was handed. It is
1187
+ // raised by `custom.context_resolve`, which is given a window or a depth a
1188
+ // caller wrote, so the class is the same one 22P02, 42703 and 22007 already
1189
+ // are: the CALL named a value the shape does not accept, and the store's own
1190
+ // sentence names the number and the range. Added 2026-09-20 (lane GRID-TWO)
1191
+ // when the live-door census caught the release gate red on it — which is the
1192
+ // census working, and the fourth time it has been the thing that found a new
1193
+ // refusal class rather than a person guessing one.
1194
+ "22003": "invalid_argument",
1195
+ "02000": "not_found",
1196
+ "23503": "missing_reference",
1197
+ "23505": "already_exists",
1198
+ "0A000": "not_supported",
1199
+ "53400": "store_limit",
1200
+ "22012": "store_limit",
1201
+ // 54001 — `custom.record_delete` stops a cascade that nests past sixty-four
1202
+ // levels of containment, because that is a loop somebody built rather than a
1203
+ // hierarchy, and saying so beats recursing until the server runs out of
1204
+ // stack. It is a LIMIT of the store, and the store's own sentence names what
1205
+ // to do about it, so it reads as one.
1206
+ "54001": "store_limit",
1207
+ // 54000 — the grid primitives' organization ceilings (GRID-PRIMITIVES, 2026-09-23):
1208
+ // `custom.action_run` on a selection past its knob, `custom.table_decorate` past the
1209
+ // rule count, `custom.table_from_example` past `custom/example_rows_max`,
1210
+ // `custom.table_webhook_declare` past `custom/table_webhooks_max`, and
1211
+ // `custom.hub_changed_by`. Each names its knob in the hint and writes nothing, so it
1212
+ // reads exactly like 54001: a limit of the store, in the store's own sentence.
1213
+ "54000": "store_limit",
1214
+ // VIS: `custom.has_visibility_at` refuses to answer from a snapshot older
1215
+ // than a write this reader has already seen, rather than returning something
1216
+ // they just lost access to. It is retryable on a fresher connection, which is
1217
+ // a different instruction from every other refusal here.
1218
+ "40001": "stale_read",
1219
+ // 57014 — the store cancelled the statement at its own timeout. Like
1220
+ // `stale_read` and unlike every other refusal here, the call was never
1221
+ // refused on its merits: the same call on a smaller page, or once the
1222
+ // organization's kernel is not being rewritten, can succeed. It gets its own
1223
+ // code so a screen can offer "try again" honestly instead of showing a
1224
+ // failure sentence the caller cannot act on.
1225
+ "57014": "timed_out",
1226
+ // PostgREST's own two: the store is not being SERVED, which is a different
1227
+ // thing from the store refusing. `custom` absent from `pgrst.db_schemas`, or
1228
+ // the grants not copied, land here.
1229
+ PGRST202: "store_not_served",
1230
+ PGRST205: "store_not_served",
1231
+ PGRST301: "door",
1232
+ // PT428 — NOT A FAILURE, and the release gate's live census is what found it
1233
+ // missing: the store raises it and this package answered `internal`, which is
1234
+ // the one classification that tells a screen nothing. A stage gate whose
1235
+ // `on_fail` is `require_approval` files the request and leaves the record
1236
+ // where it was; the transaction is rolled back to the pipeline's own block, so
1237
+ // "nothing was written" is a fact rather than a promise. Landed by lane
1238
+ // STAGE-RULES; mapped here by STAGE-RULES-2 the day after, when the census
1239
+ // caught it — the sixth time that census has been the thing that found a
1240
+ // refusal class rather than a person guessing one.
1241
+ PT428: "needs_approval"
1242
+ };
1243
+ var MAPPED_SQLSTATES = Object.keys(BY_SQLSTATE);
1244
+ function parseDetail(details) {
1245
+ if (!details) return void 0;
1246
+ const trimmed = details.trim();
1247
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return details;
1248
+ try {
1249
+ return JSON.parse(trimmed);
1250
+ } catch {
1251
+ return details;
1252
+ }
1253
+ }
1254
+ function isNotASqlstate(code) {
1255
+ if (!code) return true;
1256
+ return /^[0-9]{3}$/.test(code);
1257
+ }
1258
+ function mapPgError(error, where) {
1259
+ const raw = error.code ?? void 0;
1260
+ const sqlstate = raw && raw !== "" ? raw : void 0;
1261
+ const mapped = {
1262
+ code: "internal",
1263
+ message: error.message,
1264
+ ...error.hint ? { hint: error.hint } : {},
1265
+ ...sqlstate ? { sqlstate } : {}
1266
+ };
1267
+ if (isNotASqlstate(sqlstate)) {
1268
+ mapped.code = "unreachable";
1269
+ } else {
1270
+ mapped.code = BY_SQLSTATE[sqlstate] ?? "internal";
1271
+ }
1272
+ const detail = parseDetail(error.details);
1273
+ if (detail !== void 0) mapped.detail = detail;
1274
+ if (mapped.code === "internal") {
1275
+ mapped.unmapped = true;
1276
+ mapped.diagnostic = `@ai-matrx/records did not recognise SQLSTATE ${sqlstate ?? "(none)"} from ${where}; the store's own message is the thing to act on.`;
1277
+ } else if (mapped.code === "unreachable") {
1278
+ mapped.unmapped = true;
1279
+ mapped.diagnostic = `@ai-matrx/records never reached the store from ${where}: ${raw ? `the transport answered ${raw}` : "no response"} \u2014 ${error.message}`;
1280
+ mapped.message = "We could not reach your data.";
1281
+ delete mapped.hint;
1282
+ }
1283
+ return mapped;
1284
+ }
1285
+
1286
+ // src/relation.ts
1287
+ var ENTITY_REF_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1288
+ function isRefLike(value) {
1289
+ return typeof value === "object" && value !== null && "token" in value && "id" in value;
1290
+ }
1291
+ function entityRefsOf(value) {
1292
+ const items = Array.isArray(value) ? value : value == null ? [] : [value];
1293
+ const out = [];
1294
+ for (const item of items) {
1295
+ if (!isRefLike(item)) continue;
1296
+ const token = String(item.token ?? "").trim().toLowerCase();
1297
+ const id = String(item.id ?? "").trim();
1298
+ if (!token || !ENTITY_REF_ID.test(id)) continue;
1299
+ const label = item.label;
1300
+ out.push({ token, id: id.toLowerCase(), label: typeof label === "string" ? label : null });
1301
+ }
1302
+ return out;
1303
+ }
1304
+ function entityRefsToWrite(value, multi) {
1305
+ const seen = /* @__PURE__ */ new Set();
1306
+ const refs = [];
1307
+ for (const ref of entityRefsOf(value)) {
1308
+ const k = `${ref.token}:${ref.id}`;
1309
+ if (seen.has(k)) continue;
1310
+ seen.add(k);
1311
+ refs.push({ token: ref.token, id: ref.id });
1312
+ }
1313
+ if (multi) return refs;
1314
+ return refs[0] ?? null;
1315
+ }
1316
+ async function entityReferenceKinds(source) {
1317
+ const { data, error } = await source.rpc("entity_reference_kinds", {}, { schema: "custom" });
1318
+ if (error) return err(mapPgError(error, "entityReferenceKinds"));
1319
+ return ok(Array.isArray(data) ? data : []);
1320
+ }
1321
+ async function entityReferenceWords(source, organizationId, refs) {
1322
+ if (refs.length === 0) return ok([]);
1323
+ const { data, error } = await source.rpc(
1324
+ "entity_reference_words",
1325
+ { p_organization_id: organizationId, p_refs: refs.map((r) => ({ token: r.token, id: r.id })) },
1326
+ { schema: "custom" }
1327
+ );
1328
+ if (error) return err(mapPgError(error, "entityReferenceWords"));
1329
+ return ok(entityRefsOf(data));
1330
+ }
1331
+
944
1332
  // src/ops.ts
945
1333
  var REMEMBER_MS = 3e4;
946
1334
  var REMEMBER_MAX = 500;
@@ -1654,6 +2042,7 @@ var STORE_DOORS = [
1654
2042
  { name: "table_work_kind", args: "p_organization_id uuid, p_table_id uuid", returns: "text", security: "invoker" },
1655
2043
  { name: "tables_at_home", args: "p_organization_id uuid, p_home_ids uuid[]", returns: "TABLE(table_id uuid, home_record_id uuid, kind text)", security: "definer" },
1656
2044
  { name: "tables_described_without_asking", args: "", returns: "TABLE(function_name text, identity_args text, why text)", security: "invoker" },
2045
+ { name: "tables_i_can_open", args: "", returns: "TABLE(table_id uuid, table_name text, organization_id uuid, organization_name text, member boolean, visibility text, updated_at timestamp with time zone)", security: "definer" },
1657
2046
  { name: "tables_shared_with_me", args: "", returns: "TABLE(table_id uuid, organization_id uuid, organization text, table_name text, level text, level_label text, shared_at timestamp with time zone, opens boolean, say text)", security: "definer" },
1658
2047
  { name: "trg_associations_bump_visibility", args: "", returns: "trigger", security: "definer" },
1659
2048
  { name: "undeclared_keys", args: "p_organization_id uuid, p_table_id uuid, p_data jsonb", returns: "text[]", security: "invoker" },
@@ -1678,6 +2067,7 @@ var STORE_DOORS = [
1678
2067
  { name: "visible_predicate_sql", args: "p_user uuid, p_organization_id uuid, p_table_id uuid, p_required permission_level DEFAULT 'viewer'::permission_level, p_alias text DEFAULT 'r'::text", returns: "text", security: "definer" },
1679
2068
  { name: "visible_record_ids", args: "p_user_id uuid, p_required permission_level DEFAULT 'viewer'::permission_level", returns: "TABLE(id uuid)", security: "definer" },
1680
2069
  { name: "visible_set", args: "p_user uuid, p_organization_id uuid, p_table_id uuid, p_required permission_level DEFAULT 'viewer'::permission_level, OUT o_all_visible boolean, OUT o_true_visibility platform.visibility[], OUT o_granted_all uuid[], OUT o_granted_visible uuid[], OUT o_carried_visible uuid[], OUT o_ladder_calls integer, OUT o_fallback boolean, OUT o_note text", returns: "record", security: "definer" },
2070
+ { name: "where_id_opens", args: "p_id uuid", returns: "jsonb", security: "definer" },
1681
2071
  { name: "widget_kernel_id", args: "", returns: "uuid", security: "invoker" },
1682
2072
  { name: "withheld_marker", args: "p_mask jsonb, p_key text", returns: "jsonb", security: "invoker" },
1683
2073
  { name: "withheld_sentence", args: "p_mask jsonb, p_key text", returns: "text", security: "invoker" },
@@ -1784,7 +2174,7 @@ var STORE_REFUSAL_CODES = [
1784
2174
  { code: "23505", raised_by: "_share_write_person,_unique_rule_holds,doc_sign,doc_signature_write,entity_field_declare,field_declare,home_add,inbound_declare,io_proposal_accept,relation_carry,relation_own,sign_request_cancel,sign_request_create,table_share_outside_resend,work_approval_decide,work_slot_hold" },
1785
2175
  { code: "23514", raised_by: "_checklist_instantiate,_checklist_step_guard,_containment_guard,_dated_values_guard,_derived_fields,_display_format_check,_display_spec_for,_entity_custom_fields_guard,_field_class_guard,_field_document_for,_field_shape_guard,_field_type_parity_guard,_merge_field_shape_guard,_merge_field_temporal_guard,_promoted_field_cap_guard,_record_rule_uses,_relation_halves_agree,_relation_kernel_targets,_resolve_choice_words,_rule_shape_guard,_rule_topology_guard,_store_relation_edge_names_its_field,_table_columns_word_guard,_table_shape_guard,_undeclared_key_guard,_value_envelope,_with_display,_work_shape_guard,_workdoors_approval_guard,action_run,assert_columns_are_defined,assert_entity_is_organization_scoped,booking_declare,capture_publish,capture_sheet_declare,checklist_declare,checklist_step_complete,doc_sign,doc_signature_write,doc_template_save,entity_field_declare,entity_field_retire,entity_field_update,entity_records_find,entity_table,field_declare,field_retire,field_update,home_add,io_comment_write,io_import_rows,migrate_merge,migrate_purge_hard,portal_archive,portal_restore,record_write_graph,relation_carry,relation_halves_repair,relation_own,sign_request_create,sign_request_sign,table_declare,table_from_example,validate_value_envelope,validate_values,work_set_state,work_slots_declare,work_take_assignment,work_template_instantiate" },
1786
2176
  { code: "40001", raised_by: "has_visibility_at" },
1787
- { code: "42501", raised_by: "_doc_render_immutable,_doc_signature_immutable,_field_definition_write,_field_write_door,_rule_definition_write,_store_door,agent_table_claim,anon_capture,anon_inbound_land,anon_publish,anon_token_issue,anon_token_verify,anon_write,assert_client_may_change,assert_client_may_open,assert_client_may_reach,assert_may_know_table,assert_organization_admin,assert_store_door,booking_confirm,booking_declare,booking_hold,capture_submit,checklist_run,comment_write,conversation_scope_bind,conversation_scope_unbind,doc_sign,enrich_declare,enrich_due,enrich_land,enrich_normalize,entity_field_declare,entity_field_retire,entity_field_update,entity_value_write,external_write_through,form_declare,form_submit,inbound_mail_land,io_comment_resolve,io_comment_write,io_import_begin,io_import_finish,io_restore,migrate_purge,migrate_purge_hard,migrate_purge_hard_request,migrate_reclass,my_levels,organization_clear,portal_declare,portal_invitation,portal_invite,portal_invite_accept,portal_principal_bind,promote_table,query_visibility_parity,read_record,read_records,read_records_archived,read_records_by_ids,read_records_matching,record_table,record_write_graph,relation_halves_disagreements,relation_halves_repair,share_grant,subscription_mute,subscription_preview,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,table_webhook_declare,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_person" },
2177
+ { code: "42501", raised_by: "_doc_render_immutable,_doc_signature_immutable,_field_definition_write,_field_write_door,_rule_definition_write,_store_door,agent_table_claim,anon_capture,anon_inbound_land,anon_publish,anon_token_issue,anon_token_verify,anon_write,assert_client_may_change,assert_client_may_open,assert_client_may_reach,assert_may_know_table,assert_organization_admin,assert_store_door,booking_confirm,booking_declare,booking_hold,capture_submit,checklist_run,comment_write,conversation_scope_bind,conversation_scope_unbind,doc_sign,enrich_declare,enrich_due,enrich_land,enrich_normalize,entity_field_declare,entity_field_retire,entity_field_update,entity_value_write,external_write_through,form_declare,form_submit,inbound_mail_land,io_comment_resolve,io_comment_write,io_import_begin,io_import_finish,io_restore,migrate_purge,migrate_purge_hard,migrate_purge_hard_request,migrate_reclass,my_levels,organization_clear,organization_contents,portal_declare,portal_invitation,portal_invite,portal_invite_accept,portal_principal_bind,promote_table,query_visibility_parity,read_record,read_records,read_records_archived,read_records_by_ids,read_records_matching,record_table,record_write_graph,relation_halves_disagreements,relation_halves_repair,share_grant,subscription_mute,subscription_preview,table_share_outside_accept,table_share_outside_invite,table_share_outside_resend,table_webhook_declare,visibility_as_of,work_approval_decide,work_approval_read,work_approval_request,work_person" },
1788
2178
  { code: "42703", raised_by: "entity_table,portal_declare" },
1789
2179
  { code: "53400", raised_by: "anon_rate_take,delete_cascade_closure,enrich_due,enrich_land,promote_field" },
1790
2180
  { code: "54000", raised_by: "action_run,hub_changed_by,table_decorate,table_from_example,table_webhook_declare" },