@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/dist/cjs/db.js ADDED
@@ -0,0 +1,307 @@
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.SQLiteDB = exports.DEFAULT_BUSY_TIMEOUT_MS = void 0;
7
+ exports.sharedConnectionCount = sharedConnectionCount;
8
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const connection_registry_js_1 = require("./utils/connection-registry.js");
11
+ Object.defineProperty(exports, "DEFAULT_BUSY_TIMEOUT_MS", { enumerable: true, get: function () { return connection_registry_js_1.DEFAULT_BUSY_TIMEOUT_MS; } });
12
+ /** Shared `better-sqlite3` handles for this process, keyed by connection identity. */
13
+ const sharedConnections = new connection_registry_js_1.ConnectionRegistry();
14
+ /** Exposed for tests and diagnostics. */
15
+ function sharedConnectionCount() {
16
+ return sharedConnections.size;
17
+ }
18
+ /**
19
+ * SQLiteDB class provides a wrapper around the better-sqlite3 library
20
+ * to simplify database operations.
21
+ */
22
+ class SQLiteDB {
23
+ /**
24
+ * Creates an instance of SQLiteDB.
25
+ * @param {string | MongoLiteOptions} dbPathOrOptions - The path to the SQLite database file or an options object.
26
+ * Use ':memory:' for an in-memory database.
27
+ */
28
+ constructor(dbPathOrOptions) {
29
+ this.db = null;
30
+ this.openPromise = null;
31
+ let shared;
32
+ if (typeof dbPathOrOptions === 'string') {
33
+ this.filePath = dbPathOrOptions;
34
+ this.verbose = false;
35
+ this.readOnly = false;
36
+ this.WAL = true;
37
+ this.busyTimeout = connection_registry_js_1.DEFAULT_BUSY_TIMEOUT_MS;
38
+ shared = false;
39
+ }
40
+ else {
41
+ this.filePath = dbPathOrOptions.filePath;
42
+ this.verbose = dbPathOrOptions.verbose || false;
43
+ this.readOnly = dbPathOrOptions.readOnly || false;
44
+ this.WAL = dbPathOrOptions.WAL ?? true;
45
+ this.busyTimeout = dbPathOrOptions.busyTimeout ?? connection_registry_js_1.DEFAULT_BUSY_TIMEOUT_MS;
46
+ shared = dbPathOrOptions.shared ?? false;
47
+ }
48
+ this.shareKey =
49
+ shared && (0, connection_registry_js_1.isShareablePath)(this.filePath)
50
+ ? (0, connection_registry_js_1.buildConnectionKey)({
51
+ backend: 'better-sqlite3',
52
+ filePath: node_path_1.default.resolve(this.filePath),
53
+ readOnly: this.readOnly,
54
+ WAL: this.WAL,
55
+ busyTimeout: this.busyTimeout,
56
+ verbose: this.verbose,
57
+ })
58
+ : null;
59
+ }
60
+ /**
61
+ * Opens the database connection if it's not already open.
62
+ * @returns {Promise<void>} A promise that resolves when the connection is open.
63
+ */
64
+ async connect() {
65
+ if (this.db && this.openPromise) {
66
+ return this.openPromise;
67
+ }
68
+ if (this.db) {
69
+ // Already connected and openPromise is null (should not happen with proper logic)
70
+ return Promise.resolve();
71
+ }
72
+ this.openPromise = new Promise((resolve, reject) => {
73
+ try {
74
+ // Only runs on a genuinely new connection — when sharing, an existing
75
+ // handle already has its pragmas and UDFs applied.
76
+ const openConnection = () => {
77
+ const options = {
78
+ readonly: this.readOnly,
79
+ verbose: this.verbose ? console.log : undefined,
80
+ };
81
+ const db = new better_sqlite3_1.default(this.filePath, options);
82
+ // Enable Write-Ahead Logging if requested
83
+ if (this.WAL && !this.readOnly) {
84
+ db.pragma('journal_mode = WAL');
85
+ }
86
+ // Wait rather than immediately throwing SQLITE_BUSY when another
87
+ // connection (often another process) holds a lock.
88
+ if (this.busyTimeout > 0) {
89
+ db.pragma(`busy_timeout = ${this.busyTimeout}`);
90
+ }
91
+ // Register regexp UDF for $regex operator support.
92
+ // WARNING: Patterns are compiled via JavaScript RegExp. User-supplied patterns that are
93
+ // not validated for catastrophic backtracking (ReDoS) could block the event loop.
94
+ // Avoid using untrusted/unvalidated patterns with $regex in security-sensitive contexts.
95
+ db.function('regexp', { deterministic: true }, (pattern, value) => {
96
+ if (typeof pattern !== 'string' || value === null || value === undefined)
97
+ return 0;
98
+ try {
99
+ return new RegExp(pattern).test(String(value)) ? 1 : 0;
100
+ }
101
+ catch {
102
+ return 0;
103
+ }
104
+ });
105
+ // Register regexp_flags UDF for $regex with $options support
106
+ db.function('regexp_flags', { deterministic: true }, (pattern, flags, value) => {
107
+ if (typeof pattern !== 'string' || value === null || value === undefined)
108
+ return 0;
109
+ try {
110
+ const f = typeof flags === 'string' ? flags : '';
111
+ return new RegExp(pattern, f).test(String(value)) ? 1 : 0;
112
+ }
113
+ catch {
114
+ return 0;
115
+ }
116
+ });
117
+ if (this.verbose) {
118
+ console.log(`SQLite database opened: ${this.filePath}`);
119
+ }
120
+ return db;
121
+ };
122
+ this.db = this.shareKey
123
+ ? sharedConnections.acquire(this.shareKey, openConnection)
124
+ : openConnection();
125
+ resolve();
126
+ }
127
+ catch (err) {
128
+ console.error(`Error opening database ${this.filePath}:`, err.message);
129
+ this.db = null;
130
+ this.openPromise = null;
131
+ reject(err);
132
+ }
133
+ });
134
+ return this.openPromise;
135
+ }
136
+ /**
137
+ * Ensures the database connection is open before performing an operation.
138
+ * @private
139
+ * @returns {Promise<Database.Database>} The active database instance.
140
+ * @throws {Error} If the database is not connected.
141
+ */
142
+ async ensureConnected() {
143
+ if (!this.db || !this.openPromise) {
144
+ await this.connect();
145
+ }
146
+ else {
147
+ await this.openPromise; // Wait for any ongoing connection attempt
148
+ }
149
+ if (!this.db) {
150
+ // This should ideally not be reached if connect() works correctly
151
+ throw new Error('Database is not connected. Connection attempt failed.');
152
+ }
153
+ return this.db;
154
+ }
155
+ /**
156
+ * Executes a SQL query that does not return rows (e.g., INSERT, UPDATE, DELETE, CREATE).
157
+ * @param {string} sql - The SQL query string.
158
+ * @param {any[]} [params=[]] - Parameters for the SQL query.
159
+ * @returns {Promise<{ lastID: number, changes: number }>} Result of the execution.
160
+ */
161
+ async run(sql, params = []) {
162
+ const dbInstance = await this.ensureConnected();
163
+ try {
164
+ const result = dbInstance.prepare(sql).run(...params);
165
+ return {
166
+ lastID: result.lastInsertRowid,
167
+ changes: result.changes,
168
+ };
169
+ }
170
+ catch (err) {
171
+ console.error(`Error running SQL: ${sql}`, err);
172
+ throw err;
173
+ }
174
+ }
175
+ /**
176
+ * Executes a SQL query that returns a single row.
177
+ * @param {string} sql - The SQL query string.
178
+ * @param {any[]} [params=[]] - Parameters for the SQL query.
179
+ * @returns {Promise<T | undefined>} The first row found, or undefined.
180
+ */
181
+ async get(sql, params = []) {
182
+ const dbInstance = await this.ensureConnected();
183
+ try {
184
+ return dbInstance.prepare(sql).get(...params);
185
+ }
186
+ catch (err) {
187
+ console.error(`Error getting SQL: ${sql}`, err);
188
+ throw err;
189
+ }
190
+ }
191
+ /**
192
+ * Executes a SQL query that returns multiple rows.
193
+ * @param {string} sql - The SQL query string.
194
+ * @param {any[]} [params=[]] - Parameters for the SQL query.
195
+ * @returns {Promise<T[]>} An array of rows.
196
+ */
197
+ async all(sql, params = []) {
198
+ const dbInstance = await this.ensureConnected();
199
+ try {
200
+ return dbInstance.prepare(sql).all(...params);
201
+ }
202
+ catch (err) {
203
+ console.error(`Error getting all SQL: ${sql}`, err);
204
+ throw err;
205
+ }
206
+ }
207
+ /**
208
+ * Executes multiple SQL statements.
209
+ * @param {string} sql - SQL string with multiple statements.
210
+ * @returns {Promise<void>}
211
+ */
212
+ async exec(sql) {
213
+ const dbInstance = await this.ensureConnected();
214
+ try {
215
+ dbInstance.exec(sql);
216
+ return Promise.resolve();
217
+ }
218
+ catch (err) {
219
+ console.error(`Error executing SQL: ${sql}`, err);
220
+ throw err;
221
+ }
222
+ }
223
+ /**
224
+ * Closes the database connection.
225
+ * @returns {Promise<void>} A promise that resolves when the connection is closed.
226
+ */
227
+ async close() {
228
+ if (this.openPromise) {
229
+ await this.openPromise; // Ensure any pending connection attempt is finished
230
+ }
231
+ if (this.db) {
232
+ try {
233
+ // When shared, only the last holder actually closes the handle.
234
+ const isLastHolder = this.shareKey
235
+ ? sharedConnections.release(this.shareKey, this.db)
236
+ : true;
237
+ if (isLastHolder) {
238
+ this.db.close();
239
+ }
240
+ if (this.verbose) {
241
+ console.log(isLastHolder
242
+ ? `SQLite database closed: ${this.filePath}`
243
+ : `SQLite database released (still in use): ${this.filePath}`);
244
+ }
245
+ }
246
+ catch (err) {
247
+ console.error(`Error closing database ${this.filePath}:`, err.message);
248
+ throw err; // Re-throw the error to indicate failure
249
+ }
250
+ finally {
251
+ this.db = null;
252
+ this.openPromise = null;
253
+ }
254
+ }
255
+ else {
256
+ // If db is null, it's already considered closed or was never opened.
257
+ this.db = null;
258
+ this.openPromise = null;
259
+ }
260
+ }
261
+ /**
262
+ * Gets the underlying better-sqlite3 Database instance.
263
+ * Useful for operations not covered by this wrapper, like transactions.
264
+ * @returns {Promise<Database.Database>} The raw database object.
265
+ * @throws {Error} If the database is not connected.
266
+ */
267
+ async getDbInstance() {
268
+ return this.ensureConnected();
269
+ }
270
+ /**
271
+ * Prepares a SQL statement for later execution.
272
+ * @param {string} sql - The SQL query string.
273
+ * @returns {Promise<Statement>} A prepared statement that can be executed multiple times.
274
+ */
275
+ async prepare(sql) {
276
+ const dbInstance = await this.ensureConnected();
277
+ return dbInstance.prepare(sql);
278
+ }
279
+ /**
280
+ * Begins a transaction.
281
+ * @returns {Promise<void>}
282
+ */
283
+ async beginTransaction() {
284
+ const dbInstance = await this.ensureConnected();
285
+ dbInstance.prepare('BEGIN').run();
286
+ return Promise.resolve();
287
+ }
288
+ /**
289
+ * Commits a transaction.
290
+ * @returns {Promise<void>}
291
+ */
292
+ async commitTransaction() {
293
+ const dbInstance = await this.ensureConnected();
294
+ dbInstance.prepare('COMMIT').run();
295
+ return Promise.resolve();
296
+ }
297
+ /**
298
+ * Rolls back a transaction.
299
+ * @returns {Promise<void>}
300
+ */
301
+ async rollbackTransaction() {
302
+ const dbInstance = await this.ensureConnected();
303
+ dbInstance.prepare('ROLLBACK').run();
304
+ return Promise.resolve();
305
+ }
306
+ }
307
+ exports.SQLiteDB = SQLiteDB;
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.QueryDebugger = void 0;
40
+ const index_js_1 = require("../index.js");
41
+ const findCursor_js_1 = require("../cursors/findCursor.js");
42
+ const readline = __importStar(require("readline"));
43
+ const path_1 = __importDefault(require("path"));
44
+ class QueryDebugger {
45
+ constructor(options = {}) {
46
+ this.currentCollection = null;
47
+ this.lastFilter = null;
48
+ this.lastSqlQuery = null;
49
+ const dbPath = options.database || path_1.default.join(process.cwd(), 'mongolite.db');
50
+ this.client = new index_js_1.MongoLite(dbPath, { verbose: options.verbose });
51
+ this.rl = readline.createInterface({
52
+ input: process.stdin,
53
+ output: process.stdout,
54
+ prompt: 'mongolite-debug> ',
55
+ });
56
+ if (options.collection) {
57
+ this.currentCollection = options.collection;
58
+ }
59
+ }
60
+ async start() {
61
+ console.log('Commands:');
62
+ console.log(' .help - Show this help');
63
+ console.log(' .collections - List all collections');
64
+ console.log(' .use <collection> - Select a collection to work with');
65
+ console.log(' .find <filter> - Convert find filter to SQL and execute');
66
+ console.log(' .sql <query> - Execute raw SQL query');
67
+ console.log(' .last - Show last generated SQL');
68
+ console.log(' .sample [count] - Show sample documents from current collection');
69
+ console.log(' .exit - Exit the debugger');
70
+ console.log('');
71
+ try {
72
+ await this.client.connect();
73
+ console.log('✅ Connected to database.');
74
+ }
75
+ catch (error) {
76
+ console.error('❌ Failed to connect to database:', error instanceof Error ? error.message : error);
77
+ throw error;
78
+ }
79
+ if (this.currentCollection) {
80
+ console.log(`Current collection: ${this.currentCollection}`);
81
+ }
82
+ this.rl.prompt();
83
+ this.rl.on('line', async (input) => {
84
+ const trimmed = input.trim();
85
+ if (trimmed === '.exit') {
86
+ await this.cleanup();
87
+ return;
88
+ }
89
+ try {
90
+ await this.handleCommand(trimmed);
91
+ }
92
+ catch (error) {
93
+ console.error('Error:', error instanceof Error ? error.message : error);
94
+ }
95
+ this.rl.prompt();
96
+ });
97
+ this.rl.on('close', async () => {
98
+ await this.cleanup();
99
+ });
100
+ }
101
+ async handleCommand(input) {
102
+ if (input === '.help') {
103
+ this.showHelp();
104
+ return;
105
+ }
106
+ if (input === '.collections') {
107
+ await this.listCollections();
108
+ return;
109
+ }
110
+ if (input.startsWith('.use ')) {
111
+ const collection = input.slice(5).trim();
112
+ this.currentCollection = collection;
113
+ console.log(`Switched to collection: ${collection}`);
114
+ return;
115
+ }
116
+ if (input.startsWith('.find ')) {
117
+ await this.handleFindCommand(input.slice(6).trim());
118
+ return;
119
+ }
120
+ if (input.startsWith('.sql ')) {
121
+ await this.handleSqlCommand(input.slice(5).trim());
122
+ return;
123
+ }
124
+ if (input === '.last') {
125
+ this.showLastQuery();
126
+ return;
127
+ }
128
+ if (input.startsWith('.sample')) {
129
+ const parts = input.split(' ');
130
+ const count = parts.length > 1 ? parseInt(parts[1]) || 5 : 5;
131
+ await this.showSample(count);
132
+ return;
133
+ }
134
+ console.log('Unknown command. Type .help for available commands.');
135
+ }
136
+ showHelp() {
137
+ console.log('');
138
+ console.log('Available commands:');
139
+ console.log(' .help - Show this help');
140
+ console.log(' .collections - List all collections');
141
+ console.log(' .use <collection> - Select a collection to work with');
142
+ console.log(' .find <filter> - Convert find filter to SQL and execute');
143
+ console.log(' .sql <query> - Execute raw SQL query');
144
+ console.log(' .last - Show last generated SQL');
145
+ console.log(' .sample [count] - Show sample documents from current collection');
146
+ console.log(' .exit - Exit the debugger');
147
+ console.log('');
148
+ console.log('Examples:');
149
+ console.log(' .use users');
150
+ console.log(' .find {"age": {"$gt": 25}}');
151
+ console.log(' .find {"name": "John", "age": {"$in": [25, 30, 35]}}');
152
+ console.log(' .sql SELECT * FROM users WHERE json_extract(data, "$.age") > 25');
153
+ console.log('');
154
+ }
155
+ async listCollections() {
156
+ try {
157
+ const collections = await this.client.listCollections().toArray();
158
+ if (collections.length === 0) {
159
+ console.log('No collections found.');
160
+ }
161
+ else {
162
+ console.log('Collections:');
163
+ collections.forEach((collection) => {
164
+ const marker = collection === this.currentCollection ? ' (current)' : '';
165
+ console.log(` - ${collection}${marker}`);
166
+ });
167
+ }
168
+ }
169
+ catch (error) {
170
+ console.error('Error listing collections:', error instanceof Error ? error.message : error);
171
+ }
172
+ }
173
+ async handleFindCommand(filterStr) {
174
+ if (!this.currentCollection) {
175
+ console.log('No collection selected. Use .use <collection> first.');
176
+ return;
177
+ }
178
+ let filter;
179
+ try {
180
+ filter = JSON.parse(filterStr);
181
+ }
182
+ catch (error) {
183
+ console.log('Invalid JSON filter:', error instanceof Error ? error.message : error);
184
+ return;
185
+ }
186
+ this.lastFilter = filter;
187
+ // Create a collection and a find cursor to extract the SQL
188
+ const collection = this.client.collection(this.currentCollection);
189
+ // We need to access the internal SQL generation. Let's create a custom cursor
190
+ // that exposes the SQL generation logic
191
+ const debugCursor = new DebugFindCursor(collection.db, this.currentCollection, filter, { verbose: true });
192
+ this.lastSqlQuery = debugCursor.getSQL();
193
+ console.log('');
194
+ console.log('📝 Generated SQL:');
195
+ console.log('Query:', this.lastSqlQuery.sql);
196
+ console.log('Parameters:', JSON.stringify(this.lastSqlQuery.params));
197
+ console.log('');
198
+ // Execute the query and show results
199
+ try {
200
+ const results = await debugCursor.toArray();
201
+ console.log(`📊 Results (${results.length} documents):`);
202
+ if (results.length > 0) {
203
+ results.forEach((doc, index) => {
204
+ console.log(`[${index}]`, JSON.stringify(doc, null, 2));
205
+ });
206
+ }
207
+ else {
208
+ console.log('No documents found.');
209
+ }
210
+ }
211
+ catch (error) {
212
+ console.error('Execution error:', error instanceof Error ? error.message : error);
213
+ }
214
+ }
215
+ async handleSqlCommand(sqlQuery) {
216
+ if (!this.currentCollection) {
217
+ console.log('No collection selected. Use .use <collection> first.');
218
+ return;
219
+ }
220
+ try {
221
+ // Get the database instance to execute raw SQL
222
+ const db = this.client.db;
223
+ console.log('');
224
+ console.log('🔧 Executing SQL:');
225
+ console.log(sqlQuery);
226
+ console.log('');
227
+ const results = await db.all(sqlQuery);
228
+ console.log(`📊 Results (${results.length} rows):`);
229
+ if (results.length > 0) {
230
+ results.forEach((row, index) => {
231
+ console.log(`[${index}]`, JSON.stringify(row, null, 2));
232
+ });
233
+ }
234
+ else {
235
+ console.log('No rows returned.');
236
+ }
237
+ }
238
+ catch (error) {
239
+ console.error('SQL execution error:', error instanceof Error ? error.message : error);
240
+ }
241
+ }
242
+ showLastQuery() {
243
+ if (!this.lastSqlQuery) {
244
+ console.log('No previous query to show.');
245
+ return;
246
+ }
247
+ console.log('');
248
+ console.log('📝 Last Generated SQL:');
249
+ console.log('Query:', this.lastSqlQuery.sql);
250
+ console.log('Parameters:', JSON.stringify(this.lastSqlQuery.params));
251
+ if (this.lastFilter) {
252
+ console.log('Original Filter:', JSON.stringify(this.lastFilter, null, 2));
253
+ }
254
+ console.log('');
255
+ }
256
+ async showSample(count) {
257
+ if (!this.currentCollection) {
258
+ console.log('No collection selected. Use .use <collection> first.');
259
+ return;
260
+ }
261
+ try {
262
+ const collection = this.client.collection(this.currentCollection);
263
+ const results = await collection.find({}).limit(count).toArray();
264
+ console.log('');
265
+ console.log(`📋 Sample documents from "${this.currentCollection}" (${results.length} of ${count} requested):`);
266
+ if (results.length > 0) {
267
+ results.forEach((doc, index) => {
268
+ console.log(`[${index}]`, JSON.stringify(doc, null, 2));
269
+ });
270
+ }
271
+ else {
272
+ console.log('Collection is empty.');
273
+ }
274
+ console.log('');
275
+ }
276
+ catch (error) {
277
+ console.error('Error fetching sample:', error instanceof Error ? error.message : error);
278
+ }
279
+ }
280
+ async cleanup() {
281
+ console.log('\nClosing database connection...');
282
+ await this.client.close();
283
+ this.rl.close();
284
+ process.exit(0);
285
+ }
286
+ }
287
+ exports.QueryDebugger = QueryDebugger;
288
+ // Extended FindCursor that exposes SQL generation
289
+ class DebugFindCursor extends findCursor_js_1.FindCursor {
290
+ constructor(db, collectionName, filter, options = {}) {
291
+ super(db, collectionName, filter, options);
292
+ }
293
+ getSQL() {
294
+ // Access the private queryParts property
295
+ return this.queryParts;
296
+ }
297
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.MongoLite = exports.NodeSqliteAdapter = exports.BrowserSqliteAdapter = exports.CloudflareDurableObjectAdapter = exports.ChangeStream = exports.MongoLiteCollection = void 0;
18
+ const node_sqlite_js_1 = require("./adapters/node-sqlite.js");
19
+ const mongo_client_js_1 = require("./mongo-client.js");
20
+ var collection_js_1 = require("./collection.js");
21
+ Object.defineProperty(exports, "MongoLiteCollection", { enumerable: true, get: function () { return collection_js_1.MongoLiteCollection; } });
22
+ __exportStar(require("./types.js"), exports);
23
+ var changeStream_js_1 = require("./changeStream.js");
24
+ Object.defineProperty(exports, "ChangeStream", { enumerable: true, get: function () { return changeStream_js_1.ChangeStream; } });
25
+ var cloudflare_js_1 = require("./adapters/cloudflare.js");
26
+ Object.defineProperty(exports, "CloudflareDurableObjectAdapter", { enumerable: true, get: function () { return cloudflare_js_1.CloudflareDurableObjectAdapter; } });
27
+ var browser_js_1 = require("./adapters/browser.js");
28
+ Object.defineProperty(exports, "BrowserSqliteAdapter", { enumerable: true, get: function () { return browser_js_1.BrowserSqliteAdapter; } });
29
+ var node_sqlite_js_2 = require("./adapters/node-sqlite.js");
30
+ Object.defineProperty(exports, "NodeSqliteAdapter", { enumerable: true, get: function () { return node_sqlite_js_2.NodeSqliteAdapter; } });
31
+ /**
32
+ * MongoLite class is the main entry point for interacting with the SQLite-backed database.
33
+ *
34
+ * You can construct it with:
35
+ * - A file path string — uses the built-in `node:sqlite` adapter (`NodeSqliteAdapter`).
36
+ * Requires Node.js 22.5.0+; for older runtimes, import `MongoLite` from
37
+ * `@semics-tech/mongolite/better-sqlite3` instead.
38
+ * - A `MongoLiteClientOptions` object — uses the built-in adapter with options.
39
+ * - An `IDatabaseAdapter` instance — use a custom adapter such as
40
+ * `CloudflareDurableObjectAdapter` for Cloudflare Durable Objects.
41
+ */
42
+ class MongoLite extends mongo_client_js_1.MongoLite {
43
+ /**
44
+ * Creates a new MongoLite client instance.
45
+ * @param dbPathOrOptions Path to the SQLite database file, an options object,
46
+ * or an `IDatabaseAdapter` for custom backends.
47
+ */
48
+ constructor(dbPathOrOptions, options = {}) {
49
+ if (dbPathOrOptions &&
50
+ typeof dbPathOrOptions === 'object' &&
51
+ typeof dbPathOrOptions.connect === 'function' &&
52
+ typeof dbPathOrOptions.run === 'function' &&
53
+ typeof dbPathOrOptions.get === 'function' &&
54
+ typeof dbPathOrOptions.all === 'function' &&
55
+ typeof dbPathOrOptions.exec === 'function' &&
56
+ typeof dbPathOrOptions.close === 'function') {
57
+ // Custom adapter (e.g. CloudflareDurableObjectAdapter)
58
+ super(dbPathOrOptions, options);
59
+ }
60
+ else {
61
+ super(new node_sqlite_js_1.NodeSqliteAdapter(dbPathOrOptions), options);
62
+ }
63
+ }
64
+ }
65
+ exports.MongoLite = MongoLite;