Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
import type { RvfOptions, RvfQueryOptions, RvfSearchResult, RvfIngestResult, RvfIngestEntry, RvfDeleteResult, RvfCompactionResult, RvfStatus, RvfFilterExpr, RvfKernelData, RvfEbpfData, RvfSegmentInfo, BackendType } from './types';
/**
* Abstract backend that wraps either the native (N-API) or WASM build of
* rvf-runtime. The `RvfDatabase` class delegates all I/O to a backend
* instance, keeping the public API identical regardless of runtime.
*/
export interface RvfBackend {
/** Create a new store file at `path` with the given options. */
create(path: string, options: RvfOptions): Promise<void>;
/** Open an existing store at `path` for read-write access. */
open(path: string): Promise<void>;
/** Open an existing store at `path` for read-only access. */
openReadonly(path: string): Promise<void>;
/** Ingest a batch of vectors. */
ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult>;
/** Query the k nearest neighbors. */
query(vector: Float32Array, k: number, options?: RvfQueryOptions): Promise<RvfSearchResult[]>;
/** Soft-delete vectors by ID. */
delete(ids: string[]): Promise<RvfDeleteResult>;
/** Soft-delete vectors matching a filter. */
deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult>;
/** Run compaction to reclaim dead space. */
compact(): Promise<RvfCompactionResult>;
/** Get the current store status. */
status(): Promise<RvfStatus>;
/** Close the store, releasing locks. */
close(): Promise<void>;
fileId(): Promise<string>;
parentId(): Promise<string>;
lineageDepth(): Promise<number>;
derive(childPath: string, options?: RvfOptions): Promise<RvfBackend>;
embedKernel(arch: number, kernelType: number, flags: number, image: Uint8Array, apiPort: number, cmdline?: string): Promise<number>;
extractKernel(): Promise<RvfKernelData | null>;
embedEbpf(programType: number, attachType: number, maxDimension: number, bytecode: Uint8Array, btf?: Uint8Array): Promise<number>;
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
}
/**
* Backend that delegates to the `@ruvector/rvf-node` native N-API addon.
*
* The native addon is loaded lazily on first use so that the SDK package can
* be imported in environments where the native build is unavailable (e.g.
* browsers) without throwing at import time.
*/
export declare class NodeBackend implements RvfBackend {
private native;
private handle;
private idToLabel;
private labelToId;
private nextLabel;
private storePath;
private loadNative;
private ensureHandle;
create(path: string, options: RvfOptions): Promise<void>;
open(path: string): Promise<void>;
openReadonly(path: string): Promise<void>;
ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult>;
query(vector: Float32Array, k: number, options?: RvfQueryOptions): Promise<RvfSearchResult[]>;
delete(ids: string[]): Promise<RvfDeleteResult>;
deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult>;
compact(): Promise<RvfCompactionResult>;
status(): Promise<RvfStatus>;
close(): Promise<void>;
fileId(): Promise<string>;
parentId(): Promise<string>;
lineageDepth(): Promise<number>;
derive(childPath: string, options?: RvfOptions): Promise<RvfBackend>;
embedKernel(arch: number, kernelType: number, flags: number, image: Uint8Array, apiPort: number, cmdline?: string): Promise<number>;
extractKernel(): Promise<RvfKernelData | null>;
embedEbpf(programType: number, attachType: number, maxDimension: number, bytecode: Uint8Array, btf?: Uint8Array): Promise<number>;
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
/**
* Get or allocate a numeric label for a string ID.
* If the ID was already seen, returns the existing label.
*/
private resolveLabel;
/** Path to the sidecar mappings file. */
private mappingsPath;
/** Persist the string↔label mapping to a sidecar JSON file. */
private saveMappings;
/** Load the string↔label mapping from the sidecar JSON file if it exists. */
private loadMappings;
}
/**
* Backend that delegates to the `@ruvector/rvf-wasm` WASM build.
*
* The WASM microkernel exposes C-ABI store functions (`rvf_store_create`,
* `rvf_store_query`, etc.) operating on integer handles. This backend wraps
* them behind the same `RvfBackend` interface.
*
* Suitable for browser environments. The WASM module is loaded lazily.
*/
export declare class WasmBackend implements RvfBackend {
private wasm;
/** Integer store handle returned by `rvf_store_create` / `rvf_store_open`. */
private handle;
private dim;
private loadWasm;
private ensureHandle;
private metricCode;
create(_path: string, options: RvfOptions): Promise<void>;
open(_path: string): Promise<void>;
openReadonly(_path: string): Promise<void>;
ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult>;
query(vector: Float32Array, k: number, _options?: RvfQueryOptions): Promise<RvfSearchResult[]>;
delete(ids: string[]): Promise<RvfDeleteResult>;
deleteByFilter(_filter: RvfFilterExpr): Promise<RvfDeleteResult>;
compact(): Promise<RvfCompactionResult>;
status(): Promise<RvfStatus>;
close(): Promise<void>;
fileId(): Promise<string>;
parentId(): Promise<string>;
lineageDepth(): Promise<number>;
derive(_childPath: string, _options?: RvfOptions): Promise<RvfBackend>;
embedKernel(): Promise<number>;
extractKernel(): Promise<RvfKernelData | null>;
embedEbpf(): Promise<number>;
extractEbpf(): Promise<RvfEbpfData | null>;
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
}
/**
* Resolve a `BackendType` to a concrete `RvfBackend` instance.
*
* - `'node'` Always returns a `NodeBackend`.
* - `'wasm'` Always returns a `WasmBackend`.
* - `'auto'` Tries `node` first, falls back to `wasm`.
*/
export declare function resolveBackend(type: BackendType): RvfBackend;
//# sourceMappingURL=backend.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["backend.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,731 @@
"use strict";
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.WasmBackend = exports.NodeBackend = void 0;
exports.resolveBackend = resolveBackend;
const errors_1 = require("./errors");
// ---------------------------------------------------------------------------
// NodeBackend — wraps @ruvector/rvf-node (N-API)
// ---------------------------------------------------------------------------
/**
* Backend that delegates to the `@ruvector/rvf-node` native N-API addon.
*
* The native addon is loaded lazily on first use so that the SDK package can
* be imported in environments where the native build is unavailable (e.g.
* browsers) without throwing at import time.
*/
class NodeBackend {
constructor() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.native = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.handle = null;
// String ID <-> Numeric Label mappings (N-API layer requires i64 labels)
this.idToLabel = new Map();
this.labelToId = new Map();
this.nextLabel = 1; // RVF uses 1-based labels
this.storePath = '';
}
async loadNative() {
if (this.native)
return;
try {
// Dynamic import so the SDK can be bundled for browsers without
// pulling in the native addon at compile time.
// The NAPI addon exports a `RvfDatabase` class with factory methods.
const mod = await Promise.resolve().then(() => __importStar(require('@ruvector/rvf-node')));
this.native = mod.RvfDatabase ?? mod.default?.RvfDatabase ?? mod;
}
catch {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'Could not load @ruvector/rvf-node — is it installed?');
}
}
ensureHandle() {
if (!this.handle) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.StoreClosed);
}
}
async create(path, options) {
await this.loadNative();
try {
this.handle = await this.native.create(path, mapOptionsToNative(options));
this.storePath = path;
this.idToLabel.clear();
this.labelToId.clear();
this.nextLabel = 1;
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async open(path) {
await this.loadNative();
try {
this.handle = await this.native.open(path);
this.storePath = path;
await this.loadMappings();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async openReadonly(path) {
await this.loadNative();
try {
this.handle = await this.native.openReadonly(path);
this.storePath = path;
await this.loadMappings();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async ingestBatch(entries) {
this.ensureHandle();
try {
// NAPI signature: ingestBatch(vectors: Float32Array, ids: i64[], metadata?)
// Flatten individual vectors into a single contiguous Float32Array.
const n = entries.length;
if (n === 0)
return { accepted: 0, rejected: 0, epoch: 0 };
const first = entries[0].vector;
const dim = first instanceof Float32Array ? first.length : first.length;
const flat = new Float32Array(n * dim);
for (let i = 0; i < n; i++) {
const v = entries[i].vector;
const f32 = v instanceof Float32Array ? v : new Float32Array(v);
flat.set(f32, i * dim);
}
// Map string IDs to numeric labels for the N-API layer.
// The native Rust HNSW expects i64 labels — non-numeric strings cause
// silent data loss (NaN → dropped). We maintain a bidirectional
// string↔label mapping and persist it as a sidecar JSON file.
const ids = entries.map((e) => this.resolveLabel(e.id));
const result = this.handle.ingestBatch(flat, ids);
// Persist mappings after every ingest so they survive crashes.
await this.saveMappings();
return {
accepted: Number(result.accepted),
rejected: Number(result.rejected),
epoch: result.epoch,
};
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async query(vector, k, options) {
this.ensureHandle();
try {
const nativeOpts = options ? mapQueryOptionsToNative(options) : undefined;
const results = this.handle.query(vector, k, nativeOpts);
// Map numeric labels back to original string IDs.
return results.map((r) => ({
id: this.labelToId.get(Number(r.id)) ?? String(r.id),
distance: r.distance,
}));
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async delete(ids) {
this.ensureHandle();
try {
// Resolve string IDs to numeric labels for the N-API layer.
const numIds = ids
.map((id) => this.idToLabel.get(id))
.filter((label) => label !== undefined);
if (numIds.length === 0) {
return { deleted: 0, epoch: 0 };
}
const result = this.handle.delete(numIds);
// Remove deleted entries from the mapping.
for (const id of ids) {
const label = this.idToLabel.get(id);
if (label !== undefined) {
this.idToLabel.delete(id);
this.labelToId.delete(label);
}
}
await this.saveMappings();
return { deleted: Number(result.deleted), epoch: result.epoch };
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async deleteByFilter(filter) {
this.ensureHandle();
try {
// NAPI takes a JSON string for the filter expression.
const result = this.handle.deleteByFilter(JSON.stringify(filter));
return { deleted: Number(result.deleted), epoch: result.epoch };
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async compact() {
this.ensureHandle();
try {
const result = this.handle.compact();
return {
segmentsCompacted: result.segmentsCompacted ?? result.segments_compacted,
bytesReclaimed: Number(result.bytesReclaimed ?? result.bytes_reclaimed),
epoch: result.epoch,
};
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async status() {
this.ensureHandle();
try {
const s = this.handle.status();
return mapNativeStatus(s);
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async close() {
if (!this.handle)
return;
try {
await this.saveMappings();
this.handle.close();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
finally {
this.handle = null;
this.idToLabel.clear();
this.labelToId.clear();
this.nextLabel = 1;
this.storePath = '';
}
}
async fileId() {
this.ensureHandle();
try {
return this.handle.fileId();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async parentId() {
this.ensureHandle();
try {
return this.handle.parentId();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async lineageDepth() {
this.ensureHandle();
try {
return this.handle.lineageDepth();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async derive(childPath, options) {
this.ensureHandle();
try {
const nativeOpts = options ? mapOptionsToNative(options) : undefined;
const childHandle = this.handle.derive(childPath, nativeOpts);
const child = new NodeBackend();
child.native = this.native;
child.handle = childHandle;
child.storePath = childPath;
// Copy parent mappings to child (COW semantics)
child.idToLabel = new Map(this.idToLabel);
child.labelToId = new Map(this.labelToId);
child.nextLabel = this.nextLabel;
await child.saveMappings();
return child;
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async embedKernel(arch, kernelType, flags, image, apiPort, cmdline) {
this.ensureHandle();
try {
return this.handle.embedKernel(arch, kernelType, flags, Buffer.from(image), apiPort, cmdline);
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async extractKernel() {
this.ensureHandle();
try {
const result = this.handle.extractKernel();
if (!result)
return null;
return {
header: new Uint8Array(result.header),
image: new Uint8Array(result.image),
};
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async embedEbpf(programType, attachType, maxDimension, bytecode, btf) {
this.ensureHandle();
try {
return this.handle.embedEbpf(programType, attachType, maxDimension, Buffer.from(bytecode), btf ? Buffer.from(btf) : undefined);
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async extractEbpf() {
this.ensureHandle();
try {
const result = this.handle.extractEbpf();
if (!result)
return null;
return {
header: new Uint8Array(result.header),
payload: new Uint8Array(result.payload),
};
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async segments() {
this.ensureHandle();
try {
const segs = this.handle.segments();
return segs.map((s) => ({
id: s.id,
offset: s.offset,
payloadLength: s.payloadLength ?? s.payload_length,
segType: s.segType ?? s.seg_type,
}));
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async dimension() {
this.ensureHandle();
try {
return this.handle.dimension();
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
// ─── String ID ↔ Numeric Label mapping helpers ───
/**
* Get or allocate a numeric label for a string ID.
* If the ID was already seen, returns the existing label.
*/
resolveLabel(id) {
let label = this.idToLabel.get(id);
if (label !== undefined)
return label;
label = this.nextLabel++;
this.idToLabel.set(id, label);
this.labelToId.set(label, id);
return label;
}
/** Path to the sidecar mappings file. */
mappingsPath() {
return this.storePath ? this.storePath + '.idmap.json' : '';
}
/** Persist the string↔label mapping to a sidecar JSON file. */
async saveMappings() {
const mp = this.mappingsPath();
if (!mp)
return;
try {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
const data = JSON.stringify({
idToLabel: Object.fromEntries(this.idToLabel),
labelToId: Object.fromEntries(Array.from(this.labelToId.entries()).map(([k, v]) => [String(k), v])),
nextLabel: this.nextLabel,
});
fs.writeFileSync(mp, data, 'utf-8');
}
catch {
// Non-fatal: mapping persistence is best-effort (e.g. read-only FS).
}
}
/** Load the string↔label mapping from the sidecar JSON file if it exists. */
async loadMappings() {
const mp = this.mappingsPath();
if (!mp)
return;
try {
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
if (!fs.existsSync(mp))
return;
const raw = JSON.parse(fs.readFileSync(mp, 'utf-8'));
this.idToLabel = new Map(Object.entries(raw.idToLabel ?? {}).map(([k, v]) => [k, Number(v)]));
this.labelToId = new Map(Object.entries(raw.labelToId ?? {}).map(([k, v]) => [Number(k), v]));
this.nextLabel = raw.nextLabel ?? this.idToLabel.size + 1;
}
catch {
// Non-fatal: start with empty mappings.
}
}
}
exports.NodeBackend = NodeBackend;
// ---------------------------------------------------------------------------
// WasmBackend — wraps @ruvector/rvf-wasm
// ---------------------------------------------------------------------------
/**
* Backend that delegates to the `@ruvector/rvf-wasm` WASM build.
*
* The WASM microkernel exposes C-ABI store functions (`rvf_store_create`,
* `rvf_store_query`, etc.) operating on integer handles. This backend wraps
* them behind the same `RvfBackend` interface.
*
* Suitable for browser environments. The WASM module is loaded lazily.
*/
class WasmBackend {
constructor() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.wasm = null;
/** Integer store handle returned by `rvf_store_create` / `rvf_store_open`. */
this.handle = 0;
this.dim = 0;
}
async loadWasm() {
if (this.wasm)
return;
try {
const mod = await Promise.resolve().then(() => __importStar(require('@ruvector/rvf-wasm')));
// wasm-pack default export is the init function
if (typeof mod.default === 'function') {
this.wasm = await mod.default();
}
else {
this.wasm = mod;
}
}
catch {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'Could not load @ruvector/rvf-wasm — is it installed?');
}
}
ensureHandle() {
if (!this.handle) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.StoreClosed);
}
}
metricCode(metric) {
switch (metric) {
case 'Cosine': return 2;
case 'InnerProduct': return 1;
default: return 0; // L2
}
}
async create(_path, options) {
await this.loadWasm();
try {
const nativeOpts = mapOptionsToNative(options);
const dim = nativeOpts.dimension;
const metric = this.metricCode(nativeOpts.metric);
const h = this.wasm.rvf_store_create(dim, metric);
if (h <= 0)
throw new Error('rvf_store_create returned ' + h);
this.handle = h;
this.dim = dim;
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async open(_path) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'WASM backend does not support file-based open (in-memory only)');
}
async openReadonly(_path) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'WASM backend does not support file-based openReadonly (in-memory only)');
}
async ingestBatch(entries) {
this.ensureHandle();
try {
const n = entries.length;
if (n === 0)
return { accepted: 0, rejected: 0, epoch: 0 };
const dim = this.dim || (entries[0].vector instanceof Float32Array
? entries[0].vector.length : entries[0].vector.length);
const flat = new Float32Array(n * dim);
const ids = new BigUint64Array(n);
for (let i = 0; i < n; i++) {
const v = entries[i].vector;
const f32 = v instanceof Float32Array ? v : new Float32Array(v);
flat.set(f32, i * dim);
ids[i] = BigInt(entries[i].id);
}
// Allocate in WASM memory and call
const vecsPtr = this.wasm.rvf_alloc(flat.byteLength);
const idsPtr = this.wasm.rvf_alloc(ids.byteLength);
new Float32Array(this.wasm.memory.buffer, vecsPtr, flat.length).set(flat);
new BigUint64Array(this.wasm.memory.buffer, idsPtr, ids.length).set(ids);
const accepted = this.wasm.rvf_store_ingest(this.handle, vecsPtr, idsPtr, n);
this.wasm.rvf_free(vecsPtr, flat.byteLength);
this.wasm.rvf_free(idsPtr, ids.byteLength);
return { accepted: accepted > 0 ? accepted : 0, rejected: accepted < 0 ? n : 0, epoch: 0 };
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async query(vector, k, _options) {
this.ensureHandle();
try {
const queryPtr = this.wasm.rvf_alloc(vector.byteLength);
new Float32Array(this.wasm.memory.buffer, queryPtr, vector.length).set(vector);
// Each result = 8 bytes id + 4 bytes dist = 12 bytes
const outSize = k * 12;
const outPtr = this.wasm.rvf_alloc(outSize);
const count = this.wasm.rvf_store_query(this.handle, queryPtr, k, 0, outPtr);
const results = [];
const view = new DataView(this.wasm.memory.buffer);
for (let i = 0; i < count; i++) {
const off = outPtr + i * 12;
const id = view.getBigUint64(off, true);
const dist = view.getFloat32(off + 8, true);
results.push({ id: String(id), distance: dist });
}
this.wasm.rvf_free(queryPtr, vector.byteLength);
this.wasm.rvf_free(outPtr, outSize);
return results;
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async delete(ids) {
this.ensureHandle();
try {
const arr = new BigUint64Array(ids.map((id) => BigInt(id)));
const ptr = this.wasm.rvf_alloc(arr.byteLength);
new BigUint64Array(this.wasm.memory.buffer, ptr, arr.length).set(arr);
const deleted = this.wasm.rvf_store_delete(this.handle, ptr, ids.length);
this.wasm.rvf_free(ptr, arr.byteLength);
return { deleted: deleted > 0 ? deleted : 0, epoch: 0 };
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async deleteByFilter(_filter) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'deleteByFilter not supported in WASM backend');
}
async compact() {
return { segmentsCompacted: 0, bytesReclaimed: 0, epoch: 0 };
}
async status() {
this.ensureHandle();
try {
const outPtr = this.wasm.rvf_alloc(20);
this.wasm.rvf_store_status(this.handle, outPtr);
const view = new DataView(this.wasm.memory.buffer);
const totalVectors = view.getUint32(outPtr, true);
const dim = view.getUint32(outPtr + 4, true);
this.wasm.rvf_free(outPtr, 20);
return {
totalVectors,
totalSegments: 1,
fileSizeBytes: 0,
epoch: 0,
profileId: 0,
compactionState: 'idle',
deadSpaceRatio: 0,
readOnly: false,
};
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
}
async close() {
if (!this.handle)
return;
try {
this.wasm.rvf_store_close(this.handle);
}
catch (err) {
throw errors_1.RvfError.fromNative(err);
}
finally {
this.handle = 0;
}
}
async fileId() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'fileId not supported in WASM backend');
}
async parentId() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'parentId not supported in WASM backend');
}
async lineageDepth() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'lineageDepth not supported in WASM backend');
}
async derive(_childPath, _options) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'derive not supported in WASM backend');
}
async embedKernel() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'embedKernel not supported in WASM backend');
}
async extractKernel() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'extractKernel not supported in WASM backend');
}
async embedEbpf() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'embedEbpf not supported in WASM backend');
}
async extractEbpf() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'extractEbpf not supported in WASM backend');
}
async segments() {
throw new errors_1.RvfError(errors_1.RvfErrorCode.BackendNotFound, 'segments not supported in WASM backend');
}
async dimension() {
this.ensureHandle();
const d = this.wasm.rvf_store_dimension(this.handle);
if (d < 0)
throw new errors_1.RvfError(errors_1.RvfErrorCode.StoreClosed);
return d;
}
}
exports.WasmBackend = WasmBackend;
// ---------------------------------------------------------------------------
// Backend resolution
// ---------------------------------------------------------------------------
/**
* Resolve a `BackendType` to a concrete `RvfBackend` instance.
*
* - `'node'` Always returns a `NodeBackend`.
* - `'wasm'` Always returns a `WasmBackend`.
* - `'auto'` Tries `node` first, falls back to `wasm`.
*/
function resolveBackend(type) {
switch (type) {
case 'node':
return new NodeBackend();
case 'wasm':
return new WasmBackend();
case 'auto': {
// In Node.js environments, prefer native; in browsers, prefer WASM.
const isNode = typeof process !== 'undefined' &&
typeof process.versions !== 'undefined' &&
typeof process.versions.node === 'string';
return isNode ? new NodeBackend() : new WasmBackend();
}
}
}
// ---------------------------------------------------------------------------
// Mapping helpers (TS options -> native/wasm shapes)
// ---------------------------------------------------------------------------
function mapMetricToNative(metric) {
switch (metric) {
case 'cosine':
return 'Cosine';
case 'dotproduct':
return 'InnerProduct';
case 'l2':
default:
return 'L2';
}
}
function mapCompressionToNative(compression) {
switch (compression) {
case 'scalar':
return 'Scalar';
case 'product':
return 'Product';
case 'none':
default:
return 'None';
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapOptionsToNative(options) {
return {
dimension: options.dimensions,
metric: mapMetricToNative(options.metric),
profile: options.profile ?? 0,
compression: mapCompressionToNative(options.compression),
signing: options.signing ?? false,
m: options.m ?? 16,
ef_construction: options.efConstruction ?? 200,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapQueryOptionsToNative(options) {
return {
ef_search: options.efSearch ?? 100,
// NAPI accepts the filter as a JSON string, not an object.
filter: options.filter ? JSON.stringify(options.filter) : undefined,
timeout_ms: options.timeoutMs ?? 0,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapNativeStatus(s) {
return {
totalVectors: s.total_vectors ?? s.totalVectors ?? 0,
totalSegments: s.total_segments ?? s.totalSegments ?? 0,
fileSizeBytes: s.file_size ?? s.fileSizeBytes ?? 0,
epoch: s.current_epoch ?? s.epoch ?? 0,
profileId: s.profile_id ?? s.profileId ?? 0,
compactionState: mapCompactionState(s.compaction_state ?? s.compactionState),
deadSpaceRatio: s.dead_space_ratio ?? s.deadSpaceRatio ?? 0,
readOnly: s.read_only ?? s.readOnly ?? false,
};
}
function mapCompactionState(state) {
if (typeof state === 'string') {
const lower = state.toLowerCase();
if (lower === 'running')
return 'running';
if (lower === 'emergency')
return 'emergency';
}
return 'idle';
}
//# sourceMappingURL=backend.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,790 @@
import type {
RvfOptions,
RvfQueryOptions,
RvfSearchResult,
RvfIngestResult,
RvfIngestEntry,
RvfDeleteResult,
RvfCompactionResult,
RvfStatus,
RvfFilterExpr,
RvfKernelData,
RvfEbpfData,
RvfSegmentInfo,
BackendType,
} from './types';
import { RvfError, RvfErrorCode } from './errors';
// ---------------------------------------------------------------------------
// Backend interface — every backend (node, wasm) must implement this.
// ---------------------------------------------------------------------------
/**
* Abstract backend that wraps either the native (N-API) or WASM build of
* rvf-runtime. The `RvfDatabase` class delegates all I/O to a backend
* instance, keeping the public API identical regardless of runtime.
*/
export interface RvfBackend {
/** Create a new store file at `path` with the given options. */
create(path: string, options: RvfOptions): Promise<void>;
/** Open an existing store at `path` for read-write access. */
open(path: string): Promise<void>;
/** Open an existing store at `path` for read-only access. */
openReadonly(path: string): Promise<void>;
/** Ingest a batch of vectors. */
ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult>;
/** Query the k nearest neighbors. */
query(vector: Float32Array, k: number, options?: RvfQueryOptions): Promise<RvfSearchResult[]>;
/** Soft-delete vectors by ID. */
delete(ids: string[]): Promise<RvfDeleteResult>;
/** Soft-delete vectors matching a filter. */
deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult>;
/** Run compaction to reclaim dead space. */
compact(): Promise<RvfCompactionResult>;
/** Get the current store status. */
status(): Promise<RvfStatus>;
/** Close the store, releasing locks. */
close(): Promise<void>;
// Lineage
fileId(): Promise<string>;
parentId(): Promise<string>;
lineageDepth(): Promise<number>;
derive(childPath: string, options?: RvfOptions): Promise<RvfBackend>;
// Kernel / eBPF
embedKernel(arch: number, kernelType: number, flags: number,
image: Uint8Array, apiPort: number, cmdline?: string): Promise<number>;
extractKernel(): Promise<RvfKernelData | null>;
embedEbpf(programType: number, attachType: number, maxDimension: number,
bytecode: Uint8Array, btf?: Uint8Array): Promise<number>;
extractEbpf(): Promise<RvfEbpfData | null>;
// Inspection
segments(): Promise<RvfSegmentInfo[]>;
dimension(): Promise<number>;
}
// ---------------------------------------------------------------------------
// NodeBackend — wraps @ruvector/rvf-node (N-API)
// ---------------------------------------------------------------------------
/**
* Backend that delegates to the `@ruvector/rvf-node` native N-API addon.
*
* The native addon is loaded lazily on first use so that the SDK package can
* be imported in environments where the native build is unavailable (e.g.
* browsers) without throwing at import time.
*/
export class NodeBackend implements RvfBackend {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private native: any = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private handle: any = null;
// String ID <-> Numeric Label mappings (N-API layer requires i64 labels)
private idToLabel: Map<string, number> = new Map();
private labelToId: Map<number, string> = new Map();
private nextLabel: number = 1; // RVF uses 1-based labels
private storePath: string = '';
private async loadNative(): Promise<void> {
if (this.native) return;
try {
// Dynamic import so the SDK can be bundled for browsers without
// pulling in the native addon at compile time.
// The NAPI addon exports a `RvfDatabase` class with factory methods.
const mod = await import('@ruvector/rvf-node');
this.native = mod.RvfDatabase ?? mod.default?.RvfDatabase ?? mod;
} catch {
throw new RvfError(
RvfErrorCode.BackendNotFound,
'Could not load @ruvector/rvf-node — is it installed?',
);
}
}
private ensureHandle(): void {
if (!this.handle) {
throw new RvfError(RvfErrorCode.StoreClosed);
}
}
async create(path: string, options: RvfOptions): Promise<void> {
await this.loadNative();
try {
this.handle = await this.native.create(path, mapOptionsToNative(options));
this.storePath = path;
this.idToLabel.clear();
this.labelToId.clear();
this.nextLabel = 1;
} catch (err) {
throw RvfError.fromNative(err);
}
}
async open(path: string): Promise<void> {
await this.loadNative();
try {
this.handle = await this.native.open(path);
this.storePath = path;
await this.loadMappings();
} catch (err) {
throw RvfError.fromNative(err);
}
}
async openReadonly(path: string): Promise<void> {
await this.loadNative();
try {
this.handle = await this.native.openReadonly(path);
this.storePath = path;
await this.loadMappings();
} catch (err) {
throw RvfError.fromNative(err);
}
}
async ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult> {
this.ensureHandle();
try {
// NAPI signature: ingestBatch(vectors: Float32Array, ids: i64[], metadata?)
// Flatten individual vectors into a single contiguous Float32Array.
const n = entries.length;
if (n === 0) return { accepted: 0, rejected: 0, epoch: 0 };
const first = entries[0].vector;
const dim = first instanceof Float32Array ? first.length : first.length;
const flat = new Float32Array(n * dim);
for (let i = 0; i < n; i++) {
const v = entries[i].vector;
const f32 = v instanceof Float32Array ? v : new Float32Array(v);
flat.set(f32, i * dim);
}
// Map string IDs to numeric labels for the N-API layer.
// The native Rust HNSW expects i64 labels — non-numeric strings cause
// silent data loss (NaN → dropped). We maintain a bidirectional
// string↔label mapping and persist it as a sidecar JSON file.
const ids = entries.map((e) => this.resolveLabel(e.id));
const result = this.handle.ingestBatch(flat, ids);
// Persist mappings after every ingest so they survive crashes.
await this.saveMappings();
return {
accepted: Number(result.accepted),
rejected: Number(result.rejected),
epoch: result.epoch,
};
} catch (err) {
throw RvfError.fromNative(err);
}
}
async query(
vector: Float32Array,
k: number,
options?: RvfQueryOptions,
): Promise<RvfSearchResult[]> {
this.ensureHandle();
try {
const nativeOpts = options ? mapQueryOptionsToNative(options) : undefined;
const results = this.handle.query(vector, k, nativeOpts);
// Map numeric labels back to original string IDs.
return (results as Array<{ id: number; distance: number }>).map((r) => ({
id: this.labelToId.get(Number(r.id)) ?? String(r.id),
distance: r.distance,
}));
} catch (err) {
throw RvfError.fromNative(err);
}
}
async delete(ids: string[]): Promise<RvfDeleteResult> {
this.ensureHandle();
try {
// Resolve string IDs to numeric labels for the N-API layer.
const numIds = ids
.map((id) => this.idToLabel.get(id))
.filter((label): label is number => label !== undefined);
if (numIds.length === 0) {
return { deleted: 0, epoch: 0 };
}
const result = this.handle.delete(numIds);
// Remove deleted entries from the mapping.
for (const id of ids) {
const label = this.idToLabel.get(id);
if (label !== undefined) {
this.idToLabel.delete(id);
this.labelToId.delete(label);
}
}
await this.saveMappings();
return { deleted: Number(result.deleted), epoch: result.epoch };
} catch (err) {
throw RvfError.fromNative(err);
}
}
async deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult> {
this.ensureHandle();
try {
// NAPI takes a JSON string for the filter expression.
const result = this.handle.deleteByFilter(JSON.stringify(filter));
return { deleted: Number(result.deleted), epoch: result.epoch };
} catch (err) {
throw RvfError.fromNative(err);
}
}
async compact(): Promise<RvfCompactionResult> {
this.ensureHandle();
try {
const result = this.handle.compact();
return {
segmentsCompacted: result.segmentsCompacted ?? result.segments_compacted,
bytesReclaimed: Number(result.bytesReclaimed ?? result.bytes_reclaimed),
epoch: result.epoch,
};
} catch (err) {
throw RvfError.fromNative(err);
}
}
async status(): Promise<RvfStatus> {
this.ensureHandle();
try {
const s = this.handle.status();
return mapNativeStatus(s);
} catch (err) {
throw RvfError.fromNative(err);
}
}
async close(): Promise<void> {
if (!this.handle) return;
try {
await this.saveMappings();
this.handle.close();
} catch (err) {
throw RvfError.fromNative(err);
} finally {
this.handle = null;
this.idToLabel.clear();
this.labelToId.clear();
this.nextLabel = 1;
this.storePath = '';
}
}
async fileId(): Promise<string> {
this.ensureHandle();
try {
return this.handle.fileId();
} catch (err) {
throw RvfError.fromNative(err);
}
}
async parentId(): Promise<string> {
this.ensureHandle();
try {
return this.handle.parentId();
} catch (err) {
throw RvfError.fromNative(err);
}
}
async lineageDepth(): Promise<number> {
this.ensureHandle();
try {
return this.handle.lineageDepth();
} catch (err) {
throw RvfError.fromNative(err);
}
}
async derive(childPath: string, options?: RvfOptions): Promise<RvfBackend> {
this.ensureHandle();
try {
const nativeOpts = options ? mapOptionsToNative(options) : undefined;
const childHandle = this.handle.derive(childPath, nativeOpts);
const child = new NodeBackend();
child.native = this.native;
child.handle = childHandle;
child.storePath = childPath;
// Copy parent mappings to child (COW semantics)
child.idToLabel = new Map(this.idToLabel);
child.labelToId = new Map(this.labelToId);
child.nextLabel = this.nextLabel;
await child.saveMappings();
return child;
} catch (err) {
throw RvfError.fromNative(err);
}
}
async embedKernel(
arch: number, kernelType: number, flags: number,
image: Uint8Array, apiPort: number, cmdline?: string
): Promise<number> {
this.ensureHandle();
try {
return this.handle.embedKernel(arch, kernelType, flags,
Buffer.from(image), apiPort, cmdline);
} catch (err) {
throw RvfError.fromNative(err);
}
}
async extractKernel(): Promise<RvfKernelData | null> {
this.ensureHandle();
try {
const result = this.handle.extractKernel();
if (!result) return null;
return {
header: new Uint8Array(result.header),
image: new Uint8Array(result.image),
};
} catch (err) {
throw RvfError.fromNative(err);
}
}
async embedEbpf(
programType: number, attachType: number, maxDimension: number,
bytecode: Uint8Array, btf?: Uint8Array
): Promise<number> {
this.ensureHandle();
try {
return this.handle.embedEbpf(programType, attachType, maxDimension,
Buffer.from(bytecode), btf ? Buffer.from(btf) : undefined);
} catch (err) {
throw RvfError.fromNative(err);
}
}
async extractEbpf(): Promise<RvfEbpfData | null> {
this.ensureHandle();
try {
const result = this.handle.extractEbpf();
if (!result) return null;
return {
header: new Uint8Array(result.header),
payload: new Uint8Array(result.payload),
};
} catch (err) {
throw RvfError.fromNative(err);
}
}
async segments(): Promise<RvfSegmentInfo[]> {
this.ensureHandle();
try {
const segs = this.handle.segments();
return segs.map((s: any) => ({
id: s.id,
offset: s.offset,
payloadLength: s.payloadLength ?? s.payload_length,
segType: s.segType ?? s.seg_type,
}));
} catch (err) {
throw RvfError.fromNative(err);
}
}
async dimension(): Promise<number> {
this.ensureHandle();
try {
return this.handle.dimension();
} catch (err) {
throw RvfError.fromNative(err);
}
}
// ─── String ID ↔ Numeric Label mapping helpers ───
/**
* Get or allocate a numeric label for a string ID.
* If the ID was already seen, returns the existing label.
*/
private resolveLabel(id: string): number {
let label = this.idToLabel.get(id);
if (label !== undefined) return label;
label = this.nextLabel++;
this.idToLabel.set(id, label);
this.labelToId.set(label, id);
return label;
}
/** Path to the sidecar mappings file. */
private mappingsPath(): string {
return this.storePath ? this.storePath + '.idmap.json' : '';
}
/** Persist the string↔label mapping to a sidecar JSON file. */
private async saveMappings(): Promise<void> {
const mp = this.mappingsPath();
if (!mp) return;
try {
const fs = await import('fs');
const data = JSON.stringify({
idToLabel: Object.fromEntries(this.idToLabel),
labelToId: Object.fromEntries(
Array.from(this.labelToId.entries()).map(([k, v]) => [String(k), v]),
),
nextLabel: this.nextLabel,
});
fs.writeFileSync(mp, data, 'utf-8');
} catch {
// Non-fatal: mapping persistence is best-effort (e.g. read-only FS).
}
}
/** Load the string↔label mapping from the sidecar JSON file if it exists. */
private async loadMappings(): Promise<void> {
const mp = this.mappingsPath();
if (!mp) return;
try {
const fs = await import('fs');
if (!fs.existsSync(mp)) return;
const raw = JSON.parse(fs.readFileSync(mp, 'utf-8'));
this.idToLabel = new Map(Object.entries(raw.idToLabel ?? {}).map(
([k, v]) => [k, Number(v)],
));
this.labelToId = new Map(
Object.entries(raw.labelToId ?? {}).map(([k, v]) => [Number(k), v as string]),
);
this.nextLabel = raw.nextLabel ?? this.idToLabel.size + 1;
} catch {
// Non-fatal: start with empty mappings.
}
}
}
// ---------------------------------------------------------------------------
// WasmBackend — wraps @ruvector/rvf-wasm
// ---------------------------------------------------------------------------
/**
* Backend that delegates to the `@ruvector/rvf-wasm` WASM build.
*
* The WASM microkernel exposes C-ABI store functions (`rvf_store_create`,
* `rvf_store_query`, etc.) operating on integer handles. This backend wraps
* them behind the same `RvfBackend` interface.
*
* Suitable for browser environments. The WASM module is loaded lazily.
*/
export class WasmBackend implements RvfBackend {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private wasm: any = null;
/** Integer store handle returned by `rvf_store_create` / `rvf_store_open`. */
private handle: number = 0;
private dim: number = 0;
private async loadWasm(): Promise<void> {
if (this.wasm) return;
try {
const mod = await import('@ruvector/rvf-wasm');
// wasm-pack default export is the init function
if (typeof mod.default === 'function') {
this.wasm = await mod.default();
} else {
this.wasm = mod;
}
} catch {
throw new RvfError(
RvfErrorCode.BackendNotFound,
'Could not load @ruvector/rvf-wasm — is it installed?',
);
}
}
private ensureHandle(): void {
if (!this.handle) {
throw new RvfError(RvfErrorCode.StoreClosed);
}
}
private metricCode(metric: string | undefined): number {
switch (metric) {
case 'Cosine': return 2;
case 'InnerProduct': return 1;
default: return 0; // L2
}
}
async create(_path: string, options: RvfOptions): Promise<void> {
await this.loadWasm();
try {
const nativeOpts = mapOptionsToNative(options);
const dim = nativeOpts.dimension as number;
const metric = this.metricCode(nativeOpts.metric as string);
const h = this.wasm.rvf_store_create(dim, metric);
if (h <= 0) throw new Error('rvf_store_create returned ' + h);
this.handle = h;
this.dim = dim;
} catch (err) {
throw RvfError.fromNative(err);
}
}
async open(_path: string): Promise<void> {
throw new RvfError(
RvfErrorCode.BackendNotFound,
'WASM backend does not support file-based open (in-memory only)',
);
}
async openReadonly(_path: string): Promise<void> {
throw new RvfError(
RvfErrorCode.BackendNotFound,
'WASM backend does not support file-based openReadonly (in-memory only)',
);
}
async ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult> {
this.ensureHandle();
try {
const n = entries.length;
if (n === 0) return { accepted: 0, rejected: 0, epoch: 0 };
const dim = this.dim || (entries[0].vector instanceof Float32Array
? entries[0].vector.length : entries[0].vector.length);
const flat = new Float32Array(n * dim);
const ids = new BigUint64Array(n);
for (let i = 0; i < n; i++) {
const v = entries[i].vector;
const f32 = v instanceof Float32Array ? v : new Float32Array(v);
flat.set(f32, i * dim);
ids[i] = BigInt(entries[i].id);
}
// Allocate in WASM memory and call
const vecsPtr = this.wasm.rvf_alloc(flat.byteLength);
const idsPtr = this.wasm.rvf_alloc(ids.byteLength);
new Float32Array(this.wasm.memory.buffer, vecsPtr, flat.length).set(flat);
new BigUint64Array(this.wasm.memory.buffer, idsPtr, ids.length).set(ids);
const accepted = this.wasm.rvf_store_ingest(this.handle, vecsPtr, idsPtr, n);
this.wasm.rvf_free(vecsPtr, flat.byteLength);
this.wasm.rvf_free(idsPtr, ids.byteLength);
return { accepted: accepted > 0 ? accepted : 0, rejected: accepted < 0 ? n : 0, epoch: 0 };
} catch (err) {
throw RvfError.fromNative(err);
}
}
async query(
vector: Float32Array,
k: number,
_options?: RvfQueryOptions,
): Promise<RvfSearchResult[]> {
this.ensureHandle();
try {
const queryPtr = this.wasm.rvf_alloc(vector.byteLength);
new Float32Array(this.wasm.memory.buffer, queryPtr, vector.length).set(vector);
// Each result = 8 bytes id + 4 bytes dist = 12 bytes
const outSize = k * 12;
const outPtr = this.wasm.rvf_alloc(outSize);
const count = this.wasm.rvf_store_query(this.handle, queryPtr, k, 0, outPtr);
const results: RvfSearchResult[] = [];
const view = new DataView(this.wasm.memory.buffer);
for (let i = 0; i < count; i++) {
const off = outPtr + i * 12;
const id = view.getBigUint64(off, true);
const dist = view.getFloat32(off + 8, true);
results.push({ id: String(id), distance: dist });
}
this.wasm.rvf_free(queryPtr, vector.byteLength);
this.wasm.rvf_free(outPtr, outSize);
return results;
} catch (err) {
throw RvfError.fromNative(err);
}
}
async delete(ids: string[]): Promise<RvfDeleteResult> {
this.ensureHandle();
try {
const arr = new BigUint64Array(ids.map((id) => BigInt(id)));
const ptr = this.wasm.rvf_alloc(arr.byteLength);
new BigUint64Array(this.wasm.memory.buffer, ptr, arr.length).set(arr);
const deleted = this.wasm.rvf_store_delete(this.handle, ptr, ids.length);
this.wasm.rvf_free(ptr, arr.byteLength);
return { deleted: deleted > 0 ? deleted : 0, epoch: 0 };
} catch (err) {
throw RvfError.fromNative(err);
}
}
async deleteByFilter(_filter: RvfFilterExpr): Promise<RvfDeleteResult> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'deleteByFilter not supported in WASM backend');
}
async compact(): Promise<RvfCompactionResult> {
return { segmentsCompacted: 0, bytesReclaimed: 0, epoch: 0 };
}
async status(): Promise<RvfStatus> {
this.ensureHandle();
try {
const outPtr = this.wasm.rvf_alloc(20);
this.wasm.rvf_store_status(this.handle, outPtr);
const view = new DataView(this.wasm.memory.buffer);
const totalVectors = view.getUint32(outPtr, true);
const dim = view.getUint32(outPtr + 4, true);
this.wasm.rvf_free(outPtr, 20);
return {
totalVectors,
totalSegments: 1,
fileSizeBytes: 0,
epoch: 0,
profileId: 0,
compactionState: 'idle',
deadSpaceRatio: 0,
readOnly: false,
};
} catch (err) {
throw RvfError.fromNative(err);
}
}
async close(): Promise<void> {
if (!this.handle) return;
try {
this.wasm.rvf_store_close(this.handle);
} catch (err) {
throw RvfError.fromNative(err);
} finally {
this.handle = 0;
}
}
async fileId(): Promise<string> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'fileId not supported in WASM backend');
}
async parentId(): Promise<string> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'parentId not supported in WASM backend');
}
async lineageDepth(): Promise<number> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'lineageDepth not supported in WASM backend');
}
async derive(_childPath: string, _options?: RvfOptions): Promise<RvfBackend> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'derive not supported in WASM backend');
}
async embedKernel(): Promise<number> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'embedKernel not supported in WASM backend');
}
async extractKernel(): Promise<RvfKernelData | null> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'extractKernel not supported in WASM backend');
}
async embedEbpf(): Promise<number> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'embedEbpf not supported in WASM backend');
}
async extractEbpf(): Promise<RvfEbpfData | null> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'extractEbpf not supported in WASM backend');
}
async segments(): Promise<RvfSegmentInfo[]> {
throw new RvfError(RvfErrorCode.BackendNotFound, 'segments not supported in WASM backend');
}
async dimension(): Promise<number> {
this.ensureHandle();
const d = this.wasm.rvf_store_dimension(this.handle);
if (d < 0) throw new RvfError(RvfErrorCode.StoreClosed);
return d;
}
}
// ---------------------------------------------------------------------------
// Backend resolution
// ---------------------------------------------------------------------------
/**
* Resolve a `BackendType` to a concrete `RvfBackend` instance.
*
* - `'node'` Always returns a `NodeBackend`.
* - `'wasm'` Always returns a `WasmBackend`.
* - `'auto'` Tries `node` first, falls back to `wasm`.
*/
export function resolveBackend(type: BackendType): RvfBackend {
switch (type) {
case 'node':
return new NodeBackend();
case 'wasm':
return new WasmBackend();
case 'auto': {
// In Node.js environments, prefer native; in browsers, prefer WASM.
const isNode =
typeof process !== 'undefined' &&
typeof process.versions !== 'undefined' &&
typeof process.versions.node === 'string';
return isNode ? new NodeBackend() : new WasmBackend();
}
}
}
// ---------------------------------------------------------------------------
// Mapping helpers (TS options -> native/wasm shapes)
// ---------------------------------------------------------------------------
function mapMetricToNative(metric: string | undefined): string {
switch (metric) {
case 'cosine':
return 'Cosine';
case 'dotproduct':
return 'InnerProduct';
case 'l2':
default:
return 'L2';
}
}
function mapCompressionToNative(compression: string | undefined): string {
switch (compression) {
case 'scalar':
return 'Scalar';
case 'product':
return 'Product';
case 'none':
default:
return 'None';
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapOptionsToNative(options: RvfOptions): Record<string, any> {
return {
dimension: options.dimensions,
metric: mapMetricToNative(options.metric),
profile: options.profile ?? 0,
compression: mapCompressionToNative(options.compression),
signing: options.signing ?? false,
m: options.m ?? 16,
ef_construction: options.efConstruction ?? 200,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapQueryOptionsToNative(options: RvfQueryOptions): Record<string, any> {
return {
ef_search: options.efSearch ?? 100,
// NAPI accepts the filter as a JSON string, not an object.
filter: options.filter ? JSON.stringify(options.filter) : undefined,
timeout_ms: options.timeoutMs ?? 0,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function mapNativeStatus(s: any): RvfStatus {
return {
totalVectors: s.total_vectors ?? s.totalVectors ?? 0,
totalSegments: s.total_segments ?? s.totalSegments ?? 0,
fileSizeBytes: s.file_size ?? s.fileSizeBytes ?? 0,
epoch: s.current_epoch ?? s.epoch ?? 0,
profileId: s.profile_id ?? s.profileId ?? 0,
compactionState: mapCompactionState(s.compaction_state ?? s.compactionState),
deadSpaceRatio: s.dead_space_ratio ?? s.deadSpaceRatio ?? 0,
readOnly: s.read_only ?? s.readOnly ?? false,
};
}
function mapCompactionState(state: unknown): 'idle' | 'running' | 'emergency' {
if (typeof state === 'string') {
const lower = state.toLowerCase();
if (lower === 'running') return 'running';
if (lower === 'emergency') return 'emergency';
}
return 'idle';
}

View File

@@ -0,0 +1,119 @@
import type { RvfOptions, RvfQueryOptions, RvfSearchResult, RvfIngestResult, RvfIngestEntry, RvfDeleteResult, RvfCompactionResult, RvfStatus, RvfFilterExpr, RvfKernelData, RvfEbpfData, RvfSegmentInfo, BackendType } from './types';
import type { RvfBackend } from './backend';
/**
* Main user-facing RVF database class.
*
* Wraps a backend implementation (`NodeBackend` or `WasmBackend`) and exposes
* an ergonomic async API that mirrors the Rust `RvfStore` surface.
*
* Use the static factory methods (`create`, `open`, `openReadonly`) to obtain
* an instance. Do not construct directly.
*/
export declare class RvfDatabase {
private backend;
private closed;
private constructor();
/**
* Create a new RVF store at `path`.
*
* @param path File path for the new store.
* @param options Store creation options (dimensions is required).
* @param backend Backend to use. Default: `'auto'`.
*/
static create(path: string, options: RvfOptions, backend?: BackendType): Promise<RvfDatabase>;
/**
* Open an existing RVF store for read-write access.
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static open(path: string, backend?: BackendType): Promise<RvfDatabase>;
/**
* Open an existing RVF store for read-only access (no lock required).
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static openReadonly(path: string, backend?: BackendType): Promise<RvfDatabase>;
/**
* Create an RvfDatabase from an already-initialized backend.
*
* Used internally (e.g. by `derive()`) to wrap a child backend that was
* created by the native layer without going through the normal open/create
* flow.
*/
static fromBackend(backend: RvfBackend): RvfDatabase;
/**
* Ingest a batch of vectors into the store.
*
* @param entries Array of `{ id, vector, metadata? }` entries.
* @returns Counts of accepted/rejected vectors and the new epoch.
*/
ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult>;
/**
* Soft-delete vectors by their IDs.
*
* @param ids Vector IDs to delete.
*/
delete(ids: string[]): Promise<RvfDeleteResult>;
/**
* Soft-delete all vectors matching a filter expression.
*
* @param filter The filter to match against vector metadata.
*/
deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult>;
/**
* Query for the `k` nearest neighbors of a given vector.
*
* @param vector The query embedding.
* @param k Number of results to return.
* @param options Optional query parameters (efSearch, filter, timeout).
* @returns Sorted search results (closest first).
*/
query(vector: Float32Array | number[], k: number, options?: RvfQueryOptions): Promise<RvfSearchResult[]>;
/**
* Run compaction to reclaim dead space from soft-deleted vectors.
*/
compact(): Promise<RvfCompactionResult>;
/**
* Get the current store status (vector count, file size, epoch, etc.).
*/
status(): Promise<RvfStatus>;
/** Get this file's unique identifier as a hex string. */
fileId(): Promise<string>;
/** Get the parent file's identifier as a hex string (all zeros if root). */
parentId(): Promise<string>;
/** Get the lineage depth (0 for root files). */
lineageDepth(): Promise<number>;
/**
* Derive a child store from this parent.
*
* Creates a new RVF file at `childPath` that records this store as its
* parent for provenance tracking. Returns a new `RvfDatabase` wrapping
* the child store.
*/
derive(childPath: string, options?: RvfOptions): Promise<RvfDatabase>;
/** Embed a kernel image. Returns the segment ID. */
embedKernel(arch: number, kernelType: number, flags: number, image: Uint8Array, apiPort: number, cmdline?: string): Promise<number>;
/** Extract the kernel image. Returns null if not present. */
extractKernel(): Promise<RvfKernelData | null>;
/** Embed an eBPF program. Returns the segment ID. */
embedEbpf(programType: number, attachType: number, maxDimension: number, bytecode: Uint8Array, btf?: Uint8Array): Promise<number>;
/** Extract the eBPF program. Returns null if not present. */
extractEbpf(): Promise<RvfEbpfData | null>;
/** Get the list of segments in the store. */
segments(): Promise<RvfSegmentInfo[]>;
/** Get the vector dimensionality. */
dimension(): Promise<number>;
/**
* Close the store, releasing the writer lock and flushing pending data.
*
* After calling `close()`, all other methods will throw `RvfError` with
* code `StoreClosed`.
*/
close(): Promise<void>;
/** True if the store has been closed. */
get isClosed(): boolean;
private ensureOpen;
}
//# sourceMappingURL=database.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["database.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,226 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RvfDatabase = void 0;
const backend_1 = require("./backend");
const errors_1 = require("./errors");
/**
* Main user-facing RVF database class.
*
* Wraps a backend implementation (`NodeBackend` or `WasmBackend`) and exposes
* an ergonomic async API that mirrors the Rust `RvfStore` surface.
*
* Use the static factory methods (`create`, `open`, `openReadonly`) to obtain
* an instance. Do not construct directly.
*/
class RvfDatabase {
constructor(backend) {
this.closed = false;
this.backend = backend;
}
// -----------------------------------------------------------------------
// Factory methods
// -----------------------------------------------------------------------
/**
* Create a new RVF store at `path`.
*
* @param path File path for the new store.
* @param options Store creation options (dimensions is required).
* @param backend Backend to use. Default: `'auto'`.
*/
static async create(path, options, backend = 'auto') {
const impl = (0, backend_1.resolveBackend)(backend);
await impl.create(path, options);
return new RvfDatabase(impl);
}
/**
* Open an existing RVF store for read-write access.
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static async open(path, backend = 'auto') {
const impl = (0, backend_1.resolveBackend)(backend);
await impl.open(path);
return new RvfDatabase(impl);
}
/**
* Open an existing RVF store for read-only access (no lock required).
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static async openReadonly(path, backend = 'auto') {
const impl = (0, backend_1.resolveBackend)(backend);
await impl.openReadonly(path);
return new RvfDatabase(impl);
}
/**
* Create an RvfDatabase from an already-initialized backend.
*
* Used internally (e.g. by `derive()`) to wrap a child backend that was
* created by the native layer without going through the normal open/create
* flow.
*/
static fromBackend(backend) {
return new RvfDatabase(backend);
}
// -----------------------------------------------------------------------
// Write operations
// -----------------------------------------------------------------------
/**
* Ingest a batch of vectors into the store.
*
* @param entries Array of `{ id, vector, metadata? }` entries.
* @returns Counts of accepted/rejected vectors and the new epoch.
*/
async ingestBatch(entries) {
this.ensureOpen();
return this.backend.ingestBatch(entries);
}
/**
* Soft-delete vectors by their IDs.
*
* @param ids Vector IDs to delete.
*/
async delete(ids) {
this.ensureOpen();
return this.backend.delete(ids);
}
/**
* Soft-delete all vectors matching a filter expression.
*
* @param filter The filter to match against vector metadata.
*/
async deleteByFilter(filter) {
this.ensureOpen();
return this.backend.deleteByFilter(filter);
}
// -----------------------------------------------------------------------
// Read operations
// -----------------------------------------------------------------------
/**
* Query for the `k` nearest neighbors of a given vector.
*
* @param vector The query embedding.
* @param k Number of results to return.
* @param options Optional query parameters (efSearch, filter, timeout).
* @returns Sorted search results (closest first).
*/
async query(vector, k, options) {
this.ensureOpen();
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
return this.backend.query(f32, k, options);
}
// -----------------------------------------------------------------------
// Maintenance
// -----------------------------------------------------------------------
/**
* Run compaction to reclaim dead space from soft-deleted vectors.
*/
async compact() {
this.ensureOpen();
return this.backend.compact();
}
/**
* Get the current store status (vector count, file size, epoch, etc.).
*/
async status() {
this.ensureOpen();
return this.backend.status();
}
// -----------------------------------------------------------------------
// Lineage
// -----------------------------------------------------------------------
/** Get this file's unique identifier as a hex string. */
async fileId() {
this.ensureOpen();
return this.backend.fileId();
}
/** Get the parent file's identifier as a hex string (all zeros if root). */
async parentId() {
this.ensureOpen();
return this.backend.parentId();
}
/** Get the lineage depth (0 for root files). */
async lineageDepth() {
this.ensureOpen();
return this.backend.lineageDepth();
}
/**
* Derive a child store from this parent.
*
* Creates a new RVF file at `childPath` that records this store as its
* parent for provenance tracking. Returns a new `RvfDatabase` wrapping
* the child store.
*/
async derive(childPath, options) {
this.ensureOpen();
const childBackend = await this.backend.derive(childPath, options);
return RvfDatabase.fromBackend(childBackend);
}
// -----------------------------------------------------------------------
// Kernel / eBPF
// -----------------------------------------------------------------------
/** Embed a kernel image. Returns the segment ID. */
async embedKernel(arch, kernelType, flags, image, apiPort, cmdline) {
this.ensureOpen();
return this.backend.embedKernel(arch, kernelType, flags, image, apiPort, cmdline);
}
/** Extract the kernel image. Returns null if not present. */
async extractKernel() {
this.ensureOpen();
return this.backend.extractKernel();
}
/** Embed an eBPF program. Returns the segment ID. */
async embedEbpf(programType, attachType, maxDimension, bytecode, btf) {
this.ensureOpen();
return this.backend.embedEbpf(programType, attachType, maxDimension, bytecode, btf);
}
/** Extract the eBPF program. Returns null if not present. */
async extractEbpf() {
this.ensureOpen();
return this.backend.extractEbpf();
}
// -----------------------------------------------------------------------
// Inspection
// -----------------------------------------------------------------------
/** Get the list of segments in the store. */
async segments() {
this.ensureOpen();
return this.backend.segments();
}
/** Get the vector dimensionality. */
async dimension() {
this.ensureOpen();
return this.backend.dimension();
}
// -----------------------------------------------------------------------
// Lifecycle
// -----------------------------------------------------------------------
/**
* Close the store, releasing the writer lock and flushing pending data.
*
* After calling `close()`, all other methods will throw `RvfError` with
* code `StoreClosed`.
*/
async close() {
if (this.closed)
return;
this.closed = true;
await this.backend.close();
}
/** True if the store has been closed. */
get isClosed() {
return this.closed;
}
// -----------------------------------------------------------------------
// Internal
// -----------------------------------------------------------------------
ensureOpen() {
if (this.closed) {
throw new errors_1.RvfError(errors_1.RvfErrorCode.StoreClosed);
}
}
}
exports.RvfDatabase = RvfDatabase;
//# sourceMappingURL=database.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"database.js","sourceRoot":"","sources":["database.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,291 @@
import type {
RvfOptions,
RvfQueryOptions,
RvfSearchResult,
RvfIngestResult,
RvfIngestEntry,
RvfDeleteResult,
RvfCompactionResult,
RvfStatus,
RvfFilterExpr,
RvfKernelData,
RvfEbpfData,
RvfSegmentInfo,
BackendType,
} from './types';
import type { RvfBackend } from './backend';
import { resolveBackend } from './backend';
import { RvfError, RvfErrorCode } from './errors';
/**
* Main user-facing RVF database class.
*
* Wraps a backend implementation (`NodeBackend` or `WasmBackend`) and exposes
* an ergonomic async API that mirrors the Rust `RvfStore` surface.
*
* Use the static factory methods (`create`, `open`, `openReadonly`) to obtain
* an instance. Do not construct directly.
*/
export class RvfDatabase {
private backend: RvfBackend;
private closed = false;
private constructor(backend: RvfBackend) {
this.backend = backend;
}
// -----------------------------------------------------------------------
// Factory methods
// -----------------------------------------------------------------------
/**
* Create a new RVF store at `path`.
*
* @param path File path for the new store.
* @param options Store creation options (dimensions is required).
* @param backend Backend to use. Default: `'auto'`.
*/
static async create(
path: string,
options: RvfOptions,
backend: BackendType = 'auto',
): Promise<RvfDatabase> {
const impl = resolveBackend(backend);
await impl.create(path, options);
return new RvfDatabase(impl);
}
/**
* Open an existing RVF store for read-write access.
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static async open(
path: string,
backend: BackendType = 'auto',
): Promise<RvfDatabase> {
const impl = resolveBackend(backend);
await impl.open(path);
return new RvfDatabase(impl);
}
/**
* Open an existing RVF store for read-only access (no lock required).
*
* @param path File path to an existing `.rvf` file.
* @param backend Backend to use. Default: `'auto'`.
*/
static async openReadonly(
path: string,
backend: BackendType = 'auto',
): Promise<RvfDatabase> {
const impl = resolveBackend(backend);
await impl.openReadonly(path);
return new RvfDatabase(impl);
}
/**
* Create an RvfDatabase from an already-initialized backend.
*
* Used internally (e.g. by `derive()`) to wrap a child backend that was
* created by the native layer without going through the normal open/create
* flow.
*/
static fromBackend(backend: RvfBackend): RvfDatabase {
return new RvfDatabase(backend);
}
// -----------------------------------------------------------------------
// Write operations
// -----------------------------------------------------------------------
/**
* Ingest a batch of vectors into the store.
*
* @param entries Array of `{ id, vector, metadata? }` entries.
* @returns Counts of accepted/rejected vectors and the new epoch.
*/
async ingestBatch(entries: RvfIngestEntry[]): Promise<RvfIngestResult> {
this.ensureOpen();
return this.backend.ingestBatch(entries);
}
/**
* Soft-delete vectors by their IDs.
*
* @param ids Vector IDs to delete.
*/
async delete(ids: string[]): Promise<RvfDeleteResult> {
this.ensureOpen();
return this.backend.delete(ids);
}
/**
* Soft-delete all vectors matching a filter expression.
*
* @param filter The filter to match against vector metadata.
*/
async deleteByFilter(filter: RvfFilterExpr): Promise<RvfDeleteResult> {
this.ensureOpen();
return this.backend.deleteByFilter(filter);
}
// -----------------------------------------------------------------------
// Read operations
// -----------------------------------------------------------------------
/**
* Query for the `k` nearest neighbors of a given vector.
*
* @param vector The query embedding.
* @param k Number of results to return.
* @param options Optional query parameters (efSearch, filter, timeout).
* @returns Sorted search results (closest first).
*/
async query(
vector: Float32Array | number[],
k: number,
options?: RvfQueryOptions,
): Promise<RvfSearchResult[]> {
this.ensureOpen();
const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
return this.backend.query(f32, k, options);
}
// -----------------------------------------------------------------------
// Maintenance
// -----------------------------------------------------------------------
/**
* Run compaction to reclaim dead space from soft-deleted vectors.
*/
async compact(): Promise<RvfCompactionResult> {
this.ensureOpen();
return this.backend.compact();
}
/**
* Get the current store status (vector count, file size, epoch, etc.).
*/
async status(): Promise<RvfStatus> {
this.ensureOpen();
return this.backend.status();
}
// -----------------------------------------------------------------------
// Lineage
// -----------------------------------------------------------------------
/** Get this file's unique identifier as a hex string. */
async fileId(): Promise<string> {
this.ensureOpen();
return this.backend.fileId();
}
/** Get the parent file's identifier as a hex string (all zeros if root). */
async parentId(): Promise<string> {
this.ensureOpen();
return this.backend.parentId();
}
/** Get the lineage depth (0 for root files). */
async lineageDepth(): Promise<number> {
this.ensureOpen();
return this.backend.lineageDepth();
}
/**
* Derive a child store from this parent.
*
* Creates a new RVF file at `childPath` that records this store as its
* parent for provenance tracking. Returns a new `RvfDatabase` wrapping
* the child store.
*/
async derive(childPath: string, options?: RvfOptions): Promise<RvfDatabase> {
this.ensureOpen();
const childBackend = await this.backend.derive(childPath, options);
return RvfDatabase.fromBackend(childBackend);
}
// -----------------------------------------------------------------------
// Kernel / eBPF
// -----------------------------------------------------------------------
/** Embed a kernel image. Returns the segment ID. */
async embedKernel(
arch: number, kernelType: number, flags: number,
image: Uint8Array, apiPort: number, cmdline?: string,
): Promise<number> {
this.ensureOpen();
return this.backend.embedKernel(arch, kernelType, flags, image, apiPort, cmdline);
}
/** Extract the kernel image. Returns null if not present. */
async extractKernel(): Promise<RvfKernelData | null> {
this.ensureOpen();
return this.backend.extractKernel();
}
/** Embed an eBPF program. Returns the segment ID. */
async embedEbpf(
programType: number, attachType: number, maxDimension: number,
bytecode: Uint8Array, btf?: Uint8Array,
): Promise<number> {
this.ensureOpen();
return this.backend.embedEbpf(programType, attachType, maxDimension, bytecode, btf);
}
/** Extract the eBPF program. Returns null if not present. */
async extractEbpf(): Promise<RvfEbpfData | null> {
this.ensureOpen();
return this.backend.extractEbpf();
}
// -----------------------------------------------------------------------
// Inspection
// -----------------------------------------------------------------------
/** Get the list of segments in the store. */
async segments(): Promise<RvfSegmentInfo[]> {
this.ensureOpen();
return this.backend.segments();
}
/** Get the vector dimensionality. */
async dimension(): Promise<number> {
this.ensureOpen();
return this.backend.dimension();
}
// -----------------------------------------------------------------------
// Lifecycle
// -----------------------------------------------------------------------
/**
* Close the store, releasing the writer lock and flushing pending data.
*
* After calling `close()`, all other methods will throw `RvfError` with
* code `StoreClosed`.
*/
async close(): Promise<void> {
if (this.closed) return;
this.closed = true;
await this.backend.close();
}
/** True if the store has been closed. */
get isClosed(): boolean {
return this.closed;
}
// -----------------------------------------------------------------------
// Internal
// -----------------------------------------------------------------------
private ensureOpen(): void {
if (this.closed) {
throw new RvfError(RvfErrorCode.StoreClosed);
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* Error codes mirroring the Rust `ErrorCode` enum (rvf-types).
*
* The high byte is the category, the low byte is the specific error.
*/
export declare enum RvfErrorCode {
Ok = 0,
OkPartial = 1,
InvalidMagic = 256,
InvalidVersion = 257,
InvalidChecksum = 258,
InvalidSignature = 259,
TruncatedSegment = 260,
InvalidManifest = 261,
ManifestNotFound = 262,
UnknownSegmentType = 263,
AlignmentError = 264,
DimensionMismatch = 512,
EmptyIndex = 513,
MetricUnsupported = 514,
FilterParseError = 515,
KTooLarge = 516,
Timeout = 517,
LockHeld = 768,
LockStale = 769,
DiskFull = 770,
FsyncFailed = 771,
SegmentTooLarge = 772,
ReadOnly = 773,
TileTrap = 1024,
TileOom = 1025,
TileTimeout = 1026,
TileInvalidMsg = 1027,
TileUnsupportedOp = 1028,
KeyNotFound = 1280,
KeyExpired = 1281,
DecryptFailed = 1282,
AlgoUnsupported = 1283,
BackendNotFound = 65280,
BackendInitFailed = 65281,
StoreClosed = 65282
}
/**
* Custom error class for all RVF operations.
*
* Carries a typed `code` field for programmatic matching and a
* human-readable `message`.
*/
export declare class RvfError extends Error {
/** The RVF error code. */
readonly code: RvfErrorCode;
/** Error category (high byte of the code). */
get category(): number;
/** True when the category indicates a format-level (fatal) error. */
get isFormatError(): boolean;
constructor(code: RvfErrorCode, detail?: string);
/**
* Create an RvfError from a native binding error.
* Attempts to extract an error code from the message or object.
*/
static fromNative(err: unknown): RvfError;
}
//# sourceMappingURL=errors.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,oBAAY,YAAY;IAEtB,EAAE,IAAS;IACX,SAAS,IAAS;IAGlB,YAAY,MAAS;IACrB,cAAc,MAAS;IACvB,eAAe,MAAS;IACxB,gBAAgB,MAAS;IACzB,gBAAgB,MAAS;IACzB,eAAe,MAAS;IACxB,gBAAgB,MAAS;IACzB,kBAAkB,MAAS;IAC3B,cAAc,MAAS;IAGvB,iBAAiB,MAAS;IAC1B,UAAU,MAAS;IACnB,iBAAiB,MAAS;IAC1B,gBAAgB,MAAS;IACzB,SAAS,MAAS;IAClB,OAAO,MAAS;IAGhB,QAAQ,MAAS;IACjB,SAAS,MAAS;IAClB,QAAQ,MAAS;IACjB,WAAW,MAAS;IACpB,eAAe,MAAS;IACxB,QAAQ,MAAS;IAGjB,QAAQ,OAAS;IACjB,OAAO,OAAS;IAChB,WAAW,OAAS;IACpB,cAAc,OAAS;IACvB,iBAAiB,OAAS;IAG1B,WAAW,OAAS;IACpB,UAAU,OAAS;IACnB,aAAa,OAAS;IACtB,eAAe,OAAS;IAGxB,eAAe,QAAS;IACxB,iBAAiB,QAAS;IAC1B,WAAW,QAAS;CACrB;AAyCD;;;;;GAKG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,0BAA0B;IAC1B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAE5B,8CAA8C;IAC9C,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,qEAAqE;IACrE,IAAI,aAAa,IAAI,OAAO,CAE3B;gBAEW,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM;IAQ/C;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ;CAc1C"}

View File

@@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RvfError = exports.RvfErrorCode = void 0;
/**
* Error codes mirroring the Rust `ErrorCode` enum (rvf-types).
*
* The high byte is the category, the low byte is the specific error.
*/
var RvfErrorCode;
(function (RvfErrorCode) {
// Category 0x00: Success
RvfErrorCode[RvfErrorCode["Ok"] = 0] = "Ok";
RvfErrorCode[RvfErrorCode["OkPartial"] = 1] = "OkPartial";
// Category 0x01: Format Errors
RvfErrorCode[RvfErrorCode["InvalidMagic"] = 256] = "InvalidMagic";
RvfErrorCode[RvfErrorCode["InvalidVersion"] = 257] = "InvalidVersion";
RvfErrorCode[RvfErrorCode["InvalidChecksum"] = 258] = "InvalidChecksum";
RvfErrorCode[RvfErrorCode["InvalidSignature"] = 259] = "InvalidSignature";
RvfErrorCode[RvfErrorCode["TruncatedSegment"] = 260] = "TruncatedSegment";
RvfErrorCode[RvfErrorCode["InvalidManifest"] = 261] = "InvalidManifest";
RvfErrorCode[RvfErrorCode["ManifestNotFound"] = 262] = "ManifestNotFound";
RvfErrorCode[RvfErrorCode["UnknownSegmentType"] = 263] = "UnknownSegmentType";
RvfErrorCode[RvfErrorCode["AlignmentError"] = 264] = "AlignmentError";
// Category 0x02: Query Errors
RvfErrorCode[RvfErrorCode["DimensionMismatch"] = 512] = "DimensionMismatch";
RvfErrorCode[RvfErrorCode["EmptyIndex"] = 513] = "EmptyIndex";
RvfErrorCode[RvfErrorCode["MetricUnsupported"] = 514] = "MetricUnsupported";
RvfErrorCode[RvfErrorCode["FilterParseError"] = 515] = "FilterParseError";
RvfErrorCode[RvfErrorCode["KTooLarge"] = 516] = "KTooLarge";
RvfErrorCode[RvfErrorCode["Timeout"] = 517] = "Timeout";
// Category 0x03: Write Errors
RvfErrorCode[RvfErrorCode["LockHeld"] = 768] = "LockHeld";
RvfErrorCode[RvfErrorCode["LockStale"] = 769] = "LockStale";
RvfErrorCode[RvfErrorCode["DiskFull"] = 770] = "DiskFull";
RvfErrorCode[RvfErrorCode["FsyncFailed"] = 771] = "FsyncFailed";
RvfErrorCode[RvfErrorCode["SegmentTooLarge"] = 772] = "SegmentTooLarge";
RvfErrorCode[RvfErrorCode["ReadOnly"] = 773] = "ReadOnly";
// Category 0x04: Tile Errors (WASM Microkernel)
RvfErrorCode[RvfErrorCode["TileTrap"] = 1024] = "TileTrap";
RvfErrorCode[RvfErrorCode["TileOom"] = 1025] = "TileOom";
RvfErrorCode[RvfErrorCode["TileTimeout"] = 1026] = "TileTimeout";
RvfErrorCode[RvfErrorCode["TileInvalidMsg"] = 1027] = "TileInvalidMsg";
RvfErrorCode[RvfErrorCode["TileUnsupportedOp"] = 1028] = "TileUnsupportedOp";
// Category 0x05: Crypto Errors
RvfErrorCode[RvfErrorCode["KeyNotFound"] = 1280] = "KeyNotFound";
RvfErrorCode[RvfErrorCode["KeyExpired"] = 1281] = "KeyExpired";
RvfErrorCode[RvfErrorCode["DecryptFailed"] = 1282] = "DecryptFailed";
RvfErrorCode[RvfErrorCode["AlgoUnsupported"] = 1283] = "AlgoUnsupported";
// SDK-level errors (0xFF__)
RvfErrorCode[RvfErrorCode["BackendNotFound"] = 65280] = "BackendNotFound";
RvfErrorCode[RvfErrorCode["BackendInitFailed"] = 65281] = "BackendInitFailed";
RvfErrorCode[RvfErrorCode["StoreClosed"] = 65282] = "StoreClosed";
})(RvfErrorCode || (exports.RvfErrorCode = RvfErrorCode = {}));
/** Human-readable labels for each error code. */
const ERROR_MESSAGES = {
[RvfErrorCode.Ok]: 'Operation succeeded',
[RvfErrorCode.OkPartial]: 'Partial success (some items failed)',
[RvfErrorCode.InvalidMagic]: 'Segment magic mismatch',
[RvfErrorCode.InvalidVersion]: 'Unsupported segment version',
[RvfErrorCode.InvalidChecksum]: 'Segment hash verification failed',
[RvfErrorCode.InvalidSignature]: 'Cryptographic signature invalid',
[RvfErrorCode.TruncatedSegment]: 'Segment payload shorter than declared',
[RvfErrorCode.InvalidManifest]: 'Root manifest validation failed',
[RvfErrorCode.ManifestNotFound]: 'No valid manifest in file',
[RvfErrorCode.UnknownSegmentType]: 'Unrecognized segment type',
[RvfErrorCode.AlignmentError]: 'Data not at expected 64-byte boundary',
[RvfErrorCode.DimensionMismatch]: 'Query vector dimension != index dimension',
[RvfErrorCode.EmptyIndex]: 'No index segments available',
[RvfErrorCode.MetricUnsupported]: 'Requested distance metric not available',
[RvfErrorCode.FilterParseError]: 'Invalid filter expression',
[RvfErrorCode.KTooLarge]: 'Requested K exceeds available vectors',
[RvfErrorCode.Timeout]: 'Query exceeded time budget',
[RvfErrorCode.LockHeld]: 'Another writer holds the lock',
[RvfErrorCode.LockStale]: 'Lock file exists but owner is dead',
[RvfErrorCode.DiskFull]: 'Insufficient space for write',
[RvfErrorCode.FsyncFailed]: 'Durable write (fsync) failed',
[RvfErrorCode.SegmentTooLarge]: 'Segment exceeds 4 GB limit',
[RvfErrorCode.ReadOnly]: 'Store opened in read-only mode',
[RvfErrorCode.TileTrap]: 'WASM trap (OOB, unreachable, stack overflow)',
[RvfErrorCode.TileOom]: 'Tile exceeded scratch memory',
[RvfErrorCode.TileTimeout]: 'Tile computation exceeded time budget',
[RvfErrorCode.TileInvalidMsg]: 'Malformed hub-tile message',
[RvfErrorCode.TileUnsupportedOp]: 'Operation not available on this profile',
[RvfErrorCode.KeyNotFound]: 'Referenced key_id not found',
[RvfErrorCode.KeyExpired]: 'Key past valid_until timestamp',
[RvfErrorCode.DecryptFailed]: 'Decryption or auth tag verification failed',
[RvfErrorCode.AlgoUnsupported]: 'Cryptographic algorithm not implemented',
[RvfErrorCode.BackendNotFound]: 'No suitable backend found (install @ruvector/rvf-node or @ruvector/rvf-wasm)',
[RvfErrorCode.BackendInitFailed]: 'Backend initialization failed',
[RvfErrorCode.StoreClosed]: 'Store has been closed',
};
/**
* Custom error class for all RVF operations.
*
* Carries a typed `code` field for programmatic matching and a
* human-readable `message`.
*/
class RvfError extends Error {
/** Error category (high byte of the code). */
get category() {
return (this.code >> 8) & 0xff;
}
/** True when the category indicates a format-level (fatal) error. */
get isFormatError() {
return this.category === 0x01;
}
constructor(code, detail) {
const base = ERROR_MESSAGES[code] ?? `RVF error 0x${code.toString(16).padStart(4, '0')}`;
const message = detail ? `${base}: ${detail}` : base;
super(message);
this.name = 'RvfError';
this.code = code;
}
/**
* Create an RvfError from a native binding error.
* Attempts to extract an error code from the message or object.
*/
static fromNative(err) {
if (err instanceof RvfError)
return err;
if (err instanceof Error) {
const codeMatch = err.message.match(/0x([0-9a-fA-F]{4})/);
if (codeMatch) {
const code = parseInt(codeMatch[1], 16);
if (code in RvfErrorCode) {
return new RvfError(code, err.message);
}
}
return new RvfError(RvfErrorCode.BackendInitFailed, err.message);
}
return new RvfError(RvfErrorCode.BackendInitFailed, String(err));
}
}
exports.RvfError = RvfError;
//# sourceMappingURL=errors.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.js","sourceRoot":"","sources":["errors.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,142 @@
/**
* Error codes mirroring the Rust `ErrorCode` enum (rvf-types).
*
* The high byte is the category, the low byte is the specific error.
*/
export enum RvfErrorCode {
// Category 0x00: Success
Ok = 0x0000,
OkPartial = 0x0001,
// Category 0x01: Format Errors
InvalidMagic = 0x0100,
InvalidVersion = 0x0101,
InvalidChecksum = 0x0102,
InvalidSignature = 0x0103,
TruncatedSegment = 0x0104,
InvalidManifest = 0x0105,
ManifestNotFound = 0x0106,
UnknownSegmentType = 0x0107,
AlignmentError = 0x0108,
// Category 0x02: Query Errors
DimensionMismatch = 0x0200,
EmptyIndex = 0x0201,
MetricUnsupported = 0x0202,
FilterParseError = 0x0203,
KTooLarge = 0x0204,
Timeout = 0x0205,
// Category 0x03: Write Errors
LockHeld = 0x0300,
LockStale = 0x0301,
DiskFull = 0x0302,
FsyncFailed = 0x0303,
SegmentTooLarge = 0x0304,
ReadOnly = 0x0305,
// Category 0x04: Tile Errors (WASM Microkernel)
TileTrap = 0x0400,
TileOom = 0x0401,
TileTimeout = 0x0402,
TileInvalidMsg = 0x0403,
TileUnsupportedOp = 0x0404,
// Category 0x05: Crypto Errors
KeyNotFound = 0x0500,
KeyExpired = 0x0501,
DecryptFailed = 0x0502,
AlgoUnsupported = 0x0503,
// SDK-level errors (0xFF__)
BackendNotFound = 0xff00,
BackendInitFailed = 0xff01,
StoreClosed = 0xff02,
}
/** Human-readable labels for each error code. */
const ERROR_MESSAGES: Record<number, string> = {
[RvfErrorCode.Ok]: 'Operation succeeded',
[RvfErrorCode.OkPartial]: 'Partial success (some items failed)',
[RvfErrorCode.InvalidMagic]: 'Segment magic mismatch',
[RvfErrorCode.InvalidVersion]: 'Unsupported segment version',
[RvfErrorCode.InvalidChecksum]: 'Segment hash verification failed',
[RvfErrorCode.InvalidSignature]: 'Cryptographic signature invalid',
[RvfErrorCode.TruncatedSegment]: 'Segment payload shorter than declared',
[RvfErrorCode.InvalidManifest]: 'Root manifest validation failed',
[RvfErrorCode.ManifestNotFound]: 'No valid manifest in file',
[RvfErrorCode.UnknownSegmentType]: 'Unrecognized segment type',
[RvfErrorCode.AlignmentError]: 'Data not at expected 64-byte boundary',
[RvfErrorCode.DimensionMismatch]: 'Query vector dimension != index dimension',
[RvfErrorCode.EmptyIndex]: 'No index segments available',
[RvfErrorCode.MetricUnsupported]: 'Requested distance metric not available',
[RvfErrorCode.FilterParseError]: 'Invalid filter expression',
[RvfErrorCode.KTooLarge]: 'Requested K exceeds available vectors',
[RvfErrorCode.Timeout]: 'Query exceeded time budget',
[RvfErrorCode.LockHeld]: 'Another writer holds the lock',
[RvfErrorCode.LockStale]: 'Lock file exists but owner is dead',
[RvfErrorCode.DiskFull]: 'Insufficient space for write',
[RvfErrorCode.FsyncFailed]: 'Durable write (fsync) failed',
[RvfErrorCode.SegmentTooLarge]: 'Segment exceeds 4 GB limit',
[RvfErrorCode.ReadOnly]: 'Store opened in read-only mode',
[RvfErrorCode.TileTrap]: 'WASM trap (OOB, unreachable, stack overflow)',
[RvfErrorCode.TileOom]: 'Tile exceeded scratch memory',
[RvfErrorCode.TileTimeout]: 'Tile computation exceeded time budget',
[RvfErrorCode.TileInvalidMsg]: 'Malformed hub-tile message',
[RvfErrorCode.TileUnsupportedOp]: 'Operation not available on this profile',
[RvfErrorCode.KeyNotFound]: 'Referenced key_id not found',
[RvfErrorCode.KeyExpired]: 'Key past valid_until timestamp',
[RvfErrorCode.DecryptFailed]: 'Decryption or auth tag verification failed',
[RvfErrorCode.AlgoUnsupported]: 'Cryptographic algorithm not implemented',
[RvfErrorCode.BackendNotFound]: 'No suitable backend found (install @ruvector/rvf-node or @ruvector/rvf-wasm)',
[RvfErrorCode.BackendInitFailed]: 'Backend initialization failed',
[RvfErrorCode.StoreClosed]: 'Store has been closed',
};
/**
* Custom error class for all RVF operations.
*
* Carries a typed `code` field for programmatic matching and a
* human-readable `message`.
*/
export class RvfError extends Error {
/** The RVF error code. */
readonly code: RvfErrorCode;
/** Error category (high byte of the code). */
get category(): number {
return (this.code >> 8) & 0xff;
}
/** True when the category indicates a format-level (fatal) error. */
get isFormatError(): boolean {
return this.category === 0x01;
}
constructor(code: RvfErrorCode, detail?: string) {
const base = ERROR_MESSAGES[code] ?? `RVF error 0x${code.toString(16).padStart(4, '0')}`;
const message = detail ? `${base}: ${detail}` : base;
super(message);
this.name = 'RvfError';
this.code = code;
}
/**
* Create an RvfError from a native binding error.
* Attempts to extract an error code from the message or object.
*/
static fromNative(err: unknown): RvfError {
if (err instanceof RvfError) return err;
if (err instanceof Error) {
const codeMatch = err.message.match(/0x([0-9a-fA-F]{4})/);
if (codeMatch) {
const code = parseInt(codeMatch[1], 16);
if (code in RvfErrorCode) {
return new RvfError(code as RvfErrorCode, err.message);
}
}
return new RvfError(RvfErrorCode.BackendInitFailed, err.message);
}
return new RvfError(RvfErrorCode.BackendInitFailed, String(err));
}
}

View File

@@ -0,0 +1,33 @@
/**
* Ambient module declarations for optional native/WASM backends.
*
* These let the SDK compile without the actual native packages installed.
* At runtime the dynamic `import()` calls in backend.ts will resolve to the
* real implementations (or throw, which is handled gracefully).
*/
declare module '@ruvector/rvf-node' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const RvfDatabase: any;
}
declare module '@ruvector/rvf-wasm' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const init: (...args: any[]) => Promise<any>;
export default init;
}
declare module '@ruvector/rvf-solver' {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const RvfSolver: any;
export type TrainOptions = any;
export type TrainResult = any;
export type AcceptanceOptions = any;
export type AcceptanceManifest = any;
export type AcceptanceModeResult = any;
export type CycleMetrics = any;
export type PolicyState = any;
export type SkipMode = any;
export type SkipModeStats = any;
export type CompiledConfig = any;
}

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,UAAU,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,EACf,eAAe,EACf,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,SAAS,EACT,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,EACd,WAAW,EACX,aAAa,EACb,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAGlD,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGrE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAC"}

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AA2BH,wBAAwB;AACxB,mCAAkD;AAAzC,kGAAA,QAAQ,OAAA;AAAE,sGAAA,YAAY,OAAA;AAI/B,qCAAqE;AAA5D,sGAAA,WAAW,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,yGAAA,cAAc,OAAA;AAEjD,oCAAoC;AACpC,uCAAyC;AAAhC,uGAAA,WAAW,OAAA;AAEpB,oCAAoC;AACpC,mDAAiD;AAAxC,uGAAA,SAAS,OAAA"}

View File

@@ -0,0 +1,68 @@
/**
* @ruvector/rvf — Unified TypeScript SDK for the RuVector Format.
*
* Works with both the native Node.js backend (`@ruvector/rvf-node`) and
* the browser WASM backend (`@ruvector/rvf-wasm`).
*
* @example
* ```ts
* import { RvfDatabase } from '@ruvector/rvf';
*
* const db = await RvfDatabase.create('./my.rvf', { dimensions: 128 });
* await db.ingestBatch([
* { id: '1', vector: new Float32Array(128) },
* ]);
* const results = await db.query(new Float32Array(128), 10);
* await db.close();
* ```
*/
// Re-export all public types
export type {
DistanceMetric,
CompressionProfile,
HardwareProfile,
RvfOptions,
RvfFilterValue,
RvfFilterExpr,
RvfQueryOptions,
RvfSearchResult,
RvfIngestResult,
RvfIngestEntry,
RvfDeleteResult,
RvfCompactionResult,
CompactionState,
RvfStatus,
DerivationType,
RvfKernelData,
RvfEbpfData,
RvfSegmentInfo,
BackendType,
RvfIndexStats,
RvfWitnessResult,
} from './types';
// Re-export error types
export { RvfError, RvfErrorCode } from './errors';
// Re-export backend interface and implementations
export type { RvfBackend } from './backend';
export { NodeBackend, WasmBackend, resolveBackend } from './backend';
// Re-export the main database class
export { RvfDatabase } from './database';
// Re-export solver (AGI components)
export { RvfSolver } from '@ruvector/rvf-solver';
export type {
TrainOptions,
TrainResult,
AcceptanceOptions,
AcceptanceManifest,
AcceptanceModeResult,
CycleMetrics,
PolicyState,
SkipMode,
SkipModeStats,
CompiledConfig,
} from '@ruvector/rvf-solver';

View File

@@ -0,0 +1,213 @@
/**
* Distance metric for vector similarity search.
*
* - `l2` Squared Euclidean distance.
* - `cosine` Cosine distance (1 - cosine_similarity).
* - `dotproduct` Negated inner (dot) product.
*/
export type DistanceMetric = 'l2' | 'cosine' | 'dotproduct';
/**
* Compression profile for stored vectors.
*
* - `none` Raw fp32 vectors.
* - `scalar` Scalar quantization (int8).
* - `product` Product quantization.
*/
export type CompressionProfile = 'none' | 'scalar' | 'product';
/**
* Hardware profile selector.
*
* 0 = Generic, 1 = Core, 2 = Hot, 3 = Full.
*/
export type HardwareProfile = 0 | 1 | 2 | 3;
/** Options for creating a new RVF store. */
export interface RvfOptions {
/** Vector dimensionality (required, must be > 0). */
dimensions: number;
/** Distance metric for similarity search. Default: `'l2'`. */
metric?: DistanceMetric;
/** Hardware profile identifier. Default: `0` (Generic). */
profile?: HardwareProfile;
/** Compression profile. Default: `'none'`. */
compression?: CompressionProfile;
/** Enable segment signing. Default: `false`. */
signing?: boolean;
/** HNSW M parameter: max edges per node per layer. Default: `16`. */
m?: number;
/** HNSW ef_construction: beam width during index build. Default: `200`. */
efConstruction?: number;
}
/** Primitive value types usable in filter expressions. */
export type RvfFilterValue = number | string | boolean;
/**
* A filter expression for metadata-based vector filtering.
*
* Leaf operators compare a `fieldId` against a literal `value`.
* Composite operators combine sub-expressions with boolean logic.
*/
export type RvfFilterExpr = {
op: 'eq';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'ne';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'lt';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'le';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'gt';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'ge';
fieldId: number;
value: RvfFilterValue;
} | {
op: 'in';
fieldId: number;
values: RvfFilterValue[];
} | {
op: 'range';
fieldId: number;
low: RvfFilterValue;
high: RvfFilterValue;
} | {
op: 'and';
exprs: RvfFilterExpr[];
} | {
op: 'or';
exprs: RvfFilterExpr[];
} | {
op: 'not';
expr: RvfFilterExpr;
};
/** Options controlling a query operation. */
export interface RvfQueryOptions {
/** HNSW ef_search parameter (beam width during search). Default: `100`. */
efSearch?: number;
/** Optional metadata filter expression. */
filter?: RvfFilterExpr;
/** Query timeout in milliseconds (0 = no timeout). Default: `0`. */
timeoutMs?: number;
}
/** A single search result: vector ID and distance. */
export interface RvfSearchResult {
/** The vector's unique identifier (string-encoded u64). */
id: string;
/** Distance from the query vector (lower = more similar). */
distance: number;
}
/** Result of a batch ingest operation. */
export interface RvfIngestResult {
/** Number of vectors successfully ingested. */
accepted: number;
/** Number of vectors rejected. */
rejected: number;
/** Manifest epoch after the ingest commit. */
epoch: number;
}
/** Result of a delete operation. */
export interface RvfDeleteResult {
/** Number of vectors soft-deleted. */
deleted: number;
/** Manifest epoch after the delete commit. */
epoch: number;
}
/** Result of a compaction operation. */
export interface RvfCompactionResult {
/** Number of segments compacted. */
segmentsCompacted: number;
/** Bytes of dead space reclaimed. */
bytesReclaimed: number;
/** Manifest epoch after compaction commit. */
epoch: number;
}
/** Compaction state as reported in store status. */
export type CompactionState = 'idle' | 'running' | 'emergency';
/** A snapshot of the store's current state. */
export interface RvfStatus {
/** Total number of live (non-deleted) vectors. */
totalVectors: number;
/** Total number of segments in the file. */
totalSegments: number;
/** Total file size in bytes. */
fileSizeBytes: number;
/** Current manifest epoch. */
epoch: number;
/** Hardware profile identifier. */
profileId: number;
/** Current compaction state. */
compactionState: CompactionState;
/** Ratio of dead space to total (0.0 - 1.0). */
deadSpaceRatio: number;
/** Whether the store is open in read-only mode. */
readOnly: boolean;
}
/** A single entry for batch ingestion. */
export interface RvfIngestEntry {
/** Unique vector identifier. */
id: string;
/** The embedding vector (must match store dimensions). */
vector: Float32Array | number[];
/** Optional per-vector metadata fields. */
metadata?: Record<string, RvfFilterValue>;
}
/** Derivation type for creating derived stores. */
export type DerivationType = 'filter' | 'merge' | 'snapshot' | 'transform';
/** Data returned from kernel extraction. */
export interface RvfKernelData {
/** Serialized KernelHeader bytes. */
header: Uint8Array;
/** Raw kernel image bytes. */
image: Uint8Array;
}
/** Data returned from eBPF extraction. */
export interface RvfEbpfData {
/** Serialized EbpfHeader bytes. */
header: Uint8Array;
/** Program bytecode + optional BTF. */
payload: Uint8Array;
}
/** Information about a segment in the store. */
export interface RvfSegmentInfo {
/** Segment ID. */
id: number;
/** File offset of the segment. */
offset: number;
/** Payload length in bytes. */
payloadLength: number;
/** Segment type name (e.g. "vec", "manifest", "kernel"). */
segType: string;
}
/** Identifies which backend implementation to use. */
export type BackendType = 'node' | 'wasm' | 'auto';
/** HNSW index statistics. */
export interface RvfIndexStats {
/** Number of indexed vectors. */
indexedVectors: number;
/** Number of HNSW layers. */
layers: number;
/** M parameter (max edges per node per layer). */
m: number;
/** ef_construction parameter. */
efConstruction: number;
/** Whether the index needs rebuilding. */
needsRebuild: boolean;
}
/** Result of witness chain verification. */
export interface RvfWitnessResult {
/** Whether the chain is valid. */
valid: boolean;
/** Number of entries in the chain. */
entries: number;
/** Error message if invalid. */
error?: string;
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,237 @@
/**
* Distance metric for vector similarity search.
*
* - `l2` Squared Euclidean distance.
* - `cosine` Cosine distance (1 - cosine_similarity).
* - `dotproduct` Negated inner (dot) product.
*/
export type DistanceMetric = 'l2' | 'cosine' | 'dotproduct';
/**
* Compression profile for stored vectors.
*
* - `none` Raw fp32 vectors.
* - `scalar` Scalar quantization (int8).
* - `product` Product quantization.
*/
export type CompressionProfile = 'none' | 'scalar' | 'product';
/**
* Hardware profile selector.
*
* 0 = Generic, 1 = Core, 2 = Hot, 3 = Full.
*/
export type HardwareProfile = 0 | 1 | 2 | 3;
/** Options for creating a new RVF store. */
export interface RvfOptions {
/** Vector dimensionality (required, must be > 0). */
dimensions: number;
/** Distance metric for similarity search. Default: `'l2'`. */
metric?: DistanceMetric;
/** Hardware profile identifier. Default: `0` (Generic). */
profile?: HardwareProfile;
/** Compression profile. Default: `'none'`. */
compression?: CompressionProfile;
/** Enable segment signing. Default: `false`. */
signing?: boolean;
/** HNSW M parameter: max edges per node per layer. Default: `16`. */
m?: number;
/** HNSW ef_construction: beam width during index build. Default: `200`. */
efConstruction?: number;
}
// ---------------------------------------------------------------------------
// Filter expressions
// ---------------------------------------------------------------------------
/** Primitive value types usable in filter expressions. */
export type RvfFilterValue = number | string | boolean;
/**
* A filter expression for metadata-based vector filtering.
*
* Leaf operators compare a `fieldId` against a literal `value`.
* Composite operators combine sub-expressions with boolean logic.
*/
export type RvfFilterExpr =
| { op: 'eq'; fieldId: number; value: RvfFilterValue }
| { op: 'ne'; fieldId: number; value: RvfFilterValue }
| { op: 'lt'; fieldId: number; value: RvfFilterValue }
| { op: 'le'; fieldId: number; value: RvfFilterValue }
| { op: 'gt'; fieldId: number; value: RvfFilterValue }
| { op: 'ge'; fieldId: number; value: RvfFilterValue }
| { op: 'in'; fieldId: number; values: RvfFilterValue[] }
| { op: 'range'; fieldId: number; low: RvfFilterValue; high: RvfFilterValue }
| { op: 'and'; exprs: RvfFilterExpr[] }
| { op: 'or'; exprs: RvfFilterExpr[] }
| { op: 'not'; expr: RvfFilterExpr };
// ---------------------------------------------------------------------------
// Query options
// ---------------------------------------------------------------------------
/** Options controlling a query operation. */
export interface RvfQueryOptions {
/** HNSW ef_search parameter (beam width during search). Default: `100`. */
efSearch?: number;
/** Optional metadata filter expression. */
filter?: RvfFilterExpr;
/** Query timeout in milliseconds (0 = no timeout). Default: `0`. */
timeoutMs?: number;
}
// ---------------------------------------------------------------------------
// Result types
// ---------------------------------------------------------------------------
/** A single search result: vector ID and distance. */
export interface RvfSearchResult {
/** The vector's unique identifier (string-encoded u64). */
id: string;
/** Distance from the query vector (lower = more similar). */
distance: number;
}
/** Result of a batch ingest operation. */
export interface RvfIngestResult {
/** Number of vectors successfully ingested. */
accepted: number;
/** Number of vectors rejected. */
rejected: number;
/** Manifest epoch after the ingest commit. */
epoch: number;
}
/** Result of a delete operation. */
export interface RvfDeleteResult {
/** Number of vectors soft-deleted. */
deleted: number;
/** Manifest epoch after the delete commit. */
epoch: number;
}
/** Result of a compaction operation. */
export interface RvfCompactionResult {
/** Number of segments compacted. */
segmentsCompacted: number;
/** Bytes of dead space reclaimed. */
bytesReclaimed: number;
/** Manifest epoch after compaction commit. */
epoch: number;
}
/** Compaction state as reported in store status. */
export type CompactionState = 'idle' | 'running' | 'emergency';
/** A snapshot of the store's current state. */
export interface RvfStatus {
/** Total number of live (non-deleted) vectors. */
totalVectors: number;
/** Total number of segments in the file. */
totalSegments: number;
/** Total file size in bytes. */
fileSizeBytes: number;
/** Current manifest epoch. */
epoch: number;
/** Hardware profile identifier. */
profileId: number;
/** Current compaction state. */
compactionState: CompactionState;
/** Ratio of dead space to total (0.0 - 1.0). */
deadSpaceRatio: number;
/** Whether the store is open in read-only mode. */
readOnly: boolean;
}
// ---------------------------------------------------------------------------
// Ingest entry
// ---------------------------------------------------------------------------
/** A single entry for batch ingestion. */
export interface RvfIngestEntry {
/** Unique vector identifier. */
id: string;
/** The embedding vector (must match store dimensions). */
vector: Float32Array | number[];
/** Optional per-vector metadata fields. */
metadata?: Record<string, RvfFilterValue>;
}
// ---------------------------------------------------------------------------
// Lineage types
// ---------------------------------------------------------------------------
/** Derivation type for creating derived stores. */
export type DerivationType = 'filter' | 'merge' | 'snapshot' | 'transform';
// ---------------------------------------------------------------------------
// Kernel / eBPF types
// ---------------------------------------------------------------------------
/** Data returned from kernel extraction. */
export interface RvfKernelData {
/** Serialized KernelHeader bytes. */
header: Uint8Array;
/** Raw kernel image bytes. */
image: Uint8Array;
}
/** Data returned from eBPF extraction. */
export interface RvfEbpfData {
/** Serialized EbpfHeader bytes. */
header: Uint8Array;
/** Program bytecode + optional BTF. */
payload: Uint8Array;
}
// ---------------------------------------------------------------------------
// Segment inspection
// ---------------------------------------------------------------------------
/** Information about a segment in the store. */
export interface RvfSegmentInfo {
/** Segment ID. */
id: number;
/** File offset of the segment. */
offset: number;
/** Payload length in bytes. */
payloadLength: number;
/** Segment type name (e.g. "vec", "manifest", "kernel"). */
segType: string;
}
// ---------------------------------------------------------------------------
// Backend identifier
// ---------------------------------------------------------------------------
/** Identifies which backend implementation to use. */
export type BackendType = 'node' | 'wasm' | 'auto';
// ---------------------------------------------------------------------------
// Solver / AGI types (re-exported from @ruvector/rvf-solver)
// ---------------------------------------------------------------------------
/** HNSW index statistics. */
export interface RvfIndexStats {
/** Number of indexed vectors. */
indexedVectors: number;
/** Number of HNSW layers. */
layers: number;
/** M parameter (max edges per node per layer). */
m: number;
/** ef_construction parameter. */
efConstruction: number;
/** Whether the index needs rebuilding. */
needsRebuild: boolean;
}
/** Result of witness chain verification. */
export interface RvfWitnessResult {
/** Whether the chain is valid. */
valid: boolean;
/** Number of entries in the chain. */
entries: number;
/** Error message if invalid. */
error?: string;
}