@prisma-next/family-sql 0.13.0-dev.28 → 0.13.0-dev.29

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 (32) hide show
  1. package/dist/{authoring-type-constructors-CN60DEWb.mjs → authoring-type-constructors-CjFfO6LM.mjs} +20 -20
  2. package/dist/authoring-type-constructors-CjFfO6LM.mjs.map +1 -0
  3. package/dist/{control-adapter-B_s-UMXg.d.mts → control-adapter-Cmw9LvEP.d.mts} +4 -20
  4. package/dist/control-adapter-Cmw9LvEP.d.mts.map +1 -0
  5. package/dist/control-adapter.d.mts +1 -1
  6. package/dist/control.d.mts +4 -4
  7. package/dist/control.d.mts.map +1 -1
  8. package/dist/control.mjs +17 -73
  9. package/dist/control.mjs.map +1 -1
  10. package/dist/migration.d.mts +1 -1
  11. package/dist/pack.d.mts +3 -3
  12. package/dist/pack.mjs +1 -1
  13. package/dist/schema-verify.d.mts +1 -1
  14. package/dist/schema-verify.mjs +1 -1
  15. package/dist/{types-BR5vHjvX.d.mts → types-kgstZ_Zd.d.mts} +2 -2
  16. package/dist/{types-BR5vHjvX.d.mts.map → types-kgstZ_Zd.d.mts.map} +1 -1
  17. package/dist/{verify-sql-schema-DcMaT5Zj.d.mts → verify-sql-schema-thU-jKpf.d.mts} +2 -14
  18. package/dist/verify-sql-schema-thU-jKpf.d.mts.map +1 -0
  19. package/dist/{verify-sql-schema-CC7EOR2x.mjs → verify-sql-schema-xT4udQLQ.mjs} +10 -117
  20. package/dist/verify-sql-schema-xT4udQLQ.mjs.map +1 -0
  21. package/package.json +21 -21
  22. package/src/core/authoring-entity-types.ts +20 -20
  23. package/src/core/control-adapter.ts +2 -37
  24. package/src/core/control-instance.ts +0 -4
  25. package/src/core/migrations/contract-to-schema-ir.ts +4 -83
  26. package/src/core/psl-contract-infer/postgres-type-map.ts +5 -22
  27. package/src/core/psl-contract-infer/sql-schema-ir-to-psl-ast.ts +17 -70
  28. package/src/core/schema-verify/verify-sql-schema.ts +8 -144
  29. package/dist/authoring-type-constructors-CN60DEWb.mjs.map +0 -1
  30. package/dist/control-adapter-B_s-UMXg.d.mts.map +0 -1
  31. package/dist/verify-sql-schema-CC7EOR2x.mjs.map +0 -1
  32. package/dist/verify-sql-schema-DcMaT5Zj.d.mts.map +0 -1
@@ -1,18 +1,14 @@
1
1
  import type { ColumnDefault, Contract, JsonValue } from '@prisma-next/contract/types';
2
2
  import type { MigrationPlannerConflict } from '@prisma-next/framework-components/control';
3
- import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
4
3
  import {
5
4
  type CheckConstraint,
6
5
  type ForeignKey,
7
6
  type Index,
8
- isPostgresEnumStorageEntry,
9
7
  isStorageTypeInstance,
10
- type PostgresEnumStorageEntry,
11
8
  type SqlStorage,
12
9
  type StorageColumn,
13
10
  StorageTable,
14
11
  type StorageTypeInstance,
15
- toStorageTypeInstance,
16
12
  type UniqueConstraint,
17
13
  } from '@prisma-next/sql-contract/types';
18
14
  import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
@@ -26,7 +22,6 @@ import type {
26
22
  SqlTableIR,
27
23
  SqlUniqueIR,
28
24
  } from '@prisma-next/sql-schema-ir/types';
29
- import { blindCast } from '@prisma-next/utils/casts';
30
25
  import { ifDefined } from '@prisma-next/utils/defined';
31
26
 
