@ftschopp/dynatable-migrations 1.0.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/.gitkeep +0 -0
- package/CHANGELOG.md +13 -0
- package/README.md +421 -0
- package/USAGE.md +557 -0
- package/bin/dynatable-migrate.js +30 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +104 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +123 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/down.d.ts +3 -0
- package/dist/commands/down.d.ts.map +1 -0
- package/dist/commands/down.js +37 -0
- package/dist/commands/down.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +70 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +84 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/up.d.ts +3 -0
- package/dist/commands/up.d.ts.map +1 -0
- package/dist/commands/up.js +37 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/core/config.d.ts +29 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +160 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/loader.d.ts +47 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +226 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/runner.d.ts +38 -0
- package/dist/core/runner.d.ts.map +1 -0
- package/dist/core/runner.js +166 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/core/tracker.d.ts +19 -0
- package/dist/core/tracker.d.ts.map +1 -0
- package/dist/core/tracker.js +172 -0
- package/dist/core/tracker.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/migration.d.ts +5 -0
- package/dist/templates/migration.d.ts.map +1 -0
- package/dist/templates/migration.js +96 -0
- package/dist/templates/migration.js.map +1 -0
- package/dist/types/index.d.ts +159 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/migrations/.gitkeep +0 -0
- package/package.json +43 -0
- package/src/cli.ts +106 -0
- package/src/commands/create.ts +110 -0
- package/src/commands/down.ts +42 -0
- package/src/commands/init.ts +37 -0
- package/src/commands/status.ts +92 -0
- package/src/commands/up.ts +39 -0
- package/src/core/config.ts +140 -0
- package/src/core/loader.ts +233 -0
- package/src/core/runner.ts +223 -0
- package/src/core/tracker.ts +219 -0
- package/src/index.ts +23 -0
- package/src/templates/migration.ts +92 -0
- package/src/types/index.ts +196 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration template generator
|
|
3
|
+
*/
|
|
4
|
+
export function generateMigrationTemplate(version: string, name: string): string {
|
|
5
|
+
return `import { Migration } from "@ftschopp/dynatable-migrations";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Migration: ${name}
|
|
9
|
+
* Version: ${version}
|
|
10
|
+
*
|
|
11
|
+
* Description:
|
|
12
|
+
* Add description of what this migration does here.
|
|
13
|
+
*
|
|
14
|
+
* Version type:
|
|
15
|
+
* - MAJOR (X.0.0): Breaking changes, incompatible schema changes
|
|
16
|
+
* - MINOR (0.X.0): New features, backwards-compatible changes
|
|
17
|
+
* - PATCH (0.0.X): Bug fixes, data corrections
|
|
18
|
+
*/
|
|
19
|
+
export const migration: Migration = {
|
|
20
|
+
version: "${version}",
|
|
21
|
+
name: "${name}",
|
|
22
|
+
description: "Add description here",
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Schema snapshot (optional)
|
|
26
|
+
* Document your schema changes here for reference
|
|
27
|
+
*/
|
|
28
|
+
schema: {
|
|
29
|
+
// Example:
|
|
30
|
+
// User: {
|
|
31
|
+
// attributes: {
|
|
32
|
+
// username: { type: String, required: true },
|
|
33
|
+
// email: { type: String, required: false },
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Apply migration
|
|
40
|
+
*/
|
|
41
|
+
async up({ client, tableName, dynamodb }) {
|
|
42
|
+
console.log("Running migration: ${name}");
|
|
43
|
+
|
|
44
|
+
// Example: Add a new attribute to existing items
|
|
45
|
+
// const { ScanCommand, UpdateCommand } = dynamodb;
|
|
46
|
+
//
|
|
47
|
+
// const result = await client.send(new ScanCommand({
|
|
48
|
+
// TableName: tableName,
|
|
49
|
+
// FilterExpression: "begins_with(PK, :pk)",
|
|
50
|
+
// ExpressionAttributeValues: { ":pk": "USER#" }
|
|
51
|
+
// }));
|
|
52
|
+
//
|
|
53
|
+
// for (const item of result.Items || []) {
|
|
54
|
+
// await client.send(new UpdateCommand({
|
|
55
|
+
// TableName: tableName,
|
|
56
|
+
// Key: { PK: item.PK, SK: item.SK },
|
|
57
|
+
// UpdateExpression: "SET emailVerified = :value",
|
|
58
|
+
// ExpressionAttributeValues: { ":value": false }
|
|
59
|
+
// }));
|
|
60
|
+
// }
|
|
61
|
+
|
|
62
|
+
console.log("✅ Migration completed");
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Rollback migration
|
|
67
|
+
*/
|
|
68
|
+
async down({ client, tableName, dynamodb }) {
|
|
69
|
+
console.log("Rolling back migration: ${name}");
|
|
70
|
+
|
|
71
|
+
// Example: Remove the attribute added in up()
|
|
72
|
+
// const { ScanCommand, UpdateCommand } = dynamodb;
|
|
73
|
+
//
|
|
74
|
+
// const result = await client.send(new ScanCommand({
|
|
75
|
+
// TableName: tableName,
|
|
76
|
+
// FilterExpression: "begins_with(PK, :pk)",
|
|
77
|
+
// ExpressionAttributeValues: { ":pk": "USER#" }
|
|
78
|
+
// }));
|
|
79
|
+
//
|
|
80
|
+
// for (const item of result.Items || []) {
|
|
81
|
+
// await client.send(new UpdateCommand({
|
|
82
|
+
// TableName: tableName,
|
|
83
|
+
// Key: { PK: item.PK, SK: item.SK },
|
|
84
|
+
// UpdateExpression: "REMOVE email, emailVerified"
|
|
85
|
+
// }));
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
console.log("✅ Rollback completed");
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import type {
|
|
3
|
+
ScanCommand,
|
|
4
|
+
QueryCommand,
|
|
5
|
+
GetCommand,
|
|
6
|
+
PutCommand,
|
|
7
|
+
UpdateCommand,
|
|
8
|
+
DeleteCommand,
|
|
9
|
+
BatchGetCommand,
|
|
10
|
+
BatchWriteCommand,
|
|
11
|
+
TransactWriteCommand,
|
|
12
|
+
TransactGetCommand,
|
|
13
|
+
} from '@aws-sdk/lib-dynamodb';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* DynamoDB commands available in migrations
|
|
17
|
+
*/
|
|
18
|
+
export interface DynamoDBCommands {
|
|
19
|
+
ScanCommand: typeof ScanCommand;
|
|
20
|
+
QueryCommand: typeof QueryCommand;
|
|
21
|
+
GetCommand: typeof GetCommand;
|
|
22
|
+
PutCommand: typeof PutCommand;
|
|
23
|
+
UpdateCommand: typeof UpdateCommand;
|
|
24
|
+
DeleteCommand: typeof DeleteCommand;
|
|
25
|
+
BatchGetCommand: typeof BatchGetCommand;
|
|
26
|
+
BatchWriteCommand: typeof BatchWriteCommand;
|
|
27
|
+
TransactWriteCommand: typeof TransactWriteCommand;
|
|
28
|
+
TransactGetCommand: typeof TransactGetCommand;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Migration context provided to up/down functions
|
|
33
|
+
*/
|
|
34
|
+
export interface MigrationContext {
|
|
35
|
+
client: DynamoDBDocumentClient;
|
|
36
|
+
tableName: string;
|
|
37
|
+
tracker: MigrationTracker;
|
|
38
|
+
config: MigrationConfig;
|
|
39
|
+
dynamodb: DynamoDBCommands;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Schema change record for tracking evolution
|
|
44
|
+
*/
|
|
45
|
+
export interface SchemaChange {
|
|
46
|
+
entity: string;
|
|
47
|
+
changes: {
|
|
48
|
+
added?: string[];
|
|
49
|
+
removed?: string[];
|
|
50
|
+
modified?: Array<{
|
|
51
|
+
field: string;
|
|
52
|
+
from: any;
|
|
53
|
+
to: any;
|
|
54
|
+
}>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Migration definition
|
|
60
|
+
*/
|
|
61
|
+
export interface Migration {
|
|
62
|
+
version: string;
|
|
63
|
+
name: string;
|
|
64
|
+
description?: string;
|
|
65
|
+
|
|
66
|
+
// Optional schema snapshot for documentation
|
|
67
|
+
schema?: Record<string, any>;
|
|
68
|
+
|
|
69
|
+
// Migration functions
|
|
70
|
+
up: (context: MigrationContext) => Promise<void>;
|
|
71
|
+
down: (context: MigrationContext) => Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Migration record stored in DynamoDB
|
|
76
|
+
*/
|
|
77
|
+
export interface MigrationRecord {
|
|
78
|
+
PK: string; // "SCHEMA#VERSION"
|
|
79
|
+
SK: string; // "v0001"
|
|
80
|
+
version: string;
|
|
81
|
+
name: string;
|
|
82
|
+
description?: string;
|
|
83
|
+
timestamp: string;
|
|
84
|
+
appliedAt: string;
|
|
85
|
+
status: 'applied' | 'rolled_back' | 'failed';
|
|
86
|
+
schemaDefinition?: Record<string, any>;
|
|
87
|
+
schemaChanges?: SchemaChange[];
|
|
88
|
+
checksum?: string; // Hash of migration file
|
|
89
|
+
error?: string; // Error message if failed
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Current schema pointer record
|
|
94
|
+
*/
|
|
95
|
+
export interface CurrentSchemaRecord {
|
|
96
|
+
GSI1PK: string; // "SCHEMA#CURRENT"
|
|
97
|
+
GSI1SK: string; // Current version
|
|
98
|
+
PK: string;
|
|
99
|
+
SK: string;
|
|
100
|
+
currentVersion: string;
|
|
101
|
+
updatedAt: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Migration configuration
|
|
106
|
+
*/
|
|
107
|
+
export interface MigrationConfig {
|
|
108
|
+
tableName: string;
|
|
109
|
+
client: {
|
|
110
|
+
endpoint?: string;
|
|
111
|
+
region: string;
|
|
112
|
+
credentials?: {
|
|
113
|
+
accessKeyId: string;
|
|
114
|
+
secretAccessKey: string;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
migrationsDir?: string;
|
|
118
|
+
trackingPrefix?: string; // Default: "_SCHEMA#VERSION"
|
|
119
|
+
gsi1Name?: string; // Default: "GSI1"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Migration tracker interface
|
|
124
|
+
*/
|
|
125
|
+
export interface MigrationTracker {
|
|
126
|
+
/**
|
|
127
|
+
* Initialize migration tracking table/items if needed
|
|
128
|
+
*/
|
|
129
|
+
initialize(): Promise<void>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get all applied migrations
|
|
133
|
+
*/
|
|
134
|
+
getAppliedMigrations(): Promise<MigrationRecord[]>;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get current schema version
|
|
138
|
+
*/
|
|
139
|
+
getCurrentVersion(): Promise<string | null>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Mark migration as applied
|
|
143
|
+
*/
|
|
144
|
+
markAsApplied(
|
|
145
|
+
version: string,
|
|
146
|
+
name: string,
|
|
147
|
+
schemaDefinition?: Record<string, any>,
|
|
148
|
+
schemaChanges?: SchemaChange[]
|
|
149
|
+
): Promise<void>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Mark migration as rolled back
|
|
153
|
+
*/
|
|
154
|
+
markAsRolledBack(version: string): Promise<void>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Mark migration as failed
|
|
158
|
+
*/
|
|
159
|
+
markAsFailed(version: string, error: string): Promise<void>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Record schema changes
|
|
163
|
+
*/
|
|
164
|
+
recordSchemaChange(change: SchemaChange): Promise<void>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get migration by version
|
|
168
|
+
*/
|
|
169
|
+
getMigration(version: string): Promise<MigrationRecord | null>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if migration was applied
|
|
173
|
+
*/
|
|
174
|
+
isApplied(version: string): Promise<boolean>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Migration file info
|
|
179
|
+
*/
|
|
180
|
+
export interface MigrationFile {
|
|
181
|
+
version: string;
|
|
182
|
+
name: string;
|
|
183
|
+
filePath: string;
|
|
184
|
+
migration: Migration;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Migration status
|
|
189
|
+
*/
|
|
190
|
+
export interface MigrationStatus {
|
|
191
|
+
version: string;
|
|
192
|
+
name: string;
|
|
193
|
+
status: 'pending' | 'applied' | 'rolled_back' | 'failed';
|
|
194
|
+
appliedAt?: string;
|
|
195
|
+
error?: string;
|
|
196
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "@repo/typescript-config/base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"module": "commonjs",
|
|
7
|
+
"target": "ES2020",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"strict": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"moduleResolution": "node"
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
|
|
19
|
+
}
|