@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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +98 -65
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/app/framework.js +1 -3
- package/dist/app/framework.js.map +1 -1
- package/dist/app/proxy-dev-app.d.ts +14 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -1
- package/dist/app/proxy-dev-app.js +109 -6
- package/dist/app/proxy-dev-app.js.map +1 -1
- package/dist/binary-cache.d.ts +1 -1
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +6 -1
- package/dist/binary-cache.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts +3 -0
- package/dist/commands/adopt.d.ts.map +1 -0
- package/dist/commands/adopt.js +58 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/cloud.d.ts +4 -9
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +49 -91
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +25 -47
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +117 -74
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +21 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +37 -37
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +77 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +80 -33
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +26 -4
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/introspect.d.ts +3 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/introspect.js +34 -0
- package/dist/commands/introspect.js.map +1 -0
- package/dist/commands/link-helpers.d.ts +15 -0
- package/dist/commands/link-helpers.d.ts.map +1 -0
- package/dist/commands/link-helpers.js +187 -0
- package/dist/commands/link-helpers.js.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +116 -14
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +32 -5
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +102 -129
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +93 -29
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +6 -2
- package/dist/commands/update.js.map +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts +23 -0
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +183 -6
- package/dist/dev-compose.js.map +1 -1
- package/dist/diff-output.d.ts +5 -1
- package/dist/diff-output.d.ts.map +1 -1
- package/dist/diff-output.js +69 -0
- package/dist/diff-output.js.map +1 -1
- package/dist/engine-client.d.ts +10 -1
- package/dist/engine-client.d.ts.map +1 -1
- package/dist/engine-client.js +64 -13
- package/dist/engine-client.js.map +1 -1
- package/dist/engine-push-output.d.ts +1 -0
- package/dist/engine-push-output.d.ts.map +1 -1
- package/dist/engine-push-output.js +4 -1
- package/dist/engine-push-output.js.map +1 -1
- package/dist/gitignore.d.ts +8 -0
- package/dist/gitignore.d.ts.map +1 -0
- package/dist/gitignore.js +41 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/link.d.ts +66 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +159 -0
- package/dist/link.js.map +1 -0
- package/dist/process-manager.d.ts +2 -0
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +2 -0
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +8 -0
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js.map +1 -1
- package/dist/pull-utils.d.ts +50 -14
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +152 -12
- package/dist/pull-utils.js.map +1 -1
- package/dist/resolve-target.d.ts +86 -0
- package/dist/resolve-target.d.ts.map +1 -0
- package/dist/resolve-target.js +291 -0
- package/dist/resolve-target.js.map +1 -0
- package/dist/runtime-routes.d.ts.map +1 -1
- package/dist/runtime-routes.js +7 -0
- package/dist/runtime-routes.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +1 -1
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +2 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-sources.d.ts +40 -0
- package/dist/schema-sources.d.ts.map +1 -0
- package/dist/schema-sources.js +183 -0
- package/dist/schema-sources.js.map +1 -0
- package/dist/self-host-compose.d.ts +10 -0
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +85 -3
- package/dist/self-host-compose.js.map +1 -1
- package/dist/storage-provision.d.ts +4 -0
- package/dist/storage-provision.d.ts.map +1 -1
- package/dist/storage-provision.js +24 -2
- package/dist/storage-provision.js.map +1 -1
- package/dist/target-client.d.ts +10 -0
- package/dist/target-client.d.ts.map +1 -0
- package/dist/target-client.js +22 -0
- package/dist/target-client.js.map +1 -0
- package/dist/type-extractor.d.ts +11 -0
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +95 -8
- package/dist/type-extractor.js.map +1 -1
- package/package.json +1 -1
- package/src/app/framework.ts +1 -3
- package/src/app/proxy-dev-app.ts +113 -6
- package/src/binary-cache.ts +6 -1
- package/src/cli.ts +6 -0
- package/src/commands/adopt.ts +83 -0
- package/src/commands/cloud.ts +66 -108
- package/src/commands/db.ts +28 -52
- package/src/commands/deploy.ts +162 -104
- package/src/commands/dev.ts +24 -10
- package/src/commands/diff.ts +40 -41
- package/src/commands/doctor.ts +102 -0
- package/src/commands/functions.ts +95 -37
- package/src/commands/init.ts +25 -4
- package/src/commands/introspect.ts +47 -0
- package/src/commands/link-helpers.ts +228 -0
- package/src/commands/migrate.ts +163 -15
- package/src/commands/pull.ts +37 -9
- package/src/commands/push.ts +132 -166
- package/src/commands/status.ts +100 -33
- package/src/commands/update.ts +6 -2
- package/src/config.ts +2 -1
- package/src/dev-compose.ts +240 -6
- package/src/diff-output.ts +79 -1
- package/src/engine-client.ts +70 -13
- package/src/engine-push-output.ts +7 -3
- package/src/gitignore.ts +48 -0
- package/src/link.ts +242 -0
- package/src/process-manager.ts +4 -0
- package/src/project-config.ts +8 -0
- package/src/pull-utils.ts +217 -23
- package/src/resolve-target.ts +419 -0
- package/src/runtime-routes.ts +7 -0
- package/src/schema-ast-v2.ts +2 -1
- package/src/schema-sources.ts +248 -0
- package/src/self-host-compose.ts +87 -3
- package/src/storage-provision.ts +33 -1
- package/src/target-client.ts +40 -0
- package/src/type-extractor.ts +124 -11
- package/tests/cli-help.test.ts +27 -2
- package/tests/init.test.ts +1 -1
- package/tests/link.test.ts +148 -0
- package/tests/proxy-dev-app.test.ts +45 -1
- package/tests/pull-utils.test.ts +5 -4
- package/tests/runtime-contract.test.ts +44 -1
- package/tests/schema-sources.test.ts +119 -0
- package/tests/storage-provision.test.ts +100 -0
- 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
|
+
}
|
package/src/commands/cloud.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
53
|
-
|
|
56
|
+
migrateLegacyLinkFiles(cwd)
|
|
57
|
+
const link = loadProjectLink(cwd)
|
|
58
|
+
return Boolean(link?.kind === "cloud" && link.projectRef && link.token)
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
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
|
|
77
|
-
|
|
72
|
+
const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
|
|
73
|
+
console.log(`Pushing schema to ${target.mode} project ${target.projectRef} (${target.environment})...`)
|
|
78
74
|
|
|
79
|
-
|
|
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
|
-
|
|
90
|
-
|
|
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((
|
|
89
|
+
return new Promise((resolvePrompt) => {
|
|
97
90
|
rl.question(question, (answer) => {
|
|
98
91
|
rl.close()
|
|
99
|
-
|
|
92
|
+
resolvePrompt(answer.trim())
|
|
100
93
|
})
|
|
101
94
|
})
|
|
102
95
|
}
|
|
103
96
|
|
|
104
|
-
// ─── Registration ──────────────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
97
|
export function registerCloud(program: Command): void {
|
|
107
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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 =
|
|
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
|
|
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 }
|
package/src/commands/db.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
27
|
+
const link = loadProjectLink(cwd)
|
|
27
28
|
const localConn = connectionString(config)
|
|
28
29
|
|
|
29
|
-
|
|
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
|
-
|
|
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
|
|
46
|
-
|
|
43
|
+
const data = await targetFetch<Array<{ name: string; databaseUrl?: string }>>(
|
|
44
|
+
target.apiBaseUrl,
|
|
45
|
+
target.apiPrefix,
|
|
47
46
|
{
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
|
78
|
+
const link = loadProjectLink(cwd)
|
|
93
79
|
|
|
94
|
-
if (!
|
|
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
|
|
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
|
|
106
|
-
|
|
90
|
+
const data = await targetFetch<{ password?: string; databaseUrl?: string }>(
|
|
91
|
+
target.apiBaseUrl,
|
|
92
|
+
target.apiPrefix,
|
|
107
93
|
{
|
|
108
94
|
method: "POST",
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|