jexidb 1.0.2 → 1.0.3

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.
package/README.md CHANGED
@@ -35,6 +35,8 @@ const db = new Database('path/to/database.jdb', { // file will be created if it
35
35
  }
36
36
  });
37
37
  ```
38
+ You can [learn a bit more about these options at this link](https://github.com/EdenwareApps/jexidb/tree/main/test#readme).
39
+
38
40
 
39
41
  ### Initializing the Database
40
42
 
package/dist/Database.cjs CHANGED
@@ -7,8 +7,7 @@ exports.Database = void 0;
7
7
  var _events = require("events");
8
8
  var _FileHandler = _interopRequireDefault(require("./FileHandler.mjs"));
9
9
  var _IndexManager = _interopRequireDefault(require("./IndexManager.mjs"));
10
- var _Simple = _interopRequireDefault(require("./serializers/Simple.mjs"));
11
- var _Advanced = _interopRequireDefault(require("./serializers/Advanced.mjs"));
10
+ var _Serializer = _interopRequireDefault(require("./Serializer.mjs"));
12
11
  var _fs = _interopRequireDefault(require("fs"));
13
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
14
13
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
@@ -45,7 +44,7 @@ function AsyncGenerator(e) { var r, t; function resume(r, t) { try { var n = e[r
45
44
  AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; }, AsyncGenerator.prototype.next = function (e) { return this._invoke("next", e); }, AsyncGenerator.prototype["throw"] = function (e) { return this._invoke("throw", e); }, AsyncGenerator.prototype["return"] = function (e) { return this._invoke("return", e); };
46
45
  function _OverloadYield(e, d) { this.v = e, this.k = d; }
47
46
  var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
48
- function Database(filePath) {
47
+ function Database(file) {
49
48
  var _this2;
50
49
  var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
51
50
  _classCallCheck(this, Database);
@@ -60,13 +59,10 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
60
59
  compressIndex: false,
61
60
  maxMemoryUsage: 64 * 1024 // 64KB
62
61
  }, opts);
62
+ _this2.offsets = [];
63
63
  _this2.shouldSave = false;
64
- if (_this2.opts.v8 || _this2.opts.compress || _this2.opts.compressIndex) {
65
- _this2.serializer = new _Advanced["default"](_this2.opts);
66
- } else {
67
- _this2.serializer = new _Simple["default"](_this2.opts);
68
- }
69
- _this2.fileHandler = new _FileHandler["default"](filePath);
64
+ _this2.serializer = new _Serializer["default"](_this2.opts);
65
+ _this2.fileHandler = new _FileHandler["default"](file);
70
66
  _this2.indexManager = new _IndexManager["default"](_this2.opts);
71
67
  _this2.indexOffset = 0;
72
68
  _this2.writeBuffer = [];
@@ -165,7 +161,7 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
165
161
  case 38:
166
162
  _context.prev = 38;
167
163
  _context.t0 = _context["catch"](9);
168
- if (!this.offsets) {
164
+ if (Array.isArray(this.offsets)) {
169
165
  this.offsets = [];
170
166
  }
171
167
  this.indexOffset = 0;
@@ -204,18 +200,30 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
204
200
  }
205
201
  throw new Error('Database is destroyed');
206
202
  case 2:
207
- if (!this.saving) {
203
+ if (this.initialized) {
208
204
  _context2.next = 4;
209
205
  break;
210
206
  }
207
+ throw new Error('Database not initialized');
208
+ case 4:
209
+ if (!this.saving) {
210
+ _context2.next = 6;
211
+ break;
212
+ }
211
213
  return _context2.abrupt("return", new Promise(function (resolve) {
212
214
  return _this4.once('save', resolve);
213
215
  }));
214
- case 4:
216
+ case 6:
215
217
  this.saving = true;
216
- _context2.next = 7;
218
+ _context2.next = 9;
217
219
  return this.flush();
218
- case 7:
220
+ case 9:
221
+ if (this.shouldSave) {
222
+ _context2.next = 11;
223
+ break;
224
+ }
225
+ return _context2.abrupt("return");
226
+ case 11:
219
227
  this.emit('before-save');
220
228
  index = Object.assign({
221
229
  data: {}
@@ -226,12 +234,14 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
226
234
  }
227
235
  }
228
236
  offsets = this.offsets.slice(0);
229
- _context2.next = 13;
237
+ _context2.next = 17;
230
238
  return this.serializer.serialize(index, {
231
- compress: this.opts.compressIndex
239
+ compress: this.opts.compressIndex,
240
+ linebreak: true
232
241
  });
233
- case 13:
242
+ case 17:
234
243
  indexString = _context2.sent;
244
+ // force linebreak here to allow 'init' to read last line as offsets correctly
235
245
  for (_field in this.indexManager.index.data) {
236
246
  for (_term in this.indexManager.index.data[_field]) {
237
247
  this.indexManager.index.data[_field][_term] = new Set(index.data[_field][_term]); // set back to set because of serialization
@@ -239,22 +249,15 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
239
249
  }
240
250
  offsets.push(this.indexOffset);
241
251
  offsets.push(this.indexOffset + indexString.length);
242
- _context2.next = 19;
252
+ // save offsets as JSON always to prevent linebreaks on last line, which breaks 'init()'
253
+ _context2.next = 23;
243
254
  return this.serializer.serialize(offsets, {
244
- compress: this.opts.compressIndex,
255
+ json: true,
256
+ compress: false,
245
257
  linebreak: false
246
258
  });
247
- case 19:
248
- offsetsString = _context2.sent;
249
- if (!this.shouldTruncate) {
250
- _context2.next = 24;
251
- break;
252
- }
253
- _context2.next = 23;
254
- return this.fileHandler.truncate(this.indexOffset);
255
259
  case 23:
256
- this.shouldTruncate = false;
257
- case 24:
260
+ offsetsString = _context2.sent;
258
261
  this.writeBuffer.push(indexString);
259
262
  this.writeBuffer.push(offsetsString);
260
263
  _context2.next = 28;
@@ -390,17 +393,24 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
390
393
  }
391
394
  throw new Error('Database is destroyed');
392
395
  case 2:
393
- _context5.next = 4;
396
+ if (this.initialized) {
397
+ _context5.next = 5;
398
+ break;
399
+ }
400
+ _context5.next = 5;
401
+ return this.init();
402
+ case 5:
403
+ if (this.shouldTruncate) {
404
+ this.writeBuffer.push(this.indexOffset);
405
+ this.shouldTruncate = false;
406
+ }
407
+ _context5.next = 8;
394
408
  return this.serializer.serialize(data, {
395
409
  compress: this.opts.compress
396
410
  });
397
- case 4:
411
+ case 8:
398
412
  line = _context5.sent;
399
413
  // using Buffer for offsets accuracy
400
- if (this.shouldTruncate) {
401
- this.writeBuffer.push(this.indexOffset);
402
- this.shouldTruncate = false;
403
- }
404
414
  position = this.offsets.length;
405
415
  this.offsets.push(this.indexOffset);
406
416
  this.indexOffset += line.length;
@@ -408,14 +418,14 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
408
418
  this.emit('insert', data, position);
409
419
  this.writeBuffer.push(line);
410
420
  if (!(!this.flushing && this.currentWriteBufferSize() > this.opts.maxMemoryUsage)) {
411
- _context5.next = 15;
421
+ _context5.next = 18;
412
422
  break;
413
423
  }
414
- _context5.next = 15;
424
+ _context5.next = 18;
415
425
  return this.flush();
416
- case 15:
426
+ case 18:
417
427
  this.shouldSave = true;
418
- case 16:
428
+ case 19:
419
429
  case "end":
420
430
  return _context5.stop();
421
431
  }
@@ -442,16 +452,18 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
442
452
  key: "flush",
443
453
  value: function flush() {
444
454
  var _this7 = this;
445
- if (this.flushing) return this.flushing;
446
- return new Promise(function (resolve, reject) {
455
+ if (this.flushing) {
456
+ return this.flushing;
457
+ }
458
+ return this.flushing = new Promise(function (resolve, reject) {
447
459
  if (_this7.destroyed) return reject(new Error('Database is destroyed'));
448
460
  if (!_this7.writeBuffer.length) return resolve();
449
461
  var err;
450
- _this7.flushing = _this7._flush()["catch"](function (e) {
462
+ _this7._flush()["catch"](function (e) {
451
463
  return err = e;
452
464
  })["finally"](function () {
453
- _this7.flushing = false;
454
465
  err ? reject(err) : resolve();
466
+ _this7.flushing = false;
455
467
  });
456
468
  });
457
469
  }
@@ -464,7 +476,7 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
464
476
  while (1) switch (_context6.prev = _context6.next) {
465
477
  case 0:
466
478
  _context6.next = 2;
467
- return _fs["default"].promises.open(this.fileHandler.filePath, 'a');
479
+ return _fs["default"].promises.open(this.fileHandler.file, 'a');
468
480
  case 2:
469
481
  fd = _context6.sent;
470
482
  _context6.prev = 3;
@@ -488,7 +500,7 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
488
500
  return this.fileHandler.truncate(this.writeBuffer.shift());
489
501
  case 12:
490
502
  _context6.next = 14;
491
- return _fs["default"].promises.open(this.fileHandler.filePath, 'a');
503
+ return _fs["default"].promises.open(this.fileHandler.file, 'a');
492
504
  case 14:
493
505
  fd = _context6.sent;
494
506
  return _context6.abrupt("continue", 4);
@@ -547,20 +559,27 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
547
559
  }
548
560
  throw new Error('Database is destroyed');
549
561
  case 2:
562
+ if (_this.initialized) {
563
+ _context8.next = 5;
564
+ break;
565
+ }
566
+ _context8.next = 5;
567
+ return _awaitAsyncGenerator(_this.init());
568
+ case 5:
550
569
  _context8.t0 = _this.shouldSave;
551
570
  if (!_context8.t0) {
552
- _context8.next = 6;
571
+ _context8.next = 9;
553
572
  break;
554
573
  }
555
- _context8.next = 6;
574
+ _context8.next = 9;
556
575
  return _awaitAsyncGenerator(_this.save()["catch"](console.error));
557
- case 6:
576
+ case 9:
558
577
  if (!(_this.indexOffset === 0)) {
559
- _context8.next = 8;
578
+ _context8.next = 11;
560
579
  break;
561
580
  }
562
581
  return _context8.abrupt("return");
563
- case 8:
582
+ case 11:
564
583
  if (!Array.isArray(map)) {
565
584
  if (map && _typeof(map) === 'object') {
566
585
  map = _this.indexManager.query(map, options.matchAny);
@@ -581,15 +600,15 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
581
600
  }
582
601
  m = 0;
583
602
  _i = 0, _partitionedRanges = partitionedRanges;
584
- case 14:
603
+ case 17:
585
604
  if (!(_i < _partitionedRanges.length)) {
586
- _context8.next = 31;
605
+ _context8.next = 34;
587
606
  break;
588
607
  }
589
608
  _ranges = _partitionedRanges[_i];
590
- _context8.next = 18;
609
+ _context8.next = 21;
591
610
  return _awaitAsyncGenerator(_this.fileHandler.readRanges(_ranges));
592
- case 18:
611
+ case 21:
593
612
  lines = _context8.sent;
594
613
  _loop = /*#__PURE__*/_regeneratorRuntime().mark(function _loop() {
595
614
  var err, entry;
@@ -621,27 +640,27 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
621
640
  }, _loop);
622
641
  });
623
642
  _context8.t1 = _regeneratorRuntime().keys(lines);
624
- case 21:
643
+ case 24:
625
644
  if ((_context8.t2 = _context8.t1()).done) {
626
- _context8.next = 28;
645
+ _context8.next = 31;
627
646
  break;
628
647
  }
629
648
  _line = _context8.t2.value;
630
- return _context8.delegateYield(_loop(), "t3", 24);
631
- case 24:
649
+ return _context8.delegateYield(_loop(), "t3", 27);
650
+ case 27:
632
651
  if (!_context8.t3) {
633
- _context8.next = 26;
652
+ _context8.next = 29;
634
653
  break;
635
654
  }
636
- return _context8.abrupt("continue", 21);
637
- case 26:
638
- _context8.next = 21;
655
+ return _context8.abrupt("continue", 24);
656
+ case 29:
657
+ _context8.next = 24;
639
658
  break;
640
- case 28:
659
+ case 31:
641
660
  _i++;
642
- _context8.next = 14;
661
+ _context8.next = 17;
643
662
  break;
644
- case 31:
663
+ case 34:
645
664
  case "end":
646
665
  return _context8.stop();
647
666
  }
@@ -671,21 +690,28 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
671
690
  }
672
691
  throw new Error('Database is destroyed');
673
692
  case 3:
693
+ if (this.initialized) {
694
+ _context9.next = 6;
695
+ break;
696
+ }
697
+ _context9.next = 6;
698
+ return this.init();
699
+ case 6:
674
700
  _context9.t0 = this.shouldSave;
675
701
  if (!_context9.t0) {
676
- _context9.next = 7;
702
+ _context9.next = 10;
677
703
  break;
678
704
  }
679
- _context9.next = 7;
705
+ _context9.next = 10;
680
706
  return this.save()["catch"](console.error);
681
- case 7:
707
+ case 10:
682
708
  if (!Array.isArray(criteria)) {
683
- _context9.next = 16;
709
+ _context9.next = 19;
684
710
  break;
685
711
  }
686
- _context9.next = 10;
712
+ _context9.next = 13;
687
713
  return this.readLines(criteria);
688
- case 10:
714
+ case 13:
689
715
  results = _context9.sent;
690
716
  if (options.orderBy) {
691
717
  _options$orderBy$spli = options.orderBy.split(' '), _options$orderBy$spli2 = _slicedToArray(_options$orderBy$spli, 2), field = _options$orderBy$spli2[0], _options$orderBy$spli3 = _options$orderBy$spli2[1], direction = _options$orderBy$spli3 === void 0 ? 'asc' : _options$orderBy$spli3;
@@ -699,22 +725,22 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
699
725
  results = results.slice(0, options.limit);
700
726
  }
701
727
  return _context9.abrupt("return", results);
702
- case 16:
703
- _context9.next = 18;
728
+ case 19:
729
+ _context9.next = 21;
704
730
  return this.indexManager.query(criteria, options.matchAny);
705
- case 18:
731
+ case 21:
706
732
  matchingLines = _context9.sent;
707
733
  if (!(!matchingLines || !matchingLines.size)) {
708
- _context9.next = 21;
734
+ _context9.next = 24;
709
735
  break;
710
736
  }
711
737
  return _context9.abrupt("return", []);
712
- case 21:
713
- _context9.next = 23;
738
+ case 24:
739
+ _context9.next = 26;
714
740
  return this.query(_toConsumableArray(matchingLines), options);
715
- case 23:
741
+ case 26:
716
742
  return _context9.abrupt("return", _context9.sent);
717
- case 24:
743
+ case 27:
718
744
  case "end":
719
745
  return _context9.stop();
720
746
  }
@@ -747,47 +773,58 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
747
773
  while (1) switch (_context11.prev = _context11.next) {
748
774
  case 0:
749
775
  options = _args11.length > 2 && _args11[2] !== undefined ? _args11[2] : {};
776
+ if (this.shouldTruncate) {
777
+ this.writeBuffer.push(this.indexOffset);
778
+ this.shouldTruncate = false;
779
+ }
750
780
  if (!this.destroyed) {
751
- _context11.next = 3;
781
+ _context11.next = 4;
752
782
  break;
753
783
  }
754
784
  throw new Error('Database is destroyed');
755
- case 3:
756
- _context11.t0 = this.shouldSave;
757
- if (!_context11.t0) {
785
+ case 4:
786
+ if (this.initialized) {
758
787
  _context11.next = 7;
759
788
  break;
760
789
  }
761
790
  _context11.next = 7;
762
- return this.save()["catch"](console.error);
791
+ return this.init();
763
792
  case 7:
764
- _context11.next = 9;
793
+ _context11.t0 = this.shouldSave;
794
+ if (!_context11.t0) {
795
+ _context11.next = 11;
796
+ break;
797
+ }
798
+ _context11.next = 11;
799
+ return this.save()["catch"](console.error);
800
+ case 11:
801
+ _context11.next = 13;
765
802
  return this.indexManager.query(criteria, options.matchAny);
766
- case 9:
803
+ case 13:
767
804
  matchingLines = _context11.sent;
768
805
  if (!(!matchingLines || !matchingLines.size)) {
769
- _context11.next = 12;
806
+ _context11.next = 16;
770
807
  break;
771
808
  }
772
809
  return _context11.abrupt("return", []);
773
- case 12:
810
+ case 16:
774
811
  ranges = this.getRanges(_toConsumableArray(matchingLines));
775
812
  validMatchingLines = new Set(ranges.map(function (r) {
776
813
  return r.index;
777
814
  }));
778
815
  if (validMatchingLines.size) {
779
- _context11.next = 16;
816
+ _context11.next = 20;
780
817
  break;
781
818
  }
782
819
  return _context11.abrupt("return", []);
783
- case 16:
784
- _context11.next = 18;
820
+ case 20:
821
+ _context11.next = 22;
785
822
  return this.readLines(_toConsumableArray(validMatchingLines), ranges);
786
- case 18:
823
+ case 22:
787
824
  entries = _context11.sent;
788
825
  lines = [];
789
826
  _iterator = _createForOfIteratorHelper(entries);
790
- _context11.prev = 21;
827
+ _context11.prev = 25;
791
828
  _loop2 = /*#__PURE__*/_regeneratorRuntime().mark(function _loop2() {
792
829
  var entry, err, updated, ret;
793
830
  return _regeneratorRuntime().wrap(function _loop2$(_context10) {
@@ -809,27 +846,27 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
809
846
  }, _loop2);
810
847
  });
811
848
  _iterator.s();
812
- case 24:
849
+ case 28:
813
850
  if ((_step = _iterator.n()).done) {
814
- _context11.next = 28;
851
+ _context11.next = 32;
815
852
  break;
816
853
  }
817
- return _context11.delegateYield(_loop2(), "t1", 26);
818
- case 26:
819
- _context11.next = 24;
854
+ return _context11.delegateYield(_loop2(), "t1", 30);
855
+ case 30:
856
+ _context11.next = 28;
820
857
  break;
821
- case 28:
822
- _context11.next = 33;
858
+ case 32:
859
+ _context11.next = 37;
823
860
  break;
824
- case 30:
825
- _context11.prev = 30;
826
- _context11.t2 = _context11["catch"](21);
861
+ case 34:
862
+ _context11.prev = 34;
863
+ _context11.t2 = _context11["catch"](25);
827
864
  _iterator.e(_context11.t2);
828
- case 33:
829
- _context11.prev = 33;
865
+ case 37:
866
+ _context11.prev = 37;
830
867
  _iterator.f();
831
- return _context11.finish(33);
832
- case 36:
868
+ return _context11.finish(37);
869
+ case 40:
833
870
  offsets = [];
834
871
  byteOffset = 0, k = 0;
835
872
  this.offsets.forEach(function (n, i) {
@@ -843,20 +880,20 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
843
880
  });
844
881
  this.offsets = offsets;
845
882
  this.indexOffset += byteOffset;
846
- _context11.next = 43;
883
+ _context11.next = 47;
847
884
  return this.fileHandler.replaceLines(ranges, lines);
848
- case 43:
885
+ case 47:
849
886
  _toConsumableArray(validMatchingLines).forEach(function (lineNumber, i) {
850
887
  _this8.indexManager.dryRemove(lineNumber);
851
888
  _this8.indexManager.add(entries[i], lineNumber);
852
889
  });
853
890
  this.shouldSave = true;
854
891
  return _context11.abrupt("return", entries);
855
- case 46:
892
+ case 50:
856
893
  case "end":
857
894
  return _context11.stop();
858
895
  }
859
- }, _callee9, this, [[21, 30, 33, 36]]);
896
+ }, _callee9, this, [[25, 34, 37, 40]]);
860
897
  }));
861
898
  function update(_x5, _x6) {
862
899
  return _update.apply(this, arguments);
@@ -879,37 +916,48 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
879
916
  while (1) switch (_context12.prev = _context12.next) {
880
917
  case 0:
881
918
  options = _args12.length > 1 && _args12[1] !== undefined ? _args12[1] : {};
919
+ if (this.shouldTruncate) {
920
+ this.writeBuffer.push(this.indexOffset);
921
+ this.shouldTruncate = false;
922
+ }
882
923
  if (!this.destroyed) {
883
- _context12.next = 3;
924
+ _context12.next = 4;
884
925
  break;
885
926
  }
886
927
  throw new Error('Database is destroyed');
887
- case 3:
888
- _context12.t0 = this.shouldSave;
889
- if (!_context12.t0) {
928
+ case 4:
929
+ if (this.initialized) {
890
930
  _context12.next = 7;
891
931
  break;
892
932
  }
893
933
  _context12.next = 7;
894
- return this.save()["catch"](console.error);
934
+ return this.init();
895
935
  case 7:
896
- _context12.next = 9;
936
+ _context12.t0 = this.shouldSave;
937
+ if (!_context12.t0) {
938
+ _context12.next = 11;
939
+ break;
940
+ }
941
+ _context12.next = 11;
942
+ return this.save()["catch"](console.error);
943
+ case 11:
944
+ _context12.next = 13;
897
945
  return this.indexManager.query(criteria, options.matchAny);
898
- case 9:
946
+ case 13:
899
947
  matchingLines = _context12.sent;
900
948
  if (!(!matchingLines || !matchingLines.size)) {
901
- _context12.next = 12;
949
+ _context12.next = 16;
902
950
  break;
903
951
  }
904
952
  return _context12.abrupt("return", 0);
905
- case 12:
953
+ case 16:
906
954
  ranges = this.getRanges(_toConsumableArray(matchingLines));
907
955
  validMatchingLines = new Set(ranges.map(function (r) {
908
956
  return r.index;
909
957
  }));
910
- _context12.next = 16;
958
+ _context12.next = 20;
911
959
  return this.fileHandler.replaceLines(ranges, []);
912
- case 16:
960
+ case 20:
913
961
  offsets = [];
914
962
  byteOffset = 0, k = 0;
915
963
  this.offsets.forEach(function (n, i) {
@@ -926,7 +974,7 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
926
974
  this.indexManager.remove(_toConsumableArray(validMatchingLines));
927
975
  this.shouldSave = true;
928
976
  return _context12.abrupt("return", ranges.length);
929
- case 24:
977
+ case 28:
930
978
  case "end":
931
979
  return _context12.stop();
932
980
  }
@@ -972,7 +1020,8 @@ var Database = exports.Database = /*#__PURE__*/function (_EventEmitter) {
972
1020
  }, {
973
1021
  key: "length",
974
1022
  get: function get() {
975
- return this.offsets.length;
1023
+ var _this$offsets;
1024
+ return (this === null || this === void 0 || (_this$offsets = this.offsets) === null || _this$offsets === void 0 ? void 0 : _this$offsets.length) || 0;
976
1025
  }
977
1026
  }, {
978
1027
  key: "index",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jexidb",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "JexiDB is a pure JS NPM library for managing data on disk using JSONL efficiently, without the need for a server.",
5
5
  "main": "./dist/Database.cjs",
6
6
  "module": "./src/Database.mjs",
@@ -11,7 +11,7 @@
11
11
  }
12
12
  },
13
13
  "scripts": {
14
- "test": "node test/test.mjs && exit 1",
14
+ "test": "node --expose-gc test/test.mjs && exit 1",
15
15
  "build": "npx babel src/Database.mjs --plugins @babel/plugin-transform-async-generator-functions --out-file-extension .cjs --out-dir dist"
16
16
  },
17
17
  "author": "EdenwareApps",
package/src/Database.mjs CHANGED
@@ -1,12 +1,11 @@
1
1
  import { EventEmitter } from 'events'
2
2
  import FileHandler from './FileHandler.mjs'
3
3
  import IndexManager from './IndexManager.mjs'
4
- import Serializer from './serializers/Simple.mjs'
5
- import AdvancedSerializer from './serializers/Advanced.mjs'
4
+ import Serializer from './Serializer.mjs'
6
5
  import fs from 'fs'
7
6
 
8
7
  export class Database extends EventEmitter {
9
- constructor(filePath, opts={}) {
8
+ constructor(file, opts={}) {
10
9
  super()
11
10
  this.opts = Object.assign({
12
11
  v8: false,
@@ -16,13 +15,10 @@ export class Database extends EventEmitter {
16
15
  compressIndex: false,
17
16
  maxMemoryUsage: 64 * 1024 // 64KB
18
17
  }, opts)
18
+ this.offsets = []
19
19
  this.shouldSave = false
20
- if(this.opts.v8 || this.opts.compress || this.opts.compressIndex) {
21
- this.serializer = new AdvancedSerializer(this.opts)
22
- } else {
23
- this.serializer = new Serializer(this.opts)
24
- }
25
- this.fileHandler = new FileHandler(filePath)
20
+ this.serializer = new Serializer(this.opts)
21
+ this.fileHandler = new FileHandler(file)
26
22
  this.indexManager = new IndexManager(this.opts)
27
23
  this.indexOffset = 0
28
24
  this.writeBuffer = []
@@ -60,7 +56,7 @@ export class Database extends EventEmitter {
60
56
  const index = await this.serializer.deserialize(indexLine, {compress: this.opts.compressIndex})
61
57
  index && this.indexManager.load(index)
62
58
  } catch (e) {
63
- if(!this.offsets) {
59
+ if(Array.isArray(this.offsets)) {
64
60
  this.offsets = []
65
61
  }
66
62
  this.indexOffset = 0
@@ -76,9 +72,11 @@ export class Database extends EventEmitter {
76
72
 
77
73
  async save() {
78
74
  if(this.destroyed) throw new Error('Database is destroyed')
75
+ if(!this.initialized) throw new Error('Database not initialized')
79
76
  if(this.saving) return new Promise(resolve => this.once('save', resolve))
80
77
  this.saving = true
81
78
  await this.flush()
79
+ if (!this.shouldSave) return
82
80
  this.emit('before-save')
83
81
  const index = Object.assign({data: {}}, this.indexManager.index)
84
82
  for(const field in this.indexManager.index.data) {
@@ -87,7 +85,7 @@ export class Database extends EventEmitter {
87
85
  }
88
86
  }
89
87
  const offsets = this.offsets.slice(0)
90
- const indexString = await this.serializer.serialize(index, {compress: this.opts.compressIndex})
88
+ const indexString = await this.serializer.serialize(index, {compress: this.opts.compressIndex, linebreak: true}) // force linebreak here to allow 'init' to read last line as offsets correctly
91
89
  for(const field in this.indexManager.index.data) {
92
90
  for(const term in this.indexManager.index.data[field]) {
93
91
  this.indexManager.index.data[field][term] = new Set(index.data[field][term]) // set back to set because of serialization
@@ -95,11 +93,8 @@ export class Database extends EventEmitter {
95
93
  }
96
94
  offsets.push(this.indexOffset)
97
95
  offsets.push(this.indexOffset + indexString.length)
98
- const offsetsString = await this.serializer.serialize(offsets, {compress: this.opts.compressIndex, linebreak: false})
99
- if (this.shouldTruncate) {
100
- await this.fileHandler.truncate(this.indexOffset)
101
- this.shouldTruncate = false
102
- }
96
+ // save offsets as JSON always to prevent linebreaks on last line, which breaks 'init()'
97
+ const offsetsString = await this.serializer.serialize(offsets, {json: true, compress: false, linebreak: false})
103
98
  this.writeBuffer.push(indexString)
104
99
  this.writeBuffer.push(offsetsString)
105
100
  await this.flush() // write the index and offsets
@@ -122,7 +117,7 @@ export class Database extends EventEmitter {
122
117
  }
123
118
  return
124
119
  }
125
- let end = this.offsets[n + 1] || this.indexOffset || Number.MAX_SAFE_INTEGER
120
+ let end = (this.offsets[n + 1] || this.indexOffset || Number.MAX_SAFE_INTEGER)
126
121
  return [this.offsets[n], end]
127
122
  }
128
123
 
@@ -147,11 +142,12 @@ export class Database extends EventEmitter {
147
142
 
148
143
  async insert(data) {
149
144
  if(this.destroyed) throw new Error('Database is destroyed')
150
- const line = await this.serializer.serialize(data, {compress: this.opts.compress}) // using Buffer for offsets accuracy
145
+ if(!this.initialized) await this.init()
151
146
  if (this.shouldTruncate) {
152
147
  this.writeBuffer.push(this.indexOffset)
153
148
  this.shouldTruncate = false
154
149
  }
150
+ const line = await this.serializer.serialize(data, {compress: this.opts.compress}) // using Buffer for offsets accuracy
155
151
  const position = this.offsets.length
156
152
  this.offsets.push(this.indexOffset)
157
153
  this.indexOffset += line.length
@@ -170,20 +166,22 @@ export class Database extends EventEmitter {
170
166
  }
171
167
 
172
168
  flush() {
173
- if(this.flushing) return this.flushing
174
- return new Promise((resolve, reject) => {
169
+ if(this.flushing) {
170
+ return this.flushing
171
+ }
172
+ return this.flushing = new Promise((resolve, reject) => {
175
173
  if(this.destroyed) return reject(new Error('Database is destroyed'))
176
174
  if(!this.writeBuffer.length) return resolve()
177
175
  let err
178
- this.flushing = this._flush().catch(e => err = e).finally(() => {
179
- this.flushing = false
176
+ this._flush().catch(e => err = e).finally(() => {
180
177
  err ? reject(err) : resolve()
178
+ this.flushing = false
181
179
  })
182
180
  })
183
181
  }
184
182
 
185
183
  async _flush() {
186
- let fd = await fs.promises.open(this.fileHandler.filePath, 'a')
184
+ let fd = await fs.promises.open(this.fileHandler.file, 'a')
187
185
  try {
188
186
  while(this.writeBuffer.length) {
189
187
  let data
@@ -191,7 +189,7 @@ export class Database extends EventEmitter {
191
189
  if(pos === 0) {
192
190
  await fd.close()
193
191
  await this.fileHandler.truncate(this.writeBuffer.shift())
194
- fd = await fs.promises.open(this.fileHandler.filePath, 'a')
192
+ fd = await fs.promises.open(this.fileHandler.file, 'a')
195
193
  continue
196
194
  } else if(pos === -1) {
197
195
  data = Buffer.concat(this.writeBuffer)
@@ -212,6 +210,7 @@ export class Database extends EventEmitter {
212
210
 
213
211
  async *walk(map, options={}) {
214
212
  if(this.destroyed) throw new Error('Database is destroyed')
213
+ if(!this.initialized) await this.init()
215
214
  this.shouldSave && await this.save().catch(console.error)
216
215
  if(this.indexOffset === 0) return
217
216
  if(!Array.isArray(map)) {
@@ -250,6 +249,7 @@ export class Database extends EventEmitter {
250
249
 
251
250
  async query(criteria, options={}) {
252
251
  if(this.destroyed) throw new Error('Database is destroyed')
252
+ if(!this.initialized) await this.init()
253
253
  this.shouldSave && await this.save().catch(console.error)
254
254
  if(Array.isArray(criteria)) {
255
255
  let results = await this.readLines(criteria)
@@ -275,7 +275,12 @@ export class Database extends EventEmitter {
275
275
  }
276
276
 
277
277
  async update(criteria, data, options={}) {
278
+ if (this.shouldTruncate) {
279
+ this.writeBuffer.push(this.indexOffset)
280
+ this.shouldTruncate = false
281
+ }
278
282
  if(this.destroyed) throw new Error('Database is destroyed')
283
+ if(!this.initialized) await this.init()
279
284
  this.shouldSave && await this.save().catch(console.error)
280
285
  const matchingLines = await this.indexManager.query(criteria, options.matchAny)
281
286
  if (!matchingLines || !matchingLines.size) {
@@ -317,7 +322,12 @@ export class Database extends EventEmitter {
317
322
  }
318
323
 
319
324
  async delete(criteria, options={}) {
325
+ if (this.shouldTruncate) {
326
+ this.writeBuffer.push(this.indexOffset)
327
+ this.shouldTruncate = false
328
+ }
320
329
  if(this.destroyed) throw new Error('Database is destroyed')
330
+ if(!this.initialized) await this.init()
321
331
  this.shouldSave && await this.save().catch(console.error)
322
332
  const matchingLines = await this.indexManager.query(criteria, options.matchAny)
323
333
  if (!matchingLines || !matchingLines.size) {
@@ -355,7 +365,7 @@ export class Database extends EventEmitter {
355
365
  }
356
366
 
357
367
  get length() {
358
- return this.offsets.length
368
+ return this?.offsets?.length || 0
359
369
  }
360
370
 
361
371
  get index() {
@@ -2,21 +2,21 @@ import fs from 'fs'
2
2
  import pLimit from 'p-limit'
3
3
 
4
4
  export default class FileHandler {
5
- constructor(filePath) {
6
- this.filePath = filePath
5
+ constructor(file) {
6
+ this.file = file
7
7
  }
8
8
 
9
9
  async truncate(offset) {
10
10
  try {
11
- await fs.promises.access(this.filePath, fs.constants.F_OK)
12
- await fs.promises.truncate(this.filePath, offset)
11
+ await fs.promises.access(this.file, fs.constants.F_OK)
12
+ await fs.promises.truncate(this.file, offset)
13
13
  } catch (err) {
14
- await fs.promises.writeFile(this.filePath, '')
14
+ await fs.promises.writeFile(this.file, '')
15
15
  }
16
16
  }
17
17
 
18
18
  async readRange(start, end) {
19
- let fd = await fs.promises.open(this.filePath, 'r')
19
+ let fd = await fs.promises.open(this.file, 'r')
20
20
  const length = end - start
21
21
  let buffer = Buffer.alloc(length)
22
22
  const { bytesRead } = await fd.read(buffer, 0, length, start).catch(console.error)
@@ -27,7 +27,7 @@ export default class FileHandler {
27
27
 
28
28
  async readRanges(ranges, mapper) {
29
29
  const lines = {}, limit = pLimit(4)
30
- const fd = await fs.promises.open(this.filePath, 'r')
30
+ const fd = await fs.promises.open(this.file, 'r')
31
31
  try {
32
32
  const tasks = ranges.map(r => {
33
33
  return async () => {
@@ -50,9 +50,9 @@ export default class FileHandler {
50
50
 
51
51
  async replaceLines(ranges, lines) {
52
52
  let closed
53
- const tmpFile = this.filePath + '.tmp'
53
+ const tmpFile = this.file + '.tmp'
54
54
  const writer = await fs.promises.open(tmpFile, 'w+')
55
- const reader = await fs.promises.open(this.filePath, 'r')
55
+ const reader = await fs.promises.open(this.file, 'r')
56
56
  try {
57
57
  let i = 0, start = 0
58
58
  for (const r of ranges) {
@@ -74,7 +74,7 @@ export default class FileHandler {
74
74
  await reader.close()
75
75
  await writer.close()
76
76
  closed = true
77
- await fs.promises.copyFile(tmpFile, this.filePath)
77
+ await fs.promises.copyFile(tmpFile, this.file)
78
78
  } catch (e) {
79
79
  console.error('Error replacing lines:', e)
80
80
  } finally {
@@ -90,11 +90,11 @@ export default class FileHandler {
90
90
  }
91
91
 
92
92
  writeDataSync(data) {
93
- fs.writeFileSync(this.filePath, data, { flag: 'a' })
93
+ fs.writeFileSync(this.file, data, { flag: 'a' })
94
94
  }
95
95
 
96
96
  async readLastLine() {
97
- const reader = await fs.promises.open(this.filePath, 'r')
97
+ const reader = await fs.promises.open(this.file, 'r')
98
98
  try {
99
99
  const { size } = await reader.stat()
100
100
  if (size < 1) throw 'empty file'
@@ -6,9 +6,6 @@ export default class Serializer extends EventEmitter {
6
6
  constructor(opts = {}) {
7
7
  super()
8
8
  this.opts = Object.assign({}, opts)
9
- this.linebreak = Buffer.from([0x0A])
10
- this.delimiter = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF])
11
- this.defaultBuffer = Buffer.alloc(4096)
12
9
  this.brotliOptions = {
13
10
  params: {
14
11
  [zlib.constants.BROTLI_PARAM_QUALITY]: 4
@@ -19,56 +16,54 @@ export default class Serializer extends EventEmitter {
19
16
  async serialize(data, opts={}) {
20
17
  let line
21
18
  let header = 0x00 // 1 byte de header
22
- const useV8 = this.opts.v8 || opts.v8 === true
23
- const compress = this.opts.compress || opts.compress === true
19
+ const useV8 = (this.opts.v8 && opts.json !== true) || opts.v8 === true
20
+ const compress = (this.opts.compress && opts.compress !== false) || opts.compress === true
21
+ const addLinebreak = opts.linebreak !== false || (!useV8 && !compress && opts.linebreak !== false)
24
22
  if (useV8) {
25
23
  header |= 0x02 // set V8
26
24
  line = v8.serialize(data)
27
25
  } else {
28
- if(compress) {
29
- line = Buffer.from(JSON.stringify(data), 'utf-8')
30
- } else {
31
- return Buffer.from(JSON.stringify(data) + (opts.linebreak !== false ? '\n' : ''), 'utf-8')
32
- }
26
+ let json = JSON.stringify(data)
27
+ line = Buffer.from(json, 'utf-8')
33
28
  }
34
29
  if (compress) {
35
30
  let err
36
- const buffer = await this.compress(line).catch(e => err = e)
37
- if(!err) {
31
+ const compressionType = useV8 ? 'deflate' : 'brotli'
32
+ const buffer = await this.compress(line, compressionType).catch(e => err = e)
33
+ if(!err && buffer.length && buffer.length < line.length) {
38
34
  header |= 0x01
39
35
  line = buffer
40
36
  }
41
37
  }
42
- const totalLength = 1 + line.length + (opts.linebreak !== false ? 1 : 0)
38
+ const totalLength = 1 + line.length + (addLinebreak ? 1 : 0)
43
39
  const result = Buffer.alloc(totalLength)
44
40
  result[0] = header
45
- line.copy(result, 1)
46
- if (opts.linebreak !== false) {
47
- result[totalLength - 1] = 0x0A
41
+ line.copy(result, 1, 0, line.length)
42
+ if(addLinebreak) {
43
+ result[result.length - 1] = 0x0A
48
44
  }
49
45
  return result
50
46
  }
51
47
 
52
- async deserialize(data, opts={}) {
53
- let line
48
+ async deserialize(data) {
49
+ let line, isCompressed, isV8
54
50
  const header = data.readUInt8(0)
55
51
  const valid = header === 0x00 || header === 0x01 || header === 0x02 || header === 0x03
56
- let isCompressed, isV8, decompresssed
57
52
  if(valid) {
58
53
  isCompressed = (header & 0x01) === 0x01
59
54
  isV8 = (header & 0x02) === 0x02
60
55
  line = data.subarray(1) // remove byte header
61
56
  } else {
62
57
  isCompressed = isV8 = false
63
- line = data
58
+ try {
59
+ return JSON.parse(data.toString('utf-8').trim())
60
+ } catch (e) {
61
+ throw new Error('Failed to deserialize legacy JSON data')
62
+ }
64
63
  }
65
64
  if (isCompressed) {
66
- let err
67
- const buffer = await this.decompress(line).catch(e => err = e)
68
- if(!err) {
69
- decompresssed = true
70
- line = buffer
71
- }
65
+ const compressionType = isV8 ? 'deflate' : 'brotli'
66
+ line = await this.decompress(line, compressionType).catch(e => err = e)
72
67
  }
73
68
  if (isV8) {
74
69
  try {
@@ -80,41 +75,43 @@ export default class Serializer extends EventEmitter {
80
75
  try {
81
76
  return JSON.parse(line.toString('utf-8').trim())
82
77
  } catch (e) {
83
- console.error('Failed to deserialize', header, line.toString('utf-8').trim())
84
78
  throw new Error('Failed to deserialize JSON data')
85
79
  }
86
80
  }
87
81
  }
88
82
 
89
- compress(data) {
83
+ compress(data, type) {
90
84
  return new Promise((resolve, reject) => {
91
- zlib.brotliCompress(data, this.brotliOptions, (err, buffer) => {
85
+ const callback = (err, buffer) => {
92
86
  if (err) {
93
87
  reject(err)
94
88
  } else {
95
89
  resolve(buffer)
96
90
  }
97
- })
91
+ }
92
+ if(type === 'brotli') {
93
+ zlib.brotliCompress(data, this.brotliOptions, callback)
94
+ } else {
95
+ zlib.deflate(data, callback)
96
+ }
98
97
  })
99
98
  }
100
99
 
101
- decompress(data) {
100
+ decompress(data, type) {
102
101
  return new Promise((resolve, reject) => {
103
- zlib.brotliDecompress(data, (err, buffer) => {
102
+ const callback = (err, buffer) => {
104
103
  if (err) {
105
104
  reject(err)
106
105
  } else {
107
106
  resolve(buffer)
108
107
  }
109
- })
108
+ }
109
+ if(type === 'brotli') {
110
+ zlib.brotliDecompress(data, callback)
111
+ } else {
112
+ zlib.inflate(data, callback)
113
+ }
110
114
  })
111
115
  }
112
-
113
- async safeDeserialize(json) {
114
- try {
115
- return await this.deserialize(json)
116
- } catch (e) {
117
- return null
118
- }
119
- }
116
+
120
117
  }
package/test/README.md ADDED
@@ -0,0 +1,13 @@
1
+ ## Test Results
2
+ The following are the results of the automated tests conducted on my PC for different serialization formats and compression methods.
3
+
4
+ | Format | Size (bytes) | Time elapsed (ms) |
5
+ |-------------------------------|--------------|--------------------|
6
+ | JSON | 1117 | 21 |
7
+ | V8 Serialization | 1124 | 12 &#127942; |
8
+ | JSON with Brotli Compression | 1038 &#127942; | 20 |
9
+ | V8 with Brotli Compression | 1052 | 15 |
10
+
11
+ When using V8 for serialization, be aware that serialized data may not deserialize correctly across different versions of V8 (which powers Chromium, Node.js, and Electron), leading to potential data loss or corruption. This issue is specific to V8 serialization; however, when using JSON, the data remains universal and compatible across environments.
12
+
13
+ To avoid this issue, always use your V8 database with the same version of Node.js.
Binary file
Binary file
Binary file
package/test/test-v8.jdb CHANGED
Binary file
package/test/test.mjs CHANGED
@@ -50,13 +50,8 @@ const runTests = async (id, name, format, opts) => {
50
50
  // Path to the test file
51
51
  const testFilePath = path.join(__dirname, 'test-' + name + '.jdb');
52
52
 
53
- // Function to clear the test file before each run
54
- const clearTestFile = () => {
55
- fs.writeFileSync(testFilePath, '', { encoding: null });
56
- }
57
-
58
53
  console.log('Battle #' + id + ' (' + format + ') is starting...\n');
59
- clearTestFile(); // Clear the file before starting the tests
54
+ fs.writeFileSync(testFilePath, '', { encoding: null }); // Clear the file before starting the tests
60
55
  const start = Date.now()
61
56
  const db = new Database(testFilePath, opts); // Instantiate the database
62
57
 
@@ -97,7 +92,6 @@ const runTests = async (id, name, format, opts) => {
97
92
 
98
93
  // 5. Test data deletion
99
94
  await db.delete({ name: character.name + ' Updated' });
100
-
101
95
  results = await db.query({ id: { '<=': 2 } });
102
96
  const pass5 = results.length === 1;
103
97
  const pass6 = results[0].name === 'Sub-Zero';
@@ -108,43 +102,65 @@ const runTests = async (id, name, format, opts) => {
108
102
  // End the battle and log the result
109
103
  if(pass1 && pass2 && pass4 && pass5 && pass6) {
110
104
  let err, elapsed = Date.now() - start;
111
- elapsed = elapsed < 1000 ? elapsed + 'ms' : (elapsed / 1000).toFixed(3) + 's';
112
105
  const { size } = await fs.promises.stat(testFilePath);
113
- benchmarks[format] = { elapsed, size };
106
+ if(!benchmarks[format]) {
107
+ benchmarks[format] = { elapsed, size }
108
+ } else {
109
+ benchmarks[format].elapsed = (elapsed + benchmarks[format].elapsed)
110
+ benchmarks[format].size = (size + benchmarks[format].size)
111
+ }
114
112
  console.log(`\nBattle #${id} ended: All tests with format "${format}" ran successfully! Fatality avoided this time.\n\n`);
