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

@prisma-next/sql-schema-ir

Package Overview
Dependencies
Maintainers
4
Versions
1097
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@prisma-next/sql-schema-ir - npm Package Compare versions

Comparing version
0.16.0-dev.30
to
0.16.0-dev.31
+734
dist/types-CP9M6LJk.mjs
import { o as normalizeIndexOptionValue } from "./naming-BD7Y-Fq1.mjs";
import { IRNodeBase, freezeNode } from "@prisma-next/framework-components/ir";
import { blindCast } from "@prisma-next/utils/casts";
import { InternalError } from "@prisma-next/utils/internal-error";
import { canonicalStringify } from "@prisma-next/utils/canonical-stringify";
import { ifDefined } from "@prisma-next/utils/defined";
import { isArrayEqual } from "@prisma-next/utils/array-equal";
//#region src/ir/schema-node-kinds.ts
/**
* The `nodeKind` discriminant for each relational schema-diff leaf node.
* Each node carries a unique value; the differ pairs siblings by `id`, and
* these kinds distinguish the node types that appear as `PostgresTableSchemaNode`
* children (columns, primary key, foreign keys, uniques, indexes, checks) from
* each other and from the RLS policy/role kinds a target defines separately.
*/
const RelationalSchemaNodeKind = {
schema: "sql-schema",
table: "sql-table",
column: "sql-column",
columnDefault: "sql-column-default",
primaryKey: "sql-primary-key",
foreignKey: "sql-foreign-key",
unique: "sql-unique",
index: "sql-index",
check: "sql-check-constraint"
};
/**
* The one real map from a relational `nodeKind` to the framework-neutral
* {@link DiffSubjectGranularity} its diff issues carry — the SQL family's
* post-diff filters (issue category, strict-mode gating) and the framework
* aggregate's unclaimed-elements sweep key on the granularity, never on the
* `nodeKind` spelling and never on anything stamped on the node. Resolution
* is by `nodeKind` equality against this map, not a suffix string match.
* Target-specific node kinds (Postgres namespace/table/policy/role) are
* outside this family layer's vocabulary and map their own kinds directly.
*/
const RELATIONAL_NODE_GRANULARITY = {
[RelationalSchemaNodeKind.schema]: "structural",
[RelationalSchemaNodeKind.table]: "entity",
[RelationalSchemaNodeKind.column]: "field",
[RelationalSchemaNodeKind.columnDefault]: "auxiliary",
[RelationalSchemaNodeKind.primaryKey]: "auxiliary",
[RelationalSchemaNodeKind.foreignKey]: "auxiliary",
[RelationalSchemaNodeKind.unique]: "auxiliary",
[RelationalSchemaNodeKind.index]: "auxiliary",
[RelationalSchemaNodeKind.check]: "auxiliary"
};
function isRelationalSchemaNodeKind(nodeKind) {
return Object.hasOwn(RELATIONAL_NODE_GRANULARITY, nodeKind);
}
/**
* Looks up the subject granularity for a relational `nodeKind`. Throws for a
* `nodeKind` outside this map (a target-specific kind, e.g. Postgres's
* namespace/table/policy/role) — those map their own kinds directly rather
* than through this family-level map.
*/
function relationalNodeGranularity(nodeKind) {
if (!isRelationalSchemaNodeKind(nodeKind)) throw new InternalError(`relationalNodeGranularity: unrecognized relational node kind "${nodeKind}"`);
return RELATIONAL_NODE_GRANULARITY[nodeKind];
}
/**
* The one real map from a relational `nodeKind` to its storage `entityKind`
* — the same vocabulary the contract storage's `entries` dictionary keys use
* (`elementCoordinates` walks it). Only the whole-table kind has an entity of
* its own; every other relational node (a column, an index, …) is nested
* under one and maps to nothing here.
*/
const RELATIONAL_NODE_ENTITY_KIND = { [RelationalSchemaNodeKind.table]: "table" };
/**
* Looks up the storage `entityKind` for a relational `nodeKind` — sibling of
* {@link relationalNodeGranularity}, resolved by `nodeKind` equality against
* {@link RELATIONAL_NODE_ENTITY_KIND}. `undefined` for a target-specific kind
* (targets map their own kinds directly) or a relational kind with no entity
* of its own.
*/
function relationalNodeEntityKind(nodeKind) {
return isRelationalSchemaNodeKind(nodeKind) ? RELATIONAL_NODE_ENTITY_KIND[nodeKind] : void 0;
}
//#endregion
//#region src/ir/sql-schema-ir-node.ts
/**
* SQL Schema IR node base. Carries the family-level
* `kind = 'sql-schema-ir'` discriminator and inherits the framework's
* `freezeNode` affordance.
*
* SQL Schema IR represents the actual database state as discovered by
* introspection (the parallel to SQL Contract IR, which represents the
* desired state).
*
* The discriminator is installed as a non-enumerable own property,
* matching the SqlNode pattern. This keeps `JSON.stringify(node)`
* canonical (no `kind` field), keeps `toEqual({...})` test assertions
* against pre-lift flat shapes passing, and keeps `node.kind` readable
* for dispatch.
*
* Both `kind` and `nodeKind` are required: every concrete leaf is a node
* the generic differ can pair and compare, so every leaf must declare which
* node it is. `nodeKind` has no default here — every direct subclass sets its
* own literal value (the relational leaves via `RelationalSchemaNodeKind`,
* target concretions via their own vocabulary, e.g. `PostgresSchemaNodeKind`).
*/
var SqlSchemaIRNode = class extends IRNodeBase {
constructor() {
super();
Object.defineProperty(this, "kind", {
value: "sql-schema-ir",
writable: false,
enumerable: false,
configurable: false
});
}
};
/**
* Asserts `node` matches `predicate`, narrowing its type to `T`. The one
* shared implementation every node class's `static assert` and `isEqualTo`
* reach for, instead of each hand-writing its own throw: the message names
* the class the caller expected.
*/
function assertNode(node, className, predicate) {
if (node === void 0 || !predicate(node)) throw new InternalError(`Expected a ${className} but got nodeKind=${node?.nodeKind ?? "undefined"}`);
}
/**
* Defines a non-enumerable own property, the same treatment `kind` gets
* above: a derivation-time render-support field stays out of
* `JSON.stringify`, `toEqual({...})` structural assertions, and spreads,
* while remaining directly readable (`node.field`) for the one consumer
* that resolves it at plan time. A no-op when `value` is `undefined` — the
* property is simply absent, matching every other optional field on these
* nodes.
*/
function defineNonEnumerable(target, key, value) {
if (value === void 0) return;
Object.defineProperty(target, key, {
value,
enumerable: false,
writable: false,
configurable: false
});
}
//#endregion
//#region src/ir/primary-key.ts
/**
* Primary-key Schema IR node. Mirrors the Contract IR `PrimaryKey`
* shape (same `columns` + optional `name`) so verification can compare
* intent and actual structurally. Defined here independently to avoid
* a sql-schema-ir -> sql-contract dependency.
*
* Implements `DiffableNode` so a primary key is directly a table's diff-tree
* child. `id` is a fixed sentinel — a table has at most one primary key, so
* there is never a sibling to disambiguate against. `isEqualTo` compares the
* column tuple; the PK's own `name` is a database-assigned label with no
* semantic weight, so it is not compared (mirrors the policy node's
* name-insensitivity to non-identifying detail).
*/
var PrimaryKey = class PrimaryKey extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.primaryKey;
columns;
constructor(input) {
super();
this.columns = input.columns;
if (input.name !== void 0) this.name = input.name;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
return "primary-key";
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.primaryKey;
}
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "PrimaryKey", PrimaryKey.is);
return this.columns.length === node.columns.length && this.columns.every((c, i) => c === node.columns[i]);
}
};
//#endregion
//#region src/ir/sql-check-constraint-ir.ts
/**
* Schema IR node for a table-level check constraint that restricts a
* column to a set of permitted values (an enum-style `IN (...)` check).
*
* Carries the **resolved values** rather than a raw SQL predicate so
* callers can compare value-sets without parsing SQL.
*
* Implements `DiffableNode` so a check constraint is directly a table's
* diff-tree child: `id` is the constraint name. `isEqualTo` compares
* `column` and the permitted-value set (order-insensitive — the database
* does not guarantee `IN (...)` ordering).
*/
var SqlCheckConstraintIR = class SqlCheckConstraintIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.check;
name;
column;
permittedValues;
constructor(input) {
super();
this.name = input.name;
this.column = input.column;
this.permittedValues = Object.freeze([...input.permittedValues]);
freezeNode(this);
}
get id() {
return `check:${this.name}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.check;
}
/**
* Compares the permitted-value sets only (unordered), matching the
* relational walk's `verifyCheckConstraints`: two checks pairing by name
* compare their value sets — the `column` field is descriptive, not part
* of the comparison, so a name-paired check with equal values verifies
* regardless of which column carries it.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlCheckConstraintIR", SqlCheckConstraintIR.is);
const thisValues = new Set(this.permittedValues);
const otherValues = new Set(node.permittedValues);
if (thisValues.size !== otherValues.size) return false;
return [...thisValues].every((v) => otherValues.has(v));
}
};
//#endregion
//#region src/ir/resolved-default-equality.ts
/**
* Structural equality for two resolved column defaults, ported from the
* relational walk's `columnDefaultsEqual` normalized branch: kinds must
* match; literal values are normalized (Date and temporal-typed strings to
* ISO instants) then compared canonically (JSON objects match their
* canonical string form); function expressions compare case- and
* whitespace-insensitively.
*
* `nativeType` provides the temporal-normalization context (the actual
* side's resolved native type in a diff comparison).
*/
function resolvedDefaultsEqual(expected, actual, nativeType) {
if (expected.kind !== actual.kind) return false;
if (expected.kind === "literal" && actual.kind === "literal") return literalValuesEqual(normalizeLiteralValue(expected.value, nativeType), normalizeLiteralValue(actual.value, nativeType));
if (expected.kind === "function" && actual.kind === "function") return normalizeFunctionExpression(expected.expression) === normalizeFunctionExpression(actual.expression);
return false;
}
function normalizeFunctionExpression(expression) {
return expression.toLowerCase().replace(/\s+/g, "");
}
function isTemporalNativeType(nativeType) {
if (!nativeType) return false;
const normalized = nativeType.toLowerCase();
return normalized.includes("timestamp") || normalized === "date";
}
function normalizeLiteralValue(value, nativeType) {
if (value instanceof Date) return value.toISOString();
if (typeof value === "string" && isTemporalNativeType(nativeType)) {
const parsed = new Date(value);
if (!Number.isNaN(parsed.getTime())) return parsed.toISOString();
}
return value;
}
function literalValuesEqual(a, b) {
if (a === b) return true;
if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) return canonicalStringify(a) === canonicalStringify(b);
if (typeof a === "object" && a !== null && typeof b === "string") try {
return canonicalStringify(a) === canonicalStringify(JSON.parse(b));
} catch {
return false;
}
if (typeof a === "string" && typeof b === "object" && b !== null) try {
return canonicalStringify(JSON.parse(a)) === canonicalStringify(b);
} catch {
return false;
}
return false;
}
//#endregion
//#region src/ir/sql-column-default-ir.ts
/**
* Schema-diff node for a column's default value. The default is the one
* column attribute with an extra/missing/drift lifecycle of its own, so it
* is a child node of the column rather than a compared attribute: an
* undeclared live default surfaces as `not-expected`, a declared default the
* database lacks as `not-found`, and a divergent value as `not-equal` — the
* three reasons cover the legacy `extra_default` / `default_missing` /
* `default_mismatch` vocabulary with no attribute inspection.
*
* `id` is a fixed sentinel — a column has at most one default. Built
* transiently by `SqlColumnIR.children()` from the column's own fields;
* never constructed by derivation or introspection directly.
*/
var SqlColumnDefaultIR = class SqlColumnDefaultIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.columnDefault;
constructor(input) {
super();
if (input.resolved !== void 0) this.resolved = input.resolved;
if (input.raw !== void 0) this.raw = input.raw;
if (input.nativeTypeContext !== void 0) this.nativeTypeContext = input.nativeTypeContext;
defineNonEnumerable(this, "many", input.many);
freezeNode(this);
}
get id() {
return "default";
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.columnDefault;
}
/**
* Structured comparison with `this` as the expected side: both sides
* resolved compare per the relational walk's `columnDefaultsEqual`
* semantics; a declared expected default against an unparseable actual
* (raw present, no resolved parse) is a mismatch; two raw-only nodes fall
* back to raw string equality.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlColumnDefaultIR", SqlColumnDefaultIR.is);
if (this.resolved !== void 0 && node.resolved !== void 0) return resolvedDefaultsEqual(this.resolved, node.resolved, node.nativeTypeContext ?? this.nativeTypeContext);
if (this.resolved !== void 0 || node.resolved !== void 0) return false;
return this.raw === node.raw;
}
};
//#endregion
//#region src/ir/sql-column-ir.ts
/**
* Schema IR node for a single column on a table, as observed by
* introspection. Unlike the Contract IR `StorageColumn`, this carries
* the column's `name` (Schema IR columns are returned as arrays from
* introspection queries; the parent table re-keys them into a record
* for downstream consumers).
*
* Implements `DiffableNode` so a column is directly a table's diff-tree
* child: `id` is the column name (unique among a table's columns); `isEqualTo`
* compares this column's own attributes only — never children, since a
* column is a leaf. When both sides carry `resolvedNativeType` (stamped at
* derivation/introspection), the comparison uses the resolved values —
* resolved native type, nullability, and structured default equality per
* the relational walk's `columnDefaultsEqual` semantics, with `this` as the
* expected side. Otherwise it falls back to comparing raw fields.
*/
var SqlColumnIR = class SqlColumnIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.column;
name;
nativeType;
nullable;
constructor(input) {
super();
this.name = input.name;
this.nativeType = input.nativeType;
this.nullable = input.nullable;
if (input.default !== void 0) this.default = input.default;
if (input.annotations !== void 0) this.annotations = input.annotations;
if (input.many !== void 0) this.many = input.many;
if (input.resolvedNativeType !== void 0) this.resolvedNativeType = input.resolvedNativeType;
if (input.resolvedDefault !== void 0) this.resolvedDefault = input.resolvedDefault;
defineNonEnumerable(this, "codecRef", input.codecRef);
defineNonEnumerable(this, "codecBaseNativeType", input.codecBaseNativeType);
defineNonEnumerable(this, "codecNamedType", input.codecNamedType);
freezeNode(this);
}
get id() {
return `column:${this.name}`;
}
/**
* The column's default, when declared/present, is the column's one child
* node — it has an extra/missing/drift lifecycle of its own, so the differ
* recurses to it rather than `isEqualTo` comparing it. Built transiently
* from this column's own fields.
*/
children() {
if (this.resolvedDefault === void 0 && this.default === void 0) return [];
return [new SqlColumnDefaultIR({
...ifDefined("resolved", this.resolvedDefault),
...ifDefined("raw", this.default),
...ifDefined("nativeTypeContext", this.resolvedNativeType),
...ifDefined("many", this.many ?? this.codecRef?.many)
})];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.column;
}
/**
* Compares the column's own attributes only — the default lives on the
* child node. When both sides carry `resolvedNativeType`, the resolved
* value governs (array-ness rides on its `[]` suffix); otherwise raw
* fields compare.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlColumnIR", SqlColumnIR.is);
if (this.resolvedNativeType !== void 0 && node.resolvedNativeType !== void 0) return this.resolvedNativeType === node.resolvedNativeType && this.nullable === node.nullable;
return this.nativeType === node.nativeType && this.nullable === node.nullable && Boolean(this.many) === Boolean(node.many);
}
};
//#endregion
//#region src/ir/sql-foreign-key-ir.ts
/**
* Schema IR node for a foreign-key constraint as observed by
* introspection. The `referencedTable` / `referencedColumns` field
* names match the introspection vocabulary (`pg_constraint.confkey`,
* etc.) and intentionally differ from the Contract IR's nested
* `references: { table, columns }` shape so that the verifier's
* structural comparison stays explicit about which side it's reading.
*
* Implements `DiffableNode` so a foreign key is directly a table's diff-tree
* child. Foreign keys are frequently unnamed (introspection may not carry a
* constraint name, and the contract side never invents one), so `id` is
* derived from the referencing/referenced coordinates rather than `name` —
* the same tuple that makes two FK constraints the same constraint. This
* also serves as the comparison key: two FKs with the same coordinates are
* paired by the differ, and `isEqualTo` then compares the remaining
* attribute — the referential actions.
*/
var SqlForeignKeyIR = class SqlForeignKeyIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.foreignKey;
columns;
referencedTable;
referencedColumns;
constructor(input) {
super();
this.columns = input.columns;
this.referencedTable = input.referencedTable;
this.referencedColumns = input.referencedColumns;
if (input.referencedSchema !== void 0) this.referencedSchema = input.referencedSchema;
if (input.name !== void 0) this.name = input.name;
if (input.onDelete !== void 0) this.onDelete = input.onDelete;
if (input.onUpdate !== void 0) this.onUpdate = input.onUpdate;
if (input.annotations !== void 0) this.annotations = input.annotations;
const resolvedReferencedNamespace = input.resolvedReferencedNamespace ?? input.referencedSchema;
if (resolvedReferencedNamespace !== void 0) this.resolvedReferencedNamespace = resolvedReferencedNamespace;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
const referencedNamespace = this.resolvedReferencedNamespace ?? "";
return `foreign-key:${this.columns.join(",")}->${referencedNamespace}.${this.referencedTable}(${this.referencedColumns.join(",")})`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.foreignKey;
}
/**
* Referential-action comparison with `this` as the expected side, matching
* the relational walk's `getReferentialActionMismatches`: `noAction` is the
* database default and equivalent to an undeclared action, and drift is
* flagged only when the expected side declares a (normalized) action.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlForeignKeyIR", SqlForeignKeyIR.is);
return referentialActionMatches(this.onDelete, node.onDelete) && referentialActionMatches(this.onUpdate, node.onUpdate);
}
};
function referentialActionMatches(expected, actual) {
const normalizedExpected = expected === "noAction" ? void 0 : expected;
if (normalizedExpected === void 0) return true;
return normalizedExpected === (actual === "noAction" ? void 0 : actual);
}
//#endregion
//#region src/ir/sql-index-ir.ts
/**
* Schema IR node for a secondary index as observed by introspection.
* Unlike the Contract IR `Index`, the Schema IR carries an explicit
* `unique` field — introspection sees the underlying index regardless
* of whether the user expressed it as `@@index` or `@@unique`, and the
* verifier needs to distinguish them when comparing to the Contract.
*
* Implements `DiffableNode` so an index is directly a table's diff-tree
* child. Indexes are name-identified: every index — contract-derived or
* introspected — carries its full physical name, and `id` is that name.
* Names are catalog-unique per schema, so two indexes legitimately sharing
* one column tuple (a unique index beside a redundant plain index) are two
* distinct siblings, and expression indexes need no column tuple at all.
*
* `isEqualTo` is selected by the receiver (the differ always calls
* `expected.isEqualTo(actual)`) and delegates to {@link contentEquals} —
* the single node-owned content relation: both modes compare `unique`
* strict, `type` and option values through the named normalization seams,
* and `columns` ordered-strict when both sides carry them; an exact-named
* receiver (`prefix === undefined`) additionally byte-compares
* `expression`/`where` (both sides are reprints in the supported flow —
* normalizing would only mask real drift); a managed receiver never
* compares bodies (the wire-name hash already commits to them).
*
* `expression`, `where`, and `unique` are genuine SQL-family attributes —
* functional and partial indexes are standard SQL that any SQL target may
* introspect, so the family node must represent them; a target declining
* to author them is a capability decision, not target-specificity.
*/
var SqlIndexIR = class SqlIndexIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.index;
name;
unique;
constructor(input) {
super();
if (input.columns === void 0 === (input.expression === void 0)) throw new InternalError(`SqlIndexIR "${input.name}": exactly one of columns or expression must be set.`);
this.name = input.name;
this.unique = input.unique;
if (input.prefix !== void 0) this.prefix = input.prefix;
if (input.columns !== void 0) this.columns = input.columns;
if (input.expression !== void 0) this.expression = input.expression;
if (input.where !== void 0) this.where = input.where;
if (input.type !== void 0) this.type = input.type;
if (input.options !== void 0) this.options = input.options;
if (input.annotations !== void 0) this.annotations = input.annotations;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
defineNonEnumerable(this, "partial", input.partial);
freezeNode(this);
}
get id() {
return `index:${this.name}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.index;
}
/**
* Mode-selected structural equality — see the class doc. Delegates to the
* single node-owned relation: `columns` compare ordered-strict when both
* sides carry them; an exact receiver (`prefix === undefined`)
* byte-compares `expression ?? ''` and `where ?? ''`; a managed receiver
* never compares bodies (the wire-name hash already commits to them).
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlIndexIR", SqlIndexIR.is);
return this.contentEquals(node, {
columnPresence: "when-both-defined",
bodies: this.prefix !== void 0 ? "ignored" : "verbatim"
});
}
/**
* The single index content-equality relation — every comparer (the differ
* via {@link isEqualTo}, the planner's rename content-pairing) calls this
* with its mode-appropriate strictness rather than growing a parallel
* relation:
*
* - `columnPresence: 'when-both-defined'` (the differ's rule) compares
* the tuples ordered-strict only when both sides carry them — a paired
* node's identity already agreed, so a column node meeting an
* expression node skips the tuple.
* - `columnPresence: 'matching'` (the rename-pairing rule) additionally
* requires presence to agree: a column index never pairs an expression
* index.
* - `bodies: 'verbatim'` byte-compares `expression ?? ''` / `where ?? ''`
* (absent ≡ empty, no normalization — both sides are reprints in the
* supported flow); `bodies: 'ignored'` skips them (managed identity —
* the wire-name hash commits to the content).
*
* `unique` compares strictly; `type` and option VALUES compare through
* the named normalization seams below.
*/
contentEquals(other, strictness) {
const columnsEqual = strictness.columnPresence === "matching" ? this.columns === void 0 === (other.columns === void 0) && (this.columns === void 0 || isArrayEqual(this.columns, other.columns ?? [])) : this.columns === void 0 || other.columns === void 0 || isArrayEqual(this.columns, other.columns);
if (!(this.unique === other.unique && normalizeIndexType(this.type) === normalizeIndexType(other.type) && indexOptionsEqual(this.options, other.options) && columnsEqual)) return false;
if (strictness.bodies === "ignored") return true;
return (this.expression ?? "") === (other.expression ?? "") && (this.where ?? "") === (other.where ?? "");
}
};
/**
* Comparison-side normalization seam: the default access method (`btree` in
* every supported SQL target) compares as absent, so an authored
* `type: "btree"` and a default-method introspected index (whose type the
* adapter or constructor normalized away) are equal. Applied by
* {@link SqlIndexIR.contentEquals} only — the wire-name hash keeps the
* authored spelling.
*/
function normalizeIndexType(type) {
return type === "btree" ? void 0 : type;
}
/**
* Option-bag equality: same key set, values compared through
* {@link normalizeIndexOptionValue} — Postgres introspection returns
* reloptions values as catalog-reprint strings (`'70'`, `'on'`) while
* contract option leaves are typed (number, boolean, string).
*/
function indexOptionsEqual(a, b) {
const aKeys = a ? Object.keys(a).sort() : [];
const bKeys = b ? Object.keys(b).sort() : [];
if (aKeys.length !== bKeys.length) return false;
for (let i = 0; i < aKeys.length; i += 1) if (aKeys[i] !== bKeys[i]) return false;
if (aKeys.length === 0) return true;
for (const key of aKeys) if (normalizeIndexOptionValue(a?.[key]) !== normalizeIndexOptionValue(b?.[key])) return false;
return true;
}
//#endregion
//#region src/ir/sql-unique-ir.ts
/**
* Schema IR node for a table-level unique constraint as observed by
* introspection.
*
* Implements `DiffableNode` so a unique constraint is directly a table's
* diff-tree child. Unique constraints are frequently unnamed, so `id` is
* derived from the column tuple rather than `name` — the column tuple is
* also what makes two unique constraints the same constraint, so it doubles
* as the pairing key. There are no further attributes to compare once
* columns are equal (the differ pairs on `id`), so `isEqualTo` is identity.
*/
var SqlUniqueIR = class SqlUniqueIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.unique;
columns;
constructor(input) {
super();
this.columns = [...input.columns];
if (input.name !== void 0) this.name = input.name;
if (input.annotations !== void 0) this.annotations = input.annotations;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
return `unique:${this.columns.join(",")}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.unique;
}
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlUniqueIR", SqlUniqueIR.is);
return this.id === node.id;
}
};
//#endregion
//#region src/ir/sql-table-ir.ts
/**
* Schema IR node for a single table as observed by introspection.
*
* Unlike the Contract IR `StorageTable`, this carries the table's
* `name` — introspection queries return tables as arrays and the
* verifier keys them into `SqlSchemaIR.tables` afterwards, so the name
* stays on the table object for downstream call sites that walk
* `Object.values(schema.tables)`.
*
* The constructor normalises nested IR-class fields so downstream
* walks see a uniform AST regardless of whether the input was a
* plain-data literal (from introspection) or already-constructed
* class instances.
*
* Implements `DiffableNode` so a flat (single-schema) tree is directly
* diffable: `id` is the table name; `isEqualTo` is identity (the table's
* structural drift is entirely expressed by its children); `children()`
* yields every column, the primary key (when present), every foreign key,
* unique, index, and check constraint — the same composition and order as
* the Postgres table node, minus policies.
*/
var SqlTableIR = class extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.table;
name;
columns;
foreignKeys;
uniques;
indexes;
constructor(input) {
super();
this.name = input.name;
this.columns = Object.freeze(Object.fromEntries(Object.entries(input.columns).map(([key, col]) => [key, col instanceof SqlColumnIR ? col : new SqlColumnIR(col)])));
this.foreignKeys = Object.freeze(input.foreignKeys.map((fk) => fk instanceof SqlForeignKeyIR ? fk : new SqlForeignKeyIR(fk)));
this.uniques = Object.freeze(input.uniques.map((u) => u instanceof SqlUniqueIR ? u : new SqlUniqueIR(u)));
this.indexes = Object.freeze(input.indexes.map((i) => i instanceof SqlIndexIR ? i : new SqlIndexIR(i)));
if (input.primaryKey !== void 0) this.primaryKey = input.primaryKey instanceof PrimaryKey ? input.primaryKey : new PrimaryKey(input.primaryKey);
if (input.annotations !== void 0) this.annotations = input.annotations;
if (input.checks !== void 0 && input.checks.length > 0) this.checks = Object.freeze(input.checks.map((c) => c instanceof SqlCheckConstraintIR ? c : new SqlCheckConstraintIR(c)));
freezeNode(this);
}
get id() {
return this.name;
}
isEqualTo(other) {
return this.id === other.id;
}
children() {
return [
...Object.values(this.columns),
...this.primaryKey ? [this.primaryKey] : [],
...this.foreignKeys,
...this.uniques,
...this.indexes,
...this.checks ?? []
];
}
};
//#endregion
//#region src/ir/sql-schema-ir.ts
/**
* Root Schema IR node representing the complete database schema as
* observed by introspection. Target-agnostic; used by both verifiers
* (compare against intended Contract storage) and migration planners
* (derive operations needed to reconcile).
*
* The constructor normalises nested `SqlTableIR` instances so
* downstream walks see a uniform AST regardless of whether the input
* was a plain-data literal or already-constructed class instances.
*
* Implements `DiffableNode` as the root of a flat (single-schema) diff
* tree: `id` is the fixed sentinel `'database'` (roots have no siblings),
* `isEqualTo` is identity (a container has no own attributes), and
* `children()` yields the table nodes.
*/
var SqlSchemaIR = class extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.schema;
tables;
constructor(input) {
super();
this.tables = Object.freeze(Object.fromEntries(Object.entries(input.tables).map(([key, t]) => [key, t instanceof SqlTableIR ? t : new SqlTableIR(t)])));
if (input.annotations !== void 0) this.annotations = input.annotations;
freezeNode(this);
}
get id() {
return "database";
}
isEqualTo(other) {
return this.id === other.id;
}
children() {
return Object.values(this.tables);
}
};
//#endregion
export { SqlForeignKeyIR as a, SqlCheckConstraintIR as c, assertNode as d, defineNonEnumerable as f, relationalNodeGranularity as h, SqlIndexIR as i, PrimaryKey as l, relationalNodeEntityKind as m, SqlTableIR as n, SqlColumnIR as o, RelationalSchemaNodeKind as p, SqlUniqueIR as r, SqlColumnDefaultIR as s, SqlSchemaIR as t, SqlSchemaIRNode as u };
//# sourceMappingURL=types-CP9M6LJk.mjs.map

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

+1
-1

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

import { a as SqlForeignKeyIR, c as SqlCheckConstraintIR, d as assertNode, f as defineNonEnumerable, h as relationalNodeGranularity, i as SqlIndexIR, l as PrimaryKey, m as relationalNodeEntityKind, n as SqlTableIR, o as SqlColumnIR, p as RelationalSchemaNodeKind, r as SqlUniqueIR, s as SqlColumnDefaultIR, t as SqlSchemaIR, u as SqlSchemaIRNode } from "../types-Btj35AIt.mjs";
import { a as SqlForeignKeyIR, c as SqlCheckConstraintIR, d as assertNode, f as defineNonEnumerable, h as relationalNodeGranularity, i as SqlIndexIR, l as PrimaryKey, m as relationalNodeEntityKind, n as SqlTableIR, o as SqlColumnIR, p as RelationalSchemaNodeKind, r as SqlUniqueIR, s as SqlColumnDefaultIR, t as SqlSchemaIR, u as SqlSchemaIRNode } from "../types-CP9M6LJk.mjs";
export { PrimaryKey, RelationalSchemaNodeKind, SqlCheckConstraintIR, SqlColumnDefaultIR, SqlColumnIR, SqlForeignKeyIR, SqlIndexIR, SqlSchemaIR, SqlSchemaIRNode, SqlTableIR, SqlUniqueIR, assertNode, defineNonEnumerable, relationalNodeEntityKind, relationalNodeGranularity };

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

import { a as SqlForeignKeyIR, c as SqlCheckConstraintIR, d as assertNode, f as defineNonEnumerable, h as relationalNodeGranularity, i as SqlIndexIR, l as PrimaryKey, m as relationalNodeEntityKind, n as SqlTableIR, o as SqlColumnIR, p as RelationalSchemaNodeKind, r as SqlUniqueIR, s as SqlColumnDefaultIR, t as SqlSchemaIR, u as SqlSchemaIRNode } from "./types-Btj35AIt.mjs";
import { a as SqlForeignKeyIR, c as SqlCheckConstraintIR, d as assertNode, f as defineNonEnumerable, h as relationalNodeGranularity, i as SqlIndexIR, l as PrimaryKey, m as relationalNodeEntityKind, n as SqlTableIR, o as SqlColumnIR, p as RelationalSchemaNodeKind, r as SqlUniqueIR, s as SqlColumnDefaultIR, t as SqlSchemaIR, u as SqlSchemaIRNode } from "./types-CP9M6LJk.mjs";
export { PrimaryKey, RelationalSchemaNodeKind, SqlCheckConstraintIR, SqlColumnDefaultIR, SqlColumnIR, SqlForeignKeyIR, SqlIndexIR, SqlSchemaIR, SqlSchemaIRNode, SqlTableIR, SqlUniqueIR, assertNode, defineNonEnumerable, relationalNodeEntityKind, relationalNodeGranularity };

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

{"version":3,"file":"types-DzLk5eOU.d.mts","names":[],"sources":["../src/ir/sql-schema-ir-node.ts","../src/ir/primary-key.ts","../src/ir/schema-node-kinds.ts","../src/ir/sql-check-constraint-ir.ts","../src/ir/sql-column-default-ir.ts","../src/ir/sql-column-ir.ts","../src/ir/sql-foreign-key-ir.ts","../src/ir/sql-index-ir.ts","../src/ir/sql-unique-ir.ts","../src/ir/sql-table-ir.ts","../src/ir/sql-schema-ir.ts","../src/types.ts"],"mappings":"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"}
{"version":3,"file":"types-DzLk5eOU.d.mts","names":[],"sources":["../src/ir/sql-schema-ir-node.ts","../src/ir/primary-key.ts","../src/ir/schema-node-kinds.ts","../src/ir/sql-check-constraint-ir.ts","../src/ir/sql-column-default-ir.ts","../src/ir/sql-column-ir.ts","../src/ir/sql-foreign-key-ir.ts","../src/ir/sql-index-ir.ts","../src/ir/sql-unique-ir.ts","../src/ir/sql-table-ir.ts","../src/ir/sql-schema-ir.ts","../src/types.ts"],"mappings":"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"}
{
"name": "@prisma-next/sql-schema-ir",
"version": "0.16.0-dev.30",
"version": "0.16.0-dev.31",
"license": "Apache-2.0",

@@ -9,10 +9,10 @@ "type": "module",

"dependencies": {
"@prisma-next/contract": "0.16.0-dev.30",
"@prisma-next/framework-components": "0.16.0-dev.30",
"@prisma-next/utils": "0.16.0-dev.30"
"@prisma-next/contract": "0.16.0-dev.31",
"@prisma-next/framework-components": "0.16.0-dev.31",
"@prisma-next/utils": "0.16.0-dev.31"
},
"devDependencies": {
"@prisma-next/test-utils": "0.16.0-dev.30",
"@prisma-next/tsconfig": "0.16.0-dev.30",
"@prisma-next/tsdown": "0.16.0-dev.30",
"@prisma-next/test-utils": "0.16.0-dev.31",
"@prisma-next/tsconfig": "0.16.0-dev.31",
"@prisma-next/tsdown": "0.16.0-dev.31",
"tsdown": "0.22.8",

@@ -19,0 +19,0 @@ "typescript": "5.9.3",

import type { DiffSubjectGranularity } from '@prisma-next/framework-components/control';
import { InternalError } from '@prisma-next/utils/internal-error';

@@ -61,3 +62,5 @@ /**

if (!isRelationalSchemaNodeKind(nodeKind)) {
throw new Error(`relationalNodeGranularity: unrecognized relational node kind "${nodeKind}"`);
throw new InternalError(
`relationalNodeGranularity: unrecognized relational node kind "${nodeKind}"`,
);
}

@@ -64,0 +67,0 @@ return RELATIONAL_NODE_GRANULARITY[nodeKind];

import { IRNodeBase } from '@prisma-next/framework-components/ir';
import { InternalError } from '@prisma-next/utils/internal-error';

@@ -60,3 +61,5 @@ /**

if (node === undefined || !predicate(node)) {
throw new Error(`Expected a ${className} but got nodeKind=${node?.nodeKind ?? 'undefined'}`);
throw new InternalError(
`Expected a ${className} but got nodeKind=${node?.nodeKind ?? 'undefined'}`,
);
}

@@ -63,0 +66,0 @@ }

import { o as normalizeIndexOptionValue } from "./naming-BD7Y-Fq1.mjs";
import { IRNodeBase, freezeNode } from "@prisma-next/framework-components/ir";
import { blindCast } from "@prisma-next/utils/casts";
import { canonicalStringify } from "@prisma-next/utils/canonical-stringify";
import { ifDefined } from "@prisma-next/utils/defined";
import { isArrayEqual } from "@prisma-next/utils/array-equal";
import { InternalError } from "@prisma-next/utils/internal-error";
//#region src/ir/schema-node-kinds.ts
/**
* The `nodeKind` discriminant for each relational schema-diff leaf node.
* Each node carries a unique value; the differ pairs siblings by `id`, and
* these kinds distinguish the node types that appear as `PostgresTableSchemaNode`
* children (columns, primary key, foreign keys, uniques, indexes, checks) from
* each other and from the RLS policy/role kinds a target defines separately.
*/
const RelationalSchemaNodeKind = {
schema: "sql-schema",
table: "sql-table",
column: "sql-column",
columnDefault: "sql-column-default",
primaryKey: "sql-primary-key",
foreignKey: "sql-foreign-key",
unique: "sql-unique",
index: "sql-index",
check: "sql-check-constraint"
};
/**
* The one real map from a relational `nodeKind` to the framework-neutral
* {@link DiffSubjectGranularity} its diff issues carry — the SQL family's
* post-diff filters (issue category, strict-mode gating) and the framework
* aggregate's unclaimed-elements sweep key on the granularity, never on the
* `nodeKind` spelling and never on anything stamped on the node. Resolution
* is by `nodeKind` equality against this map, not a suffix string match.
* Target-specific node kinds (Postgres namespace/table/policy/role) are
* outside this family layer's vocabulary and map their own kinds directly.
*/
const RELATIONAL_NODE_GRANULARITY = {
[RelationalSchemaNodeKind.schema]: "structural",
[RelationalSchemaNodeKind.table]: "entity",
[RelationalSchemaNodeKind.column]: "field",
[RelationalSchemaNodeKind.columnDefault]: "auxiliary",
[RelationalSchemaNodeKind.primaryKey]: "auxiliary",
[RelationalSchemaNodeKind.foreignKey]: "auxiliary",
[RelationalSchemaNodeKind.unique]: "auxiliary",
[RelationalSchemaNodeKind.index]: "auxiliary",
[RelationalSchemaNodeKind.check]: "auxiliary"
};
function isRelationalSchemaNodeKind(nodeKind) {
return Object.hasOwn(RELATIONAL_NODE_GRANULARITY, nodeKind);
}
/**
* Looks up the subject granularity for a relational `nodeKind`. Throws for a
* `nodeKind` outside this map (a target-specific kind, e.g. Postgres's
* namespace/table/policy/role) — those map their own kinds directly rather
* than through this family-level map.
*/
function relationalNodeGranularity(nodeKind) {
if (!isRelationalSchemaNodeKind(nodeKind)) throw new Error(`relationalNodeGranularity: unrecognized relational node kind "${nodeKind}"`);
return RELATIONAL_NODE_GRANULARITY[nodeKind];
}
/**
* The one real map from a relational `nodeKind` to its storage `entityKind`
* — the same vocabulary the contract storage's `entries` dictionary keys use
* (`elementCoordinates` walks it). Only the whole-table kind has an entity of
* its own; every other relational node (a column, an index, …) is nested
* under one and maps to nothing here.
*/
const RELATIONAL_NODE_ENTITY_KIND = { [RelationalSchemaNodeKind.table]: "table" };
/**
* Looks up the storage `entityKind` for a relational `nodeKind` — sibling of
* {@link relationalNodeGranularity}, resolved by `nodeKind` equality against
* {@link RELATIONAL_NODE_ENTITY_KIND}. `undefined` for a target-specific kind
* (targets map their own kinds directly) or a relational kind with no entity
* of its own.
*/
function relationalNodeEntityKind(nodeKind) {
return isRelationalSchemaNodeKind(nodeKind) ? RELATIONAL_NODE_ENTITY_KIND[nodeKind] : void 0;
}
//#endregion
//#region src/ir/sql-schema-ir-node.ts
/**
* SQL Schema IR node base. Carries the family-level
* `kind = 'sql-schema-ir'` discriminator and inherits the framework's
* `freezeNode` affordance.
*
* SQL Schema IR represents the actual database state as discovered by
* introspection (the parallel to SQL Contract IR, which represents the
* desired state).
*
* The discriminator is installed as a non-enumerable own property,
* matching the SqlNode pattern. This keeps `JSON.stringify(node)`
* canonical (no `kind` field), keeps `toEqual({...})` test assertions
* against pre-lift flat shapes passing, and keeps `node.kind` readable
* for dispatch.
*
* Both `kind` and `nodeKind` are required: every concrete leaf is a node
* the generic differ can pair and compare, so every leaf must declare which
* node it is. `nodeKind` has no default here — every direct subclass sets its
* own literal value (the relational leaves via `RelationalSchemaNodeKind`,
* target concretions via their own vocabulary, e.g. `PostgresSchemaNodeKind`).
*/
var SqlSchemaIRNode = class extends IRNodeBase {
constructor() {
super();
Object.defineProperty(this, "kind", {
value: "sql-schema-ir",
writable: false,
enumerable: false,
configurable: false
});
}
};
/**
* Asserts `node` matches `predicate`, narrowing its type to `T`. The one
* shared implementation every node class's `static assert` and `isEqualTo`
* reach for, instead of each hand-writing its own throw: the message names
* the class the caller expected.
*/
function assertNode(node, className, predicate) {
if (node === void 0 || !predicate(node)) throw new Error(`Expected a ${className} but got nodeKind=${node?.nodeKind ?? "undefined"}`);
}
/**
* Defines a non-enumerable own property, the same treatment `kind` gets
* above: a derivation-time render-support field stays out of
* `JSON.stringify`, `toEqual({...})` structural assertions, and spreads,
* while remaining directly readable (`node.field`) for the one consumer
* that resolves it at plan time. A no-op when `value` is `undefined` — the
* property is simply absent, matching every other optional field on these
* nodes.
*/
function defineNonEnumerable(target, key, value) {
if (value === void 0) return;
Object.defineProperty(target, key, {
value,
enumerable: false,
writable: false,
configurable: false
});
}
//#endregion
//#region src/ir/primary-key.ts
/**
* Primary-key Schema IR node. Mirrors the Contract IR `PrimaryKey`
* shape (same `columns` + optional `name`) so verification can compare
* intent and actual structurally. Defined here independently to avoid
* a sql-schema-ir -> sql-contract dependency.
*
* Implements `DiffableNode` so a primary key is directly a table's diff-tree
* child. `id` is a fixed sentinel — a table has at most one primary key, so
* there is never a sibling to disambiguate against. `isEqualTo` compares the
* column tuple; the PK's own `name` is a database-assigned label with no
* semantic weight, so it is not compared (mirrors the policy node's
* name-insensitivity to non-identifying detail).
*/
var PrimaryKey = class PrimaryKey extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.primaryKey;
columns;
constructor(input) {
super();
this.columns = input.columns;
if (input.name !== void 0) this.name = input.name;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
return "primary-key";
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.primaryKey;
}
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "PrimaryKey", PrimaryKey.is);
return this.columns.length === node.columns.length && this.columns.every((c, i) => c === node.columns[i]);
}
};
//#endregion
//#region src/ir/sql-check-constraint-ir.ts
/**
* Schema IR node for a table-level check constraint that restricts a
* column to a set of permitted values (an enum-style `IN (...)` check).
*
* Carries the **resolved values** rather than a raw SQL predicate so
* callers can compare value-sets without parsing SQL.
*
* Implements `DiffableNode` so a check constraint is directly a table's
* diff-tree child: `id` is the constraint name. `isEqualTo` compares
* `column` and the permitted-value set (order-insensitive — the database
* does not guarantee `IN (...)` ordering).
*/
var SqlCheckConstraintIR = class SqlCheckConstraintIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.check;
name;
column;
permittedValues;
constructor(input) {
super();
this.name = input.name;
this.column = input.column;
this.permittedValues = Object.freeze([...input.permittedValues]);
freezeNode(this);
}
get id() {
return `check:${this.name}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.check;
}
/**
* Compares the permitted-value sets only (unordered), matching the
* relational walk's `verifyCheckConstraints`: two checks pairing by name
* compare their value sets — the `column` field is descriptive, not part
* of the comparison, so a name-paired check with equal values verifies
* regardless of which column carries it.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlCheckConstraintIR", SqlCheckConstraintIR.is);
const thisValues = new Set(this.permittedValues);
const otherValues = new Set(node.permittedValues);
if (thisValues.size !== otherValues.size) return false;
return [...thisValues].every((v) => otherValues.has(v));
}
};
//#endregion
//#region src/ir/resolved-default-equality.ts
/**
* Structural equality for two resolved column defaults, ported from the
* relational walk's `columnDefaultsEqual` normalized branch: kinds must
* match; literal values are normalized (Date and temporal-typed strings to
* ISO instants) then compared canonically (JSON objects match their
* canonical string form); function expressions compare case- and
* whitespace-insensitively.
*
* `nativeType` provides the temporal-normalization context (the actual
* side's resolved native type in a diff comparison).
*/
function resolvedDefaultsEqual(expected, actual, nativeType) {
if (expected.kind !== actual.kind) return false;
if (expected.kind === "literal" && actual.kind === "literal") return literalValuesEqual(normalizeLiteralValue(expected.value, nativeType), normalizeLiteralValue(actual.value, nativeType));
if (expected.kind === "function" && actual.kind === "function") return normalizeFunctionExpression(expected.expression) === normalizeFunctionExpression(actual.expression);
return false;
}
function normalizeFunctionExpression(expression) {
return expression.toLowerCase().replace(/\s+/g, "");
}
function isTemporalNativeType(nativeType) {
if (!nativeType) return false;
const normalized = nativeType.toLowerCase();
return normalized.includes("timestamp") || normalized === "date";
}
function normalizeLiteralValue(value, nativeType) {
if (value instanceof Date) return value.toISOString();
if (typeof value === "string" && isTemporalNativeType(nativeType)) {
const parsed = new Date(value);
if (!Number.isNaN(parsed.getTime())) return parsed.toISOString();
}
return value;
}
function literalValuesEqual(a, b) {
if (a === b) return true;
if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) return canonicalStringify(a) === canonicalStringify(b);
if (typeof a === "object" && a !== null && typeof b === "string") try {
return canonicalStringify(a) === canonicalStringify(JSON.parse(b));
} catch {
return false;
}
if (typeof a === "string" && typeof b === "object" && b !== null) try {
return canonicalStringify(JSON.parse(a)) === canonicalStringify(b);
} catch {
return false;
}
return false;
}
//#endregion
//#region src/ir/sql-column-default-ir.ts
/**
* Schema-diff node for a column's default value. The default is the one
* column attribute with an extra/missing/drift lifecycle of its own, so it
* is a child node of the column rather than a compared attribute: an
* undeclared live default surfaces as `not-expected`, a declared default the
* database lacks as `not-found`, and a divergent value as `not-equal` — the
* three reasons cover the legacy `extra_default` / `default_missing` /
* `default_mismatch` vocabulary with no attribute inspection.
*
* `id` is a fixed sentinel — a column has at most one default. Built
* transiently by `SqlColumnIR.children()` from the column's own fields;
* never constructed by derivation or introspection directly.
*/
var SqlColumnDefaultIR = class SqlColumnDefaultIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.columnDefault;
constructor(input) {
super();
if (input.resolved !== void 0) this.resolved = input.resolved;
if (input.raw !== void 0) this.raw = input.raw;
if (input.nativeTypeContext !== void 0) this.nativeTypeContext = input.nativeTypeContext;
defineNonEnumerable(this, "many", input.many);
freezeNode(this);
}
get id() {
return "default";
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.columnDefault;
}
/**
* Structured comparison with `this` as the expected side: both sides
* resolved compare per the relational walk's `columnDefaultsEqual`
* semantics; a declared expected default against an unparseable actual
* (raw present, no resolved parse) is a mismatch; two raw-only nodes fall
* back to raw string equality.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlColumnDefaultIR", SqlColumnDefaultIR.is);
if (this.resolved !== void 0 && node.resolved !== void 0) return resolvedDefaultsEqual(this.resolved, node.resolved, node.nativeTypeContext ?? this.nativeTypeContext);
if (this.resolved !== void 0 || node.resolved !== void 0) return false;
return this.raw === node.raw;
}
};
//#endregion
//#region src/ir/sql-column-ir.ts
/**
* Schema IR node for a single column on a table, as observed by
* introspection. Unlike the Contract IR `StorageColumn`, this carries
* the column's `name` (Schema IR columns are returned as arrays from
* introspection queries; the parent table re-keys them into a record
* for downstream consumers).
*
* Implements `DiffableNode` so a column is directly a table's diff-tree
* child: `id` is the column name (unique among a table's columns); `isEqualTo`
* compares this column's own attributes only — never children, since a
* column is a leaf. When both sides carry `resolvedNativeType` (stamped at
* derivation/introspection), the comparison uses the resolved values —
* resolved native type, nullability, and structured default equality per
* the relational walk's `columnDefaultsEqual` semantics, with `this` as the
* expected side. Otherwise it falls back to comparing raw fields.
*/
var SqlColumnIR = class SqlColumnIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.column;
name;
nativeType;
nullable;
constructor(input) {
super();
this.name = input.name;
this.nativeType = input.nativeType;
this.nullable = input.nullable;
if (input.default !== void 0) this.default = input.default;
if (input.annotations !== void 0) this.annotations = input.annotations;
if (input.many !== void 0) this.many = input.many;
if (input.resolvedNativeType !== void 0) this.resolvedNativeType = input.resolvedNativeType;
if (input.resolvedDefault !== void 0) this.resolvedDefault = input.resolvedDefault;
defineNonEnumerable(this, "codecRef", input.codecRef);
defineNonEnumerable(this, "codecBaseNativeType", input.codecBaseNativeType);
defineNonEnumerable(this, "codecNamedType", input.codecNamedType);
freezeNode(this);
}
get id() {
return `column:${this.name}`;
}
/**
* The column's default, when declared/present, is the column's one child
* node — it has an extra/missing/drift lifecycle of its own, so the differ
* recurses to it rather than `isEqualTo` comparing it. Built transiently
* from this column's own fields.
*/
children() {
if (this.resolvedDefault === void 0 && this.default === void 0) return [];
return [new SqlColumnDefaultIR({
...ifDefined("resolved", this.resolvedDefault),
...ifDefined("raw", this.default),
...ifDefined("nativeTypeContext", this.resolvedNativeType),
...ifDefined("many", this.many ?? this.codecRef?.many)
})];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.column;
}
/**
* Compares the column's own attributes only — the default lives on the
* child node. When both sides carry `resolvedNativeType`, the resolved
* value governs (array-ness rides on its `[]` suffix); otherwise raw
* fields compare.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlColumnIR", SqlColumnIR.is);
if (this.resolvedNativeType !== void 0 && node.resolvedNativeType !== void 0) return this.resolvedNativeType === node.resolvedNativeType && this.nullable === node.nullable;
return this.nativeType === node.nativeType && this.nullable === node.nullable && Boolean(this.many) === Boolean(node.many);
}
};
//#endregion
//#region src/ir/sql-foreign-key-ir.ts
/**
* Schema IR node for a foreign-key constraint as observed by
* introspection. The `referencedTable` / `referencedColumns` field
* names match the introspection vocabulary (`pg_constraint.confkey`,
* etc.) and intentionally differ from the Contract IR's nested
* `references: { table, columns }` shape so that the verifier's
* structural comparison stays explicit about which side it's reading.
*
* Implements `DiffableNode` so a foreign key is directly a table's diff-tree
* child. Foreign keys are frequently unnamed (introspection may not carry a
* constraint name, and the contract side never invents one), so `id` is
* derived from the referencing/referenced coordinates rather than `name` —
* the same tuple that makes two FK constraints the same constraint. This
* also serves as the comparison key: two FKs with the same coordinates are
* paired by the differ, and `isEqualTo` then compares the remaining
* attribute — the referential actions.
*/
var SqlForeignKeyIR = class SqlForeignKeyIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.foreignKey;
columns;
referencedTable;
referencedColumns;
constructor(input) {
super();
this.columns = input.columns;
this.referencedTable = input.referencedTable;
this.referencedColumns = input.referencedColumns;
if (input.referencedSchema !== void 0) this.referencedSchema = input.referencedSchema;
if (input.name !== void 0) this.name = input.name;
if (input.onDelete !== void 0) this.onDelete = input.onDelete;
if (input.onUpdate !== void 0) this.onUpdate = input.onUpdate;
if (input.annotations !== void 0) this.annotations = input.annotations;
const resolvedReferencedNamespace = input.resolvedReferencedNamespace ?? input.referencedSchema;
if (resolvedReferencedNamespace !== void 0) this.resolvedReferencedNamespace = resolvedReferencedNamespace;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
const referencedNamespace = this.resolvedReferencedNamespace ?? "";
return `foreign-key:${this.columns.join(",")}->${referencedNamespace}.${this.referencedTable}(${this.referencedColumns.join(",")})`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.foreignKey;
}
/**
* Referential-action comparison with `this` as the expected side, matching
* the relational walk's `getReferentialActionMismatches`: `noAction` is the
* database default and equivalent to an undeclared action, and drift is
* flagged only when the expected side declares a (normalized) action.
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlForeignKeyIR", SqlForeignKeyIR.is);
return referentialActionMatches(this.onDelete, node.onDelete) && referentialActionMatches(this.onUpdate, node.onUpdate);
}
};
function referentialActionMatches(expected, actual) {
const normalizedExpected = expected === "noAction" ? void 0 : expected;
if (normalizedExpected === void 0) return true;
return normalizedExpected === (actual === "noAction" ? void 0 : actual);
}
//#endregion
//#region src/ir/sql-index-ir.ts
/**
* Schema IR node for a secondary index as observed by introspection.
* Unlike the Contract IR `Index`, the Schema IR carries an explicit
* `unique` field — introspection sees the underlying index regardless
* of whether the user expressed it as `@@index` or `@@unique`, and the
* verifier needs to distinguish them when comparing to the Contract.
*
* Implements `DiffableNode` so an index is directly a table's diff-tree
* child. Indexes are name-identified: every index — contract-derived or
* introspected — carries its full physical name, and `id` is that name.
* Names are catalog-unique per schema, so two indexes legitimately sharing
* one column tuple (a unique index beside a redundant plain index) are two
* distinct siblings, and expression indexes need no column tuple at all.
*
* `isEqualTo` is selected by the receiver (the differ always calls
* `expected.isEqualTo(actual)`) and delegates to {@link contentEquals} —
* the single node-owned content relation: both modes compare `unique`
* strict, `type` and option values through the named normalization seams,
* and `columns` ordered-strict when both sides carry them; an exact-named
* receiver (`prefix === undefined`) additionally byte-compares
* `expression`/`where` (both sides are reprints in the supported flow —
* normalizing would only mask real drift); a managed receiver never
* compares bodies (the wire-name hash already commits to them).
*
* `expression`, `where`, and `unique` are genuine SQL-family attributes —
* functional and partial indexes are standard SQL that any SQL target may
* introspect, so the family node must represent them; a target declining
* to author them is a capability decision, not target-specificity.
*/
var SqlIndexIR = class SqlIndexIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.index;
name;
unique;
constructor(input) {
super();
if (input.columns === void 0 === (input.expression === void 0)) throw new InternalError(`SqlIndexIR "${input.name}": exactly one of columns or expression must be set.`);
this.name = input.name;
this.unique = input.unique;
if (input.prefix !== void 0) this.prefix = input.prefix;
if (input.columns !== void 0) this.columns = input.columns;
if (input.expression !== void 0) this.expression = input.expression;
if (input.where !== void 0) this.where = input.where;
if (input.type !== void 0) this.type = input.type;
if (input.options !== void 0) this.options = input.options;
if (input.annotations !== void 0) this.annotations = input.annotations;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
defineNonEnumerable(this, "partial", input.partial);
freezeNode(this);
}
get id() {
return `index:${this.name}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.index;
}
/**
* Mode-selected structural equality — see the class doc. Delegates to the
* single node-owned relation: `columns` compare ordered-strict when both
* sides carry them; an exact receiver (`prefix === undefined`)
* byte-compares `expression ?? ''` and `where ?? ''`; a managed receiver
* never compares bodies (the wire-name hash already commits to them).
*/
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlIndexIR", SqlIndexIR.is);
return this.contentEquals(node, {
columnPresence: "when-both-defined",
bodies: this.prefix !== void 0 ? "ignored" : "verbatim"
});
}
/**
* The single index content-equality relation — every comparer (the differ
* via {@link isEqualTo}, the planner's rename content-pairing) calls this
* with its mode-appropriate strictness rather than growing a parallel
* relation:
*
* - `columnPresence: 'when-both-defined'` (the differ's rule) compares
* the tuples ordered-strict only when both sides carry them — a paired
* node's identity already agreed, so a column node meeting an
* expression node skips the tuple.
* - `columnPresence: 'matching'` (the rename-pairing rule) additionally
* requires presence to agree: a column index never pairs an expression
* index.
* - `bodies: 'verbatim'` byte-compares `expression ?? ''` / `where ?? ''`
* (absent ≡ empty, no normalization — both sides are reprints in the
* supported flow); `bodies: 'ignored'` skips them (managed identity —
* the wire-name hash commits to the content).
*
* `unique` compares strictly; `type` and option VALUES compare through
* the named normalization seams below.
*/
contentEquals(other, strictness) {
const columnsEqual = strictness.columnPresence === "matching" ? this.columns === void 0 === (other.columns === void 0) && (this.columns === void 0 || isArrayEqual(this.columns, other.columns ?? [])) : this.columns === void 0 || other.columns === void 0 || isArrayEqual(this.columns, other.columns);
if (!(this.unique === other.unique && normalizeIndexType(this.type) === normalizeIndexType(other.type) && indexOptionsEqual(this.options, other.options) && columnsEqual)) return false;
if (strictness.bodies === "ignored") return true;
return (this.expression ?? "") === (other.expression ?? "") && (this.where ?? "") === (other.where ?? "");
}
};
/**
* Comparison-side normalization seam: the default access method (`btree` in
* every supported SQL target) compares as absent, so an authored
* `type: "btree"` and a default-method introspected index (whose type the
* adapter or constructor normalized away) are equal. Applied by
* {@link SqlIndexIR.contentEquals} only — the wire-name hash keeps the
* authored spelling.
*/
function normalizeIndexType(type) {
return type === "btree" ? void 0 : type;
}
/**
* Option-bag equality: same key set, values compared through
* {@link normalizeIndexOptionValue} — Postgres introspection returns
* reloptions values as catalog-reprint strings (`'70'`, `'on'`) while
* contract option leaves are typed (number, boolean, string).
*/
function indexOptionsEqual(a, b) {
const aKeys = a ? Object.keys(a).sort() : [];
const bKeys = b ? Object.keys(b).sort() : [];
if (aKeys.length !== bKeys.length) return false;
for (let i = 0; i < aKeys.length; i += 1) if (aKeys[i] !== bKeys[i]) return false;
if (aKeys.length === 0) return true;
for (const key of aKeys) if (normalizeIndexOptionValue(a?.[key]) !== normalizeIndexOptionValue(b?.[key])) return false;
return true;
}
//#endregion
//#region src/ir/sql-unique-ir.ts
/**
* Schema IR node for a table-level unique constraint as observed by
* introspection.
*
* Implements `DiffableNode` so a unique constraint is directly a table's
* diff-tree child. Unique constraints are frequently unnamed, so `id` is
* derived from the column tuple rather than `name` — the column tuple is
* also what makes two unique constraints the same constraint, so it doubles
* as the pairing key. There are no further attributes to compare once
* columns are equal (the differ pairs on `id`), so `isEqualTo` is identity.
*/
var SqlUniqueIR = class SqlUniqueIR extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.unique;
columns;
constructor(input) {
super();
this.columns = [...input.columns];
if (input.name !== void 0) this.name = input.name;
if (input.annotations !== void 0) this.annotations = input.annotations;
defineNonEnumerable(this, "dependsOn", input.dependsOn);
freezeNode(this);
}
get id() {
return `unique:${this.columns.join(",")}`;
}
children() {
return [];
}
static is(node) {
return node.nodeKind === RelationalSchemaNodeKind.unique;
}
isEqualTo(other) {
const node = blindCast(other);
assertNode(node, "SqlUniqueIR", SqlUniqueIR.is);
return this.id === node.id;
}
};
//#endregion
//#region src/ir/sql-table-ir.ts
/**
* Schema IR node for a single table as observed by introspection.
*
* Unlike the Contract IR `StorageTable`, this carries the table's
* `name` — introspection queries return tables as arrays and the
* verifier keys them into `SqlSchemaIR.tables` afterwards, so the name
* stays on the table object for downstream call sites that walk
* `Object.values(schema.tables)`.
*
* The constructor normalises nested IR-class fields so downstream
* walks see a uniform AST regardless of whether the input was a
* plain-data literal (from introspection) or already-constructed
* class instances.
*
* Implements `DiffableNode` so a flat (single-schema) tree is directly
* diffable: `id` is the table name; `isEqualTo` is identity (the table's
* structural drift is entirely expressed by its children); `children()`
* yields every column, the primary key (when present), every foreign key,
* unique, index, and check constraint — the same composition and order as
* the Postgres table node, minus policies.
*/
var SqlTableIR = class extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.table;
name;
columns;
foreignKeys;
uniques;
indexes;
constructor(input) {
super();
this.name = input.name;
this.columns = Object.freeze(Object.fromEntries(Object.entries(input.columns).map(([key, col]) => [key, col instanceof SqlColumnIR ? col : new SqlColumnIR(col)])));
this.foreignKeys = Object.freeze(input.foreignKeys.map((fk) => fk instanceof SqlForeignKeyIR ? fk : new SqlForeignKeyIR(fk)));
this.uniques = Object.freeze(input.uniques.map((u) => u instanceof SqlUniqueIR ? u : new SqlUniqueIR(u)));
this.indexes = Object.freeze(input.indexes.map((i) => i instanceof SqlIndexIR ? i : new SqlIndexIR(i)));
if (input.primaryKey !== void 0) this.primaryKey = input.primaryKey instanceof PrimaryKey ? input.primaryKey : new PrimaryKey(input.primaryKey);
if (input.annotations !== void 0) this.annotations = input.annotations;
if (input.checks !== void 0 && input.checks.length > 0) this.checks = Object.freeze(input.checks.map((c) => c instanceof SqlCheckConstraintIR ? c : new SqlCheckConstraintIR(c)));
freezeNode(this);
}
get id() {
return this.name;
}
isEqualTo(other) {
return this.id === other.id;
}
children() {
return [
...Object.values(this.columns),
...this.primaryKey ? [this.primaryKey] : [],
...this.foreignKeys,
...this.uniques,
...this.indexes,
...this.checks ?? []
];
}
};
//#endregion
//#region src/ir/sql-schema-ir.ts
/**
* Root Schema IR node representing the complete database schema as
* observed by introspection. Target-agnostic; used by both verifiers
* (compare against intended Contract storage) and migration planners
* (derive operations needed to reconcile).
*
* The constructor normalises nested `SqlTableIR` instances so
* downstream walks see a uniform AST regardless of whether the input
* was a plain-data literal or already-constructed class instances.
*
* Implements `DiffableNode` as the root of a flat (single-schema) diff
* tree: `id` is the fixed sentinel `'database'` (roots have no siblings),
* `isEqualTo` is identity (a container has no own attributes), and
* `children()` yields the table nodes.
*/
var SqlSchemaIR = class extends SqlSchemaIRNode {
nodeKind = RelationalSchemaNodeKind.schema;
tables;
constructor(input) {
super();
this.tables = Object.freeze(Object.fromEntries(Object.entries(input.tables).map(([key, t]) => [key, t instanceof SqlTableIR ? t : new SqlTableIR(t)])));
if (input.annotations !== void 0) this.annotations = input.annotations;
freezeNode(this);
}
get id() {
return "database";
}
isEqualTo(other) {
return this.id === other.id;
}
children() {
return Object.values(this.tables);
}
};
//#endregion
export { SqlForeignKeyIR as a, SqlCheckConstraintIR as c, assertNode as d, defineNonEnumerable as f, relationalNodeGranularity as h, SqlIndexIR as i, PrimaryKey as l, relationalNodeEntityKind as m, SqlTableIR as n, SqlColumnIR as o, RelationalSchemaNodeKind as p, SqlUniqueIR as r, SqlColumnDefaultIR as s, SqlSchemaIR as t, SqlSchemaIRNode as u };
//# sourceMappingURL=types-Btj35AIt.mjs.map

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