@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,147 @@
1
+ /**
2
+ * Branded Ink chrome for one-shot CLI commands (push, diff, adopt, …).
3
+ * Reuses the flow shell: figlet logo, coloured log pane, ink-spinner, prompts.
4
+ */
5
+
6
+ import type { Command } from "commander"
7
+ import type { ClackApi } from "../clack-api.js"
8
+ import { getActiveFlowApi } from "./flow-session.js"
9
+ import { runClackFlow } from "../clack.js"
10
+ import { isInteractive } from "../interactive.js"
11
+ import {
12
+ filterCommandLogLine,
13
+ formatConsoleArgs,
14
+ stripAnsi,
15
+ } from "../../dev-log-filter.js"
16
+
17
+ function patchConsoleForCommand(api: ClackApi): () => void {
18
+ const original = {
19
+ log: console.log,
20
+ warn: console.warn,
21
+ error: console.error,
22
+ }
23
+
24
+ const emitLine = (raw: string, stream: "log" | "warn" | "error"): void => {
25
+ if (!filterCommandLogLine(raw)) return
26
+ const t = stripAnsi(raw).trim()
27
+ if (!t) return
28
+ const stripped = t.replace(/^\[supatype\]\s*/, "")
29
+
30
+ if (stream === "error" || stripped.startsWith("✗")) {
31
+ api.log.error(stripped.replace(/^✗\s*/, ""))
32
+ return
33
+ }
34
+ if (stream === "warn" || stripped.startsWith("⚠")) {
35
+ api.log.warn(stripped.replace(/^⚠\s*/, ""))
36
+ return
37
+ }
38
+ if (stripped.startsWith("✓")) {
39
+ api.log.success(stripped.replace(/^✓\s*/, ""))
40
+ return
41
+ }
42
+ if (/\[supatype\]/.test(raw)) {
43
+ api.log.info(stripped)
44
+ return
45
+ }
46
+ api.note(t)
47
+ }
48
+
49
+ const wrap =
50
+ (stream: "log" | "warn" | "error") =>
51
+ (...args: unknown[]): void => {
52
+ for (const part of formatConsoleArgs(args).split(/\r?\n/)) {
53
+ emitLine(part, stream)
54
+ }
55
+ }
56
+
57
+ console.log = wrap("log")
58
+ console.warn = wrap("warn")
59
+ console.error = wrap("error")
60
+
61
+ return () => {
62
+ console.log = original.log
63
+ console.warn = original.warn
64
+ console.error = original.error
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Run a command body inside the branded Ink shell (logo + logs + spinner).
70
+ * Falls back to plain stdout when not interactive.
71
+ */
72
+ export async function runCommandChrome<T>(fn: () => Promise<T>): Promise<T> {
73
+ if (!isInteractive() || isCommandChromeActive()) {
74
+ return fn()
75
+ }
76
+
77
+ return runClackFlow(async (api) => {
78
+ const restore = patchConsoleForCommand(api)
79
+ try {
80
+ return await fn()
81
+ } finally {
82
+ restore()
83
+ }
84
+ })
85
+ }
86
+
87
+ /** True while `runCommandChrome` / `runClackFlow` is active. */
88
+ export function isCommandChromeActive(): boolean {
89
+ return getActiveFlowApi() !== null
90
+ }
91
+
92
+ /** Commands that mount their own Ink UI — do not wrap with command chrome. */
93
+ const WIZARD_COMMAND_PATHS = new Set(["dev", "init", "link"])
94
+
95
+ /** Prefixes for subcommands that call `runClackFlow` internally. */
96
+ const WIZARD_COMMAND_PREFIXES = ["add "]
97
+
98
+ /** Long-running or stdio-inherit commands where chrome would fight subprocess output. */
99
+ const STREAMING_COMMAND_PATHS = new Set([
100
+ "logs",
101
+ "pg psql",
102
+ "functions serve",
103
+ "self-host compose logs",
104
+ ])
105
+
106
+ type CommandWithAction = Command & {
107
+ parent?: Command | null
108
+ _actionHandler?: ((args: unknown) => unknown) | null
109
+ }
110
+
111
+ /** Whether a registered command path should skip automatic command chrome. */
112
+ export function shouldExcludeCommandChrome(commandPath: string): boolean {
113
+ if (WIZARD_COMMAND_PATHS.has(commandPath)) return true
114
+ if (WIZARD_COMMAND_PREFIXES.some((prefix) => commandPath.startsWith(prefix))) return true
115
+ if (STREAMING_COMMAND_PATHS.has(commandPath)) return true
116
+ return false
117
+ }
118
+
119
+ function commandPath(cmd: CommandWithAction, root: Command): string {
120
+ const segments: string[] = []
121
+ let current: CommandWithAction | null = cmd
122
+ while (current && current !== root) {
123
+ segments.unshift(current.name())
124
+ current = current.parent ?? null
125
+ }
126
+ return segments.join(" ")
127
+ }
128
+
129
+ function wrapCommandAction(cmd: CommandWithAction, root: Command): void {
130
+ const path = commandPath(cmd, root)
131
+ const handler = cmd._actionHandler
132
+ if (!handler || shouldExcludeCommandChrome(path)) return
133
+
134
+ cmd._actionHandler = (args: unknown) =>
135
+ runCommandChrome(() => Promise.resolve(handler(args)))
136
+ }
137
+
138
+ /** Wrap every leaf command action with branded Ink chrome (see exclusions above). */
139
+ export function wrapProgramActionsWithChrome(program: Command): void {
140
+ const walk = (cmd: CommandWithAction): void => {
141
+ wrapCommandAction(cmd, program)
142
+ for (const sub of cmd.commands) {
143
+ walk(sub as CommandWithAction)
144
+ }
145
+ }
146
+ walk(program as CommandWithAction)
147
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Imperative prompt queue for the dev dashboard overlay.
3
+ */
4
+
5
+ import { CLACK_CANCEL } from "./cancel.js"
6
+
7
+ export type DevPromptKind = "confirm" | "text" | "password" | "select"
8
+
9
+ export interface DevSelectOption<T extends string = string> {
10
+ value: T
11
+ label: string
12
+ hint?: string
13
+ }
14
+
15
+ export interface DevPromptRequest<T = unknown> {
16
+ id: number
17
+ kind: DevPromptKind
18
+ message: string
19
+ initialValue?: string | boolean
20
+ defaultValue?: string
21
+ placeholder?: string
22
+ mask?: boolean
23
+ options?: DevSelectOption[]
24
+ validate?: (value: string) => string | undefined
25
+ resolve: (value: T | typeof CLACK_CANCEL) => void
26
+ }
27
+
28
+ let nextId = 0
29
+ let active: DevPromptRequest | null = null
30
+ const waiters: DevPromptRequest[] = []
31
+ const listeners = new Set<() => void>()
32
+
33
+ function notify(): void {
34
+ for (const listener of listeners) listener()
35
+ }
36
+
37
+ export function subscribeDevPrompts(listener: () => void): () => void {
38
+ listeners.add(listener)
39
+ return () => listeners.delete(listener)
40
+ }
41
+
42
+ export function getActiveDevPrompt(): DevPromptRequest | null {
43
+ return active
44
+ }
45
+
46
+ export function enqueueDevPrompt<T>(spec: Omit<DevPromptRequest<T>, "id" | "resolve">): Promise<T | typeof CLACK_CANCEL> {
47
+ return new Promise((resolve) => {
48
+ const request: DevPromptRequest<T> = {
49
+ ...spec,
50
+ id: nextId++,
51
+ resolve: resolve as DevPromptRequest["resolve"],
52
+ }
53
+ waiters.push(request as DevPromptRequest)
54
+ if (!active) {
55
+ active = waiters.shift() ?? null
56
+ }
57
+ notify()
58
+ })
59
+ }
60
+
61
+ export function resolveDevPrompt(value: unknown): void {
62
+ if (!active) return
63
+ const current = active
64
+ active = waiters.shift() ?? null
65
+ current.resolve(value)
66
+ notify()
67
+ }
68
+
69
+ export function clearDevPromptQueue(): void {
70
+ while (active) {
71
+ active.resolve(CLACK_CANCEL)
72
+ active = waiters.shift() ?? null
73
+ }
74
+ for (const pending of waiters.splice(0)) {
75
+ pending.resolve(CLACK_CANCEL)
76
+ }
77
+ notify()
78
+ }
79
+
80
+ /** @internal Tests */
81
+ export function resetDevPromptQueueForTests(): void {
82
+ clearDevPromptQueue()
83
+ nextId = 0
84
+ }
@@ -0,0 +1,11 @@
1
+ import type { ClackApi } from "../clack-api.js"
2
+
3
+ let activeFlowApi: ClackApi | null = null
4
+
5
+ export function setActiveFlowApi(api: ClackApi | null): void {
6
+ activeFlowApi = api
7
+ }
8
+
9
+ export function getActiveFlowApi(): ClackApi | null {
10
+ return activeFlowApi
11
+ }
@@ -0,0 +1,65 @@
1
+ import React, { useRef } from "react"
2
+ import { render, type Instance } from "ink"
3
+ import { isInteractive } from "../interactive.js"
4
+ import { createClackApi, type ClackApi } from "../clack-api.js"
5
+ import { FlowApp } from "../flows/FlowApp.js"
6
+ import { restoreStdinAfterInk } from "./stdin-after-ink.js"
7
+
8
+ interface RunClackFlowRootProps {
9
+ run: (api: ClackApi) => Promise<unknown>
10
+ onComplete: (value: unknown) => void
11
+ onError: (err: unknown) => void
12
+ }
13
+
14
+ function RunClackFlowRoot({ run, onComplete, onError }: RunClackFlowRootProps): React.ReactElement {
15
+ const started = useRef(false)
16
+
17
+ return (
18
+ <FlowApp
19
+ bind={(controller) => {
20
+ if (started.current) return
21
+ started.current = true
22
+ void run(createClackApi(controller))
23
+ .then(onComplete)
24
+ .catch(onError)
25
+ }}
26
+ />
27
+ )
28
+ }
29
+
30
+ /** Mount a single Ink flow session (init wizard, standalone confirm, etc.). */
31
+ export async function runClackFlow<T>(run: (api: ClackApi) => Promise<T>): Promise<T> {
32
+ if (!isInteractive()) {
33
+ throw new Error("Interactive Ink flow requires a TTY.")
34
+ }
35
+
36
+ return new Promise<T>((resolve, reject) => {
37
+ const instance = render(
38
+ <RunClackFlowRoot
39
+ run={run as (api: ClackApi) => Promise<unknown>}
40
+ onComplete={(value) => {
41
+ teardownInk(instance)
42
+ resolve(value as T)
43
+ }}
44
+ onError={(err) => {
45
+ teardownInk(instance)
46
+ reject(err)
47
+ }}
48
+ />,
49
+ { patchConsole: false },
50
+ )
51
+ })
52
+ }
53
+
54
+ function teardownInk(instance: Instance): void {
55
+ try {
56
+ instance.clear()
57
+ } catch {
58
+ // ignore — terminal may already be reset
59
+ }
60
+ instance.unmount()
61
+ instance.cleanup()
62
+ restoreStdinAfterInk()
63
+ }
64
+
65
+ export { CLACK_CANCEL } from "./cancel.js"
@@ -0,0 +1,18 @@
1
+ /** Restore stdin after Ink unmount so one-shot commands (init, link) can exit cleanly. */
2
+ export function restoreStdinAfterInk(): void {
3
+ if (!process.stdin.isTTY) return
4
+
5
+ const stdin = process.stdin as NodeJS.ReadStream & {
6
+ isRaw?: boolean
7
+ setRawMode?(mode: boolean): void
8
+ }
9
+
10
+ try {
11
+ if (stdin.isRaw) stdin.setRawMode?.(false)
12
+ } catch {
13
+ // ignore — stdin may already be restored
14
+ }
15
+
16
+ stdin.resume()
17
+ process.stdout.write("\n")
18
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Supatype terminal design tokens — shared by Ink components and plain CI output.
3
+ */
4
+
5
+ export const RESET = "\x1b[0m"
6
+ export const DIM = "\x1b[2m"
7
+ export const BOLD = "\x1b[1m"
8
+
9
+ export const theme = {
10
+ brand: "magenta" as const,
11
+ brandHex: "#c084fc",
12
+ info: "cyan" as const,
13
+ warn: "yellow" as const,
14
+ error: "red" as const,
15
+ success: "green" as const,
16
+ dim: "gray" as const,
17
+ muted: "gray" as const,
18
+ } as const
19
+
20
+ /** Legacy ANSI exports used by dev-logo / dev-task-colors. */
21
+ export const BRAND_COLOR = "\x1b[35m"
22
+
23
+ export function brandStyle(text: string): string {
24
+ return `${BRAND_COLOR}${BOLD}${text}${RESET}`
25
+ }
26
+
27
+ export function plainInfo(message: string): string {
28
+ return `${theme.info === "cyan" ? "\x1b[36m" : ""}ℹ ${message}${RESET}`
29
+ }
30
+
31
+ export function plainWarn(message: string): string {
32
+ return `\x1b[33m⚠ ${message}${RESET}`
33
+ }
34
+
35
+ export function plainError(message: string): string {
36
+ return `\x1b[31m✗ ${message}${RESET}`
37
+ }
38
+
39
+ export function plainSuccess(message: string): string {
40
+ return `\x1b[32m✓ ${message}${RESET}`
41
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Lightweight Valkey container for native `supatype dev` (REST response cache).
3
+ */
4
+
5
+ import { spawnSync } from "node:child_process"
6
+ import { createConnection } from "node:net"
7
+
8
+ const DEFAULT_HOST = "127.0.0.1"
9
+ const DEFAULT_PORT = 6379
10
+ const IMAGE = "valkey/valkey:8-alpine"
11
+
12
+ function containerName(projectName: string): string {
13
+ const safe = projectName.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 48)
14
+ return `supatype-valkey-dev-${safe}`
15
+ }
16
+
17
+ function dockerAvailable(): boolean {
18
+ const r = spawnSync("docker", ["info"], { stdio: "ignore" })
19
+ return r.status === 0
20
+ }
21
+
22
+ function isAddrReachable(host: string, port: number, timeoutMs = 800): boolean {
23
+ try {
24
+ const r = spawnSync(
25
+ process.execPath,
26
+ [
27
+ "-e",
28
+ `const n=require('net');const s=n.createConnection({host:${JSON.stringify(host)},port:${port}});` +
29
+ `s.on('connect',()=>{s.end();process.exit(0)});s.on('error',()=>process.exit(1));` +
30
+ `setTimeout(()=>process.exit(1),${timeoutMs})`,
31
+ ],
32
+ { stdio: "ignore" },
33
+ )
34
+ return r.status === 0
35
+ } catch {
36
+ return false
37
+ }
38
+ }
39
+
40
+ function sleepMs(ms: number): void {
41
+ const end = Date.now() + ms
42
+ while (Date.now() < end) {
43
+ /* spin */
44
+ }
45
+ }
46
+
47
+ export interface ValkeySidecarResult {
48
+ addr: string
49
+ containerName: string | null
50
+ started: boolean
51
+ }
52
+
53
+ /**
54
+ * Ensure Valkey is reachable at 127.0.0.1:6379 for native dev.
55
+ * Starts a named Docker container when Docker is available and nothing is listening.
56
+ */
57
+ export function ensureValkeySidecar(projectName: string): ValkeySidecarResult {
58
+ const envAddr = process.env["SUPATYPE_VALKEY_ADDR"]?.trim()
59
+ if (envAddr) {
60
+ return { addr: envAddr, containerName: null, started: false }
61
+ }
62
+
63
+ const addr = `${DEFAULT_HOST}:${DEFAULT_PORT}`
64
+
65
+ if (isAddrReachable(DEFAULT_HOST, DEFAULT_PORT)) {
66
+ return { addr, containerName: null, started: false }
67
+ }
68
+
69
+ if (!dockerAvailable()) {
70
+ console.warn(
71
+ "[supatype] ⚠ Valkey not available (Docker not running). " +
72
+ "REST server cache (`cache({ server: true })`) will bypass until Valkey is reachable.",
73
+ )
74
+ return { addr: "", containerName: null, started: false }
75
+ }
76
+
77
+ const name = containerName(projectName)
78
+ spawnSync("docker", ["rm", "-f", name], { stdio: "ignore" })
79
+
80
+ const run = spawnSync(
81
+ "docker",
82
+ ["run", "-d", "--name", name, "-p", `${DEFAULT_PORT}:6379`, IMAGE],
83
+ { encoding: "utf8" },
84
+ )
85
+
86
+ if (run.status !== 0) {
87
+ const detail = (run.stderr || run.stdout || "").trim()
88
+ console.warn(
89
+ `[supatype] ⚠ Could not start Valkey sidecar: ${detail || `exit ${run.status}`}\n` +
90
+ " REST server cache will bypass until Valkey is available.",
91
+ )
92
+ return { addr: "", containerName: null, started: false }
93
+ }
94
+
95
+ for (let i = 0; i < 30; i++) {
96
+ if (isAddrReachable(DEFAULT_HOST, DEFAULT_PORT)) {
97
+ console.log(`[supatype] Valkey sidecar running (${addr})`)
98
+ return { addr, containerName: name, started: true }
99
+ }
100
+ sleepMs(100)
101
+ }
102
+
103
+ console.warn("[supatype] ⚠ Valkey sidecar started but not reachable yet — cache may bypass initially.")
104
+ return { addr, containerName: name, started: true }
105
+ }
106
+
107
+ export function stopValkeySidecar(containerName: string | null): void {
108
+ if (!containerName) return
109
+ spawnSync("docker", ["rm", "-f", containerName], { stdio: "ignore" })
110
+ }
111
+
112
+ /** @internal Test helper — probe without subprocess. */
113
+ export function probeTcp(host: string, port: number): Promise<boolean> {
114
+ return new Promise((resolve) => {
115
+ const s = createConnection({ host, port })
116
+ s.once("connect", () => {
117
+ s.destroy()
118
+ resolve(true)
119
+ })
120
+ s.once("error", () => resolve(false))
121
+ setTimeout(() => {
122
+ s.destroy()
123
+ resolve(false)
124
+ }, 800)
125
+ })
126
+ }
@@ -3,7 +3,11 @@ import {
3
3
  ADMIN_EMAIL_ENV,
4
4
  ADMIN_PASSWORD_ENV,
5
5
  clearAdminSeedPassword,
6
+ composePostgresPassword,
7
+ GOTRUE_NIL_INSTANCE_ID,
8
+ gotrueJwtAud,
6
9
  hashPasswordForAuth,
10
+ resolveAuthConfirmedAtColumn,
7
11
  } from "../src/commands/admin.js"
8
12
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
9
13
  import { join } from "node:path"
@@ -38,6 +42,82 @@ describe("clearAdminSeedPassword", () => {
38
42
  })
39
43
  })
40
44
 
45
+ describe("composePostgresPassword", () => {
46
+ it("reads POSTGRES_PASSWORD from .env", () => {
47
+ const dir = join(tmpdir(), `supatype-admin-pgpass-${Date.now()}`)
48
+ mkdirSync(dir, { recursive: true })
49
+ writeFileSync(join(dir, ".env"), "POSTGRES_PASSWORD=secret\n", "utf8")
50
+ try {
51
+ expect(composePostgresPassword(dir)).toBe("secret")
52
+ } finally {
53
+ rmSync(dir, { recursive: true, force: true })
54
+ }
55
+ })
56
+
57
+ it("defaults to postgres when unset", () => {
58
+ const dir = join(tmpdir(), `supatype-admin-pgpass-default-${Date.now()}`)
59
+ mkdirSync(dir, { recursive: true })
60
+ try {
61
+ expect(composePostgresPassword(dir)).toBe("postgres")
62
+ } finally {
63
+ rmSync(dir, { recursive: true, force: true })
64
+ }
65
+ })
66
+ })
67
+
68
+ describe("gotrueJwtAud", () => {
69
+ it("reads GOTRUE_JWT_AUD from .env", () => {
70
+ const dir = join(tmpdir(), `supatype-admin-aud-${Date.now()}`)
71
+ mkdirSync(dir, { recursive: true })
72
+ writeFileSync(join(dir, ".env"), "GOTRUE_JWT_AUD=custom-aud\n", "utf8")
73
+ try {
74
+ expect(gotrueJwtAud(dir)).toBe("custom-aud")
75
+ } finally {
76
+ rmSync(dir, { recursive: true, force: true })
77
+ }
78
+ })
79
+
80
+ it("defaults to authenticated", () => {
81
+ const dir = join(tmpdir(), `supatype-admin-aud-default-${Date.now()}`)
82
+ mkdirSync(dir, { recursive: true })
83
+ try {
84
+ expect(gotrueJwtAud(dir)).toBe("authenticated")
85
+ } finally {
86
+ rmSync(dir, { recursive: true, force: true })
87
+ }
88
+ })
89
+ })
90
+
91
+ describe("GOTRUE_NIL_INSTANCE_ID", () => {
92
+ it("is the nil UUID GoTrue uses for lookup", () => {
93
+ expect(GOTRUE_NIL_INSTANCE_ID).toBe("00000000-0000-0000-0000-000000000000")
94
+ })
95
+ })
96
+
97
+ describe("resolveAuthConfirmedAtColumn", () => {
98
+ it("prefers email_confirmed_at when both columns exist", async () => {
99
+ const column = await resolveAuthConfirmedAtColumn(async () => ({
100
+ rows: [{ column_name: "email_confirmed_at" }],
101
+ rowCount: 1,
102
+ command: "SELECT",
103
+ oid: 0,
104
+ fields: [],
105
+ }))
106
+ expect(column).toBe("email_confirmed_at")
107
+ })
108
+
109
+ it("falls back to confirmed_at for postgres init schema", async () => {
110
+ const column = await resolveAuthConfirmedAtColumn(async () => ({
111
+ rows: [{ value: "confirmed_at" }],
112
+ rowCount: 1,
113
+ command: "SELECT",
114
+ oid: 0,
115
+ fields: [],
116
+ }))
117
+ expect(column).toBe("confirmed_at")
118
+ })
119
+ })
120
+
41
121
  describe("init admin seed in .env", () => {
42
122
  it("writes SUPATYPE_ADMIN_* when scaffold options include credentials", () => {
43
123
  const dir = join(tmpdir(), `supatype-init-admin-${Date.now()}`)
@@ -98,7 +98,7 @@ describe("VERSION_PIN_LOCAL + resolveBinary", () => {
98
98
 
99
99
  it("throws when versions.engine is local without overrides.engine", async () => {
100
100
  const cfg = baseConfig()
101
- cfg.versions.engine = VERSION_PIN_LOCAL
101
+ cfg.versions!.engine = VERSION_PIN_LOCAL
102
102
  await expect(resolveBinary("engine", cfg)).rejects.toThrow(/overrides\.engine/)
103
103
  })
104
104
 
@@ -106,7 +106,7 @@ describe("VERSION_PIN_LOCAL + resolveBinary", () => {
106
106
  const fakeBin = join(tmp, "supatype-engine-local")
107
107
  writeFileSync(fakeBin, "#")
108
108
  const cfg = baseConfig({ engine: fakeBin })
109
- cfg.versions.engine = VERSION_PIN_LOCAL
109
+ cfg.versions!.engine = VERSION_PIN_LOCAL
110
110
  const p = await resolveBinary("engine", cfg)
111
111
  expect(p.replace(/\\/g, "/")).toContain("supatype-engine-local")
112
112
  })
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it, beforeEach, afterEach } from "vitest"
2
+ import { existsSync, mkdirSync, rmSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import {
6
+ tryAcquireDownloadLock,
7
+ releaseDownloadLock,
8
+ isDownloadInProgress,
9
+ clearDownloadLock,
10
+ } from "../src/binary-download-lock.js"
11
+ import { cachePath } from "../src/binary-cache.js"
12
+
13
+ const VERSION = "9.9.9-test"
14
+
15
+ describe("binary-download-lock", () => {
16
+ let dir: string
17
+
18
+ beforeEach(() => {
19
+ dir = cachePath("deno", VERSION)
20
+ if (existsSync(dir)) rmSync(dir, { recursive: true, force: true })
21
+ mkdirSync(dir, { recursive: true })
22
+ })
23
+
24
+ afterEach(() => {
25
+ clearDownloadLock("deno", VERSION)
26
+ if (existsSync(dir)) rmSync(dir, { recursive: true, force: true })
27
+ })
28
+
29
+ it("acquires and releases a download lock", () => {
30
+ expect(tryAcquireDownloadLock("deno", VERSION)).toBe(true)
31
+ expect(isDownloadInProgress("deno", VERSION)).toBe(true)
32
+ releaseDownloadLock("deno", VERSION)
33
+ expect(isDownloadInProgress("deno", VERSION)).toBe(false)
34
+ })
35
+
36
+ it("rejects a second acquire while the lock is held", () => {
37
+ expect(tryAcquireDownloadLock("deno", VERSION)).toBe(true)
38
+ expect(tryAcquireDownloadLock("deno", VERSION)).toBe(false)
39
+ releaseDownloadLock("deno", VERSION)
40
+ })
41
+ })
@@ -4,12 +4,15 @@
4
4
  */
5
5
  import { describe, it, expect } from "vitest"
6
6
  import { spawnSync } from "node:child_process"
7
- import { resolve, dirname } from "node:path"
7
+ import { readFileSync } from "node:fs"
8
+ import { resolve, dirname, join } from "node:path"
8
9
  import { fileURLToPath } from "node:url"
9
10
 
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url))
11
12
  const CLI_BIN = resolve(__dirname, "../bin/supatype.js")
12
- const DIST_CLI = resolve(__dirname, "../dist/cli.js")
13
+ const PACKAGE_VERSION = (
14
+ JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8")) as { version: string }
15
+ ).version
13
16
 
14
17
  function runCli(args: string[]): { stdout: string; stderr: string; exitCode: number } {
15
18
  const result = spawnSync(process.execPath, [CLI_BIN, ...args], {
@@ -51,10 +54,10 @@ describe("CLI binary (requires built dist/)", () => {
51
54
  }
52
55
  })
53
56
 
54
- it("--version prints a semver string", () => {
57
+ it("--version prints package.json version", () => {
55
58
  const { stdout, exitCode } = runCli(["--version"])
56
59
  expect(exitCode).toBe(0)
57
- expect(stdout.trim()).toMatch(/^\d+\.\d+\.\d+/)
60
+ expect(stdout.trim()).toBe(PACKAGE_VERSION)
58
61
  })
59
62
 
60
63
  it("self-update --help describes the command", () => {