@bcts/envelope-pattern 1.0.0-alpha.12

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 (53) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +13 -0
  3. package/dist/index.cjs +6781 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +2628 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +2628 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +6781 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +6545 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +77 -0
  14. package/src/error.ts +262 -0
  15. package/src/format.ts +375 -0
  16. package/src/index.ts +27 -0
  17. package/src/parse/index.ts +923 -0
  18. package/src/parse/token.ts +906 -0
  19. package/src/parse/utils.ts +339 -0
  20. package/src/pattern/index.ts +719 -0
  21. package/src/pattern/leaf/array-pattern.ts +273 -0
  22. package/src/pattern/leaf/bool-pattern.ts +140 -0
  23. package/src/pattern/leaf/byte-string-pattern.ts +172 -0
  24. package/src/pattern/leaf/cbor-pattern.ts +355 -0
  25. package/src/pattern/leaf/date-pattern.ts +178 -0
  26. package/src/pattern/leaf/index.ts +280 -0
  27. package/src/pattern/leaf/known-value-pattern.ts +192 -0
  28. package/src/pattern/leaf/map-pattern.ts +152 -0
  29. package/src/pattern/leaf/null-pattern.ts +110 -0
  30. package/src/pattern/leaf/number-pattern.ts +248 -0
  31. package/src/pattern/leaf/tagged-pattern.ts +228 -0
  32. package/src/pattern/leaf/text-pattern.ts +165 -0
  33. package/src/pattern/matcher.ts +88 -0
  34. package/src/pattern/meta/and-pattern.ts +109 -0
  35. package/src/pattern/meta/any-pattern.ts +81 -0
  36. package/src/pattern/meta/capture-pattern.ts +111 -0
  37. package/src/pattern/meta/group-pattern.ts +110 -0
  38. package/src/pattern/meta/index.ts +269 -0
  39. package/src/pattern/meta/not-pattern.ts +91 -0
  40. package/src/pattern/meta/or-pattern.ts +146 -0
  41. package/src/pattern/meta/search-pattern.ts +201 -0
  42. package/src/pattern/meta/traverse-pattern.ts +146 -0
  43. package/src/pattern/structure/assertions-pattern.ts +244 -0
  44. package/src/pattern/structure/digest-pattern.ts +225 -0
  45. package/src/pattern/structure/index.ts +272 -0
  46. package/src/pattern/structure/leaf-structure-pattern.ts +85 -0
  47. package/src/pattern/structure/node-pattern.ts +188 -0
  48. package/src/pattern/structure/object-pattern.ts +149 -0
  49. package/src/pattern/structure/obscured-pattern.ts +159 -0
  50. package/src/pattern/structure/predicate-pattern.ts +151 -0
  51. package/src/pattern/structure/subject-pattern.ts +152 -0
  52. package/src/pattern/structure/wrapped-pattern.ts +195 -0
  53. package/src/pattern/vm.ts +1021 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Traverse pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust traverse_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/meta/traverse-pattern
