sekejap 0.16.5 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,37 @@
1
- # sekejap
2
-
3
- Embedded **graph-first, multi-model database** for Node.js — SQL + graph + vector +
4
- spatial in one native library. No server, no external process; the whole engine runs
5
- in-process on a Rust core.
6
-
7
- - **PostgreSQL-like SQL** — `CREATE TABLE`, `INSERT`, `SELECT … WHERE`, parameters (`$1`).
8
- - **Graph** — link records and traverse relationships.
9
- - **Prepared statements** — compile once, run many with varying parameters.
10
- - **No native build** — the prebuilt binaries for every platform ship inside the
11
- package. No Rust toolchain, no `node-gyp`, no compile step.
1
+ # sekejap (Node.js)
2
+
3
+ Embedded **graph-first, multi-model database** for Node.js: SQL + graph + vector
4
+ + spatial in one native library. No server, no external process — the whole
5
+ engine runs in-process, in Rust, and this package talks to it over the C ABI
6
+ (`docs/dist/C_ABI.md`), not napi-rs.
7
+
8
+ - **PostgreSQL-like SQL** — `CREATE TABLE`, `INSERT`, `SELECT … WHERE`, `$n` parameters.
9
+ - **Graph** — `link`/`unlink`/`neighbours`, and `GRAPH_TABLE` in SQL for deeper walks.
10
+ - **Prepared statements and paged scans** — compile once, run many; walk a
11
+ collection or a SELECT's answer a page at a time.
12
+ - **Transactions** — many writes under one commit barrier.
13
+ - **Documents already parsed** — every call returns a JS value, not a JSON
14
+ string to `JSON.parse` yourself.
15
+
16
+ ## Choosing the FFI route
17
+
18
+ This wrapper binds `libsekejap` through [koffi](https://koffi.dev), not
19
+ through a compiled napi-rs addon (e1's approach, and what this package used
20
+ before). Two reasons:
21
+
22
+ 1. **No native build for this package.** koffi ships its own prebuilt N-API
23
+ glue for the platforms it supports; this file does one `koffi.load()` of
24
+ the sekejap shared library at `require()` time. There is no `cargo build`,
25
+ no `node-gyp`, and no per-Node-ABI addon to rebuild — the wrapper itself
26
+ is pure JS, and only `libsekejap.{dylib,so,dll}` is platform-specific.
27
+ 2. **`node-ffi-napi`/`ref-napi` are gone.** They were the other candidate
28
+ considered, but `node-ffi-napi` is unpublished from the npm registry
29
+ (`npm view node-ffi-napi` → 404) and its `ref-napi` dependency compiles a
30
+ native addon of its own — the opposite of what "no native build" needs.
31
+ koffi is the smaller, actively maintained port: one dependency, prebuilt,
32
+ with disposable types (`koffi.disposable`) that map directly onto this
33
+ ABI's "every returned `char*` is freed once with `sekejap_string_free`"
34
+ rule (see `index.js` §2-3).
12
35
 
13
36
  ## Install
14
37
 
@@ -16,71 +39,159 @@ in-process on a Rust core.
16
39
  npm install sekejap
17
40
  ```
18
41
 
19
- Works on macOS (arm64/x64), Linux (x64/arm64), and Windows (x64), Node ≥ 16.
42
+ The published package bundles a `native/<platform>-<arch>/lib/libsekejap.*`
43
+ per supported platform (assembled by the `publish-node` CI job from the same
44
+ `build-native-libs` artifacts every other wrapper links). Locally, or against
45
+ a library you built yourself, point at it explicitly:
46
+
47
+ ```sh
48
+ export SEKEJAP_LIB_PATH=/path/to/libsekejap.dylib # exact file, or:
49
+ export SEKEJAP_LIB_DIR=/path/to/dir/holding/it # a directory, or:
50
+ export DYLD_LIBRARY_PATH=/path/to/dir # (Linux: LD_LIBRARY_PATH)
51
+ ```
52
+
53
+ Resolution order: `SEKEJAP_LIB_PATH` → `SEKEJAP_LIB_DIR` →
54
+ `native/<platform>-<arch>/` inside the package → the bare library name,
55
+ which the OS loader then searches its own path for.
20
56
 
21
57
  ## Quick start
22
58
 
23
59
  ```js
24
- const { Db, version } = require('sekejap');
60
+ const { Db } = require('sekejap');
25
61
 
26
- const db = Db.open('/tmp/notes'); // open (or create) a database directory
62
+ const db = Db.open('/tmp/notes'); // open (or create) a database directory
27
63
 
28
- // Define a table and insert rows with SQL.
29
- db.execute('CREATE TABLE note (_key TEXT PRIMARY KEY, title TEXT, pinned INTEGER)');
30
- db.execute("INSERT INTO note (_key, title, pinned) VALUES ('n1', 'Buy milk', 1)");
64
+ db.execute('CREATE TABLE note (title TEXT, pinned BOOL)');
65
+ db.execute("INSERT INTO note (_key, title, pinned) VALUES ('n1', 'Buy milk', true)");
31
66
 
32
- // Insert a record directly (no SQL) as a JSON payload.
33
- db.put('note/n2', JSON.stringify({ _collection: 'note', _key: 'n2', title: 'Call Sam', pinned: 0 }));
67
+ // The document API — no SQL. `_key` in the value is optional; it is set
68
+ // from the `key` argument when absent.
69
+ db.put('note', 'n2', { title: 'Call Sam', pinned: false });
34
70
 
35
- // Query. Results come back as a JSON string — JSON.parse it.
36
- const notes = JSON.parse(db.query('SELECT * FROM note'));
37
- console.log(notes);
38
- // [ { _key: 'n1', title: 'Buy milk', pinned: 1, ... }, { _key: 'n2', ... } ]
71
+ console.log(db.get('note', 'n1')); // { _key: 'n1', title: 'Buy milk', pinned: true }
39
72
 
40
- // Parameterized query ($1, $2, …) — injection-safe. Params are a JSON array string.
41
- const pinned = JSON.parse(db.queryParams('SELECT title FROM note WHERE pinned = $1', JSON.stringify([1])));
73
+ db.execute('CREATE INDEX note_pinned ON note USING btree (pinned)');
74
+ const pinned = db.query('SELECT title FROM note WHERE pinned = $1', [true]);
42
75
 
43
- // Prepared statement — compile once, run many.
44
- const byPin = db.prepare('SELECT _key FROM note WHERE pinned = $1');
45
- for (const p of [0, 1]) {
46
- console.log(JSON.parse(db.queryPrepared(byPin, JSON.stringify([p]))));
47
- }
76
+ const stmt = db.prepare('SELECT title FROM note WHERE pinned = $1');
77
+ for (const want of [true, false]) console.log(stmt.query([want]));
78
+ stmt.close();
48
79
 
49
- // Graph: link two records.
50
- db.link('note/n1', 'note/n2', 'related');
51
- console.log('nodes:', db.nodeCount(), 'edges:', db.edgeCount());
80
+ db.link('note', 'n1', 'related', 'note', 'n2');
81
+ console.log(db.neighbours('note', 'n1', 'related', 'outgoing', 10));
52
82
 
53
- // Flush + compact to disk after heavy writes.
54
- db.compact();
83
+ const tx = db.transaction();
84
+ tx.put('note', 'n3', { title: 'Committed together', pinned: false });
85
+ tx.commit(); // or tx.rollback()
86
+
87
+ console.log('rows:', db.countRows('note'));
88
+ db.close();
55
89
  ```
56
90
 
57
- ## API
91
+ More: `examples/tour.js`.
92
+
93
+ ## API mapping (C function → wrapper call)
58
94
 
59
- | Method | Purpose |
95
+ | C function | wrapper call |
60
96
  |---|---|
61
- | `Db.open(path)` | Open (or create) a database at a directory path |
62
- | `db.execute(sql)` | Run DDL/DML (`CREATE`/`INSERT`/`UPDATE`/`DELETE`) → rows affected |
63
- | `db.query(sql)` | Run a `SELECT`/graph query → **JSON string** |
64
- | `db.queryParams(sql, paramsJson)` | Parameterized query; `paramsJson` is a JSON array string |
65
- | `db.prepare(sql)` / `db.queryPrepared(stmt, paramsJson)` | Prepared statements |
66
- | `db.put(slug, payloadJson)` | Insert/replace one record by slug with a JSON payload |
67
- | `db.link(from, to, edgeType)` | Create a graph edge |
68
- | `db.nodeCount()` / `db.edgeCount()` | Counts |
69
- | `db.compact()` | Flush WAL, rewrite payloads, reclaim RAM |
70
- | `version()` | Library version |
71
-
72
- `query` / `queryParams` / `queryPrepared` return a **JSON string** — decode with
73
- `JSON.parse(...)`. It's an array of row objects.
74
-
75
- ### TypeScript
76
-
77
- Types ship with the package (`index.d.ts`) — no `@types` needed.
78
-
79
- ```ts
80
- import { Db } from 'sekejap';
81
- const db = Db.open('/tmp/notes');
82
- ```
97
+ | `sekejap_open` | `Db.open(path)` |
98
+ | `sekejap_open_with_config` | `Db.openWithConfig(path, config)` |
99
+ | `sekejap_open_service` | `Db.openService(path)` |
100
+ | `sekejap_close` | `db.close()` |
101
+ | `sekejap_version` | `version()` (module function) |
102
+ | `sekejap_format_version` | `formatVersion()` (module function) |
103
+ | `sekejap_last_error` / `sekejap_last_error_code` | folded into every thrown `SekejapError` (`.message`, `.code`, `.status`) |
104
+ | `sekejap_string_free` | never called by user code — wired as a `koffi.disposable` type, freed automatically after every C→JS string conversion |
105
+ | `sekejap_put` | `db.put(collection, key, doc)` |
106
+ | `sekejap_put_many` | `db.putMany(collection, rows)` |
107
+ | `sekejap_get` | `db.get(collection, key)` → object or `null` |
108
+ | `sekejap_exists` | `db.exists(collection, key)` → boolean |
109
+ | `sekejap_delete` | `db.delete(collection, key)` → boolean |
110
+ | `sekejap_scan_open` / `_next` / `_close` | `db.scan(collection, pageRows)` → `Scan` (`for...of`, `.rows()`, `.next()`, `.close()`) |
111
+ | `sekejap_execute` | `db.execute(sql, params)` |
112
+ | `sekejap_query` | `db.query(sql, params)` |
113
+ | `sekejap_explain` | `db.explain(sql, params)` |
114
+ | `sekejap_prepare` | `db.prepare(sql)` → `Statement` |
115
+ | `sekejap_stmt_query` | `stmt.query(params)` |
116
+ | `sekejap_stmt_execute` | `stmt.execute(params)` |
117
+ | `sekejap_stmt_rebindable` | `stmt.rebindable()` → boolean or `null` (unbound) |
118
+ | `sekejap_stmt_free` | `stmt.close()` |
119
+ | `sekejap_query_open` / `_next` / `_close` | `db.stream(sql, params, pageRows)` → `Scan` |
120
+ | `sekejap_link` | `db.link(fromCollection, fromKey, edgeType, toCollection, toKey)` |
121
+ | `sekejap_link_with` | `db.linkWith(..., properties)` |
122
+ | `sekejap_unlink` | `db.unlink(...)` → boolean |
123
+ | `sekejap_neighbours` | `db.neighbours(collection, key, edgeType, direction, limit)` |
124
+ | `sekejap_create_collection` | `db.createCollection(name, fields)` → boolean |
125
+ | `sekejap_drop_collection` | `db.dropCollection(name)` → boolean |
126
+ | `sekejap_collections` | `db.collections()` |
127
+ | `sekejap_describe` | `db.describe(collection)` → object or `null` |
128
+ | `sekejap_count_rows` | `db.countRows(collection)` |
129
+ | `sekejap_scan_count_rows` | `db.scanCountRows(collection)` |
130
+ | `sekejap_scan_count_edges` | `db.scanCountEdges()` |
131
+ | `sekejap_tx_begin` | `db.transaction()` → `Tx` |
132
+ | `sekejap_tx_put` / `_delete` / `_link` / `_execute` | `tx.put` / `tx.delete` / `tx.link` / `tx.execute` |
133
+ | `sekejap_tx_commit` / `_rollback` | `tx.commit()` / `tx.rollback()` |
134
+ | `sekejap_checkpoint` | `db.checkpoint()` → boolean (folded/deferred) |
135
+ | `sekejap_publish` | `db.publish()` |
136
+ | `sekejap_storage` | `db.storage()` |
137
+ | `sekejap_statement_timeout_ms` | `db.statementTimeoutMs(ms)` |
138
+ | `sekejap_cancel` / `_clear_interrupt` | `db.cancel()` / `db.clearInterrupt()` |
139
+ | `sekejap_subscribe` / `_next_change` / `_unsubscribe` | `db.subscribe()` / `db.nextChange(id, timeoutMs)` / `db.unsubscribe(id)` |
140
+ | `sekejap_open_memory`, `_trim_memory`, `_compact`, `_show` | `Db.openMemory()`, `db.trimMemory()`, `db.compact()`, `db.show()` — all always throw `SekejapError` with `.code === 'Refused'`, by name |
141
+
142
+ ## What was removed from e1's wrapper
143
+
144
+ e1's `dist/bindings/wrappers/node/` was a napi-rs addon (`src/lib.rs`,
145
+ `Cargo.toml`/`Cargo.lock`, `build.rs`) compiled against the OLD `CoreDB`
146
+ (slug-addressed rows, `nodeCount`/`edgeCount`, a single-string `Db.open`) and
147
+ a prebuilt `sekejap.darwin-arm64.node` binary, plus a TypeScript ORM layer
148
+ (`orm/`, and its build output `dist/`) and Next.js/Express example apps built
149
+ on that ORM. All of it is gone:
150
+
151
+ - **The napi-rs Rust glue crate** (`src/`, `Cargo.toml`, `Cargo.lock`,
152
+ `build.rs`) — this wrapper is pure JS over koffi now; there is nothing to
153
+ compile, so no Rust crate belongs here at all. This is also why `build-node`
154
+ no longer runs `cargo`/`napi build`.
155
+ - **The committed `.node` binary and `node_modules/`** — a native binary and
156
+ ~2,000 files of transitively-vendored TypeScript/React tooling had no
157
+ business in version control; `npm install` fetches the one runtime
158
+ dependency (`koffi`) and `.gitignore` keeps `node_modules/` out from here on.
159
+ - **`orm/` and its `dist/` build output** — a hand-rolled query builder /
160
+ React-hooks layer over the old slug API. It does not carry over: this
161
+ wrapper's job is the one-to-one C ABI mirror above, and a higher-level ORM
162
+ over the 0.17 shape is a separate, later decision, not part of re-opening
163
+ the publish channel.
164
+ - **`examples/nextjs/`, `examples/express-server.js`** — both were demos of
165
+ the ORM layer, not of the C ABI surface. `examples/tour.js` stays,
166
+ rewritten against the 0.17 API (`db.query()`/`db.get()` already return
167
+ parsed values, not JSON strings; `_key` is implicit rather than a declared
168
+ `TEXT PRIMARY KEY` column — see `lang/src/compile/ddl.rs`: names starting
169
+ with `_` are reserved in `CREATE TABLE`).
170
+ - **`test.cjs`, `test_prepared.cjs`, `bench.cjs`** — kept as a single
171
+ `test.cjs` (every leg of the common wrapper checklist: open, create,
172
+ put/get, a parameterized query, scan, prepare+rebind, link+neighbours,
173
+ tx commit/rollback, count_rows, an error path, close) and a rewritten
174
+ `bench.cjs` (same shape, `Db.get` instead of a SQL SELECT by `_key`,
175
+ because `_key` equality now needs a named index like every other Tier-1
176
+ predicate — QL_CONTRACT §6 — and the point of this bench is the FFI/JSON
177
+ round trip, not the planner).
178
+
179
+ ## Gaps in the C ABI found
180
+
181
+ None. Every one of the 59 functions in `dist/ffi/include/sekejap.h` is bound
182
+ and reachable from JS (the four "refused by name" calls are bound too, so
183
+ the refusal surfaces as a named `SekejapError`, never `TypeError: ... is not
184
+ a function`).
185
+
186
+ ## Tests
83
187
 
84
- ## License
188
+ ```sh
189
+ export SEKEJAP_LIB_DIR=/path/to/libsekejap # or SEKEJAP_LIB_PATH
190
+ npm install
191
+ npm test
192
+ npm run bench # optional
193
+ node examples/tour.js
194
+ ```
85
195
 
86
- Dual-licensed under **MIT OR Apache-2.0**.
196
+ `test.cjs` runs against the real library — no mocks — and prints one `ok -`
197
+ line per leg.
package/index.d.ts CHANGED
@@ -1,138 +1,196 @@
1
- /* tslint:disable */
2
- /* eslint-disable */
3
-
4
- /* auto-generated by NAPI-RS */
5
-
6
- /** The library version. */
7
- export declare function version(): string
8
- /**
9
- * An open sekejap database. JS is single-threaded, but we guard with a Mutex so
10
- * the handle is sound even if shared via worker threads.
11
- */
12
- export declare class Db {
13
- /** Open (or create) a database at `path`. */
14
- static open(path: string): Db
15
- /** Run a mutating statement; returns affected rows. */
16
- execute(sql: string): number
17
- /** Run a SELECT; returns a JSON-array string (caller `JSON.parse`s it). */
18
- query(sql: string): string
19
- /** Parameterized SELECT ($1, $2, …); `paramsJson` is a JSON array string. */
20
- queryParams(sql: string, paramsJson: string): string
21
- /**
22
- * Parameterized mutating statement ($1, $2, …); `paramsJson` is a JSON array
23
- * string. Returns affected rows. The typed layer's update/delete lower here.
24
- */
25
- executeParams(sql: string, paramsJson: string): number
26
- /**
27
- * Subscribe to the change feed. `callback` is invoked once per committed
28
- * mutation (a transaction fires once, at COMMIT) with a JSON string
29
- * `{"collections":[…],"keys":[…],"edge_types":[…]}`. Returns a subscription
30
- * id; pass it to [`unwatch`] to stop. The napi ThreadsafeFunction marshals
31
- * the call onto the JS event loop, so no manual threading is needed.
32
- */
33
- watch(callback: (arg: string) => any): number
34
- /** Stop a change-feed subscription created by [`watch`]. */
35
- unwatch(id: number): void
36
- /** Compile a query once for repeated execution — a prepared statement. */
37
- prepare(sql: string): PreparedStatement
38
- /**
39
- * Run a prepared statement, binding `$1`, `$2`, … from a JSON-array string.
40
- * Returns a JSON-array string (caller `JSON.parse`s it).
41
- */
42
- queryPrepared(stmt: PreparedStatement, paramsJson: string): string
43
- /** Insert/replace one node by slug with a JSON payload. */
44
- put(slug: string, payloadJson: string): void
45
- /** Create a plain edge from -> to of the given type. */
46
- link(from: string, to: string, edgeType: string): void
47
- /** Number of nodes. */
48
- nodeCount(): number
49
- /** Number of edges. */
50
- edgeCount(): number
51
- /** Compact: truncate WAL, rewrite payloads/topology, reclaim RAM. */
52
- compact(): void
53
- /**
54
- * Open an existing database in paged mode: identity/topology served from
55
- * memory-mapped files — small open time and resident memory at any size.
56
- */
57
- static openPaged(path: string): Db
58
- /** Open an existing database read-only. */
59
- static openReadOnly(path: string): Db
60
- /** A node's raw JSON payload, or null. */
61
- get(slug: string): string | null
62
- /** True if the node exists. */
63
- contains(slug: string): boolean
64
- /** Delete a node (and its edges). */
65
- remove(slug: string): void
66
- /**
67
- * Store many nodes in one batch (single disk sync).
68
- * `pairsJson` is a JSON array of `[slug, payloadJson]` pairs.
69
- */
70
- putMany(pairsJson: string): number
71
- /** Begin a bulk-load scope (defer the per-write disk sync). Pair with `endBulk`. */
72
- beginBulk(): void
73
- /** End a bulk-load scope: one disk sync for the whole batch. */
74
- endBulk(): void
75
- /** Store an embedding under a named field of a node. */
76
- putVector(slug: string, field: string, data: Array<number>): void
77
- /** The stored embedding for a node's field, or null. */
78
- getVector(slug: string, field: string): Array<number> | null
79
- /** Create an edge with JSON attributes (primitives ride fast columns). */
80
- linkMeta(from: string, to: string, edgeType: string, metaJson: string): void
81
- /**
82
- * Create many edges in one batch (single disk sync).
83
- * `edgesJson` is a JSON array of `[from, to, edgeType]` triples.
84
- */
85
- linkMany(edgesJson: string): void
86
- /** Remove a directed edge. */
87
- unlink(from: string, to: string, edgeType: string): void
88
- /**
89
- * Remove edges matching attribute equality conditions (`propsJson` is a
90
- * JSON object). Returns how many were removed.
91
- */
92
- unlinkWhere(from: string, to: string, edgeType: string, propsJson: string): number
93
- /**
94
- * Update attributes on matching edges: `propsJson` selects, `setsJson`
95
- * assigns. Returns how many were updated.
96
- */
97
- updateEdge(from: string, to: string, edgeType: string, propsJson: string, setsJson: string): number
98
- /**
99
- * Edges leaving a node, as a JSON-array string of
100
- * `{from, to, type, meta}` objects.
101
- */
102
- edgesFrom(slug: string): string
103
- /** Edges arriving at a node, same shape as `edgesFrom`. */
104
- edgesTo(slug: string): string
105
- /** Edges from one collection to another, same shape as `edgesFrom`. */
106
- edgesBetween(fromCollection: string, toCollection: string): string
107
- /** All collection names. */
108
- collectionNames(): Array<string>
109
- /** Every node slug. */
110
- allSlugs(): Array<string>
111
- /** DDL string for a collection schema, or null. */
112
- schemaDdl(collection: string): string | null
113
- /**
114
- * Ranked text search over a BM25-indexed field: JSON-array string of
115
- * `[slug, score]` pairs, best first.
116
- */
117
- bm25Search(field: string, query: string, topK: number): string
118
- /**
119
- * The query plan for a statement, as a JSON-array string (one step per
120
- * element). `analyze = true` also executes it and adds per-step timings.
121
- */
122
- explain(sql: string, analyze?: boolean | undefined | null): string
123
- /** Run a SHOW statement (`SHOW TABLES`, `SHOW EDGES`, …); JSON-array string. */
124
- show(sql: string): string
125
- /** Shrink resident memory to the live working set (never drops indexes). */
126
- trimMemory(): void
127
- /** Per-structure resident-memory estimate, as a JSON object string. */
128
- memoryReport(): string
129
- /** Override HNSW search breadth (`efSearch`); null restores the default. */
130
- setHnswEfSearch(ef?: number | undefined | null): void
131
- /**
132
- * Close the database and release its lock. Further calls error (or no-op
133
- * for count/list getters). Safe to call twice.
134
- */
135
- close(): void
1
+ // TypeScript definitions for the sekejap Node.js wrapper (over libsekejap's C ABI).
2
+ // See docs/dist/C_ABI.md for the contract this file mirrors.
3
+
4
+ export type SekejapStatusName =
5
+ | 'Ok'
6
+ | 'Refused'
7
+ | 'Corrupt'
8
+ | 'Unsupported'
9
+ | 'Io'
10
+ | 'Invalid'
11
+ | 'Busy'
12
+ | 'UnknownRow'
13
+ | 'Unknown';
14
+
15
+ export const SekejapStatus: {
16
+ readonly Ok: 0;
17
+ readonly Refused: 1;
18
+ readonly Corrupt: 2;
19
+ readonly Unsupported: 3;
20
+ readonly Io: 4;
21
+ readonly Invalid: 5;
22
+ readonly Busy: 6;
23
+ readonly UnknownRow: 7;
24
+ readonly Unknown: 8;
25
+ };
26
+
27
+ export const SekejapDirection: {
28
+ readonly Outgoing: 0;
29
+ readonly Incoming: 1;
30
+ readonly Both: 2;
31
+ };
32
+
33
+ export type Direction = 'outgoing' | 'incoming' | 'both' | 0 | 1 | 2;
34
+
35
+ export class SekejapError extends Error {
36
+ code: SekejapStatusName;
37
+ status: number;
38
+ }
39
+
40
+ export type JsonValue =
41
+ | null
42
+ | boolean
43
+ | number
44
+ | string
45
+ | JsonValue[]
46
+ | { [key: string]: JsonValue };
47
+
48
+ export type Document = { _key?: string; [field: string]: JsonValue | undefined };
49
+
50
+ export type FieldKind = 'text' | 'int' | 'real' | 'bool' | 'json' | 'geo' | 'point' | 'vector';
51
+
52
+ export interface FieldDecl {
53
+ name: string;
54
+ kind: FieldKind;
55
+ dimension?: number;
56
+ }
57
+
58
+ export interface IndexDecl {
59
+ name: string;
60
+ field: string;
61
+ family: 'scalar' | 'text' | 'exact_vector' | 'quantized_vector' | 'spatial_point' | 'spatial_geometry';
62
+ unique: boolean;
63
+ ready: boolean;
64
+ }
65
+
66
+ export interface CollectionDescriptor {
67
+ name: string;
68
+ timestamps: boolean;
69
+ rows: number | null;
70
+ fields: Array<FieldDecl & { declared: string | null; primary_key: boolean }>;
71
+ indexes: IndexDecl[];
72
+ }
73
+
74
+ export interface Neighbour {
75
+ collection: string;
76
+ key: string;
77
+ document: Document;
78
+ }
79
+
80
+ export interface StoreConfig {
81
+ budgetBytes?: number;
82
+ io?: 'buffered' | 'direct';
83
+ sync?: 'full' | 'normal' | 'off';
84
+ }
85
+
86
+ export interface Storage {
87
+ dataBytes: number;
88
+ walBytes: number;
89
+ totalBytes: number;
136
90
  }
137
- /** A prepared (compiled) query — from `Db.prepare`, run with `Db.queryPrepared`. */
138
- export declare class PreparedStatement { }
91
+
92
+ export interface ChangeEvent {
93
+ sequence: number;
94
+ collections: string[];
95
+ edge_types: string[];
96
+ keys: Array<{ collection: string; key: string; kind: 'put' | 'delete' }>;
97
+ keys_total: number;
98
+ keys_truncated: boolean;
99
+ unnamed_writes: number;
100
+ rows_affected: number;
101
+ }
102
+
103
+ export class Scan {
104
+ next(): JsonValue[] | null;
105
+ close(): void;
106
+ rows(): Generator<JsonValue, void, void>;
107
+ [Symbol.iterator](): Generator<JsonValue[], void, void>;
108
+ }
109
+
110
+ export class Statement {
111
+ query(params?: JsonValue): Document[];
112
+ execute(params?: JsonValue): number;
113
+ rebindable(): boolean | null;
114
+ close(): void;
115
+ }
116
+
117
+ export class Tx {
118
+ put(collection: string, key: string, doc: Document): void;
119
+ delete(collection: string, key: string): boolean;
120
+ link(fromCollection: string, fromKey: string, edgeType: string, toCollection: string, toKey: string): void;
121
+ execute(sql: string, params?: JsonValue): number;
122
+ commit(): void;
123
+ rollback(): void;
124
+ }
125
+
126
+ export class Db {
127
+ static open(path: string): Db;
128
+ static openWithConfig(path: string, config?: StoreConfig): Db;
129
+ static openService(path: string): Db;
130
+ /** REFUSED by name: sekejap has no in-memory store. Always throws. */
131
+ static openMemory(): never;
132
+
133
+ close(): void;
134
+
135
+ put(collection: string, key: string, doc: Document): void;
136
+ putMany(collection: string, rows: Array<{ key: string; doc: Document }>): number;
137
+ get(collection: string, key: string): Document | null;
138
+ exists(collection: string, key: string): boolean;
139
+ delete(collection: string, key: string): boolean;
140
+ scan(collection: string, pageRows?: number): Scan;
141
+
142
+ execute(sql: string, params?: JsonValue): number;
143
+ query(sql: string, params?: JsonValue): Document[];
144
+ explain(sql: string, params?: JsonValue): string;
145
+ prepare(sql: string): Statement;
146
+ stream(sql: string, params?: JsonValue, pageRows?: number): Scan;
147
+
148
+ link(fromCollection: string, fromKey: string, edgeType: string, toCollection: string, toKey: string): void;
149
+ linkWith(
150
+ fromCollection: string,
151
+ fromKey: string,
152
+ edgeType: string,
153
+ toCollection: string,
154
+ toKey: string,
155
+ properties: JsonValue
156
+ ): void;
157
+ unlink(fromCollection: string, fromKey: string, edgeType: string, toCollection: string, toKey: string): boolean;
158
+ neighbours(
159
+ collection: string,
160
+ key: string,
161
+ edgeType?: string | null,
162
+ direction?: Direction,
163
+ limit?: number
164
+ ): Neighbour[];
165
+
166
+ createCollection(name: string, fields: FieldDecl[]): boolean;
167
+ dropCollection(name: string): boolean;
168
+ collections(): string[];
169
+ describe(collection: string): CollectionDescriptor | null;
170
+ countRows(collection: string): number;
171
+ scanCountRows(collection: string): number;
172
+ scanCountEdges(): number;
173
+
174
+ transaction(): Tx;
175
+
176
+ checkpoint(): boolean;
177
+ publish(): void;
178
+ storage(): Storage;
179
+
180
+ statementTimeoutMs(milliseconds: number): void;
181
+ cancel(): void;
182
+ clearInterrupt(): boolean;
183
+ subscribe(): number;
184
+ nextChange(subscriptionId: number, timeoutMs?: number): ChangeEvent | null;
185
+ unsubscribe(subscriptionId: number): boolean;
186
+
187
+ /** REFUSED: no proportional-to-rows memory to trim. Always throws. */
188
+ trimMemory(): never;
189
+ /** REFUSED: no payload-rewriting compaction. Always throws; use checkpoint(). */
190
+ compact(): never;
191
+ /** REFUSED: SHOW has no Tier-1 spelling. Always throws; use collections()/describe(). */
192
+ show(statement?: string | null): never;
193
+ }
194
+
195
+ export function version(): string;
196
+ export function formatVersion(): number;