@prisma-next/contract 0.3.0-dev.137 → 0.3.0-dev.138
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/dist/contract-types-C0y-Bn8M.d.mts +512 -0
- package/dist/contract-types-C0y-Bn8M.d.mts.map +1 -0
- package/dist/{ir-DkfZ4S60.d.mts → ir-BPkihpFL.d.mts} +1 -1
- package/dist/{ir-DkfZ4S60.d.mts.map → ir-BPkihpFL.d.mts.map} +1 -1
- package/dist/ir.d.mts +1 -1
- package/dist/types.d.mts +2 -443
- package/dist/types.mjs.map +1 -1
- 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-WfuBWGsF.mjs +84 -0
- package/dist/validate-domain-WfuBWGsF.mjs.map +1 -0
- package/dist/validate-domain.mjs +2 -83
- package/package.json +5 -3
- package/src/contract-types.ts +54 -0
- package/src/domain-types.ts +41 -18
- package/src/exports/types.ts +11 -0
- package/src/exports/validate-contract.ts +5 -0
- package/src/types.ts +9 -0
- package/src/validate-contract.ts +93 -0
- package/dist/types.d.mts.map +0 -1
- package/dist/validate-domain.mjs.map +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/validate-domain.ts
|
|
2
|
+
function validateContractDomain(contract) {
|
|
3
|
+
const errors = [];
|
|
4
|
+
const warnings = [];
|
|
5
|
+
const modelNames = new Set(Object.keys(contract.models));
|
|
6
|
+
validateRoots(contract, modelNames, errors);
|
|
7
|
+
validateVariantsAndBases(contract, modelNames, errors);
|
|
8
|
+
validateRelationTargets(contract, modelNames, errors);
|
|
9
|
+
validateDiscriminators(contract, errors);
|
|
10
|
+
validateOwnership(contract, modelNames, errors);
|
|
11
|
+
detectOrphanedModels(contract, modelNames, warnings);
|
|
12
|
+
if (errors.length > 0) throw new Error(`Contract domain validation failed:\n- ${errors.join("\n- ")}`);
|
|
13
|
+
return { warnings };
|
|
14
|
+
}
|
|
15
|
+
function validateRoots(contract, modelNames, errors) {
|
|
16
|
+
const seenValues = /* @__PURE__ */ new Set();
|
|
17
|
+
for (const [rootKey, modelName] of Object.entries(contract.roots)) {
|
|
18
|
+
if (seenValues.has(modelName)) errors.push(`Duplicate root value: "${modelName}" is mapped by multiple root keys`);
|
|
19
|
+
seenValues.add(modelName);
|
|
20
|
+
if (!modelNames.has(modelName)) errors.push(`Root "${rootKey}" references model "${modelName}" which does not exist in models`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function validateVariantsAndBases(contract, modelNames, errors) {
|
|
24
|
+
const models = new Map(Object.entries(contract.models));
|
|
25
|
+
for (const [modelName, model] of models) {
|
|
26
|
+
if (model.variants) for (const variantName of Object.keys(model.variants)) {
|
|
27
|
+
if (!modelNames.has(variantName)) {
|
|
28
|
+
errors.push(`Model "${modelName}" lists variant "${variantName}" which does not exist in models`);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const variantModel = models.get(variantName);
|
|
32
|
+
if (!variantModel) continue;
|
|
33
|
+
if (variantModel.base !== modelName) errors.push(`Variant "${variantName}" has base "${variantModel.base ?? "(none)"}" but expected "${modelName}"`);
|
|
34
|
+
}
|
|
35
|
+
if (model.base) {
|
|
36
|
+
if (!modelNames.has(model.base)) {
|
|
37
|
+
errors.push(`Model "${modelName}" has base "${model.base}" which does not exist in models`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const baseModel = models.get(model.base);
|
|
41
|
+
if (!baseModel) continue;
|
|
42
|
+
if (!baseModel.variants || !Object.hasOwn(baseModel.variants, modelName)) errors.push(`Model "${modelName}" has base "${model.base}" which does not list it as a variant`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function validateRelationTargets(contract, modelNames, errors) {
|
|
47
|
+
for (const [modelName, model] of Object.entries(contract.models)) for (const [relName, relation] of Object.entries(model.relations ?? {})) if (!modelNames.has(relation.to)) errors.push(`Relation "${relName}" on model "${modelName}" targets "${relation.to}" which does not exist in models`);
|
|
48
|
+
}
|
|
49
|
+
function validateDiscriminators(contract, errors) {
|
|
50
|
+
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
51
|
+
if (model.discriminator) {
|
|
52
|
+
if (!model.variants || Object.keys(model.variants).length === 0) errors.push(`Model "${modelName}" has discriminator but no variants`);
|
|
53
|
+
if (!Object.hasOwn(model.fields, model.discriminator.field)) errors.push(`Discriminator field "${model.discriminator.field}" is not a field on model "${modelName}"`);
|
|
54
|
+
}
|
|
55
|
+
if (model.variants && Object.keys(model.variants).length > 0 && !model.discriminator) errors.push(`Model "${modelName}" has variants but no discriminator`);
|
|
56
|
+
if (model.base) {
|
|
57
|
+
if (model.discriminator) errors.push(`Model "${modelName}" has base and must not have discriminator`);
|
|
58
|
+
if (model.variants && Object.keys(model.variants).length > 0) errors.push(`Model "${modelName}" has base and must not have variants`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function validateOwnership(contract, modelNames, errors) {
|
|
63
|
+
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
64
|
+
if (!model.owner) continue;
|
|
65
|
+
if (model.owner === modelName) errors.push(`Model "${modelName}" cannot own itself`);
|
|
66
|
+
if (!modelNames.has(model.owner)) errors.push(`Model "${modelName}" has owner "${model.owner}" which does not exist in models`);
|
|
67
|
+
for (const [rootKey, rootModel] of Object.entries(contract.roots)) if (rootModel === modelName) errors.push(`Owned model "${modelName}" must not appear in roots (found as root "${rootKey}")`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function detectOrphanedModels(contract, modelNames, warnings) {
|
|
71
|
+
const referenced = /* @__PURE__ */ new Set();
|
|
72
|
+
for (const modelName of Object.values(contract.roots)) referenced.add(modelName);
|
|
73
|
+
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
74
|
+
for (const relation of Object.values(model.relations ?? {})) referenced.add(relation.to);
|
|
75
|
+
if (model.variants) for (const variantName of Object.keys(model.variants)) referenced.add(variantName);
|
|
76
|
+
if (model.base) referenced.add(model.base);
|
|
77
|
+
if (model.owner) referenced.add(modelName);
|
|
78
|
+
}
|
|
79
|
+
for (const modelName of modelNames) if (!referenced.has(modelName)) warnings.push(`Orphaned model: "${modelName}" is not referenced by any root, relation, or variant`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { validateContractDomain as t };
|
|
84
|
+
//# sourceMappingURL=validate-domain-WfuBWGsF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-domain-WfuBWGsF.mjs","names":["errors: string[]","warnings: string[]"],"sources":["../src/validate-domain.ts"],"sourcesContent":["export interface DomainModelShape {\n readonly fields: Record<string, unknown>;\n readonly relations?: Record<string, { readonly to: string }>;\n readonly discriminator?: { readonly field: string };\n readonly variants?: Record<string, unknown>;\n readonly base?: string;\n readonly owner?: string;\n}\n\nexport interface DomainContractShape {\n readonly roots: Record<string, string>;\n readonly models: Record<string, DomainModelShape>;\n}\n\nexport interface DomainValidationResult {\n readonly warnings: string[];\n}\n\nexport function validateContractDomain(contract: DomainContractShape): DomainValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const modelNames = new Set(Object.keys(contract.models));\n\n validateRoots(contract, modelNames, errors);\n validateVariantsAndBases(contract, modelNames, errors);\n validateRelationTargets(contract, modelNames, errors);\n validateDiscriminators(contract, errors);\n validateOwnership(contract, modelNames, errors);\n detectOrphanedModels(contract, modelNames, warnings);\n\n if (errors.length > 0) {\n throw new Error(`Contract domain validation failed:\\n- ${errors.join('\\n- ')}`);\n }\n\n return { warnings };\n}\n\nfunction validateRoots(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n const seenValues = new Set<string>();\n for (const [rootKey, modelName] of Object.entries(contract.roots)) {\n if (seenValues.has(modelName)) {\n errors.push(`Duplicate root value: \"${modelName}\" is mapped by multiple root keys`);\n }\n seenValues.add(modelName);\n\n if (!modelNames.has(modelName)) {\n errors.push(\n `Root \"${rootKey}\" references model \"${modelName}\" which does not exist in models`,\n );\n }\n }\n}\n\nfunction validateVariantsAndBases(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n const models = new Map(Object.entries(contract.models));\n\n for (const [modelName, model] of models) {\n if (model.variants) {\n for (const variantName of Object.keys(model.variants)) {\n if (!modelNames.has(variantName)) {\n errors.push(\n `Model \"${modelName}\" lists variant \"${variantName}\" which does not exist in models`,\n );\n continue;\n }\n const variantModel = models.get(variantName);\n if (!variantModel) continue;\n if (variantModel.base !== modelName) {\n errors.push(\n `Variant \"${variantName}\" has base \"${variantModel.base ?? '(none)'}\" but expected \"${modelName}\"`,\n );\n }\n }\n }\n\n if (model.base) {\n if (!modelNames.has(model.base)) {\n errors.push(`Model \"${modelName}\" has base \"${model.base}\" which does not exist in models`);\n continue;\n }\n const baseModel = models.get(model.base);\n if (!baseModel) continue;\n if (!baseModel.variants || !Object.hasOwn(baseModel.variants, modelName)) {\n errors.push(\n `Model \"${modelName}\" has base \"${model.base}\" which does not list it as a variant`,\n );\n }\n }\n }\n}\n\nfunction validateRelationTargets(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n for (const [relName, relation] of Object.entries(model.relations ?? {})) {\n if (!modelNames.has(relation.to)) {\n errors.push(\n `Relation \"${relName}\" on model \"${modelName}\" targets \"${relation.to}\" which does not exist in models`,\n );\n }\n }\n }\n}\n\nfunction validateDiscriminators(contract: DomainContractShape, errors: string[]): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n if (model.discriminator) {\n if (!model.variants || Object.keys(model.variants).length === 0) {\n errors.push(`Model \"${modelName}\" has discriminator but no variants`);\n }\n if (!Object.hasOwn(model.fields, model.discriminator.field)) {\n errors.push(\n `Discriminator field \"${model.discriminator.field}\" is not a field on model \"${modelName}\"`,\n );\n }\n }\n\n if (model.variants && Object.keys(model.variants).length > 0 && !model.discriminator) {\n errors.push(`Model \"${modelName}\" has variants but no discriminator`);\n }\n\n if (model.base) {\n if (model.discriminator) {\n errors.push(`Model \"${modelName}\" has base and must not have discriminator`);\n }\n if (model.variants && Object.keys(model.variants).length > 0) {\n errors.push(`Model \"${modelName}\" has base and must not have variants`);\n }\n }\n }\n}\n\nfunction validateOwnership(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n if (!model.owner) continue;\n\n if (model.owner === modelName) {\n errors.push(`Model \"${modelName}\" cannot own itself`);\n }\n\n if (!modelNames.has(model.owner)) {\n errors.push(`Model \"${modelName}\" has owner \"${model.owner}\" which does not exist in models`);\n }\n\n for (const [rootKey, rootModel] of Object.entries(contract.roots)) {\n if (rootModel === modelName) {\n errors.push(\n `Owned model \"${modelName}\" must not appear in roots (found as root \"${rootKey}\")`,\n );\n }\n }\n }\n}\n\nfunction detectOrphanedModels(\n contract: DomainContractShape,\n modelNames: Set<string>,\n warnings: string[],\n): void {\n const referenced = new Set<string>();\n\n for (const modelName of Object.values(contract.roots)) {\n referenced.add(modelName);\n }\n\n for (const [modelName, model] of Object.entries(contract.models)) {\n for (const relation of Object.values(model.relations ?? {})) {\n referenced.add(relation.to);\n }\n if (model.variants) {\n for (const variantName of Object.keys(model.variants)) {\n referenced.add(variantName);\n }\n }\n if (model.base) {\n referenced.add(model.base);\n }\n if (model.owner) {\n referenced.add(modelName);\n }\n }\n\n for (const modelName of modelNames) {\n if (!referenced.has(modelName)) {\n warnings.push(\n `Orphaned model: \"${modelName}\" is not referenced by any root, relation, or variant`,\n );\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/validate-domain.mjs
CHANGED
|
@@ -1,84 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
function validateContractDomain(contract) {
|
|
3
|
-
const errors = [];
|
|
4
|
-
const warnings = [];
|
|
5
|
-
const modelNames = new Set(Object.keys(contract.models));
|
|
6
|
-
validateRoots(contract, modelNames, errors);
|
|
7
|
-
validateVariantsAndBases(contract, modelNames, errors);
|
|
8
|
-
validateRelationTargets(contract, modelNames, errors);
|
|
9
|
-
validateDiscriminators(contract, errors);
|
|
10
|
-
validateOwnership(contract, modelNames, errors);
|
|
11
|
-
detectOrphanedModels(contract, modelNames, warnings);
|
|
12
|
-
if (errors.length > 0) throw new Error(`Contract domain validation failed:\n- ${errors.join("\n- ")}`);
|
|
13
|
-
return { warnings };
|
|
14
|
-
}
|
|
15
|
-
function validateRoots(contract, modelNames, errors) {
|
|
16
|
-
const seenValues = /* @__PURE__ */ new Set();
|
|
17
|
-
for (const [rootKey, modelName] of Object.entries(contract.roots)) {
|
|
18
|
-
if (seenValues.has(modelName)) errors.push(`Duplicate root value: "${modelName}" is mapped by multiple root keys`);
|
|
19
|
-
seenValues.add(modelName);
|
|
20
|
-
if (!modelNames.has(modelName)) errors.push(`Root "${rootKey}" references model "${modelName}" which does not exist in models`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function validateVariantsAndBases(contract, modelNames, errors) {
|
|
24
|
-
const models = new Map(Object.entries(contract.models));
|
|
25
|
-
for (const [modelName, model] of models) {
|
|
26
|
-
if (model.variants) for (const variantName of Object.keys(model.variants)) {
|
|
27
|
-
if (!modelNames.has(variantName)) {
|
|
28
|
-
errors.push(`Model "${modelName}" lists variant "${variantName}" which does not exist in models`);
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const variantModel = models.get(variantName);
|
|
32
|
-
if (!variantModel) continue;
|
|
33
|
-
if (variantModel.base !== modelName) errors.push(`Variant "${variantName}" has base "${variantModel.base ?? "(none)"}" but expected "${modelName}"`);
|
|
34
|
-
}
|
|
35
|
-
if (model.base) {
|
|
36
|
-
if (!modelNames.has(model.base)) {
|
|
37
|
-
errors.push(`Model "${modelName}" has base "${model.base}" which does not exist in models`);
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
const baseModel = models.get(model.base);
|
|
41
|
-
if (!baseModel) continue;
|
|
42
|
-
if (!baseModel.variants || !Object.hasOwn(baseModel.variants, modelName)) errors.push(`Model "${modelName}" has base "${model.base}" which does not list it as a variant`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function validateRelationTargets(contract, modelNames, errors) {
|
|
47
|
-
for (const [modelName, model] of Object.entries(contract.models)) for (const [relName, relation] of Object.entries(model.relations ?? {})) if (!modelNames.has(relation.to)) errors.push(`Relation "${relName}" on model "${modelName}" targets "${relation.to}" which does not exist in models`);
|
|
48
|
-
}
|
|
49
|
-
function validateDiscriminators(contract, errors) {
|
|
50
|
-
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
51
|
-
if (model.discriminator) {
|
|
52
|
-
if (!model.variants || Object.keys(model.variants).length === 0) errors.push(`Model "${modelName}" has discriminator but no variants`);
|
|
53
|
-
if (!Object.hasOwn(model.fields, model.discriminator.field)) errors.push(`Discriminator field "${model.discriminator.field}" is not a field on model "${modelName}"`);
|
|
54
|
-
}
|
|
55
|
-
if (model.variants && Object.keys(model.variants).length > 0 && !model.discriminator) errors.push(`Model "${modelName}" has variants but no discriminator`);
|
|
56
|
-
if (model.base) {
|
|
57
|
-
if (model.discriminator) errors.push(`Model "${modelName}" has base and must not have discriminator`);
|
|
58
|
-
if (model.variants && Object.keys(model.variants).length > 0) errors.push(`Model "${modelName}" has base and must not have variants`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function validateOwnership(contract, modelNames, errors) {
|
|
63
|
-
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
64
|
-
if (!model.owner) continue;
|
|
65
|
-
if (model.owner === modelName) errors.push(`Model "${modelName}" cannot own itself`);
|
|
66
|
-
if (!modelNames.has(model.owner)) errors.push(`Model "${modelName}" has owner "${model.owner}" which does not exist in models`);
|
|
67
|
-
for (const [rootKey, rootModel] of Object.entries(contract.roots)) if (rootModel === modelName) errors.push(`Owned model "${modelName}" must not appear in roots (found as root "${rootKey}")`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function detectOrphanedModels(contract, modelNames, warnings) {
|
|
71
|
-
const referenced = /* @__PURE__ */ new Set();
|
|
72
|
-
for (const modelName of Object.values(contract.roots)) referenced.add(modelName);
|
|
73
|
-
for (const [modelName, model] of Object.entries(contract.models)) {
|
|
74
|
-
for (const relation of Object.values(model.relations ?? {})) referenced.add(relation.to);
|
|
75
|
-
if (model.variants) for (const variantName of Object.keys(model.variants)) referenced.add(variantName);
|
|
76
|
-
if (model.base) referenced.add(model.base);
|
|
77
|
-
if (model.owner) referenced.add(modelName);
|
|
78
|
-
}
|
|
79
|
-
for (const modelName of modelNames) if (!referenced.has(modelName)) warnings.push(`Orphaned model: "${modelName}" is not referenced by any root, relation, or variant`);
|
|
80
|
-
}
|
|
1
|
+
import { t as validateContractDomain } from "./validate-domain-WfuBWGsF.mjs";
|
|
81
2
|
|
|
82
|
-
|
|
83
|
-
export { validateContractDomain };
|
|
84
|
-
//# sourceMappingURL=validate-domain.mjs.map
|
|
3
|
+
export { validateContractDomain };
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/contract",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.138",
|
|
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
|
-
"
|
|
9
|
-
"@prisma-next/utils": "0.3.0-dev.
|
|
8
|
+
"arktype": "^2.1.29",
|
|
9
|
+
"@prisma-next/utils": "0.3.0-dev.138",
|
|
10
|
+
"@prisma-next/operations": "0.3.0-dev.138"
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
12
13
|
"tsdown": "0.18.4",
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
"./ir": "./dist/ir.mjs",
|
|
29
30
|
"./types": "./dist/types.mjs",
|
|
30
31
|
"./validate-domain": "./dist/validate-domain.mjs",
|
|
32
|
+
"./validate-contract": "./dist/validate-contract.mjs",
|
|
31
33
|
"./package.json": "./package.json"
|
|
32
34
|
},
|
|
33
35
|
"repository": {
|
|
@@ -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
|
+
}
|
package/src/domain-types.ts
CHANGED
|
@@ -1,53 +1,76 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type ContractField = {
|
|
2
2
|
readonly nullable: boolean;
|
|
3
3
|
readonly codecId: string;
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
-
export type
|
|
6
|
+
export type ContractRelationOn = {
|
|
7
7
|
readonly localFields: readonly string[];
|
|
8
8
|
readonly targetFields: readonly string[];
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
export type
|
|
11
|
+
export type ContractReferenceRelation = {
|
|
12
12
|
readonly to: string;
|
|
13
13
|
readonly cardinality: '1:1' | '1:N' | 'N:1';
|
|
14
|
-
readonly on:
|
|
14
|
+
readonly on: ContractRelationOn;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export type
|
|
17
|
+
export type ContractEmbedRelation = {
|
|
18
18
|
readonly to: string;
|
|
19
19
|
readonly cardinality: '1:1' | '1:N';
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export type
|
|
22
|
+
export type ContractRelation = ContractReferenceRelation | ContractEmbedRelation;
|
|
23
23
|
|
|
24
|
-
export type
|
|
24
|
+
export type ContractDiscriminator = {
|
|
25
25
|
readonly field: string;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
export type
|
|
28
|
+
export type ContractVariantEntry = {
|
|
29
29
|
readonly value: string;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
export type
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
readonly
|
|
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>;
|
|
38
40
|
readonly base?: string;
|
|
39
41
|
readonly owner?: string;
|
|
40
|
-
}
|
|
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 ─────────────────────────────────────────────────────
|
|
41
64
|
|
|
42
65
|
type HasModelsWithRelations = {
|
|
43
|
-
readonly models: Record<string, { readonly relations: Record<string,
|
|
66
|
+
readonly models: Record<string, { readonly relations: Record<string, ContractRelation> }>;
|
|
44
67
|
};
|
|
45
68
|
|
|
46
69
|
export type ReferenceRelationKeys<
|
|
47
70
|
TContract extends HasModelsWithRelations,
|
|
48
71
|
ModelName extends string & keyof TContract['models'],
|
|
49
72
|
> = {
|
|
50
|
-
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends
|
|
73
|
+
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends ContractReferenceRelation
|
|
51
74
|
? K
|
|
52
75
|
: never;
|
|
53
76
|
}[keyof TContract['models'][ModelName]['relations']];
|
|
@@ -56,7 +79,7 @@ export type EmbedRelationKeys<
|
|
|
56
79
|
TContract extends HasModelsWithRelations,
|
|
57
80
|
ModelName extends string & keyof TContract['models'],
|
|
58
81
|
> = {
|
|
59
|
-
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends
|
|
82
|
+
[K in keyof TContract['models'][ModelName]['relations']]: TContract['models'][ModelName]['relations'][K] extends ContractReferenceRelation
|
|
60
83
|
? never
|
|
61
84
|
: K;
|
|
62
85
|
}[keyof TContract['models'][ModelName]['relations']];
|
package/src/exports/types.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
export type { Contract, ContractExecutionSection } from '../contract-types';
|
|
1
2
|
export type {
|
|
3
|
+
ContractDiscriminator,
|
|
4
|
+
ContractEmbedRelation,
|
|
5
|
+
ContractField,
|
|
6
|
+
ContractModel,
|
|
7
|
+
ContractReferenceRelation,
|
|
8
|
+
ContractRelation,
|
|
9
|
+
ContractRelationOn,
|
|
10
|
+
ContractVariantEntry,
|
|
2
11
|
DomainDiscriminator,
|
|
3
12
|
DomainEmbedRelation,
|
|
4
13
|
DomainField,
|
|
@@ -8,6 +17,7 @@ export type {
|
|
|
8
17
|
DomainRelationOn,
|
|
9
18
|
DomainVariantEntry,
|
|
10
19
|
EmbedRelationKeys,
|
|
20
|
+
ModelStorageBase,
|
|
11
21
|
ReferenceRelationKeys,
|
|
12
22
|
} from '../domain-types';
|
|
13
23
|
export type {
|
|
@@ -41,6 +51,7 @@ export type {
|
|
|
41
51
|
RenderTypeContext,
|
|
42
52
|
ResultType,
|
|
43
53
|
Source,
|
|
54
|
+
StorageBase,
|
|
44
55
|
StorageHashBase,
|
|
45
56
|
TaggedBigInt,
|
|
46
57
|
TaggedLiteralValue,
|
package/src/types.ts
CHANGED
|
@@ -56,6 +56,15 @@ export function profileHash<const T extends string>(value: T): ProfileHashBase<T
|
|
|
56
56
|
return value as ProfileHashBase<T>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Base type for family-specific storage blocks.
|
|
61
|
+
* Family storage types (SqlStorage, MongoStorage, etc.) extend this to carry the
|
|
62
|
+
* storage hash alongside family-specific data (tables, collections, etc.).
|
|
63
|
+
*/
|
|
64
|
+
export interface StorageBase<THash extends string = string> {
|
|
65
|
+
readonly storageHash: StorageHashBase<THash>;
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
export interface ContractBase<
|
|
60
69
|
TStorageHash extends StorageHashBase<string> = StorageHashBase<string>,
|
|
61
70
|
TExecutionHash extends ExecutionHashBase<string> = ExecutionHashBase<string>,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type } from 'arktype';
|
|
2
|
+
import type { Contract } from './contract-types';
|
|
3
|
+
import type { DomainContractShape, DomainValidationResult } from './validate-domain';
|
|
4
|
+
import { validateContractDomain } from './validate-domain';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Family-provided storage validator.
|
|
8
|
+
* SQL validates tables/columns/FKs; Mongo validates collections/embedding.
|
|
9
|
+
*/
|
|
10
|
+
export type StorageValidator = (contract: Contract) => void;
|
|
11
|
+
|
|
12
|
+
export interface ValidateContractResult {
|
|
13
|
+
readonly warnings: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const ContractSchema = type({
|
|
17
|
+
target: 'string',
|
|
18
|
+
targetFamily: 'string',
|
|
19
|
+
roots: 'Record<string, string>',
|
|
20
|
+
models: 'Record<string, unknown>',
|
|
21
|
+
storage: 'Record<string, unknown>',
|
|
22
|
+
capabilities: 'Record<string, Record<string, boolean>>',
|
|
23
|
+
extensionPacks: 'Record<string, unknown>',
|
|
24
|
+
meta: 'Record<string, unknown>',
|
|
25
|
+
'execution?': {
|
|
26
|
+
'executionHash?': 'string',
|
|
27
|
+
mutations: {
|
|
28
|
+
defaults: 'unknown[]',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
'profileHash?': 'string',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function stripPersistenceFields(raw: Record<string, unknown>): Record<string, unknown> {
|
|
35
|
+
const { schemaVersion: _, sources: _s, ...rest } = raw;
|
|
36
|
+
return rest;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function extractDomainShape(contract: Contract): DomainContractShape {
|
|
40
|
+
return {
|
|
41
|
+
roots: contract.roots,
|
|
42
|
+
models: contract.models,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Framework-level contract validation (ADR 182).
|
|
48
|
+
*
|
|
49
|
+
* Three-pass validation:
|
|
50
|
+
* 1. **Structural validation** (arktype): verifies required fields exist with
|
|
51
|
+
* correct base types.
|
|
52
|
+
* 2. **Domain validation** (framework-owned): roots, relation targets,
|
|
53
|
+
* variant/base consistency, discriminators, ownership, orphans.
|
|
54
|
+
* 3. **Storage validation** (family-provided): SQL validates tables/columns/FKs;
|
|
55
|
+
* Mongo validates collections/embedding.
|
|
56
|
+
*
|
|
57
|
+
* JSON persistence fields (`schemaVersion`, `sources`) are stripped before
|
|
58
|
+
* validation — they are not part of the in-memory contract representation.
|
|
59
|
+
*
|
|
60
|
+
* @template TContract The fully-typed contract type (preserves literal types).
|
|
61
|
+
* @param value Raw contract value (e.g. parsed from JSON).
|
|
62
|
+
* @param storageValidator Family-specific storage validation function.
|
|
63
|
+
* @returns The validated contract with full literal types.
|
|
64
|
+
*/
|
|
65
|
+
export function validateContract<TContract extends Contract>(
|
|
66
|
+
value: unknown,
|
|
67
|
+
storageValidator: StorageValidator,
|
|
68
|
+
): TContract & ValidateContractResult {
|
|
69
|
+
if (typeof value !== 'object' || value === null) {
|
|
70
|
+
throw new Error('Contract must be a non-null object');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const stripped = stripPersistenceFields(value as Record<string, unknown>);
|
|
74
|
+
|
|
75
|
+
const parsed = ContractSchema(stripped);
|
|
76
|
+
if (parsed instanceof type.errors) {
|
|
77
|
+
throw new Error(`Invalid contract structure: ${parsed.summary}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Arktype verified the structural shape; Contract adds branded hash types and
|
|
81
|
+
// ContractModel generics that can't be expressed in the schema.
|
|
82
|
+
const contract = parsed as unknown as Contract;
|
|
83
|
+
|
|
84
|
+
const domainResult: DomainValidationResult = validateContractDomain(extractDomainShape(contract));
|
|
85
|
+
|
|
86
|
+
storageValidator(contract);
|
|
87
|
+
|
|
88
|
+
// TContract narrows Contract with literal types from the caller's contract.d.ts;
|
|
89
|
+
// the runtime object is the same — the cast preserves the caller's type parameter.
|
|
90
|
+
return Object.assign(contract as unknown as TContract, {
|
|
91
|
+
warnings: domainResult.warnings,
|
|
92
|
+
});
|
|
93
|
+
}
|
package/dist/types.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/domain-types.ts","../src/types.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validate-domain.mjs","names":["errors: string[]","warnings: string[]"],"sources":["../src/validate-domain.ts"],"sourcesContent":["export interface DomainModelShape {\n readonly fields: Record<string, unknown>;\n readonly relations?: Record<string, { readonly to: string }>;\n readonly discriminator?: { readonly field: string };\n readonly variants?: Record<string, unknown>;\n readonly base?: string;\n readonly owner?: string;\n}\n\nexport interface DomainContractShape {\n readonly roots: Record<string, string>;\n readonly models: Record<string, DomainModelShape>;\n}\n\nexport interface DomainValidationResult {\n readonly warnings: string[];\n}\n\nexport function validateContractDomain(contract: DomainContractShape): DomainValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const modelNames = new Set(Object.keys(contract.models));\n\n validateRoots(contract, modelNames, errors);\n validateVariantsAndBases(contract, modelNames, errors);\n validateRelationTargets(contract, modelNames, errors);\n validateDiscriminators(contract, errors);\n validateOwnership(contract, modelNames, errors);\n detectOrphanedModels(contract, modelNames, warnings);\n\n if (errors.length > 0) {\n throw new Error(`Contract domain validation failed:\\n- ${errors.join('\\n- ')}`);\n }\n\n return { warnings };\n}\n\nfunction validateRoots(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n const seenValues = new Set<string>();\n for (const [rootKey, modelName] of Object.entries(contract.roots)) {\n if (seenValues.has(modelName)) {\n errors.push(`Duplicate root value: \"${modelName}\" is mapped by multiple root keys`);\n }\n seenValues.add(modelName);\n\n if (!modelNames.has(modelName)) {\n errors.push(\n `Root \"${rootKey}\" references model \"${modelName}\" which does not exist in models`,\n );\n }\n }\n}\n\nfunction validateVariantsAndBases(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n const models = new Map(Object.entries(contract.models));\n\n for (const [modelName, model] of models) {\n if (model.variants) {\n for (const variantName of Object.keys(model.variants)) {\n if (!modelNames.has(variantName)) {\n errors.push(\n `Model \"${modelName}\" lists variant \"${variantName}\" which does not exist in models`,\n );\n continue;\n }\n const variantModel = models.get(variantName);\n if (!variantModel) continue;\n if (variantModel.base !== modelName) {\n errors.push(\n `Variant \"${variantName}\" has base \"${variantModel.base ?? '(none)'}\" but expected \"${modelName}\"`,\n );\n }\n }\n }\n\n if (model.base) {\n if (!modelNames.has(model.base)) {\n errors.push(`Model \"${modelName}\" has base \"${model.base}\" which does not exist in models`);\n continue;\n }\n const baseModel = models.get(model.base);\n if (!baseModel) continue;\n if (!baseModel.variants || !Object.hasOwn(baseModel.variants, modelName)) {\n errors.push(\n `Model \"${modelName}\" has base \"${model.base}\" which does not list it as a variant`,\n );\n }\n }\n }\n}\n\nfunction validateRelationTargets(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n for (const [relName, relation] of Object.entries(model.relations ?? {})) {\n if (!modelNames.has(relation.to)) {\n errors.push(\n `Relation \"${relName}\" on model \"${modelName}\" targets \"${relation.to}\" which does not exist in models`,\n );\n }\n }\n }\n}\n\nfunction validateDiscriminators(contract: DomainContractShape, errors: string[]): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n if (model.discriminator) {\n if (!model.variants || Object.keys(model.variants).length === 0) {\n errors.push(`Model \"${modelName}\" has discriminator but no variants`);\n }\n if (!Object.hasOwn(model.fields, model.discriminator.field)) {\n errors.push(\n `Discriminator field \"${model.discriminator.field}\" is not a field on model \"${modelName}\"`,\n );\n }\n }\n\n if (model.variants && Object.keys(model.variants).length > 0 && !model.discriminator) {\n errors.push(`Model \"${modelName}\" has variants but no discriminator`);\n }\n\n if (model.base) {\n if (model.discriminator) {\n errors.push(`Model \"${modelName}\" has base and must not have discriminator`);\n }\n if (model.variants && Object.keys(model.variants).length > 0) {\n errors.push(`Model \"${modelName}\" has base and must not have variants`);\n }\n }\n }\n}\n\nfunction validateOwnership(\n contract: DomainContractShape,\n modelNames: Set<string>,\n errors: string[],\n): void {\n for (const [modelName, model] of Object.entries(contract.models)) {\n if (!model.owner) continue;\n\n if (model.owner === modelName) {\n errors.push(`Model \"${modelName}\" cannot own itself`);\n }\n\n if (!modelNames.has(model.owner)) {\n errors.push(`Model \"${modelName}\" has owner \"${model.owner}\" which does not exist in models`);\n }\n\n for (const [rootKey, rootModel] of Object.entries(contract.roots)) {\n if (rootModel === modelName) {\n errors.push(\n `Owned model \"${modelName}\" must not appear in roots (found as root \"${rootKey}\")`,\n );\n }\n }\n }\n}\n\nfunction detectOrphanedModels(\n contract: DomainContractShape,\n modelNames: Set<string>,\n warnings: string[],\n): void {\n const referenced = new Set<string>();\n\n for (const modelName of Object.values(contract.roots)) {\n referenced.add(modelName);\n }\n\n for (const [modelName, model] of Object.entries(contract.models)) {\n for (const relation of Object.values(model.relations ?? {})) {\n referenced.add(relation.to);\n }\n if (model.variants) {\n for (const variantName of Object.keys(model.variants)) {\n referenced.add(variantName);\n }\n }\n if (model.base) {\n referenced.add(model.base);\n }\n if (model.owner) {\n referenced.add(modelName);\n }\n }\n\n for (const modelName of modelNames) {\n if (!referenced.has(modelName)) {\n warnings.push(\n `Orphaned model: \"${modelName}\" is not referenced by any root, relation, or variant`,\n );\n }\n }\n}\n"],"mappings":"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"}
|