32
27
  /**
@@ -105,17 +100,7 @@ function convertColumn(
105
100
  };
106
101
  }
107
102
 
108
- /**
109
- * `storageTypes` is polymorphic per Decision 18 (Option B) — codec-typed
110
- * entries match `StorageTypeInstance`; enum entries match the structural
111
- * `PostgresEnumStorageEntry` shape (Postgres-only; cross-domain layering
112
- * keeps target IR classes out of the family layer). Both shapes resolve
113
- * into the same `(codecId, nativeType, typeParams)` triplet at the
114
- * column-resolution boundary so downstream walks stay uniform.
115
- */
116
- type ResolvedStorageTypes = Readonly<
117
- Record<string, StorageTypeInstance | PostgresEnumStorageEntry>
118
- >;
103
+ type ResolvedStorageTypes = Readonly<Record<string, StorageTypeInstance>>;
119
104
 
120
105
  function resolveColumnTypeMetadata(
121
106
  column: StorageColumn,
@@ -130,13 +115,6 @@ function resolveColumnTypeMetadata(
130
115
  `Column references storage type "${column.typeRef}" but it is not defined in storage.types.`,
131
116
  );
132
117
  }
133
- if (isPostgresEnumStorageEntry(referenced)) {
134
- return {
135
- codecId: referenced.codecId,
136
- nativeType: referenced.nativeType,
137
- typeParams: { values: referenced.values } as Record<string, unknown>,
138
- };
139
- }
140
118
  if (isStorageTypeInstance(referenced)) {
141
119
  return {
142
120
  codecId: referenced.codecId,
@@ -145,7 +123,7 @@ function resolveColumnTypeMetadata(
145
123
  };
146
124
  }
147
125
  throw new Error(
148
- `Storage type "${column.typeRef}" has an unknown polymorphic kind; expected codec-instance or postgres-enum.`,
126
+ `Storage type "${column.typeRef}" has an unknown polymorphic kind; expected a codec-typed StorageTypeInstance.`,
149
127
  );
150
128
  }
151
129
 
@@ -393,21 +371,9 @@ export function contractToSchemaIR(
393
371
  }
394
372
 
395
373
  const storage = contract.storage;
