rapid-fuzzy 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -69,6 +69,9 @@ search('app', items, 5);
69
69
  const [match] = search('hlo', ['hello world'], { includePositions: true });
70
70
  // → { item: 'hello world', score: 0.75, index: 0, positions: [0, 2, 4] }
71
71
 
72
+ // Case-sensitive matching (default: smart case)
73
+ search('Type', items, { isCaseSensitive: true });
74
+
72
75
  // Find the single best match
73
76
  closest('tsc', ['TypeScript', 'JavaScript', 'Python']);
74
77
  // → 'TypeScript'
@@ -78,6 +81,59 @@ closest('xyz', items, 0.5);
78
81
  // → null
79
82
  ```
80
83
 
84
+ ### Object Search
85
+
86
+ Search across object properties with weighted keys — a drop-in replacement for fuse.js's `keys` option:
87
+
88
+ ```typescript
89
+ import { searchObjects } from 'rapid-fuzzy';
90
+
91
+ const users = [
92
+ { name: 'John Smith', email: 'john@example.com' },
93
+ { name: 'Jane Doe', email: 'jane@example.com' },
94
+ { name: 'Bob Johnson', email: 'bob@test.com' },
95
+ ];
96
+
97
+ // Search across multiple keys
98
+ const results = searchObjects('john', users, {
99
+ keys: ['name', 'email'],
100
+ });
101
+ // → [{ item: { name: 'John Smith', ... }, score: 0.95, keyScores: [0.98, 0.85], index: 0 }]
102
+
103
+ // Weighted keys — prioritize name matches over email
104
+ searchObjects('john', users, {
105
+ keys: [
106
+ { name: 'name', weight: 2.0 },
107
+ { name: 'email', weight: 1.0 },
108
+ ],
109
+ });
110
+
111
+ // Nested key paths
112
+ searchObjects('new york', items, { keys: ['address.city'] });
113
+ ```
114
+
115
+ ### Match Highlighting
116
+
117
+ Convert matched positions into highlighted markup for UI rendering:
118
+
119
+ ```typescript
120
+ import { search, highlight, highlightRanges } from 'rapid-fuzzy';
121
+
122
+ const results = search('fzy', ['fuzzy'], { includePositions: true });
123
+ const { item, positions } = results[0];
124
+
125
+ // String markers
126
+ highlight(item, positions, '<b>', '</b>');
127
+ // → '<b>f</b>u<b>zy</b>'
128
+
129
+ // Callback (React, JSX, custom DOM)
130
+ highlight(item, positions, (matched) => `<mark>${matched}</mark>`);
131
+
132
+ // Raw ranges for custom rendering
133
+ highlightRanges(item, positions);
134
+ // → [{ start: 0, end: 1, matched: true }, { start: 1, end: 2, matched: false }, ...]
135
+ ```
136
+
81
137
  ### Token-Based Matching
82
138
 
83
139
  Order-independent and partial string matching, inspired by Python's [RapidFuzz](https://github.com/rapidfuzz/RapidFuzz):
@@ -199,8 +255,10 @@ cargo bench # Rust internal benchmarks
199
255
  |---|---|---|---|---|
200
256
  | **Algorithms** | Levenshtein, Jaro-Winkler, Sorensen-Dice, Damerau-Levenshtein, token sort/set, partial ratio, fuzzy search | Bitap-based fuzzy | Levenshtein only | Substring fuzzy |
201
257
  | **Runtime** | Rust (native + WASM) | Pure JS | Pure JS | Pure JS |
258
+ | **Object search** | Yes (searchObjects with weighted keys) | Yes (keys option) | No | Yes (keys) |
202
259
  | **Score threshold** | Yes (minScore) | Yes (threshold) | No | Yes (threshold) |
203
260
  | **Match positions** | Yes (includePositions) | Yes | No | Yes |
261
+ | **Highlight utility** | Yes (highlight, highlightRanges) | No (manual) | No | Yes (highlight) |
204
262
  | **Batch API** | Yes | No | No | No |
205
263
  | **Node.js native** | Yes (napi-rs) | No | No | No |
206
264
  | **Browser support** | Yes (WASM) | Yes | Yes | Yes |
@@ -211,7 +269,7 @@ cargo bench # Rust internal benchmarks
211
269
  Switching from another library? These guides provide API mapping tables, code examples, and performance comparisons:
212
270
 
213
271
  - [**From string-similarity**](docs/migration/from-string-similarity.md) — Same Dice coefficient algorithm, now maintained and faster
214
- - [**From fuse.js**](docs/migration/from-fuse-js.md) — 1341x faster fuzzy search with a simpler API
272
+ - [**From fuse.js**](docs/migration/from-fuse-js.md) — 1740x faster fuzzy search with a simpler API
215
273
  - [**From leven / fastest-levenshtein**](docs/migration/from-leven.md) — Multi-algorithm upgrade with batch APIs
216
274
 
217
275
  ## License
package/browser.js CHANGED
@@ -1 +1,4 @@
1
1
  export * from 'rapid-fuzzy-wasm32-wasi'
2
+
3
+ // --- JS utilities (appended by scripts/patch-binding.js) ---
4
+ export { highlight, highlightRanges } from './highlight.mjs';
package/highlight.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ /** A range within a string, indicating whether it was matched. */
2
+ export interface HighlightRange {
3
+ /** Start index (inclusive). */
4
+ start: number;
5
+ /** End index (exclusive). */
6
+ end: number;
7
+ /** Whether this range was part of the match. */
8
+ matched: boolean;
9
+ }
10
+
11
+ /**
12
+ * Highlight matched characters in a search result string.
13
+ *
14
+ * Use with `SearchResult.positions` from a search with `includePositions: true`.
15
+ *
16
+ * @example String markers
17
+ * ```typescript
18
+ * const results = search('fzy', ['fuzzy'], { includePositions: true });
19
+ * highlight(results[0].item, results[0].positions, '<b>', '</b>');
20
+ * // → '<b>f</b>u<b>zy</b>'
21
+ * ```
22
+ *
23
+ * @example Callback (React, custom DOM, etc.)
24
+ * ```typescript
25
+ * highlight(result.item, result.positions, (matched) => `<mark>${matched}</mark>`);
26
+ * ```
27
+ */
28
+ export declare function highlight(
29
+ item: string,
30
+ positions: Array<number>,
31
+ open: string,
32
+ close: string,
33
+ ): string;
34
+ export declare function highlight(
35
+ item: string,
36
+ positions: Array<number>,
37
+ callback: (matched: string) => string,
38
+ ): string;
39
+
40
+ /**
41
+ * Convert matched positions into an array of ranges for custom rendering.
42
+ *
43
+ * Each range indicates a contiguous segment of the string and whether it was
44
+ * part of the match. Useful for building custom highlight components.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const ranges = highlightRanges(result.item, result.positions);
49
+ * // → [{ start: 0, end: 1, matched: true }, { start: 1, end: 2, matched: false }, ...]
50
+ * ```
51
+ */
52
+ export declare function highlightRanges(
53
+ item: string,
54
+ positions: Array<number>,
55
+ ): Array<HighlightRange>;
package/highlight.js ADDED
@@ -0,0 +1,58 @@
1
+ // Pure JS highlight utilities — works in both Node.js and browser environments.
2
+ // This file is manually maintained (not auto-generated by napi-rs).
3
+
4
+ 'use strict';
5
+
6
+ /**
7
+ * @param {string} item
8
+ * @param {number[]} positions
9
+ * @returns {Array<{start: number, end: number, matched: boolean}>}
10
+ */
11
+ function highlightRanges(item, positions) {
12
+ if (!item) return [];
13
+ if (!positions || positions.length === 0) {
14
+ return [{ start: 0, end: item.length, matched: false }];
15
+ }
16
+
17
+ const set = new Set(positions);
18
+ const ranges = [];
19
+ let i = 0;
20
+
21
+ while (i < item.length) {
22
+ const matched = set.has(i);
23
+ const start = i;
24
+ while (i < item.length && set.has(i) === matched) i++;
25
+ ranges.push({ start, end: i, matched });
26
+ }
27
+
28
+ return ranges;
29
+ }
30
+
31
+ /**
32
+ * @param {string} item
33
+ * @param {number[]} positions
34
+ * @param {string | ((substring: string) => string)} openOrCallback
35
+ * @param {string} [close]
36
+ * @returns {string}
37
+ */
38
+ function highlight(item, positions, openOrCallback, close) {
39
+ if (!positions || positions.length === 0) return item;
40
+
41
+ const ranges = highlightRanges(item, positions);
42
+ const useCallback = typeof openOrCallback === 'function';
43
+
44
+ const parts = [];
45
+ for (const range of ranges) {
46
+ const segment = item.slice(range.start, range.end);
47
+ if (range.matched) {
48
+ parts.push(useCallback ? openOrCallback(segment) : openOrCallback + segment + (close ?? ''));
49
+ } else {
50
+ parts.push(segment);
51
+ }
52
+ }
53
+
54
+ return parts.join('');
55
+ }
56
+
57
+ module.exports.highlight = highlight;
58
+ module.exports.highlightRanges = highlightRanges;
package/highlight.mjs ADDED
@@ -0,0 +1,57 @@
1
+ // ESM version of highlight utilities — for browser bundlers and ESM-only environments.
2
+ // Keep in sync with highlight.js (CJS version).
3
+
4
+ /**
5
+ * Convert matched positions into an array of ranges for custom rendering.
6
+ *
7
+ * @param {string} item - The original string from the search result.
8
+ * @param {number[]} positions - Array of matched character indices.
9
+ * @returns {Array<{start: number, end: number, matched: boolean}>} Array of ranges.
10
+ */
11
+ export function highlightRanges(item, positions) {
12
+ if (!item) return [];
13
+ if (!positions || positions.length === 0) {
14
+ return [{ start: 0, end: item.length, matched: false }];
15
+ }
16
+
17
+ const set = new Set(positions);
18
+ const ranges = [];
19
+ let i = 0;
20
+
21
+ while (i < item.length) {
22
+ const matched = set.has(i);
23
+ const start = i;
24
+ while (i < item.length && set.has(i) === matched) i++;
25
+ ranges.push({ start, end: i, matched });
26
+ }
27
+
28
+ return ranges;
29
+ }
30
+
31
+ /**
32
+ * Highlight matched characters in a search result string.
33
+ *
34
+ * @param {string} item - The original string from the search result.
35
+ * @param {number[]} positions - Array of matched character indices.
36
+ * @param {string | ((substring: string) => string)} openOrCallback - Opening tag or callback.
37
+ * @param {string} [close] - Closing tag (required when openOrCallback is a string).
38
+ * @returns {string} The highlighted string.
39
+ */
40
+ export function highlight(item, positions, openOrCallback, close) {
41
+ if (!positions || positions.length === 0) return item;
42
+
43
+ const ranges = highlightRanges(item, positions);
44
+ const useCallback = typeof openOrCallback === 'function';
45
+
46
+ const parts = [];
47
+ for (const range of ranges) {
48
+ const segment = item.slice(range.start, range.end);
49
+ if (range.matched) {
50
+ parts.push(useCallback ? openOrCallback(segment) : openOrCallback + segment + (close ?? ''));
51
+ } else {
52
+ parts.push(segment);
53
+ }
54
+ }
55
+
56
+ return parts.join('');
57
+ }
package/index.d.mts CHANGED
@@ -1 +1,5 @@
1
1
  export * from './index.d.ts';
