jsql-neo 5.4.0 → 5.4.2

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.
Files changed (72) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +0 -0
  3. package/bin/jsql +0 -0
  4. package/bin/jsql-server +0 -0
  5. package/bin/jsql-server-control.js +0 -0
  6. package/index.d.ts +6 -0
  7. package/index.js +0 -0
  8. package/lib/btree.js +0 -0
  9. package/lib/cache.js +0 -0
  10. package/lib/client.js +0 -0
  11. package/lib/database.js +0 -0
  12. package/lib/date-types.js +0 -0
  13. package/lib/errors.js +0 -0
  14. package/lib/jsql_format.js +0 -0
  15. package/lib/migrate.js +0 -0
  16. package/lib/mod.js +0 -0
  17. package/lib/mongo_server.js +1 -1
  18. package/lib/multiserver.js +1 -1
  19. package/lib/mysql_compat.js +0 -0
  20. package/lib/mysql_server.js +13 -1
  21. package/lib/native_client.js +12 -0
  22. package/lib/nedb_compat.js +0 -0
  23. package/lib/pg_server.js +1 -1
  24. package/lib/plugin.js +0 -0
  25. package/lib/query.js +0 -0
  26. package/lib/redis_server.js +1 -1
  27. package/lib/sql.js +19 -0
  28. package/lib/sqlite_compat.js +0 -0
  29. package/lib/sqlite_worker.js +0 -0
  30. package/lib/table.js +0 -0
  31. package/lib/tui.js +0 -0
  32. package/lib/wasm.js +0 -0
  33. package/lib/wasm_client.js +11 -0
  34. package/lib/web_ui.js +0 -0
  35. package/nativesrc/jsql-neo-core/Cargo.lock +0 -0
  36. package/nativesrc/jsql-neo-core/Cargo.toml +0 -0
  37. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +0 -0
  38. package/nativesrc/jsql-neo-core/src/engine/memory.rs +0 -0
  39. package/nativesrc/jsql-neo-core/src/engine/mod.rs +0 -0
  40. package/nativesrc/jsql-neo-core/src/engine/table.rs +0 -0
  41. package/nativesrc/jsql-neo-core/src/lib.rs +0 -0
  42. package/nativesrc/jsql-neo-core/src/storage/mod.rs +0 -0
  43. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +0 -0
  44. package/nativesrc/jsql-neo-core/src/storage/wal.rs +0 -0
  45. package/nativesrc/jsql-neo-core/src/types.rs +0 -0
  46. package/nativesrc/jsql-neo-native/Cargo.lock +0 -0
  47. package/nativesrc/jsql-neo-native/Cargo.toml +0 -0
  48. package/nativesrc/jsql-neo-native/build.rs +0 -0
  49. package/nativesrc/jsql-neo-native/jsql-neo-native.node +0 -0
  50. package/nativesrc/jsql-neo-native/package.json +0 -0
  51. package/nativesrc/jsql-neo-native/src/lib.rs +0 -0
  52. package/nativesrc/jsql-neo-wasm/Cargo.lock +0 -0
  53. package/nativesrc/jsql-neo-wasm/Cargo.toml +0 -0
  54. package/nativesrc/jsql-neo-wasm/src/lib.rs +0 -0
  55. package/package.json +1 -1
  56. package/postinstall.js +146 -25
  57. package/test/btree.test.js +0 -0
  58. package/test/coverage.js +0 -0
  59. package/test/join.test.js +0 -0
  60. package/test/native.test.js +0 -0
  61. package/test/regress-5.1.0.js +0 -0
  62. package/test/smoke.js +0 -0
  63. package/test/wasm.test.js +0 -0
  64. package/wasm/browser.d.ts +0 -0
  65. package/wasm/browser.mjs +0 -0
  66. package/wasm/browser_bg.mjs +0 -0
  67. package/wasm/jsql_neo_wasm.d.ts +0 -0
  68. package/wasm/jsql_neo_wasm.js +0 -0
  69. package/wasm/jsql_neo_wasm_bg.wasm +0 -0
  70. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +0 -0
  71. package/wasm/package.json +0 -0
  72. package/bin/jsql-neo-server +0 -0
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
File without changes
package/bin/jsql CHANGED
File without changes
package/bin/jsql-server CHANGED
File without changes
File without changes
package/index.d.ts CHANGED
@@ -106,6 +106,12 @@ declare namespace JSQLNeo {
106
106
  hasTable(name: string): Promise<boolean>;
107
107
  getTables(): Promise<string[]>;
108
108
  getTableSchema(name: string): Promise<Schema | null>;
109
+ /**
110
+ * 直接执行原生 SQL(本实例即充当 engine)。
111
+ * @example await db.query("SELECT * FROM users WHERE age > ?", [21])
112
+ * @returns 单条语句返回单个结果对象(含 columns/rows),多条返回数组。
113
+ */
114
+ query(sql: string, paramsOrOpts?: unknown[] | Record<string, unknown>, opts?: Record<string, unknown>): Promise<any>;
109
115
  }
