@prisma-next/target-sqlite 0.14.0-dev.49 → 0.14.0-dev.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/control.mjs +6 -18
  2. package/dist/control.mjs.map +1 -1
  3. package/dist/migration.mjs +2 -2
  4. package/dist/native-type-normalizer.mjs +13 -1
  5. package/dist/native-type-normalizer.mjs.map +1 -0
  6. package/dist/{op-factory-call-CMBTpb4M.mjs → op-factory-call-D_eYsHTp.mjs} +82 -65
  7. package/dist/op-factory-call-D_eYsHTp.mjs.map +1 -0
  8. package/dist/op-factory-call.mjs +1 -1
  9. package/dist/planner-DhvFrgPT.mjs +854 -0
  10. package/dist/planner-DhvFrgPT.mjs.map +1 -0
  11. package/dist/{planner-produced-sqlite-migration-BSjMKJhI.mjs → planner-produced-sqlite-migration-DyRmcteq.mjs} +2 -2
  12. package/dist/{planner-produced-sqlite-migration-BSjMKJhI.mjs.map → planner-produced-sqlite-migration-DyRmcteq.mjs.map} +1 -1
  13. package/dist/planner-produced-sqlite-migration.mjs +1 -1
  14. package/dist/planner.d.mts +46 -10
  15. package/dist/planner.d.mts.map +1 -1
  16. package/dist/planner.mjs +1 -1
  17. package/dist/{sqlite-migration-BdT_3SlM.mjs → sqlite-migration-D93wCdSD.mjs} +2 -2
  18. package/dist/{sqlite-migration-BdT_3SlM.mjs.map → sqlite-migration-D93wCdSD.mjs.map} +1 -1
  19. package/package.json +18 -18
  20. package/src/core/control-target.ts +9 -22
  21. package/src/core/migrations/column-ddl-rendering.ts +177 -0
  22. package/src/core/migrations/diff-database-schema.ts +197 -31
  23. package/src/core/migrations/issue-planner.ts +359 -488
  24. package/src/core/migrations/operations/tables.ts +107 -67
  25. package/src/core/migrations/planner-strategies.ts +118 -137
  26. package/src/core/migrations/planner.ts +112 -37
  27. package/src/core/sqlite-schema-verifier.ts +6 -3
  28. package/dist/native-type-normalizer-CiSyVmMP.mjs +0 -14
  29. package/dist/native-type-normalizer-CiSyVmMP.mjs.map +0 -1
  30. package/dist/op-factory-call-CMBTpb4M.mjs.map +0 -1
  31. package/dist/planner-EoQcJGzv.mjs +0 -676
  32. package/dist/planner-EoQcJGzv.mjs.map +0 -1
