create-better-t-stack 3.39.2 → 3.40.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/README.md +4 -3
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +45 -11
- package/dist/index.mjs +2 -2
- package/dist/{src-BYgmfow5.mjs → src-DBE8djc0.mjs} +462 -60
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ generated commands.
|
|
|
43
43
|
| Category | Options |
|
|
44
44
|
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
45
|
| **TypeScript** | End-to-end type safety across all parts of your application |
|
|
46
|
-
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>•
|
|
46
|
+
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• Solid (SSR)<br>• Astro<br>• React Native bare Expo<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
|
47
47
|
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Fastify<br>• Self (fullstack inside the web app)<br>• Convex<br>• None |
|
|
48
48
|
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
|
49
49
|
| **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
|
|
@@ -78,6 +78,7 @@ Options:
|
|
|
78
78
|
--package-manager <pm> Package manager (npm, pnpm, bun)
|
|
79
79
|
--install Install dependencies
|
|
80
80
|
--no-install Skip installing dependencies
|
|
81
|
+
--open <target> Open in an editor, IDE, or coding agent after creation
|
|
81
82
|
--db-setup <setup> Database setup (turso, d1, neon, supabase, prisma-postgres, planetscale, mongodb-atlas, docker, none)
|
|
82
83
|
--web-deploy <setup> Web deployment (cloudflare, docker, vercel, none)
|
|
83
84
|
--server-deploy <setup> Server deployment (cloudflare, docker, vercel, none)
|
|
@@ -259,8 +260,8 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
|
|
|
259
260
|
- **Cloudflare D1 setup**: Requires `sqlite` and either `--runtime workers --server-deploy cloudflare` or `--backend self --web-deploy cloudflare`. For `backend self`, D1 is supported on `next`, `tanstack-start`, `nuxt`, `svelte`, `solid`, and `astro`.
|
|
260
261
|
- **Addons 'none'**: Skips all addons.
|
|
261
262
|
- **Examples 'none'**: Skips all example implementations (todo, AI chat).
|
|
262
|
-
- **Nuxt, Svelte,
|
|
263
|
-
- **PWA support** requires TanStack Router, React Router, Next.js, or
|
|
263
|
+
- **Nuxt, Svelte, Solid, and Astro** frontends are only compatible with the oRPC API layer
|
|
264
|
+
- **PWA support** requires TanStack Router, React Router, Next.js, or Solid
|
|
264
265
|
- **Tauri desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, or Astro
|
|
265
266
|
- **Electrobun desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, or Astro. Desktop packaging uses static web assets, so SSR-first frontends need a static/export build before desktop builds will work.
|
|
266
267
|
- **AI example** is not compatible with Solid or Astro. With Convex backend, it also excludes Nuxt and Svelte.
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as types_exports, y as getLatestCLIVersion } from "./src-DBE8djc0.mjs";
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
package/dist/index.d.mts
CHANGED
|
@@ -96,6 +96,39 @@ declare class DatabaseSetupError extends DatabaseSetupError_base<{
|
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
//#endregion
|
|
99
|
+
//#region src/utils/project-launcher.d.ts
|
|
100
|
+
declare const ProjectLauncherSchema: z.ZodEnum<{
|
|
101
|
+
none: "none";
|
|
102
|
+
cursor: "cursor";
|
|
103
|
+
vscode: "vscode";
|
|
104
|
+
"vscode-insiders": "vscode-insiders";
|
|
105
|
+
zed: "zed";
|
|
106
|
+
windsurf: "windsurf";
|
|
107
|
+
vscodium: "vscodium";
|
|
108
|
+
webstorm: "webstorm";
|
|
109
|
+
"intellij-idea": "intellij-idea";
|
|
110
|
+
"sublime-text": "sublime-text";
|
|
111
|
+
neovim: "neovim";
|
|
112
|
+
"codex-app": "codex-app";
|
|
113
|
+
codex: "codex";
|
|
114
|
+
"claude-code": "claude-code";
|
|
115
|
+
opencode: "opencode";
|
|
116
|
+
pi: "pi";
|
|
117
|
+
"gemini-cli": "gemini-cli";
|
|
118
|
+
"github-copilot": "github-copilot";
|
|
119
|
+
"kiro-cli": "kiro-cli";
|
|
120
|
+
droid: "droid";
|
|
121
|
+
goose: "goose";
|
|
122
|
+
cline: "cline";
|
|
123
|
+
continue: "continue";
|
|
124
|
+
amp: "amp";
|
|
125
|
+
aider: "aider";
|
|
126
|
+
"qwen-code": "qwen-code";
|
|
127
|
+
crush: "crush";
|
|
128
|
+
"cursor-agent": "cursor-agent";
|
|
129
|
+
}>;
|
|
130
|
+
type ProjectLauncher = z.infer<typeof ProjectLauncherSchema>;
|
|
131
|
+
//#endregion
|
|
99
132
|
//#region src/helpers/core/command-handlers.d.ts
|
|
100
133
|
/**
|
|
101
134
|
* Result type for project creation
|
|
@@ -175,6 +208,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
175
208
|
git?: boolean | undefined;
|
|
176
209
|
packageManager?: "bun" | "npm" | "pnpm" | undefined;
|
|
177
210
|
install?: boolean | undefined;
|
|
211
|
+
open?: "none" | "cursor" | "vscode" | "vscode-insiders" | "zed" | "windsurf" | "vscodium" | "webstorm" | "intellij-idea" | "sublime-text" | "neovim" | "codex-app" | "codex" | "claude-code" | "opencode" | "pi" | "gemini-cli" | "github-copilot" | "kiro-cli" | "droid" | "goose" | "cline" | "continue" | "amp" | "aider" | "qwen-code" | "crush" | "cursor-agent" | undefined;
|
|
178
212
|
dbSetup?: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "d1" | "docker" | undefined;
|
|
179
213
|
backend?: "none" | "hono" | "express" | "fastify" | "elysia" | "convex" | "self" | undefined;
|
|
180
214
|
runtime?: "none" | "bun" | "node" | "workers" | undefined;
|
|
@@ -186,7 +220,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
186
220
|
disableAnalytics?: boolean | undefined;
|
|
187
221
|
manualDb?: boolean | undefined;
|
|
188
222
|
dbSetupOptions?: {
|
|
189
|
-
mode?: "manual" | "auto" | undefined;
|
|
223
|
+
mode?: "manual" | "auto" | "alchemy" | undefined;
|
|
190
224
|
neon?: {
|
|
191
225
|
method?: "neon" | "neon-new" | "neondb" | "neonctl" | undefined;
|
|
192
226
|
projectName?: string | undefined;
|
|
@@ -231,11 +265,11 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
231
265
|
mcp?: {
|
|
232
266
|
scope?: "project" | "global" | undefined;
|
|
233
267
|
servers?: ("nx" | "better-auth" | "clerk" | "polar" | "neon" | "planetscale" | "supabase" | "convex" | "better-t-stack" | "context7" | "cloudflare-docs" | "shadcn" | "next-devtools" | "nuxt-docs" | "nuxt-ui-docs" | "svelte-docs" | "astro-docs" | "expo")[] | undefined;
|
|
234
|
-
agents?: ("
|
|
268
|
+
agents?: ("cursor" | "vscode" | "zed" | "windsurf" | "codex" | "claude-code" | "opencode" | "gemini-cli" | "goose" | "cline" | "antigravity" | "cline-cli" | "github-copilot-cli" | "grok-build" | "mcporter" | "claude-desktop")[] | undefined;
|
|
235
269
|
} | undefined;
|
|
236
270
|
skills?: {
|
|
237
271
|
scope?: "project" | "global" | undefined;
|
|
238
|
-
agents?: ("
|
|
272
|
+
agents?: ("cursor" | "zed" | "windsurf" | "codex" | "claude-code" | "opencode" | "pi" | "gemini-cli" | "github-copilot" | "kiro-cli" | "droid" | "goose" | "cline" | "continue" | "amp" | "qwen-code" | "crush" | "antigravity" | "universal" | "adal" | "aider-desk" | "antigravity-cli" | "astrbot" | "augment" | "autohand-code" | "bob" | "codearts-agent" | "codebuddy" | "codemaker" | "codestudio" | "command-code" | "cortex" | "deepagents" | "devin" | "dexto" | "eve" | "firebender" | "forgecode" | "grok" | "hermes-agent" | "iflow-cli" | "inference-sh" | "jazz" | "junie" | "kilo" | "kimchi" | "kimi-code-cli" | "kode" | "lingma" | "loaf" | "mcpjam" | "mistral-vibe" | "moxby" | "mux" | "neovate" | "ona" | "clawdbot" | "openclaw" | "openhands" | "pochi" | "promptscript" | "qoder" | "qoder-cn" | "reasonix" | "replit" | "roo" | "rovodev" | "tabnine-cli" | "terramind" | "tinycloud" | "trae" | "trae-cn" | "warp" | "zcode" | "zencoder" | "zenflow")[] | undefined;
|
|
239
273
|
selections?: {
|
|
240
274
|
source: "vercel-labs/agent-skills" | "vercel/ai" | "vercel/turborepo" | "yusukebe/hono-skill" | "vercel-labs/next-skills" | "nuxt/ui" | "heroui-inc/heroui" | "shadcn/ui" | "better-auth/skills" | "clerk/skills" | "neondatabase/agent-skills" | "supabase/agent-skills" | "planetscale/database-skills" | "expo/skills" | "prisma/skills" | "elysiajs/skills" | "waynesutton/convexskills" | "msmps/opentui-skill" | "haydenbleasel/ultracite" | "https://www.evlog.dev";
|
|
241
275
|
skills: string[];
|
|
@@ -243,13 +277,13 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
243
277
|
} | undefined;
|
|
244
278
|
ultracite?: {
|
|
245
279
|
linter?: "biome" | "oxlint" | undefined;
|
|
246
|
-
editors?: ("void" | "
|
|
247
|
-
agents?: ("
|
|
280
|
+
editors?: ("void" | "cursor" | "vscode" | "zed" | "windsurf" | "antigravity" | "bob" | "codebuddy" | "trae" | "kiro")[] | undefined;
|
|
281
|
+
agents?: ("codex" | "opencode" | "pi" | "droid" | "goose" | "cline" | "continue" | "amp" | "aider" | "crush" | "vercel" | "universal" | "adal" | "bob" | "deepagents" | "devin" | "firebender" | "junie" | "mcpjam" | "mistral-vibe" | "mux" | "ona" | "openclaw" | "qoder" | "replit" | "warp" | "zencoder" | "claude" | "jules" | "lovable" | "snowflake-cortex" | "kimi-cli" | "copilot" | "firebase-studio" | "open-hands" | "gemini" | "augmentcode" | "kilo-code" | "roo-code" | "qwen" | "amazon-q-cli" | "cursor-cli")[] | undefined;
|
|
248
282
|
hooks?: ("cursor" | "windsurf" | "codebuddy" | "claude" | "copilot")[] | undefined;
|
|
249
283
|
} | undefined;
|
|
250
284
|
} | undefined;
|
|
251
285
|
dbSetupOptions?: {
|
|
252
|
-
mode?: "manual" | "auto" | undefined;
|
|
286
|
+
mode?: "manual" | "auto" | "alchemy" | undefined;
|
|
253
287
|
neon?: {
|
|
254
288
|
method?: "neon" | "neon-new" | "neondb" | "neonctl" | undefined;
|
|
255
289
|
projectName?: string | undefined;
|
|
@@ -342,11 +376,11 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
342
376
|
mcp?: {
|
|
343
377
|
scope?: "project" | "global" | undefined;
|
|
344
378
|
servers?: ("nx" | "better-auth" | "clerk" | "polar" | "neon" | "planetscale" | "supabase" | "convex" | "better-t-stack" | "context7" | "cloudflare-docs" | "shadcn" | "next-devtools" | "nuxt-docs" | "nuxt-ui-docs" | "svelte-docs" | "astro-docs" | "expo")[] | undefined;
|
|
345
|
-
agents?: ("
|
|
379
|
+
agents?: ("cursor" | "vscode" | "zed" | "windsurf" | "codex" | "claude-code" | "opencode" | "gemini-cli" | "goose" | "cline" | "antigravity" | "cline-cli" | "github-copilot-cli" | "grok-build" | "mcporter" | "claude-desktop")[] | undefined;
|
|
346
380
|
} | undefined;
|
|
347
381
|
skills?: {
|
|
348
382
|
scope?: "project" | "global" | undefined;
|
|
349
|
-
agents?: ("
|
|
383
|
+
agents?: ("cursor" | "zed" | "windsurf" | "codex" | "claude-code" | "opencode" | "pi" | "gemini-cli" | "github-copilot" | "kiro-cli" | "droid" | "goose" | "cline" | "continue" | "amp" | "qwen-code" | "crush" | "antigravity" | "universal" | "adal" | "aider-desk" | "antigravity-cli" | "astrbot" | "augment" | "autohand-code" | "bob" | "codearts-agent" | "codebuddy" | "codemaker" | "codestudio" | "command-code" | "cortex" | "deepagents" | "devin" | "dexto" | "eve" | "firebender" | "forgecode" | "grok" | "hermes-agent" | "iflow-cli" | "inference-sh" | "jazz" | "junie" | "kilo" | "kimchi" | "kimi-code-cli" | "kode" | "lingma" | "loaf" | "mcpjam" | "mistral-vibe" | "moxby" | "mux" | "neovate" | "ona" | "clawdbot" | "openclaw" | "openhands" | "pochi" | "promptscript" | "qoder" | "qoder-cn" | "reasonix" | "replit" | "roo" | "rovodev" | "tabnine-cli" | "terramind" | "tinycloud" | "trae" | "trae-cn" | "warp" | "zcode" | "zencoder" | "zenflow")[] | undefined;
|
|
350
384
|
selections?: {
|
|
351
385
|
source: "vercel-labs/agent-skills" | "vercel/ai" | "vercel/turborepo" | "yusukebe/hono-skill" | "vercel-labs/next-skills" | "nuxt/ui" | "heroui-inc/heroui" | "shadcn/ui" | "better-auth/skills" | "clerk/skills" | "neondatabase/agent-skills" | "supabase/agent-skills" | "planetscale/database-skills" | "expo/skills" | "prisma/skills" | "elysiajs/skills" | "waynesutton/convexskills" | "msmps/opentui-skill" | "haydenbleasel/ultracite" | "https://www.evlog.dev";
|
|
352
386
|
skills: string[];
|
|
@@ -354,8 +388,8 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
354
388
|
} | undefined;
|
|
355
389
|
ultracite?: {
|
|
356
390
|
linter?: "biome" | "oxlint" | undefined;
|
|
357
|
-
editors?: ("void" | "
|
|
358
|
-
agents?: ("
|
|
391
|
+
editors?: ("void" | "cursor" | "vscode" | "zed" | "windsurf" | "antigravity" | "bob" | "codebuddy" | "trae" | "kiro")[] | undefined;
|
|
392
|
+
agents?: ("codex" | "opencode" | "pi" | "droid" | "goose" | "cline" | "continue" | "amp" | "aider" | "crush" | "vercel" | "universal" | "adal" | "bob" | "deepagents" | "devin" | "firebender" | "junie" | "mcpjam" | "mistral-vibe" | "mux" | "ona" | "openclaw" | "qoder" | "replit" | "warp" | "zencoder" | "claude" | "jules" | "lovable" | "snowflake-cortex" | "kimi-cli" | "copilot" | "firebase-studio" | "open-hands" | "gemini" | "augmentcode" | "kilo-code" | "roo-code" | "qwen" | "amazon-q-cli" | "cursor-cli")[] | undefined;
|
|
359
393
|
hooks?: ("cursor" | "windsurf" | "codebuddy" | "claude" | "copilot")[] | undefined;
|
|
360
394
|
} | undefined;
|
|
361
395
|
} | undefined;
|
|
@@ -478,4 +512,4 @@ type Runtime = import__better_t_stack_types.Runtime;
|
|
|
478
512
|
type ServerDeploy = import__better_t_stack_types.ServerDeploy;
|
|
479
513
|
type Template = import__better_t_stack_types.Template;
|
|
480
514
|
type WebDeploy = import__better_t_stack_types.WebDeploy;
|
|
481
|
-
export { type API, AddOptions, type AddResult, type AddonOptions, type Addons, type Auth, type Backend, type BetterTStackConfig, CLIError, CompatibilityError, CreateError, type CreateInput, type Database, type DatabaseSetup, DatabaseSetupError, type DbSetupOptions, type DirectoryConflict, DirectoryConflictError, EMBEDDED_TEMPLATES, type Examples, type Frontend, GeneratorError, type GeneratorOptions, type InitResult, type ORM, type PackageManager, type Payments, type ProjectConfig, ProjectCreationError, Result, type Runtime, SchemaName, SchemaNameSchema, type ServerDeploy, TEMPLATE_COUNT, type Template, UserCancelledError, ValidationError, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
|
515
|
+
export { type API, AddOptions, type AddResult, type AddonOptions, type Addons, type Auth, type Backend, type BetterTStackConfig, CLIError, CompatibilityError, CreateError, type CreateInput, type Database, type DatabaseSetup, DatabaseSetupError, type DbSetupOptions, type DirectoryConflict, DirectoryConflictError, EMBEDDED_TEMPLATES, type Examples, type Frontend, GeneratorError, type GeneratorOptions, type InitResult, type ORM, type PackageManager, type Payments, type ProjectConfig, ProjectCreationError, type ProjectLauncher, ProjectLauncherSchema, Result, type Runtime, SchemaName, SchemaNameSchema, type ServerDeploy, TEMPLATE_COUNT, type Template, UserCancelledError, ValidationError, type VirtualDirectory, type VirtualFile, VirtualFileSystem, type VirtualFileTree, type VirtualNode, type WebDeploy, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as
|
|
3
|
-
export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
|
2
|
+
import { C as DirectoryConflictError, E as ValidationError, S as DatabaseSetupError, T as UserCancelledError, _ as ProjectLauncherSchema, a as TEMPLATE_COUNT, b as CLIError, c as builder, d as createVirtual, f as docs, g as sponsors, h as router, i as SchemaNameSchema, l as create, m as getSchemaResult, n as GeneratorError, o as VirtualFileSystem, p as generate, r as Result, s as add, t as EMBEDDED_TEMPLATES, u as createBtsCli, w as ProjectCreationError, x as CompatibilityError } from "./src-DBE8djc0.mjs";
|
|
3
|
+
export { CLIError, CompatibilityError, DatabaseSetupError, DirectoryConflictError, EMBEDDED_TEMPLATES, GeneratorError, ProjectCreationError, ProjectLauncherSchema, Result, SchemaNameSchema, TEMPLATE_COUNT, UserCancelledError, ValidationError, VirtualFileSystem, add, builder, create, createBtsCli, createVirtual, docs, generate, getSchemaResult, router, sponsors };
|
|
@@ -32,7 +32,7 @@ const VALUE_LABELS = {
|
|
|
32
32
|
next: "Next.js",
|
|
33
33
|
nuxt: "Nuxt",
|
|
34
34
|
svelte: "SvelteKit",
|
|
35
|
-
solid: "
|
|
35
|
+
solid: "Solid",
|
|
36
36
|
astro: "Astro",
|
|
37
37
|
"native-bare": "Expo (bare)",
|
|
38
38
|
"native-uniwind": "Expo + Uniwind",
|
|
@@ -88,6 +88,9 @@ const VALUE_LABELS = {
|
|
|
88
88
|
docker: "Docker",
|
|
89
89
|
cloudflare: "Cloudflare",
|
|
90
90
|
vercel: "Vercel",
|
|
91
|
+
alchemy: "Alchemy",
|
|
92
|
+
auto: "Automatic",
|
|
93
|
+
manual: "Manual",
|
|
91
94
|
npm: "npm",
|
|
92
95
|
pnpm: "pnpm"
|
|
93
96
|
};
|
|
@@ -132,7 +135,8 @@ function getConfigSections(config) {
|
|
|
132
135
|
section("Data", [
|
|
133
136
|
["Database", config.database],
|
|
134
137
|
["ORM", config.orm],
|
|
135
|
-
["Setup", config.dbSetup]
|
|
138
|
+
["Setup", config.dbSetup],
|
|
139
|
+
["Provisioning", config.dbSetupOptions?.mode]
|
|
136
140
|
]),
|
|
137
141
|
section("Product", [
|
|
138
142
|
["Auth", config.auth],
|
|
@@ -964,11 +968,11 @@ function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
|
964
968
|
const frontends = config.frontend || options.frontend || [];
|
|
965
969
|
if (backend === "self") {
|
|
966
970
|
const { web, native } = splitFrontends(frontends);
|
|
967
|
-
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) return validationErr$1("Backend 'self' (fullstack) currently only supports Next.js, TanStack Start, Nuxt, SvelteKit,
|
|
971
|
+
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) return validationErr$1("Backend 'self' (fullstack) currently only supports Next.js, TanStack Start, Nuxt, SvelteKit, Solid, and Astro frontends. Please use --frontend next, --frontend tanstack-start, --frontend nuxt, --frontend svelte, --frontend solid, or --frontend astro.");
|
|
968
972
|
if (native.length > 1) return validationErr$1("Cannot select multiple native frameworks. Choose only one of: native-bare, native-uniwind, native-unistyles");
|
|
969
973
|
}
|
|
970
974
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
971
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") return validationErr$1("Backend 'self' (fullstack) currently only supports Next.js, TanStack Start, Nuxt, SvelteKit,
|
|
975
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") return validationErr$1("Backend 'self' (fullstack) currently only supports Next.js, TanStack Start, Nuxt, SvelteKit, Solid, and Astro frontends. Please use --frontend next, --frontend tanstack-start, --frontend nuxt, --frontend svelte, --frontend solid, --frontend astro, or choose a different backend.");
|
|
972
976
|
return Result.ok(void 0);
|
|
973
977
|
}
|
|
974
978
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
@@ -1074,7 +1078,7 @@ function supportsPrismaWebDeploy(frontend) {
|
|
|
1074
1078
|
}
|
|
1075
1079
|
function validatePrismaWebDeploy(webDeploy, frontend) {
|
|
1076
1080
|
if (webDeploy !== "prisma" || !frontend) return Result.ok(void 0);
|
|
1077
|
-
if (!supportsPrismaWebDeploy(frontend)) return validationErr$1("'--web-deploy prisma' requires a supported server frontend. Choose Next.js, Nuxt, Astro, React Router, TanStack Start, SvelteKit, or
|
|
1081
|
+
if (!supportsPrismaWebDeploy(frontend)) return validationErr$1("'--web-deploy prisma' requires a supported server frontend. Choose Next.js, Nuxt, Astro, React Router, TanStack Start, SvelteKit, or Solid. TanStack Router is a static SPA, while Prisma Compute requires an executable server artifact.");
|
|
1078
1082
|
return Result.ok(void 0);
|
|
1079
1083
|
}
|
|
1080
1084
|
function validateCloudflareWebDeployKnownIssues(config) {
|
|
@@ -1763,7 +1767,7 @@ function getNodeToolingRequirements(config) {
|
|
|
1763
1767
|
addNodeRequirement(requirements, "^22.19.0 || ^24.11.0 || >=26.0.0", "Nuxt 4");
|
|
1764
1768
|
break;
|
|
1765
1769
|
case "solid":
|
|
1766
|
-
addNodeRequirement(requirements, ">=24.0.0", "
|
|
1770
|
+
addNodeRequirement(requirements, ">=24.0.0", "Solid");
|
|
1767
1771
|
break;
|
|
1768
1772
|
case "react-router":
|
|
1769
1773
|
addNodeRequirement(requirements, ">=22.22.0", "React Router 8");
|
|
@@ -4888,6 +4892,54 @@ async function addHandlerInternal(input) {
|
|
|
4888
4892
|
});
|
|
4889
4893
|
}
|
|
4890
4894
|
//#endregion
|
|
4895
|
+
//#region src/helpers/core/db-setup-options.ts
|
|
4896
|
+
function withDbSetupMode(dbSetupOptions, mode) {
|
|
4897
|
+
const resolved = { ...dbSetupOptions };
|
|
4898
|
+
if (mode === void 0) delete resolved.mode;
|
|
4899
|
+
else resolved.mode = mode;
|
|
4900
|
+
return Object.keys(resolved).length === 0 ? void 0 : resolved;
|
|
4901
|
+
}
|
|
4902
|
+
const REMOTE_PROVISIONING_DB_SETUPS = [
|
|
4903
|
+
"turso",
|
|
4904
|
+
"neon",
|
|
4905
|
+
"prisma-postgres",
|
|
4906
|
+
"supabase",
|
|
4907
|
+
"mongodb-atlas"
|
|
4908
|
+
];
|
|
4909
|
+
function requiresProvisioningGuardrails(dbSetup) {
|
|
4910
|
+
return REMOTE_PROVISIONING_DB_SETUPS.includes(dbSetup);
|
|
4911
|
+
}
|
|
4912
|
+
function resolveDbSetupMode(dbSetup, cliOptions = {}) {
|
|
4913
|
+
if (dbSetup === "none") return;
|
|
4914
|
+
const explicitMode = cliOptions.dbSetupOptions?.mode;
|
|
4915
|
+
if (explicitMode) return explicitMode;
|
|
4916
|
+
if (cliOptions.manualDb === true) return "manual";
|
|
4917
|
+
if (isSilent() && requiresProvisioningGuardrails(dbSetup)) return "manual";
|
|
4918
|
+
}
|
|
4919
|
+
function mergeResolvedDbSetupOptions(dbSetup, dbSetupOptions, cliOptions = {}) {
|
|
4920
|
+
if (dbSetup === "none") return;
|
|
4921
|
+
const resolvedMode = resolveDbSetupMode(dbSetup, {
|
|
4922
|
+
...cliOptions,
|
|
4923
|
+
dbSetupOptions: dbSetupOptions ?? cliOptions.dbSetupOptions
|
|
4924
|
+
});
|
|
4925
|
+
if (!dbSetupOptions && !resolvedMode) return;
|
|
4926
|
+
return {
|
|
4927
|
+
...dbSetupOptions,
|
|
4928
|
+
mode: resolvedMode
|
|
4929
|
+
};
|
|
4930
|
+
}
|
|
4931
|
+
function resolveProjectDbSetupOptions(config, cliOptions = {}) {
|
|
4932
|
+
const dbSetupOptions = config.dbSetupOptions ?? cliOptions.dbSetupOptions;
|
|
4933
|
+
if (!(dbSetupOptions?.mode ?? (cliOptions.manualDb === true ? "manual" : void 0)) && (0, types_exports.supportsAlchemyManagedDatabase)(config)) return {
|
|
4934
|
+
...dbSetupOptions,
|
|
4935
|
+
mode: "alchemy"
|
|
4936
|
+
};
|
|
4937
|
+
return mergeResolvedDbSetupOptions(config.dbSetup, dbSetupOptions, {
|
|
4938
|
+
...cliOptions,
|
|
4939
|
+
dbSetupOptions
|
|
4940
|
+
});
|
|
4941
|
+
}
|
|
4942
|
+
//#endregion
|
|
4891
4943
|
//#region src/prompts/api.ts
|
|
4892
4944
|
async function getApiChoice(Api, frontend, backend, previousValue) {
|
|
4893
4945
|
if (backend === "convex" || backend === "none") return "none";
|
|
@@ -5164,6 +5216,47 @@ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime, p
|
|
|
5164
5216
|
if (isCancel$1(response)) throw new UserCancelledError({ message: "Operation cancelled" });
|
|
5165
5217
|
return response;
|
|
5166
5218
|
}
|
|
5219
|
+
const providerLabels = {
|
|
5220
|
+
neon: "Neon",
|
|
5221
|
+
planetscale: "PlanetScale",
|
|
5222
|
+
"prisma-postgres": "Prisma Postgres"
|
|
5223
|
+
};
|
|
5224
|
+
async function getDbProvisioningChoice(mode, dbSetup, backend, webDeploy, serverDeploy, previousValue) {
|
|
5225
|
+
if (!dbSetup || !backend || !webDeploy || !serverDeploy) return mode;
|
|
5226
|
+
if (!(0, types_exports.supportsAlchemyManagedDatabase)({
|
|
5227
|
+
backend,
|
|
5228
|
+
dbSetup,
|
|
5229
|
+
webDeploy,
|
|
5230
|
+
serverDeploy
|
|
5231
|
+
})) return mode === "alchemy" ? void 0 : mode;
|
|
5232
|
+
if (mode !== void 0) return mode;
|
|
5233
|
+
const provider = providerLabels[dbSetup];
|
|
5234
|
+
if (!provider) return void 0;
|
|
5235
|
+
const options = [
|
|
5236
|
+
{
|
|
5237
|
+
value: "alchemy",
|
|
5238
|
+
label: "Alchemy",
|
|
5239
|
+
hint: `Provision ${provider} during deploy and inject its credentials`
|
|
5240
|
+
},
|
|
5241
|
+
...dbSetup === "neon" || dbSetup === "prisma-postgres" ? [{
|
|
5242
|
+
value: "auto",
|
|
5243
|
+
label: "Automatic",
|
|
5244
|
+
hint: `Set up ${provider} now and write its connection credentials`
|
|
5245
|
+
}] : [],
|
|
5246
|
+
{
|
|
5247
|
+
value: "manual",
|
|
5248
|
+
label: "Manual",
|
|
5249
|
+
hint: `Use an existing ${provider} database and configure credentials yourself`
|
|
5250
|
+
}
|
|
5251
|
+
];
|
|
5252
|
+
const response = await navigableSelect({
|
|
5253
|
+
message: `How should ${provider} be provisioned?`,
|
|
5254
|
+
options,
|
|
5255
|
+
initialValue: preferValidInitial(options, previousValue, "alchemy")
|
|
5256
|
+
});
|
|
5257
|
+
if (isCancel$1(response)) throw new UserCancelledError({ message: "Operation cancelled" });
|
|
5258
|
+
return response;
|
|
5259
|
+
}
|
|
5167
5260
|
//#endregion
|
|
5168
5261
|
//#region src/prompts/examples.ts
|
|
5169
5262
|
async function getExamplesChoice(examples, database, frontends, backend, api, previousValue) {
|
|
@@ -5431,6 +5524,20 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
5431
5524
|
}
|
|
5432
5525
|
return Result.ok(void 0);
|
|
5433
5526
|
}
|
|
5527
|
+
function validateDatabaseProvisioningMode(config) {
|
|
5528
|
+
if (config.dbSetup === "planetscale" && config.dbSetupOptions?.mode === "auto") return validationErr("PlanetScale does not support automatic database setup. Use dbSetupOptions.mode 'alchemy' or 'manual'.");
|
|
5529
|
+
if (config.dbSetupOptions?.mode !== "alchemy") return Result.ok(void 0);
|
|
5530
|
+
const { backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
5531
|
+
if (!backend || !dbSetup || !webDeploy || !serverDeploy) return Result.ok(void 0);
|
|
5532
|
+
if (!(0, types_exports.supportsAlchemyManagedDatabase)({
|
|
5533
|
+
backend,
|
|
5534
|
+
dbSetup,
|
|
5535
|
+
webDeploy,
|
|
5536
|
+
serverDeploy,
|
|
5537
|
+
dbSetupOptions: config.dbSetupOptions
|
|
5538
|
+
})) return validationErr("Alchemy database provisioning requires Neon, PlanetScale, or Prisma Postgres and an Alchemy deployment target for the app that consumes the database.");
|
|
5539
|
+
return Result.ok(void 0);
|
|
5540
|
+
}
|
|
5434
5541
|
function validateConvexConstraints(config, providedFlags) {
|
|
5435
5542
|
const { backend } = config;
|
|
5436
5543
|
if (backend !== "convex") return Result.ok(void 0);
|
|
@@ -5515,6 +5622,7 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
5515
5622
|
return Result.gen(function* () {
|
|
5516
5623
|
yield* validateDatabaseOrmAuth(config, providedFlags);
|
|
5517
5624
|
yield* validateDatabaseSetup(config, providedFlags);
|
|
5625
|
+
yield* validateDatabaseProvisioningMode(config);
|
|
5518
5626
|
yield* validateConvexConstraints(config, providedFlags);
|
|
5519
5627
|
yield* validateBackendNoneConstraints(config, providedFlags);
|
|
5520
5628
|
yield* validateSelfBackendConstraints(config, providedFlags);
|
|
@@ -5827,6 +5935,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath, option
|
|
|
5827
5935
|
examples: ({ results, previousAnswer }) => getExamplesChoice(flags.examples, results.database, results.frontend, results.backend, results.api, previousAnswer),
|
|
5828
5936
|
webDeploy: ({ results, previousAnswer }) => getDeploymentChoice(flags.webDeploy, results.runtime, results.backend, results.frontend, results.dbSetup, results.database, results.orm, results.addons, previousAnswer),
|
|
5829
5937
|
serverDeploy: ({ results, previousAnswer }) => getServerDeploymentChoice(flags.serverDeploy, results.runtime, results.backend, results.webDeploy, previousAnswer),
|
|
5938
|
+
dbSetupMode: ({ results, previousAnswer }) => getDbProvisioningChoice(flags.dbSetupOptions?.mode ?? (options.manualDb === true ? "manual" : void 0), results.dbSetup, results.backend, results.webDeploy, results.serverDeploy, previousAnswer),
|
|
5830
5939
|
git: ({ previousAnswer }) => getGitChoice(flags.git, previousAnswer),
|
|
5831
5940
|
packageManager: ({ previousAnswer }) => getPackageManagerChoice(flags.packageManager, previousAnswer),
|
|
5832
5941
|
install: ({ previousAnswer }) => getinstallChoice(flags.install, previousAnswer)
|
|
@@ -5864,6 +5973,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath, option
|
|
|
5864
5973
|
prompts: [
|
|
5865
5974
|
"webDeploy",
|
|
5866
5975
|
"serverDeploy",
|
|
5976
|
+
"dbSetupMode",
|
|
5867
5977
|
"git",
|
|
5868
5978
|
"packageManager",
|
|
5869
5979
|
"install"
|
|
@@ -5879,7 +5989,7 @@ async function gatherConfig(flags, projectName, projectDir, relativePath, option
|
|
|
5879
5989
|
projectDir,
|
|
5880
5990
|
relativePath,
|
|
5881
5991
|
addonOptions: flags.addonOptions,
|
|
5882
|
-
dbSetupOptions: flags.dbSetupOptions,
|
|
5992
|
+
dbSetupOptions: withDbSetupMode(flags.dbSetupOptions, result.dbSetupMode),
|
|
5883
5993
|
frontend: result.frontend,
|
|
5884
5994
|
backend: result.backend,
|
|
5885
5995
|
runtime: result.runtime,
|
|
@@ -6203,6 +6313,321 @@ async function findAvailableIncrementedPath(currentPathInput) {
|
|
|
6203
6313
|
}
|
|
6204
6314
|
}
|
|
6205
6315
|
//#endregion
|
|
6316
|
+
//#region src/utils/command-exists.ts
|
|
6317
|
+
async function commandExists(command) {
|
|
6318
|
+
const result = await Result.tryPromise({
|
|
6319
|
+
try: async () => {
|
|
6320
|
+
if (process.platform === "win32") return (await $({ reject: false })`where ${command}`).exitCode === 0;
|
|
6321
|
+
return (await $({ reject: false })`which ${command}`).exitCode === 0;
|
|
6322
|
+
},
|
|
6323
|
+
catch: () => false
|
|
6324
|
+
});
|
|
6325
|
+
return result.isOk() ? result.value : false;
|
|
6326
|
+
}
|
|
6327
|
+
//#endregion
|
|
6328
|
+
//#region src/utils/project-launcher.ts
|
|
6329
|
+
const ProjectLauncherSchema = z.enum([
|
|
6330
|
+
"cursor",
|
|
6331
|
+
"vscode",
|
|
6332
|
+
"vscode-insiders",
|
|
6333
|
+
"zed",
|
|
6334
|
+
"windsurf",
|
|
6335
|
+
"vscodium",
|
|
6336
|
+
"webstorm",
|
|
6337
|
+
"intellij-idea",
|
|
6338
|
+
"sublime-text",
|
|
6339
|
+
"neovim",
|
|
6340
|
+
"codex-app",
|
|
6341
|
+
"codex",
|
|
6342
|
+
"claude-code",
|
|
6343
|
+
"opencode",
|
|
6344
|
+
"pi",
|
|
6345
|
+
"gemini-cli",
|
|
6346
|
+
"github-copilot",
|
|
6347
|
+
"kiro-cli",
|
|
6348
|
+
"droid",
|
|
6349
|
+
"goose",
|
|
6350
|
+
"cline",
|
|
6351
|
+
"continue",
|
|
6352
|
+
"amp",
|
|
6353
|
+
"aider",
|
|
6354
|
+
"qwen-code",
|
|
6355
|
+
"crush",
|
|
6356
|
+
"cursor-agent",
|
|
6357
|
+
"none"
|
|
6358
|
+
]).describe("Editor, IDE, or coding agent to open after creating the project");
|
|
6359
|
+
const PROJECT_LAUNCHERS = [
|
|
6360
|
+
{
|
|
6361
|
+
id: "cursor",
|
|
6362
|
+
label: "Cursor",
|
|
6363
|
+
kind: "editor",
|
|
6364
|
+
commands: ["cursor"],
|
|
6365
|
+
args: (projectDir) => [projectDir]
|
|
6366
|
+
},
|
|
6367
|
+
{
|
|
6368
|
+
id: "vscode",
|
|
6369
|
+
label: "Visual Studio Code",
|
|
6370
|
+
kind: "editor",
|
|
6371
|
+
commands: ["code"],
|
|
6372
|
+
args: (projectDir) => [projectDir]
|
|
6373
|
+
},
|
|
6374
|
+
{
|
|
6375
|
+
id: "vscode-insiders",
|
|
6376
|
+
label: "Visual Studio Code Insiders",
|
|
6377
|
+
kind: "editor",
|
|
6378
|
+
commands: ["code-insiders"],
|
|
6379
|
+
args: (projectDir) => [projectDir]
|
|
6380
|
+
},
|
|
6381
|
+
{
|
|
6382
|
+
id: "zed",
|
|
6383
|
+
label: "Zed",
|
|
6384
|
+
kind: "editor",
|
|
6385
|
+
commands: ["zed", "zeditor"],
|
|
6386
|
+
args: (projectDir) => [projectDir]
|
|
6387
|
+
},
|
|
6388
|
+
{
|
|
6389
|
+
id: "windsurf",
|
|
6390
|
+
label: "Windsurf",
|
|
6391
|
+
kind: "editor",
|
|
6392
|
+
commands: ["windsurf"],
|
|
6393
|
+
args: (projectDir) => [projectDir]
|
|
6394
|
+
},
|
|
6395
|
+
{
|
|
6396
|
+
id: "vscodium",
|
|
6397
|
+
label: "VSCodium",
|
|
6398
|
+
kind: "editor",
|
|
6399
|
+
commands: ["codium"],
|
|
6400
|
+
args: (projectDir) => [projectDir]
|
|
6401
|
+
},
|
|
6402
|
+
{
|
|
6403
|
+
id: "webstorm",
|
|
6404
|
+
label: "JetBrains WebStorm",
|
|
6405
|
+
kind: "editor",
|
|
6406
|
+
commands: [
|
|
6407
|
+
"webstorm",
|
|
6408
|
+
"webstorm64.exe",
|
|
6409
|
+
"webstorm.bat",
|
|
6410
|
+
"webstorm.sh"
|
|
6411
|
+
],
|
|
6412
|
+
args: (projectDir) => [projectDir]
|
|
6413
|
+
},
|
|
6414
|
+
{
|
|
6415
|
+
id: "intellij-idea",
|
|
6416
|
+
label: "JetBrains IntelliJ IDEA",
|
|
6417
|
+
kind: "editor",
|
|
6418
|
+
commands: [
|
|
6419
|
+
"idea",
|
|
6420
|
+
"intellij-idea",
|
|
6421
|
+
"idea64.exe",
|
|
6422
|
+
"idea.bat",
|
|
6423
|
+
"idea.sh"
|
|
6424
|
+
],
|
|
6425
|
+
args: (projectDir) => [projectDir]
|
|
6426
|
+
},
|
|
6427
|
+
{
|
|
6428
|
+
id: "sublime-text",
|
|
6429
|
+
label: "Sublime Text",
|
|
6430
|
+
kind: "editor",
|
|
6431
|
+
commands: ["subl"],
|
|
6432
|
+
args: (projectDir) => [projectDir]
|
|
6433
|
+
},
|
|
6434
|
+
{
|
|
6435
|
+
id: "neovim",
|
|
6436
|
+
label: "Neovim",
|
|
6437
|
+
kind: "editor",
|
|
6438
|
+
commands: ["nvim"],
|
|
6439
|
+
args: () => ["."],
|
|
6440
|
+
cwd: (projectDir) => projectDir
|
|
6441
|
+
},
|
|
6442
|
+
{
|
|
6443
|
+
id: "codex-app",
|
|
6444
|
+
label: "Codex app",
|
|
6445
|
+
kind: "agent",
|
|
6446
|
+
commands: ["codex"],
|
|
6447
|
+
args: (projectDir) => ["app", projectDir],
|
|
6448
|
+
platforms: ["darwin"]
|
|
6449
|
+
},
|
|
6450
|
+
{
|
|
6451
|
+
id: "codex",
|
|
6452
|
+
label: "Codex CLI",
|
|
6453
|
+
kind: "agent",
|
|
6454
|
+
commands: ["codex"],
|
|
6455
|
+
cwd: (projectDir) => projectDir
|
|
6456
|
+
},
|
|
6457
|
+
{
|
|
6458
|
+
id: "claude-code",
|
|
6459
|
+
label: "Claude Code",
|
|
6460
|
+
kind: "agent",
|
|
6461
|
+
commands: ["claude"],
|
|
6462
|
+
cwd: (projectDir) => projectDir
|
|
6463
|
+
},
|
|
6464
|
+
{
|
|
6465
|
+
id: "opencode",
|
|
6466
|
+
label: "OpenCode",
|
|
6467
|
+
kind: "agent",
|
|
6468
|
+
commands: ["opencode"],
|
|
6469
|
+
cwd: (projectDir) => projectDir
|
|
6470
|
+
},
|
|
6471
|
+
{
|
|
6472
|
+
id: "pi",
|
|
6473
|
+
label: "Pi",
|
|
6474
|
+
kind: "agent",
|
|
6475
|
+
commands: ["pi"],
|
|
6476
|
+
cwd: (projectDir) => projectDir
|
|
6477
|
+
},
|
|
6478
|
+
{
|
|
6479
|
+
id: "gemini-cli",
|
|
6480
|
+
label: "Gemini CLI",
|
|
6481
|
+
kind: "agent",
|
|
6482
|
+
commands: ["gemini"],
|
|
6483
|
+
cwd: (projectDir) => projectDir
|
|
6484
|
+
},
|
|
6485
|
+
{
|
|
6486
|
+
id: "github-copilot",
|
|
6487
|
+
label: "GitHub Copilot CLI",
|
|
6488
|
+
kind: "agent",
|
|
6489
|
+
commands: ["copilot"],
|
|
6490
|
+
cwd: (projectDir) => projectDir
|
|
6491
|
+
},
|
|
6492
|
+
{
|
|
6493
|
+
id: "kiro-cli",
|
|
6494
|
+
label: "Kiro CLI",
|
|
6495
|
+
kind: "agent",
|
|
6496
|
+
commands: ["kiro-cli"],
|
|
6497
|
+
cwd: (projectDir) => projectDir
|
|
6498
|
+
},
|
|
6499
|
+
{
|
|
6500
|
+
id: "droid",
|
|
6501
|
+
label: "Factory Droid",
|
|
6502
|
+
kind: "agent",
|
|
6503
|
+
commands: ["droid"],
|
|
6504
|
+
cwd: (projectDir) => projectDir
|
|
6505
|
+
},
|
|
6506
|
+
{
|
|
6507
|
+
id: "goose",
|
|
6508
|
+
label: "goose",
|
|
6509
|
+
kind: "agent",
|
|
6510
|
+
commands: ["goose"],
|
|
6511
|
+
args: () => ["session"],
|
|
6512
|
+
cwd: (projectDir) => projectDir
|
|
6513
|
+
},
|
|
6514
|
+
{
|
|
6515
|
+
id: "cline",
|
|
6516
|
+
label: "Cline CLI",
|
|
6517
|
+
kind: "agent",
|
|
6518
|
+
commands: ["cline"],
|
|
6519
|
+
cwd: (projectDir) => projectDir,
|
|
6520
|
+
platforms: ["darwin", "linux"]
|
|
6521
|
+
},
|
|
6522
|
+
{
|
|
6523
|
+
id: "continue",
|
|
6524
|
+
label: "Continue CLI",
|
|
6525
|
+
kind: "agent",
|
|
6526
|
+
commands: ["cn"],
|
|
6527
|
+
cwd: (projectDir) => projectDir
|
|
6528
|
+
},
|
|
6529
|
+
{
|
|
6530
|
+
id: "amp",
|
|
6531
|
+
label: "Amp",
|
|
6532
|
+
kind: "agent",
|
|
6533
|
+
commands: ["amp"],
|
|
6534
|
+
cwd: (projectDir) => projectDir
|
|
6535
|
+
},
|
|
6536
|
+
{
|
|
6537
|
+
id: "aider",
|
|
6538
|
+
label: "Aider",
|
|
6539
|
+
kind: "agent",
|
|
6540
|
+
commands: ["aider"],
|
|
6541
|
+
cwd: (projectDir) => projectDir
|
|
6542
|
+
},
|
|
6543
|
+
{
|
|
6544
|
+
id: "qwen-code",
|
|
6545
|
+
label: "Qwen Code",
|
|
6546
|
+
kind: "agent",
|
|
6547
|
+
commands: ["qwen"],
|
|
6548
|
+
cwd: (projectDir) => projectDir
|
|
6549
|
+
},
|
|
6550
|
+
{
|
|
6551
|
+
id: "crush",
|
|
6552
|
+
label: "Crush",
|
|
6553
|
+
kind: "agent",
|
|
6554
|
+
commands: ["crush"],
|
|
6555
|
+
cwd: (projectDir) => projectDir
|
|
6556
|
+
},
|
|
6557
|
+
{
|
|
6558
|
+
id: "cursor-agent",
|
|
6559
|
+
label: "Cursor Agent",
|
|
6560
|
+
kind: "agent",
|
|
6561
|
+
commands: ["cursor-agent"],
|
|
6562
|
+
cwd: (projectDir) => projectDir
|
|
6563
|
+
}
|
|
6564
|
+
];
|
|
6565
|
+
async function detectProjectLaunchers(projectDir, detectCommand = commandExists, platform = process.platform) {
|
|
6566
|
+
const supportedDefinitions = PROJECT_LAUNCHERS.filter(({ platforms }) => platforms === void 0 || platforms.includes(platform));
|
|
6567
|
+
const commands = [...new Set(supportedDefinitions.flatMap(({ commands }) => commands))];
|
|
6568
|
+
const detectedCommands = new Map(await Promise.all(commands.map(async (command) => [command, await detectCommand(command)])));
|
|
6569
|
+
return supportedDefinitions.flatMap((launcher) => {
|
|
6570
|
+
const command = launcher.commands.find((candidate) => detectedCommands.get(candidate));
|
|
6571
|
+
if (!command) return [];
|
|
6572
|
+
return [{
|
|
6573
|
+
id: launcher.id,
|
|
6574
|
+
label: launcher.label,
|
|
6575
|
+
kind: launcher.kind,
|
|
6576
|
+
command,
|
|
6577
|
+
args: launcher.args?.(projectDir) ?? [],
|
|
6578
|
+
cwd: launcher.cwd?.(projectDir)
|
|
6579
|
+
}];
|
|
6580
|
+
});
|
|
6581
|
+
}
|
|
6582
|
+
async function getProjectLauncherChoice(requestedLauncher, projectDir, options = {}) {
|
|
6583
|
+
if (requestedLauncher === "none") return Result.ok(void 0);
|
|
6584
|
+
if (!requestedLauncher && options.prompt === false) return Result.ok(void 0);
|
|
6585
|
+
const availableLaunchers = await detectProjectLaunchers(projectDir, options.detectCommand, options.platform);
|
|
6586
|
+
if (requestedLauncher) {
|
|
6587
|
+
const launcher = availableLaunchers.find(({ id }) => id === requestedLauncher);
|
|
6588
|
+
if (launcher) return Result.ok(launcher);
|
|
6589
|
+
const definition = PROJECT_LAUNCHERS.find(({ id }) => id === requestedLauncher);
|
|
6590
|
+
return Result.err(new CLIError({ message: `${definition?.label ?? requestedLauncher} is not installed or its command is not available in PATH.` }));
|
|
6591
|
+
}
|
|
6592
|
+
if (availableLaunchers.length === 0) return Result.ok(void 0);
|
|
6593
|
+
setIsFirstPrompt(true);
|
|
6594
|
+
try {
|
|
6595
|
+
const selected = await navigableSelect({
|
|
6596
|
+
message: "Open project with?",
|
|
6597
|
+
options: [...availableLaunchers.map(({ id, label, kind }) => ({
|
|
6598
|
+
value: id,
|
|
6599
|
+
label,
|
|
6600
|
+
hint: kind === "editor" ? "editor / IDE" : "coding agent"
|
|
6601
|
+
})), {
|
|
6602
|
+
value: "none",
|
|
6603
|
+
label: "Not now"
|
|
6604
|
+
}],
|
|
6605
|
+
initialValue: "none",
|
|
6606
|
+
maxItems: 12
|
|
6607
|
+
});
|
|
6608
|
+
if (isCancel$1(selected) || selected === "none") return Result.ok(void 0);
|
|
6609
|
+
return Result.ok(availableLaunchers.find(({ id }) => id === selected));
|
|
6610
|
+
} finally {
|
|
6611
|
+
setIsFirstPrompt(false);
|
|
6612
|
+
}
|
|
6613
|
+
}
|
|
6614
|
+
const runCommand = async (command, args, options) => {
|
|
6615
|
+
await execa(command, args, options);
|
|
6616
|
+
};
|
|
6617
|
+
async function launchProject(launcher, options = {}) {
|
|
6618
|
+
if (options.skipExternalCommands ?? shouldSkipExternalCommands()) return Result.ok(void 0);
|
|
6619
|
+
return Result.tryPromise({
|
|
6620
|
+
try: () => (options.runner ?? runCommand)(launcher.command, launcher.args, {
|
|
6621
|
+
cwd: launcher.cwd,
|
|
6622
|
+
stdio: launcher.cwd ? "inherit" : "ignore"
|
|
6623
|
+
}),
|
|
6624
|
+
catch: (cause) => new CLIError({
|
|
6625
|
+
message: `Could not open the project with ${launcher.label}.`,
|
|
6626
|
+
cause
|
|
6627
|
+
})
|
|
6628
|
+
});
|
|
6629
|
+
}
|
|
6630
|
+
//#endregion
|
|
6206
6631
|
//#region src/utils/project-name-validation.ts
|
|
6207
6632
|
function validateProjectName(name) {
|
|
6208
6633
|
const hardeningResult = validateAgentSafePathInput(name, "projectName");
|
|
@@ -6558,49 +6983,6 @@ function getDatabaseUrl(database, projectName) {
|
|
|
6558
6983
|
}
|
|
6559
6984
|
}
|
|
6560
6985
|
//#endregion
|
|
6561
|
-
//#region src/utils/command-exists.ts
|
|
6562
|
-
async function commandExists(command) {
|
|
6563
|
-
const result = await Result.tryPromise({
|
|
6564
|
-
try: async () => {
|
|
6565
|
-
if (process.platform === "win32") return (await $({ reject: false })`where ${command}`).exitCode === 0;
|
|
6566
|
-
return (await $({ reject: false })`which ${command}`).exitCode === 0;
|
|
6567
|
-
},
|
|
6568
|
-
catch: () => false
|
|
6569
|
-
});
|
|
6570
|
-
return result.isOk() ? result.value : false;
|
|
6571
|
-
}
|
|
6572
|
-
//#endregion
|
|
6573
|
-
//#region src/helpers/core/db-setup-options.ts
|
|
6574
|
-
const REMOTE_PROVISIONING_DB_SETUPS = [
|
|
6575
|
-
"turso",
|
|
6576
|
-
"neon",
|
|
6577
|
-
"prisma-postgres",
|
|
6578
|
-
"supabase",
|
|
6579
|
-
"mongodb-atlas"
|
|
6580
|
-
];
|
|
6581
|
-
function requiresProvisioningGuardrails(dbSetup) {
|
|
6582
|
-
return REMOTE_PROVISIONING_DB_SETUPS.includes(dbSetup);
|
|
6583
|
-
}
|
|
6584
|
-
function resolveDbSetupMode(dbSetup, cliOptions = {}) {
|
|
6585
|
-
if (dbSetup === "none") return;
|
|
6586
|
-
const explicitMode = cliOptions.dbSetupOptions?.mode;
|
|
6587
|
-
if (explicitMode) return explicitMode;
|
|
6588
|
-
if (cliOptions.manualDb === true) return "manual";
|
|
6589
|
-
if (isSilent() && requiresProvisioningGuardrails(dbSetup)) return "manual";
|
|
6590
|
-
}
|
|
6591
|
-
function mergeResolvedDbSetupOptions(dbSetup, dbSetupOptions, cliOptions = {}) {
|
|
6592
|
-
if (dbSetup === "none") return;
|
|
6593
|
-
const resolvedMode = resolveDbSetupMode(dbSetup, {
|
|
6594
|
-
...cliOptions,
|
|
6595
|
-
dbSetupOptions: dbSetupOptions ?? cliOptions.dbSetupOptions
|
|
6596
|
-
});
|
|
6597
|
-
if (!dbSetupOptions && !resolvedMode) return;
|
|
6598
|
-
return {
|
|
6599
|
-
...dbSetupOptions,
|
|
6600
|
-
mode: resolvedMode
|
|
6601
|
-
};
|
|
6602
|
-
}
|
|
6603
|
-
//#endregion
|
|
6604
6986
|
//#region src/helpers/database-providers/mongodb-atlas-setup.ts
|
|
6605
6987
|
async function checkAtlasCLI() {
|
|
6606
6988
|
const exists = await commandExists("atlas");
|
|
@@ -7893,7 +8275,7 @@ function getDesktopStaticBuildNote(frontend) {
|
|
|
7893
8275
|
return `${pc.yellow("NOTE:")} Desktop builds package static web assets.\n ${staticBuildFrontends.get(staticBuildFrontend)} needs a static/export web build before desktop packaging will work.`;
|
|
7894
8276
|
}
|
|
7895
8277
|
async function displayPostInstallInstructions(config) {
|
|
7896
|
-
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
8278
|
+
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy, dbSetupOptions } = config;
|
|
7897
8279
|
const isConvex = backend === "convex";
|
|
7898
8280
|
const isBackendSelf = backend === "self";
|
|
7899
8281
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
@@ -7903,7 +8285,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
7903
8285
|
const hasVitePlus = addons?.includes("vite-plus");
|
|
7904
8286
|
const hasVitePlusNativeHooks = hasVitePlus && !hasHusky && !hasLefthook;
|
|
7905
8287
|
const hasGitHooksOrLinting = addons?.includes("husky") || addons?.includes("biome") || addons?.includes("lefthook") || addons?.includes("oxlint") || hasVitePlus;
|
|
7906
|
-
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, webDeploy, serverDeploy, backend) : "";
|
|
8288
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, webDeploy, serverDeploy, backend, dbSetupOptions) : "";
|
|
7907
8289
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
|
|
7908
8290
|
const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
|
|
7909
8291
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
@@ -7914,7 +8296,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
7914
8296
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
7915
8297
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
7916
8298
|
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(frontend || [], backend, api) : "";
|
|
7917
|
-
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
8299
|
+
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend, config.auth, frontend || []);
|
|
7918
8300
|
const hasWeb = frontend?.some((f) => types_exports.webFrontends.includes(f));
|
|
7919
8301
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
7920
8302
|
const hasReactRouter = frontend?.includes("react-router");
|
|
@@ -8038,7 +8420,7 @@ function getLefthookInstructions(packageManager) {
|
|
|
8038
8420
|
function getVitePlusNativeHooksInstructions(runCmd) {
|
|
8039
8421
|
return `${pc.bold("Vite+ native Git hooks:")}\n${pc.cyan("•")} Optional hook setup: ${`${runCmd} hooks:setup`}\n${pc.dim(" (runs vp config; hooks install into .vite-hooks and use vp staged)")}\n`;
|
|
8040
8422
|
}
|
|
8041
|
-
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend) {
|
|
8423
|
+
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend, dbSetupOptions) {
|
|
8042
8424
|
const notes = [];
|
|
8043
8425
|
const commands = [];
|
|
8044
8426
|
const isD1Alchemy = dbSetup === "d1" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare");
|
|
@@ -8046,7 +8428,8 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
8046
8428
|
backend,
|
|
8047
8429
|
dbSetup,
|
|
8048
8430
|
webDeploy,
|
|
8049
|
-
serverDeploy
|
|
8431
|
+
serverDeploy,
|
|
8432
|
+
dbSetupOptions
|
|
8050
8433
|
});
|
|
8051
8434
|
if (dbSetup === "docker") {
|
|
8052
8435
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -8210,7 +8593,7 @@ function getPolarInstructions(backend, packageManager) {
|
|
|
8210
8593
|
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
8211
8594
|
return `${pc.bold("Polar Payments Setup:")}\n${pc.cyan("•")} Get access token & product ID from ${pc.underline("https://sandbox.polar.sh/")}\n${pc.cyan("•")} Set POLAR_ACCESS_TOKEN in ${envPath}`;
|
|
8212
8595
|
}
|
|
8213
|
-
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
8596
|
+
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend, auth, frontend) {
|
|
8214
8597
|
const instructions = [];
|
|
8215
8598
|
const isBackendSelf = backend === "self";
|
|
8216
8599
|
const hasAlchemyWeb = (0, types_exports.isAlchemyDeployTarget)(webDeploy);
|
|
@@ -8219,7 +8602,12 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend)
|
|
|
8219
8602
|
if (hasAlchemyWeb || hasAlchemyServer) {
|
|
8220
8603
|
const targetParts = [...hasAlchemyWeb ? [`web on ${webDeploy === "cloudflare" ? "Cloudflare" : "Prisma"}`] : [], ...hasAlchemyServer && !isBackendSelf ? [`server on ${serverDeploy === "cloudflare" ? "Cloudflare" : "Prisma"}`] : []];
|
|
8221
8604
|
const deployScript = webDeploy === "vercel" ? "deploy:server" : serverDeploy === "vercel" ? "deploy:web" : "deploy";
|
|
8222
|
-
|
|
8605
|
+
const originSteps = [];
|
|
8606
|
+
const hasWeb = frontend.some((value) => types_exports.webFrontends.includes(value));
|
|
8607
|
+
if (!isBackendSelf && hasAlchemyServer && hasWeb) originSteps.push(`${pc.cyan("•")} Required after the first deploy: set CORS_ORIGIN in apps/server/.env to the deployed web origin, then deploy again`);
|
|
8608
|
+
if (auth === "better-auth" && isBackendSelf && webDeploy === "prisma") originSteps.push(`${pc.cyan("•")} After the first deploy, set BETTER_AUTH_URL in apps/web/.env to the returned web URL, then deploy again`);
|
|
8609
|
+
else if (auth === "better-auth" && !isBackendSelf && serverDeploy === "prisma") originSteps.push(`${pc.cyan("•")} After the first deploy, set BETTER_AUTH_URL in apps/server/.env to the returned server URL, then deploy again`);
|
|
8610
|
+
instructions.push(`${pc.bold(`Deploy with Alchemy (${targetParts.join(" + ")}):`)}\n${pc.cyan("•")} Configure provider login: ${`cd packages/infra && ${alchemyExec} alchemy login --configure`}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} ${deployScript}`}\n${originSteps.join("\n")}${originSteps.length > 0 ? "\n" : ""}${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
|
|
8223
8611
|
}
|
|
8224
8612
|
if (webDeploy === "docker" || serverDeploy === "docker") {
|
|
8225
8613
|
const dockerTargets = webDeploy === "docker" && serverDeploy === "docker" ? "web + server" : webDeploy === "docker" ? "web" : "server";
|
|
@@ -8466,7 +8854,10 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
8466
8854
|
const isTemplateSetup = input.template && input.template !== "none";
|
|
8467
8855
|
if (!isSilent() && !isTemplateSetup && Object.keys(otherFlags).length > 0) log.info(pc.dim("Command-line options applied."));
|
|
8468
8856
|
config = yield* Result.await(Result.tryPromise({
|
|
8469
|
-
try: async () => gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput, {
|
|
8857
|
+
try: async () => gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput, {
|
|
8858
|
+
skipCompatibilityChecks: cliInput.yolo,
|
|
8859
|
+
manualDb: cliInput.manualDb ?? input.manualDb
|
|
8860
|
+
}),
|
|
8470
8861
|
catch: (cause) => {
|
|
8471
8862
|
if (cause instanceof UserCancelledError) return cause;
|
|
8472
8863
|
return new CLIError({
|
|
@@ -8476,7 +8867,7 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
8476
8867
|
}
|
|
8477
8868
|
}));
|
|
8478
8869
|
}
|
|
8479
|
-
const effectiveDbSetupOptions =
|
|
8870
|
+
const effectiveDbSetupOptions = resolveProjectDbSetupOptions(config, {
|
|
8480
8871
|
manualDb: cliInput.manualDb ?? input.manualDb,
|
|
8481
8872
|
dbSetupOptions: cliInput.dbSetupOptions ?? input.dbSetupOptions
|
|
8482
8873
|
});
|
|
@@ -8541,10 +8932,20 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
8541
8932
|
const historyCommand = getCliSubcommandCommand("history", config.packageManager);
|
|
8542
8933
|
log.message(`${pc.dim("Setup saved to history")}\n${pc.cyan(historyCommand)}`);
|
|
8543
8934
|
}
|
|
8935
|
+
let projectLauncher;
|
|
8936
|
+
if (!isSilent()) {
|
|
8937
|
+
const launcherResult = await getProjectLauncherChoice(input.open, config.projectDir, { prompt: !input.yes && process.env.CI === void 0 && process.stdin.isTTY === true && process.stdout.isTTY === true });
|
|
8938
|
+
if (launcherResult.isErr()) log.warn(pc.yellow(launcherResult.error.message));
|
|
8939
|
+
else projectLauncher = launcherResult.value;
|
|
8940
|
+
}
|
|
8544
8941
|
const elapsedTimeMs = Date.now() - startTime;
|
|
8545
8942
|
if (!isSilent()) {
|
|
8546
8943
|
const elapsedTimeInSeconds = (elapsedTimeMs / 1e3).toFixed(1);
|
|
8547
8944
|
outro(pc.magenta(`Project ready in ${pc.bold(`${elapsedTimeInSeconds}s`)}`));
|
|
8945
|
+
if (projectLauncher) {
|
|
8946
|
+
const launchResult = await launchProject(projectLauncher);
|
|
8947
|
+
if (launchResult.isErr()) log.warn(pc.yellow(launchResult.error.message));
|
|
8948
|
+
}
|
|
8548
8949
|
}
|
|
8549
8950
|
return Result.ok({
|
|
8550
8951
|
success: true,
|
|
@@ -8699,6 +9100,7 @@ const router = t.router({
|
|
|
8699
9100
|
git: z.boolean().optional(),
|
|
8700
9101
|
packageManager: types_exports.PackageManagerSchema.optional(),
|
|
8701
9102
|
install: z.boolean().optional(),
|
|
9103
|
+
open: ProjectLauncherSchema.optional(),
|
|
8702
9104
|
dbSetup: types_exports.DatabaseSetupSchema.optional(),
|
|
8703
9105
|
backend: types_exports.BackendSchema.optional(),
|
|
8704
9106
|
runtime: types_exports.RuntimeSchema.optional(),
|
|
@@ -8937,4 +9339,4 @@ async function add(options = {}) {
|
|
|
8937
9339
|
};
|
|
8938
9340
|
}
|
|
8939
9341
|
//#endregion
|
|
8940
|
-
export {
|
|
9342
|
+
export { DirectoryConflictError as C, ValidationError as E, DatabaseSetupError as S, UserCancelledError as T, ProjectLauncherSchema as _, TEMPLATE_COUNT as a, CLIError as b, builder as c, createVirtual as d, docs as f, sponsors as g, router as h, SchemaNameSchema as i, create as l, getSchemaResult as m, GeneratorError$1 as n, VirtualFileSystem$1 as o, generate$1 as p, Result$1 as r, add as s, EMBEDDED_TEMPLATES$1 as t, createBtsCli as u, types_exports as v, ProjectCreationError as w, CompatibilityError as x, getLatestCLIVersion as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.40.1",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"better-auth",
|
|
@@ -69,8 +69,8 @@
|
|
|
69
69
|
"prepublishOnly": "npm run build"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@better-t-stack/template-generator": "^3.
|
|
73
|
-
"@better-t-stack/types": "^3.
|
|
72
|
+
"@better-t-stack/template-generator": "^3.40.1",
|
|
73
|
+
"@better-t-stack/types": "^3.40.1",
|
|
74
74
|
"@clack/core": "^1.4.3",
|
|
75
75
|
"@clack/prompts": "^1.7.0",
|
|
76
76
|
"@modelcontextprotocol/sdk": "1.30.0",
|