@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,134 @@
1
+ import React from "react"
2
+ import { Box, Text } from "ink"
3
+ import type { DevReadyPanel } from "../../dev-ready-panel.js"
4
+ import { theme } from "../theme.js"
5
+
6
+ function truncate(text: string, width: number): string {
7
+ if (width <= 0) return ""
8
+ if (text.length <= width) return text
9
+ if (width <= 1) return text.slice(0, width)
10
+ return `${text.slice(0, width - 1)}…`
11
+ }
12
+
13
+ function shortenKey(key: string, width: number): string {
14
+ if (key.length <= width) return key
15
+ if (width <= 12) return truncate(key, width)
16
+ return `${key.slice(0, width - 10)}…${key.slice(-8)}`
17
+ }
18
+
19
+ /** Inner text width inside the round border + horizontal padding. */
20
+ function panelInnerWidth(outerWidth: number): number {
21
+ return Math.max(20, outerWidth - 4)
22
+ }
23
+
24
+ function ServiceLinkRow({
25
+ label,
26
+ url,
27
+ innerWidth,
28
+ labelWidth,
29
+ }: {
30
+ label: string
31
+ url: string
32
+ innerWidth: number
33
+ labelWidth: number
34
+ }): React.ReactElement {
35
+ return (
36
+ <Text wrap="truncate">
37
+ <Text dimColor>{truncate(label.padEnd(labelWidth), labelWidth)}</Text>
38
+ <Text color={theme.info}>{truncate(url, Math.max(8, innerWidth - labelWidth))}</Text>
39
+ </Text>
40
+ )
41
+ }
42
+
43
+ function CompactLinks({ panel, innerWidth }: { panel: DevReadyPanel; innerWidth: number }): React.ReactElement {
44
+ const gateway = panel.links.find((link) => link.label === "API")?.url ?? panel.links[0]?.url ?? ""
45
+ const studio =
46
+ panel.links.find((link) => link.label === "Studio")?.url ??
47
+ panel.links[panel.links.length - 1]?.url ??
48
+ ""
49
+ const otherPaths = panel.links
50
+ .filter((link) => link.label !== "API" && link.label !== "Studio")
51
+ .map((link) => {
52
+ const prefix = `${link.url.split("://")[0] ?? "http"}://`
53
+ const path = link.url.slice(link.url.indexOf("://") + 3)
54
+ const slash = path.indexOf("/")
55
+ return slash >= 0 ? path.slice(slash) : link.url
56
+ })
57
+ .join(", ")
58
+
59
+ return (
60
+ <>
61
+ <Text wrap="truncate">
62
+ <Text dimColor>{"Gateway".padEnd(14)}</Text>
63
+ <Text color={theme.info}>{truncate(gateway, innerWidth - 14)}</Text>
64
+ </Text>
65
+ {otherPaths ? (
66
+ <Text dimColor wrap="truncate">
67
+ {truncate(`Also: ${otherPaths}`, innerWidth)}
68
+ </Text>
69
+ ) : null}
70
+ {studio ? (
71
+ <Text wrap="truncate">
72
+ <Text dimColor>{"Studio".padEnd(14)}</Text>
73
+ <Text color={theme.info}>{truncate(studio, innerWidth - 14)}</Text>
74
+ </Text>
75
+ ) : null}
76
+ </>
77
+ )
78
+ }
79
+
80
+ export function DevReadyPanelView({
81
+ panel,
82
+ width,
83
+ compact = false,
84
+ }: {
85
+ panel: DevReadyPanel
86
+ width: number
87
+ compact?: boolean
88
+ }): React.ReactElement {
89
+ const innerWidth = panelInnerWidth(width)
90
+ const labelWidth = 14
91
+
92
+ return (
93
+ <Box
94
+ flexDirection="column"
95
+ flexShrink={0}
96
+ width={width}
97
+ marginBottom={1}
98
+ borderStyle="round"
99
+ borderColor={theme.brand}
100
+ paddingX={1}
101
+ >
102
+ <Text bold color={theme.brand}>
103
+ {truncate(panel.title, innerWidth)}
104
+ </Text>
105
+ {compact ? (
106
+ <CompactLinks panel={panel} innerWidth={innerWidth} />
107
+ ) : (
108
+ panel.links.map((link) => (
109
+ <ServiceLinkRow
110
+ key={`${link.label}-${link.url}`}
111
+ label={link.label}
112
+ url={link.url}
113
+ innerWidth={innerWidth}
114
+ labelWidth={labelWidth}
115
+ />
116
+ ))
117
+ )}
118
+ {panel.hints?.map((hint) => (
119
+ <Text key={hint} dimColor wrap="truncate">
120
+ {truncate(hint, innerWidth)}
121
+ </Text>
122
+ ))}
123
+ {panel.anonKey ? (
124
+ <>
125
+ <Text dimColor>API keys (local dev)</Text>
126
+ <Text dimColor wrap="truncate">{`anon ${shortenKey(panel.anonKey, innerWidth - 5)}`}</Text>
127
+ {panel.serviceRoleKey ? (
128
+ <Text dimColor wrap="truncate">{`svc ${shortenKey(panel.serviceRoleKey, innerWidth - 5)}`}</Text>
129
+ ) : null}
130
+ </>
131
+ ) : null}
132
+ </Box>
133
+ )
134
+ }
package/src/ui/fatal.ts CHANGED
@@ -1,8 +1,7 @@
1
- import * as p from "@clack/prompts"
2
1
  import type { DockerBrandOptions } from "../docker-runtime.js"