113
+ global.gc()
115
114
  } else {
116
115
  benchmarks[format] = { elapsed: 'Error', size: 'Error' };
116
+ global.gc();
117
117
  throw `\nBattle #${id} ended: Some tests failed with format "${format}"! Time to train harder.\n\n`;
118
118
  }
119
119
  }
120
120
 
121
121
  async function runAllTests() {
122
- let err
123
- await runTests(1, 'json', 'JSON', {
124
- indexes: {id: 'number', name: 'string'},
125
- v8: false,
126
- compress: false,
127
- compressIndex: false
128
- }).catch(e => err = e)
129
- await runTests(2, 'v8', 'V8 serialization', {
130
- indexes: {id: 'number', name: 'string'},
131
- v8: true,
132
- compress: false,
133
- compressIndex: false
134
- }).catch(e => err = e)
135
- await runTests(3, 'json-compressed', 'JSON with Brotli compression', {
136
- indexes: {id: 'number', name: 'string'},
137
- v8: false,
138
- compress: false,
139
- compressIndex: true
140
- }).catch(e => err = e)
141
- await runTests(3, 'v8-compressed', 'V8 with Brotli compression', {
142
- indexes: {id: 'number', name: 'string'},
143
- v8: true,
144
- compress: false,
145
- compressIndex: true
146
- }).catch(e => err = e)
122
+ const depth = 100
123
+ let err, i = 1
124
+ let tests = [
125
+ ['json', 'JSON', { indexes: { id: 'number', name: 'string' }, v8: false, compress: false, compressIndex: false }],
126
+ ['v8', 'V8 serialization', { indexes: { id: 'number', name: 'string' }, v8: true, compress: false, compressIndex: false }],
127
+ ['json-compressed', 'JSON with Brotli compression', { indexes: { id: 'number', name: 'string' }, v8: false, compress: false, compressIndex: true }],
128
+ ['v8-compressed', 'V8 with Deflate compression', { indexes: { id: 'number', name: 'string' }, v8: true, compress: false, compressIndex: true }]
129
+ ]
130
+ tests = Array(depth).fill(tests).flat()
131
+ tests = tests.map(value => ({ value, sort: Math.random() })).sort((a, b) => a.sort - b.sort).map(({ value }) => value)
132
+ for(const test of tests) {
133
+ await runTests(i++, test[0], test[1], test[2]).catch(e => {
134
+ benchmarks[test[1]] = { elapsed: 'Error', size: 'Error' };
135
+ console.error(e)
136
+ err = e
137
+ })
138
+ }
139
+ const winners = {}
140
+ for (const [format, result] of Object.entries(benchmarks)) {
141
+ if (result.elapsed !== 'Error' && result.size !== 'Error') {
142
+ if (typeof(winners.elapsed) === 'undefined' || result.elapsed < winners.elapsed) {
143
+ winners.elapsed = result.elapsed
144
+ winners.format = format
145
+ }
146
+ if (typeof(winners.size) === 'undefined' || result.size < winners.size) {
147
+ winners.size = result.size
148
+ winners.format = format
149
+ }
150
+ }
151
+ }
152
+ for(const format in benchmarks) {
153
+ for(const prop of ['elapsed', 'size']) {
154
+ if(benchmarks[format][prop] === winners[prop]) {
155
+ benchmarks[format][prop] += ' \uD83C\uDFC6'
156
+ }
157
+ }
158
+ }
159
+ console.log('Benchmarks results after '+ tests.length +' battles:')
147
160
  console.table(benchmarks)
161
+ // setInterval(() => {}, 1000)
162
+ // global.Database = Database
163
+ // global.__dirname = __dirname
148
164
  process.exit(err ? 1 : 0)
149
165
  }
150
166
 
@@ -1,21 +0,0 @@
1
- export default class Serializer {
2
-
3
- constructor(opts={}) {
4
- this.opts = Object.assign({}, opts)
5
- }
6
-
7
- async serialize(data, opts={}) {
8
- return Buffer.from(JSON.stringify(data) + (opts.linebreak !== false ? '\n' : ''), 'utf-8')
9
- }
10
-
11
- async deserialize(data, opts={}) {
12
- const line = data.toString('utf-8')
13
- try {
14
- return JSON.parse(line)
15
- } catch (e) {
16
- console.error('Failed to deserialize', line)
17
- throw new Error('Failed to deserialize JSON data')
18
- }
19
- }
20
-
21
- }