@supatype/cli 0.1.0-alpha.9 → 0.1.1

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 (407) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +285 -69
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -1
  6. package/dist/app/framework.js +1 -3
  7. package/dist/app/framework.js.map +1 -1
  8. package/dist/app/proxy-dev-app.d.ts +14 -0
  9. package/dist/app/proxy-dev-app.d.ts.map +1 -1
  10. package/dist/app/proxy-dev-app.js +110 -6
  11. package/dist/app/proxy-dev-app.js.map +1 -1
  12. package/dist/app-config.d.ts +10 -0
  13. package/dist/app-config.d.ts.map +1 -1
  14. package/dist/app-config.js +72 -0
  15. package/dist/app-config.js.map +1 -1
  16. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  17. package/dist/binary-cache.d.ts +19 -7
  18. package/dist/binary-cache.d.ts.map +1 -1
  19. package/dist/binary-cache.js +92 -46
  20. package/dist/binary-cache.js.map +1 -1
  21. package/dist/cli.d.ts +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +17 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/add.d.ts +3 -0
  26. package/dist/commands/add.d.ts.map +1 -0
  27. package/dist/commands/add.js +86 -0
  28. package/dist/commands/add.js.map +1 -0
  29. package/dist/commands/admin.d.ts +28 -1
  30. package/dist/commands/admin.d.ts.map +1 -1
  31. package/dist/commands/admin.js +297 -149
  32. package/dist/commands/admin.js.map +1 -1
  33. package/dist/commands/adopt.d.ts +3 -0
  34. package/dist/commands/adopt.d.ts.map +1 -0
  35. package/dist/commands/adopt.js +55 -0
  36. package/dist/commands/adopt.js.map +1 -0
  37. package/dist/commands/app.d.ts.map +1 -1
  38. package/dist/commands/app.js +20 -17
  39. package/dist/commands/app.js.map +1 -1
  40. package/dist/commands/cache.d.ts.map +1 -1
  41. package/dist/commands/cache.js +11 -10
  42. package/dist/commands/cache.js.map +1 -1
  43. package/dist/commands/cloud.d.ts +4 -9
  44. package/dist/commands/cloud.d.ts.map +1 -1
  45. package/dist/commands/cloud.js +75 -125
  46. package/dist/commands/cloud.js.map +1 -1
  47. package/dist/commands/db.d.ts.map +1 -1
  48. package/dist/commands/db.js +37 -58
  49. package/dist/commands/db.js.map +1 -1
  50. package/dist/commands/deploy.d.ts.map +1 -1
  51. package/dist/commands/deploy.js +140 -96
  52. package/dist/commands/deploy.js.map +1 -1
  53. package/dist/commands/dev.d.ts.map +1 -1
  54. package/dist/commands/dev.js +74 -39
  55. package/dist/commands/dev.js.map +1 -1
  56. package/dist/commands/diff.d.ts.map +1 -1
  57. package/dist/commands/diff.js +39 -39
  58. package/dist/commands/diff.js.map +1 -1
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.d.ts.map +1 -0
  61. package/dist/commands/doctor.js +78 -0
  62. package/dist/commands/doctor.js.map +1 -0
  63. package/dist/commands/engine.d.ts.map +1 -1
  64. package/dist/commands/engine.js +5 -4
  65. package/dist/commands/engine.js.map +1 -1
  66. package/dist/commands/functions.d.ts.map +1 -1
  67. package/dist/commands/functions.js +172 -119
  68. package/dist/commands/functions.js.map +1 -1
  69. package/dist/commands/generate.d.ts.map +1 -1
  70. package/dist/commands/generate.js +5 -4
  71. package/dist/commands/generate.js.map +1 -1
  72. package/dist/commands/init.d.ts +35 -1
  73. package/dist/commands/init.d.ts.map +1 -1
  74. package/dist/commands/init.js +883 -107
  75. package/dist/commands/init.js.map +1 -1
  76. package/dist/commands/introspect.d.ts +3 -0
  77. package/dist/commands/introspect.d.ts.map +1 -0
  78. package/dist/commands/introspect.js +35 -0
  79. package/dist/commands/introspect.js.map +1 -0
  80. package/dist/commands/keys.d.ts +15 -1
  81. package/dist/commands/keys.d.ts.map +1 -1
  82. package/dist/commands/keys.js +46 -10
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/link-helpers.d.ts +15 -0
  85. package/dist/commands/link-helpers.d.ts.map +1 -0
  86. package/dist/commands/link-helpers.js +225 -0
  87. package/dist/commands/link-helpers.js.map +1 -0
  88. package/dist/commands/logs.d.ts.map +1 -1
  89. package/dist/commands/logs.js +5 -4
  90. package/dist/commands/logs.js.map +1 -1
  91. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  92. package/dist/commands/migrate-from-v1.js +3 -2
  93. package/dist/commands/migrate-from-v1.js.map +1 -1
  94. package/dist/commands/migrate.d.ts.map +1 -1
  95. package/dist/commands/migrate.js +119 -26
  96. package/dist/commands/migrate.js.map +1 -1
  97. package/dist/commands/pg.d.ts.map +1 -1
  98. package/dist/commands/pg.js +11 -12
  99. package/dist/commands/pg.js.map +1 -1
  100. package/dist/commands/plugins.d.ts.map +1 -1
  101. package/dist/commands/plugins.js +55 -46
  102. package/dist/commands/plugins.js.map +1 -1
  103. package/dist/commands/pull.d.ts.map +1 -1
  104. package/dist/commands/pull.js +33 -5
  105. package/dist/commands/pull.js.map +1 -1
  106. package/dist/commands/push.d.ts.map +1 -1
  107. package/dist/commands/push.js +111 -138
  108. package/dist/commands/push.js.map +1 -1
  109. package/dist/commands/seed.d.ts.map +1 -1
  110. package/dist/commands/seed.js +4 -3
  111. package/dist/commands/seed.js.map +1 -1
  112. package/dist/commands/self-host.d.ts +2 -2
  113. package/dist/commands/self-host.d.ts.map +1 -1
  114. package/dist/commands/self-host.js +65 -50
  115. package/dist/commands/self-host.js.map +1 -1
  116. package/dist/commands/self-update.d.ts.map +1 -1
  117. package/dist/commands/self-update.js +3 -2
  118. package/dist/commands/self-update.js.map +1 -1
  119. package/dist/commands/status.d.ts +1 -1
  120. package/dist/commands/status.d.ts.map +1 -1
  121. package/dist/commands/status.js +95 -29
  122. package/dist/commands/status.js.map +1 -1
  123. package/dist/commands/types.d.ts.map +1 -1
  124. package/dist/commands/types.js +3 -2
  125. package/dist/commands/types.js.map +1 -1
  126. package/dist/commands/update.d.ts.map +1 -1
  127. package/dist/commands/update.js +54 -21
  128. package/dist/commands/update.js.map +1 -1
  129. package/dist/compose-rename.d.ts +10 -0
  130. package/dist/compose-rename.d.ts.map +1 -0
  131. package/dist/compose-rename.js +67 -0
  132. package/dist/compose-rename.js.map +1 -0
  133. package/dist/config.d.ts +2 -1
  134. package/dist/config.d.ts.map +1 -1
  135. package/dist/config.js.map +1 -1
  136. package/dist/dev-compose.d.ts +26 -0
  137. package/dist/dev-compose.d.ts.map +1 -1
  138. package/dist/dev-compose.js +357 -79
  139. package/dist/dev-compose.js.map +1 -1
  140. package/dist/dev-log-bus.d.ts +30 -0
  141. package/dist/dev-log-bus.d.ts.map +1 -0
  142. package/dist/dev-log-bus.js +87 -0
  143. package/dist/dev-log-bus.js.map +1 -0
  144. package/dist/dev-log-filter.d.ts +10 -0
  145. package/dist/dev-log-filter.d.ts.map +1 -0
  146. package/dist/dev-log-filter.js +36 -0
  147. package/dist/dev-log-filter.js.map +1 -0
  148. package/dist/dev-logo.d.ts +12 -0
  149. package/dist/dev-logo.d.ts.map +1 -0
  150. package/dist/dev-logo.js +56 -0
  151. package/dist/dev-logo.js.map +1 -0
  152. package/dist/dev-ports.d.ts +27 -0
  153. package/dist/dev-ports.d.ts.map +1 -0
  154. package/dist/dev-ports.js +171 -0
  155. package/dist/dev-ports.js.map +1 -0
  156. package/dist/dev-session-lock.d.ts +25 -0
  157. package/dist/dev-session-lock.d.ts.map +1 -0
  158. package/dist/dev-session-lock.js +81 -0
  159. package/dist/dev-session-lock.js.map +1 -0
  160. package/dist/dev-session.d.ts +26 -0
  161. package/dist/dev-session.d.ts.map +1 -0
  162. package/dist/dev-session.js +106 -0
  163. package/dist/dev-session.js.map +1 -0
  164. package/dist/dev-shutdown.d.ts +25 -0
  165. package/dist/dev-shutdown.d.ts.map +1 -0
  166. package/dist/dev-shutdown.js +114 -0
  167. package/dist/dev-shutdown.js.map +1 -0
  168. package/dist/dev-task-colors.d.ts +13 -0
  169. package/dist/dev-task-colors.d.ts.map +1 -0
  170. package/dist/dev-task-colors.js +43 -0
  171. package/dist/dev-task-colors.js.map +1 -0
  172. package/dist/dev-tui.d.ts +24 -0
  173. package/dist/dev-tui.d.ts.map +1 -0
  174. package/dist/dev-tui.js +188 -0
  175. package/dist/dev-tui.js.map +1 -0
  176. package/dist/diff-output.d.ts +5 -1
  177. package/dist/diff-output.d.ts.map +1 -1
  178. package/dist/diff-output.js +69 -0
  179. package/dist/diff-output.js.map +1 -1
  180. package/dist/docker-runtime.d.ts +30 -0
  181. package/dist/docker-runtime.d.ts.map +1 -0
  182. package/dist/docker-runtime.js +118 -0
  183. package/dist/docker-runtime.js.map +1 -0
  184. package/dist/engine-client.d.ts +10 -1
  185. package/dist/engine-client.d.ts.map +1 -1
  186. package/dist/engine-client.js +76 -17
  187. package/dist/engine-client.js.map +1 -1
  188. package/dist/engine-push-output.d.ts +17 -0
  189. package/dist/engine-push-output.d.ts.map +1 -0
  190. package/dist/engine-push-output.js +64 -0
  191. package/dist/engine-push-output.js.map +1 -0
  192. package/dist/ensure-binary.js +2 -2
  193. package/dist/ensure-binary.js.map +1 -1
  194. package/dist/env-file.d.ts +5 -0
  195. package/dist/env-file.d.ts.map +1 -0
  196. package/dist/env-file.js +33 -0
  197. package/dist/env-file.js.map +1 -0
  198. package/dist/gitignore.d.ts +8 -0
  199. package/dist/gitignore.d.ts.map +1 -0
  200. package/dist/gitignore.js +41 -0
  201. package/dist/gitignore.js.map +1 -0
  202. package/dist/kong-config.d.ts +9 -0
  203. package/dist/kong-config.d.ts.map +1 -1
  204. package/dist/kong-config.js +18 -1
  205. package/dist/kong-config.js.map +1 -1
  206. package/dist/link.d.ts +66 -0
  207. package/dist/link.d.ts.map +1 -0
  208. package/dist/link.js +160 -0
  209. package/dist/link.js.map +1 -0
  210. package/dist/process-manager.d.ts +8 -0
  211. package/dist/process-manager.d.ts.map +1 -1
  212. package/dist/process-manager.js +53 -9
  213. package/dist/process-manager.js.map +1 -1
  214. package/dist/project-config.d.ts +30 -3
  215. package/dist/project-config.d.ts.map +1 -1
  216. package/dist/project-config.js +37 -4
  217. package/dist/project-config.js.map +1 -1
  218. package/dist/prompts.d.ts +3 -0
  219. package/dist/prompts.d.ts.map +1 -0
  220. package/dist/prompts.js +3 -0
  221. package/dist/prompts.js.map +1 -0
  222. package/dist/pull-utils.d.ts +50 -14
  223. package/dist/pull-utils.d.ts.map +1 -1
  224. package/dist/pull-utils.js +152 -12
  225. package/dist/pull-utils.js.map +1 -1
  226. package/dist/resolve-target.d.ts +86 -0
  227. package/dist/resolve-target.d.ts.map +1 -0
  228. package/dist/resolve-target.js +291 -0
  229. package/dist/resolve-target.js.map +1 -0
  230. package/dist/restore-system-relation-targets.d.ts +3 -0
  231. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  232. package/dist/restore-system-relation-targets.js +45 -0
  233. package/dist/restore-system-relation-targets.js.map +1 -0
  234. package/dist/runtime-routes.d.ts.map +1 -1
  235. package/dist/runtime-routes.js +7 -0
  236. package/dist/runtime-routes.js.map +1 -1
  237. package/dist/schema-ast-v2.d.ts +1 -1
  238. package/dist/schema-ast-v2.d.ts.map +1 -1
  239. package/dist/schema-ast-v2.js +2 -2
  240. package/dist/schema-ast-v2.js.map +1 -1
  241. package/dist/schema-sources.d.ts +40 -0
  242. package/dist/schema-sources.d.ts.map +1 -0
  243. package/dist/schema-sources.js +183 -0
  244. package/dist/schema-sources.js.map +1 -0
  245. package/dist/scripts/postinstall.js +5 -1
  246. package/dist/scripts/postinstall.js.map +1 -1
  247. package/dist/self-host-compose.d.ts +37 -1
  248. package/dist/self-host-compose.d.ts.map +1 -1
  249. package/dist/self-host-compose.js +234 -43
  250. package/dist/self-host-compose.js.map +1 -1
  251. package/dist/storage-provision.d.ts +4 -0
  252. package/dist/storage-provision.d.ts.map +1 -1
  253. package/dist/storage-provision.js +24 -2
  254. package/dist/storage-provision.js.map +1 -1
  255. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  256. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  257. package/dist/supatype-eval-1781522769253.mjs +3 -0
  258. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  259. package/dist/systemd.js +2 -2
  260. package/dist/systemd.js.map +1 -1
  261. package/dist/target-client.d.ts +10 -0
  262. package/dist/target-client.d.ts.map +1 -0
  263. package/dist/target-client.js +22 -0
  264. package/dist/target-client.js.map +1 -0
  265. package/dist/type-extractor.d.ts +11 -0
  266. package/dist/type-extractor.d.ts.map +1 -1
  267. package/dist/type-extractor.js +95 -8
  268. package/dist/type-extractor.js.map +1 -1
  269. package/dist/ui/brand.d.ts +9 -0
  270. package/dist/ui/brand.d.ts.map +1 -0
  271. package/dist/ui/brand.js +11 -0
  272. package/dist/ui/brand.js.map +1 -0
  273. package/dist/ui/confirm.d.ts +12 -0
  274. package/dist/ui/confirm.d.ts.map +1 -0
  275. package/dist/ui/confirm.js +28 -0
  276. package/dist/ui/confirm.js.map +1 -0
  277. package/dist/ui/fatal.d.ts +10 -0
  278. package/dist/ui/fatal.d.ts.map +1 -0
  279. package/dist/ui/fatal.js +34 -0
  280. package/dist/ui/fatal.js.map +1 -0
  281. package/dist/ui/index.d.ts +9 -0
  282. package/dist/ui/index.d.ts.map +1 -0
  283. package/dist/ui/index.js +9 -0
  284. package/dist/ui/index.js.map +1 -0
  285. package/dist/ui/interactive.d.ts +3 -0
  286. package/dist/ui/interactive.d.ts.map +1 -0
  287. package/dist/ui/interactive.js +5 -0
  288. package/dist/ui/interactive.js.map +1 -0
  289. package/dist/ui/messages.d.ts +10 -0
  290. package/dist/ui/messages.d.ts.map +1 -0
  291. package/dist/ui/messages.js +35 -0
  292. package/dist/ui/messages.js.map +1 -0
  293. package/dist/ui/next-steps.d.ts +3 -0
  294. package/dist/ui/next-steps.d.ts.map +1 -0
  295. package/dist/ui/next-steps.js +10 -0
  296. package/dist/ui/next-steps.js.map +1 -0
  297. package/dist/ui/progress.d.ts +5 -0
  298. package/dist/ui/progress.d.ts.map +1 -0
  299. package/dist/ui/progress.js +24 -0
  300. package/dist/ui/progress.js.map +1 -0
  301. package/dist/ui/prompts.d.ts +14 -0
  302. package/dist/ui/prompts.d.ts.map +1 -0
  303. package/dist/ui/prompts.js +34 -0
  304. package/dist/ui/prompts.js.map +1 -0
  305. package/package.json +5 -2
  306. package/src/app/framework.ts +1 -3
  307. package/src/app/proxy-dev-app.ts +114 -6
  308. package/src/app-config.ts +80 -0
  309. package/src/binary-cache.ts +102 -52
  310. package/src/cli.ts +16 -2
  311. package/src/commands/add.ts +97 -0
  312. package/src/commands/admin.ts +381 -190
  313. package/src/commands/adopt.ts +82 -0
  314. package/src/commands/app.ts +20 -17
  315. package/src/commands/cache.ts +11 -10
  316. package/src/commands/cloud.ts +91 -142
  317. package/src/commands/db.ts +40 -63
  318. package/src/commands/deploy.ts +186 -126
  319. package/src/commands/dev.ts +98 -55
  320. package/src/commands/diff.ts +52 -43
  321. package/src/commands/doctor.ts +103 -0
  322. package/src/commands/engine.ts +5 -4
  323. package/src/commands/functions.ts +187 -123
  324. package/src/commands/generate.ts +5 -4
  325. package/src/commands/init.ts +1087 -104
  326. package/src/commands/introspect.ts +48 -0
  327. package/src/commands/keys.ts +56 -14
  328. package/src/commands/link-helpers.ts +273 -0
  329. package/src/commands/logs.ts +5 -4
  330. package/src/commands/migrate-from-v1.ts +3 -2
  331. package/src/commands/migrate.ts +167 -27
  332. package/src/commands/pg.ts +13 -18
  333. package/src/commands/plugins.ts +55 -46
  334. package/src/commands/pull.ts +38 -9
  335. package/src/commands/push.ts +148 -175
  336. package/src/commands/seed.ts +5 -4
  337. package/src/commands/self-host.ts +85 -54
  338. package/src/commands/self-update.ts +3 -2
  339. package/src/commands/status.ts +102 -33
  340. package/src/commands/types.ts +3 -2
  341. package/src/commands/update.ts +59 -23
  342. package/src/compose-rename.ts +76 -0
  343. package/src/config.ts +2 -1
  344. package/src/dev-compose.ts +462 -76
  345. package/src/dev-log-bus.ts +101 -0
  346. package/src/dev-log-filter.ts +32 -0
  347. package/src/dev-logo.ts +61 -0
  348. package/src/dev-ports.ts +212 -0
  349. package/src/dev-session-lock.ts +101 -0
  350. package/src/dev-session.ts +130 -0
  351. package/src/dev-shutdown.ts +147 -0
  352. package/src/dev-task-colors.ts +47 -0
  353. package/src/dev-tui.ts +232 -0
  354. package/src/diff-output.ts +79 -1
  355. package/src/docker-runtime.ts +151 -0
  356. package/src/engine-client.ts +81 -17
  357. package/src/engine-push-output.ts +75 -0
  358. package/src/ensure-binary.ts +2 -2
  359. package/src/env-file.ts +37 -0
  360. package/src/gitignore.ts +48 -0
  361. package/src/kong-config.ts +24 -1
  362. package/src/link.ts +243 -0
  363. package/src/process-manager.ts +66 -10
  364. package/src/project-config.ts +62 -7
  365. package/src/prompts.ts +2 -0
  366. package/src/pull-utils.ts +217 -23
  367. package/src/resolve-target.ts +419 -0
  368. package/src/restore-system-relation-targets.ts +45 -0
  369. package/src/runtime-routes.ts +7 -0
  370. package/src/schema-ast-v2.ts +2 -1
  371. package/src/schema-sources.ts +248 -0
  372. package/src/scripts/postinstall.ts +7 -1
  373. package/src/self-host-compose.ts +262 -46
  374. package/src/storage-provision.ts +33 -1
  375. package/src/supatype-eval-1781522769253.mts +1 -0
  376. package/src/systemd.ts +2 -2
  377. package/src/target-client.ts +40 -0
  378. package/src/type-extractor.ts +124 -11
  379. package/src/ui/README.md +17 -0
  380. package/src/ui/brand.ts +12 -0
  381. package/src/ui/confirm.ts +38 -0
  382. package/src/ui/fatal.ts +43 -0
  383. package/src/ui/index.ts +8 -0
  384. package/src/ui/interactive.ts +4 -0
  385. package/src/ui/messages.ts +43 -0
  386. package/src/ui/next-steps.ts +10 -0
  387. package/src/ui/progress.ts +28 -0
  388. package/src/ui/prompts.ts +40 -0
  389. package/tests/admin-ensure.test.ts +59 -0
  390. package/tests/cli-help.test.ts +27 -2
  391. package/tests/config.test.ts +29 -2
  392. package/tests/dev-ports.test.ts +41 -0
  393. package/tests/dev-session-lock.test.ts +54 -0
  394. package/tests/dev-ui.test.ts +162 -0
  395. package/tests/docker-runtime.test.ts +236 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/init.test.ts +197 -18
  398. package/tests/link.test.ts +148 -0
  399. package/tests/minisign.test.ts +102 -0
  400. package/tests/proxy-dev-app.test.ts +45 -1
  401. package/tests/pull-utils.test.ts +5 -4
  402. package/tests/runtime-contract.test.ts +186 -2
  403. package/tests/schema-sources.test.ts +119 -0
  404. package/tests/storage-provision.test.ts +100 -0
  405. package/tests/ui-confirm.test.ts +41 -0
  406. package/tests/ui-messages.test.ts +66 -0
  407. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,13 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { resolve, join, dirname } from "node:path";
