@ai-matrx/records 0.52.0 → 0.53.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.
- package/CHANGELOG.md +69 -0
- package/dist/core/index.cjs +26 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +45 -5
- package/dist/core/index.d.ts +45 -5
- package/dist/core/index.js +26 -0
- package/dist/core/index.js.map +1 -1
- package/dist/{field-xRstzLKK.d.cts → field-ddMymGEI.d.cts} +5 -0
- package/dist/{field-xRstzLKK.d.ts → field-ddMymGEI.d.ts} +5 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +106 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +153 -2
- package/dist/react/index.d.ts +153 -2
- package/dist/react/index.js +106 -0
- package/dist/react/index.js.map +1 -1
- package/dist/use-cases/index.cjs.map +1 -1
- package/dist/use-cases/index.d.cts +1 -1
- package/dist/use-cases/index.d.ts +1 -1
- package/dist/use-cases/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records
|
|
2
2
|
|
|
3
|
+
## 0.53.0
|
|
4
|
+
|
|
5
|
+
**Archiving a table is a job, not a statement.** (lane FIX-11A, VERIFIER-11 finding F3.)
|
|
6
|
+
|
|
7
|
+
An admin of Greenline Landscaping Crew opened Settings on "Riverside Yard season tickets"
|
|
8
|
+
— 1,430 records the product's own import had just written — pressed **Delete this table**
|
|
9
|
+
and waited twelve seconds for `57014`, "canceling statement due to statement timeout …
|
|
10
|
+
Nothing was changed. Try a smaller page." There is no page. `custom.record_delete` aimed at
|
|
11
|
+
a Table recurses through everything the Table contains inside ONE transaction: a delete
|
|
12
|
+
rule, an access check, a history capture and an outbox row per record. That is unbounded by
|
|
13
|
+
construction — no timeout is long enough — and a store whose write path is batched and
|
|
14
|
+
whose delete path is not has one honest shape available to it.
|
|
15
|
+
|
|
16
|
+
* New client method `tableArchive({ table_id, chunk, includeTable })` over
|
|
17
|
+
`custom.table_archive`, typed as `TableArchiveProgress`: `archived`, `remaining`,
|
|
18
|
+
`total`, `archived_total`, `table_archived`, `done`, `chunk` and the store's own
|
|
19
|
+
`message`. `chunk: 0` reports and changes nothing, so a screen can say how many records
|
|
20
|
+
will be archived BEFORE a person commits to it.
|
|
21
|
+
* New hook `useTableArchive(tableId, { chunk, includeTable })` — it owns the loop, the
|
|
22
|
+
numbers, the stop and the resume, so every screen that ever gets rid of a table inherits
|
|
23
|
+
one behaviour instead of each re-deriving it. Resumable because it is STATELESS: what
|
|
24
|
+
remains is read off the records themselves every pass, so a refused pass, a `stop()`, a
|
|
25
|
+
closed tab or a deploy mid-run leave `interrupted` true and a `carryOn()` that continues
|
|
26
|
+
from exactly where it stopped. It must never be reported as "nothing happened":
|
|
27
|
+
`archived_total` is what actually landed.
|
|
28
|
+
* **The pass size is 50, measured, and it backs off.** One record costs about 35ms to
|
|
29
|
+
archive on the live store, so a pass of 200 is ~7s against an 8s statement timeout — it
|
|
30
|
+
died on `57014` on the first real table it was pointed at, which is the original defect
|
|
31
|
+
one level up. When the store does run out of time the loop HALVES the pass and carries
|
|
32
|
+
on (floor 10, `slowedDown` true so a screen can say so), because a pass that timed out
|
|
33
|
+
changed nothing and retrying it smaller repeats no work.
|
|
34
|
+
* Nothing is hard-deleted: every row still goes through `custom.record_delete`, so every
|
|
35
|
+
guard, history capture and outbox row happens and `recordRestore` still brings it back.
|
|
36
|
+
|
|
37
|
+
Proven on the main database on 2026-09-22 from the admin seat, through this package's own
|
|
38
|
+
client: 1,430 records archived in 25 passes across a deliberate cut and a resume (450 at
|
|
39
|
+
the cut, 1,430 at the end), then 312 more in 7 passes. Consumer action: a screen that
|
|
40
|
+
deletes a whole Table must call `useTableArchive`, never `useRecordMutation().remove`;
|
|
41
|
+
matrx-frontend's `pnpm check:table-archive-is-chunked` fails on the old shape.
|
|
42
|
+
|
|
43
|
+
## 0.52.0
|
|
44
|
+
|
|
45
|
+
**The portal archive doors are reachable.** (lane PUBLISH-SWEEP, closing ORG-CLEANUP's
|
|
46
|
+
own "left behind, named".)
|
|
47
|
+
|
|
48
|
+
* `custom.portal_archive`, `custom.portal_restore` and the archive-aware
|
|
49
|
+
`custom.list_portals(org, 'active'|'archived'|'all')` landed on the main database on
|
|
50
|
+
2026-09-22, and nine of Rincon Plumbing Co's ten identical portals were archived through
|
|
51
|
+
them FROM A SCRIPT. This package knew none of them, so from every client those nine simply
|
|
52
|
+
ceased to exist: `custom.portals` hides archived portals by design and there was no other
|
|
53
|
+
reader, no restore, and no archive.
|
|
54
|
+
* New client methods: `listPortals({ archived })`, `portalArchive({ portal_id, confirm_title,
|
|
55
|
+
reason })`, `portalRestore({ portal_id, confirm_title })`, over the three new door names.
|
|
56
|
+
* New vocabulary and words, so every screen says the same thing: `PORTAL_ARCHIVE_FILTERS` /
|
|
57
|
+
`PortalArchiveFilter` / `isPortalArchiveFilter` (three values, no fourth — the door refuses
|
|
58
|
+
a fourth `22023`), `PortalArchiveAnswer`, `portalArchiveConsequence`, `portalConfirmLine`,
|
|
59
|
+
`portalTitleTypedBack` and `emptyPortalArchiveLine`. The typed-title confirmation is the
|
|
60
|
+
DOOR's rule, not a screen's ceremony: ten portals can carry one title, so the title is not
|
|
61
|
+
what picks this one and `23514` is what says so.
|
|
62
|
+
* `src/store.generated.ts` regenerated from the live store (570 doors), which also picks up
|
|
63
|
+
IMPORT-2's `custom.io_import_declare_columns`.
|
|
64
|
+
|
|
65
|
+
**The import wizard adds the columns as its own step.** (lane IMPORT-2.)
|
|
66
|
+
|
|
67
|
+
* `importDeclareColumns` over `custom.io_import_declare_columns`, and the batch size the store
|
|
68
|
+
itself sizes, so a wide file stops being one statement the store refuses.
|
|
69
|
+
|
|
70
|
+
**Consumer action:** none — everything here is additive.
|
|
71
|
+
|
|
3
72
|
## 0.51.0
|
|
4
73
|
|
|
5
74
|
**The seal is asked through its own door.** (lane FIX-10B, from VERIFIER-10 finding F6.)
|
package/dist/core/index.cjs
CHANGED
|
@@ -1469,6 +1469,7 @@ var STORE_DOORS = [
|
|
|
1469
1469
|
{ name: "stamp_value_envelopes", args: "p_data jsonb, p_actor text, p_on_behalf_of text, p_at timestamp with time zone", returns: "jsonb", security: "invoker" },
|
|
1470
1470
|
{ name: "storage_modes", args: "", returns: "text[]", security: "invoker" },
|
|
1471
1471
|
{ name: "store_is_open", args: "p_organization_id uuid DEFAULT NULL::uuid", returns: "boolean", security: "invoker" },
|
|
1472
|
+
{ name: "store_off_sentence", args: "p_organization_id uuid", returns: "text", security: "invoker" },
|
|
1472
1473
|
{ name: "subscription_cadences", args: "p_organization_id uuid", returns: "text[]", security: "definer" },
|
|
1473
1474
|
{ name: "subscription_declare", args: "p_organization_id uuid, p_table_id uuid, p_spec jsonb", returns: "uuid", security: "definer" },
|
|
1474
1475
|
{ name: "subscription_mute", args: "p_organization_id uuid, p_rule_id uuid, p_muted boolean DEFAULT true", returns: "boolean", security: "definer" },
|
|
@@ -2751,6 +2752,19 @@ var DOORS = {
|
|
|
2751
2752
|
fieldOptions: "field_options",
|
|
2752
2753
|
parityValues: "parity_values",
|
|
2753
2754
|
tableDeclare: "table_declare",
|
|
2755
|
+
// GETTING RID OF A TABLE — which is a JOB, NOT A STATEMENT (VERIFIER-11 F3).
|
|
2756
|
+
// `record_delete` above is the store's one soft-delete door and it is right
|
|
2757
|
+
// for a record; aimed at a TABLE it recurses through everything the Table
|
|
2758
|
+
// contains inside one transaction, and that is unbounded by construction. A
|
|
2759
|
+
// person pressing "Delete this table" on 1,430 records the product itself
|
|
2760
|
+
// had just imported waited twelve seconds and was told `57014`, "canceling
|
|
2761
|
+
// statement due to statement timeout … Nothing was changed. Try a smaller
|
|
2762
|
+
// page" — advice that means nothing when what you are doing is getting rid
|
|
2763
|
+
// of a table. `custom.table_archive` does the same work in passes and
|
|
2764
|
+
// answers each one with numbers, so a screen shows a sentence and a count
|
|
2765
|
+
// instead of a spinner and an SQLSTATE. It is STATELESS, so a run cut in
|
|
2766
|
+
// half resumes exactly where it stopped: there is no job row to lose.
|
|
2767
|
+
tableArchive: "table_archive",
|
|
2754
2768
|
// ADDING A COLUMN — the first thing anybody does with a table, and until
|
|
2755
2769
|
// 2026-09-19 there was no door for it. A Field is a record (REC-25), so a
|
|
2756
2770
|
// screen wrote one with `record_write`; that door writes ONE row with
|
|
@@ -3414,6 +3428,18 @@ function createRecordsClient(config) {
|
|
|
3414
3428
|
"recordRestore"
|
|
3415
3429
|
);
|
|
3416
3430
|
},
|
|
3431
|
+
async tableArchive({ table_id, chunk, includeTable }) {
|
|
3432
|
+
return callDoor(
|
|
3433
|
+
DOORS.tableArchive,
|
|
3434
|
+
{
|
|
3435
|
+
p_organization_id: org,
|
|
3436
|
+
p_table_id: table_id,
|
|
3437
|
+
p_chunk: chunk ?? 50,
|
|
3438
|
+
p_include_table: includeTable ?? true
|
|
3439
|
+
},
|
|
3440
|
+
"tableArchive"
|
|
3441
|
+
);
|
|
3442
|
+
},
|
|
3417
3443
|
async recordRead({ record_id, withComputed = false }) {
|
|
3418
3444
|
const door = await callDoor(
|
|
3419
3445
|
DOORS.readRecord,
|