2
+ export { highlight, highlightRanges } from './highlight.d.ts';
3
+ export type { HighlightRange } from './highlight.d.ts';
4
+ export { searchObjects } from './objects';
5
+ export type { KeyConfig, ObjectSearchOptions, ObjectSearchResult } from './objects';
package/index.d.ts CHANGED
@@ -114,6 +114,19 @@ export declare function jaroWinklerBatch(pairs: Array<Array<string>>): Array<num
114
114
  */
115
115
  export declare function jaroWinklerMany(reference: string, candidates: Array<string>): Array<number>
116
116
 
117
+ /** A single result from multi-key fuzzy search. */
118
+ export interface KeySearchResult {
119
+ /** The index of the item in the original input array. */
120
+ index: number
121
+ /** The combined weighted score normalized to 0.0-1.0 range. */
122
+ score: number
123
+ /**
124
+ * Per-key scores in the same order as the input keys.
125
+ * A score of 0.0 means the item did not match on that key.
126
+ */
127
+ keyScores: Array<number>
128
+ }
129
+
117
130
  /**
118
131
  * Compute the Levenshtein distance between two strings.
119
132
  *
@@ -196,6 +209,17 @@ export declare function partialRatioMany(reference: string, candidates: Array<st
196
209
  */
197
210
  export declare function search(query: string, items: Array<string>, options?: number | SearchOptions | undefined | null): Array<SearchResult>
