@semics-tech/mongolite 1.0.0 → 1.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.
Files changed (44) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +78 -2
  3. package/dist/adapters/node-sqlite.d.ts +65 -0
  4. package/dist/adapters/node-sqlite.js +241 -0
  5. package/dist/adapters/node-sqlite.js.map +1 -0
  6. package/dist/better-sqlite3.d.ts +49 -0
  7. package/dist/better-sqlite3.js +60 -0
  8. package/dist/better-sqlite3.js.map +1 -0
  9. package/dist/bin/mongolite-debug.js +0 -0
  10. package/dist/cjs/adapters/browser.js +106 -0
  11. package/dist/cjs/adapters/cloudflare.js +97 -0
  12. package/dist/cjs/adapters/node-sqlite.js +248 -0
  13. package/dist/cjs/better-sqlite3.js +80 -0
  14. package/dist/cjs/bin/mongolite-debug.js +152 -0
  15. package/dist/cjs/changeStream.js +341 -0
  16. package/dist/cjs/cloudflare.js +59 -0
  17. package/dist/cjs/collection.js +1748 -0
  18. package/dist/cjs/cursors/findCursor.js +837 -0
  19. package/dist/cjs/db.js +307 -0
  20. package/dist/cjs/debugger/query-debugger.js +297 -0
  21. package/dist/cjs/index.js +65 -0
  22. package/dist/cjs/mongo-client.js +58 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/types.js +2 -0
  25. package/dist/cjs/utils/connection-registry.js +122 -0
  26. package/dist/cjs/utils/indexing.js +45 -0
  27. package/dist/cjs/utils/projection.js +101 -0
  28. package/dist/cjs/utils/queryOperators.js +142 -0
  29. package/dist/collection.d.ts +25 -0
  30. package/dist/collection.js +53 -14
  31. package/dist/collection.js.map +1 -1
  32. package/dist/cursors/findCursor.d.ts +58 -0
  33. package/dist/cursors/findCursor.js +88 -11
  34. package/dist/cursors/findCursor.js.map +1 -1
  35. package/dist/db.d.ts +23 -0
  36. package/dist/db.js +85 -39
  37. package/dist/db.js.map +1 -1
  38. package/dist/index.d.ts +5 -2
  39. package/dist/index.js +6 -3
  40. package/dist/index.js.map +1 -1
  41. package/dist/utils/connection-registry.d.ts +74 -0
  42. package/dist/utils/connection-registry.js +117 -0
  43. package/dist/utils/connection-registry.js.map +1 -0
  44. package/package.json +20 -10
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 YOUR_NAME
3
+ Copyright (c) 2025 Oliver Breeden
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -18,12 +18,13 @@ A MongoDB-like client backed by SQLite. Use a familiar MongoDB API with the simp
18
18
  - **MongoDB-compatible API** — `insertOne`, `findOne`, `updateOne`, `deleteOne`, `find`, `aggregate`, and more
19
19
  - **SQLite persistence** — single file, zero configuration, works offline
20
20
  - **Automatic `_id` generation** — UUID assigned on insert if not provided
21
- - **WAL mode** — Write-Ahead Logging for better concurrent read access
21
+ - **WAL mode** — Write-Ahead Logging for better concurrent read access, on by default
22
+ - **Shared connections** — opt into one pooled connection per file per process
22
23
  - **Rich query operators** — `$eq`, `$gt`, `$in`, `$and`, `$or`, `$elemMatch`, `$regex`, and more
23
24
  - **Update operators** — `$set`, `$inc`, `$push`, `$pull`, `$addToSet`, `$mul`, and more
24
25
  - **Indexing** — create, list, and drop indexes including unique and compound indexes
25
26
  - **Change streams** — real-time change tracking via `collection.watch()`
26
- - **JSON safety** — validates documents before insert and recovers from corrupted data
27
+ - **JSON safety** — validates documents before insert, and a corrupted row can't break queries for the rest of the collection
27
28
  - **TypeScript** — fully typed with strict mode
28
29
 
29
30
  ## Installation
