@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,15 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { readFileSync } from "node:fs"
3
+ import { join, dirname } from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+ import { cliPackageVersion } from "../src/cli-package-version.js"
6
+
7
+ const packageJson = JSON.parse(
8
+ readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../package.json"), "utf8"),
9
+ ) as { version: string }
10
+
11
+ describe("cliPackageVersion", () => {
12
+ it("matches packages/cli/package.json version", () => {
13
+ expect(cliPackageVersion()).toBe(packageJson.version)
14
+ })
15
+ })
@@ -0,0 +1,149 @@
1
+ import { Command } from "commander"
2
+ import { describe, expect, it } from "vitest"
3
+ import { registerInit } from "../src/commands/init.js"
4
+ import { registerDev } from "../src/commands/dev.js"
5
+ import { registerCache } from "../src/commands/cache.js"
6
+ import { registerSelfUpdate } from "../src/commands/self-update.js"
7
+ import { registerUpdate } from "../src/commands/update.js"
8
+ import { registerPg } from "../src/commands/pg.js"
9
+ import { registerPush } from "../src/commands/push.js"
10
+ import { registerDiff } from "../src/commands/diff.js"
11
+ import { registerPull } from "../src/commands/pull.js"
12
+ import { registerDoctor } from "../src/commands/doctor.js"
13
+ import { registerIntrospect } from "../src/commands/introspect.js"
14
+ import { registerAdopt } from "../src/commands/adopt.js"
15
+ import { registerGenerate } from "../src/commands/generate.js"
16
+ import { registerMigrate } from "../src/commands/migrate.js"
17
+ import { registerSeed } from "../src/commands/seed.js"
18
+ import { registerKeys } from "../src/commands/keys.js"
19
+ import { registerApp } from "../src/commands/app.js"
20
+ import { registerAdd } from "../src/commands/add.js"
21
+ import { registerSelfHost } from "../src/commands/self-host.js"
22
+ import { registerCloud } from "../src/commands/cloud.js"
23
+ import { registerEngine } from "../src/commands/engine.js"
24
+ import { registerDb } from "../src/commands/db.js"
25
+ import { registerDeploy } from "../src/commands/deploy.js"
26
+ import { registerStatus } from "../src/commands/status.js"
27
+ import { registerLogs } from "../src/commands/logs.js"
28
+ import { registerAdmin } from "../src/commands/admin.js"
29
+ import { registerFunctions } from "../src/commands/functions.js"
30
+ import { registerPlugins } from "../src/commands/plugins.js"
31
+ import { registerTypes } from "../src/commands/types.js"
32
+ import { registerMigrateFromV1 } from "../src/commands/migrate-from-v1.js"
33
+ import {
34
+ shouldExcludeCommandChrome,
35
+ wrapProgramActionsWithChrome,
36
+ } from "../src/ui/runtime/command-chrome.js"
37
+
38
+ type CommandWithAction = Command & {
39
+ parent?: Command | null
40
+ _actionHandler?: ((args: unknown) => unknown) | null
41
+ }
42
+
43
+ function commandPath(cmd: CommandWithAction, root: Command): string {
44
+ const segments: string[] = []
45
+ let current: CommandWithAction | null = cmd
46
+ while (current && current !== root) {
47
+ segments.unshift(current.name())
48
+ current = current.parent ?? null
49
+ }
50
+ return segments.join(" ")
51
+ }
52
+
53
+ function collectActionCommands(cmd: CommandWithAction, root: Command): CommandWithAction[] {
54
+ const out: CommandWithAction[] = []
55
+ if (cmd._actionHandler) out.push(cmd)
56
+ for (const sub of cmd.commands) {
57
+ out.push(...collectActionCommands(sub as CommandWithAction, root))
58
+ }
59
+ return out
60
+ }
61
+
62
+ function buildProgram(): Command {
63
+ const program = new Command().name("supatype")
64
+ registerInit(program)
65
+ registerDev(program)
66
+ registerCache(program)
67
+ registerSelfUpdate(program)
68
+ registerUpdate(program)
69
+ registerPg(program)
70
+ registerPush(program)
71
+ registerDiff(program)
72
+ registerPull(program)
73
+ registerDoctor(program)
74
+ registerIntrospect(program)
75
+ registerAdopt(program)
76
+ registerGenerate(program)
77
+ registerMigrate(program)
78
+ registerSeed(program)
79
+ registerKeys(program)
80
+ registerApp(program)
81
+ registerAdd(program)
82
+ registerSelfHost(program)
83
+ registerCloud(program)
84
+ registerEngine(program)
85
+ registerDb(program)
86
+ registerDeploy(program)
87
+ registerStatus(program)
88
+ registerLogs(program)
89
+ registerAdmin(program)
90
+ registerFunctions(program)
91
+ registerPlugins(program)
92
+ registerTypes(program)
93
+ registerMigrateFromV1(program)
94
+ wrapProgramActionsWithChrome(program)
95
+ return program
96
+ }
97
+
98
+ describe("CLI command chrome coverage", () => {
99
+ it("wraps every non-excluded leaf command", () => {
100
+ const program = buildProgram()
101
+ const root = program as CommandWithAction
102
+ const commands = collectActionCommands(root, program)
103
+
104
+ expect(commands.length).toBeGreaterThan(50)
105
+
106
+ const excluded: string[] = []
107
+ const wrapped: string[] = []
108
+
109
+ for (const cmd of commands) {
110
+ const path = commandPath(cmd, program)
111
+ const handlerBefore = cmd._actionHandler
112
+ // Re-wrap is idempotent only when already wrapped; detect first-wrap by path policy.
113
+ if (shouldExcludeCommandChrome(path)) {
114
+ excluded.push(path)
115
+ continue
116
+ }
117
+ wrapped.push(path)
118
+ expect(handlerBefore, `${path} should be wrapped`).toBeDefined()
119
+ }
120
+
121
+ expect(excluded).toEqual(
122
+ expect.arrayContaining([
123
+ "dev",
124
+ "init",
125
+ "link",
126
+ "add domain",
127
+ "logs",
128
+ "pg psql",
129
+ "functions serve",
130
+ "self-host compose logs",
131
+ ]),
132
+ )
133
+
134
+ expect(wrapped).toEqual(
135
+ expect.arrayContaining([
136
+ "push",
137
+ "diff",
138
+ "doctor",
139
+ "pull",
140
+ "cache list",
141
+ "cache rest list",
142
+ "deploy",
143
+ "deploy rollback",
144
+ "keys",
145
+ "generate",
146
+ ]),
147
+ )
148
+ })
149
+ })
@@ -0,0 +1,51 @@
1
+ import { Command } from "commander"
2
+ import { describe, expect, it, vi } from "vitest"
3
+ import {
4
+ shouldExcludeCommandChrome,
5
+ wrapProgramActionsWithChrome,
6
+ } from "../src/ui/runtime/command-chrome.js"
7
+
8
+ describe("shouldExcludeCommandChrome", () => {
9
+ it("excludes wizard and streaming commands", () => {
10
+ expect(shouldExcludeCommandChrome("dev")).toBe(true)
11
+ expect(shouldExcludeCommandChrome("init")).toBe(true)
12
+ expect(shouldExcludeCommandChrome("link")).toBe(true)
13
+ expect(shouldExcludeCommandChrome("add domain")).toBe(true)
14
+ expect(shouldExcludeCommandChrome("logs")).toBe(true)
15
+ expect(shouldExcludeCommandChrome("pg psql")).toBe(true)
16
+ expect(shouldExcludeCommandChrome("functions serve")).toBe(true)
17
+ expect(shouldExcludeCommandChrome("self-host compose logs")).toBe(true)
18
+ })
19
+
20
+ it("includes one-shot commands", () => {
21
+ expect(shouldExcludeCommandChrome("push")).toBe(false)
22
+ expect(shouldExcludeCommandChrome("doctor")).toBe(false)
23
+ expect(shouldExcludeCommandChrome("cache rest list")).toBe(false)
24
+ expect(shouldExcludeCommandChrome("deploy rollback")).toBe(false)
25
+ })
26
+ })
27
+
28
+ describe("wrapProgramActionsWithChrome", () => {
29
+ it("wraps leaf actions but skips excluded paths", () => {
30
+ const program = new Command()
31
+ const pushFn = vi.fn()
32
+ const devFn = vi.fn()
33
+
34
+ program.command("push").action(pushFn)
35
+ program.command("dev").action(devFn)
36
+
37
+ type CommandWithAction = Command & {
38
+ _actionHandler?: ((args: unknown) => unknown) | null
39
+ }
40
+
41
+ const pushCmd = program.commands.find((c) => c.name() === "push") as CommandWithAction
42
+ const devCmd = program.commands.find((c) => c.name() === "dev") as CommandWithAction
43
+ const pushHandlerBefore = pushCmd._actionHandler
44
+ const devHandlerBefore = devCmd._actionHandler
45
+
46
+ wrapProgramActionsWithChrome(program)
47
+
48
+ expect(pushCmd._actionHandler).not.toBe(pushHandlerBefore)
49
+ expect(devCmd._actionHandler).toBe(devHandlerBefore)
50
+ })
51
+ })
@@ -176,9 +176,28 @@ describe("loadConfig()", () => {
176
176
  })}`,
177
177
  )
178
178
  const cfg = loadConfig(tmpDir)
179
- expect(cfg.versions.engine).toBe("0.4.2")
179
+ expect(cfg.versions!.engine).toBe("0.4.2")
180
180
  expect(cfg.schema?.path).toBe("./a.ts")
181
181
  })
182
+
183
+ it("loads defineConfig import before @supatype/cli is installed (init scaffold)", () => {
184
+ writeFileSync(
185
+ join(tmpDir, "supatype.config.ts"),
186
+ `import { defineConfig } from "@supatype/cli"
187
+
188
+ export default defineConfig({
189
+ project: { name: "fresh" },
190
+ database: { provider: "docker" },
191
+ server: { mode: "dev" },
192
+ app: { mode: "none" },
193
+ schema: { path: "./schema/index.ts" },
194
+ })
195
+ `,
196
+ )
197
+ const cfg = loadConfig(tmpDir)
198
+ expect(cfg.project?.name).toBe("fresh")
199
+ expect(cfg.schema?.path).toBe("./schema/index.ts")
200
+ })
182
201
  })
183
202
 
184
203
  describe("resolveRuntimeProvider()", () => {
@@ -207,7 +226,9 @@ describe("mergeProjectConfig()", () => {
207
226
  ...minimalProject("p"),
208
227
  email: { provider: "smtp", smtp: { host: "h1", port: 587, user: "u0" } },
209
228
  })
210
- const merged = mergeProjectConfig(base, { email: { smtp: { host: "h2", pass: "x" } } })
229
+ const merged = mergeProjectConfig(base, {
230
+ email: { smtp: { host: "h2", pass: "x" } },
231
+ } as Partial<typeof base>)
211
232
  expect(merged.email?.provider).toBe("smtp")
212
233
  expect(merged.email?.smtp?.host).toBe("h2")
213
234
  expect(merged.email?.smtp?.port).toBe(587)
@@ -222,7 +243,7 @@ describe("mergeProjectConfig()", () => {
222
243
  })
223
244
  const merged = mergeProjectConfig(base, {
224
245
  email: { send_email_hook_secrets: "v1,whsec_customsecret0000000000000000" },
225
- })
246
+ } as Partial<typeof base>)
226
247
  expect(merged.email?.send_email_hook).toBe(true)
227
248
  expect(merged.email?.send_email_hook_uri).toBe("http://old/hook")
228
249
  expect(merged.email?.send_email_hook_secrets).toBe("v1,whsec_customsecret0000000000000000")
@@ -233,7 +254,9 @@ describe("mergeProjectConfig()", () => {
233
254
  ...minimalProject("p"),
234
255
  app: { mode: "static", static_dir: "./dist", vite_dev_url: "http://127.0.0.1:1111" },
235
256
  })
236
- const merged = mergeProjectConfig(base, { app: { vite_dev_url: "http://127.0.0.1:5173" } })
257
+ const merged = mergeProjectConfig(base, {
258
+ app: { vite_dev_url: "http://127.0.0.1:5173" },
259
+ } as Partial<typeof base>)
237
260
  expect(merged.app?.vite_dev_url).toBe("http://127.0.0.1:5173")
238
261
  })
239
262
 
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { devReadyPanelCompactRowCount, devReadyPanelRowCount } from "../src/dev-ready-panel.js"
3
+
4
+ describe("devReadyPanelRowCount", () => {
5
+ it("accounts for border, keys block, and hints", () => {
6
+ const rows = devReadyPanelRowCount({
7
+ title: "Services running",
8
+ links: [
9
+ { label: "API", url: "http://localhost:1" },
10
+ { label: "Studio", url: "http://localhost:1/studio/" },
11
+ ],
12
+ hints: ["Demo data: pnpm seed"],
13
+ anonKey: "anon-jwt",
14
+ serviceRoleKey: "svc-jwt",
15
+ })
16
+
17
+ // border(2) + title(1) + links(2) + hint(1) + keys header(1) + anon(1) + svc(1) + margin(1)
18
+ expect(rows).toBe(10)
19
+ })
20
+ })
21
+
22
+ describe("devReadyPanelCompactRowCount", () => {
23
+ it("uses a shorter layout than the full panel", () => {
24
+ const panel = {
25
+ title: "Services running",
26
+ links: [
27
+ { label: "API", url: "http://localhost:1" },
28
+ { label: "REST", url: "http://localhost:1/rest/v1/" },
29
+ { label: "Studio", url: "http://localhost:1/studio/" },
30
+ ],
31
+ hints: ["Demo data: pnpm seed"],
32
+ anonKey: "anon-jwt",
33
+ serviceRoleKey: "svc-jwt",
34
+ }
35
+ expect(devReadyPanelCompactRowCount(panel)).toBeLessThan(devReadyPanelRowCount(panel))
36
+ })
37
+ })
@@ -2,9 +2,13 @@ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
2
2
  import { mkdtempSync, writeFileSync } from "node:fs"
3
3
  import { join } from "node:path"
4
4
  import { tmpdir } from "node:os"
5
+
6
+ vi.mock("ink", () => ({
7
+ render: vi.fn(() => ({ unmount: vi.fn() })),
8
+ }))
5
9
  import { DevLogBus } from "../src/dev-log-bus.js"
6
- import { filterDevSubprocessLine, formatConsoleArgs, stripAnsi } from "../src/dev-log-filter.js"
7
- import { appendDevTaskLog, beginDevSession, endDevSession, resolveDevUiMode } from "../src/dev-session.js"
10
+ import { filterCommandLogLine, filterDevSubprocessLine, filterStackLogLine, formatConsoleArgs, stripAnsi } from "../src/dev-log-filter.js"
11
+ import { appendDevTaskLog, beginDevSession, endDevSession, getActiveDevSession, resolveDevUiMode, withDevSessionSuspended } from "../src/dev-session.js"
8
12
  import {
9
13
  layoutLogoBlock,
10
14
  logoRowCount,
@@ -25,6 +29,28 @@ describe("filterDevSubprocessLine()", () => {
25
29
  })
26
30
  })
27
31
 
32
+ describe("filterStackLogLine()", () => {
33
+ it("drops docker build progress but keeps supatype messages", () => {
34
+ expect(filterStackLogLine("#1 [internal] load build definition from Dockerfile")).toBe(false)
35
+ expect(filterStackLogLine(" Container supatype-demo-db-1 Running")).toBe(false)
36
+ expect(filterStackLogLine("[supatype] Waiting for Postgres (compose)...")).toBe(true)
37
+ expect(filterStackLogLine("Services running.")).toBe(true)
38
+ expect(filterStackLogLine(" API (Kong) http://localhost:18473")).toBe(true)
39
+ })
40
+
41
+ it("keeps override banner lines", () => {
42
+ expect(filterStackLogLine(" engine → ../supatype-schema-engine/bin/engine")).toBe(true)
43
+ })
44
+ })
45
+
46
+ describe("filterCommandLogLine()", () => {
47
+ it("keeps diff output but drops docker container progress", () => {
48
+ expect(filterCommandLogLine(" [+] add_column users.email")).toBe(true)
49
+ expect(filterCommandLogLine(" Container supatype-demo-db-1 Running")).toBe(false)
50
+ expect(filterCommandLogLine("[supatype] Schema pushed.")).toBe(true)
51
+ })
52
+ })
53
+
28
54
  describe("stripAnsi()", () => {
29
55
  it("removes colour codes", () => {
30
56
  expect(stripAnsi("\x1b[33mhello\x1b[0m")).toBe("hello")
@@ -78,6 +104,20 @@ describe("appendDevTaskLog()", () => {
78
104
  expect(session.bus.getTask("app")?.lines).toEqual(["Proxy mode: running npm run vite (/tmp/app)"])
79
105
  expect(session.bus.getTask("stack")?.lines ?? []).toEqual([])
80
106
  })
107
+
108
+ it("captures console.log to the bus before ink mounts", () => {
109
+ beginDevSession("tui")
110
+ console.log("[supatype] bootstrap line")
111
+ expect(getActiveDevSession()?.bus.getTask("stack")?.lines).toEqual(["bootstrap line"])
112
+ expect(getActiveDevSession()?.isInkMounted()).toBe(true)
113
+ })
114
+
115
+ it("withDevSessionSuspended is a no-op during bootstrap", async () => {
116
+ beginDevSession("tui")
117
+ await withDevSessionSuspended(async () => "ok")
118
+ expect(getActiveDevSession()?.isConsoleCaptured()).toBe(true)
119
+ expect(getActiveDevSession()?.isInkMounted()).toBe(true)
120
+ })
81
121
  })
82
122
 
83
123
  describe("dev-logo", () => {
@@ -1,9 +1,8 @@
1
- import { describe, expect, it, vi, beforeEach, afterEach, afterAll } from "vitest"
1
+ import { describe, expect, it, vi, beforeEach, afterEach, afterAll, type MockInstance } from "vitest"
2
2
  import type { SpawnSyncReturns } from "node:child_process"
3
3
 
4
4
  const spawnSyncMock = vi.hoisted(() => vi.fn())
5
5
  const isInteractiveMock = vi.hoisted(() => vi.fn(() => false))
6
- const clackLogErrorMock = vi.hoisted(() => vi.fn())
7
6
  const clackNoteMock = vi.hoisted(() => vi.fn())
8
7
  const clackIntroMock = vi.hoisted(() => vi.fn())
9
8
  const printLogoMock = vi.hoisted(() => vi.fn())
@@ -16,18 +15,12 @@ vi.mock("../src/ui/interactive.js", () => ({
16
15
  isInteractive: isInteractiveMock,
17
16
  }))
18
17
 
19
- vi.mock("@clack/prompts", async (importOriginal) => {
20
- const original = await importOriginal<typeof import("@clack/prompts")>()
21
- return {
22
- ...original,
18
+ vi.mock("../src/ui/clack.js", () => ({
19
+ p: {
23
20
  intro: clackIntroMock,
24
21
  note: clackNoteMock,
25
- log: {
26
- ...original.log,
27
- error: clackLogErrorMock,
28
- },
29
- }
30
- })
22
+ },
23
+ }))
31
24
 
32
25
  vi.mock("../src/ui/prompts.js", async (importOriginal) => {
33
26
  const original = await importOriginal<typeof import("../src/ui/prompts.js")>()
@@ -124,7 +117,6 @@ describe("reportDockerUnavailable", () => {
124
117
  isInteractiveMock.mockReturnValue(false)
125
118
  errorMock.mockClear()
126
119
  logMock.mockClear()
127
- clackLogErrorMock.mockClear()
128
120
  clackNoteMock.mockClear()
129
121
  clackIntroMock.mockClear()
130
122
  printLogoMock.mockClear()
@@ -171,7 +163,7 @@ describe("reportDockerUnavailable", () => {
171
163
  expect(logMock.mock.calls.some((call) => String(call[0]).includes("docker-desktop"))).toBe(true)
172
164
  })
173
165
 
174
- it("uses Clack log + note in interactive mode", () => {
166
+ it("uses Ink intro + note and themed error in interactive mode", () => {
175
167
  isInteractiveMock.mockReturnValue(true)
176
168
 
177
169
  reportDockerUnavailable(
@@ -185,20 +177,17 @@ describe("reportDockerUnavailable", () => {
185
177
 
186
178
  expect(printLogoMock).toHaveBeenCalled()
187
179
  expect(clackIntroMock).toHaveBeenCalledWith("Local development")
188
- expect(clackLogErrorMock).toHaveBeenCalledWith(
189
- "Docker is installed but the daemon is not running.",
190
- )
180
+ expect(logMock.mock.calls.some((call) => String(call[0]).includes("daemon is not running"))).toBe(true)
191
181
  expect(clackNoteMock).toHaveBeenCalledWith(
192
182
  expect.stringContaining("Unpause Docker Desktop"),
193
183
  )
194
184
  expect(errorMock).not.toHaveBeenCalled()
195
- expect(logMock).not.toHaveBeenCalled()
196
185
  })
197
186
  })
198
187
 
199
188
  describe("requireDockerDaemon", () => {
200
- let exitMock: ReturnType<typeof vi.spyOn<typeof process, "exit">>
201
- let errorMock: ReturnType<typeof vi.spyOn<typeof console, "error">>
189
+ let exitMock: MockInstance<(code?: string | number | null | undefined) => never>
190
+ let errorMock: MockInstance<typeof console.error>
202
191
 
203
192
  beforeEach(() => {
204
193
  spawnSyncMock.mockReset()
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { ensureDevApiConfig } from "../src/ensure-dev-api-config.js"
6
+
7
+ describe("ensureDevApiConfig()", () => {
8
+ it("copies config/api-config.json when target is missing", () => {
9
+ const dir = mkdtempSync(join(tmpdir(), "supatype-api-config-"))
10
+ const configDir = join(dir, "config")
11
+ const template = join(configDir, "api-config.json")
12
+ const target = join(dir, ".supatype", "api-config.json")
13
+
14
+ mkdirSync(configDir, { recursive: true })
15
+ writeFileSync(template, '{"rest":{"schema":"public"}}\n', "utf8")
16
+
17
+ expect(ensureDevApiConfig(dir)).toBe(true)
18
+ expect(existsSync(target)).toBe(true)
19
+ expect(readFileSync(target, "utf8")).toContain('"rest"')
20
+ expect(ensureDevApiConfig(dir)).toBe(false)
21
+ })
22
+ })
@@ -0,0 +1,62 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest"
2
+ import {
3
+ initDependencyVersionsFallback,
4
+ resolveInitDependencyVersions,
5
+ } from "../src/init-dependency-versions.js"
6
+
7
+ function mockRegistry(packages: Record<string, { latest: string; versions: string[] }>) {
8
+ vi.stubGlobal(
9
+ "fetch",
10
+ vi.fn(async (url: string) => {
11
+ const name = decodeURIComponent(url.replace("https://registry.npmjs.org/", ""))
12
+ const pkg = packages[name]
13
+ if (!pkg) return new Response(null, { status: 404 })
14
+ const versions = Object.fromEntries(pkg.versions.map((v) => [v, {}]))
15
+ return Response.json({ versions, "dist-tags": { latest: pkg.latest } })
16
+ }),
17
+ )
18
+ }
19
+
20
+ describe("resolveInitDependencyVersions", () => {
21
+ afterEach(() => {
22
+ vi.unstubAllGlobals()
23
+ })
24
+
25
+ it("uses the running CLI version when both packages publish it", async () => {
26
+ mockRegistry({
27
+ "@supatype/cli": { latest: "0.1.3", versions: ["0.1.1", "0.1.3"] },
28
+ "@supatype/types": { latest: "0.1.3", versions: ["0.1.1", "0.1.3"] },
29
+ })
30
+
31
+ const deps = await resolveInitDependencyVersions()
32
+ expect(deps.cli).toBe("0.1.3")
33
+ expect(deps.types).toBe("0.1.3")
34
+ })
35
+
36
+ it("falls back to npm latest when the running CLI version is unpublished", async () => {
37
+ mockRegistry({
38
+ "@supatype/cli": { latest: "0.1.1", versions: ["0.1.1"] },
39
+ "@supatype/types": { latest: "0.1.1", versions: ["0.1.1"] },
40
+ })
41
+
42
+ const deps = await resolveInitDependencyVersions()
43
+ expect(deps.cli).toBe("0.1.1")
44
+ expect(deps.types).toBe("0.1.1")
45
+ })
46
+
47
+ it("can pin cli and types to different published versions", async () => {
48
+ mockRegistry({
49
+ "@supatype/cli": { latest: "0.1.3", versions: ["0.1.1", "0.1.3"] },
50
+ "@supatype/types": { latest: "0.1.1", versions: ["0.1.1"] },
51
+ })
52
+
53
+ const deps = await resolveInitDependencyVersions()
54
+ expect(deps.cli).toBe("0.1.3")
55
+ expect(deps.types).toBe("0.1.1")
56
+ })
57
+
58
+ it("falls back to the running CLI version when the registry is unreachable", async () => {
59
+ vi.stubGlobal("fetch", vi.fn(async () => new Response(null, { status: 500 })))
60
+ expect(await resolveInitDependencyVersions()).toEqual(initDependencyVersionsFallback())
61
+ })
62
+ })
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { mergeSupatypePackageJson } from "../src/init-package-json.js"
3
+
4
+ describe("mergeSupatypePackageJson", () => {
5
+ it("adds Supatype deps and scripts without overwriting existing dev script", () => {
6
+ const merged = mergeSupatypePackageJson(
7
+ { name: "existing", scripts: { dev: "next dev" } },
8
+ { projectName: "existing", app: { mode: "none" }, helloFunction: false },
9
+ { cli: "0.1.1", types: "0.1.1" },
10
+ )
11
+
12
+ expect(merged.dependencies?.["@supatype/cli"]).toBe("^0.1.1")
13
+ expect(merged.dependencies?.["@supatype/types"]).toBe("^0.1.1")
14
+ expect(merged.scripts?.dev).toBe("next dev")
15
+ expect(merged.scripts?.["supatype:dev"]).toBe("supatype dev")
16
+ expect(merged.scripts?.push).toBe("supatype push")
17
+ })
18
+ })
@@ -0,0 +1,53 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest"
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { detectProjectSetup } from "../src/init-project-detect.js"
6
+
7
+ let dir: string
8
+
9
+ beforeEach(() => {
10
+ dir = join(tmpdir(), `supatype-detect-${Date.now()}`)
11
+ mkdirSync(dir, { recursive: true })
12
+ })
13
+
14
+ afterEach(() => {
15
+ rmSync(dir, { recursive: true, force: true })
16
+ })
17
+
18
+ describe("detectProjectSetup()", () => {
19
+ it("returns empty summary for an empty directory", () => {
20
+ const detected = detectProjectSetup(dir)
21
+ expect(detected.hasExistingFiles).toBe(false)
22
+ expect(detected.hasVite).toBe(false)
23
+ expect(detected.summaryLines).toEqual([])
24
+ })
25
+
26
+ it("detects Vite config and port", () => {
27
+ writeFileSync(
28
+ join(dir, "vite.config.ts"),
29
+ `export default { server: { port: 5174 } }`,
30
+ "utf8",
31
+ )
32
+ const detected = detectProjectSetup(dir)
33
+ expect(detected.hasVite).toBe(true)
34
+ expect(detected.hasViteConfig).toBe(true)
35
+ expect(detected.viteDevUrl).toBe("http://127.0.0.1:5174")
36
+ expect(detected.summaryLines.some((line) => line.includes("Vite config"))).toBe(true)
37
+ })
38
+
39
+ it("detects static dist directory", () => {
40
+ mkdirSync(join(dir, "dist"), { recursive: true })
41
+ writeFileSync(join(dir, "dist", "index.html"), "<html></html>", "utf8")
42
+ const detected = detectProjectSetup(dir)
43
+ expect(detected.staticDir).toBe("./dist")
44
+ expect(detected.summaryLines.some((line) => line.includes("./dist"))).toBe(true)
45
+ })
46
+
47
+ it("detects existing supatype.config.ts", () => {
48
+ writeFileSync(join(dir, "supatype.config.ts"), "export default {}", "utf8")
49
+ const detected = detectProjectSetup(dir)
50
+ expect(detected.hasSupatypeConfig).toBe(true)
51
+ expect(detected.summaryLines).toContain("supatype.config already present")
52
+ })
53
+ })
@@ -53,11 +53,15 @@ describe("scaffold()", () => {
53
53
  expect(content).toContain("pkg-app")
54
54
  })
55
55
 
56
- it("skips package.json when it already exists", () => {
56
+ it("merges Supatype deps into package.json when it already exists", () => {
57
57
  const pkgPath = join(tmpRoot, "package.json")
58
- writeFileSync(pkgPath, '{"name":"existing"}', "utf8")
58
+ writeFileSync(pkgPath, '{"name":"existing","scripts":{"dev":"next dev"}}', "utf8")
59
59
  scaffold(tmpRoot, defaultScaffoldOptions("my-app"))
60
- expect(readFileSync(pkgPath, "utf8")).toBe('{"name":"existing"}')
60
+ const content = readFileSync(pkgPath, "utf8")
61
+ expect(content).toContain("@supatype/cli")
62
+ expect(content).toContain("@supatype/types")
63
+ expect(content).toContain("next dev")
64
+ expect(content).toContain("supatype:dev")
61
65
  })
62
66
 
63
67
  it("supatype.config.ts documents self-host workflow", () => {
@@ -185,6 +189,29 @@ describe("scaffold()", () => {
185
189
  expect(html).toContain("github.com/supatype")
186
190
  })
187
191
 
192
+ it("static app mode with Vite on self-host splits production static and local proxy override", () => {
193
+ scaffold(tmpRoot, {
194
+ ...defaultScaffoldOptions("my-app", "self-host"),
195
+ domain: "api.example.com",
196
+ app: {
197
+ mode: "static",
198
+ staticDir: "./public",
199
+ viteDevUrl: "http://127.0.0.1:5173",
200
+ },
201
+ })
202
+ const committed = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
203
+ expect(committed).toContain('mode: "static"')
204
+ expect(committed).toContain('static_dir: "./dist"')
205
+ expect(committed).not.toContain("vite_dev_url")
206
+
207
+ const local = readFileSync(join(tmpRoot, "supatype.local.config.ts"), "utf8")
208
+ expect(local).toContain('mode: "proxy"')
209
+ expect(local).toContain('start: "vite"')
210
+ expect(local).toContain('vite_dev_url: "http://127.0.0.1:5173"')
211
+ expect(existsSync(join(tmpRoot, "dist/index.html"))).toBe(true)
212
+ expect(existsSync(join(tmpRoot, "vite.config.ts"))).toBe(true)
213
+ })
214
+
188
215
  it("static app mode with Vite scaffolds root index.html and vite.config.ts", () => {
189
216
  scaffold(tmpRoot, {
190
217
  ...defaultScaffoldOptions("my-app"),