198
211
 
212
+ /**
213
+ * Perform fuzzy search across multiple text keys with weights.
214
+ *
215
+ * `key_texts[k]` is an array of strings for key `k`, one per item.
216
+ * All inner arrays must have the same length (the number of items).
217
+ * `weights` specifies the relative importance of each key.
218
+ *
219
+ * Returns results sorted by combined weighted score (best match first).
220
+ */
221
+ export declare function searchKeys(query: string, keyTexts: Array<Array<string>>, weights: Array<number>, options?: SearchOptions | undefined | null): Array<KeySearchResult>
222
+
199
223
  /** Options for the search function. */
200
224
  export interface SearchOptions {
201
225
  /** Maximum number of results to return. */
@@ -204,6 +228,11 @@ export interface SearchOptions {
204
228
  minScore?: number
205
229
  /** If true, include matched character positions in results. */
206
230
  includePositions?: boolean
231
+ /**
232
+ * If true, matching is case-sensitive. Default is smart case
233
+ * (case-insensitive unless the query contains uppercase characters).
234
+ */
235
+ isCaseSensitive?: boolean
207
236
  }
208
237
 
209
238
  /** A single fuzzy search result with the matched item and its score. */
@@ -314,3 +343,6 @@ export declare function weightedRatioBatch(pairs: Array<Array<string>>): Array<n
314
343
  * Returns an array of similarity scores, one per candidate, in the same order as the input.
315
344
  */
316
345
  export declare function weightedRatioMany(reference: string, candidates: Array<string>): Array<number>
346
+
347
+ // --- JS utilities (appended by scripts/patch-binding.js) ---
348
+ export { highlight, highlightRanges, HighlightRange } from './highlight';
package/index.js CHANGED
@@ -597,6 +597,7 @@ module.exports.partialRatio = nativeBinding.partialRatio
597
597
  module.exports.partialRatioBatch = nativeBinding.partialRatioBatch
598
598
  module.exports.partialRatioMany = nativeBinding.partialRatioMany
599
599
  module.exports.search = nativeBinding.search
600
+ module.exports.searchKeys = nativeBinding.searchKeys
600
601
  module.exports.sorensenDice = nativeBinding.sorensenDice
601
602
  module.exports.sorensenDiceBatch = nativeBinding.sorensenDiceBatch
602
603
  module.exports.sorensenDiceMany = nativeBinding.sorensenDiceMany
@@ -609,3 +610,8 @@ module.exports.tokenSortRatioMany = nativeBinding.tokenSortRatioMany
609
610
  module.exports.weightedRatio = nativeBinding.weightedRatio
610
611
  module.exports.weightedRatioBatch = nativeBinding.weightedRatioBatch
611
612
  module.exports.weightedRatioMany = nativeBinding.weightedRatioMany
613
+
614
+ // --- JS utilities (appended by scripts/patch-binding.js) ---
615
+ const _hl = require('./highlight.js');
616
+ module.exports.highlight = _hl.highlight;
617
+ module.exports.highlightRanges = _hl.highlightRanges;
package/index.mjs CHANGED
@@ -6,6 +6,7 @@ const binding = require('./index.js');
6
6
  export const {
7
7
  FuzzyIndex,
8
8
  closest,
9
+ searchKeys,
9
10
  damerauLevenshtein,
10
11
  damerauLevenshteinBatch,
11
12
  damerauLevenshteinMany,
@@ -37,4 +38,8 @@ export const {
37
38
  weightedRatio,
38
39
  weightedRatioBatch,
39
40
  weightedRatioMany,
40
- } = binding;
41
+ highlight,
42
+ highlightRanges,
43
+ } = { ...binding, ...require('./highlight.js') };
44
+
45
+ export const { searchObjects } = require('./objects.js');
package/objects.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ import type { SearchOptions } from './index';
2
+
3
+ export interface KeyConfig {
4
+ name: string;
5
+ weight?: number;
6
+ }
7
+
8
+ export interface ObjectSearchOptions extends SearchOptions {
9
+ keys: Array<string | KeyConfig>;
10
+ }
11
+
12
+ export interface ObjectSearchResult<T> {
13
+ item: T;
14
+ index: number;
15
+ score: number;
16
+ keyScores: Array<number>;
17
+ }
18
+
19
+ /**
20
+ * Perform fuzzy search across object arrays with weighted keys.
21
+ *
22
+ * Wraps `searchKeys()` with an ergonomic API that accepts row-oriented
23
+ * objects and returns matched items directly.
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const users = [
28
+ * { name: 'John Smith', email: 'john@example.com' },
29
+ * { name: 'Jane Doe', email: 'jane@example.com' },
30
+ * ];
31
+ *
32
+ * const results = searchObjects('john', users, {
33
+ * keys: [{ name: 'name', weight: 2.0 }, 'email'],
34
+ * });
35
+ * // results[0].item → { name: 'John Smith', email: 'john@example.com' }
36
+ * ```
37
+ */
38
+ export declare function searchObjects<T>(
39
+ query: string,
40
+ items: Array<T>,
41
+ options: ObjectSearchOptions,
42
+ ): Array<ObjectSearchResult<T>>;
package/objects.js ADDED
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ const { searchKeys } = require('./index.js');
4
+
5
+ /**
6
+ * Get a nested property value from an object using a dot-separated path.
7
+ * @param {Record<string, unknown>} obj
8
+ * @param {string} path
9
+ * @returns {string}
10
+ */
11
+ function getNestedValue(obj, path) {
12
+ let current = obj;
13
+ for (const key of path.split('.')) {
14
+ if (current == null) return '';
15
+ current = current[key];
16
+ }
17
+ return current == null ? '' : String(current);
18
+ }
19
+
20
+ /**
21
+ * Perform fuzzy search across object arrays with weighted keys.
22
+ *
23
+ * Wraps `searchKeys()` with an ergonomic API that accepts row-oriented
24
+ * objects and returns matched items directly.
25
+ *
26
+ * @template T
27
+ * @param {string} query - The search query.
28
+ * @param {T[]} items - Array of objects to search.
29
+ * @param {object} options - Search options with keys configuration.
30
+ * @param {Array<string | { name: string; weight?: number }>} options.keys - Keys to search.
31
+ * @param {number} [options.maxResults] - Maximum results to return.
32
+ * @param {number} [options.minScore] - Minimum score threshold.
33
+ * @param {boolean} [options.isCaseSensitive] - Enable case-sensitive matching.
34
+ * @returns {Array<{ item: T; index: number; score: number; keyScores: number[] }>}
35
+ */
36
+ function searchObjects(query, items, options) {
37
+ const { keys, ...searchOpts } = options;
38
+
39
+ const normalizedKeys = keys.map((k) =>
40
+ typeof k === 'string' ? { name: k, weight: 1.0 } : { name: k.name, weight: k.weight ?? 1.0 },
41
+ );
42
+
43
+ const keyTexts = normalizedKeys.map((k) => items.map((item) => getNestedValue(item, k.name)));
44
+ const weights = normalizedKeys.map((k) => k.weight);
45
+
46
+ const nativeOpts = Object.keys(searchOpts).length > 0 ? searchOpts : undefined;
47
+
48
+ const results = searchKeys(query, keyTexts, weights, nativeOpts);
49
+
50
+ return results.map((r) => ({
51
+ item: items[r.index],
52
+ index: r.index,
53
+ score: r.score,
54
+ keyScores: r.keyScores,
55
+ }));
56
+ }
57
+
58
+ module.exports = { searchObjects };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rapid-fuzzy",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Rust-powered fuzzy search and string distance for JavaScript/TypeScript. 10-50x faster than fuse.js/leven.",
5
5
  "license": "MIT",
6
6
  "author": "derodero24",
@@ -46,7 +46,12 @@
46
46
  "index.mjs",
47
47
  "index.d.ts",
48
48
  "index.d.mts",
49
- "browser.js"
49
+ "objects.js",
50
+ "objects.d.ts",
51
+ "browser.js",
52
+ "highlight.js",
53
+ "highlight.mjs",
54
+ "highlight.d.ts"
50
55
  ],
