@prisma-next/sql-contract-ts 0.3.0-dev.7 → 0.3.0-dev.71
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 +78 -12
- package/dist/config-types.d.mts +8 -0
- package/dist/config-types.d.mts.map +1 -0
- package/dist/config-types.mjs +14 -0
- package/dist/config-types.mjs.map +1 -0
- package/dist/contract-builder.d.mts +110 -0
- package/dist/contract-builder.d.mts.map +1 -0
- package/dist/contract-builder.mjs +385 -0
- package/dist/contract-builder.mjs.map +1 -0
- package/package.json +24 -22
- package/schemas/data-contract-sql-v1.json +168 -8
- package/src/config-types.ts +11 -0
- package/src/contract-builder.ts +377 -47
- package/src/contract.ts +107 -96
- package/src/exports/config-types.ts +2 -0
- package/dist/chunk-SEOX3AAQ.js +0 -309
- package/dist/chunk-SEOX3AAQ.js.map +0 -1
- package/dist/contract-builder.d.ts +0 -87
- package/dist/contract-builder.d.ts.map +0 -1
- package/dist/contract.d.ts +0 -50
- package/dist/contract.d.ts.map +0 -1
- package/dist/exports/contract-builder.d.ts +0 -3
- package/dist/exports/contract-builder.d.ts.map +0 -1
- package/dist/exports/contract-builder.js +0 -216
- package/dist/exports/contract-builder.js.map +0 -1
- package/dist/exports/contract.d.ts +0 -2
- package/dist/exports/contract.d.ts.map +0 -1
- package/dist/exports/contract.js +0 -9
- package/dist/exports/contract.js.map +0 -1
- package/src/exports/contract.ts +0 -1
package/src/contract.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
ForeignKey,
|
|
3
|
-
ForeignKeyReferences,
|
|
4
2
|
Index,
|
|
5
3
|
ModelDefinition,
|
|
6
4
|
ModelField,
|
|
@@ -9,10 +7,11 @@ import type {
|
|
|
9
7
|
SqlContract,
|
|
10
8
|
SqlMappings,
|
|
11
9
|
SqlStorage,
|
|
12
|
-
|
|
13
|
-
StorageTable,
|
|
10
|
+
StorageTypeInstance,
|
|
14
11
|
UniqueConstraint,
|
|
15
12
|
} from '@prisma-next/sql-contract/types';
|
|
13
|
+
import { decodeContractDefaults } from '@prisma-next/sql-contract/validate';
|
|
14
|
+
import { ColumnDefaultSchema, ForeignKeySchema } from '@prisma-next/sql-contract/validators';
|
|
16
15
|
import { type } from 'arktype';
|
|
17
16
|
import type { O } from 'ts-toolbelt';
|
|
18
17
|
|
|
@@ -20,10 +19,20 @@ import type { O } from 'ts-toolbelt';
|
|
|
20
19
|
* Structural validation schema for SqlContract using Arktype.
|
|
21
20
|
* This validates the shape and types of the contract structure.
|
|
22
21
|
*/
|
|
23
|
-
|
|
22
|
+
|
|
23
|
+
const StorageColumnSchema = type({
|
|
24
24
|
nativeType: 'string',
|
|
25
25
|
codecId: 'string',
|
|
26
26
|
nullable: 'boolean',
|
|
27
|
+
'typeParams?': 'Record<string, unknown>',
|
|
28
|
+
'typeRef?': 'string',
|
|
29
|
+
'default?': ColumnDefaultSchema,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const StorageTypeInstanceSchema = type.declare<StorageTypeInstance>().type({
|
|
33
|
+
codecId: 'string',
|
|
34
|
+
nativeType: 'string',
|
|
35
|
+
typeParams: 'Record<string, unknown>',
|
|
27
36
|
});
|
|
28
37
|
|
|
29
38
|
const PrimaryKeySchema = type.declare<PrimaryKey>().type({
|
|
@@ -41,18 +50,7 @@ const IndexSchema = type.declare<Index>().type({
|
|
|
41
50
|
'name?': 'string',
|
|
42
51
|
});
|
|
43
52
|
|
|
44
|
-
const
|
|
45
|
-
table: 'string',
|
|
46
|
-
columns: type.string.array().readonly(),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const ForeignKeySchema = type.declare<ForeignKey>().type({
|
|
50
|
-
columns: type.string.array().readonly(),
|
|
51
|
-
references: ForeignKeyReferencesSchema,
|
|
52
|
-
'name?': 'string',
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
const StorageTableSchema = type.declare<StorageTable>().type({
|
|
53
|
+
const StorageTableSchema = type({
|
|
56
54
|
columns: type({ '[string]': StorageColumnSchema }),
|
|
57
55
|
'primaryKey?': PrimaryKeySchema,
|
|
58
56
|
uniques: UniqueConstraintSchema.array().readonly(),
|
|
@@ -60,8 +58,9 @@ const StorageTableSchema = type.declare<StorageTable>().type({
|
|
|
60
58
|
foreignKeys: ForeignKeySchema.array().readonly(),
|
|
61
59
|
});
|
|
62
60
|
|
|
63
|
-
const StorageSchema = type
|
|
61
|
+
const StorageSchema = type({
|
|
64
62
|
tables: type({ '[string]': StorageTableSchema }),
|
|
63
|
+
'types?': type({ '[string]': StorageTypeInstanceSchema }),
|
|
65
64
|
});
|
|
66
65
|
|
|
67
66
|
const ModelFieldSchema = type.declare<ModelField>().type({
|
|
@@ -78,6 +77,27 @@ const ModelSchema = type.declare<ModelDefinition>().type({
|
|
|
78
77
|
relations: type({ '[string]': 'unknown' }),
|
|
79
78
|
});
|
|
80
79
|
|
|
80
|
+
const ExecutionMutationDefaultValueSchema = type({
|
|
81
|
+
kind: "'generator'",
|
|
82
|
+
id: "'ulid' | 'nanoid' | 'uuidv7' | 'uuidv4' | 'cuid2' | 'ksuid'",
|
|
83
|
+
'params?': 'Record<string, unknown>',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const ExecutionMutationDefaultSchema = type({
|
|
87
|
+
ref: {
|
|
88
|
+
table: 'string',
|
|
89
|
+
column: 'string',
|
|
90
|
+
},
|
|
91
|
+
'onCreate?': ExecutionMutationDefaultValueSchema,
|
|
92
|
+
'onUpdate?': ExecutionMutationDefaultValueSchema,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const ExecutionSchema = type({
|
|
96
|
+
mutations: {
|
|
97
|
+
defaults: ExecutionMutationDefaultSchema.array().readonly(),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
81
101
|
/**
|
|
82
102
|
* Complete SqlContract schema for structural validation.
|
|
83
103
|
* This validates the entire contract structure at once.
|
|
@@ -86,7 +106,8 @@ const SqlContractSchema = type({
|
|
|
86
106
|
'schemaVersion?': "'1'",
|
|
87
107
|
target: 'string',
|
|
88
108
|
targetFamily: "'sql'",
|
|
89
|
-
|
|
109
|
+
storageHash: 'string',
|
|
110
|
+
'executionHash?': 'string',
|
|
90
111
|
'profileHash?': 'string',
|
|
91
112
|
'capabilities?': 'Record<string, Record<string, boolean>>',
|
|
92
113
|
'extensionPacks?': 'Record<string, unknown>',
|
|
@@ -94,6 +115,7 @@ const SqlContractSchema = type({
|
|
|
94
115
|
'sources?': 'Record<string, unknown>',
|
|
95
116
|
models: type({ '[string]': ModelSchema }),
|
|
96
117
|
storage: StorageSchema,
|
|
118
|
+
'execution?': ExecutionSchema,
|
|
97
119
|
});
|
|
98
120
|
|
|
99
121
|
/**
|
|
@@ -188,6 +210,9 @@ export function computeMappings(
|
|
|
188
210
|
* This checks that references (e.g., foreign keys, primary keys, uniques) point to storage objects that already exist.
|
|
189
211
|
* Structural validation is expected to have already completed before this helper runs.
|
|
190
212
|
*
|
|
213
|
+
* Rule: keep this focused on structural consistency only; capability/feature
|
|
214
|
+
* gating (e.g., defaults.*) belongs in migration/runtime verification, not here.
|
|
215
|
+
*
|
|
191
216
|
* @param structurallyValidatedContract - The contract whose structure has already been validated
|
|
192
217
|
* @throws Error if logical validation fails
|
|
193
218
|
*/
|
|
@@ -195,6 +220,66 @@ function validateContractLogic(structurallyValidatedContract: SqlContract<SqlSto
|
|
|
195
220
|
const { storage, models } = structurallyValidatedContract;
|
|
196
221
|
const tableNames = new Set(Object.keys(storage.tables));
|
|
197
222
|
|
|
223
|
+
// Validate storage.types if present
|
|
224
|
+
if (storage.types) {
|
|
225
|
+
for (const [typeName, typeInstance] of Object.entries(storage.types)) {
|
|
226
|
+
// Validate typeParams is not an array (arrays are objects in JS but not valid here)
|
|
227
|
+
if (Array.isArray(typeInstance.typeParams)) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Type instance "${typeName}" has invalid typeParams: must be a plain object, not an array`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Validate columns in all tables
|
|
236
|
+
for (const [tableName, table] of Object.entries(storage.tables)) {
|
|
237
|
+
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
238
|
+
// Validate typeParams and typeRef are mutually exclusive
|
|
239
|
+
if (column.typeParams !== undefined && column.typeRef !== undefined) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`Column "${columnName}" in table "${tableName}" has both typeParams and typeRef; these are mutually exclusive`,
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Validate typeParams is not an array (arrays are objects in JS but not valid here)
|
|
246
|
+
if (column.typeParams !== undefined && Array.isArray(column.typeParams)) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Column "${columnName}" in table "${tableName}" has invalid typeParams: must be a plain object, not an array`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Validate NOT NULL columns do not have literal null defaults
|
|
253
|
+
if (!column.nullable && column.default?.kind === 'literal' && column.default.value === null) {
|
|
254
|
+
throw new Error(
|
|
255
|
+
`Table "${tableName}" column "${columnName}" is NOT NULL but has a literal null default`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Validate typeRef points to an existing storage.types key and matches codecId/nativeType
|
|
260
|
+
if (column.typeRef !== undefined) {
|
|
261
|
+
const referencedType = storage.types?.[column.typeRef];
|
|
262
|
+
if (!referencedType) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`Column "${columnName}" in table "${tableName}" references non-existent type instance "${column.typeRef}" (not found in storage.types)`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (column.codecId !== referencedType.codecId) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`Column "${columnName}" in table "${tableName}" has codecId "${column.codecId}" but references type instance "${column.typeRef}" with codecId "${referencedType.codecId}"`,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (column.nativeType !== referencedType.nativeType) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Column "${columnName}" in table "${tableName}" has nativeType "${column.nativeType}" but references type instance "${column.typeRef}" with nativeType "${referencedType.nativeType}"`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
198
283
|
// Validate models
|
|
199
284
|
for (const [modelName, modelUnknown] of Object.entries(models)) {
|
|
200
285
|
const model = modelUnknown as ModelDefinition;
|
|
@@ -382,82 +467,8 @@ function validateContractLogic(structurallyValidatedContract: SqlContract<SqlSto
|
|
|
382
467
|
}
|
|
383
468
|
}
|
|
384
469
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
// Only normalize if storage exists (validation will catch if it's missing)
|
|
389
|
-
let normalizedStorage = contractObj['storage'];
|
|
390
|
-
if (normalizedStorage && typeof normalizedStorage === 'object' && normalizedStorage !== null) {
|
|
391
|
-
const storage = normalizedStorage as Record<string, unknown>;
|
|
392
|
-
const tables = storage['tables'] as Record<string, unknown> | undefined;
|
|
393
|
-
|
|
394
|
-
if (tables) {
|
|
395
|
-
// Normalize storage tables
|
|
396
|
-
const normalizedTables: Record<string, unknown> = {};
|
|
397
|
-
for (const [tableName, table] of Object.entries(tables)) {
|
|
398
|
-
const tableObj = table as Record<string, unknown>;
|
|
399
|
-
const columns = tableObj['columns'] as Record<string, unknown> | undefined;
|
|
400
|
-
|
|
401
|
-
if (columns) {
|
|
402
|
-
// Normalize columns: add nullable: false if missing
|
|
403
|
-
const normalizedColumns: Record<string, unknown> = {};
|
|
404
|
-
for (const [columnName, column] of Object.entries(columns)) {
|
|
405
|
-
const columnObj = column as Record<string, unknown>;
|
|
406
|
-
const normalizedColumn: Record<string, unknown> = {
|
|
407
|
-
...columnObj,
|
|
408
|
-
nullable: columnObj['nullable'] ?? false,
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
normalizedColumns[columnName] = normalizedColumn;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Normalize table arrays: add empty arrays if missing
|
|
415
|
-
normalizedTables[tableName] = {
|
|
416
|
-
...tableObj,
|
|
417
|
-
columns: normalizedColumns,
|
|
418
|
-
uniques: tableObj['uniques'] ?? [],
|
|
419
|
-
indexes: tableObj['indexes'] ?? [],
|
|
420
|
-
foreignKeys: tableObj['foreignKeys'] ?? [],
|
|
421
|
-
};
|
|
422
|
-
} else {
|
|
423
|
-
normalizedTables[tableName] = tableObj;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
normalizedStorage = {
|
|
428
|
-
...storage,
|
|
429
|
-
tables: normalizedTables,
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Only normalize if models exists (validation will catch if it's missing)
|
|
435
|
-
let normalizedModels = contractObj['models'];
|
|
436
|
-
if (normalizedModels && typeof normalizedModels === 'object' && normalizedModels !== null) {
|
|
437
|
-
const models = normalizedModels as Record<string, unknown>;
|
|
438
|
-
const normalizedModelsObj: Record<string, unknown> = {};
|
|
439
|
-
for (const [modelName, model] of Object.entries(models)) {
|
|
440
|
-
const modelObj = model as Record<string, unknown>;
|
|
441
|
-
normalizedModelsObj[modelName] = {
|
|
442
|
-
...modelObj,
|
|
443
|
-
relations: modelObj['relations'] ?? {},
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
normalizedModels = normalizedModelsObj;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Normalize top-level fields: add empty objects if missing
|
|
450
|
-
return {
|
|
451
|
-
...contractObj,
|
|
452
|
-
models: normalizedModels,
|
|
453
|
-
relations: contractObj['relations'] ?? {},
|
|
454
|
-
storage: normalizedStorage,
|
|
455
|
-
extensionPacks: contractObj['extensionPacks'] ?? {},
|
|
456
|
-
capabilities: contractObj['capabilities'] ?? {},
|
|
457
|
-
meta: contractObj['meta'] ?? {},
|
|
458
|
-
sources: contractObj['sources'] ?? {},
|
|
459
|
-
} as SqlContract<SqlStorage>;
|
|
460
|
-
}
|
|
470
|
+
import { normalizeContract } from '@prisma-next/sql-contract/validate';
|
|
471
|
+
export { normalizeContract };
|
|
461
472
|
|
|
462
473
|
/**
|
|
463
474
|
* Validates that a JSON import conforms to the SqlContract structure
|
|
@@ -529,5 +540,5 @@ export function validateContract<TContract extends SqlContract<SqlStorage>>(
|
|
|
529
540
|
|
|
530
541
|
// Type assertion: The caller provides the strict type via TContract.
|
|
531
542
|
// We validate the structure matches, but the precise types come from contract.d.ts
|
|
532
|
-
return contractWithMappings as TContract;
|
|
543
|
+
return decodeContractDefaults(contractWithMappings) as TContract;
|
|
533
544
|
}
|
package/dist/chunk-SEOX3AAQ.js
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
// src/contract.ts
|
|
2
|
-
import { type } from "arktype";
|
|
3
|
-
var StorageColumnSchema = type.declare().type({
|
|
4
|
-
nativeType: "string",
|
|
5
|
-
codecId: "string",
|
|
6
|
-
nullable: "boolean"
|
|
7
|
-
});
|
|
8
|
-
var PrimaryKeySchema = type.declare().type({
|
|
9
|
-
columns: type.string.array().readonly(),
|
|
10
|
-
"name?": "string"
|
|
11
|
-
});
|
|
12
|
-
var UniqueConstraintSchema = type.declare().type({
|
|
13
|
-
columns: type.string.array().readonly(),
|
|
14
|
-
"name?": "string"
|
|
15
|
-
});
|
|
16
|
-
var IndexSchema = type.declare().type({
|
|
17
|
-
columns: type.string.array().readonly(),
|
|
18
|
-
"name?": "string"
|
|
19
|
-
});
|
|
20
|
-
var ForeignKeyReferencesSchema = type.declare().type({
|
|
21
|
-
table: "string",
|
|
22
|
-
columns: type.string.array().readonly()
|
|
23
|
-
});
|
|
24
|
-
var ForeignKeySchema = type.declare().type({
|
|
25
|
-
columns: type.string.array().readonly(),
|
|
26
|
-
references: ForeignKeyReferencesSchema,
|
|
27
|
-
"name?": "string"
|
|
28
|
-
});
|
|
29
|
-
var StorageTableSchema = type.declare().type({
|
|
30
|
-
columns: type({ "[string]": StorageColumnSchema }),
|
|
31
|
-
"primaryKey?": PrimaryKeySchema,
|
|
32
|
-
uniques: UniqueConstraintSchema.array().readonly(),
|
|
33
|
-
indexes: IndexSchema.array().readonly(),
|
|
34
|
-
foreignKeys: ForeignKeySchema.array().readonly()
|
|
35
|
-
});
|
|
36
|
-
var StorageSchema = type.declare().type({
|
|
37
|
-
tables: type({ "[string]": StorageTableSchema })
|
|
38
|
-
});
|
|
39
|
-
var ModelFieldSchema = type.declare().type({
|
|
40
|
-
column: "string"
|
|
41
|
-
});
|
|
42
|
-
var ModelStorageSchema = type.declare().type({
|
|
43
|
-
table: "string"
|
|
44
|
-
});
|
|
45
|
-
var ModelSchema = type.declare().type({
|
|
46
|
-
storage: ModelStorageSchema,
|
|
47
|
-
fields: type({ "[string]": ModelFieldSchema }),
|
|
48
|
-
relations: type({ "[string]": "unknown" })
|
|
49
|
-
});
|
|
50
|
-
var SqlContractSchema = type({
|
|
51
|
-
"schemaVersion?": "'1'",
|
|
52
|
-
target: "string",
|
|
53
|
-
targetFamily: "'sql'",
|
|
54
|
-
coreHash: "string",
|
|
55
|
-
"profileHash?": "string",
|
|
56
|
-
"capabilities?": "Record<string, Record<string, boolean>>",
|
|
57
|
-
"extensionPacks?": "Record<string, unknown>",
|
|
58
|
-
"meta?": "Record<string, unknown>",
|
|
59
|
-
"sources?": "Record<string, unknown>",
|
|
60
|
-
models: type({ "[string]": ModelSchema }),
|
|
61
|
-
storage: StorageSchema
|
|
62
|
-
});
|
|
63
|
-
function validateContractStructure(value) {
|
|
64
|
-
const rawValue = value;
|
|
65
|
-
if (rawValue.targetFamily !== void 0 && rawValue.targetFamily !== "sql") {
|
|
66
|
-
throw new Error(`Unsupported target family: ${rawValue.targetFamily}`);
|
|
67
|
-
}
|
|
68
|
-
const contractResult = SqlContractSchema(value);
|
|
69
|
-
if (contractResult instanceof type.errors) {
|
|
70
|
-
const messages = contractResult.map((p) => p.message).join("; ");
|
|
71
|
-
throw new Error(`Contract structural validation failed: ${messages}`);
|
|
72
|
-
}
|
|
73
|
-
return contractResult;
|
|
74
|
-
}
|
|
75
|
-
function computeMappings(models, _storage, existingMappings) {
|
|
76
|
-
const modelToTable = {};
|
|
77
|
-
const tableToModel = {};
|
|
78
|
-
const fieldToColumn = {};
|
|
79
|
-
const columnToField = {};
|
|
80
|
-
for (const [modelName, model] of Object.entries(models)) {
|
|
81
|
-
const tableName = model.storage.table;
|
|
82
|
-
modelToTable[modelName] = tableName;
|
|
83
|
-
tableToModel[tableName] = modelName;
|
|
84
|
-
const modelFieldToColumn = {};
|
|
85
|
-
for (const [fieldName, field] of Object.entries(model.fields)) {
|
|
86
|
-
const columnName = field.column;
|
|
87
|
-
modelFieldToColumn[fieldName] = columnName;
|
|
88
|
-
if (!columnToField[tableName]) {
|
|
89
|
-
columnToField[tableName] = {};
|
|
90
|
-
}
|
|
91
|
-
columnToField[tableName][columnName] = fieldName;
|
|
92
|
-
}
|
|
93
|
-
fieldToColumn[modelName] = modelFieldToColumn;
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
modelToTable: existingMappings?.modelToTable ?? modelToTable,
|
|
97
|
-
tableToModel: existingMappings?.tableToModel ?? tableToModel,
|
|
98
|
-
fieldToColumn: existingMappings?.fieldToColumn ?? fieldToColumn,
|
|
99
|
-
columnToField: existingMappings?.columnToField ?? columnToField,
|
|
100
|
-
codecTypes: existingMappings?.codecTypes ?? {},
|
|
101
|
-
operationTypes: existingMappings?.operationTypes ?? {}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
function validateContractLogic(structurallyValidatedContract) {
|
|
105
|
-
const { storage, models } = structurallyValidatedContract;
|
|
106
|
-
const tableNames = new Set(Object.keys(storage.tables));
|
|
107
|
-
for (const [modelName, modelUnknown] of Object.entries(models)) {
|
|
108
|
-
const model = modelUnknown;
|
|
109
|
-
if (!model.storage?.table) {
|
|
110
|
-
throw new Error(`Model "${modelName}" is missing storage.table`);
|
|
111
|
-
}
|
|
112
|
-
const tableName = model.storage.table;
|
|
113
|
-
if (!tableNames.has(tableName)) {
|
|
114
|
-
throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
|
|
115
|
-
}
|
|
116
|
-
const table = storage.tables[tableName];
|
|
117
|
-
if (!table) {
|
|
118
|
-
throw new Error(`Model "${modelName}" references non-existent table "${tableName}"`);
|
|
119
|
-
}
|
|
120
|
-
if (!table.primaryKey) {
|
|
121
|
-
throw new Error(`Model "${modelName}" table "${tableName}" is missing a primary key`);
|
|
122
|
-
}
|
|
123
|
-
const columnNames = new Set(Object.keys(table.columns));
|
|
124
|
-
if (!model.fields) {
|
|
125
|
-
throw new Error(`Model "${modelName}" is missing fields`);
|
|
126
|
-
}
|
|
127
|
-
for (const [fieldName, fieldUnknown] of Object.entries(model.fields)) {
|
|
128
|
-
const field = fieldUnknown;
|
|
129
|
-
if (!field.column) {
|
|
130
|
-
throw new Error(`Model "${modelName}" field "${fieldName}" is missing column property`);
|
|
131
|
-
}
|
|
132
|
-
if (!columnNames.has(field.column)) {
|
|
133
|
-
throw new Error(
|
|
134
|
-
`Model "${modelName}" field "${fieldName}" references non-existent column "${field.column}" in table "${tableName}"`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (model.relations) {
|
|
139
|
-
for (const [relationName, relation] of Object.entries(model.relations)) {
|
|
140
|
-
if (typeof relation === "object" && relation !== null && "on" in relation && "to" in relation) {
|
|
141
|
-
const on = relation.on;
|
|
142
|
-
const cardinality = relation.cardinality;
|
|
143
|
-
if (on.parentCols && on.childCols) {
|
|
144
|
-
if (cardinality === "1:N") {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
const hasMatchingFk = table.foreignKeys?.some((fk) => {
|
|
148
|
-
return fk.columns.length === on.childCols?.length && fk.columns.every((col, i) => col === on.childCols?.[i]) && fk.references.table && fk.references.columns.length === on.parentCols?.length && fk.references.columns.every((col, i) => col === on.parentCols?.[i]);
|
|
149
|
-
});
|
|
150
|
-
if (!hasMatchingFk) {
|
|
151
|
-
throw new Error(
|
|
152
|
-
`Model "${modelName}" relation "${relationName}" does not have a corresponding foreign key in table "${tableName}"`
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
for (const [tableName, table] of Object.entries(storage.tables)) {
|
|
161
|
-
const columnNames = new Set(Object.keys(table.columns));
|
|
162
|
-
if (table.primaryKey) {
|
|
163
|
-
for (const colName of table.primaryKey.columns) {
|
|
164
|
-
if (!columnNames.has(colName)) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`Table "${tableName}" primaryKey references non-existent column "${colName}"`
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
for (const unique of table.uniques) {
|
|
172
|
-
for (const colName of unique.columns) {
|
|
173
|
-
if (!columnNames.has(colName)) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
`Table "${tableName}" unique constraint references non-existent column "${colName}"`
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
for (const index of table.indexes) {
|
|
181
|
-
for (const colName of index.columns) {
|
|
182
|
-
if (!columnNames.has(colName)) {
|
|
183
|
-
throw new Error(`Table "${tableName}" index references non-existent column "${colName}"`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
for (const fk of table.foreignKeys) {
|
|
188
|
-
for (const colName of fk.columns) {
|
|
189
|
-
if (!columnNames.has(colName)) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
`Table "${tableName}" foreignKey references non-existent column "${colName}"`
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (!tableNames.has(fk.references.table)) {
|
|
196
|
-
throw new Error(
|
|
197
|
-
`Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
const referencedTable = storage.tables[fk.references.table];
|
|
201
|
-
if (!referencedTable) {
|
|
202
|
-
throw new Error(
|
|
203
|
-
`Table "${tableName}" foreignKey references non-existent table "${fk.references.table}"`
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
const referencedColumnNames = new Set(Object.keys(referencedTable.columns));
|
|
207
|
-
for (const colName of fk.references.columns) {
|
|
208
|
-
if (!referencedColumnNames.has(colName)) {
|
|
209
|
-
throw new Error(
|
|
210
|
-
`Table "${tableName}" foreignKey references non-existent column "${colName}" in table "${fk.references.table}"`
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (fk.columns.length !== fk.references.columns.length) {
|
|
215
|
-
throw new Error(
|
|
216
|
-
`Table "${tableName}" foreignKey column count (${fk.columns.length}) does not match referenced column count (${fk.references.columns.length})`
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function normalizeContract(contract) {
|
|
223
|
-
const contractObj = contract;
|
|
224
|
-
let normalizedStorage = contractObj["storage"];
|
|
225
|
-
if (normalizedStorage && typeof normalizedStorage === "object" && normalizedStorage !== null) {
|
|
226
|
-
const storage = normalizedStorage;
|
|
227
|
-
const tables = storage["tables"];
|
|
228
|
-
if (tables) {
|
|
229
|
-
const normalizedTables = {};
|
|
230
|
-
for (const [tableName, table] of Object.entries(tables)) {
|
|
231
|
-
const tableObj = table;
|
|
232
|
-
const columns = tableObj["columns"];
|
|
233
|
-
if (columns) {
|
|
234
|
-
const normalizedColumns = {};
|
|
235
|
-
for (const [columnName, column] of Object.entries(columns)) {
|
|
236
|
-
const columnObj = column;
|
|
237
|
-
const normalizedColumn = {
|
|
238
|
-
...columnObj,
|
|
239
|
-
nullable: columnObj["nullable"] ?? false
|
|
240
|
-
};
|
|
241
|
-
normalizedColumns[columnName] = normalizedColumn;
|
|
242
|
-
}
|
|
243
|
-
normalizedTables[tableName] = {
|
|
244
|
-
...tableObj,
|
|
245
|
-
columns: normalizedColumns,
|
|
246
|
-
uniques: tableObj["uniques"] ?? [],
|
|
247
|
-
indexes: tableObj["indexes"] ?? [],
|
|
248
|
-
foreignKeys: tableObj["foreignKeys"] ?? []
|
|
249
|
-
};
|
|
250
|
-
} else {
|
|
251
|
-
normalizedTables[tableName] = tableObj;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
normalizedStorage = {
|
|
255
|
-
...storage,
|
|
256
|
-
tables: normalizedTables
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
let normalizedModels = contractObj["models"];
|
|
261
|
-
if (normalizedModels && typeof normalizedModels === "object" && normalizedModels !== null) {
|
|
262
|
-
const models = normalizedModels;
|
|
263
|
-
const normalizedModelsObj = {};
|
|
264
|
-
for (const [modelName, model] of Object.entries(models)) {
|
|
265
|
-
const modelObj = model;
|
|
266
|
-
normalizedModelsObj[modelName] = {
|
|
267
|
-
...modelObj,
|
|
268
|
-
relations: modelObj["relations"] ?? {}
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
normalizedModels = normalizedModelsObj;
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
...contractObj,
|
|
275
|
-
models: normalizedModels,
|
|
276
|
-
relations: contractObj["relations"] ?? {},
|
|
277
|
-
storage: normalizedStorage,
|
|
278
|
-
extensionPacks: contractObj["extensionPacks"] ?? {},
|
|
279
|
-
capabilities: contractObj["capabilities"] ?? {},
|
|
280
|
-
meta: contractObj["meta"] ?? {},
|
|
281
|
-
sources: contractObj["sources"] ?? {}
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
function validateContract(value) {
|
|
285
|
-
const normalized = normalizeContract(value);
|
|
286
|
-
const structurallyValid = validateContractStructure(normalized);
|
|
287
|
-
const contractForValidation = structurallyValid;
|
|
288
|
-
validateContractLogic(contractForValidation);
|
|
289
|
-
const existingMappings = contractForValidation.mappings;
|
|
290
|
-
const mappings = computeMappings(
|
|
291
|
-
contractForValidation.models,
|
|
292
|
-
contractForValidation.storage,
|
|
293
|
-
existingMappings
|
|
294
|
-
);
|
|
295
|
-
const contractWithMappings = {
|
|
296
|
-
...structurallyValid,
|
|
297
|
-
models: contractForValidation.models,
|
|
298
|
-
relations: contractForValidation.relations,
|
|
299
|
-
storage: contractForValidation.storage,
|
|
300
|
-
mappings
|
|
301
|
-
};
|
|
302
|
-
return contractWithMappings;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export {
|
|
306
|
-
computeMappings,
|
|
307
|
-
validateContract
|
|
308
|
-
};
|
|
309
|
-
//# sourceMappingURL=chunk-SEOX3AAQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/contract.ts"],"sourcesContent":["import type {\n ForeignKey,\n ForeignKeyReferences,\n Index,\n ModelDefinition,\n ModelField,\n ModelStorage,\n PrimaryKey,\n SqlContract,\n SqlMappings,\n SqlStorage,\n StorageColumn,\n StorageTable,\n UniqueConstraint,\n} from '@prisma-next/sql-contract/types';\nimport { type } from 'arktype';\nimport type { O } from 'ts-toolbelt';\n\n/**\n * Structural validation schema for SqlContract using Arktype.\n * This validates the shape and types of the contract structure.\n */\nconst StorageColumnSchema = type.declare<StorageColumn>().type({\n nativeType: 'string',\n codecId: 'string',\n nullable: 'boolean',\n});\n\nconst PrimaryKeySchema = type.declare<PrimaryKey>().type({\n columns: type.string.array().readonly(),\n 'name?': 'string',\n});\n\nconst UniqueConstraintSchema = type.declare<UniqueConstraint>().type({\n columns: type.string.array().readonly(),\n 'name?': 'string',\n});\n\nconst IndexSchema = type.declare<Index>().type({\n columns: type.string.array().readonly(),\n 'name?': 'string',\n});\n\nconst ForeignKeyReferencesSchema = type.declare<ForeignKeyReferences>().type({\n table: 'string',\n columns: type.string.array().readonly(),\n});\n\nconst ForeignKeySchema = type.declare<ForeignKey>().type({\n columns: type.string.array().readonly(),\n references: ForeignKeyReferencesSchema,\n 'name?': 'string',\n});\n\nconst StorageTableSchema = type.declare<StorageTable>().type({\n columns: type({ '[string]': StorageColumnSchema }),\n 'primaryKey?': PrimaryKeySchema,\n uniques: UniqueConstraintSchema.array().readonly(),\n indexes: IndexSchema.array().readonly(),\n foreignKeys: ForeignKeySchema.array().readonly(),\n});\n\nconst StorageSchema = type.declare<SqlStorage>().type({\n tables: type({ '[string]': StorageTableSchema }),\n});\n\nconst ModelFieldSchema = type.declare<ModelField>().type({\n column: 'string',\n});\n\nconst ModelStorageSchema = type.declare<ModelStorage>().type({\n table: 'string',\n});\n\nconst ModelSchema = type.declare<ModelDefinition>().type({\n storage: ModelStorageSchema,\n fields: type({ '[string]': ModelFieldSchema }),\n relations: type({ '[string]': 'unknown' }),\n});\n\n/**\n * Complete SqlContract schema for structural validation.\n * This validates the entire contract structure at once.\n */\nconst SqlContractSchema = type({\n 'schemaVersion?': \"'1'\",\n target: 'string',\n targetFamily: \"'sql'\",\n coreHash: 'string',\n 'profileHash?': 'string',\n 'capabilities?': 'Record<string, Record<string, boolean>>',\n 'extensionPacks?': 'Record<string, unknown>',\n 'meta?': 'Record<string, unknown>',\n 'sources?': 'Record<string, unknown>',\n models: type({ '[string]': ModelSchema }),\n storage: StorageSchema,\n});\n\n/**\n * Validates the structural shape of a SqlContract using Arktype.\n *\n * **Responsibility: Validation Only**\n * This function validates that the contract has the correct structure and types.\n * It does NOT normalize the contract - normalization must happen in the contract builder.\n *\n * The contract passed to this function must already be normalized (all required fields present).\n * If normalization is needed, it should be done by the contract builder before calling this function.\n *\n * This ensures all required fields are present and have the correct types.\n *\n * @param value - The contract value to validate (typically from a JSON import)\n * @returns The validated contract if structure is valid\n * @throws Error if the contract structure is invalid\n */\nfunction validateContractStructure<T extends SqlContract<SqlStorage>>(\n value: unknown,\n): O.Overwrite<T, { targetFamily: 'sql' }> {\n // Check targetFamily first to provide a clear error message for unsupported target families\n const rawValue = value as { targetFamily?: string };\n if (rawValue.targetFamily !== undefined && rawValue.targetFamily !== 'sql') {\n /* c8 ignore next */\n throw new Error(`Unsupported target family: ${rawValue.targetFamily}`);\n }\n\n const contractResult = SqlContractSchema(value);\n\n if (contractResult instanceof type.errors) {\n const messages = contractResult.map((p: { message: string }) => p.message).join('; ');\n throw new Error(`Contract structural validation failed: ${messages}`);\n }\n\n // After validation, contractResult matches the schema and preserves the input structure\n // TypeScript needs an assertion here due to exactOptionalPropertyTypes differences\n // between Arktype's inferred type and the generic T, but runtime-wise they're compatible\n return contractResult as O.Overwrite<T, { targetFamily: 'sql' }>;\n}\n\n/**\n * Computes mapping dictionaries from models and storage structures.\n * Assumes valid input - validation happens separately in validateContractLogic().\n *\n * @param models - Models object from contract\n * @param storage - Storage object from contract\n * @param existingMappings - Existing mappings from contract input (optional)\n * @returns Computed mappings dictionary\n */\nexport function computeMappings(\n models: Record<string, ModelDefinition>,\n _storage: SqlStorage,\n existingMappings?: Partial<SqlMappings>,\n): SqlMappings {\n const modelToTable: Record<string, string> = {};\n const tableToModel: Record<string, string> = {};\n const fieldToColumn: Record<string, Record<string, string>> = {};\n const columnToField: Record<string, Record<string, string>> = {};\n\n for (const [modelName, model] of Object.entries(models)) {\n const tableName = model.storage.table;\n modelToTable[modelName] = tableName;\n tableToModel[tableName] = modelName;\n\n const modelFieldToColumn: Record<string, string> = {};\n for (const [fieldName, field] of Object.entries(model.fields)) {\n const columnName = field.column;\n modelFieldToColumn[fieldName] = columnName;\n\n if (!columnToField[tableName]) {\n columnToField[tableName] = {};\n }\n columnToField[tableName][columnName] = fieldName;\n }\n fieldToColumn[modelName] = modelFieldToColumn;\n }\n\n // Preserve existing mappings if provided, otherwise use computed ones\n return {\n modelToTable: existingMappings?.modelToTable ?? modelToTable,\n tableToModel: existingMappings?.tableToModel ?? tableToModel,\n fieldToColumn: existingMappings?.fieldToColumn ?? fieldToColumn,\n columnToField: existingMappings?.columnToField ?? columnToField,\n codecTypes: existingMappings?.codecTypes ?? {},\n operationTypes: existingMappings?.operationTypes ?? {},\n };\n}\n\n/**\n * Validates logical consistency of a **structurally validated** SqlContract.\n * This checks that references (e.g., foreign keys, primary keys, uniques) point to storage objects that already exist.\n * Structural validation is expected to have already completed before this helper runs.\n *\n * @param structurallyValidatedContract - The contract whose structure has already been validated\n * @throws Error if logical validation fails\n */\nfunction validateContractLogic(structurallyValidatedContract: SqlContract<SqlStorage>): void {\n const { storage, models } = structurallyValidatedContract;\n const tableNames = new Set(Object.keys(storage.tables));\n\n // Validate models\n for (const [modelName, modelUnknown] of Object.entries(models)) {\n const model = modelUnknown as ModelDefinition;\n // Validate model has storage.table\n if (!model.storage?.table) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" is missing storage.table`);\n }\n\n const tableName = model.storage.table;\n\n // Validate model's table exists in storage\n if (!tableNames.has(tableName)) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\n\n const table = storage.tables[tableName];\n if (!table) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" references non-existent table \"${tableName}\"`);\n }\n\n // Validate model's table has a primary key\n if (!table.primaryKey) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" table \"${tableName}\" is missing a primary key`);\n }\n\n const columnNames = new Set(Object.keys(table.columns));\n\n // Validate model fields\n if (!model.fields) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" is missing fields`);\n }\n\n for (const [fieldName, fieldUnknown] of Object.entries(model.fields)) {\n const field = fieldUnknown as { column: string };\n // Validate field has column property\n if (!field.column) {\n /* c8 ignore next */\n throw new Error(`Model \"${modelName}\" field \"${fieldName}\" is missing column property`);\n }\n\n // Validate field's column exists in the model's backing table\n if (!columnNames.has(field.column)) {\n /* c8 ignore next */\n throw new Error(\n `Model \"${modelName}\" field \"${fieldName}\" references non-existent column \"${field.column}\" in table \"${tableName}\"`,\n );\n }\n }\n\n // Validate model relations have corresponding foreign keys\n if (model.relations) {\n for (const [relationName, relation] of Object.entries(model.relations)) {\n // For now, we'll do basic validation. Full FK validation can be added later\n // This would require checking that the relation's on.parentCols/childCols match FKs\n if (\n typeof relation === 'object' &&\n relation !== null &&\n 'on' in relation &&\n 'to' in relation\n ) {\n const on = relation.on as { parentCols?: string[]; childCols?: string[] };\n const cardinality = (relation as { cardinality?: string }).cardinality;\n if (on.parentCols && on.childCols) {\n // For 1:N relations, the foreign key is on the child table\n // For N:1 relations, the foreign key is on the parent table (this table)\n // For now, we'll skip validation for 1:N relations as the FK is on the child table\n // and we'll validate it when we process the child model\n if (cardinality === '1:N') {\n // Foreign key is on the child table, skip validation here\n // It will be validated when we process the child model\n continue;\n }\n\n // For N:1 relations, check that there's a foreign key matching this relation\n const hasMatchingFk = table.foreignKeys?.some((fk) => {\n return (\n fk.columns.length === on.childCols?.length &&\n fk.columns.every((col, i) => col === on.childCols?.[i]) &&\n fk.references.table &&\n fk.references.columns.length === on.parentCols?.length &&\n fk.references.columns.every((col, i) => col === on.parentCols?.[i])\n );\n });\n\n if (!hasMatchingFk) {\n /* c8 ignore next */\n throw new Error(\n `Model \"${modelName}\" relation \"${relationName}\" does not have a corresponding foreign key in table \"${tableName}\"`,\n );\n }\n }\n }\n }\n }\n }\n\n for (const [tableName, table] of Object.entries(storage.tables)) {\n const columnNames = new Set(Object.keys(table.columns));\n\n // Validate primaryKey references existing columns\n if (table.primaryKey) {\n for (const colName of table.primaryKey.columns) {\n if (!columnNames.has(colName)) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" primaryKey references non-existent column \"${colName}\"`,\n );\n }\n }\n }\n\n // Validate unique constraints reference existing columns\n for (const unique of table.uniques) {\n for (const colName of unique.columns) {\n if (!columnNames.has(colName)) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" unique constraint references non-existent column \"${colName}\"`,\n );\n }\n }\n }\n\n // Validate indexes reference existing columns\n for (const index of table.indexes) {\n for (const colName of index.columns) {\n if (!columnNames.has(colName)) {\n /* c8 ignore next */\n throw new Error(`Table \"${tableName}\" index references non-existent column \"${colName}\"`);\n }\n }\n }\n\n // Validate foreignKeys reference existing tables and columns\n for (const fk of table.foreignKeys) {\n // Validate FK columns exist in the referencing table\n for (const colName of fk.columns) {\n if (!columnNames.has(colName)) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\"`,\n );\n }\n }\n\n // Validate referenced table exists\n if (!tableNames.has(fk.references.table)) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\n }\n\n // Validate referenced columns exist in the referenced table\n const referencedTable = storage.tables[fk.references.table];\n if (!referencedTable) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent table \"${fk.references.table}\"`,\n );\n }\n const referencedColumnNames = new Set(Object.keys(referencedTable.columns));\n\n for (const colName of fk.references.columns) {\n if (!referencedColumnNames.has(colName)) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" foreignKey references non-existent column \"${colName}\" in table \"${fk.references.table}\"`,\n );\n }\n }\n\n if (fk.columns.length !== fk.references.columns.length) {\n /* c8 ignore next */\n throw new Error(\n `Table \"${tableName}\" foreignKey column count (${fk.columns.length}) does not match referenced column count (${fk.references.columns.length})`,\n );\n }\n }\n }\n}\n\nexport function normalizeContract(contract: unknown): SqlContract<SqlStorage> {\n const contractObj = contract as Record<string, unknown>;\n\n // Only normalize if storage exists (validation will catch if it's missing)\n let normalizedStorage = contractObj['storage'];\n if (normalizedStorage && typeof normalizedStorage === 'object' && normalizedStorage !== null) {\n const storage = normalizedStorage as Record<string, unknown>;\n const tables = storage['tables'] as Record<string, unknown> | undefined;\n\n if (tables) {\n // Normalize storage tables\n const normalizedTables: Record<string, unknown> = {};\n for (const [tableName, table] of Object.entries(tables)) {\n const tableObj = table as Record<string, unknown>;\n const columns = tableObj['columns'] as Record<string, unknown> | undefined;\n\n if (columns) {\n // Normalize columns: add nullable: false if missing\n const normalizedColumns: Record<string, unknown> = {};\n for (const [columnName, column] of Object.entries(columns)) {\n const columnObj = column as Record<string, unknown>;\n const normalizedColumn: Record<string, unknown> = {\n ...columnObj,\n nullable: columnObj['nullable'] ?? false,\n };\n\n normalizedColumns[columnName] = normalizedColumn;\n }\n\n // Normalize table arrays: add empty arrays if missing\n normalizedTables[tableName] = {\n ...tableObj,\n columns: normalizedColumns,\n uniques: tableObj['uniques'] ?? [],\n indexes: tableObj['indexes'] ?? [],\n foreignKeys: tableObj['foreignKeys'] ?? [],\n };\n } else {\n normalizedTables[tableName] = tableObj;\n }\n }\n\n normalizedStorage = {\n ...storage,\n tables: normalizedTables,\n };\n }\n }\n\n // Only normalize if models exists (validation will catch if it's missing)\n let normalizedModels = contractObj['models'];\n if (normalizedModels && typeof normalizedModels === 'object' && normalizedModels !== null) {\n const models = normalizedModels as Record<string, unknown>;\n const normalizedModelsObj: Record<string, unknown> = {};\n for (const [modelName, model] of Object.entries(models)) {\n const modelObj = model as Record<string, unknown>;\n normalizedModelsObj[modelName] = {\n ...modelObj,\n relations: modelObj['relations'] ?? {},\n };\n }\n normalizedModels = normalizedModelsObj;\n }\n\n // Normalize top-level fields: add empty objects if missing\n return {\n ...contractObj,\n models: normalizedModels,\n relations: contractObj['relations'] ?? {},\n storage: normalizedStorage,\n extensionPacks: contractObj['extensionPacks'] ?? {},\n capabilities: contractObj['capabilities'] ?? {},\n meta: contractObj['meta'] ?? {},\n sources: contractObj['sources'] ?? {},\n } as SqlContract<SqlStorage>;\n}\n\n/**\n * Validates that a JSON import conforms to the SqlContract structure\n * and returns a fully typed SqlContract.\n *\n * This function is specifically for validating JSON imports (e.g., from contract.json).\n * Contracts created via the builder API (defineContract) are already valid and should\n * not be passed to this function - use them directly without validation.\n *\n * Performs both structural validation (using Arktype) and logical validation\n * (ensuring all references are valid).\n *\n *\n * The type parameter `TContract` must be a fully-typed contract type (e.g., from `contract.d.ts`),\n * NOT a generic `SqlContract<SqlStorage>`.\n *\n * **Correct:**\n * ```typescript\n * import type { Contract } from './contract.d';\n * const contract = validateContract<Contract>(contractJson);\n * ```\n *\n * **Incorrect:**\n * ```typescript\n * import type { SqlContract, SqlStorage } from '@prisma-next/sql-contract/types';\n * const contract = validateContract<SqlContract<SqlStorage>>(contractJson);\n * // ❌ Types will be inferred as 'unknown' - this won't work!\n * ```\n *\n * The type parameter provides the specific table structure, column types, and model definitions.\n * This function validates the runtime structure matches the type, but does not infer types\n * from JSON (as JSON imports lose literal type information).\n *\n * @param value - The contract value to validate (must be from a JSON import, not a builder)\n * @returns A validated contract matching the TContract type\n * @throws Error if the contract structure or logic is invalid\n */\nexport function validateContract<TContract extends SqlContract<SqlStorage>>(\n value: unknown,\n): TContract {\n // Normalize contract first (add defaults for missing fields)\n const normalized = normalizeContract(value);\n\n const structurallyValid = validateContractStructure<SqlContract<SqlStorage>>(normalized);\n\n const contractForValidation = structurallyValid as SqlContract<SqlStorage>;\n\n // Validate contract logic (contracts must already have fully qualified type IDs)\n validateContractLogic(contractForValidation);\n\n // Extract existing mappings (optional - will be computed if missing)\n const existingMappings = (contractForValidation as { mappings?: Partial<SqlMappings> }).mappings;\n\n // Compute mappings from models and storage\n const mappings = computeMappings(\n contractForValidation.models as Record<string, ModelDefinition>,\n contractForValidation.storage,\n existingMappings,\n );\n\n // Add default values for optional metadata fields if missing\n const contractWithMappings = {\n ...structurallyValid,\n models: contractForValidation.models,\n relations: contractForValidation.relations,\n storage: contractForValidation.storage,\n mappings,\n };\n\n // Type assertion: The caller provides the strict type via TContract.\n // We validate the structure matches, but the precise types come from contract.d.ts\n return contractWithMappings as TContract;\n}\n"],"mappings":"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","names":[]}
|