@prisma-next/emitter 0.3.0-dev.135 → 0.3.0-dev.146

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 (36) hide show
  1. package/README.md +33 -30
  2. package/dist/domain-type-generation.d.mts +14 -2
  3. package/dist/domain-type-generation.d.mts.map +1 -1
  4. package/dist/domain-type-generation.mjs +139 -1
  5. package/dist/domain-type-generation.mjs.map +1 -1
  6. package/dist/exports/index.d.mts +39 -4
  7. package/dist/exports/index.d.mts.map +1 -0
  8. package/dist/exports/index.mjs +104 -3
  9. package/dist/exports/index.mjs.map +1 -0
  10. package/dist/test/utils.d.mts +16 -14
  11. package/dist/test/utils.d.mts.map +1 -1
  12. package/dist/test/utils.mjs +12 -51
  13. package/dist/test/utils.mjs.map +1 -1
  14. package/dist/type-expression-safety-7_1tfJXA.mjs +8 -0
  15. package/dist/type-expression-safety-7_1tfJXA.mjs.map +1 -0
  16. package/dist/type-expression-safety.d.mts +5 -0
  17. package/dist/type-expression-safety.d.mts.map +1 -0
  18. package/dist/type-expression-safety.mjs +3 -0
  19. package/package.json +12 -6
  20. package/src/domain-type-generation.ts +227 -1
  21. package/src/emit-types.ts +23 -0
  22. package/src/emit.ts +68 -0
  23. package/src/exports/index.ts +4 -9
  24. package/src/generate-contract-dts.ts +116 -0
  25. package/src/type-expression-safety.ts +3 -0
  26. package/test/canonicalization.test.ts +25 -28
  27. package/test/domain-type-generation.test.ts +509 -2
  28. package/test/emitter.integration.test.ts +81 -139
  29. package/test/emitter.roundtrip.test.ts +114 -184
  30. package/test/emitter.test.ts +82 -467
  31. package/test/hashing.test.ts +8 -30
  32. package/test/mock-spi.ts +18 -0
  33. package/test/type-expression-safety.test.ts +34 -0
  34. package/test/utils.ts +30 -156
  35. package/src/target-family.ts +0 -7
  36. package/test/factories.test.ts +0 -274
package/README.md CHANGED
@@ -9,7 +9,7 @@ The emitter is the core of Prisma Next's contract-first architecture. It takes a
9
9
  1. **`contract.json`** — Canonical JSON representation of the data contract with embedded `storageHash` and optional `executionHash`/`profileHash`. Callers may add `_generated` metadata field to indicate it's a generated artifact (excluded from canonicalization/hashing).
10
10
  2. **`contract.d.ts`** — TypeScript type definitions used by query builders and tooling (types-only, no runtime code). Includes warning header comments generated by target family hooks to indicate it's a generated file.
11
11
 
12
- The emitter is target-family-agnostic and uses a pluggable hook system (`TargetFamilyHook`) to handle family-specific validation and type generation. This keeps the core thin while allowing SQL, Document, and other target families to extend emission behavior.
12
+ The emitter is target-family-agnostic. The framework-owned `generateContractDts()` assembles the `.d.ts` template using shared domain utilities and delegates family-specific concerns to an `EmissionSpi` interface. This keeps the core thin while allowing SQL, Document, and other target families to extend emission behavior.
13
13
 
14
14
  ## Purpose
15
15
 
@@ -17,8 +17,8 @@ Provide a deterministic, verifiable representation of the application's data con
17
17
 
18
18
  ## Responsibilities
19
19
 
