gorsee 0.1.3 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gorsee",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
4
4
  "description": "Full-stack TypeScript framework — islands, reactive WebSocket, optimistic mutations, built-in auth, type-safe routes",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -42,7 +42,16 @@
42
42
  "./env": "./src/env/index.ts",
43
43
  "./auth": "./src/auth/index.ts",
44
44
  "./routes": "./src/runtime/typed-routes.ts",
45
- "./cli/cmd-create": "./src/cli/cmd-create.ts"
45
+ "./cli/cmd-create": "./src/cli/cmd-create.ts",
46
+ "./plugins": "./src/plugins/index.ts",
47
+ "./plugins/drizzle": "./src/plugins/drizzle.ts",
48
+ "./plugins/prisma": "./src/plugins/prisma.ts",
49
+ "./plugins/tailwind": "./src/plugins/tailwind.ts",
50
+ "./plugins/lucia": "./src/plugins/lucia.ts",
51
+ "./plugins/s3": "./src/plugins/s3.ts",
52
+ "./plugins/resend": "./src/plugins/resend.ts",
53
+ "./plugins/stripe": "./src/plugins/stripe.ts",
54
+ "./deploy": "./src/deploy/index.ts"
46
55
  },
47
56
  "files": [
48
57
  "src/",
@@ -0,0 +1,141 @@
1
+ // Gorsee.js — CLI deploy command
2
+
3
+ import { writeFile, access, mkdir } from "node:fs/promises"
4
+ import { join } from "node:path"
5
+
6
+ type Target = "vercel" | "fly" | "cloudflare" | "netlify" | "docker"
7
+
8
+ const TARGETS: Target[] = ["vercel", "fly", "cloudflare", "netlify", "docker"]
9
+
10
+ const DETECT_FILES: Record<string, Target> = {
11
+ "vercel.json": "vercel",
12
+ "fly.toml": "fly",
13
+ "wrangler.toml": "cloudflare",
14
+ "netlify.toml": "netlify",
15
+ "Dockerfile": "docker",
16
+ }
17
+
18
+ async function fileExists(path: string): Promise<boolean> {
19
+ try {
20
+ await access(path)
21
+ return true
22
+ } catch {
23
+ return false
24
+ }
25
+ }
26
+
27
+ async function detectTarget(cwd: string): Promise<Target | null> {
28
+ for (const [file, target] of Object.entries(DETECT_FILES)) {
29
+ if (await fileExists(join(cwd, file))) return target
30
+ }
31
+ return null
32
+ }
33
+
34
+ async function writeAndLog(filePath: string, content: string): Promise<void> {
35
+ await writeFile(filePath, content, "utf-8")
36
+ console.log(` created ${filePath}`)
37
+ }
38
+
39
+ async function deployVercel(cwd: string): Promise<void> {
40
+ const { generateVercelConfig, generateVercelServerlessEntry } = await import("../deploy/vercel.ts")
41
+ await writeAndLog(join(cwd, "vercel.json"), JSON.stringify(generateVercelConfig(), null, 2))
42
+ await mkdir(join(cwd, "api"), { recursive: true })
43
+ await writeAndLog(join(cwd, "api/index.ts"), generateVercelServerlessEntry())
44
+ console.log("\n Next steps:")
45
+ console.log(" 1. Install Vercel CLI: npm i -g vercel")
46
+ console.log(" 2. Run: vercel")
47
+ console.log(" 3. Follow prompts to link your project")
48
+ }
49
+
50
+ async function deployFly(cwd: string, appName: string): Promise<void> {
51
+ const { generateFlyConfig, generateFlyDockerfile } = await import("../deploy/fly.ts")
52
+ await writeAndLog(join(cwd, "fly.toml"), generateFlyConfig(appName))
53
+ await writeAndLog(join(cwd, "Dockerfile"), generateFlyDockerfile())
54
+ console.log("\n Next steps:")
55
+ console.log(" 1. Install Fly CLI: curl -L https://fly.io/install.sh | sh")
56
+ console.log(` 2. Run: fly launch --name ${appName}`)
57
+ console.log(" 3. Deploy: fly deploy")
58
+ }
59
+
60
+ async function deployCloudflare(cwd: string, name: string): Promise<void> {
61
+ const { generateWranglerConfig, generateCloudflareEntry, generateCloudflareStaticAssets } =
62
+ await import("../deploy/cloudflare.ts")
63
+ await writeAndLog(join(cwd, "wrangler.toml"), generateWranglerConfig(name))
64
+ await writeAndLog(join(cwd, "worker.ts"), generateCloudflareEntry())
65
+ await writeAndLog(join(cwd, "_routes.json"), JSON.stringify(generateCloudflareStaticAssets(), null, 2))
66
+ console.log("\n Next steps:")
67
+ console.log(" 1. Install Wrangler: npm i -g wrangler")
68
+ console.log(" 2. Authenticate: wrangler login")
69
+ console.log(" 3. Deploy: wrangler deploy")
70
+ }
71
+
72
+ async function deployNetlify(cwd: string): Promise<void> {
73
+ const { generateNetlifyConfig, generateNetlifyFunction } = await import("../deploy/netlify.ts")
74
+ await writeAndLog(join(cwd, "netlify.toml"), generateNetlifyConfig())
75
+ const edgeFnDir = join(cwd, "netlify/edge-functions")
76
+ await mkdir(edgeFnDir, { recursive: true })
77
+ await writeAndLog(join(edgeFnDir, "gorsee-handler.ts"), generateNetlifyFunction())
78
+ console.log("\n Next steps:")
79
+ console.log(" 1. Install Netlify CLI: npm i -g netlify-cli")
80
+ console.log(" 2. Run: netlify init")
81
+ console.log(" 3. Deploy: netlify deploy --prod")
82
+ }
83
+
84
+ async function deployDocker(cwd: string): Promise<void> {
85
+ const { generateDockerfile, generateDockerignore } = await import("../deploy/dockerfile.ts")
86
+ await writeAndLog(join(cwd, "Dockerfile"), generateDockerfile())
87
+ await writeAndLog(join(cwd, ".dockerignore"), generateDockerignore())
88
+ console.log("\n Next steps:")
89
+ console.log(" 1. Build image: docker build -t gorsee-app .")
90
+ console.log(" 2. Run: docker run -p 3000:3000 gorsee-app")
91
+ }
92
+
93
+ export async function runDeploy(args: string[]): Promise<void> {
94
+ const cwd = process.cwd()
95
+ const initOnly = args.includes("--init")
96
+ const targetArg = args.find((a) => !a.startsWith("-")) as Target | undefined
97
+
98
+ let target = targetArg ?? null
99
+ if (!target) {
100
+ target = await detectTarget(cwd)
101
+ if (!target) {
102
+ console.error(" No deploy target specified and none detected.")
103
+ console.error(` Usage: gorsee deploy <${TARGETS.join("|")}> [--init]`)
104
+ process.exit(1)
105
+ }
106
+ console.log(` Auto-detected target: ${target}`)
107
+ }
108
+
109
+ if (!TARGETS.includes(target)) {
110
+ console.error(` Unknown target: ${target}`)
111
+ console.error(` Available: ${TARGETS.join(", ")}`)
112
+ process.exit(1)
113
+ }
114
+
115
+ const projectName = cwd.split("/").pop() ?? "gorsee-app"
116
+ console.log(`\n Generating ${target} deploy config...\n`)
117
+
118
+ switch (target) {
119
+ case "vercel":
120
+ await deployVercel(cwd)
121
+ break
122
+ case "fly":
123
+ await deployFly(cwd, projectName)
124
+ break
125
+ case "cloudflare":
126
+ await deployCloudflare(cwd, projectName)
127
+ break
128
+ case "netlify":
129
+ await deployNetlify(cwd)
130
+ break
131
+ case "docker":
132
+ await deployDocker(cwd)
133
+ break
134
+ }
135
+
136
+ if (initOnly) {
137
+ console.log("\n Config generated (--init mode). Deploy manually when ready.")
138
+ }
139
+
140
+ console.log()
141
+ }
@@ -0,0 +1,145 @@
1
+ // gorsee docs -- generate API documentation from route files
2
+
3
+ import { join } from "node:path"
4
+ import { readFile, mkdir, writeFile } from "node:fs/promises"
5
+ import { createRouter, type Route } from "../router/scanner.ts"
6
+
7
+ const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"] as const
8
+
9
+ interface DocFlags {
10
+ output: string
11
+ format: "md" | "json" | "html"
12
+ routesOnly: boolean
13
+ }
14
+
15
+ interface RouteDoc {
16
+ path: string
17
+ methods: string[]
18
+ hasLoader: boolean
19
+ isApi: boolean
20
+ hasMiddleware: boolean
21
+ title: string
22
+ meta: Record<string, unknown> | null
23
+ }
24
+
25
+ export function parseDocsFlags(args: string[]): DocFlags {
26
+ const flags: DocFlags = { output: "docs/api.md", format: "md", routesOnly: false }
27
+
28
+ for (let i = 0; i < args.length; i++) {
29
+ const arg = args[i]!
30
+ if (arg === "--output" && args[i + 1]) flags.output = args[++i]!
31
+ else if (arg === "--format" && args[i + 1]) {
32
+ const fmt = args[++i]!
33
+ if (fmt === "md" || fmt === "json" || fmt === "html") flags.format = fmt
34
+ } else if (arg === "--routes-only") flags.routesOnly = true
35
+ }
36
+
37
+ return flags
38
+ }
39
+
40
+ async function extractRouteInfo(route: Route): Promise<RouteDoc> {
41
+ const content = await readFile(route.filePath, "utf-8")
42
+ const methods: string[] = []
43
+
44
+ for (const method of HTTP_METHODS) {
45
+ if (new RegExp(`export\\s+(async\\s+)?function\\s+${method}\\b`, "i").test(content)) {
46
+ methods.push(method)
47
+ }
48
+ }
49
+
50
+ const hasDefault = /export\s+default\s+/.test(content)
51
+ const hasLoader = /export\s+(async\s+)?function\s+loader\b/.test(content)
52
+ const isApi = !hasDefault || methods.length > 0
53
+
54
+ if (methods.length === 0 && hasDefault) methods.push("GET")
55
+ if (methods.length === 0 && hasLoader) methods.push("GET")
56
+
57
+ // Extract title from JSDoc or component name
58
+ let title = ""
59
+ const jsdocMatch = content.match(/\/\*\*\s*\n?\s*\*\s*(.+?)(?:\n|\*\/)/s)
60
+ if (jsdocMatch) title = jsdocMatch[1]!.trim()
61
+
62
+ // Extract meta export
63
+ let meta: Record<string, unknown> | null = null
64
+ const metaMatch = content.match(/export\s+const\s+meta\s*=\s*(\{[^}]+\})/)
65
+ if (metaMatch) {
66
+ try { meta = JSON.parse(metaMatch[1]!.replace(/'/g, '"')) } catch { /* skip */ }
67
+ }
68
+
69
+ return {
70
+ path: route.path,
71
+ methods,
72
+ hasLoader,
73
+ isApi,
74
+ hasMiddleware: route.middlewarePaths.length > 0,
75
+ title,
76
+ meta,
77
+ }
78
+ }
79
+
80
+ function generateMarkdown(docs: RouteDoc[]): string {
81
+ const lines = ["# API Documentation", "", "| Path | Methods | Type | Loader | Middleware |", "| --- | --- | --- | --- | --- |"]
82
+ for (const doc of docs) {
83
+ const type = doc.isApi ? "API" : "Page"
84
+ const mw = doc.hasMiddleware ? "Yes" : "-"
85
+ lines.push(`| ${doc.path} | ${doc.methods.join(", ")} | ${type} | ${doc.hasLoader ? "Yes" : "-"} | ${mw} |`)
86
+ }
87
+ return lines.join("\n") + "\n"
88
+ }
89
+
90
+ function generateJson(docs: RouteDoc[]): string {
91
+ return JSON.stringify(docs, null, 2) + "\n"
92
+ }
93
+
94
+ function generateHtml(docs: RouteDoc[]): string {
95
+ const rows = docs.map((d) => {
96
+ const type = d.isApi ? "API" : "Page"
97
+ const mw = d.hasMiddleware ? "Yes" : "-"
98
+ return `<tr><td>${d.path}</td><td>${d.methods.join(", ")}</td><td>${type}</td><td>${d.hasLoader ? "Yes" : "-"}</td><td>${mw}</td></tr>`
99
+ }).join("\n ")
100
+
101
+ return `<!DOCTYPE html>
102
+ <html><head><meta charset="utf-8"><title>API Docs</title>
103
+ <style>body{font-family:sans-serif;margin:2rem}table{border-collapse:collapse;width:100%}
104
+ th,td{border:1px solid #ddd;padding:8px;text-align:left}th{background:#f5f5f5}</style>
105
+ </head><body><h1>API Documentation</h1>
106
+ <table><thead><tr><th>Path</th><th>Methods</th><th>Type</th><th>Loader</th><th>Middleware</th></tr></thead>
107
+ <tbody>${rows}</tbody></table></body></html>\n`
108
+ }
109
+
110
+ const GENERATORS: Record<string, (docs: RouteDoc[]) => string> = {
111
+ md: generateMarkdown,
112
+ json: generateJson,
113
+ html: generateHtml,
114
+ }
115
+
116
+ export async function runDocs(args: string[]) {
117
+ const cwd = process.cwd()
118
+ const flags = parseDocsFlags(args)
119
+ const routesDir = join(cwd, "routes")
120
+
121
+ const routes = await createRouter(routesDir)
122
+ if (routes.length === 0) {
123
+ console.log("\n No routes found in routes/\n")
124
+ return
125
+ }
126
+
127
+ const docs: RouteDoc[] = []
128
+ for (const route of routes) {
129
+ const info = await extractRouteInfo(route)
130
+ if (flags.routesOnly && info.isApi) continue
131
+ docs.push(info)
132
+ }
133
+
134
+ const generate = GENERATORS[flags.format]!
135
+ const content = generate(docs)
136
+
137
+ const outputPath = join(cwd, flags.output)
138
+ await mkdir(join(outputPath, ".."), { recursive: true })
139
+ await writeFile(outputPath, content, "utf-8")
140
+
141
+ console.log(`\n Generated docs for ${docs.length} routes -> ${flags.output}\n`)
142
+ }
143
+
144
+ export { extractRouteInfo, generateMarkdown, generateJson, generateHtml }
145
+ export type { RouteDoc, DocFlags }
@@ -0,0 +1,121 @@
1
+ // gorsee test -- smart test runner wrapping bun test
2
+
3
+ import { readdir, stat } from "node:fs/promises"
4
+ import { join } from "node:path"
5
+
6
+ interface TestFlags {
7
+ watch: boolean
8
+ coverage: boolean
9
+ filter: string | null
10
+ e2e: boolean
11
+ unit: boolean
12
+ integration: boolean
13
+ }
14
+
15
+ function parseFlags(args: string[]): TestFlags {
16
+ const flags: TestFlags = {
17
+ watch: false,
18
+ coverage: false,
19
+ filter: null,
20
+ e2e: false,
21
+ unit: false,
22
+ integration: false,
23
+ }
24
+
25
+ for (let i = 0; i < args.length; i++) {
26
+ const arg = args[i]!
27
+ if (arg === "--watch") flags.watch = true
28
+ else if (arg === "--coverage") flags.coverage = true
29
+ else if (arg === "--filter" && args[i + 1]) flags.filter = args[++i]!
30
+ else if (arg === "--e2e") flags.e2e = true
31
+ else if (arg === "--unit") flags.unit = true
32
+ else if (arg === "--integration") flags.integration = true
33
+ }
34
+
35
+ return flags
36
+ }
37
+
38
+ async function findTestFiles(dir: string, pattern: RegExp): Promise<string[]> {
39
+ const results: string[] = []
40
+ let entries: string[]
41
+ try {
42
+ entries = await readdir(dir)
43
+ } catch {
44
+ return results
45
+ }
46
+
47
+ for (const entry of entries) {
48
+ if (entry === "node_modules" || entry === "dist") continue
49
+ const fullPath = join(dir, entry)
50
+ const s = await stat(fullPath)
51
+ if (s.isDirectory()) {
52
+ results.push(...(await findTestFiles(fullPath, pattern)))
53
+ } else if (pattern.test(fullPath)) {
54
+ results.push(fullPath)
55
+ }
56
+ }
57
+ return results
58
+ }
59
+
60
+ function getTestPattern(flags: TestFlags): RegExp {
61
+ if (flags.e2e) return /(?:\.e2e\.test\.ts$|e2e\/.*\.test\.ts$)/
62
+ if (flags.integration) return /(?:\.integration\.test\.ts$|tests\/integration\/.*\.test\.ts$)/
63
+ if (flags.unit) return /(?:\.unit\.test\.ts$|tests\/unit\/.*\.test\.ts$)/
64
+ return /\.test\.ts$/
65
+ }
66
+
67
+ /** Build bun test args from parsed flags */
68
+ export function buildTestArgs(flags: TestFlags, files: string[]): string[] {
69
+ const bunArgs = ["test"]
70
+
71
+ if (flags.watch) bunArgs.push("--watch")
72
+ if (flags.coverage) bunArgs.push("--coverage")
73
+ if (flags.filter) {
74
+ bunArgs.push("--bail", "--filter", flags.filter)
75
+ }
76
+
77
+ bunArgs.push(...files)
78
+ return bunArgs
79
+ }
80
+
81
+ export async function runTest(args: string[]) {
82
+ const cwd = process.cwd()
83
+ const flags = parseFlags(args)
84
+ const pattern = getTestPattern(flags)
85
+
86
+ const files = await findTestFiles(cwd, pattern)
87
+
88
+ if (files.length === 0) {
89
+ const kind = flags.e2e ? "e2e" : flags.integration ? "integration" : flags.unit ? "unit" : "any"
90
+ console.log(`\n No ${kind} test files found.\n`)
91
+ return
92
+ }
93
+
94
+ console.log(`\n Running ${files.length} test file(s)...\n`)
95
+
96
+ const bunArgs = buildTestArgs(flags, files)
97
+
98
+ const proc = Bun.spawn(["bun", ...bunArgs], {
99
+ cwd,
100
+ stdout: "inherit",
101
+ stderr: "inherit",
102
+ env: {
103
+ ...process.env,
104
+ NODE_ENV: "test",
105
+ GORSEE_TEST: "1",
106
+ },
107
+ })
108
+
109
+ const exitCode = await proc.exited
110
+
111
+ console.log()
112
+ if (exitCode === 0) {
113
+ console.log(` Tests passed (${files.length} file(s))`)
114
+ } else {
115
+ console.log(` Tests failed (exit code ${exitCode})`)
116
+ process.exit(exitCode)
117
+ }
118
+ }
119
+
120
+ // Re-export for testing
121
+ export { parseFlags, findTestFiles, getTestPattern }
@@ -0,0 +1,135 @@
1
+ // gorsee upgrade -- upgrade framework version with migration guidance
2
+
3
+ import { readFile } from "node:fs/promises"
4
+ import { join } from "node:path"
5
+
6
+ interface UpgradeFlags {
7
+ check: boolean
8
+ force: boolean
9
+ }
10
+
11
+ export function parseUpgradeFlags(args: string[]): UpgradeFlags {
12
+ const flags: UpgradeFlags = { check: false, force: false }
13
+ for (const arg of args) {
14
+ if (arg === "--check") flags.check = true
15
+ else if (arg === "--force") flags.force = true
16
+ }
17
+ return flags
18
+ }
19
+
20
+ /** Compare semver strings: -1 (a < b), 0 (equal), 1 (a > b) */
21
+ export function compareVersions(a: string, b: string): number {
22
+ const pa = a.replace(/^v/, "").split(".").map(Number)
23
+ const pb = b.replace(/^v/, "").split(".").map(Number)
24
+ for (let i = 0; i < 3; i++) {
25
+ const va = pa[i] ?? 0
26
+ const vb = pb[i] ?? 0
27
+ if (va < vb) return -1
28
+ if (va > vb) return 1
29
+ }
30
+ return 0
31
+ }
32
+
33
+ async function getCurrentVersion(cwd: string): Promise<string | null> {
34
+ try {
35
+ const pkg = await readFile(join(cwd, "node_modules/gorsee/package.json"), "utf-8")
36
+ return JSON.parse(pkg).version ?? null
37
+ } catch {
38
+ return null
39
+ }
40
+ }
41
+
42
+ export const NPM_REGISTRY_URL = "https://registry.npmjs.org/gorsee/latest"
43
+
44
+ async function fetchLatestVersion(): Promise<string | null> {
45
+ try {
46
+ const res = await fetch(NPM_REGISTRY_URL)
47
+ if (!res.ok) return null
48
+ const data = (await res.json()) as { version?: string }
49
+ return data.version ?? null
50
+ } catch {
51
+ return null
52
+ }
53
+ }
54
+
55
+ async function checkMigrationHints(cwd: string): Promise<string[]> {
56
+ const hints: string[] = []
57
+
58
+ // Check tsconfig.json jsx setting
59
+ try {
60
+ const tsconfig = await readFile(join(cwd, "tsconfig.json"), "utf-8")
61
+ const parsed = JSON.parse(tsconfig)
62
+ if (parsed.compilerOptions?.jsx !== "react-jsx") {
63
+ hints.push("tsconfig.json: set compilerOptions.jsx to \"react-jsx\"")
64
+ }
65
+ } catch { /* no tsconfig */ }
66
+
67
+ // Check for deprecated app.config.ts keys
68
+ try {
69
+ const config = await readFile(join(cwd, "app.config.ts"), "utf-8")
70
+ if (config.includes("ssr:")) {
71
+ hints.push("app.config.ts: 'ssr' key is deprecated, use 'rendering' instead")
72
+ }
73
+ } catch { /* no config */ }
74
+
75
+ return hints
76
+ }
77
+
78
+ export async function runUpgrade(args: string[]) {
79
+ const cwd = process.cwd()
80
+ const flags = parseUpgradeFlags(args)
81
+
82
+ const current = await getCurrentVersion(cwd)
83
+ if (!current) {
84
+ console.log("\n Gorsee.js not found in node_modules. Run: bun add gorsee\n")
85
+ return
86
+ }
87
+
88
+ console.log(`\n Current version: v${current}`)
89
+
90
+ const latest = await fetchLatestVersion()
91
+ if (!latest) {
92
+ console.log(" Could not fetch latest version from npm registry.\n")
93
+ return
94
+ }
95
+
96
+ if (compareVersions(current, latest) >= 0) {
97
+ console.log(` Already up to date (v${current})\n`)
98
+ return
99
+ }
100
+
101
+ console.log(` Latest version: v${latest}`)
102
+ console.log(` Upgrade: v${current} -> v${latest}`)
103
+
104
+ if (flags.check) {
105
+ console.log()
106
+ return
107
+ }
108
+
109
+ if (!flags.force) {
110
+ console.log("\n Run with --force to install, or confirm interactively.")
111
+ }
112
+
113
+ console.log("\n Installing gorsee@latest...")
114
+ const proc = Bun.spawn(["bun", "add", "gorsee@latest"], {
115
+ cwd,
116
+ stdout: "inherit",
117
+ stderr: "inherit",
118
+ })
119
+ const exitCode = await proc.exited
120
+
121
+ if (exitCode !== 0) {
122
+ console.log(`\n Install failed (exit code ${exitCode})\n`)
123
+ process.exit(exitCode)
124
+ }
125
+
126
+ const hints = await checkMigrationHints(cwd)
127
+ if (hints.length > 0) {
128
+ console.log("\n Migration hints:")
129
+ for (const hint of hints) {
130
+ console.log(` - ${hint}`)
131
+ }
132
+ }
133
+
134
+ console.log(`\n Upgraded successfully to v${latest}\n`)
135
+ }
package/src/cli/index.ts CHANGED
@@ -14,6 +14,10 @@ const COMMANDS: Record<string, string> = {
14
14
  migrate: "Run database migrations",
15
15
  generate: "Generate CRUD scaffold for entity",
16
16
  typegen: "Generate typed route definitions",
17
+ deploy: "Generate deploy config (vercel/fly/cloudflare/netlify/docker)",
18
+ test: "Run tests (unit/integration/e2e)",
19
+ docs: "Generate API documentation from routes",
20
+ upgrade: "Upgrade Gorsee.js to latest version",
17
21
  help: "Show this help message",
18
22
  }
19
23
 
@@ -55,6 +59,25 @@ async function main() {
55
59
  const { runTypegen } = await import("./cmd-typegen.ts")
56
60
  await runTypegen(args.slice(1))
57
61
  break
62
+ case "deploy":
63
+ const { runDeploy } = await import("./cmd-deploy.ts")
64
+ await runDeploy(args.slice(1))
65
+ break
66
+ case "test": {
67
+ const { runTest } = await import("./cmd-test.ts")
68
+ await runTest(args.slice(1))
69
+ break
70
+ }
71
+ case "docs": {
72
+ const { runDocs } = await import("./cmd-docs.ts")
73
+ await runDocs(args.slice(1))
74
+ break
75
+ }
76
+ case "upgrade": {
77
+ const { runUpgrade } = await import("./cmd-upgrade.ts")
78
+ await runUpgrade(args.slice(1))
79
+ break
80
+ }
58
81
  case "help":
59
82
  case undefined:
60
83
  case "--help":