@prisma-next/target-postgres 0.16.0-dev.3 → 0.16.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 (166) hide show
  1. package/README.md +1 -1
  2. package/dist/{authoring-34L18kA1.mjs → authoring-DnO1B0N9.mjs} +29 -18
  3. package/dist/authoring-DnO1B0N9.mjs.map +1 -0
  4. package/dist/canonicalize-C7zKHw8m.mjs +59 -0
  5. package/dist/canonicalize-C7zKHw8m.mjs.map +1 -0
  6. package/dist/{canonicalize-D1F4vUnP.d.mts → canonicalize-CQlxSR4D.d.mts} +3 -18
  7. package/dist/canonicalize-CQlxSR4D.d.mts.map +1 -0
  8. package/dist/{codec-types-CQ07nOz-.d.mts → codec-types-DdoPc2dy.d.mts} +3 -2
  9. package/dist/codec-types-DdoPc2dy.d.mts.map +1 -0
  10. package/dist/codec-types.d.mts +1 -1
  11. package/dist/{codecs-G4DTY0F_.d.mts → codecs-CGE5dh94.d.mts} +7 -2
  12. package/dist/codecs-CGE5dh94.d.mts.map +1 -0
  13. package/dist/{codecs-TCsGgW-Y.mjs → codecs-Deand7sy.mjs} +84 -26
  14. package/dist/codecs-Deand7sy.mjs.map +1 -0
  15. package/dist/codecs.d.mts +2 -2
  16. package/dist/codecs.mjs +2 -2
  17. package/dist/contract-free.d.mts +1 -1
  18. package/dist/contract-free.d.mts.map +1 -1
  19. package/dist/contract-free.mjs +2 -2
  20. package/dist/{control-policy-CZKdMztu.mjs → control-policy-DEwdS3oK.mjs} +29 -29
  21. package/dist/control-policy-DEwdS3oK.mjs.map +1 -0
  22. package/dist/control.mjs +91 -239
  23. package/dist/control.mjs.map +1 -1
  24. package/dist/data-transform-BOWpliq8.mjs.map +1 -1
  25. package/dist/{ddl-DHXKzQ99.mjs → ddl-BQC6u-L9.mjs} +28 -4
  26. package/dist/{ddl-DHXKzQ99.mjs.map → ddl-BQC6u-L9.mjs.map} +1 -1
  27. package/dist/ddl.d.mts +3 -3
  28. package/dist/ddl.mjs +2 -2
  29. package/dist/{descriptor-meta-qubYaxQA.mjs → descriptor-meta-DGlJurgA.mjs} +4 -4
  30. package/dist/{descriptor-meta-qubYaxQA.mjs.map → descriptor-meta-DGlJurgA.mjs.map} +1 -1
  31. package/dist/{diff-database-schema-C-hnJ5mr.mjs → diff-database-schema-qRPwuYBH.mjs} +37 -15
  32. package/dist/diff-database-schema-qRPwuYBH.mjs.map +1 -0
  33. package/dist/diff-database-schema.mjs +1 -1
  34. package/dist/{errors-ByDS0xJL.mjs → errors-DBSNx7EJ.mjs} +6 -2
  35. package/dist/errors-DBSNx7EJ.mjs.map +1 -0
  36. package/dist/errors.d.mts +4 -1
  37. package/dist/errors.d.mts.map +1 -1
  38. package/dist/errors.mjs +2 -2
  39. package/dist/issue-planner.d.mts +2 -2
  40. package/dist/issue-planner.d.mts.map +1 -1
  41. package/dist/issue-planner.mjs +1 -1
  42. package/dist/migration.d.mts +2 -2
  43. package/dist/migration.mjs +2 -2
  44. package/dist/{nodes-DJC5B8Id.mjs → nodes-04EILM-z.mjs} +65 -2
  45. package/dist/nodes-04EILM-z.mjs.map +1 -0
  46. package/dist/{nodes-CQzM2hR0.d.mts → nodes-DjCsN-uv.d.mts} +69 -4
  47. package/dist/nodes-DjCsN-uv.d.mts.map +1 -0
  48. package/dist/{op-factory-call-DT7kdjVP.d.mts → op-factory-call-0TB5XNij.d.mts} +46 -8
  49. package/dist/op-factory-call-0TB5XNij.d.mts.map +1 -0
  50. package/dist/{op-factory-call-DuRj7WXX.mjs → op-factory-call-CIaPDDUc.mjs} +147 -64
  51. package/dist/op-factory-call-CIaPDDUc.mjs.map +1 -0
  52. package/dist/op-factory-call.d.mts +2 -2
  53. package/dist/op-factory-call.mjs +2 -2
  54. package/dist/pack.d.mts +2 -2
  55. package/dist/pack.d.mts.map +1 -1
  56. package/dist/pack.mjs +1 -1
  57. package/dist/{planner-DPGxFcXo.mjs → planner-BhnkIJX-.mjs} +121 -13
  58. package/dist/planner-BhnkIJX-.mjs.map +1 -0
  59. package/dist/{planner-ddl-builders-BKlmGDJl.mjs → planner-ddl-builders-CVo3L7Nd.mjs} +12 -7
  60. package/dist/planner-ddl-builders-CVo3L7Nd.mjs.map +1 -0
  61. package/dist/planner-ddl-builders.d.mts.map +1 -1
  62. package/dist/planner-ddl-builders.mjs +1 -1
  63. package/dist/{planner-produced-postgres-migration-CFDpqWWE.mjs → planner-produced-postgres-migration-BE3EJIRK.mjs} +9 -6
  64. package/dist/planner-produced-postgres-migration-BE3EJIRK.mjs.map +1 -0
  65. package/dist/{planner-produced-postgres-migration-C-dwGdb2.d.mts → planner-produced-postgres-migration-CRAHl6ak.d.mts} +3 -3
  66. package/dist/planner-produced-postgres-migration-CRAHl6ak.d.mts.map +1 -0
  67. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  68. package/dist/planner-produced-postgres-migration.mjs +1 -1
  69. package/dist/{planner-schema-lookup-CiVaAQP-.mjs → planner-schema-lookup-D3Y_OL-C.mjs} +3 -3
  70. package/dist/planner-schema-lookup-D3Y_OL-C.mjs.map +1 -0
  71. package/dist/planner-schema-lookup.mjs +1 -1
  72. package/dist/{planner-sql-checks-BuB877wH.mjs → planner-sql-checks-CmiQ6Nei.mjs} +3 -3
  73. package/dist/{planner-sql-checks-BuB877wH.mjs.map → planner-sql-checks-CmiQ6Nei.mjs.map} +1 -1
  74. package/dist/planner-sql-checks.mjs +1 -1
  75. package/dist/planner.d.mts +27 -1
  76. package/dist/planner.d.mts.map +1 -1
  77. package/dist/planner.mjs +2 -2
  78. package/dist/{postgres-contract-view-DebHMqGR.mjs → postgres-contract-view-BeIEyZfD.mjs} +3 -3
  79. package/dist/{postgres-contract-view-DebHMqGR.mjs.map → postgres-contract-view-BeIEyZfD.mjs.map} +1 -1
  80. package/dist/{postgres-migration-C_OPFhIF.mjs → postgres-migration--BzvpA92.mjs} +9 -6
  81. package/dist/postgres-migration--BzvpA92.mjs.map +1 -0
  82. package/dist/{postgres-migration-DLIvzHjX.d.mts → postgres-migration-BXelnqHO.d.mts} +15 -10
  83. package/dist/postgres-migration-BXelnqHO.d.mts.map +1 -0
  84. package/dist/{postgres-schema-CewLYHB7.mjs → postgres-schema-CTplQZDK.mjs} +2 -2
  85. package/dist/{postgres-schema-CewLYHB7.mjs.map → postgres-schema-CTplQZDK.mjs.map} +1 -1
  86. package/dist/{render-ops-BREh1kHe.mjs → render-ops-TcY9XEkz.mjs} +7 -2
  87. package/dist/render-ops-TcY9XEkz.mjs.map +1 -0
  88. package/dist/render-ops.d.mts.map +1 -1
  89. package/dist/render-ops.mjs +1 -1
  90. package/dist/{render-typescript-7yqILcwr.mjs → render-typescript-Ta7_6CGf.mjs} +20 -8
  91. package/dist/render-typescript-Ta7_6CGf.mjs.map +1 -0
  92. package/dist/render-typescript.d.mts +2 -0
  93. package/dist/render-typescript.d.mts.map +1 -1
  94. package/dist/render-typescript.mjs +1 -1
  95. package/dist/{resolve-ddl-schema-D0wi_P9Q.mjs → resolve-ddl-schema-BAsJEKW8.mjs} +2 -2
  96. package/dist/{resolve-ddl-schema-D0wi_P9Q.mjs.map → resolve-ddl-schema-BAsJEKW8.mjs.map} +1 -1
  97. package/dist/rls-canonicalize.d.mts +2 -26
  98. package/dist/rls-canonicalize.mjs +2 -2
  99. package/dist/runtime.mjs +1 -1
  100. package/dist/schema-ir-annotations.mjs +1 -1
  101. package/dist/{sql-utils-SU4FDvIV.mjs → sql-utils-B2lzkKfP.mjs} +22 -19
  102. package/dist/sql-utils-B2lzkKfP.mjs.map +1 -0
  103. package/dist/sql-utils.d.mts +4 -9
  104. package/dist/sql-utils.d.mts.map +1 -1
  105. package/dist/sql-utils.mjs +2 -2
  106. package/dist/types.mjs +1 -1
  107. package/package.json +20 -20
  108. package/src/contract-free/ddl.ts +47 -0
  109. package/src/core/authoring.ts +35 -26
  110. package/src/core/codec-helpers.ts +69 -16
  111. package/src/core/codec-type-map.ts +2 -0
  112. package/src/core/codecs.ts +29 -14
  113. package/src/core/ddl/nodes.ts +104 -0
  114. package/src/core/errors.ts +30 -0
  115. package/src/core/migrations/column-ddl-rendering.ts +2 -1
  116. package/src/core/migrations/contract-to-postgres-database-schema-node.ts +38 -14
  117. package/src/core/migrations/issue-planner.ts +40 -13
  118. package/src/core/migrations/op-factory-call.ts +189 -44
  119. package/src/core/migrations/operations/constraints.ts +3 -2
  120. package/src/core/migrations/operations/data-transform.ts +1 -1
  121. package/src/core/migrations/operations/indexes.ts +80 -32
  122. package/src/core/migrations/operations/rls.ts +4 -1
  123. package/src/core/migrations/planner-ddl-builders.ts +16 -6
  124. package/src/core/migrations/planner-produced-postgres-migration.ts +8 -1
  125. package/src/core/migrations/planner-schema-lookup.ts +2 -2
  126. package/src/core/migrations/planner.ts +163 -5
  127. package/src/core/migrations/postgres-migration.ts +26 -8
  128. package/src/core/migrations/render-ops.ts +4 -1
  129. package/src/core/migrations/render-typescript.ts +20 -8
  130. package/src/core/migrations/runner.ts +7 -3
  131. package/src/core/psl-infer/infer-psl-contract.ts +36 -185
  132. package/src/core/psl-infer/postgres-type-map.ts +43 -65
  133. package/src/core/rls/canonicalize.ts +4 -21
  134. package/src/core/sql-utils.ts +16 -27
  135. package/src/exports/codecs.ts +2 -0
  136. package/src/exports/ddl.ts +4 -0
  137. package/src/exports/errors.ts +1 -1
  138. package/src/exports/op-factory-call.ts +1 -0
  139. package/src/exports/rls-canonicalize.ts +0 -6
  140. package/src/exports/sql-utils.ts +0 -1
  141. package/dist/authoring-34L18kA1.mjs.map +0 -1
  142. package/dist/canonicalize-D1F4vUnP.d.mts.map +0 -1
  143. package/dist/codec-types-CQ07nOz-.d.mts.map +0 -1
  144. package/dist/codecs-G4DTY0F_.d.mts.map +0 -1
  145. package/dist/codecs-TCsGgW-Y.mjs.map +0 -1
  146. package/dist/control-policy-CZKdMztu.mjs.map +0 -1
  147. package/dist/diff-database-schema-C-hnJ5mr.mjs.map +0 -1
  148. package/dist/errors-ByDS0xJL.mjs.map +0 -1
  149. package/dist/nodes-CQzM2hR0.d.mts.map +0 -1
  150. package/dist/nodes-DJC5B8Id.mjs.map +0 -1
  151. package/dist/op-factory-call-DT7kdjVP.d.mts.map +0 -1
  152. package/dist/op-factory-call-DuRj7WXX.mjs.map +0 -1
  153. package/dist/planner-DPGxFcXo.mjs.map +0 -1
  154. package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +0 -1
  155. package/dist/planner-produced-postgres-migration-C-dwGdb2.d.mts.map +0 -1
  156. package/dist/planner-produced-postgres-migration-CFDpqWWE.mjs.map +0 -1
  157. package/dist/planner-schema-lookup-CiVaAQP-.mjs.map +0 -1
  158. package/dist/postgres-migration-C_OPFhIF.mjs.map +0 -1
  159. package/dist/postgres-migration-DLIvzHjX.d.mts.map +0 -1
  160. package/dist/render-ops-BREh1kHe.mjs.map +0 -1
  161. package/dist/render-typescript-7yqILcwr.mjs.map +0 -1
  162. package/dist/rls-canonicalize.d.mts.map +0 -1
  163. package/dist/sql-utils-SU4FDvIV.mjs.map +0 -1
  164. package/dist/wire-name-DbQ9-2RC.mjs +0 -104
  165. package/dist/wire-name-DbQ9-2RC.mjs.map +0 -1
  166. package/src/core/rls/wire-name.ts +0 -33
