@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.
- package/README.md +33 -30
- package/dist/domain-type-generation.d.mts +14 -2
- package/dist/domain-type-generation.d.mts.map +1 -1
- package/dist/domain-type-generation.mjs +139 -1
- package/dist/domain-type-generation.mjs.map +1 -1
- package/dist/exports/index.d.mts +39 -4
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +104 -3
- package/dist/exports/index.mjs.map +1 -0
- package/dist/test/utils.d.mts +16 -14
- package/dist/test/utils.d.mts.map +1 -1
- package/dist/test/utils.mjs +12 -51
- package/dist/test/utils.mjs.map +1 -1
- package/dist/type-expression-safety-7_1tfJXA.mjs +8 -0
- package/dist/type-expression-safety-7_1tfJXA.mjs.map +1 -0
- package/dist/type-expression-safety.d.mts +5 -0
- package/dist/type-expression-safety.d.mts.map +1 -0
- package/dist/type-expression-safety.mjs +3 -0
- package/package.json +12 -6
- package/src/domain-type-generation.ts +227 -1
- package/src/emit-types.ts +23 -0
- package/src/emit.ts +68 -0
- package/src/exports/index.ts +4 -9
- package/src/generate-contract-dts.ts +116 -0
- package/src/type-expression-safety.ts +3 -0
- package/test/canonicalization.test.ts +25 -28
- package/test/domain-type-generation.test.ts +509 -2
- package/test/emitter.integration.test.ts +81 -139
- package/test/emitter.roundtrip.test.ts +114 -184
- package/test/emitter.test.ts +82 -467
- package/test/hashing.test.ts +8 -30
- package/test/mock-spi.ts +18 -0
- package/test/type-expression-safety.test.ts +34 -0
- package/test/utils.ts +30 -156
- package/src/target-family.ts +0 -7
- 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
|
|
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
|
|
21
|
-
- **Validate**: Core structure validation
|
|
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
|
|
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 (`
|
|
80
|
-
- Orchestrates
|
|
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:
|
|
84
|
-
|
|
85
|
-
###
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
- `
|
|
89
|
-
- `
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
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
|
|
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 {
|
|
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 {
|
|
143
|
-
// or: import {
|
|
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
|
|
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(
|
|
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
|
-
},
|
|
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 `
|
|
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 {
|
|
179
|
+
import { createTestContract } from './test/utils';
|
|
177
180
|
|
|
178
|
-
const
|
|
181
|
+
const contract = createTestContract({
|
|
179
182
|
storage: {
|
|
180
183
|
tables: {
|
|
181
184
|
user: {
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
import {
|
|
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":"
|
|
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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"}
|
package/dist/exports/index.d.mts
CHANGED
|
@@ -1,4 +1,39 @@
|
|
|
1
|
-
import { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
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"}
|
package/dist/exports/index.mjs
CHANGED
|
@@ -1,4 +1,105 @@
|
|
|
1
|
-
import { deduplicateImports, generateCodecTypeIntersection, generateHashTypeAliases, generateImportLines, generateModelRelationsType, generateRootsType, serializeObjectKey, serializeValue } from "../domain-type-generation.mjs";
|
|
2
|
-
import {
|
|
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
|
-
|
|
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"}
|