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,6 +1,11 @@
1
- import { b, b64ToInt, codeB2ToB64, codeB64ToB2, concatBytes, decodeB64, encodeB64, intToB64, nabSextets, sceil, } from "../core/bytes.js";
1
+ import { b, b64ToInt, bytesEqual, codeB2ToB64, codeB64ToB2, concatBytes, decodeB64, encodeB64, intToB64, nabSextets, sceil, } from "../core/bytes.js";
2
2
  import { DeserializeError, ShortageError, UnknownCodeError } from "../core/errors.js";
3
- import { MATTER_HARDS, MATTER_SIZES } from "../tables/matter.tables.generated.js";
3
+ import { MATTER_CODE_NAMES, MATTER_HARDS, MATTER_SIZES } from "../tables/matter.tables.generated.js";
4
+ import { DIGEST_CODES, NON_TRANSFERABLE_CODES, PREFIX_CODES } from "./codex.js";
5
+ const SMALL_VAR_SELECTORS = ["4", "5", "6"];
6
+ const LARGE_VAR_SELECTORS = ["7", "8", "9"];
7
+ const MATTER_PAD = "_";
8
+ const MATTER_CODE_NAMES_BY_CODE = MATTER_CODE_NAMES;
4
9
  const MATTER_BARDS = new Map([...MATTER_HARDS.entries()].map(([code, hs]) => [
5
10
  String.fromCharCode(codeB64ToB2(code)[0]),
6
11
  hs,
@@ -14,6 +19,54 @@ function isMatterData(value) {
14
19
  && "fullSize" in value
15
20
  && "fullSizeB2" in value;
16
21
  }
22
+ function rawSizeForCode(code) {
23
+ const sizage = sizageForCode(code);
24
+ if (sizage.fs === null) {
25
+ throw new DeserializeError(`Non-fixed raw size for code ${code}.`);
26
+ }
27
+ const cs = sizage.hs + sizage.ss;
28
+ return Math.floor(((sizage.fs - cs) * 3) / 4) - sizage.ls;
29
+ }
30
+ function sizageForCode(code) {
31
+ const sizage = MATTER_SIZES.get(code);
32
+ if (!sizage) {
33
+ throw new UnknownCodeError(`Unknown matter code ${code}`);
34
+ }
35
+ return sizage;
36
+ }
37
+ function matterNameForCode(code) {
38
+ const name = MATTER_CODE_NAMES_BY_CODE[code];
39
+ if (!name) {
40
+ throw new UnknownCodeError(`Unknown matter code ${code}`);
41
+ }
42
+ return name;
43
+ }
44
+ function normalizeVariableMatterCode(code, raw) {
45
+ const sizage = MATTER_SIZES.get(code);
46
+ if (!sizage || sizage.fs !== null) {
47
+ return code;
48
+ }
49
+ const selector = code[0];
50
+ const leadSize = (3 - (raw.length % 3)) % 3;
51
+ const size = (raw.length + leadSize) / 3;
52
+ if (SMALL_VAR_SELECTORS.includes(selector)) {
53
+ if (size <= (64 ** 2) - 1) {
54
+ return `${SMALL_VAR_SELECTORS[leadSize]}${code.slice(1, 2)}`;
55
+ }
56
+ if (size <= (64 ** 4) - 1) {
57
+ return `${LARGE_VAR_SELECTORS[leadSize]}AA${code.slice(1, 2)}`;
58
+ }
59
+ throw new DeserializeError(`Unsupported variable raw size for ${code}: ${raw.length}.`);
60
+ }
61
+ if (LARGE_VAR_SELECTORS.includes(selector)) {
62
+ if (size <= (64 ** 4) - 1) {
63
+ return `${LARGE_VAR_SELECTORS[leadSize]}${code.slice(1, 4)}`;
64
+ }
65
+ throw new DeserializeError(`Unsupported variable raw size for large ${code}: ${raw.length}.`);
66
+ }
67
+ throw new DeserializeError(`Unsupported variable raw-size family for ${code}.`);
68
+ }
69
+ /** Resolve the effective matter code from the text-domain hard-selector prefix. */
17
70
  function parseMatterCodeFromText(txt) {
18
71
  const hs = MATTER_HARDS.get(txt[0]);
19
72
  if (!hs) {
@@ -32,6 +85,7 @@ function parseMatterCodeFromText(txt) {
32
85
  }
33
86
  return { code, hs: sizage.hs };
34
87
  }
88
+ /** Inhale one text-domain CESR primitive into normalized matter data fields. */
35
89
  function parseMatterFromTextData(input) {
36
90
  const txt = String.fromCharCode(...input);
37
91
  if (txt.length === 0) {
@@ -57,6 +111,7 @@ function parseMatterFromTextData(input) {
57
111
  fullSizeB2: sceil((fullSize * 3) / 4),
58
112
  };
59
113
  }
114
+ /** Inhale one qb2 CESR primitive into the canonical text-oriented matter shape. */
60
115
  function parseMatterFromBinaryData(input) {
61
116
  if (input.length === 0) {
62
117
  throw new ShortageError(1, 0);
@@ -112,7 +167,9 @@ function parseMatterFromBinaryData(input) {
112
167
  fullSizeB2: bfs,
113
168
  };
114
169
  }
170
+ /** Exhale raw bytes plus code into fully qualified CESR matter encodings. */
115
171
  function encodeMatterFromRaw(code, raw) {
172
+ code = normalizeVariableMatterCode(code, raw);
116
173
  const sizage = MATTER_SIZES.get(code);
117
174
  if (!sizage) {
118
175
  throw new UnknownCodeError(`Unknown matter code ${code}`);
@@ -146,6 +203,7 @@ function encodeMatterFromRaw(code, raw) {
146
203
  fullSizeB2: sceil((fullSize * 3) / 4),
147
204
  };
148
205
  }
206
+ /** Normalize the supported constructor variants into one shared matter payload. */
149
207
  function parseMatterInit(init) {
150
208
  if (init.raw && init.code) {
151
209
  return encodeMatterFromRaw(init.code, init.raw);
@@ -165,7 +223,8 @@ function parseMatterInit(init) {
165
223
  * Base CESR primitive carrying qualified matter material.
166
224
  *
167
225
  * KERIpy substance: `Matter` is the foundational primitive abstraction that
168
- * handles exfil/infil between raw bytes and qualified CESR encodings.
226
+ * handles exfil/infil between raw bytes and qualified CESR encodings and owns
227
+ * the base derivation-code semantic projections shared by semantic subclasses.
169
228
  */
170
229
  export class Matter {
171
230
  constructor(init) {
@@ -222,6 +281,36 @@ export class Matter {
222
281
  get code() {
223
282
  return this._code;
224
283
  }
284
+ /** KERIpy parity alias: codex member name for this derivation code. */
285
+ get name() {
286
+ return matterNameForCode(this._code);
287
+ }
288
+ /** KERIpy parity alias: hard code portion of the qualified text code. */
289
+ get hard() {
290
+ return this._code;
291
+ }
292
+ /** Soft code portion exclusive of any prepad xtra characters. */
293
+ get soft() {
294
+ const sizage = sizageForCode(this._code);
295
+ if (sizage.ss === 0) {
296
+ return "";
297
+ }
298
+ return this._qb64.slice(sizage.hs + sizage.xs, sizage.hs + sizage.ss);
299
+ }
300
+ /**
301
+ * KERIpy parity size projection.
302
+ *
303
+ * This follows KERIpy's generic `Matter.size` rule: decode the soft sextets
304
+ * when present, otherwise return `null`.
305
+ */
306
+ get size() {
307
+ return this.soft ? b64ToInt(this.soft) : null;
308
+ }
309
+ /** Hard + padded-soft view of the full text code. */
310
+ get both() {
311
+ const sizage = sizageForCode(this._code);
312
+ return `${this._code}${MATTER_PAD.repeat(sizage.xs)}${this.soft}`;
313
+ }
225
314
  get raw() {
226
315
  return this._raw;
227
316
  }
@@ -240,12 +329,53 @@ export class Matter {
240
329
  get fullSizeB2() {
241
330
  return this._fullSizeB2;
242
331
  }
332
+ /** Generic KERIpy derivation-code transferability rule. */
333
+ get transferable() {
334
+ return !NON_TRANSFERABLE_CODES.has(this._code);
335
+ }
336
+ /** Generic KERIpy digest-family semantic projection. */
337
+ get digestive() {
338
+ return DIGEST_CODES.has(this._code);
339
+ }
340
+ /** Generic KERIpy identifier-prefix semantic projection. */
341
+ get prefixive() {
342
+ return PREFIX_CODES.has(this._code);
343
+ }
344
+ /** True when the code family carries fixed-size soft text metadata. */
345
+ get special() {
346
+ const sizage = sizageForCode(this._code);
347
+ return sizage.fs !== null && sizage.ss > 0;
348
+ }
349
+ /** True when both qb64 and qb2 are 24-bit aligned and round-trip exactly. */
350
+ get composable() {
351
+ const qb2 = this.qb2;
352
+ return this.qb64b.length % 4 === 0
353
+ && qb2.length % 3 === 0
354
+ && encodeB64(qb2) === this.qb64
355
+ && bytesEqual(decodeB64(this.qb64), qb2);
356
+ }
243
357
  equals(other) {
244
358
  return this._qb64 === other.qb64;
245
359
  }
246
360
  toString() {
247
361
  return this._qb64;
248
362
  }
363
+ /** KERIpy parity helper: fixed raw size for one code. */
364
+ static rawSizeForCode(code) {
365
+ return rawSizeForCode(code);
366
+ }
367
+ /** KERIpy parity helper: fixed full qb64 size for one code. */
368
+ static fullSizeForCode(code) {
369
+ const sizage = sizageForCode(code);
370
+ if (sizage.fs === null) {
371
+ throw new DeserializeError(`Non-fixed full size for code ${code}.`);
372
+ }
373
+ return sizage.fs;
374
+ }
375
+ /** KERIpy parity helper: lead-byte size for one code. */
376
+ static leadSizeForCode(code) {
377
+ return sizageForCode(code).ls;
378
+ }
249
379
  }
250
380
  /** Parse matter from text-domain CESR bytes. */
251
381
  export function parseMatterFromText(input) {
@@ -10,6 +10,12 @@ export function isMediarCode(code) {
10
10
  *
11
11
  * KERIpy substance: `Mediar` materializes media metadata/value tuple groups
12
12
  * used in blinded media attachments and related CESR payload envelopes.
13
+ *
14
+ * Maintainer note:
15
+ * - this class owns counted-group transport framing only
16
+ * - semantic disclosure helpers live in `disclosure.ts`
17
+ * - the semantic fixed-field record carried by these groups is `TypeMedia`
18
+ * from `structing.ts`
13
19
  */
14
20
  export class Mediar extends Structor {
15
21
  constructor(init) {
@@ -1,7 +1,7 @@
1
1
  import { UnknownCodeError } from "../core/errors.js";
2
2
  import { NONCE_CODES, NonceDex } from "./codex.js";
3
3
  import { Diger } from "./diger.js";
4
- import { Matter, parseMatter } from "./matter.js";
4
+ import { parseMatter } from "./matter.js";
5
5
  /**
6
6
  * Nonce primitive for UUID/salt-or-digest nonce representations.
7
7
  *
@@ -10,8 +10,7 @@ import { Matter, parseMatter } from "./matter.js";
10
10
  */
11
11
  export class Noncer extends Diger {
12
12
  constructor(init) {
13
- const matter = init instanceof Matter ? init : new Matter(init);
14
- super(matter, { strict: false });
13
+ super(init, { strict: false });
15
14
  if (!NONCE_CODES.has(this.code)) {
16
15
  throw new UnknownCodeError(`Expected nonce code, got ${this.code}`);
17
16
  }
@@ -9,8 +9,7 @@ import { Matter, parseMatter } from "./matter.js";
9
9
  */
10
10
  export class NumberPrimitive extends Matter {
11
11
  constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
12
+ super(init);
14
13
  if (!NUMBER_CODES.has(this.code)) {
15
14
  throw new UnknownCodeError(`Expected number code, got ${this.code}`);
16
15
  }
@@ -1,11 +1,109 @@
1
- import { t } from "../core/bytes.js";
2
- import { UnknownCodeError } from "../core/errors.js";
1
+ import { b, t } from "../core/bytes.js";
2
+ import { DeserializeError, UnknownCodeError } from "../core/errors.js";
3
3
  import { Bexter } from "./bexter.js";
4
- import { BEXTER_CODES, TEXTER_CODES } from "./codex.js";
4
+ import { BEXTER_CODES, MtrDex, TEXTER_CODES } from "./codex.js";
5
5
  import { Matter, parseMatter } from "./matter.js";
6
6
  function isPatherCode(code) {
7
7
  return BEXTER_CODES.has(code) || TEXTER_CODES.has(code);
8
8
  }
9
+ function normalizeParts(path) {
10
+ if (typeof path !== "string") {
11
+ return [...path];
12
+ }
13
+ if (path.includes("/")) {
14
+ return path.split("/");
15
+ }
16
+ return path.split("-");
17
+ }
18
+ const RE_PATH_PART = /^[A-Za-z0-9_]*$/;
19
+ function strB64CodeForText(bext) {
20
+ const ts = bext.length % 4;
21
+ const ls = (3 - ts) % 3;
22
+ switch (ls) {
23
+ case 0:
24
+ return MtrDex.StrB64_L0;
25
+ case 1:
26
+ return MtrDex.StrB64_L1;
27
+ case 2:
28
+ return MtrDex.StrB64_L2;
29
+ default:
30
+ throw new DeserializeError(`Unsupported StrB64 leader size=${ls}`);
31
+ }
32
+ }
33
+ function bytesCodeForRaw(raw) {
34
+ const ls = (3 - (raw.length % 3)) % 3;
35
+ switch (ls) {
36
+ case 0:
37
+ return MtrDex.Bytes_L0;
38
+ case 1:
39
+ return MtrDex.Bytes_L1;
40
+ case 2:
41
+ return MtrDex.Bytes_L2;
42
+ default:
43
+ throw new DeserializeError(`Unsupported bytes leader size=${ls}`);
44
+ }
45
+ }
46
+ /**
47
+ * Build a Pather from semantic route/path text.
48
+ *
49
+ * This mirrors the KERIpy `Pather(path=..., relative=..., pathive=...)`
50
+ * constructor path:
51
+ * - "pathive" base64-safe segments use compact StrB64 encoding with `-`
52
+ * separators
53
+ * - non-pathive routes fall back to byte/text encoding when they contain
54
+ * characters like `/`
55
+ * - relative routes keep the first segment as-is, while absolute paths are
56
+ * forced to start with an empty root segment
57
+ *
58
+ * Examples:
59
+ * - `makePather("ksn", { relative: true, pathive: false }).qb64 === "4AABAksn"`
60
+ * - `makePather("reply", { relative: true, pathive: false }).qb64 === "6AACAAAreply"`
61
+ * - `makePather("credential/issue", { relative: true, pathive: false }).qb64 === "4AAEcredential-issue"`
62
+ */
63
+ export function makePather(path, opts = {}) {
64
+ const relative = opts.relative ?? false;
65
+ const pathive = opts.pathive ?? true;
66
+ const parts = normalizeParts(path);
67
+ let bextable = true;
68
+ for (const part of parts) {
69
+ if (!RE_PATH_PART.test(part)) {
70
+ if (pathive) {
71
+ throw new DeserializeError(`Invalid pathive path part=${part}`);
72
+ }
73
+ bextable = false;
74
+ }
75
+ }
76
+ if (!relative) {
77
+ if (parts.length > 0 && parts[0] !== "") {
78
+ parts.unshift("");
79
+ }
80
+ else if (parts.length === 0) {
81
+ parts.push("", "");
82
+ }
83
+ }
84
+ if (bextable) {
85
+ let bext = parts.join("-");
86
+ if (bext.includes("--")) {
87
+ throw new DeserializeError(`Non-unitary path separators for path=${bext}`);
88
+ }
89
+ const ws = (4 - (bext.length % 4)) % 4;
90
+ if (bext.startsWith("A") && (ws === 0 || ws === 1)) {
91
+ // KERIpy reserves `--` as an escape prefix when the compact path would
92
+ // otherwise start with an ambiguous leading `A`.
93
+ bext = `--${bext}`;
94
+ }
95
+ return new Pather({
96
+ raw: Bexter.rawify(bext),
97
+ code: strB64CodeForText(bext),
98
+ });
99
+ }
100
+ const text = parts.join("/");
101
+ if (text.includes("//")) {
102
+ throw new DeserializeError(`Non-unitary path separators for path=${text}`);
103
+ }
104
+ const raw = b(text);
105
+ return new Pather({ raw, code: bytesCodeForRaw(raw) });
106
+ }
9
107
  /**
10
108
  * CESR path primitive for SAD traversal routes.
11
109
  *
@@ -14,8 +112,7 @@ function isPatherCode(code) {
14
112
  */
15
113
  export class Pather extends Matter {
16
114
  constructor(init) {
17
- const matter = init instanceof Matter ? init : new Matter(init);
18
- super(matter);
115
+ super(init);
19
116
  if (!isPatherCode(this.code)) {
20
117
  throw new UnknownCodeError(`Expected pather-compatible code, got ${this.code}`);
21
118
  }
@@ -10,8 +10,7 @@ import { Matter, parseMatter } from "./matter.js";
10
10
  */
11
11
  export class Prefixer extends Matter {
12
12
  constructor(init) {
13
- const matter = init instanceof Matter ? init : new Matter(init);
14
- super(matter);
13
+ super(init);
15
14
  if (!PREFIX_CODES.has(this.code)) {
16
15
  throw new UnknownCodeError(`Expected prefix code, got ${this.code}`);
17
16
  }
@@ -7,3 +7,17 @@ export function isCounterGroupLike(entry) {
7
7
  return typeof entry === "object" && entry !== null && !Array.isArray(entry)
8
8
  && "items" in entry && "count" in entry && "code" in entry;
9
9
  }
10
+ /**
11
+ * Runtime guard for one rehydratable qualified primitive in a parsed group graph.
12
+ *
13
+ * This intentionally means "real CESR primitive with `qb64b`" rather than
14
+ * "any parser primitive union member", so tuples, nested counter groups, and
15
+ * `UnknownPrimitive` placeholders are all excluded.
16
+ */
17
+ export function isQualifiedPrimitive(entry) {
18
+ return typeof entry === "object"
19
+ && entry !== null
20
+ && !Array.isArray(entry)
21
+ && !isCounterGroupLike(entry)
22
+ && "qb64b" in entry;
23
+ }
@@ -1,5 +1,6 @@
1
1
  import { MATTER_CODE_NAMES } from "../tables/matter.tables.generated.js";
2
2
  import { parseMatterFromText } from "./matter.js";
3
+ /** Parse one text-domain primitive token and include its generated code name. */
3
4
  export function parsePrimitiveFromText(input) {
4
5
  const matter = parseMatterFromText(input);
5
6
  const name = MATTER_CODE_NAMES[matter.code]
@@ -12,6 +13,7 @@ export function parsePrimitiveFromText(input) {
12
13
  fullSize: matter.fullSize,
13
14
  };
14
15
  }
16
+ /** Return all known generated matter codes sorted for deterministic scans. */
15
17
  export function supportedPrimitiveCodes() {
16
18
  return Object.keys(MATTER_CODE_NAMES).sort();
17
19
  }
@@ -1,5 +1,9 @@
1
- import { UnknownCodeError } from "../core/errors.js";
1
+ import { SerializeError, UnknownCodeError } from "../core/errors.js";
2
+ import { dumps, sizeify } from "../serder/serder.js";
3
+ import { smell } from "../serder/smell.js";
4
+ import { MATTER_SIZES } from "../tables/matter.tables.generated.js";
2
5
  import { DIGEST_CODES } from "./codex.js";
6
+ import { Diger } from "./diger.js";
3
7
  import { Matter, parseMatter } from "./matter.js";
4
8
  /**
5
9
  * Self-addressing identifier digest primitive.
@@ -9,8 +13,7 @@ import { Matter, parseMatter } from "./matter.js";
9
13
  */
10
14
  export class Saider extends Matter {
11
15
  constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
16
+ super(init);
14
17
  if (!DIGEST_CODES.has(this.code)) {
15
18
  throw new UnknownCodeError(`Expected said digest code, got ${this.code}`);
16
19
  }
@@ -21,6 +24,80 @@ export class Saider extends Matter {
21
24
  get digest() {
22
25
  return this.raw;
23
26
  }
27
+ /**
28
+ * Derive one SAID from a SAD, inject it at `label`, and return the updated SAD.
29
+ *
30
+ * KERIpy places `saidify` on `Saider`, not `Serder`: the responsibility here
31
+ * is digest derivation for one saidive field, while event-specific policies
32
+ * such as mirrored `i == d` handling belong in higher layers.
33
+ */
34
+ static saidify(sad, { code = "E", kind, label = "d", ignore, digest = Diger.digest, }) {
35
+ const working = { ...sad };
36
+ if (ignore) {
37
+ for (const field of ignore) {
38
+ delete working[field];
39
+ }
40
+ }
41
+ const { sad: saidified, saiders } = Saider.saidifyFields(working, {
42
+ kind,
43
+ saids: { [label]: code },
44
+ digest,
45
+ });
46
+ const saider = saiders[label];
47
+ if (!saider) {
48
+ throw new SerializeError(`Expected digestive SAID result for ${label}`);
49
+ }
50
+ return { saider, sad: saidified };
51
+ }
52
+ /**
53
+ * Compute all digestive saidive fields from the same sized-dummied SAD bytes.
54
+ *
55
+ * This mirrors the KERIpy `makify/_compute` model for top-level saidive
56
+ * fields: resolve effective field codes first, dummy only digestive fields,
57
+ * size the version string once, hash once, then fill all digestive fields
58
+ * from that same raw serialization.
59
+ */
60
+ static saidifyFields(sad, { kind, saids, digest = Diger.digest, }) {
61
+ const working = { ...sad };
62
+ const actualKind = kind ?? inferKind(working);
63
+ for (const [label, code] of Object.entries(saids)) {
64
+ if (!(label in working)) {
65
+ throw new SerializeError(`Missing SAID field labeled ${label}`);
66
+ }
67
+ if (!DIGEST_CODES.has(code)) {
68
+ continue;
69
+ }
70
+ const sizage = MATTER_SIZES.get(code);
71
+ if (!sizage || sizage.fs === null) {
72
+ throw new SerializeError(`Unsupported fixed-size SAID code ${code}`);
73
+ }
74
+ working[label] = "#".repeat(sizage.fs);
75
+ }
76
+ if ("v" in working) {
77
+ sizeify(working, actualKind);
78
+ }
79
+ const digestRaw = dumps(working, actualKind);
80
+ const saiders = {};
81
+ for (const [label, code] of Object.entries(saids)) {
82
+ if (!DIGEST_CODES.has(code)) {
83
+ continue;
84
+ }
85
+ const saider = new Saider({
86
+ code,
87
+ raw: digest(digestRaw, code),
88
+ });
89
+ working[label] = saider.qb64;
90
+ saiders[label] = saider;
91
+ }
92
+ const raw = dumps(working, actualKind);
93
+ return { sad: working, raw, saiders };
94
+ }
95
+ }
96
+ function inferKind(sad) {
97
+ if (typeof sad.v !== "string") {
98
+ return "JSON";
99
+ }
100
+ return smell(new TextEncoder().encode(sad.v)).smellage.kind;
24
101
  }
25
102
  /** Parse and hydrate `Saider` from txt/qb2 bytes. */
26
103
  export function parseSaider(input, cold) {
@@ -1,22 +1,116 @@
1
+ import { argon2id } from "@noble/hashes/argon2";
1
2
  import { UnknownCodeError } from "../core/errors.js";
3
+ import { Tiers } from "../core/vocabulary.js";
4
+ import { MtrDex } from "./codex.js";
2
5
  import { SALTER_CODES } from "./codex.js";
3
6
  import { Matter } from "./matter.js";
7
+ import { Signer } from "./signer.js";
8
+ /** Encode one KERI salty-derivation path string into bytes for Argon2id input. */
9
+ function pathToBytes(path) {
10
+ return new TextEncoder().encode(path);
11
+ }
12
+ /**
13
+ * Map KERI tier/temp policy to Argon2id work factors.
14
+ *
15
+ * `temp=true` keeps the KERIpy testing rule: intentionally cheap stretching
16
+ * for test-only paths, never for normal persisted key derivation.
17
+ */
18
+ function tierParams(tier, temp) {
19
+ if (temp) {
20
+ return { t: 1, m: 8 };
21
+ }
22
+ if (tier === Tiers.low)
23
+ return { t: 2, m: 65536 };
24
+ if (tier === Tiers.med)
25
+ return { t: 3, m: 262144 };
26
+ if (tier === Tiers.high)
27
+ return { t: 4, m: 1048576 };
28
+ throw new Error(`Unsupported security tier=${tier}`);
29
+ }
4
30
  /**
5
31
  * Salt primitive for deterministic secret/signer derivation workflows.
6
32
  *
7
33
  * KERIpy substance: Salter stores random or fixed salt bytes and is used as
8
34
  * input to deterministic signer/seed generation paths.
35
+ *
36
+ * Maintainer model:
37
+ * - `tier` is the default stretch policy carried with the salt instance
38
+ * - individual `stretch()` / `signer()` calls may still override it
9
39
  */
10
40
  export class Salter extends Matter {
11
41
  constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
42
+ super(init);
43
+ Object.defineProperty(this, "tier", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: void 0
48
+ });
14
49
  if (!SALTER_CODES.has(this.code)) {
15
50
  throw new UnknownCodeError(`Expected salt code, got ${this.code}`);
16
51
  }
52
+ this.tier = init instanceof Salter
53
+ ? init.tier
54
+ : init.tier ?? Tiers.low;
17
55
  }
18
56
  /** Raw salt bytes used by key-derivation consumers. */
19
57
  get salt() {
20
58
  return this.raw;
21
59
  }
60
+ /**
61
+ * Derive deterministic secret bytes from this salt and one derivation path.
62
+ *
63
+ * KERIpy correspondence:
64
+ * - mirrors `Salter.stretch(...)` using Argon2id and tier/temp policy
65
+ *
66
+ * Path rule:
67
+ * - callers supply the fully formed KERI salty path string
68
+ * - `Salter` does not impose `pidx/ridx/kidx` structure itself; that remains
69
+ * a higher-layer manager/creator responsibility
70
+ */
71
+ stretch({ size = 32, path = "", tier = this.tier, temp = false, } = {}) {
72
+ const params = tierParams(tier, temp);
73
+ return argon2id(pathToBytes(path), this.raw, {
74
+ p: 1,
75
+ t: params.t,
76
+ m: params.m,
77
+ dkLen: size,
78
+ version: 0x13,
79
+ });
80
+ }
81
+ /**
82
+ * Derive one deterministic executable signer from this salt and one path.
83
+ *
84
+ * The signer suite controls seed width; `Salter` only owns the deterministic
85
+ * seed derivation.
86
+ */
87
+ signer({ code = MtrDex.Ed25519_Seed, transferable = true, path = "", tier, temp = false, } = {}) {
88
+ return new Signer({
89
+ code,
90
+ raw: this.stretch({
91
+ size: Signer.seedSizeForCode(code),
92
+ path,
93
+ tier,
94
+ temp,
95
+ }),
96
+ transferable,
97
+ });
98
+ }
99
+ /**
100
+ * Derive an ordered signer list from one path prefix plus hex suffix range.
101
+ *
102
+ * KERIpy correspondence:
103
+ * - successive signers append lowercase-hex offsets to the supplied path
104
+ * prefix
105
+ */
106
+ signers({ count = 1, start = 0, path = "", code = MtrDex.Ed25519_Seed, codes, transferable = true, tier, temp = false, } = {}) {
107
+ const effectiveCodes = codes ?? Array.from({ length: count }, () => code);
108
+ return effectiveCodes.map((suite, offset) => this.signer({
109
+ code: suite,
110
+ transferable,
111
+ path: `${path}${(start + offset).toString(16)}`,
112
+ tier,
113
+ temp,
114
+ }));
115
+ }
22
116
  }
@@ -0,0 +1,41 @@
1
+ import sodium from "libsodium-wrappers";
2
+ await sodium.ready;
3
+ /**
4
+ * Internal libsodium-backed sealed-box seam for KERIpy cipher parity.
5
+ *
6
+ * Boundary rule:
7
+ * - CESR primitives own sealed-box behavior
8
+ * - higher layers consume `Cipher`/`Encrypter`/`Decrypter`, not sodium calls
9
+ * - this module returns only raw key/plaintext/cipher bytes; derivation codes
10
+ * and primitive hydration stay outside at the CESR primitive layer
11
+ */
12
+ /** Derive X25519 public key material from one Ed25519 verifier key. */
13
+ export function boxPublicKeyFromEd25519Verfer(verferRaw) {
14
+ return new Uint8Array(sodium.crypto_sign_ed25519_pk_to_curve25519(verferRaw));
15
+ }
16
+ /**
17
+ * Derive the X25519 box keypair corresponding to one Ed25519 signing seed.
18
+ *
19
+ * KERIpy parity:
20
+ * - derive the Ed25519 signing keypair first
21
+ * - then convert both sides to the associated X25519 box keys
22
+ */
23
+ export function boxKeyPairFromEd25519Seed(seedRaw) {
24
+ const keyPair = sodium.crypto_sign_seed_keypair(seedRaw);
25
+ return {
26
+ publicKey: new Uint8Array(sodium.crypto_sign_ed25519_pk_to_curve25519(keyPair.publicKey)),
27
+ privateKey: new Uint8Array(sodium.crypto_sign_ed25519_sk_to_curve25519(keyPair.privateKey)),
28
+ };
29
+ }
30
+ /** Recover the X25519 public key corresponding to one raw X25519 private key. */
31
+ export function boxPublicKeyFromPrivateKey(privateKey) {
32
+ return new Uint8Array(sodium.crypto_scalarmult_base(privateKey));
33
+ }
34
+ /** Encrypt one plaintext using libsodium's X25519 sealed-box primitive. */
35
+ export function sealBox(plaintext, publicKey) {
36
+ return new Uint8Array(sodium.crypto_box_seal(plaintext, publicKey));
37
+ }
38
+ /** Open one libsodium X25519 sealed-box ciphertext back to raw plaintext bytes. */
39
+ export function openSealedBox(cipher, publicKey, privateKey) {
40
+ return new Uint8Array(sodium.crypto_box_seal_open(cipher, publicKey, privateKey));
41
+ }
@@ -9,8 +9,7 @@ import { Matter, parseMatter } from "./matter.js";
9
9
  */
10
10
  export class Seqner extends Matter {
11
11
  constructor(init) {
12
- const matter = init instanceof Matter ? init : new Matter(init);
13
- super(matter);
12
+ super(init);
14
13
  if (!SEQNER_CODES.has(this.code)) {
15
14
  throw new UnknownCodeError(`Expected seqner Salt_128 code, got ${this.code}`);
16
15
  }