@supatype/cli 0.1.0-alpha.10 → 0.1.0-alpha.12

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 (188) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +98 -65
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/dist/app/framework.js +1 -3
  5. package/dist/app/framework.js.map +1 -1
  6. package/dist/app/proxy-dev-app.d.ts +14 -0
  7. package/dist/app/proxy-dev-app.d.ts.map +1 -1
  8. package/dist/app/proxy-dev-app.js +109 -6
  9. package/dist/app/proxy-dev-app.js.map +1 -1
  10. package/dist/binary-cache.d.ts +1 -1
  11. package/dist/binary-cache.d.ts.map +1 -1
  12. package/dist/binary-cache.js +6 -1
  13. package/dist/binary-cache.js.map +1 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +6 -0
  16. package/dist/cli.js.map +1 -1
  17. package/dist/commands/adopt.d.ts +3 -0
  18. package/dist/commands/adopt.d.ts.map +1 -0
  19. package/dist/commands/adopt.js +58 -0
  20. package/dist/commands/adopt.js.map +1 -0
  21. package/dist/commands/cloud.d.ts +4 -9
  22. package/dist/commands/cloud.d.ts.map +1 -1
  23. package/dist/commands/cloud.js +49 -91
  24. package/dist/commands/cloud.js.map +1 -1
  25. package/dist/commands/db.d.ts.map +1 -1
  26. package/dist/commands/db.js +25 -47
  27. package/dist/commands/db.js.map +1 -1
  28. package/dist/commands/deploy.d.ts.map +1 -1
  29. package/dist/commands/deploy.js +117 -74
  30. package/dist/commands/deploy.js.map +1 -1
  31. package/dist/commands/dev.d.ts.map +1 -1
  32. package/dist/commands/dev.js +21 -3
  33. package/dist/commands/dev.js.map +1 -1
  34. package/dist/commands/diff.d.ts.map +1 -1
  35. package/dist/commands/diff.js +37 -37
  36. package/dist/commands/diff.js.map +1 -1
  37. package/dist/commands/doctor.d.ts +3 -0
  38. package/dist/commands/doctor.d.ts.map +1 -0
  39. package/dist/commands/doctor.js +77 -0
  40. package/dist/commands/doctor.js.map +1 -0
  41. package/dist/commands/functions.d.ts.map +1 -1
  42. package/dist/commands/functions.js +80 -33
  43. package/dist/commands/functions.js.map +1 -1
  44. package/dist/commands/init.d.ts +1 -0
  45. package/dist/commands/init.d.ts.map +1 -1
  46. package/dist/commands/init.js +26 -4
  47. package/dist/commands/init.js.map +1 -1
  48. package/dist/commands/introspect.d.ts +3 -0
  49. package/dist/commands/introspect.d.ts.map +1 -0
  50. package/dist/commands/introspect.js +34 -0
  51. package/dist/commands/introspect.js.map +1 -0
  52. package/dist/commands/link-helpers.d.ts +15 -0
  53. package/dist/commands/link-helpers.d.ts.map +1 -0
  54. package/dist/commands/link-helpers.js +187 -0
  55. package/dist/commands/link-helpers.js.map +1 -0
  56. package/dist/commands/migrate.d.ts.map +1 -1
  57. package/dist/commands/migrate.js +116 -14
  58. package/dist/commands/migrate.js.map +1 -1
  59. package/dist/commands/pull.d.ts.map +1 -1
  60. package/dist/commands/pull.js +32 -5
  61. package/dist/commands/pull.js.map +1 -1
  62. package/dist/commands/push.d.ts.map +1 -1
  63. package/dist/commands/push.js +102 -129
  64. package/dist/commands/push.js.map +1 -1
  65. package/dist/commands/status.d.ts +1 -1
  66. package/dist/commands/status.d.ts.map +1 -1
  67. package/dist/commands/status.js +93 -29
  68. package/dist/commands/status.js.map +1 -1
  69. package/dist/commands/update.d.ts.map +1 -1
  70. package/dist/commands/update.js +6 -2
  71. package/dist/commands/update.js.map +1 -1
  72. package/dist/config.d.ts +2 -1
  73. package/dist/config.d.ts.map +1 -1
  74. package/dist/config.js.map +1 -1
  75. package/dist/dev-compose.d.ts +23 -0
  76. package/dist/dev-compose.d.ts.map +1 -1
  77. package/dist/dev-compose.js +183 -6
  78. package/dist/dev-compose.js.map +1 -1
  79. package/dist/diff-output.d.ts +5 -1
  80. package/dist/diff-output.d.ts.map +1 -1
  81. package/dist/diff-output.js +69 -0
  82. package/dist/diff-output.js.map +1 -1
  83. package/dist/engine-client.d.ts +10 -1
  84. package/dist/engine-client.d.ts.map +1 -1
  85. package/dist/engine-client.js +64 -13
  86. package/dist/engine-client.js.map +1 -1
  87. package/dist/engine-push-output.d.ts +1 -0
  88. package/dist/engine-push-output.d.ts.map +1 -1
  89. package/dist/engine-push-output.js +4 -1
  90. package/dist/engine-push-output.js.map +1 -1
  91. package/dist/gitignore.d.ts +8 -0
  92. package/dist/gitignore.d.ts.map +1 -0
  93. package/dist/gitignore.js +41 -0
  94. package/dist/gitignore.js.map +1 -0
  95. package/dist/link.d.ts +66 -0
  96. package/dist/link.d.ts.map +1 -0
  97. package/dist/link.js +159 -0
  98. package/dist/link.js.map +1 -0
  99. package/dist/process-manager.d.ts +2 -0
  100. package/dist/process-manager.d.ts.map +1 -1
  101. package/dist/process-manager.js +2 -0
  102. package/dist/process-manager.js.map +1 -1
  103. package/dist/project-config.d.ts +8 -0
  104. package/dist/project-config.d.ts.map +1 -1
  105. package/dist/project-config.js.map +1 -1
  106. package/dist/pull-utils.d.ts +50 -14
  107. package/dist/pull-utils.d.ts.map +1 -1
  108. package/dist/pull-utils.js +152 -12
  109. package/dist/pull-utils.js.map +1 -1
  110. package/dist/resolve-target.d.ts +86 -0
  111. package/dist/resolve-target.d.ts.map +1 -0
  112. package/dist/resolve-target.js +291 -0
  113. package/dist/resolve-target.js.map +1 -0
  114. package/dist/runtime-routes.d.ts.map +1 -1
  115. package/dist/runtime-routes.js +7 -0
  116. package/dist/runtime-routes.js.map +1 -1
  117. package/dist/schema-ast-v2.d.ts +1 -1
  118. package/dist/schema-ast-v2.d.ts.map +1 -1
  119. package/dist/schema-ast-v2.js +2 -2
  120. package/dist/schema-ast-v2.js.map +1 -1
  121. package/dist/schema-sources.d.ts +40 -0
  122. package/dist/schema-sources.d.ts.map +1 -0
  123. package/dist/schema-sources.js +183 -0
  124. package/dist/schema-sources.js.map +1 -0
  125. package/dist/self-host-compose.d.ts +10 -0
  126. package/dist/self-host-compose.d.ts.map +1 -1
  127. package/dist/self-host-compose.js +85 -3
  128. package/dist/self-host-compose.js.map +1 -1
  129. package/dist/storage-provision.d.ts +4 -0
  130. package/dist/storage-provision.d.ts.map +1 -1
  131. package/dist/storage-provision.js +24 -2
  132. package/dist/storage-provision.js.map +1 -1
  133. package/dist/target-client.d.ts +10 -0
  134. package/dist/target-client.d.ts.map +1 -0
  135. package/dist/target-client.js +22 -0
  136. package/dist/target-client.js.map +1 -0
  137. package/dist/type-extractor.d.ts +11 -0
  138. package/dist/type-extractor.d.ts.map +1 -1
  139. package/dist/type-extractor.js +95 -8
  140. package/dist/type-extractor.js.map +1 -1
  141. package/package.json +1 -1
  142. package/src/app/framework.ts +1 -3
  143. package/src/app/proxy-dev-app.ts +113 -6
  144. package/src/binary-cache.ts +6 -1
  145. package/src/cli.ts +6 -0
  146. package/src/commands/adopt.ts +83 -0
  147. package/src/commands/cloud.ts +66 -108
  148. package/src/commands/db.ts +28 -52
  149. package/src/commands/deploy.ts +162 -104
  150. package/src/commands/dev.ts +24 -10
  151. package/src/commands/diff.ts +40 -41
  152. package/src/commands/doctor.ts +102 -0
  153. package/src/commands/functions.ts +95 -37
  154. package/src/commands/init.ts +25 -4
  155. package/src/commands/introspect.ts +47 -0
  156. package/src/commands/link-helpers.ts +228 -0
  157. package/src/commands/migrate.ts +163 -15
  158. package/src/commands/pull.ts +37 -9
  159. package/src/commands/push.ts +132 -166
  160. package/src/commands/status.ts +100 -33
  161. package/src/commands/update.ts +6 -2
  162. package/src/config.ts +2 -1
  163. package/src/dev-compose.ts +240 -6
  164. package/src/diff-output.ts +79 -1
  165. package/src/engine-client.ts +70 -13
  166. package/src/engine-push-output.ts +7 -3
  167. package/src/gitignore.ts +48 -0
  168. package/src/link.ts +242 -0
  169. package/src/process-manager.ts +4 -0
  170. package/src/project-config.ts +8 -0
  171. package/src/pull-utils.ts +217 -23
  172. package/src/resolve-target.ts +419 -0
  173. package/src/runtime-routes.ts +7 -0
  174. package/src/schema-ast-v2.ts +2 -1
  175. package/src/schema-sources.ts +248 -0
  176. package/src/self-host-compose.ts +87 -3
  177. package/src/storage-provision.ts +33 -1
  178. package/src/target-client.ts +40 -0
  179. package/src/type-extractor.ts +124 -11
  180. package/tests/cli-help.test.ts +27 -2
  181. package/tests/init.test.ts +1 -1
  182. package/tests/link.test.ts +148 -0
  183. package/tests/proxy-dev-app.test.ts +45 -1
  184. package/tests/pull-utils.test.ts +5 -4
  185. package/tests/runtime-contract.test.ts +44 -1
  186. package/tests/schema-sources.test.ts +119 -0
  187. package/tests/storage-provision.test.ts +100 -0
  188. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,83 @@
