# @sqlite-actor/sqlite-vec 🎭

A compiled WASM module + API combining **SQLite** and **sqlite-vec**, designed specifically to run in actors (such as Cloudflare Durable Objects and Rivet Actors). This allows for more advanced RAG applications to be built on top of the actor model.

## Installation

```bash
npm install @sqlite-actor/sqlite-vec
```

---

## Usage (Cloudflare Durable Objects Example)

```typescript
import { DurableObject } from "cloudflare:workers";
import { KVStorage, SqliteVec } from "@sqlite-actor/sqlite-vec";
import sqliteVecWasm from "@sqlite-actor/sqlite-vec/sqlite-vec.wasm";

export class VectorActor extends DurableObject {
  private db: SqliteVec | null = null;

  constructor(ctx: DurableObjectState, env: any) {
    super(ctx, env);
    const kvStorage = ctx.storage.kv as KVStorage;

    ctx.blockConcurrencyWhile(async () => {
      this.db = await SqliteVec.create(kvStorage, { wasmModule: sqliteVecWasm });
      this.db.exec("CREATE VIRTUAL TABLE vec_items USING vec0(embedding float[3]);").rowsWritten;
    });
  }

  // example RPC method that inserts and searches
  hello() {
    if (!this.db) throw new Error("Database not initialized");

    const writeCursor = this.db.exec(
      "INSERT INTO vec_items(rowid, embedding) VALUES (?, ?)", 
      [1, '[1.1, 2.2, 3.3]']
    );
    console.log(`rowsWritten=${writeCursor.rowsWritten}`);

    const cursor = this.db.exec(`
      SELECT rowid, distance 
      FROM vec_items 
      WHERE embedding MATCH ? 
      ORDER BY distance 
      LIMIT 5
    `, ['[1.0, 2.0, 3.0]']);

    for (const row of cursor) {
      console.log(row);
    }
  }
}
```

---
## Performance Benchmarks
See [sqlite-vec-actor-benchmark](https://github.com/sqlite-actor/sqlite-vec-actor-benchmark) to compare performance of sqlite-actor with sqlite-vec against a native js heap and actor sqlite implementation.

## Notes
- **Synchronous KV**: you must provide a synchronous key value api for the wasm module to use.
- **Large result sets**: `toArray()` and `one()` are convenience helpers that materialize rows in JavaScript. For large queries, iterate the cursor directly so rows are processed incrementally.
- **Cursor properties**: every `exec()` call returns a cursor exposing `columnNames`, `rowsRead`, and `rowsWritten`.
- **Error handling**: SQLite execution failures are surfaced as JavaScript `Error` values.

### Cursor API

`db.exec(sql, params)` is the only public SQL API. Every call returns a cursor object for both reads and writes.

- `columnNames: string[]`
  - Column names for row-producing queries.
- `rowsRead: number`
  - Number of rows consumed so far from the cursor.
- `rowsWritten: number`
  - Number of rows written by non-row statements.
  - Accessing `rowsWritten` on a non-row statement forces execution if needed.

For row-producing queries, `rowsWritten` is `0`.

### Error Handling

SQLite failures are surfaced as standard JavaScript `Error` values:

- prepare failures throw from `exec()`
- step-time failures throw while consuming the cursor (`toArray()`, `one()`, iteration)

Any numeric value returned by SQLite is represented as JavaScript `number`, so very large `int64` values may lose precision.

### Supported `db.exec(sql, params)` parameter inputs

Current binding behavior:
- `null` / `undefined` -> SQL `NULL`
- `number` -> SQLite `REAL`
- `string` -> SQLite `TEXT`
- `Uint8Array` or any TypedArray/DataView -> SQLite `BLOB`

For sqlite-vec this supports both common vector forms:
- JSON-style vector strings (TEXT), like `'[1.1, 2.2, 3.3]'`
- Compact binary vectors (BLOB), like `Float32Array`

Other JS value types (for example plain objects, arrays, booleans, bigint) are not currently first-class binder types.

## Upstream Credits
- **SQLite**: https://sqlite.org/
- **sqlite-vec** by Alex Garcia: https://github.com/asg017/sqlite-vec

Licensing and attribution details are documented in [../../THIRD_PARTY_NOTICES.md](../../THIRD_PARTY_NOTICES.md).

## License
MIT
