@supatype/cli 0.1.0-alpha.8 → 0.1.0

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 (388) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +274 -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.map +1 -1
  30. package/dist/commands/admin.js +39 -53
  31. package/dist/commands/admin.js.map +1 -1
  32. package/dist/commands/adopt.d.ts +3 -0
  33. package/dist/commands/adopt.d.ts.map +1 -0
  34. package/dist/commands/adopt.js +55 -0
  35. package/dist/commands/adopt.js.map +1 -0
  36. package/dist/commands/app.d.ts.map +1 -1
  37. package/dist/commands/app.js +20 -17
  38. package/dist/commands/app.js.map +1 -1
  39. package/dist/commands/cache.d.ts.map +1 -1
  40. package/dist/commands/cache.js +11 -10
  41. package/dist/commands/cache.js.map +1 -1
  42. package/dist/commands/cloud.d.ts +4 -9
  43. package/dist/commands/cloud.d.ts.map +1 -1
  44. package/dist/commands/cloud.js +75 -125
  45. package/dist/commands/cloud.js.map +1 -1
  46. package/dist/commands/db.d.ts.map +1 -1
  47. package/dist/commands/db.js +37 -58
  48. package/dist/commands/db.js.map +1 -1
  49. package/dist/commands/deploy.d.ts.map +1 -1
  50. package/dist/commands/deploy.js +140 -96
  51. package/dist/commands/deploy.js.map +1 -1
  52. package/dist/commands/dev.d.ts.map +1 -1
  53. package/dist/commands/dev.js +72 -39
  54. package/dist/commands/dev.js.map +1 -1
  55. package/dist/commands/diff.d.ts.map +1 -1
  56. package/dist/commands/diff.js +39 -39
  57. package/dist/commands/diff.js.map +1 -1
  58. package/dist/commands/doctor.d.ts +3 -0
  59. package/dist/commands/doctor.d.ts.map +1 -0
  60. package/dist/commands/doctor.js +78 -0
  61. package/dist/commands/doctor.js.map +1 -0
  62. package/dist/commands/engine.d.ts.map +1 -1
  63. package/dist/commands/engine.js +5 -4
  64. package/dist/commands/engine.js.map +1 -1
  65. package/dist/commands/functions.d.ts.map +1 -1
  66. package/dist/commands/functions.js +172 -119
  67. package/dist/commands/functions.js.map +1 -1
  68. package/dist/commands/generate.d.ts.map +1 -1
  69. package/dist/commands/generate.js +5 -4
  70. package/dist/commands/generate.js.map +1 -1
  71. package/dist/commands/init.d.ts +30 -1
  72. package/dist/commands/init.d.ts.map +1 -1
  73. package/dist/commands/init.js +814 -107
  74. package/dist/commands/init.js.map +1 -1
  75. package/dist/commands/introspect.d.ts +3 -0
  76. package/dist/commands/introspect.d.ts.map +1 -0
  77. package/dist/commands/introspect.js +35 -0
  78. package/dist/commands/introspect.js.map +1 -0
  79. package/dist/commands/keys.d.ts +15 -1
  80. package/dist/commands/keys.d.ts.map +1 -1
  81. package/dist/commands/keys.js +46 -10
  82. package/dist/commands/keys.js.map +1 -1
  83. package/dist/commands/link-helpers.d.ts +15 -0
  84. package/dist/commands/link-helpers.d.ts.map +1 -0
  85. package/dist/commands/link-helpers.js +225 -0
  86. package/dist/commands/link-helpers.js.map +1 -0
  87. package/dist/commands/logs.d.ts.map +1 -1
  88. package/dist/commands/logs.js +5 -4
  89. package/dist/commands/logs.js.map +1 -1
  90. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  91. package/dist/commands/migrate-from-v1.js +3 -2
  92. package/dist/commands/migrate-from-v1.js.map +1 -1
  93. package/dist/commands/migrate.d.ts.map +1 -1
  94. package/dist/commands/migrate.js +119 -26
  95. package/dist/commands/migrate.js.map +1 -1
  96. package/dist/commands/pg.d.ts.map +1 -1
  97. package/dist/commands/pg.js +11 -12
  98. package/dist/commands/pg.js.map +1 -1
  99. package/dist/commands/plugins.d.ts.map +1 -1
  100. package/dist/commands/plugins.js +55 -46
  101. package/dist/commands/plugins.js.map +1 -1
  102. package/dist/commands/pull.d.ts.map +1 -1
  103. package/dist/commands/pull.js +33 -5
  104. package/dist/commands/pull.js.map +1 -1
  105. package/dist/commands/push.d.ts.map +1 -1
  106. package/dist/commands/push.js +110 -137
  107. package/dist/commands/push.js.map +1 -1
  108. package/dist/commands/seed.d.ts.map +1 -1
  109. package/dist/commands/seed.js +4 -3
  110. package/dist/commands/seed.js.map +1 -1
  111. package/dist/commands/self-host.d.ts +2 -2
  112. package/dist/commands/self-host.d.ts.map +1 -1
  113. package/dist/commands/self-host.js +65 -50
  114. package/dist/commands/self-host.js.map +1 -1
  115. package/dist/commands/self-update.d.ts.map +1 -1
  116. package/dist/commands/self-update.js +3 -2
  117. package/dist/commands/self-update.js.map +1 -1
  118. package/dist/commands/status.d.ts +1 -1
  119. package/dist/commands/status.d.ts.map +1 -1
  120. package/dist/commands/status.js +95 -29
  121. package/dist/commands/status.js.map +1 -1
  122. package/dist/commands/types.d.ts.map +1 -1
  123. package/dist/commands/types.js +3 -2
  124. package/dist/commands/types.js.map +1 -1
  125. package/dist/commands/update.d.ts.map +1 -1
  126. package/dist/commands/update.js +54 -21
  127. package/dist/commands/update.js.map +1 -1
  128. package/dist/config.d.ts +2 -1
  129. package/dist/config.d.ts.map +1 -1
  130. package/dist/config.js.map +1 -1
  131. package/dist/dev-compose.d.ts +26 -0
  132. package/dist/dev-compose.d.ts.map +1 -1
  133. package/dist/dev-compose.js +328 -34
  134. package/dist/dev-compose.js.map +1 -1
  135. package/dist/dev-log-bus.d.ts +30 -0
  136. package/dist/dev-log-bus.d.ts.map +1 -0
  137. package/dist/dev-log-bus.js +87 -0
  138. package/dist/dev-log-bus.js.map +1 -0
  139. package/dist/dev-log-filter.d.ts +10 -0
  140. package/dist/dev-log-filter.d.ts.map +1 -0
  141. package/dist/dev-log-filter.js +36 -0
  142. package/dist/dev-log-filter.js.map +1 -0
  143. package/dist/dev-logo.d.ts +12 -0
  144. package/dist/dev-logo.d.ts.map +1 -0
  145. package/dist/dev-logo.js +56 -0
  146. package/dist/dev-logo.js.map +1 -0
  147. package/dist/dev-session.d.ts +26 -0
  148. package/dist/dev-session.d.ts.map +1 -0
  149. package/dist/dev-session.js +106 -0
  150. package/dist/dev-session.js.map +1 -0
  151. package/dist/dev-shutdown.d.ts +9 -0
  152. package/dist/dev-shutdown.d.ts.map +1 -0
  153. package/dist/dev-shutdown.js +50 -0
  154. package/dist/dev-shutdown.js.map +1 -0
  155. package/dist/dev-task-colors.d.ts +13 -0
  156. package/dist/dev-task-colors.d.ts.map +1 -0
  157. package/dist/dev-task-colors.js +43 -0
  158. package/dist/dev-task-colors.js.map +1 -0
  159. package/dist/dev-tui.d.ts +24 -0
  160. package/dist/dev-tui.d.ts.map +1 -0
  161. package/dist/dev-tui.js +188 -0
  162. package/dist/dev-tui.js.map +1 -0
  163. package/dist/diff-output.d.ts +5 -1
  164. package/dist/diff-output.d.ts.map +1 -1
  165. package/dist/diff-output.js +69 -0
  166. package/dist/diff-output.js.map +1 -1
  167. package/dist/docker-runtime.d.ts +30 -0
  168. package/dist/docker-runtime.d.ts.map +1 -0
  169. package/dist/docker-runtime.js +118 -0
  170. package/dist/docker-runtime.js.map +1 -0
  171. package/dist/engine-client.d.ts +10 -1
  172. package/dist/engine-client.d.ts.map +1 -1
  173. package/dist/engine-client.js +76 -17
  174. package/dist/engine-client.js.map +1 -1
  175. package/dist/engine-push-output.d.ts +17 -0
  176. package/dist/engine-push-output.d.ts.map +1 -0
  177. package/dist/engine-push-output.js +64 -0
  178. package/dist/engine-push-output.js.map +1 -0
  179. package/dist/ensure-binary.js +2 -2
  180. package/dist/ensure-binary.js.map +1 -1
  181. package/dist/gitignore.d.ts +8 -0
  182. package/dist/gitignore.d.ts.map +1 -0
  183. package/dist/gitignore.js +41 -0
  184. package/dist/gitignore.js.map +1 -0
  185. package/dist/kong-config.d.ts +9 -0
  186. package/dist/kong-config.d.ts.map +1 -1
  187. package/dist/kong-config.js +18 -1
  188. package/dist/kong-config.js.map +1 -1
  189. package/dist/link.d.ts +66 -0
  190. package/dist/link.d.ts.map +1 -0
  191. package/dist/link.js +160 -0
  192. package/dist/link.js.map +1 -0
  193. package/dist/process-manager.d.ts +8 -0
  194. package/dist/process-manager.d.ts.map +1 -1
  195. package/dist/process-manager.js +53 -9
  196. package/dist/process-manager.js.map +1 -1
  197. package/dist/project-config.d.ts +30 -3
  198. package/dist/project-config.d.ts.map +1 -1
  199. package/dist/project-config.js +37 -4
  200. package/dist/project-config.js.map +1 -1
  201. package/dist/prompts.d.ts +3 -0
  202. package/dist/prompts.d.ts.map +1 -0
  203. package/dist/prompts.js +3 -0
  204. package/dist/prompts.js.map +1 -0
  205. package/dist/pull-utils.d.ts +50 -14
  206. package/dist/pull-utils.d.ts.map +1 -1
  207. package/dist/pull-utils.js +152 -12
  208. package/dist/pull-utils.js.map +1 -1
  209. package/dist/resolve-target.d.ts +86 -0
  210. package/dist/resolve-target.d.ts.map +1 -0
  211. package/dist/resolve-target.js +291 -0
  212. package/dist/resolve-target.js.map +1 -0
  213. package/dist/restore-system-relation-targets.d.ts +3 -0
  214. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  215. package/dist/restore-system-relation-targets.js +45 -0
  216. package/dist/restore-system-relation-targets.js.map +1 -0
  217. package/dist/runtime-routes.d.ts.map +1 -1
  218. package/dist/runtime-routes.js +7 -0
  219. package/dist/runtime-routes.js.map +1 -1
  220. package/dist/schema-ast-v2.d.ts +1 -1
  221. package/dist/schema-ast-v2.d.ts.map +1 -1
  222. package/dist/schema-ast-v2.js +2 -2
  223. package/dist/schema-ast-v2.js.map +1 -1
  224. package/dist/schema-sources.d.ts +40 -0
  225. package/dist/schema-sources.d.ts.map +1 -0
  226. package/dist/schema-sources.js +183 -0
  227. package/dist/schema-sources.js.map +1 -0
  228. package/dist/scripts/postinstall.js +5 -1
  229. package/dist/scripts/postinstall.js.map +1 -1
  230. package/dist/seed.d.ts +8 -0
  231. package/dist/seed.d.ts.map +1 -0
  232. package/dist/seed.js +32 -0
  233. package/dist/seed.js.map +1 -0
  234. package/dist/self-host-compose.d.ts +37 -1
  235. package/dist/self-host-compose.d.ts.map +1 -1
  236. package/dist/self-host-compose.js +233 -43
  237. package/dist/self-host-compose.js.map +1 -1
  238. package/dist/storage-provision.d.ts +4 -0
  239. package/dist/storage-provision.d.ts.map +1 -1
  240. package/dist/storage-provision.js +24 -2
  241. package/dist/storage-provision.js.map +1 -1
  242. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  243. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  244. package/dist/supatype-eval-1781522769253.mjs +3 -0
  245. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  246. package/dist/systemd.js +2 -2
  247. package/dist/systemd.js.map +1 -1
  248. package/dist/target-client.d.ts +10 -0
  249. package/dist/target-client.d.ts.map +1 -0
  250. package/dist/target-client.js +22 -0
  251. package/dist/target-client.js.map +1 -0
  252. package/dist/type-extractor.d.ts +11 -0
  253. package/dist/type-extractor.d.ts.map +1 -1
  254. package/dist/type-extractor.js +95 -8
  255. package/dist/type-extractor.js.map +1 -1
  256. package/dist/ui/brand.d.ts +9 -0
  257. package/dist/ui/brand.d.ts.map +1 -0
  258. package/dist/ui/brand.js +11 -0
  259. package/dist/ui/brand.js.map +1 -0
  260. package/dist/ui/confirm.d.ts +12 -0
  261. package/dist/ui/confirm.d.ts.map +1 -0
  262. package/dist/ui/confirm.js +28 -0
  263. package/dist/ui/confirm.js.map +1 -0
  264. package/dist/ui/fatal.d.ts +10 -0
  265. package/dist/ui/fatal.d.ts.map +1 -0
  266. package/dist/ui/fatal.js +34 -0
  267. package/dist/ui/fatal.js.map +1 -0
  268. package/dist/ui/index.d.ts +9 -0
  269. package/dist/ui/index.d.ts.map +1 -0
  270. package/dist/ui/index.js +9 -0
  271. package/dist/ui/index.js.map +1 -0
  272. package/dist/ui/interactive.d.ts +3 -0
  273. package/dist/ui/interactive.d.ts.map +1 -0
  274. package/dist/ui/interactive.js +5 -0
  275. package/dist/ui/interactive.js.map +1 -0
  276. package/dist/ui/messages.d.ts +10 -0
  277. package/dist/ui/messages.d.ts.map +1 -0
  278. package/dist/ui/messages.js +35 -0
  279. package/dist/ui/messages.js.map +1 -0
  280. package/dist/ui/next-steps.d.ts +3 -0
  281. package/dist/ui/next-steps.d.ts.map +1 -0
  282. package/dist/ui/next-steps.js +10 -0
  283. package/dist/ui/next-steps.js.map +1 -0
  284. package/dist/ui/progress.d.ts +5 -0
  285. package/dist/ui/progress.d.ts.map +1 -0
  286. package/dist/ui/progress.js +24 -0
  287. package/dist/ui/progress.js.map +1 -0
  288. package/dist/ui/prompts.d.ts +14 -0
  289. package/dist/ui/prompts.d.ts.map +1 -0
  290. package/dist/ui/prompts.js +34 -0
  291. package/dist/ui/prompts.js.map +1 -0
  292. package/package.json +9 -4
  293. package/src/app/framework.ts +1 -3
  294. package/src/app/proxy-dev-app.ts +114 -6
  295. package/src/app-config.ts +80 -0
  296. package/src/binary-cache.ts +102 -52
  297. package/src/cli.ts +16 -2
  298. package/src/commands/add.ts +97 -0
  299. package/src/commands/admin.ts +39 -73
  300. package/src/commands/adopt.ts +82 -0
  301. package/src/commands/app.ts +20 -17
  302. package/src/commands/cache.ts +11 -10
  303. package/src/commands/cloud.ts +91 -142
  304. package/src/commands/db.ts +40 -63
  305. package/src/commands/deploy.ts +186 -126
  306. package/src/commands/dev.ts +95 -55
  307. package/src/commands/diff.ts +52 -43
  308. package/src/commands/doctor.ts +103 -0
  309. package/src/commands/engine.ts +5 -4
  310. package/src/commands/functions.ts +187 -123
  311. package/src/commands/generate.ts +5 -4
  312. package/src/commands/init.ts +996 -105
  313. package/src/commands/introspect.ts +48 -0
  314. package/src/commands/keys.ts +56 -14
  315. package/src/commands/link-helpers.ts +273 -0
  316. package/src/commands/logs.ts +5 -4
  317. package/src/commands/migrate-from-v1.ts +3 -2
  318. package/src/commands/migrate.ts +167 -27
  319. package/src/commands/pg.ts +13 -18
  320. package/src/commands/plugins.ts +55 -46
  321. package/src/commands/pull.ts +38 -9
  322. package/src/commands/push.ts +147 -174
  323. package/src/commands/seed.ts +5 -4
  324. package/src/commands/self-host.ts +85 -54
  325. package/src/commands/self-update.ts +3 -2
  326. package/src/commands/status.ts +102 -33
  327. package/src/commands/types.ts +3 -2
  328. package/src/commands/update.ts +59 -23
  329. package/src/config.ts +2 -1
  330. package/src/dev-compose.ts +426 -34
  331. package/src/dev-log-bus.ts +101 -0
  332. package/src/dev-log-filter.ts +32 -0
  333. package/src/dev-logo.ts +61 -0
  334. package/src/dev-session.ts +130 -0
  335. package/src/dev-shutdown.ts +54 -0
  336. package/src/dev-task-colors.ts +47 -0
  337. package/src/dev-tui.ts +232 -0
  338. package/src/diff-output.ts +79 -1
  339. package/src/docker-runtime.ts +151 -0
  340. package/src/engine-client.ts +81 -17
  341. package/src/engine-push-output.ts +75 -0
  342. package/src/ensure-binary.ts +2 -2
  343. package/src/gitignore.ts +48 -0
  344. package/src/kong-config.ts +24 -1
  345. package/src/link.ts +243 -0
  346. package/src/process-manager.ts +66 -10
  347. package/src/project-config.ts +62 -7
  348. package/src/prompts.ts +2 -0
  349. package/src/pull-utils.ts +217 -23
  350. package/src/resolve-target.ts +419 -0
  351. package/src/restore-system-relation-targets.ts +45 -0
  352. package/src/runtime-routes.ts +7 -0
  353. package/src/schema-ast-v2.ts +2 -1
  354. package/src/schema-sources.ts +248 -0
  355. package/src/scripts/postinstall.ts +7 -1
  356. package/src/seed.ts +43 -0
  357. package/src/self-host-compose.ts +261 -46
  358. package/src/storage-provision.ts +33 -1
  359. package/src/supatype-eval-1781522769253.mts +1 -0
  360. package/src/systemd.ts +2 -2
  361. package/src/target-client.ts +40 -0
  362. package/src/type-extractor.ts +124 -11
  363. package/src/ui/README.md +17 -0
  364. package/src/ui/brand.ts +12 -0
  365. package/src/ui/confirm.ts +38 -0
  366. package/src/ui/fatal.ts +43 -0
  367. package/src/ui/index.ts +8 -0
  368. package/src/ui/interactive.ts +4 -0
  369. package/src/ui/messages.ts +43 -0
  370. package/src/ui/next-steps.ts +10 -0
  371. package/src/ui/progress.ts +28 -0
  372. package/src/ui/prompts.ts +40 -0
  373. package/tests/cli-help.test.ts +27 -2
  374. package/tests/config.test.ts +29 -2
  375. package/tests/dev-ui.test.ts +139 -0
  376. package/tests/docker-runtime.test.ts +236 -0
  377. package/tests/engine-push-output.test.ts +67 -0
  378. package/tests/init.test.ts +197 -18
  379. package/tests/link.test.ts +148 -0
  380. package/tests/minisign.test.ts +102 -0
  381. package/tests/proxy-dev-app.test.ts +45 -1
  382. package/tests/pull-utils.test.ts +5 -4
  383. package/tests/runtime-contract.test.ts +186 -2
  384. package/tests/schema-sources.test.ts +119 -0
  385. package/tests/storage-provision.test.ts +100 -0
  386. package/tests/ui-confirm.test.ts +41 -0
  387. package/tests/ui-messages.test.ts +66 -0
  388. package/tsconfig.tsbuildinfo +1 -1
