@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/README.md +46 -7
- package/dist/index.cjs +160 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -6
- package/dist/index.d.ts +40 -6
- package/dist/index.js +159 -29
- package/dist/index.js.map +1 -1
- package/package.json +16 -3
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/
|
|
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.
|
|
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
|
-
|
|
489
|
-
for (const item of
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
695
|
-
|
|
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
|
-
|
|
702
|
-
|
|
703
|
-
readonly: options.readonly
|
|
704
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
918
|
+
this.driver.close();
|
|
789
919
|
}
|
|
790
920
|
return Promise.resolve();
|
|
791
921
|
}
|