@prisma-next/target-postgres 0.5.0-dev.7 → 0.5.0-dev.70

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 (177) hide show
  1. package/dist/{codec-ids-CojIXVf9.mjs → codec-ids-C5qzBqus.mjs} +4 -4
  2. package/dist/{codec-ids-CojIXVf9.mjs.map → codec-ids-C5qzBqus.mjs.map} +1 -1
  3. package/dist/codec-ids-CplrEfmx.d.mts +29 -0
  4. package/dist/codec-ids-CplrEfmx.d.mts.map +1 -0
  5. package/dist/codec-ids.d.mts +2 -28
  6. package/dist/codec-ids.mjs +2 -3
  7. package/dist/codec-types-lrsb3N07.d.mts +79 -0
  8. package/dist/codec-types-lrsb3N07.d.mts.map +1 -0
  9. package/dist/codec-types.d.mts +2 -42
  10. package/dist/codec-types.mjs +1 -3
  11. package/dist/codecs-Cue97Xqf.d.mts +558 -0
  12. package/dist/codecs-Cue97Xqf.d.mts.map +1 -0
  13. package/dist/codecs.d.mts +13 -2
  14. package/dist/codecs.d.mts.map +1 -0
  15. package/dist/codecs.mjs +738 -2
  16. package/dist/codecs.mjs.map +1 -0
  17. package/dist/control.d.mts +1 -1
  18. package/dist/control.d.mts.map +1 -1
  19. package/dist/control.mjs +131 -94
  20. package/dist/control.mjs.map +1 -1
  21. package/dist/{data-transform-VfEGzXWt.mjs → data-transform-DKWXdHuZ.mjs} +25 -7
  22. package/dist/data-transform-DKWXdHuZ.mjs.map +1 -0
  23. package/dist/data-transform-bIeAcZIJ.d.mts +38 -0
  24. package/dist/data-transform-bIeAcZIJ.d.mts.map +1 -0
  25. package/dist/data-transform.d.mts +1 -1
  26. package/dist/data-transform.mjs +2 -3
  27. package/dist/{default-normalizer-DNOpRoOF.mjs → default-normalizer-C8XyZj85.mjs} +2 -2
  28. package/dist/{default-normalizer-DNOpRoOF.mjs.map → default-normalizer-C8XyZj85.mjs.map} +1 -1
  29. package/dist/default-normalizer.d.mts +0 -1
  30. package/dist/default-normalizer.d.mts.map +1 -1
  31. package/dist/default-normalizer.mjs +2 -3
  32. package/dist/descriptor-meta-Dde_BS3K.mjs +99 -0
  33. package/dist/descriptor-meta-Dde_BS3K.mjs.map +1 -0
  34. package/dist/{errors-AFvEPZ1R.mjs → errors-Chm2bKcS.mjs} +2 -3
  35. package/dist/{errors-AFvEPZ1R.mjs.map → errors-Chm2bKcS.mjs.map} +1 -1
  36. package/dist/errors.d.mts +0 -1
  37. package/dist/errors.d.mts.map +1 -1
  38. package/dist/errors.mjs +2 -3
  39. package/dist/{issue-planner-CFjB0_oO.mjs → issue-planner-B10B70JF.mjs} +9 -13
  40. package/dist/issue-planner-B10B70JF.mjs.map +1 -0
  41. package/dist/issue-planner.d.mts +2 -4
  42. package/dist/issue-planner.d.mts.map +1 -1
  43. package/dist/issue-planner.mjs +2 -3
  44. package/dist/migration.d.mts +3 -3
  45. package/dist/migration.d.mts.map +1 -1
  46. package/dist/migration.mjs +4 -5
  47. package/dist/migration.mjs.map +1 -1
  48. package/dist/{native-type-normalizer-CInai_oY.mjs → native-type-normalizer-Cry4QoLf.mjs} +2 -2
  49. package/dist/native-type-normalizer-Cry4QoLf.mjs.map +1 -0
  50. package/dist/native-type-normalizer.d.mts.map +1 -1
  51. package/dist/native-type-normalizer.mjs +2 -3
  52. package/dist/{op-factory-call-C3bWXKSP.d.mts → op-factory-call-CW8pzxmB.d.mts} +3 -4
  53. package/dist/op-factory-call-CW8pzxmB.d.mts.map +1 -0
  54. package/dist/{op-factory-call-BKlruaiC.mjs → op-factory-call-Cq8s4Fz1.mjs} +3 -4
  55. package/dist/{op-factory-call-BKlruaiC.mjs.map → op-factory-call-Cq8s4Fz1.mjs.map} +1 -1
  56. package/dist/op-factory-call.d.mts +1 -2
  57. package/dist/op-factory-call.mjs +2 -3
  58. package/dist/pack.d.mts +28 -9
  59. package/dist/pack.d.mts.map +1 -1
  60. package/dist/pack.mjs +2 -3
  61. package/dist/{planner-CLUvVhUN.mjs → planner-JmqeNfGa.mjs} +23 -16
  62. package/dist/planner-JmqeNfGa.mjs.map +1 -0
  63. package/dist/{planner-ddl-builders-Dxvw1LHw.mjs → planner-ddl-builders-CLB7Umhh.mjs} +4 -5
  64. package/dist/planner-ddl-builders-CLB7Umhh.mjs.map +1 -0
  65. package/dist/planner-ddl-builders.d.mts +1 -1
  66. package/dist/planner-ddl-builders.d.mts.map +1 -1
  67. package/dist/planner-ddl-builders.mjs +2 -3
  68. package/dist/{planner-identity-values-Dju-o5GF.mjs → planner-identity-values-DTx0gePL.mjs} +2 -3
  69. package/dist/{planner-identity-values-Dju-o5GF.mjs.map → planner-identity-values-DTx0gePL.mjs.map} +1 -1
  70. package/dist/planner-identity-values.d.mts +0 -1
  71. package/dist/planner-identity-values.d.mts.map +1 -1
  72. package/dist/planner-identity-values.mjs +2 -3
  73. package/dist/{planner-produced-postgres-migration-DSSPq8QS.mjs → planner-produced-postgres-migration-Br3NgwpH.mjs} +16 -8
  74. package/dist/planner-produced-postgres-migration-Br3NgwpH.mjs.map +1 -0
  75. package/dist/{planner-produced-postgres-migration-CRRTno6Z.d.mts → planner-produced-postgres-migration-D7Pe160c.d.mts} +11 -6
  76. package/dist/planner-produced-postgres-migration-D7Pe160c.d.mts.map +1 -0
  77. package/dist/planner-produced-postgres-migration.d.mts +1 -4
  78. package/dist/planner-produced-postgres-migration.mjs +2 -3
  79. package/dist/{planner-schema-lookup-B7lkypwn.mjs → planner-schema-lookup-B1ags8ys.mjs} +2 -2
  80. package/dist/{planner-schema-lookup-B7lkypwn.mjs.map → planner-schema-lookup-B1ags8ys.mjs.map} +1 -1
  81. package/dist/planner-schema-lookup.d.mts +0 -1
  82. package/dist/planner-schema-lookup.d.mts.map +1 -1
  83. package/dist/planner-schema-lookup.mjs +2 -3
  84. package/dist/{planner-sql-checks-7jkgm9TX.mjs → planner-sql-checks-DwZvGlV4.mjs} +3 -5
  85. package/dist/planner-sql-checks-DwZvGlV4.mjs.map +1 -0
  86. package/dist/planner-sql-checks.d.mts.map +1 -1
  87. package/dist/planner-sql-checks.mjs +2 -3
  88. package/dist/{planner-target-details-DH-azLu-.d.mts → planner-target-details-bVVcanWh.d.mts} +1 -1
  89. package/dist/planner-target-details-bVVcanWh.d.mts.map +1 -0
  90. package/dist/planner-target-details.d.mts +1 -1
  91. package/dist/planner-target-details.mjs +1 -1
  92. package/dist/planner.d.mts +21 -12
  93. package/dist/planner.d.mts.map +1 -1
  94. package/dist/planner.mjs +2 -4
  95. package/dist/{postgres-migration-qtmtbONe.mjs → postgres-migration-Bkv140RW.mjs} +4 -5
  96. package/dist/postgres-migration-Bkv140RW.mjs.map +1 -0
  97. package/dist/{postgres-migration-BjA3Zmts.d.mts → postgres-migration-UkcHfZAA.d.mts} +6 -6
  98. package/dist/postgres-migration-UkcHfZAA.d.mts.map +1 -0
  99. package/dist/{render-ops-D6_DHdOK.mjs → render-ops-DXxV-PbF.mjs} +2 -2
  100. package/dist/{render-ops-D6_DHdOK.mjs.map → render-ops-DXxV-PbF.mjs.map} +1 -1
  101. package/dist/render-ops.d.mts +2 -3
  102. package/dist/render-ops.d.mts.map +1 -1
  103. package/dist/render-ops.mjs +2 -3
  104. package/dist/{render-typescript-1rF_SB4g.mjs → render-typescript-pyz96rAY.mjs} +2 -4
  105. package/dist/render-typescript-pyz96rAY.mjs.map +1 -0
  106. package/dist/render-typescript.d.mts +2 -5
  107. package/dist/render-typescript.d.mts.map +1 -1
  108. package/dist/render-typescript.mjs +2 -3
  109. package/dist/runtime.d.mts +5 -9
  110. package/dist/runtime.d.mts.map +1 -1
  111. package/dist/runtime.mjs +7 -14
  112. package/dist/runtime.mjs.map +1 -1
  113. package/dist/{shared-Bxkt8pNO.d.mts → shared-MpwjwAjM.d.mts} +2 -2
  114. package/dist/shared-MpwjwAjM.d.mts.map +1 -0
  115. package/dist/{sql-utils-r-Lw535w.mjs → sql-utils-CggjWNij.mjs} +4 -2
  116. package/dist/sql-utils-CggjWNij.mjs.map +1 -0
  117. package/dist/sql-utils.d.mts.map +1 -1
  118. package/dist/sql-utils.mjs +2 -3
  119. package/dist/{statement-builders-BPnmt6wx.mjs → statement-builders-BT889jV0.mjs} +28 -13
  120. package/dist/statement-builders-BT889jV0.mjs.map +1 -0
  121. package/dist/statement-builders.d.mts +31 -3
  122. package/dist/statement-builders.d.mts.map +1 -1
  123. package/dist/statement-builders.mjs +2 -3
  124. package/dist/{tables-BmdW_FWO.mjs → tables-Ej122-iI.mjs} +4 -11
  125. package/dist/tables-Ej122-iI.mjs.map +1 -0
  126. package/dist/{types-ClK03Ojd.d.mts → types-CTqpysRY.d.mts} +1 -1
  127. package/dist/types-CTqpysRY.d.mts.map +1 -0
  128. package/dist/types.d.mts +1 -1
  129. package/dist/types.mjs +1 -1
  130. package/package.json +21 -19
  131. package/src/core/authoring.ts +5 -11
  132. package/src/core/codec-helpers.ts +135 -0
  133. package/src/core/codec-ids.ts +1 -0
  134. package/src/core/codec-type-map.ts +81 -0
  135. package/src/core/codecs.ts +941 -547
  136. package/src/core/descriptor-meta.ts +1 -1
  137. package/src/core/migrations/operations/data-transform.ts +86 -21
  138. package/src/core/migrations/planner-produced-postgres-migration.ts +12 -2
  139. package/src/core/migrations/planner.ts +71 -20
  140. package/src/core/migrations/postgres-migration.ts +3 -6
  141. package/src/core/migrations/render-typescript.ts +1 -5
  142. package/src/core/migrations/runner.ts +172 -151
  143. package/src/core/migrations/statement-builders.ts +49 -10
  144. package/src/core/registry.ts +11 -0
  145. package/src/exports/codec-types.ts +4 -13
  146. package/src/exports/codecs.ts +49 -2
  147. package/src/exports/runtime.ts +6 -11
  148. package/src/exports/statement-builders.ts +2 -1
  149. package/dist/codec-ids.d.mts.map +0 -1
  150. package/dist/codec-types.d.mts.map +0 -1
  151. package/dist/codecs-BoahtY_Q.mjs +0 -385
  152. package/dist/codecs-BoahtY_Q.mjs.map +0 -1
  153. package/dist/codecs-D-F2KJqt.d.mts +0 -299
  154. package/dist/codecs-D-F2KJqt.d.mts.map +0 -1
  155. package/dist/data-transform-CxFRBIUp.d.mts +0 -32
  156. package/dist/data-transform-CxFRBIUp.d.mts.map +0 -1
  157. package/dist/data-transform-VfEGzXWt.mjs.map +0 -1
  158. package/dist/descriptor-meta-BVoVtyp-.mjs +0 -120
  159. package/dist/descriptor-meta-BVoVtyp-.mjs.map +0 -1
  160. package/dist/issue-planner-CFjB0_oO.mjs.map +0 -1
  161. package/dist/native-type-normalizer-CInai_oY.mjs.map +0 -1
  162. package/dist/op-factory-call-C3bWXKSP.d.mts.map +0 -1
  163. package/dist/planner-CLUvVhUN.mjs.map +0 -1
  164. package/dist/planner-ddl-builders-Dxvw1LHw.mjs.map +0 -1
  165. package/dist/planner-produced-postgres-migration-CRRTno6Z.d.mts.map +0 -1
  166. package/dist/planner-produced-postgres-migration-DSSPq8QS.mjs.map +0 -1
  167. package/dist/planner-sql-checks-7jkgm9TX.mjs.map +0 -1
  168. package/dist/planner-target-details-DH-azLu-.d.mts.map +0 -1
  169. package/dist/postgres-migration-BjA3Zmts.d.mts.map +0 -1
  170. package/dist/postgres-migration-qtmtbONe.mjs.map +0 -1
  171. package/dist/render-typescript-1rF_SB4g.mjs.map +0 -1
  172. package/dist/shared-Bxkt8pNO.d.mts.map +0 -1
  173. package/dist/sql-utils-r-Lw535w.mjs.map +0 -1
  174. package/dist/statement-builders-BPnmt6wx.mjs.map +0 -1
  175. package/dist/tables-BmdW_FWO.mjs.map +0 -1
  176. package/dist/types-ClK03Ojd.d.mts.map +0 -1
  177. package/src/core/json-schema-type-expression.ts +0 -131
