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,470 @@
1
+ const path = require('path');
2
+ const os = require('os');
3
+ const native = require(path.join(__dirname, '..', 'native', 'jsql-neo-native.node'));
4
+ const INT64_TAG = 1;
5
+ const FLOAT_TAG = 2;
6
+ const STR_TAG = 3;
7
+ const BOOL_TAG = 4;
8
+ const INT32_TAG = 5;
9
+
10
+ function encodeBatch(rows) {
11
+ if (rows.length === 0) return new Uint8Array(0);
12
+ const fieldNames = Object.keys(rows[0]);
13
+ const nFields = fieldNames.length;
14
+ const est = 100 + rows.length * 120;
15
+ const buf = Buffer.allocUnsafe(est);
16
+ let off = 0;
17
+
18
+ off = buf.writeUInt8(nFields, off);
19
+ for (let fi = 0; fi < nFields; fi++) {
20
+ const s = fieldNames[fi];
21
+ const nlen = Buffer.byteLength(s, 'utf8');
22
+ off = buf.writeUInt8(nlen, off);
23
+ off += buf.write(s, off, nlen, 'utf8');
24
+ }
25
+ off = buf.writeUInt32LE(rows.length, off);
26
+
27
+ for (let ri = 0; ri < rows.length; ri++) {
28
+ const row = rows[ri];
29
+
30
+ if (nFields >= 1) {
31
+ const v = row[fieldNames[0]];
32
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
33
+ else if (typeof v === 'number') {
34
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
35
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
36
+ } else if (typeof v === 'string') {
37
+ off = buf.writeUInt8(STR_TAG, off);
38
+ const sl = Buffer.byteLength(v, 'utf8');
39
+ off = buf.writeUInt32LE(sl, off);
40
+ off += buf.write(v, off, sl, 'utf8');
41
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
42
+ }
43
+ if (nFields >= 2) {
44
+ const v = row[fieldNames[1]];
45
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
46
+ else if (typeof v === 'number') {
47
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
48
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
49
+ } else if (typeof v === 'string') {
50
+ off = buf.writeUInt8(STR_TAG, off);
51
+ const sl = Buffer.byteLength(v, 'utf8');
52
+ off = buf.writeUInt32LE(sl, off);
53
+ off += buf.write(v, off, sl, 'utf8');
54
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
55
+ }
56
+ if (nFields >= 3) {
57
+ const v = row[fieldNames[2]];
58
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
59
+ else if (typeof v === 'number') {
60
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
61
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
62
+ } else if (typeof v === 'string') {
63
+ off = buf.writeUInt8(STR_TAG, off);
64
+ const sl = Buffer.byteLength(v, 'utf8');
65
+ off = buf.writeUInt32LE(sl, off);
66
+ off += buf.write(v, off, sl, 'utf8');
67
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
68
+ }
69
+ if (nFields >= 4) {
70
+ const v = row[fieldNames[3]];
71
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
72
+ else if (typeof v === 'number') {
73
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
74
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
75
+ } else if (typeof v === 'string') {
76
+ off = buf.writeUInt8(STR_TAG, off);
77
+ const sl = Buffer.byteLength(v, 'utf8');
78
+ off = buf.writeUInt32LE(sl, off);
79
+ off += buf.write(v, off, sl, 'utf8');
80
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
81
+ }
82
+ if (nFields >= 5) {
83
+ const v = row[fieldNames[4]];
84
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
85
+ else if (typeof v === 'number') {
86
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
87
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
88
+ } else if (typeof v === 'string') {
89
+ off = buf.writeUInt8(STR_TAG, off);
90
+ const sl = Buffer.byteLength(v, 'utf8');
91
+ off = buf.writeUInt32LE(sl, off);
92
+ off += buf.write(v, off, sl, 'utf8');
93
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
94
+ }
95
+ for (let fi = 5; fi < nFields; fi++) {
96
+ const v = row[fieldNames[fi]];
97
+ if (v === null || v === undefined) { off = buf.writeUInt8(0, off); }
98
+ else if (typeof v === 'number') {
99
+ if (Number.isInteger(v)) { if (v >= -2147483648 && v <= 2147483647) { off = buf.writeUInt8(INT32_TAG, off); off = buf.writeInt32LE(v, off); } else { off = buf.writeUInt8(INT64_TAG, off); off = buf.writeBigInt64LE(BigInt(v), off); } }
100
+ else { off = buf.writeUInt8(FLOAT_TAG, off); off = buf.writeDoubleLE(v, off); }
101
+ } else if (typeof v === 'string') {
102
+ off = buf.writeUInt8(STR_TAG, off);
103
+ const sl = Buffer.byteLength(v, 'utf8');
104
+ off = buf.writeUInt32LE(sl, off);
105
+ off += buf.write(v, off, sl, 'utf8');
106
+ } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
107
+ }
108
+ }
109
+
110
+ return buf.slice(0, off);
111
+ }
112
+
113
+ class JSQL {
114
+ constructor(opts = {}) {
115
+ this._flushThreshold = opts.flushThreshold || 5000;
116
+ this._buffer = {};
117
+ this._bufferSize = 0;
118
+ this._opBuffer = { remove: {}, update: {} };
119
+ this._opTimer = null;
120
+ this._opFlushInterval = opts.opFlushInterval || 50;
121
+ this._tableNames = new Set();
122
+ this._schemas = {};
123
+ this._autoPlugins = opts.modules !== false;
124
+
125
+ // 持久化模式: 'memory' | 'hybrid' | 'disk'
126
+ this._path = opts.path || null;
127
+ this._mode = opts.mode || (this._path ? 'hybrid' : 'memory');
128
+ this._memReserveMB = opts.memReserveMB !== undefined ? opts.memReserveMB : 512;
129
+ this._flushInterval = opts.flushInterval !== undefined
130
+ ? opts.flushInterval
131
+ : (this._mode === 'disk' ? 50 : 200);
132
+ this._evictInterval = opts.evictInterval !== undefined ? opts.evictInterval : 1000;
133
+ this._diskFlushTimer = null;
134
+ this._evictTimer = null;
135
+
136
+ this._plugins = [];
137
+ this._hooks = {
138
+ beforeInsert: [], afterInsert: [],
139
+ beforeUpdate: [], afterUpdate: [],
140
+ beforeDelete: [], afterDelete: [],
141
+ beforeFind: [], afterFind: [],
142
+ beforeCreateTable: [], afterCreateTable: [],
143
+ beforeDropTable: [], afterDropTable: [],
144
+ beforeFlush: [], afterFlush: [],
145
+ beforeCount: [], afterCount: [],
146
+ onStart: [], onStop: []
147
+ };
148
+ this._eventListeners = [];
149
+ if (this._autoPlugins) {
150
+ const { ModuleManager } = require('./mod');
151
+ new ModuleManager().applyTo(this);
152
+ }
153
+ }
154
+
155
+ use(plugin) {
156
+ if (typeof plugin === 'function') {
157
+ plugin = { install: plugin };
158
+ }
159
+ if (plugin.hooks) {
160
+ for (const [event, fn] of Object.entries(plugin.hooks)) {
161
+ if (this._hooks[event]) {
162
+ this._hooks[event].push(fn);
163
+ }
164
+ }
165
+ }
166
+ if (typeof plugin.install === 'function') {
167
+ plugin.install(this, this._buildCtx(plugin));
168
+ }
169
+ if (typeof plugin.onEvent === 'function') {
170
+ this._eventListeners.push(plugin.onEvent);
171
+ }
172
+ this._plugins.push(plugin);
173
+ return this;
174
+ }
175
+
176
+ _buildCtx(plugin) {
177
+ const self = this;
178
+ return {
179
+ name: plugin.name || 'anonymous',
180
+ engine: this,
181
+ plugin,
182
+ on(hook, fn) { return self.on(hook, fn); },
183
+ onEvent(fn) { return self.onEvent(fn); },
184
+ emit(eventName, data) { self._emit(eventName, data); },
185
+ tables() { return Array.from(self._tableNames); },
186
+ hasTable(name) { return self._tableNames.has(name); },
187
+ getTableSchema(name) { return self._schemas[name] || null; }
188
+ };
189
+ }
190
+
191
+ on(event, fn) {
192
+ if (this._hooks[event]) {
193
+ this._hooks[event].push(fn);
194
+ }
195
+ return this;
196
+ }
197
+
198
+ onEvent(fn) {
199
+ this._eventListeners.push(fn);
200
+ return this;
201
+ }
202
+
203
+ _emit(eventName, data) {
204
+ for (const fn of this._eventListeners) {
205
+ try { fn(eventName, data); } catch (e) { /* ignore */ }
206
+ }
207
+ }
208
+
209
+ _runHooks(hookName, args) {
210
+ const hooks = this._hooks[hookName];
211
+ if (!hooks || hooks.length === 0) return true;
212
+ for (const fn of hooks) {
213
+ const r = fn(...args);
214
+ if (r === false) return false;
215
+ if (r !== undefined && args.length > 0) {
216
+ args[0] = r;
217
+ }
218
+ }
219
+ return true;
220
+ }
221
+
222
+ async start() {
223
+ if (this._mode !== 'memory') {
224
+ if (!this._path) throw new Error('hybrid/disk mode requires a directory path');
225
+ const r = JSON.parse(native.jsqlOpen(this._path, this._mode));
226
+ if (r && r.ok === false) throw new Error(r.error || 'open storage failed');
227
+ if (r && Array.isArray(r.tables)) {
228
+ this._tableNames = new Set(r.tables);
229
+ if (r.schemas) this._schemas = r.schemas;
230
+ }
231
+ if (this._flushInterval > 0) {
232
+ this._diskFlushTimer = setInterval(() => {
233
+ try { native.jsqlFlushDirty(); } catch (e) { /* ignore */ }
234
+ }, this._flushInterval);
235
+ if (this._diskFlushTimer.unref) this._diskFlushTimer.unref();
236
+ }
237
+ this._evictTimer = setInterval(() => {
238
+ try { this._checkMemory(); } catch (e) { /* ignore */ }
239
+ }, this._evictInterval);
240
+ if (this._evictTimer.unref) this._evictTimer.unref();
241
+ }
242
+ this._runHooks('onStart', []);
243
+ this._emit('start', {});
244
+ }
245
+
246
+ _checkMemory() {
247
+ const total = os.totalmem();
248
+ if (!total) return;
249
+ const budget = total - this._memReserveMB * 1024 * 1024;
250
+ if (budget <= 0) return;
251
+ if (process.memoryUsage().rss <= budget) return;
252
+ for (let i = 0; i < 16; i++) {
253
+ let r;
254
+ try { r = JSON.parse(native.jsqlEvict()); } catch (e) { return; }
255
+ if (r.ok === false || !r.evicted || r.remaining === 0) return;
256
+ if (process.memoryUsage().rss <= budget) return;
257
+ }
258
+ }
259
+
260
+ async _insertBatch(table, rows) {
261
+ const bin = encodeBatch(rows);
262
+ const r = JSON.parse(native.jsqlInsertBuf(table, bin));
263
+ if (r && r.error) throw new Error(r.error);
264
+ return r;
265
+ }
266
+
267
+ _scheduleOpFlush() {
268
+ if (this._opTimer) return;
269
+ this._opTimer = setTimeout(() => {
270
+ this._opTimer = null;
271
+ this._flushOps();
272
+ }, this._opFlushInterval);
273
+ }
274
+
275
+ _flushOps() {
276
+ var remove = this._opBuffer.remove;
277
+ var update = this._opBuffer.update;
278
+ for (var table in remove) {
279
+ var ids = remove[table];
280
+ if (ids.size === 0) continue;
281
+ var idsArr = Array.from(ids);
282
+ var r = JSON.parse(native.jsqlRemoveByIds(table, JSON.stringify(idsArr)));
283
+ this._emit('delete', { table, ids: idsArr, result: r });
284
+ }
285
+ for (var table in update) {
286
+ var entries = update[table];
287
+ if (entries.length === 0) continue;
288
+ var r = JSON.parse(native.jsqlUpdateByIds(table, JSON.stringify(entries)));
289
+ this._emit('update', { table, entries, result: r });
290
+ }
291
+ this._opBuffer = { remove: {}, update: {} };
292
+ }
293
+
294
+ async _flush() {
295
+ this._flushOpsNow();
296
+ if (!this._runHooks('beforeFlush', [])) return null;
297
+ const flushed = {};
298
+ for (const [table, rows] of Object.entries(this._buffer)) {
299
+ if (rows.length === 0) continue;
300
+ const r = await this._insertBatch(table, rows);
301
+ flushed[table] = r;
302
+ }
303
+ this._buffer = {};
304
+ this._bufferSize = 0;
305
+ this._runHooks('afterFlush', []);
306
+ return flushed;
307
+ }
308
+
309
+ _flushOpsNow() {
310
+ if (this._opTimer) {
311
+ clearTimeout(this._opTimer);
312
+ this._opTimer = null;
313
+ }
314
+ this._flushOps();
315
+ }
316
+
317
+ async flush() {
318
+ await this._flush();
319
+ this._flushOpsNow();
320
+ if (this._mode !== 'memory') {
321
+ try { native.jsqlFlushDirty(); } catch (e) { /* ignore */ }
322
+ }
323
+ }
324
+
325
+ async insert(table, data) {
326
+ const arr = Array.isArray(data) ? data : [data];
327
+ var filtered = arr;
328
+ if (!this._runHooks('beforeInsert', [table, filtered])) return [];
329
+ if (arr.length > 1) {
330
+ await this._flush();
331
+ let result;
332
+ for (let i = 0; i < arr.length; i += this._flushThreshold) {
333
+ const chunk = arr.slice(i, i + this._flushThreshold);
334
+ const r = await this._insertBatch(table, chunk);
335
+ if (r && r.error) throw new Error(r.error);
336
+ if (!result) result = r;
337
+ }
338
+ this._emit('insert', { table, count: arr.length, ids: result });
339
+ this._runHooks('afterInsert', [table, filtered, result]);
340
+ return result;
341
+ }
342
+ if (!this._buffer[table]) this._buffer[table] = [];
343
+ this._buffer[table].push(arr[0]);
344
+ this._bufferSize++;
345
+ const flushed = await this._flush();
346
+ return flushed && flushed[table] ? flushed[table] : null;
347
+ }
348
+
349
+ async createTable(name, schema) {
350
+ await this._flush();
351
+ if (!this._runHooks('beforeCreateTable', [name, schema])) return null;
352
+ const r = JSON.parse(native.jsqlCreateTable(name, JSON.stringify(schema)));
353
+ if (r && r.ok === false) throw new Error(r.error || 'create table failed');
354
+ this._tableNames.add(name);
355
+ this._schemas[name] = schema;
356
+ this._emit('createTable', { name, schema });
357
+ this._runHooks('afterCreateTable', [name, schema]);
358
+ return r;
359
+ }
360
+
361
+ async dropTable(name) {
362
+ await this._flush();
363
+ if (!this._runHooks('beforeDropTable', [name])) return null;
364
+ const r = JSON.parse(native.jsqlDropTable(name));
365
+ if (r && r.ok === false) throw new Error(r.error || 'drop table failed');
366
+ this._tableNames.delete(name);
367
+ delete this._schemas[name];
368
+ this._emit('dropTable', { name });
369
+ this._runHooks('afterDropTable', [name]);
370
+ return r;
371
+ }
372
+
373
+ findById(table, id) {
374
+ this._flushOpsNow();
375
+ if (!this._runHooks('beforeFind', [table, { id }])) return null;
376
+ const raw = JSON.parse(native.jsqlFindById(table, Number(id)));
377
+ if (raw && raw.error) throw new Error(raw.error);
378
+ this._runHooks('afterFind', [table, { id }, raw]);
379
+ return raw;
380
+ }
381
+
382
+ findByIds(table, ids) {
383
+ this._flushOpsNow();
384
+ if (!this._runHooks('beforeFind', [table, { ids }])) return null;
385
+ const resultStr = native.jsqlFindByIds(table, JSON.stringify(ids));
386
+ const r = JSON.parse(resultStr);
387
+ if (r && r.error) throw new Error(r.error);
388
+ this._runHooks('afterFind', [table, { ids }, r]);
389
+ return r;
390
+ }
391
+
392
+ findByIdsRaw(table, ids) {
393
+ return this.findByIds(table, ids);
394
+ }
395
+
396
+ async find(table, filter, opts = {}) {
397
+ this._flushOpsNow();
398
+ if (!this._runHooks('beforeFind', [table, { filter, opts }])) return [];
399
+ const filterStr = filter ? JSON.stringify(filter) : '';
400
+ const { limit = 100, offset = 0 } = opts;
401
+ const r = JSON.parse(native.jsqlFind(table, filterStr, limit, offset));
402
+ if (r && r.error) throw new Error(r.error);
403
+ this._runHooks('afterFind', [table, { filter, opts }, r]);
404
+ return r;
405
+ }
406
+
407
+ async count(table) {
408
+ this._flushOpsNow();
409
+ this._runHooks('beforeCount', [table]);
410
+ const r = parseInt(native.jsqlCount(table), 10);
411
+ const n = isNaN(r) ? 0 : r;
412
+ this._runHooks('afterCount', [table, n]);
413
+ return n;
414
+ }
415
+
416
+ updateByIds(table, entries) {
417
+ const pairs = entries.map(([id, data]) => [Number(id), data]);
418
+ if (!this._runHooks('beforeUpdate', [table, pairs])) return;
419
+ if (!this._opBuffer.update[table]) this._opBuffer.update[table] = [];
420
+ this._opBuffer.update[table].push(...pairs);
421
+ this._scheduleOpFlush();
422
+ }
423
+
424
+ removeByIds(table, ids) {
425
+ if (!this._runHooks('beforeDelete', [table, ids])) return;
426
+ if (!this._opBuffer.remove[table]) this._opBuffer.remove[table] = new Set();
427
+ for (const id of ids) this._opBuffer.remove[table].add(Number(id));
428
+ this._scheduleOpFlush();
429
+ }
430
+
431
+ updateById(table, id, data) {
432
+ if (!this._runHooks('beforeUpdate', [table, id, data])) return;
433
+ if (!this._opBuffer.update[table]) this._opBuffer.update[table] = [];
434
+ this._opBuffer.update[table].push([Number(id), data]);
435
+ this._scheduleOpFlush();
436
+ }
437
+
438
+ removeById(table, id) {
439
+ if (!this._runHooks('beforeDelete', [table, id])) return;
440
+ if (!this._opBuffer.remove[table]) this._opBuffer.remove[table] = new Set();
441
+ this._opBuffer.remove[table].add(Number(id));
442
+ this._scheduleOpFlush();
443
+ }
444
+
445
+ hasTable(name) {
446
+ return this._tableNames.has(name);
447
+ }
448
+
449
+ getTables() {
450
+ return Array.from(this._tableNames);
451
+ }
452
+
453
+ getTableSchema(name) {
454
+ return this._schemas[name] || null;
455
+ }
456
+
457
+ async stop() {
458
+ this._flushOpsNow();
459
+ await this._flush();
460
+ if (this._diskFlushTimer) { clearInterval(this._diskFlushTimer); this._diskFlushTimer = null; }
461
+ if (this._evictTimer) { clearInterval(this._evictTimer); this._evictTimer = null; }
462
+ if (this._mode !== 'memory') {
463
+ try { JSON.parse(native.jsqlClose()); } catch (e) { /* ignore */ }
464
+ }
465
+ this._runHooks('onStop', []);
466
+ this._emit('stop', {});
467
+ }
468
+ }
469
+
470
+ module.exports = { JSQL };