@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
@@ -0,0 +1,45 @@
1
+ /**
2
+ * supatype self-update — Phase 10.6C8.
3
+ *
4
+ * npm-installed CLI: instruct to use `npm update -g`.
5
+ * Standalone / future curl|sh installs: CDN-published CLI binary replacement is not wired yet.
6
+ */
7
+
8
+ import type { Command } from "commander"
9
+ import { sep } from "node:path"
10
+
11
+ function looksLikeNpmOrWorkspaceCLI(): boolean {
12
+ const main = process.argv[1] ?? ""
13
+ return (
14
+ main.includes("node_modules") ||
15
+ main.includes(`${sep}dist${sep}cli`) ||
16
+ main.includes(`${sep}bin${sep}supatype.js`) ||
17
+ Boolean(process.env["npm_execpath"]) ||
18
+ Boolean(process.env["npm_lifecycle_event"])
19
+ )
20
+ }
21
+
22
+ export function registerSelfUpdate(program: Command): void {
23
+ program
24
+ .command("self-update")
25
+ .description(
26
+ "Update the supatype CLI (npm: use npm update; standalone binary swap from CDN is not available yet)",
27
+ )
28
+ .action(() => {
29
+ if (looksLikeNpmOrWorkspaceCLI()) {
30
+ console.log(
31
+ "This CLI was installed via npm (or runs from the package workspace).\n" +
32
+ "Update with:\n\n npm update -g @supatype/cli\n\n" +
33
+ "To bump engine/server/postgres/deno pinned in supatype.config.ts, use:\n\n supatype update\n",
34
+ )
35
+ return
36
+ }
37
+
38
+ console.error(
39
+ "Standalone CLI self-update (replace binary from CDN) is not published yet.\n\n" +
40
+ "Install or upgrade via npm:\n npm install -g @supatype/cli\n\n" +
41
+ "Component binaries (engine, server, postgres, deno):\n supatype update\n",
42
+ )
43
+ process.exit(1)
44
+ })
45
+ }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { Command } from "commander"
5
5
  import { spawnSync } from "node:child_process"
6
+ import { LOCAL_KONG_HOST_PORT, localKongBaseUrl } from "../local-gateway.js"
6
7
 
7
8
  interface ServiceStatus {
8
9
  name: string
@@ -21,7 +22,7 @@ export function registerStatus(program: Command): void {
21
22
  { name: "Postgres", container: "supatype-postgres", port: 5432 },
22
23
  { name: "PostgREST", container: "supatype-postgrest", port: 3000 },
23
24
  { name: "GoTrue", container: "supatype-gotrue", port: 9999 },
24
- { name: "Kong", container: "supatype-kong", port: 8000 },
25
+ { name: "Kong", container: "supatype-kong", port: LOCAL_KONG_HOST_PORT },
25
26
  { name: "MinIO", container: "supatype-minio", port: 9000 },
26
27
  { name: "Realtime", container: "supatype-realtime", port: 4000 },
27
28
  { name: "Studio", container: "supatype-studio", port: 3100 },
@@ -46,9 +47,9 @@ export function registerStatus(program: Command): void {
46
47
  console.log(`\n${running.length}/${services.length} services running`)
47
48
 
48
49
  if (running.length > 0) {
49
- console.log(`\nAPI URL: http://localhost:8000`)
50
+ console.log(`\nAPI URL: ${localKongBaseUrl()}`)
50
51
  console.log(`Studio: http://localhost:3100`)
51
- console.log(`Database: postgresql://postgres:postgres@localhost:5432/postgres`)
52
+ console.log(`Database: postgresql://supatype_admin:postgres@localhost:5432/postgres`)
52
53
  }
53
54
  })
54
55
  }
