@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
@@ -59,12 +59,12 @@ describe("CDN URL construction", () => {
59
59
  const { getCdnUrl } = await import("../src/engine/platform.js")
60
60
 
61
61
  const url = getCdnUrl(
62
- "https://releases.supatype.io/engine",
62
+ "https://releases.supatype.com/engine",
63
63
  "0.1.0-alpha.1",
64
64
  "supatype-engine-0.1.0-alpha.1-linux-x64",
65
65
  )
66
66
  expect(url).toBe(
67
- "https://releases.supatype.io/engine/v0.1.0-alpha.1/supatype-engine-0.1.0-alpha.1-linux-x64",
67
+ "https://releases.supatype.com/engine/v0.1.0-alpha.1/supatype-engine-0.1.0-alpha.1-linux-x64",
68
68
  )
69
69
  })
70
70
  })
@@ -329,7 +329,7 @@ describe("Engine version constants", () => {
329
329
  } = await import("../src/engine-version.js")
330
330
 
331
331
  expect(ENGINE_VERSION).toMatch(/^\d+\.\d+\.\d+/)
332
- expect(CDN_BASE_URL).toBe("https://releases.supatype.io/engine")
332
+ expect(CDN_BASE_URL).toBe("https://releases.supatype.com/engine")
333
333
  expect(ENGINE_RELEASES_REPO).toBe("supatype/engine-releases")
334
334
  expect(GITHUB_RELEASES_FALLBACK_URL).toContain("github.com")
335
335
  })
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
3
+ import type { SupatypeProjectConfig } from "../src/project-config.js"
4
+ import * as binaryCache from "../src/binary-cache.js"
5
+ import { ensureBinary } from "../src/ensure-binary.js"
6
+
7
+ const TEST_VERSION = "9.9.9"
8
+
9
+ const config = (): SupatypeProjectConfig => ({
10
+ project: { name: "test" },
11
+ database: { provider: "native" },
12
+ server: { mode: "dev" },
13
+ app: { mode: "none" },
14
+ versions: {
15
+ engine: "0.4.2",
16
+ server: "0.1.0",
17
+ postgres: "17.2",
18
+ deno: TEST_VERSION,
19
+ },
20
+ })
21
+
22
+ beforeEach(() => {
23
+ const dir = binaryCache.cachePath("deno", TEST_VERSION)
24
+ if (existsSync(dir)) {
25
+ rmSync(dir, { recursive: true, force: true })
26
+ }
27
+ mkdirSync(dir, { recursive: true })
28
+ })
29
+
30
+ afterEach(() => {
31
+ vi.restoreAllMocks()
32
+ const dir = binaryCache.cachePath("deno", TEST_VERSION)
33
+ if (existsSync(dir)) {
34
+ rmSync(dir, { recursive: true, force: true })
35
+ }
36
+ })
37
+
38
+ describe("ensureBinary", () => {
39
+ it("returns cached path without calling download", async () => {
40
+ const platform = binaryCache.currentPlatform()
41
+ const path = binaryCache.cachedBinaryPath("deno", TEST_VERSION, platform)
42
+ writeFileSync(path, "stub", { mode: 0o755 })
43
+
44
+ const downloadSpy = vi.spyOn(binaryCache, "download")
45
+ const resolved = await ensureBinary("deno", config())
46
+ expect(resolved).toBe(path)
47
+ expect(downloadSpy).not.toHaveBeenCalled()
48
+ })
49
+
50
+ it("downloads on cache miss", async () => {
51
+ const platform = binaryCache.currentPlatform()
52
+ const expected = binaryCache.cachedBinaryPath("deno", TEST_VERSION, platform)
53
+ const downloadSpy = vi.spyOn(binaryCache, "download").mockResolvedValue(expected)
54
+
55
+ const path = await ensureBinary("deno", config())
56
+ expect(path).toBe(expected)
57
+ expect(downloadSpy).toHaveBeenCalledWith("deno", TEST_VERSION, platform)
58
+ })
59
+ })
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from "vitest"
2
- import { mkdirSync, rmSync, existsSync, readFileSync } from "node:fs"
2
+ import { mkdirSync, rmSync, existsSync, readFileSync, writeFileSync } from "node:fs"
3
3
  import { join } from "node:path"
