@supatype/cli 0.1.0-alpha.9 → 0.1.1

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 (407) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +285 -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 +28 -1
  30. package/dist/commands/admin.d.ts.map +1 -1
  31. package/dist/commands/admin.js +297 -149
  32. package/dist/commands/admin.js.map +1 -1
  33. package/dist/commands/adopt.d.ts +3 -0
  34. package/dist/commands/adopt.d.ts.map +1 -0
  35. package/dist/commands/adopt.js +55 -0
  36. package/dist/commands/adopt.js.map +1 -0
  37. package/dist/commands/app.d.ts.map +1 -1
  38. package/dist/commands/app.js +20 -17
  39. package/dist/commands/app.js.map +1 -1
  40. package/dist/commands/cache.d.ts.map +1 -1
  41. package/dist/commands/cache.js +11 -10
  42. package/dist/commands/cache.js.map +1 -1
  43. package/dist/commands/cloud.d.ts +4 -9
  44. package/dist/commands/cloud.d.ts.map +1 -1
  45. package/dist/commands/cloud.js +75 -125
  46. package/dist/commands/cloud.js.map +1 -1
  47. package/dist/commands/db.d.ts.map +1 -1
  48. package/dist/commands/db.js +37 -58
  49. package/dist/commands/db.js.map +1 -1
  50. package/dist/commands/deploy.d.ts.map +1 -1
  51. package/dist/commands/deploy.js +140 -96
  52. package/dist/commands/deploy.js.map +1 -1
  53. package/dist/commands/dev.d.ts.map +1 -1
  54. package/dist/commands/dev.js +74 -39
  55. package/dist/commands/dev.js.map +1 -1
  56. package/dist/commands/diff.d.ts.map +1 -1
  57. package/dist/commands/diff.js +39 -39
  58. package/dist/commands/diff.js.map +1 -1
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.d.ts.map +1 -0
  61. package/dist/commands/doctor.js +78 -0
  62. package/dist/commands/doctor.js.map +1 -0
  63. package/dist/commands/engine.d.ts.map +1 -1
  64. package/dist/commands/engine.js +5 -4
  65. package/dist/commands/engine.js.map +1 -1
  66. package/dist/commands/functions.d.ts.map +1 -1
  67. package/dist/commands/functions.js +172 -119
  68. package/dist/commands/functions.js.map +1 -1
  69. package/dist/commands/generate.d.ts.map +1 -1
  70. package/dist/commands/generate.js +5 -4
  71. package/dist/commands/generate.js.map +1 -1
  72. package/dist/commands/init.d.ts +35 -1
  73. package/dist/commands/init.d.ts.map +1 -1
  74. package/dist/commands/init.js +883 -107
  75. package/dist/commands/init.js.map +1 -1
  76. package/dist/commands/introspect.d.ts +3 -0
  77. package/dist/commands/introspect.d.ts.map +1 -0
  78. package/dist/commands/introspect.js +35 -0
  79. package/dist/commands/introspect.js.map +1 -0
  80. package/dist/commands/keys.d.ts +15 -1
  81. package/dist/commands/keys.d.ts.map +1 -1
  82. package/dist/commands/keys.js +46 -10
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/link-helpers.d.ts +15 -0
  85. package/dist/commands/link-helpers.d.ts.map +1 -0
  86. package/dist/commands/link-helpers.js +225 -0
  87. package/dist/commands/link-helpers.js.map +1 -0
  88. package/dist/commands/logs.d.ts.map +1 -1
  89. package/dist/commands/logs.js +5 -4
  90. package/dist/commands/logs.js.map +1 -1
  91. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  92. package/dist/commands/migrate-from-v1.js +3 -2
  93. package/dist/commands/migrate-from-v1.js.map +1 -1
  94. package/dist/commands/migrate.d.ts.map +1 -1
  95. package/dist/commands/migrate.js +119 -26
  96. package/dist/commands/migrate.js.map +1 -1
  97. package/dist/commands/pg.d.ts.map +1 -1
  98. package/dist/commands/pg.js +11 -12
  99. package/dist/commands/pg.js.map +1 -1
  100. package/dist/commands/plugins.d.ts.map +1 -1
  101. package/dist/commands/plugins.js +55 -46
  102. package/dist/commands/plugins.js.map +1 -1
  103. package/dist/commands/pull.d.ts.map +1 -1
  104. package/dist/commands/pull.js +33 -5
  105. package/dist/commands/pull.js.map +1 -1
  106. package/dist/commands/push.d.ts.map +1 -1
  107. package/dist/commands/push.js +111 -138
  108. package/dist/commands/push.js.map +1 -1
  109. package/dist/commands/seed.d.ts.map +1 -1
  110. package/dist/commands/seed.js +4 -3
  111. package/dist/commands/seed.js.map +1 -1
  112. package/dist/commands/self-host.d.ts +2 -2
  113. package/dist/commands/self-host.d.ts.map +1 -1
  114. package/dist/commands/self-host.js +65 -50
  115. package/dist/commands/self-host.js.map +1 -1
  116. package/dist/commands/self-update.d.ts.map +1 -1
  117. package/dist/commands/self-update.js +3 -2
  118. package/dist/commands/self-update.js.map +1 -1
  119. package/dist/commands/status.d.ts +1 -1
  120. package/dist/commands/status.d.ts.map +1 -1
  121. package/dist/commands/status.js +95 -29
  122. package/dist/commands/status.js.map +1 -1
  123. package/dist/commands/types.d.ts.map +1 -1
  124. package/dist/commands/types.js +3 -2
  125. package/dist/commands/types.js.map +1 -1
  126. package/dist/commands/update.d.ts.map +1 -1
  127. package/dist/commands/update.js +54 -21
  128. package/dist/commands/update.js.map +1 -1
  129. package/dist/compose-rename.d.ts +10 -0
  130. package/dist/compose-rename.d.ts.map +1 -0
  131. package/dist/compose-rename.js +67 -0
  132. package/dist/compose-rename.js.map +1 -0
  133. package/dist/config.d.ts +2 -1
  134. package/dist/config.d.ts.map +1 -1
  135. package/dist/config.js.map +1 -1
  136. package/dist/dev-compose.d.ts +26 -0
  137. package/dist/dev-compose.d.ts.map +1 -1
  138. package/dist/dev-compose.js +357 -79
  139. package/dist/dev-compose.js.map +1 -1
  140. package/dist/dev-log-bus.d.ts +30 -0
  141. package/dist/dev-log-bus.d.ts.map +1 -0
  142. package/dist/dev-log-bus.js +87 -0
  143. package/dist/dev-log-bus.js.map +1 -0
  144. package/dist/dev-log-filter.d.ts +10 -0
  145. package/dist/dev-log-filter.d.ts.map +1 -0
  146. package/dist/dev-log-filter.js +36 -0
  147. package/dist/dev-log-filter.js.map +1 -0
  148. package/dist/dev-logo.d.ts +12 -0
  149. package/dist/dev-logo.d.ts.map +1 -0
  150. package/dist/dev-logo.js +56 -0
  151. package/dist/dev-logo.js.map +1 -0
  152. package/dist/dev-ports.d.ts +27 -0
  153. package/dist/dev-ports.d.ts.map +1 -0
  154. package/dist/dev-ports.js +171 -0
  155. package/dist/dev-ports.js.map +1 -0
  156. package/dist/dev-session-lock.d.ts +25 -0
  157. package/dist/dev-session-lock.d.ts.map +1 -0
  158. package/dist/dev-session-lock.js +81 -0
  159. package/dist/dev-session-lock.js.map +1 -0
  160. package/dist/dev-session.d.ts +26 -0
  161. package/dist/dev-session.d.ts.map +1 -0
  162. package/dist/dev-session.js +106 -0
  163. package/dist/dev-session.js.map +1 -0
  164. package/dist/dev-shutdown.d.ts +25 -0
  165. package/dist/dev-shutdown.d.ts.map +1 -0
  166. package/dist/dev-shutdown.js +114 -0
  167. package/dist/dev-shutdown.js.map +1 -0
  168. package/dist/dev-task-colors.d.ts +13 -0
  169. package/dist/dev-task-colors.d.ts.map +1 -0
  170. package/dist/dev-task-colors.js +43 -0
  171. package/dist/dev-task-colors.js.map +1 -0
  172. package/dist/dev-tui.d.ts +24 -0
  173. package/dist/dev-tui.d.ts.map +1 -0
  174. package/dist/dev-tui.js +188 -0
  175. package/dist/dev-tui.js.map +1 -0
  176. package/dist/diff-output.d.ts +5 -1
  177. package/dist/diff-output.d.ts.map +1 -1
  178. package/dist/diff-output.js +69 -0
  179. package/dist/diff-output.js.map +1 -1
  180. package/dist/docker-runtime.d.ts +30 -0
  181. package/dist/docker-runtime.d.ts.map +1 -0
  182. package/dist/docker-runtime.js +118 -0
  183. package/dist/docker-runtime.js.map +1 -0
  184. package/dist/engine-client.d.ts +10 -1
  185. package/dist/engine-client.d.ts.map +1 -1
  186. package/dist/engine-client.js +76 -17
  187. package/dist/engine-client.js.map +1 -1
  188. package/dist/engine-push-output.d.ts +17 -0
  189. package/dist/engine-push-output.d.ts.map +1 -0
  190. package/dist/engine-push-output.js +64 -0
  191. package/dist/engine-push-output.js.map +1 -0
  192. package/dist/ensure-binary.js +2 -2
  193. package/dist/ensure-binary.js.map +1 -1
  194. package/dist/env-file.d.ts +5 -0
  195. package/dist/env-file.d.ts.map +1 -0
  196. package/dist/env-file.js +33 -0
  197. package/dist/env-file.js.map +1 -0
  198. package/dist/gitignore.d.ts +8 -0
  199. package/dist/gitignore.d.ts.map +1 -0
  200. package/dist/gitignore.js +41 -0
  201. package/dist/gitignore.js.map +1 -0
  202. package/dist/kong-config.d.ts +9 -0
  203. package/dist/kong-config.d.ts.map +1 -1
  204. package/dist/kong-config.js +18 -1
  205. package/dist/kong-config.js.map +1 -1
  206. package/dist/link.d.ts +66 -0
  207. package/dist/link.d.ts.map +1 -0
  208. package/dist/link.js +160 -0
  209. package/dist/link.js.map +1 -0
  210. package/dist/process-manager.d.ts +8 -0
  211. package/dist/process-manager.d.ts.map +1 -1
  212. package/dist/process-manager.js +53 -9
  213. package/dist/process-manager.js.map +1 -1
  214. package/dist/project-config.d.ts +30 -3
  215. package/dist/project-config.d.ts.map +1 -1
  216. package/dist/project-config.js +37 -4
  217. package/dist/project-config.js.map +1 -1
  218. package/dist/prompts.d.ts +3 -0
  219. package/dist/prompts.d.ts.map +1 -0
  220. package/dist/prompts.js +3 -0
  221. package/dist/prompts.js.map +1 -0
  222. package/dist/pull-utils.d.ts +50 -14
  223. package/dist/pull-utils.d.ts.map +1 -1
  224. package/dist/pull-utils.js +152 -12
  225. package/dist/pull-utils.js.map +1 -1
  226. package/dist/resolve-target.d.ts +86 -0
  227. package/dist/resolve-target.d.ts.map +1 -0
  228. package/dist/resolve-target.js +291 -0
  229. package/dist/resolve-target.js.map +1 -0
  230. package/dist/restore-system-relation-targets.d.ts +3 -0
  231. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  232. package/dist/restore-system-relation-targets.js +45 -0
  233. package/dist/restore-system-relation-targets.js.map +1 -0
  234. package/dist/runtime-routes.d.ts.map +1 -1
  235. package/dist/runtime-routes.js +7 -0
  236. package/dist/runtime-routes.js.map +1 -1
  237. package/dist/schema-ast-v2.d.ts +1 -1
  238. package/dist/schema-ast-v2.d.ts.map +1 -1
  239. package/dist/schema-ast-v2.js +2 -2
  240. package/dist/schema-ast-v2.js.map +1 -1
  241. package/dist/schema-sources.d.ts +40 -0
  242. package/dist/schema-sources.d.ts.map +1 -0
  243. package/dist/schema-sources.js +183 -0
  244. package/dist/schema-sources.js.map +1 -0
  245. package/dist/scripts/postinstall.js +5 -1
  246. package/dist/scripts/postinstall.js.map +1 -1
  247. package/dist/self-host-compose.d.ts +37 -1
  248. package/dist/self-host-compose.d.ts.map +1 -1
  249. package/dist/self-host-compose.js +234 -43
  250. package/dist/self-host-compose.js.map +1 -1
  251. package/dist/storage-provision.d.ts +4 -0
  252. package/dist/storage-provision.d.ts.map +1 -1
  253. package/dist/storage-provision.js +24 -2
  254. package/dist/storage-provision.js.map +1 -1
  255. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  256. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  257. package/dist/supatype-eval-1781522769253.mjs +3 -0
  258. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  259. package/dist/systemd.js +2 -2
  260. package/dist/systemd.js.map +1 -1
  261. package/dist/target-client.d.ts +10 -0
  262. package/dist/target-client.d.ts.map +1 -0
  263. package/dist/target-client.js +22 -0
  264. package/dist/target-client.js.map +1 -0
  265. package/dist/type-extractor.d.ts +11 -0
  266. package/dist/type-extractor.d.ts.map +1 -1
  267. package/dist/type-extractor.js +95 -8
  268. package/dist/type-extractor.js.map +1 -1
  269. package/dist/ui/brand.d.ts +9 -0
  270. package/dist/ui/brand.d.ts.map +1 -0
  271. package/dist/ui/brand.js +11 -0
  272. package/dist/ui/brand.js.map +1 -0
  273. package/dist/ui/confirm.d.ts +12 -0
  274. package/dist/ui/confirm.d.ts.map +1 -0
  275. package/dist/ui/confirm.js +28 -0
  276. package/dist/ui/confirm.js.map +1 -0
  277. package/dist/ui/fatal.d.ts +10 -0
  278. package/dist/ui/fatal.d.ts.map +1 -0
  279. package/dist/ui/fatal.js +34 -0
  280. package/dist/ui/fatal.js.map +1 -0
  281. package/dist/ui/index.d.ts +9 -0
  282. package/dist/ui/index.d.ts.map +1 -0
  283. package/dist/ui/index.js +9 -0
  284. package/dist/ui/index.js.map +1 -0
  285. package/dist/ui/interactive.d.ts +3 -0
  286. package/dist/ui/interactive.d.ts.map +1 -0
  287. package/dist/ui/interactive.js +5 -0
  288. package/dist/ui/interactive.js.map +1 -0
  289. package/dist/ui/messages.d.ts +10 -0
  290. package/dist/ui/messages.d.ts.map +1 -0
  291. package/dist/ui/messages.js +35 -0
  292. package/dist/ui/messages.js.map +1 -0
  293. package/dist/ui/next-steps.d.ts +3 -0
  294. package/dist/ui/next-steps.d.ts.map +1 -0
  295. package/dist/ui/next-steps.js +10 -0
  296. package/dist/ui/next-steps.js.map +1 -0
  297. package/dist/ui/progress.d.ts +5 -0
  298. package/dist/ui/progress.d.ts.map +1 -0
  299. package/dist/ui/progress.js +24 -0
  300. package/dist/ui/progress.js.map +1 -0
  301. package/dist/ui/prompts.d.ts +14 -0
  302. package/dist/ui/prompts.d.ts.map +1 -0
  303. package/dist/ui/prompts.js +34 -0
  304. package/dist/ui/prompts.js.map +1 -0
  305. package/package.json +5 -2
  306. package/src/app/framework.ts +1 -3
  307. package/src/app/proxy-dev-app.ts +114 -6
  308. package/src/app-config.ts +80 -0
  309. package/src/binary-cache.ts +102 -52
  310. package/src/cli.ts +16 -2
  311. package/src/commands/add.ts +97 -0
  312. package/src/commands/admin.ts +381 -190
  313. package/src/commands/adopt.ts +82 -0
  314. package/src/commands/app.ts +20 -17
  315. package/src/commands/cache.ts +11 -10
  316. package/src/commands/cloud.ts +91 -142
  317. package/src/commands/db.ts +40 -63
  318. package/src/commands/deploy.ts +186 -126
  319. package/src/commands/dev.ts +98 -55
  320. package/src/commands/diff.ts +52 -43
  321. package/src/commands/doctor.ts +103 -0
  322. package/src/commands/engine.ts +5 -4
  323. package/src/commands/functions.ts +187 -123
  324. package/src/commands/generate.ts +5 -4
  325. package/src/commands/init.ts +1087 -104
  326. package/src/commands/introspect.ts +48 -0
  327. package/src/commands/keys.ts +56 -14
  328. package/src/commands/link-helpers.ts +273 -0
  329. package/src/commands/logs.ts +5 -4
  330. package/src/commands/migrate-from-v1.ts +3 -2
  331. package/src/commands/migrate.ts +167 -27
  332. package/src/commands/pg.ts +13 -18
  333. package/src/commands/plugins.ts +55 -46
  334. package/src/commands/pull.ts +38 -9
  335. package/src/commands/push.ts +148 -175
  336. package/src/commands/seed.ts +5 -4
  337. package/src/commands/self-host.ts +85 -54
  338. package/src/commands/self-update.ts +3 -2
  339. package/src/commands/status.ts +102 -33
  340. package/src/commands/types.ts +3 -2
  341. package/src/commands/update.ts +59 -23
  342. package/src/compose-rename.ts +76 -0
  343. package/src/config.ts +2 -1
  344. package/src/dev-compose.ts +462 -76
  345. package/src/dev-log-bus.ts +101 -0
  346. package/src/dev-log-filter.ts +32 -0
  347. package/src/dev-logo.ts +61 -0
  348. package/src/dev-ports.ts +212 -0
  349. package/src/dev-session-lock.ts +101 -0
  350. package/src/dev-session.ts +130 -0
  351. package/src/dev-shutdown.ts +147 -0
  352. package/src/dev-task-colors.ts +47 -0
  353. package/src/dev-tui.ts +232 -0
  354. package/src/diff-output.ts +79 -1
  355. package/src/docker-runtime.ts +151 -0
  356. package/src/engine-client.ts +81 -17
  357. package/src/engine-push-output.ts +75 -0
  358. package/src/ensure-binary.ts +2 -2
  359. package/src/env-file.ts +37 -0
  360. package/src/gitignore.ts +48 -0
  361. package/src/kong-config.ts +24 -1
  362. package/src/link.ts +243 -0
  363. package/src/process-manager.ts +66 -10
  364. package/src/project-config.ts +62 -7
  365. package/src/prompts.ts +2 -0
  366. package/src/pull-utils.ts +217 -23
  367. package/src/resolve-target.ts +419 -0
  368. package/src/restore-system-relation-targets.ts +45 -0
  369. package/src/runtime-routes.ts +7 -0
  370. package/src/schema-ast-v2.ts +2 -1
  371. package/src/schema-sources.ts +248 -0
  372. package/src/scripts/postinstall.ts +7 -1
  373. package/src/self-host-compose.ts +262 -46
  374. package/src/storage-provision.ts +33 -1
  375. package/src/supatype-eval-1781522769253.mts +1 -0
  376. package/src/systemd.ts +2 -2
  377. package/src/target-client.ts +40 -0
  378. package/src/type-extractor.ts +124 -11
  379. package/src/ui/README.md +17 -0
  380. package/src/ui/brand.ts +12 -0
  381. package/src/ui/confirm.ts +38 -0
  382. package/src/ui/fatal.ts +43 -0
  383. package/src/ui/index.ts +8 -0
  384. package/src/ui/interactive.ts +4 -0
  385. package/src/ui/messages.ts +43 -0
  386. package/src/ui/next-steps.ts +10 -0
  387. package/src/ui/progress.ts +28 -0
  388. package/src/ui/prompts.ts +40 -0
  389. package/tests/admin-ensure.test.ts +59 -0
  390. package/tests/cli-help.test.ts +27 -2
  391. package/tests/config.test.ts +29 -2
  392. package/tests/dev-ports.test.ts +41 -0
  393. package/tests/dev-session-lock.test.ts +54 -0
  394. package/tests/dev-ui.test.ts +162 -0
  395. package/tests/docker-runtime.test.ts +236 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/init.test.ts +197 -18
  398. package/tests/link.test.ts +148 -0
  399. package/tests/minisign.test.ts +102 -0
  400. package/tests/proxy-dev-app.test.ts +45 -1
  401. package/tests/pull-utils.test.ts +5 -4
  402. package/tests/runtime-contract.test.ts +186 -2
  403. package/tests/schema-sources.test.ts +119 -0
  404. package/tests/storage-provision.test.ts +100 -0
  405. package/tests/ui-confirm.test.ts +41 -0
  406. package/tests/ui-messages.test.ts +66 -0
  407. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Per-task log buffers for `supatype dev` TUI mode.
