@bcts/envelope-pattern 1.0.0-alpha.22 → 1.0.0-beta.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 (60) hide show
  1. package/dist/index.cjs +1291 -826
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +101 -59
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +102 -60
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.iife.js +1643 -1179
  8. package/dist/index.iife.js.map +1 -1
  9. package/dist/index.mjs +1315 -853
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +13 -11
  12. package/src/error.ts +1 -1
  13. package/src/format.ts +19 -31
  14. package/src/parse/index.ts +17 -1010
  15. package/src/parse/leaf/array-parser.ts +36 -0
  16. package/src/parse/leaf/cbor-parser.ts +43 -0
  17. package/src/parse/leaf/date-parser.ts +81 -0
  18. package/src/parse/leaf/known-value-parser.ts +73 -0
  19. package/src/parse/leaf/null-parser.ts +16 -0
  20. package/src/parse/leaf/number-parser.ts +90 -0
  21. package/src/parse/leaf/tag-parser.ts +160 -0
  22. package/src/parse/meta/and-parser.ts +40 -0
  23. package/src/parse/meta/capture-parser.ts +50 -0
  24. package/src/parse/meta/group-parser.ts +77 -0
  25. package/src/parse/meta/not-parser.ts +30 -0
  26. package/src/parse/meta/or-parser.ts +36 -0
  27. package/src/parse/meta/primary-parser.ts +234 -0
  28. package/src/parse/meta/search-parser.ts +41 -0
  29. package/src/parse/meta/traverse-parser.ts +42 -0
  30. package/src/parse/structure/assertion-obj-parser.ts +44 -0
  31. package/src/parse/structure/assertion-parser.ts +22 -0
  32. package/src/parse/structure/assertion-pred-parser.ts +45 -0
  33. package/src/parse/structure/compressed-parser.ts +17 -0
  34. package/src/parse/structure/digest-parser.ts +132 -0
  35. package/src/parse/structure/elided-parser.ts +17 -0
  36. package/src/parse/structure/encrypted-parser.ts +17 -0
  37. package/src/parse/structure/node-parser.ts +54 -0
  38. package/src/parse/structure/object-parser.ts +32 -0
  39. package/src/parse/structure/obscured-parser.ts +17 -0
  40. package/src/parse/structure/predicate-parser.ts +32 -0
  41. package/src/parse/structure/subject-parser.ts +32 -0
  42. package/src/parse/structure/wrapped-parser.ts +36 -0
  43. package/src/pattern/dcbor-integration.ts +40 -8
  44. package/src/pattern/index.ts +29 -0
  45. package/src/pattern/leaf/array-pattern.ts +67 -169
  46. package/src/pattern/leaf/cbor-pattern.ts +37 -23
  47. package/src/pattern/leaf/index.ts +1 -1
  48. package/src/pattern/leaf/map-pattern.ts +21 -2
  49. package/src/pattern/leaf/tagged-pattern.ts +6 -1
  50. package/src/pattern/meta/search-pattern.ts +13 -38
  51. package/src/pattern/meta/traverse-pattern.ts +2 -2
  52. package/src/pattern/structure/assertions-pattern.ts +19 -53
  53. package/src/pattern/structure/digest-pattern.ts +18 -22
  54. package/src/pattern/structure/index.ts +3 -0
  55. package/src/pattern/structure/node-pattern.ts +10 -29
  56. package/src/pattern/structure/object-pattern.ts +2 -2
  57. package/src/pattern/structure/predicate-pattern.ts +2 -2
  58. package/src/pattern/structure/subject-pattern.ts +31 -4
  59. package/src/pattern/structure/wrapped-pattern.ts +28 -9
  60. package/src/pattern/vm.ts +4 -4
package/dist/index.mjs CHANGED
@@ -1,9 +1,8 @@
1
- import { Interval, Interval as Interval$1, Quantifier, Quantifier as Quantifier$1, Reluctance, Reluctance as Reluctance$1, boolPatternAny, boolPatternDisplay, boolPatternPaths, boolPatternValue, byteStringPatternAny, byteStringPatternBinaryRegex, byteStringPatternDisplay, byteStringPatternPaths, byteStringPatternValue, datePatternAny, datePatternDisplay, datePatternEarliest, datePatternLatest, datePatternPaths, datePatternRange, datePatternRegex, datePatternStringValue, datePatternValue, knownValuePatternAny, knownValuePatternDisplay, knownValuePatternMatches, knownValuePatternNamed, knownValuePatternRegex, knownValuePatternValue, nullPatternDisplay, nullPatternPaths, numberPatternAny, numberPatternDisplay, numberPatternGreaterThan, numberPatternGreaterThanOrEqual, numberPatternInfinity, numberPatternLessThan, numberPatternLessThanOrEqual, numberPatternNaN, numberPatternNegInfinity, numberPatternPaths, numberPatternRange, numberPatternValue, parse as parse$1, patternDisplay, patternPathsWithCaptures as patternPathsWithCaptures$1, taggedPatternAny, taggedPatternDisplay, taggedPatternPathsWithCaptures, taggedPatternWithName, taggedPatternWithRegex, taggedPatternWithTag, textPatternAny, textPatternDisplay, textPatternPaths, textPatternRegex, textPatternValue } from "@bcts/dcbor-pattern";
2
- import { UNIT } from "@bcts/known-values";
3
- import { Envelope } from "@bcts/envelope";
4
- import { CborDate, asCborArray, asCborMap, bytesToHex, cbor } from "@bcts/dcbor";
1
+ import { Interval, Interval as Interval$1, Quantifier, Quantifier as Quantifier$1, Reluctance, Reluctance as Reluctance$1, arrayPatternAny, arrayPatternDisplay, arrayPatternEquals, arrayPatternMatches, arrayPatternWithElements, arrayPatternWithLength, arrayPatternWithLengthInterval, arrayPatternWithLengthRange, boolPatternAny, boolPatternDisplay, boolPatternPaths, boolPatternValue, byteStringPatternAny, byteStringPatternBinaryRegex, byteStringPatternDisplay, byteStringPatternPaths, byteStringPatternValue, datePatternAny, datePatternDisplay, datePatternEarliest, datePatternLatest, datePatternPaths, datePatternRange, datePatternRegex, datePatternStringValue, datePatternValue, knownValuePatternAny, knownValuePatternDisplay, knownValuePatternMatches, knownValuePatternNamed, knownValuePatternRegex, knownValuePatternValue, nullPatternDisplay, nullPatternPaths, numberPatternAny, numberPatternDisplay, numberPatternGreaterThan, numberPatternGreaterThanOrEqual, numberPatternInfinity, numberPatternLessThan, numberPatternLessThanOrEqual, numberPatternNaN, numberPatternNegInfinity, numberPatternPaths, numberPatternRange, numberPatternValue, parse as parse$1, patternDisplay, patternPathsWithCaptures as patternPathsWithCaptures$1, taggedPatternAny, taggedPatternDisplay, taggedPatternPathsWithCaptures, taggedPatternWithName, taggedPatternWithRegex, taggedPatternWithTag, textPatternAny, textPatternDisplay, textPatternPaths, textPatternRegex, textPatternValue } from "@bcts/dcbor-pattern";
2
+ import { KnownValue, UNIT } from "@bcts/known-values";
3
+ import { CborDate, asCborMap, bytesToHex, cbor, cborData, cborEquals } from "@bcts/dcbor";
4
+ import { Digest, Envelope } from "@bcts/envelope";
5
5
  import { parseDcborItemPartial } from "@bcts/dcbor-parse";
6
-
7
6
  //#region src/error.ts
8
7
  /**
9
8
  * Creates a successful result.
@@ -218,7 +217,6 @@ function dcborPatternError(error) {
218
217
  error
219
218
  };
220
219
  }
221
-
222
220
  //#endregion
223
221
  //#region src/format.ts
224
222
  /**
@@ -307,6 +305,14 @@ function formatPathsOpts() {
307
305
  /**
308
306
  * Gets a summary of an envelope for display.
309
307
  *
308
+ * Mirrors Rust `envelope_summary` in `format.rs`: defers to
309
+ * `Envelope::format_flat()` for nodes / wrapped / assertions and to
310
+ * `cbor.envelope_summary(usize::MAX, ...)` for raw CBOR leaves. The
311
+ * obscured cases (`elided` / `encrypted` / `compressed`) emit just the
312
+ * keyword. KnownValue envelopes look up the canonical name via
313
+ * `KnownValue.name()`, matching the Rust call to
314
+ * `KnownValuesStore::known_value_for_raw_value(value, …)`.
315
+ *
310
316
  * @param env - The envelope to summarize
311
317
  * @returns A string summary of the envelope
312
318
  */
@@ -315,25 +321,17 @@ function envelopeSummary(env) {
315
321
  const c = env.case();
316
322
  let summary;
317
323
  switch (c.type) {
318
- case "node": {
319
- const subjectSummary = env.subject().summary(Number.MAX_SAFE_INTEGER);
320
- const assertions = env.assertions();
321
- if (assertions.length > 0) summary = `NODE ${subjectSummary} [ ${assertions.map((a) => {
322
- const ac = a.case();
323
- if (ac.type === "assertion") return `${ac.assertion.predicate().summary(Number.MAX_SAFE_INTEGER)}: ${ac.assertion.object().summary(Number.MAX_SAFE_INTEGER)}`;
324
- return a.summary(Number.MAX_SAFE_INTEGER);
325
- }).join(", ")} ]`;
326
- else summary = `NODE ${subjectSummary}`;
324
+ case "node":
325
+ summary = `NODE ${env.formatFlat()}`;
327
326
  break;
328
- }
329
327
  case "leaf":
330
328
  summary = `LEAF ${env.summary(Number.MAX_SAFE_INTEGER)}`;
331
329
  break;
332
330
  case "wrapped":
333
- summary = `WRAPPED ${env.summary(Number.MAX_SAFE_INTEGER)}`;
331
+ summary = `WRAPPED ${env.formatFlat()}`;
334
332
  break;
335
333
  case "assertion":
336
- summary = `ASSERTION ${c.assertion.predicate().summary(Number.MAX_SAFE_INTEGER)}: ${c.assertion.object().summary(Number.MAX_SAFE_INTEGER)}`;
334
+ summary = `ASSERTION ${env.formatFlat()}`;
337
335
  break;
338
336
  case "elided":
339
337
  summary = "ELIDED";
@@ -379,8 +377,8 @@ function formatPathOpt(path, opts = defaultFormatPathsOpts()) {
379
377
  if (element === void 0) return "";
380
378
  switch (opts.elementFormat.type) {
381
379
  case "Summary": return truncateWithEllipsis(envelopeSummary(element), opts.elementFormat.maxLength);
382
- case "EnvelopeUR": return element.digest().toString();
383
- case "DigestUR": return element.digest().toString();
380
+ case "EnvelopeUR": return element.urString();
381
+ case "DigestUR": return element.digest().urString();
384
382
  }
385
383
  }
386
384
  switch (opts.elementFormat.type) {
@@ -395,8 +393,8 @@ function formatPathOpt(path, opts = defaultFormatPathsOpts()) {
395
393
  }
396
394
  return lines.join("\n");
397
395
  }
398
- case "EnvelopeUR": return path.map((element) => element.digest().toString()).join(" ");
399
- case "DigestUR": return path.map((element) => element.digest().toString()).join(" ");
396
+ case "EnvelopeUR": return path.map((element) => element.urString()).join(" ");
397
+ case "DigestUR": return path.map((element) => element.digest().urString()).join(" ");
400
398
  }
401
399
  }
402
400
  /**
@@ -477,19 +475,27 @@ function formatPathsOpt(paths, opts = defaultFormatPathsOpts()) {
477
475
  function formatPaths(paths) {
478
476
  return formatPathsOpt(paths, defaultFormatPathsOpts());
479
477
  }
480
-
481
478
  //#endregion
482
479
  //#region src/pattern/matcher.ts
483
480
  /**
484
481
  * Default implementations for Matcher methods.
485
482
  */
486
483
  const MatcherDefaults = {
484
+ /**
485
+ * Default implementation of paths() - calls pathsWithCaptures and discards captures.
486
+ */
487
487
  paths(matcher, haystack) {
488
488
  return matcher.pathsWithCaptures(haystack)[0];
489
489
  },
490
+ /**
491
+ * Default implementation of matches() - checks if paths() returns any results.
492
+ */
490
493
  matches(matcher, haystack) {
491
494
  return matcher.paths(haystack).length > 0;
492
495
  },
496
+ /**
497
+ * Default implementation of isComplex() - returns false.
498
+ */
493
499
  isComplex() {
494
500
  return false;
495
501
  }
@@ -577,7 +583,6 @@ function dispatchPatternToString(pattern) {
577
583
  if (patternToStringFn === void 0) throw new Error("Pattern dispatch functions not registered");
578
584
  return patternToStringFn(pattern);
579
585
  }
580
-
581
586
  //#endregion
582
587
  //#region src/pattern/leaf/bool-pattern.ts
583
588
  let createLeafBoolPattern;
@@ -661,7 +666,6 @@ var BoolPattern = class BoolPattern {
661
666
  return hash;
662
667
  }
663
668
  };
664
-
665
669
  //#endregion
666
670
  //#region src/pattern/leaf/null-pattern.ts
667
671
  let createLeafNullPattern;
@@ -730,7 +734,6 @@ var NullPattern = class NullPattern {
730
734
  return 0;
731
735
  }
732
736
  };
733
-
734
737
  //#endregion
735
738
  //#region src/pattern/leaf/number-pattern.ts
736
739
  let createLeafNumberPattern;
@@ -903,7 +906,6 @@ var NumberPattern = class NumberPattern {
903
906
  return hash;
904
907
  }
905
908
  };
906
-
907
909
  //#endregion
908
910
  //#region src/pattern/leaf/text-pattern.ts
909
911
  let createLeafTextPattern;
@@ -1008,7 +1010,6 @@ var TextPattern = class TextPattern {
1008
1010
  return hash;
1009
1011
  }
1010
1012
  };
1011
-
1012
1013
  //#endregion
1013
1014
  //#region src/pattern/leaf/byte-string-pattern.ts
1014
1015
  let createLeafByteStringPattern;
@@ -1119,7 +1120,6 @@ var ByteStringPattern = class ByteStringPattern {
1119
1120
  return hash;
1120
1121
  }
1121
1122
  };
1122
-
1123
1123
  //#endregion
1124
1124
  //#region src/pattern/leaf/date-pattern.ts
1125
1125
  let createLeafDatePattern;
@@ -1234,28 +1234,18 @@ var DatePattern = class DatePattern {
1234
1234
  return hash;
1235
1235
  }
1236
1236
  };
1237
-
1238
1237
  //#endregion
1239
1238
  //#region src/pattern/leaf/array-pattern.ts
1240
- /**
1241
- * Copyright © 2023-2026 Blockchain Commons, LLC
1242
- * Copyright © 2025-2026 Parity Technologies
1243
- *
1244
- *
1245
- * @bcts/envelope-pattern - Array pattern matching
1246
- *
1247
- * This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs
1248
- *
1249
- * @module envelope-pattern/pattern/leaf/array-pattern
1250
- */
1251
1239
  let createLeafArrayPattern;
1252
1240
  function registerArrayPatternFactory(factory) {
1253
1241
  createLeafArrayPattern = factory;
1254
1242
  }
1255
1243
  /**
1256
- * Pattern for matching array values in envelope leaf nodes.
1244
+ * Pattern for matching arrays.
1257
1245
  *
1258
- * Corresponds to the Rust `ArrayPattern` struct in array_pattern.rs
1246
+ * Mirrors Rust `ArrayPattern(dcbor_pattern::ArrayPattern)` from
1247
+ * `bc-envelope-pattern-rust/src/pattern/leaf/array_pattern.rs`. All
1248
+ * matching, display, and equality is delegated to dcbor-pattern.
1259
1249
  */
