@ai-matrx/records 0.20.0 → 0.24.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 +125 -2
- package/dist/core/index.cjs +179 -17
- 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 +179 -17
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +302 -1
- package/dist/index.d.ts +302 -1
- package/dist/react/index.cjs +167 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +410 -1
- package/dist/react/index.d.ts +410 -1
- package/dist/react/index.js +167 -0
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,80 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
## 0.24.0 — 2026-09-20
|
|
6
|
+
|
|
7
|
+
**0.23.0 never reached npm.** Its release run died before any package could be
|
|
8
|
+
built: five `node_modules` SYMLINKS, each pointing at an absolute path inside
|
|
9
|
+
one machine's checkout, had been committed to the repository, and on a CI runner
|
|
10
|
+
a dangling symlink makes `pnpm install --frozen-lockfile` answer ENOENT. Every
|
|
11
|
+
package release was blocked, not just this one. The symlinks are untracked and
|
|
12
|
+
`.gitignore` now carries both spellings — `node_modules/` with the slash matches
|
|
13
|
+
a directory only, which is how a symlink of that name walked past it. This
|
|
14
|
+
version is 0.23.0's contents from the repaired tree; a tag is never moved, so
|
|
15
|
+
0.23.0 is simply absent from npm.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## 0.23.0
|
|
19
|
+
|
|
20
|
+
**A column a model owns — seven doors, and the four states one cell can be in.**
|
|
21
|
+
|
|
22
|
+
`enrichments`, `enrichDeclare`, `enrichDue`, `enrichCells`, `enrichLand`,
|
|
23
|
+
`enrichPin` and `enrichRuns` (PRODUCTS row 10 / AGT-6). An Enrichment is not a new
|
|
24
|
+
object: it IS the Field, saying `source = agent`, with its instruction, inputs,
|
|
25
|
+
model, confidence floor and freshness in `source_config` and
|
|
26
|
+
`review_interval_days` — so nothing here is keyed by an enrichment id.
|
|
27
|
+
|
|
28
|
+
`EnrichCell` is the type a screen draws a badge from, and it is deliberately able
|
|
29
|
+
to say four different things, because they are four different facts: a value an
|
|
30
|
+
agent wrote (with the model, what it read and how sure it was), a value a person
|
|
31
|
+
took back (`pinned`), an answer the model would not commit to (`absent_reason:
|
|
32
|
+
"conflicting"`, the guess in `alternates[0]`), and a value past its freshness date
|
|
33
|
+
(`stale` — still holding its value; stale marks it, it never erases it).
|
|
34
|
+
|
|
35
|
+
`enrichCells` answers a WHOLE PAGE in one call. Asked per cell it is one round trip
|
|
36
|
+
per badge.
|
|
37
|
+
|
|
38
|
+
The door catalogue is regenerated from the live store: 478 doors.
|
|
39
|
+
|
|
40
|
+
## 0.22.0 — 2026-09-20
|
|
41
|
+
|
|
42
|
+
**BREAKING: a parse is a parse. `importCsv` / `importXlsx` no longer take the Fields.**
|
|
43
|
+
|
|
44
|
+
Found by looking at the import screen, not by reading it. The plan showed *"every
|
|
45
|
+
row in this column is empty, so there is nothing to go on"* for columns plainly
|
|
46
|
+
full of values, and every unmapped column's sample list was blank.
|
|
47
|
+
|
|
48
|
+
The cause was in this package. The parse took the Fields, matched each heading by
|
|
49
|
+
key or label, keyed the rows by whatever it matched and **dropped every column it
|
|
50
|
+
did not**. That is a mapping decision, made by the one component that must not
|
|
51
|
+
make it: the rows handed to `custom.io_import_rows` carried nothing for the
|
|
52
|
+
unmatched columns, so the store never saw them and could not offer a single one as
|
|
53
|
+
a new column. The whole "never a junk bag" half of SCR-N-7 was unreachable through
|
|
54
|
+
that screen — and it shipped in **0.21.0**, so anyone on that version has it.
|
|
55
|
+
|
|
56
|
+
Fixed in the SHAPE rather than beside it. These functions no longer take `fields`
|
|
57
|
+
at all, so a parse cannot decide a mapping even by accident:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// before
|
|
61
|
+
importCsv(fields, text) // -> { columns: [{header, field}], rows, unmapped }
|
|
62
|
+
// after
|
|
63
|
+
importCsv(text) // -> { headers, rows } rows keyed by heading
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`ParsedImport` is now `{ headers: string[]; rows: Array<Record<string, string>> }`
|
|
67
|
+
— every heading, every row keyed by heading, nothing dropped — with no `field`, no
|
|
68
|
+
`unmapped`, and **no safe path left standing beside the unsafe one**. Only the door
|
|
69
|
+
decides. `parseCsv`, `exportCsv`, `exportXlsx`, `toAoa` and `cellText` are
|
|
70
|
+
unchanged.
|
|
71
|
+
|
|
72
|
+
**Migrating:** drop the first argument, and send `rows` straight to
|
|
73
|
+
`custom.io_import_rows` — the store matches the headings and offers what it could
|
|
74
|
+
not match. A caller that genuinely wants the old pre-matched shape can build it
|
|
75
|
+
from `headers` in four lines, which is what this package's own red twin does.
|
|
76
|
+
|
|
77
|
+
## 0.21.0 — 2026-09-20
|
|
5
78
|
|
|
6
79
|
**The store moved and this package's census caught it — twice.** The release
|
|
7
80
|
gate runs the live-door suite against the real record store, and it went red on
|
|
@@ -18,6 +91,40 @@ main:
|
|
|
18
91
|
are: the call named a value the shape does not accept, and the store's own
|
|
19
92
|
sentence names the number and the range.
|
|
20
93
|
|
|
94
|
+
> **About 0.20.0 on npm.** This lane tagged `0.20.0` from a commit that carried
|
|
95
|
+
> the census fixes above but NOT the checklist doors below — the tag was pushed
|
|
96
|
+
> while a merge of main was still open, and a tag is never moved. `0.21.0` is
|
|
97
|
+
> the first version that carries both. Nobody should sit on 0.20.0.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## 0.20.0
|
|
101
|
+
|
|
102
|
+
**"Every new hire gets these twelve steps." — the checklist doors, PRODUCTS row 13.**
|
|
103
|
+
|
|
104
|
+
A checklist template is a record of the store: ordered steps, each with the role
|
|
105
|
+
that owns it, how many days in it is due, what it waits for and what it asks for
|
|
106
|
+
before it counts as done, plus the thing that starts a run. Starting one makes
|
|
107
|
+
ONE work item per step in the one work layer — assigned by role to real people,
|
|
108
|
+
dated from the offsets, dependencies carried across — so a step appears in the
|
|
109
|
+
person's ordinary inbox beside everything else and there is no
|
|
110
|
+
checklist-shaped queue anywhere.
|
|
111
|
+
|
|
112
|
+
Nine doors, all of them live on the main database:
|
|
113
|
+
`checklistRefusal` (pure — a screen may ask it on every keystroke, and the
|
|
114
|
+
refusal arrives before the save), `checklistDeclare`, `checklistTemplates`,
|
|
115
|
+
`checklistTemplateShape`, `checklistStart`, `checklistRun`, `checklistRuns`,
|
|
116
|
+
`checklistStepComplete`, `checklistStepRefusal`.
|
|
117
|
+
|
|
118
|
+
`ChecklistRunStep` carries `blocked_by`, `refusal`, `may_complete` and
|
|
119
|
+
`requires_key`, so a screen can say why a step cannot be finished yet — in the
|
|
120
|
+
words a person would use — before anybody clicks, file the evidence under the
|
|
121
|
+
key the checklist itself named, and show no control at all where a control would
|
|
122
|
+
fail.
|
|
123
|
+
|
|
124
|
+
THE DEPENDENCY RULE LIVES IN THE STORE, not in these methods: a BEFORE trigger
|
|
125
|
+
on `custom.record` refuses a step reaching Done while a step it waits for is
|
|
126
|
+
open, so `workSetState` meets the same sentence `checklistStepComplete` does.
|
|
127
|
+
|
|
21
128
|
|
|
22
129
|
## 0.19.0
|
|
23
130
|
|
|
@@ -34,6 +141,23 @@ cannot unzip a workbook; **everything after the parse is the store's**, so a fil
|
|
|
34
141
|
imported from a screen, from an agent and from a script land identically and
|
|
35
142
|
there is no second importer in this package to drift.
|
|
36
143
|
|
|
144
|
+
**BREAKING, and it is the point: `importCsv` / `importXlsx` no longer take the
|
|
145
|
+
Fields.** They took them, matched each heading to a Field by key or label, keyed
|
|
146
|
+
the rows by whatever they matched and **dropped every column they did not** —
|
|
147
|
+
and that cannot be the input to a door whose whole job is to decide the mapping
|
|
148
|
+
and offer what it could not match. Measured by looking at the screen: every
|
|
149
|
+
unmapped column's samples came back empty and its proposal read *"every row in
|
|
150
|
+
this column is empty, so there is nothing to go on"* for columns plainly full of
|
|
151
|
+
values. The store can only offer a column whose values reached it.
|
|
152
|
+
|
|
153
|
+
`ParsedImport` is now `{ headers: string[]; rows: Array<Record<string, string>> }`
|
|
154
|
+
— every heading, every row keyed by heading, nothing dropped — and there is no
|
|
155
|
+
`field`, no `unmapped` and no way to ask for one. The fix is the SHAPE rather
|
|
156
|
+
than a second field beside the first: a parse that is not given the Fields
|
|
157
|
+
cannot decide a mapping even by accident, and no safe path is left standing
|
|
158
|
+
beside an unsafe one. `parseCsv`, `exportCsv`, `exportXlsx`, `toAoa` and
|
|
159
|
+
`cellText` are unchanged.
|
|
160
|
+
|
|
37
161
|
- **`importPlan({ table_id, columns })`** — what the store makes of a file's
|
|
38
162
|
columns before anything is written, reading the parity map AND this
|
|
39
163
|
organization: an address that belongs to a member is a `member` column, a few
|
|
@@ -70,7 +194,6 @@ New types in `@ai-matrx/records`: `ImportPlan`, `ImportColumnPlan`,
|
|
|
70
194
|
`InboundAddress`, `InboundDeclared`.
|
|
71
195
|
|
|
72
196
|
`store.generated.ts` regenerated against the live store: **428 doors**.
|
|
73
|
-
|
|
74
197
|
## 0.18.0
|
|
75
198
|
|
|
76
199
|
**"Who changed this price, and can I put it back?" — the doors for PRODUCTS row 4.**
|
package/dist/core/index.cjs
CHANGED
|
@@ -2085,6 +2085,22 @@ var DOORS = {
|
|
|
2085
2085
|
dashboardRun: "dashboard_run",
|
|
2086
2086
|
dashboardStuck: "dashboard_stuck",
|
|
2087
2087
|
dashboardDelete: "dashboard_delete",
|
|
2088
|
+
// PRODUCTS row 10 — "fill in each company's industry and headcount, and keep
|
|
2089
|
+
// it fresh". An Enrichment is not an object of its own: it is a Field saying
|
|
2090
|
+
// `source = agent`, with its instruction, inputs, model, confidence floor and
|
|
2091
|
+
// freshness in `source_config` (AGT-6). `enrichCells` is the one a GRID calls —
|
|
2092
|
+
// the badge data for a whole page in one round trip, because a badge asked per
|
|
2093
|
+
// cell is fifty round trips to draw fifty badges. `enrichPin` is the half of
|
|
2094
|
+
// this product that decides whether anybody ever trusts it: the moment a
|
|
2095
|
+
// person types over an agent-owned cell the store pins it, and the next run
|
|
2096
|
+
// leaves it exactly as they left it.
|
|
2097
|
+
enrichments: "enrichments",
|
|
2098
|
+
enrichDeclare: "enrich_declare",
|
|
2099
|
+
enrichDue: "enrich_due",
|
|
2100
|
+
enrichCells: "enrich_cells",
|
|
2101
|
+
enrichLand: "enrich_land",
|
|
2102
|
+
enrichPin: "enrich_pin",
|
|
2103
|
+
enrichRuns: "enrich_runs",
|
|
2088
2104
|
// PRODUCTS row 4 — "Who changed this price, and can I put it back?" The
|
|
2089
2105
|
// history has been in the store since W3-HIST; until 2026-09-20 the only way
|
|
2090
2106
|
// to ask for it was `io_revisions`, which answers a raw uuid for the author
|
|
@@ -2214,6 +2230,16 @@ var DOORS = {
|
|
|
2214
2230
|
workApprovalMayDecide: "work_approval_may_decide",
|
|
2215
2231
|
workApprovalDecide: "work_approval_decide",
|
|
2216
2232
|
workApprovalRead: "work_approval_read",
|
|
2233
|
+
// PRODUCTS row 13 — checklists and SOP runs.
|
|
2234
|
+
checklistRefusal: "checklist_refusal",
|
|
2235
|
+
checklistDeclare: "checklist_declare",
|
|
2236
|
+
checklistTemplates: "checklist_templates",
|
|
2237
|
+
checklistTemplateShape: "checklist_template_shape",
|
|
2238
|
+
checklistStart: "checklist_start",
|
|
2239
|
+
checklistRun: "checklist_run",
|
|
2240
|
+
checklistRuns: "checklist_runs",
|
|
2241
|
+
checklistStepComplete: "checklist_step_complete",
|
|
2242
|
+
checklistStepRefusal: "checklist_step_refusal",
|
|
2217
2243
|
// Declared here BECAUSE they do not exist yet: naming them is what makes the
|
|
2218
2244
|
// absence visible to `pnpm check:store-registry` and to the suite.
|
|
2219
2245
|
metadataSearch: "metadata_search",
|
|
@@ -2992,6 +3018,67 @@ function createRecordsClient(config) {
|
|
|
2992
3018
|
"ruleEval"
|
|
2993
3019
|
);
|
|
2994
3020
|
},
|
|
3021
|
+
async enrichments(args) {
|
|
3022
|
+
return callDoor(
|
|
3023
|
+
DOORS.enrichments,
|
|
3024
|
+
{ p_organization_id: org, p_table_id: args?.table_id ?? null },
|
|
3025
|
+
"enrichments"
|
|
3026
|
+
);
|
|
3027
|
+
},
|
|
3028
|
+
async enrichDeclare(args) {
|
|
3029
|
+
return callDoor(
|
|
3030
|
+
DOORS.enrichDeclare,
|
|
3031
|
+
{ p_organization_id: org, p_field_id: args.field_id, p_spec: args.spec },
|
|
3032
|
+
"enrichDeclare"
|
|
3033
|
+
);
|
|
3034
|
+
},
|
|
3035
|
+
async enrichDue(args) {
|
|
3036
|
+
return callDoor(
|
|
3037
|
+
DOORS.enrichDue,
|
|
3038
|
+
{
|
|
3039
|
+
p_organization_id: org,
|
|
3040
|
+
p_field_id: args.field_id,
|
|
3041
|
+
p_limit: args.limit ?? 50,
|
|
3042
|
+
p_include_fresh: args.include_fresh ?? false
|
|
3043
|
+
},
|
|
3044
|
+
"enrichDue"
|
|
3045
|
+
);
|
|
3046
|
+
},
|
|
3047
|
+
async enrichCells(args) {
|
|
3048
|
+
return callDoor(
|
|
3049
|
+
DOORS.enrichCells,
|
|
3050
|
+
{
|
|
3051
|
+
p_organization_id: org,
|
|
3052
|
+
p_table_id: args.table_id,
|
|
3053
|
+
p_field_keys: args.field_keys ?? null,
|
|
3054
|
+
p_record_ids: args.record_ids ?? null
|
|
3055
|
+
},
|
|
3056
|
+
"enrichCells"
|
|
3057
|
+
);
|
|
3058
|
+
},
|
|
3059
|
+
async enrichPin(args) {
|
|
3060
|
+
return callDoor(
|
|
3061
|
+
DOORS.enrichPin,
|
|
3062
|
+
{
|
|
3063
|
+
p_organization_id: org,
|
|
3064
|
+
p_record_id: args.record_id,
|
|
3065
|
+
p_field_key: args.field_key,
|
|
3066
|
+
p_pinned: args.pinned ?? true
|
|
3067
|
+
},
|
|
3068
|
+
"enrichPin"
|
|
3069
|
+
);
|
|
3070
|
+
},
|
|
3071
|
+
async enrichRuns(args) {
|
|
3072
|
+
return callDoor(
|
|
3073
|
+
DOORS.enrichRuns,
|
|
3074
|
+
{
|
|
3075
|
+
p_organization_id: org,
|
|
3076
|
+
p_field_id: args?.field_id ?? null,
|
|
3077
|
+
p_limit: args?.limit ?? 50
|
|
3078
|
+
},
|
|
3079
|
+
"enrichRuns"
|
|
3080
|
+
);
|
|
3081
|
+
},
|
|
2995
3082
|
async dashboards(args) {
|
|
2996
3083
|
return callDoor(
|
|
2997
3084
|
DOORS.dashboards,
|
|
@@ -3552,6 +3639,86 @@ function createRecordsClient(config) {
|
|
|
3552
3639
|
"workSlotExpire"
|
|
3553
3640
|
);
|
|
3554
3641
|
},
|
|
3642
|
+
// ── checklists and SOP runs ──────────────────────────────────────────
|
|
3643
|
+
async checklistRefusal({ spec }) {
|
|
3644
|
+
return callDoor(
|
|
3645
|
+
DOORS.checklistRefusal,
|
|
3646
|
+
{ p_spec: spec },
|
|
3647
|
+
"checklistRefusal"
|
|
3648
|
+
);
|
|
3649
|
+
},
|
|
3650
|
+
async checklistDeclare({ spec, template_id = null }) {
|
|
3651
|
+
return callDoor(
|
|
3652
|
+
DOORS.checklistDeclare,
|
|
3653
|
+
{ p_organization_id: org, p_spec: spec, p_template_id: template_id },
|
|
3654
|
+
"checklistDeclare"
|
|
3655
|
+
);
|
|
3656
|
+
},
|
|
3657
|
+
async checklistTemplates(args) {
|
|
3658
|
+
return callDoor(
|
|
3659
|
+
DOORS.checklistTemplates,
|
|
3660
|
+
{
|
|
3661
|
+
p_organization_id: org,
|
|
3662
|
+
p_about_table_id: args?.about_table_id ?? null,
|
|
3663
|
+
p_limit: args?.limit ?? 100
|
|
3664
|
+
},
|
|
3665
|
+
"checklistTemplates"
|
|
3666
|
+
);
|
|
3667
|
+
},
|
|
3668
|
+
async checklistTemplateShape({ template_id }) {
|
|
3669
|
+
return callDoor(
|
|
3670
|
+
DOORS.checklistTemplateShape,
|
|
3671
|
+
{ p_organization_id: org, p_template_id: template_id },
|
|
3672
|
+
"checklistTemplateShape"
|
|
3673
|
+
);
|
|
3674
|
+
},
|
|
3675
|
+
async checklistStart({ template_id, about_record_id = null, roles = {}, startingAt = null }) {
|
|
3676
|
+
return callDoor(
|
|
3677
|
+
DOORS.checklistStart,
|
|
3678
|
+
{
|
|
3679
|
+
p_organization_id: org,
|
|
3680
|
+
p_template_id: template_id,
|
|
3681
|
+
p_about_record_id: about_record_id,
|
|
3682
|
+
p_roles: roles,
|
|
3683
|
+
p_starting_at: startingAt
|
|
3684
|
+
},
|
|
3685
|
+
"checklistStart"
|
|
3686
|
+
);
|
|
3687
|
+
},
|
|
3688
|
+
async checklistRun({ run_id }) {
|
|
3689
|
+
return callDoor(
|
|
3690
|
+
DOORS.checklistRun,
|
|
3691
|
+
{ p_organization_id: org, p_run_id: run_id },
|
|
3692
|
+
"checklistRun"
|
|
3693
|
+
);
|
|
3694
|
+
},
|
|
3695
|
+
async checklistRuns(args) {
|
|
3696
|
+
return callDoor(
|
|
3697
|
+
DOORS.checklistRuns,
|
|
3698
|
+
{
|
|
3699
|
+
p_organization_id: org,
|
|
3700
|
+
p_about_table_id: args?.about_table_id ?? null,
|
|
3701
|
+
p_about_record_id: args?.about_record_id ?? null,
|
|
3702
|
+
p_include_closed: args?.includeClosed ?? true,
|
|
3703
|
+
p_limit: args?.limit ?? 100
|
|
3704
|
+
},
|
|
3705
|
+
"checklistRuns"
|
|
3706
|
+
);
|
|
3707
|
+
},
|
|
3708
|
+
async checklistStepComplete({ step_id, evidence = {} }) {
|
|
3709
|
+
return callDoor(
|
|
3710
|
+
DOORS.checklistStepComplete,
|
|
3711
|
+
{ p_organization_id: org, p_step_id: step_id, p_evidence: evidence },
|
|
3712
|
+
"checklistStepComplete"
|
|
3713
|
+
);
|
|
3714
|
+
},
|
|
3715
|
+
async checklistStepRefusal({ step_id }) {
|
|
3716
|
+
return callDoor(
|
|
3717
|
+
DOORS.checklistStepRefusal,
|
|
3718
|
+
{ p_organization_id: org, p_step_id: step_id },
|
|
3719
|
+
"checklistStepRefusal"
|
|
3720
|
+
);
|
|
3721
|
+
},
|
|
3555
3722
|
// ── the work layer a person actually uses ────────────────────────────
|
|
3556
3723
|
async workInbox(args) {
|
|
3557
3724
|
return callDoor(
|
|
@@ -4210,10 +4377,6 @@ async function exportXlsx(fields, rows, sheetLabel = "Records") {
|
|
|
4210
4377
|
XLSX.utils.book_append_sheet(workbook, worksheet, name);
|
|
4211
4378
|
return XLSX.write(workbook, { type: "array", bookType: "xlsx", compression: true });
|
|
4212
4379
|
}
|
|
4213
|
-
function matchField(header, fields) {
|
|
4214
|
-
const wanted = header.trim().toLowerCase();
|
|
4215
|
-
return fields.find((f) => f.key.toLowerCase() === wanted) ?? fields.find((f) => (f.label ?? "").toLowerCase() === wanted) ?? null;
|
|
4216
|
-
}
|
|
4217
4380
|
function parseCsv(text) {
|
|
4218
4381
|
const rows = [];
|
|
4219
4382
|
let row = [];
|
|
@@ -4248,31 +4411,30 @@ function parseCsv(text) {
|
|
|
4248
4411
|
}
|
|
4249
4412
|
return rows;
|
|
4250
4413
|
}
|
|
4251
|
-
function importCsv(
|
|
4252
|
-
return fromMatrix(
|
|
4414
|
+
function importCsv(text) {
|
|
4415
|
+
return fromMatrix(parseCsv(text));
|
|
4253
4416
|
}
|
|
4254
|
-
async function importXlsx(
|
|
4417
|
+
async function importXlsx(bytes) {
|
|
4255
4418
|
const XLSX = await import("xlsx");
|
|
4256
4419
|
const workbook = XLSX.read(bytes, { type: "array" });
|
|
4257
4420
|
const first = workbook.SheetNames[0];
|
|
4258
|
-
if (!first) return {
|
|
4421
|
+
if (!first) return { headers: [], rows: [] };
|
|
4259
4422
|
const sheet = workbook.Sheets[first];
|
|
4260
|
-
if (!sheet) return {
|
|
4423
|
+
if (!sheet) return { headers: [], rows: [] };
|
|
4261
4424
|
const matrix = XLSX.utils.sheet_to_json(sheet, { header: 1, raw: false, defval: "" });
|
|
4262
|
-
return fromMatrix(
|
|
4425
|
+
return fromMatrix(matrix);
|
|
4263
4426
|
}
|
|
4264
|
-
function fromMatrix(
|
|
4427
|
+
function fromMatrix(matrix) {
|
|
4265
4428
|
const [header, ...body] = matrix;
|
|
4266
|
-
if (!header) return {
|
|
4267
|
-
const
|
|
4268
|
-
const unmapped = columns.filter((c) => c.field === null).map((c) => c.header).filter((h) => h.trim().length > 0);
|
|
4429
|
+
if (!header) return { headers: [], rows: [] };
|
|
4430
|
+
const headers = header.map((text) => String(text ?? ""));
|
|
4269
4431
|
const rows = body.filter((line) => line.some((cell) => String(cell ?? "").trim().length > 0)).map((line) => {
|
|
4270
4432
|
const record = {};
|
|
4271
|
-
|
|
4272
|
-
if (
|
|
4433
|
+
headers.forEach((heading, index) => {
|
|
4434
|
+
if (heading.trim().length > 0) record[heading] = String(line[index] ?? "");
|
|
4273
4435
|
});
|
|
4274
4436
|
return record;
|
|
4275
4437
|
});
|
|
4276
|
-
return {
|
|
4438
|
+
return { headers, rows };
|
|
4277
4439
|
}
|
|
4278
4440
|
//# sourceMappingURL=index.cjs.map
|