cesr-ts 0.3.1 → 0.5.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 (248) hide show
  1. package/README.md +3 -3
  2. package/esm/src/adapters/async-iterable.js +8 -0
  3. package/esm/src/adapters/effection.js +6 -0
  4. package/esm/src/annotate/annotator.js +16 -7
  5. package/esm/src/annotate/cli.js +12 -0
  6. package/esm/src/annotate/comments.js +6 -2
  7. package/esm/src/annotate/denot.js +6 -0
  8. package/esm/src/annotate/render.js +76 -76
  9. package/esm/src/core/bytes.js +56 -0
  10. package/esm/src/core/cbor.js +122 -0
  11. package/esm/src/core/errors.js +16 -0
  12. package/esm/src/core/parser-attachment-collector.js +1 -2
  13. package/esm/src/core/parser-engine.js +8 -8
  14. package/esm/src/core/parser-frame-parser.js +32 -35
  15. package/esm/src/core/vocabulary.js +47 -0
  16. package/esm/src/index.js +64 -33
  17. package/esm/src/parser/attachment-parser.js +5 -1
  18. package/esm/src/parser/cold-start.js +6 -0
  19. package/esm/src/parser/group-dispatch.js +28 -42
  20. package/esm/src/primitives/aggor.js +423 -36
  21. package/esm/src/primitives/bexter.js +59 -16
  22. package/esm/src/primitives/blinder.js +33 -20
  23. package/esm/src/primitives/byte-like.js +19 -0
  24. package/esm/src/primitives/cigar.js +39 -15
  25. package/esm/src/primitives/cipher.js +103 -0
  26. package/esm/src/primitives/codex.js +107 -0
  27. package/esm/src/primitives/compactor.js +308 -10
  28. package/esm/src/primitives/counter.js +207 -13
  29. package/esm/src/primitives/dater.js +30 -16
  30. package/esm/src/primitives/decimer.js +61 -0
  31. package/esm/src/primitives/decrypter.js +134 -0
  32. package/esm/src/primitives/diger.js +78 -18
  33. package/esm/src/primitives/disclosure.js +300 -0
  34. package/esm/src/primitives/encrypter.js +142 -0
  35. package/esm/src/primitives/hydrate.js +49 -0
  36. package/esm/src/primitives/ilker.js +22 -11
  37. package/esm/src/primitives/indexer.js +217 -8
  38. package/esm/src/primitives/labeler.js +73 -24
  39. package/esm/src/primitives/mapper.js +624 -68
  40. package/esm/src/primitives/matter.js +290 -6
  41. package/esm/src/primitives/mediar.js +33 -18
  42. package/esm/src/primitives/noncer.js +31 -14
  43. package/esm/src/primitives/number.js +27 -27
  44. package/esm/src/primitives/pather.js +128 -19
  45. package/esm/src/primitives/prefixer.js +22 -20
  46. package/esm/src/primitives/primitive.js +23 -0
  47. package/esm/src/primitives/registry.js +5 -3
  48. package/esm/src/primitives/saider.js +102 -19
  49. package/esm/src/primitives/salter.js +116 -0
  50. package/esm/src/primitives/sealed-box.js +41 -0
  51. package/esm/src/primitives/sealer.js +27 -32
  52. package/esm/src/primitives/seqner.js +27 -19
  53. package/esm/src/primitives/siger.js +84 -0
  54. package/esm/src/primitives/signer.js +257 -0
  55. package/esm/src/primitives/streamer.js +28 -0
  56. package/esm/src/primitives/structing.js +727 -0
  57. package/esm/src/primitives/structor.js +124 -0
  58. package/esm/src/primitives/tagger.js +43 -0
  59. package/esm/src/primitives/texter.js +23 -15
  60. package/esm/src/primitives/tholder.js +567 -22
  61. package/esm/src/primitives/traitor.js +21 -15
  62. package/esm/src/primitives/unknown.js +89 -0
  63. package/esm/src/primitives/verfer.js +69 -25
  64. package/esm/src/primitives/verser.js +63 -22
  65. package/esm/src/router/router-stub.js +1 -0
  66. package/esm/src/serder/native.js +1326 -0
  67. package/esm/src/serder/serder.js +1699 -20
  68. package/esm/src/serder/serdery.js +94 -9
  69. package/esm/src/serder/smell.js +26 -1
  70. package/esm/src/tables/codex-utils.js +11 -0
  71. package/esm/src/tables/counter-codex.js +3 -1
  72. package/esm/src/tables/counter-groups.js +52 -0
  73. package/esm/src/tables/counter-version-registry.js +1 -1
  74. package/esm/src/tables/indexer.codex.generated.js +64 -0
  75. package/esm/src/tables/indexer.tables.generated.js +72 -0
  76. package/esm/src/tables/matter.codex.generated.js +399 -0
  77. package/esm/src/tables/matter.tables.generated.js +112 -0
  78. package/esm/src/tables/trait.codex.generated.js +10 -0
  79. package/esm/src/tables/versions.js +4 -0
  80. package/esm/src/version.js +5 -2
  81. package/package.json +5 -2
  82. package/types/src/adapters/async-iterable.d.ts +9 -1
  83. package/types/src/adapters/async-iterable.d.ts.map +1 -1
  84. package/types/src/adapters/effection.d.ts +8 -1
  85. package/types/src/adapters/effection.d.ts.map +1 -1
  86. package/types/src/annotate/annotator.d.ts +12 -0
  87. package/types/src/annotate/annotator.d.ts.map +1 -1
  88. package/types/src/annotate/cli.d.ts +13 -0
  89. package/types/src/annotate/cli.d.ts.map +1 -1
  90. package/types/src/annotate/comments.d.ts +4 -0
  91. package/types/src/annotate/comments.d.ts.map +1 -1
  92. package/types/src/annotate/denot.d.ts +6 -0
  93. package/types/src/annotate/denot.d.ts.map +1 -1
  94. package/types/src/annotate/render.d.ts.map +1 -1
  95. package/types/src/annotate/types.d.ts +2 -0
  96. package/types/src/annotate/types.d.ts.map +1 -1
  97. package/types/src/bench/parser-benchmark.d.ts +3 -0
  98. package/types/src/bench/parser-benchmark.d.ts.map +1 -1
  99. package/types/src/core/bytes.d.ts +31 -0
  100. package/types/src/core/bytes.d.ts.map +1 -1
  101. package/types/src/core/cbor.d.ts +30 -0
  102. package/types/src/core/cbor.d.ts.map +1 -0
  103. package/types/src/core/errors.d.ts +16 -0
  104. package/types/src/core/errors.d.ts.map +1 -1
  105. package/types/src/core/parser-attachment-collector.d.ts.map +1 -1
  106. package/types/src/core/parser-engine.d.ts +5 -1
  107. package/types/src/core/parser-engine.d.ts.map +1 -1
  108. package/types/src/core/parser-frame-parser.d.ts +1 -1
  109. package/types/src/core/parser-frame-parser.d.ts.map +1 -1
  110. package/types/src/core/types.d.ts +9 -36
  111. package/types/src/core/types.d.ts.map +1 -1
  112. package/types/src/core/vocabulary.d.ts +48 -0
  113. package/types/src/core/vocabulary.d.ts.map +1 -0
  114. package/types/src/index.d.ts +64 -33
  115. package/types/src/index.d.ts.map +1 -1
  116. package/types/src/parser/attachment-fallback-policy.d.ts.map +1 -1
  117. package/types/src/parser/attachment-parser.d.ts +4 -0
  118. package/types/src/parser/attachment-parser.d.ts.map +1 -1
  119. package/types/src/parser/cold-start.d.ts +6 -0
  120. package/types/src/parser/cold-start.d.ts.map +1 -1
  121. package/types/src/parser/group-dispatch.d.ts +2 -2
  122. package/types/src/parser/group-dispatch.d.ts.map +1 -1
  123. package/types/src/primitives/aggor.d.ts +122 -9
  124. package/types/src/primitives/aggor.d.ts.map +1 -1
  125. package/types/src/primitives/bexter.d.ts +28 -6
  126. package/types/src/primitives/bexter.d.ts.map +1 -1
  127. package/types/src/primitives/blinder.d.ts +22 -8
  128. package/types/src/primitives/blinder.d.ts.map +1 -1
  129. package/types/src/primitives/byte-like.d.ts +7 -0
  130. package/types/src/primitives/byte-like.d.ts.map +1 -0
  131. package/types/src/primitives/cigar.d.ts +22 -7
  132. package/types/src/primitives/cigar.d.ts.map +1 -1
  133. package/types/src/primitives/cipher.d.ts +82 -0
  134. package/types/src/primitives/cipher.d.ts.map +1 -0
  135. package/types/src/primitives/codex.d.ts +84 -0
  136. package/types/src/primitives/codex.d.ts.map +1 -0
  137. package/types/src/primitives/compactor.d.ts +94 -2
  138. package/types/src/primitives/compactor.d.ts.map +1 -1
  139. package/types/src/primitives/counter.d.ts +63 -2
  140. package/types/src/primitives/counter.d.ts.map +1 -1
  141. package/types/src/primitives/dater.d.ts +12 -7
  142. package/types/src/primitives/dater.d.ts.map +1 -1
  143. package/types/src/primitives/decimer.d.ts +23 -0
  144. package/types/src/primitives/decimer.d.ts.map +1 -0
  145. package/types/src/primitives/decrypter.d.ts +72 -0
  146. package/types/src/primitives/decrypter.d.ts.map +1 -0
  147. package/types/src/primitives/diger.d.ts +32 -7
  148. package/types/src/primitives/diger.d.ts.map +1 -1
  149. package/types/src/primitives/disclosure.d.ts +201 -0
  150. package/types/src/primitives/disclosure.d.ts.map +1 -0
  151. package/types/src/primitives/encrypter.d.ts +74 -0
  152. package/types/src/primitives/encrypter.d.ts.map +1 -0
  153. package/types/src/primitives/hydrate.d.ts +20 -0
  154. package/types/src/primitives/hydrate.d.ts.map +1 -0
  155. package/types/src/primitives/ilker.d.ts +13 -6
  156. package/types/src/primitives/ilker.d.ts.map +1 -1
  157. package/types/src/primitives/indexer.d.ts +52 -1
  158. package/types/src/primitives/indexer.d.ts.map +1 -1
  159. package/types/src/primitives/labeler.d.ts +28 -11
  160. package/types/src/primitives/labeler.d.ts.map +1 -1
  161. package/types/src/primitives/mapper.d.ts +136 -17
  162. package/types/src/primitives/mapper.d.ts.map +1 -1
  163. package/types/src/primitives/matter.d.ts +76 -1
  164. package/types/src/primitives/matter.d.ts.map +1 -1
  165. package/types/src/primitives/mediar.d.ts +22 -8
  166. package/types/src/primitives/mediar.d.ts.map +1 -1
  167. package/types/src/primitives/noncer.d.ts +20 -6
  168. package/types/src/primitives/noncer.d.ts.map +1 -1
  169. package/types/src/primitives/number.d.ts +12 -7
  170. package/types/src/primitives/number.d.ts.map +1 -1
  171. package/types/src/primitives/pather.d.ts +33 -6
  172. package/types/src/primitives/pather.d.ts.map +1 -1
  173. package/types/src/primitives/prefixer.d.ts +12 -6
  174. package/types/src/primitives/prefixer.d.ts.map +1 -1
  175. package/types/src/primitives/primitive.d.ts +51 -0
  176. package/types/src/primitives/primitive.d.ts.map +1 -0
  177. package/types/src/primitives/registry.d.ts +3 -0
  178. package/types/src/primitives/registry.d.ts.map +1 -1
  179. package/types/src/primitives/saider.d.ts +53 -7
  180. package/types/src/primitives/saider.d.ts.map +1 -1
  181. package/types/src/primitives/salter.d.ts +68 -0
  182. package/types/src/primitives/salter.d.ts.map +1 -0
  183. package/types/src/primitives/sealed-box.d.ts +29 -0
  184. package/types/src/primitives/sealed-box.d.ts.map +1 -0
  185. package/types/src/primitives/sealer.d.ts +16 -8
  186. package/types/src/primitives/sealer.d.ts.map +1 -1
  187. package/types/src/primitives/seqner.d.ts +12 -7
  188. package/types/src/primitives/seqner.d.ts.map +1 -1
  189. package/types/src/primitives/siger.d.ts +30 -0
  190. package/types/src/primitives/siger.d.ts.map +1 -0
  191. package/types/src/primitives/signer.d.ts +90 -0
  192. package/types/src/primitives/signer.d.ts.map +1 -0
  193. package/types/src/primitives/streamer.d.ts +31 -0
  194. package/types/src/primitives/streamer.d.ts.map +1 -0
  195. package/types/src/primitives/structing.d.ts +4114 -0
  196. package/types/src/primitives/structing.d.ts.map +1 -0
  197. package/types/src/primitives/structor.d.ts +50 -0
  198. package/types/src/primitives/structor.d.ts.map +1 -0
  199. package/types/src/primitives/tagger.d.ts +17 -0
  200. package/types/src/primitives/tagger.d.ts.map +1 -0
  201. package/types/src/primitives/texter.d.ts +11 -6
  202. package/types/src/primitives/texter.d.ts.map +1 -1
  203. package/types/src/primitives/tholder.d.ts +156 -6
  204. package/types/src/primitives/tholder.d.ts.map +1 -1
  205. package/types/src/primitives/traitor.d.ts +13 -6
  206. package/types/src/primitives/traitor.d.ts.map +1 -1
  207. package/types/src/primitives/unknown.d.ts +32 -0
  208. package/types/src/primitives/unknown.d.ts.map +1 -0
  209. package/types/src/primitives/verfer.d.ts +24 -7
  210. package/types/src/primitives/verfer.d.ts.map +1 -1
  211. package/types/src/primitives/verser.d.ts +29 -8
  212. package/types/src/primitives/verser.d.ts.map +1 -1
  213. package/types/src/router/router-stub.d.ts +3 -0
  214. package/types/src/router/router-stub.d.ts.map +1 -1
  215. package/types/src/serder/native.d.ts +115 -0
  216. package/types/src/serder/native.d.ts.map +1 -0
  217. package/types/src/serder/serder.d.ts +247 -3
  218. package/types/src/serder/serder.d.ts.map +1 -1
  219. package/types/src/serder/serdery.d.ts +42 -0
  220. package/types/src/serder/serdery.d.ts.map +1 -1
  221. package/types/src/serder/smell.d.ts +22 -0
  222. package/types/src/serder/smell.d.ts.map +1 -1
  223. package/types/src/tables/codex-utils.d.ts +7 -0
  224. package/types/src/tables/codex-utils.d.ts.map +1 -0
  225. package/types/src/tables/counter-codex.d.ts +4 -0
  226. package/types/src/tables/counter-codex.d.ts.map +1 -1
  227. package/types/src/tables/counter-groups.d.ts +14 -0
  228. package/types/src/tables/counter-groups.d.ts.map +1 -0
  229. package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
  230. package/types/src/tables/indexer.codex.generated.d.ts +127 -0
  231. package/types/src/tables/indexer.codex.generated.d.ts.map +1 -0
  232. package/types/src/tables/indexer.tables.generated.d.ts +46 -0
  233. package/types/src/tables/indexer.tables.generated.d.ts.map +1 -0
  234. package/types/src/tables/matter.codex.generated.d.ts +797 -0
  235. package/types/src/tables/matter.codex.generated.d.ts.map +1 -0
  236. package/types/src/tables/matter.tables.generated.d.ts +112 -0
  237. package/types/src/tables/matter.tables.generated.d.ts.map +1 -1
  238. package/types/src/tables/table-types.d.ts +13 -0
  239. package/types/src/tables/table-types.d.ts.map +1 -1
  240. package/types/src/tables/trait.codex.generated.d.ts +19 -0
  241. package/types/src/tables/trait.codex.generated.d.ts.map +1 -0
  242. package/types/src/tables/versions.d.ts +6 -0
  243. package/types/src/tables/versions.d.ts.map +1 -1
  244. package/types/src/version.d.ts +5 -2
  245. package/types/src/version.d.ts.map +1 -1
  246. package/esm/src/tables/indexer.tables.js +0 -30
  247. package/types/src/tables/indexer.tables.d.ts +0 -12
  248. 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
 
