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.
Files changed (29) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +1 -1
  4. package/dist/{src-BOnnM-3r.js → src-BMkTBm89.js} +328 -85
  5. package/package.json +1 -1
  6. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +32 -3
  7. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +1 -1
  8. package/templates/backend/server/elysia/src/index.ts.hbs +31 -2
  9. package/templates/backend/server/express/src/index.ts.hbs +38 -4
  10. package/templates/backend/server/fastify/src/index.ts.hbs +33 -2
  11. package/templates/backend/server/hono/src/index.ts.hbs +40 -5
  12. package/templates/db/drizzle/mysql/src/db/index.ts.hbs +25 -0
  13. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +5 -0
  14. package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +6 -0
  15. package/templates/db/prisma/mysql/src/db/index.ts.hbs +12 -0
  16. package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +3 -0
  17. package/templates/db/prisma/postgres/src/db/index.ts.hbs +5 -0
  18. package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +10 -0
  19. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +29 -1
  20. package/templates/db/prisma/sqlite/src/db/index.ts.hbs +28 -0
  21. package/templates/deploy/alchemy/alchemy.run.ts.hbs +6 -2
  22. package/templates/deploy/wrangler/server/wrangler.jsonc.hbs +5 -0
  23. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +2 -2
  24. package/templates/extras/bunfig.toml.hbs +0 -5
  25. package/templates/frontend/react/next/next.config.ts.hbs +7 -0
  26. package/templates/db/prisma/mongodb/prisma/index.ts.hbs +0 -5
  27. package/templates/db/prisma/mysql/prisma/index.ts +0 -5
  28. package/templates/db/prisma/postgres/prisma/index.ts +0 -5
  29. 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.3.0",
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.63.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, orm, backend, runtime) {
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
- value: "docker",
794
- label: "Docker",
795
- hint: "Run locally with docker compose"
796
- }, {
797
- value: "none",
798
- label: "None",
799
- hint: "Manual setup"
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 hasIncompatibleFrontend = frontend.some((f) => f === "next");
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: hasIncompatibleFrontend ? "wrangler" : DEFAULT_CONFIG.webDeploy
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" && !hasIncompatibleFrontend) {
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: hasIncompatibleFrontend ? "wrangler" : DEFAULT_CONFIG.webDeploy
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 = "1";
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 = "phc_8ZUxEwwfKMajJLvxz1daGd931dYbQrwKNficBmsdIrs";
1345
- const POSTHOG_HOST = "https://us.i.posthog.com";
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 (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
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 === "npm" ? "npm" : packageManager;
3051
- await execa(runCmd, ["run", "cf-typegen"], { cwd: serverDir });
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
- devDependencies: ["alchemy", "dotenv"],
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: ["@orpc/server", "@orpc/client"] };
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) output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
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") instructions.push(`${pc.yellow("NOTE:")} D1 migrations are automatically handled by 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
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
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
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
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
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
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
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
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
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "prisma migrate dev";
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.amanv.dev/sponsors.json";
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
- if (sponsors$1.length === 0) {
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.forEach((entry, idx) => {
6388
- const sponsor = entry.sponsor;
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