Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
50
npm/wasm/.npmignore
Normal file
50
npm/wasm/.npmignore
Normal file
@@ -0,0 +1,50 @@
|
||||
# Source files
|
||||
src/
|
||||
*.ts
|
||||
!*.d.ts
|
||||
|
||||
# Build config
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
||||
wasm-pack.log
|
||||
|
||||
# Development
|
||||
node_modules/
|
||||
.git/
|
||||
.github/
|
||||
.gitignore
|
||||
*.test.js
|
||||
*.test.ts
|
||||
*.spec.js
|
||||
*.spec.ts
|
||||
|
||||
# Logs and temp files
|
||||
*.log
|
||||
*.tmp
|
||||
.DS_Store
|
||||
.cache/
|
||||
*.tsbuildinfo
|
||||
|
||||
# CI/CD
|
||||
.travis.yml
|
||||
.gitlab-ci.yml
|
||||
azure-pipelines.yml
|
||||
.circleci/
|
||||
|
||||
# Documentation (keep README.md)
|
||||
docs/
|
||||
*.md
|
||||
!README.md
|
||||
!pkg/README.md
|
||||
!pkg-node/README.md
|
||||
|
||||
# Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# WASM build artifacts to exclude
|
||||
target/
|
||||
Cargo.lock
|
||||
21
npm/wasm/LICENSE
Normal file
21
npm/wasm/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 rUv
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
263
npm/wasm/README.md
Normal file
263
npm/wasm/README.md
Normal file
@@ -0,0 +1,263 @@
|
||||
# @ruvector/wasm
|
||||
|
||||
WebAssembly bindings for Ruvector - High-performance vector database for browsers and Node.js.
|
||||
|
||||
## Features
|
||||
|
||||
- 🚀 **High Performance**: SIMD-accelerated vector operations
|
||||
- 🌐 **Universal**: Works in browsers and Node.js
|
||||
- 🎯 **Multiple Distance Metrics**: Cosine, Euclidean, Dot Product, Manhattan
|
||||
- 🔍 **Fast Search**: HNSW indexing for approximate nearest neighbor search
|
||||
- 💾 **Persistent Storage**: IndexedDB (browser) and file system (Node.js)
|
||||
- 🦀 **Rust-powered**: Built with Rust and WebAssembly
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @ruvector/wasm
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Browser
|
||||
|
||||
```javascript
|
||||
import { VectorDB } from '@ruvector/wasm/browser';
|
||||
|
||||
// Create database
|
||||
const db = new VectorDB({ dimensions: 128 });
|
||||
await db.init();
|
||||
|
||||
// Insert vectors
|
||||
const vector = new Float32Array(128).fill(0.5);
|
||||
const id = db.insert(vector, 'my-vector', { label: 'example' });
|
||||
|
||||
// Search
|
||||
const results = db.search(vector, 10);
|
||||
console.log(results);
|
||||
|
||||
// Save to IndexedDB
|
||||
await db.saveToIndexedDB();
|
||||
```
|
||||
|
||||
### Node.js
|
||||
|
||||
```javascript
|
||||
import { VectorDB } from '@ruvector/wasm/node';
|
||||
|
||||
// Create database
|
||||
const db = new VectorDB({ dimensions: 128 });
|
||||
await db.init();
|
||||
|
||||
// Insert vectors
|
||||
const vector = new Float32Array(128).fill(0.5);
|
||||
const id = db.insert(vector, 'my-vector', { label: 'example' });
|
||||
|
||||
// Search
|
||||
const results = db.search(vector, 10);
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
### Universal (Auto-detect)
|
||||
|
||||
```javascript
|
||||
import { VectorDB } from '@ruvector/wasm';
|
||||
|
||||
// Works in both browser and Node.js
|
||||
const db = new VectorDB({ dimensions: 128 });
|
||||
await db.init();
|
||||
|
||||
const vector = new Float32Array(128).fill(0.5);
|
||||
const id = db.insert(vector);
|
||||
const results = db.search(vector, 10);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### VectorDB
|
||||
|
||||
#### Constructor
|
||||
|
||||
```typescript
|
||||
new VectorDB(options: DbOptions)
|
||||
```
|
||||
|
||||
Options:
|
||||
- `dimensions: number` - Vector dimensions (required)
|
||||
- `metric?: 'euclidean' | 'cosine' | 'dotproduct' | 'manhattan'` - Distance metric (default: 'cosine')
|
||||
- `useHnsw?: boolean` - Use HNSW index (default: true)
|
||||
|
||||
#### Methods
|
||||
|
||||
##### init()
|
||||
|
||||
Initialize the database (must be called before use).
|
||||
|
||||
```typescript
|
||||
await db.init(): Promise<void>
|
||||
```
|
||||
|
||||
##### insert()
|
||||
|
||||
Insert a single vector.
|
||||
|
||||
```typescript
|
||||
db.insert(
|
||||
vector: Float32Array | number[],
|
||||
id?: string,
|
||||
metadata?: Record<string, any>
|
||||
): string
|
||||
```
|
||||
|
||||
##### insertBatch()
|
||||
|
||||
Insert multiple vectors efficiently.
|
||||
|
||||
```typescript
|
||||
db.insertBatch(entries: VectorEntry[]): string[]
|
||||
```
|
||||
|
||||
##### search()
|
||||
|
||||
Search for similar vectors.
|
||||
|
||||
```typescript
|
||||
db.search(
|
||||
query: Float32Array | number[],
|
||||
k: number,
|
||||
filter?: Record<string, any>
|
||||
): SearchResult[]
|
||||
```
|
||||
|
||||
##### delete()
|
||||
|
||||
Delete a vector by ID.
|
||||
|
||||
```typescript
|
||||
db.delete(id: string): boolean
|
||||
```
|
||||
|
||||
##### get()
|
||||
|
||||
Get a vector by ID.
|
||||
|
||||
```typescript
|
||||
db.get(id: string): VectorEntry | null
|
||||
```
|
||||
|
||||
##### len()
|
||||
|
||||
Get the number of vectors.
|
||||
|
||||
```typescript
|
||||
db.len(): number
|
||||
```
|
||||
|
||||
##### isEmpty()
|
||||
|
||||
Check if database is empty.
|
||||
|
||||
```typescript
|
||||
db.isEmpty(): boolean
|
||||
```
|
||||
|
||||
##### getDimensions()
|
||||
|
||||
Get vector dimensions.
|
||||
|
||||
```typescript
|
||||
db.getDimensions(): number
|
||||
```
|
||||
|
||||
##### save()
|
||||
|
||||
Save database to persistent storage.
|
||||
|
||||
```typescript
|
||||
await db.save(path?: string): Promise<void>
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
#### detectSIMD()
|
||||
|
||||
Check if SIMD is supported.
|
||||
|
||||
```typescript
|
||||
const hasSIMD = await detectSIMD();
|
||||
```
|
||||
|
||||
#### version()
|
||||
|
||||
Get library version.
|
||||
|
||||
```typescript
|
||||
const ver = await version();
|
||||
```
|
||||
|
||||
#### benchmark()
|
||||
|
||||
Run performance benchmark.
|
||||
|
||||
```typescript
|
||||
const opsPerSec = await benchmark('insert', 1000, 128);
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
### VectorEntry
|
||||
|
||||
```typescript
|
||||
interface VectorEntry {
|
||||
id?: string;
|
||||
vector: Float32Array | number[];
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
### SearchResult
|
||||
|
||||
```typescript
|
||||
interface SearchResult {
|
||||
id: string;
|
||||
score: number;
|
||||
vector?: Float32Array;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
### DbOptions
|
||||
|
||||
```typescript
|
||||
interface DbOptions {
|
||||
dimensions: number;
|
||||
metric?: 'euclidean' | 'cosine' | 'dotproduct' | 'manhattan';
|
||||
useHnsw?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
Ruvector WASM delivers exceptional performance:
|
||||
|
||||
- **SIMD Acceleration**: Up to 4x faster with WebAssembly SIMD
|
||||
- **HNSW Index**: Sub-linear search complexity
|
||||
- **Zero-copy**: Efficient memory usage with transferable objects
|
||||
- **Batch Operations**: Optimized bulk inserts
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- Chrome 91+ (SIMD support)
|
||||
- Firefox 89+ (SIMD support)
|
||||
- Safari 16.4+ (SIMD support)
|
||||
- Edge 91+ (SIMD support)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Links
|
||||
|
||||
- [GitHub Repository](https://github.com/ruvnet/ruvector)
|
||||
- [Documentation](https://github.com/ruvnet/ruvector#readme)
|
||||
- [Issues](https://github.com/ruvnet/ruvector/issues)
|
||||
75
npm/wasm/package.json
Normal file
75
npm/wasm/package.json
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@ruvector/wasm",
|
||||
"version": "0.1.1",
|
||||
"description": "WebAssembly bindings for Ruvector - High-performance vector database for browsers and Node.js",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"browser": {
|
||||
"import": "./dist/browser.mjs",
|
||||
"require": "./dist/browser.js"
|
||||
},
|
||||
"node": {
|
||||
"import": "./dist/node.mjs",
|
||||
"require": "./dist/node.js"
|
||||
},
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./browser": {
|
||||
"types": "./dist/browser.d.ts",
|
||||
"import": "./dist/browser.mjs",
|
||||
"require": "./dist/browser.js"
|
||||
},
|
||||
"./node": {
|
||||
"types": "./dist/node.d.ts",
|
||||
"import": "./dist/node.mjs",
|
||||
"require": "./dist/node.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"pkg",
|
||||
"pkg-node",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"build:wasm": "npm run build:wasm:bundler && npm run build:wasm:node",
|
||||
"build:wasm:bundler": "cd ../../crates/ruvector-wasm && wasm-pack build --target bundler --out-dir ../../npm/wasm/pkg",
|
||||
"build:wasm:node": "cd ../../crates/ruvector-wasm && wasm-pack build --target nodejs --out-dir ../../npm/wasm/pkg-node",
|
||||
"build:ts": "tsc && tsc -p tsconfig.esm.json",
|
||||
"build": "npm run build:wasm && npm run build:ts",
|
||||
"test": "node --test dist/index.test.js",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"vector",
|
||||
"database",
|
||||
"wasm",
|
||||
"webassembly",
|
||||
"embeddings",
|
||||
"similarity-search",
|
||||
"machine-learning",
|
||||
"ai",
|
||||
"browser",
|
||||
"rust"
|
||||
],
|
||||
"author": "Ruvector Team",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ruvnet/ruvector.git",
|
||||
"directory": "npm/wasm"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ruvnet/ruvector/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ruvnet/ruvector#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.19.25",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
29
npm/wasm/src/browser.d.ts
vendored
Normal file
29
npm/wasm/src/browser.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Browser-specific exports for @ruvector/wasm
|
||||
*/
|
||||
import type { VectorEntry, SearchResult, DbOptions } from './index';
|
||||
/**
|
||||
* VectorDB class for browser
|
||||
*/
|
||||
export declare class VectorDB {
|
||||
private db;
|
||||
private dimensions;
|
||||
constructor(options: DbOptions);
|
||||
init(): Promise<void>;
|
||||
insert(vector: Float32Array | number[], id?: string, metadata?: Record<string, any>): string;
|
||||
insertBatch(entries: VectorEntry[]): string[];
|
||||
search(query: Float32Array | number[], k: number, filter?: Record<string, any>): SearchResult[];
|
||||
delete(id: string): boolean;
|
||||
get(id: string): VectorEntry | null;
|
||||
len(): number;
|
||||
isEmpty(): boolean;
|
||||
getDimensions(): number;
|
||||
saveToIndexedDB(): Promise<void>;
|
||||
static loadFromIndexedDB(dbName: string, options: DbOptions): Promise<VectorDB>;
|
||||
}
|
||||
export declare function detectSIMD(): Promise<boolean>;
|
||||
export declare function version(): Promise<string>;
|
||||
export declare function benchmark(name: string, iterations: number, dimensions: number): Promise<number>;
|
||||
export type { VectorEntry, SearchResult, DbOptions };
|
||||
export default VectorDB;
|
||||
//# sourceMappingURL=browser.d.ts.map
|
||||
1
npm/wasm/src/browser.d.ts.map
Normal file
1
npm/wasm/src/browser.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAepE;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,SAAS;IAIxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAM5F,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE;IAU7C,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,YAAY,EAAE;IAY/F,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAK3B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOnC,GAAG,IAAI,MAAM;IAKb,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,MAAM;IAIjB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;WAKzB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;CAMtF;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAGnD;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAG/C;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrG;AAED,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AACrD,eAAe,QAAQ,CAAC"}
|
||||
145
npm/wasm/src/browser.js
Normal file
145
npm/wasm/src/browser.js
Normal file
@@ -0,0 +1,145 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Browser-specific exports for @ruvector/wasm
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VectorDB = void 0;
|
||||
exports.detectSIMD = detectSIMD;
|
||||
exports.version = version;
|
||||
exports.benchmark = benchmark;
|
||||
let wasmModule = null;
|
||||
/**
|
||||
* Initialize WASM module for browser
|
||||
*/
|
||||
async function initWasm() {
|
||||
if (!wasmModule) {
|
||||
wasmModule = await Promise.resolve().then(() => __importStar(require('../pkg/ruvector_wasm.js')));
|
||||
await wasmModule.default();
|
||||
}
|
||||
return wasmModule;
|
||||
}
|
||||
/**
|
||||
* VectorDB class for browser
|
||||
*/
|
||||
class VectorDB {
|
||||
constructor(options) {
|
||||
this.dimensions = options.dimensions;
|
||||
}
|
||||
async init() {
|
||||
const module = await initWasm();
|
||||
this.db = new module.VectorDB(this.dimensions, 'cosine', true);
|
||||
}
|
||||
insert(vector, id, metadata) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const vectorArray = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
||||
return this.db.insert(vectorArray, id, metadata);
|
||||
}
|
||||
insertBatch(entries) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const processedEntries = entries.map(entry => ({
|
||||
id: entry.id,
|
||||
vector: entry.vector instanceof Float32Array ? entry.vector : new Float32Array(entry.vector),
|
||||
metadata: entry.metadata
|
||||
}));
|
||||
return this.db.insertBatch(processedEntries);
|
||||
}
|
||||
search(query, k, filter) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const queryArray = query instanceof Float32Array ? query : new Float32Array(query);
|
||||
const results = this.db.search(queryArray, k, filter);
|
||||
return results.map((r) => ({
|
||||
id: r.id,
|
||||
score: r.score,
|
||||
vector: r.vector,
|
||||
metadata: r.metadata
|
||||
}));
|
||||
}
|
||||
delete(id) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.delete(id);
|
||||
}
|
||||
get(id) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const entry = this.db.get(id);
|
||||
if (!entry)
|
||||
return null;
|
||||
return { id: entry.id, vector: entry.vector, metadata: entry.metadata };
|
||||
}
|
||||
len() {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.len();
|
||||
}
|
||||
isEmpty() {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.isEmpty();
|
||||
}
|
||||
getDimensions() {
|
||||
return this.dimensions;
|
||||
}
|
||||
async saveToIndexedDB() {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
await this.db.saveToIndexedDB();
|
||||
}
|
||||
static async loadFromIndexedDB(dbName, options) {
|
||||
const db = new VectorDB(options);
|
||||
await db.init();
|
||||
await db.db.loadFromIndexedDB(dbName);
|
||||
return db;
|
||||
}
|
||||
}
|
||||
exports.VectorDB = VectorDB;
|
||||
async function detectSIMD() {
|
||||
const module = await initWasm();
|
||||
return module.detectSIMD();
|
||||
}
|
||||
async function version() {
|
||||
const module = await initWasm();
|
||||
return module.version();
|
||||
}
|
||||
async function benchmark(name, iterations, dimensions) {
|
||||
const module = await initWasm();
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
}
|
||||
exports.default = VectorDB;
|
||||
//# sourceMappingURL=browser.js.map
|
||||
1
npm/wasm/src/browser.js.map
Normal file
1
npm/wasm/src/browser.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"browser.js","sourceRoot":"","sources":["browser.ts"],"names":[],"mappings":"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"}
|
||||
123
npm/wasm/src/browser.ts
Normal file
123
npm/wasm/src/browser.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Browser-specific exports for @ruvector/wasm
|
||||
*/
|
||||
|
||||
import type { VectorEntry, SearchResult, DbOptions } from './index';
|
||||
|
||||
let wasmModule: any = null;
|
||||
|
||||
/**
|
||||
* Initialize WASM module for browser
|
||||
*/
|
||||
async function initWasm() {
|
||||
if (!wasmModule) {
|
||||
wasmModule = await import('../pkg/ruvector_wasm.js');
|
||||
await wasmModule.default();
|
||||
}
|
||||
return wasmModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* VectorDB class for browser
|
||||
*/
|
||||
export class VectorDB {
|
||||
private db: any;
|
||||
private dimensions: number;
|
||||
|
||||
constructor(options: DbOptions) {
|
||||
this.dimensions = options.dimensions;
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const module = await initWasm();
|
||||
this.db = new module.VectorDB(
|
||||
this.dimensions,
|
||||
'cosine',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
insert(vector: Float32Array | number[], id?: string, metadata?: Record<string, any>): string {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const vectorArray = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
||||
return this.db.insert(vectorArray, id, metadata);
|
||||
}
|
||||
|
||||
insertBatch(entries: VectorEntry[]): string[] {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const processedEntries = entries.map(entry => ({
|
||||
id: entry.id,
|
||||
vector: entry.vector instanceof Float32Array ? entry.vector : new Float32Array(entry.vector),
|
||||
metadata: entry.metadata
|
||||
}));
|
||||
return this.db.insertBatch(processedEntries);
|
||||
}
|
||||
|
||||
search(query: Float32Array | number[], k: number, filter?: Record<string, any>): SearchResult[] {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const queryArray = query instanceof Float32Array ? query : new Float32Array(query);
|
||||
const results = this.db.search(queryArray, k, filter);
|
||||
return results.map((r: any) => ({
|
||||
id: r.id,
|
||||
score: r.score,
|
||||
vector: r.vector,
|
||||
metadata: r.metadata
|
||||
}));
|
||||
}
|
||||
|
||||
delete(id: string): boolean {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.delete(id);
|
||||
}
|
||||
|
||||
get(id: string): VectorEntry | null {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const entry = this.db.get(id);
|
||||
if (!entry) return null;
|
||||
return { id: entry.id, vector: entry.vector, metadata: entry.metadata };
|
||||
}
|
||||
|
||||
len(): number {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.len();
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.isEmpty();
|
||||
}
|
||||
|
||||
getDimensions(): number {
|
||||
return this.dimensions;
|
||||
}
|
||||
|
||||
async saveToIndexedDB(): Promise<void> {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
await this.db.saveToIndexedDB();
|
||||
}
|
||||
|
||||
static async loadFromIndexedDB(dbName: string, options: DbOptions): Promise<VectorDB> {
|
||||
const db = new VectorDB(options);
|
||||
await db.init();
|
||||
await db.db.loadFromIndexedDB(dbName);
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectSIMD(): Promise<boolean> {
|
||||
const module = await initWasm();
|
||||
return module.detectSIMD();
|
||||
}
|
||||
|
||||
export async function version(): Promise<string> {
|
||||
const module = await initWasm();
|
||||
return module.version();
|
||||
}
|
||||
|
||||
export async function benchmark(name: string, iterations: number, dimensions: number): Promise<number> {
|
||||
const module = await initWasm();
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
}
|
||||
|
||||
export type { VectorEntry, SearchResult, DbOptions };
|
||||
export default VectorDB;
|
||||
1
npm/wasm/src/index.d.ts.map
Normal file
1
npm/wasm/src/index.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,SAAS;IAI9B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAY5F;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE;IAgB7C;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,YAAY,EAAE;IAoB/F;;OAEG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQ3B;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAenC;;OAEG;IACH,GAAG,IAAI,MAAM;IAQb;;OAEG;IACH,OAAO,IAAI,OAAO;IAQlB;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;OAIG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC;;OAEG;WACU,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;CAavE;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAenD;AAED;;GAEG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAe/C;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAejB;AAGD,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AAGrD,eAAe,QAAQ,CAAC"}
|
||||
1
npm/wasm/src/index.js.map
Normal file
1
npm/wasm/src/index.js.map
Normal file
File diff suppressed because one or more lines are too long
125
npm/wasm/src/index.test.ts
Normal file
125
npm/wasm/src/index.test.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Tests for @ruvector/wasm
|
||||
*/
|
||||
|
||||
import { VectorDB, detectSIMD, version } from './node';
|
||||
|
||||
async function testBasicOperations() {
|
||||
console.log('Testing basic VectorDB operations...');
|
||||
|
||||
// Create database
|
||||
const db = new VectorDB({ dimensions: 3 });
|
||||
await db.init();
|
||||
|
||||
// Test insert
|
||||
const vector1 = new Float32Array([1.0, 0.0, 0.0]);
|
||||
const id1 = db.insert(vector1, 'vec1', { label: 'test1' });
|
||||
console.log('✓ Insert single vector:', id1);
|
||||
|
||||
// Test batch insert
|
||||
const entries = [
|
||||
{ vector: [0.0, 1.0, 0.0], id: 'vec2', metadata: { label: 'test2' } },
|
||||
{ vector: [0.0, 0.0, 1.0], id: 'vec3', metadata: { label: 'test3' } },
|
||||
];
|
||||
const ids = db.insertBatch(entries);
|
||||
console.log('✓ Batch insert:', ids);
|
||||
|
||||
// Test len
|
||||
const count = db.len();
|
||||
console.log('✓ Vector count:', count);
|
||||
if (count !== 3) throw new Error('Expected 3 vectors');
|
||||
|
||||
// Test search
|
||||
const query = new Float32Array([1.0, 0.1, 0.0]);
|
||||
const results = db.search(query, 2);
|
||||
console.log('✓ Search results:', results.length);
|
||||
if (results.length !== 2) throw new Error('Expected 2 results');
|
||||
|
||||
// Test get
|
||||
const entry = db.get('vec1');
|
||||
console.log('✓ Get by ID:', entry?.id);
|
||||
if (!entry || entry.id !== 'vec1') throw new Error('Expected vec1');
|
||||
|
||||
// Test delete
|
||||
const deleted = db.delete('vec1');
|
||||
console.log('✓ Delete:', deleted);
|
||||
if (!deleted) throw new Error('Expected delete to succeed');
|
||||
|
||||
// Test isEmpty
|
||||
const isEmpty = db.isEmpty();
|
||||
console.log('✓ Is empty:', isEmpty);
|
||||
if (isEmpty) throw new Error('Expected database to not be empty');
|
||||
|
||||
// Test getDimensions
|
||||
const dims = db.getDimensions();
|
||||
console.log('✓ Dimensions:', dims);
|
||||
if (dims !== 3) throw new Error('Expected 3 dimensions');
|
||||
|
||||
console.log('✓ All basic operations passed!\n');
|
||||
}
|
||||
|
||||
async function testUtilities() {
|
||||
console.log('Testing utility functions...');
|
||||
|
||||
// Test version
|
||||
const ver = await version();
|
||||
console.log('✓ Version:', ver);
|
||||
|
||||
// Test SIMD detection
|
||||
const hasSIMD = await detectSIMD();
|
||||
console.log('✓ SIMD support:', hasSIMD);
|
||||
|
||||
console.log('✓ All utility tests passed!\n');
|
||||
}
|
||||
|
||||
async function testErrorHandling() {
|
||||
console.log('Testing error handling...');
|
||||
|
||||
try {
|
||||
const db = new VectorDB({ dimensions: 3 });
|
||||
// Should throw error if not initialized
|
||||
db.insert(new Float32Array([1, 2, 3]));
|
||||
throw new Error('Expected error when using uninitialized database');
|
||||
} catch (err: any) {
|
||||
if (err.message.includes('not initialized')) {
|
||||
console.log('✓ Uninitialized database error');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const db = new VectorDB({ dimensions: 3 });
|
||||
await db.init();
|
||||
// Should handle dimension mismatch
|
||||
const wrongVector = new Float32Array([1, 2, 3, 4, 5]);
|
||||
db.search(wrongVector, 5);
|
||||
throw new Error('Expected dimension mismatch error');
|
||||
} catch (err: any) {
|
||||
if (err.message.includes('dimension')) {
|
||||
console.log('✓ Dimension mismatch error');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✓ All error handling tests passed!\n');
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
console.log('Starting @ruvector/wasm tests...\n');
|
||||
|
||||
try {
|
||||
await testUtilities();
|
||||
await testBasicOperations();
|
||||
await testErrorHandling();
|
||||
|
||||
console.log('✅ ALL TESTS PASSED!');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ TEST FAILED:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runAllTests();
|
||||
302
npm/wasm/src/index.ts
Normal file
302
npm/wasm/src/index.ts
Normal file
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* @ruvector/wasm - WebAssembly bindings for Ruvector
|
||||
*
|
||||
* High-performance vector database for browsers and Node.js
|
||||
* Features:
|
||||
* - SIMD acceleration (when available)
|
||||
* - Multiple distance metrics (cosine, euclidean, dot product, manhattan)
|
||||
* - HNSW indexing for fast approximate nearest neighbor search
|
||||
* - Zero-copy operations via transferable objects
|
||||
* - IndexedDB persistence (browser)
|
||||
* - File system persistence (Node.js)
|
||||
*/
|
||||
|
||||
// Auto-detect environment and load appropriate WASM module
|
||||
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||
const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
|
||||
|
||||
/**
|
||||
* Vector entry interface
|
||||
*/
|
||||
export interface VectorEntry {
|
||||
id?: string;
|
||||
vector: Float32Array | number[];
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search result interface
|
||||
*/
|
||||
export interface SearchResult {
|
||||
id: string;
|
||||
score: number;
|
||||
vector?: Float32Array;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database options
|
||||
*/
|
||||
export interface DbOptions {
|
||||
dimensions: number;
|
||||
metric?: 'euclidean' | 'cosine' | 'dotproduct' | 'manhattan';
|
||||
useHnsw?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* VectorDB class - unified interface for browser and Node.js
|
||||
*/
|
||||
export class VectorDB {
|
||||
private wasmModule: any;
|
||||
private db: any;
|
||||
private dimensions: number;
|
||||
|
||||
constructor(options: DbOptions) {
|
||||
this.dimensions = options.dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the database (async)
|
||||
* Must be called before using the database
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
if (isBrowser) {
|
||||
this.wasmModule = await import('../pkg/ruvector_wasm.js');
|
||||
await this.wasmModule.default();
|
||||
this.db = new this.wasmModule.VectorDB(
|
||||
this.dimensions,
|
||||
'cosine',
|
||||
true
|
||||
);
|
||||
} else if (isNode) {
|
||||
this.wasmModule = await import('../pkg-node/ruvector_wasm.js');
|
||||
this.db = new this.wasmModule.VectorDB(
|
||||
this.dimensions,
|
||||
'cosine',
|
||||
true
|
||||
);
|
||||
} else {
|
||||
throw new Error('Unsupported environment');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a single vector
|
||||
*/
|
||||
insert(vector: Float32Array | number[], id?: string, metadata?: Record<string, any>): string {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
const vectorArray = vector instanceof Float32Array
|
||||
? vector
|
||||
: new Float32Array(vector);
|
||||
|
||||
return this.db.insert(vectorArray, id, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert multiple vectors in a batch
|
||||
*/
|
||||
insertBatch(entries: VectorEntry[]): string[] {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
const processedEntries = entries.map(entry => ({
|
||||
id: entry.id,
|
||||
vector: entry.vector instanceof Float32Array
|
||||
? entry.vector
|
||||
: new Float32Array(entry.vector),
|
||||
metadata: entry.metadata
|
||||
}));
|
||||
|
||||
return this.db.insertBatch(processedEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for similar vectors
|
||||
*/
|
||||
search(query: Float32Array | number[], k: number, filter?: Record<string, any>): SearchResult[] {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
const queryArray = query instanceof Float32Array
|
||||
? query
|
||||
: new Float32Array(query);
|
||||
|
||||
const results = this.db.search(queryArray, k, filter);
|
||||
|
||||
// Convert WASM results to plain objects
|
||||
return results.map((r: any) => ({
|
||||
id: r.id,
|
||||
score: r.score,
|
||||
vector: r.vector,
|
||||
metadata: r.metadata
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a vector by ID
|
||||
*/
|
||||
delete(id: string): boolean {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
return this.db.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a vector by ID
|
||||
*/
|
||||
get(id: string): VectorEntry | null {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
const entry = this.db.get(id);
|
||||
if (!entry) return null;
|
||||
|
||||
return {
|
||||
id: entry.id,
|
||||
vector: entry.vector,
|
||||
metadata: entry.metadata
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of vectors in the database
|
||||
*/
|
||||
len(): number {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
return this.db.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the database is empty
|
||||
*/
|
||||
isEmpty(): boolean {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
return this.db.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database dimensions
|
||||
*/
|
||||
getDimensions(): number {
|
||||
return this.dimensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save database to persistent storage
|
||||
* - Browser: IndexedDB
|
||||
* - Node.js: File system
|
||||
*/
|
||||
async save(path?: string): Promise<void> {
|
||||
if (!this.db) {
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
}
|
||||
|
||||
if (isBrowser) {
|
||||
await this.db.saveToIndexedDB();
|
||||
} else if (isNode) {
|
||||
// Node.js file system persistence would go here
|
||||
console.warn('Node.js persistence not yet implemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load database from persistent storage
|
||||
*/
|
||||
static async load(path: string, options: DbOptions): Promise<VectorDB> {
|
||||
const db = new VectorDB(options);
|
||||
await db.init();
|
||||
|
||||
if (isBrowser) {
|
||||
await db.db.loadFromIndexedDB(path);
|
||||
} else if (isNode) {
|
||||
// Node.js file system loading would go here
|
||||
console.warn('Node.js persistence not yet implemented');
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect SIMD support in current environment
|
||||
*/
|
||||
export async function detectSIMD(): Promise<boolean> {
|
||||
try {
|
||||
if (isBrowser) {
|
||||
const module = await import('../pkg/ruvector_wasm.js');
|
||||
await module.default();
|
||||
return module.detectSIMD();
|
||||
} else if (isNode) {
|
||||
const module = await import('../pkg-node/ruvector_wasm.js');
|
||||
return module.detectSIMD();
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('Error detecting SIMD:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get version information
|
||||
*/
|
||||
export async function version(): Promise<string> {
|
||||
try {
|
||||
if (isBrowser) {
|
||||
const module = await import('../pkg/ruvector_wasm.js');
|
||||
await module.default();
|
||||
return module.version();
|
||||
} else if (isNode) {
|
||||
const module = await import('../pkg-node/ruvector_wasm.js');
|
||||
return module.version();
|
||||
}
|
||||
return 'unknown';
|
||||
} catch (error) {
|
||||
console.error('Error getting version:', error);
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a benchmark
|
||||
*/
|
||||
export async function benchmark(
|
||||
name: string,
|
||||
iterations: number,
|
||||
dimensions: number
|
||||
): Promise<number> {
|
||||
try {
|
||||
if (isBrowser) {
|
||||
const module = await import('../pkg/ruvector_wasm.js');
|
||||
await module.default();
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
} else if (isNode) {
|
||||
const module = await import('../pkg-node/ruvector_wasm.js');
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
}
|
||||
return 0;
|
||||
} catch (error) {
|
||||
console.error('Error running benchmark:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Export types
|
||||
export type { DbOptions, VectorEntry, SearchResult };
|
||||
|
||||
// Default export
|
||||
export default VectorDB;
|
||||
29
npm/wasm/src/node.d.ts
vendored
Normal file
29
npm/wasm/src/node.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Node.js-specific exports for @ruvector/wasm
|
||||
*/
|
||||
import type { VectorEntry, SearchResult, DbOptions } from './index';
|
||||
/**
|
||||
* VectorDB class for Node.js
|
||||
*/
|
||||
export declare class VectorDB {
|
||||
private db;
|
||||
private dimensions;
|
||||
constructor(options: DbOptions);
|
||||
init(): Promise<void>;
|
||||
insert(vector: Float32Array | number[], id?: string, metadata?: Record<string, any>): string;
|
||||
insertBatch(entries: VectorEntry[]): string[];
|
||||
search(query: Float32Array | number[], k: number, filter?: Record<string, any>): SearchResult[];
|
||||
delete(id: string): boolean;
|
||||
get(id: string): VectorEntry | null;
|
||||
len(): number;
|
||||
isEmpty(): boolean;
|
||||
getDimensions(): number;
|
||||
saveToFile(path: string): Promise<void>;
|
||||
static loadFromFile(path: string, options: DbOptions): Promise<VectorDB>;
|
||||
}
|
||||
export declare function detectSIMD(): Promise<boolean>;
|
||||
export declare function version(): Promise<string>;
|
||||
export declare function benchmark(name: string, iterations: number, dimensions: number): Promise<number>;
|
||||
export type { VectorEntry, SearchResult, DbOptions };
|
||||
export default VectorDB;
|
||||
//# sourceMappingURL=node.d.ts.map
|
||||
1
npm/wasm/src/node.d.ts.map
Normal file
1
npm/wasm/src/node.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["node.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAcpE;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAM;IAChB,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,EAAE,SAAS;IAIxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM;IAM5F,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,EAAE;IAU7C,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,YAAY,EAAE;IAY/F,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAK3B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAOnC,GAAG,IAAI,MAAM;IAKb,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,MAAM;IAKjB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;WAIhC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC;CAM/E;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAGnD;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAG/C;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrG;AAED,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AACrD,eAAe,QAAQ,CAAC"}
|
||||
143
npm/wasm/src/node.js
Normal file
143
npm/wasm/src/node.js
Normal file
@@ -0,0 +1,143 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Node.js-specific exports for @ruvector/wasm
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VectorDB = void 0;
|
||||
exports.detectSIMD = detectSIMD;
|
||||
exports.version = version;
|
||||
exports.benchmark = benchmark;
|
||||
let wasmModule = null;
|
||||
/**
|
||||
* Initialize WASM module for Node.js
|
||||
*/
|
||||
async function initWasm() {
|
||||
if (!wasmModule) {
|
||||
wasmModule = await Promise.resolve().then(() => __importStar(require('../pkg-node/ruvector_wasm.js')));
|
||||
}
|
||||
return wasmModule;
|
||||
}
|
||||
/**
|
||||
* VectorDB class for Node.js
|
||||
*/
|
||||
class VectorDB {
|
||||
constructor(options) {
|
||||
this.dimensions = options.dimensions;
|
||||
}
|
||||
async init() {
|
||||
const module = await initWasm();
|
||||
this.db = new module.VectorDB(this.dimensions, 'cosine', true);
|
||||
}
|
||||
insert(vector, id, metadata) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const vectorArray = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
||||
return this.db.insert(vectorArray, id, metadata);
|
||||
}
|
||||
insertBatch(entries) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const processedEntries = entries.map(entry => ({
|
||||
id: entry.id,
|
||||
vector: entry.vector instanceof Float32Array ? entry.vector : new Float32Array(entry.vector),
|
||||
metadata: entry.metadata
|
||||
}));
|
||||
return this.db.insertBatch(processedEntries);
|
||||
}
|
||||
search(query, k, filter) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const queryArray = query instanceof Float32Array ? query : new Float32Array(query);
|
||||
const results = this.db.search(queryArray, k, filter);
|
||||
return results.map((r) => ({
|
||||
id: r.id,
|
||||
score: r.score,
|
||||
vector: r.vector,
|
||||
metadata: r.metadata
|
||||
}));
|
||||
}
|
||||
delete(id) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.delete(id);
|
||||
}
|
||||
get(id) {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
const entry = this.db.get(id);
|
||||
if (!entry)
|
||||
return null;
|
||||
return { id: entry.id, vector: entry.vector, metadata: entry.metadata };
|
||||
}
|
||||
len() {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.len();
|
||||
}
|
||||
isEmpty() {
|
||||
if (!this.db)
|
||||
throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.isEmpty();
|
||||
}
|
||||
getDimensions() {
|
||||
return this.dimensions;
|
||||
}
|
||||
// Node.js specific: save to file system
|
||||
async saveToFile(path) {
|
||||
console.warn('Node.js file persistence not yet implemented');
|
||||
}
|
||||
static async loadFromFile(path, options) {
|
||||
const db = new VectorDB(options);
|
||||
await db.init();
|
||||
console.warn('Node.js file persistence not yet implemented');
|
||||
return db;
|
||||
}
|
||||
}
|
||||
exports.VectorDB = VectorDB;
|
||||
async function detectSIMD() {
|
||||
const module = await initWasm();
|
||||
return module.detectSIMD();
|
||||
}
|
||||
async function version() {
|
||||
const module = await initWasm();
|
||||
return module.version();
|
||||
}
|
||||
async function benchmark(name, iterations, dimensions) {
|
||||
const module = await initWasm();
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
}
|
||||
exports.default = VectorDB;
|
||||
//# sourceMappingURL=node.js.map
|
||||
1
npm/wasm/src/node.js.map
Normal file
1
npm/wasm/src/node.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"node.js","sourceRoot":"","sources":["node.ts"],"names":[],"mappings":"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"}
|
||||
122
npm/wasm/src/node.ts
Normal file
122
npm/wasm/src/node.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Node.js-specific exports for @ruvector/wasm
|
||||
*/
|
||||
|
||||
import type { VectorEntry, SearchResult, DbOptions } from './index';
|
||||
|
||||
let wasmModule: any = null;
|
||||
|
||||
/**
|
||||
* Initialize WASM module for Node.js
|
||||
*/
|
||||
async function initWasm() {
|
||||
if (!wasmModule) {
|
||||
wasmModule = await import('../pkg-node/ruvector_wasm.js');
|
||||
}
|
||||
return wasmModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* VectorDB class for Node.js
|
||||
*/
|
||||
export class VectorDB {
|
||||
private db: any;
|
||||
private dimensions: number;
|
||||
|
||||
constructor(options: DbOptions) {
|
||||
this.dimensions = options.dimensions;
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const module = await initWasm();
|
||||
this.db = new module.VectorDB(
|
||||
this.dimensions,
|
||||
'cosine',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
insert(vector: Float32Array | number[], id?: string, metadata?: Record<string, any>): string {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const vectorArray = vector instanceof Float32Array ? vector : new Float32Array(vector);
|
||||
return this.db.insert(vectorArray, id, metadata);
|
||||
}
|
||||
|
||||
insertBatch(entries: VectorEntry[]): string[] {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const processedEntries = entries.map(entry => ({
|
||||
id: entry.id,
|
||||
vector: entry.vector instanceof Float32Array ? entry.vector : new Float32Array(entry.vector),
|
||||
metadata: entry.metadata
|
||||
}));
|
||||
return this.db.insertBatch(processedEntries);
|
||||
}
|
||||
|
||||
search(query: Float32Array | number[], k: number, filter?: Record<string, any>): SearchResult[] {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const queryArray = query instanceof Float32Array ? query : new Float32Array(query);
|
||||
const results = this.db.search(queryArray, k, filter);
|
||||
return results.map((r: any) => ({
|
||||
id: r.id,
|
||||
score: r.score,
|
||||
vector: r.vector,
|
||||
metadata: r.metadata
|
||||
}));
|
||||
}
|
||||
|
||||
delete(id: string): boolean {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.delete(id);
|
||||
}
|
||||
|
||||
get(id: string): VectorEntry | null {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
const entry = this.db.get(id);
|
||||
if (!entry) return null;
|
||||
return { id: entry.id, vector: entry.vector, metadata: entry.metadata };
|
||||
}
|
||||
|
||||
len(): number {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.len();
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
if (!this.db) throw new Error('Database not initialized. Call init() first.');
|
||||
return this.db.isEmpty();
|
||||
}
|
||||
|
||||
getDimensions(): number {
|
||||
return this.dimensions;
|
||||
}
|
||||
|
||||
// Node.js specific: save to file system
|
||||
async saveToFile(path: string): Promise<void> {
|
||||
console.warn('Node.js file persistence not yet implemented');
|
||||
}
|
||||
|
||||
static async loadFromFile(path: string, options: DbOptions): Promise<VectorDB> {
|
||||
const db = new VectorDB(options);
|
||||
await db.init();
|
||||
console.warn('Node.js file persistence not yet implemented');
|
||||
return db;
|
||||
}
|
||||
}
|
||||
|
||||
export async function detectSIMD(): Promise<boolean> {
|
||||
const module = await initWasm();
|
||||
return module.detectSIMD();
|
||||
}
|
||||
|
||||
export async function version(): Promise<string> {
|
||||
const module = await initWasm();
|
||||
return module.version();
|
||||
}
|
||||
|
||||
export async function benchmark(name: string, iterations: number, dimensions: number): Promise<number> {
|
||||
const module = await initWasm();
|
||||
return module.benchmark(name, iterations, dimensions);
|
||||
}
|
||||
|
||||
export type { VectorEntry, SearchResult, DbOptions };
|
||||
export default VectorDB;
|
||||
10
npm/wasm/tsconfig.esm.json
Normal file
10
npm/wasm/tsconfig.esm.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "ES2020",
|
||||
"outDir": "./dist",
|
||||
"declaration": false
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
20
npm/wasm/tsconfig.json
Normal file
20
npm/wasm/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user