dbdock 1.1.16 → 1.1.19
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 +236 -11
- package/dist/cli/commands/analyze.d.ts +1 -0
- package/dist/cli/commands/analyze.js +135 -0
- package/dist/cli/commands/analyze.js.map +1 -0
- package/dist/cli/commands/cross-migrate.d.ts +11 -0
- package/dist/cli/commands/cross-migrate.js +307 -0
- package/dist/cli/commands/cross-migrate.js.map +1 -0
- package/dist/cli/index.js +20 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/config.js +125 -5
- package/dist/cli/utils/config.js.map +1 -1
- package/dist/config/config.service.js +21 -3
- package/dist/config/config.service.js.map +1 -1
- package/dist/config/env-url.helper.d.ts +11 -0
- package/dist/config/env-url.helper.js +56 -0
- package/dist/config/env-url.helper.js.map +1 -0
- package/dist/config/secrets.validator.js +5 -1
- package/dist/config/secrets.validator.js.map +1 -1
- package/dist/migration/analyzers/mongodb.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/mongodb.analyzer.js +208 -0
- package/dist/migration/analyzers/mongodb.analyzer.js.map +1 -0
- package/dist/migration/analyzers/postgres.analyzer.d.ts +3 -0
- package/dist/migration/analyzers/postgres.analyzer.js +176 -0
- package/dist/migration/analyzers/postgres.analyzer.js.map +1 -0
- package/dist/migration/config.manager.d.ts +3 -0
- package/dist/migration/config.manager.js +80 -0
- package/dist/migration/config.manager.js.map +1 -0
- package/dist/migration/engines/migration.engine.d.ts +6 -0
- package/dist/migration/engines/migration.engine.js +62 -0
- package/dist/migration/engines/migration.engine.js.map +1 -0
- package/dist/migration/engines/mongo-to-postgres.engine.d.ts +2 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js +409 -0
- package/dist/migration/engines/mongo-to-postgres.engine.js.map +1 -0
- package/dist/migration/engines/postgres-to-mongo.engine.d.ts +2 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js +192 -0
- package/dist/migration/engines/postgres-to-mongo.engine.js.map +1 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.d.ts +2 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js +263 -0
- package/dist/migration/mappers/mongo-to-postgres.mapper.js.map +1 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.d.ts +2 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js +174 -0
- package/dist/migration/mappers/postgres-to-mongo.mapper.js.map +1 -0
- package/dist/migration/reference.detector.d.ts +2 -0
- package/dist/migration/reference.detector.js +57 -0
- package/dist/migration/reference.detector.js.map +1 -0
- package/dist/migration/type.mapper.d.ts +12 -0
- package/dist/migration/type.mapper.js +197 -0
- package/dist/migration/type.mapper.js.map +1 -0
- package/dist/migration/types.d.ts +183 -0
- package/dist/migration/types.js +11 -0
- package/dist/migration/types.js.map +1 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DBDock
|
|
2
2
|
|
|
3
|
-
Stop writing backup scripts. Stop losing sleep over database migrations. DBDock handles PostgreSQL backups, restores, and database
|
|
3
|
+
Stop writing backup scripts. Stop losing sleep over database migrations. DBDock handles PostgreSQL backups, restores, database copies, and cross-database migrations between MongoDB and PostgreSQL in one command.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/dbdock)
|
|
6
6
|
[](LICENSE)
|
|
@@ -20,13 +20,14 @@ It's not hard. It's just repetitive. And repetitive stuff should be one command.
|
|
|
20
20
|
## The Fix
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npx dbdock init
|
|
24
|
-
npx dbdock backup
|
|
25
|
-
npx dbdock restore
|
|
26
|
-
npx dbdock copydb "db_url_1" "db_url_2"
|
|
23
|
+
npx dbdock init # One-time setup (takes 30 seconds)
|
|
24
|
+
npx dbdock backup # Backup with encryption + compression
|
|
25
|
+
npx dbdock restore # Restore from any backup
|
|
26
|
+
npx dbdock copydb "db_url_1" "db_url_2" # Copy entire database, zero config
|
|
27
|
+
npx dbdock migrate "mongo_url" "postgres_url" # Cross-database migration
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
That's it. No shell scripts. No manual uploads. No
|
|
30
|
+
That's it. No shell scripts. No manual uploads. No throwaway migration code.
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
@@ -72,6 +73,8 @@ It asks for your database connection, picks your storage (Local, S3, R2, Cloudin
|
|
|
72
73
|
- Config (safe stuff) goes to `dbdock.config.json` — commit this
|
|
73
74
|
- Secrets go to `.env` — never committed, `.gitignore` updated automatically
|
|
74
75
|
|
|
76
|
+
You can also run **without a config file**: set `DBDOCK_DB_URL` (or `DATABASE_URL`) and other env vars and DBDock will use env-only configuration.
|
|
77
|
+
|
|
75
78
|
---
|
|
76
79
|
|
|
77
80
|
### `dbdock backup` — One command, full backup
|
|
@@ -258,6 +261,221 @@ Got secrets sitting in `dbdock.config.json` from an older version? This extracts
|
|
|
258
261
|
|
|
259
262
|
---
|
|
260
263
|
|
|
264
|
+
## Cross-Database Migration (MongoDB ↔ PostgreSQL)
|
|
265
|
+
|
|
266
|
+
Move your data between MongoDB and PostgreSQL without writing throwaway scripts. DBDock analyzes the source, proposes a schema mapping, lets you review it, and handles the transfer.
|
|
267
|
+
|
|
268
|
+
### `dbdock analyze` — Understand your database first
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npx dbdock analyze "mongodb://localhost:27017/myapp"
|
|
272
|
+
npx dbdock analyze "postgresql://user:pass@localhost:5432/myapp"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Scans the source database and shows you everything — collections/tables, field types, nested structures, inconsistencies (like a `price` field that's a string in 200 docs and a number in 15,000), and missing fields.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
DBDock - Database Analysis
|
|
279
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
280
|
+
|
|
281
|
+
ℹ Database: MongoDB — myapp
|
|
282
|
+
ℹ Host: localhost:27017
|
|
283
|
+
|
|
284
|
+
✓ Analysis complete
|
|
285
|
+
|
|
286
|
+
Found 4 collections, 57.2K total documents
|
|
287
|
+
|
|
288
|
+
users (45K docs)
|
|
289
|
+
├─ _id (objectId)
|
|
290
|
+
├─ name (string)
|
|
291
|
+
├─ email (string)
|
|
292
|
+
├─ address (object)
|
|
293
|
+
│ ├─ street (string)
|
|
294
|
+
│ ├─ city (string)
|
|
295
|
+
│ └─ zip (string)
|
|
296
|
+
├─ phone (string) [62% present]
|
|
297
|
+
└─ orders (array: object)
|
|
298
|
+
|
|
299
|
+
products (12K docs)
|
|
300
|
+
├─ _id (objectId)
|
|
301
|
+
├─ title (string)
|
|
302
|
+
├─ price (string(203), number(11797)) ⚠ mixed types
|
|
303
|
+
├─ tags (array: string)
|
|
304
|
+
└─ meta (object)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
### `dbdock migrate` — Cross-database migration
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
npx dbdock migrate "mongodb://localhost:27017/myapp" "postgresql://user:pass@localhost:5432/myapp"
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
DBDock analyzes the source, generates a schema mapping proposal, and presents it for review before touching anything:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
DBDock - Cross-Database Migration
|
|
319
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
320
|
+
|
|
321
|
+
ℹ Source: MongoDB — myapp
|
|
322
|
+
ℹ Target: PostgreSQL — myapp
|
|
323
|
+
|
|
324
|
+
✓ Source analyzed: 4 collections, 57.2K documents
|
|
325
|
+
|
|
326
|
+
Proposed Schema Mapping:
|
|
327
|
+
|
|
328
|
+
users → users
|
|
329
|
+
├─ _id → (uuid_from_objectid) users.id (uuid) PK
|
|
330
|
+
├─ name → users.name (text)
|
|
331
|
+
├─ email → users.email (text)
|
|
332
|
+
├─ address {} → user_addresses (1:1 relation)
|
|
333
|
+
│ ├─ street → street (text)
|
|
334
|
+
│ ├─ city → city (text)
|
|
335
|
+
│ └─ zip → zip (text)
|
|
336
|
+
├─ orders [] → user_orders child table
|
|
337
|
+
|
|
338
|
+
products → products
|
|
339
|
+
├─ _id → (uuid_from_objectid) products.id (uuid) PK
|
|
340
|
+
├─ title → products.title (text)
|
|
341
|
+
├─ price → products.price (numeric) nullable
|
|
342
|
+
├─ tags [] → product_tags text[]
|
|
343
|
+
├─ meta {} → products.meta (jsonb)
|
|
344
|
+
|
|
345
|
+
⚠ Conflicts Found:
|
|
346
|
+
|
|
347
|
+
• products.price: string in 203 docs, number in 11797 docs
|
|
348
|
+
→ Suggestion: cast to numeric, log failures
|
|
349
|
+
|
|
350
|
+
• users.phone: missing in 38.0% of documents
|
|
351
|
+
→ Suggestion: nullable column
|
|
352
|
+
|
|
353
|
+
? Accept mapping? (Y / export / cancel)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
You review the mapping, then choose: execute it, export it as a config file to tweak, or cancel.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
### What it handles
|
|
361
|
+
|
|
362
|
+
| Scenario | How DBDock handles it |
|
|
363
|
+
|---|---|
|
|
364
|
+
| **Nested objects** | Consistent shape → flattened to a related table. Varying/messy → kept as `jsonb` column |
|
|
365
|
+
| **Arrays of primitives** | `tags: ["a", "b"]` → PostgreSQL array column or junction table |
|
|
366
|
+
| **Arrays of objects** | `orders: [{item, qty}]` → child table with foreign key back to parent |
|
|
367
|
+
| **Missing fields** | Detects frequency across all documents, makes sparse fields nullable |
|
|
368
|
+
| **Type mismatches** | Same field with different types → casts to majority type, logs failures to `_migration_errors` |
|
|
369
|
+
| **ObjectId references** | Auto-detects `userId: ObjectId(...)` patterns → creates proper foreign keys |
|
|
370
|
+
| **Deep nesting** | Flattens up to configurable depth (default: 2), stores deeper levels as `jsonb` |
|
|
371
|
+
| **Postgres → Mongo** | 1:1 joins → embedded objects. Small 1:many → embedded arrays. Large 1:many → separate collections with refs |
|
|
372
|
+
| **Many-to-many** | Junction tables → arrays of values in the document |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### The reverse — PostgreSQL to MongoDB
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
npx dbdock migrate "postgresql://user:pass@localhost:5432/myapp" "mongodb://localhost:27017/myapp"
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
DBDock detects table relationships and proposes embedding vs referencing strategies:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
Proposed Document Mapping:
|
|
386
|
+
|
|
387
|
+
users → users collection
|
|
388
|
+
├─ id → _id
|
|
389
|
+
├─ name → name
|
|
390
|
+
├─ email → email
|
|
391
|
+
├─ addresses → embed object as address
|
|
392
|
+
├─ orders → embed array as orders (< 1000 rows)
|
|
393
|
+
|
|
394
|
+
products → products collection
|
|
395
|
+
├─ id → _id
|
|
396
|
+
├─ title → title
|
|
397
|
+
├─ price → price
|
|
398
|
+
├─ product_tags → embed as tags array
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Small 1:1 and 1:many relations get embedded. Large 1:many relations stay as separate collections with reference fields. You can override per table.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
### Dry run & validation
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
npx dbdock migrate --dry-run "mongodb://..." "postgresql://..."
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Runs the full migration into a temporary schema, validates counts and referential integrity, then cleans up. Nothing touches the real target:
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
Dry Run Results:
|
|
415
|
+
─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
416
|
+
✔ users: 45,000 → 45,000
|
|
417
|
+
✔ addresses: 45,000 → 44,998 (2 failed)
|
|
418
|
+
✔ orders: 312,400 → 312,400
|
|
419
|
+
✔ products: 12,000 → 12,000
|
|
420
|
+
✔ product_tags: 38,200 → 38,200
|
|
421
|
+
|
|
422
|
+
⚠ 2 rows failed (see _migration_errors)
|
|
423
|
+
✓ All foreign keys valid — dry run passed
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
### Incremental / delta migration
|
|
429
|
+
|
|
430
|
+
Already migrated once but your app is still running on the old database? Sync only new or changed data:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npx dbdock migrate --incremental --since "2026-03-10" "mongodb://..." "postgresql://..."
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Uses `createdAt`/`updatedAt` timestamps to only transfer new and changed documents. Existing rows in the target are upserted.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
### Save & reuse config
|
|
441
|
+
|
|
442
|
+
Export the mapping for your team to use reproducibly:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npx dbdock migrate --export-config ./migration.yaml "mongodb://..." "postgresql://..."
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Then anyone runs the same migration:
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
npx dbdock migrate --config ./migration.yaml "mongodb://..." "postgresql://..."
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Tweak the YAML/JSON config to adjust type mappings, embedding strategies, or rename target tables — then re-run.
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
### Migration options
|
|
459
|
+
|
|
460
|
+
| Flag | What it does |
|
|
461
|
+
|---|---|
|
|
462
|
+
| `--dry-run` | Migrate into temp schema, validate, clean up |
|
|
463
|
+
| `--incremental` | Only sync new/changed data |
|
|
464
|
+
| `--since <date>` | Cutoff date for incremental mode (ISO format) |
|
|
465
|
+
| `--config <path>` | Load a saved migration config (YAML or JSON) |
|
|
466
|
+
| `--export-config <path>` | Save migration plan to file without executing |
|
|
467
|
+
| `--batch-size <n>` | Documents per batch (default: 1000) |
|
|
468
|
+
| `--max-depth <n>` | Max nesting depth before storing as jsonb (default: 2) |
|
|
469
|
+
|
|
470
|
+
### Core principles
|
|
471
|
+
|
|
472
|
+
- **Never lose data** — failed rows go to `_migration_errors`, never silently dropped
|
|
473
|
+
- **Never surprise the user** — full mapping shown before execution, conflicts flagged
|
|
474
|
+
- **Always let you review** — nothing executes without explicit confirmation
|
|
475
|
+
- **Reproducible** — export/import configs for team-wide consistency
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
261
479
|
## Storage Providers
|
|
262
480
|
|
|
263
481
|
Pick your storage during `dbdock init`, or set it in `dbdock.config.json`:
|
|
@@ -327,13 +545,20 @@ DBDock splits your config into two parts by design:
|
|
|
327
545
|
|
|
328
546
|
| What | Where | Git safe? |
|
|
329
547
|
|------|-------|-----------|
|
|
330
|
-
| Host, port, bucket names, settings | `dbdock.config.json` | Yes |
|
|
331
|
-
| Passwords, API keys, secrets | `.env` | No (auto-gitignored) |
|
|
548
|
+
| Host, port, bucket names, settings | `dbdock.config.json` or env | Yes (if in config) |
|
|
549
|
+
| Passwords, API keys, secrets | `.env` or `DBDOCK_DB_URL` | No (auto-gitignored) |
|
|
332
550
|
|
|
333
551
|
### Environment Variables
|
|
334
552
|
|
|
553
|
+
Use either a **full database URL** or **separate credentials**:
|
|
554
|
+
|
|
335
555
|
```bash
|
|
336
|
-
|
|
556
|
+
# Option 1: Full URL (env-only config, no password var needed)
|
|
557
|
+
DBDOCK_DB_URL=postgresql://user:password@host:5432/database
|
|
558
|
+
# or DATABASE_URL=postgresql://user:password@host:5432/database
|
|
559
|
+
|
|
560
|
+
# Option 2: Separate credentials (with or without dbdock.config.json)
|
|
561
|
+
DBDOCK_DB_PASSWORD=your-database-password # Required if not using URL
|
|
337
562
|
DBDOCK_STORAGE_ACCESS_KEY=your-access-key # For cloud storage
|
|
338
563
|
DBDOCK_STORAGE_SECRET_KEY=your-secret-key # For cloud storage
|
|
339
564
|
DBDOCK_ENCRYPTION_SECRET=64-char-hex-string # If encryption enabled
|
|
@@ -342,7 +567,7 @@ DBDOCK_SMTP_PASS=your-app-password # For email alerts
|
|
|
342
567
|
DBDOCK_SLACK_WEBHOOK=https://hooks.slack.com/... # For Slack alerts
|
|
343
568
|
```
|
|
344
569
|
|
|
345
|
-
Reads from both `.env` and `.env.local` (`.env.local` takes priority).
|
|
570
|
+
When `DBDOCK_DB_URL` or `DATABASE_URL` is set, it overrides database settings from `dbdock.config.json`. You can run with **env-only** (no config file) by setting the URL plus storage/encryption/alert vars in `.env`. Reads from both `.env` and `.env.local` (`.env.local` takes priority).
|
|
346
571
|
|
|
347
572
|
### .pgpass Support
|
|
348
573
|
|
|
@@ -431,7 +656,7 @@ Don't just use the CLI — drop DBDock into your Node.js app and trigger backups
|
|
|
431
656
|
npm install dbdock
|
|
432
657
|
```
|
|
433
658
|
|
|
434
|
-
|
|
659
|
+
Use `dbdock.config.json` (run `npx dbdock init`) or configure entirely via env vars (`DBDOCK_DB_URL` / `DATABASE_URL` plus storage and other env vars).
|
|
435
660
|
|
|
436
661
|
### Create a Backup
|
|
437
662
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function analyzeCommand(url: string): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
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.analyzeCommand = analyzeCommand;
|
|
7
|
+
const ora_1 = __importDefault(require("ora"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const migration_engine_1 = require("../../migration/engines/migration.engine");
|
|
11
|
+
async function analyzeCommand(url) {
|
|
12
|
+
console.log('');
|
|
13
|
+
console.log(chalk_1.default.bold(' DBDock - Database Analysis'));
|
|
14
|
+
console.log(chalk_1.default.gray(' ─'.repeat(30)));
|
|
15
|
+
console.log('');
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = (0, migration_engine_1.parseDatabaseUrl)(url);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
logger_1.logger.error(error instanceof Error ? error.message : String(error));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
logger_1.logger.info(`Database: ${chalk_1.default.cyan(parsed.type === 'mongodb' ? 'MongoDB' : 'PostgreSQL')} — ${parsed.database}`);
|
|
25
|
+
logger_1.logger.info(`Host: ${parsed.host}:${parsed.port}`);
|
|
26
|
+
console.log('');
|
|
27
|
+
const spinner = (0, ora_1.default)('Analyzing database structure...').start();
|
|
28
|
+
try {
|
|
29
|
+
const analysis = await (0, migration_engine_1.analyzeDatabase)(url);
|
|
30
|
+
spinner.succeed('Analysis complete');
|
|
31
|
+
console.log('');
|
|
32
|
+
if (analysis.type === 'mongodb') {
|
|
33
|
+
displayMongoAnalysis(analysis);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
displayPostgresAnalysis(analysis);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
spinner.fail('Analysis failed');
|
|
41
|
+
logger_1.logger.error(error instanceof Error ? error.message : String(error));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function displayMongoAnalysis(analysis) {
|
|
46
|
+
console.log(chalk_1.default.bold(` Found ${chalk_1.default.cyan(String(analysis.collections.length))} collections, ${chalk_1.default.cyan(formatNumber(analysis.totalDocuments))} total documents`));
|
|
47
|
+
console.log('');
|
|
48
|
+
for (const collection of analysis.collections) {
|
|
49
|
+
console.log(chalk_1.default.bold(` ${chalk_1.default.green(collection.name)} ${chalk_1.default.gray(`(${formatNumber(collection.documentCount)} docs)`)}`));
|
|
50
|
+
for (let i = 0; i < collection.fields.length; i++) {
|
|
51
|
+
const field = collection.fields[i];
|
|
52
|
+
const isLast = i === collection.fields.length - 1;
|
|
53
|
+
const prefix = isLast ? ' └─' : ' ├─';
|
|
54
|
+
displayMongoField(field, prefix, isLast ? ' ' : ' │ ');
|
|
55
|
+
}
|
|
56
|
+
console.log('');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function displayMongoField(field, prefix, childPrefix) {
|
|
60
|
+
const typeEntries = Object.entries(field.types).filter(([t]) => t !== 'null' && t !== 'undefined');
|
|
61
|
+
const typeStr = typeEntries
|
|
62
|
+
.map(([t, c]) => {
|
|
63
|
+
if (typeEntries.length > 1)
|
|
64
|
+
return `${t}(${c})`;
|
|
65
|
+
return t;
|
|
66
|
+
})
|
|
67
|
+
.join(', ');
|
|
68
|
+
let extra = '';
|
|
69
|
+
if (field.frequency < 100) {
|
|
70
|
+
extra += chalk_1.default.yellow(` [${field.frequency.toFixed(0)}% present]`);
|
|
71
|
+
}
|
|
72
|
+
if (field.isArray) {
|
|
73
|
+
extra += chalk_1.default.blue(` [array: ${field.arrayElementType || 'mixed'}]`);
|
|
74
|
+
}
|
|
75
|
+
if (field.isObjectId && field.name !== '_id') {
|
|
76
|
+
extra += chalk_1.default.magenta(` → ref${field.possibleReference ? `: ${field.possibleReference}` : ''}`);
|
|
77
|
+
}
|
|
78
|
+
if (typeEntries.length > 1) {
|
|
79
|
+
extra += chalk_1.default.red(' ⚠ mixed types');
|
|
80
|
+
}
|
|
81
|
+
console.log(`${prefix} ${chalk_1.default.white(field.name)} ${chalk_1.default.gray(`(${typeStr})`)}${extra}`);
|
|
82
|
+
if (field.nestedFields && field.isNestedObject && !field.isArray) {
|
|
83
|
+
for (let i = 0; i < field.nestedFields.length; i++) {
|
|
84
|
+
const nested = field.nestedFields[i];
|
|
85
|
+
const isLast = i === field.nestedFields.length - 1;
|
|
86
|
+
const nPrefix = childPrefix + (isLast ? ' └─' : ' ├─');
|
|
87
|
+
const nChildPrefix = childPrefix + (isLast ? ' ' : ' │ ');
|
|
88
|
+
displayMongoField(nested, nPrefix, nChildPrefix);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function displayPostgresAnalysis(analysis) {
|
|
93
|
+
console.log(chalk_1.default.bold(` Found ${chalk_1.default.cyan(String(analysis.tables.length))} tables, ${chalk_1.default.cyan(formatNumber(analysis.totalRows))} total rows`));
|
|
94
|
+
console.log('');
|
|
95
|
+
for (const table of analysis.tables) {
|
|
96
|
+
console.log(chalk_1.default.bold(` ${chalk_1.default.green(table.name)} ${chalk_1.default.gray(`(${formatNumber(table.rowCount)} rows)`)}`));
|
|
97
|
+
for (let i = 0; i < table.columns.length; i++) {
|
|
98
|
+
const col = table.columns[i];
|
|
99
|
+
const isLast = i === table.columns.length - 1 && table.foreignKeys.length === 0;
|
|
100
|
+
const prefix = isLast ? ' └─' : ' ├─';
|
|
101
|
+
let extra = '';
|
|
102
|
+
if (col.isPrimaryKey)
|
|
103
|
+
extra += chalk_1.default.yellow(' PK');
|
|
104
|
+
if (col.isUnique && !col.isPrimaryKey)
|
|
105
|
+
extra += chalk_1.default.blue(' UNIQUE');
|
|
106
|
+
if (!col.isNullable)
|
|
107
|
+
extra += chalk_1.default.gray(' NOT NULL');
|
|
108
|
+
if (col.columnDefault)
|
|
109
|
+
extra += chalk_1.default.gray(` DEFAULT ${col.columnDefault}`);
|
|
110
|
+
const fk = table.foreignKeys.find((f) => f.columnName === col.name);
|
|
111
|
+
if (fk) {
|
|
112
|
+
extra += chalk_1.default.magenta(` → ${fk.referencedTable}.${fk.referencedColumn}`);
|
|
113
|
+
}
|
|
114
|
+
console.log(`${prefix} ${chalk_1.default.white(col.name)} ${chalk_1.default.gray(`(${col.dataType})`)}${extra}`);
|
|
115
|
+
}
|
|
116
|
+
if (table.indexes.length > 0) {
|
|
117
|
+
const nonPkIndexes = table.indexes.filter((idx) => !idx.isPrimary);
|
|
118
|
+
for (let i = 0; i < nonPkIndexes.length; i++) {
|
|
119
|
+
const idx = nonPkIndexes[i];
|
|
120
|
+
const isLast = i === nonPkIndexes.length - 1;
|
|
121
|
+
const prefix = isLast ? ' └─' : ' ├─';
|
|
122
|
+
console.log(`${prefix} ${chalk_1.default.gray('idx:')} ${idx.name} ${chalk_1.default.gray(`(${idx.columns.join(', ')})`)}${idx.isUnique ? chalk_1.default.blue(' UNIQUE') : ''}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
console.log('');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function formatNumber(n) {
|
|
129
|
+
if (n >= 1000000)
|
|
130
|
+
return `${(n / 1000000).toFixed(1)}M`;
|
|
131
|
+
if (n >= 1000)
|
|
132
|
+
return `${(n / 1000).toFixed(1)}K`;
|
|
133
|
+
return String(n);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../src/cli/commands/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface MigrateOptions {
|
|
2
|
+
dryRun?: boolean;
|
|
3
|
+
incremental?: boolean;
|
|
4
|
+
since?: string;
|
|
5
|
+
config?: string;
|
|
6
|
+
exportConfig?: string;
|
|
7
|
+
batchSize?: number;
|
|
8
|
+
maxDepth?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function crossMigrateCommand(sourceUrl: string, targetUrl: string, options: MigrateOptions): Promise<void>;
|
|
11
|
+
export {};
|