@supatype/cli 0.1.0-alpha.10

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 (416) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +221 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -0
  6. package/bin/supatype.js +5 -0
  7. package/dist/app/framework.d.ts +44 -0
  8. package/dist/app/framework.d.ts.map +1 -0
  9. package/dist/app/framework.js +200 -0
  10. package/dist/app/framework.js.map +1 -0
  11. package/dist/app/proxy-dev-app.d.ts +13 -0
  12. package/dist/app/proxy-dev-app.d.ts.map +1 -0
  13. package/dist/app/proxy-dev-app.js +54 -0
  14. package/dist/app/proxy-dev-app.js.map +1 -0
  15. package/dist/app-config.d.ts +7 -0
  16. package/dist/app-config.d.ts.map +1 -0
  17. package/dist/app-config.js +113 -0
  18. package/dist/app-config.js.map +1 -0
  19. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  20. package/dist/augmentation-generator.d.ts +2 -0
  21. package/dist/augmentation-generator.d.ts.map +1 -0
  22. package/dist/augmentation-generator.js +111 -0
  23. package/dist/augmentation-generator.js.map +1 -0
  24. package/dist/binary-cache.d.ts +98 -0
  25. package/dist/binary-cache.d.ts.map +1 -0
  26. package/dist/binary-cache.js +687 -0
  27. package/dist/binary-cache.js.map +1 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +61 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/commands/admin.d.ts +4 -0
  33. package/dist/commands/admin.d.ts.map +1 -0
  34. package/dist/commands/admin.js +271 -0
  35. package/dist/commands/admin.js.map +1 -0
  36. package/dist/commands/app.d.ts +3 -0
  37. package/dist/commands/app.d.ts.map +1 -0
  38. package/dist/commands/app.js +82 -0
  39. package/dist/commands/app.js.map +1 -0
  40. package/dist/commands/cache.d.ts +6 -0
  41. package/dist/commands/cache.d.ts.map +1 -0
  42. package/dist/commands/cache.js +105 -0
  43. package/dist/commands/cache.js.map +1 -0
  44. package/dist/commands/cloud.d.ts +23 -0
  45. package/dist/commands/cloud.d.ts.map +1 -0
  46. package/dist/commands/cloud.js +254 -0
  47. package/dist/commands/cloud.js.map +1 -0
  48. package/dist/commands/db.d.ts +8 -0
  49. package/dist/commands/db.d.ts.map +1 -0
  50. package/dist/commands/db.js +116 -0
  51. package/dist/commands/db.js.map +1 -0
  52. package/dist/commands/deploy-types.d.ts +14 -0
  53. package/dist/commands/deploy-types.d.ts.map +1 -0
  54. package/dist/commands/deploy-types.js +38 -0
  55. package/dist/commands/deploy-types.js.map +1 -0
  56. package/dist/commands/deploy.d.ts +15 -0
  57. package/dist/commands/deploy.d.ts.map +1 -0
  58. package/dist/commands/deploy.js +322 -0
  59. package/dist/commands/deploy.js.map +1 -0
  60. package/dist/commands/dev.d.ts +14 -0
  61. package/dist/commands/dev.d.ts.map +1 -0
  62. package/dist/commands/dev.js +806 -0
  63. package/dist/commands/dev.js.map +1 -0
  64. package/dist/commands/diff.d.ts +3 -0
  65. package/dist/commands/diff.d.ts.map +1 -0
  66. package/dist/commands/diff.js +54 -0
  67. package/dist/commands/diff.js.map +1 -0
  68. package/dist/commands/engine.d.ts +7 -0
  69. package/dist/commands/engine.d.ts.map +1 -0
  70. package/dist/commands/engine.js +27 -0
  71. package/dist/commands/engine.js.map +1 -0
  72. package/dist/commands/functions.d.ts +3 -0
  73. package/dist/commands/functions.d.ts.map +1 -0
  74. package/dist/commands/functions.js +749 -0
  75. package/dist/commands/functions.js.map +1 -0
  76. package/dist/commands/generate.d.ts +3 -0
  77. package/dist/commands/generate.d.ts.map +1 -0
  78. package/dist/commands/generate.js +38 -0
  79. package/dist/commands/generate.js.map +1 -0
  80. package/dist/commands/init.d.ts +7 -0
  81. package/dist/commands/init.d.ts.map +1 -0
  82. package/dist/commands/init.js +228 -0
  83. package/dist/commands/init.js.map +1 -0
  84. package/dist/commands/keys.d.ts +4 -0
  85. package/dist/commands/keys.d.ts.map +1 -0
  86. package/dist/commands/keys.js +57 -0
  87. package/dist/commands/keys.js.map +1 -0
  88. package/dist/commands/logs.d.ts +6 -0
  89. package/dist/commands/logs.d.ts.map +1 -0
  90. package/dist/commands/logs.js +52 -0
  91. package/dist/commands/logs.js.map +1 -0
  92. package/dist/commands/migrate-from-v1.d.ts +5 -0
  93. package/dist/commands/migrate-from-v1.d.ts.map +1 -0
  94. package/dist/commands/migrate-from-v1.js +125 -0
  95. package/dist/commands/migrate-from-v1.js.map +1 -0
  96. package/dist/commands/migrate.d.ts +3 -0
  97. package/dist/commands/migrate.d.ts.map +1 -0
  98. package/dist/commands/migrate.js +75 -0
  99. package/dist/commands/migrate.js.map +1 -0
  100. package/dist/commands/pg.d.ts +8 -0
  101. package/dist/commands/pg.d.ts.map +1 -0
  102. package/dist/commands/pg.js +102 -0
  103. package/dist/commands/pg.js.map +1 -0
  104. package/dist/commands/plugins.d.ts +3 -0
  105. package/dist/commands/plugins.d.ts.map +1 -0
  106. package/dist/commands/plugins.js +431 -0
  107. package/dist/commands/plugins.js.map +1 -0
  108. package/dist/commands/pull.d.ts +3 -0
  109. package/dist/commands/pull.d.ts.map +1 -0
  110. package/dist/commands/pull.js +12 -0
  111. package/dist/commands/pull.js.map +1 -0
  112. package/dist/commands/push.d.ts +3 -0
  113. package/dist/commands/push.d.ts.map +1 -0
  114. package/dist/commands/push.js +179 -0
  115. package/dist/commands/push.js.map +1 -0
  116. package/dist/commands/seed.d.ts +5 -0
  117. package/dist/commands/seed.d.ts.map +1 -0
  118. package/dist/commands/seed.js +55 -0
  119. package/dist/commands/seed.js.map +1 -0
  120. package/dist/commands/self-host.d.ts +9 -0
  121. package/dist/commands/self-host.d.ts.map +1 -0
  122. package/dist/commands/self-host.js +310 -0
  123. package/dist/commands/self-host.js.map +1 -0
  124. package/dist/commands/self-update.d.ts +9 -0
  125. package/dist/commands/self-update.d.ts.map +1 -0
  126. package/dist/commands/self-update.js +33 -0
  127. package/dist/commands/self-update.js.map +1 -0
  128. package/dist/commands/status.d.ts +6 -0
  129. package/dist/commands/status.d.ts.map +1 -0
  130. package/dist/commands/status.js +70 -0
  131. package/dist/commands/status.js.map +1 -0
  132. package/dist/commands/types.d.ts +3 -0
  133. package/dist/commands/types.d.ts.map +1 -0
  134. package/dist/commands/types.js +62 -0
  135. package/dist/commands/types.js.map +1 -0
  136. package/dist/commands/update.d.ts +7 -0
  137. package/dist/commands/update.d.ts.map +1 -0
  138. package/dist/commands/update.js +118 -0
  139. package/dist/commands/update.js.map +1 -0
  140. package/dist/components.d.ts +5 -0
  141. package/dist/components.d.ts.map +1 -0
  142. package/dist/components.js +3 -0
  143. package/dist/components.js.map +1 -0
  144. package/dist/config.d.ts +65 -0
  145. package/dist/config.d.ts.map +1 -0
  146. package/dist/config.js +134 -0
  147. package/dist/config.js.map +1 -0
  148. package/dist/dev-compose.d.ts +19 -0
  149. package/dist/dev-compose.d.ts.map +1 -0
  150. package/dist/dev-compose.js +468 -0
  151. package/dist/dev-compose.js.map +1 -0
  152. package/dist/dev-log-bus.d.ts +30 -0
  153. package/dist/dev-log-bus.d.ts.map +1 -0
  154. package/dist/dev-log-bus.js +87 -0
  155. package/dist/dev-log-bus.js.map +1 -0
  156. package/dist/dev-log-filter.d.ts +10 -0
  157. package/dist/dev-log-filter.d.ts.map +1 -0
  158. package/dist/dev-log-filter.js +36 -0
  159. package/dist/dev-log-filter.js.map +1 -0
  160. package/dist/dev-logo.d.ts +12 -0
  161. package/dist/dev-logo.d.ts.map +1 -0
  162. package/dist/dev-logo.js +57 -0
  163. package/dist/dev-logo.js.map +1 -0
  164. package/dist/dev-session.d.ts +26 -0
  165. package/dist/dev-session.d.ts.map +1 -0
  166. package/dist/dev-session.js +106 -0
  167. package/dist/dev-session.js.map +1 -0
  168. package/dist/dev-shutdown.d.ts +9 -0
  169. package/dist/dev-shutdown.d.ts.map +1 -0
  170. package/dist/dev-shutdown.js +50 -0
  171. package/dist/dev-shutdown.js.map +1 -0
  172. package/dist/dev-task-colors.d.ts +14 -0
  173. package/dist/dev-task-colors.d.ts.map +1 -0
  174. package/dist/dev-task-colors.js +44 -0
  175. package/dist/dev-task-colors.js.map +1 -0
  176. package/dist/dev-tui.d.ts +24 -0
  177. package/dist/dev-tui.d.ts.map +1 -0
  178. package/dist/dev-tui.js +188 -0
  179. package/dist/dev-tui.js.map +1 -0
  180. package/dist/diff-output.d.ts +4 -0
  181. package/dist/diff-output.d.ts.map +1 -0
  182. package/dist/diff-output.js +12 -0
  183. package/dist/diff-output.js.map +1 -0
  184. package/dist/docker-postgres.d.ts +57 -0
  185. package/dist/docker-postgres.d.ts.map +1 -0
  186. package/dist/docker-postgres.js +208 -0
  187. package/dist/docker-postgres.js.map +1 -0
  188. package/dist/engine-client.d.ts +69 -0
  189. package/dist/engine-client.d.ts.map +1 -0
  190. package/dist/engine-client.js +157 -0
  191. package/dist/engine-client.js.map +1 -0
  192. package/dist/engine-push-output.d.ts +16 -0
  193. package/dist/engine-push-output.d.ts.map +1 -0
  194. package/dist/engine-push-output.js +61 -0
  195. package/dist/engine-push-output.js.map +1 -0
  196. package/dist/ensure-binary.d.ts +7 -0
  197. package/dist/ensure-binary.d.ts.map +1 -0
  198. package/dist/ensure-binary.js +17 -0
  199. package/dist/ensure-binary.js.map +1 -0
  200. package/dist/functions-router-gen.d.ts +14 -0
  201. package/dist/functions-router-gen.d.ts.map +1 -0
  202. package/dist/functions-router-gen.js +199 -0
  203. package/dist/functions-router-gen.js.map +1 -0
  204. package/dist/index.d.ts +11 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +9 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/jwt.d.ts +3 -0
  209. package/dist/jwt.d.ts.map +1 -0
  210. package/dist/jwt.js +13 -0
  211. package/dist/jwt.js.map +1 -0
  212. package/dist/kong-config.d.ts +25 -0
  213. package/dist/kong-config.d.ts.map +1 -0
  214. package/dist/kong-config.js +71 -0
  215. package/dist/kong-config.js.map +1 -0
  216. package/dist/local-gateway.d.ts +7 -0
  217. package/dist/local-gateway.d.ts.map +1 -0
  218. package/dist/local-gateway.js +9 -0
  219. package/dist/local-gateway.js.map +1 -0
  220. package/dist/local-storage.d.ts +8 -0
  221. package/dist/local-storage.d.ts.map +1 -0
  222. package/dist/local-storage.js +14 -0
  223. package/dist/local-storage.js.map +1 -0
  224. package/dist/pgbouncer-userlist.d.ts +5 -0
  225. package/dist/pgbouncer-userlist.d.ts.map +1 -0
  226. package/dist/pgbouncer-userlist.js +14 -0
  227. package/dist/pgbouncer-userlist.js.map +1 -0
  228. package/dist/postgres-ctl.d.ts +44 -0
  229. package/dist/postgres-ctl.d.ts.map +1 -0
  230. package/dist/postgres-ctl.js +137 -0
  231. package/dist/postgres-ctl.js.map +1 -0
  232. package/dist/process-manager.d.ts +49 -0
  233. package/dist/process-manager.d.ts.map +1 -0
  234. package/dist/process-manager.js +177 -0
  235. package/dist/process-manager.js.map +1 -0
  236. package/dist/project-config.d.ts +238 -0
  237. package/dist/project-config.d.ts.map +1 -0
  238. package/dist/project-config.js +159 -0
  239. package/dist/project-config.js.map +1 -0
  240. package/dist/pull-utils.d.ts +31 -0
  241. package/dist/pull-utils.d.ts.map +1 -0
  242. package/dist/pull-utils.js +77 -0
  243. package/dist/pull-utils.js.map +1 -0
  244. package/dist/release-pins.d.ts +7 -0
  245. package/dist/release-pins.d.ts.map +1 -0
  246. package/dist/release-pins.js +27 -0
  247. package/dist/release-pins.js.map +1 -0
  248. package/dist/release-public-key.d.ts +8 -0
  249. package/dist/release-public-key.d.ts.map +1 -0
  250. package/dist/release-public-key.js +13 -0
  251. package/dist/release-public-key.js.map +1 -0
  252. package/dist/restore-system-relation-targets.d.ts +3 -0
  253. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  254. package/dist/restore-system-relation-targets.js +45 -0
  255. package/dist/restore-system-relation-targets.js.map +1 -0
  256. package/dist/runtime-routes.d.ts +34 -0
  257. package/dist/runtime-routes.d.ts.map +1 -0
  258. package/dist/runtime-routes.js +252 -0
  259. package/dist/runtime-routes.js.map +1 -0
  260. package/dist/schema-ast-v2.d.ts +127 -0
  261. package/dist/schema-ast-v2.d.ts.map +1 -0
  262. package/dist/schema-ast-v2.js +226 -0
  263. package/dist/schema-ast-v2.js.map +1 -0
  264. package/dist/scripts/postinstall.d.ts +11 -0
  265. package/dist/scripts/postinstall.d.ts.map +1 -0
  266. package/dist/scripts/postinstall.js +47 -0
  267. package/dist/scripts/postinstall.js.map +1 -0
  268. package/dist/seed.d.ts +8 -0
  269. package/dist/seed.d.ts.map +1 -0
  270. package/dist/seed.js +32 -0
  271. package/dist/seed.js.map +1 -0
  272. package/dist/self-host-compose.d.ts +43 -0
  273. package/dist/self-host-compose.d.ts.map +1 -0
  274. package/dist/self-host-compose.js +400 -0
  275. package/dist/self-host-compose.js.map +1 -0
  276. package/dist/storage-provision.d.ts +24 -0
  277. package/dist/storage-provision.d.ts.map +1 -0
  278. package/dist/storage-provision.js +44 -0
  279. package/dist/storage-provision.js.map +1 -0
  280. package/dist/studio-admin-roles.d.ts +7 -0
  281. package/dist/studio-admin-roles.d.ts.map +1 -0
  282. package/dist/studio-admin-roles.js +14 -0
  283. package/dist/studio-admin-roles.js.map +1 -0
  284. package/dist/studio-dev-server.d.ts +22 -0
  285. package/dist/studio-dev-server.d.ts.map +1 -0
  286. package/dist/studio-dev-server.js +28 -0
  287. package/dist/studio-dev-server.js.map +1 -0
  288. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  289. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  290. package/dist/supatype-eval-1781522769253.mjs +3 -0
  291. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  292. package/dist/systemd.d.ts +26 -0
  293. package/dist/systemd.d.ts.map +1 -0
  294. package/dist/systemd.js +102 -0
  295. package/dist/systemd.js.map +1 -0
  296. package/dist/tsx-runner.d.ts +18 -0
  297. package/dist/tsx-runner.d.ts.map +1 -0
  298. package/dist/tsx-runner.js +69 -0
  299. package/dist/tsx-runner.js.map +1 -0
  300. package/dist/type-extractor.d.ts +4 -0
  301. package/dist/type-extractor.d.ts.map +1 -0
  302. package/dist/type-extractor.js +1213 -0
  303. package/dist/type-extractor.js.map +1 -0
  304. package/dist/type-resolver.d.ts +33 -0
  305. package/dist/type-resolver.d.ts.map +1 -0
  306. package/dist/type-resolver.js +338 -0
  307. package/dist/type-resolver.js.map +1 -0
  308. package/package.json +41 -0
  309. package/releases/deno/VERSION +1 -0
  310. package/scripts/mirror-deno-release.sh +76 -0
  311. package/src/TYPE-RESOLUTION.md +294 -0
  312. package/src/app/framework.ts +249 -0
  313. package/src/app/proxy-dev-app.ts +68 -0
  314. package/src/app-config.ts +128 -0
  315. package/src/augmentation-generator.ts +126 -0
  316. package/src/binary-cache.ts +845 -0
  317. package/src/cli.ts +63 -0
  318. package/src/commands/admin.ts +372 -0
  319. package/src/commands/app.ts +97 -0
  320. package/src/commands/cache.ts +117 -0
  321. package/src/commands/cloud.ts +325 -0
  322. package/src/commands/db.ts +136 -0
  323. package/src/commands/deploy-types.ts +49 -0
  324. package/src/commands/deploy.ts +400 -0
  325. package/src/commands/dev.ts +1009 -0
  326. package/src/commands/diff.ts +63 -0
  327. package/src/commands/engine.ts +30 -0
  328. package/src/commands/functions.ts +901 -0
  329. package/src/commands/generate.ts +44 -0
  330. package/src/commands/init.ts +253 -0
  331. package/src/commands/keys.ts +66 -0
  332. package/src/commands/logs.ts +58 -0
  333. package/src/commands/migrate-from-v1.ts +131 -0
  334. package/src/commands/migrate.ts +87 -0
  335. package/src/commands/pg.ts +133 -0
  336. package/src/commands/plugins.ts +508 -0
  337. package/src/commands/pull.ts +17 -0
  338. package/src/commands/push.ts +226 -0
  339. package/src/commands/seed.ts +68 -0
  340. package/src/commands/self-host.ts +364 -0
  341. package/src/commands/self-update.ts +45 -0
  342. package/src/commands/status.ts +84 -0
  343. package/src/commands/types.ts +76 -0
  344. package/src/commands/update.ts +136 -0
  345. package/src/components.ts +6 -0
  346. package/src/config.ts +223 -0
  347. package/src/dev-compose.ts +583 -0
  348. package/src/dev-log-bus.ts +101 -0
  349. package/src/dev-log-filter.ts +32 -0
  350. package/src/dev-logo.ts +62 -0
  351. package/src/dev-session.ts +130 -0
  352. package/src/dev-shutdown.ts +54 -0
  353. package/src/dev-task-colors.ts +47 -0
  354. package/src/dev-tui.ts +232 -0
  355. package/src/diff-output.ts +12 -0
  356. package/src/docker-postgres.ts +295 -0
  357. package/src/engine-client.ts +236 -0
  358. package/src/engine-push-output.ts +71 -0
  359. package/src/ensure-binary.ts +28 -0
  360. package/src/functions-router-gen.ts +224 -0
  361. package/src/index.ts +11 -0
  362. package/src/jwt.ts +14 -0
  363. package/src/kong-config.ts +93 -0
  364. package/src/local-gateway.ts +9 -0
  365. package/src/local-storage.ts +14 -0
  366. package/src/pgbouncer-userlist.ts +15 -0
  367. package/src/postgres-ctl.ts +171 -0
  368. package/src/process-manager.ts +220 -0
  369. package/src/project-config.ts +388 -0
  370. package/src/pull-utils.ts +81 -0
  371. package/src/release-pins.ts +31 -0
  372. package/src/release-public-key.ts +12 -0
  373. package/src/restore-system-relation-targets.ts +45 -0
  374. package/src/runtime-routes.ts +291 -0
  375. package/src/schema-ast-v2.ts +324 -0
  376. package/src/scripts/postinstall.ts +51 -0
  377. package/src/seed.ts +43 -0
  378. package/src/self-host-compose.ts +452 -0
  379. package/src/storage-provision.ts +58 -0
  380. package/src/studio-admin-roles.ts +16 -0
  381. package/src/studio-dev-server.ts +53 -0
  382. package/src/supatype-eval-1781522769253.mts +1 -0
  383. package/src/systemd.ts +137 -0
  384. package/src/tsx-runner.ts +89 -0
  385. package/src/type-extractor.ts +1479 -0
  386. package/src/type-resolver.ts +457 -0
  387. package/tests/app-command.test.ts +54 -0
  388. package/tests/augmentation-generator.test.ts +59 -0
  389. package/tests/binary-cache-cloud-overrides.test.ts +123 -0
  390. package/tests/cached-artifact-format.test.ts +84 -0
  391. package/tests/cli-help.test.ts +133 -0
  392. package/tests/config.test.ts +252 -0
  393. package/tests/dev-ui.test.ts +139 -0
  394. package/tests/docker-postgres.test.ts +39 -0
  395. package/tests/engine-distribution.test.ts +418 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/ensure-binary.test.ts +59 -0
  398. package/tests/init.test.ts +127 -0
  399. package/tests/keys.test.ts +160 -0
  400. package/tests/migrate-from-v1.test.ts +29 -0
  401. package/tests/normalize-admin-config.test.ts +48 -0
  402. package/tests/pg-spawn-env.test.ts +18 -0
  403. package/tests/postgres-archive-tag.test.ts +9 -0
  404. package/tests/proxy-dev-app.test.ts +33 -0
  405. package/tests/pull-utils.test.ts +150 -0
  406. package/tests/release-pins.test.ts +28 -0
  407. package/tests/runtime-contract.test.ts +370 -0
  408. package/tests/seed-discover.test.ts +31 -0
  409. package/tests/studio-admin-roles.test.ts +27 -0
  410. package/tests/tsconfig.json +9 -0
  411. package/tests/tsx-runner.test.ts +66 -0
  412. package/tests/type-extractor.test.ts +985 -0
  413. package/tests/type-resolver.test.ts +59 -0
  414. package/tsconfig.json +10 -0
  415. package/tsconfig.tsbuildinfo +1 -0
  416. package/vitest.config.ts +12 -0
