🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@prisma-next/family-sql

Package Overview
Dependencies
Maintainers
4
Versions
991
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@prisma-next/family-sql - npm Package Compare versions

Comparing version
0.15.0
to
0.16.0-dev.1
+186
dist/control-adapter-CW7FdxeS.d.mts
import { ControlAdapterInstance, ControlStack } from "@prisma-next/framework-components/control";
import { ColumnDefault, ContractMarkerRecord, LedgerEntryRecord } from "@prisma-next/contract/types";
import { SqlControlDriverInstance } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { AnyQueryAst, DdlNode, LoweredStatement, LowererContext, SqlExecuteRequest } from "@prisma-next/sql-relational-core/ast";
//#region src/core/diff/sql-schema-diff.d.ts
/**
* Function type for normalizing raw database default expressions into ColumnDefault.
* Target-specific implementations handle database dialect differences.
*/
type DefaultNormalizer = (rawDefault: string, nativeType: string) => ColumnDefault | undefined;
/**
* Function type for normalizing schema native types to canonical form for comparison.
* Target-specific implementations handle dialect-specific type name variations
* (e.g., Postgres 'varchar' → 'character varying', 'timestamptz' normalization).
*/
type NativeTypeNormalizer = (nativeType: string) => string;
/**
* Compares two arrays of strings for equality (order-sensitive).
*/
declare function arraysEqual(a: readonly string[], b: readonly string[]): boolean;
//#endregion
//#region src/core/control-adapter.d.ts
/**
* Structural interface for anything that can lower a SQL/DDL AST node to a
* `LoweredStatement`. `SqlControlAdapter` satisfies this interface; the
* migration planner and op-factory calls accept `Lowerer` rather than the
* full `SqlControlAdapter` so they are not coupled to the broader control
* adapter surface.
*/
interface Lowerer {
lower(ast: AnyQueryAst | DdlNode, context: LowererContext<unknown>): LoweredStatement;
}
/**
* Extends {@link Lowerer} with async codec-routed DDL lowering. Control
* adapters implement this; the planner's `CreateTableCall.toOp` and
* `CreateSchemaCall.toOp` accept it to produce executable DDL statements
* with literal defaults encoded through their codec.
*/
interface ExecuteRequestLowerer extends Lowerer {
lowerToExecuteRequest(ast: AnyQueryAst | DdlNode, context?: LowererContext<unknown>): Promise<SqlExecuteRequest>;
}
/**
* SQL control adapter interface for control-plane operations.
* Implemented by target-specific adapters (e.g., Postgres, MySQL).
*
* @template TTarget - The target ID (e.g., 'postgres', 'mysql')
*/
interface SqlControlAdapter<TTarget extends string = string> extends ControlAdapterInstance<'sql', TTarget>, ExecuteRequestLowerer {
/**
* Reads the contract marker for `space` from the database, returning
* `null` if no marker row exists for that space (or if the marker
* table itself is missing). Implementations are responsible for the
* dialect-specific existence probe (e.g. Postgres
* `information_schema.tables` vs SQLite `sqlite_master`) and parameter
* placeholders.
*
* `space` is required so callers cannot accidentally fall through to
* the app's marker row when reading per-extension markers.
*
* @param driver - ControlDriverInstance for executing queries (target-specific)
* @param space - Contract space id whose marker row to read (e.g. `'app'`)
* @returns Resolved marker record, or `null` if not yet stamped.
*/
readMarker(driver: SqlControlDriverInstance<TTarget>, space: string): Promise<ContractMarkerRecord | null>;
/**
* Reads every marker row from `prisma_contract.marker` (one per
* contract space) and returns them keyed by `space`. Used by the
* per-space verifier to detect marker-vs-on-disk drift and orphan
* marker rows. Returns an empty map when the marker table does not
* yet exist (fresh database / never-signed project).
*/
readAllMarkers(driver: SqlControlDriverInstance<TTarget>): Promise<ReadonlyMap<string, ContractMarkerRecord>>;
/**
* Reads the per-migration ledger journal in apply order. When `space` is
* omitted, returns rows for every space.
*/
readLedger(driver: SqlControlDriverInstance<TTarget>, space?: string): Promise<readonly LedgerEntryRecord[]>;
/**
* Inserts the initial marker row for `space` (`INSERT` only). Fails when a
* row for that space already exists. Used by `sign()` so concurrent first-time
* stamps cannot silently overwrite each other. `updated_at` is DB-side
* (`now()` / `datetime('now')`). Mirrors `MongoControlAdapter.initMarker`.
*/
insertMarker(driver: SqlControlDriverInstance<TTarget>, space: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<void>;
/**
* Writes the initial marker row for `space` as an upsert (`INSERT … ON
* CONFLICT (space) DO UPDATE SET …`), so re-stamping a space overwrites the
* existing row rather than failing. `updated_at` is stamped with a DB-side
* time expression (`now()` / `datetime('now')`), never an app-side clock.
*/
initMarker(driver: SqlControlDriverInstance<TTarget>, space: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<void>;
/**
* Atomically advances the marker row for `space` (compare-and-swap on
* `core_hash = expectedFrom`). Returns `true` when the swap matched a row,
* `false` when another process advanced the marker first. `destination.invariants`
* is written verbatim when supplied (the union/dedupe policy lives upstream)
* and left untouched when omitted. Mirrors `MongoControlAdapter.updateMarker`.
*/
updateMarker(driver: SqlControlDriverInstance<TTarget>, space: string, expectedFrom: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<boolean>;
/**
* Appends a ledger entry for `space`. Mirrors `MongoControlAdapter.writeLedgerEntry`.
* The SQL ledger table keys rows by an autoincrement id and partitions reads
* by `space`, so `entry.edgeId` carries no dedicated column; `from` / `to`
* land in `origin_core_hash` / `destination_core_hash`.
*/
writeLedgerEntry(driver: SqlControlDriverInstance<TTarget>, space: string, entry: {
readonly edgeId: string;
readonly from: string;
readonly to: string;
readonly migrationName: string;
readonly migrationHash: string;
readonly operations: readonly unknown[];
readonly destinationContractJson?: unknown;
}): Promise<void>;
/**
* Introspects a database schema and returns the target's schema-IR node.
*
* This is a pure schema discovery operation that queries the database catalog
* and returns the schema structure without type mapping or contract enrichment.
* Type mapping and enrichment are handled separately by enrichment helpers.
*
* The return type is the family-base `SqlSchemaIRNode` so each target returns
* its own node shape: SQLite returns a flat `SqlSchemaIR`, Postgres returns a
* `PostgresDatabaseSchemaNode` tree root. Consumers `ensure` the concrete
* target type before walking it.
*
* @param driver - ControlDriverInstance instance for executing queries (target-specific)
* @param contract - Optional contract for contract-guided introspection (filtering, optimization)
* @param schema - Schema name to introspect (defaults to 'public')
* @returns Promise resolving to the live database schema node
*/
introspect(driver: SqlControlDriverInstance<TTarget>, contract?: unknown, schema?: string): Promise<SqlSchemaIRNode>;
/**
* Optional target-specific normalizer for raw database default expressions.
* When provided, schema defaults (raw strings) are normalized before comparison
* with contract defaults (ColumnDefault objects) during schema verification.
*/
readonly normalizeDefault?: DefaultNormalizer;
/**
* Optional target-specific normalizer for schema native type names.
* When provided, schema native types (from introspection) are normalized
* before comparison with contract native types during schema verification.
*/
readonly normalizeNativeType?: NativeTypeNormalizer;
/**
* Ordered DDL queries that bootstrap marker/ledger control tables for migration
* runners. Postgres includes `CREATE SCHEMA`; SQLite does not.
*/
bootstrapControlTableQueries(): readonly DdlNode[];
/**
* Ordered DDL queries that bootstrap the marker table (and Postgres schema) for
* `sign` — excludes the ledger table.
*/
bootstrapSignMarkerQueries(): readonly DdlNode[];
}
/**
* SQL control adapter descriptor interface.
* Provides a factory method to create control adapter instances.
*
* @template TTarget - The target ID (e.g., 'postgres', 'mysql')
*/
interface SqlControlAdapterDescriptor<TTarget extends string = string> {
/**
* Creates a SQL control adapter instance for control-plane operations.
*
* Receives the assembled `ControlStack` so adapters can read aggregated
* metadata (codec lookup, extension contributions) when materializing.
*/
create(stack: ControlStack<'sql', TTarget>): SqlControlAdapter<TTarget>;
}
//#endregion
export { NativeTypeNormalizer as a, SqlControlAdapterDescriptor as i, Lowerer as n, arraysEqual as o, SqlControlAdapter as r, ExecuteRequestLowerer as t };
//# sourceMappingURL=control-adapter-CW7FdxeS.d.mts.map
{"version":3,"file":"control-adapter-CW7FdxeS.d.mts","names":[],"sources":["../src/core/diff/sql-schema-diff.ts","../src/core/control-adapter.ts"],"mappings":";;;;;;;;;;KAeY,qBACV,oBACA,uBACG;;;;;;KAOO,wBAAwB;;;;iBAKpB,YAAY,sBAAsB;;;;;;;;;;UCPjC;EACf,MAAM,KAAK,cAAc,SAAS,SAAS,0BAA0B;;;;;;;;UAStD,8BAA8B;EAC7C,sBACE,KAAK,cAAc,SACnB,UAAU,0BACT,QAAQ;;;;;;;;UASI,kBAAkB,yCACzB,8BAA8B,UACpC;;;;;;;;;;;;;;;;EAgBF,WACE,QAAQ,yBAAyB,UACjC,gBACC,QAAQ;;;;;;;;EASX,eACE,QAAQ,yBAAyB,WAChC,QAAQ,oBAAoB;;;;;EAM/B,WACE,QAAQ,yBAAyB,UACjC,iBACC,iBAAiB;;;;;;;EAQpB,aACE,QAAQ,yBAAyB,UACjC,eACA;aACW;aACA;aACA;MAEV;;;;;;;EAQH,WACE,QAAQ,yBAAyB,UACjC,eACA;aACW;aACA;aACA;MAEV;;;;;;;;EASH,aACE,QAAQ,yBAAyB,UACjC,eACA,sBACA;aACW;aACA;aACA;MAEV;;;;;;;EAQH,iBACE,QAAQ,yBAAyB,UACjC,eACA;aACW;aACA;aACA;aACA;aACA;aACA;aACA;MAEV;;;;;;;;;;;;;;;;;;EAmBH,WACE,QAAQ,yBAAyB,UACjC,oBACA,kBACC,QAAQ;;;;;;WAOF,mBAAmB;;;;;;WAOnB,sBAAsB;;;;;EAM/B,yCAAyC;;;;;EAMzC,uCAAuC;;;;;;;;UASxB,4BAA4B;;;;;;;EAO3C,OAAO,OAAO,oBAAoB,WAAW,kBAAkB"}
import { SchemaDiffIssue } from "@prisma-next/framework-components/control";
import { Contract, ControlPolicy } from "@prisma-next/contract/types";
import { SqlStorage } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
//#region src/core/migrations/schema-differ.d.ts
/**
* The full-tree node diff a SQL target produces for the family verify
* verdict: the target derives the expected tree from the contract, applies
* the pre-diff normalizations (semantic satisfaction, FK schema-segment
* resolution), runs the generic differ, and ownership-scopes the result.
* Strict gating, control-policy disposition, and the verdict itself are the
* family's post-diff filters over this output.
*/
interface SqlSchemaDiffResult {
/** The full, ownership-scoped diff issue list. */
readonly issues: readonly SchemaDiffIssue[];
/**
* Resolves a diff issue's subject table's declared control policy directly
* from the contract (Decision 5's own-layer-per-concern discipline extends
* here too: control policy is a contract concern, resolved by the target
* at disposition time — never stamped on the diff node). `undefined`
* when the issue's path resolves to no contract table (a genuine orphan,
* or a non-table subject).
*/
readonly resolveControlPolicy: (issue: SchemaDiffIssue) => ControlPolicy | undefined;
/**
* The expected/actual namespace-node pairs the codec `verifyType` hooks
* run over — one per contract namespace with tables, paired by DDL
* schema; a flat target repeats its sole actual root per such namespace.
*/
readonly namespacePairs: ReadonlyArray<{
readonly actual: SqlSchemaIRNode | undefined;
}>;
}
interface SqlSchemaDiffInput {
readonly contract: Contract<SqlStorage>;
readonly schema: SqlSchemaIRNode;
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
}
type SqlSchemaDiffFn = (input: SqlSchemaDiffInput) => SqlSchemaDiffResult;
//#endregion
export { SqlSchemaDiffInput as n, SqlSchemaDiffResult as r, SqlSchemaDiffFn as t };
//# sourceMappingURL=schema-differ-C8OHo1V5.d.mts.map
{"version":3,"file":"schema-differ-C8OHo1V5.d.mts","names":[],"sources":["../src/core/migrations/schema-differ.ts"],"mappings":";;;;;;;;;;;;;;UAciB;;WAEN,iBAAiB;;;;;;;;;WASjB,uBAAuB,OAAO,oBAAoB;;;;;;WAMlD,gBAAgB;aAAyB,QAAQ;;;UAG3C;WACN,UAAU,SAAS;WACnB,QAAQ;WACR,qBAAqB,cAAc;;KAGlC,mBAAmB,OAAO,uBAAuB"}
import { blindCast } from "@prisma-next/utils/casts";
import { assertUniqueCodecOwner, dispositionForCategory, issueOutcome } from "@prisma-next/framework-components/control";
import { ifDefined } from "@prisma-next/utils/defined";
import { effectiveControlPolicy } from "@prisma-next/contract/types";
import { isStorageTypeInstance } from "@prisma-next/sql-contract/types";
import { RelationalSchemaNodeKind } from "@prisma-next/sql-schema-ir/types";
//#region src/core/assembly.ts
function hasCodecControlHooks(descriptor) {
if (typeof descriptor !== "object" || descriptor === null) return false;
const hooks = descriptor.types?.codecTypes?.controlPlaneHooks;
return hooks !== null && hooks !== void 0 && typeof hooks === "object";
}
function extractCodecControlHooks(descriptors) {
const hooks = /* @__PURE__ */ new Map();
const owners = /* @__PURE__ */ new Map();
for (const descriptor of descriptors) {
if (typeof descriptor !== "object" || descriptor === null) continue;
if (!hasCodecControlHooks(descriptor)) continue;
const controlPlaneHooks = descriptor.types.codecTypes.controlPlaneHooks;
for (const [codecId, hook] of Object.entries(controlPlaneHooks)) {
assertUniqueCodecOwner({
codecId,
owners,
descriptorId: descriptor.id,
entityLabel: "control hooks",
entityOwnershipLabel: "owner"
});
hooks.set(codecId, hook);
owners.set(codecId, descriptor.id);
}
}
return hooks;
}
//#endregion
//#region src/core/diff/verifier-disposition.ts
/**
* Classifies a codec `verifyType` hook finding into the target-neutral
* categories the framework grades. A storage type is a named type instance
* (e.g. a native enum); the only shape divergence it can carry is a change
* to its value set, so a paired mismatch always classifies as `valueDrift`.
*/
function classifyStorageTypeDiffIssue(issue) {
if (issueOutcome(issue) === "not-found") return "declaredMissing";
if (issueOutcome(issue) === "not-expected") return "extraAuxiliary";
return "valueDrift";
}
function verifierDisposition(controlPolicy, issue) {
return dispositionForCategory(controlPolicy, classifyStorageTypeDiffIssue(issue));
}
//#endregion
//#region src/core/diff/schema-verify.ts
function issueNode(issue) {
const node = issue.expected ?? issue.actual;
if (node === void 0) return void 0;
return blindCast(node);
}
/**
* Resolves an issue's framework-neutral {@link DiffSubjectGranularity} on
* demand, from the issue's node's `nodeKind` via the target-provided
* `granularityOf` map. The node carries only its `nodeKind` identity, never a
* classification, and nothing is stamped onto the issue — every consumer
* (the family verdict below, the framework aggregate's unclaimed-elements
* sweep via {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable})
* calls this the same way, resolved by the family/target that owns the node
* vocabulary. `undefined` for an issue with no node.
*/
function classifyDiffSubjectGranularity(issue, granularityOf) {
const node = issueNode(issue);
return node === void 0 ? void 0 : granularityOf(node.nodeKind);
}
/**
* Resolves an issue's storage `entityKind` on demand, from the issue's
* node's `nodeKind` via the target-provided `entityKindOf` map — the sibling
* of {@link classifyDiffSubjectGranularity}, called the same way by the same
* consumers (via
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}).
* `undefined` for an issue with no node, or for a node kind with no storage
* entity of its own.
*/
function classifyDiffEntityKind(issue, entityKindOf) {
const node = issueNode(issue);
return node === void 0 ? void 0 : entityKindOf(node.nodeKind);
}
/**
* Re-keys the legacy `classifySqlVerifierIssueKind` category mapping on the
* issue's {@link DiffSubjectGranularity} (resolved via `granularityOf`) + the
* issue reason. The vocabulary maps one-to-one: an undeclared live entity or
* namespace is `extraTopLevelObject`, an undeclared live field
* `extraNestedElement`, undeclared auxiliaries (constraints, indexes,
* defaults) and structural leaves (policies) `extraAuxiliary`; a value-set
* drift on a check node is `valueDrift`; every other paired divergence is
* `declaredIncompatible`; anything the database lacks is `declaredMissing`.
* `granularityOf` is the target's classifier, so target and extension node
* kinds classify without the family importing them.
*/
function classifySqlDiffIssue(issue, granularityOf) {
if (issueOutcome(issue) === "not-found") return "declaredMissing";
if (issueOutcome(issue) === "not-expected") {
const granularity = classifyDiffSubjectGranularity(issue, granularityOf);
if (granularity === "entity" || granularity === "namespace") return "extraTopLevelObject";
if (granularity === "field") return "extraNestedElement";
return "extraAuxiliary";
}
if (issueNode(issue)?.nodeKind === RelationalSchemaNodeKind.check) return "valueDrift";
return "declaredIncompatible";
}
/**
* Whether a `not-expected` issue is a strict-mode-only finding. The legacy
* walk detected every relational extra (namespaces, entities, fields, and
* their auxiliaries) only under `--strict`; the structural diff (roots, RLS
* policies, roles) was never strict-gated — its extras fail in both modes.
* Keyed on the issue's granularity, resolved via `granularityOf`.
*/
function isStrictOnlyExtra(issue, granularityOf) {
const granularity = classifyDiffSubjectGranularity(issue, granularityOf);
return granularity === "namespace" || granularity === "entity" || granularity === "field" || granularity === "auxiliary";
}
/**
* Applies the two consumer filters to a diff issue list: strict gating
* (relational `not-expected` findings drop in lenient mode) and
* control-policy disposition (each surviving issue grades against its
* subject table's effective policy; suppressed issues drop, `observed`
* subjects warn). The verify verdict is `failures.length === 0`.
*/
function computeSqlDiffVerdict(input) {
const failures = [];
const warnings = [];
for (const issue of input.issues) {
if (!input.strict && issueOutcome(issue) === "not-expected" && isStrictOnlyExtra(issue, input.granularityOf)) continue;
const disposition = dispositionForCategory(effectiveControlPolicy(input.resolveControlPolicy(issue), input.defaultControlPolicy), classifySqlDiffIssue(issue, input.granularityOf));
if (disposition === "suppress") continue;
if (disposition === "warn") {
warnings.push(issue);
continue;
}
failures.push(issue);
}
return {
failures,
warnings
};
}
function computeStorageTypeVerdict(input) {
const failures = [];
const warnings = [];
const policy = effectiveControlPolicy(void 0, input.contract.defaultControlPolicy);
for (const pair of input.namespacePairs) {
if (pair.actual === void 0) continue;
for (const [typeName, typeInstance] of Object.entries(input.contract.storage.types ?? {})) {
if (!isStorageTypeInstance(typeInstance)) continue;
const hook = input.codecHooks.get(typeInstance.codecId);
if (!hook?.verifyType) continue;
const typeIssues = hook.verifyType({
typeName,
typeInstance,
schema: pair.actual
});
for (const issue of typeIssues) {
const disposition = verifierDisposition(policy, issue);
if (disposition === "suppress") continue;
if (disposition === "warn") {
warnings.push(issue);
continue;
}
failures.push(issue);
}
}
}
return {
failures,
warnings
};
}
/**
* THE SQL schema verify: runs the target's full-tree node diff, grades it
* through the family's post-diff filters (strict gating + control-policy
* disposition) plus the codec `verifyType` hook findings, and wraps the
* verdict in the issue-based result envelope. `ok` holds exactly when both
* issue lists are empty — the lists carry the verdict's failures.
*/
function verifySqlSchemaByDiff(input) {
const startTime = Date.now();
const verdictDiff = input.diffSchema({
contract: input.contract,
schema: input.schema,
frameworkComponents: input.frameworkComponents
});
const diffVerdict = computeSqlDiffVerdict({
issues: verdictDiff.issues,
resolveControlPolicy: verdictDiff.resolveControlPolicy,
strict: input.strict,
defaultControlPolicy: input.contract.defaultControlPolicy,
granularityOf: input.granularityOf
});
const storageTypeVerdict = computeStorageTypeVerdict({
contract: input.contract,
namespacePairs: verdictDiff.namespacePairs,
codecHooks: extractCodecControlHooks(input.frameworkComponents)
});
const failCount = diffVerdict.failures.length + storageTypeVerdict.failures.length;
const ok = failCount === 0;
const profileHash = "profileHash" in input.contract && typeof input.contract.profileHash === "string" ? input.contract.profileHash : void 0;
return {
ok,
...ok ? {} : { code: "CONTRACT.SCHEMA_VERIFICATION_FAILED" },
summary: ok ? "Database schema satisfies contract" : `Database schema does not satisfy contract (${failCount} failure${failCount === 1 ? "" : "s"})`,
contract: {
storageHash: input.contract.storage.storageHash,
...ifDefined("profileHash", profileHash)
},
target: {
expected: input.contract.target,
actual: input.contract.target
},
schema: {
issues: [...diffVerdict.failures, ...storageTypeVerdict.failures],
warnings: { issues: [...diffVerdict.warnings, ...storageTypeVerdict.warnings] }
},
meta: { strict: input.strict },
timings: { total: Date.now() - startTime }
};
}
//#endregion
export { computeStorageTypeVerdict as a, computeSqlDiffVerdict as i, classifyDiffSubjectGranularity as n, verifySqlSchemaByDiff as o, classifySqlDiffIssue as r, extractCodecControlHooks as s, classifyDiffEntityKind as t };
//# sourceMappingURL=schema-verify-DY21bMwy.mjs.map
{"version":3,"file":"schema-verify-DY21bMwy.mjs","names":["d"],"sources":["../src/core/assembly.ts","../src/core/diff/verifier-disposition.ts","../src/core/diff/schema-verify.ts"],"sourcesContent":["import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport { assertUniqueCodecOwner } from '@prisma-next/framework-components/control';\nimport type { CodecControlHooks } from './migrations/types';\n\ntype CodecControlHooksMap = Record<string, CodecControlHooks>;\n\nfunction hasCodecControlHooks(descriptor: unknown): descriptor is {\n readonly id: string;\n readonly types: {\n readonly codecTypes: {\n readonly controlPlaneHooks: CodecControlHooksMap;\n };\n };\n} {\n if (typeof descriptor !== 'object' || descriptor === null) {\n return false;\n }\n const d = descriptor as { types?: { codecTypes?: { controlPlaneHooks?: unknown } } };\n const hooks = d.types?.codecTypes?.controlPlaneHooks;\n return hooks !== null && hooks !== undefined && typeof hooks === 'object';\n}\n\nexport function extractCodecControlHooks(\n descriptors: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>,\n): Map<string, CodecControlHooks> {\n const hooks = new Map<string, CodecControlHooks>();\n const owners = new Map<string, string>();\n\n for (const descriptor of descriptors) {\n if (typeof descriptor !== 'object' || descriptor === null) {\n continue;\n }\n if (!hasCodecControlHooks(descriptor)) {\n continue;\n }\n const controlPlaneHooks = descriptor.types.codecTypes.controlPlaneHooks;\n for (const [codecId, hook] of Object.entries(controlPlaneHooks)) {\n assertUniqueCodecOwner({\n codecId,\n owners,\n descriptorId: descriptor.id,\n entityLabel: 'control hooks',\n entityOwnershipLabel: 'owner',\n });\n hooks.set(codecId, hook);\n owners.set(codecId, descriptor.id);\n }\n }\n\n return hooks;\n}\n","import type { ControlPolicy } from '@prisma-next/contract/types';\nimport type {\n SchemaDiffIssue,\n VerifierIssueCategory,\n VerifierOutcome,\n} from '@prisma-next/framework-components/control';\nimport { dispositionForCategory, issueOutcome } from '@prisma-next/framework-components/control';\n\n/**\n * Classifies a codec `verifyType` hook finding into the target-neutral\n * categories the framework grades. A storage type is a named type instance\n * (e.g. a native enum); the only shape divergence it can carry is a change\n * to its value set, so a paired mismatch always classifies as `valueDrift`.\n */\nexport function classifyStorageTypeDiffIssue(issue: SchemaDiffIssue): VerifierIssueCategory {\n if (issueOutcome(issue) === 'not-found') {\n return 'declaredMissing';\n }\n if (issueOutcome(issue) === 'not-expected') {\n return 'extraAuxiliary';\n }\n return 'valueDrift';\n}\n\nexport function verifierDisposition(\n controlPolicy: ControlPolicy,\n issue: SchemaDiffIssue,\n): VerifierOutcome {\n return dispositionForCategory(controlPolicy, classifyStorageTypeDiffIssue(issue));\n}\n","/**\n * The differ-based SQL schema verify: post-diff filters and verdict.\n *\n * The generic node differ (`diffSchemas`) reports every node-level\n * difference between the derived expected tree and the introspected actual\n * tree. This module is the consumer side the spec assigns to the SQL\n * family: strict-mode extras gating and control-policy disposition are\n * reason/kind-keyed filters applied AFTER the diff — never inside it — and\n * the verify verdict derives from the filtered issue list.\n *\n * `verifySqlSchemaByDiff` wraps the verdict in the issue-based result\n * envelope — this is THE SQL schema verify (the legacy relational walk and\n * its verification tree are retired).\n */\n\nimport type { Contract, ControlPolicy } from '@prisma-next/contract/types';\nimport { effectiveControlPolicy } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n DiffSubjectGranularity,\n SchemaDiffIssue,\n VerifierIssueCategory,\n VerifierOutcome,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { dispositionForCategory, issueOutcome } from '@prisma-next/framework-components/control';\nimport { isStorageTypeInstance, type SqlStorage } from '@prisma-next/sql-contract/types';\nimport { RelationalSchemaNodeKind, type SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { extractCodecControlHooks } from '../assembly';\nimport type { SqlSchemaDiffFn } from '../migrations/schema-differ';\nimport type { CodecControlHooks } from '../migrations/types';\nimport { verifierDisposition } from './verifier-disposition';\n\n// ============================================================================\n// Subject-granularity classification — nodeKind → framework-neutral granularity\n// ============================================================================\n\nfunction issueNode(issue: SchemaDiffIssue): SqlSchemaIRNode | undefined {\n const node = issue.expected ?? issue.actual;\n if (node === undefined) return undefined;\n return blindCast<\n SqlSchemaIRNode,\n 'every node in a SQL schema diff tree is a SqlSchemaIRNode; nodeKind is its identity'\n >(node);\n}\n\n/**\n * Resolves an issue's framework-neutral {@link DiffSubjectGranularity} on\n * demand, from the issue's node's `nodeKind` via the target-provided\n * `granularityOf` map. The node carries only its `nodeKind` identity, never a\n * classification, and nothing is stamped onto the issue — every consumer\n * (the family verdict below, the framework aggregate's unclaimed-elements\n * sweep via {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable})\n * calls this the same way, resolved by the family/target that owns the node\n * vocabulary. `undefined` for an issue with no node.\n */\nexport function classifyDiffSubjectGranularity(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): DiffSubjectGranularity | undefined {\n const node = issueNode(issue);\n return node === undefined ? undefined : granularityOf(node.nodeKind);\n}\n\n/**\n * Resolves an issue's storage `entityKind` on demand, from the issue's\n * node's `nodeKind` via the target-provided `entityKindOf` map — the sibling\n * of {@link classifyDiffSubjectGranularity}, called the same way by the same\n * consumers (via\n * {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}).\n * `undefined` for an issue with no node, or for a node kind with no storage\n * entity of its own.\n */\nexport function classifyDiffEntityKind(\n issue: SchemaDiffIssue,\n entityKindOf: (nodeKind: string) => string | undefined,\n): string | undefined {\n const node = issueNode(issue);\n return node === undefined ? undefined : entityKindOf(node.nodeKind);\n}\n\n// ============================================================================\n// Issue classification — subject granularity + reason → target-neutral category\n// ============================================================================\n\n/**\n * Re-keys the legacy `classifySqlVerifierIssueKind` category mapping on the\n * issue's {@link DiffSubjectGranularity} (resolved via `granularityOf`) + the\n * issue reason. The vocabulary maps one-to-one: an undeclared live entity or\n * namespace is `extraTopLevelObject`, an undeclared live field\n * `extraNestedElement`, undeclared auxiliaries (constraints, indexes,\n * defaults) and structural leaves (policies) `extraAuxiliary`; a value-set\n * drift on a check node is `valueDrift`; every other paired divergence is\n * `declaredIncompatible`; anything the database lacks is `declaredMissing`.\n * `granularityOf` is the target's classifier, so target and extension node\n * kinds classify without the family importing them.\n */\nexport function classifySqlDiffIssue(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): VerifierIssueCategory {\n if (issueOutcome(issue) === 'not-found') {\n return 'declaredMissing';\n }\n if (issueOutcome(issue) === 'not-expected') {\n const granularity = classifyDiffSubjectGranularity(issue, granularityOf);\n if (granularity === 'entity' || granularity === 'namespace') {\n return 'extraTopLevelObject';\n }\n if (granularity === 'field') {\n return 'extraNestedElement';\n }\n return 'extraAuxiliary';\n }\n if (issueNode(issue)?.nodeKind === RelationalSchemaNodeKind.check) {\n return 'valueDrift';\n }\n return 'declaredIncompatible';\n}\n\n/**\n * Whether a `not-expected` issue is a strict-mode-only finding. The legacy\n * walk detected every relational extra (namespaces, entities, fields, and\n * their auxiliaries) only under `--strict`; the structural diff (roots, RLS\n * policies, roles) was never strict-gated — its extras fail in both modes.\n * Keyed on the issue's granularity, resolved via `granularityOf`.\n */\nfunction isStrictOnlyExtra(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): boolean {\n const granularity = classifyDiffSubjectGranularity(issue, granularityOf);\n return (\n granularity === 'namespace' ||\n granularity === 'entity' ||\n granularity === 'field' ||\n granularity === 'auxiliary'\n );\n}\n\n// ============================================================================\n// The post-diff filter + verdict\n// ============================================================================\n\nexport interface SqlDiffVerdictInput {\n /** The full, ownership-scoped diff issue list from the target's differ. */\n readonly issues: readonly SchemaDiffIssue[];\n /** Resolves a diff issue's subject table's declared control policy directly from the contract. */\n readonly resolveControlPolicy: (issue: SchemaDiffIssue) => ControlPolicy | undefined;\n readonly strict: boolean;\n readonly defaultControlPolicy: ControlPolicy | undefined;\n /** The target's classifier: a diff issue node's `nodeKind` → its subject granularity. */\n readonly granularityOf: (nodeKind: string) => DiffSubjectGranularity;\n}\n\nexport interface SqlDiffVerdict {\n readonly failures: readonly SchemaDiffIssue[];\n readonly warnings: readonly SchemaDiffIssue[];\n}\n\n/**\n * Applies the two consumer filters to a diff issue list: strict gating\n * (relational `not-expected` findings drop in lenient mode) and\n * control-policy disposition (each surviving issue grades against its\n * subject table's effective policy; suppressed issues drop, `observed`\n * subjects warn). The verify verdict is `failures.length === 0`.\n */\nexport function computeSqlDiffVerdict(input: SqlDiffVerdictInput): SqlDiffVerdict {\n const failures: SchemaDiffIssue[] = [];\n const warnings: SchemaDiffIssue[] = [];\n for (const issue of input.issues) {\n if (\n !input.strict &&\n issueOutcome(issue) === 'not-expected' &&\n isStrictOnlyExtra(issue, input.granularityOf)\n ) {\n continue;\n }\n const tablePolicy = input.resolveControlPolicy(issue);\n const policy = effectiveControlPolicy(tablePolicy, input.defaultControlPolicy);\n const disposition: VerifierOutcome = dispositionForCategory(\n policy,\n classifySqlDiffIssue(issue, input.granularityOf),\n );\n if (disposition === 'suppress') continue;\n if (disposition === 'warn') {\n warnings.push(issue);\n continue;\n }\n failures.push(issue);\n }\n return { failures, warnings };\n}\n\n// ============================================================================\n// Storage-types check — the codec verifyType hook path\n// ============================================================================\n\nexport interface StorageTypeVerdictInput {\n readonly contract: Contract<SqlStorage>;\n /**\n * Expected/actual namespace-node pairs the target's differ input produced:\n * for a namespaced tree, one entry per expected namespace with a\n * non-empty table set, paired by DDL schema name (absent actual side for\n * a schema the database lacks); a flat tree is the sole pair.\n */\n readonly namespacePairs: ReadonlyArray<{\n readonly actual: SqlSchemaIRNode | undefined;\n }>;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n}\n\n/**\n * Runs the codec `verifyType` hooks the way the legacy walk did: once per\n * contract namespace with tables, against that namespace's paired actual\n * node (the hook reads namespace-scoped state such as `enums` off it).\n * Issue dispositions grade against the contract default policy, matching\n * the legacy `pushTypeNode` semantics.\n */\nexport interface StorageTypeVerdict {\n readonly failures: readonly SchemaDiffIssue[];\n readonly warnings: readonly SchemaDiffIssue[];\n}\n\nexport function computeStorageTypeVerdict(input: StorageTypeVerdictInput): StorageTypeVerdict {\n const failures: SchemaDiffIssue[] = [];\n const warnings: SchemaDiffIssue[] = [];\n const policy = effectiveControlPolicy(undefined, input.contract.defaultControlPolicy);\n for (const pair of input.namespacePairs) {\n if (pair.actual === undefined) continue;\n for (const [typeName, typeInstance] of Object.entries(input.contract.storage.types ?? {})) {\n if (!isStorageTypeInstance(typeInstance)) continue;\n const hook = input.codecHooks.get(typeInstance.codecId);\n if (!hook?.verifyType) continue;\n const typeIssues = hook.verifyType({ typeName, typeInstance, schema: pair.actual });\n for (const issue of typeIssues) {\n const disposition = verifierDisposition(policy, issue);\n if (disposition === 'suppress') continue;\n if (disposition === 'warn') {\n warnings.push(issue);\n continue;\n }\n failures.push(issue);\n }\n }\n }\n return { failures, warnings };\n}\n\n// ============================================================================\n// The issue-based verify envelope\n// ============================================================================\n\nexport interface VerifySqlSchemaByDiffInput {\n readonly contract: Contract<SqlStorage>;\n readonly schema: SqlSchemaIRNode;\n readonly strict: boolean;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n /** The target's full-tree node diff (`diffSchema` descriptor hook). */\n readonly diffSchema: SqlSchemaDiffFn;\n /** The target's classifier: a diff issue node's `nodeKind` → its subject granularity. */\n readonly granularityOf: (nodeKind: string) => DiffSubjectGranularity;\n}\n\n/**\n * THE SQL schema verify: runs the target's full-tree node diff, grades it\n * through the family's post-diff filters (strict gating + control-policy\n * disposition) plus the codec `verifyType` hook findings, and wraps the\n * verdict in the issue-based result envelope. `ok` holds exactly when both\n * issue lists are empty — the lists carry the verdict's failures.\n */\nexport function verifySqlSchemaByDiff(\n input: VerifySqlSchemaByDiffInput,\n): VerifyDatabaseSchemaResult {\n const startTime = Date.now();\n const verdictDiff = input.diffSchema({\n contract: input.contract,\n schema: input.schema,\n frameworkComponents: input.frameworkComponents,\n });\n const diffVerdict = computeSqlDiffVerdict({\n issues: verdictDiff.issues,\n resolveControlPolicy: verdictDiff.resolveControlPolicy,\n strict: input.strict,\n defaultControlPolicy: input.contract.defaultControlPolicy,\n granularityOf: input.granularityOf,\n });\n const storageTypeVerdict = computeStorageTypeVerdict({\n contract: input.contract,\n namespacePairs: verdictDiff.namespacePairs,\n codecHooks: extractCodecControlHooks(input.frameworkComponents),\n });\n const failCount = diffVerdict.failures.length + storageTypeVerdict.failures.length;\n const ok = failCount === 0;\n const profileHash =\n 'profileHash' in input.contract && typeof input.contract.profileHash === 'string'\n ? input.contract.profileHash\n : undefined;\n return {\n ok,\n ...(ok ? {} : { code: 'CONTRACT.SCHEMA_VERIFICATION_FAILED' }),\n summary: ok\n ? 'Database schema satisfies contract'\n : `Database schema does not satisfy contract (${failCount} failure${failCount === 1 ? '' : 's'})`,\n contract: {\n storageHash: input.contract.storage.storageHash,\n ...ifDefined('profileHash', profileHash),\n },\n target: {\n expected: input.contract.target,\n actual: input.contract.target,\n },\n schema: {\n issues: [...diffVerdict.failures, ...storageTypeVerdict.failures],\n warnings: {\n issues: [...diffVerdict.warnings, ...storageTypeVerdict.warnings],\n },\n },\n meta: { strict: input.strict },\n timings: { total: Date.now() - startTime },\n };\n}\n"],"mappings":"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"}
//#region src/core/timestamp-now-generator.ts
/**
* Canonical id for the wall-clock-now mutation default generator.
*
* Owned by `family-sql` because that's where the generator lives. The
* id flows out from here to (1) the control-plane descriptor and the
* temporal field-preset pair below, (2) the runtime-plane sibling
* `timestamp-now-runtime-generator.ts`, and (3) authoring surfaces
* (PSL `temporal.updatedAt()`, TS `field.temporal.updatedAt()`) via
* the descriptor flow. Co-locating the constant with its only owner
* keeps the framework layer free of concrete generator ids.
*/
const TIMESTAMP_NOW_GENERATOR_ID = "timestampNow";
/**
* Builds the canonical control-plane descriptor for the wall-clock-now
* mutation default generator. The descriptor's `id` and `buildPhases`
* are target-agnostic so PSL `temporal.updatedAt()` and TS
* `field.temporal.updatedAt()` lower to byte-identical contracts.
*
* `applicableCodecIds` is omitted: `timestampNow` is preset-only (not
* reachable via `@default(timestampNow())` lowering), and the codec is
* co-registered by the preset descriptor itself, so the
* `@default(...)` compatibility check has no role to play here.
*/
function timestampNowControlDescriptor() {
return {
id: TIMESTAMP_NOW_GENERATOR_ID,
buildPhases: () => ({
onCreate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
},
onUpdate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
}
})
};
}
/**
* Builds the canonical `temporal.{createdAt,updatedAt}` field-preset pair
* for a SQL target. `createdAt` lowers to a `now()` storage default;
* `updatedAt` lowers to the `timestampNow` execution generator on both
* `onCreate` and `onUpdate` (RD: "last modified time", non-null). Targets
* supply the codec/native-type pair that matches their timestamp column;
* everything else is shared so PSL `temporal.updatedAt()` and TS
* `field.temporal.updatedAt()` lower to byte-identical contracts across
* targets by construction.
*/
/* @__NO_SIDE_EFFECTS__ */
function temporalAuthoringPresets(input) {
const { codecId, nativeType } = input;
return {
createdAt: {
kind: "fieldPreset",
output: {
codecId,
nativeType,
default: {
kind: "function",
expression: "now()"
}
}
},
updatedAt: {
kind: "fieldPreset",
output: {
codecId,
nativeType,
executionDefaults: {
onCreate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
},
onUpdate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
}
}
}
}
};
}
const TEMPORAL_PRECISION_ARG = {
name: "precision",
kind: "number",
optional: true,
integer: true,
minimum: 0
};
const TEMPORAL_ON_CREATE_ARG = {
name: "onCreate",
kind: "option",
values: ["now"],
optional: true
};
const TEMPORAL_ON_UPDATE_ARG = {
name: "onUpdate",
kind: "option",
values: ["now"],
optional: true
};
/**
* Selects the `timestampNow` generator descriptor for the preset's `now`
* token. The token is preset vocabulary; the generator id never appears in a
* user's spelling (ADR 169 — `timestampNow` is preset-only).
*/
function temporalPhaseTemplate(index) {
return {
kind: "select",
index,
cases: { now: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
} }
};
}
/**
* Builds a `temporal.<codec>` field preset for a codec that takes a precision
* parameter (`pg/timestamp@1`, `pg/timestamptz@1`). Arguments change field
* properties only — never the codec, which the caller fixes here.
*
* All three arguments are optional: omitting `precision` omits `typeParams`
* entirely, and omitting a phase omits that phase (both omitted omits
* `executionDefaults`).
*/
/* @__NO_SIDE_EFFECTS__ */
function temporalCodecPresetWithPrecision(input) {
return {
kind: "fieldPreset",
args: [
TEMPORAL_PRECISION_ARG,
TEMPORAL_ON_CREATE_ARG,
TEMPORAL_ON_UPDATE_ARG
],
output: {
codecId: input.codecId,
nativeType: input.nativeType,
typeParams: { precision: {
kind: "arg",
index: 0
} },
executionDefaults: {
onCreate: temporalPhaseTemplate(1),
onUpdate: temporalPhaseTemplate(2)
}
}
};
}
/**
* Builds a `temporal.<codec>` field preset for a codec with no type
* parameters (`sqlite/datetime@1`). As with the precision-bearing variant,
* both phase arguments are optional and omitting one omits that phase.
*/
/* @__NO_SIDE_EFFECTS__ */
function temporalCodecPreset(input) {
return {
kind: "fieldPreset",
args: [TEMPORAL_ON_CREATE_ARG, TEMPORAL_ON_UPDATE_ARG],
output: {
codecId: input.codecId,
nativeType: input.nativeType,
executionDefaults: {
onCreate: temporalPhaseTemplate(0),
onUpdate: temporalPhaseTemplate(1)
}
}
};
}
//#endregion
export { timestampNowControlDescriptor as a, temporalCodecPresetWithPrecision as i, temporalAuthoringPresets as n, temporalCodecPreset as r, TIMESTAMP_NOW_GENERATOR_ID as t };
//# sourceMappingURL=timestamp-now-generator-DRXygu32.mjs.map
{"version":3,"file":"timestamp-now-generator-DRXygu32.mjs","names":[],"sources":["../src/core/timestamp-now-generator.ts"],"sourcesContent":["import type { AuthoringFieldPresetDescriptor } from '@prisma-next/framework-components/authoring';\nimport type { MutationDefaultGeneratorDescriptor } from '@prisma-next/framework-components/control';\n\n/**\n * Canonical id for the wall-clock-now mutation default generator.\n *\n * Owned by `family-sql` because that's where the generator lives. The\n * id flows out from here to (1) the control-plane descriptor and the\n * temporal field-preset pair below, (2) the runtime-plane sibling\n * `timestamp-now-runtime-generator.ts`, and (3) authoring surfaces\n * (PSL `temporal.updatedAt()`, TS `field.temporal.updatedAt()`) via\n * the descriptor flow. Co-locating the constant with its only owner\n * keeps the framework layer free of concrete generator ids.\n */\nexport const TIMESTAMP_NOW_GENERATOR_ID = 'timestampNow' as const;\n\n/**\n * Builds the canonical control-plane descriptor for the wall-clock-now\n * mutation default generator. The descriptor's `id` and `buildPhases`\n * are target-agnostic so PSL `temporal.updatedAt()` and TS\n * `field.temporal.updatedAt()` lower to byte-identical contracts.\n *\n * `applicableCodecIds` is omitted: `timestampNow` is preset-only (not\n * reachable via `@default(timestampNow())` lowering), and the codec is\n * co-registered by the preset descriptor itself, so the\n * `@default(...)` compatibility check has no role to play here.\n */\nexport function timestampNowControlDescriptor(): MutationDefaultGeneratorDescriptor {\n return {\n id: TIMESTAMP_NOW_GENERATOR_ID,\n buildPhases: () => ({\n onCreate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n onUpdate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n }),\n };\n}\n\n/**\n * Builds the canonical `temporal.{createdAt,updatedAt}` field-preset pair\n * for a SQL target. `createdAt` lowers to a `now()` storage default;\n * `updatedAt` lowers to the `timestampNow` execution generator on both\n * `onCreate` and `onUpdate` (RD: \"last modified time\", non-null). Targets\n * supply the codec/native-type pair that matches their timestamp column;\n * everything else is shared so PSL `temporal.updatedAt()` and TS\n * `field.temporal.updatedAt()` lower to byte-identical contracts across\n * targets by construction.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function temporalAuthoringPresets<\n const CodecId extends string,\n const NativeType extends string,\n>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {\n const { codecId, nativeType } = input;\n return {\n createdAt: {\n kind: 'fieldPreset',\n output: {\n codecId,\n nativeType,\n default: { kind: 'function', expression: 'now()' },\n },\n },\n updatedAt: {\n kind: 'fieldPreset',\n output: {\n codecId,\n nativeType,\n executionDefaults: {\n onCreate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n onUpdate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n },\n },\n },\n } as const satisfies Record<string, AuthoringFieldPresetDescriptor>;\n}\n\nconst TEMPORAL_PRECISION_ARG = {\n name: 'precision',\n kind: 'number',\n optional: true,\n integer: true,\n minimum: 0,\n} as const;\n\nconst TEMPORAL_ON_CREATE_ARG = {\n name: 'onCreate',\n kind: 'option',\n values: ['now'],\n optional: true,\n} as const;\n\nconst TEMPORAL_ON_UPDATE_ARG = {\n name: 'onUpdate',\n kind: 'option',\n values: ['now'],\n optional: true,\n} as const;\n\n/**\n * Selects the `timestampNow` generator descriptor for the preset's `now`\n * token. The token is preset vocabulary; the generator id never appears in a\n * user's spelling (ADR 169 — `timestampNow` is preset-only).\n */\nfunction temporalPhaseTemplate<const Index extends number>(index: Index) {\n return {\n kind: 'select',\n index,\n cases: { now: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID } },\n } as const;\n}\n\n/**\n * Builds a `temporal.<codec>` field preset for a codec that takes a precision\n * parameter (`pg/timestamp@1`, `pg/timestamptz@1`). Arguments change field\n * properties only — never the codec, which the caller fixes here.\n *\n * All three arguments are optional: omitting `precision` omits `typeParams`\n * entirely, and omitting a phase omits that phase (both omitted omits\n * `executionDefaults`).\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function temporalCodecPresetWithPrecision<\n const CodecId extends string,\n const NativeType extends string,\n>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {\n return {\n kind: 'fieldPreset',\n args: [TEMPORAL_PRECISION_ARG, TEMPORAL_ON_CREATE_ARG, TEMPORAL_ON_UPDATE_ARG],\n output: {\n codecId: input.codecId,\n nativeType: input.nativeType,\n typeParams: { precision: { kind: 'arg', index: 0 } },\n executionDefaults: {\n onCreate: temporalPhaseTemplate(1),\n onUpdate: temporalPhaseTemplate(2),\n },\n },\n } as const satisfies AuthoringFieldPresetDescriptor;\n}\n\n/**\n * Builds a `temporal.<codec>` field preset for a codec with no type\n * parameters (`sqlite/datetime@1`). As with the precision-bearing variant,\n * both phase arguments are optional and omitting one omits that phase.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function temporalCodecPreset<\n const CodecId extends string,\n const NativeType extends string,\n>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {\n return {\n kind: 'fieldPreset',\n args: [TEMPORAL_ON_CREATE_ARG, TEMPORAL_ON_UPDATE_ARG],\n output: {\n codecId: input.codecId,\n nativeType: input.nativeType,\n executionDefaults: {\n onCreate: temporalPhaseTemplate(0),\n onUpdate: temporalPhaseTemplate(1),\n },\n },\n } as const satisfies AuthoringFieldPresetDescriptor;\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,6BAA6B;;;;;;;;;;;;AAa1C,SAAgB,gCAAoE;CAClF,OAAO;EACL,IAAI;EACJ,oBAAoB;GAClB,UAAU;IAAE,MAAM;IAAa,IAAI;GAA2B;GAC9D,UAAU;IAAE,MAAM;IAAa,IAAI;GAA2B;EAChE;CACF;AACF;;;;;;;;;;;;AAaA,SAAgB,yBAGd,OAAuE;CACvE,MAAM,EAAE,SAAS,eAAe;CAChC,OAAO;EACL,WAAW;GACT,MAAM;GACN,QAAQ;IACN;IACA;IACA,SAAS;KAAE,MAAM;KAAY,YAAY;IAAQ;GACnD;EACF;EACA,WAAW;GACT,MAAM;GACN,QAAQ;IACN;IACA;IACA,mBAAmB;KACjB,UAAU;MAAE,MAAM;MAAa,IAAI;KAA2B;KAC9D,UAAU;MAAE,MAAM;MAAa,IAAI;KAA2B;IAChE;GACF;EACF;CACF;AACF;AAEA,MAAM,yBAAyB;CAC7B,MAAM;CACN,MAAM;CACN,UAAU;CACV,SAAS;CACT,SAAS;AACX;AAEA,MAAM,yBAAyB;CAC7B,MAAM;CACN,MAAM;CACN,QAAQ,CAAC,KAAK;CACd,UAAU;AACZ;AAEA,MAAM,yBAAyB;CAC7B,MAAM;CACN,MAAM;CACN,QAAQ,CAAC,KAAK;CACd,UAAU;AACZ;;;;;;AAOA,SAAS,sBAAkD,OAAc;CACvE,OAAO;EACL,MAAM;EACN;EACA,OAAO,EAAE,KAAK;GAAE,MAAM;GAAa,IAAI;EAA2B,EAAE;CACtE;AACF;;;;;;;;;;;AAYA,SAAgB,iCAGd,OAAuE;CACvE,OAAO;EACL,MAAM;EACN,MAAM;GAAC;GAAwB;GAAwB;EAAsB;EAC7E,QAAQ;GACN,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,YAAY,EAAE,WAAW;IAAE,MAAM;IAAO,OAAO;GAAE,EAAE;GACnD,mBAAmB;IACjB,UAAU,sBAAsB,CAAC;IACjC,UAAU,sBAAsB,CAAC;GACnC;EACF;CACF;AACF;;;;;;;AAQA,SAAgB,oBAGd,OAAuE;CACvE,OAAO;EACL,MAAM;EACN,MAAM,CAAC,wBAAwB,sBAAsB;EACrD,QAAQ;GACN,SAAS,MAAM;GACf,YAAY,MAAM;GAClB,mBAAmB;IACjB,UAAU,sBAAsB,CAAC;IACjC,UAAU,sBAAsB,CAAC;GACnC;EACF;CACF;AACF"}
import { r as SqlControlAdapter } from "./control-adapter-CW7FdxeS.mjs";
import { ContractSpace, ControlAdapterDescriptor, ControlExtensionDescriptor, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlannerConflict, MigrationPlannerFailureResult, MigrationPlannerSuccessResult, MigrationRunnerExecutionChecks, MigrationRunnerFailure, MigrationRunnerPerSpaceSuccessValue, MigrationRunnerResult, OpFactoryCall, OperationContext, SchemaDiffIssue, SchemaOwnership } from "@prisma-next/framework-components/control";
import { Contract } from "@prisma-next/contract/types";
import { SqlControlDriverInstance, SqlStorage, StorageColumn, StorageTable, StorageTypeInstance } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { Result } from "@prisma-next/utils/result";
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
import { AggregateMigrationEdgeRef } from "@prisma-next/migration-tools/aggregate";
import { SqlOperationDescriptors } from "@prisma-next/sql-operations";
//#region src/core/migrations/types.d.ts
type AnyRecord = Readonly<Record<string, unknown>>;
interface StorageTypePlanResult<TTargetDetails> {
readonly operations: readonly SqlMigrationPlanOperation<TTargetDetails>[];
}
/**
* Input for expanding parameterized native types.
*/
interface ExpandNativeTypeInput {
readonly nativeType: string;
readonly codecId?: string;
readonly typeParams?: Record<string, unknown>;
}
/**
* Input for resolving an identity-value SQL literal used to backfill existing rows when
* adding a NOT NULL column without an explicit default.
*
* "Identity value" in the algebraic (monoid) sense: the neutral element for the type
* (0 for numbers, '' for strings, false for booleans, etc.).
*/
interface ResolveIdentityValueInput {
readonly nativeType: string;
readonly codecId?: string;
readonly typeParams?: Record<string, unknown>;
}
/**
* Per-field lifecycle event a codec hook can react to.
*
* Fired during app-space migration emission as the SQL family diffs the
* prior contract against the new contract. See
* `docs/architecture docs/adrs/ADR 213 - Codec lifecycle hooks.md`
* for the wiring contract.
*
* - `'added'` — the field is present in the new contract but not the prior.
* - `'dropped'` — the field is present in the prior contract but not the new.
* - `'altered'` — the field is present in both and any property other than
* `codecId` differs. Codec-id changes are a v1 non-goal:
* when only `codecId` differs, no `'altered'` event fires.
*/
type FieldEvent = 'added' | 'dropped' | 'altered';
/**
* Context passed to {@link CodecControlHooks.onFieldEvent}.
*
* `namespaceId`, `tableName`, and `fieldName` are always populated; `priorTable` /
* `priorField` carry the prior contract's view of the table and column
* (present for `'dropped'` and `'altered'`); `newTable` / `newField`
* carry the new contract's view (present for `'added'` and `'altered'`).
*
* The hook only ever receives app-space contract IR — extension-space
* fields are scoped out by the API: the hook is wired at the
* application emitter only.
*/
interface FieldEventContext {
readonly namespaceId: string;
readonly tableName: string;
readonly fieldName: string;
readonly priorTable?: StorageTable;
readonly newTable?: StorageTable;
readonly priorField?: StorageColumn;
readonly newField?: StorageColumn;
}
interface CodecControlHooks<TTargetDetails = unknown> {
/**
* `schema` is typed as the family-level `SqlSchemaIRNode` (not the concrete
* `SqlSchemaIR` class) because the actual value handed in is whatever
* per-namespace node the calling target's tree shape produces — a flat
* `SqlSchemaIR` for SQLite, a `PostgresNamespaceSchemaNode` for Postgres —
* read structurally for its `tables`/`nativeEnums` fields. Hooks that need
* the concrete Postgres shape narrow via `PostgresNamespaceSchemaNode.is(schema)`.
*/
planTypeOperations?: (options: {
readonly typeName: string;
readonly typeInstance: StorageTypeInstance;
readonly contract: Contract<SqlStorage>;
readonly schema: SqlSchemaIRNode;
readonly schemaName?: string;
readonly policy: MigrationOperationPolicy;
}) => StorageTypePlanResult<TTargetDetails>;
verifyType?: (options: {
readonly typeName: string;
readonly typeInstance: StorageTypeInstance;
readonly schema: SqlSchemaIRNode;
readonly schemaName?: string;
}) => readonly SchemaDiffIssue[];
introspectTypes?: (options: {
readonly driver: SqlControlDriverInstance<string>;
readonly schemaName?: string;
}) => Promise<Record<string, StorageTypeInstance>>;
/**
* Expands a parameterized native type to its full SQL representation.
* Used by schema verification to compare contract types against database types.
*
* For example, expands:
* - { nativeType: 'character varying', typeParams: { length: 255 } } -> 'character varying(255)'
* - { nativeType: 'numeric', typeParams: { precision: 10, scale: 2 } } -> 'numeric(10,2)'
*
* Returns the expanded type string, or the original nativeType if no expansion is needed.
*/
expandNativeType?: (input: ExpandNativeTypeInput) => string;
/**
* Resolves the identity value (monoid neutral element) as a SQL literal for safely adding
* a NOT NULL column without an explicit default to a non-empty table.
*
* Return semantics:
* - string: use this literal
* - null: explicitly no safe identity value is known; fall back to another strategy
* - undefined: no opinion; planner may use built-in fallbacks
*/
resolveIdentityValue?: (input: ResolveIdentityValueInput) => string | null | undefined;
/**
* Reacts to per-field added / dropped / altered events as the app-space
* emitter diffs the prior contract against the new contract. Returned
* ops are inlined into the app-space migration's `ops.json` alongside
* the user's structural ops.
*
* Synchronous. Each returned op must carry its own `invariantId`. Hooks
* are dispatched per `(table, field)` based on the field's `codecId`
* (the new field's codec for `'added'` / `'altered'`; the prior field's
* codec for `'dropped'`).
*
* See `docs/architecture docs/adrs/ADR 213 - Codec lifecycle hooks.md`
* for the wiring contract and the deterministic ordering rule.
*/
onFieldEvent?: (event: FieldEvent, ctx: FieldEventContext) => readonly OpFactoryCall[];
}
interface SqlControlExtensionDescriptor<TTargetId extends string> extends ControlExtensionDescriptor<'sql', TTargetId> {
readonly queryOperations?: () => SqlOperationDescriptors;
/**
* Schema-contributing extensions opt into the per-space planner / runner /
* verifier by setting this field. Extensions without it are codec-only or
* query-ops-only — today's behaviour preserved.
*
* The shape comes from `@prisma-next/framework-components/control`
* (`ContractSpace`) — contract-space identity is a framework concept,
* not a SQL-specific one. The SQL family specialises the generic to
* `Contract<SqlStorage>` so descriptor authors continue to see a
* typed contract value.
*/
readonly contractSpace?: ContractSpace<Contract<SqlStorage>>;
}
interface SqlControlAdapterDescriptor<TTargetId extends string> extends ControlAdapterDescriptor<'sql', TTargetId, SqlControlAdapter<TTargetId>> {
readonly queryOperations?: () => SqlOperationDescriptors;
}
interface SqlMigrationPlanOperationStep {
readonly description: string;
readonly sql: string;
/**
* Optional parameter values bound at execution time. The runner forwards
* these to `driver.query(sql, params ?? [])`, so step authors can use
* placeholder syntax (`$1`, `$2`, …) instead of inlining literals into
* the SQL string. Reuses the driver's parameter binder rather than
* rolling per-target literal serialization for every type the planner
* may emit.
*/
readonly params?: readonly unknown[];
readonly meta?: AnyRecord;
}
/**
* Minimal shape every SQL-family target must conform to for its per-operation
* `target.details` payload. Each SQL operation addresses a named database
* object in some schema; targets (Postgres, MySQL, SQLite, …) extend this
* shape with their own fields (e.g. Postgres adds `objectType` and optional
* `table`).
*/
interface SqlPlanTargetDetails {
readonly schema: string;
readonly name: string;
}
interface SqlMigrationPlanOperationTarget<TTargetDetails> {
readonly id: string;
readonly details?: TTargetDetails;
}
interface SqlMigrationPlanOperation<TTargetDetails> extends MigrationPlanOperation {
readonly summary?: string;
readonly target: SqlMigrationPlanOperationTarget<TTargetDetails>;
readonly precheck: readonly SqlMigrationPlanOperationStep[];
readonly execute: readonly SqlMigrationPlanOperationStep[];
readonly postcheck: readonly SqlMigrationPlanOperationStep[];
readonly meta?: AnyRecord;
}
interface SqlMigrationPlanContractInfo {
readonly storageHash: string;
readonly profileHash?: string;
}
interface SqlMigrationPlan<TTargetDetails> extends MigrationPlan {
/**
* Contract space this plan applies to. The runner uses this to key the
* `prisma_contract.marker` row it writes/reads (`space = <spaceId>`),
* so per-extension plans hit per-extension marker rows instead of all
* collapsing onto the app's row.
*
* App-plan callers pass `APP_SPACE_ID` (`'app'`); per-extension plans
* pass the extension's space id. Required at every call site so the
* type system surfaces every place that needs to thread the value
* (rather than letting an `?? APP_SPACE_ID` fall-through silently
* collapse per-space markers onto the `'app'` row).
*
* @see specs/framework-mechanism.spec.md § 2.
*/
readonly spaceId: string;
/**
* Origin contract identity that the plan expects the database to currently be at.
* If omitted or null, the runner skips origin validation entirely.
*/
readonly origin?: SqlMigrationPlanContractInfo | null;
/**
* Destination contract identity that the plan intends to reach.
*/
readonly destination: SqlMigrationPlanContractInfo;
readonly operations: readonly (SqlMigrationPlanOperation<TTargetDetails> | Promise<SqlMigrationPlanOperation<TTargetDetails>>)[];
/**
* Sorted, deduplicated invariant ids declared by this plan's data-transform
* ops. Required at the SQL-family layer (the SQL runners consume this as
* the source of truth for marker writes and self-edge no-op checks); the
* framework-level {@link MigrationPlan.providedInvariants} stays optional
* because `db init` / `db update` plans don't have a corresponding
* migration manifest.
*/
readonly providedInvariants: readonly string[];
readonly meta?: AnyRecord;
}
type SqlPlannerConflictKind = 'typeMismatch' | 'nullabilityConflict' | 'indexIncompatible' | 'foreignKeyConflict' | 'missingButNonAdditive' | 'unsupportedOperation' | 'controlPolicySuppressedCall';
interface SqlPlannerConflictLocation {
readonly namespaceId?: string;
readonly entityKind?: string;
readonly entityName?: string;
readonly column?: string;
readonly index?: string;
readonly constraint?: string;
}
interface SqlPlannerConflict extends MigrationPlannerConflict {
readonly kind: SqlPlannerConflictKind;
readonly location?: SqlPlannerConflictLocation;
readonly meta?: AnyRecord;
}
interface SqlPlannerSuccessResult<TTargetDetails> extends Omit<MigrationPlannerSuccessResult, 'plan'> {
readonly kind: 'success';
readonly plan: SqlMigrationPlan<TTargetDetails>;
}
interface SqlPlannerFailureResult extends Omit<MigrationPlannerFailureResult, 'conflicts'> {
readonly kind: 'failure';
readonly conflicts: readonly SqlPlannerConflict[];
}
type SqlPlannerResult<TTargetDetails> = SqlPlannerSuccessResult<TTargetDetails> | SqlPlannerFailureResult;
interface SqlMigrationPlannerPlanOptions {
readonly contract: Contract<SqlStorage>;
/**
* The "from"/live schema as the target's introspected node (SQLite a flat
* `SqlSchemaIR`, Postgres a `PostgresDatabaseSchemaNode` root). Structure-aware
* consumers narrow the concrete shape before walking it.
*/
readonly schema: SqlSchemaIRNode;
readonly policy: MigrationOperationPolicy;
readonly schemaName?: string;
/**
* Contract space the plan applies to. The planner stamps this onto
* the produced {@link SqlMigrationPlan.spaceId} so the runner keys
* the marker row by the right space. App-plan callers pass
* `APP_SPACE_ID`; per-extension callers pass the extension's space
* id.
*/
readonly spaceId: string;
/**
* The "from" contract (state the planner assumes the database starts at),
* or `null` for reconciliation flows that have no prior contract.
*
* Required at every call site so the structural fact "I have a prior
* contract / I don't" is visible in the type. `migration plan` reads
* the predecessor bundle's `end-contract.json` from disk and passes
* the parsed value; `db update` / `db init` reconcile against the
* live schema and pass `null`. Strategies that
* need from/to column-shape comparisons (unsafe type change, nullability
* tightening) use this to decide whether to emit `dataTransform`
* placeholders; they short-circuit when it is `null`.
*
* Planners also derive the "from" identity they stamp onto the produced
* plan's `describe()` as `fromContract?.storage.storageHash ?? null`.
*/
readonly fromContract: Contract<SqlStorage> | null;
/**
* Active framework components participating in this composition.
* Each component is target-bound so SQL targets can dispatch
* component-owned planning behaviour from the same descriptor list.
* All components must have matching familyId ('sql') and targetId.
*/
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
/**
* Ownership oracle over the whole contract-space composition (the passive
* aggregate). The planner asks it, per live extra node, whether any space
* declares that entity: a sibling-owned node is left untouched, an unowned
* node is a genuine extra it may drop under a destructive policy. The
* planner holds no list of other spaces' names — ownership lives in the
* aggregate; it only asks. Absent for a single-space plan handed no
* aggregate. See {@link SchemaOwnership}.
*/
readonly ownership?: SchemaOwnership;
}
interface SqlMigrationPlanner<TTargetDetails> {
plan(options: SqlMigrationPlannerPlanOptions): SqlPlannerResult<TTargetDetails>;
}
interface SqlMigrationRunnerExecuteCallbacks<TTargetDetails> {
onOperationStart?(operation: SqlMigrationPlanOperation<TTargetDetails>): void;
onOperationComplete?(operation: SqlMigrationPlanOperation<TTargetDetails>): void;
}
interface SqlMigrationRunnerExecuteOptions<TTargetDetails> {
readonly plan: SqlMigrationPlan<TTargetDetails>;
readonly driver: SqlControlDriverInstance<string>;
/**
* Logical contract space this plan applies to. When omitted the
* runner derives the space from {@link SqlMigrationPlan.spaceId};
* when supplied, the runner asserts it matches `plan.spaceId` so a
* caller cannot accidentally write the marker row for a different
* space than the plan was produced for.
*/
readonly space?: string;
/**
* Destination contract IR.
* Must correspond to `plan.destination` and is used for schema verification and marker/ledger writes.
*/
readonly destinationContract: Contract<SqlStorage>;
/**
* Execution-time policy that defines which operation classes are allowed.
* The runner validates each operation against this policy before execution.
*/
readonly policy: MigrationOperationPolicy;
readonly schemaName?: string;
readonly strictVerification?: boolean;
readonly callbacks?: SqlMigrationRunnerExecuteCallbacks<TTargetDetails>;
readonly context?: OperationContext;
/**
* Execution-time checks configuration.
* All checks default to `true` (enabled) when omitted.
*/
readonly executionChecks?: MigrationRunnerExecutionChecks;
/**
* Active framework components participating in this composition.
* Each component is target-bound so SQL targets can dispatch
* component-owned execution behaviour from the same descriptor list.
* All components must have matching familyId ('sql') and targetId.
*/
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
/**
* Per-edge breakdown from graph-walk planning. When present, the runner
* writes one ledger row per edge instead of one collapsed row per apply.
*/
readonly migrationEdges: readonly AggregateMigrationEdgeRef[];
}
type SqlMigrationRunnerErrorCode = 'MIGRATION.DESTINATION_CONTRACT_MISMATCH' | 'MIGRATION.LEGACY_MARKER_SHAPE' | 'MIGRATION.MARKER_ORIGIN_MISMATCH' | 'MIGRATION.MARKER_CAS_FAILURE' | 'MIGRATION.POLICY_VIOLATION' | 'MIGRATION.PRECHECK_FAILED' | 'MIGRATION.POSTCHECK_FAILED' | 'MIGRATION.SCHEMA_VERIFY_FAILED' | 'MIGRATION.FOREIGN_KEY_VIOLATION' | 'MIGRATION.EXECUTION_FAILED';
interface SqlMigrationRunnerFailure extends MigrationRunnerFailure {
readonly code: SqlMigrationRunnerErrorCode;
readonly meta?: AnyRecord;
}
interface SqlMigrationRunnerSuccessValue extends MigrationRunnerPerSpaceSuccessValue {}
type SqlMigrationRunnerResult = Result<SqlMigrationRunnerSuccessValue, SqlMigrationRunnerFailure>;
interface SqlMigrationRunner<TTargetDetails> {
/**
* Apply one or more per-space migration plans, opening and managing the
* outer transaction (and any target-specific connection-level setup, e.g.
* SQLite's `PRAGMA foreign_keys` toggle). An apply that targets one space
* passes a one-element `perSpaceOptions` list.
*
* The caller orders the input list (typically via the aggregate planner's
* `applyOrder`: extensions alphabetical, then app). A failure on any space
* rolls back every space's writes.
*
* Each entry must reference the same `driver` as the top-level `driver`
* (the connection the outer transaction is open on).
*/
execute(options: {
readonly driver: SqlControlDriverInstance<string>;
readonly perSpaceOptions: ReadonlyArray<SqlMigrationRunnerExecuteOptions<TTargetDetails>>;
}): Promise<MigrationRunnerResult>;
/**
* Apply a single migration plan against an already-open connection
* **without** opening a transaction. The caller is responsible for
* wrapping the call (and any siblings) in `BEGIN` / `COMMIT` /
* `ROLLBACK`. Used by {@link SqlMigrationRunner.execute} to fan out
* across contract spaces inside one outer transaction.
*
* Idempotent control-table setup (`prisma_contract.*`) and marker
* writes use `options.space` to address the per-space marker row.
*/
executeOnConnection(options: SqlMigrationRunnerExecuteOptions<TTargetDetails>): Promise<SqlMigrationRunnerResult>;
}
interface CreateSqlMigrationPlanOptions<TTargetDetails> {
readonly targetId: string;
/**
* Contract space this plan applies to. Mirrors {@link SqlMigrationPlan.spaceId}.
*/
readonly spaceId: string;
readonly origin?: SqlMigrationPlanContractInfo | null;
readonly destination: SqlMigrationPlanContractInfo;
readonly operations: readonly SqlMigrationPlanOperation<TTargetDetails>[];
/**
* Sorted, deduplicated invariant ids for this plan; mirrors the required
* field on {@link SqlMigrationPlan}. Callers without a migration manifest
* (`db init`, `db update`, planner-built plans) pass `[]`.
*/
readonly providedInvariants: readonly string[];
readonly meta?: AnyRecord;
}
//#endregion
export { SqlPlannerSuccessResult as A, SqlMigrationRunnerSuccessValue as C, SqlPlannerConflictLocation as D, SqlPlannerConflictKind as E, SqlPlannerFailureResult as O, SqlMigrationRunnerResult as S, SqlPlannerConflict as T, SqlMigrationRunner as _, FieldEvent as a, SqlMigrationRunnerExecuteOptions as b, SqlControlAdapterDescriptor as c, SqlMigrationPlanContractInfo as d, SqlMigrationPlanOperation as f, SqlMigrationPlannerPlanOptions as g, SqlMigrationPlanner as h, ExpandNativeTypeInput as i, StorageTypePlanResult as j, SqlPlannerResult as k, SqlControlExtensionDescriptor as l, SqlMigrationPlanOperationTarget as m, CodecControlHooks as n, FieldEventContext as o, SqlMigrationPlanOperationStep as p, CreateSqlMigrationPlanOptions as r, ResolveIdentityValueInput as s, AnyRecord as t, SqlMigrationPlan as u, SqlMigrationRunnerErrorCode as v, SqlPlanTargetDetails as w, SqlMigrationRunnerFailure as x, SqlMigrationRunnerExecuteCallbacks as y };
//# sourceMappingURL=types-6JMCRorL.d.mts.map
{"version":3,"file":"types-6JMCRorL.d.mts","names":[],"sources":["../src/core/migrations/types.ts"],"mappings":";;;;;;;;;;KAkCY,YAAY,SAAS;UAEhB,sBAAsB;WAC5B,qBAAqB,0BAA0B;;;;;UAMzC;WACN;WACA;WACA,aAAa;;;;;;;;;UAUP;WACN;WACA;WACA,aAAa;;;;;;;;;;;;;;;;KAiBZ;;;;;;;;;;;;;UAcK;WACN;WACA;WACA;WACA,aAAa;WACb,WAAW;WACX,aAAa;WACb,WAAW;;UAGL,kBAAkB;;;;;;;;;EASjC,sBAAsB;aACX;aACA,cAAc;aACd,UAAU,SAAS;aACnB,QAAQ;aACR;aACA,QAAQ;QACb,sBAAsB;EAC5B,cAAc;aACH;aACA,cAAc;aACd,QAAQ;aACR;iBACI;EACf,mBAAmB;aACR,QAAQ;aACR;QACL,QAAQ,eAAe;;;;;;;;;;;EAW7B,oBAAoB,OAAO;;;;;;;;;;EAU3B,wBAAwB,OAAO;;;;;;;;;;;;;;;EAe/B,gBAAgB,OAAO,YAAY,KAAK,+BAA+B;;UAGxD,8BAA8B,kCACrC,kCAAkC;WACjC,wBAAwB;;;;;;;;;;;;WAYxB,gBAAgB,cAAc,SAAS;;UAGjC,4BAA4B,kCACnC,gCAAgC,WAAW,kBAAkB;WAC5D,wBAAwB;;UAGlB;WACN;WACA;;;;;;;;;WASA;WACA,OAAO;;;;;;;;;UAUD;WACN;WACA;;UAGM,gCAAgC;WACtC;WACA,UAAU;;UAGJ,0BAA0B,wBAAwB;WACxD;WACA,QAAQ,gCAAgC;WACxC,mBAAmB;WACnB,kBAAkB;WAClB,oBAAoB;WACpB,OAAO;;UAGD;WACN;WACA;;UAGM,iBAAiB,wBAAwB;;;;;;;;;;;;;;;WAe/C;;;;;WAKA,SAAS;;;;WAIT,aAAa;WACb,sBACL,0BAA0B,kBAC1B,QAAQ,0BAA0B;;;;;;;;;WAU7B;WACA,OAAO;;KAGN;UASK;WACN;WACA;WACA;WACA;WACA;WACA;;UAGM,2BAA2B;WACjC,MAAM;WACN,WAAW;WACX,OAAO;;UAGD,wBAAwB,wBAC/B,KAAK;WACJ;WACA,MAAM,iBAAiB;;UAGjB,gCAAgC,KAAK;WAC3C;WACA,oBAAoB;;KAGnB,iBAAiB,kBACzB,wBAAwB,kBACxB;UAEa;WACN,UAAU,SAAS;;;;;;WAMnB,QAAQ;WACR,QAAQ;WACR;;;;;;;;WAQA;;;;;;;;;;;;;;;;;WAiBA,cAAc,SAAS;;;;;;;WAOvB,qBAAqB,cAAc;;;;;;;;;;WAUnC,YAAY;;UAGN,oBAAoB;EACnC,KAAK,SAAS,iCAAiC,iBAAiB;;UAGjD,mCAAmC;EAClD,kBAAkB,WAAW,0BAA0B;EACvD,qBAAqB,WAAW,0BAA0B;;UAG3C,iCAAiC;WACvC,MAAM,iBAAiB;WACvB,QAAQ;;;;;;;;WAQR;;;;;WAKA,qBAAqB,SAAS;;;;;WAK9B,QAAQ;WACR;WACA;WACA,YAAY,mCAAmC;WAC/C,UAAU;;;;;WAKV,kBAAkB;;;;;;;WAOlB,qBAAqB,cAAc;;;;;WAKnC,yBAAyB;;KAGxB;UAYK,kCAAkC;WACxC,MAAM;WACN,OAAO;;UAGD,uCAAuC;KAE5C,2BAA2B,OACrC,gCACA;UAGe,mBAAmB;;;;;;;;;;;;;;EAclC,QAAQ;aACG,QAAQ;aACR,iBAAiB,cAAc,iCAAiC;MACvE,QAAQ;;;;;;;;;;;EAYZ,oBACE,SAAS,iCAAiC,kBACzC,QAAQ;;UAGI,8BAA8B;WACpC;;;;WAIA;WACA,SAAS;WACT,aAAa;WACb,qBAAqB,0BAA0B;;;;;;WAM/C;WACA,OAAO"}
+1
-1

@@ -1,2 +0,2 @@

import { i as SqlControlAdapterDescriptor, n as Lowerer, r as SqlControlAdapter, t as ExecuteRequestLowerer } from "./control-adapter-BQgad8Zc.mjs";
import { i as SqlControlAdapterDescriptor, n as Lowerer, r as SqlControlAdapter, t as ExecuteRequestLowerer } from "./control-adapter-CW7FdxeS.mjs";
export type { ExecuteRequestLowerer, Lowerer, SqlControlAdapter, SqlControlAdapterDescriptor };

@@ -1,4 +0,4 @@

import { r as SqlControlAdapter } from "./control-adapter-BQgad8Zc.mjs";
import { A as SqlPlannerSuccessResult, C as SqlMigrationRunnerSuccessValue, D as SqlPlannerConflictLocation, E as SqlPlannerConflictKind, O as SqlPlannerFailureResult, S as SqlMigrationRunnerResult, T as SqlPlannerConflict, _ as SqlMigrationRunner, a as FieldEvent, b as SqlMigrationRunnerExecuteOptions, c as SqlControlAdapterDescriptor, d as SqlMigrationPlanContractInfo, f as SqlMigrationPlanOperation, g as SqlMigrationPlannerPlanOptions, h as SqlMigrationPlanner, i as ExpandNativeTypeInput, j as StorageTypePlanResult, k as SqlPlannerResult, l as SqlControlExtensionDescriptor, m as SqlMigrationPlanOperationTarget, n as CodecControlHooks, o as FieldEventContext, p as SqlMigrationPlanOperationStep, r as CreateSqlMigrationPlanOptions, s as ResolveIdentityValueInput, t as AnyRecord, u as SqlMigrationPlan, v as SqlMigrationRunnerErrorCode, w as SqlPlanTargetDetails, x as SqlMigrationRunnerFailure, y as SqlMigrationRunnerExecuteCallbacks } from "./types-BPv_y7iS.mjs";
import { n as SqlSchemaDiffInput, r as SqlSchemaDiffResult, t as SqlSchemaDiffFn } from "./schema-differ-DnoopSXm.mjs";
import { r as SqlControlAdapter } from "./control-adapter-CW7FdxeS.mjs";
import { A as SqlPlannerSuccessResult, C as SqlMigrationRunnerSuccessValue, D as SqlPlannerConflictLocation, E as SqlPlannerConflictKind, O as SqlPlannerFailureResult, S as SqlMigrationRunnerResult, T as SqlPlannerConflict, _ as SqlMigrationRunner, a as FieldEvent, b as SqlMigrationRunnerExecuteOptions, c as SqlControlAdapterDescriptor, d as SqlMigrationPlanContractInfo, f as SqlMigrationPlanOperation, g as SqlMigrationPlannerPlanOptions, h as SqlMigrationPlanner, i as ExpandNativeTypeInput, j as StorageTypePlanResult, k as SqlPlannerResult, l as SqlControlExtensionDescriptor, m as SqlMigrationPlanOperationTarget, n as CodecControlHooks, o as FieldEventContext, p as SqlMigrationPlanOperationStep, r as CreateSqlMigrationPlanOptions, s as ResolveIdentityValueInput, t as AnyRecord, u as SqlMigrationPlan, v as SqlMigrationRunnerErrorCode, w as SqlPlanTargetDetails, x as SqlMigrationRunnerFailure, y as SqlMigrationRunnerExecuteCallbacks } from "./types-6JMCRorL.mjs";
import { n as SqlSchemaDiffInput, r as SqlSchemaDiffResult, t as SqlSchemaDiffFn } from "./schema-differ-C8OHo1V5.mjs";
import { ContractSerializer, ControlFamilyDescriptor, ControlFamilyInstance, ControlStack, DiffSubjectGranularity, MigratableTargetDescriptor, MigrationOperationClass, MigrationOperationPolicy, MigrationOperationPolicy as MigrationOperationPolicy$1, MigrationPlan, MigrationPlanOperation, MigrationPlanOperation as MigrationPlanOperation$1, MigrationPlanner, MigrationPlannerConflict, MigrationPlannerConflict as MigrationPlannerConflict$1, MigrationPlannerResult, MutationDefaultGeneratorDescriptor, OpFactoryCall, OperationPreview, OperationPreviewCapable, PslContractInferCapable, SchemaDiffIssue, SchemaVerifier, SchemaViewCapable, SignDatabaseResult, TargetMigrationsCapability, VerifyDatabaseResult, VerifyDatabaseSchemaResult, assembleAuthoringContributions } from "@prisma-next/framework-components/control";

@@ -14,3 +14,2 @@ import { ColumnDefault, Contract, ControlPolicy } from "@prisma-next/contract/types";

import { SqlOperationDescriptors } from "@prisma-next/sql-operations";
//#region src/core/control-instance.d.ts

@@ -494,2 +493,12 @@ interface SqlTypeMetadata {

/**
* Target-supplied hook (same IoC seam as `NativeTypeExpander`/`DefaultRenderer`)
* that normalizes a contract-declared `ColumnDefault` into the resolved shape
* the target's introspection parses from the live database — e.g. a
* `dbgenerated("'{}'::jsonb")` function call and the literal Postgres reports
* are the same value in different shapes, and `resolvedDefaultsEqual`
* compares `kind` before content. When omitted, the contract's raw default
* is the resolved default unchanged.
*/
type DefaultResolver = (def: ColumnDefault, resolvedNativeType: string) => ColumnDefault;
/**
* Target-supplied callback that resolves a contract namespace to the live

@@ -526,2 +535,3 @@ * database schema its enums are stored under.

readonly renderDefault?: DefaultRenderer;
readonly resolveDefault?: DefaultResolver;
/**

@@ -811,2 +821,114 @@ * Target-supplied resolver mapping a namespace to the live database schema

};
/**
* Builds a `temporal.<codec>` field preset for a codec that takes a precision
* parameter (`pg/timestamp@1`, `pg/timestamptz@1`). Arguments change field
* properties only — never the codec, which the caller fixes here.
*
* All three arguments are optional: omitting `precision` omits `typeParams`
* entirely, and omitting a phase omits that phase (both omitted omits
* `executionDefaults`).
*/
declare function temporalCodecPresetWithPrecision<const CodecId extends string, const NativeType extends string>(input: {
readonly codecId: CodecId;
readonly nativeType: NativeType;
}): {
readonly kind: "fieldPreset";
readonly args: readonly [{
readonly name: "precision";
readonly kind: "number";
readonly optional: true;
readonly integer: true;
readonly minimum: 0;
}, {
readonly name: "onCreate";
readonly kind: "option";
readonly values: readonly ["now"];
readonly optional: true;
}, {
readonly name: "onUpdate";
readonly kind: "option";
readonly values: readonly ["now"];
readonly optional: true;
}];
readonly output: {
readonly codecId: CodecId;
readonly nativeType: NativeType;
readonly typeParams: {
readonly precision: {
readonly kind: "arg";
readonly index: 0;
};
};
readonly executionDefaults: {
readonly onCreate: {
readonly kind: "select";
readonly index: 1;
readonly cases: {
readonly now: {
readonly kind: "generator";
readonly id: "timestampNow";
};
};
};
readonly onUpdate: {
readonly kind: "select";
readonly index: 2;
readonly cases: {
readonly now: {
readonly kind: "generator";
readonly id: "timestampNow";
};
};
};
};
};
};
/**
* Builds a `temporal.<codec>` field preset for a codec with no type
* parameters (`sqlite/datetime@1`). As with the precision-bearing variant,
* both phase arguments are optional and omitting one omits that phase.
*/
declare function temporalCodecPreset<const CodecId extends string, const NativeType extends string>(input: {
readonly codecId: CodecId;
readonly nativeType: NativeType;
}): {
readonly kind: "fieldPreset";
readonly args: readonly [{
readonly name: "onCreate";
readonly kind: "option";
readonly values: readonly ["now"];
readonly optional: true;
}, {
readonly name: "onUpdate";
readonly kind: "option";
readonly values: readonly ["now"];
readonly optional: true;
}];
readonly output: {
readonly codecId: CodecId;
readonly nativeType: NativeType;
readonly executionDefaults: {
readonly onCreate: {
readonly kind: "select";
readonly index: 0;
readonly cases: {
readonly now: {
readonly kind: "generator";
readonly id: "timestampNow";
};
};
};
readonly onUpdate: {
readonly kind: "select";
readonly index: 1;
readonly cases: {
readonly now: {
readonly kind: "generator";
readonly id: "timestampNow";
};
};
};
};
};
};
//#endregion

@@ -816,3 +938,3 @@ //#region src/exports/control.d.ts

//#endregion
export { type CodecControlHooks, type ContractToSchemaIROptions, type ControlPolicySubject, type CreateSqlMigrationPlanOptions, type DefaultRenderer, type EnumNamespaceSchemaResolver, type ExpandNativeTypeInput, type FieldEvent, type FieldEventContext, INIT_ADDITIVE_POLICY, type MigrationOperationClass, type MigrationOperationPolicy, type MigrationPlan, type MigrationPlanOperation, type MigrationPlanner, type MigrationPlannerConflict, type MigrationPlannerResult, type NativeTypeExpander, type PlanFieldEventOperationsOptions, type ResolveIdentityValueInput, type SqlControlAdapterDescriptor, type SqlControlExtensionDescriptor, type SqlControlFamilyInstance, type SqlControlTargetDescriptor, type SqlDescribedContractSpace, type SqlMigrationPlan, type SqlMigrationPlanContractInfo, type SqlMigrationPlanOperation, type SqlMigrationPlanOperationStep, type SqlMigrationPlanOperationTarget, type SqlMigrationPlanner, type SqlMigrationPlannerPlanOptions, type SqlMigrationRunner, type SqlMigrationRunnerErrorCode, type SqlMigrationRunnerExecuteCallbacks, type SqlMigrationRunnerExecuteOptions, type SqlMigrationRunnerFailure, type SqlMigrationRunnerResult, type SqlMigrationRunnerSuccessValue, type SqlPlanTargetDetails, type SqlPlannerConflict, type SqlPlannerConflictKind, type SqlPlannerConflictLocation, type SqlPlannerFailureResult, type SqlPlannerResult, type SqlPlannerSuccessResult, type SqlSchemaDiffFn, type SqlSchemaDiffInput, type SqlSchemaDiffResult, type StorageTypePlanResult, type SuppressionRecord, type TargetMigrationsCapability, assembleAuthoringContributions, buildNativeTypeExpander, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, _default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
export { type CodecControlHooks, type ContractToSchemaIROptions, type ControlPolicySubject, type CreateSqlMigrationPlanOptions, type DefaultRenderer, type DefaultResolver, type EnumNamespaceSchemaResolver, type ExpandNativeTypeInput, type FieldEvent, type FieldEventContext, INIT_ADDITIVE_POLICY, type MigrationOperationClass, type MigrationOperationPolicy, type MigrationPlan, type MigrationPlanOperation, type MigrationPlanner, type MigrationPlannerConflict, type MigrationPlannerResult, type NativeTypeExpander, type PlanFieldEventOperationsOptions, type ResolveIdentityValueInput, type SqlControlAdapterDescriptor, type SqlControlExtensionDescriptor, type SqlControlFamilyInstance, type SqlControlTargetDescriptor, type SqlDescribedContractSpace, type SqlMigrationPlan, type SqlMigrationPlanContractInfo, type SqlMigrationPlanOperation, type SqlMigrationPlanOperationStep, type SqlMigrationPlanOperationTarget, type SqlMigrationPlanner, type SqlMigrationPlannerPlanOptions, type SqlMigrationRunner, type SqlMigrationRunnerErrorCode, type SqlMigrationRunnerExecuteCallbacks, type SqlMigrationRunnerExecuteOptions, type SqlMigrationRunnerFailure, type SqlMigrationRunnerResult, type SqlMigrationRunnerSuccessValue, type SqlPlanTargetDetails, type SqlPlannerConflict, type SqlPlannerConflictKind, type SqlPlannerConflictLocation, type SqlPlannerFailureResult, type SqlPlannerResult, type SqlPlannerSuccessResult, type SqlSchemaDiffFn, type SqlSchemaDiffInput, type SqlSchemaDiffResult, type StorageTypePlanResult, type SuppressionRecord, type TargetMigrationsCapability, assembleAuthoringContributions, buildNativeTypeExpander, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, _default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, temporalCodecPreset, temporalCodecPresetWithPrecision, timestampNowControlDescriptor };
//# sourceMappingURL=control.d.mts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/assembly.ts","../src/core/control-target-descriptor.ts","../src/core/migrations/contract-to-schema-ir.ts","../src/core/migrations/control-policy.ts","../src/core/migrations/field-event-planner.ts","../src/core/migrations/native-type-expander.ts","../src/core/migrations/plan-helpers.ts","../src/core/migrations/policies.ts","../src/core/timestamp-now-generator.ts","../src/exports/control.ts"],"mappings":"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"}
{"version":3,"file":"control.d.mts","names":[],"sources":["../src/core/control-instance.ts","../src/core/control-descriptor.ts","../src/core/assembly.ts","../src/core/control-target-descriptor.ts","../src/core/migrations/contract-to-schema-ir.ts","../src/core/migrations/control-policy.ts","../src/core/migrations/field-event-planner.ts","../src/core/migrations/native-type-expander.ts","../src/core/migrations/plan-helpers.ts","../src/core/migrations/policies.ts","../src/core/timestamp-now-generator.ts","../src/exports/control.ts"],"mappings":"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"}
import { i as sqlFamilyPslBlockDescriptors, n as sqlFamilyAuthoringFieldPresets, r as sqlFamilyEntityTypes, t as sqlFamilyAuthoringTypes } from "./authoring-type-constructors-CXd-8ydc.mjs";
import { n as classifyDiffSubjectGranularity, o as verifySqlSchemaByDiff, s as extractCodecControlHooks, t as classifyDiffEntityKind } from "./schema-verify-W3r631Jh.mjs";
import { n as classifyDiffSubjectGranularity, o as verifySqlSchemaByDiff, s as extractCodecControlHooks, t as classifyDiffEntityKind } from "./schema-verify-DY21bMwy.mjs";
import { t as SqlContractSerializer } from "./sql-contract-serializer-C75cfMSS.mjs";
import { t as collectSupportedCodecTypeIds } from "./verify-C-G0obRm.mjs";
import { t as backingIndexColumnKeys } from "./foreign-key-index-backing-BP71iI-Q.mjs";
import { n as temporalAuthoringPresets, r as timestampNowControlDescriptor } from "./timestamp-now-generator-CloimujU.mjs";
import { a as timestampNowControlDescriptor, i as temporalCodecPresetWithPrecision, n as temporalAuthoringPresets, r as temporalCodecPreset } from "./timestamp-now-generator-DRXygu32.mjs";
import { sqlEmission } from "@prisma-next/sql-contract-emitter";

@@ -17,3 +16,3 @@ import { blindCast } from "@prisma-next/utils/casts";

import { StorageTable, isStorageTypeInstance } from "@prisma-next/sql-contract/types";
import { SqlSchemaIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
import { RelationalSchemaNodeKind, SqlSchemaIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
import { notOk, ok } from "@prisma-next/utils/result";

@@ -580,3 +579,3 @@ //#region src/core/operation-preview.ts

//#region src/core/migrations/contract-to-schema-ir.ts
function convertColumn(name, column, storageTypes, expandNativeType, renderDefault) {
function convertColumn(name, column, storageTypes, expandNativeType, renderDefault, resolveDefault) {
const resolved = resolveColumnTypeMetadata(column, storageTypes);

@@ -590,2 +589,4 @@ const baseNativeType = expandNativeType ? expandNativeType({

const resolvedNativeType = column.many ? `${baseNativeType}[]` : baseNativeType;
const rawColumnDefault = column.default ?? void 0;
const resolvedColumnDefault = rawColumnDefault !== void 0 && resolveDefault ? resolveDefault(rawColumnDefault, resolvedNativeType) : rawColumnDefault;
return {

@@ -598,3 +599,3 @@ name,

resolvedNativeType,
...ifDefined("resolvedDefault", column.default ?? void 0),
...ifDefined("resolvedDefault", resolvedColumnDefault),
codecRef: buildColumnCodecRef(resolved, column.many),

@@ -671,9 +672,10 @@ codecBaseNativeType: resolved.nativeType,

}
function convertUnique(unique) {
function convertUnique(unique, tableName) {
return {
columns: unique.columns,
...ifDefined("name", unique.name)
...ifDefined("name", unique.name),
dependsOn: flatColumnDependsOn(tableName, unique.columns)
};
}
function convertIndex(index) {
function convertIndex(index, tableName) {
return {

@@ -684,6 +686,46 @@ columns: index.columns,

...ifDefined("type", index.type),
...ifDefined("options", index.options)
...ifDefined("options", index.options),
dependsOn: flatColumnDependsOn(tableName, index.columns)
};
}
/**
* The referenced table's chain in the flat (single-schema) tree
* `contractToSchemaIR`/`contractNamespaceToSchemaIR` build: the root
* (`SqlSchemaIR`, fixed `'database'` id) followed by the table's own id.
* Postgres discards this when it re-derives the FK against its own
* multi-schema tree shape (`contractToPostgresDatabaseSchemaNode`); SQLite's
* flat tree uses it as-is.
*/
function flatSchemaDependsOn(tableName) {
return [{
nodeKind: RelationalSchemaNodeKind.schema,
id: "database"
}, {
nodeKind: RelationalSchemaNodeKind.table,
id: tableName
}];
}
/**
* The chains from a table-child object (foreign key, index, unique, primary
* key) to each of the own columns it is built on, in the flat tree. Dropping
* a covered column auto-drops the object, so the object's drop must precede
* the column's; the graph derives that direction from these edges.
*/
function flatColumnDependsOn(tableName, columns) {
return columns.map((column) => [
{
nodeKind: RelationalSchemaNodeKind.schema,
id: "database"
},
{
nodeKind: RelationalSchemaNodeKind.table,
id: tableName
},
{
nodeKind: RelationalSchemaNodeKind.column,
id: `column:${column}`
}
]);
}
/**
* The FK's referenced-namespace identity comes from the target's namespace

@@ -697,2 +739,6 @@ * node, not the raw namespace-id string. An unbound target namespace stamps

* real DDL schema downstream.
*
* `dependsOn` carries the referenced table (created before the FK, dropped
* after it) plus the FK's own columns (dropped after the FK, since dropping a
* column auto-drops the FK built on it).
*/

@@ -708,21 +754,9 @@ function convertForeignKey(fk, storage) {

...ifDefined("onDelete", fk.onDelete),
...ifDefined("onUpdate", fk.onUpdate)
...ifDefined("onUpdate", fk.onUpdate),
dependsOn: [flatSchemaDependsOn(fk.target.tableName), ...flatColumnDependsOn(fk.source.tableName, fk.source.columns)]
};
}
function convertTable(name, table, storageTypes, expandNativeType, renderDefault, storage) {
function convertTable(name, table, storageTypes, expandNativeType, renderDefault, resolveDefault, storage) {
const columns = {};
for (const [colName, colDef] of Object.entries(table.columns)) columns[colName] = convertColumn(colName, colDef, storageTypes, expandNativeType, renderDefault);
const satisfiedIndexColumns = new Set(backingIndexColumnKeys(table));
const fkBackingIndexes = [];
for (const fk of table.foreignKeys) {
if (fk.index === false) continue;
const key = fk.source.columns.join(",");
if (satisfiedIndexColumns.has(key)) continue;
fkBackingIndexes.push({
columns: fk.source.columns,
unique: false,
name: defaultIndexName(name, fk.source.columns)
});
satisfiedIndexColumns.add(key);
}
for (const [colName, colDef] of Object.entries(table.columns)) columns[colName] = convertColumn(colName, colDef, storageTypes, expandNativeType, renderDefault, resolveDefault);
const checks = table.checks && table.checks.length > 0 ? table.checks.map((c) => convertCheck(c, storage)) : void 0;

@@ -732,6 +766,10 @@ return new SqlTableIR({

columns,
...ifDefined("primaryKey", table.primaryKey),
foreignKeys: table.foreignKeys.filter((fk) => fk.constraint !== false).map((fk) => convertForeignKey(fk, storage)),
uniques: table.uniques.map(convertUnique),
indexes: [...table.indexes.map(convertIndex), ...fkBackingIndexes],
...ifDefined("primaryKey", table.primaryKey !== void 0 ? {
columns: table.primaryKey.columns,
...ifDefined("name", table.primaryKey.name),
dependsOn: flatColumnDependsOn(name, table.primaryKey.columns)
} : void 0),
foreignKeys: table.foreignKeys.map((fk) => convertForeignKey(fk, storage)),
uniques: table.uniques.map((u) => convertUnique(u, name)),
indexes: table.indexes.map((i) => convertIndex(i, name)),
...ifDefined("checks", checks)

@@ -816,3 +854,3 @@ });

StorageTable.assert(tableDefRaw, `namespaces.${namespaceId}.entries.table.${tableName}`);
tables[tableName] = convertTable(tableName, tableDefRaw, storageTypes, options.expandNativeType, options.renderDefault, storage);
tables[tableName] = convertTable(tableName, tableDefRaw, storageTypes, options.expandNativeType, options.renderDefault, options.resolveDefault, storage);
}

@@ -831,3 +869,3 @@ return new SqlSchemaIR({ tables });

if (tables[tableName] !== void 0) throw new Error(`contractToSchemaIR: duplicate SQL table name "${tableName}" across namespaces (ambiguous for flat SqlSchemaIR.tables).`);
tables[tableName] = convertTable(tableName, tableDef, storageTypes, options.expandNativeType, options.renderDefault, storage);
tables[tableName] = convertTable(tableName, tableDef, storageTypes, options.expandNativeType, options.renderDefault, options.resolveDefault, storage);
}

@@ -1240,4 +1278,4 @@ return new SqlSchemaIR({

//#endregion
export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, buildNativeTypeExpander, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, timestampNowControlDescriptor };
export { INIT_ADDITIVE_POLICY, assembleAuthoringContributions, buildNativeTypeExpander, contractNamespaceToSchemaIR, contractToSchemaIR, controlPolicyForCall, createMigrationPlan, control_default as default, detectDestructiveChanges, extractCodecControlHooks, partitionCallsByControlPolicy, partitionIssuesByControlPolicy, planFieldEventOperations, plannerFailure, plannerSuccess, resolveValueSetValues, runnerFailure, runnerSuccess, temporalAuthoringPresets, temporalCodecPreset, temporalCodecPresetWithPrecision, timestampNowControlDescriptor };
//# sourceMappingURL=control.mjs.map

@@ -1,4 +0,4 @@

import { a as NativeTypeNormalizer, o as arraysEqual } from "./control-adapter-BQgad8Zc.mjs";
import { n as CodecControlHooks } from "./types-BPv_y7iS.mjs";
import { t as SqlSchemaDiffFn } from "./schema-differ-DnoopSXm.mjs";
import { a as NativeTypeNormalizer, o as arraysEqual } from "./control-adapter-CW7FdxeS.mjs";
import { n as CodecControlHooks } from "./types-6JMCRorL.mjs";
import { t as SqlSchemaDiffFn } from "./schema-differ-C8OHo1V5.mjs";
import { DiffSubjectGranularity, SchemaDiffIssue, VerifierIssueCategory, VerifyDatabaseSchemaResult } from "@prisma-next/framework-components/control";

@@ -9,3 +9,2 @@ import { Contract, ControlPolicy } from "@prisma-next/contract/types";

import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
//#region src/core/diff/schema-verify.d.ts

@@ -12,0 +11,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"diff.d.mts","names":[],"sources":["../src/core/diff/schema-verify.ts"],"mappings":";;;;;;;;;;;;;;AA6DyB;AAsCzB;;;;;iBAzCgB,8BAAA,CACd,KAAA,EAAO,eAAA,EACP,aAAA,GAAgB,QAAA,aAAqB,sBAAA,GACpC,sBAAA;AAqFH;;;;;;;;;;;;AAAA,iBA/CgB,oBAAA,CACd,KAAA,EAAO,eAAA,EACP,aAAA,GAAgB,QAAA,aAAqB,sBAAA,GACpC,qBAAA;AAAA,UA4Cc,mBAAA;EAIiB;EAAA,SAFvB,MAAA,WAAiB,eAAA;EAGjB;EAAA,SADA,oBAAA,GAAuB,KAAA,EAAO,eAAA,KAAoB,aAAA;EAAA,SAClD,MAAA;EAAA,SACA,oBAAA,EAAsB,aAAA;EAEN;EAAA,SAAhB,aAAA,GAAgB,QAAA,aAAqB,sBAAA;AAAA;AAAA,UAG/B,cAAA;EAAA,SACN,QAAA,WAAmB,eAAA;EAAA,SACnB,QAAA,WAAmB,eAAe;AAAA;;;;;;;AAAA;iBAU7B,qBAAA,CAAsB,KAAA,EAAO,mBAAA,GAAsB,cAAc;AAAA,UA+BhE,uBAAA;EAAA,SACN,QAAA,EAAU,QAAA,CAAS,UAAA;EAhCmD;;;;;AAAA;EAAA,SAuCtE,cAAA,EAAgB,aAAA;IAAA,SACd,MAAA,EAAQ,eAAA;EAAA;EAAA,SAEV,UAAA,EAAY,WAAA,SAAoB,iBAAA;AAAA;;;;;;;;UAU1B,kBAAA;EAAA,SACN,QAAA,WAAmB,eAAA;EAAA,SACnB,QAAA,WAAmB,eAAe;AAAA;AAAA,iBAG7B,yBAAA,CAA0B,KAAA,EAAO,uBAAA,GAA0B,kBAAkB;AAAA,UA6B5E,0BAAA;EAAA,SACN,QAAA,EAAU,QAAA,CAAS,UAAA;EAAA,SACnB,MAAA,EAAQ,eAAA;EAAA,SACR,MAAA;EAAA,SACA,mBAAA,EAAqB,aAAA,CAAc,8BAAA;EAhDc;EAAA,SAkDjD,UAAA,EAAY,eAAA;EAxCY;EAAA,SA0CxB,aAAA,GAAgB,QAAA,aAAqB,sBAAA;AAAA;;;;;;AAxCH;AAG7C;iBA+CgB,qBAAA,CACd,KAAA,EAAO,0BAAA,GACN,0BAA0B"}
{"version":3,"file":"diff.d.mts","names":[],"sources":["../src/core/diff/schema-verify.ts"],"mappings":";;;;;;;;;;;;;;;;;;;iBA0DgB,+BACd,OAAO,iBACP,gBAAgB,qBAAqB,yBACpC;;;;;;;;;;;;;iBAsCa,qBACd,OAAO,iBACP,gBAAgB,qBAAqB,yBACpC;UA4Cc;;WAEN,iBAAiB;;WAEjB,uBAAuB,OAAO,oBAAoB;WAClD;WACA,sBAAsB;;WAEtB,gBAAgB,qBAAqB;;UAG/B;WACN,mBAAmB;WACnB,mBAAmB;;;;;;;;;iBAUd,sBAAsB,OAAO,sBAAsB;UA+BlD;WACN,UAAU,SAAS;;;;;;;WAOnB,gBAAgB;aACd,QAAQ;;WAEV,YAAY,oBAAoB;;;;;;;;;UAU1B;WACN,mBAAmB;WACnB,mBAAmB;;iBAGd,0BAA0B,OAAO,0BAA0B;UA6B1D;WACN,UAAU,SAAS;WACnB,QAAQ;WACR;WACA,qBAAqB,cAAc;;WAEnC,YAAY;;WAEZ,gBAAgB,qBAAqB;;;;;;;;;iBAUhC,sBACd,OAAO,6BACN"}

@@ -1,2 +0,2 @@

import { a as computeStorageTypeVerdict, i as computeSqlDiffVerdict, n as classifyDiffSubjectGranularity, o as verifySqlSchemaByDiff, r as classifySqlDiffIssue } from "./schema-verify-W3r631Jh.mjs";
import { a as computeStorageTypeVerdict, i as computeSqlDiffVerdict, n as classifyDiffSubjectGranularity, o as verifySqlSchemaByDiff, r as classifySqlDiffIssue } from "./schema-verify-DY21bMwy.mjs";
//#region src/core/diff/sql-schema-diff.ts

@@ -3,0 +3,0 @@ /**

@@ -6,3 +6,2 @@ import { ContractSerializer, SchemaDiffIssue, SchemaVerifier, SchemaVerifyOptions, SchemaVerifyResult } from "@prisma-next/framework-components/control";

import { JsonObject } from "@prisma-next/utils/json";
//#region src/core/ir/sql-contract-serializer-base.d.ts

@@ -9,0 +8,0 @@ type SqlEntityHydrationFactory = (entry: unknown) => unknown;

@@ -1,1 +0,1 @@

{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/sql-contract-serializer-base.ts","../src/core/ir/sql-contract-serializer.ts","../src/core/ir/sql-schema-verifier-base.ts"],"mappings":";;;;;;;KAmCY,yBAAA,IAA6B,KAAc;;AAAvD;;;;AAAuD;AA0BvD;;;;;;;;;;;;;;;;;;uBAAsB,yBAAA,mBAA4C,QAAA,CAAS,UAAA,cAC9D,kBAAA,CAAmB,SAAA;EAAA,mBAMT,uBAAA,EAAyB,WAAA,SAE1C,yBAAA;EAAA,iBANa,cAAA;EAAA,iBACA,UAAA;cAGI,uBAAA,GAAyB,WAAA,SAE1C,yBAAA,GAEF,eAAA,YAA0B,uBAAA;EAO5B,mBAAA,WAA8B,SAAA,GAAY,SAAA,EAAW,IAAA,YAAgB,CAAA;EAMrE,iBAAA,CAAkB,QAAA,EAAU,SAAA,GAAY,UAAA;EAIxC,mBAAA,0CAAmB,sBAAA;EAEnB,WAAA,0CAAW,WAAA;EAAA,UAED,yBAAA,CAA0B,IAAA,YAAgB,QAAA,CAAS,UAAA;EAAA,UAOnD,iBAAA,CAAkB,SAAA,EAAW,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,UAAA;EAAA,UAuC7D,sBAAA,CACR,UAAA,EAAY,QAAA,CAAS,MAAA,SAAe,MAAA,sBACnC,QAAA,CAAS,MAAA,SAAe,SAAA;EAAA,UAcjB,wBAAA,CACR,IAAA,UACA,GAAA,EAAK,MAAA,oBACJ,SAAA,GAAY,iBAAA;EAAA,UA+BL,uBAAA,CAAwB,KAAA,EAAO,mBAAA,GAAsB,mBAAA;EAAA,UAkBrD,uBAAA,CAAwB,QAAA,EAAU,QAAA,CAAS,UAAA,IAAc,SAAA;EAAd;;;;;;;;;;EAAA,UAc3C,yBAAA,CACR,OAAA,EAAS,QAAA,CAAS,MAAA,SAAe,QAAA,CAAS,MAAA,uBACzC,MAAA,SAAe,MAAA,SAAe,UAAA;EAAA,QAazB,gBAAA;EAAA,UAQE,mBAAA,CAAoB,KAAA,YAAiB,UAAA;EAAA,QAOvC,kBAAA;AAAA;;;;;;;AAxNV;;;;AAAuD;AA0BvD;;;;;cC3Ca,qBAAA,SAA8B,yBAAA,CAA0B,QAAA,CAAS,UAAA;;;;;;;;;;;ADiB9E;;;;AAAuD;AA0BvD;;;;;;;;;uBElCsB,qBAAA,gCACT,cAAA,CAAe,SAAA,EAAW,OAAA;EAErC,YAAA,CAAa,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,IAAW,kBAAA;EFiDlC;;;;;;EAAA,mBEpCX,qBAAA,CACjB,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,eAAA;EFuDoC;;;;;EAAA,mBEhD7B,sBAAA,CACjB,OAAA,EAAS,mBAAA,CAAoB,SAAA,EAAW,OAAA,aAC9B,eAAA;AAAA"}
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/core/ir/sql-contract-serializer-base.ts","../src/core/ir/sql-contract-serializer.ts","../src/core/ir/sql-schema-verifier-base.ts"],"mappings":";;;;;;KAmCY,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;uBA0BnB,0BAA0B,kBAAkB,SAAS,wBAC9D,mBAAmB;qBAMT,yBAAyB,oBAE1C;mBANa;mBACA;cAGI,0BAAyB,oBAE1C,4BAEF,2BAA0B;EAO5B,oBAAoB,UAAU,YAAY,WAAW,gBAAgB;EAMrE,kBAAkB,UAAU,YAAY;EAIxC,6DAAmB;EAEnB,qDAAW;YAED,0BAA0B,gBAAgB,SAAS;YAOnD,kBAAkB,WAAW,SAAS,cAAc,SAAS;YAuC7D,uBACR,YAAY,SAAS,eAAe,4BACnC,SAAS,eAAe;YAcjB,yBACR,cACA,KAAK,0BACJ,YAAY;YA+BL,wBAAwB,OAAO,sBAAsB;YAkBrD,wBAAwB,UAAU,SAAS,cAAc;;;;;;;;;;;YAczD,0BACR,SAAS,SAAS,eAAe,SAAS,6BACzC,eAAe,eAAe;UAazB;YAQE,oBAAoB,iBAAiB;UAOvC;;;;;;;;;;;;;;;;;;cCzOG,8BAA8B,0BAA0B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;uBCSxD,sBAAsB,WAAW,oBAC1C,eAAe,WAAW;EAErC,aAAa,SAAS,oBAAoB,WAAW,WAAW;;;;;;;qBAa7C,sBACjB,SAAS,oBAAoB,WAAW,oBAC9B;;;;;;qBAOO,uBACjB,SAAS,oBAAoB,WAAW,oBAC9B"}

@@ -1,6 +0,5 @@

import { f as SqlMigrationPlanOperation, w as SqlPlanTargetDetails } from "./types-BPv_y7iS.mjs";
import { f as SqlMigrationPlanOperation, w as SqlPlanTargetDetails } from "./types-6JMCRorL.mjs";
import { Contract } from "@prisma-next/contract/types";
import { SqlStorage } from "@prisma-next/sql-contract/types";
import { Migration } from "@prisma-next/migration-tools/migration";
//#region src/core/sql-migration.d.ts

@@ -7,0 +6,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/sql-migration.ts"],"mappings":";;;;;;;;AA4BA;;;;;;;;;;;;;;;;;;;uBAAsB,YAAA,kBACH,oBAAA,mDAEH,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,UAAA,eAClC,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,UAAA,WACpC,SAAA,CAAU,yBAAA,CAA0B,QAAA,UAAkB,SAAA,EAAW,KAAA,EAAO,GAAA;EAJhF;;;;;;;;;;EAAA,IAeI,kBAAA;AAAA"}
{"version":3,"file":"migration.d.mts","names":[],"sources":["../src/core/sql-migration.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;uBA4BsB,aACpB,iBAAiB,sBACjB,mCACA,cAAc,SAAS,cAAc,SAAS,aAC9C,YAAY,SAAS,cAAc,SAAS,qBACpC,UAAU,0BAA0B,kBAAkB,WAAW,OAAO;;;;;;;;;;;MAW5E"}

@@ -1,1 +0,1 @@

{"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";cAKM,aAAA;EAAA"}
{"version":3,"file":"pack.d.mts","names":[],"sources":["../src/exports/pack.ts"],"mappings":";cAKM"}
import { ColumnDefault } from "@prisma-next/contract/types";
import { SqlForeignKeyIR, SqlTableIR } from "@prisma-next/sql-schema-ir/types";
//#region src/core/psl-contract-infer/default-mapping.d.ts

@@ -86,3 +85,4 @@ interface DefaultMappingOptions {

readonly onDelete?: string | undefined;
readonly onUpdate?: string | undefined; /** `false` when the FK's source columns have no live backing index; omitted (the default) otherwise. */
readonly onUpdate?: string | undefined;
/** `false` when the FK's source columns have no live backing index; omitted (the default) otherwise. */
readonly index?: boolean | undefined;

@@ -89,0 +89,0 @@ };

@@ -1,1 +0,1 @@

{"version":3,"file":"psl-infer.d.mts","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/printer-config.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"mappings":";;;;UAOiB,qBAAA;EAAA,SACN,kBAAA,GAAqB,QAAQ,CAAC,MAAA;EAAA,SAC9B,yBAAA,KAA8B,UAAA;AAAA;AAAA,KAG7B,oBAAA;EAAA,SAAkC,SAAA;AAAA;EAAA,SAAiC,OAAO;AAAA;AAAA,iBAEtE,UAAA,CACd,aAAA,EAAe,aAAA,EACf,OAAA,GAAU,qBAAA,GACT,oBAAA;;;KCbE,UAAA;EAAA,SACM,IAAA;EAAA,SACA,GAAG;AAAA;AAAA,iBA4DE,WAAA,CAAY,SAAA,WAAoB,UAAU;AAAA,iBAqB1C,WAAA,CAAY,UAAA,WAAqB,UAAU;AAAA,iBAqB3C,UAAA,CAAW,UAAA,WAAqB,UAAU;;;;;;;iBA6B1C,gBAAA,CAAiB,KAAa;AAAA,iBAO9B,SAAA,CAAU,IAAY;AAAA,iBAgBtB,uBAAA,CACd,SAAA,qBACA,mBAA2B;AAAA,iBAeb,2BAAA,CAA4B,cAAA,UAAwB,UAAmB;AAAA,iBAKvE,eAAA,CAAgB,UAAkB;;;KCnLtC,sBAAA;EAAA,SACD,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGH,iBAAA;EAAA,SAEG,OAAA;EAAA,SACA,UAAA;EAAA,SACA,UAAA,GAAa,MAAA;EAAA,SACb,mBAAA,GAAsB,sBAAsB;AAAA;EAAA,SAG5C,WAAA;EAAA,SACA,UAAA;AAAA;AAAA,UAGE,UAAA;EACf,OAAA,CAAQ,UAAA,UAAoB,WAAA,GAAc,MAAA,oBAA0B,iBAAiB;AAAA;AAAA,UAGtE,QAAA;EAAA,SACN,SAAA,EAAW,WAAA;EAAA,SACX,WAAA,EAAa,WAAW;AAAA;AAAA,UAGlB,iBAAA;EAAA,SACN,OAAA,EAAS,UAAA;EAAA,SACT,cAAA,GAAiB,qBAAA;EAAA,SACjB,QAAA,GAAW,QAAA;EAAA,SACX,eAAA,IAAmB,UAAA,UAAoB,UAAA,cAAwB,aAAA;AAAA;AAAA,KAG9D,aAAA;EAAA,SACD,SAAA;EAAA,SACA,QAAA;EFtBT;;;AACqB;;EADrB,SE4BS,eAAA;;;;;;WAMA,mBAAA;EAAA,SACA,mBAAA;EAAA,SACA,QAAA;EAAA,SACA,IAAA;EAAA,SACA,YAAA;EAAA,SACA,MAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA,uBD4BgD;EAAA,SC1BhD,KAAA;AAAA;;;iBCxBK,eAAA,CACd,UAAA,UACA,UAAA,YACC,aAAa;;;KCxBJ,iBAAA;EAAA,SACD,gBAAA,EAAkB,WAAW,kBAAkB,aAAA;AAAA;AAAA,iBAG1C,cAAA,CACd,MAAA,EAAQ,MAAA,SAAe,UAAA,GACvB,YAAA,EAAc,WAAA,mBACb,iBAAA;;;;;;;;;AJdwD;AAG3D;;;;AAAsF;AAEtF;;;;iBIgJgB,uBAAA,CACd,SAAA,UACA,eAAA,UACA,EAAA,EAAI,eAAA,EACJ,QAAA,WACA,YAAA,WACA,SAAA,GAAY,UAAA,GACX,aAAA"}
{"version":3,"file":"psl-infer.d.mts","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/printer-config.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"mappings":";;;UAOiB;WACN,qBAAqB,SAAS;WAC9B,8BAA8B;;KAG7B;WAAkC;;WAAiC;;iBAE/D,WACd,eAAe,eACf,UAAU,wBACT;;;KCXE;WACM;WACA;;iBA4DK,YAAY,oBAAoB;iBAqBhC,YAAY,qBAAqB;iBAqBjC,WAAW,qBAAqB;;;;;;;iBA6BhC,iBAAiB;iBAOjB,UAAU;iBAIV,wBACd,8BACA;iBAec,4BAA4B,wBAAwB;iBAKpD,gBAAgB;;;KCzKpB;WACD;WACA;;KAGC;WAEG;WACA;WACA,aAAa;WACb,sBAAsB;;WAGtB;WACA;;UAGE;EACf,QAAQ,oBAAoB,cAAc,0BAA0B;;UAGrD;WACN,WAAW;WACX,aAAa;;UAGP;WACN,SAAS;WACT,iBAAiB;WACjB,WAAW;WACX,mBAAmB,oBAAoB,wBAAwB;;KAG9D;WACD;WACA;;;;;;WAMA;;;;;;WAMA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;;;;iBCxBK,gBACd,oBACA,sBACC;;;KCrBS;WACD,kBAAkB,6BAA6B;;iBAG1C,eACd,QAAQ,eAAe,aACvB,cAAc,8BACb;;;;;;;;;;;;;;;;;;;iBAuIa,wBACd,mBACA,yBACA,IAAI,iBACJ,mBACA,uBACA,YAAY,aACX"}

@@ -1,2 +0,3 @@

import { n as isBackedByColumnKeys, t as backingIndexColumnKeys } from "./foreign-key-index-backing-BP71iI-Q.mjs";
import pluralizeLib from "pluralize";
import { backingIndexColumnKeys, isBackedByColumnKeys } from "@prisma-next/sql-contract/foreign-key-materialization";
//#region src/core/psl-contract-infer/default-mapping.ts

@@ -137,5 +138,3 @@ const DEFAULT_FUNCTION_ATTRIBUTES = {

function pluralize(word) {
if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) return `${word}es`;
if (word.endsWith("y") && !/[aeiou]y$/i.test(word)) return `${word.slice(0, -1)}ies`;
return `${word}s`;
return pluralizeLib.plural(word);
}

@@ -142,0 +141,0 @@ function deriveRelationFieldName(fkColumns, referencedTableName) {

@@ -1,1 +0,1 @@

{"version":3,"file":"psl-infer.mjs","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"sourcesContent":["import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst DEFAULT_FUNCTION_ATTRIBUTES: Readonly<Record<string, string>> = {\n 'autoincrement()': '@default(autoincrement())',\n 'now()': '@default(now())',\n};\n\nexport interface DefaultMappingOptions {\n readonly functionAttributes?: Readonly<Record<string, string>>;\n readonly fallbackFunctionAttribute?: ((expression: string) => string | undefined) | undefined;\n}\n\nexport type DefaultMappingResult = { readonly attribute: string } | { readonly comment: string };\n\nexport function mapDefault(\n columnDefault: ColumnDefault,\n options?: DefaultMappingOptions,\n): DefaultMappingResult {\n switch (columnDefault.kind) {\n case 'literal':\n return { attribute: `@default(${formatLiteralValue(columnDefault.value)})` };\n case 'function': {\n const attribute =\n options?.functionAttributes?.[columnDefault.expression] ??\n DEFAULT_FUNCTION_ATTRIBUTES[columnDefault.expression] ??\n options?.fallbackFunctionAttribute?.(columnDefault.expression);\n return attribute\n ? { attribute }\n : { comment: `// Raw default: ${columnDefault.expression.replace(/[\\r\\n]+/g, ' ')}` };\n }\n }\n}\n\nfunction formatLiteralValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n\n switch (typeof value) {\n case 'boolean':\n case 'number':\n return String(value);\n case 'string':\n return quoteString(value);\n default:\n return quoteString(JSON.stringify(value));\n }\n}\n\nfunction quoteString(str: string): string {\n return `\"${escapeString(str)}\"`;\n}\n\nfunction escapeString(str: string): string {\n return JSON.stringify(str).slice(1, -1);\n}\n","const PSL_RESERVED_WORDS = new Set(['model', 'enum', 'types', 'type', 'generator', 'datasource']);\n\nconst IDENTIFIER_PART_PATTERN = /[A-Za-z0-9]+/g;\n\ntype NameResult = {\n readonly name: string;\n readonly map?: string;\n};\n\nfunction hasSeparators(input: string): boolean {\n return /[^A-Za-z0-9]/.test(input);\n}\n\nfunction extractIdentifierParts(input: string): string[] {\n return input.match(IDENTIFIER_PART_PATTERN) ?? [];\n}\n\nfunction createSyntheticIdentifier(input: string): string {\n let hash = 2166136261;\n\n for (const char of input) {\n hash ^= char.codePointAt(0) ?? 0;\n hash = Math.imul(hash, 16777619);\n }\n\n return `x${(hash >>> 0).toString(16)}`;\n}\n\nfunction sanitizeIdentifierCharacters(input: string): string {\n const sanitized = input.replace(/[^\\w]/g, '');\n return sanitized.length > 0 ? sanitized : createSyntheticIdentifier(input);\n}\n\nfunction capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1);\n}\n\nfunction snakeToPascalCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return capitalize(sanitizeIdentifierCharacters(input));\n }\n return parts.map(capitalize).join('');\n}\n\nfunction snakeToCamelCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return sanitizeIdentifierCharacters(input);\n }\n const [firstPart = input, ...rest] = parts;\n return firstPart.charAt(0).toLowerCase() + firstPart.slice(1) + rest.map(capitalize).join('');\n}\n\nfunction needsEscaping(name: string): boolean {\n return PSL_RESERVED_WORDS.has(name.toLowerCase()) || /^\\d/.test(name);\n}\n\nfunction escapeName(name: string): string {\n return `_${name}`;\n}\n\nfunction escapeIfNeeded(name: string): string {\n return needsEscaping(name) ? escapeName(name) : name;\n}\n\nexport function toModelName(tableName: string): NameResult {\n let name: string;\n\n if (hasSeparators(tableName)) {\n name = snakeToPascalCase(tableName);\n } else {\n name = tableName.charAt(0).toUpperCase() + tableName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: tableName };\n }\n\n if (name !== tableName) {\n return { name, map: tableName };\n }\n\n return { name };\n}\n\nexport function toFieldName(columnName: string): NameResult {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToCamelCase(columnName);\n } else {\n name = columnName.charAt(0).toLowerCase() + columnName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: columnName };\n }\n\n if (name !== columnName) {\n return { name, map: columnName };\n }\n\n return { name };\n}\n\nexport function toEnumName(pgTypeName: string): NameResult {\n let name: string;\n\n if (hasSeparators(pgTypeName)) {\n name = snakeToPascalCase(pgTypeName);\n } else {\n name = pgTypeName.charAt(0).toUpperCase() + pgTypeName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: pgTypeName };\n }\n\n if (name !== pgTypeName) {\n return { name, map: pgTypeName };\n }\n\n return { name };\n}\n\nconst VALID_IDENTIFIER_PATTERN = /^[A-Za-z_]\\w*$/;\n\n/**\n * PSL member name for a native-enum value. The value itself always prints\n * explicitly (`member = \"value\"`), so the returned name never needs a map:\n * a value that already is a valid, non-reserved identifier is kept verbatim\n * (case included); anything else is camelCased/escaped like a field name.\n */\nexport function toEnumMemberName(value: string): string {\n if (VALID_IDENTIFIER_PATTERN.test(value) && !needsEscaping(value)) {\n return value;\n }\n return escapeIfNeeded(snakeToCamelCase(value));\n}\n\nexport function pluralize(word: string): string {\n if (\n word.endsWith('s') ||\n word.endsWith('x') ||\n word.endsWith('z') ||\n word.endsWith('ch') ||\n word.endsWith('sh')\n ) {\n return `${word}es`;\n }\n if (word.endsWith('y') && !/[aeiou]y$/i.test(word)) {\n return `${word.slice(0, -1)}ies`;\n }\n return `${word}s`;\n}\n\nexport function deriveRelationFieldName(\n fkColumns: readonly string[],\n referencedTableName: string,\n): string {\n if (fkColumns.length === 1) {\n const [col = referencedTableName] = fkColumns;\n const stripped = col.replace(/_id$/i, '').replace(/Id$/, '');\n\n if (stripped.length > 0 && stripped !== col) {\n return escapeIfNeeded(snakeToCamelCase(stripped));\n }\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n }\n\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n}\n\nexport function deriveBackRelationFieldName(childModelName: string, isOneToOne: boolean): string {\n const base = childModelName.charAt(0).toLowerCase() + childModelName.slice(1);\n return isOneToOne ? base : pluralize(base);\n}\n\nexport function toNamedTypeName(columnName: string): string {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToPascalCase(columnName);\n } else {\n name = columnName.charAt(0).toUpperCase() + columnName.slice(1);\n }\n\n return escapeIfNeeded(name);\n}\n","import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst NEXTVAL_PATTERN = /^nextval\\s*\\(/i;\nconst NOW_FUNCTION_PATTERN = /^(now\\s*\\(\\s*\\)|CURRENT_TIMESTAMP)$/i;\nconst CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\\s*\\(\\s*\\)$/i;\nconst TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\\s+(?:with|without)\\s+time\\s+zone)?$/i;\nconst TEXT_CAST_SUFFIX = /::text$/i;\nconst NOW_LITERAL_PATTERN = /^'now'$/i;\nconst UUID_PATTERN = /^gen_random_uuid\\s*\\(\\s*\\)$/i;\nconst UUID_OSSP_PATTERN = /^uuid_generate_v4\\s*\\(\\s*\\)$/i;\nconst NULL_PATTERN = /^NULL(?:::.+)?$/i;\nconst TRUE_PATTERN = /^true$/i;\nconst FALSE_PATTERN = /^false$/i;\nconst NUMERIC_PATTERN = /^-?\\d+(\\.\\d+)?$/;\nconst JSON_CAST_SUFFIX = /::jsonb?$/i;\nconst STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:\"[^\"]+\"|[\\w\\s]+)(?:\\(\\d+\\))?)?$/;\n\nfunction canonicalizeTimestampDefault(expr: string): string | undefined {\n if (NOW_FUNCTION_PATTERN.test(expr)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return 'clock_timestamp()';\n\n if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return undefined;\n\n let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, '').trim();\n if (inner.startsWith('(') && inner.endsWith(')')) {\n inner = inner.slice(1, -1).trim();\n }\n\n if (NOW_FUNCTION_PATTERN.test(inner)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return 'clock_timestamp()';\n\n inner = inner.replace(TEXT_CAST_SUFFIX, '').trim();\n if (NOW_LITERAL_PATTERN.test(inner)) return 'now()';\n\n return undefined;\n}\n\nexport function parseRawDefault(\n rawDefault: string,\n nativeType?: string,\n): ColumnDefault | undefined {\n const trimmed = rawDefault.trim();\n const normalizedType = nativeType?.toLowerCase();\n\n if (NEXTVAL_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'autoincrement()' };\n }\n\n const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);\n if (canonicalTimestamp) {\n return { kind: 'function', expression: canonicalTimestamp };\n }\n\n if (UUID_PATTERN.test(trimmed) || UUID_OSSP_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'gen_random_uuid()' };\n }\n\n if (NULL_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: null };\n }\n\n if (TRUE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: true };\n }\n\n if (FALSE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: false };\n }\n\n if (NUMERIC_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: Number(trimmed) };\n }\n\n const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);\n if (stringMatch?.[1] !== undefined) {\n const unescaped = stringMatch[1].replace(/''/g, \"'\");\n if (normalizedType === 'json' || normalizedType === 'jsonb') {\n if (JSON_CAST_SUFFIX.test(trimmed)) {\n return { kind: 'function', expression: trimmed };\n }\n try {\n return { kind: 'literal', value: JSON.parse(unescaped) };\n } catch {\n // Fall through to the string form for malformed/non-JSON values.\n }\n }\n return { kind: 'literal', value: unescaped };\n }\n\n return { kind: 'function', expression: trimmed };\n}\n","import type { SqlForeignKeyIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';\nimport { backingIndexColumnKeys, isBackedByColumnKeys } from '../foreign-key-index-backing';\nimport { deriveBackRelationFieldName, deriveRelationFieldName, pluralize } from './name-transforms';\nimport type { RelationField } from './printer-config';\n\nconst DEFAULT_ON_DELETE = 'noAction';\nconst DEFAULT_ON_UPDATE = 'noAction';\n\nconst REFERENTIAL_ACTION_PSL: Record<string, string> = {\n noAction: 'NoAction',\n restrict: 'Restrict',\n cascade: 'Cascade',\n setNull: 'SetNull',\n setDefault: 'SetDefault',\n};\n\nexport type InferredRelations = {\n readonly relationsByTable: ReadonlyMap<string, readonly RelationField[]>;\n};\n\nexport function inferRelations(\n tables: Record<string, SqlTableIR>,\n modelNameMap: ReadonlyMap<string, string>,\n): InferredRelations {\n const relationsByTable = new Map<string, RelationField[]>();\n\n const fkCountByPair = new Map<string, number>();\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const pairKey = `${table.name}→${fk.referencedTable}`;\n fkCountByPair.set(pairKey, (fkCountByPair.get(pairKey) ?? 0) + 1);\n }\n }\n\n const usedFieldNames = new Map<string, Set<string>>();\n for (const table of Object.values(tables)) {\n const names = new Set<string>();\n for (const col of Object.values(table.columns)) {\n names.add(col.name);\n }\n usedFieldNames.set(table.name, names);\n }\n\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const childTableName = table.name;\n const parentTableName = fk.referencedTable;\n const childUsed = usedFieldNames.get(childTableName) as Set<string>;\n const childModelName = modelNameMap.get(childTableName) ?? childTableName;\n const parentModelName = modelNameMap.get(parentTableName) ?? parentTableName;\n const pairKey = `${childTableName}→${parentTableName}`;\n const isSelfRelation = childTableName === parentTableName;\n const needsRelationName = (fkCountByPair.get(pairKey) as number) > 1 || isSelfRelation;\n\n const isOneToOne = detectOneToOne(fk, table);\n\n const childRelFieldName = resolveUniqueFieldName(\n deriveRelationFieldName(fk.columns, parentTableName),\n childUsed,\n parentModelName,\n );\n const relationName = needsRelationName\n ? deriveRelationName(fk, childRelFieldName, parentModelName, isSelfRelation)\n : undefined;\n const childOptional = fk.columns.some(\n (columnName) => table.columns[columnName]?.nullable ?? false,\n );\n\n const childRelField = buildChildRelationField(\n childRelFieldName,\n parentModelName,\n fk,\n childOptional,\n relationName,\n table,\n );\n\n addRelationField(relationsByTable, childTableName, childRelField);\n childUsed.add(childRelFieldName);\n\n const parentUsed = usedFieldNames.get(parentTableName) ?? new Set();\n usedFieldNames.set(parentTableName, parentUsed);\n\n const backRelFieldName = resolveUniqueFieldName(\n deriveBackRelationFieldName(childModelName, isOneToOne),\n parentUsed,\n childModelName,\n );\n\n const backRelField: RelationField = {\n fieldName: backRelFieldName,\n typeName: childModelName,\n optional: isOneToOne,\n list: !isOneToOne,\n relationName,\n };\n\n addRelationField(relationsByTable, parentTableName, backRelField);\n parentUsed.add(backRelFieldName);\n }\n }\n\n return { relationsByTable };\n}\n\nfunction detectOneToOne(fk: SqlForeignKeyIR, table: SqlTableIR): boolean {\n const fkCols = [...fk.columns].sort();\n\n if (table.primaryKey) {\n const pkCols = [...table.primaryKey.columns].sort();\n if (pkCols.length === fkCols.length && pkCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n for (const unique of table.uniques) {\n const uniqueCols = [...unique.columns].sort();\n if (uniqueCols.length === fkCols.length && uniqueCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction deriveRelationName(\n fk: SqlForeignKeyIR,\n childRelationFieldName: string,\n parentModelName: string,\n isSelfRelation: boolean,\n): string {\n if (fk.name) {\n return fk.name;\n }\n if (isSelfRelation) {\n return `${childRelationFieldName.charAt(0).toUpperCase() + childRelationFieldName.slice(1)}${pluralize(parentModelName)}`;\n }\n return fk.columns.join('_');\n}\n\n/**\n * Builds the child-side {@link RelationField} for a single foreign key:\n * `typeName` is the parent model name, `fields`/`references` are the FK's raw\n * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.\n * Exported so a caller resolving a foreign key against a model outside\n * `tables` (e.g. a cross-space reference into another contract) can reuse the\n * same normalization instead of duplicating it.\n *\n * `hostTable` is the table the FK is declared on (i.e. `table`, not the\n * referenced parent) — its own indexes/uniques/primary key are what a\n * backing index for this FK would be. When none of them exactly match the\n * FK's source columns (in order), `index: false` is stamped so the emitted\n * `@relation` opts out of the framework's default derived backing-index\n * expectation, matching what `db verify` will find live. Passing the table\n * is optional so existing single-FK callers (e.g. cross-space resolution\n * before a host table reference is threaded through) can omit it and keep\n * the framework default.\n */\nexport function buildChildRelationField(\n fieldName: string,\n parentModelName: string,\n fk: SqlForeignKeyIR,\n optional: boolean,\n relationName?: string,\n hostTable?: SqlTableIR,\n): RelationField {\n const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : undefined;\n const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : undefined;\n const index =\n hostTable && !isBackedByColumnKeys(fk.columns, backingIndexColumnKeys(hostTable))\n ? false\n : undefined;\n\n return {\n fieldName,\n typeName: parentModelName,\n referencedTableName: fk.referencedTable,\n optional,\n list: false,\n relationName,\n fkName: fk.name,\n fields: fk.columns,\n references: fk.referencedColumns,\n onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : undefined,\n onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : undefined,\n index,\n };\n}\n\nfunction resolveUniqueFieldName(\n desired: string,\n usedNames: ReadonlySet<string>,\n fallbackSuffix: string,\n): string {\n if (!usedNames.has(desired)) {\n return desired;\n }\n\n const withSuffix = `${desired}${fallbackSuffix}`;\n if (!usedNames.has(withSuffix)) {\n return withSuffix;\n }\n\n let counter = 2;\n while (usedNames.has(`${desired}${counter}`)) {\n counter++;\n }\n return `${desired}${counter}`;\n}\n\nfunction addRelationField(\n map: Map<string, RelationField[]>,\n tableName: string,\n field: RelationField,\n): void {\n const existing = map.get(tableName);\n if (existing) {\n existing.push(field);\n } else {\n map.set(tableName, [field]);\n }\n}\n"],"mappings":"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"}
{"version":3,"file":"psl-infer.mjs","names":[],"sources":["../src/core/psl-contract-infer/default-mapping.ts","../src/core/psl-contract-infer/name-transforms.ts","../src/core/psl-contract-infer/raw-default-parser.ts","../src/core/psl-contract-infer/relation-inference.ts"],"sourcesContent":["import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst DEFAULT_FUNCTION_ATTRIBUTES: Readonly<Record<string, string>> = {\n 'autoincrement()': '@default(autoincrement())',\n 'now()': '@default(now())',\n};\n\nexport interface DefaultMappingOptions {\n readonly functionAttributes?: Readonly<Record<string, string>>;\n readonly fallbackFunctionAttribute?: ((expression: string) => string | undefined) | undefined;\n}\n\nexport type DefaultMappingResult = { readonly attribute: string } | { readonly comment: string };\n\nexport function mapDefault(\n columnDefault: ColumnDefault,\n options?: DefaultMappingOptions,\n): DefaultMappingResult {\n switch (columnDefault.kind) {\n case 'literal':\n return { attribute: `@default(${formatLiteralValue(columnDefault.value)})` };\n case 'function': {\n const attribute =\n options?.functionAttributes?.[columnDefault.expression] ??\n DEFAULT_FUNCTION_ATTRIBUTES[columnDefault.expression] ??\n options?.fallbackFunctionAttribute?.(columnDefault.expression);\n return attribute\n ? { attribute }\n : { comment: `// Raw default: ${columnDefault.expression.replace(/[\\r\\n]+/g, ' ')}` };\n }\n }\n}\n\nfunction formatLiteralValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n\n switch (typeof value) {\n case 'boolean':\n case 'number':\n return String(value);\n case 'string':\n return quoteString(value);\n default:\n return quoteString(JSON.stringify(value));\n }\n}\n\nfunction quoteString(str: string): string {\n return `\"${escapeString(str)}\"`;\n}\n\nfunction escapeString(str: string): string {\n return JSON.stringify(str).slice(1, -1);\n}\n","import pluralizeLib from 'pluralize';\n\nconst PSL_RESERVED_WORDS = new Set(['model', 'enum', 'types', 'type', 'generator', 'datasource']);\n\nconst IDENTIFIER_PART_PATTERN = /[A-Za-z0-9]+/g;\n\ntype NameResult = {\n readonly name: string;\n readonly map?: string;\n};\n\nfunction hasSeparators(input: string): boolean {\n return /[^A-Za-z0-9]/.test(input);\n}\n\nfunction extractIdentifierParts(input: string): string[] {\n return input.match(IDENTIFIER_PART_PATTERN) ?? [];\n}\n\nfunction createSyntheticIdentifier(input: string): string {\n let hash = 2166136261;\n\n for (const char of input) {\n hash ^= char.codePointAt(0) ?? 0;\n hash = Math.imul(hash, 16777619);\n }\n\n return `x${(hash >>> 0).toString(16)}`;\n}\n\nfunction sanitizeIdentifierCharacters(input: string): string {\n const sanitized = input.replace(/[^\\w]/g, '');\n return sanitized.length > 0 ? sanitized : createSyntheticIdentifier(input);\n}\n\nfunction capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1);\n}\n\nfunction snakeToPascalCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return capitalize(sanitizeIdentifierCharacters(input));\n }\n return parts.map(capitalize).join('');\n}\n\nfunction snakeToCamelCase(input: string): string {\n const parts = extractIdentifierParts(input);\n if (parts.length === 0) {\n return sanitizeIdentifierCharacters(input);\n }\n const [firstPart = input, ...rest] = parts;\n return firstPart.charAt(0).toLowerCase() + firstPart.slice(1) + rest.map(capitalize).join('');\n}\n\nfunction needsEscaping(name: string): boolean {\n return PSL_RESERVED_WORDS.has(name.toLowerCase()) || /^\\d/.test(name);\n}\n\nfunction escapeName(name: string): string {\n return `_${name}`;\n}\n\nfunction escapeIfNeeded(name: string): string {\n return needsEscaping(name) ? escapeName(name) : name;\n}\n\nexport function toModelName(tableName: string): NameResult {\n let name: string;\n\n if (hasSeparators(tableName)) {\n name = snakeToPascalCase(tableName);\n } else {\n name = tableName.charAt(0).toUpperCase() + tableName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: tableName };\n }\n\n if (name !== tableName) {\n return { name, map: tableName };\n }\n\n return { name };\n}\n\nexport function toFieldName(columnName: string): NameResult {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToCamelCase(columnName);\n } else {\n name = columnName.charAt(0).toLowerCase() + columnName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: columnName };\n }\n\n if (name !== columnName) {\n return { name, map: columnName };\n }\n\n return { name };\n}\n\nexport function toEnumName(pgTypeName: string): NameResult {\n let name: string;\n\n if (hasSeparators(pgTypeName)) {\n name = snakeToPascalCase(pgTypeName);\n } else {\n name = pgTypeName.charAt(0).toUpperCase() + pgTypeName.slice(1);\n }\n\n if (needsEscaping(name)) {\n const escaped = escapeName(name);\n return { name: escaped, map: pgTypeName };\n }\n\n if (name !== pgTypeName) {\n return { name, map: pgTypeName };\n }\n\n return { name };\n}\n\nconst VALID_IDENTIFIER_PATTERN = /^[A-Za-z_]\\w*$/;\n\n/**\n * PSL member name for a native-enum value. The value itself always prints\n * explicitly (`member = \"value\"`), so the returned name never needs a map:\n * a value that already is a valid, non-reserved identifier is kept verbatim\n * (case included); anything else is camelCased/escaped like a field name.\n */\nexport function toEnumMemberName(value: string): string {\n if (VALID_IDENTIFIER_PATTERN.test(value) && !needsEscaping(value)) {\n return value;\n }\n return escapeIfNeeded(snakeToCamelCase(value));\n}\n\nexport function pluralize(word: string): string {\n return pluralizeLib.plural(word);\n}\n\nexport function deriveRelationFieldName(\n fkColumns: readonly string[],\n referencedTableName: string,\n): string {\n if (fkColumns.length === 1) {\n const [col = referencedTableName] = fkColumns;\n const stripped = col.replace(/_id$/i, '').replace(/Id$/, '');\n\n if (stripped.length > 0 && stripped !== col) {\n return escapeIfNeeded(snakeToCamelCase(stripped));\n }\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n }\n\n return escapeIfNeeded(snakeToCamelCase(referencedTableName));\n}\n\nexport function deriveBackRelationFieldName(childModelName: string, isOneToOne: boolean): string {\n const base = childModelName.charAt(0).toLowerCase() + childModelName.slice(1);\n return isOneToOne ? base : pluralize(base);\n}\n\nexport function toNamedTypeName(columnName: string): string {\n let name: string;\n\n if (hasSeparators(columnName)) {\n name = snakeToPascalCase(columnName);\n } else {\n name = columnName.charAt(0).toUpperCase() + columnName.slice(1);\n }\n\n return escapeIfNeeded(name);\n}\n","import type { ColumnDefault } from '@prisma-next/contract/types';\n\nconst NEXTVAL_PATTERN = /^nextval\\s*\\(/i;\nconst NOW_FUNCTION_PATTERN = /^(now\\s*\\(\\s*\\)|CURRENT_TIMESTAMP)$/i;\nconst CLOCK_TIMESTAMP_PATTERN = /^clock_timestamp\\s*\\(\\s*\\)$/i;\nconst TIMESTAMP_CAST_SUFFIX = /::timestamp(?:tz|\\s+(?:with|without)\\s+time\\s+zone)?$/i;\nconst TEXT_CAST_SUFFIX = /::text$/i;\nconst NOW_LITERAL_PATTERN = /^'now'$/i;\nconst UUID_PATTERN = /^gen_random_uuid\\s*\\(\\s*\\)$/i;\nconst UUID_OSSP_PATTERN = /^uuid_generate_v4\\s*\\(\\s*\\)$/i;\nconst NULL_PATTERN = /^NULL(?:::.+)?$/i;\nconst TRUE_PATTERN = /^true$/i;\nconst FALSE_PATTERN = /^false$/i;\nconst NUMERIC_PATTERN = /^-?\\d+(\\.\\d+)?$/;\nconst JSON_CAST_SUFFIX = /::jsonb?$/i;\nconst STRING_LITERAL_PATTERN = /^'((?:[^']|'')*)'(?:::(?:\"[^\"]+\"|[\\w\\s]+)(?:\\(\\d+\\))?)?$/;\n\nfunction canonicalizeTimestampDefault(expr: string): string | undefined {\n if (NOW_FUNCTION_PATTERN.test(expr)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(expr)) return 'clock_timestamp()';\n\n if (!TIMESTAMP_CAST_SUFFIX.test(expr)) return undefined;\n\n let inner = expr.replace(TIMESTAMP_CAST_SUFFIX, '').trim();\n if (inner.startsWith('(') && inner.endsWith(')')) {\n inner = inner.slice(1, -1).trim();\n }\n\n if (NOW_FUNCTION_PATTERN.test(inner)) return 'now()';\n if (CLOCK_TIMESTAMP_PATTERN.test(inner)) return 'clock_timestamp()';\n\n inner = inner.replace(TEXT_CAST_SUFFIX, '').trim();\n if (NOW_LITERAL_PATTERN.test(inner)) return 'now()';\n\n return undefined;\n}\n\nexport function parseRawDefault(\n rawDefault: string,\n nativeType?: string,\n): ColumnDefault | undefined {\n const trimmed = rawDefault.trim();\n const normalizedType = nativeType?.toLowerCase();\n\n if (NEXTVAL_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'autoincrement()' };\n }\n\n const canonicalTimestamp = canonicalizeTimestampDefault(trimmed);\n if (canonicalTimestamp) {\n return { kind: 'function', expression: canonicalTimestamp };\n }\n\n if (UUID_PATTERN.test(trimmed) || UUID_OSSP_PATTERN.test(trimmed)) {\n return { kind: 'function', expression: 'gen_random_uuid()' };\n }\n\n if (NULL_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: null };\n }\n\n if (TRUE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: true };\n }\n\n if (FALSE_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: false };\n }\n\n if (NUMERIC_PATTERN.test(trimmed)) {\n return { kind: 'literal', value: Number(trimmed) };\n }\n\n const stringMatch = trimmed.match(STRING_LITERAL_PATTERN);\n if (stringMatch?.[1] !== undefined) {\n const unescaped = stringMatch[1].replace(/''/g, \"'\");\n if (normalizedType === 'json' || normalizedType === 'jsonb') {\n if (JSON_CAST_SUFFIX.test(trimmed)) {\n return { kind: 'function', expression: trimmed };\n }\n try {\n return { kind: 'literal', value: JSON.parse(unescaped) };\n } catch {\n // Fall through to the string form for malformed/non-JSON values.\n }\n }\n return { kind: 'literal', value: unescaped };\n }\n\n return { kind: 'function', expression: trimmed };\n}\n","import {\n backingIndexColumnKeys,\n isBackedByColumnKeys,\n} from '@prisma-next/sql-contract/foreign-key-materialization';\nimport type { SqlForeignKeyIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';\nimport { deriveBackRelationFieldName, deriveRelationFieldName, pluralize } from './name-transforms';\nimport type { RelationField } from './printer-config';\n\nconst DEFAULT_ON_DELETE = 'noAction';\nconst DEFAULT_ON_UPDATE = 'noAction';\n\nconst REFERENTIAL_ACTION_PSL: Record<string, string> = {\n noAction: 'NoAction',\n restrict: 'Restrict',\n cascade: 'Cascade',\n setNull: 'SetNull',\n setDefault: 'SetDefault',\n};\n\nexport type InferredRelations = {\n readonly relationsByTable: ReadonlyMap<string, readonly RelationField[]>;\n};\n\nexport function inferRelations(\n tables: Record<string, SqlTableIR>,\n modelNameMap: ReadonlyMap<string, string>,\n): InferredRelations {\n const relationsByTable = new Map<string, RelationField[]>();\n\n const fkCountByPair = new Map<string, number>();\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const pairKey = `${table.name}→${fk.referencedTable}`;\n fkCountByPair.set(pairKey, (fkCountByPair.get(pairKey) ?? 0) + 1);\n }\n }\n\n const usedFieldNames = new Map<string, Set<string>>();\n for (const table of Object.values(tables)) {\n const names = new Set<string>();\n for (const col of Object.values(table.columns)) {\n names.add(col.name);\n }\n usedFieldNames.set(table.name, names);\n }\n\n for (const table of Object.values(tables)) {\n for (const fk of table.foreignKeys) {\n const childTableName = table.name;\n const parentTableName = fk.referencedTable;\n const childUsed = usedFieldNames.get(childTableName) as Set<string>;\n const childModelName = modelNameMap.get(childTableName) ?? childTableName;\n const parentModelName = modelNameMap.get(parentTableName) ?? parentTableName;\n const pairKey = `${childTableName}→${parentTableName}`;\n const isSelfRelation = childTableName === parentTableName;\n const needsRelationName = (fkCountByPair.get(pairKey) as number) > 1 || isSelfRelation;\n\n const isOneToOne = detectOneToOne(fk, table);\n\n const childRelFieldName = resolveUniqueFieldName(\n deriveRelationFieldName(fk.columns, parentTableName),\n childUsed,\n parentModelName,\n );\n const relationName = needsRelationName\n ? deriveRelationName(fk, childRelFieldName, parentModelName, isSelfRelation)\n : undefined;\n const childOptional = fk.columns.some(\n (columnName) => table.columns[columnName]?.nullable ?? false,\n );\n\n const childRelField = buildChildRelationField(\n childRelFieldName,\n parentModelName,\n fk,\n childOptional,\n relationName,\n table,\n );\n\n addRelationField(relationsByTable, childTableName, childRelField);\n childUsed.add(childRelFieldName);\n\n const parentUsed = usedFieldNames.get(parentTableName) ?? new Set();\n usedFieldNames.set(parentTableName, parentUsed);\n\n const backRelFieldName = resolveUniqueFieldName(\n deriveBackRelationFieldName(childModelName, isOneToOne),\n parentUsed,\n childModelName,\n );\n\n const backRelField: RelationField = {\n fieldName: backRelFieldName,\n typeName: childModelName,\n optional: isOneToOne,\n list: !isOneToOne,\n relationName,\n };\n\n addRelationField(relationsByTable, parentTableName, backRelField);\n parentUsed.add(backRelFieldName);\n }\n }\n\n return { relationsByTable };\n}\n\nfunction detectOneToOne(fk: SqlForeignKeyIR, table: SqlTableIR): boolean {\n const fkCols = [...fk.columns].sort();\n\n if (table.primaryKey) {\n const pkCols = [...table.primaryKey.columns].sort();\n if (pkCols.length === fkCols.length && pkCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n for (const unique of table.uniques) {\n const uniqueCols = [...unique.columns].sort();\n if (uniqueCols.length === fkCols.length && uniqueCols.every((c, i) => c === fkCols[i])) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction deriveRelationName(\n fk: SqlForeignKeyIR,\n childRelationFieldName: string,\n parentModelName: string,\n isSelfRelation: boolean,\n): string {\n if (fk.name) {\n return fk.name;\n }\n if (isSelfRelation) {\n return `${childRelationFieldName.charAt(0).toUpperCase() + childRelationFieldName.slice(1)}${pluralize(parentModelName)}`;\n }\n return fk.columns.join('_');\n}\n\n/**\n * Builds the child-side {@link RelationField} for a single foreign key:\n * `typeName` is the parent model name, `fields`/`references` are the FK's raw\n * columns, and `onDelete`/`onUpdate` are normalized to their PSL spelling.\n * Exported so a caller resolving a foreign key against a model outside\n * `tables` (e.g. a cross-space reference into another contract) can reuse the\n * same normalization instead of duplicating it.\n *\n * `hostTable` is the table the FK is declared on (i.e. `table`, not the\n * referenced parent) — its own indexes/uniques/primary key are what a\n * backing index for this FK would be. When none of them exactly match the\n * FK's source columns (in order), `index: false` is stamped so the emitted\n * `@relation` opts out of the framework's default derived backing-index\n * expectation, matching what `db verify` will find live. Passing the table\n * is optional so existing single-FK callers (e.g. cross-space resolution\n * before a host table reference is threaded through) can omit it and keep\n * the framework default.\n */\nexport function buildChildRelationField(\n fieldName: string,\n parentModelName: string,\n fk: SqlForeignKeyIR,\n optional: boolean,\n relationName?: string,\n hostTable?: SqlTableIR,\n): RelationField {\n const onDelete = fk.onDelete && fk.onDelete !== DEFAULT_ON_DELETE ? fk.onDelete : undefined;\n const onUpdate = fk.onUpdate && fk.onUpdate !== DEFAULT_ON_UPDATE ? fk.onUpdate : undefined;\n const index =\n hostTable && !isBackedByColumnKeys(fk.columns, backingIndexColumnKeys(hostTable))\n ? false\n : undefined;\n\n return {\n fieldName,\n typeName: parentModelName,\n referencedTableName: fk.referencedTable,\n optional,\n list: false,\n relationName,\n fkName: fk.name,\n fields: fk.columns,\n references: fk.referencedColumns,\n onDelete: onDelete ? REFERENTIAL_ACTION_PSL[onDelete] : undefined,\n onUpdate: onUpdate ? REFERENTIAL_ACTION_PSL[onUpdate] : undefined,\n index,\n };\n}\n\nfunction resolveUniqueFieldName(\n desired: string,\n usedNames: ReadonlySet<string>,\n fallbackSuffix: string,\n): string {\n if (!usedNames.has(desired)) {\n return desired;\n }\n\n const withSuffix = `${desired}${fallbackSuffix}`;\n if (!usedNames.has(withSuffix)) {\n return withSuffix;\n }\n\n let counter = 2;\n while (usedNames.has(`${desired}${counter}`)) {\n counter++;\n }\n return `${desired}${counter}`;\n}\n\nfunction addRelationField(\n map: Map<string, RelationField[]>,\n tableName: string,\n field: RelationField,\n): void {\n const existing = map.get(tableName);\n if (existing) {\n existing.push(field);\n } else {\n map.set(tableName, [field]);\n }\n}\n"],"mappings":"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"}

