cesr-ts 0.4.0 → 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/esm/src/adapters/async-iterable.js +8 -0
- package/esm/src/adapters/effection.js +6 -0
- package/esm/src/annotate/annotator.js +12 -0
- package/esm/src/annotate/cli.js +12 -0
- package/esm/src/annotate/comments.js +5 -1
- package/esm/src/annotate/denot.js +6 -0
- package/esm/src/annotate/render.js +15 -3
- package/esm/src/core/bytes.js +9 -0
- package/esm/src/core/errors.js +3 -0
- package/esm/src/core/parser-engine.js +1 -1
- package/esm/src/core/parser-frame-parser.js +22 -18
- package/esm/src/core/vocabulary.js +47 -0
- package/esm/src/index.js +5 -0
- package/esm/src/parser/attachment-parser.js +4 -0
- package/esm/src/parser/cold-start.js +6 -0
- package/esm/src/parser/group-dispatch.js +2 -2
- package/esm/src/primitives/aggor.js +374 -30
- package/esm/src/primitives/bexter.js +2 -3
- package/esm/src/primitives/blinder.js +6 -0
- package/esm/src/primitives/byte-like.js +19 -0
- package/esm/src/primitives/cigar.js +17 -3
- package/esm/src/primitives/cipher.js +91 -6
- package/esm/src/primitives/codex.js +39 -6
- package/esm/src/primitives/compactor.js +307 -3
- package/esm/src/primitives/counter.js +6 -2
- package/esm/src/primitives/dater.js +1 -2
- package/esm/src/primitives/decimer.js +7 -11
- package/esm/src/primitives/decrypter.js +123 -7
- package/esm/src/primitives/diger.js +57 -3
- package/esm/src/primitives/disclosure.js +300 -0
- package/esm/src/primitives/encrypter.js +130 -7
- package/esm/src/primitives/hydrate.js +49 -0
- package/esm/src/primitives/ilker.js +2 -3
- package/esm/src/primitives/indexer.js +21 -4
- package/esm/src/primitives/labeler.js +1 -2
- package/esm/src/primitives/mapper.js +612 -35
- package/esm/src/primitives/matter.js +133 -3
- package/esm/src/primitives/mediar.js +6 -0
- package/esm/src/primitives/noncer.js +2 -3
- package/esm/src/primitives/number.js +1 -2
- package/esm/src/primitives/pather.js +102 -5
- package/esm/src/primitives/prefixer.js +1 -2
- package/esm/src/primitives/primitive.js +14 -0
- package/esm/src/primitives/registry.js +2 -0
- package/esm/src/primitives/saider.js +80 -3
- package/esm/src/primitives/salter.js +96 -2
- package/esm/src/primitives/sealed-box.js +41 -0
- package/esm/src/primitives/seqner.js +1 -2
- package/esm/src/primitives/siger.js +58 -3
- package/esm/src/primitives/signer.js +243 -9
- package/esm/src/primitives/streamer.js +28 -0
- package/esm/src/primitives/structing.js +727 -0
- package/esm/src/primitives/structor.js +20 -0
- package/esm/src/primitives/tagger.js +1 -2
- package/esm/src/primitives/texter.js +1 -2
- package/esm/src/primitives/tholder.js +552 -10
- package/esm/src/primitives/traitor.js +2 -3
- package/esm/src/primitives/verfer.js +50 -6
- package/esm/src/primitives/verser.js +16 -6
- package/esm/src/router/router-stub.js +1 -0
- package/esm/src/serder/native.js +1326 -0
- package/esm/src/serder/serder.js +1558 -59
- package/esm/src/serder/serdery.js +92 -7
- package/esm/src/serder/smell.js +26 -1
- package/esm/src/tables/codex-utils.js +2 -0
- package/esm/src/tables/counter-codex.js +2 -0
- package/esm/src/tables/counter-groups.js +6 -0
- package/esm/src/tables/versions.js +4 -0
- package/esm/src/version.js +5 -2
- package/package.json +5 -2
- package/types/src/adapters/async-iterable.d.ts +8 -0
- package/types/src/adapters/async-iterable.d.ts.map +1 -1
- package/types/src/adapters/effection.d.ts +7 -0
- package/types/src/adapters/effection.d.ts.map +1 -1
- package/types/src/annotate/annotator.d.ts +12 -0
- package/types/src/annotate/annotator.d.ts.map +1 -1
- package/types/src/annotate/cli.d.ts +13 -0
- package/types/src/annotate/cli.d.ts.map +1 -1
- package/types/src/annotate/comments.d.ts +4 -0
- package/types/src/annotate/comments.d.ts.map +1 -1
- package/types/src/annotate/denot.d.ts +6 -0
- package/types/src/annotate/denot.d.ts.map +1 -1
- package/types/src/annotate/render.d.ts.map +1 -1
- package/types/src/annotate/types.d.ts +2 -0
- package/types/src/annotate/types.d.ts.map +1 -1
- package/types/src/bench/parser-benchmark.d.ts +3 -0
- package/types/src/bench/parser-benchmark.d.ts.map +1 -1
- package/types/src/core/bytes.d.ts +9 -0
- package/types/src/core/bytes.d.ts.map +1 -1
- package/types/src/core/errors.d.ts +3 -0
- package/types/src/core/errors.d.ts.map +1 -1
- package/types/src/core/parser-engine.d.ts +4 -0
- package/types/src/core/parser-engine.d.ts.map +1 -1
- package/types/src/core/parser-frame-parser.d.ts.map +1 -1
- package/types/src/core/types.d.ts +2 -0
- package/types/src/core/types.d.ts.map +1 -1
- package/types/src/core/vocabulary.d.ts +48 -0
- package/types/src/core/vocabulary.d.ts.map +1 -0
- package/types/src/index.d.ts +5 -0
- package/types/src/index.d.ts.map +1 -1
- package/types/src/parser/attachment-parser.d.ts +4 -0
- package/types/src/parser/attachment-parser.d.ts.map +1 -1
- package/types/src/parser/cold-start.d.ts +6 -0
- package/types/src/parser/cold-start.d.ts.map +1 -1
- package/types/src/parser/group-dispatch.d.ts.map +1 -1
- package/types/src/primitives/aggor.d.ts +100 -13
- package/types/src/primitives/aggor.d.ts.map +1 -1
- package/types/src/primitives/bexter.d.ts.map +1 -1
- package/types/src/primitives/blinder.d.ts +6 -0
- package/types/src/primitives/blinder.d.ts.map +1 -1
- package/types/src/primitives/byte-like.d.ts +7 -0
- package/types/src/primitives/byte-like.d.ts.map +1 -0
- package/types/src/primitives/cigar.d.ts +11 -1
- package/types/src/primitives/cigar.d.ts.map +1 -1
- package/types/src/primitives/cipher.d.ts +73 -2
- package/types/src/primitives/cipher.d.ts.map +1 -1
- package/types/src/primitives/codex.d.ts +34 -4
- package/types/src/primitives/codex.d.ts.map +1 -1
- package/types/src/primitives/compactor.d.ts +94 -2
- package/types/src/primitives/compactor.d.ts.map +1 -1
- package/types/src/primitives/counter.d.ts.map +1 -1
- package/types/src/primitives/dater.d.ts.map +1 -1
- package/types/src/primitives/decimer.d.ts.map +1 -1
- package/types/src/primitives/decrypter.d.ts +64 -3
- package/types/src/primitives/decrypter.d.ts.map +1 -1
- package/types/src/primitives/diger.d.ts +16 -0
- package/types/src/primitives/diger.d.ts.map +1 -1
- package/types/src/primitives/disclosure.d.ts +201 -0
- package/types/src/primitives/disclosure.d.ts.map +1 -0
- package/types/src/primitives/encrypter.d.ts +66 -4
- package/types/src/primitives/encrypter.d.ts.map +1 -1
- package/types/src/primitives/hydrate.d.ts +20 -0
- package/types/src/primitives/hydrate.d.ts.map +1 -0
- package/types/src/primitives/ilker.d.ts.map +1 -1
- package/types/src/primitives/indexer.d.ts.map +1 -1
- package/types/src/primitives/labeler.d.ts.map +1 -1
- package/types/src/primitives/mapper.d.ts +132 -11
- package/types/src/primitives/mapper.d.ts.map +1 -1
- package/types/src/primitives/matter.d.ts +33 -1
- package/types/src/primitives/matter.d.ts.map +1 -1
- package/types/src/primitives/mediar.d.ts +6 -0
- package/types/src/primitives/mediar.d.ts.map +1 -1
- package/types/src/primitives/noncer.d.ts.map +1 -1
- package/types/src/primitives/number.d.ts.map +1 -1
- package/types/src/primitives/pather.d.ts +21 -0
- package/types/src/primitives/pather.d.ts.map +1 -1
- package/types/src/primitives/prefixer.d.ts.map +1 -1
- package/types/src/primitives/primitive.d.ts +26 -0
- package/types/src/primitives/primitive.d.ts.map +1 -1
- package/types/src/primitives/registry.d.ts +3 -0
- package/types/src/primitives/registry.d.ts.map +1 -1
- package/types/src/primitives/saider.d.ts +41 -0
- package/types/src/primitives/saider.d.ts.map +1 -1
- package/types/src/primitives/salter.d.ts +56 -1
- package/types/src/primitives/salter.d.ts.map +1 -1
- package/types/src/primitives/sealed-box.d.ts +29 -0
- package/types/src/primitives/sealed-box.d.ts.map +1 -0
- package/types/src/primitives/seqner.d.ts.map +1 -1
- package/types/src/primitives/siger.d.ts +14 -0
- package/types/src/primitives/siger.d.ts.map +1 -1
- package/types/src/primitives/signer.d.ts +80 -4
- package/types/src/primitives/signer.d.ts.map +1 -1
- package/types/src/primitives/streamer.d.ts +31 -0
- package/types/src/primitives/streamer.d.ts.map +1 -0
- package/types/src/primitives/structing.d.ts +4114 -0
- package/types/src/primitives/structing.d.ts.map +1 -0
- package/types/src/primitives/structor.d.ts +6 -0
- package/types/src/primitives/structor.d.ts.map +1 -1
- package/types/src/primitives/tagger.d.ts.map +1 -1
- package/types/src/primitives/texter.d.ts.map +1 -1
- package/types/src/primitives/tholder.d.ts +150 -5
- package/types/src/primitives/tholder.d.ts.map +1 -1
- package/types/src/primitives/traitor.d.ts.map +1 -1
- package/types/src/primitives/unknown.d.ts +1 -0
- package/types/src/primitives/unknown.d.ts.map +1 -1
- package/types/src/primitives/verfer.d.ts +12 -0
- package/types/src/primitives/verfer.d.ts.map +1 -1
- package/types/src/primitives/verser.d.ts +14 -3
- package/types/src/primitives/verser.d.ts.map +1 -1
- package/types/src/router/router-stub.d.ts +3 -0
- package/types/src/router/router-stub.d.ts.map +1 -1
- package/types/src/serder/native.d.ts +115 -0
- package/types/src/serder/native.d.ts.map +1 -0
- package/types/src/serder/serder.d.ts +187 -9
- package/types/src/serder/serder.d.ts.map +1 -1
- package/types/src/serder/serdery.d.ts +42 -0
- package/types/src/serder/serdery.d.ts.map +1 -1
- package/types/src/serder/smell.d.ts +22 -0
- package/types/src/serder/smell.d.ts.map +1 -1
- package/types/src/tables/codex-utils.d.ts +3 -0
- package/types/src/tables/codex-utils.d.ts.map +1 -1
- package/types/src/tables/counter-codex.d.ts +4 -0
- package/types/src/tables/counter-codex.d.ts.map +1 -1
- package/types/src/tables/counter-groups.d.ts +6 -0
- package/types/src/tables/counter-groups.d.ts.map +1 -1
- package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
- package/types/src/tables/indexer.codex.generated.d.ts.map +1 -1
- package/types/src/tables/matter.codex.generated.d.ts.map +1 -1
- package/types/src/tables/table-types.d.ts +6 -0
- package/types/src/tables/table-types.d.ts.map +1 -1
- package/types/src/tables/versions.d.ts +6 -0
- package/types/src/tables/versions.d.ts.map +1 -1
- package/types/src/version.d.ts +5 -2
- package/types/src/version.d.ts.map +1 -1
|
@@ -1,22 +1,49 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decode as decodeMsgpack, encode as encodeMsgpack } from "@msgpack/msgpack";
|
|
2
|
+
import { b, codeB2ToB64, codeB64ToB2, t } from "../core/bytes.js";
|
|
3
|
+
import { decodeKeriCbor, encodeKeriCbor } from "../core/cbor.js";
|
|
4
|
+
import { DeserializeError, SemanticInterpretationError, SerializeError, ShortageError, SyntaxParseError, UnknownCodeError, } from "../core/errors.js";
|
|
2
5
|
import { parseAttachmentDispatch } from "../parser/group-dispatch.js";
|
|
6
|
+
import { CtrDexV2 } from "../tables/counter-codex.js";
|
|
3
7
|
import { AGGOR_LIST_CODES, AGGOR_MAP_CODES } from "../tables/counter-groups.js";
|
|
4
|
-
import {
|
|
8
|
+
import { MATTER_SIZES } from "../tables/matter.tables.generated.js";
|
|
9
|
+
import { Bexter } from "./bexter.js";
|
|
10
|
+
import { BEXTER_CODES, DECIMAL_CODES, ESCAPE_CODES, LabelDex, LABELER_CODES, MtrDex } from "./codex.js";
|
|
11
|
+
import { Counter, parseCounter } from "./counter.js";
|
|
12
|
+
import { Decimer } from "./decimer.js";
|
|
13
|
+
import { Diger } from "./diger.js";
|
|
5
14
|
import { parseLabeler } from "./labeler.js";
|
|
6
|
-
import { parseMatter } from "./matter.js";
|
|
15
|
+
import { Matter, parseMatter } from "./matter.js";
|
|
16
|
+
import { Texter } from "./texter.js";
|
|
17
|
+
/** Deep-clone helper so semantic constructors never mutate caller-provided SAD input. */
|
|
18
|
+
function cloneValue(value) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.map((entry) => cloneValue(entry));
|
|
21
|
+
}
|
|
22
|
+
if (value && typeof value === "object") {
|
|
23
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cloneValue(v)]));
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
7
27
|
function tokenSize(token, domain) {
|
|
8
28
|
return domain === "bny" ? token.fullSizeB2 : token.fullSize;
|
|
9
29
|
}
|
|
10
30
|
function isMapGroupCode(code) {
|
|
11
31
|
return AGGOR_MAP_CODES.has(code);
|
|
12
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Normalize qb2 map bytes into the canonical qb64 text form used internally.
|
|
35
|
+
*
|
|
36
|
+
* This follows the broader native-serder rule that qb64 text is the maintainer
|
|
37
|
+
* source of truth even when the original transport was qb2.
|
|
38
|
+
*/
|
|
39
|
+
function canonicalizeNativeRaw(input, version) {
|
|
40
|
+
if (input.length > 0 && input[0] === "-".charCodeAt(0)) {
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
const counter = parseCounter(input, version, "bny");
|
|
44
|
+
return b(codeB2ToB64(input, counter.fullSize + counter.count * 4));
|
|
45
|
+
}
|
|
13
46
|
function parseCounterProbe(input, version, domain) {
|
|
14
|
-
/**
|
|
15
|
-
* Syntax probe only: attempt known major versions without committing semantic
|
|
16
|
-
* meaning until dispatch selection succeeds.
|
|
17
|
-
*/
|
|
18
|
-
// Probe both active and known majors so map parsing remains robust when
|
|
19
|
-
// streams mix legacy and current counters in nested structures.
|
|
20
47
|
const attempts = [
|
|
21
48
|
version,
|
|
22
49
|
{ major: 2, minor: 0 },
|
|
@@ -40,7 +67,8 @@ function parseCounterProbe(input, version, domain) {
|
|
|
40
67
|
return null;
|
|
41
68
|
}
|
|
42
69
|
function parseLabelProbe(input, domain) {
|
|
43
|
-
|
|
70
|
+
// A failed label parse is not automatically an error. In a native map
|
|
71
|
+
// payload, it may simply mean the next token is a value rather than a key.
|
|
44
72
|
try {
|
|
45
73
|
return parseLabeler(input, domain);
|
|
46
74
|
}
|
|
@@ -56,12 +84,8 @@ function parseLabelProbe(input, domain) {
|
|
|
56
84
|
}
|
|
57
85
|
}
|
|
58
86
|
function parseMapperValueSyntax(input, version, domain) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
* If the value is itself a map-group counter, recurse into
|
|
63
|
-
* `parseMapperBodySyntax` and attach nested syntax in `children`.
|
|
64
|
-
*/
|
|
87
|
+
// Grouped list/map values always start with a counter token, so probe that
|
|
88
|
+
// shape before falling back to plain Matter parsing.
|
|
65
89
|
const counter = parseCounterProbe(input, version, domain);
|
|
66
90
|
if (counter) {
|
|
67
91
|
const dispatch = parseAttachmentDispatch(input, version, domain);
|
|
@@ -85,12 +109,8 @@ function parseMapperValueSyntax(input, version, domain) {
|
|
|
85
109
|
};
|
|
86
110
|
}
|
|
87
111
|
function parseMapPayloadSyntax(input, version, domain, start, end) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
*
|
|
91
|
-
* This phase does not pair labels with values; pairing is deferred to
|
|
92
|
-
* `interpretMapperBodySyntax`.
|
|
93
|
-
*/
|
|
112
|
+
// This phase is syntax-only: it tokenizes "label" and "value" artifacts
|
|
113
|
+
// without yet enforcing that every label is paired with a value.
|
|
94
114
|
const entries = [];
|
|
95
115
|
let offset = start;
|
|
96
116
|
while (offset < end) {
|
|
@@ -177,9 +197,558 @@ export function parseMapperBodySyntax(input, version, domain) {
|
|
|
177
197
|
throw error;
|
|
178
198
|
}
|
|
179
199
|
}
|
|
200
|
+
function encodeTag(text) {
|
|
201
|
+
const tags = new Map([
|
|
202
|
+
[1, LabelDex.Tag1],
|
|
203
|
+
[2, LabelDex.Tag2],
|
|
204
|
+
[3, LabelDex.Tag3],
|
|
205
|
+
[4, LabelDex.Tag4],
|
|
206
|
+
[5, LabelDex.Tag5],
|
|
207
|
+
[6, LabelDex.Tag6],
|
|
208
|
+
[7, LabelDex.Tag7],
|
|
209
|
+
[8, LabelDex.Tag8],
|
|
210
|
+
[9, LabelDex.Tag9],
|
|
211
|
+
[10, LabelDex.Tag10],
|
|
212
|
+
[11, LabelDex.Tag11],
|
|
213
|
+
]);
|
|
214
|
+
const code = tags.get(text.length);
|
|
215
|
+
if (!code) {
|
|
216
|
+
throw new SerializeError(`Unsupported mapper tag length=${text.length}`);
|
|
217
|
+
}
|
|
218
|
+
const pad = code === LabelDex.Tag1 || code === LabelDex.Tag5 || code === LabelDex.Tag9
|
|
219
|
+
? "_"
|
|
220
|
+
: "";
|
|
221
|
+
return `${code}${pad}${text}`;
|
|
222
|
+
}
|
|
223
|
+
/** Encode base64-safe text through the compact StrB64/Bexter families. */
|
|
224
|
+
function encodeBext(text) {
|
|
225
|
+
const rem = text.length % 4;
|
|
226
|
+
const code = rem === 0
|
|
227
|
+
? LabelDex.StrB64_L0
|
|
228
|
+
: rem === 1
|
|
229
|
+
? LabelDex.StrB64_L1
|
|
230
|
+
: LabelDex.StrB64_L2;
|
|
231
|
+
const raw = Bexter.rawify(text);
|
|
232
|
+
return new Bexter({ code, raw }).qb64;
|
|
233
|
+
}
|
|
234
|
+
/** Encode arbitrary UTF-8 text through CESR bytes-label families. */
|
|
235
|
+
function encodeBytes(text) {
|
|
236
|
+
const raw = b(text);
|
|
237
|
+
const rem = raw.length % 3;
|
|
238
|
+
const code = rem === 0
|
|
239
|
+
? LabelDex.Bytes_L0
|
|
240
|
+
: rem === 1
|
|
241
|
+
? LabelDex.Bytes_L1
|
|
242
|
+
: LabelDex.Bytes_L2;
|
|
243
|
+
return new Texter({ code, raw }).qb64;
|
|
244
|
+
}
|
|
180
245
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
246
|
+
* Choose the narrowest native text encoding that can round-trip `text`.
|
|
247
|
+
*
|
|
248
|
+
* Read this as the mapper-side "smallest truthful token" rule:
|
|
249
|
+
* tag first for tiny safe labels/text, then StrB64, then raw bytes.
|
|
250
|
+
*/
|
|
251
|
+
function encodeText(text) {
|
|
252
|
+
if (text.length === 0) {
|
|
253
|
+
return LabelDex.Empty;
|
|
254
|
+
}
|
|
255
|
+
if (/^[A-Za-z0-9_-]{1,11}$/.test(text)) {
|
|
256
|
+
return encodeTag(text);
|
|
257
|
+
}
|
|
258
|
+
if (/^[A-Za-z0-9_-]+$/.test(text)) {
|
|
259
|
+
return encodeBext(text);
|
|
260
|
+
}
|
|
261
|
+
return encodeBytes(text);
|
|
262
|
+
}
|
|
263
|
+
function serializeValue(value, strict) {
|
|
264
|
+
// This is the recursive mapper exhale ladder. Ordering matters because a
|
|
265
|
+
// string might be either already-qualified CESR text or ordinary text that
|
|
266
|
+
// needs label-style encoding.
|
|
267
|
+
if (value === null) {
|
|
268
|
+
return new Matter({ code: MtrDex.Null, raw: new Uint8Array() }).qb64;
|
|
269
|
+
}
|
|
270
|
+
if (typeof value === "boolean") {
|
|
271
|
+
return new Matter({
|
|
272
|
+
code: value ? MtrDex.Yes : MtrDex.No,
|
|
273
|
+
raw: new Uint8Array(),
|
|
274
|
+
}).qb64;
|
|
275
|
+
}
|
|
276
|
+
if (typeof value === "number") {
|
|
277
|
+
return new Decimer({ decimal: value }).qb64;
|
|
278
|
+
}
|
|
279
|
+
if (typeof value === "bigint") {
|
|
280
|
+
return new Decimer({ dns: value.toString() }).qb64;
|
|
281
|
+
}
|
|
282
|
+
if (typeof value === "string") {
|
|
283
|
+
try {
|
|
284
|
+
const primitive = parseMatter(b(value), "txt");
|
|
285
|
+
if (primitive.qb64.length === value.length) {
|
|
286
|
+
const escape = ESCAPE_CODES.has(primitive.code)
|
|
287
|
+
? new Matter({ code: MtrDex.Escape, raw: new Uint8Array() }).qb64
|
|
288
|
+
: "";
|
|
289
|
+
return `${escape}${primitive.qb64}`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// fall through to text encoding
|
|
294
|
+
}
|
|
295
|
+
return encodeText(value);
|
|
296
|
+
}
|
|
297
|
+
if (value instanceof Uint8Array) {
|
|
298
|
+
return new Texter({
|
|
299
|
+
code: value.length % 3 === 0
|
|
300
|
+
? LabelDex.Bytes_L0
|
|
301
|
+
: value.length % 3 === 1
|
|
302
|
+
? LabelDex.Bytes_L1
|
|
303
|
+
: LabelDex.Bytes_L2,
|
|
304
|
+
raw: value,
|
|
305
|
+
}).qb64;
|
|
306
|
+
}
|
|
307
|
+
if (Array.isArray(value)) {
|
|
308
|
+
const payload = value.map((entry) => serializeValue(entry, strict)).join("");
|
|
309
|
+
const code = payload.length / 4 < 64 ** 2
|
|
310
|
+
? CtrDexV2.GenericListGroup
|
|
311
|
+
: CtrDexV2.BigGenericListGroup;
|
|
312
|
+
return `${new Counter({ code, count: payload.length / 4 }).qb64}${payload}`;
|
|
313
|
+
}
|
|
314
|
+
if (value && typeof value === "object") {
|
|
315
|
+
const payload = Object.entries(value).map(([label, entry]) => `${strict ? encodeText(label) : encodeText(label)}${serializeValue(entry, strict)}`).join("");
|
|
316
|
+
const code = payload.length / 4 < 64 ** 2
|
|
317
|
+
? CtrDexV2.GenericMapGroup
|
|
318
|
+
: CtrDexV2.BigGenericMapGroup;
|
|
319
|
+
return `${new Counter({ code, count: payload.length / 4 }).qb64}${payload}`;
|
|
320
|
+
}
|
|
321
|
+
throw new SerializeError(`Nonserializable mapper value=${String(value)}`);
|
|
322
|
+
}
|
|
323
|
+
function deserializeValue(raw, strict) {
|
|
324
|
+
// This is the inverse of `serializeValue()`: grouped containers first, then
|
|
325
|
+
// leaf primitives. `nextOffset` tells the caller exactly how much wire space
|
|
326
|
+
// was consumed by this one semantic value.
|
|
327
|
+
const counter = parseCounterProbe(raw, { major: 2, minor: 0 }, "txt");
|
|
328
|
+
if (counter) {
|
|
329
|
+
if (AGGOR_LIST_CODES.has(counter.code)) {
|
|
330
|
+
const total = counter.fullSize + counter.count * 4;
|
|
331
|
+
const payload = raw.slice(counter.fullSize, total);
|
|
332
|
+
const list = [];
|
|
333
|
+
let offset = 0;
|
|
334
|
+
while (offset < payload.length) {
|
|
335
|
+
const parsed = deserializeValue(payload.slice(offset), strict);
|
|
336
|
+
list.push(parsed.value);
|
|
337
|
+
offset += parsed.nextOffset;
|
|
338
|
+
}
|
|
339
|
+
return { value: list, nextOffset: total };
|
|
340
|
+
}
|
|
341
|
+
if (AGGOR_MAP_CODES.has(counter.code)) {
|
|
342
|
+
const total = counter.fullSize + counter.count * 4;
|
|
343
|
+
const payload = raw.slice(counter.fullSize, total);
|
|
344
|
+
const map = {};
|
|
345
|
+
let offset = 0;
|
|
346
|
+
while (offset < payload.length) {
|
|
347
|
+
const labeler = parseLabeler(payload.slice(offset), "txt");
|
|
348
|
+
const label = strict ? labeler.label : labeler.text;
|
|
349
|
+
offset += labeler.fullSize;
|
|
350
|
+
const parsed = deserializeValue(payload.slice(offset), strict);
|
|
351
|
+
map[label] = parsed.value;
|
|
352
|
+
offset += parsed.nextOffset;
|
|
353
|
+
}
|
|
354
|
+
return { value: map, nextOffset: total };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const matter = parseMatter(raw, "txt");
|
|
358
|
+
let offset = matter.fullSize;
|
|
359
|
+
if (matter.code === MtrDex.Escape) {
|
|
360
|
+
const escaped = parseMatter(raw.slice(offset), "txt");
|
|
361
|
+
return { value: escaped.qb64, nextOffset: offset + escaped.fullSize };
|
|
362
|
+
}
|
|
363
|
+
if (matter.code === MtrDex.Null) {
|
|
364
|
+
return { value: null, nextOffset: offset };
|
|
365
|
+
}
|
|
366
|
+
if (matter.code === MtrDex.Yes) {
|
|
367
|
+
return { value: true, nextOffset: offset };
|
|
368
|
+
}
|
|
369
|
+
if (matter.code === MtrDex.No) {
|
|
370
|
+
return { value: false, nextOffset: offset };
|
|
371
|
+
}
|
|
372
|
+
if (DECIMAL_CODES.has(matter.code)) {
|
|
373
|
+
return { value: new Decimer(matter).decimal, nextOffset: offset };
|
|
374
|
+
}
|
|
375
|
+
if (LABELER_CODES.has(matter.code) || BEXTER_CODES.has(matter.code)) {
|
|
376
|
+
return {
|
|
377
|
+
value: parseLabeler(b(matter.qb64), "txt").text,
|
|
378
|
+
nextOffset: offset,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return { value: matter.qb64, nextOffset: offset };
|
|
382
|
+
}
|
|
383
|
+
function buildFieldProjection(value, strict) {
|
|
384
|
+
// `fields` is rebuilt from semantic data so the projection stays coherent
|
|
385
|
+
// even when the mapper originated from `Mapper.fromSad(...)` rather than a
|
|
386
|
+
// syntax-parse path.
|
|
387
|
+
return Object.entries(value).map(([label, entry]) => {
|
|
388
|
+
const serialized = serializeValue(entry, strict);
|
|
389
|
+
const primitive = serialized.startsWith("-")
|
|
390
|
+
? parseCounter(b(serialized), { major: 2, minor: 0 }, "txt")
|
|
391
|
+
: parseMatter(b(serialized), "txt");
|
|
392
|
+
const children = entry && typeof entry === "object" && !Array.isArray(entry)
|
|
393
|
+
? buildFieldProjection(entry, strict)
|
|
394
|
+
: undefined;
|
|
395
|
+
return {
|
|
396
|
+
label,
|
|
397
|
+
primitive,
|
|
398
|
+
isCounter: AGGOR_LIST_CODES.has(primitive.code)
|
|
399
|
+
|| AGGOR_MAP_CODES.has(primitive.code),
|
|
400
|
+
children,
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function verifyMapRoundTrip(mapper) {
|
|
405
|
+
// Verification is explicit on purpose: rebuild a mapper from the semantic
|
|
406
|
+
// view and require byte-for-byte raw equality. If this fails, our semantic
|
|
407
|
+
// projection lost information or our serializer is dishonest.
|
|
408
|
+
const actual = mapper.kind === "CESR"
|
|
409
|
+
? Mapper.fromSad(mapper.mad, {
|
|
410
|
+
strict: mapper.strict,
|
|
411
|
+
saidive: mapper.saidive,
|
|
412
|
+
saids: mapper.saids,
|
|
413
|
+
kind: mapper.kind,
|
|
414
|
+
makify: false,
|
|
415
|
+
verify: false,
|
|
416
|
+
})
|
|
417
|
+
: Mapper.fromSad(mapper.mad, {
|
|
418
|
+
strict: mapper.strict,
|
|
419
|
+
saidive: mapper.saidive,
|
|
420
|
+
saids: mapper.saids,
|
|
421
|
+
kind: mapper.kind,
|
|
422
|
+
makify: false,
|
|
423
|
+
verify: false,
|
|
424
|
+
});
|
|
425
|
+
if (t(actual.raw) !== t(mapper.raw)) {
|
|
426
|
+
throw new DeserializeError("Invalid mapper round trip against raw.");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Semantic CESR-native map primitive.
|
|
431
|
+
*
|
|
432
|
+
* KERIpy substance: `Mapper` is the reusable native field-map engine beneath
|
|
433
|
+
* compactable ACDC sections. It owns both the semantic map view (`mad`) and the
|
|
434
|
+
* exact enclosed native group serialization (`raw`, `qb64`, `qb2`).
|
|
435
|
+
*
|
|
436
|
+
* Cohesive mental model:
|
|
437
|
+
* 1. `parseMapperBodySyntax()` tokenizes raw native bytes into syntax artifacts.
|
|
438
|
+
* 2. `interpretMapperBodySyntax()` pairs those artifacts into readable fields.
|
|
439
|
+
* 3. `Mapper` owns the full semantic lifecycle: build from `mad`, or inhale
|
|
440
|
+
* from raw, then expose both semantic (`mad`) and wire (`raw`, `qb64`,
|
|
441
|
+
* `qb2`) views.
|
|
442
|
+
*
|
|
443
|
+
* Put differently:
|
|
444
|
+
* syntax helpers are for inspection and parser diagnostics;
|
|
445
|
+
* `Mapper` is for truth.
|
|
446
|
+
*
|
|
447
|
+
* Compare carefully:
|
|
448
|
+
* - use `Mapper` when the disclosed thing is a general map
|
|
449
|
+
* - do not use it for fixed-field blind/bound/media tuples, which have their
|
|
450
|
+
* own explicit schema and workflow layer
|
|
451
|
+
*/
|
|
452
|
+
export class Mapper {
|
|
453
|
+
/**
|
|
454
|
+
* Create one semantic mapper from either a field map (`mad`) or raw bytes.
|
|
455
|
+
*
|
|
456
|
+
* Constructor modes:
|
|
457
|
+
* - semantic mode: caller provides `mad`, optional `makify`, optional
|
|
458
|
+
* `saidive`, and the constructor serializes deterministically
|
|
459
|
+
* - native inhale mode: caller provides `raw`/`qb64`/`qb64b`/`qb2` with
|
|
460
|
+
* `kind="CESR"` and the constructor decodes grouped native content
|
|
461
|
+
* - non-native inhale mode: caller provides raw JSON/CBOR/MGPK with
|
|
462
|
+
* `kind!="CESR"` and the constructor decodes via the corresponding codec
|
|
463
|
+
*/
|
|
464
|
+
constructor(init = {}) {
|
|
465
|
+
Object.defineProperty(this, "code", {
|
|
466
|
+
enumerable: true,
|
|
467
|
+
configurable: true,
|
|
468
|
+
writable: true,
|
|
469
|
+
value: void 0
|
|
470
|
+
});
|
|
471
|
+
Object.defineProperty(this, "count", {
|
|
472
|
+
enumerable: true,
|
|
473
|
+
configurable: true,
|
|
474
|
+
writable: true,
|
|
475
|
+
value: void 0
|
|
476
|
+
});
|
|
477
|
+
Object.defineProperty(this, "fullSize", {
|
|
478
|
+
enumerable: true,
|
|
479
|
+
configurable: true,
|
|
480
|
+
writable: true,
|
|
481
|
+
value: void 0
|
|
482
|
+
});
|
|
483
|
+
Object.defineProperty(this, "fullSizeB2", {
|
|
484
|
+
enumerable: true,
|
|
485
|
+
configurable: true,
|
|
486
|
+
writable: true,
|
|
487
|
+
value: void 0
|
|
488
|
+
});
|
|
489
|
+
Object.defineProperty(this, "totalSize", {
|
|
490
|
+
enumerable: true,
|
|
491
|
+
configurable: true,
|
|
492
|
+
writable: true,
|
|
493
|
+
value: void 0
|
|
494
|
+
});
|
|
495
|
+
Object.defineProperty(this, "totalSizeB2", {
|
|
496
|
+
enumerable: true,
|
|
497
|
+
configurable: true,
|
|
498
|
+
writable: true,
|
|
499
|
+
value: void 0
|
|
500
|
+
});
|
|
501
|
+
Object.defineProperty(this, "fields", {
|
|
502
|
+
enumerable: true,
|
|
503
|
+
configurable: true,
|
|
504
|
+
writable: true,
|
|
505
|
+
value: void 0
|
|
506
|
+
});
|
|
507
|
+
Object.defineProperty(this, "raw", {
|
|
508
|
+
enumerable: true,
|
|
509
|
+
configurable: true,
|
|
510
|
+
writable: true,
|
|
511
|
+
value: void 0
|
|
512
|
+
});
|
|
513
|
+
Object.defineProperty(this, "strict", {
|
|
514
|
+
enumerable: true,
|
|
515
|
+
configurable: true,
|
|
516
|
+
writable: true,
|
|
517
|
+
value: void 0
|
|
518
|
+
});
|
|
519
|
+
Object.defineProperty(this, "saids", {
|
|
520
|
+
enumerable: true,
|
|
521
|
+
configurable: true,
|
|
522
|
+
writable: true,
|
|
523
|
+
value: void 0
|
|
524
|
+
});
|
|
525
|
+
Object.defineProperty(this, "saidive", {
|
|
526
|
+
enumerable: true,
|
|
527
|
+
configurable: true,
|
|
528
|
+
writable: true,
|
|
529
|
+
value: void 0
|
|
530
|
+
});
|
|
531
|
+
Object.defineProperty(this, "kind", {
|
|
532
|
+
enumerable: true,
|
|
533
|
+
configurable: true,
|
|
534
|
+
writable: true,
|
|
535
|
+
value: void 0
|
|
536
|
+
});
|
|
537
|
+
Object.defineProperty(this, "_mad", {
|
|
538
|
+
enumerable: true,
|
|
539
|
+
configurable: true,
|
|
540
|
+
writable: true,
|
|
541
|
+
value: void 0
|
|
542
|
+
});
|
|
543
|
+
this.strict = init.strict ?? true;
|
|
544
|
+
this.saids = { ...(init.saids ?? { d: "E" }) };
|
|
545
|
+
this.saidive = init.saidive ?? false;
|
|
546
|
+
this.kind = init.kind ?? "CESR";
|
|
547
|
+
if (init.mad || (!init.raw && !init.qb64 && !init.qb64b && !init.qb2)) {
|
|
548
|
+
const mad = cloneValue(init.mad ?? {});
|
|
549
|
+
if (init.makify && this.saidive) {
|
|
550
|
+
// Mapper saidification is intentionally top-level only. Recursive leaf
|
|
551
|
+
// discovery belongs to `Compactor`; `Mapper` just makes one map
|
|
552
|
+
// self-addressing when asked.
|
|
553
|
+
const dummy = cloneValue(mad);
|
|
554
|
+
for (const [label, code] of Object.entries(this.saids)) {
|
|
555
|
+
if (!(label in dummy))
|
|
556
|
+
continue;
|
|
557
|
+
try {
|
|
558
|
+
const valueCode = parseMatter(b(String(dummy[label])), "txt").code;
|
|
559
|
+
if (valueCode) {
|
|
560
|
+
this.saids[label] = valueCode;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
// keep configured default
|
|
565
|
+
}
|
|
566
|
+
const sizage = MATTER_SIZES.get(this.saids[label]);
|
|
567
|
+
if (!sizage?.fs) {
|
|
568
|
+
throw new SerializeError(`Unsupported mapper SAID code=${this.saids[label]}`);
|
|
569
|
+
}
|
|
570
|
+
dummy[label] = "#".repeat(sizage.fs);
|
|
571
|
+
}
|
|
572
|
+
const dummiedRaw = this.kind === "CESR"
|
|
573
|
+
? Mapper.serializeCesrMap(dummy, this.strict, true, this.saids)
|
|
574
|
+
: Mapper.serializeNonNativeMap(dummy, this.kind);
|
|
575
|
+
for (const [label, code] of Object.entries(this.saids)) {
|
|
576
|
+
if (!(label in mad))
|
|
577
|
+
continue;
|
|
578
|
+
mad[label] = new Diger({ code, raw: Diger.digest(dummiedRaw, code) }).qb64;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (this.kind === "CESR") {
|
|
582
|
+
// In semantic->native mode we already trust `mad`, so we derive the
|
|
583
|
+
// readable `fields` projection directly from semantic data instead of
|
|
584
|
+
// re-parsing the raw bytes we just emitted.
|
|
585
|
+
this.raw = Mapper.serializeCesrMap(mad, this.strict);
|
|
586
|
+
const counter = parseCounter(this.raw, init.version ?? { major: 2, minor: 0 }, "txt");
|
|
587
|
+
this.code = counter.code;
|
|
588
|
+
this.count = counter.count;
|
|
589
|
+
this.fullSize = counter.fullSize;
|
|
590
|
+
this.fullSizeB2 = counter.fullSizeB2;
|
|
591
|
+
this.totalSize = counter.fullSize + counter.count * 4;
|
|
592
|
+
this.totalSizeB2 = Math.ceil(this.totalSize * 3 / 4);
|
|
593
|
+
this.fields = buildFieldProjection(mad, this.strict);
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
this.raw = Mapper.serializeNonNativeMap(mad, this.kind);
|
|
597
|
+
this.code = CtrDexV2.GenericMapGroup;
|
|
598
|
+
this.count = 0;
|
|
599
|
+
this.fullSize = 0;
|
|
600
|
+
this.fullSizeB2 = 0;
|
|
601
|
+
this.totalSize = this.raw.length;
|
|
602
|
+
this.totalSizeB2 = this.raw.length;
|
|
603
|
+
this.fields = buildFieldProjection(mad, this.strict);
|
|
604
|
+
}
|
|
605
|
+
this._mad = mad;
|
|
606
|
+
if (init.verify ?? true) {
|
|
607
|
+
verifyMapRoundTrip(this);
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (this.kind !== "CESR") {
|
|
612
|
+
// Non-native support matters because compact/disclose logic for ACDC has
|
|
613
|
+
// to reason about JSON/CBOR/MGPK section forms too, not just native wire
|
|
614
|
+
// forms.
|
|
615
|
+
this.raw = init.raw ?? init.qb64b ?? b(init.qb64 ?? "");
|
|
616
|
+
this._mad = Mapper.deserializeNonNativeMap(this.raw, this.kind);
|
|
617
|
+
this.code = CtrDexV2.GenericMapGroup;
|
|
618
|
+
this.count = 0;
|
|
619
|
+
this.fullSize = 0;
|
|
620
|
+
this.fullSizeB2 = 0;
|
|
621
|
+
this.totalSize = this.raw.length;
|
|
622
|
+
this.totalSizeB2 = this.raw.length;
|
|
623
|
+
this.fields = buildFieldProjection(this._mad, this.strict);
|
|
624
|
+
if (init.verify ?? true) {
|
|
625
|
+
verifyMapRoundTrip(this);
|
|
626
|
+
}
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const version = init.version ?? { major: 2, minor: 0 };
|
|
630
|
+
const raw = init.qb2
|
|
631
|
+
? canonicalizeNativeRaw(init.qb2, version)
|
|
632
|
+
: init.qb64
|
|
633
|
+
? b(init.qb64)
|
|
634
|
+
: init.qb64b
|
|
635
|
+
? init.qb64b
|
|
636
|
+
: init.raw;
|
|
637
|
+
// In native inhale mode we *do* walk the wire payload because the whole
|
|
638
|
+
// point is to reconstruct semantic values from the compact map encoding.
|
|
639
|
+
const counter = parseCounter(raw, version, "txt");
|
|
640
|
+
if (!isMapGroupCode(counter.code)) {
|
|
641
|
+
throw new UnknownCodeError(`Expected map-body/group counter, got ${counter.code}`);
|
|
642
|
+
}
|
|
643
|
+
const totalSize = counter.fullSize + counter.count * 4;
|
|
644
|
+
const payload = raw.slice(counter.fullSize, totalSize);
|
|
645
|
+
const mad = {};
|
|
646
|
+
let offset = 0;
|
|
647
|
+
while (offset < payload.length) {
|
|
648
|
+
const labeler = parseLabeler(payload.slice(offset), "txt");
|
|
649
|
+
const label = this.strict ? labeler.label : labeler.text;
|
|
650
|
+
offset += labeler.fullSize;
|
|
651
|
+
const parsed = deserializeValue(payload.slice(offset), this.strict);
|
|
652
|
+
mad[label] = parsed.value;
|
|
653
|
+
offset += parsed.nextOffset;
|
|
654
|
+
}
|
|
655
|
+
this.raw = raw.slice(0, totalSize);
|
|
656
|
+
this.code = counter.code;
|
|
657
|
+
this.count = counter.count;
|
|
658
|
+
this.fullSize = counter.fullSize;
|
|
659
|
+
this.fullSizeB2 = counter.fullSizeB2;
|
|
660
|
+
this.totalSize = totalSize;
|
|
661
|
+
this.totalSizeB2 = Math.ceil(totalSize * 3 / 4);
|
|
662
|
+
this.fields = buildFieldProjection(mad, this.strict);
|
|
663
|
+
this._mad = mad;
|
|
664
|
+
if (init.verify ?? true) {
|
|
665
|
+
verifyMapRoundTrip(this);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
static fromSad(mad, options = {}) {
|
|
669
|
+
return new Mapper({ ...options, mad });
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Serialize one semantic map into an enclosed CESR native map-group.
|
|
673
|
+
*
|
|
674
|
+
* The result includes the leading group counter. Nested maps/lists are
|
|
675
|
+
* recursively enclosed in their own grouped payloads.
|
|
676
|
+
*/
|
|
677
|
+
static serializeCesrMap(mad, strict = true, dummy = false, saids = {}) {
|
|
678
|
+
const payload = Object.entries(mad).map(([label, value]) => {
|
|
679
|
+
const rendered = dummy && label in saids
|
|
680
|
+
? (() => {
|
|
681
|
+
const sizage = MATTER_SIZES.get(saids[label]);
|
|
682
|
+
if (!sizage?.fs) {
|
|
683
|
+
throw new SerializeError(`Unsupported mapper SAID code=${saids[label]}`);
|
|
684
|
+
}
|
|
685
|
+
return "#".repeat(sizage.fs);
|
|
686
|
+
})()
|
|
687
|
+
: serializeValue(value, strict);
|
|
688
|
+
return `${encodeText(label)}${rendered}`;
|
|
689
|
+
}).join("");
|
|
690
|
+
const code = payload.length / 4 < 64 ** 2
|
|
691
|
+
? CtrDexV2.GenericMapGroup
|
|
692
|
+
: CtrDexV2.BigGenericMapGroup;
|
|
693
|
+
return b(`${new Counter({ code, count: payload.length / 4 }).qb64}${payload}`);
|
|
694
|
+
}
|
|
695
|
+
static serializeNonNativeMap(mad, kind) {
|
|
696
|
+
if (kind === "JSON") {
|
|
697
|
+
return b(JSON.stringify(mad));
|
|
698
|
+
}
|
|
699
|
+
if (kind === "CBOR") {
|
|
700
|
+
return encodeKeriCbor(mad);
|
|
701
|
+
}
|
|
702
|
+
if (kind === "MGPK") {
|
|
703
|
+
return encodeMsgpack(mad);
|
|
704
|
+
}
|
|
705
|
+
throw new SerializeError(`Unsupported mapper serialization kind=${kind}`);
|
|
706
|
+
}
|
|
707
|
+
static deserializeNonNativeMap(raw, kind) {
|
|
708
|
+
if (kind === "JSON") {
|
|
709
|
+
return JSON.parse(t(raw));
|
|
710
|
+
}
|
|
711
|
+
if (kind === "CBOR") {
|
|
712
|
+
return decodeKeriCbor(raw);
|
|
713
|
+
}
|
|
714
|
+
if (kind === "MGPK") {
|
|
715
|
+
return decodeMsgpack(raw);
|
|
716
|
+
}
|
|
717
|
+
throw new DeserializeError(`Unsupported mapper deserialization kind=${kind}`);
|
|
718
|
+
}
|
|
719
|
+
get mad() {
|
|
720
|
+
// Clone on read so callers can inspect or tweak the returned object
|
|
721
|
+
// without mutating mapper internals behind the class's back.
|
|
722
|
+
return cloneValue(this._mad);
|
|
723
|
+
}
|
|
724
|
+
get qb64b() {
|
|
725
|
+
return this.raw.slice();
|
|
726
|
+
}
|
|
727
|
+
get qb64() {
|
|
728
|
+
return t(this.raw);
|
|
729
|
+
}
|
|
730
|
+
get qb2() {
|
|
731
|
+
if (this.kind !== "CESR") {
|
|
732
|
+
throw new SerializeError(`Binary domain undefined for non-native kind=${this.kind}`);
|
|
733
|
+
}
|
|
734
|
+
return codeB64ToB2(this.qb64);
|
|
735
|
+
}
|
|
736
|
+
get said() {
|
|
737
|
+
const label = Object.keys(this.saids)[0];
|
|
738
|
+
const value = this._mad[label];
|
|
739
|
+
return typeof value === "string" ? value : null;
|
|
740
|
+
}
|
|
741
|
+
get size() {
|
|
742
|
+
return this.raw.length;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Parse map-style native bodies/counters into a semantic `Mapper`.
|
|
747
|
+
*
|
|
748
|
+
* Compatibility wrapper: existing parser-oriented tests can keep calling this
|
|
749
|
+
* helper, while newer code can instantiate `Mapper` directly from `mad` or
|
|
750
|
+
* raw. This wrapper preserves the older parser-friendly feel while the class
|
|
751
|
+
* itself is the new semantic authority.
|
|
183
752
|
*/
|
|
184
753
|
export function parseMapperBody(input, version, domain) {
|
|
185
754
|
let syntax;
|
|
@@ -192,9 +761,8 @@ export function parseMapperBody(input, version, domain) {
|
|
|
192
761
|
}
|
|
193
762
|
throw error;
|
|
194
763
|
}
|
|
195
|
-
let fields;
|
|
196
764
|
try {
|
|
197
|
-
|
|
765
|
+
interpretMapperBodySyntax(syntax);
|
|
198
766
|
}
|
|
199
767
|
catch (error) {
|
|
200
768
|
if (error instanceof SemanticInterpretationError) {
|
|
@@ -202,15 +770,24 @@ export function parseMapperBody(input, version, domain) {
|
|
|
202
770
|
}
|
|
203
771
|
throw error;
|
|
204
772
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
773
|
+
const mapper = new Mapper({
|
|
774
|
+
mad: {},
|
|
775
|
+
strict: true,
|
|
776
|
+
kind: "CESR",
|
|
777
|
+
verify: false,
|
|
778
|
+
});
|
|
779
|
+
mapper.code = syntax.code;
|
|
780
|
+
mapper.count = syntax.count;
|
|
781
|
+
mapper.fullSize = syntax.fullSize;
|
|
782
|
+
mapper.fullSizeB2 = syntax.fullSizeB2;
|
|
783
|
+
mapper.totalSize = syntax.totalSize;
|
|
784
|
+
mapper.totalSizeB2 = syntax.totalSizeB2;
|
|
785
|
+
mapper.fields = interpretMapperBodySyntax(syntax);
|
|
786
|
+
mapper.raw = domain === "txt"
|
|
787
|
+
? input.slice(0, syntax.totalSize)
|
|
788
|
+
: canonicalizeNativeRaw(input, version).slice(0, syntax.totalSize);
|
|
789
|
+
mapper._mad = {};
|
|
790
|
+
return mapper;
|
|
214
791
|
}
|
|
215
792
|
/** True for counter codes that represent map/list-native aggregate primitives. */
|
|
216
793
|
export function isMapperCounterCode(code) {
|