396
- const allTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {
374
+ const storageTypes: ResolvedStorageTypes = {
397
375
  ...((storage.types ?? {}) as ResolvedStorageTypes),
398
376
  };
399
- for (const ns of Object.values(storage.namespaces)) {
400
- const nsEnums = ns.entries['type'];
401
- if (nsEnums) {
402
- for (const [k, v] of Object.entries(nsEnums)) {
403
- allTypes[k] = blindCast<
404
- PostgresEnumStorageEntry | StorageTypeInstance,
405
- 'entries.type holds postgres-specific enum entries at runtime'
406
- >(v);
407
- }
408
- }
409
- }
410
- const storageTypes = allTypes as ResolvedStorageTypes;
411
377
  const tables: Record<string, SqlTableIR> = {};
412
378
  for (const ns of Object.values(storage.namespaces)) {
413
379
  for (const [tableName, tableDefRaw] of Object.entries(ns.entries.table ?? {})) {
@@ -445,66 +411,21 @@ export function contractToSchemaIR(
445
411
  };
446
412
  }
447
413
 
448
- /**
449
- * Normalises a native enum storage entry to the codec-typed annotation shape
450
- * `{codecId, nativeType, typeParams}` the introspector writes and
451
- * `readExistingEnumValues` reads (`existing.codecId` + `existing.typeParams.values`).
452
- * Without this the projector would emit the raw `PostgresEnumStorageEntry`
453
- * shape (top-level `values`, no `typeParams`) and the enum would read as new.
454
- */
455
- function normalizeEnumAnnotation(entry: PostgresEnumStorageEntry): StorageTypeInstance {
456
- return toStorageTypeInstance({
457
- codecId: entry.codecId,
458
- nativeType: entry.nativeType,
459
- typeParams: { values: entry.values },
460
- });
461
- }
462
-
463
414
  function deriveAnnotations(
464
415
  storage: SqlStorage,
465
416
  annotationNamespace: string,
466
- resolveEnumNamespaceSchema: EnumNamespaceSchemaResolver | undefined,
417
+ _resolveEnumNamespaceSchema: EnumNamespaceSchemaResolver | undefined,
467
418
  ): SqlAnnotations | undefined {
468
419
  const storageTypes: Record<string, StorageTypeInstance> = {};
469
- const enumTypes: Record<string, Record<string, StorageTypeInstance>> = {};
470
-
471
- const addEnum = (namespaceId: string, entry: PostgresEnumStorageEntry): void => {
472
- const schemaName = resolveEnumNamespaceSchema
473
- ? resolveEnumNamespaceSchema(storage, namespaceId)
474
- : 'public';
475
- const bySchema = enumTypes[schemaName] ?? {};
476
- bySchema[entry.nativeType] = normalizeEnumAnnotation(entry);
477
- enumTypes[schemaName] = bySchema;
478
- };
479
420
 
480
- // Top-level `storage.types`: non-enum codec entries (vector, decimal, …) keyed
481
- // by bare `nativeType`. Post-S1.B enums live in `namespaces[*].entries.type`;
482
- // a defensive top-level enum is nested under the unbound coordinate's schema.
483
421
  for (const typeInstance of Object.values((storage.types ?? {}) as ResolvedStorageTypes)) {
484
- if (isPostgresEnumStorageEntry(typeInstance)) {
485
- addEnum(UNBOUND_NAMESPACE_ID, typeInstance);
486
- continue;
487
- }
488
422
  if (isStorageTypeInstance(typeInstance)) {
489
423
  storageTypes[typeInstance.nativeType] = typeInstance;
490
424
  }
491
425
  }
492
426
 
493
- // Namespace-scoped enums: nested by live schema so two namespaces sharing a
494
- // native type resolve to distinct live-database types, matching the target's
495
- // `readExistingEnumValues` read side (`enumTypes[schema][nativeType]`).
496
- for (const [namespaceId, ns] of Object.entries(storage.namespaces)) {
497
- const nsEnums = ns.entries['type'];
498
- if (!nsEnums) continue;
499
- for (const entry of Object.values(nsEnums)) {
500
- if (!isPostgresEnumStorageEntry(entry)) continue;
501
- addEnum(namespaceId, entry);
502
- }
503
- }
504
-
505
427
  const envelope = {
506
428
  ...(Object.keys(storageTypes).length > 0 ? { storageTypes } : {}),
507
- ...(Object.keys(enumTypes).length > 0 ? { enumTypes } : {}),
508
429
  };
509
430
  if (Object.keys(envelope).length === 0) return undefined;
510
431
  return { [annotationNamespace]: envelope };
@@ -63,8 +63,6 @@ const PARAMETERIZED_NATIVE_TYPES: Record<
63
63
 
64
64
  const PARAMETERIZED_TYPE_PATTERN = /^(.+?)\((.+)\)$/;
65
65
 
66
- const ENUM_CODEC_ID = 'pg/enum@1';
67
-
68
66
  function getOwnMappingValue(map: Record<string, string>, key: string): string | undefined {
69
67
  return Object.hasOwn(map, key) ? map[key] : undefined;
70
68
  }
@@ -132,30 +130,15 @@ export function createPostgresTypeMap(enumTypeNames?: ReadonlySet<string>): PslT
132
130
 
133
131
  export function extractEnumInfo(annotations?: Record<string, unknown>): EnumInfo {
134
132
  const pgAnnotations = annotations?.['pg'] as Record<string, unknown> | undefined;
135
- const enumTypes = pgAnnotations?.['enumTypes'] as
136
- | Record<
137
- string,
138
- Record<
139
- string,
140
- { codecId: string; nativeType: string; typeParams?: Record<string, unknown> }
141
- >
142
- >
143
- | undefined;
133
+ const nativeEnumTypeNames = pgAnnotations?.['nativeEnumTypeNames'];
144
134
 
145
135
  const typeNames = new Set<string>();
146
136
  const definitions = new Map<string, readonly string[]>();
147
137
 
148
- if (enumTypes) {
149
- for (const bySchema of Object.values(enumTypes)) {
150
- for (const typeInstance of Object.values(bySchema)) {
151
- if (typeInstance.codecId === ENUM_CODEC_ID) {
152
- const nativeType = typeInstance.nativeType;
153
- typeNames.add(nativeType);
154
- const values = typeInstance.typeParams?.['values'];
155
- if (Array.isArray(values) && values.every((v): v is string => typeof v === 'string')) {
156
- definitions.set(nativeType, values);
157
- }
158
- }
138
+ if (Array.isArray(nativeEnumTypeNames)) {
139
+ for (const name of nativeEnumTypeNames) {
140
+ if (typeof name === 'string') {
141
+ typeNames.add(name);
159
142
  }
160
143
  }
161
144
  }
@@ -3,7 +3,6 @@ import type {
3
3
  PslAttribute,
4
4
  PslAttributeArgument,
5
5
  PslDocumentAst,
6
- PslEnum,
7
6
  PslField,
8
7
  PslFieldAttribute,
9
8
  PslModel,
@@ -20,11 +19,10 @@ import {
20
19
  import type { SqlColumnIR, SqlSchemaIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
21
20
  import type { DefaultMappingOptions } from './default-mapping';
22
21
  import { mapDefault } from './default-mapping';
23
- import { toEnumName, toFieldName, toModelName, toNamedTypeName } from './name-transforms';
22
+ import { toFieldName, toModelName, toNamedTypeName } from './name-transforms';
24
23
  import { createPostgresDefaultMapping } from './postgres-default-mapping';
25
24
  import { createPostgresTypeMap, extractEnumInfo } from './postgres-type-map';
26
25
  import type {
27
- EnumInfo,
28
26
  PslNativeTypeAttribute,
29
27
  PslPrinterOptions,
30
28
  PslTypeMap,
@@ -82,10 +80,19 @@ type TopLevelNameResult = {
82
80
  */
83
81
  export function sqlSchemaIrToPslAst(schemaIR: SqlSchemaIR): PslDocumentAst {
84
82
  const enumInfo = extractEnumInfo(schemaIR.annotations);
83
+ if (enumInfo.typeNames.size > 0) {
84
+ const names = [...enumInfo.typeNames].join(', ');
85
+ throw new Error(
86
+ `contract infer: the database contains native Postgres enum type(s): ${names}. ` +
87
+ 'Native Postgres enums (CREATE TYPE … AS ENUM) are not adoptable by contract infer. ' +
88
+ 'Drop the native type and replace each column with a text column carrying a CHECK constraint, ' +
89
+ `then re-run contract infer. The domain enum (enum Name { @@type("pg/text@1") … }) authoring ` +
90
+ 'surface generates the required check automatically.',
91
+ );
92
+ }
85
93
  const options: PslPrinterOptions = {
86
- typeMap: createPostgresTypeMap(enumInfo.typeNames),
94
+ typeMap: createPostgresTypeMap(new Set()),
87
95
  defaultMapping: createPostgresDefaultMapping(),
88
- enumInfo,
89
96
  parseRawDefault,
90
97
  };
91
98
 
@@ -93,12 +100,7 @@ export function sqlSchemaIrToPslAst(schemaIR: SqlSchemaIR): PslDocumentAst {
93
100
  }
94
101
 
95
102
  function buildPslDocumentAst(schemaIR: SqlSchemaIR, options: PslPrinterOptions): PslDocumentAst {
96
- const { typeMap, defaultMapping, enumInfo, parseRawDefault: rawDefaultParser } = options;
97
- const emptyEnumInfo: EnumInfo = {
98
- typeNames: new Set<string>(),
99
- definitions: new Map<string, readonly string[]>(),
100
- };
101
- const { typeNames: enumTypeNames, definitions: enumDefinitions } = enumInfo ?? emptyEnumInfo;
103
+ const { typeMap, defaultMapping, parseRawDefault: rawDefaultParser } = options;
102
104
 
103
105
  const modelNames = buildTopLevelNameMap(
104
106
  Object.keys(schemaIR.tables),
@@ -106,20 +108,15 @@ function buildPslDocumentAst(schemaIR: SqlSchemaIR, options: PslPrinterOptions):
106
108
  'model',
107
109
  'table',
108
110
  );
109
- const enumNames = buildTopLevelNameMap(enumTypeNames, toEnumName, 'enum', 'enum type');
110
- assertNoCrossKindNameCollisions(modelNames, enumNames);
111
111
 
112
112
  const modelNameMap = new Map(
113
113
  [...modelNames].map(([tableName, result]) => [tableName, result.name]),
114
114
  );
115
- const enumNameMap = new Map(
116
- [...enumNames].map(([pgTypeName, result]) => [pgTypeName, result.name]),
117
- );
118
- const reservedNamedTypeNames = createReservedNamedTypeNames(modelNames, enumNames);
115
+ const reservedNamedTypeNames = createReservedNamedTypeNames(modelNames);
119
116
 
120
117
  const fieldNamesByTable = buildFieldNamesByTable(schemaIR.tables);
121
118
  const { relationsByTable } = inferRelations(schemaIR.tables, modelNameMap);
122
- const namedTypes = seedNamedTypeRegistry(schemaIR, typeMap, enumNameMap, reservedNamedTypeNames);
119
+ const namedTypes = seedNamedTypeRegistry(schemaIR, typeMap, new Map(), reservedNamedTypeNames);
123
120
 
124
121
  const models: PslModel[] = [];
125
122
  for (const table of Object.values(schemaIR.tables)) {
@@ -127,7 +124,7 @@ function buildPslDocumentAst(schemaIR: SqlSchemaIR, options: PslPrinterOptions):
127
124
  buildModel(
128
125
  table,
129
126
  typeMap,
130
- enumNameMap,
127
+ new Map(),
131
128
  fieldNamesByTable,
132
129
  namedTypes,
133
130
  defaultMapping,
@@ -139,13 +136,6 @@ function buildPslDocumentAst(schemaIR: SqlSchemaIR, options: PslPrinterOptions):
139
136
 
140
137
  const sortedModels = topologicalSort(models, schemaIR.tables, modelNameMap);
141
138
 
142
- const enums: PslEnum[] = [];
143
- for (const [pgTypeName, values] of enumDefinitions) {
144
- const enumName = enumNames.get(pgTypeName) as TopLevelNameResult;
145
- enums.push(buildEnum(enumName, values));
146
- }
147
- enums.sort((a, b) => a.name.localeCompare(b.name));
148
-
149
139
  const namedTypeEntries = [...namedTypes.entriesByKey.values()].sort((a, b) =>
150
140
  a.name.localeCompare(b.name),
151
141
  );
@@ -171,7 +161,7 @@ function buildPslDocumentAst(schemaIR: SqlSchemaIR, options: PslPrinterOptions):
171
161
  makePslNamespace({
172
162
  kind: 'namespace',
173
163
  name: UNSPECIFIED_PSL_NAMESPACE_ID,
174
- entries: makePslNamespaceEntries(sortedModels, enums, [], []),
164
+ entries: makePslNamespaceEntries(sortedModels, [], []),
175
165
  span: SYNTHETIC_SPAN,
176
166
  }),
177
167
  ],
@@ -485,24 +475,6 @@ function namedArg(name: string, value: string): PslAttributeArgument {
485
475
  return { kind: 'named', name, value, span: SYNTHETIC_SPAN };
486
476
  }
487
477
 
488
- function buildEnum(name: TopLevelNameResult, values: readonly string[]): PslEnum {
489
- const attrs: PslAttribute[] = [];
490
- if (name.map) {
491
- attrs.push(buildMapAttribute('enum', name.map));
492
- }
493
- return {
494
- kind: 'enum',
495
- name: name.name,
496
- values: values.map((value) => ({
497
- kind: 'enumValue',
498
- name: value,
499
- span: SYNTHETIC_SPAN,
500
- })),
501
- attributes: attrs,
502
- span: SYNTHETIC_SPAN,
503
- };
504
- }
505
-
506
478
  function buildNamedTypeDeclaration(entry: NamedTypeRegistryEntry): PslNamedTypeDeclaration {
507
479
  const attribute = buildAttribute(
508
480
  'namedType',
@@ -647,29 +619,8 @@ function buildTopLevelNameMap(
647
619
  return results;
648
620
  }
649
621
 
650
- function assertNoCrossKindNameCollisions(
651
- modelNames: ReadonlyMap<string, TopLevelNameResult>,
652
- enumNames: ReadonlyMap<string, TopLevelNameResult>,
653
- ): void {
654
- const enumSourceByName = new Map([...enumNames].map(([source, result]) => [result.name, source]));
655
-
656
- const collisions = [...modelNames.entries()]
657
- .map(([tableName, result]) => {
658
- const enumSource = enumSourceByName.get(result.name);
659
- return enumSource
660
- ? `- identifier "${result.name}" from table "${tableName}" collides with enum type "${enumSource}"`
661
- : undefined;
662
- })
663
- .filter((detail): detail is string => detail !== undefined);
664
-
665
- if (collisions.length > 0) {
666
- throw new Error(`PSL top-level name collisions detected:\n${collisions.join('\n')}`);
667
- }
668
- }
669
-
670
622
  function createReservedNamedTypeNames(
671
623
  modelNames: ReadonlyMap<string, TopLevelNameResult>,
672
- enumNames: ReadonlyMap<string, TopLevelNameResult>,
673
624
  ): Set<string> {
674
625
  const reservedNames = new Set<string>(PSL_SCALAR_TYPE_NAMES);
675
626
 
@@ -677,10 +628,6 @@ function createReservedNamedTypeNames(
677
628
  reservedNames.add(result.name);
678
629
  }
679
630
 
680
- for (const result of enumNames.values()) {
681
- reservedNames.add(result.name);
682
- }
683
-
684
631
  return reservedNames;
685
632
  }
686
633
 
@@ -18,9 +18,7 @@ import type {
18
18
  } from '@prisma-next/framework-components/control';
19
19
 
20
20
  import {
21
- isPostgresEnumStorageEntry,
22
21
  isStorageTypeInstance,
23
- type PostgresEnumStorageEntry,
24
22
  type SqlStorage,
25
23
  type StorageColumn,
26
24
  StorageTable,
@@ -28,7 +26,6 @@ import {
28
26
  } from '@prisma-next/sql-contract/types';
29
27
  import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
30
28
  import { canonicalStringify } from '@prisma-next/utils/canonical-stringify';
31
- import { blindCast } from '@prisma-next/utils/casts';
32
29
  import { ifDefined } from '@prisma-next/utils/defined';
33
30
  import { extractCodecControlHooks } from '../assembly';
34
31
  import { resolveValueSetValues } from '../migrations/contract-to-schema-ir';
@@ -92,22 +89,6 @@ export interface VerifySqlSchemaOptions {
92
89
  * with contract native types (e.g., Postgres 'varchar' → 'character varying').
93
90
  */
94
91
  readonly normalizeNativeType?: NativeTypeNormalizer;
95
- /**
96
- * Bridging adapter that resolves the existing values for a `PostgresEnumStorageEntry`
97
- * (looked up by its native type) from the introspected schema IR. Targets
98
- * supply this so the family-level verifier can walk `PostgresEnumStorageEntry` instances
99
- * natively without reaching into target-specific `schema.annotations`
100
- * shapes itself.
101
- *
102
- * Returning `null` indicates the type is missing from the database; the
103
- * verifier emits a `type_missing` issue. A non-null array triggers a
104
- * value-set comparison against the contract's `PostgresEnumStorageEntry.values`.
105
- */
106
- readonly resolveExistingEnumValues?: (
107
- schema: SqlSchemaIR,
108
- enumType: PostgresEnumStorageEntry,
109
- namespaceId: string,
110
- ) => readonly string[] | null;
111
92
  }
112
93
 
113
94
  /**
@@ -129,7 +110,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
129
110
  typeMetadataRegistry,
130
111
  normalizeDefault,
131
112
  normalizeNativeType,
132
- resolveExistingEnumValues,
133
113
  } = options;
134
114
  const startTime = Date.now();
135
115
 
@@ -138,28 +118,7 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
138
118
 
139
119
  const { contractStorageHash, contractProfileHash, contractTarget } =
140
120
  extractContractMetadata(contract);
141
- // Column `typeRef` resolution map: keyed by the bare contract type name
142
- // (columns carry bare `typeRef`s). Used by `verifySchemaTables` only.
143
- const allStorageTypesMap: Record<string, PostgresEnumStorageEntry | StorageTypeInstance> = {
144
- ...((contract.storage.types ?? {}) as Record<
145
- string,
146
- PostgresEnumStorageEntry | StorageTypeInstance
147
- >),
148
- };
149
- for (const ns of Object.values(contract.storage.namespaces)) {
150
- const nsEnums = blindCast<
151
- { readonly type?: Readonly<Record<string, PostgresEnumStorageEntry | StorageTypeInstance>> },
152
- 'postgres target namespace entries carry a type slot beyond the family-shared SqlNamespace.entries type'
153
- >(ns.entries).type;
154
- if (nsEnums) {
155
- for (const [k, v] of Object.entries(nsEnums)) {
156
- allStorageTypesMap[k] = v;
157
- }
158
- }
159
- }
160
- const storageTypes = allStorageTypesMap as Readonly<
161
- Record<string, PostgresEnumStorageEntry | StorageTypeInstance>
162
- >;
121
+ const storageTypes: Readonly<Record<string, StorageTypeInstance>> = contract.storage.types ?? {};
163
122
  const { issues, rootChildren } = verifySchemaTables({
164
123
  contract,
165
124
  schema,
@@ -173,13 +132,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
173
132
 
174
133
  validateFrameworkComponentsForExtensions(contract, options.frameworkComponents);
175
134
 
176
- // Verify storage type instances. Codec-typed `storage.types` entries dispatch
177
- // through the generic codec-hook `verifyType` path (keyed by bare name).
178
- // `PostgresEnumStorageEntry` enums are walked natively *per namespace* (using
179
- // the bridging adapter `resolveExistingEnumValues`) so two namespaces that
180
- // declare an enum with the same name are each verified with their own
181
- // namespace coordinate — a bare-name aggregation would collapse them
182
- // (last-write-wins) and verify only one.
183
135
  const typeNodes: SchemaVerificationNode[] = [];
184
136
  // Storage-type findings dispatch through the same control policy as tables
185
137
  // and columns: each issue's disposition (fail / warn / suppress) is resolved
@@ -233,29 +185,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
233
185
  }
234
186
  }
235
187
 
236
- // Namespace-scoped enums, verified per `(namespaceId, typeName)`.
237
- for (const nsId of Object.keys(contract.storage.namespaces)) {
238
- const ns = contract.storage.namespaces[nsId];
239
- if (!ns) continue;
240
- const nsEnums = ns.entries['type'];
241
- if (!nsEnums) continue;
242
- for (const [typeName, entry] of Object.entries(nsEnums)) {
243
- if (!isPostgresEnumStorageEntry(entry)) continue;
244
- pushTypeNode(
245
- typeName,
246
- `storage.namespaces.${nsId}.entries.type.${typeName}`,
247
- verifyEnumType({
248
- typeName,
249
- typeInstance: entry,
250
- schema,
251
- resolveExistingEnumValues,
252
- namespaceId: nsId,
253
- }),
254
- effectiveControlPolicy(entry.control, contract.defaultControlPolicy),
255
- );
256
- }
257
- }
258
-
259
188
  if (typeNodes.length > 0) {
260
189
  const typesStatus: VerificationStatus = typeNodes.some((n) => n.status === 'fail')
261
190
  ? 'fail'
@@ -321,63 +250,6 @@ export function verifySqlSchema(options: VerifySqlSchemaOptions): VerifyDatabase
321
250
  };
322
251
  }
323
252
 
324
- /**
325
- * Native verification walk for `PostgresEnumStorageEntry` instances (no codec hook).
326
- *
327
- * Bridges the native `PostgresEnumStorageEntry.values` against the introspected schema
328
- * IR via the target-supplied `resolveExistingEnumValues` adapter. Without an
329
- * adapter, the verifier conservatively reports the enum as missing — there
330
- * is no other way for the family layer to learn about live enum types.
331
- */
332
- function verifyEnumType(options: {
333
- readonly typeName: string;
334
- readonly typeInstance: PostgresEnumStorageEntry;
335
- readonly schema: SqlSchemaIR;
336
- readonly namespaceId: string;
337
- readonly resolveExistingEnumValues?:
338
- | ((
339
- schema: SqlSchemaIR,
340
- enumType: PostgresEnumStorageEntry,
341
- namespaceId: string,
342
- ) => readonly string[] | null)
343
- | undefined;
344
- }): readonly SchemaIssue[] {
345
- const { typeName, typeInstance, schema, namespaceId, resolveExistingEnumValues } = options;
346
- const desired = typeInstance.values;
347
- const existing = resolveExistingEnumValues?.(schema, typeInstance, namespaceId) ?? null;
348
- if (!existing) {
349
- return [
350
- {
351
- kind: 'type_missing',
352
- typeName,
353
- namespaceId,
354
- message: `Type "${typeName}" is missing from database`,
355
- },
356
- ];
357
- }
358
- if (arraysEqual(existing, desired)) {
359
- return [];
360
- }
361
- const existingSet = new Set(existing);
362
- const desiredSet = new Set(desired);
363
- const addedValues = desired.filter((v) => !existingSet.has(v));
364
- const removedValues = existing.filter((v) => !desiredSet.has(v));
365
- const message =
366
- removedValues.length === 0
367
- ? `Enum type "${typeName}" needs new values: ${addedValues.join(', ')}`
368
- : `Enum type "${typeName}" values changed (requires rebuild): +[${addedValues.join(', ')}] -[${removedValues.join(', ')}]`;
369
- return [
370
- {
371
- kind: 'enum_values_changed' as const,
372
- namespaceId,
373
- typeName,
374
- addedValues,
375
- removedValues,
376
- message,
377
- },
378
- ];
379
- }
380
-
381
253
  function extractContractMetadata(contract: Contract<SqlStorage>): {
382
254
  contractStorageHash: SqlStorage['storageHash'];
383
255
  contractProfileHash?: Contract<SqlStorage>['profileHash'] | undefined;
@@ -399,7 +271,7 @@ function verifySchemaTables(options: {
399
271
  strict: boolean;
400
272
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
401
273
  codecHooks: Map<string, CodecControlHooks>;
402
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
274
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>;
403
275
  normalizeDefault?: DefaultNormalizer;
404
276
  normalizeNativeType?: NativeTypeNormalizer;
405
277
  }): { issues: SchemaIssue[]; rootChildren: SchemaVerificationNode[] } {
@@ -533,7 +405,7 @@ function verifyTableChildren(options: {
533
405
  strict: boolean;
534
406
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
535
407
  codecHooks: Map<string, CodecControlHooks>;
536
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
408
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>;
537
409
  normalizeDefault?: DefaultNormalizer;
538
410
  normalizeNativeType?: NativeTypeNormalizer;
539
411
  contractStorage: SqlStorage;
@@ -717,7 +589,6 @@ function verifyTableChildren(options: {
717
589
  // Verify check constraints when the contract declares checks for this table OR
718
590
  // when strict mode is on (so extra live checks on zero-check tables are detected).
719
591
  // schemaTable.checks carries the introspected live checks (parsed value sets).
720
- // This call is additive: verifyEnumType (the native enum path) is untouched.
721
592
  const contractCheckIRs = (contractTable.checks ?? []).map((c) => ({
722
593
  name: c.name,
723
594
  column: c.column,
@@ -751,7 +622,7 @@ function collectContractColumnNodes(options: {
751
622
  strict: boolean;
752
623
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
753
624
  codecHooks: Map<string, CodecControlHooks>;
754
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
625
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>;
755
626
  normalizeDefault?: DefaultNormalizer;
756
627
  normalizeNativeType?: NativeTypeNormalizer;
757
628
  }): SchemaVerificationNode[] {
@@ -889,7 +760,7 @@ function verifyColumn(options: {
889
760
  strict: boolean;
890
761
  typeMetadataRegistry: ReadonlyMap<string, { nativeType?: string }>;
891
762
  codecHooks: Map<string, CodecControlHooks>;
892
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>;
763
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>;
893
764
  normalizeDefault?: DefaultNormalizer;
894
765
  normalizeNativeType?: NativeTypeNormalizer;
895
766
  }): SchemaVerificationNode {
@@ -1260,7 +1131,7 @@ function validateFrameworkComponentsForExtensions(
1260
1131
  */
1261
1132
  function renderExpectedNativeType(
1262
1133
  contractColumn: StorageColumn,
1263
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>,
1134
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>,
1264
1135
  codecHooks: Map<string, CodecControlHooks>,
1265
1136
  context?: {
1266
1137
  readonly tableName: string;
@@ -1290,7 +1161,7 @@ function renderExpectedNativeType(
1290
1161
 
1291
1162
  function resolveContractColumnTypeMetadata(
1292
1163
  contractColumn: StorageColumn,
1293
- storageTypes: Readonly<Record<string, StorageTypeInstance | PostgresEnumStorageEntry>>,
1164
+ storageTypes: Readonly<Record<string, StorageTypeInstance>>,
1294
1165
  context?: {
1295
1166
  readonly tableName: string;
1296
1167
  readonly columnName: string;
@@ -1310,13 +1181,6 @@ function resolveContractColumnTypeMetadata(
1310
1181
  );
1311
1182
  }
1312
1183
 
1313
- if (isPostgresEnumStorageEntry(referencedType)) {
1314
- return {
1315
- codecId: referencedType.codecId,
1316
- nativeType: referencedType.nativeType,
1317
- typeParams: { values: referencedType.values } as Record<string, unknown>,
1318
- };
1319
- }
1320
1184
  if (isStorageTypeInstance(referencedType)) {
1321
1185
  return {
1322
1186
  codecId: referencedType.codecId,
@@ -1325,7 +1189,7 @@ function resolveContractColumnTypeMetadata(
1325
1189
  };
1326
1190
  }
1327
1191
  throw new Error(
1328
- `Storage type "${contractColumn.typeRef}" has an unknown polymorphic kind; expected codec-instance or postgres-enum.`,
1192
+ `Storage type "${contractColumn.typeRef}" has an unknown kind; expected a codec-typed StorageTypeInstance.`,
1329
1193
  );
1330
1194
  }
1331
1195