@@ -5,3 +5,2 @@ import { ResolvedDomainModel, UNBOUND_DOMAIN_NAMESPACE_ID, resolveDomainModel } from "@prisma-next/contract/types";

import { RuntimeMutationDefaultGenerator } from "@prisma-next/sql-runtime";
//#region src/core/runtime-instance.d.ts

@@ -8,0 +7,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/core/runtime-instance.ts","../src/core/runtime-descriptor.ts","../src/core/timestamp-now-runtime-generator.ts"],"mappings":";;;;;;;;;;;AAUA;;;UAAiB,wBAAA,SAAiC,qBAAqB;;;;;;;AAAvE;;;;cCCa,0BAAA,EAA4B,uBAAuB,QAAQ,wBAAA;;;;;;;;ADDxE;;;;AAAuE;;;;ACCvE;iBCMgB,4BAAA,IAAgC,+BAA+B"}
{"version":3,"file":"runtime.d.mts","names":[],"sources":["../src/core/runtime-instance.ts","../src/core/runtime-descriptor.ts","../src/core/timestamp-now-runtime-generator.ts"],"mappings":";;;;;;;;;;;;;UAUiB,iCAAiC;;;;;;;;;;;cCCrC,4BAA4B,+BAA+B;;;;;;;;;;;;;;;;;iBCMxD,gCAAgC"}

