usearch 2.9.1 → 2.10.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 (169) hide show
  1. package/README.md +50 -52
  2. package/javascript/{types → dist/cjs}/usearch.d.ts +90 -115
  3. package/javascript/dist/cjs/usearch.js +531 -0
  4. package/javascript/dist/esm/usearch.d.ts +212 -0
  5. package/javascript/{usearch.js → dist/esm/usearch.js} +202 -153
  6. package/package.json +25 -23
  7. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  8. package/prebuilds/linux-arm64/usearch.node +0 -0
  9. package/prebuilds/linux-x64/usearch.node +0 -0
  10. package/prebuilds/win32-ia32/usearch.node +0 -0
  11. package/prebuilds/win32-x64/usearch.node +0 -0
  12. package/.clang-format +0 -10
  13. package/.clang-tidy +0 -60
  14. package/.cmake-format.py +0 -19
  15. package/.dockerignore +0 -93
  16. package/.gitattributes +0 -9
  17. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  18. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  19. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -64
  20. package/.github/workflows/Dockerfile.libs +0 -36
  21. package/.github/workflows/prerelease.yml +0 -331
  22. package/.github/workflows/release.yml +0 -722
  23. package/.github/workflows/update_version.sh +0 -15
  24. package/.gitmodules +0 -9
  25. package/.releaserc +0 -90
  26. package/CITATION.cff +0 -11
  27. package/CMakeLists.txt +0 -324
  28. package/CODE_OF_CONDUCT.md +0 -128
  29. package/CONTRIBUTING.md +0 -353
  30. package/Cargo.toml +0 -19
  31. package/Dockerfile +0 -16
  32. package/VERSION +0 -1
  33. package/assets/unum.png +0 -0
  34. package/assets/usearch-approaches-transparent.png +0 -0
  35. package/assets/usearch-approaches-white.png +0 -0
  36. package/assets/usearch-neighbor-types.png +0 -0
  37. package/binding.gyp +0 -87
  38. package/build.gradle +0 -204
  39. package/build.rs +0 -18
  40. package/c/CMakeLists.txt +0 -25
  41. package/c/README.md +0 -34
  42. package/c/lib.cpp +0 -358
  43. package/c/test.c +0 -374
  44. package/c/usearch.h +0 -311
  45. package/cmake/clipp.cmake +0 -7
  46. package/cmake/config.cmake.in +0 -15
  47. package/cmake/emscripten.cmake +0 -8
  48. package/cmake/jemalloc.cmake +0 -62
  49. package/cmake/pkg-config.pc.in +0 -4
  50. package/cmake/usearchConfigVersion.cmake.in +0 -20
  51. package/conanfile.py +0 -26
  52. package/cpp/CMakeLists.txt +0 -14
  53. package/cpp/README.md +0 -161
  54. package/cpp/bench.cpp +0 -618
  55. package/cpp/test.cpp +0 -323
  56. package/fp16/CMakeLists.txt +0 -176
  57. package/fp16/LICENSE +0 -11
  58. package/fp16/README.md +0 -20
  59. package/fp16/bench/alt-element.cc +0 -89
  60. package/fp16/bench/from-alt-array.cc +0 -175
  61. package/fp16/bench/from-ieee-array.cc +0 -399
  62. package/fp16/bench/ieee-element.cc +0 -272
  63. package/fp16/bench/to-alt-array.cc +0 -91
  64. package/fp16/bench/to-ieee-array.cc +0 -303
  65. package/fp16/cmake/DownloadGoogleBenchmark.cmake +0 -15
  66. package/fp16/cmake/DownloadGoogleTest.cmake +0 -15
  67. package/fp16/cmake/DownloadPSimd.cmake +0 -15
  68. package/fp16/configure.py +0 -65
  69. package/fp16/confu.yaml +0 -8
  70. package/fp16/include/fp16/__init__.py +0 -0
  71. package/fp16/include/fp16/avx.py +0 -50
  72. package/fp16/include/fp16/avx2.py +0 -53
  73. package/fp16/include/fp16/bitcasts.h +0 -92
  74. package/fp16/include/fp16/fp16.h +0 -451
  75. package/fp16/include/fp16/psimd.h +0 -131
  76. package/fp16/include/fp16.h +0 -11
  77. package/fp16/jni/Android.mk +0 -6
  78. package/fp16/jni/Application.mk +0 -4
  79. package/fp16/test/alt-from-fp32-value.cc +0 -498
  80. package/fp16/test/alt-to-fp32-bits.cc +0 -263
  81. package/fp16/test/alt-to-fp32-psimd.cc +0 -145
  82. package/fp16/test/alt-to-fp32-value.cc +0 -348
  83. package/fp16/test/alt-to-fp32x2-psimd.cc +0 -245
  84. package/fp16/test/bitcasts.cc +0 -56
  85. package/fp16/test/ieee-from-fp32-value.cc +0 -523
  86. package/fp16/test/ieee-to-fp32-bits.cc +0 -333
  87. package/fp16/test/ieee-to-fp32-psimd.cc +0 -312
  88. package/fp16/test/ieee-to-fp32-value.cc +0 -420
  89. package/fp16/test/ieee-to-fp32x2-psimd.cc +0 -541
  90. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +0 -96
  91. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +0 -96
  92. package/fp16/test/peachpy/stubs.py +0 -38
  93. package/fp16/test/tables.cc +0 -530
  94. package/fp16/test/tables.h +0 -13
  95. package/fp16/third-party/THHalf.h +0 -92
  96. package/fp16/third-party/eigen-half.h +0 -170
  97. package/fp16/third-party/float16-compressor.h +0 -80
  98. package/fp16/third-party/half.hpp +0 -2916
  99. package/fp16/third-party/npy-halffloat.h +0 -328
  100. package/include/usearch/index.hpp +0 -3836
  101. package/include/usearch/index_dense.hpp +0 -2004
  102. package/include/usearch/index_plugins.hpp +0 -2203
  103. package/javascript/lib.cpp +0 -341
  104. package/javascript/types/usearch.d.ts.map +0 -1
  105. package/javascript/usearch.test.js +0 -74
  106. package/package-ci.json +0 -10
  107. package/pyproject.toml +0 -51
  108. package/setup.py +0 -201
  109. package/simsimd/.releaserc +0 -86
  110. package/simsimd/Cargo.lock +0 -708
  111. package/simsimd/Cargo.toml +0 -36
  112. package/simsimd/LICENSE +0 -201
  113. package/simsimd/README.md +0 -275
  114. package/simsimd/binding.gyp +0 -36
  115. package/simsimd/build.rs +0 -20
  116. package/simsimd/include/simsimd/binary.h +0 -201
  117. package/simsimd/include/simsimd/probability.h +0 -548
  118. package/simsimd/include/simsimd/simsimd.h +0 -456
  119. package/simsimd/include/simsimd/spatial.h +0 -1175
  120. package/simsimd/include/simsimd/types.h +0 -207
  121. package/simsimd/javascript/bench.js +0 -127
  122. package/simsimd/javascript/fallback.ts +0 -193
  123. package/simsimd/javascript/lib.c +0 -102
  124. package/simsimd/javascript/node-gyp-build.d.ts +0 -1
  125. package/simsimd/javascript/simsimd.ts +0 -124
  126. package/simsimd/javascript/test.mjs +0 -153
  127. package/simsimd/javascript/tsconfig-base.json +0 -25
  128. package/simsimd/javascript/tsconfig-cjs.json +0 -8
  129. package/simsimd/javascript/tsconfig-esm.json +0 -8
  130. package/simsimd/package-lock.json +0 -1688
  131. package/simsimd/package.json +0 -61
  132. package/simsimd/rust/benches/cosine.rs +0 -27
  133. package/simsimd/rust/benches/native.rs +0 -27
  134. package/simsimd/rust/benches/sqeuclidean.rs +0 -27
  135. package/simsimd/rust/lib.c +0 -68
  136. package/simsimd/rust/lib.rs +0 -232
  137. package/sqlite/README.md +0 -178
  138. package/sqlite/lib.cpp +0 -325
  139. package/stringzilla/CONTRIBUTING.md +0 -429
  140. package/stringzilla/Cargo.lock +0 -25
  141. package/stringzilla/Cargo.toml +0 -26
  142. package/stringzilla/LICENSE +0 -201
  143. package/stringzilla/README.md +0 -1281
  144. package/stringzilla/VERSION +0 -1
  145. package/stringzilla/assets/cover-strinzilla.jpeg +0 -0
  146. package/stringzilla/assets/meme-stringzilla-v2.jpeg +0 -0
  147. package/stringzilla/assets/meme-stringzilla-v3.jpeg +0 -0
  148. package/stringzilla/binding.gyp +0 -16
  149. package/stringzilla/build.rs +0 -14
  150. package/stringzilla/c/lib.c +0 -304
  151. package/stringzilla/cli/README.md +0 -48
  152. package/stringzilla/cli/split.py +0 -72
  153. package/stringzilla/cli/wc.py +0 -37
  154. package/stringzilla/include/stringzilla/experimental.h +0 -724
  155. package/stringzilla/include/stringzilla/module.modulemap +0 -9
  156. package/stringzilla/include/stringzilla/stringzilla.h +0 -5240
  157. package/stringzilla/include/stringzilla/stringzilla.hpp +0 -3830
  158. package/stringzilla/javascript/lib.c +0 -125
  159. package/stringzilla/javascript/stringzilla.js +0 -22
  160. package/stringzilla/package-ci.json +0 -10
  161. package/stringzilla/package.json +0 -32
  162. package/stringzilla/rust/lib.rs +0 -362
  163. package/stringzilla/swift/StringProtocol+StringZilla.swift +0 -280
  164. package/stringzilla/swift/Test.swift +0 -60
  165. package/tsconfig.json +0 -21
  166. package/wasmer.toml +0 -12
  167. package/winlibinstaller.bat +0 -42
  168. /package/{simsimd/javascript/dist-package-cjs.json → javascript/dist/cjs/package.json} +0 -0
  169. /package/{simsimd/javascript/dist-package-esm.json → javascript/dist/esm/package.json} +0 -0
