cesr-ts 0.3.0 → 0.4.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.
Files changed (192) hide show
  1. package/README.md +6 -1
  2. package/esm/src/annotate/annotator.js +4 -7
  3. package/esm/src/annotate/comments.js +1 -1
  4. package/esm/src/annotate/render.js +66 -75
  5. package/esm/src/core/bytes.js +47 -0
  6. package/esm/src/core/cbor.js +122 -0
  7. package/esm/src/core/errors.js +13 -0
  8. package/esm/src/core/parser-attachment-collector.js +1 -2
  9. package/esm/src/core/parser-engine.js +7 -7
  10. package/esm/src/core/parser-frame-parser.js +14 -20
  11. package/esm/src/index.js +59 -33
  12. package/esm/src/parser/attachment-parser.js +1 -1
  13. package/esm/src/parser/group-dispatch.js +27 -41
  14. package/esm/src/primitives/aggor.js +74 -31
  15. package/esm/src/primitives/bexter.js +60 -16
  16. package/esm/src/primitives/blinder.js +27 -20
  17. package/esm/src/primitives/cigar.js +25 -15
  18. package/esm/src/primitives/cipher.js +18 -0
  19. package/esm/src/primitives/codex.js +74 -0
  20. package/esm/src/primitives/compactor.js +1 -7
  21. package/esm/src/primitives/counter.js +203 -13
  22. package/esm/src/primitives/dater.js +31 -16
  23. package/esm/src/primitives/decimer.js +65 -0
  24. package/esm/src/primitives/decrypter.js +18 -0
  25. package/esm/src/primitives/diger.js +24 -18
  26. package/esm/src/primitives/encrypter.js +19 -0
  27. package/esm/src/primitives/ilker.js +24 -12
  28. package/esm/src/primitives/indexer.js +199 -7
  29. package/esm/src/primitives/labeler.js +74 -24
  30. package/esm/src/primitives/mapper.js +14 -35
  31. package/esm/src/primitives/matter.js +160 -6
  32. package/esm/src/primitives/mediar.js +27 -18
  33. package/esm/src/primitives/noncer.js +33 -15
  34. package/esm/src/primitives/number.js +28 -27
  35. package/esm/src/primitives/pather.js +30 -18
  36. package/esm/src/primitives/prefixer.js +23 -20
  37. package/esm/src/primitives/primitive.js +9 -0
  38. package/esm/src/primitives/registry.js +3 -3
  39. package/esm/src/primitives/saider.js +24 -18
  40. package/esm/src/primitives/salter.js +22 -0
  41. package/esm/src/primitives/sealer.js +27 -32
  42. package/esm/src/primitives/seqner.js +28 -19
  43. package/esm/src/primitives/siger.js +29 -0
  44. package/esm/src/primitives/signer.js +23 -0
  45. package/esm/src/primitives/structor.js +104 -0
  46. package/esm/src/primitives/tagger.js +44 -0
  47. package/esm/src/primitives/texter.js +24 -15
  48. package/esm/src/primitives/tholder.js +27 -24
  49. package/esm/src/primitives/traitor.js +23 -16
  50. package/esm/src/primitives/unknown.js +89 -0
  51. package/esm/src/primitives/verfer.js +25 -25
  52. package/esm/src/primitives/verser.js +54 -23
  53. package/esm/src/serder/serder.js +190 -10
  54. package/esm/src/serder/serdery.js +2 -2
  55. package/esm/src/tables/codex-utils.js +9 -0
  56. package/esm/src/tables/counter-codex.js +1 -1
  57. package/esm/src/tables/counter-groups.js +46 -0
  58. package/esm/src/tables/counter-version-registry.js +1 -1
  59. package/esm/src/tables/indexer.codex.generated.js +64 -0
  60. package/esm/src/tables/indexer.tables.generated.js +72 -0
  61. package/esm/src/tables/matter.codex.generated.js +399 -0
  62. package/esm/src/tables/matter.tables.generated.js +112 -0
  63. package/esm/src/tables/trait.codex.generated.js +10 -0
  64. package/esm/src/version.js +2 -2
  65. package/package.json +1 -1
  66. package/types/src/adapters/async-iterable.d.ts +1 -1
  67. package/types/src/adapters/async-iterable.d.ts.map +1 -1
  68. package/types/src/adapters/effection.d.ts +1 -1
  69. package/types/src/adapters/effection.d.ts.map +1 -1
  70. package/types/src/annotate/annotator.d.ts.map +1 -1
  71. package/types/src/annotate/comments.d.ts.map +1 -1
  72. package/types/src/annotate/render.d.ts.map +1 -1
  73. package/types/src/bench/parser-benchmark.d.ts.map +1 -1
  74. package/types/src/core/bytes.d.ts +22 -0
  75. package/types/src/core/bytes.d.ts.map +1 -1
  76. package/types/src/core/cbor.d.ts +30 -0
  77. package/types/src/core/cbor.d.ts.map +1 -0
  78. package/types/src/core/errors.d.ts +13 -0
  79. package/types/src/core/errors.d.ts.map +1 -1
  80. package/types/src/core/parser-attachment-collector.d.ts.map +1 -1
  81. package/types/src/core/parser-engine.d.ts +1 -1
  82. package/types/src/core/parser-engine.d.ts.map +1 -1
  83. package/types/src/core/parser-frame-parser.d.ts +1 -1
  84. package/types/src/core/parser-frame-parser.d.ts.map +1 -1
  85. package/types/src/core/types.d.ts +7 -36
  86. package/types/src/core/types.d.ts.map +1 -1
  87. package/types/src/index.d.ts +59 -33
  88. package/types/src/index.d.ts.map +1 -1
  89. package/types/src/parser/attachment-fallback-policy.d.ts.map +1 -1
  90. package/types/src/parser/attachment-parser.d.ts.map +1 -1
  91. package/types/src/parser/group-dispatch.d.ts +2 -2
  92. package/types/src/parser/group-dispatch.d.ts.map +1 -1
  93. package/types/src/primitives/aggor.d.ts +34 -8
  94. package/types/src/primitives/aggor.d.ts.map +1 -1
  95. package/types/src/primitives/bexter.d.ts +28 -6
  96. package/types/src/primitives/bexter.d.ts.map +1 -1
  97. package/types/src/primitives/blinder.d.ts +16 -8
  98. package/types/src/primitives/blinder.d.ts.map +1 -1
  99. package/types/src/primitives/cigar.d.ts +12 -7
  100. package/types/src/primitives/cigar.d.ts.map +1 -1
  101. package/types/src/primitives/cipher.d.ts +11 -0
  102. package/types/src/primitives/cipher.d.ts.map +1 -0
  103. package/types/src/primitives/codex.d.ts +54 -0
  104. package/types/src/primitives/codex.d.ts.map +1 -0
  105. package/types/src/primitives/compactor.d.ts.map +1 -1
  106. package/types/src/primitives/counter.d.ts +63 -2
  107. package/types/src/primitives/counter.d.ts.map +1 -1
  108. package/types/src/primitives/dater.d.ts +12 -7
  109. package/types/src/primitives/dater.d.ts.map +1 -1
  110. package/types/src/primitives/decimer.d.ts +23 -0
  111. package/types/src/primitives/decimer.d.ts.map +1 -0
  112. package/types/src/primitives/decrypter.d.ts +11 -0
  113. package/types/src/primitives/decrypter.d.ts.map +1 -0
  114. package/types/src/primitives/diger.d.ts +16 -7
  115. package/types/src/primitives/diger.d.ts.map +1 -1
  116. package/types/src/primitives/encrypter.d.ts +12 -0
  117. package/types/src/primitives/encrypter.d.ts.map +1 -0
  118. package/types/src/primitives/ilker.d.ts +13 -6
  119. package/types/src/primitives/ilker.d.ts.map +1 -1
  120. package/types/src/primitives/indexer.d.ts +52 -1
  121. package/types/src/primitives/indexer.d.ts.map +1 -1
  122. package/types/src/primitives/labeler.d.ts +28 -11
  123. package/types/src/primitives/labeler.d.ts.map +1 -1
  124. package/types/src/primitives/mapper.d.ts +4 -6
  125. package/types/src/primitives/mapper.d.ts.map +1 -1
  126. package/types/src/primitives/matter.d.ts +44 -1
  127. package/types/src/primitives/matter.d.ts.map +1 -1
  128. package/types/src/primitives/mediar.d.ts +16 -8
  129. package/types/src/primitives/mediar.d.ts.map +1 -1
  130. package/types/src/primitives/noncer.d.ts +20 -6
  131. package/types/src/primitives/noncer.d.ts.map +1 -1
  132. package/types/src/primitives/number.d.ts +12 -7
  133. package/types/src/primitives/number.d.ts.map +1 -1
  134. package/types/src/primitives/pather.d.ts +12 -6
  135. package/types/src/primitives/pather.d.ts.map +1 -1
  136. package/types/src/primitives/prefixer.d.ts +12 -6
  137. package/types/src/primitives/prefixer.d.ts.map +1 -1
  138. package/types/src/primitives/primitive.d.ts +25 -0
  139. package/types/src/primitives/primitive.d.ts.map +1 -0
  140. package/types/src/primitives/registry.d.ts.map +1 -1
  141. package/types/src/primitives/saider.d.ts +12 -7
  142. package/types/src/primitives/saider.d.ts.map +1 -1
  143. package/types/src/primitives/salter.d.ts +13 -0
  144. package/types/src/primitives/salter.d.ts.map +1 -0
  145. package/types/src/primitives/sealer.d.ts +16 -8
  146. package/types/src/primitives/sealer.d.ts.map +1 -1
  147. package/types/src/primitives/seqner.d.ts +12 -7
  148. package/types/src/primitives/seqner.d.ts.map +1 -1
  149. package/types/src/primitives/siger.d.ts +16 -0
  150. package/types/src/primitives/siger.d.ts.map +1 -0
  151. package/types/src/primitives/signer.d.ts +14 -0
  152. package/types/src/primitives/signer.d.ts.map +1 -0
  153. package/types/src/primitives/structor.d.ts +44 -0
  154. package/types/src/primitives/structor.d.ts.map +1 -0
  155. package/types/src/primitives/tagger.d.ts +17 -0
  156. package/types/src/primitives/tagger.d.ts.map +1 -0
  157. package/types/src/primitives/texter.d.ts +11 -6
  158. package/types/src/primitives/texter.d.ts.map +1 -1
  159. package/types/src/primitives/tholder.d.ts +11 -6
  160. package/types/src/primitives/tholder.d.ts.map +1 -1
  161. package/types/src/primitives/traitor.d.ts +13 -6
  162. package/types/src/primitives/traitor.d.ts.map +1 -1
  163. package/types/src/primitives/unknown.d.ts +31 -0
  164. package/types/src/primitives/unknown.d.ts.map +1 -0
  165. package/types/src/primitives/verfer.d.ts +12 -7
  166. package/types/src/primitives/verfer.d.ts.map +1 -1
  167. package/types/src/primitives/verser.d.ts +18 -8
  168. package/types/src/primitives/verser.d.ts.map +1 -1
  169. package/types/src/serder/serder.d.ts +69 -3
  170. package/types/src/serder/serder.d.ts.map +1 -1
  171. package/types/src/serder/serdery.d.ts.map +1 -1
  172. package/types/src/tables/codex-utils.d.ts +4 -0
  173. package/types/src/tables/codex-utils.d.ts.map +1 -0
  174. package/types/src/tables/counter-codex.d.ts.map +1 -1
  175. package/types/src/tables/counter-groups.d.ts +8 -0
  176. package/types/src/tables/counter-groups.d.ts.map +1 -0
  177. package/types/src/tables/indexer.codex.generated.d.ts +127 -0
  178. package/types/src/tables/indexer.codex.generated.d.ts.map +1 -0
  179. package/types/src/tables/indexer.tables.generated.d.ts +46 -0
  180. package/types/src/tables/indexer.tables.generated.d.ts.map +1 -0
  181. package/types/src/tables/matter.codex.generated.d.ts +797 -0
  182. package/types/src/tables/matter.codex.generated.d.ts.map +1 -0
  183. package/types/src/tables/matter.tables.generated.d.ts +112 -0
  184. package/types/src/tables/matter.tables.generated.d.ts.map +1 -1
  185. package/types/src/tables/table-types.d.ts +7 -0
  186. package/types/src/tables/table-types.d.ts.map +1 -1
  187. package/types/src/tables/trait.codex.generated.d.ts +19 -0
  188. package/types/src/tables/trait.codex.generated.d.ts.map +1 -0
  189. package/types/src/version.d.ts +2 -2
  190. package/esm/src/tables/indexer.tables.js +0 -30
  191. package/types/src/tables/indexer.tables.d.ts +0 -12
  192. package/types/src/tables/indexer.tables.d.ts.map +0 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@ const parser = createParser();
