cesr-ts 0.4.0 → 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 (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 +11 -2
  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 +19 -16
  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,8 +1,26 @@
1
- import { UnknownCodeError } from "../core/errors.js";
1
+ import { b, codeB2ToB64 } from "../core/bytes.js";
2
+ import { DeserializeError, SerializeError, UnknownCodeError } from "../core/errors.js";
3
+ import { CtrDexV2 } from "../tables/counter-codex.js";
2
4
  import { AGGOR_CODES, AGGOR_LIST_CODES, AGGOR_MAP_CODES } from "../tables/counter-groups.js";
3
- import { parseCompactor } from "./compactor.js";
4
- import { parseCounter } from "./counter.js";
5
+ import { MATTER_SIZES } from "../tables/matter.tables.generated.js";
6
+ import { DigDex } from "./codex.js";
7
+ import { Counter, parseCounter } from "./counter.js";
8
+ import { Diger } from "./diger.js";
9
+ import { Mapper, parseMapperBody } from "./mapper.js";
10
+ import { parseMatter } from "./matter.js";
5
11
  import { parseStructor, Structor } from "./structor.js";
12
+ /**
13
+ * `Aggor` is the aggregate-list sibling to `Compactor`.
14
+ *
15
+ * Where `Compactor` contracts nested map branches to SAIDs, `Aggor` contracts a
16
+ * list of aggregate elements down to one commitment in slot zero (`agid`) while
17
+ * still supporting later selective disclosure of map elements.
18
+ *
19
+ * Boundary rule:
20
+ * - `Aggor` owns selective disclosure for aggregate lists
21
+ * - `Compactor` owns hierarchical map disclosure
22
+ * - `disclosure.ts` owns fixed-field blind/bound/media disclosure
23
+ */
6
24
  /** True when counter code belongs to aggregate-list group families. */
7
25
  export function isAggorListCode(code) {
8
26
  return AGGOR_LIST_CODES.has(code);
@@ -16,20 +34,32 @@ export function isAggorCode(code) {
16
34
  return AGGOR_CODES.has(code);
17
35
  }
18
36
  /**
19
- * Aggregate list/map structor primitive.
37
+ * Aggregate list/map primitive.
20
38
  *
21
- * KERIpy substance: `Aggor` represents counted aggregate list/map payload
22
- * groups and exposes either tuple/list items or semantic map fields.
39
+ * KERIpy substance:
40
+ * - list-form `Aggor` owns aggregate-element-list (`ael`) semantics and `agid`
41
+ * computation/disclosure
42
+ * - map-form `Aggor` is preserved here only as a compatibility bridge for
43
+ * parser-group projections that still classify generic map groups as aggor
44
+ * families in the TS codebase
45
+ *
46
+ * The important tradeoff is intentional:
47
+ * - the list lane is the real graduated-disclosure story here
48
+ * - the map lane mainly keeps parser projections lossless and readable
23
49
  */
24
50
  export class Aggor extends Structor {
51
+ /**
52
+ * Construct one aggregate primitive.
53
+ *
54
+ * Two semantic lanes exist:
55
+ * - list lane: real aggregate semantics (`ael`, `agid`, disclosure)
56
+ * - map lane: compatibility bridge for existing TS parser projections that
57
+ * still bucket generic map groups into aggregate families
58
+ */
25
59
  constructor(init) {
26
- const payload = init instanceof Structor
27
- ? { structor: init }
28
- : "structor" in init
29
- ? init
30
- : {
31
- structor: new Structor(init),
32
- };
60
+ const payload = "structor" in init
61
+ ? init
62
+ : Aggor.materializeStructor(init);
33
63
  super(payload.structor);
34
64
  Object.defineProperty(this, "kind", {
35
65
  enumerable: true,
@@ -37,48 +67,362 @@ export class Aggor extends Structor {
37
67
  writable: true,
38
68
  value: void 0
39
69
  });
70
+ /** Map-style compatibility projection for parser-origin map groups. */
40
71
  Object.defineProperty(this, "mapFields", {
41
72
  enumerable: true,
42
73
  configurable: true,
43
74
  writable: true,
44
75
  value: void 0
45
76
  });
77
+ Object.defineProperty(this, "strict", {
78
+ enumerable: true,
79
+ configurable: true,
80
+ writable: true,
81
+ value: void 0
82
+ });
83
+ Object.defineProperty(this, "saids", {
84
+ enumerable: true,
85
+ configurable: true,
86
+ writable: true,
87
+ value: void 0
88
+ });
89
+ /** Digest code used to compute the aggregate identifier (`agid`). */
90
+ Object.defineProperty(this, "digestCode", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ Object.defineProperty(this, "wireKind", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: void 0
101
+ });
102
+ Object.defineProperty(this, "_ael", {
103
+ enumerable: true,
104
+ configurable: true,
105
+ writable: true,
106
+ value: void 0
107
+ });
46
108
  if (!isAggorCode(this.code)) {
47
109
  throw new UnknownCodeError(`Expected aggregate list/map group code, got ${this.code}`);
48
110
  }
49
111
  this.kind = isAggorMapCode(this.code) ? "map" : "list";
50
112
  this.mapFields = payload.mapFields;
113
+ this.strict = "structor" in init
114
+ ? true
115
+ : (init.strict ?? true);
116
+ this.saids = {
117
+ ...("structor" in init
118
+ ? {}
119
+ : (init.saids ?? { d: DigDex.Blake3_256 })),
120
+ };
121
+ this.digestCode = "structor" in init
122
+ ? DigDex.Blake3_256
123
+ : (init.code ?? DigDex.Blake3_256);
124
+ this.wireKind = "structor" in init
125
+ ? "CESR"
126
+ : (init.kind ?? "CESR");
127
+ if (this.kind === "list") {
128
+ try {
129
+ // Parser-origin list groups may be arbitrary generic list payloads, so
130
+ // deserialization into semantic aggregate elements is best-effort. When
131
+ // that semantic view is not meaningful we still preserve the structor.
132
+ this._ael = Aggor.deserializeList(this.qb64g, this.strict, this.saids);
133
+ }
134
+ catch {
135
+ this._ael = [];
136
+ }
137
+ }
138
+ else {
139
+ this._ael = [];
140
+ }
141
+ }
142
+ /** Aggregate identifier at element zero for list-form aggregates. */
143
+ get agid() {
144
+ return this.kind === "list" && typeof this._ael[0] === "string"
145
+ ? this._ael[0]
146
+ : null;
51
147
  }
52
- /** Tuple/list payload items for aggregate-list families. */
148
+ /** Aggregate element list in semantic form. */
149
+ get ael() {
150
+ return this._ael.map((entry) => Aggor.clone(entry));
151
+ }
152
+ /** Tuple/list payload items for parser-origin list aggregate families. */
53
153
  get listItems() {
54
154
  return this.kind === "list" ? this.items : undefined;
55
155
  }
156
+ /**
157
+ * Verify that the disclosed aggregate list still hashes to the agid in slot zero.
158
+ *
159
+ * Example:
160
+ * a disclosure list like `[agid, {d: "...", x: 1}, "E...undisclosed"]`
161
+ * verifies when replacing the disclosed map with its computed SAID reproduces
162
+ * the agid at position zero.
163
+ */
164
+ static verifyDisclosure(ael, kind = "CESR", code = DigDex.Blake3_256, saids = { d: DigDex.Blake3_256 }) {
165
+ // Verification is intentionally "rebuild and compare agid", not a more
166
+ // magical shortcut. Disclosure semantics are easier to trust when the test
167
+ // is literally "does this disclosed list still hash back to slot zero?"
168
+ try {
169
+ const aggor = new Aggor({
170
+ ael,
171
+ kind,
172
+ code,
173
+ saids,
174
+ verify: true,
175
+ });
176
+ return aggor.agid === (typeof ael[0] === "string" ? ael[0] : null);
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ }
182
+ /**
183
+ * Produce a disclosure view where only `indices` are expanded back into maps.
184
+ *
185
+ * Index `0` is always the agid and stays compact; only later map elements are
186
+ * eligible for disclosure expansion.
187
+ */
188
+ disclose(indices = []) {
189
+ if (this.kind !== "list") {
190
+ throw new SerializeError("Disclosure is only defined for aggregate lists.");
191
+ }
192
+ // We first normalize every element into either:
193
+ // - the aggregate's compact commitment string, or
194
+ // - a `Mapper` that knows how to compute its own compact SAID.
195
+ const atoms = this._ael.map((element, index) => {
196
+ if (index === 0) {
197
+ return element;
198
+ }
199
+ if (element && typeof element === "object" && !Array.isArray(element)) {
200
+ return Mapper.fromSad(element, {
201
+ strict: this.strict,
202
+ saidive: true,
203
+ saids: this.saids,
204
+ kind: this.wireKind,
205
+ makify: true,
206
+ verify: false,
207
+ });
208
+ }
209
+ return element;
210
+ });
211
+ const disclosure = atoms.map((atom) => atom instanceof Mapper ? (atom.said ?? "") : atom);
212
+ for (const index of indices) {
213
+ if (index > 0 && index < atoms.length && atoms[index] instanceof Mapper) {
214
+ // Disclosure only expands selected post-agid map elements. The agid at
215
+ // slot zero always remains compact because it is the commitment anchor.
216
+ disclosure[index] = atoms[index].mad;
217
+ }
218
+ }
219
+ return [disclosure, this.wireKind];
220
+ }
56
221
  /** Hydrate from an already parsed counter-group node. */
57
222
  static fromGroup(group, sourceDomain = "txt") {
58
- return new Aggor({ structor: Structor.fromGroup(group, sourceDomain) });
223
+ const structor = Structor.fromGroup(group, sourceDomain);
224
+ if (isAggorMapCode(structor.code)) {
225
+ const map = parseMapperBody(b(structor.qb64g), { major: 2, minor: 0 }, "txt");
226
+ return new Aggor({ structor, mapFields: map.fields });
227
+ }
228
+ return new Aggor({ structor });
229
+ }
230
+ static materializeStructor(init) {
231
+ const version = init.version ?? { major: 2, minor: 0 };
232
+ const raw = init.qb2
233
+ ? Aggor.canonicalize(init.qb2, version)
234
+ : init.qb64
235
+ ? b(init.qb64)
236
+ : init.qb64b
237
+ ? init.qb64b
238
+ : init.raw
239
+ ? init.raw
240
+ : init.ael
241
+ ? b(Aggor.serializeList(Aggor.clone(init.ael), init))
242
+ : null;
243
+ if (!raw) {
244
+ throw new SerializeError("Aggor requires ael or raw/qb64/qb64b/qb2 input.");
245
+ }
246
+ const structor = parseStructor(raw, version, "txt", AGGOR_CODES, "aggregate list/map");
247
+ const mapFields = isAggorMapCode(structor.code)
248
+ ? new Mapper({
249
+ raw: b(structor.qb64g),
250
+ version,
251
+ kind: "CESR",
252
+ verify: false,
253
+ }).fields
254
+ : undefined;
255
+ if (init.verify ?? true) {
256
+ if (isAggorListCode(structor.code)) {
257
+ // Today verification proves the aggregate identifier can be recomputed.
258
+ // It does not yet enforce every deeper KERIpy edge case, so keep this
259
+ // seam explicit for future parity expansion.
260
+ const ael = Aggor.deserializeList(structor.qb64g, init.strict ?? true, init.saids ?? { d: DigDex.Blake3_256 });
261
+ Aggor.computeAgid(ael, init.kind ?? "CESR", init.code ?? DigDex.Blake3_256, init.strict ?? true, init.saids ?? { d: DigDex.Blake3_256 });
262
+ }
263
+ }
264
+ return { structor, mapFields };
265
+ }
266
+ static canonicalize(raw, version) {
267
+ if (raw.length > 0 && raw[0] === "-".charCodeAt(0)) {
268
+ return raw;
269
+ }
270
+ const counter = parseCounter(raw, version, "bny");
271
+ return b(codeB2ToB64(raw, counter.fullSize + counter.count * 4));
272
+ }
273
+ static clone(value) {
274
+ if (Array.isArray(value)) {
275
+ return value.map((entry) => Aggor.clone(entry));
276
+ }
277
+ if (value && typeof value === "object") {
278
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Aggor.clone(v)]));
279
+ }
280
+ return value;
281
+ }
282
+ static serializeList(ael, options) {
283
+ const strict = options.strict ?? true;
284
+ const saids = options.saids ?? { d: DigDex.Blake3_256 };
285
+ const kind = options.kind ?? "CESR";
286
+ const code = options.code ?? DigDex.Blake3_256;
287
+ const makify = options.makify ?? false;
288
+ const working = Aggor.clone(ael);
289
+ if (makify && working.length > 0) {
290
+ // Slot zero is always the aggregate identifier. During makify we dummy it
291
+ // first, normalize later map elements into saidified `Mapper`s, then
292
+ // compute the final agid over that compacted view.
293
+ const sizage = MATTER_SIZES.get(code);
294
+ if (!sizage?.fs) {
295
+ throw new SerializeError(`Unsupported aggregate digest code=${code}`);
296
+ }
297
+ working[0] = "#".repeat(sizage.fs);
298
+ for (let idx = 1; idx < working.length; idx++) {
299
+ const element = working[idx];
300
+ if (element && typeof element === "object" && !Array.isArray(element)) {
301
+ const mapper = Mapper.fromSad(element, {
302
+ strict,
303
+ saidive: true,
304
+ saids,
305
+ kind,
306
+ makify: true,
307
+ verify: false,
308
+ });
309
+ working[idx] = mapper.mad;
310
+ }
311
+ }
312
+ const agid = Aggor.computeAgid(working, kind, code, strict, saids);
313
+ if (ael.length > 0) {
314
+ ael[0] = agid;
315
+ }
316
+ }
317
+ const compacted = working.map((element, idx) => {
318
+ if (idx === 0) {
319
+ return typeof ael[0] === "string" ? ael[0] : "";
320
+ }
321
+ if (element && typeof element === "object" && !Array.isArray(element)) {
322
+ // Aggregate list elements compact exactly like disclosed map sections:
323
+ // nested map content collapses to its mapper-native representation.
324
+ const mapper = Mapper.fromSad(element, {
325
+ strict,
326
+ saidive: true,
327
+ saids,
328
+ kind,
329
+ makify: true,
330
+ verify: false,
331
+ });
332
+ return mapper.qb64;
333
+ }
334
+ return String(element);
335
+ }).join("");
336
+ const codeName = compacted.length / 4 < 64 ** 2
337
+ ? CtrDexV2.GenericListGroup
338
+ : CtrDexV2.BigGenericListGroup;
339
+ return `${new Counter({ code: codeName, count: compacted.length / 4 }).qb64}${compacted}`;
340
+ }
341
+ static deserializeList(qb64, strict, saids) {
342
+ // Aggregate lists are walked element-by-element from the enclosed list
343
+ // payload. Elements may be:
344
+ // - compact qb64 commitments, or
345
+ // - nested map groups representing disclosed element bodies.
346
+ const raw = b(qb64);
347
+ const counter = parseCounter(raw, { major: 2, minor: 0 }, "txt");
348
+ const payload = raw.slice(counter.fullSize, counter.fullSize + counter.count * 4);
349
+ const out = [];
350
+ let offset = 0;
351
+ while (offset < payload.length) {
352
+ if (payload[offset] === "-".charCodeAt(0)) {
353
+ const nextCounter = parseCounter(payload.slice(offset), {
354
+ major: 2,
355
+ minor: 0,
356
+ }, "txt");
357
+ if (!isAggorMapCode(nextCounter.code)) {
358
+ throw new DeserializeError(`Expected aggregate element map group, got ${nextCounter.code}`);
359
+ }
360
+ const total = nextCounter.fullSize + nextCounter.count * 4;
361
+ const mapper = new Mapper({
362
+ raw: payload.slice(offset, offset + total),
363
+ strict,
364
+ saidive: true,
365
+ saids,
366
+ kind: "CESR",
367
+ verify: false,
368
+ });
369
+ out.push(mapper.mad);
370
+ offset += total;
371
+ continue;
372
+ }
373
+ const matter = parseMatter(payload.slice(offset), "txt");
374
+ out.push(matter.qb64);
375
+ offset += matter.fullSize;
376
+ }
377
+ return out;
378
+ }
379
+ static computeAgid(ael, kind, code, strict, saids) {
380
+ // `agid` is computed over the compacted aggregate view:
381
+ // dummy slot zero, compact every nested map element to its mapper SAID, then
382
+ // hash the fully enclosed list-group serialization.
383
+ const compacted = ael.map((element, idx) => {
384
+ if (idx === 0) {
385
+ const sizage = MATTER_SIZES.get(code);
386
+ if (!sizage?.fs) {
387
+ throw new SerializeError(`Unsupported aggregate digest code=${code}`);
388
+ }
389
+ return "#".repeat(sizage.fs);
390
+ }
391
+ if (element && typeof element === "object" && !Array.isArray(element)) {
392
+ const mapper = Mapper.fromSad(element, {
393
+ strict,
394
+ saidive: true,
395
+ saids,
396
+ kind,
397
+ makify: true,
398
+ verify: false,
399
+ });
400
+ return mapper.said ?? "";
401
+ }
402
+ return String(element);
403
+ }).join("");
404
+ const groupCode = compacted.length / 4 < 64 ** 2
405
+ ? CtrDexV2.GenericListGroup
406
+ : CtrDexV2.BigGenericListGroup;
407
+ const raw = b(`${new Counter({ code: groupCode, count: compacted.length / 4 }).qb64}${compacted}`);
408
+ return new Diger({ code, raw: Diger.digest(raw, code) }).qb64;
59
409
  }
60
410
  }
61
411
  /**
62
- * Parse aggregate attachment groups as list/map semantic containers.
412
+ * Parse aggregate attachment groups as semantic aggregate containers.
63
413
  *
64
- * KERIpy substance: aggregate counters can represent generic lists or map-body
65
- * structures; this helper normalizes both into one discriminated result.
414
+ * Example:
415
+ * a list body `-JAE<agid><map-or-said>...` becomes an `Aggor` whose `.ael`
416
+ * exposes the readable element list and whose `.agid` is the commitment in slot
417
+ * zero. A map-group still parses for compatibility, but exposes `.mapFields`
418
+ * rather than aggregate-list semantics.
66
419
  */
67
420
  export function parseAggor(input, version, cold) {
68
421
  const counter = parseCounter(input, version, cold);
69
422
  if (!isAggorCode(counter.code)) {
70
423
  throw new UnknownCodeError(`Expected aggregate list/map group code, got ${counter.code}`);
71
424
  }
72
- const structor = parseStructor(input, version, cold, new Set([counter.code]), "aggregate list/map");
73
- if (isAggorMapCode(structor.code)) {
74
- const map = parseCompactor(input, version, cold);
75
- return new Aggor({
76
- structor,
77
- mapFields: map.fields,
78
- });
79
- }
80
- if (isAggorListCode(structor.code)) {
81
- return new Aggor(structor);
82
- }
83
- throw new UnknownCodeError(`Expected aggregate list/map group code, got ${structor.code}`);
425
+ return new Aggor(cold === "txt"
426
+ ? { raw: input, version, kind: "CESR", verify: false }
427
+ : { qb2: input, version, kind: "CESR", verify: false });
84
428
  }
@@ -38,12 +38,11 @@ export class Bexter extends Matter {
38
38
  static derawify(raw, code) {
39
39
  const ls = getLeadSize(code);
40
40
  const bext = encodeB64(new Uint8Array([...new Uint8Array(ls), ...raw]));
41
- const ws = ls === 0 && bext.startsWith("A") ? 1 : (ls + 1) % 4;
41
+ const ws = ls === 0 ? (bext.startsWith("A") ? 1 : 0) : (ls + 1) % 4;
42
42
  return bext.slice(ws);
43
43
  }
44
44
  constructor(init) {
45
- const matter = init instanceof Matter ? init : new Matter(init);
46
- super(matter);
45
+ super(init);
47
46
  if (!isBexterCode(this.code)) {
48
47
  throw new UnknownCodeError(`Expected bexter strb64 code, got ${this.code}`);
49
48
  }
@@ -10,6 +10,12 @@ export function isBlinderCode(code) {
10
10
  *
11
11
  * KERIpy substance: `Blinder` materializes blinded/bound state tuple groups
12
12
  * used for blindable ACDC/TEL state disclosures.
13
+ *
14
+ * Maintainer note:
15
+ * - this class owns counted-group transport framing only
16
+ * - semantic blind/unblind/commit helpers live in `disclosure.ts`
17
+ * - the semantic fixed-field records carried by these groups are
18
+ * `BlindState` and `BoundState` from `structing.ts`
13
19
  */
14
20
  export class Blinder extends Structor {
15
21
  constructor(init) {
@@ -0,0 +1,19 @@
1
+ import { b } from "../core/bytes.js";
2
+ /** Normalize text or buffer-view inputs into one detached `Uint8Array`. */
3
+ export function normalizeByteLike(value) {
4
+ if (typeof value === "string") {
5
+ return b(value);
6
+ }
7
+ if (value instanceof Uint8Array) {
8
+ return Object.getPrototypeOf(value) === Uint8Array.prototype
9
+ ? value.slice()
10
+ : new Uint8Array(value);
11
+ }
12
+ return new Uint8Array(value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength));
13
+ }
14
+ /** Runtime guard for byte-like primitive constructor inputs. */
15
+ export function isByteLike(value) {
16
+ return typeof value === "string"
17
+ || value instanceof Uint8Array
18
+ || ArrayBuffer.isView(value);
19
+ }
@@ -6,18 +6,32 @@ import { Matter, parseMatter } from "./matter.js";
6
6
  *
7
7
  * KERIpy substance: `Cigar` wraps detached signature material where code
8
8
  * determines signature suite and payload holds raw signature bytes.
9
+ *
10
+ * Runtime note:
11
+ * - `.verfer` is contextual verifier metadata attached by signing, dispatch,
12
+ * or DB-rehydration code
13
+ * - it is not encoded inside the cigar bytes themselves
9
14
  */
10
15
  export class Cigar extends Matter {
11
- constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
16
+ constructor(init, verfer) {
17
+ super(init);
18
+ /** Optional verifier context for the key that created this detached signature. */
19
+ Object.defineProperty(this, "verfer", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: void 0
24
+ });
14
25
  if (!CIGAR_CODES.has(this.code)) {
15
26
  throw new UnknownCodeError(`Expected non-indexed signature code, got ${this.code}`);
16
27
  }
28
+ this.verfer = verfer;
17
29
  }
30
+ /** Raw detached signature bytes. */
18
31
  get sig() {
19
32
  return this.raw;
20
33
  }
34
+ /** Human-oriented generated codex member name for diagnostics and tooling. */
21
35
  get algorithm() {
22
36
  return matterCodexName(this.code) ?? "UnknownSig";
23
37
  }
@@ -1,18 +1,103 @@
1
- import { UnknownCodeError } from "../core/errors.js";
2
- import { CIPHER_X25519_ALL_CODES } from "./codex.js";
1
+ import { DeserializeError, UnknownCodeError } from "../core/errors.js";
2
+ import { normalizeByteLike } from "./byte-like.js";
3
+ import { CiXDex } from "./codex.js";
4
+ import { CIPHER_X25519_ALL_CODES, MtrDex } from "./codex.js";
5
+ import { Decrypter } from "./decrypter.js";
3
6
  import { Matter } from "./matter.js";
7
+ /** Map the KERIpy fixed cipher families to their expected raw sealed-box sizes. */
8
+ function expectedFixedCipherRawSize(code) {
9
+ return Matter.rawSizeForCode(code);
10
+ }
11
+ const FIXED_RAW_SIZES = new Map([
12
+ [
13
+ MtrDex.X25519_Cipher_Seed,
14
+ expectedFixedCipherRawSize(MtrDex.X25519_Cipher_Seed),
15
+ ],
16
+ [
17
+ MtrDex.X25519_Cipher_Salt,
18
+ expectedFixedCipherRawSize(MtrDex.X25519_Cipher_Salt),
19
+ ],
20
+ ]);
21
+ function inferFixedCipherCode(raw) {
22
+ for (const [code, size] of FIXED_RAW_SIZES.entries()) {
23
+ if (raw.length === size) {
24
+ return code;
25
+ }
26
+ }
27
+ throw new DeserializeError(`Unsupported fixed raw size ${raw.length} for X25519 cipher material.`);
28
+ }
29
+ /**
30
+ * Support KERIpy's convenience rule where fixed cipher families may be
31
+ * inferred from raw ciphertext size when the caller omits `code`.
32
+ */
33
+ function normalizeCipherInit(init) {
34
+ if (init instanceof Matter || !init.raw) {
35
+ return init;
36
+ }
37
+ const normalized = {
38
+ ...init,
39
+ raw: init.raw.slice(),
40
+ };
41
+ if (!normalized.code) {
42
+ normalized.code = inferFixedCipherCode(normalized.raw);
43
+ }
44
+ return normalized;
45
+ }
4
46
  /**
5
47
  * Ciphertext primitive for encrypted secret payloads.
6
48
  *
7
- * KERIpy substance: cipher material carries encrypted seed/salt-like plaintext
8
- * while code determines ciphertext family/size semantics.
49
+ * KERIpy substance: cipher material carries sealed-box encrypted CESR payloads
50
+ * while the cipher code preserves how the plaintext should be rehydrated.
51
+ *
52
+ * Construction rule:
53
+ * - fixed salt/seed cipher families may infer `code` from raw size
54
+ * - variable families still require the caller or parser to provide the
55
+ * correct derivation code explicitly
9
56
  */
10
57
  export class Cipher extends Matter {
11
58
  constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
59
+ super(normalizeCipherInit(init));
14
60
  if (!CIPHER_X25519_ALL_CODES.has(this.code)) {
15
61
  throw new UnknownCodeError(`Expected cipher code, got ${this.code}`);
16
62
  }
17
63
  }
64
+ /**
65
+ * Recover plaintext from this cipher using either a box private key or seed.
66
+ *
67
+ * KERIpy parity:
68
+ * - `prikey` or `seed` instantiates a `Decrypter`
69
+ * - default plaintext constructor is inferred from the cipher family when
70
+ * possible
71
+ *
72
+ * Key-material rule:
73
+ * - `prikey` is already-qualified X25519 private box material
74
+ * - `seed` is an Ed25519 signer seed that is first converted into the
75
+ * corresponding X25519 private box key
76
+ */
77
+ decrypt({ prikey, seed, ctor, transferable = false, bare = false, } = {}) {
78
+ return new Decrypter({
79
+ qb64: typeof prikey === "string" ? prikey : undefined,
80
+ qb64b: prikey && typeof prikey !== "string"
81
+ ? normalizeByteLike(prikey)
82
+ : undefined,
83
+ seed,
84
+ }).decrypt({
85
+ cipher: this,
86
+ ctor,
87
+ transferable,
88
+ bare,
89
+ });
90
+ }
18
91
  }
92
+ Object.defineProperty(Cipher, "Codex", {
93
+ enumerable: true,
94
+ configurable: true,
95
+ writable: true,
96
+ value: CiXDex
97
+ });
98
+ Object.defineProperty(Cipher, "Codes", {
99
+ enumerable: true,
100
+ configurable: true,
101
+ writable: true,
102
+ value: Object.freeze({ ...CiXDex })
103
+ });