1
+ import type { Command } from "commander"
2
+ import { createInterface } from "node:readline"
3
+ import { loadConfig, loadSchemaAst } from "../config.js"
4
+ import { schemaPathFromProject } from "../project-config.js"
5
+ import { loadProjectLink } from "../link.js"
6
+ import { resolveTarget, targetSchemaAdopt, schemaPgSchema } from "../resolve-target.js"
7
+
8
+ interface AdoptPreview {
9
+ status: string
10
+ stampStatements?: string[]
11
+ doctor?: {
12
+ missing: unknown[]
13
+ staleManaged: unknown[]
14
+ unmanagedDrift: unknown[]
15
+ }
16
+ }
17
+
18
+ export function registerAdopt(program: Command): void {
19
+ program
20
+ .command("adopt")
21
+ .description("Stamp Supatype-managed comments on DB objects matching the schema (adoption ceremony)")
22
+ .option("--connection <url>", "Database connection URL (overrides config)")
23
+ .option("--env <name>", "Target environment when linked")
24
+ .option("--direct", "Use local engine subprocess")
25
+ .option("--yes", "Apply stamps without interactive confirmation")
26
+ .option("--no-cache", "Force full database introspection")
27
+ .action(async (opts: {
28
+ connection?: string
29
+ env?: string
30
+ direct?: boolean
31
+ yes?: boolean
32
+ noCache?: boolean
33
+ }) => {
34
+ const cwd = process.cwd()
35
+ const config = loadConfig(cwd)
36
+ const pgSchema = schemaPgSchema(cwd)
37
+
38
+ console.log("Loading schema...")
39
+ const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
40
+
41
+ const linked = loadProjectLink(cwd)
42
+ const target = linked && !opts.direct && !opts.connection
43
+ ? resolveTarget(cwd, { env: opts.env })
44
+ : resolveTarget(cwd, { env: opts.env, direct: true, connection: opts.connection })
45
+
46
+ const preview = (await targetSchemaAdopt(target, ast, {
47
+ schema: pgSchema,
48
+ noCache: opts.noCache ?? false,
49
+ yes: false,
50
+ })) as AdoptPreview
51
+
52
+ const statements = preview.stampStatements ?? []
53
+ if (statements.length === 0) {
54
+ console.log("Nothing to stamp — matching objects are already managed or absent.")
55
+ return
56
+ }
57
+
58
+ console.log(`\nWill stamp ${statements.length} object(s):\n`)
59
+ for (const sql of statements) {
60
+ console.log(` ${sql}`)
61
+ }
62
+
63
+ if (!opts.yes) {
64
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
65
+ const answer = await new Promise<string>((resolve) => {
66
+ rl.question("\nApply adoption stamps? [y/N] ", resolve)
67
+ })
68
+ rl.close()
69
+ if (!/^y(es)?$/i.test(answer.trim())) {
70
+ console.log("Adoption cancelled.")
71
+ return
72
+ }
73
+ }
74
+
75
+ const result = (await targetSchemaAdopt(target, ast, {
76
+ schema: pgSchema,
77
+ noCache: opts.noCache ?? false,
78
+ yes: true,
79
+ })) as { status: string; stamped?: number; name?: string }
80
+
81
+ console.log(`\nAdopted: ${result.stamped ?? 0} object(s) stamped (${result.name ?? "ok"}).`)
82
+ })
83
+ }
@@ -2,19 +2,35 @@ import type { Command } from "commander"
2
2
  import { readFileSync, writeFileSync, existsSync } from "node:fs"
