jsql-neo 6.3.10 → 6.4.0-beta.10
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 +22 -1
- package/bin/jsql +90 -0
- package/index.js +1 -1
- package/lib/cache.js +21 -8
- package/lib/database.js +232 -91
- package/lib/date-types.js +5 -2
- package/lib/jsql_format.js +17 -5
- package/lib/mongo_server.js +31 -14
- package/lib/mysql_compat.js +2 -1
- package/lib/mysql_server.js +6 -0
- package/lib/native_client.js +75 -32
- package/lib/nedb_compat.js +6 -6
- package/lib/plugin.js +12 -10
- package/lib/query.js +2 -1
- package/lib/redis_server.js +38 -13
- package/lib/sql.js +187 -46
- package/lib/sqlite_compat.js +2 -1
- package/lib/table.js +81 -3
- package/lib/wasm_client.js +73 -30
- package/lib/web_ui.js +14 -8
- package/native/jsql-neo-native.node +0 -0
- package/package.json +3 -3
- package/test/coverage.js +3 -1
- package/test/dml-constraints.test.js +16 -0
- package/test/query-opt.test.js +8 -8
- package/test/readme-audit.test.js +8 -7
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **One engine to rule them all** — a Rust-powered embedded database that speaks your language:
|
|
4
4
|
> MySQL, PostgreSQL, MongoDB, Redis, SQL, TypeScript, **and the browser**.
|
|
5
5
|
|
|
6
|
-
> **v6.
|
|
6
|
+
> **v6.4.0-beta.10** — official release build · [github.com/vexify-org/JSQL-neo](https://github.com/vexify-org/JSQL-neo) · [jsql.vexify.top](https://jsql.vexify.top)
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|

|
|
@@ -112,6 +112,27 @@ const db = new Database(':memory:');
|
|
|
112
112
|
// same API as Node.js
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
### 🦀 NativeJSQL
|
|
116
|
+
|
|
117
|
+
Pure Rust native bindings — the highest-performance way to run JSQL-NEO:
|
|
118
|
+
|
|
119
|
+
- **Zero abstraction overhead** — direct calls into the Rust core
|
|
120
|
+
- **Batch operations** — up to **1.54M rows/sec** peak throughput
|
|
121
|
+
- **Automatic memory management** with smart caching
|
|
122
|
+
|
|
123
|
+
**Install:**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm install jsql-neo
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Usage:**
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
import { Database } from 'jsql-neo';
|
|
133
|
+
const db = new Database('mydb.jsql');
|
|
134
|
+
```
|
|
135
|
+
|
|
115
136
|
### Storage modes
|
|
116
137
|
|
|
117
138
|
| Mode | Usage | Use Case |
|
package/bin/jsql
CHANGED
|
@@ -247,6 +247,96 @@ const cli = yaggs({ pkg: require('../package.json') })
|
|
|
247
247
|
console.log(`jsql-neo Redis server on ${argv.host || '127.0.0.1'}:${argv.port != null ? argv.port : 6379}${argv['data-dir'] ? ' (data: ' + argv['data-dir'] + ')' : ''}`);
|
|
248
248
|
setInterval(() => {}, 1 << 30);
|
|
249
249
|
})
|
|
250
|
+
.command('plugin', 'Plugin registry management (list, search, install, uninstall, info)', (sub) => {
|
|
251
|
+
sub.option('json', { type: 'boolean', description: 'Output as JSON' });
|
|
252
|
+
sub.option('category', { type: 'string', description: 'Filter by category' });
|
|
253
|
+
sub.option('limit', { type: 'number', description: 'Max results per page' });
|
|
254
|
+
}, async (argv) => {
|
|
255
|
+
const pluginMgr = require('../src/cli/plugin');
|
|
256
|
+
const action = argv._[0];
|
|
257
|
+
const arg = argv._[1];
|
|
258
|
+
const useJson = !!argv.json;
|
|
259
|
+
|
|
260
|
+
const fail = (e) => { console.error(`Error: ${e.message}`); process.exitCode = 1; };
|
|
261
|
+
const out = (obj) => {
|
|
262
|
+
if (obj === undefined) return;
|
|
263
|
+
if (useJson) console.log(JSON.stringify(obj, null, 2));
|
|
264
|
+
else console.log(obj);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
switch (action) {
|
|
269
|
+
case 'list':
|
|
270
|
+
case 'ls': {
|
|
271
|
+
const opts = {};
|
|
272
|
+
if (argv.category) opts.category = argv.category;
|
|
273
|
+
if (argv.limit) opts.limit = argv.limit;
|
|
274
|
+
const plugins = await pluginMgr.listPlugins(opts);
|
|
275
|
+
out(useJson ? plugins : pluginMgr.formatPluginList(plugins));
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case 'search': {
|
|
279
|
+
if (!arg) throw new Error('Usage: jsql plugin search <query>');
|
|
280
|
+
const opts = {};
|
|
281
|
+
if (argv.category) opts.category = argv.category;
|
|
282
|
+
const data = await pluginMgr.searchPlugins(arg, opts);
|
|
283
|
+
// search 返回 { results: [...], query, total } 或 [...]
|
|
284
|
+
const plugins = Array.isArray(data) ? data : (data.results || []);
|
|
285
|
+
if (useJson) {
|
|
286
|
+
out(data);
|
|
287
|
+
} else {
|
|
288
|
+
if (!plugins.length) {
|
|
289
|
+
console.log(`No plugins found for '${arg}'.`);
|
|
290
|
+
} else {
|
|
291
|
+
console.log(`Found ${plugins.length} plugin(s) for '${arg}':`);
|
|
292
|
+
console.log(pluginMgr.formatPluginList(plugins.map(p => p.plugin || p)));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case 'install': {
|
|
298
|
+
if (!arg) throw new Error('Usage: jsql plugin install <name>');
|
|
299
|
+
const manifest = await pluginMgr.installPlugin(arg);
|
|
300
|
+
out(useJson ? manifest : `Installed plugin '${manifest.name}' (${manifest.version})`);
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
case 'uninstall':
|
|
304
|
+
case 'remove': {
|
|
305
|
+
if (!arg) throw new Error('Usage: jsql plugin uninstall <name>');
|
|
306
|
+
const r = pluginMgr.uninstallPlugin(arg);
|
|
307
|
+
out(useJson ? r : `Uninstalled plugin '${r.name}'.`);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case 'info': {
|
|
311
|
+
if (!arg) throw new Error('Usage: jsql plugin info <name>');
|
|
312
|
+
const p = await pluginMgr.getPluginInfo(arg);
|
|
313
|
+
out(useJson ? p : pluginMgr.formatPluginInfo(p));
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
case undefined:
|
|
317
|
+
// 无子命令 → 显示已安装插件
|
|
318
|
+
const installed = pluginMgr.listInstalled();
|
|
319
|
+
if (useJson) {
|
|
320
|
+
out(installed);
|
|
321
|
+
} else {
|
|
322
|
+
if (!installed.length) {
|
|
323
|
+
console.log('No plugins installed.');
|
|
324
|
+
console.log(`Plugin directory: ${pluginMgr.PLUGIN_DIR}`);
|
|
325
|
+
} else {
|
|
326
|
+
console.log(`Installed plugins (${installed.length}):`);
|
|
327
|
+
installed.forEach(p => {
|
|
328
|
+
console.log(` ${p.name.padEnd(24)} ${p.version || '?'}`);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
default:
|
|
334
|
+
throw new Error(`Unknown plugin action: ${action}`);
|
|
335
|
+
}
|
|
336
|
+
} catch (e) {
|
|
337
|
+
fail(e);
|
|
338
|
+
}
|
|
339
|
+
})
|
|
250
340
|
.command('version', 'Show version', null, () => {
|
|
251
341
|
console.log(require('../package.json').version);
|
|
252
342
|
})
|
package/index.js
CHANGED
package/lib/cache.js
CHANGED
|
@@ -50,7 +50,7 @@ class Cache {
|
|
|
50
50
|
if (typeof val === 'string') return 'string';
|
|
51
51
|
if (val instanceof Set) return 'set';
|
|
52
52
|
if (Array.isArray(val)) return val._type === 'hash' ? 'hash' : 'list';
|
|
53
|
-
if (typeof val === 'object' && val._type === 'hash') return 'hash';
|
|
53
|
+
if (val !== null && typeof val === 'object' && val._type === 'hash') return 'hash';
|
|
54
54
|
return 'string';
|
|
55
55
|
}
|
|
56
56
|
expire(key, seconds) {
|
|
@@ -85,7 +85,10 @@ class Cache {
|
|
|
85
85
|
hset(key, field, value) {
|
|
86
86
|
const k = String(key);
|
|
87
87
|
let hash = this._getData().get(k);
|
|
88
|
-
if (
|
|
88
|
+
if (hash !== undefined && (typeof hash !== 'object' || hash._type !== 'hash')) {
|
|
89
|
+
throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
90
|
+
}
|
|
91
|
+
if (!hash) { hash = { _type: 'hash' }; this._getData().set(k, hash); }
|
|
89
92
|
const existed = hash[field] !== undefined;
|
|
90
93
|
hash[field] = String(value);
|
|
91
94
|
this._dirty = true;
|
|
@@ -134,7 +137,10 @@ class Cache {
|
|
|
134
137
|
lpush(key, ...values) {
|
|
135
138
|
const k = String(key);
|
|
136
139
|
let list = this._getData().get(k);
|
|
137
|
-
if (
|
|
140
|
+
if (list !== undefined && (!Array.isArray(list) || list._type === 'hash')) {
|
|
141
|
+
throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
142
|
+
}
|
|
143
|
+
if (!list) { list = []; this._getData().set(k, list); }
|
|
138
144
|
list.unshift(...values.map(String).reverse());
|
|
139
145
|
this._dirty = true;
|
|
140
146
|
return list.length;
|
|
@@ -142,7 +148,10 @@ class Cache {
|
|
|
142
148
|
rpush(key, ...values) {
|
|
143
149
|
const k = String(key);
|
|
144
150
|
let list = this._getData().get(k);
|
|
145
|
-
if (
|
|
151
|
+
if (list !== undefined && (!Array.isArray(list) || list._type === 'hash')) {
|
|
152
|
+
throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
153
|
+
}
|
|
154
|
+
if (!list) { list = []; this._getData().set(k, list); }
|
|
146
155
|
list.push(...values.map(String));
|
|
147
156
|
this._dirty = true;
|
|
148
157
|
return list.length;
|
|
@@ -185,7 +194,10 @@ class Cache {
|
|
|
185
194
|
sadd(key, ...members) {
|
|
186
195
|
const k = String(key);
|
|
187
196
|
let set = this._getData().get(k);
|
|
188
|
-
if (
|
|
197
|
+
if (set !== undefined && !(set instanceof Set)) {
|
|
198
|
+
throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
199
|
+
}
|
|
200
|
+
if (!set) { set = new Set(); this._getData().set(k, set); }
|
|
189
201
|
let count = 0;
|
|
190
202
|
for (const m of members) { if (!set.has(String(m))) { set.add(String(m)); count++; } }
|
|
191
203
|
if (count > 0) this._dirty = true;
|
|
@@ -223,10 +235,11 @@ class Cache {
|
|
|
223
235
|
}
|
|
224
236
|
|
|
225
237
|
flushall() {
|
|
238
|
+
const currentDb = this._db;
|
|
226
239
|
for (let i = 0; i < this._dbs.length; i++) { if (this._dbs[i]) this._dbs[i].clear(); if (this._dbsExpires[i]) this._dbsExpires[i].clear(); }
|
|
227
|
-
this._data = this._dbs[
|
|
228
|
-
this._expires = this._dbsExpires[
|
|
229
|
-
this._db =
|
|
240
|
+
this._data = this._dbs[currentDb] = new Map();
|
|
241
|
+
this._expires = this._dbsExpires[currentDb] = new Map();
|
|
242
|
+
this._db = currentDb;
|
|
230
243
|
this._dirty = true;
|
|
231
244
|
return 'OK';
|
|
232
245
|
}
|