jsql-neo 5.4.2 → 5.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.
Files changed (72) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8097 -7971
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +385 -357
  7. package/index.js +124 -118
  8. package/lib/ast.js +214 -0
  9. package/lib/btree.js +413 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1744 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +732 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +34 -34
  26. package/lib/query.js +815 -815
  27. package/lib/redis_server.js +571 -571
  28. package/lib/sql.js +3460 -2925
  29. package/lib/sqlite_compat.js +323 -323
  30. package/lib/sqlite_worker.js +264 -264
  31. package/lib/table.js +1304 -1304
  32. package/lib/tui.js +502 -502
  33. package/lib/wasm.js +1 -1
  34. package/lib/wasm_client.js +718 -718
  35. package/lib/web_ui.js +257 -257
  36. package/native/jsql-neo-native.node +0 -0
  37. package/nativesrc/jsql-neo-core/Cargo.lock +455 -455
  38. package/nativesrc/jsql-neo-core/Cargo.toml +24 -24
  39. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +455 -455
  40. package/nativesrc/jsql-neo-core/src/engine/memory.rs +168 -168
  41. package/nativesrc/jsql-neo-core/src/engine/mod.rs +62 -62
  42. package/nativesrc/jsql-neo-core/src/engine/table.rs +700 -700
  43. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  44. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  45. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  46. package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -85
  47. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  48. package/nativesrc/jsql-neo-native/Cargo.lock +606 -606
  49. package/nativesrc/jsql-neo-native/Cargo.toml +16 -16
  50. package/nativesrc/jsql-neo-native/build.rs +3 -3
  51. package/nativesrc/jsql-neo-native/package.json +7 -7
  52. package/nativesrc/jsql-neo-native/src/lib.rs +269 -269
  53. package/nativesrc/jsql-neo-wasm/Cargo.lock +465 -465
  54. package/nativesrc/jsql-neo-wasm/Cargo.toml +15 -15
  55. package/nativesrc/jsql-neo-wasm/src/lib.rs +447 -447
  56. package/package.json +87 -86
  57. package/postinstall.js +158 -158
  58. package/test/btree.test.js +128 -128
  59. package/test/coverage.js +187 -187
  60. package/test/join.test.js +110 -110
  61. package/test/native.test.js +158 -158
  62. package/test/regress-5.1.0.js +271 -271
  63. package/test/smoke.js +58 -58
  64. package/test/sql-parser.test.js +273 -0
  65. package/test/wasm.test.js +110 -110
  66. package/wasm/browser.d.ts +89 -89
  67. package/wasm/browser.mjs +569 -569
  68. package/wasm/browser_bg.mjs +597 -597
  69. package/wasm/jsql_neo_wasm.d.ts +38 -38
  70. package/wasm/jsql_neo_wasm.js +520 -520
  71. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  72. package/wasm/package.json +11 -11