@@ -32,6 +33,13 @@ A MongoDB-like client backed by SQLite. Use a familiar MongoDB API with the simp
32
33
  npm install @semics-tech/mongolite
33
34
  ```
34
35
 
36
+ Requires **Node.js 22.5.0+** by default — the main entry point is backed by
37
+ Node's built-in `node:sqlite` module, so there's no native addon to install or
38
+ build. On an older Node.js runtime, use the `better-sqlite3`-backed adapter
39
+ instead; see [Native `node:sqlite` vs. `better-sqlite3`](#native-nodesqlite-vs-better-sqlite3) below.
40
+
41
+ Ships both ESM (`import`) and CommonJS (`require`) builds — use whichever your project already uses.
42
+
35
43
  ## Quick Start
36
44
 
37
45
  ```typescript
@@ -99,6 +107,43 @@ await users.insertOne({ name: 'Alice', age: 30 });
99
107
  await client.close();
100
108
  ```
101
109
 
110
+ ### Connection options
111
+
112
+ ```typescript
113
+ const client = new MongoLite({
114
+ filePath: './myapp.sqlite',
115
+ WAL: true, // Write-Ahead Logging. Default: true
116
+ busyTimeout: 5000, // ms to wait for a lock before SQLITE_BUSY. Default: 5000
117
+ shared: true, // reuse one connection per file in this process. Default: false
118
+ readOnly: false,
119
+ verbose: false,
120
+ });
121
+ ```
122
+
123
+ #### Sharing one connection across a process
124
+
125
+ Applications often end up with several clients pointing at the same database file
126
+ — a module-level client, a background job, a request handler — each opening its
127
+ own connection, and each then hand-rolling an instance cache to avoid the
128
+ resulting lock contention. `shared: true` moves that into the library:
129
+
130
+ ```typescript
131
+ // Both resolve to the same file, so they share a single SQLite connection.
132
+ const a = new MongoLite({ filePath: './app.sqlite', shared: true });
133
+ const b = new MongoLite({ filePath: './app.sqlite', shared: true });
134
+
135
+ await a.close(); // b keeps working — the handle is reference counted
136
+ await b.close(); // last holder out actually closes it
137
+ ```
138
+
139
+ Connections are only shared when the resolved path *and* every setting that
140
+ changes the handle's behaviour match. `:memory:` databases are never shared,
141
+ since two `:memory:` opens are two unrelated databases.
142
+
143
+ This dedupes connections **within a process only**. Separate processes each get
144
+ their own, so cross-process contention is handled by WAL mode and `busyTimeout`
145
+ instead.
146
+
102
147
  ### In-memory (tests / ephemeral)
103
148
 
104
149
  ```typescript
@@ -112,6 +157,37 @@ await users.insertOne({ name: 'Alice', age: 30 });
112
157
  await client.close();
