@prisma-next/sql-contract-psl 0.13.0-dev.9 → 0.14.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import { ControlPolicy } from "@prisma-next/contract/types";
2
- import { SqlNamespaceTablesInput } from "@prisma-next/sql-contract/types";
3
2
  import { ContractConfig } from "@prisma-next/config/config-types";
4
3
  import { ExtensionPackRef, TargetPackRef } from "@prisma-next/framework-components/components";
5
4
  import { Namespace } from "@prisma-next/framework-components/ir";
5
+ import { SqlNamespaceTablesInput } from "@prisma-next/sql-contract/types";
6
6
 
7
7
  //#region src/provider.d.ts
8
8
  interface PrismaContractOptions {
package/dist/provider.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as interpretPslDocumentToSqlContract } from "./interpreter-B_KtZusL.mjs";
1
+ import { t as interpretPslDocumentToSqlContract } from "./interpreter-B0BsCLKT.mjs";
2
2
  import { ifDefined } from "@prisma-next/utils/defined";
3
3
  import { notOk, ok } from "@prisma-next/utils/result";
4
4
  import { parsePslDocument } from "@prisma-next/psl-parser";
@@ -60,7 +60,8 @@ function prismaContract(schemaPath, options) {
60
60
  }
61
61
  const document = parsePslDocument({
62
62
  schema,
63
- sourceId: schemaPath
63
+ sourceId: schemaPath,
64
+ pslBlockDescriptors: context.authoringContributions.pslBlockDescriptors
64
65
  });
65
66
  const scalarTypeDescriptors = buildColumnDescriptorMap(context.scalarTypeDescriptors, context.codecLookup);
