@prisma-next/target-sqlite 0.13.0-dev.13 → 0.13.0-dev.14

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 (60) hide show
  1. package/dist/contract-free.mjs +1 -20
  2. package/dist/contract-free.mjs.map +1 -1
  3. package/dist/control.mjs +8 -8
  4. package/dist/control.mjs.map +1 -1
  5. package/dist/ddl-CH8V_qcd.mjs +23 -0
  6. package/dist/ddl-CH8V_qcd.mjs.map +1 -0
  7. package/dist/migration.d.mts +3 -3
  8. package/dist/migration.d.mts.map +1 -1
  9. package/dist/migration.mjs +4 -3
  10. package/dist/migration.mjs.map +1 -1
  11. package/dist/{tables-CjB7vXCr.mjs → op-factory-call-Cc-rDnXB.mjs} +370 -17
  12. package/dist/op-factory-call-Cc-rDnXB.mjs.map +1 -0
  13. package/dist/op-factory-call.d.mts +16 -12
  14. package/dist/op-factory-call.d.mts.map +1 -1
  15. package/dist/op-factory-call.mjs +1 -1
  16. package/dist/{planner-DSNDwQy9.mjs → planner-D-scZdGe.mjs} +71 -10
  17. package/dist/planner-D-scZdGe.mjs.map +1 -0
  18. package/dist/{planner-produced-sqlite-migration-C1yqJAiM.d.mts → planner-produced-sqlite-migration-DcrsKVHA.d.mts} +4 -3
  19. package/dist/planner-produced-sqlite-migration-DcrsKVHA.d.mts.map +1 -0
  20. package/dist/{planner-produced-sqlite-migration-DowV_vHw.mjs → planner-produced-sqlite-migration-WsWCQb58.mjs} +7 -5
  21. package/dist/planner-produced-sqlite-migration-WsWCQb58.mjs.map +1 -0
  22. package/dist/planner-produced-sqlite-migration.d.mts +1 -1
  23. package/dist/planner-produced-sqlite-migration.mjs +1 -1
  24. package/dist/planner.d.mts +5 -2
  25. package/dist/planner.d.mts.map +1 -1
  26. package/dist/planner.mjs +1 -1
  27. package/dist/render-ops-D2IuTDVc.mjs +9 -0
  28. package/dist/render-ops-D2IuTDVc.mjs.map +1 -0
  29. package/dist/render-ops.d.mts +2 -1
  30. package/dist/render-ops.d.mts.map +1 -1
  31. package/dist/render-ops.mjs +1 -1
  32. package/dist/runtime.mjs +1 -1
  33. package/dist/{sqlite-contract-serializer-jcRu8aHh.mjs → sqlite-contract-serializer-C41PO7DT.mjs} +2 -2
  34. package/dist/{sqlite-contract-serializer-jcRu8aHh.mjs.map → sqlite-contract-serializer-C41PO7DT.mjs.map} +1 -1
  35. package/dist/sqlite-migration-CteI6Mrg.mjs +73 -0
  36. package/dist/sqlite-migration-CteI6Mrg.mjs.map +1 -0
  37. package/dist/sqlite-migration-znjM-dc_.d.mts +46 -0
  38. package/dist/sqlite-migration-znjM-dc_.d.mts.map +1 -0
  39. package/package.json +18 -18
  40. package/src/core/control-target.ts +4 -4
  41. package/src/core/errors.ts +28 -0
  42. package/src/core/migrations/issue-planner.ts +114 -2
  43. package/src/core/migrations/op-factory-call.ts +155 -23
  44. package/src/core/migrations/planner-produced-sqlite-migration.ts +5 -1
  45. package/src/core/migrations/planner.ts +12 -2
  46. package/src/core/migrations/render-ops.ts +10 -2
  47. package/src/core/migrations/sqlite-migration.ts +51 -1
  48. package/src/exports/migration.ts +9 -1
  49. package/dist/op-factory-call-DymqdXQW.mjs +0 -279
  50. package/dist/op-factory-call-DymqdXQW.mjs.map +0 -1
  51. package/dist/planner-DSNDwQy9.mjs.map +0 -1
  52. package/dist/planner-produced-sqlite-migration-C1yqJAiM.d.mts.map +0 -1
  53. package/dist/planner-produced-sqlite-migration-DowV_vHw.mjs.map +0 -1
  54. package/dist/render-ops-CFRbJ3Yb.mjs +0 -8
  55. package/dist/render-ops-CFRbJ3Yb.mjs.map +0 -1
  56. package/dist/sqlite-migration-CUqgmzQH.mjs +0 -16
  57. package/dist/sqlite-migration-CUqgmzQH.mjs.map +0 -1
  58. package/dist/sqlite-migration-D4XGYzgQ.d.mts +0 -17
  59. package/dist/sqlite-migration-D4XGYzgQ.d.mts.map +0 -1
  60. package/dist/tables-CjB7vXCr.mjs.map +0 -1
