jsql-neo 4.5.1 → 5.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.
@@ -909,7 +909,7 @@ class MysqlServer {
909
909
  this._engine = null;
910
910
  this._ownEngine = false;
911
911
  this._databases = new Map();
912
- this._dbDir = options.dataDir ? path.resolve(options.dataDir) : null;
912
+ this._dbDir = options.dataDir && options.dataDir !== ':memory:' ? path.resolve(options.dataDir) : null;
913
913
  this._defaultDbName = options.defaultDatabase || 'default';
914
914
  this._connectionCounter = 0;
915
915
  this._sockets = new Set();
@@ -7,6 +7,69 @@ const STR_TAG = 3;
7
7
  const BOOL_TAG = 4;
8
8
  const INT32_TAG = 5;
9
9
 
10
+ function safeParse(str, fallback) {
11
+ if (typeof str !== 'string') return fallback;
12
+ try { return JSON.parse(str); } catch (e) { return fallback; }
13
+ }
14
+
15
+ const NATIVE_TYPE_MAP = {
16
+ text: 'string',
17
+ varchar: 'string',
18
+ double: 'float',
19
+ number: 'float',
20
+ numeric: 'float',
21
+ decimal: 'float',
22
+ date: 'string',
23
+ timestamp: 'string',
24
+ datetime: 'string',
25
+ json: 'string',
26
+ array: 'string',
27
+ object: 'string',
28
+ bool: 'boolean',
29
+ bigint: 'string',
30
+ int: 'integer',
31
+ };
32
+
33
+ function mapNativeSchema(schema) {
34
+ const mapped = {};
35
+ for (const [field, def] of Object.entries(schema || {})) {
36
+ if (typeof def === 'string') {
37
+ mapped[field] = { type: NATIVE_TYPE_MAP[def.toLowerCase()] || def.toLowerCase() };
38
+ } else if (def && typeof def === 'object') {
39
+ const t = (def.type || 'string').toLowerCase();
40
+ mapped[field] = { ...def, type: NATIVE_TYPE_MAP[t] || t };
41
+ } else {
42
+ mapped[field] = { type: 'string' };
43
+ }
44
+ }
45
+ return mapped;
46
+ }
47
+
48
+ function restoreRow(row, schema) {
49
+ if (!schema || typeof row !== 'object' || row === null) return row;
50
+ const out = {};
51
+ for (const [field, def] of Object.entries(row)) {
52
+ if (field === 'fields' && typeof row[field] === 'object' && row[field] !== null) {
53
+ out[field] = restoreRow(row[field], schema);
54
+ continue;
55
+ }
56
+ const colDef = schema[field];
57
+ let t = null;
58
+ if (typeof colDef === 'string') t = colDef.toLowerCase();
59
+ else if (colDef && typeof colDef === 'object' && colDef.type) t = String(colDef.type).toLowerCase();
60
+ if (['json', 'array', 'object'].includes(t) && typeof row[field] === 'string') {
61
+ try { out[field] = JSON.parse(row[field]); } catch (e) { out[field] = row[field]; }
62
+ } else if ((t === 'integer' || t === 'int' || t === 'bigint') && typeof row[field] === 'string' && /^-?\d+$/.test(row[field])) {
63
+ out[field] = Number(row[field]);
64
+ } else if ((t === 'float' || t === 'double' || t === 'number') && typeof row[field] === 'string' && !Number.isNaN(Number(row[field]))) {
65
+ out[field] = Number(row[field]);
66
+ } else {
67
+ out[field] = row[field];
68
+ }
69
+ }
70
+ return out;
71
+ }
72
+
10
73
  function encodeBatch(rows) {
11
74
  if (rows.length === 0) return new Uint8Array(0);
12
75
  const fieldNames = Object.keys(rows[0]);
@@ -279,14 +342,23 @@ class JSQL {
279
342
  async start() {
280
343
  if (this._mode !== 'memory') {
281
344
  if (!this._path) throw new Error('hybrid/disk mode requires a directory path');
282
- const r = JSON.parse(native.jsqlOpen(this._path, this._mode));
345
+ let r;
346
+ try {
347
+ r = safeParse(native.jsqlOpen(this._path, this._mode));
348
+ } catch (e) {
349
+ throw new Error('failed to open native storage: ' + e.message);
350
+ }
283
351
  if (r && r.ok === false) throw new Error(r.error || 'open storage failed');
284
352
  if (r && Array.isArray(r.tables)) {
285
353
  this._tableNames = new Set(r.tables);
286
354
  if (r.schemas) this._schemas = r.schemas;
287
355
  }
288
356
  } else {
289
- JSON.parse(native.jsqlOpen('', 'memory'));
357
+ try {
358
+ JSON.parse(native.jsqlOpen('', 'memory'));
359
+ } catch (e) {
360
+ throw new Error('failed to open native memory storage: ' + e.message);
361
+ }
290
362
  }
291
363
  if (this._mode !== 'memory') {
292
364
  if (this._flushInterval > 0) {
@@ -319,8 +391,19 @@ class JSQL {
319
391
  }
320
392
 
321
393
  async _insertBatch(table, rows) {
394
+ const schema = this._schemas[table];
395
+ if (schema) {
396
+ rows = rows.map(row => {
397
+ const out = {};
398
+ for (const [k, v] of Object.entries(row)) {
399
+ if (v instanceof Date) out[k] = v.toISOString();
400
+ else out[k] = v;
401
+ }
402
+ return out;
403
+ });
404
+ }
322
405
  const bin = encodeBatch(rows);
323
- const r = JSON.parse(native.jsqlInsertBuf(table, bin));
406
+ const r = safeParse(native.jsqlInsertBuf(table, bin));
324
407
  if (r && r.error) throw new Error(r.error);
325
408
  return r;
326
409
  }
@@ -340,13 +423,13 @@ class JSQL {
340
423
  var ids = remove[table];
341
424
  if (ids.size === 0) continue;
342
425
  var idsArr = Array.from(ids);
343
- var r = JSON.parse(native.jsqlRemoveByIds(table, JSON.stringify(idsArr)));
426
+ var r = safeParse(native.jsqlRemoveByIds(table, JSON.stringify(idsArr)));
344
427
  this._emit('delete', { table, ids: idsArr, result: r });
345
428
  }
346
429
  for (var table in update) {
347
430
  var entries = update[table];
348
431
  if (entries.length === 0) continue;
349
- var r = JSON.parse(native.jsqlUpdateByIds(table, JSON.stringify(entries)));
432
+ var r = safeParse(native.jsqlUpdateByIds(table, JSON.stringify(entries)));
350
433
  this._emit('update', { table, entries, result: r });
351
434
  }
352
435
  this._opBuffer = { remove: {}, update: {} };
@@ -410,7 +493,7 @@ class JSQL {
410
493
  async createTable(name, schema) {
411
494
  await this._flush();
412
495
  if (!this._runHooks('beforeCreateTable', [name, schema])) return null;
413
- const r = JSON.parse(native.jsqlCreateTable(name, JSON.stringify(schema)));
496
+ const r = safeParse(native.jsqlCreateTable(name, JSON.stringify(mapNativeSchema(schema))));
414
497
  if (r && r.ok === false) throw new Error(r.error || 'create table failed');
415
498
  this._tableNames.add(name);
416
499
  this._schemas[name] = schema;
@@ -422,7 +505,7 @@ class JSQL {
422
505
  async dropTable(name) {
423
506
  await this._flush();
424
507
  if (!this._runHooks('beforeDropTable', [name])) return null;
425
- const r = JSON.parse(native.jsqlDropTable(name));
508
+ const r = safeParse(native.jsqlDropTable(name));
426
509
  if (r && r.ok === false) throw new Error(r.error || 'drop table failed');
427
510
  this._tableNames.delete(name);
428
511
  delete this._schemas[name];
@@ -434,9 +517,11 @@ class JSQL {
434
517
  findById(table, id) {
435
518
  this._flushOpsNow();
436
519
  if (!this._runHooks('beforeFind', [table, { id }])) return null;
437
- const raw = JSON.parse(native.jsqlFindById(table, Number(id)));
520
+ const raw = safeParse(native.jsqlFindById(table, Number(id)));
438
521
  if (raw && raw.error) throw new Error(raw.error);
439
522
  this._runHooks('afterFind', [table, { id }, raw]);
523
+ const schema = this._schemas[table];
524
+ if (schema && raw && typeof raw === 'object') return restoreRow(raw, schema);
440
525
  return raw;
441
526
  }
442
527
 
@@ -444,9 +529,11 @@ class JSQL {
444
529
  this._flushOpsNow();
445
530
  if (!this._runHooks('beforeFind', [table, { ids }])) return null;
446
531
  const resultStr = native.jsqlFindByIds(table, JSON.stringify(ids));
447
- const r = JSON.parse(resultStr);
532
+ const r = safeParse(resultStr);
448
533
  if (r && r.error) throw new Error(r.error);
449
534
  this._runHooks('afterFind', [table, { ids }, r]);
535
+ const schema = this._schemas[table];
536
+ if (schema && Array.isArray(r)) return r.map(row => restoreRow(row, schema));
450
537
  return r;
451
538
  }
452
539
 
@@ -459,9 +546,11 @@ class JSQL {
459
546
  if (!this._runHooks('beforeFind', [table, { filter, opts }])) return [];
460
547
  const filterStr = filter ? JSON.stringify(filter) : '';
461
548
  const { limit = 100, offset = 0 } = opts;
462
- const r = JSON.parse(native.jsqlFind(table, filterStr, limit, offset));
549
+ const r = safeParse(native.jsqlFind(table, filterStr, limit, offset));
463
550
  if (r && r.error) throw new Error(r.error);
464
551
  this._runHooks('afterFind', [table, { filter, opts }, r]);
552
+ const schema = this._schemas[table];
553
+ if (schema && Array.isArray(r)) return r.map(row => restoreRow(row, schema));
465
554
  return r;
466
555
  }
467
556
 
@@ -529,19 +618,19 @@ class JSQL {
529
618
 
530
619
  async beginTx() {
531
620
  this._flushOpsNow();
532
- const r = JSON.parse(native.jsqlBeginTx());
621
+ const r = safeParse(native.jsqlBeginTx());
533
622
  if (r && r.ok === false) throw new Error(r.error || 'begin transaction failed');
534
623
  return r.txId;
535
624
  }
536
625
 
537
626
  async commitTx(txId) {
538
- const r = JSON.parse(native.jsqlCommitTx(String(txId)));
627
+ const r = safeParse(native.jsqlCommitTx(String(txId)));
539
628
  if (r && r.ok === false) throw new Error(r.error || 'commit transaction failed');
540
629
  return true;
541
630
  }
542
631
 
543
632
  async rollbackTx(txId) {
544
- const r = JSON.parse(native.jsqlRollbackTx(String(txId)));
633
+ const r = safeParse(native.jsqlRollbackTx(String(txId)));
545
634
  if (r && r.ok === false) throw new Error(r.error || 'rollback transaction failed');
546
635
  return true;
547
636
  }