laterite 0.5.1 → 0.6.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/dist/index.cjs +424 -82
- package/dist/index.d.mts +604 -101
- package/dist/index.d.ts +604 -101
- package/dist/index.mjs +441 -98
- package/index.d.ts +270 -143
- package/index.js +549 -269
- package/package.json +15 -19
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Table } from 'apache-arrow';
|
|
2
|
-
import {
|
|
3
|
-
export { AppliedFix, Finding, GroupMeta, version } from '#native';
|
|
2
|
+
import { Finding, AppliedFix, ValidationReport, Sidecar, Reading, displayHint, PackStats, UnpackStats, ExcelStats } from '#native';
|
|
3
|
+
export { AppliedFix, ExcelStats, Finding, GroupMeta, Sidecar, version } from '#native';
|
|
4
4
|
|
|
5
5
|
/** One query result row — JS-native values (`getRowObjectsJS`: Dates, numbers,
|
|
6
6
|
* nulls; 64-bit ints stay `bigint`). */
|
|
@@ -12,25 +12,161 @@ interface QueryOptions {
|
|
|
12
12
|
arrow?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The product of {@link fix} — the Node port of laterite-py's `FixResult`.
|
|
17
|
+
*
|
|
18
|
+
* `fix()` mechanically repairs an AGS4 document — always the *safe* set (CRLF /
|
|
19
|
+
* BOM / embedded-CR normalisation, short-row padding, numeric reformatting, the
|
|
20
|
+
* TRAN delimiter+concatenator rows), plus the intent-guessing *risky* set when
|
|
21
|
+
* asked — then re-validates the repaired bytes. This object is what that pass
|
|
22
|
+
* hands back: the fixed document, an account of what was changed, and an honest
|
|
23
|
+
* record of what it could not touch. Construction is internal; you receive one
|
|
24
|
+
* from `fix()`, you don't build it.
|
|
25
|
+
*
|
|
26
|
+
* The repair is non-destructive — nothing is written to disk by the fixer. The
|
|
27
|
+
* repaired document rides home on `.bytes`, decoded on demand via the `.text`
|
|
28
|
+
* getter (UTF-8), and persisted only when you choose to with `.save(path)`
|
|
29
|
+
* (which writes the bytes and returns the path). `applied` enumerates each fix
|
|
30
|
+
* that landed (`{kind, label, rule, line?, risk}` — serde snake_case, identical
|
|
31
|
+
* across Python / CLI / Node), with `.fixesApplied` as its count for a quick
|
|
32
|
+
* "did anything change?". Crucially, `findings` is *not* the input's problems —
|
|
33
|
+
* it is the residual after re-validation: the rule violations that survived the
|
|
34
|
+
* repair and still need a human. `dictVersion` records the AGS4 edition the fix
|
|
35
|
+
* resolved against. `toString()` gives a one-line summary (byte count, fixes
|
|
36
|
+
* applied, residual findings).
|
|
37
|
+
*/
|
|
38
|
+
declare class FixResult {
|
|
39
|
+
readonly bytes: Buffer;
|
|
40
|
+
/** Findings that remain after the fixes — what could NOT be mechanically fixed. */
|
|
41
|
+
readonly findings: Finding[];
|
|
42
|
+
/** The fixes that were applied (`{kind, label, rule, line?, risk}`). */
|
|
43
|
+
readonly applied: AppliedFix[];
|
|
44
|
+
readonly dictVersion: string;
|
|
45
|
+
constructor(bytes: Buffer,
|
|
46
|
+
/** Findings that remain after the fixes — what could NOT be mechanically fixed. */
|
|
47
|
+
findings: Finding[],
|
|
48
|
+
/** The fixes that were applied (`{kind, label, rule, line?, risk}`). */
|
|
49
|
+
applied: AppliedFix[], dictVersion: string);
|
|
50
|
+
/** How many fixes were applied. */
|
|
51
|
+
get fixesApplied(): number;
|
|
52
|
+
/** The repaired AGS4 document decoded as text. */
|
|
53
|
+
get text(): string;
|
|
54
|
+
/** Save the repaired bytes to `path`; returns `path`. */
|
|
55
|
+
save(path: string): string;
|
|
56
|
+
toString(): string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** One finding without its `rule` key — the value shape `byRule()` groups. */
|
|
60
|
+
type RuleFinding = Omit<Finding, "rule">;
|
|
61
|
+
/**
|
|
62
|
+
* The verdict from {@link validate} — the Node port of laterite-py's `Report`.
|
|
63
|
+
*
|
|
64
|
+
* `validate()` runs the numbered-rule engine over an AGS4 source and hands back
|
|
65
|
+
* one of these: an immutable wrapper over the native `ValidationReport`. It is a
|
|
66
|
+
* pure *result* object (it does not carry the file's bytes — that is what the
|
|
67
|
+
* read/build outputs are for); what it carries is the answer to "is this file
|
|
68
|
+
* conformant, and if not, why".
|
|
69
|
+
*
|
|
70
|
+
* Start with the headline getters. `isValid` is the one most callers branch on —
|
|
71
|
+
* it is `true` iff there are zero findings, which is deliberately *distinct* from
|
|
72
|
+
* the native `ok` flag (`ok` only means the source was parseable enough to
|
|
73
|
+
* validate). `count` is the number of findings, and `exitCode` mirrors what the
|
|
74
|
+
* `lat-check` binary would return for the same file, so a CLI wrapper can pass it
|
|
75
|
+
* straight through. The provenance getters — `file`, `dictVersion` (the AGS
|
|
76
|
+
* edition the rules were drawn from), and `resolution` — say *what* was checked
|
|
77
|
+
* and *against which dictionary*.
|
|
78
|
+
*
|
|
79
|
+
* The findings themselves come three ways. `findings` is the flat array in
|
|
80
|
+
* `lat-check` order (each `{rule, line?, group, desc, severity?}`). `byRule()`
|
|
81
|
+
* regroups them into the spec-rule map `{ "AGS Format Rule N": [...] }` for
|
|
82
|
+
* rule-oriented reporting. For machine output, `toJson()` and `toNdjson()` return
|
|
83
|
+
* strings byte-identical to `lat-check --json` / `--ndjson` (minted native-side),
|
|
84
|
+
* and `toString()` gives a one-line human summary.
|
|
85
|
+
*
|
|
86
|
+
* @see {@link validate} — the verb that produces a `Report`.
|
|
87
|
+
*/
|
|
88
|
+
declare class Report {
|
|
89
|
+
#private;
|
|
90
|
+
constructor(r: ValidationReport);
|
|
91
|
+
/** Synthesise a clean report from a fresh certificate — the engine-skipped
|
|
92
|
+
* outcome of `Ags4File.validate()` on an `index=`-certified file. `resolution`
|
|
93
|
+
* is the sentinel `"certified"` (the engine never emits it), `count` is 0, and
|
|
94
|
+
* the edition is the cert's. Mirrors laterite-py's `Report.from_cert`. */
|
|
95
|
+
static fromCert(cert: Sidecar, label: string): Report;
|
|
96
|
+
get file(): string;
|
|
97
|
+
get dictVersion(): string;
|
|
98
|
+
get resolution(): string;
|
|
99
|
+
get count(): number;
|
|
100
|
+
/** `true` iff there are zero findings (distinct from the native `ok`, which
|
|
101
|
+
* only means "validatable"). */
|
|
102
|
+
get isValid(): boolean;
|
|
103
|
+
get exitCode(): number;
|
|
104
|
+
/** All findings, in `lat-check` order: `{rule, line?, group, desc, severity?}`. */
|
|
105
|
+
get findings(): Finding[];
|
|
106
|
+
/** `{ "AGS Format Rule N": [{line?, group, desc, …}] }` — the spec-rule
|
|
107
|
+
* grouping (mirrors `Report.by_rule`). */
|
|
108
|
+
byRule(): Record<string, RuleFinding[]>;
|
|
109
|
+
/** `{file, findings:{…}}` pretty-JSON — byte-identical to `lat-check --json`. */
|
|
110
|
+
toJson(): string;
|
|
111
|
+
/** One flat `{rule, …}` per line — byte-identical to `lat-check --ndjson`. */
|
|
112
|
+
toNdjson(): string;
|
|
113
|
+
toString(): string;
|
|
114
|
+
}
|
|
115
|
+
|
|
15
116
|
/** A `(keyColumn, values)` filter, e.g. `["LOCA_ID", ["BH01", "BH02"]]`. */
|
|
16
117
|
type Filter = [key: string, values: unknown[]];
|
|
118
|
+
/**
|
|
119
|
+
* A key-filtered view over an `Ags4File`'s engine — the Node port of laterite-py's
|
|
120
|
+
* `_AgsSubset`, returned by `Ags4File.at()`. Filters ACCUMULATE by chaining
|
|
121
|
+
* (`at("LOCA", […]).at("SAMP", […])` keeps both): `table(code)` applies every
|
|
122
|
+
* filter whose key column is present in `code` (others ignored), so groups
|
|
123
|
+
* carrying none of the keys pass through unfiltered. Async, because the engine
|
|
124
|
+
* is promise-based (`@duckdb/node-api`).
|
|
125
|
+
*/
|
|
17
126
|
declare class AgsSubset {
|
|
18
127
|
#private;
|
|
19
128
|
constructor(parent: Ags4File, filters: Filter[]);
|
|
20
|
-
/**
|
|
129
|
+
/**
|
|
130
|
+
* Narrow further by another entity's id (e.g. add a SAMP filter on top of a
|
|
131
|
+
* LOCA one). Returns a fresh subset — filters accumulate, they never mutate.
|
|
132
|
+
*
|
|
133
|
+
* @param group - The group code whose `_ID` key to filter on (`"SAMP"` becomes
|
|
134
|
+
* the `SAMP_ID` key column).
|
|
135
|
+
* @param values - The ids to keep; a row survives when its `<group>_ID` is one
|
|
136
|
+
* of these.
|
|
137
|
+
* @returns A new `AgsSubset` carrying this filter plus all the existing ones.
|
|
138
|
+
*/
|
|
21
139
|
at(group: string, values: Iterable<unknown>): AgsSubset;
|
|
22
140
|
/** The related groups — those carrying at least one filter's key column. */
|
|
23
141
|
get groups(): string[];
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
142
|
+
/**
|
|
143
|
+
* `code` filtered by every applicable key (groups carrying none of the keys
|
|
144
|
+
* pass through unfiltered), as JS-native row objects by default — or a
|
|
145
|
+
* born-typed arrow-js `Table` with `{ arrow: true }`.
|
|
146
|
+
*
|
|
147
|
+
* @param code - The group to read (its clean name).
|
|
148
|
+
* @param opts - Query options; `{ arrow: true }` switches the return to an
|
|
149
|
+
* arrow-js `Table`.
|
|
150
|
+
* @returns The filtered rows as `Row[]`, or a `Table` when arrow output is
|
|
151
|
+
* requested.
|
|
152
|
+
* @throws If `{ arrow: true }` is set but DuckDB's `arrow` community extension
|
|
153
|
+
* can't be installed/loaded (offline or air-gapped).
|
|
154
|
+
*/
|
|
27
155
|
table(code: string): Promise<Row[]>;
|
|
28
156
|
table(code: string, opts: {
|
|
29
157
|
arrow: true;
|
|
30
158
|
}): Promise<Table>;
|
|
31
159
|
table(code: string, opts?: QueryOptions): Promise<Row[] | Table>;
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
160
|
+
/**
|
|
161
|
+
* `{group: rows}` for every related group, each filtered — a location's whole
|
|
162
|
+
* related record set in one call. Returns arrow-js `Table`s with
|
|
163
|
+
* `{ arrow: true }`, JS-native row objects otherwise.
|
|
164
|
+
*
|
|
165
|
+
* @param opts - Query options; `{ arrow: true }` makes each value a `Table`.
|
|
166
|
+
* @returns A map from group code to its filtered rows (or `Table`).
|
|
167
|
+
* @throws If `{ arrow: true }` is set but DuckDB's `arrow` community extension
|
|
168
|
+
* can't be installed/loaded (offline or air-gapped).
|
|
169
|
+
*/
|
|
34
170
|
frames(): Promise<Record<string, Row[]>>;
|
|
35
171
|
frames(opts: {
|
|
36
172
|
arrow: true;
|
|
@@ -38,39 +174,114 @@ declare class AgsSubset {
|
|
|
38
174
|
frames(opts?: QueryOptions): Promise<Record<string, Row[] | Table>>;
|
|
39
175
|
}
|
|
40
176
|
|
|
177
|
+
/** The source a handle was read from — retained so the chained `validate`/`fix`/
|
|
178
|
+
* `diff` verbs re-run against the TRUE bytes (matching original line numbers),
|
|
179
|
+
* not the byte-faithful `.bytes` re-emit. A synthesised handle has none. */
|
|
180
|
+
interface Ags4Source {
|
|
181
|
+
path?: string;
|
|
182
|
+
text?: string;
|
|
183
|
+
data?: Uint8Array;
|
|
184
|
+
encoding?: string;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* A parsed AGS4 file — the Node port of laterite-py's `Ags4File`. The read
|
|
188
|
+
* surface is **Arrow-direct**: `table(code)` decodes the native typed Arrow IPC
|
|
189
|
+
* straight to an arrow-js Table (no DuckDB round-trip). Python routes reads
|
|
190
|
+
* through DuckDB only because its pandas path is otherwise pyarrow-bound; Node
|
|
191
|
+
* has no such reason, so the base read surface needs no engine at all.
|
|
192
|
+
*
|
|
193
|
+
* The OPTIONAL DuckDB layer — `sql()` / `at()` / `connection` — is lazy: the
|
|
194
|
+
* engine spins up only on first use, and `@duckdb/node-api` is an optional peer
|
|
195
|
+
* (absent → a helpful install error). These are async (the Neo client is
|
|
196
|
+
* promise-based); everything else stays sync.
|
|
197
|
+
*/
|
|
41
198
|
declare class Ags4File {
|
|
42
199
|
#private;
|
|
43
|
-
constructor(reading: Reading);
|
|
200
|
+
constructor(reading: Reading, src?: Ags4Source);
|
|
201
|
+
/** @internal — `read(..., { index })` attaches a freshness-checked certificate
|
|
202
|
+
* so a later errors-only `.validate()` can skip the rule engine. */
|
|
203
|
+
_attachCert(cert: Sidecar): void;
|
|
44
204
|
/** Group codes in file order. */
|
|
45
205
|
get groups(): string[];
|
|
46
206
|
/** The file's `TRAN_AGS` edition string, if present. */
|
|
47
207
|
get tranAgs(): string | null;
|
|
208
|
+
/** The HEADING-row codes of `code`, in file order. Throws if `code` isn't in the file. */
|
|
48
209
|
headings(code: string): string[];
|
|
210
|
+
/** The UNIT row of `code`, one per heading. Throws if `code` isn't in the file. */
|
|
49
211
|
units(code: string): string[];
|
|
212
|
+
/** The TYPE row of `code` (the AGS data types), one per heading. Throws if `code` isn't in the file. */
|
|
50
213
|
types(code: string): string[];
|
|
51
214
|
/** AGS TYPE → the DuckDB column type each heading lands as (for the P3 engine). */
|
|
52
215
|
sqlTypes(code: string): string[];
|
|
53
216
|
/** 1-indexed source line of each DATA row. */
|
|
54
217
|
lineNumbers(code: string): number[];
|
|
218
|
+
/** Whether `code` is one of the file's groups — the cheap membership check `headings`/`table` would otherwise throw on. */
|
|
55
219
|
has(code: string): boolean;
|
|
56
220
|
/** One group as a born-typed arrow-js `Table` (a 2DP heading is Float64, an ID
|
|
57
221
|
* Utf8, a DT a Timestamp) — the SAME typing the Python/wasm hosts produce,
|
|
58
222
|
* byte-identical by construction (one shared `build_record_batch`). Cached per
|
|
59
|
-
* group. Throws if `code` isn't in the file.
|
|
60
|
-
|
|
223
|
+
* group. Throws if `code` isn't in the file.
|
|
224
|
+
*
|
|
225
|
+
* By default the synthetic `_id`/`_parent_id` key columns are **dropped**; pass
|
|
226
|
+
* `{ keys: true }` to include them (the relational `sql()`/`at()` layer always
|
|
227
|
+
* carries them regardless — that's what makes cross-group joins work). */
|
|
228
|
+
table(code: string, opts?: {
|
|
229
|
+
keys?: boolean;
|
|
230
|
+
}): Table;
|
|
61
231
|
/** Spec-correct AGS4 as text — byte-faithful to the source DATA values
|
|
62
232
|
* (re-emitted native-side from the retained parse). Memoised. */
|
|
63
233
|
get text(): string;
|
|
64
234
|
/** `text` encoded UTF-8 — the bytes `save()` writes. Memoised. */
|
|
65
235
|
get bytes(): Buffer;
|
|
66
|
-
/** Write the AGS4 to `path` (UTF-8)
|
|
236
|
+
/** Write the AGS4 to `path` (UTF-8) — the inverse of `read`. The bytes are
|
|
237
|
+
* byte-faithful to the source DATA values, re-emitted from the retained parse.
|
|
238
|
+
*
|
|
239
|
+
* @param path Filesystem path to write the UTF-8 AGS4 to.
|
|
240
|
+
* @returns The same `path`, for chaining. */
|
|
67
241
|
save(path: string): string;
|
|
242
|
+
/** The last `.validate()` outcome (`undefined` until validated). */
|
|
243
|
+
get report(): Report | undefined;
|
|
244
|
+
/** The `FixResult` on a handle returned by `.fix()` (`undefined` otherwise). */
|
|
245
|
+
get fixReport(): FixResult | undefined;
|
|
246
|
+
/** Validate this file against the AGS4 rules and return `this` (chainable —
|
|
247
|
+
* `read(p).validate().sql(...)`); the outcome lands on `.report`. Same engine as
|
|
248
|
+
* the free `validate()`, run on the source this handle was read from so line
|
|
249
|
+
* numbers match the original. Errors + WARNINGs by default (`warnings`); `fyi`
|
|
250
|
+
* adds the low-signal tier. `encoding` defaults to the one this handle was read
|
|
251
|
+
* with. Mirrors `laterite.Ags4File.validate()`. */
|
|
252
|
+
validate(opts?: Omit<ValidateOptions, "text">): this;
|
|
253
|
+
/** Mechanically repair this file and return a NEW, repaired `Ags4File` — the
|
|
254
|
+
* fluent transform, so `read(p).fix().validate().save(out)` reads as one chain.
|
|
255
|
+
* The `FixResult` (what was applied + residual findings) rides on the returned
|
|
256
|
+
* handle's `.fixReport`. Safe fixes always apply; `risky` adds the intent-
|
|
257
|
+
* guessing set. `encoding` defaults to this handle's read encoding. Non-
|
|
258
|
+
* destructive — the source on disk is untouched. Mirrors
|
|
259
|
+
* `laterite.Ags4File.fix()`. */
|
|
260
|
+
fix(opts?: Omit<FixOptions, "text">): Ags4File;
|
|
261
|
+
/** Compare this file (the baseline) against `other` (the revision) — the
|
|
262
|
+
* `RevisionDelta` the free `diff()` returns. `other` is a path, bytes, or another
|
|
263
|
+
* `Ags4File`. `encoding` defaults to this handle's read encoding. Mirrors
|
|
264
|
+
* `laterite.Ags4File.diff()`. */
|
|
265
|
+
diff(other: DiffSource, opts?: DiffOptions): RevisionDelta;
|
|
266
|
+
/** Mint this file's `.ags.idx` validity certificate — a clean-validation proof
|
|
267
|
+
* plus a byte-offset index — and write it beside the file. REQUIRES a prior
|
|
268
|
+
* clean `.validate()`: certify *vouches for* a passed validation, it does not run
|
|
269
|
+
* one. `path` is the certificate's OUTPUT location (default `<source>.idx`), not
|
|
270
|
+
* a file to certify — it refuses to overwrite the source or any existing
|
|
271
|
+
* non-certificate file. Returns the written path. The cert is byte-identical to
|
|
272
|
+
* Python's / `lat-check --emit-index`'s, so `lat-check` reads it, and a later
|
|
273
|
+
* `read(f, { index })` consumes it to skip re-validation. Mirrors
|
|
274
|
+
* `laterite.Ags4File.certify()`. */
|
|
275
|
+
certify(path?: string): string;
|
|
68
276
|
/** Run SQL over the file's groups by their clean names — e.g.
|
|
69
277
|
* `await ags.sql("SELECT * FROM SAMP JOIN LOCA USING (LOCA_ID) WHERE …")`.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
278
|
+
* Any group may be referenced, so this loads them all into the engine.
|
|
279
|
+
*
|
|
280
|
+
* @param query SQL referencing groups by their bare AGS code as table names.
|
|
281
|
+
* @param opts `{ arrow: true }` returns a born-typed arrow-js `Table` (loads the
|
|
282
|
+
* `arrow` community extension on first use) instead of JS-native rows.
|
|
283
|
+
* @returns JS-native row objects by default, or a `Table` when `arrow` is set.
|
|
284
|
+
* @throws If the optional `@duckdb/node-api` peer is absent. */
|
|
74
285
|
sql(query: string): Promise<Row[]>;
|
|
75
286
|
sql(query: string, opts: {
|
|
76
287
|
arrow: true;
|
|
@@ -80,7 +291,11 @@ declare class Ags4File {
|
|
|
80
291
|
* returns a view whose `table(code)` yields only the rows whose `{group}_ID`
|
|
81
292
|
* is in `values`. Chain to narrow (`.at("SAMP", […])`); `sub.groups` is the
|
|
82
293
|
* related groups, `sub.frames()` pulls them all. Groups carrying none of the
|
|
83
|
-
* keys pass through. For any other predicate, use `sql("… WHERE …")`.
|
|
294
|
+
* keys pass through. For any other predicate, use `sql("… WHERE …")`.
|
|
295
|
+
*
|
|
296
|
+
* @param group The parent group whose `{group}_ID` key drives the filter.
|
|
297
|
+
* @param values The id values to keep (empty matches nothing).
|
|
298
|
+
* @returns An `AgsSubset` view; further `.at()` calls accumulate filters. */
|
|
84
299
|
at(group: string, values: Iterable<unknown>): AgsSubset;
|
|
85
300
|
/** The raw `@duckdb/node-api` connection — every engine feature — seeded with
|
|
86
301
|
* all of this file's groups under their clean names. */
|
|
@@ -91,7 +306,9 @@ declare class Ags4File {
|
|
|
91
306
|
/** Drop the decoded-Table cache and close the DuckDB engine (if any).
|
|
92
307
|
* `using f = read(…)` runs this automatically. */
|
|
93
308
|
close(): void;
|
|
309
|
+
/** `using f = read(…)` disposal hook — delegates to `close()`. */
|
|
94
310
|
[Symbol.dispose](): void;
|
|
311
|
+
/** A compact one-line summary — group count and `TRAN_AGS` — for logs and the REPL. */
|
|
95
312
|
toString(): string;
|
|
96
313
|
}
|
|
97
314
|
|
|
@@ -103,11 +320,38 @@ interface BuildFinding {
|
|
|
103
320
|
desc?: string;
|
|
104
321
|
[key: string]: unknown;
|
|
105
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* The product of {@link buildAgs4} — the data→AGS4 door's return value, and the
|
|
325
|
+
* Node port of laterite-py's `BuildResult`. Where `read` hands you an *existing*
|
|
326
|
+
* file, `buildAgs4` *constructs* one from your data and then runs the output back
|
|
327
|
+
* through the validator; this object is what falls out of that round trip. It is
|
|
328
|
+
* a plain, inert carrier — no DuckDB, no native handle to hold open — so you can
|
|
329
|
+
* keep it, pass it around, or persist it at leisure.
|
|
330
|
+
*
|
|
331
|
+
* It carries three things. {@link BuildResult.bytes | `bytes`} is the AGS4
|
|
332
|
+
* document as the validator emitted it (UTF-8); reach for {@link BuildResult.text
|
|
333
|
+
* | `text`} when you want it decoded as a string, or {@link BuildResult.save |
|
|
334
|
+
* `save(path)`} to write the bytes straight to disk (it returns the path).
|
|
335
|
+
* {@link BuildResult.findings | `findings`} is the *residual* set of validator
|
|
336
|
+
* findings — what the build could **not** clear given the `mode` it ran under
|
|
337
|
+
* (e.g. `"autofix"` applies the safe fixes and leaves only what it can't touch,
|
|
338
|
+
* `"report"` records everything); each is a flat {@link BuildFinding} of `rule`
|
|
339
|
+
* plus whatever rich keys the validator set. {@link BuildResult.fixesApplied |
|
|
340
|
+
* `fixesApplied`} counts how many safe fixes were applied along the way, and
|
|
341
|
+
* {@link BuildResult.applied | `applied`} is the ledger of those fixes (each a
|
|
342
|
+
* `{kind, label, rule, line?, risk}` record, the same shape `fix()`'s
|
|
343
|
+
* `FixResult.applied` carries). A clean build is an empty `findings` array; a
|
|
344
|
+
* non-empty one tells you exactly what the emitted document still trips on.
|
|
345
|
+
*/
|
|
106
346
|
declare class BuildResult {
|
|
107
347
|
readonly bytes: Buffer;
|
|
108
348
|
readonly findings: BuildFinding[];
|
|
349
|
+
/** The safe fixes AutoFix applied (`{kind, label, rule, line?, risk}`); empty outside `"autofix"`. */
|
|
350
|
+
readonly applied: AppliedFix[];
|
|
109
351
|
readonly fixesApplied: number;
|
|
110
|
-
constructor(bytes: Buffer, findings: BuildFinding[],
|
|
352
|
+
constructor(bytes: Buffer, findings: BuildFinding[],
|
|
353
|
+
/** The safe fixes AutoFix applied (`{kind, label, rule, line?, risk}`); empty outside `"autofix"`. */
|
|
354
|
+
applied: AppliedFix[], fixesApplied: number);
|
|
111
355
|
/** The AGS4 document decoded as text. */
|
|
112
356
|
get text(): string;
|
|
113
357
|
/** Save the bytes to `path`; returns `path`. */
|
|
@@ -115,52 +359,6 @@ declare class BuildResult {
|
|
|
115
359
|
toString(): string;
|
|
116
360
|
}
|
|
117
361
|
|
|
118
|
-
declare class FixResult {
|
|
119
|
-
readonly bytes: Buffer;
|
|
120
|
-
/** Findings that remain after the fixes — what could NOT be mechanically fixed. */
|
|
121
|
-
readonly findings: Finding[];
|
|
122
|
-
/** The fixes that were applied (`{kind, label, rule, line?, risk}`). */
|
|
123
|
-
readonly applied: AppliedFix[];
|
|
124
|
-
readonly dictVersion: string;
|
|
125
|
-
constructor(bytes: Buffer,
|
|
126
|
-
/** Findings that remain after the fixes — what could NOT be mechanically fixed. */
|
|
127
|
-
findings: Finding[],
|
|
128
|
-
/** The fixes that were applied (`{kind, label, rule, line?, risk}`). */
|
|
129
|
-
applied: AppliedFix[], dictVersion: string);
|
|
130
|
-
/** How many fixes were applied. */
|
|
131
|
-
get fixesApplied(): number;
|
|
132
|
-
/** The repaired AGS4 document decoded as text. */
|
|
133
|
-
get text(): string;
|
|
134
|
-
/** Save the repaired bytes to `path`; returns `path`. */
|
|
135
|
-
save(path: string): string;
|
|
136
|
-
toString(): string;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** One finding without its `rule` key — the value shape `byRule()` groups. */
|
|
140
|
-
type RuleFinding = Omit<Finding, "rule">;
|
|
141
|
-
declare class Report {
|
|
142
|
-
#private;
|
|
143
|
-
constructor(r: ValidationReport);
|
|
144
|
-
get file(): string;
|
|
145
|
-
get dictVersion(): string;
|
|
146
|
-
get resolution(): string;
|
|
147
|
-
get count(): number;
|
|
148
|
-
/** `true` iff there are zero findings (distinct from the native `ok`, which
|
|
149
|
-
* only means "validatable"). */
|
|
150
|
-
get isValid(): boolean;
|
|
151
|
-
get exitCode(): number;
|
|
152
|
-
/** All findings, in `lat-check` order: `{rule, line?, group, desc, severity?}`. */
|
|
153
|
-
get findings(): Finding[];
|
|
154
|
-
/** `{ "AGS Format Rule N": [{line?, group, desc, …}] }` — the spec-rule
|
|
155
|
-
* grouping (mirrors `Report.by_rule`). */
|
|
156
|
-
byRule(): Record<string, RuleFinding[]>;
|
|
157
|
-
/** `{file, findings:{…}}` pretty-JSON — byte-identical to `lat-check --json`. */
|
|
158
|
-
toJson(): string;
|
|
159
|
-
/** One flat `{rule, …}` per line — byte-identical to `lat-check --ndjson`. */
|
|
160
|
-
toNdjson(): string;
|
|
161
|
-
toString(): string;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
362
|
declare abstract class AgsGroup {
|
|
165
363
|
}
|
|
166
364
|
|
|
@@ -4550,20 +4748,41 @@ declare class UnsupportedEditionError extends Ags4Error {
|
|
|
4550
4748
|
declare class BadDictError extends Ags4Error {
|
|
4551
4749
|
constructor(message: string, exitCode?: number);
|
|
4552
4750
|
}
|
|
4751
|
+
/** A passed `index=` certificate (`.ags.idx`) does not match the file it was read
|
|
4752
|
+
* for — its size / SHA-256 differ, so its byte offsets and clean verdict are now
|
|
4753
|
+
* lies. Raised at `read` time (fail-fast): an explicit `index=` asserts "this cert
|
|
4754
|
+
* is for this file", so a mismatch is an error, never a silent fall-back. Rebuild
|
|
4755
|
+
* it (`read(p).validate().certify()`). (#294 Batch E / #14) */
|
|
4756
|
+
declare class StaleCertError extends Ags4Error {
|
|
4757
|
+
constructor(message: string, exitCode?: number);
|
|
4758
|
+
}
|
|
4553
4759
|
|
|
4554
4760
|
/** Cross-system target categories — the lowercase labels the engine returns. */
|
|
4555
4761
|
type CanonicalType = "string" | "integer" | "decimal" | "datetime" | "date" | "time" | "bool" | "enum";
|
|
4556
4762
|
/** A parsed AGS value: number (integer/decimal), boolean (YN), string
|
|
4557
4763
|
* (text/enum **and** the canonical datetime/date/time strings), or null. */
|
|
4558
4764
|
type AgsValue = string | number | boolean | null;
|
|
4559
|
-
/**
|
|
4560
|
-
*
|
|
4765
|
+
/**
|
|
4766
|
+
* AGS spec type code → canonical category. Throws for unknown codes (mirrors
|
|
4767
|
+
* Python's `ValueError`), so the engine's permissive `null` never leaks into
|
|
4768
|
+
* caller code as a silent miss.
|
|
4769
|
+
*
|
|
4770
|
+
* @param agsType - An AGS4 spec type code (e.g. `"2DP"`, `"ID"`, `"YN"`, `"DT"`).
|
|
4771
|
+
* @returns The lowercase canonical category the engine maps that code to.
|
|
4772
|
+
* @throws {Ags4Error} If the code is not a recognised AGS type.
|
|
4773
|
+
*/
|
|
4561
4774
|
declare function canonicalType(agsType: string): CanonicalType;
|
|
4562
4775
|
|
|
4563
|
-
/**
|
|
4776
|
+
/**
|
|
4777
|
+
* Parse an AGS4-shaped raw value into its canonical JS value (empty /
|
|
4564
4778
|
* unparseable → null). datetime/date/time come back as the canonical **string**
|
|
4565
4779
|
* (engine shape; `new Date(s)` if you want a Date). Non-string input is
|
|
4566
|
-
* stringified first (matches the Python wrapper).
|
|
4780
|
+
* stringified first (matches the Python wrapper).
|
|
4781
|
+
*
|
|
4782
|
+
* @param raw - The raw cell value; non-string input is coerced via `String(...)`, and `null`/`undefined` short-circuit to `null`.
|
|
4783
|
+
* @param agsType - The AGS4 spec type code governing how `raw` is interpreted.
|
|
4784
|
+
* @returns The canonical value: a number (integer/decimal), boolean (YN), string (text/enum and the datetime/date/time strings), or `null`.
|
|
4785
|
+
*/
|
|
4567
4786
|
declare function parseValue(raw: unknown, agsType: string): AgsValue;
|
|
4568
4787
|
|
|
4569
4788
|
type agsTypes_AgsValue = AgsValue;
|
|
@@ -4596,6 +4815,10 @@ type Heading = GeneratedHeading;
|
|
|
4596
4815
|
* dictionary carries combined statuses like `KEY+REQUIRED`, so a bare
|
|
4597
4816
|
* `status === "KEY"` would wrongly miss them (matches the Rust/Python check). */
|
|
4598
4817
|
declare function isKeyStatus(status: string): boolean;
|
|
4818
|
+
/** A typed, immutable view onto one standard AGS group: its 4-letter `code`,
|
|
4819
|
+
* human `contents` description, `parent` code (or `null` for a root group), and
|
|
4820
|
+
* its ordered `headings`. Wraps a single entry from the generated dictionary so
|
|
4821
|
+
* the registry never hands out mutable raw rows. */
|
|
4599
4822
|
declare class GroupDescriptor {
|
|
4600
4823
|
readonly code: string;
|
|
4601
4824
|
readonly contents: string;
|
|
@@ -4606,21 +4829,71 @@ declare class GroupDescriptor {
|
|
|
4606
4829
|
get table(): string;
|
|
4607
4830
|
/** DuckDB view name (`v_<code>`). */
|
|
4608
4831
|
get view(): string;
|
|
4832
|
+
/** This group's KEY headings (status part-matched via {@link isKeyStatus}), in
|
|
4833
|
+
* declaration order. */
|
|
4609
4834
|
get keyHeadings(): readonly Heading[];
|
|
4835
|
+
/** This group's non-KEY headings — the complement of {@link keyHeadings} — in
|
|
4836
|
+
* declaration order. */
|
|
4610
4837
|
get nonKeyHeadings(): readonly Heading[];
|
|
4611
4838
|
}
|
|
4612
4839
|
/** Every standard AGS group, keyed by 4-letter code. */
|
|
4613
4840
|
declare const GROUPS: Readonly<Record<string, GroupDescriptor>>;
|
|
4614
4841
|
/** Single-group lookup; `undefined` for unknown codes. */
|
|
4615
4842
|
declare function get(code: string): GroupDescriptor | undefined;
|
|
4843
|
+
/** One heading in a {@link DictionarySnapshot} group (`type` is the AGS data type). */
|
|
4844
|
+
interface DictHeading {
|
|
4845
|
+
name: string;
|
|
4846
|
+
status: string;
|
|
4847
|
+
type: string;
|
|
4848
|
+
unit?: string;
|
|
4849
|
+
description: string;
|
|
4850
|
+
}
|
|
4851
|
+
/** One group in a {@link DictionarySnapshot}. */
|
|
4852
|
+
interface DictGroup {
|
|
4853
|
+
code: string;
|
|
4854
|
+
contents: string;
|
|
4855
|
+
parent?: string;
|
|
4856
|
+
headings: DictHeading[];
|
|
4857
|
+
}
|
|
4858
|
+
/** The bundled standard dictionary for one edition. */
|
|
4859
|
+
interface DictionarySnapshot {
|
|
4860
|
+
ags_edition: string;
|
|
4861
|
+
groups: DictGroup[];
|
|
4862
|
+
}
|
|
4863
|
+
/**
|
|
4864
|
+
* The bundled STANDARD dictionary for one AGS `edition` — the per-edition view
|
|
4865
|
+
* of the official dictionary (canonical group + heading names, descriptions,
|
|
4866
|
+
* UNIT/TYPE, status). Where {@link GROUPS} is the *union* registry across all
|
|
4867
|
+
* editions (the default), this is a single edition's standard dictionary — the
|
|
4868
|
+
* same content the browser and `laterite.registry.dictionary()` render, from
|
|
4869
|
+
* one shared Rust builder (#294 F#6).
|
|
4870
|
+
*
|
|
4871
|
+
* @param edition `"4.0.3" | "4.0.4" | "4.1" | "4.1.1" | "4.2"`; omit (or
|
|
4872
|
+
* `"auto"`) for the fallback edition.
|
|
4873
|
+
* @throws {Error} if `edition` is not a recognised edition.
|
|
4874
|
+
*/
|
|
4875
|
+
declare function dictionary(edition?: string): DictionarySnapshot;
|
|
4616
4876
|
/** Every direct child group of `parentCode`, alphabetically. */
|
|
4617
4877
|
declare function childGroups(parentCode: string): GroupDescriptor[];
|
|
4618
4878
|
/** Parent chain from `code` to root: `[code, parent, …, root]`. Throws for an
|
|
4619
4879
|
* unknown code (so root groups — `[code]` — are distinguishable). */
|
|
4620
4880
|
declare function ancestorChain(code: string): string[];
|
|
4621
|
-
/** KEY heading names a group inherits from its
|
|
4881
|
+
/** KEY heading names a group inherits from its **direct parent** — the
|
|
4882
|
+
* intersection of this group's KEY headings with its immediate parent's. This
|
|
4883
|
+
* matches the Rust/Python `inherited_key_names` (NOT the whole ancestor chain):
|
|
4884
|
+
* because AGS re-declares inherited keys at every level, the direct-parent
|
|
4885
|
+
* intersection already captures every key a group carries from above, so an
|
|
4886
|
+
* ancestor-chain union would only add ancestor keys the group doesn't have
|
|
4887
|
+
* (e.g. `PROJ_ID` on `SAMP`).
|
|
4888
|
+
*
|
|
4889
|
+
* @param code The group whose inherited KEY names to gather.
|
|
4890
|
+
* @returns The set of KEY heading names shared with the direct parent (empty for a root).
|
|
4891
|
+
* @throws {Ags4Error} If `code` isn't in the registry. */
|
|
4622
4892
|
declare function inheritedKeyNames(code: string): Set<string>;
|
|
4623
4893
|
|
|
4894
|
+
type registry_DictGroup = DictGroup;
|
|
4895
|
+
type registry_DictHeading = DictHeading;
|
|
4896
|
+
type registry_DictionarySnapshot = DictionarySnapshot;
|
|
4624
4897
|
declare const registry_GROUPS: typeof GROUPS;
|
|
4625
4898
|
type registry_GroupDescriptor = GroupDescriptor;
|
|
4626
4899
|
declare const registry_GroupDescriptor: typeof GroupDescriptor;
|
|
@@ -4628,21 +4901,56 @@ type registry_Heading = Heading;
|
|
|
4628
4901
|
type registry_HeadingStatus = HeadingStatus;
|
|
4629
4902
|
declare const registry_ancestorChain: typeof ancestorChain;
|
|
4630
4903
|
declare const registry_childGroups: typeof childGroups;
|
|
4904
|
+
declare const registry_dictionary: typeof dictionary;
|
|
4631
4905
|
declare const registry_get: typeof get;
|
|
4632
4906
|
declare const registry_inheritedKeyNames: typeof inheritedKeyNames;
|
|
4633
4907
|
declare const registry_isKeyStatus: typeof isKeyStatus;
|
|
4634
4908
|
declare namespace registry {
|
|
4635
|
-
export { registry_GROUPS as GROUPS, registry_GroupDescriptor as GroupDescriptor, type registry_Heading as Heading, type registry_HeadingStatus as HeadingStatus, registry_ancestorChain as ancestorChain, registry_childGroups as childGroups, registry_get as get, registry_inheritedKeyNames as inheritedKeyNames, registry_isKeyStatus as isKeyStatus };
|
|
4909
|
+
export { type registry_DictGroup as DictGroup, type registry_DictHeading as DictHeading, type registry_DictionarySnapshot as DictionarySnapshot, registry_GROUPS as GROUPS, registry_GroupDescriptor as GroupDescriptor, type registry_Heading as Heading, type registry_HeadingStatus as HeadingStatus, registry_ancestorChain as ancestorChain, registry_childGroups as childGroups, registry_dictionary as dictionary, registry_get as get, registry_inheritedKeyNames as inheritedKeyNames, registry_isKeyStatus as isKeyStatus };
|
|
4636
4910
|
}
|
|
4637
4911
|
|
|
4638
|
-
/**
|
|
4912
|
+
/**
|
|
4913
|
+
* zstd-compress `src` → `dest` for transport — content-agnostic, so it works on
|
|
4914
|
+
* any file (an `.ags` transfer, an `.ags5db`, anything), not just AGS data.
|
|
4915
|
+
*
|
|
4916
|
+
* @param src - Path to the file to compress.
|
|
4917
|
+
* @param dest - Path the compressed output is written to.
|
|
4918
|
+
* @param level - zstd level 1–22 (default 9, the empirical sweet spot on AGS
|
|
4919
|
+
* data — higher levels buy minutes, not bytes).
|
|
4920
|
+
* @returns The output size, compression ratio vs source, and elapsed seconds.
|
|
4921
|
+
*/
|
|
4639
4922
|
declare function pack(src: string, dest: string, level?: number): PackStats;
|
|
4640
|
-
/**
|
|
4923
|
+
/**
|
|
4924
|
+
* zstd-decompress a `.zst` produced by {@link pack} back to its original bytes.
|
|
4925
|
+
*
|
|
4926
|
+
* @param src - Path to the compressed `.zst` file.
|
|
4927
|
+
* @param dest - Path the decompressed output is written to.
|
|
4928
|
+
* @returns The output size and elapsed seconds.
|
|
4929
|
+
*/
|
|
4641
4930
|
declare function unpack(src: string, dest: string): UnpackStats;
|
|
4642
|
-
/**
|
|
4931
|
+
/**
|
|
4932
|
+
* zstd-compress, then age-passphrase-encrypt `src` → `dest`. Compress-then-
|
|
4933
|
+
* encrypt is load-bearing: zstd needs low-entropy input, and ciphertext is
|
|
4934
|
+
* random — so the order can't flip. The age envelope is interoperable with the
|
|
4935
|
+
* Python side (pyrage) and `lat-db lock`, all linking the same Rust `age` crate.
|
|
4936
|
+
*
|
|
4937
|
+
* @param src - Path to the file to compress and encrypt.
|
|
4938
|
+
* @param dest - Path the encrypted output is written to.
|
|
4939
|
+
* @param password - Passphrase for the age envelope (scrypt + ChaCha20-Poly1305).
|
|
4940
|
+
* @param level - zstd level 1–22 (default 9).
|
|
4941
|
+
* @returns The output size, compression ratio vs source, and elapsed seconds.
|
|
4942
|
+
*/
|
|
4643
4943
|
declare function lock(src: string, dest: string, password: string, level?: number): PackStats;
|
|
4644
|
-
/**
|
|
4645
|
-
* passphrase
|
|
4944
|
+
/**
|
|
4945
|
+
* age-passphrase-decrypt, then zstd-decompress a `.zst.age` produced by
|
|
4946
|
+
* {@link lock} back to its original bytes.
|
|
4947
|
+
*
|
|
4948
|
+
* @param src - Path to the encrypted `.zst.age` file.
|
|
4949
|
+
* @param dest - Path the recovered output is written to.
|
|
4950
|
+
* @param password - Passphrase the envelope was sealed with.
|
|
4951
|
+
* @returns The output size and elapsed seconds.
|
|
4952
|
+
* @throws If the passphrase is wrong or the input is not a passphrase envelope.
|
|
4953
|
+
*/
|
|
4646
4954
|
declare function unlock(src: string, dest: string, password: string): UnpackStats;
|
|
4647
4955
|
|
|
4648
4956
|
declare const transport_PackStats: typeof PackStats;
|
|
@@ -4660,12 +4968,37 @@ interface ReadOptions {
|
|
|
4660
4968
|
text?: string;
|
|
4661
4969
|
/** Source encoding label (`"utf-8"` default, `"windows-1252"`, …). */
|
|
4662
4970
|
encoding?: string;
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4971
|
+
/** Path to this file's `.ags.idx` certificate (minted by `Ags4File.certify()`).
|
|
4972
|
+
* Opt-in, no autodiscovery — naming it asserts the cert is for THIS file. A
|
|
4973
|
+
* fresh cert is carried so a later errors-only `.validate()` skips the rule
|
|
4974
|
+
* engine; a size/SHA mismatch throws {@link StaleCertError}. (#294 Batch E / #14) */
|
|
4975
|
+
index?: string;
|
|
4976
|
+
}
|
|
4977
|
+
/**
|
|
4978
|
+
* Parse AGS4 into an `Ags4File`. Where the data→AGS4 door (`buildAgs4`)
|
|
4979
|
+
* *constructs* a file, `read` *loads* one that already exists — from a file
|
|
4980
|
+
* `source` path, raw `Uint8Array`/`Buffer` bytes, or in-memory `opts.text`.
|
|
4981
|
+
*
|
|
4982
|
+
* Prefer bytes over a string for large inputs: V8 caps a single string at
|
|
4983
|
+
* ~512 MB, but a `Uint8Array` does not, so a web backend can hand a
|
|
4984
|
+
* multi-hundred-MB upload straight in without first stringifying it. A string
|
|
4985
|
+
* `source` is the file path; non-string bytes are the raw content; an absent
|
|
4986
|
+
* `source` means the input came in as `opts.text`.
|
|
4987
|
+
*
|
|
4988
|
+
* The engine never returns a soft failure for un-parseable input — it throws a
|
|
4989
|
+
* typed error (the same exit-code identity as `lat-check`) so callers branch on
|
|
4990
|
+
* the class, not a brittle message match.
|
|
4991
|
+
*
|
|
4992
|
+
* @param source - File path (string), raw `Uint8Array`/`Buffer` bytes, or
|
|
4993
|
+
* omitted when the input is supplied via `opts.text`.
|
|
4994
|
+
* @param opts - Read options ({@link ReadOptions}): in-memory `text` and the
|
|
4995
|
+
* source `encoding` label (default `"utf-8"`).
|
|
4996
|
+
* @returns An {@link Ags4File} wrapping the parsed groups.
|
|
4997
|
+
* @throws {FileNotFoundError} The path could not be opened.
|
|
4998
|
+
* @throws {NotAgs4Error} The input has no GROUP rows / is not decodable as AGS4.
|
|
4999
|
+
* @throws {UnsupportedEditionError} A recognised but unsupported edition (e.g. AGS3).
|
|
5000
|
+
* @throws {Ags4Error} Any other native parse failure (the fallback mapping).
|
|
5001
|
+
*/
|
|
4669
5002
|
declare function read(source?: string | Uint8Array, opts?: ReadOptions): Ags4File;
|
|
4670
5003
|
interface ValidateOptions extends ReadOptions {
|
|
4671
5004
|
/** Force an edition (`"4.0.3"`…`"4.2"`); default auto-detects from `TRAN_AGS`. */
|
|
@@ -4678,9 +5011,29 @@ interface ValidateOptions extends ReadOptions {
|
|
|
4678
5011
|
checkFiles?: boolean;
|
|
4679
5012
|
}
|
|
4680
5013
|
/** Validate AGS4 — a file `source` path, raw `Uint8Array`/`Buffer` bytes, or
|
|
4681
|
-
* in-memory `text` — against the AGS4 rules
|
|
4682
|
-
*
|
|
4683
|
-
*
|
|
5014
|
+
* in-memory `text` (via `opts.text`) — against the numbered AGS4 rules, returning
|
|
5015
|
+
* a `Report`. The crucial distinction: rule *violations* are data, not errors —
|
|
5016
|
+
* they come back inside the `Report` (`.findings`, `.byRule()`, `.isValid`); only
|
|
5017
|
+
* *un-validatable* input (missing file, not AGS4, AGS3, bad dictionary) throws.
|
|
5018
|
+
* By default the report is the error-tier findings; opt warnings and FYIs in with
|
|
5019
|
+
* `opts.warnings` / `opts.fyi`. The edition auto-detects from `TRAN_AGS` unless
|
|
5020
|
+
* pinned with `opts.dictVersion`. Pass bytes (not a string) for large inputs:
|
|
5021
|
+
* V8 caps strings at ~512 MB but a `Uint8Array` does not, so a web backend can
|
|
5022
|
+
* hand a multi-hundred-MB upload straight in — the same byte door as `read`.
|
|
5023
|
+
* Mirrors `laterite.validate()` / the `lat-check` binary.
|
|
5024
|
+
*
|
|
5025
|
+
* @param source File path (string), or raw bytes (`Uint8Array`/`Buffer`); omit to
|
|
5026
|
+
* validate `opts.text` instead.
|
|
5027
|
+
* @param opts Validation knobs (`ValidateOptions`) — the dictionary-version pin
|
|
5028
|
+
* (`dictVersion`), severity gates, in-memory text/encoding, and the on-disk
|
|
5029
|
+
* Rule-20 toggle; see the interface for each field.
|
|
5030
|
+
* @returns A `Report` carrying the findings, the resolved `dictVersion`, the
|
|
5031
|
+
* finding `count`, `isValid`, and `lat-check`-faithful `toJson()` / `toNdjson()`.
|
|
5032
|
+
* @throws {FileNotFoundError} the path could not be opened.
|
|
5033
|
+
* @throws {NotAgs4Error} the input has no GROUP rows / is not decodable AGS4.
|
|
5034
|
+
* @throws {UnsupportedEditionError} a recognised-but-unsupported edition (AGS3).
|
|
5035
|
+
* @throws {BadDictError} an invalid `opts.dictVersion` / unimplemented dictionary.
|
|
5036
|
+
*/
|
|
4684
5037
|
declare function validate(source?: string | Uint8Array, opts?: ValidateOptions): Report;
|
|
4685
5038
|
/** Row-oriented group data: an array of `{HEADING: value}` objects. */
|
|
4686
5039
|
type GroupRows = Array<Record<string, unknown>>;
|
|
@@ -4691,14 +5044,42 @@ interface EmitOptions {
|
|
|
4691
5044
|
dictVersion?: string;
|
|
4692
5045
|
/** `"autofix"` (default) | `"report"` | `"strict"`. */
|
|
4693
5046
|
mode?: "autofix" | "report" | "strict";
|
|
4694
|
-
}
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
5047
|
+
/** Per-heading UNIT overrides, keyed `{ code: { heading: unit } }` (#294 F#9)
|
|
5048
|
+
* — e.g. `{ LOCA: { LOCA_XTRA: "kPa" } }`. Name only the headings you want to
|
|
5049
|
+
* set; the rest fill from the dictionary. Throws on an unknown code/heading. */
|
|
5050
|
+
units?: Record<string, Record<string, string>>;
|
|
5051
|
+
/** Per-heading AGS data-TYPE overrides, same `{ code: { heading: type } }` shape. */
|
|
5052
|
+
types?: Record<string, Record<string, string>>;
|
|
5053
|
+
}
|
|
5054
|
+
/**
|
|
5055
|
+
* Build valid AGS4 from your own data — the data→AGS4 door. Where `read` loads
|
|
5056
|
+
* an *existing* file, `buildAgs4` *constructs* a new one: it lays the groups out
|
|
5057
|
+
* in order, fills UNIT/TYPE from the chosen `dictVersion`, then runs the output
|
|
5058
|
+
* through the validator (the `mode` knob on `opts` decides what happens to the
|
|
5059
|
+
* findings — `"autofix"` applies the safe fixes *and* synthesizes any missing
|
|
5060
|
+
* UNIT/TYPE/TRAN/ABBR metadata group so a data-only build is valid; `"report"` merely
|
|
5061
|
+
* records them). The returned `BuildResult` carries the bytes, the residual `findings`,
|
|
5062
|
+
* and a `fixesApplied` count; persist it with `BuildResult.save`. Needs no DuckDB.
|
|
5063
|
+
*
|
|
5064
|
+
* `groups` accepts two shapes. A **typed-graph root** (`new PROJ({…, locas:[new
|
|
5065
|
+
* LOCA({…})]})`) is walked depth-first via the registry's parent→child links,
|
|
5066
|
+
* only the headings you set becoming columns (entirely-unset ones are dropped,
|
|
5067
|
+
* except KEY). The walk covers only
|
|
5068
|
+
* PROJ's subtree (the root-metadata groups have no parent), but under `"autofix"`
|
|
5069
|
+
* the missing UNIT/TYPE/TRAN (and ABBR for PA codes) are synthesized, so a
|
|
5070
|
+
* typed-graph build is valid out of the box. Or pass a **Map / array of `[code,
|
|
5071
|
+
* data]`** entries where `data` is
|
|
5072
|
+
* an arrow-js `Table` or row objects whose **keys are the AGS headings**
|
|
5073
|
+
* (`LOCA_ID`, …). Either way group order is preserved, so put `PROJ` first.
|
|
5074
|
+
*
|
|
5075
|
+
* @param groups The data to emit — a typed-graph root (`new PROJ({…})`), or a
|
|
5076
|
+
* `Map`/array of `[groupCode, Table | rowObjects]` entries (headings as keys).
|
|
5077
|
+
* @param opts Emit options (`dictVersion`, `mode`); see {@link EmitOptions}.
|
|
5078
|
+
* @returns A {@link BuildResult} — `.bytes`/`.text` of the AGS4 document, the
|
|
5079
|
+
* `findings` it could not fix, the `fixesApplied` count, and `.save(path)`.
|
|
5080
|
+
* @throws {Ags4Error} If a typed-graph node is not a registered AGS group.
|
|
5081
|
+
* @throws If the native emitter rejects the input (e.g. an unknown `dictVersion`).
|
|
5082
|
+
*/
|
|
4702
5083
|
declare function buildAgs4(groups: AgsGroup | Map<string, GroupData> | Array<[string, GroupData]>, opts?: EmitOptions): BuildResult;
|
|
4703
5084
|
/** One cited divergence observation on a rule (`{id, note}`). */
|
|
4704
5085
|
interface RuleObservation {
|
|
@@ -4714,9 +5095,20 @@ interface RuleMeta {
|
|
|
4714
5095
|
fixable: boolean;
|
|
4715
5096
|
observations: RuleObservation[];
|
|
4716
5097
|
}
|
|
4717
|
-
/**
|
|
4718
|
-
*
|
|
4719
|
-
*
|
|
5098
|
+
/**
|
|
5099
|
+
* The AGS4 rule catalogue — one `RuleMeta` per numbered rule, surfaced so a
|
|
5100
|
+
* caller can show *which* rules exist and how each behaves before (or instead
|
|
5101
|
+
* of) running `validate`/`fix` over a file. Each entry carries the rule id and
|
|
5102
|
+
* title, a one-line `checks` summary, its `severity`, whether `fix` can
|
|
5103
|
+
* mechanically repair it (`fixable`), and any cited `O-N` divergence
|
|
5104
|
+
* observations. Mirrors `laterite.list_rules()` / `lat-check --list-rules`;
|
|
5105
|
+
* backed by the gated `rules_meta.json` (the catalogue is static, so this takes
|
|
5106
|
+
* no input and reads no file).
|
|
5107
|
+
*
|
|
5108
|
+
* @returns The full rule catalogue as a `RuleMeta[]` — see `RuleMeta` for the
|
|
5109
|
+
* per-entry shape (`rule`, `title`, `checks`, `severity`, `fixable`,
|
|
5110
|
+
* `observations`).
|
|
5111
|
+
*/
|
|
4720
5112
|
declare function listRules(): RuleMeta[];
|
|
4721
5113
|
interface FixOptions {
|
|
4722
5114
|
/** Repair in-memory `text` instead of a file path. */
|
|
@@ -4728,11 +5120,122 @@ interface FixOptions {
|
|
|
4728
5120
|
/** Also apply the intent-guessing (risky) fixes, not just the safe set. */
|
|
4729
5121
|
risky?: boolean;
|
|
4730
5122
|
}
|
|
4731
|
-
/** Mechanically repair AGS4 —
|
|
4732
|
-
*
|
|
4733
|
-
*
|
|
4734
|
-
*
|
|
4735
|
-
* `
|
|
5123
|
+
/** Mechanically repair AGS4 — the headless twin of the browser's Fix engine.
|
|
5124
|
+
* `source` is a file path, raw `Uint8Array`/`Buffer` bytes, or (via `opts.text`)
|
|
5125
|
+
* in-memory text. The *safe* fixes — CRLF / BOM / embedded-CR normalisation,
|
|
5126
|
+
* short-row padding, numeric reformatting, and the TRAN delimiter+concatenator
|
|
5127
|
+
* rows — are always applied; pass `risky` to also run the intent-guessing set
|
|
5128
|
+
* (duplicate-heading rename, `dd/mm` datetime canonicalisation, smart-quote→ASCII
|
|
5129
|
+
* typography). The repaired bytes are re-validated, so `FixResult.findings` is
|
|
5130
|
+
* what could NOT be mechanically fixed.
|
|
5131
|
+
*
|
|
5132
|
+
* Non-destructive: nothing is written here — the repaired bytes come back on the
|
|
5133
|
+
* result (`.bytes` / `.text` / `.save(path)`), already UTF-8 with no BOM, so
|
|
5134
|
+
* fixing a non-UTF-8 file also normalises its encoding. Mirrors `laterite.fix()`
|
|
5135
|
+
* / `lat-check --fix`.
|
|
5136
|
+
*
|
|
5137
|
+
* @param source - The AGS4 input: a filesystem path (`string`) or raw bytes
|
|
5138
|
+
* (`Uint8Array`/`Buffer`). Omit to repair `opts.text` instead.
|
|
5139
|
+
* @param opts - {@link FixOptions} — `text` source, `risky` fixes, `dictVersion`
|
|
5140
|
+
* override, and source `encoding`.
|
|
5141
|
+
* @returns A {@link FixResult} carrying the repaired `bytes` (and `.text` /
|
|
5142
|
+
* `.save`), the `applied` fixes (with `fixesApplied` count), the residual
|
|
5143
|
+
* `findings` left after re-validation, and the resolved `dictVersion`.
|
|
5144
|
+
* @throws {Ags4Error} (or a subclass — {@link FileNotFoundError},
|
|
5145
|
+
* {@link NotAgs4Error}, {@link UnsupportedEditionError}, {@link BadDictError})
|
|
5146
|
+
* for un-fixable input, carrying the matching `lat-check` exit code.
|
|
5147
|
+
*/
|
|
4736
5148
|
declare function fix(source?: string | Uint8Array, opts?: FixOptions): FixResult;
|
|
5149
|
+
/** One changed cell of a matched row (`kind === "changed"`). `type` is the AGS
|
|
5150
|
+
* data type; `a`/`b` are the raw values on each side (`null` if that side's row
|
|
5151
|
+
* is shorter than the heading list). Snake_case fields mirror the wire shape the
|
|
5152
|
+
* shared `laterite-ags4-diff` leaf serialises — identical to Python's dict. */
|
|
5153
|
+
interface CellDelta {
|
|
5154
|
+
heading: string;
|
|
5155
|
+
type: string;
|
|
5156
|
+
a: string | null;
|
|
5157
|
+
b: string | null;
|
|
5158
|
+
}
|
|
5159
|
+
/** One row's verdict: `added` (only in `b`), `removed` (only in `a`), or
|
|
5160
|
+
* `changed` (matched by KEY, ≥1 typed cell differs). `key` is the KEY values (or
|
|
5161
|
+
* whole-row tuple when unkeyed); `cells` is populated only for `changed`. */
|
|
5162
|
+
interface RowDelta {
|
|
5163
|
+
kind: "added" | "removed" | "changed";
|
|
5164
|
+
key: string[];
|
|
5165
|
+
line_a: number | null;
|
|
5166
|
+
line_b: number | null;
|
|
5167
|
+
cells: CellDelta[];
|
|
5168
|
+
}
|
|
5169
|
+
/** A group's deltas. `added`/`removed`/`changed` are true totals; `keyed` is
|
|
5170
|
+
* false when matched on the whole-row tuple (no dictionary KEY headings). */
|
|
5171
|
+
interface GroupDelta {
|
|
5172
|
+
code: string;
|
|
5173
|
+
added: number;
|
|
5174
|
+
removed: number;
|
|
5175
|
+
changed: number;
|
|
5176
|
+
headings_added: string[];
|
|
5177
|
+
headings_removed: string[];
|
|
5178
|
+
keyed: boolean;
|
|
5179
|
+
key_headings: string[];
|
|
5180
|
+
rows: RowDelta[];
|
|
5181
|
+
}
|
|
5182
|
+
/** The revision diff — the shape `diff()` returns (parsed from the shared
|
|
5183
|
+
* `laterite-ags4-diff` leaf's JSON; byte-identical to Python / wasm / `lat-check
|
|
5184
|
+
* --diff`). */
|
|
5185
|
+
interface RevisionDelta {
|
|
5186
|
+
groups: GroupDelta[];
|
|
5187
|
+
groups_added: string[];
|
|
5188
|
+
groups_removed: string[];
|
|
5189
|
+
total_added: number;
|
|
5190
|
+
total_removed: number;
|
|
5191
|
+
total_changed: number;
|
|
5192
|
+
}
|
|
5193
|
+
interface DiffOptions {
|
|
5194
|
+
/** Force the edition used to resolve each group's KEY headings (`"4.0.3"`…
|
|
5195
|
+
* `"4.2"`); default takes it from the revision's `TRAN_AGS`. */
|
|
5196
|
+
dictVersion?: string;
|
|
5197
|
+
/** Source encoding label for path / bytes inputs (default `"utf-8"`). */
|
|
5198
|
+
encoding?: string;
|
|
5199
|
+
}
|
|
5200
|
+
/** A diff input: a file path (`string`), raw bytes, or an already-read `Ags4File`. */
|
|
5201
|
+
type DiffSource = string | Uint8Array | Ags4File;
|
|
5202
|
+
/** Compare two AGS4 documents and return their **revision diff** — the Node port
|
|
5203
|
+
* of `laterite.diff()` and the browser's revision-diff tool, over the SAME shared
|
|
5204
|
+
* `laterite-ags4-diff` engine `lat-check --diff` uses.
|
|
5205
|
+
*
|
|
5206
|
+
* `a` (baseline) and `b` (revision) are each a path, raw `Uint8Array`/`Buffer`
|
|
5207
|
+
* bytes, or an already-read `Ags4File`. Two choices make the diff meaningful
|
|
5208
|
+
* rather than noisy: rows are matched by the group's dictionary **KEY** headings
|
|
5209
|
+
* (not line order — re-sorted boreholes still pair up), and cells are compared
|
|
5210
|
+
* through the **typed** value (a formatting-only edit like `"1.0"` → `"1.00"` is
|
|
5211
|
+
* not a diff). The KEY-heading edition is the revision's `TRAN_AGS` unless pinned
|
|
5212
|
+
* with `opts.dictVersion`.
|
|
5213
|
+
*
|
|
5214
|
+
* @param a - The baseline document (path / bytes / `Ags4File`).
|
|
5215
|
+
* @param b - The revision document, in any of the same forms.
|
|
5216
|
+
* @param opts - {@link DiffOptions} — the `dictVersion` pin and source `encoding`.
|
|
5217
|
+
* @returns A {@link RevisionDelta}: per-group row/heading deltas, `groups_added`/
|
|
5218
|
+
* `groups_removed`, and the `total_added`/`total_removed`/`total_changed` counts.
|
|
5219
|
+
* @throws {FileNotFoundError} a path input could not be opened.
|
|
5220
|
+
* @throws {NotAgs4Error} either side is not decodable AGS4.
|
|
5221
|
+
* @throws {BadDictError} an invalid `opts.dictVersion`.
|
|
5222
|
+
*/
|
|
5223
|
+
declare function diff(a: DiffSource, b: DiffSource, opts?: DiffOptions): RevisionDelta;
|
|
5224
|
+
/**
|
|
5225
|
+
* Write an AGS4 file's groups to an `.xlsx` — one worksheet per group (the
|
|
5226
|
+
* Node analog of Python's `to_excel`). `groups` forces the worksheet order;
|
|
5227
|
+
* otherwise AGS4 source order is preserved. Returns the conversion stats.
|
|
5228
|
+
*/
|
|
5229
|
+
declare function toExcel(agsPath: string, xlsxPath: string, opts?: {
|
|
5230
|
+
groups?: string[];
|
|
5231
|
+
}): ExcelStats;
|
|
5232
|
+
/**
|
|
5233
|
+
* Read an `.xlsx` back into an AGS4 file (the Node analog of `from_excel`).
|
|
5234
|
+
* `formatNumericColumns` (default true) re-applies AGS4 numeric formatting to
|
|
5235
|
+
* numeric-looking columns. Returns the conversion stats.
|
|
5236
|
+
*/
|
|
5237
|
+
declare function fromExcel(xlsxPath: string, agsPath: string, opts?: {
|
|
5238
|
+
formatNumericColumns?: boolean;
|
|
5239
|
+
}): ExcelStats;
|
|
4737
5240
|
|
|
4738
|
-
export { AAVT, ABBR, ACVT, AELO, AFLK, AIVT, ALOS, APSV, ARTW, ASDI, ASNS, AWAD, Ags4Error, Ags4File, AgsGroup, AgsSubset, type AgsValue, BKFL, BadDictError, type BuildFinding, BuildResult, CBRG, CBRP, CBRT, CDIA, CHIS, CHOC, CMPG, CMPT, CONG, CONS, CORE, CPDG, CPDT, CPTG, CPTM, CPTP, CPTT, CPTY, CPTZ, CTRC, CTRD, CTRG, CTRP, CTRS, type CanonicalType, DCPG, DCPT, DETL, DICT, DISC, DLOG, DMDG, DMDT, DMTG, DMTP, DMTT, DMTZ, DOBS, DPRB, DPRG, DREM, ECTN, ELRG, ERES, ESCG, ESCT, type EmitOptions, FGHG, FGHI, FGHS, FGHT, FILE, FLSH, FRAC, FRST, FileNotFoundError, type Filter, type FixOptions, FixResult, GCHM, GEOL, GRAG, GRAT, type GroupData, GroupDescriptor, type GroupRows, HDIA, HDPH, HORN, type Heading, type HeadingStatus, ICBR, IDEN, IFID, IPEN, IPID, IPRG, IPRT, IRDX, IRES, ISAG, ISAT, ISPT, ISTA, ISTG, ISTR, ISTS, ITCH, IVAN, LBSG, LBST, LDEN, LDYN, LFCN, LLIN, LLPL, LNMC, LOCA, LPDN, LPEN, LRES, LSLT, LSTG, LSTT, LSWL, LTCH, LUCT, LVAN, MCVG, MCVT, MOND, MONG, MONS, NotAgs4Error, PIPE, PLTG, PLTT, PMMC, PMMD, PMMG, PMTD, PMTG, PMTL, PMTP, PMTZ, PREM, PROJ, PTIM, PTST, PUMG, PUMT, type QueryOptions, RCAG, RCAT, RCCV, RDEN, RELD, RESC, RESD, RESG, RESP, RESS, RPLT, RSCH, RSHR, RTEN, RUCS, RWCO, type ReadOptions, Report, type Row, type RuleFinding, type RuleMeta, type RuleObservation, SAMP, SCDG, SCDT, SCPG, SCPP, SCPT, SHBG, SHBT, STND, SUCT, TNPC, TRAN, TREG, TREM, TRET, TRIG, TRIT, TYPE, UNIT, UnsupportedEditionError, type ValidateOptions, WADD, WETH, WGPG, WGPT, WINS, WSTD, WSTG, agsTypes, buildAgs4, fix, listRules, read, registry, transport, validate };
|
|
5241
|
+
export { AAVT, ABBR, ACVT, AELO, AFLK, AIVT, ALOS, APSV, ARTW, ASDI, ASNS, AWAD, Ags4Error, Ags4File, AgsGroup, AgsSubset, type AgsValue, BKFL, BadDictError, type BuildFinding, BuildResult, CBRG, CBRP, CBRT, CDIA, CHIS, CHOC, CMPG, CMPT, CONG, CONS, CORE, CPDG, CPDT, CPTG, CPTM, CPTP, CPTT, CPTY, CPTZ, CTRC, CTRD, CTRG, CTRP, CTRS, type CanonicalType, type CellDelta, DCPG, DCPT, DETL, DICT, DISC, DLOG, DMDG, DMDT, DMTG, DMTP, DMTT, DMTZ, DOBS, DPRB, DPRG, DREM, type DiffOptions, type DiffSource, ECTN, ELRG, ERES, ESCG, ESCT, type EmitOptions, FGHG, FGHI, FGHS, FGHT, FILE, FLSH, FRAC, FRST, FileNotFoundError, type Filter, type FixOptions, FixResult, GCHM, GEOL, GRAG, GRAT, type GroupData, type GroupDelta, GroupDescriptor, type GroupRows, HDIA, HDPH, HORN, type Heading, type HeadingStatus, ICBR, IDEN, IFID, IPEN, IPID, IPRG, IPRT, IRDX, IRES, ISAG, ISAT, ISPT, ISTA, ISTG, ISTR, ISTS, ITCH, IVAN, LBSG, LBST, LDEN, LDYN, LFCN, LLIN, LLPL, LNMC, LOCA, LPDN, LPEN, LRES, LSLT, LSTG, LSTT, LSWL, LTCH, LUCT, LVAN, MCVG, MCVT, MOND, MONG, MONS, NotAgs4Error, PIPE, PLTG, PLTT, PMMC, PMMD, PMMG, PMTD, PMTG, PMTL, PMTP, PMTZ, PREM, PROJ, PTIM, PTST, PUMG, PUMT, type QueryOptions, RCAG, RCAT, RCCV, RDEN, RELD, RESC, RESD, RESG, RESP, RESS, RPLT, RSCH, RSHR, RTEN, RUCS, RWCO, type ReadOptions, Report, type RevisionDelta, type Row, type RowDelta, type RuleFinding, type RuleMeta, type RuleObservation, SAMP, SCDG, SCDT, SCPG, SCPP, SCPT, SHBG, SHBT, STND, SUCT, StaleCertError, TNPC, TRAN, TREG, TREM, TRET, TRIG, TRIT, TYPE, UNIT, UnsupportedEditionError, type ValidateOptions, WADD, WETH, WGPG, WGPT, WINS, WSTD, WSTG, agsTypes, buildAgs4, diff, fix, fromExcel, listRules, read, registry, toExcel, transport, validate };
|