@supabase/pg-delta 1.0.0-alpha.4 → 1.0.0-alpha.6

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 (359) hide show
  1. package/README.md +40 -23
  2. package/dist/cli/app.js +26 -3
  3. package/dist/cli/bin/cli.js +5 -0
  4. package/dist/cli/commands/catalog-export.d.ts +5 -0
  5. package/dist/cli/commands/catalog-export.js +64 -0
  6. package/dist/cli/commands/declarative-apply.d.ts +6 -0
  7. package/dist/cli/commands/declarative-apply.js +288 -0
  8. package/dist/cli/commands/declarative-export.d.ts +5 -0
  9. package/dist/cli/commands/declarative-export.js +245 -0
  10. package/dist/cli/commands/plan.js +19 -6
  11. package/dist/cli/exit-code.d.ts +2 -0
  12. package/dist/cli/exit-code.js +7 -0
  13. package/dist/cli/formatters/tree/tree.js +3 -2
  14. package/dist/cli/utils/apply-display.d.ts +52 -0
  15. package/dist/cli/utils/apply-display.js +183 -0
  16. package/dist/cli/utils/export-display.d.ts +43 -0
  17. package/dist/cli/utils/export-display.js +202 -0
  18. package/dist/cli/utils/resolve-input.d.ts +7 -0
  19. package/dist/cli/utils/resolve-input.js +13 -0
  20. package/dist/core/catalog-export/index.d.ts +11 -0
  21. package/dist/core/catalog-export/index.js +10 -0
  22. package/dist/core/catalog.diff.d.ts +1 -0
  23. package/dist/core/catalog.diff.js +64 -48
  24. package/dist/core/catalog.model.d.ts +14 -1
  25. package/dist/core/catalog.model.js +103 -1
  26. package/dist/core/catalog.snapshot.d.ts +66 -0
  27. package/dist/core/catalog.snapshot.js +206 -0
  28. package/dist/core/declarative-apply/discover-sql.d.ts +18 -0
  29. package/dist/core/declarative-apply/discover-sql.js +86 -0
  30. package/dist/core/declarative-apply/extract-catalog-providers.d.ts +23 -0
  31. package/dist/core/declarative-apply/extract-catalog-providers.js +159 -0
  32. package/dist/core/declarative-apply/index.d.ts +49 -0
  33. package/dist/core/declarative-apply/index.js +134 -0
  34. package/dist/core/declarative-apply/round-apply.d.ts +100 -0
  35. package/dist/core/declarative-apply/round-apply.js +378 -0
  36. package/dist/core/export/file-mapper.d.ts +71 -0
  37. package/dist/core/export/file-mapper.js +474 -0
  38. package/dist/core/export/grouper.d.ts +13 -0
  39. package/dist/core/export/grouper.js +76 -0
  40. package/dist/core/export/index.d.ts +45 -0
  41. package/dist/core/export/index.js +63 -0
  42. package/dist/core/export/types.d.ts +84 -0
  43. package/dist/core/export/types.js +25 -0
  44. package/dist/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  45. package/dist/core/integrations/filter/dsl.d.ts +38 -1
  46. package/dist/core/integrations/filter/dsl.js +20 -2
  47. package/dist/core/integrations/filter/extractors.js +42 -0
  48. package/dist/core/integrations/integration-dsl.d.ts +10 -0
  49. package/dist/core/integrations/supabase.d.ts +8 -0
  50. package/dist/core/integrations/supabase.js +9 -0
  51. package/dist/core/objects/aggregate/aggregate.diff.d.ts +2 -8
  52. package/dist/core/objects/aggregate/aggregate.diff.js +16 -70
  53. package/dist/core/objects/aggregate/aggregate.model.d.ts +8 -8
  54. package/dist/core/objects/aggregate/aggregate.model.js +1 -1
  55. package/dist/core/objects/aggregate/changes/aggregate.create.js +1 -1
  56. package/dist/core/objects/aggregate/changes/aggregate.drop.js +1 -1
  57. package/dist/core/objects/base.privilege-diff.d.ts +38 -13
  58. package/dist/core/objects/base.privilege-diff.js +104 -22
  59. package/dist/core/objects/base.privilege.d.ts +1 -0
  60. package/dist/core/objects/base.privilege.js +9 -2
  61. package/dist/core/objects/collation/collation.diff.d.ts +2 -3
  62. package/dist/core/objects/diff-context.d.ts +15 -0
  63. package/dist/core/objects/diff-context.js +1 -0
  64. package/dist/core/objects/domain/changes/domain.create.js +4 -2
  65. package/dist/core/objects/domain/domain.diff.d.ts +2 -8
  66. package/dist/core/objects/domain/domain.diff.js +16 -77
  67. package/dist/core/objects/domain/domain.model.js +1 -1
  68. package/dist/core/objects/event-trigger/event-trigger.diff.d.ts +2 -3
  69. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.d.ts +2 -8
  70. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.js +13 -77
  71. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.js +2 -2
  72. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.d.ts +2 -8
  73. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -77
  74. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +1 -1
  75. package/dist/core/objects/foreign-data-wrapper/server/server.diff.d.ts +2 -8
  76. package/dist/core/objects/foreign-data-wrapper/server/server.diff.js +13 -77
  77. package/dist/core/objects/language/language.diff.d.ts +2 -5
  78. package/dist/core/objects/language/language.diff.js +7 -39
  79. package/dist/core/objects/materialized-view/materialized-view.diff.d.ts +2 -8
  80. package/dist/core/objects/materialized-view/materialized-view.diff.js +16 -158
  81. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +3 -3
  82. package/dist/core/objects/materialized-view/materialized-view.model.js +1 -1
  83. package/dist/core/objects/procedure/changes/procedure.alter.js +12 -12
  84. package/dist/core/objects/procedure/procedure.diff.d.ts +2 -8
  85. package/dist/core/objects/procedure/procedure.diff.js +16 -77
  86. package/dist/core/objects/procedure/procedure.model.d.ts +9 -9
  87. package/dist/core/objects/procedure/procedure.model.js +1 -1
  88. package/dist/core/objects/publication/changes/publication.alter.d.ts +0 -9
  89. package/dist/core/objects/publication/changes/publication.alter.js +0 -14
  90. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -2
  91. package/dist/core/objects/publication/publication.diff.d.ts +2 -3
  92. package/dist/core/objects/publication/publication.diff.js +8 -13
  93. package/dist/core/objects/rls-policy/changes/rls-policy.alter.js +3 -3
  94. package/dist/core/objects/rls-policy/rls-policy.model.d.ts +2 -2
  95. package/dist/core/objects/role/role.diff.js +22 -1
  96. package/dist/core/objects/role/role.model.d.ts +4 -3
  97. package/dist/core/objects/role/role.model.js +118 -12
  98. package/dist/core/objects/rule/rule.model.d.ts +1 -1
  99. package/dist/core/objects/schema/schema.diff.d.ts +2 -8
  100. package/dist/core/objects/schema/schema.diff.js +16 -77
  101. package/dist/core/objects/schema/schema.model.js +1 -1
  102. package/dist/core/objects/sequence/sequence.diff.d.ts +2 -8
  103. package/dist/core/objects/sequence/sequence.diff.js +16 -79
  104. package/dist/core/objects/sequence/sequence.model.js +1 -1
  105. package/dist/core/objects/subscription/subscription.diff.d.ts +2 -3
  106. package/dist/core/objects/table/changes/table.create.js +3 -0
  107. package/dist/core/objects/table/table.diff.d.ts +2 -8
  108. package/dist/core/objects/table/table.diff.js +26 -157
  109. package/dist/core/objects/table/table.model.d.ts +23 -22
  110. package/dist/core/objects/table/table.model.js +1 -1
  111. package/dist/core/objects/trigger/changes/trigger.create.js +2 -4
  112. package/dist/core/objects/trigger/trigger.model.d.ts +8 -0
  113. package/dist/core/objects/trigger/trigger.model.js +11 -0
  114. package/dist/core/objects/type/composite-type/composite-type.diff.d.ts +2 -8
  115. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -77
  116. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +3 -3
  117. package/dist/core/objects/type/composite-type/composite-type.model.js +2 -1
  118. package/dist/core/objects/type/enum/enum.diff.d.ts +2 -8
  119. package/dist/core/objects/type/enum/enum.diff.js +25 -112
  120. package/dist/core/objects/type/enum/enum.model.js +1 -1
  121. package/dist/core/objects/type/range/changes/range.create.js +6 -3
  122. package/dist/core/objects/type/range/range.diff.d.ts +2 -8
  123. package/dist/core/objects/type/range/range.diff.js +16 -77
  124. package/dist/core/objects/type/range/range.model.js +1 -1
  125. package/dist/core/objects/view/view.diff.d.ts +2 -8
  126. package/dist/core/objects/view/view.diff.js +16 -158
  127. package/dist/core/objects/view/view.model.d.ts +18 -4
  128. package/dist/core/objects/view/view.model.js +3 -13
  129. package/dist/core/plan/apply.js +9 -26
  130. package/dist/core/plan/create.d.ts +19 -6
  131. package/dist/core/plan/create.js +134 -174
  132. package/dist/core/plan/serialize.js +16 -4
  133. package/dist/core/plan/sql-format/fixtures.js +3 -5
  134. package/dist/core/plan/sql-format/keyword-case.js +26 -1
  135. package/dist/core/plan/ssl-config.d.ts +32 -0
  136. package/dist/core/plan/ssl-config.js +115 -0
  137. package/dist/core/plan/types.d.ts +6 -0
  138. package/dist/core/postgres-config.d.ts +14 -0
  139. package/dist/core/postgres-config.js +53 -2
  140. package/dist/core/sort/graph-builder.js +10 -0
  141. package/dist/core/sort/logical-sort.js +31 -23
  142. package/dist/core/test-utils/assert-valid-sql.d.ts +10 -0
  143. package/dist/core/test-utils/assert-valid-sql.js +19 -0
  144. package/dist/index.d.ts +6 -0
  145. package/dist/index.js +6 -1
  146. package/package.json +21 -4
  147. package/src/cli/app.ts +27 -3
  148. package/src/cli/bin/cli.ts +6 -0
  149. package/src/cli/commands/catalog-export.ts +78 -0
  150. package/src/cli/commands/declarative-apply.diagnostics.test.ts +77 -0
  151. package/src/cli/commands/declarative-apply.ts +380 -0
  152. package/src/cli/commands/declarative-export.ts +330 -0
  153. package/src/cli/commands/plan.ts +28 -7
  154. package/src/cli/exit-code.test.ts +19 -0
  155. package/src/cli/exit-code.ts +7 -0
  156. package/src/cli/formatters/tree/tree.ts +3 -2
  157. package/src/cli/utils/apply-display.test.ts +348 -0
  158. package/src/cli/utils/apply-display.ts +238 -0
  159. package/src/cli/utils/export-display.test.ts +103 -0
  160. package/src/cli/utils/export-display.ts +275 -0
  161. package/src/cli/utils/integrations.test.ts +44 -0
  162. package/src/cli/utils/resolve-input.test.ts +38 -0
  163. package/src/cli/utils/resolve-input.ts +17 -0
  164. package/src/core/catalog-export/index.ts +20 -0
  165. package/src/core/catalog.diff.ts +79 -78
  166. package/src/core/catalog.model.test.ts +122 -0
  167. package/src/core/catalog.model.ts +127 -1
  168. package/src/core/catalog.snapshot.test.ts +464 -0
  169. package/src/core/catalog.snapshot.ts +289 -0
  170. package/src/core/declarative-apply/discover-sql.test.ts +103 -0
  171. package/src/core/declarative-apply/discover-sql.ts +107 -0
  172. package/src/core/declarative-apply/extract-catalog-providers.ts +220 -0
  173. package/src/core/declarative-apply/index.test.ts +67 -0
  174. package/src/core/declarative-apply/index.ts +205 -0
  175. package/src/core/declarative-apply/round-apply.test.ts +504 -0
  176. package/src/core/declarative-apply/round-apply.ts +562 -0
  177. package/src/core/expand-replace-dependencies.test.ts +70 -0
  178. package/src/core/export/file-mapper.test.ts +816 -0
  179. package/src/core/export/file-mapper.ts +574 -0
  180. package/src/core/export/grouper.ts +108 -0
  181. package/src/core/export/index.ts +129 -0
  182. package/src/core/export/types.ts +104 -0
  183. package/src/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  184. package/src/core/integrations/filter/dsl.test.ts +211 -0
  185. package/src/core/integrations/filter/dsl.ts +65 -3
  186. package/src/core/integrations/filter/extractors.test.ts +244 -0
  187. package/src/core/integrations/filter/extractors.ts +42 -0
  188. package/src/core/integrations/integration-dsl.ts +10 -0
  189. package/src/core/integrations/serialize/dsl.test.ts +91 -0
  190. package/src/core/integrations/supabase.ts +9 -0
  191. package/src/core/objects/aggregate/aggregate.diff.ts +39 -95
  192. package/src/core/objects/aggregate/aggregate.model.ts +1 -1
  193. package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +3 -1
  194. package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +5 -2
  195. package/src/core/objects/aggregate/changes/aggregate.create.test.ts +6 -3
  196. package/src/core/objects/aggregate/changes/aggregate.create.ts +1 -1
  197. package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +7 -3
  198. package/src/core/objects/aggregate/changes/aggregate.drop.ts +1 -1
  199. package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +9 -3
  200. package/src/core/objects/base.privilege-diff.ts +178 -30
  201. package/src/core/objects/base.privilege.ts +9 -2
  202. package/src/core/objects/collation/changes/collation.alter.test.ts +7 -2
  203. package/src/core/objects/collation/changes/collation.create.test.ts +7 -2
  204. package/src/core/objects/collation/changes/collation.drop.test.ts +4 -1
  205. package/src/core/objects/collation/collation.diff.test.ts +9 -12
  206. package/src/core/objects/collation/collation.diff.ts +2 -1
  207. package/src/core/objects/diff-context.ts +16 -0
  208. package/src/core/objects/domain/changes/domain.alter.test.ts +28 -9
  209. package/src/core/objects/domain/changes/domain.create.test.ts +32 -2
  210. package/src/core/objects/domain/changes/domain.create.ts +7 -1
  211. package/src/core/objects/domain/changes/domain.drop.test.ts +4 -1
  212. package/src/core/objects/domain/domain.diff.ts +39 -102
  213. package/src/core/objects/domain/domain.model.ts +1 -1
  214. package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +10 -3
  215. package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +4 -1
  216. package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +4 -1
  217. package/src/core/objects/event-trigger/event-trigger.diff.test.ts +12 -7
  218. package/src/core/objects/event-trigger/event-trigger.diff.ts +2 -1
  219. package/src/core/objects/extension/changes/extension.alter.test.ts +7 -2
  220. package/src/core/objects/extension/changes/extension.create.test.ts +4 -1
  221. package/src/core/objects/extension/changes/extension.drop.test.ts +4 -1
  222. package/src/core/objects/extension/extension.model.test.ts +98 -0
  223. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +16 -5
  224. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +51 -16
  225. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +4 -1
  226. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +111 -4
  227. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +31 -101
  228. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +2 -2
  229. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +46 -15
  230. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +13 -4
  231. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +4 -1
  232. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +39 -102
  233. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +1 -1
  234. package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +22 -7
  235. package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +19 -6
  236. package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +4 -1
  237. package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +95 -0
  238. package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +31 -101
  239. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +13 -4
  240. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +16 -5
  241. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +10 -3
  242. package/src/core/objects/index/changes/index.alter.test.ts +13 -4
  243. package/src/core/objects/index/changes/index.create.test.ts +4 -1
  244. package/src/core/objects/index/changes/index.drop.test.ts +4 -1
  245. package/src/core/objects/language/changes/language.alter.test.ts +4 -1
  246. package/src/core/objects/language/changes/language.create.test.ts +4 -1
  247. package/src/core/objects/language/changes/language.drop.test.ts +4 -1
  248. package/src/core/objects/language/language.diff.test.ts +86 -4
  249. package/src/core/objects/language/language.diff.ts +17 -49
  250. package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +10 -3
  251. package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +7 -2
  252. package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +4 -1
  253. package/src/core/objects/materialized-view/materialized-view.diff.test.ts +162 -0
  254. package/src/core/objects/materialized-view/materialized-view.diff.ts +41 -191
  255. package/src/core/objects/materialized-view/materialized-view.model.ts +1 -1
  256. package/src/core/objects/procedure/changes/procedure.alter.test.ts +121 -49
  257. package/src/core/objects/procedure/changes/procedure.alter.ts +15 -12
  258. package/src/core/objects/procedure/changes/procedure.create.test.ts +4 -1
  259. package/src/core/objects/procedure/changes/procedure.drop.test.ts +7 -2
  260. package/src/core/objects/procedure/procedure.diff.ts +39 -102
  261. package/src/core/objects/procedure/procedure.model.ts +1 -1
  262. package/src/core/objects/publication/changes/publication.alter.test.ts +15 -21
  263. package/src/core/objects/publication/changes/publication.alter.ts +0 -18
  264. package/src/core/objects/publication/changes/publication.comment.test.ts +5 -2
  265. package/src/core/objects/publication/changes/publication.create.test.ts +5 -2
  266. package/src/core/objects/publication/changes/publication.drop.test.ts +3 -1
  267. package/src/core/objects/publication/changes/publication.types.ts +0 -2
  268. package/src/core/objects/publication/publication.diff.test.ts +24 -19
  269. package/src/core/objects/publication/publication.diff.ts +9 -15
  270. package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +31 -14
  271. package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +3 -3
  272. package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +10 -3
  273. package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +4 -1
  274. package/src/core/objects/role/changes/role.alter.test.ts +31 -15
  275. package/src/core/objects/role/changes/role.create.test.ts +6 -2
  276. package/src/core/objects/role/changes/role.drop.test.ts +4 -1
  277. package/src/core/objects/role/role.diff.test.ts +235 -0
  278. package/src/core/objects/role/role.diff.ts +21 -1
  279. package/src/core/objects/role/role.model.ts +122 -14
  280. package/src/core/objects/rule/changes/rule.alter.test.ts +7 -3
  281. package/src/core/objects/rule/changes/rule.comment.test.ts +5 -2
  282. package/src/core/objects/rule/changes/rule.create.test.ts +6 -2
  283. package/src/core/objects/rule/changes/rule.drop.test.ts +3 -1
  284. package/src/core/objects/schema/changes/schema.alter.test.ts +4 -1
  285. package/src/core/objects/schema/changes/schema.create.test.ts +4 -1
  286. package/src/core/objects/schema/changes/schema.drop.test.ts +4 -1
  287. package/src/core/objects/schema/schema.diff.ts +39 -102
  288. package/src/core/objects/schema/schema.model.ts +1 -1
  289. package/src/core/objects/sequence/changes/sequence.alter.test.ts +11 -5
  290. package/src/core/objects/sequence/changes/sequence.create.test.ts +8 -3
  291. package/src/core/objects/sequence/changes/sequence.drop.test.ts +4 -1
  292. package/src/core/objects/sequence/sequence.diff.test.ts +114 -0
  293. package/src/core/objects/sequence/sequence.diff.ts +39 -104
  294. package/src/core/objects/sequence/sequence.model.ts +1 -1
  295. package/src/core/objects/subscription/changes/subscription.alter.test.ts +15 -5
  296. package/src/core/objects/subscription/changes/subscription.comment.test.ts +5 -2
  297. package/src/core/objects/subscription/changes/subscription.create.test.ts +5 -2
  298. package/src/core/objects/subscription/changes/subscription.drop.test.ts +3 -1
  299. package/src/core/objects/subscription/subscription.diff.test.ts +16 -11
  300. package/src/core/objects/subscription/subscription.diff.ts +2 -1
  301. package/src/core/objects/table/changes/table.alter.test.ts +38 -15
  302. package/src/core/objects/table/changes/table.create.test.ts +41 -3
  303. package/src/core/objects/table/changes/table.create.ts +4 -0
  304. package/src/core/objects/table/changes/table.drop.test.ts +3 -1
  305. package/src/core/objects/table/table.diff.test.ts +157 -0
  306. package/src/core/objects/table/table.diff.ts +54 -190
  307. package/src/core/objects/table/table.model.ts +1 -1
  308. package/src/core/objects/trigger/changes/trigger.alter.test.ts +8 -4
  309. package/src/core/objects/trigger/changes/trigger.create.test.ts +5 -1
  310. package/src/core/objects/trigger/changes/trigger.create.ts +7 -4
  311. package/src/core/objects/trigger/changes/trigger.drop.test.ts +5 -1
  312. package/src/core/objects/trigger/trigger.diff.test.ts +1 -0
  313. package/src/core/objects/trigger/trigger.model.ts +12 -0
  314. package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +10 -4
  315. package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +7 -2
  316. package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +4 -1
  317. package/src/core/objects/type/composite-type/composite-type.diff.test.ts +78 -0
  318. package/src/core/objects/type/composite-type/composite-type.diff.ts +39 -101
  319. package/src/core/objects/type/composite-type/composite-type.model.ts +2 -1
  320. package/src/core/objects/type/enum/changes/enum.alter.test.ts +14 -5
  321. package/src/core/objects/type/enum/changes/enum.create.test.ts +4 -1
  322. package/src/core/objects/type/enum/changes/enum.drop.test.ts +4 -1
  323. package/src/core/objects/type/enum/enum.diff.test.ts +181 -0
  324. package/src/core/objects/type/enum/enum.diff.ts +58 -146
  325. package/src/core/objects/type/enum/enum.model.ts +1 -1
  326. package/src/core/objects/type/range/changes/range.alter.test.ts +3 -1
  327. package/src/core/objects/type/range/changes/range.create.test.ts +5 -2
  328. package/src/core/objects/type/range/changes/range.create.ts +6 -2
  329. package/src/core/objects/type/range/changes/range.drop.test.ts +3 -1
  330. package/src/core/objects/type/range/range.diff.test.ts +77 -0
  331. package/src/core/objects/type/range/range.diff.ts +39 -101
  332. package/src/core/objects/type/range/range.model.ts +1 -1
  333. package/src/core/objects/view/changes/view.alter.test.ts +8 -3
  334. package/src/core/objects/view/changes/view.create.test.ts +7 -2
  335. package/src/core/objects/view/changes/view.drop.test.ts +4 -1
  336. package/src/core/objects/view/view.diff.test.ts +82 -0
  337. package/src/core/objects/view/view.diff.ts +41 -191
  338. package/src/core/objects/view/view.model.ts +3 -17
  339. package/src/core/plan/apply.ts +9 -27
  340. package/src/core/plan/create.ts +173 -237
  341. package/src/core/plan/serialize.test.ts +317 -0
  342. package/src/core/plan/serialize.ts +18 -4
  343. package/src/core/plan/sql-format/fixtures.ts +2 -5
  344. package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +52 -0
  345. package/src/core/plan/sql-format/format-off.test.ts +14 -17
  346. package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +27 -22
  347. package/src/core/plan/sql-format/format-pretty-narrow.test.ts +17 -21
  348. package/src/core/plan/sql-format/format-pretty-preserve.test.ts +25 -20
  349. package/src/core/plan/sql-format/format-pretty-upper.test.ts +23 -20
  350. package/src/core/plan/sql-format/keyword-case.ts +36 -1
  351. package/src/core/plan/ssl-config.ts +172 -0
  352. package/src/core/plan/types.ts +6 -0
  353. package/src/core/postgres-config.ts +71 -2
  354. package/src/core/sort/graph-builder.ts +12 -0
  355. package/src/core/sort/logical-sort.test.ts +371 -0
  356. package/src/core/sort/logical-sort.ts +32 -25
  357. package/src/core/sort/topological-sort.test.ts +275 -0
  358. package/src/core/test-utils/assert-valid-sql.ts +20 -0
  359. package/src/index.ts +26 -2
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Declarative export command - export a declarative SQL schema from a database diff.
3
+ */
4
+
5
+ import { mkdir, rm, writeFile } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { buildCommand, type CommandContext } from "@stricli/core";
8
+ import chalk from "chalk";
9
+ import type { CatalogSnapshot } from "../../core/catalog.snapshot.ts";
10
+ import { exportDeclarativeSchema } from "../../core/export/index.ts";
11
+ import type { Grouping, GroupingPattern } from "../../core/export/types.ts";
12
+ import type { FilterDSL } from "../../core/integrations/filter/dsl.ts";
13
+ import type { ChangeFilter } from "../../core/integrations/filter/filter.types.ts";
14
+ import type { SerializeDSL } from "../../core/integrations/serialize/dsl.ts";
15
+ import type { ChangeSerializer } from "../../core/integrations/serialize/serialize.types.ts";
16
+ import { createPlan } from "../../core/plan/index.ts";
17
+ import type { SqlFormatOptions } from "../../core/plan/sql-format.ts";
18
+ import {
19
+ assertSafePath,
20
+ buildFileTree,
21
+ computeFileDiff,
22
+ formatExportSummary,
23
+ } from "../utils/export-display.ts";
24
+ import { loadIntegrationDSL } from "../utils/integrations.ts";
25
+ import { isPostgresUrl, loadCatalogFromFile } from "../utils/resolve-input.ts";
26
+
27
+ function parseJsonFlag<T>(label: string, value: string): T {
28
+ try {
29
+ return JSON.parse(value) as T;
30
+ } catch (error) {
31
+ throw new Error(
32
+ `Invalid ${label} JSON: ${error instanceof Error ? error.message : String(error)}`,
33
+ );
34
+ }
35
+ }
36
+
37
+ export const declarativeExportCommand = buildCommand({
38
+ parameters: {
39
+ flags: {
40
+ source: {
41
+ kind: "parsed",
42
+ brief:
43
+ "Source (current state): postgres URL or catalog snapshot file path. Omit to export all objects from target.",
44
+ parse: String,
45
+ optional: true,
46
+ },
47
+ target: {
48
+ kind: "parsed",
49
+ brief:
50
+ "Target (desired state): postgres URL or catalog snapshot file path",
51
+ parse: String,
52
+ },
53
+ output: {
54
+ kind: "parsed",
55
+ brief: "Output directory path for declarative schema files",
56
+ parse: String,
57
+ },
58
+ integration: {
59
+ kind: "parsed",
60
+ brief:
61
+ "Integration name (e.g., 'supabase') or path to integration JSON file",
62
+ parse: String,
63
+ optional: true,
64
+ },
65
+ filter: {
66
+ kind: "parsed",
67
+ brief: 'Filter DSL as inline JSON (e.g., \'{"schema":"public"}\')',
68
+ parse: (v: string) => parseJsonFlag<FilterDSL>("filter", v),
69
+ optional: true,
70
+ },
71
+ serialize: {
72
+ kind: "parsed",
73
+ brief:
74
+ 'Serialize DSL as inline JSON array (e.g., \'[{"when":{"type":"schema"},"options":{"skipAuthorization":true}}]\')',
75
+ parse: (v: string) => parseJsonFlag<SerializeDSL>("serialize", v),
76
+ optional: true,
77
+ },
78
+ "grouping-mode": {
79
+ kind: "enum",
80
+ brief: "How grouped entities are organized on disk",
81
+ values: ["single-file", "subdirectory"] as const,
82
+ optional: true,
83
+ },
84
+ "group-patterns": {
85
+ kind: "parsed",
86
+ brief:
87
+ 'JSON array of {pattern, name} objects (e.g., \'[{"pattern":"^auth","name":"auth"}]\')',
88
+ parse: (v: string) => {
89
+ const parsed = parseJsonFlag<GroupingPattern[]>("group-patterns", v);
90
+ if (!Array.isArray(parsed)) {
91
+ throw new Error("group-patterns must be a JSON array");
92
+ }
93
+ return parsed;
94
+ },
95
+ optional: true,
96
+ },
97
+ "flat-schemas": {
98
+ kind: "parsed",
99
+ brief:
100
+ "Comma-separated list of schemas to flatten (e.g., partman,pgboss,audit)",
101
+ parse: String,
102
+ optional: true,
103
+ },
104
+ "format-options": {
105
+ kind: "parsed",
106
+ brief:
107
+ 'SQL format options as inline JSON (e.g., \'{"keywordCase":"lower","maxWidth":180}\')',
108
+ parse: (v: string) =>
109
+ parseJsonFlag<SqlFormatOptions>("format-options", v),
110
+ optional: true,
111
+ },
112
+ force: {
113
+ kind: "boolean",
114
+ brief: "Remove entire output directory before writing",
115
+ optional: true,
116
+ },
117
+ "dry-run": {
118
+ kind: "boolean",
119
+ brief: "Show tree and summary without writing files",
120
+ optional: true,
121
+ },
122
+ "diff-focus": {
123
+ kind: "boolean",
124
+ brief:
125
+ "Show only files that changed (created/updated/deleted) in the tree",
126
+ optional: true,
127
+ },
128
+ verbose: {
129
+ kind: "boolean",
130
+ brief: "Show detailed output",
131
+ optional: true,
132
+ },
133
+ },
134
+ aliases: {
135
+ s: "source",
136
+ t: "target",
137
+ o: "output",
138
+ },
139
+ },
140
+ docs: {
141
+ brief: "Export a declarative schema from a database diff",
142
+ fullDescription: `
143
+ Export a declarative SQL schema by comparing two databases (source → target).
144
+ Writes .sql files to the output directory, grouped by object type and optional
145
+ grouping rules.
146
+
147
+ When --source is omitted, all objects from the target database are exported
148
+ (equivalent to diffing from an empty database).
149
+
150
+ Flags:
151
+ source - Source database connection URL (optional; omit for full export)
152
+ target - Target database connection URL (desired state)
153
+ output - Directory path for generated .sql files
154
+ integration - Integration name or path (e.g., supabase) for filter/serialize
155
+ filter - Filter DSL as JSON to include/exclude changes
156
+ serialize - Serialize DSL as JSON array for custom SQL generation
157
+ grouping-mode - single-file or subdirectory for grouped entities
158
+ group-patterns - JSON array of {pattern, name} for name-based grouping
159
+ flat-schemas - Comma-separated schemas to merge into one file per category
160
+ format-options - SQL format options as JSON
161
+ force - Remove output directory before writing (full replace)
162
+ dry-run - Show tree and summary only, do not write files
163
+ diff-focus - Show only changed files (created/updated/deleted) in the tree
164
+ verbose - Show detailed output
165
+
166
+ After export, a tip is printed with the command to apply the schema to an empty database.
167
+ `.trim(),
168
+ },
169
+ async func(
170
+ this: CommandContext,
171
+ flags: {
172
+ source?: string;
173
+ target: string;
174
+ output: string;
175
+ integration?: string;
176
+ filter?: FilterDSL;
177
+ serialize?: SerializeDSL;
178
+ "grouping-mode"?: "single-file" | "subdirectory";
179
+ "group-patterns"?: GroupingPattern[];
180
+ "flat-schemas"?: string;
181
+ "format-options"?: SqlFormatOptions;
182
+ force?: boolean;
183
+ "dry-run"?: boolean;
184
+ "diff-focus"?: boolean;
185
+ verbose?: boolean;
186
+ },
187
+ ) {
188
+ const { compileSerializeDSL } = await import(
189
+ "../../core/integrations/serialize/dsl.ts"
190
+ );
191
+
192
+ let filterOption: FilterDSL | ChangeFilter | undefined = flags.filter;
193
+ let serializeOption: SerializeDSL | ChangeSerializer | undefined =
194
+ flags.serialize;
195
+ let integrationEmptyCatalog: CatalogSnapshot | undefined;
196
+ if (flags.integration) {
197
+ const integrationDSL = await loadIntegrationDSL(flags.integration);
198
+ filterOption = filterOption ?? integrationDSL.filter;
199
+ serializeOption = serializeOption ?? integrationDSL.serialize;
200
+ integrationEmptyCatalog = integrationDSL.emptyCatalog;
201
+ }
202
+
203
+ const resolvedSource = flags.source
204
+ ? isPostgresUrl(flags.source)
205
+ ? flags.source
206
+ : await loadCatalogFromFile(flags.source)
207
+ : integrationEmptyCatalog
208
+ ? (await import("../../core/catalog.snapshot.ts")).deserializeCatalog(
209
+ integrationEmptyCatalog,
210
+ )
211
+ : null;
212
+
213
+ const resolvedTarget = isPostgresUrl(flags.target)
214
+ ? flags.target
215
+ : await loadCatalogFromFile(flags.target);
216
+
217
+ // Pass raw DSL to createPlan (not pre-compiled functions).
218
+ // createPlan compiles them internally and uses the DSL type to correctly
219
+ // determine cascade behavior: DSL filters disable cascading by default
220
+ // (unless cascade:true is set), preventing unintended exclusion of
221
+ // changes that depend on filtered objects (e.g. RLS policies that
222
+ // reference auth.uid() when the auth schema is filtered out).
223
+ const planResult = await createPlan(resolvedSource, resolvedTarget, {
224
+ filter: filterOption,
225
+ serialize: serializeOption,
226
+ skipDefaultPrivilegeSubtraction: true,
227
+ });
228
+
229
+ if (!planResult) {
230
+ this.process.stdout.write("No changes detected.\n");
231
+ return;
232
+ }
233
+
234
+ const hasGrouping =
235
+ flags["grouping-mode"] !== undefined ||
236
+ (flags["group-patterns"] !== undefined &&
237
+ flags["group-patterns"].length > 0) ||
238
+ (flags["flat-schemas"] !== undefined && flags["flat-schemas"].length > 0);
239
+
240
+ let grouping: Grouping | undefined;
241
+ if (hasGrouping) {
242
+ grouping = {
243
+ mode: flags["grouping-mode"] ?? "single-file",
244
+ groupPatterns: flags["group-patterns"],
245
+ autoGroupPartitions: true,
246
+ flatSchemas:
247
+ flags["flat-schemas"] !== undefined
248
+ ? flags["flat-schemas"]
249
+ .split(",")
250
+ .map((s) => s.trim())
251
+ .filter(Boolean)
252
+ : undefined,
253
+ };
254
+ }
255
+
256
+ const serializeFn =
257
+ serializeOption !== undefined
258
+ ? compileSerializeDSL(serializeOption)
259
+ : undefined;
260
+
261
+ const output = exportDeclarativeSchema(planResult, {
262
+ integration:
263
+ serializeFn !== undefined ? { serialize: serializeFn } : undefined,
264
+ formatOptions: flags["format-options"] ?? undefined,
265
+ grouping,
266
+ onWarning: (msg) => {
267
+ this.process.stderr.write(chalk.yellow(`Warning: ${msg}\n`));
268
+ },
269
+ });
270
+
271
+ const outputDir = path.resolve(flags.output);
272
+ const applyTip = (dir: string) =>
273
+ `\nTip: To apply this schema to an empty database, run:\n pgdelta declarative apply --path ${dir} --target <database_url>\n`;
274
+ const diff = await computeFileDiff(outputDir, output.files);
275
+
276
+ this.process.stdout.write("\n");
277
+ this.process.stdout.write(
278
+ `${buildFileTree(
279
+ output.files.map((f) => f.path),
280
+ path.basename(outputDir) || outputDir,
281
+ { diff, diffFocus: !!flags["diff-focus"] },
282
+ )}\n`,
283
+ );
284
+ this.process.stdout.write("\n");
285
+ this.process.stdout.write(
286
+ `${chalk.green("+")} created ${chalk.yellow("~")} updated ${chalk.red("-")} deleted\n`,
287
+ );
288
+ this.process.stdout.write("\n");
289
+
290
+ const summary = formatExportSummary(diff, !!flags["dry-run"]);
291
+ if (summary) {
292
+ this.process.stdout.write(`${summary}\n`);
293
+ }
294
+
295
+ const totalChanges = planResult.sortedChanges.length;
296
+ const totalStatements = output.files.reduce((s, f) => s + f.statements, 0);
297
+ this.process.stdout.write(
298
+ `Changes: ${totalChanges} | Files: ${output.files.length} | Statements: ${totalStatements}\n`,
299
+ );
300
+
301
+ if (flags["dry-run"]) {
302
+ this.process.stdout.write(chalk.dim("\n(dry-run: no files written)\n"));
303
+ this.process.stdout.write(chalk.cyan(applyTip(outputDir)));
304
+ return;
305
+ }
306
+
307
+ if (flags.force) {
308
+ await rm(outputDir, { recursive: true, force: true });
309
+ await mkdir(outputDir, { recursive: true });
310
+ } else if (diff.deleted.length > 0) {
311
+ this.process.stderr.write(
312
+ chalk.yellow(
313
+ `Warning: ${diff.deleted.length} existing file(s) will no longer be present. Use --force to replace the output directory.\n`,
314
+ ),
315
+ );
316
+ }
317
+
318
+ for (const file of output.files) {
319
+ assertSafePath(file.path, outputDir);
320
+ const filePath = path.join(outputDir, file.path);
321
+ await mkdir(path.dirname(filePath), { recursive: true });
322
+ await writeFile(filePath, file.sql);
323
+ }
324
+
325
+ this.process.stdout.write(
326
+ chalk.green(`Wrote ${output.files.length} file(s) to ${outputDir}\n`),
327
+ );
328
+ this.process.stdout.write(chalk.cyan(applyTip(outputDir)));
329
+ },
330
+ });
@@ -10,7 +10,9 @@ import type { SerializeDSL } from "../../core/integrations/serialize/dsl.ts";
10
10
  import type { ChangeSerializer } from "../../core/integrations/serialize/serialize.types.ts";