@@ -0,0 +1,76 @@
1
+ import type { Command } from "commander"
2
+ import { existsSync, readFileSync } from "node:fs"
3
+ import { resolve, relative } from "node:path"
4
+ import ts from "typescript"
5
+ import { loadConfig } from "../config.js"
6
+
7
+ export function registerTypes(program: Command): void {
8
+ const types = program.command("types").description("Type generation and validation utilities")
9
+
10
+ types
11
+ .command("check")
12
+ .description("Validate generated client augmentation wiring")
13
+ .action(() => {
14
+ const cwd = process.cwd()
15
+ const config = loadConfig(cwd)
16
+ const augmentationPath = resolve(cwd, config.output?.client ?? "supatype/generated/index.d.ts")
17
+
18
+ const errors: string[] = []
19
+
20
+ if (!existsSync(augmentationPath)) {
21
+ errors.push(
22
+ `Missing generated augmentation file: ${relative(cwd, augmentationPath)}.\nRun: supatype generate`,
23
+ )
24
+ }
25
+
26
+ const tsconfigPath = resolve(cwd, "tsconfig.json")
27
+ if (!existsSync(tsconfigPath)) {
28
+ errors.push("Missing tsconfig.json in project root.")
29
+ } else {
30
+ const tsconfigRaw = readFileSync(tsconfigPath, "utf8")
31
+ const parsed = ts.parseConfigFileTextToJson(tsconfigPath, tsconfigRaw)
32
+ const cfg = (parsed.config ?? {}) as { include?: unknown; files?: unknown }
33
+ const include = Array.isArray(cfg.include) ? cfg.include.map(String) : []
34
+ const files = Array.isArray(cfg.files) ? cfg.files.map(String) : []
35
+ const relAug = toPosix(relative(cwd, augmentationPath))
36
+ const coveredByFiles = files.includes(relAug)
37
+ const coveredByInclude = include.some((entry) => {
38
+ if (entry.includes("**")) return relAug.startsWith(entry.split("**")[0] ?? "")
39
+ if (entry.endsWith("*.ts") || entry.endsWith("*.d.ts")) {
40
+ return relAug.startsWith(entry.replace(/\*\.d?ts$/, ""))
41
+ }
42
+ return relAug === entry || relAug.startsWith(entry.replace(/\/$/, "") + "/")
43
+ })
44
+ if (!coveredByFiles && !coveredByInclude) {
45
+ errors.push(
46
+ `tsconfig.json does not include ${relAug}. Add it to "include" or "files" so module augmentation is visible to TypeScript.`,
47
+ )
48
+ }
49
+ }
50
+
51
+ const packageJsonPath = resolve(cwd, "package.json")
52
+ if (existsSync(packageJsonPath)) {
53
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
54
+ dependencies?: Record<string, string>
55
+ devDependencies?: Record<string, string>
56
+ }
57
+ const hasClient =
58
+ (pkg.dependencies && "@supatype/client" in pkg.dependencies) ||
59
+ (pkg.devDependencies && "@supatype/client" in pkg.devDependencies)
60
+ if (!hasClient) {
61
+ errors.push('package.json is missing "@supatype/client" dependency.')
62
+ }
63
+ }
64
+
65
+ if (errors.length > 0) {
66
+ for (const err of errors) console.error(`- ${err}`)
67
+ process.exit(1)
68
+ }
69
+
70
+ console.log("types check passed")
71
+ })
72
+ }
73
+
74
+ function toPosix(path: string): string {
75
+ return path.replace(/\\/g, "/")
76
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * supatype update — bump component versions in supatype.config.ts
3
+ * and download the new binaries.
4
+ */
5
+
6
+ import type { Command } from "commander"
7
+ import { existsSync, readFileSync, writeFileSync } from "node:fs"
8
+ import { basename, resolve } from "node:path"
9
+ import { loadConfig } from "../config.js"
10
+ import { download, currentPlatform, fetchAllLatestVersions, type Component } from "../binary-cache.js"
11
+
12
+ const CONFIG_CANDIDATES = ["supatype.config.ts", "supatype.config.js", "supatype.config.mjs"]
13
+
14
+ function resolveConfigFile(cwd: string): string | null {
15
+ for (const name of CONFIG_CANDIDATES) {
16
+ const p = resolve(cwd, name)
17
+ if (existsSync(p)) return p
18
+ }
19
+ return null
20
+ }
21
+
22
+ export function registerUpdate(program: Command): void {
23
+ program
24
+ .command("update")
25
+ .description(
26
+ "Download the latest component binaries and update versions in supatype.config.ts",
27
+ )
28
+ .option("--check", "Print available updates without downloading", false)
29
+ .action(async (opts: { check: boolean }) => {
30
+ const cwd = process.cwd()
31
+ const config = loadConfig(cwd)
32
+ const platform = currentPlatform()
33
+
34
+ const components: Component[] = ["engine", "server", "postgres", "deno"]
35
+ const updates: Array<{ component: Component; from: string; to: string }> = []
36
+
37
+ console.log("Fetching latest component versions from CDN...")
38
+ const latestVersions = await fetchAllLatestVersions()
39
+
40
+ for (const component of components) {
41
+ const current = config.versions[component]
42
+ if (current === "local") continue
43
+ const latest = latestVersions[component]
44
+ if (current !== latest) {
45
+ updates.push({ component, from: current, to: latest })
46
+ }
47
+ }
48
+
49
+ if (updates.length === 0) {
50
+ console.log("All components are up to date.")
51
+ return
52
+ }
53
+
54
+ console.log("Available updates:")
55
+ for (const { component, from, to } of updates) {
56
+ console.log(` ${component} ${from} → ${to}`)
57
+ }
58
+
59
+ if (opts.check) return
60
+
61
+ // Download updated binaries.
62
+ for (const { component, to } of updates) {
63
+ console.log(`\nDownloading ${component} v${to}...`)
64
+ try {
65
+ await download(component, to, platform)
66
+ } catch (err) {
67
+ console.error(` Failed: ${(err as Error).message}`)
68
+ continue
69
+ }
70
+ console.log(` ${component} v${to} downloaded.`)
71
+ }
72
+
73
+ const configPath = resolveConfigFile(cwd)
74
+ if (configPath === null) {
75
+ console.error("No supatype.config.ts (or .js/.mjs) found to patch versions.")
76
+ process.exit(1)
77
+ }
78
+
79
+ let text = readFileSync(configPath, "utf8")
80
+ for (const { component, to } of updates) {
81
+ const key = component
82
+ // engine: "0.4.1" or engine: '0.4.1'
83
+ text = text.replace(
84
+ new RegExp(`(${key}\\s*:\\s*['"])[^'"]*(['"])`),
85
+ `$1${to}$2`,
86
+ )
87
+ }
88
+
89
+ writeFileSync(configPath, text, "utf8")
90
+ console.log(`\n${basename(configPath)} updated.`)
91
+ })
92
+ }
@@ -0,0 +1,6 @@
1
+ /** CDN-cached binaries resolved by the CLI (`engine`, `server`, `postgres`, `deno`). */
2
+ export const BINARY_COMPONENTS = ["engine", "server", "postgres", "deno"] as const
3
+
4
+ export type Component = (typeof BINARY_COMPONENTS)[number]
5
+
6
+ export type ComponentVersions = Record<Component, string>
package/src/config.ts CHANGED
@@ -1,6 +1,19 @@
1
- import { existsSync } from "node:fs"
1
+ import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs"
2
2
  import { resolve } from "node:path"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
3
5
  import { evalTsSnippet } from "./tsx-runner.js"
6
+ import {
7
+ mergeProjectConfig,
8
+ validateProjectConfig,
9
+ type SupatypeProjectConfig,
10
+ } from "./project-config.js"
11
+ import { extractSchemaAstFromTypes } from "./type-extractor.js"
12
+
13
+ export type { SupatypeProjectConfig } from "./project-config.js"
14
+
15
+ /** Canonical project configuration shape (alias for clarity at call sites). */
16
+ export type SupatypeConfig = SupatypeProjectConfig
4
17
 
5
18
  export interface ServiceVersionPin {
6
19
  /** Docker image tag to pin this service to (e.g. "v1.2.3"). When set, `self-host upgrade` skips this service. */
@@ -27,12 +40,6 @@ export interface SelfHostConfig {
27
40
  * Pin specific services to fixed Docker image versions.
28
41
  * When a service is pinned, `self-host upgrade` will skip it.
29
42
  * Omit a service or set to `undefined` to allow automatic upgrades.
30
- *
31
- * @example
32
- * services: {
33
- * db: { version: "17-latest" },
34
- * postgrest: { version: "v12.2.8" },
35
- * }
36
43
  */
37
44
  services?: {
38
45
  db?: ServiceVersionPin
@@ -63,97 +70,136 @@ export interface AppConfig {
63
70
  headers?: Record<string, string>
64
71
  }
65
72
 
66
- export interface SupatypeConfig {
67
- /** Database connection string. */
68
- connection: string
69
- /**
70
- * Path (or glob) to the schema entry point.
71
- * Must export model definitions as named exports.
72
- * @example "./schema/index.ts"
73
- */
74
- schema: string
75
- output?: {
76
- /** Path for generated TypeScript types. */
77
- types?: string
78
- /** Path for generated client helpers. */
79
- client?: string
80
- }
81
- /** Self-hosted production deployment configuration. */
82
- selfHost?: SelfHostConfig
83
- /** Cloud project reference (set by `supatype link --project <ref>`). */
84
- projectRef?: string
85
- /** Cloud API URL override. */
86
- apiUrl?: string
87
- /** Cloud access token (prefer SUPATYPE_ACCESS_TOKEN env var). */
88
- accessToken?: string
89
- /** CORS configuration. */
90
- cors?: {
91
- /** Allowed origins. Defaults to ['*'] in development. */
92
- allowedOrigins?: string[]
93
- }
94
- /** Static site hosting configuration. */
95
- app?: AppConfig
96
- /** Registered plugins (provider, field, composite, widget). */
97
- plugins?: Array<unknown> | undefined
98
- /** Admin panel configuration (see Gap Appendices tasks 47–50). */
99
- admin?: {
100
- /**
101
- * Roles from {ref}_auth.users that grant admin panel access.
102
- * Checked against `app_metadata.role` in the project JWT.
103
- * @default ["admin"]
104
- */
105
- roles?: string[]
106
- }
107
- }
108
-
109
73
  /** Identity helper — provides type inference for config files. */
110
- export function defineConfig(config: SupatypeConfig): SupatypeConfig {
74
+ export function defineConfig(config: SupatypeProjectConfig): SupatypeProjectConfig {
111
75
  return config
112
76
  }
113
77
 
114
- const CONFIG_CANDIDATES = [
78
+ const LEGACY_TOML = "supatype.config.toml"
79
+
80
+ const MAIN_CONFIG_CANDIDATES = [
115
81
  "supatype.config.ts",
116
82
  "supatype.config.js",
117
83
  "supatype.config.mjs",
118
84
  ]
119
85
 
120
- /** Load and evaluate supatype.config.ts from the given directory. */
121
- export function loadConfig(cwd: string = process.cwd()): SupatypeConfig {
122
- for (const candidate of CONFIG_CANDIDATES) {
86
+ const LOCAL_CONFIG_CANDIDATES = [
87
+ "supatype.local.config.ts",
88
+ "supatype.local.config.js",
89
+ "supatype.local.config.mjs",
90
+ ]
91
+
92
+ /**
93
+ * Normalize a config object parsed from JSON (e.g. shorthand `schema: "./x"`).
94
+ */
95
+ function normalizeProjectJson(raw: unknown): unknown {
96
+ if (typeof raw !== "object" || raw === null) return raw
97
+ const o = { ...(raw as Record<string, unknown>) }
98
+ const sch = o["schema"]
99
+ if (typeof sch === "string") {
100
+ o["schema"] = {
101
+ path: sch,
102
+ pg_schema: typeof o["pg_schema"] === "string" ? o["pg_schema"] : "public",
103
+ }
104
+ delete o["pg_schema"]
105
+ }
106
+ return o
107
+ }
108
+
109
+ /**
110
+ * Load project config from `supatype.config.ts` (or .js/.mjs), merged with
111
+ * optional `supatype.local.config.*` (gitignored overrides).
112
+ */
113
+ export function loadConfig(cwd: string = process.cwd()): SupatypeProjectConfig {
114
+ if (existsSync(resolve(cwd, LEGACY_TOML))) {
115
+ throw new Error(
116
+ `Found ${LEGACY_TOML}, which is no longer supported.\n` +
117
+ "Move settings into supatype.config.ts (export default { project, database, server, app, versions, … }).\n" +
118
+ "Run `supatype init` in a new folder for a fresh template.",
119
+ )
120
+ }
121
+
122
+ const baseRaw = loadFirstTsConfig(cwd, MAIN_CONFIG_CANDIDATES)
123
+ if (baseRaw === null) {
124
+ throw new Error(
125
+ "No supatype.config.ts (or .js/.mjs) found in the current directory.\n" +
126
+ "Run: supatype init",
127
+ )
128
+ }
129
+
130
+ const baseNorm = normalizeProjectJson(baseRaw)
131
+ const base = validateProjectConfig(baseNorm, "supatype.config.ts")
132
+
133
+ const localRaw = loadFirstTsConfig(cwd, LOCAL_CONFIG_CANDIDATES)
134
+ if (localRaw === null) return base
135
+
136
+ const localNorm = normalizeProjectJson(localRaw) as Partial<SupatypeProjectConfig>
137
+ return mergeProjectConfig(base, localNorm)
138
+ }
139
+
140
+ function loadFirstTsConfig(
141
+ cwd: string,
142
+ candidates: string[],
143
+ ): Record<string, unknown> | null {
144
+ for (const candidate of candidates) {
123
145
  const configPath = resolve(cwd, candidate)
124
146
  if (!existsSync(configPath)) continue
125
147
 
126
148
  const urlPath = "file:///" + configPath.replace(/\\/g, "/")
127
-
128
- // Use dynamic import so we can always access .default —
129
- // files without a parent package.json are treated as CJS by tsx,
130
- // meaning export default becomes module.exports.default rather than
131
- // the namespace default. Dynamic import + fallback handles both.
132
149
  const snippet = `
133
150
  const mod = await import(${JSON.stringify(urlPath)})
134
151
  const config = mod.default ?? mod
135
152
  process.stdout.write(JSON.stringify(config))
136
153
  `
137
154
  const result = evalTsSnippet(snippet, { cwd })
138
- if (result.exitCode !== 0) {
139
- throw new Error(
140
- `Failed to load ${candidate}:\n${result.stderr || result.stdout}`,
141
- )
155
+ if (result.exitCode === 0) {
156
+ return JSON.parse(result.stdout) as Record<string, unknown>
142
157
  }
143
158
 
144
- const parsed = JSON.parse(result.stdout) as SupatypeConfig
145
- if (!parsed.connection || !parsed.schema) {
146
- throw new Error(
147
- `${candidate} must export { connection, schema } via defineConfig()`,
148
- )
159
+ const failure = result.stderr || result.stdout
160
+ if (!failure.includes("ERR_PACKAGE_PATH_NOT_EXPORTED")) {
161
+ throw new Error(`Failed to load ${candidate}:\n${failure}`)
149
162
  }
150
- return parsed
163
+
164
+ const fallback = loadTsConfigWithoutCliImport(configPath, cwd)
165
+ if (fallback !== null) return fallback
166
+ throw new Error(`Failed to load ${candidate}:\n${failure}`)
151
167
  }
168
+ return null
169
+ }
152
170
 
153
- throw new Error(
154
- "No supatype.config.ts found in the current directory.\n" +
155
- "Run: supatype init",
156
- )
171
+ function loadTsConfigWithoutCliImport(
172
+ configPath: string,
173
+ cwd: string,
174
+ ): Record<string, unknown> | null {
175
+ const original = readFileSync(configPath, "utf8")
176
+ const patched = original
177
+ .replace(/^import\s+type\s+\{[^}]*\}\s+from\s+["']@supatype\/cli["'];?\s*$/gm, "")
178
+ .replace(/^import\s+\{[^}]*defineConfig[^}]*\}\s+from\s+["']@supatype\/cli["'];?\s*$/gm, "")
179
+
180
+ // If the file didn't import from @supatype/cli, this fallback won't help.
181
+ if (patched === original) return null
182
+
183
+ const tmpPath = join(tmpdir(), `supatype-config-fallback-${Date.now()}.mts`)
184
+ const wrapper = `const defineConfig = (config) => config\n${patched}`
185
+ writeFileSync(tmpPath, wrapper, "utf8")
186
+ try {
187
+ const urlPath = "file:///" + tmpPath.replace(/\\/g, "/")
188
+ const snippet = `
189
+ const mod = await import(${JSON.stringify(urlPath)})
190
+ const config = mod.default ?? mod
191
+ process.stdout.write(JSON.stringify(config))
192
+ `
193
+ const result = evalTsSnippet(snippet, { cwd })
194
+ if (result.exitCode !== 0) return null
195
+ return JSON.parse(result.stdout) as Record<string, unknown>
196
+ } finally {
197
+ try {
198
+ unlinkSync(tmpPath)
199
+ } catch {
200
+ // ignore cleanup errors
201
+ }
202
+ }
157
203
  }
158
204
 
159
205
  /** Load schema AST by evaluating the user's schema entry point via tsx. */
@@ -161,30 +207,17 @@ export function loadSchemaAst(
161
207
  schemaPath: string,
162
208
  cwd: string = process.cwd(),
163
209
  ): unknown {
210
+ const extracted = extractSchemaAstFromTypes(schemaPath, cwd)
211
+ if (extracted !== null) return extracted
212
+
164
213
  const absPath = resolve(cwd, schemaPath)
165
214
  if (!existsSync(absPath)) {
166
215
  throw new Error(`Schema file not found: ${absPath}`)
167
216
  }
168
217
 
169
- const urlPath = "file:///" + absPath.replace(/\\/g, "/")
170
-
171
- const snippet = `
172
- import { serialiseSchema } from "@supatype/schema"
173
- const mod = await import(${JSON.stringify(urlPath)})
174
- const { default: _default, ...named } = mod
175
- const models = Object.fromEntries(
176
- Object.entries(named).filter(([, v]) =>
177
- v != null && typeof v === "object" && "__modelMeta" in (v as object)
218
+ throw new Error(
219
+ "Runtime model() schemas are no longer supported.\n" +
220
+ `Could not extract type-based models from: ${absPath}\n` +
221
+ "Migrate this file to @supatype/types Model<> definitions (or run `supatype migrate-from-v1`).",
178
222
  )
179
- )
180
- process.stdout.write(JSON.stringify(serialiseSchema(models)))
181
- `
182
- const result = evalTsSnippet(snippet, { cwd })
183
- if (result.exitCode !== 0) {
184
- throw new Error(
185
- `Failed to load schema from ${absPath}:\n${result.stderr || result.stdout}`,
186
- )
187
- }
188
-
189
- return JSON.parse(result.stdout)
190
223
  }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * docker-postgres — manage a supatype/postgres Docker container for local dev.
3
+ *
4
+ * Used by `supatype dev` when database.provider = "docker".
5
+ * The container is named supatype-{projectName} and persists data in a
6
+ * named Docker volume (supatype-{projectName}-data) across restarts.
7
+ */
8
+
9
+ import { spawnSync } from "node:child_process"
10
+
11
+ export interface DockerPgOptions {
12
+ /** Docker image to run. Defaults to supatype/postgres:17-latest. */
13
+ image: string
14
+ /** Project name — used to derive the container and volume names. */
15
+ projectName: string
16
+ /** Host port to bind to container's 5432. */
17
+ port: number
18
+ /** Superuser password (dev only). Defaults to "postgres". */
19
+ password?: string
20
+ }
21
+
22
+ const PG_USER = "supatype_admin"
23
+
24
+ /** Derived container name for a project. */
25
+ export function containerName(projectName: string): string {
26
+ return `supatype-${projectName}`
27
+ }
28
+
29
+ /**
30
+ * Start the supatype/postgres Docker container.
31
+ * Removes any stopped container with the same name before starting.
32
+ * Throws if `docker run` exits non-zero.
33
+ */
34
+ export function dockerPgStart(opts: DockerPgOptions): void {
35
+ const { image, projectName, port, password = "postgres" } = opts
36
+ const name = containerName(projectName)
37
+ const volume = `${name}-data`
38
+
39
+ // Remove any stopped container from a previous session.
40
+ spawnSync("docker", ["rm", "-f", name], { encoding: "utf8" })
41
+
42
+ const result = spawnSync(
43
+ "docker",
44
+ [
45
+ "run", "-d",
46
+ "--name", name,
47
+ "-e", `POSTGRES_USER=${PG_USER}`,
48
+ "-e", `POSTGRES_PASSWORD=${password}`,
49
+ "-e", `POSTGRES_DB=${projectName}`,
50
+ "-p", `${port}:5432`,
51
+ "-v", `${volume}:/var/lib/postgresql/data`,
52
+ image,
53
+ ],
54
+ { encoding: "utf8", stdio: "pipe" },
55
+ )
56
+
57
+ if (result.status !== 0) {
58
+ const detail = (result.stderr ?? result.stdout ?? "").trim()
59
+ throw new Error(
60
+ `Failed to start Docker container "${name}".\n` +
61
+ (detail ? ` docker: ${detail}\n` : "") +
62
+ ` Is Docker running? docker info`,
63
+ )
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Stop the container (fast — does not remove it or the data volume).
69
+ * Safe to call even if the container is not running.
70
+ */
71
+ export function dockerPgStop(projectName: string): void {
72
+ spawnSync("docker", ["stop", containerName(projectName)], { encoding: "utf8" })
73
+ }
74
+
75
+ /**
76
+ * Poll until Postgres accepts connections and the image entrypoint init has
77
+ * finished (anon/authenticated/service_role come from supatype-postgres
78
+ * migrations/db/init-scripts/00000000000000-initial-schema.sql via migrate.sh).
79
+ */
80
+ export async function dockerPgWaitReady(
81
+ projectName: string,
82
+ timeoutMs = 30_000,
83
+ ): Promise<void> {
84
+ const name = containerName(projectName)
85
+ const deadline = Date.now() + timeoutMs
86
+
87
+ while (Date.now() < deadline) {
88
+ const ready = spawnSync(
89
+ "docker",
90
+ ["exec", name, "pg_isready", "-U", PG_USER, "-q"],
91
+ { encoding: "utf8" },
92
+ )
93
+ if (ready.status === 0) {
94
+ const initDone = spawnSync(
95
+ "docker",
96
+ [
97
+ "exec", name,
98
+ "psql", "-U", PG_USER, "-d", projectName, "-tAc",
99
+ "SELECT EXISTS (SELECT FROM pg_roles WHERE rolname = 'anon')",
100
+ ],
101
+ { encoding: "utf8", stdio: "pipe" },
102
+ )
103
+ if (initDone.status === 0 && initDone.stdout?.trim() === "t") return
104
+ }
105
+ await sleep(300)
106
+ }
107
+
108
+ const logs = spawnSync("docker", ["logs", "--tail", "30", name], {
109
+ encoding: "utf8",
110
+ })
111
+ throw new Error(
112
+ `Docker Postgres "${name}" did not finish image init within ${timeoutMs}ms.\n` +
113
+ " API roles (anon, authenticated, service_role) are created by the supatype/postgres\n" +
114
+ " entrypoint (99-supatype-migrate.sh), not by the CLI.\n" +
115
+ " If you upgraded the image, remove the stale volume:\n" +
116
+ ` docker volume rm ${name}-data\n` +
117
+ (logs.stdout ? ` stdout:\n${indent(logs.stdout)}\n` : "") +
118
+ (logs.stderr ? ` stderr:\n${indent(logs.stderr)}\n` : ""),
119
+ )
120
+ }
121
+
122
+ /** Connection string for the Docker container (local dev credentials). */
123
+ export function dockerDbUrl(projectName: string, port: number): string {
124
+ // Local image has no TLS; sqlx/libpq default "prefer" can mis-handle the SSLRequest on some hosts.
125
+ return `postgres://${PG_USER}:postgres@127.0.0.1:${port}/${projectName}?sslmode=disable`
126
+ }
127
+
128
+ function sleep(ms: number): Promise<void> {
129
+ return new Promise((resolve) => setTimeout(resolve, ms))
130
+ }
131
+
132
+ function indent(s: string): string {
133
+ return s
134
+ .trimEnd()
135
+ .split("\n")
136
+ .map((l) => ` ${l}`)
137
+ .join("\n")
138
+ }