@supatype/cli 0.1.0 → 0.1.2

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 (331) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +121 -86
  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 +28 -1
  22. package/dist/commands/admin.d.ts.map +1 -1
  23. package/dist/commands/admin.js +290 -112
  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 +28 -22
  31. package/dist/commands/dev.js.map +1 -1
  32. package/dist/commands/init.d.ts +8 -4
  33. package/dist/commands/init.d.ts.map +1 -1
  34. package/dist/commands/init.js +278 -139
  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/commands/push.js +3 -3
  40. package/dist/commands/push.js.map +1 -1
  41. package/dist/compose-local-server-image.d.ts +13 -0
  42. package/dist/compose-local-server-image.d.ts.map +1 -0
  43. package/dist/compose-local-server-image.js +57 -0
  44. package/dist/compose-local-server-image.js.map +1 -0
  45. package/dist/compose-rename.d.ts +10 -0
  46. package/dist/compose-rename.d.ts.map +1 -0
  47. package/dist/compose-rename.js +64 -0
  48. package/dist/compose-rename.js.map +1 -0
  49. package/dist/dev-compose.d.ts.map +1 -1
  50. package/dist/dev-compose.js +138 -97
  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 +14 -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 +27 -0
  61. package/dist/dev-ports.d.ts.map +1 -0
  62. package/dist/dev-ports.js +178 -0
  63. package/dist/dev-ports.js.map +1 -0
  64. package/dist/dev-ready-panel.d.ts +19 -0
  65. package/dist/dev-ready-panel.d.ts.map +1 -0
  66. package/dist/dev-ready-panel.js +49 -0
  67. package/dist/dev-ready-panel.js.map +1 -0
  68. package/dist/dev-session-lock.d.ts +25 -0
  69. package/dist/dev-session-lock.d.ts.map +1 -0
  70. package/dist/dev-session-lock.js +78 -0
  71. package/dist/dev-session-lock.js.map +1 -0
  72. package/dist/dev-session.d.ts +18 -5
  73. package/dist/dev-session.d.ts.map +1 -1
  74. package/dist/dev-session.js +91 -18
  75. package/dist/dev-session.js.map +1 -1
  76. package/dist/dev-shutdown.d.ts +20 -2
  77. package/dist/dev-shutdown.d.ts.map +1 -1
  78. package/dist/dev-shutdown.js +88 -10
  79. package/dist/dev-shutdown.js.map +1 -1
  80. package/dist/dev-task-colors.d.ts +3 -1
  81. package/dist/dev-task-colors.d.ts.map +1 -1
  82. package/dist/dev-task-colors.js +23 -3
  83. package/dist/dev-task-colors.js.map +1 -1
  84. package/dist/docker-runtime.js +1 -1
  85. package/dist/docker-runtime.js.map +1 -1
  86. package/dist/engine-push-output.d.ts +1 -0
  87. package/dist/engine-push-output.d.ts.map +1 -1
  88. package/dist/engine-push-output.js +4 -1
  89. package/dist/engine-push-output.js.map +1 -1
  90. package/dist/ensure-binary.d.ts.map +1 -1
  91. package/dist/ensure-binary.js +14 -2
  92. package/dist/ensure-binary.js.map +1 -1
  93. package/dist/ensure-component-binaries.d.ts +17 -0
  94. package/dist/ensure-component-binaries.d.ts.map +1 -0
  95. package/dist/ensure-component-binaries.js +45 -0
  96. package/dist/ensure-component-binaries.js.map +1 -0
  97. package/dist/ensure-dev-api-config.d.ts +3 -0
  98. package/dist/ensure-dev-api-config.d.ts.map +1 -0
  99. package/dist/ensure-dev-api-config.js +17 -0
  100. package/dist/ensure-dev-api-config.js.map +1 -0
  101. package/dist/env-file.d.ts +5 -0
  102. package/dist/env-file.d.ts.map +1 -0
  103. package/dist/env-file.js +33 -0
  104. package/dist/env-file.js.map +1 -0
  105. package/dist/init-dependency-versions.d.ts +13 -0
  106. package/dist/init-dependency-versions.d.ts.map +1 -0
  107. package/dist/init-dependency-versions.js +53 -0
  108. package/dist/init-dependency-versions.js.map +1 -0
  109. package/dist/init-package-json.d.ts +21 -0
  110. package/dist/init-package-json.d.ts.map +1 -0
  111. package/dist/init-package-json.js +46 -0
  112. package/dist/init-package-json.js.map +1 -0
  113. package/dist/postgres-ctl.d.ts.map +1 -1
  114. package/dist/postgres-ctl.js +2 -1
  115. package/dist/postgres-ctl.js.map +1 -1
  116. package/dist/required-host-components.d.ts +11 -0
  117. package/dist/required-host-components.d.ts.map +1 -0
  118. package/dist/required-host-components.js +25 -0
  119. package/dist/required-host-components.js.map +1 -0
  120. package/dist/resolve-api-url.d.ts +6 -0
  121. package/dist/resolve-api-url.d.ts.map +1 -0
  122. package/dist/resolve-api-url.js +39 -0
  123. package/dist/resolve-api-url.js.map +1 -0
  124. package/dist/rest-cache-admin.d.ts +33 -0
  125. package/dist/rest-cache-admin.d.ts.map +1 -0
  126. package/dist/rest-cache-admin.js +96 -0
  127. package/dist/rest-cache-admin.js.map +1 -0
  128. package/dist/scripts/postinstall.d.ts +4 -6
  129. package/dist/scripts/postinstall.d.ts.map +1 -1
  130. package/dist/scripts/postinstall.js +8 -41
  131. package/dist/scripts/postinstall.js.map +1 -1
  132. package/dist/self-host-compose.d.ts.map +1 -1
  133. package/dist/self-host-compose.js +24 -13
  134. package/dist/self-host-compose.js.map +1 -1
  135. package/dist/studio-dev-server.d.ts +4 -2
  136. package/dist/studio-dev-server.d.ts.map +1 -1
  137. package/dist/studio-dev-server.js +3 -2
  138. package/dist/studio-dev-server.js.map +1 -1
  139. package/dist/ui/brand.d.ts +2 -5
  140. package/dist/ui/brand.d.ts.map +1 -1
  141. package/dist/ui/brand.js +2 -7
  142. package/dist/ui/brand.js.map +1 -1
  143. package/dist/ui/clack-api.d.ts +44 -0
  144. package/dist/ui/clack-api.d.ts.map +1 -0
  145. package/dist/ui/clack-api.js +76 -0
  146. package/dist/ui/clack-api.js.map +1 -0
  147. package/dist/ui/clack.d.ts +14 -0
  148. package/dist/ui/clack.d.ts.map +1 -0
  149. package/dist/ui/clack.js +147 -0
  150. package/dist/ui/clack.js.map +1 -0
  151. package/dist/ui/components/FlowSpinner.d.ts +5 -0
  152. package/dist/ui/components/FlowSpinner.d.ts.map +1 -0
  153. package/dist/ui/components/FlowSpinner.js +8 -0
  154. package/dist/ui/components/FlowSpinner.js.map +1 -0
  155. package/dist/ui/components/LogoWordmark.d.ts +9 -0
  156. package/dist/ui/components/LogoWordmark.d.ts.map +1 -0
  157. package/dist/ui/components/LogoWordmark.js +17 -0
  158. package/dist/ui/components/LogoWordmark.js.map +1 -0
  159. package/dist/ui/components/PromptPanel.d.ts +5 -0
  160. package/dist/ui/components/PromptPanel.d.ts.map +1 -0
  161. package/dist/ui/components/PromptPanel.js +7 -0
  162. package/dist/ui/components/PromptPanel.js.map +1 -0
  163. package/dist/ui/components/SelectField.d.ts +11 -0
  164. package/dist/ui/components/SelectField.d.ts.map +1 -0
  165. package/dist/ui/components/SelectField.js +19 -0
  166. package/dist/ui/components/SelectField.js.map +1 -0
  167. package/dist/ui/confirm.d.ts +2 -1
  168. package/dist/ui/confirm.d.ts.map +1 -1
  169. package/dist/ui/confirm.js +4 -4
  170. package/dist/ui/confirm.js.map +1 -1
  171. package/dist/ui/dev/DevDashboard.d.ts +8 -0
  172. package/dist/ui/dev/DevDashboard.d.ts.map +1 -0
  173. package/dist/ui/dev/DevDashboard.js +151 -0
  174. package/dist/ui/dev/DevDashboard.js.map +1 -0
  175. package/dist/ui/dev/DevReadyPanelView.d.ts +7 -0
  176. package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -0
  177. package/dist/ui/dev/DevReadyPanelView.js +29 -0
  178. package/dist/ui/dev/DevReadyPanelView.js.map +1 -0
  179. package/dist/ui/fatal.d.ts.map +1 -1
  180. package/dist/ui/fatal.js +5 -11
  181. package/dist/ui/fatal.js.map +1 -1
  182. package/dist/ui/flows/FlowApp.d.ts +8 -0
  183. package/dist/ui/flows/FlowApp.d.ts.map +1 -0
  184. package/dist/ui/flows/FlowApp.js +42 -0
  185. package/dist/ui/flows/FlowApp.js.map +1 -0
  186. package/dist/ui/flows/prompt-fields.d.ts +48 -0
  187. package/dist/ui/flows/prompt-fields.d.ts.map +1 -0
  188. package/dist/ui/flows/prompt-fields.js +88 -0
  189. package/dist/ui/flows/prompt-fields.js.map +1 -0
  190. package/dist/ui/flows/types.d.ts +41 -0
  191. package/dist/ui/flows/types.d.ts.map +1 -0
  192. package/dist/ui/flows/types.js +2 -0
  193. package/dist/ui/flows/types.js.map +1 -0
  194. package/dist/ui/index.d.ts +1 -1
  195. package/dist/ui/index.js +1 -1
  196. package/dist/ui/messages.d.ts +1 -0
  197. package/dist/ui/messages.d.ts.map +1 -1
  198. package/dist/ui/messages.js +48 -8
  199. package/dist/ui/messages.js.map +1 -1
  200. package/dist/ui/progress.d.ts +5 -1
  201. package/dist/ui/progress.d.ts.map +1 -1
  202. package/dist/ui/progress.js +12 -3
  203. package/dist/ui/progress.js.map +1 -1
  204. package/dist/ui/prompts.d.ts +7 -5
  205. package/dist/ui/prompts.d.ts.map +1 -1
  206. package/dist/ui/prompts.js +15 -8
  207. package/dist/ui/prompts.js.map +1 -1
  208. package/dist/ui/runtime/cancel.d.ts +4 -0
  209. package/dist/ui/runtime/cancel.d.ts.map +1 -0
  210. package/dist/ui/runtime/cancel.js +6 -0
  211. package/dist/ui/runtime/cancel.js.map +1 -0
  212. package/dist/ui/runtime/command-chrome.d.ts +17 -0
  213. package/dist/ui/runtime/command-chrome.d.ts.map +1 -0
  214. package/dist/ui/runtime/command-chrome.js +123 -0
  215. package/dist/ui/runtime/command-chrome.js.map +1 -0
  216. package/dist/ui/runtime/dev-prompt-queue.d.ts +30 -0
  217. package/dist/ui/runtime/dev-prompt-queue.d.ts.map +1 -0
  218. package/dist/ui/runtime/dev-prompt-queue.js +57 -0
  219. package/dist/ui/runtime/dev-prompt-queue.js.map +1 -0
  220. package/dist/ui/runtime/flow-session.d.ts +4 -0
  221. package/dist/ui/runtime/flow-session.d.ts.map +1 -0
  222. package/dist/ui/runtime/flow-session.js +8 -0
  223. package/dist/ui/runtime/flow-session.js.map +1 -0
  224. package/dist/ui/runtime/run-clack-flow.d.ts +5 -0
  225. package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -0
  226. package/dist/ui/runtime/run-clack-flow.js +34 -0
  227. package/dist/ui/runtime/run-clack-flow.js.map +1 -0
  228. package/dist/ui/theme.d.ts +24 -0
  229. package/dist/ui/theme.d.ts.map +1 -0
  230. package/dist/ui/theme.js +34 -0
  231. package/dist/ui/theme.js.map +1 -0
  232. package/dist/valkey-sidecar.d.ts +17 -0
  233. package/dist/valkey-sidecar.d.ts.map +1 -0
  234. package/dist/valkey-sidecar.js +94 -0
  235. package/dist/valkey-sidecar.js.map +1 -0
  236. package/package.json +11 -3
  237. package/src/binary-cache.ts +61 -0
  238. package/src/binary-download-lock.ts +124 -0
  239. package/src/cli-package-version.ts +15 -0
  240. package/src/cli.ts +6 -2
  241. package/src/commands/add.ts +26 -19
  242. package/src/commands/admin.ts +380 -137
  243. package/src/commands/cache.ts +105 -5
  244. package/src/commands/dev.ts +29 -21
  245. package/src/commands/init.ts +217 -46
  246. package/src/commands/link-helpers.ts +30 -29
  247. package/src/commands/push.ts +3 -3
  248. package/src/compose-local-server-image.ts +68 -0
  249. package/src/compose-rename.ts +73 -0
  250. package/src/dev-compose.ts +175 -96
  251. package/src/dev-log-bus.ts +18 -0
  252. package/src/dev-log-filter.ts +42 -0
  253. package/src/dev-ports.ts +223 -0
  254. package/src/dev-ready-panel.ts +63 -0
  255. package/src/dev-session-lock.ts +98 -0
  256. package/src/dev-session.ts +98 -18
  257. package/src/dev-shutdown.ts +114 -9
  258. package/src/dev-task-colors.ts +22 -3
  259. package/src/docker-runtime.ts +1 -1
  260. package/src/engine-push-output.ts +5 -1
  261. package/src/ensure-binary.ts +21 -1
  262. package/src/ensure-component-binaries.ts +71 -0
  263. package/src/ensure-dev-api-config.ts +18 -0
  264. package/src/env-file.ts +37 -0
  265. package/src/init-dependency-versions.ts +69 -0
  266. package/src/init-package-json.ts +63 -0
  267. package/src/postgres-ctl.ts +2 -1
  268. package/src/required-host-components.ts +37 -0
  269. package/src/resolve-api-url.ts +43 -0
  270. package/src/rest-cache-admin.ts +135 -0
  271. package/src/scripts/postinstall.ts +9 -46
  272. package/src/self-host-compose.ts +23 -12
  273. package/src/studio-dev-server.ts +5 -4
  274. package/src/ui/README.md +13 -9
  275. package/src/ui/brand.ts +2 -8
  276. package/src/ui/clack-api.ts +123 -0
  277. package/src/ui/clack.ts +153 -0
  278. package/src/ui/components/FlowSpinner.tsx +15 -0
  279. package/src/ui/components/LogoWordmark.tsx +36 -0
  280. package/src/ui/components/PromptPanel.tsx +17 -0
  281. package/src/ui/components/SelectField.tsx +58 -0
  282. package/src/ui/confirm.ts +5 -4
  283. package/src/ui/dev/DevDashboard.tsx +213 -0
  284. package/src/ui/dev/DevReadyPanelView.tsx +67 -0
  285. package/src/ui/fatal.ts +5 -10
  286. package/src/ui/flows/FlowApp.tsx +67 -0
  287. package/src/ui/flows/prompt-fields.tsx +192 -0
  288. package/src/ui/flows/types.ts +50 -0
  289. package/src/ui/index.ts +1 -1
  290. package/src/ui/messages.ts +50 -8
  291. package/src/ui/progress.ts +14 -3
  292. package/src/ui/prompts.ts +17 -9
  293. package/src/ui/runtime/cancel.ts +6 -0
  294. package/src/ui/runtime/command-chrome.ts +147 -0
  295. package/src/ui/runtime/dev-prompt-queue.ts +84 -0
  296. package/src/ui/runtime/flow-session.ts +11 -0
  297. package/src/ui/runtime/run-clack-flow.tsx +52 -0
  298. package/src/ui/theme.ts +41 -0
  299. package/src/valkey-sidecar.ts +126 -0
  300. package/tests/admin-ensure.test.ts +59 -0
  301. package/tests/binary-cache-cloud-overrides.test.ts +2 -2
  302. package/tests/binary-download-lock.test.ts +41 -0
  303. package/tests/cli-help.test.ts +7 -4
  304. package/tests/cli-package-version.test.ts +15 -0
  305. package/tests/command-chrome-coverage.test.ts +149 -0
  306. package/tests/command-chrome-wrap.test.ts +51 -0
  307. package/tests/config.test.ts +8 -4
  308. package/tests/dev-ports.test.ts +41 -0
  309. package/tests/dev-ready-panel.test.ts +20 -0
  310. package/tests/dev-session-lock.test.ts +54 -0
  311. package/tests/dev-ui.test.ts +66 -3
  312. package/tests/docker-runtime.test.ts +9 -20
  313. package/tests/ensure-dev-api-config.test.ts +22 -0
  314. package/tests/init-dependency-versions.test.ts +62 -0
  315. package/tests/init-package-json.test.ts +18 -0
  316. package/tests/init.test.ts +30 -3
  317. package/tests/required-host-components.test.ts +51 -0
  318. package/tests/resolve-api-url.test.ts +23 -0
  319. package/tests/rest-cache-admin.test.ts +28 -0
  320. package/tests/runtime-contract.test.ts +12 -3
  321. package/tests/tsconfig.json +8 -2
  322. package/tests/ui-confirm.test.ts +13 -9
  323. package/tests/ui-messages.test.ts +4 -18
  324. package/tsconfig.json +1 -0
  325. package/tsconfig.tsbuildinfo +1 -1
  326. package/vitest.config.ts +3 -0
  327. package/dist/dev-tui.d.ts +0 -24
  328. package/dist/dev-tui.d.ts.map +0 -1
  329. package/dist/dev-tui.js +0 -188
  330. package/dist/dev-tui.js.map +0 -1
  331. package/src/dev-tui.ts +0 -232
