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

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
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-contract-serializer-jcRu8aHh.mjs","names":[],"sources":["../src/core/sqlite-unbound-database.ts","../src/core/sqlite-contract-serializer.ts"],"sourcesContent":["import {\n freezeNode,\n type Namespace,\n NamespaceBase,\n UNBOUND_NAMESPACE_ID,\n} from '@prisma-next/framework-components/ir';\nimport {\n type SqlNamespaceTablesInput,\n StorageTable,\n type StorageTableInput,\n} from '@prisma-next/sql-contract/types';\nimport { blindCast, castAs } from '@prisma-next/utils/casts';\n\nexport type SqliteDatabaseInput = {\n readonly id: string;\n readonly entries: {\n readonly table: Readonly<Record<string, StorageTable | StorageTableInput>>;\n };\n};\n\nconst SQLITE_NAMESPACE_KIND = 'sqlite-namespace' as const;\n\nfunction isMaterializedSqliteNamespace(\n ns: Namespace | SqlNamespaceTablesInput,\n): ns is SqliteDatabase | SqliteUnboundDatabase {\n if (typeof ns !== 'object' || ns === null) {\n return false;\n }\n const proto = Object.getPrototypeOf(ns);\n if (proto === Object.prototype || proto === null) {\n return false;\n }\n return (ns as { kind?: unknown }).kind === SQLITE_NAMESPACE_KIND;\n}\n\n/**\n * SQLite namespace concretion carrying table metadata under\n * `entries.table` and unqualified `qualifyTable()` emission for runtime\n * SQL rendering.\n */\nexport class SqliteDatabase extends NamespaceBase {\n declare readonly kind: string;\n\n readonly id: string;\n readonly entries: Readonly<{\n readonly table: Readonly<Record<string, StorageTable>>;\n }>;\n\n constructor(input: SqliteDatabaseInput) {\n super();\n this.id = input.id;\n this.entries = Object.freeze({\n table: Object.freeze(\n Object.fromEntries(\n Object.entries(input.entries.table).map(([k, v]) => [\n k,\n v instanceof StorageTable ? v : new StorageTable(v as StorageTableInput),\n ]),\n ),\n ),\n });\n Object.defineProperty(this, 'kind', {\n value: SQLITE_NAMESPACE_KIND,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n freezeNode(this);\n }\n\n qualifier(): string {\n return '';\n }\n\n qualifyTable(tableName: string): string {\n return `\"${tableName}\"`;\n }\n}\n\n/**\n * SQLite target `Namespace` concretion. SQLite has no schema or\n * database-namespacing concept at the SQL level — there is exactly one\n * effective namespace per connection, so the target ships a single\n * singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.\n *\n * Qualifier emission elides the prefix entirely: rendered DDL and\n * queries look unqualified (`CREATE TABLE \"users\" (...)`), matching\n * SQLite's native dialect. Call sites stay polymorphic — they ask the\n * namespace for its qualifier and consume the empty/unqualified result\n * the same way Postgres consumes a `\"schema\"` prefix.\n *\n * The SQLite PSL interpreter rejects every explicit `namespace { … }`\n * block with a diagnostic naming SQLite; only the implicit\n * `__unspecified__` AST bucket reaches the SQLite interpreter, which\n * lowers it to this singleton.\n */\nexport class SqliteUnboundDatabase extends SqliteDatabase {\n static readonly instance: SqliteUnboundDatabase = new SqliteUnboundDatabase();\n\n private constructor() {\n super({ id: UNBOUND_NAMESPACE_ID, entries: { table: {} } });\n }\n}\n\nexport function buildSqliteNamespace(\n input: SqlNamespaceTablesInput,\n): SqliteDatabase | SqliteUnboundDatabase {\n if (input.id !== UNBOUND_NAMESPACE_ID) {\n throw new Error(\n `buildSqliteNamespace: SQLite has no schema concept; the only valid namespace id is \"${UNBOUND_NAMESPACE_ID}\" (received \"${input.id}\").`,\n );\n }\n if (Object.keys(input.entries.table).length === 0) {\n return castAs<SqliteUnboundDatabase>(SqliteUnboundDatabase.instance);\n }\n return new SqliteDatabase({ id: input.id, entries: input.entries });\n}\n\nexport function buildSqliteNamespaceMap(\n namespaces: Readonly<Record<string, Namespace | SqlNamespaceTablesInput>>,\n): Readonly<Record<string, SqliteDatabase | SqliteUnboundDatabase>> {\n return Object.fromEntries(\n Object.entries(namespaces).map(([nsKey, ns]) => [\n nsKey,\n isMaterializedSqliteNamespace(ns)\n ? ns\n : buildSqliteNamespace(\n blindCast<\n SqlNamespaceTablesInput,\n 'non-materialized SQLite namespace map entry is a SqlNamespaceTablesInput'\n >(ns),\n ),\n ]),\n );\n}\n\n/**\n * Target-supplied `Namespace` factory the SQLite target plumbs through\n * `defineContract({ createNamespace })`. SQLite has only one\n * effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`\n * sentinel — so the factory always returns the singleton or a fresh\n * `SqliteDatabase` for the unbound slot with tables. The SQL family's\n * defensive validation in `defineContract` already rejects\n * user-declared SQLite namespaces, so this throw is a structural\n * safety net rather than a user-facing surface.\n */\nexport function sqliteCreateNamespace(\n input: SqlNamespaceTablesInput,\n): SqliteDatabase | SqliteUnboundDatabase {\n return buildSqliteNamespace(input);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';\nimport { type Namespace, NamespaceBase } from '@prisma-next/framework-components/ir';\nimport type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { buildSqliteNamespace } from './sqlite-unbound-database';\n\n/**\n * SQLite target `ContractSerializer` concretion. Mirrors the Postgres\n * shape: inherits the full SQL-family deserialization pipeline and\n * materialises namespace entries as SQLite database concretions that\n * expose `qualifyTable()` for runtime SQL rendering.\n */\nexport class SqliteContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n\n protected override hydrateSqlNamespaceEntry(\n nsId: string,\n raw: Namespace | Record<string, unknown>,\n ): Namespace | SqlNamespaceTablesInput {\n if (raw instanceof NamespaceBase) {\n return raw;\n }\n const hydrated = blindCast<\n SqlNamespaceTablesInput,\n 'super.hydrateSqlNamespaceEntry returns the tables form when raw is not a NamespaceBase'\n >(super.hydrateSqlNamespaceEntry(nsId, raw));\n return buildSqliteNamespace(hydrated);\n }\n}\n"],"mappings":";;;;;AAoBA,MAAM,wBAAwB;;;;;;AAoB9B,IAAa,iBAAb,cAAoC,cAAc;CAGhD;CACA;CAIA,YAAY,OAA4B;EACtC,MAAM;EACN,KAAK,KAAK,MAAM;EAChB,KAAK,UAAU,OAAO,OAAO,EAC3B,OAAO,OAAO,OACZ,OAAO,YACL,OAAO,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAClD,GACA,aAAa,eAAe,IAAI,IAAI,aAAa,CAAsB,CACzE,CAAC,CACH,CACF,EACF,CAAC;EACD,OAAO,eAAe,MAAM,QAAQ;GAClC,OAAO;GACP,UAAU;GACV,YAAY;GACZ,cAAc;EAChB,CAAC;EACD,WAAW,IAAI;CACjB;CAEA,YAAoB;EAClB,OAAO;CACT;CAEA,aAAa,WAA2B;EACtC,OAAO,IAAI,UAAU;CACvB;AACF;;;;;;;;;;;;;;;;;;AAmBA,IAAa,wBAAb,MAAa,8BAA8B,eAAe;CACxD,OAAgB,WAAkC,IAAI,sBAAsB;CAE5E,cAAsB;EACpB,MAAM;GAAE,IAAI;GAAsB,SAAS,EAAE,OAAO,CAAC,EAAE;EAAE,CAAC;CAC5D;AACF;AAEA,SAAgB,qBACd,OACwC;CACxC,IAAI,MAAM,OAAO,sBACf,MAAM,IAAI,MACR,uFAAuF,qBAAqB,eAAe,MAAM,GAAG,IACtI;CAEF,IAAI,OAAO,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,WAAW,GAC9C,OAAO,OAA8B,sBAAsB,QAAQ;CAErE,OAAO,IAAI,eAAe;EAAE,IAAI,MAAM;EAAI,SAAS,MAAM;CAAQ,CAAC;AACpE;;;;;;;;;;;AA8BA,SAAgB,sBACd,OACwC;CACxC,OAAO,qBAAqB,KAAK;AACnC;;;;;;;;;ACzIA,IAAa,2BAAb,cAA8C,0BAAgD;CAC5F,cAAc;EACZ,sBAAM,IAAI,IAAI,CAAC;CACjB;CAEA,yBACE,MACA,KACqC;EACrC,IAAI,eAAe,eACjB,OAAO;EAMT,OAAO,qBAJU,UAGf,MAAM,yBAAyB,MAAM,GAAG,CACP,CAAC;CACtC;AACF"}
1
+ {"version":3,"file":"sqlite-contract-serializer-C41PO7DT.mjs","names":[],"sources":["../src/core/sqlite-unbound-database.ts","../src/core/sqlite-contract-serializer.ts"],"sourcesContent":["import {\n freezeNode,\n type Namespace,\n NamespaceBase,\n UNBOUND_NAMESPACE_ID,\n} from '@prisma-next/framework-components/ir';\nimport {\n type SqlNamespaceTablesInput,\n StorageTable,\n type StorageTableInput,\n} from '@prisma-next/sql-contract/types';\nimport { blindCast, castAs } from '@prisma-next/utils/casts';\n\nexport type SqliteDatabaseInput = {\n readonly id: string;\n readonly entries: {\n readonly table: Readonly<Record<string, StorageTable | StorageTableInput>>;\n };\n};\n\nconst SQLITE_NAMESPACE_KIND = 'sqlite-namespace' as const;\n\nfunction isMaterializedSqliteNamespace(\n ns: Namespace | SqlNamespaceTablesInput,\n): ns is SqliteDatabase | SqliteUnboundDatabase {\n if (typeof ns !== 'object' || ns === null) {\n return false;\n }\n const proto = Object.getPrototypeOf(ns);\n if (proto === Object.prototype || proto === null) {\n return false;\n }\n return (ns as { kind?: unknown }).kind === SQLITE_NAMESPACE_KIND;\n}\n\n/**\n * SQLite namespace concretion carrying table metadata under\n * `entries.table` and unqualified `qualifyTable()` emission for runtime\n * SQL rendering.\n */\nexport class SqliteDatabase extends NamespaceBase {\n declare readonly kind: string;\n\n readonly id: string;\n readonly entries: Readonly<{\n readonly table: Readonly<Record<string, StorageTable>>;\n }>;\n\n constructor(input: SqliteDatabaseInput) {\n super();\n this.id = input.id;\n this.entries = Object.freeze({\n table: Object.freeze(\n Object.fromEntries(\n Object.entries(input.entries.table).map(([k, v]) => [\n k,\n v instanceof StorageTable ? v : new StorageTable(v as StorageTableInput),\n ]),\n ),\n ),\n });\n Object.defineProperty(this, 'kind', {\n value: SQLITE_NAMESPACE_KIND,\n writable: false,\n enumerable: false,\n configurable: true,\n });\n freezeNode(this);\n }\n\n qualifier(): string {\n return '';\n }\n\n qualifyTable(tableName: string): string {\n return `\"${tableName}\"`;\n }\n}\n\n/**\n * SQLite target `Namespace` concretion. SQLite has no schema or\n * database-namespacing concept at the SQL level — there is exactly one\n * effective namespace per connection, so the target ships a single\n * singleton bound to the framework's `UNBOUND_NAMESPACE_ID` slot.\n *\n * Qualifier emission elides the prefix entirely: rendered DDL and\n * queries look unqualified (`CREATE TABLE \"users\" (...)`), matching\n * SQLite's native dialect. Call sites stay polymorphic — they ask the\n * namespace for its qualifier and consume the empty/unqualified result\n * the same way Postgres consumes a `\"schema\"` prefix.\n *\n * The SQLite PSL interpreter rejects every explicit `namespace { … }`\n * block with a diagnostic naming SQLite; only the implicit\n * `__unspecified__` AST bucket reaches the SQLite interpreter, which\n * lowers it to this singleton.\n */\nexport class SqliteUnboundDatabase extends SqliteDatabase {\n static readonly instance: SqliteUnboundDatabase = new SqliteUnboundDatabase();\n\n private constructor() {\n super({ id: UNBOUND_NAMESPACE_ID, entries: { table: {} } });\n }\n}\n\nexport function buildSqliteNamespace(\n input: SqlNamespaceTablesInput,\n): SqliteDatabase | SqliteUnboundDatabase {\n if (input.id !== UNBOUND_NAMESPACE_ID) {\n throw new Error(\n `buildSqliteNamespace: SQLite has no schema concept; the only valid namespace id is \"${UNBOUND_NAMESPACE_ID}\" (received \"${input.id}\").`,\n );\n }\n if (Object.keys(input.entries.table).length === 0) {\n return castAs<SqliteUnboundDatabase>(SqliteUnboundDatabase.instance);\n }\n return new SqliteDatabase({ id: input.id, entries: input.entries });\n}\n\nexport function buildSqliteNamespaceMap(\n namespaces: Readonly<Record<string, Namespace | SqlNamespaceTablesInput>>,\n): Readonly<Record<string, SqliteDatabase | SqliteUnboundDatabase>> {\n return Object.fromEntries(\n Object.entries(namespaces).map(([nsKey, ns]) => [\n nsKey,\n isMaterializedSqliteNamespace(ns)\n ? ns\n : buildSqliteNamespace(\n blindCast<\n SqlNamespaceTablesInput,\n 'non-materialized SQLite namespace map entry is a SqlNamespaceTablesInput'\n >(ns),\n ),\n ]),\n );\n}\n\n/**\n * Target-supplied `Namespace` factory the SQLite target plumbs through\n * `defineContract({ createNamespace })`. SQLite has only one\n * effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`\n * sentinel — so the factory always returns the singleton or a fresh\n * `SqliteDatabase` for the unbound slot with tables. The SQL family's\n * defensive validation in `defineContract` already rejects\n * user-declared SQLite namespaces, so this throw is a structural\n * safety net rather than a user-facing surface.\n */\nexport function sqliteCreateNamespace(\n input: SqlNamespaceTablesInput,\n): SqliteDatabase | SqliteUnboundDatabase {\n return buildSqliteNamespace(input);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';\nimport { type Namespace, NamespaceBase } from '@prisma-next/framework-components/ir';\nimport type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { buildSqliteNamespace } from './sqlite-unbound-database';\n\n/**\n * SQLite target `ContractSerializer` concretion. Mirrors the Postgres\n * shape: inherits the full SQL-family deserialization pipeline and\n * materialises namespace entries as SQLite database concretions that\n * expose `qualifyTable()` for runtime SQL rendering.\n */\nexport class SqliteContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {\n constructor() {\n super(new Map());\n }\n\n protected override hydrateSqlNamespaceEntry(\n nsId: string,\n raw: Namespace | Record<string, unknown>,\n ): Namespace | SqlNamespaceTablesInput {\n if (raw instanceof NamespaceBase) {\n return raw;\n }\n const hydrated = blindCast<\n SqlNamespaceTablesInput,\n 'super.hydrateSqlNamespaceEntry returns the tables form when raw is not a NamespaceBase'\n >(super.hydrateSqlNamespaceEntry(nsId, raw));\n return buildSqliteNamespace(hydrated);\n }\n}\n"],"mappings":";;;;;AAoBA,MAAM,wBAAwB;;;;;;AAoB9B,IAAa,iBAAb,cAAoC,cAAc;CAGhD;CACA;CAIA,YAAY,OAA4B;EACtC,MAAM;EACN,KAAK,KAAK,MAAM;EAChB,KAAK,UAAU,OAAO,OAAO,EAC3B,OAAO,OAAO,OACZ,OAAO,YACL,OAAO,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAClD,GACA,aAAa,eAAe,IAAI,IAAI,aAAa,CAAsB,CACzE,CAAC,CACH,CACF,EACF,CAAC;EACD,OAAO,eAAe,MAAM,QAAQ;GAClC,OAAO;GACP,UAAU;GACV,YAAY;GACZ,cAAc;EAChB,CAAC;EACD,WAAW,IAAI;CACjB;CAEA,YAAoB;EAClB,OAAO;CACT;CAEA,aAAa,WAA2B;EACtC,OAAO,IAAI,UAAU;CACvB;AACF;;;;;;;;;;;;;;;;;;AAmBA,IAAa,wBAAb,MAAa,8BAA8B,eAAe;CACxD,OAAgB,WAAkC,IAAI,sBAAsB;CAE5E,cAAsB;EACpB,MAAM;GAAE,IAAI;GAAsB,SAAS,EAAE,OAAO,CAAC,EAAE;EAAE,CAAC;CAC5D;AACF;AAEA,SAAgB,qBACd,OACwC;CACxC,IAAI,MAAM,OAAO,sBACf,MAAM,IAAI,MACR,uFAAuF,qBAAqB,eAAe,MAAM,GAAG,IACtI;CAEF,IAAI,OAAO,KAAK,MAAM,QAAQ,KAAK,CAAC,CAAC,WAAW,GAC9C,OAAO,OAA8B,sBAAsB,QAAQ;CAErE,OAAO,IAAI,eAAe;EAAE,IAAI,MAAM;EAAI,SAAS,MAAM;CAAQ,CAAC;AACpE;;;;;;;;;;;AA8BA,SAAgB,sBACd,OACwC;CACxC,OAAO,qBAAqB,KAAK;AACnC;;;;;;;;;ACzIA,IAAa,2BAAb,cAA8C,0BAAgD;CAC5F,cAAc;EACZ,sBAAM,IAAI,IAAI,CAAC;CACjB;CAEA,yBACE,MACA,KACqC;EACrC,IAAI,eAAe,eACjB,OAAO;EAMT,OAAO,qBAJU,UAGf,MAAM,yBAAyB,MAAM,GAAG,CACP,CAAC;CACtC;AACF"}
@@ -0,0 +1,73 @@
1
+ import { r as CreateTableCall } from "./op-factory-call-Cc-rDnXB.mjs";
2
+ import { blindCast } from "@prisma-next/utils/casts";
3
+ import { Migration } from "@prisma-next/family-sql/migration";
4
+ import { CliStructuredError } from "@prisma-next/errors/control";
5
+ //#region src/core/errors.ts
6
+ /**
7
+ * A `SqliteMigration` instance method that needs the materialized control
8
+ * adapter (currently only `this.createTable(...)`) was invoked, but the
9
+ * migration was constructed without a `ControlStack`. Concrete authoring
10
+ * usage always goes through the migration CLI entrypoint, which assembles
11
+ * a stack from the loaded `prisma-next.config.ts`; reaching this error
12
+ * means a test fixture or ad-hoc consumer instantiated `SqliteMigration`
13
+ * with the no-arg form (legal for `operations` / `describe` introspection
14
+ * only).
15
+ *
16
+ * Distinct from `PN-MIG-2001` (placeholder not filled) because the missing
17
+ * input is the stack itself, not the per-operation contract.
18
+ *
19
+ * Lives in `@prisma-next/target-sqlite/errors` rather than the shared
20
+ * framework migration errors module because the failure is target-specific:
21
+ * the contract it talks about (`SqliteMigration`, the SQLite control
22
+ * adapter, the SQLite-target stack) only exists in this package.
23
+ */
24
+ function errorSqliteMigrationStackMissing() {
25
+ return new CliStructuredError("2008", "SqliteMigration.createTable requires a control adapter", {
26
+ domain: "MIG",
27
+ why: "SqliteMigration.createTable was invoked on an instance constructed without a ControlStack. The stored controlAdapter is undefined, so createTable cannot lower its DDL node.",
28
+ fix: "Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a SQLite adapter to the migration constructor in test fixtures.",
29
+ meta: {}
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region src/core/migrations/sqlite-migration.ts
34
+ /**
35
+ * Target-owned base class for SQLite migrations. Fixes the `SqlMigration`
36
+ * generic to `SqlitePlanTargetDetails` and the abstract `targetId` to the
37
+ * SQLite literal, so both user-authored migrations and renderer-generated
38
+ * scaffolds can extend `SqliteMigration` directly without redeclaring
39
+ * target-local identity.
40
+ *
41
+ * The constructor materializes a single SQLite `SqlControlAdapter` from
42
+ * `stack.adapter.create(stack)` and stores it; the protected `createTable`
43
+ * instance method forwards to `CreateTableCall` with that stored adapter,
44
+ * so user migrations can write `this.createTable({...})` without threading
45
+ * the adapter through every call.
46
+ */
47
+ var SqliteMigration = class extends Migration {
48
+ targetId = "sqlite";
49
+ /**
50
+ * Materialized SQLite control adapter, created once per migration
51
+ * instance from the injected stack. `undefined` only when the migration
52
+ * was instantiated without a stack (test fixtures); `createTable`
53
+ * throws in that case to surface the misuse.
54
+ */
55
+ controlAdapter;
56
+ constructor(stack) {
57
+ super(stack);
58
+ this.controlAdapter = stack?.adapter ? blindCast(stack.adapter.create(stack)) : void 0;
59
+ }
60
+ /**
61
+ * Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from
62
+ * the supplied options and lowers it through the stored control adapter.
63
+ * Throws if no adapter is present (i.e. migration instantiated without a stack).
64
+ */
65
+ createTable(options) {
66
+ if (!this.controlAdapter) throw errorSqliteMigrationStackMissing();
67
+ return new CreateTableCall(options.table, options.columns, options.constraints).toOp(this.controlAdapter);
68
+ }
69
+ };
70
+ //#endregion
71
+ export { SqliteMigration as t };
72
+
73
+ //# sourceMappingURL=sqlite-migration-CteI6Mrg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-migration-CteI6Mrg.mjs","names":["SqlMigration"],"sources":["../src/core/errors.ts","../src/core/migrations/sqlite-migration.ts"],"sourcesContent":["import { CliStructuredError } from '@prisma-next/errors/control';\n\n/**\n * A `SqliteMigration` instance method that needs the materialized control\n * adapter (currently only `this.createTable(...)`) was invoked, but the\n * migration was constructed without a `ControlStack`. Concrete authoring\n * usage always goes through the migration CLI entrypoint, which assembles\n * a stack from the loaded `prisma-next.config.ts`; reaching this error\n * means a test fixture or ad-hoc consumer instantiated `SqliteMigration`\n * with the no-arg form (legal for `operations` / `describe` introspection\n * only).\n *\n * Distinct from `PN-MIG-2001` (placeholder not filled) because the missing\n * input is the stack itself, not the per-operation contract.\n *\n * Lives in `@prisma-next/target-sqlite/errors` rather than the shared\n * framework migration errors module because the failure is target-specific:\n * the contract it talks about (`SqliteMigration`, the SQLite control\n * adapter, the SQLite-target stack) only exists in this package.\n */\nexport function errorSqliteMigrationStackMissing(): CliStructuredError {\n return new CliStructuredError('2008', 'SqliteMigration.createTable requires a control adapter', {\n domain: 'MIG',\n why: 'SqliteMigration.createTable was invoked on an instance constructed without a ControlStack. The stored controlAdapter is undefined, so createTable cannot lower its DDL node.',\n fix: 'Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a SQLite adapter to the migration constructor in test fixtures.',\n meta: {},\n });\n}\n","import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';\nimport type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';\nimport { Migration as SqlMigration } from '@prisma-next/family-sql/migration';\nimport type { ControlStack } from '@prisma-next/framework-components/control';\nimport type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';\nimport { blindCast } from '@prisma-next/utils/casts';\nimport { errorSqliteMigrationStackMissing } from '../errors';\nimport { CreateTableCall } from './op-factory-call';\nimport type { SqlitePlanTargetDetails } from './planner-target-details';\n\n/**\n * Target-owned base class for SQLite migrations. Fixes the `SqlMigration`\n * generic to `SqlitePlanTargetDetails` and the abstract `targetId` to the\n * SQLite literal, so both user-authored migrations and renderer-generated\n * scaffolds can extend `SqliteMigration` directly without redeclaring\n * target-local identity.\n *\n * The constructor materializes a single SQLite `SqlControlAdapter` from\n * `stack.adapter.create(stack)` and stores it; the protected `createTable`\n * instance method forwards to `CreateTableCall` with that stored adapter,\n * so user migrations can write `this.createTable({...})` without threading\n * the adapter through every call.\n */\nexport abstract class SqliteMigration extends SqlMigration<SqlitePlanTargetDetails, 'sqlite'> {\n readonly targetId = 'sqlite' as const;\n\n /**\n * Materialized SQLite control adapter, created once per migration\n * instance from the injected stack. `undefined` only when the migration\n * was instantiated without a stack (test fixtures); `createTable`\n * throws in that case to surface the misuse.\n */\n protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;\n\n constructor(stack?: ControlStack<'sql', 'sqlite'>) {\n super(stack);\n this.controlAdapter = stack?.adapter\n ? blindCast<\n SqlControlAdapter<'sqlite'>,\n 'The SQLite descriptor create() returns SqlControlAdapter<sqlite>; typed as wider ControlAdapterInstance at the framework boundary'\n >(stack.adapter.create(stack))\n : undefined;\n }\n\n /**\n * Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from\n * the supplied options and lowers it through the stored control adapter.\n * Throws if no adapter is present (i.e. migration instantiated without a stack).\n */\n protected createTable(options: {\n readonly table: string;\n readonly ifNotExists?: boolean;\n readonly columns: readonly DdlColumn[];\n readonly constraints?: readonly DdlTableConstraint[];\n }): SqlMigrationPlanOperation<SqlitePlanTargetDetails> {\n if (!this.controlAdapter) {\n throw errorSqliteMigrationStackMissing();\n }\n return new CreateTableCall(options.table, options.columns, options.constraints).toOp(\n this.controlAdapter,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,mCAAuD;CACrE,OAAO,IAAI,mBAAmB,QAAQ,0DAA0D;EAC9F,QAAQ;EACR,KAAK;EACL,KAAK;EACL,MAAM,CAAC;CACT,CAAC;AACH;;;;;;;;;;;;;;;;ACJA,IAAsB,kBAAtB,cAA8CA,UAAgD;CAC5F,WAAoB;;;;;;;CAQpB;CAEA,YAAY,OAAuC;EACjD,MAAM,KAAK;EACX,KAAK,iBAAiB,OAAO,UACzB,UAGE,MAAM,QAAQ,OAAO,KAAK,CAAC,IAC7B,KAAA;CACN;;;;;;CAOA,YAAsB,SAKiC;EACrD,IAAI,CAAC,KAAK,gBACR,MAAM,iCAAiC;EAEzC,OAAO,IAAI,gBAAgB,QAAQ,OAAO,QAAQ,SAAS,QAAQ,WAAW,CAAC,CAAC,KAC9E,KAAK,cACP;CACF;AACF"}
@@ -0,0 +1,46 @@
1
+ import { t as SqlitePlanTargetDetails } from "./planner-target-details-xR6UfIcz.mjs";
2
+ import { DdlColumn, DdlTableConstraint } from "@prisma-next/sql-relational-core/ast";
3
+ import { SqlMigrationPlanOperation } from "@prisma-next/family-sql/control";
4
+ import { Migration } from "@prisma-next/family-sql/migration";
5
+ import { ControlStack } from "@prisma-next/framework-components/control";
6
+ import { SqlControlAdapter } from "@prisma-next/family-sql/control-adapter";
7
+
8
+ //#region src/core/migrations/sqlite-migration.d.ts
9
+ /**
10
+ * Target-owned base class for SQLite migrations. Fixes the `SqlMigration`
11
+ * generic to `SqlitePlanTargetDetails` and the abstract `targetId` to the
12
+ * SQLite literal, so both user-authored migrations and renderer-generated
13
+ * scaffolds can extend `SqliteMigration` directly without redeclaring
14
+ * target-local identity.
15
+ *
16
+ * The constructor materializes a single SQLite `SqlControlAdapter` from
17
+ * `stack.adapter.create(stack)` and stores it; the protected `createTable`
18
+ * instance method forwards to `CreateTableCall` with that stored adapter,
19
+ * so user migrations can write `this.createTable({...})` without threading
20
+ * the adapter through every call.
21
+ */
22
+ declare abstract class SqliteMigration extends Migration<SqlitePlanTargetDetails, 'sqlite'> {
23
+ readonly targetId: "sqlite";
24
+ /**
25
+ * Materialized SQLite control adapter, created once per migration
26
+ * instance from the injected stack. `undefined` only when the migration
27
+ * was instantiated without a stack (test fixtures); `createTable`
28
+ * throws in that case to surface the misuse.
29
+ */
30
+ protected readonly controlAdapter: SqlControlAdapter<'sqlite'> | undefined;
31
+ constructor(stack?: ControlStack<'sql', 'sqlite'>);
32
+ /**
33
+ * Emit a `CREATE TABLE` migration operation. Builds a typed DDL node from
34
+ * the supplied options and lowers it through the stored control adapter.
35
+ * Throws if no adapter is present (i.e. migration instantiated without a stack).
36
+ */
37
+ protected createTable(options: {
38
+ readonly table: string;
39
+ readonly ifNotExists?: boolean;
40
+ readonly columns: readonly DdlColumn[];
41
+ readonly constraints?: readonly DdlTableConstraint[];
42
+ }): SqlMigrationPlanOperation<SqlitePlanTargetDetails>;
43
+ }
44
+ //#endregion
45
+ export { SqliteMigration as t };
46
+ //# sourceMappingURL=sqlite-migration-znjM-dc_.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-migration-znjM-dc_.d.mts","names":[],"sources":["../src/core/migrations/sqlite-migration.ts"],"mappings":";;;;;;;;;;AAuBA;;;;;;;;;;;uBAAsB,eAAA,SAAwB,SAAA,CAAa,uBAAA;EAAA,SAChD,QAAA;EADmC;;;;;;EAAA,mBASzB,cAAA,EAAgB,iBAAA;cAEvB,KAAA,GAAQ,YAAA;EAeV;;;;;EAAA,UAAA,WAAA,CAAY,OAAA;IAAA,SACX,KAAA;IAAA,SACA,WAAA;IAAA,SACA,OAAA,WAAkB,SAAA;IAAA,SAClB,WAAA,YAAuB,kBAAA;EAAA,IAC9B,yBAAA,CAA0B,uBAAA;AAAA"}
package/package.json CHANGED
@@ -1,30 +1,30 @@
1
1
  {
2
2
  "name": "@prisma-next/target-sqlite",
3
- "version": "0.13.0-dev.13",
3
+ "version": "0.13.0-dev.15",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "dependencies": {
8
- "@prisma-next/cli": "0.13.0-dev.13",
9
- "@prisma-next/contract": "0.13.0-dev.13",
10
- "@prisma-next/errors": "0.13.0-dev.13",
11
- "@prisma-next/family-sql": "0.13.0-dev.13",
12
- "@prisma-next/framework-components": "0.13.0-dev.13",
13
- "@prisma-next/migration-tools": "0.13.0-dev.13",
14
- "@prisma-next/sql-contract": "0.13.0-dev.13",
15
- "@prisma-next/sql-errors": "0.13.0-dev.13",
16
- "@prisma-next/sql-relational-core": "0.13.0-dev.13",
17
- "@prisma-next/sql-runtime": "0.13.0-dev.13",
18
- "@prisma-next/sql-schema-ir": "0.13.0-dev.13",
19
- "@prisma-next/ts-render": "0.13.0-dev.13",
20
- "@prisma-next/utils": "0.13.0-dev.13",
8
+ "@prisma-next/cli": "0.13.0-dev.15",
9
+ "@prisma-next/contract": "0.13.0-dev.15",
10
+ "@prisma-next/errors": "0.13.0-dev.15",
11
+ "@prisma-next/family-sql": "0.13.0-dev.15",
12
+ "@prisma-next/framework-components": "0.13.0-dev.15",
13
+ "@prisma-next/migration-tools": "0.13.0-dev.15",
14
+ "@prisma-next/sql-contract": "0.13.0-dev.15",
15
+ "@prisma-next/sql-errors": "0.13.0-dev.15",
16
+ "@prisma-next/sql-relational-core": "0.13.0-dev.15",
17
+ "@prisma-next/sql-runtime": "0.13.0-dev.15",
18
+ "@prisma-next/sql-schema-ir": "0.13.0-dev.15",
19
+ "@prisma-next/ts-render": "0.13.0-dev.15",
20
+ "@prisma-next/utils": "0.13.0-dev.15",
21
21
  "@standard-schema/spec": "1.1.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@prisma-next/driver-sqlite": "0.13.0-dev.13",
25
- "@prisma-next/test-utils": "0.13.0-dev.13",
26
- "@prisma-next/tsconfig": "0.13.0-dev.13",
27
- "@prisma-next/tsdown": "0.13.0-dev.13",
24
+ "@prisma-next/driver-sqlite": "0.13.0-dev.15",
25
+ "@prisma-next/test-utils": "0.13.0-dev.15",
26
+ "@prisma-next/tsconfig": "0.13.0-dev.15",
27
+ "@prisma-next/tsdown": "0.13.0-dev.15",
28
28
  "tsdown": "0.22.1",
29
29
  "typescript": "5.9.3",
30
30
  "vitest": "4.1.8"
@@ -36,8 +36,8 @@ const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', Sqlite
36
36
  contractSerializer: new SqliteContractSerializer(),
37
37
  schemaVerifier: new SqliteSchemaVerifier(),
38
38
  migrations: {
39
- createPlanner(_adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {
40
- return createSqliteMigrationPlanner();
39
+ createPlanner(adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {
40
+ return createSqliteMigrationPlanner(adapter);
41
41
  },
42
42
  createRunner(family) {
43
43
  return createSqliteMigrationRunner(family) as MigrationRunner<'sql', 'sqlite'>;
@@ -67,8 +67,8 @@ const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', Sqlite
67
67
  targetId: 'sqlite',
68
68
  };
69
69
  },
70
- createPlanner(_adapter: SqlControlAdapter<'sqlite'>) {
71
- return createSqliteMigrationPlanner();
70
+ createPlanner(adapter: SqlControlAdapter<'sqlite'>) {
71
+ return createSqliteMigrationPlanner(adapter);
72
72
  },
73
73
  createRunner(family) {
74
74
  return createSqliteMigrationRunner(family);
@@ -0,0 +1,28 @@
1
+ import { CliStructuredError } from '@prisma-next/errors/control';
2
+
3
+ /**
4
+ * A `SqliteMigration` instance method that needs the materialized control
5
+ * adapter (currently only `this.createTable(...)`) was invoked, but the
6
+ * migration was constructed without a `ControlStack`. Concrete authoring
7
+ * usage always goes through the migration CLI entrypoint, which assembles
8
+ * a stack from the loaded `prisma-next.config.ts`; reaching this error
9
+ * means a test fixture or ad-hoc consumer instantiated `SqliteMigration`
10
+ * with the no-arg form (legal for `operations` / `describe` introspection
11
+ * only).
12
+ *
13
+ * Distinct from `PN-MIG-2001` (placeholder not filled) because the missing
14
+ * input is the stack itself, not the per-operation contract.
15
+ *
16
+ * Lives in `@prisma-next/target-sqlite/errors` rather than the shared
17
+ * framework migration errors module because the failure is target-specific:
18
+ * the contract it talks about (`SqliteMigration`, the SQLite control
19
+ * adapter, the SQLite-target stack) only exists in this package.
20
+ */
21
+ export function errorSqliteMigrationStackMissing(): CliStructuredError {
22
+ return new CliStructuredError('2008', 'SqliteMigration.createTable requires a control adapter', {
23
+ domain: 'MIG',
24
+ why: 'SqliteMigration.createTable was invoked on an instance constructed without a ControlStack. The stored controlAdapter is undefined, so createTable cannot lower its DDL node.',
25
+ fix: 'Construct the migration via the migration CLI entrypoint (which assembles a ControlStack from the loaded prisma-next.config.ts), or pass a ControlStack containing a SQLite adapter to the migration constructor in test fixtures.',
26
+ meta: {},
27
+ });
28
+ }
@@ -24,8 +24,19 @@ import type {
24
24
  StorageTable,
25
25
  StorageTypeInstance,
26
26
  } from '@prisma-next/sql-contract/types';
27
+ import type { DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
28
+ import {
29
+ DdlColumn,
30
+ ForeignKeyConstraint,
31
+ FunctionColumnDefault,
32
+ LiteralColumnDefault,
33
+ PrimaryKeyConstraint,
34
+ UniqueConstraint,
35
+ } from '@prisma-next/sql-relational-core/ast';
27
36
  import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
28
37
  import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
38
+ import { blindCast } from '@prisma-next/utils/casts';
39
+ import { ifDefined } from '@prisma-next/utils/defined';
29
40
  import type { Result } from '@prisma-next/utils/result';
30
41
  import { notOk, ok } from '@prisma-next/utils/result';
31
42
  import { CONTROL_TABLE_NAMES } from '../control-tables';
@@ -256,6 +267,98 @@ export function toTableSpec(
256
267
  };
257
268
  }
258
269
 
270
+ // ============================================================================
271
+ // StorageTable / StorageColumn → DdlColumn[] + DdlTableConstraint[] (for CreateTableCall)
272
+ // ============================================================================
273
+
274
+ function sqliteDefaultToDdlColumnDefault(
275
+ columnDefault: StorageColumn['default'],
276
+ ): DdlColumn['default'] {
277
+ if (!columnDefault) return undefined;
278
+ switch (columnDefault.kind) {
279
+ case 'literal':
280
+ return new LiteralColumnDefault(columnDefault.value);
281
+ case 'function':
282
+ // `autoincrement()` is not a DEFAULT clause — SQLite encodes it as
283
+ // `INTEGER PRIMARY KEY AUTOINCREMENT` inline on the column. Skip it
284
+ // here; the renderer also has a defensive guard for the same case.
285
+ if (columnDefault.expression === 'autoincrement()') return undefined;
286
+ return new FunctionColumnDefault(columnDefault.expression);
287
+ default: {
288
+ const exhaustive: never = columnDefault;
289
+ throw new Error(
290
+ `sqliteDefaultToDdlColumnDefault: unhandled kind "${blindCast<{ kind: string }, 'exhaustiveness: surface the unhandled default kind'>(exhaustive).kind}"`,
291
+ );
292
+ }
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Converts a `StorageTable` to the `DdlColumn[]` + `DdlTableConstraint[]`
298
+ * pair used by `CreateTableCall`. This is the structured form consumed by
299
+ * the DDL lowering path; `toTableSpec` / `toColumnSpec` remain in use for
300
+ * `RecreateTableCall` and `AddColumnCall` (Phase 2).
301
+ */
302
+ export function tableToDdlParts(
303
+ table: StorageTable,
304
+ storageTypes: Record<string, StorageTypeInstance | PostgresEnumStorageEntry>,
305
+ ): { columns: DdlColumn[]; constraints: DdlTableConstraint[] } {
306
+ const columns: DdlColumn[] = Object.entries(table.columns).map(([name, column]) => {
307
+ const inlineAutoincrement = isInlineAutoincrementPrimaryKey(table, name);
308
+ const typeSql = buildColumnTypeSql(column, storageTypes);
309
+ if (inlineAutoincrement) {
310
+ // `DdlColumn` has no SQLite-specific autoincrement flag, so the full
311
+ // `PRIMARY KEY AUTOINCREMENT` clause is embedded in the `type` string.
312
+ // The DDL renderer (`ddl-renderer.ts`) substring-detects `AUTOINCREMENT`
313
+ // to suppress the normal NOT NULL / PRIMARY KEY / DEFAULT clause rendering
314
+ // and emit the entire type string verbatim. Both sites must stay in sync.
315
+ // The structural fix (a SQLite-specific column option) is tracked in TML-2866.
316
+ return new DdlColumn({ name, type: `${typeSql} PRIMARY KEY AUTOINCREMENT` });
317
+ }
318
+ const colDefault = sqliteDefaultToDdlColumnDefault(column.default);
319
+ return new DdlColumn({
320
+ name,
321
+ type: typeSql,
322
+ ...(!column.nullable ? { notNull: true } : {}),
323
+ ...(colDefault !== undefined ? { default: colDefault } : {}),
324
+ });
325
+ });
326
+
327
+ const constraints: DdlTableConstraint[] = [];
328
+
329
+ const hasInlinePk = Object.entries(table.columns).some(([name]) =>
330
+ isInlineAutoincrementPrimaryKey(table, name),
331
+ );
332
+ if (table.primaryKey && !hasInlinePk) {
333
+ constraints.push(new PrimaryKeyConstraint({ columns: table.primaryKey.columns }));
334
+ }
335
+
336
+ for (const u of table.uniques) {
337
+ constraints.push(
338
+ new UniqueConstraint({
339
+ columns: u.columns,
340
+ ...(u.name !== undefined ? { name: u.name } : {}),
341
+ }),
342
+ );
343
+ }
344
+
345
+ for (const fk of table.foreignKeys) {
346
+ if (fk.constraint === false) continue;
347
+ constraints.push(
348
+ new ForeignKeyConstraint({
349
+ columns: fk.source.columns,
350
+ refTable: fk.target.tableName,
351
+ refColumns: fk.target.columns,
352
+ ...ifDefined('name', fk.name),
353
+ ...ifDefined('onDelete', fk.onDelete),
354
+ ...ifDefined('onUpdate', fk.onUpdate),
355
+ }),
356
+ );
357
+ }
358
+
359
+ return { columns, constraints };
360
+ }
361
+
259
362
  // ============================================================================
260
363
  // Issue planner
261
364
  // ============================================================================
@@ -309,8 +412,17 @@ function mapIssueToCall(
309
412
  ),
310
413
  );
311
414
  }
312
- const tableSpec = toTableSpec(contractTable, ctx.storageTypes);
313
- const calls: SqliteOpFactoryCall[] = [new CreateTableCall(issue.table, tableSpec)];
415
+ const { columns: ddlColumns, constraints: ddlConstraints } = tableToDdlParts(
416
+ contractTable,
417
+ ctx.storageTypes,
418
+ );
419
+ const calls: SqliteOpFactoryCall[] = [
420
+ new CreateTableCall(
421
+ issue.table,
422
+ ddlColumns,
423
+ ddlConstraints.length > 0 ? ddlConstraints : undefined,
424
+ ),
425
+ ];
314
426
  const declaredIndexColumnKeys = new Set<string>();
315
427
  for (const index of contractTable.indexes) {
316
428
  const indexName = index.name ?? defaultIndexName(issue.table, index.columns);