@@ -1,2 +0,2 @@

import { t as TIMESTAMP_NOW_GENERATOR_ID } from "./timestamp-now-generator-CloimujU.mjs";
import { t as TIMESTAMP_NOW_GENERATOR_ID } from "./timestamp-now-generator-DRXygu32.mjs";
import { UNBOUND_DOMAIN_NAMESPACE_ID, resolveDomainModel } from "@prisma-next/contract/types";

@@ -3,0 +3,0 @@ import { resolveStorageTable } from "@prisma-next/sql-contract/resolve-storage-table";

import { ContractMarkerRecord } from "@prisma-next/contract/types";
//#region src/core/verify.d.ts

@@ -4,0 +3,0 @@ /**

@@ -1,1 +0,1 @@

{"version":3,"file":"verify.d.mts","names":[],"sources":["../src/core/verify.ts"],"mappings":";;;;;AAiDA;;;KAAY,iBAAA;EACV,SAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;EACA,UAAA,EAAY,IAAI;EAChB,OAAA;EACA,IAAA;EAIA,UAAA;AAAA;;AAAU;AAkBZ;;iBAAgB,sBAAA,CAAuB,GAAA,YAAe,oBAAoB"}
{"version":3,"file":"verify.d.mts","names":[],"sources":["../src/core/verify.ts"],"mappings":";;;;;;;KAiDY;EACV;EACA;EACA;EACA;EACA,YAAY;EACZ;EACA;EAIA;;;;;;iBAkBc,uBAAuB,eAAe"}
{
"name": "@prisma-next/family-sql",
"version": "0.15.0",
"version": "0.16.0-dev.1",
"license": "Apache-2.0",

@@ -9,26 +9,28 @@ "type": "module",

"dependencies": {
"@prisma-next/contract": "0.15.0",
"@prisma-next/emitter": "0.15.0",
"@prisma-next/framework-components": "0.15.0",
"@prisma-next/migration-tools": "0.15.0",
"@prisma-next/operations": "0.15.0",
"@prisma-next/sql-contract": "0.15.0",
"@prisma-next/sql-contract-emitter": "0.15.0",
"@prisma-next/sql-contract-ts": "0.15.0",
"@prisma-next/sql-operations": "0.15.0",
"@prisma-next/sql-relational-core": "0.15.0",
"@prisma-next/sql-runtime": "0.15.0",
"@prisma-next/sql-schema-ir": "0.15.0",
"@prisma-next/utils": "0.15.0",
"arktype": "^2.2.2"
"@prisma-next/contract": "0.16.0-dev.1",
"@prisma-next/emitter": "0.16.0-dev.1",
"@prisma-next/framework-components": "0.16.0-dev.1",
"@prisma-next/migration-tools": "0.16.0-dev.1",
"@prisma-next/operations": "0.16.0-dev.1",
"@prisma-next/sql-contract": "0.16.0-dev.1",
"@prisma-next/sql-contract-emitter": "0.16.0-dev.1",
"@prisma-next/sql-contract-ts": "0.16.0-dev.1",
"@prisma-next/sql-operations": "0.16.0-dev.1",
"@prisma-next/sql-relational-core": "0.16.0-dev.1",
"@prisma-next/sql-runtime": "0.16.0-dev.1",
"@prisma-next/sql-schema-ir": "0.16.0-dev.1",
"@prisma-next/utils": "0.16.0-dev.1",
"arktype": "^2.2.2",
"pluralize": "^8.0.0"
},
"devDependencies": {
"@prisma-next/driver-postgres": "0.15.0",
"@prisma-next/psl-parser": "0.15.0",
"@prisma-next/psl-printer": "0.15.0",
"@prisma-next/sql-contract-psl": "0.15.0",
"@prisma-next/test-utils": "0.15.0",
"@prisma-next/tsconfig": "0.15.0",
"@prisma-next/tsdown": "0.15.0",
"tsdown": "0.22.3",
"@prisma-next/driver-postgres": "0.16.0-dev.1",
"@prisma-next/psl-parser": "0.16.0-dev.1",
"@prisma-next/psl-printer": "0.16.0-dev.1",
"@prisma-next/sql-contract-psl": "0.16.0-dev.1",
"@prisma-next/test-utils": "0.16.0-dev.1",
"@prisma-next/tsconfig": "0.16.0-dev.1",
"@prisma-next/tsdown": "0.16.0-dev.1",
"@types/pluralize": "^0.0.33",
"tsdown": "0.22.8",
"typescript": "5.9.3",

@@ -35,0 +37,0 @@ "vitest": "4.1.10"

@@ -26,3 +26,3 @@ /**

} from '@prisma-next/framework-components/control';
import { dispositionForCategory } from '@prisma-next/framework-components/control';
import { dispositionForCategory, issueOutcome } from '@prisma-next/framework-components/control';
import { isStorageTypeInstance, type SqlStorage } from '@prisma-next/sql-contract/types';

@@ -105,6 +105,6 @@ import { RelationalSchemaNodeKind, type SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';

): VerifierIssueCategory {
if (issue.reason === 'not-found') {
if (issueOutcome(issue) === 'not-found') {
return 'declaredMissing';
}
if (issue.reason === 'not-expected') {
if (issueOutcome(issue) === 'not-expected') {
const granularity = classifyDiffSubjectGranularity(issue, granularityOf);

@@ -178,3 +178,3 @@ if (granularity === 'entity' || granularity === 'namespace') {

!input.strict &&
issue.reason === 'not-expected' &&
issueOutcome(issue) === 'not-expected' &&
isStrictOnlyExtra(issue, input.granularityOf)

@@ -306,3 +306,3 @@ ) {

ok,
...(ok ? {} : { code: 'PN-SCHEMA-0001' }),
...(ok ? {} : { code: 'CONTRACT.SCHEMA_VERIFICATION_FAILED' }),
summary: ok

@@ -309,0 +309,0 @@ ? 'Database schema satisfies contract'

@@ -7,3 +7,3 @@ import type { ControlPolicy } from '@prisma-next/contract/types';

} from '@prisma-next/framework-components/control';
import { dispositionForCategory } from '@prisma-next/framework-components/control';
import { dispositionForCategory, issueOutcome } from '@prisma-next/framework-components/control';

@@ -17,6 +17,6 @@ /**

export function classifyStorageTypeDiffIssue(issue: SchemaDiffIssue): VerifierIssueCategory {
if (issue.reason === 'not-found') {
if (issueOutcome(issue) === 'not-found') {
return 'declaredMissing';
}
if (issue.reason === 'not-expected') {
if (issueOutcome(issue) === 'not-expected') {
return 'extraAuxiliary';

@@ -23,0 +23,0 @@ }

import type { ColumnDefault, Contract, JsonValue } from '@prisma-next/contract/types';
import type { CodecRef } from '@prisma-next/framework-components/codec';
import type { MigrationPlannerConflict } from '@prisma-next/framework-components/control';
import type {
MigrationPlannerConflict,
SchemaNodeRef,
} from '@prisma-next/framework-components/control';
import {

@@ -15,4 +18,4 @@ type CheckConstraint,

} from '@prisma-next/sql-contract/types';
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
import {
RelationalSchemaNodeKind,
type SqlAnnotations,

@@ -29,3 +32,2 @@ type SqlCheckConstraintIRInput,

import { ifDefined } from '@prisma-next/utils/defined';
import { backingIndexColumnKeys } from '../foreign-key-index-backing';

@@ -60,2 +62,13 @@ /**

/**
* Target-supplied hook (same IoC seam as `NativeTypeExpander`/`DefaultRenderer`)
* that normalizes a contract-declared `ColumnDefault` into the resolved shape
* the target's introspection parses from the live database — e.g. a
* `dbgenerated("'{}'::jsonb")` function call and the literal Postgres reports
* are the same value in different shapes, and `resolvedDefaultsEqual`
* compares `kind` before content. When omitted, the contract's raw default
* is the resolved default unchanged.
*/
export type DefaultResolver = (def: ColumnDefault, resolvedNativeType: string) => ColumnDefault;
/**
* Target-supplied callback that resolves a contract namespace to the live

@@ -81,2 +94,3 @@ * database schema its enums are stored under.

renderDefault: DefaultRenderer | undefined,
resolveDefault: DefaultResolver | undefined,
): SqlColumnIRInput {

@@ -112,2 +126,7 @@ // Resolve `typeRef` so columns that delegate their `nativeType`/`codecId`/

const resolvedNativeType = column.many ? `${baseNativeType}[]` : baseNativeType;
const rawColumnDefault = column.default ?? undefined;
const resolvedColumnDefault =
rawColumnDefault !== undefined && resolveDefault
? resolveDefault(rawColumnDefault, resolvedNativeType)
: rawColumnDefault;
return {

@@ -123,7 +142,10 @@ name,

// Contract-derived columns are resolved by construction: the computed
// full native type doubles as the resolved value, and the contract's
// structured default is the resolved default (the introspected side
// stamps its normalizer's parse of the raw expression).
// full native type doubles as the resolved value. The contract's raw
// structured default becomes the resolved default after passing through
// the target's `resolveDefault` hook (when supplied), so a default the
// target's introspection side would normalize differently (e.g. a
// `dbgenerated(...)` function call that is actually a literal) compares
// equal instead of drifting on `kind` alone.
resolvedNativeType,
...ifDefined('resolvedDefault', column.default ?? undefined),
...ifDefined('resolvedDefault', resolvedColumnDefault),
// The column's codec identity, carried the same way the query AST

@@ -255,10 +277,11 @@ // carries `CodecRef` (TML-2456) — the migration planner's op-builders

function convertUnique(unique: UniqueConstraint): SqlUniqueIRInput {
function convertUnique(unique: UniqueConstraint, tableName: string): SqlUniqueIRInput {
return {
columns: unique.columns,
...ifDefined('name', unique.name),
dependsOn: flatColumnDependsOn(tableName, unique.columns),
};
}
function convertIndex(index: Index): SqlIndexIRInput {
function convertIndex(index: Index, tableName: string): SqlIndexIRInput {
return {

@@ -272,2 +295,3 @@ columns: index.columns,

...ifDefined('options', index.options),
dependsOn: flatColumnDependsOn(tableName, index.columns),
};

@@ -277,2 +301,34 @@ }

/**
* The referenced table's chain in the flat (single-schema) tree
* `contractToSchemaIR`/`contractNamespaceToSchemaIR` build: the root
* (`SqlSchemaIR`, fixed `'database'` id) followed by the table's own id.
* Postgres discards this when it re-derives the FK against its own
* multi-schema tree shape (`contractToPostgresDatabaseSchemaNode`); SQLite's
* flat tree uses it as-is.
*/
function flatSchemaDependsOn(tableName: string): SchemaNodeRef {
return [
{ nodeKind: RelationalSchemaNodeKind.schema, id: 'database' },
{ nodeKind: RelationalSchemaNodeKind.table, id: tableName },
];
}
/**
* The chains from a table-child object (foreign key, index, unique, primary
* key) to each of the own columns it is built on, in the flat tree. Dropping
* a covered column auto-drops the object, so the object's drop must precede
* the column's; the graph derives that direction from these edges.
*/
function flatColumnDependsOn(
tableName: string,
columns: readonly string[],
): readonly SchemaNodeRef[] {
return columns.map((column) => [
{ nodeKind: RelationalSchemaNodeKind.schema, id: 'database' },
{ nodeKind: RelationalSchemaNodeKind.table, id: tableName },
{ nodeKind: RelationalSchemaNodeKind.column, id: `column:${column}` },
]);
}
/**
* The FK's referenced-namespace identity comes from the target's namespace

@@ -286,2 +342,6 @@ * node, not the raw namespace-id string. An unbound target namespace stamps

* real DDL schema downstream.
*
* `dependsOn` carries the referenced table (created before the FK, dropped
* after it) plus the FK's own columns (dropped after the FK, since dropping a
* column auto-drops the FK built on it).
*/