20
- - **Parse**: Accept contract IR (Intermediate Representation) from authoring surfaces
21
- - **Validate**: Core structure validation plus family-specific type and structure validation via hooks
20
+ - **Parse**: Accept `Contract` values from authoring surfaces
21
+ - **Validate**: Core structure validation (family-specific validation is the caller's responsibility)
22
22
  - **Canonicalize**: Compute `storageHash` (schema meaning), `executionHash` (execution defaults), and `profileHash` (capabilities/pins) from canonical JSON
23
23
  - **Emit**: Generate `contract.json` and `contract.d.ts` with family-specific type generation
24
24
  - **Descriptor-Agnostic**: The emitter is completely agnostic to how descriptors are produced. It receives pre-assembled `OperationRegistry`, `codecTypeImports`, `operationTypeImports`, and `extensionIds` from the CLI or family helpers—no pack manifest parsing happens inside the emitter.
@@ -41,7 +41,7 @@ flowchart TD
41
41
  end
42
42
 
43
43
  subgraph "Emitter Core"
44
- IR[Contract IR]
44
+ IR[Contract]
45
45
  VAL[Validate Core Structure]
46
46
  HASH[Compute Hashes]
47
47
  EMIT[Emit JSON + DTS]
@@ -76,21 +76,26 @@ flowchart TD
76
76
 
77
77
  ## Components
78
78
 
79
- ### Core Emitter (`emitter.ts`)
80
- - Orchestrates validation, hashing, and type generation
79
+ ### Core Emitter (`emit.ts`)
80
+ - Orchestrates hashing and type generation
81
81
  - Returns contract JSON and TypeScript definitions as strings (no file I/O)
82
82
  - Pure transformation function
83
- - Accepts `targetFamily: TargetFamilyHook` as a required parameter (no global registry)
84
-
85
- ### Target Family Hook (`target-family.ts`)
86
- - SPI interface (`TargetFamilyHook`) for extending emission with family-specific logic:
87
- - `validateTypes`: Validate type IDs against referenced extensions (receives `ValidationContext` with `operationRegistry` and `extensionIds`)
88
- - `validateStructure`: Family-specific structural validation
89
- - `generateContractTypes`: Generate `contract.d.ts` content (receives separate `codecTypeImports` and `operationTypeImports` arrays)
90
- - Authoring surfaces determine which target family SPI to use based on the contract's `targetFamily` field and pass it directly to `emit()`
91
- - No global registry or auto-registration - dependencies are explicit and passed directly
92
- - **Manifest-Agnostic**: Hooks receive pre-assembled context (operation registry, type imports, extension IDs), not extension packs. Manifest parsing and assembly happens in the CLI layer.
93
- - **Note**: `TargetFamilyHook`, `ValidationContext`, and `TypesImportSpec` types are defined in `@prisma-next/contract/types` (shared plane) and re-exported from this package for backward compatibility.
83
+ - Accepts `targetFamily: EmissionSpi` as a required parameter (no global registry)
84
+
85
+ ### Framework Template (`generate-contract-dts.ts`)
86
+ - Assembles the full `.d.ts` template using shared domain-level utilities from `domain-type-generation.ts`
87
+ - Delegates family-specific concerns to `EmissionSpi` callbacks:
88
+ - `generateStorageType`: Generate the family-specific storage type literal
89
+ - `generateModelStorageType`: Generate storage type for a single model
90
+ - `generateModelsType` (optional): Override default domain model type generation
91
+ - `getFamilyImports`, `getFamilyTypeAliases`, `getTypeMapsExpression`, `getContractWrapper`: Family-specific template fragments
92
+
93
+ ### EmissionSpi (defined in `@prisma-next/framework-components/emission`)
94
+ - Focused interface for family-specific parts of contract type emission
95
+ - Authoring surfaces determine which SPI to use based on the contract's `targetFamily` field and pass it to `emit()`
96
+ - **Manifest-Agnostic**: The emitter receives pre-assembled context (type imports), not extension packs
97
+
98
+ **Known limitation**: The SQL emitter provides its own `generateModelsType` that derives field codecs from storage table columns (since SQL `model.fields.codecId` is not populated at authoring time). This means the shared `generateModelsType` utility is only used by Mongo. A future change to populate `model.fields.codecId` at authoring time would eliminate this override.
94
99
 
95
100
  ### Hashing (`hashing.ts`)
96
101
  - `computeStorageHash`: SHA-256 of schema structure (models, storage, relations)
@@ -98,13 +103,11 @@ flowchart TD
98
103
  - `computeProfileHash`: SHA-256 of capabilities and adapter pins
99
104
 
100
105
  ### Canonicalization (`canonicalization.ts`)
101
- - `canonicalizeContract`: Normalizes contract IR into stable JSON string for hashing
106
+ - `canonicalizeContract`: Normalizes contract into stable JSON string for hashing
102
107
  - Excludes `_generated` metadata field from canonicalization to ensure determinism
103
108
  - Sorts object keys, omits default values, and orders top-level fields consistently
104
109
 
105
- **Note**: Extension pack descriptor wiring happens in the CLI/family layer. The emitter only sees the resulting registry/type import arrays and extension IDs. Operation manifest types (`OperationManifest`) live in `@prisma-next/contract/types`.
106
-
107
- **Note**: `TargetFamilyHook`, `ValidationContext`, and `TypesImportSpec` types are defined in `@prisma-next/contract/types` (shared plane) to allow both migration-plane (emitter) and shared-plane (control-plane) packages to import them without violating dependency rules. These types are re-exported from this package for backward compatibility.
110
+ **Note**: Extension pack descriptor wiring happens in the CLI/family layer. The emitter only sees the resulting type import arrays.
108
111
 
109
112
  ## Dependencies
110
113
 
@@ -135,15 +138,15 @@ This package is part of the **framework domain**, **tooling layer**, **migration
135
138
 
136
139
  ```typescript
137
140
  import { emit } from '@prisma-next/emitter';
138
- import type { ContractIR, EmitOptions } from '@prisma-next/emitter';
141
+ import type { Contract } from '@prisma-next/contract/types';
139
142
  import { createOperationRegistry } from '@prisma-next/operations';
140
143
 
141
144
  // Determine target family SPI based on target family
142
- import { sqlTargetFamilyHook } from '@prisma-next/sql-contract-emitter';
143
- // or: import { mongoTargetFamilyHook } from '@prisma-next/mongo-emitter';
145
+ import { sqlEmission } from '@prisma-next/sql-contract-emitter';
146
+ // or: import { mongoEmission } from '@prisma-next/mongo-emitter';
144
147
 
145
148
  // Emit contract
146
- const ir: ContractIR = {
149
+ const contract: Contract = {
147
150
  schemaVersion: '1',
148
151
  targetFamily: 'sql',
149
152
  target: 'postgres',
@@ -151,13 +154,13 @@ const ir: ContractIR = {
151
154
  };
152
155
 
153
156
  // Pass pre-assembled context to emit() (pack loading happens in CLI layer)
154
- const result = await emit(ir, {
157
+ const result = await emit(contract, {
155
158
  outputDir: './dist',
156
159
  operationRegistry: createOperationRegistry(), // Pre-assembled from packs
157
160
  codecTypeImports: [], // Extracted from packs (codec types)
158
161
  operationTypeImports: [], // Extracted from packs (operation types)
159
162
  extensionIds: ['postgres', 'pg'], // Extracted from packs
160
- }, sqlTargetFamilyHook);
163
+ }, sqlEmission);
161
164
 
162
165
  // result.contractJson: string (JSON) - canonical JSON without _generated metadata
163
166
  // result.contractDts: string (TypeScript definitions) - includes warning header
@@ -170,12 +173,12 @@ const result = await emit(ir, {
170
173
 
171
174
  ## Test Utilities
172
175
 
173
- When writing tests that create `ContractIR` objects, use the factory function from test utilities:
176
+ When writing tests that create `Contract` objects, use the factory helpers from `@prisma-next/contract/testing` or this package’s `test/utils` (`createTestContract`):
174
177
 
175
178
  ```typescript
176
- import { createContractIR } from './test/utils';
179
+ import { createTestContract } from './test/utils';
177
180
 
178
- const ir = createContractIR({
181
+ const contract = createTestContract({
179
182
  storage: {
180
183
  tables: {
181
184
  user: {
@@ -1,18 +1,30 @@
1
- import { TypesImportSpec } from "@prisma-next/contract/types";
1
+ import { ContractField, ContractModel, ContractValueObject } from "@prisma-next/contract/types";
2
+ import { CodecLookup } from "@prisma-next/framework-components/codec";
3
+ import { TypesImportSpec } from "@prisma-next/framework-components/emission";
2
4
 
3
5
  //#region src/domain-type-generation.d.ts
4
6
  declare function serializeValue(value: unknown): string;
5
7
  declare function serializeObjectKey(key: string): string;
6
8
  declare function generateRootsType(roots: Record<string, string> | undefined): string;
9
+ declare function generateModelFieldEntry(fieldName: string, field: ContractField): string;
10
+ declare function generateModelFieldsType(fields: Record<string, ContractField>): string;
7
11
  declare function generateModelRelationsType(relations: Record<string, unknown>): string;
12
+ declare function generateModelsType(models: Record<string, ContractModel>, generateModelStorage: (modelName: string, model: ContractModel) => string): string;
8
13
  declare function deduplicateImports(imports: TypesImportSpec[]): TypesImportSpec[];
9
14
  declare function generateImportLines(imports: TypesImportSpec[]): string[];
10
15
  declare function generateCodecTypeIntersection(imports: ReadonlyArray<TypesImportSpec>, named: string): string;
16
+ declare function serializeExecutionType(execution: Record<string, unknown>): string;
11
17
  declare function generateHashTypeAliases(hashes: {
12
18
  readonly storageHash: string;
13
19
  readonly executionHash?: string;
14
20
  readonly profileHash: string;
15
21
  }): string;
22
+ declare function generateFieldResolvedType(field: ContractField, codecLookup?: CodecLookup): string;
23
+ declare function generateFieldOutputTypesMap(models: Record<string, ContractModel> | undefined, codecLookup?: CodecLookup): string;
24
+ declare function generateValueObjectType(_voName: string, vo: ContractValueObject, _valueObjects: Record<string, ContractValueObject>): string;
25
+ declare function generateContractFieldDescriptor(fieldName: string, field: ContractField): string;
26
+ declare function generateValueObjectsDescriptorType(valueObjects: Record<string, ContractValueObject> | undefined): string;
27
+ declare function generateValueObjectTypeAliases(valueObjects: Record<string, ContractValueObject> | undefined): string;
16
28
  //#endregion
17
- export { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
29
+ export { deduplicateImports, generateCodecTypeIntersection, generateContractFieldDescriptor, generateFieldOutputTypesMap, generateFieldResolvedType, generateHashTypeAliases, generateImportLines, generateModelFieldEntry, generateModelFieldsType, generateModelRelationsType, generateModelsType, generateRootsType, generateValueObjectType, generateValueObjectTypeAliases, generateValueObjectsDescriptorType, serializeExecutionType, serializeObjectKey, serializeValue };
18
30
  //# sourceMappingURL=domain-type-generation.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"domain-type-generation.d.mts","names":[],"sources":["../src/domain-type-generation.ts"],"sourcesContent":[],"mappings":";;;iBAEgB,cAAA;iBA+BA,kBAAA;AA/BA,iBAsCA,iBAAA,CAtCc,KAAA,EAsCW,MAtCX,CAAA,MAAA,EAAA,MAAA,CAAA,GAAA,SAAA,CAAA,EAAA,MAAA;AA+Bd,iBAiBA,0BAAA,CAjBkB,SAAA,EAiBoB,MAjBpB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAAA,MAAA;AAOlB,iBAgDA,kBAAA,CAhD+B,OAAA,EAgDH,eAhDG,EAAA,CAAA,EAgDiB,eAhDjB,EAAA;AAU/B,iBAmDA,mBAAA,CAnDsC,OAAM,EAmDf,eAnDe,EAAA,CAAA,EAAA,MAAA,EAAA;AAsC5C,iBAoBA,6BAAA,CApBgD,OAAA,EAqBrD,aArBoE,CAqBtD,eArBsD,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAa/D,iBAeA,uBAAA,CAf6B,MAAe,EAAA;EAO5C,SAAA,WAAA,EAAA,MAAA;EAQA,SAAA,aAAA,CAAA,EAAA,MAAuB"}
1
+ {"version":3,"file":"domain-type-generation.d.mts","names":[],"sources":["../src/domain-type-generation.ts"],"sourcesContent":[],"mappings":";;;;;iBASgB,cAAA;iBA+BA,kBAAA;AA/BA,iBAsCA,iBAAA,CAtCc,KAAA,EAsCW,MAtCX,CAAA,MAAA,EAAA,MAAA,CAAA,GAAA,SAAA,CAAA,EAAA,MAAA;AA+Bd,iBAuBA,uBAAA,CAvBkB,SAAA,EAAA,MAAA,EAAA,KAAA,EAuBgC,aAvBhC,CAAA,EAAA,MAAA;AAOlB,iBAgCA,uBAAA,CAhC+B,MAAA,EAgCC,MAhCD,CAAA,MAAA,EAgCgB,aAhChB,CAAA,CAAA,EAAA,MAAA;AAgB/B,iBAwBA,0BAAA,CAxBkD,SAAa,EAwBzB,MAxByB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAAA,MAAA;AAgB/D,iBA8CA,kBAAA,CA9C+C,MAAA,EA+CrD,MA/CsC,CAAA,MAAM,EA+C7B,aA/C6B,CAAA,EAAA,oBAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,KAAA,EAgDH,aAhDG,EAAA,GAAA,MAAA,CAAA,EAAA,MAAA;AAQtC,iBA6EA,kBAAA,CA7E0B,OAAY,EA6EV,eA7EgB,EAAA,CAAA,EA6EI,eA7EJ,EAAA;AAsC5C,iBAoDA,mBAAA,CApDkB,OAAA,EAoDW,eApDX,EAAA,CAAA,EAAA,MAAA,EAAA;AACT,iBA0DT,6BAAA,CA1DS,OAAA,EA2Dd,aA3Dc,CA2DA,eA3DA,CAAA,EAAA,KAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAAf,iBAkEM,sBAAA,CAlEN,SAAA,EAkEwC,MAlExC,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAAA,MAAA;AACyC,iBA0EnC,uBAAA,CA1EmC,MAAA,EAAA;EAAa,SAAA,WAAA,EAAA,MAAA;EAqChD,SAAA,aAAkB,CAAA,EAAA,MAAA;EAalB,SAAA,WAAA,EAAmB,MAAA;AAOnC,CAAA,CAAA,EAAgB,MAAA;AAQA,iBAyBA,yBAAA,CAzBwC,KAAA,EAyBP,aAzBO,EAAA,WAAA,CAAA,EAyBsB,WAzBtB,CAAA,EAAA,MAAA;AASxC,iBAgEA,2BAAA,CAhEuB,MAAA,EAiE7B,MAjE6B,CAAA,MAAA,EAiEd,aAjEc,CAAA,GAAA,SAAA,EAAA,WAAA,CAAA,EAkEvB,WAlEuB,CAAA,EAAA,MAAA;AAgBvB,iBAwEA,uBAAA,CAxEiC,OAAA,EAAA,MAA6B,EAAA,EAAA,EA0ExE,mBA1EmF,EAAA,aAAA,EA2ExE,MA3EwE,CAAA,MAAA,EA2EzD,mBA3EyD,CAAA,CAAA,EAAA,MAAA;AAgDzE,iBAqCA,+BAAA,CArC2B,SAAA,EAAA,MAAA,EAAA,KAAA,EAqC+B,aArC/B,CAAA,EAAA,MAAA;AAClB,iBAwDT,kCAAA,CAxDS,YAAA,EAyDT,MAzDS,CAAA,MAAA,EAyDM,mBAzDN,CAAA,GAAA,SAAA,CAAA,EAAA,MAAA;AAAf,iBA6EM,8BAAA,CA7EN,YAAA,EA8EM,MA9EN,CAAA,MAAA,EA8EqB,mBA9ErB,CAAA,GAAA,SAAA,CAAA,EAAA,MAAA"}
@@ -1,3 +1,5 @@
1
+ import { t as isSafeTypeExpression } from "./type-expression-safety-7_1tfJXA.mjs";
2
+
1
3
  //#region src/domain-type-generation.ts
2
4
  function serializeValue(value) {
3
5
  if (value === null) return "null";
@@ -21,6 +23,24 @@ function generateRootsType(roots) {
21
23
  if (!roots || Object.keys(roots).length === 0) return "Record<string, string>";
22
24
  return `{ ${Object.entries(roots).map(([key, value]) => `readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`).join("; ")} }`;
23
25
  }
26
+ function contractFieldModifierSuffix(field) {
27
+ return (field.many === true ? "; readonly many: true" : "") + (field.dict === true ? "; readonly dict: true" : "");
28
+ }
29
+ function generateModelFieldEntry(fieldName, field) {
30
+ const mods = contractFieldModifierSuffix(field);
31
+ const { nullable, type } = field;
32
+ if (type.kind === "scalar") {
33
+ const typeParamsSpec = type.typeParams && Object.keys(type.typeParams).length > 0 ? `; readonly typeParams: ${serializeValue(type.typeParams)}` : "";
34
+ return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: 'scalar'; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${mods} }`;
35
+ }
36
+ if (type.kind === "valueObject") return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: 'valueObject'; readonly name: ${serializeValue(type.name)} }${mods} }`;
37
+ return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: ${serializeValue(type)}${mods} }`;
38
+ }
39
+ function generateModelFieldsType(fields) {
40
+ const fieldEntries = [];
41
+ for (const [fieldName, field] of Object.entries(fields)) fieldEntries.push(generateModelFieldEntry(fieldName, field));
42
+ return fieldEntries.length > 0 ? `{ ${fieldEntries.join("; ")} }` : "Record<string, never>";
43
+ }
24
44
  function generateModelRelationsType(relations) {
25
45
  const relationEntries = [];
26
46
  for (const [relName, rel] of Object.entries(relations)) {
@@ -41,6 +61,26 @@ function generateModelRelationsType(relations) {
41
61
  if (relationEntries.length === 0) return "Record<string, never>";
42
62
  return `{ ${relationEntries.join("; ")} }`;
43
63
  }
64
+ function generateModelsType(models, generateModelStorage) {
65
+ if (!models || Object.keys(models).length === 0) return "Record<string, never>";
66
+ const modelTypes = [];
67
+ for (const [modelName, model] of Object.entries(models).sort(([a], [b]) => a.localeCompare(b))) {
68
+ const fieldsType = generateModelFieldsType(model.fields);
69
+ const relationsType = generateModelRelationsType(model.relations);
70
+ const storageType = generateModelStorage(modelName, model);
71
+ const modelParts = [
72
+ `readonly fields: ${fieldsType}`,
73
+ `readonly relations: ${relationsType}`,
74
+ `readonly storage: ${storageType}`
75
+ ];
76
+ if (model.owner) modelParts.push(`readonly owner: ${serializeValue(model.owner)}`);
77
+ if (model.discriminator) modelParts.push(`readonly discriminator: ${serializeValue(model.discriminator)}`);
78
+ if (model.variants) modelParts.push(`readonly variants: ${serializeValue(model.variants)}`);
79
+ if (model.base) modelParts.push(`readonly base: ${serializeValue(model.base)}`);
80
+ modelTypes.push(`readonly ${modelName}: { ${modelParts.join("; ")} }`);
81
+ }
82
+ return `{ ${modelTypes.join("; ")} }`;
83
+ }
44
84
  function deduplicateImports(imports) {
45
85
  const seenKeys = /* @__PURE__ */ new Set();
46
86
  const result = [];
@@ -61,6 +101,14 @@ function generateImportLines(imports) {
61
101
  function generateCodecTypeIntersection(imports, named) {
62
102
  return imports.filter((imp) => imp.named === named).map((imp) => imp.alias).join(" & ") || "Record<string, never>";
63
103
  }
104
+ function serializeExecutionType(execution) {
105
+ const parts = ["readonly executionHash: ExecutionHash"];
106
+ for (const [key, value] of Object.entries(execution)) {
107
+ if (key === "executionHash") continue;
108
+ parts.push(`readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`);
109
+ }
110
+ return `{ ${parts.join("; ")} }`;
111
+ }
64
112
  function generateHashTypeAliases(hashes) {
65
113
  const executionHashType = hashes.executionHash ? `ExecutionHashBase<'${hashes.executionHash}'>` : "ExecutionHashBase<string>";
66
114
  return [
@@ -69,7 +117,97 @@ function generateHashTypeAliases(hashes) {
69
117
  `export type ProfileHash = ProfileHashBase<'${hashes.profileHash}'>;`
70
118
  ].join("\n");
71
119
  }
120
+ function generateFieldResolvedType(field, codecLookup) {
121
+ let baseType;
122
+ const { type } = field;
123
+ switch (type.kind) {
124
+ case "scalar": {
125
+ let resolved;
126
+ if (codecLookup && type.typeParams && Object.keys(type.typeParams).length > 0) {
127
+ const codec = codecLookup.get(type.codecId);
128
+ if (codec?.renderOutputType) {
129
+ const rendered = codec.renderOutputType(type.typeParams);
130
+ if (rendered && isSafeTypeExpression(rendered)) resolved = rendered;
131
+ }
132
+ }
133
+ baseType = resolved ?? `CodecTypes[${serializeValue(type.codecId)}]['output']`;
134
+ break;
135
+ }
136
+ case "valueObject":
137
+ baseType = type.name;
138
+ break;
139
+ case "union":
140
+ baseType = type.members.map((m) => {
141
+ if (m.kind === "scalar") return `CodecTypes[${serializeValue(m.codecId)}]['output']`;
142
+ return m.name;
143
+ }).join(" | ");
144
+ break;
145
+ default:
146
+ baseType = "unknown";
147
+ break;
148
+ }
149
+ if (field.many === true) baseType = `ReadonlyArray<${baseType}>`;
150
+ if (field.dict === true) baseType = `Readonly<Record<string, ${baseType}>>`;
151
+ if (field.nullable) baseType = `${baseType} | null`;
152
+ return baseType;
153
+ }
154
+ function generateFieldOutputTypesMap(models, codecLookup) {
155
+ if (!models || Object.keys(models).length === 0) return "Record<string, never>";
156
+ const modelEntries = [];
157
+ for (const [modelName, model] of Object.entries(models).sort(([a], [b]) => a.localeCompare(b))) {
158
+ if (!model) continue;
159
+ const fieldEntries = [];
160
+ for (const [fieldName, field] of Object.entries(model.fields)) {
161
+ const resolvedType = generateFieldResolvedType(field, codecLookup);
162
+ fieldEntries.push(`readonly ${serializeObjectKey(fieldName)}: ${resolvedType}`);
163
+ }
164
+ const fieldsType = fieldEntries.length > 0 ? `{ ${fieldEntries.join("; ")} }` : "Record<string, never>";
165
+ modelEntries.push(`readonly ${serializeObjectKey(modelName)}: ${fieldsType}`);
166
+ }
167
+ return `{ ${modelEntries.join("; ")} }`;
168
+ }
169
+ function generateValueObjectType(_voName, vo, _valueObjects) {
170
+ const fieldEntries = [];
171
+ for (const [fieldName, field] of Object.entries(vo.fields)) {
172
+ const tsType = generateFieldResolvedType(field);
173
+ fieldEntries.push(`readonly ${serializeObjectKey(fieldName)}: ${tsType}`);
174
+ }
175
+ return fieldEntries.length > 0 ? `{ ${fieldEntries.join("; ")} }` : "Record<string, never>";
176
+ }
177
+ function generateContractFieldDescriptor(fieldName, field) {
178
+ const mods = [];
179
+ if (field.many === true) mods.push("; readonly many: true");
180
+ if (field.dict === true) mods.push("; readonly dict: true");
181
+ const modStr = mods.join("");
182
+ const { type } = field;
183
+ if (type.kind === "scalar") {
184
+ const typeParamsSpec = type.typeParams && Object.keys(type.typeParams).length > 0 ? `; readonly typeParams: ${serializeValue(type.typeParams)}` : "";
185
+ return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: 'scalar'; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${modStr} }`;
186
+ }
187
+ if (type.kind === "valueObject") return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: 'valueObject'; readonly name: ${serializeValue(type.name)} }${modStr} }`;
188
+ return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: ${serializeValue(type)}${modStr} }`;
189
+ }
190
+ function generateValueObjectsDescriptorType(valueObjects) {
191
+ if (!valueObjects || Object.keys(valueObjects).length === 0) return "Record<string, never>";
192
+ const voEntries = [];
193
+ for (const [voName, vo] of Object.entries(valueObjects)) {
194
+ const fieldEntries = [];
195
+ for (const [fieldName, field] of Object.entries(vo.fields)) fieldEntries.push(generateContractFieldDescriptor(fieldName, field));
196
+ const fieldsType = fieldEntries.length > 0 ? `{ ${fieldEntries.join("; ")} }` : "Record<string, never>";
197
+ voEntries.push(`readonly ${serializeObjectKey(voName)}: { readonly fields: ${fieldsType} }`);
198
+ }
199
+ return `{ ${voEntries.join("; ")} }`;
200
+ }
201
+ function generateValueObjectTypeAliases(valueObjects) {
202
+ if (!valueObjects || Object.keys(valueObjects).length === 0) return "";
203
+ const aliases = [];
204
+ for (const [voName, vo] of Object.entries(valueObjects)) {
205
+ const voType = generateValueObjectType(voName, vo, valueObjects);
206
+ aliases.push(`export type ${voName} = ${voType};`);
207
+ }
208
+ return aliases.join("\n");
209
+ }
72
210
 
73
211
  //#endregion
74
- export { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
212
+ export { deduplicateImports, generateCodecTypeIntersection, generateContractFieldDescriptor, generateFieldOutputTypesMap, generateFieldResolvedType, generateHashTypeAliases, generateImportLines, generateModelFieldEntry, generateModelFieldsType, generateModelRelationsType, generateModelsType, generateRootsType, generateValueObjectType, generateValueObjectTypeAliases, generateValueObjectsDescriptorType, serializeExecutionType, serializeObjectKey, serializeValue };
75
213
  //# sourceMappingURL=domain-type-generation.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"domain-type-generation.mjs","names":["entries: string[]","relationEntries: string[]","parts: string[]","result: TypesImportSpec[]"],"sources":["../src/domain-type-generation.ts"],"sourcesContent":["import type { TypesImportSpec } from '@prisma-next/contract/types';\n\nexport function serializeValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value === 'string') {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n return `'${escaped}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'bigint') {\n return `${value}n`;\n }\n if (Array.isArray(value)) {\n const items = value.map((v) => serializeValue(v)).join(', ');\n return `readonly [${items}]`;\n }\n if (typeof value === 'object') {\n const entries: string[] = [];\n for (const [k, v] of Object.entries(value)) {\n entries.push(`readonly ${serializeObjectKey(k)}: ${serializeValue(v)}`);\n }\n return `{ ${entries.join('; ')} }`;\n }\n return 'unknown';\n}\n\nexport function serializeObjectKey(key: string): string {\n if (/^[$A-Z_a-z][$\\w]*$/.test(key)) {\n return key;\n }\n return serializeValue(key);\n}\n\nexport function generateRootsType(roots: Record<string, string> | undefined): string {\n if (!roots || Object.keys(roots).length === 0) {\n return 'Record<string, string>';\n }\n const entries = Object.entries(roots)\n .map(([key, value]) => `readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`)\n .join('; ');\n return `{ ${entries} }`;\n}\n\nexport function generateModelRelationsType(relations: Record<string, unknown>): string {\n const relationEntries: string[] = [];\n\n for (const [relName, rel] of Object.entries(relations)) {\n if (typeof rel !== 'object' || rel === null) continue;\n const relObj = rel as Record<string, unknown>;\n const parts: string[] = [];\n\n if (relObj['to']) parts.push(`readonly to: ${serializeValue(relObj['to'])}`);\n if (relObj['cardinality'])\n parts.push(`readonly cardinality: ${serializeValue(relObj['cardinality'])}`);\n\n const on = relObj['on'] as { localFields?: string[]; targetFields?: string[] } | undefined;\n if (on && (!on.localFields || !on.targetFields)) {\n throw new Error(\n `Relation \"${relName}\" has an \"on\" block but is missing localFields or targetFields`,\n );\n }\n if (on?.localFields && on.targetFields) {\n const localFields = on.localFields.map((f) => serializeValue(f)).join(', ');\n const targetFields = on.targetFields.map((f) => serializeValue(f)).join(', ');\n parts.push(\n `readonly on: { readonly localFields: readonly [${localFields}]; readonly targetFields: readonly [${targetFields}] }`,\n );\n }\n\n if (parts.length > 0) {\n relationEntries.push(`readonly ${relName}: { ${parts.join('; ')} }`);\n }\n }\n\n if (relationEntries.length === 0) {\n return 'Record<string, never>';\n }\n\n return `{ ${relationEntries.join('; ')} }`;\n}\n\nexport function deduplicateImports(imports: TypesImportSpec[]): TypesImportSpec[] {\n const seenKeys = new Set<string>();\n const result: TypesImportSpec[] = [];\n for (const imp of imports) {\n const key = `${imp.package}::${imp.named}`;\n if (!seenKeys.has(key)) {\n seenKeys.add(key);\n result.push(imp);\n }\n }\n return result;\n}\n\nexport function generateImportLines(imports: TypesImportSpec[]): string[] {\n return imports.map((imp) => {\n const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;\n return `import type { ${importClause} } from '${imp.package}';`;\n });\n}\n\nexport function generateCodecTypeIntersection(\n imports: ReadonlyArray<TypesImportSpec>,\n named: string,\n): string {\n const aliases = imports.filter((imp) => imp.named === named).map((imp) => imp.alias);\n return aliases.join(' & ') || 'Record<string, never>';\n}\n\nexport function generateHashTypeAliases(hashes: {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n}): string {\n const executionHashType = hashes.executionHash\n ? `ExecutionHashBase<'${hashes.executionHash}'>`\n : 'ExecutionHashBase<string>';\n\n return [\n `export type StorageHash = StorageHashBase<'${hashes.storageHash}'>;`,\n `export type ExecutionHash = ${executionHashType};`,\n `export type ProfileHash = ProfileHashBase<'${hashes.profileHash}'>;`,\n ].join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"domain-type-generation.mjs","names":["entries: string[]","fieldEntries: string[]","relationEntries: string[]","parts: string[]","modelTypes: string[]","modelParts: string[]","result: TypesImportSpec[]","baseType: string","resolved: string | undefined","modelEntries: string[]","mods: string[]","voEntries: string[]","aliases: string[]"],"sources":["../src/domain-type-generation.ts"],"sourcesContent":["import type {\n ContractField,\n ContractModel,\n ContractValueObject,\n} from '@prisma-next/contract/types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type { TypesImportSpec } from '@prisma-next/framework-components/emission';\nimport { isSafeTypeExpression } from './type-expression-safety';\n\nexport function serializeValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return 'undefined';\n }\n if (typeof value === 'string') {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n return `'${escaped}'`;\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'bigint') {\n return `${value}n`;\n }\n if (Array.isArray(value)) {\n const items = value.map((v) => serializeValue(v)).join(', ');\n return `readonly [${items}]`;\n }\n if (typeof value === 'object') {\n const entries: string[] = [];\n for (const [k, v] of Object.entries(value)) {\n entries.push(`readonly ${serializeObjectKey(k)}: ${serializeValue(v)}`);\n }\n return `{ ${entries.join('; ')} }`;\n }\n return 'unknown';\n}\n\nexport function serializeObjectKey(key: string): string {\n if (/^[$A-Z_a-z][$\\w]*$/.test(key)) {\n return key;\n }\n return serializeValue(key);\n}\n\nexport function generateRootsType(roots: Record<string, string> | undefined): string {\n if (!roots || Object.keys(roots).length === 0) {\n return 'Record<string, string>';\n }\n const entries = Object.entries(roots)\n .map(([key, value]) => `readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`)\n .join('; ');\n return `{ ${entries} }`;\n}\n\nfunction contractFieldModifierSuffix(field: ContractField): string {\n const many = field.many === true ? '; readonly many: true' : '';\n const dict = field.dict === true ? '; readonly dict: true' : '';\n return many + dict;\n}\n\nexport function generateModelFieldEntry(fieldName: string, field: ContractField): string {\n const mods = contractFieldModifierSuffix(field);\n const { nullable, type } = field;\n if (type.kind === 'scalar') {\n const typeParamsSpec =\n type.typeParams && Object.keys(type.typeParams).length > 0\n ? `; readonly typeParams: ${serializeValue(type.typeParams)}`\n : '';\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: 'scalar'; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${mods} }`;\n }\n if (type.kind === 'valueObject') {\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: { readonly kind: 'valueObject'; readonly name: ${serializeValue(type.name)} }${mods} }`;\n }\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${nullable}; readonly type: ${serializeValue(type)}${mods} }`;\n}\n\nexport function generateModelFieldsType(fields: Record<string, ContractField>): string {\n const fieldEntries: string[] = [];\n for (const [fieldName, field] of Object.entries(fields)) {\n fieldEntries.push(generateModelFieldEntry(fieldName, field));\n }\n return fieldEntries.length > 0 ? `{ ${fieldEntries.join('; ')} }` : 'Record<string, never>';\n}\n\nexport function generateModelRelationsType(relations: Record<string, unknown>): string {\n const relationEntries: string[] = [];\n\n for (const [relName, rel] of Object.entries(relations)) {\n if (typeof rel !== 'object' || rel === null) continue;\n const relObj = rel as Record<string, unknown>;\n const parts: string[] = [];\n\n if (relObj['to']) parts.push(`readonly to: ${serializeValue(relObj['to'])}`);\n if (relObj['cardinality'])\n parts.push(`readonly cardinality: ${serializeValue(relObj['cardinality'])}`);\n\n const on = relObj['on'] as { localFields?: string[]; targetFields?: string[] } | undefined;\n if (on && (!on.localFields || !on.targetFields)) {\n throw new Error(\n `Relation \"${relName}\" has an \"on\" block but is missing localFields or targetFields`,\n );\n }\n if (on?.localFields && on.targetFields) {\n const localFields = on.localFields.map((f) => serializeValue(f)).join(', ');\n const targetFields = on.targetFields.map((f) => serializeValue(f)).join(', ');\n parts.push(\n `readonly on: { readonly localFields: readonly [${localFields}]; readonly targetFields: readonly [${targetFields}] }`,\n );\n }\n\n if (parts.length > 0) {\n relationEntries.push(`readonly ${relName}: { ${parts.join('; ')} }`);\n }\n }\n\n if (relationEntries.length === 0) {\n return 'Record<string, never>';\n }\n\n return `{ ${relationEntries.join('; ')} }`;\n}\n\nexport function generateModelsType(\n models: Record<string, ContractModel>,\n generateModelStorage: (modelName: string, model: ContractModel) => string,\n): string {\n if (!models || Object.keys(models).length === 0) {\n return 'Record<string, never>';\n }\n\n const modelTypes: string[] = [];\n for (const [modelName, model] of Object.entries(models).sort(([a], [b]) => a.localeCompare(b))) {\n const fieldsType = generateModelFieldsType(model.fields);\n const relationsType = generateModelRelationsType(model.relations);\n const storageType = generateModelStorage(modelName, model);\n\n const modelParts: string[] = [\n `readonly fields: ${fieldsType}`,\n `readonly relations: ${relationsType}`,\n `readonly storage: ${storageType}`,\n ];\n\n if (model.owner) {\n modelParts.push(`readonly owner: ${serializeValue(model.owner)}`);\n }\n if (model.discriminator) {\n modelParts.push(`readonly discriminator: ${serializeValue(model.discriminator)}`);\n }\n if (model.variants) {\n modelParts.push(`readonly variants: ${serializeValue(model.variants)}`);\n }\n if (model.base) {\n modelParts.push(`readonly base: ${serializeValue(model.base)}`);\n }\n\n modelTypes.push(`readonly ${modelName}: { ${modelParts.join('; ')} }`);\n }\n\n return `{ ${modelTypes.join('; ')} }`;\n}\n\nexport function deduplicateImports(imports: TypesImportSpec[]): TypesImportSpec[] {\n const seenKeys = new Set<string>();\n const result: TypesImportSpec[] = [];\n for (const imp of imports) {\n const key = `${imp.package}::${imp.named}`;\n if (!seenKeys.has(key)) {\n seenKeys.add(key);\n result.push(imp);\n }\n }\n return result;\n}\n\nexport function generateImportLines(imports: TypesImportSpec[]): string[] {\n return imports.map((imp) => {\n const importClause = imp.named === imp.alias ? imp.named : `${imp.named} as ${imp.alias}`;\n return `import type { ${importClause} } from '${imp.package}';`;\n });\n}\n\nexport function generateCodecTypeIntersection(\n imports: ReadonlyArray<TypesImportSpec>,\n named: string,\n): string {\n const aliases = imports.filter((imp) => imp.named === named).map((imp) => imp.alias);\n return aliases.join(' & ') || 'Record<string, never>';\n}\n\nexport function serializeExecutionType(execution: Record<string, unknown>): string {\n const parts: string[] = ['readonly executionHash: ExecutionHash'];\n for (const [key, value] of Object.entries(execution)) {\n if (key === 'executionHash') continue;\n parts.push(`readonly ${serializeObjectKey(key)}: ${serializeValue(value)}`);\n }\n return `{ ${parts.join('; ')} }`;\n}\n\nexport function generateHashTypeAliases(hashes: {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n}): string {\n const executionHashType = hashes.executionHash\n ? `ExecutionHashBase<'${hashes.executionHash}'>`\n : 'ExecutionHashBase<string>';\n\n return [\n `export type StorageHash = StorageHashBase<'${hashes.storageHash}'>;`,\n `export type ExecutionHash = ${executionHashType};`,\n `export type ProfileHash = ProfileHashBase<'${hashes.profileHash}'>;`,\n ].join('\\n');\n}\n\nexport function generateFieldResolvedType(field: ContractField, codecLookup?: CodecLookup): string {\n let baseType: string;\n const { type } = field;\n\n switch (type.kind) {\n case 'scalar': {\n let resolved: string | undefined;\n if (codecLookup && type.typeParams && Object.keys(type.typeParams).length > 0) {\n const codec = codecLookup.get(type.codecId);\n if (codec?.renderOutputType) {\n const rendered = codec.renderOutputType(type.typeParams);\n if (rendered && isSafeTypeExpression(rendered)) {\n resolved = rendered;\n }\n }\n }\n baseType = resolved ?? `CodecTypes[${serializeValue(type.codecId)}]['output']`;\n break;\n }\n case 'valueObject':\n baseType = type.name;\n break;\n case 'union': {\n const memberTypes = type.members.map((m) => {\n if (m.kind === 'scalar') return `CodecTypes[${serializeValue(m.codecId)}]['output']`;\n return m.name;\n });\n baseType = memberTypes.join(' | ');\n break;\n }\n default:\n baseType = 'unknown';\n break;\n }\n\n if (field.many === true) {\n baseType = `ReadonlyArray<${baseType}>`;\n }\n if (field.dict === true) {\n baseType = `Readonly<Record<string, ${baseType}>>`;\n }\n if (field.nullable) {\n baseType = `${baseType} | null`;\n }\n\n return baseType;\n}\n\nexport function generateFieldOutputTypesMap(\n models: Record<string, ContractModel> | undefined,\n codecLookup?: CodecLookup,\n): string {\n if (!models || Object.keys(models).length === 0) {\n return 'Record<string, never>';\n }\n\n const modelEntries: string[] = [];\n for (const [modelName, model] of Object.entries(models).sort(([a], [b]) => a.localeCompare(b))) {\n if (!model) continue;\n const fieldEntries: string[] = [];\n for (const [fieldName, field] of Object.entries(model.fields)) {\n const resolvedType = generateFieldResolvedType(field, codecLookup);\n fieldEntries.push(`readonly ${serializeObjectKey(fieldName)}: ${resolvedType}`);\n }\n const fieldsType =\n fieldEntries.length > 0 ? `{ ${fieldEntries.join('; ')} }` : 'Record<string, never>';\n modelEntries.push(`readonly ${serializeObjectKey(modelName)}: ${fieldsType}`);\n }\n\n return `{ ${modelEntries.join('; ')} }`;\n}\n\nexport function generateValueObjectType(\n _voName: string,\n vo: ContractValueObject,\n _valueObjects: Record<string, ContractValueObject>,\n): string {\n const fieldEntries: string[] = [];\n for (const [fieldName, field] of Object.entries(vo.fields)) {\n const tsType = generateFieldResolvedType(field);\n fieldEntries.push(`readonly ${serializeObjectKey(fieldName)}: ${tsType}`);\n }\n return fieldEntries.length > 0 ? `{ ${fieldEntries.join('; ')} }` : 'Record<string, never>';\n}\n\nexport function generateContractFieldDescriptor(fieldName: string, field: ContractField): string {\n const mods: string[] = [];\n if (field.many === true) mods.push('; readonly many: true');\n if (field.dict === true) mods.push('; readonly dict: true');\n const modStr = mods.join('');\n\n const { type } = field;\n if (type.kind === 'scalar') {\n const typeParamsSpec =\n type.typeParams && Object.keys(type.typeParams).length > 0\n ? `; readonly typeParams: ${serializeValue(type.typeParams)}`\n : '';\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: 'scalar'; readonly codecId: ${serializeValue(type.codecId)}${typeParamsSpec} }${modStr} }`;\n }\n if (type.kind === 'valueObject') {\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: { readonly kind: 'valueObject'; readonly name: ${serializeValue(type.name)} }${modStr} }`;\n }\n return `readonly ${serializeObjectKey(fieldName)}: { readonly nullable: ${field.nullable}; readonly type: ${serializeValue(type)}${modStr} }`;\n}\n\nexport function generateValueObjectsDescriptorType(\n valueObjects: Record<string, ContractValueObject> | undefined,\n): string {\n if (!valueObjects || Object.keys(valueObjects).length === 0) {\n return 'Record<string, never>';\n }\n\n const voEntries: string[] = [];\n for (const [voName, vo] of Object.entries(valueObjects)) {\n const fieldEntries: string[] = [];\n for (const [fieldName, field] of Object.entries(vo.fields)) {\n fieldEntries.push(generateContractFieldDescriptor(fieldName, field));\n }\n const fieldsType =\n fieldEntries.length > 0 ? `{ ${fieldEntries.join('; ')} }` : 'Record<string, never>';\n voEntries.push(`readonly ${serializeObjectKey(voName)}: { readonly fields: ${fieldsType} }`);\n }\n\n return `{ ${voEntries.join('; ')} }`;\n}\n\nexport function generateValueObjectTypeAliases(\n valueObjects: Record<string, ContractValueObject> | undefined,\n): string {\n if (!valueObjects || Object.keys(valueObjects).length === 0) {\n return '';\n }\n\n const aliases: string[] = [];\n for (const [voName, vo] of Object.entries(valueObjects)) {\n const voType = generateValueObjectType(voName, vo, valueObjects);\n aliases.push(`export type ${voName} = ${voType};`);\n }\n return aliases.join('\\n');\n}\n"],"mappings":"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"}
@@ -1,4 +1,39 @@
1
- import { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
2
- import { EmitOptions, EmitResult, emit } from "@prisma-next/core-control-plane/emission";
3
- import { TargetFamilyHook, TypesImportSpec, ValidationContext } from "@prisma-next/contract/types";
4
- export { type EmitOptions, type EmitResult, type TargetFamilyHook, type TypesImportSpec, type ValidationContext, deduplicateImports, emit, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
1
+ import { deduplicateImports, generateCodecTypeIntersection, generateFieldOutputTypesMap, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
2
+ import { Contract } from "@prisma-next/contract/types";
3
+ import { CodecLookup } from "@prisma-next/framework-components/codec";
4
+ import { EmissionSpi, GenerateContractTypesOptions, TypesImportSpec } from "@prisma-next/framework-components/emission";
5
+
6
+ //#region src/emit-types.d.ts
7
+
8
+ /**
9
+ * The subset of ControlStack that emit() reads.
10
+ * All fields are optional so tests can pass minimal objects.
11
+ * A full ControlStack satisfies this via structural typing.
12
+ */
13
+ interface EmitStackInput {
14
+ readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
15
+ readonly operationTypeImports?: ReadonlyArray<TypesImportSpec>;
16
+ readonly queryOperationTypeImports?: ReadonlyArray<TypesImportSpec>;
17
+ readonly extensionIds?: ReadonlyArray<string>;
18
+ readonly codecLookup?: CodecLookup;
19
+ }
20
+ interface EmitResult {
21
+ readonly contractJson: string;
22
+ readonly contractDts: string;
23
+ readonly storageHash: string;
24
+ readonly executionHash?: string;
25
+ readonly profileHash: string;
26
+ }
27
+ //#endregion
28
+ //#region src/emit.d.ts
29
+ declare function emit(contract: Contract, stack: EmitStackInput, targetFamily: EmissionSpi): Promise<EmitResult>;
30
+ //#endregion
31
+ //#region src/generate-contract-dts.d.ts
32
+ declare function generateContractDts(contract: Contract, emitter: EmissionSpi, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec>, hashes: {
33
+ readonly storageHash: string;
34
+ readonly executionHash?: string;
35
+ readonly profileHash: string;
36
+ }, options?: GenerateContractTypesOptions, codecLookup?: CodecLookup): string;
37
+ //#endregion
38
+ export { type EmitResult, type EmitStackInput, deduplicateImports, emit, generateCodecTypeIntersection, generateContractDts, generateFieldOutputTypesMap, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
39
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/emit-types.ts","../../src/emit.ts","../../src/generate-contract-dts.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAQA;AAC4C,UAD3B,cAAA,CAC2B;EAAd,SAAA,gBAAA,CAAA,EAAA,aAAA,CAAc,eAAd,CAAA;EACkB,SAAA,oBAAA,CAAA,EAAd,aAAc,CAAA,eAAA,CAAA;EAAd,SAAA,yBAAA,CAAA,EACK,aADL,CACmB,eADnB,CAAA;EACmB,SAAA,YAAA,CAAA,EAC3B,aAD2B,CAAA,MAAA,CAAA;EAAd,SAAA,WAAA,CAAA,EAEd,WAFc;;AAEd,UAGR,UAAA,CAHQ;EAAW,SAAA,YAAA,EAAA,MAAA;EAGnB,SAAA,WAAU,EAAA,MAAA;;;;ACN3B;;;iBAAsB,IAAA,WACV,iBACH,8BACO,cACb,QAAQ;;;iBCOK,mBAAA,WACJ,mBACD,+BACS,cAAc,wCACV,cAAc;;;EFjBrB,SAAA,WAAc,EAAA,MAAA;CACa,EAAA,OAAA,CAAA,EEsBhC,4BFtBgC,EAAA,WAAA,CAAA,EEuB5B,WFvB4B,CAAA,EAAA,MAAA"}
@@ -1,4 +1,105 @@
1
- import { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
2
- import { emit } from "@prisma-next/core-control-plane/emission";
1
+ import { deduplicateImports, generateCodecTypeIntersection, generateFieldOutputTypesMap, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateModelsType, generateRootsType, generateValueObjectTypeAliases, generateValueObjectsDescriptorType, serializeExecutionType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
2
+ import { canonicalizeContractToObject } from "@prisma-next/contract/hashing";
3
+ import { ifDefined } from "@prisma-next/utils/defined";
4
+ import { format } from "prettier";
3
5
 
4
- export { deduplicateImports, emit, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
6
+ //#region src/generate-contract-dts.ts
7
+ function generateContractDts(contract, emitter, codecTypeImports, operationTypeImports, hashes, options, codecLookup) {
8
+ const allImports = [...codecTypeImports, ...operationTypeImports];
9
+ if (options?.queryOperationTypeImports) allImports.push(...options.queryOperationTypeImports);
10
+ const importLines = generateImportLines(deduplicateImports(allImports));
11
+ const familyImportLines = emitter.getFamilyImports();
12
+ const hashAliases = generateHashTypeAliases(hashes);
13
+ const codecTypes = generateCodecTypeIntersection(codecTypeImports, "CodecTypes");
14
+ const operationTypes = generateCodecTypeIntersection(operationTypeImports, "OperationTypes");
15
+ const familyTypeAliases = emitter.getFamilyTypeAliases(options);
16
+ const typeMapsExpr = emitter.getTypeMapsExpression();
17
+ const storageType = emitter.generateStorageType(contract, "StorageHash");
18
+ const modelsType = generateModelsType(contract.models, (name, model) => emitter.generateModelStorageType(name, model));
19
+ const rootsType = generateRootsType(contract.roots);
20
+ const valueObjects = contract.valueObjects;
21
+ const valueObjectTypeAliases = generateValueObjectTypeAliases(valueObjects);
22
+ const valueObjectsDescriptor = generateValueObjectsDescriptorType(valueObjects);
23
+ const executionClause = contract.execution !== void 0 ? `\n readonly execution: ${serializeExecutionType(contract.execution)};` : "";
24
+ const fieldOutputTypesMap = generateFieldOutputTypesMap(contract.models, codecLookup);
25
+ const contractWrapper = emitter.getContractWrapper("ContractBase", "TypeMaps");
26
+ return `// ⚠️ GENERATED FILE - DO NOT EDIT
27
+ // This file is automatically generated by 'prisma-next contract emit'.
28
+ // To regenerate, run: prisma-next contract emit
29
+ ${importLines.join("\n")}
30
+
31
+ ${familyImportLines.join("\n")}
32
+ import type {
33
+ Contract as ContractType,
34
+ ExecutionHashBase,
35
+ ProfileHashBase,
36
+ StorageHashBase,
37
+ } from '@prisma-next/contract/types';
38
+
39
+ ${hashAliases}
40
+
41
+ export type CodecTypes = ${codecTypes};
42
+ export type OperationTypes = ${operationTypes};
43
+ ${familyTypeAliases}
44
+ ${valueObjectTypeAliases}
45
+ export type FieldOutputTypes = ${fieldOutputTypesMap};
46
+ export type TypeMaps = ${typeMapsExpr};
47
+
48
+ type ContractBase = ContractType<
49
+ ${storageType},
50
+ ${modelsType}
51
+ > & {
52
+ readonly target: ${serializeValue(contract.target)};
53
+ readonly targetFamily: ${serializeValue(contract.targetFamily)};
54
+ readonly roots: ${rootsType};
55
+ readonly capabilities: ${serializeValue(contract.capabilities)};
56
+ readonly extensionPacks: ${serializeValue(contract.extensionPacks)};${executionClause}
57
+ readonly meta: ${serializeValue(contract.meta)};
58
+ ${valueObjects ? `readonly valueObjects: ${valueObjectsDescriptor};` : ""}
59
+ readonly profileHash: ProfileHash;
60
+ };
61
+
62
+ ${contractWrapper}
63
+ `;
64
+ }
65
+
66
+ //#endregion
67
+ //#region src/emit.ts
68
+ const SCHEMA_VERSION = "1";
69
+ async function emit(contract, stack, targetFamily) {
70
+ const { codecTypeImports, operationTypeImports, queryOperationTypeImports } = stack;
71
+ const { storageHash } = contract.storage;
72
+ const executionHash = contract.execution?.executionHash;
73
+ const { profileHash } = contract;
74
+ const canonicalized = canonicalizeContractToObject(contract, { schemaVersion: SCHEMA_VERSION });
75
+ const contractJsonString = JSON.stringify({
76
+ ...canonicalized,
77
+ _generated: {
78
+ warning: "⚠️ GENERATED FILE - DO NOT EDIT",
79
+ message: "This file is automatically generated by \"prisma-next contract emit\".",
80
+ regenerate: "To regenerate, run: prisma-next contract emit"
81
+ }
82
+ }, null, 2);
83
+ const generateOptions = queryOperationTypeImports ? { queryOperationTypeImports } : void 0;
84
+ const contractTypeHashes = {
85
+ storageHash,
86
+ ...ifDefined("executionHash", executionHash),
87
+ profileHash
88
+ };
89
+ return {
90
+ contractJson: contractJsonString,
91
+ contractDts: await format(generateContractDts(contract, targetFamily, codecTypeImports ?? [], operationTypeImports ?? [], contractTypeHashes, generateOptions, stack.codecLookup), {
92
+ parser: "typescript",
93
+ singleQuote: true,
94
+ semi: true,
95
+ printWidth: 100
96
+ }),
97
+ storageHash,
98
+ ...ifDefined("executionHash", executionHash),
99
+ profileHash
100
+ };
101
+ }
102
+
103
+ //#endregion
104
+ export { deduplicateImports, emit, generateCodecTypeIntersection, generateContractDts, generateFieldOutputTypesMap, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue };
105
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["allImports: TypesImportSpec[]"],"sources":["../../src/generate-contract-dts.ts","../../src/emit.ts"],"sourcesContent":["import type { Contract, ContractModel, ContractValueObject } from '@prisma-next/contract/types';\nimport type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport type {\n EmissionSpi,\n GenerateContractTypesOptions,\n TypesImportSpec,\n} from '@prisma-next/framework-components/emission';\nimport {\n deduplicateImports,\n generateCodecTypeIntersection,\n generateFieldOutputTypesMap,\n generateHashTypeAliases,\n generateImportLines,\n generateModelsType,\n generateRootsType,\n generateValueObjectsDescriptorType,\n generateValueObjectTypeAliases,\n serializeExecutionType,\n serializeValue,\n} from './domain-type-generation';\n\nexport function generateContractDts(\n contract: Contract,\n emitter: EmissionSpi,\n codecTypeImports: ReadonlyArray<TypesImportSpec>,\n operationTypeImports: ReadonlyArray<TypesImportSpec>,\n hashes: {\n readonly storageHash: string;\n readonly executionHash?: string;\n readonly profileHash: string;\n },\n options?: GenerateContractTypesOptions,\n codecLookup?: CodecLookup,\n): string {\n const allImports: TypesImportSpec[] = [...codecTypeImports, ...operationTypeImports];\n if (options?.queryOperationTypeImports) {\n allImports.push(...options.queryOperationTypeImports);\n }\n const uniqueImports = deduplicateImports(allImports);\n const importLines = generateImportLines(uniqueImports);\n\n const familyImportLines = emitter.getFamilyImports();\n\n const hashAliases = generateHashTypeAliases(hashes);\n\n const codecTypes = generateCodecTypeIntersection(codecTypeImports, 'CodecTypes');\n const operationTypes = generateCodecTypeIntersection(operationTypeImports, 'OperationTypes');\n\n const familyTypeAliases = emitter.getFamilyTypeAliases(options);\n\n const typeMapsExpr = emitter.getTypeMapsExpression();\n\n const storageType = emitter.generateStorageType(contract, 'StorageHash');\n\n const modelsType = generateModelsType(\n contract.models as Record<string, ContractModel>,\n (name, model) => emitter.generateModelStorageType(name, model),\n );\n\n const rootsType = generateRootsType(contract.roots);\n\n const valueObjects = contract.valueObjects as Record<string, ContractValueObject> | undefined;\n const valueObjectTypeAliases = generateValueObjectTypeAliases(valueObjects);\n const valueObjectsDescriptor = generateValueObjectsDescriptorType(valueObjects);\n\n const executionClause =\n contract.execution !== undefined\n ? `\\n readonly execution: ${serializeExecutionType(contract.execution)};`\n : '';\n\n const fieldOutputTypesMap = generateFieldOutputTypesMap(\n contract.models as Record<string, ContractModel> | undefined,\n codecLookup,\n );\n\n const contractWrapper = emitter.getContractWrapper('ContractBase', 'TypeMaps');\n\n return `// ⚠️ GENERATED FILE - DO NOT EDIT\n// This file is automatically generated by 'prisma-next contract emit'.\n// To regenerate, run: prisma-next contract emit\n${importLines.join('\\n')}\n\n${familyImportLines.join('\\n')}\nimport type {\n Contract as ContractType,\n ExecutionHashBase,\n ProfileHashBase,\n StorageHashBase,\n} from '@prisma-next/contract/types';\n\n${hashAliases}\n\nexport type CodecTypes = ${codecTypes};\nexport type OperationTypes = ${operationTypes};\n${familyTypeAliases}\n${valueObjectTypeAliases}\nexport type FieldOutputTypes = ${fieldOutputTypesMap};\nexport type TypeMaps = ${typeMapsExpr};\n\ntype ContractBase = ContractType<\n${storageType},\n${modelsType}\n> & {\n readonly target: ${serializeValue(contract.target)};\n readonly targetFamily: ${serializeValue(contract.targetFamily)};\n readonly roots: ${rootsType};\n readonly capabilities: ${serializeValue(contract.capabilities)};\n readonly extensionPacks: ${serializeValue(contract.extensionPacks)};${executionClause}\n readonly meta: ${serializeValue(contract.meta)};\n ${valueObjects ? `readonly valueObjects: ${valueObjectsDescriptor};` : ''}\n readonly profileHash: ProfileHash;\n};\n\n${contractWrapper}\n`;\n}\n","import { canonicalizeContractToObject } from '@prisma-next/contract/hashing';\nimport type { Contract } from '@prisma-next/contract/types';\nimport type { EmissionSpi } from '@prisma-next/framework-components/emission';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { format } from 'prettier';\nimport type { EmitResult, EmitStackInput } from './emit-types';\nimport { generateContractDts } from './generate-contract-dts';\n\nconst SCHEMA_VERSION = '1';\n\nexport async function emit(\n contract: Contract,\n stack: EmitStackInput,\n targetFamily: EmissionSpi,\n): Promise<EmitResult> {\n const { codecTypeImports, operationTypeImports, queryOperationTypeImports } = stack;\n\n const { storageHash } = contract.storage;\n const executionHash = contract.execution?.executionHash;\n const { profileHash } = contract;\n\n const canonicalized = canonicalizeContractToObject(contract, {\n schemaVersion: SCHEMA_VERSION,\n });\n const contractJsonString = JSON.stringify(\n {\n ...canonicalized,\n _generated: {\n warning: '⚠️ GENERATED FILE - DO NOT EDIT',\n message: 'This file is automatically generated by \"prisma-next contract emit\".',\n regenerate: 'To regenerate, run: prisma-next contract emit',\n },\n },\n null,\n 2,\n );\n\n const generateOptions = queryOperationTypeImports ? { queryOperationTypeImports } : undefined;\n\n const contractTypeHashes = {\n storageHash,\n ...ifDefined('executionHash', executionHash),\n profileHash,\n };\n const contractDtsRaw = generateContractDts(\n contract,\n targetFamily,\n codecTypeImports ?? [],\n operationTypeImports ?? [],\n contractTypeHashes,\n generateOptions,\n stack.codecLookup,\n );\n const contractDts = await format(contractDtsRaw, {\n parser: 'typescript',\n singleQuote: true,\n semi: true,\n printWidth: 100,\n });\n\n return {\n contractJson: contractJsonString,\n contractDts,\n storageHash,\n ...ifDefined('executionHash', executionHash),\n profileHash,\n };\n}\n"],"mappings":";;;;;;AAqBA,SAAgB,oBACd,UACA,SACA,kBACA,sBACA,QAKA,SACA,aACQ;CACR,MAAMA,aAAgC,CAAC,GAAG,kBAAkB,GAAG,qBAAqB;AACpF,KAAI,SAAS,0BACX,YAAW,KAAK,GAAG,QAAQ,0BAA0B;CAGvD,MAAM,cAAc,oBADE,mBAAmB,WAAW,CACE;CAEtD,MAAM,oBAAoB,QAAQ,kBAAkB;CAEpD,MAAM,cAAc,wBAAwB,OAAO;CAEnD,MAAM,aAAa,8BAA8B,kBAAkB,aAAa;CAChF,MAAM,iBAAiB,8BAA8B,sBAAsB,iBAAiB;CAE5F,MAAM,oBAAoB,QAAQ,qBAAqB,QAAQ;CAE/D,MAAM,eAAe,QAAQ,uBAAuB;CAEpD,MAAM,cAAc,QAAQ,oBAAoB,UAAU,cAAc;CAExE,MAAM,aAAa,mBACjB,SAAS,SACR,MAAM,UAAU,QAAQ,yBAAyB,MAAM,MAAM,CAC/D;CAED,MAAM,YAAY,kBAAkB,SAAS,MAAM;CAEnD,MAAM,eAAe,SAAS;CAC9B,MAAM,yBAAyB,+BAA+B,aAAa;CAC3E,MAAM,yBAAyB,mCAAmC,aAAa;CAE/E,MAAM,kBACJ,SAAS,cAAc,SACnB,2BAA2B,uBAAuB,SAAS,UAAU,CAAC,KACtE;CAEN,MAAM,sBAAsB,4BAC1B,SAAS,QACT,YACD;CAED,MAAM,kBAAkB,QAAQ,mBAAmB,gBAAgB,WAAW;AAE9E,QAAO;;;EAGP,YAAY,KAAK,KAAK,CAAC;;EAEvB,kBAAkB,KAAK,KAAK,CAAC;;;;;;;;EAQ7B,YAAY;;2BAEa,WAAW;+BACP,eAAe;EAC5C,kBAAkB;EAClB,uBAAuB;iCACQ,oBAAoB;yBAC5B,aAAa;;;EAGpC,YAAY;EACZ,WAAW;;qBAEQ,eAAe,SAAS,OAAO,CAAC;2BAC1B,eAAe,SAAS,aAAa,CAAC;oBAC7C,UAAU;2BACH,eAAe,SAAS,aAAa,CAAC;6BACpC,eAAe,SAAS,eAAe,CAAC,GAAG,gBAAgB;mBACrE,eAAe,SAAS,KAAK,CAAC;IAC7C,eAAe,0BAA0B,uBAAuB,KAAK,GAAG;;;;EAI1E,gBAAgB;;;;;;ACzGlB,MAAM,iBAAiB;AAEvB,eAAsB,KACpB,UACA,OACA,cACqB;CACrB,MAAM,EAAE,kBAAkB,sBAAsB,8BAA8B;CAE9E,MAAM,EAAE,gBAAgB,SAAS;CACjC,MAAM,gBAAgB,SAAS,WAAW;CAC1C,MAAM,EAAE,gBAAgB;CAExB,MAAM,gBAAgB,6BAA6B,UAAU,EAC3D,eAAe,gBAChB,CAAC;CACF,MAAM,qBAAqB,KAAK,UAC9B;EACE,GAAG;EACH,YAAY;GACV,SAAS;GACT,SAAS;GACT,YAAY;GACb;EACF,EACD,MACA,EACD;CAED,MAAM,kBAAkB,4BAA4B,EAAE,2BAA2B,GAAG;CAEpF,MAAM,qBAAqB;EACzB;EACA,GAAG,UAAU,iBAAiB,cAAc;EAC5C;EACD;AAiBD,QAAO;EACL,cAAc;EACd,aATkB,MAAM,OATH,oBACrB,UACA,cACA,oBAAoB,EAAE,EACtB,wBAAwB,EAAE,EAC1B,oBACA,iBACA,MAAM,YACP,EACgD;GAC/C,QAAQ;GACR,aAAa;GACb,MAAM;GACN,YAAY;GACb,CAAC;EAKA;EACA,GAAG,UAAU,iBAAiB,cAAc;EAC5C;EACD"}