@@ -0,0 +1,177 @@
1
+ import type { StorageColumn } from '@prisma-next/sql-contract/types';
2
+ import {
3
+ DdlColumn,
4
+ type DdlTableConstraint,
5
+ ForeignKeyConstraint,
6
+ FunctionColumnDefault,
7
+ LiteralColumnDefault,
8
+ PrimaryKeyConstraint,
9
+ UniqueConstraint,
10
+ } from '@prisma-next/sql-relational-core/ast';
11
+ import type { SqlColumnIR, SqlTableIR } from '@prisma-next/sql-schema-ir/types';
12
+ import { blindCast } from '@prisma-next/utils/casts';
13
+ import type { SqliteColumnSpec } from './operations/shared';
14
+ import { buildColumnDefaultSql, buildColumnTypeSql } from './planner-ddl-builders';
15
+
16
+ /**
17
+ * Reconstructs the `StorageColumn`-shaped fields `buildColumnTypeSql` /
18
+ * `buildColumnDefaultSql` expect, from a column node's own stamped codec
19
+ * identity (`codecRef` / `codecBaseNativeType`, Decision 5) — never the
20
+ * contract. SQLite's type renderer only uppercases the resolved base type
21
+ * (no parameterized expansion, no named-type quoting), so `typeRef` is
22
+ * deliberately left unset here: setting it would send `buildColumnTypeSql`
23
+ * back through a live `storageTypes` lookup the node's fields have already
24
+ * resolved past, which throws for an unrecognized reference (unlike
25
+ * Postgres's lenient fallback).
26
+ */
27
+ function columnLike(
28
+ column: SqlColumnIR,
29
+ ): Pick<StorageColumn, 'nativeType' | 'codecId' | 'nullable' | 'many' | 'typeParams' | 'default'> {
30
+ if (column.codecRef === undefined || column.codecBaseNativeType === undefined) {
31
+ throw new Error(
32
+ `columnLike: expected column "${column.name}" carries no codec identity — the expected tree must be derived via contractToSchemaIR for planning`,
33
+ );
34
+ }
35
+ return {
36
+ nativeType: column.codecBaseNativeType,
37
+ codecId: column.codecRef.codecId,
38
+ nullable: column.nullable,
39
+ // `column.many` is unset on contract-derived columns (array-ness rides
40
+ // on the `nativeType` `[]` suffix there instead) — `codecRef.many`
41
+ // carries it. Hand-built/introspected columns set `column.many` directly.
42
+ ...((column.many ?? column.codecRef.many) !== undefined
43
+ ? { many: column.many ?? column.codecRef.many }
44
+ : {}),
45
+ ...(column.codecRef.typeParams !== undefined
46
+ ? {
47
+ typeParams: blindCast<
48
+ Record<string, unknown>,
49
+ 'CodecRef.typeParams is JsonValue-shaped; the DDL builders only ever read it as the Record the contract column originally carried'
50
+ >(column.codecRef.typeParams),
51
+ }
52
+ : {}),
53
+ ...(column.resolvedDefault !== undefined ? { default: column.resolvedDefault } : {}),
54
+ };
55
+ }
56
+
57
+ function sqliteDefaultToDdlColumnDefault(
58
+ columnDefault: StorageColumn['default'],
59
+ ): DdlColumn['default'] {
60
+ if (!columnDefault) return undefined;
61
+ switch (columnDefault.kind) {
62
+ case 'literal':
63
+ return new LiteralColumnDefault(columnDefault.value);
64
+ case 'function':
65
+ // `autoincrement()` is not a DEFAULT clause — SQLite encodes it as
66
+ // `INTEGER PRIMARY KEY AUTOINCREMENT` inline on the column. Skip it
67
+ // here; the renderer also has a defensive guard for the same case.
68
+ if (columnDefault.expression === 'autoincrement()') return undefined;
69
+ return new FunctionColumnDefault(columnDefault.expression);
70
+ default: {
71
+ const exhaustive: never = columnDefault;
72
+ throw new Error(
73
+ `sqliteDefaultToDdlColumnDefault: unhandled kind "${blindCast<{ kind: string }, 'exhaustiveness: surface the unhandled default kind'>(exhaustive).kind}"`,
74
+ );
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * True when the column is rendered inline as `INTEGER PRIMARY KEY
81
+ * AUTOINCREMENT` — the sole member of the table's primary key with an
82
+ * `autoincrement()` default. Node-based sibling of the retired
83
+ * `isInlineAutoincrementPrimaryKey` (which read the raw `StorageTable`);
84
+ * reads the table/column nodes instead.
85
+ */
86
+ export function isInlineAutoincrementPrimaryKeyNode(
87
+ table: SqlTableIR,
88
+ column: SqlColumnIR,
89
+ ): boolean {
90
+ if (table.primaryKey?.columns.length !== 1) return false;
91
+ if (table.primaryKey.columns[0] !== column.name) return false;
92
+ return (
93
+ column.resolvedDefault?.kind === 'function' &&
94
+ column.resolvedDefault.expression === 'autoincrement()'
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Builds the flat `SqliteColumnSpec` `AddColumnCall` / `RecreateTableCall`
100
+ * need, resolved from the column node's codec identity — the same builders
101
+ * the pre-`plan(start, end)` op-path called, so the output is
102
+ * byte-identical.
103
+ */
104
+ export function columnSpecFromNode(column: SqlColumnIR, inline: boolean): SqliteColumnSpec {
105
+ const like = columnLike(column);
106
+ const typeSql = buildColumnTypeSql(like, {});
107
+ const defaultSql = buildColumnDefaultSql(like.default);
108
+ return {
109
+ name: column.name,
110
+ typeSql,
111
+ defaultSql,
112
+ nullable: column.nullable,
113
+ ...(inline ? { inlineAutoincrementPrimaryKey: true } : {}),
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Builds the `DdlColumn` the `CreateTableCall` path needs, resolved from the
119
+ * column node's codec identity.
120
+ */
121
+ export function ddlColumnFromNode(column: SqlColumnIR, inline: boolean): DdlColumn {
122
+ const like = columnLike(column);
123
+ const typeSql = buildColumnTypeSql(like, {});
124
+ if (inline) {
125
+ // `DdlColumn` has no SQLite-specific autoincrement flag, so the full
126
+ // `PRIMARY KEY AUTOINCREMENT` clause is embedded in the `type` string.
127
+ // The DDL renderer (`ddl-renderer.ts`) substring-detects `AUTOINCREMENT`
128
+ // to suppress the normal NOT NULL / PRIMARY KEY / DEFAULT clause rendering
129
+ // and emit the entire type string verbatim. Both sites must stay in sync.
130
+ // The structural fix (a SQLite-specific column option) is tracked in TML-2866.
131
+ return new DdlColumn({ name: column.name, type: `${typeSql} PRIMARY KEY AUTOINCREMENT` });
132
+ }
133
+ const colDefault = sqliteDefaultToDdlColumnDefault(like.default);
134
+ return new DdlColumn({
135
+ name: column.name,
136
+ type: typeSql,
137
+ ...(!column.nullable ? { notNull: true } : {}),
138
+ ...(colDefault !== undefined ? { default: colDefault } : {}),
139
+ ...(column.codecRef !== undefined ? { codecRef: column.codecRef } : {}),
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Builds the table-level constraints (PK / unique / FK) for a `CreateTable`
145
+ * path from the table node — the node-sourced sibling of the retired
146
+ * contract-based `tableToDdlParts`'s constraint half.
147
+ */
148
+ export function tableConstraintsFromNode(
149
+ table: SqlTableIR,
150
+ hasInlinePk: boolean,
151
+ ): DdlTableConstraint[] {
152
+ const constraints: DdlTableConstraint[] = [];
153
+ if (table.primaryKey && !hasInlinePk) {
154
+ constraints.push(new PrimaryKeyConstraint({ columns: table.primaryKey.columns }));
155
+ }
156
+ for (const u of table.uniques) {
157
+ constraints.push(
158
+ new UniqueConstraint({
159
+ columns: u.columns,
160
+ ...(u.name !== undefined ? { name: u.name } : {}),
161
+ }),
162
+ );
163
+ }
164
+ for (const fk of table.foreignKeys) {
165
+ constraints.push(
166
+ new ForeignKeyConstraint({
167
+ columns: fk.columns,
168
+ refTable: fk.referencedTable,
169
+ refColumns: fk.referencedColumns,
170
+ ...(fk.name !== undefined ? { name: fk.name } : {}),
171
+ ...(fk.onDelete !== undefined ? { onDelete: fk.onDelete } : {}),
172
+ ...(fk.onUpdate !== undefined ? { onUpdate: fk.onUpdate } : {}),
173
+ }),
174
+ );
175
+ }
176
+ return constraints;
177
+ }
@@ -1,13 +1,28 @@
1
- import type { ColumnDefault, Contract } from '@prisma-next/contract/types';
2
- import { contractToSchemaIR } from '@prisma-next/family-sql/control';
3
- import { verifySqlSchemaTree } from '@prisma-next/family-sql/diff';
1
+ import type { ColumnDefault, Contract, ControlPolicy } from '@prisma-next/contract/types';
2
+ import type { NativeTypeExpander, SqlSchemaDiffResult } from '@prisma-next/family-sql/control';
3
+ import { buildNativeTypeExpander, contractToSchemaIR } from '@prisma-next/family-sql/control';
4
+ import {
5
+ neutralizeFlatExpectedFkSchemas,
6
+ normalizeFlatActualForDiff,
7
+ verifySqlSchemaByDiff,
8
+ } from '@prisma-next/family-sql/diff';
4
9
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
5
- import type { VerifyDatabaseSchemaResult } from '@prisma-next/framework-components/control';
6
- import { SchemaDiff } from '@prisma-next/framework-components/control';
7
- import type { SqlStorage, StorageColumn } from '@prisma-next/sql-contract/types';
8
- import type { SqlSchemaIR, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
9
- import { parseSqliteDefault } from '../default-normalizer';
10
- import { normalizeSqliteNativeType } from '../native-type-normalizer';
10
+ import type {
11
+ SchemaDiffIssue,
12
+ VerifyDatabaseSchemaResult,
13
+ } from '@prisma-next/framework-components/control';
14
+ import { diffSchemas } from '@prisma-next/framework-components/control';
15
+ import { entityAt } from '@prisma-next/framework-components/ir';
16
+ import type { SqlStorage, StorageColumn, StorageTable } from '@prisma-next/sql-contract/types';
17
+ import type {
18
+ SqlColumnIRInput,
19
+ SqlSchemaIRInput,
20
+ SqlSchemaIRNode,
21
+ SqlTableIRInput,
22
+ } from '@prisma-next/sql-schema-ir/types';
23
+ import { relationalNodeGranularity, SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
24
+ import { blindCast } from '@prisma-next/utils/casts';
25
+ import { ifDefined } from '@prisma-next/utils/defined';
11
26
  import { renderDefaultLiteral } from './planner-ddl-builders';
12
27
 
13
28
  interface SqliteDiffDatabaseSchemaInput {
@@ -29,46 +44,197 @@ export function sqliteRenderDefault(def: ColumnDefault, _column: StorageColumn):
29
44
  return renderDefaultLiteral(def.value);
30
45
  }
31
46
 
32
- /** The SQLite expected-side projection: contract → flat relational schema IR. */
33
- export function sqliteContractToSchema(contract: Contract<SqlStorage> | null): SqlSchemaIR {
47
+ /**
48
+ * The SQLite expected-side projection: contract → flat relational schema IR.
49
+ *
50
+ * `extras` thread the plan-time derivation input: the native-type expander,
51
+ * so the expected side carries resolved native types (like the verify
52
+ * side). Every expected column also carries its `codecRef` unconditionally
53
+ * (Decision 5) — the planner's op-builders resolve DDL rendering from it at
54
+ * plan time, so no separate render stamper is threaded here.
55
+ */
56
+ export function sqliteContractToSchema(
57
+ contract: Contract<SqlStorage> | null,
58
+ extras?: {
59
+ readonly expandNativeType?: NativeTypeExpander;
60
+ },
61
+ ): SqlSchemaIR {
34
62
  return contractToSchemaIR(contract, {
35
63
  annotationNamespace: 'sqlite',
36
64
  renderDefault: sqliteRenderDefault,
65
+ ...ifDefined('expandNativeType', extras?.expandNativeType),
37
66
  });
38
67
  }
39
68
 
40
- function computeSqliteSchemaComparison(
69
+ /**
70
+ * The SQLite schema verify: the full-tree node-diff verdict wrapped in the
71
+ * issue-based result envelope. Used by the runner's post-apply check; the
72
+ * family `verifySchema` runs the same composition via the descriptor hook.
73
+ */
74
+ export function verifySqliteDatabaseSchema(
41
75
  input: SqliteDiffDatabaseSchemaInput,
42
76
  ): VerifyDatabaseSchemaResult {
43
- return verifySqlSchemaTree({
77
+ return verifySqlSchemaByDiff({
44
78
  contract: input.contract,
45
- actualSchema: input.actualSchema,
46
- buildExpectedSchema: sqliteContractToSchema,
79
+ schema: input.actualSchema,
47
80
  strict: input.strict,
48
- typeMetadataRegistry: input.typeMetadataRegistry,
49
81
  frameworkComponents: input.frameworkComponents,
50
- normalizeDefault: parseSqliteDefault,
51
- normalizeNativeType: normalizeSqliteNativeType,
82
+ diffSchema: diffSqliteSchema,
83
+ granularityOf: relationalNodeGranularity,
52
84
  });
53
85
  }
54
86
 
55
87
  /**
56
- * The SQLite `SchemaDiffer` relational only. SQLite has a single flat
57
- * schema and no structural (policy) diff, so it runs the shared per-schema
58
- * relational diff and returns no `schemaDiffIssues`.
88
+ * Resolves a verdict-diff issue's subject table's declared control policy
89
+ * directly from the contract. SQLite's expected tree is flat, so the issue
90
+ * path carries no namespace segment `path[1]` is the table name (`path[0]`
91
+ * is the tree root's own id); every contract namespace is searched for that
92
+ * table name (table names are globally unique in the flat tree — duplicates
93
+ * across namespaces are rejected earlier, at `contractToSchemaIR`).
59
94
  */
60
- export function diffSqliteDatabaseSchema(input: SqliteDiffDatabaseSchemaInput): SchemaDiff {
61
- const relational = computeSqliteSchemaComparison(input);
62
- return new SchemaDiff(relational.schema.issues, relational.schema.schemaDiffIssues);
95
+ function resolveControlPolicy(
96
+ issue: SchemaDiffIssue,
97
+ contract: Contract<SqlStorage>,
98
+ ): ControlPolicy | undefined {
99
+ const tableName = issue.path[1];
100
+ if (tableName === undefined) return undefined;
101
+ for (const namespaceId of Object.keys(contract.storage.namespaces)) {
102
+ const table = entityAt<StorageTable>(contract.storage, {
103
+ namespaceId,
104
+ entityKind: 'table',
105
+ entityName: tableName,
106
+ });
107
+ if (table !== undefined) return table.control;
108
+ }
109
+ return undefined;
63
110
  }
64
111
 
65
112
  /**
66
- * The same comparison as {@link diffSqliteDatabaseSchema}, wrapped in the
67
- * verify envelope (`ok`/`summary`/`code`/`target`/`timings`) plus the
68
- * pass/warn/fail tree the CLI renders.
113
+ * The SQLite full-tree node diff for the family verify verdict: derive the
114
+ * expected flat tree with resolved leaf values (expander threaded so
115
+ * parameterized types compare expanded), neutralize the FK schema segment
116
+ * (single-schema target — introspection stamps none), normalize the actual
117
+ * tree for semantic satisfaction, and run the generic differ. Flat targets
118
+ * need no ownership scoping. The codec `verifyType` hooks run once per
119
+ * contract namespace with tables, each against the sole flat actual root —
120
+ * exactly the legacy per-namespace pairing.
69
121
  */
70
- export function verifySqliteDatabaseSchema(
71
- input: SqliteDiffDatabaseSchemaInput,
72
- ): VerifyDatabaseSchemaResult {
73
- return computeSqliteSchemaComparison(input);
122
+ export function diffSqliteSchema(input: {
123
+ readonly contract: Contract<SqlStorage>;
124
+ readonly schema: SqlSchemaIRNode;
125
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
126
+ }): SqlSchemaDiffResult {
127
+ const expandNativeType = buildNativeTypeExpander(input.frameworkComponents);
128
+ const expected = neutralizeFlatExpectedFkSchemas(
129
+ contractToSchemaIR(input.contract, {
130
+ annotationNamespace: 'sqlite',
131
+ renderDefault: sqliteRenderDefault,
132
+ ...ifDefined('expandNativeType', expandNativeType),
133
+ }),
134
+ );
135
+ const actual =
136
+ input.schema instanceof SqlSchemaIR
137
+ ? input.schema
138
+ : blindCast<
139
+ SqlSchemaIR,
140
+ 'the SQLite introspection adapter always produces a flat SqlSchemaIR root'
141
+ >(input.schema);
142
+ const normalizedActual = normalizeFlatActualForDiff(expected, actual);
143
+ const issues = diffSchemas(expected, normalizedActual);
144
+ const namespacesWithTables = Object.values(input.contract.storage.namespaces).filter(
145
+ (ns) => Object.keys(ns.entries.table ?? {}).length > 0,
146
+ );
147
+ return {
148
+ issues,
149
+ resolveControlPolicy: (issue) => resolveControlPolicy(issue, input.contract),
150
+ namespacePairs: namespacesWithTables.map(() => ({ actual })),
151
+ };
152
+ }
153
+
154
+ export interface SqlitePlanDiff {
155
+ /** The desired ("end") tree — resolved leaf values, incl. `codecRef`, on every column. */
156
+ readonly expected: SqlSchemaIR;
157
+ /** The live ("start") tree, normalized for semantic satisfaction against `expected`. */
158
+ readonly actual: SqlSchemaIR;
159
+ readonly issues: readonly SchemaDiffIssue[];
160
+ }
161
+
162
+ /**
163
+ * The SQLite planner's diff input: the same tree-building
164
+ * `diffSqliteSchema` uses (expander threaded, FK schema segment
165
+ * neutralized, actual tree normalized for semantic satisfaction). One differ
166
+ * drives both verify and plan; this is the plan-side derivation — column DDL
167
+ * resolves from each expected column's `codecRef` at plan time
168
+ * (`column-ddl-rendering.ts`), so no separate render stamping happens here.
169
+ */
170
+ export function buildSqlitePlanDiff(input: {
171
+ readonly contract: Contract<SqlStorage>;
172
+ readonly actualSchema: SqlSchemaIRNode;
173
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
174
+ }): SqlitePlanDiff {
175
+ const expandNativeType = buildNativeTypeExpander(input.frameworkComponents);
176
+ const expected = neutralizeFlatExpectedFkSchemas(
177
+ sqliteContractToSchema(input.contract, {
178
+ ...ifDefined('expandNativeType', expandNativeType),
179
+ }),
180
+ );
181
+ // The differ dispatches polymorphically (`.isEqualTo()` / `.children()`), so
182
+ // the actual tree must be genuine `SqlSchemaIR`/`SqlTableIR`/`SqlColumnIR`
183
+ // instances, not plain data shaped like them. `new SqlSchemaIR(...)`
184
+ // normalizes either input uniformly (an already-real tree passes through
185
+ // untouched — its nested values are already instances) and is a no-op
186
+ // rebuild in the common (real-instance) case, so this is always safe to run.
187
+ const actualRaw = new SqlSchemaIR(withRecordKeyNames(input.actualSchema));
188
+ const actual = normalizeFlatActualForDiff(expected, actualRaw);
189
+ const issues = diffSchemas(expected, actual);
190
+ return { expected, actual, issues };
191
+ }
192
+
193
+ /**
194
+ * Every schema-tree builder in this codebase derives a table's / column's
195
+ * `name` from the record key it's stored under (`contractToSchemaIR`,
196
+ * the SQLite introspection adapter) rather than trusting a redundant
197
+ * embedded field — the record key IS the identity. Mirrors that discipline
198
+ * for the actual/live tree before construction, so `SqlTableIR.id` /
199
+ * `SqlColumnIR.id` (both derived from `.name`) are always correct without
200
+ * requiring every caller to duplicate the key onto the value. A no-op for a
201
+ * tree that already carries matching names (the real introspection adapter
202
+ * always does).
203
+ */
204
+ function withRecordKeyNames(actualSchema: SqlSchemaIRNode): SqlSchemaIRInput {
205
+ const raw = blindCast<
206
+ { readonly tables?: Readonly<Record<string, unknown>> },
207
+ 'the SQLite introspection adapter always produces a flat, tables-keyed root'
208
+ >(actualSchema);
209
+ const tables: Record<string, SqlTableIRInput> = {};
210
+ for (const [tableName, table] of Object.entries(raw.tables ?? {})) {
211
+ const rawTable = blindCast<
212
+ Omit<SqlTableIRInput, 'name' | 'columns'>,
213
+ 'every table value in a tables record is SqlTableIR(Input)-shaped'
214
+ >(table);
215
+ const columns: Record<string, SqlColumnIRInput> = {};
216
+ for (const [columnName, column] of Object.entries(
217
+ blindCast<
218
+ { readonly columns?: Readonly<Record<string, unknown>> },
219
+ 'every SqlTableIR(Input) carries a columns record keyed by column name'
220
+ >(table).columns ?? {},
221
+ )) {
222
+ columns[columnName] = {
223
+ ...blindCast<
224
+ SqlColumnIRInput,
225
+ 'every column value in a columns record is SqlColumnIR(Input)-shaped'
226
+ >(column),
227
+ name: columnName,
228
+ };
229
+ }
230
+ tables[tableName] = {
231
+ ...rawTable,
232
+ name: tableName,
233
+ columns,
234
+ foreignKeys: rawTable.foreignKeys ?? [],
235
+ uniques: rawTable.uniques ?? [],
236
+ indexes: rawTable.indexes ?? [],
237
+ };
238
+ }
239
+ return { tables };
74
240
  }