jsql-neo 3.4.3 → 3.4.6
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 +91 -12
- package/lib/wasm_client.js +84 -2
- package/package.json +1 -1
- package/wasm/jsql_neo_wasm.d.ts +2 -0
- package/wasm/jsql_neo_wasm.js +23 -0
- package/wasm/jsql_neo_wasm_bg.wasm +0 -0
- package/wasm/jsql_neo_wasm_bg.wasm.d.ts +1 -0
package/README.md
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
# JSQL-NEO v3.
|
|
1
|
+
# JSQL-NEO v3.4.3
|
|
2
2
|
|
|
3
|
-
Rust-powered embedded database
|
|
3
|
+
Rust-powered embedded database with **three engines** in one npm package.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Engines
|
|
6
|
+
|
|
7
|
+
| Engine | Entry | Use Case | Speed |
|
|
8
|
+
|--------|-------|----------|-------|
|
|
9
|
+
| **WASM** (Rust → wasm-pack) | `JSQL` | Zero native deps, Node.js | 1.4M rows/sec insert |
|
|
10
|
+
| **HTTP** (Rust actix-web) | `HttpJSQL` | Multi-process / remote | 340K rows/sec insert |
|
|
11
|
+
| **Pure JS** (Database class) | `Database` | Local JSON file, SQLite-like | 617K rows/sec insert |
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### WASM (no server, zero deps)
|
|
6
16
|
|
|
7
17
|
```js
|
|
8
18
|
const { JSQL } = require('jsql-neo');
|
|
19
|
+
|
|
9
20
|
const db = new JSQL();
|
|
10
21
|
await db.start();
|
|
11
22
|
|
|
@@ -16,26 +27,94 @@ await db.createTable('users', {
|
|
|
16
27
|
|
|
17
28
|
const [id] = await db.insert('users', { name: 'Alice', age: 30 });
|
|
18
29
|
const user = await db.findById('users', id);
|
|
19
|
-
|
|
30
|
+
// → { id: 1, fields: { name: 'Alice', age: 30 }, created_at: '...', updated_at: '...' }
|
|
31
|
+
|
|
32
|
+
await db.stop();
|
|
20
33
|
```
|
|
21
34
|
|
|
22
|
-
|
|
35
|
+
### Pure JS (local file, SQLite-like)
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const { Database } = require('jsql-neo');
|
|
39
|
+
|
|
40
|
+
const db = new Database('/tmp/mydb.json');
|
|
41
|
+
const users = db.createTable('users', {
|
|
42
|
+
id: { type: 'integer', autoIncrement: true, primaryKey: true },
|
|
43
|
+
name: { type: 'string', length: 32 },
|
|
44
|
+
age: { type: 'integer' }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const ids = users.insertMany([{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]);
|
|
48
|
+
users.updateById(ids[0], { age: 31 });
|
|
49
|
+
const user = users.findById(ids[0]);
|
|
50
|
+
db.save();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### HTTP Server
|
|
23
54
|
|
|
24
55
|
```bash
|
|
25
|
-
# Start the server
|
|
26
56
|
JSQL_DATA_DIR=/tmp/jsql-neo npx jsql-neo
|
|
57
|
+
```
|
|
27
58
|
|
|
28
|
-
|
|
59
|
+
```js
|
|
29
60
|
const { HttpJSQL } = require('jsql-neo');
|
|
61
|
+
|
|
30
62
|
const db = new HttpJSQL({ host: '127.0.0.1', port: 6379 });
|
|
63
|
+
await db.start();
|
|
64
|
+
|
|
65
|
+
await db.createTable('users', { name: { type: 'string' }, age: { type: 'integer' } });
|
|
66
|
+
const [id] = await db.insert('users', { name: 'Alice', age: 30 });
|
|
67
|
+
const user = await db.findById('users', id);
|
|
68
|
+
|
|
69
|
+
await db.stop();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API
|
|
73
|
+
|
|
74
|
+
| Method | WASM | HTTP | Pure JS | Description |
|
|
75
|
+
|--------|------|------|---------|-------------|
|
|
76
|
+
| `createTable(name, schema)` | ✅ | ✅ | ✅ | Define table with typed fields |
|
|
77
|
+
| `insert(table, data)` | ✅ | ✅ | ✅ | Insert row(s), returns IDs |
|
|
78
|
+
| `findById(table, id)` | ✅ | ✅ | ✅ | O(1) PK lookup |
|
|
79
|
+
| `find(table, filter?)` | ✅ | ✅ | ✅ | Filtered query with B-Tree index |
|
|
80
|
+
| `count(table)` | ✅ | ✅ | ✅ | Row count |
|
|
81
|
+
| `updateById(table, id, data)` | ✅ | ✅ | ✅ | O(1) PK update |
|
|
82
|
+
| `removeById(table, id)` | ✅ | ✅ | ✅ | O(1) PK delete |
|
|
83
|
+
| `dropTable(name)` | ✅ | ✅ | ✅ | Remove table |
|
|
84
|
+
|
|
85
|
+
All engines share the same async API for CRUD operations.
|
|
86
|
+
|
|
87
|
+
## Performance (Pure JS, 100K rows)
|
|
88
|
+
|
|
89
|
+
| Operation | Time | Rate |
|
|
90
|
+
|-----------|------|------|
|
|
91
|
+
| Insert 100K | 128 ms | 781K rows/sec |
|
|
92
|
+
| findById × 10,000 | 3 ms | 0.3 μs each |
|
|
93
|
+
| updateById × 10,000 | 82 ms | 8.2 μs each (O(1) hash index) |
|
|
94
|
+
| count × 100 | 0 ms | — |
|
|
95
|
+
| findAll × 5 | 16 ms | — |
|
|
96
|
+
| Filtered query × 100 | 407 ms | — |
|
|
97
|
+
|
|
98
|
+
## Schema Field Options
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
{
|
|
102
|
+
type: 'string' | 'integer' | 'float' | 'boolean',
|
|
103
|
+
primaryKey: true, // PK field (auto-indexed)
|
|
104
|
+
autoIncrement: true, // Auto-generate integer PK
|
|
105
|
+
length: 32, // Max string length
|
|
106
|
+
default: 'value', // Default value
|
|
107
|
+
nullable: true // Allow null
|
|
108
|
+
}
|
|
31
109
|
```
|
|
32
110
|
|
|
33
111
|
## Features
|
|
34
112
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
113
|
+
- Three engines: WASM (native Rust), HTTP (actix-web), Pure JS (local JSON)
|
|
114
|
+
- O(1) primary key hash index (`FxHashMap` / `Map`)
|
|
115
|
+
- B-Tree indexing for range queries
|
|
116
|
+
- WAL + snapshot crash recovery (HTTP engine)
|
|
117
|
+
- Strings Pool for memory-efficient storage
|
|
38
118
|
- Batch insert / update / delete
|
|
39
119
|
- Cursor-based pagination
|
|
40
|
-
- Transaction support
|
|
41
|
-
- WASM — zero native dependencies, runs in Node.js and browsers
|
|
120
|
+
- Transaction support (HTTP engine)
|
package/lib/wasm_client.js
CHANGED
|
@@ -5,9 +5,90 @@ function safeJsonParse(str) {
|
|
|
5
5
|
try { return JSON.parse(str); } catch { return str; }
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
const textEncoder = new TextEncoder();
|
|
9
|
+
|
|
10
|
+
function encodeBatch(rows) {
|
|
11
|
+
if (rows.length === 0) return new Uint8Array(0);
|
|
12
|
+
const fieldNames = Object.keys(rows[0]);
|
|
13
|
+
const nFields = fieldNames.length;
|
|
14
|
+
|
|
15
|
+
const nameBytes = fieldNames.map(n => textEncoder.encode(n));
|
|
16
|
+
let size = 1;
|
|
17
|
+
for (const nb of nameBytes) size += 1 + nb.length;
|
|
18
|
+
size += 4;
|
|
19
|
+
|
|
20
|
+
const strBufs = [];
|
|
21
|
+
for (let ri = 0; ri < rows.length; ri++) {
|
|
22
|
+
const row = rows[ri];
|
|
23
|
+
for (let fi = 0; fi < nFields; fi++) {
|
|
24
|
+
const val = row[fieldNames[fi]];
|
|
25
|
+
if (val === null || val === undefined) {
|
|
26
|
+
size += 1;
|
|
27
|
+
strBufs.push(null);
|
|
28
|
+
} else if (typeof val === 'number') {
|
|
29
|
+
size += 9;
|
|
30
|
+
strBufs.push(null);
|
|
31
|
+
} else if (typeof val === 'string') {
|
|
32
|
+
const b = textEncoder.encode(val);
|
|
33
|
+
strBufs.push(b);
|
|
34
|
+
size += 1 + 4 + b.length;
|
|
35
|
+
} else {
|
|
36
|
+
size += 2;
|
|
37
|
+
strBufs.push(val ? 1 : 0);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const buf = new Uint8Array(size);
|
|
43
|
+
const dv = new DataView(buf.buffer, 0, size);
|
|
44
|
+
let off = 0;
|
|
45
|
+
|
|
46
|
+
buf[off++] = nFields;
|
|
47
|
+
for (const nb of nameBytes) {
|
|
48
|
+
buf[off++] = nb.length;
|
|
49
|
+
buf.set(nb, off);
|
|
50
|
+
off += nb.length;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dv.setUint32(off, rows.length, true);
|
|
54
|
+
off += 4;
|
|
55
|
+
|
|
56
|
+
let si = 0;
|
|
57
|
+
for (const row of rows) {
|
|
58
|
+
for (let fi = 0; fi < nFields; fi++) {
|
|
59
|
+
const val = row[fieldNames[fi]];
|
|
60
|
+
const item = strBufs[si++];
|
|
61
|
+
if (val === null || val === undefined) {
|
|
62
|
+
buf[off++] = 0;
|
|
63
|
+
} else if (typeof val === 'number') {
|
|
64
|
+
if (Number.isInteger(val)) {
|
|
65
|
+
buf[off++] = 1;
|
|
66
|
+
dv.setBigInt64(off, BigInt(val), true);
|
|
67
|
+
off += 8;
|
|
68
|
+
} else {
|
|
69
|
+
buf[off++] = 2;
|
|
70
|
+
dv.setFloat64(off, val, true);
|
|
71
|
+
off += 8;
|
|
72
|
+
}
|
|
73
|
+
} else if (typeof val === 'string') {
|
|
74
|
+
buf[off++] = 3;
|
|
75
|
+
dv.setUint32(off, item.length, true);
|
|
76
|
+
off += 4;
|
|
77
|
+
buf.set(item, off);
|
|
78
|
+
off += item.length;
|
|
79
|
+
} else {
|
|
80
|
+
buf[off++] = 4;
|
|
81
|
+
buf[off++] = item;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return buf;
|
|
87
|
+
}
|
|
88
|
+
|
|
8
89
|
class JSQL {
|
|
9
90
|
constructor(opts = {}) {
|
|
10
|
-
this._flushThreshold = opts.flushThreshold ||
|
|
91
|
+
this._flushThreshold = opts.flushThreshold || 5000;
|
|
11
92
|
this._buffer = {};
|
|
12
93
|
this._bufferSize = 0;
|
|
13
94
|
}
|
|
@@ -15,7 +96,8 @@ class JSQL {
|
|
|
15
96
|
async start() {}
|
|
16
97
|
|
|
17
98
|
async _insertBatch(table, rows) {
|
|
18
|
-
const
|
|
99
|
+
const bin = encodeBatch(rows);
|
|
100
|
+
const r = safeJsonParse(wasmBindings.jsql_insert_buf(table, bin));
|
|
19
101
|
if (r && r.error) throw new Error(r.error);
|
|
20
102
|
return r;
|
|
21
103
|
}
|
package/package.json
CHANGED
package/wasm/jsql_neo_wasm.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export function jsql_insert(table: string, data_json: string): string;
|
|
|
15
15
|
|
|
16
16
|
export function jsql_insert_buf(table: string, data: Uint8Array): string;
|
|
17
17
|
|
|
18
|
+
export function jsql_insert_buf_count(table: string, data: Uint8Array): string;
|
|
19
|
+
|
|
18
20
|
export function jsql_remove_by_id(table: string, id: bigint): string;
|
|
19
21
|
|
|
20
22
|
export function jsql_update_by_id(table: string, id: bigint, data_json: string): string;
|
package/wasm/jsql_neo_wasm.js
CHANGED
|
@@ -155,6 +155,29 @@ function jsql_insert_buf(table, data) {
|
|
|
155
155
|
}
|
|
156
156
|
exports.jsql_insert_buf = jsql_insert_buf;
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @param {string} table
|
|
160
|
+
* @param {Uint8Array} data
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
function jsql_insert_buf_count(table, data) {
|
|
164
|
+
let deferred3_0;
|
|
165
|
+
let deferred3_1;
|
|
166
|
+
try {
|
|
167
|
+
const ptr0 = passStringToWasm0(table, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
168
|
+
const len0 = WASM_VECTOR_LEN;
|
|
169
|
+
const ptr1 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
|
|
170
|
+
const len1 = WASM_VECTOR_LEN;
|
|
171
|
+
const ret = wasm.jsql_insert_buf_count(ptr0, len0, ptr1, len1);
|
|
172
|
+
deferred3_0 = ret[0];
|
|
173
|
+
deferred3_1 = ret[1];
|
|
174
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
175
|
+
} finally {
|
|
176
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
exports.jsql_insert_buf_count = jsql_insert_buf_count;
|
|
180
|
+
|
|
158
181
|
/**
|
|
159
182
|
* @param {string} table
|
|
160
183
|
* @param {bigint} id
|
|
Binary file
|
|
@@ -8,6 +8,7 @@ export const jsql_find: (a: number, b: number, c: number, d: number, e: number,
|
|
|
8
8
|
export const jsql_find_by_id: (a: number, b: number, c: bigint) => [number, number];
|
|
9
9
|
export const jsql_insert: (a: number, b: number, c: number, d: number) => [number, number];
|
|
10
10
|
export const jsql_insert_buf: (a: number, b: number, c: number, d: number) => [number, number];
|
|
11
|
+
export const jsql_insert_buf_count: (a: number, b: number, c: number, d: number) => [number, number];
|
|
11
12
|
export const jsql_remove_by_id: (a: number, b: number, c: bigint) => [number, number];
|
|
12
13
|
export const jsql_update_by_id: (a: number, b: number, c: bigint, d: number, e: number) => [number, number];
|
|
13
14
|
export const __wbindgen_malloc: (a: number, b: number) => number;
|