@prisma-next/target-sqlite 0.14.0-dev.4 → 0.14.0-dev.41

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 (58) hide show
  1. package/dist/contract-free.mjs +1 -1
  2. package/dist/control.d.mts +3 -4
  3. package/dist/control.d.mts.map +1 -1
  4. package/dist/control.mjs +28 -27
  5. package/dist/control.mjs.map +1 -1
  6. package/dist/{descriptor-meta-DxmEeTJ-.mjs → descriptor-meta-D1fap-5t.mjs} +4 -11
  7. package/dist/descriptor-meta-D1fap-5t.mjs.map +1 -0
  8. package/dist/descriptor-meta-runtime-BkXK3OjD.mjs +12 -0
  9. package/dist/descriptor-meta-runtime-BkXK3OjD.mjs.map +1 -0
  10. package/dist/migration.d.mts +2 -2
  11. package/dist/migration.mjs +2 -2
  12. package/dist/{op-factory-call-DmdfD1yd.mjs → op-factory-call-CMBTpb4M.mjs} +54 -53
  13. package/dist/{op-factory-call-DmdfD1yd.mjs.map → op-factory-call-CMBTpb4M.mjs.map} +1 -1
  14. package/dist/op-factory-call.d.mts +2 -2
  15. package/dist/op-factory-call.d.mts.map +1 -1
  16. package/dist/op-factory-call.mjs +1 -1
  17. package/dist/pack.mjs +1 -1
  18. package/dist/{planner-Ciq8p_dL.mjs → planner-BzIVyjD-.mjs} +68 -14
  19. package/dist/planner-BzIVyjD-.mjs.map +1 -0
  20. package/dist/{planner-produced-sqlite-migration-CpgsY-M9.d.mts → planner-produced-sqlite-migration-B9UZVDPa.d.mts} +3 -3
  21. package/dist/{planner-produced-sqlite-migration-CpgsY-M9.d.mts.map → planner-produced-sqlite-migration-B9UZVDPa.d.mts.map} +1 -1
  22. package/dist/{planner-produced-sqlite-migration-0xPEm3R1.mjs → planner-produced-sqlite-migration-DuQtGNO7.mjs} +46 -17
  23. package/dist/planner-produced-sqlite-migration-DuQtGNO7.mjs.map +1 -0
  24. package/dist/planner-produced-sqlite-migration.d.mts +1 -1
  25. package/dist/planner-produced-sqlite-migration.mjs +1 -1
  26. package/dist/planner.d.mts +1 -1
  27. package/dist/planner.mjs +1 -1
  28. package/dist/runtime.d.mts +6 -4
  29. package/dist/runtime.d.mts.map +1 -1
  30. package/dist/runtime.mjs +3 -3
  31. package/dist/{shared-Dhc8mLK1.d.mts → shared-C6sM-wel.d.mts} +2 -2
  32. package/dist/{shared-Dhc8mLK1.d.mts.map → shared-C6sM-wel.d.mts.map} +1 -1
  33. package/dist/{sqlite-contract-serializer--iaDgC8e.mjs → sqlite-contract-view-1h0aHx1n.mjs} +34 -9
  34. package/dist/sqlite-contract-view-1h0aHx1n.mjs.map +1 -0
  35. package/dist/sqlite-contract-view-J32K82Gi.d.mts +29 -0
  36. package/dist/sqlite-contract-view-J32K82Gi.d.mts.map +1 -0
  37. package/dist/{sqlite-migration-A0rwqPOG.mjs → sqlite-migration-Dx3LlQNU.mjs} +29 -2
  38. package/dist/sqlite-migration-Dx3LlQNU.mjs.map +1 -0
  39. package/dist/{sqlite-migration-DVfhQwN_.d.mts → sqlite-migration-_BhTcnIe.d.mts} +27 -4
  40. package/dist/sqlite-migration-_BhTcnIe.d.mts.map +1 -0
  41. package/package.json +18 -18
  42. package/src/core/control-target.ts +26 -18
  43. package/src/core/migrations/diff-database-schema.ts +74 -0
  44. package/src/core/migrations/op-factory-call.ts +5 -0
  45. package/src/core/migrations/planner.ts +21 -9
  46. package/src/core/migrations/render-typescript.ts +42 -15
  47. package/src/core/migrations/runner.ts +4 -10
  48. package/src/core/migrations/sqlite-migration.ts +42 -1
  49. package/src/core/sqlite-contract-serializer.ts +41 -14
  50. package/src/core/sqlite-contract-view.ts +41 -0
  51. package/src/core/sqlite-unbound-database.ts +8 -8
  52. package/src/exports/runtime.ts +1 -0
  53. package/dist/descriptor-meta-DxmEeTJ-.mjs.map +0 -1
  54. package/dist/planner-Ciq8p_dL.mjs.map +0 -1
  55. package/dist/planner-produced-sqlite-migration-0xPEm3R1.mjs.map +0 -1
  56. package/dist/sqlite-contract-serializer--iaDgC8e.mjs.map +0 -1
  57. package/dist/sqlite-migration-A0rwqPOG.mjs.map +0 -1
  58. package/dist/sqlite-migration-DVfhQwN_.d.mts.map +0 -1
