@supatype/cli 0.1.0-alpha.6 → 0.1.0-alpha.7

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 (309) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +203 -1
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/dist/app-config.d.ts +7 -0
  5. package/dist/app-config.d.ts.map +1 -0
  6. package/dist/app-config.js +113 -0
  7. package/dist/app-config.js.map +1 -0
  8. package/dist/augmentation-generator.d.ts +2 -0
  9. package/dist/augmentation-generator.d.ts.map +1 -0
  10. package/dist/augmentation-generator.js +111 -0
  11. package/dist/augmentation-generator.js.map +1 -0
  12. package/dist/binary-cache.d.ts +89 -0
  13. package/dist/binary-cache.d.ts.map +1 -0
  14. package/dist/binary-cache.js +656 -0
  15. package/dist/binary-cache.js.map +1 -0
  16. package/dist/cli.d.ts.map +1 -1
  17. package/dist/cli.js +13 -7
  18. package/dist/cli.js.map +1 -1
  19. package/dist/commands/admin.d.ts.map +1 -1
  20. package/dist/commands/admin.js +4 -3
  21. package/dist/commands/admin.js.map +1 -1
  22. package/dist/commands/app.d.ts.map +1 -1
  23. package/dist/commands/app.js +56 -209
  24. package/dist/commands/app.js.map +1 -1
  25. package/dist/commands/cache.d.ts +6 -0
  26. package/dist/commands/cache.d.ts.map +1 -0
  27. package/dist/commands/cache.js +105 -0
  28. package/dist/commands/cache.js.map +1 -0
  29. package/dist/commands/cloud.d.ts +12 -0
  30. package/dist/commands/cloud.d.ts.map +1 -1
  31. package/dist/commands/cloud.js +36 -46
  32. package/dist/commands/cloud.js.map +1 -1
  33. package/dist/commands/db.d.ts.map +1 -1
  34. package/dist/commands/db.js +47 -54
  35. package/dist/commands/db.js.map +1 -1
  36. package/dist/commands/deploy.d.ts +2 -1
  37. package/dist/commands/deploy.d.ts.map +1 -1
  38. package/dist/commands/deploy.js +92 -51
  39. package/dist/commands/deploy.js.map +1 -1
  40. package/dist/commands/dev.d.ts +11 -0
  41. package/dist/commands/dev.d.ts.map +1 -1
  42. package/dist/commands/dev.js +751 -384
  43. package/dist/commands/dev.js.map +1 -1
  44. package/dist/commands/diff.d.ts.map +1 -1
  45. package/dist/commands/diff.js +20 -15
  46. package/dist/commands/diff.js.map +1 -1
  47. package/dist/commands/engine.d.ts +1 -3
  48. package/dist/commands/engine.d.ts.map +1 -1
  49. package/dist/commands/engine.js +13 -85
  50. package/dist/commands/engine.js.map +1 -1
  51. package/dist/commands/functions.d.ts.map +1 -1
  52. package/dist/commands/functions.js +92 -105
  53. package/dist/commands/functions.js.map +1 -1
  54. package/dist/commands/generate.d.ts.map +1 -1
  55. package/dist/commands/generate.js +22 -12
  56. package/dist/commands/generate.js.map +1 -1
  57. package/dist/commands/init.d.ts +1 -1
  58. package/dist/commands/init.d.ts.map +1 -1
  59. package/dist/commands/init.js +124 -410
  60. package/dist/commands/init.js.map +1 -1
  61. package/dist/commands/migrate-from-v1.d.ts +5 -0
  62. package/dist/commands/migrate-from-v1.d.ts.map +1 -0
  63. package/dist/commands/migrate-from-v1.js +125 -0
  64. package/dist/commands/migrate-from-v1.js.map +1 -0
  65. package/dist/commands/migrate.d.ts.map +1 -1
  66. package/dist/commands/migrate.js +27 -23
  67. package/dist/commands/migrate.js.map +1 -1
  68. package/dist/commands/pg.d.ts +8 -0
  69. package/dist/commands/pg.d.ts.map +1 -0
  70. package/dist/commands/pg.js +102 -0
  71. package/dist/commands/pg.js.map +1 -0
  72. package/dist/commands/pull.d.ts.map +1 -1
  73. package/dist/commands/pull.js +5 -66
  74. package/dist/commands/pull.js.map +1 -1
  75. package/dist/commands/push.d.ts.map +1 -1
  76. package/dist/commands/push.js +99 -39
  77. package/dist/commands/push.js.map +1 -1
  78. package/dist/commands/seed.d.ts +2 -0
  79. package/dist/commands/seed.d.ts.map +1 -1
  80. package/dist/commands/seed.js +44 -11
  81. package/dist/commands/seed.js.map +1 -1
  82. package/dist/commands/self-host.d.ts +7 -1
  83. package/dist/commands/self-host.d.ts.map +1 -1
  84. package/dist/commands/self-host.js +272 -758
  85. package/dist/commands/self-host.js.map +1 -1
  86. package/dist/commands/self-update.d.ts +9 -0
  87. package/dist/commands/self-update.d.ts.map +1 -0
  88. package/dist/commands/self-update.js +33 -0
  89. package/dist/commands/self-update.js.map +1 -0
  90. package/dist/commands/status.d.ts.map +1 -1
  91. package/dist/commands/status.js +4 -3
  92. package/dist/commands/status.js.map +1 -1
  93. package/dist/commands/types.d.ts +3 -0
  94. package/dist/commands/types.d.ts.map +1 -0
  95. package/dist/commands/types.js +62 -0
  96. package/dist/commands/types.js.map +1 -0
  97. package/dist/commands/update.d.ts +7 -0
  98. package/dist/commands/update.d.ts.map +1 -0
  99. package/dist/commands/update.js +77 -0
  100. package/dist/commands/update.js.map +1 -0
  101. package/dist/components.d.ts +5 -0
  102. package/dist/components.d.ts.map +1 -0
  103. package/dist/components.js +3 -0
  104. package/dist/components.js.map +1 -0
  105. package/dist/config.d.ts +10 -51
  106. package/dist/config.d.ts.map +1 -1
  107. package/dist/config.js +101 -33
  108. package/dist/config.js.map +1 -1
  109. package/dist/docker-postgres.d.ts +39 -0
  110. package/dist/docker-postgres.d.ts.map +1 -0
  111. package/dist/docker-postgres.js +96 -0
  112. package/dist/docker-postgres.js.map +1 -0
  113. package/dist/engine-client.d.ts +67 -0
  114. package/dist/engine-client.d.ts.map +1 -0
  115. package/dist/engine-client.js +156 -0
  116. package/dist/engine-client.js.map +1 -0
  117. package/dist/ensure-binary.d.ts +7 -0
  118. package/dist/ensure-binary.d.ts.map +1 -0
  119. package/dist/ensure-binary.js +17 -0
  120. package/dist/ensure-binary.js.map +1 -0
  121. package/dist/functions-router-gen.d.ts +14 -0
  122. package/dist/functions-router-gen.d.ts.map +1 -0
  123. package/dist/functions-router-gen.js +199 -0
  124. package/dist/functions-router-gen.js.map +1 -0
  125. package/dist/index.d.ts +4 -5
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +2 -3
  128. package/dist/index.js.map +1 -1
  129. package/dist/kong-config.d.ts +21 -0
  130. package/dist/kong-config.d.ts.map +1 -0
  131. package/dist/kong-config.js +60 -0
  132. package/dist/kong-config.js.map +1 -0
  133. package/dist/local-gateway.d.ts +7 -0
  134. package/dist/local-gateway.d.ts.map +1 -0
  135. package/dist/local-gateway.js +9 -0
  136. package/dist/local-gateway.js.map +1 -0
  137. package/dist/local-storage.d.ts +8 -0
  138. package/dist/local-storage.d.ts.map +1 -0
  139. package/dist/local-storage.js +14 -0
  140. package/dist/local-storage.js.map +1 -0
  141. package/dist/pgbouncer-userlist.d.ts +5 -0
  142. package/dist/pgbouncer-userlist.d.ts.map +1 -0
  143. package/dist/pgbouncer-userlist.js +14 -0
  144. package/dist/pgbouncer-userlist.js.map +1 -0
  145. package/dist/postgres-ctl.d.ts +44 -0
  146. package/dist/postgres-ctl.d.ts.map +1 -0
  147. package/dist/postgres-ctl.js +137 -0
  148. package/dist/postgres-ctl.js.map +1 -0
  149. package/dist/process-manager.d.ts +41 -0
  150. package/dist/process-manager.d.ts.map +1 -0
  151. package/dist/process-manager.js +120 -0
  152. package/dist/process-manager.js.map +1 -0
  153. package/dist/project-config.d.ts +215 -0
  154. package/dist/project-config.d.ts.map +1 -0
  155. package/dist/project-config.js +145 -0
  156. package/dist/project-config.js.map +1 -0
  157. package/dist/pull-utils.d.ts +15 -0
  158. package/dist/pull-utils.d.ts.map +1 -1
  159. package/dist/pull-utils.js +12 -0
  160. package/dist/pull-utils.js.map +1 -1
  161. package/dist/release-pins.d.ts +7 -0
  162. package/dist/release-pins.d.ts.map +1 -0
  163. package/dist/release-pins.js +27 -0
  164. package/dist/release-pins.js.map +1 -0
  165. package/dist/release-public-key.d.ts +8 -0
  166. package/dist/release-public-key.d.ts.map +1 -0
  167. package/dist/release-public-key.js +13 -0
  168. package/dist/release-public-key.js.map +1 -0
  169. package/dist/runtime-routes.d.ts +25 -0
  170. package/dist/runtime-routes.d.ts.map +1 -0
  171. package/dist/runtime-routes.js +189 -0
  172. package/dist/runtime-routes.js.map +1 -0
  173. package/dist/scripts/postinstall.d.ts +5 -6
  174. package/dist/scripts/postinstall.d.ts.map +1 -1
  175. package/dist/scripts/postinstall.js +36 -20
  176. package/dist/scripts/postinstall.js.map +1 -1
  177. package/dist/self-host-compose.d.ts +14 -0
  178. package/dist/self-host-compose.d.ts.map +1 -0
  179. package/dist/self-host-compose.js +236 -0
  180. package/dist/self-host-compose.js.map +1 -0
  181. package/dist/storage-provision.d.ts +24 -0
  182. package/dist/storage-provision.d.ts.map +1 -0
  183. package/dist/storage-provision.js +44 -0
  184. package/dist/storage-provision.js.map +1 -0
  185. package/dist/systemd.d.ts +26 -0
  186. package/dist/systemd.d.ts.map +1 -0
  187. package/dist/systemd.js +102 -0
  188. package/dist/systemd.js.map +1 -0
  189. package/dist/tsx-runner.d.ts.map +1 -1
  190. package/dist/tsx-runner.js +9 -2
  191. package/dist/tsx-runner.js.map +1 -1
  192. package/dist/type-extractor.d.ts +31 -0
  193. package/dist/type-extractor.d.ts.map +1 -0
  194. package/dist/type-extractor.js +876 -0
  195. package/dist/type-extractor.js.map +1 -0
  196. package/package.json +4 -3
  197. package/releases/deno/VERSION +1 -0
  198. package/scripts/mirror-deno-release.sh +76 -0
  199. package/src/app-config.ts +128 -0
  200. package/src/augmentation-generator.ts +126 -0
  201. package/src/binary-cache.ts +802 -0
  202. package/src/cli.ts +13 -8
  203. package/src/commands/admin.ts +4 -3
  204. package/src/commands/app.ts +67 -231
  205. package/src/commands/cache.ts +117 -0
  206. package/src/commands/cloud.ts +46 -57
  207. package/src/commands/db.ts +54 -63
  208. package/src/commands/deploy.ts +110 -61
  209. package/src/commands/dev.ts +930 -405
  210. package/src/commands/diff.ts +21 -29
  211. package/src/commands/engine.ts +13 -116
  212. package/src/commands/functions.ts +97 -115
  213. package/src/commands/generate.ts +23 -10
  214. package/src/commands/init.ts +136 -414
  215. package/src/commands/migrate-from-v1.ts +131 -0
  216. package/src/commands/migrate.ts +27 -23
  217. package/src/commands/pg.ts +133 -0
  218. package/src/commands/pull.ts +6 -85
  219. package/src/commands/push.ts +128 -59
  220. package/src/commands/seed.ts +54 -12
  221. package/src/commands/self-host.ts +312 -880
  222. package/src/commands/self-update.ts +45 -0
  223. package/src/commands/status.ts +4 -3
  224. package/src/commands/types.ts +76 -0
  225. package/src/commands/update.ts +92 -0
  226. package/src/components.ts +6 -0
  227. package/src/config.ts +127 -94
  228. package/src/docker-postgres.ts +138 -0
  229. package/src/engine-client.ts +231 -0
  230. package/src/ensure-binary.ts +28 -0
  231. package/src/functions-router-gen.ts +224 -0
  232. package/src/index.ts +4 -12
  233. package/src/kong-config.ts +78 -0
  234. package/src/local-gateway.ts +9 -0
  235. package/src/local-storage.ts +14 -0
  236. package/src/pgbouncer-userlist.ts +15 -0
  237. package/src/postgres-ctl.ts +171 -0
  238. package/src/process-manager.ts +151 -0
  239. package/src/project-config.ts +353 -0
  240. package/src/pull-utils.ts +24 -0
  241. package/src/release-pins.ts +31 -0
  242. package/src/release-public-key.ts +12 -0
  243. package/src/runtime-routes.ts +216 -0
  244. package/src/scripts/postinstall.ts +36 -25
  245. package/src/self-host-compose.ts +257 -0
  246. package/src/storage-provision.ts +58 -0
  247. package/src/systemd.ts +137 -0
  248. package/src/tsx-runner.ts +11 -1
  249. package/src/type-extractor.ts +1016 -0
  250. package/tests/app-command.test.ts +54 -0
  251. package/tests/augmentation-generator.test.ts +59 -0
  252. package/tests/binary-cache-cloud-overrides.test.ts +123 -0
  253. package/tests/cached-artifact-format.test.ts +84 -0
  254. package/tests/cli-help.test.ts +40 -14
  255. package/tests/config.test.ts +140 -37
  256. package/tests/engine-distribution.test.ts +3 -3
  257. package/tests/ensure-binary.test.ts +59 -0
  258. package/tests/init.test.ts +28 -86
  259. package/tests/migrate-from-v1.test.ts +29 -0
  260. package/tests/pg-spawn-env.test.ts +18 -0
  261. package/tests/postgres-archive-tag.test.ts +9 -0
  262. package/tests/pull-utils.test.ts +36 -1
  263. package/tests/release-pins.test.ts +28 -0
  264. package/tests/runtime-contract.test.ts +236 -0
  265. package/tests/seed-discover.test.ts +31 -0
  266. package/tests/tsconfig.json +9 -0
  267. package/tests/type-extractor.test.ts +401 -0
  268. package/tsconfig.tsbuildinfo +1 -1
  269. package/vitest.config.ts +12 -0
  270. package/dist/engine/cache.d.ts +0 -37
  271. package/dist/engine/cache.d.ts.map +0 -1
  272. package/dist/engine/cache.js +0 -121
  273. package/dist/engine/cache.js.map +0 -1
  274. package/dist/engine/download.d.ts +0 -19
  275. package/dist/engine/download.d.ts.map +0 -1
  276. package/dist/engine/download.js +0 -108
  277. package/dist/engine/download.js.map +0 -1
  278. package/dist/engine/platform.d.ts +0 -24
  279. package/dist/engine/platform.d.ts.map +0 -1
  280. package/dist/engine/platform.js +0 -50
  281. package/dist/engine/platform.js.map +0 -1
  282. package/dist/engine/resolve.d.ts +0 -37
  283. package/dist/engine/resolve.d.ts.map +0 -1
  284. package/dist/engine/resolve.js +0 -133
  285. package/dist/engine/resolve.js.map +0 -1
  286. package/dist/engine/update-notify.d.ts +0 -11
  287. package/dist/engine/update-notify.d.ts.map +0 -1
  288. package/dist/engine/update-notify.js +0 -43
  289. package/dist/engine/update-notify.js.map +0 -1
  290. package/dist/engine/verify.d.ts +0 -50
  291. package/dist/engine/verify.d.ts.map +0 -1
  292. package/dist/engine/verify.js +0 -161
  293. package/dist/engine/verify.js.map +0 -1
  294. package/dist/engine-version.d.ts +0 -35
  295. package/dist/engine-version.d.ts.map +0 -1
  296. package/dist/engine-version.js +0 -35
  297. package/dist/engine-version.js.map +0 -1
  298. package/dist/engine.d.ts +0 -34
  299. package/dist/engine.d.ts.map +0 -1
  300. package/dist/engine.js +0 -76
  301. package/dist/engine.js.map +0 -1
  302. package/src/engine/cache.ts +0 -135
  303. package/src/engine/download.ts +0 -143
  304. package/src/engine/platform.ts +0 -66
  305. package/src/engine/resolve.ts +0 -197
  306. package/src/engine/update-notify.ts +0 -50
  307. package/src/engine/verify.ts +0 -206
  308. package/src/engine-version.ts +0 -39
  309. package/src/engine.ts +0 -99