7
+ */
8
+
9
+ import type { Envelope } from "@bcts/envelope";
10
+ import type { Path } from "../../format";
11
+ import type { Matcher } from "../matcher";
12
+ import type { Instr } from "../vm";
13
+ import type { Pattern } from "../index";
14
+
15
+ // Forward declaration for Pattern factory (used for late binding)
16
+ export let createMetaTraversePattern: ((pattern: TraversePattern) => Pattern) | undefined;
17
+
18
+ export function registerTraversePatternFactory(
19
+ factory: (pattern: TraversePattern) => Pattern,
20
+ ): void {
21
+ createMetaTraversePattern = factory;
22
+ }
23
+
24
+ /**
25
+ * A pattern that matches a traversal order of patterns.
26
+ *
27
+ * Corresponds to the Rust `TraversePattern` struct in traverse_pattern.rs
28
+ */
29
+ export class TraversePattern implements Matcher {
30
+ readonly #first: Pattern;
31
+ readonly #rest: TraversePattern | undefined;
32
+
33
+ private constructor(first: Pattern, rest: TraversePattern | undefined) {
34
+ this.#first = first;
35
+ this.#rest = rest;
36
+ }
37
+
38
+ /**
39
+ * Creates a new TraversePattern with the given patterns.
40
+ */
41
+ static new(patterns: Pattern[]): TraversePattern {
42
+ if (patterns.length === 0) {
43
+ throw new Error("TraversePattern requires at least one pattern");
44
+ }
45
+
46
+ const firstPat = patterns[0];
47
+ const restPatterns = patterns.slice(1);
48
+ const rest = restPatterns.length === 0 ? undefined : TraversePattern.new(restPatterns);
49
+ return new TraversePattern(firstPat, rest);
50
+ }
51
+
52
+ /**
53
+ * Gets all patterns in this traversal.
54
+ */
55
+ patterns(): Pattern[] {
56
+ const result: Pattern[] = [this.#first];
57
+ if (this.#rest !== undefined) {
58
+ result.push(...this.#rest.patterns());
59
+ }
60
+ return result;
61
+ }
62
+
63
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
64
+ const firstMatcher = this.#first as unknown as Matcher;
65
+ const headPaths = firstMatcher.paths(haystack);
66
+
67
+ // If there's no further traversal, return head paths
68
+ if (this.#rest === undefined) {
69
+ return [headPaths, new Map<string, Path[]>()];
70
+ }
71
+
72
+ const result: Path[] = [];
73
+ for (const path of headPaths) {
74
+ const lastEnv = path[path.length - 1];
75
+ if (lastEnv !== undefined) {
76
+ // Recursively match the rest of the traversal
77
+ const tailPaths = this.#rest.paths(lastEnv);
78
+ for (const tailPath of tailPaths) {
79
+ const combined = [...path, ...tailPath];
80
+ result.push(combined);
81
+ }
82
+ }
83
+ }
84
+
85
+ return [result, new Map<string, Path[]>()];
86
+ }
87
+
88
+ paths(haystack: Envelope): Path[] {
89
+ return this.pathsWithCaptures(haystack)[0];
90
+ }
91
+
92
+ matches(haystack: Envelope): boolean {
93
+ return this.paths(haystack).length > 0;
94
+ }
95
+
96
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
97
+ // Compile the first pattern
98
+ const firstMatcher = this.#first as unknown as Matcher;
99
+ firstMatcher.compile(code, literals, captures);
100
+
101
+ if (this.#rest !== undefined) {
102
+ // Save the current path and switch to last envelope
103
+ code.push({ type: "ExtendTraversal" });
104
+ // Compile the rest of the traversal
105
+ this.#rest.compile(code, literals, captures);
106
+ // Combine the paths correctly
107
+ code.push({ type: "CombineTraversal" });
108
+ }
109
+ }
110
+
111
+ isComplex(): boolean {
112
+ // A traversal is complex if `first` is complex, or it has more than one pattern
113
+ const firstMatcher = this.#first as unknown as Matcher;
114
+ return firstMatcher.isComplex() || this.#rest !== undefined;
115
+ }
116
+
117
+ toString(): string {
118
+ return this.patterns()
119
+ .map((p) => (p as unknown as { toString(): string }).toString())
120
+ .join(" -> ");
121
+ }
122
+
123
+ /**
124
+ * Equality comparison.
125
+ */
126
+ equals(other: TraversePattern): boolean {
127
+ const thisPatterns = this.patterns();
128
+ const otherPatterns = other.patterns();
129
+ if (thisPatterns.length !== otherPatterns.length) {
130
+ return false;
131
+ }
132
+ for (let i = 0; i < thisPatterns.length; i++) {
133
+ if (thisPatterns[i] !== otherPatterns[i]) {
134
+ return false;
135
+ }
136
+ }
137
+ return true;
138
+ }
139
+
140
+ /**
141
+ * Hash code for use in Maps/Sets.
142
+ */
143
+ hashCode(): number {
144
+ return this.patterns().length;
145
+ }
146
+ }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Assertions pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust assertions_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/structure/assertions-pattern
7
+ */
8
+
9
+ import type { Envelope } from "@bcts/envelope";
10
+ import type { Path } from "../../format";
11
+ import type { Matcher } from "../matcher";
12
+ import type { Instr } from "../vm";
13
+ import type { Pattern } from "../index";
14
+
15
+ // Forward declaration for Pattern factory
16
+ let createStructureAssertionsPattern: ((pattern: AssertionsPattern) => Pattern) | undefined;
17
+
18
+ export function registerAssertionsPatternFactory(
19
+ factory: (pattern: AssertionsPattern) => Pattern,
20
+ ): void {
21
+ createStructureAssertionsPattern = factory;
22
+ }
23
+
24
+ /**
25
+ * Pattern type for assertions pattern matching.
26
+ *
27
+ * Corresponds to the Rust `AssertionsPattern` enum in assertions_pattern.rs
28
+ */
29
+ export type AssertionsPatternType =
30
+ | { readonly type: "Any" }
31
+ | { readonly type: "WithPredicate"; readonly pattern: Pattern }
32
+ | { readonly type: "WithObject"; readonly pattern: Pattern }
33
+ | {
34
+ readonly type: "WithBoth";
35
+ readonly predicatePattern: Pattern;
36
+ readonly objectPattern: Pattern;
37
+ };
38
+
39
+ /**
40
+ * Pattern for matching assertions in envelopes.
41
+ *
42
+ * Corresponds to the Rust `AssertionsPattern` enum in assertions_pattern.rs
43
+ */
44
+ export class AssertionsPattern implements Matcher {
45
+ readonly #pattern: AssertionsPatternType;
46
+
47
+ private constructor(pattern: AssertionsPatternType) {
48
+ this.#pattern = pattern;
49
+ }
50
+
51
+ /**
52
+ * Creates a new AssertionsPattern that matches any assertion.
53
+ */
54
+ static any(): AssertionsPattern {
55
+ return new AssertionsPattern({ type: "Any" });
56
+ }
57
+
58
+ /**
59
+ * Creates a new AssertionsPattern that matches assertions with predicates
60
+ * that match a specific pattern.
61
+ */
62
+ static withPredicate(pattern: Pattern): AssertionsPattern {
63
+ return new AssertionsPattern({ type: "WithPredicate", pattern });
64
+ }
65
+
66
+ /**
67
+ * Creates a new AssertionsPattern that matches assertions with objects
68
+ * that match a specific pattern.
69
+ */
70
+ static withObject(pattern: Pattern): AssertionsPattern {
71
+ return new AssertionsPattern({ type: "WithObject", pattern });
72
+ }
73
+
74
+ /**
75
+ * Creates a new AssertionsPattern that matches assertions with both
76
+ * predicate and object patterns.
77
+ */
78
+ static withBoth(predicatePattern: Pattern, objectPattern: Pattern): AssertionsPattern {
79
+ return new AssertionsPattern({ type: "WithBoth", predicatePattern, objectPattern });
80
+ }
81
+
82
+ /**
83
+ * Gets the pattern type.
84
+ */
85
+ get patternType(): AssertionsPatternType {
86
+ return this.#pattern;
87
+ }
88
+
89
+ /**
90
+ * Gets the predicate pattern if this has one, undefined otherwise.
91
+ */
92
+ predicatePattern(): Pattern | undefined {
93
+ if (this.#pattern.type === "WithPredicate") {
94
+ return this.#pattern.pattern;
95
+ }
96
+ if (this.#pattern.type === "WithBoth") {
97
+ return this.#pattern.predicatePattern;
98
+ }
99
+ return undefined;
100
+ }
101
+
102
+ /**
103
+ * Gets the object pattern if this has one, undefined otherwise.
104
+ */
105
+ objectPattern(): Pattern | undefined {
106
+ if (this.#pattern.type === "WithObject") {
107
+ return this.#pattern.pattern;
108
+ }
109
+ if (this.#pattern.type === "WithBoth") {
110
+ return this.#pattern.objectPattern;
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
116
+ const paths: Path[] = [];
117
+
118
+ for (const assertion of haystack.assertions()) {
119
+ switch (this.#pattern.type) {
120
+ case "Any":
121
+ paths.push([assertion]);
122
+ break;
123
+ case "WithPredicate": {
124
+ const predicate = assertion.asPredicate?.();
125
+ if (predicate !== undefined) {
126
+ const innerMatcher = this.#pattern.pattern as unknown as Matcher;
127
+ if (innerMatcher.matches(predicate)) {
128
+ paths.push([assertion]);
129
+ }
130
+ }
131
+ break;
132
+ }
133
+ case "WithObject": {
134
+ const object = assertion.asObject?.();
135
+ if (object !== undefined) {
136
+ const innerMatcher = this.#pattern.pattern as unknown as Matcher;
137
+ if (innerMatcher.matches(object)) {
138
+ paths.push([assertion]);
139
+ }
140
+ }
141
+ break;
142
+ }
143
+ case "WithBoth": {
144
+ const predicate = assertion.asPredicate?.();
145
+ const object = assertion.asObject?.();
146
+ if (predicate !== undefined && object !== undefined) {
147
+ const predMatcher = this.#pattern.predicatePattern as unknown as Matcher;
148
+ const objMatcher = this.#pattern.objectPattern as unknown as Matcher;
149
+ if (predMatcher.matches(predicate) && objMatcher.matches(object)) {
150
+ paths.push([assertion]);
151
+ }
152
+ }
153
+ break;
154
+ }
155
+ }
156
+ }
157
+
158
+ return [paths, new Map<string, Path[]>()];
159
+ }
160
+
161
+ paths(haystack: Envelope): Path[] {
162
+ return this.pathsWithCaptures(haystack)[0];
163
+ }
164
+
165
+ matches(haystack: Envelope): boolean {
166
+ return this.paths(haystack).length > 0;
167
+ }
168
+
169
+ compile(code: Instr[], literals: Pattern[], _captures: string[]): void {
170
+ if (createStructureAssertionsPattern === undefined) {
171
+ throw new Error("AssertionsPattern factory not registered");
172
+ }
173
+ const idx = literals.length;
174
+ literals.push(createStructureAssertionsPattern(this));
175
+ code.push({ type: "MatchStructure", literalIndex: idx });
176
+ }
177
+
178
+ isComplex(): boolean {
179
+ return false;
180
+ }
181
+
182
+ toString(): string {
183
+ switch (this.#pattern.type) {
184
+ case "Any":
185
+ return "assert";
186
+ case "WithPredicate":
187
+ return `assertpred(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
188
+ case "WithObject":
189
+ return `assertobj(${(this.#pattern.pattern as unknown as { toString(): string }).toString()})`;
190
+ case "WithBoth":
191
+ return `assert(${(this.#pattern.predicatePattern as unknown as { toString(): string }).toString()}, ${(this.#pattern.objectPattern as unknown as { toString(): string }).toString()})`;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Equality comparison.
197
+ */
198
+ equals(other: AssertionsPattern): boolean {
199
+ if (this.#pattern.type !== other.#pattern.type) {
200
+ return false;
201
+ }
202
+ switch (this.#pattern.type) {
203
+ case "Any":
204
+ return true;
205
+ case "WithPredicate":
206
+ case "WithObject": {
207
+ const thisPattern = (
208
+ this.#pattern as { type: "WithPredicate" | "WithObject"; pattern: Pattern }
209
+ ).pattern;
210
+ const otherPattern = (
211
+ other.#pattern as { type: "WithPredicate" | "WithObject"; pattern: Pattern }
212
+ ).pattern;
213
+ return thisPattern === otherPattern;
214
+ }
215
+ case "WithBoth": {
216
+ const otherBoth = other.#pattern as {
217
+ type: "WithBoth";
218
+ predicatePattern: Pattern;
219
+ objectPattern: Pattern;
220
+ };
221
+ return (
222
+ this.#pattern.predicatePattern === otherBoth.predicatePattern &&
223
+ this.#pattern.objectPattern === otherBoth.objectPattern
224
+ );
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Hash code for use in Maps/Sets.
231
+ */
232
+ hashCode(): number {
233
+ switch (this.#pattern.type) {
234
+ case "Any":
235
+ return 0;
236
+ case "WithPredicate":
237
+ return 1;
238
+ case "WithObject":
239
+ return 2;
240
+ case "WithBoth":
241
+ return 3;
242
+ }
243
+ }
244
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Digest pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust digest_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/structure/digest-pattern
7
+ */
8
+
9
+ import type { Envelope, Digest } from "@bcts/envelope";
10
+ import { bytesToHex } from "@bcts/dcbor";
11
+ import type { Path } from "../../format";
12
+ import type { Matcher } from "../matcher";
13
+ import { compileAsAtomic } from "../matcher";
14
+ import type { Instr } from "../vm";
15
+ import type { Pattern } from "../index";
16
+
17
+ // Forward declaration for Pattern factory
18
+ let createStructureDigestPattern: ((pattern: DigestPattern) => Pattern) | undefined;
19
+
20
+ export function registerDigestPatternFactory(factory: (pattern: DigestPattern) => Pattern): void {
21
+ createStructureDigestPattern = factory;
22
+ }
23
+
24
+ /**
25
+ * Helper to convert bytes to Latin-1 string for regex matching.
26
+ */
27
+ function bytesToLatin1(bytes: Uint8Array): string {
28
+ let result = "";
29
+ for (const byte of bytes) {
30
+ result += String.fromCharCode(byte);
31
+ }
32
+ return result;
33
+ }
34
+
35
+ /**
36
+ * Pattern type for digest pattern matching.
37
+ *
38
+ * Corresponds to the Rust `DigestPattern` enum in digest_pattern.rs
39
+ */
40
+ export type DigestPatternType =
41
+ | { readonly type: "Any" }
42
+ | { readonly type: "Digest"; readonly digest: Digest }
43
+ | { readonly type: "Prefix"; readonly prefix: Uint8Array }
44
+ | { readonly type: "BinaryRegex"; readonly regex: RegExp };
45
+
46
+ /**
47
+ * Pattern for matching envelopes by their digest.
48
+ *
49
+ * Corresponds to the Rust `DigestPattern` enum in digest_pattern.rs
50
+ */
51
+ export class DigestPattern implements Matcher {
52
+ readonly #pattern: DigestPatternType;
53
+
54
+ private constructor(pattern: DigestPatternType) {
55
+ this.#pattern = pattern;
56
+ }
57
+
58
+ /**
59
+ * Creates a new DigestPattern that matches any digest.
60
+ */
61
+ static any(): DigestPattern {
62
+ return new DigestPattern({ type: "Any" });
63
+ }
64
+
65
+ /**
66
+ * Creates a new DigestPattern that matches the exact digest.
67
+ */
68
+ static digest(digest: Digest): DigestPattern {
69
+ return new DigestPattern({ type: "Digest", digest });
70
+ }
71
+
72
+ /**
73
+ * Creates a new DigestPattern that matches the prefix of a digest.
74
+ */
75
+ static prefix(prefix: Uint8Array): DigestPattern {
76
+ return new DigestPattern({ type: "Prefix", prefix });
77
+ }
78
+
79
+ /**
80
+ * Creates a new DigestPattern that matches the binary regex for a digest.
81
+ */
82
+ static binaryRegex(regex: RegExp): DigestPattern {
83
+ return new DigestPattern({ type: "BinaryRegex", regex });
84
+ }
85
+
86
+ /**
87
+ * Gets the pattern type.
88
+ */
89
+ get patternType(): DigestPatternType {
90
+ return this.#pattern;
91
+ }
92
+
93
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
94
+ const digest = haystack.digest();
95
+ const digestData = digest.data();
96
+ let isHit = false;
97
+
98
+ switch (this.#pattern.type) {
99
+ case "Any":
100
+ // Any digest matches - every envelope has a digest
101
+ isHit = true;
102
+ break;
103
+ case "Digest":
104
+ isHit = digest.equals(this.#pattern.digest);
105
+ break;
106
+ case "Prefix": {
107
+ const prefix = this.#pattern.prefix;
108
+ if (digestData.length >= prefix.length) {
109
+ isHit = true;
110
+ for (let i = 0; i < prefix.length; i++) {
111
+ if (digestData[i] !== prefix[i]) {
112
+ isHit = false;
113
+ break;
114
+ }
115
+ }
116
+ }
117
+ break;
118
+ }
119
+ case "BinaryRegex": {
120
+ const latin1 = bytesToLatin1(digestData);
121
+ isHit = this.#pattern.regex.test(latin1);
122
+ break;
123
+ }
124
+ }
125
+
126
+ const paths = isHit ? [[haystack]] : [];
127
+ return [paths, new Map<string, Path[]>()];
128
+ }
129
+
130
+ paths(haystack: Envelope): Path[] {
131
+ return this.pathsWithCaptures(haystack)[0];
132
+ }
133
+
134
+ matches(haystack: Envelope): boolean {
135
+ return this.paths(haystack).length > 0;
136
+ }
137
+
138
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
139
+ if (createStructureDigestPattern === undefined) {
140
+ throw new Error("DigestPattern factory not registered");
141
+ }
142
+ compileAsAtomic(createStructureDigestPattern(this), code, literals, captures);
143
+ }
144
+
145
+ isComplex(): boolean {
146
+ return false;
147
+ }
148
+
149
+ toString(): string {
150
+ switch (this.#pattern.type) {
151
+ case "Any":
152
+ return "digest";
153
+ case "Digest":
154
+ return `digest(${this.#pattern.digest.hex()})`;
155
+ case "Prefix":
156
+ return `digest(${bytesToHex(this.#pattern.prefix)})`;
157
+ case "BinaryRegex":
158
+ return `digest(/${this.#pattern.regex.source}/)`;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Equality comparison.
164
+ */
165
+ equals(other: DigestPattern): boolean {
166
+ if (this.#pattern.type !== other.#pattern.type) {
167
+ return false;
168
+ }
169
+ switch (this.#pattern.type) {
170
+ case "Any":
171
+ return true;
172
+ case "Digest":
173
+ return this.#pattern.digest.equals(
174
+ (other.#pattern as { type: "Digest"; digest: Digest }).digest,
175
+ );
176
+ case "Prefix": {
177
+ const thisPrefix = this.#pattern.prefix;
178
+ const otherPrefix = (other.#pattern as { type: "Prefix"; prefix: Uint8Array }).prefix;
179
+ if (thisPrefix.length !== otherPrefix.length) return false;
180
+ for (let i = 0; i < thisPrefix.length; i++) {
181
+ if (thisPrefix[i] !== otherPrefix[i]) return false;
182
+ }
183
+ return true;
184
+ }
185
+ case "BinaryRegex":
186
+ return (
187
+ this.#pattern.regex.source ===
188
+ (other.#pattern as { type: "BinaryRegex"; regex: RegExp }).regex.source
189
+ );
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Hash code for use in Maps/Sets.
195
+ */
196
+ hashCode(): number {
197
+ switch (this.#pattern.type) {
198
+ case "Any":
199
+ return 0;
200
+ case "Digest": {
201
+ // Hash based on first few bytes of digest
202
+ const data = this.#pattern.digest.data().slice(0, 8);
203
+ let hash = 0;
204
+ for (const byte of data) {
205
+ hash = (hash * 31 + byte) | 0;
206
+ }
207
+ return hash;
208
+ }
209
+ case "Prefix": {
210
+ let hash = 0;
211
+ for (const byte of this.#pattern.prefix) {
212
+ hash = (hash * 31 + byte) | 0;
213
+ }
214
+ return hash;
215
+ }
216
+ case "BinaryRegex": {
217
+ let hash = 0;
218
+ for (const char of this.#pattern.regex.source) {
219
+ hash = (hash * 31 + char.charCodeAt(0)) | 0;
220
+ }
221
+ return hash;
222
+ }
223
+ }
224
+ }
225
+ }