@ai-matrx/records 0.57.2 → 0.58.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.
@@ -2219,6 +2219,31 @@ function sameDocument(a, b) {
2219
2219
  return JSON.stringify(a) === JSON.stringify(b);
2220
2220
  }
2221
2221
 
2222
+ // src/aggregate.ts
2223
+ function isRuleFilter(filter) {
2224
+ if (!filter || typeof filter !== "object" || Array.isArray(filter)) return false;
2225
+ const node = filter;
2226
+ return typeof node.op === "string" && Array.isArray(node.args);
2227
+ }
2228
+ function measureKey(measure) {
2229
+ return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
2230
+ }
2231
+ function stableFilterKey(filter) {
2232
+ if (!filter) return "{}";
2233
+ if (isRuleFilter(filter)) return JSON.stringify(filter);
2234
+ const keys = Object.keys(filter).sort();
2235
+ if (keys.length === 0) return "{}";
2236
+ const parts = keys.map((key) => {
2237
+ const value = filter[key];
2238
+ if (value !== null && typeof value === "object") {
2239
+ const window2 = value;
2240
+ return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
2241
+ }
2242
+ return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
2243
+ });
2244
+ return `{${parts.join(",")}}`;
2245
+ }
2246
+
2222
2247
  // src/ops.ts
2223
2248
  var REMEMBER_MS = 3e4;
2224
2249
  var REMEMBER_MAX = 500;
@@ -2374,6 +2399,51 @@ function recordChangeTrigger(tableId, options = {}) {
2374
2399
  ...options.changedFields && options.changedFields.length > 0 ? { changed_fields: options.changedFields } : {}
2375
2400
  };
2376
2401
  }
2402
+ function sheetSyncSpec(args) {
2403
+ const [header = [], ...body] = args.cells;
2404
+ const columns = header.map((c, i) => {
2405
+ const name = c == null ? "" : String(c).trim();
2406
+ return name === "" ? `Column ${i + 1}` : name;
2407
+ });
2408
+ if (columns.length === 0) {
2409
+ throw new Error("The sheet has no header row, so there are no columns to sync. Nothing was sent.");
2410
+ }
2411
+ const seenCol = /* @__PURE__ */ new Set();
2412
+ for (const c of columns) {
2413
+ if (seenCol.has(c)) throw new Error(`The sheet has two columns named "${c}". Rename one and sync again.`);
2414
+ seenCol.add(c);
2415
+ }
2416
+ const refIdx = args.refColumn == null ? -1 : columns.indexOf(args.refColumn);
2417
+ if (args.refColumn != null && refIdx < 0) {
2418
+ throw new Error(`The id column "${args.refColumn}" is not in the sheet's header. Nothing was sent.`);
2419
+ }
2420
+ const seen = /* @__PURE__ */ new Set();
2421
+ const rows = [];
2422
+ body.forEach((cells, i) => {
2423
+ if (cells.every((v) => v == null || String(v).trim() === "")) return;
2424
+ const ref = refIdx >= 0 ? String(cells[refIdx] ?? "").trim() : `row-${i + 2}`;
2425
+ if (ref === "") throw new Error(`Sheet row ${i + 2} has no value in the id column "${args.refColumn}".`);
2426
+ if (seen.has(ref)) throw new Error(`Two sheet rows share the id "${ref}"; each row needs its own.`);
2427
+ seen.add(ref);
2428
+ const values = {};
2429
+ columns.forEach((c, j) => {
2430
+ const v = cells[j];
2431
+ values[c] = v === void 0 || v === "" ? null : v;
2432
+ });
2433
+ rows.push({ ref, values });
2434
+ });
2435
+ return { source: args.source, table_name: args.tableName, columns, rows };
2436
+ }
2437
+ function withRowCounts(tables, counts) {
2438
+ if (!counts) return [...tables];
2439
+ const byId = new Map(counts.map((c) => [c.table_id, Number(c.visible_rows)]));
2440
+ return tables.map((t) => byId.has(t.id) ? { ...t, row_count: byId.get(t.id) } : t);
2441
+ }
2442
+ function moveInOrder(order, moved, toIndex) {
2443
+ const rest = order.filter((id) => id !== moved);
2444
+ const at = Math.max(0, Math.min(Math.trunc(toIndex), rest.length));
2445
+ return [...rest.slice(0, at), moved, ...rest.slice(at)];
2446
+ }
2377
2447
 
2378
2448
  // src/store.generated.ts
2379
2449
  var ACTOR_VOCABULARY = ["user", "agent", "system"];
