@bcts/envelope-pattern 1.0.0-alpha.23 → 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 (59) hide show
  1. package/dist/index.cjs +1302 -766
  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 +1299 -763
  8. package/dist/index.iife.js.map +1 -1
  9. package/dist/index.mjs +1299 -766
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +8 -6
  12. package/src/format.ts +19 -31
  13. package/src/parse/index.ts +16 -1009
  14. package/src/parse/leaf/array-parser.ts +36 -0
  15. package/src/parse/leaf/cbor-parser.ts +43 -0
  16. package/src/parse/leaf/date-parser.ts +81 -0
  17. package/src/parse/leaf/known-value-parser.ts +73 -0
  18. package/src/parse/leaf/null-parser.ts +16 -0
  19. package/src/parse/leaf/number-parser.ts +90 -0
  20. package/src/parse/leaf/tag-parser.ts +160 -0
  21. package/src/parse/meta/and-parser.ts +40 -0
  22. package/src/parse/meta/capture-parser.ts +50 -0
  23. package/src/parse/meta/group-parser.ts +77 -0
  24. package/src/parse/meta/not-parser.ts +30 -0
  25. package/src/parse/meta/or-parser.ts +36 -0
  26. package/src/parse/meta/primary-parser.ts +234 -0
  27. package/src/parse/meta/search-parser.ts +41 -0
  28. package/src/parse/meta/traverse-parser.ts +42 -0
  29. package/src/parse/structure/assertion-obj-parser.ts +44 -0
  30. package/src/parse/structure/assertion-parser.ts +22 -0
  31. package/src/parse/structure/assertion-pred-parser.ts +45 -0
  32. package/src/parse/structure/compressed-parser.ts +17 -0
  33. package/src/parse/structure/digest-parser.ts +132 -0
  34. package/src/parse/structure/elided-parser.ts +17 -0
  35. package/src/parse/structure/encrypted-parser.ts +17 -0
  36. package/src/parse/structure/node-parser.ts +54 -0
  37. package/src/parse/structure/object-parser.ts +32 -0
  38. package/src/parse/structure/obscured-parser.ts +17 -0
  39. package/src/parse/structure/predicate-parser.ts +32 -0
  40. package/src/parse/structure/subject-parser.ts +32 -0
  41. package/src/parse/structure/wrapped-parser.ts +36 -0
  42. package/src/pattern/dcbor-integration.ts +40 -8
  43. package/src/pattern/index.ts +29 -0
  44. package/src/pattern/leaf/array-pattern.ts +67 -169
  45. package/src/pattern/leaf/cbor-pattern.ts +37 -23
  46. package/src/pattern/leaf/index.ts +1 -1
  47. package/src/pattern/leaf/map-pattern.ts +21 -2
  48. package/src/pattern/leaf/tagged-pattern.ts +6 -1
  49. package/src/pattern/meta/search-pattern.ts +13 -38
  50. package/src/pattern/meta/traverse-pattern.ts +2 -2
  51. package/src/pattern/structure/assertions-pattern.ts +19 -53
  52. package/src/pattern/structure/digest-pattern.ts +18 -22
  53. package/src/pattern/structure/index.ts +3 -0
  54. package/src/pattern/structure/node-pattern.ts +10 -29
  55. package/src/pattern/structure/object-pattern.ts +2 -2
  56. package/src/pattern/structure/predicate-pattern.ts +2 -2
  57. package/src/pattern/structure/subject-pattern.ts +31 -4
  58. package/src/pattern/structure/wrapped-pattern.ts +28 -9
  59. package/src/pattern/vm.ts +4 -4
@@ -1,4 +1,4 @@
1
- var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_values, _bcts_envelope, _bcts_dcbor, _bcts_dcbor_parse) {
1
+ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_values, _bcts_dcbor, _bcts_envelope, _bcts_dcbor_parse) {
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  //#region src/error.ts
4
4
  /**
@@ -302,6 +302,14 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
302
302
  /**
303
303
  * Gets a summary of an envelope for display.
304
304
  *
305
+ * Mirrors Rust `envelope_summary` in `format.rs`: defers to
306
+ * `Envelope::format_flat()` for nodes / wrapped / assertions and to
307
+ * `cbor.envelope_summary(usize::MAX, ...)` for raw CBOR leaves. The
308
+ * obscured cases (`elided` / `encrypted` / `compressed`) emit just the
309
+ * keyword. KnownValue envelopes look up the canonical name via
310
+ * `KnownValue.name()`, matching the Rust call to
311
+ * `KnownValuesStore::known_value_for_raw_value(value, …)`.
312
+ *
305
313
  * @param env - The envelope to summarize
306
314
  * @returns A string summary of the envelope
307
315
  */
@@ -310,25 +318,17 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
310
318
  const c = env.case();
311
319
  let summary;
312
320
  switch (c.type) {
313
- case "node": {
314
- const subjectSummary = env.subject().summary(Number.MAX_SAFE_INTEGER);
315
- const assertions = env.assertions();
316
- if (assertions.length > 0) summary = `NODE ${subjectSummary} [ ${assertions.map((a) => {
317
- const ac = a.case();
318
- if (ac.type === "assertion") return `${ac.assertion.predicate().summary(Number.MAX_SAFE_INTEGER)}: ${ac.assertion.object().summary(Number.MAX_SAFE_INTEGER)}`;
319
- return a.summary(Number.MAX_SAFE_INTEGER);
320
- }).join(", ")} ]`;
321
- else summary = `NODE ${subjectSummary}`;
321
+ case "node":
322
+ summary = `NODE ${env.formatFlat()}`;
322
323
  break;
323
- }
324
324
  case "leaf":
325
325
  summary = `LEAF ${env.summary(Number.MAX_SAFE_INTEGER)}`;
326
326
  break;
327
327
  case "wrapped":
328
- summary = `WRAPPED ${env.summary(Number.MAX_SAFE_INTEGER)}`;
328
+ summary = `WRAPPED ${env.formatFlat()}`;
329
329
  break;
330
330
  case "assertion":
331
- summary = `ASSERTION ${c.assertion.predicate().summary(Number.MAX_SAFE_INTEGER)}: ${c.assertion.object().summary(Number.MAX_SAFE_INTEGER)}`;
331
+ summary = `ASSERTION ${env.formatFlat()}`;
332
332
  break;
333
333
  case "elided":
334
334
  summary = "ELIDED";
@@ -374,8 +374,8 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
374
374
  if (element === void 0) return "";
375
375
  switch (opts.elementFormat.type) {
376
376
  case "Summary": return truncateWithEllipsis(envelopeSummary(element), opts.elementFormat.maxLength);
377
- case "EnvelopeUR": return element.digest().toString();
378
- case "DigestUR": return element.digest().toString();
377
+ case "EnvelopeUR": return element.urString();
378
+ case "DigestUR": return element.digest().urString();
379
379
  }
380
380
  }
381
381
  switch (opts.elementFormat.type) {
@@ -390,8 +390,8 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
390
390
  }
391
391
  return lines.join("\n");
392
392
  }
393
- case "EnvelopeUR": return path.map((element) => element.digest().toString()).join(" ");
394
- case "DigestUR": return path.map((element) => element.digest().toString()).join(" ");
393
+ case "EnvelopeUR": return path.map((element) => element.urString()).join(" ");
394
+ case "DigestUR": return path.map((element) => element.digest().urString()).join(" ");
395
395
  }
396
396
  }
397
397
  /**
@@ -1233,25 +1233,16 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1233
1233
  };
1234
1234
  //#endregion
1235
1235
  //#region src/pattern/leaf/array-pattern.ts
1236
- /**
1237
- * Copyright © 2023-2026 Blockchain Commons, LLC
1238
- * Copyright © 2025-2026 Parity Technologies
1239
- *
1240
- *
1241
- * @bcts/envelope-pattern - Array pattern matching
1242
- *
1243
- * This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs
1244
- *
1245
- * @module envelope-pattern/pattern/leaf/array-pattern
1246
- */
1247
1236
  let createLeafArrayPattern;
1248
1237
  function registerArrayPatternFactory(factory) {
1249
1238
  createLeafArrayPattern = factory;
1250
1239
  }
1251
1240
  /**
1252
- * Pattern for matching array values in envelope leaf nodes.
1241
+ * Pattern for matching arrays.
1253
1242
  *
1254
- * Corresponds to the Rust `ArrayPattern` struct in array_pattern.rs
1243
+ * Mirrors Rust `ArrayPattern(dcbor_pattern::ArrayPattern)` from
1244
+ * `bc-envelope-pattern-rust/src/pattern/leaf/array_pattern.rs`. All
1245
+ * matching, display, and equality is delegated to dcbor-pattern.
1255
1246
  */