@@ -299,2 +359,6 @@ function convertForeignKey(fk: ForeignKey, storage: SqlStorage): SqlForeignKeyIRInput {

...ifDefined('onUpdate', fk.onUpdate),
dependsOn: [
flatSchemaDependsOn(fk.target.tableName),
...flatColumnDependsOn(fk.source.tableName, fk.source.columns),
],
};

@@ -309,2 +373,3 @@ }

renderDefault: DefaultRenderer | undefined,
resolveDefault: DefaultResolver | undefined,
storage: SqlStorage,

@@ -320,19 +385,6 @@ ): SqlTableIR {

renderDefault,
resolveDefault,
);
}
const satisfiedIndexColumns = new Set(backingIndexColumnKeys(table));
const fkBackingIndexes: SqlIndexIRInput[] = [];
for (const fk of table.foreignKeys) {
if (fk.index === false) continue;
const key = fk.source.columns.join(',');
if (satisfiedIndexColumns.has(key)) continue;
fkBackingIndexes.push({
columns: fk.source.columns,
unique: false,
name: defaultIndexName(name, fk.source.columns),
});
satisfiedIndexColumns.add(key);
}
const checks: SqlCheckConstraintIRInput[] | undefined =

@@ -343,11 +395,23 @@ table.checks && table.checks.length > 0

