@stacksjs/ts-cloud-core 0.1.1
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/LICENSE.md +21 -0
- package/README.md +321 -0
- package/package.json +31 -0
- package/src/advanced-features.test.ts +465 -0
- package/src/aws/cloudformation.ts +421 -0
- package/src/aws/cloudfront.ts +158 -0
- package/src/aws/credentials.test.ts +132 -0
- package/src/aws/credentials.ts +545 -0
- package/src/aws/index.ts +87 -0
- package/src/aws/s3.test.ts +188 -0
- package/src/aws/s3.ts +1088 -0
- package/src/aws/signature.test.ts +670 -0
- package/src/aws/signature.ts +1155 -0
- package/src/backup/disaster-recovery.test.ts +726 -0
- package/src/backup/disaster-recovery.ts +500 -0
- package/src/backup/index.ts +34 -0
- package/src/backup/manager.test.ts +498 -0
- package/src/backup/manager.ts +432 -0
- package/src/cicd/circleci.ts +430 -0
- package/src/cicd/github-actions.ts +424 -0
- package/src/cicd/gitlab-ci.ts +255 -0
- package/src/cicd/index.ts +8 -0
- package/src/cli/history.ts +396 -0
- package/src/cli/index.ts +10 -0
- package/src/cli/progress.ts +458 -0
- package/src/cli/repl.ts +454 -0
- package/src/cli/suggestions.ts +327 -0
- package/src/cli/table.test.ts +319 -0
- package/src/cli/table.ts +332 -0
- package/src/cloudformation/builder.test.ts +327 -0
- package/src/cloudformation/builder.ts +378 -0
- package/src/cloudformation/builders/api-gateway.ts +449 -0
- package/src/cloudformation/builders/cache.ts +334 -0
- package/src/cloudformation/builders/cdn.ts +278 -0
- package/src/cloudformation/builders/compute.ts +485 -0
- package/src/cloudformation/builders/database.ts +392 -0
- package/src/cloudformation/builders/functions.ts +343 -0
- package/src/cloudformation/builders/messaging.ts +140 -0
- package/src/cloudformation/builders/monitoring.ts +300 -0
- package/src/cloudformation/builders/network.ts +264 -0
- package/src/cloudformation/builders/queue.ts +147 -0
- package/src/cloudformation/builders/security.ts +399 -0
- package/src/cloudformation/builders/storage.ts +285 -0
- package/src/cloudformation/index.ts +30 -0
- package/src/cloudformation/types.ts +173 -0
- package/src/compliance/aws-config.ts +543 -0
- package/src/compliance/cloudtrail.ts +376 -0
- package/src/compliance/compliance.test.ts +423 -0
- package/src/compliance/guardduty.ts +446 -0
- package/src/compliance/index.ts +66 -0
- package/src/compliance/security-hub.ts +456 -0
- package/src/containers/build-optimization.ts +416 -0
- package/src/containers/containers.test.ts +508 -0
- package/src/containers/image-scanning.ts +360 -0
- package/src/containers/index.ts +9 -0
- package/src/containers/registry.ts +293 -0
- package/src/containers/service-mesh.ts +520 -0
- package/src/database/database.test.ts +762 -0
- package/src/database/index.ts +9 -0
- package/src/database/migrations.ts +444 -0
- package/src/database/performance.ts +528 -0
- package/src/database/replicas.ts +534 -0
- package/src/database/users.ts +494 -0
- package/src/dependency-graph.ts +143 -0
- package/src/deployment/ab-testing.ts +582 -0
- package/src/deployment/blue-green.ts +452 -0
- package/src/deployment/canary.ts +500 -0
- package/src/deployment/deployment.test.ts +526 -0
- package/src/deployment/index.ts +61 -0
- package/src/deployment/progressive.ts +62 -0
- package/src/dns/dns.test.ts +641 -0
- package/src/dns/dnssec.ts +315 -0
- package/src/dns/index.ts +8 -0
- package/src/dns/resolver.ts +496 -0
- package/src/dns/routing.ts +593 -0
- package/src/email/advanced/analytics.ts +445 -0
- package/src/email/advanced/index.ts +11 -0
- package/src/email/advanced/rules.ts +465 -0
- package/src/email/advanced/scheduling.ts +352 -0
- package/src/email/advanced/search.ts +412 -0
- package/src/email/advanced/shared-mailboxes.ts +404 -0
- package/src/email/advanced/templates.ts +455 -0
- package/src/email/advanced/threading.ts +281 -0
- package/src/email/analytics.ts +467 -0
- package/src/email/bounce-handling.ts +425 -0
- package/src/email/email.test.ts +431 -0
- package/src/email/handlers/__tests__/inbound.test.ts +38 -0
- package/src/email/handlers/__tests__/outbound.test.ts +37 -0
- package/src/email/handlers/converter.ts +227 -0
- package/src/email/handlers/feedback.ts +228 -0
- package/src/email/handlers/inbound.ts +169 -0
- package/src/email/handlers/outbound.ts +178 -0
- package/src/email/index.ts +15 -0
- package/src/email/reputation.ts +303 -0
- package/src/email/templates.ts +352 -0
- package/src/errors/index.test.ts +434 -0
- package/src/errors/index.ts +416 -0
- package/src/health-checks/index.ts +40 -0
- package/src/index.ts +360 -0
- package/src/intrinsic-functions.ts +118 -0
- package/src/lambda/concurrency.ts +330 -0
- package/src/lambda/destinations.ts +345 -0
- package/src/lambda/dlq.ts +425 -0
- package/src/lambda/index.ts +11 -0
- package/src/lambda/lambda.test.ts +840 -0
- package/src/lambda/layers.ts +263 -0
- package/src/lambda/versions.ts +376 -0
- package/src/lambda/vpc.ts +399 -0
- package/src/local/config.ts +114 -0
- package/src/local/index.ts +6 -0
- package/src/local/mock-aws.ts +351 -0
- package/src/modules/ai.ts +340 -0
- package/src/modules/api.ts +478 -0
- package/src/modules/auth.ts +805 -0
- package/src/modules/cache.ts +417 -0
- package/src/modules/cdn.ts +1062 -0
- package/src/modules/communication.ts +1094 -0
- package/src/modules/compute.ts +3348 -0
- package/src/modules/database.ts +554 -0
- package/src/modules/deployment.ts +1079 -0
- package/src/modules/dns.ts +337 -0
- package/src/modules/email.ts +1538 -0
- package/src/modules/filesystem.ts +515 -0
- package/src/modules/index.ts +32 -0
- package/src/modules/messaging.ts +486 -0
- package/src/modules/monitoring.ts +2086 -0
- package/src/modules/network.ts +664 -0
- package/src/modules/parameter-store.ts +325 -0
- package/src/modules/permissions.ts +1081 -0
- package/src/modules/phone.ts +494 -0
- package/src/modules/queue.ts +1260 -0
- package/src/modules/redirects.ts +464 -0
- package/src/modules/registry.ts +699 -0
- package/src/modules/search.ts +401 -0
- package/src/modules/secrets.ts +416 -0
- package/src/modules/security.ts +731 -0
- package/src/modules/sms.ts +389 -0
- package/src/modules/storage.ts +1120 -0
- package/src/modules/workflow.ts +680 -0
- package/src/multi-account/config.ts +521 -0
- package/src/multi-account/index.ts +7 -0
- package/src/multi-account/manager.ts +427 -0
- package/src/multi-region/cross-region.ts +410 -0
- package/src/multi-region/index.ts +8 -0
- package/src/multi-region/manager.ts +483 -0
- package/src/multi-region/regions.ts +435 -0
- package/src/network-security/index.ts +48 -0
- package/src/observability/index.ts +9 -0
- package/src/observability/logs.ts +522 -0
- package/src/observability/metrics.ts +460 -0
- package/src/observability/observability.test.ts +782 -0
- package/src/observability/synthetics.ts +568 -0
- package/src/observability/xray.ts +358 -0
- package/src/phone/advanced/analytics.ts +349 -0
- package/src/phone/advanced/callbacks.ts +428 -0
- package/src/phone/advanced/index.ts +8 -0
- package/src/phone/advanced/ivr-builder.ts +504 -0
- package/src/phone/advanced/recording.ts +310 -0
- package/src/phone/handlers/__tests__/incoming-call.test.ts +40 -0
- package/src/phone/handlers/incoming-call.ts +117 -0
- package/src/phone/handlers/missed-call.ts +116 -0
- package/src/phone/handlers/voicemail.ts +179 -0
- package/src/phone/index.ts +9 -0
- package/src/presets/api-backend.ts +134 -0
- package/src/presets/data-pipeline.ts +204 -0
- package/src/presets/extend.test.ts +295 -0
- package/src/presets/extend.ts +297 -0
- package/src/presets/fullstack-app.ts +144 -0
- package/src/presets/index.ts +27 -0
- package/src/presets/jamstack.ts +135 -0
- package/src/presets/microservices.ts +167 -0
- package/src/presets/ml-api.ts +208 -0
- package/src/presets/nodejs-server.ts +104 -0
- package/src/presets/nodejs-serverless.ts +114 -0
- package/src/presets/realtime-app.ts +184 -0
- package/src/presets/static-site.ts +64 -0
- package/src/presets/traditional-web-app.ts +339 -0
- package/src/presets/wordpress.ts +138 -0
- package/src/preview/github.test.ts +249 -0
- package/src/preview/github.ts +297 -0
- package/src/preview/index.ts +37 -0
- package/src/preview/manager.test.ts +440 -0
- package/src/preview/manager.ts +326 -0
- package/src/preview/notifications.test.ts +582 -0
- package/src/preview/notifications.ts +341 -0
- package/src/queue/batch-processing.ts +402 -0
- package/src/queue/dlq-monitoring.ts +402 -0
- package/src/queue/fifo.ts +342 -0
- package/src/queue/index.ts +9 -0
- package/src/queue/management.ts +428 -0
- package/src/queue/queue.test.ts +429 -0
- package/src/resource-mgmt/index.ts +39 -0
- package/src/resource-naming.ts +62 -0
- package/src/s3/index.ts +523 -0
- package/src/schema/cloud-config.schema.json +554 -0
- package/src/schema/index.ts +68 -0
- package/src/security/certificate-manager.ts +492 -0
- package/src/security/index.ts +9 -0
- package/src/security/scanning.ts +545 -0
- package/src/security/secrets-manager.ts +476 -0
- package/src/security/secrets-rotation.ts +456 -0
- package/src/security/security.test.ts +738 -0
- package/src/sms/advanced/ab-testing.ts +389 -0
- package/src/sms/advanced/analytics.ts +336 -0
- package/src/sms/advanced/campaigns.ts +523 -0
- package/src/sms/advanced/chatbot.ts +224 -0
- package/src/sms/advanced/index.ts +10 -0
- package/src/sms/advanced/link-tracking.ts +248 -0
- package/src/sms/advanced/mms.ts +308 -0
- package/src/sms/handlers/__tests__/send.test.ts +40 -0
- package/src/sms/handlers/delivery-status.ts +133 -0
- package/src/sms/handlers/receive.ts +162 -0
- package/src/sms/handlers/send.ts +174 -0
- package/src/sms/index.ts +9 -0
- package/src/stack-diff.ts +389 -0
- package/src/static-site/index.ts +85 -0
- package/src/template-builder.ts +110 -0
- package/src/template-validator.ts +574 -0
- package/src/utils/cache.ts +291 -0
- package/src/utils/diff.ts +269 -0
- package/src/utils/hash.ts +227 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/parallel.ts +294 -0
- package/src/validators/credentials.test.ts +274 -0
- package/src/validators/credentials.ts +233 -0
- package/src/validators/quotas.test.ts +434 -0
- package/src/validators/quotas.ts +217 -0
- package/test/ai.test.ts +327 -0
- package/test/api.test.ts +511 -0
- package/test/auth.test.ts +632 -0
- package/test/cache.test.ts +406 -0
- package/test/cdn.test.ts +247 -0
- package/test/compute.test.ts +861 -0
- package/test/database.test.ts +523 -0
- package/test/deployment.test.ts +499 -0
- package/test/dns.test.ts +270 -0
- package/test/email.test.ts +439 -0
- package/test/filesystem.test.ts +382 -0
- package/test/integration.test.ts +350 -0
- package/test/messaging.test.ts +514 -0
- package/test/monitoring.test.ts +634 -0
- package/test/network.test.ts +425 -0
- package/test/permissions.test.ts +488 -0
- package/test/queue.test.ts +484 -0
- package/test/registry.test.ts +306 -0
- package/test/security.test.ts +462 -0
- package/test/storage.test.ts +463 -0
- package/test/template-validator.test.ts +559 -0
- package/test/workflow.test.ts +592 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Migration Management
|
|
3
|
+
* Version-controlled schema changes with rollback support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Migration {
|
|
7
|
+
id: string
|
|
8
|
+
version: string
|
|
9
|
+
name: string
|
|
10
|
+
description?: string
|
|
11
|
+
up: string // SQL for applying migration
|
|
12
|
+
down: string // SQL for rolling back migration
|
|
13
|
+
checksum: string // For integrity verification
|
|
14
|
+
appliedAt?: Date
|
|
15
|
+
rolledBackAt?: Date
|
|
16
|
+
executionTimeMs?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MigrationPlan {
|
|
20
|
+
id: string
|
|
21
|
+
name: string
|
|
22
|
+
database: DatabaseTarget
|
|
23
|
+
migrations: Migration[]
|
|
24
|
+
autoApply?: boolean
|
|
25
|
+
backupBeforeMigration?: boolean
|
|
26
|
+
testMigrations?: boolean
|
|
27
|
+
validateRollback?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DatabaseTarget {
|
|
31
|
+
type: 'rds' | 'aurora' | 'dynamodb'
|
|
32
|
+
identifier: string
|
|
33
|
+
engine?: 'postgres' | 'mysql' | 'mariadb'
|
|
34
|
+
endpoint?: string
|
|
35
|
+
database?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MigrationResult {
|
|
39
|
+
success: boolean
|
|
40
|
+
version: string
|
|
41
|
+
appliedMigrations: string[]
|
|
42
|
+
failedMigrations: string[]
|
|
43
|
+
executionTimeMs: number
|
|
44
|
+
error?: string
|
|
45
|
+
rollbackPerformed?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface MigrationStatus {
|
|
49
|
+
currentVersion: string
|
|
50
|
+
pendingMigrations: Migration[]
|
|
51
|
+
appliedMigrations: Migration[]
|
|
52
|
+
lastMigration?: Migration
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Migration manager
|
|
57
|
+
*/
|
|
58
|
+
export class MigrationManager {
|
|
59
|
+
private plans: Map<string, MigrationPlan> = new Map()
|
|
60
|
+
private migrations: Map<string, Migration> = new Map()
|
|
61
|
+
private planCounter = 0
|
|
62
|
+
private migrationCounter = 0
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create migration plan
|
|
66
|
+
*/
|
|
67
|
+
createPlan(plan: Omit<MigrationPlan, 'id'>): MigrationPlan {
|
|
68
|
+
const id = `migration-plan-${Date.now()}-${this.planCounter++}`
|
|
69
|
+
|
|
70
|
+
const migrationPlan: MigrationPlan = {
|
|
71
|
+
id,
|
|
72
|
+
...plan,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.plans.set(id, migrationPlan)
|
|
76
|
+
|
|
77
|
+
return migrationPlan
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create migration
|
|
82
|
+
*/
|
|
83
|
+
createMigration(migration: Omit<Migration, 'id' | 'checksum'>): Migration {
|
|
84
|
+
const id = `migration-${Date.now()}-${this.migrationCounter++}`
|
|
85
|
+
|
|
86
|
+
// Generate checksum from up and down SQL
|
|
87
|
+
const checksum = this.generateChecksum(migration.up + migration.down)
|
|
88
|
+
|
|
89
|
+
const newMigration: Migration = {
|
|
90
|
+
id,
|
|
91
|
+
checksum,
|
|
92
|
+
...migration,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.migrations.set(id, newMigration)
|
|
96
|
+
|
|
97
|
+
return newMigration
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create schema change migration
|
|
102
|
+
*/
|
|
103
|
+
createSchemaMigration(options: {
|
|
104
|
+
version: string
|
|
105
|
+
name: string
|
|
106
|
+
tableName: string
|
|
107
|
+
changes: SchemaChange[]
|
|
108
|
+
engine?: 'postgres' | 'mysql'
|
|
109
|
+
}): Migration {
|
|
110
|
+
const { up, down } = this.generateSchemaSQL(options.changes, options.tableName, options.engine)
|
|
111
|
+
|
|
112
|
+
return this.createMigration({
|
|
113
|
+
version: options.version,
|
|
114
|
+
name: options.name,
|
|
115
|
+
description: `Schema changes for ${options.tableName}`,
|
|
116
|
+
up,
|
|
117
|
+
down,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Create data migration
|
|
123
|
+
*/
|
|
124
|
+
createDataMigration(options: {
|
|
125
|
+
version: string
|
|
126
|
+
name: string
|
|
127
|
+
description?: string
|
|
128
|
+
upSQL: string
|
|
129
|
+
downSQL: string
|
|
130
|
+
}): Migration {
|
|
131
|
+
return this.createMigration({
|
|
132
|
+
version: options.version,
|
|
133
|
+
name: options.name,
|
|
134
|
+
description: options.description,
|
|
135
|
+
up: options.upSQL,
|
|
136
|
+
down: options.downSQL,
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Add migration to plan
|
|
142
|
+
*/
|
|
143
|
+
addMigrationToPlan(planId: string, migration: Migration): void {
|
|
144
|
+
const plan = this.plans.get(planId)
|
|
145
|
+
|
|
146
|
+
if (!plan) {
|
|
147
|
+
throw new Error(`Migration plan not found: ${planId}`)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
plan.migrations.push(migration)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Execute migration plan
|
|
155
|
+
*/
|
|
156
|
+
async executePlan(planId: string, dryRun: boolean = false): Promise<MigrationResult> {
|
|
157
|
+
const plan = this.plans.get(planId)
|
|
158
|
+
|
|
159
|
+
if (!plan) {
|
|
160
|
+
throw new Error(`Migration plan not found: ${planId}`)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const startTime = Date.now()
|
|
164
|
+
const appliedMigrations: string[] = []
|
|
165
|
+
const failedMigrations: string[] = []
|
|
166
|
+
|
|
167
|
+
console.log(`${dryRun ? '[DRY RUN] ' : ''}Executing migration plan: ${plan.name}`)
|
|
168
|
+
console.log(`Database: ${plan.database.type} - ${plan.database.identifier}`)
|
|
169
|
+
console.log(`Migrations to apply: ${plan.migrations.length}\n`)
|
|
170
|
+
|
|
171
|
+
if (plan.backupBeforeMigration && !dryRun) {
|
|
172
|
+
console.log('Creating database backup before migration...')
|
|
173
|
+
// Backup logic would go here
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const migration of plan.migrations) {
|
|
177
|
+
try {
|
|
178
|
+
console.log(`Applying migration: ${migration.version} - ${migration.name}`)
|
|
179
|
+
|
|
180
|
+
if (!dryRun) {
|
|
181
|
+
// Verify checksum
|
|
182
|
+
const currentChecksum = this.generateChecksum(migration.up + migration.down)
|
|
183
|
+
if (currentChecksum !== migration.checksum) {
|
|
184
|
+
throw new Error('Migration checksum mismatch - migration has been modified')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Execute migration
|
|
188
|
+
const migrationStart = Date.now()
|
|
189
|
+
// Actual SQL execution would go here
|
|
190
|
+
migration.appliedAt = new Date()
|
|
191
|
+
migration.executionTimeMs = Date.now() - migrationStart
|
|
192
|
+
|
|
193
|
+
console.log(`✓ Applied in ${migration.executionTimeMs}ms\n`)
|
|
194
|
+
} else {
|
|
195
|
+
console.log(`[SKIPPED - DRY RUN]\n`)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
appliedMigrations.push(migration.version)
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error(`✗ Failed: ${error instanceof Error ? error.message : String(error)}\n`)
|
|
201
|
+
failedMigrations.push(migration.version)
|
|
202
|
+
|
|
203
|
+
if (!dryRun) {
|
|
204
|
+
// Rollback previously applied migrations
|
|
205
|
+
console.log('Rolling back previously applied migrations...')
|
|
206
|
+
await this.rollbackMigrations(appliedMigrations.reverse(), plan)
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
version: migration.version,
|
|
211
|
+
appliedMigrations: [],
|
|
212
|
+
failedMigrations,
|
|
213
|
+
executionTimeMs: Date.now() - startTime,
|
|
214
|
+
error: error instanceof Error ? error.message : String(error),
|
|
215
|
+
rollbackPerformed: true,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const executionTimeMs = Date.now() - startTime
|
|
222
|
+
|
|
223
|
+
console.log(`${dryRun ? '[DRY RUN] ' : ''}Migration plan completed successfully`)
|
|
224
|
+
console.log(`Total time: ${executionTimeMs}ms`)
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
success: true,
|
|
228
|
+
version: plan.migrations[plan.migrations.length - 1]?.version || 'unknown',
|
|
229
|
+
appliedMigrations,
|
|
230
|
+
failedMigrations,
|
|
231
|
+
executionTimeMs,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Rollback migrations
|
|
237
|
+
*/
|
|
238
|
+
private async rollbackMigrations(versions: string[], plan: MigrationPlan): Promise<void> {
|
|
239
|
+
for (const version of versions) {
|
|
240
|
+
const migration = plan.migrations.find(m => m.version === version)
|
|
241
|
+
if (migration) {
|
|
242
|
+
console.log(`Rolling back: ${migration.version} - ${migration.name}`)
|
|
243
|
+
// Execute down SQL
|
|
244
|
+
migration.rolledBackAt = new Date()
|
|
245
|
+
console.log('✓ Rolled back\n')
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get migration status
|
|
252
|
+
*/
|
|
253
|
+
getMigrationStatus(planId: string): MigrationStatus {
|
|
254
|
+
const plan = this.plans.get(planId)
|
|
255
|
+
|
|
256
|
+
if (!plan) {
|
|
257
|
+
throw new Error(`Migration plan not found: ${planId}`)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const appliedMigrations = plan.migrations.filter(m => m.appliedAt && !m.rolledBackAt)
|
|
261
|
+
const pendingMigrations = plan.migrations.filter(m => !m.appliedAt)
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
currentVersion: appliedMigrations[appliedMigrations.length - 1]?.version || '0.0.0',
|
|
265
|
+
pendingMigrations,
|
|
266
|
+
appliedMigrations,
|
|
267
|
+
lastMigration: appliedMigrations[appliedMigrations.length - 1],
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Generate schema SQL from changes
|
|
273
|
+
*/
|
|
274
|
+
private generateSchemaSQL(
|
|
275
|
+
changes: SchemaChange[],
|
|
276
|
+
tableName: string,
|
|
277
|
+
engine: 'postgres' | 'mysql' = 'postgres'
|
|
278
|
+
): { up: string; down: string } {
|
|
279
|
+
const upStatements: string[] = []
|
|
280
|
+
const downStatements: string[] = []
|
|
281
|
+
|
|
282
|
+
for (const change of changes) {
|
|
283
|
+
switch (change.type) {
|
|
284
|
+
case 'add_column':
|
|
285
|
+
upStatements.push(
|
|
286
|
+
`ALTER TABLE ${tableName} ADD COLUMN ${change.columnName} ${change.columnType}${
|
|
287
|
+
change.nullable === false ? ' NOT NULL' : ''
|
|
288
|
+
}${change.defaultValue ? ` DEFAULT ${change.defaultValue}` : ''};`
|
|
289
|
+
)
|
|
290
|
+
downStatements.push(`ALTER TABLE ${tableName} DROP COLUMN ${change.columnName};`)
|
|
291
|
+
break
|
|
292
|
+
|
|
293
|
+
case 'drop_column':
|
|
294
|
+
upStatements.push(`ALTER TABLE ${tableName} DROP COLUMN ${change.columnName};`)
|
|
295
|
+
// Note: Cannot restore dropped column without backup
|
|
296
|
+
downStatements.push(`-- Cannot restore dropped column ${change.columnName}`)
|
|
297
|
+
break
|
|
298
|
+
|
|
299
|
+
case 'modify_column':
|
|
300
|
+
if (engine === 'postgres') {
|
|
301
|
+
upStatements.push(
|
|
302
|
+
`ALTER TABLE ${tableName} ALTER COLUMN ${change.columnName} TYPE ${change.newType};`
|
|
303
|
+
)
|
|
304
|
+
} else {
|
|
305
|
+
upStatements.push(
|
|
306
|
+
`ALTER TABLE ${tableName} MODIFY COLUMN ${change.columnName} ${change.newType};`
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
downStatements.push(`-- Reverting ${change.columnName} type change requires manual intervention`)
|
|
310
|
+
break
|
|
311
|
+
|
|
312
|
+
case 'add_index':
|
|
313
|
+
upStatements.push(
|
|
314
|
+
`CREATE INDEX ${change.indexName} ON ${tableName} (${(change.columns ?? []).join(', ')});`
|
|
315
|
+
)
|
|
316
|
+
downStatements.push(`DROP INDEX ${change.indexName};`)
|
|
317
|
+
break
|
|
318
|
+
|
|
319
|
+
case 'drop_index':
|
|
320
|
+
upStatements.push(`DROP INDEX ${change.indexName};`)
|
|
321
|
+
downStatements.push(`-- Cannot restore index ${change.indexName} without schema details`)
|
|
322
|
+
break
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
up: upStatements.join('\n'),
|
|
328
|
+
down: downStatements.reverse().join('\n'),
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Generate checksum for migration
|
|
334
|
+
*/
|
|
335
|
+
private generateChecksum(content: string): string {
|
|
336
|
+
// Simple checksum implementation (in production, use crypto hash)
|
|
337
|
+
let hash = 0
|
|
338
|
+
for (let i = 0; i < content.length; i++) {
|
|
339
|
+
const char = content.charCodeAt(i)
|
|
340
|
+
hash = (hash << 5) - hash + char
|
|
341
|
+
hash = hash & hash // Convert to 32-bit integer
|
|
342
|
+
}
|
|
343
|
+
return hash.toString(16)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Validate migration plan
|
|
348
|
+
*/
|
|
349
|
+
validatePlan(planId: string): { valid: boolean; errors: string[] } {
|
|
350
|
+
const plan = this.plans.get(planId)
|
|
351
|
+
|
|
352
|
+
if (!plan) {
|
|
353
|
+
return { valid: false, errors: ['Migration plan not found'] }
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const errors: string[] = []
|
|
357
|
+
|
|
358
|
+
// Check for version conflicts
|
|
359
|
+
const versions = new Set<string>()
|
|
360
|
+
for (const migration of plan.migrations) {
|
|
361
|
+
if (versions.has(migration.version)) {
|
|
362
|
+
errors.push(`Duplicate migration version: ${migration.version}`)
|
|
363
|
+
}
|
|
364
|
+
versions.add(migration.version)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Check for missing down migrations
|
|
368
|
+
for (const migration of plan.migrations) {
|
|
369
|
+
if (!migration.down || migration.down.trim() === '') {
|
|
370
|
+
errors.push(`Migration ${migration.version} is missing rollback SQL`)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check for checksum mismatches
|
|
375
|
+
for (const migration of plan.migrations) {
|
|
376
|
+
const currentChecksum = this.generateChecksum(migration.up + migration.down)
|
|
377
|
+
if (currentChecksum !== migration.checksum) {
|
|
378
|
+
errors.push(`Migration ${migration.version} has invalid checksum`)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
valid: errors.length === 0,
|
|
384
|
+
errors,
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get plan
|
|
390
|
+
*/
|
|
391
|
+
getPlan(id: string): MigrationPlan | undefined {
|
|
392
|
+
return this.plans.get(id)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* List plans
|
|
397
|
+
*/
|
|
398
|
+
listPlans(): MigrationPlan[] {
|
|
399
|
+
return Array.from(this.plans.values())
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Get migration
|
|
404
|
+
*/
|
|
405
|
+
getMigration(id: string): Migration | undefined {
|
|
406
|
+
return this.migrations.get(id)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* List migrations
|
|
411
|
+
*/
|
|
412
|
+
listMigrations(): Migration[] {
|
|
413
|
+
return Array.from(this.migrations.values())
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Clear all data
|
|
418
|
+
*/
|
|
419
|
+
clear(): void {
|
|
420
|
+
this.plans.clear()
|
|
421
|
+
this.migrations.clear()
|
|
422
|
+
this.planCounter = 0
|
|
423
|
+
this.migrationCounter = 0
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Schema change types
|
|
429
|
+
*/
|
|
430
|
+
export interface SchemaChange {
|
|
431
|
+
type: 'add_column' | 'drop_column' | 'modify_column' | 'add_index' | 'drop_index'
|
|
432
|
+
columnName?: string
|
|
433
|
+
columnType?: string
|
|
434
|
+
newType?: string
|
|
435
|
+
nullable?: boolean
|
|
436
|
+
defaultValue?: string
|
|
437
|
+
indexName?: string
|
|
438
|
+
columns?: string[]
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Global migration manager instance
|
|
443
|
+
*/
|
|
444
|
+
export const migrationManager: MigrationManager = new MigrationManager()
|