1260
1250
  var ArrayPattern = class ArrayPattern {
1261
1251
  _pattern;
@@ -1266,93 +1256,54 @@ var ArrayPattern = class ArrayPattern {
1266
1256
  * Creates a new ArrayPattern that matches any array.
1267
1257
  */
1268
1258
  static any() {
1269
- return new ArrayPattern({ type: "Any" });
1259
+ return new ArrayPattern(arrayPatternAny());
1270
1260
  }
1271
1261
  /**
1272
1262
  * Creates a new ArrayPattern that matches arrays with a specific length.
1273
1263
  */
1274
1264
  static count(count) {
1275
- return new ArrayPattern({
1276
- type: "Interval",
1277
- interval: Interval$1.exactly(count)
1278
- });
1265
+ return new ArrayPattern(arrayPatternWithLength(count));
1279
1266
  }
1280
1267
  /**
1281
1268
  * Creates a new ArrayPattern that matches arrays within a length range.
1282
1269
  */
1283
1270
  static interval(min, max) {
1284
- return new ArrayPattern({
1285
- type: "Interval",
1286
- interval: max !== void 0 ? Interval$1.from(min, max) : Interval$1.atLeast(min)
1287
- });
1271
+ return new ArrayPattern(arrayPatternWithLengthRange(min, max));
1288
1272
  }
1289
1273
  /**
1290
- * Creates a new ArrayPattern from a dcbor-pattern.
1274
+ * Creates a new ArrayPattern from a length Interval.
1291
1275
  */
1292
- static fromDcborPattern(dcborPattern) {
1293
- return new ArrayPattern({
1294
- type: "DCBORPattern",
1295
- pattern: dcborPattern
1296
- });
1276
+ static fromInterval(interval) {
1277
+ return new ArrayPattern(arrayPatternWithLengthInterval(interval));
1297
1278
  }
1298
1279
  /**
1299
- * Creates a new ArrayPattern with envelope patterns for element matching.
1280
+ * Creates a new ArrayPattern from a top-level dcbor-pattern.
1281
+ *
1282
+ * Mirrors Rust `ArrayPattern::from_dcbor_pattern`, which constructs an
1283
+ * `ArrayPattern::Elements`-style dcbor array pattern.
1300
1284
  */
1301
- static withPatterns(patterns) {
1302
- return new ArrayPattern({
1303
- type: "WithPatterns",
1304
- patterns
1305
- });
1285
+ static fromDcborPattern(pattern) {
1286
+ return new ArrayPattern(arrayPatternWithElements(pattern));
1306
1287
  }
1307
1288
  /**
1308
- * Gets the pattern type.
1289
+ * Creates a new ArrayPattern from an existing dcbor-pattern ArrayPattern.
1290
+ *
1291
+ * Mirrors Rust `ArrayPattern::from_dcbor_array_pattern`.
1309
1292
  */
1310
- get pattern() {
1293
+ static fromDcborArrayPattern(arrayPattern) {
1294
+ return new ArrayPattern(arrayPattern);
1295
+ }
1296
+ /**
1297
+ * Returns the underlying dcbor-pattern ArrayPattern.
1298
+ */
1299
+ inner() {
1311
1300
  return this._pattern;
1312
1301
  }
1313
1302
  pathsWithCaptures(haystack) {
1314
1303
  const cbor = haystack.subject().asLeaf();
1315
1304
  if (cbor === void 0) return [[], /* @__PURE__ */ new Map()];
1316
- const array = asCborArray(cbor);
1317
- if (array === void 0) return [[], /* @__PURE__ */ new Map()];
1318
- switch (this._pattern.type) {
1319
- case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1320
- case "Interval": {
1321
- const length = array.length;
1322
- if (this._pattern.interval.contains(length)) return [[[haystack]], /* @__PURE__ */ new Map()];
1323
- return [[], /* @__PURE__ */ new Map()];
1324
- }
1325
- case "DCBORPattern": {
1326
- const { paths: dcborPaths, captures: dcborCaptures } = patternPathsWithCaptures$1(this._pattern.pattern, cbor);
1327
- if (dcborPaths.length > 0) {
1328
- const envelopePaths = dcborPaths.map((dcborPath) => {
1329
- const envPath = [haystack];
1330
- for (let i = 1; i < dcborPath.length; i++) {
1331
- const elem = dcborPath[i];
1332
- if (elem !== void 0) envPath.push(Envelope.newLeaf(elem));
1333
- }
1334
- return envPath;
1335
- });
1336
- const envelopeCaptures = /* @__PURE__ */ new Map();
1337
- for (const [name, capturePaths] of dcborCaptures) {
1338
- const envCapturePaths = capturePaths.map((dcborPath) => {
1339
- const envPath = [haystack];
1340
- for (let i = 1; i < dcborPath.length; i++) {
1341
- const elem = dcborPath[i];
1342
- if (elem !== void 0) envPath.push(Envelope.newLeaf(elem));
1343
- }
1344
- return envPath;
1345
- });
1346
- envelopeCaptures.set(name, envCapturePaths);
1347
- }
1348
- return [envelopePaths, envelopeCaptures];
1349
- }
1350
- return [[], /* @__PURE__ */ new Map()];
1351
- }
1352
- case "WithPatterns":
1353
- if (array.length === this._pattern.patterns.length) return [[[haystack]], /* @__PURE__ */ new Map()];
1354
- return [[], /* @__PURE__ */ new Map()];
1355
- }
1305
+ if (arrayPatternMatches(this._pattern, cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1306
+ return [[], /* @__PURE__ */ new Map()];
1356
1307
  }
1357
1308
  paths(haystack) {
1358
1309
  return this.pathsWithCaptures(haystack)[0];
@@ -1368,55 +1319,31 @@ var ArrayPattern = class ArrayPattern {
1368
1319
  return false;
1369
1320
  }
1370
1321
  toString() {
1371
- switch (this._pattern.type) {
1372
- case "Any": return "[*]";
1373
- case "Interval": return `[{${this._pattern.interval.toString()}}]`;
1374
- case "DCBORPattern": return patternDisplay(this._pattern.pattern);
1375
- case "WithPatterns": return `[${this._pattern.patterns.map(String).join(", ")}]`;
1376
- }
1322
+ return arrayPatternDisplay(this._pattern, patternDisplay);
1377
1323
  }
1378
1324
  /**
1379
- * Equality comparison.
1325
+ * Equality comparison. Delegates to dcbor-pattern's structural equality
1326
+ * with a display-string fallback for pattern-equality (mirrors Rust's
1327
+ * `Hash` impl that hashes the display, since dcbor `ArrayPattern`
1328
+ * itself does not derive `Hash`).
1380
1329
  */
1381
1330
  equals(other) {
1382
- if (this._pattern.type !== other._pattern.type) return false;
1383
- switch (this._pattern.type) {
1384
- case "Any": return true;
1385
- case "Interval": return this._pattern.interval.equals(other._pattern.interval);
1386
- case "DCBORPattern": return patternDisplay(this._pattern.pattern) === patternDisplay(other._pattern.pattern);
1387
- case "WithPatterns": {
1388
- const otherPatterns = other._pattern.patterns;
1389
- if (this._pattern.patterns.length !== otherPatterns.length) return false;
1390
- for (let i = 0; i < this._pattern.patterns.length; i++) if (this._pattern.patterns[i] !== otherPatterns[i]) return false;
1391
- return true;
1392
- }
1393
- }
1331
+ return arrayPatternEquals(this._pattern, other._pattern, (a, b) => patternDisplay(a) === patternDisplay(b));
1394
1332
  }
1395
1333
  /**
1396
- * Hash code for use in Maps/Sets.
1334
+ * Hash code for use in Maps/Sets. Mirrors Rust's
1335
+ * "hash the string representation" approach.
1397
1336
  */
1398
1337
  hashCode() {
1399
- switch (this._pattern.type) {
1400
- case "Any": return 0;
1401
- case "Interval": return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
1402
- case "DCBORPattern": return simpleStringHash$3(patternDisplay(this._pattern.pattern));
1403
- case "WithPatterns": return this._pattern.patterns.length;
1338
+ let hash = 0;
1339
+ const str = this.toString();
1340
+ for (let i = 0; i < str.length; i++) {
1341
+ hash = (hash << 5) - hash + str.charCodeAt(i);
1342
+ hash = hash & hash;
1404
1343
  }
1344
+ return hash;
1405
1345
  }
1406
1346
  };
1407
- /**
1408
- * Simple string hash function for hashCode implementations.
1409
- */
1410
- function simpleStringHash$3(str) {
1411
- let hash = 0;
1412
- for (let i = 0; i < str.length; i++) {
1413
- const char = str.charCodeAt(i);
1414
- hash = (hash << 5) - hash + char;
1415
- hash = hash & hash;
1416
- }
1417
- return hash;
1418
- }
1419
-
1420
1347
  //#endregion
1421
1348
  //#region src/pattern/leaf/map-pattern.ts
1422
1349
  let createLeafMapPattern;
@@ -1449,6 +1376,19 @@ var MapPattern = class MapPattern {
1449
1376
  });
1450
1377
  }
1451
1378
  /**
1379
+ * Creates a new MapPattern from a length Interval.
1380
+ *
1381
+ * Mirrors Rust `MapPattern::from_interval`. Used by the
1382
+ * dcbor-pattern → envelope-pattern bridge to preserve `{{n,m}}`
1383
+ * length info.
1384
+ */
1385
+ static fromInterval(interval) {
1386
+ return new MapPattern({
1387
+ type: "Interval",
1388
+ interval
1389
+ });
1390
+ }
1391
+ /**
1452
1392
  * Gets the pattern type.
1453
1393
  */
1454
1394
  get pattern() {
@@ -1483,8 +1423,8 @@ var MapPattern = class MapPattern {
1483
1423
  }
1484
1424
  toString() {
1485
1425
  switch (this._pattern.type) {
1486
- case "Any": return "{*}";
1487
- case "Interval": return `{{${this._pattern.interval.toString()}}}`;
1426
+ case "Any": return "map";
1427
+ case "Interval": return `{${this._pattern.interval.toString()}}`;
1488
1428
  }
1489
1429
  }
1490
1430
  /**
@@ -1507,7 +1447,6 @@ var MapPattern = class MapPattern {
1507
1447
  }
1508
1448
  }
1509
1449
  };
1510
-
1511
1450
  //#endregion
1512
1451
  //#region src/pattern/leaf/known-value-pattern.ts
1513
1452
  let createLeafKnownValuePattern;
@@ -1628,7 +1567,6 @@ function simpleStringHash$2(str) {
1628
1567
  }
1629
1568
  return hash;
1630
1569
  }
1631
-
1632
1570
  //#endregion
1633
1571
  //#region src/pattern/leaf/tagged-pattern.ts
1634
1572
  /**
@@ -1739,7 +1677,7 @@ var TaggedPattern = class TaggedPattern {
1739
1677
  return false;
1740
1678
  }
1741
1679
  toString() {
1742
- return taggedPatternDisplay(this._inner, patternDisplay);
1680
+ return taggedPatternDisplay(this._inner, patternDisplay).replace(", ", ", ");
1743
1681
  }
1744
1682
  /**
1745
1683
  * Equality comparison.
@@ -1786,7 +1724,6 @@ function simpleStringHash$1(str) {
1786
1724
  }
1787
1725
  return hash;
1788
1726
  }
1789
-
1790
1727
  //#endregion
1791
1728
  //#region src/pattern/leaf/cbor-pattern.ts
1792
1729
  /**
@@ -1866,10 +1803,18 @@ var CBORPattern = class CBORPattern {
1866
1803
  }
1867
1804
  /**
1868
1805
  * Convert a single dcbor path to an envelope path.
1806
+ *
1807
+ * Uses canonical CBOR-byte equality (`cborEquals`) for the "skip the
1808
+ * dcbor root if it duplicates our base envelope" check, mirroring
1809
+ * Rust's `dcbor_path.first().map(|first| first == &base_cbor)`. The
1810
+ * earlier port compared diagnostic strings, which collapses values
1811
+ * that share a textual representation but differ structurally
1812
+ * (e.g. NaN payloads).
1869
1813
  */
1870
1814
  _convertDcborPathToEnvelopePath(dcborPath, baseEnvelope, baseCbor) {
1871
1815
  const envelopePath = [baseEnvelope];
1872
- const elementsToAdd = dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === baseCbor.toDiagnostic() ? dcborPath.slice(1) : dcborPath;
1816
+ const first = dcborPath[0];
1817
+ const elementsToAdd = first !== void 0 && cborEquals(first, baseCbor) ? dcborPath.slice(1) : dcborPath;
1873
1818
  for (const cborElement of elementsToAdd) envelopePath.push(Envelope.newLeaf(cborElement));
1874
1819
  return envelopePath;
1875
1820
  }
@@ -1896,7 +1841,7 @@ var CBORPattern = class CBORPattern {
1896
1841
  switch (this._pattern.type) {
1897
1842
  case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1898
1843
  case "Value":
1899
- if (knownValueCbor.toDiagnostic() === this._pattern.cbor.toDiagnostic()) return [[[haystack]], /* @__PURE__ */ new Map()];
1844
+ if (cborEquals(knownValueCbor, this._pattern.cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1900
1845
  return [[], /* @__PURE__ */ new Map()];
1901
1846
  case "Pattern": {
1902
1847
  const { paths: dcborPaths, captures: dcborCaptures } = patternPathsWithCaptures$1(this._pattern.pattern, knownValueCbor);
@@ -1920,7 +1865,7 @@ var CBORPattern = class CBORPattern {
1920
1865
  switch (this._pattern.type) {
1921
1866
  case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1922
1867
  case "Value":
1923
- if (leafCbor.toDiagnostic() === this._pattern.cbor.toDiagnostic()) return [[[haystack]], /* @__PURE__ */ new Map()];
1868
+ if (cborEquals(leafCbor, this._pattern.cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1924
1869
  return [[], /* @__PURE__ */ new Map()];
1925
1870
  case "Pattern": {
1926
1871
  const { paths: dcborPaths, captures: dcborCaptures } = patternPathsWithCaptures$1(this._pattern.pattern, leafCbor);
@@ -1928,7 +1873,8 @@ var CBORPattern = class CBORPattern {
1928
1873
  const basePath = [haystack];
1929
1874
  return [dcborPaths.map((dcborPath) => {
1930
1875
  const extendedPath = [...basePath];
1931
- const elementsToAdd = dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === leafCbor.toDiagnostic() ? dcborPath.slice(1) : dcborPath;
1876
+ const first = dcborPath[0];
1877
+ const elementsToAdd = first !== void 0 && cborEquals(first, leafCbor) ? dcborPath.slice(1) : dcborPath;
1932
1878
  for (const cborElement of elementsToAdd) extendedPath.push(Envelope.newLeaf(cborElement));
1933
1879
  return extendedPath;
1934
1880
  }), this._convertDcborCapturesToEnvelopeCaptures(dcborCaptures, haystack, leafCbor)];
@@ -1963,13 +1909,16 @@ var CBORPattern = class CBORPattern {
1963
1909
  }
1964
1910
  }
1965
1911
  /**
1966
- * Equality comparison.
1912
+ * Equality comparison. `Value` variants compare by canonical CBOR
1913
+ * byte sequence (mirrors Rust `==` on `CBOR`); `Pattern` variants fall
1914
+ * back to display-string compare since `DCBORPattern` doesn't expose
1915
+ * structural equality outside the crate.
1967
1916
  */
1968
1917
  equals(other) {
1969
1918
  if (this._pattern.type !== other._pattern.type) return false;
1970
1919
  switch (this._pattern.type) {
1971
1920
  case "Any": return true;
1972
- case "Value": return this._pattern.cbor.toDiagnostic() === other._pattern.cbor.toDiagnostic();
1921
+ case "Value": return cborEquals(this._pattern.cbor, other._pattern.cbor);
1973
1922
  case "Pattern": return patternDisplay(this._pattern.pattern) === patternDisplay(other._pattern.pattern);
1974
1923
  }
1975
1924
  }
@@ -1979,7 +1928,12 @@ var CBORPattern = class CBORPattern {
1979
1928
  hashCode() {
1980
1929
  switch (this._pattern.type) {
1981
1930
  case "Any": return 0;
1982
- case "Value": return simpleStringHash(this._pattern.cbor.toDiagnostic());
1931
+ case "Value": {
1932
+ const bytes = cborData(this._pattern.cbor);
1933
+ let hash = 0;
1934
+ for (const byte of bytes) hash = hash * 31 + byte | 0;
1935
+ return hash;
1936
+ }
1983
1937
  case "Pattern": return simpleStringHash(patternDisplay(this._pattern.pattern));
1984
1938
  }
1985
1939
  }
@@ -1995,7 +1949,6 @@ function simpleStringHash(str) {
1995
1949
  }
1996
1950
  return hash;
1997
1951
  }
1998
-
1999
1952
  //#endregion
2000
1953
  //#region src/pattern/leaf/index.ts
2001
1954
  /**
@@ -2197,7 +2150,6 @@ function leafPatternToString(pattern) {
2197
2150
  case "KnownValue": return pattern.pattern.toString();
2198
2151
  }
2199
2152
  }
2200
-
2201
2153
  //#endregion
2202
2154
  //#region src/pattern/structure/leaf-structure-pattern.ts
2203
2155
  let createStructureLeafPattern;
@@ -2250,12 +2202,12 @@ var LeafStructurePattern = class LeafStructurePattern {
2250
2202
  return 0;
2251
2203
  }
2252
2204
  };
2253
-
2254
- //#endregion
2255
- //#region src/pattern/structure/subject-pattern.ts
2256
- let createStructureSubjectPattern;
2257
- function registerSubjectPatternFactory(factory) {
2258
- createStructureSubjectPattern = factory;
2205
+ let dispatchPatternCompile$1;
2206
+ let dispatchPatternToString$3;
2207
+ function registerSubjectPatternFactory(factory) {}
2208
+ function registerSubjectPatternDispatch(dispatch) {
2209
+ dispatchPatternCompile$1 = dispatch.compile;
2210
+ dispatchPatternToString$3 = dispatch.toString;
2259
2211
  }
2260
2212
  /**
2261
2213
  * Pattern for matching subjects in envelopes.
@@ -2320,9 +2272,10 @@ var SubjectPattern = class SubjectPattern {
2320
2272
  code.push({ type: "NavigateSubject" });
2321
2273
  break;
2322
2274
  case "Pattern":
2275
+ if (dispatchPatternCompile$1 === void 0) throw new Error("SubjectPattern.compile requires the top-level Pattern compile dispatch; not registered");
2323
2276
  code.push({ type: "NavigateSubject" });
2324
2277
  code.push({ type: "ExtendTraversal" });
2325
- this._pattern.pattern.compile(code, literals, captures);
2278
+ dispatchPatternCompile$1(this._pattern.pattern, code, literals, captures);
2326
2279
  code.push({ type: "CombineTraversal" });
2327
2280
  break;
2328
2281
  }
@@ -2333,7 +2286,10 @@ var SubjectPattern = class SubjectPattern {
2333
2286
  toString() {
2334
2287
  switch (this._pattern.type) {
2335
2288
  case "Any": return "subj";
2336
- case "Pattern": return `subj(${this._pattern.pattern.toString()})`;
2289
+ case "Pattern": {
2290
+ const fmt = dispatchPatternToString$3;
2291
+ return `subj(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2292
+ }
2337
2293
  }
2338
2294
  }
2339
2295
  /**
@@ -2351,7 +2307,6 @@ var SubjectPattern = class SubjectPattern {
2351
2307
  return this._pattern.type === "Any" ? 0 : 1;
2352
2308
  }
2353
2309
  };
2354
-
2355
2310
  //#endregion
2356
2311
  //#region src/pattern/structure/predicate-pattern.ts
2357
2312
  let createStructurePredicatePattern;
@@ -2431,7 +2386,7 @@ var PredicatePattern = class PredicatePattern {
2431
2386
  toString() {
2432
2387
  switch (this._pattern.type) {
2433
2388
  case "Any": return "pred";
2434
- case "Pattern": return `pred(${this._pattern.pattern.toString()})`;
2389
+ case "Pattern": return `pred(${dispatchPatternToString(this._pattern.pattern)})`;
2435
2390
  }
2436
2391
  }
2437
2392
  /**
@@ -2449,7 +2404,6 @@ var PredicatePattern = class PredicatePattern {
2449
2404
  return this._pattern.type === "Any" ? 0 : 1;
2450
2405
  }
2451
2406
  };
2452
-
2453
2407
  //#endregion
2454
2408
  //#region src/pattern/structure/object-pattern.ts
2455
2409
  let createStructureObjectPattern;
@@ -2529,7 +2483,7 @@ var ObjectPattern = class ObjectPattern {
2529
2483
  toString() {
2530
2484
  switch (this._pattern.type) {
2531
2485
  case "Any": return "obj";
2532
- case "Pattern": return `obj(${this._pattern.pattern.toString()})`;
2486
+ case "Pattern": return `obj(${dispatchPatternToString(this._pattern.pattern)})`;
2533
2487
  }
2534
2488
  }
2535
2489
  /**
@@ -2547,13 +2501,16 @@ var ObjectPattern = class ObjectPattern {
2547
2501
  return this._pattern.type === "Any" ? 0 : 1;
2548
2502
  }
2549
2503
  };
2550
-
2551
2504
  //#endregion
2552
2505
  //#region src/pattern/structure/assertions-pattern.ts
2553
2506
  let createStructureAssertionsPattern;
2507
+ let dispatchPatternToString$2;
2554
2508
  function registerAssertionsPatternFactory(factory) {
2555
2509
  createStructureAssertionsPattern = factory;
2556
2510
  }
2511
+ function registerAssertionsPatternToStringDispatch(fn) {
2512
+ dispatchPatternToString$2 = fn;
2513
+ }
2557
2514
  /**
2558
2515
  * Pattern for matching assertions in envelopes.
2559
2516
  *
@@ -2591,17 +2548,6 @@ var AssertionsPattern = class AssertionsPattern {
2591
2548
  });
2592
2549
  }
2593
2550
  /**
2594
- * Creates a new AssertionsPattern that matches assertions with both
2595
- * predicate and object patterns.
2596
- */
2597
- static withBoth(predicatePattern, objectPattern) {
2598
- return new AssertionsPattern({
2599
- type: "WithBoth",
2600
- predicatePattern,
2601
- objectPattern
2602
- });
2603
- }
2604
- /**
2605
2551
  * Gets the pattern type.
2606
2552
  */
2607
2553
  get patternType() {
@@ -2612,14 +2558,12 @@ var AssertionsPattern = class AssertionsPattern {
2612
2558
  */
2613
2559
  predicatePattern() {
2614
2560
  if (this._pattern.type === "WithPredicate") return this._pattern.pattern;
2615
- if (this._pattern.type === "WithBoth") return this._pattern.predicatePattern;
2616
2561
  }
2617
2562
  /**
2618
2563
  * Gets the object pattern if this has one, undefined otherwise.
2619
2564
  */
2620
2565
  objectPattern() {
2621
2566
  if (this._pattern.type === "WithObject") return this._pattern.pattern;
2622
- if (this._pattern.type === "WithBoth") return this._pattern.objectPattern;
2623
2567
  }
2624
2568
  pathsWithCaptures(haystack) {
2625
2569
  const paths = [];
@@ -2641,14 +2585,6 @@ var AssertionsPattern = class AssertionsPattern {
2641
2585
  }
2642
2586
  break;
2643
2587
  }
2644
- case "WithBoth": {
2645
- const predicate = assertion.asPredicate?.();
2646
- const object = assertion.asObject?.();
2647
- if (predicate !== void 0 && object !== void 0) {
2648
- if (matchPattern(this._pattern.predicatePattern, predicate) && matchPattern(this._pattern.objectPattern, object)) paths.push([assertion]);
2649
- }
2650
- break;
2651
- }
2652
2588
  }
2653
2589
  return [paths, /* @__PURE__ */ new Map()];
2654
2590
  }
@@ -2671,11 +2607,11 @@ var AssertionsPattern = class AssertionsPattern {
2671
2607
  return false;
2672
2608
  }
2673
2609
  toString() {
2610
+ const fmt = dispatchPatternToString$2;
2674
2611
  switch (this._pattern.type) {
2675
2612
  case "Any": return "assert";
2676
- case "WithPredicate": return `assertpred(${this._pattern.pattern.toString()})`;
2677
- case "WithObject": return `assertobj(${this._pattern.pattern.toString()})`;
2678
- case "WithBoth": return `assert(${this._pattern.predicatePattern.toString()}, ${this._pattern.objectPattern.toString()})`;
2613
+ case "WithPredicate": return `assertpred(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2614
+ case "WithObject": return `assertobj(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2679
2615
  }
2680
2616
  }
2681
2617
  /**
@@ -2687,10 +2623,6 @@ var AssertionsPattern = class AssertionsPattern {
2687
2623
  case "Any": return true;
2688
2624
  case "WithPredicate":
2689
2625
  case "WithObject": return this._pattern.pattern === other._pattern.pattern;
2690
- case "WithBoth": {
2691
- const otherBoth = other._pattern;
2692
- return this._pattern.predicatePattern === otherBoth.predicatePattern && this._pattern.objectPattern === otherBoth.objectPattern;
2693
- }
2694
2626
  }
2695
2627
  }
2696
2628
  /**
@@ -2701,11 +2633,9 @@ var AssertionsPattern = class AssertionsPattern {
2701
2633
  case "Any": return 0;
2702
2634
  case "WithPredicate": return 1;
2703
2635
  case "WithObject": return 2;
2704
- case "WithBoth": return 3;
2705
2636
  }
2706
2637
  }
2707
2638
  };
2708
-
2709
2639
  //#endregion
2710
2640
  //#region src/pattern/structure/digest-pattern.ts
2711
2641
  let createStructureDigestPattern;
@@ -2731,12 +2661,6 @@ var DigestPattern = class DigestPattern {
2731
2661
  this._pattern = pattern;
2732
2662
  }
2733
2663
  /**
2734
- * Creates a new DigestPattern that matches any digest.
2735
- */
2736
- static any() {
2737
- return new DigestPattern({ type: "Any" });
2738
- }
2739
- /**
2740
2664
  * Creates a new DigestPattern that matches the exact digest.
2741
2665
  */
2742
2666
  static digest(digest) {
@@ -2774,9 +2698,6 @@ var DigestPattern = class DigestPattern {
2774
2698
  const digestData = digest.data();
2775
2699
  let isHit = false;
2776
2700
  switch (this._pattern.type) {
2777
- case "Any":
2778
- isHit = true;
2779
- break;
2780
2701
  case "Digest":
2781
2702
  isHit = digest.equals(this._pattern.digest);
2782
2703
  break;
@@ -2814,7 +2735,6 @@ var DigestPattern = class DigestPattern {
2814
2735
  }
2815
2736
  toString() {
2816
2737
  switch (this._pattern.type) {
2817
- case "Any": return "digest";
2818
2738
  case "Digest": return `digest(${this._pattern.digest.hex()})`;
2819
2739
  case "Prefix": return `digest(${bytesToHex(this._pattern.prefix)})`;
2820
2740
  case "BinaryRegex": return `digest(/${this._pattern.regex.source}/)`;
@@ -2822,17 +2742,26 @@ var DigestPattern = class DigestPattern {
2822
2742
  }
2823
2743
  /**
2824
2744
  * Equality comparison.
2745
+ *
2746
+ * `Prefix` comparison is case-insensitive on the *hex representation* to
2747
+ * mirror Rust's `eq_ignore_ascii_case` (which compares the underlying
2748
+ * `Vec<u8>` of hex bytes byte-for-byte modulo ASCII case). For raw byte
2749
+ * prefixes that happen to be ASCII, this is an ordinary byte compare.
2825
2750
  */
2826
2751
  equals(other) {
2827
2752
  if (this._pattern.type !== other._pattern.type) return false;
2828
2753
  switch (this._pattern.type) {
2829
- case "Any": return true;
2830
2754
  case "Digest": return this._pattern.digest.equals(other._pattern.digest);
2831
2755
  case "Prefix": {
2832
2756
  const thisPrefix = this._pattern.prefix;
2833
2757
  const otherPrefix = other._pattern.prefix;
2834
2758
  if (thisPrefix.length !== otherPrefix.length) return false;
2835
- for (let i = 0; i < thisPrefix.length; i++) if (thisPrefix[i] !== otherPrefix[i]) return false;
2759
+ for (let i = 0; i < thisPrefix.length; i++) {
2760
+ const a = thisPrefix[i];
2761
+ const b = otherPrefix[i];
2762
+ if (a === b) continue;
2763
+ if ((a >= 65 && a <= 90 ? a + 32 : a) !== (b >= 65 && b <= 90 ? b + 32 : b)) return false;
2764
+ }
2836
2765
  return true;
2837
2766
  }
2838
2767
  case "BinaryRegex": return this._pattern.regex.source === other._pattern.regex.source;
@@ -2843,7 +2772,6 @@ var DigestPattern = class DigestPattern {
2843
2772
  */
2844
2773
  hashCode() {
2845
2774
  switch (this._pattern.type) {
2846
- case "Any": return 0;
2847
2775
  case "Digest": {
2848
2776
  const data = this._pattern.digest.data().slice(0, 8);
2849
2777
  let hash = 0;
@@ -2852,7 +2780,10 @@ var DigestPattern = class DigestPattern {
2852
2780
  }
2853
2781
  case "Prefix": {
2854
2782
  let hash = 0;
2855
- for (const byte of this._pattern.prefix) hash = hash * 31 + byte | 0;
2783
+ for (const byte of this._pattern.prefix) {
2784
+ const folded = byte >= 65 && byte <= 90 ? byte + 32 : byte;
2785
+ hash = hash * 31 + folded | 0;
2786
+ }
2856
2787
  return hash;
2857
2788
  }
2858
2789
  case "BinaryRegex": {
@@ -2863,7 +2794,6 @@ var DigestPattern = class DigestPattern {
2863
2794
  }
2864
2795
  }
2865
2796
  };
2866
-
2867
2797
  //#endregion
2868
2798
  //#region src/pattern/structure/node-pattern.ts
2869
2799
  let createStructureNodePattern;
@@ -2905,28 +2835,19 @@ var NodePattern = class NodePattern {
2905
2835
  });
2906
2836
  }
2907
2837
  /**
2908
- * Creates a new NodePattern with a subject pattern constraint.
2909
- */
2910
- static withSubject(subjectPattern) {
2911
- return new NodePattern({
2912
- type: "WithSubject",
2913
- subjectPattern
2914
- });
2915
- }
2916
- /**
2917
2838
  * Gets the pattern type.
2918
2839
  */
2919
2840
  get patternType() {
2920
2841
  return this._pattern;
2921
2842
  }
2922
2843
  /**
2923
- * Gets the subject pattern if this is a WithSubject type, undefined otherwise.
2844
+ * Returns the subject pattern, if any. Rust's `NodePattern` does not carry
2845
+ * subject patterns, so this always returns `undefined`.
2924
2846
  */
2925
- subjectPattern() {
2926
- return this._pattern.type === "WithSubject" ? this._pattern.subjectPattern : void 0;
2927
- }
2847
+ subjectPattern() {}
2928
2848
  /**
2929
- * Gets the assertion patterns (empty array if none).
2849
+ * Returns the assertion patterns. Rust's `NodePattern` does not carry
2850
+ * assertion sub-patterns, so this always returns an empty array.
2930
2851
  */
2931
2852
  assertionPatterns() {
2932
2853
  return [];
@@ -2941,9 +2862,6 @@ var NodePattern = class NodePattern {
2941
2862
  case "AssertionsInterval":
2942
2863
  isHit = this._pattern.interval.contains(haystack.assertions().length);
2943
2864
  break;
2944
- case "WithSubject":
2945
- isHit = true;
2946
- break;
2947
2865
  }
2948
2866
  return [isHit ? [[haystack]] : [], /* @__PURE__ */ new Map()];
2949
2867
  }
@@ -2964,7 +2882,6 @@ var NodePattern = class NodePattern {
2964
2882
  switch (this._pattern.type) {
2965
2883
  case "Any": return "node";
2966
2884
  case "AssertionsInterval": return `node(${this._pattern.interval.toString()})`;
2967
- case "WithSubject": return `node(${this._pattern.subjectPattern.toString()})`;
2968
2885
  }
2969
2886
  }
2970
2887
  /**
@@ -2975,7 +2892,6 @@ var NodePattern = class NodePattern {
2975
2892
  switch (this._pattern.type) {
2976
2893
  case "Any": return true;
2977
2894
  case "AssertionsInterval": return this._pattern.interval.equals(other._pattern.interval);
2978
- case "WithSubject": return this._pattern.subjectPattern === other._pattern.subjectPattern;
2979
2895
  }
2980
2896
  }
2981
2897
  /**
@@ -2985,11 +2901,9 @@ var NodePattern = class NodePattern {
2985
2901
  switch (this._pattern.type) {
2986
2902
  case "Any": return 0;
2987
2903
  case "AssertionsInterval": return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
2988
- case "WithSubject": return 1;
2989
2904
  }
2990
2905
  }
2991
2906
  };
2992
-
2993
2907
  //#endregion
2994
2908
  //#region src/pattern/structure/obscured-pattern.ts
2995
2909
  let createStructureObscuredPattern;
@@ -3093,16 +3007,19 @@ var ObscuredPattern = class ObscuredPattern {
3093
3007
  }
3094
3008
  }
3095
3009
  };
3096
-
3097
3010
  //#endregion
3098
3011
  //#region src/pattern/structure/wrapped-pattern.ts
3099
3012
  let createStructureWrappedPattern;
3013
+ let createAnyPattern;
3100
3014
  let dispatchPatternPathsWithCaptures;
3101
3015
  let dispatchPatternCompile;
3102
3016
  let dispatchPatternToString$1;
3103
3017
  function registerWrappedPatternFactory(factory) {
3104
3018
  createStructureWrappedPattern = factory;
3105
3019
  }
3020
+ function registerWrappedPatternAny(factory) {
3021
+ createAnyPattern = factory;
3022
+ }
3106
3023
  function registerWrappedPatternDispatch(dispatch) {
3107
3024
  dispatchPatternPathsWithCaptures = dispatch.pathsWithCaptures;
3108
3025
  dispatchPatternCompile = dispatch.compile;
@@ -3136,10 +3053,15 @@ var WrappedPattern = class WrappedPattern {
3136
3053
  }
3137
3054
  /**
3138
3055
  * Creates a new WrappedPattern that matches any wrapped envelope and descends into it.
3139
- * Note: This requires Pattern.any() to be available, so it's set up during registration.
3056
+ *
3057
+ * Mirrors Rust `WrappedPattern::unwrap()` which delegates to
3058
+ * `Self::unwrap_matching(Pattern::any())`. The `any` factory is wired in
3059
+ * during module-load registration to break the circular import on the
3060
+ * top-level `Pattern` type.
3140
3061
  */
3141
3062
  static unwrap() {
3142
- return new WrappedPattern({ type: "Any" });
3063
+ if (createAnyPattern === void 0) throw new Error("WrappedPattern.unwrap() requires Pattern.any factory; not registered");
3064
+ return WrappedPattern.unwrapMatching(createAnyPattern());
3143
3065
  }
3144
3066
  /**
3145
3067
  * Gets the pattern type.
@@ -3215,9 +3137,9 @@ var WrappedPattern = class WrappedPattern {
3215
3137
  switch (this._pattern.type) {
3216
3138
  case "Any": return "wrapped";
3217
3139
  case "Unwrap": {
3218
- const patternStr = dispatchPatternToString$1 !== void 0 ? dispatchPatternToString$1(this._pattern.pattern) : "*";
3219
- if (patternStr === "*") return "unwrap";
3220
- return `unwrap(${patternStr})`;
3140
+ const inner = this._pattern.pattern;
3141
+ if (inner.type === "Meta" && inner.pattern.type === "Any") return "unwrap";
3142
+ return `unwrap(${dispatchPatternToString$1 !== void 0 ? dispatchPatternToString$1(inner) : "?"})`;
3221
3143
  }
3222
3144
  }
3223
3145
  }
@@ -3236,7 +3158,6 @@ var WrappedPattern = class WrappedPattern {
3236
3158
  return this._pattern.type === "Any" ? 0 : 1;
3237
3159
  }
3238
3160
  };
3239
-
3240
3161
  //#endregion
3241
3162
  //#region src/pattern/structure/index.ts
3242
3163
  /**
@@ -3408,7 +3329,6 @@ function structurePatternToString(pattern) {
3408
3329
  case "Wrapped": return pattern.pattern.toString();
3409
3330
  }
3410
3331
  }
3411
-
3412
3332
  //#endregion
3413
3333
  //#region src/pattern/vm.ts
3414
3334
  let _patternPathsWithCaptures$1;
@@ -3443,10 +3363,7 @@ function axisChildren(axis, env) {
3443
3363
  case "Wrapped":
3444
3364
  if (envCase.type === "node") {
3445
3365
  const subject = envCase.subject;
3446
- if (subject.isWrapped()) {
3447
- const unwrapped = subject.unwrap();
3448
- if (unwrapped !== void 0) return [[unwrapped, "Content"]];
3449
- }
3366
+ if (subject.isWrapped()) return [[subject.tryUnwrap(), "Content"]];
3450
3367
  } else if (envCase.type === "wrapped") return [[envCase.envelope, "Content"]];
3451
3368
  return [];
3452
3369
  }
@@ -4055,7 +3972,6 @@ function compileMetaPattern(pattern, code, literals, captureNames) {
4055
3972
  }
4056
3973
  }
4057
3974
  }
4058
-
4059
3975
  //#endregion
4060
3976
  //#region src/pattern/meta/any-pattern.ts
4061
3977
  let createMetaAnyPattern;
@@ -4107,13 +4023,7 @@ var AnyPattern = class AnyPattern {
4107
4023
  return 0;
4108
4024
  }
4109
4025
  };
4110
-
4111
- //#endregion
4112
- //#region src/pattern/meta/and-pattern.ts
4113
- let createMetaAndPattern;
4114
- function registerAndPatternFactory(factory) {
4115
- createMetaAndPattern = factory;
4116
- }
4026
+ function registerAndPatternFactory(factory) {}
4117
4027
  /**
4118
4028
  * A pattern that matches if all contained patterns match.
4119
4029
  *
@@ -4169,13 +4079,7 @@ var AndPattern = class AndPattern {
4169
4079
  return this._patterns.length;
4170
4080
  }
4171
4081
  };
4172
-
4173
- //#endregion
4174
- //#region src/pattern/meta/or-pattern.ts
4175
- let createMetaOrPattern;
4176
- function registerOrPatternFactory(factory) {
4177
- createMetaOrPattern = factory;
4178
- }
4082
+ function registerOrPatternFactory(factory) {}
4179
4083
  /**
4180
4084
  * A pattern that matches if any contained pattern matches.
4181
4085
  *
@@ -4269,13 +4173,7 @@ var OrPattern = class OrPattern {
4269
4173
  return this._patterns.length;
4270
4174
  }
4271
4175
  };
4272
-
4273
- //#endregion
4274
- //#region src/pattern/meta/not-pattern.ts
4275
- let createMetaNotPattern;
4276
- function registerNotPatternFactory(factory) {
4277
- createMetaNotPattern = factory;
4278
- }
4176
+ function registerNotPatternFactory(factory) {}
4279
4177
  /**
4280
4178
  * A pattern that negates another pattern; matches when the inner pattern does not match.
4281
4179
  *
@@ -4334,13 +4232,7 @@ var NotPattern = class NotPattern {
4334
4232
  return 1;
4335
4233
  }
4336
4234
  };
4337
-
4338
- //#endregion
4339
- //#region src/pattern/meta/capture-pattern.ts
4340
- let createMetaCapturePattern;
4341
- function registerCapturePatternFactory(factory) {
4342
- createMetaCapturePattern = factory;
4343
- }
4235
+ function registerCapturePatternFactory(factory) {}
4344
4236
  /**
4345
4237
  * A pattern that captures a match with a name.
4346
4238
  *
@@ -4419,13 +4311,7 @@ var CapturePattern = class CapturePattern {
4419
4311
  return hash;
4420
4312
  }
4421
4313
  };
4422
-
4423
- //#endregion
4424
- //#region src/pattern/meta/search-pattern.ts
4425
- let createMetaSearchPattern;
4426
- function registerSearchPatternFactory(factory) {
4427
- createMetaSearchPattern = factory;
4428
- }
4314
+ function registerSearchPatternFactory(factory) {}
4429
4315
  /**
4430
4316
  * A pattern that searches the entire envelope tree for matches.
4431
4317
  *
@@ -4475,30 +4361,20 @@ var SearchPattern = class SearchPattern {
4475
4361
  return [uniquePaths, /* @__PURE__ */ new Map()];
4476
4362
  }
4477
4363
  /**
4478
- * Walk the envelope tree recursively.
4364
+ * Walk the envelope tree using the canonical `Envelope.walk` traversal.
4365
+ *
4366
+ * Mirrors Rust `bc_envelope::Envelope::walk(false, vec![], visitor)`
4367
+ * which is what `SearchPattern::paths_with_captures` uses. The earlier
4368
+ * port hand-rolled a recursion that double-recursed assertions and
4369
+ * stepped through wrapped subjects manually, producing a different
4370
+ * path order (and extra duplicates that the digest-set deduplication
4371
+ * would partially mask).
4479
4372
  */
4480
4373
  _walkEnvelope(envelope, pathToCurrent, visitor) {
4481
- visitor(envelope, pathToCurrent);
4482
- const subject = envelope.subject();
4483
- const newPath = [...pathToCurrent, envelope];
4484
- if (!subject.digest().equals(envelope.digest())) this._walkEnvelope(subject, newPath, visitor);
4485
- for (const assertion of envelope.assertions()) {
4486
- this._walkEnvelope(assertion, newPath, visitor);
4487
- const predicate = assertion.asPredicate?.();
4488
- if (predicate !== void 0) {
4489
- const assertionPath = [...newPath, assertion];
4490
- this._walkEnvelope(predicate, assertionPath, visitor);
4491
- }
4492
- const object = assertion.asObject?.();
4493
- if (object !== void 0) {
4494
- const assertionPath = [...newPath, assertion];
4495
- this._walkEnvelope(object, assertionPath, visitor);
4496
- }
4497
- }
4498
- if (subject.isWrapped()) {
4499
- const unwrapped = subject.tryUnwrap?.();
4500
- if (unwrapped !== void 0) this._walkEnvelope(unwrapped, newPath, visitor);
4501
- }
4374
+ envelope.walk(false, pathToCurrent, (current, _level, _edge, state) => {
4375
+ visitor(current, state);
4376
+ return [[...state, current], false];
4377
+ });
4502
4378
  }
4503
4379
  paths(haystack) {
4504
4380
  return this.pathsWithCaptures(haystack)[0];
@@ -4552,13 +4428,7 @@ function collectCaptureNames(pattern, out) {
4552
4428
  const p = pattern;
4553
4429
  if (p.collectCaptureNames !== void 0) p.collectCaptureNames(out);
4554
4430
  }
4555
-
4556
- //#endregion
4557
- //#region src/pattern/meta/traverse-pattern.ts
4558
- let createMetaTraversePattern;
4559
- function registerTraversePatternFactory(factory) {
4560
- createMetaTraversePattern = factory;
4561
- }
4431
+ function registerTraversePatternFactory(factory) {}
4562
4432
  let _patternPathsWithCaptures;
4563
4433
  let _patternCompile;
4564
4434
  let _patternIsComplex;
@@ -4633,7 +4503,7 @@ var TraversePattern = class TraversePattern {
4633
4503
  return _patternIsComplex(this._first) || this._rest !== void 0;
4634
4504
  }
4635
4505
  toString() {
4636
- return this.patterns().map((p) => p.toString()).join(" -> ");
4506
+ return this.patterns().map((p) => dispatchPatternToString(p)).join(" -> ");
4637
4507
  }
4638
4508
  /**
4639
4509
  * Equality comparison.
@@ -4652,13 +4522,7 @@ var TraversePattern = class TraversePattern {
4652
4522
  return this.patterns().length;
4653
4523
  }
4654
4524
  };
4655
-
4656
- //#endregion
4657
- //#region src/pattern/meta/group-pattern.ts
4658
- let createMetaGroupPattern;
4659
- function registerGroupPatternFactory(factory) {
4660
- createMetaGroupPattern = factory;
4661
- }
4525
+ function registerGroupPatternFactory(factory) {}
4662
4526
  /**
4663
4527
  * A pattern that matches with repetition.
4664
4528
  *
@@ -4734,7 +4598,6 @@ var GroupPattern = class GroupPattern {
4734
4598
  return this._quantifier.min() * 31 + (this._quantifier.max() ?? 0);
4735
4599
  }
4736
4600
  };
4737
-
4738
4601
  //#endregion
4739
4602
  //#region src/pattern/meta/index.ts
4740
4603
  /**
@@ -4924,7 +4787,6 @@ function collectCaptureNamesFromPattern(pattern, out) {
4924
4787
  const p = pattern;
4925
4788
  if (p.collectCaptureNames !== void 0) p.collectCaptureNames(out);
4926
4789
  }
4927
-
4928
4790
  //#endregion
4929
4791
  //#region src/pattern/dcbor-integration.ts
4930
4792
  /**
@@ -4990,17 +4852,44 @@ function convertValuePatternToEnvelopePattern(valuePattern) {
4990
4852
  */
4991
4853
  function convertStructurePatternToEnvelopePattern(structurePattern) {
4992
4854
  switch (structurePattern.type) {
4993
- case "Array": return ok({
4994
- type: "Leaf",
4995
- pattern: leafArray(ArrayPattern.fromDcborPattern({
4996
- kind: "Structure",
4997
- pattern: structurePattern
4998
- }))
4999
- });
5000
- case "Map": return ok({
5001
- type: "Leaf",
5002
- pattern: leafMap(MapPattern.any())
5003
- });
4855
+ case "Array": {
4856
+ const inner = structurePattern.pattern;
4857
+ let arrayPattern;
4858
+ switch (inner.variant) {
4859
+ case "Any":
4860
+ arrayPattern = ArrayPattern.any();
4861
+ break;
4862
+ case "Length":
4863
+ arrayPattern = ArrayPattern.fromInterval(inner.length);
4864
+ break;
4865
+ case "Elements":
4866
+ arrayPattern = ArrayPattern.fromDcborArrayPattern(inner);
4867
+ break;
4868
+ }
4869
+ return ok({
4870
+ type: "Leaf",
4871
+ pattern: leafArray(arrayPattern)
4872
+ });
4873
+ }
4874
+ case "Map": {
4875
+ const inner = structurePattern.pattern;
4876
+ let mapPattern;
4877
+ switch (inner.variant) {
4878
+ case "Any":
4879
+ mapPattern = MapPattern.any();
4880
+ break;
4881
+ case "Length":
4882
+ mapPattern = MapPattern.fromInterval(inner.length);
4883
+ break;
4884
+ case "Constraints":
4885
+ mapPattern = MapPattern.any();
4886
+ break;
4887
+ }
4888
+ return ok({
4889
+ type: "Leaf",
4890
+ pattern: leafMap(mapPattern)
4891
+ });
4892
+ }
5004
4893
  case "Tagged": return ok({
5005
4894
  type: "Leaf",
5006
4895
  pattern: leafTag(TaggedPattern.fromDcborPattern(structurePattern.pattern))
@@ -5057,7 +4946,169 @@ function convertMetaPatternToEnvelopePattern(metaPattern, originalPattern) {
5057
4946
  });
5058
4947
  }
5059
4948
  }
5060
-
4949
+ //#endregion
4950
+ //#region src/parse/utils.ts
4951
+ let createCborPattern;
4952
+ let createCborPatternFromDcbor;
4953
+ let createAnyArray;
4954
+ let createArrayWithCount;
4955
+ let createArrayWithRange;
4956
+ let createArrayFromDcborPattern;
4957
+ /**
4958
+ * Register pattern factory functions.
4959
+ * This is called by the pattern module to avoid circular dependencies.
4960
+ */
4961
+ function registerPatternFactories(factories) {
4962
+ createCborPattern = factories.cborPattern;
4963
+ createCborPatternFromDcbor = factories.cborPatternFromDcbor;
4964
+ createAnyArray = factories.anyArray;
4965
+ createArrayWithCount = factories.arrayWithCount;
4966
+ createArrayWithRange = factories.arrayWithRange;
4967
+ createArrayFromDcborPattern = factories.arrayFromDcborPattern;
4968
+ }
4969
+ /**
4970
+ * Skips whitespace in the source string.
4971
+ *
4972
+ * @param src - The source string
4973
+ * @param pos - The current position (modified in place)
4974
+ */
4975
+ function skipWs$1(src, pos) {
4976
+ while (pos.value < src.length) {
4977
+ const ch = src[pos.value];
4978
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f") pos.value++;
4979
+ else break;
4980
+ }
4981
+ }
4982
+ /**
4983
+ * Parses a CBOR value or dcbor-pattern expression.
4984
+ *
4985
+ * @param src - The source string
4986
+ * @returns The parsed pattern and consumed character count, or an error
4987
+ */
4988
+ function parseCborInner(src) {
4989
+ if (createCborPattern === void 0 || createCborPatternFromDcbor === void 0) return err(unknown());
4990
+ const pos = { value: 0 };
4991
+ skipWs$1(src, pos);
4992
+ if (src[pos.value] === "/") {
4993
+ pos.value++;
4994
+ const start = pos.value;
4995
+ let escape = false;
4996
+ while (pos.value < src.length) {
4997
+ const b = src[pos.value];
4998
+ pos.value++;
4999
+ if (escape) {
5000
+ escape = false;
5001
+ continue;
5002
+ }
5003
+ if (b === "\\") {
5004
+ escape = true;
5005
+ continue;
5006
+ }
5007
+ if (b === "/") {
5008
+ const parseResult = parse$1(src.slice(start, pos.value - 1));
5009
+ if (!parseResult.ok) return err(invalidPattern({
5010
+ start,
5011
+ end: pos.value - 1
5012
+ }));
5013
+ skipWs$1(src, pos);
5014
+ return ok([createCborPatternFromDcbor(parseResult.value), pos.value]);
5015
+ }
5016
+ }
5017
+ return err(unterminatedRegex({
5018
+ start: start - 1,
5019
+ end: pos.value
5020
+ }));
5021
+ }
5022
+ if (src.slice(pos.value, pos.value + 3) === "ur:") {
5023
+ const parseResult = parseDcborItemPartial(src.slice(pos.value));
5024
+ if (!parseResult.ok) return err(unknown());
5025
+ const [cborValue, consumed] = parseResult.value;
5026
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
5027
+ }
5028
+ const parseResult = parseDcborItemPartial(src.slice(pos.value));
5029
+ if (!parseResult.ok) return err(unknown());
5030
+ const [cborValue, consumed] = parseResult.value;
5031
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
5032
+ }
5033
+ /**
5034
+ * Parses an array pattern inner content.
5035
+ *
5036
+ * @param src - The source string (content between [ and ])
5037
+ * @returns The parsed pattern and consumed character count, or an error
5038
+ */
5039
+ function parseArrayInner(src) {
5040
+ if (createAnyArray === void 0 || createArrayWithCount === void 0 || createArrayWithRange === void 0 || createArrayFromDcborPattern === void 0) return err(unknown());
5041
+ const pos = { value: 0 };
5042
+ skipWs$1(src, pos);
5043
+ if (src[pos.value] === "*") {
5044
+ pos.value++;
5045
+ skipWs$1(src, pos);
5046
+ return ok([createAnyArray(), pos.value]);
5047
+ }
5048
+ if (src[pos.value] === "{") {
5049
+ pos.value++;
5050
+ skipWs$1(src, pos);
5051
+ const startPos = pos.value;
5052
+ while (pos.value < src.length && src[pos.value] !== void 0 && /\d/.test(src[pos.value])) pos.value++;
5053
+ if (startPos === pos.value) return err(invalidRange({
5054
+ start: pos.value,
5055
+ end: pos.value
5056
+ }));
5057
+ const firstNum = parseInt(src.slice(startPos, pos.value), 10);
5058
+ if (Number.isNaN(firstNum)) return err(invalidNumberFormat({
5059
+ start: startPos,
5060
+ end: pos.value
5061
+ }));
5062
+ skipWs$1(src, pos);
5063
+ if (pos.value >= src.length) return err(unexpectedEndOfInput());
5064
+ const ch = src[pos.value];
5065
+ if (ch === "}") {
5066
+ pos.value++;
5067
+ skipWs$1(src, pos);
5068
+ return ok([createArrayWithCount(firstNum), pos.value]);
5069
+ }
5070
+ if (ch === ",") {
5071
+ pos.value++;
5072
+ skipWs$1(src, pos);
5073
+ if (pos.value >= src.length) return err(unexpectedEndOfInput());
5074
+ const nextCh = src[pos.value];
5075
+ if (nextCh === "}") {
5076
+ pos.value++;
5077
+ skipWs$1(src, pos);
5078
+ return ok([createArrayWithRange(firstNum, void 0), pos.value]);
5079
+ }
5080
+ if (nextCh !== void 0 && /\d/.test(nextCh)) {
5081
+ const secondStart = pos.value;
5082
+ while (pos.value < src.length && src[pos.value] !== void 0 && /\d/.test(src[pos.value])) pos.value++;
5083
+ const secondNum = parseInt(src.slice(secondStart, pos.value), 10);
5084
+ if (Number.isNaN(secondNum)) return err(invalidNumberFormat({
5085
+ start: secondStart,
5086
+ end: pos.value
5087
+ }));
5088
+ skipWs$1(src, pos);
5089
+ if (pos.value >= src.length || src[pos.value] !== "}") return err(unexpectedEndOfInput());
5090
+ pos.value++;
5091
+ skipWs$1(src, pos);
5092
+ return ok([createArrayWithRange(firstNum, secondNum), pos.value]);
5093
+ }
5094
+ return err(invalidRange({
5095
+ start: pos.value,
5096
+ end: pos.value
5097
+ }));
5098
+ }
5099
+ return err(invalidRange({
5100
+ start: pos.value,
5101
+ end: pos.value
5102
+ }));
5103
+ }
5104
+ const parseResult = parse$1(`[${src.slice(pos.value)}]`);
5105
+ if (!parseResult.ok) return err(invalidPattern({
5106
+ start: pos.value,
5107
+ end: src.length
5108
+ }));
5109
+ const consumed = src.length - pos.value;
5110
+ return ok([createArrayFromDcborPattern(parseResult.value), consumed]);
5111
+ }
5061
5112
  //#endregion
5062
5113
  //#region src/pattern/index.ts
5063
5114
  /**
@@ -5573,6 +5624,12 @@ function registerAllFactories() {
5573
5624
  compile: patternCompile,
5574
5625
  toString: patternToString
5575
5626
  });
5627
+ registerWrappedPatternAny(any);
5628
+ registerAssertionsPatternToStringDispatch(patternToString);
5629
+ registerSubjectPatternDispatch({
5630
+ compile: patternCompile,
5631
+ toString: patternToString
5632
+ });
5576
5633
  registerAnyPatternFactory((p) => patternMeta(metaAny(p)));
5577
5634
  registerAndPatternFactory((p) => patternMeta(metaAnd(p)));
5578
5635
  registerOrPatternFactory((p) => patternMeta(metaOr(p)));
@@ -5584,6 +5641,14 @@ function registerAllFactories() {
5584
5641
  }
5585
5642
  registerAllFactories();
5586
5643
  registerVMPatternFunctions(patternPathsWithCaptures, patternMatches, patternPaths);
5644
+ registerPatternFactories({
5645
+ cborPattern: (value) => cborValue(value),
5646
+ cborPatternFromDcbor: (pattern) => cborPattern(pattern),
5647
+ anyArray,
5648
+ arrayWithCount: (count) => patternLeaf(leafArray(ArrayPattern.count(count))),
5649
+ arrayWithRange: (min, max) => patternLeaf(leafArray(ArrayPattern.interval(min, max))),
5650
+ arrayFromDcborPattern: (pattern) => patternLeaf(leafArray(ArrayPattern.fromDcborPattern(pattern)))
5651
+ });
5587
5652
  registerPatternMatchFn(patternMatches);
5588
5653
  registerPatternDispatchFns({
5589
5654
  pathsWithCaptures: patternPathsWithCaptures,
@@ -5593,7 +5658,6 @@ registerPatternDispatchFns({
5593
5658
  toString: patternToString
5594
5659
  });
5595
5660
  registerTraverseDispatchFunctions(patternPathsWithCaptures, patternCompile, patternIsComplex);
5596
-
5597
5661
  //#endregion
5598
5662
  //#region src/parse/token.ts
5599
5663
  /**
@@ -6337,307 +6401,199 @@ var Lexer = class {
6337
6401
  }
6338
6402
  }
6339
6403
  };
6340
-
6341
6404
  //#endregion
6342
- //#region src/parse/index.ts
6405
+ //#region src/parse/leaf/array-parser.ts
6343
6406
  /**
6344
6407
  * Copyright © 2023-2026 Blockchain Commons, LLC
6345
6408
  * Copyright © 2025-2026 Parity Technologies
6346
6409
  *
6410
+ * Array parser — port of `bc-envelope-pattern-rust`
6411
+ * `parse/leaf/array_parser.rs`.
6347
6412
  *
6348
- * @bcts/envelope-pattern - Parser entry point
6349
- *
6350
- * This is a 1:1 TypeScript port of bc-envelope-pattern-rust parse/mod.rs
6351
- * Recursive descent parser for Gordian Envelope pattern syntax.
6413
+ * Mirrors Rust's flow exactly: after the `[` token has been consumed,
6414
+ * delegate to `utils::parseArrayInner` (which handles `*`, `{n}`, `{n,m}`,
6415
+ * `{n,}` directly and otherwise wraps the body in `[...]` and re-parses
6416
+ * via dcbor-pattern), then expect a closing `]`.
6352
6417
  *
6353
- * @module envelope-pattern/parse
6418
+ * @module envelope-pattern/parse/leaf/array-parser
6354
6419
  */
6420
+ function parseArray(lexer) {
6421
+ const inner = parseArrayInner(lexer.remainder());
6422
+ if (!inner.ok) return inner;
6423
+ const [pattern, consumed] = inner.value;
6424
+ lexer.bump(consumed);
6425
+ const close = lexer.next();
6426
+ if (close === void 0) return err(expectedCloseBracket(lexer.span()));
6427
+ if (close.token.type !== "BracketClose") return err(unexpectedToken(close.token, close.span));
6428
+ return ok(pattern);
6429
+ }
6430
+ //#endregion
6431
+ //#region src/parse/leaf/cbor-parser.ts
6355
6432
  /**
6356
- * Parse a pattern expression string into a Pattern.
6433
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6434
+ * Copyright © 2025-2026 Parity Technologies
6435
+ *
6436
+ * CBOR pattern parser — port of `bc-envelope-pattern-rust`
6437
+ * `parse/leaf/cbor_parser.rs`.
6438
+ *
6439
+ * Mirrors Rust's flow: lookahead for `(`. If absent, return `any_cbor()`.
6440
+ * Otherwise consume the `(`, delegate to `parseCborInner` (handles
6441
+ * `/regex/`, `ur:…`, and CBOR diagnostic notation), and expect a closing
6442
+ * `)`.
6443
+ *
6444
+ * @module envelope-pattern/parse/leaf/cbor-parser
6357
6445
  */
6358
- function parse(input) {
6359
- const lexer = new Lexer(input);
6360
- const result = parseOr(lexer);
6361
- if (!result.ok) {
6362
- const dcborResult = parse$1(input);
6363
- if (dcborResult.ok) return convertDcborPatternToEnvelopePattern$1(dcborResult.value);
6364
- return result;
6365
- }
6366
- const next = lexer.next();
6367
- if (next !== void 0) return err(extraData(next.span));
6368
- return result;
6369
- }
6370
- /**
6371
- * Parse a pattern, allowing extra data after the pattern.
6372
- */
6373
- function parsePartial(input) {
6374
- const lexer = new Lexer(input);
6375
- const result = parseOr(lexer);
6376
- if (!result.ok) return result;
6377
- return ok([result.value, lexer.position]);
6378
- }
6379
- /**
6380
- * Convert a dcbor-pattern Pattern to an envelope-pattern Pattern.
6381
- */
6382
- function convertDcborPatternToEnvelopePattern$1(_dcborPattern) {
6383
- return ok(any());
6446
+ function parseCbor(lexer) {
6447
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyCbor());
6448
+ lexer.next();
6449
+ const innerResult = parseCborInner(lexer.remainder());
6450
+ if (!innerResult.ok) return innerResult;
6451
+ const [pattern, consumed] = innerResult.value;
6452
+ lexer.bump(consumed);
6453
+ const close = lexer.next();
6454
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6455
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6456
+ return ok(pattern);
6384
6457
  }
6458
+ //#endregion
6459
+ //#region src/parse/leaf/date-parser.ts
6385
6460
  /**
6386
- * Parse an Or expression: expr (| expr)*
6461
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6462
+ * Copyright © 2025-2026 Parity Technologies
6463
+ *
6464
+ * Date content parser — port of `bc-envelope-pattern-rust`
6465
+ * `parse/leaf/date_parser.rs`.
6466
+ *
6467
+ * Mirrors Rust's `Date::from_string`, which accepts a strict ISO-8601
6468
+ * subset, by deferring to dcbor's `CborDate.fromString`. Falls back to JS
6469
+ * `Date.parse` only as a defensive shim — that branch is unreachable for
6470
+ * conformant inputs.
6471
+ *
6472
+ * @module envelope-pattern/parse/leaf/date-parser
6387
6473
  */
6388
- function parseOr(lexer) {
6389
- const patterns = [];
6390
- const first = parseTraverse(lexer);
6391
- if (!first.ok) return first;
6392
- patterns.push(first.value);
6393
- while (true) {
6394
- if (lexer.peekToken()?.token.type !== "Or") break;
6395
- lexer.next();
6396
- const nextExpr = parseTraverse(lexer);
6397
- if (!nextExpr.ok) return nextExpr;
6398
- patterns.push(nextExpr.value);
6399
- }
6400
- if (patterns.length === 1) return ok(patterns[0]);
6401
- return ok(or(patterns));
6402
- }
6403
6474
  /**
6404
- * Parse a Traverse expression: expr (-> expr)*
6475
+ * Parse a date pattern of one of the forms accepted by Rust:
6476
+ *
6477
+ * - `/regex/` (regex match against ISO-8601 string)
6478
+ * - `start...end` (inclusive range)
6479
+ * - `start...` (earliest)
6480
+ * - `...end` (latest)
6481
+ * - `iso-8601` (exact)
6482
+ *
6483
+ * Mirrors `parse_date_content` in Rust; uses `CborDate.fromString` so the
6484
+ * accepted formats match Rust's `bc_envelope::prelude::Date::from_string`
6485
+ * exactly rather than the looser JS `Date.parse`.
6405
6486
  */
6406
- function parseTraverse(lexer) {
6407
- const patterns = [];
6408
- const first = parseAnd(lexer);
6409
- if (!first.ok) return first;
6410
- patterns.push(first.value);
6411
- while (true) {
6412
- if (lexer.peekToken()?.token.type !== "Traverse") break;
6413
- lexer.next();
6414
- const nextExpr = parseAnd(lexer);
6415
- if (!nextExpr.ok) return nextExpr;
6416
- patterns.push(nextExpr.value);
6487
+ function parseDateContent(content, span) {
6488
+ if (content.startsWith("/") && content.endsWith("/") && content.length >= 2) {
6489
+ const regexStr = content.slice(1, -1);
6490
+ try {
6491
+ return ok(dateRegex(new RegExp(regexStr)));
6492
+ } catch {
6493
+ return err(invalidRegex(span));
6494
+ }
6417
6495
  }
6418
- if (patterns.length === 1) return ok(patterns[0]);
6419
- return ok(traverse(patterns));
6420
- }
6421
- /**
6422
- * Parse an And expression: expr (& expr)*
6423
- */
6424
- function parseAnd(lexer) {
6425
- const patterns = [];
6426
- const first = parseNot(lexer);
6427
- if (!first.ok) return first;
6428
- patterns.push(first.value);
6429
- while (true) {
6430
- if (lexer.peekToken()?.token.type !== "And") break;
6431
- lexer.next();
6432
- const nextExpr = parseNot(lexer);
6433
- if (!nextExpr.ok) return nextExpr;
6434
- patterns.push(nextExpr.value);
6496
+ const ellipsisIdx = content.indexOf("...");
6497
+ if (ellipsisIdx !== -1) {
6498
+ const left = content.slice(0, ellipsisIdx);
6499
+ const right = content.slice(ellipsisIdx + 3);
6500
+ if (left.length === 0 && right.length > 0) {
6501
+ const parsed = parseIsoDateStrict(right);
6502
+ if (parsed === void 0) return err(invalidDateFormat(span));
6503
+ return ok(dateLatest(parsed));
6504
+ }
6505
+ if (left.length > 0 && right.length === 0) {
6506
+ const parsed = parseIsoDateStrict(left);
6507
+ if (parsed === void 0) return err(invalidDateFormat(span));
6508
+ return ok(dateEarliest(parsed));
6509
+ }
6510
+ if (left.length > 0 && right.length > 0) {
6511
+ const start = parseIsoDateStrict(left);
6512
+ const end = parseIsoDateStrict(right);
6513
+ if (start === void 0 || end === void 0) return err(invalidDateFormat(span));
6514
+ return ok(dateRange(start, end));
6515
+ }
6516
+ return err(invalidDateFormat(span));
6435
6517
  }
6436
- if (patterns.length === 1) return ok(patterns[0]);
6437
- return ok(and(patterns));
6518
+ const parsed = parseIsoDateStrict(content);
6519
+ if (parsed === void 0) return err(invalidDateFormat(span));
6520
+ return ok(date(parsed));
6438
6521
  }
6439
- /**
6440
- * Parse a Not expression: !? group
6441
- */
6442
- function parseNot(lexer) {
6443
- if (lexer.peekToken()?.token.type === "Not") {
6444
- lexer.next();
6445
- const inner = parseGroup(lexer);
6446
- if (!inner.ok) return inner;
6447
- return ok(notMatching(inner.value));
6522
+ function parseIsoDateStrict(value) {
6523
+ try {
6524
+ return CborDate.fromString(value);
6525
+ } catch {
6526
+ return;
6448
6527
  }
6449
- return parseGroup(lexer);
6450
6528
  }
6529
+ //#endregion
6530
+ //#region src/parse/leaf/known-value-parser.ts
6451
6531
  /**
6452
- * Parse a Group expression: primary quantifier?
6532
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6533
+ * Copyright © 2025-2026 Parity Technologies
6534
+ *
6535
+ * Helpers for parsing the body of a `'…'` (single-quoted) known-value
6536
+ * literal. Mirrors the inline body of Rust's `Token::SingleQuotedPattern`
6537
+ * branch in `parse_primary`:
6538
+ *
6539
+ * - If the contents are a valid `u64`, build `Pattern::known_value(...)`.
6540
+ * - Otherwise, build `Pattern::known_value_named(...)`.
6541
+ *
6542
+ * The earlier port duck-typed a fake `KnownValue`; this version uses the
6543
+ * real `KnownValue` constructor so all subsequent KnownValue methods work
6544
+ * (e.g., `taggedCbor()`, `name()`, etc.).
6545
+ *
6546
+ * @module envelope-pattern/parse/leaf/known-value-parser
6453
6547
  */
6454
- function parseGroup(lexer) {
6455
- const primary = parsePrimary(lexer);
6456
- if (!primary.ok) return primary;
6457
- const next = lexer.peekToken();
6458
- if (next === void 0) return primary;
6459
- const tokenType = next.token.type;
6460
- let quantifier;
6461
- if (tokenType === "RepeatZeroOrMore") {
6462
- lexer.next();
6463
- quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Greedy);
6464
- } else if (tokenType === "RepeatZeroOrMoreLazy") {
6465
- lexer.next();
6466
- quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Lazy);
6467
- } else if (tokenType === "RepeatZeroOrMorePossessive") {
6468
- lexer.next();
6469
- quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Possessive);
6470
- } else if (tokenType === "RepeatOneOrMore") {
6471
- lexer.next();
6472
- quantifier = Quantifier$1.oneOrMore(Reluctance$1.Greedy);
6473
- } else if (tokenType === "RepeatOneOrMoreLazy") {
6474
- lexer.next();
6475
- quantifier = Quantifier$1.oneOrMore(Reluctance$1.Lazy);
6476
- } else if (tokenType === "RepeatOneOrMorePossessive") {
6477
- lexer.next();
6478
- quantifier = Quantifier$1.oneOrMore(Reluctance$1.Possessive);
6479
- } else if (tokenType === "RepeatZeroOrOne") {
6480
- lexer.next();
6481
- quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Greedy);
6482
- } else if (tokenType === "RepeatZeroOrOneLazy") {
6483
- lexer.next();
6484
- quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Lazy);
6485
- } else if (tokenType === "RepeatZeroOrOnePossessive") {
6486
- lexer.next();
6487
- quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Possessive);
6488
- } else if (tokenType === "Range") {
6489
- lexer.next();
6490
- if (!next.token.value.ok) return err(next.token.value.error);
6491
- quantifier = next.token.value.value;
6492
- } else return primary;
6493
- return ok(repeat(primary.value, quantifier.min(), quantifier.max(), quantifier.reluctance()));
6494
- }
6495
6548
  /**
6496
- * Parse a primary expression (atoms and structure keywords).
6549
+ * Maximum value of a Rust `u64`. Used to reject literals that would
6550
+ * silently wrap or lose precision when constructing a `KnownValue`.
6497
6551
  */
6498
- function parsePrimary(lexer) {
6499
- const tokenResult = lexer.next();
6500
- if (tokenResult === void 0) return err(unexpectedEndOfInput());
6501
- const { token, span } = tokenResult;
6502
- switch (token.type) {
6503
- case "Search": return parseSearch(lexer);
6504
- case "Node": return parseNode(lexer);
6505
- case "Assertion": return parseAssertion(lexer);
6506
- case "AssertionPred": return parseAssertionPred(lexer);
6507
- case "AssertionObj": return parseAssertionObj(lexer);
6508
- case "Digest": return parseDigest(lexer);
6509
- case "Obj": return parseObject(lexer);
6510
- case "Obscured": return ok(obscured());
6511
- case "Elided": return ok(elided());
6512
- case "Encrypted": return ok(encrypted());
6513
- case "Compressed": return ok(compressed());
6514
- case "Pred": return parsePredicate(lexer);
6515
- case "Subject": return parseSubject(lexer);
6516
- case "Wrapped": return ok(wrapped());
6517
- case "Unwrap": return parseUnwrap(lexer);
6518
- case "Leaf": return ok(leaf());
6519
- case "GroupName": return parseCapture(lexer, token.name);
6520
- case "ParenOpen": return parseParenGroup(lexer);
6521
- case "Cbor": return parseCbor(lexer);
6522
- case "RepeatZeroOrMore": return ok(any());
6523
- case "BoolKeyword": return ok(anyBool());
6524
- case "BoolTrue": return ok(bool(true));
6525
- case "BoolFalse": return ok(bool(false));
6526
- case "NumberKeyword": return ok(anyNumber());
6527
- case "TextKeyword": return ok(anyText());
6528
- case "StringLiteral":
6529
- if (!token.value.ok) return err(token.value.error);
6530
- return ok(text(token.value.value));
6531
- case "UnsignedInteger":
6532
- if (!token.value.ok) return err(token.value.error);
6533
- return parseNumberRangeOrComparison(lexer, token.value.value);
6534
- case "Integer":
6535
- if (!token.value.ok) return err(token.value.error);
6536
- return parseNumberRangeOrComparison(lexer, token.value.value);
6537
- case "Float":
6538
- if (!token.value.ok) return err(token.value.error);
6539
- return parseNumberRangeOrComparison(lexer, token.value.value);
6540
- case "GreaterThanOrEqual": return parseComparisonNumber(lexer, ">=");
6541
- case "LessThanOrEqual": return parseComparisonNumber(lexer, "<=");
6542
- case "GreaterThan": return parseComparisonNumber(lexer, ">");
6543
- case "LessThan": return parseComparisonNumber(lexer, "<");
6544
- case "NaN": return ok(patternLeaf(leafNumber(NumberPattern.nan())));
6545
- case "Infinity": return ok(number(Infinity));
6546
- case "NegativeInfinity": return ok(number(-Infinity));
6547
- case "Regex":
6548
- if (!token.value.ok) return err(token.value.error);
6549
- try {
6550
- return ok(textRegex(new RegExp(token.value.value)));
6551
- } catch {
6552
- return err(invalidRegex(span));
6553
- }
6554
- case "BracketOpen": return parseArray(lexer);
6555
- case "ByteString": return ok(anyByteString());
6556
- case "HexPattern":
6557
- if (!token.value.ok) return err(token.value.error);
6558
- return ok(byteString(token.value.value));
6559
- case "HexBinaryRegex":
6560
- if (!token.value.ok) return err(token.value.error);
6561
- try {
6562
- return ok(patternLeaf(leafByteString(ByteStringPattern.regex(new RegExp(token.value.value)))));
6563
- } catch {
6564
- return err(invalidRegex(span));
6565
- }
6566
- case "DateKeyword": return ok(anyDate());
6567
- case "DatePattern":
6568
- if (!token.value.ok) return err(token.value.error);
6569
- return parseDateContent(token.value.value, span);
6570
- case "Tagged": return parseTag(lexer);
6571
- case "Known": return ok(anyKnownValue());
6572
- case "SingleQuotedPattern":
6573
- if (!token.value.ok) return err(token.value.error);
6574
- return parseKnownValueContent(token.value.value);
6575
- case "SingleQuotedRegex":
6576
- if (!token.value.ok) return err(token.value.error);
6577
- try {
6578
- return ok(patternLeaf(leafKnownValue(KnownValuePattern.regex(new RegExp(token.value.value)))));
6579
- } catch {
6580
- return err(invalidRegex(span));
6581
- }
6582
- case "Null": return ok(nullPattern());
6583
- case "And":
6584
- case "Or":
6585
- case "Not":
6586
- case "Traverse":
6587
- case "RepeatZeroOrMoreLazy":
6588
- case "RepeatZeroOrMorePossessive":
6589
- case "RepeatOneOrMore":
6590
- case "RepeatOneOrMoreLazy":
6591
- case "RepeatOneOrMorePossessive":
6592
- case "RepeatZeroOrOne":
6593
- case "RepeatZeroOrOneLazy":
6594
- case "RepeatZeroOrOnePossessive":
6595
- case "ParenClose":
6596
- case "BracketClose":
6597
- case "Comma":
6598
- case "Ellipsis":
6599
- case "Range":
6600
- case "Identifier": return err(unexpectedToken(token, span));
6601
- }
6602
- }
6552
+ const U64_MAX = 18446744073709551615n;
6603
6553
  /**
6604
- * Parse a parenthesized group expression.
6554
+ * Parse the inner contents of a `'…'` known-value pattern token.
6555
+ *
6556
+ * Mirrors the Rust dispatch
6557
+ * ```ignore
6558
+ * if let Ok(value) = content.parse::<u64>() {
6559
+ * Pattern::known_value(KnownValue::new(value))
6560
+ * } else {
6561
+ * Pattern::known_value_named(content)
6562
+ * }
6563
+ * ```
6564
+ * but uses BigInt parsing to preserve full `u64` range — the previous
6565
+ * `parseInt(...)` path silently truncated above `2^53-1`.
6605
6566
  */
6606
- function parseParenGroup(lexer) {
6607
- const inner = parseOr(lexer);
6608
- if (!inner.ok) return inner;
6609
- if (lexer.next()?.token.type !== "ParenClose") return err({
6610
- type: "ExpectedCloseParen",
6611
- span: lexer.span()
6612
- });
6613
- return ok(group(inner.value));
6567
+ function parseKnownValueContent(content) {
6568
+ if (isU64Literal(content)) return ok(knownValue(new KnownValue(BigInt(content))));
6569
+ return ok(patternLeaf(leafKnownValue(KnownValuePattern.named(content))));
6614
6570
  }
6615
- /**
6616
- * Parse a capture group: @name pattern
6617
- */
6618
- function parseCapture(lexer, name) {
6619
- const inner = parseGroup(lexer);
6620
- if (!inner.ok) return inner;
6621
- return ok(capture(name, inner.value));
6571
+ function isU64Literal(content) {
6572
+ if (content.length === 0) return false;
6573
+ for (let i = 0; i < content.length; i++) {
6574
+ const c = content.charCodeAt(i);
6575
+ if (c < 48 || c > 57) return false;
6576
+ }
6577
+ try {
6578
+ const value = BigInt(content);
6579
+ return value >= 0n && value <= U64_MAX;
6580
+ } catch {
6581
+ return false;
6582
+ }
6622
6583
  }
6584
+ //#endregion
6585
+ //#region src/parse/leaf/number-parser.ts
6623
6586
  /**
6624
- * Parse a search pattern: search(pattern)
6587
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6588
+ * Copyright © 2025-2026 Parity Technologies
6589
+ *
6590
+ * Number parsers — port of `bc-envelope-pattern-rust` `parse/leaf/number_parser.rs`.
6591
+ *
6592
+ * @module envelope-pattern/parse/leaf/number-parser
6625
6593
  */
6626
- function parseSearch(lexer) {
6627
- if (lexer.next()?.token.type !== "ParenOpen") return err({
6628
- type: "ExpectedOpenParen",
6629
- span: lexer.span()
6630
- });
6631
- const inner = parseOr(lexer);
6632
- if (!inner.ok) return inner;
6633
- if (lexer.next()?.token.type !== "ParenClose") return err({
6634
- type: "ExpectedCloseParen",
6635
- span: lexer.span()
6636
- });
6637
- return ok(search(inner.value));
6638
- }
6639
6594
  /**
6640
- * Parse number with possible range or comparison.
6595
+ * Parses an optional `...end` suffix following an already-consumed number,
6596
+ * mirroring Rust `parse_number_range_or_comparison`.
6641
6597
  */
6642
6598
  function parseNumberRangeOrComparison(lexer, firstValue) {
6643
6599
  const next = lexer.peekToken();
@@ -6647,10 +6603,7 @@ function parseNumberRangeOrComparison(lexer, firstValue) {
6647
6603
  const endToken = lexer.next();
6648
6604
  if (endToken === void 0) return err(unexpectedEndOfInput());
6649
6605
  let endValue;
6650
- if (endToken.token.type === "UnsignedInteger" || endToken.token.type === "Integer") {
6651
- if (!endToken.token.value.ok) return err(endToken.token.value.error);
6652
- endValue = endToken.token.value.value;
6653
- } else if (endToken.token.type === "Float") {
6606
+ if (endToken.token.type === "UnsignedInteger" || endToken.token.type === "Integer" || endToken.token.type === "Float") {
6654
6607
  if (!endToken.token.value.ok) return err(endToken.token.value.error);
6655
6608
  endValue = endToken.token.value.value;
6656
6609
  } else return err(unexpectedToken(endToken.token, endToken.span));
@@ -6659,16 +6612,14 @@ function parseNumberRangeOrComparison(lexer, firstValue) {
6659
6612
  return ok(number(firstValue));
6660
6613
  }
6661
6614
  /**
6662
- * Parse comparison number: >=n, <=n, >n, <n
6615
+ * Parses a number following a comparison operator, mirroring Rust
6616
+ * `parse_comparison_number`.
6663
6617
  */
6664
6618
  function parseComparisonNumber(lexer, op) {
6665
6619
  const numToken = lexer.next();
6666
6620
  if (numToken === void 0) return err(unexpectedEndOfInput());
6667
6621
  let value;
6668
- if (numToken.token.type === "UnsignedInteger" || numToken.token.type === "Integer") {
6669
- if (!numToken.token.value.ok) return err(numToken.token.value.error);
6670
- value = numToken.token.value.value;
6671
- } else if (numToken.token.type === "Float") {
6622
+ if (numToken.token.type === "UnsignedInteger" || numToken.token.type === "Integer" || numToken.token.type === "Float") {
6672
6623
  if (!numToken.token.value.ok) return err(numToken.token.value.error);
6673
6624
  value = numToken.token.value.value;
6674
6625
  } else return err(unexpectedToken(numToken.token, numToken.span));
@@ -6677,307 +6628,818 @@ function parseComparisonNumber(lexer, op) {
6677
6628
  case "<=": return ok(patternLeaf(leafNumber(NumberPattern.lessThanOrEqual(value))));
6678
6629
  case ">": return ok(numberGreaterThan(value));
6679
6630
  case "<": return ok(numberLessThan(value));
6680
- default: return ok(number(value));
6681
6631
  }
6682
6632
  }
6633
+ //#endregion
6634
+ //#region src/parse/leaf/tag-parser.ts
6683
6635
  /**
6684
- * Parse an array pattern.
6636
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6637
+ * Copyright © 2025-2026 Parity Technologies
6638
+ *
6639
+ * Tag parser — port of `bc-envelope-pattern-rust`
6640
+ * `parse/leaf/tag_parser.rs`.
6641
+ *
6642
+ * Mirrors the Rust dispatch exactly: lookahead for `(`; if absent, return
6643
+ * the bare `any_tag()`. Otherwise build a synthetic dcbor-pattern
6644
+ * expression `tagged(<inner>)`, parse it via `@bcts/dcbor-pattern`, and
6645
+ * extract the resulting `TaggedPattern` to wrap as an envelope-pattern
6646
+ * leaf. This keeps the **full** tag selector (number, name, regex)
6647
+ * intact — the previous port discarded the tag value entirely.
6648
+ *
6649
+ * @module envelope-pattern/parse/leaf/tag-parser
6685
6650
  */
6686
- function parseArray(lexer) {
6687
- const first = lexer.peekToken();
6688
- if (first === void 0) return err(unexpectedEndOfInput());
6689
- if (first.token.type === "BracketClose") {
6690
- lexer.next();
6691
- return ok(patternLeaf(leafArray(ArrayPattern.count(0))));
6692
- }
6693
- if (first.token.type === "RepeatZeroOrMore") {
6694
- lexer.next();
6695
- if (lexer.next()?.token.type !== "BracketClose") return err({
6696
- type: "ExpectedCloseBracket",
6697
- span: lexer.span()
6698
- });
6699
- return ok(anyArray());
6700
- }
6701
- const patterns = [];
6702
- while (true) {
6703
- const next = lexer.peekToken();
6704
- if (next === void 0) return err(unexpectedEndOfInput());
6705
- if (next.token.type === "BracketClose") {
6706
- lexer.next();
6707
- break;
6708
- }
6709
- const pattern = parseOr(lexer);
6710
- if (!pattern.ok) return pattern;
6711
- patterns.push(pattern.value);
6712
- const afterPattern = lexer.peekToken();
6713
- if (afterPattern === void 0) return err(unexpectedEndOfInput());
6714
- if (afterPattern.token.type === "Comma") lexer.next();
6715
- else if (afterPattern.token.type !== "BracketClose") return err(unexpectedToken(afterPattern.token, afterPattern.span));
6716
- }
6717
- if (patterns.length === 0) return ok(patternLeaf(leafArray(ArrayPattern.count(0))));
6718
- return ok(patternLeaf(leafArray(ArrayPattern.withPatterns(patterns))));
6719
- }
6720
6651
  /**
6721
- * Parse a tag pattern.
6652
+ * Parse `tagged` and `tagged(...)` patterns.
6722
6653
  */
6723
6654
  function parseTag(lexer) {
6724
- if (lexer.next()?.token.type !== "ParenOpen") return ok(anyTag());
6725
- const tagToken = lexer.next();
6726
- if (tagToken === void 0) return err(unexpectedEndOfInput());
6727
- if (tagToken.token.type !== "UnsignedInteger") return err(unexpectedToken(tagToken.token, tagToken.span));
6728
- if (!tagToken.token.value.ok) return err(tagToken.token.value.error);
6729
- if (lexer.next()?.token.type !== "ParenClose") return err({
6730
- type: "ExpectedCloseParen",
6731
- span: lexer.span()
6732
- });
6733
- return ok(anyTag());
6734
- }
6735
- /**
6736
- * Parse date content from date'...' pattern.
6737
- */
6738
- function parseDateContent(content, span) {
6739
- if (content.startsWith("/") && content.endsWith("/")) {
6740
- const regexStr = content.slice(1, -1);
6741
- try {
6742
- return ok(dateRegex(new RegExp(regexStr)));
6743
- } catch {
6744
- return err(invalidRegex(span));
6655
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyTag());
6656
+ lexer.next();
6657
+ const remainder = lexer.remainder();
6658
+ const closeIdx = findMatchingCloseParen(remainder);
6659
+ if (closeIdx === void 0) return err(expectedCloseParen(lexer.span()));
6660
+ const innerContent = remainder.slice(0, closeIdx);
6661
+ const dcborResult = parse$1(`tagged(${innerContent})`);
6662
+ if (dcborResult.ok) {
6663
+ const dcborPattern = dcborResult.value;
6664
+ if (dcborPattern.kind === "Structure" && dcborPattern.pattern.type === "Tagged") {
6665
+ lexer.bump(closeIdx);
6666
+ const close = lexer.next();
6667
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6668
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6669
+ return ok(patternLeaf(leafTag(TaggedPattern.fromDcborPattern(dcborPattern.pattern.pattern))));
6745
6670
  }
6746
6671
  }
6747
- const rangeIdx = content.indexOf("...");
6748
- if (rangeIdx !== -1) {
6749
- const left = content.slice(0, rangeIdx).trim();
6750
- const right = content.slice(rangeIdx + 3).trim();
6751
- if (left.length === 0 && right.length > 0) {
6752
- const parsed = Date.parse(right);
6753
- if (isNaN(parsed)) return err({
6754
- type: "InvalidDateFormat",
6755
- span
6756
- });
6757
- return ok(dateLatest(CborDate.fromDatetime(new Date(parsed))));
6758
- }
6759
- if (left.length > 0 && right.length === 0) {
6760
- const parsed = Date.parse(left);
6761
- if (isNaN(parsed)) return err({
6762
- type: "InvalidDateFormat",
6763
- span
6764
- });
6765
- return ok(dateEarliest(CborDate.fromDatetime(new Date(parsed))));
6766
- }
6767
- if (left.length > 0 && right.length > 0) {
6768
- const parsedStart = Date.parse(left);
6769
- const parsedEnd = Date.parse(right);
6770
- if (isNaN(parsedStart) || isNaN(parsedEnd)) return err({
6771
- type: "InvalidDateFormat",
6772
- span
6773
- });
6774
- return ok(dateRange(CborDate.fromDatetime(new Date(parsedStart)), CborDate.fromDatetime(new Date(parsedEnd))));
6672
+ const fallback = parseTagInner(innerContent);
6673
+ if (!fallback.ok) return fallback;
6674
+ lexer.bump(closeIdx);
6675
+ const close = lexer.next();
6676
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6677
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6678
+ return ok(fallback.value);
6679
+ }
6680
+ /**
6681
+ * Locate the index of the closing `)` matching the `(` that has already
6682
+ * been consumed by `parseTag`. Mirrors Rust `find_matching_paren`.
6683
+ */
6684
+ function findMatchingCloseParen(src) {
6685
+ let depth = 0;
6686
+ for (let i = 0; i < src.length; i++) {
6687
+ const ch = src.charCodeAt(i);
6688
+ if (ch === 40) depth += 1;
6689
+ else if (ch === 41) {
6690
+ if (depth === 0) return i;
6691
+ depth -= 1;
6775
6692
  }
6776
- return err({
6777
- type: "InvalidDateFormat",
6778
- span
6779
- });
6780
6693
  }
6781
- const parsed = Date.parse(content);
6782
- if (isNaN(parsed)) return err({
6783
- type: "InvalidDateFormat",
6784
- span
6785
- });
6786
- return ok(date(CborDate.fromDatetime(new Date(parsed))));
6787
6694
  }
6788
6695
  /**
6789
- * Parse known value content from '...' pattern.
6696
+ * Fallback for `tagged(N)` and `tagged(name)` when the full delegation
6697
+ * to dcbor-pattern fails. Mirrors Rust `parse_tag_inner`.
6790
6698
  */
6791
- function parseKnownValueContent(content) {
6792
- const numValue = parseInt(content, 10);
6793
- if (!isNaN(numValue)) return ok(knownValue({ value: () => BigInt(numValue) }));
6794
- return ok(patternLeaf(leafKnownValue(KnownValuePattern.named(content))));
6699
+ function parseTagInner(src) {
6700
+ const trimmed = src.trim();
6701
+ if (trimmed.length === 0) return err(unexpectedEndOfInput());
6702
+ if (trimmed.startsWith("/")) return err(unexpectedEndOfInput());
6703
+ if (/^\d+$/.test(trimmed)) try {
6704
+ const dcborResult = parse$1(`tagged(${trimmed})`);
6705
+ if (dcborResult.ok && dcborResult.value.kind === "Structure" && dcborResult.value.pattern.type === "Tagged") return ok(patternLeaf(leafTag(TaggedPattern.fromDcborPattern(dcborResult.value.pattern.pattern))));
6706
+ } catch {}
6707
+ const dcborResult = parse$1(`tagged(${trimmed})`);
6708
+ if (dcborResult.ok && dcborResult.value.kind === "Structure" && dcborResult.value.pattern.type === "Tagged") return ok(patternLeaf(leafTag(TaggedPattern.fromDcborPattern(dcborResult.value.pattern.pattern))));
6709
+ return err(unexpectedEndOfInput());
6795
6710
  }
6711
+ //#endregion
6712
+ //#region src/parse/structure/assertion-parser.ts
6796
6713
  /**
6797
- * Parse CBOR pattern.
6714
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6715
+ * Copyright © 2025-2026 Parity Technologies
6716
+ *
6717
+ * Assertion parser — port of `bc-envelope-pattern-rust`
6718
+ * `parse/structure/assertion_parser.rs`.
6798
6719
  *
6799
- * Matches Rust parse_cbor: tries dcbor-pattern regex first (/keyword/),
6800
- * then CBOR diagnostic notation via parseDcborItemPartial, then falls
6801
- * back to parseOr for envelope pattern expressions.
6720
+ * Note: Rust's `parse_assertion` ignores its lexer entirely and always
6721
+ * returns `Pattern::any_assertion()`. There is intentionally **no**
6722
+ * `assert(pred, obj)` syntax predicate/object filters are written via
6723
+ * the dedicated `assertpred(...)` / `assertobj(...)` keywords.
6724
+ *
6725
+ * @module envelope-pattern/parse/structure/assertion-parser
6802
6726
  */
6803
- function parseCbor(lexer) {
6804
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyCbor());
6805
- lexer.next();
6806
- if (lexer.peek() === "/") {
6807
- const regexTokenResult = lexer.next();
6808
- if (regexTokenResult?.token.type === "Regex") {
6809
- const regexToken = regexTokenResult.token;
6810
- if (!regexToken.value.ok) return err(regexToken.value.error);
6811
- const keyword = regexToken.value.value;
6812
- const dcborResult = parse$1(keyword);
6813
- if (!dcborResult.ok) return err(unexpectedToken(regexToken, regexTokenResult.span));
6814
- if (lexer.next()?.token.type !== "ParenClose") return err({
6815
- type: "ExpectedCloseParen",
6816
- span: lexer.span()
6817
- });
6818
- return ok(cborPattern(dcborResult.value));
6819
- }
6820
- }
6821
- const cborResult = parseDcborItemPartial(lexer.remainder());
6822
- if (cborResult.ok) {
6823
- const [cborData, consumed] = cborResult.value;
6824
- lexer.bump(consumed);
6825
- while (lexer.peek() === " " || lexer.peek() === " " || lexer.peek() === "\n") lexer.bump(1);
6826
- if (lexer.next()?.token.type !== "ParenClose") return err({
6827
- type: "ExpectedCloseParen",
6828
- span: lexer.span()
6829
- });
6830
- return ok(cborValue(cborData));
6831
- }
6832
- const inner = parseOr(lexer);
6833
- if (!inner.ok) return inner;
6834
- if (lexer.next()?.token.type !== "ParenClose") return err({
6835
- type: "ExpectedCloseParen",
6836
- span: lexer.span()
6837
- });
6838
- return inner;
6727
+ function parseAssertion(_lexer) {
6728
+ return ok(anyAssertion());
6839
6729
  }
6840
- function parseNode(lexer) {
6841
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyNode());
6842
- lexer.next();
6843
- const afterParen = lexer.peekToken();
6844
- if (afterParen?.token.type === "Range") {
6845
- lexer.next();
6846
- const rangeToken = afterParen.token;
6847
- if (!rangeToken.value.ok) return err(rangeToken.value.error);
6848
- const interval = rangeToken.value.value.interval();
6849
- if (lexer.next()?.token.type !== "ParenClose") return err({
6850
- type: "ExpectedCloseParen",
6851
- span: lexer.span()
6852
- });
6853
- return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
6854
- }
6730
+ //#endregion
6731
+ //#region src/parse/structure/assertion-obj-parser.ts
6732
+ /**
6733
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6734
+ * Copyright © 2025-2026 Parity Technologies
6735
+ *
6736
+ * Assertion-object parser — port of
6737
+ * `bc-envelope-pattern-rust/src/parse/structure/assertion_obj_parser.rs`.
6738
+ *
6739
+ * Requires `assertobj(<pattern>)`. The bare `assertobj` keyword is a
6740
+ * syntax error in Rust; we now mirror that behaviour.
6741
+ *
6742
+ * @module envelope-pattern/parse/structure/assertion-obj-parser
6743
+ */
6744
+ function parseAssertionObj(lexer) {
6745
+ const open = lexer.next();
6746
+ if (open === void 0) return err(unexpectedEndOfInput());
6747
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6855
6748
  const inner = parseOr(lexer);
6856
6749
  if (!inner.ok) return inner;
6857
- if (lexer.next()?.token.type !== "ParenClose") return err({
6858
- type: "ExpectedCloseParen",
6859
- span: lexer.span()
6860
- });
6861
- return ok(patternStructure(structureNode(NodePattern.withSubject(inner.value))));
6862
- }
6863
- function parseAssertion(lexer) {
6864
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6865
- lexer.next();
6866
- const pred = parseOr(lexer);
6867
- if (!pred.ok) return pred;
6868
- const comma = lexer.next();
6869
- if (comma?.token.type !== "Comma") return err(unexpectedToken(comma?.token ?? { type: "Null" }, comma?.span ?? lexer.span()));
6870
- const obj = parseOr(lexer);
6871
- if (!obj.ok) return obj;
6872
- if (lexer.next()?.token.type !== "ParenClose") return err({
6873
- type: "ExpectedCloseParen",
6874
- span: lexer.span()
6875
- });
6876
- return ok(patternStructure(structureAssertions(AssertionsPattern.withBoth(pred.value, obj.value))));
6750
+ const close = lexer.next();
6751
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6752
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6753
+ return ok(assertionWithObject(inner.value));
6877
6754
  }
6755
+ //#endregion
6756
+ //#region src/parse/structure/assertion-pred-parser.ts
6757
+ /**
6758
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6759
+ * Copyright © 2025-2026 Parity Technologies
6760
+ *
6761
+ * Assertion-predicate parser — port of
6762
+ * `bc-envelope-pattern-rust/src/parse/structure/assertion_pred_parser.rs`.
6763
+ *
6764
+ * Requires `assertpred(<pattern>)`. The bare `assertpred` keyword is a
6765
+ * syntax error in Rust (it errors on `UnexpectedEndOfInput` /
6766
+ * `UnexpectedToken`); we now mirror that behaviour.
6767
+ *
6768
+ * @module envelope-pattern/parse/structure/assertion-pred-parser
6769
+ */
6878
6770
  function parseAssertionPred(lexer) {
6879
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6880
- lexer.next();
6771
+ const open = lexer.next();
6772
+ if (open === void 0) return err(unexpectedEndOfInput());
6773
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6881
6774
  const inner = parseOr(lexer);
6882
6775
  if (!inner.ok) return inner;
6883
- if (lexer.next()?.token.type !== "ParenClose") return err({
6884
- type: "ExpectedCloseParen",
6885
- span: lexer.span()
6886
- });
6776
+ const close = lexer.next();
6777
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6778
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6887
6779
  return ok(assertionWithPredicate(inner.value));
6888
6780
  }
6889
- function parseAssertionObj(lexer) {
6890
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6891
- lexer.next();
6892
- const inner = parseOr(lexer);
6893
- if (!inner.ok) return inner;
6894
- if (lexer.next()?.token.type !== "ParenClose") return err({
6895
- type: "ExpectedCloseParen",
6896
- span: lexer.span()
6897
- });
6898
- return ok(assertionWithObject(inner.value));
6781
+ //#endregion
6782
+ //#region src/parse/structure/compressed-parser.ts
6783
+ /**
6784
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6785
+ * Copyright © 2025-2026 Parity Technologies
6786
+ *
6787
+ * Compressed parser — port of `bc-envelope-pattern-rust`
6788
+ * `parse/structure/compressed_parser.rs`.
6789
+ *
6790
+ * @module envelope-pattern/parse/structure/compressed-parser
6791
+ */
6792
+ function parseCompressed(_lexer) {
6793
+ return ok(compressed());
6899
6794
  }
6795
+ //#endregion
6796
+ //#region src/parse/structure/digest-parser.ts
6797
+ /**
6798
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6799
+ * Copyright © 2025-2026 Parity Technologies
6800
+ *
6801
+ * Digest parser — port of `bc-envelope-pattern-rust`
6802
+ * `parse/structure/digest_parser.rs`.
6803
+ *
6804
+ * Mirrors Rust exactly:
6805
+ *
6806
+ * - Requires `digest(...)` — bare `digest` is an `UnexpectedEndOfInput`
6807
+ * error (the previous TS port silently returned `digest(any)`).
6808
+ * - Inside the parens, accepts either a UR string (`ur:digest/...`,
6809
+ * parsed via `Digest.fromURString`) or a hex byte prefix.
6810
+ * - Hex prefixes must have even length and not exceed `Digest.DIGEST_SIZE`
6811
+ * bytes; otherwise the parser surfaces `InvalidHexString`.
6812
+ *
6813
+ * @module envelope-pattern/parse/structure/digest-parser
6814
+ */
6815
+ const DIGEST_SIZE = Digest.DIGEST_SIZE;
6900
6816
  function parseDigest(lexer) {
6901
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(patternStructure(structureDigest(DigestPattern.any())));
6902
- lexer.next();
6903
- const digestToken = lexer.next();
6904
- if (digestToken === void 0) return err(unexpectedEndOfInput());
6905
- if (digestToken.token.type === "HexPattern") {
6906
- if (!digestToken.token.value.ok) return err(digestToken.token.value.error);
6907
- if (lexer.next()?.token.type !== "ParenClose") return err({
6908
- type: "ExpectedCloseParen",
6909
- span: lexer.span()
6910
- });
6911
- return ok(digestPrefix(digestToken.token.value.value));
6817
+ const open = lexer.next();
6818
+ if (open === void 0) return err(unexpectedEndOfInput());
6819
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6820
+ const innerResult = parseDigestInner(lexer.remainder(), lexer.position);
6821
+ if (!innerResult.ok) return innerResult;
6822
+ const [pattern, consumed] = innerResult.value;
6823
+ lexer.bump(consumed);
6824
+ const close = lexer.next();
6825
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6826
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6827
+ return ok(pattern);
6828
+ }
6829
+ function parseDigestInner(src, basePos) {
6830
+ let pos = 0;
6831
+ pos = skipWs(src, pos);
6832
+ if (src.startsWith("ur:", pos)) {
6833
+ const start = pos;
6834
+ while (pos < src.length && src[pos] !== ")") pos += 1;
6835
+ const ur = src.slice(start, pos).trimEnd();
6836
+ let parsed;
6837
+ try {
6838
+ parsed = Digest.fromURString(ur);
6839
+ } catch {
6840
+ return err(invalidUr(ur, {
6841
+ start: basePos + start,
6842
+ end: basePos + pos
6843
+ }));
6844
+ }
6845
+ pos = skipWs(src, pos);
6846
+ return ok([digest(parsed), pos]);
6847
+ }
6848
+ const start = pos;
6849
+ while (pos < src.length && isAsciiHexDigit(src.charCodeAt(pos))) pos += 1;
6850
+ if (pos === start) return err(invalidHexString({
6851
+ start: basePos + pos,
6852
+ end: basePos + pos
6853
+ }));
6854
+ const hexStr = src.slice(start, pos);
6855
+ if (hexStr.length % 2 !== 0) return err(invalidHexString({
6856
+ start: basePos + pos,
6857
+ end: basePos + pos
6858
+ }));
6859
+ const bytes = decodeHex(hexStr);
6860
+ if (bytes === void 0) return err(invalidHexString({
6861
+ start: basePos + pos,
6862
+ end: basePos + pos
6863
+ }));
6864
+ if (bytes.length > DIGEST_SIZE) return err(invalidHexString({
6865
+ start: basePos + pos,
6866
+ end: basePos + pos
6867
+ }));
6868
+ pos = skipWs(src, pos);
6869
+ return ok([digestPrefix(bytes), pos]);
6870
+ }
6871
+ function skipWs(src, pos) {
6872
+ while (pos < src.length) {
6873
+ const ch = src[pos];
6874
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f") pos += 1;
6875
+ else break;
6912
6876
  }
6913
- if (digestToken.token.type === "Identifier") {
6914
- const hexStr = digestToken.token.value;
6915
- if (hexStr.length === 0 || hexStr.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hexStr)) return err({
6916
- type: "InvalidHexString",
6917
- span: digestToken.span
6918
- });
6919
- const bytes = new Uint8Array(hexStr.length / 2);
6920
- for (let i = 0; i < hexStr.length; i += 2) bytes[i / 2] = Number.parseInt(hexStr.slice(i, i + 2), 16);
6921
- if (lexer.next()?.token.type !== "ParenClose") return err({
6922
- type: "ExpectedCloseParen",
6923
- span: lexer.span()
6924
- });
6925
- return ok(digestPrefix(bytes));
6877
+ return pos;
6878
+ }
6879
+ function isAsciiHexDigit(c) {
6880
+ return c >= 48 && c <= 57 || c >= 65 && c <= 70 || c >= 97 && c <= 102;
6881
+ }
6882
+ function decodeHex(hex) {
6883
+ if (hex.length % 2 !== 0) return void 0;
6884
+ const out = new Uint8Array(hex.length / 2);
6885
+ for (let i = 0; i < hex.length; i += 2) {
6886
+ const value = Number.parseInt(hex.slice(i, i + 2), 16);
6887
+ if (Number.isNaN(value)) return void 0;
6888
+ out[i / 2] = value;
6926
6889
  }
6927
- return err(unexpectedToken(digestToken.token, digestToken.span));
6890
+ return out;
6891
+ }
6892
+ //#endregion
6893
+ //#region src/parse/structure/elided-parser.ts
6894
+ /**
6895
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6896
+ * Copyright © 2025-2026 Parity Technologies
6897
+ *
6898
+ * Elided parser — port of `bc-envelope-pattern-rust`
6899
+ * `parse/structure/elided_parser.rs`.
6900
+ *
6901
+ * @module envelope-pattern/parse/structure/elided-parser
6902
+ */
6903
+ function parseElided(_lexer) {
6904
+ return ok(elided());
6905
+ }
6906
+ //#endregion
6907
+ //#region src/parse/structure/encrypted-parser.ts
6908
+ /**
6909
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6910
+ * Copyright © 2025-2026 Parity Technologies
6911
+ *
6912
+ * Encrypted parser — port of `bc-envelope-pattern-rust`
6913
+ * `parse/structure/encrypted_parser.rs`.
6914
+ *
6915
+ * @module envelope-pattern/parse/structure/encrypted-parser
6916
+ */
6917
+ function parseEncrypted(_lexer) {
6918
+ return ok(encrypted());
6919
+ }
6920
+ //#endregion
6921
+ //#region src/parse/structure/node-parser.ts
6922
+ /**
6923
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6924
+ * Copyright © 2025-2026 Parity Technologies
6925
+ *
6926
+ * Node parser — port of `bc-envelope-pattern-rust`
6927
+ * `parse/structure/node_parser.rs`.
6928
+ *
6929
+ * Mirrors Rust:
6930
+ * - Bare `node` → `any_node()`.
6931
+ * - `node({n})` / `node({n,m})` / `node({n,})` → `node_with_assertions_range`.
6932
+ * - Anything else inside the parens (e.g. `node(text)`) is a syntax error
6933
+ * in Rust; the previous TS port silently produced an always-matching
6934
+ * `WithSubject` node, which we have removed.
6935
+ *
6936
+ * @module envelope-pattern/parse/structure/node-parser
6937
+ */
6938
+ function parseNode(lexer) {
6939
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyNode());
6940
+ lexer.next();
6941
+ const inner = lexer.next();
6942
+ if (inner === void 0) return err(unexpectedEndOfInput());
6943
+ if (inner.token.type !== "Range") return err(unexpectedToken(inner.token, inner.span));
6944
+ if (!inner.token.value.ok) return err(inner.token.value.error);
6945
+ const interval = inner.token.value.value.interval();
6946
+ const close = lexer.next();
6947
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6948
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6949
+ return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
6928
6950
  }
6951
+ //#endregion
6952
+ //#region src/parse/structure/object-parser.ts
6953
+ /**
6954
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6955
+ * Copyright © 2025-2026 Parity Technologies
6956
+ *
6957
+ * Object parser — port of `bc-envelope-pattern-rust`
6958
+ * `parse/structure/object_parser.rs`.
6959
+ *
6960
+ * @module envelope-pattern/parse/structure/object-parser
6961
+ */
6929
6962
  function parseObject(lexer) {
6930
6963
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyObject());
6931
6964
  lexer.next();
6932
6965
  const inner = parseOr(lexer);
6933
6966
  if (!inner.ok) return inner;
6934
- if (lexer.next()?.token.type !== "ParenClose") return err({
6935
- type: "ExpectedCloseParen",
6936
- span: lexer.span()
6937
- });
6967
+ const close = lexer.next();
6968
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6969
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6938
6970
  return ok(object(inner.value));
6939
6971
  }
6972
+ //#endregion
6973
+ //#region src/parse/structure/obscured-parser.ts
6974
+ /**
6975
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6976
+ * Copyright © 2025-2026 Parity Technologies
6977
+ *
6978
+ * Obscured parser — port of `bc-envelope-pattern-rust`
6979
+ * `parse/structure/obscured_parser.rs`.
6980
+ *
6981
+ * @module envelope-pattern/parse/structure/obscured-parser
6982
+ */
6983
+ function parseObscured(_lexer) {
6984
+ return ok(obscured());
6985
+ }
6986
+ //#endregion
6987
+ //#region src/parse/structure/predicate-parser.ts
6988
+ /**
6989
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6990
+ * Copyright © 2025-2026 Parity Technologies
6991
+ *
6992
+ * Predicate parser — port of `bc-envelope-pattern-rust`
6993
+ * `parse/structure/predicate_parser.rs`.
6994
+ *
6995
+ * @module envelope-pattern/parse/structure/predicate-parser
6996
+ */
6940
6997
  function parsePredicate(lexer) {
6941
6998
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyPredicate());
6942
6999
  lexer.next();
6943
7000
  const inner = parseOr(lexer);
6944
7001
  if (!inner.ok) return inner;
6945
- if (lexer.next()?.token.type !== "ParenClose") return err({
6946
- type: "ExpectedCloseParen",
6947
- span: lexer.span()
6948
- });
7002
+ const close = lexer.next();
7003
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7004
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6949
7005
  return ok(predicate(inner.value));
6950
7006
  }
7007
+ //#endregion
7008
+ //#region src/parse/structure/subject-parser.ts
7009
+ /**
7010
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7011
+ * Copyright © 2025-2026 Parity Technologies
7012
+ *
7013
+ * Subject parser — port of `bc-envelope-pattern-rust`
7014
+ * `parse/structure/subject_parser.rs`.
7015
+ *
7016
+ * @module envelope-pattern/parse/structure/subject-parser
7017
+ */
6951
7018
  function parseSubject(lexer) {
6952
7019
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anySubject());
6953
7020
  lexer.next();
6954
7021
  const inner = parseOr(lexer);
6955
7022
  if (!inner.ok) return inner;
6956
- if (lexer.next()?.token.type !== "ParenClose") return err({
6957
- type: "ExpectedCloseParen",
6958
- span: lexer.span()
6959
- });
7023
+ const close = lexer.next();
7024
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7025
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6960
7026
  return ok(subject(inner.value));
6961
7027
  }
7028
+ //#endregion
7029
+ //#region src/parse/structure/wrapped-parser.ts
7030
+ /**
7031
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7032
+ * Copyright © 2025-2026 Parity Technologies
7033
+ *
7034
+ * Wrapped/unwrap parser — port of `bc-envelope-pattern-rust`
7035
+ * `parse/structure/wrapped_parser.rs`.
7036
+ *
7037
+ * @module envelope-pattern/parse/structure/wrapped-parser
7038
+ */
7039
+ function parseWrapped(_lexer) {
7040
+ return ok(wrapped());
7041
+ }
6962
7042
  function parseUnwrap(lexer) {
6963
7043
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(unwrapEnvelope());
6964
7044
  lexer.next();
6965
7045
  const inner = parseOr(lexer);
6966
7046
  if (!inner.ok) return inner;
6967
- if (lexer.next()?.token.type !== "ParenClose") return err({
6968
- type: "ExpectedCloseParen",
6969
- span: lexer.span()
6970
- });
7047
+ const close = lexer.next();
7048
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7049
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6971
7050
  return ok(unwrapMatching(inner.value));
6972
7051
  }
6973
-
7052
+ //#endregion
7053
+ //#region src/parse/meta/capture-parser.ts
7054
+ /**
7055
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7056
+ * Copyright © 2025-2026 Parity Technologies
7057
+ *
7058
+ * Capture parser — port of `bc-envelope-pattern-rust`
7059
+ * `parse/meta/capture_parser.rs`.
7060
+ *
7061
+ * The `@name(...)` form requires explicit parentheses. Mirrors Rust
7062
+ * exactly:
7063
+ * ```ignore
7064
+ * @name ( expr )
7065
+ * ```
7066
+ * The previous TS port called `parse_group` (primary + quantifier), which
7067
+ * wrapped the inner expression in a redundant `GroupPattern` and accepted
7068
+ * the bare `@name p` form that Rust rejects.
7069
+ *
7070
+ * @module envelope-pattern/parse/meta/capture-parser
7071
+ */
7072
+ function parseCapture(lexer, name) {
7073
+ const open = lexer.next();
7074
+ if (open === void 0) return err(unexpectedEndOfInput());
7075
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
7076
+ const inner = parseOr(lexer);
7077
+ if (!inner.ok) return inner;
7078
+ const close = lexer.next();
7079
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7080
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7081
+ return ok(capture(name, inner.value));
7082
+ }
7083
+ //#endregion
7084
+ //#region src/parse/meta/group-parser.ts
7085
+ /**
7086
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7087
+ * Copyright © 2025-2026 Parity Technologies
7088
+ *
7089
+ * Group parser — port of `bc-envelope-pattern-rust`
7090
+ * `parse/meta/group_parser.rs`.
7091
+ *
7092
+ * Called from `parse_primary` after the opening `(` has been consumed.
7093
+ *
7094
+ * Mirrors Rust exactly:
7095
+ *
7096
+ * - Parse the inner expression with `parse_or`.
7097
+ * - Expect `)`. If missing, surface `ExpectedCloseParen`.
7098
+ * - Lookahead **once** for a quantifier suffix. If present, consume it
7099
+ * and wrap as `Pattern::repeat(inner, …)`. Otherwise return the inner
7100
+ * expression unchanged.
7101
+ *
7102
+ * The previous TS port wrapped every parenthesised expression in a
7103
+ * dedicated `GroupPattern` and applied quantifiers to bare primaries —
7104
+ * both broke `format(parse(s)) === s` round-tripping.
7105
+ *
7106
+ * @module envelope-pattern/parse/meta/group-parser
7107
+ */
7108
+ function parseGroup(lexer) {
7109
+ const inner = parseOr(lexer);
7110
+ if (!inner.ok) return inner;
7111
+ const close = lexer.next();
7112
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7113
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7114
+ const next = lexer.peekToken();
7115
+ if (next === void 0) return inner;
7116
+ let quantifier;
7117
+ const tokenType = next.token.type;
7118
+ if (tokenType === "RepeatZeroOrMore") quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Greedy);
7119
+ else if (tokenType === "RepeatZeroOrMoreLazy") quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Lazy);
7120
+ else if (tokenType === "RepeatZeroOrMorePossessive") quantifier = Quantifier$1.zeroOrMore(Reluctance$1.Possessive);
7121
+ else if (tokenType === "RepeatOneOrMore") quantifier = Quantifier$1.oneOrMore(Reluctance$1.Greedy);
7122
+ else if (tokenType === "RepeatOneOrMoreLazy") quantifier = Quantifier$1.oneOrMore(Reluctance$1.Lazy);
7123
+ else if (tokenType === "RepeatOneOrMorePossessive") quantifier = Quantifier$1.oneOrMore(Reluctance$1.Possessive);
7124
+ else if (tokenType === "RepeatZeroOrOne") quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Greedy);
7125
+ else if (tokenType === "RepeatZeroOrOneLazy") quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Lazy);
7126
+ else if (tokenType === "RepeatZeroOrOnePossessive") quantifier = Quantifier$1.zeroOrOne(Reluctance$1.Possessive);
7127
+ else if (tokenType === "Range") {
7128
+ if (!next.token.value.ok) return err(next.token.value.error);
7129
+ quantifier = next.token.value.value;
7130
+ } else return inner;
7131
+ lexer.next();
7132
+ return ok(repeat(inner.value, quantifier.min(), quantifier.max(), quantifier.reluctance()));
7133
+ }
7134
+ //#endregion
7135
+ //#region src/parse/meta/search-parser.ts
7136
+ /**
7137
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7138
+ * Copyright © 2025-2026 Parity Technologies
7139
+ *
7140
+ * Search parser — port of `bc-envelope-pattern-rust`
7141
+ * `parse/meta/search_parser.rs`.
7142
+ *
7143
+ * @module envelope-pattern/parse/meta/search-parser
7144
+ */
7145
+ function parseSearch(lexer) {
7146
+ const open = lexer.next();
7147
+ if (open === void 0) return err(unexpectedEndOfInput());
7148
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
7149
+ const inner = parseOr(lexer);
7150
+ if (!inner.ok) return inner;
7151
+ const close = lexer.next();
7152
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7153
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7154
+ return ok(search(inner.value));
7155
+ }
7156
+ //#endregion
7157
+ //#region src/parse/meta/primary-parser.ts
7158
+ /**
7159
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7160
+ * Copyright © 2025-2026 Parity Technologies
7161
+ *
7162
+ * Primary parser — port of `bc-envelope-pattern-rust`
7163
+ * `parse/meta/primary_parser.rs`.
7164
+ *
7165
+ * Dispatches on the next token to the appropriate leaf/structure/meta
7166
+ * parser. When a `(` is encountered the open paren is consumed here and
7167
+ * `parse_group` handles the rest (paren'd expression + optional
7168
+ * quantifier suffix).
7169
+ *
7170
+ * @module envelope-pattern/parse/meta/primary-parser
7171
+ */
7172
+ function parsePrimary(lexer) {
7173
+ const tokenResult = lexer.next();
7174
+ if (tokenResult === void 0) return err(unexpectedEndOfInput());
7175
+ const { token, span } = tokenResult;
7176
+ switch (token.type) {
7177
+ case "Search": return parseSearch(lexer);
7178
+ case "Node": return parseNode(lexer);
7179
+ case "Assertion": return parseAssertion(lexer);
7180
+ case "AssertionPred": return parseAssertionPred(lexer);
7181
+ case "AssertionObj": return parseAssertionObj(lexer);
7182
+ case "Digest": return parseDigest(lexer);
7183
+ case "Obj": return parseObject(lexer);
7184
+ case "Obscured": return parseObscured(lexer);
7185
+ case "Elided": return parseElided(lexer);
7186
+ case "Encrypted": return parseEncrypted(lexer);
7187
+ case "Compressed": return parseCompressed(lexer);
7188
+ case "Pred": return parsePredicate(lexer);
7189
+ case "Subject": return parseSubject(lexer);
7190
+ case "Wrapped": return parseWrapped(lexer);
7191
+ case "Unwrap": return parseUnwrap(lexer);
7192
+ case "Leaf": return ok(leaf());
7193
+ case "GroupName": return parseCapture(lexer, token.name);
7194
+ case "ParenOpen": return parseGroup(lexer);
7195
+ case "Cbor": return parseCbor(lexer);
7196
+ case "RepeatZeroOrMore": return ok(any());
7197
+ case "BoolKeyword": return ok(anyBool());
7198
+ case "BoolTrue": return ok(bool(true));
7199
+ case "BoolFalse": return ok(bool(false));
7200
+ case "NumberKeyword": return ok(anyNumber());
7201
+ case "TextKeyword": return ok(anyText());
7202
+ case "StringLiteral":
7203
+ if (!token.value.ok) return err(token.value.error);
7204
+ return ok(text(token.value.value));
7205
+ case "UnsignedInteger":
7206
+ if (!token.value.ok) return err(token.value.error);
7207
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7208
+ case "Integer":
7209
+ if (!token.value.ok) return err(token.value.error);
7210
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7211
+ case "Float":
7212
+ if (!token.value.ok) return err(token.value.error);
7213
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7214
+ case "GreaterThanOrEqual": return parseComparisonNumber(lexer, ">=");
7215
+ case "LessThanOrEqual": return parseComparisonNumber(lexer, "<=");
7216
+ case "GreaterThan": return parseComparisonNumber(lexer, ">");
7217
+ case "LessThan": return parseComparisonNumber(lexer, "<");
7218
+ case "NaN": return ok(patternLeaf(leafNumber(NumberPattern.nan())));
7219
+ case "Infinity": return ok(number(Infinity));
7220
+ case "NegativeInfinity": return ok(number(-Infinity));
7221
+ case "Regex":
7222
+ if (!token.value.ok) return err(token.value.error);
7223
+ try {
7224
+ return ok(textRegex(new RegExp(token.value.value)));
7225
+ } catch {
7226
+ return err(invalidRegex(span));
7227
+ }
7228
+ case "BracketOpen": return parseArray(lexer);
7229
+ case "ByteString": return ok(anyByteString());
7230
+ case "HexPattern":
7231
+ if (!token.value.ok) return err(token.value.error);
7232
+ return ok(byteString(token.value.value));
7233
+ case "HexBinaryRegex":
7234
+ if (!token.value.ok) return err(token.value.error);
7235
+ try {
7236
+ return ok(patternLeaf(leafByteString(ByteStringPattern.regex(new RegExp(token.value.value)))));
7237
+ } catch {
7238
+ return err(invalidRegex(span));
7239
+ }
7240
+ case "DateKeyword": return ok(anyDate());
7241
+ case "DatePattern":
7242
+ if (!token.value.ok) return err(token.value.error);
7243
+ return parseDateContent(token.value.value, span);
7244
+ case "Tagged": return parseTag(lexer);
7245
+ case "Known": return ok(anyKnownValue());
7246
+ case "SingleQuotedPattern":
7247
+ if (!token.value.ok) return err(token.value.error);
7248
+ return parseKnownValueContent(token.value.value);
7249
+ case "SingleQuotedRegex":
7250
+ if (!token.value.ok) return err(token.value.error);
7251
+ try {
7252
+ return ok(patternLeaf(leafKnownValue(KnownValuePattern.regex(new RegExp(token.value.value)))));
7253
+ } catch {
7254
+ return err(invalidRegex(span));
7255
+ }
7256
+ case "Null": return ok(nullPattern());
7257
+ case "Identifier": return err(unrecognizedToken(span));
7258
+ case "And":
7259
+ case "Or":
7260
+ case "Not":
7261
+ case "Traverse":
7262
+ case "RepeatZeroOrMoreLazy":
7263
+ case "RepeatZeroOrMorePossessive":
7264
+ case "RepeatOneOrMore":
7265
+ case "RepeatOneOrMoreLazy":
7266
+ case "RepeatOneOrMorePossessive":
7267
+ case "RepeatZeroOrOne":
7268
+ case "RepeatZeroOrOneLazy":
7269
+ case "RepeatZeroOrOnePossessive":
7270
+ case "ParenClose":
7271
+ case "BracketClose":
7272
+ case "Comma":
7273
+ case "Ellipsis":
7274
+ case "Range": return err(unexpectedToken(token, span));
7275
+ }
7276
+ }
7277
+ //#endregion
7278
+ //#region src/parse/meta/and-parser.ts
7279
+ /**
7280
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7281
+ * Copyright © 2025-2026 Parity Technologies
7282
+ *
7283
+ * And parser — port of `bc-envelope-pattern-rust`
7284
+ * `parse/meta/and_parser.rs`.
7285
+ *
7286
+ * Mirrors Rust: `parse_and` calls `parse_primary` (NOT `parse_not`); `!`
7287
+ * is handled at a higher precedence level by `parse_not`.
7288
+ *
7289
+ * @module envelope-pattern/parse/meta/and-parser
7290
+ */
7291
+ function parseAnd(lexer) {
7292
+ const patterns = [];
7293
+ const first = parsePrimary(lexer);
7294
+ if (!first.ok) return first;
7295
+ patterns.push(first.value);
7296
+ while (true) {
7297
+ if (lexer.peekToken()?.token.type !== "And") break;
7298
+ lexer.next();
7299
+ const nextExpr = parsePrimary(lexer);
7300
+ if (!nextExpr.ok) return nextExpr;
7301
+ patterns.push(nextExpr.value);
7302
+ }
7303
+ if (patterns.length === 1) return ok(patterns[0]);
7304
+ return ok(and(patterns));
7305
+ }
7306
+ //#endregion
7307
+ //#region src/parse/meta/not-parser.ts
7308
+ /**
7309
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7310
+ * Copyright © 2025-2026 Parity Technologies
7311
+ *
7312
+ * Not parser — port of `bc-envelope-pattern-rust`
7313
+ * `parse/meta/not_parser.rs`.
7314
+ *
7315
+ * Mirrors Rust:
7316
+ * - On `!`, recurse into `parse_not` so chained negation parses as
7317
+ * `not(not(x))` rather than `not(group(x))`.
7318
+ * - Otherwise descend into `parse_and`.
7319
+ *
7320
+ * @module envelope-pattern/parse/meta/not-parser
7321
+ */
7322
+ function parseNot(lexer) {
7323
+ if (lexer.peekToken()?.token.type === "Not") {
7324
+ lexer.next();
7325
+ const inner = parseNot(lexer);
7326
+ if (!inner.ok) return inner;
7327
+ return ok(notMatching(inner.value));
7328
+ }
7329
+ return parseAnd(lexer);
7330
+ }
7331
+ //#endregion
7332
+ //#region src/parse/meta/traverse-parser.ts
7333
+ /**
7334
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7335
+ * Copyright © 2025-2026 Parity Technologies
7336
+ *
7337
+ * Traverse parser — port of `bc-envelope-pattern-rust`
7338
+ * `parse/meta/traverse_parser.rs`.
7339
+ *
7340
+ * Note the precedence chain: `parse_or → parse_traverse → parse_not →
7341
+ * parse_and → parse_primary`. The earlier TS port had `parse_traverse`
7342
+ * call `parse_and` directly, which pushed `!` below `&` and
7343
+ * miscompiled `!a & b`.
7344
+ *
7345
+ * @module envelope-pattern/parse/meta/traverse-parser
7346
+ */
7347
+ function parseTraverse(lexer) {
7348
+ const patterns = [];
7349
+ const first = parseNot(lexer);
7350
+ if (!first.ok) return first;
7351
+ patterns.push(first.value);
7352
+ while (true) {
7353
+ if (lexer.peekToken()?.token.type !== "Traverse") break;
7354
+ lexer.next();
7355
+ const nextExpr = parseNot(lexer);
7356
+ if (!nextExpr.ok) return nextExpr;
7357
+ patterns.push(nextExpr.value);
7358
+ }
7359
+ if (patterns.length === 1) return ok(patterns[0]);
7360
+ return ok(traverse(patterns));
7361
+ }
7362
+ //#endregion
7363
+ //#region src/parse/meta/or-parser.ts
7364
+ /**
7365
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7366
+ * Copyright © 2025-2026 Parity Technologies
7367
+ *
7368
+ * Or parser — port of `bc-envelope-pattern-rust` `parse/meta/or_parser.rs`.
7369
+ *
7370
+ * @module envelope-pattern/parse/meta/or-parser
7371
+ */
7372
+ function parseOr(lexer) {
7373
+ const patterns = [];
7374
+ const first = parseTraverse(lexer);
7375
+ if (!first.ok) return first;
7376
+ patterns.push(first.value);
7377
+ while (true) {
7378
+ if (lexer.peekToken()?.token.type !== "Or") break;
7379
+ lexer.next();
7380
+ const nextExpr = parseTraverse(lexer);
7381
+ if (!nextExpr.ok) return nextExpr;
7382
+ patterns.push(nextExpr.value);
7383
+ }
7384
+ if (patterns.length === 1) return ok(patterns[0]);
7385
+ return ok(or(patterns));
7386
+ }
7387
+ //#endregion
7388
+ //#region src/parse/index.ts
7389
+ /**
7390
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7391
+ * Copyright © 2025-2026 Parity Technologies
7392
+ *
7393
+ *
7394
+ * @bcts/envelope-pattern - Parser entry point
7395
+ *
7396
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust parse/mod.rs.
7397
+ *
7398
+ * Recursive-descent parser for the Gordian Envelope pattern syntax. The
7399
+ * parsing rules live under `parse/leaf/`, `parse/meta/`, and
7400
+ * `parse/structure/`, mirroring the Rust crate's module layout.
7401
+ *
7402
+ * @module envelope-pattern/parse
7403
+ */
7404
+ /**
7405
+ * Parse a pattern expression string into a Pattern.
7406
+ *
7407
+ * Mirrors Rust `Pattern::parse`: tries envelope-pattern parsing first;
7408
+ * on failure falls back to dcbor-pattern parsing and converts the
7409
+ * result into an envelope pattern via the
7410
+ * `dcbor_integration::convert_dcbor_pattern_to_envelope_pattern` bridge.
7411
+ */
7412
+ function parse(input) {
7413
+ const lexer = new Lexer(input);
7414
+ const result = parseOr(lexer);
7415
+ if (!result.ok) {
7416
+ const dcborResult = parse$1(input);
7417
+ if (dcborResult.ok) return convertDcborPatternToEnvelopePattern(dcborResult.value);
7418
+ return result;
7419
+ }
7420
+ const next = lexer.next();
7421
+ if (next !== void 0) return err(extraData(next.span));
7422
+ return result;
7423
+ }
7424
+ /**
7425
+ * Parse a pattern, allowing extra data after the pattern.
7426
+ *
7427
+ * Returns the parsed pattern and the byte offset at which parsing
7428
+ * stopped, mirroring `Pattern::parse_partial` in spirit.
7429
+ */
7430
+ function parsePartial(input) {
7431
+ const lexer = new Lexer(input);
7432
+ const result = parseOr(lexer);
7433
+ if (!result.ok) return result;
7434
+ return ok([result.value, lexer.position]);
7435
+ }
6974
7436
  //#endregion
6975
7437
  //#region src/index.ts
6976
7438
  /**
6977
7439
  * Package version.
6978
7440
  */
6979
7441
  const VERSION = "1.0.0-alpha.11";
6980
-
6981
7442
  //#endregion
6982
- export { AndPattern, AnyPattern, ArrayPattern, AssertionsPattern, BoolPattern, ByteStringPattern, CBORPattern, CapturePattern, DatePattern, DigestPattern, FormatPathsOptsBuilder, GroupPattern, Interval, KnownValuePattern, LeafStructurePattern, Lexer, MapPattern, MatcherDefaults, NodePattern, NotPattern, NullPattern, NumberPattern, ObjectPattern, ObscuredPattern, OrPattern, PredicatePattern, Quantifier, Reluctance, SearchPattern, SubjectPattern, TaggedPattern, TextPattern, TraversePattern, VERSION, WrappedPattern, and, any, anyArray, anyAssertion, anyBool, anyByteString, anyCbor, anyDate, anyKnownValue, anyMap, anyNode, anyNumber, anyObject, anyPredicate, anySubject, anyTag, anyText, assertionWithObject, assertionWithPredicate, axisChildren, bool, byteString, capture, cborPattern, cborValue, compile, compileAsAtomic, compressed, convertDcborPatternToEnvelopePattern, date, dateEarliest, dateLatest, dateRange, dateRegex, dcborPatternError, defaultFormatPathsOpts, defaultPathElementFormat, digest, digestPrefix, digestURFormat, dispatchCompile, dispatchIsComplex, dispatchPaths, dispatchPathsWithCaptures, dispatchPatternToString, elided, emptyInput, encrypted, envelopeSummary, envelopeURFormat, err, expectedCloseBracket, expectedCloseParen, expectedOpenBracket, expectedOpenParen, expectedPattern, extraData, formatError, formatPath, formatPathOpt, formatPaths, formatPathsOpt, formatPathsOpts, formatPathsWithCaptures, formatPathsWithCapturesOpt, group, invalidCaptureGroupName, invalidDateFormat, invalidHexString, invalidNumberFormat, invalidPattern, invalidRange, invalidRegex, invalidUr, isErr, isOk, knownValue, leaf, leafArray, leafBool, leafByteString, leafCbor, leafDate, leafKnownValue, leafMap, leafNull, leafNumber, leafPatternCompile, leafPatternIsComplex, leafPatternPaths, leafPatternPathsWithCaptures, leafPatternToString, leafTag, leafText, map, matchPattern, metaAnd, metaAny, metaCapture, metaGroup, metaNot, metaOr, metaPatternCollectCaptureNames, metaPatternCompile, metaPatternIsComplex, metaPatternPathsWithCaptures, metaPatternToString, metaSearch, metaTraverse, notMatching, nullPattern, number, numberGreaterThan, numberLessThan, numberRange, object, obscured, ok, or, parse, parsePartial, patternCollectCaptureNames, patternCompile, patternIsComplex, patternLeaf, patternMatches, patternMeta, patternPaths, patternPathsWithCaptures, patternStructure, patternToString, predicate, registerAndPatternFactory, registerAnyPatternFactory, registerArrayPatternFactory, registerAssertionsPatternFactory, registerBoolPatternFactory, registerByteStringPatternFactory, registerCBORPatternFactory, registerCapturePatternFactory, registerDatePatternFactory, registerDigestPatternFactory, registerGroupPatternFactory, registerKnownValuePatternFactory, registerLeafStructurePatternFactory, registerMapPatternFactory, registerNodePatternFactory, registerNotPatternFactory, registerNullPatternFactory, registerNumberPatternFactory, registerObjectPatternFactory, registerObscuredPatternFactory, registerOrPatternFactory, registerPatternDispatchFns, registerPatternMatchFn, registerPredicatePatternFactory, registerSearchPatternFactory, registerSubjectPatternFactory, registerTaggedPatternFactory, registerTextPatternFactory, registerTraversePatternFactory, registerVMPatternFunctions, registerWrappedPatternDispatch, registerWrappedPatternFactory, repeat, run, search, structureAssertions, structureDigest, structureLeaf, structureNode, structureObject, structureObscured, structurePatternCompile, structurePatternIsComplex, structurePatternPaths, structurePatternPathsWithCaptures, structurePatternToString, structurePredicate, structureSubject, structureWrapped, subject, summaryFormat, tagged, text, textRegex, traverse, unexpectedEndOfInput, unexpectedToken, unit, unknown, unmatchedBraces, unmatchedParentheses, unrecognizedToken, unterminatedRegex, unwrap, unwrapEnvelope, unwrapMatching, unwrapOr, wrapped };
7443
+ export { AndPattern, AnyPattern, ArrayPattern, AssertionsPattern, BoolPattern, ByteStringPattern, CBORPattern, CapturePattern, DatePattern, DigestPattern, FormatPathsOptsBuilder, GroupPattern, Interval, KnownValuePattern, LeafStructurePattern, Lexer, MapPattern, MatcherDefaults, NodePattern, NotPattern, NullPattern, NumberPattern, ObjectPattern, ObscuredPattern, OrPattern, PredicatePattern, Quantifier, Reluctance, SearchPattern, SubjectPattern, TaggedPattern, TextPattern, TraversePattern, VERSION, WrappedPattern, and, any, anyArray, anyAssertion, anyBool, anyByteString, anyCbor, anyDate, anyKnownValue, anyMap, anyNode, anyNumber, anyObject, anyPredicate, anySubject, anyTag, anyText, assertionWithObject, assertionWithPredicate, axisChildren, bool, byteString, capture, cborPattern, cborValue, compile, compileAsAtomic, compressed, convertDcborPatternToEnvelopePattern, date, dateEarliest, dateLatest, dateRange, dateRegex, dcborPatternError, defaultFormatPathsOpts, defaultPathElementFormat, digest, digestPrefix, digestURFormat, dispatchCompile, dispatchIsComplex, dispatchPaths, dispatchPathsWithCaptures, dispatchPatternToString, elided, emptyInput, encrypted, envelopeSummary, envelopeURFormat, err, expectedCloseBracket, expectedCloseParen, expectedOpenBracket, expectedOpenParen, expectedPattern, extraData, formatError, formatPath, formatPathOpt, formatPaths, formatPathsOpt, formatPathsOpts, formatPathsWithCaptures, formatPathsWithCapturesOpt, group, invalidCaptureGroupName, invalidDateFormat, invalidHexString, invalidNumberFormat, invalidPattern, invalidRange, invalidRegex, invalidUr, isErr, isOk, knownValue, leaf, leafArray, leafBool, leafByteString, leafCbor, leafDate, leafKnownValue, leafMap, leafNull, leafNumber, leafPatternCompile, leafPatternIsComplex, leafPatternPaths, leafPatternPathsWithCaptures, leafPatternToString, leafTag, leafText, map, matchPattern, metaAnd, metaAny, metaCapture, metaGroup, metaNot, metaOr, metaPatternCollectCaptureNames, metaPatternCompile, metaPatternIsComplex, metaPatternPathsWithCaptures, metaPatternToString, metaSearch, metaTraverse, notMatching, nullPattern, number, numberGreaterThan, numberLessThan, numberRange, object, obscured, ok, or, parse, parsePartial, patternCollectCaptureNames, patternCompile, patternIsComplex, patternLeaf, patternMatches, patternMeta, patternPaths, patternPathsWithCaptures, patternStructure, patternToString, predicate, registerAndPatternFactory, registerAnyPatternFactory, registerArrayPatternFactory, registerAssertionsPatternFactory, registerAssertionsPatternToStringDispatch, registerBoolPatternFactory, registerByteStringPatternFactory, registerCBORPatternFactory, registerCapturePatternFactory, registerDatePatternFactory, registerDigestPatternFactory, registerGroupPatternFactory, registerKnownValuePatternFactory, registerLeafStructurePatternFactory, registerMapPatternFactory, registerNodePatternFactory, registerNotPatternFactory, registerNullPatternFactory, registerNumberPatternFactory, registerObjectPatternFactory, registerObscuredPatternFactory, registerOrPatternFactory, registerPatternDispatchFns, registerPatternMatchFn, registerPredicatePatternFactory, registerSearchPatternFactory, registerSubjectPatternDispatch, registerSubjectPatternFactory, registerTaggedPatternFactory, registerTextPatternFactory, registerTraversePatternFactory, registerVMPatternFunctions, registerWrappedPatternAny, registerWrappedPatternDispatch, registerWrappedPatternFactory, repeat, run, search, structureAssertions, structureDigest, structureLeaf, structureNode, structureObject, structureObscured, structurePatternCompile, structurePatternIsComplex, structurePatternPaths, structurePatternPathsWithCaptures, structurePatternToString, structurePredicate, structureSubject, structureWrapped, subject, summaryFormat, tagged, text, textRegex, traverse, unexpectedEndOfInput, unexpectedToken, unit, unknown, unmatchedBraces, unmatchedParentheses, unrecognizedToken, unterminatedRegex, unwrap, unwrapEnvelope, unwrapMatching, unwrapOr, wrapped };
7444
+
6983
7445
  //# sourceMappingURL=index.mjs.map