@prisma-next/sql-contract 0.16.0-dev.25 → 0.16.0-dev.26

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.
Files changed (62) hide show
  1. package/dist/canonicalization-hooks.d.mts.map +1 -1
  2. package/dist/canonicalization-hooks.mjs +10 -0
  3. package/dist/canonicalization-hooks.mjs.map +1 -1
  4. package/dist/contract-view.d.mts +1 -1
  5. package/dist/{entity-kinds-La8Jg-jW.mjs → entity-kinds-CdHJQGi0.mjs} +9 -5
  6. package/dist/entity-kinds-CdHJQGi0.mjs.map +1 -0
  7. package/dist/entity-kinds.d.mts +1 -1
  8. package/dist/entity-kinds.mjs +1 -1
  9. package/dist/factories.d.mts +10 -4
  10. package/dist/factories.d.mts.map +1 -1
  11. package/dist/factories.mjs +11 -3
  12. package/dist/factories.mjs.map +1 -1
  13. package/dist/foreign-key-materialization.d.mts +7 -6
  14. package/dist/foreign-key-materialization.d.mts.map +1 -1
  15. package/dist/foreign-key-materialization.mjs +12 -9
  16. package/dist/foreign-key-materialization.mjs.map +1 -1
  17. package/dist/index-naming-DsnarUVW.mjs +43 -0
  18. package/dist/index-naming-DsnarUVW.mjs.map +1 -0
  19. package/dist/index-naming.d.mts +25 -0
  20. package/dist/index-naming.d.mts.map +1 -0
  21. package/dist/index-naming.mjs +2 -0
  22. package/dist/index-type-validation.d.mts +2 -2
  23. package/dist/index-type-validation.mjs +3 -3
  24. package/dist/index-type-validation.mjs.map +1 -1
  25. package/dist/resolve-storage-table.d.mts +2 -2
  26. package/dist/sql-index-Wzbj2nbH.d.mts +79 -0
  27. package/dist/sql-index-Wzbj2nbH.d.mts.map +1 -0
  28. package/dist/{sql-storage-BIErcfFA.d.mts → sql-storage-DjqU3XXV.d.mts} +2 -2
  29. package/dist/{sql-storage-BIErcfFA.d.mts.map → sql-storage-DjqU3XXV.d.mts.map} +1 -1
  30. package/dist/{storage-table-9O0ZNiMs.mjs → storage-table-DKTrkwZt.mjs} +28 -5
  31. package/dist/storage-table-DKTrkwZt.mjs.map +1 -0
  32. package/dist/{storage-table-mNGckl3_.d.mts → storage-table-zrxIuCJ3.d.mts} +3 -2
  33. package/dist/storage-table-zrxIuCJ3.d.mts.map +1 -0
  34. package/dist/{storage-value-set-DiGJT-LT.mjs → storage-value-set-DdL4oin-.mjs} +2 -2
  35. package/dist/{storage-value-set-DiGJT-LT.mjs.map → storage-value-set-DdL4oin-.mjs.map} +1 -1
  36. package/dist/{types-lpzJtKpv.d.mts → types-Bxa1Pyzg.d.mts} +5 -4
  37. package/dist/types-Bxa1Pyzg.d.mts.map +1 -0
  38. package/dist/types.d.mts +5 -4
  39. package/dist/types.d.mts.map +1 -1
  40. package/dist/types.mjs +2 -2
  41. package/dist/unique-constraint-Bp-q-mr-.d.mts +31 -0
  42. package/dist/unique-constraint-Bp-q-mr-.d.mts.map +1 -0
  43. package/dist/validators.d.mts +15 -7
  44. package/dist/validators.d.mts.map +1 -1
  45. package/dist/validators.mjs +8 -5
  46. package/dist/validators.mjs.map +1 -1
  47. package/package.json +9 -8
  48. package/src/canonicalization-hooks.ts +4 -0
  49. package/src/exports/index-naming.ts +1 -0
  50. package/src/factories.ts +19 -2
  51. package/src/foreign-key-materialization.ts +18 -11
  52. package/src/index-naming.ts +65 -0
  53. package/src/index-type-validation.ts +3 -3
  54. package/src/ir/sql-index.ts +94 -11
  55. package/src/ir/storage-entry-schemas.ts +6 -2
  56. package/src/validators.ts +7 -4
  57. package/dist/entity-kinds-La8Jg-jW.mjs.map +0 -1
  58. package/dist/storage-table-9O0ZNiMs.mjs.map +0 -1
  59. package/dist/storage-table-mNGckl3_.d.mts.map +0 -1
  60. package/dist/types-lpzJtKpv.d.mts.map +0 -1
  61. package/dist/unique-constraint-WHfanEUq.d.mts +0 -54
  62. package/dist/unique-constraint-WHfanEUq.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"validators.mjs","names":["f","referencedTable"],"sources":["../src/validators.ts"],"sourcesContent":["import { ContractValidationError } from '@prisma-next/contract/contract-validation-error';\nimport {\n type Contract,\n type ContractField,\n type ContractModel,\n CrossReferenceSchema,\n} from '@prisma-next/contract/types';\nimport { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport {\n type AnyEntityKindDescriptor,\n isPlainRecord,\n type Namespace,\n} from '@prisma-next/framework-components/ir';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { type Type, type } from 'arktype';\nimport { contractError } from './contract-errors';\nimport { composeSqlEntityKinds } from './entity-kinds';\n\nexport {\n CheckConstraintSchema,\n ColumnDefaultFunctionSchema,\n ColumnDefaultLiteralSchema,\n ColumnDefaultSchema,\n ForeignKeyReferenceSchema,\n ForeignKeySchema,\n ForeignKeySourceSchema,\n IndexSchema,\n ReferentialActionSchema,\n StorageTableSchema,\n StorageValueSetSchema,\n} from './ir/storage-entry-schemas';\n\nimport type {\n SqlModelStorage,\n SqlStorage,\n StorageColumn,\n StorageTable,\n StorageTypeInstanceInput,\n} from './types';\n\nconst generatorKindSchema = type(\"'generator'\");\nconst ControlPolicySchema = type(\"'managed' | 'tolerated' | 'external' | 'observed'\");\nconst generatorIdSchema = type('string').narrow((value, ctx) => {\n return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value) ? true : ctx.mustBe('a flat generator id');\n});\n\nconst ExecutionMutationDefaultValueSchema = type({\n '+': 'reject',\n kind: generatorKindSchema,\n id: generatorIdSchema,\n 'params?': 'Record<string, unknown>',\n});\n\nconst ExecutionMutationDefaultSchema = type({\n '+': 'reject',\n ref: {\n '+': 'reject',\n namespace: 'string',\n table: 'string',\n column: 'string',\n },\n 'onCreate?': ExecutionMutationDefaultValueSchema,\n 'onUpdate?': ExecutionMutationDefaultValueSchema,\n});\n\nconst ExecutionSchema = type({\n '+': 'reject',\n executionHash: 'string',\n mutations: {\n '+': 'reject',\n defaults: ExecutionMutationDefaultSchema.array().readonly(),\n },\n});\n\nconst DomainEnumRefSchema = type({\n plane: \"'domain'\",\n namespaceId: 'string',\n entityKind: \"'enum'\",\n entityName: 'string',\n 'spaceId?': 'string',\n});\n\n/**\n * Codec-triple entry persisted under `storage.types[name]`. Carries an\n * enumerable literal `kind: 'codec-instance'` discriminator so the\n * polymorphic slot dispatch can distinguish codec triples from\n * class-instance kinds (e.g. `'postgres-enum'`) sharing the slot.\n */\nconst StorageTypeInstanceSchema = type\n .declare<StorageTypeInstanceInput & { kind: 'codec-instance' }>()\n .type({\n kind: \"'codec-instance'\",\n codecId: 'string',\n nativeType: 'string',\n 'typeParams?': 'Record<string, unknown>',\n });\n\n/** Document-scoped `storage.types`: codec triples only. */\nconst DocumentScopedStorageTypeSchema = StorageTypeInstanceSchema;\n\n/**\n * Domain enum entry under `domain.namespaces[id].enum[name]`.\n * Carries the codec id and an ordered `members` array of `{name, value}` pairs.\n */\nexport const ContractEnumSchema = type({\n '+': 'reject',\n codecId: 'string',\n members: type({\n name: 'string',\n value: 'string | number | boolean | null | unknown[] | Record<string, unknown>',\n })\n .array()\n .readonly(),\n});\n\n/**\n * Derives a schema map from a descriptor map: maps each kind's key to its\n * `schema` field. Used by validation functions to validate entries.\n */\nfunction schemaViewOf(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): ReadonlyMap<string, Type<unknown>> {\n return new Map([...kinds].map(([k, d]) => [k, d.schema]));\n}\n\nconst DEFAULT_SQL_KINDS = composeSqlEntityKinds();\n\n/**\n * Builds the per-namespace entry schema for `storage.namespaces[id]`.\n *\n * Validation is descriptor-driven: the `kinds` map carries both the schema\n * (used here for structural validation) and the construct function (used at\n * hydration time). An unregistered key fails validation naming the kind and\n * the namespace id, so validation fails closed.\n */\nexport function createNamespaceEntrySchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const schemas = schemaViewOf(kinds);\n const knownKinds = new Set(kinds.keys());\n return type({\n '+': 'reject',\n id: 'string',\n 'kind?': 'string',\n entries: 'object',\n }).narrow((ns, ctx) => {\n if (!isPlainRecord(ns.entries)) {\n return ctx.mustBe('an entries object');\n }\n for (const [key, innerMap] of Object.entries(ns.entries)) {\n if (!knownKinds.has(key)) {\n return ctx.reject({\n expected: `entries key \"${key}\" in namespace \"${ns.id}\" is not a registered entity kind`,\n });\n }\n if (!isPlainRecord(innerMap)) {\n return ctx.reject({\n expected: `entries[\"${key}\"] in namespace \"${ns.id}\" must be an object`,\n });\n }\n const entrySchema = blindCast<\n Type<unknown>,\n 'knownKinds.has(key) guarantees schemas.get(key) is defined'\n >(schemas.get(key));\n for (const [, value] of Object.entries(innerMap)) {\n const parsed = entrySchema(value);\n if (parsed instanceof type.errors) {\n return ctx.reject({ expected: parsed.summary });\n }\n }\n }\n return true;\n }) as Type<unknown>;\n}\n\n/**\n * Builds the storage schema. Pack contributions reach the per-namespace\n * entry shape through {@link createNamespaceEntrySchema}; the\n * document-scoped `storage.types` field (codec triples only) and the\n * storage hash stay family-shared.\n */\nexport function createSqlStorageSchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const namespaceEntry = createNamespaceEntrySchema(kinds);\n return type({\n '+': 'reject',\n storageHash: 'string',\n 'types?': type({ '[string]': DocumentScopedStorageTypeSchema }),\n // `__unbound__` is NOT required here: cross-namespace contracts can\n // declare only named namespaces (see cross-namespace FK fixtures). The\n // unbound slot is injected when absent by `ensureUnboundNamespaceSlot`\n // in `build-contract.ts`, not enforced here structurally.\n 'namespaces?': type({ '[string]': namespaceEntry }),\n }) as Type<unknown>;\n}\n\nconst StorageSchema = createSqlStorageSchema(DEFAULT_SQL_KINDS);\n\ntype NamespacedStorageWalk = {\n readonly namespaces: Readonly<\n Record<\n string,\n Namespace & { readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>> }\n >\n >;\n};\n\nfunction eachStorageTable(storage: NamespacedStorageWalk) {\n return Object.entries(storage.namespaces).flatMap(([namespaceId, ns]) =>\n Object.entries(ns.entries['table'] ?? {}).map(([tableName, table]) => ({\n namespaceId,\n tableName,\n table,\n })),\n );\n}\n\nfunction findDuplicateValue(values: readonly string[]): string | undefined {\n const seen = new Set<string>();\n for (const value of values) {\n if (seen.has(value)) {\n return value;\n }\n seen.add(value);\n }\n return undefined;\n}\n\nfunction isContractFieldType(value: unknown): boolean {\n if (!isPlainRecord(value)) return false;\n const kind = value['kind'];\n if (kind === 'scalar') {\n if (typeof value['codecId'] !== 'string') return false;\n const typeParams = value['typeParams'];\n if (typeParams !== undefined && !isPlainRecord(typeParams)) return false;\n return true;\n }\n if (kind === 'valueObject') {\n return typeof value['name'] === 'string';\n }\n if (kind === 'union') {\n const members = value['members'];\n if (!Array.isArray(members)) return false;\n return members.every((m) => isContractFieldType(m));\n }\n return false;\n}\n\nconst ContractFieldTypeSchema = type('unknown').narrow((value, ctx) =>\n isContractFieldType(value) ? true : ctx.mustBe('scalar, valueObject, or union field type'),\n);\n\nconst ModelFieldSchema = type({\n '+': 'reject',\n nullable: 'boolean',\n type: ContractFieldTypeSchema,\n 'many?': 'true',\n 'dict?': 'true',\n 'valueSet?': DomainEnumRefSchema,\n});\n\nconst ModelStorageFieldSchema = type({\n column: 'string',\n 'codecId?': 'string',\n 'nullable?': 'boolean',\n});\n\nconst ModelStorageSchema = type({\n table: 'string',\n namespaceId: 'string',\n fields: type({ '[string]': ModelStorageFieldSchema }),\n});\n\nconst ContractRelationThroughSchema = type({\n '+': 'reject',\n table: 'string',\n namespaceId: 'string',\n parentColumns: type.string.array().readonly(),\n childColumns: type.string.array().readonly(),\n targetColumns: type.string.array().readonly(),\n});\n\nconst ContractRelationOnSchema = type({\n '+': 'reject',\n localFields: type.string.array().readonly(),\n targetFields: type.string.array().readonly(),\n});\n\nconst ContractManyToManyRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'N:M'\",\n on: ContractRelationOnSchema,\n through: ContractRelationThroughSchema,\n});\n\nconst ContractNonJunctionRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'1:1' | '1:N' | 'N:1'\",\n on: ContractRelationOnSchema,\n});\n\nconst ContractReferenceRelationSchema = ContractManyToManyRelationSchema.or(\n ContractNonJunctionRelationSchema,\n);\n\nconst ContractEmbedRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'1:1' | '1:N'\",\n});\n\nconst ContractRelationSchema = ContractReferenceRelationSchema.or(ContractEmbedRelationSchema);\n\nconst ModelSchema = type({\n storage: ModelStorageSchema,\n 'fields?': type({ '[string]': ModelFieldSchema }),\n 'relations?': type({ '[string]': ContractRelationSchema }),\n 'discriminator?': 'unknown',\n 'variants?': 'unknown',\n 'base?': CrossReferenceSchema,\n 'owner?': 'string',\n});\n\nconst ContractMetaSchema = type({\n '[string]': 'unknown',\n});\n\n/**\n * Builds the full SQL contract schema. The storage subtree threads\n * pack contributions through {@link createSqlStorageSchema}; the rest\n * of the contract envelope is family-shared.\n */\nexport function createSqlContractSchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const storage = createSqlStorageSchema(kinds);\n return type({\n '+': 'reject',\n target: 'string',\n targetFamily: \"'sql'\",\n 'coreHash?': 'string',\n profileHash: 'string',\n 'capabilities?': 'Record<string, Record<string, boolean>>',\n 'extensions?': 'Record<string, unknown>',\n 'meta?': ContractMetaSchema,\n 'defaultControlPolicy?': ControlPolicySchema,\n 'roots?': type({ '[string]': CrossReferenceSchema }),\n domain: type({\n namespaces: type({\n '[string]': type({\n models: type({ '[string]': ModelSchema }),\n 'valueObjects?': 'Record<string, unknown>',\n 'enum?': type({ '[string]': ContractEnumSchema }),\n }),\n }),\n }),\n storage,\n 'execution?': ExecutionSchema,\n }) as Type<unknown>;\n}\n\nconst SqlContractSchema = createSqlContractSchema(DEFAULT_SQL_KINDS);\n\n/**\n * Validates the structural shape of SqlStorage using Arktype. Pure\n * structural check: namespace IR is never materialized here (that needs\n * a target concretion via the serializer hydration path), so this throws\n * on invalid input and constructs nothing.\n *\n * @param value - The storage value to validate\n * @throws Error if the storage structure is invalid\n */\nexport function validateStorage(value: unknown): void {\n const result = StorageSchema(value);\n if (result instanceof type.errors) {\n const errors = result.map((p: { message: string }) => p.message);\n throw contractError(\n 'CONTRACT.VALIDATION_FAILED',\n `Storage validation failed: ${errors.join('; ')}`,\n {\n meta: { errors },\n },\n );\n }\n}\n\nexport function validateModel(value: unknown): unknown {\n const result = ModelSchema(value);\n if (result instanceof type.errors) {\n const errors = result.map((p: { message: string }) => p.message);\n throw contractError(\n 'CONTRACT.VALIDATION_FAILED',\n `Model validation failed: ${errors.join('; ')}`,\n {\n meta: { errors },\n },\n );\n }\n return result;\n}\n\n/**\n * Structural arktype validation of an SQL contract envelope. Internal\n * helper for {@link validateSqlContractFully} — exposed only inside\n * this module, since the family seam-of-record is the\n * `SqlContractSerializerBase.deserializeContract` SPI.\n */\nfunction validateSqlContractStructure<T extends Contract<SqlStorage>>(\n value: unknown,\n contractSchema: Type<unknown>,\n): T {\n if (typeof value !== 'object' || value === null) {\n throw new ContractValidationError(\n 'Contract structural validation failed: value must be an object',\n 'structural',\n );\n }\n\n const rawValue = value as { targetFamily?: string };\n if (rawValue.targetFamily !== undefined && rawValue.targetFamily !== 'sql') {\n throw new ContractValidationError(\n `Unsupported target family: ${rawValue.targetFamily}`,\n 'structural',\n );\n }\n\n const contractResult = contractSchema(value);\n\n if (contractResult instanceof type.errors) {\n const messages = contractResult.map((p: { message: string }) => p.message).join('; ');\n throw new ContractValidationError(\n `Contract structural validation failed: ${messages}`,\n 'structural',\n );\n }\n\n // Arktype's inferred output type differs from T due to exactOptionalPropertyTypes\n // and branded hash types — the runtime value is structurally compatible after validation\n return contractResult as unknown as T;\n}\n\n/**\n * Validates semantic constraints on SqlStorage that cannot be expressed in Arktype schemas.\n *\n * Returns an array of human-readable error strings. Empty array = valid.\n *\n * Currently checks:\n * - duplicate named primary key / unique / index / foreign key objects within a table\n * - duplicate unique, index, or foreign key declarations within a table\n * - duplicate columns within primary key / unique / index definitions\n * - nullable columns in primary key definitions\n * - `setNull` referential action on a non-nullable FK column (would fail at runtime)\n * - `setDefault` referential action on a non-nullable FK column without a DEFAULT (would fail at runtime)\n */\nexport function validateStorageSemantics(storage: SqlStorage): string[] {\n const errors: string[] = [];\n\n for (const { namespaceId, tableName, table: rawTable } of eachStorageTable(storage)) {\n const table = rawTable as StorageTable;\n const namedObjects = new Map<string, string[]>();\n const registerNamedObject = (kind: string, name: string | undefined) => {\n if (!name) return;\n namedObjects.set(name, [...(namedObjects.get(name) ?? []), kind]);\n };\n\n registerNamedObject('primary key', table.primaryKey?.name);\n for (const unique of table.uniques) {\n registerNamedObject('unique constraint', unique.name);\n }\n for (const index of table.indexes) {\n registerNamedObject('index', index.name);\n }\n for (const fk of table.foreignKeys) {\n registerNamedObject('foreign key', fk.name);\n }\n for (const check of table.checks ?? []) {\n registerNamedObject('check constraint', check.name);\n }\n\n for (const [name, kinds] of namedObjects) {\n if (kinds.length > 1) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": named object \"${name}\" is declared multiple times (${kinds.join(', ')})`,\n );\n }\n }\n\n if (table.primaryKey) {\n const duplicateColumn = findDuplicateValue(table.primaryKey.columns);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": primary key contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n for (const columnName of table.primaryKey.columns) {\n const column = table.columns[columnName];\n if (column?.nullable === true) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": primary key column \"${columnName}\" is nullable; primary key columns must be NOT NULL`,\n );\n }\n }\n }\n\n const seenUniqueDefinitions = new Set<string>();\n for (const unique of table.uniques) {\n const duplicateColumn = findDuplicateValue(unique.columns);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": unique constraint contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n const signature = JSON.stringify({ columns: unique.columns });\n if (seenUniqueDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate unique constraint definition on columns [${unique.columns.join(', ')}]`,\n );\n continue;\n }\n seenUniqueDefinitions.add(signature);\n }\n\n const sortOptions = (o: Record<string, unknown> | undefined): Record<string, unknown> | null =>\n o ? Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b))) : null;\n\n const seenIndexDefinitions = new Set<string>();\n for (const index of table.indexes) {\n const duplicateColumn = findDuplicateValue(index.columns);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": index contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n const signature = JSON.stringify({\n columns: index.columns,\n type: index.type ?? null,\n options: sortOptions(index.options),\n });\n if (seenIndexDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate index definition on columns [${index.columns.join(', ')}]`,\n );\n continue;\n }\n seenIndexDefinitions.add(signature);\n }\n\n const seenForeignKeyDefinitions = new Set<string>();\n for (const fk of table.foreignKeys) {\n const signature = JSON.stringify({\n source: fk.source,\n target: fk.target,\n onDelete: fk.onDelete ?? null,\n onUpdate: fk.onUpdate ?? null,\n });\n if (seenForeignKeyDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate foreign key definition on columns [${fk.source.columns.join(', ')}]`,\n );\n continue;\n }\n seenForeignKeyDefinitions.add(signature);\n }\n\n for (const fk of table.foreignKeys) {\n for (const colName of fk.source.columns) {\n const column = table.columns[colName];\n if (!column) continue;\n\n if (fk.onDelete === 'setNull' && !column.nullable) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onDelete setNull on foreign key column \"${colName}\" which is NOT NULL`,\n );\n }\n if (fk.onUpdate === 'setNull' && !column.nullable) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onUpdate setNull on foreign key column \"${colName}\" which is NOT NULL`,\n );\n }\n if (fk.onDelete === 'setDefault' && !column.nullable && column.default === undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onDelete setDefault on foreign key column \"${colName}\" which is NOT NULL and has no DEFAULT`,\n );\n }\n if (fk.onUpdate === 'setDefault' && !column.nullable && column.default === undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onUpdate setDefault on foreign key column \"${colName}\" which is NOT NULL and has no DEFAULT`,\n );\n }\n }\n }\n\n const seenCheckDefinitions = new Set<string>();\n for (const check of table.checks ?? []) {\n const signature = JSON.stringify({ column: check.column, valueSet: check.valueSet });\n if (seenCheckDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate check constraint definition on column \"${check.column}\"`,\n );\n continue;\n }\n seenCheckDefinitions.add(signature);\n }\n }\n\n return errors;\n}\n\n/**\n * SQL storage logical-consistency checks: every model.storage.table\n * resolves to a real table, every model.storage.fields[*].column\n * resolves to a real column, and value-object fields land on JSON-native\n * columns. Throws `ContractValidationError` on the first mismatch.\n */\nexport function validateModelStorageReferences(contract: Contract<SqlStorage>): void {\n for (const [namespaceId, namespace] of Object.entries(contract.domain.namespaces)) {\n const models = namespace.models as Record<string, ContractModel<SqlModelStorage>>;\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n const storageNamespaceId = model.storage.namespaceId;\n if (storageNamespaceId !== namespaceId) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" storage.namespaceId \"${storageNamespaceId}\" does not match domain namespace \"${namespaceId}\"`,\n 'storage',\n );\n }\n\n const storageTable = model.storage.table;\n const storageNs = contract.storage.namespaces[storageNamespaceId];\n const rawTable = storageNs?.entries.table?.[storageTable];\n if (rawTable === undefined) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" references non-existent table \"${storageNamespaceId}.${storageTable}\"`,\n 'storage',\n );\n }\n\n const table = rawTable as StorageTable;\n\n const columnNames = new Set(Object.keys(table.columns));\n for (const [fieldName, field] of Object.entries(model.storage.fields)) {\n if (!columnNames.has(field.column)) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" field \"${fieldName}\" references non-existent column \"${field.column}\" in table \"${storageTable}\"`,\n 'storage',\n );\n }\n }\n\n const JSON_NATIVE_TYPES = new Set(['json', 'jsonb']);\n for (const [fieldName, domainField] of Object.entries(model.fields ?? {})) {\n const f = domainField as ContractField;\n if (f.type?.kind !== 'valueObject') continue;\n const storageField = model.storage.fields[fieldName];\n if (!storageField) continue;\n const column = table.columns[storageField.column];\n if (!column) continue;\n if (!JSON_NATIVE_TYPES.has(column.nativeType)) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" field \"${fieldName}\" is a value object but storage column \"${storageField.column}\" has nativeType \"${column.nativeType}\" (expected json or jsonb)`,\n 'storage',\n );\n }\n }\n }\n }\n}\n\n/**\n * Cross-table consistency checks for SQL storage: primary key, unique,\n * index, and foreign key column references resolve to real columns;\n * NOT NULL columns don't carry a literal `null` default; FK column\n * counts match their referenced columns. Throws on the first mismatch.\n */\nexport function validateSqlStorageConsistency(contract: Contract<SqlStorage>): void {\n for (const { namespaceId, tableName, table: rawTable } of eachStorageTable(contract.storage)) {\n const table = rawTable as StorageTable;\n const columnNames = new Set(Object.keys(table.columns));\n\n if (table.primaryKey) {\n for (const colName of table.primaryKey.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" primaryKey references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const unique of table.uniques) {\n for (const colName of unique.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" unique constraint references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const index of table.indexes) {\n for (const colName of index.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" index references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const check of table.checks ?? []) {\n if (!columnNames.has(check.column)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" check constraint \"${check.name}\" references non-existent column \"${check.column}\"`,\n 'storage',\n );\n }\n }\n\n for (const [colName, column] of Object.entries(table.columns)) {\n if (!column.nullable && column.default?.kind === 'literal' && column.default.value === null) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" column \"${colName}\" is NOT NULL but has a literal null default`,\n 'storage',\n );\n }\n }\n\n for (const fk of table.foreignKeys) {\n if (fk.source.namespaceId !== namespaceId || fk.source.tableName !== tableName) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" contains foreignKey with mismatched source coordinates (${fk.source.namespaceId}.${fk.source.tableName})`,\n 'storage',\n );\n }\n\n for (const colName of fk.source.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n\n if (fk.target.spaceId === undefined) {\n const targetNamespace = contract.storage.namespaces[fk.target.namespaceId];\n const referencedRaw = targetNamespace?.entries.table?.[fk.target.tableName];\n if (referencedRaw === undefined) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent table \"${fk.target.namespaceId}.${fk.target.tableName}\"`,\n 'storage',\n );\n }\n const referencedTable = referencedRaw as StorageTable;\n const referencedColumnNames = new Set(Object.keys(referencedTable.columns));\n for (const colName of fk.target.columns) {\n if (!referencedColumnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent column \"${colName}\" in table \"${fk.target.tableName}\"`,\n 'storage',\n );\n }\n }\n }\n\n if (fk.source.columns.length !== fk.target.columns.length) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey column count (${fk.source.columns.length}) does not match referenced column count (${fk.target.columns.length})`,\n 'storage',\n );\n }\n }\n }\n}\n\nexport interface ValidateSqlContractFullyOptions {\n /**\n * Precomputed structural schema to validate against. Built once at\n * serializer construction time when the family `ContractSerializer`\n * has folded pack-contributed `validatorSchema` fragments into the\n * per-namespace entry shape; absent for the family-default validator\n * path (no pack contributions). Falls back to the cached default\n * `SqlContractSchema` when omitted.\n */\n readonly contractSchema?: Type<unknown>;\n}\n\n/**\n * Full SQL contract validation: structural (arktype) +\n * framework-shared domain + SQL storage logical-consistency + SQL\n * storage semantic + model ↔ storage reference checks. Throws\n * `ContractValidationError` on the first failure. Returns the\n * validated flat-data shape; IR class hydration happens in the SPI\n * base on top of this helper.\n */\nexport function validateSqlContractFully<T extends Contract<SqlStorage>>(\n value: unknown,\n options?: ValidateSqlContractFullyOptions,\n): T {\n const stripped =\n typeof value === 'object' && value !== null\n ? (() => {\n const { schemaVersion: _, _generated: _g, ...rest } = value as Record<string, unknown>;\n return rest;\n })()\n : value;\n const schema = options?.contractSchema ?? SqlContractSchema;\n const validated = validateSqlContractStructure<T>(stripped, schema);\n validateContractDomain({\n roots: validated.roots,\n domain: validated.domain,\n });\n validateSqlStorageConsistency(validated);\n const semanticErrors = validateStorageSemantics(validated.storage);\n if (semanticErrors.length > 0) {\n throw new ContractValidationError(\n `Contract semantic validation failed: ${semanticErrors.join('; ')}`,\n 'storage',\n );\n }\n validateModelStorageReferences(validated);\n validateRelationThroughConsistency(validated);\n return validated;\n}\n\n/** Storage column lookup for through-consistency validation. */\nfunction lookupStorageColumn(\n contract: Contract<SqlStorage>,\n namespaceId: string,\n tableName: string,\n columnName: string,\n): StorageColumn | undefined {\n const rawTable = contract.storage.namespaces[namespaceId]?.entries.table?.[tableName];\n if (rawTable === undefined) {\n return undefined;\n }\n const table = blindCast<StorageTable, 'structurally validated storage table'>(rawTable);\n return table.columns[columnName];\n}\n\n/**\n * Two storage columns share a type when their `nativeType` and `typeParams`\n * match. The contract is canonicalized, so `typeParams` key order is stable and\n * a JSON comparison is exact. `codecId` and `nullable` are intentionally not\n * compared: they do not change the database-level type that governs a join.\n */\nfunction sameStorageType(a: StorageColumn, b: StorageColumn): boolean {\n return (\n a.nativeType === b.nativeType &&\n JSON.stringify(a.typeParams ?? null) === JSON.stringify(b.typeParams ?? null)\n );\n}\n\nfunction describeColumnType(column: StorageColumn): string {\n return column.typeParams === undefined\n ? column.nativeType\n : `${column.nativeType} ${JSON.stringify(column.typeParams)}`;\n}\n\n/**\n * Validates one side of an N:M join: the junction columns and the model\n * columns they pair against positionally must be equal in number, exist in\n * their tables, and share the same storage type (`nativeType` + `typeParams`).\n * The junction's storage foreign keys already guarantee this for user-declared\n * FK constraints, but `through` is a logical descriptor never tied to them by\n * the rest of validation — and the TS builder accepts explicit join columns\n * without requiring a junction FK at all — so this checks the columns directly\n * against storage, one path regardless of how the junction was authored.\n *\n * Joined columns must be the *same* storage type, not merely compatible:\n * relying on implicit conversion (e.g. `text`↔`character`) is unsafe on writes\n * — `character(n)` space-padding makes such coercions non-associative — and no\n * ADR sanctions heterogeneous junction columns. Equality is the conservative\n * default; it can be relaxed deliberately if a real use case ever appears.\n */\nfunction validateThroughJoinSide(input: {\n readonly contract: Contract<SqlStorage>;\n readonly qualifiedName: string;\n readonly modelColumns: readonly string[];\n readonly modelColumnsLabel: string;\n /**\n * The model side of the join, when resolvable. Absent for a cross-space\n * target whose storage lives in another contract: the length and\n * junction-column-existence checks still run, but the target-column\n * existence and type checks are skipped because the target table is not\n * available here. `fieldToColumn` is present when `modelColumns` are domain\n * field names (the `on.*Fields` arrays), absent when they are already storage\n * column names (the `through.*Columns` arrays).\n */\n readonly model?: {\n readonly namespaceId: string;\n readonly table: string;\n readonly fieldToColumn?: Readonly<Record<string, { readonly column: string }>>;\n };\n readonly junctionColumns: readonly string[];\n readonly junctionColumnsLabel: string;\n readonly junctionNamespaceId: string;\n readonly junctionTable: string;\n}): void {\n const fail = (detail: string): ContractValidationError =>\n new ContractValidationError(\n `Many-to-many relation \"${input.qualifiedName}\" ${detail}`,\n 'storage',\n );\n if (input.junctionColumns.length !== input.modelColumns.length) {\n throw fail(\n `pairs ${input.junctionColumnsLabel} (${input.junctionColumns.length}) with ${input.modelColumnsLabel} (${input.modelColumns.length}) of differing length; they join positionally and must match.`,\n );\n }\n for (const [index, junctionColumnName] of input.junctionColumns.entries()) {\n const modelColumnRef = input.modelColumns[index];\n if (modelColumnRef === undefined) {\n continue;\n }\n const junctionColumn = lookupStorageColumn(\n input.contract,\n input.junctionNamespaceId,\n input.junctionTable,\n junctionColumnName,\n );\n if (junctionColumn === undefined) {\n throw fail(\n `${input.junctionColumnsLabel} references column \"${junctionColumnName}\" absent from junction table \"${input.junctionNamespaceId}.${input.junctionTable}\".`,\n );\n }\n // Cross-space target: its storage lives in another contract, so the target\n // column's existence and type cannot be checked here. Length and\n // junction-column existence have already been validated above.\n const model = input.model;\n if (model === undefined) {\n continue;\n }\n let modelColumnName = modelColumnRef;\n if (model.fieldToColumn !== undefined) {\n const mapped = model.fieldToColumn[modelColumnRef];\n if (mapped === undefined) {\n throw fail(\n `${input.modelColumnsLabel} references field \"${modelColumnRef}\" absent from model on table \"${model.namespaceId}.${model.table}\".`,\n );\n }\n modelColumnName = mapped.column;\n }\n const modelColumn = lookupStorageColumn(\n input.contract,\n model.namespaceId,\n model.table,\n modelColumnName,\n );\n if (modelColumn === undefined) {\n throw fail(\n `${input.modelColumnsLabel} references column \"${modelColumnName}\" absent from table \"${model.namespaceId}.${model.table}\".`,\n );\n }\n if (!sameStorageType(junctionColumn, modelColumn)) {\n throw fail(\n `joins \"${input.junctionTable}.${junctionColumnName}\" (${describeColumnType(junctionColumn)}) with \"${model.table}.${modelColumnName}\" (${describeColumnType(modelColumn)}) of differing storage type; junction columns must match the type of the column they reference.`,\n );\n }\n }\n}\n\n/**\n * Validates that every N:M relation's `through` descriptor is consistent with\n * the storage columns it joins: both join sides match in column count,\n * reference columns that exist in their tables, and pair columns of the same\n * storage type. Without this, a `through` that disagrees with storage surfaces\n * as a silently wrong JOIN at query time rather than a validation error here.\n */\nfunction validateRelationThroughConsistency(contract: Contract<SqlStorage>): void {\n for (const [namespaceId, namespace] of Object.entries(contract.domain.namespaces)) {\n for (const [modelName, model] of Object.entries(namespace.models)) {\n for (const [relationName, relation] of Object.entries(model.relations)) {\n if (relation.cardinality !== 'N:M') continue;\n const qualifiedName = `${namespaceId}.${modelName}.${relationName}`;\n const { on, through } = relation;\n const modelStorage = blindCast<SqlModelStorage, 'SQL contract model storage'>(\n model.storage,\n );\n // Parent side: the owning model's localFields (domain field names) join\n // the junction's parentColumns (storage columns).\n validateThroughJoinSide({\n contract,\n qualifiedName,\n modelColumns: on.localFields,\n modelColumnsLabel: 'on.localFields',\n model: {\n namespaceId,\n table: modelStorage.table,\n fieldToColumn: modelStorage.fields,\n },\n junctionColumns: through.parentColumns,\n junctionColumnsLabel: 'through.parentColumns',\n junctionNamespaceId: through.namespaceId,\n junctionTable: through.table,\n });\n // Child side: the junction's childColumns join the target model's\n // targetColumns. Length and junction-column existence are checked\n // regardless; a cross-space target lives in another contract, so its\n // column existence and type are checked only when it is resolvable here.\n const targetModel =\n relation.to.space === undefined\n ? contract.domain.namespaces[relation.to.namespace]?.models[relation.to.model]\n : undefined;\n const targetModelSide =\n targetModel === undefined\n ? undefined\n : {\n namespaceId: relation.to.namespace,\n table: blindCast<SqlModelStorage, 'SQL contract model storage'>(targetModel.storage)\n .table,\n };\n validateThroughJoinSide({\n contract,\n qualifiedName,\n modelColumns: through.targetColumns,\n modelColumnsLabel: 'through.targetColumns',\n ...ifDefined('model', targetModelSide),\n junctionColumns: through.childColumns,\n junctionColumnsLabel: 'through.childColumns',\n junctionNamespaceId: through.namespaceId,\n junctionTable: through.table,\n });\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"validators.mjs","names":["f","referencedTable"],"sources":["../src/validators.ts"],"sourcesContent":["import { ContractValidationError } from '@prisma-next/contract/contract-validation-error';\nimport {\n type Contract,\n type ContractField,\n type ContractModel,\n CrossReferenceSchema,\n} from '@prisma-next/contract/types';\nimport { validateContractDomain } from '@prisma-next/contract/validate-domain';\nimport {\n type AnyEntityKindDescriptor,\n isPlainRecord,\n type Namespace,\n} from '@prisma-next/framework-components/ir';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { type Type, type } from 'arktype';\nimport { contractError } from './contract-errors';\nimport { composeSqlEntityKinds } from './entity-kinds';\n\nexport {\n CheckConstraintSchema,\n ColumnDefaultFunctionSchema,\n ColumnDefaultLiteralSchema,\n ColumnDefaultSchema,\n ForeignKeyReferenceSchema,\n ForeignKeySchema,\n ForeignKeySourceSchema,\n IndexSchema,\n ReferentialActionSchema,\n StorageTableSchema,\n StorageValueSetSchema,\n} from './ir/storage-entry-schemas';\n\nimport type {\n SqlModelStorage,\n SqlStorage,\n StorageColumn,\n StorageTable,\n StorageTypeInstanceInput,\n} from './types';\n\nconst generatorKindSchema = type(\"'generator'\");\nconst ControlPolicySchema = type(\"'managed' | 'tolerated' | 'external' | 'observed'\");\nconst generatorIdSchema = type('string').narrow((value, ctx) => {\n return /^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(value) ? true : ctx.mustBe('a flat generator id');\n});\n\nconst ExecutionMutationDefaultValueSchema = type({\n '+': 'reject',\n kind: generatorKindSchema,\n id: generatorIdSchema,\n 'params?': 'Record<string, unknown>',\n});\n\nconst ExecutionMutationDefaultSchema = type({\n '+': 'reject',\n ref: {\n '+': 'reject',\n namespace: 'string',\n table: 'string',\n column: 'string',\n },\n 'onCreate?': ExecutionMutationDefaultValueSchema,\n 'onUpdate?': ExecutionMutationDefaultValueSchema,\n});\n\nconst ExecutionSchema = type({\n '+': 'reject',\n executionHash: 'string',\n mutations: {\n '+': 'reject',\n defaults: ExecutionMutationDefaultSchema.array().readonly(),\n },\n});\n\nconst DomainEnumRefSchema = type({\n plane: \"'domain'\",\n namespaceId: 'string',\n entityKind: \"'enum'\",\n entityName: 'string',\n 'spaceId?': 'string',\n});\n\n/**\n * Codec-triple entry persisted under `storage.types[name]`. Carries an\n * enumerable literal `kind: 'codec-instance'` discriminator so the\n * polymorphic slot dispatch can distinguish codec triples from\n * class-instance kinds (e.g. `'postgres-enum'`) sharing the slot.\n */\nconst StorageTypeInstanceSchema = type\n .declare<StorageTypeInstanceInput & { kind: 'codec-instance' }>()\n .type({\n kind: \"'codec-instance'\",\n codecId: 'string',\n nativeType: 'string',\n 'typeParams?': 'Record<string, unknown>',\n });\n\n/** Document-scoped `storage.types`: codec triples only. */\nconst DocumentScopedStorageTypeSchema = StorageTypeInstanceSchema;\n\n/**\n * Domain enum entry under `domain.namespaces[id].enum[name]`.\n * Carries the codec id and an ordered `members` array of `{name, value}` pairs.\n */\nexport const ContractEnumSchema = type({\n '+': 'reject',\n codecId: 'string',\n members: type({\n name: 'string',\n value: 'string | number | boolean | null | unknown[] | Record<string, unknown>',\n })\n .array()\n .readonly(),\n});\n\n/**\n * Derives a schema map from a descriptor map: maps each kind's key to its\n * `schema` field. Used by validation functions to validate entries.\n */\nfunction schemaViewOf(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): ReadonlyMap<string, Type<unknown>> {\n return new Map([...kinds].map(([k, d]) => [k, d.schema]));\n}\n\nconst DEFAULT_SQL_KINDS = composeSqlEntityKinds();\n\n/**\n * Builds the per-namespace entry schema for `storage.namespaces[id]`.\n *\n * Validation is descriptor-driven: the `kinds` map carries both the schema\n * (used here for structural validation) and the construct function (used at\n * hydration time). An unregistered key fails validation naming the kind and\n * the namespace id, so validation fails closed.\n */\nexport function createNamespaceEntrySchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const schemas = schemaViewOf(kinds);\n const knownKinds = new Set(kinds.keys());\n return type({\n '+': 'reject',\n id: 'string',\n 'kind?': 'string',\n entries: 'object',\n }).narrow((ns, ctx) => {\n if (!isPlainRecord(ns.entries)) {\n return ctx.mustBe('an entries object');\n }\n for (const [key, innerMap] of Object.entries(ns.entries)) {\n if (!knownKinds.has(key)) {\n return ctx.reject({\n expected: `entries key \"${key}\" in namespace \"${ns.id}\" is not a registered entity kind`,\n });\n }\n if (!isPlainRecord(innerMap)) {\n return ctx.reject({\n expected: `entries[\"${key}\"] in namespace \"${ns.id}\" must be an object`,\n });\n }\n const entrySchema = blindCast<\n Type<unknown>,\n 'knownKinds.has(key) guarantees schemas.get(key) is defined'\n >(schemas.get(key));\n for (const [, value] of Object.entries(innerMap)) {\n const parsed = entrySchema(value);\n if (parsed instanceof type.errors) {\n return ctx.reject({ expected: parsed.summary });\n }\n }\n }\n return true;\n }) as Type<unknown>;\n}\n\n/**\n * Builds the storage schema. Pack contributions reach the per-namespace\n * entry shape through {@link createNamespaceEntrySchema}; the\n * document-scoped `storage.types` field (codec triples only) and the\n * storage hash stay family-shared.\n */\nexport function createSqlStorageSchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const namespaceEntry = createNamespaceEntrySchema(kinds);\n return type({\n '+': 'reject',\n storageHash: 'string',\n 'types?': type({ '[string]': DocumentScopedStorageTypeSchema }),\n // `__unbound__` is NOT required here: cross-namespace contracts can\n // declare only named namespaces (see cross-namespace FK fixtures). The\n // unbound slot is injected when absent by `ensureUnboundNamespaceSlot`\n // in `build-contract.ts`, not enforced here structurally.\n 'namespaces?': type({ '[string]': namespaceEntry }),\n }) as Type<unknown>;\n}\n\nconst StorageSchema = createSqlStorageSchema(DEFAULT_SQL_KINDS);\n\ntype NamespacedStorageWalk = {\n readonly namespaces: Readonly<\n Record<\n string,\n Namespace & { readonly entries: Readonly<Record<string, Readonly<Record<string, unknown>>>> }\n >\n >;\n};\n\nfunction eachStorageTable(storage: NamespacedStorageWalk) {\n return Object.entries(storage.namespaces).flatMap(([namespaceId, ns]) =>\n Object.entries(ns.entries['table'] ?? {}).map(([tableName, table]) => ({\n namespaceId,\n tableName,\n table,\n })),\n );\n}\n\nfunction findDuplicateValue(values: readonly string[]): string | undefined {\n const seen = new Set<string>();\n for (const value of values) {\n if (seen.has(value)) {\n return value;\n }\n seen.add(value);\n }\n return undefined;\n}\n\nfunction isContractFieldType(value: unknown): boolean {\n if (!isPlainRecord(value)) return false;\n const kind = value['kind'];\n if (kind === 'scalar') {\n if (typeof value['codecId'] !== 'string') return false;\n const typeParams = value['typeParams'];\n if (typeParams !== undefined && !isPlainRecord(typeParams)) return false;\n return true;\n }\n if (kind === 'valueObject') {\n return typeof value['name'] === 'string';\n }\n if (kind === 'union') {\n const members = value['members'];\n if (!Array.isArray(members)) return false;\n return members.every((m) => isContractFieldType(m));\n }\n return false;\n}\n\nconst ContractFieldTypeSchema = type('unknown').narrow((value, ctx) =>\n isContractFieldType(value) ? true : ctx.mustBe('scalar, valueObject, or union field type'),\n);\n\nconst ModelFieldSchema = type({\n '+': 'reject',\n nullable: 'boolean',\n type: ContractFieldTypeSchema,\n 'many?': 'true',\n 'dict?': 'true',\n 'valueSet?': DomainEnumRefSchema,\n});\n\nconst ModelStorageFieldSchema = type({\n column: 'string',\n 'codecId?': 'string',\n 'nullable?': 'boolean',\n});\n\nconst ModelStorageSchema = type({\n table: 'string',\n namespaceId: 'string',\n fields: type({ '[string]': ModelStorageFieldSchema }),\n});\n\nconst ContractRelationThroughSchema = type({\n '+': 'reject',\n table: 'string',\n namespaceId: 'string',\n parentColumns: type.string.array().readonly(),\n childColumns: type.string.array().readonly(),\n targetColumns: type.string.array().readonly(),\n});\n\nconst ContractRelationOnSchema = type({\n '+': 'reject',\n localFields: type.string.array().readonly(),\n targetFields: type.string.array().readonly(),\n});\n\nconst ContractManyToManyRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'N:M'\",\n on: ContractRelationOnSchema,\n through: ContractRelationThroughSchema,\n});\n\nconst ContractNonJunctionRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'1:1' | '1:N' | 'N:1'\",\n on: ContractRelationOnSchema,\n});\n\nconst ContractReferenceRelationSchema = ContractManyToManyRelationSchema.or(\n ContractNonJunctionRelationSchema,\n);\n\nconst ContractEmbedRelationSchema = type({\n '+': 'reject',\n to: CrossReferenceSchema,\n cardinality: \"'1:1' | '1:N'\",\n});\n\nconst ContractRelationSchema = ContractReferenceRelationSchema.or(ContractEmbedRelationSchema);\n\nconst ModelSchema = type({\n storage: ModelStorageSchema,\n 'fields?': type({ '[string]': ModelFieldSchema }),\n 'relations?': type({ '[string]': ContractRelationSchema }),\n 'discriminator?': 'unknown',\n 'variants?': 'unknown',\n 'base?': CrossReferenceSchema,\n 'owner?': 'string',\n});\n\nconst ContractMetaSchema = type({\n '[string]': 'unknown',\n});\n\n/**\n * Builds the full SQL contract schema. The storage subtree threads\n * pack contributions through {@link createSqlStorageSchema}; the rest\n * of the contract envelope is family-shared.\n */\nexport function createSqlContractSchema(\n kinds: ReadonlyMap<string, AnyEntityKindDescriptor>,\n): Type<unknown> {\n const storage = createSqlStorageSchema(kinds);\n return type({\n '+': 'reject',\n target: 'string',\n targetFamily: \"'sql'\",\n 'coreHash?': 'string',\n profileHash: 'string',\n 'capabilities?': 'Record<string, Record<string, boolean>>',\n 'extensions?': 'Record<string, unknown>',\n 'meta?': ContractMetaSchema,\n 'defaultControlPolicy?': ControlPolicySchema,\n 'roots?': type({ '[string]': CrossReferenceSchema }),\n domain: type({\n namespaces: type({\n '[string]': type({\n models: type({ '[string]': ModelSchema }),\n 'valueObjects?': 'Record<string, unknown>',\n 'enum?': type({ '[string]': ContractEnumSchema }),\n }),\n }),\n }),\n storage,\n 'execution?': ExecutionSchema,\n }) as Type<unknown>;\n}\n\nconst SqlContractSchema = createSqlContractSchema(DEFAULT_SQL_KINDS);\n\n/**\n * Validates the structural shape of SqlStorage using Arktype. Pure\n * structural check: namespace IR is never materialized here (that needs\n * a target concretion via the serializer hydration path), so this throws\n * on invalid input and constructs nothing.\n *\n * @param value - The storage value to validate\n * @throws Error if the storage structure is invalid\n */\nexport function validateStorage(value: unknown): void {\n const result = StorageSchema(value);\n if (result instanceof type.errors) {\n const errors = result.map((p: { message: string }) => p.message);\n throw contractError(\n 'CONTRACT.VALIDATION_FAILED',\n `Storage validation failed: ${errors.join('; ')}`,\n {\n meta: { errors },\n },\n );\n }\n}\n\nexport function validateModel(value: unknown): unknown {\n const result = ModelSchema(value);\n if (result instanceof type.errors) {\n const errors = result.map((p: { message: string }) => p.message);\n throw contractError(\n 'CONTRACT.VALIDATION_FAILED',\n `Model validation failed: ${errors.join('; ')}`,\n {\n meta: { errors },\n },\n );\n }\n return result;\n}\n\n/**\n * Structural arktype validation of an SQL contract envelope. Internal\n * helper for {@link validateSqlContractFully} — exposed only inside\n * this module, since the family seam-of-record is the\n * `SqlContractSerializerBase.deserializeContract` SPI.\n */\nfunction validateSqlContractStructure<T extends Contract<SqlStorage>>(\n value: unknown,\n contractSchema: Type<unknown>,\n): T {\n if (typeof value !== 'object' || value === null) {\n throw new ContractValidationError(\n 'Contract structural validation failed: value must be an object',\n 'structural',\n );\n }\n\n const rawValue = value as { targetFamily?: string };\n if (rawValue.targetFamily !== undefined && rawValue.targetFamily !== 'sql') {\n throw new ContractValidationError(\n `Unsupported target family: ${rawValue.targetFamily}`,\n 'structural',\n );\n }\n\n const contractResult = contractSchema(value);\n\n if (contractResult instanceof type.errors) {\n const messages = contractResult.map((p: { message: string }) => p.message).join('; ');\n throw new ContractValidationError(\n `Contract structural validation failed: ${messages}`,\n 'structural',\n );\n }\n\n // Arktype's inferred output type differs from T due to exactOptionalPropertyTypes\n // and branded hash types — the runtime value is structurally compatible after validation\n return contractResult as unknown as T;\n}\n\n/**\n * Validates semantic constraints on SqlStorage that cannot be expressed in Arktype schemas.\n *\n * Returns an array of human-readable error strings. Empty array = valid.\n *\n * Currently checks:\n * - duplicate named primary key / unique / index / foreign key objects within a table\n * - duplicate unique, index, or foreign key declarations within a table\n * - duplicate columns within primary key / unique / index definitions\n * - nullable columns in primary key definitions\n * - `setNull` referential action on a non-nullable FK column (would fail at runtime)\n * - `setDefault` referential action on a non-nullable FK column without a DEFAULT (would fail at runtime)\n */\nexport function validateStorageSemantics(storage: SqlStorage): string[] {\n const errors: string[] = [];\n\n for (const { namespaceId, tableName, table: rawTable } of eachStorageTable(storage)) {\n const table = rawTable as StorageTable;\n const namedObjects = new Map<string, string[]>();\n const registerNamedObject = (kind: string, name: string | undefined) => {\n if (!name) return;\n namedObjects.set(name, [...(namedObjects.get(name) ?? []), kind]);\n };\n\n registerNamedObject('primary key', table.primaryKey?.name);\n for (const unique of table.uniques) {\n registerNamedObject('unique constraint', unique.name);\n }\n for (const index of table.indexes) {\n registerNamedObject('index', index.name);\n }\n for (const fk of table.foreignKeys) {\n registerNamedObject('foreign key', fk.name);\n }\n for (const check of table.checks ?? []) {\n registerNamedObject('check constraint', check.name);\n }\n\n for (const [name, kinds] of namedObjects) {\n if (kinds.length > 1) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": named object \"${name}\" is declared multiple times (${kinds.join(', ')})`,\n );\n }\n }\n\n if (table.primaryKey) {\n const duplicateColumn = findDuplicateValue(table.primaryKey.columns);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": primary key contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n for (const columnName of table.primaryKey.columns) {\n const column = table.columns[columnName];\n if (column?.nullable === true) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": primary key column \"${columnName}\" is nullable; primary key columns must be NOT NULL`,\n );\n }\n }\n }\n\n const seenUniqueDefinitions = new Set<string>();\n for (const unique of table.uniques) {\n const duplicateColumn = findDuplicateValue(unique.columns);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": unique constraint contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n const signature = JSON.stringify({ columns: unique.columns });\n if (seenUniqueDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate unique constraint definition on columns [${unique.columns.join(', ')}]`,\n );\n continue;\n }\n seenUniqueDefinitions.add(signature);\n }\n\n const sortOptions = (o: Record<string, unknown> | undefined): Record<string, unknown> | null =>\n o ? Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b))) : null;\n\n const seenIndexDefinitions = new Set<string>();\n for (const index of table.indexes) {\n const duplicateColumn = findDuplicateValue(index.columns ?? []);\n if (duplicateColumn !== undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": index contains duplicate column \"${duplicateColumn}\"`,\n );\n }\n\n const signature = JSON.stringify({\n columns: index.columns ?? null,\n expression: index.expression ?? null,\n where: index.where ?? null,\n unique: index.unique,\n type: index.type ?? null,\n options: sortOptions(index.options),\n });\n if (seenIndexDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate index definition on columns [${(index.columns ?? []).join(', ')}]`,\n );\n continue;\n }\n seenIndexDefinitions.add(signature);\n }\n\n const seenForeignKeyDefinitions = new Set<string>();\n for (const fk of table.foreignKeys) {\n const signature = JSON.stringify({\n source: fk.source,\n target: fk.target,\n onDelete: fk.onDelete ?? null,\n onUpdate: fk.onUpdate ?? null,\n });\n if (seenForeignKeyDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate foreign key definition on columns [${fk.source.columns.join(', ')}]`,\n );\n continue;\n }\n seenForeignKeyDefinitions.add(signature);\n }\n\n for (const fk of table.foreignKeys) {\n for (const colName of fk.source.columns) {\n const column = table.columns[colName];\n if (!column) continue;\n\n if (fk.onDelete === 'setNull' && !column.nullable) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onDelete setNull on foreign key column \"${colName}\" which is NOT NULL`,\n );\n }\n if (fk.onUpdate === 'setNull' && !column.nullable) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onUpdate setNull on foreign key column \"${colName}\" which is NOT NULL`,\n );\n }\n if (fk.onDelete === 'setDefault' && !column.nullable && column.default === undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onDelete setDefault on foreign key column \"${colName}\" which is NOT NULL and has no DEFAULT`,\n );\n }\n if (fk.onUpdate === 'setDefault' && !column.nullable && column.default === undefined) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": onUpdate setDefault on foreign key column \"${colName}\" which is NOT NULL and has no DEFAULT`,\n );\n }\n }\n }\n\n const seenCheckDefinitions = new Set<string>();\n for (const check of table.checks ?? []) {\n const signature = JSON.stringify({ column: check.column, valueSet: check.valueSet });\n if (seenCheckDefinitions.has(signature)) {\n errors.push(\n `Namespace \"${namespaceId}\" table \"${tableName}\": duplicate check constraint definition on column \"${check.column}\"`,\n );\n continue;\n }\n seenCheckDefinitions.add(signature);\n }\n }\n\n return errors;\n}\n\n/**\n * SQL storage logical-consistency checks: every model.storage.table\n * resolves to a real table, every model.storage.fields[*].column\n * resolves to a real column, and value-object fields land on JSON-native\n * columns. Throws `ContractValidationError` on the first mismatch.\n */\nexport function validateModelStorageReferences(contract: Contract<SqlStorage>): void {\n for (const [namespaceId, namespace] of Object.entries(contract.domain.namespaces)) {\n const models = namespace.models as Record<string, ContractModel<SqlModelStorage>>;\n for (const [modelName, model] of Object.entries(models)) {\n const qualifiedName = `${namespaceId}:${modelName}`;\n const storageNamespaceId = model.storage.namespaceId;\n if (storageNamespaceId !== namespaceId) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" storage.namespaceId \"${storageNamespaceId}\" does not match domain namespace \"${namespaceId}\"`,\n 'storage',\n );\n }\n\n const storageTable = model.storage.table;\n const storageNs = contract.storage.namespaces[storageNamespaceId];\n const rawTable = storageNs?.entries.table?.[storageTable];\n if (rawTable === undefined) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" references non-existent table \"${storageNamespaceId}.${storageTable}\"`,\n 'storage',\n );\n }\n\n const table = rawTable as StorageTable;\n\n const columnNames = new Set(Object.keys(table.columns));\n for (const [fieldName, field] of Object.entries(model.storage.fields)) {\n if (!columnNames.has(field.column)) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" field \"${fieldName}\" references non-existent column \"${field.column}\" in table \"${storageTable}\"`,\n 'storage',\n );\n }\n }\n\n const JSON_NATIVE_TYPES = new Set(['json', 'jsonb']);\n for (const [fieldName, domainField] of Object.entries(model.fields ?? {})) {\n const f = domainField as ContractField;\n if (f.type?.kind !== 'valueObject') continue;\n const storageField = model.storage.fields[fieldName];\n if (!storageField) continue;\n const column = table.columns[storageField.column];\n if (!column) continue;\n if (!JSON_NATIVE_TYPES.has(column.nativeType)) {\n throw new ContractValidationError(\n `Model \"${qualifiedName}\" field \"${fieldName}\" is a value object but storage column \"${storageField.column}\" has nativeType \"${column.nativeType}\" (expected json or jsonb)`,\n 'storage',\n );\n }\n }\n }\n }\n}\n\n/**\n * Cross-table consistency checks for SQL storage: primary key, unique,\n * index, and foreign key column references resolve to real columns;\n * NOT NULL columns don't carry a literal `null` default; FK column\n * counts match their referenced columns. Throws on the first mismatch.\n */\nexport function validateSqlStorageConsistency(contract: Contract<SqlStorage>): void {\n for (const { namespaceId, tableName, table: rawTable } of eachStorageTable(contract.storage)) {\n const table = rawTable as StorageTable;\n const columnNames = new Set(Object.keys(table.columns));\n\n if (table.primaryKey) {\n for (const colName of table.primaryKey.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" primaryKey references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const unique of table.uniques) {\n for (const colName of unique.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" unique constraint references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const index of table.indexes) {\n for (const colName of index.columns ?? []) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" index references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n }\n\n for (const check of table.checks ?? []) {\n if (!columnNames.has(check.column)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" check constraint \"${check.name}\" references non-existent column \"${check.column}\"`,\n 'storage',\n );\n }\n }\n\n for (const [colName, column] of Object.entries(table.columns)) {\n if (!column.nullable && column.default?.kind === 'literal' && column.default.value === null) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" column \"${colName}\" is NOT NULL but has a literal null default`,\n 'storage',\n );\n }\n }\n\n for (const fk of table.foreignKeys) {\n if (fk.source.namespaceId !== namespaceId || fk.source.tableName !== tableName) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" contains foreignKey with mismatched source coordinates (${fk.source.namespaceId}.${fk.source.tableName})`,\n 'storage',\n );\n }\n\n for (const colName of fk.source.columns) {\n if (!columnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent column \"${colName}\"`,\n 'storage',\n );\n }\n }\n\n if (fk.target.spaceId === undefined) {\n const targetNamespace = contract.storage.namespaces[fk.target.namespaceId];\n const referencedRaw = targetNamespace?.entries.table?.[fk.target.tableName];\n if (referencedRaw === undefined) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent table \"${fk.target.namespaceId}.${fk.target.tableName}\"`,\n 'storage',\n );\n }\n const referencedTable = referencedRaw as StorageTable;\n const referencedColumnNames = new Set(Object.keys(referencedTable.columns));\n for (const colName of fk.target.columns) {\n if (!referencedColumnNames.has(colName)) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey references non-existent column \"${colName}\" in table \"${fk.target.tableName}\"`,\n 'storage',\n );\n }\n }\n }\n\n if (fk.source.columns.length !== fk.target.columns.length) {\n throw new ContractValidationError(\n `Namespace \"${namespaceId}\" table \"${tableName}\" foreignKey column count (${fk.source.columns.length}) does not match referenced column count (${fk.target.columns.length})`,\n 'storage',\n );\n }\n }\n }\n}\n\nexport interface ValidateSqlContractFullyOptions {\n /**\n * Precomputed structural schema to validate against. Built once at\n * serializer construction time when the family `ContractSerializer`\n * has folded pack-contributed `validatorSchema` fragments into the\n * per-namespace entry shape; absent for the family-default validator\n * path (no pack contributions). Falls back to the cached default\n * `SqlContractSchema` when omitted.\n */\n readonly contractSchema?: Type<unknown>;\n}\n\n/**\n * Full SQL contract validation: structural (arktype) +\n * framework-shared domain + SQL storage logical-consistency + SQL\n * storage semantic + model ↔ storage reference checks. Throws\n * `ContractValidationError` on the first failure. Returns the\n * validated flat-data shape; IR class hydration happens in the SPI\n * base on top of this helper.\n */\nexport function validateSqlContractFully<T extends Contract<SqlStorage>>(\n value: unknown,\n options?: ValidateSqlContractFullyOptions,\n): T {\n const stripped =\n typeof value === 'object' && value !== null\n ? (() => {\n const { schemaVersion: _, _generated: _g, ...rest } = value as Record<string, unknown>;\n return rest;\n })()\n : value;\n const schema = options?.contractSchema ?? SqlContractSchema;\n const validated = validateSqlContractStructure<T>(stripped, schema);\n validateContractDomain({\n roots: validated.roots,\n domain: validated.domain,\n });\n validateSqlStorageConsistency(validated);\n const semanticErrors = validateStorageSemantics(validated.storage);\n if (semanticErrors.length > 0) {\n throw new ContractValidationError(\n `Contract semantic validation failed: ${semanticErrors.join('; ')}`,\n 'storage',\n );\n }\n validateModelStorageReferences(validated);\n validateRelationThroughConsistency(validated);\n return validated;\n}\n\n/** Storage column lookup for through-consistency validation. */\nfunction lookupStorageColumn(\n contract: Contract<SqlStorage>,\n namespaceId: string,\n tableName: string,\n columnName: string,\n): StorageColumn | undefined {\n const rawTable = contract.storage.namespaces[namespaceId]?.entries.table?.[tableName];\n if (rawTable === undefined) {\n return undefined;\n }\n const table = blindCast<StorageTable, 'structurally validated storage table'>(rawTable);\n return table.columns[columnName];\n}\n\n/**\n * Two storage columns share a type when their `nativeType` and `typeParams`\n * match. The contract is canonicalized, so `typeParams` key order is stable and\n * a JSON comparison is exact. `codecId` and `nullable` are intentionally not\n * compared: they do not change the database-level type that governs a join.\n */\nfunction sameStorageType(a: StorageColumn, b: StorageColumn): boolean {\n return (\n a.nativeType === b.nativeType &&\n JSON.stringify(a.typeParams ?? null) === JSON.stringify(b.typeParams ?? null)\n );\n}\n\nfunction describeColumnType(column: StorageColumn): string {\n return column.typeParams === undefined\n ? column.nativeType\n : `${column.nativeType} ${JSON.stringify(column.typeParams)}`;\n}\n\n/**\n * Validates one side of an N:M join: the junction columns and the model\n * columns they pair against positionally must be equal in number, exist in\n * their tables, and share the same storage type (`nativeType` + `typeParams`).\n * The junction's storage foreign keys already guarantee this for user-declared\n * FK constraints, but `through` is a logical descriptor never tied to them by\n * the rest of validation — and the TS builder accepts explicit join columns\n * without requiring a junction FK at all — so this checks the columns directly\n * against storage, one path regardless of how the junction was authored.\n *\n * Joined columns must be the *same* storage type, not merely compatible:\n * relying on implicit conversion (e.g. `text`↔`character`) is unsafe on writes\n * — `character(n)` space-padding makes such coercions non-associative — and no\n * ADR sanctions heterogeneous junction columns. Equality is the conservative\n * default; it can be relaxed deliberately if a real use case ever appears.\n */\nfunction validateThroughJoinSide(input: {\n readonly contract: Contract<SqlStorage>;\n readonly qualifiedName: string;\n readonly modelColumns: readonly string[];\n readonly modelColumnsLabel: string;\n /**\n * The model side of the join, when resolvable. Absent for a cross-space\n * target whose storage lives in another contract: the length and\n * junction-column-existence checks still run, but the target-column\n * existence and type checks are skipped because the target table is not\n * available here. `fieldToColumn` is present when `modelColumns` are domain\n * field names (the `on.*Fields` arrays), absent when they are already storage\n * column names (the `through.*Columns` arrays).\n */\n readonly model?: {\n readonly namespaceId: string;\n readonly table: string;\n readonly fieldToColumn?: Readonly<Record<string, { readonly column: string }>>;\n };\n readonly junctionColumns: readonly string[];\n readonly junctionColumnsLabel: string;\n readonly junctionNamespaceId: string;\n readonly junctionTable: string;\n}): void {\n const fail = (detail: string): ContractValidationError =>\n new ContractValidationError(\n `Many-to-many relation \"${input.qualifiedName}\" ${detail}`,\n 'storage',\n );\n if (input.junctionColumns.length !== input.modelColumns.length) {\n throw fail(\n `pairs ${input.junctionColumnsLabel} (${input.junctionColumns.length}) with ${input.modelColumnsLabel} (${input.modelColumns.length}) of differing length; they join positionally and must match.`,\n );\n }\n for (const [index, junctionColumnName] of input.junctionColumns.entries()) {\n const modelColumnRef = input.modelColumns[index];\n if (modelColumnRef === undefined) {\n continue;\n }\n const junctionColumn = lookupStorageColumn(\n input.contract,\n input.junctionNamespaceId,\n input.junctionTable,\n junctionColumnName,\n );\n if (junctionColumn === undefined) {\n throw fail(\n `${input.junctionColumnsLabel} references column \"${junctionColumnName}\" absent from junction table \"${input.junctionNamespaceId}.${input.junctionTable}\".`,\n );\n }\n // Cross-space target: its storage lives in another contract, so the target\n // column's existence and type cannot be checked here. Length and\n // junction-column existence have already been validated above.\n const model = input.model;\n if (model === undefined) {\n continue;\n }\n let modelColumnName = modelColumnRef;\n if (model.fieldToColumn !== undefined) {\n const mapped = model.fieldToColumn[modelColumnRef];\n if (mapped === undefined) {\n throw fail(\n `${input.modelColumnsLabel} references field \"${modelColumnRef}\" absent from model on table \"${model.namespaceId}.${model.table}\".`,\n );\n }\n modelColumnName = mapped.column;\n }\n const modelColumn = lookupStorageColumn(\n input.contract,\n model.namespaceId,\n model.table,\n modelColumnName,\n );\n if (modelColumn === undefined) {\n throw fail(\n `${input.modelColumnsLabel} references column \"${modelColumnName}\" absent from table \"${model.namespaceId}.${model.table}\".`,\n );\n }\n if (!sameStorageType(junctionColumn, modelColumn)) {\n throw fail(\n `joins \"${input.junctionTable}.${junctionColumnName}\" (${describeColumnType(junctionColumn)}) with \"${model.table}.${modelColumnName}\" (${describeColumnType(modelColumn)}) of differing storage type; junction columns must match the type of the column they reference.`,\n );\n }\n }\n}\n\n/**\n * Validates that every N:M relation's `through` descriptor is consistent with\n * the storage columns it joins: both join sides match in column count,\n * reference columns that exist in their tables, and pair columns of the same\n * storage type. Without this, a `through` that disagrees with storage surfaces\n * as a silently wrong JOIN at query time rather than a validation error here.\n */\nfunction validateRelationThroughConsistency(contract: Contract<SqlStorage>): void {\n for (const [namespaceId, namespace] of Object.entries(contract.domain.namespaces)) {\n for (const [modelName, model] of Object.entries(namespace.models)) {\n for (const [relationName, relation] of Object.entries(model.relations)) {\n if (relation.cardinality !== 'N:M') continue;\n const qualifiedName = `${namespaceId}.${modelName}.${relationName}`;\n const { on, through } = relation;\n const modelStorage = blindCast<SqlModelStorage, 'SQL contract model storage'>(\n model.storage,\n );\n // Parent side: the owning model's localFields (domain field names) join\n // the junction's parentColumns (storage columns).\n validateThroughJoinSide({\n contract,\n qualifiedName,\n modelColumns: on.localFields,\n modelColumnsLabel: 'on.localFields',\n model: {\n namespaceId,\n table: modelStorage.table,\n fieldToColumn: modelStorage.fields,\n },\n junctionColumns: through.parentColumns,\n junctionColumnsLabel: 'through.parentColumns',\n junctionNamespaceId: through.namespaceId,\n junctionTable: through.table,\n });\n // Child side: the junction's childColumns join the target model's\n // targetColumns. Length and junction-column existence are checked\n // regardless; a cross-space target lives in another contract, so its\n // column existence and type are checked only when it is resolvable here.\n const targetModel =\n relation.to.space === undefined\n ? contract.domain.namespaces[relation.to.namespace]?.models[relation.to.model]\n : undefined;\n const targetModelSide =\n targetModel === undefined\n ? undefined\n : {\n namespaceId: relation.to.namespace,\n table: blindCast<SqlModelStorage, 'SQL contract model storage'>(targetModel.storage)\n .table,\n };\n validateThroughJoinSide({\n contract,\n qualifiedName,\n modelColumns: through.targetColumns,\n modelColumnsLabel: 'through.targetColumns',\n ...ifDefined('model', targetModelSide),\n junctionColumns: through.childColumns,\n junctionColumnsLabel: 'through.childColumns',\n junctionNamespaceId: through.namespaceId,\n junctionTable: through.table,\n });\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,21 +1,21 @@
1
1
  {
2
2
  "name": "@prisma-next/sql-contract",
3
- "version": "0.16.0-dev.25",
3
+ "version": "0.16.0-dev.26",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "SQL contract types, validators, and IR factories for Prisma Next",
8
8
  "dependencies": {
9
- "@prisma-next/contract": "0.16.0-dev.25",
10
- "@prisma-next/framework-components": "0.16.0-dev.25",
11
- "@prisma-next/sql-schema-ir": "0.16.0-dev.25",
12
- "@prisma-next/utils": "0.16.0-dev.25",
9
+ "@prisma-next/contract": "0.16.0-dev.26",
10
+ "@prisma-next/framework-components": "0.16.0-dev.26",
11
+ "@prisma-next/sql-schema-ir": "0.16.0-dev.26",
12
+ "@prisma-next/utils": "0.16.0-dev.26",
13
13
  "arktype": "^2.2.2"
14
14
  },
15
15
  "devDependencies": {
16
- "@prisma-next/test-utils": "0.16.0-dev.25",
17
- "@prisma-next/tsconfig": "0.16.0-dev.25",
18
- "@prisma-next/tsdown": "0.16.0-dev.25",
16
+ "@prisma-next/test-utils": "0.16.0-dev.26",
17
+ "@prisma-next/tsconfig": "0.16.0-dev.26",
18
+ "@prisma-next/tsdown": "0.16.0-dev.26",
19
19
  "tsdown": "0.22.8",
20
20
  "typescript": "5.9.3",
21
21
  "vitest": "4.1.10"
@@ -39,6 +39,7 @@
39
39
  "./entity-kinds": "./dist/entity-kinds.mjs",
40
40
  "./factories": "./dist/factories.mjs",
41
41
  "./foreign-key-materialization": "./dist/foreign-key-materialization.mjs",
42
+ "./index-naming": "./dist/index-naming.mjs",
42
43
  "./index-type-validation": "./dist/index-type-validation.mjs",
43
44
  "./index-types": "./dist/index-types.mjs",
44
45
  "./pack-types": "./dist/pack-types.mjs",
@@ -11,6 +11,10 @@ const preserveEmptyPatterns = [
11
11
  ['storage', 'namespaces', '*', 'entries', 'table'],
12
12
  ['storage', 'namespaces', '*', 'entries', 'table', '*'],
13
13
  ['storage', 'namespaces', '*', 'entries', 'table', '*', ['uniques', 'indexes', 'foreignKeys']],
14
+ // An index's `unique` flag is a required boolean: `unique: false` must
15
+ // survive the default-omission walk or the emitted contract fails its own
16
+ // IndexSchema validation on the next read.
17
+ ['storage', 'namespaces', '*', 'entries', 'table', '*', 'indexes', 'unique'],
14
18
  // A column default's literal payload is data, not shape — `{ kind:
15
19
  // 'literal', value: false }` (or `value: []`) must survive the
16
20
  // default-omission walk or the emitted contract fails its own
@@ -0,0 +1 @@
1
+ export { type AuthoredIndexInput, lowerAuthoredIndex } from '../index-naming';
package/src/factories.ts CHANGED
@@ -35,8 +35,25 @@ export function unique(...columns: readonly string[]): UniqueConstraint {
35
35
  return new UniqueConstraint({ columns });
36
36
  }
37
37
 
38
- export function index(...columns: readonly string[]): Index {
39
- return new Index({ columns });
38
+ export function index(
39
+ name: string,
40
+ columns: readonly string[],
41
+ opts?: {
42
+ readonly prefix?: string;
43
+ readonly unique?: boolean;
44
+ readonly type?: string;
45
+ readonly options?: Record<string, unknown>;
46
+ },
47
+ ): Index {
48
+ return new Index({
49
+ name,
50
+ prefix: opts?.prefix,
51
+ columns,
52
+ where: undefined,
53
+ unique: opts?.unique ?? false,
54
+ type: opts?.type,
55
+ options: opts?.options,
56
+ });
40
57
  }
41
58
 
42
59
  export function fk(
@@ -1,4 +1,4 @@
1
- import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
1
+ import { lowerAuthoredIndex } from './index-naming';
2
2
  import type { ForeignKeyInput, ReferentialAction } from './ir/foreign-key';
3
3
  import type { ForeignKeyReferenceInput } from './ir/foreign-key-reference';
4
4
  import type { PrimaryKeyInput } from './ir/primary-key';
@@ -6,7 +6,7 @@ import type { IndexInput } from './ir/sql-index';
6
6
  import type { UniqueConstraintInput } from './ir/unique-constraint';
7
7
 
8
8
  export type BackingIndexCandidates = {
9
- readonly indexes: readonly { readonly columns: readonly string[] }[];
9
+ readonly indexes: readonly { readonly columns?: readonly string[] }[];
10
10
  readonly uniques: readonly { readonly columns: readonly string[] }[];
11
11
  readonly primaryKey?: { readonly columns: readonly string[] } | undefined;
12
12
  };
@@ -24,7 +24,9 @@ export type BackingIndexCandidates = {
24
24
  */
25
25
  export function backingIndexColumnKeys(table: BackingIndexCandidates): readonly string[] {
26
26
  return [
27
- ...table.indexes.map((index) => index.columns.join(',')),
27
+ ...table.indexes.flatMap((index) =>
28
+ index.columns !== undefined ? [index.columns.join(',')] : [],
29
+ ),
28
30
  ...table.uniques.map((unique) => unique.columns.join(',')),
29
31
  ...(table.primaryKey ? [table.primaryKey.columns.join(',')] : []),
30
32
  ];
@@ -66,10 +68,10 @@ export interface MaterializedTableConstraints {
66
68
  * Lowers a table's authored foreign keys into the discrete entities
67
69
  * `contract.json` persists: a `constraint: false` FK contributes no
68
70
  * `foreignKeys[]` entry, and an `index: true` FK whose columns aren't already
69
- * backed by a declared index/unique/primary-key contributes a named
70
- * `indexes[]` entry (`defaultIndexName`). Declared indexes always survive
71
- * unchanged; a second FK sharing already-synthesized backing columns does not
72
- * mint a duplicate index.
71
+ * backed by a declared index/unique/primary-key contributes a managed
72
+ * `indexes[]` entry (default prefix + content-hash wire name). Declared
73
+ * indexes always survive unchanged; a second FK sharing already-synthesized
74
+ * backing columns does not mint a duplicate index.
73
75
  */
74
76
  export function materializeForeignKeysAndIndexes(
75
77
  tableName: string,
@@ -91,10 +93,15 @@ export function materializeForeignKeysAndIndexes(
91
93
  if (index !== false) {
92
94
  const key = reference.source.columns.join(',');
93
95
  if (!satisfiedIndexColumns.has(key)) {
94
- synthesizedIndexes.push({
95
- columns: reference.source.columns,
96
- name: defaultIndexName(tableName, reference.source.columns),
97
- });
96
+ synthesizedIndexes.push(
97
+ lowerAuthoredIndex(tableName, {
98
+ columns: reference.source.columns,
99
+ map: undefined,
100
+ name: undefined,
101
+ type: undefined,
102
+ options: undefined,
103
+ }),
104
+ );
98
105
  satisfiedIndexColumns.add(key);
99
106
  }
100
107
  }
@@ -0,0 +1,65 @@
1
+ import {
2
+ assertWireNamePrefixLength,
3
+ computeIndexContentHash,
4
+ defaultIndexName,
5
+ formatWireName,
6
+ } from '@prisma-next/sql-schema-ir/naming';
7
+ import { InternalError } from '@prisma-next/utils/internal-error';
8
+ import type { IndexInput } from './ir/sql-index';
9
+
10
+ /**
11
+ * An index as authored, before naming: `map` is an exact physical name
12
+ * (adopted verbatim, PSL `map:`); `name` is a managed wire-name prefix
13
+ * (TS `name:`). With neither, the managed prefix defaults to
14
+ * `defaultIndexName(table, columns)`.
15
+ */
16
+ export interface AuthoredIndexInput {
17
+ readonly columns: readonly string[];
18
+ readonly map: string | undefined;
19
+ readonly name: string | undefined;
20
+ readonly type: string | undefined;
21
+ readonly options: Record<string, unknown> | undefined;
22
+ }
23
+
24
+ /**
25
+ * Lowers an authored index into the name-identified entity `contract.json`
26
+ * persists: exact mode adopts `map` verbatim (no prefix, no hash); managed
27
+ * mode appends the content-hash suffix to the authored or default prefix.
28
+ * `unique` always lowers `false` — no authoring surface sets it yet.
29
+ */
30
+ export function lowerAuthoredIndex(tableName: string, authored: AuthoredIndexInput): IndexInput {
31
+ if (authored.map !== undefined) {
32
+ if (authored.name !== undefined) {
33
+ throw new InternalError(
34
+ `Index "${authored.map}" on table "${tableName}": map and name are mutually exclusive.`,
35
+ );
36
+ }
37
+ return {
38
+ name: authored.map,
39
+ prefix: undefined,
40
+ columns: authored.columns,
41
+ where: undefined,
42
+ unique: false,
43
+ type: authored.type,
44
+ options: authored.options,
45
+ };
46
+ }
47
+
48
+ const prefix = authored.name ?? defaultIndexName(tableName, authored.columns);
49
+ assertWireNamePrefixLength(prefix, 'index prefix');
50
+ const hash = computeIndexContentHash({
51
+ columns: authored.columns,
52
+ unique: false,
53
+ ...(authored.type !== undefined && { type: authored.type }),
54
+ ...(authored.options !== undefined && { options: authored.options }),
55
+ });
56
+ return {
57
+ name: formatWireName(prefix, hash),
58
+ prefix,
59
+ columns: authored.columns,
60
+ where: undefined,
61
+ unique: false,
62
+ type: authored.type,
63
+ options: authored.options,
64
+ };
65
+ }
@@ -13,7 +13,7 @@ export function validateIndexTypes(
13
13
  for (const index of table.indexes) {
14
14
  if (index.type === undefined && index.options !== undefined) {
15
15
  throw new ContractValidationError(
16
- `Namespace "${namespaceId}" table "${tableName}" index on columns [${index.columns.join(', ')}] has options without a type`,
16
+ `Namespace "${namespaceId}" table "${tableName}" index "${index.name}" has options without a type`,
17
17
  'storage',
18
18
  );
19
19
  }
@@ -21,7 +21,7 @@ export function validateIndexTypes(
21
21
  const entry = indexTypeRegistry.get(index.type);
22
22
  if (entry === undefined) {
23
23
  throw new ContractValidationError(
24
- `Namespace "${namespaceId}" table "${tableName}" index on columns [${index.columns.join(', ')}] uses unregistered index type "${index.type}"`,
24
+ `Namespace "${namespaceId}" table "${tableName}" index "${index.name}" uses unregistered index type "${index.type}"`,
25
25
  'storage',
26
26
  );
27
27
  }
@@ -29,7 +29,7 @@ export function validateIndexTypes(
29
29
  const result = entry.options(optionsValue);
30
30
  if (result instanceof type.errors) {
31
31
  throw new ContractValidationError(
32
- `Namespace "${namespaceId}" table "${tableName}" index on columns [${index.columns.join(', ')}] has invalid options for type "${index.type}": ${result.summary}`,
32
+ `Namespace "${namespaceId}" table "${tableName}" index "${index.name}" has invalid options for type "${index.type}": ${result.summary}`,
33
33
  'storage',
34
34
  );
35
35
  }
@@ -1,15 +1,68 @@
1
+ import { ContractValidationError } from '@prisma-next/contract/contract-validation-error';
1
2
  import { freezeNode } from '@prisma-next/framework-components/ir';
3
+ import { formatWireName, parseWireName } from '@prisma-next/sql-schema-ir/naming';
2
4
  import { SqlNode } from './sql-node';
3
5
 
4
- export interface IndexInput {
5
- readonly columns: readonly string[];
6
- readonly name?: string;
7
- readonly type?: string;
8
- readonly options?: Record<string, unknown>;
9
- }
6
+ /**
7
+ * An index's element structure — exactly one of a column tuple or an opaque
8
+ * expression, made unrepresentable-otherwise at the type level. No
9
+ * discriminant is stored: the JSON shape stays flat (`columns` or
10
+ * `expression`, never both), and the runtime xor guard in the constructor
11
+ * remains as the backstop for JSON loads that bypass this input type.
12
+ */
13
+ export type IndexElements =
14
+ | {
15
+ /** Column-tuple elements. */
16
+ readonly columns: readonly string[];
17
+ readonly expression?: never;
18
+ }
19
+ | {
20
+ readonly columns?: never;
21
+ /**
22
+ * Opaque SQL: the entire element list between the parens of CREATE
23
+ * INDEX — one string, never parsed.
24
+ */
25
+ readonly expression: string;
26
+ };
10
27
 
11
28
  /**
12
- * SQL Contract IR node for a table-level secondary index.
29
+ * Construction input for {@link Index}. Internal seam (built by lowering and
30
+ * FK materialization, not by end users), so every non-element key is
31
+ * required and absence is stated explicitly as `undefined` — matching the
32
+ * `SqlIndexIRInput` convention.
33
+ */
34
+ export type IndexInput = IndexElements & {
35
+ /** Full wire name (managed) or verbatim physical name (exact). Always present. */
36
+ readonly name: string;
37
+ /**
38
+ * The managed-mode name prefix — its PRESENCE is the naming-mode
39
+ * discriminator (there is no stored enum). Present ⇔ managed: the
40
+ * toolchain owns the physical name and `name === formatWireName(prefix,
41
+ * <8hex content hash>)`, so the author chooses the prefix but never the
42
+ * whole name. Absent ⇔ exact: `name` is an adopted verbatim physical name
43
+ * (PSL `map:`) whose identity the author owns entirely.
44
+ */
45
+ readonly prefix: string | undefined;
46
+ /** Opaque SQL: partial-index predicate (WHERE body, without the keyword). */
47
+ readonly where: string | undefined;
48
+ /** Rendered as CREATE UNIQUE INDEX. */
49
+ readonly unique: boolean;
50
+ readonly type: string | undefined;
51
+ readonly options: Record<string, unknown> | undefined;
52
+ };
53
+
54
+ /**
55
+ * SQL Contract IR node for a table-level secondary index, name-identified:
56
+ * `name` is the full physical name; a present `prefix` marks the index as
57
+ * managed (`name` is `formatWireName(prefix, <8hex>)`), an absent `prefix`
58
+ * marks it exact (the name is adopted verbatim).
59
+ *
60
+ * `expression`, `where`, and `unique` are genuine SQL-family attributes —
61
+ * functional and partial indexes are standard SQL supported natively by
62
+ * Postgres and SQLite alike, and the family IR must be able to represent
63
+ * anything any SQL target can introspect. A target declining to AUTHOR them
64
+ * (SQLite's rejection at namespace construction) is a capability decision,
65
+ * not evidence of target-specificity.
13
66
  *
14
67
  * Note that this class shadows the global TypeScript `Index` lib type
15
68
  * at the family-shared name; consumer files that need both should
@@ -17,15 +70,45 @@ export interface IndexInput {
17
70
  * `import { Index as SqlIndexNode } from '@prisma-next/sql-contract/types'`).
18
71
  */
19
72
  export class Index extends SqlNode {
20
- readonly columns: readonly string[];
21
- declare readonly name?: string;
73
+ readonly name: string;
74
+ readonly unique: boolean;
75
+ /** See {@link IndexInput.prefix} — presence is the naming-mode discriminator. */
76
+ declare readonly prefix?: string;
77
+ declare readonly columns?: readonly string[];
78
+ declare readonly expression?: string;
79
+ declare readonly where?: string;
22
80
  declare readonly type?: string;
23
81
  declare readonly options?: Record<string, unknown>;
24
82
 
25
83
  constructor(input: IndexInput) {
26
84
  super();
27
- this.columns = input.columns;
28
- if (input.name !== undefined) this.name = input.name;
85
+ if (input.name === undefined || input.name.length === 0) {
86
+ throw new ContractValidationError(
87
+ 'Index: every index carries a full physical name; an expression index must be explicitly named (a default name cannot be derived from an expression).',
88
+ 'storage',
89
+ );
90
+ }
91
+ if ((input.columns === undefined) === (input.expression === undefined)) {
92
+ throw new ContractValidationError(
93
+ `Index "${input.name}": exactly one of columns or expression must be set.`,
94
+ 'storage',
95
+ );
96
+ }
97
+ if (input.prefix !== undefined) {
98
+ const parsed = parseWireName(input.name);
99
+ if (parsed === undefined || parsed.prefix !== input.prefix) {
100
+ throw new ContractValidationError(
101
+ `Index "${input.name}": prefix "${input.prefix}" does not match the wire name (expected "${formatWireName(input.prefix, '<8hex>')}").`,
102
+ 'storage',
103
+ );
104
+ }
105
+ }
106
+ this.name = input.name;
107
+ this.unique = input.unique;
108
+ if (input.prefix !== undefined) this.prefix = input.prefix;
109
+ if (input.columns !== undefined) this.columns = input.columns;
110
+ if (input.expression !== undefined) this.expression = input.expression;
111
+ if (input.where !== undefined) this.where = input.where;
29
112
  if (input.type !== undefined) this.type = input.type;
30
113
  if (input.options !== undefined) this.options = input.options;
31
114
  freezeNode(this);