@@ -1,45 +1,50 @@
1
- const compiled = require('bindings')('usearch');
2
-
1
+ import build from "node-gyp-build";
2
+ import * as path from "node:path";
3
+ import { existsSync } from "node:fs";
4
+ import { getFileName, getRoot } from "bindings";
5
+ const compiled = build(getBuildDir(getDirName()));
3
6
  /**
4
7
  * Enumeration representing the various metric kinds used to measure the distance between vectors in the index.
5
8
  * @enum {string}
6
9
  * @readonly
7
10
  */
8
- const MetricKind = {
9
- Unknown: 'unknown',
10
- Cos: 'cos',
11
- IP: 'ip',
12
- L2sq: 'l2sq',
13
- Haversine: 'haversine',
14
- Divergence: 'divergence',
15
- Pearson: 'pearson',
16
- Jaccard: 'jaccard',
17
- Hamming: 'hamming',
18
- Tanimoto: 'tanimoto',
19
- Sorensen: 'sorensen'
20
- };
21
-
11
+ export var MetricKind;
12
+ (function (MetricKind) {
13
+ MetricKind["Unknown"] = "unknown";
14
+ MetricKind["Cos"] = "cos";
15
+ MetricKind["IP"] = "ip";
16
+ MetricKind["L2sq"] = "l2sq";
17
+ MetricKind["Haversine"] = "haversine";
18
+ MetricKind["Divergence"] = "divergence";
19
+ MetricKind["Pearson"] = "pearson";
20
+ MetricKind["Jaccard"] = "jaccard";
21
+ MetricKind["Hamming"] = "hamming";
22
+ MetricKind["Tanimoto"] = "tanimoto";
23
+ MetricKind["Sorensen"] = "sorensen";
24
+ })(MetricKind || (MetricKind = {}));
22
25
  /**
23
26
  * Enumeration representing the various scalar kinds used to define the type of scalar values in vectors.
24
27
  * @enum {string}
25
28
  * @readonly
26
29
  */
