@happyvertical/smrt-core 0.40.69 → 0.41.0

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 (237) hide show
  1. package/AGENTS.md +29 -4
  2. package/README.md +20 -1
  3. package/agents/change-feed.md +1 -1
  4. package/agents/query-bounds.md +45 -0
  5. package/agents/schema-paths.md +786 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +5 -3
  9. package/dist/cascade.d.ts +120 -0
  10. package/dist/cascade.d.ts.map +1 -0
  11. package/dist/cascade.js +430 -0
  12. package/dist/cascade.js.map +1 -0
  13. package/dist/change-feed.d.ts +34 -2
  14. package/dist/change-feed.d.ts.map +1 -1
  15. package/dist/change-feed.js +52 -11
  16. package/dist/change-feed.js.map +1 -1
  17. package/dist/class.d.ts +36 -3
  18. package/dist/class.d.ts.map +1 -1
  19. package/dist/class.js +87 -9
  20. package/dist/class.js.map +1 -1
  21. package/dist/collection-cache.js +0 -0
  22. package/dist/collection-cache.js.map +1 -1
  23. package/dist/collection.d.ts +130 -2
  24. package/dist/collection.d.ts.map +1 -1
  25. package/dist/collection.js +290 -57
  26. package/dist/collection.js.map +1 -1
  27. package/dist/config.d.ts +10 -0
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/database.d.ts +8 -0
  31. package/dist/database.d.ts.map +1 -1
  32. package/dist/database.js +16 -8
  33. package/dist/database.js.map +1 -1
  34. package/dist/db-errors.d.ts +105 -0
  35. package/dist/db-errors.d.ts.map +1 -0
  36. package/dist/db-errors.js +382 -0
  37. package/dist/db-errors.js.map +1 -0
  38. package/dist/decorators/index.d.ts +80 -6
  39. package/dist/decorators/index.d.ts.map +1 -1
  40. package/dist/decorators/index.js +102 -12
  41. package/dist/decorators/index.js.map +1 -1
  42. package/dist/dispatch/bus.d.ts.map +1 -1
  43. package/dist/dispatch/bus.js +4 -3
  44. package/dist/dispatch/bus.js.map +1 -1
  45. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  46. package/dist/dispatch/collections/Dispatches.js +19 -4
  47. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  48. package/dist/dispatch/types.d.ts +5 -0
  49. package/dist/dispatch/types.d.ts.map +1 -1
  50. package/dist/embedded-write-queue.d.ts +46 -0
  51. package/dist/embedded-write-queue.d.ts.map +1 -0
  52. package/dist/embedded-write-queue.js +66 -0
  53. package/dist/embedded-write-queue.js.map +1 -0
  54. package/dist/embeddings/storage.d.ts +7 -0
  55. package/dist/embeddings/storage.d.ts.map +1 -1
  56. package/dist/embeddings/storage.js +29 -12
  57. package/dist/embeddings/storage.js.map +1 -1
  58. package/dist/errors.d.ts +31 -3
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +34 -2
  61. package/dist/errors.js.map +1 -1
  62. package/dist/generators/changes-route.d.ts.map +1 -1
  63. package/dist/generators/changes-route.js +6 -3
  64. package/dist/generators/changes-route.js.map +1 -1
  65. package/dist/generators/mcp-runtime-template.d.ts +8 -0
  66. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  67. package/dist/generators/mcp-runtime-template.js +38 -4
  68. package/dist/generators/mcp-runtime-template.js.map +1 -1
  69. package/dist/generators/mcp.d.ts +16 -0
  70. package/dist/generators/mcp.d.ts.map +1 -1
  71. package/dist/generators/mcp.js +41 -3
  72. package/dist/generators/mcp.js.map +1 -1
  73. package/dist/generators/rest.d.ts +22 -0
  74. package/dist/generators/rest.d.ts.map +1 -1
  75. package/dist/generators/rest.js +34 -3
  76. package/dist/generators/rest.js.map +1 -1
  77. package/dist/hierarchical.js +1 -1
  78. package/dist/index.d.ts +7 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +12 -5
  81. package/dist/interceptors.d.ts +21 -0
  82. package/dist/interceptors.d.ts.map +1 -1
  83. package/dist/interceptors.js +27 -1
  84. package/dist/interceptors.js.map +1 -1
  85. package/dist/manifest/generator.d.ts.map +1 -1
  86. package/dist/manifest/generator.js +4 -7
  87. package/dist/manifest/generator.js.map +1 -1
  88. package/dist/manifest/static-manifest.js +10 -10
  89. package/dist/manifest/static-manifest.js.map +1 -1
  90. package/dist/manifest/store.js +1 -1
  91. package/dist/manifest/store.js.map +1 -1
  92. package/dist/manifest.json +19 -19
  93. package/dist/migrations/differ.d.ts +211 -9
  94. package/dist/migrations/differ.d.ts.map +1 -1
  95. package/dist/migrations/differ.js +613 -50
  96. package/dist/migrations/differ.js.map +1 -1
  97. package/dist/migrations/generator.d.ts +31 -4
  98. package/dist/migrations/generator.d.ts.map +1 -1
  99. package/dist/migrations/generator.js +49 -5
  100. package/dist/migrations/generator.js.map +1 -1
  101. package/dist/migrations/index.d.ts +4 -2
  102. package/dist/migrations/index.d.ts.map +1 -1
  103. package/dist/migrations/index.js +6 -3
  104. package/dist/migrations/minor-units.d.ts +162 -0
  105. package/dist/migrations/minor-units.d.ts.map +1 -0
  106. package/dist/migrations/minor-units.js +381 -0
  107. package/dist/migrations/minor-units.js.map +1 -0
  108. package/dist/migrations/orchestrate.js +35 -6
  109. package/dist/migrations/orchestrate.js.map +1 -1
  110. package/dist/migrations/sqlite-rebuild.d.ts +142 -0
  111. package/dist/migrations/sqlite-rebuild.d.ts.map +1 -0
  112. package/dist/migrations/sqlite-rebuild.js +514 -0
  113. package/dist/migrations/sqlite-rebuild.js.map +1 -0
  114. package/dist/migrations/tracker.d.ts +114 -1
  115. package/dist/migrations/tracker.d.ts.map +1 -1
  116. package/dist/migrations/tracker.js +331 -16
  117. package/dist/migrations/tracker.js.map +1 -1
  118. package/dist/migrations/types.d.ts +19 -4
  119. package/dist/migrations/types.d.ts.map +1 -1
  120. package/dist/migrations.js +6 -3
  121. package/dist/object.d.ts +142 -10
  122. package/dist/object.d.ts.map +1 -1
  123. package/dist/object.js +196 -41
  124. package/dist/object.js.map +1 -1
  125. package/dist/postgres-timeouts.d.ts +240 -0
  126. package/dist/postgres-timeouts.d.ts.map +1 -0
  127. package/dist/postgres-timeouts.js +204 -0
  128. package/dist/postgres-timeouts.js.map +1 -0
  129. package/dist/query-bounds.d.ts +101 -0
  130. package/dist/query-bounds.d.ts.map +1 -0
  131. package/dist/query-bounds.js +177 -0
  132. package/dist/query-bounds.js.map +1 -0
  133. package/dist/registry/class-registration.d.ts.map +1 -1
  134. package/dist/registry/class-registration.js +3 -1
  135. package/dist/registry/class-registration.js.map +1 -1
  136. package/dist/registry/manifest-field-merge.d.ts +12 -0
  137. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  138. package/dist/registry/manifest-field-merge.js +14 -2
  139. package/dist/registry/manifest-field-merge.js.map +1 -1
  140. package/dist/registry/schema-builder.d.ts +22 -1
  141. package/dist/registry/schema-builder.d.ts.map +1 -1
  142. package/dist/registry/schema-builder.js +205 -165
  143. package/dist/registry/schema-builder.js.map +1 -1
  144. package/dist/registry/types.d.ts +35 -3
  145. package/dist/registry/types.d.ts.map +1 -1
  146. package/dist/registry.d.ts +41 -46
  147. package/dist/registry.d.ts.map +1 -1
  148. package/dist/registry.js +61 -83
  149. package/dist/registry.js.map +1 -1
  150. package/dist/scanner/manifest-generator.d.ts +45 -0
  151. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  152. package/dist/scanner/manifest-generator.js +92 -28
  153. package/dist/scanner/manifest-generator.js.map +1 -1
  154. package/dist/scanner/types.d.ts +5 -0
  155. package/dist/scanner/types.d.ts.map +1 -1
  156. package/dist/scanner/types.js.map +1 -1
  157. package/dist/schema/conflict-target.d.ts +104 -0
  158. package/dist/schema/conflict-target.d.ts.map +1 -0
  159. package/dist/schema/conflict-target.js +129 -0
  160. package/dist/schema/conflict-target.js.map +1 -0
  161. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  162. package/dist/schema/ddl/base-strategy.js +2 -2
  163. package/dist/schema/ddl/base-strategy.js.map +1 -1
  164. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  165. package/dist/schema/ddl/duckdb-strategy.js +2 -1
  166. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  167. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  168. package/dist/schema/ddl/postgres-strategy.js +12 -1
  169. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  170. package/dist/schema/generator.d.ts +307 -6
  171. package/dist/schema/generator.d.ts.map +1 -1
  172. package/dist/schema/generator.js +510 -87
  173. package/dist/schema/generator.js.map +1 -1
  174. package/dist/schema/index-utils.d.ts +120 -0
  175. package/dist/schema/index-utils.d.ts.map +1 -1
  176. package/dist/schema/index-utils.js +242 -1
  177. package/dist/schema/index-utils.js.map +1 -1
  178. package/dist/schema/index.d.ts +3 -0
  179. package/dist/schema/index.d.ts.map +1 -1
  180. package/dist/schema/index.js +4 -1
  181. package/dist/schema/live-parity.d.ts +90 -0
  182. package/dist/schema/live-parity.d.ts.map +1 -0
  183. package/dist/schema/live-parity.js +602 -0
  184. package/dist/schema/live-parity.js.map +1 -0
  185. package/dist/schema/manifest-schema.d.ts +121 -0
  186. package/dist/schema/manifest-schema.d.ts.map +1 -0
  187. package/dist/schema/manifest-schema.js +267 -0
  188. package/dist/schema/manifest-schema.js.map +1 -0
  189. package/dist/schema/schema-aggregator.d.ts +24 -10
  190. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  191. package/dist/schema/schema-aggregator.js +35 -90
  192. package/dist/schema/schema-aggregator.js.map +1 -1
  193. package/dist/schema/system-table-shapes.d.ts +65 -0
  194. package/dist/schema/system-table-shapes.d.ts.map +1 -0
  195. package/dist/schema/system-table-shapes.js +187 -0
  196. package/dist/schema/system-table-shapes.js.map +1 -0
  197. package/dist/schema/types.d.ts +103 -4
  198. package/dist/schema/types.d.ts.map +1 -1
  199. package/dist/schema/utils.d.ts +2 -1
  200. package/dist/schema/utils.d.ts.map +1 -1
  201. package/dist/schema/utils.js +5 -3
  202. package/dist/schema/utils.js.map +1 -1
  203. package/dist/schema.js +4 -1
  204. package/dist/smrt-knowledge.json +20 -8
  205. package/dist/sync/apply.d.ts.map +1 -1
  206. package/dist/sync/apply.js +9 -16
  207. package/dist/sync/apply.js.map +1 -1
  208. package/dist/system/compatibility.d.ts +42 -0
  209. package/dist/system/compatibility.d.ts.map +1 -1
  210. package/dist/system/compatibility.js +182 -9
  211. package/dist/system/compatibility.js.map +1 -1
  212. package/dist/system/index.d.ts +1 -0
  213. package/dist/system/index.d.ts.map +1 -1
  214. package/dist/system/index.js +3 -2
  215. package/dist/system/retention.d.ts +237 -0
  216. package/dist/system/retention.d.ts.map +1 -0
  217. package/dist/system/retention.js +497 -0
  218. package/dist/system/retention.js.map +1 -0
  219. package/dist/system/schema.d.ts +100 -15
  220. package/dist/system/schema.d.ts.map +1 -1
  221. package/dist/system/schema.js +81 -45
  222. package/dist/system/schema.js.map +1 -1
  223. package/dist/system/types.d.ts +0 -2
  224. package/dist/system/types.d.ts.map +1 -1
  225. package/dist/testing/database.d.ts.map +1 -1
  226. package/dist/testing/database.js +1 -0
  227. package/dist/testing/database.js.map +1 -1
  228. package/dist/vite-plugin/index.d.ts.map +1 -1
  229. package/dist/vite-plugin/index.js +4 -9
  230. package/dist/vite-plugin/index.js.map +1 -1
  231. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  232. package/dist/vite-plugin/sveltekit-generator.js +71 -5
  233. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  234. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  235. package/dist/vite-plugin/web-collections.js +6 -4
  236. package/dist/vite-plugin/web-collections.js.map +1 -1
  237. package/package.json +5 -5
