cesr-ts 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/esm/src/adapters/async-iterable.js +8 -0
  2. package/esm/src/adapters/effection.js +6 -0
  3. package/esm/src/annotate/annotator.js +12 -0
  4. package/esm/src/annotate/cli.js +12 -0
  5. package/esm/src/annotate/comments.js +5 -1
  6. package/esm/src/annotate/denot.js +6 -0
  7. package/esm/src/annotate/render.js +15 -3
  8. package/esm/src/core/bytes.js +9 -0
  9. package/esm/src/core/errors.js +3 -0
  10. package/esm/src/core/parser-engine.js +1 -1
  11. package/esm/src/core/parser-frame-parser.js +22 -18
  12. package/esm/src/core/vocabulary.js +47 -0
  13. package/esm/src/index.js +5 -0
  14. package/esm/src/parser/attachment-parser.js +4 -0
  15. package/esm/src/parser/cold-start.js +6 -0
  16. package/esm/src/parser/group-dispatch.js +2 -2
  17. package/esm/src/primitives/aggor.js +374 -30
  18. package/esm/src/primitives/bexter.js +2 -3
  19. package/esm/src/primitives/blinder.js +6 -0
  20. package/esm/src/primitives/byte-like.js +19 -0
  21. package/esm/src/primitives/cigar.js +17 -3
  22. package/esm/src/primitives/cipher.js +91 -6
  23. package/esm/src/primitives/codex.js +39 -6
  24. package/esm/src/primitives/compactor.js +307 -3
  25. package/esm/src/primitives/counter.js +6 -2
  26. package/esm/src/primitives/dater.js +1 -2
  27. package/esm/src/primitives/decimer.js +7 -11
  28. package/esm/src/primitives/decrypter.js +123 -7
  29. package/esm/src/primitives/diger.js +57 -3
  30. package/esm/src/primitives/disclosure.js +300 -0
  31. package/esm/src/primitives/encrypter.js +130 -7
  32. package/esm/src/primitives/hydrate.js +49 -0
  33. package/esm/src/primitives/ilker.js +2 -3
  34. package/esm/src/primitives/indexer.js +21 -4
  35. package/esm/src/primitives/labeler.js +1 -2
  36. package/esm/src/primitives/mapper.js +612 -35
  37. package/esm/src/primitives/matter.js +133 -3
  38. package/esm/src/primitives/mediar.js +6 -0
  39. package/esm/src/primitives/noncer.js +2 -3
  40. package/esm/src/primitives/number.js +1 -2
  41. package/esm/src/primitives/pather.js +102 -5
  42. package/esm/src/primitives/prefixer.js +1 -2
  43. package/esm/src/primitives/primitive.js +14 -0
  44. package/esm/src/primitives/registry.js +2 -0
  45. package/esm/src/primitives/saider.js +80 -3
  46. package/esm/src/primitives/salter.js +96 -2
  47. package/esm/src/primitives/sealed-box.js +41 -0
  48. package/esm/src/primitives/seqner.js +1 -2
  49. package/esm/src/primitives/siger.js +58 -3
  50. package/esm/src/primitives/signer.js +243 -9
  51. package/esm/src/primitives/streamer.js +28 -0
  52. package/esm/src/primitives/structing.js +727 -0
  53. package/esm/src/primitives/structor.js +20 -0
  54. package/esm/src/primitives/tagger.js +1 -2
  55. package/esm/src/primitives/texter.js +1 -2
  56. package/esm/src/primitives/tholder.js +552 -10
  57. package/esm/src/primitives/traitor.js +2 -3
  58. package/esm/src/primitives/verfer.js +50 -6
  59. package/esm/src/primitives/verser.js +16 -6
  60. package/esm/src/router/router-stub.js +1 -0
  61. package/esm/src/serder/native.js +1326 -0
  62. package/esm/src/serder/serder.js +1558 -59
  63. package/esm/src/serder/serdery.js +92 -7
  64. package/esm/src/serder/smell.js +26 -1
  65. package/esm/src/tables/codex-utils.js +2 -0
  66. package/esm/src/tables/counter-codex.js +2 -0
  67. package/esm/src/tables/counter-groups.js +6 -0
  68. package/esm/src/tables/versions.js +4 -0
  69. package/esm/src/version.js +5 -2
  70. package/package.json +5 -2
  71. package/types/src/adapters/async-iterable.d.ts +8 -0
  72. package/types/src/adapters/async-iterable.d.ts.map +1 -1
  73. package/types/src/adapters/effection.d.ts +7 -0
  74. package/types/src/adapters/effection.d.ts.map +1 -1
  75. package/types/src/annotate/annotator.d.ts +12 -0
  76. package/types/src/annotate/annotator.d.ts.map +1 -1
  77. package/types/src/annotate/cli.d.ts +13 -0
  78. package/types/src/annotate/cli.d.ts.map +1 -1
  79. package/types/src/annotate/comments.d.ts +4 -0
  80. package/types/src/annotate/comments.d.ts.map +1 -1
  81. package/types/src/annotate/denot.d.ts +6 -0
  82. package/types/src/annotate/denot.d.ts.map +1 -1
  83. package/types/src/annotate/render.d.ts.map +1 -1
  84. package/types/src/annotate/types.d.ts +2 -0
  85. package/types/src/annotate/types.d.ts.map +1 -1
  86. package/types/src/bench/parser-benchmark.d.ts +3 -0
  87. package/types/src/bench/parser-benchmark.d.ts.map +1 -1
  88. package/types/src/core/bytes.d.ts +9 -0
  89. package/types/src/core/bytes.d.ts.map +1 -1
  90. package/types/src/core/errors.d.ts +3 -0
  91. package/types/src/core/errors.d.ts.map +1 -1
  92. package/types/src/core/parser-engine.d.ts +4 -0
  93. package/types/src/core/parser-engine.d.ts.map +1 -1
  94. package/types/src/core/parser-frame-parser.d.ts.map +1 -1
  95. package/types/src/core/types.d.ts +2 -0
  96. package/types/src/core/types.d.ts.map +1 -1
  97. package/types/src/core/vocabulary.d.ts +48 -0
  98. package/types/src/core/vocabulary.d.ts.map +1 -0
  99. package/types/src/index.d.ts +5 -0
  100. package/types/src/index.d.ts.map +1 -1
  101. package/types/src/parser/attachment-parser.d.ts +4 -0
  102. package/types/src/parser/attachment-parser.d.ts.map +1 -1
  103. package/types/src/parser/cold-start.d.ts +6 -0
  104. package/types/src/parser/cold-start.d.ts.map +1 -1
  105. package/types/src/parser/group-dispatch.d.ts.map +1 -1
  106. package/types/src/primitives/aggor.d.ts +100 -13
  107. package/types/src/primitives/aggor.d.ts.map +1 -1
  108. package/types/src/primitives/bexter.d.ts.map +1 -1
  109. package/types/src/primitives/blinder.d.ts +6 -0
  110. package/types/src/primitives/blinder.d.ts.map +1 -1
  111. package/types/src/primitives/byte-like.d.ts +7 -0
  112. package/types/src/primitives/byte-like.d.ts.map +1 -0
  113. package/types/src/primitives/cigar.d.ts +11 -1
  114. package/types/src/primitives/cigar.d.ts.map +1 -1
  115. package/types/src/primitives/cipher.d.ts +73 -2
  116. package/types/src/primitives/cipher.d.ts.map +1 -1
  117. package/types/src/primitives/codex.d.ts +34 -4
  118. package/types/src/primitives/codex.d.ts.map +1 -1
  119. package/types/src/primitives/compactor.d.ts +94 -2
  120. package/types/src/primitives/compactor.d.ts.map +1 -1
  121. package/types/src/primitives/counter.d.ts.map +1 -1
  122. package/types/src/primitives/dater.d.ts.map +1 -1
  123. package/types/src/primitives/decimer.d.ts.map +1 -1
  124. package/types/src/primitives/decrypter.d.ts +64 -3
  125. package/types/src/primitives/decrypter.d.ts.map +1 -1
  126. package/types/src/primitives/diger.d.ts +16 -0
  127. package/types/src/primitives/diger.d.ts.map +1 -1
  128. package/types/src/primitives/disclosure.d.ts +201 -0
  129. package/types/src/primitives/disclosure.d.ts.map +1 -0
  130. package/types/src/primitives/encrypter.d.ts +66 -4
  131. package/types/src/primitives/encrypter.d.ts.map +1 -1
  132. package/types/src/primitives/hydrate.d.ts +20 -0
  133. package/types/src/primitives/hydrate.d.ts.map +1 -0
  134. package/types/src/primitives/ilker.d.ts.map +1 -1
  135. package/types/src/primitives/indexer.d.ts.map +1 -1
  136. package/types/src/primitives/labeler.d.ts.map +1 -1
  137. package/types/src/primitives/mapper.d.ts +132 -11
  138. package/types/src/primitives/mapper.d.ts.map +1 -1
  139. package/types/src/primitives/matter.d.ts +33 -1
  140. package/types/src/primitives/matter.d.ts.map +1 -1
  141. package/types/src/primitives/mediar.d.ts +6 -0
  142. package/types/src/primitives/mediar.d.ts.map +1 -1
  143. package/types/src/primitives/noncer.d.ts.map +1 -1
  144. package/types/src/primitives/number.d.ts.map +1 -1
  145. package/types/src/primitives/pather.d.ts +21 -0
  146. package/types/src/primitives/pather.d.ts.map +1 -1
  147. package/types/src/primitives/prefixer.d.ts.map +1 -1
  148. package/types/src/primitives/primitive.d.ts +26 -0
  149. package/types/src/primitives/primitive.d.ts.map +1 -1
  150. package/types/src/primitives/registry.d.ts +3 -0
  151. package/types/src/primitives/registry.d.ts.map +1 -1
  152. package/types/src/primitives/saider.d.ts +41 -0
  153. package/types/src/primitives/saider.d.ts.map +1 -1
  154. package/types/src/primitives/salter.d.ts +56 -1
  155. package/types/src/primitives/salter.d.ts.map +1 -1
  156. package/types/src/primitives/sealed-box.d.ts +29 -0
  157. package/types/src/primitives/sealed-box.d.ts.map +1 -0
  158. package/types/src/primitives/seqner.d.ts.map +1 -1
  159. package/types/src/primitives/siger.d.ts +14 -0
  160. package/types/src/primitives/siger.d.ts.map +1 -1
  161. package/types/src/primitives/signer.d.ts +80 -4
  162. package/types/src/primitives/signer.d.ts.map +1 -1
  163. package/types/src/primitives/streamer.d.ts +31 -0
  164. package/types/src/primitives/streamer.d.ts.map +1 -0
  165. package/types/src/primitives/structing.d.ts +4114 -0
  166. package/types/src/primitives/structing.d.ts.map +1 -0
  167. package/types/src/primitives/structor.d.ts +6 -0
  168. package/types/src/primitives/structor.d.ts.map +1 -1
  169. package/types/src/primitives/tagger.d.ts.map +1 -1
  170. package/types/src/primitives/texter.d.ts.map +1 -1
  171. package/types/src/primitives/tholder.d.ts +150 -5
  172. package/types/src/primitives/tholder.d.ts.map +1 -1
  173. package/types/src/primitives/traitor.d.ts.map +1 -1
  174. package/types/src/primitives/unknown.d.ts +1 -0
  175. package/types/src/primitives/unknown.d.ts.map +1 -1
  176. package/types/src/primitives/verfer.d.ts +12 -0
  177. package/types/src/primitives/verfer.d.ts.map +1 -1
  178. package/types/src/primitives/verser.d.ts +14 -3
  179. package/types/src/primitives/verser.d.ts.map +1 -1
  180. package/types/src/router/router-stub.d.ts +3 -0
  181. package/types/src/router/router-stub.d.ts.map +1 -1
  182. package/types/src/serder/native.d.ts +115 -0
  183. package/types/src/serder/native.d.ts.map +1 -0
  184. package/types/src/serder/serder.d.ts +187 -9
  185. package/types/src/serder/serder.d.ts.map +1 -1
  186. package/types/src/serder/serdery.d.ts +42 -0
  187. package/types/src/serder/serdery.d.ts.map +1 -1
  188. package/types/src/serder/smell.d.ts +22 -0
  189. package/types/src/serder/smell.d.ts.map +1 -1
  190. package/types/src/tables/codex-utils.d.ts +3 -0
  191. package/types/src/tables/codex-utils.d.ts.map +1 -1
  192. package/types/src/tables/counter-codex.d.ts +4 -0
  193. package/types/src/tables/counter-codex.d.ts.map +1 -1
  194. package/types/src/tables/counter-groups.d.ts +6 -0
  195. package/types/src/tables/counter-groups.d.ts.map +1 -1
  196. package/types/src/tables/counter.tables.generated.d.ts.map +1 -1
  197. package/types/src/tables/indexer.codex.generated.d.ts.map +1 -1
  198. package/types/src/tables/matter.codex.generated.d.ts.map +1 -1
  199. package/types/src/tables/table-types.d.ts +6 -0
  200. package/types/src/tables/table-types.d.ts.map +1 -1
  201. package/types/src/tables/versions.d.ts +6 -0
  202. package/types/src/tables/versions.d.ts.map +1 -1
  203. package/types/src/version.d.ts +5 -2
  204. package/types/src/version.d.ts.map +1 -1
