@stratadb/core 0.6.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/Cargo.toml ADDED
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "stratadb-node"
3
+ version = "0.6.0"
4
+ edition = "2021"
5
+ description = "Node.js bindings for StrataDB"
6
+ license = "MIT"
7
+ repository = "https://github.com/stratadb-labs/strata-node"
8
+
9
+ [lib]
10
+ crate-type = ["cdylib"]
11
+
12
+ [dependencies]
13
+ napi = { version = "2", features = ["napi8", "async", "serde-json"] }
14
+ napi-derive = "2"
15
+ serde = { version = "1.0", features = ["derive"] }
16
+ serde_json = "1.0"
17
+
18
+ # Use git dependency during development
19
+ stratadb = { git = "https://github.com/stratadb-labs/strata-core", branch = "main" }
20
+
21
+ [build-dependencies]
22
+ napi-build = "2"
23
+
24
+ [profile.release]
25
+ lto = true
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Strata
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # strata-node
2
+
3
+ Node.js SDK for [StrataDB](https://github.com/strata-systems/strata-core) - an embedded database for AI agents.
4
+
5
+ NAPI-RS bindings embedding the Rust library directly in a native Node.js addon. No network hop, no serialization overhead beyond the Node/Rust boundary.
6
+
7
+ ## Installation
8
+
9
+ ### From npm (coming soon)
10
+
11
+ ```bash
12
+ npm install stratadb
13
+ ```
14
+
15
+ ### From Source
16
+
17
+ Requires Rust toolchain and Node.js:
18
+
19
+ ```bash
20
+ git clone https://github.com/strata-systems/strata-node.git
21
+ cd strata-node
22
+ npm install
23
+ npm run build
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```javascript
29
+ const { Strata } = require('stratadb');
30
+
31
+ // Open a database (or use Strata.cache() for in-memory)
32
+ const db = Strata.open('/path/to/data');
33
+
34
+ // Key-value storage
35
+ db.kvPut('user:123', 'Alice');
36
+ console.log(db.kvGet('user:123')); // "Alice"
37
+
38
+ // Branch isolation (like git branches)
39
+ db.createBranch('experiment');
40
+ db.setBranch('experiment');
41
+ console.log(db.kvGet('user:123')); // null - isolated
42
+
43
+ // Space organization within branches
44
+ db.setSpace('conversations');
45
+ db.kvPut('msg_001', 'hello');
46
+ ```
47
+
48
+ ## Features
49
+
50
+ ### Six Data Primitives
51
+
52
+ | Primitive | Purpose | Key Methods |
53
+ |-----------|---------|-------------|
54
+ | **KV Store** | Working memory, config | `kvPut`, `kvGet`, `kvDelete`, `kvList` |
55
+ | **Event Log** | Immutable audit trail | `eventAppend`, `eventGet`, `eventList` |
56
+ | **State Cell** | CAS-based coordination | `stateSet`, `stateGet`, `stateCas` |
57
+ | **JSON Store** | Structured documents | `jsonSet`, `jsonGet`, `jsonDelete` |
58
+ | **Vector Store** | Embeddings, similarity search | `vectorUpsert`, `vectorSearch` |
59
+ | **Branch** | Data isolation | `createBranch`, `setBranch`, `forkBranch` |
60
+
61
+ ### Vector Operations
62
+
63
+ ```javascript
64
+ // Create a collection
65
+ db.vectorCreateCollection('embeddings', 384, 'cosine');
66
+
67
+ // Upsert with array
68
+ const embedding = new Array(384).fill(0).map(() => Math.random());
69
+ db.vectorUpsert('embeddings', 'doc-1', embedding, { title: 'Hello' });
70
+
71
+ // Search returns matches with scores
72
+ const results = db.vectorSearch('embeddings', embedding, 10);
73
+ for (const match of results) {
74
+ console.log(`${match.key}: ${match.score}`);
75
+ }
76
+ ```
77
+
78
+ ### Branch Operations
79
+
80
+ ```javascript
81
+ // Fork current branch (copies all data)
82
+ db.forkBranch('experiment');
83
+
84
+ // Compare branches
85
+ const diff = db.diffBranches('default', 'experiment');
86
+ console.log(`Added: ${diff.summary.totalAdded}`);
87
+
88
+ // Merge branches
89
+ const result = db.mergeBranches('experiment', 'last_writer_wins');
90
+ console.log(`Keys applied: ${result.keysApplied}`);
91
+ ```
92
+
93
+ ### Event Log
94
+
95
+ ```javascript
96
+ // Append events
97
+ db.eventAppend('tool_call', { tool: 'search', query: 'weather' });
98
+ db.eventAppend('tool_call', { tool: 'calculator', expr: '2+2' });
99
+
100
+ // Get by sequence number
101
+ const event = db.eventGet(0);
102
+
103
+ // List by type
104
+ const toolCalls = db.eventList('tool_call');
105
+ ```
106
+
107
+ ### Compare-and-Swap (Version-based)
108
+
109
+ ```javascript
110
+ // Initialize if not exists
111
+ db.stateInit('counter', 0);
112
+
113
+ // CAS is version-based - pass expected version, not expected value
114
+ // Get current value and its version via stateSet (returns version)
115
+ const version = db.stateSet('counter', 1);
116
+
117
+ // Update only if version matches
118
+ const newVersion = db.stateCas('counter', 2, version); // (cell, new_value, expected_version)
119
+ if (newVersion === null) {
120
+ console.log('CAS failed - version mismatch');
121
+ }
122
+ ```
123
+
124
+ ## API Reference
125
+
126
+ ### Strata
127
+
128
+ | Method | Description |
129
+ |--------|-------------|
130
+ | `Strata.open(path)` | Open database at path |
131
+ | `Strata.cache()` | Create in-memory database |
132
+
133
+ ### KV Store
134
+
135
+ | Method | Description |
136
+ |--------|-------------|
137
+ | `kvPut(key, value)` | Store a value |
138
+ | `kvGet(key)` | Get a value (returns null if missing) |
139
+ | `kvDelete(key)` | Delete a key |
140
+ | `kvList(prefix?)` | List keys |
141
+ | `kvHistory(key)` | Get version history |
142
+
143
+ ### State Cell
144
+
145
+ | Method | Description |
146
+ |--------|-------------|
147
+ | `stateSet(cell, value)` | Set value |
148
+ | `stateGet(cell)` | Get value |
149
+ | `stateInit(cell, value)` | Initialize if not exists |
150
+ | `stateCas(cell, newValue, expectedVersion?)` | Compare-and-swap (version-based) |
151
+ | `stateHistory(cell)` | Get version history |
152
+
153
+ ### Event Log
154
+
155
+ | Method | Description |
156
+ |--------|-------------|
157
+ | `eventAppend(type, payload)` | Append event |
158
+ | `eventGet(sequence)` | Get by sequence |
159
+ | `eventList(type)` | List by type |
160
+ | `eventLen()` | Get count |
161
+
162
+ ### JSON Store
163
+
164
+ | Method | Description |
165
+ |--------|-------------|
166
+ | `jsonSet(key, path, value)` | Set at JSONPath |
167
+ | `jsonGet(key, path)` | Get at JSONPath |
168
+ | `jsonDelete(key, path)` | Delete |
169
+ | `jsonHistory(key)` | Get version history |
170
+ | `jsonList(limit, prefix?, cursor?)` | List keys |
171
+
172
+ ### Vector Store
173
+
174
+ | Method | Description |
175
+ |--------|-------------|
176
+ | `vectorCreateCollection(name, dim, metric?)` | Create collection |
177
+ | `vectorDeleteCollection(name)` | Delete collection |
178
+ | `vectorListCollections()` | List collections |
179
+ | `vectorUpsert(collection, key, vector, metadata?)` | Insert/update |
180
+ | `vectorGet(collection, key)` | Get vector |
181
+ | `vectorDelete(collection, key)` | Delete vector |
182
+ | `vectorSearch(collection, query, k)` | Search |
183
+ | `vectorCollectionStats(collection)` | Get collection statistics |
184
+ | `vectorBatchUpsert(collection, vectors)` | Batch insert/update vectors |
185
+
186
+ ### Branches
187
+
188
+ | Method | Description |
189
+ |--------|-------------|
190
+ | `currentBranch()` | Get current branch |
191
+ | `setBranch(name)` | Switch branch |
192
+ | `createBranch(name)` | Create empty branch |
193
+ | `forkBranch(dest)` | Fork with data copy |
194
+ | `listBranches()` | List all branches |
195
+ | `deleteBranch(name)` | Delete branch |
196
+ | `branchExists(name)` | Check if branch exists |
197
+ | `branchGet(name)` | Get branch metadata |
198
+ | `diffBranches(a, b)` | Compare branches |
199
+ | `mergeBranches(source, strategy?)` | Merge into current |
200
+
201
+ ### Spaces
202
+
203
+ | Method | Description |
204
+ |--------|-------------|
205
+ | `currentSpace()` | Get current space |
206
+ | `setSpace(name)` | Switch space |
207
+ | `listSpaces()` | List spaces |
208
+ | `deleteSpace(name)` | Delete space |
209
+ | `deleteSpaceForce(name)` | Force delete space |
210
+
211
+ ### Database
212
+
213
+ | Method | Description |
214
+ |--------|-------------|
215
+ | `ping()` | Health check |
216
+ | `info()` | Get database info |
217
+ | `flush()` | Flush to disk |
218
+ | `compact()` | Trigger compaction |
219
+
220
+ ### Bundle Operations
221
+
222
+ | Method | Description |
223
+ |--------|-------------|
224
+ | `branchExport(branch, path)` | Export branch to bundle file |
225
+ | `branchImport(path)` | Import branch from bundle file |
226
+ | `branchValidateBundle(path)` | Validate bundle file |
227
+
228
+ ## TypeScript
229
+
230
+ Full TypeScript definitions are included:
231
+
232
+ ```typescript
233
+ import { Strata, JsonValue, SearchMatch } from 'stratadb';
234
+
235
+ const db = Strata.cache();
236
+ db.kvPut('key', { count: 42 });
237
+ const value: JsonValue = db.kvGet('key');
238
+ ```
239
+
240
+ ## Development
241
+
242
+ ```bash
243
+ # Install dev dependencies
244
+ npm install
245
+
246
+ # Build native module
247
+ npm run build
248
+
249
+ # Run tests
250
+ npm test
251
+ ```
252
+
253
+ ## License
254
+
255
+ MIT
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Integration tests for the StrataDB Node.js SDK.
3
+ */
4
+
5
+ const { Strata } = require('../index');
6
+
7
+ describe('Strata', () => {
8
+ let db;
9
+
10
+ beforeEach(() => {
11
+ db = Strata.cache();
12
+ });
13
+
14
+ describe('KV Store', () => {
15
+ test('put and get', () => {
16
+ db.kvPut('key1', 'value1');
17
+ expect(db.kvGet('key1')).toBe('value1');
18
+ });
19
+
20
+ test('put and get object', () => {
21
+ db.kvPut('config', { theme: 'dark', count: 42 });
22
+ const result = db.kvGet('config');
23
+ expect(result.theme).toBe('dark');
24
+ expect(result.count).toBe(42);
25
+ });
26
+
27
+ test('get missing returns null', () => {
28
+ expect(db.kvGet('nonexistent')).toBeNull();
29
+ });
30
+
31
+ test('delete', () => {
32
+ db.kvPut('to_delete', 'value');
33
+ expect(db.kvDelete('to_delete')).toBe(true);
34
+ expect(db.kvGet('to_delete')).toBeNull();
35
+ });
36
+
37
+ test('list', () => {
38
+ db.kvPut('user:1', 'alice');
39
+ db.kvPut('user:2', 'bob');
40
+ db.kvPut('item:1', 'book');
41
+
42
+ const allKeys = db.kvList();
43
+ expect(allKeys.length).toBe(3);
44
+
45
+ const userKeys = db.kvList('user:');
46
+ expect(userKeys.length).toBe(2);
47
+ });
48
+ });
49
+
50
+ describe('State Cell', () => {
51
+ test('set and get', () => {
52
+ db.stateSet('counter', 100);
53
+ expect(db.stateGet('counter')).toBe(100);
54
+ });
55
+
56
+ test('init', () => {
57
+ db.stateInit('status', 'pending');
58
+ expect(db.stateGet('status')).toBe('pending');
59
+ });
60
+
61
+ test('cas', () => {
62
+ // CAS is version-based, not value-based
63
+ const version = db.stateSet('value', 1);
64
+ // Try to update with correct version
65
+ const newVersion = db.stateCas('value', 2, version);
66
+ expect(newVersion).not.toBeNull();
67
+ expect(db.stateGet('value')).toBe(2);
68
+ // Try with wrong version - should fail
69
+ const result = db.stateCas('value', 3, 999);
70
+ expect(result).toBeNull(); // CAS failed
71
+ });
72
+ });
73
+
74
+ describe('Event Log', () => {
75
+ test('append and get', () => {
76
+ db.eventAppend('user_action', { action: 'click', target: 'button' });
77
+ expect(db.eventLen()).toBe(1);
78
+
79
+ const event = db.eventGet(0);
80
+ expect(event).not.toBeNull();
81
+ expect(event.value.action).toBe('click');
82
+ });
83
+
84
+ test('list by type', () => {
85
+ db.eventAppend('click', { x: 10 });
86
+ db.eventAppend('scroll', { y: 100 });
87
+ db.eventAppend('click', { x: 20 });
88
+
89
+ const clicks = db.eventList('click');
90
+ expect(clicks.length).toBe(2);
91
+ });
92
+ });
93
+
94
+ describe('JSON Store', () => {
95
+ test('set and get', () => {
96
+ db.jsonSet('config', '$', { theme: 'dark', lang: 'en' });
97
+ const result = db.jsonGet('config', '$');
98
+ expect(result.theme).toBe('dark');
99
+ });
100
+
101
+ test('get path', () => {
102
+ db.jsonSet('config', '$', { theme: 'dark', lang: 'en' });
103
+ const theme = db.jsonGet('config', '$.theme');
104
+ expect(theme).toBe('dark');
105
+ });
106
+
107
+ test('list', () => {
108
+ db.jsonSet('doc1', '$', { a: 1 });
109
+ db.jsonSet('doc2', '$', { b: 2 });
110
+ const result = db.jsonList(100); // limit is required
111
+ expect(result.keys.length).toBe(2);
112
+ });
113
+ });
114
+
115
+ describe('Vector Store', () => {
116
+ test('create collection', () => {
117
+ db.vectorCreateCollection('embeddings', 4);
118
+ const collections = db.vectorListCollections();
119
+ expect(collections.some((c) => c.name === 'embeddings')).toBe(true);
120
+ });
121
+
122
+ test('upsert and search', () => {
123
+ db.vectorCreateCollection('embeddings', 4);
124
+
125
+ const v1 = [1.0, 0.0, 0.0, 0.0];
126
+ const v2 = [0.0, 1.0, 0.0, 0.0];
127
+
128
+ db.vectorUpsert('embeddings', 'v1', v1);
129
+ db.vectorUpsert('embeddings', 'v2', v2);
130
+
131
+ const results = db.vectorSearch('embeddings', v1, 2);
132
+ expect(results.length).toBe(2);
133
+ expect(results[0].key).toBe('v1'); // Most similar
134
+ });
135
+
136
+ test('upsert with metadata', () => {
137
+ db.vectorCreateCollection('docs', 4);
138
+ const vec = [1.0, 0.0, 0.0, 0.0];
139
+ db.vectorUpsert('docs', 'doc1', vec, { title: 'Hello' });
140
+
141
+ const result = db.vectorGet('docs', 'doc1');
142
+ expect(result.metadata.title).toBe('Hello');
143
+ });
144
+ });
145
+
146
+ describe('Branches', () => {
147
+ test('create and list', () => {
148
+ db.createBranch('feature');
149
+ const branches = db.listBranches();
150
+ expect(branches).toContain('default');
151
+ expect(branches).toContain('feature');
152
+ });
153
+
154
+ test('switch', () => {
155
+ db.kvPut('x', 1);
156
+ db.createBranch('feature');
157
+ db.setBranch('feature');
158
+
159
+ // Data isolated in new branch
160
+ expect(db.kvGet('x')).toBeNull();
161
+
162
+ db.kvPut('x', 2);
163
+ db.setBranch('default');
164
+ expect(db.kvGet('x')).toBe(1);
165
+ });
166
+
167
+ test('fork', () => {
168
+ db.kvPut('shared', 'original');
169
+ const result = db.forkBranch('forked');
170
+ expect(result.keysCopied).toBeGreaterThan(0);
171
+
172
+ db.setBranch('forked');
173
+ expect(db.kvGet('shared')).toBe('original');
174
+ });
175
+
176
+ test('current branch', () => {
177
+ expect(db.currentBranch()).toBe('default');
178
+ db.createBranch('test');
179
+ db.setBranch('test');
180
+ expect(db.currentBranch()).toBe('test');
181
+ });
182
+ });
183
+
184
+ describe('Spaces', () => {
185
+ test('list spaces', () => {
186
+ const spaces = db.listSpaces();
187
+ expect(spaces).toContain('default');
188
+ });
189
+
190
+ test('switch space', () => {
191
+ db.kvPut('key', 'value1');
192
+ db.setSpace('other');
193
+ expect(db.kvGet('key')).toBeNull();
194
+
195
+ db.kvPut('key', 'value2');
196
+ db.setSpace('default');
197
+ expect(db.kvGet('key')).toBe('value1');
198
+ });
199
+
200
+ test('current space', () => {
201
+ expect(db.currentSpace()).toBe('default');
202
+ });
203
+ });
204
+
205
+ describe('Database', () => {
206
+ test('ping', () => {
207
+ const version = db.ping();
208
+ expect(version).toBeTruthy();
209
+ });
210
+
211
+ test('info', () => {
212
+ const info = db.info();
213
+ expect(info.version).toBeTruthy();
214
+ expect(info.branchCount).toBeGreaterThanOrEqual(1);
215
+ });
216
+ });
217
+ });
package/build.rs ADDED
@@ -0,0 +1,5 @@
1
+ extern crate napi_build;
2
+
3
+ fn main() {
4
+ napi_build::setup();
5
+ }