# Ruvector NPM Package Architecture Design **Version:** 1.0.0 **Status:** Design Document **Author:** System Architecture Designer **Date:** 2025-11-20 ## Table of Contents 1. [Executive Summary](#executive-summary) 2. [Architecture Overview](#architecture-overview) 3. [Package Structure](#package-structure) 4. [Platform Support Matrix](#platform-support-matrix) 5. [Directory Structure](#directory-structure) 6. [Package Specifications](#package-specifications) 7. [Platform Detection Logic](#platform-detection-logic) 8. [Build & Publish Workflow](#build--publish-workflow) 9. [TypeScript Definitions](#typescript-definitions) 10. [API Consistency](#api-consistency) 11. [Implementation Roadmap](#implementation-roadmap) 12. [ADRs (Architecture Decision Records)](#adrs-architecture-decision-records) --- ## Executive Summary This document outlines a modular npm package architecture for Ruvector, a high-performance Rust-native vector database. The architecture provides: - **Platform-specific optimizations** via NAPI-RS native bindings - **Universal fallback** via WebAssembly for unsupported platforms - **Modular design** allowing users to install only what they need - **Automatic platform detection** with zero-config setup - **Consistent API** across NAPI-RS and WASM implementations ### Key Packages | Package | Purpose | Size | Platforms | |---------|---------|------|-----------| | `ruvector` | Main package with auto-detection | ~50KB | All | | `@ruvector/core` | Native bindings (platform-specific) | ~5-8MB | linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64 | | `@ruvector/wasm` | WebAssembly fallback | ~1.5MB | Universal | | `@ruvector/cli` | CLI tools | ~8MB | linux-x64, linux-arm64, darwin-x64, darwin-arm64, win32-x64 | --- ## Architecture Overview ### Design Principles 1. **Zero-Configuration**: Users install `ruvector` and it just works 2. **Performance-First**: Native bindings preferred, WASM as fallback 3. **Modularity**: Users can opt into specific packages 4. **Consistency**: Same API across native and WASM 5. **Type-Safety**: Full TypeScript support 6. **Bundle Size**: Main package stays minimal (~50KB) ### Architecture Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ ruvector (Main) │ │ - Platform detection │ │ - Auto-loader │ │ - TypeScript definitions │ └───────────────┬─────────────────────────────────────────────┘ │ ├─────────────────┬──────────────────┐ │ │ │ ┌───────▼──────┐ ┌──────▼──────┐ ┌───────▼──────┐ │ @ruvector/ │ │ @ruvector/ │ │ @ruvector/ │ │ core │ │ wasm │ │ cli │ │ │ │ │ │ │ │ NAPI-RS │ │ WebAssembly │ │ CLI Tools │ │ Native │ │ Universal │ │ Standalone │ └──────────────┘ └─────────────┘ └──────────────┘ │ │ │ │ ┌───────▼─────────────────▼────────┐ │ ruvector-core (Rust) │ │ - HNSW indexing │ │ - SIMD optimizations │ │ - Storage engine │ └───────────────────────────────────┘ ``` --- ## Package Structure ### 1. `ruvector` (Main Package) **Purpose**: Zero-config entry point with automatic platform detection and loading. **Features**: - Detects platform and loads appropriate native module - Falls back to WASM if native binding unavailable - Re-exports consistent API - Minimal bundle size (~50KB) - TypeScript definitions included **Dependencies**: ```json { "optionalDependencies": { "@ruvector/core-linux-x64": "^0.1.1", "@ruvector/core-linux-arm64": "^0.1.1", "@ruvector/core-darwin-x64": "^0.1.1", "@ruvector/core-darwin-arm64": "^0.1.1", "@ruvector/core-win32-x64": "^0.1.1" }, "dependencies": { "@ruvector/wasm": "^0.1.1" } } ``` ### 2. `@ruvector/core` (Native Bindings) **Purpose**: High-performance NAPI-RS native bindings with platform-specific builds. **Platform Packages**: - `@ruvector/core-linux-x64` - `@ruvector/core-linux-arm64` - `@ruvector/core-darwin-x64` - `@ruvector/core-darwin-arm64` - `@ruvector/core-win32-x64` **Features**: - Full SIMD optimizations - Native file I/O - Maximum performance - ~5-8MB per platform ### 3. `@ruvector/wasm` (WebAssembly Fallback) **Purpose**: Universal WebAssembly implementation for unsupported platforms and browsers. **Features**: - IndexedDB persistence - Web Worker support - SIMD WASM where available - ~1.5MB gzipped - Browser and Node.js compatible ### 4. `@ruvector/cli` (CLI Tools) **Purpose**: Command-line interface for database management and operations. **Features**: - Database creation and management - Import/export utilities - Benchmarking tools - MCP server support - Standalone binary --- ## Platform Support Matrix | Platform | Architecture | Native Support | WASM Fallback | Package Name | |----------|-------------|----------------|---------------|--------------| | Linux | x64 | ✅ | ✅ | `@ruvector/core-linux-x64` | | Linux | ARM64 | ✅ | ✅ | `@ruvector/core-linux-arm64` | | macOS | x64 (Intel) | ✅ | ✅ | `@ruvector/core-darwin-x64` | | macOS | ARM64 (Apple Silicon) | ✅ | ✅ | `@ruvector/core-darwin-arm64` | | Windows | x64 | ✅ | ✅ | `@ruvector/core-win32-x64` | | Windows | ARM64 | ❌ | ✅ | WASM only | | FreeBSD | x64 | ❌ | ✅ | WASM only | | Browser | Any | ❌ | ✅ | `@ruvector/wasm` | **Performance Characteristics**: - **Native (NAPI-RS)**: 100% performance baseline - **WASM with SIMD**: ~70-80% of native performance - **WASM without SIMD**: ~40-50% of native performance --- ## Directory Structure ``` /workspaces/ruvector/ ├── npm/ # NPM package root │ ├── ruvector/ # Main package │ │ ├── package.json │ │ ├── index.js # Platform detection & loader │ │ ├── index.d.ts # TypeScript definitions │ │ ├── lib/ │ │ │ ├── loader.js # Dynamic loader │ │ │ ├── platform.js # Platform detection │ │ │ └── error-handler.js # Error handling │ │ ├── README.md │ │ ├── LICENSE │ │ └── CHANGELOG.md │ │ │ ├── core/ # Native bindings base │ │ ├── package.json # Meta package │ │ ├── index.js # Exports common interface │ │ ├── index.d.ts # TypeScript definitions │ │ └── README.md │ │ │ ├── core-linux-x64/ # Platform-specific packages │ │ ├── package.json │ │ ├── ruvector.linux-x64-gnu.node # Native binary │ │ └── README.md │ │ │ ├── core-linux-arm64/ │ │ ├── package.json │ │ ├── ruvector.linux-arm64-gnu.node │ │ └── README.md │ │ │ ├── core-darwin-x64/ │ │ ├── package.json │ │ ├── ruvector.darwin-x64.node │ │ └── README.md │ │ │ ├── core-darwin-arm64/ │ │ ├── package.json │ │ ├── ruvector.darwin-arm64.node │ │ └── README.md │ │ │ ├── core-win32-x64/ │ │ ├── package.json │ │ ├── ruvector.win32-x64-msvc.node │ │ └── README.md │ │ │ ├── wasm/ # WebAssembly package │ │ ├── package.json │ │ ├── index.js # WASM loader │ │ ├── index.d.ts # TypeScript definitions │ │ ├── pkg/ # wasm-pack output │ │ │ ├── ruvector_wasm.js │ │ │ ├── ruvector_wasm.d.ts │ │ │ ├── ruvector_wasm_bg.wasm │ │ │ └── ruvector_wasm_bg.wasm.d.ts │ │ ├── pkg-simd/ # SIMD build │ │ │ └── ... │ │ ├── src/ │ │ │ ├── worker.js # Web Worker support │ │ │ ├── worker-pool.js # Worker pool │ │ │ └── indexeddb.js # IndexedDB persistence │ │ ├── README.md │ │ └── LICENSE │ │ │ └── cli/ # CLI package │ ├── package.json │ ├── bin/ │ │ ├── ruvector # Binary entry │ │ └── ruvector-mcp # MCP server entry │ ├── lib/ │ │ ├── index.js │ │ └── commands/ │ ├── README.md │ └── LICENSE │ ├── crates/ # Rust source (existing) │ ├── ruvector-core/ │ ├── ruvector-node/ # Builds to npm/core-*/ │ ├── ruvector-wasm/ # Builds to npm/wasm/ │ └── ruvector-cli/ # Builds to npm/cli/ │ └── scripts/ # Build scripts ├── build-npm-packages.sh # Build all packages ├── build-native.sh # Build native bindings ├── build-wasm.sh # Build WASM ├── publish-npm-packages.sh # Publish workflow └── test-npm-packages.sh # Test packages ``` --- ## Package Specifications ### 1. `ruvector` (Main Package) #### package.json ```json { "name": "ruvector", "version": "0.1.1", "description": "High-performance Rust-native vector database with automatic platform detection", "main": "index.js", "types": "index.d.ts", "type": "module", "keywords": [ "vector", "database", "embeddings", "similarity-search", "hnsw", "rust", "napi", "wasm", "semantic-search", "machine-learning", "rag", "simd" ], "author": "Ruvector Team", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/ruvnet/ruvector.git", "directory": "npm/ruvector" }, "homepage": "https://github.com/ruvnet/ruvector#readme", "bugs": { "url": "https://github.com/ruvnet/ruvector/issues" }, "engines": { "node": ">=18.0.0" }, "files": [ "index.js", "index.d.ts", "lib/", "README.md", "LICENSE", "CHANGELOG.md" ], "optionalDependencies": { "@ruvector/core-linux-x64": "0.1.1", "@ruvector/core-linux-arm64": "0.1.1", "@ruvector/core-darwin-x64": "0.1.1", "@ruvector/core-darwin-arm64": "0.1.1", "@ruvector/core-win32-x64": "0.1.1" }, "dependencies": { "@ruvector/wasm": "0.1.1" }, "scripts": { "test": "node --test", "postinstall": "node lib/postinstall.js" } } ``` #### index.js (Loader) ```javascript /** * Ruvector - High-performance vector database * Automatically detects platform and loads native or WASM implementation */ import { createRequire } from 'node:module'; import { platform, arch } from 'node:os'; import { loadNative, loadWasm } from './lib/loader.js'; import { getPlatformIdentifier } from './lib/platform.js'; let binding = null; let usingWasm = false; /** * Initialize and load appropriate implementation */ async function initialize() { if (binding) return binding; const platformId = getPlatformIdentifier(); try { // Try to load native binding first binding = await loadNative(platformId); console.debug(`[ruvector] Loaded native binding for ${platformId}`); return binding; } catch (nativeError) { console.debug(`[ruvector] Native binding not available: ${nativeError.message}`); try { // Fall back to WASM binding = await loadWasm(); usingWasm = true; console.debug('[ruvector] Using WebAssembly fallback'); return binding; } catch (wasmError) { throw new Error( `Failed to load ruvector:\n` + `Native: ${nativeError.message}\n` + `WASM: ${wasmError.message}` ); } } } /** * Get implementation info */ export function getImplementation() { return { type: usingWasm ? 'wasm' : 'native', platform: getPlatformIdentifier(), initialized: binding !== null }; } // Pre-initialize on import (for synchronous usage) const initPromise = initialize(); // Export promise for async initialization export { initPromise }; // Re-export all APIs export * from './lib/loader.js'; // Default export for CJS compatibility export default { initialize, getImplementation }; ``` #### lib/platform.js ```javascript /** * Platform detection utilities */ import { platform as osPlatform, arch as osArch } from 'node:os'; /** * Platform mapping for npm package names */ const PLATFORM_MAP = { 'linux-x64': 'linux-x64', 'linux-arm64': 'linux-arm64', 'darwin-x64': 'darwin-x64', 'darwin-arm64': 'darwin-arm64', 'win32-x64': 'win32-x64' }; /** * Architecture normalization */ function normalizeArch(arch) { switch (arch) { case 'x64': case 'x86_64': case 'amd64': return 'x64'; case 'arm64': case 'aarch64': return 'arm64'; default: return arch; } } /** * Get platform identifier for package lookup */ export function getPlatformIdentifier() { const platform = osPlatform(); const arch = normalizeArch(osArch()); const key = `${platform}-${arch}`; return PLATFORM_MAP[key] || null; } /** * Get package name for current platform */ export function getPlatformPackageName() { const platformId = getPlatformIdentifier(); if (!platformId) return null; return `@ruvector/core-${platformId}`; } /** * Check if platform is supported natively */ export function isPlatformSupported() { return getPlatformIdentifier() !== null; } /** * Get platform info */ export function getPlatformInfo() { return { platform: osPlatform(), arch: osArch(), normalizedArch: normalizeArch(osArch()), identifier: getPlatformIdentifier(), supported: isPlatformSupported() }; } ``` #### lib/loader.js ```javascript /** * Dynamic loader for native and WASM implementations */ import { createRequire } from 'node:module'; import { getPlatformPackageName } from './platform.js'; const require = createRequire(import.meta.url); /** * Load native binding for current platform */ export async function loadNative(platformId) { if (!platformId) { throw new Error('Platform not supported for native bindings'); } const packageName = `@ruvector/core-${platformId}`; try { const nativeBinding = require(packageName); return nativeBinding; } catch (error) { throw new Error( `Failed to load native binding '${packageName}': ${error.message}. ` + `Try reinstalling or use WASM fallback.` ); } } /** * Load WASM implementation */ export async function loadWasm() { try { const wasm = await import('@ruvector/wasm'); await wasm.default(); // Initialize WASM return wasm; } catch (error) { throw new Error(`Failed to load WASM implementation: ${error.message}`); } } /** * Try loading in order: native -> WASM */ export async function loadAny() { const platformId = getPlatformPackageName(); try { return await loadNative(platformId); } catch { return await loadWasm(); } } ``` ### 2. `@ruvector/core-*` (Platform-Specific Packages) #### package.json Template ```json { "name": "@ruvector/core-linux-x64", "version": "0.1.1", "description": "Ruvector native binding for Linux x64", "main": "ruvector.linux-x64-gnu.node", "os": ["linux"], "cpu": ["x64"], "keywords": [ "ruvector", "native", "napi", "vector-database" ], "author": "Ruvector Team", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/ruvnet/ruvector.git", "directory": "npm/core-linux-x64" }, "engines": { "node": ">=18.0.0" }, "files": [ "ruvector.linux-x64-gnu.node", "README.md" ] } ``` ### 3. `@ruvector/wasm` #### package.json ```json { "name": "@ruvector/wasm", "version": "0.1.1", "description": "Ruvector WebAssembly implementation for universal compatibility", "main": "index.js", "types": "index.d.ts", "type": "module", "keywords": [ "ruvector", "wasm", "webassembly", "vector-database", "browser" ], "author": "Ruvector Team", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/ruvnet/ruvector.git", "directory": "npm/wasm" }, "engines": { "node": ">=18.0.0" }, "files": [ "index.js", "index.d.ts", "pkg/", "pkg-simd/", "src/worker.js", "src/worker-pool.js", "src/indexeddb.js", "README.md", "LICENSE" ], "exports": { ".": { "import": "./index.js", "types": "./index.d.ts" }, "./worker": "./src/worker.js", "./worker-pool": "./src/worker-pool.js", "./indexeddb": "./src/indexeddb.js" }, "scripts": { "test": "node --test" } } ``` #### index.js (WASM Loader) ```javascript /** * Ruvector WASM implementation * Universal WebAssembly bindings */ let wasm = null; let wasmSimd = null; /** * Detect SIMD support */ function detectSimd() { try { return WebAssembly.validate(new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7b, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x0a, 0x01, 0x08, 0x00, 0x41, 0x00, 0xfd, 0x0f, 0x0b ])); } catch { return false; } } /** * Initialize WASM module */ async function initWasm() { if (wasm) return wasm; const hasSimd = detectSimd(); try { if (hasSimd) { // Try SIMD build first wasmSimd = await import('./pkg-simd/ruvector_wasm.js'); await wasmSimd.default(); wasm = wasmSimd; console.debug('[ruvector-wasm] Loaded SIMD build'); } } catch (error) { console.debug('[ruvector-wasm] SIMD build not available:', error.message); } if (!wasm) { // Fall back to regular build const wasmRegular = await import('./pkg/ruvector_wasm.js'); await wasmRegular.default(); wasm = wasmRegular; console.debug('[ruvector-wasm] Loaded regular build'); } return wasm; } /** * Export default initializer */ export default initWasm; /** * Get WASM info */ export function getWasmInfo() { return { initialized: wasm !== null, simdEnabled: wasmSimd !== null, simdSupported: detectSimd() }; } // Re-export APIs (lazy initialization) export const VectorDatabase = (...args) => initWasm().then(w => new w.VectorDatabase(...args)); export const Collection = (...args) => initWasm().then(w => new w.Collection(...args)); // ... other exports ``` ### 4. `@ruvector/cli` #### package.json ```json { "name": "@ruvector/cli", "version": "0.1.1", "description": "CLI tools for Ruvector vector database", "type": "module", "bin": { "ruvector": "bin/ruvector", "ruvector-mcp": "bin/ruvector-mcp" }, "keywords": [ "ruvector", "cli", "vector-database", "mcp" ], "author": "Ruvector Team", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/ruvnet/ruvector.git", "directory": "npm/cli" }, "engines": { "node": ">=18.0.0" }, "files": [ "bin/", "lib/", "README.md", "LICENSE" ], "dependencies": { "ruvector": "^0.1.1" }, "optionalDependencies": { "@ruvector/core-linux-x64": "0.1.1", "@ruvector/core-linux-arm64": "0.1.1", "@ruvector/core-darwin-x64": "0.1.1", "@ruvector/core-darwin-arm64": "0.1.1", "@ruvector/core-win32-x64": "0.1.1" } } ``` --- ## Platform Detection Logic ### Detection Flow ``` ┌─────────────────────────┐ │ Application Import │ │ import ruvector from │ │ 'ruvector' │ └───────────┬─────────────┘ │ ▼ ┌─────────────────────────┐ │ Detect Platform │ │ - OS: linux/darwin/win │ │ - Arch: x64/arm64 │ └───────────┬─────────────┘ │ ▼ ┌─────────────────────────┐ │ Try Load Native │ │ @ruvector/core-{plat} │ └───────────┬─────────────┘ │ ┌───┴───┐ │ │ Success Failure │ │ │ ▼ │ ┌─────────────────────────┐ │ │ Try Load WASM │ │ │ @ruvector/wasm │ │ └───────────┬─────────────┘ │ │ │ ┌───┴───┐ │ │ │ │ Success Failure │ │ │ ▼ ▼ ▼ ┌────────────────────────────┐ │ Return Binding │ │ (Native or WASM) │ └────────────────────────────┘ │ ▼ ┌────────────────────────────┐ │ Initialize Error │ │ (No compatible runtime) │ └────────────────────────────┘ ``` ### Implementation Strategy 1. **Synchronous Detection**: Platform detection at module load time 2. **Asynchronous Loading**: Dynamic import for actual implementation 3. **Graceful Degradation**: Native → WASM → Error 4. **Cache Results**: Store loaded binding to avoid re-detection 5. **Debug Logging**: Console debug messages for troubleshooting ### Error Handling ```javascript try { const db = await ruvector.createDatabase('mydb'); } catch (error) { if (error.code === 'PLATFORM_UNSUPPORTED') { console.error('Your platform is not supported. Install @ruvector/wasm manually.'); } else if (error.code === 'NATIVE_LOAD_FAILED') { console.error('Native binding failed to load. Falling back to WASM.'); } else { throw error; } } ``` --- ## Build & Publish Workflow ### Build Process ```bash # scripts/build-npm-packages.sh #!/bin/bash set -e echo "Building Ruvector NPM packages..." # 1. Build Rust crates echo "Building Rust crates..." cargo build --release --workspace # 2. Build native bindings for all platforms echo "Building native bindings..." ./scripts/build-native.sh # 3. Build WASM packages echo "Building WASM packages..." ./scripts/build-wasm.sh # 4. Create npm package structure echo "Creating npm packages..." ./scripts/create-npm-packages.sh # 5. Copy TypeScript definitions echo "Copying TypeScript definitions..." ./scripts/copy-typedefs.sh # 6. Run tests echo "Testing packages..." ./scripts/test-npm-packages.sh echo "Build complete!" ``` ### scripts/build-native.sh ```bash #!/bin/bash set -e echo "Building native bindings for all platforms..." # Define platforms PLATFORMS=( "x86_64-unknown-linux-gnu:linux-x64" "aarch64-unknown-linux-gnu:linux-arm64" "x86_64-apple-darwin:darwin-x64" "aarch64-apple-darwin:darwin-arm64" "x86_64-pc-windows-msvc:win32-x64" ) cd crates/ruvector-node for platform_pair in "${PLATFORMS[@]}"; do IFS=':' read -r rust_target npm_platform <<< "$platform_pair" echo "Building for $rust_target ($npm_platform)..." # Build with cargo cargo build --release --target "$rust_target" # Copy to npm directory mkdir -p "../../npm/core-$npm_platform" # Determine file extension if [[ $npm_platform == win32-* ]]; then ext=".dll" node_ext=".node" else ext=".so" if [[ $npm_platform == darwin-* ]]; then ext=".dylib" fi node_ext=".node" fi # Copy and rename cp "../../target/$rust_target/release/libruvector_node$ext" \ "../../npm/core-$npm_platform/ruvector.$npm_platform$node_ext" # Create package.json ./scripts/create-platform-package.sh "$npm_platform" done echo "Native bindings built successfully!" ``` ### scripts/build-wasm.sh ```bash #!/bin/bash set -e echo "Building WASM packages..." cd crates/ruvector-wasm # Build regular WASM echo "Building regular WASM..." wasm-pack build --target web --out-dir ../../npm/wasm/pkg --release # Build SIMD WASM echo "Building SIMD WASM..." wasm-pack build --target web --out-dir ../../npm/wasm/pkg-simd --release -- --features simd # Optimize WASM files echo "Optimizing WASM..." wasm-opt -Oz ../../npm/wasm/pkg/ruvector_wasm_bg.wasm -o ../../npm/wasm/pkg/ruvector_wasm_bg.wasm wasm-opt -Oz ../../npm/wasm/pkg-simd/ruvector_wasm_bg.wasm -o ../../npm/wasm/pkg-simd/ruvector_wasm_bg.wasm # Copy worker files echo "Copying worker support files..." cp src/worker.js ../../npm/wasm/src/ cp src/worker-pool.js ../../npm/wasm/src/ cp src/indexeddb.js ../../npm/wasm/src/ # Create package.json cd ../../npm/wasm cat > package.json << 'EOF' { "name": "@ruvector/wasm", "version": "0.1.1", ... } EOF echo "WASM packages built successfully!" ``` ### Publish Workflow ```bash # scripts/publish-npm-packages.sh #!/bin/bash set -e VERSION=${1:-"0.1.1"} echo "Publishing Ruvector packages version $VERSION..." # 1. Publish platform-specific packages first PLATFORMS=( "linux-x64" "linux-arm64" "darwin-x64" "darwin-arm64" "win32-x64" ) for platform in "${PLATFORMS[@]}"; do echo "Publishing @ruvector/core-$platform..." cd "npm/core-$platform" npm publish --access public cd ../.. done # 2. Publish WASM package echo "Publishing @ruvector/wasm..." cd npm/wasm npm publish --access public cd ../.. # 3. Publish CLI package echo "Publishing @ruvector/cli..." cd npm/cli npm publish --access public cd ../.. # 4. Publish main package last echo "Publishing ruvector..." cd npm/ruvector npm publish --access public cd ../.. echo "All packages published successfully!" ``` ### CI/CD Integration (GitHub Actions) ```yaml # .github/workflows/publish-npm.yml name: Publish NPM Packages on: push: tags: - 'v*' jobs: build-native: strategy: matrix: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu platform: linux-x64 - os: ubuntu-latest target: aarch64-unknown-linux-gnu platform: linux-arm64 - os: macos-latest target: x86_64-apple-darwin platform: darwin-x64 - os: macos-latest target: aarch64-apple-darwin platform: darwin-arm64 - os: windows-latest target: x86_64-pc-windows-msvc platform: win32-x64 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: toolchain: stable target: ${{ matrix.target }} - name: Build native run: | cd crates/ruvector-node cargo build --release --target ${{ matrix.target }} - name: Upload artifact uses: actions/upload-artifact@v4 with: name: native-${{ matrix.platform }} path: target/${{ matrix.target }}/release/ build-wasm: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: toolchain: stable target: wasm32-unknown-unknown - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - name: Build WASM run: ./scripts/build-wasm.sh - name: Upload artifact uses: actions/upload-artifact@v4 with: name: wasm-build path: npm/wasm/ publish: needs: [build-native, build-wasm] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org' - name: Download artifacts uses: actions/download-artifact@v4 - name: Setup packages run: ./scripts/create-npm-packages.sh - name: Publish packages run: ./scripts/publish-npm-packages.sh ${{ github.ref_name }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ``` --- ## TypeScript Definitions ### Shared Interface (index.d.ts) ```typescript /** * Ruvector - High-performance vector database * TypeScript definitions */ export interface VectorDatabaseConfig { path?: string; dimensions: number; metric?: 'cosine' | 'euclidean' | 'dotProduct'; maxElements?: number; efConstruction?: number; m?: number; quantization?: 'none' | 'scalar' | 'product'; simdEnabled?: boolean; } export interface Vector { id: string; vector: Float32Array | number[]; metadata?: Record; } export interface SearchResult { id: string; score: number; metadata?: Record; } export interface SearchOptions { k?: number; ef?: number; filter?: (metadata: Record) => boolean; } export interface InsertOptions { batch?: boolean; skipDuplicates?: boolean; } export interface DatabaseStats { vectorCount: number; dimensions: number; metric: string; memoryUsage: number; indexSize: number; quantization: string; simdEnabled: boolean; } export class VectorDatabase { constructor(config: VectorDatabaseConfig); /** * Create a new collection */ createCollection(name: string, config?: Partial): Promise; /** * Get existing collection */ getCollection(name: string): Promise; /** * List all collections */ listCollections(): Promise; /** * Delete collection */ deleteCollection(name: string): Promise; /** * Get database statistics */ stats(): Promise; /** * Close database connection */ close(): Promise; } export class Collection { /** * Insert single vector */ insert(id: string, vector: Float32Array | number[], metadata?: Record): Promise; /** * Insert multiple vectors */ insertBatch(vectors: Vector[]): Promise; /** * Search for similar vectors */ search(query: Float32Array | number[], options?: SearchOptions): Promise; /** * Get vector by ID */ get(id: string): Promise; /** * Update vector */ update(id: string, vector: Float32Array | number[], metadata?: Record): Promise; /** * Delete vector */ delete(id: string): Promise; /** * Count vectors */ count(): Promise; /** * Clear all vectors */ clear(): Promise; } /** * Get implementation information */ export function getImplementation(): { type: 'native' | 'wasm'; platform: string; initialized: boolean; }; /** * Initialize ruvector (if not auto-initialized) */ export function initialize(): Promise; /** * Create database instance */ export function createDatabase(config: VectorDatabaseConfig): Promise; export default VectorDatabase; ``` ### Platform-Specific Augmentations ```typescript // @ruvector/core-*/index.d.ts /// /** * Native implementation specific features */ export interface NativeFeatures { simdSupport: boolean; threadPoolSize: number; memoryMapped: boolean; } export function getNativeFeatures(): NativeFeatures; ``` ```typescript // @ruvector/wasm/index.d.ts /// /** * WASM implementation specific features */ export interface WasmFeatures { simdEnabled: boolean; simdSupported: boolean; workerPoolSize: number; indexedDbEnabled: boolean; } export function getWasmInfo(): WasmFeatures; /** * Initialize web worker pool */ export function initWorkerPool(poolSize?: number): Promise; /** * Enable IndexedDB persistence */ export function enableIndexedDb(dbName?: string): Promise; ``` --- ## API Consistency ### Consistent Methods Across Implementations Both NAPI-RS and WASM implementations must provide identical APIs: ```javascript // Identical usage for both native and WASM import { createDatabase } from 'ruvector'; const db = await createDatabase({ path: './vectors.db', dimensions: 384, metric: 'cosine' }); const collection = await db.createCollection('embeddings'); await collection.insert('doc1', new Float32Array([0.1, 0.2, ...])); const results = await collection.search(queryVector, { k: 10 }); ``` ### Implementation Differences (Internal Only) | Feature | Native (NAPI-RS) | WASM | |---------|-----------------|------| | File I/O | Direct filesystem | IndexedDB | | Threading | Tokio runtime | Web Workers | | SIMD | Native instructions | WASM SIMD | | Memory | Direct allocation | Linear memory | ### Performance Characteristics ```javascript // Performance hints available via API const impl = getImplementation(); if (impl.type === 'native') { // Native: optimized for throughput await collection.insertBatch(largeArray); } else { // WASM: optimized for smaller batches for (const chunk of chunkArray(largeArray, 100)) { await collection.insertBatch(chunk); } } ``` --- ## Implementation Roadmap ### Phase 1: Foundation (Week 1-2) **Tasks**: 1. Create `/workspaces/ruvector/npm/` directory structure 2. Set up build scripts for native and WASM 3. Create platform detection logic 4. Implement loader mechanism 5. Write TypeScript definitions **Deliverables**: - Complete directory structure - Working build scripts - Platform detection working ### Phase 2: Native Packages (Week 2-3) **Tasks**: 1. Set up NAPI-RS builds for all platforms 2. Create platform-specific package.json files 3. Test native bindings on each platform 4. Set up cross-compilation CI/CD **Deliverables**: - `@ruvector/core-*` packages for all platforms - Working native bindings - CI/CD pipeline for builds ### Phase 3: WASM Package (Week 3-4) **Tasks**: 1. Build WASM with and without SIMD 2. Implement worker pool 3. Add IndexedDB persistence 4. Optimize WASM size 5. Test in browsers and Node.js **Deliverables**: - `@ruvector/wasm` package - Worker support - IndexedDB integration - Browser compatibility ### Phase 4: Main Package (Week 4-5) **Tasks**: 1. Implement platform detection 2. Create dynamic loader 3. Add error handling 4. Write comprehensive tests 5. Create usage examples **Deliverables**: - `ruvector` main package - Full test suite - Documentation - Examples ### Phase 5: CLI Package (Week 5-6) **Tasks**: 1. Package CLI binaries 2. Create npm package structure 3. Add global installation support 4. Write CLI documentation **Deliverables**: - `@ruvector/cli` package - Global CLI commands - MCP server binary - CLI documentation ### Phase 6: Testing & Documentation (Week 6-7) **Tasks**: 1. Integration testing across platforms 2. Performance benchmarking 3. Write comprehensive README files 4. Create migration guide 5. Prepare release notes **Deliverables**: - Test reports - Benchmark results - Complete documentation - Release candidate ### Phase 7: Publication (Week 7) **Tasks**: 1. Set up npm organization 2. Publish beta versions 3. Gather feedback 4. Fix issues 5. Publish stable v0.1.1 **Deliverables**: - Published packages on npm - Release announcement - Migration guide - Support channels --- ## ADRs (Architecture Decision Records) ### ADR-001: Use Optional Dependencies for Platform-Specific Packages **Status**: Accepted **Context**: We need to distribute platform-specific native bindings without forcing users to download all platforms. **Decision**: Use npm `optionalDependencies` for platform packages and programmatic fallback to WASM. **Consequences**: - ✅ Users only download their platform - ✅ Graceful degradation to WASM - ✅ Smaller install size - ⚠️ Requires runtime detection - ⚠️ More complex loader logic **Alternatives Considered**: 1. Single package with all binaries (rejected: too large ~40MB) 2. Manual platform selection (rejected: poor UX) 3. Postinstall script download (rejected: security concerns) --- ### ADR-002: WASM as Universal Fallback **Status**: Accepted **Context**: Not all platforms can be supported with native bindings. **Decision**: Include `@ruvector/wasm` as a required dependency with automatic fallback. **Consequences**: - ✅ Works on any platform - ✅ Browser compatibility - ✅ No native compilation needed - ⚠️ 40-50% slower than native - ⚠️ ~1.5MB added to install **Alternatives Considered**: 1. Native-only (rejected: poor platform coverage) 2. Pure JavaScript (rejected: too slow) 3. Optional WASM (rejected: fallback must be guaranteed) --- ### ADR-003: Separate CLI Package **Status**: Accepted **Context**: CLI tools have different distribution requirements than library. **Decision**: Create separate `@ruvector/cli` package with optional dependency on main package. **Consequences**: - ✅ Users can install library without CLI - ✅ CLI can be installed globally - ✅ Cleaner separation of concerns - ⚠️ Additional package to maintain - ⚠️ Version synchronization needed **Alternatives Considered**: 1. Bundle CLI in main package (rejected: bloat) 2. Separate binary distribution (rejected: npm is standard) --- ### ADR-004: Platform Detection at Runtime **Status**: Accepted **Context**: Need to determine which native binding to load. **Decision**: Implement runtime platform detection with try-catch fallback logic. **Consequences**: - ✅ Automatic platform selection - ✅ Works without configuration - ✅ Handles missing binaries gracefully - ⚠️ Small runtime overhead on first load - ⚠️ Async initialization required **Alternatives Considered**: 1. Build-time detection (rejected: doesn't work for pre-built packages) 2. User configuration (rejected: poor UX) 3. Separate imports (rejected: confusing API) --- ### ADR-005: TypeScript Definitions in All Packages **Status**: Accepted **Context**: TypeScript is widely used and type safety is critical for vector operations. **Decision**: Include `.d.ts` files in all packages with consistent types. **Consequences**: - ✅ Full TypeScript support - ✅ Better developer experience - ✅ Type checking for vectors/metadata - ⚠️ Must maintain type definitions - ⚠️ Sync types across implementations **Alternatives Considered**: 1. Types-only package (rejected: fragmentation) 2. No types (rejected: poor DX) 3. Generate from Rust (considered for future) --- ## Appendix ### A. Package Size Estimates | Package | Uncompressed | Gzipped | Install Size | |---------|--------------|---------|--------------| | `ruvector` | 150 KB | 45 KB | ~200 KB | | `@ruvector/core-linux-x64` | 6.5 MB | 2.1 MB | ~8 MB | | `@ruvector/core-linux-arm64` | 6.2 MB | 2.0 MB | ~7.5 MB | | `@ruvector/core-darwin-x64` | 5.8 MB | 1.9 MB | ~7 MB | | `@ruvector/core-darwin-arm64` | 5.5 MB | 1.8 MB | ~6.5 MB | | `@ruvector/core-win32-x64` | 7.1 MB | 2.3 MB | ~9 MB | | `@ruvector/wasm` | 2.2 MB | 1.1 MB | ~3 MB | | `@ruvector/cli` | 8.5 MB | 2.8 MB | ~10 MB | ### B. Compatibility Matrix | Environment | Native | WASM | Notes | |-------------|--------|------|-------| | Node.js 18+ | ✅ | ✅ | Full support | | Node.js 16 | ⚠️ | ✅ | Native may work, WASM guaranteed | | Chrome 90+ | ❌ | ✅ | WASM only | | Firefox 89+ | ❌ | ✅ | WASM only | | Safari 15+ | ❌ | ✅ | WASM only | | Edge 90+ | ❌ | ✅ | WASM only | | Deno | ❌ | ✅ | WASM only | | Bun | ✅ | ✅ | Native preferred | ### C. Performance Benchmarks | Operation | Native | WASM (SIMD) | WASM (No SIMD) | |-----------|--------|-------------|----------------| | Insert (single) | 1.0x | 0.75x | 0.45x | | Insert (batch 1000) | 1.0x | 0.80x | 0.50x | | Search (k=10) | 1.0x | 0.72x | 0.42x | | Search (k=100) | 1.0x | 0.68x | 0.38x | | Index build | 1.0x | 0.65x | 0.35x | ### D. Release Checklist - [ ] All Rust crates build successfully - [ ] Native bindings built for all platforms - [ ] WASM packages built (regular + SIMD) - [ ] TypeScript definitions generated - [ ] All tests passing - [ ] Documentation complete - [ ] Examples tested - [ ] Benchmarks run - [ ] CHANGELOG.md updated - [ ] Version numbers synced - [ ] npm packages created - [ ] CI/CD pipeline validated - [ ] Pre-publish dry run completed - [ ] Packages published to npm - [ ] GitHub release created - [ ] Documentation site updated --- ## Conclusion This modular npm package architecture for Ruvector provides: 1. **Optimal Performance**: Native bindings where available 2. **Universal Compatibility**: WASM fallback for all platforms 3. **Developer Experience**: Zero-config with TypeScript support 4. **Flexibility**: Users can install specific packages if needed 5. **Maintainability**: Clear separation of concerns 6. **Scalability**: Easy to add new platforms The architecture balances performance, compatibility, and usability while maintaining a clean and maintainable codebase. **Next Steps**: Begin Phase 1 implementation by creating the directory structure and build scripts.