@@ -1,16 +1,29 @@
1
1
  import { codexValues, invertCodex } from "../tables/codex-utils.js";
2
- import { IdrDex, IdxBthSigDex, IdxCrtSigDex, IdxSigDex, } from "../tables/indexer.codex.generated.js";
2
+ import { IdrDex, IdxBthSigDex, IdxCrtSigDex, IdxSigDex } from "../tables/indexer.codex.generated.js";
3
3
  import { BexDex, CiXAllQB64Dex, CiXDex, CiXFixQB64Dex, CiXVarDex, CiXVarQB2Dex, CiXVarQB64Dex, CiXVarStrmDex, DecDex, DigDex, EscapeDex, LabelDex, MtrDex, NonceDex, NonTransDex, NumDex, PreDex, PreNonDigDex, TagDex, TexDex, } from "../tables/matter.codex.generated.js";
4
4
  import { TraitDex } from "../tables/trait.codex.generated.js";
5
5
  import { Protocols } from "../tables/versions.js";
6
6
  /**
7
7
  * Derived readability layer over the generated KERIpy-parity codex objects.
8
8
  *
9
- * Canonical names such as `MtrDex`, `PreDex`, `DigDex`, and `IdrDex` are the
10
- * primary source of truth. The sets exported here are convenience views for
11
- * primitive-family validation and TS ergonomics.
9
+ * Canonical names such as `MtrDex` and `IdrDex` are the primary source of
10
+ * truth for the shared Matter/Indexer code spaces. Semantic families such as
11
+ * `PreDex`, `DigDex`, `NonceDex`, and `IdxSigDex` are KERIpy-style subset
12
+ * views over those same base codices, not separate versioned registries.
13
+ *
14
+ * The sets exported here are convenience views for primitive-family validation
15
+ * and TS ergonomics. Counter codices are the separate genus/version-aware
16
+ * layer; Matter and Indexer family subsets are not.
12
17
  */