@@ -3049,6 +3119,7 @@ var STORE_DOORS = [
3049
3119
  { name: "table_work_kind", args: "p_organization_id uuid, p_table_id uuid", returns: "text", security: "invoker" },
3050
3120
  { 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" },
3051
3121
  { name: "tables_described_without_asking", args: "", returns: "TABLE(function_name text, identity_args text, why text)", security: "invoker" },
3122
+ { 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" },
3052
3123
  { 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" },
3053
3124
  { name: "trg_associations_bump_visibility", args: "", returns: "trigger", security: "definer" },
3054
3125
  { name: "undeclared_keys", args: "p_organization_id uuid, p_table_id uuid, p_data jsonb", returns: "text[]", security: "invoker" },
@@ -3073,6 +3144,7 @@ var STORE_DOORS = [
3073
3144
  { 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" },
3074
3145
  { name: "visible_record_ids", args: "p_user_id uuid, p_required permission_level DEFAULT 'viewer'::permission_level", returns: "TABLE(id uuid)", security: "definer" },
3075
3146
  { 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" },
3147
+ { name: "where_id_opens", args: "p_id uuid", returns: "jsonb", security: "definer" },
3076
3148
  { name: "widget_kernel_id", args: "", returns: "uuid", security: "invoker" },
3077
3149
  { name: "withheld_marker", args: "p_mask jsonb, p_key text", returns: "jsonb", security: "invoker" },
3078
3150
  { name: "withheld_sentence", args: "p_mask jsonb, p_key text", returns: "text", security: "invoker" },
@@ -3179,7 +3251,7 @@ var STORE_REFUSAL_CODES = [
3179
3251
  { 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" },
3180
3252
  { 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" },
3181
3253
  { code: "40001", raised_by: "has_visibility_at" },
3182
- { 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" },
3254
+ { 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" },
3183
3255
  { code: "42703", raised_by: "entity_table,portal_declare" },
3184
3256
  { code: "53400", raised_by: "anon_rate_take,delete_cascade_closure,enrich_due,enrich_land,promote_field" },
3185
3257
  { code: "54000", raised_by: "action_run,hub_changed_by,table_decorate,table_from_example,table_webhook_declare" },
@@ -4240,6 +4312,20 @@ var OWED_BY = {
4240
4312
  table_webhook_archive: "GRID-PRIMITIVES G4 \u2014 gridprim_every_row_change_reaches_the_webhook.sql (chair's production apply)",
4241
4313
  table_from_example: "GRID-PRIMITIVES G5 \u2014 gridprim_a_table_can_start_from_a_real_example.sql (chair's production apply)",
4242
4314
  autonumber_backfill: "GRID-PRIMITIVES G5 \u2014 gridprim_a_table_can_start_from_a_real_example.sql (chair's production apply)",
4315
+ table_list_everywhere: "GRID-PRIMITIVES G9 \u2014 gridprim_every_picker_lists_both_stores.sql (chair's production apply)",
4316
+ table_sync: "GRID-PRIMITIVES G10 \u2014 gridprim_a_synced_sheet_is_one_table_that_refreshes.sql (chair's production apply)",
4317
+ scope_table_provision: "GRID-PRIMITIVES G11 \u2014 gridprim_a_scope_provisions_its_table_in_the_store.sql (chair's production apply)",
4318
+ table_row_counts: "GRID-PRIMITIVES G12 \u2014 gridprim_every_table_counts_its_rows_and_every_row_shows_its_header.sql + its grant file (chair's production apply)",
4319
+ view_record_order_set: "GRID-PRIMITIVES G13 \u2014 gridprim_a_view_keeps_the_order_a_person_dragged.sql + its grant file (chair's production apply)",
4320
+ table_share_outside_grant: "GRID-PRIMITIVES G14 \u2014 gridprim_a_known_outsider_is_granted_at_once.sql + gridprim_a_signed_in_person_may_share_with_a_known_outsider.sql (chair's production apply)",
4321
+ read_records_in_view_order: "GRID-PRIMITIVES G13 \u2014 gridprim_a_view_keeps_the_order_a_person_dragged.sql + its grant file (chair's production apply)",
4322
+ rule_members_visible: "S2-PRIME FILTER-GROUPS \u2014 filtergroups_a_views_nested_question_is_one_where_clause.sql + its grant file (chair's production apply)",
4323
+ record_headers: "GRID-PRIMITIVES G12 \u2014 gridprim_every_table_counts_its_rows_and_every_row_shows_its_header.sql + its grant file (chair's production apply)",
4324
+ pipeline_move_many: "UI-CHAMPIONS S4 \u2014 uichamp_s4_a_selection_moves_and_decides_in_one_call.sql + its grant file (chair's production apply)",
4325
+ work_decide_many: "UI-CHAMPIONS S4 \u2014 uichamp_s4_a_selection_moves_and_decides_in_one_call.sql + its grant file (chair's production apply)",
4326
+ portal_form: "UI-CHAMPIONS S6 \u2014 uichamp_s6_a_portal_carries_its_look_and_its_forms.sql + uichamp_s6_a_signed_in_person_may_open_a_portals_look_and_forms.sql (chair's production apply)",
4327
+ portal_form_submit: "UI-CHAMPIONS S6 \u2014 uichamp_s6_a_portal_carries_its_look_and_its_forms.sql + uichamp_s6_a_signed_in_person_may_open_a_portals_look_and_forms.sql (chair's production apply)",
4328
+ form_preview_asks: "FORMS-FIX-1 \u2014 formsfix1_the_owners_preview_asks_the_store_the_same_question.sql + its grant file (chair's production apply)",
4243
4329
  rule_declare: "W1-RULE \u2014 `custom.record_write` writes `data_class = 'record'`, so the ONE write door cannot make a Rule record; declaring a Rule (and therefore a DOOR-18 subscription) needs a door of its own"
4244
4330
  };
4245
4331
  var TRUST_SCHEMA = "iam";
@@ -4443,6 +4529,8 @@ var DOORS = {
4443
4529
  pipelineBoard: "pipeline_board",
4444
4530
  pipelineTransitionRefusal: "pipeline_transition_refusal",
4445
4531
  pipelineMove: "pipeline_move",
4532
+ /** Lane S4: many cards, one call, a verdict and a version per card. */
4533
+ pipelineMoveMany: "pipeline_move_many",
4446
4534
  tableStageField: "table_stage_field",
4447
4535
  // STAGE-RULES. A gate can refuse or it can hand the change to the approvals queue; the
4448
4536
  // closed list is the store's, the knob is the organization's, and what is WAITING on a
@@ -4453,6 +4541,7 @@ var DOORS = {
4453
4541
  pipelinePending: "pipeline_pending",
4454
4542
  pipelineGatePreview: "pipeline_gate_preview",
4455
4543
  ruleMembers: "rule_members",
4544
+ ruleMembersVisible: "rule_members_visible",
4456
4545
  ruleMembership: "rule_membership",
4457
4546
  ruleRun: "rule_run",
4458
4547
  ruleEval: "rule_eval",
@@ -4467,6 +4556,12 @@ var DOORS = {
4467
4556
  // are server-lane doors and a browser never calls them.
4468
4557
  forms: "forms",
4469
4558
  formDeclare: "form_declare",
4559
+ /**
4560
+ * WHICH QUESTIONS THE OWNER'S PREVIEW ASKS NEXT (lane FORMS-FIX-1). The same store answer a
4561
+ * stranger's form gets (`custom._form_questions_asked` → `custom.rule_eval`), behind the
4562
+ * organization wall and the caller's own rung on the Table — never `rule_eval` from a browser.
4563
+ */
4564
+ formPreviewAsks: "form_preview_asks",
4470
4565
  // PRODUCTS row 14 — "let clients book a 30-minute consult". A booking page IS an
4471
4566
  // `anon_form`, so it is opened and closed by `anonPublish` like any other; this is
4472
4567
  // the owner's list of them with the numbers that make it worth opening. The
@@ -4526,6 +4621,11 @@ var DOORS = {
4526
4621
  portalInvite: "portal_invite",
4527
4622
  portalRevoke: "portal_revoke",
4528
4623
  portalPreview: "portal_preview",
4624
+ // S6 — the CLIENT's two form doors. They are store doors (schema `custom`), called by a
4625
+ // signed-in portal principal, and each refuses anybody who is not a live principal of that
4626
+ // live portal — so, unlike the sign-in pair above, they belong in this table.
4627
+ portalForm: "portal_form",
4628
+ portalFormSubmit: "portal_form_submit",
4529
4629
  // ORG-CLEANUP, 2026-09-22 — A PORTAL IS ARCHIVED, NEVER DELETED.
4530
4630
  // `portals` keeps its signature and HIDES archived portals (the law's default
4531
4631
  // is to hide); `list_portals(org, 'active'|'archived'|'all')` is the
@@ -4721,6 +4821,8 @@ var DOORS = {
4721
4821
  workApprovalRequest: "work_approval_request",
4722
4822
  workApprovalMayDecide: "work_approval_may_decide",
4723
4823
  workApprovalDecide: "work_approval_decide",
4824
+ /** Lane S4: many inbox decisions, one call, a verdict per item. */
4825
+ workDecideMany: "work_decide_many",
4724
4826
  workApprovalRead: "work_approval_read",
4725
4827
  // PRODUCTS row 13 — checklists and SOP runs.
4726
4828
  checklistRefusal: "checklist_refusal",
@@ -4746,6 +4848,14 @@ var DOORS = {
4746
4848
  tableFromExample: "table_from_example",
4747
4849
  autonumberBackfill: "autonumber_backfill",
4748
4850
  ioExport: "io_export",
4851
+ tableListEverywhere: "table_list_everywhere",
4852
+ tableSync: "table_sync",
4853
+ scopeTableProvision: "scope_table_provision",
4854
+ tableRowCounts: "table_row_counts",
4855
+ recordHeaders: "record_headers",
4856
+ viewRecordOrderSet: "view_record_order_set",
4857
+ readRecordsInViewOrder: "read_records_in_view_order",
4858
+ tableShareOutsideGrant: "table_share_outside_grant",
4749
4859
  // Declared here BECAUSE they do not exist yet: naming them is what makes the
4750
4860
  // absence visible to `pnpm check:store-registry` and to the suite.
4751
4861
  metadataSearch: "metadata_search",
@@ -5145,6 +5255,10 @@ function createRecordsClient(config) {
5145
5255
  })
5146
5256
  );
5147
5257
  },
5258
+ async tableFacts() {
5259
+ if (!doorExists(DOORS.tableFacts)) return ok(null);
5260
+ return callDoor(DOORS.tableFacts, { p_organization_id: org }, "tableFacts");
5261
+ },
5148
5262
  async tableArchived({ table_id }) {
5149
5263
  const kernel = await callDoor(DOORS.tableKernelId, {}, "tableArchived.kernel");
5150
5264
  if (!kernel.ok) return err(kernel.error);
@@ -5328,23 +5442,6 @@ function createRecordsClient(config) {
5328
5442
  const { document } = unfold(row.document);
5329
5443
  return { ...document, id: row.id, organization_id: org };
5330
5444
  });
5331
- if (doorExists(DOORS.tableFacts)) {
5332
- const facts = await callDoor(
5333
- DOORS.tableFacts,
5334
- { p_organization_id: org },
5335
- "tableList.facts"
5336
- );
5337
- if (facts.ok) {
5338
- const byId = new Map((facts.data ?? []).map((f) => [f.table_id, f]));
5339
- const me = config.actor.user_id ?? null;
5340
- for (const table of tables) {
5341
- const fact = byId.get(table.id);
5342
- if (!fact) continue;
5343
- table.visibility = fact.visibility;
5344
- if (fact.mine && me) table.created_by = me;
5345
- }
5346
- }
5347
- }
5348
5445
  tables.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
5349
5446
  return ok(tables);
5350
5447
  },
@@ -5538,10 +5635,11 @@ function createRecordsClient(config) {
5538
5635
  "pipelineRead"
5539
5636
  );
5540
5637
  },
5541
- async pipelineBoard({ table_id, measure }) {
5638
+ async pipelineBoard({ table_id, measure, filter }) {
5639
+ const asked = filter && stableFilterKey(filter) !== "{}";
5542
5640
  return callDoor(
5543
5641
  DOORS.pipelineBoard,
5544
- { p_organization_id: org, p_table_id: table_id, p_measure: measure ?? null },
5642
+ asked ? { p_organization_id: org, p_table_id: table_id, p_measure: measure ?? null, p_filter: filter } : { p_organization_id: org, p_table_id: table_id, p_measure: measure ?? null },
5545
5643
  "pipelineBoard"
5546
5644
  );
5547
5645
  },
@@ -5565,6 +5663,13 @@ function createRecordsClient(config) {
5565
5663
  "pipelineMove"
5566
5664
  );
5567
5665
  },
5666
+ async pipelineMoveMany({ moves }) {
5667
+ return callDoor(
5668
+ DOORS.pipelineMoveMany,
5669
+ { p_organization_id: org, p_moves: moves },
5670
+ "pipelineMoveMany"
5671
+ );
5672
+ },
5568
5673
  async pipelineDeclare({ table_id, spec }) {
5569
5674
  return callDoor(
5570
5675
  DOORS.pipelineDeclare,
@@ -5614,6 +5719,18 @@ function createRecordsClient(config) {
5614
5719
  "pipelineGatePreview"
5615
5720
  );
5616
5721
  },
5722
+ async ruleMembersVisible({ rule_id, limit = 200, offset = 0 }) {
5723
+ const door = await callDoor(
5724
+ DOORS.ruleMembersVisible,
5725
+ { p_organization_id: org, p_rule_id: rule_id, p_limit: limit, p_offset: offset },
5726
+ "ruleMembersVisible"
5727
+ );
5728
+ if (!door.ok) return err(door.error);
5729
+ return ok((door.data ?? []).map((row) => {
5730
+ const { document, hidden } = unfold(row.document);
5731
+ return { id: row.id, document, level: row.level, hidden };
5732
+ }));
5733
+ },
5617
5734
  async ruleMembers({ rule_id }) {
5618
5735
  return callDoor(
5619
5736
  DOORS.ruleMembers,
@@ -5666,6 +5783,13 @@ function createRecordsClient(config) {
5666
5783
  "bookingDeclare"
5667
5784
  );
5668
5785
  },
5786
+ async formPreviewAsks({ table_id, questions, values }) {
5787
+ return callDoor(
5788
+ DOORS.formPreviewAsks,
5789
+ { p_organization_id: org, p_table_id: table_id, p_questions: questions, p_values: values },
5790
+ "formPreviewAsks"
5791
+ );
5792
+ },
5669
5793
  async formDeclare(args) {
5670
5794
  return callDoor(
5671
5795
  DOORS.formDeclare,
@@ -5841,11 +5965,33 @@ function createRecordsClient(config) {
5841
5965
  p_tables: args.tables,
5842
5966
  p_portal_id: args.portal_id ?? null,
5843
5967
  p_slug: args.slug ?? null,
5844
- p_sign_in_method: args.sign_in_method ?? "magic_link"
5968
+ p_sign_in_method: args.sign_in_method ?? "magic_link",
5969
+ // S6: sent only when given, so a caller that says nothing about the look keeps it.
5970
+ ...args.config !== void 0 ? { p_config: args.config } : {}
5845
5971
  },
5846
5972
  "portalDeclare"
5847
5973
  );
5848
5974
  },
5975
+ async portalForm({ portal_id, form_id }) {
5976
+ return callDoor(
5977
+ DOORS.portalForm,
5978
+ { p_organization_id: org, p_portal_id: portal_id, p_form_id: form_id },
5979
+ "portalForm"
5980
+ );
5981
+ },
5982
+ async portalFormSubmit({ portal_id, form_id, answers, client_key }) {
5983
+ return callDoor(
5984
+ DOORS.portalFormSubmit,
5985
+ {
5986
+ p_organization_id: org,
5987
+ p_portal_id: portal_id,
5988
+ p_form_id: form_id,
5989
+ p_payload: answers,
5990
+ p_client_key: client_key ?? null
5991
+ },
5992
+ "portalFormSubmit"
5993
+ );
5994
+ },
5849
5995
  async portalInvite(args) {
5850
5996
  return callDoor(
5851
5997
  DOORS.portalInvite,
@@ -6044,10 +6190,17 @@ function createRecordsClient(config) {
6044
6190
  "dashboardDeclare"
6045
6191
  );
6046
6192
  },
6047
- async dashboardRun({ dashboard_id, filter }) {
6193
+ async dashboardRun({ dashboard_id, filter, compare, grain }) {
6048
6194
  return callDoor(
6049
6195
  DOORS.dashboardRun,
6050
- { p_organization_id: org, p_dashboard_id: dashboard_id, p_filter: filter ?? {} },
6196
+ {
6197
+ p_organization_id: org,
6198
+ p_dashboard_id: dashboard_id,
6199
+ p_filter: filter ?? {},
6200
+ // Sent only when asked, so a caller that never compares calls exactly what it did.
6201
+ ...compare ? { p_compare: compare } : {},
6202
+ ...grain ? { p_grain: grain } : {}
6203
+ },
6051
6204
  "dashboardRun"
6052
6205
  );
6053
6206
  },
@@ -6072,7 +6225,7 @@ function createRecordsClient(config) {
6072
6225
  "dashboardDelete"
6073
6226
  );
6074
6227
  },
6075
- async recordAggregate({ table_id, groupBy, measures, bucket, filter, limit }) {
6228
+ async recordAggregate({ table_id, groupBy, measures, bucket, filter, limit, compare }) {
6076
6229
  return callDoor(
6077
6230
  DOORS.recordAggregate,
6078
6231
  {
@@ -6082,7 +6235,8 @@ function createRecordsClient(config) {
6082
6235
  p_measures: measures ?? [],
6083
6236
  p_bucket: bucket ?? null,
6084
6237
  p_filter: filter ?? {},
6085
- p_limit: limit ?? 200
6238
+ p_limit: limit ?? 200,
6239
+ ...compare ? { p_compare: compare } : {}
6086
6240
  },
6087
6241
  "recordAggregate"
6088
6242
  );
@@ -6810,6 +6964,13 @@ function createRecordsClient(config) {
6810
6964
  "workApprovalDecide"
6811
6965
  );
6812
6966
  },
6967
+ async workDecideMany({ decisions }) {
6968
+ return callDoor(
6969
+ DOORS.workDecideMany,
6970
+ { p_organization_id: org, p_decisions: decisions },
6971
+ "workDecideMany"
6972
+ );
6973
+ },
6813
6974
  async workApprovalRead({ approval_id }) {
6814
6975
  return callDoor(
6815
6976
  DOORS.workApprovalRead,
@@ -7013,6 +7174,69 @@ function createRecordsClient(config) {
7013
7174
  "tableFromExample"
7014
7175
  );
7015
7176
  },
7177
+ async tableListEverywhere() {
7178
+ return callDoor(
7179
+ DOORS.tableListEverywhere,
7180
+ { p_organization_id: org },
7181
+ "tableListEverywhere"
7182
+ );
7183
+ },
7184
+ async tableSync({ home_id, spec }) {
7185
+ return callDoor(
7186
+ DOORS.tableSync,
7187
+ { p_organization_id: org, p_home_id: home_id, p_spec: spec },
7188
+ "tableSync"
7189
+ );
7190
+ },
7191
+ async scopeTableProvision({ home_id, item_id, scope_id }) {
7192
+ return callDoor(
7193
+ DOORS.scopeTableProvision,
7194
+ { p_organization_id: org, p_item_id: item_id, p_scope_id: scope_id, p_home_id: home_id ?? null },
7195
+ "scopeTableProvision"
7196
+ );
7197
+ },
7198
+ async tableRowCounts({ table_ids }) {
7199
+ return callDoor(
7200
+ DOORS.tableRowCounts,
7201
+ { p_organization_id: org, p_table_ids: table_ids },
7202
+ "tableRowCounts"
7203
+ );
7204
+ },
7205
+ async recordHeaders({ ids }) {
7206
+ return callDoor(
7207
+ DOORS.recordHeaders,
7208
+ { p_organization_id: org, p_ids: ids },
7209
+ "recordHeaders"
7210
+ );
7211
+ },
7212
+ async tableShareOutsideGrant({ table_id, person, level }) {
7213
+ return callDoor(
7214
+ DOORS.tableShareOutsideGrant,
7215
+ { p_organization_id: org, p_table_id: table_id, p_person: person, p_level: level ?? "viewer" },
7216
+ "tableShareOutsideGrant"
7217
+ );
7218
+ },
7219
+ async viewRecordOrderSet({ view_id, record_ids }) {
7220
+ return callDoor(
7221
+ DOORS.viewRecordOrderSet,
7222
+ { p_organization_id: org, p_view_id: view_id, p_record_ids: record_ids },
7223
+ "viewRecordOrderSet"
7224
+ );
7225
+ },
7226
+ async readInViewOrder({ view_id, limit, offset }) {
7227
+ const door = await callDoor(
7228
+ DOORS.readRecordsInViewOrder,
7229
+ { p_organization_id: org, p_view_id: view_id, p_by_id: false, p_limit: limit ?? 200, p_offset: offset ?? 0 },
7230
+ "readInViewOrder"
7231
+ );
7232
+ if (!door.ok) return err(door.error);
7233
+ return ok(
7234
+ (door.data ?? []).map((row) => {
7235
+ const { document, hidden } = unfold(row.document);
7236
+ return { id: row.id, document, level: row.level, hidden, position: row.position ?? null };
7237
+ })
7238
+ );
7239
+ },
7016
7240
  async autonumberBackfill({ field_id }) {
7017
7241
  return callDoor(
7018
7242
  DOORS.autonumberBackfill,
@@ -7107,27 +7331,6 @@ function useRecordsActor() {
7107
7331
 
7108
7332
  // src/react/hooks.ts
7109
7333
  import { useCallback, useEffect, useMemo as useMemo2, useRef, useState, useSyncExternalStore } from "react";
7110
-
7111
- // src/aggregate.ts
7112
- function measureKey(measure) {
7113
- return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
7114
- }
7115
- function stableFilterKey(filter) {
7116
- if (!filter) return "{}";
7117
- const keys = Object.keys(filter).sort();
7118
- if (keys.length === 0) return "{}";
7119
- const parts = keys.map((key) => {
7120
- const value = filter[key];
7121
- if (value !== null && typeof value === "object") {
7122
- const window2 = value;
7123
- return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
7124
- }
7125
- return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
7126
- });
7127
- return `{${parts.join(",")}}`;
7128
- }
7129
-
7130
- // src/react/hooks.ts
7131
7334
  function useAsync(run, deps) {
7132
7335
  const client = useRecordsClient();
7133
7336
  const [state, setState] = useState(
@@ -7607,6 +7810,88 @@ function useMergeField(key, state) {
7607
7810
  return resolution;
7608
7811
  }
7609
7812
 
7813
+ // src/react/entity-reference.ts
7814
+ import { useEffect as useEffect2, useState as useState2 } from "react";
7815
+
7816
+ // src/relation.ts
7817
+ 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;
7818
+ function isRefLike(value) {
7819
+ return typeof value === "object" && value !== null && "token" in value && "id" in value;
7820
+ }
7821
+ function entityRefsOf(value) {
7822
+ const items = Array.isArray(value) ? value : value == null ? [] : [value];
7823
+ const out = [];
7824
+ for (const item of items) {
7825
+ if (!isRefLike(item)) continue;
7826
+ const token = String(item.token ?? "").trim().toLowerCase();
7827
+ const id = String(item.id ?? "").trim();
7828
+ if (!token || !ENTITY_REF_ID.test(id)) continue;
7829
+ const label = item.label;
7830
+ out.push({ token, id: id.toLowerCase(), label: typeof label === "string" ? label : null });
7831
+ }
7832
+ return out;
7833
+ }
7834
+ function entityRefsToWrite(value, multi) {
7835
+ const seen = /* @__PURE__ */ new Set();
7836
+ const refs = [];
7837
+ for (const ref of entityRefsOf(value)) {
7838
+ const k = `${ref.token}:${ref.id}`;
7839
+ if (seen.has(k)) continue;
7840
+ seen.add(k);
7841
+ refs.push({ token: ref.token, id: ref.id });
7842
+ }
7843
+ if (multi) return refs;
7844
+ return refs[0] ?? null;
7845
+ }
7846
+ async function entityReferenceKinds(source) {
7847
+ const { data, error } = await source.rpc("entity_reference_kinds", {}, { schema: "custom" });
7848
+ if (error) return err(mapPgError(error, "entityReferenceKinds"));
7849
+ return ok(Array.isArray(data) ? data : []);
7850
+ }
7851
+ async function entityReferenceWords(source, organizationId, refs) {
7852
+ if (refs.length === 0) return ok([]);
7853
+ const { data, error } = await source.rpc(
7854
+ "entity_reference_words",
7855
+ { p_organization_id: organizationId, p_refs: refs.map((r) => ({ token: r.token, id: r.id })) },
7856
+ { schema: "custom" }
7857
+ );
7858
+ if (error) return err(mapPgError(error, "entityReferenceWords"));
7859
+ return ok(entityRefsOf(data));
7860
+ }
7861
+
7862
+ // src/react/entity-reference.ts
7863
+ function useEntityReferenceKinds() {
7864
+ const client = useRecordsClient();
7865
+ const [state, setState] = useState2({ data: null, loading: true, error: null });
7866
+ useEffect2(() => {
7867
+ let live = true;
7868
+ void entityReferenceKinds(client.config.dataSource).then((res) => {
7869
+ if (!live) return;
7870
+ setState(res.ok ? { data: res.data, loading: false, error: null } : { data: null, loading: false, error: res.error });
7871
+ });
7872
+ return () => {
7873
+ live = false;
7874
+ };
7875
+ }, [client]);
7876
+ return state;
7877
+ }
7878
+ function useEntityReferenceWords(refs) {
7879
+ const client = useRecordsClient();
7880
+ const key = refs.map((r) => `${r.token}:${r.id}`).join(",");
7881
+ const [state, setState] = useState2({ data: null, loading: refs.length > 0, error: null });
7882
+ useEffect2(() => {
7883
+ let live = true;
7884
+ void entityReferenceWords(client.config.dataSource, client.config.organizationId, refs).then((res) => {
7885
+ if (!live) return;
7886
+ setState(res.ok ? { data: res.data, loading: false, error: null } : { data: null, loading: false, error: res.error });
7887
+ });
7888
+ return () => {
7889
+ live = false;
7890
+ };
7891
+ }, [client, key]);
7892
+ return state;
7893
+ }
7894
+
7610
7895
  // src/vocabulary.ts
7611
7896
  var TABLE_TYPES = [
7612
7897
  "entity",
@@ -7718,6 +8003,7 @@ var FIELD_KINDS = [
7718
8003
  "currency",
7719
8004
  "datetime",
7720
8005
  "email",
8006
+ "entity_reference",
7721
8007
  "formula",
7722
8008
  "json",
7723
8009
  "long_text",
@@ -7756,6 +8042,7 @@ function fieldKindFor(field, kernel) {
7756
8042
  case "list":
7757
8043
  return field.multi ? "multi_select" : "select";
7758
8044
  case "relation": {
8045
+ if (isEntityReferenceConfig(config)) return "entity_reference";
7759
8046
  const parity = String(config["parity"] ?? "");
7760
8047
  if (parity === "attachment" || parity === "member") return parity;
7761
8048
  if (kernel?.file && field.relation_target === kernel.file) return "attachment";
@@ -7773,6 +8060,14 @@ function fieldKindFor(field, kernel) {
7773
8060
  }
7774
8061
  }
7775
8062
  var UUID_TEXT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8063
+ function isEntityReferenceConfig(config) {
8064
+ const allowed = config?.["allowed_types"];
8065
+ return Array.isArray(allowed) && allowed.length > 0;
8066
+ }
8067
+ function entityReferenceKindsOf(field) {
8068
+ const allowed = field.config?.["allowed_types"];
8069
+ return Array.isArray(allowed) ? allowed.map((k) => String(k).trim().toLowerCase()).filter(Boolean) : [];
8070
+ }
7776
8071
  var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
7777
8072
  var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
7778
8073
  function coercionLabel(field) {
@@ -7888,6 +8183,23 @@ function coerceTypedAnswer(field, raw, opts = {}) {
7888
8183
  return {
7889
8184
  refusal: `${label} points at another record, so it is chosen rather than typed. Open it and pick the record.`
7890
8185
  };
8186
+ // SC-R / P12. An entity reference names a platform thing by kind and id.
8187
+ // An import or an agent may hand over `note:<id>` — or a bare id when the
8188
+ // column allows exactly one kind; the store then decides whether the thing
8189
+ // is there and whether the writer may open it. Words are chosen, not typed.
8190
+ case "entity_reference": {
8191
+ const allowed = entityReferenceKindsOf(field);
8192
+ const [maybeToken, maybeId] = text.includes(":") ? text.split(/:(.*)/s, 2) : ["", text];
8193
+ const token = (maybeToken ?? "").trim().toLowerCase() || (allowed.length === 1 ? allowed[0] : "");
8194
+ const id = (maybeId ?? "").trim();
8195
+ if (token && UUID_TEXT.test(id)) {
8196
+ const ref = { token, id: id.toLowerCase() };
8197
+ return { value: field.multi ? [ref] : ref };
8198
+ }
8199
+ return {
8200
+ refusal: `${label} points at something on the platform, so it is chosen rather than typed. Open it and pick it.`
8201
+ };
8202
+ }
7891
8203
  case "formula":
7892
8204
  case "lookup":
7893
8205
  case "rollup":
@@ -7944,6 +8256,10 @@ function publicFormPath(formId) {
7944
8256
  }
7945
8257
 
7946
8258
  // src/portal.ts
8259
+ var PORTAL_ACCENTS = ["slate", "green", "amber", "red", "blue", "violet", "teal"];
8260
+ function isPortalAccent(word) {
8261
+ return typeof word === "string" && PORTAL_ACCENTS.includes(word);
8262
+ }
7947
8263
  function portalPath(slug) {
7948
8264
  return `/portal/c/${slug}`;
7949
8265
  }
@@ -7966,6 +8282,56 @@ function emptyPortalArchiveLine() {
7966
8282
  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.";
7967
8283
  }
7968
8284
 
8285
+ // src/placement.ts
8286
+ function keptOutOfSight(placement) {
8287
+ return placement?.kept_by_the_app === true;
8288
+ }
8289
+ function offeredAsContext(placement) {
8290
+ return placement?.offered_as_context === true;
8291
+ }
8292
+ function withTablePlacement(tables, facts) {
8293
+ if (!facts) return [...tables];
8294
+ const byId = new Map(facts.map((f) => [f.table_id, f]));
8295
+ return tables.map((table) => {
8296
+ const fact = byId.get(table.id);
8297
+ if (!fact || fact.kept_by_the_app === void 0) return table;
8298
+ return {
8299
+ ...table,
8300
+ kept_by_the_app: fact.kept_by_the_app,
8301
+ kept_for: fact.kept_for ?? null,
8302
+ offered_as_context: fact.offered_as_context ?? false,
8303
+ keeper_group: fact.keeper_group ?? null,
8304
+ keeper_says: fact.keeper_says ?? null,
8305
+ used_in_kind: fact.used_in_kind ?? null,
8306
+ used_in_id: fact.used_in_id ?? null,
8307
+ used_in_table_id: fact.used_in_table_id ?? null
8308
+ };
8309
+ });
8310
+ }
8311
+ function keptGroups(tables) {
8312
+ const out = [];
8313
+ const at = /* @__PURE__ */ new Map();
8314
+ for (const table of tables) {
8315
+ if (!keptOutOfSight(table)) continue;
8316
+ const group = table.keeper_group?.trim() || "Kept by the app";
8317
+ let i = at.get(group);
8318
+ if (i === void 0) {
8319
+ i = out.length;
8320
+ at.set(group, i);
8321
+ out.push({ group, tables: [] });
8322
+ }
8323
+ out[i].tables.push(table);
8324
+ }
8325
+ return out;
8326
+ }
8327
+ function keptTableHref(fact, hrefs) {
8328
+ if (fact.used_in_kind === "scope" && fact.used_in_id) return hrefs.scope(fact.used_in_id);
8329
+ if (fact.used_in_kind === "table" && (fact.used_in_table_id || fact.used_in_id)) {
8330
+ return hrefs.table(fact.used_in_table_id || fact.used_in_id);
8331
+ }
8332
+ return hrefs.table(fact.table_id);
8333
+ }
8334
+
7969
8335
  // src/capture-queue.ts
7970
8336
  var DB_NAME = "matrx-capture";
7971
8337
  var DB_VERSION = 1;
@@ -8276,6 +8642,7 @@ export {
8276
8642
  PERMISSION_LEVELS,
8277
8643
  PER_VALUE_ACCESS_WORDS,
8278
8644
  PIPELINE_RULE_KINDS,
8645
+ PORTAL_ACCENTS,
8279
8646
  PORTAL_ARCHIVE_FILTERS,
8280
8647
  REFUSALS_WORTH_REPEATING,
8281
8648
  RELATION_BINDINGS,
@@ -8311,6 +8678,11 @@ export {
8311
8678
  coercionLabel,
8312
8679
  emptyArchiveLine,
8313
8680
  emptyPortalArchiveLine,
8681
+ entityReferenceKinds,
8682
+ entityReferenceKindsOf,
8683
+ entityReferenceWords,
8684
+ entityRefsOf,
8685
+ entityRefsToWrite,
8314
8686
  err,
8315
8687
  exampleSpec,
8316
8688
  exportPageXlsx,
@@ -8321,16 +8693,24 @@ export {
8321
8693
  indexedDbStore,
8322
8694
  isArchiveLane,
8323
8695
  isDecided,
8696
+ isEntityReferenceConfig,
8324
8697
  isOwnOp,
8698
+ isPortalAccent,
8325
8699
  isPortalArchiveFilter,
8326
8700
  isRecordsErr,
8701
+ isRuleFilter,
8327
8702
  isTheChosen,
8703
+ keptGroups,
8704
+ keptOutOfSight,
8705
+ keptTableHref,
8328
8706
  looksLikeAnId,
8329
8707
  mayDrag,
8330
8708
  measureKey,
8331
8709
  memoryStore,
8332
8710
  mintClientKey,
8333
8711
  mintOpId,
8712
+ moveInOrder,
8713
+ offeredAsContext,
8334
8714
  ok,
8335
8715
  openCaptureQueue,
8336
8716
  optionKey,
@@ -8344,12 +8724,15 @@ export {
8344
8724
  rememberedOpCount,
8345
8725
  sameDocument,
8346
8726
  scopeChip,
8727
+ sheetSyncSpec,
8347
8728
  stableFilterKey,
8348
8729
  tableShapeChanged,
8349
8730
  useEntityFieldMutation,
8350
8731
  useEntityFieldRights,
8351
8732
  useEntityFields,
8352
8733
  useEntityRecord,
8734
+ useEntityReferenceKinds,
8735
+ useEntityReferenceWords,
8353
8736
  useEntityTable,
8354
8737
  useFieldMutation,
8355
8738
  useFields,
@@ -8365,6 +8748,8 @@ export {
8365
8748
  useRecordsClient,
8366
8749
  useTable,
8367
8750
  useTableArchive,
8368
- useTables
8751
+ useTables,
8752
+ withRowCounts,
8753
+ withTablePlacement
8369
8754
  };
8370
8755
  //# sourceMappingURL=index.js.map