@ai-matrx/records 0.87.31 → 0.92.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +115 -0
  3. package/dist/{client-E0VQ0NPO.d.ts → client-DE9Mq6YX.d.cts} +8 -5
  4. package/dist/{client-C3ZZNfGH.d.cts → client-vJAb7g9r.d.ts} +8 -5
  5. package/dist/core/index.cjs +25 -7
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +23 -12
  8. package/dist/core/index.d.ts +23 -12
  9. package/dist/core/index.js +25 -7
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/data/index.cjs +19 -7
  12. package/dist/data/index.cjs.map +1 -1
  13. package/dist/data/index.d.cts +12 -142
  14. package/dist/data/index.d.ts +12 -142
  15. package/dist/data/index.js +19 -7
  16. package/dist/data/index.js.map +1 -1
  17. package/dist/{declareTable-BK3zufr7.d.cts → declareTable-Btg5bql1.d.cts} +4 -4
  18. package/dist/{declareTable-B12XybRp.d.ts → declareTable-JKt2q34d.d.ts} +4 -4
  19. package/dist/entity/index.cjs +984 -0
  20. package/dist/entity/index.cjs.map +1 -0
  21. package/dist/entity/index.d.cts +36 -0
  22. package/dist/entity/index.d.ts +36 -0
  23. package/dist/entity/index.js +964 -0
  24. package/dist/entity/index.js.map +1 -0
  25. package/dist/entity/react.cjs +1024 -0
  26. package/dist/entity/react.cjs.map +1 -0
  27. package/dist/entity/react.d.cts +511 -0
  28. package/dist/entity/react.d.ts +511 -0
  29. package/dist/entity/react.js +1015 -0
  30. package/dist/entity/react.js.map +1 -0
  31. package/dist/{field-BanTVGK2.d.cts → field-BJBnTyJ0.d.cts} +1 -1
  32. package/dist/{field-BPABOJ2Y.d.ts → field-Ti5iAxFr.d.ts} +1 -1
  33. package/dist/forms/index.cjs +984 -0
  34. package/dist/forms/index.cjs.map +1 -0
  35. package/dist/forms/index.d.cts +333 -0
  36. package/dist/forms/index.d.ts +333 -0
  37. package/dist/forms/index.js +961 -0
  38. package/dist/forms/index.js.map +1 -0
  39. package/dist/index.cjs +10 -1
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +11 -8
  42. package/dist/index.d.ts +11 -8
  43. package/dist/index.js +10 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/memory/index.d.cts +7 -6
  46. package/dist/memory/index.d.ts +7 -6
  47. package/dist/port-CPVBfDxS.d.cts +301 -0
  48. package/dist/port-D2i2o7Lg.d.ts +301 -0
  49. package/dist/portal-Db4CYyHe.d.ts +331 -0
  50. package/dist/portal-DfeY2Boy.d.cts +331 -0
  51. package/dist/{ports-TMWX-t9l.d.ts → ports-BWtxAnXh.d.ts} +2 -2
  52. package/dist/{ports-D8-iuY1P.d.cts → ports-DSU2RsR6.d.cts} +2 -2
  53. package/dist/react/index.cjs +55 -523
  54. package/dist/react/index.cjs.map +1 -1
  55. package/dist/react/index.d.cts +14 -1
  56. package/dist/react/index.d.ts +14 -1
  57. package/dist/react/index.js +52 -527
  58. package/dist/react/index.js.map +1 -1
  59. package/dist/realtime/index.d.cts +2 -2
  60. package/dist/realtime/index.d.ts +2 -2
  61. package/dist/{record-DpB2QgAZ.d.cts → record-BQi92d4-.d.cts} +7 -1
  62. package/dist/{record-DpB2QgAZ.d.ts → record-BQi92d4-.d.ts} +7 -1
  63. package/dist/relation-DMwkmzgN.d.cts +230 -0
  64. package/dist/relation-DlcqV_W5.d.ts +230 -0
  65. package/dist/rule-B8kR0l01.d.ts +250 -0
  66. package/dist/rule-BMViCLmW.d.cts +250 -0
  67. package/dist/search/index.cjs +283 -0
  68. package/dist/search/index.cjs.map +1 -0
  69. package/dist/search/index.d.cts +44 -0
  70. package/dist/search/index.d.ts +44 -0
  71. package/dist/search/index.js +260 -0
  72. package/dist/search/index.js.map +1 -0
  73. package/dist/setup/index.cjs +6854 -0
  74. package/dist/setup/index.cjs.map +1 -0
  75. package/dist/setup/index.d.cts +87 -0
  76. package/dist/setup/index.d.ts +87 -0
  77. package/dist/setup/index.js +6829 -0
  78. package/dist/setup/index.js.map +1 -0
  79. package/dist/{subscription-D7KQBHXN.d.ts → subscription-5CLN3Jug.d.ts} +3 -249
  80. package/dist/{subscription-BD9DnJxX.d.cts → subscription-D-VEzvyy.d.cts} +3 -249
  81. package/dist/{table-kind-BZu8mtND.d.cts → table-kind-CVp43g6h.d.ts} +3 -558
  82. package/dist/{table-kind-xKxTHtrV.d.ts → table-kind-Cm6PXkWx.d.cts} +3 -558
  83. package/dist/templates/index.d.cts +10 -9
  84. package/dist/templates/index.d.ts +10 -9
  85. package/dist/typed-table/index.cjs +19 -7
  86. package/dist/typed-table/index.cjs.map +1 -1
  87. package/dist/typed-table/index.d.cts +10 -7
  88. package/dist/typed-table/index.d.ts +10 -7
  89. package/dist/typed-table/index.js +19 -7
  90. package/dist/typed-table/index.js.map +1 -1
  91. package/dist/{types-CLP5H4Wk.d.cts → types-9Q5SHRDJ.d.cts} +2 -2
  92. package/dist/{types-DmPkWQic.d.ts → types-CfYy-vdV.d.ts} +2 -2
  93. package/dist/{types-C2uspRtf.d.cts → types-Cs2MyE3u.d.cts} +2 -2
  94. package/dist/{types-Dv61jStB.d.ts → types-DF-dxB_l.d.ts} +2 -2
  95. package/dist/types-DTEGaxso.d.cts +139 -0
  96. package/dist/types-Dji5SpFS.d.ts +139 -0
  97. package/dist/use-cases/index.d.cts +5 -5
  98. package/dist/use-cases/index.d.ts +5 -5
  99. package/dist/{validate-CWVToe1T.d.ts → validate-BydKd-Le.d.ts} +1 -1
  100. package/dist/{validate-DEB_mn4Y.d.cts → validate-CqJakTKk.d.cts} +1 -1
  101. package/dist/versions/index.cjs +375 -0
  102. package/dist/versions/index.cjs.map +1 -0
  103. package/dist/versions/index.d.cts +160 -0
  104. package/dist/versions/index.d.ts +160 -0
  105. package/dist/versions/index.js +353 -0
  106. package/dist/versions/index.js.map +1 -0
  107. package/package.json +62 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,103 @@
