@supatype/cli 0.1.0-alpha.7 → 0.1.0-alpha.8
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 +67 -62
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/app/proxy-dev-app.d.ts +13 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -0
- package/dist/app/proxy-dev-app.js +53 -0
- package/dist/app/proxy-dev-app.js.map +1 -0
- package/dist/binary-cache.d.ts +5 -0
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +13 -0
- package/dist/binary-cache.js.map +1 -1
- package/dist/commands/cloud.d.ts +11 -3
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +33 -25
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +3 -17
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -3
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +66 -59
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +11 -1
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/init.js +16 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +42 -12
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +16 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/dev-compose.d.ts +17 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +374 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/diff-output.d.ts +4 -0
- package/dist/diff-output.d.ts.map +1 -0
- package/dist/diff-output.js +12 -0
- package/dist/diff-output.js.map +1 -0
- package/dist/docker-postgres.d.ts +21 -3
- package/dist/docker-postgres.d.ts.map +1 -1
- package/dist/docker-postgres.js +130 -18
- package/dist/docker-postgres.js.map +1 -1
- package/dist/engine-client.d.ts +5 -3
- package/dist/engine-client.d.ts.map +1 -1
- package/dist/engine-client.js +2 -1
- package/dist/engine-client.js.map +1 -1
- package/dist/kong-config.d.ts +4 -0
- package/dist/kong-config.d.ts.map +1 -1
- package/dist/kong-config.js +12 -1
- package/dist/kong-config.js.map +1 -1
- package/dist/process-manager.d.ts +2 -0
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +16 -1
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +21 -1
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +15 -0
- package/dist/project-config.js.map +1 -1
- package/dist/runtime-routes.d.ts +9 -0
- package/dist/runtime-routes.d.ts.map +1 -1
- package/dist/runtime-routes.js +75 -12
- package/dist/runtime-routes.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +127 -0
- package/dist/schema-ast-v2.d.ts.map +1 -0
- package/dist/schema-ast-v2.js +226 -0
- package/dist/schema-ast-v2.js.map +1 -0
- package/dist/self-host-compose.d.ts +12 -4
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +146 -35
- package/dist/self-host-compose.js.map +1 -1
- package/dist/studio-admin-roles.d.ts +7 -0
- package/dist/studio-admin-roles.d.ts.map +1 -0
- package/dist/studio-admin-roles.js +14 -0
- package/dist/studio-admin-roles.js.map +1 -0
- package/dist/studio-dev-server.d.ts +22 -0
- package/dist/studio-dev-server.d.ts.map +1 -0
- package/dist/studio-dev-server.js +28 -0
- package/dist/studio-dev-server.js.map +1 -0
- package/dist/type-extractor.d.ts +3 -30
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +485 -148
- package/dist/type-extractor.js.map +1 -1
- package/dist/type-resolver.d.ts +33 -0
- package/dist/type-resolver.d.ts.map +1 -0
- package/dist/type-resolver.js +338 -0
- package/dist/type-resolver.js.map +1 -0
- package/package.json +1 -1
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/proxy-dev-app.ts +67 -0
- package/src/binary-cache.ts +20 -0
- package/src/commands/cloud.ts +40 -30
- package/src/commands/deploy.ts +3 -18
- package/src/commands/dev.ts +72 -69
- package/src/commands/diff.ts +11 -1
- package/src/commands/init.ts +16 -3
- package/src/commands/push.ts +49 -13
- package/src/commands/update.ts +17 -0
- package/src/dev-compose.ts +455 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +184 -27
- package/src/engine-client.ts +9 -4
- package/src/kong-config.ts +16 -1
- package/src/process-manager.ts +18 -1
- package/src/project-config.ts +34 -1
- package/src/runtime-routes.ts +87 -12
- package/src/schema-ast-v2.ts +324 -0
- package/src/self-host-compose.ts +168 -36
- package/src/studio-admin-roles.ts +16 -0
- package/src/studio-dev-server.ts +53 -0
- package/src/type-extractor.ts +649 -186
- package/src/type-resolver.ts +457 -0
- package/tests/config.test.ts +34 -3
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/normalize-admin-config.test.ts +48 -0
- package/tests/proxy-dev-app.test.ts +33 -0
- package/tests/runtime-contract.test.ts +119 -4
- package/tests/studio-admin-roles.test.ts +27 -0
- package/tests/type-extractor.test.ts +607 -23
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/commands/dev.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* supatype dev — start local Postgres, apply schema, run supatype-server.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Runtime provider (top-level `provider` or legacy `database.provider`):
|
|
5
|
+
* native — host Postgres + host server + host engine (default)
|
|
6
|
+
* docker — full self-host Compose stack (Kong :18473); see dev-compose.ts
|
|
7
7
|
*
|
|
8
8
|
* Edge functions (when a functions/ dir exists): Deno is resolved from the CDN cache
|
|
9
9
|
* (auto-download on miss). Self-host/cloud Docker stacks use supatype-server in-container;
|
|
@@ -18,6 +18,7 @@ import { isAbsolute, join, relative, resolve } from "node:path"
|
|
|
18
18
|
import { loadConfig } from "../config.js"
|
|
19
19
|
import {
|
|
20
20
|
functionsPathCandidatesFromProject,
|
|
21
|
+
resolveRuntimeProvider,
|
|
21
22
|
schemaPathFromProject,
|
|
22
23
|
type SupatypeProjectConfig,
|
|
23
24
|
} from "../project-config.js"
|
|
@@ -32,7 +33,9 @@ import {
|
|
|
32
33
|
postgresArchiveTag,
|
|
33
34
|
} from "../binary-cache.js"
|
|
34
35
|
import { ensureBinary } from "../ensure-binary.js"
|
|
36
|
+
import { startProxyDevApp } from "../app/proxy-dev-app.js"
|
|
35
37
|
import { ProcessManager } from "../process-manager.js"
|
|
38
|
+
import { startStudioViteDevServer } from "../studio-dev-server.js"
|
|
36
39
|
import { localStorageEnv } from "../local-storage.js"
|
|
37
40
|
import {
|
|
38
41
|
initdb,
|
|
@@ -42,15 +45,6 @@ import {
|
|
|
42
45
|
isPortInUse,
|
|
43
46
|
pgSpawnEnv,
|
|
44
47
|
} from "../postgres-ctl.js"
|
|
45
|
-
import {
|
|
46
|
-
dockerPgStart,
|
|
47
|
-
dockerPgStop,
|
|
48
|
-
dockerPgWaitReady,
|
|
49
|
-
dockerDbUrl,
|
|
50
|
-
} from "../docker-postgres.js"
|
|
51
|
-
|
|
52
|
-
const DEFAULT_DOCKER_IMAGE = "supatype/postgres:17-latest"
|
|
53
|
-
|
|
54
48
|
/** Map `email.smtp` from supatype.config.ts into GOTRUE_SMTP_* for the embedded GoTrue process. */
|
|
55
49
|
function gotrueSMTPFromEmailConfig(email: SupatypeProjectConfig["email"] | undefined): Record<string, string> {
|
|
56
50
|
const s = email?.smtp
|
|
@@ -90,7 +84,13 @@ export function registerDev(program: Command): void {
|
|
|
90
84
|
const projectName = config.project.name
|
|
91
85
|
const serverPort = opts.port ?? String(config.server.port ?? 54321)
|
|
92
86
|
const postgrestPort = String(config.server.postgrestPort ?? 3001)
|
|
93
|
-
const provider = config
|
|
87
|
+
const provider = resolveRuntimeProvider(config)
|
|
88
|
+
|
|
89
|
+
if (provider === "docker") {
|
|
90
|
+
const { runDevCompose } = await import("../dev-compose.js")
|
|
91
|
+
await runDevCompose(cwd, config, { watch: opts.watch !== false })
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
94
|
|
|
95
95
|
// ── 2. Resolve engine + server binaries ──────────────────────────────
|
|
96
96
|
console.log(`[supatype] Resolving component binaries for "${projectName}"...`)
|
|
@@ -136,22 +136,12 @@ export function registerDev(program: Command): void {
|
|
|
136
136
|
// ── 5–7. Start Postgres ───────────────────────────────────────────────
|
|
137
137
|
let dbURL: string
|
|
138
138
|
let stopPostgres: () => void | Promise<void>
|
|
139
|
+
const pgPassword = "postgres"
|
|
139
140
|
// pgBinDir is set on the native path and used to add DLL search path for
|
|
140
141
|
// PostgREST on Windows (PostgREST links against libpq + SSL from MinGW).
|
|
141
142
|
let pgBinDir: string | null = null
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
console.log(
|
|
145
|
-
"[supatype] database.provider \"docker\" — Postgres runs in Docker; engine and supatype-server stay native.",
|
|
146
|
-
)
|
|
147
|
-
const image = config.database.image ?? DEFAULT_DOCKER_IMAGE
|
|
148
|
-
console.log(`[supatype] Starting Postgres via Docker (${image})...`)
|
|
149
|
-
dockerPgStart({ image, projectName, port: pgPort })
|
|
150
|
-
await dockerPgWaitReady(projectName, 90_000)
|
|
151
|
-
console.log("[supatype] Postgres is ready.")
|
|
152
|
-
dbURL = dockerDbUrl(projectName, pgPort)
|
|
153
|
-
stopPostgres = () => dockerPgStop(projectName)
|
|
154
|
-
} else {
|
|
144
|
+
{
|
|
155
145
|
// native — resolve pg bin dir and manage with pg_ctl
|
|
156
146
|
pgBinDir = await resolvePgBinDir(config)
|
|
157
147
|
const dataDir = config.database.data_dir ?? join(stateRoot, "data")
|
|
@@ -167,7 +157,7 @@ export function registerDev(program: Command): void {
|
|
|
167
157
|
dbURL = `postgres://postgres:postgres@127.0.0.1:${pgPort}/${projectName}?sslmode=disable`
|
|
168
158
|
stopPostgres = () => pgStop(pgOpts)
|
|
169
159
|
|
|
170
|
-
// Create project database if it doesn't exist.
|
|
160
|
+
// Create project database if it doesn't exist (native only).
|
|
171
161
|
const psqlBin = join(pgBinDir, process.platform === "win32" ? "psql.exe" : "psql")
|
|
172
162
|
const createdbBin = join(pgBinDir, process.platform === "win32" ? "createdb.exe" : "createdb")
|
|
173
163
|
const pgConnArgs = ["-h", "127.0.0.1", "-p", String(pgPort), "-U", "postgres"]
|
|
@@ -225,7 +215,8 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
225
215
|
|
|
226
216
|
// ── 8. GoTrue migrations (auth.users before engine studio SQL) ─────────
|
|
227
217
|
console.log("[supatype] Running GoTrue migrations...")
|
|
228
|
-
|
|
218
|
+
const migrateEnv = gotrueMigrateEnv(serverPort, dbURL, LOCAL_JWT_SECRET)
|
|
219
|
+
runGotrueMigrations(serverBin, migrateEnv)
|
|
229
220
|
|
|
230
221
|
// ── 9. Engine: apply schema ───────────────────────────────────────────
|
|
231
222
|
const schemaPath = schemaPathFromProject(config, cwd)
|
|
@@ -236,9 +227,9 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
236
227
|
|
|
237
228
|
const localStoragePath = config.storage?.provider !== "s3" ? join(stateRoot, "storage") : undefined
|
|
238
229
|
// Native Postgres builds don't include PostGIS — skip geo fields rather than failing.
|
|
239
|
-
const skipFieldKinds: ReadonlySet<string> =
|
|
230
|
+
const skipFieldKinds: ReadonlySet<string> = new Set(["geo", "vector"])
|
|
240
231
|
|
|
241
|
-
await runSchemaPush(cwd, engineBin, schemaPath, dbURL, manifestPath, adminConfigPath, localStoragePath, skipFieldKinds).catch(
|
|
232
|
+
await runSchemaPush(cwd, engineBin, schemaPath, dbURL, manifestPath, adminConfigPath, localStoragePath, skipFieldKinds, config).catch(
|
|
242
233
|
(e: unknown) => console.error("[supatype] Initial schema push failed:", (e as Error).message),
|
|
243
234
|
)
|
|
244
235
|
|
|
@@ -325,8 +316,11 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
325
316
|
? { SUPATYPE_VITE_DEV_URL: config.app.vite_dev_url.trim() }
|
|
326
317
|
: {}),
|
|
327
318
|
// GoTrue required fields (sensible local-dev defaults)
|
|
319
|
+
GOTRUE_DB_DATABASE_URL: authDbURL,
|
|
328
320
|
DATABASE_URL: authDbURL,
|
|
329
321
|
SUPATYPE_SQL_DATABASE_URL: dbURL,
|
|
322
|
+
PGSSLMODE: "disable",
|
|
323
|
+
GOTRUE_DB_NAMESPACE: "auth",
|
|
330
324
|
GOTRUE_DB_DRIVER: "postgres",
|
|
331
325
|
GOTRUE_JWT_SECRET: LOCAL_JWT_SECRET,
|
|
332
326
|
GOTRUE_JWT_EXP: "3600",
|
|
@@ -400,7 +394,7 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
400
394
|
|
|
401
395
|
const postgrestEnv: Record<string, string> = {
|
|
402
396
|
PGRST_DB_URI: dbURL,
|
|
403
|
-
PGRST_DB_SCHEMA: "public, supatype",
|
|
397
|
+
PGRST_DB_SCHEMA: "public, supatype, graphql_public",
|
|
404
398
|
PGRST_DB_ANON_ROLE: "anon",
|
|
405
399
|
PGRST_SERVER_PORT: postgrestPort,
|
|
406
400
|
PGRST_SERVER_HOST: "127.0.0.1",
|
|
@@ -445,39 +439,19 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
445
439
|
|
|
446
440
|
const studioOverride = config.overrides?.studio
|
|
447
441
|
if (studioOverride) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
pidDir,
|
|
458
|
-
cwd: studioDir,
|
|
459
|
-
colour: "\x1b[35m",
|
|
460
|
-
env: {
|
|
461
|
-
// Point the studio at the Vite dev server (same origin as the
|
|
462
|
-
// browser) so all API requests are same-origin — CORS never fires.
|
|
463
|
-
// Vite's dev proxy (configured via SUPATYPE_PROXY_TARGET) then
|
|
464
|
-
// forwards those requests server-side to the actual backend.
|
|
465
|
-
VITE_SUPATYPE_URL: `http://localhost:${studioPort}`,
|
|
466
|
-
SUPATYPE_PROXY_TARGET: `http://localhost:${serverPort}`,
|
|
467
|
-
// Studio is a developer tool — use service_role key to bypass
|
|
468
|
-
// RLS so all tables and rows are visible regardless of policies.
|
|
469
|
-
VITE_SUPATYPE_ANON_KEY: serviceRoleKey,
|
|
470
|
-
VITE_SUPATYPE_SERVICE_ROLE_KEY: serviceRoleKey,
|
|
471
|
-
VITE_BASE_PATH: "/",
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
)
|
|
475
|
-
studioProc.start()
|
|
476
|
-
} else {
|
|
477
|
-
console.warn(`[supatype] ⚠ Studio override set but vite not found at ${viteJs}. Run: pnpm install`)
|
|
478
|
-
}
|
|
442
|
+
studioProc = startStudioViteDevServer({
|
|
443
|
+
cwd,
|
|
444
|
+
studioOverride,
|
|
445
|
+
pidDir,
|
|
446
|
+
serviceRoleKey,
|
|
447
|
+
proxyTarget: `http://localhost:${serverPort}`,
|
|
448
|
+
viteSupatypeUrl: `http://localhost:${studioPort}`,
|
|
449
|
+
})
|
|
450
|
+
studioProc?.start()
|
|
479
451
|
}
|
|
480
452
|
|
|
453
|
+
const appProc = startProxyDevApp(cwd, config, pidDir)
|
|
454
|
+
|
|
481
455
|
// ── Print status ──────────────────────────────────────────────────────
|
|
482
456
|
console.log(`
|
|
483
457
|
[supatype] Services running:
|
|
@@ -505,6 +479,7 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
505
479
|
serverProc.stop(),
|
|
506
480
|
postgrestProc?.stop(),
|
|
507
481
|
studioProc?.stop(),
|
|
482
|
+
appProc?.stop(),
|
|
508
483
|
])
|
|
509
484
|
await stopPostgres()
|
|
510
485
|
process.exit(0)
|
|
@@ -527,7 +502,7 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticate
|
|
|
527
502
|
debounceTimer = setTimeout(() => {
|
|
528
503
|
debounceTimer = null
|
|
529
504
|
console.log(`\n[supatype] Change detected in ${filename}, checking schema...`)
|
|
530
|
-
runSchemaPush(cwd, engineBin, schemaPath, dbURL, manifestPath, adminConfigPath, localStoragePath, skipFieldKinds).catch((e: unknown) =>
|
|
505
|
+
runSchemaPush(cwd, engineBin, schemaPath, dbURL, manifestPath, adminConfigPath, localStoragePath, skipFieldKinds, config).catch((e: unknown) =>
|
|
531
506
|
console.error("[supatype] Schema push failed:", (e as Error).message),
|
|
532
507
|
)
|
|
533
508
|
}, 300)
|
|
@@ -557,6 +532,7 @@ async function runSchemaPush(
|
|
|
557
532
|
adminConfigPath?: string,
|
|
558
533
|
storagePath?: string,
|
|
559
534
|
skipFieldKinds?: ReadonlySet<string>,
|
|
535
|
+
config?: import("../project-config.js").SupatypeProjectConfig,
|
|
560
536
|
): Promise<void> {
|
|
561
537
|
// Build AST JSON from schema file.
|
|
562
538
|
const { loadSchemaAst } = await import("../config.js")
|
|
@@ -590,7 +566,7 @@ async function runSchemaPush(
|
|
|
590
566
|
console.log("[supatype] Applying schema...")
|
|
591
567
|
const pushResult = spawnSync(
|
|
592
568
|
engineBin,
|
|
593
|
-
["push", "-i", astPath, "--database-url", dbURL, "--force"],
|
|
569
|
+
["push", "-i", astPath, "--database-url", dbURL, "--force", "--non-interactive"],
|
|
594
570
|
{ cwd, stdio: "inherit", encoding: "utf8" },
|
|
595
571
|
)
|
|
596
572
|
if (pushResult.status !== 0) {
|
|
@@ -640,7 +616,15 @@ async function runSchemaPush(
|
|
|
640
616
|
{ cwd, stdio: "pipe", encoding: "utf8" },
|
|
641
617
|
)
|
|
642
618
|
if (adminResult.status === 0 && adminResult.stdout) {
|
|
643
|
-
|
|
619
|
+
const { withAdminRoles } = await import("../studio-admin-roles.js")
|
|
620
|
+
let admin: unknown
|
|
621
|
+
try {
|
|
622
|
+
admin = JSON.parse(adminResult.stdout) as unknown
|
|
623
|
+
} catch {
|
|
624
|
+
admin = adminResult.stdout
|
|
625
|
+
}
|
|
626
|
+
const merged = config ? withAdminRoles(admin, config) : admin
|
|
627
|
+
writeFileSync(adminConfigPath, `${JSON.stringify(merged, null, 2)}\n`)
|
|
644
628
|
}
|
|
645
629
|
}
|
|
646
630
|
|
|
@@ -960,20 +944,39 @@ function adaptUnsupportedKinds(
|
|
|
960
944
|
// .env loader
|
|
961
945
|
// ---------------------------------------------------------------------------
|
|
962
946
|
|
|
947
|
+
/** Minimal GoTrue env for `migrate` (matches required fields in serverEnv below). */
|
|
948
|
+
function gotrueMigrateEnv(
|
|
949
|
+
serverPort: string,
|
|
950
|
+
sqlDbURL: string,
|
|
951
|
+
jwtSecret: string,
|
|
952
|
+
): Record<string, string> {
|
|
953
|
+
const base = `http://localhost:${serverPort}`
|
|
954
|
+
return {
|
|
955
|
+
// envconfig: gotrue + DB.DATABASE_URL → GOTRUE_DB_DATABASE_URL
|
|
956
|
+
GOTRUE_DB_DATABASE_URL: sqlDbURL,
|
|
957
|
+
DATABASE_URL: sqlDbURL,
|
|
958
|
+
GOTRUE_DB_DRIVER: "postgres",
|
|
959
|
+
GOTRUE_DB_NAMESPACE: "auth",
|
|
960
|
+
PGSSLMODE: "disable",
|
|
961
|
+
GOTRUE_JWT_SECRET: jwtSecret,
|
|
962
|
+
API_EXTERNAL_URL: `${base}/auth/v1`,
|
|
963
|
+
GOTRUE_API_HOST: "localhost",
|
|
964
|
+
GOTRUE_SITE_URL: base,
|
|
965
|
+
GOTRUE_MAILER_AUTOCONFIRM: "true",
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
963
969
|
/** Apply GoTrue DDL (auth.users, etc.) before engine push references auth schema. */
|
|
964
970
|
function runGotrueMigrations(
|
|
965
971
|
serverBin: string,
|
|
966
|
-
|
|
967
|
-
jwtSecret: string,
|
|
972
|
+
migrateEnv: Record<string, string>,
|
|
968
973
|
): void {
|
|
969
974
|
const result = spawnSync(serverBin, ["migrate"], {
|
|
970
975
|
stdio: "pipe",
|
|
971
976
|
encoding: "utf8",
|
|
972
977
|
env: {
|
|
973
978
|
...process.env,
|
|
974
|
-
|
|
975
|
-
GOTRUE_DB_DRIVER: "postgres",
|
|
976
|
-
GOTRUE_JWT_SECRET: jwtSecret,
|
|
979
|
+
...migrateEnv,
|
|
977
980
|
},
|
|
978
981
|
})
|
|
979
982
|
if (result.status !== 0) {
|
package/src/commands/diff.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Command } from "commander"
|
|
|
2
2
|
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
3
3
|
import { connectionString, schemaPathFromProject } from "../project-config.js"
|
|
4
4
|
import { ensureEngine, engineRequest, type DiffResult } from "../engine-client.js"
|
|
5
|
+
import { printDiffWarnings } from "../diff-output.js"
|
|
5
6
|
|
|
6
7
|
export function registerDiff(program: Command): void {
|
|
7
8
|
program
|
|
@@ -25,6 +26,7 @@ export function registerDiff(program: Command): void {
|
|
|
25
26
|
})
|
|
26
27
|
|
|
27
28
|
const ops = diff.operations ?? []
|
|
29
|
+
printDiffWarnings(diff)
|
|
28
30
|
|
|
29
31
|
if (ops.length === 0) {
|
|
30
32
|
console.log("No changes.")
|
|
@@ -34,9 +36,17 @@ export function registerDiff(program: Command): void {
|
|
|
34
36
|
const symbol: Record<NonNullable<DiffResult["operations"][number]["risk"]>, string> = {
|
|
35
37
|
safe: "+",
|
|
36
38
|
warn: "~",
|
|
39
|
+
cautious: "~",
|
|
37
40
|
danger: "!",
|
|
41
|
+
destructive: "!",
|
|
42
|
+
}
|
|
43
|
+
const legend: typeof symbol = {
|
|
44
|
+
safe: "safe",
|
|
45
|
+
warn: "caution",
|
|
46
|
+
cautious: "caution",
|
|
47
|
+
danger: "DANGER",
|
|
48
|
+
destructive: "DANGER",
|
|
38
49
|
}
|
|
39
|
-
const legend: typeof symbol = { safe: "safe", warn: "caution", danger: "DANGER" }
|
|
40
50
|
|
|
41
51
|
console.log(`\n${ops.length} change(s):\n`)
|
|
42
52
|
for (const op of ops) {
|
package/src/commands/init.ts
CHANGED
|
@@ -134,9 +134,10 @@ function tsConfigTemplate(projectName: string, mode: "dev" | "standalone", versi
|
|
|
134
134
|
|
|
135
135
|
export default defineConfig({
|
|
136
136
|
project: { name: "${projectName}" },
|
|
137
|
+
provider: "native",
|
|
138
|
+
// provider: "docker" // full self-host Compose stack (Kong :18473)
|
|
137
139
|
database: {
|
|
138
140
|
provider: "native",
|
|
139
|
-
// provider: "docker", image: "supatype/postgres:17-latest" // full extensions stack via Docker
|
|
140
141
|
},
|
|
141
142
|
server: {
|
|
142
143
|
mode: "${mode}",
|
|
@@ -145,7 +146,7 @@ ${domainField} },
|
|
|
145
146
|
app: {
|
|
146
147
|
mode: "none",
|
|
147
148
|
// mode: "static", static_dir: "./public", // supatype app add --static ./public
|
|
148
|
-
// mode: "proxy", upstream: "http://localhost:3000",
|
|
149
|
+
// mode: "proxy", upstream: "http://localhost:3000", start: "dev",
|
|
149
150
|
// vite_dev_url: "http://127.0.0.1:5173", // dev HMR at /_vite (when using a separate Vite server)
|
|
150
151
|
},
|
|
151
152
|
versions: {
|
|
@@ -163,7 +164,7 @@ ${domainField} },
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
function schemaTemplate(): string {
|
|
166
|
-
return `import type { Model, Public, Owner, Role, SupatypeAuthUserId, Unique, Email } from "@supatype/types"
|
|
167
|
+
return `import type { Model, Public, Owner, Role, SupatypeAuthUserId, Unique, Email, UUID } from "@supatype/types"
|
|
167
168
|
|
|
168
169
|
export type User = Model<{
|
|
169
170
|
id: SupatypeAuthUserId
|
|
@@ -179,6 +180,18 @@ export type User = Model<{
|
|
|
179
180
|
delete: Role<"admin">
|
|
180
181
|
}
|
|
181
182
|
}>
|
|
183
|
+
|
|
184
|
+
/** Example singleton global — editable in Studio under Settings. */
|
|
185
|
+
export type SiteSettings = Model<{
|
|
186
|
+
id: UUID
|
|
187
|
+
site_name: string
|
|
188
|
+
}, {
|
|
189
|
+
singleton: true
|
|
190
|
+
access: {
|
|
191
|
+
read: Public
|
|
192
|
+
update: Role<"admin">
|
|
193
|
+
}
|
|
194
|
+
}>
|
|
182
195
|
`
|
|
183
196
|
}
|
|
184
197
|
|
package/src/commands/push.ts
CHANGED
|
@@ -3,11 +3,15 @@ import { mkdirSync, writeFileSync } from "node:fs"
|
|
|
3
3
|
import { createInterface } from "node:readline"
|
|
4
4
|
import { join } from "node:path"
|
|
5
5
|
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
6
|
-
import { connectionString, schemaPathFromProject, serverBaseUrl } from "../project-config.js"
|
|
6
|
+
import { connectionString, resolveRuntimeProvider, schemaPathFromProject, serverBaseUrl } from "../project-config.js"
|
|
7
|
+
import { isCloudLinked, pushSchemaToLinkedProject } from "./cloud.js"
|
|
7
8
|
import { ensureEngine, engineRequest, type DiffResult, type Operation } from "../engine-client.js"
|
|
9
|
+
import { printDiffWarnings } from "../diff-output.js"
|
|
8
10
|
import { signJwt } from "../jwt.js"
|
|
9
11
|
import { provisionBuckets } from "../storage-provision.js"
|
|
10
12
|
import { promptFirstAdminUser } from "./admin.js"
|
|
13
|
+
import { withAdminRoles } from "../studio-admin-roles.js"
|
|
14
|
+
import type { SupatypeProjectConfig } from "../project-config.js"
|
|
11
15
|
|
|
12
16
|
const DEV_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
|
|
13
17
|
|
|
@@ -21,7 +25,27 @@ export function registerPush(program: Command): void {
|
|
|
21
25
|
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
22
26
|
.action(async (opts: { yes?: boolean; connection?: string }) => {
|
|
23
27
|
const cwd = process.cwd()
|
|
28
|
+
|
|
29
|
+
if (isCloudLinked(cwd)) {
|
|
30
|
+
if (opts.connection) {
|
|
31
|
+
console.error("--connection is not allowed when linked to a cloud project (credentials stay server-side).")
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
await pushSchemaToLinkedProject(cwd, { force: opts.yes ?? true })
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
const config = loadConfig(cwd)
|
|
39
|
+
|
|
40
|
+
// Docker provider: the compose Postgres isn't published to the host, so
|
|
41
|
+
// apply the schema through the in-compose schema-engine (unless the user
|
|
42
|
+
// gave an explicit --connection to a reachable database).
|
|
43
|
+
if (!opts.connection && resolveRuntimeProvider(config) === "docker") {
|
|
44
|
+
const { pushSchemaDocker } = await import("../dev-compose.js")
|
|
45
|
+
await pushSchemaDocker(cwd, config)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
25
49
|
const connection = opts.connection ?? connectionString(config)
|
|
26
50
|
|
|
27
51
|
await ensureEngine()
|
|
@@ -37,18 +61,21 @@ export function registerPush(program: Command): void {
|
|
|
37
61
|
})
|
|
38
62
|
|
|
39
63
|
const ops = diff.operations ?? []
|
|
64
|
+
printDiffWarnings(diff)
|
|
40
65
|
|
|
41
66
|
if (ops.length === 0) {
|
|
42
67
|
console.log(
|
|
43
|
-
"Schema matches the database (no DDL).
|
|
68
|
+
"Schema matches the database (no DDL). Syncing Studio metadata...",
|
|
44
69
|
)
|
|
45
70
|
} else {
|
|
46
71
|
printDiff(ops)
|
|
47
72
|
|
|
48
|
-
const
|
|
49
|
-
|
|
73
|
+
const risky = ops.filter(
|
|
74
|
+
(o) => o.risk === "cautious" || o.risk === "destructive" || o.risk === "warn" || o.risk === "danger",
|
|
75
|
+
)
|
|
76
|
+
if (risky.length > 0 && !opts.yes) {
|
|
50
77
|
const confirmed = await confirm(
|
|
51
|
-
`\n${
|
|
78
|
+
`\n${risky.length} risky operation(s) above (type changes or data loss). Proceed? [y/N] `,
|
|
52
79
|
)
|
|
53
80
|
if (!confirmed) {
|
|
54
81
|
console.log("Aborted.")
|
|
@@ -71,14 +98,14 @@ export function registerPush(program: Command): void {
|
|
|
71
98
|
if (pushResult.status === "up_to_date") {
|
|
72
99
|
console.log(
|
|
73
100
|
pushResult.admin_refreshed
|
|
74
|
-
? "
|
|
101
|
+
? "Database schema unchanged — Studio metadata synced."
|
|
75
102
|
: "Schema is up to date.",
|
|
76
103
|
)
|
|
77
104
|
} else {
|
|
78
105
|
console.log(pushResult.message ?? "Migration applied.")
|
|
79
106
|
}
|
|
80
107
|
|
|
81
|
-
await writeLocalAdminConfig(ast)
|
|
108
|
+
await writeLocalAdminConfig(ast, config)
|
|
82
109
|
|
|
83
110
|
// After a DDL migration, check if this is the first push and offer to create an
|
|
84
111
|
// admin user if none exist (Gap Appendices task 48).
|
|
@@ -130,20 +157,29 @@ export function registerPush(program: Command): void {
|
|
|
130
157
|
console.log(genResult.message ?? "Types generated.")
|
|
131
158
|
}
|
|
132
159
|
|
|
133
|
-
|
|
160
|
+
const studioBase = baseUrl?.replace(/\/$/, "") ?? ""
|
|
161
|
+
if (studioBase) {
|
|
162
|
+
console.log(`\nStudio: ${studioBase}/studio/ — sign in with the admin user you created.`)
|
|
163
|
+
} else {
|
|
164
|
+
console.log("\nDone.")
|
|
165
|
+
}
|
|
134
166
|
})
|
|
135
167
|
}
|
|
136
168
|
|
|
137
169
|
function printDiff(ops: Operation[]): void {
|
|
138
|
-
const symbol: Record<
|
|
170
|
+
const symbol: Record<string, string> = {
|
|
139
171
|
safe: "+",
|
|
140
172
|
warn: "~",
|
|
173
|
+
cautious: "~",
|
|
141
174
|
danger: "!",
|
|
175
|
+
destructive: "!",
|
|
142
176
|
}
|
|
143
177
|
console.log(`\n${ops.length} change(s) planned:\n`)
|
|
144
178
|
for (const op of ops) {
|
|
145
|
-
const
|
|
146
|
-
|
|
179
|
+
const riskKey = op.risk ?? "safe"
|
|
180
|
+
const s = symbol[riskKey] ?? "?"
|
|
181
|
+
const label = op.warning ?? op.description ?? formatOperation(op)
|
|
182
|
+
console.log(` [${s}] ${label}`)
|
|
147
183
|
}
|
|
148
184
|
}
|
|
149
185
|
|
|
@@ -179,10 +215,10 @@ async function confirm(prompt: string): Promise<boolean> {
|
|
|
179
215
|
}
|
|
180
216
|
|
|
181
217
|
/** Write `.supatype/admin-config.json` for local Studio (same layout as `supatype dev`). */
|
|
182
|
-
async function writeLocalAdminConfig(ast: unknown): Promise<void> {
|
|
218
|
+
async function writeLocalAdminConfig(ast: unknown, config: SupatypeProjectConfig): Promise<void> {
|
|
183
219
|
const cwd = process.cwd()
|
|
184
220
|
const dir = join(cwd, ".supatype")
|
|
185
221
|
mkdirSync(dir, { recursive: true })
|
|
186
|
-
const admin = await engineRequest<unknown>("/admin", { ast })
|
|
222
|
+
const admin = withAdminRoles(await engineRequest<unknown>("/admin", { ast }), config)
|
|
187
223
|
writeFileSync(join(dir, "admin-config.json"), `${JSON.stringify(admin, null, 2)}\n`)
|
|
188
224
|
}
|
package/src/commands/update.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type { Command } from "commander"
|
|
|
7
7
|
import { existsSync, readFileSync, writeFileSync } from "node:fs"
|
|
8
8
|
import { basename, resolve } from "node:path"
|
|
9
9
|
import { loadConfig } from "../config.js"
|
|
10
|
+
import { resolveRuntimeProvider } from "../project-config.js"
|
|
11
|
+
import { runDockerCompose, writeSelfHostCompose } from "../self-host-compose.js"
|
|
10
12
|
import { download, currentPlatform, fetchAllLatestVersions, type Component } from "../binary-cache.js"
|
|
11
13
|
|
|
12
14
|
const CONFIG_CANDIDATES = ["supatype.config.ts", "supatype.config.js", "supatype.config.mjs"]
|
|
@@ -29,6 +31,21 @@ export function registerUpdate(program: Command): void {
|
|
|
29
31
|
.action(async (opts: { check: boolean }) => {
|
|
30
32
|
const cwd = process.cwd()
|
|
31
33
|
const config = loadConfig(cwd)
|
|
34
|
+
const provider = resolveRuntimeProvider(config)
|
|
35
|
+
|
|
36
|
+
if (provider === "docker") {
|
|
37
|
+
if (opts.check) {
|
|
38
|
+
console.log("Docker provider: run without --check to pull compose images (supatype self-host compose pull).")
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
const paths = writeSelfHostCompose(cwd, config, { devLocal: true })
|
|
42
|
+
console.log("Pulling self-host compose images...")
|
|
43
|
+
const status = runDockerCompose(paths.composePath, ["pull"], cwd)
|
|
44
|
+
if (status !== 0) process.exit(status)
|
|
45
|
+
console.log("Compose images updated.")
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
const platform = currentPlatform()
|
|
33
50
|
|
|
34
51
|
const components: Component[] = ["engine", "server", "postgres", "deno"]
|