2
+ import { resolve, join, dirname, basename } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { fetchAllLatestVersions } from "../binary-cache.js";
4
+ import { spawnSync } from "node:child_process";
5
+ import * as p from "@clack/prompts";
6
+ import { ensureNotCancelled, printLogo } from "../ui/prompts.js";
7
+ import { generateAndWriteKeys } from "./keys.js";
8
+ import { file, error, info, plain, warn } from "../ui/messages.js";
9
+ import { nextSteps } from "../ui/next-steps.js";
10
+ import { probeDockerDaemon, reportDockerUnavailable } from "../docker-runtime.js";
5
11
  export { scaffold };
6
12
  // ─── Markers used by `supatype app add / remove` (app.ts) ────────────────────
7
13
  export const APP_COMPOSE_MARKER = " # ─── App service (run: supatype app add) ───";
@@ -16,145 +22,735 @@ function cliPackageVersion() {
16
22
  return "0.1.0";
17
23
  }
18
24
  }
25
+ const STORAGE_PROVIDER_OPTIONS = [
26
+ { value: "local", label: "Local", hint: "storage you host yourself (MinIO)" },
27
+ { value: "s3", label: "S3", hint: "external bucket (AWS S3 or compatible)" },
28
+ ];
29
+ /** `--mode dev|standalone` is mapped onto a production target for back-compat. */
30
+ function productionTargetFromMode(mode) {
31
+ return mode === "standalone" ? "self-host" : "later";
32
+ }
33
+ /** supatype-server mode written to the committed config for a production target. */
34
+ function serverModeForTarget(target) {
35
+ switch (target) {
36
+ case "cloud":
37
+ return "managed";
38
+ case "self-host":
39
+ return "standalone";
40
+ case "later":
41
+ return "dev";
42
+ }
43
+ }
44
+ export function defaultScaffoldOptions(projectName, productionTarget = "later") {
45
+ return {
46
+ projectName,
47
+ provider: "docker",
48
+ productionTarget,
49
+ ...(productionTarget === "self-host" ? { domain: "" } : {}),
50
+ schemaPath: "schema/index.ts",
51
+ app: { mode: "none" },
52
+ email: "console",
53
+ storageLocal: "local",
54
+ storageProduction: "local",
55
+ helloFunction: false,
56
+ };
57
+ }
19
58
  export function registerInit(program) {
20
59
  program
21
60
  .command("init [name]")
22
61
  .description("Scaffold a new Supatype project")
23
- .option("--mode <mode>", "Server mode in supatype.config.ts: dev (default) | standalone (native ACME — not Compose self-host)", "dev")
24
- .action(async (name, opts = { mode: "dev" }) => {
25
- const projectName = name ?? "my-project";
62
+ .option("--mode <mode>", "Back-compat: dev (default, local only) | standalone (self-host production target)", "dev")
63
+ .option("-y, --defaults", "Skip all prompts and use sensible defaults")
64
+ .option("--no-install", "Do not run the package manager install step")
65
+ .option("--no-keys", "Do not generate ANON_KEY / SERVICE_ROLE_KEY")
66
+ .option("--admin-email <email>", "First admin panel user email (written to .env)")
67
+ .option("--admin-password <password>", "First admin panel user password (written to .env)")
68
+ .option("--no-admin", "Do not configure a first admin user")
69
+ .action(async (name, opts) => {
26
70
  const dir = name ? resolve(process.cwd(), name) : process.cwd();
27
71
  if (name && existsSync(dir)) {
28
- console.error(`Directory already exists: ${dir}`);
72
+ error(`Directory already exists: ${dir}`);
29
73
  process.exit(1);
30
74
  }
31
- if (name)
32
- mkdirSync(dir, { recursive: true });
33
- let versions = {};
34
- try {
35
- console.log("Fetching latest component versions from CDN...");
36
- versions = await fetchAllLatestVersions();
75
+ const defaultName = name ?? basename(dir) ?? "my-project";
76
+ const interactive = !opts.defaults && Boolean(process.stdin.isTTY);
77
+ const modeTarget = productionTargetFromMode(opts.mode);
78
+ let result;
79
+ if (interactive) {
80
+ printLogo();
81
+ result = await runWizard(defaultName, modeTarget);
82
+ }
83
+ else {
84
+ result = {
85
+ ...defaultScaffoldOptions(defaultName, modeTarget),
86
+ packageManager: detectInvokingPackageManager(),
87
+ install: true,
88
+ generateKeys: true,
89
+ };
90
+ if (result.provider === "docker") {
91
+ const { findNextFreePort } = await import("../dev-ports.js");
92
+ const { COMPOSE_DEV_KONG_PORT } = await import("../project-config.js");
93
+ result.kongPort = await findNextFreePort(COMPOSE_DEV_KONG_PORT);
94
+ }
95
+ if (!opts.admin && opts.adminEmail && opts.adminPassword) {
96
+ result.adminEmail = opts.adminEmail;
97
+ result.adminPassword = opts.adminPassword;
98
+ }
37
99
  }
38
- catch {
39
- // Non-fatal: scaffold with placeholder versions; user can run `supatype update`.
100
+ if (opts.admin === false) {
101
+ delete result.adminEmail;
102
+ delete result.adminPassword;
40
103
  }
41
- scaffold(dir, projectName, opts.mode, versions);
42
- console.log(`\nSupatype project ready${name ? ` in ${name}/` : ""}.\n`);
43
- console.log("Next steps:");
104
+ else if (opts.adminEmail && opts.adminPassword) {
105
+ result.adminEmail = opts.adminEmail;
106
+ result.adminPassword = opts.adminPassword;
107
+ }
108
+ // CLI flags override wizard / default action choices.
109
+ const doInstall = opts.install !== false && result.install;
110
+ const doKeys = opts.keys !== false && result.generateKeys;
44
111
  if (name)
45
- console.log(` cd ${name}`);
46
- console.log(" npm install");
47
- console.log(" supatype keys");
48
- console.log(" supatype dev # native Postgres + supatype-server");
49
- console.log(" supatype push # apply schema + generate types");
50
- console.log("\nStatic frontend (self-host):");
51
- console.log(" supatype app add --static ./public");
52
- console.log(" npm run build # write files into public/");
53
- console.log(" supatype self-host compose up -d");
54
- if (opts.mode === "standalone") {
55
- console.log("\nStandalone (native TLS with ACME):");
56
- console.log(" Edit supatype.config.ts — set server.domain");
57
- console.log(" supatype dev # or run supatype-server with your TLS setup");
112
+ mkdirSync(dir, { recursive: true });
113
+ scaffold(dir, result);
114
+ if (doInstall)
115
+ runInstall(dir, result.packageManager);
116
+ const keysGenerated = doKeys ? writeKeys(dir) : false;
117
+ warnDockerUnavailableForProvider(result.provider);
118
+ printNextSteps({
119
+ name,
120
+ result,
121
+ installed: doInstall,
122
+ keysGenerated,
123
+ });
124
+ });
125
+ }
126
+ // ─── Wizard ──────────────────────────────────────────────────────────────────
127
+ async function runWizard(defaultName, defaultTarget) {
128
+ p.intro("Create a new Supatype project");
129
+ const projectName = ensureNotCancelled(await p.text({
130
+ message: "Project name",
131
+ defaultValue: defaultName,
132
+ placeholder: defaultName,
133
+ })).trim() || defaultName;
134
+ const packageManager = ensureNotCancelled(await p.select({
135
+ message: "Package manager",
136
+ initialValue: detectInvokingPackageManager(),
137
+ options: [
138
+ { value: "npm", label: "npm" },
139
+ { value: "pnpm", label: "pnpm" },
140
+ { value: "yarn", label: "yarn" },
141
+ { value: "bun", label: "bun" },
142
+ ],
143
+ }));
144
+ const productionTarget = ensureNotCancelled(await p.select({
145
+ message: "Where will this run in production?",
146
+ initialValue: defaultTarget,
147
+ options: [
148
+ { value: "cloud", label: "Supatype Cloud", hint: "managed; deploy via supatype link" },
149
+ { value: "self-host", label: "Self-host", hint: "your own server with TLS" },
150
+ { value: "later", label: "Decide later", hint: "local development only for now" },
151
+ ],
152
+ }));
153
+ let domain;
154
+ let tlsEmail;
155
+ if (productionTarget === "self-host") {
156
+ domain = ensureNotCancelled(await p.text({
157
+ message: "Production domain for ACME TLS (optional, can set later)",
158
+ placeholder: "api.example.com",
159
+ defaultValue: "",
160
+ })).trim();
161
+ if (domain) {
162
+ tlsEmail =
163
+ ensureNotCancelled(await p.text({
164
+ message: "Email for Let's Encrypt (HTTPS) certificates",
165
+ placeholder: "you@example.com",
166
+ defaultValue: "",
167
+ })).trim() || undefined;
168
+ }
169
+ }
170
+ const provider = ensureNotCancelled(await p.select({
171
+ message: "How should Postgres and the server run for local development?",
172
+ initialValue: "docker",
173
+ options: [
174
+ { value: "docker", label: "Docker", hint: "Docker Compose stack (recommended)" },
175
+ { value: "native", label: "Native", hint: "host Postgres + server binaries, no Docker" },
176
+ ],
177
+ }));
178
+ let kongPort;
179
+ if (provider === "docker") {
180
+ const { promptKongPortChoice } = await import("../dev-ports.js");
181
+ kongPort = await promptKongPortChoice();
182
+ }
183
+ const schemaPath = ensureNotCancelled(await p.text({
184
+ message: "Where should your schema live?",
185
+ defaultValue: "schema/index.ts",
186
+ placeholder: "schema/index.ts",
187
+ })).trim() || "schema/index.ts";
188
+ const app = await promptApp();
189
+ const email = ensureNotCancelled(await p.select({
190
+ message: "Email provider",
191
+ initialValue: "console",
192
+ options: [
193
+ { value: "console", label: "console", hint: "log emails to the terminal (dev)" },
194
+ { value: "smtp", label: "SMTP" },
195
+ { value: "resend", label: "Resend" },
196
+ { value: "ses", label: "Amazon SES" },
197
+ ],
198
+ }));
199
+ const storageLocal = ensureNotCancelled(await p.select({
200
+ message: "Local storage (for development)?",
201
+ initialValue: "local",
202
+ options: STORAGE_PROVIDER_OPTIONS,
203
+ }));
204
+ const storageProduction = ensureNotCancelled(await p.select({
205
+ message: "Production storage?",
206
+ initialValue: "local",
207
+ options: STORAGE_PROVIDER_OPTIONS,
208
+ }));
209
+ const helloFunction = ensureNotCancelled(await p.confirm({
210
+ message: "Create a hello-world edge function?",
211
+ initialValue: false,
212
+ }));
213
+ const install = ensureNotCancelled(await p.confirm({
214
+ message: `Install dependencies with ${packageManager} now?`,
215
+ initialValue: true,
216
+ }));
217
+ const generateKeys = ensureNotCancelled(await p.confirm({
218
+ message: "Generate ANON_KEY and SERVICE_ROLE_KEY now?",
219
+ initialValue: true,
220
+ }));
221
+ let adminEmail;
222
+ let adminPassword;
223
+ const createAdmin = ensureNotCancelled(await p.confirm({
224
+ message: "Create an admin user for /admin?",
225
+ initialValue: true,
226
+ }));
227
+ if (createAdmin) {
228
+ adminEmail =
229
+ ensureNotCancelled(await p.text({
230
+ message: "Admin email",
231
+ placeholder: "you@example.com",
232
+ defaultValue: "",
233
+ })).trim() || undefined;
234
+ if (adminEmail) {
235
+ adminPassword =
236
+ ensureNotCancelled(await p.text({
237
+ message: "Admin password (min 8 chars)",
238
+ defaultValue: "",
239
+ })).trim() || undefined;
240
+ if (adminPassword && adminPassword.length < 8) {
241
+ adminPassword = undefined;
242
+ }
58
243
  }
59
- console.log();
244
+ }
245
+ p.outro("Setting up your project...");
246
+ return {
247
+ projectName,
248
+ provider,
249
+ ...(kongPort !== undefined ? { kongPort } : {}),
250
+ productionTarget,
251
+ ...(domain !== undefined ? { domain } : {}),
252
+ ...(tlsEmail !== undefined ? { tlsEmail } : {}),
253
+ schemaPath,
254
+ app,
255
+ email,
256
+ storageLocal,
257
+ storageProduction,
258
+ helloFunction,
259
+ packageManager,
260
+ install,
261
+ generateKeys,
262
+ ...(adminEmail && adminPassword ? { adminEmail, adminPassword } : {}),
263
+ };
264
+ }
265
+ async function promptApp() {
266
+ const mode = ensureNotCancelled(await p.select({
267
+ message: "Host a frontend app at /?",
268
+ initialValue: "none",
269
+ options: [
270
+ { value: "none", label: "No app", hint: "API only" },
271
+ { value: "static", label: "Static site", hint: "serve a built directory" },
272
+ { value: "proxy", label: "Local dev server", hint: "forward requests to a dev server you run" },
273
+ ],
274
+ }));
275
+ if (mode === "static") {
276
+ const staticDir = ensureNotCancelled(await p.text({
277
+ message: "Directory to serve",
278
+ defaultValue: "./public",
279
+ placeholder: "./public",
280
+ })).trim() || "./public";
281
+ const viteDevUrl = await promptViteDevUrl();
282
+ return { mode, staticDir, ...(viteDevUrl ? { viteDevUrl } : {}) };
283
+ }
284
+ if (mode === "proxy") {
285
+ const upstream = ensureNotCancelled(await p.text({
286
+ message: "URL of your running dev server",
287
+ defaultValue: "http://localhost:3000",
288
+ placeholder: "http://localhost:3000",
289
+ })).trim() || "http://localhost:3000";
290
+ const start = ensureNotCancelled(await p.text({
291
+ message: "package.json script that starts your dev server",
292
+ defaultValue: "dev",
293
+ placeholder: "dev",
294
+ })).trim() || "dev";
295
+ const viteDevUrl = await promptViteDevUrl();
296
+ return { mode, upstream, start, ...(viteDevUrl ? { viteDevUrl } : {}) };
297
+ }
298
+ return { mode: "none" };
299
+ }
300
+ async function promptViteDevUrl() {
301
+ const useVite = ensureNotCancelled(await p.confirm({
302
+ message: "Enable live reload from a separate Vite dev server?",
303
+ initialValue: false,
304
+ }));
305
+ if (!useVite)
306
+ return undefined;
307
+ return (ensureNotCancelled(await p.text({
308
+ message: "Vite dev server URL",
309
+ defaultValue: "http://127.0.0.1:5173",
310
+ placeholder: "http://127.0.0.1:5173",
311
+ })).trim() || "http://127.0.0.1:5173");
312
+ }
313
+ // ─── Package manager ───────────────────────────────────────────────────────--
314
+ function detectInvokingPackageManager() {
315
+ const ua = process.env["npm_config_user_agent"] ?? "";
316
+ if (ua.startsWith("pnpm"))
317
+ return "pnpm";
318
+ if (ua.startsWith("yarn"))
319
+ return "yarn";
320
+ if (ua.startsWith("bun"))
321
+ return "bun";
322
+ return "npm";
323
+ }
324
+ function runInstall(dir, pm) {
325
+ info(`Installing dependencies with ${pm}...`);
326
+ const res = spawnSync(pm, ["install"], {
327
+ cwd: dir,
328
+ stdio: "inherit",
329
+ shell: process.platform === "win32",
60
330
  });
331
+ if (res.status !== 0 || res.error) {
332
+ warn(`Dependency install did not complete (run "${pm} install" manually).`);
333
+ }
334
+ }
335
+ function writeKeys(dir) {
336
+ const keys = generateAndWriteKeys(dir);
337
+ if (!keys) {
338
+ warn("Could not generate keys (JWT_SECRET missing). Run `supatype keys` manually.");
339
+ return false;
340
+ }
341
+ return true;
61
342
  }
62
- function scaffold(dir, projectName, mode = "dev", versions = {}) {
343
+ // ─── Scaffold ──────────────────────────────────────────────────────────────--
344
+ function scaffold(dir, optsOrName) {
345
+ const opts = typeof optsOrName === "string" ? defaultScaffoldOptions(optsOrName) : optsOrName;
63
346
  const write = (rel, content) => {
64
347
  const full = join(dir, rel);
65
348
  mkdirSync(resolve(full, ".."), { recursive: true });
66
349
  writeFileSync(full, content, "utf8");
67
- console.log(` created ${rel}`);
350
+ file("created", rel);
68
351
  };
69
352
  const pkgPath = join(dir, "package.json");
70
353
  if (!existsSync(pkgPath)) {
71
- write("package.json", packageJsonTemplate(projectName, cliPackageVersion()));
354
+ write("package.json", packageJsonTemplate(opts, cliPackageVersion()));
72
355
  }
73
356
  else {
74
- console.log(" skipped package.json (already exists)");
357
+ file("skipped", "package.json (already exists)");
75
358
  }
76
- write("supatype.config.ts", tsConfigTemplate(projectName, mode, versions));
77
- write("schema/index.ts", schemaTemplate());
78
- write(".env", envTemplate(projectName));
79
- write("seed.ts", seedTemplate(projectName));
359
+ write("supatype.config.ts", tsConfigTemplate(opts));
360
+ if (opts.productionTarget !== "later") {
361
+ write("supatype.local.config.ts", localConfigTemplate(opts.app));
362
+ }
363
+ write(opts.schemaPath, schemaTemplate());
364
+ write(".env", envTemplate(opts));
365
+ write("seed.ts", seedTemplate(opts.projectName));
80
366
  write("seeds/.gitkeep", "");
367
+ scaffoldAppAssets(opts, write);
368
+ if (opts.helloFunction)
369
+ scaffoldHelloFunction(dir, write);
370
+ const gitignorePath = join(dir, ".gitignore");
371
+ if (existsSync(gitignorePath)) {
372
+ const merged = mergeGitignoreTemplate(readFileSync(gitignorePath, "utf8"));
373
+ if (merged !== readFileSync(gitignorePath, "utf8")) {
374
+ writeFileSync(gitignorePath, merged, "utf8");
375
+ file("updated", ".gitignore (added .supatype/)");
376
+ }
377
+ else {
378
+ file("skipped", ".gitignore (already exists)");
379
+ }
380
+ }
381
+ else {
382
+ write(".gitignore", gitignoreTemplate());
383
+ }
384
+ }
385
+ function staticDirRelative(staticDir) {
386
+ const raw = (staticDir ?? "./public").trim();
387
+ return raw.replace(/^\.\//, "").replace(/\/+$/, "") || "public";
388
+ }
389
+ function scaffoldAppAssets(opts, write) {
390
+ const holding = holdingPageTemplate(opts.projectName);
391
+ const hasVite = Boolean(opts.app.viteDevUrl);
392
+ if (opts.app.mode === "static") {
393
+ const staticRel = staticDirRelative(opts.app.staticDir);
394
+ write(`${staticRel}/index.html`, holding);
395
+ if (hasVite)
396
+ scaffoldVite(opts, write, holding);
397
+ return;
398
+ }
399
+ if (opts.app.mode === "proxy" && opts.productionTarget !== "later") {
400
+ write("dist/index.html", holding);
401
+ if (hasVite)
402
+ scaffoldVite(opts, write, holding);
403
+ return;
404
+ }
405
+ if (opts.app.mode === "proxy" && hasVite) {
406
+ scaffoldVite(opts, write, holding);
407
+ return;
408
+ }
81
409
  write("public/.gitkeep", "");
82
- write(".gitignore", gitignoreTemplate());
410
+ }
411
+ function scaffoldVite(opts, write, holding) {
412
+ if (!opts.app.viteDevUrl)
413
+ return;
414
+ write("index.html", holding);
415
+ write("vite.config.ts", viteConfigTemplate(opts.app.viteDevUrl));
416
+ }
417
+ function scaffoldHelloFunction(dir, write) {
418
+ write("functions/hello/index.ts", helloFunctionTemplate());
419
+ if (!existsSync(join(dir, "functions/_shared/README.md"))) {
420
+ write("functions/_shared/README.md", sharedFunctionsReadme());
421
+ }
422
+ if (!existsSync(join(dir, "functions/.env.local"))) {
423
+ write("functions/.env.local", functionsEnvLocalTemplate());
424
+ }
83
425
  }
84
426
  // ─── Templates ───────────────────────────────────────────────────────────────
85
- function packageJsonTemplate(projectName, cliVersion) {
427
+ function packageJsonTemplate(opts, cliVersion) {
428
+ const scripts = [
429
+ ` "dev": "supatype dev"`,
430
+ ` "push": "supatype push"`,
431
+ ` "seed": "tsx seed.ts"`,
432
+ ];
433
+ if (opts.app.viteDevUrl) {
434
+ scripts.push(` "vite": "vite"`);
435
+ }
436
+ if (opts.helloFunction) {
437
+ scripts.push(` "functions": "supatype functions serve"`);
438
+ }
439
+ const devDeps = [` "tsx": "^4.19.2"`, ` "typescript": "^5"`];
440
+ if (opts.app.viteDevUrl) {
441
+ devDeps.push(` "vite": "^6"`);
442
+ }
86
443
  return `{
87
- "name": "${projectName}",
444
+ "name": "${opts.projectName}",
88
445
  "private": true,
89
446
  "type": "module",
90
447
  "scripts": {
91
- "dev": "supatype dev",
92
- "push": "supatype push",
93
- "seed": "tsx seed.ts"
448
+ ${scripts.join(",\n")}
94
449
  },
95
450
  "dependencies": {
96
451
  "@supatype/cli": "^${cliVersion}",
97
452
  "@supatype/types": "^${cliVersion}"
98
453
  },
99
454
  "devDependencies": {
100
- "tsx": "^4.19.2",
101
- "typescript": "^5"
455
+ ${devDeps.join(",\n")}
102
456
  }
103
457
  }
104
458
  `;
105
459
  }
106
- function tsConfigTemplate(projectName, mode, versions = {}) {
107
- const domainField = mode === "standalone"
108
- ? ` domain: "", // e.g. "api.example.com" for ACME TLS\n`
109
- : "";
110
- const v = (key, fallback) => versions[key] ?? fallback;
111
- return `import { defineConfig } from "@supatype/cli"
460
+ function tsConfigTemplate(opts) {
461
+ const serverMode = serverModeForTarget(opts.productionTarget);
462
+ const hasLocalOverride = opts.productionTarget !== "later";
463
+ const lines = [];
464
+ lines.push(`import { defineConfig } from "@supatype/cli"`);
465
+ lines.push("");
466
+ if (hasLocalOverride) {
467
+ lines.push(`// Committed config = ${opts.productionTarget} production target.`);
468
+ lines.push(`// Local development overrides live in supatype.local.config.ts (gitignored).`);
469
+ }
470
+ lines.push(`export default defineConfig({`);
471
+ lines.push(` project: { name: "${opts.projectName}" },`);
472
+ lines.push(` provider: "${opts.provider}",`);
473
+ if (opts.provider === "docker") {
474
+ lines.push(` // provider: "native" // host Postgres + supatype-server binaries (no Docker)`);
475
+ }
476
+ lines.push(` database: {`);
477
+ lines.push(` provider: "${opts.provider}",`);
478
+ lines.push(` },`);
479
+ lines.push(` server: {`);
480
+ lines.push(` mode: "${serverMode}",`);
481
+ lines.push(` port: 54321,`);
482
+ if (serverMode === "standalone") {
483
+ lines.push(` domain: "${opts.domain ?? ""}", // e.g. "api.example.com" for ACME TLS`);
484
+ if (opts.tlsEmail) {
485
+ lines.push(` tls: { email: "${opts.tlsEmail}" }, // automatic HTTPS via Let's Encrypt`);
486
+ }
487
+ else {
488
+ lines.push(` // tls: { email: "you@example.com" }, // set to enable automatic HTTPS (Let's Encrypt)`);
489
+ }
490
+ }
491
+ lines.push(` },`);
492
+ lines.push(...appConfigLines(opts.app, opts.productionTarget));
493
+ if (opts.productionTarget !== "later") {
494
+ lines.push(` environments: { default: "production" }, // supatype link --env production ...`);
495
+ }
496
+ lines.push(` // Optional: pin component versions (native cache + Docker images synced to .env on dev/push)`);
497
+ lines.push(` // versions: { engine: "0.1.2", server: "1.0.5", postgres: "17.2", deno: "2.2.0" },`);
498
+ lines.push(` email: { provider: "${opts.email}" },`);
499
+ lines.push(...storageConfigLines(opts.storageLocal, opts.storageProduction));
500
+ lines.push(` schema: { path: "${opts.schemaPath}", pg_schema: "public" },`);
501
+ lines.push(` // Self-host production: supatype self-host compose (Docker only). Standalone + domain = native ACME dev.`);
502
+ lines.push(`})`);
503
+ return lines.join("\n") + "\n";
504
+ }
505
+ function localConfigTemplate(app) {
506
+ const lines = [
507
+ `import type { SupatypeConfig } from "@supatype/cli"`,
508
+ ``,
509
+ `// Local development overrides — gitignored, deep-merged over supatype.config.ts.`,
510
+ `// Keeps \`supatype dev\` in local mode while the committed config targets production.`,
511
+ `const localConfig: Partial<SupatypeConfig> = {`,
512
+ ` server: { mode: "dev" },`,
513
+ ];
514
+ if (app.mode === "proxy") {
515
+ lines.push(` app: {`);
516
+ lines.push(` mode: "proxy",`);
517
+ lines.push(` upstream: "${app.upstream ?? "http://localhost:3000"}",`);
518
+ lines.push(` start: "${app.start ?? "dev"}",`);
519
+ if (app.viteDevUrl)
520
+ lines.push(` vite_dev_url: "${app.viteDevUrl}",`);
521
+ lines.push(` },`);
522
+ }
523
+ lines.push(`}`, ``, `export default localConfig`, ``);
524
+ return lines.join("\n");
525
+ }
526
+ function appConfigLines(app, productionTarget) {
527
+ if (app.mode === "static") {
528
+ const out = [
529
+ ` app: {`,
530
+ ` mode: "static",`,
531
+ ` static_dir: "${app.staticDir ?? "./public"}",`,
532
+ ];
533
+ if (app.viteDevUrl)
534
+ out.push(` vite_dev_url: "${app.viteDevUrl}",`);
535
+ out.push(` },`);
536
+ return out;
537
+ }
538
+ if (app.mode === "proxy") {
539
+ if (productionTarget !== "later") {
540
+ return [
541
+ ` app: {`,
542
+ ` mode: "static",`,
543
+ ` static_dir: "./dist", // production build output`,
544
+ ` },`,
545
+ ];
546
+ }
547
+ const out = [
548
+ ` app: {`,
549
+ ` mode: "proxy",`,
550
+ ` upstream: "${app.upstream ?? "http://localhost:3000"}",`,
551
+ ` start: "${app.start ?? "dev"}",`,
552
+ ];
553
+ if (app.viteDevUrl)
554
+ out.push(` vite_dev_url: "${app.viteDevUrl}",`);
555
+ out.push(` },`);
556
+ return out;
557
+ }
558
+ return [
559
+ ` app: {`,
560
+ ` mode: "none",`,
561
+ ` // mode: "static", static_dir: "./public", // supatype app add --static ./public`,
562
+ ` // mode: "proxy", upstream: "http://localhost:3000", start: "dev",`,
563
+ ` // vite_dev_url: "http://127.0.0.1:5173", // live reload from a separate Vite dev server`,
564
+ ` },`,
565
+ ];
566
+ }
567
+ const HOLDING_PAGE_LOGO_URL = "https://supatype.github.io/supatype/supatype.svg";
568
+ const HOLDING_PAGE_DOCS_URL = "https://supatype.github.io/supatype/";
569
+ const HOLDING_PAGE_GITHUB_URL = "https://github.com/supatype";
570
+ const HOLDING_PAGE_DISCORD_URL = "https://discord.gg/yaQrjQD4";
571
+ function escapeHtml(text) {
572
+ return text
573
+ .replace(/&/g, "&amp;")
574
+ .replace(/</g, "&lt;")
575
+ .replace(/>/g, "&gt;")
576
+ .replace(/"/g, "&quot;");
577
+ }
578
+ function holdingPageTemplate(projectName) {
579
+ const name = escapeHtml(projectName);
580
+ return `<!doctype html>
581
+ <html lang="en">
582
+ <head>
583
+ <meta charset="UTF-8" />
584
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
585
+ <title>${name} — Supatype</title>
586
+ <meta name="description" content="A Supatype project. Define your types — we generate your backend." />
587
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
588
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
589
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
590
+ <style>
591
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
592
+ :root {
593
+ --bg: #0a0a0f;
594
+ --border: rgba(255, 255, 255, 0.08);
595
+ --text: #e8e8f0;
596
+ --text-muted: #8888a8;
597
+ --purple: #7c3aed;
598
+ --purple-light: #a855f7;
599
+ }
600
+ body {
601
+ min-height: 100vh;
602
+ font-family: Inter, system-ui, sans-serif;
603
+ background: var(--bg);
604
+ color: var(--text);
605
+ line-height: 1.6;
606
+ display: flex;
607
+ align-items: center;
608
+ justify-content: center;
609
+ padding: 2rem;
610
+ }
611
+ body::before {
612
+ content: "";
613
+ position: fixed;
614
+ inset: 0;
615
+ background: radial-gradient(ellipse 80% 50% at 50% -20%, rgba(124, 58, 237, 0.18), transparent);
616
+ pointer-events: none;
617
+ }
618
+ main {
619
+ position: relative;
620
+ max-width: 32rem;
621
+ width: 100%;
622
+ text-align: center;
623
+ }
624
+ .logo {
625
+ display: block;
626
+ height: 2rem;
627
+ width: auto;
628
+ margin: 0 auto 2rem;
629
+ }
630
+ h1 {
631
+ font-size: 1.5rem;
632
+ font-weight: 700;
633
+ letter-spacing: -0.02em;
634
+ margin-bottom: 0.75rem;
635
+ }
636
+ .tagline {
637
+ color: var(--text-muted);
638
+ font-size: 1rem;
639
+ margin-bottom: 2rem;
640
+ }
641
+ .links {
642
+ display: flex;
643
+ flex-wrap: wrap;
644
+ gap: 0.75rem;
645
+ justify-content: center;
646
+ margin-bottom: 2.5rem;
647
+ }
648
+ .links a {
649
+ display: inline-flex;
650
+ align-items: center;
651
+ padding: 0.6rem 1.1rem;
652
+ border-radius: 10px;
653
+ border: 1px solid var(--border);
654
+ color: var(--text);
655
+ text-decoration: none;
656
+ font-size: 0.9rem;
657
+ font-weight: 500;
658
+ transition: border-color 0.15s, background 0.15s;
659
+ }
660
+ .links a:hover {
661
+ border-color: rgba(168, 85, 247, 0.45);
662
+ background: rgba(124, 58, 237, 0.08);
663
+ }
664
+ .links a.primary {
665
+ background: linear-gradient(135deg, var(--purple), var(--purple-light));
666
+ border-color: transparent;
667
+ color: #fff;
668
+ }
669
+ .links a.primary:hover {
670
+ opacity: 0.92;
671
+ }
672
+ .hint {
673
+ font-size: 0.85rem;
674
+ color: var(--text-muted);
675
+ }
676
+ .hint code {
677
+ font-family: ui-monospace, "JetBrains Mono", monospace;
678
+ font-size: 0.8rem;
679
+ background: rgba(255, 255, 255, 0.06);
680
+ padding: 0.15rem 0.4rem;
681
+ border-radius: 4px;
682
+ }
683
+ </style>
684
+ </head>
685
+ <body>
686
+ <main>
687
+ <img class="logo" src="${HOLDING_PAGE_LOGO_URL}" alt="Supatype" width="160" height="30" />
688
+ <h1>${name}</h1>
689
+ <p class="tagline">Your Supatype project is ready. Replace this page when you build your app.</p>
690
+ <nav class="links" aria-label="Supatype resources">
691
+ <a class="primary" href="${HOLDING_PAGE_DOCS_URL}" target="_blank" rel="noopener noreferrer">Documentation</a>
692
+ <a href="${HOLDING_PAGE_GITHUB_URL}" target="_blank" rel="noopener noreferrer">GitHub</a>
693
+ <a href="${HOLDING_PAGE_DISCORD_URL}" target="_blank" rel="noopener noreferrer">Discord</a>
694
+ </nav>
695
+ <p class="hint">Run <code>supatype dev</code> then open <code>http://localhost:18473/</code></p>
696
+ </main>
697
+ </body>
698
+ </html>
699
+ `;
700
+ }
701
+ function vitePortFromDevUrl(viteDevUrl) {
702
+ try {
703
+ const url = new URL(viteDevUrl);
704
+ if (url.port)
705
+ return Number.parseInt(url.port, 10);
706
+ return url.protocol === "https:" ? 443 : 80;
707
+ }
708
+ catch {
709
+ return 5173;
710
+ }
711
+ }
712
+ function viteConfigTemplate(viteDevUrl) {
713
+ const port = vitePortFromDevUrl(viteDevUrl);
714
+ return `import { defineConfig } from "vite"
112
715
 
113
716
  export default defineConfig({
114
- project: { name: "${projectName}" },
115
- provider: "native",
116
- // provider: "docker" // full self-host Compose stack (Kong :18473)
117
- database: {
118
- provider: "native",
119
- },
120
717
  server: {
121
- mode: "${mode}",
122
- port: 54321,
123
- ${domainField} },
124
- app: {
125
- mode: "none",
126
- // mode: "static", static_dir: "./public", // supatype app add --static ./public
127
- // mode: "proxy", upstream: "http://localhost:3000", start: "dev",
128
- // vite_dev_url: "http://127.0.0.1:5173", // dev HMR at /_vite (when using a separate Vite server)
129
- },
130
- versions: {
131
- engine: "${v("engine", "latest")}",
132
- server: "${v("server", "latest")}",
133
- postgres: "${v("postgres", "latest")}",
134
- deno: "${v("deno", "latest")}",
718
+ host: "127.0.0.1",
719
+ port: ${port},
720
+ strictPort: true,
135
721
  },
136
- email: { provider: "console" },
137
- storage: { provider: "local", local_path: ".supatype/storage" },
138
- schema: { path: "schema/index.ts", pg_schema: "public" },
139
- // Self-host production: supatype self-host compose (Docker only). Standalone + domain = native ACME dev.
140
722
  })
141
723
  `;
142
724
  }
725
+ function storageConfigLines(storageLocal, storageProduction) {
726
+ const lines = [];
727
+ if (storageLocal === "s3") {
728
+ lines.push(` storage: { provider: "s3" }, // dev — configure S3_* in .env`);
729
+ }
730
+ else {
731
+ lines.push(` storage: { provider: "local", local_path: ".supatype/storage" },`);
732
+ }
733
+ if (storageProduction === "s3" && storageLocal !== "s3") {
734
+ lines.push(` // Production storage: external S3 bucket — set production S3_* in .env`);
735
+ }
736
+ else if (storageProduction === "local" && storageLocal === "s3") {
737
+ lines.push(` // Production storage: MinIO on your server (included in self-host compose)`);
738
+ }
739
+ return lines;
740
+ }
143
741
  function schemaTemplate() {
144
- return `import type { Model, Public, Owner, Role, SupatypeAuthUserId, Unique, Email, UUID } from "@supatype/types"
742
+ return `import type { Model, LoggedIn, Owner, Public, Role, SupatypeAuthUserId, UUID } from "@supatype/types"
145
743
 
146
- export type User = Model<{
744
+ /** App profile for a signed-in user. \`id\` matches the Supatype auth user id. */
745
+ export type Profile = Model<{
147
746
  id: SupatypeAuthUserId
148
- email: Unique<Email>
149
- name: string
150
- created_at: string
151
- updated_at: string
747
+ display_name: string
152
748
  }, {
153
749
  access: {
154
- read: Public
155
- create: Public
750
+ read: LoggedIn
751
+ create: Owner<"id">
156
752
  update: Owner<"id">
157
- delete: Role<"admin">
753
+ delete: Owner<"id">
158
754
  }
159
755
  }>
160
756
 
@@ -171,34 +767,107 @@ export type SiteSettings = Model<{
171
767
  }>
172
768
  `;
173
769
  }
174
- function envTemplate(projectName) {
175
- return `DATABASE_URL=postgresql://supatype_admin:postgres@localhost:5432/${projectName}
770
+ function envTemplate(opts) {
771
+ const sections = [];
772
+ sections.push(`DATABASE_URL=postgresql://supatype_admin:postgres@localhost:5432/${opts.projectName}
176
773
  POSTGRES_USER=supatype_admin
177
774
  POSTGRES_PASSWORD=postgres
178
- POSTGRES_DB=${projectName}
179
-
180
- # JWT — run \`supatype keys\` to generate ANON_KEY and SERVICE_ROLE_KEY
775
+ POSTGRES_DB=${opts.projectName}`);
776
+ sections.push(`# JWT — run \`supatype keys\` to generate ANON_KEY and SERVICE_ROLE_KEY
181
777
  JWT_SECRET=super-secret-jwt-token-change-in-production
182
778
  ANON_KEY=
183
- SERVICE_ROLE_KEY=
184
-
185
- # Site URL (used by GoTrue for email redirects)
186
- SITE_URL=http://localhost:3000
187
-
188
- # SMTP leave empty to use email autoconfirm in dev (no emails sent)
779
+ SERVICE_ROLE_KEY=`);
780
+ sections.push(`# Site URL (used by GoTrue for email redirects)
781
+ SITE_URL=http://localhost:3000`);
782
+ if (opts.provider === "docker" && opts.kongPort !== undefined) {
783
+ const apiUrl = `http://localhost:${opts.kongPort}`;
784
+ sections.push(`# Local API gateway (Kong) unique per project so multiple stacks can run concurrently
785
+ SUPATYPE_KONG_PORT=${opts.kongPort}
786
+ PUBLIC_SUPATYPE_URL=${apiUrl}
787
+ API_EXTERNAL_URL=${apiUrl}`);
788
+ }
789
+ sections.push(emailEnvSection(opts.email, opts.projectName));
790
+ sections.push(storageEnvSections(opts.storageLocal, opts.storageProduction));
791
+ if (opts.adminEmail && opts.adminPassword) {
792
+ sections.push(`# First admin for /admin — consumed on first supatype dev or push (password removed after use)
793
+ SUPATYPE_ADMIN_EMAIL=${opts.adminEmail}
794
+ SUPATYPE_ADMIN_PASSWORD=${opts.adminPassword}`);
795
+ }
796
+ sections.push(`# Self-host compose uses the same DATABASE_URL when Postgres is published on localhost:5432`);
797
+ return sections.join("\n\n") + "\n";
798
+ }
799
+ function emailEnvSection(email, projectName) {
800
+ switch (email) {
801
+ case "resend":
802
+ return `# Email (Resend)
803
+ RESEND_API_KEY=
804
+ RESEND_FROM=onboarding@resend.dev`;
805
+ case "ses":
806
+ return `# Email (Amazon SES)
807
+ SES_FROM=
808
+ AWS_REGION=us-east-1
809
+ AWS_ACCESS_KEY_ID=
810
+ AWS_SECRET_ACCESS_KEY=`;
811
+ case "smtp":
812
+ return `# Email (SMTP)
813
+ SMTP_HOST=
814
+ SMTP_PORT=587
815
+ SMTP_USER=
816
+ SMTP_PASS=
817
+ SMTP_SENDER_NAME=${projectName}`;
818
+ case "console":
819
+ default:
820
+ return `# SMTP — leave empty to use email autoconfirm in dev (no emails sent)
189
821
  SMTP_HOST=
190
822
  SMTP_PORT=
191
823
  SMTP_USER=
192
824
  SMTP_PASS=
193
- SMTP_SENDER_NAME=${projectName}
825
+ SMTP_SENDER_NAME=${projectName}`;
826
+ }
827
+ }
828
+ function storageEnvSections(storageLocal, storageProduction) {
829
+ if (storageLocal === storageProduction) {
830
+ if (storageLocal === "s3") {
831
+ return `# Storage (local development and production — external bucket)
832
+ # Use separate buckets for dev and production in your provider.
833
+ S3_ENDPOINT=
834
+ S3_REGION=us-east-1
835
+ S3_BUCKET=
836
+ S3_ACCESS_KEY=
837
+ S3_SECRET_KEY=`;
838
+ }
839
+ return `${localStorageEnvSection("local")}
194
840
 
195
- # Storage (MinIO for local dev)
841
+ # Production storage (MinIO on your server)
842
+ # Included in the self-host compose stack — no extra configuration needed.`;
843
+ }
844
+ return [localStorageEnvSection(storageLocal), productionStorageEnvSection(storageProduction)].join("\n\n");
845
+ }
846
+ function localStorageEnvSection(storage) {
847
+ if (storage === "s3") {
848
+ return `# Storage (local development — external bucket)
849
+ S3_ENDPOINT=
850
+ S3_REGION=us-east-1
851
+ S3_BUCKET=
852
+ S3_ACCESS_KEY=
853
+ S3_SECRET_KEY=`;
854
+ }
855
+ return `# Storage (local development — MinIO)
196
856
  S3_ENDPOINT=http://localhost:9000
197
857
  S3_ACCESS_KEY=supatype
198
- S3_SECRET_KEY=supatype-secret
199
-
200
- # Self-host compose uses the same DATABASE_URL when Postgres is published on localhost:5432
201
- `;
858
+ S3_SECRET_KEY=supatype-secret`;
859
+ }
860
+ function productionStorageEnvSection(storage) {
861
+ if (storage === "s3") {
862
+ return `# Storage (production — external bucket)
863
+ S3_ENDPOINT=
864
+ S3_REGION=us-east-1
865
+ S3_BUCKET=
866
+ S3_ACCESS_KEY=
867
+ S3_SECRET_KEY=`;
868
+ }
869
+ return `# Storage (production — MinIO on your server)
870
+ # Included in the self-host compose stack — no extra configuration needed.`;
202
871
  }
203
872
  function seedTemplate(projectName) {
204
873
  return `import { sql } from "@supatype/cli/seed"
@@ -213,7 +882,7 @@ async function seed() {
213
882
  console.log("Seeding ${projectName}...")
214
883
 
215
884
  // TODO: insert seed data
216
- // await db\`INSERT INTO users (email, name) VALUES ('admin@example.com', 'Admin')\`
885
+ // await db\`INSERT INTO profile (id, display_name) VALUES ('...', 'Admin')\`
217
886
 
218
887
  await db.end()
219
888
  console.log("Done.")
@@ -225,18 +894,125 @@ seed().catch((e) => {
225
894
  })
226
895
  `;
227
896
  }
897
+ function helloFunctionTemplate() {
898
+ return `// hello — Supatype Edge Function
899
+ // Docs: https://supatype.com/docs/edge-functions
900
+
901
+ export default async function handler(req: Request): Promise<Response> {
902
+ const { method } = req
903
+
904
+ if (method === "POST") {
905
+ const body = await req.json()
906
+ return new Response(JSON.stringify({ message: "Hello from hello!", received: body }), {
907
+ status: 200,
908
+ headers: { "Content-Type": "application/json" },
909
+ })
910
+ }
911
+
912
+ return new Response(JSON.stringify({ message: "Hello from hello!" }), {
913
+ status: 200,
914
+ headers: { "Content-Type": "application/json" },
915
+ })
916
+ }
917
+ `;
918
+ }
919
+ function sharedFunctionsReadme() {
920
+ return "# Shared Code\n\nFiles in `_shared/` are available to all functions via relative imports.\nThis directory is not deployed as a function.\n\nExample: `import { sendEmail } from '../_shared/email.ts'`\n";
921
+ }
922
+ function functionsEnvLocalTemplate() {
923
+ return "# Local environment variables for edge functions\n# These are NOT committed to git\n# Set production env vars via: npx supatype functions env set KEY=value\n";
924
+ }
228
925
  function gitignoreTemplate() {
229
926
  return `.env
230
927
  node_modules/
231
928
  dist/
232
- .supatype/engine/
233
- # Local overrides — never commit
929
+ .supatype/
234
930
  supatype.local.config.ts
235
931
  supatype.local.config.js
236
932
  supatype.local.config.mjs
237
- # Generated by supatype push
933
+ # Generated by supatype push (legacy paths — prefer output.types in config)
238
934
  src/types/supatype.d.ts
239
935
  src/lib/supatype.ts
240
936
  `;
241
937
  }
938
+ export function mergeGitignoreTemplate(existingContent) {
939
+ if (existingContent.includes(".supatype/") || existingContent.includes(".supatype\n")) {
940
+ return existingContent;
941
+ }
942
+ const block = `
943
+ # Supatype — local runtime (contains secrets in link.json)
944
+ .supatype/
945
+ `;
946
+ return existingContent.endsWith("\n") ? `${existingContent}${block}` : `${existingContent}\n${block}`;
947
+ }
948
+ // ─── Next steps ────────────────────────────────────────────────────────────--
949
+ function warnDockerUnavailableForProvider(provider) {
950
+ if (provider !== "docker")
951
+ return;
952
+ const probe = probeDockerDaemon();
953
+ if (probe.ok)
954
+ return;
955
+ reportDockerUnavailable(probe);
956
+ plain();
957
+ }
958
+ function printNextSteps(args) {
959
+ const { name, result, installed, keysGenerated } = args;
960
+ const steps = [];
961
+ if (name)
962
+ steps.push(`cd ${name}`);
963
+ if (!installed)
964
+ steps.push(`${result.packageManager} install`);
965
+ if (!keysGenerated)
966
+ steps.push("supatype keys");
967
+ const kongHint = result.provider === "docker" && result.kongPort !== undefined
968
+ ? `supatype dev # Docker Compose stack (Kong :${result.kongPort})`
969
+ : "supatype dev # Docker Compose stack (Kong :18473)";
970
+ steps.push(kongHint);
971
+ steps.push("supatype push # apply schema + generate types");
972
+ if (result.adminEmail) {
973
+ steps.push(" # first admin user is created on dev or push");
974
+ }
975
+ if (result.helloFunction) {
976
+ steps.push("supatype functions serve # run edge functions locally");
977
+ }
978
+ info(`Supatype project ready${name ? ` in ${name}/` : ""}.`);
979
+ nextSteps("Next steps:", steps);
980
+ if (result.app.mode === "none") {
981
+ nextSteps("Static frontend (self-host):", [
982
+ "supatype app add --static ./public",
983
+ "npm run build # write files into public/",
984
+ "supatype self-host compose up -d",
985
+ ]);
986
+ }
987
+ if (result.productionTarget === "cloud") {
988
+ nextSteps("Deploy to Supatype Cloud:", [
989
+ "supatype login",
990
+ "supatype link --env production --project <ref>",
991
+ "supatype push --env production",
992
+ ]);
993
+ info("supatype.local.config.ts keeps `supatype dev` local while the committed config targets cloud.");
994
+ }
995
+ else if (result.productionTarget === "self-host") {
996
+ const selfHostSteps = [];
997
+ const domain = result.domain?.trim();
998
+ if (domain) {
999
+ selfHostSteps.push(`1. Point DNS: an A record for ${domain} -> your server's public IP`);
1000
+ selfHostSteps.push("2. Open ports 80 and 443 on the server firewall");
1001
+ if (!result.tlsEmail) {
1002
+ selfHostSteps.push("3. Set server.tls.email in supatype.config.ts (required for HTTPS)");
1003
+ }
1004
+ selfHostSteps.push("supatype self-host compose up -d # Kong provisions HTTPS automatically");
1005
+ selfHostSteps.push(`Your Supatype platform goes live at https://${domain}`);
1006
+ selfHostSteps.push("Your app, REST, Auth, Storage, Realtime, Functions, and Studio — all behind one HTTPS domain (certs persist in valkey-data)");
1007
+ }
1008
+ else {
1009
+ selfHostSteps.push("Set server.domain + server.tls.email in supatype.config.ts to enable automatic HTTPS");
1010
+ selfHostSteps.push("supatype self-host compose up -d # Docker stack");
1011
+ }
1012
+ selfHostSteps.push("supatype link --env production ... # then: supatype push --env production");
1013
+ nextSteps("Self-host production (your own server):", selfHostSteps);
1014
+ info("supatype.local.config.ts keeps `supatype dev` local while the committed config targets self-host.");
1015
+ }
1016
+ plain();
1017
+ }
242
1018
  //# sourceMappingURL=init.js.map