@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
package/src/commands/deploy.ts
CHANGED
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { Command } from "commander"
|
|
15
|
-
import { existsSync, readdirSync, statSync, createReadStream } from "node:fs"
|
|
16
|
-
import { join } from "node:path"
|
|
15
|
+
import { existsSync, readdirSync, statSync, createReadStream, mkdirSync, cpSync } from "node:fs"
|
|
16
|
+
import { join, relative } from "node:path"
|
|
17
17
|
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
18
18
|
import { connectionString, schemaPathFromProject } from "../project-config.js"
|
|
19
19
|
import { deploySchemaToLinkedProject, loadCloudConfig, pushSchemaToLinkedProject } from "./cloud.js"
|
|
20
|
+
import { loadProjectLink } from "../link.js"
|
|
21
|
+
import { resolveTarget } from "../resolve-target.js"
|
|
22
|
+
import { targetFetch } from "../target-client.js"
|
|
20
23
|
import { ensureEngine, engineRequest, type DiffResult } from "../engine-client.js"
|
|
21
24
|
import { resolveAppConfig, validateStaticMode, validateBuildOutput, detectPackageManager } from "../app/framework.js"
|
|
22
25
|
import { TIER_LIMITS, type Tier } from "./deploy-types.js"
|
|
@@ -29,7 +32,8 @@ export function registerDeploy(program: Command): void {
|
|
|
29
32
|
"Deploy schema and app — Supatype Cloud by default when linked (`supatype link`); pass --local for engine + your database",
|
|
30
33
|
)
|
|
31
34
|
.option("--local", "Use local schema engine and database_url from config (skip cloud control plane)")
|
|
32
|
-
.option("--environment <name>", "
|
|
35
|
+
.option("--environment <name>", "Target environment when linked", "production")
|
|
36
|
+
.option("--env <name>", "Alias for --environment")
|
|
33
37
|
.option("--app-only", "Skip schema push, only deploy the static site")
|
|
34
38
|
.option("--schema-only", "Skip app build, only push schema changes")
|
|
35
39
|
.option("--skip-build", "Deploy existing build output without building")
|
|
@@ -38,6 +42,7 @@ export function registerDeploy(program: Command): void {
|
|
|
38
42
|
.action(async (opts: {
|
|
39
43
|
local?: boolean
|
|
40
44
|
environment?: string
|
|
45
|
+
env?: string
|
|
41
46
|
appOnly?: boolean
|
|
42
47
|
schemaOnly?: boolean
|
|
43
48
|
skipBuild?: boolean
|
|
@@ -46,18 +51,19 @@ export function registerDeploy(program: Command): void {
|
|
|
46
51
|
}) => {
|
|
47
52
|
const cwd = process.cwd()
|
|
48
53
|
const config = loadConfig(cwd)
|
|
54
|
+
const link = loadProjectLink(cwd)
|
|
49
55
|
const cloudCfg = loadCloudConfig(cwd)
|
|
56
|
+
const envName = opts.env ?? opts.environment ?? "production"
|
|
50
57
|
|
|
51
58
|
let schemaDone = false
|
|
52
59
|
|
|
53
|
-
// Default: cloud — .supatype/cloud.json → control plane schema deploy
|
|
54
60
|
if (
|
|
55
61
|
!opts.local &&
|
|
56
|
-
|
|
62
|
+
link &&
|
|
57
63
|
!opts.appOnly &&
|
|
58
64
|
!opts.skipBuild
|
|
59
65
|
) {
|
|
60
|
-
await deploySchemaToLinkedProject(cwd,
|
|
66
|
+
await deploySchemaToLinkedProject(cwd, envName)
|
|
61
67
|
schemaDone = true
|
|
62
68
|
if (opts.schemaOnly) {
|
|
63
69
|
return
|
|
@@ -92,9 +98,9 @@ export function registerDeploy(program: Command): void {
|
|
|
92
98
|
} else {
|
|
93
99
|
console.log("Schema is up to date.")
|
|
94
100
|
}
|
|
95
|
-
} else if (
|
|
96
|
-
console.log("=== Schema Push (
|
|
97
|
-
await pushSchemaToLinkedProject(cwd, { force: opts.yes ?? true })
|
|
101
|
+
} else if (link) {
|
|
102
|
+
console.log("=== Schema Push (linked) ===")
|
|
103
|
+
await pushSchemaToLinkedProject(cwd, { force: opts.yes ?? true, env: envName })
|
|
98
104
|
} else {
|
|
99
105
|
console.error(
|
|
100
106
|
"Not linked to Supatype Cloud. Run: supatype link\n" +
|
|
@@ -184,21 +190,21 @@ export function registerDeploy(program: Command): void {
|
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
// Deploy (--local never uploads to cloud, even if linked)
|
|
187
|
-
if (
|
|
188
|
-
await
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
)
|
|
193
|
+
if (link && !opts.local) {
|
|
194
|
+
await deployStaticSite(cwd, appConfig.outputDirectory, {
|
|
195
|
+
preview: opts.preview ?? false,
|
|
196
|
+
env: envName,
|
|
197
|
+
})
|
|
193
198
|
} else {
|
|
194
199
|
deploySelfHost(appConfig.outputDirectory, cwd)
|
|
195
200
|
}
|
|
196
201
|
|
|
197
202
|
console.log("\nDeployment complete!")
|
|
198
|
-
if (
|
|
203
|
+
if (link && !opts.local) {
|
|
204
|
+
const target = resolveTarget(cwd, { env: envName })
|
|
199
205
|
const url = opts.preview
|
|
200
|
-
?
|
|
201
|
-
:
|
|
206
|
+
? `${target.apiBaseUrl}/preview`
|
|
207
|
+
: target.apiBaseUrl
|
|
202
208
|
console.log(`URL: ${url}`)
|
|
203
209
|
}
|
|
204
210
|
}
|
|
@@ -208,123 +214,174 @@ export function registerDeploy(program: Command): void {
|
|
|
208
214
|
deploy
|
|
209
215
|
.command("rollback")
|
|
210
216
|
.description("Roll back to the previous static site deployment")
|
|
211
|
-
.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const apiUrl = cloudCfg.apiUrl || "https://api.supatype.com"
|
|
219
|
-
const token = cloudCfg.token || process.env["SUPATYPE_ACCESS_TOKEN"] || ""
|
|
220
|
-
|
|
221
|
-
const res = await fetch(`${apiUrl}/platform/v1/projects/${cloudCfg.projectSlug}/deployments/rollback`, {
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
if (!res.ok) {
|
|
227
|
-
const body = await res.text()
|
|
228
|
-
console.error(`Rollback failed: ${res.status} ${body}`)
|
|
217
|
+
.option("--env <name>", "Target environment when linked")
|
|
218
|
+
.option("--to <id>", "Roll back to a specific deployment id or version")
|
|
219
|
+
.action(async (opts: { env?: string; to?: string }) => {
|
|
220
|
+
const cwd = process.cwd()
|
|
221
|
+
const link = loadProjectLink(cwd)
|
|
222
|
+
if (!link) {
|
|
223
|
+
console.error("Not linked to a project. Rollback requires a linked target.")
|
|
229
224
|
process.exit(1)
|
|
230
225
|
}
|
|
231
226
|
|
|
232
|
-
const
|
|
233
|
-
|
|
227
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
228
|
+
const body = opts.to ? { to: opts.to } : undefined
|
|
229
|
+
const data = await targetFetch<{ version: string; message: string }>(
|
|
230
|
+
target.apiBaseUrl,
|
|
231
|
+
target.apiPrefix,
|
|
232
|
+
{
|
|
233
|
+
method: "POST",
|
|
234
|
+
path: `/projects/${target.projectRef}/deployments/rollback`,
|
|
235
|
+
body,
|
|
236
|
+
token: target.token!,
|
|
237
|
+
orgId: target.orgId,
|
|
238
|
+
environment: target.mode === "cloud" ? target.environment : undefined,
|
|
239
|
+
},
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
console.log(`Rolled back to deployment ${data.version ?? "previous"}.`)
|
|
234
243
|
})
|
|
235
244
|
|
|
236
245
|
// supatype deploy status
|
|
237
246
|
deploy
|
|
238
247
|
.command("status")
|
|
239
248
|
.description("Show current deployment status")
|
|
240
|
-
.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
249
|
+
.option("--env <name>", "Target environment when linked")
|
|
250
|
+
.action(async (opts: { env?: string }) => {
|
|
251
|
+
const cwd = process.cwd()
|
|
252
|
+
const link = loadProjectLink(cwd)
|
|
253
|
+
if (!link) {
|
|
254
|
+
console.error("Not linked to a project.")
|
|
244
255
|
process.exit(1)
|
|
245
256
|
}
|
|
246
257
|
|
|
247
|
-
const
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
258
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
259
|
+
const data = await targetFetch<{
|
|
260
|
+
version?: string
|
|
261
|
+
id?: string
|
|
262
|
+
timestamp?: string
|
|
263
|
+
createdAt?: string
|
|
264
|
+
size?: number
|
|
265
|
+
buildDuration?: number
|
|
266
|
+
url?: string
|
|
267
|
+
status?: string
|
|
268
|
+
} | null>(
|
|
269
|
+
target.apiBaseUrl,
|
|
270
|
+
target.apiPrefix,
|
|
271
|
+
{
|
|
272
|
+
method: "GET",
|
|
273
|
+
path: `/projects/${target.projectRef}/deployments/current`,
|
|
274
|
+
token: target.token!,
|
|
275
|
+
orgId: target.orgId,
|
|
276
|
+
environment: target.mode === "cloud" ? target.environment : undefined,
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if (!data) {
|
|
281
|
+
console.log("No active deployment found.")
|
|
256
282
|
return
|
|
257
283
|
}
|
|
258
284
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
console.log(`Deployment: ${data.version}`)
|
|
269
|
-
console.log(`Status: ${data.status}`)
|
|
270
|
-
console.log(`Deployed: ${data.timestamp}`)
|
|
271
|
-
console.log(`Size: ${(data.size / (1024 * 1024)).toFixed(1)}MB`)
|
|
272
|
-
console.log(`Build duration: ${data.buildDuration}s`)
|
|
273
|
-
console.log(`URL: ${data.url}`)
|
|
285
|
+
console.log(`Deployment: ${data.version ?? data.id ?? "unknown"}`)
|
|
286
|
+
console.log(`Status: ${data.status ?? "live"}`)
|
|
287
|
+
if (data.timestamp ?? data.createdAt) {
|
|
288
|
+
console.log(`Deployed: ${data.timestamp ?? data.createdAt}`)
|
|
289
|
+
}
|
|
290
|
+
if (data.size) console.log(`Size: ${(data.size / (1024 * 1024)).toFixed(1)}MB`)
|
|
291
|
+
if (data.buildDuration) console.log(`Build duration: ${data.buildDuration}s`)
|
|
292
|
+
if (data.url) console.log(`URL: ${data.url}`)
|
|
274
293
|
})
|
|
275
294
|
|
|
276
295
|
// supatype deploy logs <version>
|
|
277
296
|
deploy
|
|
278
297
|
.command("logs [version]")
|
|
279
298
|
.description("Show build logs for a deployment")
|
|
280
|
-
.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
299
|
+
.option("--env <name>", "Target environment when linked")
|
|
300
|
+
.action(async (version: string | undefined, opts: { env?: string }) => {
|
|
301
|
+
const cwd = process.cwd()
|
|
302
|
+
const link = loadProjectLink(cwd)
|
|
303
|
+
if (!link) {
|
|
304
|
+
console.error("Not linked to a project.")
|
|
284
305
|
process.exit(1)
|
|
285
306
|
}
|
|
286
307
|
|
|
287
|
-
const
|
|
288
|
-
const token = cloudCfg.token || process.env["SUPATYPE_ACCESS_TOKEN"] || ""
|
|
289
|
-
|
|
308
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
290
309
|
const versionPath = version ? `/${version}` : "/current"
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
310
|
+
const data = await targetFetch<{ logs: string }>(
|
|
311
|
+
target.apiBaseUrl,
|
|
312
|
+
target.apiPrefix,
|
|
313
|
+
{
|
|
314
|
+
method: "GET",
|
|
315
|
+
path: `/projects/${target.projectRef}/deployments${versionPath}/logs`,
|
|
316
|
+
token: target.token!,
|
|
317
|
+
orgId: target.orgId,
|
|
318
|
+
environment: target.mode === "cloud" ? target.environment : undefined,
|
|
319
|
+
},
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
console.log(data.logs ?? "(no logs)")
|
|
302
323
|
})
|
|
303
324
|
}
|
|
304
325
|
|
|
305
|
-
async function
|
|
306
|
-
|
|
326
|
+
async function deployStaticSite(
|
|
327
|
+
cwd: string,
|
|
307
328
|
outputDir: string,
|
|
308
|
-
|
|
329
|
+
opts: { preview?: boolean; env?: string },
|
|
309
330
|
): Promise<void> {
|
|
310
|
-
const
|
|
311
|
-
const token =
|
|
331
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
332
|
+
const token = target.token
|
|
333
|
+
if (!token) {
|
|
334
|
+
throw new Error("No token for linked target. Re-run supatype link --token ...")
|
|
335
|
+
}
|
|
312
336
|
|
|
313
337
|
console.log("Uploading build artifacts...")
|
|
314
|
-
|
|
315
|
-
// Collect files from output directory
|
|
316
338
|
const files = collectFiles(outputDir, outputDir)
|
|
317
339
|
console.log(`${files.length} files to upload (${formatSize(files.reduce((s, f) => s + f.size, 0))})`)
|
|
318
340
|
|
|
319
|
-
|
|
320
|
-
|
|
341
|
+
const { readFileSync } = await import("node:fs")
|
|
342
|
+
|
|
343
|
+
if (target.apiPrefix === "/platform/v1") {
|
|
344
|
+
const filePayload = files.map((f) => ({
|
|
345
|
+
path: f.relativePath,
|
|
346
|
+
content: readFileSync(f.absolutePath).toString("base64"),
|
|
347
|
+
encoding: "base64",
|
|
348
|
+
}))
|
|
349
|
+
|
|
350
|
+
const created = await targetFetch<{ id: string }>(
|
|
351
|
+
target.apiBaseUrl,
|
|
352
|
+
target.apiPrefix,
|
|
353
|
+
{
|
|
354
|
+
method: "POST",
|
|
355
|
+
path: `/projects/${target.projectRef}/deployments`,
|
|
356
|
+
body: { preview: opts.preview ?? false, files: filePayload },
|
|
357
|
+
token,
|
|
358
|
+
orgId: target.orgId,
|
|
359
|
+
},
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
await targetFetch(
|
|
363
|
+
target.apiBaseUrl,
|
|
364
|
+
target.apiPrefix,
|
|
365
|
+
{
|
|
366
|
+
method: "POST",
|
|
367
|
+
path: `/projects/${target.projectRef}/deployments/${created.id}/finalize`,
|
|
368
|
+
token,
|
|
369
|
+
orgId: target.orgId,
|
|
370
|
+
},
|
|
371
|
+
)
|
|
372
|
+
return
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const createRes = await fetch(`${target.apiBaseUrl}/api/v1/projects/${target.projectRef}/deployments`, {
|
|
321
376
|
method: "POST",
|
|
322
377
|
headers: {
|
|
323
378
|
Authorization: `Bearer ${token}`,
|
|
324
379
|
"Content-Type": "application/json",
|
|
380
|
+
...(target.orgId ? { "X-Org-Id": target.orgId } : {}),
|
|
381
|
+
...(target.environment ? { "X-Supatype-Environment": target.environment } : {}),
|
|
325
382
|
},
|
|
326
383
|
body: JSON.stringify({
|
|
327
|
-
preview:
|
|
384
|
+
preview: opts.preview ?? false,
|
|
328
385
|
fileCount: files.length,
|
|
329
386
|
totalSize: files.reduce((s, f) => s + f.size, 0),
|
|
330
387
|
files: files.map((f) => ({ path: f.relativePath, size: f.size })),
|
|
@@ -338,9 +395,8 @@ async function deployToCloud(
|
|
|
338
395
|
|
|
339
396
|
const { data } = await createRes.json() as { data: { deploymentId: string; uploadUrl: string } }
|
|
340
397
|
|
|
341
|
-
// Upload files (simplified — in production, use multipart or presigned URLs)
|
|
342
398
|
for (const file of files) {
|
|
343
|
-
const content =
|
|
399
|
+
const content = readFileSync(file.absolutePath)
|
|
344
400
|
await fetch(`${data.uploadUrl}/${file.relativePath}`, {
|
|
345
401
|
method: "PUT",
|
|
346
402
|
headers: {
|
|
@@ -352,16 +408,20 @@ async function deployToCloud(
|
|
|
352
408
|
})
|
|
353
409
|
}
|
|
354
410
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
411
|
+
await fetch(
|
|
412
|
+
`${target.apiBaseUrl}/api/v1/projects/${target.projectRef}/deployments/${data.deploymentId}/finalize`,
|
|
413
|
+
{
|
|
414
|
+
method: "POST",
|
|
415
|
+
headers: {
|
|
416
|
+
Authorization: `Bearer ${token}`,
|
|
417
|
+
...(target.orgId ? { "X-Org-Id": target.orgId } : {}),
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
)
|
|
360
421
|
}
|
|
361
422
|
|
|
362
423
|
function deploySelfHost(outputDir: string, cwd: string): void {
|
|
363
424
|
const servingDir = join(cwd, ".supatype", "static")
|
|
364
|
-
const { mkdirSync, cpSync } = require("node:fs") as typeof import("node:fs")
|
|
365
425
|
mkdirSync(servingDir, { recursive: true })
|
|
366
426
|
cpSync(outputDir, servingDir, { recursive: true })
|
|
367
427
|
console.log(`Static files deployed to ${servingDir}`)
|
|
@@ -375,8 +435,6 @@ interface FileEntry {
|
|
|
375
435
|
|
|
376
436
|
function collectFiles(dir: string, baseDir: string): FileEntry[] {
|
|
377
437
|
const files: FileEntry[] = []
|
|
378
|
-
const { readdirSync, statSync } = require("node:fs") as typeof import("node:fs")
|
|
379
|
-
const { relative } = require("node:path") as typeof import("node:path")
|
|
380
438
|
|
|
381
439
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
382
440
|
const fullPath = join(dir, entry.name)
|
package/src/commands/dev.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
|
16
16
|
import { homedir } from "node:os"
|
|
17
17
|
import { isAbsolute, join, relative, resolve } from "node:path"
|
|
18
18
|
import { loadConfig } from "../config.js"
|
|
19
|
+
import type { ExtractedSchemaAstV2 } from "../schema-ast-v2.js"
|
|
19
20
|
import {
|
|
20
21
|
functionsPathCandidatesFromProject,
|
|
21
22
|
resolveRuntimeProvider,
|
|
@@ -572,14 +573,27 @@ async function runSchemaPush(
|
|
|
572
573
|
|
|
573
574
|
// Push schema.
|
|
574
575
|
console.log("[supatype] Applying schema...")
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
)
|
|
580
|
-
|
|
576
|
+
const { ensureEngine, engineRequest } = await import("../engine-client.js")
|
|
577
|
+
const { writeSchemaSourcePushArtifacts } = await import("../schema-sources.js")
|
|
578
|
+
await ensureEngine()
|
|
579
|
+
const pgSchema = config?.schema?.pg_schema ?? "public"
|
|
580
|
+
const sources = writeSchemaSourcePushArtifacts(cwd)
|
|
581
|
+
try {
|
|
582
|
+
await engineRequest("/push", {
|
|
583
|
+
ast,
|
|
584
|
+
database_url: dbURL,
|
|
585
|
+
schema: pgSchema,
|
|
586
|
+
force: true,
|
|
587
|
+
...(sources
|
|
588
|
+
? {
|
|
589
|
+
schema_sources_gz_base64: sources.payload.dataBase64,
|
|
590
|
+
schema_sources_manifest: sources.payload.manifest,
|
|
591
|
+
}
|
|
592
|
+
: {}),
|
|
593
|
+
})
|
|
594
|
+
} catch (err) {
|
|
581
595
|
_lastFailedAst = astJson
|
|
582
|
-
throw
|
|
596
|
+
throw err
|
|
583
597
|
}
|
|
584
598
|
_lastPushedAst = astJson
|
|
585
599
|
_lastFailedAst = null
|
|
@@ -919,9 +933,9 @@ const EXTENSION_FIELDS: Record<string, { ext: string; fallback: AstField }> = {
|
|
|
919
933
|
}
|
|
920
934
|
|
|
921
935
|
function adaptUnsupportedKinds(
|
|
922
|
-
ast:
|
|
936
|
+
ast: ExtractedSchemaAstV2,
|
|
923
937
|
skipKinds: ReadonlySet<string>,
|
|
924
|
-
): { filtered:
|
|
938
|
+
): { filtered: ExtractedSchemaAstV2; adapted: string[] } {
|
|
925
939
|
const adapted: string[] = []
|
|
926
940
|
if (!ast || typeof ast !== "object") return { filtered: ast, adapted }
|
|
927
941
|
const schema = ast as AstSchema
|
|
@@ -941,7 +955,7 @@ function adaptUnsupportedKinds(
|
|
|
941
955
|
return { ...model, fields }
|
|
942
956
|
})
|
|
943
957
|
|
|
944
|
-
return { filtered: { ...schema, models }, adapted }
|
|
958
|
+
return { filtered: { ...schema, models } as ExtractedSchemaAstV2, adapted }
|
|
945
959
|
}
|
|
946
960
|
|
|
947
961
|
// ---------------------------------------------------------------------------
|
package/src/commands/diff.ts
CHANGED
|
@@ -1,63 +1,62 @@
|
|
|
1
1
|
import type { Command } from "commander"
|
|
2
2
|
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { resolveRuntimeProvider, schemaPathFromProject } from "../project-config.js"
|
|
4
|
+
import { printDiffOperations, printDiffWarnings } from "../diff-output.js"
|
|
5
|
+
import { resolveTarget, targetSchemaDiff, schemaPgSchema } from "../resolve-target.js"
|
|
6
|
+
import { loadProjectLink } from "../link.js"
|
|
6
7
|
|
|
7
8
|
export function registerDiff(program: Command): void {
|
|
8
9
|
program
|
|
9
10
|
.command("diff")
|
|
10
11
|
.description("Show planned schema changes without applying them (dry run)")
|
|
11
12
|
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
12
|
-
.
|
|
13
|
+
.option("--env <name>", "Target environment when linked")
|
|
14
|
+
.option("--direct", "Use local engine subprocess")
|
|
15
|
+
.action(async (opts: { connection?: string; env?: string; direct?: boolean }) => {
|
|
13
16
|
const cwd = process.cwd()
|
|
14
17
|
const config = loadConfig(cwd)
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
await ensureEngine()
|
|
18
|
+
const pgSchema = schemaPgSchema(cwd)
|
|
18
19
|
|
|
19
20
|
console.log("Loading schema...")
|
|
20
21
|
const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
database_url: connection,
|
|
25
|
-
schema: "public",
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
const ops = diff.operations ?? []
|
|
29
|
-
printDiffWarnings(diff)
|
|
23
|
+
const linked = loadProjectLink(cwd)
|
|
24
|
+
const useDirect = opts.direct || Boolean(opts.connection)
|
|
30
25
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
26
|
+
if (linked && !useDirect && !opts.connection) {
|
|
27
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
28
|
+
const diff = await targetSchemaDiff(target, ast, { schema: pgSchema })
|
|
29
|
+
printDiffWarnings(diff)
|
|
30
|
+
printDiffOperations(diff)
|
|
33
31
|
return
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const r = op.risk ?? "safe"
|
|
54
|
-
console.log(` [${symbol[r]}] ${op.description} (${legend[r]})`)
|
|
34
|
+
if (
|
|
35
|
+
!opts.connection &&
|
|
36
|
+
!useDirect &&
|
|
37
|
+
resolveRuntimeProvider(config) === "docker"
|
|
38
|
+
) {
|
|
39
|
+
const localTarget = resolveTarget(cwd, { env: opts.env })
|
|
40
|
+
if (localTarget.mode === "local" && localTarget.token) {
|
|
41
|
+
const diff = await targetSchemaDiff(localTarget, ast, { schema: pgSchema })
|
|
42
|
+
printDiffWarnings(diff)
|
|
43
|
+
printDiffOperations(diff)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
const { diffSchemaDocker } = await import("../dev-compose.js")
|
|
47
|
+
const diff = await diffSchemaDocker(cwd, config)
|
|
48
|
+
printDiffWarnings(diff)
|
|
49
|
+
printDiffOperations(diff)
|
|
50
|
+
return
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const target = resolveTarget(cwd, {
|
|
54
|
+
env: opts.env,
|
|
55
|
+
direct: true,
|
|
56
|
+
connection: opts.connection,
|
|
57
|
+
})
|
|
58
|
+
const diff = await targetSchemaDiff(target, ast, { schema: pgSchema })
|
|
59
|
+
printDiffWarnings(diff)
|
|
60
|
+
printDiffOperations(diff)
|
|
62
61
|
})
|
|
63
62
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
3
|
+
import { schemaPathFromProject } from "../project-config.js"
|
|
4
|
+
import { resolveTarget, targetSchemaDoctor, schemaPgSchema } from "../resolve-target.js"
|
|
5
|
+
import { loadProjectLink } from "../link.js"
|
|
6
|
+
import { resolveHostEngineDatabaseUrl } from "../dev-compose.js"
|
|
7
|
+
|
|
8
|
+
interface DoctorItem {
|
|
9
|
+
kind: string
|
|
10
|
+
table: string
|
|
11
|
+
name: string
|
|
12
|
+
fields: string[]
|
|
13
|
+
message: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface DoctorReport {
|
|
17
|
+
missing: DoctorItem[]
|
|
18
|
+
staleManaged: DoctorItem[]
|
|
19
|
+
unmanagedDrift: DoctorItem[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function printSection(title: string, items: DoctorItem[]): void {
|
|
23
|
+
if (items.length === 0) return
|
|
24
|
+
console.log(`\n${title} (${items.length}):\n`)
|
|
25
|
+
for (const item of items) {
|
|
26
|
+
const fields = item.fields.length > 0 ? ` (${item.fields.join(", ")})` : ""
|
|
27
|
+
console.log(` • ${item.table}.${item.name}${fields}`)
|
|
28
|
+
console.log(` ${item.message}`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function registerDoctor(program: Command): void {
|
|
33
|
+
program
|
|
34
|
+
.command("doctor")
|
|
35
|
+
.description("Report schema drift between schema/index.ts and the live database")
|
|
36
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
37
|
+
.option("--env <name>", "Target environment when linked")
|
|
38
|
+
.option("--strict", "Exit non-zero when missing or stale managed drift exists")
|
|
39
|
+
.option("--no-cache", "Force full database introspection")
|
|
40
|
+
.option("--direct", "Use local engine subprocess")
|
|
41
|
+
.action(async (opts: {
|
|
42
|
+
connection?: string
|
|
43
|
+
env?: string
|
|
44
|
+
strict?: boolean
|
|
45
|
+
noCache?: boolean
|
|
46
|
+
direct?: boolean
|
|
47
|
+
}) => {
|
|
48
|
+
const cwd = process.cwd()
|
|
49
|
+
const config = loadConfig(cwd)
|
|
50
|
+
const pgSchema = schemaPgSchema(cwd)
|
|
51
|
+
|
|
52
|
+
console.log("Loading schema...")
|
|
53
|
+
const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
|
|
54
|
+
|
|
55
|
+
let report: DoctorReport
|
|
56
|
+
|
|
57
|
+
const linked = loadProjectLink(cwd)
|
|
58
|
+
if (linked && !opts.direct && !opts.connection) {
|
|
59
|
+
const target = resolveTarget(cwd, { env: opts.env })
|
|
60
|
+
report = (await targetSchemaDoctor(target, ast, {
|
|
61
|
+
noCache: opts.noCache,
|
|
62
|
+
schema: pgSchema,
|
|
63
|
+
})) as DoctorReport
|
|
64
|
+
} else if (!opts.direct && !opts.connection) {
|
|
65
|
+
const connection = await resolveHostEngineDatabaseUrl(cwd, config, opts.connection)
|
|
66
|
+
const target = resolveTarget(cwd, { direct: true, connection })
|
|
67
|
+
report = (await targetSchemaDoctor(target, ast, {
|
|
68
|
+
noCache: opts.noCache,
|
|
69
|
+
schema: pgSchema,
|
|
70
|
+
})) as DoctorReport
|
|
71
|
+
void connection
|
|
72
|
+
} else {
|
|
73
|
+
const target = resolveTarget(cwd, {
|
|
74
|
+
env: opts.env,
|
|
75
|
+
direct: true,
|
|
76
|
+
connection: opts.connection,
|
|
77
|
+
})
|
|
78
|
+
report = (await targetSchemaDoctor(target, ast, {
|
|
79
|
+
noCache: opts.noCache,
|
|
80
|
+
schema: pgSchema,
|
|
81
|
+
})) as DoctorReport
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
printSection("Missing (in AST, not in DB)", report.missing ?? [])
|
|
85
|
+
printSection("Stale managed (stamped, not in AST)", report.staleManaged ?? [])
|
|
86
|
+
printSection("Unmanaged drift (manual decision)", report.unmanagedDrift ?? [])
|
|
87
|
+
|
|
88
|
+
const missing = report.missing?.length ?? 0
|
|
89
|
+
const stale = report.staleManaged?.length ?? 0
|
|
90
|
+
const unmanaged = report.unmanagedDrift?.length ?? 0
|
|
91
|
+
|
|
92
|
+
if (missing + stale + unmanaged === 0) {
|
|
93
|
+
console.log("\nNo drift detected.")
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`\nSummary: ${missing} missing, ${stale} stale managed, ${unmanaged} unmanaged`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (opts.strict && (missing > 0 || stale > 0)) {
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
}
|