@datacapy/migrate 0.1.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 (64) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +658 -0
  3. package/dist/cli/command-parser.d.ts +18 -0
  4. package/dist/cli/command-parser.d.ts.map +1 -0
  5. package/dist/cli/command-parser.js +159 -0
  6. package/dist/cli/command-parser.js.map +1 -0
  7. package/dist/cli/index.d.ts +3 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +22 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/cli/runner.d.ts +6 -0
  12. package/dist/cli/runner.d.ts.map +1 -0
  13. package/dist/cli/runner.js +141 -0
  14. package/dist/cli/runner.js.map +1 -0
  15. package/dist/executor/patch-executor.d.ts +13 -0
  16. package/dist/executor/patch-executor.d.ts.map +1 -0
  17. package/dist/executor/patch-executor.js +58 -0
  18. package/dist/executor/patch-executor.js.map +1 -0
  19. package/dist/index.d.ts +13 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +20 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/interface/context-resolver.d.ts +4 -0
  24. package/dist/interface/context-resolver.d.ts.map +1 -0
  25. package/dist/interface/context-resolver.js +3 -0
  26. package/dist/interface/context-resolver.js.map +1 -0
  27. package/dist/interface/database-patch.d.ts +8 -0
  28. package/dist/interface/database-patch.d.ts.map +1 -0
  29. package/dist/interface/database-patch.js +3 -0
  30. package/dist/interface/database-patch.js.map +1 -0
  31. package/dist/interface/index.d.ts +4 -0
  32. package/dist/interface/index.d.ts.map +1 -0
  33. package/dist/interface/index.js +20 -0
  34. package/dist/interface/index.js.map +1 -0
  35. package/dist/interface/migration-config.d.ts +26 -0
  36. package/dist/interface/migration-config.d.ts.map +1 -0
  37. package/dist/interface/migration-config.js +3 -0
  38. package/dist/interface/migration-config.js.map +1 -0
  39. package/dist/interface/migration-result.d.ts +31 -0
  40. package/dist/interface/migration-result.d.ts.map +1 -0
  41. package/dist/interface/migration-result.js +3 -0
  42. package/dist/interface/migration-result.js.map +1 -0
  43. package/dist/logger/migration-logger.d.ts +21 -0
  44. package/dist/logger/migration-logger.d.ts.map +1 -0
  45. package/dist/logger/migration-logger.js +78 -0
  46. package/dist/logger/migration-logger.js.map +1 -0
  47. package/dist/manager/migration-manager.d.ts +12 -0
  48. package/dist/manager/migration-manager.d.ts.map +1 -0
  49. package/dist/manager/migration-manager.js +262 -0
  50. package/dist/manager/migration-manager.js.map +1 -0
  51. package/dist/meta/meta-table.d.ts +22 -0
  52. package/dist/meta/meta-table.d.ts.map +1 -0
  53. package/dist/meta/meta-table.js +86 -0
  54. package/dist/meta/meta-table.js.map +1 -0
  55. package/dist/scanner/patch-scanner.d.ts +11 -0
  56. package/dist/scanner/patch-scanner.d.ts.map +1 -0
  57. package/dist/scanner/patch-scanner.js +166 -0
  58. package/dist/scanner/patch-scanner.js.map +1 -0
  59. package/dist/version/version-manager.d.ts +11 -0
  60. package/dist/version/version-manager.d.ts.map +1 -0
  61. package/dist/version/version-manager.js +90 -0
  62. package/dist/version/version-manager.js.map +1 -0
  63. package/package.json +51 -0
  64. package/tsconfig.json +27 -0
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2016, Kevin Foster
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,658 @@
1
+ # @datacapy/migrate
2
+
3
+ Database migration tool for @datacapy/om applications. Manage schema changes, data transformations, and database versioning across multiple datasources with transaction safety and rollback support.
4
+
5
+ ## Features
6
+
7
+ - **Multi-Datasource Support**: Migrate account-level databases, project-specific databases, or any custom datasource
8
+ - **Transaction Safety**: Each patch runs in a transaction with automatic rollback on failure
9
+ - **Version Tracking**: Track applied migrations in a metadata table
10
+ - **Dry Run Mode**: Preview changes without applying them
11
+ - **TypeScript First**: Full TypeScript support with type-safe patch interfaces
12
+ - **Flexible Organisation**: Organise patches by year/month with timestamped versions
13
+ - **Resume Capability**: Automatically resume from last successful patch
14
+ - **CLI Tool**: Simple command-line interface for running migrations
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pnpm add @datacapy/migrate
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### 1. Create a Migration Config
25
+
26
+ Create `migrate.config.js` in your project root:
27
+
28
+ ```javascript
29
+ const modelManager = require("./src/model-manager").default;
30
+
31
+ module.exports = async () => {
32
+ return {
33
+ modelManager, // Your existing ModelManager instance
34
+ patchDirectory: "./migrate",
35
+ };
36
+ };
37
+ ```
38
+
39
+ ### 2. Create Your First Patch
40
+
41
+ Create patches in `migrate/YYYY/MM/YYYY-MM-DD_HHMM_label.ts`:
42
+
43
+ ```typescript
44
+ // migrate/2024/02/2024-02-05_1430_add-users-table.ts
45
+ import { DatabasePatchInterface } from "@datacapy/migrate";
46
+ import { ModelManager } from "@datacapy/om";
47
+
48
+ export default class AddUsersTable implements DatabasePatchInterface {
49
+ version = "2024-02-05_1430";
50
+ description = "Add users table";
51
+ dataSourceName = "db"; // Target datasource
52
+
53
+ async update(modelManager: ModelManager): Promise<void> {
54
+ const dataSource = modelManager.getDataSource("db");
55
+
56
+ // Your migration logic here
57
+ await dataSource.insertOne("users", {
58
+ _id: "000000000000000000000001",
59
+ email: "admin@example.com",
60
+ role: "admin",
61
+ });
62
+ }
63
+ }
64
+ ```
65
+
66
+ ### 3. Run Migration
67
+
68
+ ```bash
69
+ # Migrate account-level database
70
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db
71
+
72
+ # Preview changes first
73
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db --dry-run
74
+
75
+ # Migrate with verbose output
76
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db --verbose
77
+ ```
78
+
79
+ ## CLI Reference
80
+
81
+ ### Command Syntax
82
+
83
+ ```bash
84
+ @datacapy/migrate [OPTIONS]
85
+ ```
86
+
87
+ ### Required Options
88
+
89
+ - `-c, --config <file>` - Path to migration config file
90
+ - `--datasource <name>` - Target datasource name (e.g., 'db', 'project')
91
+
92
+ ### Optional Options
93
+
94
+ - `--context <key=value>` - Context for dynamic datasources (can be specified multiple times)
95
+ - `-d, --patch-dir <dir>` - Patch directory (default: ./migrate)
96
+ - `-t, --target <version>` - Target version to migrate to (default: latest)
97
+ - `--dry-run` - Preview migration without making changes
98
+ - `-v, --verbose` - Enable verbose logging
99
+ - `-h, --help` - Show help message
100
+ - `--version` - Show package version
101
+
102
+ ### Examples
103
+
104
+ ```bash
105
+ # Migrate account-level database
106
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db
107
+
108
+ # Migrate specific project database (dynamic datasource)
109
+ pnpm @datacapy/migrate --config ./migrate.config.js \\
110
+ --datasource project \\
111
+ --context projectId=abc123
112
+
113
+ # Dry run to preview changes
114
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db --dry-run
115
+
116
+ # Migrate to specific version
117
+ pnpm @datacapy/migrate --config ./migrate.config.js \\
118
+ --datasource db \\
119
+ --target 2024-02-05_1430
120
+
121
+ # Verbose output
122
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db --verbose
123
+ ```
124
+
125
+ ## Configuration
126
+
127
+ ### Config File
128
+
129
+ The config file should export an async function that returns a `MigrationConfig` object:
130
+
131
+ ```typescript
132
+ import { ModelManager } from "@datacapy/om";
133
+
134
+ export default async () => {
135
+ return {
136
+ modelManager, // Required: Your ModelManager instance
137
+ patchDirectory: "./migrate", // Optional: Patch directory
138
+ verbose: false, // Optional: Enable verbose logging
139
+ };
140
+ };
141
+ ```
142
+
143
+ ### Configuration Options
144
+
145
+ - `modelManager`: **Required** - Existing ModelManager instance with configured datasources
146
+ - `dataSourceName`: **Required** - Provided via CLI `--datasource` argument
147
+ - `context`: **Optional** - Provided via CLI `--context` argument for dynamic datasources
148
+ - `patchDirectory`: **Optional** - Directory containing patches (default: './migrate')
149
+ - `targetVersion`: **Optional** - Target version to migrate to (default: latest)
150
+ - `metaTableName`: **Optional** - Name of meta table (default: 'migrationMeta')
151
+ - `dryRun`: **Optional** - Preview mode (default: false)
152
+ - `verbose`: **Optional** - Verbose logging (default: false)
153
+ - `stopOnError`: **Optional** - Stop on first error (default: true)
154
+ - `logger`: **Optional** - Custom logger function
155
+
156
+ ## Writing Patches
157
+
158
+ ### Patch Interface
159
+
160
+ All patches must implement `DatabasePatchInterface`:
161
+
162
+ ```typescript
163
+ export interface DatabasePatchInterface {
164
+ version: string; // Format: YYYY-MM-DD_HHMM
165
+ description: string; // Human-readable description
166
+ dataSourceName: string; // Target datasource: 'db', 'project', etc.
167
+ update(modelManager: ModelManager): Promise<void>;
168
+ }
169
+ ```
170
+
171
+ ### Patch File Structure
172
+
173
+ Patches must be organised in a timestamped directory structure:
174
+
175
+ ```
176
+ migrate/
177
+ ├── 2024/
178
+ │ ├── 01/
179
+ │ │ ├── 2024-01-15_1200_add-users-table.ts
180
+ │ │ └── 2024-01-20_1430_add-user-indexes.ts
181
+ │ └── 02/
182
+ │ ├── 2024-02-05_1430_add-validation.ts
183
+ │ └── 2024-02-10_0900_seed-data.ts
184
+ ```
185
+
186
+ ### Example Patches
187
+
188
+ #### Simple Table Creation
189
+
190
+ ```typescript
191
+ import { DatabasePatchInterface } from "@datacapy/migrate";
192
+ import { ModelManager } from "@datacapy/om";
193
+
194
+ export default class AddUsersTable implements DatabasePatchInterface {
195
+ version = "2024-02-05_1430";
196
+ description = "Add users table";
197
+ dataSourceName = "db";
198
+
199
+ async update(modelManager: ModelManager): Promise<void> {
200
+ const db = modelManager.getDataSource("db");
201
+
202
+ // Insert initial record to create table
203
+ await db.insertOne("users", {
204
+ _id: "000000000000000000000001",
205
+ email: "admin@example.com",
206
+ role: "admin",
207
+ createdAt: new Date(),
208
+ });
209
+ }
210
+ }
211
+ ```
212
+
213
+ #### Adding Indexes
214
+
215
+ ```typescript
216
+ export default class AddUserIndexes implements DatabasePatchInterface {
217
+ version = "2024-02-06_1000";
218
+ description = "Add indexes to users table";
219
+ dataSourceName = "db";
220
+
221
+ async update(modelManager: ModelManager): Promise<void> {
222
+ const db = modelManager.getDataSource("db");
223
+
224
+ // Create unique index on email
225
+ await db.createIndex(
226
+ "users",
227
+ { email: 1 },
228
+ {
229
+ name: "idx_users_email",
230
+ unique: true,
231
+ },
232
+ );
233
+
234
+ // Create index on createdAt
235
+ await db.createIndex(
236
+ "users",
237
+ { createdAt: -1 },
238
+ {
239
+ name: "idx_users_created",
240
+ },
241
+ );
242
+ }
243
+ }
244
+ ```
245
+
246
+ #### Using Repositories
247
+
248
+ ```typescript
249
+ export default class SeedDefaultRoles implements DatabasePatchInterface {
250
+ version = "2024-02-07_1400";
251
+ description = "Seed default user roles";
252
+ dataSourceName = "db";
253
+
254
+ async update(modelManager: ModelManager): Promise<void> {
255
+ // Access repo through ModelManager
256
+ const roleRepo = modelManager.getRepo("role");
257
+
258
+ // Check if already seeded
259
+ const count = await roleRepo.count();
260
+ if (count > 0) {
261
+ console.log("Roles already exist, skipping seed");
262
+ return;
263
+ }
264
+
265
+ // Insert default roles
266
+ await roleRepo.insertMany([
267
+ { name: "admin", permissions: ["*"] },
268
+ { name: "editor", permissions: ["read", "write"] },
269
+ { name: "viewer", permissions: ["read"] },
270
+ ]);
271
+ }
272
+ }
273
+ ```
274
+
275
+ #### Project-Specific Migration
276
+
277
+ ```typescript
278
+ export default class AddProjectStatus implements DatabasePatchInterface {
279
+ version = "2024-02-08_1000";
280
+ description = "Add status field to surveys";
281
+ dataSourceName = "project"; // Targets dynamic project datasource
282
+
283
+ async update(modelManager: ModelManager): Promise<void> {
284
+ // Get project datasource (resolved via CLI --context)
285
+ const projectDS = modelManager.getDataSource("project");
286
+
287
+ // Update all surveys in this project
288
+ await projectDS.updateMany(
289
+ "surveys",
290
+ {},
291
+ {
292
+ $set: { status: "draft" },
293
+ },
294
+ );
295
+ }
296
+ }
297
+ ```
298
+
299
+ ## Multi-Datasource Migrations
300
+
301
+ @datacapy/migrate supports migrating multiple datasources independently:
302
+
303
+ ### Account-Level Migrations
304
+
305
+ Migrate the main account database:
306
+
307
+ ```bash
308
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db
309
+ ```
310
+
311
+ ### Project-Level Migrations
312
+
313
+ Migrate a specific project's database:
314
+
315
+ ```bash
316
+ pnpm @datacapy/migrate --config ./migrate.config.js \\
317
+ --datasource project \\
318
+ --context projectId=abc123
319
+ ```
320
+
321
+ ### Migrating Multiple Projects
322
+
323
+ ```bash
324
+ # Get all project IDs, then migrate each
325
+ for projectId in $(get-project-ids); do
326
+ echo "Migrating project $projectId..."
327
+ pnpm @datacapy/migrate --config ./migrate.config.js \\
328
+ --datasource project \\
329
+ --context projectId=$projectId
330
+ done
331
+ ```
332
+
333
+ ## Version Format
334
+
335
+ Versions use the format `YYYY-MM-DD_HHMM`:
336
+
337
+ - **YYYY**: 4-digit year
338
+ - **MM**: 2-digit month (01-12)
339
+ - **DD**: 2-digit day (01-31)
340
+ - **HHMM**: 4-digit time (0000-2359)
341
+
342
+ Examples:
343
+
344
+ - `2024-02-05_1430` - February 5, 2024 at 2:30 PM
345
+ - `2024-12-31_2359` - December 31, 2024 at 11:59 PM
346
+
347
+ **Benefits:**
348
+
349
+ - Natural chronological ordering
350
+ - Easy to generate: `const version = new Date().toISOString().slice(0, 16).replace('T', '_').replace(':', '')`
351
+ - Eliminates merge conflicts (timestamps are unique)
352
+ - Human-readable
353
+
354
+ ## Transaction Safety
355
+
356
+ ### Automatic Transactions
357
+
358
+ Each patch runs in a transaction with automatic rollback on failure:
359
+
360
+ ```typescript
361
+ // This patch will rollback if any operation fails
362
+ export default class SafeMigration implements DatabasePatchInterface {
363
+ version = "2024-02-09_1000";
364
+ description = "Safe migration with automatic rollback";
365
+ dataSourceName = "db";
366
+
367
+ async update(modelManager: ModelManager): Promise<void> {
368
+ const db = modelManager.getDataSource("db");
369
+
370
+ // All these operations are in a transaction
371
+ await db.insertOne("users", { email: "user1@example.com" });
372
+ await db.insertOne("users", { email: "user2@example.com" });
373
+
374
+ // If this fails, both inserts are rolled back
375
+ await db.createIndex("users", { email: 1 }, { unique: true });
376
+ }
377
+ }
378
+ ```
379
+
380
+ ### Resume Capability
381
+
382
+ If a migration fails:
383
+
384
+ 1. All changes from the failed patch are rolled back
385
+ 2. Successfully applied patches remain applied
386
+ 3. Running the migration again resumes from where it stopped
387
+
388
+ ```bash
389
+ # First run - patches 1 and 2 succeed, patch 3 fails
390
+ $ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db
391
+ # ✓ Patch 1 applied
392
+ # ✓ Patch 2 applied
393
+ # ✗ Patch 3 failed - rolled back
394
+
395
+ # Fix the issue in patch 3 and run again
396
+ $ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db
397
+ # Skipping patch 1 (already applied)
398
+ # Skipping patch 2 (already applied)
399
+ # ✓ Patch 3 applied
400
+ ```
401
+
402
+ ## Metadata Tracking
403
+
404
+ Migration status is tracked in a `migrationMeta` table (customisable via `metaTableName`):
405
+
406
+ ```javascript
407
+ {
408
+ version: '2024-02-05_1430',
409
+ description: 'Add users table',
410
+ appliedAt: new Date('2024-02-05T14:30:00Z'),
411
+ duration: 150 // milliseconds
412
+ }
413
+ ```
414
+
415
+ ### Querying Migration Status
416
+
417
+ ```typescript
418
+ import { MetaTable } from "@datacapy/migrate";
419
+
420
+ // Get current database version
421
+ const metaTable = new MetaTable(dataSource, "migrationMeta");
422
+ const currentVersion = await metaTable.getCurrentVersion();
423
+ console.log(`Current version: ${currentVersion}`);
424
+
425
+ // Get all applied patches
426
+ const patches = await metaTable.getAppliedPatches();
427
+ patches.forEach((p) => {
428
+ console.log(`${p.version}: ${p.description} (applied ${p.appliedAt})`);
429
+ });
430
+
431
+ // Check if specific patch was applied
432
+ const isApplied = await metaTable.isPatchApplied("2024-02-05_1430");
433
+ ```
434
+
435
+ ## Programmatic Usage
436
+
437
+ You can also use @datacapy/migrate programmatically:
438
+
439
+ ```typescript
440
+ import { MigrationManager } from "@datacapy/migrate";
441
+ import modelManager from "./src/model-manager";
442
+
443
+ const config = {
444
+ modelManager,
445
+ dataSourceName: "db",
446
+ patchDirectory: "./migrate",
447
+ verbose: true,
448
+ };
449
+
450
+ const manager = new MigrationManager(config);
451
+ const result = await manager.migrate();
452
+
453
+ console.log(`Applied ${result.successCount} patches`);
454
+ console.log(`Current version: ${result.currentVersion}`);
455
+
456
+ if (result.failedCount > 0) {
457
+ console.error("Migration failed!");
458
+ result.patchResults.forEach((r) => {
459
+ if (r.status === "failed") {
460
+ console.error(`${r.version}: ${r.error?.message}`);
461
+ }
462
+ });
463
+ }
464
+ ```
465
+
466
+ ## Best Practices
467
+
468
+ ### 1. Always Use Dry Run First
469
+
470
+ ```bash
471
+ pnpm @datacapy/migrate --config ./migrate.config.js --datasource db --dry-run
472
+ ```
473
+
474
+ ### 2. Commit Patches to Version Control
475
+
476
+ Patches should be committed to Git for team coordination and deployment automation.
477
+
478
+ ### 3. Keep Patches Atomic
479
+
480
+ Each patch should do one thing and be reversible if needed:
481
+
482
+ ```typescript
483
+ // Good - Single, clear purpose
484
+ export default class AddUserEmailIndex implements DatabasePatchInterface {
485
+ version = "2024-02-10_1000";
486
+ description = "Add index on users.email for faster lookups";
487
+ dataSourceName = "db";
488
+ // ...
489
+ }
490
+
491
+ // Avoid - Multiple unrelated changes
492
+ export default class MiscChanges implements DatabasePatchInterface {
493
+ version = "2024-02-10_1100";
494
+ description = "Add indexes, update roles, and seed data";
495
+ dataSourceName = "db";
496
+ // Too much in one patch!
497
+ }
498
+ ```
499
+
500
+ ### 4. Test Patches Locally First
501
+
502
+ Use DataSourceMock to test patches:
503
+
504
+ ```typescript
505
+ import { DataSourceMock } from "@datacapy/om";
506
+ import AddUsersTable from "./2024-02-05_1430_add-users-table";
507
+
508
+ describe("AddUsersTable patch", () => {
509
+ it("should create users table", async () => {
510
+ const mockDS = new DataSourceMock({});
511
+ const mockMM = new ModelManager({});
512
+ mockMM.addDataSource("db", mockDS);
513
+
514
+ const patch = new AddUsersTable();
515
+ await patch.update(mockMM);
516
+
517
+ // Verify patch worked
518
+ expect(mockDS.dataInsert).toHaveLength(1);
519
+ });
520
+ });
521
+ ```
522
+
523
+ ### 5. Use Descriptive Patch Names
524
+
525
+ ```
526
+ ✓ 2024-02-05_1430_add-users-table.ts
527
+ ✓ 2024-02-06_1000_add-user-email-index.ts
528
+ ✓ 2024-02-07_1400_seed-default-roles.ts
529
+
530
+ ✗ 2024-02-05_1430_patch1.ts
531
+ ✗ 2024-02-06_1000_update.ts
532
+ ✗ 2024-02-07_1400_fix.ts
533
+ ```
534
+
535
+ ### 6. Handle Idempotency
536
+
537
+ Patches should be safe to run multiple times:
538
+
539
+ ```typescript
540
+ export default class SeedData implements DatabasePatchInterface {
541
+ version = "2024-02-11_1000";
542
+ description = "Seed initial data";
543
+ dataSourceName = "db";
544
+
545
+ async update(modelManager: ModelManager): Promise<void> {
546
+ const repo = modelManager.getRepo("setting");
547
+
548
+ // Check if already seeded
549
+ const exists = await repo.findOne({ key: "app.initialized" });
550
+ if (exists) {
551
+ console.log("Already initialized, skipping");
552
+ return;
553
+ }
554
+
555
+ // Safe to seed
556
+ await repo.insertOne({ key: "app.initialized", value: true });
557
+ }
558
+ }
559
+ ```
560
+
561
+ ## Troubleshooting
562
+
563
+ ### Migration Fails with "Datasource not found"
564
+
565
+ **Problem:** Cannot find datasource 'project'
566
+
567
+ **Solution:** Dynamic datasources need context:
568
+
569
+ ```bash
570
+ pnpm @datacapy/migrate --config ./migrate.config.js \\
571
+ --datasource project \\
572
+ --context projectId=abc123
573
+ ```
574
+
575
+ ### Patch Throws "Duplicate key error"
576
+
577
+ **Problem:** Patch already applied, trying to re-apply
578
+
579
+ **Solution:** Check meta table, patch may have been applied in previous run:
580
+
581
+ ```typescript
582
+ const metaTable = new MetaTable(dataSource);
583
+ const isApplied = await metaTable.isPatchApplied("2024-02-05_1430");
584
+ ```
585
+
586
+ ### Invalid Version Format Error
587
+
588
+ **Problem:** Version doesn't match `YYYY-MM-DD_HHMM` format
589
+
590
+ **Solution:** Ensure version and filename match exactly:
591
+
592
+ ```typescript
593
+ // Filename: 2024-02-05_1430_add-table.ts
594
+ export default class AddTable implements DatabasePatchInterface {
595
+ version = "2024-02-05_1430"; // Must match filename
596
+ // ...
597
+ }
598
+ ```
599
+
600
+ ## API Reference
601
+
602
+ ### MigrationManager
603
+
604
+ ```typescript
605
+ class MigrationManager {
606
+ constructor(config: MigrationConfig);
607
+ migrate(): Promise<MigrationResult>;
608
+ }
609
+ ```
610
+
611
+ ### MetaTable
612
+
613
+ ```typescript
614
+ class MetaTable {
615
+ constructor(dataSource: DataSourceInterface, tableName?: string);
616
+ initialize(): Promise<void>;
617
+ getCurrentVersion(): Promise<string>;
618
+ recordPatch(
619
+ version: string,
620
+ description: string,
621
+ duration?: number,
622
+ ): Promise<void>;
623
+ getAppliedPatches(): Promise<MetaRecord[]>;
624
+ isPatchApplied(version: string): Promise<boolean>;
625
+ }
626
+ ```
627
+
628
+ ### VersionManager
629
+
630
+ ```typescript
631
+ class VersionManager {
632
+ static parseVersion(version: string): number;
633
+ static compareVersions(v1: string, v2: string): number;
634
+ static sortVersions(versions: string[]): string[];
635
+ static isValidVersion(version: string): boolean;
636
+ static generateVersion(date?: Date): string;
637
+ static getLatestVersion(versions: string[]): string | undefined;
638
+ }
639
+ ```
640
+
641
+ ## Documentation
642
+
643
+ - **[Architecture](./docs/architecture/index.md)** - Migration system internals and technical implementation
644
+ - **[Best Practices](./docs/best-practices/index.md)** - Guidelines for writing safe, maintainable migrations
645
+ - **[Advanced Usage](./docs/advanced-usage/index.md)** - Advanced patterns, troubleshooting, and deployment
646
+
647
+ ## Contributing
648
+
649
+ Contributions are welcome! Please open an issue or submit a pull request on GitHub.
650
+
651
+ ## License
652
+
653
+ BSD-3-Clause
654
+
655
+ ## Support
656
+
657
+ - Documentation: https://github.com/datacapy/datacapy
658
+ - Issues: https://github.com/datacapy/datacapy/issues
@@ -0,0 +1,18 @@
1
+ export interface CliArguments {
2
+ config?: string;
3
+ datasource?: string;
4
+ context: Record<string, string>;
5
+ contextLookup?: string;
6
+ patchDirectory?: string;
7
+ targetVersion?: string;
8
+ dryRun: boolean;
9
+ verbose: boolean;
10
+ help: boolean;
11
+ version: boolean;
12
+ }
13
+ export declare class CommandParser {
14
+ static parse(args: string[]): CliArguments;
15
+ static validate(args: CliArguments): void;
16
+ static getHelpText(): string;
17
+ }
18
+ //# sourceMappingURL=command-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-parser.d.ts","sourceRoot":"","sources":["../../src/cli/command-parser.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,aAAa;IAOxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY;IAqF1C,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAoBzC,MAAM,CAAC,WAAW,IAAI,MAAM;CAgF7B"}