51
56
  "napi": {
52
57
  "binaryName": "rapid-fuzzy",
@@ -68,6 +73,7 @@
68
73
  "packageManager": "pnpm@10.32.1",
69
74
  "scripts": {
70
75
  "build": "napi build --manifest-path crates/core/Cargo.toml --platform --release --js index.js --dts index.d.ts --output-dir .",
76
+ "postbuild": "node scripts/patch-binding.js",
71
77
  "build:debug": "napi build --manifest-path crates/core/Cargo.toml --platform --js index.js --dts index.d.ts --output-dir .",
72
78
  "build:wasm": "napi build --manifest-path crates/core/Cargo.toml --platform --release --js index.js --dts index.d.ts --output-dir . --target wasm32-wasip1-threads",
73
79
  "artifacts": "napi artifacts",
@@ -80,6 +86,9 @@
80
86
  "typecheck": "tsc --noEmit",
81
87
  "test": "vitest run",
82
88
  "test:wasm": "vitest run __test__/wasm.spec.ts",
89
+ "test:browser": "playwright test",
90
+ "test:bun": "bun test e2e/wasm-bun.test.ts",
91
+ "test:deno": "deno test --allow-read --allow-env --allow-net --node-modules-dir=auto e2e/wasm-deno.test.ts",
83
92
  "test:watch": "vitest watch",
84
93
  "bench": "vitest bench",
85
94
  "bench:readme": "npx tsx scripts/update-bench-readme.ts",
@@ -99,6 +108,7 @@
99
108
  "@emnapi/runtime": "^1.9.0",
100
109
  "@napi-rs/cli": "^3.5.1",
101
110
  "@napi-rs/wasm-runtime": "^1.1.1",
111
+ "@playwright/test": "^1.58.2",
102
112
  "@tybys/wasm-util": "^0.10.1",
103
113
  "@types/node": "^24.0.0",
104
114
  "@types/string-similarity": "^4.0.2",
@@ -111,6 +121,7 @@
111
121
  "publint": "^0.3.18",
112
122
  "string-similarity": "^4.0.4",
113
123
  "typescript": "^5.9.3",
124
+ "vite": "^8.0.0",
114
125
  "vitest": "^4.1.0"
115
126
  },
116
127
  "pnpm": {
@@ -119,14 +130,14 @@
119
130
  ]
120
131
  },
