jsql-neo 3.5.2 → 4.0.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 (36) hide show
  1. package/LICENSE +202 -0
  2. package/bin/jsql +97 -0
  3. package/index.js +20 -0
  4. package/lib/database.js +484 -28
  5. package/lib/mod.js +316 -0
  6. package/lib/mysql_compat.js +232 -0
  7. package/lib/mysql_server.js +514 -0
  8. package/lib/native_client.js +470 -0
  9. package/lib/nedb_compat.js +506 -0
  10. package/lib/plugin.js +35 -0
  11. package/lib/sql.js +1160 -0
  12. package/lib/table.js +42 -21
  13. package/lib/wasm_client.js +176 -8
  14. package/native/jsql-neo-native.node +0 -0
  15. package/nativesrc/jsql-neo-core/Cargo.toml +24 -0
  16. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +449 -0
  17. package/nativesrc/jsql-neo-core/src/engine/memory.rs +147 -0
  18. package/nativesrc/jsql-neo-core/src/engine/mod.rs +41 -0
  19. package/nativesrc/jsql-neo-core/src/engine/table.rs +664 -0
  20. package/nativesrc/jsql-neo-core/src/lib.rs +3 -0
  21. package/nativesrc/jsql-neo-core/src/storage/mod.rs +1 -0
  22. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +2 -0
  23. package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -0
  24. package/nativesrc/jsql-neo-core/src/types.rs +94 -0
  25. package/nativesrc/jsql-neo-native/Cargo.lock +606 -0
  26. package/nativesrc/jsql-neo-native/Cargo.toml +16 -0
  27. package/nativesrc/jsql-neo-native/jsql-neo-native.node +0 -0
  28. package/nativesrc/jsql-neo-native/package.json +7 -0
  29. package/nativesrc/jsql-neo-native/src/lib.rs +281 -0
  30. package/package.json +9 -1
  31. package/wasm/jsql_neo_wasm.d.ts +8 -0
  32. package/wasm/jsql_neo_wasm.js +455 -6
  33. package/wasm/jsql_neo_wasm_bg.js +22 -0
  34. package/wasm/jsql_neo_wasm_bg.wasm +0 -0
  35. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +4 -0
  36. package/wasm/package.json +1 -8
