@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
@@ -198,6 +198,27 @@ const STRUCTURAL_TOP_LEVEL_KEYWORDS = new Set([
198
198
  "WRAPPER",
199
199
  "MAPPING",
200
200
  ]);
201
+ const EMPTY_SCOPED_SET = new Set();
202
+ const ALTER_DEFAULT_PRIVILEGES_KEYWORDS = new Set([
203
+ "PUBLIC",
204
+ "SEQUENCES",
205
+ "ROUTINES",
206
+ "TYPES",
207
+ "SCHEMAS",
208
+ ]);
209
+ const GRANT_REVOKE_KEYWORDS = new Set(["PUBLIC"]);
210
+ function getStatementScopedKeywords(topLevelTokens) {
211
+ const first = topLevelTokens[0]?.token.upper;
212
+ const second = topLevelTokens[1]?.token.upper;
213
+ const third = topLevelTokens[2]?.token.upper;
214
+ if (first === "ALTER" && second === "DEFAULT" && third === "PRIVILEGES") {
215
+ return ALTER_DEFAULT_PRIVILEGES_KEYWORDS;
216
+ }
217
+ if (first === "GRANT" || first === "REVOKE") {
218
+ return GRANT_REVOKE_KEYWORDS;
219
+ }
220
+ return EMPTY_SCOPED_SET;
221
+ }
201
222
  const ALTER_TYPE_BOUNDARY_KEYWORDS = new Set([
202
223
  "COLLATE",
203
224
  "USING",
@@ -259,6 +280,7 @@ function collectCaseableTokenStarts(statement, tokens) {
259
280
  if (topLevelTokens.length === 0)
260
281
  return caseable;
261
282
  const command = topLevelTokens[0].token.upper;
283
+ const scopedKeywords = getStatementScopedKeywords(topLevelTokens);
262
284
  const objectNameTokenIndexes = new Set();
263
285
  for (let topIndex = 0; topIndex < topLevelTokens.length; topIndex += 1) {
264
286
  if (isLikelyObjectNameToken(command, topLevelTokens, topIndex)) {
@@ -268,7 +290,7 @@ function collectCaseableTokenStarts(statement, tokens) {
268
290
  for (let index = 0; index < tokens.length; index += 1) {
269
291
  const token = tokens[index];
270
292
  const upper = token.upper;
271
- if (!STRUCTURAL_TOP_LEVEL_KEYWORDS.has(upper))
293
+ if (!STRUCTURAL_TOP_LEVEL_KEYWORDS.has(upper) && !scopedKeywords.has(upper))
272
294
  continue;
273
295
  if (objectNameTokenIndexes.has(index))
274
296
  continue;
@@ -379,6 +401,9 @@ function isCaseableInContext(command, upper, prev) {
379
401
  if (upper === "IDENTITY") {
380
402
  return prev === "REPLICA" || prev === "AS";
381
403
  }
404
+ if (upper === "PUBLIC") {
405
+ return prev === "TO" || prev === "FROM";
406
+ }
382
407
  if (upper === "OR") {
383
408
  return true;
384
409
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * SSL configuration parsing for PostgreSQL connection URLs.
3
+ *
4
+ * Supports sslmode and certificate paths (URL params or env). Used by plan,
5
+ * apply, and catalog-export when connecting to source/target databases.
6
+ */
7
+ /** Parsed SSL options for the pg client plus URL with SSL params stripped (internal). */
8
+ type SslConfig = {
9
+ ssl?: boolean | {
10
+ rejectUnauthorized: boolean;
11
+ ca?: string;
12
+ cert?: string;
13
+ key?: string;
14
+ /**
15
+ * Custom server identity check function.
16
+ * Used to skip hostname verification for verify-ca mode.
17
+ * Returns undefined to indicate success (no error).
18
+ */
19
+ checkServerIdentity?: () => undefined;
20
+ };
21
+ cleanedUrl: string;
22
+ };
23
+ /**
24
+ * Parse SSL configuration from a PostgreSQL connection URL.
25
+ * Supports sslmode (require, verify-ca, verify-full, prefer, disable).
26
+ * Certificates can be provided via:
27
+ * - Query string parameters (file paths): sslrootcert, sslcert, sslkey (preferred)
28
+ * - Environment variables (content): PGDELTA_SOURCE_SSLROOTCERT/SSLCERT/SSLKEY or PGDELTA_TARGET_SSLROOTCERT/SSLCERT/SSLKEY
29
+ * Returns SSL options for the postgres.js library and a cleaned URL without SSL-related query parameters.
30
+ */
31
+ export declare function parseSslConfig(url: string, connectionType: "source" | "target"): Promise<SslConfig>;
32
+ export {};
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SSL configuration parsing for PostgreSQL connection URLs.
3
+ *
4
+ * Supports sslmode and certificate paths (URL params or env). Used by plan,
5
+ * apply, and catalog-export when connecting to source/target databases.
6
+ */
7
+ import { readFile } from "node:fs/promises";
8
+ /**
9
+ * Parse SSL configuration from a PostgreSQL connection URL.
10
+ * Supports sslmode (require, verify-ca, verify-full, prefer, disable).
11
+ * Certificates can be provided via:
12
+ * - Query string parameters (file paths): sslrootcert, sslcert, sslkey (preferred)
13
+ * - Environment variables (content): PGDELTA_SOURCE_SSLROOTCERT/SSLCERT/SSLKEY or PGDELTA_TARGET_SSLROOTCERT/SSLCERT/SSLKEY
14
+ * Returns SSL options for the postgres.js library and a cleaned URL without SSL-related query parameters.
15
+ */
16
+ export async function parseSslConfig(url, connectionType) {
17
+ const urlObj = new URL(url);
18
+ const sslmode = urlObj.searchParams.get("sslmode");
19
+ const sslrootcert = urlObj.searchParams.get("sslrootcert");
20
+ const sslcert = urlObj.searchParams.get("sslcert");
21
+ const sslkey = urlObj.searchParams.get("sslkey");
22
+ // Remove SSL-related query parameters since we parse them ourselves
23
+ urlObj.searchParams.delete("sslmode");
24
+ urlObj.searchParams.delete("sslrootcert");
25
+ urlObj.searchParams.delete("sslcert");
26
+ urlObj.searchParams.delete("sslkey");
27
+ const cleanedUrl = urlObj.toString();
28
+ // Handle different SSL modes
29
+ if (sslmode === "disable") {
30
+ return { cleanedUrl, ssl: false };
31
+ }
32
+ if (sslmode === "require" ||
33
+ sslmode === "prefer" ||
34
+ sslmode === "verify-ca" ||
35
+ sslmode === "verify-full") {
36
+ // Helper function to get certificate value: query param (file path) takes precedence over env var (content)
37
+ const getCertValue = async (queryParam, envVarName) => {
38
+ // Prefer query parameter (file path)
39
+ if (queryParam) {
40
+ try {
41
+ return await readFile(queryParam, "utf-8");
42
+ }
43
+ catch (error) {
44
+ throw new Error(`Failed to read certificate file '${queryParam}': ${error instanceof Error ? error.message : String(error)}`);
45
+ }
46
+ }
47
+ // Fallback to environment variable (content)
48
+ const envValue = process.env[envVarName];
49
+ return envValue || undefined;
50
+ };
51
+ const hasExplicitVerification = sslmode === "verify-ca" || sslmode === "verify-full";
52
+ // Get CA certificate value.
53
+ // - verify-ca/verify-full: check query param first, then env var
54
+ // - require/prefer: only check query param (libpq backward compatibility
55
+ // requires an explicit root CA *file*, not a global env var)
56
+ const caEnvVar = connectionType === "source"
57
+ ? "PGDELTA_SOURCE_SSLROOTCERT"
58
+ : "PGDELTA_TARGET_SSLROOTCERT";
59
+ let caValue;
60
+ if (sslrootcert) {
61
+ // Explicit file path in query param — always honour it
62
+ caValue = await getCertValue(sslrootcert, caEnvVar);
63
+ }
64
+ else if (hasExplicitVerification) {
65
+ // verify-ca / verify-full without file path — fall back to env var
66
+ caValue = await getCertValue(null, caEnvVar);
67
+ }
68
+ // require/prefer without sslrootcert: no CA cert, no verification
69
+ // Determine if we should verify the CA chain
70
+ // From PostgreSQL docs: "if a root CA file exists, the behavior of sslmode=require
71
+ // will be the same as that of verify-ca"
72
+ const hasLibpqCompatibility = (sslmode === "require" || sslmode === "prefer") && caValue !== undefined;
73
+ const shouldVerifyCa = hasExplicitVerification || hasLibpqCompatibility;
74
+ // Determine if we should verify hostname
75
+ // - verify-full: verify both CA and hostname
76
+ // - verify-ca: verify CA only (skip hostname)
77
+ // - require/prefer with CA (libpq compat): behaves like verify-ca (skip hostname)
78
+ const shouldVerifyHostname = sslmode === "verify-full";
79
+ const ssl = {
80
+ rejectUnauthorized: shouldVerifyCa,
81
+ };
82
+ // Add CA certificate if verifying
83
+ if (shouldVerifyCa && caValue) {
84
+ ssl.ca = caValue;
85
+ }
86
+ // For verify-ca and libpq compatibility mode: skip hostname verification
87
+ // This matches PostgreSQL semantics where verify-ca only checks the CA chain
88
+ if (shouldVerifyCa && !shouldVerifyHostname) {
89
+ ssl.checkServerIdentity = () => undefined;
90
+ }
91
+ // Get client certificate (optional, for mutual TLS)
92
+ const certEnvVar = connectionType === "source"
93
+ ? "PGDELTA_SOURCE_SSLCERT"
94
+ : "PGDELTA_TARGET_SSLCERT";
95
+ const certValue = await getCertValue(sslcert, certEnvVar);
96
+ if (certValue) {
97
+ ssl.cert = certValue;
98
+ }
99
+ // Get client key (optional, for mutual TLS, required if cert is provided)
100
+ const keyEnvVar = connectionType === "source"
101
+ ? "PGDELTA_SOURCE_SSLKEY"
102
+ : "PGDELTA_TARGET_SSLKEY";
103
+ const keyValue = await getCertValue(sslkey, keyEnvVar);
104
+ if (keyValue) {
105
+ ssl.key = keyValue;
106
+ }
107
+ // Warn if cert is provided without key (or vice versa)
108
+ if ((ssl.cert && !ssl.key) || (!ssl.cert && ssl.key)) {
109
+ throw new Error("Both client certificate and key must be provided together for mutual TLS");
110
+ }
111
+ return { ssl, cleanedUrl };
112
+ }
113
+ // No sslmode specified or invalid value - explicitly disable SSL
114
+ return { cleanedUrl, ssl: false };
115
+ }
@@ -138,5 +138,11 @@ export interface CreatePlanOptions {
138
138
  serialize?: SerializeDSL | ChangeSerializer;
139
139
  /** Role to use when executing the migration (SET ROLE will be added to statements) */
140
140
  role?: string;
141
+ /**
142
+ * When true, don't subtract privileges covered by ALTER DEFAULT PRIVILEGES
143
+ * from explicit GRANTs during diffing. Use this for declarative export where
144
+ * the output must be self-contained and not rely on statement execution order.
145
+ */
146
+ skipDefaultPrivilegeSubtraction?: boolean;
141
147
  }
142
148
  export {};
@@ -34,5 +34,19 @@ export declare function createPool(connectionString: string, options?: CreatePoo
34
34
  * inside each `client.end()` callback — ensuring all sockets are
35
35
  * truly closed before it resolves.
36
36
  */
37
+ /**
38
+ * Create a pool from a connection URL with standard session setup:
39
+ * SSL parsing, search_path isolation, optional SET ROLE, and 57P01 suppression.
40
+ *
41
+ * Returns the pool and a `close` function that properly waits for all sockets
42
+ * to close (via {@link endPool}).
43
+ */
44
+ export declare function createManagedPool(url: string, options?: {
45
+ role?: string;
46
+ label?: "source" | "target";
47
+ }): Promise<{
48
+ pool: Pool;
49
+ close: () => Promise<void>;
50
+ }>;
37
51
  export declare function endPool(pool: Pool): Promise<void>;
38
52
  export {};
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * PostgreSQL connection configuration with custom type handlers.
3
3
  */
4
- import { Pool, types } from "pg";
4
+ import { escapeIdentifier, Pool, types } from "pg";
5
+ import { parseSslConfig } from "./plan/ssl-config.js";
5
6
  // ============================================================================
6
7
  // Array Parser
7
8
  // ============================================================================
@@ -92,12 +93,20 @@ types.setTypeParser(1005, (val) => parseArray(val, parseIntElement)); // int2[]
92
93
  types.setTypeParser(1007, (val) => parseArray(val, parseIntElement)); // int4[]
93
94
  // @ts-expect-error - pg types expects TypeId but raw OID numbers work fine
94
95
  types.setTypeParser(1016, (val) => parseArray(val, parseIntElement)); // int8[]
96
+ const DEFAULT_POOL_MAX = Number(process.env.PGDELTA_POOL_MAX) || 5;
97
+ const DEFAULT_CONNECTION_TIMEOUT_MS = Number(process.env.PGDELTA_CONNECTION_TIMEOUT_MS) || 3_000;
98
+ const DEFAULT_CONNECT_TIMEOUT_MS = Number(process.env.PGDELTA_CONNECT_TIMEOUT_MS) || 2_500;
95
99
  /**
96
100
  * Create a Pool with custom type handlers and optional event listeners.
97
101
  */
98
102
  export function createPool(connectionString, options) {
99
103
  const { onConnect, onError, onAcquire, onRemove, ...config } = options ?? {};
100
- const pool = new Pool({ connectionString, ...config });
104
+ const pool = new Pool({
105
+ connectionString,
106
+ max: DEFAULT_POOL_MAX,
107
+ connectionTimeoutMillis: DEFAULT_CONNECTION_TIMEOUT_MS,
108
+ ...config,
109
+ });
101
110
  if (onConnect)
102
111
  pool.on("connect", onConnect);
103
112
  if (onError)
@@ -122,6 +131,48 @@ export function createPool(connectionString, options) {
122
131
  * inside each `client.end()` callback — ensuring all sockets are
123
132
  * truly closed before it resolves.
124
133
  */
134
+ /**
135
+ * Create a pool from a connection URL with standard session setup:
136
+ * SSL parsing, search_path isolation, optional SET ROLE, and 57P01 suppression.
137
+ *
138
+ * Returns the pool and a `close` function that properly waits for all sockets
139
+ * to close (via {@link endPool}).
140
+ */
141
+ export async function createManagedPool(url, options) {
142
+ const sslConfig = await parseSslConfig(url, options?.label ?? "target");
143
+ const pool = createPool(sslConfig.cleanedUrl, {
144
+ ...(sslConfig.ssl !== undefined ? { ssl: sslConfig.ssl } : {}),
145
+ onError: (err) => {
146
+ if (err.code !== "57P01") {
147
+ console.error("Pool error:", err);
148
+ }
149
+ },
150
+ onConnect: async (client) => {
151
+ await client.query("SET search_path = ''");
152
+ if (options?.role) {
153
+ await client.query(`SET ROLE ${escapeIdentifier(options.role)}`);
154
+ }
155
+ },
156
+ });
157
+ // Eagerly validate connectivity so SSL/auth failures surface immediately
158
+ // instead of hanging on the first real query. node-pg's connectionTimeoutMillis
159
+ // is not reliably enforced under Bun when SSL negotiation hangs.
160
+ const label = options?.label ?? "target";
161
+ const timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS;
162
+ try {
163
+ const client = await Promise.race([
164
+ pool.connect(),
165
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Connection to ${label} database timed out after ${timeoutMs}ms. ` +
166
+ `The server may require SSL, use an invalid certificate, or be unreachable.`)), timeoutMs)),
167
+ ]);
168
+ client.release();
169
+ }
170
+ catch (err) {
171
+ await pool.end().catch(() => { });
172
+ throw err;
173
+ }
174
+ return { pool, close: () => endPool(pool) };
175
+ }
125
176
  export function endPool(pool) {
126
177
  const clientCount = pool.totalCount;
127
178
  if (clientCount === 0) {
@@ -56,7 +56,17 @@ export function convertExplicitRequirementsToConstraints(phaseChanges, graphData
56
56
  const requiredIds = graphData.explicitRequirementSets[consumerIndex];
57
57
  if (requiredIds.size === 0)
58
58
  continue;
59
+ // Collect dropped IDs for this change so we can skip requirements
60
+ // for stableIds that this change also drops. A change that drops a
61
+ // stableId should not depend on another change that creates the same
62
+ // stableId, because the entity already exists in the source database.
63
+ // This prevents false ordering constraints such as Grant → Revoke
64
+ // when both operate on the same ACL stableId.
65
+ const droppedIds = new Set(phaseChanges[consumerIndex].drops);
59
66
  for (const requiredId of requiredIds) {
67
+ if (droppedIds.has(requiredId)) {
68
+ continue;
69
+ }
60
70
  const producerIndexes = graphData.changeIndexesByCreatedId.get(requiredId);
61
71
  if (!producerIndexes || producerIndexes.size === 0)
62
72
  continue;
@@ -145,6 +145,25 @@ function getMainStableId(change) {
145
145
  }
146
146
  return null;
147
147
  }
148
+ // For default_privilege operations: group by role + schema combination (before CREATE so we group and use tiebreaker)
149
+ if (change.scope === "default_privilege") {
150
+ if (change.requires.length > 0) {
151
+ let grantingRole = null;
152
+ let schemaId = null;
153
+ for (const id of change.requires) {
154
+ if (id.startsWith("role:")) {
155
+ grantingRole = id;
156
+ }
157
+ else if (id.startsWith("schema:")) {
158
+ schemaId = id;
159
+ }
160
+ }
161
+ if (schemaId && grantingRole) {
162
+ return `${grantingRole}:${schemaId}`;
163
+ }
164
+ return grantingRole ?? null;
165
+ }
166
+ }
148
167
  // For CREATE operations: check if creating a constraint (sub-entity of table)
149
168
  if (change.operation === "create" && change.creates.length > 0) {
150
169
  // Iterate through creates to find the first non-metadata stable ID
@@ -182,29 +201,6 @@ function getMainStableId(change) {
182
201
  // Fallback: if all drops are metadata, use first
183
202
  return change.drops[0] ?? null;
184
203
  }
185
- // For default_privilege operations: group by role + schema combination
186
- // This groups all "FOR ROLE X IN SCHEMA Y" statements together
187
- if (change.scope === "default_privilege") {
188
- if (change.requires.length > 0) {
189
- // Iterate through requires to find role and schema
190
- let grantingRole = null;
191
- let schemaId = null;
192
- for (const id of change.requires) {
193
- if (id.startsWith("role:")) {
194
- grantingRole = id;
195
- }
196
- else if (id.startsWith("schema:")) {
197
- schemaId = id;
198
- }
199
- }
200
- if (schemaId && grantingRole) {
201
- // Create composite key: "role:postgres:schema:public"
202
- return `${grantingRole}:${schemaId}`;
203
- }
204
- // If no schema, just group by role
205
- return grantingRole ?? null;
206
- }
207
- }
208
204
  // For ALTER operations: check if creating/dropping a constraint
209
205
  // Skip this for privilege/comment/default_privilege scopes (handled above)
210
206
  if (change.operation === "alter") {
@@ -525,6 +521,18 @@ function sortPhase(changes, phase) {
525
521
  if (operationOrderA !== operationOrderB) {
526
522
  return operationOrderA - operationOrderB;
527
523
  }
524
+ // 6b. For default_privilege: deterministic tiebreaker by objtype then grantee (canonical order for objtype)
525
+ if (scopeA === "default_privilege" && scopeB === "default_privilege") {
526
+ const defPrivA = changeA;
527
+ const defPrivB = changeB;
528
+ const objtypeOrder = (code) => ({ n: 0, r: 1, S: 2, f: 3, T: 4 })[code] ?? 99;
529
+ const objtypeCompare = objtypeOrder(defPrivA.objtype) - objtypeOrder(defPrivB.objtype);
530
+ if (objtypeCompare !== 0)
531
+ return objtypeCompare;
532
+ const granteeCompare = defPrivA.grantee.localeCompare(defPrivB.grantee);
533
+ if (granteeCompare !== 0)
534
+ return granteeCompare;
535
+ }
528
536
  // 7. Preserve original order (stability)
529
537
  return a.originalIndex - b.originalIndex;
530
538
  });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Assert that the given SQL string is syntactically valid PostgreSQL.
3
+ *
4
+ * Uses the PostgreSQL parser from `@supabase/pg-topo` to ensure that
5
+ * serialized DDL statements are syntactically correct. This catches
6
+ * issues like malformed function signatures, missing keywords, etc.
7
+ *
8
+ * @param sql - The SQL string to validate (typically from `change.serialize()`).
9
+ */
10
+ export declare function assertValidSql(sql: string): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import { validateSqlSyntax } from "@supabase/pg-topo";
2
+ /**
3
+ * Assert that the given SQL string is syntactically valid PostgreSQL.
4
+ *
5
+ * Uses the PostgreSQL parser from `@supabase/pg-topo` to ensure that
6
+ * serialized DDL statements are syntactically correct. This catches
7
+ * issues like malformed function signatures, missing keywords, etc.
8
+ *
9
+ * @param sql - The SQL string to validate (typically from `change.serialize()`).
10
+ */
11
+ export async function assertValidSql(sql) {
12
+ try {
13
+ await validateSqlSyntax(sql);
14
+ }
15
+ catch (error) {
16
+ const message = error instanceof Error ? error.message : "Unknown parser error";
17
+ throw new Error(`Invalid SQL syntax: ${message}\nSQL: ${sql}`);
18
+ }
19
+ }
package/dist/index.d.ts CHANGED
@@ -3,8 +3,14 @@
3
3
  *
4
4
  * This module exports the public API for the pg-delta library.
5
5
  */
6
+ export { Catalog, createEmptyCatalog, extractCatalog, } from "./core/catalog.model.ts";
7
+ export type { CatalogSnapshot } from "./core/catalog.snapshot.ts";
8
+ export { deserializeCatalog, serializeCatalog, stringifyCatalogSnapshot, } from "./core/catalog.snapshot.ts";
9
+ export { exportDeclarativeSchema } from "./core/export/index.ts";
10
+ export type { DeclarativeSchemaOutput, FileCategory, FileEntry, FileMetadata, } from "./core/export/types.ts";
6
11
  export type { IntegrationDSL } from "./core/integrations/integration-dsl.ts";
7
12
  export { applyPlan } from "./core/plan/apply.ts";
13
+ export type { CatalogInput } from "./core/plan/create.ts";
8
14
  export { createPlan } from "./core/plan/create.ts";
9
15
  export type { SqlFormatOptions } from "./core/plan/sql-format.ts";
10
16
  export { formatSqlStatements } from "./core/plan/sql-format.ts";
package/dist/index.js CHANGED
@@ -3,7 +3,12 @@
3
3
  *
4
4
  * This module exports the public API for the pg-delta library.
5
5
  */
6
+ // Catalog model and extraction
7
+ export { Catalog, createEmptyCatalog, extractCatalog, } from "./core/catalog.model.js";
8
+ export { deserializeCatalog, serializeCatalog, stringifyCatalogSnapshot, } from "./core/catalog.snapshot.js";
9
+ // Declarative schema export
10
+ export { exportDeclarativeSchema } from "./core/export/index.js";
11
+ // Plan operations
6
12
  export { applyPlan } from "./core/plan/apply.js";
7
- // Core operations
8
13
  export { createPlan } from "./core/plan/create.js";
9
14
  export { formatSqlStatements } from "./core/plan/sql-format.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supabase/pg-delta",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.6",
4
4
  "description": "PostgreSQL migrations made easy",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,6 +20,20 @@
20
20
  "require": "./dist/core/integrations/supabase.js",
21
21
  "types": "./dist/core/integrations/supabase.d.ts",
22
22
  "default": "./dist/core/integrations/supabase.js"
23
+ },
24
+ "./declarative": {
25
+ "bun": "./src/core/declarative-apply/index.ts",
26
+ "import": "./dist/core/declarative-apply/index.js",
27
+ "require": "./dist/core/declarative-apply/index.js",
28
+ "types": "./dist/core/declarative-apply/index.d.ts",
29
+ "default": "./dist/core/declarative-apply/index.js"
30
+ },
31
+ "./catalog-export": {
32
+ "bun": "./src/core/catalog-export/index.ts",
33
+ "import": "./dist/core/catalog-export/index.js",
34
+ "require": "./dist/core/catalog-export/index.js",
35
+ "types": "./dist/core/catalog-export/index.d.ts",
36
+ "default": "./dist/core/catalog-export/index.js"
23
37
  }
24
38
  },
25
39
  "bin": {
@@ -57,20 +71,23 @@
57
71
  "format-and-lint": "biome check . --error-on-warnings",
58
72
  "knip": "knip",
59
73
  "pgdelta": "bun src/cli/bin/cli.ts",
60
- "test": "bun test --concurrent --timeout 15000 --preload ./tests/global-setup.ts",
61
- "test:unit": "bun test --concurrent src/",
62
- "test:integration": "bun test --concurrent --timeout 15000 --preload ./tests/global-setup.ts --max-concurrency=4 tests/",
74
+ "test": "bun scripts/run-tests.ts",
75
+ "test:unit": "bun run test src/",
76
+ "test:integration": "bun run test tests/",
77
+ "update-empty-baseline": "bun scripts/update-empty-catalog-baseline.ts",
63
78
  "version": "changeset version && bun install --no-frozen-lockfile && bun run format-and-lint --write"
64
79
  },
65
80
  "dependencies": {
66
81
  "@stricli/core": "^1.2.4",
67
82
  "@ts-safeql/sql-tag": "^0.2.0",
83
+ "@supabase/pg-topo": "^1.0.0-alpha.1",
68
84
  "chalk": "^5.6.2",
69
85
  "debug": "^4.3.7",
70
86
  "pg": "^8.17.2",
71
87
  "zod": "^4.2.1"
72
88
  },
73
89
  "devDependencies": {
90
+ "@supabase/bun-istanbul-coverage": "workspace:*",
74
91
  "@tsconfig/node-ts": "^23.6.2",
75
92
  "@tsconfig/node24": "^24.0.3",
76
93
  "@types/bun": "^1.3.9",
package/src/cli/app.ts CHANGED
@@ -1,13 +1,35 @@
1
1
  import { buildApplication, buildRouteMap } from "@stricli/core";
2
2
  import { applyCommand } from "./commands/apply.ts";
3
+ import { catalogExportCommand } from "./commands/catalog-export.ts";
4
+ import { declarativeApplyCommand } from "./commands/declarative-apply.ts";
5
+ import { declarativeExportCommand } from "./commands/declarative-export.ts";
3
6
  import { planCommand } from "./commands/plan.ts";
4
7
  import { syncCommand } from "./commands/sync.ts";
5
8
 
9
+ const declarativeRouteMap = buildRouteMap({
10
+ routes: {
11
+ apply: declarativeApplyCommand,
12
+ export: declarativeExportCommand,
13
+ },
14
+ docs: {
15
+ brief: "Declarative schema management",
16
+ fullDescription: `
17
+ Manage declarative SQL schemas.
18
+
19
+ Commands:
20
+ apply - Apply a declarative SQL schema to a database
21
+ export - Export a declarative schema from a database diff
22
+ `.trim(),
23
+ },
24
+ });
25
+
6
26
  const root = buildRouteMap({
7
27
  routes: {
8
28
  plan: planCommand,
9
29
  apply: applyCommand,
10
30
  sync: syncCommand,
31
+ declarative: declarativeRouteMap,
32
+ "catalog-export": catalogExportCommand,
11
33
  },
12
34
  defaultCommand: "sync",
13
35
  docs: {
@@ -16,9 +38,11 @@ const root = buildRouteMap({
16
38
  pgdelta generates migration scripts by comparing two PostgreSQL databases.
17
39
 
18
40
  Commands:
19
- plan - Compute schema diff and preview changes
20
- apply - Apply a plan's migration script to a database
21
- sync - Plan and apply changes in one go
41
+ plan - Compute schema diff and preview changes
42
+ apply - Apply a plan's migration script to a database
43
+ sync - Plan and apply changes in one go
44
+ declarative - Declarative schema (apply | export)
45
+ catalog-export - Export a database catalog as a snapshot JSON file
22
46
  `.trim(),
23
47
  },
24
48
  });
@@ -2,8 +2,14 @@
2
2
 
3
3
  import { run } from "@stricli/core";
4
4
  import { app } from "../app.ts";
5
+ import { getCommandExitCode } from "../exit-code.ts";
5
6
 
6
7
  await run(app, process.argv.slice(2), { process }).catch((error) => {
7
8
  console.error(error);
8
9
  process.exit(1);
9
10
  });
11
+
12
+ const code = getCommandExitCode();
13
+ if (code !== undefined) {
14
+ process.exitCode = code;
15
+ }