API Reference
Vecal 1.0 methods, types, states, and stable errors.
API reference
All storage and search methods are asynchronous and execute through a Dedicated Worker. Vectors are Float32Array values with exactly the configured dimension.
VectorDB.open(options)
const db = await VectorDB.open<MyMetadata>({
name: 'documents',
dimension: 384,
metric: 'cosine',
// Optional; use one override, not both:
workerUrl: customWorkerUrl,
// workerFactory: () => new Worker(appWorkerUrl, { type: 'module' }),
});name, dimension, and metric are required. The metric is fixed for the database and all Exact/HNSW searches. A conflicting reopen throws SchemaMismatchError.
Records
add(input) => Promise<string>
Inserts a record. Omit id to generate one with crypto.randomUUID(). An existing ID throws RecordConflictError.
addMany(inputs) => Promise<string[]>
Inserts all inputs in one transaction. The operation is all-or-nothing, including conflicts within the input batch.
upsert(inputWithId) => Promise<string>
Inserts or completely overwrites the record at an explicit ID.
get(id) => Promise<VectorRecord | undefined>
Returns one record. With cosine metric, the returned stored vector is normalized.
getMany(ids) => Promise<Array<VectorRecord | undefined>>
Returns records in the same order as the requested IDs.
update(id, patch) => Promise<void>
Updates vector, metadata, or both. If metadata is present, it replaces the entire metadata object. A missing ID throws RecordNotFoundError.
delete(id) => Promise<boolean>
Returns whether an existing record was deleted.
deleteMany(ids) => Promise<number>
Deletes unique IDs in one transaction and returns the number found.
count() => Promise<number>
Returns the current entry count.
clear() => Promise<void>
Atomically removes entries and snapshots, increments the revision, and returns the index state to absent.
Search
search(query, options?) => Promise<SearchResult[]>
const results = await db.search(query, {
k: 10,
strategy: 'auto', // auto | exact | hnsw
efSearch: 64,
where: {
tenant: { $eq: 'acme' },
rating: { $gte: 4 },
},
minScore: 0.3,
signal: controller.signal,
});kdefaults to10and must be a positive integer.strategydefaults toauto.efSearchdefaults to64and applies to HNSW.wheresupports top-level AND conditions with$eq,$in,$gt,$gte,$lt, and$lte.minScoreis inclusive.signalcancels Worker computation withOperationCancelledError.
Scores are higher-is-better: cosine similarity, dot product, or negative Euclidean distance. Equal scores sort by ID.
auto uses HNSW only in ready state. Explicit hnsw throws IndexNotReadyError if the index is not ready.
Index lifecycle
ensureIndex(options) => Promise<void>
await db.ensureIndex({
type: 'hnsw',
m: 16,
efConstruction: 200,
seed: 0x5eed1234,
onProgress: ({ completed, total, ratio }) => {},
signal: controller.signal,
});Defaults are m: 16, efConstruction: 200, and a stable built-in seed. A revision collision retries once; a second collision throws IndexBuildConflictError.
indexStatus() => IndexStatus
Returns the most recently reported Worker status synchronously:
interface IndexStatus {
state: 'absent' | 'building' | 'ready' | 'stale' | 'closed';
revision: number;
nodeCount: number;
tombstoneRatio: number;
config?: { m: number; efConstruction: number; seed: number };
}Each subsequent public operation reads the authoritative IndexedDB revision and refreshes this status if another instance committed a change.
close() => Promise<void>
Waits for a ready index checkpoint, closes IndexedDB and BroadcastChannel, terminates the Worker, and moves the instance to closed. It is idempotent. Later operations throw DatabaseClosedError.
Core types
type Metric = 'cosine' | 'l2' | 'dot';
type SearchStrategy = 'auto' | 'exact' | 'hnsw';
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
type Metadata = Record<string, JSONValue>;
interface VectorInput<M extends Metadata> {
id?: string;
vector: Float32Array;
metadata?: M;
}
interface VectorRecord<M extends Metadata> {
id: string;
vector: Float32Array;
metadata?: M;
}
interface SearchResult<M extends Metadata> {
id: string;
score: number;
metadata?: M;
}Metadata must be a finite, acyclic JSON object that structured clone can transfer. undefined, Date, class instances, functions, symbols, bigint, and non-finite numbers are rejected.
Stable errors
All public errors extend VecalError and carry a stable code.
| Class | Code | Meaning |
|---|---|---|
UnsupportedEnvironmentError | UNSUPPORTED_ENVIRONMENT | Worker or IndexedDB unavailable/blocked |
SchemaMismatchError | SCHEMA_MISMATCH | Durable dimension, metric, or schema differs |
RecordConflictError | RECORD_CONFLICT | add/addMany ID already exists |
RecordNotFoundError | RECORD_NOT_FOUND | update target does not exist |
IndexNotReadyError | INDEX_NOT_READY | Explicit HNSW requested without a ready graph |
IndexBuildConflictError | INDEX_BUILD_CONFLICT | Data changed during both build attempts |
OperationCancelledError | OPERATION_CANCELLED | AbortSignal cancelled the request |
DatabaseClosedError | DATABASE_CLOSED | Operation used after close |
ValidationError | VALIDATION_ERROR | Invalid option, vector, metadata, or filter |