13
18
  export { BexDex, CiXAllQB64Dex, CiXDex, CiXFixQB64Dex, CiXVarDex, CiXVarQB2Dex, CiXVarQB64Dex, CiXVarStrmDex, DecDex, DigDex, EscapeDex, IdrDex, IdxBthSigDex, IdxCrtSigDex, IdxSigDex, LabelDex, MtrDex, NonceDex, NonTransDex, NumDex, PreDex, PreNonDigDex, TagDex, TexDex, TraitDex, };
19
+ /**
20
+ * Derived matter/indexer family views used by semantic primitive validators.
21
+ *
22
+ * Maintainer rule:
23
+ * - generated codex objects such as `MtrDex`, `PreDex`, and `IdrDex` remain
24
+ * the authority
25
+ * - these exported sets are readability helpers layered over that authority
26
+ */
14
27
  export const BEXTER_CODES = codexValues(BexDex);
15
28
  export const TEXTER_CODES = codexValues(TexDex);
16
29
  export const DECIMAL_CODES = codexValues(DecDex);
@@ -20,9 +33,18 @@ export const NUMBER_CODES = codexValues(NumDex);
20
33
  export const TAG_CODES = codexValues(TagDex);
21
34
  export const LABELER_CODES = codexValues(LabelDex);
22
35
  export const PREFIX_CODES = codexValues(PreDex);