@@ -2,13 +2,11 @@
2
2
  * SQLite migration IR: one concrete `*Call` class per pure factory under
3
3
  * `operations/`, plus a shared `SqliteOpFactoryCallNode` abstract base.
4
4
  *
5
- * Each call class carries fully-resolved literal arguments (flat
6
- * `SqliteColumnSpec` / `SqliteTableSpec` etc.) codec / `typeRef` / default
7
- * expansion happens upstream in the issue-planner / strategies, mirroring
8
- * the Postgres `ColumnSpec` pattern. As a result, `toOp()` and
9
- * `renderTypeScript()` both pass the same flat data through; the rendered
10
- * TypeScript scaffold is fully self-contained and does not need access to a
11
- * `storageTypes` map at runtime.
5
+ * Each call class carries fully-resolved literal arguments. `CreateTableCall`
6
+ * holds structured `DdlColumn[]` + `DdlTableConstraint[]` and lowers via the
7
+ * adapter's DDL path; other call classes carry flat SQL fragments. Codec /
8
+ * `typeRef` / default expansion happens upstream in the issue-planner /
9
+ * strategies, mirroring the Postgres `ColumnSpec` pattern.
12
10
  */
13
11
 
14
12
  import { errorUnfilledPlaceholder } from '@prisma-next/errors/migration';
@@ -16,13 +14,23 @@ import type {
16
14
  MigrationOperationClass,
17
15
  SqlMigrationPlanOperation,
18
16
  } from '@prisma-next/family-sql/control';
17
+ import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
19
18
  import type { OpFactoryCall as FrameworkOpFactoryCall } from '@prisma-next/framework-components/control';
19
+ import type {
20
+ AnyDdlColumnDefault,
21
+ DdlColumn,
22
+ DdlTableConstraint,
23
+ } from '@prisma-next/sql-relational-core/ast';
20
24
  import { type ImportRequirement, jsonToTsSource, TsExpression } from '@prisma-next/ts-render';
25
+ import * as contractFreeDdl from '../../contract-free/ddl';
26
+ import { escapeLiteral } from '../sql-utils';
21
27
  import { addColumn, dropColumn } from './operations/columns';
22
28
  import { createIndex, dropIndex } from './operations/indexes';
23
29
  import type { SqliteColumnSpec, SqliteIndexSpec, SqliteTableSpec } from './operations/shared';
24
- import { createTable, dropTable, recreateTable } from './operations/tables';
30
+ import { step } from './operations/shared';
31
+ import { dropTable, recreateTable } from './operations/tables';
25
32
  import type { SqlitePlanTargetDetails } from './planner-target-details';
33
+ import { buildTargetDetails } from './planner-target-details';
26
34
 
27
35
  type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
28
36
 
@@ -32,7 +40,7 @@ abstract class SqliteOpFactoryCallNode extends TsExpression implements Framework
32
40
  abstract readonly factoryName: string;
