@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.
- package/LICENSE +1 -1
- package/README.md +78 -2
- package/dist/adapters/node-sqlite.d.ts +65 -0
- package/dist/adapters/node-sqlite.js +241 -0
- package/dist/adapters/node-sqlite.js.map +1 -0
- package/dist/better-sqlite3.d.ts +49 -0
- package/dist/better-sqlite3.js +60 -0
- package/dist/better-sqlite3.js.map +1 -0
- package/dist/bin/mongolite-debug.js +0 -0
- package/dist/cjs/adapters/browser.js +106 -0
- package/dist/cjs/adapters/cloudflare.js +97 -0
- package/dist/cjs/adapters/node-sqlite.js +248 -0
- package/dist/cjs/better-sqlite3.js +80 -0
- package/dist/cjs/bin/mongolite-debug.js +152 -0
- package/dist/cjs/changeStream.js +341 -0
- package/dist/cjs/cloudflare.js +59 -0
- package/dist/cjs/collection.js +1748 -0
- package/dist/cjs/cursors/findCursor.js +837 -0
- package/dist/cjs/db.js +307 -0
- package/dist/cjs/debugger/query-debugger.js +297 -0
- package/dist/cjs/index.js +65 -0
- package/dist/cjs/mongo-client.js +58 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils/connection-registry.js +122 -0
- package/dist/cjs/utils/indexing.js +45 -0
- package/dist/cjs/utils/projection.js +101 -0
- package/dist/cjs/utils/queryOperators.js +142 -0
- package/dist/collection.d.ts +25 -0
- package/dist/collection.js +53 -14
- package/dist/collection.js.map +1 -1
- package/dist/cursors/findCursor.d.ts +58 -0
- package/dist/cursors/findCursor.js +88 -11
- package/dist/cursors/findCursor.js.map +1 -1
- package/dist/db.d.ts +23 -0
- package/dist/db.js +85 -39
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/utils/connection-registry.d.ts +74 -0
- package/dist/utils/connection-registry.js +117 -0
- package/dist/utils/connection-registry.js.map +1 -0
- package/package.json +20 -10
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CloudflareDurableObjectAdapter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Database adapter that wraps the Cloudflare Durable Objects `SqlStorage` API,
|
|
6
|
+
* allowing MongoLite to be used inside a Cloudflare Durable Object.
|
|
7
|
+
*
|
|
8
|
+
* ## Usage
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { DurableObject } from 'cloudflare:workers';
|
|
12
|
+
* import { MongoLite, CloudflareDurableObjectAdapter } from '@semics-tech/mongolite/cloudflare';
|
|
13
|
+
*
|
|
14
|
+
* export class MyDurableObject extends DurableObject {
|
|
15
|
+
* private client: MongoLite;
|
|
16
|
+
*
|
|
17
|
+
* constructor(ctx: DurableObjectState, env: Env) {
|
|
18
|
+
* super(ctx, env);
|
|
19
|
+
* const adapter = new CloudflareDurableObjectAdapter(ctx.storage.sql);
|
|
20
|
+
* this.client = new MongoLite(adapter);
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* async fetch(request: Request) {
|
|
24
|
+
* await this.client.connect();
|
|
25
|
+
* const users = this.client.collection('users');
|
|
26
|
+
* await users.insertOne({ name: 'Alice', age: 30 });
|
|
27
|
+
* const user = await users.findOne({ name: 'Alice' });
|
|
28
|
+
* return Response.json(user);
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ## Limitations
|
|
34
|
+
*
|
|
35
|
+
* - **`$regex` queries are not supported.** Cloudflare's SQLite does not allow custom
|
|
36
|
+
* SQL functions, so the `regexp()` UDF registered by the default Node.js adapter is
|
|
37
|
+
* unavailable. Queries using `$regex` will throw a SQL error at runtime.
|
|
38
|
+
* - `WAL` mode and most `PRAGMA` statements are not available in Cloudflare Durable
|
|
39
|
+
* Objects (Cloudflare manages this internally).
|
|
40
|
+
* - Change Streams (`collection.watch()`) use SQL triggers and a change-log table.
|
|
41
|
+
* They work in Cloudflare Durable Objects, but the polling mechanism relies on
|
|
42
|
+
* `setInterval`, which is supported in the Workers runtime.
|
|
43
|
+
*/
|
|
44
|
+
class CloudflareDurableObjectAdapter {
|
|
45
|
+
/**
|
|
46
|
+
* @param sql The `SqlStorage` instance from a Cloudflare Durable Object context
|
|
47
|
+
* (i.e. `ctx.storage.sql`).
|
|
48
|
+
*/
|
|
49
|
+
constructor(sql) {
|
|
50
|
+
this.sql = sql;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* No-op: Cloudflare Durable Objects manage the storage lifecycle automatically.
|
|
54
|
+
*/
|
|
55
|
+
async connect() {
|
|
56
|
+
// Nothing to do — the SqlStorage instance is already live.
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* No-op: Cloudflare Durable Objects manage the storage lifecycle automatically.
|
|
60
|
+
*/
|
|
61
|
+
async close() {
|
|
62
|
+
// Nothing to do — storage is managed by the Durable Object runtime.
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Executes a SQL statement that does not return rows (INSERT, UPDATE, DELETE, DDL).
|
|
66
|
+
*/
|
|
67
|
+
async run(sql, params = []) {
|
|
68
|
+
const cursor = this.sql.exec(sql, ...params);
|
|
69
|
+
return { lastID: 0, changes: cursor.rowsWritten };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Executes a SQL query and returns the first matching row, or `undefined`.
|
|
73
|
+
*/
|
|
74
|
+
async get(sql, params = []) {
|
|
75
|
+
const rows = this.sql.exec(sql, ...params).toArray();
|
|
76
|
+
return rows[0];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Executes a SQL query and returns all matching rows.
|
|
80
|
+
*/
|
|
81
|
+
async all(sql, params = []) {
|
|
82
|
+
return this.sql.exec(sql, ...params).toArray();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Executes a single SQL statement (DDL, transactions, etc.).
|
|
86
|
+
*
|
|
87
|
+
* Cloudflare's `SqlStorage.exec` only supports one statement at a time, and
|
|
88
|
+
* some valid statements (e.g. `CREATE TRIGGER ... BEGIN ...; ...; END;`)
|
|
89
|
+
* contain internal semicolons. Callers must therefore pass exactly one
|
|
90
|
+
* statement per call; this method will not attempt to split multi-statement
|
|
91
|
+
* SQL strings.
|
|
92
|
+
*/
|
|
93
|
+
async exec(sql) {
|
|
94
|
+
this.sql.exec(sql);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.CloudflareDurableObjectAdapter = CloudflareDurableObjectAdapter;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NodeSqliteAdapter = void 0;
|
|
7
|
+
exports.sharedConnectionCount = sharedConnectionCount;
|
|
8
|
+
const node_sqlite_1 = require("node:sqlite");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const connection_registry_js_1 = require("../utils/connection-registry.js");
|
|
11
|
+
/** Shared `node:sqlite` handles for this process, keyed by connection identity. */
|
|
12
|
+
const sharedConnections = new connection_registry_js_1.ConnectionRegistry();
|
|
13
|
+
/** Exposed for tests and diagnostics. */
|
|
14
|
+
function sharedConnectionCount() {
|
|
15
|
+
return sharedConnections.size;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Database adapter backed by Node.js's built-in `node:sqlite` module — no native
|
|
19
|
+
* addon, no prebuilt binary, no `node-gyp`. Requires Node.js 22.5.0+ (the module
|
|
20
|
+
* is marked experimental by Node.js until it graduates to stable).
|
|
21
|
+
*
|
|
22
|
+
* ## Usage
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { MongoLite, NodeSqliteAdapter } from '@semics-tech/mongolite';
|
|
26
|
+
*
|
|
27
|
+
* const client = new MongoLite(new NodeSqliteAdapter('./data.db'));
|
|
28
|
+
* await client.connect();
|
|
29
|
+
* const users = client.collection('users');
|
|
30
|
+
* await users.insertOne({ name: 'Alice', age: 30 });
|
|
31
|
+
* await client.close();
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ## Limitations
|
|
35
|
+
*
|
|
36
|
+
* - **Experimental.** `node:sqlite` is still marked experimental upstream and its
|
|
37
|
+
* API may change between Node.js minor versions.
|
|
38
|
+
* - **Node.js 22.5.0+ only.** Importing this adapter on an older runtime (or on
|
|
39
|
+
* Bun/Deno without a `node:sqlite` polyfill) throws at import time.
|
|
40
|
+
* - Rows are returned with a `null` prototype (per `node:sqlite`'s API). Plain
|
|
41
|
+
* property access (`row.foo`) and `JSON.stringify`/`Object.keys` all work as
|
|
42
|
+
* expected; `row.hasOwnProperty(...)` does not.
|
|
43
|
+
*/
|
|
44
|
+
class NodeSqliteAdapter {
|
|
45
|
+
/**
|
|
46
|
+
* @param dbPathOrOptions Path to the SQLite database file (use `':memory:'`
|
|
47
|
+
* for an in-memory database), or an options object mirroring the built-in
|
|
48
|
+
* `better-sqlite3` adapter's `MongoLiteOptions`.
|
|
49
|
+
*/
|
|
50
|
+
constructor(dbPathOrOptions) {
|
|
51
|
+
this.db = null;
|
|
52
|
+
this.openPromise = null;
|
|
53
|
+
let shared;
|
|
54
|
+
if (typeof dbPathOrOptions === 'string') {
|
|
55
|
+
this.filePath = dbPathOrOptions;
|
|
56
|
+
this.verbose = false;
|
|
57
|
+
this.readOnly = false;
|
|
58
|
+
this.WAL = true;
|
|
59
|
+
this.busyTimeout = connection_registry_js_1.DEFAULT_BUSY_TIMEOUT_MS;
|
|
60
|
+
shared = false;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.filePath = dbPathOrOptions.filePath;
|
|
64
|
+
this.verbose = dbPathOrOptions.verbose || false;
|
|
65
|
+
this.readOnly = dbPathOrOptions.readOnly || false;
|
|
66
|
+
this.WAL = dbPathOrOptions.WAL ?? true;
|
|
67
|
+
this.busyTimeout = dbPathOrOptions.busyTimeout ?? connection_registry_js_1.DEFAULT_BUSY_TIMEOUT_MS;
|
|
68
|
+
shared = dbPathOrOptions.shared ?? false;
|
|
69
|
+
}
|
|
70
|
+
this.shareKey =
|
|
71
|
+
shared && (0, connection_registry_js_1.isShareablePath)(this.filePath)
|
|
72
|
+
? (0, connection_registry_js_1.buildConnectionKey)({
|
|
73
|
+
backend: 'node:sqlite',
|
|
74
|
+
filePath: node_path_1.default.resolve(this.filePath),
|
|
75
|
+
readOnly: this.readOnly,
|
|
76
|
+
WAL: this.WAL,
|
|
77
|
+
busyTimeout: this.busyTimeout,
|
|
78
|
+
verbose: this.verbose,
|
|
79
|
+
})
|
|
80
|
+
: null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Opens the database connection if it's not already open.
|
|
84
|
+
*/
|
|
85
|
+
async connect() {
|
|
86
|
+
if (this.db && this.openPromise) {
|
|
87
|
+
return this.openPromise;
|
|
88
|
+
}
|
|
89
|
+
if (this.db) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
this.openPromise = new Promise((resolve, reject) => {
|
|
93
|
+
try {
|
|
94
|
+
// Only runs on a genuinely new connection — when sharing, an existing
|
|
95
|
+
// handle already has its pragmas and UDFs applied.
|
|
96
|
+
const openConnection = () => {
|
|
97
|
+
const db = new node_sqlite_1.DatabaseSync(this.filePath, { readOnly: this.readOnly });
|
|
98
|
+
if (this.WAL && !this.readOnly) {
|
|
99
|
+
db.exec('PRAGMA journal_mode = WAL');
|
|
100
|
+
}
|
|
101
|
+
// Wait rather than immediately throwing SQLITE_BUSY when another
|
|
102
|
+
// connection (often another process) holds a lock.
|
|
103
|
+
if (this.busyTimeout > 0) {
|
|
104
|
+
db.exec(`PRAGMA busy_timeout = ${this.busyTimeout}`);
|
|
105
|
+
}
|
|
106
|
+
// Register regexp UDF for $regex operator support.
|
|
107
|
+
// WARNING: Patterns are compiled via JavaScript RegExp. User-supplied patterns that are
|
|
108
|
+
// not validated for catastrophic backtracking (ReDoS) could block the event loop.
|
|
109
|
+
// Avoid using untrusted/unvalidated patterns with $regex in security-sensitive contexts.
|
|
110
|
+
db.function('regexp', { deterministic: true }, (pattern, value) => {
|
|
111
|
+
if (typeof pattern !== 'string' || value === null || value === undefined)
|
|
112
|
+
return 0;
|
|
113
|
+
try {
|
|
114
|
+
return new RegExp(pattern).test(String(value)) ? 1 : 0;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Register regexp_flags UDF for $regex with $options support
|
|
121
|
+
db.function('regexp_flags', { deterministic: true }, (pattern, flags, value) => {
|
|
122
|
+
if (typeof pattern !== 'string' || value === null || value === undefined)
|
|
123
|
+
return 0;
|
|
124
|
+
try {
|
|
125
|
+
const f = typeof flags === 'string' ? flags : '';
|
|
126
|
+
return new RegExp(pattern, f).test(String(value)) ? 1 : 0;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
if (this.verbose) {
|
|
133
|
+
console.log(`SQLite database opened (node:sqlite): ${this.filePath}`);
|
|
134
|
+
}
|
|
135
|
+
return db;
|
|
136
|
+
};
|
|
137
|
+
this.db = this.shareKey
|
|
138
|
+
? sharedConnections.acquire(this.shareKey, openConnection)
|
|
139
|
+
: openConnection();
|
|
140
|
+
resolve();
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
console.error(`Error opening database ${this.filePath}:`, err.message);
|
|
144
|
+
this.db = null;
|
|
145
|
+
this.openPromise = null;
|
|
146
|
+
reject(err);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return this.openPromise;
|
|
150
|
+
}
|
|
151
|
+
async ensureConnected() {
|
|
152
|
+
if (!this.db || !this.openPromise) {
|
|
153
|
+
await this.connect();
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
await this.openPromise;
|
|
157
|
+
}
|
|
158
|
+
if (!this.db) {
|
|
159
|
+
throw new Error('Database is not connected. Connection attempt failed.');
|
|
160
|
+
}
|
|
161
|
+
return this.db;
|
|
162
|
+
}
|
|
163
|
+
async run(sql, params = []) {
|
|
164
|
+
const dbInstance = await this.ensureConnected();
|
|
165
|
+
try {
|
|
166
|
+
const result = dbInstance.prepare(sql).run(...params);
|
|
167
|
+
return {
|
|
168
|
+
lastID: Number(result.lastInsertRowid),
|
|
169
|
+
changes: Number(result.changes),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.error(`Error running SQL: ${sql}`, err);
|
|
174
|
+
throw err;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async get(sql, params = []) {
|
|
178
|
+
const dbInstance = await this.ensureConnected();
|
|
179
|
+
try {
|
|
180
|
+
return dbInstance.prepare(sql).get(...params);
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
console.error(`Error getting SQL: ${sql}`, err);
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async all(sql, params = []) {
|
|
188
|
+
const dbInstance = await this.ensureConnected();
|
|
189
|
+
try {
|
|
190
|
+
return dbInstance.prepare(sql).all(...params);
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
console.error(`Error getting all SQL: ${sql}`, err);
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async exec(sql) {
|
|
198
|
+
const dbInstance = await this.ensureConnected();
|
|
199
|
+
try {
|
|
200
|
+
dbInstance.exec(sql);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
console.error(`Error executing SQL: ${sql}`, err);
|
|
204
|
+
throw err;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async close() {
|
|
208
|
+
if (this.openPromise) {
|
|
209
|
+
await this.openPromise;
|
|
210
|
+
}
|
|
211
|
+
if (this.db) {
|
|
212
|
+
try {
|
|
213
|
+
// When shared, only the last holder actually closes the handle.
|
|
214
|
+
const isLastHolder = this.shareKey
|
|
215
|
+
? sharedConnections.release(this.shareKey, this.db)
|
|
216
|
+
: true;
|
|
217
|
+
if (isLastHolder) {
|
|
218
|
+
this.db.close();
|
|
219
|
+
}
|
|
220
|
+
if (this.verbose) {
|
|
221
|
+
console.log(isLastHolder
|
|
222
|
+
? `SQLite database closed (node:sqlite): ${this.filePath}`
|
|
223
|
+
: `SQLite database released (still in use): ${this.filePath}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
console.error(`Error closing database ${this.filePath}:`, err.message);
|
|
228
|
+
throw err;
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
this.db = null;
|
|
232
|
+
this.openPromise = null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
this.db = null;
|
|
237
|
+
this.openPromise = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Gets the underlying `node:sqlite` `DatabaseSync` instance.
|
|
242
|
+
* Useful for operations not covered by this wrapper.
|
|
243
|
+
*/
|
|
244
|
+
async getDbInstance() {
|
|
245
|
+
return this.ensureConnected();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
exports.NodeSqliteAdapter = NodeSqliteAdapter;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `better-sqlite3`-backed entry point for MongoLite.
|
|
4
|
+
*
|
|
5
|
+
* The main `@semics-tech/mongolite` entry point defaults to `NodeSqliteAdapter`, backed by
|
|
6
|
+
* Node.js's built-in `node:sqlite` module, which requires Node.js 22.5.0+. Import
|
|
7
|
+
* from `@semics-tech/mongolite/better-sqlite3` instead if you need to run on an older
|
|
8
|
+
* Node.js runtime, or you simply prefer the more battle-tested `better-sqlite3`
|
|
9
|
+
* native addon.
|
|
10
|
+
*
|
|
11
|
+
* `better-sqlite3` is an **optional dependency** of this package — install it
|
|
12
|
+
* yourself if it wasn't already pulled in:
|
|
13
|
+
*
|
|
14
|
+
* ```bash
|
|
15
|
+
* npm install better-sqlite3
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## Usage
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { MongoLite } from '@semics-tech/mongolite/better-sqlite3';
|
|
22
|
+
*
|
|
23
|
+
* const client = new MongoLite('./myapp.sqlite');
|
|
24
|
+
* await client.connect();
|
|
25
|
+
* const users = client.collection('users');
|
|
26
|
+
* await users.insertOne({ name: 'Alice', age: 30 });
|
|
27
|
+
* await client.close();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
33
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
34
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
35
|
+
}
|
|
36
|
+
Object.defineProperty(o, k2, desc);
|
|
37
|
+
}) : (function(o, m, k, k2) {
|
|
38
|
+
if (k2 === undefined) k2 = k;
|
|
39
|
+
o[k2] = m[k];
|
|
40
|
+
}));
|
|
41
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
42
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.MongoLite = exports.SQLiteDB = exports.ChangeStream = exports.MongoLiteCollection = void 0;
|
|
46
|
+
const db_js_1 = require("./db.js");
|
|
47
|
+
const mongo_client_js_1 = require("./mongo-client.js");
|
|
48
|
+
var collection_js_1 = require("./collection.js");
|
|
49
|
+
Object.defineProperty(exports, "MongoLiteCollection", { enumerable: true, get: function () { return collection_js_1.MongoLiteCollection; } });
|
|
50
|
+
__exportStar(require("./types.js"), exports);
|
|
51
|
+
var changeStream_js_1 = require("./changeStream.js");
|
|
52
|
+
Object.defineProperty(exports, "ChangeStream", { enumerable: true, get: function () { return changeStream_js_1.ChangeStream; } });
|
|
53
|
+
var db_js_2 = require("./db.js");
|
|
54
|
+
Object.defineProperty(exports, "SQLiteDB", { enumerable: true, get: function () { return db_js_2.SQLiteDB; } });
|
|
55
|
+
/**
|
|
56
|
+
* MongoLite client backed by the `better-sqlite3` native addon.
|
|
57
|
+
*
|
|
58
|
+
* You can construct it with:
|
|
59
|
+
* - A file path string — uses the built-in `SQLiteDB` (`better-sqlite3`) adapter.
|
|
60
|
+
* - A `MongoLiteClientOptions` object — uses the built-in adapter with options.
|
|
61
|
+
* - An `IDatabaseAdapter` instance — use a custom adapter for other backends.
|
|
62
|
+
*/
|
|
63
|
+
class MongoLite extends mongo_client_js_1.MongoLite {
|
|
64
|
+
constructor(dbPathOrOptions, options = {}) {
|
|
65
|
+
if (dbPathOrOptions &&
|
|
66
|
+
typeof dbPathOrOptions === 'object' &&
|
|
67
|
+
typeof dbPathOrOptions.connect === 'function' &&
|
|
68
|
+
typeof dbPathOrOptions.run === 'function' &&
|
|
69
|
+
typeof dbPathOrOptions.get === 'function' &&
|
|
70
|
+
typeof dbPathOrOptions.all === 'function' &&
|
|
71
|
+
typeof dbPathOrOptions.exec === 'function' &&
|
|
72
|
+
typeof dbPathOrOptions.close === 'function') {
|
|
73
|
+
super(dbPathOrOptions, options);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
super(new db_js_1.SQLiteDB(dbPathOrOptions), options);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.MongoLite = MongoLite;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const query_debugger_js_1 = require("../debugger/query-debugger.js");
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
// CLI argument parsing
|
|
43
|
+
function parseArgs() {
|
|
44
|
+
const args = process.argv.slice(2);
|
|
45
|
+
const options = {};
|
|
46
|
+
for (let i = 0; i < args.length; i++) {
|
|
47
|
+
const arg = args[i];
|
|
48
|
+
if (arg === '--verbose' || arg === '-v') {
|
|
49
|
+
options.verbose = true;
|
|
50
|
+
}
|
|
51
|
+
else if (arg === '--collection' || arg === '-c') {
|
|
52
|
+
options.collection = args[++i];
|
|
53
|
+
}
|
|
54
|
+
else if (arg === '--database' || arg === '-d') {
|
|
55
|
+
options.database = args[++i];
|
|
56
|
+
}
|
|
57
|
+
else if (arg === '--help' || arg === '-h') {
|
|
58
|
+
options.help = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
function showHelp() {
|
|
64
|
+
console.log('MongoLite Query Debugger');
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log('Interactive debugging tool for MongoLite queries.');
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log('Usage: npx mongolite-debug [options]');
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log('Options:');
|
|
71
|
+
console.log(' -d, --database <path> Database file path (default: ./mongolite.db)');
|
|
72
|
+
console.log(' -c, --collection <name> Initial collection to use');
|
|
73
|
+
console.log(' -v, --verbose Enable verbose output');
|
|
74
|
+
console.log(' -h, --help Show this help');
|
|
75
|
+
console.log('');
|
|
76
|
+
console.log('Interactive Commands:');
|
|
77
|
+
console.log(' .help Show available commands');
|
|
78
|
+
console.log(' .collections List all collections');
|
|
79
|
+
console.log(' .use <collection> Select a collection to work with');
|
|
80
|
+
console.log(' .find <filter> Convert find filter to SQL and execute');
|
|
81
|
+
console.log(' .sql <query> Execute raw SQL query');
|
|
82
|
+
console.log(' .last Show last generated SQL');
|
|
83
|
+
console.log(' .sample [count] Show sample documents from current collection');
|
|
84
|
+
console.log(' .exit Exit the debugger');
|
|
85
|
+
console.log('');
|
|
86
|
+
console.log('Examples:');
|
|
87
|
+
console.log(' npx mongolite-debug -d ./myapp.db -c users');
|
|
88
|
+
console.log(' npx mongolite-debug --verbose');
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(' # Inside the debugger:');
|
|
91
|
+
console.log(' .use users');
|
|
92
|
+
console.log(' .find {"age": {"$gt": 25}}');
|
|
93
|
+
console.log(' .sql SELECT * FROM users WHERE json_extract(data, "$.age") > 25');
|
|
94
|
+
console.log('');
|
|
95
|
+
}
|
|
96
|
+
async function main() {
|
|
97
|
+
const options = parseArgs();
|
|
98
|
+
if (options.help) {
|
|
99
|
+
showHelp();
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
// Set default database path if not provided
|
|
103
|
+
if (!options.database) {
|
|
104
|
+
// Look for common database files in the current directory
|
|
105
|
+
const cwd = process.cwd();
|
|
106
|
+
const commonNames = [
|
|
107
|
+
'mongolite.db',
|
|
108
|
+
'mongolite.sqlite',
|
|
109
|
+
'database.db',
|
|
110
|
+
'database.sqlite',
|
|
111
|
+
'app.db',
|
|
112
|
+
'app.sqlite',
|
|
113
|
+
];
|
|
114
|
+
// Check if any common database files exist
|
|
115
|
+
const { existsSync } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
116
|
+
let foundDb = false;
|
|
117
|
+
for (const name of commonNames) {
|
|
118
|
+
const dbPath = path_1.default.join(cwd, name);
|
|
119
|
+
if (existsSync(dbPath)) {
|
|
120
|
+
options.database = dbPath;
|
|
121
|
+
foundDb = true;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (!foundDb) {
|
|
126
|
+
options.database = path_1.default.join(cwd, 'mongolite.db');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log('🔍 MongoLite Query Debugger (npx version)');
|
|
130
|
+
console.log(`📁 Database: ${options.database}`);
|
|
131
|
+
if (options.collection) {
|
|
132
|
+
console.log(`📋 Initial collection: ${options.collection}`);
|
|
133
|
+
}
|
|
134
|
+
console.log('');
|
|
135
|
+
try {
|
|
136
|
+
const queryDebugger = new query_debugger_js_1.QueryDebugger(options);
|
|
137
|
+
await queryDebugger.start();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error('Fatal error:', error instanceof Error ? error.message : error);
|
|
141
|
+
if (error instanceof Error && error.message.includes('SQLITE_CANTOPEN')) {
|
|
142
|
+
console.error('');
|
|
143
|
+
console.error('💡 Tip: Make sure the database file exists or specify a different path with -d');
|
|
144
|
+
console.error(' Example: npx mongolite-debug -d ./path/to/your/database.db');
|
|
145
|
+
}
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
main().catch((error) => {
|
|
150
|
+
console.error('Unexpected error:', error);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
});
|