@supatype/cli 0.1.0-alpha.9 → 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 (383) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +270 -65
  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/self-host-compose.d.ts +37 -1
  231. package/dist/self-host-compose.d.ts.map +1 -1
  232. package/dist/self-host-compose.js +233 -43
  233. package/dist/self-host-compose.js.map +1 -1
  234. package/dist/storage-provision.d.ts +4 -0
  235. package/dist/storage-provision.d.ts.map +1 -1
  236. package/dist/storage-provision.js +24 -2
  237. package/dist/storage-provision.js.map +1 -1
  238. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  239. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  240. package/dist/supatype-eval-1781522769253.mjs +3 -0
  241. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  242. package/dist/systemd.js +2 -2
  243. package/dist/systemd.js.map +1 -1
  244. package/dist/target-client.d.ts +10 -0
  245. package/dist/target-client.d.ts.map +1 -0
  246. package/dist/target-client.js +22 -0
  247. package/dist/target-client.js.map +1 -0
  248. package/dist/type-extractor.d.ts +11 -0
  249. package/dist/type-extractor.d.ts.map +1 -1
  250. package/dist/type-extractor.js +95 -8
  251. package/dist/type-extractor.js.map +1 -1
  252. package/dist/ui/brand.d.ts +9 -0
  253. package/dist/ui/brand.d.ts.map +1 -0
  254. package/dist/ui/brand.js +11 -0
  255. package/dist/ui/brand.js.map +1 -0
  256. package/dist/ui/confirm.d.ts +12 -0
  257. package/dist/ui/confirm.d.ts.map +1 -0
  258. package/dist/ui/confirm.js +28 -0
  259. package/dist/ui/confirm.js.map +1 -0
  260. package/dist/ui/fatal.d.ts +10 -0
  261. package/dist/ui/fatal.d.ts.map +1 -0
  262. package/dist/ui/fatal.js +34 -0
  263. package/dist/ui/fatal.js.map +1 -0
  264. package/dist/ui/index.d.ts +9 -0
  265. package/dist/ui/index.d.ts.map +1 -0
  266. package/dist/ui/index.js +9 -0
  267. package/dist/ui/index.js.map +1 -0
  268. package/dist/ui/interactive.d.ts +3 -0
  269. package/dist/ui/interactive.d.ts.map +1 -0
  270. package/dist/ui/interactive.js +5 -0
  271. package/dist/ui/interactive.js.map +1 -0
  272. package/dist/ui/messages.d.ts +10 -0
  273. package/dist/ui/messages.d.ts.map +1 -0
  274. package/dist/ui/messages.js +35 -0
  275. package/dist/ui/messages.js.map +1 -0
  276. package/dist/ui/next-steps.d.ts +3 -0
  277. package/dist/ui/next-steps.d.ts.map +1 -0
  278. package/dist/ui/next-steps.js +10 -0
  279. package/dist/ui/next-steps.js.map +1 -0
  280. package/dist/ui/progress.d.ts +5 -0
  281. package/dist/ui/progress.d.ts.map +1 -0
  282. package/dist/ui/progress.js +24 -0
  283. package/dist/ui/progress.js.map +1 -0
  284. package/dist/ui/prompts.d.ts +14 -0
  285. package/dist/ui/prompts.d.ts.map +1 -0
  286. package/dist/ui/prompts.js +34 -0
  287. package/dist/ui/prompts.js.map +1 -0
  288. package/package.json +3 -2
  289. package/src/app/framework.ts +1 -3
  290. package/src/app/proxy-dev-app.ts +114 -6
  291. package/src/app-config.ts +80 -0
  292. package/src/binary-cache.ts +102 -52
  293. package/src/cli.ts +16 -2
  294. package/src/commands/add.ts +97 -0
  295. package/src/commands/admin.ts +39 -73
  296. package/src/commands/adopt.ts +82 -0
  297. package/src/commands/app.ts +20 -17
  298. package/src/commands/cache.ts +11 -10
  299. package/src/commands/cloud.ts +91 -142
  300. package/src/commands/db.ts +40 -63
  301. package/src/commands/deploy.ts +186 -126
  302. package/src/commands/dev.ts +95 -55
  303. package/src/commands/diff.ts +52 -43
  304. package/src/commands/doctor.ts +103 -0
  305. package/src/commands/engine.ts +5 -4
  306. package/src/commands/functions.ts +187 -123
  307. package/src/commands/generate.ts +5 -4
  308. package/src/commands/init.ts +996 -105
  309. package/src/commands/introspect.ts +48 -0
  310. package/src/commands/keys.ts +56 -14
  311. package/src/commands/link-helpers.ts +273 -0
  312. package/src/commands/logs.ts +5 -4
  313. package/src/commands/migrate-from-v1.ts +3 -2
  314. package/src/commands/migrate.ts +167 -27
  315. package/src/commands/pg.ts +13 -18
  316. package/src/commands/plugins.ts +55 -46
  317. package/src/commands/pull.ts +38 -9
  318. package/src/commands/push.ts +147 -174
  319. package/src/commands/seed.ts +5 -4
  320. package/src/commands/self-host.ts +85 -54
  321. package/src/commands/self-update.ts +3 -2
  322. package/src/commands/status.ts +102 -33
  323. package/src/commands/types.ts +3 -2
  324. package/src/commands/update.ts +59 -23
  325. package/src/config.ts +2 -1
  326. package/src/dev-compose.ts +426 -34
  327. package/src/dev-log-bus.ts +101 -0
  328. package/src/dev-log-filter.ts +32 -0
  329. package/src/dev-logo.ts +61 -0
  330. package/src/dev-session.ts +130 -0
  331. package/src/dev-shutdown.ts +54 -0
  332. package/src/dev-task-colors.ts +47 -0
  333. package/src/dev-tui.ts +232 -0
  334. package/src/diff-output.ts +79 -1
  335. package/src/docker-runtime.ts +151 -0
  336. package/src/engine-client.ts +81 -17
  337. package/src/engine-push-output.ts +75 -0
  338. package/src/ensure-binary.ts +2 -2
  339. package/src/gitignore.ts +48 -0
  340. package/src/kong-config.ts +24 -1
  341. package/src/link.ts +243 -0
  342. package/src/process-manager.ts +66 -10
  343. package/src/project-config.ts +62 -7
  344. package/src/prompts.ts +2 -0
  345. package/src/pull-utils.ts +217 -23
  346. package/src/resolve-target.ts +419 -0
  347. package/src/restore-system-relation-targets.ts +45 -0
  348. package/src/runtime-routes.ts +7 -0
  349. package/src/schema-ast-v2.ts +2 -1
  350. package/src/schema-sources.ts +248 -0
  351. package/src/scripts/postinstall.ts +7 -1
  352. package/src/self-host-compose.ts +261 -46
  353. package/src/storage-provision.ts +33 -1
  354. package/src/supatype-eval-1781522769253.mts +1 -0
  355. package/src/systemd.ts +2 -2
  356. package/src/target-client.ts +40 -0
  357. package/src/type-extractor.ts +124 -11
  358. package/src/ui/README.md +17 -0
  359. package/src/ui/brand.ts +12 -0
  360. package/src/ui/confirm.ts +38 -0
  361. package/src/ui/fatal.ts +43 -0
  362. package/src/ui/index.ts +8 -0
  363. package/src/ui/interactive.ts +4 -0
  364. package/src/ui/messages.ts +43 -0
  365. package/src/ui/next-steps.ts +10 -0
  366. package/src/ui/progress.ts +28 -0
  367. package/src/ui/prompts.ts +40 -0
  368. package/tests/cli-help.test.ts +27 -2
  369. package/tests/config.test.ts +29 -2
  370. package/tests/dev-ui.test.ts +139 -0
  371. package/tests/docker-runtime.test.ts +236 -0
  372. package/tests/engine-push-output.test.ts +67 -0
  373. package/tests/init.test.ts +197 -18
  374. package/tests/link.test.ts +148 -0
  375. package/tests/minisign.test.ts +102 -0
  376. package/tests/proxy-dev-app.test.ts +45 -1
  377. package/tests/pull-utils.test.ts +5 -4
  378. package/tests/runtime-contract.test.ts +186 -2
  379. package/tests/schema-sources.test.ts +119 -0
  380. package/tests/storage-provision.test.ts +100 -0
  381. package/tests/ui-confirm.test.ts +41 -0
  382. package/tests/ui-messages.test.ts +66 -0
  383. package/tsconfig.tsbuildinfo +1 -1