@@ -9,13 +9,19 @@
9
9
  * shape is, so future consolidation to a framework-level helper is mechanical.
10
10
  */
11
11
 
12
- import type { OpFactoryCall } from '@prisma-next/framework-components/control';
12
+ import {
13
+ contractSnapshotJsonSpecifier,
14
+ contractSnapshotTypesSpecifier,
15
+ type OpFactoryCall,
16
+ } from '@prisma-next/framework-components/control';
13
17
  import { detectScaffoldRuntime, shebangLineFor } from '@prisma-next/migration-tools/migration-ts';
14
18
  import { type ImportRequirement, renderImports } from '@prisma-next/ts-render';
15
19
 
16
20
  export interface RenderMigrationMeta {
17
21
  readonly from: string | null;
18
22
  readonly to: string;
23
+ /** POSIX-relative path from the migration package dir to `migrations/snapshots`, e.g. '../../snapshots'. */
24
+ readonly snapshotsImportPath: string;
19
25
  }
20
26
 
21
27
  /**
@@ -86,31 +92,37 @@ function buildImports(calls: ReadonlyArray<OpFactoryCall>, meta: RenderMigration
86
92
 
87
93
  /**
88
94
  * The committed contract-JSON imports the scaffold reads its from/to identity
89
- * from. `end-contract.json` is always present; `start-contract.json` is added
90
- * only for a non-baseline migration (`meta.from !== null`). The matching
91
- * `Contract` type imports (aliased `Start`/`End`) feed the
95
+ * from, resolved to the deduplicated snapshot store under
96
+ * `meta.snapshotsImportPath`. The end snapshot is always present; the start
97
+ * snapshot is added only for a non-baseline migration (`meta.from !== null`).
98
+ * The matching `Contract` type imports (aliased `Start`/`End`) feed the
92
99
  * `Migration<Start, End>` generics. Baseline emits `Migration<never, End>` with
93
100
  * no start imports — `never` is the honest "no prior contract" Start.
94
101
  */
