@supatype/cli 0.1.0-alpha.10

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 (416) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +221 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -0
  6. package/bin/supatype.js +5 -0
  7. package/dist/app/framework.d.ts +44 -0
  8. package/dist/app/framework.d.ts.map +1 -0
  9. package/dist/app/framework.js +200 -0
  10. package/dist/app/framework.js.map +1 -0
  11. package/dist/app/proxy-dev-app.d.ts +13 -0
  12. package/dist/app/proxy-dev-app.d.ts.map +1 -0
  13. package/dist/app/proxy-dev-app.js +54 -0
  14. package/dist/app/proxy-dev-app.js.map +1 -0
  15. package/dist/app-config.d.ts +7 -0
  16. package/dist/app-config.d.ts.map +1 -0
  17. package/dist/app-config.js +113 -0
  18. package/dist/app-config.js.map +1 -0
  19. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  20. package/dist/augmentation-generator.d.ts +2 -0
  21. package/dist/augmentation-generator.d.ts.map +1 -0
  22. package/dist/augmentation-generator.js +111 -0
  23. package/dist/augmentation-generator.js.map +1 -0
  24. package/dist/binary-cache.d.ts +98 -0
  25. package/dist/binary-cache.d.ts.map +1 -0
  26. package/dist/binary-cache.js +687 -0
  27. package/dist/binary-cache.js.map +1 -0
  28. package/dist/cli.d.ts +2 -0
  29. package/dist/cli.d.ts.map +1 -0
  30. package/dist/cli.js +61 -0
  31. package/dist/cli.js.map +1 -0
  32. package/dist/commands/admin.d.ts +4 -0
  33. package/dist/commands/admin.d.ts.map +1 -0
  34. package/dist/commands/admin.js +271 -0
  35. package/dist/commands/admin.js.map +1 -0
  36. package/dist/commands/app.d.ts +3 -0
  37. package/dist/commands/app.d.ts.map +1 -0
  38. package/dist/commands/app.js +82 -0
  39. package/dist/commands/app.js.map +1 -0
  40. package/dist/commands/cache.d.ts +6 -0
  41. package/dist/commands/cache.d.ts.map +1 -0
  42. package/dist/commands/cache.js +105 -0
  43. package/dist/commands/cache.js.map +1 -0
  44. package/dist/commands/cloud.d.ts +23 -0
  45. package/dist/commands/cloud.d.ts.map +1 -0
  46. package/dist/commands/cloud.js +254 -0
  47. package/dist/commands/cloud.js.map +1 -0
  48. package/dist/commands/db.d.ts +8 -0
  49. package/dist/commands/db.d.ts.map +1 -0
  50. package/dist/commands/db.js +116 -0
  51. package/dist/commands/db.js.map +1 -0
  52. package/dist/commands/deploy-types.d.ts +14 -0
  53. package/dist/commands/deploy-types.d.ts.map +1 -0
  54. package/dist/commands/deploy-types.js +38 -0
  55. package/dist/commands/deploy-types.js.map +1 -0
  56. package/dist/commands/deploy.d.ts +15 -0
  57. package/dist/commands/deploy.d.ts.map +1 -0
  58. package/dist/commands/deploy.js +322 -0
  59. package/dist/commands/deploy.js.map +1 -0
  60. package/dist/commands/dev.d.ts +14 -0
  61. package/dist/commands/dev.d.ts.map +1 -0
  62. package/dist/commands/dev.js +806 -0
  63. package/dist/commands/dev.js.map +1 -0
  64. package/dist/commands/diff.d.ts +3 -0
  65. package/dist/commands/diff.d.ts.map +1 -0
  66. package/dist/commands/diff.js +54 -0
  67. package/dist/commands/diff.js.map +1 -0
  68. package/dist/commands/engine.d.ts +7 -0
  69. package/dist/commands/engine.d.ts.map +1 -0
  70. package/dist/commands/engine.js +27 -0
  71. package/dist/commands/engine.js.map +1 -0
  72. package/dist/commands/functions.d.ts +3 -0
  73. package/dist/commands/functions.d.ts.map +1 -0
  74. package/dist/commands/functions.js +749 -0
  75. package/dist/commands/functions.js.map +1 -0
  76. package/dist/commands/generate.d.ts +3 -0
  77. package/dist/commands/generate.d.ts.map +1 -0
  78. package/dist/commands/generate.js +38 -0
  79. package/dist/commands/generate.js.map +1 -0
  80. package/dist/commands/init.d.ts +7 -0
  81. package/dist/commands/init.d.ts.map +1 -0
  82. package/dist/commands/init.js +228 -0
  83. package/dist/commands/init.js.map +1 -0
  84. package/dist/commands/keys.d.ts +4 -0
  85. package/dist/commands/keys.d.ts.map +1 -0
  86. package/dist/commands/keys.js +57 -0
  87. package/dist/commands/keys.js.map +1 -0
  88. package/dist/commands/logs.d.ts +6 -0
  89. package/dist/commands/logs.d.ts.map +1 -0
  90. package/dist/commands/logs.js +52 -0
  91. package/dist/commands/logs.js.map +1 -0
  92. package/dist/commands/migrate-from-v1.d.ts +5 -0
  93. package/dist/commands/migrate-from-v1.d.ts.map +1 -0
  94. package/dist/commands/migrate-from-v1.js +125 -0
  95. package/dist/commands/migrate-from-v1.js.map +1 -0
  96. package/dist/commands/migrate.d.ts +3 -0
  97. package/dist/commands/migrate.d.ts.map +1 -0
  98. package/dist/commands/migrate.js +75 -0
  99. package/dist/commands/migrate.js.map +1 -0
  100. package/dist/commands/pg.d.ts +8 -0
  101. package/dist/commands/pg.d.ts.map +1 -0
  102. package/dist/commands/pg.js +102 -0
  103. package/dist/commands/pg.js.map +1 -0
  104. package/dist/commands/plugins.d.ts +3 -0
  105. package/dist/commands/plugins.d.ts.map +1 -0
  106. package/dist/commands/plugins.js +431 -0
  107. package/dist/commands/plugins.js.map +1 -0
  108. package/dist/commands/pull.d.ts +3 -0
  109. package/dist/commands/pull.d.ts.map +1 -0
  110. package/dist/commands/pull.js +12 -0
  111. package/dist/commands/pull.js.map +1 -0
  112. package/dist/commands/push.d.ts +3 -0
  113. package/dist/commands/push.d.ts.map +1 -0
  114. package/dist/commands/push.js +179 -0
  115. package/dist/commands/push.js.map +1 -0
  116. package/dist/commands/seed.d.ts +5 -0
  117. package/dist/commands/seed.d.ts.map +1 -0
  118. package/dist/commands/seed.js +55 -0
  119. package/dist/commands/seed.js.map +1 -0
  120. package/dist/commands/self-host.d.ts +9 -0
  121. package/dist/commands/self-host.d.ts.map +1 -0
  122. package/dist/commands/self-host.js +310 -0
  123. package/dist/commands/self-host.js.map +1 -0
  124. package/dist/commands/self-update.d.ts +9 -0
  125. package/dist/commands/self-update.d.ts.map +1 -0
  126. package/dist/commands/self-update.js +33 -0
  127. package/dist/commands/self-update.js.map +1 -0
  128. package/dist/commands/status.d.ts +6 -0
  129. package/dist/commands/status.d.ts.map +1 -0
  130. package/dist/commands/status.js +70 -0
  131. package/dist/commands/status.js.map +1 -0
  132. package/dist/commands/types.d.ts +3 -0
  133. package/dist/commands/types.d.ts.map +1 -0
  134. package/dist/commands/types.js +62 -0
  135. package/dist/commands/types.js.map +1 -0
  136. package/dist/commands/update.d.ts +7 -0
  137. package/dist/commands/update.d.ts.map +1 -0
  138. package/dist/commands/update.js +118 -0
  139. package/dist/commands/update.js.map +1 -0
  140. package/dist/components.d.ts +5 -0
  141. package/dist/components.d.ts.map +1 -0
  142. package/dist/components.js +3 -0
  143. package/dist/components.js.map +1 -0
  144. package/dist/config.d.ts +65 -0
  145. package/dist/config.d.ts.map +1 -0
  146. package/dist/config.js +134 -0
  147. package/dist/config.js.map +1 -0
  148. package/dist/dev-compose.d.ts +19 -0
  149. package/dist/dev-compose.d.ts.map +1 -0
  150. package/dist/dev-compose.js +468 -0
  151. package/dist/dev-compose.js.map +1 -0
  152. package/dist/dev-log-bus.d.ts +30 -0
  153. package/dist/dev-log-bus.d.ts.map +1 -0
  154. package/dist/dev-log-bus.js +87 -0
  155. package/dist/dev-log-bus.js.map +1 -0
  156. package/dist/dev-log-filter.d.ts +10 -0
  157. package/dist/dev-log-filter.d.ts.map +1 -0
  158. package/dist/dev-log-filter.js +36 -0
  159. package/dist/dev-log-filter.js.map +1 -0
  160. package/dist/dev-logo.d.ts +12 -0
  161. package/dist/dev-logo.d.ts.map +1 -0
  162. package/dist/dev-logo.js +57 -0
  163. package/dist/dev-logo.js.map +1 -0
  164. package/dist/dev-session.d.ts +26 -0
  165. package/dist/dev-session.d.ts.map +1 -0
  166. package/dist/dev-session.js +106 -0
  167. package/dist/dev-session.js.map +1 -0
  168. package/dist/dev-shutdown.d.ts +9 -0
  169. package/dist/dev-shutdown.d.ts.map +1 -0
  170. package/dist/dev-shutdown.js +50 -0
  171. package/dist/dev-shutdown.js.map +1 -0
  172. package/dist/dev-task-colors.d.ts +14 -0
  173. package/dist/dev-task-colors.d.ts.map +1 -0
  174. package/dist/dev-task-colors.js +44 -0
  175. package/dist/dev-task-colors.js.map +1 -0
  176. package/dist/dev-tui.d.ts +24 -0
  177. package/dist/dev-tui.d.ts.map +1 -0
  178. package/dist/dev-tui.js +188 -0
  179. package/dist/dev-tui.js.map +1 -0
  180. package/dist/diff-output.d.ts +4 -0
  181. package/dist/diff-output.d.ts.map +1 -0
  182. package/dist/diff-output.js +12 -0
  183. package/dist/diff-output.js.map +1 -0
  184. package/dist/docker-postgres.d.ts +57 -0
  185. package/dist/docker-postgres.d.ts.map +1 -0
  186. package/dist/docker-postgres.js +208 -0
  187. package/dist/docker-postgres.js.map +1 -0
  188. package/dist/engine-client.d.ts +69 -0
  189. package/dist/engine-client.d.ts.map +1 -0
  190. package/dist/engine-client.js +157 -0
  191. package/dist/engine-client.js.map +1 -0
  192. package/dist/engine-push-output.d.ts +16 -0
  193. package/dist/engine-push-output.d.ts.map +1 -0
  194. package/dist/engine-push-output.js +61 -0
  195. package/dist/engine-push-output.js.map +1 -0
  196. package/dist/ensure-binary.d.ts +7 -0
  197. package/dist/ensure-binary.d.ts.map +1 -0
  198. package/dist/ensure-binary.js +17 -0
  199. package/dist/ensure-binary.js.map +1 -0
  200. package/dist/functions-router-gen.d.ts +14 -0
  201. package/dist/functions-router-gen.d.ts.map +1 -0
  202. package/dist/functions-router-gen.js +199 -0
  203. package/dist/functions-router-gen.js.map +1 -0
  204. package/dist/index.d.ts +11 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +9 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/jwt.d.ts +3 -0
  209. package/dist/jwt.d.ts.map +1 -0
  210. package/dist/jwt.js +13 -0
  211. package/dist/jwt.js.map +1 -0
  212. package/dist/kong-config.d.ts +25 -0
  213. package/dist/kong-config.d.ts.map +1 -0
  214. package/dist/kong-config.js +71 -0
  215. package/dist/kong-config.js.map +1 -0
  216. package/dist/local-gateway.d.ts +7 -0
  217. package/dist/local-gateway.d.ts.map +1 -0
  218. package/dist/local-gateway.js +9 -0
  219. package/dist/local-gateway.js.map +1 -0
  220. package/dist/local-storage.d.ts +8 -0
  221. package/dist/local-storage.d.ts.map +1 -0
  222. package/dist/local-storage.js +14 -0
  223. package/dist/local-storage.js.map +1 -0
  224. package/dist/pgbouncer-userlist.d.ts +5 -0
  225. package/dist/pgbouncer-userlist.d.ts.map +1 -0
  226. package/dist/pgbouncer-userlist.js +14 -0
  227. package/dist/pgbouncer-userlist.js.map +1 -0
  228. package/dist/postgres-ctl.d.ts +44 -0
  229. package/dist/postgres-ctl.d.ts.map +1 -0
  230. package/dist/postgres-ctl.js +137 -0
  231. package/dist/postgres-ctl.js.map +1 -0
  232. package/dist/process-manager.d.ts +49 -0
  233. package/dist/process-manager.d.ts.map +1 -0
  234. package/dist/process-manager.js +177 -0
  235. package/dist/process-manager.js.map +1 -0
  236. package/dist/project-config.d.ts +238 -0
  237. package/dist/project-config.d.ts.map +1 -0
  238. package/dist/project-config.js +159 -0
  239. package/dist/project-config.js.map +1 -0
  240. package/dist/pull-utils.d.ts +31 -0
  241. package/dist/pull-utils.d.ts.map +1 -0
  242. package/dist/pull-utils.js +77 -0
  243. package/dist/pull-utils.js.map +1 -0
  244. package/dist/release-pins.d.ts +7 -0
  245. package/dist/release-pins.d.ts.map +1 -0
  246. package/dist/release-pins.js +27 -0
  247. package/dist/release-pins.js.map +1 -0
  248. package/dist/release-public-key.d.ts +8 -0
  249. package/dist/release-public-key.d.ts.map +1 -0
  250. package/dist/release-public-key.js +13 -0
  251. package/dist/release-public-key.js.map +1 -0
  252. package/dist/restore-system-relation-targets.d.ts +3 -0
  253. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  254. package/dist/restore-system-relation-targets.js +45 -0
  255. package/dist/restore-system-relation-targets.js.map +1 -0
  256. package/dist/runtime-routes.d.ts +34 -0
  257. package/dist/runtime-routes.d.ts.map +1 -0
  258. package/dist/runtime-routes.js +252 -0
  259. package/dist/runtime-routes.js.map +1 -0
  260. package/dist/schema-ast-v2.d.ts +127 -0
  261. package/dist/schema-ast-v2.d.ts.map +1 -0
  262. package/dist/schema-ast-v2.js +226 -0
  263. package/dist/schema-ast-v2.js.map +1 -0
  264. package/dist/scripts/postinstall.d.ts +11 -0
  265. package/dist/scripts/postinstall.d.ts.map +1 -0
  266. package/dist/scripts/postinstall.js +47 -0
  267. package/dist/scripts/postinstall.js.map +1 -0
  268. package/dist/seed.d.ts +8 -0
  269. package/dist/seed.d.ts.map +1 -0
  270. package/dist/seed.js +32 -0
  271. package/dist/seed.js.map +1 -0
  272. package/dist/self-host-compose.d.ts +43 -0
  273. package/dist/self-host-compose.d.ts.map +1 -0
  274. package/dist/self-host-compose.js +400 -0
  275. package/dist/self-host-compose.js.map +1 -0
  276. package/dist/storage-provision.d.ts +24 -0
  277. package/dist/storage-provision.d.ts.map +1 -0
  278. package/dist/storage-provision.js +44 -0
  279. package/dist/storage-provision.js.map +1 -0
  280. package/dist/studio-admin-roles.d.ts +7 -0
  281. package/dist/studio-admin-roles.d.ts.map +1 -0
  282. package/dist/studio-admin-roles.js +14 -0
  283. package/dist/studio-admin-roles.js.map +1 -0
  284. package/dist/studio-dev-server.d.ts +22 -0
  285. package/dist/studio-dev-server.d.ts.map +1 -0
  286. package/dist/studio-dev-server.js +28 -0
  287. package/dist/studio-dev-server.js.map +1 -0
  288. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  289. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  290. package/dist/supatype-eval-1781522769253.mjs +3 -0
  291. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  292. package/dist/systemd.d.ts +26 -0
  293. package/dist/systemd.d.ts.map +1 -0
  294. package/dist/systemd.js +102 -0
  295. package/dist/systemd.js.map +1 -0
  296. package/dist/tsx-runner.d.ts +18 -0
  297. package/dist/tsx-runner.d.ts.map +1 -0
  298. package/dist/tsx-runner.js +69 -0
  299. package/dist/tsx-runner.js.map +1 -0
  300. package/dist/type-extractor.d.ts +4 -0
  301. package/dist/type-extractor.d.ts.map +1 -0
  302. package/dist/type-extractor.js +1213 -0
  303. package/dist/type-extractor.js.map +1 -0
  304. package/dist/type-resolver.d.ts +33 -0
  305. package/dist/type-resolver.d.ts.map +1 -0
  306. package/dist/type-resolver.js +338 -0
  307. package/dist/type-resolver.js.map +1 -0
  308. package/package.json +41 -0
  309. package/releases/deno/VERSION +1 -0
  310. package/scripts/mirror-deno-release.sh +76 -0
  311. package/src/TYPE-RESOLUTION.md +294 -0
  312. package/src/app/framework.ts +249 -0
  313. package/src/app/proxy-dev-app.ts +68 -0
  314. package/src/app-config.ts +128 -0
  315. package/src/augmentation-generator.ts +126 -0
  316. package/src/binary-cache.ts +845 -0
  317. package/src/cli.ts +63 -0
  318. package/src/commands/admin.ts +372 -0
  319. package/src/commands/app.ts +97 -0
  320. package/src/commands/cache.ts +117 -0
  321. package/src/commands/cloud.ts +325 -0
  322. package/src/commands/db.ts +136 -0
  323. package/src/commands/deploy-types.ts +49 -0
  324. package/src/commands/deploy.ts +400 -0
  325. package/src/commands/dev.ts +1009 -0
  326. package/src/commands/diff.ts +63 -0
  327. package/src/commands/engine.ts +30 -0
  328. package/src/commands/functions.ts +901 -0
  329. package/src/commands/generate.ts +44 -0
  330. package/src/commands/init.ts +253 -0
  331. package/src/commands/keys.ts +66 -0
  332. package/src/commands/logs.ts +58 -0
  333. package/src/commands/migrate-from-v1.ts +131 -0
  334. package/src/commands/migrate.ts +87 -0
  335. package/src/commands/pg.ts +133 -0
  336. package/src/commands/plugins.ts +508 -0
  337. package/src/commands/pull.ts +17 -0
  338. package/src/commands/push.ts +226 -0
  339. package/src/commands/seed.ts +68 -0
  340. package/src/commands/self-host.ts +364 -0
  341. package/src/commands/self-update.ts +45 -0
  342. package/src/commands/status.ts +84 -0
  343. package/src/commands/types.ts +76 -0
  344. package/src/commands/update.ts +136 -0
  345. package/src/components.ts +6 -0
  346. package/src/config.ts +223 -0
  347. package/src/dev-compose.ts +583 -0
  348. package/src/dev-log-bus.ts +101 -0
  349. package/src/dev-log-filter.ts +32 -0
  350. package/src/dev-logo.ts +62 -0
  351. package/src/dev-session.ts +130 -0
  352. package/src/dev-shutdown.ts +54 -0
  353. package/src/dev-task-colors.ts +47 -0
  354. package/src/dev-tui.ts +232 -0
  355. package/src/diff-output.ts +12 -0
  356. package/src/docker-postgres.ts +295 -0
  357. package/src/engine-client.ts +236 -0
  358. package/src/engine-push-output.ts +71 -0
  359. package/src/ensure-binary.ts +28 -0
  360. package/src/functions-router-gen.ts +224 -0
  361. package/src/index.ts +11 -0
  362. package/src/jwt.ts +14 -0
  363. package/src/kong-config.ts +93 -0
  364. package/src/local-gateway.ts +9 -0
  365. package/src/local-storage.ts +14 -0
  366. package/src/pgbouncer-userlist.ts +15 -0
  367. package/src/postgres-ctl.ts +171 -0
  368. package/src/process-manager.ts +220 -0
  369. package/src/project-config.ts +388 -0
  370. package/src/pull-utils.ts +81 -0
  371. package/src/release-pins.ts +31 -0
  372. package/src/release-public-key.ts +12 -0
  373. package/src/restore-system-relation-targets.ts +45 -0
  374. package/src/runtime-routes.ts +291 -0
  375. package/src/schema-ast-v2.ts +324 -0
  376. package/src/scripts/postinstall.ts +51 -0
  377. package/src/seed.ts +43 -0
  378. package/src/self-host-compose.ts +452 -0
  379. package/src/storage-provision.ts +58 -0
  380. package/src/studio-admin-roles.ts +16 -0
  381. package/src/studio-dev-server.ts +53 -0
  382. package/src/supatype-eval-1781522769253.mts +1 -0
  383. package/src/systemd.ts +137 -0
  384. package/src/tsx-runner.ts +89 -0
  385. package/src/type-extractor.ts +1479 -0
  386. package/src/type-resolver.ts +457 -0
  387. package/tests/app-command.test.ts +54 -0
  388. package/tests/augmentation-generator.test.ts +59 -0
  389. package/tests/binary-cache-cloud-overrides.test.ts +123 -0
  390. package/tests/cached-artifact-format.test.ts +84 -0
  391. package/tests/cli-help.test.ts +133 -0
  392. package/tests/config.test.ts +252 -0
  393. package/tests/dev-ui.test.ts +139 -0
  394. package/tests/docker-postgres.test.ts +39 -0
  395. package/tests/engine-distribution.test.ts +418 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/ensure-binary.test.ts +59 -0
  398. package/tests/init.test.ts +127 -0
  399. package/tests/keys.test.ts +160 -0
  400. package/tests/migrate-from-v1.test.ts +29 -0
  401. package/tests/normalize-admin-config.test.ts +48 -0
  402. package/tests/pg-spawn-env.test.ts +18 -0
  403. package/tests/postgres-archive-tag.test.ts +9 -0
  404. package/tests/proxy-dev-app.test.ts +33 -0
  405. package/tests/pull-utils.test.ts +150 -0
  406. package/tests/release-pins.test.ts +28 -0
  407. package/tests/runtime-contract.test.ts +370 -0
  408. package/tests/seed-discover.test.ts +31 -0
  409. package/tests/studio-admin-roles.test.ts +27 -0
  410. package/tests/tsconfig.json +9 -0
  411. package/tests/tsx-runner.test.ts +66 -0
  412. package/tests/type-extractor.test.ts +985 -0
  413. package/tests/type-resolver.test.ts +59 -0
  414. package/tsconfig.json +10 -0
  415. package/tsconfig.tsbuildinfo +1 -0
  416. package/vitest.config.ts +12 -0
