Repository: unum-cloud/usearch (language: rust)

Issue:
test: usearch-js expected results
Test example code

The `BigUint64Array` and `Float32Array` have a length of 10, not 1

```
  AssertionError [ERR_ASSERTION]: Expected values to be loosely deep-equal:

  BigUint64Array(10) [
    42n,
    0n,
    0n,
    0n,
    0n,
    0n,
    0n,
    0n,
    0n,
    0n
  ]

  should loosely deep-equal

  BigUint64Array(1) [
    42n
  ]
      at TestContext.<anonymous> (/Users/jlarmst/Desktop/typefoundriesdirect/usearch/javascript/usearch.test.js:46:10)
      at Test.runInAsyncScope (node:async_hooks:203:9)
      at Test.run (node:internal/test_runner/test:573:25)
      at Test.processPendingSubtests (node:internal/test_runner/test:318:18)
      at Test.postRun (node:internal/test_runner/test:642:19)
      at Test.run (node:internal/test_runner/test:601:10)
      at async startSubtest (node:internal/test_runner/harness:202:3) {
    generatedMessage: true,
    code: 'ERR_ASSERTION',
    actual: BigUint64Array(10) [
      42n, 0n, 0n, 0n, 0n,
       0n, 0n, 0n, 0n, 0n
    ],
    expected: BigUint64Array(1) [ 42n ],
    operator: 'deepEqual'
  }
```


Discussion / hints:

Fix patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 17fb954e..0663405b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -144,6 +144,29 @@ node --test ./javascript/usearch.test.js
 npm publish
 ```
 
+To compile for AWS Lambda you'd need to recompile the binding.
+You can test the setup locally, overriding some of the compilation variables in Docker image:
+
+```Dockerfile
+FROM public.ecr.aws/lambda/nodejs:18-x86_64
+RUN npm init -y
+RUN yum install tar git python3 cmake gcc-c++ -y && yum groupinstall "Development Tools" -y
+
+# Assuming AWS Linux 2 uses old compilers:
+ENV USEARCH_USE_FP16LIB 1
+ENV DUSEARCH_USE_SIMSIMD 1
+ENV SIMSIMD_TARGET_X86_AVX2 1
+ENV SIMSIMD_TARGET_X86_AVX512 0
+ENV SIMSIMD_TARGET_ARM_NEON 1
+ENV SIMSIMD_TARGET_ARM_SVE 0
+
+# For specific PR:
+# RUN npm install --build-from-source unum-cloud/usearch#pull/302/head
+# For specific version:
+# RUN npm install --build-from-source usearch@2.8.8
+RUN npm install --build-from-source usearch
+```
+
 To compile to WebAssembly make sure you have `emscripten` installed and run the following script:
 
 ```sh
diff --git a/include/usearch/index_plugins.hpp b/include/usearch/index_plugins.hpp
index 73af6910..ccf48f00 100644
--- a/include/usearch/index_plugins.hpp
+++ b/include/usearch/index_plugins.hpp
@@ -1419,7 +1419,7 @@ class metric_punned_t {
         if (simd_metric == nullptr)
             return false;
 
-        raw_ptr_ = (punned_ptr_t)simd_metric;
+        std::memcpy(&raw_ptr_, &simd_metric, sizeof(simd_metric));
         isa_kind_ = simd_kind;
         return true;
     }
diff --git a/javascript/README.md b/javascript/README.md
index 3d15f205..d02c3fd7 100644
--- a/javascript/README.md
+++ b/javascript/README.md
@@ -1,15 +1,16 @@
 # USearch for JavaScript
 
+USearch is a high-performance library for building and querying vector search indexes, optimized for Node.js and WASM environments.
+
 ## Installation
 
-USearch is available both for Node.js backend runtime and WASM frontend runtime.
-For the first option, use the conventional `npm install`:
+For Node.js environments, install USearch using `npm`:
 
 ```sh
 npm install usearch
 ```
 
-For latter:
+For front-end applications using WASM, use the Wasmer package manager:
 
 ```sh
 wasmer install unum/usearch
@@ -17,20 +18,71 @@ wasmer install unum/usearch
 
 ## Quickstart
 
+Create an index, add vectors, and perform searches with ease:
+
 ```js
-var index = new usearch.Index({ metric: 'cos', connectivity: 16n, dimensions: 3n })
-index.add(42n, new Float32Array([0.2, 0.6, 0.4]))
-var results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10n)
+const usearch = require('usearch');
+const index = new usearch.Index({ metric: 'cos', connectivity: 16, dimensions: 3 });
+index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
+const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
+
+assert(index.size() === 1);
+assert.deepEqual(results.keys, new BigUint64Array([42n]));
+assert.deepEqual(results.distances, new Float32Array([0]));
 