@@ -0,0 +1,514 @@
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * MySQL 协议服务端 — TCP 3306
4
+ * 实现 MySQL 握手/认证/命令循环,任何 mysql/mysql2 客户端可直接连接。
5
+ */
6
+
7
+ const net = require('net');
8
+ const crypto = require('crypto');
9
+ const { executeSQL } = require('./sql');
10
+ const Database = require('./database');
11
+
12
+ const SERVER_VERSION = '8.0.0-jsql-neo';
13
+ const CLIENT_PROTOCOL_41 = 0x00000001 << 9;
14
+ const CLIENT_SECURE_CONNECTION = 0x00008000;
15
+ const CLIENT_PLUGIN_AUTH = 0x00080000;
16
+ const CLIENT_CONNECT_WITH_DB = 0x00000008;
17
+ const CLIENT_LONG_PASSWORD = 0x00000001;
18
+ const CLIENT_TRANSACTIONS = 0x00002000;
19
+ const CLIENT_MULTI_STATEMENTS = 0x00010000;
20
+ const CLIENT_MULTI_RESULTS = 0x00020000;
21
+ const CLIENT_LONG_FLAG = 0x00000004;
22
+ const CLIENT_DEPRECATE_EOF = 0x01000000;
23
+ const SERVER_STATUS_AUTOCOMMIT = 0x0002;
24
+ const CHARSET_UTF8 = 0x21;
25
+
26
+ const MYSQL_TYPE_TINY = 1, MYSQL_TYPE_LONG = 3, MYSQL_TYPE_LONGLONG = 8,
27
+ MYSQL_TYPE_DATE = 10, MYSQL_TYPE_DATETIME = 12, MYSQL_TYPE_DOUBLE = 5,
28
+ MYSQL_TYPE_STRING = 254, MYSQL_TYPE_VAR_STRING = 253, MYSQL_TYPE_BLOB = 252,
29
+ MYSQL_TYPE_JSON = 245, MYSQL_TYPE_NULL = 6;
30
+
31
+ function encodeLenenc(value) {
32
+ if (value === null) return Buffer.from([0xfb]);
33
+ if (value < 0xfb) return Buffer.from([value]);
34
+ if (value <= 0xffff) {
35
+ const b = Buffer.alloc(3);
36
+ b[0] = 0xfc; b.writeUInt16LE(value, 1);
37
+ return b;
38
+ }
39
+ if (value <= 0xffffff) {
40
+ const b = Buffer.alloc(4);
41
+ b[0] = 0xfd; b.writeUIntLE(value, 1, 3);
42
+ return b;
43
+ }
44
+ const b = Buffer.alloc(9);
45
+ b[0] = 0xfe; b.writeUInt32LE(value, 1); b.writeUInt32LE(Math.floor(value / 4294967296), 5);
46
+ return b;
47
+ }
48
+
49
+ function encodeLenencString(str) {
50
+ const buf = Buffer.from(String(str), 'utf8');
51
+ return Buffer.concat([encodeLenenc(buf.length), buf]);
52
+ }
53
+
54
+ function parseLenenc(buf, offset) {
55
+ const first = buf[offset];
56
+ if (first < 0xfb) return { value: first, size: 1 };
57
+ if (first === 0xfb) return { value: null, size: 1 };
58
+ if (first === 0xfc) return { value: buf.readUInt16LE(offset + 1), size: 3 };
59
+ if (first === 0xfd) return { value: buf.readUIntLE(offset + 1, 3), size: 4 };
60
+ return { value: buf.readUInt32LE(offset + 1), size: 5 };
61
+ }
62
+
63
+ class PacketBuilder {
64
+ constructor() {
65
+ this.bufs = [];
66
+ }
67
+ byte(v) { this.bufs.push(Buffer.from([v & 0xff])); return this; }
68
+ int16(v) { const b = Buffer.alloc(2); b.writeUInt16LE(v & 0xffff); this.bufs.push(b); return this; }
69
+ int32(v) { const b = Buffer.alloc(4); b.writeUInt32LE(v >>> 0); this.bufs.push(b); return this; }
70
+ raw(buf) { this.bufs.push(buf); return this; }
71
+ string(str) { this.bufs.push(Buffer.from(str, 'utf8')); return this; }
72
+ nul(str) { this.bufs.push(Buffer.concat([Buffer.from(String(str), 'utf8'), Buffer.from([0])])); return this; }
73
+ bytes(list) { this.bufs.push(Buffer.from(list)); return this; }
74
+ build() { return Buffer.concat(this.bufs); }
75
+ }
76
+
77
+ function handshakePacket(connectionId, seed) {
78
+ const b = new PacketBuilder();
79
+ b.byte(10);
80
+ b.nul(SERVER_VERSION);
81
+ b.int32(connectionId);
82
+ b.bytes(seed.slice(0, 8));
83
+ b.byte(0);
84
+ const caps = CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_CONNECT_WITH_DB | CLIENT_LONG_PASSWORD | CLIENT_TRANSACTIONS | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS | CLIENT_LONG_FLAG;
85
+ b.int16(caps & 0xffff);
86
+ b.byte(CHARSET_UTF8);
87
+ b.int16(SERVER_STATUS_AUTOCOMMIT);
88
+ b.int16((caps >>> 16) & 0xffff);
89
+ b.byte(21);
90
+ b.bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
91
+ b.bytes(seed.slice(8, 20));
92
+ b.byte(0);
93
+ b.nul('mysql_native_password');
94
+ return b.build();
95
+ }
96
+
97
+ function okPacket(affectedRows = 0, insertId = 0, status = SERVER_STATUS_AUTOCOMMIT) {
98
+ const b = new PacketBuilder();
99
+ b.byte(0x00);
100
+ b.raw(encodeLenenc(affectedRows));
101
+ b.raw(encodeLenenc(insertId));
102
+ b.int16(status);
103
+ b.int16(0);
104
+ return b.build();
105
+ }
106
+
107
+ function eofPacket() {
108
+ const b = new PacketBuilder();
109
+ b.byte(0xfe);
110
+ b.int16(0);
111
+ b.int16(SERVER_STATUS_AUTOCOMMIT);
112
+ return b.build();
113
+ }
114
+
115
+ function errPacket(errno, message, sqlState = 'HY000') {
116
+ const b = new PacketBuilder();
117
+ b.byte(0xff);
118
+ b.int16(errno);
119
+ b.byte(0x23);
120
+ b.string(sqlState);
121
+ b.string(String(message).slice(0, 200));
122
+ return b.build();
123
+ }
124
+
125
+ function toMysqlErrno(e) {
126
+ if (e && typeof e.code === 'number') return e.code;
127
+ const m = e && e.message ? String(e.message).match(/^(ER_[A-Z_]+)/) : null;
128
+ if (m) {
129
+ const known = { ER_DUP_ENTRY: 1062, ER_NO_SUCH_TABLE: 1146, ER_TABLE_EXISTS: 1050, ER_PARSE_ERROR: 1064 };
130
+ if (known[m[1]]) return known[m[1]];
131
+ }
132
+ return 1105;
133
+ }
134
+
135
+ function columnDefinition(column) {
136
+ const b = new PacketBuilder();
137
+ b.raw(encodeLenencString('def'));
138
+ b.raw(encodeLenencString(column.table || ''));
139
+ b.raw(encodeLenencString(column.table || ''));
140
+ b.raw(encodeLenencString(column.table || ''));
141
+ b.raw(encodeLenencString(column.name || ''));
142
+ b.raw(encodeLenencString(column.name || ''));
143
+ b.raw(encodeLenenc(0x0c));
144
+ b.int16(column.charset || CHARSET_UTF8);
145
+ b.int32(column.length || 1024);
146
+ b.byte(column.type !== undefined ? column.type : MYSQL_TYPE_VAR_STRING);
147
+ b.int16(0);
148
+ b.byte(0);
149
+ b.int16(0);
150
+ return b.build();
151
+ }
152
+
153
+ function columnTypeFromSchema(def) {
154
+ const type = def && def.type ? String(def.type).toLowerCase() : 'string';
155
+ if (type === 'integer' || type === 'int' || type === 'bigint') return MYSQL_TYPE_LONG;
156
+ if (type === 'number' || type === 'float' || type === 'double' || type === 'real') return MYSQL_TYPE_DOUBLE;
157
+ if (type === 'boolean' || type === 'bool') return MYSQL_TYPE_TINY;
158
+ if (type === 'date') return MYSQL_TYPE_DATE;
159
+ if (type === 'datetime' || type === 'timestamp') return MYSQL_TYPE_DATETIME;
160
+ if (type === 'object' || type === 'array') return MYSQL_TYPE_JSON;
161
+ if (type === 'binary') return MYSQL_TYPE_BLOB;
162
+ return MYSQL_TYPE_VAR_STRING;
163
+ }
164
+
165
+ function resultSetPacket(result, tableSchema, baseSeq) {
166
+ const packets = [];
167
+ let sequence = baseSeq || 0;
168
+ const push = (buf) => {
169
+ const header = Buffer.alloc(4);
170
+ header.writeUIntLE(buf.length, 0, 3);
171
+ header[3] = sequence;
172
+ sequence++;
173
+ packets.push(header);
174
+ packets.push(buf);
175
+ };
176
+
177
+ push(encodeLenenc((result.columns || []).length));
178
+ for (const name of result.columns || []) {
179
+ const col = {
180
+ name,
181
+ table: result.table || '',
182
+ type: tableSchema && tableSchema[name] ? columnTypeFromSchema(tableSchema[name]) : MYSQL_TYPE_VAR_STRING,
183
+ length: 1024,
184
+ };
185
+ push(columnDefinition(col));
186
+ }
187
+ push(eofPacket());
188
+
189
+ for (const row of result.rows || []) {
190
+ const parts = [];
191
+ for (let i = 0; i < (result.columns || []).length; i++) {
192
+ const v = row[i];
193
+ if (v === null || v === undefined) {
194
+ parts.push(Buffer.from([0xfb]));
195
+ } else if (typeof v === 'object') {
196
+ parts.push(encodeLenencString(JSON.stringify(v)));
197
+ } else {
198
+ parts.push(encodeLenencString(v));
199
+ }
200
+ }
201
+ push(Buffer.concat(parts));
202
+ }
203
+ push(eofPacket());
204
+ return { packets, sequence };
205
+ }
206
+
207
+ class MysqlConnection {
208
+ constructor(socket, server) {
209
+ this.socket = socket;
210
+ this.server = server;
211
+ this.connectionId = ++server._connectionCounter;
212
+ this.seed = crypto.randomBytes(20);
213
+ this.buffer = Buffer.alloc(0);
214
+ this.sequence = 0;
215
+ this.authenticated = false;
216
+ this.user = null;
217
+ this.authFails = 0;
218
+ this.multiStatements = false;
219
+ if (server.handshakeTimeout > 0) {
220
+ this._authTimer = setTimeout(() => {
221
+ if (!this.authenticated) {
222
+ this.server._onSecurityEvent({ type: 'auth-timeout', user: this.user, remote: socket.remoteAddress });
223
+ socket.destroy();
224
+ }
225
+ }, server.handshakeTimeout);
226
+ this._authTimer.unref();
227
+ }
228
+ this.socket.on('data', chunk => this._onData(chunk));
229
+ this.socket.on('error', () => {});
230
+ this._send(handshakePacket(this.connectionId, this.seed));
231
+ }
232
+
233
+ _send(payload) {
234
+ if (!this.socket.writable) return;
235
+ const header = Buffer.alloc(4);
236
+ header.writeUIntLE(payload.length, 0, 3);
237
+ header[3] = this.sequence;
238
+ this.sequence = (this.sequence + 1) & 0xff;
239
+ this.socket.write(Buffer.concat([header, payload]));
240
+ }
241
+
242
+ _onData(chunk) {
243
+ this.buffer = Buffer.concat([this.buffer, chunk]);
244
+ if (this.buffer.length > this.server.maxPacketSize + 4) {
245
+ this._malicious('packet exceeds maxPacketSize (' + this.server.maxPacketSize + ')');
246
+ return;
247
+ }
248
+ while (true) {
249
+ if (this.buffer.length < 4) return;
250
+ const len = this.buffer.readUIntLE(0, 3);
251
+ if (len > this.server.maxPacketSize) {
252
+ this._malicious('packet length ' + len + ' exceeds maxPacketSize');
253
+ return;
254
+ }
255
+ if (this.buffer.length < 4 + len) return;
256
+ const seq = this.buffer[3];
257
+ const payload = this.buffer.slice(4, 4 + len);
258
+ this.buffer = this.buffer.slice(4 + len);
259
+ this._handlePacket(payload, seq);
260
+ if (this.socket.destroyed) return;
261
+ }
262
+ }
263
+
264
+ _malicious(reason) {
265
+ this.socket.destroy();
266
+ this.server._onSecurityEvent({ type: 'malicious', reason, user: this.user, remote: this.socket.remoteAddress });
267
+ }
268
+
269
+ _handlePacket(payload, cmdSeq) {
270
+ if (!this.authenticated) {
271
+ this.sequence = (cmdSeq + 1) & 0xff;
272
+ this._handleAuth(payload);
273
+ return;
274
+ }
275
+ this.sequence = (cmdSeq + 1) & 0xff;
276
+ const cmd = payload[0];
277
+ const body = payload.slice(1);
278
+ try {
279
+ switch (cmd) {
280
+ case 0x01: this.socket.end(); break; // COM_QUIT
281
+ case 0x02: { // COM_INIT_DB
282
+ const db = body.toString('utf8');
283
+ if (!/^[a-zA-Z0-9_$.\-]+$/.test(db)) {
284
+ this._malicious('COM_INIT_DB with invalid database name');
285
+ return;
286
+ }
287
+ this.currentDb = db;
288
+ this._send(okPacket());
289
+ break;
290
+ }
291
+ case 0x03: {
292
+ const sql = body.toString('utf8');
293
+ if (sql.length === 0) {
294
+ this._send(errPacket(1065, 'Query was empty'));
295
+ break;
296
+ }
297
+ this._handleQuery(sql);
298
+ break;
299
+ }
300
+ case 0x0e: this._send(okPacket()); break; // COM_PING
301
+ case 0x1f: this._send(okPacket()); break; // COM_RESET_CONNECTION
302
+ case 0x0a: { // COM_PROCESS_INFO
303
+ const seq = this.sequence;
304
+ this.sequence = (this.sequence + 1) & 0xff;
305
+ this._send(encodeLenenc(1));
306
+ this._send(columnDefinition({ name: 'Id', type: MYSQL_TYPE_LONG }));
307
+ this._send(eofPacket());
308
+ this._send(Buffer.from([0x31]));
309
+ this._send(eofPacket());
310
+ break;
311
+ }
312
+ case 0x09: this._send(okPacket(0, 0, SERVER_STATUS_AUTOCOMMIT)); break; // COM_STATISTICS
313
+ default:
314
+ this._send(errPacket(1105, `Unsupported command: ${cmd}`));
315
+ }
316
+ } catch (e) {
317
+ this._send(errPacket(toMysqlErrno(e), e.message));
318
+ }
319
+ }
320
+
321
+ _handleAuth(payload) {
322
+ try {
323
+ const caps = payload.readUInt32LE(0);
324
+ let pos = 32; // 4 caps + 4 maxpacket + 1 charset + 23 reserved
325
+ const userEnd = payload.indexOf(0, pos);
326
+ this.user = payload.slice(pos, userEnd).toString('utf8');
327
+ pos = userEnd + 1;
328
+ let authResponse = Buffer.alloc(0);
329
+ if (caps & CLIENT_SECURE_CONNECTION) {
330
+ const lenenc = parseLenenc(payload, pos);
331
+ if (lenenc.value === null) { pos += lenenc.size; }
332
+ else {
333
+ authResponse = payload.slice(pos + lenenc.size, pos + lenenc.size + lenenc.value);
334
+ pos += lenenc.size + lenenc.value;
335
+ }
336
+ } else {
337
+ const end = payload.indexOf(0, pos);
338
+ if (end !== -1) {
339
+ authResponse = payload.slice(pos, end);
340
+ pos = end + 1;
341
+ }
342
+ }
343
+ let db = null;
344
+ if (caps & CLIENT_CONNECT_WITH_DB) {
345
+ const dbEnd = payload.indexOf(0, pos);
346
+ if (dbEnd !== -1) {
347
+ db = payload.slice(pos, dbEnd).toString('utf8');
348
+ pos = dbEnd + 1;
349
+ }
350
+ }
351
+ this.currentDb = db;
352
+
353
+ const valid = this.server._checkAuth(this.user, authResponse, this.seed);
354
+ if (!valid) {
355
+ this.authFails++;
356
+ this.server._onSecurityEvent({ type: 'auth-fail', user: this.user, fails: this.authFails, remote: this.socket.remoteAddress });
357
+ if (this.authFails >= this.server.maxAuthFails) {
358
+ this.socket.destroy();
359
+ return;
360
+ }
361
+ this._send(errPacket(1045, `Access denied for user '${this.user}'`));
362
+ this.socket.end();
363
+ return;
364
+ }
365
+ this.multiStatements = !!(caps & CLIENT_MULTI_STATEMENTS);
366
+ this.authenticated = true;
367
+ if (this._authTimer) { clearTimeout(this._authTimer); this._authTimer = null; }
368
+ this._send(okPacket());
369
+ } catch (e) {
370
+ this._send(errPacket(1105, 'auth failed: ' + e.message));
371
+ this.socket.end();
372
+ }
373
+ }
374
+
375
+ _handleQuery(sql) {
376
+ (async () => {
377
+ try {
378
+ const results = await executeSQL(this.server._engine, sql, {
379
+ allowComments: this.server.allowComments,
380
+ safety: this.server.safety,
381
+ maxStatements: this.multiStatements ? null : 1,
382
+ });
383
+ const list = Array.isArray(results) ? results : [results];
384
+ for (const r of list) {
385
+ if (r.type === 'select' || r.type === 'showTables' || r.type === 'showDatabases' || r.type === 'describe') {
386
+ let schema = null;
387
+ if (r.table) {
388
+ const engine = this.server._engine;
389
+ schema = engine.getTableSchema
390
+ ? await engine.getTableSchema(r.table)
391
+ : (engine._schemas ? engine._schemas[r.table] : null);
392
+ }
393
+ const { packets, sequence } = resultSetPacket(r, schema, this.sequence);
394
+ this.sequence = sequence;
395
+ this.socket.write(Buffer.concat(packets));
396
+ } else {
397
+ this._send(okPacket(r.affectedRows || 0, r.insertId || 0));
398
+ }
399
+ }
400
+ } catch (e) {
401
+ this._send(errPacket(toMysqlErrno(e), e.message));
402
+ }
403
+ })();
404
+ }
405
+ }
406
+
407
+ class MysqlServer {
408
+ constructor(options = {}) {
409
+ this.options = options;
410
+ this.port = options.port || 3306;
411
+ this.host = options.host || '127.0.0.1';
412
+ this.user = options.user || null;
413
+ this.password = options.password || null;
414
+ this.auth = options.auth || null;
415
+ this.safety = options.safety !== false;
416
+ this.allowComments = options.allowComments !== false;
417
+ this.maxPacketSize = options.maxPacketSize || 1024 * 1024;
418
+ this.handshakeTimeout = options.handshakeTimeout != null ? options.handshakeTimeout : 10000;
419
+ this.maxAuthFails = options.maxAuthFails || 3;
420
+ this.maxConnections = options.maxConnections || 128;
421
+ this._engine = null;
422
+ this._ownEngine = false;
423
+ this._connectionCounter = 0;
424
+ this._sockets = new Set();
425
+ this._securityHandler = typeof options.onSecurityEvent === 'function' ? options.onSecurityEvent : null;
426
+ }
427
+
428
+ _onSecurityEvent(event) {
429
+ if (this._securityHandler) {
430
+ try { this._securityHandler(event); } catch (e) {}
431
+ }
432
+ }
433
+
434
+ async _getEngine() {
435
+ if (this._engine) return this._engine;
436
+ if (this.options.engine || this.options.database) {
437
+ this._engine = this.options.engine || this.options.database;
438
+ if (typeof this._engine.start === 'function') await this._engine.start();
439
+ } else {
440
+ this._engine = new Database(this.options.filename || ':memory:');
441
+ if (typeof this._engine.start === 'function') await this._engine.start();
442
+ this._ownEngine = true;
443
+ }
444
+ return this._engine;
445
+ }
446
+
447
+ _checkAuth(user, authResponse, seed) {
448
+ if (this.auth) {
449
+ if (!Object.prototype.hasOwnProperty.call(this.auth, user)) return false;
450
+ const pwd = this.auth[user];
451
+ if (!pwd) return authResponse.length === 0;
452
+ const pwdHash1 = crypto.createHash('sha1').update(pwd).digest();
453
+ const pwdHash2 = crypto.createHash('sha1').update(pwdHash1).digest();
454
+ const seedHash = crypto.createHash('sha1').update(Buffer.concat([seed, pwdHash2])).digest();
455
+ const expected = Buffer.alloc(20);
456
+ for (let i = 0; i < 20; i++) expected[i] = pwdHash1[i] ^ seedHash[i];
457
+ return authResponse.length === 20 && crypto.timingSafeEqual(expected, authResponse);
458
+ }
459
+ if (this.user === null) return true;
460
+ if (user !== this.user) return false;
461
+ if (!this.password) return authResponse.length === 0;
462
+ const pwdHash1 = crypto.createHash('sha1').update(this.password).digest();
463
+ const pwdHash2 = crypto.createHash('sha1').update(pwdHash1).digest();
464
+ const seedHash = crypto.createHash('sha1').update(Buffer.concat([seed, pwdHash2])).digest();
465
+ const expected = Buffer.alloc(20);
466
+ for (let i = 0; i < 20; i++) expected[i] = pwdHash1[i] ^ seedHash[i];
467
+ return authResponse.length === 20 && crypto.timingSafeEqual(expected, authResponse);
468
+ }
469
+
470
+ listen(cb) {
471
+ this._getEngine().then(() => {
472
+ this._server = net.createServer(socket => {
473
+ if (this._sockets.size >= this.maxConnections) {
474
+ this._onSecurityEvent({ type: 'max-connections', remote: socket.remoteAddress });
475
+ socket.destroy();
476
+ return;
477
+ }
478
+ this._sockets.add(socket);
479
+ socket.on('close', () => this._sockets.delete(socket));
480
+ new MysqlConnection(socket, this);
481
+ });
482
+ this._server.listen(this.port, this.host, cb || (() => {}));
483
+ }).catch(err => {
484
+ if (cb) cb(err);
485
+ else throw err;
486
+ });
487
+ return this;
488
+ }
489
+
490
+ get address() {
491
+ return this._server ? this._server.address() : null;
492
+ }
493
+
494
+ close(cb) {
495
+ const done = () => {
496
+ for (const s of this._sockets) s.destroy();
497
+ if (this._ownEngine && this._engine && typeof this._engine.stop === 'function') {
498
+ this._engine.stop().then(() => cb && cb()).catch(() => cb && cb());
499
+ } else if (cb) cb();
500
+ };
501
+ if (this._server) {
502
+ this._server.close(() => done());
503
+ } else {
504
+ done();
505
+ }
506
+ return this;
507
+ }
508
+ }
509
+
510
+ function createMysqlServer(options) {
511
+ return new MysqlServer(options || {});
512
+ }
513
+
514
+ module.exports = { createMysqlServer, MysqlServer };