@prisma-next/core-control-plane 0.3.0-dev.9 → 0.3.0-dev.90
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/LICENSE +201 -0
- package/README.md +31 -99
- package/dist/constants.d.mts +9 -0
- package/dist/constants.d.mts.map +1 -0
- package/dist/constants.mjs +10 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/emission.d.mts +58 -0
- package/dist/emission.d.mts.map +1 -0
- package/dist/emission.mjs +315 -0
- package/dist/emission.mjs.map +1 -0
- package/dist/errors.d.mts +232 -0
- package/dist/errors.d.mts.map +1 -0
- package/dist/errors.mjs +330 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/{schema-view.d.ts → schema-view-DObwT8x9.d.mts} +17 -14
- package/dist/schema-view-DObwT8x9.d.mts.map +1 -0
- package/dist/schema-view.d.mts +2 -0
- package/dist/schema-view.mjs +1 -0
- package/dist/stack.d.mts +30 -0
- package/dist/stack.d.mts.map +1 -0
- package/dist/stack.mjs +30 -0
- package/dist/stack.mjs.map +1 -0
- package/dist/types-BArIWumw.d.mts +615 -0
- package/dist/types-BArIWumw.d.mts.map +1 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +1 -0
- package/package.json +31 -43
- package/src/constants.ts +5 -0
- package/src/emission/canonicalization.ts +68 -21
- package/src/emission/emit.ts +82 -21
- package/src/emission/hashing.ts +29 -27
- package/src/emission/types.ts +13 -2
- package/src/errors.ts +127 -20
- package/src/exports/constants.ts +1 -0
- package/src/exports/emission.ts +1 -1
- package/src/exports/errors.ts +6 -1
- package/src/exports/types.ts +0 -1
- package/src/migrations.ts +27 -1
- package/src/schema-view.ts +5 -5
- package/src/types.ts +24 -12
- package/dist/chunk-473ODD3P.js +0 -14
- package/dist/chunk-473ODD3P.js.map +0 -1
- package/dist/chunk-U5RYT6PT.js +0 -229
- package/dist/chunk-U5RYT6PT.js.map +0 -1
- package/dist/config-types.d.ts +0 -68
- package/dist/config-types.d.ts.map +0 -1
- package/dist/config-validation.d.ts +0 -10
- package/dist/config-validation.d.ts.map +0 -1
- package/dist/emission/canonicalization.d.ts +0 -6
- package/dist/emission/canonicalization.d.ts.map +0 -1
- package/dist/emission/emit.d.ts +0 -5
- package/dist/emission/emit.d.ts.map +0 -1
- package/dist/emission/hashing.d.ts +0 -17
- package/dist/emission/hashing.d.ts.map +0 -1
- package/dist/emission/types.d.ts +0 -16
- package/dist/emission/types.d.ts.map +0 -1
- package/dist/errors.d.ts +0 -183
- package/dist/errors.d.ts.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -53
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/config-validation.d.ts +0 -2
- package/dist/exports/config-validation.d.ts.map +0 -1
- package/dist/exports/config-validation.js +0 -252
- package/dist/exports/config-validation.js.map +0 -1
- package/dist/exports/emission.d.ts +0 -5
- package/dist/exports/emission.d.ts.map +0 -1
- package/dist/exports/emission.js +0 -310
- package/dist/exports/emission.js.map +0 -1
- package/dist/exports/errors.d.ts +0 -3
- package/dist/exports/errors.d.ts.map +0 -1
- package/dist/exports/errors.js +0 -43
- package/dist/exports/errors.js.map +0 -1
- package/dist/exports/schema-view.d.ts +0 -2
- package/dist/exports/schema-view.d.ts.map +0 -1
- package/dist/exports/schema-view.js +0 -1
- package/dist/exports/schema-view.js.map +0 -1
- package/dist/exports/stack.d.ts +0 -2
- package/dist/exports/stack.d.ts.map +0 -1
- package/dist/exports/stack.js +0 -7
- package/dist/exports/stack.js.map +0 -1
- package/dist/exports/types.d.ts +0 -3
- package/dist/exports/types.d.ts.map +0 -1
- package/dist/exports/types.js +0 -7
- package/dist/exports/types.js.map +0 -1
- package/dist/migrations.d.ts +0 -190
- package/dist/migrations.d.ts.map +0 -1
- package/dist/schema-view.d.ts.map +0 -1
- package/dist/stack.d.ts +0 -25
- package/dist/stack.d.ts.map +0 -1
- package/dist/types.d.ts +0 -411
- package/dist/types.d.ts.map +0 -1
- package/src/config-types.ts +0 -157
- package/src/config-validation.ts +0 -270
- package/src/exports/config-types.ts +0 -5
- package/src/exports/config-validation.ts +0 -1
package/package.json
CHANGED
|
@@ -1,65 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/core-control-plane",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.90",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
|
-
"description": "Control
|
|
6
|
+
"description": "Control-plane migration/emission primitives and structured error utilities",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"arktype": "^2.1.26",
|
|
9
9
|
"prettier": "^3.3.3",
|
|
10
|
-
"@prisma-next/contract": "0.3.0-dev.
|
|
11
|
-
"@prisma-next/
|
|
12
|
-
"@prisma-next/
|
|
10
|
+
"@prisma-next/contract": "0.3.0-dev.90",
|
|
11
|
+
"@prisma-next/utils": "0.3.0-dev.90",
|
|
12
|
+
"@prisma-next/operations": "0.3.0-dev.90"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"
|
|
16
|
-
"tsup": "8.5.1",
|
|
15
|
+
"tsdown": "0.18.4",
|
|
17
16
|
"typescript": "5.9.3",
|
|
18
|
-
"vitest": "4.0.
|
|
19
|
-
"@prisma-next/test-utils": "0.0.1"
|
|
17
|
+
"vitest": "4.0.17",
|
|
18
|
+
"@prisma-next/test-utils": "0.0.1",
|
|
19
|
+
"@prisma-next/tsdown": "0.0.0",
|
|
20
|
+
"@prisma-next/tsconfig": "0.0.0"
|
|
20
21
|
},
|
|
21
22
|
"files": [
|
|
22
23
|
"dist",
|
|
23
24
|
"src"
|
|
24
25
|
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
25
29
|
"exports": {
|
|
26
|
-
"./
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"./
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"./types": {
|
|
39
|
-
"types": "./dist/exports/types.d.ts",
|
|
40
|
-
"import": "./dist/exports/types.js"
|
|
41
|
-
},
|
|
42
|
-
"./stack": {
|
|
43
|
-
"types": "./dist/exports/stack.d.ts",
|
|
44
|
-
"import": "./dist/exports/stack.js"
|
|
45
|
-
},
|
|
46
|
-
"./emission": {
|
|
47
|
-
"types": "./dist/exports/emission.d.ts",
|
|
48
|
-
"import": "./dist/exports/emission.js"
|
|
49
|
-
},
|
|
50
|
-
"./schema-view": {
|
|
51
|
-
"types": "./dist/exports/schema-view.d.ts",
|
|
52
|
-
"import": "./dist/exports/schema-view.js"
|
|
53
|
-
}
|
|
30
|
+
"./constants": "./dist/constants.mjs",
|
|
31
|
+
"./emission": "./dist/emission.mjs",
|
|
32
|
+
"./errors": "./dist/errors.mjs",
|
|
33
|
+
"./schema-view": "./dist/schema-view.mjs",
|
|
34
|
+
"./stack": "./dist/stack.mjs",
|
|
35
|
+
"./types": "./dist/types.mjs",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/prisma/prisma-next.git",
|
|
41
|
+
"directory": "packages/1-framework/1-core/migration/control-plane"
|
|
54
42
|
},
|
|
55
43
|
"scripts": {
|
|
56
|
-
"build": "
|
|
44
|
+
"build": "tsdown",
|
|
57
45
|
"test": "vitest run --passWithNoTests",
|
|
58
46
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
59
|
-
"typecheck": "tsc --
|
|
60
|
-
"lint": "biome check . --
|
|
61
|
-
"lint:fix": "biome check --write .
|
|
62
|
-
"lint:fix:unsafe": "biome check --write --unsafe .
|
|
63
|
-
"clean": "
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"lint": "biome check . --error-on-warnings",
|
|
49
|
+
"lint:fix": "biome check --write .",
|
|
50
|
+
"lint:fix:unsafe": "biome check --write --unsafe .",
|
|
51
|
+
"clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
|
|
64
52
|
}
|
|
65
53
|
}
|
package/src/constants.ts
ADDED
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { bigintJsonReplacer } from '@prisma-next/contract/types';
|
|
2
2
|
import { isArrayEqual } from '@prisma-next/utils/array-equal';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
4
|
|
|
4
5
|
type NormalizedContract = {
|
|
5
6
|
schemaVersion: string;
|
|
6
7
|
targetFamily: string;
|
|
7
8
|
target: string;
|
|
8
|
-
|
|
9
|
+
storageHash?: string;
|
|
10
|
+
executionHash?: string;
|
|
9
11
|
profileHash?: string;
|
|
10
12
|
models: Record<string, unknown>;
|
|
11
13
|
relations: Record<string, unknown>;
|
|
12
14
|
storage: Record<string, unknown>;
|
|
15
|
+
execution?: Record<string, unknown>;
|
|
13
16
|
extensionPacks: Record<string, unknown>;
|
|
14
17
|
capabilities: Record<string, Record<string, boolean>>;
|
|
15
18
|
meta: Record<string, unknown>;
|
|
16
|
-
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type CanonicalContractInput = {
|
|
22
|
+
schemaVersion: string;
|
|
23
|
+
targetFamily: string;
|
|
24
|
+
target: string;
|
|
25
|
+
models: Record<string, unknown>;
|
|
26
|
+
relations: Record<string, unknown>;
|
|
27
|
+
storage: Record<string, unknown>;
|
|
28
|
+
execution?: Record<string, unknown>;
|
|
29
|
+
extensionPacks: Record<string, unknown>;
|
|
30
|
+
capabilities: Record<string, Record<string, boolean>>;
|
|
31
|
+
meta: Record<string, unknown>;
|
|
32
|
+
storageHash?: string;
|
|
33
|
+
executionHash?: string;
|
|
34
|
+
profileHash?: string;
|
|
17
35
|
};
|
|
18
36
|
|
|
19
37
|
const TOP_LEVEL_ORDER = [
|
|
@@ -21,19 +39,22 @@ const TOP_LEVEL_ORDER = [
|
|
|
21
39
|
'canonicalVersion',
|
|
22
40
|
'targetFamily',
|
|
23
41
|
'target',
|
|
24
|
-
'
|
|
42
|
+
'storageHash',
|
|
43
|
+
'executionHash',
|
|
25
44
|
'profileHash',
|
|
26
45
|
'models',
|
|
46
|
+
'relations',
|
|
27
47
|
'storage',
|
|
48
|
+
'execution',
|
|
28
49
|
'capabilities',
|
|
29
50
|
'extensionPacks',
|
|
30
51
|
'meta',
|
|
31
|
-
'sources',
|
|
32
52
|
] as const;
|
|
33
53
|
|
|
34
54
|
function isDefaultValue(value: unknown): boolean {
|
|
35
55
|
if (value === false) return true;
|
|
36
56
|
if (value === null) return false;
|
|
57
|
+
if (value instanceof Date) return false;
|
|
37
58
|
if (Array.isArray(value) && value.length === 0) return true;
|
|
38
59
|
if (typeof value === 'object' && value !== null) {
|
|
39
60
|
const keys = Object.keys(value);
|
|
@@ -47,6 +68,10 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
|
47
68
|
return obj;
|
|
48
69
|
}
|
|
49
70
|
|
|
71
|
+
if (obj instanceof Date) {
|
|
72
|
+
return obj;
|
|
73
|
+
}
|
|
74
|
+
|
|
50
75
|
if (Array.isArray(obj)) {
|
|
51
76
|
return obj.map((item) => omitDefaults(item, path));
|
|
52
77
|
}
|
|
@@ -69,6 +94,13 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
|
69
94
|
continue;
|
|
70
95
|
}
|
|
71
96
|
|
|
97
|
+
// Strip 'noAction' referential actions (the database default) for hash stability.
|
|
98
|
+
// A contract with explicit `onDelete: 'noAction'` is semantically identical to
|
|
99
|
+
// one that omits `onDelete` entirely, so they should produce the same hash.
|
|
100
|
+
if ((key === 'onDelete' || key === 'onUpdate') && value === 'noAction') {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
72
104
|
if (isDefaultValue(value)) {
|
|
73
105
|
const isRequiredModels = isArrayEqual(currentPath, ['models']);
|
|
74
106
|
const isRequiredTables = isArrayEqual(currentPath, ['storage', 'tables']);
|
|
@@ -76,7 +108,11 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
|
76
108
|
const isRequiredExtensionPacks = isArrayEqual(currentPath, ['extensionPacks']);
|
|
77
109
|
const isRequiredCapabilities = isArrayEqual(currentPath, ['capabilities']);
|
|
78
110
|
const isRequiredMeta = isArrayEqual(currentPath, ['meta']);
|
|
79
|
-
const
|
|
111
|
+
const isRequiredExecutionDefaults = isArrayEqual(currentPath, [
|
|
112
|
+
'execution',
|
|
113
|
+
'mutations',
|
|
114
|
+
'defaults',
|
|
115
|
+
]);
|
|
80
116
|
const isExtensionNamespace = currentPath.length === 2 && currentPath[0] === 'extensionPacks';
|
|
81
117
|
const isModelRelations =
|
|
82
118
|
currentPath.length === 3 &&
|
|
@@ -100,6 +136,16 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
|
100
136
|
['storage', 'tables', 'foreignKeys'],
|
|
101
137
|
);
|
|
102
138
|
|
|
139
|
+
// Preserve per-FK `constraint` and `index` booleans (even when `false`)
|
|
140
|
+
// so that hash distinguishes `false` from absent.
|
|
141
|
+
// Path: ['storage', 'tables', <tableName>, 'foreignKeys', 'constraint' | 'index']
|
|
142
|
+
const isFkBooleanField =
|
|
143
|
+
currentPath.length === 5 &&
|
|
144
|
+
currentPath[0] === 'storage' &&
|
|
145
|
+
currentPath[1] === 'tables' &&
|
|
146
|
+
currentPath[3] === 'foreignKeys' &&
|
|
147
|
+
(key === 'constraint' || key === 'index');
|
|
148
|
+
|
|
103
149
|
if (
|
|
104
150
|
!isRequiredModels &&
|
|
105
151
|
!isRequiredTables &&
|
|
@@ -107,12 +153,13 @@ function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
|
107
153
|
!isRequiredExtensionPacks &&
|
|
108
154
|
!isRequiredCapabilities &&
|
|
109
155
|
!isRequiredMeta &&
|
|
110
|
-
!
|
|
156
|
+
!isRequiredExecutionDefaults &&
|
|
111
157
|
!isExtensionNamespace &&
|
|
112
158
|
!isModelRelations &&
|
|
113
159
|
!isTableUniques &&
|
|
114
160
|
!isTableIndexes &&
|
|
115
|
-
!isTableForeignKeys
|
|
161
|
+
!isTableForeignKeys &&
|
|
162
|
+
!isFkBooleanField
|
|
116
163
|
) {
|
|
117
164
|
continue;
|
|
118
165
|
}
|
|
@@ -129,6 +176,10 @@ function sortObjectKeys(obj: unknown): unknown {
|
|
|
129
176
|
return obj;
|
|
130
177
|
}
|
|
131
178
|
|
|
179
|
+
if (obj instanceof Date) {
|
|
180
|
+
return obj;
|
|
181
|
+
}
|
|
182
|
+
|
|
132
183
|
if (Array.isArray(obj)) {
|
|
133
184
|
return obj.map((item) => sortObjectKeys(item));
|
|
134
185
|
}
|
|
@@ -219,9 +270,7 @@ function orderTopLevel(obj: Record<string, unknown>): Record<string, unknown> {
|
|
|
219
270
|
return ordered;
|
|
220
271
|
}
|
|
221
272
|
|
|
222
|
-
export function canonicalizeContract(
|
|
223
|
-
ir: ContractIR & { coreHash?: string; profileHash?: string },
|
|
224
|
-
): string {
|
|
273
|
+
export function canonicalizeContract(ir: CanonicalContractInput): string {
|
|
225
274
|
const normalized: NormalizedContract = {
|
|
226
275
|
schemaVersion: ir.schemaVersion,
|
|
227
276
|
targetFamily: ir.targetFamily,
|
|
@@ -229,19 +278,17 @@ export function canonicalizeContract(
|
|
|
229
278
|
models: ir.models,
|
|
230
279
|
relations: ir.relations,
|
|
231
280
|
storage: ir.storage,
|
|
281
|
+
...ifDefined('execution', ir.execution),
|
|
232
282
|
extensionPacks: ir.extensionPacks,
|
|
233
283
|
capabilities: ir.capabilities,
|
|
234
284
|
meta: ir.meta,
|
|
235
|
-
sources: ir.sources,
|
|
236
285
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
normalized.profileHash = ir.profileHash;
|
|
244
|
-
}
|
|
286
|
+
Object.assign(
|
|
287
|
+
normalized,
|
|
288
|
+
ifDefined('storageHash', ir.storageHash),
|
|
289
|
+
ifDefined('executionHash', ir.executionHash),
|
|
290
|
+
ifDefined('profileHash', ir.profileHash),
|
|
291
|
+
);
|
|
245
292
|
|
|
246
293
|
const withDefaultsOmitted = omitDefaults(normalized, []) as NormalizedContract;
|
|
247
294
|
const withSortedIndexes = sortIndexesAndUniques(withDefaultsOmitted.storage);
|
|
@@ -249,5 +296,5 @@ export function canonicalizeContract(
|
|
|
249
296
|
const withSortedKeys = sortObjectKeys(withSortedStorage) as Record<string, unknown>;
|
|
250
297
|
const withOrderedTopLevel = orderTopLevel(withSortedKeys);
|
|
251
298
|
|
|
252
|
-
return JSON.stringify(withOrderedTopLevel,
|
|
299
|
+
return JSON.stringify(withOrderedTopLevel, bigintJsonReplacer, 2);
|
|
253
300
|
}
|
package/src/emission/emit.ts
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import type { ContractIR } from '@prisma-next/contract/ir';
|
|
2
2
|
import type { TargetFamilyHook, ValidationContext } from '@prisma-next/contract/types';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
|
+
import { type } from 'arktype';
|
|
3
5
|
import { format } from 'prettier';
|
|
4
6
|
import { canonicalizeContract } from './canonicalization';
|
|
5
|
-
import {
|
|
7
|
+
import { computeExecutionHash, computeProfileHash, computeStorageHash } from './hashing';
|
|
6
8
|
import type { EmitOptions, EmitResult } from './types';
|
|
7
9
|
|
|
10
|
+
const CanonicalMetaSchema = type({
|
|
11
|
+
'[string]': 'unknown',
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const CanonicalContractSchema = type({
|
|
15
|
+
'+': 'reject',
|
|
16
|
+
schemaVersion: 'string',
|
|
17
|
+
targetFamily: 'string',
|
|
18
|
+
target: 'string',
|
|
19
|
+
models: type({ '[string]': 'unknown' }),
|
|
20
|
+
relations: type({ '[string]': 'unknown' }),
|
|
21
|
+
storage: type({ '[string]': 'unknown' }),
|
|
22
|
+
'execution?': type({ '[string]': 'unknown' }),
|
|
23
|
+
extensionPacks: type({ '[string]': 'unknown' }),
|
|
24
|
+
capabilities: type({
|
|
25
|
+
'[string]': type({
|
|
26
|
+
'[string]': 'boolean',
|
|
27
|
+
}),
|
|
28
|
+
}),
|
|
29
|
+
meta: CanonicalMetaSchema,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function assertCanonicalArtifactShape(value: unknown): void {
|
|
33
|
+
const result = CanonicalContractSchema(value);
|
|
34
|
+
if (result instanceof type.errors) {
|
|
35
|
+
const issues = result
|
|
36
|
+
.map((error) => {
|
|
37
|
+
const path = error.path?.toString() ?? '<root>';
|
|
38
|
+
return `${path}: ${error.message}`;
|
|
39
|
+
})
|
|
40
|
+
.join('; ');
|
|
41
|
+
throw new Error(`ContractIR canonical artifact validation failed: ${issues}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
8
45
|
function validateCoreStructure(ir: ContractIR): void {
|
|
9
46
|
if (!ir.targetFamily) {
|
|
10
47
|
throw new Error('ContractIR must have targetFamily');
|
|
@@ -33,9 +70,6 @@ function validateCoreStructure(ir: ContractIR): void {
|
|
|
33
70
|
if (!ir.meta || typeof ir.meta !== 'object') {
|
|
34
71
|
throw new Error('ContractIR must have meta');
|
|
35
72
|
}
|
|
36
|
-
if (!ir.sources || typeof ir.sources !== 'object') {
|
|
37
|
-
throw new Error('ContractIR must have sources');
|
|
38
|
-
}
|
|
39
73
|
}
|
|
40
74
|
|
|
41
75
|
export async function emit(
|
|
@@ -43,40 +77,51 @@ export async function emit(
|
|
|
43
77
|
options: EmitOptions,
|
|
44
78
|
targetFamily: TargetFamilyHook,
|
|
45
79
|
): Promise<EmitResult> {
|
|
46
|
-
const {
|
|
80
|
+
const {
|
|
81
|
+
operationRegistry,
|
|
82
|
+
codecTypeImports,
|
|
83
|
+
operationTypeImports,
|
|
84
|
+
extensionIds,
|
|
85
|
+
parameterizedRenderers,
|
|
86
|
+
parameterizedTypeImports,
|
|
87
|
+
} = options;
|
|
47
88
|
|
|
48
89
|
validateCoreStructure(ir);
|
|
49
90
|
|
|
50
91
|
const ctx: ValidationContext = {
|
|
51
|
-
...(operationRegistry
|
|
52
|
-
...(codecTypeImports
|
|
53
|
-
...(operationTypeImports
|
|
54
|
-
...(extensionIds
|
|
92
|
+
...ifDefined('operationRegistry', operationRegistry),
|
|
93
|
+
...ifDefined('codecTypeImports', codecTypeImports),
|
|
94
|
+
...ifDefined('operationTypeImports', operationTypeImports),
|
|
95
|
+
...ifDefined('extensionIds', extensionIds),
|
|
55
96
|
};
|
|
56
97
|
targetFamily.validateTypes(ir, ctx);
|
|
57
98
|
|
|
58
99
|
targetFamily.validateStructure(ir);
|
|
59
100
|
|
|
60
|
-
const
|
|
101
|
+
const canonicalContract = {
|
|
61
102
|
schemaVersion: ir.schemaVersion,
|
|
62
103
|
targetFamily: ir.targetFamily,
|
|
63
104
|
target: ir.target,
|
|
64
105
|
models: ir.models,
|
|
65
106
|
relations: ir.relations,
|
|
66
107
|
storage: ir.storage,
|
|
108
|
+
...ifDefined('execution', ir.execution),
|
|
67
109
|
extensionPacks: ir.extensionPacks,
|
|
68
110
|
capabilities: ir.capabilities,
|
|
69
111
|
meta: ir.meta,
|
|
70
|
-
|
|
71
|
-
|
|
112
|
+
};
|
|
113
|
+
assertCanonicalArtifactShape(canonicalContract);
|
|
72
114
|
|
|
73
|
-
const
|
|
74
|
-
const
|
|
115
|
+
const storageHash = computeStorageHash(canonicalContract);
|
|
116
|
+
const executionHash = canonicalContract.execution
|
|
117
|
+
? computeExecutionHash(canonicalContract)
|
|
118
|
+
: undefined;
|
|
119
|
+
const profileHash = computeProfileHash(canonicalContract);
|
|
75
120
|
|
|
76
|
-
const contractWithHashes
|
|
77
|
-
...
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
const contractWithHashes = {
|
|
122
|
+
...canonicalContract,
|
|
123
|
+
storageHash,
|
|
124
|
+
...ifDefined('executionHash', executionHash),
|
|
80
125
|
profileHash,
|
|
81
126
|
};
|
|
82
127
|
|
|
@@ -91,16 +136,31 @@ export async function emit(
|
|
|
91
136
|
...contractJsonObj,
|
|
92
137
|
_generated: {
|
|
93
138
|
warning: '⚠️ GENERATED FILE - DO NOT EDIT',
|
|
94
|
-
message: 'This file is automatically generated by "prisma-next emit".',
|
|
95
|
-
regenerate: 'To regenerate, run: prisma-next emit',
|
|
139
|
+
message: 'This file is automatically generated by "prisma-next contract emit".',
|
|
140
|
+
regenerate: 'To regenerate, run: prisma-next contract emit',
|
|
96
141
|
},
|
|
97
142
|
};
|
|
98
143
|
const contractJsonString = JSON.stringify(contractJsonWithMeta, null, 2);
|
|
99
144
|
|
|
145
|
+
const generateOptions =
|
|
146
|
+
parameterizedRenderers || parameterizedTypeImports
|
|
147
|
+
? {
|
|
148
|
+
...ifDefined('parameterizedRenderers', parameterizedRenderers),
|
|
149
|
+
...ifDefined('parameterizedTypeImports', parameterizedTypeImports),
|
|
150
|
+
}
|
|
151
|
+
: undefined;
|
|
152
|
+
|
|
153
|
+
const contractTypeHashes = {
|
|
154
|
+
storageHash,
|
|
155
|
+
...ifDefined('executionHash', executionHash),
|
|
156
|
+
profileHash,
|
|
157
|
+
};
|
|
100
158
|
const contractDtsRaw = targetFamily.generateContractTypes(
|
|
101
159
|
ir,
|
|
102
160
|
codecTypeImports ?? [],
|
|
103
161
|
operationTypeImports ?? [],
|
|
162
|
+
contractTypeHashes,
|
|
163
|
+
generateOptions,
|
|
104
164
|
);
|
|
105
165
|
const contractDts = await format(contractDtsRaw, {
|
|
106
166
|
parser: 'typescript',
|
|
@@ -112,7 +172,8 @@ export async function emit(
|
|
|
112
172
|
return {
|
|
113
173
|
contractJson: contractJsonString,
|
|
114
174
|
contractDts,
|
|
115
|
-
|
|
175
|
+
storageHash,
|
|
176
|
+
...ifDefined('executionHash', executionHash),
|
|
116
177
|
profileHash,
|
|
117
178
|
};
|
|
118
179
|
}
|
package/src/emission/hashing.ts
CHANGED
|
@@ -1,46 +1,32 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import
|
|
2
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
|
+
import type { CanonicalContractInput } from './canonicalization';
|
|
3
4
|
import { canonicalizeContract } from './canonicalization';
|
|
4
5
|
|
|
5
|
-
type ContractInput = {
|
|
6
|
-
schemaVersion: string;
|
|
7
|
-
targetFamily: string;
|
|
8
|
-
target: string;
|
|
9
|
-
models: Record<string, unknown>;
|
|
10
|
-
relations: Record<string, unknown>;
|
|
11
|
-
storage: Record<string, unknown>;
|
|
12
|
-
extensionPacks: Record<string, unknown>;
|
|
13
|
-
sources: Record<string, unknown>;
|
|
14
|
-
capabilities: Record<string, Record<string, boolean>>;
|
|
15
|
-
meta: Record<string, unknown>;
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
6
|
function computeHash(content: string): string {
|
|
20
7
|
const hash = createHash('sha256');
|
|
21
8
|
hash.update(content);
|
|
22
9
|
return `sha256:${hash.digest('hex')}`;
|
|
23
10
|
}
|
|
24
11
|
|
|
25
|
-
export function
|
|
26
|
-
const
|
|
12
|
+
export function computeStorageHash(contract: CanonicalContractInput): string {
|
|
13
|
+
const storageContract = {
|
|
27
14
|
schemaVersion: contract.schemaVersion,
|
|
28
15
|
targetFamily: contract.targetFamily,
|
|
29
16
|
target: contract.target,
|
|
30
|
-
models: contract.models,
|
|
31
|
-
relations: contract.relations,
|
|
32
17
|
storage: contract.storage,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
models: {},
|
|
19
|
+
relations: {},
|
|
20
|
+
extensionPacks: {},
|
|
21
|
+
capabilities: {},
|
|
22
|
+
meta: {},
|
|
37
23
|
};
|
|
38
|
-
const canonical = canonicalizeContract(
|
|
24
|
+
const canonical = canonicalizeContract(storageContract);
|
|
39
25
|
return computeHash(canonical);
|
|
40
26
|
}
|
|
41
27
|
|
|
42
|
-
export function computeProfileHash(contract:
|
|
43
|
-
const profileContract
|
|
28
|
+
export function computeProfileHash(contract: CanonicalContractInput): string {
|
|
29
|
+
const profileContract = {
|
|
44
30
|
schemaVersion: contract.schemaVersion,
|
|
45
31
|
targetFamily: contract.targetFamily,
|
|
46
32
|
target: contract.target,
|
|
@@ -50,8 +36,24 @@ export function computeProfileHash(contract: ContractInput): string {
|
|
|
50
36
|
extensionPacks: {},
|
|
51
37
|
capabilities: contract.capabilities,
|
|
52
38
|
meta: {},
|
|
53
|
-
sources: {},
|
|
54
39
|
};
|
|
55
40
|
const canonical = canonicalizeContract(profileContract);
|
|
56
41
|
return computeHash(canonical);
|
|
57
42
|
}
|
|
43
|
+
|
|
44
|
+
export function computeExecutionHash(contract: CanonicalContractInput): string {
|
|
45
|
+
const executionContract = {
|
|
46
|
+
schemaVersion: contract.schemaVersion,
|
|
47
|
+
targetFamily: contract.targetFamily,
|
|
48
|
+
target: contract.target,
|
|
49
|
+
models: {},
|
|
50
|
+
relations: {},
|
|
51
|
+
storage: {},
|
|
52
|
+
extensionPacks: {},
|
|
53
|
+
capabilities: {},
|
|
54
|
+
meta: {},
|
|
55
|
+
...ifDefined('execution', contract.execution),
|
|
56
|
+
};
|
|
57
|
+
const canonical = canonicalizeContract(executionContract);
|
|
58
|
+
return computeHash(canonical);
|
|
59
|
+
}
|
package/src/emission/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TypesImportSpec } from '@prisma-next/contract/types';
|
|
1
|
+
import type { TypeRenderEntry, TypesImportSpec } from '@prisma-next/contract/types';
|
|
2
2
|
import type { OperationRegistry } from '@prisma-next/operations';
|
|
3
3
|
|
|
4
4
|
export interface EmitOptions {
|
|
@@ -7,11 +7,22 @@ export interface EmitOptions {
|
|
|
7
7
|
readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
8
8
|
readonly operationTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
9
9
|
readonly extensionIds?: ReadonlyArray<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Normalized parameterized type renderers, keyed by codecId.
|
|
12
|
+
* These are extracted from descriptors and normalized during assembly.
|
|
13
|
+
*/
|
|
14
|
+
readonly parameterizedRenderers?: Map<string, TypeRenderEntry>;
|
|
15
|
+
/**
|
|
16
|
+
* Type imports for parameterized codecs.
|
|
17
|
+
* These are added to contract.d.ts alongside codec and operation type imports.
|
|
18
|
+
*/
|
|
19
|
+
readonly parameterizedTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
export interface EmitResult {
|
|
13
23
|
readonly contractJson: string;
|
|
14
24
|
readonly contractDts: string;
|
|
15
|
-
readonly
|
|
25
|
+
readonly storageHash: string;
|
|
26
|
+
readonly executionHash?: string;
|
|
16
27
|
readonly profileHash: string;
|
|
17
28
|
}
|