-assert.equal(index.size(), 1n)
-assert.deepEqual(results.keys, new BigUint64Array([42n]))
-assert.deepEqual(results.distances, new Float32Array([0]))
+index.remove(42n);
 ```
 
 ## Serialization
 
+Persist and restore your index with serialization methods:
+
+```js
+index.save('index.usearch'); // Save the index to a file
+index.load('index.usearch'); // Load the index from a file
+index.view('index.usearch'); // View the index from a file without loading into memory
+```
+
+## Advanced Index Configuration
+
+Customize your index with additional configuration options:
+
+```js
+const index = new usearch.Index({
+  dimensions: 128,
+  metric: 'ip',
+  quantization: 'f32',
+  connectivity: 10,
+  expansion_add: 5,
+  expansion_search: 3,
+  multi: true
+});
+```
+
+## Batch Operations
+
+Process multiple vectors at once for efficiency.
+For performance reasons we prefer a flattened `TypedArray` over an Array of Arrays:
+
+```js
+const keys = new BigUint64Array([15n, 16n]);
+const vectors = new Float32Array([10, 20, 10, 25]);
+index.add(keys, vectors);
+```
+
+Retrieve batch search results:
+
+```js
+const batchResults = index.search(vectors, 2);
+const firstMatch = batchResults.get(0);
+```
+
+## Index Introspection
+
+Inspect and interact with the index:
+
 ```js
-index.save('index.usearch')
-index.load('index.usearch')
-index.view('index.usearch')
+const dimensions = index.dimensions(); // Get the number of dimensions
+const containsKey = index.contains(42n); // Check if a key is in the index
+const count = index.count(42n); // Get the count of vectors for a key
 ```
diff --git a/javascript/lib.cpp b/javascript/lib.cpp
index bfaae93b..e20bd307 100644
--- a/javascript/lib.cpp
+++ b/javascript/lib.cpp
@@ -181,7 +181,7 @@ Napi::Value CompiledIndex::Search(Napi::CallbackInfo const& ctx) {
     std::size_t tasks = queries.ElementLength() / native_->dimensions();
     std::size_t wanted = napi_argument_to_size(ctx[1]);
 
-    auto run_parallel = [&](auto vectors) {
+    auto run_parallel = [&](auto vectors) -> Napi::Value {
         Napi::Array result_js = Napi::Array::New(env, 3);
         Napi::BigUint64Array matches_js = Napi::BigUint64Array::New(env, tasks * wanted);
         Napi::Float32Array distances_js = Napi::Float32Array::New(env, tasks * wanted);
@@ -192,21 +192,29 @@ Napi::Value CompiledIndex::Search(Napi::CallbackInfo const& ctx) {
         auto counts_data = counts_js.Data();
 
         try {
+            bool failed = false;
             executor_stl_t executor;
             executor.fixed(tasks, [&](std::size_t /*thread_idx*/, std::size_t task_idx) {
                 auto result = native_->search(vectors + task_idx * native_->dimensions(), wanted);
                 if (!result) {
-                    // Handle the error appropriately
-                    // For example, log the error or set some flag in the result_js object
+                    failed = true;
+                    Napi::TypeError::New(env, result.error.release()).ThrowAsJavaScriptException();
                 } else {
                     counts_data[task_idx] = result.dump_to(matches_data + task_idx * native_->dimensions(),
                                                            distances_data + task_idx * native_->dimensions());
                 }
             });
+
+            if (failed)
+                return env.Null();
+
         } catch (std::bad_alloc const&) {
             Napi::TypeError::New(env, "Out of memory").ThrowAsJavaScriptException();
+            return env.Null();
+
         } catch (...) {
             Napi::TypeError::New(env, "Search failed").ThrowAsJavaScriptException();
+            return env.Null();
         }
 
         result_js.Set(0u, matches_js);
diff --git a/javascript/usearch.js b/javascript/usearch.js
index 2daaed5c..3c6f7292 100644
--- a/javascript/usearch.js
+++ b/javascript/usearch.js
@@ -247,10 +247,12 @@ class Index {
         // Call the compiled method and create Matches or BatchMatches object with the result
         const result = this._compiledIndex.search(normalizedVectors, k);
         const countInQueries = normalizedVectors.length / Number(this._compiledIndex.dimensions());
+        const batchMatches = new BatchMatches(result[0], result[1], result[2], k);
+
         if (countInQueries === 1) {
-            return new Matches(result[0], result[1]);
+            return batchMatches.get(0);
         } else {
-            return new BatchMatches(result[0], result[1], result[2], k);
+            return batchMatches;
         }
     }
 
diff --git a/simsimd b/simsimd
index 82e86e37..ab2534dd 160000
--- a/simsimd
+++ b/simsimd
@@ -1,1 +1,1 @@
-Subproject commit 82e86e37389e454636bd83b5027b3246ea30c7a7
+Subproject commit ab2534dd2a725f98bbeb1e976820279ab5928f47


Test patch:
diff --git a/javascript/usearch.test.js b/javascript/usearch.test.js
index 5f0fffa4..c86e9f18 100644
--- a/javascript/usearch.test.js
+++ b/javascript/usearch.test.js
@@ -36,6 +36,21 @@ test('Batch operations', () => {
     assert.deepEqual(results.distances, new Float32Array([45, 130]), 'distances should be 45 and 130');
 });
 
+test("Expected results", () => {
+    var index = new usearch.Index({
+        metric: "cos",
+        connectivity: 16,
+        dimensions: 3,
+    });
+    index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
+    var results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
+
+    assert.equal(index.size(), 1);
+    assert.deepEqual(results.keys, new BigUint64Array([42n]));
+    assert.deepEqual(results.distances, new Float32Array([0]));
+});
+
+
 
 test('Operations with invalid values', () => {
     const indexBatch = new usearch.Index(2, 'l2sq');