3
+ */
4
+
5
+ export type DevLogLevel = "log" | "warn" | "error"
6
+
7
+ export interface DevTask {
8
+ id: string
9
+ title: string
10
+ lines: string[]
11
+ unread: boolean
12
+ }
13
+
14
+ const DEFAULT_MAX_LINES = 5_000
15
+
16
+ export class DevLogBus {
17
+ private readonly tasks = new Map<string, DevTask>()
18
+ private readonly order: string[] = []
19
+ private readonly listeners = new Set<() => void>()
20
+ private focusedTaskId = "stack"
21
+
22
+ constructor(private readonly maxLines = DEFAULT_MAX_LINES) {
23
+ this.registerTask("stack", "supatype")
24
+ }
25
+
26
+ registerTask(id: string, title: string): void {
27
+ if (this.tasks.has(id)) return
28
+ this.tasks.set(id, { id, title, lines: [], unread: false })
29
+ this.order.push(id)
30
+ this.notify()
31
+ }
32
+
33
+ ensureTask(id: string, title = id): void {
34
+ this.registerTask(id, title)
35
+ }
36
+
37
+ getTaskOrder(): readonly string[] {
38
+ return this.order
39
+ }
40
+
41
+ getTask(id: string): DevTask | undefined {
42
+ return this.tasks.get(id)
43
+ }
44
+
45
+ getFocusedTaskId(): string {
46
+ return this.focusedTaskId
47
+ }
48
+
49
+ setFocusedTaskId(id: string): void {
50
+ if (!this.tasks.has(id)) return
51
+ this.focusedTaskId = id
52
+ const task = this.tasks.get(id)
53
+ if (task) task.unread = false
54
+ this.notify()
55
+ }
56
+
57
+ focusNext(): void {
58
+ const idx = this.order.indexOf(this.focusedTaskId)
59
+ const next = this.order[(idx + 1) % this.order.length]
60
+ if (next) this.setFocusedTaskId(next)
61
+ }
62
+
63
+ focusPrevious(): void {
64
+ const idx = this.order.indexOf(this.focusedTaskId)
65
+ const prev = this.order[(idx - 1 + this.order.length) % this.order.length]
66
+ if (prev) this.setFocusedTaskId(prev)
67
+ }
68
+
69
+ append(taskId: string, line: string, level: DevLogLevel = "log"): void {
70
+ this.ensureTask(taskId, taskId)
71
+ const task = this.tasks.get(taskId)
72
+ if (!task) return
73
+
74
+ const levelPrefix = level === "warn" ? "⚠ " : level === "error" ? "✗ " : ""
75
+ const parts = line.split(/\r?\n/)
76
+
77
+ for (const part of parts) {
78
+ if (part.length === 0) continue
79
+ const text = taskId === "stack" ? part.replace(/^\[supatype\]\s*/, "") : part
80
+ if (text.length === 0) continue
81
+ task.lines.push(levelPrefix + text)
82
+ if (task.lines.length > this.maxLines) {
83
+ task.lines.splice(0, task.lines.length - this.maxLines)
84
+ }
85
+ }
86
+
87
+ if (taskId !== this.focusedTaskId && parts.some((p) => p.length > 0)) {
88
+ task.unread = true
89
+ }
90
+ this.notify()
91
+ }
92
+
93
+ onUpdate(listener: () => void): () => void {
94
+ this.listeners.add(listener)
95
+ return () => this.listeners.delete(listener)
96
+ }
97
+
98
+ private notify(): void {
99
+ for (const listener of this.listeners) listener()
100
+ }
101
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Line filters for subprocess output during `supatype dev`.
3
+ */
4
+
5
+ /** Strip ANSI SGR sequences for TUI rendering. */
6
+ export function stripAnsi(text: string): string {
7
+ return text.replace(/\x1b\[[0-9;]*m/g, "")
8
+ }
9
+
10
+ /** Drop npm/vite noise from frontend dev servers (proxy app + Studio). */
11
+ export function filterDevSubprocessLine(taskId: string, line: string): boolean {
12
+ if (taskId !== "app" && taskId !== "studio") return true
13
+ const trimmed = stripAnsi(line).trim()
14
+ if (!trimmed) return false
15
+ if (/^>\s+\S/.test(trimmed)) return false
16
+ if (trimmed.includes("Network:") && trimmed.includes("➜")) return false
17
+ return true
18
+ }
19
+
20
+ /** Format console.* arguments the way Node does for a single log line. */
21
+ export function formatConsoleArgs(args: unknown[]): string {
22
+ return args
23
+ .map((arg) => {
24
+ if (typeof arg === "string") return arg
25
+ try {
26
+ return JSON.stringify(arg)
27
+ } catch {
28
+ return String(arg)
29
+ }
30
+ })
31
+ .join(" ")
32
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Supatype ASCII wordmark for the dev TUI header.
3
+ * Baked from `figlet -f slant supatype` → assets/supatype-logo-wordmark.ascii.txt
4
+ */
5
+
6
+ import { existsSync, readFileSync } from "node:fs"
7
+ import { dirname, join } from "node:path"
8
+ import { fileURLToPath } from "node:url"
9
+ import { BOLD, BRAND_COLOR, RESET } from "./ui/brand.js"
10
+
11
+ const EMBEDDED_LOGO_WORDMARK = [
12
+ " __ ",
13
+ " _______ ______ ____ _/ /___ ______ ___ ",
14
+ " / ___/ / / / __ \\/ __ `/ __/ / / / __ \\/ _ \\",
15
+ " (__ ) /_/ / /_/ / /_/ / /_/ /_/ / /_/ / __/",
16
+ "/____/\\__,_/ .___/\\__,_/\\__/\\__, / .___/\\___/ ",
17
+ " /_/ /____/_/ ",
18
+ ] as const
19
+
20
+ function readLogoFile(basename: string): readonly string[] | null {
21
+ const here = dirname(fileURLToPath(import.meta.url))
22
+ const candidates = [
23
+ join(here, "assets", basename),
24
+ join(here, "..", "assets", basename),
25
+ ]
26
+ for (const path of candidates) {
27
+ if (!existsSync(path)) continue
28
+ const lines = readFileSync(path, "utf8")
29
+ .split(/\r?\n/)
30
+ .map((line) => (line.endsWith("\r") ? line.slice(0, -1) : line))
31
+ .filter((line) => line.length > 0)
32
+ if (lines.length > 0) return lines
33
+ }
34
+ return null
35
+ }
36
+
37
+ function loadWordmarkLogoLines(): readonly string[] {
38
+ const fromFile = readLogoFile("supatype-logo-wordmark.ascii.txt")
39
+ if (fromFile) return fromFile
40
+ return [...EMBEDDED_LOGO_WORDMARK]
41
+ }
42
+
43
+ /** Figlet slant wordmark (~46×6). */
44
+ export const SUPATYPE_ASCII_LOGO_WORDMARK = loadWordmarkLogoLines()
45
+
46
+ export function pickLogoLines(): readonly string[] {
47
+ return SUPATYPE_ASCII_LOGO_WORDMARK
48
+ }
49
+
50
+ export function logoRowCount(): number {
51
+ return pickLogoLines().length
52
+ }
53
+
54
+ /** Return logo rows exactly as authored (spaces preserved). */
55
+ export function layoutLogoBlock(lines: readonly string[]): string[] {
56
+ return [...lines]
57
+ }
58
+
59
+ export function colorLogoLines(lines: readonly string[]): string[] {
60
+ return lines.map((line) => `${BRAND_COLOR}${BOLD}${line}${RESET}`)
61
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Kong / Postgres host port resolution for local Docker dev.
3
+ * Persists SUPATYPE_KONG_PORT in `.env` so re-runs are stable, but re-checks
4
+ * availability so multiple projects and port collisions are surfaced clearly.
5
+ */
6
+
7
+ import * as p from "@clack/prompts"
8
+ import { COMPOSE_DEV_KONG_PORT } from "./project-config.js"
9
+ import { isPortInUse } from "./postgres-ctl.js"
10
+ import { readEnvInt, upsertEnvFile } from "./env-file.js"
11
+ import { isInteractive } from "./ui/interactive.js"
12
+ import { fatalError } from "./ui/fatal.js"
13
+
14
+ const MIN_PORT = 1024
15
+ const MAX_PORT = 65535
16
+
17
+ export function isValidHostPort(port: number): boolean {
18
+ return Number.isInteger(port) && port >= MIN_PORT && port <= MAX_PORT
19
+ }
20
+
21
+ export function parseHostPortInput(raw: string): number | null {
22
+ const port = Number(raw.trim())
23
+ return isValidHostPort(port) ? port : null
24
+ }
25
+
26
+ /** Next free TCP port on 127.0.0.1 starting at `start`. */
27
+ export async function findNextFreePort(start: number): Promise<number> {
28
+ let port = Math.max(MIN_PORT, start)
29
+ while (port <= MAX_PORT && (await isPortInUse(port))) port++
30
+ if (port > MAX_PORT) {
31
+ throw new Error(`No free local port found in range ${start}–${MAX_PORT}.`)
32
+ }
33
+ return port
34
+ }
35
+
36
+ export function readPersistedKongPort(cwd: string): number | null {
37
+ return readEnvInt(cwd, "SUPATYPE_KONG_PORT")
38
+ }
39
+
40
+ function persistKongPort(cwd: string, port: number): void {
41
+ const apiUrl = `http://localhost:${port}`
42
+ upsertEnvFile(cwd, {
43
+ SUPATYPE_KONG_PORT: String(port),
44
+ PUBLIC_SUPATYPE_URL: apiUrl,
45
+ API_EXTERNAL_URL: apiUrl,
46
+ SITE_URL: apiUrl,
47
+ })
48
+ }
49
+
50
+ async function promptPortConflict(
51
+ cwd: string,
52
+ blockedPort: number,
53
+ reason: "in_use" | "init",
54
+ ): Promise<number> {
55
+ const port = await promptPortConflictWithoutPersist(blockedPort, reason)
56
+ persistKongPort(cwd, port)
57
+ return port
58
+ }
59
+
60
+ export interface EnsureKongPortOptions {
61
+ /** When false, fail fast instead of prompting (CI / scripts). */
62
+ interactive?: boolean
63
+ /** init wizard — slightly different copy */
64
+ context?: "dev" | "init"
65
+ }
66
+
67
+ /**
68
+ * Resolve the Kong host port for this project directory.
69
+ * - Uses `.env` when set and available.
70
+ * - Prompts (or errors) when the configured port is taken.
71
+ * - Auto-picks the next free port on first run when unset.
72
+ */
73
+ export async function ensureKongPort(
74
+ cwd: string,
75
+ opts: EnsureKongPortOptions = {},
76
+ ): Promise<number> {
77
+ const interactive = opts.interactive ?? isInteractive()
78
+ const context = opts.context ?? "dev"
79
+ const persisted = readPersistedKongPort(cwd)
80
+
81
+ if (persisted !== null) {
82
+ if (!(await isPortInUse(persisted))) return persisted
83
+
84
+ if (!interactive) {
85
+ fatalError(`Port ${persisted} is already in use (SUPATYPE_KONG_PORT in .env).`, [
86
+ "Stop the other service or set a different SUPATYPE_KONG_PORT.",
87
+ "Run `supatype dev` in a terminal to pick a new port interactively.",
88
+ ])
89
+ }
90
+
91
+ return promptPortConflict(cwd, persisted, "in_use")
92
+ }
93
+
94
+ const preferred = COMPOSE_DEV_KONG_PORT
95
+ const port =
96
+ (await isPortInUse(preferred))
97
+ ? interactive && context === "init"
98
+ ? await promptPortConflict(cwd, preferred, "init")
99
+ : await findNextFreePort(preferred)
100
+ : preferred
101
+
102
+ return port
103
+ }
104
+
105
+ /** Wizard / init — pick a Kong port without writing `.env` (scaffold writes it). */
106
+ export async function promptKongPortChoice(): Promise<number> {
107
+ const freeDefault = (await isPortInUse(COMPOSE_DEV_KONG_PORT))
108
+ ? await findNextFreePort(COMPOSE_DEV_KONG_PORT)
109
+ : COMPOSE_DEV_KONG_PORT
110
+
111
+ const value = await p.text({
112
+ message: "Local API gateway port (Kong)",
113
+ defaultValue: String(freeDefault),
114
+ placeholder: String(COMPOSE_DEV_KONG_PORT),
115
+ validate: (raw) => {
116
+ const port = parseHostPortInput(raw ?? "")
117
+ if (!port) return `Enter a port between ${MIN_PORT} and ${MAX_PORT}.`
118
+ return undefined
119
+ },
120
+ })
121
+
122
+ if (p.isCancel(value)) {
123
+ p.cancel("Cancelled.")
124
+ process.exit(0)
125
+ }
126
+
127
+ const port = parseHostPortInput(value)!
128
+ if (await isPortInUse(port)) {
129
+ return promptPortConflictWithoutPersist(port, "init")
130
+ }
131
+ return port
132
+ }
133
+
134
+ async function promptPortConflictWithoutPersist(
135
+ blockedPort: number,
136
+ reason: "in_use" | "init",
137
+ ): Promise<number> {
138
+ const suggested = await findNextFreePort(blockedPort + 1)
139
+ const headline =
140
+ reason === "init"
141
+ ? `Port ${blockedPort} is already in use on this machine.`
142
+ : `Port ${blockedPort} is in use — another Supatype project or service may already be bound to it.`
143
+
144
+ const choice = await p.select<"suggested" | "custom" | "cancel">({
145
+ message: headline,
146
+ options: [
147
+ {
148
+ value: "suggested",
149
+ label: `Use ${suggested} instead`,
150
+ hint: "next available port",
151
+ },
152
+ { value: "custom", label: "Enter a different port" },
153
+ { value: "cancel", label: "Cancel" },
154
+ ],
155
+ })
156
+
157
+ if (p.isCancel(choice) || choice === "cancel") {
158
+ p.cancel("Cancelled.")
159
+ process.exit(0)
160
+ }
161
+
162
+ if (choice === "suggested") return suggested
163
+
164
+ const custom = await p.text({
165
+ message: "Local API gateway port (Kong)",
166
+ defaultValue: String(suggested),
167
+ validate: (value) => {
168
+ const port = parseHostPortInput(value ?? "")
169
+ if (!port) return `Enter a port between ${MIN_PORT} and ${MAX_PORT}.`
170
+ return undefined
171
+ },
172
+ })
173
+
174
+ if (p.isCancel(custom)) {
175
+ p.cancel("Cancelled.")
176
+ process.exit(0)
177
+ }
178
+
179
+ const port = parseHostPortInput(custom)
180
+ if (!port) {
181
+ p.cancel("Invalid port.")
182
+ process.exit(1)
183
+ }
184
+
185
+ if (await isPortInUse(port)) {
186
+ fatalError(`Port ${port} is still in use.`, [
187
+ "Pick another port or stop the service using it.",
188
+ "Check Docker Desktop for other Supatype stacks.",
189
+ ])
190
+ }
191
+
192
+ return port
193
+ }
194
+
195
+ const COMPOSE_DEV_DB_PORT = 54329
196
+
197
+ export async function ensureDevDbPort(cwd: string): Promise<number> {
198
+ const persisted = readEnvInt(cwd, "SUPATYPE_DEV_DB_PORT")
199
+ if (persisted !== null) {
200
+ if (!(await isPortInUse(persisted))) return persisted
201
+ const next = await findNextFreePort(persisted + 1)
202
+ upsertEnvFile(cwd, {
203
+ SUPATYPE_DEV_DB_PORT: String(next),
204
+ DATABASE_URL: `postgresql://supatype_admin:postgres@localhost:${next}/supatype?sslmode=disable`,
205
+ })
206
+ return next
207
+ }
208
+
209
+ let port = COMPOSE_DEV_DB_PORT
210
+ while (await isPortInUse(port)) port++
211
+ return port
212
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Tracks an active `supatype dev` session so we can recover when the CLI exits
3
+ * without graceful shutdown (terminal closed, kill signal, etc.).
4
+ */
5
+
6
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs"
7
+ import { join, resolve } from "node:path"
8
+ import { spawnSync } from "node:child_process"
9
+ import * as p from "@clack/prompts"
10
+ import { runDockerCompose } from "./self-host-compose.js"
11
+ import { isInteractive } from "./ui/interactive.js"
12
+ import { warn } from "./ui/messages.js"
13
+
14
+ const LOCK_VERSION = 1 as const
15
+
16
+ export interface DevSessionLock {
17
+ version: typeof LOCK_VERSION
18
+ composeProject: string
19
+ projectRef: string
20
+ composePath: string
21
+ kongPort: number
22
+ startedAt: string
23
+ }
24
+
25
+ export function devSessionLockPath(cwd: string): string {
26
+ return resolve(cwd, ".supatype/dev-session.json")
27
+ }
28
+
29
+ export function readDevSessionLock(cwd: string): DevSessionLock | null {
30
+ const path = devSessionLockPath(cwd)
31
+ if (!existsSync(path)) return null
32
+ try {
33
+ const data = JSON.parse(readFileSync(path, "utf8")) as DevSessionLock
34
+ if (data.version !== LOCK_VERSION) return null
35
+ return data
36
+ } catch {
37
+ return null
38
+ }
39
+ }
40
+
41
+ export function writeDevSessionLock(cwd: string, lock: Omit<DevSessionLock, "version">): void {
42
+ const dir = join(cwd, ".supatype")
43
+ mkdirSync(dir, { recursive: true })
44
+ const payload: DevSessionLock = { version: LOCK_VERSION, ...lock }
45
+ writeFileSync(devSessionLockPath(cwd), `${JSON.stringify(payload, null, 2)}\n`, "utf8")
46
+ }
47
+
48
+ export function clearDevSessionLock(cwd: string): void {
49
+ const path = devSessionLockPath(cwd)
50
+ if (existsSync(path)) unlinkSync(path)
51
+ }
52
+
53
+ export function composeStackHasContainers(composeProject: string): boolean {
54
+ const result = spawnSync(
55
+ "docker",
56
+ ["ps", "-a", "--filter", `label=com.docker.compose.project=${composeProject}`, "--format", "{{.Names}}"],
57
+ { encoding: "utf8", shell: process.platform === "win32" },
58
+ )
59
+ return Boolean(result.stdout?.trim())
60
+ }
61
+
62
+ /**
63
+ * When a previous dev session did not shut down cleanly, offer to stop its stack
64
+ * before starting a new one.
65
+ */
66
+ export async function recoverStaleDevSession(cwd: string): Promise<void> {
67
+ const lock = readDevSessionLock(cwd)
68
+ if (!lock) return
69
+ if (!composeStackHasContainers(lock.composeProject)) {
70
+ clearDevSessionLock(cwd)
71
+ return
72
+ }
73
+
74
+ const message =
75
+ `Previous dev session for "${lock.projectRef}" may not have shut down cleanly ` +
76
+ `(stack "${lock.composeProject}" is still running).`
77
+
78
+ if (!isInteractive()) {
79
+ warn(message)
80
+ warn(`Stop it manually: docker compose -p ${lock.composeProject} down`)
81
+ return
82
+ }
83
+
84
+ const stop = await p.confirm({
85
+ message: `${message}\n\nStop the orphaned stack before starting?`,
86
+ initialValue: true,
87
+ })
88
+
89
+ if (p.isCancel(stop) || !stop) {
90
+ warn(`Leaving "${lock.composeProject}" running.`)
91
+ return
92
+ }
93
+
94
+ const status = runDockerCompose(lock.composePath, ["down"], cwd, lock.composeProject, { quiet: true })
95
+ if (status === 0) {
96
+ clearDevSessionLock(cwd)
97
+ p.log.success(`Stopped orphaned stack "${lock.composeProject}".`)
98
+ } else {
99
+ warn(`Could not stop "${lock.composeProject}" (exit ${status}).`)
100
+ }
101
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * `supatype dev` output session — TUI (default) or interleaved stream mode.
3
+ */
4
+
5
+ import type { ProcessOptions } from "./process-manager.js"
6
+ import { filterDevSubprocessLine, formatConsoleArgs } from "./dev-log-filter.js"
7
+ import { DevLogBus, type DevLogLevel } from "./dev-log-bus.js"
8
+ import { DevTui } from "./dev-tui.js"
9
+
10
+ export type DevUiMode = "tui" | "stream"
11
+
12
+ export function resolveDevUiMode(streamFlag: boolean): DevUiMode {
13
+ if (streamFlag) return "stream"
14
+ if (!process.stdout.isTTY || !process.stdin.isTTY) return "stream"
15
+ return "tui"
16
+ }
17
+
18
+ let activeSession: DevSession | null = null
19
+
20
+ export function beginDevSession(mode: DevUiMode): DevSession {
21
+ activeSession?.stop()
22
+ activeSession = new DevSession(mode)
23
+ return activeSession
24
+ }
25
+
26
+ /** Enter console capture + TUI. */
27
+ export function startDevSession(): void {
28
+ activeSession?.start()
29
+ }
30
+
31
+ export function endDevSession(): void {
32
+ activeSession?.stop()
33
+ activeSession = null
34
+ }
35
+
36
+ export function getActiveDevSession(): DevSession | null {
37
+ return activeSession
38
+ }
39
+
40
+ /** Log to a TUI task stream, or prefixed console output in stream mode. */
41
+ export function appendDevTaskLog(
42
+ taskId: string,
43
+ taskTitle: string,
44
+ line: string,
45
+ level: DevLogLevel = "log",
46
+ ): void {
47
+ const session = getActiveDevSession()
48
+ if (session?.isTui()) {
49
+ session.bus.ensureTask(taskId, taskTitle)
50
+ session.bus.append(taskId, line, level)
51
+ return
52
+ }
53
+ const prefix = taskId === "stack" ? "[supatype]" : `[${taskId}]`
54
+ const write = level === "warn" ? console.warn : level === "error" ? console.error : console.log
55
+ write(`${prefix} ${line}`)
56
+ }
57
+
58
+ export class DevSession {
59
+ readonly bus = new DevLogBus()
60
+ readonly mode: DevUiMode
61
+ private tui: DevTui | null = null
62
+ private restoreConsole: (() => void) | null = null
63
+
64
+ constructor(mode: DevUiMode) {
65
+ this.mode = mode
66
+ }
67
+
68
+ isTui(): boolean {
69
+ return this.mode === "tui"
70
+ }
71
+
72
+ start(): void {
73
+ if (!this.isTui()) return
74
+ this.restoreConsole = patchConsole(this.bus)
75
+ this.tui = new DevTui(this.bus)
76
+ this.tui.start()
77
+ }
78
+
79
+ stop(): void {
80
+ this.tui?.stop()
81
+ this.tui = null
82
+ this.restoreConsole?.()
83
+ this.restoreConsole = null
84
+ }
85
+ }
86
+
87
+ export function enhanceProcessOptions(label: string, opts: ProcessOptions): ProcessOptions {
88
+ const session = getActiveDevSession()
89
+ const subprocessFilter =
90
+ label === "app" || label === "studio"
91
+ ? { shouldLogLine: (line: string) => filterDevSubprocessLine(label, line) }
92
+ : {}
93
+
94
+ if (!session?.isTui()) {
95
+ return { ...opts, ...subprocessFilter }
96
+ }
97
+
98
+ session.bus.ensureTask(label, label)
99
+ return {
100
+ ...opts,
101
+ ...subprocessFilter,
102
+ onLine: (line, stream) => {
103
+ session.bus.append(label, line, stream === "stderr" ? "error" : "log")
104
+ },
105
+ }
106
+ }
107
+
108
+ function patchConsole(bus: DevLogBus): () => void {
109
+ const original = {
110
+ log: console.log,
111
+ warn: console.warn,
112
+ error: console.error,
113
+ }
114
+
115
+ console.log = (...args: unknown[]) => {
116
+ bus.append("stack", formatConsoleArgs(args), "log")
117
+ }
118
+ console.warn = (...args: unknown[]) => {
119
+ bus.append("stack", formatConsoleArgs(args), "warn")
120
+ }
121
+ console.error = (...args: unknown[]) => {
122
+ bus.append("stack", formatConsoleArgs(args), "error")
123
+ }
124
+
125
+ return () => {
126
+ console.log = original.log
127
+ console.warn = original.warn
128
+ console.error = original.error
129
+ }
130
+ }