113
158
  ```
114
159
 
160
+ ### `better-sqlite3` (optional, for Node.js <22.5 or Bun)
161
+
162
+ The default backend above uses Node's built-in `node:sqlite` module, which
163
+ requires Node.js 22.5.0+. If you're on an older Node.js runtime, or you
164
+ simply prefer the more battle-tested native addon, import from the
165
+ `@semics-tech/mongolite/better-sqlite3` entry point instead. `better-sqlite3` is an
166
+ **optional dependency** — install it yourself if it wasn't already pulled in:
167
+
168
+ ```bash
169
+ npm install better-sqlite3
170
+ ```
171
+
172
+ ```typescript
173
+ import { MongoLite } from '@semics-tech/mongolite/better-sqlite3';
174
+
175
+ const client = new MongoLite('./myapp.sqlite');
176
+ await client.connect();
177
+ const users = client.collection('users');
178
+ await users.insertOne({ name: 'Alice', age: 30 });
179
+ await client.close();
180
+ ```
181
+
182
+ <a id="native-nodesqlite-vs-better-sqlite3"></a>
183
+ > **Which one should I use?** `node:sqlite` (the default) is still marked
184
+ > experimental upstream but requires no native build step, which is why it's
185
+ > the default here. `better-sqlite3` is the older, more battle-tested native
186
+ > addon — reach for it if you're stuck on Node.js <22.5, targeting Bun without
187
+ > a `node:sqlite` polyfill, or want to avoid depending on an experimental
188
+ > Node.js API in production. Both implement the same `IDatabaseAdapter`
189
+ > interface, so switching between them is a one-line import change.
190
+
115
191
  ### Browser (via sql.js)
116
192
 
117
193
  Requires [sql.js](https://www.npmjs.com/package/sql.js) (`npm install sql.js`).
@@ -0,0 +1,65 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import type { IDatabaseAdapter, MongoLiteOptions } from '../db.js';
3
+ /** Exposed for tests and diagnostics. */
4
+ export declare function sharedConnectionCount(): number;
5
+ /**
6
+ * Database adapter backed by Node.js's built-in `node:sqlite` module — no native
7
+ * addon, no prebuilt binary, no `node-gyp`. Requires Node.js 22.5.0+ (the module
8
+ * is marked experimental by Node.js until it graduates to stable).
9
+ *
10
+ * ## Usage
11
+ *
12
+ * ```ts
13
+ * import { MongoLite, NodeSqliteAdapter } from '@semics-tech/mongolite';
14
+ *
15
+ * const client = new MongoLite(new NodeSqliteAdapter('./data.db'));
16
+ * await client.connect();
17
+ * const users = client.collection('users');
18
+ * await users.insertOne({ name: 'Alice', age: 30 });
19
+ * await client.close();
20
+ * ```
21
+ *
22
+ * ## Limitations
23
+ *
24
+ * - **Experimental.** `node:sqlite` is still marked experimental upstream and its
25
+ * API may change between Node.js minor versions.
26
+ * - **Node.js 22.5.0+ only.** Importing this adapter on an older runtime (or on
27
+ * Bun/Deno without a `node:sqlite` polyfill) throws at import time.
28
+ * - Rows are returned with a `null` prototype (per `node:sqlite`'s API). Plain
29
+ * property access (`row.foo`) and `JSON.stringify`/`Object.keys` all work as
30
+ * expected; `row.hasOwnProperty(...)` does not.
31
+ */
32
+ export declare class NodeSqliteAdapter implements IDatabaseAdapter {
33
+ private db;
34
+ private readonly filePath;
35
+ private readonly verbose;
36
+ private readonly readOnly;
37
+ private readonly WAL;
38
+ private readonly busyTimeout;
39
+ private readonly shareKey;
40
+ private openPromise;
41
+ /**
42
+ * @param dbPathOrOptions Path to the SQLite database file (use `':memory:'`
43
+ * for an in-memory database), or an options object mirroring the built-in
44
+ * `better-sqlite3` adapter's `MongoLiteOptions`.
45
+ */
46
+ constructor(dbPathOrOptions: string | MongoLiteOptions);
47
+ /**
48
+ * Opens the database connection if it's not already open.
49
+ */
50
+ connect(): Promise<void>;
51
+ private ensureConnected;
52
+ run(sql: string, params?: unknown[]): Promise<{
53
+ lastID: number;
54
+ changes: number;
55
+ }>;
56
+ get<T>(sql: string, params?: unknown[]): Promise<T | undefined>;
57
+ all<T>(sql: string, params?: unknown[]): Promise<T[]>;
58
+ exec(sql: string): Promise<void>;
59
+ close(): Promise<void>;
60
+ /**
61
+ * Gets the underlying `node:sqlite` `DatabaseSync` instance.
62
+ * Useful for operations not covered by this wrapper.
63
+ */
64
+ getDbInstance(): Promise<DatabaseSync>;
65
+ }
@@ -0,0 +1,241 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import path from 'node:path';
3
+ import { ConnectionRegistry, DEFAULT_BUSY_TIMEOUT_MS, buildConnectionKey, isShareablePath, } from '../utils/connection-registry.js';
4
+ /** Shared `node:sqlite` handles for this process, keyed by connection identity. */
5
+ const sharedConnections = new ConnectionRegistry();
6
+ /** Exposed for tests and diagnostics. */
7
+ export function sharedConnectionCount() {
8
+ return sharedConnections.size;
9
+ }
10
+ /**
11
+ * Database adapter backed by Node.js's built-in `node:sqlite` module — no native
12
+ * addon, no prebuilt binary, no `node-gyp`. Requires Node.js 22.5.0+ (the module
13
+ * is marked experimental by Node.js until it graduates to stable).
14
+ *
15
+ * ## Usage
16
+ *
17
+ * ```ts
18
+ * import { MongoLite, NodeSqliteAdapter } from '@semics-tech/mongolite';
19
+ *
20
+ * const client = new MongoLite(new NodeSqliteAdapter('./data.db'));
21
+ * await client.connect();
22
+ * const users = client.collection('users');
23
+ * await users.insertOne({ name: 'Alice', age: 30 });
24
+ * await client.close();
25
+ * ```
26
+ *
27
+ * ## Limitations
28
+ *
29
+ * - **Experimental.** `node:sqlite` is still marked experimental upstream and its
30
+ * API may change between Node.js minor versions.
31
+ * - **Node.js 22.5.0+ only.** Importing this adapter on an older runtime (or on
32
+ * Bun/Deno without a `node:sqlite` polyfill) throws at import time.
33
+ * - Rows are returned with a `null` prototype (per `node:sqlite`'s API). Plain
34
+ * property access (`row.foo`) and `JSON.stringify`/`Object.keys` all work as
35
+ * expected; `row.hasOwnProperty(...)` does not.
36
+ */
37
+ export class NodeSqliteAdapter {
38
+ /**
39
+ * @param dbPathOrOptions Path to the SQLite database file (use `':memory:'`
40
+ * for an in-memory database), or an options object mirroring the built-in
41
+ * `better-sqlite3` adapter's `MongoLiteOptions`.
42
+ */
43
+ constructor(dbPathOrOptions) {
44
+ this.db = null;
45
+ this.openPromise = null;
46
+ let shared;
47
+ if (typeof dbPathOrOptions === 'string') {
48
+ this.filePath = dbPathOrOptions;
49
+ this.verbose = false;
50
+ this.readOnly = false;
51
+ this.WAL = true;
52
+ this.busyTimeout = DEFAULT_BUSY_TIMEOUT_MS;
53
+ shared = false;
54
+ }
55
+ else {
56
+ this.filePath = dbPathOrOptions.filePath;
57
+ this.verbose = dbPathOrOptions.verbose || false;
58
+ this.readOnly = dbPathOrOptions.readOnly || false;
59
+ this.WAL = dbPathOrOptions.WAL ?? true;
60
+ this.busyTimeout = dbPathOrOptions.busyTimeout ?? DEFAULT_BUSY_TIMEOUT_MS;
61
+ shared = dbPathOrOptions.shared ?? false;
62
+ }
63
+ this.shareKey =
64
+ shared && isShareablePath(this.filePath)
65
+ ? buildConnectionKey({
66
+ backend: 'node:sqlite',
67
+ filePath: path.resolve(this.filePath),
68
+ readOnly: this.readOnly,
69
+ WAL: this.WAL,
70
+ busyTimeout: this.busyTimeout,
71
+ verbose: this.verbose,
72
+ })
73
+ : null;
74
+ }
75
+ /**
76
+ * Opens the database connection if it's not already open.
77
+ */
78
+ async connect() {
79
+ if (this.db && this.openPromise) {
80
+ return this.openPromise;
81
+ }
82
+ if (this.db) {
83
+ return Promise.resolve();
84
+ }
85
+ this.openPromise = new Promise((resolve, reject) => {
86
+ try {
87
+ // Only runs on a genuinely new connection — when sharing, an existing
88
+ // handle already has its pragmas and UDFs applied.
89
+ const openConnection = () => {
90
+ const db = new DatabaseSync(this.filePath, { readOnly: this.readOnly });
91
+ if (this.WAL && !this.readOnly) {
92
+ db.exec('PRAGMA journal_mode = WAL');
93
+ }
94
+ // Wait rather than immediately throwing SQLITE_BUSY when another
95
+ // connection (often another process) holds a lock.
96
+ if (this.busyTimeout > 0) {
97
+ db.exec(`PRAGMA busy_timeout = ${this.busyTimeout}`);
98
+ }
99
+ // Register regexp UDF for $regex operator support.
100
+ // WARNING: Patterns are compiled via JavaScript RegExp. User-supplied patterns that are
101
+ // not validated for catastrophic backtracking (ReDoS) could block the event loop.
102
+ // Avoid using untrusted/unvalidated patterns with $regex in security-sensitive contexts.
103
+ db.function('regexp', { deterministic: true }, (pattern, value) => {
104
+ if (typeof pattern !== 'string' || value === null || value === undefined)
105
+ return 0;
106
+ try {
107
+ return new RegExp(pattern).test(String(value)) ? 1 : 0;
108
+ }
109
+ catch {
110
+ return 0;
111
+ }
112
+ });
113
+ // Register regexp_flags UDF for $regex with $options support
114
+ db.function('regexp_flags', { deterministic: true }, (pattern, flags, value) => {
115
+ if (typeof pattern !== 'string' || value === null || value === undefined)
116
+ return 0;
117
+ try {
118
+ const f = typeof flags === 'string' ? flags : '';
119
+ return new RegExp(pattern, f).test(String(value)) ? 1 : 0;
120
+ }
121
+ catch {
122
+ return 0;
123
+ }
124
+ });
125
+ if (this.verbose) {
126
+ console.log(`SQLite database opened (node:sqlite): ${this.filePath}`);
127
+ }
128
+ return db;
129
+ };
130
+ this.db = this.shareKey
131
+ ? sharedConnections.acquire(this.shareKey, openConnection)
132
+ : openConnection();
133
+ resolve();
134
+ }
135
+ catch (err) {
136
+ console.error(`Error opening database ${this.filePath}:`, err.message);
137
+ this.db = null;
138
+ this.openPromise = null;
139
+ reject(err);
140
+ }
141
+ });
142
+ return this.openPromise;
143
+ }
144
+ async ensureConnected() {
145
+ if (!this.db || !this.openPromise) {
146
+ await this.connect();
147
+ }
148
+ else {
149
+ await this.openPromise;
150
+ }
151
+ if (!this.db) {
152
+ throw new Error('Database is not connected. Connection attempt failed.');
153
+ }
154
+ return this.db;
155
+ }
156
+ async run(sql, params = []) {
157
+ const dbInstance = await this.ensureConnected();
158
+ try {
159
+ const result = dbInstance.prepare(sql).run(...params);
160
+ return {
161
+ lastID: Number(result.lastInsertRowid),
162
+ changes: Number(result.changes),
163
+ };
164
+ }
165
+ catch (err) {
166
+ console.error(`Error running SQL: ${sql}`, err);
167
+ throw err;
168
+ }
169
+ }
170
+ async get(sql, params = []) {
171
+ const dbInstance = await this.ensureConnected();
172
+ try {
173
+ return dbInstance.prepare(sql).get(...params);
174
+ }
175
+ catch (err) {
176
+ console.error(`Error getting SQL: ${sql}`, err);
177
+ throw err;
178
+ }
179
+ }
180
+ async all(sql, params = []) {
181
+ const dbInstance = await this.ensureConnected();
182
+ try {
183
+ return dbInstance.prepare(sql).all(...params);
184
+ }
185
+ catch (err) {
186
+ console.error(`Error getting all SQL: ${sql}`, err);
187
+ throw err;
188
+ }
189
+ }
190
+ async exec(sql) {
191
+ const dbInstance = await this.ensureConnected();
192
+ try {
193
+ dbInstance.exec(sql);
194
+ }
195
+ catch (err) {
196
+ console.error(`Error executing SQL: ${sql}`, err);
197
+ throw err;
198
+ }
199
+ }
200
+ async close() {
201
+ if (this.openPromise) {
202
+ await this.openPromise;
203
+ }
204
+ if (this.db) {
205
+ try {
206
+ // When shared, only the last holder actually closes the handle.
207
+ const isLastHolder = this.shareKey
208
+ ? sharedConnections.release(this.shareKey, this.db)
209
+ : true;
210
+ if (isLastHolder) {
211
+ this.db.close();
212
+ }
213
+ if (this.verbose) {
214
+ console.log(isLastHolder
215
+ ? `SQLite database closed (node:sqlite): ${this.filePath}`
216
+ : `SQLite database released (still in use): ${this.filePath}`);
217
+ }
218
+ }
219
+ catch (err) {
220
+ console.error(`Error closing database ${this.filePath}:`, err.message);
221
+ throw err;
222
+ }
223
+ finally {
224
+ this.db = null;
225
+ this.openPromise = null;
226
+ }
227
+ }
228
+ else {
229
+ this.db = null;
230
+ this.openPromise = null;
231
+ }
232
+ }
233
+ /**
234
+ * Gets the underlying `node:sqlite` `DatabaseSync` instance.
235
+ * Useful for operations not covered by this wrapper.
236
+ */
237
+ async getDbInstance() {
238
+ return this.ensureConnected();
239
+ }
240
+ }
241
+ //# sourceMappingURL=node-sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-sqlite.js","sourceRoot":"","sources":["../../src/adapters/node-sqlite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `better-sqlite3`-backed entry point for MongoLite.
3
+ *
4
+ * The main `@semics-tech/mongolite` entry point defaults to `NodeSqliteAdapter`, backed by
5
+ * Node.js's built-in `node:sqlite` module, which requires Node.js 22.5.0+. Import
6
+ * from `@semics-tech/mongolite/better-sqlite3` instead if you need to run on an older
7
+ * Node.js runtime, or you simply prefer the more battle-tested `better-sqlite3`
8
+ * native addon.
9
+ *
10
+ * `better-sqlite3` is an **optional dependency** of this package — install it
11
+ * yourself if it wasn't already pulled in:
12
+ *
13
+ * ```bash
14
+ * npm install better-sqlite3
15
+ * ```
16
+ *
17
+ * ## Usage
18
+ *
19
+ * ```ts
20
+ * import { MongoLite } from '@semics-tech/mongolite/better-sqlite3';
21
+ *
22
+ * const client = new MongoLite('./myapp.sqlite');
23
+ * await client.connect();
24
+ * const users = client.collection('users');
25
+ * await users.insertOne({ name: 'Alice', age: 30 });
26
+ * await client.close();
27
+ * ```
28
+ */
29
+ import type { IDatabaseAdapter, MongoLiteOptions as DBMongoLiteOptions } from './db.js';
30
+ import { MongoLite as MongoLiteBase, MongoLiteBaseOptions } from './mongo-client.js';
31
+ export { MongoLiteCollection } from './collection.js';
32
+ export * from './types.js';
33
+ export { ChangeStream } from './changeStream.js';
34
+ export type { ChangeStreamDocument, ChangeStreamOptions, ChangeOperationType, } from './changeStream.js';
35
+ export type { IDatabaseAdapter } from './db.js';
36
+ export { SQLiteDB } from './db.js';
37
+ export interface MongoLiteClientOptions extends DBMongoLiteOptions {
38
+ }
39
+ /**
40
+ * MongoLite client backed by the `better-sqlite3` native addon.
41
+ *
42
+ * You can construct it with:
43
+ * - A file path string — uses the built-in `SQLiteDB` (`better-sqlite3`) adapter.
44
+ * - A `MongoLiteClientOptions` object — uses the built-in adapter with options.
45
+ * - An `IDatabaseAdapter` instance — use a custom adapter for other backends.
46
+ */
47
+ export declare class MongoLite extends MongoLiteBase {
48
+ constructor(dbPathOrOptions: string | MongoLiteClientOptions | IDatabaseAdapter, options?: MongoLiteBaseOptions);
49
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `better-sqlite3`-backed entry point for MongoLite.
3
+ *
4
+ * The main `@semics-tech/mongolite` entry point defaults to `NodeSqliteAdapter`, backed by
5
+ * Node.js's built-in `node:sqlite` module, which requires Node.js 22.5.0+. Import
6
+ * from `@semics-tech/mongolite/better-sqlite3` instead if you need to run on an older
7
+ * Node.js runtime, or you simply prefer the more battle-tested `better-sqlite3`
8
+ * native addon.
9
+ *
10
+ * `better-sqlite3` is an **optional dependency** of this package — install it
11
+ * yourself if it wasn't already pulled in:
12
+ *
13
+ * ```bash
14
+ * npm install better-sqlite3
15
+ * ```
16
+ *
17
+ * ## Usage
18
+ *
19
+ * ```ts
20
+ * import { MongoLite } from '@semics-tech/mongolite/better-sqlite3';
21
+ *
22
+ * const client = new MongoLite('./myapp.sqlite');
23
+ * await client.connect();
24
+ * const users = client.collection('users');
25
+ * await users.insertOne({ name: 'Alice', age: 30 });
26
+ * await client.close();
27
+ * ```
28
+ */
29
+ import { SQLiteDB } from './db.js';
30
+ import { MongoLite as MongoLiteBase } from './mongo-client.js';
31
+ export { MongoLiteCollection } from './collection.js';
32
+ export * from './types.js';
33
+ export { ChangeStream } from './changeStream.js';
34
+ export { SQLiteDB } from './db.js';
35
+ /**
36
+ * MongoLite client backed by the `better-sqlite3` native addon.
37
+ *
38
+ * You can construct it with:
39
+ * - A file path string — uses the built-in `SQLiteDB` (`better-sqlite3`) adapter.
40
+ * - A `MongoLiteClientOptions` object — uses the built-in adapter with options.
41
+ * - An `IDatabaseAdapter` instance — use a custom adapter for other backends.
42
+ */
43
+ export class MongoLite extends MongoLiteBase {
44
+ constructor(dbPathOrOptions, options = {}) {
45
+ if (dbPathOrOptions &&
46
+ typeof dbPathOrOptions === 'object' &&
47
+ typeof dbPathOrOptions.connect === 'function' &&
48
+ typeof dbPathOrOptions.run === 'function' &&
49
+ typeof dbPathOrOptions.get === 'function' &&
50
+ typeof dbPathOrOptions.all === 'function' &&
51
+ typeof dbPathOrOptions.exec === 'function' &&
52
+ typeof dbPathOrOptions.close === 'function') {
53
+ super(dbPathOrOptions, options);
54
+ }
55
+ else {
56
+ super(new SQLiteDB(dbPathOrOptions), options);
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=better-sqlite3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"better-sqlite3.js","sourceRoot":"","sources":["../src/better-sqlite3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,SAAS,IAAI,aAAa,EAAwB,MAAM,mBAAmB,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAOjD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInC;;;;;;;GAOG;AACH,MAAM,OAAO,SAAU,SAAQ,aAAa;IAC1C,YACE,eAAmE,EACnE,UAAgC,EAAE;QAElC,IACE,eAAe;YACf,OAAO,eAAe,KAAK,QAAQ;YACnC,OAAQ,eAAoC,CAAC,OAAO,KAAK,UAAU;YACnE,OAAQ,eAAoC,CAAC,GAAG,KAAK,UAAU;YAC/D,OAAQ,eAAoC,CAAC,GAAG,KAAK,UAAU;YAC/D,OAAQ,eAAoC,CAAC,GAAG,KAAK,UAAU;YAC/D,OAAQ,eAAoC,CAAC,IAAI,KAAK,UAAU;YAChE,OAAQ,eAAoC,CAAC,KAAK,KAAK,UAAU,EACjE,CAAC;YACD,KAAK,CAAC,eAAmC,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,QAAQ,CAAC,eAAkD,CAAC,EAAE,OAAO,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;CACF"}
File without changes
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserSqliteAdapter = void 0;
4
+ /**
5
+ * Database adapter that wraps a {@link SqlJsDatabase} instance, enabling MongoLite
6
+ * to run in browser environments via [sql.js](https://sql.js.org/).
7
+ *
8
+ * ## Usage
9
+ *
10
+ * Install [sql.js](https://www.npmjs.com/package/sql.js) and initialise it before
11
+ * constructing the adapter:
12
+ *
13
+ * ```ts
14
+ * import initSqlJs from 'sql.js';
15
+ * import { MongoLite, BrowserSqliteAdapter } from '@semics-tech/mongolite';
16
+ *
17
+ * const SQL = await initSqlJs({
18
+ * locateFile: (file) => `https://cdn.jsdelivr.net/npm/sql.js/dist/${file}`,
19
+ * });
20
+ * const sqlJsDb = new SQL.Database(); // in-memory; persist via OPFS or IndexedDB
21
+ *
22
+ * const client = new MongoLite(new BrowserSqliteAdapter(sqlJsDb));
23
+ * await client.connect();
24
+ * const users = client.collection('users');
25
+ * await users.insertOne({ name: 'Alice', age: 30 });
26
+ * await client.close();
27
+ * ```
28
+ *
29
+ * ## Limitations
30
+ *
31
+ * - **`$regex` queries are not supported** — sql.js does not ship with a `regexp()`
32
+ * SQL function. Use JS-side filtering as a workaround.
33
+ * - **`lastID` / `changes`** are not exposed by the sql.js API. `run()` always
34
+ * returns `{ lastID: 0, changes: 0 }`, which is safe because MongoLite does not
35
+ * use these values internally.
36
+ * - **Persistence** — by default sql.js stores data in memory. For durable storage
37
+ * use the OPFS back-end or export the database buffer to IndexedDB.
38
+ */
39
+ class BrowserSqliteAdapter {
40
+ /**
41
+ * @param db An initialised sql.js `Database` instance.
42
+ */
43
+ constructor(db) {
44
+ this.db = db;
45
+ }
46
+ /**
47
+ * No-op: sql.js Database is already open on construction.
48
+ */
49
+ async connect() {
50
+ // Nothing to do — the sql.js Database is ready to use immediately.
51
+ }
52
+ /**
53
+ * Closes the underlying sql.js Database and frees its WASM memory.
54
+ */
55
+ async close() {
56
+ this.db.close();
57
+ }
58
+ /**
59
+ * Executes a DML statement (INSERT, UPDATE, DELETE) with optional bound parameters.
60
+ * Returns `{ lastID: 0, changes: 0 }` — sql.js does not expose these values.
61
+ */
62
+ async run(sql, params = []) {
63
+ this.db.run(sql, params);
64
+ return { lastID: 0, changes: 0 };
65
+ }
66
+ /**
67
+ * Executes a SQL query and returns the first matching row, or `undefined`.
68
+ */
69
+ async get(sql, params = []) {
70
+ const stmt = this.db.prepare(sql);
71
+ try {
72
+ stmt.bind(params);
73
+ if (stmt.step()) {
74
+ return stmt.getAsObject();
75
+ }
76
+ return undefined;
77
+ }
78
+ finally {
79
+ stmt.free();
80
+ }
81
+ }
82
+ /**
83
+ * Executes a SQL query and returns all matching rows.
84
+ */
85
+ async all(sql, params = []) {
86
+ const stmt = this.db.prepare(sql);
87
+ try {
88
+ stmt.bind(params);
89
+ const rows = [];
90
+ while (stmt.step()) {
91
+ rows.push(stmt.getAsObject());
92
+ }
93
+ return rows;
94
+ }
95
+ finally {
96
+ stmt.free();
97
+ }
98
+ }
99
+ /**
100
+ * Executes one or more SQL statements (DDL, transactions, etc.).
101
+ */
102
+ async exec(sql) {
103
+ this.db.exec(sql);
104
+ }
105
+ }
106
+ exports.BrowserSqliteAdapter = BrowserSqliteAdapter;