@@ -10,6 +10,7 @@ import { startProxyDevApp } from "./app/proxy-dev-app.js"
10
10
  import { loadSchemaAst } from "./config.js"
11
11
  import {
12
12
  COMPOSE_DEV_KONG_PORT,
13
+ connectionString,
13
14
  projectRootFromConfig,
14
15
  resolveRuntimeProvider,
15
16
  schemaPathFromProject,
@@ -17,17 +18,47 @@ import {
17
18
  } from "./project-config.js"
18
19
  import { signJwt } from "./jwt.js"
19
20
  import { isPortInUse } from "./postgres-ctl.js"
20
- import { composeProjectName, runDockerCompose, writeSelfHostCompose, type SelfHostComposePaths } from "./self-host-compose.js"
21
+ import {
22
+ COMPOSE_PINNED_IMAGE_ENV_KEYS,
23
+ composeDockerImageEnv,
24
+ composeProjectName,
25
+ exitComposeFailed,
26
+ runDockerCompose,
27
+ schemaEngineImageForPush,
28
+ writeSelfHostCompose,
29
+ type SelfHostComposePaths,
30
+ } from "./self-host-compose.js"
31
+ import type { DockerBrandOptions } from "./docker-runtime.js"
21
32
  import { hasEngineOverride } from "./binary-cache.js"
22
33
  import { startStudioViteDevServer } from "./studio-dev-server.js"
23
- import { ensureEngine, engineRequest } from "./engine-client.js"
34
+ import { ensureEngine, engineRequest, type DiffResult } from "./engine-client.js"
35
+ import { writeSchemaSourcePushArtifacts, type SchemaSourcePushArtifacts } from "./schema-sources.js"
36
+ import { endDevSession } from "./dev-session.js"
37
+ import { writeLocalEnvironment } from "./link.js"
38
+ import { registerDevShutdown } from "./dev-shutdown.js"
39
+ import {
40
+ filterComposeNoise,
41
+ formatEnginePushMessage,
42
+ parseEngineJsonOutput,
43
+ parseEnginePushOutput,
44
+ } from "./engine-push-output.js"
24
45
  import { withAdminRoles } from "./studio-admin-roles.js"
46
+ import { restoreSystemRelationTargets } from "./restore-system-relation-targets.js"
47
+ import { provisionBucketsFromAst } from "./storage-provision.js"
48
+ import type { ExtractedSchemaAstV2 } from "./schema-ast-v2.js"
25
49
 
26
50
  const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
27
51
 
28
52
  /** Default host port for compose Postgres when `overrides.engine` is set (devLocal). */
29
53
  const COMPOSE_DEV_DB_PORT = 54329
30
54
 
55
+ /** Sync optional Docker image pins from config into `.env` (no JWT rotation). */
56
+ export function syncComposeImagePins(cwd: string, config: SupatypeProjectConfig): void {
57
+ const imagePins = composeDockerImageEnv(config)
58
+ const removeImageKeys = COMPOSE_PINNED_IMAGE_ENV_KEYS.filter((key) => !(key in imagePins))
59
+ upsertEnvFile(cwd, imagePins, removeImageKeys)
60
+ }
61
+
31
62
  export interface DevComposeOptions {
32
63
  watch: boolean
33
64
  }
@@ -71,6 +102,78 @@ function hostComposeDbUrl(cwd: string): string {
71
102
  return `postgresql://${user}:${pass}@127.0.0.1:${port}/${db}?sslmode=disable`
72
103
  }
73
104
 
105
+ /**
106
+ * When `provider: docker` and `overrides.engine` is set, ensure Postgres is published
107
+ * on the host (SUPATYPE_DEV_DB_PORT) so the local engine binary can connect.
108
+ */
109
+ export async function ensureDockerDbPublishedForHostEngine(
110
+ cwd: string,
111
+ config: SupatypeProjectConfig,
112
+ brand?: DockerBrandOptions,
113
+ ): Promise<void> {
114
+ if (resolveRuntimeProvider(config) !== "docker") {
115
+ throw new Error("ensureDockerDbPublishedForHostEngine requires provider: docker")
116
+ }
117
+ if (!hasEngineOverride(config)) {
118
+ throw new Error(
119
+ "Docker Postgres is not published to the host without overrides.engine. " +
120
+ "Set overrides.engine in supatype.local.config.ts or pass --connection.",
121
+ )
122
+ }
123
+
124
+ const project = composeProjectName(config.project.name)
125
+ const kongPort = await resolveKongPort(cwd)
126
+ const devDbPort = await resolveDevDbPort(cwd)
127
+
128
+ const now = Math.floor(Date.now() / 1000)
129
+ const jwtBase = { iss: "supatype", iat: now, exp: now + 315_360_000 }
130
+ const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
131
+ const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
132
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
133
+
134
+ const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
135
+ const up = runDockerCompose(paths.composePath, ["up", "-d", "db"], cwd, project, {
136
+ quiet: true,
137
+ ...(brand !== undefined && { brand }),
138
+ })
139
+ if (up !== 0) {
140
+ exitComposeFailed(up, "Could not start Postgres (compose db service).", brand)
141
+ }
142
+ await waitComposeHealthy(paths, cwd, 120_000, project)
143
+ }
144
+
145
+ /**
146
+ * True when CLI should publish local Compose Postgres for the host-side engine
147
+ * (local dev with overrides.engine). False for remote DB URLs via config or --connection.
148
+ */
149
+ export function usesLocalDockerEngineDb(
150
+ config: SupatypeProjectConfig,
151
+ explicitConnection?: string,
152
+ ): boolean {
153
+ if (explicitConnection?.trim()) return false
154
+ if (config.connection?.trim()) return false
155
+ return resolveRuntimeProvider(config) === "docker" && hasEngineOverride(config)
156
+ }
157
+
158
+ /**
159
+ * Resolve a Postgres URL reachable from the host-side engine binary.
160
+ * Local docker + overrides.engine → SUPATYPE_DEV_DB_PORT on localhost.
161
+ * Remote self-host → set `connection` in config or pass `--connection`.
162
+ */
163
+ export async function resolveHostEngineDatabaseUrl(
164
+ cwd: string,
165
+ config: SupatypeProjectConfig,
166
+ explicit?: string,
167
+ ): Promise<string> {
168
+ if (explicit?.trim()) return explicit
169
+ if (config.connection?.trim()) return config.connection
170
+ if (usesLocalDockerEngineDb(config)) {
171
+ await ensureDockerDbPublishedForHostEngine(cwd, config)
172
+ return hostComposeDbUrl(cwd)
173
+ }
174
+ return connectionString(config)
175
+ }
176
+
74
177
  async function resolveKongPort(cwd: string): Promise<number> {
75
178
  const envPath = join(cwd, ".env")
76
179
  if (existsSync(envPath)) {
@@ -82,10 +185,14 @@ async function resolveKongPort(cwd: string): Promise<number> {
82
185
  return port
83
186
  }
84
187
 
85
- function upsertEnvFile(cwd: string, updates: Record<string, string>): void {
188
+ function upsertEnvFile(
189
+ cwd: string,
190
+ updates: Record<string, string>,
191
+ removeKeys: readonly string[] = [],
192
+ ): void {
86
193
  const envPath = join(cwd, ".env")
87
194
  const existing = existsSync(envPath) ? readFileSync(envPath, "utf8") : ""
88
- const keys = new Set(Object.keys(updates))
195
+ const keys = new Set([...Object.keys(updates), ...removeKeys])
89
196
  const kept = existing
90
197
  .split("\n")
91
198
  .filter((line) => {
@@ -96,15 +203,17 @@ function upsertEnvFile(cwd: string, updates: Record<string, string>): void {
96
203
  writeFileSync(envPath, `${merged.join("\n").trimEnd()}\n`, "utf8")
97
204
  }
98
205
 
99
- /** Keep compose + Studio + Astro on the same freshly signed dev JWTs every `dev` / `push`. */
206
+ /** Keep compose + Studio on the same freshly signed dev JWTs; sync optional image pins from config. */
100
207
  function ensureDevComposeEnv(
101
208
  cwd: string,
209
+ config: SupatypeProjectConfig,
102
210
  anonKey: string,
103
211
  serviceRoleKey: string,
104
212
  kongPort: number,
105
213
  devDbPort?: number,
106
214
  ): void {
107
215
  const apiUrl = `http://localhost:${kongPort}`
216
+ const imagePins = composeDockerImageEnv(config)
108
217
  const updates: Record<string, string> = {
109
218
  POSTGRES_USER: "supatype_admin",
110
219
  POSTGRES_PASSWORD: "postgres",
@@ -118,11 +227,15 @@ function ensureDevComposeEnv(
118
227
  API_EXTERNAL_URL: apiUrl,
119
228
  SITE_URL: apiUrl,
120
229
  GOTRUE_MAILER_AUTOCONFIRM: "true",
230
+ ...imagePins,
121
231
  }
122
232
  if (devDbPort !== undefined) {
123
233
  updates.SUPATYPE_DEV_DB_PORT = String(devDbPort)
234
+ updates.DATABASE_URL =
235
+ `postgresql://supatype_admin:postgres@localhost:${devDbPort}/supatype?sslmode=disable`
124
236
  }
125
- upsertEnvFile(cwd, updates)
237
+ const removeImageKeys = COMPOSE_PINNED_IMAGE_ENV_KEYS.filter((key) => !(key in imagePins))
238
+ upsertEnvFile(cwd, updates, removeImageKeys)
126
239
  }
127
240
 
128
241
  async function waitComposeHealthy(paths: SelfHostComposePaths, cwd: string, maxMs: number, composeProject: string): Promise<void> {
@@ -158,8 +271,18 @@ async function waitKongReady(kongPort: number, maxSec: number): Promise<void> {
158
271
  throw new Error(`Kong gateway at ${base} did not become ready within ${maxSec}s`)
159
272
  }
160
273
 
274
+ async function provisionDockerStorageBuckets(
275
+ ast: ExtractedSchemaAstV2,
276
+ kongPort: number,
277
+ serviceRoleKey: string,
278
+ ): Promise<void> {
279
+ await provisionBucketsFromAst(ast, `http://localhost:${kongPort}/storage/v1`, serviceRoleKey)
280
+ }
281
+
161
282
  let _lastPushedAst: string | null = null
162
283
  let _lastFailedAst: string | null = null
284
+ let _composePushInFlight = false
285
+ let _composePushQueued = false
163
286
 
164
287
  /**
165
288
  * Regenerate admin-config + TypeScript types from the AST using the **host** engine.
@@ -207,6 +330,7 @@ async function refreshSchemaArtifacts(
207
330
 
208
331
  try {
209
332
  const admin = withAdminRoles(await engineRequest<unknown>("/admin", { ast }), config)
333
+ restoreSystemRelationTargets(admin, ast)
210
334
  writeFileSync(adminConfigPath, `${JSON.stringify(admin, null, 2)}\n`)
211
335
  console.log("[supatype] Admin config written to .supatype/admin-config.json")
212
336
  } catch (err) {
@@ -245,12 +369,19 @@ async function runComposeSchemaPush(
245
369
  console.log("[supatype] Applying schema via local engine (overrides.engine)...")
246
370
  await ensureEngine()
247
371
  const pgSchema = config.schema?.pg_schema ?? "public"
372
+ const sources = writeSchemaSourcePushArtifacts(cwd)
248
373
  try {
249
374
  await engineRequest("/push", {
250
375
  ast,
251
376
  database_url: hostComposeDbUrl(cwd),
252
377
  schema: pgSchema,
253
378
  force: true,
379
+ ...(sources
380
+ ? {
381
+ schema_sources_gz_base64: sources.payload.dataBase64,
382
+ schema_sources_manifest: sources.payload.manifest,
383
+ }
384
+ : {}),
254
385
  })
255
386
  } catch (err) {
256
387
  _lastFailedAst = astJson
@@ -258,34 +389,66 @@ async function runComposeSchemaPush(
258
389
  }
259
390
  _lastPushedAst = astJson
260
391
  _lastFailedAst = null
392
+ if (astHasSystemAuthRelation(ast)) {
393
+ grantAuthSchemaAccess(paths, cwd, composeProject)
394
+ }
261
395
  console.log("[supatype] Schema applied.")
262
396
  return
263
397
  }
264
398
 
265
399
  console.log("[supatype] Applying schema via compose schema-engine...")
266
- let push = runComposeEnginePush(paths, cwd, composeProject)
400
+ const sources = writeSchemaSourcePushArtifacts(cwd)
401
+ let push = await runComposeEnginePush(paths, cwd, composeProject, config, sources)
267
402
  // Windows Docker bind mounts can lag briefly after the host write.
268
403
  if (push.status !== 0) {
269
404
  await new Promise((r) => setTimeout(r, 250))
270
- push = runComposeEnginePush(paths, cwd, composeProject)
405
+ push = await runComposeEnginePush(paths, cwd, composeProject, config, sources)
271
406
  }
272
407
  if (push.status !== 0) {
273
408
  _lastFailedAst = astJson
274
- throw new Error(push.output || `Engine schema push failed (exit ${push.status})`)
409
+ const detail = filterComposeNoise(push.output) || push.output
410
+ throw new Error(detail || `Engine schema push failed (exit ${push.status})`)
275
411
  }
276
412
  _lastPushedAst = astJson
277
413
  _lastFailedAst = null
278
414
 
279
- console.log("[supatype] Schema applied.")
415
+ if (astHasSystemAuthRelation(ast)) {
416
+ grantAuthSchemaAccess(paths, cwd, composeProject)
417
+ }
418
+ }
419
+
420
+ /** Serialize watch-triggered pushes so docker output cannot interleave. */
421
+ async function runComposeSchemaPushQueued(
422
+ cwd: string,
423
+ config: SupatypeProjectConfig,
424
+ paths: SelfHostComposePaths,
425
+ schemaPath: string,
426
+ composeProject: string,
427
+ ): Promise<void> {
428
+ if (_composePushInFlight) {
429
+ _composePushQueued = true
430
+ return
431
+ }
432
+ _composePushInFlight = true
433
+ try {
434
+ do {
435
+ _composePushQueued = false
436
+ await runComposeSchemaPush(cwd, config, paths, schemaPath, composeProject)
437
+ } while (_composePushQueued)
438
+ } finally {
439
+ _composePushInFlight = false
440
+ }
280
441
  }
281
442
 
282
- function runComposeEnginePush(
443
+ async function runComposeEnginePush(
283
444
  paths: SelfHostComposePaths,
284
445
  cwd: string,
285
446
  composeProject: string,
286
- ): { status: number; output: string } {
447
+ config: SupatypeProjectConfig,
448
+ sources?: SchemaSourcePushArtifacts | null,
449
+ ): Promise<{ status: number; output: string }> {
287
450
  const envFile = resolve(cwd, ".env")
288
- const composeArgs = ["compose"]
451
+ const composeArgs = ["compose", "--progress", "quiet"]
289
452
  if (composeProject) composeArgs.push("-p", composeProject)
290
453
  composeArgs.push("--project-directory", cwd)
291
454
  composeArgs.push("-f", paths.composePath)
@@ -306,16 +469,159 @@ function runComposeEnginePush(
306
469
  "--force",
307
470
  "--non-interactive",
308
471
  )
472
+ if (sources) {
473
+ composeArgs.push(
474
+ "--schema-sources-gz",
475
+ sources.dockerGzPath,
476
+ "--schema-sources-manifest",
477
+ sources.dockerManifestPath,
478
+ )
479
+ }
480
+ const pushEnv: NodeJS.ProcessEnv = {
481
+ ...process.env,
482
+ COMPOSE_PROGRESS: "quiet",
483
+ }
484
+ const engineImage = await schemaEngineImageForPush(config)
485
+ if (engineImage) {
486
+ pushEnv.SUPATYPE_ENGINE_IMAGE = engineImage
487
+ }
309
488
  const result = spawnSync("docker", composeArgs, {
310
489
  cwd,
311
490
  encoding: "utf8",
312
491
  maxBuffer: 10 * 1024 * 1024,
492
+ env: pushEnv,
313
493
  })
314
494
  const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim()
315
- if (output.length > 0) {
316
- console.error(output)
495
+ const exitStatus = result.status ?? 1
496
+ const pushResult = parseEnginePushOutput(output)
497
+
498
+ if (exitStatus === 0) {
499
+ if (pushResult) {
500
+ console.log(`[supatype] ${formatEnginePushMessage(pushResult)}`)
501
+ } else {
502
+ console.log("[supatype] Schema applied.")
503
+ }
317
504
  }
318
- return { status: result.status ?? 1, output }
505
+
506
+ return { status: exitStatus, output }
507
+ }
508
+
509
+ async function runComposeEngineDiff(
510
+ paths: SelfHostComposePaths,
511
+ cwd: string,
512
+ composeProject: string,
513
+ config: SupatypeProjectConfig,
514
+ pgSchema: string,
515
+ ): Promise<{ status: number; output: string; diff: DiffResult | null }> {
516
+ const envFile = resolve(cwd, ".env")
517
+ const composeArgs = ["compose", "--progress", "quiet"]
518
+ if (composeProject) composeArgs.push("-p", composeProject)
519
+ composeArgs.push("--project-directory", cwd)
520
+ composeArgs.push("-f", paths.composePath)
521
+ if (existsSync(envFile)) {
522
+ composeArgs.push("--env-file", envFile)
523
+ }
524
+ composeArgs.push(
525
+ "--profile",
526
+ "tools",
527
+ "run",
528
+ "--rm",
529
+ "schema-engine",
530
+ "diff",
531
+ "-i",
532
+ "/project/.supatype/schema.ast.json",
533
+ "--database-url",
534
+ composeDbUrl(),
535
+ "--schema",
536
+ pgSchema,
537
+ )
538
+ const diffEnv: NodeJS.ProcessEnv = {
539
+ ...process.env,
540
+ COMPOSE_PROGRESS: "quiet",
541
+ }
542
+ const engineImage = await schemaEngineImageForPush(config)
543
+ if (engineImage) {
544
+ diffEnv.SUPATYPE_ENGINE_IMAGE = engineImage
545
+ }
546
+ const result = spawnSync("docker", composeArgs, {
547
+ cwd,
548
+ encoding: "utf8",
549
+ maxBuffer: 10 * 1024 * 1024,
550
+ env: diffEnv,
551
+ })
552
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim()
553
+ const exitStatus = result.status ?? 1
554
+ const diff = parseEngineJsonOutput<DiffResult>(output)
555
+
556
+ return { status: exitStatus, output, diff }
557
+ }
558
+
559
+ /**
560
+ * `supatype diff` when `provider: docker`. Uses in-compose schema-engine unless
561
+ * `overrides.engine` is set — then Postgres is published to the host and diff runs
562
+ * through the local engine binary.
563
+ */
564
+ export async function diffSchemaDocker(cwd: string, config: SupatypeProjectConfig): Promise<DiffResult> {
565
+ if (resolveRuntimeProvider(config) !== "docker") {
566
+ throw new Error("diffSchemaDocker requires provider: docker")
567
+ }
568
+ const project = composeProjectName(config.project.name)
569
+ const pgSchema = config.schema?.pg_schema ?? "public"
570
+
571
+ if (hasEngineOverride(config)) {
572
+ const brand = { intro: "Schema diff" }
573
+ await ensureDockerDbPublishedForHostEngine(cwd, config, brand)
574
+ const schemaPath = schemaPathFromProject(config, cwd)
575
+ const ast = loadSchemaAst(schemaPath, cwd)
576
+ await ensureEngine()
577
+ return engineRequest<DiffResult>("/diff", {
578
+ ast,
579
+ database_url: hostComposeDbUrl(cwd),
580
+ schema: pgSchema,
581
+ })
582
+ }
583
+
584
+ const kongPort = await resolveKongPort(cwd)
585
+ const now = Math.floor(Date.now() / 1000)
586
+ const jwtBase = { iss: "supatype", iat: now, exp: now + 315_360_000 }
587
+ const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
588
+ const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
589
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, undefined)
590
+
591
+ const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
592
+ const diffBrand = { intro: "Schema diff" }
593
+
594
+ const up = runDockerCompose(paths.composePath, ["up", "-d", "db"], cwd, project, {
595
+ quiet: true,
596
+ brand: diffBrand,
597
+ })
598
+ if (up !== 0) {
599
+ exitComposeFailed(up, "Could not start Postgres (compose db service).", diffBrand)
600
+ }
601
+ await waitComposeHealthy(paths, cwd, 120_000, project)
602
+
603
+ const schemaPath = schemaPathFromProject(config, cwd)
604
+ const ast = loadSchemaAst(schemaPath, cwd)
605
+
606
+ const supatypeDir = join(cwd, ".supatype")
607
+ mkdirSync(supatypeDir, { recursive: true })
608
+ const astPath = join(supatypeDir, "schema.ast.json")
609
+ writeFileSync(astPath, JSON.stringify(ast))
610
+
611
+ let result = await runComposeEngineDiff(paths, cwd, project, config, pgSchema)
612
+ // Windows Docker bind mounts can lag briefly after the host write.
613
+ if (result.status !== 0) {
614
+ await new Promise((r) => setTimeout(r, 250))
615
+ result = await runComposeEngineDiff(paths, cwd, project, config, pgSchema)
616
+ }
617
+ if (result.status !== 0) {
618
+ const detail = filterComposeNoise(result.output) || result.output
619
+ throw new Error(detail || `Engine schema diff failed (exit ${result.status})`)
620
+ }
621
+ if (!result.diff) {
622
+ throw new Error("Engine diff returned no result")
623
+ }
624
+ return result.diff
319
625
  }
320
626
 
321
627
  /**
@@ -335,17 +641,35 @@ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfi
335
641
  const jwtBase = { iss: "supatype", iat: now, exp: now + 315_360_000 }
336
642
  const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
337
643
  const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
338
- ensureDevComposeEnv(cwd, anonKey, serviceRoleKey, kongPort, devDbPort)
644
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
339
645
 
340
646
  const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
647
+ const pushBrand = { intro: "Push schema" }
341
648
 
342
649
  console.log(`[supatype] provider: docker — applying schema via compose (project ${project})...`)
343
- const up = runDockerCompose(paths.composePath, ["up", "-d", "db"], cwd, project)
344
- if (up !== 0) process.exit(up)
650
+ const up = runDockerCompose(paths.composePath, ["up", "-d", "db"], cwd, project, {
651
+ quiet: true,
652
+ brand: pushBrand,
653
+ })
654
+ if (up !== 0) {
655
+ exitComposeFailed(up, "Could not start Postgres (compose db service).", pushBrand)
656
+ }
345
657
  await waitComposeHealthy(paths, cwd, 120_000, project)
346
658
 
347
659
  const schemaPath = schemaPathFromProject(config, cwd)
660
+ const ast = loadSchemaAst(schemaPath, cwd)
348
661
  await runComposeSchemaPush(cwd, config, paths, schemaPath, project)
662
+
663
+ const upGateway = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project, {
664
+ quiet: true,
665
+ brand: pushBrand,
666
+ })
667
+ if (upGateway !== 0) {
668
+ exitComposeFailed(upGateway, "Could not start the Compose gateway stack.", pushBrand)
669
+ }
670
+ await waitKongReady(kongPort, 120)
671
+ await provisionDockerStorageBuckets(ast, kongPort, serviceRoleKey)
672
+
349
673
  console.log("[supatype] Schema pushed.")
350
674
  }
351
675
 
@@ -365,13 +689,20 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
365
689
  const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
366
690
  const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
367
691
 
368
- ensureDevComposeEnv(cwd, anonKey, serviceRoleKey, kongPort, devDbPort)
692
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
369
693
 
370
694
  console.log(`[supatype] provider: docker — starting self-host Compose stack (project ${project}, gateway :${kongPort})...`)
371
695
  const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
696
+ const devBrand = { intro: "Local development" }
372
697
 
373
- const upStatus = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project)
374
- if (upStatus !== 0) process.exit(upStatus)
698
+ const upStatus = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project, {
699
+ quiet: true,
700
+ brand: devBrand,
701
+ })
702
+ if (upStatus !== 0) {
703
+ endDevSession()
704
+ exitComposeFailed(upStatus, "Could not start the local Compose stack.", devBrand)
705
+ }
375
706
 
376
707
  console.log("[supatype] Waiting for Postgres (compose)...")
377
708
  await waitComposeHealthy(paths, cwd, 180_000, project)
@@ -384,6 +715,18 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
384
715
  console.log("[supatype] Waiting for API gateway...")
385
716
  await waitKongReady(kongPort, 120)
386
717
 
718
+ writeLocalEnvironment(cwd, {
719
+ target: "local",
720
+ apiUrl: `http://localhost:${kongPort}`,
721
+ databaseUrl: hasEngineOverride(config) ? hostComposeDbUrl(cwd) : composeDbUrl(),
722
+ projectRef: config.project.name,
723
+ kongPort,
724
+ provider: "docker",
725
+ })
726
+
727
+ const ast = loadSchemaAst(schemaPath, cwd)
728
+ await provisionDockerStorageBuckets(ast, kongPort, serviceRoleKey)
729
+
387
730
  const pidDir = join(homedir(), ".supatype", "projects", config.project.name, "pid")
388
731
  mkdirSync(pidDir, { recursive: true })
389
732
 
@@ -423,33 +766,82 @@ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig,
423
766
 
424
767
  const appProc = startProxyDevApp(cwd, config, pidDir)
425
768
 
426
- const cleanup = async () => {
427
- console.log("\n[supatype] Shutting down compose...")
769
+ let schemaWatcher: import("node:fs").FSWatcher | null = null
770
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null
771
+
772
+ registerDevShutdown(async () => {
773
+ schemaWatcher?.close()
774
+ schemaWatcher = null
775
+ if (debounceTimer) {
776
+ clearTimeout(debounceTimer)
777
+ debounceTimer = null
778
+ }
779
+ console.log("[supatype] Shutting down compose...")
428
780
  await studioProc?.stop()
429
781
  await appProc?.stop()
430
- runDockerCompose(paths.composePath, ["down"], cwd, project)
431
- process.exit(0)
432
- }
433
- process.once("SIGINT", () => void cleanup())
434
- process.once("SIGTERM", () => void cleanup())
782
+ const downStatus = runDockerCompose(paths.composePath, ["down"], cwd, project, { quiet: true })
783
+ if (downStatus === 0) {
784
+ console.log("[supatype] Compose stack stopped.")
785
+ } else {
786
+ console.warn(`[supatype] Compose down exited with status ${downStatus}.`)
787
+ }
788
+ })
435
789
 
436
790
  if (opts.watch) {
437
791
  const schemaDir = join(projectRootFromConfig(config, cwd), config.schema?.path ?? "schema/index.ts", "..")
438
792
  console.log(`[supatype] Watching ${schemaDir} for changes...`)
439
793
  const { watch } = await import("node:fs")
440
- let debounceTimer: ReturnType<typeof setTimeout> | null = null
441
- watch(schemaDir, { recursive: true }, (_eventType, filename) => {
794
+ schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
442
795
  if (!filename?.endsWith(".ts")) return
443
796
  if (debounceTimer) clearTimeout(debounceTimer)
444
797
  debounceTimer = setTimeout(() => {
445
798
  debounceTimer = null
446
799
  console.log(`\n[supatype] Change detected in ${filename}, pushing schema...`)
447
- runComposeSchemaPush(cwd, config, paths, schemaPath, project).catch((e: unknown) =>
448
- console.error("[supatype] Schema push failed:", (e as Error).message),
449
- )
800
+ runComposeSchemaPushQueued(cwd, config, paths, schemaPath, project)
801
+ .then(async () => {
802
+ const updatedAst = loadSchemaAst(schemaPath, cwd)
803
+ await provisionDockerStorageBuckets(updatedAst, kongPort, serviceRoleKey)
804
+ })
805
+ .catch((e: unknown) =>
806
+ console.error("[supatype] Schema push failed:", (e as Error).message),
807
+ )
450
808
  }, 300)
451
809
  })
452
810
  }
453
811
 
454
812
  await new Promise<never>(() => undefined)
455
813
  }
814
+
815
+ function astHasSystemAuthRelation(ast: unknown): boolean {
816
+ const obj = ast as { models?: Array<{ fields?: Record<string, { kind?: string; target?: string }> }> }
817
+ if (!obj?.models) return false
818
+ for (const model of obj.models) {
819
+ if (!model.fields) continue
820
+ for (const field of Object.values(model.fields)) {
821
+ if (field.kind === "relation" && field.target === "supatype:user") return true
822
+ }
823
+ }
824
+ return false
825
+ }
826
+
827
+ function grantAuthSchemaAccess(
828
+ paths: SelfHostComposePaths,
829
+ cwd: string,
830
+ composeProject: string,
831
+ ): void {
832
+ const composeDir = dirname(paths.composePath)
833
+ const baseArgs = [
834
+ "compose", "-p", composeProject,
835
+ "-f", paths.composePath,
836
+ ]
837
+ const sql = "GRANT USAGE ON SCHEMA auth TO service_role; GRANT SELECT ON auth.users TO service_role;"
838
+ const result = spawnSync(
839
+ "docker",
840
+ [...baseArgs, "exec", "-T", "-e", "PGPASSWORD=postgres", "db",
841
+ "psql", "-U", "supatype_admin", "-d", "supatype", "-c", sql],
842
+ { cwd: composeDir, encoding: "utf8", timeout: 10_000 },
843
+ )
844
+ if (result.status !== 0) {
845
+ console.warn("[supatype] Could not grant service_role access to auth.users — Studio relation preview may fail.")
846
+ }
847
+ }