@prisma-next/target-postgres 0.13.0-dev.3 → 0.13.0-dev.30

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 (175) hide show
  1. package/dist/{codec-ids-CTikp1if.mjs → codec-ids-BvytN2P8.mjs} +3 -3
  2. package/dist/codec-ids-BvytN2P8.mjs.map +1 -0
  3. package/dist/{codec-ids-B1vOchLE.d.mts → codec-ids-CnXu9Qy3.d.mts} +3 -3
  4. package/dist/codec-ids-CnXu9Qy3.d.mts.map +1 -0
  5. package/dist/codec-ids.d.mts +2 -2
  6. package/dist/codec-ids.mjs +2 -2
  7. package/dist/{codec-types-CnFiNML4.d.mts → codec-types-DHCkwPKE.d.mts} +3 -3
  8. package/dist/{codec-types-CnFiNML4.d.mts.map → codec-types-DHCkwPKE.d.mts.map} +1 -1
  9. package/dist/codec-types.d.mts +1 -1
  10. package/dist/{codecs-CBpEv4s5.d.mts → codecs--0A5_4Bq.d.mts} +26 -23
  11. package/dist/codecs--0A5_4Bq.d.mts.map +1 -0
  12. package/dist/codecs.d.mts +2 -2
  13. package/dist/codecs.mjs +28 -35
  14. package/dist/codecs.mjs.map +1 -1
  15. package/dist/contract-free.d.mts +17 -2
  16. package/dist/contract-free.d.mts.map +1 -1
  17. package/dist/contract-free.mjs +3 -3
  18. package/dist/control.d.mts.map +1 -1
  19. package/dist/control.mjs +21 -27
  20. package/dist/control.mjs.map +1 -1
  21. package/dist/{data-transform-D25tLeYU.mjs → data-transform-BOWpliq8.mjs} +9 -17
  22. package/dist/data-transform-BOWpliq8.mjs.map +1 -0
  23. package/dist/{data-transform-DGOqcLrf.d.mts → data-transform-DDgWdB5o.d.mts} +2 -2
  24. package/dist/data-transform-DDgWdB5o.d.mts.map +1 -0
  25. package/dist/data-transform.d.mts +1 -1
  26. package/dist/data-transform.mjs +1 -1
  27. package/dist/{ddl-77SyXgFt.mjs → ddl-DY2R_Yqz.mjs} +18 -3
  28. package/dist/ddl-DY2R_Yqz.mjs.map +1 -0
  29. package/dist/ddl.d.mts +2 -2
  30. package/dist/ddl.mjs +2 -2
  31. package/dist/{descriptor-meta-DKmj-IMN.mjs → descriptor-meta-BKma_hQ5.mjs} +2 -2
  32. package/dist/{descriptor-meta-DKmj-IMN.mjs.map → descriptor-meta-BKma_hQ5.mjs.map} +1 -1
  33. package/dist/descriptor-meta-runtime-e5f2tscJ.mjs +131 -0
  34. package/dist/descriptor-meta-runtime-e5f2tscJ.mjs.map +1 -0
  35. package/dist/{issue-planner-Br0pt1Ea.mjs → issue-planner-DsjB7xDj.mjs} +48 -252
  36. package/dist/issue-planner-DsjB7xDj.mjs.map +1 -0
  37. package/dist/issue-planner.d.mts +8 -11
  38. package/dist/issue-planner.d.mts.map +1 -1
  39. package/dist/issue-planner.mjs +1 -1
  40. package/dist/migration.d.mts +4 -15
  41. package/dist/migration.d.mts.map +1 -1
  42. package/dist/migration.mjs +4 -4
  43. package/dist/{nodes-DZk2JZG3.mjs → nodes-Bbhs2rwj.mjs} +31 -2
  44. package/dist/nodes-Bbhs2rwj.mjs.map +1 -0
  45. package/dist/{nodes-779hmCfL.d.mts → nodes-pLeLgdis.d.mts} +30 -3
  46. package/dist/nodes-pLeLgdis.d.mts.map +1 -0
  47. package/dist/{op-factory-call-DMA86_2D.d.mts → op-factory-call-CdtMyrlU.d.mts} +12 -56
  48. package/dist/op-factory-call-CdtMyrlU.d.mts.map +1 -0
  49. package/dist/{op-factory-call-D2aAUhmS.mjs → op-factory-call-CjR846f7.mjs} +70 -198
  50. package/dist/op-factory-call-CjR846f7.mjs.map +1 -0
  51. package/dist/op-factory-call.d.mts +2 -2
  52. package/dist/op-factory-call.mjs +2 -2
  53. package/dist/pack.d.mts +36 -15
  54. package/dist/pack.d.mts.map +1 -1
  55. package/dist/pack.mjs +1 -1
  56. package/dist/{planner-CAYPJObw.mjs → planner-_FOL4I21.mjs} +25 -45
  57. package/dist/planner-_FOL4I21.mjs.map +1 -0
  58. package/dist/{planner-ddl-builders-Cw2n2llW.mjs → planner-ddl-builders-B2wOwLqI.mjs} +2 -2
  59. package/dist/planner-ddl-builders-B2wOwLqI.mjs.map +1 -0
  60. package/dist/planner-ddl-builders.d.mts +4 -4
  61. package/dist/planner-ddl-builders.d.mts.map +1 -1
  62. package/dist/planner-ddl-builders.mjs +1 -1
  63. package/dist/{planner-identity-values-BIpa5p2I.mjs → planner-identity-values-CJPha2Sz.mjs} +3 -9
  64. package/dist/planner-identity-values-CJPha2Sz.mjs.map +1 -0
  65. package/dist/planner-identity-values.d.mts +1 -1
  66. package/dist/planner-identity-values.d.mts.map +1 -1
  67. package/dist/planner-identity-values.mjs +1 -1
  68. package/dist/{planner-produced-postgres-migration-NSEhWL0L.mjs → planner-produced-postgres-migration-BmCpyWLJ.mjs} +6 -4
  69. package/dist/planner-produced-postgres-migration-BmCpyWLJ.mjs.map +1 -0
  70. package/dist/{planner-produced-postgres-migration-B4EDvLdz.d.mts → planner-produced-postgres-migration-wLhnJMMA.d.mts} +5 -6
  71. package/dist/planner-produced-postgres-migration-wLhnJMMA.d.mts.map +1 -0
  72. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  73. package/dist/planner-produced-postgres-migration.mjs +1 -1
  74. package/dist/{planner-sql-checks-DAdhnI2c.mjs → planner-sql-checks-CJJtPfDH.mjs} +3 -3
  75. package/dist/planner-sql-checks-CJJtPfDH.mjs.map +1 -0
  76. package/dist/planner-sql-checks.d.mts +2 -2
  77. package/dist/planner-sql-checks.d.mts.map +1 -1
  78. package/dist/planner-sql-checks.mjs +1 -1
  79. package/dist/{planner-type-resolution-836DExFN.mjs → planner-type-resolution-Bt2f_q-F.mjs} +1 -6
  80. package/dist/planner-type-resolution-Bt2f_q-F.mjs.map +1 -0
  81. package/dist/planner.d.mts +4 -4
  82. package/dist/planner.d.mts.map +1 -1
  83. package/dist/planner.mjs +1 -1
  84. package/dist/{postgres-contract-serializer-DYTyXjPf.mjs → postgres-contract-serializer-CyAe8ZFv.mjs} +27 -37
  85. package/dist/postgres-contract-serializer-CyAe8ZFv.mjs.map +1 -0
  86. package/dist/{postgres-migration-DZ_gLUOW.d.mts → postgres-migration-DLXL0GBf.d.mts} +10 -5
  87. package/dist/postgres-migration-DLXL0GBf.d.mts.map +1 -0
  88. package/dist/{postgres-migration-COore9Mz.mjs → postgres-migration-dG-J0aI8.mjs} +7 -3
  89. package/dist/postgres-migration-dG-J0aI8.mjs.map +1 -0
  90. package/dist/{postgres-schema-BuxCxbvB.mjs → postgres-schema-CTKYiTHu.mjs} +30 -13
  91. package/dist/postgres-schema-CTKYiTHu.mjs.map +1 -0
  92. package/dist/{render-ops-BpjstrKQ.mjs → render-ops-BREh1kHe.mjs} +10 -5
  93. package/dist/render-ops-BREh1kHe.mjs.map +1 -0
  94. package/dist/render-ops.d.mts +2 -2
  95. package/dist/render-ops.d.mts.map +1 -1
  96. package/dist/render-ops.mjs +1 -1
  97. package/dist/runtime.d.mts.map +1 -1
  98. package/dist/runtime.mjs +2 -2
  99. package/dist/{shared-DarONYBZ.d.mts → shared-jcsbXxiW.d.mts} +2 -20
  100. package/dist/shared-jcsbXxiW.d.mts.map +1 -0
  101. package/dist/types.d.mts +8 -13
  102. package/dist/types.d.mts.map +1 -1
  103. package/dist/types.mjs +2 -3
  104. package/package.json +17 -18
  105. package/src/contract-free/ddl.ts +28 -1
  106. package/src/core/authoring.ts +43 -44
  107. package/src/core/codec-helpers.ts +0 -17
  108. package/src/core/codec-ids.ts +1 -1
  109. package/src/core/codec-type-map.ts +2 -2
  110. package/src/core/codecs.ts +43 -48
  111. package/src/core/ddl/nodes.ts +59 -1
  112. package/src/core/migrations/control-policy.ts +17 -47
  113. package/src/core/migrations/issue-planner.ts +34 -70
  114. package/src/core/migrations/op-factory-call.ts +89 -142
  115. package/src/core/migrations/operations/data-transform.ts +15 -18
  116. package/src/core/migrations/planner-ddl-builders.ts +3 -4
  117. package/src/core/migrations/planner-identity-values.ts +4 -28
  118. package/src/core/migrations/planner-produced-postgres-migration.ts +15 -7
  119. package/src/core/migrations/planner-recipes.ts +2 -6
  120. package/src/core/migrations/planner-sql-checks.ts +2 -6
  121. package/src/core/migrations/planner-strategies.ts +51 -376
  122. package/src/core/migrations/planner-type-resolution.ts +2 -20
  123. package/src/core/migrations/planner.ts +6 -6
  124. package/src/core/migrations/postgres-migration.ts +19 -4
  125. package/src/core/migrations/render-ops.ts +26 -13
  126. package/src/core/migrations/runner.ts +26 -20
  127. package/src/core/postgres-contract-serializer.ts +32 -54
  128. package/src/core/postgres-enum-type-schema.ts +17 -0
  129. package/src/core/postgres-schema.ts +56 -34
  130. package/src/exports/codecs.ts +2 -2
  131. package/src/exports/contract-free.ts +1 -1
  132. package/src/exports/control.ts +0 -22
  133. package/src/exports/ddl.ts +4 -0
  134. package/src/exports/migration.ts +0 -7
  135. package/src/exports/op-factory-call.ts +0 -4
  136. package/src/exports/types.ts +0 -1
  137. package/dist/codec-ids-B1vOchLE.d.mts.map +0 -1
  138. package/dist/codec-ids-CTikp1if.mjs.map +0 -1
  139. package/dist/codecs-CBpEv4s5.d.mts.map +0 -1
  140. package/dist/data-transform-D25tLeYU.mjs.map +0 -1
  141. package/dist/data-transform-DGOqcLrf.d.mts.map +0 -1
  142. package/dist/ddl-77SyXgFt.mjs.map +0 -1
  143. package/dist/descriptor-meta-runtime-My8_s4cs.mjs +0 -130
  144. package/dist/descriptor-meta-runtime-My8_s4cs.mjs.map +0 -1
  145. package/dist/enum-planning-BCyvlFHk.mjs +0 -0
  146. package/dist/enum-planning-BCyvlFHk.mjs.map +0 -1
  147. package/dist/enum-planning.d.mts +0 -86
  148. package/dist/enum-planning.d.mts.map +0 -1
  149. package/dist/enum-planning.mjs +0 -2
  150. package/dist/issue-planner-Br0pt1Ea.mjs.map +0 -1
  151. package/dist/nodes-779hmCfL.d.mts.map +0 -1
  152. package/dist/nodes-DZk2JZG3.mjs.map +0 -1
  153. package/dist/op-factory-call-D2aAUhmS.mjs.map +0 -1
  154. package/dist/op-factory-call-DMA86_2D.d.mts.map +0 -1
  155. package/dist/planner-CAYPJObw.mjs.map +0 -1
  156. package/dist/planner-ddl-builders-Cw2n2llW.mjs.map +0 -1
  157. package/dist/planner-identity-values-BIpa5p2I.mjs.map +0 -1
  158. package/dist/planner-produced-postgres-migration-B4EDvLdz.d.mts.map +0 -1
  159. package/dist/planner-produced-postgres-migration-NSEhWL0L.mjs.map +0 -1
  160. package/dist/planner-sql-checks-DAdhnI2c.mjs.map +0 -1
  161. package/dist/planner-type-resolution-836DExFN.mjs.map +0 -1
  162. package/dist/postgres-contract-serializer-DYTyXjPf.mjs.map +0 -1
  163. package/dist/postgres-enum-type-BVn63a89.d.mts +0 -72
  164. package/dist/postgres-enum-type-BVn63a89.d.mts.map +0 -1
  165. package/dist/postgres-enum-type-DPKqCBem.mjs +0 -62
  166. package/dist/postgres-enum-type-DPKqCBem.mjs.map +0 -1
  167. package/dist/postgres-migration-COore9Mz.mjs.map +0 -1
  168. package/dist/postgres-migration-DZ_gLUOW.d.mts.map +0 -1
  169. package/dist/postgres-schema-BuxCxbvB.mjs.map +0 -1
  170. package/dist/render-ops-BpjstrKQ.mjs.map +0 -1
  171. package/dist/shared-DarONYBZ.d.mts.map +0 -1
  172. package/src/core/migrations/enum-planning.ts +0 -213
  173. package/src/core/migrations/operations/enums.ts +0 -114
  174. package/src/core/postgres-enum-type.ts +0 -89
  175. package/src/exports/enum-planning.ts +0 -11