1256
1247
  var ArrayPattern = class ArrayPattern {
1257
1248
  _pattern;
@@ -1262,93 +1253,54 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1262
1253
  * Creates a new ArrayPattern that matches any array.
1263
1254
  */
1264
1255
  static any() {
1265
- return new ArrayPattern({ type: "Any" });
1256
+ return new ArrayPattern((0, _bcts_dcbor_pattern.arrayPatternAny)());
1266
1257
  }
1267
1258
  /**
1268
1259
  * Creates a new ArrayPattern that matches arrays with a specific length.
1269
1260
  */
1270
1261
  static count(count) {
1271
- return new ArrayPattern({
1272
- type: "Interval",
1273
- interval: _bcts_dcbor_pattern.Interval.exactly(count)
1274
- });
1262
+ return new ArrayPattern((0, _bcts_dcbor_pattern.arrayPatternWithLength)(count));
1275
1263
  }
1276
1264
  /**
1277
1265
  * Creates a new ArrayPattern that matches arrays within a length range.
1278
1266
  */
1279
1267
  static interval(min, max) {
1280
- return new ArrayPattern({
1281
- type: "Interval",
1282
- interval: max !== void 0 ? _bcts_dcbor_pattern.Interval.from(min, max) : _bcts_dcbor_pattern.Interval.atLeast(min)
1283
- });
1268
+ return new ArrayPattern((0, _bcts_dcbor_pattern.arrayPatternWithLengthRange)(min, max));
1284
1269
  }
1285
1270
  /**
1286
- * Creates a new ArrayPattern from a dcbor-pattern.
1271
+ * Creates a new ArrayPattern from a length Interval.
1287
1272
  */
1288
- static fromDcborPattern(dcborPattern) {
1289
- return new ArrayPattern({
1290
- type: "DCBORPattern",
1291
- pattern: dcborPattern
1292
- });
1273
+ static fromInterval(interval) {
1274
+ return new ArrayPattern((0, _bcts_dcbor_pattern.arrayPatternWithLengthInterval)(interval));
1293
1275
  }
1294
1276
  /**
1295
- * Creates a new ArrayPattern with envelope patterns for element matching.
1277
+ * Creates a new ArrayPattern from a top-level dcbor-pattern.
1278
+ *
1279
+ * Mirrors Rust `ArrayPattern::from_dcbor_pattern`, which constructs an
1280
+ * `ArrayPattern::Elements`-style dcbor array pattern.
1296
1281
  */
1297
- static withPatterns(patterns) {
1298
- return new ArrayPattern({
1299
- type: "WithPatterns",
1300
- patterns
1301
- });
1282
+ static fromDcborPattern(pattern) {
1283
+ return new ArrayPattern((0, _bcts_dcbor_pattern.arrayPatternWithElements)(pattern));
1302
1284
  }
1303
1285
  /**
1304
- * Gets the pattern type.
1286
+ * Creates a new ArrayPattern from an existing dcbor-pattern ArrayPattern.
1287
+ *
1288
+ * Mirrors Rust `ArrayPattern::from_dcbor_array_pattern`.
1305
1289
  */
1306
- get pattern() {
1290
+ static fromDcborArrayPattern(arrayPattern) {
1291
+ return new ArrayPattern(arrayPattern);
1292
+ }
1293
+ /**
1294
+ * Returns the underlying dcbor-pattern ArrayPattern.
1295
+ */
1296
+ inner() {
1307
1297
  return this._pattern;
1308
1298
  }
1309
1299
  pathsWithCaptures(haystack) {
1310
1300
  const cbor = haystack.subject().asLeaf();
1311
1301
  if (cbor === void 0) return [[], /* @__PURE__ */ new Map()];
1312
- const array = (0, _bcts_dcbor.asCborArray)(cbor);
1313
- if (array === void 0) return [[], /* @__PURE__ */ new Map()];
1314
- switch (this._pattern.type) {
1315
- case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1316
- case "Interval": {
1317
- const length = array.length;
1318
- if (this._pattern.interval.contains(length)) return [[[haystack]], /* @__PURE__ */ new Map()];
1319
- return [[], /* @__PURE__ */ new Map()];
1320
- }
1321
- case "DCBORPattern": {
1322
- const { paths: dcborPaths, captures: dcborCaptures } = (0, _bcts_dcbor_pattern.patternPathsWithCaptures)(this._pattern.pattern, cbor);
1323
- if (dcborPaths.length > 0) {
1324
- const envelopePaths = dcborPaths.map((dcborPath) => {
1325
- const envPath = [haystack];
1326
- for (let i = 1; i < dcborPath.length; i++) {
1327
- const elem = dcborPath[i];
1328
- if (elem !== void 0) envPath.push(_bcts_envelope.Envelope.newLeaf(elem));
1329
- }
1330
- return envPath;
1331
- });
1332
- const envelopeCaptures = /* @__PURE__ */ new Map();
1333
- for (const [name, capturePaths] of dcborCaptures) {
1334
- const envCapturePaths = capturePaths.map((dcborPath) => {
1335
- const envPath = [haystack];
1336
- for (let i = 1; i < dcborPath.length; i++) {
1337
- const elem = dcborPath[i];
1338
- if (elem !== void 0) envPath.push(_bcts_envelope.Envelope.newLeaf(elem));
1339
- }
1340
- return envPath;
1341
- });
1342
- envelopeCaptures.set(name, envCapturePaths);
1343
- }
1344
- return [envelopePaths, envelopeCaptures];
1345
- }
1346
- return [[], /* @__PURE__ */ new Map()];
1347
- }
1348
- case "WithPatterns":
1349
- if (array.length === this._pattern.patterns.length) return [[[haystack]], /* @__PURE__ */ new Map()];
1350
- return [[], /* @__PURE__ */ new Map()];
1351
- }
1302
+ if ((0, _bcts_dcbor_pattern.arrayPatternMatches)(this._pattern, cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1303
+ return [[], /* @__PURE__ */ new Map()];
1352
1304
  }
1353
1305
  paths(haystack) {
1354
1306
  return this.pathsWithCaptures(haystack)[0];
@@ -1364,54 +1316,31 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1364
1316
  return false;
1365
1317
  }
1366
1318
  toString() {
1367
- switch (this._pattern.type) {
1368
- case "Any": return "[*]";
1369
- case "Interval": return `[{${this._pattern.interval.toString()}}]`;
1370
- case "DCBORPattern": return (0, _bcts_dcbor_pattern.patternDisplay)(this._pattern.pattern);
1371
- case "WithPatterns": return `[${this._pattern.patterns.map(String).join(", ")}]`;
1372
- }
1319
+ return (0, _bcts_dcbor_pattern.arrayPatternDisplay)(this._pattern, _bcts_dcbor_pattern.patternDisplay);
1373
1320
  }
1374
1321
  /**
1375
- * Equality comparison.
1322
+ * Equality comparison. Delegates to dcbor-pattern's structural equality
1323
+ * with a display-string fallback for pattern-equality (mirrors Rust's
1324
+ * `Hash` impl that hashes the display, since dcbor `ArrayPattern`
1325
+ * itself does not derive `Hash`).
1376
1326
  */
1377
1327
  equals(other) {
1378
- if (this._pattern.type !== other._pattern.type) return false;
1379
- switch (this._pattern.type) {
1380
- case "Any": return true;
1381
- case "Interval": return this._pattern.interval.equals(other._pattern.interval);
1382
- case "DCBORPattern": return (0, _bcts_dcbor_pattern.patternDisplay)(this._pattern.pattern) === (0, _bcts_dcbor_pattern.patternDisplay)(other._pattern.pattern);
1383
- case "WithPatterns": {
1384
- const otherPatterns = other._pattern.patterns;
1385
- if (this._pattern.patterns.length !== otherPatterns.length) return false;
1386
- for (let i = 0; i < this._pattern.patterns.length; i++) if (this._pattern.patterns[i] !== otherPatterns[i]) return false;
1387
- return true;
1388
- }
1389
- }
1328
+ return (0, _bcts_dcbor_pattern.arrayPatternEquals)(this._pattern, other._pattern, (a, b) => (0, _bcts_dcbor_pattern.patternDisplay)(a) === (0, _bcts_dcbor_pattern.patternDisplay)(b));
1390
1329
  }
1391
1330
  /**
1392
- * Hash code for use in Maps/Sets.
1331
+ * Hash code for use in Maps/Sets. Mirrors Rust's
1332
+ * "hash the string representation" approach.
1393
1333
  */
1394
1334
  hashCode() {
1395
- switch (this._pattern.type) {
1396
- case "Any": return 0;
1397
- case "Interval": return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
1398
- case "DCBORPattern": return simpleStringHash$3((0, _bcts_dcbor_pattern.patternDisplay)(this._pattern.pattern));
1399
- case "WithPatterns": return this._pattern.patterns.length;
1335
+ let hash = 0;
1336
+ const str = this.toString();
1337
+ for (let i = 0; i < str.length; i++) {
1338
+ hash = (hash << 5) - hash + str.charCodeAt(i);
1339
+ hash = hash & hash;
1400
1340
  }
1341
+ return hash;
1401
1342
  }
1402
1343
  };
1403
- /**
1404
- * Simple string hash function for hashCode implementations.
1405
- */
1406
- function simpleStringHash$3(str) {
1407
- let hash = 0;
1408
- for (let i = 0; i < str.length; i++) {
1409
- const char = str.charCodeAt(i);
1410
- hash = (hash << 5) - hash + char;
1411
- hash = hash & hash;
1412
- }
1413
- return hash;
1414
- }
1415
1344
  //#endregion
1416
1345
  //#region src/pattern/leaf/map-pattern.ts
1417
1346
  let createLeafMapPattern;
@@ -1444,6 +1373,19 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1444
1373
  });
1445
1374
  }
1446
1375
  /**
1376
+ * Creates a new MapPattern from a length Interval.
1377
+ *
1378
+ * Mirrors Rust `MapPattern::from_interval`. Used by the
1379
+ * dcbor-pattern → envelope-pattern bridge to preserve `{{n,m}}`
1380
+ * length info.
1381
+ */
1382
+ static fromInterval(interval) {
1383
+ return new MapPattern({
1384
+ type: "Interval",
1385
+ interval
1386
+ });
1387
+ }
1388
+ /**
1447
1389
  * Gets the pattern type.
1448
1390
  */
1449
1391
  get pattern() {
@@ -1478,8 +1420,8 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1478
1420
  }
1479
1421
  toString() {
1480
1422
  switch (this._pattern.type) {
1481
- case "Any": return "{*}";
1482
- case "Interval": return `{{${this._pattern.interval.toString()}}}`;
1423
+ case "Any": return "map";
1424
+ case "Interval": return `{${this._pattern.interval.toString()}}`;
1483
1425
  }
1484
1426
  }
1485
1427
  /**
@@ -1732,7 +1674,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1732
1674
  return false;
1733
1675
  }
1734
1676
  toString() {
1735
- return (0, _bcts_dcbor_pattern.taggedPatternDisplay)(this._inner, _bcts_dcbor_pattern.patternDisplay);
1677
+ return (0, _bcts_dcbor_pattern.taggedPatternDisplay)(this._inner, _bcts_dcbor_pattern.patternDisplay).replace(", ", ", ");
1736
1678
  }
1737
1679
  /**
1738
1680
  * Equality comparison.
@@ -1858,10 +1800,18 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1858
1800
  }
1859
1801
  /**
1860
1802
  * Convert a single dcbor path to an envelope path.
1803
+ *
1804
+ * Uses canonical CBOR-byte equality (`cborEquals`) for the "skip the
1805
+ * dcbor root if it duplicates our base envelope" check, mirroring
1806
+ * Rust's `dcbor_path.first().map(|first| first == &base_cbor)`. The
1807
+ * earlier port compared diagnostic strings, which collapses values
1808
+ * that share a textual representation but differ structurally
1809
+ * (e.g. NaN payloads).
1861
1810
  */
1862
1811
  _convertDcborPathToEnvelopePath(dcborPath, baseEnvelope, baseCbor) {
1863
1812
  const envelopePath = [baseEnvelope];
1864
- const elementsToAdd = dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === baseCbor.toDiagnostic() ? dcborPath.slice(1) : dcborPath;
1813
+ const first = dcborPath[0];
1814
+ const elementsToAdd = first !== void 0 && (0, _bcts_dcbor.cborEquals)(first, baseCbor) ? dcborPath.slice(1) : dcborPath;
1865
1815
  for (const cborElement of elementsToAdd) envelopePath.push(_bcts_envelope.Envelope.newLeaf(cborElement));
1866
1816
  return envelopePath;
1867
1817
  }
@@ -1888,7 +1838,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1888
1838
  switch (this._pattern.type) {
1889
1839
  case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1890
1840
  case "Value":
1891
- if (knownValueCbor.toDiagnostic() === this._pattern.cbor.toDiagnostic()) return [[[haystack]], /* @__PURE__ */ new Map()];
1841
+ if ((0, _bcts_dcbor.cborEquals)(knownValueCbor, this._pattern.cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1892
1842
  return [[], /* @__PURE__ */ new Map()];
1893
1843
  case "Pattern": {
1894
1844
  const { paths: dcborPaths, captures: dcborCaptures } = (0, _bcts_dcbor_pattern.patternPathsWithCaptures)(this._pattern.pattern, knownValueCbor);
@@ -1912,7 +1862,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1912
1862
  switch (this._pattern.type) {
1913
1863
  case "Any": return [[[haystack]], /* @__PURE__ */ new Map()];
1914
1864
  case "Value":
1915
- if (leafCbor.toDiagnostic() === this._pattern.cbor.toDiagnostic()) return [[[haystack]], /* @__PURE__ */ new Map()];
1865
+ if ((0, _bcts_dcbor.cborEquals)(leafCbor, this._pattern.cbor)) return [[[haystack]], /* @__PURE__ */ new Map()];
1916
1866
  return [[], /* @__PURE__ */ new Map()];
1917
1867
  case "Pattern": {
1918
1868
  const { paths: dcborPaths, captures: dcborCaptures } = (0, _bcts_dcbor_pattern.patternPathsWithCaptures)(this._pattern.pattern, leafCbor);
@@ -1920,7 +1870,8 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1920
1870
  const basePath = [haystack];
1921
1871
  return [dcborPaths.map((dcborPath) => {
1922
1872
  const extendedPath = [...basePath];
1923
- const elementsToAdd = dcborPath.length > 0 && dcborPath[0]?.toDiagnostic() === leafCbor.toDiagnostic() ? dcborPath.slice(1) : dcborPath;
1873
+ const first = dcborPath[0];
1874
+ const elementsToAdd = first !== void 0 && (0, _bcts_dcbor.cborEquals)(first, leafCbor) ? dcborPath.slice(1) : dcborPath;
1924
1875
  for (const cborElement of elementsToAdd) extendedPath.push(_bcts_envelope.Envelope.newLeaf(cborElement));
1925
1876
  return extendedPath;
1926
1877
  }), this._convertDcborCapturesToEnvelopeCaptures(dcborCaptures, haystack, leafCbor)];
@@ -1955,13 +1906,16 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1955
1906
  }
1956
1907
  }
1957
1908
  /**
1958
- * Equality comparison.
1909
+ * Equality comparison. `Value` variants compare by canonical CBOR
1910
+ * byte sequence (mirrors Rust `==` on `CBOR`); `Pattern` variants fall
1911
+ * back to display-string compare since `DCBORPattern` doesn't expose
1912
+ * structural equality outside the crate.
1959
1913
  */
1960
1914
  equals(other) {
1961
1915
  if (this._pattern.type !== other._pattern.type) return false;
1962
1916
  switch (this._pattern.type) {
1963
1917
  case "Any": return true;
1964
- case "Value": return this._pattern.cbor.toDiagnostic() === other._pattern.cbor.toDiagnostic();
1918
+ case "Value": return (0, _bcts_dcbor.cborEquals)(this._pattern.cbor, other._pattern.cbor);
1965
1919
  case "Pattern": return (0, _bcts_dcbor_pattern.patternDisplay)(this._pattern.pattern) === (0, _bcts_dcbor_pattern.patternDisplay)(other._pattern.pattern);
1966
1920
  }
1967
1921
  }
@@ -1971,7 +1925,12 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
1971
1925
  hashCode() {
1972
1926
  switch (this._pattern.type) {
1973
1927
  case "Any": return 0;
1974
- case "Value": return simpleStringHash(this._pattern.cbor.toDiagnostic());
1928
+ case "Value": {
1929
+ const bytes = (0, _bcts_dcbor.cborData)(this._pattern.cbor);
1930
+ let hash = 0;
1931
+ for (const byte of bytes) hash = hash * 31 + byte | 0;
1932
+ return hash;
1933
+ }
1975
1934
  case "Pattern": return simpleStringHash((0, _bcts_dcbor_pattern.patternDisplay)(this._pattern.pattern));
1976
1935
  }
1977
1936
  }
@@ -2240,7 +2199,13 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2240
2199
  return 0;
2241
2200
  }
2242
2201
  };
2202
+ let dispatchPatternCompile$1;
2203
+ let dispatchPatternToString$3;
2243
2204
  function registerSubjectPatternFactory(factory) {}
2205
+ function registerSubjectPatternDispatch(dispatch) {
2206
+ dispatchPatternCompile$1 = dispatch.compile;
2207
+ dispatchPatternToString$3 = dispatch.toString;
2208
+ }
2244
2209
  /**
2245
2210
  * Pattern for matching subjects in envelopes.
2246
2211
  *
@@ -2304,9 +2269,10 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2304
2269
  code.push({ type: "NavigateSubject" });
2305
2270
  break;
2306
2271
  case "Pattern":
2272
+ if (dispatchPatternCompile$1 === void 0) throw new Error("SubjectPattern.compile requires the top-level Pattern compile dispatch; not registered");
2307
2273
  code.push({ type: "NavigateSubject" });
2308
2274
  code.push({ type: "ExtendTraversal" });
2309
- this._pattern.pattern.compile(code, literals, captures);
2275
+ dispatchPatternCompile$1(this._pattern.pattern, code, literals, captures);
2310
2276
  code.push({ type: "CombineTraversal" });
2311
2277
  break;
2312
2278
  }
@@ -2317,7 +2283,10 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2317
2283
  toString() {
2318
2284
  switch (this._pattern.type) {
2319
2285
  case "Any": return "subj";
2320
- case "Pattern": return `subj(${this._pattern.pattern.toString()})`;
2286
+ case "Pattern": {
2287
+ const fmt = dispatchPatternToString$3;
2288
+ return `subj(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2289
+ }
2321
2290
  }
2322
2291
  }
2323
2292
  /**
@@ -2414,7 +2383,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2414
2383
  toString() {
2415
2384
  switch (this._pattern.type) {
2416
2385
  case "Any": return "pred";
2417
- case "Pattern": return `pred(${this._pattern.pattern.toString()})`;
2386
+ case "Pattern": return `pred(${dispatchPatternToString(this._pattern.pattern)})`;
2418
2387
  }
2419
2388
  }
2420
2389
  /**
@@ -2511,7 +2480,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2511
2480
  toString() {
2512
2481
  switch (this._pattern.type) {
2513
2482
  case "Any": return "obj";
2514
- case "Pattern": return `obj(${this._pattern.pattern.toString()})`;
2483
+ case "Pattern": return `obj(${dispatchPatternToString(this._pattern.pattern)})`;
2515
2484
  }
2516
2485
  }
2517
2486
  /**
@@ -2532,9 +2501,13 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2532
2501
  //#endregion
2533
2502
  //#region src/pattern/structure/assertions-pattern.ts
2534
2503
  let createStructureAssertionsPattern;
2504
+ let dispatchPatternToString$2;
2535
2505
  function registerAssertionsPatternFactory(factory) {
2536
2506
  createStructureAssertionsPattern = factory;
2537
2507
  }
2508
+ function registerAssertionsPatternToStringDispatch(fn) {
2509
+ dispatchPatternToString$2 = fn;
2510
+ }
2538
2511
  /**
2539
2512
  * Pattern for matching assertions in envelopes.
2540
2513
  *
@@ -2572,17 +2545,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2572
2545
  });
2573
2546
  }
2574
2547
  /**
2575
- * Creates a new AssertionsPattern that matches assertions with both
2576
- * predicate and object patterns.
2577
- */
2578
- static withBoth(predicatePattern, objectPattern) {
2579
- return new AssertionsPattern({
2580
- type: "WithBoth",
2581
- predicatePattern,
2582
- objectPattern
2583
- });
2584
- }
2585
- /**
2586
2548
  * Gets the pattern type.
2587
2549
  */
2588
2550
  get patternType() {
@@ -2593,14 +2555,12 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2593
2555
  */
2594
2556
  predicatePattern() {
2595
2557
  if (this._pattern.type === "WithPredicate") return this._pattern.pattern;
2596
- if (this._pattern.type === "WithBoth") return this._pattern.predicatePattern;
2597
2558
  }
2598
2559
  /**
2599
2560
  * Gets the object pattern if this has one, undefined otherwise.
2600
2561
  */
2601
2562
  objectPattern() {
2602
2563
  if (this._pattern.type === "WithObject") return this._pattern.pattern;
2603
- if (this._pattern.type === "WithBoth") return this._pattern.objectPattern;
2604
2564
  }
2605
2565
  pathsWithCaptures(haystack) {
2606
2566
  const paths = [];
@@ -2622,14 +2582,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2622
2582
  }
2623
2583
  break;
2624
2584
  }
2625
- case "WithBoth": {
2626
- const predicate = assertion.asPredicate?.();
2627
- const object = assertion.asObject?.();
2628
- if (predicate !== void 0 && object !== void 0) {
2629
- if (matchPattern(this._pattern.predicatePattern, predicate) && matchPattern(this._pattern.objectPattern, object)) paths.push([assertion]);
2630
- }
2631
- break;
2632
- }
2633
2585
  }
2634
2586
  return [paths, /* @__PURE__ */ new Map()];
2635
2587
  }
@@ -2652,11 +2604,11 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2652
2604
  return false;
2653
2605
  }
2654
2606
  toString() {
2607
+ const fmt = dispatchPatternToString$2;
2655
2608
  switch (this._pattern.type) {
2656
2609
  case "Any": return "assert";
2657
- case "WithPredicate": return `assertpred(${this._pattern.pattern.toString()})`;
2658
- case "WithObject": return `assertobj(${this._pattern.pattern.toString()})`;
2659
- case "WithBoth": return `assert(${this._pattern.predicatePattern.toString()}, ${this._pattern.objectPattern.toString()})`;
2610
+ case "WithPredicate": return `assertpred(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2611
+ case "WithObject": return `assertobj(${fmt !== void 0 ? fmt(this._pattern.pattern) : "?"})`;
2660
2612
  }
2661
2613
  }
2662
2614
  /**
@@ -2668,10 +2620,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2668
2620
  case "Any": return true;
2669
2621
  case "WithPredicate":
2670
2622
  case "WithObject": return this._pattern.pattern === other._pattern.pattern;
2671
- case "WithBoth": {
2672
- const otherBoth = other._pattern;
2673
- return this._pattern.predicatePattern === otherBoth.predicatePattern && this._pattern.objectPattern === otherBoth.objectPattern;
2674
- }
2675
2623
  }
2676
2624
  }
2677
2625
  /**
@@ -2682,7 +2630,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2682
2630
  case "Any": return 0;
2683
2631
  case "WithPredicate": return 1;
2684
2632
  case "WithObject": return 2;
2685
- case "WithBoth": return 3;
2686
2633
  }
2687
2634
  }
2688
2635
  };
@@ -2711,12 +2658,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2711
2658
  this._pattern = pattern;
2712
2659
  }
2713
2660
  /**
2714
- * Creates a new DigestPattern that matches any digest.
2715
- */
2716
- static any() {
2717
- return new DigestPattern({ type: "Any" });
2718
- }
2719
- /**
2720
2661
  * Creates a new DigestPattern that matches the exact digest.
2721
2662
  */
2722
2663
  static digest(digest) {
@@ -2754,9 +2695,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2754
2695
  const digestData = digest.data();
2755
2696
  let isHit = false;
2756
2697
  switch (this._pattern.type) {
2757
- case "Any":
2758
- isHit = true;
2759
- break;
2760
2698
  case "Digest":
2761
2699
  isHit = digest.equals(this._pattern.digest);
2762
2700
  break;
@@ -2794,7 +2732,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2794
2732
  }
2795
2733
  toString() {
2796
2734
  switch (this._pattern.type) {
2797
- case "Any": return "digest";
2798
2735
  case "Digest": return `digest(${this._pattern.digest.hex()})`;
2799
2736
  case "Prefix": return `digest(${(0, _bcts_dcbor.bytesToHex)(this._pattern.prefix)})`;
2800
2737
  case "BinaryRegex": return `digest(/${this._pattern.regex.source}/)`;
@@ -2802,17 +2739,26 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2802
2739
  }
2803
2740
  /**
2804
2741
  * Equality comparison.
2742
+ *
2743
+ * `Prefix` comparison is case-insensitive on the *hex representation* to
2744
+ * mirror Rust's `eq_ignore_ascii_case` (which compares the underlying
2745
+ * `Vec<u8>` of hex bytes byte-for-byte modulo ASCII case). For raw byte
2746
+ * prefixes that happen to be ASCII, this is an ordinary byte compare.
2805
2747
  */
2806
2748
  equals(other) {
2807
2749
  if (this._pattern.type !== other._pattern.type) return false;
2808
2750
  switch (this._pattern.type) {
2809
- case "Any": return true;
2810
2751
  case "Digest": return this._pattern.digest.equals(other._pattern.digest);
2811
2752
  case "Prefix": {
2812
2753
  const thisPrefix = this._pattern.prefix;
2813
2754
  const otherPrefix = other._pattern.prefix;
2814
2755
  if (thisPrefix.length !== otherPrefix.length) return false;
2815
- for (let i = 0; i < thisPrefix.length; i++) if (thisPrefix[i] !== otherPrefix[i]) return false;
2756
+ for (let i = 0; i < thisPrefix.length; i++) {
2757
+ const a = thisPrefix[i];
2758
+ const b = otherPrefix[i];
2759
+ if (a === b) continue;
2760
+ if ((a >= 65 && a <= 90 ? a + 32 : a) !== (b >= 65 && b <= 90 ? b + 32 : b)) return false;
2761
+ }
2816
2762
  return true;
2817
2763
  }
2818
2764
  case "BinaryRegex": return this._pattern.regex.source === other._pattern.regex.source;
@@ -2823,7 +2769,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2823
2769
  */
2824
2770
  hashCode() {
2825
2771
  switch (this._pattern.type) {
2826
- case "Any": return 0;
2827
2772
  case "Digest": {
2828
2773
  const data = this._pattern.digest.data().slice(0, 8);
2829
2774
  let hash = 0;
@@ -2832,7 +2777,10 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2832
2777
  }
2833
2778
  case "Prefix": {
2834
2779
  let hash = 0;
2835
- for (const byte of this._pattern.prefix) hash = hash * 31 + byte | 0;
2780
+ for (const byte of this._pattern.prefix) {
2781
+ const folded = byte >= 65 && byte <= 90 ? byte + 32 : byte;
2782
+ hash = hash * 31 + folded | 0;
2783
+ }
2836
2784
  return hash;
2837
2785
  }
2838
2786
  case "BinaryRegex": {
@@ -2884,28 +2832,19 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2884
2832
  });
2885
2833
  }
2886
2834
  /**
2887
- * Creates a new NodePattern with a subject pattern constraint.
2888
- */
2889
- static withSubject(subjectPattern) {
2890
- return new NodePattern({
2891
- type: "WithSubject",
2892
- subjectPattern
2893
- });
2894
- }
2895
- /**
2896
2835
  * Gets the pattern type.
2897
2836
  */
2898
2837
  get patternType() {
2899
2838
  return this._pattern;
2900
2839
  }
2901
2840
  /**
2902
- * Gets the subject pattern if this is a WithSubject type, undefined otherwise.
2841
+ * Returns the subject pattern, if any. Rust's `NodePattern` does not carry
2842
+ * subject patterns, so this always returns `undefined`.
2903
2843
  */
2904
- subjectPattern() {
2905
- return this._pattern.type === "WithSubject" ? this._pattern.subjectPattern : void 0;
2906
- }
2844
+ subjectPattern() {}
2907
2845
  /**
2908
- * Gets the assertion patterns (empty array if none).
2846
+ * Returns the assertion patterns. Rust's `NodePattern` does not carry
2847
+ * assertion sub-patterns, so this always returns an empty array.
2909
2848
  */
2910
2849
  assertionPatterns() {
2911
2850
  return [];
@@ -2920,9 +2859,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2920
2859
  case "AssertionsInterval":
2921
2860
  isHit = this._pattern.interval.contains(haystack.assertions().length);
2922
2861
  break;
2923
- case "WithSubject":
2924
- isHit = true;
2925
- break;
2926
2862
  }
2927
2863
  return [isHit ? [[haystack]] : [], /* @__PURE__ */ new Map()];
2928
2864
  }
@@ -2943,7 +2879,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2943
2879
  switch (this._pattern.type) {
2944
2880
  case "Any": return "node";
2945
2881
  case "AssertionsInterval": return `node(${this._pattern.interval.toString()})`;
2946
- case "WithSubject": return `node(${this._pattern.subjectPattern.toString()})`;
2947
2882
  }
2948
2883
  }
2949
2884
  /**
@@ -2954,7 +2889,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2954
2889
  switch (this._pattern.type) {
2955
2890
  case "Any": return true;
2956
2891
  case "AssertionsInterval": return this._pattern.interval.equals(other._pattern.interval);
2957
- case "WithSubject": return this._pattern.subjectPattern === other._pattern.subjectPattern;
2958
2892
  }
2959
2893
  }
2960
2894
  /**
@@ -2964,7 +2898,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
2964
2898
  switch (this._pattern.type) {
2965
2899
  case "Any": return 0;
2966
2900
  case "AssertionsInterval": return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
2967
- case "WithSubject": return 1;
2968
2901
  }
2969
2902
  }
2970
2903
  };
@@ -3074,12 +3007,16 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
3074
3007
  //#endregion
3075
3008
  //#region src/pattern/structure/wrapped-pattern.ts
3076
3009
  let createStructureWrappedPattern;
3010
+ let createAnyPattern;
3077
3011
  let dispatchPatternPathsWithCaptures;
3078
3012
  let dispatchPatternCompile;
3079
3013
  let dispatchPatternToString$1;
3080
3014
  function registerWrappedPatternFactory(factory) {
3081
3015
  createStructureWrappedPattern = factory;
3082
3016
  }
3017
+ function registerWrappedPatternAny(factory) {
3018
+ createAnyPattern = factory;
3019
+ }
3083
3020
  function registerWrappedPatternDispatch(dispatch) {
3084
3021
  dispatchPatternPathsWithCaptures = dispatch.pathsWithCaptures;
3085
3022
  dispatchPatternCompile = dispatch.compile;
@@ -3113,10 +3050,15 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
3113
3050
  }
3114
3051
  /**
3115
3052
  * Creates a new WrappedPattern that matches any wrapped envelope and descends into it.
3116
- * Note: This requires Pattern.any() to be available, so it's set up during registration.
3053
+ *
3054
+ * Mirrors Rust `WrappedPattern::unwrap()` which delegates to
3055
+ * `Self::unwrap_matching(Pattern::any())`. The `any` factory is wired in
3056
+ * during module-load registration to break the circular import on the
3057
+ * top-level `Pattern` type.
3117
3058
  */
3118
3059
  static unwrap() {
3119
- return new WrappedPattern({ type: "Any" });
3060
+ if (createAnyPattern === void 0) throw new Error("WrappedPattern.unwrap() requires Pattern.any factory; not registered");
3061
+ return WrappedPattern.unwrapMatching(createAnyPattern());
3120
3062
  }
3121
3063
  /**
3122
3064
  * Gets the pattern type.
@@ -3192,9 +3134,9 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
3192
3134
  switch (this._pattern.type) {
3193
3135
  case "Any": return "wrapped";
3194
3136
  case "Unwrap": {
3195
- const patternStr = dispatchPatternToString$1 !== void 0 ? dispatchPatternToString$1(this._pattern.pattern) : "*";
3196
- if (patternStr === "*") return "unwrap";
3197
- return `unwrap(${patternStr})`;
3137
+ const inner = this._pattern.pattern;
3138
+ if (inner.type === "Meta" && inner.pattern.type === "Any") return "unwrap";
3139
+ return `unwrap(${dispatchPatternToString$1 !== void 0 ? dispatchPatternToString$1(inner) : "?"})`;
3198
3140
  }
3199
3141
  }
3200
3142
  }
@@ -3418,10 +3360,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
3418
3360
  case "Wrapped":
3419
3361
  if (envCase.type === "node") {
3420
3362
  const subject = envCase.subject;
3421
- if (subject.isWrapped()) {
3422
- const unwrapped = subject.unwrap();
3423
- if (unwrapped !== void 0) return [[unwrapped, "Content"]];
3424
- }
3363
+ if (subject.isWrapped()) return [[subject.tryUnwrap(), "Content"]];
3425
3364
  } else if (envCase.type === "wrapped") return [[envCase.envelope, "Content"]];
3426
3365
  return [];
3427
3366
  }
@@ -4419,30 +4358,20 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
4419
4358
  return [uniquePaths, /* @__PURE__ */ new Map()];
4420
4359
  }
4421
4360
  /**
4422
- * Walk the envelope tree recursively.
4361
+ * Walk the envelope tree using the canonical `Envelope.walk` traversal.
4362
+ *
4363
+ * Mirrors Rust `bc_envelope::Envelope::walk(false, vec![], visitor)`
4364
+ * which is what `SearchPattern::paths_with_captures` uses. The earlier
4365
+ * port hand-rolled a recursion that double-recursed assertions and
4366
+ * stepped through wrapped subjects manually, producing a different
4367
+ * path order (and extra duplicates that the digest-set deduplication
4368
+ * would partially mask).
4423
4369
  */
4424
4370
  _walkEnvelope(envelope, pathToCurrent, visitor) {
4425
- visitor(envelope, pathToCurrent);
4426
- const subject = envelope.subject();
4427
- const newPath = [...pathToCurrent, envelope];
4428
- if (!subject.digest().equals(envelope.digest())) this._walkEnvelope(subject, newPath, visitor);
4429
- for (const assertion of envelope.assertions()) {
4430
- this._walkEnvelope(assertion, newPath, visitor);
4431
- const predicate = assertion.asPredicate?.();
4432
- if (predicate !== void 0) {
4433
- const assertionPath = [...newPath, assertion];
4434
- this._walkEnvelope(predicate, assertionPath, visitor);
4435
- }
4436
- const object = assertion.asObject?.();
4437
- if (object !== void 0) {
4438
- const assertionPath = [...newPath, assertion];
4439
- this._walkEnvelope(object, assertionPath, visitor);
4440
- }
4441
- }
4442
- if (subject.isWrapped()) {
4443
- const unwrapped = subject.tryUnwrap?.();
4444
- if (unwrapped !== void 0) this._walkEnvelope(unwrapped, newPath, visitor);
4445
- }
4371
+ envelope.walk(false, pathToCurrent, (current, _level, _edge, state) => {
4372
+ visitor(current, state);
4373
+ return [[...state, current], false];
4374
+ });
4446
4375
  }
4447
4376
  paths(haystack) {
4448
4377
  return this.pathsWithCaptures(haystack)[0];
@@ -4571,7 +4500,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
4571
4500
  return _patternIsComplex(this._first) || this._rest !== void 0;
4572
4501
  }
4573
4502
  toString() {
4574
- return this.patterns().map((p) => p.toString()).join(" -> ");
4503
+ return this.patterns().map((p) => dispatchPatternToString(p)).join(" -> ");
4575
4504
  }
4576
4505
  /**
4577
4506
  * Equality comparison.
@@ -4920,17 +4849,44 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
4920
4849
  */
4921
4850
  function convertStructurePatternToEnvelopePattern(structurePattern) {
4922
4851
  switch (structurePattern.type) {
4923
- case "Array": return ok({
4924
- type: "Leaf",
4925
- pattern: leafArray(ArrayPattern.fromDcborPattern({
4926
- kind: "Structure",
4927
- pattern: structurePattern
4928
- }))
4929
- });
4930
- case "Map": return ok({
4931
- type: "Leaf",
4932
- pattern: leafMap(MapPattern.any())
4933
- });
4852
+ case "Array": {
4853
+ const inner = structurePattern.pattern;
4854
+ let arrayPattern;
4855
+ switch (inner.variant) {
4856
+ case "Any":
4857
+ arrayPattern = ArrayPattern.any();
4858
+ break;
4859
+ case "Length":
4860
+ arrayPattern = ArrayPattern.fromInterval(inner.length);
4861
+ break;
4862
+ case "Elements":
4863
+ arrayPattern = ArrayPattern.fromDcborArrayPattern(inner);
4864
+ break;
4865
+ }
4866
+ return ok({
4867
+ type: "Leaf",
4868
+ pattern: leafArray(arrayPattern)
4869
+ });
4870
+ }
4871
+ case "Map": {
4872
+ const inner = structurePattern.pattern;
4873
+ let mapPattern;
4874
+ switch (inner.variant) {
4875
+ case "Any":
4876
+ mapPattern = MapPattern.any();
4877
+ break;
4878
+ case "Length":
4879
+ mapPattern = MapPattern.fromInterval(inner.length);
4880
+ break;
4881
+ case "Constraints":
4882
+ mapPattern = MapPattern.any();
4883
+ break;
4884
+ }
4885
+ return ok({
4886
+ type: "Leaf",
4887
+ pattern: leafMap(mapPattern)
4888
+ });
4889
+ }
4934
4890
  case "Tagged": return ok({
4935
4891
  type: "Leaf",
4936
4892
  pattern: leafTag(TaggedPattern.fromDcborPattern(structurePattern.pattern))
@@ -4988,6 +4944,169 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
4988
4944
  }
4989
4945
  }
4990
4946
  //#endregion
4947
+ //#region src/parse/utils.ts
4948
+ let createCborPattern;
4949
+ let createCborPatternFromDcbor;
4950
+ let createAnyArray;
4951
+ let createArrayWithCount;
4952
+ let createArrayWithRange;
4953
+ let createArrayFromDcborPattern;
4954
+ /**
4955
+ * Register pattern factory functions.
4956
+ * This is called by the pattern module to avoid circular dependencies.
4957
+ */
4958
+ function registerPatternFactories(factories) {
4959
+ createCborPattern = factories.cborPattern;
4960
+ createCborPatternFromDcbor = factories.cborPatternFromDcbor;
4961
+ createAnyArray = factories.anyArray;
4962
+ createArrayWithCount = factories.arrayWithCount;
4963
+ createArrayWithRange = factories.arrayWithRange;
4964
+ createArrayFromDcborPattern = factories.arrayFromDcborPattern;
4965
+ }
4966
+ /**
4967
+ * Skips whitespace in the source string.
4968
+ *
4969
+ * @param src - The source string
4970
+ * @param pos - The current position (modified in place)
4971
+ */
4972
+ function skipWs$1(src, pos) {
4973
+ while (pos.value < src.length) {
4974
+ const ch = src[pos.value];
4975
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f") pos.value++;
4976
+ else break;
4977
+ }
4978
+ }
4979
+ /**
4980
+ * Parses a CBOR value or dcbor-pattern expression.
4981
+ *
4982
+ * @param src - The source string
4983
+ * @returns The parsed pattern and consumed character count, or an error
4984
+ */
4985
+ function parseCborInner(src) {
4986
+ if (createCborPattern === void 0 || createCborPatternFromDcbor === void 0) return err(unknown());
4987
+ const pos = { value: 0 };
4988
+ skipWs$1(src, pos);
4989
+ if (src[pos.value] === "/") {
4990
+ pos.value++;
4991
+ const start = pos.value;
4992
+ let escape = false;
4993
+ while (pos.value < src.length) {
4994
+ const b = src[pos.value];
4995
+ pos.value++;
4996
+ if (escape) {
4997
+ escape = false;
4998
+ continue;
4999
+ }
5000
+ if (b === "\\") {
5001
+ escape = true;
5002
+ continue;
5003
+ }
5004
+ if (b === "/") {
5005
+ const parseResult = (0, _bcts_dcbor_pattern.parse)(src.slice(start, pos.value - 1));
5006
+ if (!parseResult.ok) return err(invalidPattern({
5007
+ start,
5008
+ end: pos.value - 1
5009
+ }));
5010
+ skipWs$1(src, pos);
5011
+ return ok([createCborPatternFromDcbor(parseResult.value), pos.value]);
5012
+ }
5013
+ }
5014
+ return err(unterminatedRegex({
5015
+ start: start - 1,
5016
+ end: pos.value
5017
+ }));
5018
+ }
5019
+ if (src.slice(pos.value, pos.value + 3) === "ur:") {
5020
+ const parseResult = (0, _bcts_dcbor_parse.parseDcborItemPartial)(src.slice(pos.value));
5021
+ if (!parseResult.ok) return err(unknown());
5022
+ const [cborValue, consumed] = parseResult.value;
5023
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
5024
+ }
5025
+ const parseResult = (0, _bcts_dcbor_parse.parseDcborItemPartial)(src.slice(pos.value));
5026
+ if (!parseResult.ok) return err(unknown());
5027
+ const [cborValue, consumed] = parseResult.value;
5028
+ return ok([createCborPattern(cborValue), pos.value + consumed]);
5029
+ }
5030
+ /**
5031
+ * Parses an array pattern inner content.
5032
+ *
5033
+ * @param src - The source string (content between [ and ])
5034
+ * @returns The parsed pattern and consumed character count, or an error
5035
+ */
5036
+ function parseArrayInner(src) {
5037
+ if (createAnyArray === void 0 || createArrayWithCount === void 0 || createArrayWithRange === void 0 || createArrayFromDcborPattern === void 0) return err(unknown());
5038
+ const pos = { value: 0 };
5039
+ skipWs$1(src, pos);
5040
+ if (src[pos.value] === "*") {
5041
+ pos.value++;
5042
+ skipWs$1(src, pos);
5043
+ return ok([createAnyArray(), pos.value]);
5044
+ }
5045
+ if (src[pos.value] === "{") {
5046
+ pos.value++;
5047
+ skipWs$1(src, pos);
5048
+ const startPos = pos.value;
5049
+ while (pos.value < src.length && src[pos.value] !== void 0 && /\d/.test(src[pos.value])) pos.value++;
5050
+ if (startPos === pos.value) return err(invalidRange({
5051
+ start: pos.value,
5052
+ end: pos.value
5053
+ }));
5054
+ const firstNum = parseInt(src.slice(startPos, pos.value), 10);
5055
+ if (Number.isNaN(firstNum)) return err(invalidNumberFormat({
5056
+ start: startPos,
5057
+ end: pos.value
5058
+ }));
5059
+ skipWs$1(src, pos);
5060
+ if (pos.value >= src.length) return err(unexpectedEndOfInput());
5061
+ const ch = src[pos.value];
5062
+ if (ch === "}") {
5063
+ pos.value++;
5064
+ skipWs$1(src, pos);
5065
+ return ok([createArrayWithCount(firstNum), pos.value]);
5066
+ }
5067
+ if (ch === ",") {
5068
+ pos.value++;
5069
+ skipWs$1(src, pos);
5070
+ if (pos.value >= src.length) return err(unexpectedEndOfInput());
5071
+ const nextCh = src[pos.value];
5072
+ if (nextCh === "}") {
5073
+ pos.value++;
5074
+ skipWs$1(src, pos);
5075
+ return ok([createArrayWithRange(firstNum, void 0), pos.value]);
5076
+ }
5077
+ if (nextCh !== void 0 && /\d/.test(nextCh)) {
5078
+ const secondStart = pos.value;
5079
+ while (pos.value < src.length && src[pos.value] !== void 0 && /\d/.test(src[pos.value])) pos.value++;
5080
+ const secondNum = parseInt(src.slice(secondStart, pos.value), 10);
5081
+ if (Number.isNaN(secondNum)) return err(invalidNumberFormat({
5082
+ start: secondStart,
5083
+ end: pos.value
5084
+ }));
5085
+ skipWs$1(src, pos);
5086
+ if (pos.value >= src.length || src[pos.value] !== "}") return err(unexpectedEndOfInput());
5087
+ pos.value++;
5088
+ skipWs$1(src, pos);
5089
+ return ok([createArrayWithRange(firstNum, secondNum), pos.value]);
5090
+ }
5091
+ return err(invalidRange({
5092
+ start: pos.value,
5093
+ end: pos.value
5094
+ }));
5095
+ }
5096
+ return err(invalidRange({
5097
+ start: pos.value,
5098
+ end: pos.value
5099
+ }));
5100
+ }
5101
+ const parseResult = (0, _bcts_dcbor_pattern.parse)(`[${src.slice(pos.value)}]`);
5102
+ if (!parseResult.ok) return err(invalidPattern({
5103
+ start: pos.value,
5104
+ end: src.length
5105
+ }));
5106
+ const consumed = src.length - pos.value;
5107
+ return ok([createArrayFromDcborPattern(parseResult.value), consumed]);
5108
+ }
5109
+ //#endregion
4991
5110
  //#region src/pattern/index.ts
4992
5111
  /**
4993
5112
  * Creates a Leaf pattern.
@@ -5502,6 +5621,12 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
5502
5621
  compile: patternCompile,
5503
5622
  toString: patternToString
5504
5623
  });
5624
+ registerWrappedPatternAny(any);
5625
+ registerAssertionsPatternToStringDispatch(patternToString);
5626
+ registerSubjectPatternDispatch({
5627
+ compile: patternCompile,
5628
+ toString: patternToString
5629
+ });
5505
5630
  registerAnyPatternFactory((p) => patternMeta(metaAny(p)));
5506
5631
  registerAndPatternFactory((p) => patternMeta(metaAnd(p)));
5507
5632
  registerOrPatternFactory((p) => patternMeta(metaOr(p)));
@@ -5513,6 +5638,14 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
5513
5638
  }
5514
5639
  registerAllFactories();
5515
5640
  registerVMPatternFunctions(patternPathsWithCaptures, patternMatches, patternPaths);
5641
+ registerPatternFactories({
5642
+ cborPattern: (value) => cborValue(value),
5643
+ cborPatternFromDcbor: (pattern) => cborPattern(pattern),
5644
+ anyArray,
5645
+ arrayWithCount: (count) => patternLeaf(leafArray(ArrayPattern.count(count))),
5646
+ arrayWithRange: (min, max) => patternLeaf(leafArray(ArrayPattern.interval(min, max))),
5647
+ arrayFromDcborPattern: (pattern) => patternLeaf(leafArray(ArrayPattern.fromDcborPattern(pattern)))
5648
+ });
5516
5649
  registerPatternMatchFn(patternMatches);
5517
5650
  registerPatternDispatchFns({
5518
5651
  pathsWithCaptures: patternPathsWithCaptures,
@@ -6266,305 +6399,198 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
6266
6399
  }
6267
6400
  };
6268
6401
  //#endregion
6269
- //#region src/parse/index.ts
6402
+ //#region src/parse/leaf/array-parser.ts
6270
6403
  /**
6271
6404
  * Copyright © 2023-2026 Blockchain Commons, LLC
6272
6405
  * Copyright © 2025-2026 Parity Technologies
6273
6406
  *
6407
+ * Array parser — port of `bc-envelope-pattern-rust`
6408
+ * `parse/leaf/array_parser.rs`.
6274
6409
  *
6275
- * @bcts/envelope-pattern - Parser entry point
6276
- *
6277
- * This is a 1:1 TypeScript port of bc-envelope-pattern-rust parse/mod.rs
6278
- * Recursive descent parser for Gordian Envelope pattern syntax.
6410
+ * Mirrors Rust's flow exactly: after the `[` token has been consumed,
6411
+ * delegate to `utils::parseArrayInner` (which handles `*`, `{n}`, `{n,m}`,
6412
+ * `{n,}` directly and otherwise wraps the body in `[...]` and re-parses
6413
+ * via dcbor-pattern), then expect a closing `]`.
6279
6414
  *
6280
- * @module envelope-pattern/parse
6281
- */
6282
- /**
6283
- * Parse a pattern expression string into a Pattern.
6415
+ * @module envelope-pattern/parse/leaf/array-parser
6284
6416
  */
6285
- function parse(input) {
6286
- const lexer = new Lexer(input);
6287
- const result = parseOr(lexer);
6288
- if (!result.ok) {
6289
- const dcborResult = (0, _bcts_dcbor_pattern.parse)(input);
6290
- if (dcborResult.ok) return convertDcborPatternToEnvelopePattern$1(dcborResult.value);
6291
- return result;
6292
- }
6293
- const next = lexer.next();
6294
- if (next !== void 0) return err(extraData(next.span));
6295
- return result;
6417
+ function parseArray(lexer) {
6418
+ const inner = parseArrayInner(lexer.remainder());
6419
+ if (!inner.ok) return inner;
6420
+ const [pattern, consumed] = inner.value;
6421
+ lexer.bump(consumed);
6422
+ const close = lexer.next();
6423
+ if (close === void 0) return err(expectedCloseBracket(lexer.span()));
6424
+ if (close.token.type !== "BracketClose") return err(unexpectedToken(close.token, close.span));
6425
+ return ok(pattern);
6296
6426
  }
6427
+ //#endregion
6428
+ //#region src/parse/leaf/cbor-parser.ts
6297
6429
  /**
6298
- * Parse a pattern, allowing extra data after the pattern.
6430
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6431
+ * Copyright © 2025-2026 Parity Technologies
6432
+ *
6433
+ * CBOR pattern parser — port of `bc-envelope-pattern-rust`
6434
+ * `parse/leaf/cbor_parser.rs`.
6435
+ *
6436
+ * Mirrors Rust's flow: lookahead for `(`. If absent, return `any_cbor()`.
6437
+ * Otherwise consume the `(`, delegate to `parseCborInner` (handles
6438
+ * `/regex/`, `ur:…`, and CBOR diagnostic notation), and expect a closing
6439
+ * `)`.
6440
+ *
6441
+ * @module envelope-pattern/parse/leaf/cbor-parser
6299
6442
  */
6300
- function parsePartial(input) {
6301
- const lexer = new Lexer(input);
6302
- const result = parseOr(lexer);
6303
- if (!result.ok) return result;
6304
- return ok([result.value, lexer.position]);
6443
+ function parseCbor(lexer) {
6444
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyCbor());
6445
+ lexer.next();
6446
+ const innerResult = parseCborInner(lexer.remainder());
6447
+ if (!innerResult.ok) return innerResult;
6448
+ const [pattern, consumed] = innerResult.value;
6449
+ lexer.bump(consumed);
6450
+ const close = lexer.next();
6451
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6452
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6453
+ return ok(pattern);
6305
6454
  }
6455
+ //#endregion
6456
+ //#region src/parse/leaf/date-parser.ts
6306
6457
  /**
6307
- * Convert a dcbor-pattern Pattern to an envelope-pattern Pattern.
6458
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6459
+ * Copyright © 2025-2026 Parity Technologies
6460
+ *
6461
+ * Date content parser — port of `bc-envelope-pattern-rust`
6462
+ * `parse/leaf/date_parser.rs`.
6463
+ *
6464
+ * Mirrors Rust's `Date::from_string`, which accepts a strict ISO-8601
6465
+ * subset, by deferring to dcbor's `CborDate.fromString`. Falls back to JS
6466
+ * `Date.parse` only as a defensive shim — that branch is unreachable for
6467
+ * conformant inputs.
6468
+ *
6469
+ * @module envelope-pattern/parse/leaf/date-parser
6308
6470
  */
6309
- function convertDcborPatternToEnvelopePattern$1(_dcborPattern) {
6310
- return ok(any());
6311
- }
6312
6471
  /**
6313
- * Parse an Or expression: expr (| expr)*
6472
+ * Parse a date pattern of one of the forms accepted by Rust:
6473
+ *
6474
+ * - `/regex/` (regex match against ISO-8601 string)
6475
+ * - `start...end` (inclusive range)
6476
+ * - `start...` (earliest)
6477
+ * - `...end` (latest)
6478
+ * - `iso-8601` (exact)
6479
+ *
6480
+ * Mirrors `parse_date_content` in Rust; uses `CborDate.fromString` so the
6481
+ * accepted formats match Rust's `bc_envelope::prelude::Date::from_string`
6482
+ * exactly rather than the looser JS `Date.parse`.
6314
6483
  */
6315
- function parseOr(lexer) {
6316
- const patterns = [];
6317
- const first = parseTraverse(lexer);
6318
- if (!first.ok) return first;
6319
- patterns.push(first.value);
6320
- while (true) {
6321
- if (lexer.peekToken()?.token.type !== "Or") break;
6322
- lexer.next();
6323
- const nextExpr = parseTraverse(lexer);
6324
- if (!nextExpr.ok) return nextExpr;
6325
- patterns.push(nextExpr.value);
6484
+ function parseDateContent(content, span) {
6485
+ if (content.startsWith("/") && content.endsWith("/") && content.length >= 2) {
6486
+ const regexStr = content.slice(1, -1);
6487
+ try {
6488
+ return ok(dateRegex(new RegExp(regexStr)));
6489
+ } catch {
6490
+ return err(invalidRegex(span));
6491
+ }
6326
6492
  }
6327
- if (patterns.length === 1) return ok(patterns[0]);
6328
- return ok(or(patterns));
6493
+ const ellipsisIdx = content.indexOf("...");
6494
+ if (ellipsisIdx !== -1) {
6495
+ const left = content.slice(0, ellipsisIdx);
6496
+ const right = content.slice(ellipsisIdx + 3);
6497
+ if (left.length === 0 && right.length > 0) {
6498
+ const parsed = parseIsoDateStrict(right);
6499
+ if (parsed === void 0) return err(invalidDateFormat(span));
6500
+ return ok(dateLatest(parsed));
6501
+ }
6502
+ if (left.length > 0 && right.length === 0) {
6503
+ const parsed = parseIsoDateStrict(left);
6504
+ if (parsed === void 0) return err(invalidDateFormat(span));
6505
+ return ok(dateEarliest(parsed));
6506
+ }
6507
+ if (left.length > 0 && right.length > 0) {
6508
+ const start = parseIsoDateStrict(left);
6509
+ const end = parseIsoDateStrict(right);
6510
+ if (start === void 0 || end === void 0) return err(invalidDateFormat(span));
6511
+ return ok(dateRange(start, end));
6512
+ }
6513
+ return err(invalidDateFormat(span));
6514
+ }
6515
+ const parsed = parseIsoDateStrict(content);
6516
+ if (parsed === void 0) return err(invalidDateFormat(span));
6517
+ return ok(date(parsed));
6329
6518
  }
6519
+ function parseIsoDateStrict(value) {
6520
+ try {
6521
+ return _bcts_dcbor.CborDate.fromString(value);
6522
+ } catch {
6523
+ return;
6524
+ }
6525
+ }
6526
+ //#endregion
6527
+ //#region src/parse/leaf/known-value-parser.ts
6330
6528
  /**
6331
- * Parse a Traverse expression: expr (-> expr)*
6332
- */
6333
- function parseTraverse(lexer) {
6334
- const patterns = [];
6335
- const first = parseAnd(lexer);
6336
- if (!first.ok) return first;
6337
- patterns.push(first.value);
6338
- while (true) {
6339
- if (lexer.peekToken()?.token.type !== "Traverse") break;
6340
- lexer.next();
6341
- const nextExpr = parseAnd(lexer);
6342
- if (!nextExpr.ok) return nextExpr;
6343
- patterns.push(nextExpr.value);
6344
- }
6345
- if (patterns.length === 1) return ok(patterns[0]);
6346
- return ok(traverse(patterns));
6347
- }
6348
- /**
6349
- * Parse an And expression: expr (& expr)*
6529
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6530
+ * Copyright © 2025-2026 Parity Technologies
6531
+ *
6532
+ * Helpers for parsing the body of a `'…'` (single-quoted) known-value
6533
+ * literal. Mirrors the inline body of Rust's `Token::SingleQuotedPattern`
6534
+ * branch in `parse_primary`:
6535
+ *
6536
+ * - If the contents are a valid `u64`, build `Pattern::known_value(...)`.
6537
+ * - Otherwise, build `Pattern::known_value_named(...)`.
6538
+ *
6539
+ * The earlier port duck-typed a fake `KnownValue`; this version uses the
6540
+ * real `KnownValue` constructor so all subsequent KnownValue methods work
6541
+ * (e.g., `taggedCbor()`, `name()`, etc.).
6542
+ *
6543
+ * @module envelope-pattern/parse/leaf/known-value-parser
6350
6544
  */
6351
- function parseAnd(lexer) {
6352
- const patterns = [];
6353
- const first = parseNot(lexer);
6354
- if (!first.ok) return first;
6355
- patterns.push(first.value);
6356
- while (true) {
6357
- if (lexer.peekToken()?.token.type !== "And") break;
6358
- lexer.next();
6359
- const nextExpr = parseNot(lexer);
6360
- if (!nextExpr.ok) return nextExpr;
6361
- patterns.push(nextExpr.value);
6362
- }
6363
- if (patterns.length === 1) return ok(patterns[0]);
6364
- return ok(and(patterns));
6365
- }
6366
6545
  /**
6367
- * Parse a Not expression: !? group
6546
+ * Maximum value of a Rust `u64`. Used to reject literals that would
6547
+ * silently wrap or lose precision when constructing a `KnownValue`.
6368
6548
  */
6369
- function parseNot(lexer) {
6370
- if (lexer.peekToken()?.token.type === "Not") {
6371
- lexer.next();
6372
- const inner = parseGroup(lexer);
6373
- if (!inner.ok) return inner;
6374
- return ok(notMatching(inner.value));
6375
- }
6376
- return parseGroup(lexer);
6377
- }
6549
+ const U64_MAX = 18446744073709551615n;
6378
6550
  /**
6379
- * Parse a Group expression: primary quantifier?
6551
+ * Parse the inner contents of a `'…'` known-value pattern token.
6552
+ *
6553
+ * Mirrors the Rust dispatch
6554
+ * ```ignore
6555
+ * if let Ok(value) = content.parse::<u64>() {
6556
+ * Pattern::known_value(KnownValue::new(value))
6557
+ * } else {
6558
+ * Pattern::known_value_named(content)
6559
+ * }
6560
+ * ```
6561
+ * but uses BigInt parsing to preserve full `u64` range — the previous
6562
+ * `parseInt(...)` path silently truncated above `2^53-1`.
6380
6563
  */
6381
- function parseGroup(lexer) {
6382
- const primary = parsePrimary(lexer);
6383
- if (!primary.ok) return primary;
6384
- const next = lexer.peekToken();
6385
- if (next === void 0) return primary;
6386
- const tokenType = next.token.type;
6387
- let quantifier;
6388
- if (tokenType === "RepeatZeroOrMore") {
6389
- lexer.next();
6390
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Greedy);
6391
- } else if (tokenType === "RepeatZeroOrMoreLazy") {
6392
- lexer.next();
6393
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Lazy);
6394
- } else if (tokenType === "RepeatZeroOrMorePossessive") {
6395
- lexer.next();
6396
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Possessive);
6397
- } else if (tokenType === "RepeatOneOrMore") {
6398
- lexer.next();
6399
- quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Greedy);
6400
- } else if (tokenType === "RepeatOneOrMoreLazy") {
6401
- lexer.next();
6402
- quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Lazy);
6403
- } else if (tokenType === "RepeatOneOrMorePossessive") {
6404
- lexer.next();
6405
- quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Possessive);
6406
- } else if (tokenType === "RepeatZeroOrOne") {
6407
- lexer.next();
6408
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Greedy);
6409
- } else if (tokenType === "RepeatZeroOrOneLazy") {
6410
- lexer.next();
6411
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Lazy);
6412
- } else if (tokenType === "RepeatZeroOrOnePossessive") {
6413
- lexer.next();
6414
- quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Possessive);
6415
- } else if (tokenType === "Range") {
6416
- lexer.next();
6417
- if (!next.token.value.ok) return err(next.token.value.error);
6418
- quantifier = next.token.value.value;
6419
- } else return primary;
6420
- return ok(repeat(primary.value, quantifier.min(), quantifier.max(), quantifier.reluctance()));
6564
+ function parseKnownValueContent(content) {
6565
+ if (isU64Literal(content)) return ok(knownValue(new _bcts_known_values.KnownValue(BigInt(content))));
6566
+ return ok(patternLeaf(leafKnownValue(KnownValuePattern.named(content))));
6421
6567
  }
6422
- /**
6423
- * Parse a primary expression (atoms and structure keywords).
6424
- */
6425
- function parsePrimary(lexer) {
6426
- const tokenResult = lexer.next();
6427
- if (tokenResult === void 0) return err(unexpectedEndOfInput());
6428
- const { token, span } = tokenResult;
6429
- switch (token.type) {
6430
- case "Search": return parseSearch(lexer);
6431
- case "Node": return parseNode(lexer);
6432
- case "Assertion": return parseAssertion(lexer);
6433
- case "AssertionPred": return parseAssertionPred(lexer);
6434
- case "AssertionObj": return parseAssertionObj(lexer);
6435
- case "Digest": return parseDigest(lexer);
6436
- case "Obj": return parseObject(lexer);
6437
- case "Obscured": return ok(obscured());
6438
- case "Elided": return ok(elided());
6439
- case "Encrypted": return ok(encrypted());
6440
- case "Compressed": return ok(compressed());
6441
- case "Pred": return parsePredicate(lexer);
6442
- case "Subject": return parseSubject(lexer);
6443
- case "Wrapped": return ok(wrapped());
6444
- case "Unwrap": return parseUnwrap(lexer);
6445
- case "Leaf": return ok(leaf());
6446
- case "GroupName": return parseCapture(lexer, token.name);
6447
- case "ParenOpen": return parseParenGroup(lexer);
6448
- case "Cbor": return parseCbor(lexer);
6449
- case "RepeatZeroOrMore": return ok(any());
6450
- case "BoolKeyword": return ok(anyBool());
6451
- case "BoolTrue": return ok(bool(true));
6452
- case "BoolFalse": return ok(bool(false));
6453
- case "NumberKeyword": return ok(anyNumber());
6454
- case "TextKeyword": return ok(anyText());
6455
- case "StringLiteral":
6456
- if (!token.value.ok) return err(token.value.error);
6457
- return ok(text(token.value.value));
6458
- case "UnsignedInteger":
6459
- if (!token.value.ok) return err(token.value.error);
6460
- return parseNumberRangeOrComparison(lexer, token.value.value);
6461
- case "Integer":
6462
- if (!token.value.ok) return err(token.value.error);
6463
- return parseNumberRangeOrComparison(lexer, token.value.value);
6464
- case "Float":
6465
- if (!token.value.ok) return err(token.value.error);
6466
- return parseNumberRangeOrComparison(lexer, token.value.value);
6467
- case "GreaterThanOrEqual": return parseComparisonNumber(lexer, ">=");
6468
- case "LessThanOrEqual": return parseComparisonNumber(lexer, "<=");
6469
- case "GreaterThan": return parseComparisonNumber(lexer, ">");
6470
- case "LessThan": return parseComparisonNumber(lexer, "<");
6471
- case "NaN": return ok(patternLeaf(leafNumber(NumberPattern.nan())));
6472
- case "Infinity": return ok(number(Infinity));
6473
- case "NegativeInfinity": return ok(number(-Infinity));
6474
- case "Regex":
6475
- if (!token.value.ok) return err(token.value.error);
6476
- try {
6477
- return ok(textRegex(new RegExp(token.value.value)));
6478
- } catch {
6479
- return err(invalidRegex(span));
6480
- }
6481
- case "BracketOpen": return parseArray(lexer);
6482
- case "ByteString": return ok(anyByteString());
6483
- case "HexPattern":
6484
- if (!token.value.ok) return err(token.value.error);
6485
- return ok(byteString(token.value.value));
6486
- case "HexBinaryRegex":
6487
- if (!token.value.ok) return err(token.value.error);
6488
- try {
6489
- return ok(patternLeaf(leafByteString(ByteStringPattern.regex(new RegExp(token.value.value)))));
6490
- } catch {
6491
- return err(invalidRegex(span));
6492
- }
6493
- case "DateKeyword": return ok(anyDate());
6494
- case "DatePattern":
6495
- if (!token.value.ok) return err(token.value.error);
6496
- return parseDateContent(token.value.value, span);
6497
- case "Tagged": return parseTag(lexer);
6498
- case "Known": return ok(anyKnownValue());
6499
- case "SingleQuotedPattern":
6500
- if (!token.value.ok) return err(token.value.error);
6501
- return parseKnownValueContent(token.value.value);
6502
- case "SingleQuotedRegex":
6503
- if (!token.value.ok) return err(token.value.error);
6504
- try {
6505
- return ok(patternLeaf(leafKnownValue(KnownValuePattern.regex(new RegExp(token.value.value)))));
6506
- } catch {
6507
- return err(invalidRegex(span));
6508
- }
6509
- case "Null": return ok(nullPattern());
6510
- case "And":
6511
- case "Or":
6512
- case "Not":
6513
- case "Traverse":
6514
- case "RepeatZeroOrMoreLazy":
6515
- case "RepeatZeroOrMorePossessive":
6516
- case "RepeatOneOrMore":
6517
- case "RepeatOneOrMoreLazy":
6518
- case "RepeatOneOrMorePossessive":
6519
- case "RepeatZeroOrOne":
6520
- case "RepeatZeroOrOneLazy":
6521
- case "RepeatZeroOrOnePossessive":
6522
- case "ParenClose":
6523
- case "BracketClose":
6524
- case "Comma":
6525
- case "Ellipsis":
6526
- case "Range":
6527
- case "Identifier": return err(unexpectedToken(token, span));
6568
+ function isU64Literal(content) {
6569
+ if (content.length === 0) return false;
6570
+ for (let i = 0; i < content.length; i++) {
6571
+ const c = content.charCodeAt(i);
6572
+ if (c < 48 || c > 57) return false;
6573
+ }
6574
+ try {
6575
+ const value = BigInt(content);
6576
+ return value >= 0n && value <= U64_MAX;
6577
+ } catch {
6578
+ return false;
6528
6579
  }
6529
6580
  }
6581
+ //#endregion
6582
+ //#region src/parse/leaf/number-parser.ts
6530
6583
  /**
6531
- * Parse a parenthesized group expression.
6532
- */
6533
- function parseParenGroup(lexer) {
6534
- const inner = parseOr(lexer);
6535
- if (!inner.ok) return inner;
6536
- if (lexer.next()?.token.type !== "ParenClose") return err({
6537
- type: "ExpectedCloseParen",
6538
- span: lexer.span()
6539
- });
6540
- return ok(group(inner.value));
6541
- }
6542
- /**
6543
- * Parse a capture group: @name pattern
6544
- */
6545
- function parseCapture(lexer, name) {
6546
- const inner = parseGroup(lexer);
6547
- if (!inner.ok) return inner;
6548
- return ok(capture(name, inner.value));
6549
- }
6550
- /**
6551
- * Parse a search pattern: search(pattern)
6584
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6585
+ * Copyright © 2025-2026 Parity Technologies
6586
+ *
6587
+ * Number parsers — port of `bc-envelope-pattern-rust` `parse/leaf/number_parser.rs`.
6588
+ *
6589
+ * @module envelope-pattern/parse/leaf/number-parser
6552
6590
  */
6553
- function parseSearch(lexer) {
6554
- if (lexer.next()?.token.type !== "ParenOpen") return err({
6555
- type: "ExpectedOpenParen",
6556
- span: lexer.span()
6557
- });
6558
- const inner = parseOr(lexer);
6559
- if (!inner.ok) return inner;
6560
- if (lexer.next()?.token.type !== "ParenClose") return err({
6561
- type: "ExpectedCloseParen",
6562
- span: lexer.span()
6563
- });
6564
- return ok(search(inner.value));
6565
- }
6566
6591
  /**
6567
- * Parse number with possible range or comparison.
6592
+ * Parses an optional `...end` suffix following an already-consumed number,
6593
+ * mirroring Rust `parse_number_range_or_comparison`.
6568
6594
  */
6569
6595
  function parseNumberRangeOrComparison(lexer, firstValue) {
6570
6596
  const next = lexer.peekToken();
@@ -6574,10 +6600,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
6574
6600
  const endToken = lexer.next();
6575
6601
  if (endToken === void 0) return err(unexpectedEndOfInput());
6576
6602
  let endValue;
6577
- if (endToken.token.type === "UnsignedInteger" || endToken.token.type === "Integer") {
6578
- if (!endToken.token.value.ok) return err(endToken.token.value.error);
6579
- endValue = endToken.token.value.value;
6580
- } else if (endToken.token.type === "Float") {
6603
+ if (endToken.token.type === "UnsignedInteger" || endToken.token.type === "Integer" || endToken.token.type === "Float") {
6581
6604
  if (!endToken.token.value.ok) return err(endToken.token.value.error);
6582
6605
  endValue = endToken.token.value.value;
6583
6606
  } else return err(unexpectedToken(endToken.token, endToken.span));
@@ -6586,16 +6609,14 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
6586
6609
  return ok(number(firstValue));
6587
6610
  }
6588
6611
  /**
6589
- * Parse comparison number: >=n, <=n, >n, <n
6612
+ * Parses a number following a comparison operator, mirroring Rust
6613
+ * `parse_comparison_number`.
6590
6614
  */
6591
6615
  function parseComparisonNumber(lexer, op) {
6592
6616
  const numToken = lexer.next();
6593
6617
  if (numToken === void 0) return err(unexpectedEndOfInput());
6594
6618
  let value;
6595
- if (numToken.token.type === "UnsignedInteger" || numToken.token.type === "Integer") {
6596
- if (!numToken.token.value.ok) return err(numToken.token.value.error);
6597
- value = numToken.token.value.value;
6598
- } else if (numToken.token.type === "Float") {
6619
+ if (numToken.token.type === "UnsignedInteger" || numToken.token.type === "Integer" || numToken.token.type === "Float") {
6599
6620
  if (!numToken.token.value.ok) return err(numToken.token.value.error);
6600
6621
  value = numToken.token.value.value;
6601
6622
  } else return err(unexpectedToken(numToken.token, numToken.span));
@@ -6604,300 +6625,812 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
6604
6625
  case "<=": return ok(patternLeaf(leafNumber(NumberPattern.lessThanOrEqual(value))));
6605
6626
  case ">": return ok(numberGreaterThan(value));
6606
6627
  case "<": return ok(numberLessThan(value));
6607
- default: return ok(number(value));
6608
6628
  }
6609
6629
  }
6630
+ //#endregion
6631
+ //#region src/parse/leaf/tag-parser.ts
6610
6632
  /**
6611
- * Parse an array pattern.
6633
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6634
+ * Copyright © 2025-2026 Parity Technologies
6635
+ *
6636
+ * Tag parser — port of `bc-envelope-pattern-rust`
6637
+ * `parse/leaf/tag_parser.rs`.
6638
+ *
6639
+ * Mirrors the Rust dispatch exactly: lookahead for `(`; if absent, return
6640
+ * the bare `any_tag()`. Otherwise build a synthetic dcbor-pattern
6641
+ * expression `tagged(<inner>)`, parse it via `@bcts/dcbor-pattern`, and
6642
+ * extract the resulting `TaggedPattern` to wrap as an envelope-pattern
6643
+ * leaf. This keeps the **full** tag selector (number, name, regex)
6644
+ * intact — the previous port discarded the tag value entirely.
6645
+ *
6646
+ * @module envelope-pattern/parse/leaf/tag-parser
6612
6647
  */
6613
- function parseArray(lexer) {
6614
- const first = lexer.peekToken();
6615
- if (first === void 0) return err(unexpectedEndOfInput());
6616
- if (first.token.type === "BracketClose") {
6617
- lexer.next();
6618
- return ok(patternLeaf(leafArray(ArrayPattern.count(0))));
6619
- }
6620
- if (first.token.type === "RepeatZeroOrMore") {
6621
- lexer.next();
6622
- if (lexer.next()?.token.type !== "BracketClose") return err({
6623
- type: "ExpectedCloseBracket",
6624
- span: lexer.span()
6625
- });
6626
- return ok(anyArray());
6627
- }
6628
- const patterns = [];
6629
- while (true) {
6630
- const next = lexer.peekToken();
6631
- if (next === void 0) return err(unexpectedEndOfInput());
6632
- if (next.token.type === "BracketClose") {
6633
- lexer.next();
6634
- break;
6635
- }
6636
- const pattern = parseOr(lexer);
6637
- if (!pattern.ok) return pattern;
6638
- patterns.push(pattern.value);
6639
- const afterPattern = lexer.peekToken();
6640
- if (afterPattern === void 0) return err(unexpectedEndOfInput());
6641
- if (afterPattern.token.type === "Comma") lexer.next();
6642
- else if (afterPattern.token.type !== "BracketClose") return err(unexpectedToken(afterPattern.token, afterPattern.span));
6643
- }
6644
- if (patterns.length === 0) return ok(patternLeaf(leafArray(ArrayPattern.count(0))));
6645
- return ok(patternLeaf(leafArray(ArrayPattern.withPatterns(patterns))));
6646
- }
6647
6648
  /**
6648
- * Parse a tag pattern.
6649
+ * Parse `tagged` and `tagged(...)` patterns.
6649
6650
  */
6650
6651
  function parseTag(lexer) {
6651
- if (lexer.next()?.token.type !== "ParenOpen") return ok(anyTag());
6652
- const tagToken = lexer.next();
6653
- if (tagToken === void 0) return err(unexpectedEndOfInput());
6654
- if (tagToken.token.type !== "UnsignedInteger") return err(unexpectedToken(tagToken.token, tagToken.span));
6655
- if (!tagToken.token.value.ok) return err(tagToken.token.value.error);
6656
- if (lexer.next()?.token.type !== "ParenClose") return err({
6657
- type: "ExpectedCloseParen",
6658
- span: lexer.span()
6659
- });
6660
- return ok(anyTag());
6661
- }
6662
- /**
6663
- * Parse date content from date'...' pattern.
6664
- */
6665
- function parseDateContent(content, span) {
6666
- if (content.startsWith("/") && content.endsWith("/")) {
6667
- const regexStr = content.slice(1, -1);
6668
- try {
6669
- return ok(dateRegex(new RegExp(regexStr)));
6670
- } catch {
6671
- return err(invalidRegex(span));
6652
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyTag());
6653
+ lexer.next();
6654
+ const remainder = lexer.remainder();
6655
+ const closeIdx = findMatchingCloseParen(remainder);
6656
+ if (closeIdx === void 0) return err(expectedCloseParen(lexer.span()));
6657
+ const innerContent = remainder.slice(0, closeIdx);
6658
+ const dcborResult = (0, _bcts_dcbor_pattern.parse)(`tagged(${innerContent})`);
6659
+ if (dcborResult.ok) {
6660
+ const dcborPattern = dcborResult.value;
6661
+ if (dcborPattern.kind === "Structure" && dcborPattern.pattern.type === "Tagged") {
6662
+ lexer.bump(closeIdx);
6663
+ const close = lexer.next();
6664
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6665
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6666
+ return ok(patternLeaf(leafTag(TaggedPattern.fromDcborPattern(dcborPattern.pattern.pattern))));
6672
6667
  }
6673
6668
  }
6674
- const rangeIdx = content.indexOf("...");
6675
- if (rangeIdx !== -1) {
6676
- const left = content.slice(0, rangeIdx).trim();
6677
- const right = content.slice(rangeIdx + 3).trim();
6678
- if (left.length === 0 && right.length > 0) {
6679
- const parsed = Date.parse(right);
6680
- if (isNaN(parsed)) return err({
6681
- type: "InvalidDateFormat",
6682
- span
6683
- });
6684
- return ok(dateLatest(_bcts_dcbor.CborDate.fromDatetime(new Date(parsed))));
6685
- }
6686
- if (left.length > 0 && right.length === 0) {
6687
- const parsed = Date.parse(left);
6688
- if (isNaN(parsed)) return err({
6689
- type: "InvalidDateFormat",
6690
- span
6691
- });
6692
- return ok(dateEarliest(_bcts_dcbor.CborDate.fromDatetime(new Date(parsed))));
6693
- }
6694
- if (left.length > 0 && right.length > 0) {
6695
- const parsedStart = Date.parse(left);
6696
- const parsedEnd = Date.parse(right);
6697
- if (isNaN(parsedStart) || isNaN(parsedEnd)) return err({
6698
- type: "InvalidDateFormat",
6699
- span
6700
- });
6701
- return ok(dateRange(_bcts_dcbor.CborDate.fromDatetime(new Date(parsedStart)), _bcts_dcbor.CborDate.fromDatetime(new Date(parsedEnd))));
6669
+ const fallback = parseTagInner(innerContent);
6670
+ if (!fallback.ok) return fallback;
6671
+ lexer.bump(closeIdx);
6672
+ const close = lexer.next();
6673
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6674
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6675
+ return ok(fallback.value);
6676
+ }
6677
+ /**
6678
+ * Locate the index of the closing `)` matching the `(` that has already
6679
+ * been consumed by `parseTag`. Mirrors Rust `find_matching_paren`.
6680
+ */
6681
+ function findMatchingCloseParen(src) {
6682
+ let depth = 0;
6683
+ for (let i = 0; i < src.length; i++) {
6684
+ const ch = src.charCodeAt(i);
6685
+ if (ch === 40) depth += 1;
6686
+ else if (ch === 41) {
6687
+ if (depth === 0) return i;
6688
+ depth -= 1;
6702
6689
  }
6703
- return err({
6704
- type: "InvalidDateFormat",
6705
- span
6706
- });
6707
6690
  }
6708
- const parsed = Date.parse(content);
6709
- if (isNaN(parsed)) return err({
6710
- type: "InvalidDateFormat",
6711
- span
6712
- });
6713
- return ok(date(_bcts_dcbor.CborDate.fromDatetime(new Date(parsed))));
6714
6691
  }
6715
6692
  /**
6716
- * Parse known value content from '...' pattern.
6693
+ * Fallback for `tagged(N)` and `tagged(name)` when the full delegation
6694
+ * to dcbor-pattern fails. Mirrors Rust `parse_tag_inner`.
6717
6695
  */
6718
- function parseKnownValueContent(content) {
6719
- const numValue = parseInt(content, 10);
6720
- if (!isNaN(numValue)) return ok(knownValue({ value: () => BigInt(numValue) }));
6721
- return ok(patternLeaf(leafKnownValue(KnownValuePattern.named(content))));
6696
+ function parseTagInner(src) {
6697
+ const trimmed = src.trim();
6698
+ if (trimmed.length === 0) return err(unexpectedEndOfInput());
6699
+ if (trimmed.startsWith("/")) return err(unexpectedEndOfInput());
6700
+ if (/^\d+$/.test(trimmed)) try {
6701
+ const dcborResult = (0, _bcts_dcbor_pattern.parse)(`tagged(${trimmed})`);
6702
+ if (dcborResult.ok && dcborResult.value.kind === "Structure" && dcborResult.value.pattern.type === "Tagged") return ok(patternLeaf(leafTag(TaggedPattern.fromDcborPattern(dcborResult.value.pattern.pattern))));
6703
+ } catch {}
6704
+ const dcborResult = (0, _bcts_dcbor_pattern.parse)(`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
+ return err(unexpectedEndOfInput());
6722
6707
  }
6708
+ //#endregion
6709
+ //#region src/parse/structure/assertion-parser.ts
6723
6710
  /**
6724
- * Parse CBOR pattern.
6711
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6712
+ * Copyright © 2025-2026 Parity Technologies
6713
+ *
6714
+ * Assertion parser — port of `bc-envelope-pattern-rust`
6715
+ * `parse/structure/assertion_parser.rs`.
6716
+ *
6717
+ * Note: Rust's `parse_assertion` ignores its lexer entirely and always
6718
+ * returns `Pattern::any_assertion()`. There is intentionally **no**
6719
+ * `assert(pred, obj)` syntax — predicate/object filters are written via
6720
+ * the dedicated `assertpred(...)` / `assertobj(...)` keywords.
6725
6721
  *
6726
- * Matches Rust parse_cbor: tries dcbor-pattern regex first (/keyword/),
6727
- * then CBOR diagnostic notation via parseDcborItemPartial, then falls
6728
- * back to parseOr for envelope pattern expressions.
6722
+ * @module envelope-pattern/parse/structure/assertion-parser
6729
6723
  */
6730
- function parseCbor(lexer) {
6731
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyCbor());
6732
- lexer.next();
6733
- if (lexer.peek() === "/") {
6734
- const regexTokenResult = lexer.next();
6735
- if (regexTokenResult?.token.type === "Regex") {
6736
- const regexToken = regexTokenResult.token;
6737
- if (!regexToken.value.ok) return err(regexToken.value.error);
6738
- const keyword = regexToken.value.value;
6739
- const dcborResult = (0, _bcts_dcbor_pattern.parse)(keyword);
6740
- if (!dcborResult.ok) return err(unexpectedToken(regexToken, regexTokenResult.span));
6741
- if (lexer.next()?.token.type !== "ParenClose") return err({
6742
- type: "ExpectedCloseParen",
6743
- span: lexer.span()
6744
- });
6745
- return ok(cborPattern(dcborResult.value));
6746
- }
6747
- }
6748
- const cborResult = (0, _bcts_dcbor_parse.parseDcborItemPartial)(lexer.remainder());
6749
- if (cborResult.ok) {
6750
- const [cborData, consumed] = cborResult.value;
6751
- lexer.bump(consumed);
6752
- while (lexer.peek() === " " || lexer.peek() === " " || lexer.peek() === "\n") lexer.bump(1);
6753
- if (lexer.next()?.token.type !== "ParenClose") return err({
6754
- type: "ExpectedCloseParen",
6755
- span: lexer.span()
6756
- });
6757
- return ok(cborValue(cborData));
6758
- }
6759
- const inner = parseOr(lexer);
6760
- if (!inner.ok) return inner;
6761
- if (lexer.next()?.token.type !== "ParenClose") return err({
6762
- type: "ExpectedCloseParen",
6763
- span: lexer.span()
6764
- });
6765
- return inner;
6724
+ function parseAssertion(_lexer) {
6725
+ return ok(anyAssertion());
6766
6726
  }
6767
- function parseNode(lexer) {
6768
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyNode());
6769
- lexer.next();
6770
- const afterParen = lexer.peekToken();
6771
- if (afterParen?.token.type === "Range") {
6772
- lexer.next();
6773
- const rangeToken = afterParen.token;
6774
- if (!rangeToken.value.ok) return err(rangeToken.value.error);
6775
- const interval = rangeToken.value.value.interval();
6776
- if (lexer.next()?.token.type !== "ParenClose") return err({
6777
- type: "ExpectedCloseParen",
6778
- span: lexer.span()
6779
- });
6780
- return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
6781
- }
6727
+ //#endregion
6728
+ //#region src/parse/structure/assertion-obj-parser.ts
6729
+ /**
6730
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6731
+ * Copyright © 2025-2026 Parity Technologies
6732
+ *
6733
+ * Assertion-object parser — port of
6734
+ * `bc-envelope-pattern-rust/src/parse/structure/assertion_obj_parser.rs`.
6735
+ *
6736
+ * Requires `assertobj(<pattern>)`. The bare `assertobj` keyword is a
6737
+ * syntax error in Rust; we now mirror that behaviour.
6738
+ *
6739
+ * @module envelope-pattern/parse/structure/assertion-obj-parser
6740
+ */
6741
+ function parseAssertionObj(lexer) {
6742
+ const open = lexer.next();
6743
+ if (open === void 0) return err(unexpectedEndOfInput());
6744
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6782
6745
  const inner = parseOr(lexer);
6783
6746
  if (!inner.ok) return inner;
6784
- if (lexer.next()?.token.type !== "ParenClose") return err({
6785
- type: "ExpectedCloseParen",
6786
- span: lexer.span()
6787
- });
6788
- return ok(patternStructure(structureNode(NodePattern.withSubject(inner.value))));
6789
- }
6790
- function parseAssertion(lexer) {
6791
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6792
- lexer.next();
6793
- const pred = parseOr(lexer);
6794
- if (!pred.ok) return pred;
6795
- const comma = lexer.next();
6796
- if (comma?.token.type !== "Comma") return err(unexpectedToken(comma?.token ?? { type: "Null" }, comma?.span ?? lexer.span()));
6797
- const obj = parseOr(lexer);
6798
- if (!obj.ok) return obj;
6799
- if (lexer.next()?.token.type !== "ParenClose") return err({
6800
- type: "ExpectedCloseParen",
6801
- span: lexer.span()
6802
- });
6803
- return ok(patternStructure(structureAssertions(AssertionsPattern.withBoth(pred.value, obj.value))));
6747
+ const close = lexer.next();
6748
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6749
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6750
+ return ok(assertionWithObject(inner.value));
6804
6751
  }
6752
+ //#endregion
6753
+ //#region src/parse/structure/assertion-pred-parser.ts
6754
+ /**
6755
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6756
+ * Copyright © 2025-2026 Parity Technologies
6757
+ *
6758
+ * Assertion-predicate parser — port of
6759
+ * `bc-envelope-pattern-rust/src/parse/structure/assertion_pred_parser.rs`.
6760
+ *
6761
+ * Requires `assertpred(<pattern>)`. The bare `assertpred` keyword is a
6762
+ * syntax error in Rust (it errors on `UnexpectedEndOfInput` /
6763
+ * `UnexpectedToken`); we now mirror that behaviour.
6764
+ *
6765
+ * @module envelope-pattern/parse/structure/assertion-pred-parser
6766
+ */
6805
6767
  function parseAssertionPred(lexer) {
6806
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6807
- lexer.next();
6768
+ const open = lexer.next();
6769
+ if (open === void 0) return err(unexpectedEndOfInput());
6770
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6808
6771
  const inner = parseOr(lexer);
6809
6772
  if (!inner.ok) return inner;
6810
- if (lexer.next()?.token.type !== "ParenClose") return err({
6811
- type: "ExpectedCloseParen",
6812
- span: lexer.span()
6813
- });
6773
+ const close = lexer.next();
6774
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6775
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6814
6776
  return ok(assertionWithPredicate(inner.value));
6815
6777
  }
6816
- function parseAssertionObj(lexer) {
6817
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyAssertion());
6818
- lexer.next();
6819
- const inner = parseOr(lexer);
6820
- if (!inner.ok) return inner;
6821
- if (lexer.next()?.token.type !== "ParenClose") return err({
6822
- type: "ExpectedCloseParen",
6823
- span: lexer.span()
6824
- });
6825
- return ok(assertionWithObject(inner.value));
6778
+ //#endregion
6779
+ //#region src/parse/structure/compressed-parser.ts
6780
+ /**
6781
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6782
+ * Copyright © 2025-2026 Parity Technologies
6783
+ *
6784
+ * Compressed parser — port of `bc-envelope-pattern-rust`
6785
+ * `parse/structure/compressed_parser.rs`.
6786
+ *
6787
+ * @module envelope-pattern/parse/structure/compressed-parser
6788
+ */
6789
+ function parseCompressed(_lexer) {
6790
+ return ok(compressed());
6826
6791
  }
6792
+ //#endregion
6793
+ //#region src/parse/structure/digest-parser.ts
6794
+ /**
6795
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6796
+ * Copyright © 2025-2026 Parity Technologies
6797
+ *
6798
+ * Digest parser — port of `bc-envelope-pattern-rust`
6799
+ * `parse/structure/digest_parser.rs`.
6800
+ *
6801
+ * Mirrors Rust exactly:
6802
+ *
6803
+ * - Requires `digest(...)` — bare `digest` is an `UnexpectedEndOfInput`
6804
+ * error (the previous TS port silently returned `digest(any)`).
6805
+ * - Inside the parens, accepts either a UR string (`ur:digest/...`,
6806
+ * parsed via `Digest.fromURString`) or a hex byte prefix.
6807
+ * - Hex prefixes must have even length and not exceed `Digest.DIGEST_SIZE`
6808
+ * bytes; otherwise the parser surfaces `InvalidHexString`.
6809
+ *
6810
+ * @module envelope-pattern/parse/structure/digest-parser
6811
+ */
6812
+ const DIGEST_SIZE = _bcts_envelope.Digest.DIGEST_SIZE;
6827
6813
  function parseDigest(lexer) {
6828
- if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(patternStructure(structureDigest(DigestPattern.any())));
6829
- lexer.next();
6830
- const digestToken = lexer.next();
6831
- if (digestToken === void 0) return err(unexpectedEndOfInput());
6832
- if (digestToken.token.type === "HexPattern") {
6833
- if (!digestToken.token.value.ok) return err(digestToken.token.value.error);
6834
- if (lexer.next()?.token.type !== "ParenClose") return err({
6835
- type: "ExpectedCloseParen",
6836
- span: lexer.span()
6837
- });
6838
- return ok(digestPrefix(digestToken.token.value.value));
6814
+ const open = lexer.next();
6815
+ if (open === void 0) return err(unexpectedEndOfInput());
6816
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
6817
+ const innerResult = parseDigestInner(lexer.remainder(), lexer.position);
6818
+ if (!innerResult.ok) return innerResult;
6819
+ const [pattern, consumed] = innerResult.value;
6820
+ lexer.bump(consumed);
6821
+ const close = lexer.next();
6822
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6823
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6824
+ return ok(pattern);
6825
+ }
6826
+ function parseDigestInner(src, basePos) {
6827
+ let pos = 0;
6828
+ pos = skipWs(src, pos);
6829
+ if (src.startsWith("ur:", pos)) {
6830
+ const start = pos;
6831
+ while (pos < src.length && src[pos] !== ")") pos += 1;
6832
+ const ur = src.slice(start, pos).trimEnd();
6833
+ let parsed;
6834
+ try {
6835
+ parsed = _bcts_envelope.Digest.fromURString(ur);
6836
+ } catch {
6837
+ return err(invalidUr(ur, {
6838
+ start: basePos + start,
6839
+ end: basePos + pos
6840
+ }));
6841
+ }
6842
+ pos = skipWs(src, pos);
6843
+ return ok([digest(parsed), pos]);
6844
+ }
6845
+ const start = pos;
6846
+ while (pos < src.length && isAsciiHexDigit(src.charCodeAt(pos))) pos += 1;
6847
+ if (pos === start) return err(invalidHexString({
6848
+ start: basePos + pos,
6849
+ end: basePos + pos
6850
+ }));
6851
+ const hexStr = src.slice(start, pos);
6852
+ if (hexStr.length % 2 !== 0) return err(invalidHexString({
6853
+ start: basePos + pos,
6854
+ end: basePos + pos
6855
+ }));
6856
+ const bytes = decodeHex(hexStr);
6857
+ if (bytes === void 0) return err(invalidHexString({
6858
+ start: basePos + pos,
6859
+ end: basePos + pos
6860
+ }));
6861
+ if (bytes.length > DIGEST_SIZE) return err(invalidHexString({
6862
+ start: basePos + pos,
6863
+ end: basePos + pos
6864
+ }));
6865
+ pos = skipWs(src, pos);
6866
+ return ok([digestPrefix(bytes), pos]);
6867
+ }
6868
+ function skipWs(src, pos) {
6869
+ while (pos < src.length) {
6870
+ const ch = src[pos];
6871
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f") pos += 1;
6872
+ else break;
6839
6873
  }
6840
- if (digestToken.token.type === "Identifier") {
6841
- const hexStr = digestToken.token.value;
6842
- if (hexStr.length === 0 || hexStr.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hexStr)) return err({
6843
- type: "InvalidHexString",
6844
- span: digestToken.span
6845
- });
6846
- const bytes = new Uint8Array(hexStr.length / 2);
6847
- for (let i = 0; i < hexStr.length; i += 2) bytes[i / 2] = Number.parseInt(hexStr.slice(i, i + 2), 16);
6848
- if (lexer.next()?.token.type !== "ParenClose") return err({
6849
- type: "ExpectedCloseParen",
6850
- span: lexer.span()
6851
- });
6852
- return ok(digestPrefix(bytes));
6874
+ return pos;
6875
+ }
6876
+ function isAsciiHexDigit(c) {
6877
+ return c >= 48 && c <= 57 || c >= 65 && c <= 70 || c >= 97 && c <= 102;
6878
+ }
6879
+ function decodeHex(hex) {
6880
+ if (hex.length % 2 !== 0) return void 0;
6881
+ const out = new Uint8Array(hex.length / 2);
6882
+ for (let i = 0; i < hex.length; i += 2) {
6883
+ const value = Number.parseInt(hex.slice(i, i + 2), 16);
6884
+ if (Number.isNaN(value)) return void 0;
6885
+ out[i / 2] = value;
6853
6886
  }
6854
- return err(unexpectedToken(digestToken.token, digestToken.span));
6887
+ return out;
6888
+ }
6889
+ //#endregion
6890
+ //#region src/parse/structure/elided-parser.ts
6891
+ /**
6892
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6893
+ * Copyright © 2025-2026 Parity Technologies
6894
+ *
6895
+ * Elided parser — port of `bc-envelope-pattern-rust`
6896
+ * `parse/structure/elided_parser.rs`.
6897
+ *
6898
+ * @module envelope-pattern/parse/structure/elided-parser
6899
+ */
6900
+ function parseElided(_lexer) {
6901
+ return ok(elided());
6855
6902
  }
6903
+ //#endregion
6904
+ //#region src/parse/structure/encrypted-parser.ts
6905
+ /**
6906
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6907
+ * Copyright © 2025-2026 Parity Technologies
6908
+ *
6909
+ * Encrypted parser — port of `bc-envelope-pattern-rust`
6910
+ * `parse/structure/encrypted_parser.rs`.
6911
+ *
6912
+ * @module envelope-pattern/parse/structure/encrypted-parser
6913
+ */
6914
+ function parseEncrypted(_lexer) {
6915
+ return ok(encrypted());
6916
+ }
6917
+ //#endregion
6918
+ //#region src/parse/structure/node-parser.ts
6919
+ /**
6920
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6921
+ * Copyright © 2025-2026 Parity Technologies
6922
+ *
6923
+ * Node parser — port of `bc-envelope-pattern-rust`
6924
+ * `parse/structure/node_parser.rs`.
6925
+ *
6926
+ * Mirrors Rust:
6927
+ * - Bare `node` → `any_node()`.
6928
+ * - `node({n})` / `node({n,m})` / `node({n,})` → `node_with_assertions_range`.
6929
+ * - Anything else inside the parens (e.g. `node(text)`) is a syntax error
6930
+ * in Rust; the previous TS port silently produced an always-matching
6931
+ * `WithSubject` node, which we have removed.
6932
+ *
6933
+ * @module envelope-pattern/parse/structure/node-parser
6934
+ */
6935
+ function parseNode(lexer) {
6936
+ if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyNode());
6937
+ lexer.next();
6938
+ const inner = lexer.next();
6939
+ if (inner === void 0) return err(unexpectedEndOfInput());
6940
+ if (inner.token.type !== "Range") return err(unexpectedToken(inner.token, inner.span));
6941
+ if (!inner.token.value.ok) return err(inner.token.value.error);
6942
+ const interval = inner.token.value.value.interval();
6943
+ const close = lexer.next();
6944
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6945
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6946
+ return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
6947
+ }
6948
+ //#endregion
6949
+ //#region src/parse/structure/object-parser.ts
6950
+ /**
6951
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6952
+ * Copyright © 2025-2026 Parity Technologies
6953
+ *
6954
+ * Object parser — port of `bc-envelope-pattern-rust`
6955
+ * `parse/structure/object_parser.rs`.
6956
+ *
6957
+ * @module envelope-pattern/parse/structure/object-parser
6958
+ */
6856
6959
  function parseObject(lexer) {
6857
6960
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyObject());
6858
6961
  lexer.next();
6859
6962
  const inner = parseOr(lexer);
6860
6963
  if (!inner.ok) return inner;
6861
- if (lexer.next()?.token.type !== "ParenClose") return err({
6862
- type: "ExpectedCloseParen",
6863
- span: lexer.span()
6864
- });
6964
+ const close = lexer.next();
6965
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
6966
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6865
6967
  return ok(object(inner.value));
6866
6968
  }
6969
+ //#endregion
6970
+ //#region src/parse/structure/obscured-parser.ts
6971
+ /**
6972
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6973
+ * Copyright © 2025-2026 Parity Technologies
6974
+ *
6975
+ * Obscured parser — port of `bc-envelope-pattern-rust`
6976
+ * `parse/structure/obscured_parser.rs`.
6977
+ *
6978
+ * @module envelope-pattern/parse/structure/obscured-parser
6979
+ */
6980
+ function parseObscured(_lexer) {
6981
+ return ok(obscured());
6982
+ }
6983
+ //#endregion
6984
+ //#region src/parse/structure/predicate-parser.ts
6985
+ /**
6986
+ * Copyright © 2023-2026 Blockchain Commons, LLC
6987
+ * Copyright © 2025-2026 Parity Technologies
6988
+ *
6989
+ * Predicate parser — port of `bc-envelope-pattern-rust`
6990
+ * `parse/structure/predicate_parser.rs`.
6991
+ *
6992
+ * @module envelope-pattern/parse/structure/predicate-parser
6993
+ */
6867
6994
  function parsePredicate(lexer) {
6868
6995
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anyPredicate());
6869
6996
  lexer.next();
6870
6997
  const inner = parseOr(lexer);
6871
6998
  if (!inner.ok) return inner;
6872
- if (lexer.next()?.token.type !== "ParenClose") return err({
6873
- type: "ExpectedCloseParen",
6874
- span: lexer.span()
6875
- });
6999
+ const close = lexer.next();
7000
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7001
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6876
7002
  return ok(predicate(inner.value));
6877
7003
  }
7004
+ //#endregion
7005
+ //#region src/parse/structure/subject-parser.ts
7006
+ /**
7007
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7008
+ * Copyright © 2025-2026 Parity Technologies
7009
+ *
7010
+ * Subject parser — port of `bc-envelope-pattern-rust`
7011
+ * `parse/structure/subject_parser.rs`.
7012
+ *
7013
+ * @module envelope-pattern/parse/structure/subject-parser
7014
+ */
6878
7015
  function parseSubject(lexer) {
6879
7016
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(anySubject());
6880
7017
  lexer.next();
6881
7018
  const inner = parseOr(lexer);
6882
7019
  if (!inner.ok) return inner;
6883
- if (lexer.next()?.token.type !== "ParenClose") return err({
6884
- type: "ExpectedCloseParen",
6885
- span: lexer.span()
6886
- });
7020
+ const close = lexer.next();
7021
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7022
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6887
7023
  return ok(subject(inner.value));
6888
7024
  }
7025
+ //#endregion
7026
+ //#region src/parse/structure/wrapped-parser.ts
7027
+ /**
7028
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7029
+ * Copyright © 2025-2026 Parity Technologies
7030
+ *
7031
+ * Wrapped/unwrap parser — port of `bc-envelope-pattern-rust`
7032
+ * `parse/structure/wrapped_parser.rs`.
7033
+ *
7034
+ * @module envelope-pattern/parse/structure/wrapped-parser
7035
+ */
7036
+ function parseWrapped(_lexer) {
7037
+ return ok(wrapped());
7038
+ }
6889
7039
  function parseUnwrap(lexer) {
6890
7040
  if (lexer.peekToken()?.token.type !== "ParenOpen") return ok(unwrapEnvelope());
6891
7041
  lexer.next();
6892
7042
  const inner = parseOr(lexer);
6893
7043
  if (!inner.ok) return inner;
6894
- if (lexer.next()?.token.type !== "ParenClose") return err({
6895
- type: "ExpectedCloseParen",
6896
- span: lexer.span()
6897
- });
7044
+ const close = lexer.next();
7045
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7046
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
6898
7047
  return ok(unwrapMatching(inner.value));
6899
7048
  }
6900
7049
  //#endregion
7050
+ //#region src/parse/meta/capture-parser.ts
7051
+ /**
7052
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7053
+ * Copyright © 2025-2026 Parity Technologies
7054
+ *
7055
+ * Capture parser — port of `bc-envelope-pattern-rust`
7056
+ * `parse/meta/capture_parser.rs`.
7057
+ *
7058
+ * The `@name(...)` form requires explicit parentheses. Mirrors Rust
7059
+ * exactly:
7060
+ * ```ignore
7061
+ * @name ( expr )
7062
+ * ```
7063
+ * The previous TS port called `parse_group` (primary + quantifier), which
7064
+ * wrapped the inner expression in a redundant `GroupPattern` and accepted
7065
+ * the bare `@name p` form that Rust rejects.
7066
+ *
7067
+ * @module envelope-pattern/parse/meta/capture-parser
7068
+ */
7069
+ function parseCapture(lexer, name) {
7070
+ const open = lexer.next();
7071
+ if (open === void 0) return err(unexpectedEndOfInput());
7072
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
7073
+ const inner = parseOr(lexer);
7074
+ if (!inner.ok) return inner;
7075
+ const close = lexer.next();
7076
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7077
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7078
+ return ok(capture(name, inner.value));
7079
+ }
7080
+ //#endregion
7081
+ //#region src/parse/meta/group-parser.ts
7082
+ /**
7083
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7084
+ * Copyright © 2025-2026 Parity Technologies
7085
+ *
7086
+ * Group parser — port of `bc-envelope-pattern-rust`
7087
+ * `parse/meta/group_parser.rs`.
7088
+ *
7089
+ * Called from `parse_primary` after the opening `(` has been consumed.
7090
+ *
7091
+ * Mirrors Rust exactly:
7092
+ *
7093
+ * - Parse the inner expression with `parse_or`.
7094
+ * - Expect `)`. If missing, surface `ExpectedCloseParen`.
7095
+ * - Lookahead **once** for a quantifier suffix. If present, consume it
7096
+ * and wrap as `Pattern::repeat(inner, …)`. Otherwise return the inner
7097
+ * expression unchanged.
7098
+ *
7099
+ * The previous TS port wrapped every parenthesised expression in a
7100
+ * dedicated `GroupPattern` and applied quantifiers to bare primaries —
7101
+ * both broke `format(parse(s)) === s` round-tripping.
7102
+ *
7103
+ * @module envelope-pattern/parse/meta/group-parser
7104
+ */
7105
+ function parseGroup(lexer) {
7106
+ const inner = parseOr(lexer);
7107
+ if (!inner.ok) return inner;
7108
+ const close = lexer.next();
7109
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7110
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7111
+ const next = lexer.peekToken();
7112
+ if (next === void 0) return inner;
7113
+ let quantifier;
7114
+ const tokenType = next.token.type;
7115
+ if (tokenType === "RepeatZeroOrMore") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Greedy);
7116
+ else if (tokenType === "RepeatZeroOrMoreLazy") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Lazy);
7117
+ else if (tokenType === "RepeatZeroOrMorePossessive") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrMore(_bcts_dcbor_pattern.Reluctance.Possessive);
7118
+ else if (tokenType === "RepeatOneOrMore") quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Greedy);
7119
+ else if (tokenType === "RepeatOneOrMoreLazy") quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Lazy);
7120
+ else if (tokenType === "RepeatOneOrMorePossessive") quantifier = _bcts_dcbor_pattern.Quantifier.oneOrMore(_bcts_dcbor_pattern.Reluctance.Possessive);
7121
+ else if (tokenType === "RepeatZeroOrOne") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Greedy);
7122
+ else if (tokenType === "RepeatZeroOrOneLazy") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Lazy);
7123
+ else if (tokenType === "RepeatZeroOrOnePossessive") quantifier = _bcts_dcbor_pattern.Quantifier.zeroOrOne(_bcts_dcbor_pattern.Reluctance.Possessive);
7124
+ else if (tokenType === "Range") {
7125
+ if (!next.token.value.ok) return err(next.token.value.error);
7126
+ quantifier = next.token.value.value;
7127
+ } else return inner;
7128
+ lexer.next();
7129
+ return ok(repeat(inner.value, quantifier.min(), quantifier.max(), quantifier.reluctance()));
7130
+ }
7131
+ //#endregion
7132
+ //#region src/parse/meta/search-parser.ts
7133
+ /**
7134
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7135
+ * Copyright © 2025-2026 Parity Technologies
7136
+ *
7137
+ * Search parser — port of `bc-envelope-pattern-rust`
7138
+ * `parse/meta/search_parser.rs`.
7139
+ *
7140
+ * @module envelope-pattern/parse/meta/search-parser
7141
+ */
7142
+ function parseSearch(lexer) {
7143
+ const open = lexer.next();
7144
+ if (open === void 0) return err(unexpectedEndOfInput());
7145
+ if (open.token.type !== "ParenOpen") return err(unexpectedToken(open.token, open.span));
7146
+ const inner = parseOr(lexer);
7147
+ if (!inner.ok) return inner;
7148
+ const close = lexer.next();
7149
+ if (close === void 0) return err(expectedCloseParen(lexer.span()));
7150
+ if (close.token.type !== "ParenClose") return err(unexpectedToken(close.token, close.span));
7151
+ return ok(search(inner.value));
7152
+ }
7153
+ //#endregion
7154
+ //#region src/parse/meta/primary-parser.ts
7155
+ /**
7156
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7157
+ * Copyright © 2025-2026 Parity Technologies
7158
+ *
7159
+ * Primary parser — port of `bc-envelope-pattern-rust`
7160
+ * `parse/meta/primary_parser.rs`.
7161
+ *
7162
+ * Dispatches on the next token to the appropriate leaf/structure/meta
7163
+ * parser. When a `(` is encountered the open paren is consumed here and
7164
+ * `parse_group` handles the rest (paren'd expression + optional
7165
+ * quantifier suffix).
7166
+ *
7167
+ * @module envelope-pattern/parse/meta/primary-parser
7168
+ */
7169
+ function parsePrimary(lexer) {
7170
+ const tokenResult = lexer.next();
7171
+ if (tokenResult === void 0) return err(unexpectedEndOfInput());
7172
+ const { token, span } = tokenResult;
7173
+ switch (token.type) {
7174
+ case "Search": return parseSearch(lexer);
7175
+ case "Node": return parseNode(lexer);
7176
+ case "Assertion": return parseAssertion(lexer);
7177
+ case "AssertionPred": return parseAssertionPred(lexer);
7178
+ case "AssertionObj": return parseAssertionObj(lexer);
7179
+ case "Digest": return parseDigest(lexer);
7180
+ case "Obj": return parseObject(lexer);
7181
+ case "Obscured": return parseObscured(lexer);
7182
+ case "Elided": return parseElided(lexer);
7183
+ case "Encrypted": return parseEncrypted(lexer);
7184
+ case "Compressed": return parseCompressed(lexer);
7185
+ case "Pred": return parsePredicate(lexer);
7186
+ case "Subject": return parseSubject(lexer);
7187
+ case "Wrapped": return parseWrapped(lexer);
7188
+ case "Unwrap": return parseUnwrap(lexer);
7189
+ case "Leaf": return ok(leaf());
7190
+ case "GroupName": return parseCapture(lexer, token.name);
7191
+ case "ParenOpen": return parseGroup(lexer);
7192
+ case "Cbor": return parseCbor(lexer);
7193
+ case "RepeatZeroOrMore": return ok(any());
7194
+ case "BoolKeyword": return ok(anyBool());
7195
+ case "BoolTrue": return ok(bool(true));
7196
+ case "BoolFalse": return ok(bool(false));
7197
+ case "NumberKeyword": return ok(anyNumber());
7198
+ case "TextKeyword": return ok(anyText());
7199
+ case "StringLiteral":
7200
+ if (!token.value.ok) return err(token.value.error);
7201
+ return ok(text(token.value.value));
7202
+ case "UnsignedInteger":
7203
+ if (!token.value.ok) return err(token.value.error);
7204
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7205
+ case "Integer":
7206
+ if (!token.value.ok) return err(token.value.error);
7207
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7208
+ case "Float":
7209
+ if (!token.value.ok) return err(token.value.error);
7210
+ return parseNumberRangeOrComparison(lexer, token.value.value);
7211
+ case "GreaterThanOrEqual": return parseComparisonNumber(lexer, ">=");
7212
+ case "LessThanOrEqual": return parseComparisonNumber(lexer, "<=");
7213
+ case "GreaterThan": return parseComparisonNumber(lexer, ">");
7214
+ case "LessThan": return parseComparisonNumber(lexer, "<");
7215
+ case "NaN": return ok(patternLeaf(leafNumber(NumberPattern.nan())));
7216
+ case "Infinity": return ok(number(Infinity));
7217
+ case "NegativeInfinity": return ok(number(-Infinity));
7218
+ case "Regex":
7219
+ if (!token.value.ok) return err(token.value.error);
7220
+ try {
7221
+ return ok(textRegex(new RegExp(token.value.value)));
7222
+ } catch {
7223
+ return err(invalidRegex(span));
7224
+ }
7225
+ case "BracketOpen": return parseArray(lexer);
7226
+ case "ByteString": return ok(anyByteString());
7227
+ case "HexPattern":
7228
+ if (!token.value.ok) return err(token.value.error);
7229
+ return ok(byteString(token.value.value));
7230
+ case "HexBinaryRegex":
7231
+ if (!token.value.ok) return err(token.value.error);
7232
+ try {
7233
+ return ok(patternLeaf(leafByteString(ByteStringPattern.regex(new RegExp(token.value.value)))));
7234
+ } catch {
7235
+ return err(invalidRegex(span));
7236
+ }
7237
+ case "DateKeyword": return ok(anyDate());
7238
+ case "DatePattern":
7239
+ if (!token.value.ok) return err(token.value.error);
7240
+ return parseDateContent(token.value.value, span);
7241
+ case "Tagged": return parseTag(lexer);
7242
+ case "Known": return ok(anyKnownValue());
7243
+ case "SingleQuotedPattern":
7244
+ if (!token.value.ok) return err(token.value.error);
7245
+ return parseKnownValueContent(token.value.value);
7246
+ case "SingleQuotedRegex":
7247
+ if (!token.value.ok) return err(token.value.error);
7248
+ try {
7249
+ return ok(patternLeaf(leafKnownValue(KnownValuePattern.regex(new RegExp(token.value.value)))));
7250
+ } catch {
7251
+ return err(invalidRegex(span));
7252
+ }
7253
+ case "Null": return ok(nullPattern());
7254
+ case "Identifier": return err(unrecognizedToken(span));
7255
+ case "And":
7256
+ case "Or":
7257
+ case "Not":
7258
+ case "Traverse":
7259
+ case "RepeatZeroOrMoreLazy":
7260
+ case "RepeatZeroOrMorePossessive":
7261
+ case "RepeatOneOrMore":
7262
+ case "RepeatOneOrMoreLazy":
7263
+ case "RepeatOneOrMorePossessive":
7264
+ case "RepeatZeroOrOne":
7265
+ case "RepeatZeroOrOneLazy":
7266
+ case "RepeatZeroOrOnePossessive":
7267
+ case "ParenClose":
7268
+ case "BracketClose":
7269
+ case "Comma":
7270
+ case "Ellipsis":
7271
+ case "Range": return err(unexpectedToken(token, span));
7272
+ }
7273
+ }
7274
+ //#endregion
7275
+ //#region src/parse/meta/and-parser.ts
7276
+ /**
7277
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7278
+ * Copyright © 2025-2026 Parity Technologies
7279
+ *
7280
+ * And parser — port of `bc-envelope-pattern-rust`
7281
+ * `parse/meta/and_parser.rs`.
7282
+ *
7283
+ * Mirrors Rust: `parse_and` calls `parse_primary` (NOT `parse_not`); `!`
7284
+ * is handled at a higher precedence level by `parse_not`.
7285
+ *
7286
+ * @module envelope-pattern/parse/meta/and-parser
7287
+ */
7288
+ function parseAnd(lexer) {
7289
+ const patterns = [];
7290
+ const first = parsePrimary(lexer);
7291
+ if (!first.ok) return first;
7292
+ patterns.push(first.value);
7293
+ while (true) {
7294
+ if (lexer.peekToken()?.token.type !== "And") break;
7295
+ lexer.next();
7296
+ const nextExpr = parsePrimary(lexer);
7297
+ if (!nextExpr.ok) return nextExpr;
7298
+ patterns.push(nextExpr.value);
7299
+ }
7300
+ if (patterns.length === 1) return ok(patterns[0]);
7301
+ return ok(and(patterns));
7302
+ }
7303
+ //#endregion
7304
+ //#region src/parse/meta/not-parser.ts
7305
+ /**
7306
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7307
+ * Copyright © 2025-2026 Parity Technologies
7308
+ *
7309
+ * Not parser — port of `bc-envelope-pattern-rust`
7310
+ * `parse/meta/not_parser.rs`.
7311
+ *
7312
+ * Mirrors Rust:
7313
+ * - On `!`, recurse into `parse_not` so chained negation parses as
7314
+ * `not(not(x))` rather than `not(group(x))`.
7315
+ * - Otherwise descend into `parse_and`.
7316
+ *
7317
+ * @module envelope-pattern/parse/meta/not-parser
7318
+ */
7319
+ function parseNot(lexer) {
7320
+ if (lexer.peekToken()?.token.type === "Not") {
7321
+ lexer.next();
7322
+ const inner = parseNot(lexer);
7323
+ if (!inner.ok) return inner;
7324
+ return ok(notMatching(inner.value));
7325
+ }
7326
+ return parseAnd(lexer);
7327
+ }
7328
+ //#endregion
7329
+ //#region src/parse/meta/traverse-parser.ts
7330
+ /**
7331
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7332
+ * Copyright © 2025-2026 Parity Technologies
7333
+ *
7334
+ * Traverse parser — port of `bc-envelope-pattern-rust`
7335
+ * `parse/meta/traverse_parser.rs`.
7336
+ *
7337
+ * Note the precedence chain: `parse_or → parse_traverse → parse_not →
7338
+ * parse_and → parse_primary`. The earlier TS port had `parse_traverse`
7339
+ * call `parse_and` directly, which pushed `!` below `&` and
7340
+ * miscompiled `!a & b`.
7341
+ *
7342
+ * @module envelope-pattern/parse/meta/traverse-parser
7343
+ */
7344
+ function parseTraverse(lexer) {
7345
+ const patterns = [];
7346
+ const first = parseNot(lexer);
7347
+ if (!first.ok) return first;
7348
+ patterns.push(first.value);
7349
+ while (true) {
7350
+ if (lexer.peekToken()?.token.type !== "Traverse") break;
7351
+ lexer.next();
7352
+ const nextExpr = parseNot(lexer);
7353
+ if (!nextExpr.ok) return nextExpr;
7354
+ patterns.push(nextExpr.value);
7355
+ }
7356
+ if (patterns.length === 1) return ok(patterns[0]);
7357
+ return ok(traverse(patterns));
7358
+ }
7359
+ //#endregion
7360
+ //#region src/parse/meta/or-parser.ts
7361
+ /**
7362
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7363
+ * Copyright © 2025-2026 Parity Technologies
7364
+ *
7365
+ * Or parser — port of `bc-envelope-pattern-rust` `parse/meta/or_parser.rs`.
7366
+ *
7367
+ * @module envelope-pattern/parse/meta/or-parser
7368
+ */
7369
+ function parseOr(lexer) {
7370
+ const patterns = [];
7371
+ const first = parseTraverse(lexer);
7372
+ if (!first.ok) return first;
7373
+ patterns.push(first.value);
7374
+ while (true) {
7375
+ if (lexer.peekToken()?.token.type !== "Or") break;
7376
+ lexer.next();
7377
+ const nextExpr = parseTraverse(lexer);
7378
+ if (!nextExpr.ok) return nextExpr;
7379
+ patterns.push(nextExpr.value);
7380
+ }
7381
+ if (patterns.length === 1) return ok(patterns[0]);
7382
+ return ok(or(patterns));
7383
+ }
7384
+ //#endregion
7385
+ //#region src/parse/index.ts
7386
+ /**
7387
+ * Copyright © 2023-2026 Blockchain Commons, LLC
7388
+ * Copyright © 2025-2026 Parity Technologies
7389
+ *
7390
+ *
7391
+ * @bcts/envelope-pattern - Parser entry point
7392
+ *
7393
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust parse/mod.rs.
7394
+ *
7395
+ * Recursive-descent parser for the Gordian Envelope pattern syntax. The
7396
+ * parsing rules live under `parse/leaf/`, `parse/meta/`, and
7397
+ * `parse/structure/`, mirroring the Rust crate's module layout.
7398
+ *
7399
+ * @module envelope-pattern/parse
7400
+ */
7401
+ /**
7402
+ * Parse a pattern expression string into a Pattern.
7403
+ *
7404
+ * Mirrors Rust `Pattern::parse`: tries envelope-pattern parsing first;
7405
+ * on failure falls back to dcbor-pattern parsing and converts the
7406
+ * result into an envelope pattern via the
7407
+ * `dcbor_integration::convert_dcbor_pattern_to_envelope_pattern` bridge.
7408
+ */
7409
+ function parse(input) {
7410
+ const lexer = new Lexer(input);
7411
+ const result = parseOr(lexer);
7412
+ if (!result.ok) {
7413
+ const dcborResult = (0, _bcts_dcbor_pattern.parse)(input);
7414
+ if (dcborResult.ok) return convertDcborPatternToEnvelopePattern(dcborResult.value);
7415
+ return result;
7416
+ }
7417
+ const next = lexer.next();
7418
+ if (next !== void 0) return err(extraData(next.span));
7419
+ return result;
7420
+ }
7421
+ /**
7422
+ * Parse a pattern, allowing extra data after the pattern.
7423
+ *
7424
+ * Returns the parsed pattern and the byte offset at which parsing
7425
+ * stopped, mirroring `Pattern::parse_partial` in spirit.
7426
+ */
7427
+ function parsePartial(input) {
7428
+ const lexer = new Lexer(input);
7429
+ const result = parseOr(lexer);
7430
+ if (!result.ok) return result;
7431
+ return ok([result.value, lexer.position]);
7432
+ }
7433
+ //#endregion
6901
7434
  //#region src/index.ts
