@prisma-next/target-postgres 0.4.0-dev.9 → 0.4.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.
Files changed (44) hide show
  1. package/dist/control.d.mts +1 -9
  2. package/dist/control.d.mts.map +1 -1
  3. package/dist/control.mjs +1693 -4798
  4. package/dist/control.mjs.map +1 -1
  5. package/dist/migration.d.mts +164 -0
  6. package/dist/migration.d.mts.map +1 -0
  7. package/dist/migration.mjs +446 -0
  8. package/dist/migration.mjs.map +1 -0
  9. package/dist/planner-target-details-MXb3oeul.d.mts +11 -0
  10. package/dist/planner-target-details-MXb3oeul.d.mts.map +1 -0
  11. package/dist/postgres-migration-BsHJHV9O.mjs +2793 -0
  12. package/dist/postgres-migration-BsHJHV9O.mjs.map +1 -0
  13. package/package.json +20 -18
  14. package/src/core/migrations/issue-planner.ts +832 -0
  15. package/src/core/migrations/op-factory-call.ts +862 -0
  16. package/src/core/migrations/operations/columns.ts +285 -0
  17. package/src/core/migrations/operations/constraints.ts +191 -0
  18. package/src/core/migrations/operations/data-transform.ts +113 -0
  19. package/src/core/migrations/operations/dependencies.ts +36 -0
  20. package/src/core/migrations/operations/enums.ts +113 -0
  21. package/src/core/migrations/operations/indexes.ts +61 -0
  22. package/src/core/migrations/operations/raw.ts +15 -0
  23. package/src/core/migrations/operations/shared.ts +67 -0
  24. package/src/core/migrations/operations/tables.ts +63 -0
  25. package/src/core/migrations/planner-produced-postgres-migration.ts +67 -0
  26. package/src/core/migrations/planner-strategies.ts +592 -151
  27. package/src/core/migrations/planner-target-details.ts +0 -6
  28. package/src/core/migrations/planner.ts +63 -781
  29. package/src/core/migrations/postgres-migration.ts +20 -0
  30. package/src/core/migrations/render-ops.ts +9 -0
  31. package/src/core/migrations/render-typescript.ts +95 -0
  32. package/src/exports/control.ts +9 -142
  33. package/src/exports/migration.ts +40 -0
  34. package/dist/migration-builders.d.mts +0 -88
  35. package/dist/migration-builders.d.mts.map +0 -1
  36. package/dist/migration-builders.mjs +0 -3
  37. package/dist/operation-descriptors-CxymFSgK.mjs +0 -52
  38. package/dist/operation-descriptors-CxymFSgK.mjs.map +0 -1
  39. package/src/core/migrations/descriptor-planner.ts +0 -464
  40. package/src/core/migrations/operation-descriptors.ts +0 -166
  41. package/src/core/migrations/operation-resolver.ts +0 -929
  42. package/src/core/migrations/planner-reconciliation.ts +0 -798
  43. package/src/core/migrations/scaffolding.ts +0 -140
  44. package/src/exports/migration-builders.ts +0 -56