95
102
  function contractImports(meta: RenderMigrationMeta): readonly ImportRequirement[] {
96
103
  const reqs: ImportRequirement[] = [
97
104
  {
98
- moduleSpecifier: './end-contract.json',
105
+ moduleSpecifier: contractSnapshotJsonSpecifier(meta.snapshotsImportPath, meta.to),
99
106
  symbol: 'endContract',
100
107
  kind: 'default',
101
108
  attributes: { type: 'json' },
102
109
  },
103
- { moduleSpecifier: './end-contract', symbol: 'Contract', alias: 'End', typeOnly: true },
110
+ {
111
+ moduleSpecifier: contractSnapshotTypesSpecifier(meta.snapshotsImportPath, meta.to),
112
+ symbol: 'Contract',
113
+ alias: 'End',
114
+ typeOnly: true,
115
+ },
104
116
  ];
105
117
  if (meta.from !== null) {
106
118
  reqs.push({
107
- moduleSpecifier: './start-contract.json',
119
+ moduleSpecifier: contractSnapshotJsonSpecifier(meta.snapshotsImportPath, meta.from),
108
120
  symbol: 'startContract',
109
121
  kind: 'default',
110
122
  attributes: { type: 'json' },
111
123
  });
112
124
  reqs.push({
113
- moduleSpecifier: './start-contract',
125
+ moduleSpecifier: contractSnapshotTypesSpecifier(meta.snapshotsImportPath, meta.from),
114
126
  symbol: 'Contract',
115
127
  alias: 'Start',
116
128
  typeOnly: true,
@@ -20,8 +20,10 @@ import { SqlQueryError } from '@prisma-next/sql-errors';
20
20
  import type { SqlExecuteRequest } from '@prisma-next/sql-relational-core/ast';
21
21
  import { blindCast } from '@prisma-next/utils/casts';
22
22
  import { ifDefined } from '@prisma-next/utils/defined';
23
+ import { InternalError } from '@prisma-next/utils/internal-error';
23
24
  import type { Result } from '@prisma-next/utils/result';
24
25
  import { notOk, ok, okVoid } from '@prisma-next/utils/result';
26
+ import { postgresError } from '../errors';
25
27
  import type { PostgresPlanTargetDetails } from './planner-target-details';
26
28
 
27
29
  interface ApplyPlanSuccessValue {
@@ -76,8 +78,10 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
76
78
  UNBOUND_NAMESPACE_ID;
77
79
  const driver = options.driver;
78
80
  if (options.space !== undefined && options.space !== options.plan.spaceId) {
79
- throw new Error(
81
+ throw postgresError(
82
+ 'MIGRATION.CONTRACT_SPACE_VIOLATION',
80
83
  `SqlMigrationRunner: options.space (${options.space}) does not match plan.spaceId (${options.plan.spaceId})`,
84
+ { meta: { space: options.space, planSpaceId: options.plan.spaceId } },
81
85
  );
82
86
  }
83
87
  const space = options.plan.spaceId;
@@ -283,7 +287,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
283
287
  const bootstrapQueries = this.family.bootstrapControlTableQueries();
284
288
  const [schemaQuery, ...tableQueries] = bootstrapQueries;
285
289
  if (schemaQuery === undefined) {
286
- throw new Error('Postgres control-table bootstrap must include CREATE SCHEMA');
290
+ throw new InternalError('Postgres control-table bootstrap must include CREATE SCHEMA');
287
291
  }
288
292
  await this.executeStatement(driver, await this.family.lowerAst(schemaQuery, lowererContext));
289
293
  const legacyDetection = await this.detectLegacyMarkerShape(driver);
@@ -640,7 +644,7 @@ class PostgresMigrationRunner implements SqlMigrationRunner<PostgresPlanTargetDe
640
644
  const edges = options.migrationEdges;
641
645
  const totalEdgeOps = edges.reduce((sum, edge) => sum + edge.operationCount, 0);
642
646
  if (totalEdgeOps !== planOpsLength) {
643
- throw new Error(
647
+ throw new InternalError(
644
648
  `Ledger write: plan.operations length (${planOpsLength}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,
645
649
  );
646
650
  }
@@ -3,7 +3,6 @@ import type { SqlDescribedContractSpace } from '@prisma-next/family-sql/control'
3
3
  import type {
4
4
  DefaultMappingOptions,
5
5
  EnumInfo,
6
- PslNativeTypeAttribute,
7
6
  PslPrinterOptions,
8
7
  PslTypeMap,
9
8
  RelationField,
@@ -18,7 +17,6 @@ import {
18
17
  toEnumName,
19
18
  toFieldName,
20
19
  toModelName,
21
- toNamedTypeName,
22
20
  } from '@prisma-next/family-sql/psl-infer';
23
21
  import { coordinateKey, elementCoordinates } from '@prisma-next/framework-components/ir';
24
22
  import type {
@@ -31,10 +29,8 @@ import type {
31
29
  PslFieldAttribute,
32
30
  PslModel,
33
31
  PslModelAttribute,
34
- PslNamedTypeDeclaration,
35
32
  PslSpan,
36
33
  PslTypeConstructorCall,
37
- PslTypesBlock,
38
34
  } from '@prisma-next/framework-components/psl-ast';
39
35
  import {
40
36
  makePslNamespace,
@@ -46,6 +42,7 @@ import type { SqlColumnIR, SqlForeignKeyIR } from '@prisma-next/sql-schema-ir/ty
46
42
  import { SqlSchemaIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
47
43
  import { blindCast } from '@prisma-next/utils/casts';
48
44
  import { ifDefined } from '@prisma-next/utils/defined';
45
+ import { postgresError } from '../errors';
49
46
  import type { PostgresDatabaseSchemaNode } from '../schema-ir/postgres-database-schema-node';
50
47
  import { createPostgresDefaultMapping } from './postgres-default-mapping';
51
48
  import { createPostgresTypeMap } from './postgres-type-map';
@@ -74,17 +71,6 @@ type ResolvedColumnFieldName = {
74
71
 
75
72
  type TableColumnFieldNameMap = ReadonlyMap<string, ResolvedColumnFieldName>;
76
73
 
77
- type NamedTypeRegistryEntry = {
78
- readonly name: string;
79
- readonly baseType: string;
80
- readonly nativeTypeAttribute: PslNativeTypeAttribute;
81
- };
82
-
83
- type NamedTypeRegistry = {
84
- readonly entriesByKey: Map<string, NamedTypeRegistryEntry>;
85
- readonly usedNames: Set<string>;
86
- };
87
-
88
74
  type TopLevelNameResult = {
89
75
  readonly name: string;
90
76
  readonly map?: string | undefined;
@@ -224,11 +210,13 @@ function resolveForeignKeys(
224
210
  if (owner !== undefined) {
225
211
  const target = resolveCrossSpaceTarget(owner, fk.referencedSchema, fk.referencedTable);
226
212
  if (target === undefined) {
227
- throw new Error(
213
+ throw postgresError(
214
+ 'CONTRACT.PACK_CONTRIBUTION_INVALID',
228
215
  `contract infer: described contract space "${owner.spaceId}" owns storage ` +
229
216
  `coordinate "${fk.referencedSchema}.${fk.referencedTable}" but declares no ` +
230
217
  'domain model mapped to it. A pack that describes a table must also declare the ' +
231
218
  'domain model it maps to; this pack is malformed.',
219
+ { meta: { spaceId: owner.spaceId, tableName: fk.referencedTable } },
232
220
  );
233
221
  }
234
222
 
@@ -384,22 +372,26 @@ export function inferPostgresPslContract(
384
372
  continue;
385
373
  }
386
374
  if (tables[tableName] !== undefined) {
387
- throw new Error(
375
+ throw postgresError(
376
+ 'CONTRACT.INFER_UNSUPPORTED',
388
377
  `contract infer: duplicate table name "${tableName}" across schemas is not yet supported ` +
389
378
  '(single-namespace PSL inference emits one flat bucket; multi-namespace `namespace { … }` ' +
390
379
  'output is a later slice).',
380
+ { meta: { tableName } },
391
381
  );
392
382
  }
393
383
  if (ownedEnumTypes !== undefined) {
394
384
  for (const column of Object.values(table.columns)) {
395
385
  const owningSpaceId = ownedEnumTypes.get(column.nativeType);
396
386
  if (owningSpaceId !== undefined) {
397
- throw new Error(
387
+ throw postgresError(
388
+ 'CONTRACT.INFER_UNSUPPORTED',
398
389
  `contract infer: column "${tableName}"."${column.name}" is typed by native enum ` +
399
390
  `type "${column.nativeType}", which extension pack space "${owningSpaceId}" ` +
400
391
  'already describes. A cross-space enum-typed column has no authorable PSL form ' +
401
392
  "yet; describe the table in that pack's contract or retype the column before " +
402
393
  're-running contract infer.',
394
+ { meta: { tableName, columnName: column.name } },
403
395
  );
404
396
  }
405
397
  }
@@ -418,12 +410,14 @@ export function inferPostgresPslContract(
418
410
  if (enumDefinitions.size > 0) {
419
411
  const contentNamespaces = new Set([...enumNamespaceNames, ...tableNamespaceNames]);
420
412
  if (contentNamespaces.size > 1) {
421
- throw new Error(
413
+ throw postgresError(
414
+ 'CONTRACT.INFER_UNSUPPORTED',
422
415
  'contract infer: native enum adoption with content across multiple schemas is not yet ' +
423
416
  'supported (single-namespace PSL inference emits one `namespace { … }` block; ' +
424
417
  `multi-namespace output is a later slice). Schemas: ${[...contentNamespaces]
425
418
  .sort()
426
419
  .join(', ')}.`,
420
+ { meta: { schemas: [...contentNamespaces].sort() } },
427
421
  );
428
422
  }
429
423
  wrapNamespaceName = [...contentNamespaces][0];
@@ -511,8 +505,6 @@ export function buildPslDocumentAst(
511
505
  }
512
506
  }
513
507
 
514
- const reservedNamedTypeNames = createReservedNamedTypeNames(modelNames, enumNameMap);
515
-
516
508
  // Cross-space entries are seeded first so a real local table of the same
517
509
  // bare name (an existing single-namespace-flat-bucket limitation, not new
518
510
  // here) always wins the merge, matching `resolveForeignKeys`'s own
@@ -522,7 +514,6 @@ export function buildPslDocumentAst(
522
514
  ...buildFieldNamesByTable(schemaIR.tables),
523
515
  ]);
524
516
  const { relationsByTable } = inferRelations(schemaIR.tables, modelNameMap);
525
- const namedTypes = seedNamedTypeRegistry(schemaIR, typeMap, enumNameMap, reservedNamedTypeNames);
526
517
 
527
518
  const models: PslModel[] = [];
528
519
  for (const table of Object.values(schemaIR.tables)) {
@@ -532,7 +523,6 @@ export function buildPslDocumentAst(
532
523
  typeMap,
533
524
  enumNameMap,
534
525
  fieldNamesByTable,
535
- namedTypes,
536
526
  defaultMapping,
537
527
  rawDefaultParser,
538
528
  [
@@ -546,18 +536,6 @@ export function buildPslDocumentAst(
546
536
 
547
537
  const sortedModels = topologicalSort(models, schemaIR.tables, modelNameMap);
548
538
 
549
- const namedTypeEntries = [...namedTypes.entriesByKey.values()].sort((a, b) =>
550
- a.name.localeCompare(b.name),
551
- );
552
- const types: PslTypesBlock | undefined =
553
- namedTypeEntries.length > 0
554
- ? {
555
- kind: 'types',
556
- declarations: namedTypeEntries.map(buildNamedTypeDeclaration),
557
- span: SYNTHETIC_SPAN,
558
- }
559
- : undefined;
560
-
561
539
  // Inferred PSL nodes will eventually be routed into per-namespace buckets
562
540
  // matching the source storage; for now everything lands in a single bucket.
563
541
  // Without a `namespaceName` that bucket is the synthesised `__unspecified__`
@@ -576,7 +554,6 @@ export function buildPslDocumentAst(
576
554
  span: SYNTHETIC_SPAN,
577
555
  }),
578
556
  ],
579
- ...(types ? { types } : {}),
580
557
  span: SYNTHETIC_SPAN,
581
558
  };
582
559
 
@@ -670,7 +647,6 @@ function buildModel(
670
647
  typeMap: PslTypeMap,
671
648
  enumNameMap: ReadonlyMap<string, string>,
672
649
  fieldNamesByTable: ReadonlyMap<string, TableColumnFieldNameMap>,
673
- namedTypes: NamedTypeRegistry,
674
650
  defaultMapping: DefaultMappingOptions | undefined,
675
651
  rawDefaultParser: PslPrinterOptions['parseRawDefault'],
676
652
  relationFields: readonly RelationField[],
@@ -703,7 +679,6 @@ function buildModel(
703
679
  typeMap,
704
680
  enumNameMap,
705
681
  fieldNameMap,
706
- namedTypes,
707
682
  defaultMapping,
708
683
  rawDefaultParser,
709
684
  pkColumns,
@@ -738,8 +713,15 @@ function buildModel(
738
713
  }
739
714
 
740
715
  for (const index of table.indexes) {
716
+ // Expression indexes (no column tuple) and partial indexes (a where
717
+ // predicate) are not emitted yet — no authoring surface can carry their
718
+ // bodies, so adopting them name-only would fail its own verify (the
719
+ // exact-mode body compare). Their inference lands when the authoring
720
+ // surfaces for those bodies exist.
721
+ if (index.columns === undefined || index.where !== undefined) continue;
741
722
  if (!index.unique) {
742
- const indexFieldNames = index.columns.map((columnName) =>
723
+ const columns = index.columns;
724
+ const indexFieldNames = columns.map((columnName) =>
743
725
  resolveColumnFieldName(fieldNamesByTable, table.name, columnName),
744
726
  );
745
727
  modelAttributes.push(
@@ -805,7 +787,7 @@ function buildDanglingForeignKeyWarning(
805
787
  return (
806
788
  `Foreign key ${descriptions.join(', ')} exists in the database, but its target schema is ` +
807
789
  'outside the introspected scope, so no relation field was generated. If the target schema ' +
808
- 'is described by an extension pack, add it to extensionPacks and re-run infer.'
790
+ 'is described by an extension pack, add it to extensions and re-run infer.'
809
791
  );
810
792
  }
811
793
 
@@ -815,7 +797,6 @@ function buildScalarField(
815
797
  typeMap: PslTypeMap,
816
798
  enumNameMap: ReadonlyMap<string, string>,
817
799
  fieldNameMap: TableColumnFieldNameMap | undefined,
818
- namedTypes: NamedTypeRegistry,
819
800
  defaultMapping: DefaultMappingOptions | undefined,
820
801
  rawDefaultParser: PslPrinterOptions['parseRawDefault'],
821
802
  pkColumns: ReadonlySet<string>,
@@ -849,8 +830,15 @@ function buildScalarField(
849
830
  // the Phase-1 authoring form a `native_enum` ref field takes — not a bare
850
831
  // name substitution. The printer renders `typeConstructor` when present and
851
832
  // composes `?`/`[]` exactly like any other field type.
852
- let typeName = resolution.pslType;
853
- let typeConstructor: PslTypeConstructorCall | undefined;
833
+ let typeName = resolution.pslType.name;
834
+ let typeConstructor: PslTypeConstructorCall | undefined = resolution.pslType.args
835
+ ? {
836
+ kind: 'typeConstructor',
837
+ path: [resolution.pslType.name],
838
+ args: resolution.pslType.args.map(positionalArg),
839
+ span: SYNTHETIC_SPAN,
840
+ }
841
+ : undefined;
854
842
  const enumPslName = enumNameMap.get(column.nativeType);
855
843
  if (enumPslName) {
856
844
  typeName = enumPslName;
@@ -861,9 +849,6 @@ function buildScalarField(
861
849
  span: SYNTHETIC_SPAN,
862
850
  };
863
851
  }
864
- if (resolution.nativeTypeAttribute && !enumPslName) {
865
- typeName = resolveNamedTypeName(namedTypes, resolution);
866
- }
867
852
 
868
853
  const attributes: PslFieldAttribute[] = [];
869
854
  const isId = isSinglePk && pkColumns.has(column.name);
@@ -1063,21 +1048,6 @@ function namedArg(name: string, value: string): PslAttributeArgument {
1063
1048
  return { kind: 'named', name, value, span: SYNTHETIC_SPAN };
1064
1049
  }
1065
1050
 
1066
- function buildNamedTypeDeclaration(entry: NamedTypeRegistryEntry): PslNamedTypeDeclaration {
1067
- const attribute = buildAttribute(
1068
- 'namedType',
1069
- entry.nativeTypeAttribute.name,
1070
- (entry.nativeTypeAttribute.args ?? []).map(positionalArg),
1071
- );
1072
- return {
1073
- kind: 'namedType',
1074
- name: entry.name,
1075
- baseType: entry.baseType,
1076
- attributes: [attribute],
1077
- span: SYNTHETIC_SPAN,
1078
- };
1079
- }
1080
-
1081
1051
  function escapePslString(value: string): string {
1082
1052
  return value
1083
1053
  .replace(/\\/g, '\\\\')
@@ -1222,135 +1192,16 @@ function buildTopLevelNameMap(
1222
1192
  .map((source) => `"${source}"`)
1223
1193
  .join(', ')}`,
1224
1194
  );
1225
- throw new Error(`PSL ${kind} name collisions detected:\n${details.join('\n')}`);
1195
+ throw postgresError(
1196
+ 'CONTRACT.NAME_DUPLICATE',
1197
+ `PSL ${kind} name collisions detected:\n${details.join('\n')}`,
1198
+ { meta: { kind, names: duplicates.map(([normalizedName]) => normalizedName) } },
1199
+ );
1226
1200
  }
1227
1201
 
1228
1202
  return results;
1229
1203
  }
1230
1204
 
1231
- function createReservedNamedTypeNames(
1232
- modelNames: ReadonlyMap<string, TopLevelNameResult>,
1233
- enumNameMap: ReadonlyMap<string, string>,
1234
- ): Set<string> {
1235
- const reservedNames = new Set<string>(PSL_SCALAR_TYPE_NAMES);
1236
-
1237
- for (const result of modelNames.values()) {
1238
- reservedNames.add(result.name);
1239
- }
1240
- for (const enumPslName of enumNameMap.values()) {
1241
- reservedNames.add(enumPslName);
1242
- }
1243
-
1244
- return reservedNames;
1245
- }
1246
-
1247
- function seedNamedTypeRegistry(
1248
- schemaIR: SqlSchemaIR,
1249
- typeMap: PslTypeMap,
1250
- enumNameMap: ReadonlyMap<string, string>,
1251
- reservedNames: ReadonlySet<string>,
1252
- ): NamedTypeRegistry {
1253
- type Seed = {
1254
- readonly baseType: string;
1255
- readonly desiredName: string;
1256
- readonly nativeTypeAttribute: PslNativeTypeAttribute;
1257
- };
1258
-
1259
- const seeds = new Map<string, Seed>();
1260
-
1261
- for (const tableName of Object.keys(schemaIR.tables).sort()) {
1262
- const table = schemaIR.tables[tableName];
1263
- if (!table) {
1264
- continue;
1265
- }
1266
-
1267
- for (const columnName of Object.keys(table.columns).sort()) {
1268
- const column = table.columns[columnName];
1269
- if (!column) {
1270
- continue;
1271
- }
1272
-
1273
- const resolution = typeMap.resolve(column.nativeType, table.annotations);
1274
- if (
1275
- 'unsupported' in resolution ||
1276
- enumNameMap.has(column.nativeType) ||
1277
- !resolution.nativeTypeAttribute
1278
- ) {
1279
- continue;
1280
- }
1281
-
1282
- const signatureKey = createNamedTypeSignatureKey(resolution);
1283
- if (!seeds.has(signatureKey)) {
1284
- seeds.set(signatureKey, {
1285
- baseType: resolution.pslType,
1286
- desiredName: toNamedTypeName(column.name),
1287
- nativeTypeAttribute: resolution.nativeTypeAttribute,
1288
- });
1289
- }
1290
- }
1291
- }
1292
-
1293
- const registry: NamedTypeRegistry = {
1294
- entriesByKey: new Map<string, NamedTypeRegistryEntry>(),
1295
- usedNames: new Set<string>(reservedNames),
1296
- };
1297
-
1298
- const sortedSeeds = [...seeds.entries()].sort((left, right) => {
1299
- const desiredNameComparison = left[1].desiredName.localeCompare(right[1].desiredName);
1300
- if (desiredNameComparison !== 0) {
1301
- return desiredNameComparison;
1302
- }
1303
- return left[0].localeCompare(right[0]);
1304
- });
1305
-
1306
- for (const [signatureKey, seed] of sortedSeeds) {
1307
- const name = createUniqueFieldName(seed.desiredName, registry.usedNames);
1308
- registry.entriesByKey.set(signatureKey, {
1309
- name,
1310
- baseType: seed.baseType,
1311
- nativeTypeAttribute: seed.nativeTypeAttribute,
1312
- });
1313
- registry.usedNames.add(name);
1314
- }
1315
-
1316
- return registry;
1317
- }
1318
-
1319
- function resolveNamedTypeName(
1320
- registry: NamedTypeRegistry,
1321
- resolution: {
1322
- readonly pslType: string;
1323
- readonly nativeType: string;
1324
- readonly typeParams?: Record<string, unknown>;
1325
- readonly nativeTypeAttribute?: PslNativeTypeAttribute;
1326
- },
1327
- ): string {
1328
- const key = createNamedTypeSignatureKey(resolution);
1329
- const existing = registry.entriesByKey.get(key);
1330
- if (existing) {
1331
- return existing.name;
1332
- }
1333
-
1334
- throw new Error(`Named type registry was not seeded for native type "${resolution.nativeType}"`);
1335
- }
1336
-
1337
- function createNamedTypeSignatureKey(resolution: {
1338
- readonly pslType: string;
1339
- readonly nativeType: string;
1340
- readonly typeParams?: Record<string, unknown>;
1341
- readonly nativeTypeAttribute?: PslNativeTypeAttribute;
1342
- }): string {
1343
- return JSON.stringify({
1344
- baseType: resolution.pslType,
1345
- nativeTypeAttribute: resolution.nativeTypeAttribute
1346
- ? {
1347
- name: resolution.nativeTypeAttribute.name,
1348
- args: resolution.nativeTypeAttribute.args ?? null,
1349
- }
1350
- : null,
1351
- });
1352
- }
1353
-
1354
1205
  function topologicalSort(
1355
1206
  models: PslModel[],
1356
1207
  tables: Record<string, SqlTableIR>,
@@ -1,8 +1,4 @@
1
- import type {
2
- PslNativeTypeAttribute,
3
- PslTypeMap,
4
- PslTypeResolution,
5
- } from '@prisma-next/family-sql/psl-infer';
1
+ import type { PslTypeMap, PslTypeResolution } from '@prisma-next/family-sql/psl-infer';
6
2
 
7
3
  const POSTGRES_TO_PSL: Record<string, string> = {
8
4
  text: 'String',
@@ -14,51 +10,46 @@ const POSTGRES_TO_PSL: Record<string, string> = {
14
10
  bigint: 'BigInt',
15
11
  float8: 'Float',
16
12
  'double precision': 'Float',
17
- numeric: 'Decimal',
18
- decimal: 'Decimal',
19
- timestamptz: 'DateTime',
20
- 'timestamp with time zone': 'DateTime',
21
- jsonb: 'Json',
13
+ jsonb: 'Jsonb',
22
14
  bytea: 'Bytes',
23
15
  };
24
16
 
25
- const PRESERVED_NATIVE_TYPES: Record<
26
- string,
27
- { readonly pslType: string; readonly attributeName: string }
28
- > = {
29
- 'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
30
- character: { pslType: 'String', attributeName: 'db.Char' },
31
- char: { pslType: 'String', attributeName: 'db.Char' },
32
- varchar: { pslType: 'String', attributeName: 'db.VarChar' },
33
- uuid: { pslType: 'String', attributeName: 'db.Uuid' },
34
- inet: { pslType: 'String', attributeName: 'db.Inet' },
35
- int2: { pslType: 'Int', attributeName: 'db.SmallInt' },
36
- smallint: { pslType: 'Int', attributeName: 'db.SmallInt' },
37
- float4: { pslType: 'Float', attributeName: 'db.Real' },
38
- real: { pslType: 'Float', attributeName: 'db.Real' },
39
- timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
40
- 'timestamp without time zone': { pslType: 'DateTime', attributeName: 'db.Timestamp' },
41
- date: { pslType: 'DateTime', attributeName: 'db.Date' },
42
- time: { pslType: 'DateTime', attributeName: 'db.Time' },
43
- 'time without time zone': { pslType: 'DateTime', attributeName: 'db.Time' },
44
- timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
45
- 'time with time zone': { pslType: 'DateTime', attributeName: 'db.Timetz' },
46
- json: { pslType: 'Json', attributeName: 'db.Json' },
17
+ const PRESERVED_NATIVE_TYPES: Record<string, string> = {
18
+ 'character varying': 'VarChar',
19
+ character: 'Char',
20
+ char: 'Char',
21
+ varchar: 'VarChar',
22
+ uuid: 'Uuid',
23
+ inet: 'Inet',
24
+ int2: 'SmallInt',
25
+ smallint: 'SmallInt',
26
+ float4: 'Real',
27
+ real: 'Real',
28
+ numeric: 'Numeric',
29
+ decimal: 'Numeric',
30
+ timestamp: 'Timestamp',
31
+ 'timestamp without time zone': 'Timestamp',
32
+ timestamptz: 'Timestamptz',
33
+ 'timestamp with time zone': 'Timestamptz',
34
+ date: 'Date',
35
+ time: 'Time',
36
+ 'time without time zone': 'Time',
37
+ timetz: 'Timetz',
38
+ 'time with time zone': 'Timetz',
39
+ json: 'Json',
47
40
  };
48
41
 
49
- const PARAMETERIZED_NATIVE_TYPES: Record<
50
- string,
51
- { readonly pslType: string; readonly attributeName: string }
52
- > = {
53
- 'character varying': { pslType: 'String', attributeName: 'db.VarChar' },
54
- character: { pslType: 'String', attributeName: 'db.Char' },
55
- char: { pslType: 'String', attributeName: 'db.Char' },
56
- varchar: { pslType: 'String', attributeName: 'db.VarChar' },
57
- numeric: { pslType: 'Decimal', attributeName: 'db.Numeric' },
58
- timestamp: { pslType: 'DateTime', attributeName: 'db.Timestamp' },
59
- timestamptz: { pslType: 'DateTime', attributeName: 'db.Timestamptz' },
60
- time: { pslType: 'DateTime', attributeName: 'db.Time' },
61
- timetz: { pslType: 'DateTime', attributeName: 'db.Timetz' },
42
+ const PARAMETERIZED_NATIVE_TYPES: Record<string, string> = {
43
+ 'character varying': 'VarChar',
44
+ character: 'Char',
45
+ char: 'Char',
46
+ varchar: 'VarChar',
47
+ numeric: 'Numeric',
48
+ decimal: 'Numeric',
49
+ timestamp: 'Timestamp',
50
+ timestamptz: 'Timestamptz',
51
+ time: 'Time',
52
+ timetz: 'Timetz',
62
53
  };
63
54
 
64
55
  const PARAMETERIZED_TYPE_PATTERN = /^(.+?)\((.+)\)$/;
@@ -67,14 +58,6 @@ function getOwnMappingValue(map: Record<string, string>, key: string): string |
67
58
  return Object.hasOwn(map, key) ? map[key] : undefined;
68
59
  }
69
60
 
70
- function getOwnRecordValue<T>(map: Record<string, T>, key: string): T | undefined {
71
- return Object.hasOwn(map, key) ? map[key] : undefined;
72
- }
73
-
74
- function createNativeTypeAttribute(name: string, args?: readonly string[]): PslNativeTypeAttribute {
75
- return args && args.length > 0 ? { name, args } : { name };
76
- }
77
-
78
61
  function splitTypeParameterList(params: string): readonly string[] {
79
62
  return params
80
63
  .split(',')
@@ -86,39 +69,34 @@ export function createPostgresTypeMap(enumTypeNames?: ReadonlySet<string>): PslT
86
69
  return {
87
70
  resolve(nativeType: string): PslTypeResolution {
88
71
  if (enumTypeNames?.has(nativeType)) {
89
- return { pslType: nativeType, nativeType };
72
+ return { pslType: { name: nativeType }, nativeType };
90
73
  }
91
74
 
92
75
  const paramMatch = nativeType.match(PARAMETERIZED_TYPE_PATTERN);
93
76
  if (paramMatch) {
94
77
  const [, baseType = nativeType, params = ''] = paramMatch;
95
- const template = getOwnRecordValue(PARAMETERIZED_NATIVE_TYPES, baseType);
96
- if (template) {
78
+ const typeName = getOwnMappingValue(PARAMETERIZED_NATIVE_TYPES, baseType);
79
+ if (typeName) {
97
80
  return {
98
- pslType: template.pslType,
81
+ pslType: { name: typeName, args: splitTypeParameterList(params) },
99
82
  nativeType,
100
83
  typeParams: { baseType, params },
101
- nativeTypeAttribute: createNativeTypeAttribute(
102
- template.attributeName,
103
- splitTypeParameterList(params),
104
- ),
105
84
  };
106
85
  }
107
86
  }
108
87
 
109
- const preservedType = getOwnRecordValue(PRESERVED_NATIVE_TYPES, nativeType);
88
+ const preservedType = getOwnMappingValue(PRESERVED_NATIVE_TYPES, nativeType);
110
89
  if (preservedType) {
111
90
  return {
112
- pslType: preservedType.pslType,
91
+ pslType: { name: preservedType },
113
92
  nativeType,
114
- nativeTypeAttribute: createNativeTypeAttribute(preservedType.attributeName),
115
93
  };
116
94
  }
117
95
 
118
96
  const pslType = getOwnMappingValue(POSTGRES_TO_PSL, nativeType);
119
97
  if (pslType) {
120
98
  return {
121
- pslType,
99
+ pslType: { name: pslType },
122
100
  nativeType,
123
101
  };
124
102
  }