@bcts/envelope-pattern 1.0.0-alpha.22 → 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/index.cjs +1291 -826
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +101 -59
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +102 -60
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.iife.js +1643 -1179
  8. package/dist/index.iife.js.map +1 -1
  9. package/dist/index.mjs +1315 -853
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +13 -11
  12. package/src/error.ts +1 -1
  13. package/src/format.ts +19 -31
  14. package/src/parse/index.ts +17 -1010
  15. package/src/parse/leaf/array-parser.ts +36 -0
  16. package/src/parse/leaf/cbor-parser.ts +43 -0
  17. package/src/parse/leaf/date-parser.ts +81 -0
  18. package/src/parse/leaf/known-value-parser.ts +73 -0
  19. package/src/parse/leaf/null-parser.ts +16 -0
  20. package/src/parse/leaf/number-parser.ts +90 -0
  21. package/src/parse/leaf/tag-parser.ts +160 -0
  22. package/src/parse/meta/and-parser.ts +40 -0
  23. package/src/parse/meta/capture-parser.ts +50 -0
  24. package/src/parse/meta/group-parser.ts +77 -0
  25. package/src/parse/meta/not-parser.ts +30 -0
  26. package/src/parse/meta/or-parser.ts +36 -0
  27. package/src/parse/meta/primary-parser.ts +234 -0
  28. package/src/parse/meta/search-parser.ts +41 -0
  29. package/src/parse/meta/traverse-parser.ts +42 -0
  30. package/src/parse/structure/assertion-obj-parser.ts +44 -0
  31. package/src/parse/structure/assertion-parser.ts +22 -0
  32. package/src/parse/structure/assertion-pred-parser.ts +45 -0
  33. package/src/parse/structure/compressed-parser.ts +17 -0
  34. package/src/parse/structure/digest-parser.ts +132 -0
  35. package/src/parse/structure/elided-parser.ts +17 -0
  36. package/src/parse/structure/encrypted-parser.ts +17 -0
  37. package/src/parse/structure/node-parser.ts +54 -0
  38. package/src/parse/structure/object-parser.ts +32 -0
  39. package/src/parse/structure/obscured-parser.ts +17 -0
  40. package/src/parse/structure/predicate-parser.ts +32 -0
  41. package/src/parse/structure/subject-parser.ts +32 -0
  42. package/src/parse/structure/wrapped-parser.ts +36 -0
  43. package/src/pattern/dcbor-integration.ts +40 -8
  44. package/src/pattern/index.ts +29 -0
  45. package/src/pattern/leaf/array-pattern.ts +67 -169
  46. package/src/pattern/leaf/cbor-pattern.ts +37 -23
  47. package/src/pattern/leaf/index.ts +1 -1
  48. package/src/pattern/leaf/map-pattern.ts +21 -2
  49. package/src/pattern/leaf/tagged-pattern.ts +6 -1
  50. package/src/pattern/meta/search-pattern.ts +13 -38
  51. package/src/pattern/meta/traverse-pattern.ts +2 -2
  52. package/src/pattern/structure/assertions-pattern.ts +19 -53
  53. package/src/pattern/structure/digest-pattern.ts +18 -22
  54. package/src/pattern/structure/index.ts +3 -0
  55. package/src/pattern/structure/node-pattern.ts +10 -29
  56. package/src/pattern/structure/object-pattern.ts +2 -2
  57. package/src/pattern/structure/predicate-pattern.ts +2 -2
  58. package/src/pattern/structure/subject-pattern.ts +31 -4
  59. package/src/pattern/structure/wrapped-pattern.ts +28 -9
  60. package/src/pattern/vm.ts +4 -4
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Node parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/node_parser.rs`.
7
+ *
8
+ * Mirrors Rust:
9
+ * - Bare `node` → `any_node()`.
10
+ * - `node({n})` / `node({n,m})` / `node({n,})` → `node_with_assertions_range`.
11
+ * - Anything else inside the parens (e.g. `node(text)`) is a syntax error
12
+ * in Rust; the previous TS port silently produced an always-matching
13
+ * `WithSubject` node, which we have removed.
14
+ *
15
+ * @module envelope-pattern/parse/structure/node-parser
16
+ */
17
+
18
+ import {
19
+ type Result,
20
+ err,
21
+ expectedCloseParen,
22
+ ok,
23
+ unexpectedEndOfInput,
24
+ unexpectedToken,
25
+ } from "../../error";
26
+ import { type Pattern, NodePattern, anyNode, patternStructure, structureNode } from "../../pattern";
27
+ import type { Lexer } from "../token";
28
+
29
+ export function parseNode(lexer: Lexer): Result<Pattern> {
30
+ const next = lexer.peekToken();
31
+ if (next?.token.type !== "ParenOpen") {
32
+ return ok(anyNode());
33
+ }
34
+ lexer.next(); // consume (
35
+
36
+ const inner = lexer.next();
37
+ if (inner === undefined) {
38
+ return err(unexpectedEndOfInput());
39
+ }
40
+ if (inner.token.type !== "Range") {
41
+ return err(unexpectedToken(inner.token, inner.span));
42
+ }
43
+ if (!inner.token.value.ok) return err(inner.token.value.error);
44
+ const interval = inner.token.value.value.interval();
45
+
46
+ const close = lexer.next();
47
+ if (close === undefined) {
48
+ return err(expectedCloseParen(lexer.span()));
49
+ }
50
+ if (close.token.type !== "ParenClose") {
51
+ return err(unexpectedToken(close.token, close.span));
52
+ }
53
+ return ok(patternStructure(structureNode(NodePattern.fromInterval(interval))));
54
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Object parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/object_parser.rs`.
7
+ *
8
+ * @module envelope-pattern/parse/structure/object-parser
9
+ */
10
+
11
+ import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
12
+ import { type Pattern, anyObject, object } from "../../pattern";
13
+ import type { Lexer } from "../token";
14
+ import { parseOr } from "../meta/or-parser";
15
+
16
+ export function parseObject(lexer: Lexer): Result<Pattern> {
17
+ const next = lexer.peekToken();
18
+ if (next?.token.type !== "ParenOpen") {
19
+ return ok(anyObject());
20
+ }
21
+ lexer.next(); // consume (
22
+ const inner = parseOr(lexer);
23
+ if (!inner.ok) return inner;
24
+ const close = lexer.next();
25
+ if (close === undefined) {
26
+ return err(expectedCloseParen(lexer.span()));
27
+ }
28
+ if (close.token.type !== "ParenClose") {
29
+ return err(unexpectedToken(close.token, close.span));
30
+ }
31
+ return ok(object(inner.value));
32
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Obscured parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/obscured_parser.rs`.
7
+ *
8
+ * @module envelope-pattern/parse/structure/obscured-parser
9
+ */
10
+
11
+ import { type Result, ok } from "../../error";
12
+ import { type Pattern, obscured } from "../../pattern";
13
+ import type { Lexer } from "../token";
14
+
15
+ export function parseObscured(_lexer: Lexer): Result<Pattern> {
16
+ return ok(obscured());
17
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Predicate parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/predicate_parser.rs`.
7
+ *
8
+ * @module envelope-pattern/parse/structure/predicate-parser
9
+ */
10
+
11
+ import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
12
+ import { type Pattern, anyPredicate, predicate } from "../../pattern";
13
+ import type { Lexer } from "../token";
14
+ import { parseOr } from "../meta/or-parser";
15
+
16
+ export function parsePredicate(lexer: Lexer): Result<Pattern> {
17
+ const next = lexer.peekToken();
18
+ if (next?.token.type !== "ParenOpen") {
19
+ return ok(anyPredicate());
20
+ }
21
+ lexer.next(); // consume (
22
+ const inner = parseOr(lexer);
23
+ if (!inner.ok) return inner;
24
+ const close = lexer.next();
25
+ if (close === undefined) {
26
+ return err(expectedCloseParen(lexer.span()));
27
+ }
28
+ if (close.token.type !== "ParenClose") {
29
+ return err(unexpectedToken(close.token, close.span));
30
+ }
31
+ return ok(predicate(inner.value));
32
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Subject parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/subject_parser.rs`.
7
+ *
8
+ * @module envelope-pattern/parse/structure/subject-parser
9
+ */
10
+
11
+ import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
12
+ import { type Pattern, anySubject, subject } from "../../pattern";
13
+ import type { Lexer } from "../token";
14
+ import { parseOr } from "../meta/or-parser";
15
+
16
+ export function parseSubject(lexer: Lexer): Result<Pattern> {
17
+ const next = lexer.peekToken();
18
+ if (next?.token.type !== "ParenOpen") {
19
+ return ok(anySubject());
20
+ }
21
+ lexer.next(); // consume (
22
+ const inner = parseOr(lexer);
23
+ if (!inner.ok) return inner;
24
+ const close = lexer.next();
25
+ if (close === undefined) {
26
+ return err(expectedCloseParen(lexer.span()));
27
+ }
28
+ if (close.token.type !== "ParenClose") {
29
+ return err(unexpectedToken(close.token, close.span));
30
+ }
31
+ return ok(subject(inner.value));
32
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Copyright © 2023-2026 Blockchain Commons, LLC
3
+ * Copyright © 2025-2026 Parity Technologies
4
+ *
5
+ * Wrapped/unwrap parser — port of `bc-envelope-pattern-rust`
6
+ * `parse/structure/wrapped_parser.rs`.
7
+ *
8
+ * @module envelope-pattern/parse/structure/wrapped-parser
9
+ */
10
+
11
+ import { type Result, err, expectedCloseParen, ok, unexpectedToken } from "../../error";
12
+ import { type Pattern, unwrapEnvelope, unwrapMatching, wrapped } from "../../pattern";
13
+ import type { Lexer } from "../token";
14
+ import { parseOr } from "../meta/or-parser";
15
+
16
+ export function parseWrapped(_lexer: Lexer): Result<Pattern> {
17
+ return ok(wrapped());
18
+ }
19
+
20
+ export function parseUnwrap(lexer: Lexer): Result<Pattern> {
21
+ const next = lexer.peekToken();
22
+ if (next?.token.type !== "ParenOpen") {
23
+ return ok(unwrapEnvelope());
24
+ }
25
+ lexer.next(); // consume (
26
+ const inner = parseOr(lexer);
27
+ if (!inner.ok) return inner;
28
+ const close = lexer.next();
29
+ if (close === undefined) {
30
+ return err(expectedCloseParen(lexer.span()));
31
+ }
32
+ if (close.token.type !== "ParenClose") {
33
+ return err(unexpectedToken(close.token, close.span));
34
+ }
35
+ return ok(unwrapMatching(inner.value));
36
+ }
@@ -135,17 +135,49 @@ function convertStructurePatternToEnvelopePattern(
135
135
  ): Result<Pattern> {
136
136
  switch (structurePattern.type) {
137
137
  case "Array": {
138
- // Wrap the full dcbor pattern for array matching
139
- const arrayPattern = ArrayPattern.fromDcborPattern({
140
- kind: "Structure",
141
- pattern: structurePattern,
142
- });
138
+ // Mirror Rust `convert_structure_pattern_to_envelope_pattern`'s
139
+ // `Array` arm: dispatch on the dcbor `ArrayPattern` variant so
140
+ // that `array` (Any) round-trips to `array`, `[{n,m}]` (Length)
141
+ // round-trips to `[{n,m}]`, and only Elements variants stay
142
+ // wrapped. Using `fromDcborPattern` for the Any variant produces
143
+ // an `Elements`-wrapped pattern that prints as `[array]`,
144
+ // breaking parser parity.
145
+ const inner = structurePattern.pattern;
146
+ let arrayPattern: ArrayPattern;
147
+ switch (inner.variant) {
148
+ case "Any":
149
+ arrayPattern = ArrayPattern.any();
150
+ break;
151
+ case "Length":
152
+ arrayPattern = ArrayPattern.fromInterval(inner.length);
153
+ break;
154
+ case "Elements":
155
+ arrayPattern = ArrayPattern.fromDcborArrayPattern(inner);
156
+ break;
157
+ }
143
158
  return ok({ type: "Leaf", pattern: leafArray(arrayPattern) });
144
159
  }
145
160
  case "Map": {
146
- // MapPattern only supports interval-based matching currently
147
- // Use any() for general map patterns from dcbor
148
- const mapPattern = MapPattern.any();
161
+ // Mirror Rust's variant dispatch preserve `{{n,m}}` length
162
+ // info instead of collapsing every map pattern to `Any` (which
163
+ // breaks parser parity for `{{3}}`, `{{2,4}}`, `{{2,}}`).
164
+ const inner = structurePattern.pattern;
165
+ let mapPattern: MapPattern;
166
+ switch (inner.variant) {
167
+ case "Any":
168
+ mapPattern = MapPattern.any();
169
+ break;
170
+ case "Length":
171
+ mapPattern = MapPattern.fromInterval(inner.length);
172
+ break;
173
+ case "Constraints":
174
+ // The envelope-pattern `MapPattern` doesn't yet model dcbor
175
+ // map constraints. Falling back to `Any` is conservative —
176
+ // round-trip parity here is broken until envelope-pattern
177
+ // grows a constraints variant.
178
+ mapPattern = MapPattern.any();
179
+ break;
180
+ }
149
181
  return ok({ type: "Leaf", pattern: leafMap(mapPattern) });
150
182
  }
151
183
  case "Tagged": {
@@ -110,6 +110,9 @@ import {
110
110
  registerObscuredPatternFactory,
111
111
  registerWrappedPatternFactory,
112
112
  registerWrappedPatternDispatch,
113
+ registerWrappedPatternAny,
114
+ registerAssertionsPatternToStringDispatch,
115
+ registerSubjectPatternDispatch,
113
116
  } from "./structure";
114
117
 
115
118
  // Import meta patterns
@@ -801,6 +804,12 @@ function registerAllFactories(): void {
801
804
  compile: patternCompile,
802
805
  toString: patternToString,
803
806
  });
807
+ registerWrappedPatternAny(any);
808
+ registerAssertionsPatternToStringDispatch(patternToString);
809
+ registerSubjectPatternDispatch({
810
+ compile: patternCompile,
811
+ toString: patternToString,
812
+ });
804
813
 
805
814
  // Meta pattern factories
806
815
  registerAnyPatternFactory((p) => patternMeta(metaAny(p)));
@@ -820,6 +829,26 @@ registerAllFactories();
820
829
  import { registerVMPatternFunctions } from "./vm";
821
830
  registerVMPatternFunctions(patternPathsWithCaptures, patternMatches, patternPaths);
822
831
 
832
+ // Register pattern-construction factories used by `parse/utils.ts` while it
833
+ // runs `parse_cbor_inner` / `parse_array_inner`. These mirror Rust's direct
834
+ // calls to `Pattern::cbor_pattern`, `Pattern::any_array`, etc. — exposed
835
+ // here as factory hooks because the parser sits one module layer below the
836
+ // `Pattern` factories and would otherwise create a circular import.
837
+ import { registerPatternFactories as registerParseUtilsFactories } from "../parse/utils";
838
+ import { type Pattern as DCBORPatternRegister } from "@bcts/dcbor-pattern";
839
+ import type { Cbor } from "@bcts/dcbor";
840
+
841
+ registerParseUtilsFactories({
842
+ cborPattern: (value: Cbor) => cborValue(value),
843
+ cborPatternFromDcbor: (pattern: DCBORPatternRegister) => cborPattern(pattern),
844
+ anyArray,
845
+ arrayWithCount: (count: number) => patternLeaf(leafArray(ArrayPattern.count(count))),
846
+ arrayWithRange: (min: number, max?: number) =>
847
+ patternLeaf(leafArray(ArrayPattern.interval(min, max))),
848
+ arrayFromDcborPattern: (pattern: DCBORPatternRegister) =>
849
+ patternLeaf(leafArray(ArrayPattern.fromDcborPattern(pattern))),
850
+ });
851
+
823
852
  // Register pattern dispatch functions for meta patterns
824
853
  import { registerPatternMatchFn, registerPatternDispatchFns } from "./matcher";
825
854
  registerPatternMatchFn(patternMatches);
@@ -5,17 +5,29 @@
5
5
  *
6
6
  * @bcts/envelope-pattern - Array pattern matching
7
7
  *
8
- * This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs
8
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust array_pattern.rs.
9
+ *
10
+ * Like the Rust port, this is a thin wrapper around `dcbor_pattern::ArrayPattern`
11
+ * that delegates all matching to dcbor-pattern. The envelope-pattern's
12
+ * `ArrayPattern` adds nothing beyond extracting the leaf CBOR from the
13
+ * envelope subject before delegating.
9
14
  *
10
15
  * @module envelope-pattern/pattern/leaf/array-pattern
11
16
  */
12
17
 
13
- import { Envelope } from "@bcts/envelope";
14
- import { asCborArray, type Cbor } from "@bcts/dcbor";
18
+ import type { Envelope } from "@bcts/envelope";
15
19
  import {
20
+ type ArrayPattern as DCBORArrayPattern,
16
21
  type Pattern as DCBORPattern,
17
- Interval,
18
- patternPathsWithCaptures as dcborPatternPathsWithCaptures,
22
+ type Interval,
23
+ arrayPatternAny,
24
+ arrayPatternMatches,
25
+ arrayPatternWithElements,
26
+ arrayPatternWithLength,
27
+ arrayPatternWithLengthInterval,
28
+ arrayPatternWithLengthRange,
29
+ arrayPatternDisplay,
30
+ arrayPatternEquals,
19
31
  patternDisplay as dcborPatternDisplay,
20
32
  } from "@bcts/dcbor-pattern";
21
33
  import type { Path } from "../../format";
@@ -32,25 +44,16 @@ export function registerArrayPatternFactory(factory: (pattern: ArrayPattern) =>
32
44
  }
33
45
 
34
46
  /**
35
- * Pattern for matching array values.
47
+ * Pattern for matching arrays.
36
48
  *
37
- * Corresponds to the Rust `ArrayPattern` enum in array_pattern.rs
38
- */
39
- export type ArrayPatternType =
40
- | { readonly type: "Any" }
41
- | { readonly type: "Interval"; readonly interval: Interval }
42
- | { readonly type: "DCBORPattern"; readonly pattern: DCBORPattern }
43
- | { readonly type: "WithPatterns"; readonly patterns: Pattern[] };
44
-
45
- /**
46
- * Pattern for matching array values in envelope leaf nodes.
47
- *
48
- * Corresponds to the Rust `ArrayPattern` struct in array_pattern.rs
49
+ * Mirrors Rust `ArrayPattern(dcbor_pattern::ArrayPattern)` from
50
+ * `bc-envelope-pattern-rust/src/pattern/leaf/array_pattern.rs`. All
51
+ * matching, display, and equality is delegated to dcbor-pattern.
49
52
  */
50
53
  export class ArrayPattern implements Matcher {
51
- private readonly _pattern: ArrayPatternType;
54
+ private readonly _pattern: DCBORArrayPattern;
52
55
 
53
- private constructor(pattern: ArrayPatternType) {
56
+ private constructor(pattern: DCBORArrayPattern) {
54
57
  this._pattern = pattern;
55
58
  }
56
59
 
@@ -58,124 +61,65 @@ export class ArrayPattern implements Matcher {
58
61
  * Creates a new ArrayPattern that matches any array.
59
62
  */
60
63
  static any(): ArrayPattern {
61
- return new ArrayPattern({ type: "Any" });
64
+ return new ArrayPattern(arrayPatternAny());
62
65
  }
63
66
 
64
67
  /**
65
68
  * Creates a new ArrayPattern that matches arrays with a specific length.
66
69
  */
67
70
  static count(count: number): ArrayPattern {
68
- return new ArrayPattern({
69
- type: "Interval",
70
- interval: Interval.exactly(count),
71
- });
71
+ return new ArrayPattern(arrayPatternWithLength(count));
72
72
  }
73
73
 
74
74
  /**
75
75
  * Creates a new ArrayPattern that matches arrays within a length range.
76
76
  */
77
77
  static interval(min: number, max?: number): ArrayPattern {
78
- const interval = max !== undefined ? Interval.from(min, max) : Interval.atLeast(min);
79
- return new ArrayPattern({ type: "Interval", interval });
78
+ return new ArrayPattern(arrayPatternWithLengthRange(min, max));
79
+ }
80
+
81
+ /**
82
+ * Creates a new ArrayPattern from a length Interval.
83
+ */
84
+ static fromInterval(interval: Interval): ArrayPattern {
85
+ return new ArrayPattern(arrayPatternWithLengthInterval(interval));
80
86
  }
81
87
 
82
88
  /**
83
- * Creates a new ArrayPattern from a dcbor-pattern.
89
+ * Creates a new ArrayPattern from a top-level dcbor-pattern.
90
+ *
91
+ * Mirrors Rust `ArrayPattern::from_dcbor_pattern`, which constructs an
92
+ * `ArrayPattern::Elements`-style dcbor array pattern.
84
93
  */
85
- static fromDcborPattern(dcborPattern: DCBORPattern): ArrayPattern {
86
- return new ArrayPattern({ type: "DCBORPattern", pattern: dcborPattern });
94
+ static fromDcborPattern(pattern: DCBORPattern): ArrayPattern {
95
+ return new ArrayPattern(arrayPatternWithElements(pattern));
87
96
  }
88
97
 
89
98
  /**
90
- * Creates a new ArrayPattern with envelope patterns for element matching.
99
+ * Creates a new ArrayPattern from an existing dcbor-pattern ArrayPattern.
100
+ *
101
+ * Mirrors Rust `ArrayPattern::from_dcbor_array_pattern`.
91
102
  */
92
- static withPatterns(patterns: Pattern[]): ArrayPattern {
93
- return new ArrayPattern({ type: "WithPatterns", patterns });
103
+ static fromDcborArrayPattern(arrayPattern: DCBORArrayPattern): ArrayPattern {
104
+ return new ArrayPattern(arrayPattern);
94
105
  }
95
106
 
96
107
  /**
97
- * Gets the pattern type.
108
+ * Returns the underlying dcbor-pattern ArrayPattern.
98
109
  */
99
- get pattern(): ArrayPatternType {
110
+ inner(): DCBORArrayPattern {
100
111
  return this._pattern;
101
112
  }
102
113
 
103
114
  pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
104
- // Try to extract CBOR from the envelope
105
115
  const cbor = haystack.subject().asLeaf();
106
116
  if (cbor === undefined) {
107
117
  return [[], new Map<string, Path[]>()];
108
118
  }
109
-
110
- // Check if it's an array
111
- const array = asCborArray(cbor);
112
- if (array === undefined) {
113
- return [[], new Map<string, Path[]>()];
114
- }
115
-
116
- switch (this._pattern.type) {
117
- case "Any":
118
- return [[[haystack]], new Map<string, Path[]>()];
119
-
120
- case "Interval": {
121
- const length = array.length;
122
- if (this._pattern.interval.contains(length)) {
123
- return [[[haystack]], new Map<string, Path[]>()];
124
- }
125
- return [[], new Map<string, Path[]>()];
126
- }
127
-
128
- case "DCBORPattern": {
129
- // Delegate to dcbor-pattern for matching
130
- const { paths: dcborPaths, captures: dcborCaptures } = dcborPatternPathsWithCaptures(
131
- this._pattern.pattern,
132
- cbor,
133
- );
134
-
135
- if (dcborPaths.length > 0) {
136
- // Convert dcbor paths to envelope paths
137
- const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
138
- const envPath: Path = [haystack];
139
- // Skip the first element (root) and convert rest to envelopes
140
- for (let i = 1; i < dcborPath.length; i++) {
141
- const elem = dcborPath[i];
142
- if (elem !== undefined) {
143
- envPath.push(Envelope.newLeaf(elem));
144
- }
145
- }
146
- return envPath;
147
- });
148
-
149
- // Convert dcbor captures to envelope captures
150
- const envelopeCaptures = new Map<string, Path[]>();
151
- for (const [name, capturePaths] of dcborCaptures) {
152
- const envCapturePaths: Path[] = capturePaths.map((dcborPath: Cbor[]) => {
153
- const envPath: Path = [haystack];
154
- for (let i = 1; i < dcborPath.length; i++) {
155
- const elem = dcborPath[i];
156
- if (elem !== undefined) {
157
- envPath.push(Envelope.newLeaf(elem));
158
- }
159
- }
160
- return envPath;
161
- });
162
- envelopeCaptures.set(name, envCapturePaths);
163
- }
164
-
165
- return [envelopePaths, envelopeCaptures];
166
- }
167
-
168
- return [[], new Map<string, Path[]>()];
169
- }
170
-
171
- case "WithPatterns":
172
- // For envelope patterns, match if array length equals patterns count
173
- // Full element-by-element matching would require additional implementation
174
- if (array.length === this._pattern.patterns.length) {
175
- return [[[haystack]], new Map<string, Path[]>()];
176
- }
177
- return [[], new Map<string, Path[]>()];
119
+ if (arrayPatternMatches(this._pattern, cbor)) {
120
+ return [[[haystack]], new Map<string, Path[]>()];
178
121
  }
122
+ return [[], new Map<string, Path[]>()];
179
123
  }
180
124
 
181
125
  paths(haystack: Envelope): Path[] {
@@ -198,80 +142,34 @@ export class ArrayPattern implements Matcher {
198
142
  }
199
143
 
200
144
  toString(): string {
201
- switch (this._pattern.type) {
202
- case "Any":
203
- return "[*]";
204
- case "Interval":
205
- return `[{${this._pattern.interval.toString()}}]`;
206
- case "DCBORPattern":
207
- return dcborPatternDisplay(this._pattern.pattern);
208
- case "WithPatterns":
209
- return `[${this._pattern.patterns.map(String).join(", ")}]`;
210
- }
145
+ return arrayPatternDisplay(this._pattern, dcborPatternDisplay);
211
146
  }
212
147
 
213
148
  /**
214
- * Equality comparison.
149
+ * Equality comparison. Delegates to dcbor-pattern's structural equality
150
+ * with a display-string fallback for pattern-equality (mirrors Rust's
151
+ * `Hash` impl that hashes the display, since dcbor `ArrayPattern`
152
+ * itself does not derive `Hash`).
215
153
  */
216
154
  equals(other: ArrayPattern): boolean {
217
- if (this._pattern.type !== other._pattern.type) {
218
- return false;
219
- }
220
- switch (this._pattern.type) {
221
- case "Any":
222
- return true;
223
- case "Interval":
224
- return this._pattern.interval.equals(
225
- (other._pattern as { type: "Interval"; interval: Interval }).interval,
226
- );
227
- case "DCBORPattern":
228
- // Compare using display representation
229
- return (
230
- dcborPatternDisplay(this._pattern.pattern) ===
231
- dcborPatternDisplay(
232
- (other._pattern as { type: "DCBORPattern"; pattern: DCBORPattern }).pattern,
233
- )
234
- );
235
- case "WithPatterns": {
236
- const otherPatterns = (other._pattern as { type: "WithPatterns"; patterns: Pattern[] })
237
- .patterns;
238
- if (this._pattern.patterns.length !== otherPatterns.length) return false;
239
- for (let i = 0; i < this._pattern.patterns.length; i++) {
240
- if (this._pattern.patterns[i] !== otherPatterns[i]) return false;
241
- }
242
- return true;
243
- }
244
- }
155
+ return arrayPatternEquals(
156
+ this._pattern,
157
+ other._pattern,
158
+ (a, b) => dcborPatternDisplay(a) === dcborPatternDisplay(b),
159
+ );
245
160
  }
246
161
 
247
162
  /**
248
- * Hash code for use in Maps/Sets.
163
+ * Hash code for use in Maps/Sets. Mirrors Rust's
164
+ * "hash the string representation" approach.
249
165
  */
250
166
  hashCode(): number {
251
- switch (this._pattern.type) {
252
- case "Any":
253
- return 0;
254
- case "Interval":
255
- // Simple hash based on min/max
256
- return this._pattern.interval.min() * 31 + (this._pattern.interval.max() ?? 0);
257
- case "DCBORPattern":
258
- // Simple hash based on display string
259
- return simpleStringHash(dcborPatternDisplay(this._pattern.pattern));
260
- case "WithPatterns":
261
- return this._pattern.patterns.length;
167
+ let hash = 0;
168
+ const str = this.toString();
169
+ for (let i = 0; i < str.length; i++) {
170
+ hash = (hash << 5) - hash + str.charCodeAt(i);
171
+ hash = hash & hash;
262
172
  }
173
+ return hash;
263
174
  }
264
175
  }
265
-
266
- /**
267
- * Simple string hash function for hashCode implementations.
268
- */
269
- function simpleStringHash(str: string): number {
270
- let hash = 0;
271
- for (let i = 0; i < str.length; i++) {
272
- const char = str.charCodeAt(i);
273
- hash = (hash << 5) - hash + char;
274
- hash = hash & hash; // Convert to 32-bit integer
275
- }
276
- return hash;
277
- }