@@ -1,36 +1,25 @@
1
1
  import type { Command } from "commander"
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
3
- import { resolve, join, dirname, basename } from "node:path"
4
- import { fileURLToPath } from "node:url"
3
+ import { resolve, join, basename } from "node:path"
5
4
  import { spawnSync } from "node:child_process"
6
- import * as p from "@clack/prompts"
5
+ import { p, runClackFlow } from "../ui/clack.js"
7
6
  import { ensureNotCancelled, printLogo } from "../ui/prompts.js"
8
7
  import { generateAndWriteKeys } from "./keys.js"
9
8
  import { file, error, info, plain, warn } from "../ui/messages.js"
10
9
  import { nextSteps } from "../ui/next-steps.js"
11
10
  import { probeDockerDaemon, reportDockerUnavailable } from "../docker-runtime.js"
12
-
13
- export { scaffold }
14
-
15
- // ─── Markers used by `supatype app add / remove` (app.ts) ────────────────────
16
- export const APP_COMPOSE_MARKER = " # ─── App service (run: supatype app add) ───"
17
- export const KONG_APP_MARKER = " # ─── App fallback route (run: supatype app add) ───"
18
-
19
- const CLI_PACKAGE_JSON = join(
20
- dirname(fileURLToPath(import.meta.url)),
21
- "..",
22
- "..",
23
- "package.json",
24
- )
25
-
26
- function cliPackageVersion(): string {
27
- try {
28
- const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8")) as { version?: string }
29
- return pkg.version ?? "0.1.0"
30
- } catch {
31
- return "0.1.0"
32
- }
33
- }
11
+ import { loadConfig } from "../config.js"
12
+ import {
13
+ ensureComponentBinaries,
14
+ reportComponentBinaryFailures,
15
+ } from "../ensure-component-binaries.js"
16
+ import { mergeSupatypePackageJson } from "../init-package-json.js"
17
+ import { cliPackageVersion } from "../cli-package-version.js"
18
+ import {
19
+ initDependencyVersionsFallback,
20
+ resolveInitDependencyVersions,
21
+ type InitDependencyVersions,
22
+ } from "../init-dependency-versions.js"
34
23
 