33
41
  abstract readonly operationClass: MigrationOperationClass;
34
42
  abstract readonly label: string;
35
- abstract toOp(): Op;
43
+ abstract toOp(lowerer?: Lowerer): Op;
36
44
 
37
45
  importRequirements(): readonly ImportRequirement[] {
38
46
  return [{ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: this.factoryName }];
@@ -47,27 +55,151 @@ abstract class SqliteOpFactoryCallNode extends TsExpression implements Framework
47
55
  // Table
48
56
  // ============================================================================
49
57
 
58
+ // ---------------------------------------------------------------------------
59
+ // TypeScript rendering helpers for DdlColumn / DdlTableConstraint
60
+ // ---------------------------------------------------------------------------
61
+
62
+ function renderDdlColumnDefault(def: AnyDdlColumnDefault | undefined): string {
63
+ if (!def) return '';
64
+ if (def.kind === 'literal') {
65
+ return `lit(${jsonToTsSource(def.value)})`;
66
+ }
67
+ return `fn(${jsonToTsSource(def.expression)})`;
68
+ }
69
+
70
+ function renderDdlColumnAsTsCall(column: DdlColumn): string {
71
+ const opts: string[] = [];
72
+ if (column.notNull) opts.push('notNull: true');
73
+ if (column.primaryKey) opts.push('primaryKey: true');
74
+ if (column.default) opts.push(`default: ${renderDdlColumnDefault(column.default)}`);
75
+ const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
76
+ return `col(${jsonToTsSource(column.name)}, ${jsonToTsSource(column.type)}${optsStr})`;
77
+ }
78
+
79
+ function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
80
+ switch (constraint.kind) {
81
+ case 'primary-key': {
82
+ const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
83
+ return `primaryKey(${jsonToTsSource(constraint.columns)}${nameOpt})`;
84
+ }
85
+ case 'foreign-key': {
86
+ const opts: string[] = [];
87
+ if (constraint.name) opts.push(`name: ${jsonToTsSource(constraint.name)}`);
88
+ if (constraint.onDelete) opts.push(`onDelete: ${jsonToTsSource(constraint.onDelete)}`);
89
+ if (constraint.onUpdate) opts.push(`onUpdate: ${jsonToTsSource(constraint.onUpdate)}`);
90
+ const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
91
+ return `foreignKey(${jsonToTsSource(constraint.columns)}, ${jsonToTsSource(constraint.refTable)}, ${jsonToTsSource(constraint.refColumns)}${optsStr})`;
92
+ }
93
+ case 'unique': {
94
+ const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
95
+ return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
96
+ }
97
+ }
98
+ }
99
+
100
+ function constraintImportSymbols(constraints: readonly DdlTableConstraint[] | undefined): string[] {
101
+ if (!constraints || constraints.length === 0) return [];
102
+ const symbols = new Set<string>();
103
+ for (const c of constraints) {
104
+ if (c.kind === 'primary-key') symbols.add('primaryKey');
105
+ else if (c.kind === 'foreign-key') symbols.add('foreignKey');
106
+ else if (c.kind === 'unique') symbols.add('unique');
107
+ }
108
+ return [...symbols];
109
+ }
110
+
111
+ function defaultImportSymbols(columns: readonly DdlColumn[]): string[] {
112
+ const symbols = new Set<string>();
113
+ for (const col of columns) {
114
+ if (col.default?.kind === 'literal') symbols.add('lit');
115
+ else if (col.default?.kind === 'function') symbols.add('fn');
116
+ }
117
+ return [...symbols];
118
+ }
119
+
50
120
  export class CreateTableCall extends SqliteOpFactoryCallNode {
51
121
  readonly factoryName = 'createTable' as const;
52
122
  readonly operationClass = 'additive' as const;
53
123
  readonly tableName: string;
54
- readonly spec: SqliteTableSpec;
124
+ readonly columns: readonly DdlColumn[];
125
+ readonly constraints: readonly DdlTableConstraint[] | undefined;
55
126
  readonly label: string;
56
127
 
57
- constructor(tableName: string, spec: SqliteTableSpec) {
128
+ constructor(
129
+ tableName: string,
130
+ columns: readonly DdlColumn[],
131
+ constraints?: readonly DdlTableConstraint[],
132
+ ) {
58
133
  super();
59
134
  this.tableName = tableName;
60
- this.spec = spec;
135
+ this.columns = Object.freeze([...columns]);
136
+ this.constraints = constraints ? Object.freeze([...constraints]) : undefined;
61
137
  this.label = `Create table ${tableName}`;
62
138
  this.freeze();
63
139
  }
64
140
 
65
- toOp(): Op {
66
- return createTable(this.tableName, this.spec);
141
+ toOp(lowerer?: Lowerer): Op {
142
+ if (lowerer === undefined) {
143
+ throw new Error(
144
+ `CreateTableCall.toOp: a DDL lowerer is required on the SQLite planner path (table "${this.tableName}"). Pass the control adapter to createSqliteMigrationPlanner.`,
145
+ );
146
+ }
147
+ const ddlNode = contractFreeDdl.createTable({
148
+ table: this.tableName,
149
+ columns: this.columns,
150
+ ...(this.constraints ? { constraints: this.constraints } : {}),
151
+ });
152
+ const { sql } = lowerer.lower(ddlNode, { contract: {} });
153
+ const tableName = this.tableName;
154
+ const escapedName = escapeLiteral(tableName);
155
+ return {
156
+ id: `table.${tableName}`,
157
+ label: `Create table ${tableName}`,
158
+ summary: `Creates table ${tableName} with required columns`,
159
+ operationClass: 'additive',
160
+ target: { id: 'sqlite', details: buildTargetDetails('table', tableName) },
161
+ precheck: [
162
+ step(
163
+ `ensure table "${tableName}" does not exist`,
164
+ `SELECT COUNT(*) = 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapedName}'`,
165
+ ),
166
+ ],
167
+ execute: [step(`create table "${tableName}"`, sql)],
168
+ postcheck: [
169
+ step(
170
+ `verify table "${tableName}" exists`,
171
+ `SELECT COUNT(*) > 0 FROM sqlite_master WHERE type = 'table' AND name = '${escapedName}'`,
172
+ ),
173
+ ],
174
+ };
67
175
  }
68
176
 
69
177
  renderTypeScript(): string {
70
- return `createTable(${jsonToTsSource(this.tableName)}, ${jsonToTsSource(this.spec)})`;
178
+ const columnsList = this.columns.map(renderDdlColumnAsTsCall).join(', ');
179
+ const constraintsList = this.constraints
180
+ ? this.constraints.map(renderDdlConstraintAsTsCall).join(', ')
181
+ : undefined;
182
+
183
+ const opts: string[] = [];
184
+ opts.push(`table: ${jsonToTsSource(this.tableName)}`);
185
+ opts.push(`columns: [${columnsList}]`);
186
+ if (constraintsList) opts.push(`constraints: [${constraintsList}]`);
187
+
188
+ return `this.createTable({ ${opts.join(', ')} })`;
189
+ }
190
+
191
+ override importRequirements(): readonly ImportRequirement[] {
192
+ const req: ImportRequirement[] = [];
193
+ if (this.columns.length > 0) {
194
+ req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: 'col' });
195
+ for (const sym of defaultImportSymbols(this.columns)) {
196
+ req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: sym });
197
+ }
198
+ }
199
+ for (const sym of constraintImportSymbols(this.constraints)) {
200
+ req.push({ moduleSpecifier: TARGET_MIGRATION_MODULE, symbol: sym });
201
+ }
202
+ return req;
71
203
  }
