@supatype/cli 0.1.0-alpha.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +274 -69
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -1
  6. package/dist/app/framework.js +1 -3
  7. package/dist/app/framework.js.map +1 -1
  8. package/dist/app/proxy-dev-app.d.ts +14 -0
  9. package/dist/app/proxy-dev-app.d.ts.map +1 -1
  10. package/dist/app/proxy-dev-app.js +110 -6
  11. package/dist/app/proxy-dev-app.js.map +1 -1
  12. package/dist/app-config.d.ts +10 -0
  13. package/dist/app-config.d.ts.map +1 -1
  14. package/dist/app-config.js +72 -0
  15. package/dist/app-config.js.map +1 -1
  16. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  17. package/dist/binary-cache.d.ts +19 -7
  18. package/dist/binary-cache.d.ts.map +1 -1
  19. package/dist/binary-cache.js +92 -46
  20. package/dist/binary-cache.js.map +1 -1
  21. package/dist/cli.d.ts +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +17 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/add.d.ts +3 -0
  26. package/dist/commands/add.d.ts.map +1 -0
  27. package/dist/commands/add.js +86 -0
  28. package/dist/commands/add.js.map +1 -0
  29. package/dist/commands/admin.d.ts.map +1 -1
  30. package/dist/commands/admin.js +39 -53
  31. package/dist/commands/admin.js.map +1 -1
  32. package/dist/commands/adopt.d.ts +3 -0
  33. package/dist/commands/adopt.d.ts.map +1 -0
  34. package/dist/commands/adopt.js +55 -0
  35. package/dist/commands/adopt.js.map +1 -0
  36. package/dist/commands/app.d.ts.map +1 -1
  37. package/dist/commands/app.js +20 -17
  38. package/dist/commands/app.js.map +1 -1
  39. package/dist/commands/cache.d.ts.map +1 -1
  40. package/dist/commands/cache.js +11 -10
  41. package/dist/commands/cache.js.map +1 -1
  42. package/dist/commands/cloud.d.ts +4 -9
  43. package/dist/commands/cloud.d.ts.map +1 -1
  44. package/dist/commands/cloud.js +75 -125
  45. package/dist/commands/cloud.js.map +1 -1
  46. package/dist/commands/db.d.ts.map +1 -1
  47. package/dist/commands/db.js +37 -58
  48. package/dist/commands/db.js.map +1 -1
  49. package/dist/commands/deploy.d.ts.map +1 -1
  50. package/dist/commands/deploy.js +140 -96
  51. package/dist/commands/deploy.js.map +1 -1
  52. package/dist/commands/dev.d.ts.map +1 -1
  53. package/dist/commands/dev.js +72 -39
  54. package/dist/commands/dev.js.map +1 -1
  55. package/dist/commands/diff.d.ts.map +1 -1
  56. package/dist/commands/diff.js +39 -39
  57. package/dist/commands/diff.js.map +1 -1
  58. package/dist/commands/doctor.d.ts +3 -0
  59. package/dist/commands/doctor.d.ts.map +1 -0
  60. package/dist/commands/doctor.js +78 -0
  61. package/dist/commands/doctor.js.map +1 -0
  62. package/dist/commands/engine.d.ts.map +1 -1
  63. package/dist/commands/engine.js +5 -4
  64. package/dist/commands/engine.js.map +1 -1
  65. package/dist/commands/functions.d.ts.map +1 -1
  66. package/dist/commands/functions.js +172 -119
  67. package/dist/commands/functions.js.map +1 -1
  68. package/dist/commands/generate.d.ts.map +1 -1
  69. package/dist/commands/generate.js +5 -4
  70. package/dist/commands/generate.js.map +1 -1
  71. package/dist/commands/init.d.ts +30 -1
  72. package/dist/commands/init.d.ts.map +1 -1
  73. package/dist/commands/init.js +814 -107
  74. package/dist/commands/init.js.map +1 -1
  75. package/dist/commands/introspect.d.ts +3 -0
  76. package/dist/commands/introspect.d.ts.map +1 -0
  77. package/dist/commands/introspect.js +35 -0
  78. package/dist/commands/introspect.js.map +1 -0
  79. package/dist/commands/keys.d.ts +15 -1
  80. package/dist/commands/keys.d.ts.map +1 -1
  81. package/dist/commands/keys.js +46 -10
  82. package/dist/commands/keys.js.map +1 -1
  83. package/dist/commands/link-helpers.d.ts +15 -0
  84. package/dist/commands/link-helpers.d.ts.map +1 -0
  85. package/dist/commands/link-helpers.js +225 -0
  86. package/dist/commands/link-helpers.js.map +1 -0
  87. package/dist/commands/logs.d.ts.map +1 -1
  88. package/dist/commands/logs.js +5 -4
  89. package/dist/commands/logs.js.map +1 -1
  90. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  91. package/dist/commands/migrate-from-v1.js +3 -2
  92. package/dist/commands/migrate-from-v1.js.map +1 -1
  93. package/dist/commands/migrate.d.ts.map +1 -1
  94. package/dist/commands/migrate.js +119 -26
  95. package/dist/commands/migrate.js.map +1 -1
  96. package/dist/commands/pg.d.ts.map +1 -1
  97. package/dist/commands/pg.js +11 -12
  98. package/dist/commands/pg.js.map +1 -1
  99. package/dist/commands/plugins.d.ts.map +1 -1
  100. package/dist/commands/plugins.js +55 -46
  101. package/dist/commands/plugins.js.map +1 -1
  102. package/dist/commands/pull.d.ts.map +1 -1
  103. package/dist/commands/pull.js +33 -5
  104. package/dist/commands/pull.js.map +1 -1
  105. package/dist/commands/push.d.ts.map +1 -1
  106. package/dist/commands/push.js +110 -137
  107. package/dist/commands/push.js.map +1 -1
  108. package/dist/commands/seed.d.ts.map +1 -1
  109. package/dist/commands/seed.js +4 -3
  110. package/dist/commands/seed.js.map +1 -1
  111. package/dist/commands/self-host.d.ts +2 -2
  112. package/dist/commands/self-host.d.ts.map +1 -1
  113. package/dist/commands/self-host.js +65 -50
  114. package/dist/commands/self-host.js.map +1 -1
  115. package/dist/commands/self-update.d.ts.map +1 -1
  116. package/dist/commands/self-update.js +3 -2
  117. package/dist/commands/self-update.js.map +1 -1
  118. package/dist/commands/status.d.ts +1 -1
  119. package/dist/commands/status.d.ts.map +1 -1
  120. package/dist/commands/status.js +95 -29
  121. package/dist/commands/status.js.map +1 -1
  122. package/dist/commands/types.d.ts.map +1 -1
  123. package/dist/commands/types.js +3 -2
  124. package/dist/commands/types.js.map +1 -1
  125. package/dist/commands/update.d.ts.map +1 -1
  126. package/dist/commands/update.js +54 -21
  127. package/dist/commands/update.js.map +1 -1
  128. package/dist/config.d.ts +2 -1
  129. package/dist/config.d.ts.map +1 -1
  130. package/dist/config.js.map +1 -1
  131. package/dist/dev-compose.d.ts +26 -0
  132. package/dist/dev-compose.d.ts.map +1 -1
  133. package/dist/dev-compose.js +328 -34
  134. package/dist/dev-compose.js.map +1 -1
  135. package/dist/dev-log-bus.d.ts +30 -0
  136. package/dist/dev-log-bus.d.ts.map +1 -0
  137. package/dist/dev-log-bus.js +87 -0
  138. package/dist/dev-log-bus.js.map +1 -0
  139. package/dist/dev-log-filter.d.ts +10 -0
  140. package/dist/dev-log-filter.d.ts.map +1 -0
  141. package/dist/dev-log-filter.js +36 -0
  142. package/dist/dev-log-filter.js.map +1 -0
  143. package/dist/dev-logo.d.ts +12 -0
  144. package/dist/dev-logo.d.ts.map +1 -0
  145. package/dist/dev-logo.js +56 -0
  146. package/dist/dev-logo.js.map +1 -0
  147. package/dist/dev-session.d.ts +26 -0
  148. package/dist/dev-session.d.ts.map +1 -0
  149. package/dist/dev-session.js +106 -0
  150. package/dist/dev-session.js.map +1 -0
  151. package/dist/dev-shutdown.d.ts +9 -0
  152. package/dist/dev-shutdown.d.ts.map +1 -0
  153. package/dist/dev-shutdown.js +50 -0
  154. package/dist/dev-shutdown.js.map +1 -0
  155. package/dist/dev-task-colors.d.ts +13 -0
  156. package/dist/dev-task-colors.d.ts.map +1 -0
  157. package/dist/dev-task-colors.js +43 -0
  158. package/dist/dev-task-colors.js.map +1 -0
  159. package/dist/dev-tui.d.ts +24 -0
  160. package/dist/dev-tui.d.ts.map +1 -0
  161. package/dist/dev-tui.js +188 -0
  162. package/dist/dev-tui.js.map +1 -0
  163. package/dist/diff-output.d.ts +5 -1
  164. package/dist/diff-output.d.ts.map +1 -1
  165. package/dist/diff-output.js +69 -0
  166. package/dist/diff-output.js.map +1 -1
  167. package/dist/docker-runtime.d.ts +30 -0
  168. package/dist/docker-runtime.d.ts.map +1 -0
  169. package/dist/docker-runtime.js +118 -0
  170. package/dist/docker-runtime.js.map +1 -0
  171. package/dist/engine-client.d.ts +10 -1
  172. package/dist/engine-client.d.ts.map +1 -1
  173. package/dist/engine-client.js +76 -17
  174. package/dist/engine-client.js.map +1 -1
  175. package/dist/engine-push-output.d.ts +17 -0
  176. package/dist/engine-push-output.d.ts.map +1 -0
  177. package/dist/engine-push-output.js +64 -0
  178. package/dist/engine-push-output.js.map +1 -0
  179. package/dist/ensure-binary.js +2 -2
  180. package/dist/ensure-binary.js.map +1 -1
  181. package/dist/gitignore.d.ts +8 -0
  182. package/dist/gitignore.d.ts.map +1 -0
  183. package/dist/gitignore.js +41 -0
  184. package/dist/gitignore.js.map +1 -0
  185. package/dist/kong-config.d.ts +9 -0
  186. package/dist/kong-config.d.ts.map +1 -1
  187. package/dist/kong-config.js +18 -1
  188. package/dist/kong-config.js.map +1 -1
  189. package/dist/link.d.ts +66 -0
  190. package/dist/link.d.ts.map +1 -0
  191. package/dist/link.js +160 -0
  192. package/dist/link.js.map +1 -0
  193. package/dist/process-manager.d.ts +8 -0
  194. package/dist/process-manager.d.ts.map +1 -1
  195. package/dist/process-manager.js +53 -9
  196. package/dist/process-manager.js.map +1 -1
  197. package/dist/project-config.d.ts +30 -3
  198. package/dist/project-config.d.ts.map +1 -1
  199. package/dist/project-config.js +37 -4
  200. package/dist/project-config.js.map +1 -1
  201. package/dist/prompts.d.ts +3 -0
  202. package/dist/prompts.d.ts.map +1 -0
  203. package/dist/prompts.js +3 -0
  204. package/dist/prompts.js.map +1 -0
  205. package/dist/pull-utils.d.ts +50 -14
  206. package/dist/pull-utils.d.ts.map +1 -1
  207. package/dist/pull-utils.js +152 -12
  208. package/dist/pull-utils.js.map +1 -1
  209. package/dist/resolve-target.d.ts +86 -0
  210. package/dist/resolve-target.d.ts.map +1 -0
  211. package/dist/resolve-target.js +291 -0
  212. package/dist/resolve-target.js.map +1 -0
  213. package/dist/restore-system-relation-targets.d.ts +3 -0
  214. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  215. package/dist/restore-system-relation-targets.js +45 -0
  216. package/dist/restore-system-relation-targets.js.map +1 -0
  217. package/dist/runtime-routes.d.ts.map +1 -1
  218. package/dist/runtime-routes.js +7 -0
  219. package/dist/runtime-routes.js.map +1 -1
  220. package/dist/schema-ast-v2.d.ts +1 -1
  221. package/dist/schema-ast-v2.d.ts.map +1 -1
  222. package/dist/schema-ast-v2.js +2 -2
  223. package/dist/schema-ast-v2.js.map +1 -1
  224. package/dist/schema-sources.d.ts +40 -0
  225. package/dist/schema-sources.d.ts.map +1 -0
  226. package/dist/schema-sources.js +183 -0
  227. package/dist/schema-sources.js.map +1 -0
  228. package/dist/scripts/postinstall.js +5 -1
  229. package/dist/scripts/postinstall.js.map +1 -1
  230. package/dist/seed.d.ts +8 -0
  231. package/dist/seed.d.ts.map +1 -0
  232. package/dist/seed.js +32 -0
  233. package/dist/seed.js.map +1 -0
  234. package/dist/self-host-compose.d.ts +37 -1
  235. package/dist/self-host-compose.d.ts.map +1 -1
  236. package/dist/self-host-compose.js +233 -43
  237. package/dist/self-host-compose.js.map +1 -1
  238. package/dist/storage-provision.d.ts +4 -0
  239. package/dist/storage-provision.d.ts.map +1 -1
  240. package/dist/storage-provision.js +24 -2
  241. package/dist/storage-provision.js.map +1 -1
  242. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  243. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  244. package/dist/supatype-eval-1781522769253.mjs +3 -0
  245. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  246. package/dist/systemd.js +2 -2
  247. package/dist/systemd.js.map +1 -1
  248. package/dist/target-client.d.ts +10 -0
  249. package/dist/target-client.d.ts.map +1 -0
  250. package/dist/target-client.js +22 -0
  251. package/dist/target-client.js.map +1 -0
  252. package/dist/type-extractor.d.ts +11 -0
  253. package/dist/type-extractor.d.ts.map +1 -1
  254. package/dist/type-extractor.js +95 -8
  255. package/dist/type-extractor.js.map +1 -1
  256. package/dist/ui/brand.d.ts +9 -0
  257. package/dist/ui/brand.d.ts.map +1 -0
  258. package/dist/ui/brand.js +11 -0
  259. package/dist/ui/brand.js.map +1 -0
  260. package/dist/ui/confirm.d.ts +12 -0
  261. package/dist/ui/confirm.d.ts.map +1 -0
  262. package/dist/ui/confirm.js +28 -0
  263. package/dist/ui/confirm.js.map +1 -0
  264. package/dist/ui/fatal.d.ts +10 -0
  265. package/dist/ui/fatal.d.ts.map +1 -0
  266. package/dist/ui/fatal.js +34 -0
  267. package/dist/ui/fatal.js.map +1 -0
  268. package/dist/ui/index.d.ts +9 -0
  269. package/dist/ui/index.d.ts.map +1 -0
  270. package/dist/ui/index.js +9 -0
  271. package/dist/ui/index.js.map +1 -0
  272. package/dist/ui/interactive.d.ts +3 -0
  273. package/dist/ui/interactive.d.ts.map +1 -0
  274. package/dist/ui/interactive.js +5 -0
  275. package/dist/ui/interactive.js.map +1 -0
  276. package/dist/ui/messages.d.ts +10 -0
  277. package/dist/ui/messages.d.ts.map +1 -0
  278. package/dist/ui/messages.js +35 -0
  279. package/dist/ui/messages.js.map +1 -0
  280. package/dist/ui/next-steps.d.ts +3 -0
  281. package/dist/ui/next-steps.d.ts.map +1 -0
  282. package/dist/ui/next-steps.js +10 -0
  283. package/dist/ui/next-steps.js.map +1 -0
  284. package/dist/ui/progress.d.ts +5 -0
  285. package/dist/ui/progress.d.ts.map +1 -0
  286. package/dist/ui/progress.js +24 -0
  287. package/dist/ui/progress.js.map +1 -0
  288. package/dist/ui/prompts.d.ts +14 -0
  289. package/dist/ui/prompts.d.ts.map +1 -0
  290. package/dist/ui/prompts.js +34 -0
  291. package/dist/ui/prompts.js.map +1 -0
  292. package/package.json +9 -4
  293. package/src/app/framework.ts +1 -3
  294. package/src/app/proxy-dev-app.ts +114 -6
  295. package/src/app-config.ts +80 -0
  296. package/src/binary-cache.ts +102 -52
  297. package/src/cli.ts +16 -2
  298. package/src/commands/add.ts +97 -0
  299. package/src/commands/admin.ts +39 -73
  300. package/src/commands/adopt.ts +82 -0
  301. package/src/commands/app.ts +20 -17
  302. package/src/commands/cache.ts +11 -10
  303. package/src/commands/cloud.ts +91 -142
  304. package/src/commands/db.ts +40 -63
  305. package/src/commands/deploy.ts +186 -126
  306. package/src/commands/dev.ts +95 -55
  307. package/src/commands/diff.ts +52 -43
  308. package/src/commands/doctor.ts +103 -0
  309. package/src/commands/engine.ts +5 -4
  310. package/src/commands/functions.ts +187 -123
  311. package/src/commands/generate.ts +5 -4
  312. package/src/commands/init.ts +996 -105
  313. package/src/commands/introspect.ts +48 -0
  314. package/src/commands/keys.ts +56 -14
  315. package/src/commands/link-helpers.ts +273 -0
  316. package/src/commands/logs.ts +5 -4
  317. package/src/commands/migrate-from-v1.ts +3 -2
  318. package/src/commands/migrate.ts +167 -27
  319. package/src/commands/pg.ts +13 -18
  320. package/src/commands/plugins.ts +55 -46
  321. package/src/commands/pull.ts +38 -9
  322. package/src/commands/push.ts +147 -174
  323. package/src/commands/seed.ts +5 -4
  324. package/src/commands/self-host.ts +85 -54
  325. package/src/commands/self-update.ts +3 -2
  326. package/src/commands/status.ts +102 -33
  327. package/src/commands/types.ts +3 -2
  328. package/src/commands/update.ts +59 -23
  329. package/src/config.ts +2 -1
  330. package/src/dev-compose.ts +426 -34
  331. package/src/dev-log-bus.ts +101 -0
  332. package/src/dev-log-filter.ts +32 -0
  333. package/src/dev-logo.ts +61 -0
  334. package/src/dev-session.ts +130 -0
  335. package/src/dev-shutdown.ts +54 -0
  336. package/src/dev-task-colors.ts +47 -0
  337. package/src/dev-tui.ts +232 -0
  338. package/src/diff-output.ts +79 -1
  339. package/src/docker-runtime.ts +151 -0
  340. package/src/engine-client.ts +81 -17
  341. package/src/engine-push-output.ts +75 -0
  342. package/src/ensure-binary.ts +2 -2
  343. package/src/gitignore.ts +48 -0
  344. package/src/kong-config.ts +24 -1
  345. package/src/link.ts +243 -0
  346. package/src/process-manager.ts +66 -10
  347. package/src/project-config.ts +62 -7
  348. package/src/prompts.ts +2 -0
  349. package/src/pull-utils.ts +217 -23
  350. package/src/resolve-target.ts +419 -0
  351. package/src/restore-system-relation-targets.ts +45 -0
  352. package/src/runtime-routes.ts +7 -0
  353. package/src/schema-ast-v2.ts +2 -1
  354. package/src/schema-sources.ts +248 -0
  355. package/src/scripts/postinstall.ts +7 -1
  356. package/src/seed.ts +43 -0
  357. package/src/self-host-compose.ts +261 -46
  358. package/src/storage-provision.ts +33 -1
  359. package/src/supatype-eval-1781522769253.mts +1 -0
  360. package/src/systemd.ts +2 -2
  361. package/src/target-client.ts +40 -0
  362. package/src/type-extractor.ts +124 -11
  363. package/src/ui/README.md +17 -0
  364. package/src/ui/brand.ts +12 -0
  365. package/src/ui/confirm.ts +38 -0
  366. package/src/ui/fatal.ts +43 -0
  367. package/src/ui/index.ts +8 -0
  368. package/src/ui/interactive.ts +4 -0
  369. package/src/ui/messages.ts +43 -0
  370. package/src/ui/next-steps.ts +10 -0
  371. package/src/ui/progress.ts +28 -0
  372. package/src/ui/prompts.ts +40 -0
  373. package/tests/cli-help.test.ts +27 -2
  374. package/tests/config.test.ts +29 -2
  375. package/tests/dev-ui.test.ts +139 -0
  376. package/tests/docker-runtime.test.ts +236 -0
  377. package/tests/engine-push-output.test.ts +67 -0
  378. package/tests/init.test.ts +197 -18
  379. package/tests/link.test.ts +148 -0
  380. package/tests/minisign.test.ts +102 -0
  381. package/tests/proxy-dev-app.test.ts +45 -1
  382. package/tests/pull-utils.test.ts +5 -4
  383. package/tests/runtime-contract.test.ts +186 -2
  384. package/tests/schema-sources.test.ts +119 -0
  385. package/tests/storage-provision.test.ts +100 -0
  386. package/tests/ui-confirm.test.ts +41 -0
  387. package/tests/ui-messages.test.ts +66 -0
  388. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,44 @@
