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
|
@@ -0,0 +1,1326 @@
|
|
|
1
|
+
import { b, codeB2ToB64, intToB64, t } from "../core/bytes.js";
|
|
2
|
+
import { DeserializeError, SerializeError } from "../core/errors.js";
|
|
3
|
+
import { Ilks } from "../core/vocabulary.js";
|
|
4
|
+
import { parseAttachmentDispatch } from "../parser/group-dispatch.js";
|
|
5
|
+
import { Aggor, isAggorListCode, isAggorMapCode, parseAggor } from "../primitives/aggor.js";
|
|
6
|
+
import { Bexter } from "../primitives/bexter.js";
|
|
7
|
+
import { BEXTER_CODES, DATER_CODES, DIGEST_CODES, LabelDex, MtrDex, NON_DIGEST_PREFIX_CODES, NONCE_CODES, NUMBER_CODES, PREFIX_CODES, TAG_CODES, } from "../primitives/codex.js";
|
|
8
|
+
import { Counter, parseCounter } from "../primitives/counter.js";
|
|
9
|
+
import { Dater } from "../primitives/dater.js";
|
|
10
|
+
import { parseIlker } from "../primitives/ilker.js";
|
|
11
|
+
import { Labeler, parseLabeler } from "../primitives/labeler.js";
|
|
12
|
+
import { Mapper } from "../primitives/mapper.js";
|
|
13
|
+
import { parseMatter } from "../primitives/matter.js";
|
|
14
|
+
import { parseNoncer } from "../primitives/noncer.js";
|
|
15
|
+
import { NumberPrimitive } from "../primitives/number.js";
|
|
16
|
+
import { makePather, parsePather } from "../primitives/pather.js";
|
|
17
|
+
import { isCounterGroupLike, isPrimitiveTuple } from "../primitives/primitive.js";
|
|
18
|
+
import { Structor } from "../primitives/structor.js";
|
|
19
|
+
import { Texter } from "../primitives/texter.js";
|
|
20
|
+
import { Tholder } from "../primitives/tholder.js";
|
|
21
|
+
import { parseTraitor } from "../primitives/traitor.js";
|
|
22
|
+
import { parseVerser } from "../primitives/verser.js";
|
|
23
|
+
import { CtrDexV2 } from "../tables/counter-codex.js";
|
|
24
|
+
import { Kinds, Protocols, Vrsn_2_0 } from "../tables/versions.js";
|
|
25
|
+
import { versify } from "./smell.js";
|
|
26
|
+
/**
|
|
27
|
+
* CESR-native serder helper layer.
|
|
28
|
+
*
|
|
29
|
+
* This module exists to keep native inhale/exhale logic in one shared place so
|
|
30
|
+
* `Serder`, `Serdery`, and parser hydration all agree on the same rules. The
|
|
31
|
+
* current implementation is intentionally a staged parity port: it handles the
|
|
32
|
+
* real KERI native flows needed by runtime integration first, while broader
|
|
33
|
+
* KERI/ACDC native parity and compactification continue to expand here.
|
|
34
|
+
*
|
|
35
|
+
* Mental model:
|
|
36
|
+
* - non-native KERI/ACDC bodies are self-describing field maps (`JSON`,
|
|
37
|
+
* `CBOR`, `MGPK`) that `smell()` can recognize directly from the embedded
|
|
38
|
+
* version string
|
|
39
|
+
* - CESR-native bodies are instead "counter + compact CESR fields", so the
|
|
40
|
+
* parser must first identify the body-group frame before the serder layer can
|
|
41
|
+
* reconstruct a SAD
|
|
42
|
+
* - this module bridges that gap by converting native bodies into the same
|
|
43
|
+
* semantic `ked` shape that non-native serders use
|
|
44
|
+
*
|
|
45
|
+
* Text-domain native example, segmented for humans:
|
|
46
|
+
*
|
|
47
|
+
* ```text
|
|
48
|
+
* -FA5 | 0OKERICAACA | Xicp | EFaYE2... | DNG2ar... | MAAA | MAAB | -JAL...
|
|
49
|
+
* ^ ^ ^ ^ ^ ^ ^ ^
|
|
50
|
+
* body verser ilk said `d` prefix `i` `s` `kt` list/group payloads
|
|
51
|
+
* ctr
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* The same message may also arrive in qb2/binary form. In that case the bytes
|
|
55
|
+
* are not human-readable, so this module canonicalizes them back to the qb64
|
|
56
|
+
* text form above before decoding semantics. That is why many helpers talk
|
|
57
|
+
* about "text-domain canonical form" even when their input may have started as
|
|
58
|
+
* raw qb2 bytes.
|
|
59
|
+
*/
|
|
60
|
+
const TAG_CODES_BY_LENGTH = new Map([
|
|
61
|
+
[1, LabelDex.Tag1],
|
|
62
|
+
[2, LabelDex.Tag2],
|
|
63
|
+
[3, LabelDex.Tag3],
|
|
64
|
+
[4, LabelDex.Tag4],
|
|
65
|
+
[5, LabelDex.Tag5],
|
|
66
|
+
[6, LabelDex.Tag6],
|
|
67
|
+
[7, LabelDex.Tag7],
|
|
68
|
+
[8, LabelDex.Tag8],
|
|
69
|
+
[9, LabelDex.Tag9],
|
|
70
|
+
[10, LabelDex.Tag10],
|
|
71
|
+
[11, LabelDex.Tag11],
|
|
72
|
+
]);
|
|
73
|
+
const NUMBER_CAPACITIES = [
|
|
74
|
+
{ code: "M", rawSize: 2 },
|
|
75
|
+
{ code: "0H", rawSize: 4 },
|
|
76
|
+
{ code: "R", rawSize: 5 },
|
|
77
|
+
{ code: "N", rawSize: 8 },
|
|
78
|
+
{ code: "S", rawSize: 11 },
|
|
79
|
+
{ code: "T", rawSize: 14 },
|
|
80
|
+
{ code: "0A", rawSize: 16 },
|
|
81
|
+
{ code: "U", rawSize: 17 },
|
|
82
|
+
];
|
|
83
|
+
/** Native text bodies always begin with a counter hard code `-`. */
|
|
84
|
+
function isTextDomain(raw) {
|
|
85
|
+
return raw.length > 0 && raw[0] === "-".charCodeAt(0);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Resolve the canonical qb64 span of one native body-group token.
|
|
89
|
+
*
|
|
90
|
+
* Even when the input is qb2, the body counter still declares the payload in
|
|
91
|
+
* qb64 quadlets, so this helper always returns the text-domain width.
|
|
92
|
+
*/
|
|
93
|
+
function nativeQb64Size(raw, version) {
|
|
94
|
+
const cold = isTextDomain(raw) ? "txt" : "bny";
|
|
95
|
+
const counter = parseCounter(raw, version, cold);
|
|
96
|
+
return counter.fullSize + counter.count * 4;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Convert native input into the canonical qb64 text-domain form used by the
|
|
100
|
+
* rest of this module.
|
|
101
|
+
*
|
|
102
|
+
* If `raw` is already text-domain, it is returned unchanged. If `raw` is qb2,
|
|
103
|
+
* the function converts exactly one native body-group span back into its qb64
|
|
104
|
+
* ASCII form.
|
|
105
|
+
*
|
|
106
|
+
* Example:
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* const txt = canonicalizeCesrNativeRaw(qb2Bytes, { major: 2, minor: 0 });
|
|
110
|
+
* // new TextDecoder().decode(txt) starts with "-F..." or "-G..."
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function canonicalizeCesrNativeRaw(raw, version) {
|
|
114
|
+
if (isTextDomain(raw)) {
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
return b(codeB2ToB64(raw, nativeQb64Size(raw, version)));
|
|
118
|
+
}
|
|
119
|
+
/** Emit the narrowest fixed-width tag family that can carry `text`. */
|
|
120
|
+
function encodeTag(text) {
|
|
121
|
+
const code = TAG_CODES_BY_LENGTH.get(text.length);
|
|
122
|
+
if (!code) {
|
|
123
|
+
throw new SerializeError(`Unsupported tag length=${text.length}`);
|
|
124
|
+
}
|
|
125
|
+
const pad = code === LabelDex.Tag1 || code === LabelDex.Tag5 || code === LabelDex.Tag9
|
|
126
|
+
? "_"
|
|
127
|
+
: "";
|
|
128
|
+
return `${code}${pad}${text}`;
|
|
129
|
+
}
|
|
130
|
+
/** Emit base64-safe text through the StrB64/Bexter family. */
|
|
131
|
+
function encodeBext(text) {
|
|
132
|
+
const rem = text.length % 4;
|
|
133
|
+
const code = rem === 0
|
|
134
|
+
? LabelDex.StrB64_L0
|
|
135
|
+
: rem === 1
|
|
136
|
+
? LabelDex.StrB64_L1
|
|
137
|
+
: LabelDex.StrB64_L2;
|
|
138
|
+
return new Bexter({ code, raw: Bexter.rawify(text) }).qb64;
|
|
139
|
+
}
|
|
140
|
+
/** Emit arbitrary UTF-8 text through the bytes label family. */
|
|
141
|
+
function encodeBytes(text) {
|
|
142
|
+
const raw = b(text);
|
|
143
|
+
const rem = raw.length % 3;
|
|
144
|
+
const code = rem === 0
|
|
145
|
+
? LabelDex.Bytes_L0
|
|
146
|
+
: rem === 1
|
|
147
|
+
? LabelDex.Bytes_L1
|
|
148
|
+
: LabelDex.Bytes_L2;
|
|
149
|
+
return new Texter({ code, raw }).qb64;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Choose the most compact supported native label/text encoding for `text`.
|
|
153
|
+
*
|
|
154
|
+
* Decision order mirrors the intended CESR-native compactness ladder:
|
|
155
|
+
* tag first, then StrB64, then generic bytes.
|
|
156
|
+
*/
|
|
157
|
+
function encodeLabel(text) {
|
|
158
|
+
if (text.length === 0) {
|
|
159
|
+
return LabelDex.Empty;
|
|
160
|
+
}
|
|
161
|
+
if (/^[A-Za-z0-9_-]{1,11}$/.test(text)) {
|
|
162
|
+
return encodeTag(text);
|
|
163
|
+
}
|
|
164
|
+
if (/^[A-Za-z0-9_-]+$/.test(text)) {
|
|
165
|
+
return encodeBext(text);
|
|
166
|
+
}
|
|
167
|
+
return encodeBytes(text);
|
|
168
|
+
}
|
|
169
|
+
/** Encode protocol + version metadata into one native verser token. */
|
|
170
|
+
function encodeVerser(proto, pvrsn, gvrsn) {
|
|
171
|
+
const payload = `${proto}${intToB64(pvrsn.major, 1)}${intToB64(pvrsn.minor, 2)}${gvrsn ? `${intToB64(gvrsn.major, 1)}${intToB64(gvrsn.minor, 2)}` : ""}`;
|
|
172
|
+
return encodeTag(payload);
|
|
173
|
+
}
|
|
174
|
+
/** Big-endian integer helper used by native numeric families. */
|
|
175
|
+
function bigintToBytes(value) {
|
|
176
|
+
if (value < 0n) {
|
|
177
|
+
throw new SerializeError(`Negative CESR number=${value}`);
|
|
178
|
+
}
|
|
179
|
+
if (value === 0n) {
|
|
180
|
+
return new Uint8Array([0]);
|
|
181
|
+
}
|
|
182
|
+
const bytes = [];
|
|
183
|
+
let working = value;
|
|
184
|
+
while (working > 0n) {
|
|
185
|
+
bytes.unshift(Number(working & 0xffn));
|
|
186
|
+
working >>= 8n;
|
|
187
|
+
}
|
|
188
|
+
return new Uint8Array(bytes);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Emit one CESR numeric primitive from a semantic integer/hex-string value.
|
|
192
|
+
*
|
|
193
|
+
* Maintainer rule: when native number-family parity broadens, extend
|
|
194
|
+
* `NUMBER_CAPACITIES` instead of growing special cases in callers.
|
|
195
|
+
*/
|
|
196
|
+
function encodeNumber(value) {
|
|
197
|
+
const bigint = typeof value === "bigint"
|
|
198
|
+
? value
|
|
199
|
+
: typeof value === "number"
|
|
200
|
+
? BigInt(value)
|
|
201
|
+
: BigInt(`0x${value || "0"}`);
|
|
202
|
+
const raw = bigintToBytes(bigint);
|
|
203
|
+
const entry = NUMBER_CAPACITIES.find(({ rawSize }) => raw.length <= rawSize);
|
|
204
|
+
if (!entry) {
|
|
205
|
+
throw new SerializeError(`Unsupported number width=${raw.length}`);
|
|
206
|
+
}
|
|
207
|
+
const padded = new Uint8Array(entry.rawSize);
|
|
208
|
+
padded.set(raw, entry.rawSize - raw.length);
|
|
209
|
+
return new NumberPrimitive({ code: entry.code, raw: padded }).qb64;
|
|
210
|
+
}
|
|
211
|
+
/** KERI thresholds are either numeric hex strings or weighted clause expressions. */
|
|
212
|
+
function encodeThreshold(value) {
|
|
213
|
+
try {
|
|
214
|
+
return new Tholder({ sith: value }).qb64;
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
throw new SerializeError(`Unsupported CESR threshold=${JSON.stringify(value)}`, undefined, error instanceof Error ? error.message : undefined);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/** CESR-native datetimes substitute base64-safe glyphs for RFC3339 punctuation. */
|
|
221
|
+
function encodeDate(iso8601) {
|
|
222
|
+
return `${MtrDex.DateTime}${iso8601.replaceAll(":", "c").replaceAll(".", "d").replaceAll("+", "p")}`;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Encode semantic route/path strings using the same Pather rules as KERIpy.
|
|
226
|
+
*
|
|
227
|
+
* Important nuance:
|
|
228
|
+
* KERI route fields are not generic labels. Even when the semantic value is a
|
|
229
|
+
* simple string like `ksn`, KERIpy encodes it through `Pather(..., relative=True,
|
|
230
|
+
* pathive=False)` so the result may be a compact StrB64 path token such as
|
|
231
|
+
* `4AABAksn` instead of a labeler/text token.
|
|
232
|
+
*/
|
|
233
|
+
function encodePath(path) {
|
|
234
|
+
return makePather(path, { relative: true, pathive: false }).qb64;
|
|
235
|
+
}
|
|
236
|
+
/** Enclose already-encoded members inside one native generic-list group. */
|
|
237
|
+
function encodeList(entries, gvrsn) {
|
|
238
|
+
const frame = entries.join("");
|
|
239
|
+
const count = frame.length / 4;
|
|
240
|
+
const code = count < 64 ** 2
|
|
241
|
+
? CtrDexV2.GenericListGroup
|
|
242
|
+
: CtrDexV2.BigGenericListGroup;
|
|
243
|
+
return `${new Counter({ code, count, version: gvrsn ?? Vrsn_2_0 }).qb64}${frame}`;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Enclose one semantic field map inside either a top-level body-group or a
|
|
247
|
+
* nested generic map-group.
|
|
248
|
+
*/
|
|
249
|
+
function encodeMap(map, gvrsn, topLevel = false) {
|
|
250
|
+
let frame = "";
|
|
251
|
+
for (const [label, value] of Object.entries(map)) {
|
|
252
|
+
frame += encodeLabel(label);
|
|
253
|
+
frame += encodeValue(value, gvrsn, label);
|
|
254
|
+
}
|
|
255
|
+
const count = frame.length / 4;
|
|
256
|
+
const code = topLevel
|
|
257
|
+
? count < 64 ** 2 ? CtrDexV2.MapBodyGroup : CtrDexV2.BigMapBodyGroup
|
|
258
|
+
: count < 64 ** 2
|
|
259
|
+
? CtrDexV2.GenericMapGroup
|
|
260
|
+
: CtrDexV2.BigGenericMapGroup;
|
|
261
|
+
return `${new Counter({ code, count, version: gvrsn ?? Vrsn_2_0 }).qb64}${frame}`;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Best-effort native emit for one semantic field value.
|
|
265
|
+
*
|
|
266
|
+
* Decision order matters:
|
|
267
|
+
* 1. preserve already-qualified CESR primitives when present
|
|
268
|
+
* 2. apply field-specific semantic encoders
|
|
269
|
+
* 3. fall back to generic scalar/list/map encoders
|
|
270
|
+
*/
|
|
271
|
+
function encodeValue(value, gvrsn, label) {
|
|
272
|
+
if (typeof value === "string") {
|
|
273
|
+
if (value.length === 0 && label && ["u", "rd", "td"].includes(label)) {
|
|
274
|
+
return LabelDex.Empty;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
const primitive = parseMatter(b(value), "txt");
|
|
278
|
+
if (DIGEST_CODES.has(primitive.code)
|
|
279
|
+
|| PREFIX_CODES.has(primitive.code)
|
|
280
|
+
|| NON_DIGEST_PREFIX_CODES.has(primitive.code)
|
|
281
|
+
|| NONCE_CODES.has(primitive.code)
|
|
282
|
+
|| BEXTER_CODES.has(primitive.code)
|
|
283
|
+
|| TAG_CODES.has(primitive.code)
|
|
284
|
+
|| DATER_CODES.has(primitive.code)) {
|
|
285
|
+
return primitive.qb64;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// fall through to semantic encoding
|
|
290
|
+
}
|
|
291
|
+
if (label === "dt") {
|
|
292
|
+
return encodeDate(value);
|
|
293
|
+
}
|
|
294
|
+
if (label === "r" || label === "rr") {
|
|
295
|
+
return encodePath(value);
|
|
296
|
+
}
|
|
297
|
+
if (label === "ts") {
|
|
298
|
+
return encodeLabel(value);
|
|
299
|
+
}
|
|
300
|
+
if (/^[A-Za-z0-9_-]{1,11}$/.test(value)) {
|
|
301
|
+
return encodeTag(value);
|
|
302
|
+
}
|
|
303
|
+
if (/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
304
|
+
return encodeBext(value);
|
|
305
|
+
}
|
|
306
|
+
return encodeBytes(value);
|
|
307
|
+
}
|
|
308
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
309
|
+
return encodeNumber(value);
|
|
310
|
+
}
|
|
311
|
+
if (Array.isArray(value)) {
|
|
312
|
+
const entries = value.map((entry) => encodeValue(entry, gvrsn));
|
|
313
|
+
return encodeList(entries, gvrsn);
|
|
314
|
+
}
|
|
315
|
+
if (value && typeof value === "object") {
|
|
316
|
+
return encodeMap(value, gvrsn);
|
|
317
|
+
}
|
|
318
|
+
throw new SerializeError(`Unsupported native value=${String(value)}`);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Decode one parser graph entry back into a semantic SAD value.
|
|
322
|
+
*
|
|
323
|
+
* This stays parser-graph aware on purpose so native deserialization reuses the
|
|
324
|
+
* existing group dispatch machinery instead of maintaining a separate
|
|
325
|
+
* attachment/group decoder.
|
|
326
|
+
*/
|
|
327
|
+
function decodeEntry(entry, gvrsn) {
|
|
328
|
+
if (isPrimitiveTuple(entry)) {
|
|
329
|
+
return entry.map((item) => decodeEntry(item, gvrsn));
|
|
330
|
+
}
|
|
331
|
+
if (isCounterGroupLike(entry)) {
|
|
332
|
+
if (isAggorListCode(entry.code)) {
|
|
333
|
+
return entry.items.map((item) => decodeEntry(item, gvrsn));
|
|
334
|
+
}
|
|
335
|
+
if (isAggorMapCode(entry.code)) {
|
|
336
|
+
const aggor = parseAggor(b(Structor.fromGroup(entry).qb64g), gvrsn, "txt");
|
|
337
|
+
const fields = aggor.mapFields ?? [];
|
|
338
|
+
return Object.fromEntries(fields.map((field) => [field.label ?? "", decodeMapperField(field, gvrsn)]));
|
|
339
|
+
}
|
|
340
|
+
return Structor.fromGroup(entry).qb64g;
|
|
341
|
+
}
|
|
342
|
+
const matter = entry;
|
|
343
|
+
if (DIGEST_CODES.has(matter.code)
|
|
344
|
+
|| PREFIX_CODES.has(matter.code)
|
|
345
|
+
|| NON_DIGEST_PREFIX_CODES.has(matter.code)) {
|
|
346
|
+
return matter.qb64;
|
|
347
|
+
}
|
|
348
|
+
if (NONCE_CODES.has(matter.code)) {
|
|
349
|
+
return parseNoncer(b(matter.qb64), "txt").nonce;
|
|
350
|
+
}
|
|
351
|
+
if (NUMBER_CODES.has(matter.code)) {
|
|
352
|
+
return new NumberPrimitive(matter).numh;
|
|
353
|
+
}
|
|
354
|
+
if (DATER_CODES.has(matter.code)) {
|
|
355
|
+
return new Dater(matter).iso8601;
|
|
356
|
+
}
|
|
357
|
+
if (matter.code === MtrDex.DateTime) {
|
|
358
|
+
return new Dater(matter).iso8601;
|
|
359
|
+
}
|
|
360
|
+
if (TAG_CODES.has(matter.code)) {
|
|
361
|
+
if (matter.code === MtrDex.Tag3) {
|
|
362
|
+
return parseIlker(b(matter.qb64), "txt").ilk;
|
|
363
|
+
}
|
|
364
|
+
return new Labeler(matter).text;
|
|
365
|
+
}
|
|
366
|
+
if (BEXTER_CODES.has(matter.code)) {
|
|
367
|
+
return parsePather(b(matter.qb64), "txt").path;
|
|
368
|
+
}
|
|
369
|
+
return matter.qb64;
|
|
370
|
+
}
|
|
371
|
+
/** Recursively convert parsed mapper fields into nested JS map/list/scalar values. */
|
|
372
|
+
function decodeMapperField(field, gvrsn) {
|
|
373
|
+
if (field.children) {
|
|
374
|
+
return Object.fromEntries(field.children.map((child) => [child.label ?? "", decodeMapperField(child, gvrsn)]));
|
|
375
|
+
}
|
|
376
|
+
return decodeEntry(field.primitive, gvrsn);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Decode one native generic-list group.
|
|
380
|
+
*
|
|
381
|
+
* Items may be plain primitives or nested counted groups, so we probe group
|
|
382
|
+
* dispatch first and fall back to `Matter` only when no valid group starts at
|
|
383
|
+
* the current offset.
|
|
384
|
+
*/
|
|
385
|
+
function decodeList(raw, gvrsn) {
|
|
386
|
+
const counter = parseCounter(raw, gvrsn, "txt");
|
|
387
|
+
if (counter.code !== CtrDexV2.GenericListGroup
|
|
388
|
+
&& counter.code !== CtrDexV2.BigGenericListGroup) {
|
|
389
|
+
throw new DeserializeError(`Expected list group, got ${counter.code}`);
|
|
390
|
+
}
|
|
391
|
+
const payload = t(raw.slice(counter.fullSize, counter.fullSize + counter.count * 4));
|
|
392
|
+
const out = [];
|
|
393
|
+
let offset = 0;
|
|
394
|
+
while (offset < payload.length) {
|
|
395
|
+
const chunk = b(payload.slice(offset));
|
|
396
|
+
try {
|
|
397
|
+
const parsed = parseAttachmentDispatch(chunk, gvrsn, "txt");
|
|
398
|
+
out.push(decodeEntry(parsed.group, gvrsn));
|
|
399
|
+
offset += parsed.consumed;
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
const matter = parseMatter(chunk, "txt");
|
|
404
|
+
out.push(decodeEntry(matter, gvrsn));
|
|
405
|
+
offset += matter.fullSize;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Decode one native field that is already carried as a complete qb64 CESR
|
|
412
|
+
* primitive.
|
|
413
|
+
*
|
|
414
|
+
* Typical KERI fixed-body examples:
|
|
415
|
+
* - `d`: top-level SAID such as `EFaYE2LTv8dItUgQzIHKRA9FaHDrHtIHNs-m5DJKWXRN`
|
|
416
|
+
* - `i`: identifier prefix such as `DNG2arBDtHK_JyHRAq-emRdC6UM-yIpCAeJIWDiXp4Hx`
|
|
417
|
+
*
|
|
418
|
+
* In these cases the semantic value is the qb64 text itself, so the helper
|
|
419
|
+
* simply parses one `Matter` and returns its canonical qb64 string.
|
|
420
|
+
*/
|
|
421
|
+
function parseQb64Field(raw, offset) {
|
|
422
|
+
const matter = parseMatter(raw.slice(offset), "txt");
|
|
423
|
+
return {
|
|
424
|
+
value: matter.qb64,
|
|
425
|
+
nextOffset: offset + matter.fullSize,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Decode one native numeric field that KERI exposes as a hex string in the SAD.
|
|
430
|
+
*
|
|
431
|
+
* Typical KERI fixed-body examples:
|
|
432
|
+
* - `s`: sequence number, where native `MAAA` becomes semantic `"0"`
|
|
433
|
+
* - `bt`: witness threshold/count field, where native `MAAA` also becomes `"0"`
|
|
434
|
+
*
|
|
435
|
+
* Native CESR uses compact numeric primitives, but KERI SADs conventionally
|
|
436
|
+
* expose these values as lowercase hex strings, so this helper performs that
|
|
437
|
+
* projection.
|
|
438
|
+
*/
|
|
439
|
+
function parseNumericHexField(raw, offset) {
|
|
440
|
+
const matter = new NumberPrimitive({ qb64b: raw.slice(offset) });
|
|
441
|
+
return {
|
|
442
|
+
value: matter.numh,
|
|
443
|
+
nextOffset: offset + matter.fullSize,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Decode one native signing-threshold field into its SAD `sith` representation.
|
|
448
|
+
*
|
|
449
|
+
* Typical KERI fixed-body examples:
|
|
450
|
+
* - `kt`: current signing threshold, e.g. native `MAAB` -> semantic `"1"`
|
|
451
|
+
* - `nt`: next signing threshold, also commonly `MAAB` -> semantic `"1"`
|
|
452
|
+
*
|
|
453
|
+
* The native bytes are parsed through `Tholder`, which understands both simple
|
|
454
|
+
* numeric thresholds and the more complex weighted threshold encodings.
|
|
455
|
+
*/
|
|
456
|
+
function parseThresholdSithField(raw, offset) {
|
|
457
|
+
const matter = new Tholder({ qb64b: raw.slice(offset) });
|
|
458
|
+
return {
|
|
459
|
+
value: matter.sith,
|
|
460
|
+
nextOffset: offset + matter.fullSize,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Decode one native list field whose members are ordinary qb64 CESR primitives.
|
|
465
|
+
*
|
|
466
|
+
* Typical KERI fixed-body examples:
|
|
467
|
+
* - `k`: current key list
|
|
468
|
+
* - `n`: next-digest list
|
|
469
|
+
* - `b`: backer/witness prefix list
|
|
470
|
+
*
|
|
471
|
+
* Wire shape:
|
|
472
|
+
* `GenericListGroup counter` + `member1 qb64` + `member2 qb64` + ...
|
|
473
|
+
*/
|
|
474
|
+
function parseQb64ListField(raw, offset, gvrsn) {
|
|
475
|
+
const listCounter = parseCounter(raw.slice(offset), gvrsn, "txt");
|
|
476
|
+
const total = listCounter.fullSize + listCounter.count * 4;
|
|
477
|
+
const payload = t(raw.slice(offset + listCounter.fullSize, offset + total));
|
|
478
|
+
const items = [];
|
|
479
|
+
let inner = 0;
|
|
480
|
+
while (inner < payload.length) {
|
|
481
|
+
const matter = parseMatter(b(payload.slice(inner)), "txt");
|
|
482
|
+
items.push(matter.qb64);
|
|
483
|
+
inner += matter.fullSize;
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
value: items,
|
|
487
|
+
nextOffset: offset + total,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Decode one native trait/config list.
|
|
492
|
+
*
|
|
493
|
+
* Typical KERI fixed-body example:
|
|
494
|
+
* - `c`: configuration trait list such as `EO`, `NB`, etc.
|
|
495
|
+
*
|
|
496
|
+
* This looks list-shaped like `k`/`n`/`b`, but each element is a `Traitor`
|
|
497
|
+
* token whose semantic value is the compact trait string, not a generic qb64
|
|
498
|
+
* primitive string.
|
|
499
|
+
*/
|
|
500
|
+
function parseTraitListField(raw, offset, gvrsn) {
|
|
501
|
+
const listCounter = parseCounter(raw.slice(offset), gvrsn, "txt");
|
|
502
|
+
const total = listCounter.fullSize + listCounter.count * 4;
|
|
503
|
+
const payload = t(raw.slice(offset + listCounter.fullSize, offset + total));
|
|
504
|
+
const items = [];
|
|
505
|
+
let inner = 0;
|
|
506
|
+
while (inner < payload.length) {
|
|
507
|
+
const trait = parseTraitor(b(payload.slice(inner)), "txt");
|
|
508
|
+
items.push(trait.trait);
|
|
509
|
+
inner += trait.fullSize;
|
|
510
|
+
}
|
|
511
|
+
return {
|
|
512
|
+
value: items,
|
|
513
|
+
nextOffset: offset + total,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Decode one native list field whose members may themselves be nested groups.
|
|
518
|
+
*
|
|
519
|
+
* Typical KERI fixed-body example:
|
|
520
|
+
* - `a`: seal/data list in inception/rotation/interact-style messages
|
|
521
|
+
*
|
|
522
|
+
* Unlike `k`/`n`/`b`, the `a` field may contain grouped or structured payloads,
|
|
523
|
+
* so we reuse `decodeList()` instead of assuming each member is one plain
|
|
524
|
+
* `Matter`.
|
|
525
|
+
*/
|
|
526
|
+
function parseNestedSealOrDataListField(raw, offset, gvrsn) {
|
|
527
|
+
const listCounter = parseCounter(raw.slice(offset), gvrsn, "txt");
|
|
528
|
+
const total = listCounter.fullSize + listCounter.count * 4;
|
|
529
|
+
return {
|
|
530
|
+
value: listCounter.count === 0
|
|
531
|
+
? []
|
|
532
|
+
: decodeList(raw.slice(offset, offset + total), gvrsn),
|
|
533
|
+
nextOffset: offset + total,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Decode one native field-map block into semantic JS map form.
|
|
538
|
+
*
|
|
539
|
+
* `strict=false` is mainly for ACDC schema sections, which may carry `$id`
|
|
540
|
+
* instead of the usual `d` saidive label.
|
|
541
|
+
*/
|
|
542
|
+
function parseMapperField(raw, offset, gvrsn, strict = true) {
|
|
543
|
+
const mapper = new Mapper({
|
|
544
|
+
raw: raw.slice(offset),
|
|
545
|
+
version: gvrsn,
|
|
546
|
+
kind: "CESR",
|
|
547
|
+
verify: false,
|
|
548
|
+
strict,
|
|
549
|
+
});
|
|
550
|
+
return {
|
|
551
|
+
value: mapper.mad,
|
|
552
|
+
nextOffset: offset + mapper.raw.length,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function nativeLayoutKey(proto, version, ilk) {
|
|
556
|
+
return `${proto}:${version.major}.${version.minor}:${ilk ?? "<none>"}`;
|
|
557
|
+
}
|
|
558
|
+
function withKinds(proto, shape, labels, families, options = {}) {
|
|
559
|
+
return {
|
|
560
|
+
proto,
|
|
561
|
+
shape,
|
|
562
|
+
labels,
|
|
563
|
+
fields: Object.fromEntries(labels.map((label) => [
|
|
564
|
+
label,
|
|
565
|
+
{
|
|
566
|
+
kind: families[label],
|
|
567
|
+
...(options[label] ?? {}),
|
|
568
|
+
},
|
|
569
|
+
])),
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
const KERI_NATIVE_FAMILIES = {
|
|
573
|
+
d: "said",
|
|
574
|
+
p: "said",
|
|
575
|
+
x: "said",
|
|
576
|
+
u: "noncer",
|
|
577
|
+
i: "aid",
|
|
578
|
+
di: "aid",
|
|
579
|
+
ri: "aid",
|
|
580
|
+
s: "number",
|
|
581
|
+
bt: "number",
|
|
582
|
+
kt: "threshold",
|
|
583
|
+
nt: "threshold",
|
|
584
|
+
k: "primitive-list",
|
|
585
|
+
n: "primitive-list",
|
|
586
|
+
b: "primitive-list",
|
|
587
|
+
ba: "primitive-list",
|
|
588
|
+
br: "primitive-list",
|
|
589
|
+
dt: "datetime",
|
|
590
|
+
r: "route",
|
|
591
|
+
rr: "route",
|
|
592
|
+
rp: "route",
|
|
593
|
+
c: "trait-list",
|
|
594
|
+
a: "seal-list-or-mapper",
|
|
595
|
+
e: "mapper",
|
|
596
|
+
q: "mapper",
|
|
597
|
+
};
|
|
598
|
+
const ACDC_NATIVE_FAMILIES = {
|
|
599
|
+
d: "said",
|
|
600
|
+
p: "said",
|
|
601
|
+
b: "said",
|
|
602
|
+
u: "nonce-or-empty",
|
|
603
|
+
i: "aid",
|
|
604
|
+
ri: "said",
|
|
605
|
+
rd: "nonce-or-empty",
|
|
606
|
+
n: "number",
|
|
607
|
+
dt: "datetime",
|
|
608
|
+
td: "nonce-or-empty",
|
|
609
|
+
ts: "label-text",
|
|
610
|
+
s: "said-or-mapper",
|
|
611
|
+
a: "said-or-mapper",
|
|
612
|
+
e: "said-or-mapper",
|
|
613
|
+
r: "said-or-mapper",
|
|
614
|
+
A: "agid-or-aggor",
|
|
615
|
+
};
|
|
616
|
+
const NATIVE_LAYOUTS = new Map([
|
|
617
|
+
[
|
|
618
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.icp),
|
|
619
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s", "kt", "k", "nt", "n", "bt", "b", "c", "a"], KERI_NATIVE_FAMILIES),
|
|
620
|
+
],
|
|
621
|
+
[
|
|
622
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.rot),
|
|
623
|
+
withKinds(Protocols.keri, "fixed", [
|
|
624
|
+
"d",
|
|
625
|
+
"i",
|
|
626
|
+
"s",
|
|
627
|
+
"p",
|
|
628
|
+
"kt",
|
|
629
|
+
"k",
|
|
630
|
+
"nt",
|
|
631
|
+
"n",
|
|
632
|
+
"bt",
|
|
633
|
+
"br",
|
|
634
|
+
"ba",
|
|
635
|
+
"a",
|
|
636
|
+
], KERI_NATIVE_FAMILIES),
|
|
637
|
+
],
|
|
638
|
+
[
|
|
639
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.ixn),
|
|
640
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s", "p", "a"], KERI_NATIVE_FAMILIES),
|
|
641
|
+
],
|
|
642
|
+
[
|
|
643
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.dip),
|
|
644
|
+
withKinds(Protocols.keri, "fixed", [
|
|
645
|
+
"d",
|
|
646
|
+
"i",
|
|
647
|
+
"s",
|
|
648
|
+
"kt",
|
|
649
|
+
"k",
|
|
650
|
+
"nt",
|
|
651
|
+
"n",
|
|
652
|
+
"bt",
|
|
653
|
+
"b",
|
|
654
|
+
"c",
|
|
655
|
+
"a",
|
|
656
|
+
"di",
|
|
657
|
+
], KERI_NATIVE_FAMILIES),
|
|
658
|
+
],
|
|
659
|
+
[
|
|
660
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.drt),
|
|
661
|
+
withKinds(Protocols.keri, "fixed", [
|
|
662
|
+
"d",
|
|
663
|
+
"i",
|
|
664
|
+
"s",
|
|
665
|
+
"p",
|
|
666
|
+
"kt",
|
|
667
|
+
"k",
|
|
668
|
+
"nt",
|
|
669
|
+
"n",
|
|
670
|
+
"bt",
|
|
671
|
+
"br",
|
|
672
|
+
"ba",
|
|
673
|
+
"a",
|
|
674
|
+
], KERI_NATIVE_FAMILIES),
|
|
675
|
+
],
|
|
676
|
+
[
|
|
677
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.rct),
|
|
678
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s"], KERI_NATIVE_FAMILIES),
|
|
679
|
+
],
|
|
680
|
+
[
|
|
681
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.qry),
|
|
682
|
+
withKinds(Protocols.keri, "fixed", ["d", "dt", "r", "rr", "q"], KERI_NATIVE_FAMILIES),
|
|
683
|
+
],
|
|
684
|
+
[
|
|
685
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.rpy),
|
|
686
|
+
withKinds(Protocols.keri, "fixed", ["d", "dt", "r", "a"], KERI_NATIVE_FAMILIES),
|
|
687
|
+
],
|
|
688
|
+
[
|
|
689
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.pro),
|
|
690
|
+
withKinds(Protocols.keri, "fixed", ["d", "dt", "r", "rr", "q"], KERI_NATIVE_FAMILIES),
|
|
691
|
+
],
|
|
692
|
+
[
|
|
693
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.bar),
|
|
694
|
+
withKinds(Protocols.keri, "fixed", ["d", "dt", "r", "a"], KERI_NATIVE_FAMILIES),
|
|
695
|
+
],
|
|
696
|
+
[
|
|
697
|
+
nativeLayoutKey(Protocols.keri, { major: 1, minor: 0 }, Ilks.exn),
|
|
698
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "rp", "p", "dt", "r", "q", "a", "e"], KERI_NATIVE_FAMILIES),
|
|
699
|
+
],
|
|
700
|
+
[
|
|
701
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.icp),
|
|
702
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s", "kt", "k", "nt", "n", "bt", "b", "c", "a"], KERI_NATIVE_FAMILIES),
|
|
703
|
+
],
|
|
704
|
+
[
|
|
705
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.rot),
|
|
706
|
+
withKinds(Protocols.keri, "fixed", [
|
|
707
|
+
"d",
|
|
708
|
+
"i",
|
|
709
|
+
"s",
|
|
710
|
+
"p",
|
|
711
|
+
"kt",
|
|
712
|
+
"k",
|
|
713
|
+
"nt",
|
|
714
|
+
"n",
|
|
715
|
+
"bt",
|
|
716
|
+
"br",
|
|
717
|
+
"ba",
|
|
718
|
+
"c",
|
|
719
|
+
"a",
|
|
720
|
+
], KERI_NATIVE_FAMILIES),
|
|
721
|
+
],
|
|
722
|
+
[
|
|
723
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.ixn),
|
|
724
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s", "p", "a"], KERI_NATIVE_FAMILIES),
|
|
725
|
+
],
|
|
726
|
+
[
|
|
727
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.dip),
|
|
728
|
+
withKinds(Protocols.keri, "fixed", [
|
|
729
|
+
"d",
|
|
730
|
+
"i",
|
|
731
|
+
"s",
|
|
732
|
+
"kt",
|
|
733
|
+
"k",
|
|
734
|
+
"nt",
|
|
735
|
+
"n",
|
|
736
|
+
"bt",
|
|
737
|
+
"b",
|
|
738
|
+
"c",
|
|
739
|
+
"a",
|
|
740
|
+
"di",
|
|
741
|
+
], KERI_NATIVE_FAMILIES),
|
|
742
|
+
],
|
|
743
|
+
[
|
|
744
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.drt),
|
|
745
|
+
withKinds(Protocols.keri, "fixed", [
|
|
746
|
+
"d",
|
|
747
|
+
"i",
|
|
748
|
+
"s",
|
|
749
|
+
"p",
|
|
750
|
+
"kt",
|
|
751
|
+
"k",
|
|
752
|
+
"nt",
|
|
753
|
+
"n",
|
|
754
|
+
"bt",
|
|
755
|
+
"br",
|
|
756
|
+
"ba",
|
|
757
|
+
"c",
|
|
758
|
+
"a",
|
|
759
|
+
], KERI_NATIVE_FAMILIES),
|
|
760
|
+
],
|
|
761
|
+
[
|
|
762
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.rct),
|
|
763
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "s"], KERI_NATIVE_FAMILIES),
|
|
764
|
+
],
|
|
765
|
+
[
|
|
766
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.qry),
|
|
767
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "dt", "r", "rr", "q"], KERI_NATIVE_FAMILIES),
|
|
768
|
+
],
|
|
769
|
+
[
|
|
770
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.rpy),
|
|
771
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "dt", "r", "a"], KERI_NATIVE_FAMILIES),
|
|
772
|
+
],
|
|
773
|
+
[
|
|
774
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.pro),
|
|
775
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "dt", "r", "rr", "q"], KERI_NATIVE_FAMILIES),
|
|
776
|
+
],
|
|
777
|
+
[
|
|
778
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.bar),
|
|
779
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "dt", "r", "a"], KERI_NATIVE_FAMILIES),
|
|
780
|
+
],
|
|
781
|
+
[
|
|
782
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.xip),
|
|
783
|
+
withKinds(Protocols.keri, "fixed", ["d", "u", "i", "ri", "dt", "r", "q", "a"], KERI_NATIVE_FAMILIES),
|
|
784
|
+
],
|
|
785
|
+
[
|
|
786
|
+
nativeLayoutKey(Protocols.keri, { major: 2, minor: 0 }, Ilks.exn),
|
|
787
|
+
withKinds(Protocols.keri, "fixed", ["d", "i", "ri", "x", "p", "dt", "r", "q", "a"], KERI_NATIVE_FAMILIES),
|
|
788
|
+
],
|
|
789
|
+
[
|
|
790
|
+
nativeLayoutKey(Protocols.acdc, { major: 1, minor: 0 }, null),
|
|
791
|
+
withKinds(Protocols.acdc, "map", ["v", "d", "u", "i", "ri", "s", "a", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
792
|
+
s: { strict: false },
|
|
793
|
+
}),
|
|
794
|
+
],
|
|
795
|
+
[
|
|
796
|
+
nativeLayoutKey(Protocols.acdc, { major: 1, minor: 0 }, Ilks.ace),
|
|
797
|
+
withKinds(Protocols.acdc, "map", ["v", "t", "d", "u", "i", "ri", "s", "a", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
798
|
+
s: { strict: false },
|
|
799
|
+
}),
|
|
800
|
+
],
|
|
801
|
+
[
|
|
802
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, null),
|
|
803
|
+
withKinds(Protocols.acdc, "map", ["v", "d", "u", "i", "rd", "s", "a", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
804
|
+
s: { strict: false },
|
|
805
|
+
}),
|
|
806
|
+
],
|
|
807
|
+
[
|
|
808
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.acm),
|
|
809
|
+
withKinds(Protocols.acdc, "map", ["v", "t", "d", "u", "i", "rd", "s", "a", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
810
|
+
s: { strict: false },
|
|
811
|
+
}),
|
|
812
|
+
],
|
|
813
|
+
[
|
|
814
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.ace),
|
|
815
|
+
withKinds(Protocols.acdc, "map", ["v", "t", "d", "u", "i", "ri", "s", "a", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
816
|
+
s: { strict: false },
|
|
817
|
+
}),
|
|
818
|
+
],
|
|
819
|
+
[
|
|
820
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.act),
|
|
821
|
+
withKinds(Protocols.acdc, "fixed", ["d", "u", "i", "rd", "s", "a", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
822
|
+
s: { strict: false },
|
|
823
|
+
}),
|
|
824
|
+
],
|
|
825
|
+
[
|
|
826
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.acg),
|
|
827
|
+
withKinds(Protocols.acdc, "fixed", ["d", "u", "i", "rd", "s", "A", "e", "r"], ACDC_NATIVE_FAMILIES, {
|
|
828
|
+
s: { strict: false },
|
|
829
|
+
}),
|
|
830
|
+
],
|
|
831
|
+
[
|
|
832
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.sch),
|
|
833
|
+
withKinds(Protocols.acdc, "fixed", ["d", "s"], ACDC_NATIVE_FAMILIES, {
|
|
834
|
+
s: { strict: false },
|
|
835
|
+
}),
|
|
836
|
+
],
|
|
837
|
+
[
|
|
838
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.att),
|
|
839
|
+
withKinds(Protocols.acdc, "fixed", ["d", "a"], ACDC_NATIVE_FAMILIES),
|
|
840
|
+
],
|
|
841
|
+
[
|
|
842
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.agg),
|
|
843
|
+
withKinds(Protocols.acdc, "fixed", ["d", "A"], ACDC_NATIVE_FAMILIES),
|
|
844
|
+
],
|
|
845
|
+
[
|
|
846
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.edg),
|
|
847
|
+
withKinds(Protocols.acdc, "fixed", ["d", "e"], ACDC_NATIVE_FAMILIES),
|
|
848
|
+
],
|
|
849
|
+
[
|
|
850
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.rul),
|
|
851
|
+
withKinds(Protocols.acdc, "fixed", ["d", "r"], ACDC_NATIVE_FAMILIES),
|
|
852
|
+
],
|
|
853
|
+
[
|
|
854
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.rip),
|
|
855
|
+
withKinds(Protocols.acdc, "fixed", ["d", "u", "i", "n", "dt"], ACDC_NATIVE_FAMILIES),
|
|
856
|
+
],
|
|
857
|
+
[
|
|
858
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.bup),
|
|
859
|
+
withKinds(Protocols.acdc, "fixed", ["d", "rd", "n", "p", "dt", "b"], ACDC_NATIVE_FAMILIES),
|
|
860
|
+
],
|
|
861
|
+
[
|
|
862
|
+
nativeLayoutKey(Protocols.acdc, { major: 2, minor: 0 }, Ilks.upd),
|
|
863
|
+
withKinds(Protocols.acdc, "fixed", ["d", "rd", "n", "p", "dt", "td", "ts"], ACDC_NATIVE_FAMILIES),
|
|
864
|
+
],
|
|
865
|
+
]);
|
|
866
|
+
/** Resolve the protocol/version/ilk entry from the shared CESR-native support matrix. */
|
|
867
|
+
function getNativeLayout(proto, version, ilk) {
|
|
868
|
+
const layout = NATIVE_LAYOUTS.get(nativeLayoutKey(proto, version, ilk));
|
|
869
|
+
if (!layout) {
|
|
870
|
+
throw new DeserializeError(`Unsupported ${proto} native ilk=${String(ilk)} version=${version.major}.${version.minor}`);
|
|
871
|
+
}
|
|
872
|
+
return layout;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Decode one native field according to the field-family declared in the
|
|
876
|
+
* support matrix.
|
|
877
|
+
*/
|
|
878
|
+
function parseNativeField(raw, offset, label, spec, gvrsn) {
|
|
879
|
+
if (spec.kind === "said" || spec.kind === "aid") {
|
|
880
|
+
return parseQb64Field(raw, offset);
|
|
881
|
+
}
|
|
882
|
+
if (spec.kind === "noncer") {
|
|
883
|
+
const nonce = parseNoncer(raw.slice(offset), "txt");
|
|
884
|
+
return {
|
|
885
|
+
value: nonce.qb64,
|
|
886
|
+
nextOffset: offset + nonce.fullSize,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
if (spec.kind === "nonce-or-empty") {
|
|
890
|
+
const nonce = parseNoncer(raw.slice(offset), "txt");
|
|
891
|
+
return {
|
|
892
|
+
value: nonce.nonce,
|
|
893
|
+
nextOffset: offset + nonce.fullSize,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
if (spec.kind === "number") {
|
|
897
|
+
return parseNumericHexField(raw, offset);
|
|
898
|
+
}
|
|
899
|
+
if (spec.kind === "threshold") {
|
|
900
|
+
return parseThresholdSithField(raw, offset);
|
|
901
|
+
}
|
|
902
|
+
if (spec.kind === "primitive-list") {
|
|
903
|
+
return parseQb64ListField(raw, offset, gvrsn);
|
|
904
|
+
}
|
|
905
|
+
if (spec.kind === "trait-list") {
|
|
906
|
+
return parseTraitListField(raw, offset, gvrsn);
|
|
907
|
+
}
|
|
908
|
+
if (spec.kind === "datetime") {
|
|
909
|
+
const date = new Dater({ qb64b: raw.slice(offset) });
|
|
910
|
+
return {
|
|
911
|
+
value: date.iso8601,
|
|
912
|
+
nextOffset: offset + date.fullSize,
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
if (spec.kind === "route") {
|
|
916
|
+
// Route fields use Pather semantics, not generic labels. That means a
|
|
917
|
+
// native token like `4AAEcredential-issue` must round-trip back to the
|
|
918
|
+
// slash-delimited semantic route `credential/issue`.
|
|
919
|
+
const pather = parsePather(raw.slice(offset), "txt");
|
|
920
|
+
return {
|
|
921
|
+
value: pather.path,
|
|
922
|
+
nextOffset: offset + pather.fullSize,
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
if (spec.kind === "mapper") {
|
|
926
|
+
return parseMapperField(raw, offset, gvrsn, spec.strict ?? true);
|
|
927
|
+
}
|
|
928
|
+
if (spec.kind === "seal-list-or-mapper") {
|
|
929
|
+
const counter = parseCounter(raw.slice(offset), gvrsn, "txt");
|
|
930
|
+
if (counter.code === CtrDexV2.GenericMapGroup
|
|
931
|
+
|| counter.code === CtrDexV2.BigGenericMapGroup) {
|
|
932
|
+
return parseMapperField(raw, offset, gvrsn, spec.strict ?? true);
|
|
933
|
+
}
|
|
934
|
+
if (counter.code === CtrDexV2.GenericListGroup
|
|
935
|
+
|| counter.code === CtrDexV2.BigGenericListGroup) {
|
|
936
|
+
return parseNestedSealOrDataListField(raw, offset, gvrsn);
|
|
937
|
+
}
|
|
938
|
+
throw new DeserializeError(`Expected native list/map group for field ${label}, got ${counter.code}`);
|
|
939
|
+
}
|
|
940
|
+
if (spec.kind === "said-or-mapper") {
|
|
941
|
+
if (raw[offset] === "-".charCodeAt(0)) {
|
|
942
|
+
return parseMapperField(raw, offset, gvrsn, spec.strict ?? true);
|
|
943
|
+
}
|
|
944
|
+
return parseQb64Field(raw, offset);
|
|
945
|
+
}
|
|
946
|
+
if (spec.kind === "agid-or-aggor") {
|
|
947
|
+
if (raw[offset] === "-".charCodeAt(0)) {
|
|
948
|
+
const aggor = new Aggor({
|
|
949
|
+
raw: raw.slice(offset),
|
|
950
|
+
version: gvrsn,
|
|
951
|
+
kind: "CESR",
|
|
952
|
+
verify: false,
|
|
953
|
+
});
|
|
954
|
+
return {
|
|
955
|
+
value: aggor.ael,
|
|
956
|
+
nextOffset: offset + aggor.serialized.length,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
return parseQb64Field(raw, offset);
|
|
960
|
+
}
|
|
961
|
+
if (spec.kind === "label-text") {
|
|
962
|
+
const text = parseLabeler(raw.slice(offset), "txt");
|
|
963
|
+
return {
|
|
964
|
+
value: text.text,
|
|
965
|
+
nextOffset: offset + text.fullSize,
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
throw new DeserializeError(`Unsupported native field label=${label}`);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Encode one semantic SAD field back into the native field-family form
|
|
972
|
+
* declared in the support matrix.
|
|
973
|
+
*/
|
|
974
|
+
function encodeNativeFieldValue(value, label, spec, gvrsn) {
|
|
975
|
+
if (spec.kind === "said" || spec.kind === "aid" || spec.kind === "noncer") {
|
|
976
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
977
|
+
throw new SerializeError(`Expected non-empty qb64 value for native field ${label}`);
|
|
978
|
+
}
|
|
979
|
+
return value;
|
|
980
|
+
}
|
|
981
|
+
if (spec.kind === "nonce-or-empty") {
|
|
982
|
+
if (typeof value !== "string") {
|
|
983
|
+
throw new SerializeError(`Expected string nonce value for native field ${label}`);
|
|
984
|
+
}
|
|
985
|
+
return value.length === 0 ? LabelDex.Empty : value;
|
|
986
|
+
}
|
|
987
|
+
if (spec.kind === "number") {
|
|
988
|
+
if (typeof value !== "string"
|
|
989
|
+
&& typeof value !== "number"
|
|
990
|
+
&& typeof value !== "bigint") {
|
|
991
|
+
throw new SerializeError(`Expected numeric value for native field ${label}`);
|
|
992
|
+
}
|
|
993
|
+
return encodeNumber(value);
|
|
994
|
+
}
|
|
995
|
+
if (spec.kind === "threshold") {
|
|
996
|
+
if (typeof value !== "string"
|
|
997
|
+
&& typeof value !== "number"
|
|
998
|
+
&& typeof value !== "bigint"
|
|
999
|
+
&& !Array.isArray(value)) {
|
|
1000
|
+
throw new SerializeError(`Expected threshold value for native field ${label}`);
|
|
1001
|
+
}
|
|
1002
|
+
return encodeThreshold(value);
|
|
1003
|
+
}
|
|
1004
|
+
if (spec.kind === "primitive-list") {
|
|
1005
|
+
if (!Array.isArray(value)) {
|
|
1006
|
+
throw new SerializeError(`Expected primitive list for native field ${label}`);
|
|
1007
|
+
}
|
|
1008
|
+
return encodeList(value.map((entry) => {
|
|
1009
|
+
if (typeof entry !== "string" || entry.length === 0) {
|
|
1010
|
+
throw new SerializeError(`Expected qb64 list member for native field ${label}`);
|
|
1011
|
+
}
|
|
1012
|
+
return entry;
|
|
1013
|
+
}), gvrsn);
|
|
1014
|
+
}
|
|
1015
|
+
if (spec.kind === "trait-list") {
|
|
1016
|
+
if (!Array.isArray(value)) {
|
|
1017
|
+
throw new SerializeError(`Expected trait list for native field ${label}`);
|
|
1018
|
+
}
|
|
1019
|
+
return encodeList(value.map((entry) => encodeTag(String(entry))), gvrsn);
|
|
1020
|
+
}
|
|
1021
|
+
if (spec.kind === "datetime") {
|
|
1022
|
+
if (typeof value !== "string") {
|
|
1023
|
+
throw new SerializeError(`Expected ISO-8601 string for native field ${label}`);
|
|
1024
|
+
}
|
|
1025
|
+
return encodeDate(value);
|
|
1026
|
+
}
|
|
1027
|
+
if (spec.kind === "route") {
|
|
1028
|
+
if (typeof value !== "string") {
|
|
1029
|
+
throw new SerializeError(`Expected route/path string for native field ${label}`);
|
|
1030
|
+
}
|
|
1031
|
+
return encodePath(value);
|
|
1032
|
+
}
|
|
1033
|
+
if (spec.kind === "mapper") {
|
|
1034
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1035
|
+
return Mapper.fromSad(value, {
|
|
1036
|
+
strict: spec.strict ?? true,
|
|
1037
|
+
kind: "CESR",
|
|
1038
|
+
verify: false,
|
|
1039
|
+
saidive: false,
|
|
1040
|
+
}).qb64;
|
|
1041
|
+
}
|
|
1042
|
+
throw new SerializeError(`Expected map value for native field ${label}`);
|
|
1043
|
+
}
|
|
1044
|
+
if (spec.kind === "seal-list-or-mapper") {
|
|
1045
|
+
if (Array.isArray(value)) {
|
|
1046
|
+
return encodeList(value.map((entry) => encodeValue(entry, gvrsn)), gvrsn);
|
|
1047
|
+
}
|
|
1048
|
+
if (value && typeof value === "object") {
|
|
1049
|
+
return Mapper.fromSad(value, {
|
|
1050
|
+
strict: spec.strict ?? true,
|
|
1051
|
+
kind: "CESR",
|
|
1052
|
+
verify: false,
|
|
1053
|
+
saidive: false,
|
|
1054
|
+
}).qb64;
|
|
1055
|
+
}
|
|
1056
|
+
throw new SerializeError(`Expected list or map for native field ${label}`);
|
|
1057
|
+
}
|
|
1058
|
+
if (spec.kind === "said-or-mapper") {
|
|
1059
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1060
|
+
return Mapper.fromSad(value, {
|
|
1061
|
+
strict: spec.strict ?? true,
|
|
1062
|
+
kind: "CESR",
|
|
1063
|
+
verify: false,
|
|
1064
|
+
saidive: false,
|
|
1065
|
+
}).qb64;
|
|
1066
|
+
}
|
|
1067
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
1068
|
+
throw new SerializeError(`Expected qb64 or map for native field ${label}`);
|
|
1069
|
+
}
|
|
1070
|
+
return value;
|
|
1071
|
+
}
|
|
1072
|
+
if (spec.kind === "agid-or-aggor") {
|
|
1073
|
+
if (Array.isArray(value)) {
|
|
1074
|
+
return new Aggor({
|
|
1075
|
+
ael: value,
|
|
1076
|
+
kind: "CESR",
|
|
1077
|
+
makify: false,
|
|
1078
|
+
verify: false,
|
|
1079
|
+
}).qb64g;
|
|
1080
|
+
}
|
|
1081
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
1082
|
+
throw new SerializeError(`Expected agid string or aggregate list for native field ${label}`);
|
|
1083
|
+
}
|
|
1084
|
+
return value;
|
|
1085
|
+
}
|
|
1086
|
+
if (spec.kind === "label-text") {
|
|
1087
|
+
if (typeof value !== "string") {
|
|
1088
|
+
throw new SerializeError(`Expected text string for native field ${label}`);
|
|
1089
|
+
}
|
|
1090
|
+
return encodeLabel(value);
|
|
1091
|
+
}
|
|
1092
|
+
throw new SerializeError(`Unsupported native field kind for ${label}`);
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Decode one CESR-native body into a semantic SAD plus core serder projections.
|
|
1096
|
+
*
|
|
1097
|
+
* Current intent is narrow but real: hydrate actual native message bodies into
|
|
1098
|
+
* something `Serder` can verify and expose through its normal accessor surface.
|
|
1099
|
+
* Broader KERI/ACDC ilk parity should continue extending this function rather
|
|
1100
|
+
* than introducing sidecar native decoders elsewhere in the stack.
|
|
1101
|
+
*
|
|
1102
|
+
* Inputs:
|
|
1103
|
+
* - `raw`: full native body-group bytes, either qb64 text or qb2 binary
|
|
1104
|
+
* - `smellage`: protocol/version metadata already derived by `Serdery` or a
|
|
1105
|
+
* parser-side native pre-read
|
|
1106
|
+
*
|
|
1107
|
+
* Output:
|
|
1108
|
+
* - `ked`: semantic SAD reconstructed from compact CESR fields
|
|
1109
|
+
* - `ilk`: top-level message type when present
|
|
1110
|
+
* - `said`: top-level `d` SAID when present
|
|
1111
|
+
*
|
|
1112
|
+
* High-level flow:
|
|
1113
|
+
* 1. canonicalize qb2 to qb64 text if needed
|
|
1114
|
+
* 2. parse the body-group counter (`-F...` or `-G...`)
|
|
1115
|
+
* 3. consume version metadata (`Verser`) and then protocol-specific fields
|
|
1116
|
+
* 4. rebuild normal SAD strings/lists/maps so `Serder` verification can run on
|
|
1117
|
+
* the same shape used by JSON/CBOR/MGPK bodies
|
|
1118
|
+
*
|
|
1119
|
+
* Maintainer rule:
|
|
1120
|
+
* top-level KERI native messages are fixed-field bodies. A top-level
|
|
1121
|
+
* `MapBodyGroup` may still be meaningful for lower-level CESR-native mapping
|
|
1122
|
+
* surfaces or ACDC, but it is not a valid KERI native message body and should
|
|
1123
|
+
* be rejected here.
|
|
1124
|
+
*
|
|
1125
|
+
* Example:
|
|
1126
|
+
*
|
|
1127
|
+
* ```ts
|
|
1128
|
+
* const { ked } = parseCesrNativeKed(
|
|
1129
|
+
* new TextEncoder().encode("-FA50OKERICAACAXicp..."),
|
|
1130
|
+
* { proto: "KERI", pvrsn: { major: 2, minor: 0 }, gvrsn: { major: 2, minor: 0 }, kind: "CESR", size: 188 },
|
|
1131
|
+
* );
|
|
1132
|
+
* // ked.t === "icp"
|
|
1133
|
+
* // ked.d === "EFaYE2LTv8dItUgQzIHKRA9FaHDrHtIHNs-m5DJKWXRN"
|
|
1134
|
+
* ```
|
|
1135
|
+
*/
|
|
1136
|
+
export function parseCesrNativeKed(raw, smellage) {
|
|
1137
|
+
const gvrsn = smellage.gvrsn ?? smellage.pvrsn;
|
|
1138
|
+
const textRaw = canonicalizeCesrNativeRaw(raw, gvrsn);
|
|
1139
|
+
const counter = parseCounter(textRaw, gvrsn, "txt");
|
|
1140
|
+
let offset = counter.fullSize;
|
|
1141
|
+
const fixed = counter.code === CtrDexV2.FixBodyGroup
|
|
1142
|
+
|| counter.code === CtrDexV2.BigFixBodyGroup;
|
|
1143
|
+
if (!fixed) {
|
|
1144
|
+
const versionLabel = parseLabeler(textRaw.slice(offset), "txt");
|
|
1145
|
+
if (versionLabel.label !== "v") {
|
|
1146
|
+
throw new DeserializeError(`Expected native version label 'v', got ${versionLabel.label}`);
|
|
1147
|
+
}
|
|
1148
|
+
offset += versionLabel.fullSize;
|
|
1149
|
+
}
|
|
1150
|
+
const verser = parseVerser(textRaw.slice(offset), "txt");
|
|
1151
|
+
offset += verser.fullSize;
|
|
1152
|
+
const ked = {
|
|
1153
|
+
v: versify({
|
|
1154
|
+
proto: verser.proto,
|
|
1155
|
+
pvrsn: verser.pvrsn,
|
|
1156
|
+
gvrsn: verser.gvrsn,
|
|
1157
|
+
kind: Kinds.cesr,
|
|
1158
|
+
size: textRaw.length,
|
|
1159
|
+
}),
|
|
1160
|
+
};
|
|
1161
|
+
if (smellage.proto === Protocols.keri && !fixed) {
|
|
1162
|
+
throw new DeserializeError("KERI CESR-native top-level messages must use FixBodyGroup, not MapBodyGroup");
|
|
1163
|
+
}
|
|
1164
|
+
let ilk = fixed ? parseIlker(textRaw.slice(offset), "txt").ilk : null;
|
|
1165
|
+
if (fixed && ilk !== null) {
|
|
1166
|
+
const ilker = parseIlker(textRaw.slice(offset), "txt");
|
|
1167
|
+
offset += ilker.fullSize;
|
|
1168
|
+
ked.t = ilker.ilk;
|
|
1169
|
+
ilk = ilker.ilk;
|
|
1170
|
+
}
|
|
1171
|
+
let layout = getNativeLayout(smellage.proto, smellage.pvrsn, ilk);
|
|
1172
|
+
if ((fixed && layout.shape !== "fixed") || (!fixed && layout.shape !== "map")) {
|
|
1173
|
+
throw new DeserializeError(`${smellage.proto} CESR-native ilk=${String(ilk)} requires ${layout.shape} body shape`);
|
|
1174
|
+
}
|
|
1175
|
+
if (fixed) {
|
|
1176
|
+
for (const label of layout.labels) {
|
|
1177
|
+
const parsed = parseNativeField(textRaw, offset, label, layout.fields[label], gvrsn);
|
|
1178
|
+
ked[label] = parsed.value;
|
|
1179
|
+
offset = parsed.nextOffset;
|
|
1180
|
+
}
|
|
1181
|
+
return {
|
|
1182
|
+
ked,
|
|
1183
|
+
ilk,
|
|
1184
|
+
said: typeof ked.d === "string" ? ked.d : null,
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
while (offset < textRaw.length) {
|
|
1188
|
+
const labeler = parseLabeler(textRaw.slice(offset), "txt");
|
|
1189
|
+
const label = labeler.label;
|
|
1190
|
+
offset += labeler.fullSize;
|
|
1191
|
+
if (!label) {
|
|
1192
|
+
break;
|
|
1193
|
+
}
|
|
1194
|
+
if (label === "t") {
|
|
1195
|
+
const ilker = parseIlker(textRaw.slice(offset), "txt");
|
|
1196
|
+
ked.t = ilker.ilk;
|
|
1197
|
+
ilk = ilker.ilk;
|
|
1198
|
+
layout = getNativeLayout(smellage.proto, smellage.pvrsn, ilk);
|
|
1199
|
+
offset += ilker.fullSize;
|
|
1200
|
+
continue;
|
|
1201
|
+
}
|
|
1202
|
+
const spec = layout.fields[label];
|
|
1203
|
+
if (!spec) {
|
|
1204
|
+
throw new DeserializeError(`Unsupported ${smellage.proto} native label=${label} for ilk=${String(ilk)}`);
|
|
1205
|
+
}
|
|
1206
|
+
const parsed = parseNativeField(textRaw, offset, label, spec, gvrsn);
|
|
1207
|
+
ked[label] = parsed.value;
|
|
1208
|
+
offset = parsed.nextOffset;
|
|
1209
|
+
}
|
|
1210
|
+
return {
|
|
1211
|
+
ked,
|
|
1212
|
+
ilk,
|
|
1213
|
+
said: typeof ked.d === "string" ? ked.d : null,
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Serialize one semantic SAD into CESR-native qb64 text-domain bytes.
|
|
1218
|
+
*
|
|
1219
|
+
* Callers that start from qb2 should still route through this text-domain emit
|
|
1220
|
+
* path; qb2 is derived from qb64 later, matching the canonicalization path used
|
|
1221
|
+
* during native inhale.
|
|
1222
|
+
*
|
|
1223
|
+
* Inputs:
|
|
1224
|
+
* - one semantic SAD in the same shape used by non-native serders
|
|
1225
|
+
*
|
|
1226
|
+
* Output:
|
|
1227
|
+
* - qb64 text-domain bytes for a CESR-native body group
|
|
1228
|
+
*
|
|
1229
|
+
* High-level flow:
|
|
1230
|
+
* 1. derive protocol/version context from the SAD's `v`
|
|
1231
|
+
* 2. emit compact CESR primitives for each field (`Verser`, `Ilker`, `Matter`,
|
|
1232
|
+
* grouped lists/maps, etc.)
|
|
1233
|
+
* 3. wrap the payload in `FixBodyGroup` or `MapBodyGroup`
|
|
1234
|
+
*
|
|
1235
|
+
* ASCII shape of the result for a KERI fixed-body message:
|
|
1236
|
+
*
|
|
1237
|
+
* ```text
|
|
1238
|
+
* -F... <verser> <ilk> <d> <i> <s> <kt> <k-list> <nt> <n-list> ...
|
|
1239
|
+
* ```
|
|
1240
|
+
*
|
|
1241
|
+
* Example:
|
|
1242
|
+
*
|
|
1243
|
+
* ```ts
|
|
1244
|
+
* const raw = dumpCesrNativeSad({
|
|
1245
|
+
* v: "KERICAACAACESRAAC8.",
|
|
1246
|
+
* t: "icp",
|
|
1247
|
+
* d: "EFaYE2LTv8dItUgQzIHKRA9FaHDrHtIHNs-m5DJKWXRN",
|
|
1248
|
+
* i: "DNG2arBDtHK_JyHRAq-emRdC6UM-yIpCAeJIWDiXp4Hx",
|
|
1249
|
+
* s: "0",
|
|
1250
|
+
* kt: "1",
|
|
1251
|
+
* k: ["DNG2arBDtHK_JyHRAq-emRdC6UM-yIpCAeJIWDiXp4Hx"],
|
|
1252
|
+
* nt: "1",
|
|
1253
|
+
* n: ["EFXIx7URwmw7AVQTBcMxPXfOOJ2YYA1SJAam69DXV8D2"],
|
|
1254
|
+
* bt: "0",
|
|
1255
|
+
* b: [],
|
|
1256
|
+
* c: [],
|
|
1257
|
+
* a: [],
|
|
1258
|
+
* });
|
|
1259
|
+
* ```
|
|
1260
|
+
*/
|
|
1261
|
+
export function dumpCesrNativeSad(sad) {
|
|
1262
|
+
if (typeof sad.v !== "string") {
|
|
1263
|
+
throw new SerializeError("Missing version string for CESR native dump");
|
|
1264
|
+
}
|
|
1265
|
+
const pvrsn = sad.v[4] === "1"
|
|
1266
|
+
? { major: 1, minor: Number.parseInt(sad.v[5], 16) }
|
|
1267
|
+
: { major: 2, minor: 0 };
|
|
1268
|
+
const gvrsn = sad.v.includes("CESR") && sad.v.length >= 19
|
|
1269
|
+
? { major: 2, minor: 0 }
|
|
1270
|
+
: null;
|
|
1271
|
+
const smellage = {
|
|
1272
|
+
proto: sad.v.slice(0, 4),
|
|
1273
|
+
pvrsn,
|
|
1274
|
+
gvrsn,
|
|
1275
|
+
};
|
|
1276
|
+
const ilk = typeof sad.t === "string" ? sad.t : null;
|
|
1277
|
+
const layout = getNativeLayout(smellage.proto, smellage.pvrsn, ilk);
|
|
1278
|
+
const version = smellage.gvrsn ?? Vrsn_2_0;
|
|
1279
|
+
const body = layout.shape === "map"
|
|
1280
|
+
? (() => {
|
|
1281
|
+
let frame = "";
|
|
1282
|
+
for (const label of layout.labels) {
|
|
1283
|
+
if (!(label in sad)) {
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
if (label === "v") {
|
|
1287
|
+
frame += encodeLabel("v");
|
|
1288
|
+
frame += encodeVerser(smellage.proto, smellage.pvrsn, smellage.gvrsn);
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
if (label === "t") {
|
|
1292
|
+
frame += encodeLabel("t");
|
|
1293
|
+
frame += encodeTag(String(sad.t));
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1296
|
+
const spec = layout.fields[label];
|
|
1297
|
+
if (!spec) {
|
|
1298
|
+
throw new SerializeError(`Unsupported native field ${label} for ilk=${String(ilk)}`);
|
|
1299
|
+
}
|
|
1300
|
+
frame += encodeLabel(label);
|
|
1301
|
+
frame += encodeNativeFieldValue(sad[label], label, spec, version);
|
|
1302
|
+
}
|
|
1303
|
+
const code = frame.length / 4 < 64 ** 2
|
|
1304
|
+
? CtrDexV2.MapBodyGroup
|
|
1305
|
+
: CtrDexV2.BigMapBodyGroup;
|
|
1306
|
+
return `${new Counter({ code, count: frame.length / 4, version }).qb64}${frame}`;
|
|
1307
|
+
})()
|
|
1308
|
+
: (() => {
|
|
1309
|
+
if (!ilk) {
|
|
1310
|
+
throw new SerializeError(`Missing ilk for fixed-body native ${smellage.proto} message`);
|
|
1311
|
+
}
|
|
1312
|
+
let frame = `${encodeVerser(smellage.proto, smellage.pvrsn, smellage.gvrsn)}${encodeTag(ilk)}`;
|
|
1313
|
+
for (const label of layout.labels) {
|
|
1314
|
+
const spec = layout.fields[label];
|
|
1315
|
+
if (!spec) {
|
|
1316
|
+
throw new SerializeError(`Unsupported native field ${label} for ilk=${String(ilk)}`);
|
|
1317
|
+
}
|
|
1318
|
+
frame += encodeNativeFieldValue(sad[label], label, spec, version);
|
|
1319
|
+
}
|
|
1320
|
+
const code = frame.length / 4 < 64 ** 2
|
|
1321
|
+
? CtrDexV2.FixBodyGroup
|
|
1322
|
+
: CtrDexV2.BigFixBodyGroup;
|
|
1323
|
+
return `${new Counter({ code, count: frame.length / 4, version }).qb64}${frame}`;
|
|
1324
|
+
})();
|
|
1325
|
+
return b(body);
|
|
1326
|
+
}
|