create-better-t-stack 2.40.3 → 2.40.4-canary.f07be855
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/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/{src-BOnnM-3r.js → src-BMkTBm89.js} +328 -85
- package/package.json +1 -1
- package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +32 -3
- package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +1 -1
- package/templates/backend/server/elysia/src/index.ts.hbs +31 -2
- package/templates/backend/server/express/src/index.ts.hbs +38 -4
- package/templates/backend/server/fastify/src/index.ts.hbs +33 -2
- package/templates/backend/server/hono/src/index.ts.hbs +40 -5
- package/templates/db/drizzle/mysql/src/db/index.ts.hbs +25 -0
- package/templates/db/prisma/mongodb/src/db/index.ts.hbs +5 -0
- package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +6 -0
- package/templates/db/prisma/mysql/src/db/index.ts.hbs +12 -0
- package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +3 -0
- package/templates/db/prisma/postgres/src/db/index.ts.hbs +5 -0
- package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +10 -0
- package/templates/db/prisma/sqlite/prisma.config.ts.hbs +29 -1
- package/templates/db/prisma/sqlite/src/db/index.ts.hbs +28 -0
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +6 -2
- package/templates/deploy/wrangler/server/wrangler.jsonc.hbs +5 -0
- package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +2 -2
- package/templates/extras/bunfig.toml.hbs +0 -5
- package/templates/frontend/react/next/next.config.ts.hbs +7 -0
- package/templates/db/prisma/mongodb/prisma/index.ts.hbs +0 -5
- package/templates/db/prisma/mysql/prisma/index.ts +0 -5
- package/templates/db/prisma/postgres/prisma/index.ts +0 -5
- package/templates/db/prisma/sqlite/prisma/index.ts +0 -5
|
@@ -67,6 +67,7 @@ const dependencyVersionMap = {
|
|
|
67
67
|
"@clerk/clerk-expo": "^2.14.25",
|
|
68
68
|
"drizzle-orm": "^0.44.2",
|
|
69
69
|
"drizzle-kit": "^0.31.2",
|
|
70
|
+
"@planetscale/database": "^1.19.0",
|
|
70
71
|
"@libsql/client": "^0.15.9",
|
|
71
72
|
"@neondatabase/serverless": "^1.0.1",
|
|
72
73
|
pg: "^8.14.1",
|
|
@@ -76,7 +77,11 @@ const dependencyVersionMap = {
|
|
|
76
77
|
mysql2: "^3.14.0",
|
|
77
78
|
"@prisma/client": "^6.15.0",
|
|
78
79
|
prisma: "^6.15.0",
|
|
80
|
+
"@prisma/adapter-d1": "^6.15.0",
|
|
79
81
|
"@prisma/extension-accelerate": "^2.0.2",
|
|
82
|
+
"@prisma/adapter-libsql": "^6.15.0",
|
|
83
|
+
"@prisma/adapter-planetscale": "^6.15.0",
|
|
84
|
+
undici: "^7.15.0",
|
|
80
85
|
mongoose: "^8.14.0",
|
|
81
86
|
"vite-plugin-pwa": "^1.0.1",
|
|
82
87
|
"@vite-pwa/assets-generator": "^1.0.0",
|
|
@@ -110,6 +115,8 @@ const dependencyVersionMap = {
|
|
|
110
115
|
streamdown: "^1.1.6",
|
|
111
116
|
"@orpc/server": "^1.8.6",
|
|
112
117
|
"@orpc/client": "^1.8.6",
|
|
118
|
+
"@orpc/openapi": "^1.8.6",
|
|
119
|
+
"@orpc/zod": "^1.8.6",
|
|
113
120
|
"@orpc/tanstack-query": "^1.8.6",
|
|
114
121
|
"@trpc/tanstack-react-query": "^11.5.0",
|
|
115
122
|
"@trpc/server": "^11.5.0",
|
|
@@ -130,11 +137,11 @@ const dependencyVersionMap = {
|
|
|
130
137
|
"@tanstack/solid-router-devtools": "^1.131.25",
|
|
131
138
|
wrangler: "^4.23.0",
|
|
132
139
|
"@cloudflare/vite-plugin": "^1.9.0",
|
|
133
|
-
"@opennextjs/cloudflare": "^1.
|
|
140
|
+
"@opennextjs/cloudflare": "^1.6.5",
|
|
134
141
|
"nitro-cloudflare-dev": "^0.2.2",
|
|
135
142
|
"@sveltejs/adapter-cloudflare": "^7.2.1",
|
|
136
143
|
"@cloudflare/workers-types": "^4.20250822.0",
|
|
137
|
-
alchemy: "^0.
|
|
144
|
+
alchemy: "^0.65.0",
|
|
138
145
|
nitropack: "^2.12.4",
|
|
139
146
|
dotenv: "^17.2.1"
|
|
140
147
|
};
|
|
@@ -233,6 +240,7 @@ const DatabaseSetupSchema = z.enum([
|
|
|
233
240
|
"turso",
|
|
234
241
|
"neon",
|
|
235
242
|
"prisma-postgres",
|
|
243
|
+
"planetscale",
|
|
236
244
|
"mongodb-atlas",
|
|
237
245
|
"supabase",
|
|
238
246
|
"d1",
|
|
@@ -496,8 +504,6 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
496
504
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
497
505
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
|
|
498
506
|
if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
|
|
499
|
-
if (providedFlags.has("runtime") && options.runtime === "workers" && config.orm && config.orm !== "drizzle" && config.orm !== "none") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
|
|
500
|
-
if (providedFlags.has("orm") && config.orm && config.orm !== "drizzle" && config.orm !== "none" && config.runtime === "workers") exitWithError(`ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
|
|
501
507
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
502
508
|
if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
|
|
503
509
|
if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") exitWithError("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
|
|
@@ -562,17 +568,6 @@ function validateExamplesCompatibility(examples, backend, database, frontend) {
|
|
|
562
568
|
if (examplesArr.includes("ai") && backend === "elysia") exitWithError("The 'ai' example is not compatible with the Elysia backend.");
|
|
563
569
|
if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) exitWithError("The 'ai' example is not compatible with the Solid frontend.");
|
|
564
570
|
}
|
|
565
|
-
function validateAlchemyCompatibility(webDeploy, serverDeploy, frontends = []) {
|
|
566
|
-
const isAlchemyWebDeploy = webDeploy === "alchemy";
|
|
567
|
-
const isAlchemyServerDeploy = serverDeploy === "alchemy";
|
|
568
|
-
if (isAlchemyWebDeploy || isAlchemyServerDeploy) {
|
|
569
|
-
const incompatibleFrontends = frontends.filter((f) => f === "next");
|
|
570
|
-
if (incompatibleFrontends.length > 0) {
|
|
571
|
-
const deployType = isAlchemyWebDeploy && isAlchemyServerDeploy ? "web and server deployment" : isAlchemyWebDeploy ? "web deployment" : "server deployment";
|
|
572
|
-
exitWithError(`Alchemy ${deployType} is temporarily not compatible with ${incompatibleFrontends.join(" and ")} frontend(s). Please choose a different frontend or deployment option.`);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
571
|
|
|
577
572
|
//#endregion
|
|
578
573
|
//#region src/prompts/api.ts
|
|
@@ -739,11 +734,10 @@ async function getDatabaseChoice(database, backend, runtime) {
|
|
|
739
734
|
|
|
740
735
|
//#endregion
|
|
741
736
|
//#region src/prompts/database-setup.ts
|
|
742
|
-
async function getDBSetupChoice(databaseType, dbSetup,
|
|
737
|
+
async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
|
|
743
738
|
if (backend === "convex") return "none";
|
|
744
739
|
if (dbSetup !== void 0) return dbSetup;
|
|
745
740
|
if (databaseType === "none") return "none";
|
|
746
|
-
if (databaseType === "sqlite" && orm === "prisma") return "none";
|
|
747
741
|
let options = [];
|
|
748
742
|
if (databaseType === "sqlite") options = [
|
|
749
743
|
{
|
|
@@ -768,6 +762,11 @@ async function getDBSetupChoice(databaseType, dbSetup, orm, backend, runtime) {
|
|
|
768
762
|
label: "Neon Postgres",
|
|
769
763
|
hint: "Serverless Postgres with branching capability"
|
|
770
764
|
},
|
|
765
|
+
{
|
|
766
|
+
value: "planetscale",
|
|
767
|
+
label: "PlanetScale",
|
|
768
|
+
hint: "Serverless MySQL platform with branching (Postgres compatible)"
|
|
769
|
+
},
|
|
771
770
|
{
|
|
772
771
|
value: "supabase",
|
|
773
772
|
label: "Supabase",
|
|
@@ -789,15 +788,23 @@ async function getDBSetupChoice(databaseType, dbSetup, orm, backend, runtime) {
|
|
|
789
788
|
hint: "Manual setup"
|
|
790
789
|
}
|
|
791
790
|
];
|
|
792
|
-
else if (databaseType === "mysql") options = [
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
791
|
+
else if (databaseType === "mysql") options = [
|
|
792
|
+
{
|
|
793
|
+
value: "planetscale",
|
|
794
|
+
label: "PlanetScale",
|
|
795
|
+
hint: "Serverless MySQL platform with branching"
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
value: "docker",
|
|
799
|
+
label: "Docker",
|
|
800
|
+
hint: "Run locally with docker compose"
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
value: "none",
|
|
804
|
+
label: "None",
|
|
805
|
+
hint: "Manual setup"
|
|
806
|
+
}
|
|
807
|
+
];
|
|
801
808
|
else if (databaseType === "mongodb") options = [
|
|
802
809
|
{
|
|
803
810
|
value: "mongodb-atlas",
|
|
@@ -995,12 +1002,11 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
|
|
|
995
1002
|
if (backend === "convex") return "none";
|
|
996
1003
|
if (!hasDatabase) return "none";
|
|
997
1004
|
if (orm !== void 0) return orm;
|
|
998
|
-
if (runtime === "workers") return "drizzle";
|
|
999
1005
|
const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
|
|
1000
1006
|
const response = await select({
|
|
1001
1007
|
message: "Select ORM",
|
|
1002
1008
|
options,
|
|
1003
|
-
initialValue: database === "mongodb" ? "prisma" : DEFAULT_CONFIG.orm
|
|
1009
|
+
initialValue: database === "mongodb" ? "prisma" : runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
|
|
1004
1010
|
});
|
|
1005
1011
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1006
1012
|
return response;
|
|
@@ -1158,8 +1164,7 @@ function getDeploymentDisplay(deployment) {
|
|
|
1158
1164
|
async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []) {
|
|
1159
1165
|
if (deployment !== void 0) return deployment;
|
|
1160
1166
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1161
|
-
const
|
|
1162
|
-
const availableDeployments = hasIncompatibleFrontend ? ["wrangler", "none"] : [
|
|
1167
|
+
const availableDeployments = [
|
|
1163
1168
|
"wrangler",
|
|
1164
1169
|
"alchemy",
|
|
1165
1170
|
"none"
|
|
@@ -1175,14 +1180,13 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
|
|
|
1175
1180
|
const response = await select({
|
|
1176
1181
|
message: "Select web deployment",
|
|
1177
1182
|
options,
|
|
1178
|
-
initialValue:
|
|
1183
|
+
initialValue: DEFAULT_CONFIG.webDeploy
|
|
1179
1184
|
});
|
|
1180
1185
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1181
1186
|
return response;
|
|
1182
1187
|
}
|
|
1183
1188
|
async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
1184
1189
|
if (!hasWebFrontend(frontend)) return "none";
|
|
1185
|
-
const hasIncompatibleFrontend = frontend.some((f) => f === "next");
|
|
1186
1190
|
const options = [];
|
|
1187
1191
|
if (existingDeployment !== "wrangler") {
|
|
1188
1192
|
const { label, hint } = getDeploymentDisplay("wrangler");
|
|
@@ -1192,7 +1196,7 @@ async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
|
1192
1196
|
hint
|
|
1193
1197
|
});
|
|
1194
1198
|
}
|
|
1195
|
-
if (existingDeployment !== "alchemy"
|
|
1199
|
+
if (existingDeployment !== "alchemy") {
|
|
1196
1200
|
const { label, hint } = getDeploymentDisplay("alchemy");
|
|
1197
1201
|
options.push({
|
|
1198
1202
|
value: "alchemy",
|
|
@@ -1210,7 +1214,7 @@ async function getDeploymentToAdd(frontend, existingDeployment) {
|
|
|
1210
1214
|
const response = await select({
|
|
1211
1215
|
message: "Select web deployment",
|
|
1212
1216
|
options,
|
|
1213
|
-
initialValue:
|
|
1217
|
+
initialValue: DEFAULT_CONFIG.webDeploy
|
|
1214
1218
|
});
|
|
1215
1219
|
if (isCancel(response)) return exitCancelled("Operation cancelled");
|
|
1216
1220
|
return response;
|
|
@@ -1333,7 +1337,7 @@ const getLatestCLIVersion = () => {
|
|
|
1333
1337
|
*/
|
|
1334
1338
|
function isTelemetryEnabled() {
|
|
1335
1339
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1336
|
-
const BTS_TELEMETRY = "
|
|
1340
|
+
const BTS_TELEMETRY = "0";
|
|
1337
1341
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1338
1342
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1339
1343
|
return true;
|
|
@@ -1341,8 +1345,8 @@ function isTelemetryEnabled() {
|
|
|
1341
1345
|
|
|
1342
1346
|
//#endregion
|
|
1343
1347
|
//#region src/utils/analytics.ts
|
|
1344
|
-
const POSTHOG_API_KEY = "
|
|
1345
|
-
const POSTHOG_HOST = "
|
|
1348
|
+
const POSTHOG_API_KEY = "random";
|
|
1349
|
+
const POSTHOG_HOST = "random";
|
|
1346
1350
|
function generateSessionId() {
|
|
1347
1351
|
const rand = Math.random().toString(36).slice(2);
|
|
1348
1352
|
const now = Date.now().toString(36);
|
|
@@ -1654,6 +1658,7 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
1654
1658
|
database: "postgres",
|
|
1655
1659
|
errorMessage: "Prisma PostgreSQL setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
|
|
1656
1660
|
},
|
|
1661
|
+
planetscale: { errorMessage: "PlanetScale setup requires PostgreSQL or MySQL database. Please use '--database postgres' or '--database mysql' or choose a different setup." },
|
|
1657
1662
|
"mongodb-atlas": {
|
|
1658
1663
|
database: "mongodb",
|
|
1659
1664
|
errorMessage: "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup."
|
|
@@ -1672,7 +1677,9 @@ function validateDatabaseSetup(config, providedFlags) {
|
|
|
1672
1677
|
};
|
|
1673
1678
|
if (dbSetup && dbSetup !== "none") {
|
|
1674
1679
|
const validation = setupValidations[dbSetup];
|
|
1675
|
-
if (
|
|
1680
|
+
if (dbSetup === "planetscale") {
|
|
1681
|
+
if (database !== "postgres" && database !== "mysql") exitWithError(validation.errorMessage);
|
|
1682
|
+
} else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
|
|
1676
1683
|
if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
|
|
1677
1684
|
if (dbSetup === "docker") {
|
|
1678
1685
|
if (database === "sqlite") exitWithError("Docker setup is not compatible with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
|
|
@@ -1753,7 +1760,6 @@ function validateFullConfig(config, providedFlags, options) {
|
|
|
1753
1760
|
config.addons = [...new Set(config.addons)];
|
|
1754
1761
|
}
|
|
1755
1762
|
validateExamplesCompatibility(config.examples ?? [], config.backend, config.database, config.frontend ?? []);
|
|
1756
|
-
validateAlchemyCompatibility(config.webDeploy, config.serverDeploy, config.frontend ?? []);
|
|
1757
1763
|
}
|
|
1758
1764
|
function validateConfigForProgrammaticUse(config) {
|
|
1759
1765
|
try {
|
|
@@ -3047,8 +3053,11 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
|
|
|
3047
3053
|
const s = spinner();
|
|
3048
3054
|
try {
|
|
3049
3055
|
s.start("Generating Cloudflare Workers types...");
|
|
3050
|
-
const runCmd = packageManager
|
|
3051
|
-
await execa(runCmd,
|
|
3056
|
+
const runCmd = getPackageExecutionCommand(packageManager, "wrangler types --env-interface CloudflareBindings");
|
|
3057
|
+
await execa(runCmd, {
|
|
3058
|
+
cwd: serverDir,
|
|
3059
|
+
shell: true
|
|
3060
|
+
});
|
|
3052
3061
|
s.stop("Cloudflare Workers types generated successfully!");
|
|
3053
3062
|
} catch {
|
|
3054
3063
|
s.stop(pc.yellow("Failed to generate Cloudflare Workers types"));
|
|
@@ -3087,7 +3096,12 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3087
3096
|
const webAppDir = path.join(projectDir, "apps/web");
|
|
3088
3097
|
if (!await fs.pathExists(webAppDir)) return;
|
|
3089
3098
|
await addPackageDependency({
|
|
3090
|
-
|
|
3099
|
+
dependencies: ["@opennextjs/cloudflare"],
|
|
3100
|
+
devDependencies: [
|
|
3101
|
+
"alchemy",
|
|
3102
|
+
"dotenv",
|
|
3103
|
+
"wrangler"
|
|
3104
|
+
],
|
|
3091
3105
|
projectDir: webAppDir
|
|
3092
3106
|
});
|
|
3093
3107
|
const pkgPath = path.join(webAppDir, "package.json");
|
|
@@ -3096,11 +3110,21 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3096
3110
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3097
3111
|
...pkg.scripts,
|
|
3098
3112
|
deploy: "alchemy deploy",
|
|
3099
|
-
destroy: "alchemy destroy"
|
|
3100
|
-
dev: "alchemy dev"
|
|
3113
|
+
destroy: "alchemy destroy"
|
|
3101
3114
|
};
|
|
3102
3115
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3103
3116
|
}
|
|
3117
|
+
const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
|
|
3118
|
+
const openNextConfigContent = `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
|
|
3119
|
+
|
|
3120
|
+
export default defineCloudflareConfig({});
|
|
3121
|
+
`;
|
|
3122
|
+
await fs.writeFile(openNextConfigPath, openNextConfigContent);
|
|
3123
|
+
const gitignorePath = path.join(webAppDir, ".gitignore");
|
|
3124
|
+
if (await fs.pathExists(gitignorePath)) {
|
|
3125
|
+
const gitignoreContent = await fs.readFile(gitignorePath, "utf-8");
|
|
3126
|
+
if (!gitignoreContent.includes("wrangler.jsonc")) await fs.appendFile(gitignorePath, "\nwrangler.jsonc\n");
|
|
3127
|
+
} else await fs.writeFile(gitignorePath, "wrangler.jsonc\n");
|
|
3104
3128
|
}
|
|
3105
3129
|
|
|
3106
3130
|
//#endregion
|
|
@@ -3122,8 +3146,7 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3122
3146
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3123
3147
|
...pkg.scripts,
|
|
3124
3148
|
deploy: "alchemy deploy",
|
|
3125
|
-
destroy: "alchemy destroy"
|
|
3126
|
-
dev: "alchemy dev"
|
|
3149
|
+
destroy: "alchemy destroy"
|
|
3127
3150
|
};
|
|
3128
3151
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3129
3152
|
}
|
|
@@ -3186,8 +3209,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
|
|
|
3186
3209
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3187
3210
|
...pkg.scripts,
|
|
3188
3211
|
deploy: "alchemy deploy",
|
|
3189
|
-
destroy: "alchemy destroy"
|
|
3190
|
-
dev: "alchemy dev"
|
|
3212
|
+
destroy: "alchemy destroy"
|
|
3191
3213
|
};
|
|
3192
3214
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3193
3215
|
}
|
|
@@ -3208,8 +3230,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3208
3230
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3209
3231
|
...pkg.scripts,
|
|
3210
3232
|
deploy: "alchemy deploy",
|
|
3211
|
-
destroy: "alchemy destroy"
|
|
3212
|
-
dev: "alchemy dev"
|
|
3233
|
+
destroy: "alchemy destroy"
|
|
3213
3234
|
};
|
|
3214
3235
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3215
3236
|
}
|
|
@@ -3234,8 +3255,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
|
|
|
3234
3255
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3235
3256
|
...pkg.scripts,
|
|
3236
3257
|
deploy: "alchemy deploy",
|
|
3237
|
-
destroy: "alchemy destroy"
|
|
3238
|
-
dev: "alchemy dev"
|
|
3258
|
+
destroy: "alchemy destroy"
|
|
3239
3259
|
};
|
|
3240
3260
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3241
3261
|
}
|
|
@@ -3301,8 +3321,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
|
|
|
3301
3321
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3302
3322
|
...pkg.scripts,
|
|
3303
3323
|
deploy: "alchemy deploy",
|
|
3304
|
-
destroy: "alchemy destroy"
|
|
3305
|
-
dev: "alchemy dev"
|
|
3324
|
+
destroy: "alchemy destroy"
|
|
3306
3325
|
};
|
|
3307
3326
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3308
3327
|
}
|
|
@@ -3327,8 +3346,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
|
|
|
3327
3346
|
if (!options?.skipAppScripts) pkg.scripts = {
|
|
3328
3347
|
...pkg.scripts,
|
|
3329
3348
|
deploy: "alchemy deploy",
|
|
3330
|
-
destroy: "alchemy destroy"
|
|
3331
|
-
dev: "alchemy dev"
|
|
3349
|
+
destroy: "alchemy destroy"
|
|
3332
3350
|
};
|
|
3333
3351
|
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
3334
3352
|
}
|
|
@@ -3892,7 +3910,12 @@ function getFrontendType(frontend) {
|
|
|
3892
3910
|
}
|
|
3893
3911
|
function getApiDependencies(api, frontendType) {
|
|
3894
3912
|
const deps = {};
|
|
3895
|
-
if (api === "orpc") deps.server = { dependencies: [
|
|
3913
|
+
if (api === "orpc") deps.server = { dependencies: [
|
|
3914
|
+
"@orpc/server",
|
|
3915
|
+
"@orpc/client",
|
|
3916
|
+
"@orpc/openapi",
|
|
3917
|
+
"@orpc/zod"
|
|
3918
|
+
] };
|
|
3896
3919
|
else if (api === "trpc") deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
|
|
3897
3920
|
if (frontendType.hasReactWeb) {
|
|
3898
3921
|
if (api === "orpc") deps.web = { dependencies: ["@orpc/tanstack-query", "@orpc/client"] };
|
|
@@ -4377,7 +4400,7 @@ async function setupEnvironmentVariables(config) {
|
|
|
4377
4400
|
//#endregion
|
|
4378
4401
|
//#region src/helpers/database-providers/d1-setup.ts
|
|
4379
4402
|
async function setupCloudflareD1(config) {
|
|
4380
|
-
const { projectDir, serverDeploy } = config;
|
|
4403
|
+
const { projectDir, serverDeploy, orm } = config;
|
|
4381
4404
|
if (serverDeploy === "wrangler") {
|
|
4382
4405
|
const envPath = path.join(projectDir, "apps/server", ".env");
|
|
4383
4406
|
const variables = [
|
|
@@ -4401,6 +4424,22 @@ async function setupCloudflareD1(config) {
|
|
|
4401
4424
|
await addEnvVariablesToFile(envPath, variables);
|
|
4402
4425
|
} catch (_err) {}
|
|
4403
4426
|
}
|
|
4427
|
+
if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
|
|
4428
|
+
const envPath = path.join(projectDir, "apps/server", ".env");
|
|
4429
|
+
const variables = [{
|
|
4430
|
+
key: "DATABASE_URL",
|
|
4431
|
+
value: "file:./local.db",
|
|
4432
|
+
condition: true
|
|
4433
|
+
}];
|
|
4434
|
+
try {
|
|
4435
|
+
await addEnvVariablesToFile(envPath, variables);
|
|
4436
|
+
} catch (_err) {}
|
|
4437
|
+
const serverDir = path.join(projectDir, "apps/server");
|
|
4438
|
+
await addPackageDependency({
|
|
4439
|
+
dependencies: ["@prisma/adapter-d1"],
|
|
4440
|
+
projectDir: serverDir
|
|
4441
|
+
});
|
|
4442
|
+
}
|
|
4404
4443
|
}
|
|
4405
4444
|
|
|
4406
4445
|
//#endregion
|
|
@@ -4531,6 +4570,26 @@ async function setupMongoDBAtlas(config) {
|
|
|
4531
4570
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4532
4571
|
try {
|
|
4533
4572
|
await fs.ensureDir(serverDir);
|
|
4573
|
+
const mode = await select({
|
|
4574
|
+
message: "MongoDB Atlas setup: choose mode",
|
|
4575
|
+
options: [{
|
|
4576
|
+
label: "Automatic",
|
|
4577
|
+
value: "auto",
|
|
4578
|
+
hint: "Automated setup with provider CLI, sets .env"
|
|
4579
|
+
}, {
|
|
4580
|
+
label: "Manual",
|
|
4581
|
+
value: "manual",
|
|
4582
|
+
hint: "Manual setup, add env vars yourself"
|
|
4583
|
+
}],
|
|
4584
|
+
initialValue: "auto"
|
|
4585
|
+
});
|
|
4586
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
4587
|
+
if (mode === "manual") {
|
|
4588
|
+
mainSpinner.stop("MongoDB Atlas manual setup selected");
|
|
4589
|
+
await writeEnvFile$3(projectDir);
|
|
4590
|
+
displayManualSetupInstructions$3();
|
|
4591
|
+
return;
|
|
4592
|
+
}
|
|
4534
4593
|
mainSpinner.stop("MongoDB Atlas setup ready");
|
|
4535
4594
|
const config$1 = await initMongoDBAtlas(serverDir);
|
|
4536
4595
|
if (config$1) {
|
|
@@ -4663,6 +4722,25 @@ DATABASE_URL="your_connection_string"`);
|
|
|
4663
4722
|
async function setupNeonPostgres(config) {
|
|
4664
4723
|
const { packageManager, projectDir } = config;
|
|
4665
4724
|
try {
|
|
4725
|
+
const mode = await select({
|
|
4726
|
+
message: "Neon setup: choose mode",
|
|
4727
|
+
options: [{
|
|
4728
|
+
label: "Automatic",
|
|
4729
|
+
value: "auto",
|
|
4730
|
+
hint: "Automated setup with provider CLI, sets .env"
|
|
4731
|
+
}, {
|
|
4732
|
+
label: "Manual",
|
|
4733
|
+
value: "manual",
|
|
4734
|
+
hint: "Manual setup, add env vars yourself"
|
|
4735
|
+
}],
|
|
4736
|
+
initialValue: "auto"
|
|
4737
|
+
});
|
|
4738
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
4739
|
+
if (mode === "manual") {
|
|
4740
|
+
await writeEnvFile$2(projectDir);
|
|
4741
|
+
displayManualSetupInstructions$2();
|
|
4742
|
+
return;
|
|
4743
|
+
}
|
|
4666
4744
|
const setupMethod = await select({
|
|
4667
4745
|
message: "Choose your Neon setup method:",
|
|
4668
4746
|
options: [{
|
|
@@ -4706,6 +4784,71 @@ async function setupNeonPostgres(config) {
|
|
|
4706
4784
|
}
|
|
4707
4785
|
}
|
|
4708
4786
|
|
|
4787
|
+
//#endregion
|
|
4788
|
+
//#region src/helpers/database-providers/planetscale-setup.ts
|
|
4789
|
+
async function setupPlanetScale(config) {
|
|
4790
|
+
const { projectDir, database, orm } = config;
|
|
4791
|
+
const envPath = path.join(projectDir, "apps/server", ".env");
|
|
4792
|
+
if (database === "mysql" && orm === "drizzle") {
|
|
4793
|
+
const variables = [
|
|
4794
|
+
{
|
|
4795
|
+
key: "# enable foreign key constraints in database settings",
|
|
4796
|
+
value: "",
|
|
4797
|
+
condition: true
|
|
4798
|
+
},
|
|
4799
|
+
{
|
|
4800
|
+
key: "DATABASE_URL",
|
|
4801
|
+
value: "mysql://username:password@host/database?ssl={\"rejectUnauthorized\":true}",
|
|
4802
|
+
condition: true
|
|
4803
|
+
},
|
|
4804
|
+
{
|
|
4805
|
+
key: "DATABASE_HOST",
|
|
4806
|
+
value: "",
|
|
4807
|
+
condition: true
|
|
4808
|
+
},
|
|
4809
|
+
{
|
|
4810
|
+
key: "DATABASE_USERNAME",
|
|
4811
|
+
value: "",
|
|
4812
|
+
condition: true
|
|
4813
|
+
},
|
|
4814
|
+
{
|
|
4815
|
+
key: "DATABASE_PASSWORD",
|
|
4816
|
+
value: "",
|
|
4817
|
+
condition: true
|
|
4818
|
+
}
|
|
4819
|
+
];
|
|
4820
|
+
await fs.ensureDir(path.join(projectDir, "apps/server"));
|
|
4821
|
+
await addEnvVariablesToFile(envPath, variables);
|
|
4822
|
+
}
|
|
4823
|
+
if (database === "postgres" && orm === "prisma") {
|
|
4824
|
+
const variables = [{
|
|
4825
|
+
key: "DATABASE_URL",
|
|
4826
|
+
value: "postgresql://username:password@host/database?sslaccept=strict",
|
|
4827
|
+
condition: true
|
|
4828
|
+
}];
|
|
4829
|
+
await fs.ensureDir(path.join(projectDir, "apps/server"));
|
|
4830
|
+
await addEnvVariablesToFile(envPath, variables);
|
|
4831
|
+
}
|
|
4832
|
+
if (database === "postgres" && orm === "drizzle") {
|
|
4833
|
+
const variables = [{
|
|
4834
|
+
key: "DATABASE_URL",
|
|
4835
|
+
value: "postgresql://username:password@host/database?sslmode=verify-full",
|
|
4836
|
+
condition: true
|
|
4837
|
+
}];
|
|
4838
|
+
await fs.ensureDir(path.join(projectDir, "apps/server"));
|
|
4839
|
+
await addEnvVariablesToFile(envPath, variables);
|
|
4840
|
+
}
|
|
4841
|
+
if (database === "mysql" && orm === "prisma") {
|
|
4842
|
+
const variables = [{
|
|
4843
|
+
key: "DATABASE_URL",
|
|
4844
|
+
value: "mysql://username:password@host/database?sslaccept=strict",
|
|
4845
|
+
condition: true
|
|
4846
|
+
}];
|
|
4847
|
+
await fs.ensureDir(path.join(projectDir, "apps/server"));
|
|
4848
|
+
await addEnvVariablesToFile(envPath, variables);
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
|
|
4709
4852
|
//#endregion
|
|
4710
4853
|
//#region src/helpers/database-providers/prisma-postgres-setup.ts
|
|
4711
4854
|
const AVAILABLE_REGIONS = [
|
|
@@ -4868,6 +5011,25 @@ async function setupPrismaPostgres(config) {
|
|
|
4868
5011
|
const serverDir = path.join(projectDir, "apps/server");
|
|
4869
5012
|
try {
|
|
4870
5013
|
await fs.ensureDir(serverDir);
|
|
5014
|
+
const mode = await select({
|
|
5015
|
+
message: "Prisma Postgres setup: choose mode",
|
|
5016
|
+
options: [{
|
|
5017
|
+
label: "Automatic",
|
|
5018
|
+
value: "auto",
|
|
5019
|
+
hint: "Automated setup with provider CLI, sets .env"
|
|
5020
|
+
}, {
|
|
5021
|
+
label: "Manual",
|
|
5022
|
+
value: "manual",
|
|
5023
|
+
hint: "Manual setup, add env vars yourself"
|
|
5024
|
+
}],
|
|
5025
|
+
initialValue: "auto"
|
|
5026
|
+
});
|
|
5027
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5028
|
+
if (mode === "manual") {
|
|
5029
|
+
await writeEnvFile$1(projectDir);
|
|
5030
|
+
displayManualSetupInstructions$1();
|
|
5031
|
+
return;
|
|
5032
|
+
}
|
|
4871
5033
|
const setupOptions = [{
|
|
4872
5034
|
label: "Quick setup with create-db",
|
|
4873
5035
|
value: "create-db",
|
|
@@ -5006,6 +5168,24 @@ async function setupSupabase(config) {
|
|
|
5006
5168
|
const serverDir = path.join(projectDir, "apps", "server");
|
|
5007
5169
|
try {
|
|
5008
5170
|
await fs.ensureDir(serverDir);
|
|
5171
|
+
const mode = await select({
|
|
5172
|
+
message: "Supabase setup: choose mode",
|
|
5173
|
+
options: [{
|
|
5174
|
+
label: "Automatic",
|
|
5175
|
+
value: "auto",
|
|
5176
|
+
hint: "Automated setup with provider CLI, sets .env"
|
|
5177
|
+
}, {
|
|
5178
|
+
label: "Manual",
|
|
5179
|
+
value: "manual",
|
|
5180
|
+
hint: "Manual setup, add env vars yourself"
|
|
5181
|
+
}],
|
|
5182
|
+
initialValue: "auto"
|
|
5183
|
+
});
|
|
5184
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5185
|
+
if (mode === "manual") {
|
|
5186
|
+
displayManualSupabaseInstructions();
|
|
5187
|
+
return;
|
|
5188
|
+
}
|
|
5009
5189
|
const initialized = await initializeSupabase(serverDir, packageManager);
|
|
5010
5190
|
if (!initialized) {
|
|
5011
5191
|
displayManualSupabaseInstructions();
|
|
@@ -5175,19 +5355,38 @@ DATABASE_AUTH_TOKEN=your_auth_token`);
|
|
|
5175
5355
|
async function setupTurso(config) {
|
|
5176
5356
|
const { orm, projectDir } = config;
|
|
5177
5357
|
const setupSpinner = spinner();
|
|
5178
|
-
setupSpinner.start("Checking Turso CLI availability...");
|
|
5179
5358
|
try {
|
|
5359
|
+
const mode = await select({
|
|
5360
|
+
message: "Turso setup: choose mode",
|
|
5361
|
+
options: [{
|
|
5362
|
+
label: "Automatic",
|
|
5363
|
+
value: "auto",
|
|
5364
|
+
hint: "Automated setup with provider CLI, sets .env"
|
|
5365
|
+
}, {
|
|
5366
|
+
label: "Manual",
|
|
5367
|
+
value: "manual",
|
|
5368
|
+
hint: "Manual setup, add env vars yourself"
|
|
5369
|
+
}],
|
|
5370
|
+
initialValue: "auto"
|
|
5371
|
+
});
|
|
5372
|
+
if (isCancel(mode)) return exitCancelled("Operation cancelled");
|
|
5373
|
+
if (mode === "manual") {
|
|
5374
|
+
await writeEnvFile(projectDir);
|
|
5375
|
+
displayManualSetupInstructions();
|
|
5376
|
+
return;
|
|
5377
|
+
}
|
|
5378
|
+
setupSpinner.start("Checking Turso CLI availability...");
|
|
5180
5379
|
const platform = os.platform();
|
|
5181
5380
|
const isMac = platform === "darwin";
|
|
5182
5381
|
const isWindows = platform === "win32";
|
|
5183
5382
|
if (isWindows) {
|
|
5184
|
-
setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
5383
|
+
if (setupSpinner) setupSpinner.stop(pc.yellow("Turso setup not supported on Windows"));
|
|
5185
5384
|
log.warn(pc.yellow("Automatic Turso setup is not supported on Windows."));
|
|
5186
5385
|
await writeEnvFile(projectDir);
|
|
5187
5386
|
displayManualSetupInstructions();
|
|
5188
5387
|
return;
|
|
5189
5388
|
}
|
|
5190
|
-
setupSpinner.stop("Turso CLI availability checked");
|
|
5389
|
+
if (setupSpinner) setupSpinner.stop("Turso CLI availability checked");
|
|
5191
5390
|
const isCliInstalled = await isTursoInstalled();
|
|
5192
5391
|
if (!isCliInstalled) {
|
|
5193
5392
|
const shouldInstall = await confirm({
|
|
@@ -5230,7 +5429,7 @@ async function setupTurso(config) {
|
|
|
5230
5429
|
}
|
|
5231
5430
|
log.success("Turso database setup completed successfully!");
|
|
5232
5431
|
} catch (error) {
|
|
5233
|
-
setupSpinner.stop(pc.red("Turso CLI availability check failed"));
|
|
5432
|
+
if (setupSpinner) setupSpinner.stop(pc.red("Turso CLI availability check failed"));
|
|
5234
5433
|
consola.error(pc.red(`Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`));
|
|
5235
5434
|
await writeEnvFile(projectDir);
|
|
5236
5435
|
displayManualSetupInstructions();
|
|
@@ -5254,7 +5453,22 @@ async function setupDatabase(config) {
|
|
|
5254
5453
|
const serverDir = path.join(projectDir, "apps/server");
|
|
5255
5454
|
if (!await fs.pathExists(serverDir)) return;
|
|
5256
5455
|
try {
|
|
5257
|
-
if (orm === "prisma") await addPackageDependency({
|
|
5456
|
+
if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
|
|
5457
|
+
dependencies: [
|
|
5458
|
+
"@prisma/client",
|
|
5459
|
+
"@prisma/adapter-planetscale",
|
|
5460
|
+
"@planetscale/database",
|
|
5461
|
+
"undici"
|
|
5462
|
+
],
|
|
5463
|
+
devDependencies: ["prisma"],
|
|
5464
|
+
projectDir: serverDir
|
|
5465
|
+
});
|
|
5466
|
+
else if (database === "sqlite" && dbSetup === "turso") await addPackageDependency({
|
|
5467
|
+
dependencies: ["@prisma/client", "@prisma/adapter-libsql"],
|
|
5468
|
+
devDependencies: ["prisma"],
|
|
5469
|
+
projectDir: serverDir
|
|
5470
|
+
});
|
|
5471
|
+
else await addPackageDependency({
|
|
5258
5472
|
dependencies: ["@prisma/client"],
|
|
5259
5473
|
devDependencies: ["prisma"],
|
|
5260
5474
|
projectDir: serverDir
|
|
@@ -5274,12 +5488,22 @@ async function setupDatabase(config) {
|
|
|
5274
5488
|
devDependencies: ["drizzle-kit", "@types/ws"],
|
|
5275
5489
|
projectDir: serverDir
|
|
5276
5490
|
});
|
|
5491
|
+
else if (dbSetup === "planetscale") await addPackageDependency({
|
|
5492
|
+
dependencies: ["drizzle-orm", "pg"],
|
|
5493
|
+
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5494
|
+
projectDir: serverDir
|
|
5495
|
+
});
|
|
5277
5496
|
else await addPackageDependency({
|
|
5278
5497
|
dependencies: ["drizzle-orm", "pg"],
|
|
5279
5498
|
devDependencies: ["drizzle-kit", "@types/pg"],
|
|
5280
5499
|
projectDir: serverDir
|
|
5281
5500
|
});
|
|
5282
|
-
else if (database === "mysql") await addPackageDependency({
|
|
5501
|
+
else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
|
|
5502
|
+
dependencies: ["drizzle-orm", "@planetscale/database"],
|
|
5503
|
+
devDependencies: ["drizzle-kit"],
|
|
5504
|
+
projectDir: serverDir
|
|
5505
|
+
});
|
|
5506
|
+
else await addPackageDependency({
|
|
5283
5507
|
dependencies: ["drizzle-orm", "mysql2"],
|
|
5284
5508
|
devDependencies: ["drizzle-kit"],
|
|
5285
5509
|
projectDir: serverDir
|
|
@@ -5295,7 +5519,10 @@ async function setupDatabase(config) {
|
|
|
5295
5519
|
else if (database === "postgres") {
|
|
5296
5520
|
if (dbSetup === "prisma-postgres") await setupPrismaPostgres(config);
|
|
5297
5521
|
else if (dbSetup === "neon") await setupNeonPostgres(config);
|
|
5522
|
+
else if (dbSetup === "planetscale") await setupPlanetScale(config);
|
|
5298
5523
|
else if (dbSetup === "supabase") await setupSupabase(config);
|
|
5524
|
+
} else if (database === "mysql") {
|
|
5525
|
+
if (dbSetup === "planetscale") await setupPlanetScale(config);
|
|
5299
5526
|
} else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config);
|
|
5300
5527
|
} catch (error) {
|
|
5301
5528
|
s.stop(pc.red("Failed to set up database"));
|
|
@@ -5740,7 +5967,7 @@ async function getDockerStatus(database) {
|
|
|
5740
5967
|
//#endregion
|
|
5741
5968
|
//#region src/helpers/core/post-installation.ts
|
|
5742
5969
|
async function displayPostInstallInstructions(config) {
|
|
5743
|
-
const { database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
5970
|
+
const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
|
|
5744
5971
|
const isConvex = backend === "convex";
|
|
5745
5972
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
5746
5973
|
const cdCmd = `cd ${relativePath}`;
|
|
@@ -5788,7 +6015,11 @@ async function displayPostInstallInstructions(config) {
|
|
|
5788
6015
|
output += `${pc.bold("Your project will be available at:")}\n`;
|
|
5789
6016
|
if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
|
|
5790
6017
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
5791
|
-
if (!isConvex)
|
|
6018
|
+
if (!isConvex) {
|
|
6019
|
+
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6020
|
+
if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
|
|
6021
|
+
else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6022
|
+
}
|
|
5792
6023
|
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
5793
6024
|
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
5794
6025
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
@@ -5837,9 +6068,14 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
|
|
|
5837
6068
|
instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
|
|
5838
6069
|
instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
|
|
5839
6070
|
}
|
|
5840
|
-
if (dbSetup === "d1" && serverDeploy === "alchemy")
|
|
6071
|
+
if (dbSetup === "d1" && serverDeploy === "alchemy") {
|
|
6072
|
+
if (orm === "drizzle") instructions.push(`${pc.yellow("NOTE:")} D1 migrations are automatically handled by Alchemy`);
|
|
6073
|
+
else if (orm === "prisma") {
|
|
6074
|
+
instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
|
|
6075
|
+
instructions.push(`${pc.cyan("•")} Apply migrations: ${`${runCmd} db:migrate`}`);
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
5841
6078
|
if (orm === "prisma") {
|
|
5842
|
-
if (dbSetup === "turso") instructions.push(`${pc.yellow("NOTE:")} Turso support with Prisma is in Early Access and requires\n additional setup. Learn more at:\n https://www.prisma.io/docs/orm/overview/databases/turso`);
|
|
5843
6079
|
if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
|
|
5844
6080
|
if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
|
|
5845
6081
|
instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
|
|
@@ -5924,7 +6160,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
5924
6160
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
|
|
5925
6161
|
if (options.orm === "prisma") {
|
|
5926
6162
|
scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
|
|
5927
|
-
|
|
6163
|
+
scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
|
|
5928
6164
|
} else if (options.orm === "drizzle") {
|
|
5929
6165
|
scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
|
|
5930
6166
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
|
|
@@ -5949,7 +6185,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
5949
6185
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
|
|
5950
6186
|
if (options.orm === "prisma") {
|
|
5951
6187
|
scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
|
|
5952
|
-
|
|
6188
|
+
scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
|
|
5953
6189
|
} else if (options.orm === "drizzle") {
|
|
5954
6190
|
scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
|
|
5955
6191
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
|
|
@@ -5974,7 +6210,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
5974
6210
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
|
|
5975
6211
|
if (options.orm === "prisma") {
|
|
5976
6212
|
scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
|
|
5977
|
-
|
|
6213
|
+
scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
|
|
5978
6214
|
} else if (options.orm === "drizzle") {
|
|
5979
6215
|
scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
|
|
5980
6216
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
|
|
@@ -5999,7 +6235,7 @@ async function updateRootPackageJson(projectDir, options) {
|
|
|
5999
6235
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
|
|
6000
6236
|
if (options.orm === "prisma") {
|
|
6001
6237
|
scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
|
|
6002
|
-
|
|
6238
|
+
scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
|
|
6003
6239
|
} else if (options.orm === "drizzle") {
|
|
6004
6240
|
scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
|
|
6005
6241
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
|
|
@@ -6042,7 +6278,7 @@ async function updateServerPackageJson(projectDir, options) {
|
|
|
6042
6278
|
scripts["db:push"] = "prisma db push";
|
|
6043
6279
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = "prisma studio";
|
|
6044
6280
|
scripts["db:generate"] = "prisma generate";
|
|
6045
|
-
|
|
6281
|
+
scripts["db:migrate"] = "prisma migrate dev";
|
|
6046
6282
|
} else if (options.orm === "drizzle") {
|
|
6047
6283
|
scripts["db:push"] = "drizzle-kit push";
|
|
6048
6284
|
if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = "drizzle-kit studio";
|
|
@@ -6365,7 +6601,7 @@ async function openUrl(url) {
|
|
|
6365
6601
|
|
|
6366
6602
|
//#endregion
|
|
6367
6603
|
//#region src/utils/sponsors.ts
|
|
6368
|
-
const SPONSORS_JSON_URL = "https://sponsors.
|
|
6604
|
+
const SPONSORS_JSON_URL = "https://sponsors.better-t-stack.dev/sponsors.json";
|
|
6369
6605
|
async function fetchSponsors(url = SPONSORS_JSON_URL) {
|
|
6370
6606
|
const s = spinner();
|
|
6371
6607
|
s.start("Fetching sponsors…");
|
|
@@ -6379,23 +6615,30 @@ async function fetchSponsors(url = SPONSORS_JSON_URL) {
|
|
|
6379
6615
|
return sponsors$1;
|
|
6380
6616
|
}
|
|
6381
6617
|
function displaySponsors(sponsors$1) {
|
|
6382
|
-
|
|
6618
|
+
const { total_sponsors } = sponsors$1.summary;
|
|
6619
|
+
if (total_sponsors === 0) {
|
|
6383
6620
|
log.info("No sponsors found. You can be the first one! ✨");
|
|
6384
6621
|
outro(pc.cyan("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
6385
6622
|
return;
|
|
6386
6623
|
}
|
|
6387
|
-
sponsors$1
|
|
6388
|
-
|
|
6389
|
-
const displayName = sponsor.name ?? sponsor.login;
|
|
6390
|
-
const tier = entry.tierName ? ` (${entry.tierName})` : "";
|
|
6391
|
-
log.step(`${idx + 1}. ${pc.green(displayName)}${pc.yellow(tier)}`);
|
|
6392
|
-
log.message(` ${pc.dim("GitHub:")} https://github.com/${sponsor.login}`);
|
|
6393
|
-
const website = sponsor.websiteUrl ?? sponsor.linkUrl;
|
|
6394
|
-
if (website) log.message(` ${pc.dim("Website:")} ${website}`);
|
|
6395
|
-
});
|
|
6396
|
-
log.message("");
|
|
6624
|
+
displaySponsorsBox(sponsors$1);
|
|
6625
|
+
if (total_sponsors - sponsors$1.specialSponsors.length > 0) log.message(pc.blue(`+${total_sponsors - sponsors$1.specialSponsors.length} more amazing sponsors.\n`));
|
|
6397
6626
|
outro(pc.magenta("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
|
|
6398
6627
|
}
|
|
6628
|
+
function displaySponsorsBox(sponsors$1) {
|
|
6629
|
+
if (sponsors$1.specialSponsors.length === 0) return;
|
|
6630
|
+
let output = `${pc.bold(pc.cyan("-> Special Sponsors"))}\n\n`;
|
|
6631
|
+
sponsors$1.specialSponsors.forEach((sponsor, idx) => {
|
|
6632
|
+
const displayName = sponsor.name ?? sponsor.githubId;
|
|
6633
|
+
const tier = sponsor.tierName ? ` ${pc.yellow(`(${sponsor.tierName})`)}` : "";
|
|
6634
|
+
output += `${pc.green(`• ${displayName}`)}${tier}\n`;
|
|
6635
|
+
output += ` ${pc.dim("GitHub:")} https://github.com/${sponsor.githubId}\n`;
|
|
6636
|
+
const website = sponsor.websiteUrl ?? sponsor.githubUrl;
|
|
6637
|
+
if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
|
|
6638
|
+
if (idx < sponsors$1.specialSponsors.length - 1) output += "\n";
|
|
6639
|
+
});
|
|
6640
|
+
consola$1.box(output);
|
|
6641
|
+
}
|
|
6399
6642
|
|
|
6400
6643
|
//#endregion
|
|
6401
6644
|
//#region src/index.ts
|