@@ -7,7 +7,7 @@ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
7
7
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
8
8
  import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
9
9
  import { errorPostgresMigrationStackMissing } from '../errors';
10
- import { CreateSchemaCall, CreateTableCall } from './op-factory-call';
10
+ import { AddColumnCall, CreateSchemaCall, CreateTableCall } from './op-factory-call';
11
11
  import { type DataTransformOptions, dataTransform } from './operations/data-transform';
12
12
  import type { PostgresPlanTargetDetails } from './planner-target-details';
13
13
 
@@ -64,7 +64,7 @@ export abstract class PostgresMigration extends SqlMigration<
64
64
  contract: TContract,
65
65
  name: string,
66
66
  options: DataTransformOptions,
67
- ): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
67
+ ): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
68
68
  if (!this.controlAdapter) {
69
69
  throw errorPostgresMigrationStackMissing();
70
70
  }
@@ -82,7 +82,7 @@ export abstract class PostgresMigration extends SqlMigration<
82
82
  readonly ifNotExists?: boolean;
83
83
  readonly columns: readonly DdlColumn[];
84
84
  readonly constraints?: readonly DdlTableConstraint[];
85
- }): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
85
+ }): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
86
86
  if (!this.controlAdapter) {
87
87
  throw errorPostgresMigrationStackMissing();
88
88
  }