const primaryKey =
table.primaryKey !== undefined
? {
columns: table.primaryKey.columns,
...ifDefined('name', table.primaryKey.name),
dependsOn: flatColumnDependsOn(name, table.primaryKey.columns),
}
: undefined;
return new SqlTableIR({
name,
columns,
...ifDefined('primaryKey', table.primaryKey),
foreignKeys: table.foreignKeys
.filter((fk) => fk.constraint !== false)
.map((fk) => convertForeignKey(fk, storage)),
uniques: table.uniques.map(convertUnique),
indexes: [...table.indexes.map(convertIndex), ...fkBackingIndexes],
...ifDefined('primaryKey', primaryKey),
// #989 persists a `constraint: false` FK's absence and its backing index as
// discrete entities at contract construction, so every `foreignKeys[]` entry
// is now constraint-bearing (no filter) and each FK-backing index is already
// a `table.indexes[]` entry — it flows through `convertIndex` below, carrying
// the object→own-column `dependsOn` edge like any other index.
foreignKeys: table.foreignKeys.map((fk) => convertForeignKey(fk, storage)),
uniques: table.uniques.map((u) => convertUnique(u, name)),
indexes: table.indexes.map((i) => convertIndex(i, name)),
...ifDefined('checks', checks),

@@ -419,2 +483,3 @@ });