110
116
 
111
117
  export class NativeJSQL extends JSQL {}
package/index.js CHANGED
File without changes
package/lib/btree.js CHANGED
File without changes
package/lib/cache.js CHANGED
File without changes
package/lib/client.js CHANGED
File without changes
package/lib/database.js CHANGED
File without changes
package/lib/date-types.js CHANGED
File without changes
package/lib/errors.js CHANGED
File without changes
File without changes
package/lib/migrate.js CHANGED
File without changes
package/lib/mod.js CHANGED
File without changes
@@ -148,7 +148,7 @@ function bsonReadValue(buf, pos, type, end) {
148
148
  class MongoServer {
149
149
  constructor(options = {}) {
150
150
  this.options = options;
151
- this.port = options.port || 27017;
151
+ this.port = options.port ?? 27017;
152
152
  this.host = options.host || '127.0.0.1';
153
153
  this.dataDir = options.dataDir || null;
154
154
  this._engine = null;
@@ -38,7 +38,7 @@ function sniffProtocol(buf) {
38
38
  class MultiServer {
39
39
  constructor(options = {}) {
40
40
  this.options = options;
41
- this.port = options.port || 5432;
41
+ this.port = options.port ?? 5432;
42
42
  this.host = options.host || '127.0.0.1';
43
43
  this.dataDir = options.dataDir || null;
44
44
  // 共享 SQL 引擎:三种 SQL 协议读写同一份数据
File without changes
@@ -898,7 +898,7 @@ class MysqlConnection {
898
898
  class MysqlServer {
899
899
  constructor(options = {}) {
900
900
  this.options = options;
901
- this.port = options.port || 3306;
901
+ this.port = options.port ?? 3306;
902
902
  this.host = options.host || '127.0.0.1';
903
903
  this.user = options.user || null;
904
904
  this.password = options.password || null;
@@ -1182,6 +1182,18 @@ class MysqlServer {
1182
1182
  socket.on('close', () => this._sockets.delete(socket));
1183
1183
  new MysqlConnection(socket, this);
1184
1184
  });
1185
+ this._server.once('error', err => {
1186
+ if (err && err.code === 'EADDRINUSE') {
1187
+ const friendly = new Error(
1188
+ `MysqlServer 端口 ${this.port} 已被占用(EADDRINUSE)。` +
1189
+ `请换一个空闲端口,或传 port: 0 让系统分配临时端口。`
1190
+ );
1191
+ friendly.code = 'EADDRINUSE';
1192
+ friendly.cause = err;
1193
+ if (cb) cb(friendly);
1194
+ else throw friendly;
1195
+ } else if (cb) cb(err);
1196
+ });
1185
1197
  this._server.listen(this.port, this.host, cb || (() => {}));
1186
1198
  }).catch(err => {
1187
1199
  if (cb) cb(err);
@@ -1,5 +1,6 @@
1
1
  const path = require('path');
2
2
  const os = require('os');
3
+ const { executeSQL } = require('./sql');
3
4
  const native = require(path.join(__dirname, '..', 'native', 'jsql-neo-native.node'));
4
5
  const INT64_TAG = 1;
5
6
  const FLOAT_TAG = 2;
@@ -667,6 +668,17 @@ class JSQL {
667
668
  return this._schemas[name] || null;
668
669
  }
669
670
 
671
+ /**
672
+ * 直接执行原生 SQL 语句。本实例自身即充当 engine,
673
+ * 支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE 等常用语句。
674
+ * @example
675
+ * const rows = await db.query("SELECT * FROM users WHERE age > ?", [21]);
676
+ * await db.query("UPDATE users SET age = age + 1 WHERE name = 'A'");
677
+ */
678
+ async query(sql, paramsOrOpts, opts) {
679
+ return executeSQL(this, sql, paramsOrOpts, opts);
680
+ }
681
+
670
682
  async stop() {
671
683
  this._flushOpsNow();
672
684
  await this._flush();
File without changes
package/lib/pg_server.js CHANGED
@@ -757,7 +757,7 @@ _pgErrorCode(e) {
757
757
  class PgServer {
758
758
  constructor(options = {}) {
759
759
  this.options = options;
760
- this.port = options.port || 5432;
760
+ this.port = options.port ?? 5432;
761
761
  this.host = options.host || '127.0.0.1';
762
762
  this.auth = options.auth || null; // { user: { password, databases: [...] } }
763
763
  this.noAuth = options.noAuth === true;
package/lib/plugin.js CHANGED
File without changes
package/lib/query.js CHANGED
File without changes
@@ -34,7 +34,7 @@ const TYPE_SIGNATURES = {
34
34
 
35
35
  class RedisServer {
36
36
  constructor(opts = {}) {
37
- this.port = opts.port || 6379;
37
+ this.port = opts.port ?? 6379;
38
38
  this.host = opts.host || '127.0.0.1';
39
39
  this.password = opts.password || null;
40
40
  this.dataDir = opts.dataDir || null;
package/lib/sql.js CHANGED
@@ -2870,6 +2870,25 @@ function applyParams(sql, values) {
2870
2870
  }
2871
2871
 
2872
2872
  async function executeSQL(engine, sql, paramsOrOpts, opts = {}) {
2873
+ // 参数守卫:engine 必须是引擎对象,sql 必须是字符串。误用时报清晰错误,
2874
+ // 而不是在后续像 `reading 'length'` 那样抛出令人费解的 TypeError。
2875
+ if (typeof engine !== 'object' || engine === null) {
2876
+ if (typeof engine === 'string') {
2877
+ throw new Error(
2878
+ `executeSQL(engine, sql, ...) 参数错误:第一个参数不能是 SQL 字符串,必须传 engine 引擎对象。\n` +
2879
+ `正确用法:await executeSQL(db, '${engine.slice(0, 40)}…'); // db 是含 hasTable/find/getTableSchema 的对象`
2880
+ );
2881
+ }
2882
+ throw new Error(
2883
+ `executeSQL(engine, sql, ...) 参数错误:缺少第一个参数 engine。\n` +
2884
+ `正确用法:await executeSQL(db, 'SELECT 1'); // db 需是含 hasTable/find/getTableSchema 的引擎对象`
2885
+ );
2886
+ }
2887
+ if (typeof sql !== 'string' || sql.length === 0) {
2888
+ throw new Error(
2889
+ `executeSQL(engine, sql, ...) 参数错误:第二个参数 sql 必须是有效的 SQL 字符串`
2890
+ );
2891
+ }
2873
2892
  if (Array.isArray(paramsOrOpts)) {
2874
2893
  sql = applyParams(sql, paramsOrOpts);
2875
2894
  } else if (paramsOrOpts && typeof paramsOrOpts === 'object') {
File without changes
File without changes
package/lib/table.js CHANGED
File without changes
package/lib/tui.js CHANGED
File without changes
package/lib/wasm.js CHANGED
File without changes
@@ -1,4 +1,5 @@
1
1
  const path = require('path');
2
+ const { executeSQL } = require('./sql');
2
3
  const wasmBindings = require(path.join(__dirname, '..', 'wasm', 'jsql_neo_wasm.js'));
3
4
 
4
5
  function safeJsonParse(str) {
@@ -697,6 +698,16 @@ class JSQL {
697
698
  return this._schemas[name] || null;
698
699
  }
699
700
 
701
+ /**
702
+ * 直接执行原生 SQL 语句。本实例自身即充当 engine,
703
+ * 支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE 等常用语句。
704
+ * @example
705
+ * const rows = await db.query("SELECT * FROM users WHERE age > ?", [21]);
706
+ */
707
+ async query(sql, paramsOrOpts, opts) {
708
+ return executeSQL(this, sql, paramsOrOpts, opts);
709
+ }
710
+
700
711
  async stop() {
701
712
  await this._flush();
702
713
  this._runHooks('onStop', []);
package/lib/web_ui.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsql-neo",
3
- "version": "5.4.0",
3
+ "version": "5.4.2",
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",
package/postinstall.js CHANGED
@@ -1,38 +1,159 @@
1
1
  const { execSync } = require('child_process');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
+ const http = require('http');
5
+ const https = require('https');
4
6
 
7
+ const ROOT = __dirname;
8
+ const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
9
+
10
+ // Server CLI binary (bin/jsql-neo-server)
5
11
  const BIN_NAME = process.platform === 'win32' ? 'jsql-neo-server.exe' : 'jsql-neo-server';
6
- const BP = path.join(__dirname, 'bin', BIN_NAME);
12
+ const BP = path.join(ROOT, 'bin', BIN_NAME);
7
13
 
8
- function main() {
9
- if (fs.existsSync(BP)) {
10
- fs.chmodSync(BP, 0o755);
11
- console.log(`[jsql-neo] binary ready: ${BP}`);
14
+ // Native N-API module (native/jsql-neo-native.node)
15
+ const NATIVE_PATH = path.join(ROOT, 'native', 'jsql-neo-native.node');
16
+
17
+ // GitHub 加速代理前缀;把原始 github 链接直接拼在后面即可
18
+ const GHPROXY = 'https://gh-proxy.com/';
19
+ const TAG = `v${pkg.version}`;
20
+
21
+ // 从 repository 字段提取 owner/repo,例如 vexify-org/JSQL-neo
22
+ const REPO = (pkg.repository && pkg.repository.url)
23
+ ? pkg.repository.url.replace(/\.git$/, '').replace(/^.*github\.com\//i, '')
24
+ : 'vexify-org/JSQL-neo';
25
+
26
+ // 与 .github/workflows/build-native.yml 中发布的资产命名保持一致
27
+ function nativeTargetSuffix(platform = process.platform, arch = process.arch) {
28
+ const p = platform === 'win32' ? 'win32' : platform === 'darwin' ? 'darwin' : 'linux';
29
+ const a = arch === 'x64' ? 'x64' : arch === 'arm64' ? 'arm64' : arch;
30
+ if (p === 'linux') return `${p}-${a}-gnu`;
31
+ if (p === 'darwin') return `${p}-${a}`;
32
+ return `${p}-${a}-msvc`;
33
+ }
34
+
35
+ function download(url, dest) {
36
+ return new Promise((resolve, reject) => {
37
+ const mod = url.startsWith('https:') ? https : http;
38
+ const req = mod.get(url, (res) => {
39
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
40
+ res.resume();
41
+ download(res.headers.location, dest).then(resolve, reject);
42
+ return;
43
+ }
44
+ if (res.statusCode !== 200) {
45
+ res.resume();
46
+ reject(new Error(`HTTP ${res.statusCode} for ${url}`));
12
47
  return;
48
+ }
49
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
50
+ const ws = fs.createWriteStream(dest);
51
+ ws.on('error', reject);
52
+ res.pipe(ws);
53
+ res.on('end', () => ws.end(() => resolve()));
54
+ });
55
+ req.on('error', reject);
56
+ });
57
+ }
58
+
59
+ function buildNativeFromSource() {
60
+ const crate = path.join(ROOT, 'nativesrc', 'jsql-neo-native');
61
+ if (!fs.existsSync(path.join(crate, 'Cargo.toml'))) return;
62
+ console.log('[jsql-neo] building native module from source...');
63
+ try {
64
+ execSync('cargo build --release', { cwd: crate, stdio: 'inherit' });
65
+ const rel = path.join(crate, 'target', 'release');
66
+ const src = process.platform === 'win32'
67
+ ? path.join(rel, 'jsql_neo_native.dll')
68
+ : process.platform === 'darwin'
69
+ ? path.join(rel, 'libjsql_neo_native.dylib')
70
+ : path.join(rel, 'libjsql_neo_native.so');
71
+ if (fs.existsSync(src)) {
72
+ fs.mkdirSync(path.dirname(NATIVE_PATH), { recursive: true });
73
+ fs.copyFileSync(src, NATIVE_PATH);
74
+ fs.chmodSync(NATIVE_PATH, 0o755);
75
+ console.log(`[jsql-neo] native module built: native/jsql-neo-native.node`);
13
76
  }
77
+ } catch (e) {
78
+ console.warn('[jsql-neo] native build failed:', e.message);
79
+ }
80
+ }
14
81
 
15
- // Build from source
16
- const serverDir = path.join(__dirname, '..', 'jsql-neo-server');
17
- if (fs.existsSync(path.join(serverDir, 'Cargo.toml'))) {
18
- console.log('[jsql-neo] building Rust server from source...');
19
- try {
20
- execSync('cargo build --release', { cwd: serverDir, stdio: 'inherit' });
21
- const src = path.join(serverDir, 'target', 'release', BIN_NAME);
22
- if (fs.existsSync(src)) {
23
- fs.mkdirSync(path.join(__dirname, 'bin'), { recursive: true });
24
- fs.copyFileSync(src, BP);
25
- fs.chmodSync(BP, 0o755);
26
- console.log(`[jsql-neo] binary built: ${BP}`);
27
- return;
28
- }
29
- } catch (e) {
30
- console.warn('[jsql-neo] cargo build failed:', e.message);
31
- }
82
+ // 尝试加载 native 模块;能成功 require 才算「可用」,否则视为不可用
83
+ function tryLoadNative() {
84
+ if (!fs.existsSync(NATIVE_PATH)) return false;
85
+ try {
86
+ require(NATIVE_PATH);
87
+ fs.chmodSync(NATIVE_PATH, 0o755);
88
+ return true;
89
+ } catch (e) {
90
+ console.warn(`[jsql-neo] existing native module cannot be loaded (${e.code || e.message}), will re-acquire.`);
91
+ try { fs.unlinkSync(NATIVE_PATH); } catch (_) { /* ignore */ }
92
+ return false;
93
+ }
94
+ }
95
+
96
+ async function downloadAndVerify() {
97
+ const asset = `jsql-neo-native.${nativeTargetSuffix()}.node`;
98
+ const url = `${GHPROXY}https://github.com/${REPO}/releases/download/${TAG}/${asset}`;
99
+ console.log(`[jsql-neo] downloading native module: ${url}`);
100
+ try {
101
+ await download(url, NATIVE_PATH);
102
+ if (!tryLoadNative()) {
103
+ console.warn('[jsql-neo] downloaded module also invalid, removed.');
104
+ return false;
32
105
  }
106
+ console.log('[jsql-neo] native module downloaded: native/jsql-neo-native.node');
107
+ return true;
108
+ } catch (e) {
109
+ console.warn('[jsql-neo] download failed, falling back to source build:', e.message);
110
+ return false;
111
+ }
112
+ }
113
+
114
+ async function ensureNativeNode() {
115
+ // 已存在且能加载 → 直接用;否则删掉并从 gh-proxy.com 下载,找不到才回退源码编译
116
+ if (tryLoadNative()) {
117
+ console.log('[jsql-neo] native module ready: native/jsql-neo-native.node');
118
+ return;
119
+ }
120
+ if (await downloadAndVerify()) return;
121
+ buildNativeFromSource();
122
+ }
123
+
124
+ function ensureServerBinary() {
125
+ if (fs.existsSync(BP)) {
126
+ fs.chmodSync(BP, 0o755);
127
+ console.log(`[jsql-neo] binary ready: ${BP}`);
128
+ return;
129
+ }
130
+
131
+ // Build from source
132
+ const serverDir = path.join(ROOT, '..', 'jsql-neo-server');
133
+ if (fs.existsSync(path.join(serverDir, 'Cargo.toml'))) {
134
+ console.log('[jsql-neo] building Rust server from source...');
135
+ try {
136
+ execSync('cargo build --release', { cwd: serverDir, stdio: 'inherit' });
137
+ const src = path.join(serverDir, 'target', 'release', BIN_NAME);
138
+ if (fs.existsSync(src)) {
139
+ fs.mkdirSync(path.join(ROOT, 'bin'), { recursive: true });
140
+ fs.copyFileSync(src, BP);
141
+ fs.chmodSync(BP, 0o755);
142
+ console.log(`[jsql-neo] binary built: ${BP}`);
143
+ return;
144
+ }
145
+ } catch (e) {
146
+ console.warn('[jsql-neo] cargo build failed:', e.message);
147
+ }
148
+ }
149
+
150
+ console.warn(`[jsql-neo] server binary not found at ${BP}`);
151
+ console.warn('[jsql-neo] install Rust from https://rustup.rs and run: npm run build');
152
+ }
33
153
 
34
- console.warn(`[jsql-neo] binary not found at ${BP}`);
35
- console.warn('[jsql-neo] install Rust from https://rustup.rs and run: npm run build');
154
+ async function main() {
155
+ await ensureNativeNode();
156
+ ensureServerBinary();
36
157
  }
37
158
 
38
- main();
159
+ main();
File without changes
package/test/coverage.js CHANGED
File without changes
package/test/join.test.js CHANGED
File without changes
File without changes
File without changes
package/test/smoke.js CHANGED
File without changes
package/test/wasm.test.js CHANGED
File without changes
package/wasm/browser.d.ts CHANGED
File without changes
package/wasm/browser.mjs CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/wasm/package.json CHANGED
File without changes
Binary file