@@ -0,0 +1,862 @@
1
+ /**
2
+ * Postgres migration IR: one concrete `*Call` class per pure factory under
3
+ * `operations/`, plus a shared `PostgresOpFactoryCallNode` abstract base.
4
+ *
5
+ * Every call class carries the literal arguments its backing factory would
6
+ * receive, computes a human-readable `label` in its constructor, and
7
+ * implements two polymorphic hooks:
8
+ *
9
+ * - `toOp()` — converts the IR node to a runtime
10
+ * `SqlMigrationPlanOperation` by delegating to the matching pure factory
11
+ * under `operations/`. `DataTransformCall.toOp()` always throws
12
+ * `PN-MIG-2001` because a planner-generated data transform is an
13
+ * unfilled authoring stub by construction.
14
+ * - `renderTypeScript()` / `importRequirements()` — inherited from
15
+ * `TsExpression`. Used by `renderCallsToTypeScript` to emit the call as
16
+ * a TypeScript expression inside the scaffolded `migration.ts`.
17
+ *
18
+ * The abstract base and all concrete classes are package-private. External
19
+ * consumers see only the framework-level `OpFactoryCall` interface and the
20
+ * `PostgresOpFactoryCall` union.
21
+ */
22
+
23
+ import { errorUnfilledPlaceholder } from '@prisma-next/errors/migration';
24
+ import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
25
+ import type {
26
+ OpFactoryCall as FrameworkOpFactoryCall,
27
+ MigrationOperationClass,
28
+ } from '@prisma-next/framework-components/control';
29
+ import { type ImportRequirement, jsonToTsSource, TsExpression } from '@prisma-next/ts-render';
30
+ import {
31
+ addColumn,
32
+ alterColumnType,
33
+ dropColumn,
34
+ dropDefault,
35
+ dropNotNull,
36
+ setDefault,
37
+ setNotNull,
38
+ } from './operations/columns';
39
+ import { addForeignKey, addPrimaryKey, addUnique, dropConstraint } from './operations/constraints';
40
+ import { createExtension, createSchema } from './operations/dependencies';
41
+ import { addEnumValues, createEnumType, dropEnumType, renameType } from './operations/enums';
42
+ import { createIndex, dropIndex } from './operations/indexes';
43
+ import type { ColumnSpec, ForeignKeySpec } from './operations/shared';
44
+ import { createTable, dropTable } from './operations/tables';
45
+ import type { PostgresPlanTargetDetails } from './planner-target-details';
46
+
47
+ type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
48
+
49
+ const TARGET_MIGRATION_MODULE = '@prisma-next/target-postgres/migration';
50
+
51
+ abstract class PostgresOpFactoryCallNode extends TsExpression implements FrameworkOpFactoryCall {
52
+ abstract readonly factoryName: string;
53
+ abstract readonly operationClass: MigrationOperationClass;
54
+ abstract readonly label: string;
55
+ abstract toOp(): Op;
56
+
57
+ importRequirements(): readonly ImportRequirement[] {
58
+ return [{ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: this.factoryName }];
59
+ }
60
+
61
+ protected freeze(): void {
62
+ Object.freeze(this);
63
+ }
64
+ }
65
+
66
+ // ============================================================================
67
+ // Table
68
+ // ============================================================================
69
+
70
+ export interface CreateTablePrimaryKey {
71
+ readonly columns: readonly string[];
72
+ }
73
+
74
+ export class CreateTableCall extends PostgresOpFactoryCallNode {
75
+ readonly factoryName = 'createTable' as const;
76
+ readonly operationClass = 'additive' as const;
77
+ readonly schemaName: string;
78
+ readonly tableName: string;
79
+ readonly columns: readonly ColumnSpec[];
80
+ readonly primaryKey: CreateTablePrimaryKey | undefined;
81
+ readonly label: string;
82
+
83
+ constructor(
84
+ schemaName: string,
85
+ tableName: string,
86
+ columns: readonly ColumnSpec[],
87
+ primaryKey?: CreateTablePrimaryKey,
88
+ ) {
89
+ super();
90
+ this.schemaName = schemaName;
91
+ this.tableName = tableName;
92
+ this.columns = columns;
93
+ this.primaryKey = primaryKey;
94
+ this.label = `Create table "${tableName}"`;
95
+ this.freeze();
96
+ }
97
+
98
+ toOp(): Op {
99
+ return createTable(this.schemaName, this.tableName, this.columns, this.primaryKey);
100
+ }
101
+
102
+ renderTypeScript(): string {
103
+ const args = [
104
+ jsonToTsSource(this.schemaName),
105
+ jsonToTsSource(this.tableName),
106
+ jsonToTsSource(this.columns),
107
+ ];
108
+ if (this.primaryKey) args.push(jsonToTsSource(this.primaryKey));
109
+ return `createTable(${args.join(', ')})`;
110
+ }
111
+ }
112
+
113
+ export class DropTableCall extends PostgresOpFactoryCallNode {
114
+ readonly factoryName = 'dropTable' as const;
115
+ readonly operationClass = 'destructive' as const;
116
+ readonly schemaName: string;
117
+ readonly tableName: string;
118
+ readonly label: string;
119
+
120
+ constructor(schemaName: string, tableName: string) {
121
+ super();
122
+ this.schemaName = schemaName;
123
+ this.tableName = tableName;
124
+ this.label = `Drop table "${tableName}"`;
125
+ this.freeze();
126
+ }
127
+
128
+ toOp(): Op {
129
+ return dropTable(this.schemaName, this.tableName);
130
+ }
131
+
132
+ renderTypeScript(): string {
133
+ return `dropTable(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)})`;
134
+ }
135
+ }
136
+
137
+ // ============================================================================
138
+ // Column
139
+ // ============================================================================
140
+
141
+ export class AddColumnCall extends PostgresOpFactoryCallNode {
142
+ readonly factoryName = 'addColumn' as const;
143
+ readonly operationClass = 'additive' as const;
144
+ readonly schemaName: string;
145
+ readonly tableName: string;
146
+ readonly column: ColumnSpec;
147
+ readonly label: string;
148
+
149
+ constructor(schemaName: string, tableName: string, column: ColumnSpec) {
150
+ super();
151
+ this.schemaName = schemaName;
152
+ this.tableName = tableName;
153
+ this.column = column;
154
+ this.label = `Add column "${column.name}" to "${tableName}"`;
155
+ this.freeze();
156
+ }
157
+
158
+ toOp(): Op {
159
+ return addColumn(this.schemaName, this.tableName, this.column);
160
+ }
161
+
162
+ renderTypeScript(): string {
163
+ return `addColumn(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.column)})`;
164
+ }
165
+ }
166
+
167
+ export class DropColumnCall extends PostgresOpFactoryCallNode {
168
+ readonly factoryName = 'dropColumn' as const;
169
+ readonly operationClass = 'destructive' as const;
170
+ readonly schemaName: string;
171
+ readonly tableName: string;
172
+ readonly columnName: string;
173
+ readonly label: string;
174
+
175
+ constructor(schemaName: string, tableName: string, columnName: string) {
176
+ super();
177
+ this.schemaName = schemaName;
178
+ this.tableName = tableName;
179
+ this.columnName = columnName;
180
+ this.label = `Drop column "${columnName}" from "${tableName}"`;
181
+ this.freeze();
182
+ }
183
+
184
+ toOp(): Op {
185
+ return dropColumn(this.schemaName, this.tableName, this.columnName);
186
+ }
187
+
188
+ renderTypeScript(): string {
189
+ return `dropColumn(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)})`;
190
+ }
191
+ }
192
+
193
+ export interface AlterColumnTypeOptions {
194
+ readonly qualifiedTargetType: string;
195
+ readonly formatTypeExpected: string;
196
+ readonly rawTargetTypeForLabel: string;
197
+ readonly using?: string;
198
+ }
199
+
200
+ export class AlterColumnTypeCall extends PostgresOpFactoryCallNode {
201
+ readonly factoryName = 'alterColumnType' as const;
202
+ readonly operationClass = 'destructive' as const;
203
+ readonly schemaName: string;
204
+ readonly tableName: string;
205
+ readonly columnName: string;
206
+ readonly options: AlterColumnTypeOptions;
207
+ readonly label: string;
208
+
209
+ constructor(
210
+ schemaName: string,
211
+ tableName: string,
212
+ columnName: string,
213
+ options: AlterColumnTypeOptions,
214
+ ) {
215
+ super();
216
+ this.schemaName = schemaName;
217
+ this.tableName = tableName;
218
+ this.columnName = columnName;
219
+ this.options = options;
220
+ this.label = `Alter type of "${tableName}"."${columnName}" to ${options.rawTargetTypeForLabel}`;
221
+ this.freeze();
222
+ }
223
+
224
+ toOp(): Op {
225
+ return alterColumnType(this.schemaName, this.tableName, this.columnName, this.options);
226
+ }
227
+
228
+ renderTypeScript(): string {
229
+ return `alterColumnType(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)}, ${jsonToTsSource(this.options)})`;
230
+ }
231
+ }
232
+
233
+ export class SetNotNullCall extends PostgresOpFactoryCallNode {
234
+ readonly factoryName = 'setNotNull' as const;
235
+ readonly operationClass = 'destructive' as const;
236
+ readonly schemaName: string;
237
+ readonly tableName: string;
238
+ readonly columnName: string;
239
+ readonly label: string;
240
+
241
+ constructor(schemaName: string, tableName: string, columnName: string) {
242
+ super();
243
+ this.schemaName = schemaName;
244
+ this.tableName = tableName;
245
+ this.columnName = columnName;
246
+ this.label = `Set NOT NULL on "${tableName}"."${columnName}"`;
247
+ this.freeze();
248
+ }
249
+
250
+ toOp(): Op {
251
+ return setNotNull(this.schemaName, this.tableName, this.columnName);
252
+ }
253
+
254
+ renderTypeScript(): string {
255
+ return `setNotNull(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)})`;
256
+ }
257
+ }
258
+
259
+ export class DropNotNullCall extends PostgresOpFactoryCallNode {
260
+ readonly factoryName = 'dropNotNull' as const;
261
+ readonly operationClass = 'widening' as const;
262
+ readonly schemaName: string;
263
+ readonly tableName: string;
264
+ readonly columnName: string;
265
+ readonly label: string;
266
+
267
+ constructor(schemaName: string, tableName: string, columnName: string) {
268
+ super();
269
+ this.schemaName = schemaName;
270
+ this.tableName = tableName;
271
+ this.columnName = columnName;
272
+ this.label = `Drop NOT NULL on "${tableName}"."${columnName}"`;
273
+ this.freeze();
274
+ }
275
+
276
+ toOp(): Op {
277
+ return dropNotNull(this.schemaName, this.tableName, this.columnName);
278
+ }
279
+
280
+ renderTypeScript(): string {
281
+ return `dropNotNull(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)})`;
282
+ }
283
+ }
284
+
285
+ export class SetDefaultCall extends PostgresOpFactoryCallNode {
286
+ readonly factoryName = 'setDefault' as const;
287
+ readonly operationClass: 'additive' | 'widening';
288
+ readonly schemaName: string;
289
+ readonly tableName: string;
290
+ readonly columnName: string;
291
+ readonly defaultSql: string;
292
+ readonly label: string;
293
+
294
+ constructor(
295
+ schemaName: string,
296
+ tableName: string,
297
+ columnName: string,
298
+ defaultSql: string,
299
+ operationClass: 'additive' | 'widening' = 'additive',
300
+ ) {
301
+ super();
302
+ this.schemaName = schemaName;
303
+ this.tableName = tableName;
304
+ this.columnName = columnName;
305
+ this.defaultSql = defaultSql;
306
+ this.operationClass = operationClass;
307
+ this.label = `Set default on "${tableName}"."${columnName}"`;
308
+ this.freeze();
309
+ }
310
+
311
+ toOp(): Op {
312
+ return setDefault(
313
+ this.schemaName,
314
+ this.tableName,
315
+ this.columnName,
316
+ this.defaultSql,
317
+ this.operationClass,
318
+ );
319
+ }
320
+
321
+ renderTypeScript(): string {
322
+ const args = [
323
+ jsonToTsSource(this.schemaName),
324
+ jsonToTsSource(this.tableName),
325
+ jsonToTsSource(this.columnName),
326
+ jsonToTsSource(this.defaultSql),
327
+ ];
328
+ if (this.operationClass !== 'additive') {
329
+ args.push(jsonToTsSource(this.operationClass));
330
+ }
331
+ return `setDefault(${args.join(', ')})`;
332
+ }
333
+ }
334
+
335
+ export class DropDefaultCall extends PostgresOpFactoryCallNode {
336
+ readonly factoryName = 'dropDefault' as const;
337
+ readonly operationClass = 'destructive' as const;
338
+ readonly schemaName: string;
339
+ readonly tableName: string;
340
+ readonly columnName: string;
341
+ readonly label: string;
342
+
343
+ constructor(schemaName: string, tableName: string, columnName: string) {
344
+ super();
345
+ this.schemaName = schemaName;
346
+ this.tableName = tableName;
347
+ this.columnName = columnName;
348
+ this.label = `Drop default on "${tableName}"."${columnName}"`;
349
+ this.freeze();
350
+ }
351
+
352
+ toOp(): Op {
353
+ return dropDefault(this.schemaName, this.tableName, this.columnName);
354
+ }
355
+
356
+ renderTypeScript(): string {
357
+ return `dropDefault(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.columnName)})`;
358
+ }
359
+ }
360
+
361
+ // ============================================================================
362
+ // Constraints
363
+ // ============================================================================
364
+
365
+ export class AddPrimaryKeyCall extends PostgresOpFactoryCallNode {
366
+ readonly factoryName = 'addPrimaryKey' as const;
367
+ readonly operationClass = 'additive' as const;
368
+ readonly schemaName: string;
369
+ readonly tableName: string;
370
+ readonly constraintName: string;
371
+ readonly columns: readonly string[];
372
+ readonly label: string;
373
+
374
+ constructor(
375
+ schemaName: string,
376
+ tableName: string,
377
+ constraintName: string,
378
+ columns: readonly string[],
379
+ ) {
380
+ super();
381
+ this.schemaName = schemaName;
382
+ this.tableName = tableName;
383
+ this.constraintName = constraintName;
384
+ this.columns = columns;
385
+ this.label = `Add primary key on "${tableName}"`;
386
+ this.freeze();
387
+ }
388
+
389
+ toOp(): Op {
390
+ return addPrimaryKey(this.schemaName, this.tableName, this.constraintName, this.columns);
391
+ }
392
+
393
+ renderTypeScript(): string {
394
+ return `addPrimaryKey(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.constraintName)}, ${jsonToTsSource(this.columns)})`;
395
+ }
396
+ }
397
+
398
+ export class AddUniqueCall extends PostgresOpFactoryCallNode {
399
+ readonly factoryName = 'addUnique' as const;
400
+ readonly operationClass = 'additive' as const;
401
+ readonly schemaName: string;
402
+ readonly tableName: string;
403
+ readonly constraintName: string;
404
+ readonly columns: readonly string[];
405
+ readonly label: string;
406
+
407
+ constructor(
408
+ schemaName: string,
409
+ tableName: string,
410
+ constraintName: string,
411
+ columns: readonly string[],
412
+ ) {
413
+ super();
414
+ this.schemaName = schemaName;
415
+ this.tableName = tableName;
416
+ this.constraintName = constraintName;
417
+ this.columns = columns;
418
+ this.label = `Add unique constraint on "${tableName}" (${columns.join(', ')})`;
419
+ this.freeze();
420
+ }
421
+
422
+ toOp(): Op {
423
+ return addUnique(this.schemaName, this.tableName, this.constraintName, this.columns);
424
+ }
425
+
426
+ renderTypeScript(): string {
427
+ return `addUnique(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.constraintName)}, ${jsonToTsSource(this.columns)})`;
428
+ }
429
+ }
430
+
431
+ export class AddForeignKeyCall extends PostgresOpFactoryCallNode {
432
+ readonly factoryName = 'addForeignKey' as const;
433
+ readonly operationClass = 'additive' as const;
434
+ readonly schemaName: string;
435
+ readonly tableName: string;
436
+ readonly fk: ForeignKeySpec;
437
+ readonly label: string;
438
+
439
+ constructor(schemaName: string, tableName: string, fk: ForeignKeySpec) {
440
+ super();
441
+ this.schemaName = schemaName;
442
+ this.tableName = tableName;
443
+ this.fk = fk;
444
+ this.label = `Add foreign key "${fk.name}" on "${tableName}"`;
445
+ this.freeze();
446
+ }
447
+
448
+ toOp(): Op {
449
+ return addForeignKey(this.schemaName, this.tableName, this.fk);
450
+ }
451
+
452
+ renderTypeScript(): string {
453
+ return `addForeignKey(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.fk)})`;
454
+ }
455
+ }
456
+
457
+ export class DropConstraintCall extends PostgresOpFactoryCallNode {
458
+ readonly factoryName = 'dropConstraint' as const;
459
+ readonly operationClass = 'destructive' as const;
460
+ readonly schemaName: string;
461
+ readonly tableName: string;
462
+ readonly constraintName: string;
463
+ readonly kind: 'foreignKey' | 'unique' | 'primaryKey';
464
+ readonly label: string;
465
+
466
+ constructor(
467
+ schemaName: string,
468
+ tableName: string,
469
+ constraintName: string,
470
+ kind: 'foreignKey' | 'unique' | 'primaryKey' = 'unique',
471
+ ) {
472
+ super();
473
+ this.schemaName = schemaName;
474
+ this.tableName = tableName;
475
+ this.constraintName = constraintName;
476
+ this.kind = kind;
477
+ this.label = `Drop constraint "${constraintName}" on "${tableName}"`;
478
+ this.freeze();
479
+ }
480
+
481
+ toOp(): Op {
482
+ return dropConstraint(this.schemaName, this.tableName, this.constraintName, this.kind);
483
+ }
484
+
485
+ renderTypeScript(): string {
486
+ const args = [
487
+ jsonToTsSource(this.schemaName),
488
+ jsonToTsSource(this.tableName),
489
+ jsonToTsSource(this.constraintName),
490
+ ];
491
+ if (this.kind !== 'unique') {
492
+ args.push(jsonToTsSource(this.kind));
493
+ }
494
+ return `dropConstraint(${args.join(', ')})`;
495
+ }
496
+ }
497
+
498
+ // ============================================================================
499
+ // Indexes
500
+ // ============================================================================
501
+
502
+ export class CreateIndexCall extends PostgresOpFactoryCallNode {
503
+ readonly factoryName = 'createIndex' as const;
504
+ readonly operationClass = 'additive' as const;
505
+ readonly schemaName: string;
506
+ readonly tableName: string;
507
+ readonly indexName: string;
508
+ readonly columns: readonly string[];
509
+ readonly label: string;
510
+
511
+ constructor(
512
+ schemaName: string,
513
+ tableName: string,
514
+ indexName: string,
515
+ columns: readonly string[],
516
+ ) {
517
+ super();
518
+ this.schemaName = schemaName;
519
+ this.tableName = tableName;
520
+ this.indexName = indexName;
521
+ this.columns = columns;
522
+ this.label = `Create index "${indexName}" on "${tableName}"`;
523
+ this.freeze();
524
+ }
525
+
526
+ toOp(): Op {
527
+ return createIndex(this.schemaName, this.tableName, this.indexName, this.columns);
528
+ }
529
+
530
+ renderTypeScript(): string {
531
+ return `createIndex(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.indexName)}, ${jsonToTsSource(this.columns)})`;
532
+ }
533
+ }
534
+
535
+ export class DropIndexCall extends PostgresOpFactoryCallNode {
536
+ readonly factoryName = 'dropIndex' as const;
537
+ readonly operationClass = 'destructive' as const;
538
+ readonly schemaName: string;
539
+ readonly tableName: string;
540
+ readonly indexName: string;
541
+ readonly label: string;
542
+
543
+ constructor(schemaName: string, tableName: string, indexName: string) {
544
+ super();
545
+ this.schemaName = schemaName;
546
+ this.tableName = tableName;
547
+ this.indexName = indexName;
548
+ this.label = `Drop index "${indexName}"`;
549
+ this.freeze();
550
+ }
551
+
552
+ toOp(): Op {
553
+ return dropIndex(this.schemaName, this.tableName, this.indexName);
554
+ }
555
+
556
+ renderTypeScript(): string {
557
+ return `dropIndex(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.indexName)})`;
558
+ }
559
+ }
560
+
561
+ // ============================================================================
562
+ // Enum types
563
+ // ============================================================================
564
+
565
+ export class CreateEnumTypeCall extends PostgresOpFactoryCallNode {
566
+ readonly factoryName = 'createEnumType' as const;
567
+ readonly operationClass = 'additive' as const;
568
+ readonly schemaName: string;
569
+ readonly typeName: string;
570
+ readonly values: readonly string[];
571
+ readonly label: string;
572
+
573
+ constructor(schemaName: string, typeName: string, values: readonly string[]) {
574
+ super();
575
+ this.schemaName = schemaName;
576
+ this.typeName = typeName;
577
+ this.values = values;
578
+ this.label = `Create enum type "${typeName}"`;
579
+ this.freeze();
580
+ }
581
+
582
+ toOp(): Op {
583
+ return createEnumType(this.schemaName, this.typeName, this.values);
584
+ }
585
+
586
+ renderTypeScript(): string {
587
+ return `createEnumType(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.typeName)}, ${jsonToTsSource(this.values)})`;
588
+ }
589
+ }
590
+
591
+ export class AddEnumValuesCall extends PostgresOpFactoryCallNode {
592
+ readonly factoryName = 'addEnumValues' as const;
593
+ readonly operationClass = 'additive' as const;
594
+ readonly schemaName: string;
595
+ readonly typeName: string;
596
+ readonly nativeType: string;
597
+ readonly values: readonly string[];
598
+ readonly label: string;
599
+
600
+ constructor(schemaName: string, typeName: string, nativeType: string, values: readonly string[]) {
601
+ super();
602
+ this.schemaName = schemaName;
603
+ this.typeName = typeName;
604
+ this.nativeType = nativeType;
605
+ this.values = values;
606
+ this.label = `Add values to enum type "${typeName}": ${values.join(', ')}`;
607
+ this.freeze();
608
+ }
609
+
610
+ toOp(): Op {
611
+ return addEnumValues(this.schemaName, this.typeName, this.nativeType, this.values);
612
+ }
613
+
614
+ renderTypeScript(): string {
615
+ return `addEnumValues(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.typeName)}, ${jsonToTsSource(this.nativeType)}, ${jsonToTsSource(this.values)})`;
616
+ }
617
+ }
618
+
619
+ export class DropEnumTypeCall extends PostgresOpFactoryCallNode {
620
+ readonly factoryName = 'dropEnumType' as const;
621
+ readonly operationClass = 'destructive' as const;
622
+ readonly schemaName: string;
623
+ readonly typeName: string;
624
+ readonly label: string;
625
+
626
+ constructor(schemaName: string, typeName: string) {
627
+ super();
628
+ this.schemaName = schemaName;
629
+ this.typeName = typeName;
630
+ this.label = `Drop enum type "${typeName}"`;
631
+ this.freeze();
632
+ }
633
+
634
+ toOp(): Op {
635
+ return dropEnumType(this.schemaName, this.typeName);
636
+ }
637
+
638
+ renderTypeScript(): string {
639
+ return `dropEnumType(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.typeName)})`;
640
+ }
641
+ }
642
+
643
+ export class RenameTypeCall extends PostgresOpFactoryCallNode {
644
+ readonly factoryName = 'renameType' as const;
645
+ readonly operationClass = 'destructive' as const;
646
+ readonly schemaName: string;
647
+ readonly fromName: string;
648
+ readonly toName: string;
649
+ readonly label: string;
650
+
651
+ constructor(schemaName: string, fromName: string, toName: string) {
652
+ super();
653
+ this.schemaName = schemaName;
654
+ this.fromName = fromName;
655
+ this.toName = toName;
656
+ this.label = `Rename type "${fromName}" to "${toName}"`;
657
+ this.freeze();
658
+ }
659
+
660
+ toOp(): Op {
661
+ return renameType(this.schemaName, this.fromName, this.toName);
662
+ }
663
+
664
+ renderTypeScript(): string {
665
+ return `renameType(${jsonToTsSource(this.schemaName)}, ${jsonToTsSource(this.fromName)}, ${jsonToTsSource(this.toName)})`;
666
+ }
667
+ }
668
+
669
+ // ============================================================================
670
+ // Raw SQL
671
+ // ============================================================================
672
+
673
+ /**
674
+ * Laundered pre-built operation.
675
+ *
676
+ * Wraps an already-materialized `SqlMigrationPlanOperation` — typically one
677
+ * produced by a SQL-family method, a codec control hook, or a component
678
+ * `databaseDependencies.init` declaration — so the planner can carry it
679
+ * alongside IR nodes without reverse-engineering it into a
680
+ * structured call class. Doubles as the user-facing escape hatch for raw
681
+ * migrations: authors can pass a full op shape to `rawSql({...})`.
682
+ *
683
+ * `toOp()` returns the stored op unchanged. `renderTypeScript()` emits
684
+ * `rawSql({...})` with the op serialized as a JSON literal — round-tripping
685
+ * requires every field on the op to be JSON-serializable (no closures).
686
+ */
687
+ export class RawSqlCall extends PostgresOpFactoryCallNode {
688
+ readonly factoryName = 'rawSql' as const;
689
+ readonly operationClass: MigrationOperationClass;
690
+ readonly label: string;
691
+ readonly op: Op;
692
+
693
+ constructor(op: Op) {
694
+ super();
695
+ this.op = op;
696
+ this.label = op.label;
697
+ this.operationClass = op.operationClass;
698
+ this.freeze();
699
+ }
700
+
701
+ toOp(): Op {
702
+ return this.op;
703
+ }
704
+
705
+ renderTypeScript(): string {
706
+ return `rawSql(${jsonToTsSource(this.op)})`;
707
+ }
708
+ }
709
+
710
+ // ============================================================================
711
+ // Database dependencies (structured DDL)
712
+ // ============================================================================
713
+
714
+ export class CreateExtensionCall extends PostgresOpFactoryCallNode {
715
+ readonly factoryName = 'createExtension' as const;
716
+ readonly operationClass = 'additive' as const;
717
+ readonly extensionName: string;
718
+ readonly label: string;
719
+
720
+ constructor(extensionName: string) {
721
+ super();
722
+ this.extensionName = extensionName;
723
+ this.label = `Create extension "${extensionName}"`;
724
+ this.freeze();
725
+ }
726
+
727
+ toOp(): Op {
728
+ return createExtension(this.extensionName);
729
+ }
730
+
731
+ renderTypeScript(): string {
732
+ return `createExtension(${jsonToTsSource(this.extensionName)})`;
733
+ }
734
+ }
735
+
736
+ export class CreateSchemaCall extends PostgresOpFactoryCallNode {
737
+ readonly factoryName = 'createSchema' as const;
738
+ readonly operationClass = 'additive' as const;
739
+ readonly schemaName: string;
740
+ readonly label: string;
741
+
742
+ constructor(schemaName: string) {
743
+ super();
744
+ this.schemaName = schemaName;
745
+ this.label = `Create schema "${schemaName}"`;
746
+ this.freeze();
747
+ }
748
+
749
+ toOp(): Op {
750
+ return createSchema(this.schemaName);
751
+ }
752
+
753
+ renderTypeScript(): string {
754
+ return `createSchema(${jsonToTsSource(this.schemaName)})`;
755
+ }
756
+ }
757
+
758
+ // ============================================================================
759
+ // Data transform
760
+ // ============================================================================
761
+
762
+ /**
763
+ * A planner-generated data-transform stub. `checkSlot` and `runSlot` name
764
+ * the unfilled authoring slots that the rendered `migration.ts` will expose
765
+ * to the user via `placeholder("…")` calls. `toOp()` always throws
766
+ * `PN-MIG-2001`: the planner cannot lower a stubbed transform to a runtime
767
+ * op — the user must fill the rendered `migration.ts` and re-emit.
768
+ */
769
+ export class DataTransformCall extends PostgresOpFactoryCallNode {
770
+ readonly factoryName = 'dataTransform' as const;
771
+ readonly operationClass: MigrationOperationClass;
772
+ readonly label: string;
773
+ readonly checkSlot: string;
774
+ readonly runSlot: string;
775
+
776
+ constructor(
777
+ label: string,
778
+ checkSlot: string,
779
+ runSlot: string,
780
+ operationClass: MigrationOperationClass = 'data',
781
+ ) {
782
+ super();
783
+ this.label = label;
784
+ this.checkSlot = checkSlot;
785
+ this.runSlot = runSlot;
786
+ this.operationClass = operationClass;
787
+ this.freeze();
788
+ }
789
+
790
+ toOp(): Op {
791
+ throw errorUnfilledPlaceholder(this.label);
792
+ }
793
+
794
+ renderTypeScript(): string {
795
+ return [
796
+ `dataTransform(endContract, ${jsonToTsSource(this.label)}, {`,
797
+ ` check: () => placeholder(${jsonToTsSource(this.checkSlot)}),`,
798
+ ` run: () => placeholder(${jsonToTsSource(this.runSlot)}),`,
799
+ '})',
800
+ ].join('\n');
801
+ }
802
+
803
+ override importRequirements(): readonly ImportRequirement[] {
804
+ return [
805
+ { moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: this.factoryName },
806
+ // `placeholder` is re-exported from `@prisma-next/target-postgres/migration`
807
+ // so the user's migration.ts only depends on a single migration-authoring
808
+ // entrypoint.
809
+ { moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: 'placeholder' },
810
+ {
811
+ moduleSpecifier: './end-contract.json',
812
+ symbol: 'endContract',
813
+ kind: 'default',
814
+ attributes: { type: 'json' },
815
+ },
816
+ ];
817
+ }
818
+ }
819
+
820
+ export type PostgresOpFactoryCall =
821
+ | CreateTableCall
822
+ | DropTableCall
823
+ | AddColumnCall
824
+ | DropColumnCall
825
+ | AlterColumnTypeCall
826
+ | SetNotNullCall
827
+ | DropNotNullCall
828
+ | SetDefaultCall
829
+ | DropDefaultCall
830
+ | AddPrimaryKeyCall
831
+ | AddForeignKeyCall
832
+ | AddUniqueCall
833
+ | CreateIndexCall
834
+ | DropIndexCall
835
+ | DropConstraintCall
836
+ | CreateEnumTypeCall
837
+ | AddEnumValuesCall
838
+ | DropEnumTypeCall
839
+ | RenameTypeCall
840
+ | RawSqlCall
841
+ | CreateExtensionCall
842
+ | CreateSchemaCall
843
+ | DataTransformCall;
844
+
845
+ /**
846
+ * Stable identity key for reconciliation-level dedup.
847
+ *
848
+ * Two calls whose runtime ops would share the same `id` return the same
849
+ * key, so a `Set<string>` can collapse them before they're emitted. The
850
+ * current implementation delegates to `toOp().id`, which is the
851
+ * authoritative identity; isolating dedup behind this helper lets a future
852
+ * pass replace it with an allocation-free computation directly from the
853
+ * call's fields without touching call sites.
854
+ *
855
+ * `DataTransformCall` intentionally has no sensible identity today — it
856
+ * throws `PN-MIG-2001` on `toOp()`. Reconciliation never produces one; the
857
+ * helper is unspecified for that variant and only meant for
858
+ * reconciliation-emitted calls.
859
+ */
860
+ export function identityKeyFor(call: PostgresOpFactoryCall): string {
861
+ return call.toOp().id;
862
+ }