72
204
  }
73
205
 
@@ -84,7 +216,7 @@ export class DropTableCall extends SqliteOpFactoryCallNode {
84
216
  this.freeze();
85
217
  }
86
218
 
87
- toOp(): Op {
219
+ toOp(_lowerer?: Lowerer): Op {
88
220
  return dropTable(this.tableName);
89
221
  }
90
222
 
@@ -125,7 +257,7 @@ export class RecreateTableCall extends SqliteOpFactoryCallNode {
125
257
  this.freeze();
126
258
  }
127
259
 
128
- toOp(): Op {
260
+ toOp(_lowerer?: Lowerer): Op {
129
261
  return recreateTable({
130
262
  tableName: this.tableName,
131
263
  contractTable: this.contractTable,
@@ -172,7 +304,7 @@ export class AddColumnCall extends SqliteOpFactoryCallNode {
172
304
  this.freeze();
173
305
  }
174
306
 
175
- toOp(): Op {
307
+ toOp(_lowerer?: Lowerer): Op {
176
308
  return addColumn(this.tableName, this.column);
177
309
  }
178
310
 
@@ -196,7 +328,7 @@ export class DropColumnCall extends SqliteOpFactoryCallNode {
196
328
  this.freeze();
197
329
  }
198
330
 
199
- toOp(): Op {
331
+ toOp(_lowerer?: Lowerer): Op {
200
332
  return dropColumn(this.tableName, this.columnName);
201
333
  }
202
334
 
@@ -226,7 +358,7 @@ export class CreateIndexCall extends SqliteOpFactoryCallNode {
226
358
  this.freeze();
227
359
  }
228
360
 
229
- toOp(): Op {
361
+ toOp(_lowerer?: Lowerer): Op {
230
362
  return createIndex(this.tableName, this.indexName, this.columns);
231
363
  }
232
364
 
@@ -250,7 +382,7 @@ export class DropIndexCall extends SqliteOpFactoryCallNode {
250
382
  this.freeze();
251
383
  }
252
384
 
253
- toOp(): Op {
385
+ toOp(_lowerer?: Lowerer): Op {
254
386
  return dropIndex(this.tableName, this.indexName);
255
387
  }
256
388
 
@@ -292,7 +424,7 @@ export class DataTransformCall extends SqliteOpFactoryCallNode {
292
424
  this.freeze();
293
425
  }
294
426
 
295
- toOp(): Op {
427
+ toOp(_lowerer?: Lowerer): Op {
296
428
  throw errorUnfilledPlaceholder(this.label);
297
429
  }
298
430
 
@@ -344,7 +476,7 @@ export class RawSqlCall extends SqliteOpFactoryCallNode {
344
476
  this.freeze();
345
477
  }
346
478
 
347
- toOp(): Op {
479
+ toOp(_lowerer?: Lowerer): Op {
348
480
  return this.op;
349
481
  }
350
482
 
@@ -1,4 +1,5 @@
1
1
  import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
+ import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
2
3
  import type {
3
4
  MigrationPlanWithAuthoringSurface,
4
5
  OpFactoryCall,
@@ -24,22 +25,25 @@ export class TypeScriptRenderableSqliteMigration
24
25
  readonly #meta: MigrationMeta;
25
26
  readonly #destination: SqliteMigrationDestinationInfo;
26
27
  readonly #spaceId: string;
28
+ readonly #lowerer: Lowerer | undefined;
27
29
 
28
30
  constructor(
29
31
  calls: readonly OpFactoryCall[],
30
32
  meta: MigrationMeta,
31
33
  spaceId: string,
32
34
  destination?: SqliteMigrationDestinationInfo,
35
+ lowerer?: Lowerer,
33
36
  ) {
34
37
  super();
35
38
  this.#calls = calls;
36
39
  this.#meta = meta;
37
40
  this.#spaceId = spaceId;
38
41
  this.#destination = destination ?? { storageHash: meta.to };
42
+ this.#lowerer = lowerer;
39
43
  }
40
44
 
41
45
  override get operations(): readonly Op[] {
42
- return renderOps(this.#calls);
46
+ return renderOps(this.#calls, this.#lowerer);
43
47
  }
44
48
 
45
49
  override describe(): MigrationMeta {
@@ -10,6 +10,7 @@ import {
10
10
  planFieldEventOperations,
11
11
  plannerFailure,
12
12
  } from '@prisma-next/family-sql/control';
13
+ import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
13
14
  import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
14
15
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
15
16
  import type {
@@ -27,8 +28,8 @@ import {
27
28
  import { sqlitePlannerStrategies } from './planner-strategies';
28
29
  import type { SqlitePlanTargetDetails } from './planner-target-details';
29
30
 
30
- export function createSqliteMigrationPlanner(): SqliteMigrationPlanner {
31
- return new SqliteMigrationPlanner();
31
+ export function createSqliteMigrationPlanner(lowerer: Lowerer): SqliteMigrationPlanner {
32
+ return new SqliteMigrationPlanner(lowerer);
32
33
  }
33
34
 
34
35
  export type SqlitePlanResult =
@@ -52,6 +53,12 @@ export type SqlitePlanResult =
52
53
  export class SqliteMigrationPlanner
53
54
  implements SqlMigrationPlanner<SqlitePlanTargetDetails>, MigrationPlanner<'sql', 'sqlite'>
54
55
  {
56
+ readonly #lowerer: Lowerer;
57
+
58
+ constructor(lowerer: Lowerer) {
59
+ this.#lowerer = lowerer;
60
+ }
61
+
55
62
  plan(options: {
56
63
  readonly contract: unknown;
57
64
  readonly schema: unknown;
@@ -89,6 +96,8 @@ export class SqliteMigrationPlanner
89
96
  to: context.toHash,
90
97
  },
91
98
  spaceId,
99
+ undefined,
100
+ this.#lowerer,
92
101
  );
93
102
  }
94
103
 
@@ -149,6 +158,7 @@ export class SqliteMigrationPlanner
149
158
  },
150
159
  options.spaceId,
151
160
  destination,
161
+ this.#lowerer,
152
162
  ),
153
163
  };
154
164
  }
@@ -1,15 +1,23 @@
1
1
  import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
+ import type { Lowerer } from '@prisma-next/family-sql/control-adapter';
2
3
  import type { OpFactoryCall } from '@prisma-next/framework-components/control';
4
+ import { blindCast } from '@prisma-next/utils/casts';
3
5
  import type { SqlitePlanTargetDetails } from './planner-target-details';
4
6
 
5
7
  type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
6
8
 
7
- export function renderOps(calls: readonly OpFactoryCall[]): Op[] {
9
+ export function renderOps(calls: readonly OpFactoryCall[], lowerer?: Lowerer): Op[] {
8
10
  // Each call's `toOp()` is typed as the framework `MigrationPlanOperation`;
9
11
  // every concrete Call class on the sqlite planner path produces an op
10
12
  // whose `target.details` is `SqlitePlanTargetDetails`-shaped (or whose
11
13
  // `target.details` is absent, which is structurally compatible). The
12
14
  // narrowing cast happens at this single integration boundary instead of
13
15
  // poisoning every caller's type.
14
- return calls.map((c) => c.toOp() as Op);
16
+ return calls.map(
17
+ (c) =>
18
+ blindCast<
19
+ { toOp(lowerer?: Lowerer): ReturnType<OpFactoryCall['toOp']> },
20
+ 'SQLite OpFactoryCall.toOp accepts an optional Lowerer; the framework interface omits it because not all targets need a lowerer — the SQLite target overrides with this extended signature'
21
+ >(c).toOp(lowerer) as Op,
22
+ );
15
23
  }
@@ -1,4 +1,11 @@
1
+ import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
2
+ import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
1
3
  import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
4
+ import type { ControlStack } from '@prisma-next/framework-components/control';
5
+ import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
6
+ import { blindCast } from '@prisma-next/utils/casts';
7
+ import { errorSqliteMigrationStackMissing } from '../errors';
8
+ import { CreateTableCall } from './op-factory-call';
2
9
  import type { SqlitePlanTargetDetails } from './planner-target-details';
3
10
 
4
11
  /**
@@ -7,7 +14,50 @@ import type { SqlitePlanTargetDetails } from './planner-target-details';
7
14
  * SQLite literal, so both user-authored migrations and renderer-generated
8
15
  * scaffolds can extend `SqliteMigration` directly without redeclaring
9
16
  * target-local identity.
17
+ *
18
+ * The constructor materializes a single SQLite `SqlControlAdapter` from
19
+ * `stack.adapter.create(stack)` and stores it; the protected `createTable`
20
+ * instance method forwards to `CreateTableCall` with that stored adapter,
21
+ * so user migrations can write `this.createTable({...})` without threading
22
+ * the adapter through every call.
10
23
  */
11
- export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails> {
24
+ export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails, 'sqlite'> {
12
25
  readonly targetId = 'sqlite' as const;
26
+
27
+ /**
28
+ * Materialized SQLite control adapter, created once per migration
29
+ * instance from the injected stack. `undefined` only when the migration
30
+ * was instantiated without a stack (test fixtures); `createTable`
31
+ * throws in that case to surface the misuse.
32
+ */
33
+ protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
34
+
35
+ constructor(stack?: ControlStack<'sql', 'sqlite'>) {
36
+ super(stack);
37
+ this.controlAdapter = stack?.adapter
38
+ ? blindCast<
39
+ SqlControlAdapter<'sqlite'>,
40
+ 'The SQLite descriptor create() returns SqlControlAdapter<sqlite>; typed as wider ControlAdapterInstance at the framework boundary'
41
+ >(stack.adapter.create(stack))
42
+ : undefined;
43
+ }
44
+
45
+ /**
46
+ * Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from
47
+ * the supplied options and lowers it through the stored control adapter.
48
+ * Throws if no adapter is present (i.e. migration instantiated without a stack).
49
+ */
50
+ protected createTable(options: {
51
+ readonly table: string;
52
+ readonly ifNotExists?: boolean;
53
+ readonly columns: readonly DdlColumn[];
54
+ readonly constraints?: readonly DdlTableConstraint[];
55
+ }): SqlMigrationPlanOperation<SqlitePlanTargetDetails> {
56
+ if (!this.controlAdapter) {
57
+ throw errorSqliteMigrationStackMissing();
58
+ }
59
+ return new CreateTableCall(options.table, options.columns, options.constraints).toOp(
60
+ this.controlAdapter,
61
+ );
62
+ }
13
63
  }
@@ -9,6 +9,14 @@ export { MigrationCLI } from '@prisma-next/cli/migration-cli';
9
9
  // `placeholder("…")` slots, instead of pulling in `@prisma-next/errors`
10
10
  // directly. The planner emits an import from this same module.
11
11
  export { placeholder } from '@prisma-next/errors/migration';
12
+ export {
13
+ col,
14
+ fn,
15
+ foreignKey,
16
+ lit,
17
+ primaryKey,
18
+ unique,
19
+ } from '@prisma-next/sql-relational-core/contract-free';
12
20
  export { addColumn, dropColumn } from '../core/migrations/operations/columns';
13
21
  export {
14
22
  type DataTransformOptions,
@@ -16,7 +24,7 @@ export {
16
24
  } from '../core/migrations/operations/data-transform';
17
25
  export { createIndex, dropIndex } from '../core/migrations/operations/indexes';
18
26
  export { rawSql } from '../core/migrations/operations/raw';
19
- export { createTable, dropTable, recreateTable } from '../core/migrations/operations/tables';
27
+ export { dropTable, recreateTable } from '../core/migrations/operations/tables';
20
28
  // Target-owned base class for migrations. Aliased to `Migration` so
21
29
  // user-edited migration.ts files (and the renderer's scaffold) read as
22
30
  // `class M extends Migration { … }` without having to thread the