@@ -102,10 +102,25 @@ export abstract class PostgresMigration extends SqlMigration<
102
102
  protected createSchema(options: {
103
103
  readonly schema: string;
104
104
  readonly ifNotExists?: boolean;
105
- }): SqlMigrationPlanOperation<PostgresPlanTargetDetails> {
105
+ }): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
106
106
  if (!this.controlAdapter) {
107
107
  throw errorPostgresMigrationStackMissing();
108
108
  }
109
109
  return new CreateSchemaCall(options.schema).toOp(this.controlAdapter);
110
110
  }
111
+
112
+ protected addColumn(options: {
113
+ readonly schema?: string;
114
+ readonly table: string;
115
+ readonly column: DdlColumn;
116
+ }): Promise<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> {
117
+ if (!this.controlAdapter) {
118
+ throw errorPostgresMigrationStackMissing();
119
+ }
120
+ return new AddColumnCall(
121
+ options.schema ?? UNBOUND_NAMESPACE_ID,
122
+ options.table,
123
+ options.column,
124
+ ).toOp(this.controlAdapter);
125
+ }
111
126
  }
@@ -1,7 +1,11 @@
1
1
  import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
- import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
3
- import type { OpFactoryCall } from '@prisma-next/framework-components/control';
2
+ import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
3
+ import type {
4
+ MigrationPlanOperation,
5
+ OpFactoryCall,
6
+ } from '@prisma-next/framework-components/control';
4
7
  import { blindCast } from '@prisma-next/utils/casts';
8
+ import { isThenable } from '@prisma-next/utils/promise';
5
9
  import type { PostgresPlanTargetDetails } from './planner-target-details';
6
10
 
7
11
  type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
@@ -13,11 +17,11 @@ type Op = SqlMigrationPlanOperation<PostgresPlanTargetDetails>;
13
17
  * silently flow through to postgres-shaped renderers — exactly the
14
18
  * place to fail loudly with op metadata (`id` + `target.id`).
15
19
  */