@@ -1,141 +1,141 @@
1
- /*
2
- * Multi-protocol multiplexing server for JSQL-NEO.
3
- *
4
- * Listens on ONE port and sniffs the first bytes of each connection to route
5
- * to the correct wire-protocol handler:
6
- *
7
- * - PostgreSQL : first byte 0x00 (big-endian Int32 length, length < 2^24)
8
- * - MySQL : first byte 0x0a/0x0d (protocol handshake version / cap flag)
9
- * - Redis : anything else (ASCII command or RESP prefix)
10
- *
11
- * This lets every npm database client (mysql2, pg, ioredis, sequelize, knex,
12
- * typeorm, redis-cli, psql, phpMyAdmin ...) connect to the same endpoint.
13
- */
14
- const net = require('net');
15
- const path = require('path');
16
- const Database = require('./database');
17
- const { MysqlServer, MysqlConnection } = require('./mysql_server');
18
- const { PgServer, PgConnection } = require('./pg_server');
19
- const { RedisServer } = require('./redis_server');
20
- const { MongoServer } = require('./mongo_server');
21
-
22
- function sniffProtocol(buf) {
23
- if (buf.length < 1) return null;
24
- const b = buf[0];
25
- if (b === 0x00) return 'pg';
26
- if (b === 0x0a || b === 0x0d) return 'mysql';
27
- // MongoDB: header 前 4 字节 = int32LE 长度,第 13 字节 = opCode(OP_QUERY 2004 / OP_COMPRESSED 2012 / OP_MSG 2013)
28
- if (buf.length >= 16) {
29
- const opc = buf.readInt32LE(12);
30
- if (opc === 2012 || opc === 2013 || opc === 2004) {
31
- const len = buf.readInt32LE(0);
32
- if (len >= 16 && len <= 48 * 1024 * 1024) return 'mongo';
33
- }
34
- }
35
- return 'redis';
36
- }
37
-
38
- class MultiServer {
39
- constructor(options = {}) {
40
- this.options = options;
41
- this.port = options.port ?? 5432;
42
- this.host = options.host || '127.0.0.1';
43
- this.dataDir = options.dataDir || null;
44
- // 共享 SQL 引擎:三种 SQL 协议读写同一份数据
45
- this._engines = new Map();
46
- const getEngine = async (dbName) => {
47
- const key = dbName || 'default';
48
- if (this._engines.has(key)) return this._engines.get(key);
49
- let engine;
50
- if (this.dataDir && this.dataDir !== ':memory:') {
51
- engine = new Database(path.join(this.dataDir, key), { autoSave: true });
52
- } else {
53
- engine = new Database(':memory:', { autoSave: false });
54
- }
55
- this._engines.set(key, engine);
56
- return engine;
57
- };
58
- // 三个底层服务器共享同一个数据目录(不各自 listen)
59
- const baseOpts = { ...options, port: 0, host: this.host };
60
- this.mysql = new MysqlServer(baseOpts);
61
- this.mysql._getEngine = getEngine;
62
- this.pg = new PgServer(baseOpts);
63
- this.pg._getEngine = getEngine;
64
- this.redis = new RedisServer({ ...baseOpts });
65
- this.mongo = new MongoServer(baseOpts);
66
- this.mongo._getEngine = getEngine;
67
- this._server = null;
68
- this._sockets = new Set();
69
- }
70
-
71
- listen(cb) {
72
- this._server = net.createServer((socket) => {
73
- this._sockets.add(socket);
74
- socket.on('close', () => this._sockets.delete(socket));
75
- let first = Buffer.alloc(0);
76
- let routed = false;
77
- // MySQL 客户端会先等服务器握手包,不主动发字节;超时后按 MySQL 处理
78
- const t = setTimeout(() => {
79
- if (!routed) { routed = true; socket.removeListener('data', sniff); this._route(socket, first); }
80
- }, 200);
81
- t.unref();
82
- const sniff = (chunk) => {
83
- first = Buffer.concat([first, chunk]);
84
- if (routed) return;
85
- if (first.length < 4) return; // 等至少 4 字节再判断
86
- routed = true;
87
- clearTimeout(t);
88
- socket.removeListener('data', sniff);
89
- try {
90
- this._route(socket, first);
91
- } catch (e) {
92
- socket.destroy();
93
- }
94
- };
95
- socket.on('data', sniff);
96
- });
97
- this._server.listen(this.port, this.host, cb || (() => {}));
98
- this._server.on('error', (err) => {
99
- if (this.options.onError) this.options.onError(err);
100
- else throw err;
101
- });
102
- return this;
103
- }
104
-
105
- _route(socket, first) {
106
- const proto = sniffProtocol(first) || 'mysql'; // 无字节(MySQL 客户端等握手)→ 按 MySQL 处理
107
- if (proto === 'mysql') {
108
- const conn = new MysqlConnection(socket, this.mysql);
109
- conn._onData(first);
110
- } else if (proto === 'pg') {
111
- const conn = new PgConnection(socket, this.pg);
112
- conn._onData(first);
113
- } else if (proto === 'mongo') {
114
- this.mongo._handleSocket(socket, first);
115
- } else {
116
- this.redis._handleSocket(socket, first.toString('utf8'));
117
- }
118
- }
119
-
120
- get address() {
121
- return this._server ? this._server.address() : null;
122
- }
123
-
124
- close(cb) {
125
- for (const s of this._sockets) s.destroy();
126
- const stops = [];
127
- for (const engine of this._engines.values()) {
128
- if (engine && typeof engine.stop === 'function') stops.push(engine.stop());
129
- }
130
- const done = () => { if (this._server) { this._server.close(cb || (() => {})); this._server = null; } else if (cb) cb(); };
131
- if (stops.length > 0) Promise.allSettled(stops).then(done);
132
- else done();
133
- return this;
134
- }
135
- }
136
-
137
- function createMultiServer(options) {
138
- return new MultiServer(options || {});
139
- }
140
-
1
+ /*
2
+ * Multi-protocol multiplexing server for JSQL-NEO.
3
+ *
4
+ * Listens on ONE port and sniffs the first bytes of each connection to route
5
+ * to the correct wire-protocol handler:
6
+ *
7
+ * - PostgreSQL : first byte 0x00 (big-endian Int32 length, length < 2^24)
8
+ * - MySQL : first byte 0x0a/0x0d (protocol handshake version / cap flag)
9
+ * - Redis : anything else (ASCII command or RESP prefix)
10
+ *
11
+ * This lets every npm database client (mysql2, pg, ioredis, sequelize, knex,
12
+ * typeorm, redis-cli, psql, phpMyAdmin ...) connect to the same endpoint.
13
+ */
14
+ const net = require('net');
15
+ const path = require('path');
16
+ const Database = require('./database');
17
+ const { MysqlServer, MysqlConnection } = require('./mysql_server');
18
+ const { PgServer, PgConnection } = require('./pg_server');
19
+ const { RedisServer } = require('./redis_server');
20
+ const { MongoServer } = require('./mongo_server');
21
+
22
+ function sniffProtocol(buf) {
23
+ if (buf.length < 1) return null;
24
+ const b = buf[0];
25
+ if (b === 0x00) return 'pg';
26
+ if (b === 0x0a || b === 0x0d) return 'mysql';
27
+ // MongoDB: header 前 4 字节 = int32LE 长度,第 13 字节 = opCode(OP_QUERY 2004 / OP_COMPRESSED 2012 / OP_MSG 2013)
28
+ if (buf.length >= 16) {
29
+ const opc = buf.readInt32LE(12);
30
+ if (opc === 2012 || opc === 2013 || opc === 2004) {
31
+ const len = buf.readInt32LE(0);
32
+ if (len >= 16 && len <= 48 * 1024 * 1024) return 'mongo';
33
+ }
34
+ }
35
+ return 'redis';
36
+ }
37
+
38
+ class MultiServer {
39
+ constructor(options = {}) {
40
+ this.options = options;
41
+ this.port = options.port ?? 5432;
42
+ this.host = options.host || '127.0.0.1';
43
+ this.dataDir = options.dataDir || null;
44
+ // 共享 SQL 引擎:三种 SQL 协议读写同一份数据
45
+ this._engines = new Map();
46
+ const getEngine = async (dbName) => {
47
+ const key = dbName || 'default';
48
+ if (this._engines.has(key)) return this._engines.get(key);
49
+ let engine;
50
+ if (this.dataDir && this.dataDir !== ':memory:') {
51
+ engine = new Database(path.join(this.dataDir, key), { autoSave: true });
52
+ } else {
53
+ engine = new Database(':memory:', { autoSave: false });
54
+ }
55
+ this._engines.set(key, engine);
56
+ return engine;
57
+ };
58
+ // 三个底层服务器共享同一个数据目录(不各自 listen)
59
+ const baseOpts = { ...options, port: 0, host: this.host };
60
+ this.mysql = new MysqlServer(baseOpts);
61
+ this.mysql._getEngine = getEngine;
62
+ this.pg = new PgServer(baseOpts);
63
+ this.pg._getEngine = getEngine;
64
+ this.redis = new RedisServer({ ...baseOpts });
65
+ this.mongo = new MongoServer(baseOpts);
66
+ this.mongo._getEngine = getEngine;
67
+ this._server = null;
68
+ this._sockets = new Set();
69
+ }
70
+
71
+ listen(cb) {
72
+ this._server = net.createServer((socket) => {
73
+ this._sockets.add(socket);
74
+ socket.on('close', () => this._sockets.delete(socket));
75
+ let first = Buffer.alloc(0);
76
+ let routed = false;
77
+ // MySQL 客户端会先等服务器握手包,不主动发字节;超时后按 MySQL 处理
78
+ const t = setTimeout(() => {
79
+ if (!routed) { routed = true; socket.removeListener('data', sniff); this._route(socket, first); }
80
+ }, 200);
81
+ t.unref();
82
+ const sniff = (chunk) => {
83
+ first = Buffer.concat([first, chunk]);
84
+ if (routed) return;
85
+ if (first.length < 4) return; // 等至少 4 字节再判断
86
+ routed = true;
87
+ clearTimeout(t);
88
+ socket.removeListener('data', sniff);
89
+ try {
90
+ this._route(socket, first);
91
+ } catch (e) {
92
+ socket.destroy();
93
+ }
94
+ };
95
+ socket.on('data', sniff);
96
+ });
97
+ this._server.listen(this.port, this.host, cb || (() => {}));
98
+ this._server.on('error', (err) => {
99
+ if (this.options.onError) this.options.onError(err);
100
+ else throw err;
101
+ });
102
+ return this;
103
+ }
104
+
105
+ _route(socket, first) {
106
+ const proto = sniffProtocol(first) || 'mysql'; // 无字节(MySQL 客户端等握手)→ 按 MySQL 处理
107
+ if (proto === 'mysql') {
108
+ const conn = new MysqlConnection(socket, this.mysql);
109
+ conn._onData(first);
110
+ } else if (proto === 'pg') {
111
+ const conn = new PgConnection(socket, this.pg);
112
+ conn._onData(first);
113
+ } else if (proto === 'mongo') {
114
+ this.mongo._handleSocket(socket, first);
115
+ } else {
116
+ this.redis._handleSocket(socket, first.toString('utf8'));
117
+ }
118
+ }
119
+
120
+ get address() {
121
+ return this._server ? this._server.address() : null;
122
+ }
123
+
124
+ close(cb) {
125
+ for (const s of this._sockets) s.destroy();
126
+ const stops = [];
127
+ for (const engine of this._engines.values()) {
128
+ if (engine && typeof engine.stop === 'function') stops.push(engine.stop());
129
+ }
130
+ const done = () => { if (this._server) { this._server.close(cb || (() => {})); this._server = null; } else if (cb) cb(); };
131
+ if (stops.length > 0) Promise.allSettled(stops).then(done);
132
+ else done();
133
+ return this;
134
+ }
135
+ }
136
+
137
+ function createMultiServer(options) {
138
+ return new MultiServer(options || {});
139
+ }
140
+
141
141
  module.exports = { MultiServer, createMultiServer, sniffProtocol };