@@ -0,0 +1,142 @@
1
+ import { SchemaChange, SQLDataType } from '../schema/types.js';
2
+ import { DatabaseInterface } from './types.js';
3
+ /**
4
+ * Prefix of the reserved staging table a rebuild creates. `_smrt_`-prefixed
5
+ * names are SMRT system tables, which the differ's dropped-table sweep skips,
6
+ * so a staging table left behind by a non-transactional adapter cannot be
7
+ * mistaken for user data.
8
+ */
9
+ export declare const SQLITE_REBUILD_TABLE_PREFIX = "_smrt_rebuild_";
10
+ /**
11
+ * The advisory SQL the differ emits for a SQLite type upgrade it cannot
12
+ * perform with an `ALTER TABLE`. `planSqliteTableRebuilds` replaces it with an
13
+ * executable plan; when it cannot, the comment survives and `db:migrate`
14
+ * reports the column as manual drift exactly as it did before #2370.
15
+ */
16
+ export declare function sqliteRebuildPlaceholderSql(columnName: string): string;
17
+ /**
18
+ * True when `sql` is the differ's "requires table recreation" placeholder —
19
+ * i.e. a type upgrade waiting for this module to turn it into a rebuild.
20
+ */
21
+ export declare function isSqliteRebuildPlaceholder(sql: string | undefined): boolean;
22
+ /** A rebuild plan: the ordered statements plus what they touch. */
23
+ export interface SqliteTableRebuildPlan {
24
+ /** Ordered, executable statements. Run them in one transaction. */
25
+ statements: string[];
26
+ /** Columns copied from the old table into the new one, in table order. */
27
+ copiedColumns: string[];
28
+ /** Names of the indexes and triggers recreated after the swap. */
29
+ recreatedObjects: string[];
30
+ }
31
+ export interface PlanSqliteTableRebuildsOptions {
32
+ db: DatabaseInterface;
33
+ tableName: string;
34
+ /** The table's changes, as produced by the differ's column comparison. */
35
+ changes: SchemaChange[];
36
+ /** Abstract → SQLite type mapping (the engine's DDL strategy `mapType`). */
37
+ mapType: (type: SQLDataType) => string;
38
+ }
39
+ /**
40
+ * Replace every "requires table recreation" placeholder for `tableName` with
41
+ * one executable rebuild.
42
+ *
43
+ * All of the table's drifted columns are retyped by a *single* rebuild, so the
44
+ * first placeholder carries the plan and the rest become no-ops that reference
45
+ * it. Returns the changes unchanged when there is nothing to rebuild, and
46
+ * leaves the placeholders in place (annotated with the reason) when the
47
+ * rebuild cannot be planned safely — the pre-#2370 manual-intervention
48
+ * behavior.
49
+ *
50
+ * When a plan is produced, the rebuild is hoisted to the front of the table's
51
+ * changes. The plan's staging DDL and copy list are captured from the live
52
+ * schema *at diff time*, so a same-batch `ALTER TABLE ... ADD COLUMN` that ran
53
+ * first would be silently undone by the rebuild — both statements succeed, the
54
+ * transaction commits, and the new column is simply gone. The differ emits
55
+ * changes in manifest field order, which puts an `add_column` first whenever
56
+ * the new field is declared above the retyped one, so the order has to be
57
+ * imposed here rather than assumed. Rebuild first, then add columns to the
58
+ * rebuilt table.
59
+ */
60
+ export declare function planSqliteTableRebuilds(options: PlanSqliteTableRebuildsOptions): Promise<SchemaChange[]>;
61
+ export interface BuildSqliteRebuildStatementsInput {
62
+ tableName: string;
63
+ /** The live `sqlite_master.sql` CREATE TABLE text. */
64
+ createTableSql: string;
65
+ /** Column name → target SQLite type, for the columns being retyped. */
66
+ columnTypes: Map<string, string>;
67
+ /** Every column of the live table, in `PRAGMA table_info` order. */
68
+ columns: string[];
69
+ /** Indexes and triggers to replay after the swap. */
70
+ objects: {
71
+ name: string;
72
+ sql: string;
73
+ }[];
74
+ /** Current `PRAGMA legacy_alter_table` value, restored after the rename. */
75
+ legacyAlterTable: boolean;
76
+ }
77
+ /**
78
+ * Build the ordered statement list for one table rebuild.
79
+ *
80
+ * Returns `null` when the live `CREATE TABLE` text cannot be rewritten (an
81
+ * unparsable statement, or a requested column that is not in it) — the caller
82
+ * then leaves the change as manual drift rather than emitting SQL that would
83
+ * "succeed" without fixing the column, which would re-diff forever.
84
+ *
85
+ * `PRAGMA legacy_alter_table` brackets the rename because SQLite ≥ 3.25
86
+ * re-parses the whole schema on `ALTER TABLE ... RENAME`: a view left
87
+ * referencing the just-dropped table makes the rename fail outright
88
+ * ("error in view … no such table"). Legacy mode renames the table without
89
+ * that reference rewriting, which is exactly what a rebuild wants — the name
90
+ * the views refer to is about to exist again. It is restored to its previous
91
+ * value immediately afterwards; a rolled-back batch leaves it set, which is
92
+ * inert for SMRT (nothing else in the toolchain renames a table) but is
93
+ * documented in `packages/core/agents/schema-paths.md`.
94
+ */
95
+ export declare function buildSqliteRebuildStatements(input: BuildSqliteRebuildStatementsInput): SqliteTableRebuildPlan | null;
96
+ /**
97
+ * Rewrite a live `CREATE TABLE` statement into the rebuild's target shape:
98
+ * the same body under a new table name, with the declared type of each column
99
+ * in `columnTypes` replaced.
100
+ *
101
+ * Everything else is preserved verbatim — other columns, their defaults,
102
+ * `NOT NULL`/`PRIMARY KEY`/`CHECK`/`COLLATE` clauses, table-level
103
+ * constraints, and trailing table options (`WITHOUT ROWID`, `STRICT`).
104
+ *
105
+ * Returns `null` if the statement cannot be parsed or if any requested column
106
+ * is not present in it.
107
+ */
108
+ export declare function rewriteSqliteCreateTable(createTableSql: string, target: {
109
+ tableName: string;
110
+ columnTypes: Map<string, string>;
111
+ }): string | null;
112
+ interface ParsedCreateTable {
113
+ /** Top-level, comma-separated column and constraint definitions. */
114
+ items: string[];
115
+ /** Table options after the closing paren (`WITHOUT ROWID`, `STRICT`). */
116
+ tail: string;
117
+ }
118
+ /**
119
+ * Split a `CREATE TABLE` statement into its top-level definition items plus
120
+ * any trailing table options, honoring SQLite's quoting (`'…'`, `"…"`,
121
+ * `` `…` ``, `[…]`) and comments so a quoted comma or paren cannot split the
122
+ * body.
123
+ *
124
+ * @internal exported for testing
125
+ */
126
+ export declare function parseCreateTableBody(sql: string): ParsedCreateTable | null;
127
+ /**
128
+ * The column name of a body item, or `null` when the item is a table-level
129
+ * constraint rather than a column definition.
130
+ *
131
+ * @internal exported for testing
132
+ */
133
+ export declare function readColumnDefinitionName(item: string): string | null;
134
+ /**
135
+ * Replace the declared type of a column definition, keeping its name and every
136
+ * constraint that follows. A column declared without a type gets one inserted.
137
+ *
138
+ * @internal exported for testing
139
+ */
140
+ export declare function replaceColumnType(item: string, newType: string): string | null;
141
+ export {};
142
+ //# sourceMappingURL=sqlite-rebuild.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-rebuild.d.ts","sourceRoot":"","sources":["../../src/migrations/sqlite-rebuild.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAIpD;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,mBAAmB,CAAC;AAE5D;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE3E;AAcD,mEAAmE;AACnE,MAAM,WAAW,sBAAsB;IACrC,mEAAmE;IACnE,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,kEAAkE;IAClE,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAsBD,MAAM,WAAW,8BAA8B;IAC7C,EAAE,EAAE,iBAAiB,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,4EAA4E;IAC5E,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;CACxC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,YAAY,EAAE,CAAC,CAyGzB;AAqHD,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qDAAqD;IACrD,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,4EAA4E;IAC5E,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,iCAAiC,GACvC,sBAAsB,GAAG,IAAI,CAgD/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC9D,MAAM,GAAG,IAAI,CAqCf;AAED,UAAU,iBAAiB;IACzB,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAgE1E;AAoGD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYpE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAgDf"}
@@ -0,0 +1,514 @@
1
+ import { quoteIdentifier } from "../schema/sql-identifiers.js";
2
+ import { createLogger } from "@happyvertical/logger";
3
+ //#region src/migrations/sqlite-rebuild.ts
4
+ /**
5
+ * SQLite table-rebuild migrations (issue #2370)
6
+ *
7
+ * SQLite has no `ALTER TABLE ... ALTER COLUMN ... TYPE`. Before this module
8
+ * every type-bucket change on SQLite (the canonical one being a numeric
9
+ * default edited from `0` to `0.0`, i.e. INTEGER → REAL) turned into an
10
+ * advisory differ comment, which `db:migrate` classifies as
11
+ * *manual intervention* and exits 1 for — forever, because nothing in the
12
+ * toolchain could ever perform the change.
13
+ *
14
+ * This module turns that comment into the executable rebuild SQLite's own
15
+ * documentation prescribes ("Making Other Kinds Of Table Schema Changes",
16
+ * lang_altertable.html):
17
+ *
18
+ * 1. `PRAGMA defer_foreign_keys = ON` — defers foreign-key enforcement to
19
+ * COMMIT so the window where the table does not exist cannot raise. It is
20
+ * honored *inside* a transaction (unlike `PRAGMA foreign_keys`, which is a
21
+ * silent no-op there) and resets itself at COMMIT/ROLLBACK.
22
+ * 2. Create a new table under a reserved `_smrt_rebuild_` name carrying the
23
+ * target shape.
24
+ * 3. Copy every row across.
25
+ * 4. Drop the original table.
26
+ * 5. Rename the new table into its place, with `PRAGMA legacy_alter_table`
27
+ * ON for the rename itself (see {@link buildSqliteRebuildStatements}).
28
+ * 6. Recreate the indexes and triggers that died with the original table.
29
+ *
30
+ * The whole plan is a plain statement list, so it is executed by the
31
+ * `MigrationTracker` inside the same transaction as the rest of the batch:
32
+ * a failure anywhere rolls the table back to its original shape and contents.
33
+ *
34
+ * ## The target shape comes from the live DDL, not from the manifest
35
+ *
36
+ * The new table is the *original* `CREATE TABLE` text with only the drifted
37
+ * columns' type tokens replaced. That deliberately preserves everything the
38
+ * manifest does not model or does not own here: columns the manifest has
39
+ * dropped (SMRT is additive — a rebuild must not become an implicit
40
+ * `DROP COLUMN`), table-level constraints, `CHECK`s, collations, and table
41
+ * options such as `WITHOUT ROWID`. The `add_column` changes in the same batch
42
+ * still run their own `ALTER TABLE ... ADD COLUMN`, against the rebuilt table
43
+ * — {@link planSqliteTableRebuilds} hoists the rebuild ahead of them for
44
+ * exactly that reason.
45
+ *
46
+ * ## Why the copy carries no CAST
47
+ *
48
+ * The copy is a plain `INSERT INTO new SELECT ... FROM old`; SQLite applies
49
+ * the destination column's *affinity* on insert, which is exactly the
50
+ * conversion a fresh table would have performed (integer `1` becomes real
51
+ * `1.0` in a REAL column). An explicit `CAST` would be strictly worse: casting
52
+ * a non-numeric TEXT value to REAL/INTEGER silently yields `0`, and casting an
53
+ * ISO timestamp string to SQLite's NUMERIC-affinity `DATETIME` silently yields
54
+ * the leading year. Affinity conversion leaves values it cannot convert
55
+ * untouched instead of destroying them.
56
+ *
57
+ * ## When the rebuild refuses
58
+ *
59
+ * With `PRAGMA foreign_keys` enabled — which the SMRT SQLite adapter is by
60
+ * default — `DROP TABLE` performs an implicit `DELETE FROM` that fires
61
+ * `ON DELETE` actions on children. A child with `ON DELETE CASCADE` therefore
62
+ * loses its rows, and `defer_foreign_keys` does not help: it defers
63
+ * constraint *checks*, not FK *actions*. So when any table declares a foreign
64
+ * key onto the rebuild target and enforcement is on, the plan is refused and
65
+ * the change stays a manual intervention. That includes the target's own
66
+ * self-references, because the staging table copies them and so becomes a
67
+ * child of the table being dropped.
68
+ */
69
+ var logger = createLogger({ level: "info" });
70
+ /**
71
+ * Prefix of the reserved staging table a rebuild creates. `_smrt_`-prefixed
72
+ * names are SMRT system tables, which the differ's dropped-table sweep skips,
73
+ * so a staging table left behind by a non-transactional adapter cannot be
74
+ * mistaken for user data.
75
+ */
76
+ var SQLITE_REBUILD_TABLE_PREFIX = "_smrt_rebuild_";
77
+ /**
78
+ * The advisory SQL the differ emits for a SQLite type upgrade it cannot
79
+ * perform with an `ALTER TABLE`. `planSqliteTableRebuilds` replaces it with an
80
+ * executable plan; when it cannot, the comment survives and `db:migrate`
81
+ * reports the column as manual drift exactly as it did before #2370.
82
+ */
83
+ function sqliteRebuildPlaceholderSql(columnName) {
84
+ return `-- SQLite: Type upgrade for ${quoteIdentifier(columnName)} requires table recreation`;
85
+ }
86
+ /**
87
+ * True when `sql` is the differ's "requires table recreation" placeholder —
88
+ * i.e. a type upgrade waiting for this module to turn it into a rebuild.
89
+ */
90
+ function isSqliteRebuildPlaceholder(sql) {
91
+ return typeof sql === "string" && sql.includes("requires table recreation");
92
+ }
93
+ function blockedSql(columnName, reason) {
94
+ return `-- SQLite: ${quoteIdentifier(columnName)} requires a table rebuild, blocked: ${reason}`;
95
+ }
96
+ function coveredSql(columnName, rebuiltWith) {
97
+ return `-- SQLite: ${quoteIdentifier(columnName)} is rebuilt with ${quoteIdentifier(rebuiltWith)} in one table rebuild (no change needed)`;
98
+ }
99
+ /**
100
+ * Replace every "requires table recreation" placeholder for `tableName` with
101
+ * one executable rebuild.
102
+ *
103
+ * All of the table's drifted columns are retyped by a *single* rebuild, so the
104
+ * first placeholder carries the plan and the rest become no-ops that reference
105
+ * it. Returns the changes unchanged when there is nothing to rebuild, and
106
+ * leaves the placeholders in place (annotated with the reason) when the
107
+ * rebuild cannot be planned safely — the pre-#2370 manual-intervention
108
+ * behavior.
109
+ *
110
+ * When a plan is produced, the rebuild is hoisted to the front of the table's
111
+ * changes. The plan's staging DDL and copy list are captured from the live
112
+ * schema *at diff time*, so a same-batch `ALTER TABLE ... ADD COLUMN` that ran
113
+ * first would be silently undone by the rebuild — both statements succeed, the
114
+ * transaction commits, and the new column is simply gone. The differ emits
115
+ * changes in manifest field order, which puts an `add_column` first whenever
116
+ * the new field is declared above the retyped one, so the order has to be
117
+ * imposed here rather than assumed. Rebuild first, then add columns to the
118
+ * rebuilt table.
119
+ */
120
+ async function planSqliteTableRebuilds(options) {
121
+ const { db, tableName, changes, mapType } = options;
122
+ const pending = changes.filter((change) => change.type === "type_upgrade" && change.name && change.column && isSqliteRebuildPlaceholder(change.sql));
123
+ if (pending.length === 0) return changes;
124
+ const block = (reason) => {
125
+ logger.debug(`[SQLiteRebuild] Cannot rebuild ${tableName}: ${reason}; leaving ${pending.length} type upgrade(s) as manual drift`);
126
+ return changes.map((change) => pending.includes(change) ? {
127
+ ...change,
128
+ sql: blockedSql(change.name, reason)
129
+ } : change);
130
+ };
131
+ let context;
132
+ try {
133
+ context = await readSqliteRebuildContext(db, tableName);
134
+ } catch (error) {
135
+ return block(`live schema introspection failed (${error instanceof Error ? error.message : String(error)})`);
136
+ }
137
+ if (!context.createTableSql) return block("no CREATE TABLE statement found in sqlite_master");
138
+ if (context.columns.length === 0) return block("PRAGMA table_info returned no columns");
139
+ if (context.foreignKeysEnabled && context.inboundReferences.length > 0) return block(`PRAGMA foreign_keys is ON and ${context.inboundReferences.map((name) => `"${name}"`).join(", ")} declare a foreign key onto this table, so DROP TABLE would fire their ON DELETE actions`);
140
+ const columnTypes = /* @__PURE__ */ new Map();
141
+ for (const change of pending) columnTypes.set(change.name, mapType(change.column.type));
142
+ const plan = buildSqliteRebuildStatements({
143
+ tableName,
144
+ createTableSql: context.createTableSql,
145
+ columnTypes,
146
+ columns: context.columns,
147
+ objects: context.objects,
148
+ legacyAlterTable: context.legacyAlterTable
149
+ });
150
+ if (!plan) return block("the live CREATE TABLE statement could not be rewritten for the target column types");
151
+ const [primary, ...covered] = pending;
152
+ const rebuilt = changes.map((change) => {
153
+ if (change === primary) return {
154
+ ...change,
155
+ sql: plan.statements[0],
156
+ sqlStatements: plan.statements
157
+ };
158
+ if (covered.includes(change)) {
159
+ const { sqlStatements: _dropped, ...rest } = change;
160
+ return {
161
+ ...rest,
162
+ sql: coveredSql(change.name, primary.name)
163
+ };
164
+ }
165
+ return change;
166
+ });
167
+ const rebuildPositions = new Set(pending.map((change) => changes.indexOf(change)));
168
+ return [...rebuilt.filter((_, index) => rebuildPositions.has(index)), ...rebuilt.filter((_, index) => !rebuildPositions.has(index))];
169
+ }
170
+ /**
171
+ * Read everything the rebuild needs from the live database.
172
+ *
173
+ * Every query is SQLite-only (`sqlite_master`, `PRAGMA`), and the caller only
174
+ * reaches this for an engine already detected as SQLite.
175
+ */
176
+ async function readSqliteRebuildContext(db, tableName) {
177
+ const createTableSql = readString((await db.query(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`, tableName)).rows[0]?.sql);
178
+ const columns = (await db.query(`PRAGMA table_info(${quoteIdentifier(tableName)})`)).rows.map((row) => readString(row.name)).filter((name) => Boolean(name));
179
+ const objectRows = await db.query(`SELECT name, sql FROM sqlite_master
180
+ WHERE tbl_name = ? AND sql IS NOT NULL
181
+ AND type IN ('index', 'trigger') AND name NOT LIKE 'sqlite_%'`, tableName);
182
+ const objects = [];
183
+ for (const row of objectRows.rows) {
184
+ const name = readString(row.name);
185
+ const sql = readString(row.sql);
186
+ if (name && sql) objects.push({
187
+ name,
188
+ sql
189
+ });
190
+ }
191
+ const foreignKeysEnabled = readPragmaFlag(await db.query("PRAGMA foreign_keys"));
192
+ return {
193
+ createTableSql,
194
+ columns,
195
+ objects,
196
+ foreignKeysEnabled,
197
+ legacyAlterTable: readPragmaFlag(await db.query("PRAGMA legacy_alter_table")),
198
+ inboundReferences: foreignKeysEnabled ? await readInboundForeignKeys(db, tableName) : []
199
+ };
200
+ }
201
+ /**
202
+ * Names of tables that declare a foreign key onto `tableName`.
203
+ *
204
+ * The table itself counts. A self-reference is copied verbatim into the
205
+ * staging table, which therefore becomes a real child of the live table, and
206
+ * `DROP TABLE` then cascades the *staged* rows away — verified: a two-row
207
+ * self-referencing table finishes the rebuild holding one row.
208
+ */
209
+ async function readInboundForeignKeys(db, tableName) {
210
+ const tables = await db.query(`SELECT name FROM sqlite_master
211
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`);
212
+ const referencing = [];
213
+ const target = tableName.toLowerCase();
214
+ for (const row of tables.rows) {
215
+ const name = readString(row.name);
216
+ if (!name) continue;
217
+ if ((await db.query(`PRAGMA foreign_key_list(${quoteIdentifier(name)})`)).rows.some((fk) => readString(fk.table).toLowerCase() === target)) referencing.push(name);
218
+ }
219
+ return referencing;
220
+ }
221
+ function readString(value) {
222
+ return typeof value === "string" ? value : "";
223
+ }
224
+ /** Read a single-row/single-column PRAGMA result as a boolean. */
225
+ function readPragmaFlag(result) {
226
+ const row = result.rows[0];
227
+ if (!row) return false;
228
+ const value = Object.values(row)[0];
229
+ if (typeof value === "number") return value !== 0;
230
+ if (typeof value === "boolean") return value;
231
+ if (typeof value === "string") return value !== "0" && value !== "";
232
+ return false;
233
+ }
234
+ /**
235
+ * Build the ordered statement list for one table rebuild.
236
+ *
237
+ * Returns `null` when the live `CREATE TABLE` text cannot be rewritten (an
238
+ * unparsable statement, or a requested column that is not in it) — the caller
239
+ * then leaves the change as manual drift rather than emitting SQL that would
240
+ * "succeed" without fixing the column, which would re-diff forever.
241
+ *
242
+ * `PRAGMA legacy_alter_table` brackets the rename because SQLite ≥ 3.25
243
+ * re-parses the whole schema on `ALTER TABLE ... RENAME`: a view left
244
+ * referencing the just-dropped table makes the rename fail outright
245
+ * ("error in view … no such table"). Legacy mode renames the table without
246
+ * that reference rewriting, which is exactly what a rebuild wants — the name
247
+ * the views refer to is about to exist again. It is restored to its previous
248
+ * value immediately afterwards; a rolled-back batch leaves it set, which is
249
+ * inert for SMRT (nothing else in the toolchain renames a table) but is
250
+ * documented in `packages/core/agents/schema-paths.md`.
251
+ */
252
+ function buildSqliteRebuildStatements(input) {
253
+ const { tableName, createTableSql, columnTypes, columns, objects, legacyAlterTable } = input;
254
+ const stagingTable = `${SQLITE_REBUILD_TABLE_PREFIX}${tableName}`;
255
+ const createStaging = rewriteSqliteCreateTable(createTableSql, {
256
+ tableName: stagingTable,
257
+ columnTypes
258
+ });
259
+ if (!createStaging) return null;
260
+ const quotedTable = quoteIdentifier(tableName);
261
+ const quotedStaging = quoteIdentifier(stagingTable);
262
+ const columnList = columns.map((name) => quoteIdentifier(name)).join(", ");
263
+ return {
264
+ statements: [
265
+ "PRAGMA defer_foreign_keys = ON",
266
+ `DROP TABLE IF EXISTS ${quotedStaging}`,
267
+ createStaging,
268
+ `INSERT INTO ${quotedStaging} (${columnList}) SELECT ${columnList} FROM ${quotedTable}`,
269
+ `DROP TABLE ${quotedTable}`,
270
+ "PRAGMA legacy_alter_table = ON",
271
+ `ALTER TABLE ${quotedStaging} RENAME TO ${quotedTable}`,
272
+ `PRAGMA legacy_alter_table = ${legacyAlterTable ? "ON" : "OFF"}`,
273
+ ...objects.map((object) => object.sql)
274
+ ],
275
+ copiedColumns: [...columns],
276
+ recreatedObjects: objects.map((object) => object.name)
277
+ };
278
+ }
279
+ /**
280
+ * Rewrite a live `CREATE TABLE` statement into the rebuild's target shape:
281
+ * the same body under a new table name, with the declared type of each column
282
+ * in `columnTypes` replaced.
283
+ *
284
+ * Everything else is preserved verbatim — other columns, their defaults,
285
+ * `NOT NULL`/`PRIMARY KEY`/`CHECK`/`COLLATE` clauses, table-level
286
+ * constraints, and trailing table options (`WITHOUT ROWID`, `STRICT`).
287
+ *
288
+ * Returns `null` if the statement cannot be parsed or if any requested column
289
+ * is not present in it.
290
+ */
291
+ function rewriteSqliteCreateTable(createTableSql, target) {
292
+ const parsed = parseCreateTableBody(createTableSql);
293
+ if (!parsed) return null;
294
+ const wanted = /* @__PURE__ */ new Map();
295
+ for (const [name, type] of target.columnTypes) wanted.set(name.toLowerCase(), type);
296
+ const rewritten = [];
297
+ for (const item of parsed.items) {
298
+ const columnName = readColumnDefinitionName(item);
299
+ const newType = columnName ? wanted.get(columnName.toLowerCase()) : null;
300
+ if (!columnName || !newType) {
301
+ rewritten.push(item);
302
+ continue;
303
+ }
304
+ const replaced = replaceColumnType(item, newType);
305
+ if (!replaced) return null;
306
+ rewritten.push(replaced);
307
+ wanted.delete(columnName.toLowerCase());
308
+ }
309
+ if (wanted.size > 0) return null;
310
+ const tail = parsed.tail ? ` ${parsed.tail}` : "";
311
+ return `CREATE TABLE ${quoteIdentifier(target.tableName)} (\n ${rewritten.join(",\n ")}\n)${tail}`;
312
+ }
313
+ /**
314
+ * Split a `CREATE TABLE` statement into its top-level definition items plus
315
+ * any trailing table options, honoring SQLite's quoting (`'…'`, `"…"`,
316
+ * `` `…` ``, `[…]`) and comments so a quoted comma or paren cannot split the
317
+ * body.
318
+ *
319
+ * @internal exported for testing
320
+ */
321
+ function parseCreateTableBody(sql) {
322
+ let index = 0;
323
+ let open = -1;
324
+ while (index < sql.length) {
325
+ const skipped = skipQuotedOrComment(sql, index);
326
+ if (skipped !== null) {
327
+ index = skipped;
328
+ continue;
329
+ }
330
+ if (sql[index] === "(") {
331
+ open = index;
332
+ break;
333
+ }
334
+ index++;
335
+ }
336
+ if (open === -1) return null;
337
+ const items = [];
338
+ let depth = 1;
339
+ let itemStart = open + 1;
340
+ let close = -1;
341
+ index = open + 1;
342
+ while (index < sql.length) {
343
+ const skipped = skipQuotedOrComment(sql, index);
344
+ if (skipped !== null) {
345
+ index = skipped;
346
+ continue;
347
+ }
348
+ const char = sql[index];
349
+ if (char === "(") depth++;
350
+ else if (char === ")") {
351
+ depth--;
352
+ if (depth === 0) {
353
+ close = index;
354
+ items.push(sql.slice(itemStart, index));
355
+ break;
356
+ }
357
+ } else if (char === "," && depth === 1) {
358
+ items.push(sql.slice(itemStart, index));
359
+ itemStart = index + 1;
360
+ }
361
+ index++;
362
+ }
363
+ if (close === -1) return null;
364
+ const trimmed = items.map((item) => item.trim()).filter(Boolean);
365
+ if (trimmed.length === 0) return null;
366
+ return {
367
+ items: trimmed,
368
+ tail: sql.slice(close + 1).trim().replace(/;+$/, "").trim()
369
+ };
370
+ }
371
+ /**
372
+ * If `sql[index]` opens a quoted identifier, string literal, or comment,
373
+ * return the index just past its end. Otherwise return `null`.
374
+ */
375
+ function skipQuotedOrComment(sql, index) {
376
+ const char = sql[index];
377
+ if (char === "'" || char === "\"" || char === "`") {
378
+ let cursor = index + 1;
379
+ while (cursor < sql.length) {
380
+ if (sql[cursor] === char) {
381
+ if (sql[cursor + 1] === char) {
382
+ cursor += 2;
383
+ continue;
384
+ }
385
+ return cursor + 1;
386
+ }
387
+ cursor++;
388
+ }
389
+ return sql.length;
390
+ }
391
+ if (char === "[") {
392
+ const end = sql.indexOf("]", index + 1);
393
+ return end === -1 ? sql.length : end + 1;
394
+ }
395
+ if (char === "-" && sql[index + 1] === "-") {
396
+ const end = sql.indexOf("\n", index);
397
+ return end === -1 ? sql.length : end + 1;
398
+ }
399
+ if (char === "/" && sql[index + 1] === "*") {
400
+ const end = sql.indexOf("*/", index + 2);
401
+ return end === -1 ? sql.length : end + 2;
402
+ }
403
+ return null;
404
+ }
405
+ /**
406
+ * Keywords that start a *table*-level constraint, i.e. a body item that is not
407
+ * a column definition.
408
+ */
409
+ var TABLE_CONSTRAINT_KEYWORDS = /* @__PURE__ */ new Set([
410
+ "constraint",
411
+ "primary",
412
+ "unique",
413
+ "check",
414
+ "foreign"
415
+ ]);
416
+ /**
417
+ * Keywords that end a column's type name and start its constraint list.
418
+ */
419
+ var COLUMN_CONSTRAINT_KEYWORDS = /* @__PURE__ */ new Set([
420
+ "constraint",
421
+ "primary",
422
+ "not",
423
+ "null",
424
+ "unique",
425
+ "check",
426
+ "default",
427
+ "collate",
428
+ "references",
429
+ "generated",
430
+ "as"
431
+ ]);
432
+ /** Read the leading identifier of a body item (quoted or bare). */
433
+ function readLeadingIdentifier(item) {
434
+ let index = 0;
435
+ while (index < item.length && /\s/.test(item[index])) index++;
436
+ if (index >= item.length) return null;
437
+ const char = item[index];
438
+ if (char === "\"" || char === "`" || char === "[") {
439
+ const end = skipQuotedOrComment(item, index);
440
+ if (end === null) return null;
441
+ const raw = item.slice(index + 1, end - 1);
442
+ return {
443
+ name: char === "[" ? raw : raw.split(char + char).join(char),
444
+ end
445
+ };
446
+ }
447
+ const match = /^[A-Za-z_][\w$]*/.exec(item.slice(index));
448
+ if (!match) return null;
449
+ return {
450
+ name: match[0],
451
+ end: index + match[0].length
452
+ };
453
+ }
454
+ /**
455
+ * The column name of a body item, or `null` when the item is a table-level
456
+ * constraint rather than a column definition.
457
+ *
458
+ * @internal exported for testing
459
+ */
460
+ function readColumnDefinitionName(item) {
461
+ const identifier = readLeadingIdentifier(item);
462
+ if (!identifier) return null;
463
+ if (!/^\s*["`[]/.test(item) && TABLE_CONSTRAINT_KEYWORDS.has(identifier.name.toLowerCase())) return null;
464
+ return identifier.name;
465
+ }
466
+ /**
467
+ * Replace the declared type of a column definition, keeping its name and every
468
+ * constraint that follows. A column declared without a type gets one inserted.
469
+ *
470
+ * @internal exported for testing
471
+ */
472
+ function replaceColumnType(item, newType) {
473
+ const identifier = readLeadingIdentifier(item);
474
+ if (!identifier) return null;
475
+ const head = item.slice(0, identifier.end);
476
+ let index = identifier.end;
477
+ let typeEnd = index;
478
+ while (index < item.length) {
479
+ while (index < item.length && /\s/.test(item[index])) index++;
480
+ const match = /^[A-Za-z_][\w$]*/.exec(item.slice(index));
481
+ if (!match || COLUMN_CONSTRAINT_KEYWORDS.has(match[0].toLowerCase())) break;
482
+ index += match[0].length;
483
+ typeEnd = index;
484
+ }
485
+ if (typeEnd > identifier.end) {
486
+ let cursor = typeEnd;
487
+ while (cursor < item.length && /\s/.test(item[cursor])) cursor++;
488
+ if (item[cursor] === "(") {
489
+ let depth = 0;
490
+ while (cursor < item.length) {
491
+ const skipped = skipQuotedOrComment(item, cursor);
492
+ if (skipped !== null) {
493
+ cursor = skipped;
494
+ continue;
495
+ }
496
+ if (item[cursor] === "(") depth++;
497
+ else if (item[cursor] === ")") {
498
+ depth--;
499
+ if (depth === 0) {
500
+ typeEnd = cursor + 1;
501
+ break;
502
+ }
503
+ }
504
+ cursor++;
505
+ }
506
+ }
507
+ }
508
+ const rest = item.slice(typeEnd);
509
+ return `${head} ${newType}${rest.length === 0 || /^\s/.test(rest) ? "" : " "}${rest}`;
510
+ }
511
+ //#endregion
512
+ export { SQLITE_REBUILD_TABLE_PREFIX, buildSqliteRebuildStatements, isSqliteRebuildPlaceholder, parseCreateTableBody, planSqliteTableRebuilds, readColumnDefinitionName, replaceColumnType, rewriteSqliteCreateTable, sqliteRebuildPlaceholderSql };
513
+
514
+ //# sourceMappingURL=sqlite-rebuild.js.map