@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,110 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Null pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust null_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/leaf/null-pattern
7
+ */
8
+
9
+ import type { Envelope } from "@bcts/envelope";
10
+ import {
11
+ type NullPattern as DCBORNullPattern,
12
+ nullPatternPaths as dcborNullPatternPaths,
13
+ nullPatternDisplay,
14
+ } from "@bcts/dcbor-pattern";
15
+ import type { Path } from "../../format";
16
+ import type { Matcher } from "../matcher";
17
+ import { compileAsAtomic } from "../matcher";
18
+ import type { Instr } from "../vm";
19
+ import type { Pattern } from "../index";
20
+
21
+ // Forward declaration for Pattern factory
22
+ let createLeafNullPattern: ((pattern: NullPattern) => Pattern) | undefined;
23
+
24
+ export function registerNullPatternFactory(factory: (pattern: NullPattern) => Pattern): void {
25
+ createLeafNullPattern = factory;
26
+ }
27
+
28
+ /**
29
+ * Pattern for matching null values.
30
+ *
31
+ * This is a wrapper around dcbor_pattern::NullPattern that provides
32
+ * envelope-specific functionality.
33
+ *
34
+ * Corresponds to the Rust `NullPattern` struct in null_pattern.rs
35
+ */
36
+ export class NullPattern implements Matcher {
37
+ readonly #inner: DCBORNullPattern;
38
+ static readonly #instance = new NullPattern();
39
+
40
+ private constructor() {
41
+ // Create the NullPattern directly - it's just { variant: "Null" }
42
+ this.#inner = { variant: "Null" };
43
+ }
44
+
45
+ /**
46
+ * Creates a new NullPattern (returns singleton).
47
+ */
48
+ static new(): NullPattern {
49
+ return NullPattern.#instance;
50
+ }
51
+
52
+ /**
53
+ * Gets the underlying dcbor-pattern NullPattern.
54
+ */
55
+ get inner(): DCBORNullPattern {
56
+ return this.#inner;
57
+ }
58
+
59
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
60
+ // For leaf envelopes, extract the CBOR and delegate to dcbor-pattern
61
+ const cbor = haystack.asLeaf();
62
+ if (cbor !== undefined) {
63
+ // Delegate to dcbor-pattern for CBOR matching
64
+ const dcborPaths = dcborNullPatternPaths(this.#inner, cbor);
65
+
66
+ if (dcborPaths.length > 0) {
67
+ return [[[haystack]], new Map<string, Path[]>()];
68
+ }
69
+ }
70
+
71
+ return [[], new Map<string, Path[]>()];
72
+ }
73
+
74
+ paths(haystack: Envelope): Path[] {
75
+ return this.pathsWithCaptures(haystack)[0];
76
+ }
77
+
78
+ matches(haystack: Envelope): boolean {
79
+ return this.paths(haystack).length > 0;
80
+ }
81
+
82
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
83
+ if (createLeafNullPattern === undefined) {
84
+ throw new Error("NullPattern factory not registered");
85
+ }
86
+ compileAsAtomic(createLeafNullPattern(this), code, literals, captures);
87
+ }
88
+
89
+ isComplex(): boolean {
90
+ return false;
91
+ }
92
+
93
+ toString(): string {
94
+ return nullPatternDisplay(this.#inner);
95
+ }
96
+
97
+ /**
98
+ * Equality comparison.
99
+ */
100
+ equals(other: NullPattern): boolean {
101
+ return other instanceof NullPattern;
102
+ }
103
+
104
+ /**
105
+ * Hash code for use in Maps/Sets.
106
+ */
107
+ hashCode(): number {
108
+ return 0; // All NullPatterns are equal
109
+ }
110
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Number pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust number_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/leaf/number-pattern
7
+ */
8
+
9
+ import type { Envelope } from "@bcts/envelope";
10
+ import {
11
+ type NumberPattern as DCBORNumberPattern,
12
+ numberPatternAny,
13
+ numberPatternValue,
14
+ numberPatternRange,
15
+ numberPatternGreaterThan,
16
+ numberPatternGreaterThanOrEqual,
17
+ numberPatternLessThan,
18
+ numberPatternLessThanOrEqual,
19
+ numberPatternNaN,
20
+ numberPatternInfinity,
21
+ numberPatternNegInfinity,
22
+ numberPatternPaths as dcborNumberPatternPaths,
23
+ numberPatternDisplay,
24
+ } from "@bcts/dcbor-pattern";
25
+ import type { Path } from "../../format";
26
+ import type { Matcher } from "../matcher";
27
+ import { compileAsAtomic } from "../matcher";
28
+ import type { Instr } from "../vm";
29
+ import type { Pattern } from "../index";
30
+
31
+ // Forward declaration for Pattern factory
32
+ let createLeafNumberPattern: ((pattern: NumberPattern) => Pattern) | undefined;
33
+
34
+ export function registerNumberPatternFactory(factory: (pattern: NumberPattern) => Pattern): void {
35
+ createLeafNumberPattern = factory;
36
+ }
37
+
38
+ /**
39
+ * Pattern for matching number values.
40
+ *
41
+ * This is a wrapper around dcbor_pattern::NumberPattern that provides
42
+ * envelope-specific integration.
43
+ *
44
+ * Corresponds to the Rust `NumberPattern` struct in number_pattern.rs
45
+ */
46
+ export class NumberPattern implements Matcher {
47
+ readonly #inner: DCBORNumberPattern;
48
+
49
+ private constructor(inner: DCBORNumberPattern) {
50
+ this.#inner = inner;
51
+ }
52
+
53
+ /**
54
+ * Creates a new NumberPattern that matches any number.
55
+ */
56
+ static any(): NumberPattern {
57
+ return new NumberPattern(numberPatternAny());
58
+ }
59
+
60
+ /**
61
+ * Creates a new NumberPattern that matches the exact number.
62
+ */
63
+ static exact(value: number): NumberPattern {
64
+ return new NumberPattern(numberPatternValue(value));
65
+ }
66
+
67
+ /**
68
+ * Creates a new NumberPattern that matches numbers within the specified range.
69
+ */
70
+ static range(min: number, max: number): NumberPattern {
71
+ return new NumberPattern(numberPatternRange(min, max));
72
+ }
73
+
74
+ /**
75
+ * Creates a new NumberPattern that matches numbers greater than the specified value.
76
+ */
77
+ static greaterThan(value: number): NumberPattern {
78
+ return new NumberPattern(numberPatternGreaterThan(value));
79
+ }
80
+
81
+ /**
82
+ * Creates a new NumberPattern that matches numbers greater than or equal to the specified value.
83
+ */
84
+ static greaterThanOrEqual(value: number): NumberPattern {
85
+ return new NumberPattern(numberPatternGreaterThanOrEqual(value));
86
+ }
87
+
88
+ /**
89
+ * Creates a new NumberPattern that matches numbers less than the specified value.
90
+ */
91
+ static lessThan(value: number): NumberPattern {
92
+ return new NumberPattern(numberPatternLessThan(value));
93
+ }
94
+
95
+ /**
96
+ * Creates a new NumberPattern that matches numbers less than or equal to the specified value.
97
+ */
98
+ static lessThanOrEqual(value: number): NumberPattern {
99
+ return new NumberPattern(numberPatternLessThanOrEqual(value));
100
+ }
101
+
102
+ /**
103
+ * Creates a new NumberPattern that matches NaN values.
104
+ */
105
+ static nan(): NumberPattern {
106
+ return new NumberPattern(numberPatternNaN());
107
+ }
108
+
109
+ /**
110
+ * Creates a new NumberPattern that matches positive infinity.
111
+ */
112
+ static infinity(): NumberPattern {
113
+ return new NumberPattern(numberPatternInfinity());
114
+ }
115
+
116
+ /**
117
+ * Creates a new NumberPattern that matches negative infinity.
118
+ */
119
+ static negInfinity(): NumberPattern {
120
+ return new NumberPattern(numberPatternNegInfinity());
121
+ }
122
+
123
+ /**
124
+ * Creates a new NumberPattern from a dcbor-pattern NumberPattern.
125
+ */
126
+ static fromDcborPattern(dcborPattern: DCBORNumberPattern): NumberPattern {
127
+ return new NumberPattern(dcborPattern);
128
+ }
129
+
130
+ /**
131
+ * Gets the underlying dcbor-pattern NumberPattern.
132
+ */
133
+ get inner(): DCBORNumberPattern {
134
+ return this.#inner;
135
+ }
136
+
137
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
138
+ // For leaf envelopes, extract the CBOR and delegate to dcbor-pattern
139
+ const cbor = haystack.asLeaf();
140
+ if (cbor !== undefined) {
141
+ // Delegate to dcbor-pattern for CBOR matching
142
+ const dcborPaths = dcborNumberPatternPaths(this.#inner, cbor);
143
+
144
+ // For simple leaf patterns, if dcbor-pattern found matches, return the envelope
145
+ if (dcborPaths.length > 0) {
146
+ const envelopePaths: Path[] = [[haystack]];
147
+ return [envelopePaths, new Map<string, Path[]>()];
148
+ }
149
+ }
150
+
151
+ return [[], new Map<string, Path[]>()];
152
+ }
153
+
154
+ paths(haystack: Envelope): Path[] {
155
+ return this.pathsWithCaptures(haystack)[0];
156
+ }
157
+
158
+ matches(haystack: Envelope): boolean {
159
+ return this.paths(haystack).length > 0;
160
+ }
161
+
162
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
163
+ if (createLeafNumberPattern === undefined) {
164
+ throw new Error("NumberPattern factory not registered");
165
+ }
166
+ compileAsAtomic(createLeafNumberPattern(this), code, literals, captures);
167
+ }
168
+
169
+ isComplex(): boolean {
170
+ return false;
171
+ }
172
+
173
+ toString(): string {
174
+ return numberPatternDisplay(this.#inner);
175
+ }
176
+
177
+ /**
178
+ * Equality comparison.
179
+ */
180
+ equals(other: NumberPattern): boolean {
181
+ // Compare by variant and values
182
+ if (this.#inner.variant !== other.#inner.variant) {
183
+ return false;
184
+ }
185
+ switch (this.#inner.variant) {
186
+ case "Any":
187
+ case "NaN":
188
+ case "Infinity":
189
+ case "NegInfinity":
190
+ return true;
191
+ case "Value":
192
+ case "GreaterThan":
193
+ case "GreaterThanOrEqual":
194
+ case "LessThan":
195
+ case "LessThanOrEqual":
196
+ return (
197
+ (this.#inner as { value: number }).value === (other.#inner as { value: number }).value
198
+ );
199
+ case "Range":
200
+ return (
201
+ (this.#inner as { min: number; max: number }).min ===
202
+ (other.#inner as { min: number; max: number }).min &&
203
+ (this.#inner as { min: number; max: number }).max ===
204
+ (other.#inner as { min: number; max: number }).max
205
+ );
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Hash code for use in Maps/Sets.
211
+ */
212
+ hashCode(): number {
213
+ let hash = 0;
214
+ switch (this.#inner.variant) {
215
+ case "Any":
216
+ hash = 1;
217
+ break;
218
+ case "Value":
219
+ hash = 2 * 31 + (this.#inner as { value: number }).value;
220
+ break;
221
+ case "Range":
222
+ hash = 3 * 31 + (this.#inner as { min: number }).min + (this.#inner as { max: number }).max;
223
+ break;
224
+ case "GreaterThan":
225
+ hash = 4 * 31 + (this.#inner as { value: number }).value;
226
+ break;
227
+ case "GreaterThanOrEqual":
228
+ hash = 5 * 31 + (this.#inner as { value: number }).value;
229
+ break;
230
+ case "LessThan":
231
+ hash = 6 * 31 + (this.#inner as { value: number }).value;
232
+ break;
233
+ case "LessThanOrEqual":
234
+ hash = 7 * 31 + (this.#inner as { value: number }).value;
235
+ break;
236
+ case "NaN":
237
+ hash = 8;
238
+ break;
239
+ case "Infinity":
240
+ hash = 9;
241
+ break;
242
+ case "NegInfinity":
243
+ hash = 10;
244
+ break;
245
+ }
246
+ return hash;
247
+ }
248
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * @bcts/envelope-pattern - Tagged pattern matching
3
+ *
4
+ * This is a 1:1 TypeScript port of bc-envelope-pattern-rust tagged_pattern.rs
5
+ *
6
+ * @module envelope-pattern/pattern/leaf/tagged-pattern
7
+ */
8
+
9
+ import { Envelope } from "@bcts/envelope";
10
+ import type { Tag, Cbor } from "@bcts/dcbor";
11
+ import {
12
+ type TaggedPattern as DCBORTaggedPattern,
13
+ type Pattern as DCBORPattern,
14
+ taggedPatternAny,
15
+ taggedPatternWithTag,
16
+ taggedPatternWithName,
17
+ taggedPatternWithRegex,
18
+ taggedPatternPathsWithCaptures,
19
+ taggedPatternDisplay,
20
+ patternDisplay,
21
+ } from "@bcts/dcbor-pattern";
22
+ import type { Path } from "../../format";
23
+ import type { Matcher } from "../matcher";
24
+ import { compileAsAtomic } from "../matcher";
25
+ import type { Instr } from "../vm";
26
+ import type { Pattern } from "../index";
27
+
28
+ // Forward declaration for Pattern factory
29
+ let createLeafTaggedPattern: ((pattern: TaggedPattern) => Pattern) | undefined;
30
+
31
+ export function registerTaggedPatternFactory(factory: (pattern: TaggedPattern) => Pattern): void {
32
+ createLeafTaggedPattern = factory;
33
+ }
34
+
35
+ /**
36
+ * Pattern for matching tagged CBOR values.
37
+ *
38
+ * This is a wrapper around dcbor_pattern::TaggedPattern that provides
39
+ * envelope-specific integration.
40
+ *
41
+ * Corresponds to the Rust `TaggedPattern` struct in tagged_pattern.rs
42
+ */
43
+ export class TaggedPattern implements Matcher {
44
+ readonly #inner: DCBORTaggedPattern;
45
+
46
+ private constructor(inner: DCBORTaggedPattern) {
47
+ this.#inner = inner;
48
+ }
49
+
50
+ /**
51
+ * Creates a new TaggedPattern that matches any tagged value.
52
+ */
53
+ static any(): TaggedPattern {
54
+ return new TaggedPattern(taggedPatternAny());
55
+ }
56
+
57
+ /**
58
+ * Creates a new TaggedPattern with a specific tag and content pattern.
59
+ */
60
+ static withTag(tag: Tag, pattern: DCBORPattern): TaggedPattern {
61
+ return new TaggedPattern(taggedPatternWithTag(tag, pattern));
62
+ }
63
+
64
+ /**
65
+ * Creates a new TaggedPattern with a specific tag name and content pattern.
66
+ */
67
+ static withName(name: string, pattern: DCBORPattern): TaggedPattern {
68
+ return new TaggedPattern(taggedPatternWithName(name, pattern));
69
+ }
70
+
71
+ /**
72
+ * Creates a new TaggedPattern with a tag name matching regex and content pattern.
73
+ */
74
+ static withRegex(regex: RegExp, pattern: DCBORPattern): TaggedPattern {
75
+ return new TaggedPattern(taggedPatternWithRegex(regex, pattern));
76
+ }
77
+
78
+ /**
79
+ * Creates a new TaggedPattern from a dcbor-pattern TaggedPattern.
80
+ */
81
+ static fromDcborPattern(dcborPattern: DCBORTaggedPattern): TaggedPattern {
82
+ return new TaggedPattern(dcborPattern);
83
+ }
84
+
85
+ /**
86
+ * Gets the underlying dcbor-pattern TaggedPattern.
87
+ */
88
+ get inner(): DCBORTaggedPattern {
89
+ return this.#inner;
90
+ }
91
+
92
+ pathsWithCaptures(haystack: Envelope): [Path[], Map<string, Path[]>] {
93
+ // Try to extract CBOR from the envelope
94
+ const subject = haystack.subject();
95
+ const cbor = subject.asLeaf();
96
+
97
+ if (cbor !== undefined) {
98
+ // Delegate to dcbor-pattern for CBOR matching
99
+ const [dcborPaths, dcborCaptures] = taggedPatternPathsWithCaptures(this.#inner, cbor);
100
+
101
+ if (dcborPaths.length > 0) {
102
+ // Convert dcbor paths to envelope paths
103
+ const envelopePaths: Path[] = dcborPaths.map((dcborPath: Cbor[]) => {
104
+ const envPath: Path = [haystack];
105
+ // Skip the first element (root) and convert rest to envelopes
106
+ for (let i = 1; i < dcborPath.length; i++) {
107
+ const elem = dcborPath[i];
108
+ if (elem !== undefined) {
109
+ envPath.push(Envelope.newLeaf(elem));
110
+ }
111
+ }
112
+ return envPath;
113
+ });
114
+
115
+ // Convert dcbor captures to envelope captures
116
+ const envelopeCaptures = new Map<string, Path[]>();
117
+ for (const [name, paths] of dcborCaptures) {
118
+ const envCapturePaths: Path[] = paths.map((dcborPath: Cbor[]) => {
119
+ const envPath: Path = [haystack];
120
+ for (let i = 1; i < dcborPath.length; i++) {
121
+ const elem = dcborPath[i];
122
+ if (elem !== undefined) {
123
+ envPath.push(Envelope.newLeaf(elem));
124
+ }
125
+ }
126
+ return envPath;
127
+ });
128
+ envelopeCaptures.set(name, envCapturePaths);
129
+ }
130
+
131
+ return [envelopePaths, envelopeCaptures];
132
+ }
133
+ }
134
+
135
+ return [[], new Map<string, Path[]>()];
136
+ }
137
+
138
+ paths(haystack: Envelope): Path[] {
139
+ return this.pathsWithCaptures(haystack)[0];
140
+ }
141
+
142
+ matches(haystack: Envelope): boolean {
143
+ return this.paths(haystack).length > 0;
144
+ }
145
+
146
+ compile(code: Instr[], literals: Pattern[], captures: string[]): void {
147
+ if (createLeafTaggedPattern === undefined) {
148
+ throw new Error("TaggedPattern factory not registered");
149
+ }
150
+ compileAsAtomic(createLeafTaggedPattern(this), code, literals, captures);
151
+ }
152
+
153
+ isComplex(): boolean {
154
+ return false;
155
+ }
156
+
157
+ toString(): string {
158
+ return taggedPatternDisplay(this.#inner, patternDisplay);
159
+ }
160
+
161
+ /**
162
+ * Equality comparison.
163
+ */
164
+ equals(other: TaggedPattern): boolean {
165
+ // Compare by variant type and values
166
+ if (this.#inner.variant !== other.#inner.variant) {
167
+ return false;
168
+ }
169
+ switch (this.#inner.variant) {
170
+ case "Any":
171
+ return true;
172
+ case "Tag": {
173
+ const otherTag = other.#inner as { variant: "Tag"; tag: Tag; pattern: DCBORPattern };
174
+ return (
175
+ this.#inner.tag.value === otherTag.tag.value &&
176
+ patternDisplay(this.#inner.pattern) === patternDisplay(otherTag.pattern)
177
+ );
178
+ }
179
+ case "Name": {
180
+ const otherName = other.#inner as { variant: "Name"; name: string; pattern: DCBORPattern };
181
+ return (
182
+ this.#inner.name === otherName.name &&
183
+ patternDisplay(this.#inner.pattern) === patternDisplay(otherName.pattern)
184
+ );
185
+ }
186
+ case "Regex": {
187
+ const otherRegex = other.#inner as {
188
+ variant: "Regex";
189
+ regex: RegExp;
190
+ pattern: DCBORPattern;
191
+ };
192
+ return (
193
+ this.#inner.regex.source === otherRegex.regex.source &&
194
+ patternDisplay(this.#inner.pattern) === patternDisplay(otherRegex.pattern)
195
+ );
196
+ }
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Hash code for use in Maps/Sets.
202
+ */
203
+ hashCode(): number {
204
+ switch (this.#inner.variant) {
205
+ case "Any":
206
+ return 0;
207
+ case "Tag":
208
+ return Number(BigInt(this.#inner.tag.value) & BigInt(0xffffffff));
209
+ case "Name":
210
+ return simpleStringHash(this.#inner.name);
211
+ case "Regex":
212
+ return simpleStringHash(this.#inner.regex.source);
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Simple string hash function for hashCode implementations.
219
+ */
220
+ function simpleStringHash(str: string): number {
221
+ let hash = 0;
222
+ for (let i = 0; i < str.length; i++) {
223
+ const char = str.charCodeAt(i);
224
+ hash = (hash << 5) - hash + char;
225
+ hash = hash & hash; // Convert to 32-bit integer
226
+ }
227
+ return hash;
228
+ }