@@ -0,0 +1,583 @@
1
+ /**
2
+ * `supatype dev` when `provider: docker` — full self-host Compose stack (Kong gateway).
3
+ */
4
+
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
6
+ import { homedir } from "node:os"
7
+ import { dirname, join, resolve } from "node:path"
8
+ import { spawnSync } from "node:child_process"
9
+ import { startProxyDevApp } from "./app/proxy-dev-app.js"
10
+ import { loadSchemaAst } from "./config.js"
11
+ import {
12
+ COMPOSE_DEV_KONG_PORT,
13
+ projectRootFromConfig,
14
+ resolveRuntimeProvider,
15
+ schemaPathFromProject,
16
+ type SupatypeProjectConfig,
17
+ } from "./project-config.js"
18
+ import { signJwt } from "./jwt.js"
19
+ import { isPortInUse } from "./postgres-ctl.js"
20
+ import {
21
+ COMPOSE_PINNED_IMAGE_ENV_KEYS,
22
+ composeDockerImageEnv,
23
+ composeProjectName,
24
+ runDockerCompose,
25
+ schemaEngineImageForPush,
26
+ writeSelfHostCompose,
27
+ type SelfHostComposePaths,
28
+ } from "./self-host-compose.js"
29
+ import { hasEngineOverride } from "./binary-cache.js"
30
+ import { startStudioViteDevServer } from "./studio-dev-server.js"
31
+ import { ensureEngine, engineRequest } from "./engine-client.js"
32
+ import { endDevSession } from "./dev-session.js"
33
+ import { registerDevShutdown } from "./dev-shutdown.js"
34
+ import {
35
+ filterComposeNoise,
36
+ formatEnginePushMessage,
37
+ parseEnginePushOutput,
38
+ } from "./engine-push-output.js"
39
+ import { withAdminRoles } from "./studio-admin-roles.js"
40
+ import { restoreSystemRelationTargets } from "./restore-system-relation-targets.js"
41
+
42
+ const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
43
+
44
+ /** Default host port for compose Postgres when `overrides.engine` is set (devLocal). */
45
+ const COMPOSE_DEV_DB_PORT = 54329
46
+
47
+ /** Sync optional Docker image pins from config into `.env` (no JWT rotation). */
48
+ export function syncComposeImagePins(cwd: string, config: SupatypeProjectConfig): void {
49
+ const imagePins = composeDockerImageEnv(config)
50
+ const removeImageKeys = COMPOSE_PINNED_IMAGE_ENV_KEYS.filter((key) => !(key in imagePins))
51
+ upsertEnvFile(cwd, imagePins, removeImageKeys)
52
+ }
53
+
54
+ export interface DevComposeOptions {
55
+ watch: boolean
56
+ }
57
+
58
+ /** In-compose Postgres URL (SCRAM; not published to the host). */
59
+ export function composeDbUrl(): string {
60
+ return "postgresql://supatype_admin:postgres@db:5432/supatype?sslmode=disable"
61
+ }
62
+
63
+ /**
64
+ * Resolve the host Kong port for this project. Persisted in `.env` as
65
+ * SUPATYPE_KONG_PORT so re-runs are stable; on first run it picks the default
66
+ * (18473) or the next free port, so multiple projects can run concurrently.
67
+ */
68
+ async function resolveDevDbPort(cwd: string): Promise<number> {
69
+ const envPath = join(cwd, ".env")
70
+ if (existsSync(envPath)) {
71
+ const m = readFileSync(envPath, "utf8").match(/^SUPATYPE_DEV_DB_PORT=(\d+)/m)
72
+ if (m && m[1]) return Number(m[1])
73
+ }
74
+ let port = COMPOSE_DEV_DB_PORT
75
+ while (await isPortInUse(port)) port++
76
+ return port
77
+ }
78
+
79
+ function readEnvValue(cwd: string, key: string, fallback: string): string {
80
+ const envPath = join(cwd, ".env")
81
+ if (existsSync(envPath)) {
82
+ const m = readFileSync(envPath, "utf8").match(new RegExp(`^${key}=(.+)$`, "m"))
83
+ if (m?.[1]) return m[1].trim()
84
+ }
85
+ return fallback
86
+ }
87
+
88
+ /** Postgres DSN for compose db when published to the host (local engine push). */
89
+ function hostComposeDbUrl(cwd: string): string {
90
+ const port = readEnvValue(cwd, "SUPATYPE_DEV_DB_PORT", String(COMPOSE_DEV_DB_PORT))
91
+ const user = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin")
92
+ const pass = readEnvValue(cwd, "POSTGRES_PASSWORD", "postgres")
93
+ const db = readEnvValue(cwd, "POSTGRES_DB", "supatype")
94
+ return `postgresql://${user}:${pass}@127.0.0.1:${port}/${db}?sslmode=disable`
95
+ }
96
+
97
+ async function resolveKongPort(cwd: string): Promise<number> {
98
+ const envPath = join(cwd, ".env")
99
+ if (existsSync(envPath)) {
100
+ const m = readFileSync(envPath, "utf8").match(/^SUPATYPE_KONG_PORT=(\d+)/m)
101
+ if (m && m[1]) return Number(m[1])
102
+ }
103
+ let port = COMPOSE_DEV_KONG_PORT
104
+ while (await isPortInUse(port)) port++
105
+ return port
106
+ }
107
+
108
+ function upsertEnvFile(
109
+ cwd: string,
110
+ updates: Record<string, string>,
111
+ removeKeys: readonly string[] = [],
112
+ ): void {
113
+ const envPath = join(cwd, ".env")
114
+ const existing = existsSync(envPath) ? readFileSync(envPath, "utf8") : ""
115
+ const keys = new Set([...Object.keys(updates), ...removeKeys])
116
+ const kept = existing
117
+ .split("\n")
118
+ .filter((line) => {
119
+ const key = line.split("=")[0]?.trim()
120
+ return key && line.includes("=") && !keys.has(key)
121
+ })
122
+ const merged = [...kept, ...Object.entries(updates).map(([key, value]) => `${key}=${value}`)]
123
+ writeFileSync(envPath, `${merged.join("\n").trimEnd()}\n`, "utf8")
124
+ }
125
+
126
+ /** Keep compose + Studio on the same freshly signed dev JWTs; sync optional image pins from config. */
127
+ function ensureDevComposeEnv(
128
+ cwd: string,
129
+ config: SupatypeProjectConfig,
130
+ anonKey: string,
131
+ serviceRoleKey: string,
132
+ kongPort: number,
133
+ devDbPort?: number,
134
+ ): void {
135
+ const apiUrl = `http://localhost:${kongPort}`
136
+ const imagePins = composeDockerImageEnv(config)
137
+ const updates: Record<string, string> = {
138
+ POSTGRES_USER: "supatype_admin",
139
+ POSTGRES_PASSWORD: "postgres",
140
+ POSTGRES_DB: "supatype",
141
+ JWT_SECRET: LOCAL_JWT_SECRET,
142
+ ANON_KEY: anonKey,
143
+ SERVICE_ROLE_KEY: serviceRoleKey,
144
+ PUBLIC_SUPATYPE_ANON_KEY: anonKey,
145
+ PUBLIC_SUPATYPE_URL: apiUrl,
146
+ SUPATYPE_KONG_PORT: String(kongPort),
147
+ API_EXTERNAL_URL: apiUrl,
148
+ SITE_URL: apiUrl,
149
+ GOTRUE_MAILER_AUTOCONFIRM: "true",
150
+ ...imagePins,
151
+ }
152
+ if (devDbPort !== undefined) {
153
+ updates.SUPATYPE_DEV_DB_PORT = String(devDbPort)
154
+ }
155
+ const removeImageKeys = COMPOSE_PINNED_IMAGE_ENV_KEYS.filter((key) => !(key in imagePins))
156
+ upsertEnvFile(cwd, updates, removeImageKeys)
157
+ }
158
+
159
+ async function waitComposeHealthy(paths: SelfHostComposePaths, cwd: string, maxMs: number, composeProject: string): Promise<void> {
160
+ const composeDir = dirname(paths.composePath)
161
+ const envFile = join(cwd, ".env")
162
+ const baseArgs = ["compose", "-p", composeProject, "-f", paths.composePath]
163
+ if (existsSync(envFile)) baseArgs.push("--env-file", envFile)
164
+
165
+ const deadline = Date.now() + maxMs
166
+ while (Date.now() < deadline) {
167
+ const ready = spawnSync(
168
+ "docker",
169
+ [...baseArgs, "exec", "-T", "db", "pg_isready", "-U", "supatype_admin"],
170
+ { cwd: composeDir, encoding: "utf8" },
171
+ )
172
+ if (ready.status === 0) return
173
+ await new Promise((r) => setTimeout(r, 2000))
174
+ }
175
+ throw new Error("Compose db service did not become healthy in time")
176
+ }
177
+
178
+ async function waitKongReady(kongPort: number, maxSec: number): Promise<void> {
179
+ const base = `http://localhost:${kongPort}`
180
+ for (let i = 0; i < maxSec; i++) {
181
+ try {
182
+ const res = await fetch(`${base}/auth/v1/health`)
183
+ if (res.ok) return
184
+ } catch {
185
+ /* retry */
186
+ }
187
+ await new Promise((r) => setTimeout(r, 1000))
188
+ }
189
+ throw new Error(`Kong gateway at ${base} did not become ready within ${maxSec}s`)
190
+ }
191
+
192
+ let _lastPushedAst: string | null = null
193
+ let _lastFailedAst: string | null = null
194
+ let _composePushInFlight = false
195
+ let _composePushQueued = false
196
+
197
+ /**
198
+ * Regenerate admin-config + TypeScript types from the AST using the **host** engine.
199
+ * Only schema push/migrate runs in compose (Postgres is not on the host).
200
+ */
201
+ async function refreshSchemaArtifacts(
202
+ cwd: string,
203
+ config: SupatypeProjectConfig,
204
+ ast: unknown,
205
+ ): Promise<void> {
206
+ const supatypeDir = join(cwd, ".supatype")
207
+ const adminConfigPath = join(supatypeDir, "admin-config.json")
208
+
209
+ try {
210
+ await ensureEngine()
211
+ } catch (err) {
212
+ console.warn(
213
+ `[supatype] Host engine unavailable — admin/types not refreshed: ${(err as Error).message}`,
214
+ )
215
+ return
216
+ }
217
+
218
+ const typesPath = config.output?.types
219
+ if (typeof typesPath === "string" && typesPath.trim().length > 0) {
220
+ try {
221
+ const result = await engineRequest<{ code?: string; message?: string }>("/generate", {
222
+ ast,
223
+ lang: "typescript",
224
+ })
225
+ const typesCode = result.code ?? result.message
226
+ if (typeof typesCode === "string" && typesCode.includes("export type")) {
227
+ const marker = typesCode.indexOf("// Generated by supatype-engine")
228
+ const ts = (marker >= 0 ? typesCode.slice(marker) : typesCode).trimStart()
229
+ const hostPath = join(cwd, typesPath)
230
+ mkdirSync(dirname(hostPath), { recursive: true })
231
+ writeFileSync(hostPath, ts)
232
+ console.log(`[supatype] Types written to ${typesPath}`)
233
+ } else {
234
+ console.warn("[supatype] Type generation produced no output.")
235
+ }
236
+ } catch (err) {
237
+ console.warn(`[supatype] Type generation failed: ${(err as Error).message}`)
238
+ }
239
+ }
240
+
241
+ try {
242
+ const admin = withAdminRoles(await engineRequest<unknown>("/admin", { ast }), config)
243
+ restoreSystemRelationTargets(admin, ast)
244
+ writeFileSync(adminConfigPath, `${JSON.stringify(admin, null, 2)}\n`)
245
+ console.log("[supatype] Admin config written to .supatype/admin-config.json")
246
+ } catch (err) {
247
+ console.warn(
248
+ `[supatype] Admin config generation failed — Studio may show stale field widgets: ${(err as Error).message}`,
249
+ )
250
+ }
251
+ }
252
+
253
+ async function runComposeSchemaPush(
254
+ cwd: string,
255
+ config: SupatypeProjectConfig,
256
+ paths: SelfHostComposePaths,
257
+ schemaPath: string,
258
+ composeProject: string,
259
+ ): Promise<void> {
260
+ const ast = loadSchemaAst(schemaPath, cwd)
261
+ const astJson = JSON.stringify(ast)
262
+
263
+ const supatypeDir = join(cwd, ".supatype")
264
+ mkdirSync(supatypeDir, { recursive: true })
265
+ const astPath = join(supatypeDir, "schema.ast.json")
266
+ // Always materialise on disk — schema-engine reads via bind mount; skip must not omit the write.
267
+ writeFileSync(astPath, astJson)
268
+ if (astJson === _lastPushedAst && astJson !== _lastFailedAst) return
269
+
270
+ if (!existsSync(astPath)) {
271
+ throw new Error(`Failed to write schema AST at ${astPath}`)
272
+ }
273
+
274
+ // Admin + types come from the AST only (no DB) — refresh before push so Studio stays
275
+ // in sync even when migration fails (e.g. bad engine image, lossy column change).
276
+ await refreshSchemaArtifacts(cwd, config, ast)
277
+
278
+ if (hasEngineOverride(config)) {
279
+ console.log("[supatype] Applying schema via local engine (overrides.engine)...")
280
+ await ensureEngine()
281
+ const pgSchema = config.schema?.pg_schema ?? "public"
282
+ try {
283
+ await engineRequest("/push", {
284
+ ast,
285
+ database_url: hostComposeDbUrl(cwd),
286
+ schema: pgSchema,
287
+ force: true,
288
+ })
289
+ } catch (err) {
290
+ _lastFailedAst = astJson
291
+ throw err
292
+ }
293
+ _lastPushedAst = astJson
294
+ _lastFailedAst = null
295
+ if (astHasSystemAuthRelation(ast)) {
296
+ grantAuthSchemaAccess(paths, cwd, composeProject)
297
+ }
298
+ console.log("[supatype] Schema applied.")
299
+ return
300
+ }
301
+
302
+ console.log("[supatype] Applying schema via compose schema-engine...")
303
+ let push = await runComposeEnginePush(paths, cwd, composeProject, config)
304
+ // Windows Docker bind mounts can lag briefly after the host write.
305
+ if (push.status !== 0) {
306
+ await new Promise((r) => setTimeout(r, 250))
307
+ push = await runComposeEnginePush(paths, cwd, composeProject, config)
308
+ }
309
+ if (push.status !== 0) {
310
+ _lastFailedAst = astJson
311
+ const detail = filterComposeNoise(push.output) || push.output
312
+ throw new Error(detail || `Engine schema push failed (exit ${push.status})`)
313
+ }
314
+ _lastPushedAst = astJson
315
+ _lastFailedAst = null
316
+
317
+ if (astHasSystemAuthRelation(ast)) {
318
+ grantAuthSchemaAccess(paths, cwd, composeProject)
319
+ }
320
+ }
321
+
322
+ /** Serialize watch-triggered pushes so docker output cannot interleave. */
323
+ async function runComposeSchemaPushQueued(
324
+ cwd: string,
325
+ config: SupatypeProjectConfig,
326
+ paths: SelfHostComposePaths,
327
+ schemaPath: string,
328
+ composeProject: string,
329
+ ): Promise<void> {
330
+ if (_composePushInFlight) {
331
+ _composePushQueued = true
332
+ return
333
+ }
334
+ _composePushInFlight = true
335
+ try {
336
+ do {
337
+ _composePushQueued = false
338
+ await runComposeSchemaPush(cwd, config, paths, schemaPath, composeProject)
339
+ } while (_composePushQueued)
340
+ } finally {
341
+ _composePushInFlight = false
342
+ }
343
+ }
344
+
345
+ async function runComposeEnginePush(
346
+ paths: SelfHostComposePaths,
347
+ cwd: string,
348
+ composeProject: string,
349
+ config: SupatypeProjectConfig,
350
+ ): Promise<{ status: number; output: string }> {
351
+ const envFile = resolve(cwd, ".env")
352
+ const composeArgs = ["compose", "--progress", "quiet"]
353
+ if (composeProject) composeArgs.push("-p", composeProject)
354
+ composeArgs.push("--project-directory", cwd)
355
+ composeArgs.push("-f", paths.composePath)
356
+ if (existsSync(envFile)) {
357
+ composeArgs.push("--env-file", envFile)
358
+ }
359
+ composeArgs.push(
360
+ "--profile",
361
+ "tools",
362
+ "run",
363
+ "--rm",
364
+ "schema-engine",
365
+ "push",
366
+ "-i",
367
+ "/project/.supatype/schema.ast.json",
368
+ "--database-url",
369
+ composeDbUrl(),
370
+ "--force",
371
+ "--non-interactive",
372
+ )
373
+ const pushEnv: NodeJS.ProcessEnv = {
374
+ ...process.env,
375
+ COMPOSE_PROGRESS: "quiet",
376
+ }
377
+ const engineImage = await schemaEngineImageForPush(config)
378
+ if (engineImage) {
379
+ pushEnv.SUPATYPE_ENGINE_IMAGE = engineImage
380
+ }
381
+ const result = spawnSync("docker", composeArgs, {
382
+ cwd,
383
+ encoding: "utf8",
384
+ maxBuffer: 10 * 1024 * 1024,
385
+ env: pushEnv,
386
+ })
387
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim()
388
+ const exitStatus = result.status ?? 1
389
+ const pushResult = parseEnginePushOutput(output)
390
+
391
+ if (exitStatus === 0) {
392
+ if (pushResult) {
393
+ console.log(`[supatype] ${formatEnginePushMessage(pushResult)}`)
394
+ } else {
395
+ console.log("[supatype] Schema applied.")
396
+ }
397
+ }
398
+
399
+ return { status: exitStatus, output }
400
+ }
401
+
402
+ /**
403
+ * `supatype push` when `provider: docker`. Uses in-compose schema-engine unless
404
+ * `overrides.engine` is set — then Postgres is published to the host and push runs
405
+ * through the local engine binary (AST v2, contributor builds).
406
+ */
407
+ export async function pushSchemaDocker(cwd: string, config: SupatypeProjectConfig): Promise<void> {
408
+ if (resolveRuntimeProvider(config) !== "docker") {
409
+ throw new Error("pushSchemaDocker requires provider: docker")
410
+ }
411
+ const project = composeProjectName(config.project.name)
412
+ const kongPort = await resolveKongPort(cwd)
413
+ const devDbPort = hasEngineOverride(config) ? await resolveDevDbPort(cwd) : undefined
414
+
415
+ const now = Math.floor(Date.now() / 1000)
416
+ const jwtBase = { iss: "supatype", iat: now, exp: now + 315_360_000 }
417
+ const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
418
+ const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
419
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
420
+
421
+ const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
422
+
423
+ console.log(`[supatype] provider: docker — applying schema via compose (project ${project})...`)
424
+ const up = runDockerCompose(paths.composePath, ["up", "-d", "db"], cwd, project, { quiet: true })
425
+ if (up !== 0) process.exit(up)
426
+ await waitComposeHealthy(paths, cwd, 120_000, project)
427
+
428
+ const schemaPath = schemaPathFromProject(config, cwd)
429
+ await runComposeSchemaPush(cwd, config, paths, schemaPath, project)
430
+ console.log("[supatype] Schema pushed.")
431
+ }
432
+
433
+ export async function runDevCompose(cwd: string, config: SupatypeProjectConfig, opts: DevComposeOptions): Promise<void> {
434
+ if (resolveRuntimeProvider(config) !== "docker") {
435
+ throw new Error("runDevCompose requires provider: docker")
436
+ }
437
+
438
+ // Per-project compose name + port isolate this project from any other Supatype
439
+ // stack on the machine (own containers, volumes, network, and gateway port).
440
+ const project = composeProjectName(config.project.name)
441
+ const kongPort = await resolveKongPort(cwd)
442
+ const devDbPort = hasEngineOverride(config) ? await resolveDevDbPort(cwd) : undefined
443
+
444
+ const now = Math.floor(Date.now() / 1000)
445
+ const jwtBase = { iss: "supatype", iat: now, exp: now + 315_360_000 }
446
+ const anonKey = signJwt({ ...jwtBase, role: "anon" }, LOCAL_JWT_SECRET)
447
+ const serviceRoleKey = signJwt({ ...jwtBase, role: "service_role" }, LOCAL_JWT_SECRET)
448
+
449
+ ensureDevComposeEnv(cwd, config, anonKey, serviceRoleKey, kongPort, devDbPort)
450
+
451
+ console.log(`[supatype] provider: docker — starting self-host Compose stack (project ${project}, gateway :${kongPort})...`)
452
+ const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
453
+
454
+ const upStatus = runDockerCompose(paths.composePath, ["up", "-d"], cwd, project, { quiet: true })
455
+ if (upStatus !== 0) {
456
+ endDevSession()
457
+ process.exit(upStatus)
458
+ }
459
+
460
+ console.log("[supatype] Waiting for Postgres (compose)...")
461
+ await waitComposeHealthy(paths, cwd, 180_000, project)
462
+
463
+ const schemaPath = schemaPathFromProject(config, cwd)
464
+ await runComposeSchemaPush(cwd, config, paths, schemaPath, project).catch((e: unknown) =>
465
+ console.error("[supatype] Initial schema push failed:", (e as Error).message),
466
+ )
467
+
468
+ console.log("[supatype] Waiting for API gateway...")
469
+ await waitKongReady(kongPort, 120)
470
+
471
+ const pidDir = join(homedir(), ".supatype", "projects", config.project.name, "pid")
472
+ mkdirSync(pidDir, { recursive: true })
473
+
474
+ let studioProc: Awaited<ReturnType<typeof startStudioViteDevServer>> = null
475
+ const studioOverride = config.overrides?.studio
476
+ if (studioOverride) {
477
+ studioProc = startStudioViteDevServer({
478
+ cwd,
479
+ studioOverride,
480
+ pidDir,
481
+ serviceRoleKey,
482
+ proxyTarget: `http://localhost:${kongPort}`,
483
+ viteSupatypeUrl: `http://localhost:${kongPort}`,
484
+ basePath: "/studio/",
485
+ })
486
+ studioProc?.start()
487
+ if (studioProc) {
488
+ console.log("[supatype] Studio Vite dev server (overrides.studio) — live reload at /studio/")
489
+ }
490
+ }
491
+
492
+ console.log(`
493
+ [supatype] Services running (Docker Compose · project ${project}):
494
+ API (Kong) http://localhost:${kongPort}
495
+ REST API http://localhost:${kongPort}/rest/v1/
496
+ Auth http://localhost:${kongPort}/auth/v1/
497
+ Storage http://localhost:${kongPort}/storage/v1/
498
+ Realtime ws://localhost:${kongPort}/realtime/v1/
499
+ Studio http://localhost:${kongPort}/studio/
500
+
501
+ API keys (local dev only):
502
+ anon key ${anonKey}
503
+ service_role ${serviceRoleKey}
504
+
505
+ Press Ctrl+C to stop.
506
+ `)
507
+
508
+ const appProc = startProxyDevApp(cwd, config, pidDir)
509
+
510
+ let schemaWatcher: import("node:fs").FSWatcher | null = null
511
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null
512
+
513
+ registerDevShutdown(async () => {
514
+ schemaWatcher?.close()
515
+ schemaWatcher = null
516
+ if (debounceTimer) {
517
+ clearTimeout(debounceTimer)
518
+ debounceTimer = null
519
+ }
520
+ console.log("[supatype] Shutting down compose...")
521
+ await studioProc?.stop()
522
+ await appProc?.stop()
523
+ const downStatus = runDockerCompose(paths.composePath, ["down"], cwd, project, { quiet: true })
524
+ if (downStatus === 0) {
525
+ console.log("[supatype] Compose stack stopped.")
526
+ } else {
527
+ console.warn(`[supatype] Compose down exited with status ${downStatus}.`)
528
+ }
529
+ })
530
+
531
+ if (opts.watch) {
532
+ const schemaDir = join(projectRootFromConfig(config, cwd), config.schema?.path ?? "schema/index.ts", "..")
533
+ console.log(`[supatype] Watching ${schemaDir} for changes...`)
534
+ const { watch } = await import("node:fs")
535
+ schemaWatcher = watch(schemaDir, { recursive: true }, (_eventType, filename) => {
536
+ if (!filename?.endsWith(".ts")) return
537
+ if (debounceTimer) clearTimeout(debounceTimer)
538
+ debounceTimer = setTimeout(() => {
539
+ debounceTimer = null
540
+ console.log(`\n[supatype] Change detected in ${filename}, pushing schema...`)
541
+ runComposeSchemaPushQueued(cwd, config, paths, schemaPath, project).catch((e: unknown) =>
542
+ console.error("[supatype] Schema push failed:", (e as Error).message),
543
+ )
544
+ }, 300)
545
+ })
546
+ }
547
+
548
+ await new Promise<never>(() => undefined)
549
+ }
550
+
551
+ function astHasSystemAuthRelation(ast: unknown): boolean {
552
+ const obj = ast as { models?: Array<{ fields?: Record<string, { kind?: string; target?: string }> }> }
553
+ if (!obj?.models) return false
554
+ for (const model of obj.models) {
555
+ if (!model.fields) continue
556
+ for (const field of Object.values(model.fields)) {
557
+ if (field.kind === "relation" && field.target === "supatype:user") return true
558
+ }
559
+ }
560
+ return false
561
+ }
562
+
563
+ function grantAuthSchemaAccess(
564
+ paths: SelfHostComposePaths,
565
+ cwd: string,
566
+ composeProject: string,
567
+ ): void {
568
+ const composeDir = dirname(paths.composePath)
569
+ const baseArgs = [
570
+ "compose", "-p", composeProject,
571
+ "-f", paths.composePath,
572
+ ]
573
+ const sql = "GRANT USAGE ON SCHEMA auth TO service_role; GRANT SELECT ON auth.users TO service_role;"
574
+ const result = spawnSync(
575
+ "docker",
576
+ [...baseArgs, "exec", "-T", "-e", "PGPASSWORD=postgres", "db",
577
+ "psql", "-U", "supatype_admin", "-d", "supatype", "-c", sql],
578
+ { cwd: composeDir, encoding: "utf8", timeout: 10_000 },
579
+ )
580
+ if (result.status !== 0) {
581
+ console.warn("[supatype] Could not grant service_role access to auth.users — Studio relation preview may fail.")
582
+ }
583
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Per-task log buffers for `supatype dev` TUI mode.
3
+ */
4
+
5
+ export type DevLogLevel = "log" | "warn" | "error"
6
+
7
+ export interface DevTask {
8
+ id: string
9
+ title: string
10
+ lines: string[]
11
+ unread: boolean
12
+ }
13
+
14
+ const DEFAULT_MAX_LINES = 5_000
15
+
16
+ export class DevLogBus {
17
+ private readonly tasks = new Map<string, DevTask>()
18
+ private readonly order: string[] = []
19
+ private readonly listeners = new Set<() => void>()
20
+ private focusedTaskId = "stack"
21
+
22
+ constructor(private readonly maxLines = DEFAULT_MAX_LINES) {
23
+ this.registerTask("stack", "supatype")
24
+ }
25
+
26
+ registerTask(id: string, title: string): void {
27
+ if (this.tasks.has(id)) return
28
+ this.tasks.set(id, { id, title, lines: [], unread: false })
29
+ this.order.push(id)
30
+ this.notify()
31
+ }
32
+
33
+ ensureTask(id: string, title = id): void {
34
+ this.registerTask(id, title)
35
+ }
36
+
37
+ getTaskOrder(): readonly string[] {
38
+ return this.order
39
+ }
40
+
41
+ getTask(id: string): DevTask | undefined {
42
+ return this.tasks.get(id)
43
+ }
44
+
45
+ getFocusedTaskId(): string {
46
+ return this.focusedTaskId
47
+ }
48
+
49
+ setFocusedTaskId(id: string): void {
50
+ if (!this.tasks.has(id)) return
51
+ this.focusedTaskId = id
52
+ const task = this.tasks.get(id)
53
+ if (task) task.unread = false
54
+ this.notify()
55
+ }
56
+
57
+ focusNext(): void {
58
+ const idx = this.order.indexOf(this.focusedTaskId)
59
+ const next = this.order[(idx + 1) % this.order.length]
60
+ if (next) this.setFocusedTaskId(next)
61
+ }
62
+
63
+ focusPrevious(): void {
64
+ const idx = this.order.indexOf(this.focusedTaskId)
65
+ const prev = this.order[(idx - 1 + this.order.length) % this.order.length]
66
+ if (prev) this.setFocusedTaskId(prev)
67
+ }
68
+
69
+ append(taskId: string, line: string, level: DevLogLevel = "log"): void {
70
+ this.ensureTask(taskId, taskId)
71
+ const task = this.tasks.get(taskId)
72
+ if (!task) return
73
+
74
+ const levelPrefix = level === "warn" ? "⚠ " : level === "error" ? "✗ " : ""
75
+ const parts = line.split(/\r?\n/)
76
+
77
+ for (const part of parts) {
78
+ if (part.length === 0) continue
79
+ const text = taskId === "stack" ? part.replace(/^\[supatype\]\s*/, "") : part
80
+ if (text.length === 0) continue
81
+ task.lines.push(levelPrefix + text)
82
+ if (task.lines.length > this.maxLines) {
83
+ task.lines.splice(0, task.lines.length - this.maxLines)
84
+ }
85
+ }
86
+
87
+ if (taskId !== this.focusedTaskId && parts.some((p) => p.length > 0)) {
88
+ task.unread = true
89
+ }
90
+ this.notify()
91
+ }
92
+
93
+ onUpdate(listener: () => void): () => void {
94
+ this.listeners.add(listener)
95
+ return () => this.listeners.delete(listener)
96
+ }
97
+
98
+ private notify(): void {
99
+ for (const listener of this.listeners) listener()
100
+ }
101
+ }