readonly renderDefault?: DefaultRenderer;
readonly resolveDefault?: DefaultResolver;
/**

@@ -480,2 +545,3 @@ * Target-supplied resolver mapping a namespace to the live database schema

options.renderDefault,
options.resolveDefault,
storage,

@@ -517,2 +583,3 @@ );

options.renderDefault,
options.resolveDefault,
storage,

@@ -519,0 +586,0 @@ );

@@ -422,12 +422,12 @@ import type { Contract } from '@prisma-next/contract/types';

export type SqlMigrationRunnerErrorCode =
| 'DESTINATION_CONTRACT_MISMATCH'
| 'LEGACY_MARKER_SHAPE'
| 'MARKER_ORIGIN_MISMATCH'
| 'MARKER_CAS_FAILURE'
| 'POLICY_VIOLATION'
| 'PRECHECK_FAILED'
| 'POSTCHECK_FAILED'
| 'SCHEMA_VERIFY_FAILED'
| 'FOREIGN_KEY_VIOLATION'
| 'EXECUTION_FAILED';
| 'MIGRATION.DESTINATION_CONTRACT_MISMATCH'
| 'MIGRATION.LEGACY_MARKER_SHAPE'
| 'MIGRATION.MARKER_ORIGIN_MISMATCH'
| 'MIGRATION.MARKER_CAS_FAILURE'
| 'MIGRATION.POLICY_VIOLATION'
| 'MIGRATION.PRECHECK_FAILED'
| 'MIGRATION.POSTCHECK_FAILED'
| 'MIGRATION.SCHEMA_VERIFY_FAILED'
| 'MIGRATION.FOREIGN_KEY_VIOLATION'
| 'MIGRATION.EXECUTION_FAILED';

@@ -434,0 +434,0 @@ export interface SqlMigrationRunnerFailure extends MigrationRunnerFailure {

@@ -0,1 +1,3 @@

import pluralizeLib from 'pluralize';
const PSL_RESERVED_WORDS = new Set(['model', 'enum', 'types', 'type', 'generator', 'datasource']);

@@ -146,15 +148,3 @@

export function pluralize(word: string): string {
if (
word.endsWith('s') ||
word.endsWith('x') ||
word.endsWith('z') ||
word.endsWith('ch') ||
word.endsWith('sh')
) {
return `${word}es`;
}
if (word.endsWith('y') && !/[aeiou]y$/i.test(word)) {
return `${word.slice(0, -1)}ies`;
}
return `${word}s`;
return pluralizeLib.plural(word);
}

@@ -161,0 +151,0 @@

@@ -0,3 +1,6 @@

import {
backingIndexColumnKeys,
isBackedByColumnKeys,
} from '@prisma-next/sql-contract/foreign-key-materialization';
import type { SqlForeignKeyIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
import { backingIndexColumnKeys, isBackedByColumnKeys } from '../foreign-key-index-backing';
import { deriveBackRelationFieldName, deriveRelationFieldName, pluralize } from './name-transforms';

@@ -4,0 +7,0 @@ import type { RelationField } from './printer-config';

@@ -76,1 +76,89 @@ import type { AuthoringFieldPresetDescriptor } from '@prisma-next/framework-components/authoring';

}
const TEMPORAL_PRECISION_ARG = {
name: 'precision',
kind: 'number',
optional: true,
integer: true,
minimum: 0,
} as const;
const TEMPORAL_ON_CREATE_ARG = {
name: 'onCreate',
kind: 'option',
values: ['now'],
optional: true,
} as const;
const TEMPORAL_ON_UPDATE_ARG = {
name: 'onUpdate',
kind: 'option',
values: ['now'],
optional: true,
} as const;
/**
* Selects the `timestampNow` generator descriptor for the preset's `now`
* token. The token is preset vocabulary; the generator id never appears in a
* user's spelling (ADR 169 — `timestampNow` is preset-only).
*/
function temporalPhaseTemplate<const Index extends number>(index: Index) {
return {
kind: 'select',
index,
cases: { now: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID } },
} as const;
}
/**
* Builds a `temporal.<codec>` field preset for a codec that takes a precision
* parameter (`pg/timestamp@1`, `pg/timestamptz@1`). Arguments change field
* properties only — never the codec, which the caller fixes here.
*
* All three arguments are optional: omitting `precision` omits `typeParams`
* entirely, and omitting a phase omits that phase (both omitted omits
* `executionDefaults`).
*/
/* @__NO_SIDE_EFFECTS__ */
export function temporalCodecPresetWithPrecision<
const CodecId extends string,
const NativeType extends string,
>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {
return {
kind: 'fieldPreset',
args: [TEMPORAL_PRECISION_ARG, TEMPORAL_ON_CREATE_ARG, TEMPORAL_ON_UPDATE_ARG],
output: {
codecId: input.codecId,
nativeType: input.nativeType,
typeParams: { precision: { kind: 'arg', index: 0 } },
executionDefaults: {
onCreate: temporalPhaseTemplate(1),
onUpdate: temporalPhaseTemplate(2),
},
},
} as const satisfies AuthoringFieldPresetDescriptor;
}
/**
* Builds a `temporal.<codec>` field preset for a codec with no type
* parameters (`sqlite/datetime@1`). As with the precision-bearing variant,
* both phase arguments are optional and omitting one omits that phase.
*/
/* @__NO_SIDE_EFFECTS__ */
export function temporalCodecPreset<
const CodecId extends string,
const NativeType extends string,
>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {
return {
kind: 'fieldPreset',
args: [TEMPORAL_ON_CREATE_ARG, TEMPORAL_ON_UPDATE_ARG],
output: {
codecId: input.codecId,
nativeType: input.nativeType,
executionDefaults: {
onCreate: temporalPhaseTemplate(0),
onUpdate: temporalPhaseTemplate(1),
},
},
} as const satisfies AuthoringFieldPresetDescriptor;
}

@@ -24,2 +24,3 @@ import { SqlFamilyDescriptor } from '../core/control-descriptor';

DefaultRenderer,
DefaultResolver,
EnumNamespaceSchemaResolver,

@@ -91,2 +92,4 @@ NativeTypeExpander,

temporalAuthoringPresets,
temporalCodecPreset,
temporalCodecPresetWithPrecision,
timestampNowControlDescriptor,

@@ -93,0 +96,0 @@ } from '../core/timestamp-now-generator';

