@supatype/cli 0.1.1 → 0.1.3

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 (334) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +126 -99
  3. package/.turbo/turbo-typecheck.log +2 -2
  4. package/dist/binary-cache.d.ts +2 -0
  5. package/dist/binary-cache.d.ts.map +1 -1
  6. package/dist/binary-cache.js +36 -0
  7. package/dist/binary-cache.js.map +1 -1
  8. package/dist/binary-download-lock.d.ts +12 -0
  9. package/dist/binary-download-lock.d.ts.map +1 -0
  10. package/dist/binary-download-lock.js +106 -0
  11. package/dist/binary-download-lock.js.map +1 -0
  12. package/dist/cli-package-version.d.ts +3 -0
  13. package/dist/cli-package-version.d.ts.map +1 -0
  14. package/dist/cli-package-version.js +15 -0
  15. package/dist/cli-package-version.js.map +1 -0
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/cli.js +5 -2
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/add.js +25 -19
  20. package/dist/commands/add.js.map +1 -1
  21. package/dist/commands/admin.d.ts +12 -0
  22. package/dist/commands/admin.d.ts.map +1 -1
  23. package/dist/commands/admin.js +116 -26
  24. package/dist/commands/admin.js.map +1 -1
  25. package/dist/commands/cache.d.ts +1 -1
  26. package/dist/commands/cache.d.ts.map +1 -1
  27. package/dist/commands/cache.js +98 -5
  28. package/dist/commands/cache.js.map +1 -1
  29. package/dist/commands/dev.d.ts.map +1 -1
  30. package/dist/commands/dev.js +26 -22
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/commands/init.d.ts +3 -4
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +296 -121
  35. package/dist/commands/init.js.map +1 -1
  36. package/dist/commands/link-helpers.d.ts.map +1 -1
  37. package/dist/commands/link-helpers.js +26 -25
  38. package/dist/commands/link-helpers.js.map +1 -1
  39. package/dist/compose-local-server-image.d.ts +13 -0
  40. package/dist/compose-local-server-image.d.ts.map +1 -0
  41. package/dist/compose-local-server-image.js +57 -0
  42. package/dist/compose-local-server-image.js.map +1 -0
  43. package/dist/compose-rename.d.ts.map +1 -1
  44. package/dist/compose-rename.js +5 -8
  45. package/dist/compose-rename.js.map +1 -1
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +12 -1
  48. package/dist/config.js.map +1 -1
  49. package/dist/dev-compose.d.ts.map +1 -1
  50. package/dist/dev-compose.js +113 -56
  51. package/dist/dev-compose.js.map +1 -1
  52. package/dist/dev-log-bus.d.ts +5 -0
  53. package/dist/dev-log-bus.d.ts.map +1 -1
  54. package/dist/dev-log-bus.js +16 -0
  55. package/dist/dev-log-bus.js.map +1 -1
  56. package/dist/dev-log-filter.d.ts +4 -0
  57. package/dist/dev-log-filter.d.ts.map +1 -1
  58. package/dist/dev-log-filter.js +52 -0
  59. package/dist/dev-log-filter.js.map +1 -1
  60. package/dist/dev-ports.d.ts.map +1 -1
  61. package/dist/dev-ports.js +19 -12
  62. package/dist/dev-ports.js.map +1 -1
  63. package/dist/dev-ready-panel.d.ts +21 -0
  64. package/dist/dev-ready-panel.d.ts.map +1 -0
  65. package/dist/dev-ready-panel.js +67 -0
  66. package/dist/dev-ready-panel.js.map +1 -0
  67. package/dist/dev-session-lock.d.ts.map +1 -1
  68. package/dist/dev-session-lock.js +5 -8
  69. package/dist/dev-session-lock.js.map +1 -1
  70. package/dist/dev-session.d.ts +18 -5
  71. package/dist/dev-session.d.ts.map +1 -1
  72. package/dist/dev-session.js +91 -18
  73. package/dist/dev-session.js.map +1 -1
  74. package/dist/dev-shutdown.d.ts +2 -0
  75. package/dist/dev-shutdown.d.ts.map +1 -1
  76. package/dist/dev-shutdown.js +29 -15
  77. package/dist/dev-shutdown.js.map +1 -1
  78. package/dist/dev-task-colors.d.ts +3 -1
  79. package/dist/dev-task-colors.d.ts.map +1 -1
  80. package/dist/dev-task-colors.js +23 -3
  81. package/dist/dev-task-colors.js.map +1 -1
  82. package/dist/docker-runtime.js +1 -1
  83. package/dist/docker-runtime.js.map +1 -1
  84. package/dist/engine-push-output.d.ts +1 -0
  85. package/dist/engine-push-output.d.ts.map +1 -1
  86. package/dist/engine-push-output.js +4 -1
  87. package/dist/engine-push-output.js.map +1 -1
  88. package/dist/ensure-binary.d.ts.map +1 -1
  89. package/dist/ensure-binary.js +14 -2
  90. package/dist/ensure-binary.js.map +1 -1
  91. package/dist/ensure-component-binaries.d.ts +17 -0
  92. package/dist/ensure-component-binaries.d.ts.map +1 -0
  93. package/dist/ensure-component-binaries.js +45 -0
  94. package/dist/ensure-component-binaries.js.map +1 -0
  95. package/dist/ensure-dev-api-config.d.ts +3 -0
  96. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  97. package/dist/ensure-dev-api-config.js +17 -0
  98. package/dist/ensure-dev-api-config.js.map +1 -0
  99. package/dist/init-dependency-versions.d.ts +13 -0
  100. package/dist/init-dependency-versions.d.ts.map +1 -0
  101. package/dist/init-dependency-versions.js +53 -0
  102. package/dist/init-dependency-versions.js.map +1 -0
  103. package/dist/init-package-json.d.ts +21 -0
  104. package/dist/init-package-json.d.ts.map +1 -0
  105. package/dist/init-package-json.js +46 -0
  106. package/dist/init-package-json.js.map +1 -0
  107. package/dist/init-project-detect.d.ts +14 -0
  108. package/dist/init-project-detect.d.ts.map +1 -0
  109. package/dist/init-project-detect.js +89 -0
  110. package/dist/init-project-detect.js.map +1 -0
  111. package/dist/postgres-ctl.d.ts.map +1 -1
  112. package/dist/postgres-ctl.js +2 -1
  113. package/dist/postgres-ctl.js.map +1 -1
  114. package/dist/required-host-components.d.ts +11 -0
  115. package/dist/required-host-components.d.ts.map +1 -0
  116. package/dist/required-host-components.js +25 -0
  117. package/dist/required-host-components.js.map +1 -0
  118. package/dist/resolve-api-url.d.ts +6 -0
  119. package/dist/resolve-api-url.d.ts.map +1 -0
  120. package/dist/resolve-api-url.js +39 -0
  121. package/dist/resolve-api-url.js.map +1 -0
  122. package/dist/rest-cache-admin.d.ts +33 -0
  123. package/dist/rest-cache-admin.d.ts.map +1 -0
  124. package/dist/rest-cache-admin.js +96 -0
  125. package/dist/rest-cache-admin.js.map +1 -0
  126. package/dist/scripts/postinstall.d.ts +4 -6
  127. package/dist/scripts/postinstall.d.ts.map +1 -1
  128. package/dist/scripts/postinstall.js +8 -41
  129. package/dist/scripts/postinstall.js.map +1 -1
  130. package/dist/self-host-compose.d.ts.map +1 -1
  131. package/dist/self-host-compose.js +23 -13
  132. package/dist/self-host-compose.js.map +1 -1
  133. package/dist/studio-dev-server.d.ts +4 -2
  134. package/dist/studio-dev-server.d.ts.map +1 -1
  135. package/dist/studio-dev-server.js +3 -2
  136. package/dist/studio-dev-server.js.map +1 -1
  137. package/dist/ui/brand.d.ts +2 -5
  138. package/dist/ui/brand.d.ts.map +1 -1
  139. package/dist/ui/brand.js +2 -7
  140. package/dist/ui/brand.js.map +1 -1
  141. package/dist/ui/clack-api.d.ts +44 -0
  142. package/dist/ui/clack-api.d.ts.map +1 -0
  143. package/dist/ui/clack-api.js +76 -0
  144. package/dist/ui/clack-api.js.map +1 -0
  145. package/dist/ui/clack.d.ts +14 -0
  146. package/dist/ui/clack.d.ts.map +1 -0
  147. package/dist/ui/clack.js +147 -0
  148. package/dist/ui/clack.js.map +1 -0
  149. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  150. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  151. package/dist/ui/components/FlowSpinner.js +8 -0
  152. package/dist/ui/components/FlowSpinner.js.map +1 -0
  153. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  154. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  155. package/dist/ui/components/LogoWordmark.js +17 -0
  156. package/dist/ui/components/LogoWordmark.js.map +1 -0
  157. package/dist/ui/components/PromptPanel.d.ts +5 -0
  158. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  159. package/dist/ui/components/PromptPanel.js +7 -0
  160. package/dist/ui/components/PromptPanel.js.map +1 -0
  161. package/dist/ui/components/SelectField.d.ts +11 -0
  162. package/dist/ui/components/SelectField.d.ts.map +1 -0
  163. package/dist/ui/components/SelectField.js +19 -0
  164. package/dist/ui/components/SelectField.js.map +1 -0
  165. package/dist/ui/confirm.d.ts +2 -1
  166. package/dist/ui/confirm.d.ts.map +1 -1
  167. package/dist/ui/confirm.js +4 -4
  168. package/dist/ui/confirm.js.map +1 -1
  169. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  170. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  171. package/dist/ui/dev/DevDashboard.js +158 -0
  172. package/dist/ui/dev/DevDashboard.js.map +1 -0
  173. package/dist/ui/dev/DevReadyPanelView.d.ts +8 -0
  174. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  175. package/dist/ui/dev/DevReadyPanelView.js +48 -0
  176. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  177. package/dist/ui/fatal.d.ts.map +1 -1
  178. package/dist/ui/fatal.js +5 -11
  179. package/dist/ui/fatal.js.map +1 -1
  180. package/dist/ui/flows/FlowApp.d.ts +8 -0
  181. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  182. package/dist/ui/flows/FlowApp.js +44 -0
  183. package/dist/ui/flows/FlowApp.js.map +1 -0
  184. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  185. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  186. package/dist/ui/flows/prompt-fields.js +92 -0
  187. package/dist/ui/flows/prompt-fields.js.map +1 -0
  188. package/dist/ui/flows/types.d.ts +41 -0
  189. package/dist/ui/flows/types.d.ts.map +1 -0
  190. package/dist/ui/flows/types.js +2 -0
  191. package/dist/ui/flows/types.js.map +1 -0
  192. package/dist/ui/index.d.ts +1 -1
  193. package/dist/ui/index.js +1 -1
  194. package/dist/ui/messages.d.ts +1 -0
  195. package/dist/ui/messages.d.ts.map +1 -1
  196. package/dist/ui/messages.js +48 -8
  197. package/dist/ui/messages.js.map +1 -1
  198. package/dist/ui/progress.d.ts +5 -1
  199. package/dist/ui/progress.d.ts.map +1 -1
  200. package/dist/ui/progress.js +12 -3
  201. package/dist/ui/progress.js.map +1 -1
  202. package/dist/ui/prompts.d.ts +7 -5
  203. package/dist/ui/prompts.d.ts.map +1 -1
  204. package/dist/ui/prompts.js +15 -8
  205. package/dist/ui/prompts.js.map +1 -1
  206. package/dist/ui/runtime/cancel.d.ts +4 -0
  207. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  208. package/dist/ui/runtime/cancel.js +6 -0
  209. package/dist/ui/runtime/cancel.js.map +1 -0
  210. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  211. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  212. package/dist/ui/runtime/command-chrome.js +123 -0
  213. package/dist/ui/runtime/command-chrome.js.map +1 -0
  214. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  215. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  216. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  217. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  218. package/dist/ui/runtime/flow-session.d.ts +4 -0
  219. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  220. package/dist/ui/runtime/flow-session.js +8 -0
  221. package/dist/ui/runtime/flow-session.js.map +1 -0
  222. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  223. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  224. package/dist/ui/runtime/run-clack-flow.js +46 -0
  225. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  226. package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
  227. package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
  228. package/dist/ui/runtime/stdin-after-ink.js +16 -0
  229. package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
  230. package/dist/ui/theme.d.ts +24 -0
  231. package/dist/ui/theme.d.ts.map +1 -0
  232. package/dist/ui/theme.js +34 -0
  233. package/dist/ui/theme.js.map +1 -0
  234. package/dist/valkey-sidecar.d.ts +17 -0
  235. package/dist/valkey-sidecar.d.ts.map +1 -0
  236. package/dist/valkey-sidecar.js +94 -0
  237. package/dist/valkey-sidecar.js.map +1 -0
  238. package/package.json +9 -3
  239. package/src/binary-cache.ts +61 -0
  240. package/src/binary-download-lock.ts +124 -0
  241. package/src/cli-package-version.ts +15 -0
  242. package/src/cli.ts +6 -2
  243. package/src/commands/add.ts +26 -19
  244. package/src/commands/admin.ts +156 -29
  245. package/src/commands/cache.ts +105 -5
  246. package/src/commands/dev.ts +26 -21
  247. package/src/commands/init.ts +473 -247
  248. package/src/commands/link-helpers.ts +30 -29
  249. package/src/compose-local-server-image.ts +68 -0
  250. package/src/compose-rename.ts +5 -8
  251. package/src/config.ts +13 -1
  252. package/src/dev-compose.ts +139 -54
  253. package/src/dev-log-bus.ts +20 -0
  254. package/src/dev-log-filter.ts +42 -0
  255. package/src/dev-ports.ts +22 -11
  256. package/src/dev-ready-panel.ts +80 -0
  257. package/src/dev-session-lock.ts +5 -8
  258. package/src/dev-session.ts +98 -18
  259. package/src/dev-shutdown.ts +29 -17
  260. package/src/dev-task-colors.ts +22 -3
  261. package/src/docker-runtime.ts +1 -1
  262. package/src/engine-push-output.ts +5 -1
  263. package/src/ensure-binary.ts +21 -1
  264. package/src/ensure-component-binaries.ts +71 -0
  265. package/src/ensure-dev-api-config.ts +18 -0
  266. package/src/init-dependency-versions.ts +69 -0
  267. package/src/init-package-json.ts +63 -0
  268. package/src/init-project-detect.ts +99 -0
  269. package/src/postgres-ctl.ts +2 -1
  270. package/src/required-host-components.ts +37 -0
  271. package/src/resolve-api-url.ts +43 -0
  272. package/src/rest-cache-admin.ts +135 -0
  273. package/src/scripts/postinstall.ts +9 -46
  274. package/src/self-host-compose.ts +22 -12
  275. package/src/studio-dev-server.ts +5 -4
  276. package/src/ui/README.md +13 -9
  277. package/src/ui/brand.ts +2 -8
  278. package/src/ui/clack-api.ts +123 -0
  279. package/src/ui/clack.ts +153 -0
  280. package/src/ui/components/FlowSpinner.tsx +15 -0
  281. package/src/ui/components/LogoWordmark.tsx +36 -0
  282. package/src/ui/components/PromptPanel.tsx +17 -0
  283. package/src/ui/components/SelectField.tsx +58 -0
  284. package/src/ui/confirm.ts +5 -4
  285. package/src/ui/dev/DevDashboard.tsx +223 -0
  286. package/src/ui/dev/DevReadyPanelView.tsx +134 -0
  287. package/src/ui/fatal.ts +5 -10
  288. package/src/ui/flows/FlowApp.tsx +69 -0
  289. package/src/ui/flows/prompt-fields.tsx +198 -0
  290. package/src/ui/flows/types.ts +50 -0
  291. package/src/ui/index.ts +1 -1
  292. package/src/ui/messages.ts +50 -8
  293. package/src/ui/progress.ts +14 -3
  294. package/src/ui/prompts.ts +17 -9
  295. package/src/ui/runtime/cancel.ts +6 -0
  296. package/src/ui/runtime/command-chrome.ts +147 -0
  297. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  298. package/src/ui/runtime/flow-session.ts +11 -0
  299. package/src/ui/runtime/run-clack-flow.tsx +65 -0
  300. package/src/ui/runtime/stdin-after-ink.ts +18 -0
  301. package/src/ui/theme.ts +41 -0
  302. package/src/valkey-sidecar.ts +126 -0
  303. package/tests/admin-ensure.test.ts +80 -0
  304. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  305. package/tests/binary-download-lock.test.ts +41 -0
  306. package/tests/cli-help.test.ts +7 -4
  307. package/tests/cli-package-version.test.ts +15 -0
  308. package/tests/command-chrome-coverage.test.ts +149 -0
  309. package/tests/command-chrome-wrap.test.ts +51 -0
  310. package/tests/config.test.ts +27 -4
  311. package/tests/dev-ready-panel.test.ts +37 -0
  312. package/tests/dev-ui.test.ts +42 -2
  313. package/tests/docker-runtime.test.ts +9 -20
  314. package/tests/ensure-dev-api-config.test.ts +22 -0
  315. package/tests/init-dependency-versions.test.ts +62 -0
  316. package/tests/init-package-json.test.ts +18 -0
  317. package/tests/init-project-detect.test.ts +53 -0
  318. package/tests/init.test.ts +30 -3
  319. package/tests/required-host-components.test.ts +51 -0
  320. package/tests/resolve-api-url.test.ts +23 -0
  321. package/tests/rest-cache-admin.test.ts +28 -0
  322. package/tests/runtime-contract.test.ts +12 -3
  323. package/tests/stdin-after-ink.test.ts +8 -0
  324. package/tests/tsconfig.json +8 -2
  325. package/tests/ui-confirm.test.ts +13 -9
  326. package/tests/ui-messages.test.ts +4 -18
  327. package/tsconfig.json +1 -0
  328. package/tsconfig.tsbuildinfo +1 -1
  329. package/vitest.config.ts +3 -0
  330. package/dist/dev-tui.d.ts +0 -24
  331. package/dist/dev-tui.d.ts.map +0 -1
  332. package/dist/dev-tui.js +0 -188
  333. package/dist/dev-tui.js.map +0 -1
  334. package/src/dev-tui.ts +0 -232