@@ -1,9 +1,12 @@
1
1
  import { t as SqlitePlanTargetDetails } from "./planner-target-details-xR6UfIcz.mjs";
2
- import { i as SqliteTableSpec, n as SqliteColumnSpec, r as SqliteIndexSpec } from "./shared-Dhc8mLK1.mjs";
2
+ import { i as SqliteTableSpec, n as SqliteColumnSpec, r as SqliteIndexSpec } from "./shared-C6sM-wel.mjs";
3
+ import { t as SqliteContractView } from "./sqlite-contract-view-J32K82Gi.mjs";
3
4
  import { DdlColumn, DdlTableConstraint } from "@prisma-next/sql-relational-core/ast";
5
+ import { SqlStorage } from "@prisma-next/sql-contract/types";
4
6
  import { MigrationOperationClass, SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
5
- import { Migration } from "@prisma-next/family-sql/migration";
6
7
  import { ControlStack } from "@prisma-next/framework-components/control";
8
+ import { Migration } from "@prisma-next/family-sql/migration";
9
+ import { Contract } from "@prisma-next/contract/types";
7
10
  import { SqlControlAdapter } from "@prisma-next/family-sql/control-adapter";
8
11
 
9
12
  //#region src/core/migrations/sqlite-migration.d.ts
@@ -20,8 +23,17 @@ type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
20
23
  * forward to the corresponding `*Call` with that stored adapter, so user
21
24
  * migrations can write `this.createTable({...})` without threading the adapter
22
25
  * through every call.
26
+ *
27
+ * Binds the framework base's `Start` / `End` contract generics so a subclass
28
+ * that assigns its `start-contract.json` / `end-contract.json` imports gets
29
+ * fully-typed view accessors: `this.endContract` is a `SqliteContractView<End>`
30
+ * (sole namespace unwrapped to the root — `this.endContract.table.<name>`),
31
+ * built lazily from the JSON fields via the shared `MigrationContractViews`
32
+ * helper. Mirrors `MongoMigration`'s view getters; the framework base derives
33
+ * `describe()` from the same JSON.
23
34
  */
24
- declare abstract class SqliteMigration extends Migration<SqlitePlanTargetDetails, 'sqlite'> {
35
+ declare abstract class SqliteMigration<Start extends Contract<SqlStorage> = Contract<SqlStorage>, End extends Contract<SqlStorage> = Contract<SqlStorage>> extends Migration<SqlitePlanTargetDetails, 'sqlite', Start, End> {
36
+ #private;
25
37
  readonly targetId: "sqlite";
26
38
  /**
27
39
  * Materialized SQLite control adapter, created once per migration
@@ -31,6 +43,17 @@ declare abstract class SqliteMigration extends Migration<SqlitePlanTargetDetails
31
43
  */
32
44
  protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
33
45
  constructor(stack?: ControlStack<'sql', 'sqlite'>);
46
+ /**
47
+ * The typed SQLite view over this migration's end-state contract — sole
48
+ * namespace unwrapped to the root, so `this.endContract.table.<name>` etc.
49
+ * Throws if no `endContractJson` was provided.
50
+ */
51
+ get endContract(): SqliteContractView<End>;
52
+ /**
53
+ * The typed SQLite view over this migration's start-state contract, or
54
+ * `null` for a baseline migration (no `startContractJson`).
55
+ */
56
+ get startContract(): SqliteContractView<Start> | null;
34
57
  protected createTable(options: {
35
58
  readonly table: string;
36
59
  readonly ifNotExists?: boolean;
@@ -72,4 +95,4 @@ declare abstract class SqliteMigration extends Migration<SqlitePlanTargetDetails
72
95
  }
73
96
  //#endregion
74
97
  export { SqliteMigration as t };
75
- //# sourceMappingURL=sqlite-migration-DVfhQwN_.d.mts.map
98
+ //# sourceMappingURL=sqlite-migration-_BhTcnIe.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-migration-_BhTcnIe.d.mts","names":[],"sources":["../src/core/migrations/sqlite-migration.ts"],"mappings":";;;;;;;;;;;;KA0BK,EAAA,GAAK,yBAAyB,CAAC,uBAAA;;AAFoC;;;;AAEb;AAuB3D;;;;;;;;;;;;;;;uBAAsB,eAAA,eACN,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,UAAA,eAClC,QAAA,CAAS,UAAA,IAAc,QAAA,CAAS,UAAA,WACpC,SAAA,CAAa,uBAAA,YAAmC,KAAA,EAAO,GAAA;EAAA;WACtD,QAAA;EA0C+B;;;;;;EAAA,mBAlCrB,cAAA,EAAgB,iBAAA;cAWvB,KAAA,GAAQ,YAAA;EAkDD;;;;;EAAA,IAnCf,WAAA,IAAe,kBAAA,CAAmB,GAAA;EAsDlC;;;;EAAA,IA9CA,aAAA,IAAiB,kBAAA,CAAmB,KAAA;EAAA,UAI9B,WAAA,CAAY,OAAA;IAAA,SACX,KAAA;IAAA,SACA,WAAA;IAAA,SACA,OAAA,WAAkB,SAAA;IAAA,SAClB,WAAA,YAAuB,kBAAA;EAAA,IAC9B,OAAA,CAAQ,EAAA;EAAA,UASF,SAAA,CAAU,OAAA;IAAA,SAAoB,KAAA;EAAA,IAAkB,OAAA,CAAQ,EAAA;EAAA,UAOxD,SAAA,CAAU,OAAA;IAAA,SACT,KAAA;IAAA,SACA,MAAA,EAAQ,gBAAA;EAAA,IACf,OAAA,CAAQ,EAAA;EAAA,UAOF,UAAA,CAAW,OAAA;IAAA,SAAoB,KAAA;IAAA,SAAwB,MAAA;EAAA,IAAmB,OAAA,CAAQ,EAAA;EAAA,UAOlF,WAAA,CAAY,OAAA;IAAA,SACX,KAAA;IAAA,SACA,KAAA;IAAA,SACA,OAAA;EAAA,IACP,OAAA,CAAQ,EAAA;EAAA,UASF,SAAA,CAAU,OAAA;IAAA,SAAoB,KAAA;IAAA,SAAwB,KAAA;EAAA,IAAkB,OAAA,CAAQ,EAAA;EAAA,UAOhF,aAAA,CAAc,OAAA;IAAA,SACb,SAAA;IAAA,SACA,aAAA,EAAe,eAAA;IAAA,SACf,iBAAA;IAAA,SACA,OAAA,WAAkB,eAAA;IAAA,SAClB,OAAA;IAAA,SACA,UAAA;MAAA,SAAgC,WAAA;MAAA,SAA8B,GAAA;IAAA;IAAA,SAC9D,cAAA,EAAgB,uBAAA;EAAA,IACvB,OAAA,CAAQ,EAAA;AAAA"}
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
1
  {
2
2
  "name": "@prisma-next/target-sqlite",
3
- "version": "0.14.0-dev.4",
3
+ "version": "0.14.0-dev.41",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "dependencies": {
8
- "@prisma-next/cli": "0.14.0-dev.4",
9
- "@prisma-next/contract": "0.14.0-dev.4",
10
- "@prisma-next/errors": "0.14.0-dev.4",
11
- "@prisma-next/family-sql": "0.14.0-dev.4",
12
- "@prisma-next/framework-components": "0.14.0-dev.4",
13
- "@prisma-next/migration-tools": "0.14.0-dev.4",
14
- "@prisma-next/sql-contract": "0.14.0-dev.4",
15
- "@prisma-next/sql-errors": "0.14.0-dev.4",
16
- "@prisma-next/sql-relational-core": "0.14.0-dev.4",
17
- "@prisma-next/sql-runtime": "0.14.0-dev.4",
18
- "@prisma-next/sql-schema-ir": "0.14.0-dev.4",
19
- "@prisma-next/ts-render": "0.14.0-dev.4",
20
- "@prisma-next/utils": "0.14.0-dev.4",
8
+ "@prisma-next/cli": "0.14.0-dev.41",
9
+ "@prisma-next/contract": "0.14.0-dev.41",
10
+ "@prisma-next/errors": "0.14.0-dev.41",
11
+ "@prisma-next/family-sql": "0.14.0-dev.41",
12
+ "@prisma-next/framework-components": "0.14.0-dev.41",
13
+ "@prisma-next/migration-tools": "0.14.0-dev.41",
14
+ "@prisma-next/sql-contract": "0.14.0-dev.41",
15
+ "@prisma-next/sql-errors": "0.14.0-dev.41",
16
+ "@prisma-next/sql-relational-core": "0.14.0-dev.41",
17
+ "@prisma-next/sql-runtime": "0.14.0-dev.41",
18
+ "@prisma-next/sql-schema-ir": "0.14.0-dev.41",
19
+ "@prisma-next/ts-render": "0.14.0-dev.41",
20
+ "@prisma-next/utils": "0.14.0-dev.41",
21
21
  "@standard-schema/spec": "1.1.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@prisma-next/driver-sqlite": "0.14.0-dev.4",
25
- "@prisma-next/test-utils": "0.14.0-dev.4",
26
- "@prisma-next/tsconfig": "0.14.0-dev.4",
27
- "@prisma-next/tsdown": "0.14.0-dev.4",
24
+ "@prisma-next/driver-sqlite": "0.14.0-dev.41",
25
+ "@prisma-next/test-utils": "0.14.0-dev.41",
26
+ "@prisma-next/tsconfig": "0.14.0-dev.41",
27
+ "@prisma-next/tsdown": "0.14.0-dev.41",
28
28
  "tsdown": "0.22.1",
29
29
  "typescript": "5.9.3",
30
30
  "vitest": "4.1.8"
@@ -1,16 +1,19 @@
1
- import type { ColumnDefault, Contract } from '@prisma-next/contract/types';
1
+ import type { Contract } from '@prisma-next/contract/types';
2
2
  import type { SqlControlTargetDescriptor } from '@prisma-next/family-sql/control';
3
- import { contractToSchemaIR } from '@prisma-next/family-sql/control';
4
3
  import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
5
4
  import type {
6
5
  ControlTargetInstance,
7
6
  MigrationPlanner,
8
7
  MigrationRunner,
9
8
  } from '@prisma-next/framework-components/control';
10
- import { SqlStorage, type StorageColumn } from '@prisma-next/sql-contract/types';
9
+ import { SqlStorage } from '@prisma-next/sql-contract/types';
11
10
  import { sqliteTargetDescriptorMeta } from './descriptor-meta';
11
+ import {
12
+ diffSqliteDatabaseSchema,
13
+ sqliteContractToSchema,
14
+ verifySqliteDatabaseSchema,
15
+ } from './migrations/diff-database-schema';
12
16
  import { createSqliteMigrationPlanner } from './migrations/planner';
13
- import { renderDefaultLiteral } from './migrations/planner-ddl-builders';
14
17
  import type { SqlitePlanTargetDetails } from './migrations/planner-target-details';
15
18
  import { createSqliteMigrationRunner } from './migrations/runner';
16
19
  import { SqliteContractSerializer } from './sqlite-contract-serializer';
@@ -20,21 +23,29 @@ function isSqlContract(contract: Contract | null): contract is Contract<SqlStora
20
23
  return contract === null || contract.storage instanceof SqlStorage;
21
24
  }
22
25
 
23
- function sqliteRenderDefault(def: ColumnDefault, _column: StorageColumn): string {
24
- if (def.kind === 'function') {
25
- if (def.expression === 'now()') {
26
- return "datetime('now')";
27
- }
28
- return def.expression;
29
- }
30
- return renderDefaultLiteral(def.value);
31
- }
32
-
33
26
  const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', SqlitePlanTargetDetails> =
34
27
  {
35
28
  ...sqliteTargetDescriptorMeta,
36
29
  contractSerializer: new SqliteContractSerializer(),
37
30
  schemaVerifier: new SqliteSchemaVerifier(),
31
+ diffDatabaseSchema(input) {
32
+ return diffSqliteDatabaseSchema({
33
+ contract: input.contract,
34
+ actualSchema: input.schema,
35
+ strict: input.strict,
36
+ typeMetadataRegistry: input.typeMetadataRegistry,
37
+ frameworkComponents: input.frameworkComponents,
38
+ });
39
+ },
40
+ verifyDatabaseSchema(input) {
41
+ return verifySqliteDatabaseSchema({
42
+ contract: input.contract,
43
+ actualSchema: input.schema,
44
+ strict: input.strict,
45
+ typeMetadataRegistry: input.typeMetadataRegistry,
46
+ frameworkComponents: input.frameworkComponents,
47
+ });
48
+ },
38
49
  migrations: {
39
50
  createPlanner(adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {
40
51
  return createSqliteMigrationPlanner(adapter);
@@ -55,10 +66,7 @@ const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', Sqlite
55
66
  'sqliteControlTargetDescriptor.contractToSchema received a non-SQL contract; expected Contract<SqlStorage>',
56
67
  );
57
68
  }
58
- return contractToSchemaIR(contract, {
59
- annotationNamespace: 'sqlite',
60
- renderDefault: sqliteRenderDefault,
61
- });
69
+ return sqliteContractToSchema(contract);
62
70
  },
63
71
  },
64
72
  create(): ControlTargetInstance<'sql', 'sqlite'> {
@@ -0,0 +1,74 @@
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';
4
+ 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';
11
+ import { renderDefaultLiteral } from './planner-ddl-builders';
12
+
13
+ interface SqliteDiffDatabaseSchemaInput {
14
+ readonly contract: Contract<SqlStorage>;
15
+ readonly actualSchema: SqlSchemaIRNode;
16
+ readonly strict: boolean;
17
+ readonly typeMetadataRegistry: ReadonlyMap<string, { readonly nativeType?: string }>;
18
+ readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<'sql', string>>;
19
+ }
20
+
21
+ /** Renders a column default for the SQLite dialect. */
22
+ export function sqliteRenderDefault(def: ColumnDefault, _column: StorageColumn): string {
23
+ if (def.kind === 'function') {
24
+ if (def.expression === 'now()') {
25
+ return "datetime('now')";
26
+ }
27
+ return def.expression;
28
+ }
29
+ return renderDefaultLiteral(def.value);
30
+ }
31
+
32
+ /** The SQLite expected-side projection: contract → flat relational schema IR. */
33
+ export function sqliteContractToSchema(contract: Contract<SqlStorage> | null): SqlSchemaIR {
34
+ return contractToSchemaIR(contract, {
35
+ annotationNamespace: 'sqlite',
36
+ renderDefault: sqliteRenderDefault,
37
+ });
38
+ }
39
+
40
+ function computeSqliteSchemaComparison(
41
+ input: SqliteDiffDatabaseSchemaInput,
42
+ ): VerifyDatabaseSchemaResult {
43
+ return verifySqlSchemaTree({
44
+ contract: input.contract,
45
+ actualSchema: input.actualSchema,
46
+ buildExpectedSchema: sqliteContractToSchema,
47
+ strict: input.strict,
48
+ typeMetadataRegistry: input.typeMetadataRegistry,
49
+ frameworkComponents: input.frameworkComponents,
50
+ normalizeDefault: parseSqliteDefault,
51
+ normalizeNativeType: normalizeSqliteNativeType,
52
+ });
53
+ }
54
+
55
+ /**
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`.
59
+ */
60
+ export function diffSqliteDatabaseSchema(input: SqliteDiffDatabaseSchemaInput): SchemaDiff {
61
+ const relational = computeSqliteSchemaComparison(input);
62
+ return new SchemaDiff(relational.schema.issues, relational.schema.schemaDiffIssues);
63
+ }
64
+
65
+ /**
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.
69
+ */
70
+ export function verifySqliteDatabaseSchema(
71
+ input: SqliteDiffDatabaseSchemaInput,
72
+ ): VerifyDatabaseSchemaResult {
73
+ return computeSqliteSchemaComparison(input);
74
+ }
@@ -96,6 +96,11 @@ function renderDdlConstraintAsTsCall(constraint: DdlTableConstraint): string {
96
96
  const nameOpt = constraint.name ? `, { name: ${jsonToTsSource(constraint.name)} }` : '';
97
97
  return `unique(${jsonToTsSource(constraint.columns)}${nameOpt})`;
98
98
  }
99
+ case 'check-expression':
100
+ throw new Error(
101
+ `SQLite does not support expression CHECK constraints (constraint "${constraint.name}"). ` +
102
+ 'Scalar-array columns and their element-non-null checks are Postgres-only.',
103
+ );
99
104
  }
100
105
  }
101
106
 
@@ -11,15 +11,15 @@ import {
11
11
  plannerFailure,
12
12
  } from '@prisma-next/family-sql/control';
13
13
  import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
14
- import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
15
14
  import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';
16
15
  import type {
17
16
  MigrationPlanner,
18
17
  MigrationScaffoldContext,
19
18
  SchemaIssue,
20
19
  } from '@prisma-next/framework-components/control';
21
- import { parseSqliteDefault } from '../default-normalizer';
22
- import { normalizeSqliteNativeType } from '../native-type-normalizer';
20
+ import type { SqlSchemaIR, SqlSchemaIRNode } from '@prisma-next/sql-schema-ir/types';
21
+ import { blindCast } from '@prisma-next/utils/casts';
22
+ import { diffSqliteDatabaseSchema } from './diff-database-schema';
23
23
  import { planIssues } from './issue-planner';
24
24
  import {
25
25
  type SqliteMigrationDestinationInfo,
@@ -117,7 +117,7 @@ export class SqliteMigrationPlanner
117
117
  fromContract: options.fromContract,
118
118
  codecHooks,
119
119
  storageTypes,
120
- schema: options.schema,
120
+ schema: sqliteFlatSchema(options.schema),
121
121
  policy: options.policy,
122
122
  frameworkComponents: options.frameworkComponents,
123
123
  strategies: sqlitePlannerStrategies,
@@ -181,15 +181,27 @@ export class SqliteMigrationPlanner
181
181
  private collectSchemaIssues(options: SqlMigrationPlannerPlanOptions): readonly SchemaIssue[] {
182
182
  const allowed = options.policy.allowedOperationClasses;
183
183
  const strict = allowed.includes('widening') || allowed.includes('destructive');
184
- const verifyResult = verifySqlSchema({
184
+ const rawDiff = diffSqliteDatabaseSchema({
185
185
  contract: options.contract,
186
- schema: options.schema,
186
+ actualSchema: options.schema,
187
187
  strict,
188
188
  typeMetadataRegistry: new Map(),
189
189
  frameworkComponents: options.frameworkComponents,
190
- normalizeDefault: parseSqliteDefault,
191
- normalizeNativeType: normalizeSqliteNativeType,
192
190
  });
193
- return verifyResult.schema.issues;
191
+ // The caller-supplied predicate is applied blindly — any scoping (e.g.
192
+ // multi-space ownership) is the orchestration's, never worked out here.
193
+ const diff = options.keepDiffIssue ? rawDiff.filter(options.keepDiffIssue) : rawDiff;
194
+ return diff.issues;
194
195
  }
195
196
  }
197
+
198
+ /**
199
+ * SQLite has a single, flat schema — its introspected node IS a per-schema
200
+ * `SqlSchemaIR`, never the multi-namespace tree the Postgres target builds. The
201
+ * planner consumes that flat shape directly when building ops.
202
+ */
203
+ function sqliteFlatSchema(schema: SqlSchemaIRNode): SqlSchemaIR {
204
+ return blindCast<SqlSchemaIR, 'the SQLite introspected node is a flat per-schema SqlSchemaIR'>(
205
+ schema,
206
+ );
207
+ }
@@ -35,15 +35,19 @@ export function renderCallsToTypeScript(
35
35
  calls: ReadonlyArray<OpFactoryCall>,
36
36
  meta: RenderMigrationMeta,
37
37
  ): string {
38
- const imports = buildImports(calls);
38
+ const imports = buildImports(calls, meta);
39
39
  const operationsBody = calls.map((c) => c.renderTypeScript()).join(',\n');
40
+ const hasStart = meta.from !== null;
41
+ const startField = hasStart ? [' override readonly startContractJson = startContract;'] : [];
40
42
 
41
43
  return [
42
44
  shebangLineFor(detectScaffoldRuntime()),
43
45
  imports,
44
46
  '',
45
- 'export default class M extends Migration {',
46
- buildDescribeMethod(meta),
47
+ `export default class M extends Migration<${hasStart ? 'Start' : 'never'}, End> {`,
48
+ ...startField,
49
+ ' override readonly endContractJson = endContract;',
50
+ '',
47
51
  ' override get operations() {',
48
52
  ' return [',
49
53
  indent(operationsBody, 6),
@@ -56,8 +60,8 @@ export function renderCallsToTypeScript(
56
60
  ].join('\n');
57
61
  }
58
62
 
59
- function buildImports(calls: ReadonlyArray<OpFactoryCall>): string {
60
- const requirements: ImportRequirement[] = [...BASE_IMPORTS];
63
+ function buildImports(calls: ReadonlyArray<OpFactoryCall>, meta: RenderMigrationMeta): string {
64
+ const requirements: ImportRequirement[] = [...BASE_IMPORTS, ...contractImports(meta)];
61
65
  for (const call of calls) {
62
66
  for (const req of call.importRequirements()) {
63
67
  requirements.push(req);
@@ -66,16 +70,39 @@ function buildImports(calls: ReadonlyArray<OpFactoryCall>): string {
66
70
  return renderImports(requirements);
67
71
  }
68
72
 
69
- function buildDescribeMethod(meta: RenderMigrationMeta): string {
70
- const lines: string[] = [];
71
- lines.push(' override describe() {');
72
- lines.push(' return {');
73
- lines.push(` from: ${JSON.stringify(meta.from)},`);
74
- lines.push(` to: ${JSON.stringify(meta.to)},`);
75
- lines.push(' };');
76
- lines.push(' }');
77
- lines.push('');
78
- return lines.join('\n');
73
+ /**
74
+ * The committed contract-JSON imports the scaffold reads its from/to identity
75
+ * from. `end-contract.json` is always present; `start-contract.json` is added
76
+ * only for a non-baseline migration (`meta.from !== null`). The matching
77
+ * `Contract` type imports (aliased `Start`/`End`) feed the
78
+ * `Migration<Start, End>` generics. Baseline emits `Migration<never, End>` with
79
+ * no start imports — `never` is the honest "no prior contract" Start.
80
+ */
81
+ function contractImports(meta: RenderMigrationMeta): readonly ImportRequirement[] {
82
+ const reqs: ImportRequirement[] = [
83
+ {
84
+ moduleSpecifier: './end-contract.json',
85
+ symbol: 'endContract',
86
+ kind: 'default',
87
+ attributes: { type: 'json' },
88
+ },
89
+ { moduleSpecifier: './end-contract', symbol: 'Contract', alias: 'End', typeOnly: true },
90
+ ];
91
+ if (meta.from !== null) {
92
+ reqs.push({
93
+ moduleSpecifier: './start-contract.json',
94
+ symbol: 'startContract',
95
+ kind: 'default',
96
+ attributes: { type: 'json' },
97
+ });
98
+ reqs.push({
99
+ moduleSpecifier: './start-contract',
100
+ symbol: 'Contract',
101
+ alias: 'Start',
102
+ typeOnly: true,
103
+ });
104
+ }
105
+ return reqs;
79
106
  }
80
107
 
81
108
  function indent(text: string, spaces: number): string {
@@ -12,7 +12,6 @@ import type {
12
12
  SqlMigrationRunnerSuccessValue,
13
13
  } from '@prisma-next/family-sql/control';
14
14
  import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
15
- import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
16
15
  import type { MigrationRunnerResult } from '@prisma-next/framework-components/control';
17
16
  import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
18
17
  import type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';
@@ -22,8 +21,7 @@ import { ifDefined } from '@prisma-next/utils/defined';
22
21
  import type { Result } from '@prisma-next/utils/result';
23
22
  import { notOk, ok, okVoid } from '@prisma-next/utils/result';
24
23
  import { MARKER_TABLE_NAME } from '../control-tables';
25
- import { parseSqliteDefault } from '../default-normalizer';
26
- import { normalizeSqliteNativeType } from '../native-type-normalizer';
24
+ import { verifySqliteDatabaseSchema } from './diff-database-schema';
27
25
  import type { SqlitePlanTargetDetails } from './planner-target-details';
28
26
 
29
27
  export function createSqliteMigrationRunner(
@@ -92,20 +90,16 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
92
90
  }
93
91
 
94
92
  if (space === APP_SPACE_ID) {
95
- const schemaIR = await this.family.introspect({
93
+ const schemaNode = await this.family.introspect({
96
94
  driver,
97
95
  contract: options.destinationContract,
98
96
  });
99
-
100
- const schemaVerifyResult = verifySqlSchema({
97
+ const schemaVerifyResult = verifySqliteDatabaseSchema({
101
98
  contract: options.destinationContract,
102
- schema: schemaIR,
99
+ actualSchema: schemaNode,
103
100
  strict: options.strictVerification ?? true,
104
- context: options.context ?? {},
105
101
  typeMetadataRegistry: this.family.typeMetadataRegistry,
106
102
  frameworkComponents: options.frameworkComponents,
107
- normalizeDefault: parseSqliteDefault,
108
- normalizeNativeType: normalizeSqliteNativeType,
109
103
  });
110
104
  if (!schemaVerifyResult.ok) {
111
105
  return runnerFailure('SCHEMA_VERIFY_FAILED', schemaVerifyResult.summary, {
@@ -1,3 +1,4 @@
1
+ import type { Contract } from '@prisma-next/contract/types';
1
2
  import type {
2
3
  MigrationOperationClass,
3
4
  SqlMigrationPlanOperation,
@@ -5,9 +6,12 @@ import type {
5
6
  import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
6
7
  import { Migration as SqlMigration } from '@prisma-next/family-sql/migration';
7
8
  import type { ControlStack } from '@prisma-next/framework-components/control';
9
+ import { MigrationContractViews } from '@prisma-next/migration-tools/migration';
10
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
8
11
  import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
9
12
  import { blindCast } from '@prisma-next/utils/casts';
10
13
  import { errorSqliteMigrationStackMissing } from '../errors';
14
+ import { SqliteContractView } from '../sqlite-contract-view';
11
15
  import {
12
16
  AddColumnCall,
13
17
  CreateIndexCall,
@@ -34,8 +38,19 @@ type Op = SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
34
38
  * forward to the corresponding `*Call` with that stored adapter, so user
35
39
  * migrations can write `this.createTable({...})` without threading the adapter
36
40
  * through every call.
41
+ *
42
+ * Binds the framework base's `Start` / `End` contract generics so a subclass
43
+ * that assigns its `start-contract.json` / `end-contract.json` imports gets
44
+ * fully-typed view accessors: `this.endContract` is a `SqliteContractView<End>`
45
+ * (sole namespace unwrapped to the root — `this.endContract.table.<name>`),
46
+ * built lazily from the JSON fields via the shared `MigrationContractViews`
47
+ * helper. Mirrors `MongoMigration`'s view getters; the framework base derives
48
+ * `describe()` from the same JSON.
37
49
  */
38
- export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails, 'sqlite'> {
50
+ export abstract class SqliteMigration<
51
+ Start extends Contract<SqlStorage> = Contract<SqlStorage>,
52
+ End extends Contract<SqlStorage> = Contract<SqlStorage>,
53
+ > extends SqlMigration<SqlitePlanTargetDetails, 'sqlite', Start, End> {
39
54
  readonly targetId = 'sqlite' as const;
40
55
 
41
56
  /**
@@ -46,6 +61,15 @@ export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetai
46
61
  */
47
62
  protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
48
63
 
64
+ #endView = new MigrationContractViews<SqliteContractView<End>>(this, 'SqliteMigration', (json) =>
65
+ SqliteContractView.fromJson<End>(json),
66
+ );
67
+ #startView = new MigrationContractViews<SqliteContractView<Start>>(
68
+ this,
69
+ 'SqliteMigration',
70
+ (json) => SqliteContractView.fromJson<Start>(json),
71
+ );
72
+
49
73
  constructor(stack?: ControlStack<'sql', 'sqlite'>) {
50
74
  super(stack);
51
75
  this.controlAdapter = stack?.adapter
@@ -56,6 +80,23 @@ export abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetai
56
80
  : undefined;
57
81
  }
58
82
 
83
+ /**
84
+ * The typed SQLite view over this migration's end-state contract — sole
85
+ * namespace unwrapped to the root, so `this.endContract.table.<name>` etc.
86
+ * Throws if no `endContractJson` was provided.
87
+ */
88
+ get endContract(): SqliteContractView<End> {
89
+ return this.#endView.endContract;
90
+ }
91
+
92
+ /**
93
+ * The typed SQLite view over this migration's start-state contract, or
94
+ * `null` for a baseline migration (no `startContractJson`).
95
+ */
96
+ get startContract(): SqliteContractView<Start> | null {
97
+ return this.#startView.startContract;
98
+ }
99
+
59
100
  protected createTable(options: {
60
101
  readonly table: string;
61
102
  readonly ifNotExists?: boolean;
@@ -1,9 +1,9 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
2
  import { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';
3
- import { type Namespace, NamespaceBase } from '@prisma-next/framework-components/ir';
4
- import type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';
3
+ import type { Namespace } from '@prisma-next/framework-components/ir';
4
+ import type { SqlNamespaceInput, SqlStorage } from '@prisma-next/sql-contract/types';
5
5
  import { blindCast } from '@prisma-next/utils/casts';
6
- import { sqliteTargetDescriptorMeta } from './descriptor-meta';
6
+ import type { JsonObject } from '@prisma-next/utils/json';
7
7
  import { buildSqliteNamespace } from './sqlite-unbound-database';
8
8
 
9
9
  /**
@@ -17,21 +17,48 @@ export class SqliteContractSerializer extends SqlContractSerializerBase<Contract
17
17
  super(new Map());
18
18
  }
19
19
 
20
- protected override get defaultNamespaceId(): string {
21
- return sqliteTargetDescriptorMeta.defaultNamespaceId;
22
- }
23
-
24
20
  protected override hydrateSqlNamespaceEntry(
25
21
  nsId: string,
26
- raw: Namespace | Record<string, unknown>,
27
- ): Namespace | SqlNamespaceTablesInput {
28
- if (raw instanceof NamespaceBase) {
29
- return raw;
30
- }
22
+ raw: Record<string, unknown>,
23
+ ): Namespace | SqlNamespaceInput {
31
24
  const hydrated = blindCast<
32
- SqlNamespaceTablesInput,
33
- 'super.hydrateSqlNamespaceEntry returns the tables form when raw is not a NamespaceBase'
25
+ SqlNamespaceInput,
26
+ 'raw is always plain JSON, so super.hydrateSqlNamespaceEntry returns SqlNamespaceInput'
34
27
  >(super.hydrateSqlNamespaceEntry(nsId, raw));
35
28
  return buildSqliteNamespace(hydrated);
36
29
  }
30
+
31
+ override serializeContract(contract: Contract<SqlStorage>): JsonObject {
32
+ const { storage, ...rest } = contract;
33
+ const namespacesJson: Record<string, JsonObject> = {};
34
+ for (const [nsId, ns] of Object.entries(storage.namespaces)) {
35
+ namespacesJson[nsId] = {
36
+ id: ns.id,
37
+ entries: {
38
+ table: blindCast<
39
+ JsonObject,
40
+ 'JSON.parse(JSON.stringify(...)) strips class methods; result is a plain JsonObject'
41
+ >(JSON.parse(JSON.stringify(ns.entries.table ?? {}))),
42
+ },
43
+ };
44
+ }
45
+ return blindCast<
46
+ JsonObject,
47
+ 'rest + storage are serialized plain values; spread preserves JSON-clean contract envelope'
48
+ >({
49
+ ...rest,
50
+ storage: {
51
+ storageHash: String(storage.storageHash),
52
+ namespaces: namespacesJson,
53
+ ...(storage.types !== undefined
54
+ ? {
55
+ types: blindCast<
56
+ JsonObject,
57
+ 'JSON.parse(JSON.stringify(...)) strips class methods; result is plain JsonObject'
58
+ >(JSON.parse(JSON.stringify(storage.types))),
59
+ }
60
+ : {}),
61
+ },
62
+ });
63
+ }
37
64
  }
@@ -0,0 +1,41 @@
1
+ import type { Contract } from '@prisma-next/contract/types';
2
+ import {
3
+ buildSqlSingleNamespaceView,
4
+ type SqlSingleNamespaceView,
5
+ } from '@prisma-next/sql-contract/contract-view';
6
+ import type { SqlStorage } from '@prisma-next/sql-contract/types';
7
+ import { SqliteContractSerializer } from './sqlite-contract-serializer';
8
+
9
+ /**
10
+ * A SQLite contract view: the deserialized contract intersected with by-name
11
+ * accessors. It is substitutable for `Contract` (carries `storage`, `domain`,
12
+ * …) and also exposes the single default namespace unwrapped with the SQL
13
+ * built-in kinds promoted:
14
+ *
15
+ * ```ts
16
+ * const view = SqliteContractView.fromJson<Contract>(contractJson);
17
+ * view.table.users // typed table leaf
18
+ * view.entries.policy.X // pack-contributed kind (singular key)
19
+ * view.storage // the full contract is still present
20
+ * ```
21
+ *
22
+ * SQLite has `sql.enums: false`, so it never emits `valueSet` entries; the
23
+ * `valueSet` slot is therefore an empty map.
24
+ */
25
+ export type SqliteContractView<TContract extends Contract<SqlStorage> = Contract<SqlStorage>> =
26
+ SqlSingleNamespaceView<TContract>;
27
+
28
+ export const SqliteContractView = {
29
+ /** Wrap an already-deserialized SQLite contract in a view. */
30
+ from<TContract extends Contract<SqlStorage>>(contract: TContract): SqliteContractView<TContract> {
31
+ return buildSqlSingleNamespaceView(contract);
32
+ },
33
+
34
+ /** Deserialize a SQLite contract JSON envelope and wrap it in a view. */
35
+ fromJson<TContract extends Contract<SqlStorage> = Contract<SqlStorage>>(
36
+ json: unknown,
37
+ ): SqliteContractView<TContract> {
38
+ const contract = new SqliteContractSerializer().deserializeContract<TContract>(json);
39
+ return buildSqlSingleNamespaceView(contract);
40
+ },
41
+ };