@@ -0,0 +1,226 @@
1
+ import type { Command } from "commander"
2
+ import { mkdirSync, writeFileSync } from "node:fs"
3
+ import { createInterface } from "node:readline"
4
+ import { join } from "node:path"
5
+ import { loadConfig, loadSchemaAst } from "../config.js"
6
+ import { connectionString, resolveRuntimeProvider, schemaPathFromProject, serverBaseUrl } from "../project-config.js"
7
+ import { isCloudLinked, pushSchemaToLinkedProject } from "./cloud.js"
8
+ import { ensureEngine, engineRequest, type DiffResult, type Operation } from "../engine-client.js"
9
+ import { printDiffWarnings } from "../diff-output.js"
10
+ import { signJwt } from "../jwt.js"
11
+ import { provisionBuckets } from "../storage-provision.js"
12
+ import { promptFirstAdminUser } from "./admin.js"
13
+ import { withAdminRoles } from "../studio-admin-roles.js"
14
+ import { restoreSystemRelationTargets } from "../restore-system-relation-targets.js"
15
+ import type { SupatypeProjectConfig } from "../project-config.js"
16
+
17
+ const DEV_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
18
+
19
+ export function registerPush(program: Command): void {
20
+ program
21
+ .command("push")
22
+ .description(
23
+ "Push schema to the database: diff, prompt for destructive changes, apply migration, generate types",
24
+ )
25
+ .option("--yes", "Skip confirmation prompts for destructive changes")
26
+ .option("--connection <url>", "Database connection URL (overrides config)")
27
+ .action(async (opts: { yes?: boolean; connection?: string }) => {
28
+ const cwd = process.cwd()
29
+
30
+ if (isCloudLinked(cwd)) {
31
+ if (opts.connection) {
32
+ console.error("--connection is not allowed when linked to a cloud project (credentials stay server-side).")
33
+ process.exit(1)
34
+ }
35
+ await pushSchemaToLinkedProject(cwd, { force: opts.yes ?? true })
36
+ return
37
+ }
38
+
39
+ const config = loadConfig(cwd)
40
+
41
+ // Docker provider: the compose Postgres isn't published to the host, so
42
+ // apply the schema through the in-compose schema-engine (unless the user
43
+ // gave an explicit --connection to a reachable database).
44
+ if (!opts.connection && resolveRuntimeProvider(config) === "docker") {
45
+ const { pushSchemaDocker } = await import("../dev-compose.js")
46
+ await pushSchemaDocker(cwd, config)
47
+ return
48
+ }
49
+
50
+ const connection = opts.connection ?? connectionString(config)
51
+
52
+ await ensureEngine()
53
+
54
+ console.log("Loading schema...")
55
+ const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
56
+
57
+ console.log("Diffing against database...")
58
+ const diff = await engineRequest<DiffResult>("/diff", {
59
+ ast,
60
+ database_url: connection,
61
+ schema: "public",
62
+ })
63
+
64
+ const ops = diff.operations ?? []
65
+ printDiffWarnings(diff)
66
+
67
+ if (ops.length === 0) {
68
+ console.log(
69
+ "Schema matches the database (no DDL). Syncing Studio metadata...",
70
+ )
71
+ } else {
72
+ printDiff(ops)
73
+
74
+ const risky = ops.filter(
75
+ (o) => o.risk === "cautious" || o.risk === "destructive" || o.risk === "warn" || o.risk === "danger",
76
+ )
77
+ if (risky.length > 0 && !opts.yes) {
78
+ const confirmed = await confirm(
79
+ `\n${risky.length} risky operation(s) above (type changes or data loss). Proceed? [y/N] `,
80
+ )
81
+ if (!confirmed) {
82
+ console.log("Aborted.")
83
+ return
84
+ }
85
+ }
86
+ }
87
+
88
+ console.log(ops.length > 0 ? "\nApplying migration..." : "\nSyncing with engine...")
89
+ const pushResult = await engineRequest<{
90
+ message?: string
91
+ status?: string
92
+ admin_refreshed?: boolean
93
+ }>("/push", {
94
+ ast,
95
+ database_url: connection,
96
+ schema: "public",
97
+ force: true,
98
+ })
99
+ if (pushResult.status === "up_to_date") {
100
+ console.log(
101
+ pushResult.admin_refreshed
102
+ ? "Database schema unchanged — Studio metadata synced."
103
+ : "Schema is up to date.",
104
+ )
105
+ } else {
106
+ console.log(pushResult.message ?? "Migration applied.")
107
+ }
108
+
109
+ await writeLocalAdminConfig(ast, config)
110
+
111
+ // After a DDL migration, check if this is the first push and offer to create an
112
+ // admin user if none exist (Gap Appendices task 48).
113
+ if (ops.length > 0) {
114
+ await promptFirstAdminUser(connection)
115
+ }
116
+
117
+ // Provision storage buckets declared in the schema.
118
+ const baseUrl = serverBaseUrl(config)
119
+ const serviceRoleKey =
120
+ process.env["SUPATYPE_SERVICE_ROLE_KEY"] ??
121
+ (config.server.mode === "dev"
122
+ ? signJwt({ role: "service_role", iss: "supatype", iat: Math.floor(Date.now() / 1000) }, DEV_JWT_SECRET)
123
+ : undefined)
124
+
125
+ if (baseUrl && serviceRoleKey) {
126
+ const parsedAst = await engineRequest<{
127
+ storageBuckets?: Array<{
128
+ id: string
129
+ public: boolean
130
+ accessMode?: "public" | "private" | "custom"
131
+ allowedMimeTypes?: string[]
132
+ fileSizeLimit?: number
133
+ s3BucketPolicy?: string
134
+ }>
135
+ }>("/parse", { ast })
136
+ const buckets = (parsedAst.storageBuckets ?? []).map((b) => ({
137
+ id: b.id,
138
+ public: b.public,
139
+ ...(b.accessMode !== undefined && { access_mode: b.accessMode }),
140
+ ...(b.allowedMimeTypes != null && { allowed_mime_types: b.allowedMimeTypes }),
141
+ ...(b.fileSizeLimit != null && { file_size_limit: b.fileSizeLimit }),
142
+ ...(b.s3BucketPolicy != null &&
143
+ b.s3BucketPolicy !== "" && { s3_bucket_policy: b.s3BucketPolicy }),
144
+ }))
145
+ if (buckets.length > 0) {
146
+ console.log("Provisioning storage buckets...")
147
+ await provisionBuckets(`${baseUrl}/storage/v1`, serviceRoleKey, buckets)
148
+ }
149
+ }
150
+
151
+ if (config.output?.types ?? config.output?.client) {
152
+ console.log("Generating types...")
153
+ const genBody: Record<string, unknown> = { ast, lang: "typescript" }
154
+ if (config.output?.types) genBody["types_path"] = config.output.types
155
+ if (config.output?.client) genBody["client_path"] = config.output.client
156
+
157
+ const genResult = await engineRequest<{ code?: string; message?: string }>("/generate", genBody)
158
+ console.log(genResult.message ?? "Types generated.")
159
+ }
160
+
161
+ const studioBase = baseUrl?.replace(/\/$/, "") ?? ""
162
+ if (studioBase) {
163
+ console.log(`\nStudio: ${studioBase}/studio/ — sign in with the admin user you created.`)
164
+ } else {
165
+ console.log("\nDone.")
166
+ }
167
+ })
168
+ }
169
+
170
+ function printDiff(ops: Operation[]): void {
171
+ const symbol: Record<string, string> = {
172
+ safe: "+",
173
+ warn: "~",
174
+ cautious: "~",
175
+ danger: "!",
176
+ destructive: "!",
177
+ }
178
+ console.log(`\n${ops.length} change(s) planned:\n`)
179
+ for (const op of ops) {
180
+ const riskKey = op.risk ?? "safe"
181
+ const s = symbol[riskKey] ?? "?"
182
+ const label = op.warning ?? op.description ?? formatOperation(op)
183
+ console.log(` [${s}] ${label}`)
184
+ }
185
+ }
186
+
187
+ function formatOperation(op: Operation): string {
188
+ if (typeof op.description === "string" && op.description.trim().length > 0) {
189
+ return op.description
190
+ }
191
+
192
+ const kind = typeof op.kind === "string" ? op.kind : "operation"
193
+ const raw = op as unknown as Record<string, unknown>
194
+ const table = raw["table"]
195
+ const column = raw["column"]
196
+
197
+ if (typeof table === "string" && typeof column === "string") {
198
+ return `${kind} ${table}.${column}`
199
+ }
200
+ if (typeof table === "string") {
201
+ return `${kind} ${table}`
202
+ }
203
+
204
+ // Last resort: show operation kind with compact payload.
205
+ return `${kind} ${JSON.stringify(op)}`
206
+ }
207
+
208
+ async function confirm(prompt: string): Promise<boolean> {
209
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
210
+ return new Promise((resolve) => {
211
+ rl.question(prompt, (answer) => {
212
+ rl.close()
213
+ resolve(answer.toLowerCase() === "y")
214
+ })
215
+ })
216
+ }
217
+
218
+ /** Write `.supatype/admin-config.json` for local Studio (same layout as `supatype dev`). */
219
+ async function writeLocalAdminConfig(ast: unknown, config: SupatypeProjectConfig): Promise<void> {
220
+ const cwd = process.cwd()
221
+ const dir = join(cwd, ".supatype")
222
+ mkdirSync(dir, { recursive: true })
223
+ const admin = withAdminRoles(await engineRequest<unknown>("/admin", { ast }), config)
224
+ restoreSystemRelationTargets(admin, ast)
225
+ writeFileSync(join(dir, "admin-config.json"), `${JSON.stringify(admin, null, 2)}\n`)
226
+ }
@@ -0,0 +1,68 @@
1
+ import type { Command } from "commander"
2
+ import { existsSync, readdirSync } from "node:fs"
3
+ import { join, resolve } from "node:path"
4
+ import { isLinkedToCloudProject } from "../binary-cache.js"
5
+ import { loadConfig } from "../config.js"
6
+ import { projectRootFromConfig } from "../project-config.js"
7
+ import { runTsFile } from "../tsx-runner.js"
8
+
9
+ const SEED_EXT = /\.(ts|mts|tsx)$/
10
+
11
+ /** Seed entries under `seeds/`, sorted by filename (Phase 10.6 C19). */
12
+ export function discoverSeedsDir(cwd: string, seedsDir: string): string[] {
13
+ if (!existsSync(seedsDir)) return []
14
+ const names = readdirSync(seedsDir).filter((n) => SEED_EXT.test(n))
15
+ names.sort((a, b) => a.localeCompare(b))
16
+ return names.map((n) => join(seedsDir, n))
17
+ }
18
+
19
+ export function registerSeed(program: Command): void {
20
+ program
21
+ .command("seed [file]")
22
+ .description(
23
+ "Run database seeds: optional single file; else all seeds/*.ts (alphabetical); else seed.ts",
24
+ )
25
+ .option(
26
+ "--force",
27
+ "Allow running when the project is linked to Supatype Cloud (dangerous)",
28
+ false,
29
+ )
30
+ .action(async (file: string | undefined, opts: { force: boolean }) => {
31
+ const cwd = process.cwd()
32
+ const config = loadConfig(cwd)
33
+ if (isLinkedToCloudProject(cwd, config) && !opts.force) {
34
+ console.error(
35
+ "[supatype] This project is linked to Supatype Cloud. Refusing to run seeds locally.\n" +
36
+ " Pass --force only if you intend to target this linked project (advanced).",
37
+ )
38
+ process.exit(1)
39
+ }
40
+
41
+ const root = projectRootFromConfig(config, cwd)
42
+ const seedsDir = join(root, "seeds")
43
+
44
+ let paths: string[]
45
+ if (file !== undefined && file.trim() !== "") {
46
+ paths = [resolve(cwd, file)]
47
+ } else {
48
+ paths = discoverSeedsDir(cwd, seedsDir)
49
+ if (paths.length === 0) {
50
+ paths = [resolve(root, "seed.ts")]
51
+ }
52
+ }
53
+
54
+ const missing = paths.filter((p) => !existsSync(p))
55
+ if (missing.length > 0) {
56
+ console.error(`Seed file(s) not found:\n ${missing.join("\n ")}`)
57
+ process.exit(1)
58
+ }
59
+
60
+ for (const seedFile of paths) {
61
+ console.log(`[supatype] Running ${seedFile}...`)
62
+ const result = runTsFile(seedFile, { cwd, stdio: "inherit" })
63
+ if (result.exitCode !== 0) {
64
+ process.exit(result.exitCode)
65
+ }
66
+ }
67
+ })
68
+ }
@@ -0,0 +1,364 @@
1
+ /**
2
+ * self-host commands — manage self-hosted deployments.
3
+ *
4
+ * Compose-based commands are the canonical path.
5
+ * Native/systemd commands are kept temporarily for migration compatibility.
6
+ */
7
+
8
+ import { Command } from "commander"
9
+ import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync } from "node:fs"
10
+ import { join, resolve } from "node:path"
11
+ import { homedir } from "node:os"
12
+ import { spawnSync } from "node:child_process"
13
+ import { gzipSync } from "node:zlib"
14
+ import { loadConfig } from "../config.js"
15
+ import { connectionString } from "../project-config.js"
16
+ import { resolveBinary } from "../binary-cache.js"
17
+ import { generateUnits } from "../systemd.js"
18
+ import { readPid } from "../process-manager.js"
19
+ import { localStorageEnv } from "../local-storage.js"
20
+ import { runDockerCompose, writeSelfHostCompose } from "../self-host-compose.js"
21
+
22
+ export function registerSelfHost(program: Command): void {
23
+ const selfHostCmd = program
24
+ .command("self-host")
25
+ .description("Manage self-hosted deployments (Docker Compose only)")
26
+
27
+ const composeCmd = selfHostCmd
28
+ .command("compose")
29
+ .description("Manage compose-based self-host runtime")
30
+
31
+ composeCmd
32
+ .command("render")
33
+ .description("Render deterministic self-host compose artifacts")
34
+ .action(() => {
35
+ const cwd = process.cwd()
36
+ const config = loadConfig(cwd)
37
+ const out = writeSelfHostCompose(cwd, config)
38
+ console.log(`Wrote ${out.composePath}`)
39
+ console.log(`Wrote ${out.kongPath}`)
40
+ })
41
+
42
+ composeCmd
43
+ .command("up")
44
+ .description("Render and start compose services")
45
+ .option("-d, --detach", "Start in detached mode", true)
46
+ .action((opts: { detach?: boolean }) => {
47
+ const cwd = process.cwd()
48
+ const config = loadConfig(cwd)
49
+ const out = writeSelfHostCompose(cwd, config)
50
+ const status = runDockerCompose(out.composePath, opts.detach ? ["up", "-d"] : ["up"], cwd)
51
+ process.exitCode = status
52
+ })
53
+
54
+ composeCmd
55
+ .command("down")
56
+ .description("Stop compose services")
57
+ .action(() => {
58
+ const cwd = process.cwd()
59
+ const config = loadConfig(cwd)
60
+ const out = writeSelfHostCompose(cwd, config)
61
+ process.exitCode = runDockerCompose(out.composePath, ["down"], cwd)
62
+ })
63
+
64
+ composeCmd
65
+ .command("status")
66
+ .description("Show compose service status")
67
+ .action(() => {
68
+ const cwd = process.cwd()
69
+ const config = loadConfig(cwd)
70
+ const out = writeSelfHostCompose(cwd, config)
71
+ process.exitCode = runDockerCompose(out.composePath, ["ps"], cwd)
72
+ })
73
+
74
+ composeCmd
75
+ .command("logs")
76
+ .description("Tail compose logs")
77
+ .option("--service <name>", "Filter to one service")
78
+ .option("-f, --follow", "Follow log output", true)
79
+ .action((opts: { service?: string; follow?: boolean }) => {
80
+ const cwd = process.cwd()
81
+ const config = loadConfig(cwd)
82
+ const out = writeSelfHostCompose(cwd, config)
83
+ const args = ["logs"]
84
+ if (opts.follow) args.push("-f")
85
+ if (opts.service) args.push(opts.service)
86
+ process.exitCode = runDockerCompose(out.composePath, args, cwd)
87
+ })
88
+
89
+ // ── Legacy native/systemd helpers (hidden; use compose for self-host) ─────
90
+
91
+ const legacyCmd = new Command("native")
92
+ selfHostCmd.addCommand(legacyCmd, { hidden: true })
93
+
94
+ legacyCmd
95
+ .command(
96
+ "install-service",
97
+ "Generate systemd unit files and (on Linux) install + enable them",
98
+ )
99
+ .option("--output-dir <path>", "Write unit files here instead of /etc/systemd/system/")
100
+ .option("--user <name>", "User to run services as")
101
+ .option("--no-enable", "Generate unit files but do not enable/start them")
102
+ .action(
103
+ async (opts: {
104
+ outputDir?: string
105
+ user?: string
106
+ enable: boolean
107
+ }) => {
108
+ logLegacyWarning("install-service")
109
+ const cwd = process.cwd()
110
+ const config = loadConfig(cwd)
111
+
112
+ const systemdDir = opts.outputDir ?? ".supatype/systemd"
113
+ const absSystemdDir = resolve(cwd, systemdDir)
114
+
115
+ console.log("Generating systemd unit files...")
116
+ const { postgres, server } = generateUnits(config, cwd, {
117
+ outputDir: absSystemdDir,
118
+ ...(opts.user !== undefined && { user: opts.user }),
119
+ })
120
+ console.log(` wrote ${postgres}`)
121
+ console.log(` wrote ${server}`)
122
+
123
+ if (!opts.enable) {
124
+ console.log(
125
+ `\nTo install manually:\n` +
126
+ ` sudo cp ${postgres} /etc/systemd/system/\n` +
127
+ ` sudo cp ${server} /etc/systemd/system/\n` +
128
+ ` sudo systemctl daemon-reload\n` +
129
+ ` sudo systemctl enable --now supatype-postgres supatype-server`,
130
+ )
131
+ return
132
+ }
133
+
134
+ if (process.platform !== "linux") {
135
+ console.log(
136
+ "\nNote: systemd unit installation is only supported on Linux.\n" +
137
+ `Unit files are at ${absSystemdDir}/`,
138
+ )
139
+ return
140
+ }
141
+
142
+ // Install to /etc/systemd/system/
143
+ console.log("\nInstalling to /etc/systemd/system/ (requires sudo)...")
144
+ const units = [
145
+ { src: postgres, dest: "/etc/systemd/system/supatype-postgres.service" },
146
+ { src: server, dest: "/etc/systemd/system/supatype-server.service" },
147
+ ]
148
+ for (const { src, dest } of units) {
149
+ const cp = spawnSync("sudo", ["cp", src, dest], { stdio: "inherit" })
150
+ if (cp.status !== 0) {
151
+ console.error(`Failed to copy ${src} to ${dest}`)
152
+ process.exit(1)
153
+ }
154
+ }
155
+
156
+ const daemonReload = spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" })
157
+ if (daemonReload.status !== 0) { process.exit(1) }
158
+
159
+ const enable = spawnSync(
160
+ "sudo",
161
+ ["systemctl", "enable", "--now", "supatype-postgres", "supatype-server"],
162
+ { stdio: "inherit" },
163
+ )
164
+ if (enable.status !== 0) { process.exit(1) }
165
+
166
+ console.log("\nServices installed and started.")
167
+ console.log(" supatype-postgres.service")
168
+ console.log(" supatype-server.service")
169
+ },
170
+ )
171
+
172
+ // ── serve ──────────────────────────────────────────────────────────────────
173
+
174
+ legacyCmd
175
+ .command("serve", "Start supatype-server in the foreground (for standalone mode)")
176
+ .option("--port <port>", "Override port from config")
177
+ .action(async (opts: { port?: string }) => {
178
+ logLegacyWarning("serve")
179
+ const cwd = process.cwd()
180
+ const config = loadConfig(cwd)
181
+
182
+ const serverBin = await resolveBinary("server", config)
183
+ const port = opts.port ?? String(config.server.port ?? 54321)
184
+
185
+ const args = [
186
+ "--port", port,
187
+ "--mode", config.server.mode,
188
+ ...(config.server.domain ? ["--domain", config.server.domain] : []),
189
+ ]
190
+
191
+ const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
192
+ const storageEnv = config.storage?.provider !== "s3" ? localStorageEnv(stateDir) : {}
193
+
194
+ console.log(`Starting supatype-server on port ${port}...`)
195
+ const result = spawnSync(serverBin, args, {
196
+ stdio: "inherit",
197
+ cwd,
198
+ env: { ...process.env, ...storageEnv },
199
+ })
200
+ process.exitCode = result.status ?? 1
201
+ })
202
+
203
+ // ── reload ─────────────────────────────────────────────────────────────────
204
+
205
+ legacyCmd
206
+ .command("reload", "Reload the running supatype-server (SIGHUP for config reload)")
207
+ .action(() => {
208
+ logLegacyWarning("reload")
209
+ const cwd = process.cwd()
210
+ const config = loadConfig(cwd)
211
+ const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
212
+ const pid = readPid(join(stateDir, "pid"), "server")
213
+
214
+ if (!pid) {
215
+ // Try systemctl if running as a service
216
+ if (process.platform === "linux") {
217
+ const result = spawnSync("systemctl", ["reload", "supatype-server"], { stdio: "inherit" })
218
+ process.exitCode = result.status ?? 1
219
+ return
220
+ }
221
+ console.error("Server does not appear to be running (no PID file found).")
222
+ process.exitCode = 1
223
+ return
224
+ }
225
+
226
+ try {
227
+ process.kill(pid, "SIGHUP")
228
+ console.log(`Sent SIGHUP to supatype-server (pid ${pid}).`)
229
+ } catch (err) {
230
+ console.error(`Failed to signal pid ${pid}:`, (err as Error).message)
231
+ process.exitCode = 1
232
+ }
233
+ })
234
+
235
+ // ── status ─────────────────────────────────────────────────────────────────
236
+
237
+ legacyCmd
238
+ .command("status", "Show running status of supatype services")
239
+ .action(() => {
240
+ logLegacyWarning("status")
241
+ const cwd = process.cwd()
242
+ const config = loadConfig(cwd)
243
+ const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
244
+
245
+ console.log(`Project: ${config.project.name}\n`)
246
+
247
+ if (process.platform === "linux" && existsSync("/run/systemd/system")) {
248
+ // systemd is active
249
+ for (const svc of ["supatype-postgres", "supatype-server"]) {
250
+ const result = spawnSync("systemctl", ["status", "--no-pager", "--lines=0", svc], {
251
+ encoding: "utf8",
252
+ })
253
+ const active = result.stdout?.includes("active (running)") ? "running" : "stopped"
254
+ console.log(` ${svc}: ${active}`)
255
+ }
256
+ } else {
257
+ // PID file check
258
+ const serverPid = readPid(join(stateDir, "pid"), "server")
259
+ const pgPid = readPid(join(stateDir, "pid"), "postgres")
260
+ console.log(` postgres: ${pgPid ? `running (pid ${pgPid})` : "stopped"}`)
261
+ console.log(` supatype-server: ${serverPid ? `running (pid ${serverPid})` : "stopped"}`)
262
+ }
263
+
264
+ const logDir = join(stateDir, "logs")
265
+ if (existsSync(logDir)) {
266
+ console.log(`\nLogs: ${logDir}`)
267
+ }
268
+ })
269
+
270
+ // ── logs ───────────────────────────────────────────────────────────────────
271
+
272
+ selfHostCmd
273
+ .command("logs", "Tail supatype service logs", { hidden: true })
274
+ .option("--service <name>", "Show logs for: postgres | server")
275
+ .option("--lines <n>", "Number of lines to show", "50")
276
+ .option("-f, --follow", "Follow log output")
277
+ .action((opts: { service?: string; lines: string; follow?: boolean }) => {
278
+ logLegacyWarning("logs")
279
+ const cwd = process.cwd()
280
+ const config = loadConfig(cwd)
281
+ const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
282
+ const logDir = join(stateDir, "logs")
283
+
284
+ if (process.platform === "linux" && existsSync("/run/systemd/system")) {
285
+ const args = ["--no-pager", "--lines", opts.lines]
286
+ if (opts.follow) args.push("--follow")
287
+ if (opts.service) args.push(`-u`, `supatype-${opts.service}`)
288
+ else args.push("-u", "supatype-postgres", "-u", "supatype-server")
289
+ spawnSync("journalctl", args, { stdio: "inherit" })
290
+ return
291
+ }
292
+
293
+ // File-based logs
294
+ const targets: Array<{ label: string; file: string }> = []
295
+ if (!opts.service || opts.service === "postgres") {
296
+ targets.push({ label: "postgres", file: join(logDir, "postgres.log") })
297
+ }
298
+ if (!opts.service || opts.service === "server") {
299
+ targets.push({ label: "server", file: join(logDir, "server.log") })
300
+ }
301
+
302
+ for (const { label, file } of targets) {
303
+ if (!existsSync(file)) {
304
+ console.log(`[${label}] log file not found: ${file}`)
305
+ continue
306
+ }
307
+ if (opts.follow) {
308
+ const tail = spawnSync("tail", ["-f", "-n", opts.lines, file], { stdio: "inherit" })
309
+ process.exitCode = tail.status ?? 0
310
+ } else {
311
+ const n = parseInt(opts.lines, 10)
312
+ const content = readFileSync(file, "utf8")
313
+ const lines = content.split("\n")
314
+ console.log(lines.slice(-n).join("\n"))
315
+ }
316
+ }
317
+ })
318
+
319
+ // ── backup ─────────────────────────────────────────────────────────────────
320
+
321
+ legacyCmd
322
+ .command("backup", "Create a Postgres dump of the project database")
323
+ .option("--output <path>", "Output file path (default: ./backups/backup-<timestamp>.sql.gz)")
324
+ .option("--connection <url>", "Database connection URL (overrides config)")
325
+ .action((opts: { output?: string; connection?: string }) => {
326
+ logLegacyWarning("backup")
327
+ const cwd = process.cwd()
328
+ const config = loadConfig(cwd)
329
+ const conn = opts.connection ?? connectionString(config)
330
+ const outFile = opts.output ?? resolve(
331
+ cwd,
332
+ "backups",
333
+ `backup-${new Date().toISOString().replace(/[:.]/g, "-")}.sql.gz`,
334
+ )
335
+
336
+ mkdirSync(resolve(outFile, ".."), { recursive: true })
337
+
338
+ console.log(`Backing up database to ${outFile}...`)
339
+ try {
340
+ // Avoid shell interpolation of user-supplied values.
341
+ const pgDump = spawnSync("pg_dump", [conn], {
342
+ stdio: ["ignore", "pipe", "pipe"],
343
+ })
344
+ if (pgDump.status !== 0) {
345
+ const stderr = pgDump.stderr?.toString("utf8") ?? ""
346
+ throw new Error(stderr.trim() || "pg_dump failed")
347
+ }
348
+
349
+ const compressed = gzipSync(pgDump.stdout)
350
+ writeFileSync(outFile, compressed)
351
+ console.log("Backup complete.")
352
+ } catch (err) {
353
+ console.error("Backup failed:", (err as Error).message)
354
+ process.exit(1)
355
+ }
356
+ })
357
+ }
358
+
359
+ function logLegacyWarning(cmd: string): void {
360
+ console.warn(
361
+ `[supatype] self-host native ${cmd} is deprecated. ` +
362
+ "Use `supatype self-host compose` commands instead.",
363
+ )
364
+ }