121
132
  "optionalDependencies": {
122
- "rapid-fuzzy-darwin-x64": "0.3.0",
123
- "rapid-fuzzy-darwin-arm64": "0.3.0",
124
- "rapid-fuzzy-linux-x64-gnu": "0.3.0",
125
- "rapid-fuzzy-linux-x64-musl": "0.3.0",
126
- "rapid-fuzzy-linux-arm64-gnu": "0.3.0",
127
- "rapid-fuzzy-linux-arm64-musl": "0.3.0",
128
- "rapid-fuzzy-win32-x64-msvc": "0.3.0",
129
- "rapid-fuzzy-win32-arm64-msvc": "0.3.0",
130
- "rapid-fuzzy-wasm32-wasi": "0.3.0"
133
+ "rapid-fuzzy-darwin-x64": "0.4.0",
134
+ "rapid-fuzzy-darwin-arm64": "0.4.0",
135
+ "rapid-fuzzy-linux-x64-gnu": "0.4.0",
136
+ "rapid-fuzzy-linux-x64-musl": "0.4.0",
137
+ "rapid-fuzzy-linux-arm64-gnu": "0.4.0",
138
+ "rapid-fuzzy-linux-arm64-musl": "0.4.0",
139
+ "rapid-fuzzy-win32-x64-msvc": "0.4.0",
140
+ "rapid-fuzzy-win32-arm64-msvc": "0.4.0",
141
+ "rapid-fuzzy-wasm32-wasi": "0.4.0"
131
142
  }
132
143
  }