import { ControlAdapterInstance, ControlStack } from "@prisma-next/framework-components/control";
import { ColumnDefault, ContractMarkerRecord, LedgerEntryRecord } from "@prisma-next/contract/types";
import { SqlControlDriverInstance } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { AnyQueryAst, DdlNode, LoweredStatement, LowererContext, SqlExecuteRequest } from "@prisma-next/sql-relational-core/ast";
//#region src/core/diff/sql-schema-diff.d.ts
/**
* Function type for normalizing raw database default expressions into ColumnDefault.
* Target-specific implementations handle database dialect differences.
*/
type DefaultNormalizer = (rawDefault: string, nativeType: string) => ColumnDefault | undefined;
/**
* Function type for normalizing schema native types to canonical form for comparison.
* Target-specific implementations handle dialect-specific type name variations
* (e.g., Postgres 'varchar' → 'character varying', 'timestamptz' normalization).
*/
type NativeTypeNormalizer = (nativeType: string) => string;
/**
* Compares two arrays of strings for equality (order-sensitive).
*/
declare function arraysEqual(a: readonly string[], b: readonly string[]): boolean;
//#endregion
//#region src/core/control-adapter.d.ts
/**
* Structural interface for anything that can lower a SQL/DDL AST node to a
* `LoweredStatement`. `SqlControlAdapter` satisfies this interface; the
* migration planner and op-factory calls accept `Lowerer` rather than the
* full `SqlControlAdapter` so they are not coupled to the broader control
* adapter surface.
*/
interface Lowerer {
lower(ast: AnyQueryAst | DdlNode, context: LowererContext<unknown>): LoweredStatement;
}
/**
* Extends {@link Lowerer} with async codec-routed DDL lowering. Control
* adapters implement this; the planner's `CreateTableCall.toOp` and
* `CreateSchemaCall.toOp` accept it to produce executable DDL statements
* with literal defaults encoded through their codec.
*/
interface ExecuteRequestLowerer extends Lowerer {
lowerToExecuteRequest(ast: AnyQueryAst | DdlNode, context?: LowererContext<unknown>): Promise<SqlExecuteRequest>;
}
/**
* SQL control adapter interface for control-plane operations.
* Implemented by target-specific adapters (e.g., Postgres, MySQL).
*
* @template TTarget - The target ID (e.g., 'postgres', 'mysql')
*/
interface SqlControlAdapter<TTarget extends string = string> extends ControlAdapterInstance<'sql', TTarget>, ExecuteRequestLowerer {
/**
* Reads the contract marker for `space` from the database, returning
* `null` if no marker row exists for that space (or if the marker
* table itself is missing). Implementations are responsible for the
* dialect-specific existence probe (e.g. Postgres
* `information_schema.tables` vs SQLite `sqlite_master`) and parameter
* placeholders.
*
* `space` is required so callers cannot accidentally fall through to
* the app's marker row when reading per-extension markers.
*
* @param driver - ControlDriverInstance for executing queries (target-specific)
* @param space - Contract space id whose marker row to read (e.g. `'app'`)
* @returns Resolved marker record, or `null` if not yet stamped.
*/
readMarker(driver: SqlControlDriverInstance<TTarget>, space: string): Promise<ContractMarkerRecord | null>;
/**
* Reads every marker row from `prisma_contract.marker` (one per
* contract space) and returns them keyed by `space`. Used by the
* per-space verifier to detect marker-vs-on-disk drift and orphan
* marker rows. Returns an empty map when the marker table does not
* yet exist (fresh database / never-signed project).
*/
readAllMarkers(driver: SqlControlDriverInstance<TTarget>): Promise<ReadonlyMap<string, ContractMarkerRecord>>;
/**
* Reads the per-migration ledger journal in apply order. When `space` is
* omitted, returns rows for every space.
*/
readLedger(driver: SqlControlDriverInstance<TTarget>, space?: string): Promise<readonly LedgerEntryRecord[]>;
/**
* Inserts the initial marker row for `space` (`INSERT` only). Fails when a
* row for that space already exists. Used by `sign()` so concurrent first-time
* stamps cannot silently overwrite each other. `updated_at` is DB-side
* (`now()` / `datetime('now')`). Mirrors `MongoControlAdapter.initMarker`.
*/
insertMarker(driver: SqlControlDriverInstance<TTarget>, space: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<void>;
/**
* Writes the initial marker row for `space` as an upsert (`INSERT … ON
* CONFLICT (space) DO UPDATE SET …`), so re-stamping a space overwrites the
* existing row rather than failing. `updated_at` is stamped with a DB-side
* time expression (`now()` / `datetime('now')`), never an app-side clock.
*/
initMarker(driver: SqlControlDriverInstance<TTarget>, space: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<void>;
/**
* Atomically advances the marker row for `space` (compare-and-swap on
* `core_hash = expectedFrom`). Returns `true` when the swap matched a row,
* `false` when another process advanced the marker first. `destination.invariants`
* is written verbatim when supplied (the union/dedupe policy lives upstream)
* and left untouched when omitted. Mirrors `MongoControlAdapter.updateMarker`.
*/
updateMarker(driver: SqlControlDriverInstance<TTarget>, space: string, expectedFrom: string, destination: {
readonly storageHash: string;
readonly profileHash: string;
readonly invariants?: readonly string[];
}): Promise<boolean>;
/**
* Appends a ledger entry for `space`. Mirrors `MongoControlAdapter.writeLedgerEntry`.
* The SQL ledger table keys rows by an autoincrement id and partitions reads
* by `space`, so `entry.edgeId` carries no dedicated column; `from` / `to`
* land in `origin_core_hash` / `destination_core_hash`.
*/
writeLedgerEntry(driver: SqlControlDriverInstance<TTarget>, space: string, entry: {
readonly edgeId: string;
readonly from: string;
readonly to: string;
readonly migrationName: string;
readonly migrationHash: string;
readonly operations: readonly unknown[];
readonly destinationContractJson?: unknown;
}): Promise<void>;
/**
* Introspects a database schema and returns the target's schema-IR node.
*
* This is a pure schema discovery operation that queries the database catalog
* and returns the schema structure without type mapping or contract enrichment.
* Type mapping and enrichment are handled separately by enrichment helpers.
*
* The return type is the family-base `SqlSchemaIRNode` so each target returns
* its own node shape: SQLite returns a flat `SqlSchemaIR`, Postgres returns a
* `PostgresDatabaseSchemaNode` tree root. Consumers `ensure` the concrete
* target type before walking it.
*
* @param driver - ControlDriverInstance instance for executing queries (target-specific)
* @param contract - Optional contract for contract-guided introspection (filtering, optimization)
* @param schema - Schema name to introspect (defaults to 'public')
* @returns Promise resolving to the live database schema node
*/
introspect(driver: SqlControlDriverInstance<TTarget>, contract?: unknown, schema?: string): Promise<SqlSchemaIRNode>;
/**
* Optional target-specific normalizer for raw database default expressions.
* When provided, schema defaults (raw strings) are normalized before comparison
* with contract defaults (ColumnDefault objects) during schema verification.
*/
readonly normalizeDefault?: DefaultNormalizer;
/**
* Optional target-specific normalizer for schema native type names.
* When provided, schema native types (from introspection) are normalized
* before comparison with contract native types during schema verification.
*/
readonly normalizeNativeType?: NativeTypeNormalizer;
/**
* Ordered DDL queries that bootstrap marker/ledger control tables for migration
* runners. Postgres includes `CREATE SCHEMA`; SQLite does not.
*/
bootstrapControlTableQueries(): readonly DdlNode[];
/**
* Ordered DDL queries that bootstrap the marker table (and Postgres schema) for
* `sign` — excludes the ledger table.
*/
bootstrapSignMarkerQueries(): readonly DdlNode[];
}
/**
* SQL control adapter descriptor interface.
* Provides a factory method to create control adapter instances.
*
* @template TTarget - The target ID (e.g., 'postgres', 'mysql')
*/
interface SqlControlAdapterDescriptor<TTarget extends string = string> {
/**
* Creates a SQL control adapter instance for control-plane operations.
*
* Receives the assembled `ControlStack` so adapters can read aggregated
* metadata (codec lookup, extension contributions) when materializing.
*/
create(stack: ControlStack<'sql', TTarget>): SqlControlAdapter<TTarget>;
}
//#endregion
export { NativeTypeNormalizer as a, SqlControlAdapterDescriptor as i, Lowerer as n, arraysEqual as o, SqlControlAdapter as r, ExecuteRequestLowerer as t };
//# sourceMappingURL=control-adapter-BQgad8Zc.d.mts.map
{"version":3,"file":"control-adapter-BQgad8Zc.d.mts","names":[],"sources":["../src/core/diff/sql-schema-diff.ts","../src/core/control-adapter.ts"],"mappings":";;;;;;;;;;;KAeY,iBAAA,IACV,UAAA,UACA,UAAA,aACG,aAAa;;AAAA;AAOlB;;;KAAY,oBAAA,IAAwB,UAAkB;AAAA;AAKtD;;AALsD,iBAKtC,WAAA,CAAY,CAAA,qBAAsB,CAAoB;;;;AAftE;;;;;;UCQiB,OAAA;EACf,KAAA,CAAM,GAAA,EAAK,WAAA,GAAc,OAAA,EAAS,OAAA,EAAS,cAAA,YAA0B,gBAAA;AAAA;ADCvE;;;;AAAsD;AAKtD;AALA,UCQiB,qBAAA,SAA8B,OAAA;EAC7C,qBAAA,CACE,GAAA,EAAK,WAAA,GAAc,OAAA,EACnB,OAAA,GAAU,cAAA,YACT,OAAA,CAAQ,iBAAA;AAAA;ADPyD;;;;ACPtE;;ADOsE,UCgBrD,iBAAA,0CACP,sBAAA,QAA8B,OAAA,GACpC,qBAAA;EAxBS;;;;;;;;;;;;;;AAA0E;EAwCrF,UAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,WACC,OAAA,CAAQ,oBAAA;EAlC0B;;;;;;;EA2CrC,cAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,IAChC,OAAA,CAAQ,WAAA,SAAoB,oBAAA;EA7CqB;;;;EAmDpD,UAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,YACC,OAAA,UAAiB,iBAAA;EApDC;;;;;;EA4DrB,YAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,UACA,WAAA;IAAA,SACW,WAAA;IAAA,SACA,WAAA;IAAA,SACA,UAAA;EAAA,IAEV,OAAA;EAxDmC;;;;;;EAgEtC,UAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,UACA,WAAA;IAAA,SACW,WAAA;IAAA,SACA,WAAA;IAAA,SACA,UAAA;EAAA,IAEV,OAAA;EAlCO;;;;;;;EA2CV,YAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,UACA,YAAA,UACA,WAAA;IAAA,SACW,WAAA;IAAA,SACA,WAAA;IAAA,SACA,UAAA;EAAA,IAEV,OAAA;EASgC;;;;;;EADnC,gBAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,KAAA,UACA,KAAA;IAAA,SACW,MAAA;IAAA,SACA,IAAA;IAAA,SACA,EAAA;IAAA,SACA,aAAA;IAAA,SACA,aAAA;IAAA,SACA,UAAA;IAAA,SACA,uBAAA;EAAA,IAEV,OAAA;EA/G8B;;;;;;;;;;;;;;;;;EAkIjC,UAAA,CACE,MAAA,EAAQ,wBAAA,CAAyB,OAAA,GACjC,QAAA,YACA,MAAA,YACC,OAAA,CAAQ,eAAA;EAhGX;;;;;EAAA,SAuGS,gBAAA,GAAmB,iBAAA;EApGR;;;;;EAAA,SA2GX,mBAAA,GAAsB,oBAAA;EA/FlB;;;;EAqGb,4BAAA,aAAyC,OAAA;EAzFzC;;;;EA+FA,0BAAA,aAAuC,OAAA;AAAA;;;;;;;UASxB,2BAAA;EAtFb;;;;;;EA6FF,MAAA,CAAO,KAAA,EAAO,YAAA,QAAoB,OAAA,IAAW,iBAAA,CAAkB,OAAA;AAAA"}
//#region src/core/foreign-key-index-backing.ts
/**
* The column-list keys (`"colA,colB"`, order preserved) a table's own
* indexes, unique constraints, and primary key already back. A foreign key
* whose source columns join to one of these keys needs no separately
* derived backing index.
*
* Shared by {@link isBackedByColumnKeys}'s two callers, which must agree on
* what counts as "backed": `contract-to-schema-ir.ts`'s `convertTable`
* (deriving the FK-backing-index expectation `db verify` checks against a
* live database) and the postgres PSL inferrer (deciding whether an
* introspected relation needs an explicit `index: false`).
*/
function backingIndexColumnKeys(table) {
return [
...table.indexes.map((index) => index.columns.join(",")),
...table.uniques.map((unique) => unique.columns.join(",")),
...table.primaryKey ? [table.primaryKey.columns.join(",")] : []
];
}
/**
* Whether `columns`, in order, matches one of `backingKeys` (see
* {@link backingIndexColumnKeys}). Order-sensitive: `(a, b)` does not
* satisfy a backing index declared as `(b, a)`.
*/
function isBackedByColumnKeys(columns, backingKeys) {
return backingKeys.includes(columns.join(","));
}
//#endregion
export { isBackedByColumnKeys as n, backingIndexColumnKeys as t };
//# sourceMappingURL=foreign-key-index-backing-BP71iI-Q.mjs.map
{"version":3,"file":"foreign-key-index-backing-BP71iI-Q.mjs","names":[],"sources":["../src/core/foreign-key-index-backing.ts"],"sourcesContent":["export type BackingIndexCandidates = {\n readonly indexes: readonly { readonly columns: readonly string[] }[];\n readonly uniques: readonly { readonly columns: readonly string[] }[];\n readonly primaryKey?: { readonly columns: readonly string[] } | undefined;\n};\n\n/**\n * The column-list keys (`\"colA,colB\"`, order preserved) a table's own\n * indexes, unique constraints, and primary key already back. A foreign key\n * whose source columns join to one of these keys needs no separately\n * derived backing index.\n *\n * Shared by {@link isBackedByColumnKeys}'s two callers, which must agree on\n * what counts as \"backed\": `contract-to-schema-ir.ts`'s `convertTable`\n * (deriving the FK-backing-index expectation `db verify` checks against a\n * live database) and the postgres PSL inferrer (deciding whether an\n * introspected relation needs an explicit `index: false`).\n */\nexport function backingIndexColumnKeys(table: BackingIndexCandidates): readonly string[] {\n return [\n ...table.indexes.map((index) => index.columns.join(',')),\n ...table.uniques.map((unique) => unique.columns.join(',')),\n ...(table.primaryKey ? [table.primaryKey.columns.join(',')] : []),\n ];\n}\n\n/**\n * Whether `columns`, in order, matches one of `backingKeys` (see\n * {@link backingIndexColumnKeys}). Order-sensitive: `(a, b)` does not\n * satisfy a backing index declared as `(b, a)`.\n */\nexport function isBackedByColumnKeys(\n columns: readonly string[],\n backingKeys: readonly string[],\n): boolean {\n return backingKeys.includes(columns.join(','));\n}\n"],"mappings":";;;;;;;;;;;;;AAkBA,SAAgB,uBAAuB,OAAkD;CACvF,OAAO;EACL,GAAG,MAAM,QAAQ,KAAK,UAAU,MAAM,QAAQ,KAAK,GAAG,CAAC;EACvD,GAAG,MAAM,QAAQ,KAAK,WAAW,OAAO,QAAQ,KAAK,GAAG,CAAC;EACzD,GAAI,MAAM,aAAa,CAAC,MAAM,WAAW,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC;CACjE;AACF;;;;;;AAOA,SAAgB,qBACd,SACA,aACS;CACT,OAAO,YAAY,SAAS,QAAQ,KAAK,GAAG,CAAC;AAC/C"}
import { SchemaDiffIssue } from "@prisma-next/framework-components/control";
import { Contract, ControlPolicy } from "@prisma-next/contract/types";
import { SqlStorage } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
//#region src/core/migrations/schema-differ.d.ts
/**
* The full-tree node diff a SQL target produces for the family verify
* verdict: the target derives the expected tree from the contract, applies
* the pre-diff normalizations (semantic satisfaction, FK schema-segment
* resolution), runs the generic differ, and ownership-scopes the result.
* Strict gating, control-policy disposition, and the verdict itself are the
* family's post-diff filters over this output.
*/
interface SqlSchemaDiffResult {
/** The full, ownership-scoped diff issue list. */
readonly issues: readonly SchemaDiffIssue[];
/**
* Resolves a diff issue's subject table's declared control policy directly
* from the contract (Decision 5's own-layer-per-concern discipline extends
* here too: control policy is a contract concern, resolved by the target
* at disposition time — never stamped on the diff node). `undefined`
* when the issue's path resolves to no contract table (a genuine orphan,
* or a non-table subject).
*/
readonly resolveControlPolicy: (issue: SchemaDiffIssue) => ControlPolicy | undefined;
/**
* The expected/actual namespace-node pairs the codec `verifyType` hooks
* run over — one per contract namespace with tables, paired by DDL
* schema; a flat target repeats its sole actual root per such namespace.
*/
readonly namespacePairs: ReadonlyArray<{
readonly actual: SqlSchemaIRNode | undefined;
}>;
}
interface SqlSchemaDiffInput {
readonly contract: Contract<SqlStorage>;
readonly schema: SqlSchemaIRNode;
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
}
type SqlSchemaDiffFn = (input: SqlSchemaDiffInput) => SqlSchemaDiffResult;
//#endregion
export { SqlSchemaDiffInput as n, SqlSchemaDiffResult as r, SqlSchemaDiffFn as t };
//# sourceMappingURL=schema-differ-DnoopSXm.d.mts.map
{"version":3,"file":"schema-differ-DnoopSXm.d.mts","names":[],"sources":["../src/core/migrations/schema-differ.ts"],"mappings":";;;;;;;;;AAcA;;;;;;UAAiB,mBAAA;EAiBU;EAAA,SAfhB,MAAA,WAAiB,eAAA;EAeY;;;;;;;;EAAA,SAN7B,oBAAA,GAAuB,KAAA,EAAO,eAAA,KAAoB,aAAA;EAMT;;;AAAuB;AAG3E;EAHoD,SAAzC,cAAA,EAAgB,aAAA;IAAA,SAAyB,MAAA,EAAQ,eAAA;EAAA;AAAA;AAAA,UAG3C,kBAAA;EAAA,SACN,QAAA,EAAU,QAAA,CAAS,UAAA;EAAA,SACnB,MAAA,EAAQ,eAAA;EAAA,SACR,mBAAA,EAAqB,aAAA,CAAc,8BAAA;AAAA;AAAA,KAGlC,eAAA,IAAmB,KAAA,EAAO,kBAAA,KAAuB,mBAAmB"}
import { blindCast } from "@prisma-next/utils/casts";
import { assertUniqueCodecOwner, dispositionForCategory } from "@prisma-next/framework-components/control";
import { ifDefined } from "@prisma-next/utils/defined";
import { effectiveControlPolicy } from "@prisma-next/contract/types";
import { isStorageTypeInstance } from "@prisma-next/sql-contract/types";
import { RelationalSchemaNodeKind } from "@prisma-next/sql-schema-ir/types";
//#region src/core/assembly.ts
function hasCodecControlHooks(descriptor) {
if (typeof descriptor !== "object" || descriptor === null) return false;
const hooks = descriptor.types?.codecTypes?.controlPlaneHooks;
return hooks !== null && hooks !== void 0 && typeof hooks === "object";
}
function extractCodecControlHooks(descriptors) {
const hooks = /* @__PURE__ */ new Map();
const owners = /* @__PURE__ */ new Map();
for (const descriptor of descriptors) {
if (typeof descriptor !== "object" || descriptor === null) continue;
if (!hasCodecControlHooks(descriptor)) continue;
const controlPlaneHooks = descriptor.types.codecTypes.controlPlaneHooks;
for (const [codecId, hook] of Object.entries(controlPlaneHooks)) {
assertUniqueCodecOwner({
codecId,
owners,
descriptorId: descriptor.id,
entityLabel: "control hooks",
entityOwnershipLabel: "owner"
});
hooks.set(codecId, hook);
owners.set(codecId, descriptor.id);
}
}
return hooks;
}
//#endregion
//#region src/core/diff/verifier-disposition.ts
/**
* Classifies a codec `verifyType` hook finding into the target-neutral
* categories the framework grades. A storage type is a named type instance
* (e.g. a native enum); the only shape divergence it can carry is a change
* to its value set, so a paired mismatch always classifies as `valueDrift`.
*/
function classifyStorageTypeDiffIssue(issue) {
if (issue.reason === "not-found") return "declaredMissing";
if (issue.reason === "not-expected") return "extraAuxiliary";
return "valueDrift";
}
function verifierDisposition(controlPolicy, issue) {
return dispositionForCategory(controlPolicy, classifyStorageTypeDiffIssue(issue));
}
//#endregion
//#region src/core/diff/schema-verify.ts
function issueNode(issue) {
const node = issue.expected ?? issue.actual;
if (node === void 0) return void 0;
return blindCast(node);
}
/**
* Resolves an issue's framework-neutral {@link DiffSubjectGranularity} on
* demand, from the issue's node's `nodeKind` via the target-provided
* `granularityOf` map. The node carries only its `nodeKind` identity, never a
* classification, and nothing is stamped onto the issue — every consumer
* (the family verdict below, the framework aggregate's unclaimed-elements
* sweep via {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable})
* calls this the same way, resolved by the family/target that owns the node
* vocabulary. `undefined` for an issue with no node.
*/
function classifyDiffSubjectGranularity(issue, granularityOf) {
const node = issueNode(issue);
return node === void 0 ? void 0 : granularityOf(node.nodeKind);
}
/**
* Resolves an issue's storage `entityKind` on demand, from the issue's
* node's `nodeKind` via the target-provided `entityKindOf` map — the sibling
* of {@link classifyDiffSubjectGranularity}, called the same way by the same
* consumers (via
* {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}).
* `undefined` for an issue with no node, or for a node kind with no storage
* entity of its own.
*/
function classifyDiffEntityKind(issue, entityKindOf) {
const node = issueNode(issue);
return node === void 0 ? void 0 : entityKindOf(node.nodeKind);
}
/**
* Re-keys the legacy `classifySqlVerifierIssueKind` category mapping on the
* issue's {@link DiffSubjectGranularity} (resolved via `granularityOf`) + the
* issue reason. The vocabulary maps one-to-one: an undeclared live entity or
* namespace is `extraTopLevelObject`, an undeclared live field
* `extraNestedElement`, undeclared auxiliaries (constraints, indexes,
* defaults) and structural leaves (policies) `extraAuxiliary`; a value-set
* drift on a check node is `valueDrift`; every other paired divergence is
* `declaredIncompatible`; anything the database lacks is `declaredMissing`.
* `granularityOf` is the target's classifier, so target and extension node
* kinds classify without the family importing them.
*/
function classifySqlDiffIssue(issue, granularityOf) {
if (issue.reason === "not-found") return "declaredMissing";
if (issue.reason === "not-expected") {
const granularity = classifyDiffSubjectGranularity(issue, granularityOf);
if (granularity === "entity" || granularity === "namespace") return "extraTopLevelObject";
if (granularity === "field") return "extraNestedElement";
return "extraAuxiliary";
}
if (issueNode(issue)?.nodeKind === RelationalSchemaNodeKind.check) return "valueDrift";
return "declaredIncompatible";
}
/**
* Whether a `not-expected` issue is a strict-mode-only finding. The legacy
* walk detected every relational extra (namespaces, entities, fields, and
* their auxiliaries) only under `--strict`; the structural diff (roots, RLS
* policies, roles) was never strict-gated — its extras fail in both modes.
* Keyed on the issue's granularity, resolved via `granularityOf`.
*/
function isStrictOnlyExtra(issue, granularityOf) {
const granularity = classifyDiffSubjectGranularity(issue, granularityOf);
return granularity === "namespace" || granularity === "entity" || granularity === "field" || granularity === "auxiliary";
}
/**
* Applies the two consumer filters to a diff issue list: strict gating
* (relational `not-expected` findings drop in lenient mode) and
* control-policy disposition (each surviving issue grades against its
* subject table's effective policy; suppressed issues drop, `observed`
* subjects warn). The verify verdict is `failures.length === 0`.
*/
function computeSqlDiffVerdict(input) {
const failures = [];
const warnings = [];
for (const issue of input.issues) {
if (!input.strict && issue.reason === "not-expected" && isStrictOnlyExtra(issue, input.granularityOf)) continue;
const disposition = dispositionForCategory(effectiveControlPolicy(input.resolveControlPolicy(issue), input.defaultControlPolicy), classifySqlDiffIssue(issue, input.granularityOf));
if (disposition === "suppress") continue;
if (disposition === "warn") {
warnings.push(issue);
continue;
}
failures.push(issue);
}
return {
failures,
warnings
};
}
function computeStorageTypeVerdict(input) {
const failures = [];
const warnings = [];
const policy = effectiveControlPolicy(void 0, input.contract.defaultControlPolicy);
for (const pair of input.namespacePairs) {
if (pair.actual === void 0) continue;
for (const [typeName, typeInstance] of Object.entries(input.contract.storage.types ?? {})) {
if (!isStorageTypeInstance(typeInstance)) continue;
const hook = input.codecHooks.get(typeInstance.codecId);
if (!hook?.verifyType) continue;
const typeIssues = hook.verifyType({
typeName,
typeInstance,
schema: pair.actual
});
for (const issue of typeIssues) {
const disposition = verifierDisposition(policy, issue);
if (disposition === "suppress") continue;
if (disposition === "warn") {
warnings.push(issue);
continue;
}
failures.push(issue);
}
}
}
return {
failures,
warnings
};
}
/**
* THE SQL schema verify: runs the target's full-tree node diff, grades it
* through the family's post-diff filters (strict gating + control-policy
* disposition) plus the codec `verifyType` hook findings, and wraps the
* verdict in the issue-based result envelope. `ok` holds exactly when both
* issue lists are empty — the lists carry the verdict's failures.
*/
function verifySqlSchemaByDiff(input) {
const startTime = Date.now();
const verdictDiff = input.diffSchema({
contract: input.contract,
schema: input.schema,
frameworkComponents: input.frameworkComponents
});
const diffVerdict = computeSqlDiffVerdict({
issues: verdictDiff.issues,
resolveControlPolicy: verdictDiff.resolveControlPolicy,
strict: input.strict,
defaultControlPolicy: input.contract.defaultControlPolicy,
granularityOf: input.granularityOf
});
const storageTypeVerdict = computeStorageTypeVerdict({
contract: input.contract,
namespacePairs: verdictDiff.namespacePairs,
codecHooks: extractCodecControlHooks(input.frameworkComponents)
});
const failCount = diffVerdict.failures.length + storageTypeVerdict.failures.length;
const ok = failCount === 0;
const profileHash = "profileHash" in input.contract && typeof input.contract.profileHash === "string" ? input.contract.profileHash : void 0;
return {
ok,
...ok ? {} : { code: "PN-SCHEMA-0001" },
summary: ok ? "Database schema satisfies contract" : `Database schema does not satisfy contract (${failCount} failure${failCount === 1 ? "" : "s"})`,
contract: {
storageHash: input.contract.storage.storageHash,
...ifDefined("profileHash", profileHash)
},
target: {
expected: input.contract.target,
actual: input.contract.target
},
schema: {
issues: [...diffVerdict.failures, ...storageTypeVerdict.failures],
warnings: { issues: [...diffVerdict.warnings, ...storageTypeVerdict.warnings] }
},
meta: { strict: input.strict },
timings: { total: Date.now() - startTime }
};
}
//#endregion
export { computeStorageTypeVerdict as a, computeSqlDiffVerdict as i, classifyDiffSubjectGranularity as n, verifySqlSchemaByDiff as o, classifySqlDiffIssue as r, extractCodecControlHooks as s, classifyDiffEntityKind as t };
//# sourceMappingURL=schema-verify-W3r631Jh.mjs.map
{"version":3,"file":"schema-verify-W3r631Jh.mjs","names":["d"],"sources":["../src/core/assembly.ts","../src/core/diff/verifier-disposition.ts","../src/core/diff/schema-verify.ts"],"sourcesContent":["import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport { assertUniqueCodecOwner } from '@prisma-next/framework-components/control';\nimport type { CodecControlHooks } from './migrations/types';\n\ntype CodecControlHooksMap = Record<string, CodecControlHooks>;\n\nfunction hasCodecControlHooks(descriptor: unknown): descriptor is {\n readonly id: string;\n readonly types: {\n readonly codecTypes: {\n readonly controlPlaneHooks: CodecControlHooksMap;\n };\n };\n} {\n if (typeof descriptor !== 'object' || descriptor === null) {\n return false;\n }\n const d = descriptor as { types?: { codecTypes?: { controlPlaneHooks?: unknown } } };\n const hooks = d.types?.codecTypes?.controlPlaneHooks;\n return hooks !== null && hooks !== undefined && typeof hooks === 'object';\n}\n\nexport function extractCodecControlHooks(\n descriptors: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>,\n): Map<string, CodecControlHooks> {\n const hooks = new Map<string, CodecControlHooks>();\n const owners = new Map<string, string>();\n\n for (const descriptor of descriptors) {\n if (typeof descriptor !== 'object' || descriptor === null) {\n continue;\n }\n if (!hasCodecControlHooks(descriptor)) {\n continue;\n }\n const controlPlaneHooks = descriptor.types.codecTypes.controlPlaneHooks;\n for (const [codecId, hook] of Object.entries(controlPlaneHooks)) {\n assertUniqueCodecOwner({\n codecId,\n owners,\n descriptorId: descriptor.id,\n entityLabel: 'control hooks',\n entityOwnershipLabel: 'owner',\n });\n hooks.set(codecId, hook);\n owners.set(codecId, descriptor.id);\n }\n }\n\n return hooks;\n}\n","import type { ControlPolicy } from '@prisma-next/contract/types';\nimport type {\n SchemaDiffIssue,\n VerifierIssueCategory,\n VerifierOutcome,\n} from '@prisma-next/framework-components/control';\nimport { dispositionForCategory } from '@prisma-next/framework-components/control';\n\n/**\n * Classifies a codec `verifyType` hook finding into the target-neutral\n * categories the framework grades. A storage type is a named type instance\n * (e.g. a native enum); the only shape divergence it can carry is a change\n * to its value set, so a paired mismatch always classifies as `valueDrift`.\n */\nexport function classifyStorageTypeDiffIssue(issue: SchemaDiffIssue): VerifierIssueCategory {\n if (issue.reason === 'not-found') {\n return 'declaredMissing';\n }\n if (issue.reason === 'not-expected') {\n return 'extraAuxiliary';\n }\n return 'valueDrift';\n}\n\nexport function verifierDisposition(\n controlPolicy: ControlPolicy,\n issue: SchemaDiffIssue,\n): VerifierOutcome {\n return dispositionForCategory(controlPolicy, classifyStorageTypeDiffIssue(issue));\n}\n","/**\n * The differ-based SQL schema verify: post-diff filters and verdict.\n *\n * The generic node differ (`diffSchemas`) reports every node-level\n * difference between the derived expected tree and the introspected actual\n * tree. This module is the consumer side the spec assigns to the SQL\n * family: strict-mode extras gating and control-policy disposition are\n * reason/kind-keyed filters applied AFTER the diff — never inside it — and\n * the verify verdict derives from the filtered issue list.\n *\n * `verifySqlSchemaByDiff` wraps the verdict in the issue-based result\n * envelope — this is THE SQL schema verify (the legacy relational walk and\n * its verification tree are retired).\n */\n\nimport type { Contract, ControlPolicy } from '@prisma-next/contract/types';\nimport { effectiveControlPolicy } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n DiffSubjectGranularity,\n SchemaDiffIssue,\n VerifierIssueCategory,\n VerifierOutcome,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport { dispositionForCategory } from '@prisma-next/framework-components/control';\nimport { isStorageTypeInstance, type SqlStorage } from '@prisma-next/sql-contract/types';\nimport { RelationalSchemaNodeKind, type SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { extractCodecControlHooks } from '../assembly';\nimport type { SqlSchemaDiffFn } from '../migrations/schema-differ';\nimport type { CodecControlHooks } from '../migrations/types';\nimport { verifierDisposition } from './verifier-disposition';\n\n// ============================================================================\n// Subject-granularity classification — nodeKind → framework-neutral granularity\n// ============================================================================\n\nfunction issueNode(issue: SchemaDiffIssue): SqlSchemaIRNode | undefined {\n const node = issue.expected ?? issue.actual;\n if (node === undefined) return undefined;\n return blindCast<\n SqlSchemaIRNode,\n 'every node in a SQL schema diff tree is a SqlSchemaIRNode; nodeKind is its identity'\n >(node);\n}\n\n/**\n * Resolves an issue's framework-neutral {@link DiffSubjectGranularity} on\n * demand, from the issue's node's `nodeKind` via the target-provided\n * `granularityOf` map. The node carries only its `nodeKind` identity, never a\n * classification, and nothing is stamped onto the issue — every consumer\n * (the family verdict below, the framework aggregate's unclaimed-elements\n * sweep via {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable})\n * calls this the same way, resolved by the family/target that owns the node\n * vocabulary. `undefined` for an issue with no node.\n */\nexport function classifyDiffSubjectGranularity(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): DiffSubjectGranularity | undefined {\n const node = issueNode(issue);\n return node === undefined ? undefined : granularityOf(node.nodeKind);\n}\n\n/**\n * Resolves an issue's storage `entityKind` on demand, from the issue's\n * node's `nodeKind` via the target-provided `entityKindOf` map — the sibling\n * of {@link classifyDiffSubjectGranularity}, called the same way by the same\n * consumers (via\n * {@link import('@prisma-next/framework-components/control').SchemaSubjectClassifierCapable}).\n * `undefined` for an issue with no node, or for a node kind with no storage\n * entity of its own.\n */\nexport function classifyDiffEntityKind(\n issue: SchemaDiffIssue,\n entityKindOf: (nodeKind: string) => string | undefined,\n): string | undefined {\n const node = issueNode(issue);\n return node === undefined ? undefined : entityKindOf(node.nodeKind);\n}\n\n// ============================================================================\n// Issue classification — subject granularity + reason → target-neutral category\n// ============================================================================\n\n/**\n * Re-keys the legacy `classifySqlVerifierIssueKind` category mapping on the\n * issue's {@link DiffSubjectGranularity} (resolved via `granularityOf`) + the\n * issue reason. The vocabulary maps one-to-one: an undeclared live entity or\n * namespace is `extraTopLevelObject`, an undeclared live field\n * `extraNestedElement`, undeclared auxiliaries (constraints, indexes,\n * defaults) and structural leaves (policies) `extraAuxiliary`; a value-set\n * drift on a check node is `valueDrift`; every other paired divergence is\n * `declaredIncompatible`; anything the database lacks is `declaredMissing`.\n * `granularityOf` is the target's classifier, so target and extension node\n * kinds classify without the family importing them.\n */\nexport function classifySqlDiffIssue(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): VerifierIssueCategory {\n if (issue.reason === 'not-found') {\n return 'declaredMissing';\n }\n if (issue.reason === 'not-expected') {\n const granularity = classifyDiffSubjectGranularity(issue, granularityOf);\n if (granularity === 'entity' || granularity === 'namespace') {\n return 'extraTopLevelObject';\n }\n if (granularity === 'field') {\n return 'extraNestedElement';\n }\n return 'extraAuxiliary';\n }\n if (issueNode(issue)?.nodeKind === RelationalSchemaNodeKind.check) {\n return 'valueDrift';\n }\n return 'declaredIncompatible';\n}\n\n/**\n * Whether a `not-expected` issue is a strict-mode-only finding. The legacy\n * walk detected every relational extra (namespaces, entities, fields, and\n * their auxiliaries) only under `--strict`; the structural diff (roots, RLS\n * policies, roles) was never strict-gated — its extras fail in both modes.\n * Keyed on the issue's granularity, resolved via `granularityOf`.\n */\nfunction isStrictOnlyExtra(\n issue: SchemaDiffIssue,\n granularityOf: (nodeKind: string) => DiffSubjectGranularity,\n): boolean {\n const granularity = classifyDiffSubjectGranularity(issue, granularityOf);\n return (\n granularity === 'namespace' ||\n granularity === 'entity' ||\n granularity === 'field' ||\n granularity === 'auxiliary'\n );\n}\n\n// ============================================================================\n// The post-diff filter + verdict\n// ============================================================================\n\nexport interface SqlDiffVerdictInput {\n /** The full, ownership-scoped diff issue list from the target's differ. */\n readonly issues: readonly SchemaDiffIssue[];\n /** Resolves a diff issue's subject table's declared control policy directly from the contract. */\n readonly resolveControlPolicy: (issue: SchemaDiffIssue) => ControlPolicy | undefined;\n readonly strict: boolean;\n readonly defaultControlPolicy: ControlPolicy | undefined;\n /** The target's classifier: a diff issue node's `nodeKind` → its subject granularity. */\n readonly granularityOf: (nodeKind: string) => DiffSubjectGranularity;\n}\n\nexport interface SqlDiffVerdict {\n readonly failures: readonly SchemaDiffIssue[];\n readonly warnings: readonly SchemaDiffIssue[];\n}\n\n/**\n * Applies the two consumer filters to a diff issue list: strict gating\n * (relational `not-expected` findings drop in lenient mode) and\n * control-policy disposition (each surviving issue grades against its\n * subject table's effective policy; suppressed issues drop, `observed`\n * subjects warn). The verify verdict is `failures.length === 0`.\n */\nexport function computeSqlDiffVerdict(input: SqlDiffVerdictInput): SqlDiffVerdict {\n const failures: SchemaDiffIssue[] = [];\n const warnings: SchemaDiffIssue[] = [];\n for (const issue of input.issues) {\n if (\n !input.strict &&\n issue.reason === 'not-expected' &&\n isStrictOnlyExtra(issue, input.granularityOf)\n ) {\n continue;\n }\n const tablePolicy = input.resolveControlPolicy(issue);\n const policy = effectiveControlPolicy(tablePolicy, input.defaultControlPolicy);\n const disposition: VerifierOutcome = dispositionForCategory(\n policy,\n classifySqlDiffIssue(issue, input.granularityOf),\n );\n if (disposition === 'suppress') continue;\n if (disposition === 'warn') {\n warnings.push(issue);\n continue;\n }\n failures.push(issue);\n }\n return { failures, warnings };\n}\n\n// ============================================================================\n// Storage-types check — the codec verifyType hook path\n// ============================================================================\n\nexport interface StorageTypeVerdictInput {\n readonly contract: Contract<SqlStorage>;\n /**\n * Expected/actual namespace-node pairs the target's differ input produced:\n * for a namespaced tree, one entry per expected namespace with a\n * non-empty table set, paired by DDL schema name (absent actual side for\n * a schema the database lacks); a flat tree is the sole pair.\n */\n readonly namespacePairs: ReadonlyArray<{\n readonly actual: SqlSchemaIRNode | undefined;\n }>;\n readonly codecHooks: ReadonlyMap<string, CodecControlHooks>;\n}\n\n/**\n * Runs the codec `verifyType` hooks the way the legacy walk did: once per\n * contract namespace with tables, against that namespace's paired actual\n * node (the hook reads namespace-scoped state such as `enums` off it).\n * Issue dispositions grade against the contract default policy, matching\n * the legacy `pushTypeNode` semantics.\n */\nexport interface StorageTypeVerdict {\n readonly failures: readonly SchemaDiffIssue[];\n readonly warnings: readonly SchemaDiffIssue[];\n}\n\nexport function computeStorageTypeVerdict(input: StorageTypeVerdictInput): StorageTypeVerdict {\n const failures: SchemaDiffIssue[] = [];\n const warnings: SchemaDiffIssue[] = [];\n const policy = effectiveControlPolicy(undefined, input.contract.defaultControlPolicy);\n for (const pair of input.namespacePairs) {\n if (pair.actual === undefined) continue;\n for (const [typeName, typeInstance] of Object.entries(input.contract.storage.types ?? {})) {\n if (!isStorageTypeInstance(typeInstance)) continue;\n const hook = input.codecHooks.get(typeInstance.codecId);\n if (!hook?.verifyType) continue;\n const typeIssues = hook.verifyType({ typeName, typeInstance, schema: pair.actual });\n for (const issue of typeIssues) {\n const disposition = verifierDisposition(policy, issue);\n if (disposition === 'suppress') continue;\n if (disposition === 'warn') {\n warnings.push(issue);\n continue;\n }\n failures.push(issue);\n }\n }\n }\n return { failures, warnings };\n}\n\n// ============================================================================\n// The issue-based verify envelope\n// ============================================================================\n\nexport interface VerifySqlSchemaByDiffInput {\n readonly contract: Contract<SqlStorage>;\n readonly schema: SqlSchemaIRNode;\n readonly strict: boolean;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;\n /** The target's full-tree node diff (`diffSchema` descriptor hook). */\n readonly diffSchema: SqlSchemaDiffFn;\n /** The target's classifier: a diff issue node's `nodeKind` → its subject granularity. */\n readonly granularityOf: (nodeKind: string) => DiffSubjectGranularity;\n}\n\n/**\n * THE SQL schema verify: runs the target's full-tree node diff, grades it\n * through the family's post-diff filters (strict gating + control-policy\n * disposition) plus the codec `verifyType` hook findings, and wraps the\n * verdict in the issue-based result envelope. `ok` holds exactly when both\n * issue lists are empty — the lists carry the verdict's failures.\n */\nexport function verifySqlSchemaByDiff(\n input: VerifySqlSchemaByDiffInput,\n): VerifyDatabaseSchemaResult {\n const startTime = Date.now();\n const verdictDiff = input.diffSchema({\n contract: input.contract,\n schema: input.schema,\n frameworkComponents: input.frameworkComponents,\n });\n const diffVerdict = computeSqlDiffVerdict({\n issues: verdictDiff.issues,\n resolveControlPolicy: verdictDiff.resolveControlPolicy,\n strict: input.strict,\n defaultControlPolicy: input.contract.defaultControlPolicy,\n granularityOf: input.granularityOf,\n });\n const storageTypeVerdict = computeStorageTypeVerdict({\n contract: input.contract,\n namespacePairs: verdictDiff.namespacePairs,\n codecHooks: extractCodecControlHooks(input.frameworkComponents),\n });\n const failCount = diffVerdict.failures.length + storageTypeVerdict.failures.length;\n const ok = failCount === 0;\n const profileHash =\n 'profileHash' in input.contract && typeof input.contract.profileHash === 'string'\n ? input.contract.profileHash\n : undefined;\n return {\n ok,\n ...(ok ? {} : { code: 'PN-SCHEMA-0001' }),\n summary: ok\n ? 'Database schema satisfies contract'\n : `Database schema does not satisfy contract (${failCount} failure${failCount === 1 ? '' : 's'})`,\n contract: {\n storageHash: input.contract.storage.storageHash,\n ...ifDefined('profileHash', profileHash),\n },\n target: {\n expected: input.contract.target,\n actual: input.contract.target,\n },\n schema: {\n issues: [...diffVerdict.failures, ...storageTypeVerdict.failures],\n warnings: {\n issues: [...diffVerdict.warnings, ...storageTypeVerdict.warnings],\n },\n },\n meta: { strict: input.strict },\n timings: { total: Date.now() - startTime },\n };\n}\n"],"mappings":";;;;;;;AAMA,SAAS,qBAAqB,YAO5B;CACA,IAAI,OAAO,eAAe,YAAY,eAAe,MACnD,OAAO;CAGT,MAAM,QAAQA,WAAE,OAAO,YAAY;CACnC,OAAO,UAAU,QAAQ,UAAU,KAAA,KAAa,OAAO,UAAU;AACnE;AAEA,SAAgB,yBACd,aACgC;CAChC,MAAM,wBAAQ,IAAI,IAA+B;CACjD,MAAM,yBAAS,IAAI,IAAoB;CAEvC,KAAK,MAAM,cAAc,aAAa;EACpC,IAAI,OAAO,eAAe,YAAY,eAAe,MACnD;EAEF,IAAI,CAAC,qBAAqB,UAAU,GAClC;EAEF,MAAM,oBAAoB,WAAW,MAAM,WAAW;EACtD,KAAK,MAAM,CAAC,SAAS,SAAS,OAAO,QAAQ,iBAAiB,GAAG;GAC/D,uBAAuB;IACrB;IACA;IACA,cAAc,WAAW;IACzB,aAAa;IACb,sBAAsB;GACxB,CAAC;GACD,MAAM,IAAI,SAAS,IAAI;GACvB,OAAO,IAAI,SAAS,WAAW,EAAE;EACnC;CACF;CAEA,OAAO;AACT;;;;;;;;;ACpCA,SAAgB,6BAA6B,OAA+C;CAC1F,IAAI,MAAM,WAAW,aACnB,OAAO;CAET,IAAI,MAAM,WAAW,gBACnB,OAAO;CAET,OAAO;AACT;AAEA,SAAgB,oBACd,eACA,OACiB;CACjB,OAAO,uBAAuB,eAAe,6BAA6B,KAAK,CAAC;AAClF;;;ACUA,SAAS,UAAU,OAAqD;CACtE,MAAM,OAAO,MAAM,YAAY,MAAM;CACrC,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;CAC/B,OAAO,UAGL,IAAI;AACR;;;;;;;;;;;AAYA,SAAgB,+BACd,OACA,eACoC;CACpC,MAAM,OAAO,UAAU,KAAK;CAC5B,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,cAAc,KAAK,QAAQ;AACrE;;;;;;;;;;AAWA,SAAgB,uBACd,OACA,cACoB;CACpB,MAAM,OAAO,UAAU,KAAK;CAC5B,OAAO,SAAS,KAAA,IAAY,KAAA,IAAY,aAAa,KAAK,QAAQ;AACpE;;;;;;;;;;;;;AAkBA,SAAgB,qBACd,OACA,eACuB;CACvB,IAAI,MAAM,WAAW,aACnB,OAAO;CAET,IAAI,MAAM,WAAW,gBAAgB;EACnC,MAAM,cAAc,+BAA+B,OAAO,aAAa;EACvE,IAAI,gBAAgB,YAAY,gBAAgB,aAC9C,OAAO;EAET,IAAI,gBAAgB,SAClB,OAAO;EAET,OAAO;CACT;CACA,IAAI,UAAU,KAAK,CAAC,EAAE,aAAa,yBAAyB,OAC1D,OAAO;CAET,OAAO;AACT;;;;;;;;AASA,SAAS,kBACP,OACA,eACS;CACT,MAAM,cAAc,+BAA+B,OAAO,aAAa;CACvE,OACE,gBAAgB,eAChB,gBAAgB,YAChB,gBAAgB,WAChB,gBAAgB;AAEpB;;;;;;;;AA6BA,SAAgB,sBAAsB,OAA4C;CAChF,MAAM,WAA8B,CAAC;CACrC,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,SAAS,MAAM,QAAQ;EAChC,IACE,CAAC,MAAM,UACP,MAAM,WAAW,kBACjB,kBAAkB,OAAO,MAAM,aAAa,GAE5C;EAIF,MAAM,cAA+B,uBADtB,uBADK,MAAM,qBAAqB,KACC,GAAG,MAAM,oBAElD,GACL,qBAAqB,OAAO,MAAM,aAAa,CACjD;EACA,IAAI,gBAAgB,YAAY;EAChC,IAAI,gBAAgB,QAAQ;GAC1B,SAAS,KAAK,KAAK;GACnB;EACF;EACA,SAAS,KAAK,KAAK;CACrB;CACA,OAAO;EAAE;EAAU;CAAS;AAC9B;AAgCA,SAAgB,0BAA0B,OAAoD;CAC5F,MAAM,WAA8B,CAAC;CACrC,MAAM,WAA8B,CAAC;CACrC,MAAM,SAAS,uBAAuB,KAAA,GAAW,MAAM,SAAS,oBAAoB;CACpF,KAAK,MAAM,QAAQ,MAAM,gBAAgB;EACvC,IAAI,KAAK,WAAW,KAAA,GAAW;EAC/B,KAAK,MAAM,CAAC,UAAU,iBAAiB,OAAO,QAAQ,MAAM,SAAS,QAAQ,SAAS,CAAC,CAAC,GAAG;GACzF,IAAI,CAAC,sBAAsB,YAAY,GAAG;GAC1C,MAAM,OAAO,MAAM,WAAW,IAAI,aAAa,OAAO;GACtD,IAAI,CAAC,MAAM,YAAY;GACvB,MAAM,aAAa,KAAK,WAAW;IAAE;IAAU;IAAc,QAAQ,KAAK;GAAO,CAAC;GAClF,KAAK,MAAM,SAAS,YAAY;IAC9B,MAAM,cAAc,oBAAoB,QAAQ,KAAK;IACrD,IAAI,gBAAgB,YAAY;IAChC,IAAI,gBAAgB,QAAQ;KAC1B,SAAS,KAAK,KAAK;KACnB;IACF;IACA,SAAS,KAAK,KAAK;GACrB;EACF;CACF;CACA,OAAO;EAAE;EAAU;CAAS;AAC9B;;;;;;;;AAwBA,SAAgB,sBACd,OAC4B;CAC5B,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,cAAc,MAAM,WAAW;EACnC,UAAU,MAAM;EAChB,QAAQ,MAAM;EACd,qBAAqB,MAAM;CAC7B,CAAC;CACD,MAAM,cAAc,sBAAsB;EACxC,QAAQ,YAAY;EACpB,sBAAsB,YAAY;EAClC,QAAQ,MAAM;EACd,sBAAsB,MAAM,SAAS;EACrC,eAAe,MAAM;CACvB,CAAC;CACD,MAAM,qBAAqB,0BAA0B;EACnD,UAAU,MAAM;EAChB,gBAAgB,YAAY;EAC5B,YAAY,yBAAyB,MAAM,mBAAmB;CAChE,CAAC;CACD,MAAM,YAAY,YAAY,SAAS,SAAS,mBAAmB,SAAS;CAC5E,MAAM,KAAK,cAAc;CACzB,MAAM,cACJ,iBAAiB,MAAM,YAAY,OAAO,MAAM,SAAS,gBAAgB,WACrE,MAAM,SAAS,cACf,KAAA;CACN,OAAO;EACL;EACA,GAAI,KAAK,CAAC,IAAI,EAAE,MAAM,iBAAiB;EACvC,SAAS,KACL,uCACA,8CAA8C,UAAU,UAAU,cAAc,IAAI,KAAK,IAAI;EACjG,UAAU;GACR,aAAa,MAAM,SAAS,QAAQ;GACpC,GAAG,UAAU,eAAe,WAAW;EACzC;EACA,QAAQ;GACN,UAAU,MAAM,SAAS;GACzB,QAAQ,MAAM,SAAS;EACzB;EACA,QAAQ;GACN,QAAQ,CAAC,GAAG,YAAY,UAAU,GAAG,mBAAmB,QAAQ;GAChE,UAAU,EACR,QAAQ,CAAC,GAAG,YAAY,UAAU,GAAG,mBAAmB,QAAQ,EAClE;EACF;EACA,MAAM,EAAE,QAAQ,MAAM,OAAO;EAC7B,SAAS,EAAE,OAAO,KAAK,IAAI,IAAI,UAAU;CAC3C;AACF"}
//#region src/core/timestamp-now-generator.ts
/**
* Canonical id for the wall-clock-now mutation default generator.
*
* Owned by `family-sql` because that's where the generator lives. The
* id flows out from here to (1) the control-plane descriptor and the
* temporal field-preset pair below, (2) the runtime-plane sibling
* `timestamp-now-runtime-generator.ts`, and (3) authoring surfaces
* (PSL `temporal.updatedAt()`, TS `field.temporal.updatedAt()`) via
* the descriptor flow. Co-locating the constant with its only owner
* keeps the framework layer free of concrete generator ids.
*/
const TIMESTAMP_NOW_GENERATOR_ID = "timestampNow";
/**
* Builds the canonical control-plane descriptor for the wall-clock-now
* mutation default generator. The descriptor's `id` and `buildPhases`
* are target-agnostic so PSL `temporal.updatedAt()` and TS
* `field.temporal.updatedAt()` lower to byte-identical contracts.
*
* `applicableCodecIds` is omitted: `timestampNow` is preset-only (not
* reachable via `@default(timestampNow())` lowering), and the codec is
* co-registered by the preset descriptor itself, so the
* `@default(...)` compatibility check has no role to play here.
*/
function timestampNowControlDescriptor() {
return {
id: TIMESTAMP_NOW_GENERATOR_ID,
buildPhases: () => ({
onCreate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
},
onUpdate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
}
})
};
}
/**
* Builds the canonical `temporal.{createdAt,updatedAt}` field-preset pair
* for a SQL target. `createdAt` lowers to a `now()` storage default;
* `updatedAt` lowers to the `timestampNow` execution generator on both
* `onCreate` and `onUpdate` (RD: "last modified time", non-null). Targets
* supply the codec/native-type pair that matches their timestamp column;
* everything else is shared so PSL `temporal.updatedAt()` and TS
* `field.temporal.updatedAt()` lower to byte-identical contracts across
* targets by construction.
*/
/* @__NO_SIDE_EFFECTS__ */
function temporalAuthoringPresets(input) {
const { codecId, nativeType } = input;
return {
createdAt: {
kind: "fieldPreset",
output: {
codecId,
nativeType,
default: {
kind: "function",
expression: "now()"
}
}
},
updatedAt: {
kind: "fieldPreset",
output: {
codecId,
nativeType,
executionDefaults: {
onCreate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
},
onUpdate: {
kind: "generator",
id: TIMESTAMP_NOW_GENERATOR_ID
}
}
}
}
};
}
//#endregion
export { temporalAuthoringPresets as n, timestampNowControlDescriptor as r, TIMESTAMP_NOW_GENERATOR_ID as t };
//# sourceMappingURL=timestamp-now-generator-CloimujU.mjs.map
{"version":3,"file":"timestamp-now-generator-CloimujU.mjs","names":[],"sources":["../src/core/timestamp-now-generator.ts"],"sourcesContent":["import type { AuthoringFieldPresetDescriptor } from '@prisma-next/framework-components/authoring';\nimport type { MutationDefaultGeneratorDescriptor } from '@prisma-next/framework-components/control';\n\n/**\n * Canonical id for the wall-clock-now mutation default generator.\n *\n * Owned by `family-sql` because that's where the generator lives. The\n * id flows out from here to (1) the control-plane descriptor and the\n * temporal field-preset pair below, (2) the runtime-plane sibling\n * `timestamp-now-runtime-generator.ts`, and (3) authoring surfaces\n * (PSL `temporal.updatedAt()`, TS `field.temporal.updatedAt()`) via\n * the descriptor flow. Co-locating the constant with its only owner\n * keeps the framework layer free of concrete generator ids.\n */\nexport const TIMESTAMP_NOW_GENERATOR_ID = 'timestampNow' as const;\n\n/**\n * Builds the canonical control-plane descriptor for the wall-clock-now\n * mutation default generator. The descriptor's `id` and `buildPhases`\n * are target-agnostic so PSL `temporal.updatedAt()` and TS\n * `field.temporal.updatedAt()` lower to byte-identical contracts.\n *\n * `applicableCodecIds` is omitted: `timestampNow` is preset-only (not\n * reachable via `@default(timestampNow())` lowering), and the codec is\n * co-registered by the preset descriptor itself, so the\n * `@default(...)` compatibility check has no role to play here.\n */\nexport function timestampNowControlDescriptor(): MutationDefaultGeneratorDescriptor {\n return {\n id: TIMESTAMP_NOW_GENERATOR_ID,\n buildPhases: () => ({\n onCreate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n onUpdate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n }),\n };\n}\n\n/**\n * Builds the canonical `temporal.{createdAt,updatedAt}` field-preset pair\n * for a SQL target. `createdAt` lowers to a `now()` storage default;\n * `updatedAt` lowers to the `timestampNow` execution generator on both\n * `onCreate` and `onUpdate` (RD: \"last modified time\", non-null). Targets\n * supply the codec/native-type pair that matches their timestamp column;\n * everything else is shared so PSL `temporal.updatedAt()` and TS\n * `field.temporal.updatedAt()` lower to byte-identical contracts across\n * targets by construction.\n */\n/* @__NO_SIDE_EFFECTS__ */\nexport function temporalAuthoringPresets<\n const CodecId extends string,\n const NativeType extends string,\n>(input: { readonly codecId: CodecId; readonly nativeType: NativeType }) {\n const { codecId, nativeType } = input;\n return {\n createdAt: {\n kind: 'fieldPreset',\n output: {\n codecId,\n nativeType,\n default: { kind: 'function', expression: 'now()' },\n },\n },\n updatedAt: {\n kind: 'fieldPreset',\n output: {\n codecId,\n nativeType,\n executionDefaults: {\n onCreate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n onUpdate: { kind: 'generator', id: TIMESTAMP_NOW_GENERATOR_ID },\n },\n },\n },\n } as const satisfies Record<string, AuthoringFieldPresetDescriptor>;\n}\n"],"mappings":";;;;;;;;;;;;AAcA,MAAa,6BAA6B;;;;;;;;;;;;AAa1C,SAAgB,gCAAoE;CAClF,OAAO;EACL,IAAI;EACJ,oBAAoB;GAClB,UAAU;IAAE,MAAM;IAAa,IAAI;GAA2B;GAC9D,UAAU;IAAE,MAAM;IAAa,IAAI;GAA2B;EAChE;CACF;AACF;;;;;;;;;;;;AAaA,SAAgB,yBAGd,OAAuE;CACvE,MAAM,EAAE,SAAS,eAAe;CAChC,OAAO;EACL,WAAW;GACT,MAAM;GACN,QAAQ;IACN;IACA;IACA,SAAS;KAAE,MAAM;KAAY,YAAY;IAAQ;GACnD;EACF;EACA,WAAW;GACT,MAAM;GACN,QAAQ;IACN;IACA;IACA,mBAAmB;KACjB,UAAU;MAAE,MAAM;MAAa,IAAI;KAA2B;KAC9D,UAAU;MAAE,MAAM;MAAa,IAAI;KAA2B;IAChE;GACF;EACF;CACF;AACF"}
import { r as SqlControlAdapter } from "./control-adapter-BQgad8Zc.mjs";
import { ContractSpace, ControlAdapterDescriptor, ControlExtensionDescriptor, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlannerConflict, MigrationPlannerFailureResult, MigrationPlannerSuccessResult, MigrationRunnerExecutionChecks, MigrationRunnerFailure, MigrationRunnerPerSpaceSuccessValue, MigrationRunnerResult, OpFactoryCall, OperationContext, SchemaDiffIssue, SchemaOwnership } from "@prisma-next/framework-components/control";
import { Contract } from "@prisma-next/contract/types";
import { SqlControlDriverInstance, SqlStorage, StorageColumn, StorageTable, StorageTypeInstance } from "@prisma-next/sql-contract/types";
import { SqlSchemaIRNode } from "@prisma-next/sql-schema-ir/types";
import { Result } from "@prisma-next/utils/result";
import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
import { AggregateMigrationEdgeRef } from "@prisma-next/migration-tools/aggregate";
import { SqlOperationDescriptors } from "@prisma-next/sql-operations";
//#region src/core/migrations/types.d.ts
type AnyRecord = Readonly<Record<string, unknown>>;
interface StorageTypePlanResult<TTargetDetails> {
readonly operations: readonly SqlMigrationPlanOperation<TTargetDetails>[];
}
/**
* Input for expanding parameterized native types.
*/
interface ExpandNativeTypeInput {
readonly nativeType: string;
readonly codecId?: string;
readonly typeParams?: Record<string, unknown>;
}
/**
* Input for resolving an identity-value SQL literal used to backfill existing rows when
* adding a NOT NULL column without an explicit default.
*
* "Identity value" in the algebraic (monoid) sense: the neutral element for the type
* (0 for numbers, '' for strings, false for booleans, etc.).
*/
interface ResolveIdentityValueInput {
readonly nativeType: string;
readonly codecId?: string;
readonly typeParams?: Record<string, unknown>;
}
/**
* Per-field lifecycle event a codec hook can react to.
*
* Fired during app-space migration emission as the SQL family diffs the
* prior contract against the new contract. See
* `docs/architecture docs/adrs/ADR 213 - Codec lifecycle hooks.md`
* for the wiring contract.
*
* - `'added'` — the field is present in the new contract but not the prior.
* - `'dropped'` — the field is present in the prior contract but not the new.
* - `'altered'` — the field is present in both and any property other than
* `codecId` differs. Codec-id changes are a v1 non-goal:
* when only `codecId` differs, no `'altered'` event fires.
*/
type FieldEvent = 'added' | 'dropped' | 'altered';
/**
* Context passed to {@link CodecControlHooks.onFieldEvent}.
*
* `namespaceId`, `tableName`, and `fieldName` are always populated; `priorTable` /
* `priorField` carry the prior contract's view of the table and column
* (present for `'dropped'` and `'altered'`); `newTable` / `newField`
* carry the new contract's view (present for `'added'` and `'altered'`).
*
* The hook only ever receives app-space contract IR — extension-space
* fields are scoped out by the API: the hook is wired at the
* application emitter only.
*/
interface FieldEventContext {
readonly namespaceId: string;
readonly tableName: string;
readonly fieldName: string;
readonly priorTable?: StorageTable;
readonly newTable?: StorageTable;
readonly priorField?: StorageColumn;
readonly newField?: StorageColumn;
}
interface CodecControlHooks<TTargetDetails = unknown> {
/**
* `schema` is typed as the family-level `SqlSchemaIRNode` (not the concrete
* `SqlSchemaIR` class) because the actual value handed in is whatever
* per-namespace node the calling target's tree shape produces — a flat
* `SqlSchemaIR` for SQLite, a `PostgresNamespaceSchemaNode` for Postgres —
* read structurally for its `tables`/`nativeEnums` fields. Hooks that need
* the concrete Postgres shape narrow via `PostgresNamespaceSchemaNode.is(schema)`.
*/
planTypeOperations?: (options: {
readonly typeName: string;
readonly typeInstance: StorageTypeInstance;
readonly contract: Contract<SqlStorage>;
readonly schema: SqlSchemaIRNode;
readonly schemaName?: string;
readonly policy: MigrationOperationPolicy;
}) => StorageTypePlanResult<TTargetDetails>;
verifyType?: (options: {
readonly typeName: string;
readonly typeInstance: StorageTypeInstance;
readonly schema: SqlSchemaIRNode;
readonly schemaName?: string;
}) => readonly SchemaDiffIssue[];
introspectTypes?: (options: {
readonly driver: SqlControlDriverInstance<string>;
readonly schemaName?: string;
}) => Promise<Record<string, StorageTypeInstance>>;
/**
* Expands a parameterized native type to its full SQL representation.
* Used by schema verification to compare contract types against database types.
*
* For example, expands:
* - { nativeType: 'character varying', typeParams: { length: 255 } } -> 'character varying(255)'
* - { nativeType: 'numeric', typeParams: { precision: 10, scale: 2 } } -> 'numeric(10,2)'
*
* Returns the expanded type string, or the original nativeType if no expansion is needed.
*/
expandNativeType?: (input: ExpandNativeTypeInput) => string;
/**
* Resolves the identity value (monoid neutral element) as a SQL literal for safely adding
* a NOT NULL column without an explicit default to a non-empty table.
*
* Return semantics:
* - string: use this literal
* - null: explicitly no safe identity value is known; fall back to another strategy
* - undefined: no opinion; planner may use built-in fallbacks
*/
resolveIdentityValue?: (input: ResolveIdentityValueInput) => string | null | undefined;
/**
* Reacts to per-field added / dropped / altered events as the app-space
* emitter diffs the prior contract against the new contract. Returned
* ops are inlined into the app-space migration's `ops.json` alongside
* the user's structural ops.
*
* Synchronous. Each returned op must carry its own `invariantId`. Hooks
* are dispatched per `(table, field)` based on the field's `codecId`
* (the new field's codec for `'added'` / `'altered'`; the prior field's
* codec for `'dropped'`).
*
* See `docs/architecture docs/adrs/ADR 213 - Codec lifecycle hooks.md`
* for the wiring contract and the deterministic ordering rule.
*/
onFieldEvent?: (event: FieldEvent, ctx: FieldEventContext) => readonly OpFactoryCall[];
}
interface SqlControlExtensionDescriptor<TTargetId extends string> extends ControlExtensionDescriptor<'sql', TTargetId> {
readonly queryOperations?: () => SqlOperationDescriptors;
/**
* Schema-contributing extensions opt into the per-space planner / runner /
* verifier by setting this field. Extensions without it are codec-only or
* query-ops-only — today's behaviour preserved.
*
* The shape comes from `@prisma-next/framework-components/control`
* (`ContractSpace`) — contract-space identity is a framework concept,
* not a SQL-specific one. The SQL family specialises the generic to
* `Contract<SqlStorage>` so descriptor authors continue to see a
* typed contract value.
*/
readonly contractSpace?: ContractSpace<Contract<SqlStorage>>;
}
interface SqlControlAdapterDescriptor<TTargetId extends string> extends ControlAdapterDescriptor<'sql', TTargetId, SqlControlAdapter<TTargetId>> {
readonly queryOperations?: () => SqlOperationDescriptors;
}
interface SqlMigrationPlanOperationStep {
readonly description: string;
readonly sql: string;
/**
* Optional parameter values bound at execution time. The runner forwards
* these to `driver.query(sql, params ?? [])`, so step authors can use
* placeholder syntax (`$1`, `$2`, …) instead of inlining literals into
* the SQL string. Reuses the driver's parameter binder rather than
* rolling per-target literal serialization for every type the planner
* may emit.
*/
readonly params?: readonly unknown[];
readonly meta?: AnyRecord;
}
/**
* Minimal shape every SQL-family target must conform to for its per-operation
* `target.details` payload. Each SQL operation addresses a named database
* object in some schema; targets (Postgres, MySQL, SQLite, …) extend this
* shape with their own fields (e.g. Postgres adds `objectType` and optional
* `table`).
*/
interface SqlPlanTargetDetails {
readonly schema: string;
readonly name: string;
}
interface SqlMigrationPlanOperationTarget<TTargetDetails> {
readonly id: string;
readonly details?: TTargetDetails;
}
interface SqlMigrationPlanOperation<TTargetDetails> extends MigrationPlanOperation {
readonly summary?: string;
readonly target: SqlMigrationPlanOperationTarget<TTargetDetails>;
readonly precheck: readonly SqlMigrationPlanOperationStep[];
readonly execute: readonly SqlMigrationPlanOperationStep[];
readonly postcheck: readonly SqlMigrationPlanOperationStep[];
readonly meta?: AnyRecord;
}
interface SqlMigrationPlanContractInfo {
readonly storageHash: string;
readonly profileHash?: string;
}
interface SqlMigrationPlan<TTargetDetails> extends MigrationPlan {
/**
* Contract space this plan applies to. The runner uses this to key the
* `prisma_contract.marker` row it writes/reads (`space = <spaceId>`),
* so per-extension plans hit per-extension marker rows instead of all
* collapsing onto the app's row.
*
* App-plan callers pass `APP_SPACE_ID` (`'app'`); per-extension plans
* pass the extension's space id. Required at every call site so the
* type system surfaces every place that needs to thread the value
* (rather than letting an `?? APP_SPACE_ID` fall-through silently
* collapse per-space markers onto the `'app'` row).
*
* @see specs/framework-mechanism.spec.md § 2.
*/
readonly spaceId: string;
/**
* Origin contract identity that the plan expects the database to currently be at.
* If omitted or null, the runner skips origin validation entirely.
*/
readonly origin?: SqlMigrationPlanContractInfo | null;
/**
* Destination contract identity that the plan intends to reach.
*/
readonly destination: SqlMigrationPlanContractInfo;
readonly operations: readonly (SqlMigrationPlanOperation<TTargetDetails> | Promise<SqlMigrationPlanOperation<TTargetDetails>>)[];
/**
* Sorted, deduplicated invariant ids declared by this plan's data-transform
* ops. Required at the SQL-family layer (the SQL runners consume this as
* the source of truth for marker writes and self-edge no-op checks); the
* framework-level {@link MigrationPlan.providedInvariants} stays optional
* because `db init` / `db update` plans don't have a corresponding
* migration manifest.
*/
readonly providedInvariants: readonly string[];
readonly meta?: AnyRecord;
}
type SqlPlannerConflictKind = 'typeMismatch' | 'nullabilityConflict' | 'indexIncompatible' | 'foreignKeyConflict' | 'missingButNonAdditive' | 'unsupportedOperation' | 'controlPolicySuppressedCall';
interface SqlPlannerConflictLocation {
readonly namespaceId?: string;
readonly entityKind?: string;
readonly entityName?: string;
readonly column?: string;
readonly index?: string;
readonly constraint?: string;
}
interface SqlPlannerConflict extends MigrationPlannerConflict {
readonly kind: SqlPlannerConflictKind;
readonly location?: SqlPlannerConflictLocation;
readonly meta?: AnyRecord;
}
interface SqlPlannerSuccessResult<TTargetDetails> extends Omit<MigrationPlannerSuccessResult, 'plan'> {
readonly kind: 'success';
readonly plan: SqlMigrationPlan<TTargetDetails>;
}
interface SqlPlannerFailureResult extends Omit<MigrationPlannerFailureResult, 'conflicts'> {
readonly kind: 'failure';
readonly conflicts: readonly SqlPlannerConflict[];
}
type SqlPlannerResult<TTargetDetails> = SqlPlannerSuccessResult<TTargetDetails> | SqlPlannerFailureResult;
interface SqlMigrationPlannerPlanOptions {
readonly contract: Contract<SqlStorage>;
/**
* The "from"/live schema as the target's introspected node (SQLite a flat
* `SqlSchemaIR`, Postgres a `PostgresDatabaseSchemaNode` root). Structure-aware
* consumers narrow the concrete shape before walking it.
*/
readonly schema: SqlSchemaIRNode;
readonly policy: MigrationOperationPolicy;
readonly schemaName?: string;
/**
* Contract space the plan applies to. The planner stamps this onto
* the produced {@link SqlMigrationPlan.spaceId} so the runner keys
* the marker row by the right space. App-plan callers pass
* `APP_SPACE_ID`; per-extension callers pass the extension's space
* id.
*/
readonly spaceId: string;
/**
* The "from" contract (state the planner assumes the database starts at),
* or `null` for reconciliation flows that have no prior contract.
*
* Required at every call site so the structural fact "I have a prior
* contract / I don't" is visible in the type. `migration plan` reads
* the predecessor bundle's `end-contract.json` from disk and passes
* the parsed value; `db update` / `db init` reconcile against the
* live schema and pass `null`. Strategies that
* need from/to column-shape comparisons (unsafe type change, nullability
* tightening) use this to decide whether to emit `dataTransform`
* placeholders; they short-circuit when it is `null`.
*
* Planners also derive the "from" identity they stamp onto the produced
* plan's `describe()` as `fromContract?.storage.storageHash ?? null`.
*/
readonly fromContract: Contract<SqlStorage> | null;
/**
* Active framework components participating in this composition.
* Each component is target-bound so SQL targets can dispatch
* component-owned planning behaviour from the same descriptor list.
* All components must have matching familyId ('sql') and targetId.
*/
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
/**
* Ownership oracle over the whole contract-space composition (the passive
* aggregate). The planner asks it, per live extra node, whether any space
* declares that entity: a sibling-owned node is left untouched, an unowned
* node is a genuine extra it may drop under a destructive policy. The
* planner holds no list of other spaces' names — ownership lives in the
* aggregate; it only asks. Absent for a single-space plan handed no
* aggregate. See {@link SchemaOwnership}.
*/
readonly ownership?: SchemaOwnership;
}
interface SqlMigrationPlanner<TTargetDetails> {
plan(options: SqlMigrationPlannerPlanOptions): SqlPlannerResult<TTargetDetails>;
}
interface SqlMigrationRunnerExecuteCallbacks<TTargetDetails> {
onOperationStart?(operation: SqlMigrationPlanOperation<TTargetDetails>): void;
onOperationComplete?(operation: SqlMigrationPlanOperation<TTargetDetails>): void;
}
interface SqlMigrationRunnerExecuteOptions<TTargetDetails> {
readonly plan: SqlMigrationPlan<TTargetDetails>;
readonly driver: SqlControlDriverInstance<string>;
/**
* Logical contract space this plan applies to. When omitted the
* runner derives the space from {@link SqlMigrationPlan.spaceId};
* when supplied, the runner asserts it matches `plan.spaceId` so a
* caller cannot accidentally write the marker row for a different
* space than the plan was produced for.
*/
readonly space?: string;
/**
* Destination contract IR.
* Must correspond to `plan.destination` and is used for schema verification and marker/ledger writes.
*/
readonly destinationContract: Contract<SqlStorage>;
/**
* Execution-time policy that defines which operation classes are allowed.
* The runner validates each operation against this policy before execution.
*/
readonly policy: MigrationOperationPolicy;
readonly schemaName?: string;
readonly strictVerification?: boolean;
readonly callbacks?: SqlMigrationRunnerExecuteCallbacks<TTargetDetails>;
readonly context?: OperationContext;
/**
* Execution-time checks configuration.
* All checks default to `true` (enabled) when omitted.
*/
readonly executionChecks?: MigrationRunnerExecutionChecks;
/**
* Active framework components participating in this composition.
* Each component is target-bound so SQL targets can dispatch
* component-owned execution behaviour from the same descriptor list.
* All components must have matching familyId ('sql') and targetId.
*/
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
/**
* Per-edge breakdown from graph-walk planning. When present, the runner
* writes one ledger row per edge instead of one collapsed row per apply.
*/
readonly migrationEdges: readonly AggregateMigrationEdgeRef[];
}
type SqlMigrationRunnerErrorCode = 'DESTINATION_CONTRACT_MISMATCH' | 'LEGACY_MARKER_SHAPE' | 'MARKER_ORIGIN_MISMATCH' | 'MARKER_CAS_FAILURE' | 'POLICY_VIOLATION' | 'PRECHECK_FAILED' | 'POSTCHECK_FAILED' | 'SCHEMA_VERIFY_FAILED' | 'FOREIGN_KEY_VIOLATION' | 'EXECUTION_FAILED';
interface SqlMigrationRunnerFailure extends MigrationRunnerFailure {
readonly code: SqlMigrationRunnerErrorCode;
readonly meta?: AnyRecord;
}
interface SqlMigrationRunnerSuccessValue extends MigrationRunnerPerSpaceSuccessValue {}
type SqlMigrationRunnerResult = Result<SqlMigrationRunnerSuccessValue, SqlMigrationRunnerFailure>;
interface SqlMigrationRunner<TTargetDetails> {
/**
* Apply one or more per-space migration plans, opening and managing the
* outer transaction (and any target-specific connection-level setup, e.g.
* SQLite's `PRAGMA foreign_keys` toggle). An apply that targets one space
* passes a one-element `perSpaceOptions` list.
*
* The caller orders the input list (typically via the aggregate planner's
* `applyOrder`: extensions alphabetical, then app). A failure on any space
* rolls back every space's writes.
*
* Each entry must reference the same `driver` as the top-level `driver`
* (the connection the outer transaction is open on).
*/
execute(options: {
readonly driver: SqlControlDriverInstance<string>;
readonly perSpaceOptions: ReadonlyArray<SqlMigrationRunnerExecuteOptions<TTargetDetails>>;
}): Promise<MigrationRunnerResult>;
/**
* Apply a single migration plan against an already-open connection
* **without** opening a transaction. The caller is responsible for
* wrapping the call (and any siblings) in `BEGIN` / `COMMIT` /
* `ROLLBACK`. Used by {@link SqlMigrationRunner.execute} to fan out
* across contract spaces inside one outer transaction.
*
* Idempotent control-table setup (`prisma_contract.*`) and marker
* writes use `options.space` to address the per-space marker row.
*/
executeOnConnection(options: SqlMigrationRunnerExecuteOptions<TTargetDetails>): Promise<SqlMigrationRunnerResult>;
}
interface CreateSqlMigrationPlanOptions<TTargetDetails> {
readonly targetId: string;
/**
* Contract space this plan applies to. Mirrors {@link SqlMigrationPlan.spaceId}.
*/
readonly spaceId: string;
readonly origin?: SqlMigrationPlanContractInfo | null;
readonly destination: SqlMigrationPlanContractInfo;
readonly operations: readonly SqlMigrationPlanOperation<TTargetDetails>[];
/**
* Sorted, deduplicated invariant ids for this plan; mirrors the required
* field on {@link SqlMigrationPlan}. Callers without a migration manifest
* (`db init`, `db update`, planner-built plans) pass `[]`.
*/
readonly providedInvariants: readonly string[];
readonly meta?: AnyRecord;
}
//#endregion
export { SqlPlannerSuccessResult as A, SqlMigrationRunnerSuccessValue as C, SqlPlannerConflictLocation as D, SqlPlannerConflictKind as E, SqlPlannerFailureResult as O, SqlMigrationRunnerResult as S, SqlPlannerConflict as T, SqlMigrationRunner as _, FieldEvent as a, SqlMigrationRunnerExecuteOptions as b, SqlControlAdapterDescriptor as c, SqlMigrationPlanContractInfo as d, SqlMigrationPlanOperation as f, SqlMigrationPlannerPlanOptions as g, SqlMigrationPlanner as h, ExpandNativeTypeInput as i, StorageTypePlanResult as j, SqlPlannerResult as k, SqlControlExtensionDescriptor as l, SqlMigrationPlanOperationTarget as m, CodecControlHooks as n, FieldEventContext as o, SqlMigrationPlanOperationStep as p, CreateSqlMigrationPlanOptions as r, ResolveIdentityValueInput as s, AnyRecord as t, SqlMigrationPlan as u, SqlMigrationRunnerErrorCode as v, SqlPlanTargetDetails as w, SqlMigrationRunnerFailure as x, SqlMigrationRunnerExecuteCallbacks as y };
//# sourceMappingURL=types-BPv_y7iS.d.mts.map
{"version":3,"file":"types-BPv_y7iS.d.mts","names":[],"sources":["../src/core/migrations/types.ts"],"mappings":"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"}
export type BackingIndexCandidates = {
readonly indexes: readonly { readonly columns: readonly string[] }[];
readonly uniques: readonly { readonly columns: readonly string[] }[];
readonly primaryKey?: { readonly columns: readonly string[] } | undefined;
};
/**
* The column-list keys (`"colA,colB"`, order preserved) a table's own
* indexes, unique constraints, and primary key already back. A foreign key
* whose source columns join to one of these keys needs no separately
* derived backing index.
*
* Shared by {@link isBackedByColumnKeys}'s two callers, which must agree on
* what counts as "backed": `contract-to-schema-ir.ts`'s `convertTable`
* (deriving the FK-backing-index expectation `db verify` checks against a
* live database) and the postgres PSL inferrer (deciding whether an
* introspected relation needs an explicit `index: false`).
*/
export function backingIndexColumnKeys(table: BackingIndexCandidates): readonly string[] {
return [
...table.indexes.map((index) => index.columns.join(',')),
...table.uniques.map((unique) => unique.columns.join(',')),
...(table.primaryKey ? [table.primaryKey.columns.join(',')] : []),
];
}
/**
* Whether `columns`, in order, matches one of `backingKeys` (see
* {@link backingIndexColumnKeys}). Order-sensitive: `(a, b)` does not
* satisfy a backing index declared as `(b, a)`.
*/
export function isBackedByColumnKeys(
columns: readonly string[],
backingKeys: readonly string[],
): boolean {
return backingKeys.includes(columns.join(','));
}

Sorry, the diff of this file is too big to display