jsql-neo 6.4.8 → 6.5.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
@@ -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.4.8** — official release build · [github.com/vexify-org/JSQL-neo](https://github.com/vexify-org/JSQL-neo) · [jsql.vexify.top](https://jsql.vexify.top)
6
+ > **v6.5.0** — 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
  ![Engines](https://img.shields.io/badge/engines-Native%20%7C%20WASM%20%7C%20Pure%20JS-7ee787)
9
9
  ![MySQL](https://img.shields.io/badge/protocol-MySQL%20compatible-1f6feb)
@@ -36,12 +36,18 @@ irm jsql.vexify.top/setup | iex
36
36
  npm install jsql-neo
37
37
  ```
38
38
 
39
- **JSQL-NEO** is a Rust-powered embedded database with **zero runtime dependencies** that speaks MySQL, PostgreSQL, MongoDB, and Redis wire protocols — all on the same engine, same data, one port. It runs in Node.js, CLI, Web UI, and the browser (WASM).
39
+ **JSQL-NEO** is a Rust-powered embedded database with **zero runtime dependencies**. It runs in Node.js, CLI, Web UI, and the browser (WASM).
40
40
 
41
- - **Any client, same data** — MySQL, PostgreSQL, MongoDB, Redis tools connect simultaneously on **one port**
41
+ - **MySQL wire protocol, ready to use** — `jsql serve` starts it; `mysql2` / most MySQL clients connect directly
42
42
  - **Zero dependencies** — no native libs to compile, no system deps to install
43
- - **Runs everywhere** — Node.js native addon, pure JS fallback, WASM for the browser
44
- - **~2× faster than better-sqlite3** — Rust core via N-API
43
+ - **Three engines, same API** — `native` (Rust N-API), `wasm` (same Rust core), `js` (pure JS)
44
+ - **~2× faster than better-sqlite3** on indexed point lookups
45
+
46
+ > **About the other wire protocols** — PostgreSQL, MongoDB and Redis protocol stacks
47
+ > ship as libraries (`lib/pg_server`, `lib/mongo_server`, `lib/redis_server`) and each
48
+ > listens on its own port; there is **no single-port multi-protocol mode**, and the CLI
49
+ > exposes no entry point for them. MongoDB additionally does not yet complete a
50
+ > handshake with the modern official `mongodb` driver. Treat them as experimental.
45
51
 
46
52
  ---
47
53
 
@@ -88,36 +94,35 @@ console.log(rows); // [[1, 'Alice']]
88
94
  db.stop();
89
95
  ```
90
96
 
91
- ### One port, four protocols
97
+ ### Wire protocol server
92
98
 
93
99
  ```bash
94
- npx jsql-neo serve --port 3306
100
+ jsql serve --port 3306 --data-dir ./data
95
101
  ```
96
102
 
97
103
  ```bash
98
- # MySQL mysql -h 127.0.0.1 -P 3306
99
- # PostgreSQL psql -h 127.0.0.1 -p 3306
100
- # MongoDB mongosh mongodb://127.0.0.1:3306
101
- # Redis redis-cli -p 3306
104
+ # MySQL clients (mysql CLI, mysql2, DBeaver, DataGrip …) connect here
105
+ mysql -h 127.0.0.1 -P 3306
102
106
  ```
103
107
 
104
- All four clients connect to the **same database** on the same port.
108
+ Only MySQL is served by the CLI today. The PostgreSQL / Redis / MongoDB stacks are
109
+ available as libraries — see the note near the top of this file.
105
110
 
106
111
  ### Browser / WASM
107
112
 
108
113
  ```js
109
- import init, { Database } from 'jsql-neo/wasm';
114
+ import init, { JSQL } from 'jsql-neo/wasm/browser.mjs';
110
115
  await init();
111
- const db = new Database(':memory:');
112
- // same API as Node.js
116
+ const db = new JSQL();
117
+ // same API as the Node.js native client
113
118
  ```
114
119
 
115
120
  ### 🦀 NativeJSQL
116
121
 
117
122
  Pure Rust native bindings — the highest-performance way to run JSQL-NEO:
118
123
 
119
- - **Zero abstraction overhead** — direct calls into the Rust core
120
- - **Batch operations** — up to **1.54M rows/sec** peak throughput
124
+ - **Low abstraction overhead** — direct calls into the Rust core
125
+ - **Batch operations** — ~700k rows/sec measured on 200k rows (`INSERT` in 5k batches)
121
126
  - **Automatic memory management** with smart caching
122
127
 
123
128
  **Install:**
@@ -129,8 +134,9 @@ npm install jsql-neo
129
134
  **Usage:**
130
135
 
131
136
  ```javascript
132
- import { Database } from 'jsql-neo';
133
- const db = new Database('mydb.jsql');
137
+ const { NativeJSQL } = require('jsql-neo');
138
+ const db = new NativeJSQL(); // :memory: — persist by passing a path
139
+ await db.start();
134
140
  ```
135
141
 
136
142
  ### Storage modes
package/bin/jsql CHANGED
@@ -5,6 +5,10 @@ const yaggs = require('@vexify-org/yaggs');
5
5
  const { ModuleManager } = require('../lib/mod');
6
6
  const Database = require('../lib/database');
7
7
  const migrate = require('../lib/migrate');
8
+ const { nag, setStarred, readConfig } = require('../lib/star-nag');
9
+
10
+ // star 提示:任何异常都不该挡住命令本身
11
+ if (!process.env.JSQL_NO_STAR_NAG) { try { nag(); } catch (e) { /* 提示失败与命令无关 */ } }
8
12
 
9
13
  function printModules(list) {
10
14
  if (list.length === 0) {
@@ -168,6 +172,13 @@ const cli = yaggs({ pkg: require('../package.json') })
168
172
  console.log(`jsql-neo MySQL server on ${options.host}:${options.port}${options.dataDir ? ' (data: ' + options.dataDir + ')' : ' (in-memory)'}`);
169
173
  setInterval(() => {}, 1 << 30);
170
174
  })
175
+ .command('star', 'Show or confirm this repo\'s star status', () => {},
176
+ async () => {
177
+ const c = readConfig();
178
+ console.log('config: ' + require('../lib/star-nag').configPath());
179
+ console.log('marked starred: ' + (c.starred ? 'yes' : 'no'));
180
+ console.log('' + require('../lib/star-nag').URL);
181
+ })
171
182
  .command('bench', 'Insert + query benchmark against a data directory', (sub) => {
172
183
  sub.option('n', { type: 'number', description: 'Number of rows (default 10000)' });
173
184
  sub.option('data-dir', { type: 'string', description: 'Data directory (default :memory:)' });
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * JSQL-NEO v6.4.8 — Rust-Powered Embedded Database (WASM + HTTP)
2
+ * JSQL-NEO v6.5.0 — Rust-Powered Embedded Database (WASM + HTTP)
3
3
  *
4
4
  * @example
5
5
  * const jsql = require('jsql-neo');
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+ /**
3
+ * CLI 启动时的 star 提示。
4
+ *
5
+ * 行为:
6
+ * 1. 已确认 star 过 → 什么都不说
7
+ * 2. 距上次提示不足 N 天 → 什么都不说
8
+ * 3. 否则打印一行提示,并在配置里记下时间
9
+ *
10
+ * star 检测:若环境里有 GitHub token(GITHUB_TOKEN / GH_TOKEN),
11
+ * 就问 GitHub「这个用户 star 过没有」。查到了就记住,以后彻底闭嘴 ——
12
+ * 用户已经 star 过了,再提就是噪音。
13
+ *
14
+ * 配置文件:~/.jsqlmr(JSON)。可用 JSQL_MR_PATH 覆盖。
15
+ *
16
+ * 全程不影响命令本身:任何异常都吞掉,绝不因为提示语让 CLI 起不来。
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const os = require('os');
21
+ const path = require('path');
22
+
23
+ const REPO = 'vexify-org/JSQL-neo';
24
+ const URL = `https://github.com/${REPO}`;
25
+ const NAG_INTERVAL_DAYS = 30;
26
+
27
+ function configPath() {
28
+ return process.env.JSQL_MR_PATH || path.join(os.homedir(), '.jsqlmr');
29
+ }
30
+
31
+ function readConfig() {
32
+ try {
33
+ const raw = fs.readFileSync(configPath(), 'utf8');
34
+ const c = JSON.parse(raw);
35
+ return (c && typeof c === 'object') ? c : {};
36
+ } catch (e) {
37
+ return {}; // 不存在 / 坏了 / 没权限,都当新用户
38
+ }
39
+ }
40
+
41
+ function writeConfig(cfg) {
42
+ try {
43
+ fs.writeFileSync(configPath(), JSON.stringify(cfg, null, 2) + '\n');
44
+ } catch (e) {
45
+ /* 写不了就算了,不该因此报错 */
46
+ }
47
+ }
48
+
49
+ /** 同步问一下 GitHub:这个身份 star 过没有。查不到就返回 null(三态)。 */
50
+ function checkStarred() {
51
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
52
+ if (!token) return null; // 没 token,无法确认
53
+ // 用同步 API:CLI 启动阶段还没有 event loop 可用
54
+ try {
55
+ const { execFileSync } = require('child_process');
56
+ const out = execFileSync('curl', [
57
+ '-sS', '-m', '3',
58
+ '-H', `Authorization: Bearer ${token}`,
59
+ '-H', 'Accept: application/vnd.github+json',
60
+ '-H', 'X-GitHub-Api-Version: 2022-11-28',
61
+ `https://api.github.com/user/starred/${REPO}`,
62
+ ], { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
63
+ if (out.trim() === '') return true; // 200 + 空数组 = 列了但没有这个仓库
64
+ const j = JSON.parse(out);
65
+ return Array.isArray(j) && j.length > 0;
66
+ } catch (e) {
67
+ return null; // 网络不通 / 401 / 没装 curl → 不确定
68
+ }
69
+ }
70
+
71
+ /**
72
+ * @param {object} [opts]
73
+ * @param {NodeJS.WritableStream} [opts.out] 输出流(默认 stdout)
74
+ * @param {boolean} [opts.force] 忽略节流,强制提示一次
75
+ * @returns {boolean} 是否真的打印了
76
+ */
77
+ function nag(opts = {}) {
78
+ const out = opts.out || process.stdout;
79
+ const cfg = readConfig();
80
+
81
+ if (!opts.force && cfg.starred) return false;
82
+
83
+ const last = Number(cfg.lastNagAt) || 0;
84
+ if (!opts.force && last && Date.now() - last < NAG_INTERVAL_DAYS * 86400000) {
85
+ // 顺便有机会补记 star 状态(不打印)
86
+ const s = checkStarred();
87
+ if (s) { cfg.starred = true; writeConfig(cfg); }
88
+ return false;
89
+ }
90
+
91
+ // 有 token 就先查,省得对已经 star 的人重复啰嗦
92
+ const starred = checkStarred();
93
+ if (starred) {
94
+ cfg.starred = true;
95
+ cfg.starredAt = Date.now();
96
+ writeConfig(cfg);
97
+ return false; // 已 star,闭嘴
98
+ }
99
+
100
+ out.write(`\n If JSQL-NEO saved you time, a star helps others find it: ${URL}\n`);
101
+ out.write(` (set JSQL_NO_STAR_NAG=1 to hide, or star it and it'll stop asking)\n\n`);
102
+
103
+ cfg.lastNagAt = Date.now();
104
+ writeConfig(cfg);
105
+ return true;
106
+ }
107
+
108
+ /** 供 `jsql star` 之类的命令用:手动确认 / 取消 */
109
+ function setStarred(v) {
110
+ const cfg = readConfig();
111
+ cfg.starred = !!v;
112
+ if (v) cfg.starredAt = Date.now();
113
+ writeConfig(cfg);
114
+ return cfg.starred;
115
+ }
116
+
117
+ module.exports = { nag, checkStarred, readConfig, setStarred, configPath, REPO, URL };
package/lib/table.js CHANGED
@@ -146,6 +146,20 @@ class Table {
146
146
  this._dateFields = {}; // { fieldName: 'date'|'datetime'|'timestamp' }
147
147
 
148
148
  for (const [field, def] of Object.entries(schema)) {
149
+ // schema 字段名归一化:驼峰 primaryKey/autoIncrement 与下划线
150
+ // primary_key/auto_increment 两种写法都要认。
151
+ //
152
+ // 此前这里只认驼峰,而 native_client 在交给 Rust 之前会归一化
153
+ // (lib/native_client.js 里 primary_key → primaryKey)。结果:
154
+ // README 与官方示例用的下划线写法,在 native 下正常,在纯 JS 下
155
+ // **静默失效** —— 自增 id 不生成,find({id:1}) 永远返回空,
156
+ // 而且不报任何错。这是纯 JS 与 native 的行为分歧。
157
+ if (def && typeof def === 'object' && !Array.isArray(def)) {
158
+ if (def.primary_key !== undefined && def.primaryKey === undefined) def.primaryKey = def.primary_key;
159
+ if (def.auto_increment !== undefined && def.autoIncrement === undefined) def.autoIncrement = def.auto_increment;
160
+ if (def.primary_key !== undefined) delete def.primary_key;
161
+ if (def.auto_increment !== undefined) delete def.auto_increment;
162
+ }
149
163
  const isPk = !!(def.primaryKey || def.primary === true);
150
164
  if (isPk) this._primaryKey = field;
151
165
  if (def.autoIncrement) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsql-neo",
3
- "version": "6.4.8",
3
+ "version": "6.5.0",
4
4
  "description": "JSQL-NEO — Rust-powered embedded database with WASM, REST API, B-Tree indexes, WAL, crash recovery",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -151,6 +151,43 @@ function ok(name, cond, extra) {
151
151
  await db.stop();
152
152
  }
153
153
 
154
+ /* ---- schema 字段名下划线写法:两引擎必须一致(静默正确性 bug)---- */
155
+ /*
156
+ * README 与官方示例用下划线写法(primary_key / auto_increment)。
157
+ * native_client 交给 Rust 前会归一化,纯 JS 的 table.js 此前不认 ——
158
+ * 结果纯 JS 下自增 id 根本不生成、find({id:1}) 永远空,且**不报任何错**。
159
+ */
160
+ {
161
+ const SNAKE = { id: { type: 'integer', primary_key: true, auto_increment: true }, name: { type: 'string' } };
162
+
163
+ // native
164
+ {
165
+ const d = new JSQL();
166
+ await d.start();
167
+ await d.createTable('snk', SNAKE);
168
+ await d.insert('snk', [{ name: 'a' }, { name: 'b' }]);
169
+ await new Promise(r => setTimeout(r, 200));
170
+ const rows = await d.find('snk', {});
171
+ const first = rows.find(r => r.name === 'a');
172
+ ok('schema 下划线写法 · native:自增 id 生成', !!(first && first.id !== undefined), rows);
173
+ const got = await d.findById('snk', first && first.id);
174
+ ok('schema 下划线写法 · native:findById 命中', !!(got && got.name === 'a'), got);
175
+ await d.stop();
176
+ }
177
+
178
+ // pure JS
179
+ {
180
+ const Database = require(path.join(ROOT, 'lib', 'database'));
181
+ const d = new Database(':memory:');
182
+ await d.createTable('snk', SNAKE);
183
+ await d.insert('snk', [{ name: 'a' }, { name: 'b' }]);
184
+ const rows = await d.find('snk', {});
185
+ const first = rows.find(r => r.name === 'a');
186
+ ok('schema 下划线写法 · pure-js:自增 id 生成', !!(first && first.id !== undefined), rows);
187
+ const got = await d.findById('snk', first && first.id);
188
+ ok('schema 下划线写法 · pure-js:findById 命中', !!(got && got.name === 'a'), got);
189
+ }
190
+ }
154
191
  console.log(`\n${passed} passed, ${failed} failed`);
155
192
  process.exit(failed ? 1 : 0);
156
193
  })().catch(e => {