@monlite/core 0.1.0 → 0.2.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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import Database from 'better-sqlite3';
2
1
  import { randomBytes } from 'crypto';
2
+ import { createRequire } from 'module';
3
3
 
4
- // src/db.ts
4
+ // src/id.ts
5
5
  var PROCESS_UNIQUE = randomBytes(5);
6
6
  var counter = randomBytes(3).readUIntBE(0, 3);
7
7
  function objectId() {
@@ -439,7 +439,7 @@ var Collection = class {
439
439
  initialized = false;
440
440
  trackPath = (path) => this.mon.autoIndexer.track(this.name, path);
441
441
  get db() {
442
- return this.mon.sqlite;
442
+ return this.mon.driver;
443
443
  }
444
444
  ensureTable() {
445
445
  if (this.initialized) return;
@@ -485,13 +485,12 @@ var Collection = class {
485
485
  const stmt = this.db.prepare(
486
486
  `INSERT INTO "${this.name}" (_id, data, created_at, updated_at) VALUES (?, ?, ?, ?)`
487
487
  );
488
- const insertAll = this.db.transaction((items) => {
489
- for (const item of items) {
488
+ this.db.transaction(() => {
489
+ for (const item of args.data) {
490
490
  const row = this.prepareInsert(item);
491
491
  stmt.run(row._id, row.data, row.created_at, row.updated_at);
492
492
  }
493
493
  });
494
- insertAll(args.data);
495
494
  return { count: args.data.length };
496
495
  }
497
496
  /* ------------------------------ read ------------------------------ */
@@ -544,7 +543,7 @@ var Collection = class {
544
543
  const stmt = this.db.prepare(
545
544
  `UPDATE "${this.name}" SET data = ?, updated_at = ? WHERE _id = ?`
546
545
  );
547
- const txn = this.db.transaction(() => {
546
+ return this.db.transaction(() => {
548
547
  const out = [];
549
548
  for (const row of rows) {
550
549
  const current = JSON.parse(row.data);
@@ -559,7 +558,6 @@ var Collection = class {
559
558
  }
560
559
  return out;
561
560
  });
562
- return txn();
563
561
  }
564
562
  async update(args) {
565
563
  return this.runUpdate(args.where, args.data, true)[0] ?? null;
@@ -589,10 +587,9 @@ var Collection = class {
589
587
  const rows = this.db.prepare(selectSql).all(...params);
590
588
  if (!rows.length) return [];
591
589
  const stmt = this.db.prepare(`DELETE FROM "${this.name}" WHERE _id = ?`);
592
- const txn = this.db.transaction(() => {
590
+ this.db.transaction(() => {
593
591
  for (const row of rows) stmt.run(row._id);
594
592
  });
595
- txn();
596
593
  return rows.map((r) => this.rowToDoc(r));
597
594
  }
598
595
  async delete(args) {
@@ -670,6 +667,134 @@ var AutoIndexer = class {
670
667
  }
671
668
  };
672
669
 
670
+ // src/driver/better-sqlite3.ts
671
+ var BetterSqlite3Driver = class {
672
+ name = "better-sqlite3";
673
+ raw;
674
+ verbose;
675
+ constructor(BetterSqlite3, filename, options) {
676
+ this.verbose = options.verbose;
677
+ this.raw = new BetterSqlite3(filename, {
678
+ readonly: options.readonly ?? false
679
+ });
680
+ if (!options.readonly && (options.wal ?? true)) {
681
+ this.raw.pragma("journal_mode = WAL");
682
+ }
683
+ }
684
+ exec(sql) {
685
+ this.verbose?.(sql);
686
+ this.raw.exec(sql);
687
+ }
688
+ prepare(sql) {
689
+ this.verbose?.(sql);
690
+ return this.raw.prepare(sql);
691
+ }
692
+ transaction(fn) {
693
+ return this.raw.transaction(fn)();
694
+ }
695
+ close() {
696
+ this.raw.close();
697
+ }
698
+ };
699
+
700
+ // src/driver/node-sqlite.ts
701
+ var NodeSqliteDriver = class {
702
+ name = "node:sqlite";
703
+ raw;
704
+ verbose;
705
+ depth = 0;
706
+ constructor(nodeSqlite, filename, options) {
707
+ this.verbose = options.verbose;
708
+ const { DatabaseSync } = nodeSqlite;
709
+ this.raw = new DatabaseSync(filename, {
710
+ readOnly: options.readonly ?? false
711
+ });
712
+ if (!options.readonly && (options.wal ?? true)) {
713
+ this.raw.exec("PRAGMA journal_mode = WAL");
714
+ }
715
+ }
716
+ exec(sql) {
717
+ this.verbose?.(sql);
718
+ this.raw.exec(sql);
719
+ }
720
+ prepare(sql) {
721
+ this.verbose?.(sql);
722
+ const stmt = this.raw.prepare(sql);
723
+ return {
724
+ run: (...p) => stmt.run(...p),
725
+ get: (...p) => stmt.get(...p),
726
+ all: (...p) => stmt.all(...p)
727
+ };
728
+ }
729
+ transaction(fn) {
730
+ const savepoint = `monlite_sp_${this.depth}`;
731
+ if (this.depth === 0) this.raw.exec("BEGIN");
732
+ else this.raw.exec(`SAVEPOINT ${savepoint}`);
733
+ this.depth++;
734
+ try {
735
+ const result = fn();
736
+ this.depth--;
737
+ if (this.depth === 0) this.raw.exec("COMMIT");
738
+ else this.raw.exec(`RELEASE ${savepoint}`);
739
+ return result;
740
+ } catch (err) {
741
+ this.depth--;
742
+ if (this.depth === 0) this.raw.exec("ROLLBACK");
743
+ else this.raw.exec(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`);
744
+ throw err;
745
+ }
746
+ }
747
+ close() {
748
+ this.raw.close();
749
+ }
750
+ };
751
+
752
+ // src/driver/index.ts
753
+ var req = createRequire(import.meta.url);
754
+ function loadBetterSqlite3() {
755
+ try {
756
+ const mod = req("better-sqlite3");
757
+ return mod?.default ?? mod;
758
+ } catch {
759
+ return null;
760
+ }
761
+ }
762
+ function loadNodeSqlite() {
763
+ try {
764
+ return req("node:sqlite");
765
+ } catch {
766
+ return null;
767
+ }
768
+ }
769
+ function createDriver(filename, options = {}) {
770
+ const choice = options.driver ?? "auto";
771
+ if (choice === "better-sqlite3") {
772
+ const mod = loadBetterSqlite3();
773
+ if (!mod) {
774
+ throw new MonliteError(
775
+ `driver "better-sqlite3" was requested but the package is not installed. Run \`npm install better-sqlite3\`.`
776
+ );
777
+ }
778
+ return new BetterSqlite3Driver(mod, filename, options);
779
+ }
780
+ if (choice === "node:sqlite") {
781
+ const mod = loadNodeSqlite();
782
+ if (!mod) {
783
+ throw new MonliteError(
784
+ `driver "node:sqlite" is unavailable. It requires Node >= 22.5.`
785
+ );
786
+ }
787
+ return new NodeSqliteDriver(mod, filename, options);
788
+ }
789
+ const better = loadBetterSqlite3();
790
+ if (better) return new BetterSqlite3Driver(better, filename, options);
791
+ const node = loadNodeSqlite();
792
+ if (node) return new NodeSqliteDriver(node, filename, options);
793
+ throw new MonliteError(
794
+ `No SQLite driver available. Either install better-sqlite3 (\`npm install better-sqlite3\`) or run on Node >= 22.5 for the built-in node:sqlite backend.`
795
+ );
796
+ }
797
+
673
798
  // src/db.ts
674
799
  function validateName(name) {
675
800
  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
@@ -691,27 +816,33 @@ function buildTagged(strings, values) {
691
816
  return { sql, params };
692
817
  }
693
818
  var Monlite = class {
694
- /** The underlying better-sqlite3 connection (escape hatch). */
695
- sqlite;
819
+ /** @internal The active SQLite driver. */
820
+ driver;
696
821
  /** @internal */
697
822
  autoIndexer;
698
823
  collections = /* @__PURE__ */ new Map();
699
824
  closed = false;
700
825
  constructor(filename, options = {}) {
701
- const verbose = options.verbose;
702
- this.sqlite = new Database(filename, {
703
- readonly: options.readonly ?? false,
704
- ...verbose ? { verbose: (msg) => verbose(String(msg)) } : {}
826
+ this.driver = createDriver(filename, {
827
+ driver: options.driver,
828
+ readonly: options.readonly,
829
+ wal: options.wal,
830
+ verbose: options.verbose
705
831
  });
706
- if (!options.readonly && (options.wal ?? true)) {
707
- this.sqlite.pragma("journal_mode = WAL");
708
- }
709
832
  this.autoIndexer = new AutoIndexer(
710
- this.sqlite,
833
+ this.driver,
711
834
  options.autoIndex ?? true,
712
835
  options.autoIndexAfter ?? 10
713
836
  );
714
837
  }
838
+ /** The underlying native database handle (escape hatch). */
839
+ get sqlite() {
840
+ return this.driver.raw;
841
+ }
842
+ /** Name of the active backend: `"better-sqlite3"` or `"node:sqlite"`. */
843
+ get driverName() {
844
+ return this.driver.name;
845
+ }
715
846
  /** Get (or lazily create) a typed collection handle. */
716
847
  collection(name) {
717
848
  this.assertOpen();
@@ -727,26 +858,26 @@ var Monlite = class {
727
858
  $queryRaw(strings, ...values) {
728
859
  this.assertOpen();
729
860
  const { sql, params } = buildTagged(strings, values);
730
- return Promise.resolve(this.sqlite.prepare(sql).all(...params));
861
+ return Promise.resolve(this.driver.prepare(sql).all(...params));
731
862
  }
732
863
  /** Like {@link $queryRaw} but takes a raw SQL string and positional params. */
733
864
  $queryRawUnsafe(sql, ...params) {
734
865
  this.assertOpen();
735
866
  return Promise.resolve(
736
- this.sqlite.prepare(sql).all(...params.map(bindable))
867
+ this.driver.prepare(sql).all(...params.map(bindable))
737
868
  );
738
869
  }
739
870
  /** Tagged-template SQL statement returning the number of affected rows. */
740
871
  $executeRaw(strings, ...values) {
741
872
  this.assertOpen();
742
873
  const { sql, params } = buildTagged(strings, values);
743
- return Promise.resolve(this.sqlite.prepare(sql).run(...params).changes);
874
+ return Promise.resolve(this.driver.prepare(sql).run(...params).changes);
744
875
  }
745
876
  /** Like {@link $executeRaw} but takes a raw SQL string and positional params. */
746
877
  $executeRawUnsafe(sql, ...params) {
747
878
  this.assertOpen();
748
879
  return Promise.resolve(
749
- this.sqlite.prepare(sql).run(...params.map(bindable)).changes
880
+ this.driver.prepare(sql).run(...params.map(bindable)).changes
750
881
  );
751
882
  }
752
883
  /**
@@ -755,13 +886,12 @@ var Monlite = class {
755
886
  */
756
887
  async $transaction(fn) {
757
888
  this.assertOpen();
758
- const txn = this.sqlite.transaction(() => fn(this));
759
- return txn();
889
+ return this.driver.transaction(() => fn(this));
760
890
  }
761
891
  /** List all collection (table) names. */
762
892
  $collections() {
763
893
  this.assertOpen();
764
- const rows = this.sqlite.prepare(
894
+ const rows = this.driver.prepare(
765
895
  `SELECT name FROM sqlite_master
766
896
  WHERE type='table' AND name NOT LIKE 'sqlite_%'
767
897
  ORDER BY name`
@@ -772,7 +902,7 @@ var Monlite = class {
772
902
  $drop(name) {
773
903
  this.assertOpen();
774
904
  validateName(name);
775
- this.sqlite.exec(`DROP TABLE IF EXISTS "${name}"`);
905
+ this.driver.exec(`DROP TABLE IF EXISTS "${name}"`);
776
906
  this.collections.delete(name);
777
907
  this.autoIndexer.reset(name);
778
908
  return Promise.resolve();
@@ -785,7 +915,7 @@ var Monlite = class {
785
915
  $disconnect() {
786
916
  if (!this.closed) {
787
917
  this.closed = true;
788
- this.sqlite.close();
918
+ this.driver.close();
789
919
  }
790
920
  return Promise.resolve();
791
921
  }