3
2
  import { isInteractive } from "./interactive.js"
4
3
  import { error, plain } from "./messages.js"
5
- import { clack, printLogo } from "./prompts.js"
4
+ import { printLogo } from "./prompts.js"
6
5
 
7
6
  export type FatalOptions = {
8
7
  brand?: DockerBrandOptions
@@ -13,19 +12,15 @@ export type FatalOptions = {
13
12
  export function fatalError(message: string, hints: string[] = [], opts?: FatalOptions): never {
14
13
  if (opts?.brand && isInteractive()) {
15
14
  printLogo()
16
- clack.intro(opts.brand.intro)
15
+ plain(opts.brand.intro)
17
16
  }
18
17
 
19
18
  error(message)
20
19
 
21
20
  if (hints.length > 0) {
22
- if (isInteractive()) {
23
- p.note(hints.join("\n\n"))
24
- } else {
25
- for (const hint of hints) {
26
- plain()
27
- plain(` ${hint}`)
28
- }
21
+ for (const hint of hints) {
22
+ plain()
23
+ plain(` ${hint}`)
29
24
  }
30
25
  }
31
26
 
@@ -0,0 +1,69 @@
1
+ import React, { useEffect, useRef, useState } from "react"
2
+ import { Box } from "ink"
3
+ import { PromptPanel } from "../components/PromptPanel.js"
4
+ import { CLACK_CANCEL } from "../runtime/cancel.js"
5
+ import {
6
+ ActiveFlowPromptView,
7
+ FlowLogPane,
8
+ FlowLogoHeader,
9
+ FlowSpinnerLine,
10
+ } from "./prompt-fields.js"
11
+ import type { ActiveFlowPrompt, FlowController, FlowLogLine } from "./types.js"
12
+
13
+ interface FlowAppProps {
14
+ bind: (controller: FlowController) => void
15
+ }
16
+
17
+ export function FlowApp({ bind }: FlowAppProps): React.ReactElement {
18
+ const [logLines, setLogLines] = useState<FlowLogLine[]>([])
19
+ const [spinner, setSpinner] = useState<string | null>(null)
20
+ const [prompt, setPrompt] = useState<ActiveFlowPrompt | null>(null)
21
+ const [promptResolver, setPromptResolver] = useState<((value: unknown) => void) | null>(null)
22
+ const bound = useRef(false)
23
+
24
+ useEffect(() => {
25
+ if (bound.current) return
26
+ bound.current = true
27
+ const controller: FlowController = {
28
+ appendLog(level, text) {
29
+ setLogLines((lines) => [...lines, { level, text }])
30
+ },
31
+ appendPlain(text) {
32
+ setLogLines((lines) => [...lines, { level: "plain", text }])
33
+ },
34
+ setSpinner(message) {
35
+ setSpinner(message)
36
+ },
37
+ waitForPrompt<T>(spec: ActiveFlowPrompt): Promise<T> {
38
+ return new Promise<T>((resolve) => {
39
+ setLogLines([])
40
+ setSpinner(null)
41
+ setPrompt(spec)
42
+ setPromptResolver(() => (value: unknown) => {
43
+ setPrompt(null)
44
+ setPromptResolver(null)
45
+ resolve(value as T)
46
+ })
47
+ })
48
+ },
49
+ }
50
+ bind(controller)
51
+ }, [bind])
52
+
53
+ return (
54
+ <Box flexDirection="column" paddingX={1}>
55
+ <FlowLogoHeader />
56
+ {prompt ? null : <FlowLogPane lines={logLines} />}
57
+ {spinner ? <FlowSpinnerLine message={spinner} /> : null}
58
+ {prompt && promptResolver ? (
59
+ <PromptPanel>
60
+ <ActiveFlowPromptView
61
+ prompt={prompt}
62
+ onSubmit={(value) => promptResolver(value)}
63
+ onCancel={() => promptResolver(CLACK_CANCEL)}
64
+ />
65
+ </PromptPanel>
66
+ ) : null}
67
+ </Box>
68
+ )
69
+ }
@@ -0,0 +1,198 @@
1
+ import React, { useState } from "react"
2
+ import { Box, Text, useInput } from "ink"
3
+ import TextInput from "ink-text-input"
4
+ import { FlowSpinner } from "../components/FlowSpinner.js"
5
+ import { LogoWordmark } from "../components/LogoWordmark.js"
6
+ import { SelectField } from "../components/SelectField.js"
7
+ import { theme } from "../theme.js"
8
+ import type { ActiveFlowPrompt, FlowLogLine } from "./types.js"
9
+
10
+ const TASK_COL_WIDTH = 22
11
+
12
+ function flowLogLineProps(level: FlowLogLine["level"]): { color?: string; dimColor?: boolean } {
13
+ if (level === "plain") return { dimColor: true }
14
+ if (level === "warn") return { color: theme.warn }
15
+ if (level === "error") return { color: theme.error }
16
+ if (level === "success") return { color: theme.success }
17
+ return { color: theme.info }
18
+ }
19
+
20
+ export function FlowLogoHeader(): React.ReactElement {
21
+ return <LogoWordmark />
22
+ }
23
+
24
+ export function FlowLogPane({ lines }: { lines: FlowLogLine[] }): React.ReactElement {
25
+ return (
26
+ <Box flexDirection="column" marginBottom={1}>
27
+ {lines.map((line, index) => (
28
+ <Text key={`${index}-${line.text.slice(0, 24)}`} {...flowLogLineProps(line.level)}>
29
+ {line.text}
30
+ </Text>
31
+ ))}
32
+ </Box>
33
+ )
34
+ }
35
+
36
+ export function FlowSpinnerLine({ message }: { message: string }): React.ReactElement {
37
+ return <FlowSpinner message={message} />
38
+ }
39
+
40
+ interface ConfirmPromptProps {
41
+ spec: Extract<ActiveFlowPrompt, { kind: "confirm" }>
42
+ onSubmit: (value: boolean) => void
43
+ }
44
+
45
+ export function ConfirmPrompt({ spec, onSubmit }: ConfirmPromptProps): React.ReactElement {
46
+ const [value, setValue] = useState(spec.initialValue)
47
+
48
+ useInput((input, key) => {
49
+ if (input === "y" || input === "Y") {
50
+ onSubmit(true)
51
+ return
52
+ }
53
+ if (input === "n" || input === "N") {
54
+ onSubmit(false)
55
+ return
56
+ }
57
+ if (key.leftArrow || input === "h") setValue(false)
58
+ if (key.rightArrow || input === "l") setValue(true)
59
+ if (key.return) onSubmit(value)
60
+ })
61
+
62
+ return (
63
+ <Box flexDirection="column">
64
+ <Text bold color={theme.brand}>
65
+ {spec.message}
66
+ </Text>
67
+ <Text>
68
+ {value ? (
69
+ <Text color={theme.brand} bold>
70
+ Yes
71
+ </Text>
72
+ ) : (
73
+ <Text dimColor>Yes</Text>
74
+ )}
75
+ <Text dimColor> / </Text>
76
+ {!value ? (
77
+ <Text color={theme.brand} bold>
78
+ No
79
+ </Text>
80
+ ) : (
81
+ <Text dimColor>No</Text>
82
+ )}
83
+ <Text dimColor> — y/n, ←/→, enter</Text>
84
+ </Text>
85
+ </Box>
86
+ )
87
+ }
88
+
89
+ interface TextPromptProps {
90
+ spec: Extract<ActiveFlowPrompt, { kind: "text" }>
91
+ onSubmit: (value: string) => void
92
+ onCancel: () => void
93
+ }
94
+
95
+ export function TextPrompt({ spec, onSubmit, onCancel }: TextPromptProps): React.ReactElement {
96
+ const [value, setValue] = useState(spec.defaultValue ?? "")
97
+ const [error, setError] = useState<string | undefined>()
98
+
99
+ useInput((_input, key) => {
100
+ if (key.escape) onCancel()
101
+ })
102
+
103
+ return (
104
+ <Box flexDirection="column">
105
+ <Text bold color={theme.brand}>
106
+ {spec.message}
107
+ </Text>
108
+ <Box>
109
+ <Text color={theme.brand}>› </Text>
110
+ <TextInput
111
+ value={value}
112
+ {...(spec.placeholder !== undefined ? { placeholder: spec.placeholder } : {})}
113
+ onChange={setValue}
114
+ onSubmit={(submitted) => {
115
+ const err = spec.validate?.(submitted)
116
+ if (err) {
117
+ setError(err)
118
+ return
119
+ }
120
+ onSubmit(submitted)
121
+ }}
122
+ />
123
+ </Box>
124
+ {error ? <Text color={theme.error}>{error}</Text> : null}
125
+ <Text dimColor>enter submit · esc cancel</Text>
126
+ </Box>
127
+ )
128
+ }
129
+
130
+ interface PasswordPromptProps {
131
+ spec: Extract<ActiveFlowPrompt, { kind: "password" }>
132
+ onSubmit: (value: string) => void
133
+ onCancel: () => void
134
+ }
135
+
136
+ export function PasswordPrompt({ spec, onSubmit, onCancel }: PasswordPromptProps): React.ReactElement {
137
+ const [value, setValue] = useState("")
138
+
139
+ useInput((_input, key) => {
140
+ if (key.escape) onCancel()
141
+ })
142
+
143
+ return (
144
+ <Box flexDirection="column">
145
+ <Text bold color={theme.brand}>
146
+ {spec.message}
147
+ </Text>
148
+ <Box>
149
+ <Text color={theme.brand}>› </Text>
150
+ <TextInput mask="*" value={value} onChange={setValue} onSubmit={onSubmit} />
151
+ </Box>
152
+ <Text dimColor>enter submit · esc cancel</Text>
153
+ </Box>
154
+ )
155
+ }
156
+
157
+ interface SelectPromptProps {
158
+ spec: Extract<ActiveFlowPrompt, { kind: "select" }>
159
+ onSubmit: (value: string) => void
160
+ onCancel: () => void
161
+ }
162
+
163
+ export function SelectPrompt({ spec, onSubmit, onCancel }: SelectPromptProps): React.ReactElement {
164
+ return (
165
+ <SelectField
166
+ message={spec.message}
167
+ options={spec.options}
168
+ {...(spec.initialValue !== undefined ? { initialValue: spec.initialValue } : {})}
169
+ onSubmit={onSubmit}
170
+ onCancel={onCancel}
171
+ />
172
+ )
173
+ }
174
+
175
+ export function ActiveFlowPromptView({
176
+ prompt,
177
+ onSubmit,
178
+ onCancel,
179
+ }: {
180
+ prompt: ActiveFlowPrompt
181
+ onSubmit: (value: unknown) => void
182
+ onCancel: () => void
183
+ }): React.ReactElement {
184
+ switch (prompt.kind) {
185
+ case "confirm":
186
+ return <ConfirmPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} />
187
+ case "text":
188
+ return <TextPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
189
+ case "password":
190
+ return <PasswordPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
191
+ case "select":
192
+ return <SelectPrompt spec={prompt} onSubmit={(v) => onSubmit(v)} onCancel={onCancel} />
193
+ default:
194
+ return <Text>Unsupported prompt</Text>
195
+ }
196
+ }
197
+
198
+ export { TASK_COL_WIDTH }
@@ -0,0 +1,50 @@
1
+ import type { ReactNode } from "react"
2
+ import type { DevSelectOption } from "../runtime/dev-prompt-queue.js"
3
+
4
+ export type FlowLogLevel = "info" | "warn" | "error" | "success" | "plain"
5
+
6
+ export interface FlowLogLine {
7
+ level: FlowLogLevel
8
+ text: string
9
+ }
10
+
11
+ export type ActiveFlowPrompt =
12
+ | {
13
+ kind: "confirm"
14
+ message: string
15
+ initialValue: boolean
16
+ }
17
+ | {
18
+ kind: "text"
19
+ message: string
20
+ defaultValue?: string
21
+ placeholder?: string
22
+ validate?: (value: string) => string | undefined
23
+ }
24
+ | {
25
+ kind: "password"
26
+ message: string
27
+ }
28
+ | {
29
+ kind: "select"
30
+ message: string
31
+ options: DevSelectOption[]
32
+ initialValue?: string
33
+ }
34
+
35
+ export interface FlowPromptHandle<T = unknown> {
36
+ resolve: (value: T) => void
37
+ reject: (err: Error) => void
38
+ }
39
+
40
+ export interface FlowController {
41
+ appendLog(level: FlowLogLevel, text: string): void
42
+ appendPlain(text: string): void
43
+ setSpinner(message: string | null): void
44
+ waitForPrompt<T>(spec: ActiveFlowPrompt): Promise<T>
45
+ }
46
+
47
+ export type FlowShellProps = {
48
+ controller: FlowController
49
+ children?: ReactNode
50
+ }
package/src/ui/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./brand.js"
1
+ export * from "./theme.js"
2
2
  export * from "./interactive.js"
3
3
  export * from "./messages.js"
4
4
  export * from "./fatal.js"
@@ -1,27 +1,47 @@
1
- import * as p from "@clack/prompts"
1
+ import { getActiveFlowApi } from "./runtime/flow-session.js"
2
2
  import { isInteractive } from "./interactive.js"
3
+ import { plainError, plainInfo, plainSuccess, plainWarn } from "./theme.js"
3
4
 
4
5
  export const SUPATYPE_PREFIX = "[supatype]"
5
6
 
7
+ function writePlain(message = ""): void {
8
+ console.log(message)
9
+ }
10
+
6
11
  export function info(message: string): void {
12
+ const api = getActiveFlowApi()
13
+ if (api) {
14
+ api.log.info(message)
15
+ return
16
+ }
7
17
  if (isInteractive()) {
8
- p.log.info(message)
18
+ writePlain(plainInfo(message))
9
19
  return
10
20
  }
11
- console.log(`${SUPATYPE_PREFIX} ${message}`)
21
+ writePlain(`${SUPATYPE_PREFIX} ${message}`)
12
22
  }
13
23
 
14
24
  export function warn(message: string): void {
25
+ const api = getActiveFlowApi()
26
+ if (api) {
27
+ api.log.warn(message)
28
+ return
29
+ }
15
30
  if (isInteractive()) {
16
- p.log.warn(message)
31
+ writePlain(plainWarn(message))
17
32
  return
18
33
  }
19
34
  console.warn(`${SUPATYPE_PREFIX} ${message}`)
20
35
  }
21
36
 
22
37
  export function error(message: string): void {
38
+ const api = getActiveFlowApi()
39
+ if (api) {
40
+ api.log.error(message)
41
+ return
42
+ }
23
43
  if (isInteractive()) {
24
- p.log.error(message)
44
+ writePlain(plainError(message))
25
45
  return
26
46
  }
27
47
  console.error(`${SUPATYPE_PREFIX} ${message}`)
@@ -29,15 +49,37 @@ export function error(message: string): void {
29
49
 
30
50
  /** Unprefixed line (tables, diff output, blank lines). */
31
51
  export function plain(message = ""): void {
32
- console.log(message)
52
+ const api = getActiveFlowApi()
53
+ if (api) {
54
+ if (message === "") {
55
+ api.note("")
56
+ return
57
+ }
58
+ api.note(message)
59
+ return
60
+ }
61
+ writePlain(message)
33
62
  }
34
63
 
35
64
  export function step(title: string): void {
36
- console.log(`\n${title}`)
65
+ plain(`\n${title}`)
37
66
  }
38
67
 
39
68
  export type FileAction = "created" | "updated" | "skipped" | "removed" | "wrote"
40
69
 
41
70
  export function file(action: FileAction, rel: string): void {
42
- console.log(` ${action} ${rel}`)
71
+ plain(` ${action} ${rel}`)
72
+ }
73
+
74
+ export function success(message: string): void {
75
+ const api = getActiveFlowApi()
76
+ if (api) {
77
+ api.log.success(message)
78
+ return
79
+ }
80
+ if (isInteractive()) {
81
+ writePlain(plainSuccess(message))
82
+ return
83
+ }
84
+ plain(`${SUPATYPE_PREFIX} ${message}`)
43
85
  }
@@ -1,9 +1,15 @@
1
- import * as p from "@clack/prompts"
1
+ /**
2
+ * Progress UI for one-shot commands — Ink spinner inside command chrome.
3
+ */
4
+
5
+ import { getActiveFlowApi } from "./runtime/flow-session.js"
2
6
  import { isInteractive } from "./interactive.js"
3
7
  import { info } from "./messages.js"
8
+ import { runCommandChrome } from "./runtime/command-chrome.js"
4
9
 
5
10
  /**
6
- * Run an async task with a Clack spinner (TTY) or a plain status line (CI/pipes).
11
+ * Show progress with the branded Ink spinner when command chrome is active
12
+ * (or mount chrome for a standalone spinner).
7
13
  */
8
14
  export async function withSpinner<T>(
9
15
  message: string,
@@ -15,7 +21,12 @@ export async function withSpinner<T>(
15
21
  return task()
16
22
  }
17
23
 
18
- const spinner = p.spinner()
24
+ const api = getActiveFlowApi()
25
+ if (!api) {
26
+ return runCommandChrome(() => withSpinner(message, task, doneMessage))
27
+ }
28
+
29
+ const spinner = api.spinner()
19
30
  spinner.start(message)
20
31
  try {
21
32
  const result = await task()
package/src/ui/prompts.ts CHANGED
@@ -1,7 +1,7 @@
1
- import * as p from "@clack/prompts"
2
- import { SUPATYPE_ASCII_LOGO_WORDMARK, colorLogoLines } from "../dev-logo.js"
3
- import { plain } from "./messages.js"
1
+ import { colorLogoLines, SUPATYPE_ASCII_LOGO_WORDMARK } from "../dev-logo.js"
4
2
  import { isInteractive } from "./interactive.js"
3
+ import { plain } from "./messages.js"
4
+ import { CLACK_CANCEL, isCancel, p } from "./clack.js"
5
5
 
6
6
  /** Print the coloured Supatype ASCII wordmark at the top of an interactive command. */
7
7
  export function printLogo(): void {
@@ -11,17 +11,16 @@ export function printLogo(): void {
11
11
  }
12
12
 
13
13
  /**
14
- * Unwrap a clack prompt result, exiting cleanly when the user cancels (Ctrl-C).
14
+ * Unwrap a prompt result, exiting cleanly when the user cancels (Ctrl-C).
15
15
  */
16
- export function ensureNotCancelled<T>(value: T | symbol, cancelMessage = "Cancelled."): T {
17
- if (p.isCancel(value)) {
16
+ export function ensureNotCancelled<T>(value: T | typeof CLACK_CANCEL, cancelMessage = "Cancelled."): T {
17
+ if (isCancel(value)) {
18
18
  p.cancel(cancelMessage)
19
- process.exit(0)
20
19
  }
21
20
  return value as T
22
21
  }
23
22
 
24
- /** Single-line text input via Clack (TTY only). */
23
+ /** Single-line text input (TTY only). */
25
24
  export async function promptText(
26
25
  message: string,
27
26
  opts?: { defaultValue?: string; placeholder?: string },
@@ -37,4 +36,13 @@ export async function promptText(
37
36
  return ensureNotCancelled(value).trim()
38
37
  }
39
38
 
40
- export { p as clack }
39
+ /** Masked password input (TTY only). */
40
+ export async function promptPassword(message: string): Promise<string> {
41
+ if (!isInteractive()) {
42
+ throw new Error(`Cannot prompt for "${message}" in non-interactive mode.`)
43
+ }
44
+ const value = await p.password({ message })
45
+ return ensureNotCancelled(value).trim()
46
+ }
47
+
48
+ export { p, clack, isCancel, CLACK_CANCEL, runClackFlow } from "./clack.js"
@@ -0,0 +1,6 @@
1
+ /** Clack-compatible cancel sentinel for Ink prompt flows. */
2
+ export const CLACK_CANCEL = Symbol("supatype:cancel")
3
+
4
+ export function isCancel(value: unknown): value is typeof CLACK_CANCEL {
5
+ return value === CLACK_CANCEL
6
+ }