3
3
  import { resolve } from "node:path"
4
4
  import { createInterface } from "node:readline"
5
+ import { loadProjectLink, migrateLegacyLinkFiles } from "../link.js"
6
+ import { targetFetch } from "../target-client.js"
7
+ import { registerEnvs, registerLinkOptions, runLinkAction } from "./link-helpers.js"
8
+ import { resolveTarget, targetSchemaPush, schemaPgSchema } from "../resolve-target.js"
9
+ import { loadConfig, loadSchemaAst } from "../config.js"
10
+ import { schemaPathFromProject } from "../project-config.js"
5
11
 
6
12
  interface CloudConfig {
7
13
  apiUrl: string
8
14
  token: string
9
15
  projectSlug?: string
10
- /** Organisation UUID — required for schema routes (`X-Org-Id`). */
11
- orgId?: string
16
+ orgId?: string | undefined
12
17
  }
13
18
 
19
+ /** @deprecated Prefer loadProjectLink */
14
20
  export function loadCloudConfig(cwd: string): CloudConfig | null {
15
- const configPath = resolve(cwd, ".supatype/cloud.json")
16
- if (!existsSync(configPath)) return null
17
- return JSON.parse(readFileSync(configPath, "utf8")) as CloudConfig
21
+ migrateLegacyLinkFiles(cwd)
22
+ const link = loadProjectLink(cwd)
23
+ if (!link || link.kind !== "cloud") return null
24
+ const legacyPath = resolve(cwd, ".supatype/cloud.json")
25
+ if (existsSync(legacyPath)) {
26
+ return JSON.parse(readFileSync(legacyPath, "utf8")) as CloudConfig
27
+ }
28
+ return {
29
+ apiUrl: link.cloudApiUrl ?? "https://api.supatype.com",
30
+ token: link.token ?? "",
31
+ projectSlug: link.projectRef,
32
+ ...(link.orgId !== undefined ? { orgId: link.orgId } : {}),
33
+ }
18
34
  }