package/src/cli.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { Command } from "commander"
2
- import { ENGINE_VERSION } from "./engine-version.js"
3
2
  import { registerInit } from "./commands/init.js"
4
3
  import { registerDev } from "./commands/dev.js"
4
+ import { registerCache } from "./commands/cache.js"
5
+ import { registerUpdate } from "./commands/update.js"
6
+ import { registerPg } from "./commands/pg.js"
5
7
  import { registerPush } from "./commands/push.js"
6
8
  import { registerDiff } from "./commands/diff.js"
7
9
  import { registerPull } from "./commands/pull.js"
@@ -20,16 +22,22 @@ import { registerLogs } from "./commands/logs.js"
20
22
  import { registerAdmin } from "./commands/admin.js"
21
23
  import { registerFunctions } from "./commands/functions.js"
22
24
  import { registerPlugins } from "./commands/plugins.js"
23
- import { showUpdateNotification } from "./engine/update-notify.js"
25
+ import { registerTypes } from "./commands/types.js"
26
+ import { registerMigrateFromV1 } from "./commands/migrate-from-v1.js"
27
+ import { registerSelfUpdate } from "./commands/self-update.js"
24
28
 
25
29
  export function run(): void {
26
30
  const program = new Command()
27
31
  .name("supatype")
28
32
  .description("Supatype — schema-first Postgres API")
29
- .version(ENGINE_VERSION)
33
+ .version("0.1.0")
30
34
 
31
35
  registerInit(program)
32
36
  registerDev(program)
37
+ registerCache(program)
38
+ registerSelfUpdate(program)
39
+ registerUpdate(program)
40
+ registerPg(program)
33
41
  registerPush(program)
34
42
  registerDiff(program)
35
43
  registerPull(program)
@@ -48,11 +56,8 @@ export function run(): void {
48
56
  registerAdmin(program)
49
57
  registerFunctions(program)
50
58
  registerPlugins(program)
51
-
52
- // After command execution, show update notification (non-blocking)
53
- program.hook("postAction", async () => {
54
- await showUpdateNotification()
55
- })
59
+ registerTypes(program)
60
+ registerMigrateFromV1(program)
56
61
 
57
62
  program.parse()
58
63
  }
@@ -8,6 +8,7 @@ import { createInterface } from "node:readline"
8
8
  import { randomBytes, scrypt } from "node:crypto"
9
9
  import { promisify } from "node:util"
10
10
  import { loadConfig } from "../config.js"
11
+ import { connectionString } from "../project-config.js"
11
12
  import { signJwt } from "../jwt.js"
12
13
 
13
14
  const scryptAsync = promisify(scrypt)
@@ -33,7 +34,7 @@ export function registerAdmin(program: Command): void {
33
34
  }) => {
34
35
  const cwd = process.cwd()
35
36
  const config = loadConfig(cwd)
36
- const connection = opts.connection ?? config.connection
37
+ const connection = opts.connection ?? connectionString(config)
37
38
 
38
39
  const email = opts.email ?? (await prompt("Admin email: "))
39
40
  if (!email || !email.includes("@")) {
@@ -146,7 +147,7 @@ export function registerAdmin(program: Command): void {
146
147
  async (opts: { email: string; role: string; connection?: string }) => {
147
148
  const cwd = process.cwd()
148
149
  const config = loadConfig(cwd)
149
- const connection = opts.connection ?? config.connection
150
+ const connection = opts.connection ?? connectionString(config)
150
151
 
151
152
  const pg = await importPg()
152
153
  const pool = new pg.Pool({ connectionString: connection, max: 2 })
@@ -194,7 +195,7 @@ export function registerAdmin(program: Command): void {
194
195
  .action(async (opts: { connection?: string }) => {
195
196
  const cwd = process.cwd()
196
197
  const config = loadConfig(cwd)
197
- const connection = opts.connection ?? config.connection
198
+ const connection = opts.connection ?? connectionString(config)
198
199
 
199
200
  const pg = await importPg()
200
201
  const pool = new pg.Pool({ connectionString: connection, max: 2 })
@@ -1,25 +1,45 @@
1
1
  import type { Command } from "commander"
2
- import { readFileSync, writeFileSync, existsSync } from "node:fs"
2
+ import { existsSync, mkdirSync } from "node:fs"
3
3
  import { resolve } from "node:path"
4
- import { APP_COMPOSE_MARKER, KONG_APP_MARKER } from "./init.js"
4
+ import { localKongBaseUrl } from "../local-gateway.js"
5
+ import { updateAppConfigInProject } from "../app-config.js"
5
6
 
6
7
  export function registerApp(program: Command): void {
7
8
  const appCmd = program
8
9
  .command("app")
9
- .description("Manage your application container in the Supatype stack")
10
+ .description("Manage application routing intent in supatype.config.ts")
10
11
 
11
12
  appCmd
12
- .command("add")
13
- .description("Add your application to the docker-compose stack at /")
14
- .option("--dockerfile <path>", "Path to your Dockerfile", "./Dockerfile")
15
- .option("--port <port>", "Port your app listens on", "3000")
16
- .action((opts: { dockerfile: string; port: string }) => {
17
- addApp(process.cwd(), opts.dockerfile, opts.port)
18
- })
13
+ .command("add [dir]")
14
+ .description("Configure app routing: --static for a built site, or proxy to a dev server")
15
+ .option("--static", "Serve a static directory at / (default: ./public, or [dir] argument)")
16
+ .option("--port <port>", "Port your app listens on (proxy mode)", "3000")
17
+ .option("--upstream <url>", "Explicit upstream URL (proxy mode; defaults to localhost:<port>)")
18
+ .option("--dockerfile <path>", "(deprecated) ignored — use supatype.config.ts")
19
+ .action(
20
+ (
21
+ dir: string | undefined,
22
+ opts: {
23
+ static?: boolean
24
+ port: string
25
+ upstream?: string
26
+ dockerfile?: string
27
+ },
28
+ ) => {
29
+ if (opts.static) {
30
+ addStaticApp(process.cwd(), resolveStaticDir(dir))
31
+ return
32
+ }
33
+ if (opts.dockerfile) {
34
+ console.warn("[supatype] --dockerfile is deprecated and ignored.")
35
+ }
36
+ addProxyApp(process.cwd(), opts.port, opts.upstream)
37
+ },
38
+ )
19
39
 
20
40
  appCmd
21
41
  .command("remove")
22
- .description("Remove your application from the docker-compose stack")
42
+ .description("Set app.mode=none in supatype.config.ts")
23
43
  .action(() => {
24
44
  removeApp(process.cwd())
25
45
  })
@@ -27,235 +47,51 @@ export function registerApp(program: Command): void {
27
47
 
28
48
  // ─── Implementation ───────────────────────────────────────────────────────────
29
49
 
30
- function addApp(cwd: string, dockerfile: string, port: string): void {
31
- const composePath = resolve(cwd, "docker-compose.yml")
32
- if (!existsSync(composePath)) {
33
- console.error("docker-compose.yml not found. Run: supatype init")
34
- process.exit(1)
35
- }
36
-
37
- let compose = readFileSync(composePath, "utf8")
38
- if (!compose.includes(APP_COMPOSE_MARKER)) {
39
- console.error("App service slot not found in docker-compose.yml. Is this a supatype project?")
40
- process.exit(1)
41
- }
42
-
43
- if (isAppActive(compose)) {
44
- console.error("App service is already configured. Run: supatype app remove first.")
45
- process.exit(1)
46
- }
47
-
48
- compose = uncommentServiceBlock(compose, APP_COMPOSE_MARKER, { dockerfile, port })
49
- writeFileSync(composePath, compose, "utf8")
50
- console.log(" updated docker-compose.yml")
51
-
52
- const kongPath = resolve(cwd, ".supatype/kong.yml")
53
- if (existsSync(kongPath)) {
54
- let kong = readFileSync(kongPath, "utf8")
55
- if (kong.includes(KONG_APP_MARKER)) {
56
- kong = uncommentKongBlock(kong, KONG_APP_MARKER, port)
57
- writeFileSync(kongPath, kong, "utf8")
58
- console.log(" updated .supatype/kong.yml")
59
- }
60
- }
61
-
62
- console.log(`\nApp service added (port ${port}). Your app will be available at http://localhost:8000/\n`)
63
- console.log("Run: supatype dev")
50
+ function resolveStaticDir(dir?: string): string {
51
+ const trimmed = dir?.trim()
52
+ return trimmed && trimmed.length > 0 ? trimmed : "./public"
64
53
  }
65
54
 
66
- function removeApp(cwd: string): void {
67
- const composePath = resolve(cwd, "docker-compose.yml")
68
- if (!existsSync(composePath)) {
69
- console.error("docker-compose.yml not found.")
70
- process.exit(1)
55
+ function addStaticApp(cwd: string, staticDir: string): void {
56
+ const abs = resolve(cwd, staticDir)
57
+ if (!existsSync(abs)) {
58
+ mkdirSync(abs, { recursive: true })
59
+ console.log(` created ${staticDir}/`)
71
60
  }
72
-
73
- let compose = readFileSync(composePath, "utf8")
74
- if (!isAppActive(compose)) {
75
- console.error("No active app service found.")
61
+ try {
62
+ const configPath = updateAppConfigInProject(cwd, { mode: "static", staticDir })
63
+ console.log(` updated ${configPath}`)
64
+ console.log(`\nStatic app directory: ${staticDir}`)
65
+ console.log(`Build your frontend into ${staticDir}/, then:`)
66
+ console.log(` supatype self-host compose render`)
67
+ console.log(` supatype self-host compose up -d`)
68
+ console.log(`\nYour app will be served at ${localKongBaseUrl()}/\n`)
69
+ } catch (err) {
70
+ console.error((err as Error).message)
76
71
  process.exit(1)
77
72
  }
78
-
79
- compose = recommentServiceBlock(compose, APP_COMPOSE_MARKER)
80
- writeFileSync(composePath, compose, "utf8")
81
- console.log(" updated docker-compose.yml")
82
-
83
- const kongPath = resolve(cwd, ".supatype/kong.yml")
84
- if (existsSync(kongPath)) {
85
- let kong = readFileSync(kongPath, "utf8")
86
- if (!kong.includes(KONG_APP_MARKER)) {
87
- // Active route — re-comment it
88
- kong = recommentKongBlock(kong)
89
- }
90
- writeFileSync(kongPath, kong, "utf8")
91
- console.log(" updated .supatype/kong.yml")
92
- }
93
-
94
- console.log("\nApp service removed.\n")
95
- }
96
-
97
- // ─── Block manipulation helpers ───────────────────────────────────────────────
98
-
99
- /** Returns true if the docker-compose has an active (uncommented) app: service. */
100
- function isAppActive(compose: string): boolean {
101
- return /^ app:/m.test(compose)
102
- }
103
-
104
- /**
105
- * Finds the commented app service block after the marker line and uncomments it,
106
- * substituting the dockerfile path and port.
107
- */
108
- function uncommentServiceBlock(
109
- compose: string,
110
- marker: string,
111
- opts: { dockerfile: string; port: string },
112
- ): string {
113
- const lines = compose.split("\n")
114
- const markerIdx = lines.findIndex((l) => l === marker)
115
- if (markerIdx === -1) return compose
116
-
117
- const result: string[] = []
118
- let i = 0
119
-
120
- while (i < lines.length) {
121
- if (i === markerIdx) {
122
- // Skip the marker line itself, then uncomment the block
123
- i++
124
- while (i < lines.length) {
125
- const line = lines[i]!
126
- // End of block: empty line followed by a non-commented service-level line
127
- if (line === "" && i + 1 < lines.length && !/^ #/.test(lines[i + 1]!)) {
128
- result.push(line)
129
- i++
130
- break
131
- }
132
- if (/^ # /.test(line)) {
133
- // Uncomment: replace " # " prefix with " "
134
- let uncommented = line.replace(/^ # /, " ")
135
- // Substitute placeholders
136
- uncommented = uncommented.replace("./Dockerfile", opts.dockerfile)
137
- uncommented = uncommented.replace(/- "3000:3000"/, `- "${opts.port}:${opts.port}"`)
138
- result.push(uncommented)
139
- } else if (line === "" || /^ #─/.test(line)) {
140
- // Skip remaining marker-style comment lines
141
- } else {
142
- result.push(line)
143
- }
144
- i++
145
- }
146
- } else {
147
- result.push(lines[i]!)
148
- i++
149
- }
150
- }
151
-
152
- return result.join("\n")
153
- }
154
-
155
- /**
156
- * Finds the active app: service block and re-comments it, restoring the marker.
157
- */
158
- function recommentServiceBlock(compose: string, marker: string): string {
159
- const lines = compose.split("\n")
160
- const appIdx = lines.findIndex((l) => l === " app:")
161
- if (appIdx === -1) return compose
162
-
163
- const result: string[] = []
164
- let i = 0
165
-
166
- while (i < lines.length) {
167
- if (i === appIdx) {
168
- result.push(marker)
169
- // Re-comment lines until we hit an empty line or the volumes: section
170
- while (i < lines.length) {
171
- const line = lines[i]!
172
- if (line === "" || /^volumes:/.test(line) || /^ \w/.test(line)) {
173
- result.push(line)
174
- i++
175
- break
176
- }
177
- // Re-comment: " X..." → " # X..."
178
- result.push(line.replace(/^ /, " # "))
179
- i++
180
- }
181
- } else {
182
- result.push(lines[i]!)
183
- i++
184
- }
185
- }
186
-
187
- return result.join("\n")
188
73
  }
189
74
 
190
- /**
191
- * Uncomments the Kong app route block after the marker.
192
- */
193
- function uncommentKongBlock(kong: string, marker: string, port: string): string {
194
- const lines = kong.split("\n")
195
- const markerIdx = lines.findIndex((l) => l === marker)
196
- if (markerIdx === -1) return kong
197
-
198
- const result: string[] = []
199
- let i = 0
200
-
201
- while (i < lines.length) {
202
- if (i === markerIdx) {
203
- i++ // skip marker
204
- while (i < lines.length) {
205
- const line = lines[i]!
206
- if (line === "" && (i + 1 >= lines.length || !/^ #/.test(lines[i + 1]!))) {
207
- result.push(line)
208
- i++
209
- break
210
- }
211
- if (/^ # /.test(line)) {
212
- let uncommented = line.replace(/^ # /, " ")
213
- uncommented = uncommented.replace(":3000", `:${port}`)
214
- result.push(uncommented)
215
- } else {
216
- result.push(line)
217
- }
218
- i++
219
- }
220
- } else {
221
- result.push(lines[i]!)
222
- i++
223
- }
75
+ function addProxyApp(cwd: string, port: string, upstream?: string): void {
76
+ const upstreamUrl = upstream?.trim() || `http://localhost:${port}`
77
+ try {
78
+ const configPath = updateAppConfigInProject(cwd, { mode: "proxy", upstream: upstreamUrl })
79
+ console.log(` updated ${configPath}`)
80
+ console.log(`\nApp upstream set to ${upstreamUrl}. The app will be available at ${localKongBaseUrl()}/\n`)
81
+ console.log("Run: supatype self-host compose render")
82
+ } catch (err) {
83
+ console.error((err as Error).message)
84
+ process.exit(1)
224
85
  }
225
-
226
- return result.join("\n")
227
86
  }
228
87
 
229
- /**
230
- * Finds the active Kong app route and re-comments it, restoring the marker.
231
- */
232
- function recommentKongBlock(kong: string): string {
233
- const lines = kong.split("\n")
234
- // Find " - name: app" (the active route)
235
- const appIdx = lines.findIndex((l) => /^ - name: app$/.test(l))
236
- if (appIdx === -1) return kong
237
-
238
- const result: string[] = []
239
- let i = 0
240
-
241
- while (i < lines.length) {
242
- if (i === appIdx) {
243
- result.push(KONG_APP_MARKER)
244
- while (i < lines.length) {
245
- const line = lines[i]!
246
- if (line === "" || (i > appIdx && /^ - /.test(line))) {
247
- result.push(line)
248
- i++
249
- break
250
- }
251
- result.push(line.replace(/^ /, " # "))
252
- i++
253
- }
254
- } else {
255
- result.push(lines[i]!)
256
- i++
257
- }
88
+ function removeApp(cwd: string): void {
89
+ try {
90
+ const configPath = updateAppConfigInProject(cwd, { mode: "none" })
91
+ console.log(` updated ${configPath}`)
92
+ console.log("\nApp routing disabled (app.mode=none).\n")
93
+ } catch (err) {
94
+ console.error((err as Error).message)
95
+ process.exit(1)
258
96
  }
259
-
260
- return result.join("\n")
261
97
  }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * supatype cache — list and clean cached component binaries.
3
+ */
4
+
5
+ import type { Command } from "commander"
6
+ import { existsSync, readdirSync, rmSync, statSync } from "node:fs"
7
+ import { join } from "node:path"
8
+ import { cacheRoot, type Component } from "../binary-cache.js"
9
+
10
+ const COMPONENTS: Component[] = ["engine", "server", "postgres", "deno"]
11
+
12
+ export function registerCache(program: Command): void {
13
+ const cache = program
14
+ .command("cache")
15
+ .description("Manage cached component binaries")
16
+
17
+ cache
18
+ .command("list")
19
+ .description("List cached component binaries and their sizes")
20
+ .action(() => {
21
+ const root = cacheRoot()
22
+ if (!existsSync(root)) {
23
+ console.log("Cache is empty.")
24
+ return
25
+ }
26
+
27
+ let totalBytes = 0
28
+ let found = false
29
+
30
+ for (const component of COMPONENTS) {
31
+ const compDir = join(root, component)
32
+ if (!existsSync(compDir)) continue
33
+
34
+ const versions = readdirSync(compDir).filter(
35
+ (v) => statSync(join(compDir, v)).isDirectory(),
36
+ )
37
+ for (const version of versions) {
38
+ const vDir = join(compDir, version)
39
+ const size = dirSize(vDir)
40
+ totalBytes += size
41
+ found = true
42
+ console.log(` ${component}@${version} ${formatBytes(size)}`)
43
+ }
44
+ }
45
+
46
+ if (!found) {
47
+ console.log("Cache is empty.")
48
+ return
49
+ }
50
+
51
+ console.log(`\nTotal: ${formatBytes(totalBytes)}`)
52
+ console.log(`Cache root: ${root}`)
53
+ })
54
+
55
+ cache
56
+ .command("clean [component] [version]")
57
+ .description(
58
+ "Remove cached binaries. Optionally specify a component and/or version.\n" +
59
+ "Examples:\n" +
60
+ " supatype cache clean # remove everything\n" +
61
+ " supatype cache clean engine # remove all engine versions\n" +
62
+ " supatype cache clean engine 0.4.2",
63
+ )
64
+ .action((component?: string, version?: string) => {
65
+ const root = cacheRoot()
66
+ if (!existsSync(root)) {
67
+ console.log("Cache is already empty.")
68
+ return
69
+ }
70
+
71
+ const targets = component ? [component as Component] : COMPONENTS
72
+
73
+ for (const comp of targets) {
74
+ const compDir = join(root, comp)
75
+ if (!existsSync(compDir)) continue
76
+
77
+ if (version) {
78
+ const vDir = join(compDir, version)
79
+ if (!existsSync(vDir)) {
80
+ console.log(` ${comp}@${version} not cached.`)
81
+ continue
82
+ }
83
+ rmSync(vDir, { recursive: true, force: true })
84
+ console.log(` removed ${comp}@${version}`)
85
+ } else {
86
+ rmSync(compDir, { recursive: true, force: true })
87
+ console.log(` removed ${comp} (all versions)`)
88
+ }
89
+ }
90
+
91
+ console.log("Done.")
92
+ })
93
+ }
94
+
95
+ function dirSize(dir: string): number {
96
+ let total = 0
97
+ try {
98
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
99
+ const full = join(dir, entry.name)
100
+ if (entry.isDirectory()) {
101
+ total += dirSize(full)
102
+ } else {
103
+ total += statSync(full).size
104
+ }
105
+ }
106
+ } catch {
107
+ // Skip unreadable entries.
108
+ }
109
+ return total
110
+ }
111
+
112
+ function formatBytes(bytes: number): string {
113
+ if (bytes < 1024) return `${bytes} B`
114
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
115
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
116
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`
117
+ }
@@ -9,7 +9,7 @@ interface CloudConfig {
9
9
  projectSlug?: string
10
10
  }
11
11
 
12
- function loadCloudConfig(cwd: string): CloudConfig | null {
12
+ export function loadCloudConfig(cwd: string): CloudConfig | null {
13
13
  const configPath = resolve(cwd, ".supatype/cloud.json")
14
14
  if (!existsSync(configPath)) return null
15
15
  return JSON.parse(readFileSync(configPath, "utf8")) as CloudConfig
@@ -41,6 +41,49 @@ async function cloudFetch<T>(config: CloudConfig, method: string, path: string,
41
41
  return json.data as T
42
42
  }
43
43
 
44
+ /**
45
+ * Push schema AST to the linked cloud project (control plane `/api/v1/.../deploy`).
46
+ * Invoked by `supatype deploy` by default when `.supatype/cloud.json` is present.
47
+ */
48
+ export async function deploySchemaToLinkedProject(
49
+ cwd: string,
50
+ environment: string,
51
+ ): Promise<void> {
52
+ const config = loadCloudConfig(cwd)
53
+ if (!config?.projectSlug) {
54
+ console.error("Not linked to a cloud project. Run: supatype link")
55
+ process.exit(1)
56
+ }
57
+
58
+ console.log(`Deploying schema to ${config.projectSlug} (${environment})...`)
59
+
60
+ const { loadConfig: loadAppConfig, loadSchemaAst } = await import("../config.js")
61
+ const { schemaPathFromProject } = await import("../project-config.js")
62
+
63
+ const appConfig = loadAppConfig(cwd)
64
+ const ast = loadSchemaAst(schemaPathFromProject(appConfig, cwd), cwd)
65
+
66
+ const { createHash } = await import("node:crypto")
67
+ const schemaHash = createHash("sha256").update(JSON.stringify(ast)).digest("hex").slice(0, 16)
68
+
69
+ const deployment = await cloudFetch<{
70
+ id: string; status: string; errorMessage?: string
71
+ }>(config, "POST", `/projects/${config.projectSlug}/deploy`, {
72
+ environment,
73
+ schemaHash,
74
+ ast,
75
+ })
76
+
77
+ if (deployment.status === "success") {
78
+ console.log(`\nDeployment successful (${deployment.id})`)
79
+ } else if (deployment.status === "failed") {
80
+ console.error(`\nDeployment failed: ${deployment.errorMessage}`)
81
+ process.exit(1)
82
+ } else {
83
+ console.log(`\nDeployment ${deployment.status} (${deployment.id})`)
84
+ }
85
+ }
86
+
44
87
  function prompt(question: string): Promise<string> {
45
88
  const rl = createInterface({ input: process.stdin, output: process.stdout })
46
89
  return new Promise((resolve) => {
@@ -59,7 +102,7 @@ export function registerCloud(program: Command): void {
59
102
  .command("link")
60
103
  .description("Link this local project to a Supatype cloud project")
61
104
  .option("--project <slug>", "Project slug to link to")
62
- .option("--api-url <url>", "Control plane API URL", "https://api.supatype.io")
105
+ .option("--api-url <url>", "Control plane API URL", "https://api.supatype.com")
63
106
  .option("--token <token>", "Authentication token")
64
107
  .action(async (opts: { project?: string; apiUrl: string; token?: string }) => {
65
108
  const cwd = process.cwd()
@@ -102,60 +145,6 @@ export function registerCloud(program: Command): void {
102
145
  console.log(`Config saved to .supatype/cloud.json\n`)
103
146
  })
104
147
 
105
- // ── Deploy ─────────────────────────────────────────────────────────────────
106
- program
107
- .command("deploy")
108
- .description("Deploy schema to the linked cloud project")
109
- .option("--environment <name>", "Target environment", "production")
110
- .action(async (opts: { environment: string }) => {
111
- const cwd = process.cwd()
112
- const config = loadCloudConfig(cwd)
113
- if (!config?.projectSlug) {
114
- console.error("Not linked to a cloud project. Run: supatype link")
115
- process.exit(1)
116
- }
117
-
118
- console.log(`Deploying to ${config.projectSlug} (${opts.environment})...`)
119
-
120
- // Load schema AST
121
- const { loadConfig: loadAppConfig, loadSchemaAst } = await import("../config.js")
122
- const { ensureEngine, invokeEngine } = await import("../engine.js")
123
-
124
- const appConfig = loadAppConfig(cwd)
125
- const ast = loadSchemaAst(appConfig.schema, cwd)
126
-
127
- // Generate migration SQL locally using the engine
128
- await ensureEngine()
129
- const migrateResult = invokeEngine(
130
- ["migrate", "--format", "sql"],
131
- JSON.stringify(ast),
132
- )
133
- if (migrateResult.exitCode !== 0) {
134
- console.error("Failed to generate migration:", migrateResult.stderr || migrateResult.stdout)
135
- process.exit(1)
136
- }
137
-
138
- const { createHash } = await import("node:crypto")
139
- const schemaHash = createHash("sha256").update(JSON.stringify(ast)).digest("hex").slice(0, 16)
140
-
141
- const deployment = await cloudFetch<{
142
- id: string; status: string; errorMessage?: string
143
- }>(config, "POST", `/projects/${config.projectSlug}/deploy`, {
144
- environment: opts.environment,
145
- schemaHash,
146
- migrationSql: migrateResult.stdout,
147
- })
148
-
149
- if (deployment.status === "success") {
150
- console.log(`\nDeployment successful (${deployment.id})`)
151
- } else if (deployment.status === "failed") {
152
- console.error(`\nDeployment failed: ${deployment.errorMessage}`)
153
- process.exit(1)
154
- } else {
155
- console.log(`\nDeployment ${deployment.status} (${deployment.id})`)
156
- }
157
- })
158
-
159
148
  // ── Projects ───────────────────────────────────────────────────────────────
160
149
  const projectsCmd = program
161
150
  .command("projects")
@@ -315,7 +304,7 @@ function getCloudConfigOrExit(): CloudConfig {
315
304
  let config = loadCloudConfig(cwd)
316
305
  if (!config) {
317
306
  const token = process.env["SUPATYPE_TOKEN"]
318
- const apiUrl = process.env["SUPATYPE_API_URL"] ?? "https://api.supatype.io"
307
+ const apiUrl = process.env["SUPATYPE_API_URL"] ?? "https://api.supatype.com"
319
308
  if (!token) {
320
309
  console.error("Not connected to Supatype Cloud. Run: supatype link, or set SUPATYPE_TOKEN.")
321
310
  process.exit(1)