66
67
  const interpreted = interpretPslDocumentToSqlContract({
@@ -72,7 +73,8 @@ function prismaContract(schemaPath, options) {
72
73
  composedExtensionContracts: context.composedExtensionContracts,
73
74
  ...ifDefined("composedExtensionPackRefs", options.composedExtensionPackRefs?.length ? options.composedExtensionPackRefs : void 0),
74
75
  controlMutationDefaults: context.controlMutationDefaults,
75
- ...ifDefined("createNamespace", options.createNamespace)
76
+ ...ifDefined("createNamespace", options.createNamespace),
77
+ codecLookup: context.codecLookup
76
78
  });
77
79
  if (!interpreted.ok) return interpreted;
78
80
  return ok(applySpecifierDefaultControlPolicy(interpreted.value, options.defaultControlPolicy));
@@ -1 +1 @@
1
- {"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractConfig } from '@prisma-next/config/config-types';\nimport { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';\nimport type { ControlPolicy } from '@prisma-next/contract/types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';\nimport type { Namespace } from '@prisma-next/framework-components/ir';\nimport { parsePslDocument } from '@prisma-next/psl-parser';\nimport type { SqlNamespaceTablesInput } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { basename, extname } from 'pathe';\nimport { interpretPslDocumentToSqlContract } from './interpreter';\nimport type { ColumnDescriptor } from './psl-column-resolution';\n\nexport interface PrismaContractOptions {\n readonly output?: string;\n readonly target: TargetPackRef<'sql', string>;\n readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];\n readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;\n readonly defaultControlPolicy?: ControlPolicy;\n}\n\n/**\n * Derives the emit output path from the schema input path so artefacts land\n * colocated with the source (e.g. `src/contract/schema.prisma` →\n * `src/contract/contract.json`). The provider owns this because it is the\n * only layer that knows the input path; the upstream `normalizeContractConfig`\n * default is a last-resort fallback for providers that don't carry one.\n */\nfunction defaultOutputFromSchemaPath(schemaPath: string): string {\n const ext = extname(schemaPath);\n if (ext.length === 0) return `${schemaPath}.json`;\n const base = schemaPath.slice(0, -ext.length);\n // PSL schemas commonly use `schema.prisma`; the emitted JSON is called\n // `contract.json` to mirror the rest of the toolchain, not `schema.json`.\n // Match only the exact basename `schema` so files like `my-schema.prisma`\n // are not silently rewritten to `my-contract.json`.\n if (basename(base) === 'schema') {\n return `${base.slice(0, -'schema'.length)}contract.json`;\n }\n return `${base}.json`;\n}\n\nfunction buildColumnDescriptorMap(\n scalarTypeDescriptors: ReadonlyMap<string, string>,\n codecLookup: CodecLookup,\n): ReadonlyMap<string, ColumnDescriptor> {\n const result = new Map<string, ColumnDescriptor>();\n for (const [typeName, codecId] of scalarTypeDescriptors) {\n const nativeType = codecLookup.targetTypesFor(codecId)?.[0];\n if (nativeType === undefined) continue;\n result.set(typeName, { codecId, nativeType });\n }\n return result;\n}\n\nexport function prismaContract(schemaPath: string, options: PrismaContractOptions): ContractConfig {\n return {\n source: {\n inputs: [schemaPath],\n load: async (context) => {\n const [absoluteSchemaPath] = context.resolvedInputs;\n if (absoluteSchemaPath === undefined) {\n throw new Error(\n 'prismaContract: context.resolvedInputs is empty. The CLI config loader should populate it positional-matched with source.inputs.',\n );\n }\n let schema: string;\n try {\n schema = await readFile(absoluteSchemaPath, 'utf-8');\n } catch (error) {\n const message = String(error);\n return notOk({\n summary: `Failed to read Prisma schema at \"${schemaPath}\"`,\n diagnostics: [\n {\n code: 'PSL_SCHEMA_READ_FAILED',\n message,\n sourceId: schemaPath,\n },\n ],\n meta: { schemaPath, absoluteSchemaPath, cause: message },\n });\n }\n\n const document = parsePslDocument({\n schema,\n sourceId: schemaPath,\n });\n\n const scalarTypeDescriptors = buildColumnDescriptorMap(\n context.scalarTypeDescriptors,\n context.codecLookup,\n );\n\n const interpreted = interpretPslDocumentToSqlContract({\n document,\n target: options.target,\n authoringContributions: context.authoringContributions,\n scalarTypeDescriptors,\n ...ifDefined(\n 'composedExtensionPacks',\n context.composedExtensionPacks.length > 0\n ? [...context.composedExtensionPacks]\n : undefined,\n ),\n composedExtensionContracts: context.composedExtensionContracts,\n ...ifDefined(\n 'composedExtensionPackRefs',\n options.composedExtensionPackRefs?.length\n ? options.composedExtensionPackRefs\n : undefined,\n ),\n controlMutationDefaults: context.controlMutationDefaults,\n ...ifDefined('createNamespace', options.createNamespace),\n });\n if (!interpreted.ok) {\n return interpreted;\n }\n\n return ok(\n applySpecifierDefaultControlPolicy(interpreted.value, options.defaultControlPolicy),\n );\n },\n },\n output: options.output ?? defaultOutputFromSchemaPath(schemaPath),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;AA8BA,SAAS,4BAA4B,YAA4B;CAC/D,MAAM,MAAM,QAAQ,UAAU;CAC9B,IAAI,IAAI,WAAW,GAAG,OAAO,GAAG,WAAW;CAC3C,MAAM,OAAO,WAAW,MAAM,GAAG,CAAC,IAAI,MAAM;CAK5C,IAAI,SAAS,IAAI,MAAM,UACrB,OAAO,GAAG,KAAK,MAAM,GAAG,EAAgB,EAAE;CAE5C,OAAO,GAAG,KAAK;AACjB;AAEA,SAAS,yBACP,uBACA,aACuC;CACvC,MAAM,yBAAS,IAAI,IAA8B;CACjD,KAAK,MAAM,CAAC,UAAU,YAAY,uBAAuB;EACvD,MAAM,aAAa,YAAY,eAAe,OAAO,CAAC,GAAG;EACzD,IAAI,eAAe,KAAA,GAAW;EAC9B,OAAO,IAAI,UAAU;GAAE;GAAS;EAAW,CAAC;CAC9C;CACA,OAAO;AACT;AAEA,SAAgB,eAAe,YAAoB,SAAgD;CACjG,OAAO;EACL,QAAQ;GACN,QAAQ,CAAC,UAAU;GACnB,MAAM,OAAO,YAAY;IACvB,MAAM,CAAC,sBAAsB,QAAQ;IACrC,IAAI,uBAAuB,KAAA,GACzB,MAAM,IAAI,MACR,kIACF;IAEF,IAAI;IACJ,IAAI;KACF,SAAS,MAAM,SAAS,oBAAoB,OAAO;IACrD,SAAS,OAAO;KACd,MAAM,UAAU,OAAO,KAAK;KAC5B,OAAO,MAAM;MACX,SAAS,oCAAoC,WAAW;MACxD,aAAa,CACX;OACE,MAAM;OACN;OACA,UAAU;MACZ,CACF;MACA,MAAM;OAAE;OAAY;OAAoB,OAAO;MAAQ;KACzD,CAAC;IACH;IAEA,MAAM,WAAW,iBAAiB;KAChC;KACA,UAAU;IACZ,CAAC;IAED,MAAM,wBAAwB,yBAC5B,QAAQ,uBACR,QAAQ,WACV;IAEA,MAAM,cAAc,kCAAkC;KACpD;KACA,QAAQ,QAAQ;KAChB,wBAAwB,QAAQ;KAChC;KACA,GAAG,UACD,0BACA,QAAQ,uBAAuB,SAAS,IACpC,CAAC,GAAG,QAAQ,sBAAsB,IAClC,KAAA,CACN;KACA,4BAA4B,QAAQ;KACpC,GAAG,UACD,6BACA,QAAQ,2BAA2B,SAC/B,QAAQ,4BACR,KAAA,CACN;KACA,yBAAyB,QAAQ;KACjC,GAAG,UAAU,mBAAmB,QAAQ,eAAe;IACzD,CAAC;IACD,IAAI,CAAC,YAAY,IACf,OAAO;IAGT,OAAO,GACL,mCAAmC,YAAY,OAAO,QAAQ,oBAAoB,CACpF;GACF;EACF;EACA,QAAQ,QAAQ,UAAU,4BAA4B,UAAU;CAClE;AACF"}
1
+ {"version":3,"file":"provider.mjs","names":[],"sources":["../src/provider.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractConfig } from '@prisma-next/config/config-types';\nimport { applySpecifierDefaultControlPolicy } from '@prisma-next/contract/apply-specifier-default-control-policy';\nimport type { ControlPolicy } from '@prisma-next/contract/types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';\nimport type { Namespace } from '@prisma-next/framework-components/ir';\nimport { parsePslDocument } from '@prisma-next/psl-parser';\nimport type { SqlNamespaceTablesInput } from '@prisma-next/sql-contract/types';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { basename, extname } from 'pathe';\nimport { interpretPslDocumentToSqlContract } from './interpreter';\nimport type { ColumnDescriptor } from './psl-column-resolution';\n\nexport interface PrismaContractOptions {\n readonly output?: string;\n readonly target: TargetPackRef<'sql', string>;\n readonly composedExtensionPackRefs?: readonly ExtensionPackRef<'sql', string>[];\n readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;\n readonly defaultControlPolicy?: ControlPolicy;\n}\n\n/**\n * Derives the emit output path from the schema input path so artefacts land\n * colocated with the source (e.g. `src/contract/schema.prisma` →\n * `src/contract/contract.json`). The provider owns this because it is the\n * only layer that knows the input path; the upstream `normalizeContractConfig`\n * default is a last-resort fallback for providers that don't carry one.\n */\nfunction defaultOutputFromSchemaPath(schemaPath: string): string {\n const ext = extname(schemaPath);\n if (ext.length === 0) return `${schemaPath}.json`;\n const base = schemaPath.slice(0, -ext.length);\n // PSL schemas commonly use `schema.prisma`; the emitted JSON is called\n // `contract.json` to mirror the rest of the toolchain, not `schema.json`.\n // Match only the exact basename `schema` so files like `my-schema.prisma`\n // are not silently rewritten to `my-contract.json`.\n if (basename(base) === 'schema') {\n return `${base.slice(0, -'schema'.length)}contract.json`;\n }\n return `${base}.json`;\n}\n\nfunction buildColumnDescriptorMap(\n scalarTypeDescriptors: ReadonlyMap<string, string>,\n codecLookup: CodecLookup,\n): ReadonlyMap<string, ColumnDescriptor> {\n const result = new Map<string, ColumnDescriptor>();\n for (const [typeName, codecId] of scalarTypeDescriptors) {\n const nativeType = codecLookup.targetTypesFor(codecId)?.[0];\n if (nativeType === undefined) continue;\n result.set(typeName, { codecId, nativeType });\n }\n return result;\n}\n\nexport function prismaContract(schemaPath: string, options: PrismaContractOptions): ContractConfig {\n return {\n source: {\n inputs: [schemaPath],\n load: async (context) => {\n const [absoluteSchemaPath] = context.resolvedInputs;\n if (absoluteSchemaPath === undefined) {\n throw new Error(\n 'prismaContract: context.resolvedInputs is empty. The CLI config loader should populate it positional-matched with source.inputs.',\n );\n }\n let schema: string;\n try {\n schema = await readFile(absoluteSchemaPath, 'utf-8');\n } catch (error) {\n const message = String(error);\n return notOk({\n summary: `Failed to read Prisma schema at \"${schemaPath}\"`,\n diagnostics: [\n {\n code: 'PSL_SCHEMA_READ_FAILED',\n message,\n sourceId: schemaPath,\n },\n ],\n meta: { schemaPath, absoluteSchemaPath, cause: message },\n });\n }\n\n const document = parsePslDocument({\n schema,\n sourceId: schemaPath,\n pslBlockDescriptors: context.authoringContributions.pslBlockDescriptors,\n });\n\n const scalarTypeDescriptors = buildColumnDescriptorMap(\n context.scalarTypeDescriptors,\n context.codecLookup,\n );\n\n const interpreted = interpretPslDocumentToSqlContract({\n document,\n target: options.target,\n authoringContributions: context.authoringContributions,\n scalarTypeDescriptors,\n ...ifDefined(\n 'composedExtensionPacks',\n context.composedExtensionPacks.length > 0\n ? [...context.composedExtensionPacks]\n : undefined,\n ),\n composedExtensionContracts: context.composedExtensionContracts,\n ...ifDefined(\n 'composedExtensionPackRefs',\n options.composedExtensionPackRefs?.length\n ? options.composedExtensionPackRefs\n : undefined,\n ),\n controlMutationDefaults: context.controlMutationDefaults,\n ...ifDefined('createNamespace', options.createNamespace),\n codecLookup: context.codecLookup,\n });\n if (!interpreted.ok) {\n return interpreted;\n }\n\n return ok(\n applySpecifierDefaultControlPolicy(interpreted.value, options.defaultControlPolicy),\n );\n },\n },\n output: options.output ?? defaultOutputFromSchemaPath(schemaPath),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;AA8BA,SAAS,4BAA4B,YAA4B;CAC/D,MAAM,MAAM,QAAQ,UAAU;CAC9B,IAAI,IAAI,WAAW,GAAG,OAAO,GAAG,WAAW;CAC3C,MAAM,OAAO,WAAW,MAAM,GAAG,CAAC,IAAI,MAAM;CAK5C,IAAI,SAAS,IAAI,MAAM,UACrB,OAAO,GAAG,KAAK,MAAM,GAAG,EAAgB,EAAE;CAE5C,OAAO,GAAG,KAAK;AACjB;AAEA,SAAS,yBACP,uBACA,aACuC;CACvC,MAAM,yBAAS,IAAI,IAA8B;CACjD,KAAK,MAAM,CAAC,UAAU,YAAY,uBAAuB;EACvD,MAAM,aAAa,YAAY,eAAe,OAAO,CAAC,GAAG;EACzD,IAAI,eAAe,KAAA,GAAW;EAC9B,OAAO,IAAI,UAAU;GAAE;GAAS;EAAW,CAAC;CAC9C;CACA,OAAO;AACT;AAEA,SAAgB,eAAe,YAAoB,SAAgD;CACjG,OAAO;EACL,QAAQ;GACN,QAAQ,CAAC,UAAU;GACnB,MAAM,OAAO,YAAY;IACvB,MAAM,CAAC,sBAAsB,QAAQ;IACrC,IAAI,uBAAuB,KAAA,GACzB,MAAM,IAAI,MACR,kIACF;IAEF,IAAI;IACJ,IAAI;KACF,SAAS,MAAM,SAAS,oBAAoB,OAAO;IACrD,SAAS,OAAO;KACd,MAAM,UAAU,OAAO,KAAK;KAC5B,OAAO,MAAM;MACX,SAAS,oCAAoC,WAAW;MACxD,aAAa,CACX;OACE,MAAM;OACN;OACA,UAAU;MACZ,CACF;MACA,MAAM;OAAE;OAAY;OAAoB,OAAO;MAAQ;KACzD,CAAC;IACH;IAEA,MAAM,WAAW,iBAAiB;KAChC;KACA,UAAU;KACV,qBAAqB,QAAQ,uBAAuB;IACtD,CAAC;IAED,MAAM,wBAAwB,yBAC5B,QAAQ,uBACR,QAAQ,WACV;IAEA,MAAM,cAAc,kCAAkC;KACpD;KACA,QAAQ,QAAQ;KAChB,wBAAwB,QAAQ;KAChC;KACA,GAAG,UACD,0BACA,QAAQ,uBAAuB,SAAS,IACpC,CAAC,GAAG,QAAQ,sBAAsB,IAClC,KAAA,CACN;KACA,4BAA4B,QAAQ;KACpC,GAAG,UACD,6BACA,QAAQ,2BAA2B,SAC/B,QAAQ,4BACR,KAAA,CACN;KACA,yBAAyB,QAAQ;KACjC,GAAG,UAAU,mBAAmB,QAAQ,eAAe;KACvD,aAAa,QAAQ;IACvB,CAAC;IACD,IAAI,CAAC,YAAY,IACf,OAAO;IAGT,OAAO,GACL,mCAAmC,YAAY,OAAO,QAAQ,oBAAoB,CACpF;GACF;EACF;EACA,QAAQ,QAAQ,UAAU,4BAA4B,UAAU;CAClE;AACF"}
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract-psl",
3
- "version": "0.13.0-dev.9",
3
+ "version": "0.14.0-dev.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "PSL-to-SQL ContractIR interpreter for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/config": "0.13.0-dev.9",
10
- "@prisma-next/contract": "0.13.0-dev.9",
11
- "@prisma-next/framework-components": "0.13.0-dev.9",
12
- "@prisma-next/psl-parser": "0.13.0-dev.9",
13
- "@prisma-next/sql-contract": "0.13.0-dev.9",
14
- "@prisma-next/sql-contract-ts": "0.13.0-dev.9",
15
- "@prisma-next/utils": "0.13.0-dev.9",
9
+ "@prisma-next/config": "0.14.0-dev.1",
10
+ "@prisma-next/contract": "0.14.0-dev.1",
11
+ "@prisma-next/framework-components": "0.14.0-dev.1",
12
+ "@prisma-next/psl-parser": "0.14.0-dev.1",
13
+ "@prisma-next/sql-contract": "0.14.0-dev.1",
14
+ "@prisma-next/sql-contract-ts": "0.14.0-dev.1",
15
+ "@prisma-next/utils": "0.14.0-dev.1",
16
16
  "pathe": "^2.0.3"
17
17
  },
18
18
  "devDependencies": {
19
- "@prisma-next/contract-authoring": "0.13.0-dev.9",
20
- "@prisma-next/test-utils": "0.13.0-dev.9",
21
- "@prisma-next/tsconfig": "0.13.0-dev.9",
22
- "@prisma-next/tsdown": "0.13.0-dev.9",
19
+ "@prisma-next/contract-authoring": "0.14.0-dev.1",
20
+ "@prisma-next/test-utils": "0.14.0-dev.1",
21
+ "@prisma-next/tsconfig": "0.14.0-dev.1",
22
+ "@prisma-next/tsdown": "0.14.0-dev.1",
23
23
  "arktype": "^2.2.0",
24
24
  "tsdown": "0.22.1",
25
25
  "typescript": "5.9.3",
@@ -14,9 +14,9 @@ import { crossRef } from '@prisma-next/contract/types';
14
14
  import type {
15
15
  AuthoringContributions,
16
16
  AuthoringEntityContext,
17
- AuthoringEntityTypeDescriptor,
18
17
  } from '@prisma-next/framework-components/authoring';
19
18
  import { instantiateAuthoringEntityType } from '@prisma-next/framework-components/authoring';
19
+ import type { CodecLookup } from '@prisma-next/framework-components/codec';
20
20
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
21
21
  import type {
22
22
  ControlMutationDefaultRegistry,
@@ -24,25 +24,25 @@ import type {
24
24
  MutationDefaultGeneratorDescriptor,
25
25
  } from '@prisma-next/framework-components/control';
26
26
  import type { Namespace } from '@prisma-next/framework-components/ir';
27
+ import { namespacePslExtensionBlocks } from '@prisma-next/framework-components/psl-ast';
27
28
  import type {
28
29
  ParsePslDocumentResult,
29
30
  PslAttribute,
30
31
  PslCompositeType,
31
- PslEnum,
32
+ PslExtensionBlock,
32
33
  PslField,
33
34
  PslModel,
34
35
  PslNamedTypeDeclaration,
35
36
  PslNamespace,
36
37
  } from '@prisma-next/psl-parser';
37
- import {
38
- isPostgresEnumStorageEntry,
39
- type PostgresEnumStorageEntry,
40
- type SqlModelStorage,
41
- type SqlNamespaceTablesInput,
42
- type StorageTypeInstance,
38
+ import type {
39
+ SqlModelStorage,
40
+ SqlNamespaceTablesInput,
41
+ StorageTypeInstance,
43
42
  } from '@prisma-next/sql-contract/types';
44
43
  import {
45
44
  buildSqlContractFromDefinition,
45
+ type EnumTypeHandle,
46
46
  type FieldNode,
47
47
  type ForeignKeyNode,
48
48
  type IndexNode,
@@ -63,7 +63,6 @@ import {
63
63
  parseAttributeFieldList,
64
64
  parseConstraintMapArgument,
65
65
  parseControlPolicyAttribute,
66
- parseMapName,
67
66
  parseObjectLiteralStringMap,
68
67
  parseQuotedStringLiteral,
69
68
  } from './psl-attribute-parsing';
@@ -124,6 +123,7 @@ export interface InterpretPslDocumentToSqlContractInput {
124
123
  * `SqlUnboundNamespace` singleton.
125
124
  */
126
125
  readonly createNamespace?: (input: SqlNamespaceTablesInput) => Namespace;
126
+ readonly codecLookup?: CodecLookup;
127
127
  }
128
128
 
129
129
  function buildComposedExtensionPackRefs(
@@ -302,77 +302,56 @@ function validateNamespaceBlocksForSqlTarget(input: {
302
302
  }
303
303
 
304
304
  interface ProcessEnumDeclarationsInput {
305
- readonly enums: readonly PslEnum[];
305
+ readonly enumBlocks: readonly PslExtensionBlock[];
306
306
  readonly sourceId: string;
307
- readonly enumEntityDescriptor: AuthoringEntityTypeDescriptor | undefined;
307
+ readonly authoringContributions: AuthoringContributions | undefined;
308
308
  readonly entityContext: AuthoringEntityContext;
309
309
  readonly diagnostics: ContractSourceDiagnostic[];
310
310
  }
311
311
 
312
312
  function processEnumDeclarations(input: ProcessEnumDeclarationsInput): {
313
- readonly storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>;
313
+ readonly enumHandles: Record<string, EnumTypeHandle>;
314
314
  readonly enumTypeDescriptors: Map<string, ColumnDescriptor>;
315
315
  } {
316
- const storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {};
316
+ const enumHandles: Record<string, EnumTypeHandle> = {};
317
317
  const enumTypeDescriptors = new Map<string, ColumnDescriptor>();
318
318
 
319
- if (input.enums.length === 0) {
320
- return { storageTypes, enumTypeDescriptors };
319
+ if (input.enumBlocks.length === 0) {
320
+ return { enumHandles, enumTypeDescriptors };
321
321
  }
322
322
 
323
- if (!input.enumEntityDescriptor) {
324
- // The PSL `enum X { … }` syntax only resolves when the active
325
- // pack composition contributes an `enum` entity-type factory (the
326
- // Postgres target pack does so today via
327
- // `authoring.entityTypes.enum`). Without the contribution we
328
- // surface a diagnostic per declaration rather than silently
329
- // swallowing the syntax.
330
- for (const enumDeclaration of input.enums) {
323
+ const enumDescriptor = getAuthoringEntity(input.authoringContributions, ['enum']);
324
+ if (!enumDescriptor) {
325
+ for (const decl of input.enumBlocks) {
331
326
  input.diagnostics.push({
332
- code: 'PSL_UNSUPPORTED_NAMED_TYPE_BASE',
333
- message: `Enum "${enumDeclaration.name}" requires the active target pack to contribute an enum entity-type helper`,
327
+ code: 'PSL_ENUM_MISSING_FACTORY',
328
+ message: `enum "${decl.name}" requires an "enum" entityType factory in the active authoring contributions`,
334
329
  sourceId: input.sourceId,
335
- span: enumDeclaration.span,
330
+ span: decl.span,
336
331
  });
337
332
  }
338
- return { storageTypes, enumTypeDescriptors };
333
+ return { enumHandles, enumTypeDescriptors };
339
334
  }
340
335
 
341
- for (const enumDeclaration of input.enums) {
342
- const nativeType = parseMapName({
343
- attribute: getAttribute(enumDeclaration.attributes, 'map'),
344
- defaultValue: enumDeclaration.name,
345
- sourceId: input.sourceId,
346
- diagnostics: input.diagnostics,
347
- entityLabel: `Enum "${enumDeclaration.name}"`,
348
- span: enumDeclaration.span,
349
- });
350
- const values = enumDeclaration.values.map((value) => value.name);
351
- const constructed = instantiateAuthoringEntityType(
336
+ for (const decl of input.enumBlocks) {
337
+ const handle = instantiateAuthoringEntityType(
352
338
  'enum',
353
- input.enumEntityDescriptor,
354
- [{ name: enumDeclaration.name, nativeType, values }],
339
+ enumDescriptor,
340
+ [decl],
355
341
  input.entityContext,
356
342
  );
357
- if (!isPostgresEnumStorageEntry(constructed)) {
358
- input.diagnostics.push({
359
- code: 'PSL_UNSUPPORTED_NAMED_TYPE_BASE',
360
- message: `Enum "${enumDeclaration.name}": enum entity-type factory must return a PostgresEnumStorageEntry-shaped value (kind: 'postgres-enum')`,
361
- sourceId: input.sourceId,
362
- span: enumDeclaration.span,
363
- });
364
- continue;
365
- }
366
- const descriptor: ColumnDescriptor = {
367
- codecId: constructed.codecId,
368
- nativeType: constructed.nativeType,
369
- typeRef: enumDeclaration.name,
370
- };
371
- enumTypeDescriptors.set(enumDeclaration.name, descriptor);
372
- storageTypes[enumDeclaration.name] = constructed;
343
+
344
+ if (handle === undefined || handle === null) continue;
345
+
346
+ const enumHandle = blindCast<EnumTypeHandle, 'enum factory returns EnumTypeHandle'>(handle);
347
+ enumHandles[decl.name] = enumHandle;
348
+ enumTypeDescriptors.set(decl.name, {
349
+ codecId: enumHandle.codecId,
350
+ nativeType: enumHandle.nativeType,
351
+ });
373
352
  }
374
353
 
375
- return { storageTypes, enumTypeDescriptors };
354
+ return { enumHandles, enumTypeDescriptors };
376
355
  }
377
356
 
378
357
  interface ResolveNamedTypeDeclarationsInput {
@@ -451,10 +430,10 @@ function validateNamedTypeAttributes(input: {
451
430
  }
452
431
 
453
432
  function resolveNamedTypeDeclarations(input: ResolveNamedTypeDeclarationsInput): {
454
- readonly storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>;
433
+ readonly storageTypes: Record<string, StorageTypeInstance>;
455
434
  readonly namedTypeDescriptors: Map<string, ColumnDescriptor>;
456
435
  } {
457
- const storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry> = {};
436
+ const storageTypes: Record<string, StorageTypeInstance> = {};
458
437
  const namedTypeDescriptors = new Map<string, ColumnDescriptor>();
459
438
 
460
439
  for (const declaration of input.declarations) {
@@ -619,6 +598,7 @@ interface BuildModelNodeInput {
619
598
  readonly diagnostics: ContractSourceDiagnostic[];
620
599
  /** Resolved namespace id keyed by model name — used to stamp the target namespace on FKs. */
621
600
  readonly modelNamespaceIds: ReadonlyMap<string, string>;
601
+ readonly enumHandles?: ReadonlyMap<string, EnumTypeHandle>;
622
602
  }
623
603
 
624
604
  interface BuildModelNodeResult {
@@ -650,6 +630,7 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
650
630
  diagnostics,
651
631
  sourceId,
652
632
  scalarTypeDescriptors: input.scalarTypeDescriptors,
633
+ ...ifDefined('enumHandles', input.enumHandles),
653
634
  });
654
635
 
655
636
  const inlineIdFields = resolvedFields.filter((field) => field.isId);
@@ -1320,14 +1301,18 @@ function buildModelNodeFromPsl(input: BuildModelNodeInput): BuildModelNodeResult
1320
1301
  modelNode: {
1321
1302
  modelName: model.name,
1322
1303
  tableName,
1323
- fields: resolvedFields.map((resolvedField) => ({
1324
- fieldName: resolvedField.field.name,
1325
- columnName: resolvedField.columnName,
1326
- descriptor: resolvedField.descriptor,
1327
- nullable: resolvedField.field.optional,
1328
- ...ifDefined('default', resolvedField.defaultValue),
1329
- ...ifDefined('executionDefaults', resolvedField.executionDefaults),
1330
- })),
1304
+ fields: resolvedFields.map((resolvedField) => {
1305
+ const enumHandle = input.enumHandles?.get(resolvedField.field.typeName);
1306
+ return {
1307
+ fieldName: resolvedField.field.name,
1308
+ columnName: resolvedField.columnName,
1309
+ descriptor: resolvedField.descriptor,
1310
+ nullable: resolvedField.field.optional,
1311
+ ...ifDefined('default', resolvedField.defaultValue),
1312
+ ...ifDefined('executionDefaults', resolvedField.executionDefaults),
1313
+ ...ifDefined('enumTypeHandle', enumHandle),
1314
+ };
1315
+ }),
1331
1316
  ...ifDefined('id', primaryKey),
1332
1317
  ...(uniqueConstraints.length > 0 ? { uniques: uniqueConstraints } : {}),
1333
1318
  ...(indexNodes.length > 0 ? { indexes: indexNodes } : {}),
@@ -1924,33 +1909,6 @@ export function interpretPslDocumentToSqlContract(
1924
1909
  }
1925
1910
  }
1926
1911
  const defaultNamespaceId = input.target.defaultNamespaceId;
1927
- // Top-level enums (the __unspecified__ bucket) route to `storageTypes`;
1928
- // enums inside a named namespace block route to `namespaceTypes[nsId]`.
1929
- const topLevelEnums = input.document.ast.namespaces
1930
- .filter((ns) => ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME)
1931
- .flatMap((ns) => ns.enums);
1932
- const namedNamespaceEnumsByNsId = new Map<string, readonly PslEnum[]>();
1933
- for (const ns of input.document.ast.namespaces) {
1934
- if (ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME || ns.enums.length === 0) {
1935
- continue;
1936
- }
1937
- const resolvedId = resolveNamespaceIdForSqlTarget({
1938
- bucketName: ns.name,
1939
- targetId: input.target.targetId,
1940
- });
1941
- if (resolvedId === undefined) {
1942
- continue;
1943
- }
1944
- // Read-then-merge so that any future change to the PSL parser (or to
1945
- // `resolveNamespaceIdForSqlTarget`) that produces two AST entries
1946
- // resolving to the same `resolvedId` would accumulate their enums
1947
- // rather than silently dropping the earlier set. Today the parser
1948
- // already merges duplicate `namespace <name> { … }` blocks into a
1949
- // single AST entry per name, so this loop sees one `ns` per
1950
- // resolvedId and the merge degrades to a plain set.
1951
- const existing = namedNamespaceEnumsByNsId.get(resolvedId) ?? [];
1952
- namedNamespaceEnumsByNsId.set(resolvedId, [...existing, ...ns.enums]);
1953
- }
1954
1912
 
1955
1913
  const compositeTypes = input.document.ast.namespaces.flatMap((ns) => ns.compositeTypes);
1956
1914
  const modelNames = new Set(models.map((model) => model.name));
@@ -1966,45 +1924,48 @@ export function interpretPslDocumentToSqlContract(
1966
1924
  generatorDescriptorById.set(descriptor.id, descriptor);
1967
1925
  }
1968
1926
 
1969
- const enumEntityDescriptor = getAuthoringEntity(input.authoringContributions, ['enum']);
1970
- const enumEntityContext = {
1971
- family: input.target.familyId,
1972
- target: input.target.targetId,
1973
- };
1927
+ const topLevelEnums = input.document.ast.namespaces
1928
+ .filter((ns) => ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME)
1929
+ .flatMap((ns) => namespacePslExtensionBlocks(ns).filter((b) => b.kind === 'enum'));
1930
+ for (const ns of input.document.ast.namespaces) {
1931
+ if (ns.name === UNSPECIFIED_PSL_NAMESPACE_NAME) continue;
1932
+ const nsEnums = namespacePslExtensionBlocks(ns).filter((b) => b.kind === 'enum');
1933
+ if (nsEnums.length === 0) continue;
1934
+ for (const decl of nsEnums) {
1935
+ diagnostics.push({
1936
+ code: 'PSL_ENUM_NAMESPACE_NOT_SUPPORTED',
1937
+ message: `enum "${decl.name}" inside namespace "${ns.name}" is not supported; declare enum at the top level`,
1938
+ sourceId,
1939
+ span: decl.span,
1940
+ });
1941
+ }
1942
+ }
1974
1943
 
1975
1944
  const enumResult = processEnumDeclarations({
1976
- enums: topLevelEnums,
1945
+ enumBlocks: topLevelEnums,
1977
1946
  sourceId,
1978
- enumEntityDescriptor,
1979
- entityContext: enumEntityContext,
1947
+ authoringContributions: input.authoringContributions,
1948
+ entityContext: {
1949
+ family: input.target.familyId,
1950
+ target: input.target.targetId,
1951
+ ...ifDefined('codecLookup', input.codecLookup),
1952
+ sourceId,
1953
+ diagnostics: {
1954
+ push: (d) => {
1955
+ diagnostics.push(
1956
+ blindCast<ContractSourceDiagnostic, 'sink diagnostics are span-compatible'>(d),
1957
+ );
1958
+ },
1959
+ },
1960
+ },
1980
1961
  diagnostics,
1981
1962
  });
1982
1963
 
1983
- // Process enums declared in named namespace blocks and collect them into
1984
- // `namespaceTypes` keyed by the resolved namespace id.
1985
1964
  const allEnumTypeDescriptors = new Map(enumResult.enumTypeDescriptors);
1986
- const namespaceEnumStorageTypes: Record<string, Record<string, PostgresEnumStorageEntry>> = {};
1987
- for (const [nsId, nsEnums] of namedNamespaceEnumsByNsId) {
1988
- const nsEnumResult = processEnumDeclarations({
1989
- enums: nsEnums,
1990
- sourceId,
1991
- enumEntityDescriptor,
1992
- entityContext: enumEntityContext,
1993
- diagnostics,
1994
- });
1995
- for (const [name, descriptor] of nsEnumResult.enumTypeDescriptors) {
1996
- allEnumTypeDescriptors.set(name, descriptor);
1997
- }
1998
- const nsEntries: Record<string, PostgresEnumStorageEntry> = {};
1999
- for (const [name, entry] of Object.entries(nsEnumResult.storageTypes)) {
2000
- if (isPostgresEnumStorageEntry(entry)) {
2001
- nsEntries[name] = entry;
2002
- }
2003
- }
2004
- if (Object.keys(nsEntries).length > 0) {
2005
- namespaceEnumStorageTypes[nsId] = nsEntries;
2006
- }
2007
- }
1965
+
1966
+ const validEnumHandles: Record<string, EnumTypeHandle> = { ...enumResult.enumHandles };
1967
+
1968
+ const enumHandlesByName = new Map(Object.entries(validEnumHandles));
2008
1969
 
2009
1970
  const namedTypeResult = resolveNamedTypeDeclarations({
2010
1971
  declarations: input.document.ast.types?.declarations ?? [],
@@ -2018,7 +1979,7 @@ export function interpretPslDocumentToSqlContract(
2018
1979
  diagnostics,
2019
1980
  });
2020
1981
 
2021
- const storageTypes = { ...enumResult.storageTypes, ...namedTypeResult.storageTypes };
1982
+ const storageTypes = { ...namedTypeResult.storageTypes };
2022
1983
 
2023
1984
  const modelMappingsByCoordinate = buildModelMappings(
2024
1985
  modelEntries,
@@ -2068,6 +2029,7 @@ export function interpretPslDocumentToSqlContract(
2068
2029
  sourceId,
2069
2030
  diagnostics,
2070
2031
  modelNamespaceIds,
2032
+ ...(enumHandlesByName.size > 0 ? { enumHandles: enumHandlesByName } : {}),
2071
2033
  });
2072
2034
  modelNodes.push(
2073
2035
  namespaceId !== undefined ? { ...result.modelNode, namespaceId } : result.modelNode,
@@ -2159,9 +2121,7 @@ export function interpretPslDocumentToSqlContract(
2159
2121
  ),
2160
2122
  ),
2161
2123
  ...(Object.keys(storageTypes).length > 0 ? { storageTypes } : {}),
2162
- ...(Object.keys(namespaceEnumStorageTypes).length > 0
2163
- ? { namespaceTypes: namespaceEnumStorageTypes }
2164
- : {}),
2124
+ ...(Object.keys(validEnumHandles).length > 0 ? { enums: validEnumHandles } : {}),
2165
2125
  ...ifDefined('createNamespace', input.createNamespace),
2166
2126
  models: stiColumnModelNodes.map((model) => ({
2167
2127
  ...model,
@@ -2233,6 +2193,7 @@ export function interpretPslDocumentToSqlContract(
2233
2193
  patchedModels[modelCoordinateKey(namespaceId, modelName)] ?? model,
2234
2194
  ]),
2235
2195
  ),
2196
+ ...(namespaceSlice.enum !== undefined ? { enum: namespaceSlice.enum } : {}),
2236
2197
  ...(namespaceSlice.valueObjects !== undefined
2237
2198
  ? { valueObjects: namespaceSlice.valueObjects }
2238
2199
  : {}),
package/src/provider.ts CHANGED
@@ -87,6 +87,7 @@ export function prismaContract(schemaPath: string, options: PrismaContractOption
87
87
  const document = parsePslDocument({
88
88
  schema,
89
89
  sourceId: schemaPath,
90
+ pslBlockDescriptors: context.authoringContributions.pslBlockDescriptors,
90
91
  });
91
92
 
92
93
  const scalarTypeDescriptors = buildColumnDescriptorMap(
@@ -114,6 +115,7 @@ export function prismaContract(schemaPath: string, options: PrismaContractOption
114
115
  ),
115
116
  controlMutationDefaults: context.controlMutationDefaults,
116
117
  ...ifDefined('createNamespace', options.createNamespace),
118
+ codecLookup: context.codecLookup,
117
119
  });
118
120
  if (!interpreted.ok) {
119
121
  return interpreted;
@@ -550,7 +550,7 @@ export const NATIVE_TYPE_SPECS: Readonly<Record<string, NativeTypeSpec>> = {
550
550
  codecId: 'sql/char@1',
551
551
  nativeType: 'character',
552
552
  },
553
- 'db.Uuid': { args: 'noArgs', baseType: 'String', codecId: null, nativeType: 'uuid' },
553
+ 'db.Uuid': { args: 'noArgs', baseType: 'String', codecId: 'pg/uuid@1', nativeType: 'uuid' },
554
554
  'db.SmallInt': { args: 'noArgs', baseType: 'Int', codecId: 'pg/int2@1', nativeType: 'int2' },
555
555
  'db.Real': { args: 'noArgs', baseType: 'Float', codecId: 'pg/float4@1', nativeType: 'float4' },
556
556
  'db.Numeric': {