@semics-tech/mongolite 1.2.0 → 1.4.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 +91 -20
- package/dist/bin/mongolite-debug.d.ts +6 -1
- package/dist/bin/mongolite-debug.js +6 -113
- package/dist/bin/mongolite-debug.js.map +1 -1
- package/dist/bin/mongolite.d.ts +13 -0
- package/dist/bin/mongolite.js +172 -0
- package/dist/bin/mongolite.js.map +1 -0
- package/dist/cjs/bin/mongolite-debug.js +6 -149
- package/dist/cjs/bin/mongolite.js +207 -0
- package/dist/cjs/cli/discover.js +156 -0
- package/dist/cjs/cli/explorer.js +929 -0
- package/dist/cjs/cli/filter-builder.js +276 -0
- package/dist/cjs/cli/format.js +161 -0
- package/dist/cjs/cli/schema.js +211 -0
- package/dist/cjs/cli/ui.js +431 -0
- package/dist/cjs/collection.js +8 -1
- package/dist/cjs/cursors/findCursor.js +20 -8
- package/dist/cjs/index.js +108 -1
- package/dist/cjs/mongo-client.js +18 -1
- package/dist/cjs/server.js +164 -0
- package/dist/cjs/sync/commands.js +126 -0
- package/dist/cjs/sync/diff.js +174 -0
- package/dist/cjs/sync/httpSink.js +159 -0
- package/dist/cjs/sync/index.js +40 -0
- package/dist/cjs/sync/mongoExecutor.js +306 -0
- package/dist/cjs/sync/mongoSink.js +161 -0
- package/dist/cjs/sync/outbox.js +358 -0
- package/dist/cjs/sync/protocol.js +177 -0
- package/dist/cjs/sync/replicator.js +747 -0
- package/dist/cjs/sync/shadow.js +178 -0
- package/dist/cjs/sync/types.js +2 -0
- package/dist/cli/discover.d.ts +40 -0
- package/dist/cli/discover.js +146 -0
- package/dist/cli/discover.js.map +1 -0
- package/dist/cli/explorer.d.ts +59 -0
- package/dist/cli/explorer.js +923 -0
- package/dist/cli/explorer.js.map +1 -0
- package/dist/cli/filter-builder.d.ts +52 -0
- package/dist/cli/filter-builder.js +268 -0
- package/dist/cli/filter-builder.js.map +1 -0
- package/dist/cli/format.d.ts +29 -0
- package/dist/cli/format.js +153 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/schema.d.ts +58 -0
- package/dist/cli/schema.js +205 -0
- package/dist/cli/schema.js.map +1 -0
- package/dist/cli/ui.d.ts +71 -0
- package/dist/cli/ui.js +379 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/cloudflare.d.ts +1 -1
- package/dist/cloudflare.js.map +1 -1
- package/dist/collection.js +8 -1
- package/dist/collection.js.map +1 -1
- package/dist/cursors/findCursor.d.ts +12 -0
- package/dist/cursors/findCursor.js +20 -8
- package/dist/cursors/findCursor.js.map +1 -1
- package/dist/index.d.ts +68 -2
- package/dist/index.js +96 -1
- package/dist/index.js.map +1 -1
- package/dist/mongo-client.d.ts +13 -0
- package/dist/mongo-client.js +18 -1
- package/dist/mongo-client.js.map +1 -1
- package/dist/server.d.ts +97 -0
- package/dist/server.js +158 -0
- package/dist/server.js.map +1 -0
- package/dist/sync/commands.d.ts +49 -0
- package/dist/sync/commands.js +118 -0
- package/dist/sync/commands.js.map +1 -0
- package/dist/sync/diff.d.ts +55 -0
- package/dist/sync/diff.js +169 -0
- package/dist/sync/diff.js.map +1 -0
- package/dist/sync/httpSink.d.ts +93 -0
- package/dist/sync/httpSink.js +155 -0
- package/dist/sync/httpSink.js.map +1 -0
- package/dist/sync/index.d.ts +17 -0
- package/dist/sync/index.js +10 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/mongoExecutor.d.ts +37 -0
- package/dist/sync/mongoExecutor.js +302 -0
- package/dist/sync/mongoExecutor.js.map +1 -0
- package/dist/sync/mongoSink.d.ts +109 -0
- package/dist/sync/mongoSink.js +123 -0
- package/dist/sync/mongoSink.js.map +1 -0
- package/dist/sync/outbox.d.ts +120 -0
- package/dist/sync/outbox.js +355 -0
- package/dist/sync/outbox.js.map +1 -0
- package/dist/sync/protocol.d.ts +93 -0
- package/dist/sync/protocol.js +168 -0
- package/dist/sync/protocol.js.map +1 -0
- package/dist/sync/replicator.d.ts +177 -0
- package/dist/sync/replicator.js +744 -0
- package/dist/sync/replicator.js.map +1 -0
- package/dist/sync/shadow.d.ts +54 -0
- package/dist/sync/shadow.js +173 -0
- package/dist/sync/shadow.js.map +1 -0
- package/dist/sync/types.d.ts +388 -0
- package/dist/sync/types.js +2 -0
- package/dist/sync/types.js.map +1 -0
- package/package.json +16 -1
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@ A MongoDB-like client backed by SQLite. Use a familiar MongoDB API with the simp
|
|
|
24
24
|
- **Update operators** — `$set`, `$inc`, `$push`, `$pull`, `$addToSet`, `$mul`, and more
|
|
25
25
|
- **Indexing** — create, list, and drop indexes including unique and compound indexes
|
|
26
26
|
- **Change streams** — real-time change tracking via `collection.watch()`
|
|
27
|
+
- **MongoDB sync** — replicate local writes upstream, surviving restarts and offline periods
|
|
28
|
+
- **HTTP sync** — post the same changes to a remote API when MongoDB is unreachable
|
|
27
29
|
- **JSON safety** — validates documents before insert, and a corrupted row can't break queries for the rest of the collection
|
|
28
30
|
- **TypeScript** — fully typed with strict mode
|
|
29
31
|
|
|
@@ -69,29 +71,43 @@ async function main() {
|
|
|
69
71
|
main();
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
## Explore a database from the terminal
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx mongolite
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Picks up the SQLite files around you, lists the collections it finds inside,
|
|
81
|
+
and builds queries out of plain-English choices — "age is at least 30",
|
|
82
|
+
"skills contains SQL" — using fields inferred from your actual documents. It
|
|
83
|
+
prints the MongoDB filter it built alongside the results, and raw filters and
|
|
84
|
+
raw SQL are a menu item away when you want them. See [the CLI docs](./docs/CLI.md).
|
|
85
|
+
|
|
72
86
|
## How does it compare?
|
|
73
87
|
|
|
74
|
-
|
|
|
75
|
-
|
|
76
|
-
| MongoDB query API (`$set`, `$elemMatch`, aggregation...) | ✅
|
|
77
|
-
| Runs in the browser
|
|
78
|
-
| Runs on the edge (Cloudflare Durable Objects)
|
|
79
|
-
| Zero infrastructure (no server process)
|
|
80
|
-
| TypeScript, strict mode
|
|
81
|
-
| Actively maintained
|
|
88
|
+
| | MongoLite | lowdb | better-sqlite3 (raw) | NeDB | PouchDB | MongoDB |
|
|
89
|
+
| -------------------------------------------------------- | ----------- | ------------------------ | -------------------- | -------------------- | ------------------------ | ------- |
|
|
90
|
+
| MongoDB query API (`$set`, `$elemMatch`, aggregation...) | ✅ | ❌ (plain object access) | ❌ (raw SQL) | ✅ | ❌ (Mango/CouchDB-style) | ✅ |
|
|
91
|
+
| Runs in the browser | ✅ (sql.js) | ✅ | ❌ (native binding) | ✅ | ✅ (IndexedDB) | ❌ |
|
|
92
|
+
| Runs on the edge (Cloudflare Durable Objects) | ✅ | ❌ | ❌ (native binding) | ❌ | ❌ | ❌ |
|
|
93
|
+
| Zero infrastructure (no server process) | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
94
|
+
| TypeScript, strict mode | ✅ | ✅ | ✅ | ⚠️ (community types) | ⚠️ (community types) | ✅ |
|
|
95
|
+
| Actively maintained | ✅ | ✅ | ✅ | ❌ (unmaintained) | ⚠️ (slow-moving) | ✅ |
|
|
82
96
|
|
|
83
97
|
MongoLite's niche: the MongoDB query API you already know, running anywhere SQLite runs — a local file, an in-memory test database, the browser, or a Cloudflare Durable Object — without standing up a MongoDB server.
|
|
84
98
|
|
|
85
99
|
## Documentation
|
|
86
100
|
|
|
87
|
-
| Topic
|
|
88
|
-
|
|
89
|
-
| [API Reference](./docs/API.md)
|
|
90
|
-
| [Change Streams](./docs/CHANGE_STREAMS.md)
|
|
91
|
-
| [
|
|
92
|
-
| [
|
|
93
|
-
| [
|
|
94
|
-
| [
|
|
101
|
+
| Topic | Description |
|
|
102
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
103
|
+
| [API Reference](./docs/API.md) | Full API docs: methods, query operators, update operators |
|
|
104
|
+
| [Change Streams](./docs/CHANGE_STREAMS.md) | Real-time change tracking with `collection.watch()` |
|
|
105
|
+
| [Syncing to MongoDB](./docs/SYNC.md) | Replication to an upstream MongoDB, directly or through an HTTP API |
|
|
106
|
+
| [JSON Safety](./docs/JSON_SAFETY.md) | Document validation and corrupted data recovery |
|
|
107
|
+
| [CLI](./docs/CLI.md) | `npx mongolite` — guided explorer for browsing and querying a database |
|
|
108
|
+
| [Query Debugger](./docs/DEBUGGER.md) | The older command-driven REPL (`npx mongolite --repl`) |
|
|
109
|
+
| [Benchmarks](./docs/BENCHMARKS.md) | Performance benchmarks and storage characteristics |
|
|
110
|
+
| [Cloudflare Durable Objects](./docs/CLOUDFLARE.md) | Using MongoLite inside a Cloudflare Durable Object |
|
|
95
111
|
|
|
96
112
|
## Backend Examples
|
|
97
113
|
|
|
@@ -112,9 +128,9 @@ await client.close();
|
|
|
112
128
|
```typescript
|
|
113
129
|
const client = new MongoLite({
|
|
114
130
|
filePath: './myapp.sqlite',
|
|
115
|
-
WAL: true,
|
|
116
|
-
busyTimeout: 5000,
|
|
117
|
-
shared: true,
|
|
131
|
+
WAL: true, // Write-Ahead Logging. Default: true
|
|
132
|
+
busyTimeout: 5000, // ms to wait for a lock before SQLITE_BUSY. Default: 5000
|
|
133
|
+
shared: true, // reuse one connection per file in this process. Default: false
|
|
118
134
|
readOnly: false,
|
|
119
135
|
verbose: false,
|
|
120
136
|
});
|
|
@@ -136,7 +152,7 @@ await a.close(); // b keeps working — the handle is reference counted
|
|
|
136
152
|
await b.close(); // last holder out actually closes it
|
|
137
153
|
```
|
|
138
154
|
|
|
139
|
-
Connections are only shared when the resolved path
|
|
155
|
+
Connections are only shared when the resolved path _and_ every setting that
|
|
140
156
|
changes the handle's behaviour match. `:memory:` databases are never shared,
|
|
141
157
|
since two `:memory:` opens are two unrelated databases.
|
|
142
158
|
|
|
@@ -180,6 +196,7 @@ await client.close();
|
|
|
180
196
|
```
|
|
181
197
|
|
|
182
198
|
<a id="native-nodesqlite-vs-better-sqlite3"></a>
|
|
199
|
+
|
|
183
200
|
> **Which one should I use?** `node:sqlite` (the default) is still marked
|
|
184
201
|
> experimental upstream but requires no native build step, which is why it's
|
|
185
202
|
> the default here. `better-sqlite3` is the older, more battle-tested native
|
|
@@ -235,6 +252,60 @@ export class MyDurableObject extends DurableObject {
|
|
|
235
252
|
|
|
236
253
|
> See [docs/CLOUDFLARE.md](./docs/CLOUDFLARE.md) for the full guide, supported operations, and limitations.
|
|
237
254
|
|
|
255
|
+
## Syncing to MongoDB
|
|
256
|
+
|
|
257
|
+
Point MongoLite at an upstream MongoDB deployment and every insert, update and
|
|
258
|
+
delete is replicated to it. The local database becomes a local-first write layer
|
|
259
|
+
that converges upstream — through restarts, network outages and offline periods.
|
|
260
|
+
|
|
261
|
+
Requires the optional `mongodb` peer dependency (`npm install mongodb`).
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
const client = new MongoLite('./app.sqlite');
|
|
265
|
+
await client.connect();
|
|
266
|
+
|
|
267
|
+
const sync = client.syncToMongo({
|
|
268
|
+
connectionString: 'mongodb+srv://user:pass@cluster.example.com/app',
|
|
269
|
+
collections: ['users', 'orders'], // Omit to replicate everything
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await sync.start();
|
|
273
|
+
|
|
274
|
+
// Ordinary writes — replication happens in the background.
|
|
275
|
+
await client.collection('users').insertOne({ name: 'Alice', age: 30 });
|
|
276
|
+
|
|
277
|
+
await sync.stop({ flush: true }); // Push anything still queued on shutdown
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Changes are captured by SQLite triggers into a durable outbox **inside the same
|
|
281
|
+
transaction as the write itself**, so a change is either committed locally _and_
|
|
282
|
+
queued for replication, or neither. A background worker coalesces the queue, applies
|
|
283
|
+
it upstream, and only then advances a persisted checkpoint — so an outage parks the
|
|
284
|
+
backlog rather than losing it, and a restart resumes exactly where it stopped.
|
|
285
|
+
|
|
286
|
+
Upstream is treated as the source of truth. Each push is a conditional write against
|
|
287
|
+
the version it last saw, and carries only the fields that actually changed, so a
|
|
288
|
+
concurrent edit by another writer is detected rather than silently overwritten.
|
|
289
|
+
|
|
290
|
+
Where the local instance cannot reach MongoDB directly, `syncToHttp` posts the same
|
|
291
|
+
operations to a remote API instead, and `@semics-tech/mongolite/server` applies them on
|
|
292
|
+
the far side:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
const sync = client.syncToHttp({
|
|
296
|
+
baseUrl: 'https://api.example.com',
|
|
297
|
+
database: 'app',
|
|
298
|
+
getAuthHeaders: async () => ({ Authorization: `Bearer ${await getAccessToken()}` }),
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
The version predicates travel with each operation and conflicts come back in the
|
|
303
|
+
response, so the HTTP hop is pure transport — replication behaves the same either way.
|
|
304
|
+
|
|
305
|
+
> See [docs/SYNC.md](./docs/SYNC.md) for the full guide: syncing through an HTTP API,
|
|
306
|
+
> advanced authentication (X.509, mTLS, AWS IAM, Azure managed identity), filtering and
|
|
307
|
+
> reshaping documents, dead-letter handling, multiple upstreams, and custom sinks.
|
|
308
|
+
|
|
238
309
|
## Development
|
|
239
310
|
|
|
240
311
|
```bash
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `mongolite-debug` is kept as an alias of `mongolite` so existing scripts and
|
|
4
|
+
* muscle memory keep working. It runs the same guided explorer; pass `--repl`
|
|
5
|
+
* for the original command-driven debugger (`.use`, `.find`, `.sql`, …).
|
|
6
|
+
*/
|
|
7
|
+
import './mongolite.js';
|
|
@@ -1,115 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
for (let i = 0; i < args.length; i++) {
|
|
9
|
-
const arg = args[i];
|
|
10
|
-
if (arg === '--verbose' || arg === '-v') {
|
|
11
|
-
options.verbose = true;
|
|
12
|
-
}
|
|
13
|
-
else if (arg === '--collection' || arg === '-c') {
|
|
14
|
-
options.collection = args[++i];
|
|
15
|
-
}
|
|
16
|
-
else if (arg === '--database' || arg === '-d') {
|
|
17
|
-
options.database = args[++i];
|
|
18
|
-
}
|
|
19
|
-
else if (arg === '--help' || arg === '-h') {
|
|
20
|
-
options.help = true;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return options;
|
|
24
|
-
}
|
|
25
|
-
function showHelp() {
|
|
26
|
-
console.log('MongoLite Query Debugger');
|
|
27
|
-
console.log('');
|
|
28
|
-
console.log('Interactive debugging tool for MongoLite queries.');
|
|
29
|
-
console.log('');
|
|
30
|
-
console.log('Usage: npx mongolite-debug [options]');
|
|
31
|
-
console.log('');
|
|
32
|
-
console.log('Options:');
|
|
33
|
-
console.log(' -d, --database <path> Database file path (default: ./mongolite.db)');
|
|
34
|
-
console.log(' -c, --collection <name> Initial collection to use');
|
|
35
|
-
console.log(' -v, --verbose Enable verbose output');
|
|
36
|
-
console.log(' -h, --help Show this help');
|
|
37
|
-
console.log('');
|
|
38
|
-
console.log('Interactive Commands:');
|
|
39
|
-
console.log(' .help Show available commands');
|
|
40
|
-
console.log(' .collections List all collections');
|
|
41
|
-
console.log(' .use <collection> Select a collection to work with');
|
|
42
|
-
console.log(' .find <filter> Convert find filter to SQL and execute');
|
|
43
|
-
console.log(' .sql <query> Execute raw SQL query');
|
|
44
|
-
console.log(' .last Show last generated SQL');
|
|
45
|
-
console.log(' .sample [count] Show sample documents from current collection');
|
|
46
|
-
console.log(' .exit Exit the debugger');
|
|
47
|
-
console.log('');
|
|
48
|
-
console.log('Examples:');
|
|
49
|
-
console.log(' npx mongolite-debug -d ./myapp.db -c users');
|
|
50
|
-
console.log(' npx mongolite-debug --verbose');
|
|
51
|
-
console.log('');
|
|
52
|
-
console.log(' # Inside the debugger:');
|
|
53
|
-
console.log(' .use users');
|
|
54
|
-
console.log(' .find {"age": {"$gt": 25}}');
|
|
55
|
-
console.log(' .sql SELECT * FROM users WHERE json_extract(data, "$.age") > 25');
|
|
56
|
-
console.log('');
|
|
57
|
-
}
|
|
58
|
-
async function main() {
|
|
59
|
-
const options = parseArgs();
|
|
60
|
-
if (options.help) {
|
|
61
|
-
showHelp();
|
|
62
|
-
process.exit(0);
|
|
63
|
-
}
|
|
64
|
-
// Set default database path if not provided
|
|
65
|
-
if (!options.database) {
|
|
66
|
-
// Look for common database files in the current directory
|
|
67
|
-
const cwd = process.cwd();
|
|
68
|
-
const commonNames = [
|
|
69
|
-
'mongolite.db',
|
|
70
|
-
'mongolite.sqlite',
|
|
71
|
-
'database.db',
|
|
72
|
-
'database.sqlite',
|
|
73
|
-
'app.db',
|
|
74
|
-
'app.sqlite',
|
|
75
|
-
];
|
|
76
|
-
// Check if any common database files exist
|
|
77
|
-
const { existsSync } = await import('fs');
|
|
78
|
-
let foundDb = false;
|
|
79
|
-
for (const name of commonNames) {
|
|
80
|
-
const dbPath = path.join(cwd, name);
|
|
81
|
-
if (existsSync(dbPath)) {
|
|
82
|
-
options.database = dbPath;
|
|
83
|
-
foundDb = true;
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
if (!foundDb) {
|
|
88
|
-
options.database = path.join(cwd, 'mongolite.db');
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
console.log('🔍 MongoLite Query Debugger (npx version)');
|
|
92
|
-
console.log(`📁 Database: ${options.database}`);
|
|
93
|
-
if (options.collection) {
|
|
94
|
-
console.log(`📋 Initial collection: ${options.collection}`);
|
|
95
|
-
}
|
|
96
|
-
console.log('');
|
|
97
|
-
try {
|
|
98
|
-
const queryDebugger = new QueryDebugger(options);
|
|
99
|
-
await queryDebugger.start();
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
console.error('Fatal error:', error instanceof Error ? error.message : error);
|
|
103
|
-
if (error instanceof Error && error.message.includes('SQLITE_CANTOPEN')) {
|
|
104
|
-
console.error('');
|
|
105
|
-
console.error('💡 Tip: Make sure the database file exists or specify a different path with -d');
|
|
106
|
-
console.error(' Example: npx mongolite-debug -d ./path/to/your/database.db');
|
|
107
|
-
}
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
main().catch((error) => {
|
|
112
|
-
console.error('Unexpected error:', error);
|
|
113
|
-
process.exit(1);
|
|
114
|
-
});
|
|
2
|
+
/**
|
|
3
|
+
* `mongolite-debug` is kept as an alias of `mongolite` so existing scripts and
|
|
4
|
+
* muscle memory keep working. It runs the same guided explorer; pass `--repl`
|
|
5
|
+
* for the original command-driven debugger (`.use`, `.find`, `.sql`, …).
|
|
6
|
+
*/
|
|
7
|
+
import './mongolite.js';
|
|
115
8
|
//# sourceMappingURL=mongolite-debug.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongolite-debug.js","sourceRoot":"","sources":["../../src/bin/mongolite-debug.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"mongolite-debug.js","sourceRoot":"","sources":["../../src/bin/mongolite-debug.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface CliOptions {
|
|
3
|
+
database?: string;
|
|
4
|
+
collection?: string;
|
|
5
|
+
verbose: boolean;
|
|
6
|
+
help: boolean;
|
|
7
|
+
version: boolean;
|
|
8
|
+
repl: boolean;
|
|
9
|
+
sampleSize?: number;
|
|
10
|
+
pageSize?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function parseArgs(argv: string[]): CliOptions;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `mongolite` — the guided explorer for MongoLite databases.
|
|
4
|
+
*
|
|
5
|
+
* Running it with no arguments is the intended path: it finds your database
|
|
6
|
+
* files, lists the collections it finds inside, and walks you through a query.
|
|
7
|
+
* Flags are shortcuts for people who already know where they are going, and
|
|
8
|
+
* `--repl` drops into the older command-driven debugger.
|
|
9
|
+
*/
|
|
10
|
+
import { Explorer } from '../cli/explorer.js';
|
|
11
|
+
import { QueryDebugger } from '../debugger/query-debugger.js';
|
|
12
|
+
import { closePrompts, style } from '../cli/ui.js';
|
|
13
|
+
export function parseArgs(argv) {
|
|
14
|
+
const options = { verbose: false, help: false, version: false, repl: false };
|
|
15
|
+
const positional = [];
|
|
16
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
17
|
+
const arg = argv[index];
|
|
18
|
+
switch (arg) {
|
|
19
|
+
case '--database':
|
|
20
|
+
case '-d':
|
|
21
|
+
options.database = argv[++index];
|
|
22
|
+
break;
|
|
23
|
+
case '--collection':
|
|
24
|
+
case '-c':
|
|
25
|
+
options.collection = argv[++index];
|
|
26
|
+
break;
|
|
27
|
+
case '--verbose':
|
|
28
|
+
case '-v':
|
|
29
|
+
options.verbose = true;
|
|
30
|
+
break;
|
|
31
|
+
case '--repl':
|
|
32
|
+
case '--expert':
|
|
33
|
+
options.repl = true;
|
|
34
|
+
break;
|
|
35
|
+
case '--sample':
|
|
36
|
+
options.sampleSize = Number(argv[++index]);
|
|
37
|
+
break;
|
|
38
|
+
case '--page-size':
|
|
39
|
+
options.pageSize = Number(argv[++index]);
|
|
40
|
+
break;
|
|
41
|
+
case '--help':
|
|
42
|
+
case '-h':
|
|
43
|
+
options.help = true;
|
|
44
|
+
break;
|
|
45
|
+
case '--version':
|
|
46
|
+
case '-V':
|
|
47
|
+
options.version = true;
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
if (arg.startsWith('-')) {
|
|
51
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
52
|
+
}
|
|
53
|
+
positional.push(arg);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// `mongolite ./app.db users` is the shape people try first, so support it.
|
|
58
|
+
if (!options.database && positional[0])
|
|
59
|
+
options.database = positional[0];
|
|
60
|
+
if (!options.collection && positional[1])
|
|
61
|
+
options.collection = positional[1];
|
|
62
|
+
return options;
|
|
63
|
+
}
|
|
64
|
+
function showHelp() {
|
|
65
|
+
const lines = [
|
|
66
|
+
`${style.bold('mongolite')} — explore a MongoLite (SQLite-backed) database`,
|
|
67
|
+
'',
|
|
68
|
+
style.bold('Usage'),
|
|
69
|
+
' npx mongolite Start the guided explorer',
|
|
70
|
+
' npx mongolite ./app.db Open a specific database',
|
|
71
|
+
' npx mongolite ./app.db users Open a specific collection',
|
|
72
|
+
'',
|
|
73
|
+
style.bold('Options'),
|
|
74
|
+
' -d, --database <path> Database file to open (otherwise you get a list to pick from)',
|
|
75
|
+
' -c, --collection <name> Collection to start in',
|
|
76
|
+
' --sample <n> Documents to sample when inferring fields (default 200)',
|
|
77
|
+
' --page-size <n> Results shown per page (default 20)',
|
|
78
|
+
' --repl Use the older command-driven debugger instead',
|
|
79
|
+
' -v, --verbose Log the SQL being built',
|
|
80
|
+
' -h, --help Show this help',
|
|
81
|
+
' -V, --version Show the version',
|
|
82
|
+
'',
|
|
83
|
+
style.bold('In the explorer'),
|
|
84
|
+
' ↑/↓ move · type to filter the list · enter to choose · esc to go back · Ctrl+C to quit',
|
|
85
|
+
'',
|
|
86
|
+
'No MongoDB knowledge is needed: pick a field, pick a condition in plain English,',
|
|
87
|
+
'and the filter it builds is printed so you can learn the syntax as you go.',
|
|
88
|
+
'If you already know it, "Write a MongoDB filter yourself" and "Run raw SQL" are',
|
|
89
|
+
'under Advanced.',
|
|
90
|
+
'',
|
|
91
|
+
];
|
|
92
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Find the package version by walking up from the running script.
|
|
96
|
+
* Deliberately avoids `import.meta` and `__dirname` so the same source compiles
|
|
97
|
+
* for both the ESM and CommonJS builds.
|
|
98
|
+
*/
|
|
99
|
+
async function showVersion() {
|
|
100
|
+
const { readFileSync, existsSync } = await import('node:fs');
|
|
101
|
+
const { dirname, join, parse } = await import('node:path');
|
|
102
|
+
let dir = dirname(process.argv[1] ?? process.cwd());
|
|
103
|
+
const { root } = parse(dir);
|
|
104
|
+
while (dir && dir !== root) {
|
|
105
|
+
const candidate = join(dir, 'package.json');
|
|
106
|
+
if (existsSync(candidate)) {
|
|
107
|
+
try {
|
|
108
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
109
|
+
if (pkg.version) {
|
|
110
|
+
process.stdout.write(`${pkg.version}\n`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Unreadable package.json - keep walking up.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
dir = dirname(dir);
|
|
119
|
+
}
|
|
120
|
+
process.stdout.write('unknown\n');
|
|
121
|
+
}
|
|
122
|
+
async function main() {
|
|
123
|
+
let options;
|
|
124
|
+
try {
|
|
125
|
+
options = parseArgs(process.argv.slice(2));
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
129
|
+
process.stderr.write('Run with --help to see the available options.\n');
|
|
130
|
+
process.exit(1);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (options.help) {
|
|
134
|
+
showHelp();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (options.version) {
|
|
138
|
+
await showVersion();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (options.repl) {
|
|
142
|
+
const queryDebugger = new QueryDebugger({
|
|
143
|
+
database: options.database,
|
|
144
|
+
collection: options.collection,
|
|
145
|
+
verbose: options.verbose,
|
|
146
|
+
});
|
|
147
|
+
await queryDebugger.start();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const explorer = new Explorer({
|
|
151
|
+
database: options.database,
|
|
152
|
+
collection: options.collection,
|
|
153
|
+
verbose: options.verbose,
|
|
154
|
+
sampleSize: Number.isFinite(options.sampleSize) ? options.sampleSize : undefined,
|
|
155
|
+
pageSize: Number.isFinite(options.pageSize) ? options.pageSize : undefined,
|
|
156
|
+
});
|
|
157
|
+
await explorer.run();
|
|
158
|
+
}
|
|
159
|
+
main()
|
|
160
|
+
.then(() => {
|
|
161
|
+
closePrompts();
|
|
162
|
+
process.exit(0);
|
|
163
|
+
})
|
|
164
|
+
.catch((error) => {
|
|
165
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
166
|
+
process.stderr.write(`\n✖ ${text}\n`);
|
|
167
|
+
if (text.includes('SQLITE_CANTOPEN') || text.includes('unable to open')) {
|
|
168
|
+
process.stderr.write(' The file may not exist, or may not be readable.\n');
|
|
169
|
+
}
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
172
|
+
//# sourceMappingURL=mongolite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongolite.js","sourceRoot":"","sources":["../../src/bin/mongolite.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,152 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
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
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
});
|
|
4
|
+
/**
|
|
5
|
+
* `mongolite-debug` is kept as an alias of `mongolite` so existing scripts and
|
|
6
|
+
* muscle memory keep working. It runs the same guided explorer; pass `--repl`
|
|
7
|
+
* for the original command-driven debugger (`.use`, `.find`, `.sql`, …).
|
|
8
|
+
*/
|
|
9
|
+
require("./mongolite.js");
|