@@ -61,5 +61,5 @@ tufa annotate --in mystream.cesr --pretty
61
61
 
62
62
  ## License
63
63
 
64
- Licensed under the Apache License, Version 2.0 (`Apache-2.0`). See the
65
- top-level `LICENSE` file in this repository.
64
+ Licensed under the Apache License, Version 2.0 (`Apache-2.0`). See the top-level
65
+ `LICENSE` file in this repository.
@@ -1,4 +1,12 @@
1
1
  import { createParser } from "../core/parser-engine.js";
2
+ /**
3
+ * Adapt an async byte stream into parsed CESR frames.
4
+ *
5
+ * Boundary contract:
6
+ * - chunks are fed in arrival order into one parser instance
7
+ * - emitted parser error events are rethrown as stream errors
8
+ * - yielded values are the historical `CesrMessage` frame payloads only
9
+ */
2
10
  export async function* toAsyncFrames(source, options = {}) {
3
11
  const parser = createParser(options);
4
12
  for await (const chunk of source) {
@@ -1,5 +1,11 @@
1
1
  import { action } from "effection";
2
2
  import { toAsyncFrames } from "./async-iterable.js";
3
+ /**
4
+ * Bridge the async-iterable parser adapter into an Effection operation.
5
+ *
6
+ * This stays intentionally thin so parser semantics continue to live in
7
+ * `toAsyncFrames()` rather than forking behavior in an Effection-only path.
8
+ */
3
9
  export function* toEffectionFrames(source, options = {}) {
4
10
  const frames = yield* action((resolve) => {
5
11
  resolve(toAsyncFrames(source, options));
@@ -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,
@@ -24,19 +25,27 @@ function parsedFramesOrThrow(frames) {
24
25
  }
25
26
  return parsedFrames;
26
27
  }
28
+ /**
29
+ * Parse input and return one annotation structure per emitted frame.
30
+ *
31
+ * Use this when callers want to inspect annotation lines programmatically
32
+ * instead of flattening the whole stream to one rendered string.
33
+ */
27
34
  export function annotateFrames(input, options) {
28
35
  const opts = resolveOptions(options);
29
- const bytes = typeof input === "string"
30
- ? new TextEncoder().encode(input)
31
- : input;
36
+ const bytes = typeof input === "string" ? b(input) : input;
32
37
  const frames = parsedFramesOrThrow(parseBytes(bytes));
33
38
  return renderAnnotatedFrames(frames, opts);
34
39
  }
40
+ /**
41
+ * Render one CESR stream into maintainer-oriented annotated text.
42
+ *
43
+ * Wrapper-aware rendering is attempted first so opaque wrapper payloads can be
44
+ * preserved faithfully before falling back to frame-by-frame annotation.
45
+ */
35
46
  export function annotate(input, options) {
36
47
  const opts = resolveOptions(options);
37
- const bytes = typeof input === "string"
38
- ? new TextEncoder().encode(input)
39
- : input;
48
+ const bytes = typeof input === "string" ? b(input) : input;
40
49
  const wrapperAnnotated = renderWrapperAnnotatedStream(bytes, opts);
41
50
  if (wrapperAnnotated !== null) {
42
51
  return wrapperAnnotated;
@@ -35,6 +35,12 @@ function parseArgs(args) {
35
35
  }
36
36
  return out;
37
37
  }
38
+ /**
39
+ * Read a full `ReadableStream` into one contiguous byte buffer.
40
+ *
41
+ * This is the shared browser/runtime-neutral helper used by annotate CLI
42
+ * shims that expose stdin as a web stream instead of synchronous file APIs.
43
+ */
38
44
  export async function readAllReadable(stream) {
39
45
  const reader = stream.getReader();
40
46
  const chunks = [];
@@ -61,6 +67,12 @@ export async function readAllReadable(stream) {
61
67
  }
62
68
  return out;
63
69
  }
70
+ /**
71
+ * Execute the standalone annotate CLI against the provided IO boundary.
72
+ *
73
+ * Error reporting is intentionally normalized here so runtime-specific entry
74
+ * points can stay as thin bootstraps.
75
+ */
64
76
  export async function annotateCli(args, io) {
65
77
  try {
66
78
  const options = parseArgs(args);
@@ -1,6 +1,6 @@
1
- import { COUNTER_CODE_NAMES_V1, COUNTER_CODE_NAMES_V2, } from "../tables/counter.tables.generated.js";
2
- import { MATTER_CODE_NAMES } from "../tables/matter.tables.generated.js";
3
1
  import { resolveCounterCodeNameTable } from "../tables/counter-version-registry.js";
2
+ import { COUNTER_CODE_NAMES_V1, COUNTER_CODE_NAMES_V2 } from "../tables/counter.tables.generated.js";
3
+ import { MATTER_CODE_NAMES } from "../tables/matter.tables.generated.js";
4
4
  const NATIVE_FIELD_LABELS = Object.freeze({
5
5
  v: "version string",
6
6
  t: "ilk",
@@ -17,6 +17,7 @@ const NATIVE_FIELD_LABELS = Object.freeze({
17
17
  c: "traits/config",
18
18
  a: "anchors/data",
19
19
  });
20
+ /** Resolve a human-readable counter name against either known counter table. */
20
21
  export function counterCodeName(code) {
21
22
  const v2 = COUNTER_CODE_NAMES_V2[code];
22
23
  if (v2)
@@ -26,13 +27,16 @@ export function counterCodeName(code) {
26
27
  return v1;
27
28
  return "Counter";
28
29
  }
30
+ /** Resolve a counter name against the registry selected for one protocol version. */
29
31
  export function counterCodeNameForVersion(code, version) {
30
32
  const table = resolveCounterCodeNameTable(version);
31
33
  return table[code] ?? "Counter";
32
34
  }
35
+ /** Resolve a human-readable matter name from the generated matter code table. */
33
36
  export function matterCodeName(code) {
34
37
  return MATTER_CODE_NAMES[code] ?? "Matter";
35
38
  }
39
+ /** Expand well-known native field labels for annotation comments when available. */
36
40
  export function nativeLabelName(label) {
37
41
  if (!label)
38
42
  return null;
@@ -1,4 +1,10 @@
1
1
  const TEXT_ENCODER = new TextEncoder();
2
+ /**
3
+ * Strip annotation comments/whitespace and rebuild the raw CESR text stream.
4
+ *
5
+ * This is the inverse of maintainer-facing annotation output, not a general
6
+ * parser: comment lines and inline `# ...` commentary are discarded.
7
+ */
2
8
  export function denot(annotated) {
3
9
  const tokens = [];
4
10
  for (const line of annotated.split(/\r?\n/)) {
@@ -1,14 +1,15 @@
1
+ import { b64ToInt, intToB64 } from "../core/bytes.js";
2
+ import { DeserializeError, GroupSizeError, ShortageError, UnknownCodeError } from "../core/errors.js";
1
3
  import { parseBytes } from "../core/parser-engine.js";
2
- import { DeserializeError, GroupSizeError, ShortageError, UnknownCodeError, } from "../core/errors.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";
8
- import { counterCodeName, counterCodeNameForVersion, matterCodeName, nativeLabelName, } from "./comments.js";
9
- import { b64ToInt, intToB64 } from "../core/bytes.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";
11
+ import { counterCodeName, counterCodeNameForVersion, matterCodeName, nativeLabelName } from "./comments.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,7 @@ 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
- }
62
+ /** Parse counters with a bounded v1/v2 fallback for annotation-only rendering paths. */
106
63
  function parseCounterCompat(input, version, domain) {
107
64
  try {
108
65
  return parseCounter(input, version, domain);
@@ -117,6 +74,7 @@ function parseCounterCompat(input, version, domain) {
117
74
  return parseCounter(input, alternate, domain);
118
75
  }
119
76
  }
77
+ /** Decode a wrapped genus-version selector into the active protocol version context. */
120
78
  function decodeVersionCounter(counter) {
121
79
  const triplet = counter.qb64.length >= 3
122
80
  ? counter.qb64.slice(-3)
@@ -128,29 +86,41 @@ function decodeVersionCounter(counter) {
128
86
  minor: minorRaw,
129
87
  };
130
88
  }
89
+ /** Render hydrated group entries recursively using semantic primitive comments. */
131
90
  function renderGroupItems(lines, items, version, indent, options) {
132
91
  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);
92
+ if (isPrimitiveTuple(item)) {
93
+ renderGroupItems(lines, item, version, indent + options.indent, options);
94
+ continue;
95
+ }
96
+ if (isCounterGroupLike(item)) {
97
+ emitLine(lines, item.qb64, `${item.name} nested group`, indent, options);
98
+ renderGroupItems(lines, [...item.items], version, indent + options.indent, options);
99
+ continue;
100
+ }
101
+ if (item instanceof UnknownPrimitive) {
102
+ const value = item.sourceDomain === "bny"
103
+ ? `0x${toHex(item.qb2)}`
104
+ : item.qb64;
105
+ emitLine(lines, value, OPAQUE_WRAPPER_PAYLOAD_COMMENT, indent, options);
139
106
  continue;
140
107
  }
141
- if (item.kind === "qb2") {
142
- emitLine(lines, `0x${toHex(item.qb2)}`, item.opaque ? OPAQUE_WRAPPER_PAYLOAD_COMMENT : "raw qb2 triplet token", indent, options);
108
+ if (item instanceof Counter) {
109
+ emitLine(lines, item.qb64, `${counterCodeNameForVersion(item.code, version)} counter`, indent, options);
143
110
  continue;
144
111
  }
145
- if (item.kind === "tuple") {
146
- renderGroupItems(lines, item.items, version, indent + options.indent, options);
112
+ if (item instanceof Indexer) {
113
+ emitLine(lines, item.qb64, `Indexer ${item.code}`, indent, options);
147
114
  continue;
148
115
  }
149
- if (item.kind === "group") {
150
- emitLine(lines, `${item.code}`, `${item.name} nested group`, indent, options);
116
+ if (item instanceof Matter) {
117
+ emitLine(lines, item.qb64, matterCodeName(item.code), indent, options);
118
+ continue;
151
119
  }
120
+ emitLine(lines, String(item), OPAQUE_TOKEN_COMMENT, indent, options);
152
121
  }
153
122
  }
123
+ /** Render one raw attachment-group span, including nested wrapper groups when present. */
154
124
  function renderAttachmentGroupRaw(lines, raw, version, indent, options) {
155
125
  const domain = asDomain(raw);
156
126
  if (domain !== "txt" && domain !== "bny") {
@@ -204,6 +174,7 @@ function renderAttachmentGroupRaw(lines, raw, version, indent, options) {
204
174
  renderGroupItems(lines, parsed.group.items, version, indent + options.indent, options);
205
175
  return parsed.consumed;
206
176
  }
177
+ /** Render a native CESR body-group payload using hydrated native field projections when available. */
207
178
  function renderNativeBody(lines, frame, options, version) {
208
179
  const raw = frame.body.raw;
209
180
  const domain = asDomain(raw);
@@ -212,18 +183,44 @@ function renderNativeBody(lines, frame, options, version) {
212
183
  }
213
184
  const counter = parseCounterCompat(raw, version, domain);
214
185
  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);
186
+ const unit = domain === "bny" ? 3 : 4;
187
+ const headerSize = domain === "bny" ? counter.fullSizeB2 : counter.fullSize;
188
+ const payloadSize = counter.count * unit;
189
+ const end = Math.min(raw.length, headerSize + payloadSize);
190
+ let offset = headerSize;
191
+ while (offset < end) {
192
+ const slice = raw.slice(offset, end);
193
+ try {
194
+ const nestedCounter = parseCounterCompat(slice, version, domain);
195
+ const nestedCounterSize = domain === "bny"
196
+ ? nestedCounter.fullSizeB2
197
+ : nestedCounter.fullSize;
198
+ emitLine(lines, nestedCounter.qb64, `${counterCodeNameForVersion(nestedCounter.code, version)} counter`, options.indent, options);
199
+ offset += nestedCounterSize;
200
+ continue;
201
+ }
202
+ catch (error) {
203
+ if (!isRecoverableParseError(error)) {
204
+ throw error;
205
+ }
206
+ }
207
+ const matter = parseMatter(slice, domain);
208
+ const matterSize = domain === "bny" ? matter.fullSizeB2 : matter.fullSize;
209
+ const field = frame.body.native?.fields.find((candidate) => candidate.primitive.qb64 === matter.qb64);
210
+ const label = field ? nativeLabelName(field.label) : null;
211
+ const primitiveComment = matter instanceof Indexer
212
+ ? `Indexer ${matter.code}`
213
+ : matterCodeName(matter.code);
214
+ const comment = label ? `${label} (${primitiveComment})` : primitiveComment;
215
+ emitLine(lines, matter.qb64, comment, options.indent, options);
216
+ offset += matterSize;
221
217
  }
222
218
  }
219
+ /** Render non-native or opaque message bodies as one annotated body line. */
223
220
  function renderMessageBody(lines, frame, options) {
224
221
  const rawBody = TEXT_DECODER.decode(frame.body.raw);
225
- const isOpaqueCesrBody = frame.body.kind === "CESR" &&
226
- frame.body.ked === null;
222
+ const isOpaqueCesrBody = frame.body.kind === "CESR"
223
+ && frame.body.ked === null;
227
224
  if (isOpaqueCesrBody) {
228
225
  emitLine(lines, rawBody, `OPAQUE CESR body (non-serder fallback, hex=${toHex(frame.body.raw)})`, 0, options);
229
226
  return;
@@ -249,6 +246,7 @@ function renderMessageBody(lines, frame, options) {
249
246
  ].filter(Boolean).join(" ");
250
247
  emitLine(lines, body, info, 0, options);
251
248
  }
249
+ /** Render one parsed frame and all of its attachment groups into annotation lines. */
252
250
  function renderFrame(frame, index, options) {
253
251
  const lines = [];
254
252
  const version = frame.body.gvrsn ?? frame.body.pvrsn;
@@ -264,6 +262,7 @@ function renderFrame(frame, index, options) {
264
262
  }
265
263
  return { index, frame, lines };
266
264
  }
265
+ /** Parse and render a nested frame chunk inside wrapper-aware annotation flows. */
267
266
  function renderFrameChunk(lines, input, indent, options) {
268
267
  const parsed = parseBytes(input);
269
268
  const frames = [];
@@ -280,6 +279,7 @@ function renderFrameChunk(lines, input, indent, options) {
280
279
  }
281
280
  }
282
281
  }
282
+ /** Walk a stream that may begin with wrapper/version selectors before ordinary frames. */
283
283
  function renderWrapperAwareStream(lines, input, inheritedVersion, indent, options) {
284
284
  let offset = 0;
285
285
  let activeVersion = inheritedVersion;
@@ -1,4 +1,6 @@
1
+ /** Shared UTF-8 encoder used by CESR and DB byte helpers. */
1
2
  export const encoder = new TextEncoder();
3
+ /** Shared UTF-8 decoder used by CESR and DB byte helpers. */
2
4
  export const decoder = new TextDecoder();
3
5
  const B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4
6
  /**
@@ -17,6 +19,7 @@ export function concatBytes(...chunks) {
17
19
  }
18
20
  return out;
19
21
  }
22
+ /** Encode a non-negative integer into CESR's URL-safe base64 sextet text. */
20
23
  export function intToB64(value, length = 1) {
21
24
  if (value < 0) {
22
25
  throw new Error(`value must be >= 0, got ${value}`);
@@ -32,6 +35,7 @@ export function intToB64(value, length = 1) {
32
35
  }
33
36
  return out.padStart(length, "A");
34
37
  }
38
+ /** Decode CESR URL-safe base64 sextet text into a non-negative integer. */
35
39
  export function b64ToInt(text) {
36
40
  let out = 0;
37
41
  for (const ch of text) {
@@ -43,10 +47,12 @@ export function b64ToInt(text) {
43
47
  }
44
48
  return out;
45
49
  }
50
+ /** Encode raw bytes to unpadded URL-safe base64 text. */
46
51
  export function encodeB64(data) {
47
52
  const str = btoa(String.fromCharCode(...data));
48
53
  return str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
49
54
  }
55
+ /** Decode unpadded URL-safe base64 text into raw bytes. */
50
56
  export function decodeB64(text) {
51
57
  const padded = text + "===".slice((text.length + 3) % 4);
52
58
  const raw = atob(padded.replace(/-/g, "+").replace(/_/g, "/"));
@@ -56,14 +62,20 @@ export function decodeB64(text) {
56
62
  }
57
63
  return out;
58
64
  }
65
+ /**
66
+ * Symmetric ceiling function
67
+ * @param value
68
+ */
59
69
  export function sceil(value) {
60
70
  return Math.ceil(value);
61
71
  }
72
+ /** Convert a CESR code string to the exact leading qb2 bytes that carry it. */
62
73
  export function codeB64ToB2(text) {
63
74
  const n = sceil((text.length * 3) / 4);
64
75
  const full = text + "A".repeat((4 - (text.length % 4)) % 4);
65
76
  return decodeB64(full).slice(0, n);
66
77
  }
78
+ /** Convert leading qb2 bytes into the requested number of CESR sextets. */
67
79
  export function codeB2ToB64(bytes, sextets) {
68
80
  const n = sceil((sextets * 3) / 4);
69
81
  if (n > bytes.length) {
@@ -71,6 +83,50 @@ export function codeB2ToB64(bytes, sextets) {
71
83
  }
72
84
  return encodeB64(bytes.slice(0, n)).slice(0, sextets);
73
85
  }
86
+ /** Re-slice the first `sextets` worth of qb2 bytes for parser/code probing. */
74
87
  export function nabSextets(bytes, sextets) {
75
88
  return codeB64ToB2(codeB2ToB64(bytes, sextets));
76
89
  }
90
+ /** UTF-8 string -> bytes helper. */
91
+ export const b = (t) => encoder.encode(t);
92
+ /** UTF-8 bytes -> string helper. */
93
+ export const t = (b) => decoder.decode(b);
94
+ /**
95
+ * Normalize lmdb-js key/value payloads to `Uint8Array`.
96
+ * Example: converts Node `Buffer`-like values from `getRange()`.
97
+ */
98
+ export function toBytes(value) {
99
+ if (value instanceof Uint8Array) {
100
+ return value;
101
+ }
102
+ return new Uint8Array(value);
103
+ }
104
+ /**
105
+ * Format number as 32-char hex string, zero padded.
106
+ * @param num
107
+ */
108
+ export const to32CharHex = (num) => {
109
+ return num
110
+ .toString(16)
111
+ .padStart(32, "0");
112
+ };
113
+ /** Constant-time-ish byte equality for small DB keys/values. */
114
+ export function bytesEqual(left, right) {
115
+ if (left.length !== right.length) {
116
+ return false;
117
+ }
118
+ for (let i = 0; i < left.length; i++) {
119
+ if (left[i] !== right[i]) {
120
+ return false;
121
+ }
122
+ }
123
+ return true;
124
+ }
125
+ /** Stable hex fingerprint used for set-membership dedupe of binary values. */
126
+ export function bytesHex(value) {
127
+ // deno-fmt-ignore
128
+ return Array.from(value)
129
+ .map((byte) => byte.toString(16)
130
+ .padStart(2, "0"))
131
+ .join("");
132
+ }
@@ -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,16 +41,24 @@ 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
  }
59
+ /** A CESR payload could not be serialized into a valid protocol representation. */
60
+ export class SerializeError extends ParserError {
61
+ }
46
62
  /** Failure while constructing syntax artifacts from token bytes. */
47
63
  export class SyntaxParseError extends ParserError {
48
64
  constructor(message, cause, offset, context) {