6902
7435
  /**
6903
7436
  * Package version.
@@ -7090,6 +7623,7 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
7090
7623
  exports.registerAnyPatternFactory = registerAnyPatternFactory;
7091
7624
  exports.registerArrayPatternFactory = registerArrayPatternFactory;
7092
7625
  exports.registerAssertionsPatternFactory = registerAssertionsPatternFactory;
7626
+ exports.registerAssertionsPatternToStringDispatch = registerAssertionsPatternToStringDispatch;
7093
7627
  exports.registerBoolPatternFactory = registerBoolPatternFactory;
7094
7628
  exports.registerByteStringPatternFactory = registerByteStringPatternFactory;
7095
7629
  exports.registerCBORPatternFactory = registerCBORPatternFactory;
@@ -7111,11 +7645,13 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
7111
7645
  exports.registerPatternMatchFn = registerPatternMatchFn;
7112
7646
  exports.registerPredicatePatternFactory = registerPredicatePatternFactory;
7113
7647
  exports.registerSearchPatternFactory = registerSearchPatternFactory;
7648
+ exports.registerSubjectPatternDispatch = registerSubjectPatternDispatch;
7114
7649
  exports.registerSubjectPatternFactory = registerSubjectPatternFactory;
7115
7650
  exports.registerTaggedPatternFactory = registerTaggedPatternFactory;
7116
7651
  exports.registerTextPatternFactory = registerTextPatternFactory;
7117
7652
  exports.registerTraversePatternFactory = registerTraversePatternFactory;
7118
7653
  exports.registerVMPatternFunctions = registerVMPatternFunctions;
7654
+ exports.registerWrappedPatternAny = registerWrappedPatternAny;
7119
7655
  exports.registerWrappedPatternDispatch = registerWrappedPatternDispatch;
7120
7656
  exports.registerWrappedPatternFactory = registerWrappedPatternFactory;
7121
7657
  exports.repeat = repeat;
@@ -7155,6 +7691,6 @@ var bctsEnvelopePattern = (function(exports, _bcts_dcbor_pattern, _bcts_known_va
7155
7691
  exports.unwrapOr = unwrapOr;
7156
7692
  exports.wrapped = wrapped;
7157
7693
  return exports;
7158
- })({}, bctsDcborPattern, bctsKnownValues, bctsEnvelope, bctsDcbor, bctsDcborParse);
7694
+ })({}, bctsDcborPattern, bctsKnownValues, bctsDcbor, bctsEnvelope, bctsDcborParse);
7159
7695
 
7160
7696
  //# sourceMappingURL=index.iife.js.map