@@ -0,0 +1,69 @@
1
+ import { cliPackageVersion } from "./cli-package-version.js"
2
+
3
+ export interface InitDependencyVersions {
4
+ cli: string
5
+ types: string
6
+ }
7
+
8
+ type NpmRegistryPackage = {
9
+ versions?: Record<string, unknown>
10
+ "dist-tags"?: { latest?: string }
11
+ }
12
+
13
+ /** Fallback when the registry is unreachable (offline / tests). */
14
+ export function initDependencyVersionsFallback(): InitDependencyVersions {
15
+ const version = cliPackageVersion()
16
+ return { cli: version, types: version }
17
+ }
18
+
19
+ async function fetchNpmPackage(name: string): Promise<NpmRegistryPackage | null> {
20
+ const url = `https://registry.npmjs.org/${encodeURIComponent(name)}`
21
+ try {
22
+ const resp = await fetch(url, { signal: AbortSignal.timeout(10_000) })
23
+ if (!resp.ok) return null
24
+ return (await resp.json()) as NpmRegistryPackage
25
+ } catch {
26
+ return null
27
+ }
28
+ }
29
+
30
+ function versionPublishedOnNpm(meta: NpmRegistryPackage | null, version: string): boolean {
31
+ return Boolean(meta?.versions?.[version])
32
+ }
33
+
34
+ function latestPublishedOnNpm(meta: NpmRegistryPackage | null): string | null {
35
+ const latest = meta?.["dist-tags"]?.latest
36
+ return typeof latest === "string" && latest.trim() !== "" ? latest.trim() : null
37
+ }
38
+
39
+ function resolvePackageVersion(
40
+ meta: NpmRegistryPackage | null,
41
+ preferred: string,
42
+ ): string {
43
+ if (versionPublishedOnNpm(meta, preferred)) return preferred
44
+ const latest = latestPublishedOnNpm(meta)
45
+ if (latest) return latest
46
+ return preferred
47
+ }
48
+
49
+ /**
50
+ * Pick @supatype/cli and @supatype/types versions that exist on npm.
51
+ * The running CLI may be a local dev build ahead of the registry (e.g. 0.1.3
52
+ * while types is only published at 0.1.1).
53
+ */
54
+ export async function resolveInitDependencyVersions(): Promise<InitDependencyVersions> {
55
+ const runningCli = cliPackageVersion()
56
+ const [cliMeta, typesMeta] = await Promise.all([
57
+ fetchNpmPackage("@supatype/cli"),
58
+ fetchNpmPackage("@supatype/types"),
59
+ ])
60
+
61
+ if (!cliMeta && !typesMeta) {
62
+ return initDependencyVersionsFallback()
63
+ }
64
+
65
+ return {
66
+ cli: resolvePackageVersion(cliMeta, runningCli),
67
+ types: resolvePackageVersion(typesMeta, runningCli),
68
+ }
69
+ }
@@ -0,0 +1,63 @@
1
+ import type { InitDependencyVersions } from "./init-dependency-versions.js"
2
+
3
+ export interface MergePackageJsonOptions {
4
+ projectName: string
5
+ app: { viteDevUrl?: string; mode: string }
6
+ helloFunction: boolean
7
+ }
8
+
9
+ type PackageJson = {
10
+ name?: string
11
+ private?: boolean
12
+ type?: string
13
+ scripts?: Record<string, string>
14
+ dependencies?: Record<string, string>
15
+ devDependencies?: Record<string, string>
16
+ }
17
+
18
+ const CORE_SCRIPTS = {
19
+ dev: "supatype dev",
20
+ push: "supatype push",
21
+ seed: "tsx seed.ts",
22
+ } as const
23
+
24
+ /** Merge Supatype deps/scripts into an existing package.json object. */
25
+ export function mergeSupatypePackageJson(
26
+ pkg: PackageJson,
27
+ opts: MergePackageJsonOptions,
28
+ deps: InitDependencyVersions,
29
+ ): PackageJson {
30
+ const next: PackageJson = { ...pkg }
31
+
32
+ if (!next.name) next.name = opts.projectName
33
+ if (next.private === undefined) next.private = true
34
+ if (!next.type) next.type = "module"
35
+
36
+ const scripts = { ...(next.scripts ?? {}) }
37
+ for (const [key, value] of Object.entries(CORE_SCRIPTS)) {
38
+ if (scripts[key] === undefined) scripts[key] = value
39
+ }
40
+ if (scripts.dev !== CORE_SCRIPTS.dev && scripts["supatype:dev"] === undefined) {
41
+ scripts["supatype:dev"] = CORE_SCRIPTS.dev
42
+ }
43
+ if (opts.app.viteDevUrl && scripts.vite === undefined) {
44
+ scripts.vite = "vite"
45
+ }
46
+ if (opts.helloFunction && scripts.functions === undefined) {
47
+ scripts.functions = "supatype functions serve"
48
+ }
49
+ next.scripts = scripts
50
+
51
+ const dependencies = { ...(next.dependencies ?? {}) }
52
+ if (dependencies["@supatype/cli"] === undefined) dependencies["@supatype/cli"] = `^${deps.cli}`
53
+ if (dependencies["@supatype/types"] === undefined) dependencies["@supatype/types"] = `^${deps.types}`
54
+ next.dependencies = dependencies
55
+
56
+ const devDependencies = { ...(next.devDependencies ?? {}) }
57
+ if (devDependencies.tsx === undefined) devDependencies.tsx = "^4.19.2"
58
+ if (devDependencies.typescript === undefined) devDependencies.typescript = "^5"
59
+ if (opts.app.viteDevUrl && devDependencies.vite === undefined) devDependencies.vite = "^6"
60
+ next.devDependencies = devDependencies
61
+
62
+ return next
63
+ }
@@ -0,0 +1,99 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs"
2
+ import { join } from "node:path"
3
+
4
+ const VITE_CONFIG_NAMES = [
5
+ "vite.config.ts",
6
+ "vite.config.js",
7
+ "vite.config.mjs",
8
+ "vite.config.cjs",
9
+ ] as const
10
+
11
+ const SUPATYPE_CONFIG_NAMES = [
12
+ "supatype.config.ts",
13
+ "supatype.config.js",
14
+ "supatype.config.mjs",
15
+ ] as const
16
+
17
+ export interface DetectedProjectSetup {
18
+ /** Directory has files other than `.git`. */
19
+ hasExistingFiles: boolean
20
+ hasSupatypeConfig: boolean
21
+ hasVite: boolean
22
+ hasViteConfig: boolean
23
+ viteDevUrl: string
24
+ staticDir: string
25
+ /** Human-readable bullets for the init wizard. */
26
+ summaryLines: string[]
27
+ }
28
+
29
+ function hasPackageVite(cwd: string): boolean {
30
+ const pkgPath = join(cwd, "package.json")
31
+ if (!existsSync(pkgPath)) return false
32
+ try {
33
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
34
+ dependencies?: Record<string, string>
35
+ devDependencies?: Record<string, string>
36
+ }
37
+ return Boolean(pkg.devDependencies?.["vite"] ?? pkg.dependencies?.["vite"])
38
+ } catch {
39
+ return false
40
+ }
41
+ }
42
+
43
+ function readVitePort(cwd: string): number {
44
+ for (const name of VITE_CONFIG_NAMES) {
45
+ const path = join(cwd, name)
46
+ if (!existsSync(path)) continue
47
+ const src = readFileSync(path, "utf8")
48
+ const portMatch = src.match(/port:\s*(\d+)/)
49
+ if (portMatch?.[1]) {
50
+ const port = Number.parseInt(portMatch[1], 10)
51
+ if (Number.isInteger(port) && port > 0) return port
52
+ }
53
+ }
54
+ return 5173
55
+ }
56
+
57
+ function detectStaticDir(cwd: string): string {
58
+ if (existsSync(join(cwd, "dist", "index.html"))) return "./dist"
59
+ if (existsSync(join(cwd, "public", "index.html"))) return "./public"
60
+ if (existsSync(join(cwd, "dist"))) return "./dist"
61
+ if (existsSync(join(cwd, "public"))) return "./public"
62
+ return "./public"
63
+ }
64
+
65
+ /** Inspect `cwd` before init prompts — Vite, static dir, existing Supatype config. */
66
+ export function detectProjectSetup(cwd: string): DetectedProjectSetup {
67
+ let entries: string[] = []
68
+ try {
69
+ entries = readdirSync(cwd).filter((entry) => entry !== ".git")
70
+ } catch {
71
+ entries = []
72
+ }
73
+
74
+ const hasExistingFiles = entries.length > 0
75
+ const hasViteConfig = VITE_CONFIG_NAMES.some((name) => existsSync(join(cwd, name)))
76
+ const hasVite = hasViteConfig || hasPackageVite(cwd)
77
+ const viteDevUrl = `http://127.0.0.1:${readVitePort(cwd)}`
78
+ const staticDir = detectStaticDir(cwd)
79
+ const hasSupatypeConfig = SUPATYPE_CONFIG_NAMES.some((name) => existsSync(join(cwd, name)))
80
+
81
+ const summaryLines: string[] = []
82
+ if (hasSupatypeConfig) summaryLines.push("supatype.config already present")
83
+ if (hasViteConfig) summaryLines.push(`Vite config found (${viteDevUrl})`)
84
+ else if (hasVite) summaryLines.push(`Vite dependency in package.json (${viteDevUrl})`)
85
+ if (existsSync(join(cwd, staticDir.replace(/^\.\//, "")))) {
86
+ summaryLines.push(`static assets in ${staticDir}`)
87
+ }
88
+ if (existsSync(join(cwd, "package.json"))) summaryLines.push("package.json present")
89
+
90
+ return {
91
+ hasExistingFiles,
92
+ hasSupatypeConfig,
93
+ hasVite,
94
+ hasViteConfig,
95
+ viteDevUrl,
96
+ staticDir,
97
+ summaryLines,
98
+ }
99
+ }
@@ -148,7 +148,8 @@ export async function isPortInUse(port: number): Promise<boolean> {
148
148
  return new Promise((resolve) => {
149
149
  const server = createServer()
150
150
  server.once("error", (err: NodeJS.ErrnoException) => {
151
- resolve(err.code === "EADDRINUSE")
151
+ // EADDRINUSE — something is listening. EACCES — Windows excluded/reserved port range.
152
+ resolve(err.code === "EADDRINUSE" || err.code === "EACCES")
152
153
  })
153
154
  server.once("listening", () => {
154
155
  server.close(() => resolve(false))
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Which CDN-cached host binaries a project needs (native vs docker).
3
+ *
4
+ * Docker runs Postgres, API, and schema push in Compose, but the CLI still uses a
5
+ * host engine binary for type generation and admin-config refresh after push.
6
+ */
7
+
8
+ import { existsSync } from "node:fs"
9
+ import type { Component } from "./components.js"
10
+ import {
11
+ functionsPathCandidatesFromProject,
12
+ resolveRuntimeProvider,
13
+ type SupatypeProjectConfig,
14
+ } from "./project-config.js"
15
+
16
+ export function requiredHostComponents(
17
+ config: SupatypeProjectConfig,
18
+ cwd: string = process.cwd(),
19
+ ): Component[] {
20
+ const provider = resolveRuntimeProvider(config)
21
+
22
+ if (provider === "docker") {
23
+ return ["engine"]
24
+ }
25
+
26
+ const components: Component[] = ["engine", "server", "postgres"]
27
+ const hasFunctions = functionsPathCandidatesFromProject(config, cwd).some((dir) =>
28
+ existsSync(dir),
29
+ )
30
+ if (hasFunctions) components.push("deno")
31
+ return components
32
+ }
33
+
34
+ export function describeRequiredHostComponents(components: readonly Component[]): string {
35
+ if (components.length === 0) return ""
36
+ return components.join(", ")
37
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Resolve the project API base URL (Kong gateway or direct server) for CLI HTTP calls.
3
+ */
4
+
5
+ import { loadConfig } from "./config.js"
6
+ import { readEnvValue } from "./env-file.js"
7
+ import { serverBaseUrl } from "./project-config.js"
8
+
9
+ const API_URL_ENV_KEYS = [
10
+ "SUPATYPE_URL",
11
+ "SUPATYPE_API_URL",
12
+ "PUBLIC_SUPATYPE_URL",
13
+ "NEXT_PUBLIC_SUPATYPE_URL",
14
+ "SITE_URL",
15
+ "API_EXTERNAL_URL",
16
+ ] as const
17
+
18
+ /** Kong or server origin without a trailing slash. */
19
+ export function resolveProjectApiUrl(cwd: string): string {
20
+ for (const key of API_URL_ENV_KEYS) {
21
+ const value = readEnvValue(cwd, key, "").trim()
22
+ if (value) {
23
+ return value.replace(/\/+$/, "")
24
+ }
25
+ }
26
+
27
+ const kongPort = readEnvValue(cwd, "SUPATYPE_KONG_PORT", "").trim()
28
+ if (kongPort) {
29
+ return `http://localhost:${kongPort}`
30
+ }
31
+
32
+ try {
33
+ const fromConfig = serverBaseUrl(loadConfig(cwd))
34
+ if (fromConfig) {
35
+ return fromConfig.replace(/\/+$/, "")
36
+ }
37
+ } catch {
38
+ // No supatype.config — fall through to PORT default.
39
+ }
40
+
41
+ const port = readEnvValue(cwd, "PORT", "54321").trim() || "54321"
42
+ return `http://localhost:${port}`
43
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Admin REST cache API client for CLI commands.
3
+ */
4
+
5
+ import { readEnvValue } from "./env-file.js"
6
+ import { resolveProjectApiUrl } from "./resolve-api-url.js"
7
+
8
+ export interface RestCacheEntrySummary {
9
+ key: string
10
+ table?: string
11
+ scope?: string
12
+ method?: string
13
+ path?: string
14
+ raw_query?: string
15
+ ttl_seconds: number
16
+ size_bytes: number
17
+ cached_at?: string
18
+ }
19
+
20
+ export interface RestCacheListResponse {
21
+ entries: RestCacheEntrySummary[]
22
+ cursor: string
23
+ }
24
+
25
+ export interface RestCacheDetail extends RestCacheEntrySummary {
26
+ status_code: number
27
+ content_type?: string
28
+ body_preview?: string
29
+ body_json?: unknown
30
+ }
31
+
32
+ function serviceRoleKey(cwd: string): string {
33
+ const key =
34
+ readEnvValue(cwd, "SUPATYPE_SERVICE_ROLE_KEY", "").trim() ||
35
+ readEnvValue(cwd, "SERVICE_ROLE_KEY", "").trim()
36
+ if (!key) {
37
+ throw new Error("SERVICE_ROLE_KEY not found in .env — run supatype dev or supatype keys")
38
+ }
39
+ return key.trim()
40
+ }
41
+
42
+ function adminHeaders(cwd: string): Record<string, string> {
43
+ const key = serviceRoleKey(cwd)
44
+ return {
45
+ Authorization: `Bearer ${key}`,
46
+ apikey: key,
47
+ "Content-Type": "application/json",
48
+ }
49
+ }
50
+
51
+ function formatCacheApiError(
52
+ operation: string,
53
+ status: number,
54
+ body: string,
55
+ baseUrl: string,
56
+ ): string {
57
+ const trimmed = body.trim()
58
+ if (status === 404) {
59
+ return (
60
+ `${operation} failed (404): ${trimmed || "not found"}\n` +
61
+ ` API: ${baseUrl}/admin/v1/cache\n` +
62
+ " Start the stack with `supatype dev`, or check SUPATYPE_URL / PUBLIC_SUPATYPE_URL in .env."
63
+ )
64
+ }
65
+ if (status === 403 && trimmed.includes("rest_cache_not_available")) {
66
+ return "REST server cache is not available on this plan or deployment."
67
+ }
68
+ if (status === 503 && trimmed.includes("valkey")) {
69
+ return "Valkey is not configured — server-side REST cache requires Valkey (supatype dev with docker)."
70
+ }
71
+ return `${operation} failed (${status}): ${trimmed || "(empty body)"}`
72
+ }
73
+
74
+ function normalizeListResponse(data: RestCacheListResponse): RestCacheListResponse {
75
+ return {
76
+ entries: data.entries ?? [],
77
+ cursor: data.cursor ?? "0",
78
+ }
79
+ }
80
+
81
+ export async function listRestCacheEntries(
82
+ cwd: string,
83
+ opts?: { table?: string | undefined; cursor?: string | undefined; limit?: number | undefined },
84
+ ): Promise<RestCacheListResponse> {
85
+ const baseUrl = resolveProjectApiUrl(cwd)
86
+ const params = new URLSearchParams()
87
+ if (opts?.table) params.set("table", opts.table)
88
+ if (opts?.cursor) params.set("cursor", opts.cursor)
89
+ if (opts?.limit !== undefined) params.set("limit", String(opts.limit))
90
+ const qs = params.toString()
91
+ const url = `${baseUrl}/admin/v1/cache${qs ? `?${qs}` : ""}`
92
+ const res = await fetch(url, { headers: adminHeaders(cwd) })
93
+ if (!res.ok) {
94
+ const body = await res.text()
95
+ throw new Error(formatCacheApiError("list cache", res.status, body, baseUrl))
96
+ }
97
+ const data = (await res.json()) as RestCacheListResponse
98
+ return normalizeListResponse(data)
99
+ }
100
+
101
+ export async function getRestCacheEntry(cwd: string, key: string): Promise<RestCacheDetail> {
102
+ const baseUrl = resolveProjectApiUrl(cwd)
103
+ const enc = Buffer.from(key, "utf8").toString("base64url")
104
+ const url = `${baseUrl}/admin/v1/cache/entries/${encodeURIComponent(enc)}`
105
+ const res = await fetch(url, { headers: adminHeaders(cwd) })
106
+ if (!res.ok) {
107
+ const body = await res.text()
108
+ throw new Error(formatCacheApiError("get cache entry", res.status, body, baseUrl))
109
+ }
110
+ return res.json() as Promise<RestCacheDetail>
111
+ }
112
+
113
+ export async function deleteRestCacheEntry(cwd: string, key: string): Promise<void> {
114
+ const baseUrl = resolveProjectApiUrl(cwd)
115
+ const enc = Buffer.from(key, "utf8").toString("base64url")
116
+ const url = `${baseUrl}/admin/v1/cache/entries/${encodeURIComponent(enc)}`
117
+ const res = await fetch(url, { method: "DELETE", headers: adminHeaders(cwd) })
118
+ if (!res.ok) {
119
+ const body = await res.text()
120
+ throw new Error(formatCacheApiError("delete cache entry", res.status, body, baseUrl))
121
+ }
122
+ }
123
+
124
+ export async function flushRestCache(cwd: string, table?: string): Promise<void> {
125
+ const baseUrl = resolveProjectApiUrl(cwd)
126
+ const params = new URLSearchParams()
127
+ if (table) params.set("table", table)
128
+ const qs = params.toString()
129
+ const url = `${baseUrl}/admin/v1/cache${qs ? `?${qs}` : ""}`
130
+ const res = await fetch(url, { method: "DELETE", headers: adminHeaders(cwd) })
131
+ if (!res.ok) {
132
+ const body = await res.text()
133
+ throw new Error(formatCacheApiError("flush cache", res.status, body, baseUrl))
134
+ }
135
+ }
@@ -1,57 +1,20 @@
1
1
  /**
2
- * Postinstall script — downloads component binaries on first install.
2
+ * Postinstall script — @supatype/cli global/project install.
3
3
  *
4
- * Components: supatype-engine, supatype-server, supatype-pg (Postgres), deno.
5
- * Binaries are cached in ~/.supatype/cache/{component}/{version}/.
6
- *
7
- * Failures are non-fatal: an installation message is printed and the user
8
- * can run `supatype update` manually to retry.
4
+ * Host binaries (engine, server, postgres, deno) are downloaded on demand:
5
+ * - native provider: first supatype dev / supatype update
6
+ * - docker provider: Compose images on first supatype dev
9
7
  */
10
8
 
11
- import { download, currentPlatform, fetchAllLatestVersions, type Component } from "../binary-cache.js"
12
-
13
9
  async function main() {
14
- const platform = currentPlatform()
15
-
16
- let versions: Record<Component, string>
17
- try {
18
- console.log("[supatype] Fetching latest component versions...")
19
- versions = await fetchAllLatestVersions()
20
- } catch (err) {
21
- console.error(`[supatype] Failed to fetch latest versions: ${(err as Error).message}`)
22
- console.error("[supatype] Run 'supatype update' to download component binaries.")
23
- return
24
- }
25
-
26
- console.log(`[supatype] Downloading component binaries for ${platform.os}/${platform.arch}...`)
27
-
28
- const components = Object.entries(versions) as [Component, string][]
29
-
30
- let anyFailed = false
31
- for (const [component, version] of components) {
32
- try {
33
- await download(component, version, platform)
34
- } catch (err) {
35
- console.error(`[supatype] Failed to download ${component} v${version}: ${(err as Error).message}`)
36
- anyFailed = true
37
- }
38
- }
39
-
40
- if (anyFailed) {
41
- // npm hides postinstall output unless --foreground-scripts, so don't rely on
42
- // this being seen: the CLI re-attempts the download (with retry) on first use.
43
- console.error(
44
- "[supatype] Some component binaries failed to download. " +
45
- "They will be re-downloaded automatically on first use; " +
46
- "run 'supatype update' to retry now.",
47
- )
48
- } else {
49
- console.log("[supatype] All component binaries downloaded successfully.")
50
- }
10
+ console.log(
11
+ "[supatype] CLI installed. " +
12
+ "Runtime components download automatically on first use " +
13
+ "(supatype dev or supatype update).",
14
+ )
51
15
  }
52
16
 
53
17
  main().catch((err) => {
54
- // Non-fatal — postinstall failures should not break npm install.
55
18
  console.error("[supatype] Postinstall failed:", err)
56
19
  process.exit(0)
57
20
  })
@@ -292,9 +292,7 @@ ${studioService}
292
292
  ? ` - "80:8000"
293
293
  - "443:8443"`
294
294
  : ` - "\${SUPATYPE_KONG_PORT:-18473}:8000"`
295
- const kongTlsDependsOn = tlsEnabled ? "\n - valkey" : ""
296
- const valkeyBlock = tlsEnabled
297
- ? `
295
+ const valkeyBlock = `
298
296
  valkey:
299
297
  image: \${SUPATYPE_VALKEY_IMAGE:-valkey/valkey:8-alpine}
300
298
  command: ["valkey-server", "--appendonly", "yes"]
@@ -303,23 +301,18 @@ ${studioService}
303
301
  volumes:
304
302
  - valkey-data:/data
305
303
  `
306
- : ""
304
+ const kongValkeyDepends = "\n - valkey"
307
305
  const tlsHintComment = tlsEnabled
308
306
  ? ""
309
307
  : ` # HTTPS is off. To enable automatic TLS (Let's Encrypt) for production, set in supatype.config.ts:
310
308
  # server: { mode: "standalone", domain: "your.domain", tls: { email: "you@example.com" } }
311
309
  # then re-run \`supatype self-host compose up -d\`. Kong publishes :80/:443 and provisions certs automatically.
312
310
  `
313
- const volumesBlock = tlsEnabled
314
- ? `volumes:
311
+ const volumesBlock = `volumes:
315
312
  db-data:
316
313
  minio-data:
317
314
  valkey-data:
318
315
  `
319
- : `volumes:
320
- db-data:
321
- minio-data:
322
- `
323
316
 
324
317
  return `# Generated by supatype self-host compose
325
318
  # Kong → supatype-server (unified gateway) → internal PostgREST / storage / etc.
@@ -431,6 +424,7 @@ ${serverPorts} volumes:
431
424
  SUPATYPE_DENO_FUNCTIONS_DIR: /project/functions
432
425
  SUPATYPE_FUNCTIONS_WORKER_URL: http://functions-worker:8001
433
426
  SUPATYPE_CONTROL_PLANE_URL: http://control-plane:8080
427
+ SUPATYPE_VALKEY_ADDR: valkey:6379
434
428
  ${appEnv}
435
429
  GOTRUE_API_HOST: 0.0.0.0
436
430
  GOTRUE_API_PORT: 9999
@@ -450,6 +444,8 @@ ${devLocal ? " STUDIO_OPEN_DEV: \"1\"\n" : ""}
450
444
  depends_on:
451
445
  db:
452
446
  condition: service_healthy
447
+ valkey:
448
+ condition: service_started
453
449
  postgrest:
454
450
  condition: service_started
455
451
  storage:
@@ -492,7 +488,7 @@ ${kongTlsEnv} volumes:
492
488
  ports:
493
489
  ${kongPorts}
494
490
  depends_on:
495
- ${kongDependsOn}${kongTlsDependsOn}
491
+ ${kongDependsOn}${kongValkeyDepends}
496
492
 
497
493
  ${volumesBlock}`
498
494
  }
@@ -594,7 +590,21 @@ export function runDockerCompose(
594
590
  ? { ...process.env, COMPOSE_PROGRESS: "quiet" }
595
591
  : process.env
596
592
  const stdio = options?.quiet ? "pipe" : "inherit"
597
- const result = spawnSync("docker", composeArgs, { stdio, cwd: projectRoot, env })
593
+ const result = spawnSync("docker", composeArgs, {
594
+ stdio,
595
+ cwd: projectRoot,
596
+ env,
597
+ ...(options?.quiet ? { encoding: "utf8" as const } : {}),
598
+ })
599
+ if (options?.quiet && result.status !== 0) {
600
+ const detail = [result.stderr, result.stdout]
601
+ .filter((s): s is string => typeof s === "string" && s.trim() !== "")
602
+ .join("\n")
603
+ .trim()
604
+ if (detail) {
605
+ console.error(`[supatype] docker compose: ${detail}`)
606
+ }
607
+ }
598
608
  return result.status ?? 1
599
609
  }
600
610
 
@@ -2,7 +2,8 @@ import { existsSync } from "node:fs"
2
2
  import { join, resolve } from "node:path"
3
3
  import { ProcessManager } from "./process-manager.js"
4
4
 
5
- const STUDIO_PORT = 3002
5
+ /** Vite dev server port when `overrides.studio` is set. */
6
+ export const STUDIO_DEV_PORT = 3002
6
7
 
7
8
  export interface StudioDevServerOptions {
8
9
  cwd: string
@@ -15,8 +16,8 @@ export interface StudioDevServerOptions {
15
16
  */
16
17
  proxyTarget: string
17
18
  /**
18
- * Public Supatype URL the browser uses. Compose dev: Kong on the host.
19
- * Native dev: Vite dev server (same origin as Studio).
19
+ * Browser API base URL. Must match the Vite dev origin (`http://localhost:3002`)
20
+ * so fetches hit the Vite proxy (SUPATYPE_PROXY_TARGET) and avoid CORS.
20
21
  */
21
22
  viteSupatypeUrl: string
22
23
  /** Vite `base` — `/studio/` when behind Kong at `/studio/`; `/` for native dev on :3002. */
@@ -35,7 +36,7 @@ export function startStudioViteDevServer(opts: StudioDevServerOptions): ProcessM
35
36
  const basePath = opts.basePath ?? "/"
36
37
  return new ProcessManager(
37
38
  process.execPath,
38
- [viteJs, "--port", String(STUDIO_PORT), "--strictPort", "--host"],
39
+ [viteJs, "--port", String(STUDIO_DEV_PORT), "--strictPort", "--host"],
39
40
  {
40
41
  label: "studio",
41
42
  pidDir: opts.pidDir,
package/src/ui/README.md CHANGED
@@ -1,17 +1,21 @@
1
1
  # CLI terminal UI (`packages/cli/src/ui/`)
2
2
 
3
- Shared output layer for `supatype` commands (not `supatype dev` TUI).
3
+ Shared Ink-based design system for the Supatype CLI.
4
4
 
5
5
  | Module | Use for |
6
6
  |--------|---------|
7
- | `messages.ts` | `info` / `warn` / `error` (Clack log in TTY; `[supatype]` in CI), `file`, `step`, `plain` |
8
- | `fatal.ts` | `fatalError`, `reportCliFatal` branded fatals + global catch helper |
9
- | `confirm.ts` | Yes/no prompts (Clack in TTY; respects `--yes` / non-TTY) |
10
- | `progress.ts` | `withSpinner` for long async work |
11
- | `prompts.ts` | Logo, Clack wizard helpers, `promptText` |
7
+ | `theme.ts` | Brand + semantic colours (Ink + plain ANSI) |
8
+ | `messages.ts` | `info` / `warn` / `error` / `success`, `file`, `step`, `plain` |
9
+ | `fatal.ts` | `fatalError`, `reportCliFatal` |
10
+ | `confirm.ts` | Yes/no prompts (Ink overlay in dev, Ink flow in TTY) |
11
+ | `progress.ts` | `withSpinner` + `runCommandChrome` for one-shot commands |
12
+ | `runtime/command-chrome.ts` | Ink shell (logo, logs, spinner) — applied globally via `wrapProgramActionsWithChrome` in `cli.ts` |
13
+ | `prompts.ts` | Logo, `promptText`, `promptPassword`, `ensureNotCancelled` |
14
+ | `clack.ts` | Clack-compatible `p.*` API + `runClackFlow()` for wizards |
15
+ | `flows/` | Ink flow shell + prompt fields |
16
+ | `dev/DevDashboard.tsx` | `supatype dev` task list + log pane + prompt overlay |
12
17
  | `next-steps.ts` | Bulleted follow-up commands |
13
- | `brand.ts` | ANSI colours + logo styling (shared with dev TUI) |
14
18
 
15
- **`supatype dev`** keeps its own alt-screen TUI (`dev-tui.ts`). Do not route dev logs through Clack.
19
+ **Convention:** interactive wizards use `runClackFlow(async (p) => …)` (`init`, `link`, `add domain`); one-shot commands get branded chrome automatically in `cli.ts` via `wrapProgramActionsWithChrome`. **`supatype dev`** keeps a persistent Ink dashboard (excluded from auto-wrap).
16
20
 
17
- **Convention:** interactive multi-step flows use Clack (`init`, `add`, `link`); one-shot commands use `info` + optional `withSpinner`.
21
+ **Plain profile:** when `!isInteractive()` (CI, pipes), helpers fall back to `[supatype]`-prefixed lines.