1
- import type { DiffResult } from "./engine-client.js"
1
+ import type { DiffResult, Operation } from "./engine-client.js"
2
+
3
+ /** Human-readable label for a single schema operation. */
4
+ export function formatOperation(op: Operation): string {
5
+ if (typeof op.description === "string" && op.description.trim().length > 0) {
6
+ return op.description
7
+ }
8
+
9
+ const kind = typeof op.type === "string" ? op.type : typeof op.kind === "string" ? op.kind : "operation"
10
+ const raw = op as unknown as Record<string, unknown>
11
+ const table = raw["table"]
12
+ const column = raw["column"]
13
+ const index = raw["index"]
14
+ const sql = typeof op.sql === "string" ? op.sql.trim() : ""
15
+
16
+ if (kind === "add_unique_constraint" || kind === "drop_unique_constraint") {
17
+ const constraint = typeof raw["constraint"] === "string" ? raw["constraint"] : null
18
+ if (typeof table === "string" && constraint) return `${kind} ${table}.${constraint}`
19
+ }
20
+
21
+ if (kind === "create_index" || kind === "drop_index" || kind === "add_index") {
22
+ const indexName = typeof index === "string" ? index : typeof raw["name"] === "string" ? raw["name"] : null
23
+ const fields = Array.isArray(raw["fields"]) ? raw["fields"].join(", ") : null
24
+ if (indexName && fields) return `${kind} ${table}.${indexName} (${fields})`
25
+ if (indexName) return `${kind} ${table}.${indexName}`
26
+ }
27
+
28
+ if (typeof table === "string" && typeof column === "string") {
29
+ return `${kind} ${table}.${column}`
30
+ }
31
+ if (typeof table === "string") {
32
+ return `${kind} ${table}`
33
+ }
34
+
35
+ if (sql) {
36
+ const oneLine = sql.replace(/\s+/g, " ").slice(0, 120)
37
+ return `${kind}: ${oneLine}${sql.length > 120 ? "…" : ""}`
38
+ }
39
+
40
+ return kind
41
+ }
2
42
 
