cesr-ts 0.2.2 → 0.3.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/README.md +31 -3
- package/esm/src/adapters/async-iterable.js +9 -9
- package/esm/src/annotate/annotator.js +18 -10
- package/esm/src/annotate/comments.js +3 -6
- package/esm/src/annotate/render.js +123 -24
- package/esm/src/bench/parser-benchmark.js +134 -0
- package/esm/src/core/bytes.js +6 -0
- package/esm/src/core/errors.js +24 -0
- package/esm/src/core/parser-attachment-collector.js +154 -0
- package/esm/src/core/parser-constants.js +74 -0
- package/esm/src/core/parser-deferred-frames.js +73 -0
- package/esm/src/core/parser-engine.js +128 -505
- package/esm/src/core/parser-frame-parser.js +643 -0
- package/esm/src/core/parser-policy.js +137 -0
- package/esm/src/core/parser-stream-state.js +62 -0
- package/esm/src/core/recovery-diagnostics.js +25 -0
- package/esm/src/index.js +4 -0
- package/esm/src/parser/attachment-fallback-policy.js +142 -0
- package/esm/src/parser/group-dispatch.js +547 -233
- package/esm/src/primitives/counter.js +4 -5
- package/esm/src/primitives/mapper.js +126 -45
- package/esm/src/primitives/matter.js +1 -1
- package/esm/src/router/router-stub.js +6 -6
- package/esm/src/serder/serder.js +44 -7
- package/esm/src/serder/smell.js +2 -1
- package/esm/src/tables/counter-version-registry.js +201 -0
- package/esm/src/version.js +2 -2
- package/package.json +3 -1
- package/types/_dnt.polyfills.d.ts +5 -0
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/src/adapters/async-iterable.d.ts +2 -2
- package/types/src/adapters/async-iterable.d.ts.map +1 -1
- package/types/src/adapters/effection.d.ts +2 -2
- package/types/src/adapters/effection.d.ts.map +1 -1
- package/types/src/annotate/annotator.d.ts.map +1 -1
- package/types/src/annotate/comments.d.ts.map +1 -1
- package/types/src/annotate/render.d.ts +8 -2
- package/types/src/annotate/render.d.ts.map +1 -1
- package/types/src/annotate/types.d.ts +2 -2
- package/types/src/annotate/types.d.ts.map +1 -1
- package/types/src/bench/parser-benchmark.d.ts +70 -0
- package/types/src/bench/parser-benchmark.d.ts.map +1 -0
- package/types/src/core/bytes.d.ts +6 -0
- package/types/src/core/bytes.d.ts.map +1 -1
- package/types/src/core/errors.d.ts +10 -0
- package/types/src/core/errors.d.ts.map +1 -1
- package/types/src/core/parser-attachment-collector.d.ts +51 -0
- package/types/src/core/parser-attachment-collector.d.ts.map +1 -0
- package/types/src/core/parser-constants.d.ts +30 -0
- package/types/src/core/parser-constants.d.ts.map +1 -0
- package/types/src/core/parser-deferred-frames.d.ts +38 -0
- package/types/src/core/parser-deferred-frames.d.ts.map +1 -0
- package/types/src/core/parser-engine.d.ts +53 -44
- package/types/src/core/parser-engine.d.ts.map +1 -1
- package/types/src/core/parser-frame-parser.d.ts +89 -0
- package/types/src/core/parser-frame-parser.d.ts.map +1 -0
- package/types/src/core/parser-policy.d.ts +27 -0
- package/types/src/core/parser-policy.d.ts.map +1 -0
- package/types/src/core/parser-stream-state.d.ts +30 -0
- package/types/src/core/parser-stream-state.d.ts.map +1 -0
- package/types/src/core/recovery-diagnostics.d.ts +59 -0
- package/types/src/core/recovery-diagnostics.d.ts.map +1 -0
- package/types/src/core/types.d.ts +61 -7
- package/types/src/core/types.d.ts.map +1 -1
- package/types/src/index.d.ts +4 -0
- package/types/src/index.d.ts.map +1 -1
- package/types/src/parser/attachment-fallback-policy.d.ts +78 -0
- package/types/src/parser/attachment-fallback-policy.d.ts.map +1 -0
- package/types/src/parser/group-dispatch.d.ts +85 -15
- package/types/src/parser/group-dispatch.d.ts.map +1 -1
- package/types/src/primitives/aggor.d.ts +2 -2
- package/types/src/primitives/aggor.d.ts.map +1 -1
- package/types/src/primitives/blinder.d.ts +2 -2
- package/types/src/primitives/blinder.d.ts.map +1 -1
- package/types/src/primitives/counter.d.ts.map +1 -1
- package/types/src/primitives/mapper.d.ts +44 -1
- package/types/src/primitives/mapper.d.ts.map +1 -1
- package/types/src/primitives/mediar.d.ts +2 -2
- package/types/src/primitives/mediar.d.ts.map +1 -1
- package/types/src/primitives/sealer.d.ts +2 -2
- package/types/src/primitives/sealer.d.ts.map +1 -1
- package/types/src/router/router-stub.d.ts +5 -5
- package/types/src/router/router-stub.d.ts.map +1 -1
- package/types/src/serder/serder.d.ts +2 -2
- package/types/src/serder/serder.d.ts.map +1 -1
- package/types/src/serder/serdery.d.ts +2 -2
- package/types/src/serder/serdery.d.ts.map +1 -1
- package/types/src/serder/smell.d.ts.map +1 -1
- package/types/src/tables/counter-version-registry.d.ts +90 -0
- package/types/src/tables/counter-version-registry.d.ts.map +1 -0
- package/types/src/version.d.ts +2 -2
- package/types/src/version.d.ts.map +1 -1
|
@@ -1,41 +1,350 @@
|
|
|
1
1
|
import { parseCounter } from "../primitives/counter.js";
|
|
2
2
|
import { parseMatter } from "../primitives/matter.js";
|
|
3
3
|
import { parseIndexer } from "../primitives/indexer.js";
|
|
4
|
-
import {
|
|
4
|
+
import { GroupSizeError, ShortageError, UnknownCodeError, } from "../core/errors.js";
|
|
5
5
|
import { CtrDexV1, CtrDexV2 } from "../tables/counter-codex.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
6
|
+
import { resolveVersionedRegistryValue, } from "../tables/counter-version-registry.js";
|
|
7
|
+
import { b64ToInt, intToB64 } from "../core/bytes.js";
|
|
8
|
+
import { composeRecoveryDiagnosticObserver, } from "../core/recovery-diagnostics.js";
|
|
9
|
+
import { createAttachmentVersionFallbackPolicy, } from "./attachment-fallback-policy.js";
|
|
10
|
+
export { createAttachmentVersionFallbackPolicy, } from "./attachment-fallback-policy.js";
|
|
11
|
+
function qb64Item(qb64, opaque = false) {
|
|
12
|
+
return { kind: "qb64", qb64, opaque };
|
|
13
|
+
}
|
|
14
|
+
function qb2Item(qb2, opaque = false) {
|
|
15
|
+
return { kind: "qb2", qb2, opaque };
|
|
16
|
+
}
|
|
17
|
+
function tupleItem(items) {
|
|
18
|
+
return { kind: "tuple", items };
|
|
19
|
+
}
|
|
20
|
+
function nestedGroupItem(code, name, count) {
|
|
21
|
+
return { kind: "group", code, name, count };
|
|
22
|
+
}
|
|
23
|
+
/** Normalize unknown throwables to an `Error` for diagnostic emission. */
|
|
24
|
+
function asError(error) {
|
|
25
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Canonical attachment dispatch specification (authoring form).
|
|
29
|
+
*
|
|
30
|
+
* This is the single source of truth for:
|
|
31
|
+
* - code -> parser routing
|
|
32
|
+
* - semantic shape classification
|
|
33
|
+
* - wrapper-group recursion eligibility
|
|
34
|
+
* - nested siger-list allowance
|
|
35
|
+
*
|
|
36
|
+
* Maintainer workflow:
|
|
37
|
+
* when adding/removing a counter route, edit this constant only, then rely on
|
|
38
|
+
* descriptor expansion + generated dispatch maps below.
|
|
39
|
+
*/
|
|
40
|
+
export const ATTACHMENT_DISPATCH_SPEC = [
|
|
41
|
+
{
|
|
42
|
+
parserKind: "genusVersion",
|
|
43
|
+
semanticShape: "genusVersionMarker",
|
|
44
|
+
codesByVersion: {
|
|
45
|
+
1: [CtrDexV1.KERIACDCGenusVersion],
|
|
46
|
+
2: [CtrDexV2.KERIACDCGenusVersion],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
parserKind: "quadlet",
|
|
51
|
+
semanticShape: "countedGroupPayload",
|
|
52
|
+
codesByVersion: {
|
|
53
|
+
1: [
|
|
54
|
+
CtrDexV1.GenericGroup,
|
|
55
|
+
CtrDexV1.BigGenericGroup,
|
|
56
|
+
CtrDexV1.NonNativeBodyGroup,
|
|
57
|
+
CtrDexV1.BigNonNativeBodyGroup,
|
|
58
|
+
CtrDexV1.ESSRPayloadGroup,
|
|
59
|
+
CtrDexV1.BigESSRPayloadGroup,
|
|
60
|
+
CtrDexV1.PathedMaterialCouples,
|
|
61
|
+
CtrDexV1.BigPathedMaterialCouples,
|
|
62
|
+
],
|
|
63
|
+
2: [
|
|
64
|
+
CtrDexV2.GenericGroup,
|
|
65
|
+
CtrDexV2.BigGenericGroup,
|
|
66
|
+
CtrDexV2.NonNativeBodyGroup,
|
|
67
|
+
CtrDexV2.BigNonNativeBodyGroup,
|
|
68
|
+
CtrDexV2.ESSRPayloadGroup,
|
|
69
|
+
CtrDexV2.BigESSRPayloadGroup,
|
|
70
|
+
CtrDexV2.PathedMaterialCouples,
|
|
71
|
+
CtrDexV2.BigPathedMaterialCouples,
|
|
72
|
+
CtrDexV2.DatagramSegmentGroup,
|
|
73
|
+
CtrDexV2.BigDatagramSegmentGroup,
|
|
74
|
+
CtrDexV2.ESSRWrapperGroup,
|
|
75
|
+
CtrDexV2.BigESSRWrapperGroup,
|
|
76
|
+
CtrDexV2.FixBodyGroup,
|
|
77
|
+
CtrDexV2.BigFixBodyGroup,
|
|
78
|
+
CtrDexV2.MapBodyGroup,
|
|
79
|
+
CtrDexV2.BigMapBodyGroup,
|
|
80
|
+
CtrDexV2.GenericMapGroup,
|
|
81
|
+
CtrDexV2.BigGenericMapGroup,
|
|
82
|
+
CtrDexV2.GenericListGroup,
|
|
83
|
+
CtrDexV2.BigGenericListGroup,
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
parserKind: "quadlet",
|
|
89
|
+
semanticShape: "wrapperGroupPayload",
|
|
90
|
+
wrapperGroup: true,
|
|
91
|
+
codesByVersion: {
|
|
92
|
+
1: [
|
|
93
|
+
CtrDexV1.AttachmentGroup,
|
|
94
|
+
CtrDexV1.BigAttachmentGroup,
|
|
95
|
+
CtrDexV1.BodyWithAttachmentGroup,
|
|
96
|
+
CtrDexV1.BigBodyWithAttachmentGroup,
|
|
97
|
+
],
|
|
98
|
+
2: [
|
|
99
|
+
CtrDexV2.AttachmentGroup,
|
|
100
|
+
CtrDexV2.BigAttachmentGroup,
|
|
101
|
+
CtrDexV2.BodyWithAttachmentGroup,
|
|
102
|
+
CtrDexV2.BigBodyWithAttachmentGroup,
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
parserKind: "repeatTuple",
|
|
108
|
+
semanticShape: "primitiveTuples",
|
|
109
|
+
tupleKinds: ["indexer"],
|
|
110
|
+
allowsSigerList: true,
|
|
111
|
+
codesByVersion: {
|
|
112
|
+
1: [
|
|
113
|
+
CtrDexV1.ControllerIdxSigs,
|
|
114
|
+
CtrDexV1.WitnessIdxSigs,
|
|
115
|
+
],
|
|
116
|
+
2: [
|
|
117
|
+
CtrDexV2.ControllerIdxSigs,
|
|
118
|
+
CtrDexV2.BigControllerIdxSigs,
|
|
119
|
+
CtrDexV2.WitnessIdxSigs,
|
|
120
|
+
CtrDexV2.BigWitnessIdxSigs,
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
parserKind: "repeatTuple",
|
|
126
|
+
semanticShape: "primitiveTuples",
|
|
127
|
+
tupleKinds: ["matter", "matter"],
|
|
128
|
+
codesByVersion: {
|
|
129
|
+
1: [
|
|
130
|
+
CtrDexV1.NonTransReceiptCouples,
|
|
131
|
+
CtrDexV1.FirstSeenReplayCouples,
|
|
132
|
+
CtrDexV1.SealSourceCouples,
|
|
133
|
+
],
|
|
134
|
+
2: [
|
|
135
|
+
CtrDexV2.NonTransReceiptCouples,
|
|
136
|
+
CtrDexV2.BigNonTransReceiptCouples,
|
|
137
|
+
CtrDexV2.FirstSeenReplayCouples,
|
|
138
|
+
CtrDexV2.BigFirstSeenReplayCouples,
|
|
139
|
+
CtrDexV2.SealSourceCouples,
|
|
140
|
+
CtrDexV2.BigSealSourceCouples,
|
|
141
|
+
CtrDexV2.BackerRegistrarSealCouples,
|
|
142
|
+
CtrDexV2.BigBackerRegistrarSealCouples,
|
|
143
|
+
CtrDexV2.TypedDigestSealCouples,
|
|
144
|
+
CtrDexV2.BigTypedDigestSealCouples,
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
parserKind: "repeatTuple",
|
|
150
|
+
semanticShape: "primitiveTuples",
|
|
151
|
+
tupleKinds: ["matter", "matter", "matter", "indexer"],
|
|
152
|
+
codesByVersion: {
|
|
153
|
+
1: [
|
|
154
|
+
CtrDexV1.TransReceiptQuadruples,
|
|
155
|
+
],
|
|
156
|
+
2: [
|
|
157
|
+
CtrDexV2.TransReceiptQuadruples,
|
|
158
|
+
CtrDexV2.BigTransReceiptQuadruples,
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
parserKind: "repeatTuple",
|
|
164
|
+
semanticShape: "primitiveTuples",
|
|
165
|
+
tupleKinds: ["matter", "matter", "matter"],
|
|
166
|
+
codesByVersion: {
|
|
167
|
+
1: [
|
|
168
|
+
CtrDexV1.SealSourceTriples,
|
|
169
|
+
],
|
|
170
|
+
2: [
|
|
171
|
+
CtrDexV2.SealSourceTriples,
|
|
172
|
+
CtrDexV2.BigSealSourceTriples,
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
parserKind: "repeatTuple",
|
|
178
|
+
semanticShape: "primitiveTuples",
|
|
179
|
+
tupleKinds: ["matter"],
|
|
180
|
+
codesByVersion: {
|
|
181
|
+
2: [
|
|
182
|
+
CtrDexV2.SealSourceLastSingles,
|
|
183
|
+
CtrDexV2.BigSealSourceLastSingles,
|
|
184
|
+
CtrDexV2.DigestSealSingles,
|
|
185
|
+
CtrDexV2.BigDigestSealSingles,
|
|
186
|
+
CtrDexV2.MerkleRootSealSingles,
|
|
187
|
+
CtrDexV2.BigMerkleRootSealSingles,
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
parserKind: "transIdxSigGroups",
|
|
193
|
+
semanticShape: "signatureGroupTuples",
|
|
194
|
+
codesByVersion: {
|
|
195
|
+
1: [
|
|
196
|
+
CtrDexV1.TransIdxSigGroups,
|
|
197
|
+
],
|
|
198
|
+
2: [
|
|
199
|
+
CtrDexV2.TransIdxSigGroups,
|
|
200
|
+
CtrDexV2.BigTransIdxSigGroups,
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
parserKind: "transLastIdxSigGroups",
|
|
206
|
+
semanticShape: "lastSignatureGroupTuples",
|
|
207
|
+
codesByVersion: {
|
|
208
|
+
1: [
|
|
209
|
+
CtrDexV1.TransLastIdxSigGroups,
|
|
210
|
+
],
|
|
211
|
+
2: [
|
|
212
|
+
CtrDexV2.TransLastIdxSigGroups,
|
|
213
|
+
CtrDexV2.BigTransLastIdxSigGroups,
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
parserKind: "sadPathSig",
|
|
219
|
+
semanticShape: "sadPathSignatures",
|
|
220
|
+
codesByVersion: {
|
|
221
|
+
1: [CtrDexV1.SadPathSig],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
parserKind: "sadPathSigGroup",
|
|
226
|
+
semanticShape: "sadPathSignatureGroup",
|
|
227
|
+
codesByVersion: {
|
|
228
|
+
1: [CtrDexV1.SadPathSigGroup],
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
parserKind: "repeatTuple",
|
|
233
|
+
semanticShape: "primitiveTuples",
|
|
234
|
+
tupleKinds: ["matter", "matter", "matter", "matter"],
|
|
235
|
+
codesByVersion: {
|
|
236
|
+
2: [
|
|
237
|
+
CtrDexV2.BlindedStateQuadruples,
|
|
238
|
+
CtrDexV2.BigBlindedStateQuadruples,
|
|
239
|
+
CtrDexV2.TypedMediaQuadruples,
|
|
240
|
+
CtrDexV2.BigTypedMediaQuadruples,
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
parserKind: "repeatTuple",
|
|
246
|
+
semanticShape: "primitiveTuples",
|
|
247
|
+
tupleKinds: [
|
|
248
|
+
"matter",
|
|
249
|
+
"matter",
|
|
250
|
+
"matter",
|
|
251
|
+
"matter",
|
|
252
|
+
"matter",
|
|
253
|
+
"matter",
|
|
254
|
+
],
|
|
255
|
+
codesByVersion: {
|
|
256
|
+
2: [
|
|
257
|
+
CtrDexV2.BoundStateSextuples,
|
|
258
|
+
CtrDexV2.BigBoundStateSextuples,
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
/**
|
|
264
|
+
* Expand grouped spec rows into per-version descriptors.
|
|
265
|
+
*
|
|
266
|
+
* Why:
|
|
267
|
+
* downstream builders operate on `(version, code)` units, while spec authoring
|
|
268
|
+
* stays concise by grouping shared semantics across major versions.
|
|
269
|
+
*/
|
|
270
|
+
function expandDispatchSpec(spec) {
|
|
271
|
+
const descriptors = [];
|
|
272
|
+
for (const family of spec) {
|
|
273
|
+
for (const version of [1, 2]) {
|
|
274
|
+
for (const code of family.codesByVersion[version] ?? []) {
|
|
275
|
+
descriptors.push({
|
|
276
|
+
version,
|
|
277
|
+
code,
|
|
278
|
+
parserKind: family.parserKind,
|
|
279
|
+
semanticShape: family.semanticShape,
|
|
280
|
+
tupleKinds: family.tupleKinds,
|
|
281
|
+
wrapperGroup: family.wrapperGroup === true,
|
|
282
|
+
allowsSigerList: family.allowsSigerList === true,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return descriptors;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Build a version-indexed code-set projection from descriptors.
|
|
291
|
+
*
|
|
292
|
+
* Used for secondary routing metadata (wrapper recursion/siger-list allowance)
|
|
293
|
+
* so those sets cannot drift from the canonical dispatch spec.
|
|
294
|
+
*/
|
|
295
|
+
function buildCodeSetByVersion(descriptors, predicate) {
|
|
296
|
+
const byVersion = {
|
|
297
|
+
1: new Set(),
|
|
298
|
+
2: new Set(),
|
|
299
|
+
};
|
|
300
|
+
for (const descriptor of descriptors) {
|
|
301
|
+
if (predicate(descriptor)) {
|
|
302
|
+
byVersion[descriptor.version].add(descriptor.code);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return byVersion;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Normalized per-code dispatch descriptors used by all derived routing tables.
|
|
309
|
+
*
|
|
310
|
+
* Invariant:
|
|
311
|
+
* this array is generated once at module load from `ATTACHMENT_DISPATCH_SPEC`
|
|
312
|
+
* and should not be hand-edited.
|
|
313
|
+
*/
|
|
314
|
+
const ATTACHMENT_DISPATCH_DESCRIPTORS = expandDispatchSpec(ATTACHMENT_DISPATCH_SPEC);
|
|
315
|
+
/** Siger-list counter codes by major version (for nested trans sig group parsing). */
|
|
316
|
+
const SIGER_LIST_CODES_BY_MAJOR = buildCodeSetByVersion(ATTACHMENT_DISPATCH_DESCRIPTORS, (descriptor) => descriptor.allowsSigerList);
|
|
317
|
+
/** Wrapper counters by major version whose payloads recurse as nested groups. */
|
|
318
|
+
const WRAPPER_GROUP_CODES_BY_MAJOR = buildCodeSetByVersion(ATTACHMENT_DISPATCH_DESCRIPTORS, (descriptor) => descriptor.wrapperGroup);
|
|
319
|
+
const WRAPPER_GROUP_CODES_V1 = WRAPPER_GROUP_CODES_BY_MAJOR[1];
|
|
320
|
+
const WRAPPER_GROUP_CODES_V2 = WRAPPER_GROUP_CODES_BY_MAJOR[2];
|
|
321
|
+
/** Universal genus-version counter code (v1/v2 compatible encoding semantics). */
|
|
322
|
+
const GENUS_VERSION_CODE = CtrDexV2.KERIACDCGenusVersion;
|
|
323
|
+
/** Domain-sensitive primitive size (qb64 vs qb2). */
|
|
30
324
|
function primitiveSize(primitive, domain) {
|
|
31
325
|
return domain === "bny" ? primitive.fullSizeB2 : primitive.fullSize;
|
|
32
326
|
}
|
|
327
|
+
/** Alias for readability at counter header consumption callsites. */
|
|
33
328
|
function counterHeaderSize(counter, domain) {
|
|
34
329
|
return primitiveSize(counter, domain);
|
|
35
330
|
}
|
|
331
|
+
/** Counter count unit size by domain: quadlets in text, triplets in binary. */
|
|
36
332
|
function quadletUnitSize(domain) {
|
|
37
333
|
return domain === "bny" ? 3 : 4;
|
|
38
334
|
}
|
|
335
|
+
/** Decode a genus-version counter payload into major/minor `Versionage`. */
|
|
336
|
+
function decodeVersionCounter(counter) {
|
|
337
|
+
const triplet = counter.qb64.length >= 3
|
|
338
|
+
? counter.qb64.slice(-3)
|
|
339
|
+
: intToB64(counter.count, 3);
|
|
340
|
+
const majorRaw = b64ToInt(triplet[0] ?? "A");
|
|
341
|
+
const minorRaw = b64ToInt(triplet[1] ?? "A");
|
|
342
|
+
return {
|
|
343
|
+
major: majorRaw === 1 ? 1 : 2,
|
|
344
|
+
minor: minorRaw,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/** Parse a fixed primitive tuple shape and return qb64 strings in source order. */
|
|
39
348
|
function parseTuple(input, kinds, domain) {
|
|
40
349
|
const items = [];
|
|
41
350
|
let offset = 0;
|
|
@@ -43,24 +352,38 @@ function parseTuple(input, kinds, domain) {
|
|
|
43
352
|
const part = kind === "indexer"
|
|
44
353
|
? parseIndexer(input.slice(offset), domain)
|
|
45
354
|
: parseMatter(input.slice(offset), domain);
|
|
46
|
-
items.push(part.qb64);
|
|
355
|
+
items.push(qb64Item(part.qb64));
|
|
47
356
|
offset += primitiveSize(part, domain);
|
|
48
357
|
}
|
|
49
358
|
return { items, consumed: offset };
|
|
50
359
|
}
|
|
360
|
+
/** Parse `count` tuple repetitions with stable ordering and exact byte accounting. */
|
|
51
361
|
function parseRepeated(input, count, kinds, domain) {
|
|
52
362
|
const items = [];
|
|
53
363
|
let offset = 0;
|
|
54
364
|
for (let i = 0; i < count; i++) {
|
|
55
365
|
const tuple = parseTuple(input.slice(offset), kinds, domain);
|
|
56
|
-
items.push(tuple.items);
|
|
366
|
+
items.push(tupleItem(tuple.items));
|
|
57
367
|
offset += tuple.consumed;
|
|
58
368
|
}
|
|
59
369
|
return { items, consumed: offset };
|
|
60
370
|
}
|
|
371
|
+
/** Split opaque counted payload into domain units (quadlets for text, triplets for binary). */
|
|
372
|
+
function splitOpaqueUnits(payload, domain, expectedCount, opaque = false) {
|
|
373
|
+
if (domain === "bny") {
|
|
374
|
+
const count = expectedCount ?? Math.floor(payload.length / 3);
|
|
375
|
+
return Array.from({ length: count }, (_v, i) => qb2Item(payload.slice(i * 3, i * 3 + 3), opaque));
|
|
376
|
+
}
|
|
377
|
+
const text = String.fromCharCode(...payload);
|
|
378
|
+
if (expectedCount !== undefined) {
|
|
379
|
+
return Array.from({ length: expectedCount }, (_v, i) => qb64Item(text.slice(i * 4, i * 4 + 4), opaque));
|
|
380
|
+
}
|
|
381
|
+
return (text.match(/.{1,4}/g) ?? []).map((token) => qb64Item(token, opaque));
|
|
382
|
+
}
|
|
383
|
+
/** Parse nested siger-list group headed by a version-appropriate siger counter. */
|
|
61
384
|
function parseSigerList(input, version, domain) {
|
|
62
385
|
const counter = parseCounter(input, version, domain);
|
|
63
|
-
const allowed = SIGER_LIST_CODES_BY_VERSION
|
|
386
|
+
const allowed = resolveVersionedRegistryValue(SIGER_LIST_CODES_BY_VERSION, version, "siger-list code set").value;
|
|
64
387
|
if (!allowed.has(counter.code)) {
|
|
65
388
|
throw new UnknownCodeError(`Expected siger-list counter but got ${counter.code}`);
|
|
66
389
|
}
|
|
@@ -68,11 +391,20 @@ function parseSigerList(input, version, domain) {
|
|
|
68
391
|
let offset = counterHeaderSize(counter, domain);
|
|
69
392
|
for (let i = 0; i < counter.count; i++) {
|
|
70
393
|
const part = parseIndexer(input.slice(offset), domain);
|
|
71
|
-
items.push(part.qb64);
|
|
394
|
+
items.push(qb64Item(part.qb64));
|
|
72
395
|
offset += primitiveSize(part, domain);
|
|
73
396
|
}
|
|
74
397
|
return { items, consumed: offset };
|
|
75
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Parse quadlet/triplet-counted groups.
|
|
401
|
+
*
|
|
402
|
+
* Behavior:
|
|
403
|
+
* - non-wrapper groups return raw unit chunks (text quadlets or binary triplets).
|
|
404
|
+
* - wrapper groups recurse into nested attachment-group dispatch.
|
|
405
|
+
* - compat mode preserves unread wrapper remainder as opaque units on non-boundary
|
|
406
|
+
* nested parse errors.
|
|
407
|
+
*/
|
|
76
408
|
function parseQuadletGroup(input, counter, version, wrapperCodes, domain, context) {
|
|
77
409
|
const unitSize = quadletUnitSize(domain);
|
|
78
410
|
const payloadSize = counter.count * unitSize;
|
|
@@ -86,32 +418,47 @@ function parseQuadletGroup(input, counter, version, wrapperCodes, domain, contex
|
|
|
86
418
|
// Wrapper payload is a packed stream of nested groups (best effort).
|
|
87
419
|
const items = [];
|
|
88
420
|
let offset = 0;
|
|
421
|
+
let nestedVersion = version;
|
|
89
422
|
while (offset < payload.length) {
|
|
90
423
|
try {
|
|
424
|
+
const nestedCounter = parseCounter(payload.slice(offset), nestedVersion, domain);
|
|
425
|
+
if (nestedCounter.code === GENUS_VERSION_CODE) {
|
|
426
|
+
// Wrapper-scoped version override for subsequent nested groups.
|
|
427
|
+
offset += counterHeaderSize(nestedCounter, domain);
|
|
428
|
+
nestedVersion = decodeVersionCounter(nestedCounter);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
91
431
|
const nested = parseAttachmentDispatchCompat(
|
|
92
432
|
// Allow mixed-version nested groups inside wrapper payloads.
|
|
93
433
|
// Some real-world streams wrap v2 groups inside v1 wrappers.
|
|
94
|
-
payload.slice(offset),
|
|
95
|
-
items.push(
|
|
96
|
-
code: nested.group.code,
|
|
97
|
-
name: nested.group.name,
|
|
98
|
-
count: nested.group.count,
|
|
99
|
-
});
|
|
434
|
+
payload.slice(offset), nestedVersion, domain, context);
|
|
435
|
+
items.push(nestedGroupItem(nested.group.code, nested.group.name, nested.group.count));
|
|
100
436
|
if (nested.consumed === 0) {
|
|
101
437
|
throw new GroupSizeError("Nested attachment parser consumed zero bytes");
|
|
102
438
|
}
|
|
103
439
|
offset += nested.consumed;
|
|
104
440
|
}
|
|
105
441
|
catch (error) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
442
|
+
const normalized = asError(error);
|
|
443
|
+
if (normalized instanceof ShortageError ||
|
|
444
|
+
normalized instanceof GroupSizeError) {
|
|
445
|
+
throw normalized;
|
|
446
|
+
}
|
|
447
|
+
if (!context.versionFallbackPolicy.shouldPreserveWrapperRemainder(normalized)) {
|
|
448
|
+
throw normalized;
|
|
109
449
|
}
|
|
110
450
|
// Intentional recovery point: keep unread wrapper tail as opaque units.
|
|
111
451
|
const remainder = payload.slice(offset);
|
|
112
|
-
const opaque = domain
|
|
113
|
-
|
|
114
|
-
:
|
|
452
|
+
const opaque = splitOpaqueUnits(remainder, domain, undefined, true);
|
|
453
|
+
context.recoveryDiagnosticObserver?.({
|
|
454
|
+
type: "wrapper-opaque-tail-preserved",
|
|
455
|
+
version: nestedVersion,
|
|
456
|
+
domain,
|
|
457
|
+
wrapperCode: counter.code,
|
|
458
|
+
opaqueItemCount: opaque.length,
|
|
459
|
+
errorName: normalized.name,
|
|
460
|
+
reason: normalized.message,
|
|
461
|
+
});
|
|
115
462
|
items.push(...opaque);
|
|
116
463
|
offset = payload.length;
|
|
117
464
|
}
|
|
@@ -121,11 +468,10 @@ function parseQuadletGroup(input, counter, version, wrapperCodes, domain, contex
|
|
|
121
468
|
}
|
|
122
469
|
return { items, consumed: total };
|
|
123
470
|
}
|
|
124
|
-
const items = domain
|
|
125
|
-
? Array.from({ length: counter.count }, (_v, i) => payload.slice(i * 3, i * 3 + 3))
|
|
126
|
-
: (String.fromCharCode(...payload).match(/.{1,4}/g) ?? []);
|
|
471
|
+
const items = splitOpaqueUnits(payload, domain, counter.count);
|
|
127
472
|
return { items, consumed: total };
|
|
128
473
|
}
|
|
474
|
+
/** Build parser for repeated tuple-based group families. */
|
|
129
475
|
function repeatTupleParser(kinds) {
|
|
130
476
|
return (input, counter, _version, domain, _context) => {
|
|
131
477
|
const headerSize = counterHeaderSize(counter, domain);
|
|
@@ -133,6 +479,7 @@ function repeatTupleParser(kinds) {
|
|
|
133
479
|
return { items: parsed.items, consumed: parsed.consumed + headerSize };
|
|
134
480
|
};
|
|
135
481
|
}
|
|
482
|
+
/** Parse transferable indexed-signature groups with a 3-field header + siger list. */
|
|
136
483
|
function transIdxSigGroupsParser(input, counter, version, domain, _context) {
|
|
137
484
|
const items = [];
|
|
138
485
|
let offset = counterHeaderSize(counter, domain);
|
|
@@ -145,10 +492,11 @@ function transIdxSigGroupsParser(input, counter, version, domain, _context) {
|
|
|
145
492
|
offset += header.consumed;
|
|
146
493
|
const sigers = parseSigerList(input.slice(offset), version, domain);
|
|
147
494
|
offset += sigers.consumed;
|
|
148
|
-
items.push([...header.items, sigers.items]);
|
|
495
|
+
items.push(tupleItem([...header.items, tupleItem(sigers.items)]));
|
|
149
496
|
}
|
|
150
497
|
return { items, consumed: offset };
|
|
151
498
|
}
|
|
499
|
+
/** Parse transferable last-establishment sig groups with header + siger list. */
|
|
152
500
|
function transLastIdxSigGroupsParser(input, counter, version, domain, _context) {
|
|
153
501
|
const items = [];
|
|
154
502
|
let offset = counterHeaderSize(counter, domain);
|
|
@@ -157,10 +505,11 @@ function transLastIdxSigGroupsParser(input, counter, version, domain, _context)
|
|
|
157
505
|
offset += header.consumed;
|
|
158
506
|
const sigers = parseSigerList(input.slice(offset), version, domain);
|
|
159
507
|
offset += sigers.consumed;
|
|
160
|
-
items.push([...header.items, sigers.items]);
|
|
508
|
+
items.push(tupleItem([...header.items, tupleItem(sigers.items)]));
|
|
161
509
|
}
|
|
162
510
|
return { items, consumed: offset };
|
|
163
511
|
}
|
|
512
|
+
/** Parse SadPathSig groups: each path is followed by one nested signature group. */
|
|
164
513
|
function sadPathSigParser(input, counter, version, domain, context) {
|
|
165
514
|
const items = [];
|
|
166
515
|
let offset = counterHeaderSize(counter, domain);
|
|
@@ -169,187 +518,125 @@ function sadPathSigParser(input, counter, version, domain, context) {
|
|
|
169
518
|
offset += primitiveSize(path, domain);
|
|
170
519
|
const sigGroup = parseAttachmentDispatchCompat(input.slice(offset), version, domain, context);
|
|
171
520
|
offset += sigGroup.consumed;
|
|
172
|
-
items.push([
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}]);
|
|
521
|
+
items.push(tupleItem([
|
|
522
|
+
qb64Item(path.qb64),
|
|
523
|
+
nestedGroupItem(sigGroup.group.code, sigGroup.group.name, sigGroup.group.count),
|
|
524
|
+
]));
|
|
177
525
|
}
|
|
178
526
|
return { items, consumed: offset };
|
|
179
527
|
}
|
|
528
|
+
/** Parse SadPathSigGroup: root path + repeated (path, nested signature group). */
|
|
180
529
|
function sadPathSigGroupParser(input, counter, version, domain, context) {
|
|
181
530
|
const items = [];
|
|
182
531
|
let offset = counterHeaderSize(counter, domain);
|
|
183
532
|
const root = parseMatter(input.slice(offset), domain);
|
|
184
533
|
offset += primitiveSize(root, domain);
|
|
185
|
-
items.push(root.qb64);
|
|
534
|
+
items.push(qb64Item(root.qb64));
|
|
186
535
|
for (let i = 0; i < counter.count; i++) {
|
|
187
536
|
const path = parseMatter(input.slice(offset), domain);
|
|
188
537
|
offset += primitiveSize(path, domain);
|
|
189
538
|
const sigGroup = parseAttachmentDispatchCompat(input.slice(offset), version, domain, context);
|
|
190
539
|
offset += sigGroup.consumed;
|
|
191
|
-
items.push([
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}]);
|
|
540
|
+
items.push(tupleItem([
|
|
541
|
+
qb64Item(path.qb64),
|
|
542
|
+
nestedGroupItem(sigGroup.group.code, sigGroup.group.name, sigGroup.group.count),
|
|
543
|
+
]));
|
|
196
544
|
}
|
|
197
545
|
return { items, consumed: offset };
|
|
198
546
|
}
|
|
547
|
+
/** Genus-version groups carry only the counter token itself as semantic payload. */
|
|
199
548
|
function genusVersionParser(_input, counter, _version, domain, _context) {
|
|
200
549
|
return {
|
|
201
|
-
items: [counter.qb64],
|
|
550
|
+
items: [qb64Item(counter.qb64)],
|
|
202
551
|
consumed: counterHeaderSize(counter, domain),
|
|
203
552
|
};
|
|
204
553
|
}
|
|
554
|
+
/** v1 quadlet/triplet parser variant with v1 wrapper-code semantics. */
|
|
205
555
|
const V1_QUADLET_PARSER = (input, counter, version, domain, context) => parseQuadletGroup(input, counter, version, WRAPPER_GROUP_CODES_V1, domain, context);
|
|
556
|
+
/** v2 quadlet/triplet parser variant with v2 wrapper-code semantics. */
|
|
206
557
|
const V2_QUADLET_PARSER = (input, counter, version, domain, context) => parseQuadletGroup(input, counter, version, WRAPPER_GROUP_CODES_V2, domain, context);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
],
|
|
286
|
-
[
|
|
287
|
-
CtrDexV2.BigSealSourceTriples,
|
|
288
|
-
repeatTupleParser(["matter", "matter", "matter"]),
|
|
289
|
-
],
|
|
290
|
-
[CtrDexV2.SealSourceLastSingles, repeatTupleParser(["matter"])],
|
|
291
|
-
[CtrDexV2.BigSealSourceLastSingles, repeatTupleParser(["matter"])],
|
|
292
|
-
[CtrDexV2.DigestSealSingles, repeatTupleParser(["matter"])],
|
|
293
|
-
[CtrDexV2.BigDigestSealSingles, repeatTupleParser(["matter"])],
|
|
294
|
-
[CtrDexV2.MerkleRootSealSingles, repeatTupleParser(["matter"])],
|
|
295
|
-
[CtrDexV2.BigMerkleRootSealSingles, repeatTupleParser(["matter"])],
|
|
296
|
-
[
|
|
297
|
-
CtrDexV2.BackerRegistrarSealCouples,
|
|
298
|
-
repeatTupleParser(["matter", "matter"]),
|
|
299
|
-
],
|
|
300
|
-
[
|
|
301
|
-
CtrDexV2.BigBackerRegistrarSealCouples,
|
|
302
|
-
repeatTupleParser(["matter", "matter"]),
|
|
303
|
-
],
|
|
304
|
-
[CtrDexV2.TypedDigestSealCouples, repeatTupleParser(["matter", "matter"])],
|
|
305
|
-
[CtrDexV2.BigTypedDigestSealCouples, repeatTupleParser(["matter", "matter"])],
|
|
306
|
-
[CtrDexV2.TransIdxSigGroups, transIdxSigGroupsParser],
|
|
307
|
-
[CtrDexV2.BigTransIdxSigGroups, transIdxSigGroupsParser],
|
|
308
|
-
[CtrDexV2.TransLastIdxSigGroups, transLastIdxSigGroupsParser],
|
|
309
|
-
[CtrDexV2.BigTransLastIdxSigGroups, transLastIdxSigGroupsParser],
|
|
310
|
-
[
|
|
311
|
-
CtrDexV2.BlindedStateQuadruples,
|
|
312
|
-
repeatTupleParser(["matter", "matter", "matter", "matter"]),
|
|
313
|
-
],
|
|
314
|
-
[
|
|
315
|
-
CtrDexV2.BigBlindedStateQuadruples,
|
|
316
|
-
repeatTupleParser(["matter", "matter", "matter", "matter"]),
|
|
317
|
-
],
|
|
318
|
-
[
|
|
319
|
-
CtrDexV2.BoundStateSextuples,
|
|
320
|
-
repeatTupleParser([
|
|
321
|
-
"matter",
|
|
322
|
-
"matter",
|
|
323
|
-
"matter",
|
|
324
|
-
"matter",
|
|
325
|
-
"matter",
|
|
326
|
-
"matter",
|
|
327
|
-
]),
|
|
328
|
-
],
|
|
329
|
-
[
|
|
330
|
-
CtrDexV2.BigBoundStateSextuples,
|
|
331
|
-
repeatTupleParser([
|
|
332
|
-
"matter",
|
|
333
|
-
"matter",
|
|
334
|
-
"matter",
|
|
335
|
-
"matter",
|
|
336
|
-
"matter",
|
|
337
|
-
"matter",
|
|
338
|
-
]),
|
|
339
|
-
],
|
|
340
|
-
[
|
|
341
|
-
CtrDexV2.TypedMediaQuadruples,
|
|
342
|
-
repeatTupleParser(["matter", "matter", "matter", "matter"]),
|
|
343
|
-
],
|
|
344
|
-
[
|
|
345
|
-
CtrDexV2.BigTypedMediaQuadruples,
|
|
346
|
-
repeatTupleParser(["matter", "matter", "matter", "matter"]),
|
|
347
|
-
],
|
|
348
|
-
[CtrDexV2.KERIACDCGenusVersion, genusVersionParser],
|
|
349
|
-
]);
|
|
558
|
+
/**
|
|
559
|
+
* Parser instance cache keyed by tuple shape (`matter|indexer|...`).
|
|
560
|
+
*
|
|
561
|
+
* Why:
|
|
562
|
+
* many codes share identical repeated-tuple semantics; caching preserves one
|
|
563
|
+
* parser closure per tuple shape and keeps dispatch-map construction stable.
|
|
564
|
+
*/
|
|
565
|
+
const REPEAT_TUPLE_PARSER_CACHE = new Map();
|
|
566
|
+
/**
|
|
567
|
+
* Resolve one expanded descriptor into its executable parser function.
|
|
568
|
+
*
|
|
569
|
+
* Invariant:
|
|
570
|
+
* this switch must remain exhaustive over `DispatchParserKind`.
|
|
571
|
+
*/
|
|
572
|
+
function parserForDescriptor(descriptor) {
|
|
573
|
+
switch (descriptor.parserKind) {
|
|
574
|
+
case "quadlet":
|
|
575
|
+
return descriptor.version === 2 ? V2_QUADLET_PARSER : V1_QUADLET_PARSER;
|
|
576
|
+
case "repeatTuple": {
|
|
577
|
+
if (!descriptor.tupleKinds || descriptor.tupleKinds.length === 0) {
|
|
578
|
+
throw new Error(`Dispatch descriptor ${descriptor.code} is missing tupleKinds`);
|
|
579
|
+
}
|
|
580
|
+
const key = descriptor.tupleKinds.join("|");
|
|
581
|
+
let parser = REPEAT_TUPLE_PARSER_CACHE.get(key);
|
|
582
|
+
if (!parser) {
|
|
583
|
+
parser = repeatTupleParser(descriptor.tupleKinds);
|
|
584
|
+
REPEAT_TUPLE_PARSER_CACHE.set(key, parser);
|
|
585
|
+
}
|
|
586
|
+
return parser;
|
|
587
|
+
}
|
|
588
|
+
case "transIdxSigGroups":
|
|
589
|
+
return transIdxSigGroupsParser;
|
|
590
|
+
case "transLastIdxSigGroups":
|
|
591
|
+
return transLastIdxSigGroupsParser;
|
|
592
|
+
case "sadPathSig":
|
|
593
|
+
return sadPathSigParser;
|
|
594
|
+
case "sadPathSigGroup":
|
|
595
|
+
return sadPathSigGroupParser;
|
|
596
|
+
case "genusVersion":
|
|
597
|
+
return genusVersionParser;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Build major-version dispatch maps from normalized descriptors.
|
|
602
|
+
*
|
|
603
|
+
* Invariant:
|
|
604
|
+
* each code appears at most once per major version in the final maps; if a code
|
|
605
|
+
* were duplicated in descriptors, later entries would overwrite earlier ones.
|
|
606
|
+
*/
|
|
607
|
+
function buildDispatchByVersion(descriptors) {
|
|
608
|
+
const dispatch = {
|
|
609
|
+
1: new Map(),
|
|
610
|
+
2: new Map(),
|
|
611
|
+
};
|
|
612
|
+
for (const descriptor of descriptors) {
|
|
613
|
+
dispatch[descriptor.version].set(descriptor.code, parserForDescriptor(descriptor));
|
|
614
|
+
}
|
|
615
|
+
return dispatch;
|
|
616
|
+
}
|
|
617
|
+
/** Generated dispatch maps keyed by major version. */
|
|
618
|
+
const DISPATCH_BY_MAJOR = buildDispatchByVersion(ATTACHMENT_DISPATCH_DESCRIPTORS);
|
|
619
|
+
/**
|
|
620
|
+
* Lift major-indexed artifacts into a major+minor registry.
|
|
621
|
+
*
|
|
622
|
+
* Current parser supports one minor (`0`) per major, but this keeps lookup
|
|
623
|
+
* contracts explicit and ready for minor-version progression.
|
|
624
|
+
*/
|
|
625
|
+
function asMinorZeroRegistry(byMajor) {
|
|
626
|
+
return Object.freeze({
|
|
627
|
+
1: Object.freeze({ 0: byMajor[1] }),
|
|
628
|
+
2: Object.freeze({ 0: byMajor[2] }),
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/** Siger-list counter code sets resolved by major+minor stream version. */
|
|
632
|
+
const SIGER_LIST_CODES_BY_VERSION = asMinorZeroRegistry(SIGER_LIST_CODES_BY_MAJOR);
|
|
633
|
+
/** Attachment-group dispatch registry resolved by major+minor stream version. */
|
|
634
|
+
const DISPATCH_BY_VERSION = asMinorZeroRegistry(DISPATCH_BY_MAJOR);
|
|
635
|
+
/** Resolve attachment dispatch table for the requested stream version. */
|
|
350
636
|
function getDispatch(version) {
|
|
351
|
-
return version
|
|
637
|
+
return resolveVersionedRegistryValue(DISPATCH_BY_VERSION, version, "attachment dispatch registry").value;
|
|
352
638
|
}
|
|
639
|
+
/** Parse one attachment group with an explicit version (no version fallback). */
|
|
353
640
|
function parseAttachmentDispatchWithVersion(input, version, domain, context) {
|
|
354
641
|
const counter = parseCounter(input, version, domain);
|
|
355
642
|
const parser = getDispatch(version).get(counter.code);
|
|
@@ -371,50 +658,77 @@ function parseAttachmentDispatchWithVersion(input, version, domain, context) {
|
|
|
371
658
|
consumed: parsed.consumed,
|
|
372
659
|
};
|
|
373
660
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
661
|
+
/**
|
|
662
|
+
* Parse one attachment group using an injected fallback strategy.
|
|
663
|
+
*
|
|
664
|
+
* Flow:
|
|
665
|
+
* 1) attempt parse with provided version
|
|
666
|
+
* 2) ask policy whether failure is terminal or retryable
|
|
667
|
+
* 3) on retry, emit fallback observation and parse with policy-selected version
|
|
668
|
+
*/
|
|
669
|
+
function parseAttachmentDispatchWithPolicy(input, version, domain, context) {
|
|
378
670
|
try {
|
|
379
671
|
return parseAttachmentDispatchWithVersion(input, version, domain, context);
|
|
380
672
|
}
|
|
381
673
|
catch (error) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
674
|
+
const normalized = asError(error);
|
|
675
|
+
const decision = context.versionFallbackPolicy.onVersionDispatchFailure(normalized, version, domain);
|
|
676
|
+
if (decision.action === "throw") {
|
|
677
|
+
context.recoveryDiagnosticObserver?.({
|
|
678
|
+
type: "version-fallback-rejected",
|
|
679
|
+
version,
|
|
680
|
+
domain,
|
|
681
|
+
errorName: normalized.name,
|
|
682
|
+
reason: normalized.message,
|
|
683
|
+
});
|
|
684
|
+
throw normalized;
|
|
385
685
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
:
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (context.onVersionFallback) {
|
|
396
|
-
context.onVersionFallback(info);
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
console.warn(`CESR attachment dispatch fallback ${version.major}.${version.minor} -> ${alternate.major}.${alternate.minor} (${domain}): ${error.message}`);
|
|
400
|
-
}
|
|
401
|
-
return parseAttachmentDispatchWithVersion(input, alternate, domain, context);
|
|
686
|
+
context.recoveryDiagnosticObserver?.({
|
|
687
|
+
type: "version-fallback-accepted",
|
|
688
|
+
from: decision.info.from,
|
|
689
|
+
to: decision.info.to,
|
|
690
|
+
domain: decision.info.domain,
|
|
691
|
+
reason: decision.info.reason,
|
|
692
|
+
});
|
|
693
|
+
context.versionFallbackPolicy.onVersionFallback(decision.info);
|
|
694
|
+
return parseAttachmentDispatchWithVersion(input, decision.retryVersion, domain, context);
|
|
402
695
|
}
|
|
403
696
|
}
|
|
404
697
|
/**
|
|
405
|
-
* Parse
|
|
406
|
-
*
|
|
698
|
+
* Parse one attachment group with policy-aware fallback behavior.
|
|
699
|
+
*
|
|
700
|
+
* By default this uses compat policy semantics to mirror historical
|
|
701
|
+
* `parseAttachmentDispatchCompat` behavior unless overridden via
|
|
702
|
+
* `options.versionFallbackPolicy`.
|
|
407
703
|
*/
|
|
408
704
|
export function parseAttachmentDispatchCompat(input, version, domain, options = {}) {
|
|
705
|
+
const versionFallbackPolicy = options.versionFallbackPolicy ??
|
|
706
|
+
createAttachmentVersionFallbackPolicy({
|
|
707
|
+
mode: options.mode,
|
|
708
|
+
});
|
|
709
|
+
const recoveryDiagnosticObserver = composeRecoveryDiagnosticObserver({
|
|
710
|
+
onRecoveryDiagnostic: options.onRecoveryDiagnostic,
|
|
711
|
+
onAttachmentVersionFallback: options.versionFallbackPolicy
|
|
712
|
+
? undefined
|
|
713
|
+
: options.onVersionFallback,
|
|
714
|
+
});
|
|
409
715
|
const context = {
|
|
410
|
-
|
|
411
|
-
|
|
716
|
+
versionFallbackPolicy,
|
|
717
|
+
recoveryDiagnosticObserver,
|
|
412
718
|
};
|
|
413
|
-
return
|
|
719
|
+
return parseAttachmentDispatchWithPolicy(input, version, domain, context);
|
|
414
720
|
}
|
|
415
|
-
/**
|
|
721
|
+
/**
|
|
722
|
+
* Parse one attachment group with strict fail-fast semantics.
|
|
723
|
+
*
|
|
724
|
+
* Why:
|
|
725
|
+
* this API guarantees no major-version fallback and no wrapper opaque-tail
|
|
726
|
+
* preservation, matching parity/validation use cases.
|
|
727
|
+
*/
|
|
416
728
|
export function parseAttachmentDispatch(input, version, domain) {
|
|
417
|
-
return
|
|
418
|
-
|
|
729
|
+
return parseAttachmentDispatchWithPolicy(input, version, domain, {
|
|
730
|
+
versionFallbackPolicy: createAttachmentVersionFallbackPolicy({
|
|
731
|
+
mode: "strict",
|
|
732
|
+
}),
|
|
419
733
|
});
|
|
420
734
|
}
|