package/src/link.ts ADDED
@@ -0,0 +1,243 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
2
+ import { resolve } from "node:path"
3
+ import { warn } from "./ui/messages.js"
4
+
5
+ export const LINK_VERSION = 1 as const
6
+ export const LINK_FILE = ".supatype/link.json"
7
+ export const LEGACY_CLOUD_FILE = ".supatype/cloud.json"
8
+ export const LEGACY_LINKED_FILE = ".supatype/linked.json"
9
+
10
+ export type ProjectLinkKind = "cloud" | "self-host" | "local"
11
+
12
+ export interface EnvironmentTarget {
13
+ name: string
14
+ apiUrl: string
15
+ token?: string
16
+ linkedAt: string
17
+ }
18
+
19
+ export interface ProjectLink {
20
+ version: typeof LINK_VERSION
21
+ kind: ProjectLinkKind
22
+ projectRef: string
23
+ defaultEnvironment: string
24
+ token?: string
25
+ orgId?: string | undefined
26
+ cloudApiUrl?: string
27
+ linkedAt: string
28
+ environments: Record<string, EnvironmentTarget>
29
+ }
30
+
31
+ export interface LocalEnvironment {
32
+ target: "local"
33
+ apiUrl: string
34
+ databaseUrl: string
35
+ projectRef: string
36
+ kongPort: number
37
+ provider: "docker" | "native"
38
+ }
39
+
40
+ export interface BranchContext {
41
+ mode: "branch"
42
+ branchId: string
43
+ apiUrl: string
44
+ token?: string | undefined
45
+ }
46
+
47
+ interface LegacyCloudFile {
48
+ apiUrl?: string
49
+ token?: string
50
+ projectSlug?: string
51
+ orgId?: string
52
+ }
53
+
54
+ interface LegacyLinkedFile {
55
+ ref?: string
56
+ orgId?: string
57
+ }
58
+
59
+ export function writeLocalEnvironment(cwd: string, env: LocalEnvironment): void {
60
+ const dir = resolve(cwd, ".supatype")
61
+ mkdirSync(dir, { recursive: true })
62
+ writeFileSync(resolve(dir, "environment.json"), `${JSON.stringify(env, null, 2)}\n`, "utf8")
63
+ }
64
+
65
+ export function loadLocalEnvironment(cwd: string): LocalEnvironment | null {
66
+ const path = resolve(cwd, ".supatype/environment.json")
67
+ if (!existsSync(path)) return null
68
+ return JSON.parse(readFileSync(path, "utf8")) as LocalEnvironment
69
+ }
70
+
71
+ export function linkPath(cwd: string): string {
72
+ return resolve(cwd, LINK_FILE)
73
+ }
74
+
75
+ export function loadProjectLink(cwd: string): ProjectLink | null {
76
+ migrateLegacyLinkFiles(cwd)
77
+ const path = linkPath(cwd)
78
+ if (!existsSync(path)) return null
79
+ return JSON.parse(readFileSync(path, "utf8")) as ProjectLink
80
+ }
81
+
82
+ export function saveProjectLink(cwd: string, link: ProjectLink): void {
83
+ const dir = resolve(cwd, ".supatype")
84
+ mkdirSync(dir, { recursive: true })
85
+ writeFileSync(linkPath(cwd), `${JSON.stringify(link, null, 2)}\n`, "utf8")
86
+ }
87
+
88
+ export function isProjectLinked(cwd: string): boolean {
89
+ const link = loadProjectLink(cwd)
90
+ return Boolean(link?.projectRef && Object.keys(link.environments).length > 0)
91
+ }
92
+
93
+ export function resolveEnvironmentName(link: ProjectLink, envFlag?: string): string {
94
+ if (envFlag?.trim()) return envFlag.trim()
95
+ return link.defaultEnvironment || "production"
96
+ }
97
+
98
+ export function getEnvironmentTarget(link: ProjectLink, envName: string): EnvironmentTarget | null {
99
+ return link.environments[envName] ?? null
100
+ }
101
+
102
+ export function resolveEnvironmentToken(link: ProjectLink, env: EnvironmentTarget): string | undefined {
103
+ return env.token ?? link.token
104
+ }
105
+
106
+ let migrationWarned = false
107
+
108
+ /** Merge legacy cloud.json + linked.json into link.json once. */
109
+ export function migrateLegacyLinkFiles(cwd: string): void {
110
+ const target = linkPath(cwd)
111
+ if (existsSync(target)) return
112
+
113
+ const cloudPath = resolve(cwd, LEGACY_CLOUD_FILE)
114
+ const linkedPath = resolve(cwd, LEGACY_LINKED_FILE)
115
+ const hasCloud = existsSync(cloudPath)
116
+ const hasLinked = existsSync(linkedPath)
117
+ if (!hasCloud && !hasLinked) return
118
+
119
+ let cloud: LegacyCloudFile | null = null
120
+ let linked: LegacyLinkedFile | null = null
121
+
122
+ if (hasCloud) {
123
+ cloud = JSON.parse(readFileSync(cloudPath, "utf8")) as LegacyCloudFile
124
+ }
125
+ if (hasLinked) {
126
+ linked = JSON.parse(readFileSync(linkedPath, "utf8")) as LegacyLinkedFile
127
+ }
128
+
129
+ const projectRef = cloud?.projectSlug ?? linked?.ref
130
+ if (!projectRef || !cloud?.token) {
131
+ return
132
+ }
133
+
134
+ const now = new Date().toISOString()
135
+ const link: ProjectLink = {
136
+ version: LINK_VERSION,
137
+ kind: "cloud",
138
+ projectRef,
139
+ defaultEnvironment: "production",
140
+ token: cloud.token,
141
+ cloudApiUrl: cloud.apiUrl ?? "https://api.supatype.com",
142
+ linkedAt: now,
143
+ environments: {
144
+ production: {
145
+ name: "production",
146
+ apiUrl: cloud.apiUrl ?? "https://api.supatype.com",
147
+ linkedAt: now,
148
+ },
149
+ },
150
+ ...(cloud.orgId !== undefined
151
+ ? { orgId: cloud.orgId }
152
+ : linked?.orgId !== undefined
153
+ ? { orgId: linked.orgId }
154
+ : {}),
155
+ }
156
+
157
+ saveProjectLink(cwd, link)
158
+
159
+ if (!migrationWarned) {
160
+ migrationWarned = true
161
+ warn(
162
+ "Migrated .supatype/cloud.json → .supatype/link.json (legacy files kept; remove manually when ready).",
163
+ )
164
+ }
165
+ }
166
+
167
+ export function createSelfHostLink(params: {
168
+ projectRef: string
169
+ apiUrl: string
170
+ token: string
171
+ envName?: string
172
+ existing?: ProjectLink | null
173
+ }): ProjectLink {
174
+ const envName = params.envName ?? "production"
175
+ const now = new Date().toISOString()
176
+ const env: EnvironmentTarget = {
177
+ name: envName,
178
+ apiUrl: params.apiUrl.replace(/\/$/, ""),
179
+ token: params.token,
180
+ linkedAt: now,
181
+ }
182
+
183
+ if (params.existing) {
184
+ return {
185
+ ...params.existing,
186
+ kind: params.existing.kind === "cloud" ? "self-host" : params.existing.kind,
187
+ projectRef: params.projectRef,
188
+ defaultEnvironment: params.existing.defaultEnvironment || envName,
189
+ linkedAt: now,
190
+ environments: {
191
+ ...params.existing.environments,
192
+ [envName]: env,
193
+ },
194
+ }
195
+ }
196
+
197
+ return {
198
+ version: LINK_VERSION,
199
+ kind: "self-host",
200
+ projectRef: params.projectRef,
201
+ defaultEnvironment: envName,
202
+ token: params.token,
203
+ linkedAt: now,
204
+ environments: { [envName]: env },
205
+ }
206
+ }
207
+
208
+ export function createCloudLink(params: {
209
+ projectRef: string
210
+ cloudApiUrl: string
211
+ token: string
212
+ orgId?: string | undefined
213
+ environments?: Array<{ name: string; apiUrl: string }>
214
+ existing?: ProjectLink | null
215
+ }): ProjectLink {
216
+ const now = new Date().toISOString()
217
+ const envMap: Record<string, EnvironmentTarget> = {}
218
+ const envs = params.environments?.length
219
+ ? params.environments
220
+ : [{ name: "production", apiUrl: params.cloudApiUrl }]
221
+
222
+ for (const e of envs) {
223
+ envMap[e.name] = {
224
+ name: e.name,
225
+ apiUrl: e.apiUrl.replace(/\/$/, ""),
226
+ linkedAt: now,
227
+ }
228
+ }
229
+
230
+ return {
231
+ version: LINK_VERSION,
232
+ kind: "cloud",
233
+ projectRef: params.projectRef,
234
+ defaultEnvironment: "production",
235
+ token: params.token,
236
+ cloudApiUrl: params.cloudApiUrl.replace(/\/$/, ""),
237
+ linkedAt: now,
238
+ environments: params.existing
239
+ ? { ...params.existing.environments, ...envMap }
240
+ : envMap,
241
+ ...(params.orgId !== undefined ? { orgId: params.orgId } : {}),
242
+ }
243
+ }
@@ -3,10 +3,11 @@
3
3
  * colored prefix, and restart on crash with exponential backoff.