16
- function assertPostgresOp(
17
- op: ReturnType<OpFactoryCall['toOp']>,
18
- callFactoryName: string,
19
- ): asserts op is Op {
20
- const targetId = (op as Partial<Op>).target?.id;
20
+ function assertPostgresOp(op: MigrationPlanOperation, callFactoryName: string): asserts op is Op {
21
+ const targetId = blindCast<
22
+ { target?: { id?: string } },
23
+ 'op.target is present on concrete SqlMigrationPlanOperation but absent on the framework MigrationPlanOperation base'
24
+ >(op).target?.id;
21
25
  if (targetId !== 'postgres') {
22
26
  throw new Error(
23
27
  `renderOps: expected postgres op but got target.id="${String(targetId)}" for op.id="${op.id}" (factoryName="${callFactoryName}"). An OpFactoryCall produced an op for a different target on the postgres planner path; check the call's target binding.`,
@@ -25,13 +29,22 @@ function assertPostgresOp(
25
29
  }
26
30
  }
27
31
 
28
- export function renderOps(calls: readonly OpFactoryCall[], lowerer?: Lowerer): Op[] {
32
+ export function renderOps(
33
+ calls: readonly OpFactoryCall[],
34
+ lowerer?: ExecuteRequestLowerer,
35
+ ): (Op | Promise<Op>)[] {
29
36
  return calls.map((c) => {
30
- const op = blindCast<
31
- { toOp(lowerer?: Lowerer): ReturnType<OpFactoryCall['toOp']> },
32
- 'PG OpFactoryCall.toOp accepts an optional Lowerer; the framework interface omits it because not all targets need a lowerer — the PG target overrides with this extended signature'
37
+ const opOrPromise = blindCast<
38
+ { toOp(lowerer?: ExecuteRequestLowerer): Op | Promise<Op> },
39
+ 'PG OpFactoryCall.toOp accepts an optional ExecuteRequestLowerer; the framework interface omits it because not all targets need a lowerer — the PG target overrides with this extended signature'
33
40
  >(c).toOp(lowerer);
34
- assertPostgresOp(op, c.factoryName);
35
- return op;
41
+ if (isThenable(opOrPromise)) {
42
+ return opOrPromise.then((op) => {
43
+ assertPostgresOp(op, c.factoryName);
44
+ return op;
45
+ });
46
+ }
47
+ assertPostgresOp(opOrPromise, c.factoryName);
48
+ return opOrPromise;
36
49
  });
37
50
  }
@@ -18,13 +18,13 @@ import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
18
18
  import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
19
19
  import type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';
20
20
  import { SqlQueryError } from '@prisma-next/sql-errors';
21
- import type { LoweredStatement } from '@prisma-next/sql-relational-core/ast';
21
+ import type { SqlExecuteRequest } from '@prisma-next/sql-relational-core/ast';
22
+ import { blindCast } from '@prisma-next/utils/casts';
22
23
  import { ifDefined } from '@prisma-next/utils/defined';
23
24
  import type { Result } from '@prisma-next/utils/result';
24
25
  import { notOk, ok, okVoid } from '@prisma-next/utils/result';
25
26
  import { parsePostgresDefault } from '../default-normalizer';
26
27
  import { normalizeSchemaNativeType } from '../native-type-normalizer';
27
- import { createResolveExistingEnumValues } from './enum-planning';
28
28
  import type { PostgresPlanTargetDetails } from './planner-target-details';
29
29
 
30
30
  interface ApplyPlanSuccessValue {
@@ -86,6 +86,12 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
86
86
  const space = options.plan.spaceId;
87
87
  const lockKey = `${LOCK_DOMAIN}:${schema}:${space}`;
88
88
 
89
+ // Materialize any async ops before running checks or executing.
90
+ const planOps = blindCast<
91
+ readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],
92
+ 'ops were produced by the PG planner and are SqlMigrationPlanOperation<PostgresPlanTargetDetails>; MigrationPlan.operations uses the wider framework type to accommodate Promise covariance'
93
+ >(await Promise.all(options.plan.operations));
94
+
89
95
  // Static checks (idempotent — safe to run again when the caller is
90
96
  // `execute(...)` because the cost is a single object comparison).
91
97
  const destinationCheck = this.ensurePlanMatchesDestinationContract(
@@ -94,7 +100,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
94
100
  );
95
101
  if (!destinationCheck.ok) return destinationCheck;
96
102
 
97
- const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);
103
+ const policyCheck = this.enforcePolicyCompatibility(options.policy, planOps);
98
104
  if (!policyCheck.ok) return policyCheck;
99
105
 
100
106
  await this.acquireLock(driver, lockKey);
@@ -113,7 +119,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
113
119
  if (skipOperations) {
114
120
  applyValue = { operationsExecuted: 0, executedOperations: [] };
115
121
  } else {
116
- const applyResult = await this.applyPlan(driver, options);
122
+ const applyResult = await this.applyPlan(driver, options, planOps);
117
123
  if (!applyResult.ok) return applyResult;
118
124
  applyValue = applyResult.value;
119
125
  }
@@ -137,9 +143,6 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
137
143
  frameworkComponents: options.frameworkComponents,
138
144
  normalizeDefault: parsePostgresDefault,
139
145
  normalizeNativeType: normalizeSchemaNativeType,
140
- resolveExistingEnumValues: createResolveExistingEnumValues(
141
- options.destinationContract.storage,
142
- ),
143
146
  });
144
147
  if (!schemaVerifyResult.ok) {
145
148
  return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {
@@ -158,11 +161,16 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
158
161
  if (!isSelfEdgeNoOp) {
159
162
  const markerResult = await this.upsertMarker(driver, options, existingMarker, space);
160
163
  if (!markerResult.ok) return markerResult;
161
- await this.recordLedgerEntries(driver, options, applyValue.executedOperations);
164
+ await this.recordLedgerEntries(
165
+ driver,
166
+ options,
167
+ applyValue.executedOperations,
168
+ planOps.length,
169
+ );
162
170
  }
163
171
 
164
172
  return runnerSuccess({
165
- operationsPlanned: options.plan.operations.length,
173
+ operationsPlanned: planOps.length,
166
174
  operationsExecuted: applyValue.operationsExecuted,
167
175
  });
168
176
  }
@@ -209,6 +217,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
209
217
  private async applyPlan(
210
218
  driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
211
219
  options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,
220
+ ops: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],
212
221
  ): Promise<Result<ApplyPlanSuccessValue, SqlMigrationRunnerFailure>> {
213
222
  const checks = options.executionChecks;
214
223
  const runPrechecks = checks?.prechecks !== false; // Default true
@@ -217,7 +226,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
217
226
 
218
227
  let operationsExecuted = 0;
219
228
  const executedOperations: Array<SqlMigrationPlanOperation<PostgresPlanTargetDetails>> = [];
220
- for (const operation of options.plan.operations) {
229
+ for (const operation of ops) {
221
230
  options.callbacks?.onOperationStart?.(operation);
222
231
  try {
223
232
  // Idempotency probe: only run if both postchecks and idempotency checks are enabled
@@ -282,13 +291,13 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
282
291
  if (schemaQuery === undefined) {
283
292
  throw new Error('Postgres control-table bootstrap must include CREATE SCHEMA');
284
293
  }
285
- await this.executeStatement(driver, this.family.lowerAst(schemaQuery, lowererContext));
294
+ await this.executeStatement(driver, await this.family.lowerAst(schemaQuery, lowererContext));
286
295
  const legacyDetection = await this.detectLegacyMarkerShape(driver);
287
296
  if (!legacyDetection.ok) {
288
297
  return legacyDetection;
289
298
  }
290
299
  for (const query of tableQueries) {
291
- await this.executeStatement(driver, this.family.lowerAst(query, lowererContext));
300
+ await this.executeStatement(driver, await this.family.lowerAst(query, lowererContext));
292
301
  }
293
302
  return okVoid();
294
303
  }
@@ -629,14 +638,15 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
629
638
  driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
630
639
  options: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>,
631
640
  executedOperations: readonly SqlMigrationPlanOperation<PostgresPlanTargetDetails>[],
641
+ planOpsLength: number,
632
642
  ): Promise<void> {
633
643
  const plan = options.plan;
634
644
  const space = plan.spaceId;
635
645
  const edges = options.migrationEdges;
636
646
  const totalEdgeOps = edges.reduce((sum, edge) => sum + edge.operationCount, 0);
637
- if (totalEdgeOps !== plan.operations.length) {
647
+ if (totalEdgeOps !== planOpsLength) {
638
648
  throw new Error(
639
- `Ledger write: plan.operations length (${plan.operations.length}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,
649
+ `Ledger write: plan.operations length (${planOpsLength}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,
640
650
  );
641
651
  }
642
652
  // The ledger records the operations as executed — idempotency-skipped ops
@@ -688,12 +698,8 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
688
698
 
689
699
  private async executeStatement(
690
700
  driver: SqlMigrationRunnerExecuteOptions<PostgresPlanTargetDetails>['driver'],
691
- statement: LoweredStatement,
701
+ statement: SqlExecuteRequest,
692
702
  ): Promise<void> {
693
- if (statement.params.length > 0) {
694
- await driver.query(statement.sql, statement.params);
695
- return;
696
- }
697
- await driver.query(statement.sql);
703
+ await driver.query(statement.sql, statement.params);
698
704
  }
699
705
  }
@@ -17,9 +17,7 @@ import {
17
17
  import type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';
18
18
  import { blindCast } from '@prisma-next/utils/casts';
19
19
  import type { JsonObject } from '@prisma-next/utils/json';
20
- import type { Type } from 'arktype';
21
20
  import { postgresAuthoringEntityTypes } from './authoring';
22
- import type { PostgresEnumType } from './postgres-enum-type';
23
21
  import { isPostgresSchema, PostgresSchema } from './postgres-schema';
24
22
 
25
23
  const POSTGRES_AUTHORING_CTX: AuthoringEntityContext = {
@@ -38,27 +36,21 @@ function isAuthoringEntityTypeFactoryOutput(
38
36
  }
39
37
 
40
38
  /**
41
- * Walks a pack's entity-type namespace tree and emits the maps the
42
- * family base consumes hydrators and validator-schema fragments, both
43
- * keyed by the descriptor's `discriminator`.
39
+ * Walks a pack's entity-type namespace tree and emits hydration factories
40
+ * keyed by the descriptor's `discriminator`. Used for `storage.types`
41
+ * (codec-triple hydration). Namespace entries hydration dispatches by
42
+ * entries key, not discriminator — handled by `hydrateEntriesKind`.
44
43
  */
45
- function collectEntityRegistryContributions(namespace: AuthoringEntityTypeNamespace): {
46
- readonly entityTypeRegistry: ReadonlyMap<string, SqlEntityHydrationFactory>;
47
- readonly validatorFragments: ReadonlyMap<string, Type<unknown>>;
48
- } {
49
- const entityTypeRegistry = new Map<string, SqlEntityHydrationFactory>();
50
- const validatorFragments = new Map<string, Type<unknown>>();
44
+ function collectStorageTypesHydrators(
45
+ namespace: AuthoringEntityTypeNamespace,
46
+ ): ReadonlyMap<string, SqlEntityHydrationFactory> {
47
+ const registry = new Map<string, SqlEntityHydrationFactory>();
51
48
  const walk = (node: AuthoringEntityTypeNamespace): void => {
52
49
  for (const value of Object.values(node)) {
53
50
  if (isAuthoringEntityTypeDescriptor(value)) {
54
51
  if (isAuthoringEntityTypeFactoryOutput(value.output)) {
55
52
  const { factory } = value.output;
56
- entityTypeRegistry.set(value.discriminator, (raw) =>
57
- factory(raw, POSTGRES_AUTHORING_CTX),
58
- );
59
- }
60
- if (value.validatorSchema !== undefined) {
61
- validatorFragments.set(value.discriminator, value.validatorSchema);
53
+ registry.set(value.discriminator, (raw) => factory(raw, POSTGRES_AUTHORING_CTX));
62
54
  }
63
55
  continue;
64
56
  }
@@ -68,15 +60,13 @@ function collectEntityRegistryContributions(namespace: AuthoringEntityTypeNamesp
68
60
  }
69
61
  };
70
62
  walk(namespace);
71
- return { entityTypeRegistry, validatorFragments };
63
+ return registry;
72
64
  }
73
65
 
74
66
  export class PostgresContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {
75
67
  constructor() {
76
- const { entityTypeRegistry, validatorFragments } = collectEntityRegistryContributions(
77
- postgresAuthoringEntityTypes,
78
- );
79
- super(entityTypeRegistry, validatorFragments);
68
+ const storageTypesHydrators = collectStorageTypesHydrators(postgresAuthoringEntityTypes);
69
+ super(storageTypesHydrators);
80
70
  }
81
71
 
82
72
  protected override hydrateSqlNamespaceEntry(
@@ -88,40 +78,22 @@ export class PostgresContractSerializer extends SqlContractSerializerBase<Contra
88
78
  }
89
79
  const hydrated = blindCast<
90
80
  SqlNamespaceTablesInput,
91
- 'super.hydrateSqlNamespaceEntry returns the tables form when raw is not a NamespaceBase'
81
+ 'super.hydrateSqlNamespaceEntry returns SqlNamespaceTablesInput when raw is not a NamespaceBase'
92
82
  >(super.hydrateSqlNamespaceEntry(nsId, raw));
93
83
  const { id, entries } = hydrated;
94
84
 
95
- // Extract the postgres-specific `type` slot directly from raw input.
96
- // The family base handles the `table` slot; the postgres target owns `type`.
97
- const rawRecord = raw as Record<string, unknown>;
98
- const rawEntries = rawRecord['entries'];
99
- let typeSlot: Record<string, PostgresEnumType> | undefined;
100
- if (rawEntries !== null && typeof rawEntries === 'object' && !Array.isArray(rawEntries)) {
101
- const rawTypeSlot = (rawEntries as Record<string, unknown>)['type'];
102
- if (rawTypeSlot !== null && typeof rawTypeSlot === 'object' && !Array.isArray(rawTypeSlot)) {
103
- const enumFactory = this.entityTypeRegistry.get('postgres-enum');
104
- typeSlot = Object.fromEntries(
105
- Object.entries(rawTypeSlot as Record<string, unknown>).map(([name, entry]) => [
106
- name,
107
- blindCast<PostgresEnumType, 'postgres-enum factory returns PostgresEnumType'>(
108
- enumFactory !== undefined ? enumFactory(entry) : entry,
109
- ),
110
- ]),
111
- );
112
- }
113
- }
114
-
115
- const emptyTables = Object.keys(entries.table).length === 0;
116
- const emptyTypes = !typeSlot || Object.keys(typeSlot).length === 0;
117
- if (id === UNBOUND_NAMESPACE_ID && emptyTables && emptyTypes) {
85
+ const valueSetSlot = entries['valueSet'];
86
+ const hasValueSets = valueSetSlot !== undefined && Object.keys(valueSetSlot).length > 0;
87
+ const emptyTables = Object.keys(entries['table'] ?? {}).length === 0;
88
+ if (id === UNBOUND_NAMESPACE_ID && emptyTables && !hasValueSets) {
118
89
  return PostgresSchema.unbound;
119
90
  }
120
91
  return new PostgresSchema({
121
92
  id,
122
93
  entries: {
123
- table: entries.table,
124
- type: typeSlot ?? {},
94
+ ...entries,
95
+ table: entries['table'] ?? {},
96
+ ...(hasValueSets ? { valueSet: valueSetSlot } : {}),
125
97
  },
126
98
  });
127
99
  }
@@ -139,7 +111,7 @@ export class PostgresContractSerializer extends SqlContractSerializerBase<Contra
139
111
  kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
140
112
  entries: {
141
113
  table: Object.fromEntries(
142
- Object.entries(ns.entries.table).map(([tableName, table]) => [
114
+ Object.entries(ns.entries.table ?? {}).map(([tableName, table]) => [
143
115
  tableName,
144
116
  this.serializeJsonValue(table) as JsonObject,
145
117
  ]),
@@ -167,19 +139,25 @@ export class PostgresContractSerializer extends SqlContractSerializerBase<Contra
167
139
 
168
140
  private serializePostgresNamespace(ns: PostgresSchema, isUnboundSlot: boolean): JsonObject {
169
141
  const tablesOut: Record<string, JsonObject> = {};
170
- for (const [tableName, table] of Object.entries(ns.entries.table)) {
142
+ for (const [tableName, table] of Object.entries(ns.table)) {
171
143
  tablesOut[tableName] = this.serializeJsonValue(table) as JsonObject;
172
144
  }
173
- const typeOut: Record<string, JsonObject> = {};
174
- for (const [typeName, ty] of Object.entries(ns.entries.type)) {
175
- typeOut[typeName] = this.serializeJsonValue(ty) as JsonObject;
145
+ const valueSetEntries = ns.valueSet;
146
+ const valueSetOut: Record<string, JsonObject> = {};
147
+ if (valueSetEntries !== undefined) {
148
+ for (const [valueSetName, valueSet] of Object.entries(valueSetEntries)) {
149
+ valueSetOut[valueSetName] = blindCast<
150
+ JsonObject,
151
+ 'serializeJsonValue round-trips the value-set node through JSON, yielding a JsonObject'
152
+ >(this.serializeJsonValue(valueSet));
153
+ }
176
154
  }
177
155
  return {
178
156
  id: ns.id,
179
157
  kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
180
158
  entries: {
181
159
  table: tablesOut,
182
- type: typeOut,
160
+ ...(Object.keys(valueSetOut).length > 0 ? { valueSet: valueSetOut } : {}),
183
161
  },
184
162
  };
185
163
  }
@@ -0,0 +1,17 @@
1
+ import { type } from 'arktype';
2
+
3
+ const ControlPolicySchema = type("'managed' | 'tolerated' | 'external' | 'observed'");
4
+
5
+ /**
6
+ * Arktype validator for a `postgres-enum` entry under
7
+ * `storage.namespaces[id].entries.type[name]`. Registered by the Postgres
8
+ * target pack against the `'type'` entries key so the family-layer namespace
9
+ * validator accepts (and rejects) entries of this kind.
10
+ */
11
+ export const PostgresEnumTypeSchema = type({
12
+ kind: "'postgres-enum'",
13
+ 'name?': 'string',
14
+ 'nativeType?': 'string',
15
+ values: type.string.array().readonly(),
16
+ 'control?': ControlPolicySchema,
17
+ });
@@ -4,21 +4,20 @@ import {
4
4
  UNBOUND_NAMESPACE_ID,
5
5
  } from '@prisma-next/framework-components/ir';
6
6
  import {
7
- type PostgresEnumStorageEntry,
8
7
  type SqlNamespaceTablesInput,
9
8
  type SqlStorage,
10
9
  StorageTable,
11
10
  type StorageTableInput,
11
+ StorageValueSet,
12
+ type StorageValueSetInput,
12
13
  } from '@prisma-next/sql-contract/types';
13
- import { PostgresEnumType, type PostgresEnumTypeInput } from './postgres-enum-type';
14
+ import { blindCast } from '@prisma-next/utils/casts';
15
+ import { ifDefined } from '@prisma-next/utils/defined';
14
16
  import { escapeLiteral } from './sql-utils';
15
17
 
16
18
  export interface PostgresSchemaInput {
17
19
  readonly id: string;
18
- readonly entries: {
19
- readonly table: Record<string, StorageTable | StorageTableInput>;
20
- readonly type: Record<string, PostgresEnumType | PostgresEnumTypeInput>;
21
- };
20
+ readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
22
21
  }
23
22
 
24
23
  /**
@@ -27,7 +26,7 @@ export interface PostgresSchemaInput {
27
26
  * `namespaces: Record<NamespaceId, PostgresSchema>` map populated by
28
27
  * the Postgres PSL interpreter from `namespace { … }` AST buckets.
29
28
  *
30
- * `entries` holds entity-kind slot maps (`table`, `type`). Qualifier
29
+ * `entries` holds entity-kind maps (`table`, `valueSet`). Qualifier
31
30
  * emission is the rendering seam: DDL / SQL emission asks the namespace
32
31
  * for its qualifier (`"<schema>"`) or for a qualified table name
33
32
  * (`"<schema>"."<table>"`) and consumes the result polymorphically.
@@ -47,31 +46,49 @@ export class PostgresSchema extends NamespaceBase {
47
46
 
48
47
  declare readonly kind: 'schema';
49
48
  readonly id: string;
50
- readonly entries: Readonly<{
51
- readonly table: Readonly<Record<string, StorageTable>>;
52
- readonly type: Readonly<Record<string, PostgresEnumType>>;
53
- }>;
49
+ readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
54
50
 
55
51
  constructor(input: PostgresSchemaInput) {
56
52
  super();
57
53
  this.id = input.id;
54
+
55
+ const carried: Record<string, Readonly<Record<string, unknown>>> = {};
56
+ let table: Readonly<Record<string, StorageTable>> = Object.freeze({});
57
+ let valueSet: Readonly<Record<string, StorageValueSet>> | undefined;
58
+ for (const [kind, rawMap] of Object.entries(input.entries)) {
59
+ if (kind === 'table') {
60
+ const tableMap: Record<string, StorageTable> = {};
61
+ for (const [name, v] of Object.entries(
62
+ blindCast<
63
+ Record<string, StorageTableInput>,
64
+ 'entries[table] holds StorageTableInput by construction'
65
+ >(rawMap),
66
+ )) {
67
+ tableMap[name] = new StorageTable(v);
68
+ }
69
+ table = Object.freeze(tableMap);
70
+ } else if (kind === 'valueSet') {
71
+ const vsMap: Record<string, StorageValueSet> = {};
72
+ for (const [name, v] of Object.entries(
73
+ blindCast<
74
+ Record<string, StorageValueSetInput>,
75
+ 'entries[valueSet] holds StorageValueSetInput by construction'
76
+ >(rawMap),
77
+ )) {
78
+ vsMap[name] = new StorageValueSet(v);
79
+ }
80
+ if (Object.keys(vsMap).length > 0) {
81
+ valueSet = Object.freeze(vsMap);
82
+ }
83
+ } else {
84
+ carried[kind] = Object.freeze(rawMap);
85
+ }
86
+ }
87
+
58
88
  this.entries = Object.freeze({
59
- table: Object.freeze(
60
- Object.fromEntries(
61
- Object.entries(input.entries.table).map(([k, v]) => [
62
- k,
63
- v instanceof StorageTable ? v : new StorageTable(v as StorageTableInput),
64
- ]),
65
- ),
66
- ),
67
- type: Object.freeze(
68
- Object.fromEntries(
69
- Object.entries(input.entries.type).map(([k, v]) => [
70
- k,
71
- v instanceof PostgresEnumType ? v : new PostgresEnumType(v as PostgresEnumTypeInput),
72
- ]),
73
- ),
74
- ),
89
+ ...carried,
90
+ table,
91
+ ...ifDefined('valueSet', valueSet),
75
92
  });
76
93
  Object.defineProperty(this, 'kind', {
77
94
  value: 'schema',
@@ -82,6 +99,14 @@ export class PostgresSchema extends NamespaceBase {
82
99
  freezeNode(this);
83
100
  }
84
101
 
102
+ get table(): Readonly<Record<string, StorageTable>> {
103
+ return this.entries['table'] ?? Object.freeze({});
104
+ }
105
+
106
+ get valueSet(): Readonly<Record<string, StorageValueSet>> | undefined {
107
+ return this.entries['valueSet'] as Readonly<Record<string, StorageValueSet>> | undefined;
108
+ }
109
+
85
110
  /**
86
111
  * The bare schema qualifier as it would appear in a rendered SQL
87
112
  * fragment (already quoted). The unbound-schema singleton overrides
@@ -162,7 +187,7 @@ export class PostgresUnboundSchema extends PostgresSchema {
162
187
  static readonly instance: PostgresUnboundSchema = new PostgresUnboundSchema();
163
188
 
164
189
  constructor(input?: PostgresSchemaInput) {
165
- super(input ?? { id: UNBOUND_NAMESPACE_ID, entries: { table: {}, type: {} } });
190
+ super(input ?? { id: UNBOUND_NAMESPACE_ID, entries: { table: {} } });
166
191
  }
167
192
 
168
193
  override qualifier(): string {
@@ -204,15 +229,12 @@ export function isPostgresSchema(ns: unknown): ns is PostgresSchema {
204
229
  * by reference and trust the resulting `SqlStorage.namespaces` map to
205
230
  * be value-stable for a given input set.
206
231
  */
207
- export function postgresCreateNamespace(
208
- input: SqlNamespaceTablesInput,
209
- enumTypes?: Readonly<Record<string, PostgresEnumStorageEntry>>,
210
- ): PostgresSchema {
232
+ export function postgresCreateNamespace(input: SqlNamespaceTablesInput): PostgresSchema {
211
233
  const schemaInput: PostgresSchemaInput = {
212
234
  id: input.id,
213
235
  entries: {
214
- table: input.entries.table,
215
- type: (enumTypes ?? {}) as Record<string, PostgresEnumTypeInput>,
236
+ ...input.entries,
237
+ table: input.entries['table'] ?? {},
216
238
  },
217
239
  };
218
240
  if (input.id === UNBOUND_NAMESPACE_ID) {
@@ -2,7 +2,6 @@ export type {
2
2
  PgBitDescriptor,
3
3
  PgBoolDescriptor,
4
4
  PgCharDescriptor,
5
- PgEnumDescriptor,
6
5
  PgFloat4Descriptor,
7
6
  PgFloat8Descriptor,
8
7
  PgFloatDescriptor,
@@ -19,6 +18,7 @@ export type {
19
18
  PgTimestampDescriptor,
20
19
  PgTimestamptzDescriptor,
21
20
  PgTimetzDescriptor,
21
+ PgUuidDescriptor,
22
22
  PgVarbitDescriptor,
23
23
  PgVarcharDescriptor,
24
24
  } from '../core/codecs';
@@ -26,7 +26,6 @@ export {
26
26
  pgBitColumn,
27
27
  pgBoolColumn,
28
28
  pgCharColumn,
29
- pgEnumColumn,
30
29
  pgFloat4Column,
31
30
  pgFloat8Column,
32
31
  pgFloatColumn,
@@ -43,6 +42,7 @@ export {
43
42
  pgTimestampColumn,
44
43
  pgTimestamptzColumn,
45
44
  pgTimetzColumn,
45
+ pgUuidColumn,
46
46
  pgVarbitColumn,
47
47
  pgVarcharColumn,
48
48
  } from '../core/codecs';
@@ -3,5 +3,5 @@ export {
3
3
  buildControlTableBootstrapQueries,
4
4
  buildSignMarkerBootstrapQueries,
5
5
  } from '../contract-free/control-bootstrap';
6
- export { createSchema, createTable } from '../contract-free/ddl';
6
+ export { addColumnAction, alterTable, createSchema, createTable } from '../contract-free/ddl';
7
7
  export { PostgresTableSource } from '../core/ast/table-source';
@@ -10,10 +10,6 @@ import type {
10
10
  import type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
11
11
  import { ifDefined } from '@prisma-next/utils/defined';
12
12
  import { postgresTargetDescriptorMeta } from '../core/descriptor-meta';
13
- import {
14
- enumStorageCompoundKey,
15
- resolveDdlSchemaForNamespaceStorage,
16
- } from '../core/migrations/enum-planning';
17
13
  import { createPostgresMigrationPlanner } from '../core/migrations/planner';
18
14
  import { renderDefaultLiteral } from '../core/migrations/planner-ddl-builders';
19
15
  import type { PostgresPlanTargetDetails } from '../core/migrations/planner-target-details';
@@ -34,12 +30,6 @@ function buildNativeTypeExpander(
34
30
  readonly typeParams?: Record<string, unknown>;
35
31
  }) => {
36
32
  if (!input.typeParams) return input.nativeType;
37
- // Mirror `renderExpectedNativeType` in verify-sql-schema: when a codec
38
- // has no `expandNativeType` hook (e.g. `pg/enum@1`, whose typeParams
39
- // describe the value set rather than a DDL suffix), fall back to the
40
- // bare native type rather than throwing. Throwing here would reject
41
- // every plan involving an enum-/values-typed column as soon as its
42
- // `typeRef` resolved to a `StorageTypeInstance` carrying typeParams.
43
33
  if (!input.codecId) return input.nativeType;
44
34
  const hooks = codecHooks.get(input.codecId);
45
35
  if (!hooks?.expandNativeType) return input.nativeType;
@@ -80,18 +70,6 @@ const postgresTargetDescriptor: SqlControlTargetDescriptor<'postgres', PostgresP
80
70
  annotationNamespace: 'pg',
81
71
  ...ifDefined('expandNativeType', expander),
82
72
  renderDefault: postgresRenderDefault,
83
- // Schema-qualify enum annotation keys so the projected "from" IR's
84
- // `storageTypes` match `readExistingEnumValues` on the read side
85
- // (the contract-to-contract `migration plan` path). The DDL-schema
86
- // resolution + compound-key format stay here in the target layer;
87
- // the family projector treats the returned string as opaque.
88
- // `undefined` schema IR ⇒ the unbound coordinate resolves to the
89
- // default `public` landing schema, matching the read-side fallback.
90
- resolveEnumStorageKey: (storage, namespaceId, nativeType) =>
91
- enumStorageCompoundKey(
92
- resolveDdlSchemaForNamespaceStorage(storage, namespaceId, undefined),
93
- nativeType,
94
- ),
95
73
  });
96
74
  },
97
75
  },