@supatype/cli 0.1.0-alpha.10
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 +4 -0
- package/.turbo/turbo-test.log +221 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/bin/dev-entry.ts +2 -0
- package/bin/supatype.js +5 -0
- package/dist/app/framework.d.ts +44 -0
- package/dist/app/framework.d.ts.map +1 -0
- package/dist/app/framework.js +200 -0
- package/dist/app/framework.js.map +1 -0
- 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 +54 -0
- package/dist/app/proxy-dev-app.js.map +1 -0
- package/dist/app-config.d.ts +7 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +113 -0
- package/dist/app-config.js.map +1 -0
- package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/dist/augmentation-generator.d.ts +2 -0
- package/dist/augmentation-generator.d.ts.map +1 -0
- package/dist/augmentation-generator.js +111 -0
- package/dist/augmentation-generator.js.map +1 -0
- package/dist/binary-cache.d.ts +98 -0
- package/dist/binary-cache.d.ts.map +1 -0
- package/dist/binary-cache.js +687 -0
- package/dist/binary-cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/admin.d.ts +4 -0
- package/dist/commands/admin.d.ts.map +1 -0
- package/dist/commands/admin.js +271 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/app.d.ts +3 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +82 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +105 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/cloud.d.ts +23 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +254 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/db.d.ts +8 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +116 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/deploy-types.d.ts +14 -0
- package/dist/commands/deploy-types.d.ts.map +1 -0
- package/dist/commands/deploy-types.js +38 -0
- package/dist/commands/deploy-types.js.map +1 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +322 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +806 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/engine.d.ts +7 -0
- package/dist/commands/engine.d.ts.map +1 -0
- package/dist/commands/engine.js +27 -0
- package/dist/commands/engine.js.map +1 -0
- package/dist/commands/functions.d.ts +3 -0
- package/dist/commands/functions.d.ts.map +1 -0
- package/dist/commands/functions.js +749 -0
- package/dist/commands/functions.js.map +1 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +38 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +228 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keys.d.ts +4 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +57 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/logs.d.ts +6 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +52 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/migrate-from-v1.d.ts +5 -0
- package/dist/commands/migrate-from-v1.d.ts.map +1 -0
- package/dist/commands/migrate-from-v1.js +125 -0
- package/dist/commands/migrate-from-v1.js.map +1 -0
- package/dist/commands/migrate.d.ts +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +75 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/pg.d.ts +8 -0
- package/dist/commands/pg.d.ts.map +1 -0
- package/dist/commands/pg.js +102 -0
- package/dist/commands/pg.js.map +1 -0
- package/dist/commands/plugins.d.ts +3 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +431 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +12 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +179 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/seed.d.ts +5 -0
- package/dist/commands/seed.d.ts.map +1 -0
- package/dist/commands/seed.js +55 -0
- package/dist/commands/seed.js.map +1 -0
- package/dist/commands/self-host.d.ts +9 -0
- package/dist/commands/self-host.d.ts.map +1 -0
- package/dist/commands/self-host.js +310 -0
- package/dist/commands/self-host.js.map +1 -0
- package/dist/commands/self-update.d.ts +9 -0
- package/dist/commands/self-update.d.ts.map +1 -0
- package/dist/commands/self-update.js +33 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +70 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/types.d.ts +3 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +62 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +118 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/components.d.ts +5 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-compose.d.ts +19 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +468 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/dev-log-bus.d.ts +30 -0
- package/dist/dev-log-bus.d.ts.map +1 -0
- package/dist/dev-log-bus.js +87 -0
- package/dist/dev-log-bus.js.map +1 -0
- package/dist/dev-log-filter.d.ts +10 -0
- package/dist/dev-log-filter.d.ts.map +1 -0
- package/dist/dev-log-filter.js +36 -0
- package/dist/dev-log-filter.js.map +1 -0
- package/dist/dev-logo.d.ts +12 -0
- package/dist/dev-logo.d.ts.map +1 -0
- package/dist/dev-logo.js +57 -0
- package/dist/dev-logo.js.map +1 -0
- package/dist/dev-session.d.ts +26 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +106 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-shutdown.d.ts +9 -0
- package/dist/dev-shutdown.d.ts.map +1 -0
- package/dist/dev-shutdown.js +50 -0
- package/dist/dev-shutdown.js.map +1 -0
- package/dist/dev-task-colors.d.ts +14 -0
- package/dist/dev-task-colors.d.ts.map +1 -0
- package/dist/dev-task-colors.js +44 -0
- package/dist/dev-task-colors.js.map +1 -0
- package/dist/dev-tui.d.ts +24 -0
- package/dist/dev-tui.d.ts.map +1 -0
- package/dist/dev-tui.js +188 -0
- package/dist/dev-tui.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 +57 -0
- package/dist/docker-postgres.d.ts.map +1 -0
- package/dist/docker-postgres.js +208 -0
- package/dist/docker-postgres.js.map +1 -0
- package/dist/engine-client.d.ts +69 -0
- package/dist/engine-client.d.ts.map +1 -0
- package/dist/engine-client.js +157 -0
- package/dist/engine-client.js.map +1 -0
- package/dist/engine-push-output.d.ts +16 -0
- package/dist/engine-push-output.d.ts.map +1 -0
- package/dist/engine-push-output.js +61 -0
- package/dist/engine-push-output.js.map +1 -0
- package/dist/ensure-binary.d.ts +7 -0
- package/dist/ensure-binary.d.ts.map +1 -0
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -0
- package/dist/functions-router-gen.d.ts +14 -0
- package/dist/functions-router-gen.d.ts.map +1 -0
- package/dist/functions-router-gen.js +199 -0
- package/dist/functions-router-gen.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +3 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +13 -0
- package/dist/jwt.js.map +1 -0
- package/dist/kong-config.d.ts +25 -0
- package/dist/kong-config.d.ts.map +1 -0
- package/dist/kong-config.js +71 -0
- package/dist/kong-config.js.map +1 -0
- package/dist/local-gateway.d.ts +7 -0
- package/dist/local-gateway.d.ts.map +1 -0
- package/dist/local-gateway.js +9 -0
- package/dist/local-gateway.js.map +1 -0
- package/dist/local-storage.d.ts +8 -0
- package/dist/local-storage.d.ts.map +1 -0
- package/dist/local-storage.js +14 -0
- package/dist/local-storage.js.map +1 -0
- package/dist/pgbouncer-userlist.d.ts +5 -0
- package/dist/pgbouncer-userlist.d.ts.map +1 -0
- package/dist/pgbouncer-userlist.js +14 -0
- package/dist/pgbouncer-userlist.js.map +1 -0
- package/dist/postgres-ctl.d.ts +44 -0
- package/dist/postgres-ctl.d.ts.map +1 -0
- package/dist/postgres-ctl.js +137 -0
- package/dist/postgres-ctl.js.map +1 -0
- package/dist/process-manager.d.ts +49 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +177 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/project-config.d.ts +238 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +159 -0
- package/dist/project-config.js.map +1 -0
- package/dist/pull-utils.d.ts +31 -0
- package/dist/pull-utils.d.ts.map +1 -0
- package/dist/pull-utils.js +77 -0
- package/dist/pull-utils.js.map +1 -0
- package/dist/release-pins.d.ts +7 -0
- package/dist/release-pins.d.ts.map +1 -0
- package/dist/release-pins.js +27 -0
- package/dist/release-pins.js.map +1 -0
- package/dist/release-public-key.d.ts +8 -0
- package/dist/release-public-key.d.ts.map +1 -0
- package/dist/release-public-key.js +13 -0
- package/dist/release-public-key.js.map +1 -0
- package/dist/restore-system-relation-targets.d.ts +3 -0
- package/dist/restore-system-relation-targets.d.ts.map +1 -0
- package/dist/restore-system-relation-targets.js +45 -0
- package/dist/restore-system-relation-targets.js.map +1 -0
- package/dist/runtime-routes.d.ts +34 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +252 -0
- package/dist/runtime-routes.js.map +1 -0
- 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/scripts/postinstall.d.ts +11 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +47 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +32 -0
- package/dist/seed.js.map +1 -0
- package/dist/self-host-compose.d.ts +43 -0
- package/dist/self-host-compose.d.ts.map +1 -0
- package/dist/self-host-compose.js +400 -0
- package/dist/self-host-compose.js.map +1 -0
- package/dist/storage-provision.d.ts +24 -0
- package/dist/storage-provision.d.ts.map +1 -0
- package/dist/storage-provision.js +44 -0
- package/dist/storage-provision.js.map +1 -0
- 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/supatype-eval-1781522769253.d.mts +2 -0
- package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
- package/dist/supatype-eval-1781522769253.mjs +3 -0
- package/dist/supatype-eval-1781522769253.mjs.map +1 -0
- package/dist/systemd.d.ts +26 -0
- package/dist/systemd.d.ts.map +1 -0
- package/dist/systemd.js +102 -0
- package/dist/systemd.js.map +1 -0
- package/dist/tsx-runner.d.ts +18 -0
- package/dist/tsx-runner.d.ts.map +1 -0
- package/dist/tsx-runner.js +69 -0
- package/dist/tsx-runner.js.map +1 -0
- package/dist/type-extractor.d.ts +4 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/type-extractor.js +1213 -0
- package/dist/type-extractor.js.map +1 -0
- 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 +41 -0
- package/releases/deno/VERSION +1 -0
- package/scripts/mirror-deno-release.sh +76 -0
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/framework.ts +249 -0
- package/src/app/proxy-dev-app.ts +68 -0
- package/src/app-config.ts +128 -0
- package/src/augmentation-generator.ts +126 -0
- package/src/binary-cache.ts +845 -0
- package/src/cli.ts +63 -0
- package/src/commands/admin.ts +372 -0
- package/src/commands/app.ts +97 -0
- package/src/commands/cache.ts +117 -0
- package/src/commands/cloud.ts +325 -0
- package/src/commands/db.ts +136 -0
- package/src/commands/deploy-types.ts +49 -0
- package/src/commands/deploy.ts +400 -0
- package/src/commands/dev.ts +1009 -0
- package/src/commands/diff.ts +63 -0
- package/src/commands/engine.ts +30 -0
- package/src/commands/functions.ts +901 -0
- package/src/commands/generate.ts +44 -0
- package/src/commands/init.ts +253 -0
- package/src/commands/keys.ts +66 -0
- package/src/commands/logs.ts +58 -0
- package/src/commands/migrate-from-v1.ts +131 -0
- package/src/commands/migrate.ts +87 -0
- package/src/commands/pg.ts +133 -0
- package/src/commands/plugins.ts +508 -0
- package/src/commands/pull.ts +17 -0
- package/src/commands/push.ts +226 -0
- package/src/commands/seed.ts +68 -0
- package/src/commands/self-host.ts +364 -0
- package/src/commands/self-update.ts +45 -0
- package/src/commands/status.ts +84 -0
- package/src/commands/types.ts +76 -0
- package/src/commands/update.ts +136 -0
- package/src/components.ts +6 -0
- package/src/config.ts +223 -0
- package/src/dev-compose.ts +583 -0
- package/src/dev-log-bus.ts +101 -0
- package/src/dev-log-filter.ts +32 -0
- package/src/dev-logo.ts +62 -0
- package/src/dev-session.ts +130 -0
- package/src/dev-shutdown.ts +54 -0
- package/src/dev-task-colors.ts +47 -0
- package/src/dev-tui.ts +232 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +295 -0
- package/src/engine-client.ts +236 -0
- package/src/engine-push-output.ts +71 -0
- package/src/ensure-binary.ts +28 -0
- package/src/functions-router-gen.ts +224 -0
- package/src/index.ts +11 -0
- package/src/jwt.ts +14 -0
- package/src/kong-config.ts +93 -0
- package/src/local-gateway.ts +9 -0
- package/src/local-storage.ts +14 -0
- package/src/pgbouncer-userlist.ts +15 -0
- package/src/postgres-ctl.ts +171 -0
- package/src/process-manager.ts +220 -0
- package/src/project-config.ts +388 -0
- package/src/pull-utils.ts +81 -0
- package/src/release-pins.ts +31 -0
- package/src/release-public-key.ts +12 -0
- package/src/restore-system-relation-targets.ts +45 -0
- package/src/runtime-routes.ts +291 -0
- package/src/schema-ast-v2.ts +324 -0
- package/src/scripts/postinstall.ts +51 -0
- package/src/seed.ts +43 -0
- package/src/self-host-compose.ts +452 -0
- package/src/storage-provision.ts +58 -0
- package/src/studio-admin-roles.ts +16 -0
- package/src/studio-dev-server.ts +53 -0
- package/src/supatype-eval-1781522769253.mts +1 -0
- package/src/systemd.ts +137 -0
- package/src/tsx-runner.ts +89 -0
- package/src/type-extractor.ts +1479 -0
- package/src/type-resolver.ts +457 -0
- package/tests/app-command.test.ts +54 -0
- package/tests/augmentation-generator.test.ts +59 -0
- package/tests/binary-cache-cloud-overrides.test.ts +123 -0
- package/tests/cached-artifact-format.test.ts +84 -0
- package/tests/cli-help.test.ts +133 -0
- package/tests/config.test.ts +252 -0
- package/tests/dev-ui.test.ts +139 -0
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/engine-distribution.test.ts +418 -0
- package/tests/engine-push-output.test.ts +67 -0
- package/tests/ensure-binary.test.ts +59 -0
- package/tests/init.test.ts +127 -0
- package/tests/keys.test.ts +160 -0
- package/tests/migrate-from-v1.test.ts +29 -0
- package/tests/normalize-admin-config.test.ts +48 -0
- package/tests/pg-spawn-env.test.ts +18 -0
- package/tests/postgres-archive-tag.test.ts +9 -0
- package/tests/proxy-dev-app.test.ts +33 -0
- package/tests/pull-utils.test.ts +150 -0
- package/tests/release-pins.test.ts +28 -0
- package/tests/runtime-contract.test.ts +370 -0
- package/tests/seed-discover.test.ts +31 -0
- package/tests/studio-admin-roles.test.ts +27 -0
- package/tests/tsconfig.json +9 -0
- package/tests/tsx-runner.test.ts +66 -0
- package/tests/type-extractor.test.ts +985 -0
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { resolve, dirname } from "node:path"
|
|
4
|
+
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
5
|
+
import { schemaPathFromProject } from "../project-config.js"
|
|
6
|
+
import { ensureEngine, engineRequest } from "../engine-client.js"
|
|
7
|
+
import { generateClientAugmentation } from "../augmentation-generator.js"
|
|
8
|
+
|
|
9
|
+
export function registerGenerate(program: Command): void {
|
|
10
|
+
program
|
|
11
|
+
.command("generate")
|
|
12
|
+
.description("Regenerate TypeScript types without running a migration")
|
|
13
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
14
|
+
.action(async (opts: { connection?: string }) => {
|
|
15
|
+
const cwd = process.cwd()
|
|
16
|
+
const config = loadConfig(cwd)
|
|
17
|
+
const schemaPath = schemaPathFromProject(config, cwd)
|
|
18
|
+
const outputTypesPath = config.output?.types ?? "types/database.ts"
|
|
19
|
+
const outputClientPath = config.output?.client ?? "supatype/generated/index.d.ts"
|
|
20
|
+
|
|
21
|
+
await ensureEngine()
|
|
22
|
+
console.log("Loading schema...")
|
|
23
|
+
const ast = loadSchemaAst(schemaPath, cwd)
|
|
24
|
+
|
|
25
|
+
const result = await engineRequest<{ code?: string; message?: string }>("/generate", { ast, lang: "typescript" })
|
|
26
|
+
|
|
27
|
+
const typesCode = result.code ?? result.message
|
|
28
|
+
if (typesCode === undefined) {
|
|
29
|
+
console.error("Engine returned no output.")
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const outPath = resolve(cwd, outputTypesPath)
|
|
34
|
+
mkdirSync(dirname(outPath), { recursive: true })
|
|
35
|
+
writeFileSync(outPath, typesCode, "utf8")
|
|
36
|
+
console.log(`Types written to ${outputTypesPath}`)
|
|
37
|
+
|
|
38
|
+
const augmentationOutPath = resolve(cwd, outputClientPath)
|
|
39
|
+
const augmentationCode = generateClientAugmentation(ast)
|
|
40
|
+
mkdirSync(dirname(augmentationOutPath), { recursive: true })
|
|
41
|
+
writeFileSync(augmentationOutPath, augmentationCode, "utf8")
|
|
42
|
+
console.log(`Client augmentation written to ${outputClientPath}`)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { resolve, join, dirname } from "node:path"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
|
|
6
|
+
export { scaffold }
|
|
7
|
+
|
|
8
|
+
// ─── Markers used by `supatype app add / remove` (app.ts) ────────────────────
|
|
9
|
+
export const APP_COMPOSE_MARKER = " # ─── App service (run: supatype app add) ───"
|
|
10
|
+
export const KONG_APP_MARKER = " # ─── App fallback route (run: supatype app add) ───"
|
|
11
|
+
|
|
12
|
+
const CLI_PACKAGE_JSON = join(
|
|
13
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
14
|
+
"..",
|
|
15
|
+
"..",
|
|
16
|
+
"package.json",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
function cliPackageVersion(): string {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(CLI_PACKAGE_JSON, "utf8")) as { version?: string }
|
|
22
|
+
return pkg.version ?? "0.1.0"
|
|
23
|
+
} catch {
|
|
24
|
+
return "0.1.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function registerInit(program: Command): void {
|
|
29
|
+
program
|
|
30
|
+
.command("init [name]")
|
|
31
|
+
.description("Scaffold a new Supatype project")
|
|
32
|
+
.option(
|
|
33
|
+
"--mode <mode>",
|
|
34
|
+
"Server mode in supatype.config.ts: dev (default) | standalone (native ACME — not Compose self-host)",
|
|
35
|
+
"dev",
|
|
36
|
+
)
|
|
37
|
+
.action(async (name?: string, opts: { mode: string } = { mode: "dev" }) => {
|
|
38
|
+
const projectName = name ?? "my-project"
|
|
39
|
+
const dir = name ? resolve(process.cwd(), name) : process.cwd()
|
|
40
|
+
|
|
41
|
+
if (name && existsSync(dir)) {
|
|
42
|
+
console.error(`Directory already exists: ${dir}`)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (name) mkdirSync(dir, { recursive: true })
|
|
47
|
+
|
|
48
|
+
scaffold(dir, projectName, opts.mode as "dev" | "standalone")
|
|
49
|
+
|
|
50
|
+
console.log(`\nSupatype project ready${name ? ` in ${name}/` : ""}.\n`)
|
|
51
|
+
console.log("Next steps:")
|
|
52
|
+
if (name) console.log(` cd ${name}`)
|
|
53
|
+
console.log(" npm install")
|
|
54
|
+
console.log(" supatype keys")
|
|
55
|
+
console.log(" supatype dev # Docker Compose stack (Kong :18473)")
|
|
56
|
+
console.log(" supatype push # apply schema + generate types")
|
|
57
|
+
console.log("\nStatic frontend (self-host):")
|
|
58
|
+
console.log(" supatype app add --static ./public")
|
|
59
|
+
console.log(" npm run build # write files into public/")
|
|
60
|
+
console.log(" supatype self-host compose up -d")
|
|
61
|
+
if (opts.mode === "standalone") {
|
|
62
|
+
console.log("\nStandalone (native TLS with ACME):")
|
|
63
|
+
console.log(" Edit supatype.config.ts — set server.domain")
|
|
64
|
+
console.log(" supatype dev # or run supatype-server with your TLS setup")
|
|
65
|
+
}
|
|
66
|
+
console.log()
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function scaffold(dir: string, projectName: string, mode: "dev" | "standalone" = "dev"): void {
|
|
71
|
+
const write = (rel: string, content: string) => {
|
|
72
|
+
const full = join(dir, rel)
|
|
73
|
+
mkdirSync(resolve(full, ".."), { recursive: true })
|
|
74
|
+
writeFileSync(full, content, "utf8")
|
|
75
|
+
console.log(` created ${rel}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const pkgPath = join(dir, "package.json")
|
|
79
|
+
if (!existsSync(pkgPath)) {
|
|
80
|
+
write("package.json", packageJsonTemplate(projectName, cliPackageVersion()))
|
|
81
|
+
} else {
|
|
82
|
+
console.log(" skipped package.json (already exists)")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
write("supatype.config.ts", tsConfigTemplate(projectName, mode))
|
|
86
|
+
write("schema/index.ts", schemaTemplate())
|
|
87
|
+
write(".env", envTemplate(projectName))
|
|
88
|
+
write("seed.ts", seedTemplate(projectName))
|
|
89
|
+
write("seeds/.gitkeep", "")
|
|
90
|
+
write("public/.gitkeep", "")
|
|
91
|
+
write(".gitignore", gitignoreTemplate())
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Templates ───────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
function packageJsonTemplate(projectName: string, cliVersion: string): string {
|
|
97
|
+
return `{
|
|
98
|
+
"name": "${projectName}",
|
|
99
|
+
"private": true,
|
|
100
|
+
"type": "module",
|
|
101
|
+
"scripts": {
|
|
102
|
+
"dev": "supatype dev",
|
|
103
|
+
"push": "supatype push",
|
|
104
|
+
"seed": "tsx seed.ts"
|
|
105
|
+
},
|
|
106
|
+
"dependencies": {
|
|
107
|
+
"@supatype/cli": "^${cliVersion}",
|
|
108
|
+
"@supatype/types": "^${cliVersion}"
|
|
109
|
+
},
|
|
110
|
+
"devDependencies": {
|
|
111
|
+
"tsx": "^4.19.2",
|
|
112
|
+
"typescript": "^5"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
`
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function tsConfigTemplate(projectName: string, mode: "dev" | "standalone"): string {
|
|
119
|
+
const domainField =
|
|
120
|
+
mode === "standalone"
|
|
121
|
+
? ` domain: "", // e.g. "api.example.com" for ACME TLS\n`
|
|
122
|
+
: ""
|
|
123
|
+
return `import { defineConfig } from "@supatype/cli"
|
|
124
|
+
|
|
125
|
+
export default defineConfig({
|
|
126
|
+
project: { name: "${projectName}" },
|
|
127
|
+
provider: "docker",
|
|
128
|
+
// provider: "native" // host Postgres + supatype-server binaries (no Docker)
|
|
129
|
+
database: {
|
|
130
|
+
provider: "docker",
|
|
131
|
+
},
|
|
132
|
+
server: {
|
|
133
|
+
mode: "${mode}",
|
|
134
|
+
port: 54321,
|
|
135
|
+
${domainField} },
|
|
136
|
+
app: {
|
|
137
|
+
mode: "none",
|
|
138
|
+
// mode: "static", static_dir: "./public", // supatype app add --static ./public
|
|
139
|
+
// mode: "proxy", upstream: "http://localhost:3000", start: "dev",
|
|
140
|
+
// vite_dev_url: "http://127.0.0.1:5173", // dev HMR at /_vite (when using a separate Vite server)
|
|
141
|
+
},
|
|
142
|
+
// Optional: pin component versions (native cache + Docker images synced to .env on dev/push)
|
|
143
|
+
// versions: { engine: "0.1.2", server: "1.0.5", postgres: "17.2", deno: "2.2.0" },
|
|
144
|
+
email: { provider: "console" },
|
|
145
|
+
storage: { provider: "local", local_path: ".supatype/storage" },
|
|
146
|
+
schema: { path: "schema/index.ts", pg_schema: "public" },
|
|
147
|
+
// Self-host production: supatype self-host compose (Docker only). Standalone + domain = native ACME dev.
|
|
148
|
+
})
|
|
149
|
+
`
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function schemaTemplate(): string {
|
|
153
|
+
return `import type { Model, Public, Owner, Role, SupatypeAuthUserId, Unique, Email, UUID } from "@supatype/types"
|
|
154
|
+
|
|
155
|
+
export type User = Model<{
|
|
156
|
+
id: SupatypeAuthUserId
|
|
157
|
+
email: Unique<Email>
|
|
158
|
+
name: string
|
|
159
|
+
created_at: string
|
|
160
|
+
updated_at: string
|
|
161
|
+
}, {
|
|
162
|
+
access: {
|
|
163
|
+
read: Public
|
|
164
|
+
create: Public
|
|
165
|
+
update: Owner<"id">
|
|
166
|
+
delete: Role<"admin">
|
|
167
|
+
}
|
|
168
|
+
}>
|
|
169
|
+
|
|
170
|
+
/** Example singleton global — editable in Studio under Settings. */
|
|
171
|
+
export type SiteSettings = Model<{
|
|
172
|
+
id: UUID
|
|
173
|
+
site_name: string
|
|
174
|
+
}, {
|
|
175
|
+
singleton: true
|
|
176
|
+
access: {
|
|
177
|
+
read: Public
|
|
178
|
+
update: Role<"admin">
|
|
179
|
+
}
|
|
180
|
+
}>
|
|
181
|
+
`
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function envTemplate(projectName: string): string {
|
|
185
|
+
return `DATABASE_URL=postgresql://supatype_admin:postgres@localhost:5432/${projectName}
|
|
186
|
+
POSTGRES_USER=supatype_admin
|
|
187
|
+
POSTGRES_PASSWORD=postgres
|
|
188
|
+
POSTGRES_DB=${projectName}
|
|
189
|
+
|
|
190
|
+
# JWT — run \`supatype keys\` to generate ANON_KEY and SERVICE_ROLE_KEY
|
|
191
|
+
JWT_SECRET=super-secret-jwt-token-change-in-production
|
|
192
|
+
ANON_KEY=
|
|
193
|
+
SERVICE_ROLE_KEY=
|
|
194
|
+
|
|
195
|
+
# Site URL (used by GoTrue for email redirects)
|
|
196
|
+
SITE_URL=http://localhost:3000
|
|
197
|
+
|
|
198
|
+
# SMTP — leave empty to use email autoconfirm in dev (no emails sent)
|
|
199
|
+
SMTP_HOST=
|
|
200
|
+
SMTP_PORT=
|
|
201
|
+
SMTP_USER=
|
|
202
|
+
SMTP_PASS=
|
|
203
|
+
SMTP_SENDER_NAME=${projectName}
|
|
204
|
+
|
|
205
|
+
# Storage (MinIO for local dev)
|
|
206
|
+
S3_ENDPOINT=http://localhost:9000
|
|
207
|
+
S3_ACCESS_KEY=supatype
|
|
208
|
+
S3_SECRET_KEY=supatype-secret
|
|
209
|
+
|
|
210
|
+
# Self-host compose uses the same DATABASE_URL when Postgres is published on localhost:5432
|
|
211
|
+
`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function seedTemplate(projectName: string): string {
|
|
215
|
+
return `import { sql } from "@supatype/cli/seed"
|
|
216
|
+
|
|
217
|
+
// Connect using DATABASE_URL from environment
|
|
218
|
+
const db = sql(
|
|
219
|
+
process.env["DATABASE_URL"] ??
|
|
220
|
+
"postgresql://supatype_admin:postgres@localhost:5432/${projectName}",
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
async function seed() {
|
|
224
|
+
console.log("Seeding ${projectName}...")
|
|
225
|
+
|
|
226
|
+
// TODO: insert seed data
|
|
227
|
+
// await db\`INSERT INTO users (email, name) VALUES ('admin@example.com', 'Admin')\`
|
|
228
|
+
|
|
229
|
+
await db.end()
|
|
230
|
+
console.log("Done.")
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
seed().catch((e) => {
|
|
234
|
+
console.error(e)
|
|
235
|
+
process.exit(1)
|
|
236
|
+
})
|
|
237
|
+
`
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function gitignoreTemplate(): string {
|
|
241
|
+
return `.env
|
|
242
|
+
node_modules/
|
|
243
|
+
dist/
|
|
244
|
+
.supatype/engine/
|
|
245
|
+
# Local overrides — never commit
|
|
246
|
+
supatype.local.config.ts
|
|
247
|
+
supatype.local.config.js
|
|
248
|
+
supatype.local.config.mjs
|
|
249
|
+
# Generated by supatype push
|
|
250
|
+
src/types/supatype.d.ts
|
|
251
|
+
src/lib/supatype.ts
|
|
252
|
+
`
|
|
253
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs"
|
|
3
|
+
import { resolve } from "node:path"
|
|
4
|
+
import { signJwt } from "../jwt.js"
|
|
5
|
+
|
|
6
|
+
export function registerKeys(program: Command): void {
|
|
7
|
+
program
|
|
8
|
+
.command("keys")
|
|
9
|
+
.description("Generate ANON_KEY and SERVICE_ROLE_KEY JWTs from your JWT_SECRET")
|
|
10
|
+
.option("--secret <secret>", "JWT secret (defaults to JWT_SECRET env var or value in .env)")
|
|
11
|
+
.option("--exp-years <years>", "Token expiry in years (default: 10)", "10")
|
|
12
|
+
.action((opts: { secret?: string; expYears: string }) => {
|
|
13
|
+
const secret = opts.secret ?? resolveSecret()
|
|
14
|
+
if (!secret) {
|
|
15
|
+
console.error(
|
|
16
|
+
"Error: JWT_SECRET not found. Set it in .env or pass --secret <value>",
|
|
17
|
+
)
|
|
18
|
+
process.exit(1)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const expYears = parseInt(opts.expYears, 10)
|
|
22
|
+
if (isNaN(expYears) || expYears < 1) {
|
|
23
|
+
console.error("Error: --exp-years must be a positive integer")
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const now = Math.floor(Date.now() / 1000)
|
|
28
|
+
const exp = now + expYears * 365 * 24 * 60 * 60
|
|
29
|
+
|
|
30
|
+
const anonKey = signJwt({ iss: "supatype", role: "anon", iat: now, exp }, secret)
|
|
31
|
+
const serviceKey = signJwt({ iss: "supatype", role: "service_role", iat: now, exp }, secret)
|
|
32
|
+
|
|
33
|
+
console.log("\nGenerated keys (valid for", expYears, "years):\n")
|
|
34
|
+
console.log("ANON_KEY=" + anonKey)
|
|
35
|
+
console.log("SERVICE_ROLE_KEY=" + serviceKey)
|
|
36
|
+
console.log(
|
|
37
|
+
"\nAdd these to your .env file. Do not commit .env to source control.",
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export function resolveSecret(): string | undefined {
|
|
45
|
+
// 1. Check environment variable
|
|
46
|
+
const fromEnv = process.env["JWT_SECRET"]
|
|
47
|
+
if (fromEnv) return fromEnv
|
|
48
|
+
|
|
49
|
+
// 2. Parse .env file in cwd
|
|
50
|
+
const envPath = resolve(process.cwd(), ".env")
|
|
51
|
+
if (!existsSync(envPath)) return undefined
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const contents = readFileSync(envPath, "utf8")
|
|
55
|
+
for (const line of contents.split("\n")) {
|
|
56
|
+
const trimmed = line.trim()
|
|
57
|
+
if (trimmed.startsWith("JWT_SECRET=")) {
|
|
58
|
+
const value = trimmed.slice("JWT_SECRET=".length).trim()
|
|
59
|
+
if (value && !value.startsWith("#")) return value
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// ignore read errors
|
|
64
|
+
}
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* supatype logs — tail aggregated logs from local Docker containers.
|
|
3
|
+
*/
|
|
4
|
+
import type { Command } from "commander"
|
|
5
|
+
import { spawn } from "node:child_process"
|
|
6
|
+
|
|
7
|
+
const SERVICES = ["postgres", "postgrest", "gotrue", "kong", "minio", "realtime", "studio"]
|
|
8
|
+
|
|
9
|
+
export function registerLogs(program: Command): void {
|
|
10
|
+
program
|
|
11
|
+
.command("logs")
|
|
12
|
+
.description("Tail aggregated logs from local dev services")
|
|
13
|
+
.option("--service <name>", "Filter to a specific service")
|
|
14
|
+
.option("--since <duration>", "Show logs since duration (e.g., 5m, 1h)", "5m")
|
|
15
|
+
.option("-f, --follow", "Follow log output", true)
|
|
16
|
+
.action((opts: { service?: string; since?: string; follow?: boolean }) => {
|
|
17
|
+
const services = opts.service
|
|
18
|
+
? [`supatype-${opts.service}`]
|
|
19
|
+
: SERVICES.map((s) => `supatype-${s}`)
|
|
20
|
+
|
|
21
|
+
if (opts.service && !SERVICES.includes(opts.service)) {
|
|
22
|
+
console.error(`Unknown service: ${opts.service}`)
|
|
23
|
+
console.error(`Available: ${SERVICES.join(", ")}`)
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const args = ["compose", "logs"]
|
|
28
|
+
if (opts.follow) args.push("-f")
|
|
29
|
+
if (opts.since) args.push("--since", opts.since)
|
|
30
|
+
args.push("--tail", "100")
|
|
31
|
+
|
|
32
|
+
// If filtering by service, use docker logs for that container
|
|
33
|
+
if (opts.service) {
|
|
34
|
+
const containerArgs = ["logs"]
|
|
35
|
+
if (opts.follow) containerArgs.push("-f")
|
|
36
|
+
if (opts.since) containerArgs.push("--since", opts.since)
|
|
37
|
+
containerArgs.push("--tail", "100")
|
|
38
|
+
containerArgs.push(`supatype-${opts.service}`)
|
|
39
|
+
|
|
40
|
+
const child = spawn("docker", containerArgs, { stdio: "inherit" })
|
|
41
|
+
child.on("error", () => {
|
|
42
|
+
console.error("Docker not found. Ensure Docker is installed and running.")
|
|
43
|
+
process.exit(1)
|
|
44
|
+
})
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Aggregated logs via docker compose
|
|
49
|
+
const child = spawn("docker", args, {
|
|
50
|
+
stdio: "inherit",
|
|
51
|
+
cwd: process.cwd(),
|
|
52
|
+
})
|
|
53
|
+
child.on("error", () => {
|
|
54
|
+
console.error("Docker not found. Ensure Docker is installed and running.")
|
|
55
|
+
process.exit(1)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { basename, dirname, extname, join, resolve } from "node:path"
|
|
4
|
+
import ts from "typescript"
|
|
5
|
+
import { loadConfig } from "../config.js"
|
|
6
|
+
import { schemaPathFromProject } from "../project-config.js"
|
|
7
|
+
|
|
8
|
+
export function registerMigrateFromV1(program: Command): void {
|
|
9
|
+
program
|
|
10
|
+
.command("migrate-from-v1")
|
|
11
|
+
.description("Codemod runtime model() schema DSL into RFC v2 Model<> type aliases")
|
|
12
|
+
.option("--schema <path>", "Path to v1 schema file (defaults to supatype.config.ts schema)")
|
|
13
|
+
.option("--write", "Overwrite source file instead of creating .v2.ts sibling")
|
|
14
|
+
.action((opts: { schema?: string; write?: boolean }) => {
|
|
15
|
+
const cwd = process.cwd()
|
|
16
|
+
const cfg = loadConfig(cwd)
|
|
17
|
+
const schemaPath = opts.schema !== undefined
|
|
18
|
+
? resolve(cwd, opts.schema)
|
|
19
|
+
: schemaPathFromProject(cfg, cwd)
|
|
20
|
+
if (!existsSync(schemaPath)) {
|
|
21
|
+
throw new Error(`Schema file not found: ${schemaPath}`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const src = readFileSync(schemaPath, "utf8")
|
|
25
|
+
const sf = ts.createSourceFile(schemaPath, src, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
|
26
|
+
const output = codemodSource(sf)
|
|
27
|
+
const outPath = opts.write
|
|
28
|
+
? schemaPath
|
|
29
|
+
: join(dirname(schemaPath), basename(schemaPath).replace(extname(schemaPath), ".v2.ts"))
|
|
30
|
+
|
|
31
|
+
writeFileSync(outPath, output, "utf8")
|
|
32
|
+
console.log(`v2 schema written to ${outPath}`)
|
|
33
|
+
if (!opts.write) {
|
|
34
|
+
console.log("Review TODO comments and replace unknown mappings before using this file in production.")
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function codemodSource(sf: ts.SourceFile): string {
|
|
40
|
+
const lines: string[] = []
|
|
41
|
+
lines.push('import type { Model, UUID, Optional } from "@supatype/types"')
|
|
42
|
+
lines.push("")
|
|
43
|
+
lines.push("// TODO(v2-migration): verify each mapped type and access rule below.")
|
|
44
|
+
lines.push("")
|
|
45
|
+
|
|
46
|
+
for (const stmt of sf.statements) {
|
|
47
|
+
if (!ts.isVariableStatement(stmt)) continue
|
|
48
|
+
if (!(ts.getModifiers(stmt)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false)) continue
|
|
49
|
+
|
|
50
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
51
|
+
if (!ts.isIdentifier(decl.name) || !decl.initializer) continue
|
|
52
|
+
const modelCall = extractModelCall(decl.initializer)
|
|
53
|
+
if (!modelCall) continue
|
|
54
|
+
const fields = extractFields(modelCall.config)
|
|
55
|
+
const modelType = renderModelType(decl.name.text, fields)
|
|
56
|
+
lines.push(modelType)
|
|
57
|
+
lines.push("")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (lines.length <= 4) {
|
|
62
|
+
lines.push("// TODO(v2-migration): no model() declarations were detected in this file.")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return lines.join("\n")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function extractModelCall(node: ts.Expression): { config: ts.ObjectLiteralExpression } | null {
|
|
69
|
+
if (!ts.isCallExpression(node)) return null
|
|
70
|
+
if (!ts.isIdentifier(node.expression) || node.expression.text !== "model") return null
|
|
71
|
+
const config = node.arguments[1]
|
|
72
|
+
if (!config || !ts.isObjectLiteralExpression(config)) return null
|
|
73
|
+
return { config }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function extractFields(config: ts.ObjectLiteralExpression): Array<{ name: string; type: string }> {
|
|
77
|
+
const fieldsProp = config.properties.find(
|
|
78
|
+
(p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "fields",
|
|
79
|
+
)
|
|
80
|
+
if (!fieldsProp || !ts.isPropertyAssignment(fieldsProp) || !ts.isObjectLiteralExpression(fieldsProp.initializer)) {
|
|
81
|
+
return []
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const result: Array<{ name: string; type: string }> = []
|
|
85
|
+
for (const prop of fieldsProp.initializer.properties) {
|
|
86
|
+
if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name)) continue
|
|
87
|
+
result.push({
|
|
88
|
+
name: prop.name.text,
|
|
89
|
+
type: mapFieldInitializerToType(prop.initializer),
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function mapFieldInitializerToType(node: ts.Expression): string {
|
|
96
|
+
if (!ts.isCallExpression(node)) return "unknown // TODO(v2-migration): unsupported field expression"
|
|
97
|
+
if (!ts.isPropertyAccessExpression(node.expression)) return "unknown // TODO(v2-migration): unsupported field expression"
|
|
98
|
+
if (!ts.isIdentifier(node.expression.expression) || node.expression.expression.text !== "field") {
|
|
99
|
+
return "unknown // TODO(v2-migration): unsupported field expression"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const kind = node.expression.name.text
|
|
103
|
+
switch (kind) {
|
|
104
|
+
case "uuid":
|
|
105
|
+
return "UUID"
|
|
106
|
+
case "text":
|
|
107
|
+
case "slug":
|
|
108
|
+
case "email":
|
|
109
|
+
case "url":
|
|
110
|
+
return "string"
|
|
111
|
+
case "integer":
|
|
112
|
+
case "smallInt":
|
|
113
|
+
case "float":
|
|
114
|
+
case "decimal":
|
|
115
|
+
return "number"
|
|
116
|
+
case "boolean":
|
|
117
|
+
return "boolean"
|
|
118
|
+
case "richText":
|
|
119
|
+
case "json":
|
|
120
|
+
return "Record<string, unknown>"
|
|
121
|
+
default:
|
|
122
|
+
return "unknown // TODO(v2-migration): map field." + kind
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function renderModelType(name: string, fields: Array<{ name: string; type: string }>): string {
|
|
127
|
+
const fieldsBlock = fields
|
|
128
|
+
.map((field) => ` ${field.name}: ${field.type}`)
|
|
129
|
+
.join("\n")
|
|
130
|
+
return `export type ${name} = Model<{\n${fieldsBlock}\n}>`
|
|
131
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { createInterface } from "node:readline"
|
|
3
|
+
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
4
|
+
import { connectionString, schemaPathFromProject } from "../project-config.js"
|
|
5
|
+
import { ensureEngine, engineRequest } from "../engine-client.js"
|
|
6
|
+
|
|
7
|
+
export function registerMigrate(program: Command): void {
|
|
8
|
+
// migrate — apply all pending migrations
|
|
9
|
+
program
|
|
10
|
+
.command("migrate")
|
|
11
|
+
.description("Apply pending migrations from the migration history")
|
|
12
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
13
|
+
.action(async (opts: { connection?: string }) => {
|
|
14
|
+
const config = loadConfig()
|
|
15
|
+
const connection = opts.connection ?? connectionString(config)
|
|
16
|
+
|
|
17
|
+
await ensureEngine()
|
|
18
|
+
const result = await engineRequest<{ message?: string }>("/migrations", {
|
|
19
|
+
database_url: connection,
|
|
20
|
+
schema: "public",
|
|
21
|
+
action: "pending",
|
|
22
|
+
})
|
|
23
|
+
console.log(result.message ?? "Migrations applied.")
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// rollback — undo the last applied migration
|
|
27
|
+
program
|
|
28
|
+
.command("rollback")
|
|
29
|
+
.description("Roll back the last applied migration")
|
|
30
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
31
|
+
.action(async (opts: { connection?: string }) => {
|
|
32
|
+
const config = loadConfig()
|
|
33
|
+
const connection = opts.connection ?? connectionString(config)
|
|
34
|
+
|
|
35
|
+
await ensureEngine()
|
|
36
|
+
const result = await engineRequest<{ message?: string }>("/migrations", {
|
|
37
|
+
database_url: connection,
|
|
38
|
+
schema: "public",
|
|
39
|
+
action: "rollback",
|
|
40
|
+
})
|
|
41
|
+
console.log(result.message ?? "Rolled back.")
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// reset — drop all tables and re-apply from scratch
|
|
45
|
+
program
|
|
46
|
+
.command("reset")
|
|
47
|
+
.description(
|
|
48
|
+
"Drop all managed tables and re-apply the schema from scratch (destructive)",
|
|
49
|
+
)
|
|
50
|
+
.option("--yes", "Skip confirmation prompt")
|
|
51
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
52
|
+
.action(async (opts: { yes?: boolean; connection?: string }) => {
|
|
53
|
+
if (!opts.yes) {
|
|
54
|
+
const confirmed = await confirm(
|
|
55
|
+
"This will DROP all managed tables and re-apply the schema. Proceed? [y/N] ",
|
|
56
|
+
)
|
|
57
|
+
if (!confirmed) {
|
|
58
|
+
console.log("Aborted.")
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const config = loadConfig()
|
|
64
|
+
const connection = opts.connection ?? connectionString(config)
|
|
65
|
+
const cwd = process.cwd()
|
|
66
|
+
|
|
67
|
+
await ensureEngine()
|
|
68
|
+
const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
|
|
69
|
+
const result = await engineRequest<{ message?: string }>("/push", {
|
|
70
|
+
ast,
|
|
71
|
+
database_url: connection,
|
|
72
|
+
schema: "public",
|
|
73
|
+
force: true,
|
|
74
|
+
})
|
|
75
|
+
console.log(result.message ?? "Reset complete.")
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function confirm(prompt: string): Promise<boolean> {
|
|
80
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
rl.question(prompt, (answer) => {
|
|
83
|
+
rl.close()
|
|
84
|
+
resolve(answer.toLowerCase() === "y")
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
}
|