4
4
  import { tmpdir } from "node:os"
5
5
  import { scaffold } from "../src/commands/init.js"
@@ -20,14 +20,13 @@ describe("scaffold()", () => {
20
20
  scaffold(tmpRoot, "my-app")
21
21
 
22
22
  const expected = [
23
+ "package.json",
23
24
  "supatype.config.ts",
24
25
  "schema/index.ts",
25
26
  ".env",
26
- "docker-compose.yml",
27
- ".supatype/kong.yml",
28
- ".supatype/pgbouncer.ini",
29
- ".supatype/userlist.txt",
30
27
  "seed.ts",
28
+ "seeds/.gitkeep",
29
+ "public/.gitkeep",
31
30
  ".gitignore",
32
31
  ]
33
32
  for (const rel of expected) {
@@ -40,77 +39,38 @@ describe("scaffold()", () => {
40
39
  const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
41
40
  expect(content).toContain("blog-app")
42
41
  expect(content).toContain("defineConfig")
42
+ expect(content).toContain('provider: "native"')
43
43
  expect(content).toContain("schema:")
44
- expect(content).toContain("output:")
44
+ expect(content).toContain("versions:")
45
45
  })
46
46
 
47
- it("supatype.config.ts contains commented selfHost section", () => {
48
- scaffold(tmpRoot, "my-app")
49
- const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
50
- expect(content).toContain("selfHost")
51
- expect(content).toContain("domain")
52
- })
53
-
54
- it("docker-compose.yml references project name, correct images, and health check", () => {
55
- scaffold(tmpRoot, "shop")
56
- const content = readFileSync(join(tmpRoot, "docker-compose.yml"), "utf8")
57
- expect(content).toContain("shop")
58
- expect(content).toContain("supatype/postgres")
59
- expect(content).toContain("postgrest/postgrest")
60
- expect(content).toContain("kong:")
61
- expect(content).toContain("service_healthy")
62
- })
63
-
64
- it("docker-compose.yml includes GoTrue auth service", () => {
65
- scaffold(tmpRoot, "shop")
66
- const content = readFileSync(join(tmpRoot, "docker-compose.yml"), "utf8")
67
- expect(content).toContain("gotrue:")
68
- expect(content).toContain("supatype/auth")
69
- expect(content).toContain("GOTRUE_JWT_SECRET")
70
- expect(content).toContain("9999")
71
- })
72
-
73
- it("docker-compose.yml includes PgBouncer service connecting services via port 6432", () => {
74
- scaffold(tmpRoot, "shop")
75
- const content = readFileSync(join(tmpRoot, "docker-compose.yml"), "utf8")
76
- expect(content).toContain("pgbouncer:")
77
- expect(content).toContain("pgbouncer/pgbouncer")
78
- expect(content).toContain("pgbouncer:6432")
79
- expect(content).toContain("PGRST_DB_POOL")
80
- })
81
-
82
- it("docker-compose.yml includes studio service (merged admin + studio)", () => {
83
- scaffold(tmpRoot, "shop")
84
- const content = readFileSync(join(tmpRoot, "docker-compose.yml"), "utf8")
85
- expect(content).toContain("studio:")
86
- expect(content).toContain("ghcr.io/supatype/studio")
87
- expect(content).toContain("3002:3002")
88
- // Admin was merged into studio — no separate admin service
89
- expect(content).not.toContain("ghcr.io/supatype/admin")
47
+ it("package.json includes @supatype/cli and @supatype/types", () => {
48
+ scaffold(tmpRoot, "pkg-app")
49
+ const content = readFileSync(join(tmpRoot, "package.json"), "utf8")
50
+ expect(content).toContain("@supatype/cli")
51
+ expect(content).toContain("@supatype/types")
52
+ expect(content).toContain("pkg-app")
90
53
  })
91
54
 
92
- it("docker-compose.yml includes commented app service slot", () => {
93
- scaffold(tmpRoot, "shop")
94
- const content = readFileSync(join(tmpRoot, "docker-compose.yml"), "utf8")
95
- expect(content).toContain("supatype app add")
96
- expect(content).toContain("SUPATYPE_URL")
97
- expect(content).toContain("SUPATYPE_ANON_KEY")
55
+ it("skips package.json when it already exists", () => {
56
+ const pkgPath = join(tmpRoot, "package.json")
57
+ writeFileSync(pkgPath, '{"name":"existing"}', "utf8")
58
+ scaffold(tmpRoot, "my-app")
59
+ expect(readFileSync(pkgPath, "utf8")).toBe('{"name":"existing"}')
98
60
  })
99
61
 
100
- it(".supatype/pgbouncer.ini has correct pool settings", () => {
62
+ it("supatype.config.ts documents self-host workflow", () => {
101
63
  scaffold(tmpRoot, "my-app")
102
- const content = readFileSync(join(tmpRoot, ".supatype/pgbouncer.ini"), "utf8")
103
- expect(content).toContain("pool_mode = transaction")
104
- expect(content).toContain("default_pool_size = 20")
105
- expect(content).toContain("max_db_connections = 60")
106
- expect(content).toContain("listen_port = 6432")
64
+ const content = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
65
+ expect(content).toContain("self-host")
107
66
  })
108
67
 
109
- it(".env contains DATABASE_URL, JWT_SECRET, POSTGRES_PASSWORD, POSTGRES_DB", () => {
68
+ it(".env contains DATABASE_URL, JWT_SECRET, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB", () => {
110
69
  scaffold(tmpRoot, "my-app")
111
70
  const content = readFileSync(join(tmpRoot, ".env"), "utf8")
112
71
  expect(content).toContain("DATABASE_URL=")
113
72
  expect(content).toContain("JWT_SECRET=")
73
+ expect(content).toContain("POSTGRES_USER=")
114
74
  expect(content).toContain("POSTGRES_PASSWORD=")
115
75
  expect(content).toContain("POSTGRES_DB=")
116
76
  })
@@ -123,31 +83,12 @@ describe("scaffold()", () => {
123
83
  expect(content).toContain("SITE_URL=")
124
84
  })
125
85
 
126
- it("schema/index.ts exports a User model with field builders and access rules", () => {
86
+ it("schema/index.ts exports a User model using RFC v2 Model<>", () => {
127
87
  scaffold(tmpRoot, "my-app")
128
88
  const content = readFileSync(join(tmpRoot, "schema/index.ts"), "utf8")
129
- expect(content).toContain("export const User")
130
- expect(content).toContain("model(")
131
- expect(content).toContain("field.")
132
- expect(content).toContain("access.")
133
- expect(content).toContain("options: { timestamps: true }")
134
- })
135
-
136
- it(".supatype/kong.yml declares REST, GraphQL, and auth routes", () => {
137
- scaffold(tmpRoot, "my-app")
138
- const content = readFileSync(join(tmpRoot, ".supatype/kong.yml"), "utf8")
139
- expect(content).toContain("/rest/v1/")
140
- expect(content).toContain("/graphql/v1")
141
- expect(content).toContain("/auth/v1/")
142
- expect(content).toContain("postgrest")
143
- expect(content).toContain("gotrue")
144
- })
145
-
146
- it(".supatype/kong.yml contains commented app fallback route", () => {
147
- scaffold(tmpRoot, "my-app")
148
- const content = readFileSync(join(tmpRoot, ".supatype/kong.yml"), "utf8")
149
- expect(content).toContain("supatype app add")
150
- expect(content).toContain("app-root")
89
+ expect(content).toContain("export type User")
90
+ expect(content).toContain("Model<")
91
+ expect(content).toContain("access:")
151
92
  })
152
93
 
153
94
  it(".gitignore excludes .env, node_modules, and engine binary", () => {
@@ -156,6 +97,7 @@ describe("scaffold()", () => {
156
97
  expect(content).toContain(".env")
157
98
  expect(content).toContain("node_modules/")
158
99
  expect(content).toContain(".supatype/engine/")
100
+ expect(content).toContain("supatype.local.config.ts")
159
101
  })
160
102
 
161
103
  it("seed.ts references the project name", () => {
@@ -164,7 +106,7 @@ describe("scaffold()", () => {
164
106
  expect(content).toContain("acme")
165
107
  })
166
108
 
167
- it("different project names produce different connection strings", () => {
109
+ it("different project names produce different config bodies", () => {
168
110
  scaffold(tmpRoot, "alpha")
169
111
  const alpha = readFileSync(join(tmpRoot, "supatype.config.ts"), "utf8")
170
112
 
@@ -0,0 +1,29 @@
1
+ import ts from "typescript"
2
+ import { describe, expect, it } from "vitest"
3
+ import { codemodSource } from "../src/commands/migrate-from-v1.js"
4
+
5
+ describe("migrate-from-v1 codemod", () => {
6
+ it("converts exported model() declarations into Model<> aliases", () => {
7
+ const source = ts.createSourceFile(
8
+ "schema.ts",
9
+ `
10
+ import { model, field } from "@supatype/schema"
11
+ export const User = model("user", {
12
+ fields: {
13
+ id: field.uuid({ required: true }),
14
+ email: field.email({ required: true, unique: true }),
15
+ name: field.text({ required: true }),
16
+ },
17
+ })
18
+ `,
19
+ ts.ScriptTarget.Latest,
20
+ true,
21
+ ts.ScriptKind.TS,
22
+ )
23
+
24
+ const out = codemodSource(source)
25
+ expect(out).toContain("export type User = Model<")
26
+ expect(out).toContain("id: UUID")
27
+ expect(out).toContain("email: string")
28
+ })
29
+ })
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { join } from "node:path"
3
+ import { pgSpawnEnv } from "../src/postgres-ctl.js"
4
+
5
+ const PG_BIN = "/cache/postgres/17.2/pg-17.2/bin"
6
+ const PG_LIB = join("/cache/postgres/17.2/pg-17.2", "lib")
7
+
8
+ describe("pgSpawnEnv", () => {
9
+ it("prepends lib dir to DYLD_LIBRARY_PATH on darwin", () => {
10
+ const env = pgSpawnEnv(PG_BIN, "darwin")
11
+ expect(env.DYLD_LIBRARY_PATH).toContain(PG_LIB)
12
+ })
13
+
14
+ it("prepends lib dir to LD_LIBRARY_PATH on linux", () => {
15
+ const env = pgSpawnEnv(PG_BIN, "linux")
16
+ expect(env.LD_LIBRARY_PATH).toContain(PG_LIB)
17
+ })
18
+ })
@@ -0,0 +1,9 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { postgresArchiveTag } from "../src/binary-cache.js"
3
+
4
+ describe("postgresArchiveTag", () => {
5
+ it("uses PG major only for CDN archive basenames", () => {
6
+ expect(postgresArchiveTag("17.2")).toBe("17")
7
+ expect(postgresArchiveTag("17")).toBe("17")
8
+ })
9
+ })
@@ -1,5 +1,10 @@
1
1
  import { describe, it, expect } from "vitest"
2
- import { pgTypeToField, toCamelCase, type ColumnInfo } from "../src/pull-utils.js"
2
+ import {
3
+ introspectColumnToColumnInfo,
4
+ pgTypeToField,
5
+ toCamelCase,
6
+ type ColumnInfo,
7
+ } from "../src/pull-utils.js"
3
8
 
4
9
  function col(overrides: Partial<ColumnInfo> & { pgType: string }): ColumnInfo {
5
10
  return {
@@ -100,6 +105,36 @@ describe("pgTypeToField()", () => {
100
105
  })
101
106
  })
102
107
 
108
+ describe("introspectColumnToColumnInfo()", () => {
109
+ it("maps engine type → pgType and flags", () => {
110
+ const out = introspectColumnToColumnInfo({
111
+ name: "id",
112
+ type: "uuid",
113
+ nullable: false,
114
+ primaryKey: true,
115
+ default: "gen_random_uuid()",
116
+ })
117
+ expect(out).toEqual({
118
+ name: "id",
119
+ pgType: "uuid",
120
+ nullable: false,
121
+ isPrimary: true,
122
+ isUnique: false,
123
+ hasDefault: true,
124
+ })
125
+ })
126
+
127
+ it("treats empty default as no default", () => {
128
+ const out = introspectColumnToColumnInfo({
129
+ name: "x",
130
+ type: "text",
131
+ nullable: true,
132
+ default: "",
133
+ })
134
+ expect(out.hasDefault).toBe(false)
135
+ })
136
+ })
137
+
103
138
  describe("toCamelCase()", () => {
104
139
  it.each([
105
140
  ["user", "User"],
@@ -0,0 +1,28 @@
1
+ import { readFileSync } from "node:fs"
2
+ import { join, resolve } from "node:path"
3
+ import { describe, expect, it } from "vitest"
4
+ import { DENO_RELEASE_PIN } from "../src/release-pins.js"
5
+
6
+ const CLI_ROOT = resolve(import.meta.dirname, "..")
7
+ const REPO_ROOT = resolve(CLI_ROOT, "../..")
8
+
9
+ const EXAMPLE_CONFIGS = [
10
+ "examples/blog/supatype.config.ts",
11
+ "examples/self-host/supatype.config.ts",
12
+ "tests/integration/supatype.config.ts",
13
+ ]
14
+
15
+ describe("release-pins", () => {
16
+ it("DENO_RELEASE_PIN matches releases/deno/VERSION", () => {
17
+ const file = join(CLI_ROOT, "releases", "deno", "VERSION")
18
+ expect(DENO_RELEASE_PIN).toBe(readFileSync(file, "utf8").trim())
19
+ })
20
+
21
+ it("example configs use the same deno pin", () => {
22
+ const needle = `deno: "${DENO_RELEASE_PIN}"`
23
+ for (const rel of EXAMPLE_CONFIGS) {
24
+ const content = readFileSync(join(REPO_ROOT, rel), "utf8")
25
+ expect(content, rel).toContain(needle)
26
+ }
27
+ })
28
+ })
@@ -0,0 +1,236 @@
1
+ import { describe, expect, it } from "vitest"
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { runtimeRouteSpec } from "../src/runtime-routes.js"
6
+ import { buildKongDeclarative } from "../src/kong-config.js"
7
+ import { renderSelfHostCompose, writeSelfHostCompose } from "../src/self-host-compose.js"
8
+ import { updateAppConfigInProject } from "../src/app-config.js"
9
+ import type { SupatypeProjectConfig } from "../src/project-config.js"
10
+ import { DENO_RELEASE_PIN } from "../src/release-pins.js"
11
+
12
+ const baseConfig: SupatypeProjectConfig = {
13
+ project: { name: "acme" },
14
+ database: { provider: "docker" },
15
+ server: { mode: "dev" },
16
+ app: { mode: "none" },
17
+ versions: {
18
+ engine: "0.4.2",
19
+ server: "0.1.0",
20
+ postgres: "17.2",
21
+ deno: DENO_RELEASE_PIN,
22
+ },
23
+ }
24
+
25
+ describe("runtime contract", () => {
26
+ it("includes core route families", () => {
27
+ const paths = runtimeRouteSpec().flatMap((r) => r.paths)
28
+ for (const path of ["/rest/v1/", "/auth/v1/", "/storage/v1/", "/realtime/v1/", "/functions/v1/"]) {
29
+ expect(paths).toContain(path)
30
+ }
31
+ })
32
+
33
+ it("renders app root route when upstream is provided", () => {
34
+ const paths = runtimeRouteSpec({ appUpstream: "http://app:3000" }).flatMap((r) => r.paths)
35
+ expect(paths).toContain("/")
36
+ })
37
+
38
+ it("renders app root route for static service URL", () => {
39
+ const paths = runtimeRouteSpec({ staticAppServiceUrl: "http://static-app:8080" }).flatMap((r) => r.paths)
40
+ expect(paths).toContain("/")
41
+ })
42
+
43
+ it("self-host kong uses unified supatype-server gateway", () => {
44
+ const kong = buildKongDeclarative({ unifiedGateway: true })
45
+ expect(kong).toContain("http://server:9999")
46
+ expect(kong).toContain("/rest/v1/")
47
+ expect(kong).toContain("/auth/v1/")
48
+ expect(kong).toContain("/storage/v1/")
49
+ expect(kong).toContain("/realtime/v1/")
50
+ expect(kong).toContain("/functions/v1/")
51
+ expect(kong).not.toContain("http://postgrest:3000")
52
+ expect(kong).not.toContain("http://storage:5000")
53
+ })
54
+
55
+ it("kong declarative output contains route contract paths", () => {
56
+ const kong = buildKongDeclarative({ appUpstream: "http://app:3000" })
57
+ expect(kong).toContain("/rest/v1/")
58
+ expect(kong).toContain("/auth/v1/")
59
+ expect(kong).toContain("/storage/v1/")
60
+ expect(kong).toContain("/realtime/v1/")
61
+ expect(kong).toContain("/functions/v1/")
62
+ expect(kong).toContain("- /")
63
+ })
64
+
65
+ it("self-host compose render is deterministic for same config", () => {
66
+ const first = renderSelfHostCompose({ ...baseConfig, app: { mode: "proxy", upstream: "http://app:3000" } })
67
+ const second = renderSelfHostCompose({ ...baseConfig, app: { mode: "proxy", upstream: "http://app:3000" } })
68
+ expect(first).toBe(second)
69
+ })
70
+
71
+ it("self-host compose does not inject a synthetic app-proxy service", () => {
72
+ const compose = renderSelfHostCompose({ ...baseConfig, app: { mode: "proxy", upstream: "http://app:3000" } })
73
+ expect(compose).not.toContain("ghcr.io/supatype/app-proxy")
74
+ expect(compose).not.toContain("\n app:\n")
75
+ })
76
+
77
+ it("self-host compose configures static app on supatype-server", () => {
78
+ const compose = renderSelfHostCompose({ ...baseConfig, app: { mode: "static", static_dir: "./public" } })
79
+ expect(compose).toContain('SUPATYPE_APP_MODE: static')
80
+ expect(compose).toContain("SUPATYPE_APP_STATIC_DIR: /project/public")
81
+ expect(compose).not.toContain("static-app:")
82
+ })
83
+
84
+ it("self-host compose runs per-project functions-worker and proxies via server", () => {
85
+ const compose = renderSelfHostCompose(baseConfig)
86
+ expect(compose).toContain("\n functions-worker:\n")
87
+ expect(compose).toContain("SUPATYPE_FUNCTIONS_WORKER_URL: http://functions-worker:8001")
88
+ expect(compose).toContain("SUPATYPE_FUNCTIONS_ROOT: /project/functions")
89
+ expect(compose).not.toContain("deploy/functions")
90
+ expect(compose).not.toContain("supatype-functions")
91
+ })
92
+
93
+ it("app config updater writes proxy mode intent", () => {
94
+ const dir = mkdtempSync(join(tmpdir(), "supatype-app-config-"))
95
+ try {
96
+ const configPath = join(dir, "supatype.config.ts")
97
+ writeFileSync(
98
+ configPath,
99
+ `export default {
100
+ project: { name: "x" },
101
+ database: { provider: "docker" },
102
+ server: { mode: "dev" },
103
+ app: {
104
+ mode: "none",
105
+ // mode: "static", static_dir: "./dist",
106
+ // mode: "proxy", upstream: "http://localhost:3000",
107
+ },
108
+ versions: { engine: "0", server: "0", postgres: "0", deno: "0" },
109
+ }
110
+ `,
111
+ "utf8",
112
+ )
113
+ updateAppConfigInProject(dir, { mode: "proxy", upstream: "http://localhost:7777" })
114
+ const next = readFileSync(configPath, "utf8")
115
+ expect(next).toContain(`mode: "proxy"`)
116
+ expect(next).toContain(`upstream: "http://localhost:7777"`)
117
+ } finally {
118
+ rmSync(dir, { recursive: true, force: true })
119
+ }
120
+ })
121
+
122
+ it("app config updater handles defineConfig wrappers", () => {
123
+ const dir = mkdtempSync(join(tmpdir(), "supatype-app-config-"))
124
+ try {
125
+ const configPath = join(dir, "supatype.config.ts")
126
+ writeFileSync(
127
+ configPath,
128
+ `import { defineConfig } from "@supatype/cli"
129
+
130
+ export default defineConfig({
131
+ project: { name: "x" },
132
+ database: { provider: "docker" },
133
+ server: { mode: "dev" },
134
+ app: { mode: "none" },
135
+ versions: { engine: "0", server: "0", postgres: "0", deno: "0" },
136
+ })
137
+ `,
138
+ "utf8",
139
+ )
140
+ updateAppConfigInProject(dir, { mode: "static", staticDir: "./site" })
141
+ const next = readFileSync(configPath, "utf8")
142
+ expect(next).toContain(`mode: "static"`)
143
+ expect(next).toContain(`static_dir: "./site"`)
144
+ } finally {
145
+ rmSync(dir, { recursive: true, force: true })
146
+ }
147
+ })
148
+
149
+ it("app config updater inserts app block when missing", () => {
150
+ const dir = mkdtempSync(join(tmpdir(), "supatype-app-config-"))
151
+ try {
152
+ const configPath = join(dir, "supatype.config.ts")
153
+ writeFileSync(
154
+ configPath,
155
+ `export default {
156
+ project: { name: "x" },
157
+ database: { provider: "docker" },
158
+ server: { mode: "dev" },
159
+ versions: { engine: "0", server: "0", postgres: "0", deno: "0" },
160
+ }
161
+ `,
162
+ "utf8",
163
+ )
164
+ updateAppConfigInProject(dir, { mode: "none" })
165
+ const next = readFileSync(configPath, "utf8")
166
+ expect(next).toContain("app:")
167
+ expect(next).toContain(`mode: "none"`)
168
+ } finally {
169
+ rmSync(dir, { recursive: true, force: true })
170
+ }
171
+ })
172
+
173
+ it("app config updater preserves unrelated app keys", () => {
174
+ const dir = mkdtempSync(join(tmpdir(), "supatype-app-config-"))
175
+ try {
176
+ const configPath = join(dir, "supatype.config.ts")
177
+ writeFileSync(
178
+ configPath,
179
+ `export default {
180
+ project: { name: "x" },
181
+ database: { provider: "docker" },
182
+ server: { mode: "dev" },
183
+ app: {
184
+ mode: "proxy",
185
+ upstream: "http://localhost:3000",
186
+ headers: { "x-feature": "on" },
187
+ },
188
+ versions: { engine: "0", server: "0", postgres: "0", deno: "0" },
189
+ }
190
+ `,
191
+ "utf8",
192
+ )
193
+ updateAppConfigInProject(dir, { mode: "proxy", upstream: "http://localhost:8080" })
194
+ const next = readFileSync(configPath, "utf8")
195
+ expect(next).toContain(`upstream: "http://localhost:8080"`)
196
+ expect(next).toContain(`headers`)
197
+ expect(next).toContain(`"x-feature"`)
198
+ } finally {
199
+ rmSync(dir, { recursive: true, force: true })
200
+ }
201
+ })
202
+
203
+ it("writes self-host compose artifacts under .supatype/self-host", () => {
204
+ const dir = mkdtempSync(join(tmpdir(), "supatype-compose-"))
205
+ try {
206
+ const out = writeSelfHostCompose(dir, { ...baseConfig, app: { mode: "none" } })
207
+ expect(out.composePath).toContain(".supatype")
208
+ expect(readFileSync(out.composePath, "utf8")).toContain("services:")
209
+ expect(readFileSync(out.kongPath, "utf8")).toContain("/rest/v1/")
210
+ const compose = readFileSync(out.composePath, "utf8")
211
+ expect(compose).toContain("${SUPATYPE_POSTGRES_IMAGE:-supatype/postgres:17-latest}")
212
+ expect(compose).toContain("${SUPATYPE_SERVER_IMAGE:-${SUPATYPE_AUTH_IMAGE:-supatype/server:latest}}")
213
+ expect(compose).toContain("${SUPATYPE_STORAGE_IMAGE:-supatype/storage:latest}")
214
+ expect(compose).toContain("${SUPATYPE_STUDIO_IMAGE:-supatype/studio:latest}")
215
+ expect(compose).toContain("SUPATYPE_POSTGREST_URL: http://postgrest:3000")
216
+ expect(compose).toContain("unified gateway")
217
+ const kong = readFileSync(out.kongPath, "utf8")
218
+ expect(kong).toContain("http://server:9999")
219
+ expect(kong).not.toContain("http://postgrest:3000")
220
+ } finally {
221
+ rmSync(dir, { recursive: true, force: true })
222
+ }
223
+ })
224
+
225
+ it("writes default manifest when missing for compose", () => {
226
+ const dir = mkdtempSync(join(tmpdir(), "supatype-manifest-"))
227
+ try {
228
+ writeSelfHostCompose(dir, { ...baseConfig, app: { mode: "none" } })
229
+ const manifest = readFileSync(join(dir, ".supatype", "manifest.json"), "utf8")
230
+ expect(manifest).toContain("postgrest_url")
231
+ expect(manifest).toContain("http://postgrest:3000")
232
+ } finally {
233
+ rmSync(dir, { recursive: true, force: true })
234
+ }
235
+ })
236
+ })
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest"
2
+ import { mkdirSync, writeFileSync, rmSync } from "node:fs"
3
+ import { join } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { discoverSeedsDir } from "../src/commands/seed.js"
6
+
7
+ let tmp: string
8
+ beforeEach(() => {
9
+ tmp = join(tmpdir(), `supatype-seed-${Date.now()}`)
10
+ mkdirSync(join(tmp, "seeds"), { recursive: true })
11
+ })
12
+ afterEach(() => {
13
+ rmSync(tmp, { recursive: true, force: true })
14
+ })
15
+
16
+ describe("discoverSeedsDir", () => {
17
+ it("returns sorted paths for seeds/*.ts", () => {
18
+ writeFileSync(join(tmp, "seeds", "z_first.ts"), "//")
19
+ writeFileSync(join(tmp, "seeds", "a_second.ts"), "//")
20
+ const paths = discoverSeedsDir(tmp, join(tmp, "seeds"))
21
+ expect(paths.map((p) => p.replace(/\\/g, "/"))).toEqual([
22
+ join(tmp, "seeds", "a_second.ts").replace(/\\/g, "/"),
23
+ join(tmp, "seeds", "z_first.ts").replace(/\\/g, "/"),
24
+ ])
25
+ })
26
+
27
+ it("returns empty when seeds dir missing", () => {
28
+ const empty = join(tmp, "no-seeds")
29
+ expect(discoverSeedsDir(tmp, empty)).toEqual([])
30
+ })
31
+ })
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "composite": false,
6
+ "rootDir": ".."
7
+ },
8
+ "include": ["./**/*.ts"]
9
+ }