3
43
  /** Print engine diff warnings before the operation list. */
4
44
  export function printDiffWarnings(diff: DiffResult): void {
@@ -10,3 +50,41 @@ export function printDiffWarnings(diff: DiffResult): void {
10
50
  }
11
51
  console.log()
12
52
  }
53
+
54
+ const RISK_SYMBOL: Record<NonNullable<DiffResult["operations"][number]["risk"]>, string> = {
55
+ safe: "+",
56
+ warn: "~",
57
+ cautious: "~",
58
+ danger: "!",
59
+ destructive: "!",
60
+ }
61
+
62
+ const RISK_LEGEND: Record<NonNullable<DiffResult["operations"][number]["risk"]>, string> = {
63
+ safe: "safe",
64
+ warn: "caution",
65
+ cautious: "caution",
66
+ danger: "DANGER",
67
+ destructive: "DANGER",
68
+ }
69
+
70
+ /** Print planned schema operations from a diff result. */
71
+ export function printDiffOperations(diff: DiffResult): void {
72
+ const ops = diff.operations ?? []
73
+ if (ops.length === 0) {
74
+ console.log("No changes.")
75
+ return
76
+ }
77
+
78
+ console.log(`\n${ops.length} change(s):\n`)
79
+ for (const op of ops) {
80
+ const r = op.risk ?? "safe"
81
+ const label = op.warning ?? formatOperation(op)
82
+ console.log(` [${RISK_SYMBOL[r]}] ${label} (${RISK_LEGEND[r]})`)
83
+ }
84
+
85
+ const dangerous = ops.filter((o) => o.risk === "danger").length
86
+ if (dangerous > 0) {
87
+ console.log(`\n ${dangerous} dangerous operation(s). Review before pushing.`)
88
+ }
89
+ console.log()
90
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Docker daemon availability — used before `docker compose` and other docker CLI calls.
3
+ */
4
+
5
+ import { spawnSync } from "node:child_process"
6
+ import { platform } from "node:os"
7
+ import { endDevSession, getActiveDevSession } from "./dev-session.js"
8
+ import { isInteractive } from "./ui/interactive.js"
9
+ import { error, plain } from "./ui/messages.js"
10
+ import { clack as p, printLogo } from "./ui/prompts.js"
11
+
12
+ export type DockerDaemonProbe =
13
+ | { ok: true }
14
+ | { ok: false; reason: "cli_missing" | "daemon_unavailable"; detail?: string }
15
+
16
+ export type DockerBrandOptions = {
17
+ intro: string
18
+ }
19
+
20
+ export type DockerReportOptions = {
21
+ /** Logo + Clack intro before the error (e.g. `supatype dev` entry). */
22
+ brand?: DockerBrandOptions
23
+ }
24
+
25
+ function dockerSpawn(args: string[]) {
26
+ return spawnSync("docker", args, {
27
+ encoding: "utf8",
28
+ stdio: "pipe",
29
+ shell: process.platform === "win32",
30
+ })
31
+ }
32
+
33
+ /** Returns whether the Docker CLI is on PATH and the daemon accepts connections. */
34
+ export function probeDockerDaemon(): DockerDaemonProbe {
35
+ const version = dockerSpawn(["version", "--format", "{{.Client.Version}}"])
36
+ if (version.error && "code" in version.error && version.error.code === "ENOENT") {
37
+ return { ok: false, reason: "cli_missing" }
38
+ }
39
+
40
+ const clientVersion = (version.stdout ?? "").trim()
41
+ const versionStderr = (version.stderr ?? "").trim()
42
+ const versionDetail = `${versionStderr}${version.stdout ?? ""}`.trim()
43
+
44
+ // No client version — CLI missing or broken.
45
+ if (!clientVersion) {
46
+ return {
47
+ ok: false,
48
+ reason: "cli_missing",
49
+ ...(versionDetail ? { detail: versionDetail } : {}),
50
+ }
51
+ }
52
+
53
+ // Client is present but daemon refused (paused/stopped). `docker info` can hang
54
+ // while paused on Docker Desktop — use the version stderr and skip info.
55
+ if (version.status !== 0 && versionStderr) {
56
+ return { ok: false, reason: "daemon_unavailable", detail: versionStderr }
57
+ }
58
+
59
+ const info = dockerSpawn(["info"])
60
+ if (info.status === 0) return { ok: true }
61
+
62
+ const infoDetail = `${info.stderr ?? ""}${info.stdout ?? ""}`.trim()
63
+ const detail = infoDetail || versionDetail
64
+ return { ok: false, reason: "daemon_unavailable", ...(detail ? { detail } : {}) }
65
+ }
66
+
67
+ function shouldShowDockerDetail(probe: Extract<DockerDaemonProbe, { ok: false }>): boolean {
68
+ if (!probe.detail) return false
69
+ const lower = probe.detail.toLowerCase()
70
+ if (probe.reason !== "daemon_unavailable") return true
71
+ // Skip raw daemon stderr when we already print a clearer hint.
72
+ return !lower.includes("paused") && !lower.includes("cannot connect")
73
+ }
74
+
75
+ function dockerUnavailableHeadline(probe: Extract<DockerDaemonProbe, { ok: false }>): string {
76
+ if (probe.reason === "cli_missing") {
77
+ return "Docker is not installed or not on your PATH."
78
+ }
79
+ return "Docker is installed but the daemon is not running."
80
+ }
81
+
82
+ function dockerUnavailableHints(probe: Extract<DockerDaemonProbe, { ok: false }>): string[] {
83
+ const hints: string[] = []
84
+
85
+ if (probe.reason === "cli_missing") {
86
+ hints.push(
87
+ "Install Docker Desktop (https://www.docker.com/products/docker-desktop/) or add the docker CLI to PATH.",
88
+ )
89
+ } else if (probe.detail?.toLowerCase().includes("paused")) {
90
+ hints.push("Unpause Docker Desktop from the whale menu or Dashboard, then try again.")
91
+ } else if (platform() === "win32" || platform() === "darwin") {
92
+ hints.push("Start Docker Desktop, then try again.")
93
+ } else {
94
+ hints.push("Start the Docker daemon (e.g. sudo systemctl start docker), then try again.")
95
+ }
96
+
97
+ hints.push('To develop without Docker, set provider: "native" in supatype.config.ts.')
98
+
99
+ if (shouldShowDockerDetail(probe)) {
100
+ const firstLine = probe.detail!.split(/\r?\n/).find((line) => line.trim().length > 0)
101
+ if (firstLine) hints.push(`docker: ${firstLine.trim()}`)
102
+ }
103
+
104
+ return hints
105
+ }
106
+
107
+ /** @deprecated Tests only — use `reportDockerUnavailable`. */
108
+ export function formatDockerUnavailableMessage(
109
+ probe: Extract<DockerDaemonProbe, { ok: false }>,
110
+ ): string {
111
+ const lines: string[] = [dockerUnavailableHeadline(probe), ...dockerUnavailableHints(probe)]
112
+ return lines.join("\n")
113
+ }
114
+
115
+ /** Print Docker-unavailable guidance via the shared CLI message layer. */
116
+ export function reportDockerUnavailable(
117
+ probe: Extract<DockerDaemonProbe, { ok: false }>,
118
+ opts?: DockerReportOptions,
119
+ ): void {
120
+ if (opts?.brand && isInteractive()) {
121
+ printLogo()
122
+ p.intro(opts.brand.intro)
123
+ }
124
+
125
+ const headline = dockerUnavailableHeadline(probe)
126
+ const hints = dockerUnavailableHints(probe)
127
+
128
+ error(headline)
129
+
130
+ if (isInteractive()) {
131
+ p.note(hints.join("\n\n"))
132
+ return
133
+ }
134
+
135
+ for (const hint of hints) {
136
+ plain()
137
+ plain(` ${hint}`)
138
+ }
139
+ }
140
+
141
+ /** Exit 1 with a friendly message when Docker is not usable. */
142
+ export function requireDockerDaemon(opts?: DockerReportOptions): void {
143
+ const probe = probeDockerDaemon()
144
+ if (probe.ok) return
145
+ // Dev TUI patches console — restore stderr before printing a fatal message.
146
+ if (getActiveDevSession()) {
147
+ endDevSession()
148
+ }
149
+ reportDockerUnavailable(probe, opts)
150
+ process.exit(1)
151
+ }
@@ -13,21 +13,25 @@ import { mkdirSync, writeFileSync, unlinkSync, existsSync, readdirSync } from "n
13
13
  import { tmpdir, homedir } from "node:os"
14
14
  import { join } from "node:path"
15
15
  import { loadConfig } from "./config.js"
16
- import { resolveBinary, currentPlatform, cachePath } from "./binary-cache.js"
16
+ import { currentPlatform, cachePath } from "./binary-cache.js"
17
+ import { ensureBinary } from "./ensure-binary.js"
17
18
 
18
19
  // ---------------------------------------------------------------------------
19
20
  // Types (kept for backward compatibility with existing callers)
20
21
  // ---------------------------------------------------------------------------
21
22
 
22
23
  export interface Operation {
23
- kind: "create_table" | "alter_table" | "drop_table" | "create_index" | "drop_index" |
24
- "create_policy" | "drop_policy" | "add_column" | "drop_column" | "alter_column" |
25
- "recreate_column"
26
24
  type?: string
25
+ kind?: string
27
26
  description?: string
28
27
  risk?: "safe" | "warn" | "danger" | "cautious" | "destructive"
29
28
  warning?: string
30
29
  sql?: string
30
+ table?: string
31
+ column?: string
32
+ constraint?: string
33
+ index_name?: string
34
+ index?: { fields?: string[]; name?: string; unique?: boolean }
31
35
  }
32
36
 
33
37
  export interface DiffResult {
@@ -83,10 +87,16 @@ async function getEngineBin(): Promise<string> {
83
87
 
84
88
  try {
85
89
  const config = loadConfig(cwd)
86
- _engineBin = await resolveBinary("engine", config)
90
+ // Download-on-miss (with retry) so a fresh machine or a failed postinstall
91
+ // self-heals on first use instead of silently skipping type/admin refresh.
92
+ _engineBin = await ensureBinary("engine", config)
87
93
  return _engineBin
88
- } catch {
89
- // No valid project config fall through to default cache path.
94
+ } catch (err) {
95
+ // A real download/verification failure must surface, not fall back to a
96
+ // possibly-stale cached binary from a different version.
97
+ const message = err instanceof Error ? err.message : String(err)
98
+ if (message.includes("Failed to download")) throw err
99
+ // Otherwise (no valid project config) fall through to default cache scan.
90
100
  }
91
101
 
92
102
  // No config found — scan the cache for any available engine binary.
@@ -146,24 +156,40 @@ export async function engineRequest<T = unknown>(
146
156
  ): Promise<T> {
147
157
  const bin = await getEngineBin()
148
158
 
149
- // Write request to a temp file.
150
- // For CLI-mode endpoints the engine reads the input file as a raw SchemaAst,
151
- // so we extract the `ast` field when present, otherwise write the full body.
152
159
  const tmpDir = join(tmpdir(), "supatype-engine")
153
160
  mkdirSync(tmpDir, { recursive: true })
161
+ const cleanup: string[] = []
154
162
  const reqFile = join(tmpDir, `req-${Date.now()}.json`)
155
163
  const inputPayload = body["ast"] !== undefined ? body["ast"] : body
156
164
  writeFileSync(reqFile, JSON.stringify(inputPayload))
165
+ cleanup.push(reqFile)
166
+
167
+ let gzPath: string | undefined
168
+ let manifestPath: string | undefined
169
+ if (typeof body["schema_sources_gz_base64"] === "string") {
170
+ gzPath = join(tmpDir, `sources-${Date.now()}.gz`)
171
+ writeFileSync(gzPath, Buffer.from(body["schema_sources_gz_base64"], "base64"))
172
+ cleanup.push(gzPath)
173
+ }
174
+ if (body["schema_sources_manifest"] !== undefined) {
175
+ manifestPath = join(tmpDir, `manifest-${Date.now()}.json`)
176
+ writeFileSync(manifestPath, JSON.stringify(body["schema_sources_manifest"]))
177
+ cleanup.push(manifestPath)
178
+ }
157
179
 
158
- const args = endpointToArgs(endpoint, body, reqFile)
180
+ const args = endpointToArgs(endpoint, body, reqFile, {
181
+ ...(gzPath !== undefined ? { gzPath } : {}),
182
+ ...(manifestPath !== undefined ? { manifestPath } : {}),
183
+ })
159
184
 
160
185
  const result = spawnSync(bin, args, {
161
186
  encoding: "utf8",
162
187
  cwd: process.cwd(),
163
188
  })
164
189
 
165
- // Clean up temp file.
166
- try { unlinkSync(reqFile) } catch { /* ignore */ }
190
+ for (const f of cleanup) {
191
+ try { unlinkSync(f) } catch { /* ignore */ }
192
+ }
167
193
 
168
194
  if (result.status !== 0) {
169
195
  const stderr = result.stderr?.trim() || "(no output)"
@@ -195,6 +221,7 @@ function endpointToArgs(
195
221
  endpoint: string,
196
222
  body: Record<string, unknown>,
197
223
  reqFile: string,
224
+ sources?: { gzPath?: string; manifestPath?: string },
198
225
  ): string[] {
199
226
  const dbUrl = (body["database_url"] as string | undefined) ?? ""
200
227
  const schema = (body["schema"] as string | undefined) ?? "public"
@@ -206,8 +233,26 @@ function endpointToArgs(
206
233
  case "/diff":
207
234
  return ["diff", "--input", reqFile, "--database-url", dbUrl, "--schema", schema]
208
235
 
209
- case "/push":
210
- return ["push", "--input", reqFile, "--database-url", dbUrl, "--schema", schema, ...force, ...nonInteractive]
236
+ case "/push": {
237
+ const sourceArgs: string[] = []
238
+ if (sources?.gzPath) sourceArgs.push("--schema-sources-gz", sources.gzPath)
239
+ if (sources?.manifestPath) sourceArgs.push("--schema-sources-manifest", sources.manifestPath)
240
+ return [
241
+ "push",
242
+ "--input",
243
+ reqFile,
244
+ "--database-url",
245
+ dbUrl,
246
+ "--schema",
247
+ schema,
248
+ ...force,
249
+ ...nonInteractive,
250
+ ...sourceArgs,
251
+ ]
252
+ }
253
+
254
+ case "/rollback":
255
+ return ["rollback", "--database-url", dbUrl, "--schema", schema]
211
256
 
212
257
  case "/parse":
213
258
  return ["parse", "--input", reqFile]
@@ -220,6 +265,18 @@ function endpointToArgs(
220
265
  case "/introspect":
221
266
  return ["introspect", "--database-url", dbUrl, "--schema", schema]
222
267
 
268
+ case "/doctor": {
269
+ const strict = body["strict"] ? ["--strict"] : []
270
+ const noCache = body["no_cache"] ? ["--no-cache"] : []
271
+ return ["doctor", "--input", reqFile, "--database-url", dbUrl, "--schema", schema, ...strict, ...noCache]
272
+ }
273
+
274
+ case "/adopt": {
275
+ const yes = body["yes"] ? ["--yes"] : []
276
+ const noCache = body["no_cache"] ? ["--no-cache"] : []
277
+ return ["adopt", "--input", reqFile, "--database-url", dbUrl, "--schema", schema, ...yes, ...noCache]
278
+ }
279
+
223
280
  case "/validate":
224
281
  return ["validate", "--input", reqFile]
225
282
 
@@ -227,9 +284,16 @@ function endpointToArgs(
227
284
  return ["admin", "--input", reqFile]
228
285
 
229
286
  default:
230
- if (endpoint.startsWith("/migrations")) {
287
+ if (endpoint === "/migrations" || endpoint.startsWith("/migrations")) {
231
288
  const action = (body["action"] as string | undefined) ?? "list"
232
- return ["migrations", action, "--database-url", dbUrl]
289
+ if (action === "rollback") {
290
+ return ["rollback", "--database-url", dbUrl, "--schema", schema]
291
+ }
292
+ const name = body["name"] as string | undefined
293
+ if (name) {
294
+ return ["migrations", "--database-url", dbUrl, "--name", name]
295
+ }
296
+ return ["migrations", "--database-url", dbUrl]
233
297
  }
234
298
  return [endpoint.replace(/^\//, ""), "--input", reqFile]
235
299
  }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Parse and format schema-engine push output (compose subprocess or HTTP).
3
+ */
4
+
5
+ export interface EnginePushResult {
6
+ status?: string
7
+ operations?: number
8
+ admin_refreshed?: boolean
9
+ message?: string
10
+ }
11
+
12
+ /** Extract the engine JSON object from mixed docker compose stdout/stderr. */
13
+ export function parseEngineJsonOutput<T>(output: string): T | null {
14
+ const trimmed = output.trim()
15
+ if (!trimmed) return null
16
+
17
+ for (const line of trimmed.split(/\r?\n/)) {
18
+ const candidate = line.trim()
19
+ if (!candidate.startsWith("{") || !candidate.endsWith("}")) continue
20
+ try {
21
+ return JSON.parse(candidate) as T
22
+ } catch {
23
+ /* try next line */
24
+ }
25
+ }
26
+
27
+ if (trimmed.startsWith("{")) {
28
+ try {
29
+ return JSON.parse(trimmed) as T
30
+ } catch {
31
+ return null
32
+ }
33
+ }
34
+
35
+ return null
36
+ }
37
+
38
+ export function parseEnginePushOutput(output: string): EnginePushResult | null {
39
+ return parseEngineJsonOutput<EnginePushResult>(output)
40
+ }
41
+
42
+ /** Human-readable one-liner for successful push (matches `supatype push` tone). */
43
+ export function formatEnginePushMessage(result: EnginePushResult): string {
44
+ if (result.status === "up_to_date") {
45
+ if (result.admin_refreshed) {
46
+ return "Schema up to date — Studio metadata synced."
47
+ }
48
+ return "Schema up to date."
49
+ }
50
+
51
+ const ops = result.operations
52
+ if (typeof ops === "number" && ops > 0) {
53
+ return `Applied ${ops} operation(s).`
54
+ }
55
+
56
+ return result.message?.trim() || "Schema applied."
57
+ }
58
+
59
+ const COMPOSE_PROGRESS_LINE =
60
+ /^Container\s+.+\s+(Running|Waiting|Healthy|Created|Starting|Started|Exited|Stopped)\s*$/i
61
+
62
+ /** Drop docker compose progress noise; keep engine JSON and real errors. */
63
+ export function filterComposeNoise(output: string): string {
64
+ return output
65
+ .split(/\r?\n/)
66
+ .map((line) => line.trimEnd())
67
+ .filter((line) => {
68
+ const t = line.trim()
69
+ if (!t) return false
70
+ if (COMPOSE_PROGRESS_LINE.test(t)) return false
71
+ return true
72
+ })
73
+ .join("\n")
74
+ .trim()
75
+ }
@@ -6,7 +6,7 @@ import {
6
6
  resolveBinary,
7
7
  download,
8
8
  currentPlatform,
9
- versionFor,
9
+ resolveVersionFor,
10
10
  type Component,
11
11
  } from "./binary-cache.js"
12
12
  import type { SupatypeProjectConfig } from "./project-config.js"
@@ -24,5 +24,5 @@ export async function ensureBinary(
24
24
  }
25
25
  }
26
26
 
27
- return download(component, versionFor(component, config), currentPlatform())
27
+ return download(component, await resolveVersionFor(component, config), currentPlatform())
28
28
  }
@@ -0,0 +1,48 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { resolve } from "node:path"
3
+
4
+ export const SUPATYPE_GITIGNORE_MARKER = "# Supatype — local runtime (contains secrets in link.json)"
5
+ export const SUPATYPE_GITIGNORE_BLOCK = `${SUPATYPE_GITIGNORE_MARKER}
6
+ .env
7
+ .supatype/
8
+ supatype.local.config.ts
9
+ supatype.local.config.js
10
+ supatype.local.config.mjs
11
+ `
12
+
13
+ export function isSupatypeGitignored(cwd: string): boolean {
14
+ const gitignorePath = resolve(cwd, ".gitignore")
15
+ if (!existsSync(gitignorePath)) return false
16
+ const content = readFileSync(gitignorePath, "utf8")
17
+ return /\.supatype\/?(\/|\s|$)/m.test(content) || content.includes(".supatype/")
18
+ }
19
+
20
+ export function ensureSupatypeGitignore(cwd: string, opts?: { silent?: boolean }): boolean {
21
+ const gitignorePath = resolve(cwd, ".gitignore")
22
+ if (isSupatypeGitignored(cwd)) return true
23
+
24
+ if (existsSync(gitignorePath)) {
25
+ const content = readFileSync(gitignorePath, "utf8")
26
+ const next = content.endsWith("\n") ? content : `${content}\n`
27
+ writeFileSync(gitignorePath, `${next}\n${SUPATYPE_GITIGNORE_BLOCK}`, "utf8")
28
+ } else {
29
+ writeFileSync(
30
+ gitignorePath,
31
+ `.env\nnode_modules/\ndist/\n${SUPATYPE_GITIGNORE_BLOCK}`,
32
+ "utf8",
33
+ )
34
+ }
35
+
36
+ if (!opts?.silent) {
37
+ console.warn("Added .supatype/ to .gitignore (link.json contains secrets — never commit).")
38
+ }
39
+ return true
40
+ }
41
+
42
+ export function warnIfLinkNotGitignored(cwd: string): void {
43
+ if (isSupatypeGitignored(cwd)) return
44
+ console.warn(
45
+ "\n⚠ Warning: .supatype/ is not in .gitignore — link.json contains tokens and must not be committed.",
46
+ )
47
+ console.warn(" Run with link --fix-gitignore to append the Supatype block.\n")
48
+ }
@@ -18,6 +18,11 @@ export interface KongDeclarativeOptions {
18
18
  studioServiceUrl?: string | undefined
19
19
  /** See {@link RuntimeRouteOptions.studioStripPath}. */
20
20
  studioStripPath?: boolean | undefined
21
+ /**
22
+ * When set, append a global Kong `acme` plugin (Let's Encrypt) with Redis/Valkey
23
+ * storage so the self-host gateway provisions and renews TLS automatically.
24
+ */
25
+ acme?: { email: string; domain: string; redisHost: string } | undefined
21
26
  }
22
27
 
23
28
  /** Escape a string for use inside YAML double quotes. */
@@ -85,9 +90,27 @@ ${route.paths.map((path) => ` - ${path}`).join("\n")}
85
90
  ${protocols}${routePlugins}${graphqlPlugins}`
86
91
  }).join("\n")
87
92
 
93
+ const acme = opts.acme
94
+ const pluginsBlock = acme
95
+ ? `
96
+ plugins:
97
+ - name: acme
98
+ config:
99
+ account_email: ${yamlQuotedString(acme.email)}
100
+ tos_accepted: true
101
+ domains:
102
+ - ${yamlQuotedString(acme.domain)}
103
+ storage: redis
104
+ storage_config:
105
+ redis:
106
+ host: ${yamlQuotedString(acme.redisHost)}
107
+ port: 6379
108
+ `
109
+ : ""
110
+
88
111
  return `_format_version: "3.0"
89
112
  ${consumersBlock}
90
113
  services:
91
114
  ${servicesBlock}
92
- `
115
+ ${pluginsBlock}`
93
116
  }