11
11
  import { createPlan } from "../../core/plan/index.ts";
12
12
  import type { SqlFormatOptions } from "../../core/plan/sql-format.ts";
13
+ import { setCommandExitCode } from "../exit-code.ts";
13
14
  import { loadIntegrationDSL } from "../utils/integrations.ts";
15
+ import { isPostgresUrl, loadCatalogFromFile } from "../utils/resolve-input.ts";
14
16
  import { formatPlanForDisplay } from "../utils.ts";
15
17
 
16
18
  export const planCommand = buildCommand({
@@ -18,12 +20,15 @@ export const planCommand = buildCommand({
18
20
  flags: {
19
21
  source: {
20
22
  kind: "parsed",
21
- brief: "Source database connection URL (current state)",
23
+ brief:
24
+ "Source (current state): postgres URL or catalog snapshot file path. Omit for empty baseline.",
22
25
  parse: String,
26
+ optional: true,
23
27
  },
24
28
  target: {
25
29
  kind: "parsed",
26
- brief: "Target database connection URL (desired state)",
30
+ brief:
31
+ "Target (desired state): postgres URL or catalog snapshot file path",
27
32
  parse: String,
28
33
  },
29
34
  format: {
@@ -121,7 +126,7 @@ json/sql outputs are available for artifacts or piping.
121
126
  async func(
122
127
  this: CommandContext,
123
128
  flags: {
124
- source: string;
129
+ source?: string;
125
130
  target: string;
126
131
  format?: "json" | "sql";
127
132
  output?: string;
@@ -133,18 +138,34 @@ json/sql outputs are available for artifacts or piping.
133
138
  "sql-format-options"?: SqlFormatOptions;
134
139
  },
135
140
  ) {
136
- // Load integration if provided and extract filter/serialize DSL
137
141
  let filterOption: FilterDSL | ChangeFilter | undefined = flags.filter;
138
142
  let serializeOption: SerializeDSL | ChangeSerializer | undefined =
139
143
  flags.serialize;
144
+ let integrationEmptyCatalog:
145
+ | import("../../core/catalog.snapshot.ts").CatalogSnapshot
146
+ | undefined;
140
147
  if (flags.integration) {
141
148
  const integrationDSL = await loadIntegrationDSL(flags.integration);
142
- // Use integration DSL if explicit flags not provided
143
149
  filterOption = filterOption ?? integrationDSL.filter;
144
150
  serializeOption = serializeOption ?? integrationDSL.serialize;
151
+ integrationEmptyCatalog = integrationDSL.emptyCatalog;
145
152
  }
146
153
 
147
- const planResult = await createPlan(flags.source, flags.target, {
154
+ const resolvedSource = flags.source
155
+ ? isPostgresUrl(flags.source)
156
+ ? flags.source
157
+ : await loadCatalogFromFile(flags.source)
158
+ : integrationEmptyCatalog
159
+ ? (await import("../../core/catalog.snapshot.ts")).deserializeCatalog(
160
+ integrationEmptyCatalog,
161
+ )
162
+ : null;
163
+
164
+ const resolvedTarget = isPostgresUrl(flags.target)
165
+ ? flags.target
166
+ : await loadCatalogFromFile(flags.target);
167
+
168
+ const planResult = await createPlan(resolvedSource, resolvedTarget, {
148
169
  role: flags.role,
149
170
  filter: filterOption,
150
171
  serialize: serializeOption,
@@ -190,6 +211,6 @@ json/sql outputs are available for artifacts or piping.
190
211
  }
191
212
 
192
213
  // Exit code 2 indicates changes were detected
193
- process.exitCode = 2;
214
+ setCommandExitCode(2);
194
215
  },
195
216
  });
@@ -0,0 +1,19 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { getCommandExitCode, setCommandExitCode } from "./exit-code.ts";
3
+
4
+ describe("exit-code", () => {
5
+ test("getCommandExitCode returns undefined when never set", () => {
6
+ expect(getCommandExitCode()).toBeUndefined();
7
+ });
8
+
9
+ test("setCommandExitCode then getCommandExitCode returns the value", () => {
10
+ setCommandExitCode(2);
11
+ expect(getCommandExitCode()).toBe(2);
12
+ });
13
+
14
+ test("setCommandExitCode overwrites previous value", () => {
15
+ setCommandExitCode(2);
16
+ setCommandExitCode(0);
17
+ expect(getCommandExitCode()).toBe(0);
18
+ });
19
+ });
@@ -0,0 +1,7 @@
1
+ let _exitCode: number | undefined;
2
+ export function setCommandExitCode(code: number) {
3
+ _exitCode = code;
4
+ }
5
+ export function getCommandExitCode() {
6
+ return _exitCode;
7
+ }
@@ -18,7 +18,7 @@ export function formatTree(plan: HierarchicalPlan): string {
18
18
  lines.push(
19
19
  chalk.bold(`📋 Migration Plan: ${total} change${total !== 1 ? "s" : ""}`),
20
20
  );
21
- const summary = buildSummaryLine(plan);
21
+ const summary = buildPlanSummaryTable(plan);
22
22
  if (summary) {
23
23
  lines.push("");
24
24
  lines.push(summary);
@@ -54,8 +54,9 @@ function countTotalChanges(plan: HierarchicalPlan): number {
54
54
 
55
55
  /**
56
56
  * Build summary as a table showing counts by entity type and operation.
57
+ * Exported for use by declarative-export to show the same summary style.
57
58
  */
58
- function buildSummaryLine(plan: HierarchicalPlan): string {
59
+ function buildPlanSummaryTable(plan: HierarchicalPlan): string {
59
60
  // Count by object type
60
61
  const byType: Record<
61
62
  string,