1
1
  # Changelog — @ai-matrx/records
2
2
 
3
+ ## 0.92.0
4
+
5
+ **A member of an archived organization is told so.** Every store door used to refuse an archived organization with
6
+ "You are not a member of that organization", even to its members. The store now says "This organization is archived"
7
+ (same refusal code `door`, the store's sentence and restore hint carried verbatim) with DETAIL
8
+ `organization_archived:<organization id>`. New `archivedOrganizationId(error)` (from `@ai-matrx/records` and
9
+ `@ai-matrx/records/core`) reads that organization out so a screen can offer "Restore" (the owner's
10
+ `iam.organization_restore` door). A non-member still gets the old sentence and `null` from it.
11
+
12
+ **The withheld note is said once.** For a reader who may not see a confidential measure, `custom.record_aggregate`
13
+ used to repeat the same `_withheld` explanation on every group row; the store now says it once (first row) and
14
+ `liftWithheld` puts it on every row's `row.withheld`, so totals and charts read exactly what they read before.
15
+
16
+ **Consumer action:** none. Hosts that read `measures._withheld` off the raw door (not through the client) must read it
17
+ from the first row.
18
+
19
+ ## 0.92.0
20
+
21
+ **New section `@ai-matrx/records/versions`** — the version rule for any screen a company builds, moved whole
22
+ from `@ai-matrx/records-ui`: `useRecordVersions` / `useTableVersion`, the write doors that carry the version
23
+ the person saw (`updateRecordAt`, `changeRecordsAt`, `movePipelineCardAt(s)`, `restoreAt`,
24
+ `updateOnTheWinningVersion`), `askOnce` (one question in flight per client) and `refusal` (a refusal shaped
25
+ like the store's own). React is the only peer it needs.
26
+
27
+ **`recordRead` answers `version`.** The read asks the record's header in the same round of requests, so a
28
+ safe edit (`recordUpdate` with `expected_version`) needs no second call. `null` when the version could not
29
+ be read (the record still comes back).
30
+
31
+ **Consumer action:** none. Hosts that read a record and then asked `recordHeaders` for its version can use
32
+ `read.data.version` instead.
33
+
34
+ ## 0.91.0
35
+
36
+ **`typedAnswersFor(fields, values)` in `@ai-matrx/records/forms`** — what a stranger typed on a public form or
37
+ booking page, turned into values against the Fields that page published, with one refusal sentence in question
38
+ order (moved whole from the website's `features/unified-data/typedAnswers.ts`). Every public door a company builds
39
+ coerces the same way the grid, the import and our own pages do.
40
+
41
+ **Consumer action:** hosts with their own copy (the website's `typedAnswers.ts`) import
42
+ `typedAnswersFor` from `@ai-matrx/records/forms` and delete the copy.
43
+
44
+ ## 0.90.0
45
+
46
+ **New section `@ai-matrx/records/setup`** — `createRecords({ auth: sessionAuth(supabase), organizationId })`:
47
+ one call that composes the typed client, live changes (one realtime manager over the session), search
48
+ across tables, platform records and a slot for the host's associations store. Signing in is a port
49
+ (`RecordsAuthPort`); an organization API key will plug in as a second mode without changing anything else.
50
+ The README opens with a quick start a company follows to list, create, edit, archive and watch records.
51
+
52
+ **Consumer action:** none.
53
+
54
+ ## 0.89.0
55
+
56
+ **New section `@ai-matrx/records/forms`** — the form document (flows, looks, endings, jumps, embeds,
57
+ the form doors' arguments) and which read a public address does, moved whole from `@ai-matrx/records-ui`
58
+ (`forms.ts`, `publicBinding.ts`), so a company's own screens can build and run forms without ours.
59
+
60
+ **New section `@ai-matrx/records/search`** — `searchRecords(dataSource, { search, tableIds?, organizationIds? })`
61
+ and `listStoreRecordCandidates(dataSource, { search })`: search across every table the person can see
62
+ through `custom.records_search` (never narrowed by the active organization), moved from the website.
63
+
64
+ **Consumer action:** `@ai-matrx/records-ui` users need none. Hosts with their own copy of record search
65
+ (the website's `storeRecordCandidates.ts`) call `listStoreRecordCandidates(supabase, args)` instead.
66
+
67
+ ## 0.88.1
68
+
69
+ **One data engine instance across sections.** `records/react` carried its own private copy of
70
+ `records/data/react` (its `DataPortContext` and host-event bus) because the build copies every module
71
+ into each entry that reaches it. It now imports the published `records/data/react`, as `records/entity/react`
72
+ does, so a `DataPortProvider` mounted from either section is the one every hook reads. The shared-state
73
+ gate gained a census: each module that holds React context or page-local state (data-engine provider,
74
+ seed provider, shape revisions) must live in exactly one section's dist files — proven red on a planted copy.
75
+
76
+ **Consumer action:** none.
77
+
78
+ ## 0.88.0
79
+
80
+ **New sections `@ai-matrx/records/entity` and `@ai-matrx/records/entity/react`** — the former
81
+ `@ai-matrx/entity-data` package, moved in whole (same API, same behaviour): platform records (contacts,
82
+ projects, tasks) through the entity doors, the entity `DataPort`, entity live notices, and the
83
+ token-first hooks (`EntityDataProvider`, `useEntityList`, `useEntityWrite`, …). `records/entity/react`
84
+ imports the published `records/react` and `records/data/react` instead of carrying a private copy, so
85
+ the providers a host mounted are the ones it reads.
86
+
87
+ The shared-state gate (`scripts/check-shared-state.mjs`, run on the packed tarball) now also proves an
88
+ entity write's op id lands in the one op ledger (ESM and CJS) and that `entity/react` holds no private
89
+ copy of the data-engine or seed providers.
90
+
91
+ **Consumer action:** replace `@ai-matrx/entity-data`, `@ai-matrx/entity-data/core` and
92
+ `@ai-matrx/entity-data/port` with `@ai-matrx/records/entity`, and `@ai-matrx/entity-data/react` with
93
+ `@ai-matrx/records/entity/react`; then drop the `@ai-matrx/entity-data` dependency.
94
+
95
+ ## 0.87.32
96
+
97
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
98
+ `git diff npm/records/v0.87.31..npm/records/v0.87.32 -- apps/shared/records`).
99
+ No source changes intended and no consumer action required.
100
+
3
101
  ## 0.87.31
4
102
 
5
103
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/README.md CHANGED
@@ -24,6 +24,69 @@ import {
24
24
  } from "@ai-matrx/records";
25
25
  ```
26
26
 
27
+ ## Quick start — our record store inside your own system
28
+
29
+ Everything a company needs to list, create, edit, archive and watch records from its own screens or
30
+ scripts, with none of ours. One package; each section loads only when imported.
31
+
32
+ ```sh
33
+ npm install @ai-matrx/records @supabase/supabase-js
34
+ ```
35
+
36
+ ```ts
37
+ import { createClient } from "@supabase/supabase-js";
38
+ import { createRecords, sessionAuth } from "@ai-matrx/records/setup";
39
+ import { ensureTable } from "@ai-matrx/records/core";
40
+
41
+ // 1. Sign in. The database address is https://db.matrxserver.com; the publishable key is on your
42
+ // AI Matrx account page. Any supabase-js sign-in works (password, magic link, SSO, server cookies).
43
+ const supabase = createClient("https://db.matrxserver.com", PUBLISHABLE_KEY);
44
+ await supabase.auth.signInWithPassword({ email, password });
45
+
46
+ // 2. One call. You choose the organization new records are saved in; reads span all of yours.
47
+ const matrx = await createRecords({ auth: sessionAuth(supabase), organizationId: (orgs) => orgs[0] ?? null });
48
+
49
+ // 3. A table (found by its slug, or made the first time), then records.
50
+ const table = await ensureTable(matrx.records, { name: "Service callbacks", slug: "service_callbacks",
51
+ fields: [{ key: "caller", label: "Caller", type: "text" }, { key: "phone", label: "Phone", type: "phone" }] });
52
+ if (!table.ok) throw new Error(table.error.message);
53
+ const tableId = table.data; // the table id
54
+
55
+ const created = await matrx.records.recordWrite({ table_id: tableId, data: { caller: "Marcus Delgado", phone: "+14155550178" } });
56
+ const page = await matrx.records.list({ table_id: tableId, limit: 50 }); // { rows, total }
57
+ const record_id = created.ok ? created.data : "";
58
+ const read = await matrx.records.recordRead({ record_id }); // .data.document
59
+ const heads = await matrx.records.recordHeaders({ ids: [record_id] }); // .data[0].version
60
+ const expectedVersion = heads.ok ? heads.data[0]?.version : undefined;
61
+ const edited = await matrx.records.recordUpdate({ record_id, patch: { phone: "+14155550199" }, expectedVersion }); // answers the new version
62
+ // Always send expectedVersion: a colleague's newer edit is then refused (`stale_write`, field by field), never overwritten.
63
+ const archived = await matrx.records.recordDelete({ record_id }); // soft: recordRestore brings it back
64
+
65
+ // 4. Live: other people's changes to this table (this setup's own writes are not echoed back).
66
+ const stop = matrx.watch(tableId, { records: (ids) => console.log("changed", ids) });
67
+
68
+ // 5. Search across every table you can see; platform records (contacts…) through matrx.entities.
69
+ const hits = await matrx.search({ search: "Delgado" });
70
+
71
+ stop(); matrx.close(); // a script closes the live socket before it exits
72
+ ```
73
+
74
+ Every call answers `{ ok: true, data } | { ok: false, error }` — the store's own sentence, never a
75
+ throw. React hosts add `@ai-matrx/records/react` or `@ai-matrx/records/data/react` hooks on the same
76
+ client; `@ai-matrx/records-ui` is our optional set of screens. Organization API keys plug into `auth`
77
+ as a second mode (planned); today `auth` is a signed-in session.
78
+
79
+ | Section | What |
80
+ |---|---|
81
+ | `records/setup` | `createRecords`, `sessionAuth` — the one call above |
82
+ | `records/core` | the typed client over every door (tables, records, versions, views, forms, totals) |
83
+ | `records/realtime` | the live-changes port setup uses |
84
+ | `records/search` | `searchRecords`, `listStoreRecordCandidates` |
85
+ | `records/forms` | the form document, embeds and which read a public address does |
86
+ | `records/entity`, `records/entity/react` | platform records (contacts, projects, tasks) |
87
+ | `records/data`, `records/data/react` | one data port and TanStack hooks over custom tables and entities |
88
+ | `records/react` | provider + hooks over the client |
89
+
27
90
  ## /core — the typed client over the doors
28
91
 
29
92
  ```ts
@@ -278,6 +341,58 @@ const { write, update, remove, conflict } = useRecordMutation();
278
341
  `live` is never quietly false: when realtime is not bound, `liveOffReason` says
279
342
  why in a sentence a screen can show.
280
343
 
344
+ ## /entity — platform records (contacts, projects, tasks) through the same port
345
+ Platform records (contacts, projects, tasks…) through the same `DataPort` an Applet reads a custom
346
+ table with. A thin typed client over the store's entity doors, and a host `DataPort` for entity
347
+ aliases, and token-first React hooks over records' one data engine (`@ai-matrx/records/data/react`).
348
+
349
+ **Reads and writes.** `create` saves into the active organization the host names (none → refused
350
+ `organization_required`), `update` sends the version the person saw (a stale one is refused with the
351
+ store's sentence), `archive` / `restore` move a row to the trash and back. **Lists are live** when the host passes `realtime: createEntityRealtime({ manager })`
352
+ (and `organization`, the active one): other people's changes arrive as `rows:<alias>` events naming the ids.
353
+
354
+ ```ts
355
+ import { createEntityDataPort } from "@ai-matrx/records/entity";
356
+ import { DataPortProvider, hostEvents, useDataRows } from "@ai-matrx/records/data/react";
357
+
358
+ const port = createEntityDataPort({
359
+ dataSource: supabase, // the signed-in person's supabase-js client
360
+ aliases: { contacts: { kind: "entity", token: "party", organizationId } },
361
+ });
362
+
363
+ // in React:
364
+ // <DataPortProvider port={port} events={hostEvents(port)} portKey="applet-1" queryClient={qc}>
365
+ // const { rows, hasMore, loadMore, error } = useDataRows("contacts", { org: organizationId, sort: [{ column: "display_name" }] });
366
+ ```
367
+
368
+ In a React app, name the type and let the engine do the rest:
369
+
370
+ ```tsx
371
+ import { EntityDataProvider, useEntityList, useEntityWrite } from "@ai-matrx/records/entity/react";
372
+
373
+ // once, high: <EntityDataProvider dataSource={supabase} activeOrganizationId={activeOrg}>…
374
+ const contacts = useEntityList("party", { where: [{ column: "last_name", op: "eq", value: "Ellis" }] });
375
+ const write = useEntityWrite("party");
376
+ await write.update(contacts.rows[0]._id, { first_name: "Ada" }); // drawn at once, rolled back on refusal
377
+ ```
378
+
379
+ ### Entries
380
+
381
+ | Entry | What |
382
+ |---|---|
383
+ | `@ai-matrx/records/entity` | `createEntityClient({ dataSource })` → `list`, `get`, `columns`, `insert`, `update`, `archive`, `unarchive`; `toWire`; `mapEntityError`; `createEntityDataPort({ client \| dataSource, aliases, activeOrganizationId })` → `HostDataPort` + `describe`; `createEntityRealtime` |
384
+ | `@ai-matrx/records/entity/react` | `EntityDataProvider`, `useEntityList`, `useEntityColumns`, `useEntityType`, `useEntityRow`, `useEntityWrite` |
385
+
386
+ ### Behaviour worth knowing
387
+
388
+ - The lane defaults to the type's own landing lane, decided by the store; `org` is a filter and never a lock.
389
+ - Paging is keyset: the cursor is the store's `next_after`.
390
+ - A filter or sort the store does not offer yet is refused by name with the store's sentence, never emulated in the browser.
391
+ - Custom fields read from `row._custom` and filter as `_custom.<key>`; a field the person may not read arrives `null`.
392
+ - A total that stopped at the store's count cap is reported as `null` (unknown), never as a wrong number.
393
+
394
+ Local mechanics: `src/entity/FEATURE.md`. Formerly the package `@ai-matrx/entity-data` (deprecated; same API, renamed imports: `/core` and `/port` → `records/entity`, `/react` → `records/entity/react`).
395
+
281
396
  ## Drill-down — declared definitions
282
397
 
283
398
  A server drill (`platform.drill_describe` / `platform.drill_ask`) answers a standard table from
@@ -1,8 +1,11 @@
1
- import { R as RelationCoordinate, C as ChoiceNudge, T as TableArchiveProgress, a as TableRestoreProgress, b as TableDuplicateProgress, c as TableDuplicated, d as TableCopyInProgress, A as ArchiveLane, e as ArchivedRow, f as Table, g as TableHome, h as TableMoved, i as ChoiceUsage, j as ChoiceRehome, k as ChoiceCellBack, l as ChoicesRehomed, m as TableCapacity, F as FieldDrift, n as FieldRetyped, o as RecordsMerged, M as MigrationUndone, p as RelationWordsManyArgs, q as RelationIcons, r as ReverseColumn, s as ReverseLinksManyArgs, t as ReverseLinks, u as ReverseLinksFieldsArgs, S as SourceArchived, v as TableKindFacts, w as TableLevelFacts, x as TableReaderRule, y as TableLevelSet, E as EntityTableFacts, z as EntityFieldsAcross, N as NewEntityFieldDeclaration, B as EntityFieldRights, D as EntityRecordRead, G as EntityBackLinks, H as EntityRowWritten, I as EntityRecordsFound, P as Pipeline, J as PipelineColumn, K as PipelineVerdict, L as PipelineMoveResult, O as PipelineMoveManyItem, Q as PipelineMoveMany, U as PipelineSpec, V as PipelineDeclared, W as StageRuleOnFail, X as StageRuleEnforcement, Y as StagePending, Z as StagePendingOnBoard, _ as PipelineGate, $ as PipelineGatePreview, a0 as FormLookOptions, a1 as ImportPlan, a2 as ImportPolicy, a3 as ImportOpened, a4 as ImportColumnsDeclared, a5 as ImportBatchResult, a6 as ImportFinished, a7 as ImportReport, a8 as ImportRun, a9 as InboundDeclared, aa as InboundAddress, ab as PortalSummary, ac as PortalArchiveFilter, ad as PortalArchiveAnswer, ae as FormArchiveAnswer, af as ArchivedForm, ag as ArchivedChecklistTemplate, ah as TableRowDefaults, ai as TableRowDefaultsPatch, aj as RowDefaultsApplied, ak as RecordRowControls, al as PortalTableRef, am as PortalCard, an as PortalConfigInput, ao as PortalFormSpec, ap as PortalFormAnswer, aq as PortalInvitation, ar as PortalRevocation, as as PortalPreviewRow, at as CaptureSheetSummary, au as CaptureSheetFace, av as CaptureFileRef, aw as CaptureOutcome, ax as EnrichmentSummary, ay as EnrichDeclared, az as EnrichDueRow, aA as EnrichCell, aB as EnrichRun, aC as DrillSource, aD as DrillDefinition, aE as DrillQuestion, aF as DrillAnswer, aG as DrillRowsPage, aH as DocToken, aI as DocUnresolvedToken, aJ as DocTemplateRow, aK as DocRenderRow, aL as DocSignatureRow, aM as RecordHistoryEntry, aN as RecordScopeContext, aO as ConversationScope, aP as ConversationScopeContext, aQ as FieldHistoryEntry, aR as RestorePreview, aS as RestoreResult, aT as CommentThreadRead, aU as CommentWritten, aV as AnonTokenBinding, aW as ChecklistSpec, aX as ChecklistDeclared, aY as ChecklistTemplateSummary, aZ as ChecklistTemplateShape, a_ as ChecklistRunStarted, a$ as ChecklistRunStep, b0 as ChecklistRunSummary, b1 as ChecklistStepCompleted, b2 as WorkState, b3 as WorkGraph, b4 as WorkInstantiation, b5 as WorkTurn, b6 as WorkAssignmentField, b7 as WorkSlotHold, b8 as WorkInboxView, b9 as WorkInboxItem, ba as InboxStateChange, bb as InboxCounts, bc as WorkListFlavour, bd as WorkListItem, be as WorkAssignment, bf as WorkRecordState, bg as WorkApprover, bh as WorkChange, bi as WorkApprovalFiled, bj as WorkApprovalDecision, bk as WorkDecideManyItem, bl as WorkDecideMany, bm as ExternalPrincipalCard, bn as PublishBinding, bo as ResolvedPublishBinding, bp as ResolvedPublishBindingKind, bq as MyLevel, br as DecorationPath, bs as TableDecorations, bt as GridLayout, bu as FormulaParse, bv as FormulaPreview, bw as RowAction, bx as ActionRunResult, by as AutomationSpec, bz as AutomationDeclared, bA as AutomationList, bB as AutomationFlipped, bC as AutomationRuns, bD as RecordEvent, bE as TableWebhookDeclared, bF as TableWebhook, bG as IoExportPage, bH as TableListEntry, bI as DataHomeTable, bJ as TableSyncSpec, bK as TableSyncResult, bL as TableRowCount, bM as RecordHeader, bN as OutsideShareAnswer, bO as ViewOrderSet } from './table-kind-xKxTHtrV.js';
2
- import { d as StoreFilter, F as FormSummary, B as BookingSummary, e as BookingAvailability, f as BookingDeclared, g as FormRoute, h as FormResults, i as DashboardSummary, j as DashboardBlock, R as RecordFilter, k as AggregateCompare, c as AggregateBucket, l as DashboardRunResult, m as StuckRow, b as AggregateMeasure, n as AggregateRow, o as Subscription, M as MySubscription, p as SubscriptionSpec, q as DigestPreview, r as SavedViewRow, s as SavedViewSpecIn, t as RowTemplateRow } from './subscription-D7KQBHXN.js';
3
- import { b as Field, c as FieldPatch, N as NewFieldDeclaration, R as RelationNamesAnswer, d as FieldDuplicated, e as FieldProposal, T as TableProposal, B as ButtonPressed } from './field-BPABOJ2Y.js';
4
- import { f as Timestamp, U as Uuid, g as RecordsErrorCode, h as RecordDocument, i as RecordsResult, j as RecordChange, k as RecordChangeResult, l as RecordGraphWritten, m as RecordRead, n as ReadRow, o as RecordPageSort, p as RecordPage, q as RecordKeysetPage, r as StoredRecord, s as RecordRevision, t as ReadValue, R as RecordsError, W as WriteConflict } from './record-DpB2QgAZ.js';
5
- import { a as RecordsConfig } from './ports-TMWX-t9l.js';
1
+ import { C as ChoiceNudge, T as TableArchiveProgress, a as TableRestoreProgress, b as TableDuplicateProgress, c as TableDuplicated, d as TableCopyInProgress, A as ArchiveLane, e as ArchivedRow, f as Table, g as TableHome, h as TableMoved, i as ChoiceUsage, j as ChoiceRehome, k as ChoiceCellBack, l as ChoicesRehomed, m as TableCapacity, F as FieldDrift, n as FieldRetyped, R as RecordsMerged, M as MigrationUndone, o as TableKindFacts, p as TableLevelFacts, q as TableReaderRule, r as TableLevelSet, E as EntityTableFacts, s as EntityFieldsAcross, N as NewEntityFieldDeclaration, t as EntityFieldRights, u as EntityRecordRead, v as EntityBackLinks, w as EntityRowWritten, x as EntityRecordsFound, P as Pipeline, y as PipelineColumn, z as PipelineVerdict, B as PipelineMoveResult, D as PipelineMoveManyItem, G as PipelineMoveMany, H as PipelineSpec, I as PipelineDeclared, S as StageRuleOnFail, J as StageRuleEnforcement, K as StagePending, L as StagePendingOnBoard, O as PipelineGate, Q as PipelineGatePreview, U as ImportPlan, V as ImportPolicy, W as ImportOpened, X as ImportColumnsDeclared, Y as ImportBatchResult, Z as ImportFinished, _ as ImportReport, $ as ImportRun, a0 as InboundDeclared, a1 as InboundAddress, a2 as FormArchiveAnswer, a3 as ArchivedForm, a4 as ArchivedChecklistTemplate, a5 as TableRowDefaults, a6 as TableRowDefaultsPatch, a7 as RowDefaultsApplied, a8 as RecordRowControls, a9 as CaptureSheetSummary, aa as CaptureSheetFace, ab as CaptureFileRef, ac as CaptureOutcome, ad as EnrichmentSummary, ae as EnrichDeclared, af as EnrichDueRow, ag as EnrichCell, ah as EnrichRun, ai as DrillSource, aj as DrillDefinition, ak as DrillQuestion, al as DrillAnswer, am as DrillRowsPage, an as DocToken, ao as DocUnresolvedToken, ap as DocTemplateRow, aq as DocRenderRow, ar as DocSignatureRow, as as RecordHistoryEntry, at as RecordScopeContext, au as ConversationScope, av as ConversationScopeContext, aw as FieldHistoryEntry, ax as RestorePreview, ay as RestoreResult, az as CommentThreadRead, aA as CommentWritten, aB as AnonTokenBinding, aC as ChecklistSpec, aD as ChecklistDeclared, aE as ChecklistTemplateSummary, aF as ChecklistTemplateShape, aG as ChecklistRunStarted, aH as ChecklistRunStep, aI as ChecklistRunSummary, aJ as ChecklistStepCompleted, aK as WorkState, aL as WorkGraph, aM as WorkInstantiation, aN as WorkTurn, aO as WorkAssignmentField, aP as WorkSlotHold, aQ as WorkInboxView, aR as WorkInboxItem, aS as InboxStateChange, aT as InboxCounts, aU as WorkListFlavour, aV as WorkListItem, aW as WorkAssignment, aX as WorkRecordState, aY as WorkApprover, aZ as WorkChange, a_ as WorkApprovalFiled, a$ as WorkApprovalDecision, b0 as WorkDecideManyItem, b1 as WorkDecideMany, b2 as ExternalPrincipalCard, b3 as PublishBinding, b4 as ResolvedPublishBinding, b5 as ResolvedPublishBindingKind, b6 as MyLevel, b7 as DecorationPath, b8 as TableDecorations, b9 as GridLayout, ba as FormulaParse, bb as FormulaPreview, bc as RowAction, bd as ActionRunResult, be as AutomationSpec, bf as AutomationDeclared, bg as AutomationList, bh as AutomationFlipped, bi as AutomationRuns, bj as RecordEvent, bk as TableWebhookDeclared, bl as TableWebhook, bm as IoExportPage, bn as TableListEntry, bo as DataHomeTable, bp as TableSyncSpec, bq as TableSyncResult, br as TableRowCount, bs as RecordHeader, bt as OutsideShareAnswer, bu as ViewOrderSet } from './table-kind-Cm6PXkWx.cjs';
2
+ import { d as StoreFilter, e as DashboardSummary, f as DashboardBlock, R as RecordFilter, g as AggregateCompare, c as AggregateBucket, h as DashboardRunResult, i as StuckRow, b as AggregateMeasure, j as AggregateRow, k as Subscription, M as MySubscription, l as SubscriptionSpec, m as DigestPreview, n as SavedViewRow, o as SavedViewSpecIn, p as RowTemplateRow } from './subscription-D-VEzvyy.cjs';
3
+ import { R as RelationCoordinate, a as RelationWordsManyArgs, b as RelationIcons, c as ReverseColumn, d as ReverseLinksManyArgs, e as ReverseLinks, f as ReverseLinksFieldsArgs, S as SourceArchived } from './relation-DMwkmzgN.cjs';
4
+ import { b as Field, c as FieldPatch, N as NewFieldDeclaration, R as RelationNamesAnswer, d as FieldDuplicated, e as FieldProposal, T as TableProposal, B as ButtonPressed } from './field-BJBnTyJ0.cjs';
5
+ import { F as FormSummary, B as BookingSummary, a as BookingAvailability, b as BookingDeclared, c as FormRoute, d as FormResults } from './rule-BMViCLmW.cjs';
6
+ import { F as FormLookOptions, P as PortalSummary, a as PortalArchiveFilter, b as PortalArchiveAnswer, c as PortalTableRef, d as PortalCard, e as PortalConfigInput, f as PortalFormSpec, g as PortalFormAnswer, h as PortalInvitation, i as PortalRevocation, j as PortalPreviewRow } from './portal-DfeY2Boy.cjs';
7
+ import { T as Timestamp, U as Uuid, k as RecordsErrorCode, l as RecordDocument, R as RecordsResult, m as RecordChange, n as RecordChangeResult, o as RecordGraphWritten, p as RecordRead, q as ReadRow, r as RecordPageSort, s as RecordPage, t as RecordKeysetPage, u as StoredRecord, v as RecordRevision, w as ReadValue, a as RecordsError, W as WriteConflict } from './record-BQi92d4-.cjs';
8
+ import { a as RecordsConfig } from './ports-DSU2RsR6.cjs';
6
9
 
7
10
  /**
8
11
  * DOOR-8. As-of on EITHER clock, and they are different questions:
@@ -1,8 +1,11 @@
1
- import { R as RelationCoordinate, C as ChoiceNudge, T as TableArchiveProgress, a as TableRestoreProgress, b as TableDuplicateProgress, c as TableDuplicated, d as TableCopyInProgress, A as ArchiveLane, e as ArchivedRow, f as Table, g as TableHome, h as TableMoved, i as ChoiceUsage, j as ChoiceRehome, k as ChoiceCellBack, l as ChoicesRehomed, m as TableCapacity, F as FieldDrift, n as FieldRetyped, o as RecordsMerged, M as MigrationUndone, p as RelationWordsManyArgs, q as RelationIcons, r as ReverseColumn, s as ReverseLinksManyArgs, t as ReverseLinks, u as ReverseLinksFieldsArgs, S as SourceArchived, v as TableKindFacts, w as TableLevelFacts, x as TableReaderRule, y as TableLevelSet, E as EntityTableFacts, z as EntityFieldsAcross, N as NewEntityFieldDeclaration, B as EntityFieldRights, D as EntityRecordRead, G as EntityBackLinks, H as EntityRowWritten, I as EntityRecordsFound, P as Pipeline, J as PipelineColumn, K as PipelineVerdict, L as PipelineMoveResult, O as PipelineMoveManyItem, Q as PipelineMoveMany, U as PipelineSpec, V as PipelineDeclared, W as StageRuleOnFail, X as StageRuleEnforcement, Y as StagePending, Z as StagePendingOnBoard, _ as PipelineGate, $ as PipelineGatePreview, a0 as FormLookOptions, a1 as ImportPlan, a2 as ImportPolicy, a3 as ImportOpened, a4 as ImportColumnsDeclared, a5 as ImportBatchResult, a6 as ImportFinished, a7 as ImportReport, a8 as ImportRun, a9 as InboundDeclared, aa as InboundAddress, ab as PortalSummary, ac as PortalArchiveFilter, ad as PortalArchiveAnswer, ae as FormArchiveAnswer, af as ArchivedForm, ag as ArchivedChecklistTemplate, ah as TableRowDefaults, ai as TableRowDefaultsPatch, aj as RowDefaultsApplied, ak as RecordRowControls, al as PortalTableRef, am as PortalCard, an as PortalConfigInput, ao as PortalFormSpec, ap as PortalFormAnswer, aq as PortalInvitation, ar as PortalRevocation, as as PortalPreviewRow, at as CaptureSheetSummary, au as CaptureSheetFace, av as CaptureFileRef, aw as CaptureOutcome, ax as EnrichmentSummary, ay as EnrichDeclared, az as EnrichDueRow, aA as EnrichCell, aB as EnrichRun, aC as DrillSource, aD as DrillDefinition, aE as DrillQuestion, aF as DrillAnswer, aG as DrillRowsPage, aH as DocToken, aI as DocUnresolvedToken, aJ as DocTemplateRow, aK as DocRenderRow, aL as DocSignatureRow, aM as RecordHistoryEntry, aN as RecordScopeContext, aO as ConversationScope, aP as ConversationScopeContext, aQ as FieldHistoryEntry, aR as RestorePreview, aS as RestoreResult, aT as CommentThreadRead, aU as CommentWritten, aV as AnonTokenBinding, aW as ChecklistSpec, aX as ChecklistDeclared, aY as ChecklistTemplateSummary, aZ as ChecklistTemplateShape, a_ as ChecklistRunStarted, a$ as ChecklistRunStep, b0 as ChecklistRunSummary, b1 as ChecklistStepCompleted, b2 as WorkState, b3 as WorkGraph, b4 as WorkInstantiation, b5 as WorkTurn, b6 as WorkAssignmentField, b7 as WorkSlotHold, b8 as WorkInboxView, b9 as WorkInboxItem, ba as InboxStateChange, bb as InboxCounts, bc as WorkListFlavour, bd as WorkListItem, be as WorkAssignment, bf as WorkRecordState, bg as WorkApprover, bh as WorkChange, bi as WorkApprovalFiled, bj as WorkApprovalDecision, bk as WorkDecideManyItem, bl as WorkDecideMany, bm as ExternalPrincipalCard, bn as PublishBinding, bo as ResolvedPublishBinding, bp as ResolvedPublishBindingKind, bq as MyLevel, br as DecorationPath, bs as TableDecorations, bt as GridLayout, bu as FormulaParse, bv as FormulaPreview, bw as RowAction, bx as ActionRunResult, by as AutomationSpec, bz as AutomationDeclared, bA as AutomationList, bB as AutomationFlipped, bC as AutomationRuns, bD as RecordEvent, bE as TableWebhookDeclared, bF as TableWebhook, bG as IoExportPage, bH as TableListEntry, bI as DataHomeTable, bJ as TableSyncSpec, bK as TableSyncResult, bL as TableRowCount, bM as RecordHeader, bN as OutsideShareAnswer, bO as ViewOrderSet } from './table-kind-BZu8mtND.cjs';
2
- import { d as StoreFilter, F as FormSummary, B as BookingSummary, e as BookingAvailability, f as BookingDeclared, g as FormRoute, h as FormResults, i as DashboardSummary, j as DashboardBlock, R as RecordFilter, k as AggregateCompare, c as AggregateBucket, l as DashboardRunResult, m as StuckRow, b as AggregateMeasure, n as AggregateRow, o as Subscription, M as MySubscription, p as SubscriptionSpec, q as DigestPreview, r as SavedViewRow, s as SavedViewSpecIn, t as RowTemplateRow } from './subscription-BD9DnJxX.cjs';
3
- import { b as Field, c as FieldPatch, N as NewFieldDeclaration, R as RelationNamesAnswer, d as FieldDuplicated, e as FieldProposal, T as TableProposal, B as ButtonPressed } from './field-BanTVGK2.cjs';
4
- import { f as Timestamp, U as Uuid, g as RecordsErrorCode, h as RecordDocument, i as RecordsResult, j as RecordChange, k as RecordChangeResult, l as RecordGraphWritten, m as RecordRead, n as ReadRow, o as RecordPageSort, p as RecordPage, q as RecordKeysetPage, r as StoredRecord, s as RecordRevision, t as ReadValue, R as RecordsError, W as WriteConflict } from './record-DpB2QgAZ.cjs';
5
- import { a as RecordsConfig } from './ports-D8-iuY1P.cjs';
1
+ import { C as ChoiceNudge, T as TableArchiveProgress, a as TableRestoreProgress, b as TableDuplicateProgress, c as TableDuplicated, d as TableCopyInProgress, A as ArchiveLane, e as ArchivedRow, f as Table, g as TableHome, h as TableMoved, i as ChoiceUsage, j as ChoiceRehome, k as ChoiceCellBack, l as ChoicesRehomed, m as TableCapacity, F as FieldDrift, n as FieldRetyped, R as RecordsMerged, M as MigrationUndone, o as TableKindFacts, p as TableLevelFacts, q as TableReaderRule, r as TableLevelSet, E as EntityTableFacts, s as EntityFieldsAcross, N as NewEntityFieldDeclaration, t as EntityFieldRights, u as EntityRecordRead, v as EntityBackLinks, w as EntityRowWritten, x as EntityRecordsFound, P as Pipeline, y as PipelineColumn, z as PipelineVerdict, B as PipelineMoveResult, D as PipelineMoveManyItem, G as PipelineMoveMany, H as PipelineSpec, I as PipelineDeclared, S as StageRuleOnFail, J as StageRuleEnforcement, K as StagePending, L as StagePendingOnBoard, O as PipelineGate, Q as PipelineGatePreview, U as ImportPlan, V as ImportPolicy, W as ImportOpened, X as ImportColumnsDeclared, Y as ImportBatchResult, Z as ImportFinished, _ as ImportReport, $ as ImportRun, a0 as InboundDeclared, a1 as InboundAddress, a2 as FormArchiveAnswer, a3 as ArchivedForm, a4 as ArchivedChecklistTemplate, a5 as TableRowDefaults, a6 as TableRowDefaultsPatch, a7 as RowDefaultsApplied, a8 as RecordRowControls, a9 as CaptureSheetSummary, aa as CaptureSheetFace, ab as CaptureFileRef, ac as CaptureOutcome, ad as EnrichmentSummary, ae as EnrichDeclared, af as EnrichDueRow, ag as EnrichCell, ah as EnrichRun, ai as DrillSource, aj as DrillDefinition, ak as DrillQuestion, al as DrillAnswer, am as DrillRowsPage, an as DocToken, ao as DocUnresolvedToken, ap as DocTemplateRow, aq as DocRenderRow, ar as DocSignatureRow, as as RecordHistoryEntry, at as RecordScopeContext, au as ConversationScope, av as ConversationScopeContext, aw as FieldHistoryEntry, ax as RestorePreview, ay as RestoreResult, az as CommentThreadRead, aA as CommentWritten, aB as AnonTokenBinding, aC as ChecklistSpec, aD as ChecklistDeclared, aE as ChecklistTemplateSummary, aF as ChecklistTemplateShape, aG as ChecklistRunStarted, aH as ChecklistRunStep, aI as ChecklistRunSummary, aJ as ChecklistStepCompleted, aK as WorkState, aL as WorkGraph, aM as WorkInstantiation, aN as WorkTurn, aO as WorkAssignmentField, aP as WorkSlotHold, aQ as WorkInboxView, aR as WorkInboxItem, aS as InboxStateChange, aT as InboxCounts, aU as WorkListFlavour, aV as WorkListItem, aW as WorkAssignment, aX as WorkRecordState, aY as WorkApprover, aZ as WorkChange, a_ as WorkApprovalFiled, a$ as WorkApprovalDecision, b0 as WorkDecideManyItem, b1 as WorkDecideMany, b2 as ExternalPrincipalCard, b3 as PublishBinding, b4 as ResolvedPublishBinding, b5 as ResolvedPublishBindingKind, b6 as MyLevel, b7 as DecorationPath, b8 as TableDecorations, b9 as GridLayout, ba as FormulaParse, bb as FormulaPreview, bc as RowAction, bd as ActionRunResult, be as AutomationSpec, bf as AutomationDeclared, bg as AutomationList, bh as AutomationFlipped, bi as AutomationRuns, bj as RecordEvent, bk as TableWebhookDeclared, bl as TableWebhook, bm as IoExportPage, bn as TableListEntry, bo as DataHomeTable, bp as TableSyncSpec, bq as TableSyncResult, br as TableRowCount, bs as RecordHeader, bt as OutsideShareAnswer, bu as ViewOrderSet } from './table-kind-CVp43g6h.js';
2
+ import { d as StoreFilter, e as DashboardSummary, f as DashboardBlock, R as RecordFilter, g as AggregateCompare, c as AggregateBucket, h as DashboardRunResult, i as StuckRow, b as AggregateMeasure, j as AggregateRow, k as Subscription, M as MySubscription, l as SubscriptionSpec, m as DigestPreview, n as SavedViewRow, o as SavedViewSpecIn, p as RowTemplateRow } from './subscription-5CLN3Jug.js';
3
+ import { R as RelationCoordinate, a as RelationWordsManyArgs, b as RelationIcons, c as ReverseColumn, d as ReverseLinksManyArgs, e as ReverseLinks, f as ReverseLinksFieldsArgs, S as SourceArchived } from './relation-DlcqV_W5.js';
4
+ import { b as Field, c as FieldPatch, N as NewFieldDeclaration, R as RelationNamesAnswer, d as FieldDuplicated, e as FieldProposal, T as TableProposal, B as ButtonPressed } from './field-Ti5iAxFr.js';
5
+ import { F as FormSummary, B as BookingSummary, a as BookingAvailability, b as BookingDeclared, c as FormRoute, d as FormResults } from './rule-B8kR0l01.js';
6
+ import { F as FormLookOptions, P as PortalSummary, a as PortalArchiveFilter, b as PortalArchiveAnswer, c as PortalTableRef, d as PortalCard, e as PortalConfigInput, f as PortalFormSpec, g as PortalFormAnswer, h as PortalInvitation, i as PortalRevocation, j as PortalPreviewRow } from './portal-Db4CYyHe.js';
7
+ import { T as Timestamp, U as Uuid, k as RecordsErrorCode, l as RecordDocument, R as RecordsResult, m as RecordChange, n as RecordChangeResult, o as RecordGraphWritten, p as RecordRead, q as ReadRow, r as RecordPageSort, s as RecordPage, t as RecordKeysetPage, u as StoredRecord, v as RecordRevision, w as ReadValue, a as RecordsError, W as WriteConflict } from './record-BQi92d4-.js';
8
+ import { a as RecordsConfig } from './ports-BWtxAnXh.js';
6
9
 
7
10
  /**
8
11
  * DOOR-8. As-of on EITHER clock, and they are different questions:
@@ -120,6 +120,7 @@ __export(core_exports, {
120
120
  announceRecordChanged: () => announceRecordChanged,
121
121
  announceTableStructureChanged: () => announceTableStructureChanged,
122
122
  archivedByLine: () => archivedByLine,
123
+ archivedOrganizationId: () => archivedOrganizationId,
123
124
  archivedWhen: () => archivedWhen,
124
125
  asChoiceNudge: () => asChoiceNudge,
125
126
  asPermissionLevel: () => asPermissionLevel,
@@ -1627,9 +1628,18 @@ function stableFilterKey(filter) {
1627
1628
  return `{${parts.join(",")}}`;
1628
1629
  }
1629
1630
  function liftWithheld(rows2) {
1631
+ let shared;
1632
+ for (const row of rows2) {
1633
+ const m = (row.measures ?? {})["_withheld"];
1634
+ if (m !== void 0 && m !== null && typeof m === "object") {
1635
+ shared = m;
1636
+ break;
1637
+ }
1638
+ }
1630
1639
  return rows2.map((row) => {
1631
1640
  const measures = row.measures ?? {};
1632
- const marker = measures["_withheld"];
1641
+ const own = measures["_withheld"];
1642
+ const marker = own !== void 0 && own !== null && typeof own === "object" ? own : shared;
1633
1643
  const rowCount = row.row_count ?? 0;
1634
1644
  if (marker === void 0 || marker === null || typeof marker !== "object") {
1635
1645
  return rowCount === row.row_count ? row : { ...row, row_count: rowCount };
@@ -2472,6 +2482,11 @@ function staleWriteDetail(error) {
2472
2482
  contested_fields: detail.contested_fields ?? {}
2473
2483
  };
2474
2484
  }
2485
+ function archivedOrganizationId(error) {
2486
+ if (error.code !== "door" || typeof error.detail !== "string") return null;
2487
+ const m = /^organization_archived:([0-9a-f-]{36})$/i.exec(error.detail.trim());
2488
+ return m ? m[1] : null;
2489
+ }
2475
2490
 
2476
2491
  // src/relation.ts
2477
2492
  var import_uuid4 = require("@ai-matrx/kit/uuid");
@@ -6895,12 +6910,15 @@ function createRecordsClient(given) {
6895
6910
  () => true
6896
6911
  );
6897
6912
  }
6898
- const door = await callDoor(
6899
- DOORS.readRecord,
6900
- { p_organization_id: org, p_record_id: record_id },
6901
- "recordRead"
6902
- );
6913
+ const [door, header] = await Promise.all([
6914
+ callDoor(DOORS.readRecord, { p_organization_id: org, p_record_id: record_id }, "recordRead"),
6915
+ // A version that cannot be read never takes the record away: it comes back as null.
6916
+ callDoor(DOORS.recordHeaders, { p_organization_id: org, p_ids: [record_id] }, "recordRead.version").catch(
6917
+ () => null
6918
+ )
6919
+ ]);
6903
6920
  if (!door.ok) return err(door.error);
6921
+ const version = header && header.ok ? header.data?.find((h) => h.id === record_id)?.version ?? null : null;
6904
6922
  const { document, hidden } = unfold(door.data);
6905
6923
  let computed = [];
6906
6924
  let computedUnavailable = null;
@@ -6913,7 +6931,7 @@ function createRecordsClient(given) {
6913
6931
  if (provenance.ok) computed = provenance.data ?? [];
6914
6932
  else computedUnavailable = provenance.error;
6915
6933
  }
6916
- return ok({ record_id, document, hidden, computed, computedUnavailable });
6934
+ return ok({ record_id, document, hidden, computed, computedUnavailable, version });
6917
6935
  },
6918
6936
  async listByIds({ table_id, ids }) {
6919
6937
  if (ids.length === 0) return ok([]);