4
4
  */
5
5
 
6
- import { type ChildProcess, spawn } from "node:child_process"
6
+ import { type ChildProcess, spawn, spawnSync } from "node:child_process"
7
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
8
8
  import { unlink } from "node:fs/promises"
9
9
  import { join } from "node:path"
10
+ import { enhanceProcessOptions } from "./dev-session.js"
10
11
 
11
12
  export interface ProcessOptions {
12
13
  /** Human-readable label (used in log prefix and PID filename). */
@@ -27,6 +28,12 @@ export interface ProcessOptions {
27
28
  onExit?: () => void
28
29
  /** Use shell to spawn (required for pnpm/npm/yarn .cmd shims on Windows). */
29
30
  shell?: boolean
31
+ /** When set (TUI mode), receive log lines instead of writing to stdout/stderr. */
32
+ onLine?: (line: string, stream: "stdout" | "stderr") => void
33
+ /** Return false to drop a line before logging. */
34
+ shouldLogLine?: (line: string) => boolean
35
+ /** Called before auto-restart after a non-zero exit (e.g. free a stale port). */
36
+ beforeRestart?: () => void
30
37
  }
31
38
 
32
39
  const RESET = "\x1b[0m"
@@ -35,13 +42,24 @@ export class ProcessManager {
35
42
  private child: ChildProcess | null = null
36
43
  private stopped = false
37
44
  private backoffMs: number
38
- private opts: Required<ProcessOptions>
45
+ private opts: ProcessOptions & {
46
+ colour: string
47
+ cwd: string
48
+ env: Record<string, string>
49
+ initialBackoffMs: number
50
+ maxBackoffMs: number
51
+ onExit: () => void
52
+ shell: boolean
53
+ }
54
+ /** Skip immediate duplicate lines (npm prints script banners to both streams). */
55
+ private lastLoggedLine = ""
39
56
 
40
57
  constructor(
41
58
  private readonly bin: string,
42
59
  private readonly args: string[],
43
60
  opts: ProcessOptions,
44
61
  ) {
62
+ const merged = enhanceProcessOptions(opts.label, opts)
45
63
  this.opts = {
46
64
  colour: "\x1b[36m",
47
65
  cwd: process.cwd(),
@@ -50,7 +68,7 @@ export class ProcessManager {
50
68
  maxBackoffMs: 30_000,
51
69
  onExit: () => {},
52
70
  shell: false,
53
- ...opts,
71
+ ...merged,
54
72
  }
55
73
  this.backoffMs = this.opts.initialBackoffMs
56
74
  }
@@ -64,7 +82,20 @@ export class ProcessManager {
64
82
  /** Stop the process and clear the PID file. */
65
83
  async stop(): Promise<void> {
66
84
  this.stopped = true
67
- if (this.child && !this.child.killed) {
85
+ if (!this.child || this.child.killed) {
86
+ await this.clearPid()
87
+ return
88
+ }
89
+
90
+ const pid = this.child.pid
91
+ if (process.platform === "win32" && this.opts.shell && pid) {
92
+ spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" })
93
+ this.child = null
94
+ await this.clearPid()
95
+ return
96
+ }
97
+
98
+ if (!this.child.killed) {
68
99
  this.child.kill("SIGTERM")
69
100
  // Give it 5s to exit gracefully, then SIGKILL.
70
101
  await new Promise<void>((resolve) => {
@@ -99,22 +130,43 @@ export class ProcessManager {
99
130
  ? `${this.opts.colour}[${this.opts.label}]${RESET} `
100
131
  : `[${this.opts.label}] `
101
132
 
133
+ const logLine = (line: string, stream: NodeJS.WriteStream): void => {
134
+ if (!line) return
135
+ if (this.opts.shouldLogLine && !this.opts.shouldLogLine(line)) return
136
+ if (this.opts.onLine) {
137
+ if (line !== this.lastLoggedLine) {
138
+ this.lastLoggedLine = line
139
+ this.opts.onLine(line, stream === process.stderr ? "stderr" : "stdout")
140
+ }
141
+ return
142
+ }
143
+ if (line === this.lastLoggedLine) return
144
+ this.lastLoggedLine = line
145
+ stream.write(prefix + line + "\n")
146
+ }
147
+
102
148
  this.child.stdout?.on("data", (chunk: Buffer) => {
103
149
  for (const line of chunk.toString().split("\n")) {
104
- if (line) process.stdout.write(prefix + line + "\n")
150
+ logLine(line, process.stdout)
105
151
  }
106
152
  })
107
153
 
108
154
  this.child.stderr?.on("data", (chunk: Buffer) => {
109
155
  for (const line of chunk.toString().split("\n")) {
110
- if (line) process.stderr.write(prefix + line + "\n")
156
+ logLine(line, process.stderr)
111
157
  }
112
158
  })
113
159
 
114
160
  this.child.once("error", (err) => {
115
161
  if (this.stopped) return
116
- process.stderr.write(`${prefix}failed to start: ${err.message}\n`)
162
+ const message = `${prefix}failed to start: ${err.message}\n`
163
+ if (this.opts.onLine) {
164
+ this.opts.onLine(`failed to start: ${err.message}`, "stderr")
165
+ } else {
166
+ process.stderr.write(message)
167
+ }
117
168
  setTimeout(() => {
169
+ this.opts.beforeRestart?.()
118
170
  this.backoffMs = Math.min(this.backoffMs * 2, this.opts.maxBackoffMs)
119
171
  this.spawn()
120
172
  }, this.backoffMs)
@@ -129,11 +181,15 @@ export class ProcessManager {
129
181
  }
130
182
 
131
183
  const reason = signal ? `signal ${signal}` : `code ${code}`
132
- process.stderr.write(
133
- `${prefix}process exited (${reason}), restarting in ${this.backoffMs}ms\n`,
134
- )
184
+ const restartMsg = `${prefix}process exited (${reason}), restarting in ${this.backoffMs}ms\n`
185
+ if (this.opts.onLine) {
186
+ this.opts.onLine(`process exited (${reason}), restarting in ${this.backoffMs}ms`, "stderr")
187
+ } else {
188
+ process.stderr.write(restartMsg)
189
+ }
135
190
 
136
191
  setTimeout(() => {
192
+ this.opts.beforeRestart?.()
137
193
  this.backoffMs = Math.min(this.backoffMs * 2, this.opts.maxBackoffMs)
138
194
  this.spawn()
139
195
  }, this.backoffMs)
@@ -59,6 +59,16 @@ export interface SupatypeProjectConfig {
59
59
  postgrestPort?: number
60
60
  /** Domain for ACME TLS certificate (mode=standalone). */
61
61
  domain?: string
62
+ /**
63
+ * TLS for self-host HTTPS (Kong ACME / Let's Encrypt).
64
+ * Requires `mode: "standalone"` and a non-empty `domain`.
65
+ */
66
+ tls?: {
67
+ /** ACME contact email for Let's Encrypt (required to enable HTTPS). */
68
+ email?: string
69
+ /** "kong" (default) = Kong acme plugin; "none" = stay HTTP even with a domain. */
70
+ provider?: "kong" | "none"
71
+ }
62
72
  }
63
73
  app: {
64
74
  /**
@@ -85,10 +95,13 @@ export interface SupatypeProjectConfig {
85
95
  start?: string
86
96
  }
87
97
  /**
88
- * Pinned binary versions per component. Use **`"local"`** with the matching **`overrides.*`**
89
- * entry when testing a local build (Phase 10.7).
98
+ * Optional pins for engine, server, postgres, and deno.
99
+ * Omitted = resolve latest from CDN at runtime (native) or use Docker :latest.
100
+ * When set, native binaries cache under ~/.supatype/cache/{component}/{version}/
101
+ * and Docker image tags are synced to `.env` on `supatype dev` / `supatype push`.
102
+ * Use **`"local"`** with the matching **`overrides.*`** entry for contributor builds.
90
103
  */
91
- versions: ComponentVersions
104
+ versions?: Partial<ComponentVersions>
92
105
  /**
93
106
  * Override component binaries with local build paths.
94
107
  * Intended for supatype contributors testing local changes.
@@ -198,6 +211,14 @@ export interface SupatypeProjectConfig {
198
211
  /** Custom response headers for the deployed static site. */
199
212
  headers?: Record<string, string>
200
213
  }
214
+ /**
215
+ * Persistent environment defaults for `resolveTarget()` when `--env` is omitted.
216
+ * Ephemeral schema branches (Phase 22) use `.supatype/branch.json`, not this block.
217
+ */
218
+ environments?: {
219
+ default?: string
220
+ branchDefaults?: Record<string, string>
221
+ }
201
222
  /**
202
223
  * Optional Postgres URL for CLI commands that talk to the DB (`push`, `migrate`, …).
203
224
  * When omitted, `DATABASE_URL` from the environment is used, then a local default DSN.
@@ -233,7 +254,9 @@ export function mergeProjectConfig(
233
254
  database: { ...base.database, ...override.database },
234
255
  server: { ...base.server, ...override.server },
235
256
  app: { ...base.app, ...override.app },
236
- versions: { ...base.versions, ...override.versions },
257
+ ...(base.versions !== undefined || override.versions !== undefined
258
+ ? { versions: { ...base.versions, ...override.versions } }
259
+ : {}),
237
260
  ...(base.overrides !== undefined || override.overrides !== undefined
238
261
  ? {
239
262
  overrides: {
@@ -285,6 +308,23 @@ export function mergeProjectConfig(
285
308
  ...(base.admin !== undefined || override.admin !== undefined
286
309
  ? { admin: { ...base.admin, ...override.admin } as NonNullable<SupatypeProjectConfig["admin"]> }
287
310
  : {}),
311
+ ...(base.environments !== undefined || override.environments !== undefined
312
+ ? (() => {
313
+ const b = base.environments
314
+ const o = override.environments
315
+ const mergedBranchDefaults =
316
+ b?.branchDefaults !== undefined || o?.branchDefaults !== undefined
317
+ ? { ...(b?.branchDefaults ?? {}), ...(o?.branchDefaults ?? {}) }
318
+ : undefined
319
+ return {
320
+ environments: {
321
+ ...b,
322
+ ...o,
323
+ ...(mergedBranchDefaults !== undefined ? { branchDefaults: mergedBranchDefaults } : {}),
324
+ } as NonNullable<SupatypeProjectConfig["environments"]>,
325
+ }
326
+ })()
327
+ : {}),
288
328
  }
289
329
  }
290
330
 
@@ -307,9 +347,6 @@ export function validateProjectConfig(raw: unknown, filename: string): SupatypeP
307
347
  if (!cfg["app"]) {
308
348
  throw new Error(`${filename}: app section is required`)
309
349
  }
310
- if (!cfg["versions"]) {
311
- throw new Error(`${filename}: versions section is required`)
312
- }
313
350
 
314
351
  return raw as SupatypeProjectConfig
315
352
  }
@@ -363,6 +400,24 @@ export function serverBaseUrl(cfg: SupatypeProjectConfig): string | undefined {
363
400
  }
364
401
  }
365
402
 
403
+ /**
404
+ * True when `supatype self-host compose` should render Kong ACME TLS (Let's Encrypt).
405
+ * Gated on a real self-host render (not `supatype dev`), standalone mode, a non-empty
406
+ * domain, an ACME contact email, and `tls.provider !== "none"`.
407
+ */
408
+ export function selfHostTlsEnabled(
409
+ cfg: SupatypeProjectConfig,
410
+ devLocal = false,
411
+ ): boolean {
412
+ if (devLocal) return false
413
+ if (cfg.server.mode !== "standalone") return false
414
+ const domain = cfg.server.domain?.trim()
415
+ if (!domain) return false
416
+ const tls = cfg.server.tls
417
+ if (!tls || tls.provider === "none") return false
418
+ return Boolean(tls.email?.trim())
419
+ }
420
+
366
421
  /** Resolved runtime provider (`config.provider` ?? `database.provider` ?? native). */
367
422
  export function resolveRuntimeProvider(cfg: SupatypeProjectConfig): "native" | "docker" {
368
423
  return cfg.provider ?? cfg.database.provider ?? "native"
package/src/prompts.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** @deprecated Import from `./ui/prompts.js` or `./ui/index.js`. */
2
+ export { printLogo, ensureNotCancelled, clack } from "./ui/prompts.js"