create-better-t-stack 3.39.2 → 3.40.0
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 +3 -3
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-BYgmfow5.mjs → src-DjuxSWWY.mjs} +135 -47
- 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 |
|
|
@@ -259,8 +259,8 @@ npx create-better-t-stack --frontend none --backend hono --api trpc --database n
|
|
|
259
259
|
- **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
260
|
- **Addons 'none'**: Skips all addons.
|
|
261
261
|
- **Examples 'none'**: Skips all example implementations (todo, AI chat).
|
|
262
|
-
- **Nuxt, Svelte,
|
|
263
|
-
- **PWA support** requires TanStack Router, React Router, Next.js, or
|
|
262
|
+
- **Nuxt, Svelte, Solid, and Astro** frontends are only compatible with the oRPC API layer
|
|
263
|
+
- **PWA support** requires TanStack Router, React Router, Next.js, or Solid
|
|
264
264
|
- **Tauri desktop app** requires TanStack Router, React Router, TanStack Start, Next.js, Nuxt, SvelteKit, or Astro
|
|
265
265
|
- **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
266
|
- **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 { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-
|
|
2
|
+
import { _ as types_exports, i as SchemaNameSchema, l as create, m as getSchemaResult, s as add, u as createBtsCli, v as getLatestCLIVersion } from "./src-DjuxSWWY.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
|
@@ -186,7 +186,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
186
186
|
disableAnalytics?: boolean | undefined;
|
|
187
187
|
manualDb?: boolean | undefined;
|
|
188
188
|
dbSetupOptions?: {
|
|
189
|
-
mode?: "manual" | "auto" | undefined;
|
|
189
|
+
mode?: "manual" | "auto" | "alchemy" | undefined;
|
|
190
190
|
neon?: {
|
|
191
191
|
method?: "neon" | "neon-new" | "neondb" | "neonctl" | undefined;
|
|
192
192
|
projectName?: string | undefined;
|
|
@@ -249,7 +249,7 @@ declare const router: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
249
249
|
} | undefined;
|
|
250
250
|
} | undefined;
|
|
251
251
|
dbSetupOptions?: {
|
|
252
|
-
mode?: "manual" | "auto" | undefined;
|
|
252
|
+
mode?: "manual" | "auto" | "alchemy" | undefined;
|
|
253
253
|
neon?: {
|
|
254
254
|
method?: "neon" | "neon-new" | "neondb" | "neonctl" | undefined;
|
|
255
255
|
projectName?: string | undefined;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, 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 UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-
|
|
2
|
+
import { C as ProjectCreationError, S as DirectoryConflictError, T as ValidationError, a as TEMPLATE_COUNT, b as CompatibilityError, 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 UserCancelledError, x as DatabaseSetupError, y as CLIError } from "./src-DjuxSWWY.mjs";
|
|
3
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 };
|
|
@@ -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,
|
|
@@ -6570,37 +6680,6 @@ async function commandExists(command) {
|
|
|
6570
6680
|
return result.isOk() ? result.value : false;
|
|
6571
6681
|
}
|
|
6572
6682
|
//#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
6683
|
//#region src/helpers/database-providers/mongodb-atlas-setup.ts
|
|
6605
6684
|
async function checkAtlasCLI() {
|
|
6606
6685
|
const exists = await commandExists("atlas");
|
|
@@ -7893,7 +7972,7 @@ function getDesktopStaticBuildNote(frontend) {
|
|
|
7893
7972
|
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
7973
|
}
|
|
7895
7974
|
async function displayPostInstallInstructions(config) {
|
|
7896
|
-
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
7975
|
+
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy, dbSetupOptions } = config;
|
|
7897
7976
|
const isConvex = backend === "convex";
|
|
7898
7977
|
const isBackendSelf = backend === "self";
|
|
7899
7978
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
@@ -7903,7 +7982,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
7903
7982
|
const hasVitePlus = addons?.includes("vite-plus");
|
|
7904
7983
|
const hasVitePlusNativeHooks = hasVitePlus && !hasHusky && !hasLefthook;
|
|
7905
7984
|
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) : "";
|
|
7985
|
+
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, webDeploy, serverDeploy, backend, dbSetupOptions) : "";
|
|
7907
7986
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd, frontend) : "";
|
|
7908
7987
|
const electrobunInstructions = addons?.includes("electrobun") ? getElectrobunInstructions(runCmd, frontend) : "";
|
|
7909
7988
|
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
@@ -7914,7 +7993,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
7914
7993
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
7915
7994
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
7916
7995
|
const clerkInstructions = config.auth === "clerk" ? getClerkInstructions(frontend || [], backend, api) : "";
|
|
7917
|
-
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend);
|
|
7996
|
+
const alchemyDeployInstructions = getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend, config.auth, frontend || []);
|
|
7918
7997
|
const hasWeb = frontend?.some((f) => types_exports.webFrontends.includes(f));
|
|
7919
7998
|
const hasNative = frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles");
|
|
7920
7999
|
const hasReactRouter = frontend?.includes("react-router");
|
|
@@ -8038,7 +8117,7 @@ function getLefthookInstructions(packageManager) {
|
|
|
8038
8117
|
function getVitePlusNativeHooksInstructions(runCmd) {
|
|
8039
8118
|
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
8119
|
}
|
|
8041
|
-
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend) {
|
|
8120
|
+
async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup, webDeploy, serverDeploy, backend, dbSetupOptions) {
|
|
8042
8121
|
const notes = [];
|
|
8043
8122
|
const commands = [];
|
|
8044
8123
|
const isD1Alchemy = dbSetup === "d1" && (serverDeploy === "cloudflare" || backend === "self" && webDeploy === "cloudflare");
|
|
@@ -8046,7 +8125,8 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
8046
8125
|
backend,
|
|
8047
8126
|
dbSetup,
|
|
8048
8127
|
webDeploy,
|
|
8049
|
-
serverDeploy
|
|
8128
|
+
serverDeploy,
|
|
8129
|
+
dbSetupOptions
|
|
8050
8130
|
});
|
|
8051
8131
|
if (dbSetup === "docker") {
|
|
8052
8132
|
const dockerStatus = await getDockerStatus(database);
|
|
@@ -8210,7 +8290,7 @@ function getPolarInstructions(backend, packageManager) {
|
|
|
8210
8290
|
const envPath = backend === "self" ? "apps/web/.env" : "apps/server/.env";
|
|
8211
8291
|
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
8292
|
}
|
|
8213
|
-
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
|
|
8293
|
+
function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend, auth, frontend) {
|
|
8214
8294
|
const instructions = [];
|
|
8215
8295
|
const isBackendSelf = backend === "self";
|
|
8216
8296
|
const hasAlchemyWeb = (0, types_exports.isAlchemyDeployTarget)(webDeploy);
|
|
@@ -8219,7 +8299,12 @@ function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend)
|
|
|
8219
8299
|
if (hasAlchemyWeb || hasAlchemyServer) {
|
|
8220
8300
|
const targetParts = [...hasAlchemyWeb ? [`web on ${webDeploy === "cloudflare" ? "Cloudflare" : "Prisma"}`] : [], ...hasAlchemyServer && !isBackendSelf ? [`server on ${serverDeploy === "cloudflare" ? "Cloudflare" : "Prisma"}`] : []];
|
|
8221
8301
|
const deployScript = webDeploy === "vercel" ? "deploy:server" : serverDeploy === "vercel" ? "deploy:web" : "deploy";
|
|
8222
|
-
|
|
8302
|
+
const originSteps = [];
|
|
8303
|
+
const hasWeb = frontend.some((value) => types_exports.webFrontends.includes(value));
|
|
8304
|
+
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`);
|
|
8305
|
+
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`);
|
|
8306
|
+
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`);
|
|
8307
|
+
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
8308
|
}
|
|
8224
8309
|
if (webDeploy === "docker" || serverDeploy === "docker") {
|
|
8225
8310
|
const dockerTargets = webDeploy === "docker" && serverDeploy === "docker" ? "web + server" : webDeploy === "docker" ? "web" : "server";
|
|
@@ -8466,7 +8551,10 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
8466
8551
|
const isTemplateSetup = input.template && input.template !== "none";
|
|
8467
8552
|
if (!isSilent() && !isTemplateSetup && Object.keys(otherFlags).length > 0) log.info(pc.dim("Command-line options applied."));
|
|
8468
8553
|
config = yield* Result.await(Result.tryPromise({
|
|
8469
|
-
try: async () => gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput, {
|
|
8554
|
+
try: async () => gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput, {
|
|
8555
|
+
skipCompatibilityChecks: cliInput.yolo,
|
|
8556
|
+
manualDb: cliInput.manualDb ?? input.manualDb
|
|
8557
|
+
}),
|
|
8470
8558
|
catch: (cause) => {
|
|
8471
8559
|
if (cause instanceof UserCancelledError) return cause;
|
|
8472
8560
|
return new CLIError({
|
|
@@ -8476,7 +8564,7 @@ async function createProjectHandlerInternal(input, startTime, timeScaffolded) {
|
|
|
8476
8564
|
}
|
|
8477
8565
|
}));
|
|
8478
8566
|
}
|
|
8479
|
-
const effectiveDbSetupOptions =
|
|
8567
|
+
const effectiveDbSetupOptions = resolveProjectDbSetupOptions(config, {
|
|
8480
8568
|
manualDb: cliInput.manualDb ?? input.manualDb,
|
|
8481
8569
|
dbSetupOptions: cliInput.dbSetupOptions ?? input.dbSetupOptions
|
|
8482
8570
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.40.0",
|
|
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.0",
|
|
73
|
+
"@better-t-stack/types": "^3.40.0",
|
|
74
74
|
"@clack/core": "^1.4.3",
|
|
75
75
|
"@clack/prompts": "^1.7.0",
|
|
76
76
|
"@modelcontextprotocol/sdk": "1.30.0",
|