23
- export const NON_TRANSFERABLE_PREFIX_CODES = codexValues(NonTransDex);
36
+ export const NON_TRANSFERABLE_CODES = codexValues(NonTransDex);
24
37
  export const NON_DIGEST_PREFIX_CODES = codexValues(PreNonDigDex);
25
38
  export const ESCAPE_CODES = codexValues(EscapeDex);
39
+ /**
40
+ * X25519 cipher-family helper sets keyed by plaintext hydration rules.
41
+ *
42
+ * KERIpy correspondence:
43
+ * - stream families preserve whole sniffable CESR streams
44
+ * - qb64 families preserve fully qualified text material
45
+ * - qb2 families preserve binary CESR encodings
46
+ * - fixed qb64 families name the common salt/seed payloads directly
47
+ */
26
48
  export const CIPHER_X25519_VARIABLE_STREAM_CODES = codexValues(CiXVarStrmDex);
27
49
  export const CIPHER_X25519_QB64_VARIABLE_CODES = codexValues(CiXVarQB64Dex);
28
50
  export const CIPHER_X25519_FIXED_QB64_CODES = codexValues(CiXFixQB64Dex);
@@ -45,6 +67,13 @@ export const THOLDER_CODES = new Set([
45
67
  ...THOLDER_NUMERIC_CODES,
46
68
  ...THOLDER_WEIGHTED_CODES,
47
69
  ]);