@@ -1,5 +1,5 @@
1
+ import type { CodecTypes } from '../exports/codec-types';
1
2
  import { postgresAuthoringFieldPresets, postgresAuthoringTypes } from './authoring';
2
- import type { CodecTypes } from './codecs';
3
3
 
4
4
  const postgresTargetDescriptorMetaBase = {
5
5
  kind: 'target',
@@ -11,7 +11,7 @@
11
11
  * override get operations() {
12
12
  * return [
13
13
  * this.dataTransform(endContract, 'backfill emails', {
14
- * check: () => db.users.count().where(({ email }) => email.isNull()),
14
+ * check: () => db.users.select('id').where(({ email }) => email.isNull()).limit(1),
15
15
  * run: () => db.users.update({ email: '' }).where(({ email }) => email.isNull()),
16
16
  * }),
17
17
  * ];
@@ -23,20 +23,49 @@
23
23
  * invokes each one, asserts that its `meta.storageHash` matches the
24
24
  * `contract` it was handed (→ `PN-MIG-2005` on mismatch), and lowers the
25
25
  * plan via the supplied control adapter to a serialized `{sql, params}`
26
- * payload for `ops.json`. The free factory remains usable standalone
27
- * (tests, ad-hoc tooling, non-class contexts) by passing the adapter
28
- * explicitly as the fourth argument.
26
+ * payload.
27
+ *
28
+ * The factory then lowers the data transform to the unified migration-op
29
+ * shape `{ precheck, execute, postcheck }`. The user's `check` plan is
30
+ * wrapped twice with opposite truth values:
31
+ *
32
+ * - precheck `SELECT EXISTS (<check>) AS ok` asserts there is work to do
33
+ * (precheck is short-circuited by the runner's pre-satisfied-skip path
34
+ * when nothing remains to backfill).
35
+ * - postcheck `SELECT NOT EXISTS (<check>) AS ok` asserts the work is
36
+ * complete after the run steps execute.
37
+ *
38
+ * The `check` plan is therefore expected to be a **rowset query whose
39
+ * presence of any row signals "work remains"** — typically `select('id')
40
+ * .where(<violation predicate>).limit(1)`. Scalar/aggregate shapes
41
+ * (`count(*)`, `bool_and(...)`) do not work under this contract: they
42
+ * always return exactly one row, so `EXISTS` is always true and
43
+ * `NOT EXISTS` is always false. (This is the same row-presence contract
44
+ * the pre-unification runner relied on; the wrapping is just lifting it
45
+ * into SQL.)
46
+ *
47
+ * Each `run` plan becomes an execute step. Because the `Step.params`
48
+ * field threads through `driver.query(sql, params)`, the user's bound
49
+ * values flow through the driver's parameter binder rather than being
50
+ * inlined into the SQL text.
51
+ *
52
+ * The free factory remains usable standalone (tests, ad-hoc tooling,
53
+ * non-class contexts) by passing the adapter explicitly as the fourth
54
+ * argument.
29
55
  */
30
56
 
31
57
  import type { Contract } from '@prisma-next/contract/types';
32
58
  import { errorDataTransformContractMismatch } from '@prisma-next/errors/migration';
33
- import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
34
59
  import type {
35
- DataTransformOperation,
36
- SerializedQueryPlan,
37
- } from '@prisma-next/framework-components/control';
60
+ SqlMigrationPlanOperation,
61
+ SqlMigrationPlanOperationStep,
62
+ } from '@prisma-next/family-sql/control';
63
+ import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
64
+ import type { SerializedQueryPlan } from '@prisma-next/framework-components/control';
38
65
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
39
66
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
67
+ import { ifDefined } from '@prisma-next/utils/defined';
68
+ import type { PostgresPlanTargetDetails } from '../planner-target-details';
40
69
 
41
70
  interface Buildable<R = unknown> {
42
71
  build(): SqlQueryPlan<R>;
@@ -49,36 +78,72 @@ interface Buildable<R = unknown> {
49
78
  export type DataTransformClosure = () => SqlQueryPlan | Buildable;
50
79
 
51
80
  export interface DataTransformOptions {
52
- /** Optional pre-flight query. `undefined` means "no check". */
81
+ /**
82
+ * Optional opt-in routing identity. Presence opts the transform into
83
+ * invariant-aware routing; absence means it is path-dependent and
84
+ * not referenceable from refs.
85
+ */
86
+ readonly invariantId?: string;
87
+ /**
88
+ * Optional pre-flight query. `undefined` means "no check". When
89
+ * supplied, the closure must return a **rowset query** whose
90
+ * presence of any row signals "violations remain". Conventional
91
+ * shape: `db.<table>.select('id').where(<violation>).limit(1)`.
92
+ * Scalar/aggregate shapes do not satisfy this contract.
93
+ */
53
94
  readonly check?: DataTransformClosure;
54
95
  /** One or more mutation queries to execute. */
55
96
  readonly run: DataTransformClosure | readonly DataTransformClosure[];
56
97
  }
57
98
 
58
- /**
59
- * Concrete Postgres flavor of `DataTransformOperation`, re-exported so the
60
- * `PostgresMigration.dataTransform` instance method can name it without
61
- * leaking the framework-components symbol into call sites.
62
- */
63
- export type PostgresDataTransformOperation = DataTransformOperation;
64
-
65
99
  export function dataTransform<TContract extends Contract<SqlStorage>>(
66
100
  contract: TContract,
67
101
  name: string,
68
102
  options: DataTransformOptions,
69
103
  adapter: SqlControlAdapter<'postgres'>,
70
- ): DataTransformOperation {
104
+ ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
71
105
  const runClosures: readonly DataTransformClosure[] = Array.isArray(options.run)
72
106
  ? options.run
73
107
  : [options.run as DataTransformClosure];
108
+
109
+ const checkPlan = options.check ? invokeAndLower(options.check, contract, adapter, name) : null;
110
+ const runPlans = runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name));
111
+
112
+ const precheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
113
+ ? [
114
+ {
115
+ description: `Check ${name} has work to do`,
116
+ sql: `SELECT EXISTS (${checkPlan.sql}) AS ok`,
117
+ params: checkPlan.params,
118
+ },
119
+ ]
120
+ : [];
121
+
122
+ const execute: readonly SqlMigrationPlanOperationStep[] = runPlans.map((plan) => ({
123
+ description: `Run ${name}`,
124
+ sql: plan.sql,
125
+ params: plan.params,
126
+ }));
127
+
128
+ const postcheck: readonly SqlMigrationPlanOperationStep[] = checkPlan
129
+ ? [
130
+ {
131
+ description: `Verify ${name} resolved all violations`,
132
+ sql: `SELECT NOT EXISTS (${checkPlan.sql}) AS ok`,
133
+ params: checkPlan.params,
134
+ },
135
+ ]
136
+ : [];
137
+
74
138
  return {
75
139
  id: `data_migration.${name}`,
76
140
  label: `Data transform: ${name}`,
77
141
  operationClass: 'data',
78
- name,
79
- source: 'migration.ts',
80
- check: options.check ? invokeAndLower(options.check, contract, adapter, name) : null,
81
- run: runClosures.map((closure) => invokeAndLower(closure, contract, adapter, name)),
142
+ ...ifDefined('invariantId', options.invariantId),
143
+ target: { id: 'postgres' },
144
+ precheck,
145
+ execute,
146
+ postcheck,
82
147
  };
83
148
  }
84
149
 
@@ -41,11 +41,13 @@ export class TypeScriptRenderablePostgresMigration
41
41
  {
42
42
  readonly #calls: readonly PostgresOpFactoryCall[];
43
43
  readonly #meta: MigrationMeta;
44
+ readonly #spaceId: string;
44
45
 
45
- constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta) {
46
+ constructor(calls: readonly PostgresOpFactoryCall[], meta: MigrationMeta, spaceId: string) {
46
47
  super();
47
48
  this.#calls = calls;
48
49
  this.#meta = meta;
50
+ this.#spaceId = spaceId;
49
51
  }
50
52
 
51
53
  override get operations(): readonly Op[] {
@@ -56,11 +58,19 @@ export class TypeScriptRenderablePostgresMigration
56
58
  return this.#meta;
57
59
  }
58
60
 
61
+ /**
62
+ * Contract space this planner-produced plan applies to. Threaded
63
+ * from the planner options so the runner keys the marker row by
64
+ * the right space when executing the plan.
65
+ */
66
+ get spaceId(): string {
67
+ return this.#spaceId;
68
+ }
69
+
59
70
  renderTypeScript(): string {
60
71
  return renderCallsToTypeScript(this.#calls, {
61
72
  from: this.#meta.from,
62
73
  to: this.#meta.to,
63
- ...ifDefined('kind', this.#meta.kind),
64
74
  ...ifDefined('labels', this.#meta.labels),
65
75
  });
66
76
  }
@@ -1,9 +1,15 @@
1
+ import type { Contract } from '@prisma-next/contract/types';
1
2
  import type {
2
3
  MigrationOperationPolicy,
3
4
  SqlMigrationPlannerPlanOptions,
5
+ SqlMigrationPlanOperation,
4
6
  SqlPlannerFailureResult,
5
7
  } from '@prisma-next/family-sql/control';
6
- import { extractCodecControlHooks, plannerFailure } from '@prisma-next/family-sql/control';
8
+ import {
9
+ extractCodecControlHooks,
10
+ planFieldEventOperations,
11
+ plannerFailure,
12
+ } from '@prisma-next/family-sql/control';
7
13
  import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
8
14
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
9
15
  import type {
@@ -15,8 +21,10 @@ import type {
15
21
  import { parsePostgresDefault } from '../default-normalizer';
16
22
  import { normalizeSchemaNativeType } from '../native-type-normalizer';
17
23
  import { planIssues } from './issue-planner';
24
+ import { RawSqlCall } from './op-factory-call';
18
25
  import { TypeScriptRenderablePostgresMigration } from './planner-produced-postgres-migration';
19
26
  import { postgresPlannerStrategies } from './planner-strategies';
27
+ import type { PostgresPlanTargetDetails } from './planner-target-details';
20
28
 
21
29
  type PlannerFrameworkComponents = SqlMigrationPlannerPlanOptions extends {
22
30
  readonly frameworkComponents: infer T;
@@ -83,30 +91,48 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
83
91
  readonly contract: unknown;
84
92
  readonly schema: unknown;
85
93
  readonly policy: MigrationOperationPolicy;
86
- readonly fromHash?: string;
87
94
  /**
88
95
  * The "from" contract (state the planner assumes the database starts
89
- * at). Only `migration plan` supplies this; `db update` / `db init`
90
- * reconcile against the live schema with no old contract. When present
91
- * alongside the `'data'` operation class, strategies that need from/to
92
- * column shape comparisons (unsafe type change, nullability tightening)
93
- * activate.
96
+ * at), or `null` for reconciliation flows. Only `migration plan` ever
97
+ * supplies a non-null value; `db update` / `db init` reconcile against
98
+ * the live schema and pass `null`. When present alongside the
99
+ * `'data'` operation class, strategies that need from/to column-shape
100
+ * comparisons (unsafe type change, nullability tightening) activate.
101
+ *
102
+ * Typed as the framework `Contract | null` to satisfy the
103
+ * `MigrationPlanner` interface contract; `planSql` narrows to the SQL
104
+ * shape via `SqlMigrationPlannerPlanOptions`. Used to populate
105
+ * `describe().from` on the produced plan as
106
+ * `fromContract?.storage.storageHash ?? null`.
94
107
  */
95
- readonly fromContract?: unknown;
108
+ readonly fromContract: Contract | null;
96
109
  readonly schemaName?: string;
97
110
  readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
111
+ /**
112
+ * Contract space this plan applies to. Stamped onto the produced
113
+ * {@link TypeScriptRenderablePostgresMigration.spaceId} so the runner keys
114
+ * the marker row by the right space.
115
+ */
116
+ readonly spaceId: string;
98
117
  }): PostgresPlanResult {
99
- return this.planSql(options as SqlMigrationPlannerPlanOptions, options.fromHash ?? '');
118
+ return this.planSql(options as SqlMigrationPlannerPlanOptions);
100
119
  }
101
120
 
102
- emptyMigration(context: MigrationScaffoldContext): MigrationPlanWithAuthoringSurface {
103
- return new TypeScriptRenderablePostgresMigration([], {
104
- from: context.fromHash,
105
- to: context.toHash,
106
- });
121
+ emptyMigration(
122
+ context: MigrationScaffoldContext,
123
+ spaceId: string,
124
+ ): MigrationPlanWithAuthoringSurface {
125
+ return new TypeScriptRenderablePostgresMigration(
126
+ [],
127
+ {
128
+ from: context.fromHash,
129
+ to: context.toHash,
130
+ },
131
+ spaceId,
132
+ );
107
133
  }
108
134
 
109
- private planSql(options: SqlMigrationPlannerPlanOptions, fromHash: string): PostgresPlanResult {
135
+ private planSql(options: SqlMigrationPlannerPlanOptions): PostgresPlanResult {
110
136
  const schemaName = options.schemaName ?? this.config.defaultSchema;
111
137
  const policyResult = this.ensureAdditivePolicy(options.policy);
112
138
  if (policyResult) {
@@ -125,7 +151,7 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
125
151
  // from/to comparisons (unsafe type change, nullable tightening) are
126
152
  // inapplicable there — reconciliation falls through to
127
153
  // `mapIssueToCall`'s direct destructive handlers.
128
- fromContract: options.fromContract ?? null,
154
+ fromContract: options.fromContract,
129
155
  schemaName,
130
156
  codecHooks,
131
157
  storageTypes,
@@ -139,12 +165,37 @@ export class PostgresMigrationPlanner implements MigrationPlanner<'sql', 'postgr
139
165
  return plannerFailure(result.failure);
140
166
  }
141
167
 
168
+ // Inline `onFieldEvent`-emitted ops after structural DDL. The fixed
169
+ // ordering is `structural → added → dropped → altered`, with
170
+ // within-group sorting by `(tableName, fieldName)` so re-emits are
171
+ // byte-stable. The hook fires only at the application emitter —
172
+ // extension-space planning never reaches this helper.
173
+ const fieldEventOps = planFieldEventOperations({
174
+ priorContract: options.fromContract,
175
+ newContract: options.contract,
176
+ codecHooks,
177
+ });
178
+ // `extractCodecControlHooks` erases target-details to `unknown`; codec
179
+ // authors target a specific lane (here, postgres) and produce ops whose
180
+ // target-details are `PostgresPlanTargetDetails`-shaped by construction.
181
+ // The cast re-specializes the type at this trust boundary.
182
+ const calls = [
183
+ ...result.value.calls,
184
+ ...fieldEventOps.map(
185
+ (op) => new RawSqlCall(op as SqlMigrationPlanOperation<PostgresPlanTargetDetails>),
186
+ ),
187
+ ];
188
+
142
189
  return Object.freeze({
143
190
  kind: 'success' as const,
144
- plan: new TypeScriptRenderablePostgresMigration(result.value.calls, {
145
- from: fromHash,
146
- to: options.contract.storage.storageHash,
147
- }),
191
+ plan: new TypeScriptRenderablePostgresMigration(
192
+ calls,
193
+ {
194
+ from: options.fromContract?.storage.storageHash ?? null,
195
+ to: options.contract.storage.storageHash,
196
+ },
197
+ options.spaceId,
198
+ ),
148
199
  });
149
200
  }
150
201
 
@@ -1,14 +1,11 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
+ import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
3
  import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
3
4
  import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
4
5
  import type { ControlStack } from '@prisma-next/framework-components/control';
5
6
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
6
7
  import { errorPostgresMigrationStackMissing } from '../errors';
7
- import {
8
- type DataTransformOptions,
9
- dataTransform,
10
- type PostgresDataTransformOperation,
11
- } from './operations/data-transform';
8
+ import { type DataTransformOptions, dataTransform } from './operations/data-transform';
12
9
  import type { PostgresPlanTargetDetails } from './planner-target-details';
13
10
 
14
11
  /**
@@ -64,7 +61,7 @@ export abstract class PostgresMigration extends SqlMigration<
64
61
  contract: TContract,
65
62
  name: string,
66
63
  options: DataTransformOptions,
67
- ): PostgresDataTransformOperation {
64
+ ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
68
65
  if (!this.controlAdapter) {
69
66
  throw errorPostgresMigrationStackMissing();
70
67
  }
@@ -14,9 +14,8 @@ import { type ImportRequirement, jsonToTsSource, renderImports } from '@prisma-n
14
14
  import type { PostgresOpFactoryCall } from './op-factory-call';
15
15
 
16
16
  export interface RenderMigrationMeta {
17
- readonly from: string;
17
+ readonly from: string | null;
18
18
  readonly to: string;
19
- readonly kind?: string;
20
19
  readonly labels?: readonly string[];
21
20
  }
22
21
 
@@ -84,9 +83,6 @@ function buildDescribeMethod(meta: RenderMigrationMeta): string {
84
83
  lines.push(' return {');
85
84
  lines.push(` from: ${JSON.stringify(meta.from)},`);
86
85
  lines.push(` to: ${JSON.stringify(meta.to)},`);
87
- if (meta.kind) {
88
- lines.push(` kind: ${JSON.stringify(meta.kind)},`);
89
- }
90
86
  if (meta.labels && meta.labels.length > 0) {
91
87
  lines.push(` labels: ${jsonToTsSource(meta.labels)},`);
92
88
  }