@prisma-next/contract 0.3.0-dev.14 → 0.3.0-dev.141
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 +43 -254
- package/dist/contract-types-x89nqTli.d.mts +49 -0
- package/dist/contract-types-x89nqTli.d.mts.map +1 -0
- package/dist/hashing-D1EPxYRl.mjs +215 -0
- package/dist/hashing-D1EPxYRl.mjs.map +1 -0
- package/dist/hashing.d.mts +38 -0
- package/dist/hashing.d.mts.map +1 -0
- package/dist/hashing.mjs +3 -0
- package/dist/testing.d.mts +29 -0
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +56 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-DYikGC04.mjs +33 -0
- package/dist/types-DYikGC04.mjs.map +1 -0
- package/dist/types-DmKtoEd-.d.mts +303 -0
- package/dist/types-DmKtoEd-.d.mts.map +1 -0
- package/dist/types.d.mts +3 -0
- package/dist/types.mjs +3 -0
- package/dist/validate-contract.d.mts +35 -0
- package/dist/validate-contract.d.mts.map +1 -0
- package/dist/validate-contract.mjs +61 -0
- package/dist/validate-contract.mjs.map +1 -0
- package/dist/validate-domain-CTQiBiei.mjs +84 -0
- package/dist/validate-domain-CTQiBiei.mjs.map +1 -0
- package/dist/validate-domain.d.mts +24 -0
- package/dist/validate-domain.d.mts.map +1 -0
- package/dist/validate-domain.mjs +3 -0
- package/package.json +24 -25
- package/schemas/data-contract-document-v1.json +5 -5
- package/src/canonicalization.ts +286 -0
- package/src/contract-types.ts +54 -0
- package/src/domain-types.ts +85 -0
- package/src/exports/hashing.ts +6 -0
- package/src/exports/testing.ts +1 -0
- package/src/exports/types.ts +49 -10
- package/src/exports/validate-contract.ts +5 -0
- package/src/exports/validate-domain.ts +6 -0
- package/src/hashing.ts +69 -0
- package/src/testing-factories.ts +93 -0
- package/src/types.ts +153 -91
- package/src/validate-contract.ts +93 -0
- package/src/validate-domain.ts +205 -0
- package/dist/exports/framework-components.d.ts +0 -3
- package/dist/exports/framework-components.d.ts.map +0 -1
- package/dist/exports/framework-components.js +0 -24
- package/dist/exports/framework-components.js.map +0 -1
- package/dist/exports/ir.d.ts +0 -2
- package/dist/exports/ir.d.ts.map +0 -1
- package/dist/exports/ir.js +0 -35
- package/dist/exports/ir.js.map +0 -1
- package/dist/exports/pack-manifest-types.d.ts +0 -2
- package/dist/exports/pack-manifest-types.d.ts.map +0 -1
- package/dist/exports/pack-manifest-types.js +0 -1
- package/dist/exports/pack-manifest-types.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 -8
- package/dist/exports/types.js.map +0 -1
- package/dist/framework-components.d.ts +0 -408
- package/dist/framework-components.d.ts.map +0 -1
- package/dist/ir.d.ts +0 -76
- package/dist/ir.d.ts.map +0 -1
- package/dist/types.d.ts +0 -222
- package/dist/types.d.ts.map +0 -1
- package/src/exports/framework-components.ts +0 -26
- package/src/exports/ir.ts +0 -1
- package/src/exports/pack-manifest-types.ts +0 -6
- package/src/framework-components.ts +0 -525
- package/src/ir.ts +0 -113
package/package.json
CHANGED
|
@@ -1,51 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/contract",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.141",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "Data contract type definitions and JSON schema for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"
|
|
8
|
+
"arktype": "^2.1.29",
|
|
9
|
+
"@prisma-next/utils": "0.3.0-dev.141"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
11
|
-
"
|
|
12
|
-
"tsup": "8.5.1",
|
|
12
|
+
"tsdown": "0.18.4",
|
|
13
13
|
"typescript": "5.9.3",
|
|
14
|
-
"vitest": "4.0.
|
|
15
|
-
"@prisma-next/test-utils": "0.0.1"
|
|
14
|
+
"vitest": "4.0.17",
|
|
15
|
+
"@prisma-next/test-utils": "0.0.1",
|
|
16
|
+
"@prisma-next/tsconfig": "0.0.0",
|
|
17
|
+
"@prisma-next/tsdown": "0.0.0"
|
|
16
18
|
},
|
|
17
19
|
"files": [
|
|
18
20
|
"dist",
|
|
19
21
|
"src",
|
|
20
22
|
"schemas"
|
|
21
23
|
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20"
|
|
26
|
+
},
|
|
22
27
|
"exports": {
|
|
23
|
-
"./
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"./
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
"./framework-components": {
|
|
36
|
-
"types": "./dist/exports/framework-components.d.ts",
|
|
37
|
-
"import": "./dist/exports/framework-components.js"
|
|
38
|
-
},
|
|
39
|
-
"./schema-document": "./schemas/data-contract-document-v1.json"
|
|
28
|
+
"./hashing": "./dist/hashing.mjs",
|
|
29
|
+
"./testing": "./dist/testing.mjs",
|
|
30
|
+
"./types": "./dist/types.mjs",
|
|
31
|
+
"./validate-contract": "./dist/validate-contract.mjs",
|
|
32
|
+
"./validate-domain": "./dist/validate-domain.mjs",
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/prisma/prisma-next.git",
|
|
38
|
+
"directory": "packages/1-framework/0-foundation/shared/contract"
|
|
40
39
|
},
|
|
41
40
|
"scripts": {
|
|
42
|
-
"build": "
|
|
41
|
+
"build": "tsdown",
|
|
43
42
|
"test": "vitest run --passWithNoTests",
|
|
44
43
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
45
44
|
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
46
45
|
"lint": "biome check . --error-on-warnings",
|
|
47
46
|
"lint:fix": "biome check --write .",
|
|
48
47
|
"lint:fix:unsafe": "biome check --write --unsafe .",
|
|
49
|
-
"clean": "
|
|
48
|
+
"clean": "rm -rf dist dist-tsc dist-tsc-prod coverage .tmp-output"
|
|
50
49
|
}
|
|
51
50
|
}
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"enum": ["document"],
|
|
25
25
|
"description": "Target family classification"
|
|
26
26
|
},
|
|
27
|
-
"
|
|
27
|
+
"storageHash": {
|
|
28
28
|
"type": "string",
|
|
29
29
|
"pattern": "^sha256:[a-f0-9]{64}$",
|
|
30
|
-
"description": "SHA-256 hash of the
|
|
30
|
+
"description": "SHA-256 hash of the storage section (DB-satisfied expectations)"
|
|
31
31
|
},
|
|
32
32
|
"profileHash": {
|
|
33
33
|
"type": "string",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
|
-
"
|
|
47
|
+
"extensionPacks": {
|
|
48
48
|
"type": "object",
|
|
49
49
|
"description": "Extension packs and their configuration",
|
|
50
50
|
"additionalProperties": true
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"required": ["document"]
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
|
-
"required": ["schemaVersion", "target", "targetFamily", "
|
|
88
|
+
"required": ["schemaVersion", "target", "targetFamily", "storageHash", "storage"],
|
|
89
89
|
"$defs": {
|
|
90
90
|
"DocCollection": {
|
|
91
91
|
"type": "object",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"properties": {
|
|
104
104
|
"strategy": {
|
|
105
105
|
"type": "string",
|
|
106
|
-
"enum": ["auto", "client", "uuid", "
|
|
106
|
+
"enum": ["auto", "client", "uuid", "objectId"],
|
|
107
107
|
"description": "ID generation strategy"
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { isArrayEqual } from '@prisma-next/utils/array-equal';
|
|
2
|
+
|
|
3
|
+
import type { StorageBase } from './types';
|
|
4
|
+
import { bigintJsonReplacer } from './types';
|
|
5
|
+
|
|
6
|
+
const TOP_LEVEL_ORDER = [
|
|
7
|
+
'schemaVersion',
|
|
8
|
+
'canonicalVersion',
|
|
9
|
+
'targetFamily',
|
|
10
|
+
'target',
|
|
11
|
+
'profileHash',
|
|
12
|
+
'roots',
|
|
13
|
+
'models',
|
|
14
|
+
'storage',
|
|
15
|
+
'execution',
|
|
16
|
+
'capabilities',
|
|
17
|
+
'extensionPacks',
|
|
18
|
+
'meta',
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
function isDefaultValue(value: unknown): boolean {
|
|
22
|
+
if (value === false) return true;
|
|
23
|
+
if (value === null) return false;
|
|
24
|
+
if (value instanceof Date) return false;
|
|
25
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
26
|
+
if (typeof value === 'object' && value !== null) {
|
|
27
|
+
const keys = Object.keys(value);
|
|
28
|
+
return keys.length === 0;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function omitDefaults(obj: unknown, path: readonly string[]): unknown {
|
|
34
|
+
if (obj === null || typeof obj !== 'object') {
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (obj instanceof Date) {
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (Array.isArray(obj)) {
|
|
43
|
+
return obj.map((item) => omitDefaults(item, path));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const result: Record<string, unknown> = {};
|
|
47
|
+
|
|
48
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
49
|
+
const currentPath = [...path, key];
|
|
50
|
+
|
|
51
|
+
if (key === '_generated') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (key === 'nullable' && value === false) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (key === 'generated' && value === false) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if ((key === 'onDelete' || key === 'onUpdate') && value === 'noAction') {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (isDefaultValue(value)) {
|
|
68
|
+
const isRequiredModels = isArrayEqual(currentPath, ['models']);
|
|
69
|
+
const isRequiredTables = isArrayEqual(currentPath, ['storage', 'tables']);
|
|
70
|
+
const isRequiredCollections = isArrayEqual(currentPath, ['storage', 'collections']);
|
|
71
|
+
const isCollectionEntry =
|
|
72
|
+
currentPath.length === 3 &&
|
|
73
|
+
isArrayEqual([currentPath[0], currentPath[1]], ['storage', 'collections']);
|
|
74
|
+
const isRequiredRoots = isArrayEqual(currentPath, ['roots']);
|
|
75
|
+
const isRequiredExtensionPacks = isArrayEqual(currentPath, ['extensionPacks']);
|
|
76
|
+
const isRequiredCapabilities = isArrayEqual(currentPath, ['capabilities']);
|
|
77
|
+
const isRequiredMeta = isArrayEqual(currentPath, ['meta']);
|
|
78
|
+
const isRequiredExecutionDefaults = isArrayEqual(currentPath, [
|
|
79
|
+
'execution',
|
|
80
|
+
'mutations',
|
|
81
|
+
'defaults',
|
|
82
|
+
]);
|
|
83
|
+
const isExtensionNamespace = currentPath.length === 2 && currentPath[0] === 'extensionPacks';
|
|
84
|
+
const isModelRelations =
|
|
85
|
+
currentPath.length === 3 &&
|
|
86
|
+
isArrayEqual([currentPath[0], currentPath[2]], ['models', 'relations']);
|
|
87
|
+
const isModelStorage =
|
|
88
|
+
currentPath.length === 3 &&
|
|
89
|
+
isArrayEqual([currentPath[0], currentPath[2]], ['models', 'storage']);
|
|
90
|
+
const isTableUniques =
|
|
91
|
+
currentPath.length === 4 &&
|
|
92
|
+
isArrayEqual(
|
|
93
|
+
[currentPath[0], currentPath[1], currentPath[3]],
|
|
94
|
+
['storage', 'tables', 'uniques'],
|
|
95
|
+
);
|
|
96
|
+
const isTableIndexes =
|
|
97
|
+
currentPath.length === 4 &&
|
|
98
|
+
isArrayEqual(
|
|
99
|
+
[currentPath[0], currentPath[1], currentPath[3]],
|
|
100
|
+
['storage', 'tables', 'indexes'],
|
|
101
|
+
);
|
|
102
|
+
const isTableForeignKeys =
|
|
103
|
+
currentPath.length === 4 &&
|
|
104
|
+
isArrayEqual(
|
|
105
|
+
[currentPath[0], currentPath[1], currentPath[3]],
|
|
106
|
+
['storage', 'tables', 'foreignKeys'],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const isFkBooleanField =
|
|
110
|
+
currentPath.length === 5 &&
|
|
111
|
+
currentPath[0] === 'storage' &&
|
|
112
|
+
currentPath[1] === 'tables' &&
|
|
113
|
+
currentPath[3] === 'foreignKeys' &&
|
|
114
|
+
(key === 'constraint' || key === 'index');
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
!isRequiredModels &&
|
|
118
|
+
!isRequiredTables &&
|
|
119
|
+
!isRequiredCollections &&
|
|
120
|
+
!isCollectionEntry &&
|
|
121
|
+
!isRequiredRoots &&
|
|
122
|
+
!isRequiredExtensionPacks &&
|
|
123
|
+
!isRequiredCapabilities &&
|
|
124
|
+
!isRequiredMeta &&
|
|
125
|
+
!isRequiredExecutionDefaults &&
|
|
126
|
+
!isExtensionNamespace &&
|
|
127
|
+
!isModelRelations &&
|
|
128
|
+
!isModelStorage &&
|
|
129
|
+
!isTableUniques &&
|
|
130
|
+
!isTableIndexes &&
|
|
131
|
+
!isTableForeignKeys &&
|
|
132
|
+
!isFkBooleanField
|
|
133
|
+
) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
result[key] = omitDefaults(value, currentPath);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function sortObjectKeys(obj: unknown): unknown {
|
|
145
|
+
if (obj === null || typeof obj !== 'object') {
|
|
146
|
+
return obj;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (obj instanceof Date) {
|
|
150
|
+
return obj;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (Array.isArray(obj)) {
|
|
154
|
+
return obj.map((item) => sortObjectKeys(item));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sorted: Record<string, unknown> = {};
|
|
158
|
+
const keys = Object.keys(obj).sort();
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
sorted[key] = sortObjectKeys((obj as Record<string, unknown>)[key]);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return sorted;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type StorageObject = {
|
|
167
|
+
tables?: Record<string, unknown>;
|
|
168
|
+
[key: string]: unknown;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
type TableObject = {
|
|
172
|
+
indexes?: unknown[];
|
|
173
|
+
uniques?: unknown[];
|
|
174
|
+
[key: string]: unknown;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
function sortIndexesAndUniques(storage: unknown): unknown {
|
|
178
|
+
if (!storage || typeof storage !== 'object') {
|
|
179
|
+
return storage;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const storageObj = storage as StorageObject;
|
|
183
|
+
if (!storageObj.tables || typeof storageObj.tables !== 'object') {
|
|
184
|
+
return storage;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const tables = storageObj.tables;
|
|
188
|
+
const result: StorageObject = { ...storageObj };
|
|
189
|
+
|
|
190
|
+
result.tables = {};
|
|
191
|
+
const sortedTableNames = Object.keys(tables).sort();
|
|
192
|
+
for (const tableName of sortedTableNames) {
|
|
193
|
+
const table = tables[tableName];
|
|
194
|
+
if (!table || typeof table !== 'object') {
|
|
195
|
+
result.tables[tableName] = table;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const tableObj = table as TableObject;
|
|
200
|
+
const sortedTable: TableObject = { ...tableObj };
|
|
201
|
+
|
|
202
|
+
if (Array.isArray(tableObj.indexes)) {
|
|
203
|
+
sortedTable.indexes = [...tableObj.indexes].sort((a, b) => {
|
|
204
|
+
const nameA = (a as { name?: string })?.name || '';
|
|
205
|
+
const nameB = (b as { name?: string })?.name || '';
|
|
206
|
+
return nameA.localeCompare(nameB);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (Array.isArray(tableObj.uniques)) {
|
|
211
|
+
sortedTable.uniques = [...tableObj.uniques].sort((a, b) => {
|
|
212
|
+
const nameA = (a as { name?: string })?.name || '';
|
|
213
|
+
const nameB = (b as { name?: string })?.name || '';
|
|
214
|
+
return nameA.localeCompare(nameB);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
result.tables[tableName] = sortedTable;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function orderTopLevel(obj: Record<string, unknown>): Record<string, unknown> {
|
|
225
|
+
const ordered: Record<string, unknown> = {};
|
|
226
|
+
const remaining = new Set(Object.keys(obj));
|
|
227
|
+
|
|
228
|
+
for (const key of TOP_LEVEL_ORDER) {
|
|
229
|
+
if (remaining.has(key)) {
|
|
230
|
+
ordered[key] = obj[key];
|
|
231
|
+
remaining.delete(key);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const key of Array.from(remaining).sort()) {
|
|
236
|
+
ordered[key] = obj[key];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return ordered;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export type CanonicalContractInput = {
|
|
243
|
+
readonly schemaVersion?: string | undefined;
|
|
244
|
+
readonly targetFamily: string;
|
|
245
|
+
readonly target: string;
|
|
246
|
+
readonly profileHash?: string | undefined;
|
|
247
|
+
readonly roots: Record<string, string>;
|
|
248
|
+
readonly models: Record<string, unknown>;
|
|
249
|
+
// StorageBase is an interface without an index signature, so it is not
|
|
250
|
+
// assignable to Record<string, unknown>. The union allows callers to pass
|
|
251
|
+
// either the typed StorageBase or a plain record.
|
|
252
|
+
readonly storage: StorageBase | Record<string, unknown>;
|
|
253
|
+
readonly execution?: Record<string, unknown> | undefined;
|
|
254
|
+
readonly extensionPacks: Record<string, unknown>;
|
|
255
|
+
readonly capabilities: Record<string, Record<string, boolean>>;
|
|
256
|
+
readonly meta: Record<string, unknown>;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export function canonicalizeContractToObject(
|
|
260
|
+
input: CanonicalContractInput,
|
|
261
|
+
): Record<string, unknown> {
|
|
262
|
+
const i = input as Record<string, unknown>;
|
|
263
|
+
const normalized: Record<string, unknown> = {
|
|
264
|
+
...(i['schemaVersion'] !== undefined ? { schemaVersion: i['schemaVersion'] } : {}),
|
|
265
|
+
targetFamily: i['targetFamily'],
|
|
266
|
+
target: i['target'],
|
|
267
|
+
...(i['profileHash'] !== undefined ? { profileHash: i['profileHash'] } : {}),
|
|
268
|
+
roots: i['roots'],
|
|
269
|
+
models: i['models'],
|
|
270
|
+
storage: i['storage'],
|
|
271
|
+
...(i['execution'] !== undefined ? { execution: i['execution'] } : {}),
|
|
272
|
+
extensionPacks: i['extensionPacks'],
|
|
273
|
+
capabilities: i['capabilities'],
|
|
274
|
+
meta: i['meta'],
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const withDefaultsOmitted = omitDefaults(normalized, []) as Record<string, unknown>;
|
|
278
|
+
const withSortedIndexes = sortIndexesAndUniques(withDefaultsOmitted['storage']);
|
|
279
|
+
const withSortedStorage = { ...withDefaultsOmitted, storage: withSortedIndexes };
|
|
280
|
+
const withSortedKeys = sortObjectKeys(withSortedStorage) as Record<string, unknown>;
|
|
281
|
+
return orderTopLevel(withSortedKeys);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function canonicalizeContract(input: CanonicalContractInput): string {
|
|
285
|
+
return JSON.stringify(canonicalizeContractToObject(input), bigintJsonReplacer, 2);
|
|
286
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ContractModel } from './domain-types';
|
|
2
|
+
import type {
|
|
3
|
+
ExecutionHashBase,
|
|
4
|
+
ExecutionMutationDefault,
|
|
5
|
+
ProfileHashBase,
|
|
6
|
+
StorageBase,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Execution section for the unified contract (ADR 182).
|
|
11
|
+
*
|
|
12
|
+
* Unlike the legacy {@link import('./types').ExecutionSection}, this type
|
|
13
|
+
* requires `executionHash` — when an execution section is present, its
|
|
14
|
+
* hash must be too (consistent with `StorageBase.storageHash`).
|
|
15
|
+
*
|
|
16
|
+
* @template THash Literal hash string type for type-safe hash tracking.
|
|
17
|
+
*/
|
|
18
|
+
export type ContractExecutionSection<THash extends string = string> = {
|
|
19
|
+
readonly executionHash: ExecutionHashBase<THash>;
|
|
20
|
+
readonly mutations: {
|
|
21
|
+
readonly defaults: ReadonlyArray<ExecutionMutationDefault>;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Unified contract representation (ADR 182).
|
|
27
|
+
*
|
|
28
|
+
* A `Contract` is the canonical in-memory representation of a data contract.
|
|
29
|
+
* It is model-first (domain models carry their own storage bridge) and
|
|
30
|
+
* family-parameterized (SQL, Mongo, etc. specialize via `TStorage` and model
|
|
31
|
+
* storage generics on `ContractModel`).
|
|
32
|
+
*
|
|
33
|
+
* JSON persistence fields (`schemaVersion`, `sources`) are not represented
|
|
34
|
+
* here — they are handled at the serialization boundary.
|
|
35
|
+
*
|
|
36
|
+
* @template TStorage Family-specific storage block (extends {@link StorageBase}).
|
|
37
|
+
* @template TModels Record of model name → {@link ContractModel} with
|
|
38
|
+
* family-specific model storage.
|
|
39
|
+
*/
|
|
40
|
+
export interface Contract<
|
|
41
|
+
TStorage extends StorageBase = StorageBase,
|
|
42
|
+
TModels extends Record<string, ContractModel> = Record<string, ContractModel>,
|
|
43
|
+
> {
|
|
44
|
+
readonly target: string;
|
|
45
|
+
readonly targetFamily: string;
|
|
46
|
+
readonly roots: Record<string, string>;
|
|
47
|
+
readonly models: TModels;
|
|
48
|
+
readonly storage: TStorage;
|
|
49
|
+
readonly capabilities: Record<string, Record<string, boolean>>;
|
|
50
|
+
readonly extensionPacks: Record<string, unknown>;
|
|
51
|
+
readonly execution?: ContractExecutionSection;
|
|
52
|
+
readonly profileHash: ProfileHashBase<string>;
|
|
53
|
+
readonly meta: Record<string, unknown>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type ContractField = {
|
|
2
|
+
readonly nullable: boolean;
|
|
3
|
+
readonly codecId: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type ContractRelationOn = {
|
|
7
|
+
readonly localFields: readonly string[];
|
|
8
|
+
readonly targetFields: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type ContractReferenceRelation = {
|
|
12
|
+
readonly to: string;
|
|
13
|
+
readonly cardinality: '1:1' | '1:N' | 'N:1';
|
|
14
|
+
readonly on: ContractRelationOn;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ContractEmbedRelation = {
|
|
18
|
+
readonly to: string;
|
|
19
|
+
readonly cardinality: '1:1' | '1:N';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ContractRelation = ContractReferenceRelation | ContractEmbedRelation;
|
|
23
|
+
|
|
24
|
+
export type ContractDiscriminator = {
|
|
25
|
+
readonly field: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ContractVariantEntry = {
|
|
29
|
+
readonly value: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type ModelStorageBase = Readonly<Record<string, unknown>>;
|
|
33
|
+
|
|
34
|
+
export interface ContractModel<TModelStorage extends ModelStorageBase = ModelStorageBase> {
|
|
35
|
+
readonly fields: Record<string, ContractField>;
|
|
36
|
+
readonly relations: Record<string, ContractRelation>;
|
|
37
|
+
readonly storage: TModelStorage;
|
|
38
|
+
readonly discriminator?: ContractDiscriminator;
|
|
39
|
+
readonly variants?: Record<string, ContractVariantEntry>;
|
|
40
|
+
readonly base?: string;
|
|
41
|
+
readonly owner?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Backward-compatible aliases ──────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/** @deprecated Use {@link ContractField} */
|
|
47
|
+
export type DomainField = ContractField;
|
|
48
|
+
/** @deprecated Use {@link ContractRelationOn} */
|
|
49
|
+
export type DomainRelationOn = ContractRelationOn;
|
|
50
|
+
/** @deprecated Use {@link ContractReferenceRelation} */
|
|
51
|
+
export type DomainReferenceRelation = ContractReferenceRelation;
|
|
52
|
+
/** @deprecated Use {@link ContractEmbedRelation} */
|
|
53
|
+
export type DomainEmbedRelation = ContractEmbedRelation;
|
|
54
|
+
/** @deprecated Use {@link ContractRelation} */
|
|
55
|
+
export type DomainRelation = ContractRelation;
|
|
56
|
+
/** @deprecated Use {@link ContractDiscriminator} */
|
|
57
|
+
export type DomainDiscriminator = ContractDiscriminator;
|
|
58
|
+
/** @deprecated Use {@link ContractVariantEntry} */
|
|
59
|
+
export type DomainVariantEntry = ContractVariantEntry;
|
|
60
|
+
/** @deprecated Use {@link ContractModel} */
|
|
61
|
+
export type DomainModel = ContractModel;
|
|
62
|
+
|
|
63
|
+
// ── Relation key helpers ─────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
type HasModelsWithRelations = {
|
|
66
|
+
readonly models: Record<string, { readonly relations: Record<string, ContractRelation> }>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type ReferenceRelationKeys<
|
|
70
|
+
TContract extends HasModelsWithRelations,
|
|
71
|
+
ModelName extends string & keyof TContract['models'],
|
|
72
|
+
> = {
|
|
73
|
+
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends ContractReferenceRelation
|
|
74
|
+
? K
|
|
75
|
+
: never;
|
|
76
|
+
}[keyof TContract['models'][ModelName]['relations']];
|
|
77
|
+
|
|
78
|
+
export type EmbedRelationKeys<
|
|
79
|
+
TContract extends HasModelsWithRelations,
|
|
80
|
+
ModelName extends string & keyof TContract['models'],
|
|
81
|
+
> = {
|
|
82
|
+
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends ContractReferenceRelation
|
|
83
|
+
? never
|
|
84
|
+
: K;
|
|
85
|
+
}[keyof TContract['models'][ModelName]['relations']];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createContract, createSqlContract, DUMMY_HASH } from '../testing-factories';
|
package/src/exports/types.ts
CHANGED
|
@@ -1,26 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
// Document family types
|
|
3
|
-
// Plan types - target-family agnostic execution types
|
|
4
|
-
// Emitter types (moved from @prisma-next/emitter)
|
|
1
|
+
export type { Contract, ContractExecutionSection } from '../contract-types';
|
|
5
2
|
export type {
|
|
3
|
+
ContractDiscriminator,
|
|
4
|
+
ContractEmbedRelation,
|
|
5
|
+
ContractField,
|
|
6
|
+
ContractModel,
|
|
7
|
+
ContractReferenceRelation,
|
|
8
|
+
ContractRelation,
|
|
9
|
+
ContractRelationOn,
|
|
10
|
+
ContractVariantEntry,
|
|
11
|
+
DomainDiscriminator,
|
|
12
|
+
DomainEmbedRelation,
|
|
13
|
+
DomainField,
|
|
14
|
+
DomainModel,
|
|
15
|
+
DomainReferenceRelation,
|
|
16
|
+
DomainRelation,
|
|
17
|
+
DomainRelationOn,
|
|
18
|
+
DomainVariantEntry,
|
|
19
|
+
EmbedRelationKeys,
|
|
20
|
+
ModelStorageBase,
|
|
21
|
+
ReferenceRelationKeys,
|
|
22
|
+
} from '../domain-types';
|
|
23
|
+
export type {
|
|
24
|
+
$,
|
|
25
|
+
Brand,
|
|
26
|
+
ColumnDefault,
|
|
27
|
+
ColumnDefaultLiteralInputValue,
|
|
28
|
+
ColumnDefaultLiteralValue,
|
|
6
29
|
ContractBase,
|
|
7
30
|
ContractMarkerRecord,
|
|
8
31
|
DocCollection,
|
|
9
32
|
DocIndex,
|
|
10
33
|
DocumentContract,
|
|
11
34
|
DocumentStorage,
|
|
35
|
+
ExecutionHashBase,
|
|
36
|
+
ExecutionMutationDefault,
|
|
37
|
+
ExecutionMutationDefaultValue,
|
|
12
38
|
ExecutionPlan,
|
|
39
|
+
ExecutionSection,
|
|
13
40
|
Expr,
|
|
14
41
|
FieldType,
|
|
15
|
-
|
|
42
|
+
GeneratedValueSpec,
|
|
43
|
+
JsonPrimitive,
|
|
44
|
+
JsonValue,
|
|
16
45
|
ParamDescriptor,
|
|
17
46
|
PlanMeta,
|
|
18
47
|
PlanRefs,
|
|
48
|
+
ProfileHashBase,
|
|
19
49
|
ResultType,
|
|
20
50
|
Source,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
51
|
+
StorageBase,
|
|
52
|
+
StorageHashBase,
|
|
53
|
+
TaggedBigInt,
|
|
54
|
+
TaggedLiteralValue,
|
|
55
|
+
TaggedRaw,
|
|
56
|
+
} from '../types';
|
|
57
|
+
export {
|
|
58
|
+
bigintJsonReplacer,
|
|
59
|
+
coreHash,
|
|
60
|
+
executionHash,
|
|
61
|
+
isDocumentContract,
|
|
62
|
+
isTaggedBigInt,
|
|
63
|
+
isTaggedRaw,
|
|
64
|
+
profileHash,
|
|
24
65
|
} from '../types';
|
|
25
|
-
// Type guards
|
|
26
|
-
export { isDocumentContract } from '../types';
|