70
+ /**
71
+ * Executable primitive suite families.
72
+ *
73
+ * These are the small semantic validation sets consumed by `Signer`,
74
+ * `Salter`, `Encrypter`, `Decrypter`, and detached-signature primitives, not
75
+ * alternative registries separate from the generated KERIpy parity codices.
76
+ */
48
77
  export const SIGNER_CODES = new Set([
49
78
  MtrDex.Ed25519_Seed,
50
79
  MtrDex.ECDSA_256k1_Seed,
@@ -63,8 +92,12 @@ export const TRAIT_TAGS = new Set([
63
92
  ...codexValues(TraitDex),
64
93
  ]);
65
94
  export const VERSER_CODES = new Set([MtrDex.Tag7, MtrDex.Tag10]);
66
- export const VERSER_PROTOCOLS = new Set(Object.values(Protocols));
95
+ export const VERSER_PROTOCOLS = new Set([
96
+ ...Object.values(Protocols),
97
+ "OCSR",
98
+ ]);
67
99
  const MATTER_CODEX_NAMES = invertCodex(MtrDex);
100
+ /** Project one matter code back to its generated codex member name. */
68
101
  export function matterCodexName(code) {
69
102
  return MATTER_CODEX_NAMES.get(code);
70
103
  }
@@ -1,10 +1,314 @@
1
- import { UnknownCodeError } from "../core/errors.js";
1
+ import { DeserializeError, UnknownCodeError } from "../core/errors.js";
2
2
  import { COMPACTOR_CODES } from "../tables/counter-groups.js";
3
- import { parseMapperBody } from "./mapper.js";
3
+ import { Mapper, parseMapperBody } from "./mapper.js";
4
+ /**
5
+ * CESR-native compactable map primitive.
6
+ *
7
+ * Maintainer mental model:
8
+ * a `Compactor` is a `Mapper` plus the tree-walking utilities needed to turn
9
+ * nested saidive maps into their "most compact" branch form and then re-expand
10
+ * them into readable partial-disclosure variants. `leaves` records the saidive
11
+ * map nodes discovered during tracing; `partials` records staged re-expansions.
12
+ */
13
+ export class Compactor extends Mapper {
14
+ /**
15
+ * Construct one compactable map primitive.
16
+ *
17
+ * If `compactify=true` and the input starts from semantic `mad`, the
18
+ * constructor eagerly runs the compact + expand lifecycle so callers
19
+ * immediately get both the compact current state and readable partials.
20
+ */
21
+ constructor(init = {}) {
22
+ super({
23
+ ...init,
24
+ saidive: init.saidive ?? true,
25
+ verify: init.verify ?? true,
26
+ });
27
+ /** Leaf-path index populated by `trace()`: dotted path -> saidified leaf mapper. */
28
+ Object.defineProperty(this, "leaves", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ /** Partially re-expanded disclosure variants built by `expand()`. */
35
+ Object.defineProperty(this, "partials", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: void 0
40
+ });
41
+ if (!COMPACTOR_CODES.has(this.code)) {
42
+ throw new UnknownCodeError(`Expected map compactor group code, got ${this.code}`);
43
+ }
44
+ this.leaves = {};
45
+ this.partials = null;
46
+ if (init.mad && (init.compactify ?? false)) {
47
+ this.compact();
48
+ this.expand();
49
+ }
50
+ }
51
+ /** Primary SAID of the current map state, if its configured saidive label is present. */
52
+ get said() {
53
+ const label = Object.keys(this.saids)[0];
54
+ const value = this._mad[label];
55
+ return typeof value === "string" ? value : null;
56
+ }
57
+ /**
58
+ * True when the current map is fully compacted.
59
+ *
60
+ * `true` means the root map itself is a leaf and its own saidive value is the
61
+ * compact representation. `false` means there are leaves but at least one
62
+ * branch is still expanded. `null` means the map tree has no saidive leaves.
63
+ */
64
+ get iscompact() {
65
+ const paths = Object.keys(this.leaves);
66
+ if (paths.length === 0) {
67
+ return null;
68
+ }
69
+ return this.said !== null && "" in this.leaves;
70
+ }
71
+ /** Return the nested tail value located at dotted `path`, or `null` when absent. */
72
+ getTail(path, mad = null) {
73
+ let tail = mad ?? this._mad;
74
+ const parts = path.split(".").slice(1);
75
+ for (const part of parts) {
76
+ if (!tail || typeof tail !== "object" || Array.isArray(tail)
77
+ || !(part in tail)) {
78
+ return null;
79
+ }
80
+ tail = tail[part];
81
+ }
82
+ return tail;
83
+ }
84
+ /**
85
+ * Return the enclosing map and tail label for dotted `path`.
86
+ *
87
+ * Example:
88
+ * path `.a.address` => returns the map stored at `.a` plus tail `"address"`.
89
+ * path `` (top level) => returns `[null, ""]`.
90
+ */
91
+ getMad(path, mad = null) {
92
+ let current = mad ?? this._mad;
93
+ const parts = path.split(".");
94
+ const tail = parts.at(-1) ?? null;
95
+ const parents = parts.slice(1, -1);
96
+ if (parts.length <= 1) {
97
+ return [null, tail];
98
+ }
99
+ for (const part of parents) {
100
+ const next = current[part];
101
+ if (!next || typeof next !== "object" || Array.isArray(next)) {
102
+ return [null, tail];
103
+ }
104
+ current = next;
105
+ }
106
+ if (tail !== null && tail !== "" && !(tail in current)) {
107
+ return [null, null];
108
+ }
109
+ return [current, tail];
110
+ }
111
+ /**
112
+ * Walk the nested map tree and index every saidive leaf by dotted path.
113
+ *
114
+ * When `saidify=true`, each leaf map is first rebuilt as a `Mapper` with
115
+ * `makify=true` so its leaf SAID is computed before the path is recorded.
116
+ */
117
+ trace(saidify = false) {
118
+ this.leaves = {};
119
+ const paths = this._trace(this._mad, [], "", saidify);
120
+ if (saidify && this.iscompact === false) {
121
+ // When nested leaves have just been saidified, the top-level map's own
122
+ // SAID must also be recomputed over that updated structure.
123
+ const top = Mapper.fromSad(this._mad, {
124
+ strict: this.strict,
125
+ saidive: true,
126
+ saids: this.saids,
127
+ kind: this.kind,
128
+ makify: true,
129
+ verify: false,
130
+ });
131
+ this._mad = top.mad;
132
+ this.raw = top.raw;
133
+ this.fields = top.fields;
134
+ this.code = top.code;
135
+ this.count = top.count;
136
+ this.fullSize = top.fullSize;
137
+ this.fullSizeB2 = top.fullSizeB2;
138
+ this.totalSize = top.totalSize;
139
+ this.totalSizeB2 = top.totalSizeB2;
140
+ }
141
+ return paths;
142
+ }
143
+ /** Apply the most-compact branch reduction recursively until the root is compact or no leaves exist. */
144
+ compact() {
145
+ while (true) {
146
+ const paths = this.trace(true);
147
+ for (const path of paths) {
148
+ const leafer = this.leaves[path];
149
+ const [mad, tail] = this.getMad(path);
150
+ if (mad && tail) {
151
+ // Replacing a nested leaf map with its SAID is the actual "compaction"
152
+ // step. `trace(true)` did the saidification; this step does the branch
153
+ // contraction.
154
+ mad[tail] = leafer.said;
155
+ }
156
+ }
157
+ if (paths.length === 0 || this.iscompact) {
158
+ break;
159
+ }
160
+ }
161
+ const rebuilt = Mapper.fromSad(this._mad, {
162
+ strict: this.strict,
163
+ saidive: true,
164
+ saids: this.saids,
165
+ kind: this.kind,
166
+ verify: false,
167
+ });
168
+ this._mad = rebuilt.mad;
169
+ this.raw = rebuilt.raw;
170
+ this.fields = rebuilt.fields;
171
+ this.code = rebuilt.code;
172
+ this.count = rebuilt.count;
173
+ this.fullSize = rebuilt.fullSize;
174
+ this.fullSizeB2 = rebuilt.fullSizeB2;
175
+ this.totalSize = rebuilt.totalSize;
176
+ this.totalSizeB2 = rebuilt.totalSizeB2;
177
+ }
178
+ /**
179
+ * Build readable partial-disclosure variants by progressively re-expanding compact leaves.
180
+ *
181
+ * `greedy=true` mirrors KERIpy’s preferred order: expand deeper leaves first
182
+ * so each staged variant reveals as much structure as possible per step.
183
+ */
184
+ expand(greedy = true) {
185
+ this.partials = {};
186
+ let paths = Object.keys(this.leaves);
187
+ if (greedy) {
188
+ paths = [...paths].reverse();
189
+ }
190
+ const used = [];
191
+ if (paths.includes("")) {
192
+ const top = this.leaves[""];
193
+ const partial = new Compactor({
194
+ mad: top.mad,
195
+ verify: false,
196
+ saidive: false,
197
+ strict: this.strict,
198
+ kind: this.kind,
199
+ });
200
+ partial.trace(false);
201
+ this.partials[""] = partial;
202
+ used.push("");
203
+ }
204
+ const pmad = this.mad;
205
+ while (true) {
206
+ const unused = paths.filter((path) => !used.includes(path));
207
+ if (unused.length === 0) {
208
+ break;
209
+ }
210
+ let created = false;
211
+ for (const path of unused) {
212
+ const [mad, tail] = this.getMad(path, pmad);
213
+ if (!mad || !tail) {
214
+ continue;
215
+ }
216
+ // Replacing one compact SAID with the full leaf map is the inverse
217
+ // operation of `compact()`. Repeating this step builds a staircase of
218
+ // disclosure variants from compact to expanded.
219
+ mad[tail] = this.leaves[path].mad;
220
+ used.push(path);
221
+ created = true;
222
+ }
223
+ if (created) {
224
+ const partial = new Compactor({
225
+ mad: pmad,
226
+ verify: false,
227
+ saidive: false,
228
+ strict: this.strict,
229
+ kind: this.kind,
230
+ });
231
+ const index = partial.trace(false);
232
+ this.partials[index.join("|")] = partial;
233
+ }
234
+ else {
235
+ break;
236
+ }
237
+ }
238
+ }
239
+ _trace(mad, paths, path, saidify) {
240
+ // A map is a leaf when it has one of the configured saidive labels at this
241
+ // level and no descendant map with its own saidive field. That is the key
242
+ // mental model for every compact/disclose operation in this class.
243
+ let isLeaf = Object.keys(this.saids).some((label) => label in mad);
244
+ for (const [label, value] of Object.entries(mad)) {
245
+ if (value && typeof value === "object" && !Array.isArray(value)) {
246
+ if (label in this.saids) {
247
+ throw new DeserializeError(`Got nested map in said field label=${label}`);
248
+ }
249
+ if (this._hasSaid(value)) {
250
+ isLeaf = false;
251
+ this._trace(value, paths, `${path}.${label}`, saidify);
252
+ }
253
+ }
254
+ }
255
+ if (isLeaf) {
256
+ paths.push(path);
257
+ // Each leaf is normalized through plain `Mapper` so `Compactor` stays
258
+ // focused on tree traversal and branch replacement rather than duplicating
259
+ // base map serialization logic.
260
+ const leafer = Mapper.fromSad(mad, {
261
+ strict: this.strict,
262
+ saidive: true,
263
+ saids: this.saids,
264
+ kind: this.kind,
265
+ makify: true,
266
+ verify: false,
267
+ });
268
+ if (saidify) {
269
+ for (const label of Object.keys(this.saids)) {
270
+ if (label in mad && leafer.mad[label] !== undefined) {
271
+ mad[label] = leafer.mad[label];
272
+ }
273
+ }
274
+ }
275
+ this.leaves[path] = leafer;
276
+ }
277
+ return paths;
278
+ }
279
+ _hasSaid(mad) {
280
+ // This recursive predicate is deliberately separate from `_trace()` because
281
+ // the leaf test is the hardest part of compactor reasoning; keeping it in
282
+ // one helper makes future parity changes less error-prone.
283
+ for (const [label, value] of Object.entries(mad)) {
284
+ if (label in this.saids) {
285
+ return true;
286
+ }
287
+ if (value && typeof value === "object" && !Array.isArray(value)
288
+ && this._hasSaid(value)) {
289
+ return true;
290
+ }
291
+ }
292
+ return false;
293
+ }
294
+ }
295
+ /**
296
+ * Parse one CESR-native map body/group and hydrate it as a `Compactor`.
297
+ *
298
+ * Example:
299
+ * a native section like `-GAB0J_dE...0J_x-GA...` becomes a `Compactor` whose
300
+ * `.mad` is the readable nested object and whose `.qb64` is the exact native
301
+ * map-group bytes used on the wire.
302
+ */
4
303
  export function parseCompactor(input, version, cold) {
5
304
  const mapper = parseMapperBody(input, version, cold);
6
305
  if (!COMPACTOR_CODES.has(mapper.code)) {
7
306
  throw new UnknownCodeError(`Expected map compactor group code, got ${mapper.code}`);
8
307
  }
9
- return mapper;
308
+ return new Compactor({
309
+ ...(cold === "txt" ? { raw: mapper.raw } : { qb2: input }),
310
+ version,
311
+ kind: "CESR",
312
+ verify: false,
313
+ });
10
314
  }
@@ -1,6 +1,6 @@
1
- import { b, b64ToInt, codeB2ToB64, codeB64ToB2, intToB64, nabSextets, sceil, } from "../core/bytes.js";
1
+ import { b, b64ToInt, codeB2ToB64, codeB64ToB2, intToB64, nabSextets, sceil } from "../core/bytes.js";
2
2
  import { DeserializeError, ShortageError, UnknownCodeError } from "../core/errors.js";
3
- import { resolveCounterCodeNameTable, resolveCounterSizeTable, } from "../tables/counter-version-registry.js";
3
+ import { resolveCounterCodeNameTable, resolveCounterSizeTable } from "../tables/counter-version-registry.js";
4
4
  import { COUNTER_HARDS } from "../tables/counter.tables.generated.js";
5
5
  const COUNTER_BARDS = new Map([...COUNTER_HARDS.entries()].map(([code, hs]) => [
6
6
  String.fromCharCode(codeB64ToB2(code)[0])
@@ -18,6 +18,7 @@ function getTables(version) {
18
18
  const nameTable = resolveCounterCodeNameTable(version);
19
19
  return { sizeTable, nameTable };
20
20
  }
21
+ /** Inhale one text-domain counter token using the version-selected counter tables. */
21
22
  function parseCounterFromTextData(input, version) {
22
23
  const txt = String.fromCharCode(...input);
23
24
  if (txt.length === 0) {
@@ -62,6 +63,7 @@ function parseCounterFromTextData(input, version) {
62
63
  version,
63
64
  };
64
65
  }
66
+ /** Inhale one qb2 counter token into the canonical text-oriented counter shape. */
65
67
  function parseCounterFromBinaryData(input, version) {
66
68
  if (input.length < 2) {
67
69
  throw new ShortageError(2, input.length);
@@ -100,6 +102,7 @@ function parseCounterFromBinaryData(input, version) {
100
102
  version,
101
103
  };
102
104
  }
105
+ /** Exhale a counter code/count pair into its fully qualified versioned token. */
103
106
  function encodeCounterFromFields(code, count, version) {
104
107
  const { sizeTable, nameTable } = getTables(version);
105
108
  const sizage = sizeTable.get(code);
@@ -123,6 +126,7 @@ function encodeCounterFromFields(code, count, version) {
123
126
  version,
124
127
  };
125
128
  }
129
+ /** Normalize the supported constructor variants into one shared counter payload. */
126
130
  function parseCounterInit(init) {
127
131
  const version = init.version ?? { major: 2, minor: 0 };
128
132
  if (init.code !== undefined) {
@@ -14,8 +14,7 @@ function toIso8601(dts) {
14
14
  */
15
15
  export class Dater extends Matter {
16
16
  constructor(init) {
17
- const matter = init instanceof Matter ? init : new Matter(init);
18
- super(matter);
17
+ super(init);
19
18
  if (!DATER_CODES.has(this.code)) {
20
19
  throw new UnknownCodeError(`Expected dater DateTime code, got ${this.code}`);
21
20
  }
@@ -29,23 +29,19 @@ function rawifyDns(dns) {
29
29
  */
30
30
  export class Decimer extends Matter {
31
31
  constructor(init) {
32
- if (!(init instanceof Matter)
32
+ const matterInit = (!(init instanceof Matter)
33
33
  && !init.raw
34
34
  && !init.qb64
35
35
  && !init.qb64b
36
36
  && !init.qb2
37
- && (init.dns !== undefined || init.decimal !== undefined)) {
38
- const dns = init.dns ?? `${init.decimal}`;
39
- super({
37
+ && (init.dns !== undefined || init.decimal !== undefined))
38
+ ? {
40
39
  ...init,
41
- raw: rawifyDns(dns),
40
+ raw: rawifyDns(init.dns ?? `${init.decimal}`),
42
41
  code: init.code ?? "4H",
43
- });
44
- }
45
- else {
46
- const matter = init instanceof Matter ? init : new Matter(init);
47
- super(matter);
48
- }
42
+ }
43
+ : init;
44
+ super(matterInit);
49
45
  if (!DECIMAL_CODES.has(this.code)) {
50
46
  throw new UnknownCodeError(`Expected decimal code, got ${this.code}`);
51
47
  }
@@ -1,18 +1,134 @@
1
- import { UnknownCodeError } from "../core/errors.js";
2
- import { DECRYPTER_CODES } from "./codex.js";
1
+ import { DeserializeError, UnknownCodeError } from "../core/errors.js";
2
+ import { normalizeByteLike } from "./byte-like.js";
3
+ import { Cipher } from "./cipher.js";
4
+ import { CIPHER_X25519_ALL_QB64_CODES, CIPHER_X25519_FIXED_QB64_CODES, CIPHER_X25519_QB2_VARIABLE_CODES, CIPHER_X25519_VARIABLE_STREAM_CODES, DECRYPTER_CODES, MtrDex, } from "./codex.js";
3
5
  import { Matter } from "./matter.js";
6
+ import { Salter } from "./salter.js";
7
+ import { boxKeyPairFromEd25519Seed, boxPublicKeyFromPrivateKey, openSealedBox } from "./sealed-box.js";
8
+ import { Signer } from "./signer.js";
9
+ import { Streamer } from "./streamer.js";
10
+ /** Normalize direct X25519 or derived-Ed25519 constructor forms into `Matter` input. */
11
+ function normalizeMatterInit(init) {
12
+ if (init instanceof Matter) {
13
+ return init;
14
+ }
15
+ const normalized = {
16
+ code: init.code ?? MtrDex.X25519_Private,
17
+ };
18
+ if (init.raw) {
19
+ normalized.raw = normalizeByteLike(init.raw);
20
+ return normalized;
21
+ }
22
+ if (init.qb64b) {
23
+ normalized.qb64b = normalizeByteLike(init.qb64b);
24
+ return normalized;
25
+ }
26
+ if (init.qb64) {
27
+ normalized.qb64b = normalizeByteLike(init.qb64);
28
+ return normalized;
29
+ }
30
+ if (init.qb2) {
31
+ normalized.qb2 = normalizeByteLike(init.qb2);
32
+ return normalized;
33
+ }
34
+ if (!init.seed) {
35
+ return normalized;
36
+ }
37
+ const signer = new Signer({ qb64b: normalizeByteLike(init.seed) });
38
+ if (signer.code !== MtrDex.Ed25519_Seed) {
39
+ throw new DeserializeError(`Unsupported signing seed derivation code = ${signer.code}.`);
40
+ }
41
+ normalized.raw = boxKeyPairFromEd25519Seed(signer.raw).privateKey;
42
+ return normalized;
43
+ }
44
+ function hydratePlaintext(cipher, plain, ctor, transferable) {
45
+ if (CIPHER_X25519_ALL_QB64_CODES.has(cipher.code)) {
46
+ return new ctor({ qb64b: plain, transferable });
47
+ }
48
+ if (CIPHER_X25519_QB2_VARIABLE_CODES.has(cipher.code)) {
49
+ return new ctor({ qb2: plain });
50
+ }
51
+ if (CIPHER_X25519_VARIABLE_STREAM_CODES.has(cipher.code)) {
52
+ return new ctor({ stream: plain });
53
+ }
54
+ throw new UnknownCodeError(`Unsupported cipher code = ${cipher.code}.`);
55
+ }
56
+ /**
57
+ * Resolve the default plaintext constructor implied by one cipher family.
58
+ *
59
+ * KERIpy correspondence:
60
+ * - fixed salt/seed families default to `Salter` / `Signer`
61
+ * - whole-stream families default to `Streamer`
62
+ * - generic qb64/qb2 variable families still require the caller to say what
63
+ * semantic primitive should be rehydrated from the plaintext bytes
64
+ */
65
+ function defaultCtorForCipher(cipher) {
66
+ if (cipher.code === MtrDex.X25519_Cipher_Salt) {
67
+ return Salter;
68
+ }
69
+ if (cipher.code === MtrDex.X25519_Cipher_Seed) {
70
+ return Signer;
71
+ }
72
+ if (CIPHER_X25519_VARIABLE_STREAM_CODES.has(cipher.code)) {
73
+ return Streamer;
74
+ }
75
+ if (CIPHER_X25519_FIXED_QB64_CODES.has(cipher.code)
76
+ || CIPHER_X25519_QB2_VARIABLE_CODES.has(cipher.code)
77
+ || CIPHER_X25519_ALL_QB64_CODES.has(cipher.code)) {
78
+ throw new UnknownCodeError(`Unsupported cipher code = ${cipher.code} when ctor is missing.`);
79
+ }
80
+ throw new UnknownCodeError(`Unsupported cipher code = ${cipher.code}.`);
81
+ }
4
82
  /**
5
83
  * Private-key decryption primitive for asymmetric envelope payloads.
6
84
  *
7
- * KERIpy substance: decrypter material is private X25519 key material used to
8
- * recover plaintext from cipher envelopes (often derived from signing seeds).
85
+ * KERIpy substance: `Decrypter` owns the private X25519 material used to
86
+ * recover CESR payloads from sealed-box ciphers, optionally deriving it from
87
+ * an Ed25519 signing seed.
9
88
  */
10
89
  export class Decrypter extends Matter {
11
- constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
90
+ /**
91
+ * Construct one X25519 private-key decrypter.
92
+ *
93
+ * Default code remains the KERIpy private-box family
94
+ * `MtrDex.X25519_Private`.
95
+ */
96
+ constructor(init = {}) {
97
+ super(normalizeMatterInit(init));
14
98
  if (!DECRYPTER_CODES.has(this.code)) {
15
99
  throw new UnknownCodeError(`Expected decrypter key code, got ${this.code}`);
16
100
  }
17
101
  }
102
+ /**
103
+ * Decrypt one cipher and rehydrate the plaintext with the requested
104
+ * constructor.
105
+ *
106
+ * Resolution rules:
107
+ * - `cipher` wins when supplied
108
+ * - otherwise `qb64` is parsed before `qb2`
109
+ * - omitted `ctor` is only inferred for the KERIpy families that already
110
+ * name their semantic plaintext shape
111
+ *
112
+ * Output rules:
113
+ * - `bare=true` returns plaintext bytes directly
114
+ * - otherwise `transferable` is forwarded only to qb64-family primitive
115
+ * constructors such as `Signer`, matching the KERIpy init seam
116
+ */
117
+ decrypt({ cipher, qb64, qb2, ctor, transferable = false, bare = false, } = {}) {
118
+ const hydrated = cipher
119
+ ?? (qb64
120
+ ? new Cipher({ qb64b: normalizeByteLike(qb64) })
121
+ : qb2
122
+ ? new Cipher({ qb2: normalizeByteLike(qb2) })
123
+ : null);
124
+ if (!hydrated) {
125
+ throw new DeserializeError("Need one of cipher, qb64, or qb2.");
126
+ }
127
+ const plain = openSealedBox(hydrated.raw, boxPublicKeyFromPrivateKey(this.raw), this.raw);
128
+ if (bare) {
129
+ return plain;
130
+ }
131
+ const effectiveCtor = (ctor ?? defaultCtorForCipher(hydrated));
132
+ return hydratePlaintext(hydrated, plain, effectiveCtor, transferable);
133
+ }
18
134
  }