35
24
  // ─── Options model ─────────────────────────────────────────────────────────--
36
25
 
@@ -52,6 +41,8 @@ export interface ScaffoldOptions {
52
41
  projectName: string
53
42
  /** Local development runtime (docker recommended). */
54
43
  provider: "docker" | "native"
44
+ /** Host Kong port when provider is docker (unique per project). */
45
+ kongPort?: number
55
46
  productionTarget: ProductionTarget
56
47
  domain?: string
57
48
  /** ACME contact email for Let's Encrypt HTTPS (self-host + domain). */
@@ -64,6 +55,9 @@ export interface ScaffoldOptions {
64
55
  /** Object storage when deployed to production. */
65
56
  storageProduction: "local" | "s3"
66
57
  helloFunction: boolean
58
+ /** When set, written to .env for first dev/push to create the admin panel user. */
59
+ adminEmail?: string
60
+ adminPassword?: string
67
61
  }
68
62
 
69
63
  type StorageProvider = ScaffoldOptions["storageLocal"]
@@ -126,6 +120,9 @@ interface InitCliOptions {
126
120
  defaults?: boolean
127
121
  install: boolean
128
122
  keys: boolean
123
+ adminEmail?: string
124
+ adminPassword?: string
125
+ admin?: boolean
129
126
  }
130
127
 
131
128
  export function registerInit(program: Command): void {
@@ -140,6 +137,9 @@ export function registerInit(program: Command): void {
140
137
  .option("-y, --defaults", "Skip all prompts and use sensible defaults")
141
138
  .option("--no-install", "Do not run the package manager install step")
142
139
  .option("--no-keys", "Do not generate ANON_KEY / SERVICE_ROLE_KEY")
140
+ .option("--admin-email <email>", "First admin panel user email (written to .env)")
141
+ .option("--admin-password <password>", "First admin panel user password (written to .env)")
142
+ .option("--no-admin", "Do not configure a first admin user")
143
143
  .action(async (name: string | undefined, opts: InitCliOptions) => {
144
144
  const dir = name ? resolve(process.cwd(), name) : process.cwd()
145
145
 
@@ -164,6 +164,23 @@ export function registerInit(program: Command): void {
164
164
  install: true,
165
165
  generateKeys: true,
166
166
  }
167
+ if (result.provider === "docker") {
168
+ const { findNextFreePort } = await import("../dev-ports.js")
169
+ const { COMPOSE_DEV_KONG_PORT } = await import("../project-config.js")
170
+ result.kongPort = await findNextFreePort(COMPOSE_DEV_KONG_PORT)
171
+ }
172
+ if (!opts.admin && opts.adminEmail && opts.adminPassword) {
173
+ result.adminEmail = opts.adminEmail
174
+ result.adminPassword = opts.adminPassword
175
+ }
176
+ }
177
+
178
+ if (opts.admin === false) {
179
+ delete result.adminEmail
180
+ delete result.adminPassword
181
+ } else if (opts.adminEmail && opts.adminPassword) {
182
+ result.adminEmail = opts.adminEmail
183
+ result.adminPassword = opts.adminPassword
167
184
  }
168
185
 
169
186
  // CLI flags override wizard / default action choices.
@@ -172,9 +189,23 @@ export function registerInit(program: Command): void {
172
189
 
173
190
  if (name) mkdirSync(dir, { recursive: true })
174
191
 
175
- scaffold(dir, result)
192
+ const deps = await resolveInitDependencyVersions()
193
+ const runningCli = cliPackageVersion()
194
+ if (deps.cli !== runningCli || deps.types !== runningCli) {
195
+ info(
196
+ `Using published npm versions: @supatype/cli ^${deps.cli}, @supatype/types ^${deps.types}`,
197
+ )
198
+ }
199
+
200
+ scaffold(dir, result, deps)
201
+
202
+ const installOk = doInstall ? runInstall(dir, result.packageManager) : true
203
+
204
+ const binariesReady = installOk ? await ensureInitBinaries(dir) : false
205
+ if (doInstall && !installOk) {
206
+ warn("Skipping component binary check until dependencies install successfully.")
207
+ }
176
208
 
177
- if (doInstall) runInstall(dir, result.packageManager)
178
209
  const keysGenerated = doKeys ? writeKeys(dir) : false
179
210
 
180
211
  warnDockerUnavailableForProvider(result.provider)
@@ -182,8 +213,9 @@ export function registerInit(program: Command): void {
182
213
  printNextSteps({
183
214
  name,
184
215
  result,
185
- installed: doInstall,
216
+ installed: doInstall && installOk,
186
217
  keysGenerated,
218
+ binariesReady,
187
219
  })
188
220
  })
189
221
  }
@@ -194,6 +226,7 @@ async function runWizard(
194
226
  defaultName: string,
195
227
  defaultTarget: ProductionTarget,
196
228
  ): Promise<WizardResult> {
229
+ return runClackFlow(async () => {
197
230
  p.intro("Create a new Supatype project")
198
231
 
199
232
  const projectName = ensureNotCancelled(
@@ -262,6 +295,12 @@ async function runWizard(
262
295
  }),
263
296
  )
264
297
 
298
+ let kongPort: number | undefined
299
+ if (provider === "docker") {
300
+ const { promptKongPortChoice } = await import("../dev-ports.js")
301
+ kongPort = await promptKongPortChoice()
302
+ }
303
+
265
304
  const schemaPath = ensureNotCancelled(
266
305
  await p.text({
267
306
  message: "Where should your schema live?",
@@ -322,11 +361,43 @@ async function runWizard(
322
361
  }),
323
362
  )
324
363
 
364
+ let adminEmail: string | undefined
365
+ let adminPassword: string | undefined
366
+ const createAdmin = ensureNotCancelled(
367
+ await p.confirm({
368
+ message: "Create an admin user for /admin?",
369
+ initialValue: true,
370
+ }),
371
+ )
372
+ if (createAdmin) {
373
+ adminEmail =
374
+ ensureNotCancelled(
375
+ await p.text({
376
+ message: "Admin email",
377
+ placeholder: "you@example.com",
378
+ defaultValue: "",
379
+ }),
380
+ ).trim() || undefined
381
+ if (adminEmail) {
382
+ adminPassword =
383
+ ensureNotCancelled(
384
+ await p.text({
385
+ message: "Admin password (min 8 chars)",
386
+ defaultValue: "",
387
+ }),
388
+ ).trim() || undefined
389
+ if (adminPassword && adminPassword.length < 8) {
390
+ adminPassword = undefined
391
+ }
392
+ }
393
+ }
394
+
325
395
  p.outro("Setting up your project...")
326
396
 
327
397
  return {
328
398
  projectName,
329
399
  provider,
400
+ ...(kongPort !== undefined ? { kongPort } : {}),
330
401
  productionTarget,
331
402
  ...(domain !== undefined ? { domain } : {}),
332
403
  ...(tlsEmail !== undefined ? { tlsEmail } : {}),
@@ -339,7 +410,9 @@ async function runWizard(
339
410
  packageManager,
340
411
  install,
341
412
  generateKeys,
413
+ ...(adminEmail && adminPassword ? { adminEmail, adminPassword } : {}),
342
414
  }
415
+ })
343
416
  }
344
417
 
345
418
  async function promptApp(): Promise<ScaffoldAppOptions> {
@@ -418,7 +491,7 @@ function detectInvokingPackageManager(): PackageManager {
418
491
  return "npm"
419
492
  }
420
493
 
421
- function runInstall(dir: string, pm: PackageManager): void {
494
+ function runInstall(dir: string, pm: PackageManager): boolean {
422
495
  info(`Installing dependencies with ${pm}...`)
423
496
  const res = spawnSync(pm, ["install"], {
424
497
  cwd: dir,
@@ -427,6 +500,29 @@ function runInstall(dir: string, pm: PackageManager): void {
427
500
  })
428
501
  if (res.status !== 0 || res.error) {
429
502
  warn(`Dependency install did not complete (run "${pm} install" manually).`)
503
+ return false
504
+ }
505
+ return true
506
+ }
507
+
508
+ async function ensureInitBinaries(dir: string): Promise<boolean> {
509
+ const previousCwd = process.cwd()
510
+ try {
511
+ process.chdir(dir)
512
+ const config = loadConfig(dir)
513
+ const result = await ensureComponentBinaries(config, dir)
514
+ if (!result.ok) {
515
+ reportComponentBinaryFailures(result.failures)
516
+ return false
517
+ }
518
+ return true
519
+ } catch (err) {
520
+ const message = err instanceof Error ? err.message : String(err)
521
+ warn(`Could not verify component binaries: ${message}`)
522
+ warn("Run manually from your project directory: supatype update")
523
+ return false
524
+ } finally {
525
+ process.chdir(previousCwd)
430
526
  }
431
527
  }
432
528
 
@@ -441,7 +537,11 @@ function writeKeys(dir: string): boolean {
441
537
 
442
538
  // ─── Scaffold ──────────────────────────────────────────────────────────────--
443
539
 
444
- function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
540
+ export function scaffold(
541
+ dir: string,
542
+ optsOrName: ScaffoldOptions | string,
543
+ deps: InitDependencyVersions = initDependencyVersionsFallback(),
544
+ ): void {
445
545
  const opts =
446
546
  typeof optsOrName === "string" ? defaultScaffoldOptions(optsOrName) : optsOrName
447
547
  const write = (rel: string, content: string) => {
@@ -453,9 +553,17 @@ function scaffold(dir: string, optsOrName: ScaffoldOptions | string): void {
453
553
 
454
554
  const pkgPath = join(dir, "package.json")
455
555
  if (!existsSync(pkgPath)) {
456
- write("package.json", packageJsonTemplate(opts, cliPackageVersion()))
556
+ write("package.json", packageJsonTemplate(opts, deps))
457
557
  } else {
458
- file("skipped", "package.json (already exists)")
558
+ try {
559
+ const existing = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>
560
+ const merged = mergeSupatypePackageJson(existing, opts, deps)
561
+ writeFileSync(pkgPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8")
562
+ file("updated", "package.json (added Supatype dependencies)")
563
+ } catch {
564
+ warn("Could not merge into package.json — add @supatype/cli and @supatype/types manually.")
565
+ file("skipped", "package.json (invalid JSON)")
566
+ }
459
567
  }
460
568
 
461
569
  write("supatype.config.ts", tsConfigTemplate(opts))
@@ -498,8 +606,13 @@ function scaffoldAppAssets(
498
606
 
499
607
  if (opts.app.mode === "static") {
500
608
  const staticRel = staticDirRelative(opts.app.staticDir)
609
+ if (opts.app.viteDevUrl && opts.productionTarget !== "later") {
610
+ write("dist/index.html", holding)
611
+ scaffoldVite(opts, write, holding)
612
+ return
613
+ }
501
614
  write(`${staticRel}/index.html`, holding)
502
- if (hasVite) scaffoldVite(opts, write, holding)
615
+ if (opts.app.viteDevUrl) scaffoldVite(opts, write, holding)
503
616
  return
504
617
  }
505
618
 
@@ -542,7 +655,7 @@ function scaffoldHelloFunction(
542
655
 
543
656
  // ─── Templates ───────────────────────────────────────────────────────────────
544
657
 
545
- function packageJsonTemplate(opts: ScaffoldOptions, cliVersion: string): string {
658
+ function packageJsonTemplate(opts: ScaffoldOptions, deps: InitDependencyVersions): string {
546
659
  const scripts: string[] = [
547
660
  ` "dev": "supatype dev"`,
548
661
  ` "push": "supatype push"`,
@@ -566,8 +679,8 @@ function packageJsonTemplate(opts: ScaffoldOptions, cliVersion: string): string
566
679
  ${scripts.join(",\n")}
567
680
  },
568
681
  "dependencies": {
569
- "@supatype/cli": "^${cliVersion}",
570
- "@supatype/types": "^${cliVersion}"
682
+ "@supatype/cli": "^${deps.cli}",
683
+ "@supatype/types": "^${deps.types}"
571
684
  },
572
685
  "devDependencies": {
573
686
  ${devDeps.join(",\n")}
@@ -634,20 +747,49 @@ function localConfigTemplate(app: ScaffoldAppOptions): string {
634
747
  `const localConfig: Partial<SupatypeConfig> = {`,
635
748
  ` server: { mode: "dev" },`,
636
749
  ]
637
- if (app.mode === "proxy") {
638
- lines.push(` app: {`)
639
- lines.push(` mode: "proxy",`)
640
- lines.push(` upstream: "${app.upstream ?? "http://localhost:3000"}",`)
641
- lines.push(` start: "${app.start ?? "dev"}",`)
642
- if (app.viteDevUrl) lines.push(` vite_dev_url: "${app.viteDevUrl}",`)
643
- lines.push(` },`)
750
+ const localApp = localDevAppConfigLines(app)
751
+ if (localApp.length > 0) {
752
+ lines.push(...localApp)
644
753
  }
645
754
  lines.push(`}`, ``, `export default localConfig`, ``)
646
755
  return lines.join("\n")
647
756
  }
648
757
 
758
+ /** App block for supatype.local.config.ts (proxy + Vite during local dev). */
759
+ function localDevAppConfigLines(app: ScaffoldAppOptions): string[] {
760
+ if (app.mode === "proxy") {
761
+ return [
762
+ ` app: {`,
763
+ ` mode: "proxy",`,
764
+ ` upstream: "${app.upstream ?? "http://localhost:3000"}",`,
765
+ ` start: "${app.start ?? "dev"}",`,
766
+ ...(app.viteDevUrl ? [` vite_dev_url: "${app.viteDevUrl}",`] : []),
767
+ ` },`,
768
+ ]
769
+ }
770
+ if (app.viteDevUrl) {
771
+ return [
772
+ ` app: {`,
773
+ ` mode: "proxy",`,
774
+ ` upstream: "${app.viteDevUrl}",`,
775
+ ` start: "vite",`,
776
+ ` vite_dev_url: "${app.viteDevUrl}",`,
777
+ ` },`,
778
+ ]
779
+ }
780
+ return []
781
+ }
782
+
649
783
  function appConfigLines(app: ScaffoldAppOptions, productionTarget: ProductionTarget): string[] {
650
784
  if (app.mode === "static") {
785
+ if (productionTarget !== "later" && app.viteDevUrl) {
786
+ return [
787
+ ` app: {`,
788
+ ` mode: "static",`,
789
+ ` static_dir: "./dist", // production build output`,
790
+ ` },`,
791
+ ]
792
+ }
651
793
  const out = [
652
794
  ` app: {`,
653
795
  ` mode: "static",`,
@@ -707,7 +849,7 @@ function holdingPageTemplate(projectName: string): string {
707
849
  <meta charset="UTF-8" />
708
850
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
709
851
  <title>${name} — Supatype</title>
710
- <meta name="description" content="A Supatype project. Define your types — we generate your backend." />
852
+ <meta name="description" content="A Supatype project. Define your types — we generate your platform." />
711
853
  <link rel="preconnect" href="https://fonts.googleapis.com" />
712
854
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
713
855
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
@@ -842,6 +984,8 @@ export default defineConfig({
842
984
  host: "127.0.0.1",
843
985
  port: ${port},
844
986
  strictPort: true,
987
+ // Docker Compose app proxy reaches the host as host.docker.internal.
988
+ allowedHosts: ["127.0.0.1", "localhost", "host.docker.internal"],
845
989
  },
846
990
  })
847
991
  `
@@ -910,9 +1054,27 @@ SERVICE_ROLE_KEY=`)
910
1054
  sections.push(`# Site URL (used by GoTrue for email redirects)
911
1055
  SITE_URL=http://localhost:3000`)
912
1056
 
1057
+ if (opts.provider === "docker" && opts.kongPort !== undefined) {
1058
+ const apiUrl = `http://localhost:${opts.kongPort}`
1059
+ sections.push(
1060
+ `# Local API gateway (Kong) — unique per project so multiple stacks can run concurrently
1061
+ SUPATYPE_KONG_PORT=${opts.kongPort}
1062
+ PUBLIC_SUPATYPE_URL=${apiUrl}
1063
+ API_EXTERNAL_URL=${apiUrl}`,
1064
+ )
1065
+ }
1066
+
913
1067
  sections.push(emailEnvSection(opts.email, opts.projectName))
914
1068
  sections.push(storageEnvSections(opts.storageLocal, opts.storageProduction))
915
1069
 
1070
+ if (opts.adminEmail && opts.adminPassword) {
1071
+ sections.push(
1072
+ `# First admin for /admin — consumed on first supatype dev or push (password removed after use)
1073
+ SUPATYPE_ADMIN_EMAIL=${opts.adminEmail}
1074
+ SUPATYPE_ADMIN_PASSWORD=${opts.adminPassword}`,
1075
+ )
1076
+ }
1077
+
916
1078
  sections.push(
917
1079
  `# Self-host compose uses the same DATABASE_URL when Postgres is published on localhost:5432`,
918
1080
  )
@@ -1100,14 +1262,23 @@ function printNextSteps(args: {
1100
1262
  result: WizardResult
1101
1263
  installed: boolean
1102
1264
  keysGenerated: boolean
1265
+ binariesReady: boolean
1103
1266
  }): void {
1104
- const { name, result, installed, keysGenerated } = args
1267
+ const { name, result, installed, keysGenerated, binariesReady } = args
1105
1268
  const steps: string[] = []
1106
1269
  if (name) steps.push(`cd ${name}`)
1107
1270
  if (!installed) steps.push(`${result.packageManager} install`)
1271
+ if (!binariesReady) steps.push("supatype update # download component binaries")
1108
1272
  if (!keysGenerated) steps.push("supatype keys")
1109
- steps.push("supatype dev # Docker Compose stack (Kong :18473)")
1273
+ const kongHint =
1274
+ result.provider === "docker" && result.kongPort !== undefined
1275
+ ? `supatype dev # Docker Compose stack (Kong :${result.kongPort})`
1276
+ : "supatype dev # Docker Compose stack (Kong :18473)"
1277
+ steps.push(kongHint)
1110
1278
  steps.push("supatype push # apply schema + generate types")
1279
+ if (result.adminEmail) {
1280
+ steps.push(" # first admin user is created on dev or push")
1281
+ }
1111
1282
  if (result.helloFunction) {
1112
1283
  steps.push("supatype functions serve # run edge functions locally")
1113
1284
  }
@@ -12,7 +12,7 @@ import { targetFetch } from "../target-client.js"
12
12
  import { error, info, plain } from "../ui/messages.js"
13
13
  import { nextSteps } from "../ui/next-steps.js"
14
14
  import { isInteractive } from "../ui/interactive.js"
15
- import { ensureNotCancelled, printLogo, clack as p } from "../ui/prompts.js"
15
+ import { ensureNotCancelled, p, runClackFlow } from "../ui/prompts.js"
16
16
 
17
17
  function resolveLinkToken(opts: {
18
18
  token?: string
@@ -145,35 +145,36 @@ export async function runLinkAction(opts: {
145
145
  let url = opts.url
146
146
 
147
147
  if (isInteractive() && !project && !url) {
148
- printLogo()
149
- p.intro("Link this project")
150
- const targetKind = ensureNotCancelled(
151
- await p.select<"cloud" | "self-host">({
152
- message: "Link target",
153
- options: [
154
- { value: "cloud", label: "Supatype Cloud", hint: "managed project on supatype.com" },
155
- { value: "self-host", label: "Self-host", hint: "your Kong gateway URL" },
156
- ],
157
- }),
158
- )
159
- if (targetKind === "cloud") {
160
- project = ensureNotCancelled(
161
- await p.text({
162
- message: "Cloud project slug",
163
- placeholder: projectRef,
164
- defaultValue: projectRef,
148
+ await runClackFlow(async () => {
149
+ p.intro("Link this project")
150
+ const targetKind = ensureNotCancelled(
151
+ await p.select<"cloud" | "self-host">({
152
+ message: "Link target",
153
+ options: [
154
+ { value: "cloud", label: "Supatype Cloud", hint: "managed project on supatype.com" },
155
+ { value: "self-host", label: "Self-host", hint: "your Kong gateway URL" },
156
+ ],
165
157
  }),
166
- ).trim()
167
- } else {
168
- url = ensureNotCancelled(
169
- await p.text({
170
- message: "Kong gateway URL",
171
- placeholder: "https://api.example.com",
172
- defaultValue: "http://localhost:18473",
173
- }),
174
- ).trim()
175
- }
176
- p.outro("Linking...")
158
+ )
159
+ if (targetKind === "cloud") {
160
+ project = ensureNotCancelled(
161
+ await p.text({
162
+ message: "Cloud project slug",
163
+ placeholder: projectRef,
164
+ defaultValue: projectRef,
165
+ }),
166
+ ).trim()
167
+ } else {
168
+ url = ensureNotCancelled(
169
+ await p.text({
170
+ message: "Kong gateway URL",
171
+ placeholder: "https://api.example.com",
172
+ defaultValue: "http://localhost:18473",
173
+ }),
174
+ ).trim()
175
+ }
176
+ p.outro("Linking...")
177
+ })
177
178
  }
178
179
 
179
180
  const token = resolveLinkToken(opts)
@@ -8,7 +8,7 @@ import { printDiffOperations, printDiffWarnings } from "../diff-output.js"
8
8
  import { signJwt } from "../jwt.js"
9
9
  import { provisionBucketsFromAst } from "../storage-provision.js"
10
10
  import type { ExtractedSchemaAstV2 } from "../schema-ast-v2.js"
11
- import { promptFirstAdminUser } from "./admin.js"
11
+ import { ensureFirstAdminUser } from "./admin.js"
12
12
  import { withAdminRoles } from "../studio-admin-roles.js"
13
13
  import { restoreSystemRelationTargets } from "../restore-system-relation-targets.js"
14
14
  import type { SupatypeProjectConfig } from "../project-config.js"
@@ -145,8 +145,8 @@ async function pushViaTarget(
145
145
 
146
146
  if (target.mode === "direct" || target.mode === "local") {
147
147
  await writeLocalAdminConfig(ast, config)
148
- if (ops.length > 0 && target.databaseUrl) {
149
- await promptFirstAdminUser(target.databaseUrl)
148
+ if (target.databaseUrl) {
149
+ await ensureFirstAdminUser(target.databaseUrl, { cwd })
150
150
  }
151
151
  await generateTypesLocal(ast, config)
152
152
  await provisionLocalStorage(ast, config)
@@ -0,0 +1,68 @@
1
+ import { existsSync } from "node:fs"
2
+ import { dirname, isAbsolute, join, resolve } from "node:path"
3
+ import { spawnSync } from "node:child_process"
4
+ import { VERSION_PIN_LOCAL } from "./binary-cache.js"
5
+ import { appendStackOutput, getActiveDevSession } from "./dev-session.js"
6
+ import { requireDockerDaemon } from "./docker-runtime.js"
7
+ import type { SupatypeProjectConfig } from "./project-config.js"
8
+
9
+ /** Docker tag for compose dev when `versions.server` is `local`. */
10
+ export const LOCAL_SERVER_DOCKER_IMAGE = "supatype/server:local-dev"
11
+
12
+ /** Find the server repo root (directory containing `Dockerfile`) from an overrides.server binary path. */
13
+ export function resolveServerSourceRoot(serverOverride: string, cwd: string): string | null {
14
+ const binaryPath = isAbsolute(serverOverride) ? serverOverride : resolve(cwd, serverOverride)
15
+ let dir = dirname(binaryPath)
16
+ for (let depth = 0; depth < 5; depth++) {
17
+ if (existsSync(join(dir, "Dockerfile"))) return dir
18
+ const parent = dirname(dir)
19
+ if (parent === dir) break
20
+ dir = parent
21
+ }
22
+ return null
23
+ }
24
+
25
+ /**
26
+ * When `versions.server` is `local`, build a Linux server image from the source tree
27
+ * next to `overrides.server` so compose dev matches the contributor's binary.
28
+ */
29
+ export async function ensureLocalServerDockerImage(
30
+ cwd: string,
31
+ config: SupatypeProjectConfig,
32
+ brand?: { intro?: string },
33
+ ): Promise<string | undefined> {
34
+ if (config.versions?.server !== VERSION_PIN_LOCAL) return undefined
35
+
36
+ const override = config.overrides?.server?.trim()
37
+ if (!override) return undefined
38
+
39
+ const root = resolveServerSourceRoot(override, cwd)
40
+ if (!root) {
41
+ console.warn(
42
+ `[supatype] ⚠ versions.server is "local" but no Dockerfile found near ${override}. ` +
43
+ "Compose will use the default supatype/server:latest image.",
44
+ )
45
+ return undefined
46
+ }
47
+
48
+ requireDockerDaemon(brand?.intro ? { brand: { intro: brand.intro } } : undefined)
49
+ const dockerfile = join(root, "Dockerfile")
50
+ console.log(`[supatype] Building local server Docker image from ${root}…`)
51
+ const result = spawnSync(
52
+ "docker",
53
+ ["build", "--progress=plain", "-t", LOCAL_SERVER_DOCKER_IMAGE, "-f", dockerfile, root],
54
+ { encoding: "utf8", stdio: "pipe" },
55
+ )
56
+ const tui = getActiveDevSession()?.isTui() ?? false
57
+ if (!tui) {
58
+ appendStackOutput(result.stdout, "log")
59
+ appendStackOutput(result.stderr, result.status === 0 ? "log" : "error")
60
+ } else if (result.status !== 0) {
61
+ appendStackOutput(result.stderr || result.stdout || "docker build failed", "error")
62
+ }
63
+ if (result.status !== 0) {
64
+ throw new Error(`docker build failed for local server (exit ${result.status ?? "unknown"})`)
65
+ }
66
+ console.log(`[supatype] Local server image ready: ${LOCAL_SERVER_DOCKER_IMAGE}`)
67
+ return LOCAL_SERVER_DOCKER_IMAGE
68
+ }