19
35
 
20
36
  function saveCloudConfig(cwd: string, config: CloudConfig): void {
@@ -27,135 +43,76 @@ function saveCloudConfig(cwd: string, config: CloudConfig): void {
27
43
  }
28
44
 
29
45
  async function cloudFetch<T>(config: CloudConfig, method: string, path: string, body?: unknown): Promise<T> {
30
- const headers: Record<string, string> = {
31
- "Content-Type": "application/json",
32
- Authorization: `Bearer ${config.token}`,
33
- }
34
- if (config.orgId) {
35
- headers["X-Org-Id"] = config.orgId
36
- }
37
- const res = await fetch(`${config.apiUrl}/api/v1${path}`, {
46
+ return targetFetch<T>(config.apiUrl, "/api/v1", {
38
47
  method,
39
- headers,
40
- ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
48
+ path,
49
+ body,
50
+ token: config.token,
51
+ orgId: config.orgId,
41
52
  })
42
-
43
- const json = await res.json() as { data?: T; error?: string; message?: string }
44
- if (!res.ok) {
45
- throw new Error(json.message ?? json.error ?? `API error: ${res.status}`)
46
- }
47
- return json.data as T
48
53
  }
49
54
 
50
- /** True when `.supatype/cloud.json` exists with a linked project slug. */
51
55
  export function isCloudLinked(cwd: string): boolean {
52
- const cfg = loadCloudConfig(cwd)
53
- return Boolean(cfg?.projectSlug && cfg.token)
56
+ migrateLegacyLinkFiles(cwd)
57
+ const link = loadProjectLink(cwd)
58
+ return Boolean(link?.kind === "cloud" && link.projectRef && link.token)
54
59
  }
55
60
 
56
- /**
57
- * Push schema AST to the linked cloud project (`POST /api/v1/projects/:ref/schema/push`).
58
- * Credentials stay server-side; only AST is sent.
59
- */
60
- export async function pushSchemaToLinkedProject(cwd: string, opts?: { force?: boolean }): Promise<void> {
61
- const config = loadCloudConfig(cwd)
62
- if (!config?.projectSlug) {
63
- console.error("Not linked to a cloud project. Run: supatype link")
61
+ export async function pushSchemaToLinkedProject(
62
+ cwd: string,
63
+ opts?: { force?: boolean; env?: string },
64
+ ): Promise<void> {
65
+ const config = loadConfig(cwd)
66
+ const target = resolveTarget(cwd, { env: opts?.env })
67
+ if (target.mode !== "cloud") {
68
+ console.error("Not linked to a cloud project. Run: supatype link --project <slug>")
64
69
  process.exit(1)
65
70
  }
66
- if (!config.orgId) {
67
- console.error(
68
- "Missing orgId in .supatype/cloud.json. Re-run: supatype link --project <slug> (after cloud login).",
69
- )
70
- process.exit(1)
71
- }
72
-
73
- const { loadConfig: loadAppConfig, loadSchemaAst } = await import("../config.js")
74
- const { schemaPathFromProject } = await import("../project-config.js")
75
71
 
76
- const appConfig = loadAppConfig(cwd)
77
- const ast = loadSchemaAst(schemaPathFromProject(appConfig, cwd), cwd)
72
+ const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
73
+ console.log(`Pushing schema to ${target.mode} project ${target.projectRef} (${target.environment})...`)
78
74
 
79
- console.log(`Pushing schema to cloud project ${config.projectSlug}...`)
80
-
81
- const result = await cloudFetch<{ message?: string }>(config, "POST", `/projects/${config.projectSlug}/schema/push`, {
82
- ast,
75
+ const result = await targetSchemaPush(target, ast, {
83
76
  force: opts?.force ?? true,
77
+ schema: schemaPgSchema(cwd),
84
78
  })
85
79
 
86
- console.log(result.message ?? "Schema push completed.")
80
+ console.log((result as { message?: string }).message ?? "Schema push completed.")
87
81
  }
88
82
 
89
- /** @deprecated Use pushSchemaToLinkedProject kept for deploy command alias */
90
- export async function deploySchemaToLinkedProject(cwd: string, _environment: string): Promise<void> {
91
- await pushSchemaToLinkedProject(cwd)
83
+ export async function deploySchemaToLinkedProject(cwd: string, environment: string): Promise<void> {
84
+ await pushSchemaToLinkedProject(cwd, { force: true, env: environment })
92
85
  }
93
86
 
94
87
  function prompt(question: string): Promise<string> {
95
88
  const rl = createInterface({ input: process.stdin, output: process.stdout })
96
- return new Promise((resolve) => {
89
+ return new Promise((resolvePrompt) => {
97
90
  rl.question(question, (answer) => {
98
91
  rl.close()
99
- resolve(answer.trim())
92
+ resolvePrompt(answer.trim())
100
93
  })
101
94
  })
102
95
  }
103
96
 
104
- // ─── Registration ──────────────────────────────────────────────────────────────
105
-
106
97
  export function registerCloud(program: Command): void {
107
- // ── Link ───────────────────────────────────────────────────────────────────
108
- program
109
- .command("link")
110
- .description("Link this local project to a Supatype cloud project")
111
- .option("--project <slug>", "Project slug to link to")
112
- .option("--api-url <url>", "Control plane API URL", "https://api.supatype.com")
113
- .option("--token <token>", "Authentication token")
114
- .action(async (opts: { project?: string; apiUrl: string; token?: string }) => {
115
- const cwd = process.cwd()
116
- const token = opts.token ?? process.env["SUPATYPE_TOKEN"]
117
- if (!token) {
118
- console.error("Authentication required. Set SUPATYPE_TOKEN or pass --token.")
119
- process.exit(1)
120
- }
121
-
122
- const config: CloudConfig = { apiUrl: opts.apiUrl, token }
123
-
124
- if (opts.project) {
125
- config.projectSlug = opts.project
126
- const one = await cloudFetch<{ slug: string; orgId: string }>(config, "GET", `/projects/${opts.project}`)
127
- config.orgId = one.orgId
128
- } else {
129
- const projects = await cloudFetch<Array<{ slug: string; name: string; status: string; tier: string; orgId: string }>>(
130
- config, "GET", "/projects",
131
- )
132
- if (projects.length === 0) {
133
- console.error("No projects found. Create one with: supatype projects create <name>")
134
- process.exit(1)
135
- }
136
-
137
- console.log("\nAvailable projects:\n")
138
- projects.forEach((p, i) => {
139
- console.log(` ${i + 1}. ${p.name} (${p.slug}) [${p.tier}] — ${p.status}`)
140
- })
141
-
142
- const answer = await prompt(`\nSelect project (1-${projects.length}): `)
143
- const idx = parseInt(answer, 10) - 1
144
- if (isNaN(idx) || idx < 0 || idx >= projects.length) {
145
- console.error("Invalid selection.")
146
- process.exit(1)
147
- }
148
- const picked = projects[idx]!
149
- config.projectSlug = picked.slug
150
- config.orgId = picked.orgId
151
- }
98
+ registerEnvs(program)
152
99
 
153
- saveCloudConfig(cwd, config)
154
- console.log(`\nLinked to project: ${config.projectSlug}`)
155
- console.log(`Config saved to .supatype/cloud.json\n`)
156
- })
100
+ const linkCmd = program
101
+ .command("link")
102
+ .description("Link this project to cloud or self-host (unified .supatype/link.json)")
103
+ registerLinkOptions(linkCmd)
104
+ linkCmd.action(async (opts: {
105
+ project?: string
106
+ url?: string
107
+ apiUrl: string
108
+ token?: string
109
+ serviceRoleKey?: string
110
+ env?: string
111
+ fixGitignore?: boolean
112
+ }) => {
113
+ await runLinkAction(opts)
114
+ })
157
115
 
158
- // ── Projects ───────────────────────────────────────────────────────────────
159
116
  const projectsCmd = program
160
117
  .command("projects")
161
118
  .description("Manage cloud projects")
@@ -223,7 +180,6 @@ export function registerCloud(program: Command): void {
223
180
  console.log(`Project ${slug} resumed.`)
224
181
  })
225
182
 
226
- // ── Domains ────────────────────────────────────────────────────────────────
227
183
  const domainsCmd = program
228
184
  .command("domains")
229
185
  .description("Manage custom domains for a project")
@@ -313,10 +269,12 @@ function getCloudConfigOrExit(): CloudConfig {
313
269
  const cwd = process.cwd()
314
270
  let config = loadCloudConfig(cwd)
315
271
  if (!config) {
316
- const token = process.env["SUPATYPE_TOKEN"]
272
+ const token =
273
+ process.env["SUPATYPE_ACCESS_TOKEN"] ??
274
+ process.env["SUPATYPE_TOKEN"]
317
275
  const apiUrl = process.env["SUPATYPE_API_URL"] ?? "https://api.supatype.com"
318
276
  if (!token) {
319
- console.error("Not connected to Supatype Cloud. Run: supatype link, or set SUPATYPE_TOKEN.")
277
+ console.error("Not connected to Supatype Cloud. Run: supatype link, or set SUPATYPE_ACCESS_TOKEN.")
320
278
  process.exit(1)
321
279
  }
322
280
  config = { apiUrl, token }
@@ -7,14 +7,15 @@
7
7
  import type { Command } from "commander"
8
8
  import { loadConfig } from "../config.js"
9
9
  import { connectionString } from "../project-config.js"
10
- import { loadCloudConfig } from "./cloud.js"
10
+ import { loadProjectLink } from "../link.js"
11
+ import { resolveTarget } from "../resolve-target.js"
12
+ import { targetFetch } from "../target-client.js"
11
13
 
12
14
  export function registerDb(program: Command): void {
13
15
  const db = program
14
16
  .command("db")
15
17
  .description("Database connection management")
16
18
 
17
- // supatype db connection-string
18
19
  db
19
20
  .command("connection-string")
20
21
  .description("Show the database connection string for the linked project")
@@ -23,11 +24,10 @@ export function registerDb(program: Command): void {
23
24
  .action(async (opts: { transaction?: boolean; env?: string }) => {
24
25
  const cwd = process.cwd()
25
26
  const config = loadConfig(cwd)
26
- const cloudCfg = loadCloudConfig(cwd)
27
+ const link = loadProjectLink(cwd)
27
28
  const localConn = connectionString(config)
28
29
 
29
- // If no cloud project linked, show the local connection string.
30
- if (!cloudCfg?.projectSlug) {
30
+ if (!link || link.kind !== "cloud") {
31
31
  const connStr = opts.transaction ? localConn.replace(/:5432\//, ":6432/") : localConn
32
32
  console.log(connStr)
33
33
  console.log()
@@ -36,30 +36,22 @@ export function registerDb(program: Command): void {
36
36
  return
37
37
  }
38
38
 
39
- // If linked to a cloud project, fetch from the API
40
- const apiUrl = cloudCfg.apiUrl || "https://api.supatype.com"
41
- const token = cloudCfg.token || process.env["SUPATYPE_ACCESS_TOKEN"] || ""
39
+ const target = resolveTarget(cwd, { env: opts.env })
42
40
  const envName = opts.env || "production"
43
41
 
44
42
  try {
45
- const res = await fetch(
46
- `${apiUrl}/platform/v1/projects/${cloudCfg.projectSlug}/environments`,
43
+ const data = await targetFetch<Array<{ name: string; databaseUrl?: string }>>(
44
+ target.apiBaseUrl,
45
+ target.apiPrefix,
47
46
  {
48
- headers: {
49
- Authorization: `Bearer ${token}`,
50
- },
47
+ method: "GET",
48
+ path: `/projects/${target.projectRef}/environments`,
49
+ token: target.token!,
50
+ orgId: target.orgId,
51
51
  },
52
52
  )
53
53
 
54
- if (!res.ok) {
55
- console.error(`Failed to fetch project info: ${res.status}`)
56
- process.exitCode = 1
57
- return
58
- }
59
-
60
- const { data } = (await res.json()) as { data: Array<{ name: string; databaseUrl?: string }> }
61
54
  const env = data.find((e) => e.name === envName)
62
-
63
55
  if (!env) {
64
56
  console.error(`Environment "${envName}" not found`)
65
57
  process.exitCode = 1
@@ -67,12 +59,7 @@ export function registerDb(program: Command): void {
67
59
  }
68
60
 
69
61
  const connStr = env.databaseUrl || "Connection string not available"
70
- if (opts.transaction) {
71
- console.log(connStr.replace(/:5432\//, ":6432/"))
72
- } else {
73
- console.log(connStr)
74
- }
75
-
62
+ console.log(opts.transaction ? connStr.replace(/:5432\//, ":6432/") : connStr)
76
63
  console.log()
77
64
  console.log("Session mode (port 5432): for interactive tools (psql, DataGrip, TablePlus)")
78
65
  console.log("Transaction mode (port 6432): for application servers and serverless functions")
@@ -82,52 +69,41 @@ export function registerDb(program: Command): void {
82
69
  }
83
70
  })
84
71
 
85
- // supatype db reset-password
86
72
  db
87
73
  .command("reset-password")
88
74
  .description("Reset the database password for the linked project")
89
75
  .option("--env <name>", "Environment name", "production")
90
76
  .action(async (opts: { env?: string }) => {
91
77
  const cwd = process.cwd()
92
- const cloudCfg = loadCloudConfig(cwd)
78
+ const link = loadProjectLink(cwd)
93
79
 
94
- if (!cloudCfg?.projectSlug) {
80
+ if (!link || link.kind !== "cloud") {
95
81
  console.error("Not linked to a cloud project. Run: supatype link --project <ref>")
96
82
  process.exitCode = 1
97
83
  return
98
84
  }
99
85
 
100
- const apiUrl = cloudCfg.apiUrl || "https://api.supatype.com"
101
- const token = cloudCfg.token || process.env["SUPATYPE_ACCESS_TOKEN"] || ""
86
+ const target = resolveTarget(cwd, { env: opts.env })
102
87
  const envName = opts.env || "production"
103
88
 
104
89
  try {
105
- const res = await fetch(
106
- `${apiUrl}/platform/v1/projects/${cloudCfg.projectSlug}/environments/${envName}/reset-db-password`,
90
+ const data = await targetFetch<{ password?: string; databaseUrl?: string }>(
91
+ target.apiBaseUrl,
92
+ target.apiPrefix,
107
93
  {
108
94
  method: "POST",
109
- headers: {
110
- Authorization: `Bearer ${token}`,
111
- "Content-Type": "application/json",
112
- },
95
+ path: `/projects/${target.projectRef}/environments/${envName}/reset-db-password`,
96
+ token: target.token!,
97
+ orgId: target.orgId,
113
98
  },
114
99
  )
115
100
 
116
- if (!res.ok) {
117
- const body = await res.text()
118
- console.error(`Failed to reset password: ${res.status} ${body}`)
119
- process.exitCode = 1
120
- return
121
- }
122
-
123
- const { data } = (await res.json()) as { data: { password: string; connectionString: string } }
124
101
  console.log("Database password reset successfully.")
125
- console.log()
126
- console.log(`New password: ${data.password}`)
127
- console.log(`Connection string: ${data.connectionString}`)
128
- console.log()
129
- console.log("Warning: Existing database connections have been terminated.")
130
- console.log("Update your application with the new connection string.")
102
+ if (data.databaseUrl) {
103
+ console.log(`\nNew connection string:\n${data.databaseUrl}`)
104
+ } else if (data.password) {
105
+ console.log(`\nNew password: ${data.password}`)
106
+ }
131
107
  } catch (err) {
132
108
  console.error("Failed to reset password:", (err as Error).message)
133
109
  process.exitCode = 1