@ai-matrx/records 0.21.0 → 0.26.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 +159 -1
- package/dist/core/index.cjs +518 -25
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +151 -17
- package/dist/core/index.d.ts +151 -17
- package/dist/core/index.js +518 -25
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +342 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +711 -12
- package/dist/index.d.ts +711 -12
- package/dist/index.js +342 -8
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +506 -8
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +821 -12
- package/dist/react/index.d.ts +821 -12
- package/dist/react/index.js +506 -8
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,147 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records
|
|
2
2
|
|
|
3
|
+
## 0.26.0
|
|
4
|
+
|
|
5
|
+
**"Text me on a new lead; email me a Monday summary" — the second half of that
|
|
6
|
+
sentence becomes something the store can be told.**
|
|
7
|
+
|
|
8
|
+
A cadence was two words, `immediate` and `digest`, and `schedule` was free text
|
|
9
|
+
nothing read. `SubscriptionCadence` is now `instant | hourly | daily | weekly` —
|
|
10
|
+
the words `custom.agg_subscription_cadences()` answers and the digest runner acts
|
|
11
|
+
on. The two old words still work everywhere: the store normalises them, so a Rule
|
|
12
|
+
written last week and one written by the picker today mean the same thing.
|
|
13
|
+
|
|
14
|
+
`MySubscription` gained the three answers a notifications list actually needs and
|
|
15
|
+
could not ask for: `quiet_hours`, `last_sent_at` and `next_digest_at`. The last is
|
|
16
|
+
computed by the SAME store functions the runner uses, so "next Monday 08:00" on the
|
|
17
|
+
screen and the moment it fires cannot drift; it is null for a muted subscription,
|
|
18
|
+
because saying "next Monday" beside a switch that is off is a screen that lies.
|
|
19
|
+
|
|
20
|
+
Two doors that did not exist:
|
|
21
|
+
|
|
22
|
+
* `subscriptionDeclare({ table_id, spec })` — WRITE one. The store refuses a cadence
|
|
23
|
+
no runner acts on, a channel this platform does not send on and quiet hours nobody
|
|
24
|
+
can parse, each by name, then declares the Rule through `rule_declare` (REC-72: a
|
|
25
|
+
Rule written as a record is invisible to every reader that goes looking for one).
|
|
26
|
+
* `subscriptionPreview({ rule_id })` — the summary it would send RIGHT NOW, with what
|
|
27
|
+
entered, left and changed NAMED rather than counted, plus the in-view total and a
|
|
28
|
+
click-through to the filtered grid. It assembles only: nothing is delivered,
|
|
29
|
+
nothing is recorded, and the watermark does not move.
|
|
30
|
+
|
|
31
|
+
`QuietHours`, `DigestPreview` and `SubscriptionSpec` are new exported types.
|
|
32
|
+
`store.generated.ts` regenerated from live — 520 doors, `check:store-registry` OK.
|
|
33
|
+
## 0.26.0 — 2026-09-20
|
|
34
|
+
|
|
35
|
+
**The queue was filing every refusal as a hiccup, and a real browser found it.**
|
|
36
|
+
|
|
37
|
+
`isDecided` listed SQLSTATEs. `RecordsClient` hands back its own WORD —
|
|
38
|
+
`invalid_argument`, `door`, `refused_by_rule` — with the sqlstate beside it, so
|
|
39
|
+
no refusal ever matched: a capture the store had refused BY NAME went back in the
|
|
40
|
+
queue and would have been sent again forever, which is the exact silent hole the
|
|
41
|
+
queue's fourth decision exists to prevent. The unit suite passed throughout
|
|
42
|
+
because it fed the check SQLSTATEs — it was testing the assumption, not the
|
|
43
|
+
client. Measured in a headless Chromium at 390 px against the main database.
|
|
44
|
+
|
|
45
|
+
It is now the other way round, and from the package's own list rather than a
|
|
46
|
+
guess: `stale_read`, `timed_out` and `store_limit` are worth repeating (which is
|
|
47
|
+
what `results.ts` says in as many words), a failure with NO code at all is the
|
|
48
|
+
network and waits, and everything the door named is a decision that stays.
|
|
49
|
+
`isDecided` takes the error, not a code, so either spelling works.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 0.25.0 — 2026-09-20
|
|
53
|
+
|
|
54
|
+
**A crew out in the field, and a phone with no bars (PRODUCTS row 15, P13).**
|
|
55
|
+
|
|
56
|
+
* `captureSheetDeclare`, `capturePublish`, `captureSheets`, `captureOpen` and
|
|
57
|
+
`captureSubmit` — the five SIGNED-IN doors of a crew capture sheet. A capture
|
|
58
|
+
sheet is not a second form store: it is the ONE Form object saying
|
|
59
|
+
`audience = 'crew'`, opened by `capture_opened_at` rather than `published_at`,
|
|
60
|
+
and the database carries a CHECK that refuses a row which is both. `captureOpen`
|
|
61
|
+
answers `null` for a sheet below `viewer` AND for a sheet that is not there —
|
|
62
|
+
the same answer the Table gives — and carries an honest `may_capture` with a
|
|
63
|
+
sentence, so a phone never discovers a refusal after the walk to the bin.
|
|
64
|
+
* `openCaptureQueue` — the offline queue itself, headless, in IndexedDB, with the
|
|
65
|
+
network calls injected. The capture is on disk before the network is asked; the
|
|
66
|
+
idempotency key is minted when the capture is TAKEN, not when it is sent; items
|
|
67
|
+
go up oldest first, one at a time; a file's bytes are uploaded once and the
|
|
68
|
+
reference written back, so a failed submit never re-sends the photograph; and a
|
|
69
|
+
REFUSAL is not a retry — it stays in the queue carrying the door's own sentence
|
|
70
|
+
until a person fixes it or throws it away. `sync()` answers with per-item
|
|
71
|
+
outcomes, never a boolean.
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
## 0.24.0 — 2026-09-20
|
|
75
|
+
|
|
76
|
+
**0.23.0 never reached npm.** Its release run died before any package could be
|
|
77
|
+
built: five `node_modules` SYMLINKS, each pointing at an absolute path inside
|
|
78
|
+
one machine's checkout, had been committed to the repository, and on a CI runner
|
|
79
|
+
a dangling symlink makes `pnpm install --frozen-lockfile` answer ENOENT. Every
|
|
80
|
+
package release was blocked, not just this one. The symlinks are untracked and
|
|
81
|
+
`.gitignore` now carries both spellings — `node_modules/` with the slash matches
|
|
82
|
+
a directory only, which is how a symlink of that name walked past it. This
|
|
83
|
+
version is 0.23.0's contents from the repaired tree; a tag is never moved, so
|
|
84
|
+
0.23.0 is simply absent from npm.
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
## 0.23.0
|
|
88
|
+
|
|
89
|
+
**A column a model owns — seven doors, and the four states one cell can be in.**
|
|
90
|
+
|
|
91
|
+
`enrichments`, `enrichDeclare`, `enrichDue`, `enrichCells`, `enrichLand`,
|
|
92
|
+
`enrichPin` and `enrichRuns` (PRODUCTS row 10 / AGT-6). An Enrichment is not a new
|
|
93
|
+
object: it IS the Field, saying `source = agent`, with its instruction, inputs,
|
|
94
|
+
model, confidence floor and freshness in `source_config` and
|
|
95
|
+
`review_interval_days` — so nothing here is keyed by an enrichment id.
|
|
96
|
+
|
|
97
|
+
`EnrichCell` is the type a screen draws a badge from, and it is deliberately able
|
|
98
|
+
to say four different things, because they are four different facts: a value an
|
|
99
|
+
agent wrote (with the model, what it read and how sure it was), a value a person
|
|
100
|
+
took back (`pinned`), an answer the model would not commit to (`absent_reason:
|
|
101
|
+
"conflicting"`, the guess in `alternates[0]`), and a value past its freshness date
|
|
102
|
+
(`stale` — still holding its value; stale marks it, it never erases it).
|
|
103
|
+
|
|
104
|
+
`enrichCells` answers a WHOLE PAGE in one call. Asked per cell it is one round trip
|
|
105
|
+
per badge.
|
|
106
|
+
|
|
107
|
+
The door catalogue is regenerated from the live store: 478 doors.
|
|
108
|
+
|
|
109
|
+
## 0.22.0 — 2026-09-20
|
|
110
|
+
|
|
111
|
+
**BREAKING: a parse is a parse. `importCsv` / `importXlsx` no longer take the Fields.**
|
|
112
|
+
|
|
113
|
+
Found by looking at the import screen, not by reading it. The plan showed *"every
|
|
114
|
+
row in this column is empty, so there is nothing to go on"* for columns plainly
|
|
115
|
+
full of values, and every unmapped column's sample list was blank.
|
|
116
|
+
|
|
117
|
+
The cause was in this package. The parse took the Fields, matched each heading by
|
|
118
|
+
key or label, keyed the rows by whatever it matched and **dropped every column it
|
|
119
|
+
did not**. That is a mapping decision, made by the one component that must not
|
|
120
|
+
make it: the rows handed to `custom.io_import_rows` carried nothing for the
|
|
121
|
+
unmatched columns, so the store never saw them and could not offer a single one as
|
|
122
|
+
a new column. The whole "never a junk bag" half of SCR-N-7 was unreachable through
|
|
123
|
+
that screen — and it shipped in **0.21.0**, so anyone on that version has it.
|
|
124
|
+
|
|
125
|
+
Fixed in the SHAPE rather than beside it. These functions no longer take `fields`
|
|
126
|
+
at all, so a parse cannot decide a mapping even by accident:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// before
|
|
130
|
+
importCsv(fields, text) // -> { columns: [{header, field}], rows, unmapped }
|
|
131
|
+
// after
|
|
132
|
+
importCsv(text) // -> { headers, rows } rows keyed by heading
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`ParsedImport` is now `{ headers: string[]; rows: Array<Record<string, string>> }`
|
|
136
|
+
— every heading, every row keyed by heading, nothing dropped — with no `field`, no
|
|
137
|
+
`unmapped`, and **no safe path left standing beside the unsafe one**. Only the door
|
|
138
|
+
decides. `parseCsv`, `exportCsv`, `exportXlsx`, `toAoa` and `cellText` are
|
|
139
|
+
unchanged.
|
|
140
|
+
|
|
141
|
+
**Migrating:** drop the first argument, and send `rows` straight to
|
|
142
|
+
`custom.io_import_rows` — the store matches the headings and offers what it could
|
|
143
|
+
not match. A caller that genuinely wants the old pre-matched shape can build it
|
|
144
|
+
from `headers` in four lines, which is what this package's own red twin does.
|
|
3
145
|
|
|
4
146
|
## 0.21.0 — 2026-09-20
|
|
5
147
|
|
|
@@ -68,6 +210,23 @@ cannot unzip a workbook; **everything after the parse is the store's**, so a fil
|
|
|
68
210
|
imported from a screen, from an agent and from a script land identically and
|
|
69
211
|
there is no second importer in this package to drift.
|
|
70
212
|
|
|
213
|
+
**BREAKING, and it is the point: `importCsv` / `importXlsx` no longer take the
|
|
214
|
+
Fields.** They took them, matched each heading to a Field by key or label, keyed
|
|
215
|
+
the rows by whatever they matched and **dropped every column they did not** —
|
|
216
|
+
and that cannot be the input to a door whose whole job is to decide the mapping
|
|
217
|
+
and offer what it could not match. Measured by looking at the screen: every
|
|
218
|
+
unmapped column's samples came back empty and its proposal read *"every row in
|
|
219
|
+
this column is empty, so there is nothing to go on"* for columns plainly full of
|
|
220
|
+
values. The store can only offer a column whose values reached it.
|
|
221
|
+
|
|
222
|
+
`ParsedImport` is now `{ headers: string[]; rows: Array<Record<string, string>> }`
|
|
223
|
+
— every heading, every row keyed by heading, nothing dropped — and there is no
|
|
224
|
+
`field`, no `unmapped` and no way to ask for one. The fix is the SHAPE rather
|
|
225
|
+
than a second field beside the first: a parse that is not given the Fields
|
|
226
|
+
cannot decide a mapping even by accident, and no safe path is left standing
|
|
227
|
+
beside an unsafe one. `parseCsv`, `exportCsv`, `exportXlsx`, `toAoa` and
|
|
228
|
+
`cellText` are unchanged.
|
|
229
|
+
|
|
71
230
|
- **`importPlan({ table_id, columns })`** — what the store makes of a file's
|
|
72
231
|
columns before anything is written, reading the parity map AND this
|
|
73
232
|
organization: an address that belongs to a member is a `member` column, a few
|
|
@@ -104,7 +263,6 @@ New types in `@ai-matrx/records`: `ImportPlan`, `ImportColumnPlan`,
|
|
|
104
263
|
`InboundAddress`, `InboundDeclared`.
|
|
105
264
|
|
|
106
265
|
`store.generated.ts` regenerated against the live store: **428 doors**.
|
|
107
|
-
|
|
108
266
|
## 0.18.0
|
|
109
267
|
|
|
110
268
|
**"Who changed this price, and can I put it back?" — the doors for PRODUCTS row 4.**
|