@ai-matrx/records 0.57.3 → 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",
@@ -5525,10 +5635,11 @@ function createRecordsClient(config) {
5525
5635
  "pipelineRead"
5526
5636
  );
5527
5637
  },
5528
- async pipelineBoard({ table_id, measure }) {
5638
+ async pipelineBoard({ table_id, measure, filter }) {
5639
+ const asked = filter && stableFilterKey(filter) !== "{}";
5529
5640
  return callDoor(
5530
5641
  DOORS.pipelineBoard,
5531
- { 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 },
5532
5643
  "pipelineBoard"
5533
5644
  );
5534
5645
  },
@@ -5552,6 +5663,13 @@ function createRecordsClient(config) {
5552
5663
  "pipelineMove"
5553
5664
  );
5554
5665
  },
5666
+ async pipelineMoveMany({ moves }) {
5667
+ return callDoor(
5668
+ DOORS.pipelineMoveMany,
5669
+ { p_organization_id: org, p_moves: moves },
5670
+ "pipelineMoveMany"
5671
+ );
5672
+ },
5555
5673
  async pipelineDeclare({ table_id, spec }) {
5556
5674
  return callDoor(
5557
5675
  DOORS.pipelineDeclare,
@@ -5601,6 +5719,18 @@ function createRecordsClient(config) {
5601
5719
  "pipelineGatePreview"
5602
5720
  );
5603
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
+ },
5604
5734
  async ruleMembers({ rule_id }) {
5605
5735
  return callDoor(
5606
5736
  DOORS.ruleMembers,
@@ -5653,6 +5783,13 @@ function createRecordsClient(config) {
5653
5783
  "bookingDeclare"
5654
5784
  );
5655
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
+ },
5656
5793
  async formDeclare(args) {
5657
5794
  return callDoor(
5658
5795
  DOORS.formDeclare,
@@ -5828,11 +5965,33 @@ function createRecordsClient(config) {
5828
5965
  p_tables: args.tables,
5829
5966
  p_portal_id: args.portal_id ?? null,
5830
5967
  p_slug: args.slug ?? null,
5831
- 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 } : {}
5832
5971
  },
5833
5972
  "portalDeclare"
5834
5973
  );
5835
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
+ },
5836
5995
  async portalInvite(args) {
5837
5996
  return callDoor(
5838
5997
  DOORS.portalInvite,
@@ -6031,10 +6190,17 @@ function createRecordsClient(config) {
6031
6190
  "dashboardDeclare"
6032
6191
  );
6033
6192
  },
6034
- async dashboardRun({ dashboard_id, filter }) {
6193
+ async dashboardRun({ dashboard_id, filter, compare, grain }) {
6035
6194
  return callDoor(
6036
6195
  DOORS.dashboardRun,
6037
- { 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
+ },
6038
6204
  "dashboardRun"
6039
6205
  );
6040
6206
  },
@@ -6059,7 +6225,7 @@ function createRecordsClient(config) {
6059
6225
  "dashboardDelete"
6060
6226
  );
6061
6227
  },
6062
- async recordAggregate({ table_id, groupBy, measures, bucket, filter, limit }) {
6228
+ async recordAggregate({ table_id, groupBy, measures, bucket, filter, limit, compare }) {
6063
6229
  return callDoor(
6064
6230
  DOORS.recordAggregate,
6065
6231
  {
@@ -6069,7 +6235,8 @@ function createRecordsClient(config) {
6069
6235
  p_measures: measures ?? [],
6070
6236
  p_bucket: bucket ?? null,
6071
6237
  p_filter: filter ?? {},
6072
- p_limit: limit ?? 200
6238
+ p_limit: limit ?? 200,
6239
+ ...compare ? { p_compare: compare } : {}
6073
6240
  },
6074
6241
  "recordAggregate"
6075
6242
  );
@@ -6797,6 +6964,13 @@ function createRecordsClient(config) {
6797
6964
  "workApprovalDecide"
6798
6965
  );
6799
6966
  },
6967
+ async workDecideMany({ decisions }) {
6968
+ return callDoor(
6969
+ DOORS.workDecideMany,
6970
+ { p_organization_id: org, p_decisions: decisions },
6971
+ "workDecideMany"
6972
+ );
6973
+ },
6800
6974
  async workApprovalRead({ approval_id }) {
6801
6975
  return callDoor(
6802
6976
  DOORS.workApprovalRead,
@@ -7000,6 +7174,69 @@ function createRecordsClient(config) {
7000
7174
  "tableFromExample"
7001
7175
  );
7002
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
+ },
7003
7240
  async autonumberBackfill({ field_id }) {
7004
7241
  return callDoor(
7005
7242
  DOORS.autonumberBackfill,
@@ -7094,27 +7331,6 @@ function useRecordsActor() {
7094
7331
 
7095
7332
  // src/react/hooks.ts
7096
7333
  import { useCallback, useEffect, useMemo as useMemo2, useRef, useState, useSyncExternalStore } from "react";
7097
-
7098
- // src/aggregate.ts
7099
- function measureKey(measure) {
7100
- return measure.op === "count" ? "count" : `${measure.op}_${measure.key ?? ""}`;
7101
- }
7102
- function stableFilterKey(filter) {
7103
- if (!filter) return "{}";
7104
- const keys = Object.keys(filter).sort();
7105
- if (keys.length === 0) return "{}";
7106
- const parts = keys.map((key) => {
7107
- const value = filter[key];
7108
- if (value !== null && typeof value === "object") {
7109
- const window2 = value;
7110
- return `${JSON.stringify(key)}:{"from":${JSON.stringify(window2.from ?? null)},"to":${JSON.stringify(window2.to ?? null)}}`;
7111
- }
7112
- return `${JSON.stringify(key)}:${JSON.stringify(value ?? null)}`;
7113
- });
7114
- return `{${parts.join(",")}}`;
7115
- }
7116
-
7117
- // src/react/hooks.ts
7118
7334
  function useAsync(run, deps) {
7119
7335
  const client = useRecordsClient();
7120
7336
  const [state, setState] = useState(
@@ -7594,6 +7810,88 @@ function useMergeField(key, state) {
7594
7810
  return resolution;
7595
7811
  }
7596
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
+
7597
7895
  // src/vocabulary.ts
7598
7896
  var TABLE_TYPES = [
7599
7897
  "entity",
@@ -7705,6 +8003,7 @@ var FIELD_KINDS = [
7705
8003
  "currency",
7706
8004
  "datetime",
7707
8005
  "email",
8006
+ "entity_reference",
7708
8007
  "formula",
7709
8008
  "json",
7710
8009
  "long_text",
@@ -7743,6 +8042,7 @@ function fieldKindFor(field, kernel) {
7743
8042
  case "list":
7744
8043
  return field.multi ? "multi_select" : "select";
7745
8044
  case "relation": {
8045
+ if (isEntityReferenceConfig(config)) return "entity_reference";
7746
8046
  const parity = String(config["parity"] ?? "");
7747
8047
  if (parity === "attachment" || parity === "member") return parity;
7748
8048
  if (kernel?.file && field.relation_target === kernel.file) return "attachment";
@@ -7760,6 +8060,14 @@ function fieldKindFor(field, kernel) {
7760
8060
  }
7761
8061
  }
7762
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
+ }
7763
8071
  var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
7764
8072
  var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
7765
8073
  function coercionLabel(field) {
@@ -7875,6 +8183,23 @@ function coerceTypedAnswer(field, raw, opts = {}) {
7875
8183
  return {
7876
8184
  refusal: `${label} points at another record, so it is chosen rather than typed. Open it and pick the record.`
7877
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
+ }
7878
8203
  case "formula":
7879
8204
  case "lookup":
7880
8205
  case "rollup":
@@ -7931,6 +8256,10 @@ function publicFormPath(formId) {
7931
8256
  }
7932
8257
 
7933
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
+ }
7934
8263
  function portalPath(slug) {
7935
8264
  return `/portal/c/${slug}`;
7936
8265
  }
@@ -7953,6 +8282,56 @@ function emptyPortalArchiveLine() {
7953
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.";
7954
8283
  }
7955
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
+
7956
8335
  // src/capture-queue.ts
7957
8336
  var DB_NAME = "matrx-capture";
7958
8337
  var DB_VERSION = 1;
@@ -8263,6 +8642,7 @@ export {
8263
8642
  PERMISSION_LEVELS,
8264
8643
  PER_VALUE_ACCESS_WORDS,
8265
8644
  PIPELINE_RULE_KINDS,
8645
+ PORTAL_ACCENTS,
8266
8646
  PORTAL_ARCHIVE_FILTERS,
8267
8647
  REFUSALS_WORTH_REPEATING,
8268
8648
  RELATION_BINDINGS,
@@ -8298,6 +8678,11 @@ export {
8298
8678
  coercionLabel,
8299
8679
  emptyArchiveLine,
8300
8680
  emptyPortalArchiveLine,
8681
+ entityReferenceKinds,
8682
+ entityReferenceKindsOf,
8683
+ entityReferenceWords,
8684
+ entityRefsOf,
8685
+ entityRefsToWrite,
8301
8686
  err,
8302
8687
  exampleSpec,
8303
8688
  exportPageXlsx,
@@ -8308,16 +8693,24 @@ export {
8308
8693
  indexedDbStore,
8309
8694
  isArchiveLane,
8310
8695
  isDecided,
8696
+ isEntityReferenceConfig,
8311
8697
  isOwnOp,
8698
+ isPortalAccent,
8312
8699
  isPortalArchiveFilter,
8313
8700
  isRecordsErr,
8701
+ isRuleFilter,
8314
8702
  isTheChosen,
8703
+ keptGroups,
8704
+ keptOutOfSight,
8705
+ keptTableHref,
8315
8706
  looksLikeAnId,
8316
8707
  mayDrag,
8317
8708
  measureKey,
8318
8709
  memoryStore,
8319
8710
  mintClientKey,
8320
8711
  mintOpId,
8712
+ moveInOrder,
8713
+ offeredAsContext,
8321
8714
  ok,
8322
8715
  openCaptureQueue,
8323
8716
  optionKey,
@@ -8331,12 +8724,15 @@ export {
8331
8724
  rememberedOpCount,
8332
8725
  sameDocument,
8333
8726
  scopeChip,
8727
+ sheetSyncSpec,
8334
8728
  stableFilterKey,
8335
8729
  tableShapeChanged,
8336
8730
  useEntityFieldMutation,
8337
8731
  useEntityFieldRights,
8338
8732
  useEntityFields,
8339
8733
  useEntityRecord,
8734
+ useEntityReferenceKinds,
8735
+ useEntityReferenceWords,
8340
8736
  useEntityTable,
8341
8737
  useFieldMutation,
8342
8738
  useFields,
@@ -8352,6 +8748,8 @@ export {
8352
8748
  useRecordsClient,
8353
8749
  useTable,
8354
8750
  useTableArchive,
8355
- useTables
8751
+ useTables,
8752
+ withRowCounts,
8753
+ withTablePlacement
8356
8754
  };
8357
8755
  //# sourceMappingURL=index.js.map