18
18
  const out = parser.feed(new TextEncoder().encode("...CESR..."));
19
19
  const last = parser.flush();
20
20
 
21
- const text = '{"v":"KERI10JSON00002e_","t":"rpy","d":"Eabc"}';
21
+ const text = "{\"v\":\"KERI10JSON00002e_\",\"t\":\"rpy\",\"d\":\"Eabc\"}";
22
22
  const annotated = annotate(text, { domainHint: "txt", pretty: true });
23
23
  ```
24
24
 
@@ -58,3 +58,8 @@ easiest on-ramp:
58
58
  tufa version
59
59
  tufa annotate --in mystream.cesr --pretty
60
60
  ```
61
+
62
+ ## License
63
+
64
+ Licensed under the Apache License, Version 2.0 (`Apache-2.0`). See the top-level
65
+ `LICENSE` file in this repository.
@@ -1,5 +1,6 @@
1
+ import { b } from "../core/bytes.js";
1
2
  import { parseBytes } from "../core/parser-engine.js";
2
- import { renderAnnotatedFrames, renderWrapperAnnotatedStream, } from "./render.js";
3
+ import { renderAnnotatedFrames, renderWrapperAnnotatedStream } from "./render.js";
3
4
  const DEFAULT_OPTIONS = Object.freeze({
4
5
  commentMode: "inline",
5
6
  indent: 2,
@@ -26,17 +27,13 @@ function parsedFramesOrThrow(frames) {
26
27
  }
27
28
  export function annotateFrames(input, options) {
28
29
  const opts = resolveOptions(options);
29
- const bytes = typeof input === "string"
30
- ? new TextEncoder().encode(input)
31
- : input;
30
+ const bytes = typeof input === "string" ? b(input) : input;
32
31
  const frames = parsedFramesOrThrow(parseBytes(bytes));
33
32
  return renderAnnotatedFrames(frames, opts);
34
33
  }
35
34
  export function annotate(input, options) {
36
35
  const opts = resolveOptions(options);
37
- const bytes = typeof input === "string"
38
- ? new TextEncoder().encode(input)
39
- : input;
36
+ const bytes = typeof input === "string" ? b(input) : input;
40
37
  const wrapperAnnotated = renderWrapperAnnotatedStream(bytes, opts);
41
38
  if (wrapperAnnotated !== null) {
42
39
  return wrapperAnnotated;
@@ -1,6 +1,6 @@
1
+ import { resolveCounterCodeNameTable } from "../tables/counter-version-registry.js";
1
2
  import { COUNTER_CODE_NAMES_V1, COUNTER_CODE_NAMES_V2, } from "../tables/counter.tables.generated.js";
2
3
  import { MATTER_CODE_NAMES } from "../tables/matter.tables.generated.js";
3
- import { resolveCounterCodeNameTable } from "../tables/counter-version-registry.js";
4
4
  const NATIVE_FIELD_LABELS = Object.freeze({
5
5
  v: "version string",
6
6
  t: "ilk",
@@ -1,14 +1,15 @@
1
- import { parseBytes } from "../core/parser-engine.js";
1
+ import { b64ToInt, intToB64 } from "../core/bytes.js";
2
2
  import { DeserializeError, GroupSizeError, ShortageError, UnknownCodeError, } from "../core/errors.js";
3
+ import { parseBytes } from "../core/parser-engine.js";
3
4
  import { sniff } from "../parser/cold-start.js";
4
- import { parseCounter } from "../primitives/counter.js";
5
- import { parseMatter } from "../primitives/matter.js";
6
- import { parseIndexer } from "../primitives/indexer.js";
7
5
  import { parseAttachmentDispatchCompat } from "../parser/group-dispatch.js";
6
+ import { Counter, parseCounter } from "../primitives/counter.js";
7
+ import { Indexer } from "../primitives/indexer.js";
8
+ import { Matter, parseMatter } from "../primitives/matter.js";
9
+ import { isCounterGroupLike, isPrimitiveTuple } from "../primitives/primitive.js";
10
+ import { UnknownPrimitive } from "../primitives/unknown.js";
8
11
  import { counterCodeName, counterCodeNameForVersion, matterCodeName, nativeLabelName, } from "./comments.js";
9
- import { b64ToInt, intToB64 } from "../core/bytes.js";
10
12
  const TEXT_DECODER = new TextDecoder();
11
- const TEXT_ENCODER = new TextEncoder();
12
13
  const OPAQUE_TOKEN_COMMENT = "opaque token";
13
14
  const OPAQUE_WRAPPER_PAYLOAD_COMMENT = "opaque wrapper payload";
14
15
  const WRAPPER_GROUP_NAMES = new Set([
@@ -27,10 +28,10 @@ function toHex(bytes) {
27
28
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
28
29
  }
29
30
  function isRecoverableParseError(error) {
30
- return error instanceof UnknownCodeError ||
31
- error instanceof DeserializeError ||
32
- error instanceof ShortageError ||
33
- error instanceof GroupSizeError;
31
+ return error instanceof UnknownCodeError
32
+ || error instanceof DeserializeError
33
+ || error instanceof ShortageError
34
+ || error instanceof GroupSizeError;
34
35
  }
35
36
  function spaces(count) {
36
37
  return " ".repeat(Math.max(0, count));
@@ -58,51 +59,6 @@ function emitLine(lines, value, comment, indent, options) {
58
59
  }
59
60
  lines.push(`${pad}${value} # ${comment}`);
60
61
  }
61
- function describeToken(token, domain, version) {
62
- const input = TEXT_ENCODER.encode(token);
63
- try {
64
- const counter = parseCounter(input, version, domain);
65
- return {
66
- comment: `${counterCodeNameForVersion(counter.code, version)} counter`,
67
- size: domain === "bny" ? counter.fullSizeB2 : counter.fullSize,
68
- };
69
- }
70
- catch (error) {
71
- if (!isRecoverableParseError(error)) {
72
- throw error;
73
- }
74
- // Try indexer/matter below.
75
- }
76
- try {
77
- const indexer = parseIndexer(input, domain);
78
- return {
79
- comment: `Indexer ${indexer.code}`,
80
- size: domain === "bny" ? indexer.fullSizeB2 : indexer.fullSize,
81
- };
82
- }
83
- catch (error) {
84
- if (!isRecoverableParseError(error)) {
85
- throw error;
86
- }
87
- // Try matter below.
88
- }
89
- try {
90
- const matter = parseMatter(input, domain);
91
- return {
92
- comment: matterCodeName(matter.code),
93
- size: domain === "bny" ? matter.fullSizeB2 : matter.fullSize,
94
- };
95
- }
96
- catch (error) {
97
- if (!isRecoverableParseError(error)) {
98
- throw error;
99
- }
100
- return {
101
- comment: OPAQUE_TOKEN_COMMENT,
102
- size: token.length,
103
- };
104
- }
105
- }
106
62
  function parseCounterCompat(input, version, domain) {
107
63
  try {
108
64
  return parseCounter(input, version, domain);
@@ -130,25 +86,35 @@ function decodeVersionCounter(counter) {
130
86
  }
131
87
  function renderGroupItems(lines, items, version, indent, options) {
132
88
  for (const item of items) {
133
- if (item.kind === "qb64") {
134
- const parsed = describeToken(item.qb64, "txt", version);
135
- const comment = item.opaque
136
- ? OPAQUE_WRAPPER_PAYLOAD_COMMENT
137
- : parsed.comment;
138
- emitLine(lines, item.qb64, comment, indent, options);
89
+ if (isPrimitiveTuple(item)) {
90
+ renderGroupItems(lines, item, version, indent + options.indent, options);
139
91
  continue;
140
92
  }
141
- if (item.kind === "qb2") {
142
- emitLine(lines, `0x${toHex(item.qb2)}`, item.opaque ? OPAQUE_WRAPPER_PAYLOAD_COMMENT : "raw qb2 triplet token", indent, options);
93
+ if (isCounterGroupLike(item)) {
94
+ emitLine(lines, item.qb64, `${item.name} nested group`, indent, options);
95
+ renderGroupItems(lines, [...item.items], version, indent + options.indent, options);
143
96
  continue;
144
97
  }
145
- if (item.kind === "tuple") {
146
- renderGroupItems(lines, item.items, version, indent + options.indent, options);
98
+ if (item instanceof UnknownPrimitive) {
99
+ const value = item.sourceDomain === "bny"
100
+ ? `0x${toHex(item.qb2)}`
101
+ : item.qb64;
102
+ emitLine(lines, value, OPAQUE_WRAPPER_PAYLOAD_COMMENT, indent, options);
147
103
  continue;
148
104
  }
149
- if (item.kind === "group") {
150
- emitLine(lines, `${item.code}`, `${item.name} nested group`, indent, options);
105
+ if (item instanceof Counter) {
106
+ emitLine(lines, item.qb64, `${counterCodeNameForVersion(item.code, version)} counter`, indent, options);
107
+ continue;
151
108
  }
109
+ if (item instanceof Indexer) {
110
+ emitLine(lines, item.qb64, `Indexer ${item.code}`, indent, options);
111
+ continue;
112
+ }
113
+ if (item instanceof Matter) {
114
+ emitLine(lines, item.qb64, matterCodeName(item.code), indent, options);
115
+ continue;
116
+ }
117
+ emitLine(lines, String(item), OPAQUE_TOKEN_COMMENT, indent, options);
152
118
  }
153
119
  }
154
120
  function renderAttachmentGroupRaw(lines, raw, version, indent, options) {
@@ -212,18 +178,43 @@ function renderNativeBody(lines, frame, options, version) {
212
178
  }
213
179
  const counter = parseCounterCompat(raw, version, domain);
214
180
  emitLine(lines, counter.qb64, `${counterCodeNameForVersion(counter.code, version)} count=${counter.count}`, 0, options);
215
- for (const field of frame.body.native?.fields ?? []) {
216
- const label = nativeLabelName(field.label);
217
- const comment = label
218
- ? `${label} (${matterCodeName(field.code)})`
219
- : matterCodeName(field.code);
220
- emitLine(lines, field.qb64, comment, options.indent, options);
181
+ const unit = domain === "bny" ? 3 : 4;
182
+ const headerSize = domain === "bny" ? counter.fullSizeB2 : counter.fullSize;
183
+ const payloadSize = counter.count * unit;
184
+ const end = Math.min(raw.length, headerSize + payloadSize);
185
+ let offset = headerSize;
186
+ while (offset < end) {
187
+ const slice = raw.slice(offset, end);
188
+ try {
189
+ const nestedCounter = parseCounterCompat(slice, version, domain);
190
+ const nestedCounterSize = domain === "bny"
191
+ ? nestedCounter.fullSizeB2
192
+ : nestedCounter.fullSize;
193
+ emitLine(lines, nestedCounter.qb64, `${counterCodeNameForVersion(nestedCounter.code, version)} counter`, options.indent, options);
194
+ offset += nestedCounterSize;
195
+ continue;
196
+ }
197
+ catch (error) {
198
+ if (!isRecoverableParseError(error)) {
199
+ throw error;
200
+ }
201
+ }
202
+ const matter = parseMatter(slice, domain);
203
+ const matterSize = domain === "bny" ? matter.fullSizeB2 : matter.fullSize;
204
+ const field = frame.body.native?.fields.find((candidate) => candidate.primitive.qb64 === matter.qb64);
205
+ const label = field ? nativeLabelName(field.label) : null;
206
+ const primitiveComment = matter instanceof Indexer
207
+ ? `Indexer ${matter.code}`
208
+ : matterCodeName(matter.code);
209
+ const comment = label ? `${label} (${primitiveComment})` : primitiveComment;
210
+ emitLine(lines, matter.qb64, comment, options.indent, options);
211
+ offset += matterSize;
221
212
  }
222
213
  }
223
214
  function renderMessageBody(lines, frame, options) {
224
215
  const rawBody = TEXT_DECODER.decode(frame.body.raw);
225
- const isOpaqueCesrBody = frame.body.kind === "CESR" &&
226
- frame.body.ked === null;
216
+ const isOpaqueCesrBody = frame.body.kind === "CESR"
217
+ && frame.body.ked === null;
227
218
  if (isOpaqueCesrBody) {
228
219
  emitLine(lines, rawBody, `OPAQUE CESR body (non-serder fallback, hex=${toHex(frame.body.raw)})`, 0, options);
229
220
  return;
@@ -56,6 +56,10 @@ export function decodeB64(text) {
56
56
  }
57
57
  return out;
58
58
  }
59
+ /**
60
+ * Symmetric ceiling function
61
+ * @param value
62
+ */
59
63
  export function sceil(value) {
60
64
  return Math.ceil(value);
61
65
  }
@@ -74,3 +78,46 @@ export function codeB2ToB64(bytes, sextets) {
74
78
  export function nabSextets(bytes, sextets) {
75
79
  return codeB64ToB2(codeB2ToB64(bytes, sextets));
76
80
  }
81
+ /** UTF-8 string -> bytes helper. */
82
+ export const b = (t) => encoder.encode(t);
83
+ /** UTF-8 bytes -> string helper. */
84
+ export const t = (b) => decoder.decode(b);
85
+ /**
86
+ * Normalize lmdb-js key/value payloads to `Uint8Array`.
87
+ * Example: converts Node `Buffer`-like values from `getRange()`.
88
+ */
89
+ export function toBytes(value) {
90
+ if (value instanceof Uint8Array) {
91
+ return value;
92
+ }
93
+ return new Uint8Array(value);
94
+ }
95
+ /**
96
+ * Format number as 32-char hex string, zero padded.
97
+ * @param num
98
+ */
99
+ export const to32CharHex = (num) => {
100
+ return num
101
+ .toString(16)
102
+ .padStart(32, "0");
103
+ };
104
+ /** Constant-time-ish byte equality for small DB keys/values. */
105
+ export function bytesEqual(left, right) {
106
+ if (left.length !== right.length) {
107
+ return false;
108
+ }
109
+ for (let i = 0; i < left.length; i++) {
110
+ if (left[i] !== right[i]) {
111
+ return false;
112
+ }
113
+ }
114
+ return true;
115
+ }
116
+ /** Stable hex fingerprint used for set-membership dedupe of binary values. */
117
+ export function bytesHex(value) {
118
+ // deno-fmt-ignore
119
+ return Array.from(value)
120
+ .map((byte) => byte.toString(16)
121
+ .padStart(2, "0"))
122
+ .join("");
123
+ }
@@ -0,0 +1,122 @@
1
+ import { Decoder } from "cbor-x/decode";
2
+ import { Encoder } from "cbor-x/encode";
3
+ /**
4
+ * Normalize encoder output to a plain `Uint8Array`.
5
+ *
6
+ * `cbor-x` may return subclasses or reused backing buffers depending on the
7
+ * runtime and code path. The shared KERI codec deliberately copies the result
8
+ * so callers see a stable, plain `Uint8Array` contract that is safe to compare,
9
+ * persist, and hand across package boundaries without inheriting library
10
+ * implementation details.
11
+ */
12
+ function normalizeEncodedBytes(bytes) {
13
+ return new Uint8Array(bytes);
14
+ }
15
+ /**
16
+ * Reject JavaScript `Map` values anywhere in the payload graph.
17
+ *
18
+ * This is not a statement about CBOR in general. CBOR absolutely supports map
19
+ * values. The narrower rule here is about KERI/ACDC parity: KERIpy serializes
20
+ * the same semantic payloads through JSON, MGPK, and CBOR, and the CBOR paths
21
+ * we are mirroring are dict/list shaped rather than "generic CBOR" shaped.
22
+ *
23
+ * In TypeScript, allowing `Map` values would silently widen the contract past
24
+ * what the JSON and MGPK paths can represent and past what KERIpy currently
25
+ * exercises in the mirrored protocol/storage serializers. This guard fails
26
+ * early so parity drift becomes an explicit decision instead of an accidental
27
+ * convenience.
28
+ *
29
+ * The walk is cycle-safe because this function is defensive validation, not a
30
+ * serializer in its own right. Cycles would fail later in the encoder anyway,
31
+ * but we avoid infinite recursion here while still checking the reachable graph
32
+ * for `Map` instances.
33
+ */
34
+ function assertNoMapValues(value, seen = new Set()) {
35
+ if (value === null || typeof value !== "object") {
36
+ return;
37
+ }
38
+ if (value instanceof Map) {
39
+ throw new TypeError("KERI/ACDC CBOR payloads must use JSON-compatible plain objects/arrays, not JavaScript Map.");
40
+ }
41
+ if (seen.has(value)) {
42
+ return;
43
+ }
44
+ seen.add(value);
45
+ if (Array.isArray(value)) {
46
+ for (const item of value) {
47
+ assertNoMapValues(item, seen);
48
+ }
49
+ return;
50
+ }
51
+ for (const item of Object.values(value)) {
52
+ assertNoMapValues(item, seen);
53
+ }
54
+ }
55
+ /**
56
+ * Canonical KERI/ACDC CBOR encoder configuration.
57
+ *
58
+ * Two choices matter for parity:
59
+ * - `useRecords: false` disables `cbor-x` record extensions so plain objects
60
+ * stay plain maps on the wire, matching KERIpy's `cbor2` behavior.
61
+ * - `variableMapSize: true` forces preferred-size map headers, avoiding the
62
+ * default `map16` preallocation strategy that broke exact byte parity.
63
+ *
64
+ * We import `Encoder` from `cbor-x/encode` rather than the package root because
65
+ * the root Node entrypoint probes `process.env` during module evaluation. That
66
+ * widened Deno test permissions for no KERI value, so the shared codec avoids
67
+ * that entrypoint on purpose.
68
+ */
69
+ const keriCborEncoder = new Encoder({
70
+ useRecords: false,
71
+ variableMapSize: true,
72
+ });
73
+ /**
74
+ * Canonical KERI/ACDC CBOR decoder configuration.
75
+ *
76
+ * `mapsAsObjects: true` keeps decoded values aligned with the JSON-compatible
77
+ * object/list contract enforced by `encodeKeriCbor()`. This prevents the decode
78
+ * side from reintroducing `Map`-typed semantics that the encode side forbids
79
+ * and keeps protocol/storage callers working with the same value shapes they
80
+ * would see from JSON or MGPK decoding.
81
+ *
82
+ * Like the encoder, this uses the `cbor-x/decode` subpath to avoid the package
83
+ * root's Node-oriented environment probe.
84
+ */
85
+ const keriCborDecoder = new Decoder({
86
+ useRecords: false,
87
+ mapsAsObjects: true,
88
+ });
89
+ /**
90
+ * Encode KERI/ACDC CBOR with the same preferred map-size choices as KERIpy's
91
+ * `cbor2.dumps`.
92
+ *
93
+ * This is a protocol/storage codec for KERI/ACDC payloads, not a generic CBOR
94
+ * helper. KERIpy's corresponding emitters serialize dict/list-shaped data
95
+ * through `cbor2.dumps`, for example:
96
+ * - `keri.core.coring.dumps`
97
+ * - `keri.core.serdering.Serder.dumps`
98
+ * - `keri.db.koming.Komer.__serializeCBOR`
99
+ *
100
+ * The same semantic payloads also serialize as JSON and MGPK in KERIpy, so the
101
+ * intended cross-format contract is JSON-compatible plain objects and arrays.
102
+ * The default `cbor-x` encoder intentionally uses fixed-width `map16` headers
103
+ * for plain JS objects so it can preallocate and patch in the size later. That
104
+ * is valid CBOR but not byte-identical to KERIpy. This helper enables
105
+ * `variableMapSize` so small maps use the same compact headers that `cbor2`
106
+ * emits and rejects `Map` values so we do not silently drift into a broader
107
+ * CBOR type contract than KERIpy currently uses.
108
+ */
109
+ export function encodeKeriCbor(value) {
110
+ assertNoMapValues(value);
111
+ return normalizeEncodedBytes(keriCborEncoder.encode(value));
112
+ }
113
+ /**
114
+ * Decode KERI/ACDC CBOR into plain JS objects/arrays.
115
+ *
116
+ * This keeps the decoder aligned with the encoder policy surface and avoids
117
+ * record-extension or `Map`-typed decode behavior on KERI/ACDC protocol and
118
+ * storage paths.
119
+ */
120
+ export function decodeKeriCbor(raw) {
121
+ return keriCborDecoder.decode(raw);
122
+ }
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Base parser failure carrying optional offset/context for maintainer
3
+ * diagnostics.
4
+ *
5
+ * Subclasses identify which parser phase rejected the input so callers can
6
+ * decide whether to recover, retry, or fail hard.
7
+ */
1
8
  export class ParserError extends Error {
2
9
  constructor(message, offset, context) {
3
10
  super(message);
@@ -16,6 +23,7 @@ export class ParserError extends Error {
16
23
  this.name = globalThis[Symbol.for("import-meta-ponyfill-esmodule")](import.meta).name;
17
24
  }
18
25
  }
26
+ /** Input ended before the parser had enough bytes to complete the current unit. */
19
27
  export class ShortageError extends ParserError {
20
28
  constructor(needed, available, offset) {
21
29
  super(`Need ${needed} bytes but only ${available} available`, offset);
@@ -33,14 +41,19 @@ export class ShortageError extends ParserError {
33
41
  });
34
42
  }
35
43
  }
44
+ /** Parser encountered an invalid cold-start selector. */
36
45
  export class ColdStartError extends ParserError {
37
46
  }
47
+ /** Version framing or version-table selection failed. */
38
48
  export class VersionError extends ParserError {
39
49
  }
50
+ /** A CESR code selector was not recognized in the active codex. */
40
51
  export class UnknownCodeError extends ParserError {
41
52
  }
53
+ /** Group/count metadata could not describe a valid parse shape. */
42
54
  export class GroupSizeError extends ParserError {
43
55
  }
56
+ /** Qualified input could not be deserialized into a valid CESR payload. */
44
57
  export class DeserializeError extends ParserError {
45
58
  }
46
59
  /** Failure while constructing syntax artifacts from token bytes. */
@@ -34,8 +34,7 @@ export class AttachmentCollector {
34
34
  value: void 0
35
35
  });
36
36
  this.frameBoundaryPolicy = options.frameBoundaryPolicy;
37
- this.attachmentVersionFallbackPolicy =
38
- options.attachmentVersionFallbackPolicy;
37
+ this.attachmentVersionFallbackPolicy = options.attachmentVersionFallbackPolicy;
39
38
  this.recoveryDiagnosticObserver = options.recoveryDiagnosticObserver;
40
39
  this.isFrameBoundaryAhead = options.isFrameBoundaryAhead;
41
40
  }
@@ -1,11 +1,11 @@
1
1
  import { createAttachmentVersionFallbackPolicy, } from "../parser/group-dispatch.js";
2
2
  import { ParserError, ShortageError } from "./errors.js";
3
+ import { AttachmentCollector } from "./parser-attachment-collector.js";
3
4
  import { DEFAULT_VERSION } from "./parser-constants.js";
4
- import { ParserStreamState } from "./parser-stream-state.js";
5
5
  import { DeferredFrameLifecycle } from "./parser-deferred-frames.js";
6
6
  import { FrameParser } from "./parser-frame-parser.js";
7
- import { AttachmentCollector } from "./parser-attachment-collector.js";
8
- import { createFrameBoundaryPolicy, } from "./parser-policy.js";
7
+ import { createFrameBoundaryPolicy } from "./parser-policy.js";
8
+ import { ParserStreamState } from "./parser-stream-state.js";
9
9
  import { composeRecoveryDiagnosticObserver, } from "./recovery-diagnostics.js";
10
10
  /**
11
11
  * Streaming CESR parser for message-domain and CESR-native body-group streams.
@@ -60,16 +60,16 @@ export class CesrParser {
60
60
  writable: true,
61
61
  value: void 0
62
62
  });
63
- this.frameBoundaryPolicy = options.frameBoundaryPolicy ??
64
- createFrameBoundaryPolicy(options.framed ?? false);
63
+ this.frameBoundaryPolicy = options.frameBoundaryPolicy
64
+ ?? createFrameBoundaryPolicy(options.framed ?? false);
65
65
  this.recoveryDiagnosticObserver = composeRecoveryDiagnosticObserver({
66
66
  onRecoveryDiagnostic: options.onRecoveryDiagnostic,
67
67
  onAttachmentVersionFallback: options.attachmentVersionFallbackPolicy
68
68
  ? undefined
69
69
  : options.onAttachmentVersionFallback,
70
70
  });
71
- const attachmentVersionFallbackPolicy = options.attachmentVersionFallbackPolicy ??
72
- createAttachmentVersionFallbackPolicy({
71
+ const attachmentVersionFallbackPolicy = options.attachmentVersionFallbackPolicy
72
+ ?? createAttachmentVersionFallbackPolicy({
73
73
  mode: options.attachmentDispatchMode,
74
74
  });
75
75
  this.stream = new ParserStreamState(DEFAULT_VERSION);
@@ -3,8 +3,8 @@ import { sniff } from "../parser/cold-start.js";
3
3
  import { parseCounter } from "../primitives/counter.js";
4
4
  import { parseIlker } from "../primitives/ilker.js";
5
5
  import { isLabelerCode, parseLabeler } from "../primitives/labeler.js";
6
- import { parseMatter } from "../primitives/matter.js";
7
6
  import { interpretMapperBodySyntax, parseMapperBodySyntax, } from "../primitives/mapper.js";
7
+ import { parseMatter } from "../primitives/matter.js";
8
8
  import { parseVerser } from "../primitives/verser.js";
9
9
  import { reapSerder } from "../serder/serdery.js";
10
10
  import { Kinds, Protocols } from "../tables/versions.js";
@@ -46,8 +46,7 @@ export class FrameParser {
46
46
  value: void 0
47
47
  });
48
48
  this.frameBoundaryPolicy = options.frameBoundaryPolicy;
49
- this.attachmentVersionFallbackPolicy =
50
- options.attachmentVersionFallbackPolicy;
49
+ this.attachmentVersionFallbackPolicy = options.attachmentVersionFallbackPolicy;
51
50
  this.onEnclosedFrames = options.onEnclosedFrames;
52
51
  this.recoveryDiagnosticObserver = options.recoveryDiagnosticObserver;
53
52
  }
@@ -432,9 +431,9 @@ export class FrameParser {
432
431
  };
433
432
  }
434
433
  catch (error) {
435
- if (error instanceof ShortageError ||
436
- error instanceof UnknownCodeError ||
437
- error instanceof DeserializeError) {
434
+ if (error instanceof ShortageError
435
+ || error instanceof UnknownCodeError
436
+ || error instanceof DeserializeError) {
438
437
  throw new SyntaxParseError(`Native body syntax parse failed: ${error.message}`, error);
439
438
  }
440
439
  throw error;
@@ -499,8 +498,7 @@ export class FrameParser {
499
498
  const size = tokenSize(ctr, cold);
500
499
  entries.push({
501
500
  kind: "value",
502
- code: ctr.code,
503
- qb64: ctr.qb64,
501
+ primitive: ctr,
504
502
  });
505
503
  offset += size;
506
504
  continue;
@@ -512,15 +510,13 @@ export class FrameParser {
512
510
  entries.push({
513
511
  kind: "label",
514
512
  label: parseLabeler(at, cold).label,
515
- code: token.code,
516
- qb64: token.qb64,
513
+ primitive: token,
517
514
  });
518
515
  continue;
519
516
  }
520
517
  entries.push({
521
518
  kind: "value",
522
- code: token.code,
523
- qb64: token.qb64,
519
+ primitive: token,
524
520
  });
525
521
  }
526
522
  if (offset !== end) {
@@ -534,8 +530,7 @@ export class FrameParser {
534
530
  try {
535
531
  return interpretMapperBodySyntax(syntax.mapper).map((field) => ({
536
532
  label: field.label,
537
- code: field.code,
538
- qb64: field.qb64,
533
+ primitive: field.primitive,
539
534
  }));
540
535
  }
541
536
  catch (error) {
@@ -554,8 +549,7 @@ export class FrameParser {
554
549
  }
555
550
  out.push({
556
551
  label: pendingLabel,
557
- code: entry.code,
558
- qb64: entry.qb64,
552
+ primitive: entry.primitive,
559
553
  });
560
554
  pendingLabel = null;
561
555
  }
@@ -591,8 +585,8 @@ export class FrameParser {
591
585
  if (error instanceof ShortageError) {
592
586
  throw error;
593
587
  }
594
- if (error instanceof UnknownCodeError ||
595
- error instanceof DeserializeError) {
588
+ if (error instanceof UnknownCodeError
589
+ || error instanceof DeserializeError) {
596
590
  continue;
597
591
  }
598
592
  throw error;
@@ -622,8 +616,8 @@ export class FrameParser {
622
616
  if (error instanceof ShortageError) {
623
617
  throw error;
624
618
  }
625
- if (error instanceof UnknownCodeError ||
626
- error instanceof DeserializeError) {
619
+ if (error instanceof UnknownCodeError
620
+ || error instanceof DeserializeError) {
627
621
  if (!firstError) {
628
622
  firstError = error;
629
623
  }