27
- const ScalarKind = {
28
- Unknown: 'unknown',
29
- F32: 'f32',
30
- F64: 'f64',
31
- F16: 'f16',
32
- I8: 'i8',
33
- B1: 'b1'
34
- };
35
-
30
+ export var ScalarKind;
31
+ (function (ScalarKind) {
32
+ ScalarKind["Unknown"] = "unknown";
33
+ ScalarKind["F32"] = "f32";
34
+ ScalarKind["F64"] = "f64";
35
+ ScalarKind["F16"] = "f16";
36
+ ScalarKind["I8"] = "i8";
37
+ ScalarKind["B1"] = "b1";
38
+ })(ScalarKind || (ScalarKind = {}));
36
39
  /**
37
40
  * Represents a set of search results.
38
41
  */
39
- class Matches {
42
+ export class Matches {
43
+ keys;
44
+ distances;
40
45
  /**
41
46
  * Constructs a Matches object.
42
- *
47
+ *
43
48
  * @param {BigUint64Array} keys - The keys of the nearest neighbors found.
44
49
  * @param {Float32Array} distances - The distances of the nearest neighbors found.
45
50
  */
@@ -48,18 +53,21 @@ class Matches {
48
53
  this.distances = distances;
49
54
  }
50
55
  }
51
-
52
56
  /**
53
57
  * Represents a set of batched search results.
54
58
  */
55
- class BatchMatches {
59
+ export class BatchMatches {
60
+ keys;
61
+ distances;
62
+ counts;
63
+ k;
56
64
  /**
57
65
  * Constructs a BatchMatches object.
58
- *
66
+ *
59
67
  * @param {BigUint64Array} keys - The keys of the nearest neighbors found in the batch.
60
68
  * @param {Float32Array} distances - The distances of the nearest neighbors found in the batch.
61
69
  * @param {BigUint64Array} counts - The number of neighbors found for each query in the batch.
62
- * @param {bigint} k - The limit for search results per query in the batch.
70
+ * @param {number} k - The limit for search results per query in the batch.
63
71
  */
64
72
  constructor(keys, distances, counts, k) {
65
73
  this.keys = keys;
@@ -67,10 +75,9 @@ class BatchMatches {
67
75
  this.counts = counts;
68
76
  this.k = k;
69
77
  }
70
-
71
78
  /**
72
79
  * Retrieves a Matches object at the specified index in the batch.
73
- *
80
+ *
74
81
  * @param {number} i - The index at which to retrieve the Matches object.
75
82
  * @returns {Matches} - A Matches object representing the search results at the specified index in the batch.
76
83
  */
@@ -82,61 +89,74 @@ class BatchMatches {
82
89
  return new Matches(keysSlice, distancesSlice);
83
90
  }
84
91
  }
85
-
86
92
  function isOneKey(keys) {
87
- return (!Number.isNaN(keys) && typeof keys === 'number') || typeof keys === 'bigint';
93
+ return ((!Number.isNaN(keys) && typeof keys === "number") ||
94
+ typeof keys === "bigint");
88
95
  }
89
-
90
96
  function normalizeKeys(keys) {
91
- if (isOneKey(keys)) {
92
- keys = BigUint64Array.of(BigInt(keys));
93
- } else if (Array.isArray(keys)) {
94
- keys = keys.map(key => {
95
- if ((typeof key !== 'bigint' && typeof key !== 'number') || Number.isNaN(key))
96
- throw new Error("All keys must be integers or bigints.");
97
- return BigInt(key);
97
+ if (keys instanceof BigUint64Array) {
98
+ return keys;
99
+ }
100
+ let normalizedKeys;
101
+ if ((typeof keys === "number" && !Number.isNaN(keys)) ||
102
+ typeof keys === "bigint") {
103
+ normalizedKeys = BigUint64Array.of(BigInt(keys));
104
+ }
105
+ else if (Array.isArray(keys)) {
106
+ const bigintkeys = keys.map((key) => {
107
+ if (typeof key === "bigint") {
108
+ return key;
109
+ }
110
+ else if (typeof key === "number" &&
111
+ !Number.isNaN(key) &&
112
+ Number.isInteger(key) &&
113
+ key >= 0) {
114
+ return BigInt(key);
115
+ }
116
+ throw new Error("All keys must be positive integers or bigints.");
98
117
  });
99
- keys = BigUint64Array.from(keys);
100
- } else if (!(keys instanceof BigUint64Array)) {
118
+ normalizedKeys = BigUint64Array.from(bigintkeys);
119
+ }
120
+ else {
101
121
  throw new Error("Keys must be a number, bigint, an array of numbers or bigints, or a BigUint64Array.");
102
122
  }
103
- return keys;
123
+ return normalizedKeys;
104
124
  }
105
-
106
125
  function isVector(vectors) {
107
- return vectors instanceof Float32Array || vectors instanceof Float64Array || vectors instanceof Int8Array;
126
+ return (vectors instanceof Float32Array ||
127
+ vectors instanceof Float64Array ||
128
+ vectors instanceof Int8Array);
108
129
  }
109
-
110
130
  function normalizeVectors(vectors, dimensions, targetType = Float32Array) {
111
131
  let flattenedVectors;
112
132
  if (isVector(vectors)) {
113
- flattenedVectors = (vectors.constructor === targetType) ? vectors : new targetType(vectors);
114
- } else if (Array.isArray(vectors)) {
133
+ flattenedVectors =
134
+ vectors.constructor === targetType
135
+ ? vectors
136
+ : new targetType(vectors);
137
+ }
138
+ else if (Array.isArray(vectors)) {
115
139
  let totalLength = 0;
116
- for (const vec of vectors) totalLength += vec.length;
117
-
140
+ for (const vec of vectors)
141
+ totalLength += vec.length;
118
142
  flattenedVectors = new targetType(totalLength);
119
143
  let offset = 0;
120
144
  for (const vec of vectors) {
121
145
  flattenedVectors.set(vec, offset);
122
146
  offset += vec.length;
123
147
  }
124
- } else {
148
+ }
149
+ else {
125
150
  throw new Error("Vectors must be a TypedArray or an array of arrays.");
126
151
  }
127
-
128
152
  if (flattenedVectors.length % dimensions !== 0)
129
153
  throw new Error("The size of the flattened vectors must be a multiple of the dimension of the vectors.");
130
-
131
154
  return flattenedVectors;
132
155
  }
133
-
134
-
135
- class Index {
136
-
156
+ export class Index {
137
157
  /**
138
158
  * Constructs a new index.
139
- *
159
+ *
140
160
  * @param {(number | {dimensions: number, metric: MetricKind = MetricKind.Cos, quantization: ScalarKind = ScalarKind.F32, connectivity: number = 0, expansion_add: number = 0, expansion_search: number = 0, multi: boolean = false})} dimensionsOrConfigs
141
161
  * @param {MetricKind} [metric=MetricKind.Cos] - Optional, default is 'cos'.
142
162
  * @param {ScalarKind} [quantization=ScalarKind.F32] - Optional, default is 'f32'.
@@ -148,50 +168,62 @@ class Index {
148
168
  */
149
169
  constructor(dimensionsOrConfigs, metric = MetricKind.Cos, quantization = ScalarKind.F32, connectivity = 0, expansion_add = 0, expansion_search = 0, multi = false) {
150
170
  let dimensions;
151
- if (typeof dimensionsOrConfigs === 'object' && dimensionsOrConfigs !== null) {
152
- // Parameters are provided as an object
153
- ({ dimensions, metric = MetricKind.Cos, quantization = ScalarKind.F32, connectivity = 0, expansion_add = 0, expansion_search = 0, multi = false } = dimensionsOrConfigs);
154
- } else if ((typeof dimensionsOrConfigs === 'number' && !Number.isNaN(dimensionsOrConfigs)) || typeof dimensionsOrConfigs === 'bigint') {
171
+ if ((typeof dimensionsOrConfigs === "number" &&
172
+ !Number.isNaN(dimensionsOrConfigs)) ||
173
+ typeof dimensionsOrConfigs === "bigint") {
155
174
  // Parameters are provided as individual arguments
156
175
  dimensions = dimensionsOrConfigs;
157
- } else {
176
+ }
177
+ else if (typeof dimensionsOrConfigs === "object" &&
178
+ dimensionsOrConfigs !== null) {
179
+ // Parameters are provided as an object
180
+ ({
181
+ dimensions,
182
+ metric = MetricKind.Cos,
183
+ quantization = ScalarKind.F32,
184
+ connectivity = 0,
185
+ expansion_add = 0,
186
+ expansion_search = 0,
187
+ multi = false,
188
+ } = dimensionsOrConfigs);
189
+ }
190
+ else {
158
191
  throw new Error("Invalid arguments. Expected either individual arguments or a single object argument.");
159
192
  }
160
-
161
- if (!Number.isInteger(dimensions) || !Number.isInteger(connectivity) || !Number.isInteger(expansion_add) || !Number.isInteger(expansion_search) || dimensions <= 0 || connectivity < 0 || expansion_add < 0 || expansion_search < 0) {
193
+ if ((typeof dimensions !== 'bigint' && (!Number.isInteger(dimensions) || dimensions <= 0)) ||
194
+ (typeof connectivity !== 'bigint' && (!Number.isInteger(connectivity) || connectivity < 0)) ||
195
+ (typeof expansion_add !== 'bigint' && (!Number.isInteger(expansion_add) || expansion_add < 0)) ||
196
+ (typeof expansion_search !== 'bigint' && (!Number.isInteger(expansion_search) || expansion_search < 0))) {
162
197
  throw new Error("`dimensions`, `connectivity`, `expansion_add`, and `expansion_search` must be non-negative integers, with `dimensions` being positive.");
163
198
  }
164
-
165
- if (typeof multi !== 'boolean') {
199
+ if (typeof multi !== "boolean") {
166
200
  throw new Error("`multi` must be a boolean value.");
167
201
  }
168
-
169
202
  if (!Object.values(MetricKind).includes(metric)) {
170
- throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(', ')}`);
203
+ throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(", ")}`);
171
204
  }
172
-
173
205
  if (!Object.values(ScalarKind).includes(quantization)) {
174
- throw new Error(`Invalid quantization: ${quantization}. It must be one of: ${Object.values(ScalarKind).join(', ')}`);
206
+ throw new Error(`Invalid quantization: ${quantization}. It must be one of: ${Object.values(ScalarKind).join(", ")}`);
175
207
  }
176
-
177
- this._compiledIndex = new compiled.CompiledIndex(dimensions, metric, quantization, connectivity, expansion_add, expansion_search, multi);
208
+ // @ts-expect-error
209
+ this.#compiledIndex = new compiled.CompiledIndex(dimensions, metric, quantization, connectivity, expansion_add, expansion_search, multi);
178
210
  }
179
-
211
+ #compiledIndex;
180
212
  /**
181
213
  * Add vectors to the index.
182
- *
214
+ *
183
215
  * This method accepts vectors and their corresponding keys for indexing.
184
216
  * Each key should correspond to a vector. If a single key is provided,
185
217
  * it is broadcasted to match the number of provided vectors.
186
- *
218
+ *
187
219
  * Vectors should be provided as a flat typed array representing a matrix
188
220
  * where each row is a vector to be indexed. The matrix should have a size
189
221
  * of n * d, where n is the number of vectors, and d is the dimensionality
190
222
  * of the vectors.
191
- *
223
+ *
192
224
  * Keys should be provided as a BigInt or an array-like object of BigInts
193
225
  * representing the unique identifier for each vector.
194
- *
226
+ *
195
227
  * @param {bigint|bigint[]|BigUint64Array} keys - Input identifiers for every vector.
196
228
  * If a single key is provided, it is associated with all provided vectors.
197
229
  * @param {Float32Array|Float64Array|Int8Array} vectors - Input matrix representing vectors,
@@ -201,173 +233,169 @@ class Index {
201
233
  */
202
234
  add(keys, vectors) {
203
235
  let normalizedKeys = normalizeKeys(keys);
204
- let normalizedVectors = normalizeVectors(vectors, this._compiledIndex.dimensions());
205
- let countVectors = normalizedVectors.length / this._compiledIndex.dimensions();
206
-
236
+ let normalizedVectors = normalizeVectors(vectors, this.#compiledIndex.dimensions());
237
+ let countVectors = normalizedVectors.length / this.#compiledIndex.dimensions();
207
238
  // If a single key is provided but there are multiple vectors,
208
239
  // broadcast the single key value to match the number of vectors
209
240
  if (normalizedKeys.length === 1 && countVectors > 1) {
210
241
  normalizedKeys = BigUint64Array.from({ length: countVectors }, () => normalizedKeys[0]);
211
- } else if (normalizedKeys.length !== countVectors) {
242
+ }
243
+ else if (normalizedKeys.length !== countVectors) {
212
244
  throw new Error(`The length of keys (${normalizedKeys.length}) must match the number of vectors (${countVectors}) or be a single key.`);
213
245
  }
214
-
215
246
  // Call the compiled method
216
- this._compiledIndex.add(normalizedKeys, normalizedVectors);
247
+ this.#compiledIndex.add(normalizedKeys, normalizedVectors);
217
248
  }
218
-
219
249
  /**
220
250
  * Perform a k-nearest neighbor search on the index.
221
- *
251
+ *
222
252
  * This method accepts a matrix of query vectors and returns the closest vectors
223
253
  * from the index for each query. The method returns an object containing the keys,
224
254
  * distances, and counts of the matches found.
225
- *
255
+ *
226
256
  * Vectors should be provided as a flat typed array representing a matrix where
227
257
  * each row is a vector. The matrix should be of size n * d, where n is the
228
258
  * number of query vectors, and d is their dimensionality.
229
- *
259
+ *
230
260
  * The parameter `k` specifies the number of nearest neighbors to return for each
231
261
  * query vector. If there are not enough results for a query, the result array is
232
262
  * padded with -1s.
233
- *
234
- * @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of arrays.
263
+ *
264
+ * @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} vectors - Input matrix representing query vectors, can be a TypedArray or an array of TypedArray.
235
265
  * @param {number} k - The number of nearest neighbors to search for each query vector.
236
266
  * @return {Matches|BatchMatches} - Search results for one or more queries, containing keys, distances, and counts of the matches found.
237
267
  * @throws Will throw an error if `k` is not a positive integer or if the size of the vectors is not a multiple of dimensions.
238
- * @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of arrays) or if its flattened size is not a multiple of dimensions.
268
+ * @throws Will throw an error if `vectors` is not a valid input type (TypedArray or an array of TypedArray) or if its flattened size is not a multiple of dimensions.
239
269
  */
240
270
  search(vectors, k) {
241
- if ((!Number.isNaN(k) && typeof k !== 'number') || k <= 0) {
271
+ if ((!Number.isNaN(k) && typeof k !== "number") || k <= 0) {
242
272
  throw new Error("`k` must be a positive integer representing the number of nearest neighbors to search for.");
243
273
  }
244
-
245
- const normalizedVectors = normalizeVectors(vectors, this._compiledIndex.dimensions());
246
-
274
+ const normalizedVectors = normalizeVectors(vectors, this.#compiledIndex.dimensions());
247
275
  // Call the compiled method and create Matches or BatchMatches object with the result
248
- const result = this._compiledIndex.search(normalizedVectors, k);
249
- const countInQueries = normalizedVectors.length / Number(this._compiledIndex.dimensions());
250
- const batchMatches = new BatchMatches(result[0], result[1], result[2], k);
251
-
276
+ const result = this.#compiledIndex.search(normalizedVectors, k);
277
+ const countInQueries = normalizedVectors.length / Number(this.#compiledIndex.dimensions());
278
+ const batchMatches = new BatchMatches(...result, k);
252
279
  if (countInQueries === 1) {
253
280
  return batchMatches.get(0);
254
- } else {
281
+ }
282
+ else {
255
283
  return batchMatches;
256
284
  }
257
285
  }
258
-
259
286
  /**
260
287
  * Verifies the presence of one or more keys in the index.
261
- *
262
- * This method accepts one or multiple keys as input and returns a boolean or
288
+ *
289
+ * This method accepts one or multiple keys as input and returns a boolean or
263
290
  * an array of booleans indicating whether each key is present in the index.
264
- *
291
+ *
265
292
  * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be checked for presence in the index.
266
293
  * @return {boolean|boolean[]} - Returns true if a single key is contained in the index, false otherwise. Returns an array of booleans corresponding to the presence of each key in the index when multiple keys are provided.
267
294
  * @throws Will throw an error if keys are not integers.
268
295
  */
269
296
  contains(keys) {
270
297
  let normalizedKeys = normalizeKeys(keys);
271
- let normalizedResults = this._compiledIndex.contains(normalizedKeys);
298
+ let normalizedResults = this.#compiledIndex.contains(normalizedKeys);
272
299
  if (isOneKey(keys))
273
300
  return normalizedResults[0];
274
301
  else
275
302
  return normalizedResults;
276
303
  }
277
-
278
304
  /**
279
305
  * Counts the number of times keys shows up in the index.
280
- *
306
+ *
281
307
  * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be enumerated.
282
308
  * @return {number|number[]} - Returns the number of vectors found when a single key is provided. Returns an array of big integers corresponding to the number of vectors found for each key when multiple keys are provided.
283
309
  * @throws Will throw an error if keys are not integers.
284
310
  */
285
311
  count(keys) {
286
312
  let normalizedKeys = normalizeKeys(keys);
287
- let normalizedResults = this._compiledIndex.count(normalizedKeys);
313
+ let normalizedResults = this.#compiledIndex.count(normalizedKeys);
288
314
  if (isOneKey(keys))
289
315
  return normalizedResults[0];
290
316
  else
291
317
  return normalizedResults;
292
318
  }
293
-
294
319
  /**
295
320
  * Removes one or multiple vectors from the index.
296
- *
321
+ *
297
322
  * This method accepts one or multiple keys as input and removes the corresponding vectors from the index.
298
323
  * It returns the number of vectors actually removed for each key provided.
299
- *
324
+ *
300
325
  * @param {bigint|bigint[]|BigUint64Array} keys - The identifier(s) of the vector(s) to be removed.
301
326
  * @return {number|number[]} - Returns the number of vectors deleted when a single key is provided. Returns an array of big integers corresponding to the number of vectors deleted for each key when multiple keys are provided.
302
327
  * @throws Will throw an error if keys are not integers.
303
328
  */
304
329
  remove(keys) {
305
330
  let normalizedKeys = normalizeKeys(keys);
306
- let normalizedResults = this._compiledIndex.remove(normalizedKeys);
331
+ let normalizedResults = this.#compiledIndex.remove(normalizedKeys);
307
332
  if (isOneKey(keys))
308
333
  return normalizedResults[0];
309
334
  else
310
335
  return normalizedResults;
311
336
  }
312
-
313
337
  /**
314
338
  * Returns the dimensionality of vectors.
315
339
  * @return {number} The dimensionality of vectors.
316
340
  */
317
- dimensions() { return this._compiledIndex.dimensions() }
318
-
341
+ dimensions() {
342
+ return this.#compiledIndex.dimensions();
343
+ }
319
344
  /**
320
345
  * Returns connectivity.
321
346
  * @return {number} The connectivity of index.
322
347
  */
323
- connectivity() { return this._compiledIndex.connectivity() }
324
-
348
+ connectivity() {
349
+ return this.#compiledIndex.connectivity();
350
+ }
325
351
  /**
326
352
  * Returns the number of vectors currently indexed.
327
353
  * @return {number} The number of vectors currently indexed.
328
354
  */
329
- size() { return this._compiledIndex.size() }
330
-
355
+ size() {
356
+ return this.#compiledIndex.size();
357
+ }
331
358
  /**
332
359
  * Returns index capacity.
333
360
  * @return {number} The capacity of index.
334
361
  */
335
- capacity() { return this._compiledIndex.capacity() }
336
-
337
- /**
362
+ capacity() {
363
+ return this.#compiledIndex.capacity();
364
+ }
365
+ /**
338
366
  * Write index to a file.
339
367
  * @param {string} path File path to write.
340
368
  * @throws Will throw an error if `path` is not a string.
341
369
  */
342
370
  save(path) {
343
- if (typeof path !== 'string') throw new Error("`path` must be a string representing the file path to write.");
344
- this._compiledIndex.save(path);
371
+ if (typeof path !== "string")
372
+ throw new Error("`path` must be a string representing the file path to write.");
373
+ this.#compiledIndex.save(path);
345
374
  }
346
-
347
- /**
375
+ /**
348
376
  * Load index from a file.
349
377
  * @param {string} path File path to read.
350
378
  * @throws Will throw an error if `path` is not a string.
351
379
  */
352
380
  load(path) {
353
- if (typeof path !== 'string') throw new Error("`path` must be a string representing the file path to read.");
354
- this._compiledIndex.load(path);
381
+ if (typeof path !== "string")
382
+ throw new Error("`path` must be a string representing the file path to read.");
383
+ this.#compiledIndex.load(path);
355
384
  }
356
-
357
- /**
385
+ /**
358
386
  * View index from a file, without loading into RAM.
359
387
  * @param {string} path File path to read.
360
388
  * @throws Will throw an error if `path` is not a string.
361
389
  */
362
390
  view(path) {
363
- if (typeof path !== 'string') throw new Error("`path` must be a string representing the file path to read.");
364
- this._compiledIndex.view(path);
391
+ if (typeof path !== "string")
392
+ throw new Error("`path` must be a string representing the file path to read.");
393
+ this.#compiledIndex.view(path);
365
394
  }
366
395
  }
367
-
368
396
  /**
369
397
  * Performs an exact search on the given dataset to find the best matching vectors for each query.
370
- *
398
+ *
371
399
  * @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} dataset - The dataset containing vectors to be searched. It can be a TypedArray or an array of arrays.
372
400
  * @param {Float32Array|Float64Array|Int8Array|Array<Array<number>>} queries - The queries containing vectors to search for in the dataset. It can be a TypedArray or an array of arrays.
373
401
  * @param {number} dimensions - The dimensionality of the vectors in both the dataset and the queries. It defines the number of elements in each vector.
@@ -379,41 +407,40 @@ class Index {
379
407
  * @throws Will throw an error if `dataset` and `queries` are not valid input types (TypedArray or an array of arrays).
380
408
  * @throws Will throw an error if the sizes of the flattened `dataset` and `queries` are not multiples of `dimensions`.
381
409
  * @throws Will throw an error if `count` is greater than the number of vectors in the `dataset`.
382
- *
410
+ *
383
411
  * @example
384
412
  * const dataset = [[1.0, 2.0], [3.0, 4.0]]; // Two vectors: [1.0, 2.0] and [3.0, 4.0]
385
413
  * const queries = [[1.5, 2.5]]; // One vector: [1.5, 2.5]
386
414
  * const dimensions = 2; // The number of elements in each vector.
387
415
  * const count = 1; // The number of nearest neighbors to return for each query.
388
416
  * const metric = MetricKind.IP; // Using the Inner Product distance metric.
389
- *
417
+ *
390
418
  * const result = exactSearch(dataset, queries, dimensions, count, metric);
391
- * // result might be:
419
+ * // result might be:
392
420
  * // {
393
421
  * // keys: BigUint64Array [ 1n ],
394
422
  * // distances: Float32Array [ some_value ],
395
423
  * // }
396
424
  */
397
425
  function exactSearch(dataset, queries, dimensions, count, metric) {
398
-
399
426
  // Validate and normalize the dimensions and count
400
427
  dimensions = Number(dimensions);
401
428
  count = Number(count);
402
429
  if (count <= 0 || dimensions <= 0) {
403
430
  throw new Error("Dimensions and count must be positive integers.");
404
431
  }
405
-
406
432
  // Validate metric
407
433
  if (!Object.values(MetricKind).includes(metric)) {
408
- throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(', ')}`);
434
+ throw new Error(`Invalid metric: ${metric}. It must be one of: ${Object.values(MetricKind).join(", ")}`);
409
435
  }
410
-
411
436
  // Flatten and normalize dataset and queries if they are arrays of arrays
412
437
  let targetType;
413
- if (dataset instanceof Float64Array) targetType = Float64Array;
414
- else if (dataset instanceof Int8Array) targetType = Int8Array;
415
- else targetType = Float32Array; // default to Float32Array if dataset is not Float64Array or Int8Array
416
-
438
+ if (dataset instanceof Float64Array)
439
+ targetType = Float64Array;
440
+ else if (dataset instanceof Int8Array)
441
+ targetType = Int8Array;
442
+ else
443
+ targetType = Float32Array; // default to Float32Array if dataset is not Float64Array or Int8Array
417
444
  dataset = normalizeVectors(dataset, dimensions, targetType);
418
445
  queries = normalizeVectors(queries, dimensions, targetType);
419
446
  const countInDataset = dataset.length / dimensions;
@@ -421,18 +448,16 @@ function exactSearch(dataset, queries, dimensions, count, metric) {
421
448
  if (count > countInDataset) {
422
449
  throw new Error("Count must be equal or smaller than the number of vectors in the dataset.");
423
450
  }
424
-
425
451
  // Call the compiled function with the normalized input
426
452
  const result = compiled.exactSearch(dataset, queries, dimensions, count, metric);
427
-
428
453
  // Create and return a Matches or BatchMatches object with the result
429
454
  if (countInQueries == 1) {
430
455
  return new Matches(result[0], result[1]);
431
- } else {
432
- return new BatchMatches(result[0], result[1], result[2], count);
456
+ }
457
+ else {
458
+ return new BatchMatches(...result, count);
433
459
  }
434
460
  }
435
-
436
461
  module.exports = {
437
462
  Index,
438
463
  MetricKind,
@@ -441,3 +466,27 @@ module.exports = {
441
466
  BatchMatches,
442
467
  exactSearch,
443
468
  };
469
+ // utility functions to help find native builds
470
+ function getBuildDir(dir) {
471
+ if (existsSync(path.join(dir, "build")))
472
+ return dir;
473
+ if (existsSync(path.join(dir, "prebuilds")))
474
+ return dir;
475
+ if (path.basename(dir) === ".next") {
476
+ // special case for next.js on custom node (not vercel)
477
+ const sideways = path.join(dir, "..", "node_modules", "usearch");
478
+ if (existsSync(sideways))
479
+ return getBuildDir(sideways);
480
+ }
481
+ if (dir === "/")
482
+ throw new Error("Could not find native build for usearch");
483
+ return getBuildDir(path.join(dir, ".."));
484
+ }
485
+ function getDirName() {
486
+ try {
487
+ if (__dirname)
488
+ return __dirname;
489
+ }
490
+ catch (e) { }
491
+ return getRoot(getFileName());
492
+ }