create-better-t-stack 3.11.0 → 3.12.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.
Files changed (71) hide show
  1. package/dist/chunk-Dt3mZKp0.mjs +24 -0
  2. package/dist/cli.mjs +1 -1
  3. package/dist/index.d.mts +40 -60
  4. package/dist/index.mjs +2 -2
  5. package/dist/{src-XVvJUQ_h.mjs → src-DBVnwTkj.mjs} +668 -631
  6. package/package.json +2 -2
  7. package/templates/addons/turborepo/turbo.json.hbs +13 -0
  8. package/templates/api/orpc/native/utils/orpc.ts.hbs +21 -20
  9. package/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs +3 -5
  10. package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +73 -67
  11. package/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs +15 -14
  12. package/templates/api/trpc/native/utils/trpc.ts.hbs +8 -7
  13. package/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs +59 -57
  14. package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +3 -5
  15. package/templates/auth/better-auth/convex/backend/convex/privateData.ts.hbs +13 -12
  16. package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +10 -9
  17. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-client.ts.hbs +2 -2
  18. package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +11 -9
  19. package/templates/auth/better-auth/convex/web/react/tanstack-router/src/lib/auth-client.ts.hbs +5 -4
  20. package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +8 -7
  21. package/templates/auth/better-auth/native/base/lib/auth-client.ts.hbs +9 -8
  22. package/templates/auth/better-auth/server/base/src/index.ts.hbs +239 -235
  23. package/templates/auth/better-auth/web/nuxt/app/plugins/auth-client.ts.hbs +2 -3
  24. package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +9 -11
  25. package/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs +3 -2
  26. package/templates/backend/server/elysia/src/index.ts.hbs +71 -71
  27. package/templates/backend/server/express/src/index.ts.hbs +57 -57
  28. package/templates/backend/server/fastify/src/index.ts.hbs +107 -107
  29. package/templates/backend/server/hono/src/index.ts.hbs +75 -85
  30. package/templates/base/tsconfig.json.hbs +3 -0
  31. package/templates/db/drizzle/mysql/src/index.ts.hbs +23 -30
  32. package/templates/db/drizzle/postgres/src/index.ts.hbs +6 -13
  33. package/templates/db/drizzle/sqlite/src/index.ts.hbs +11 -18
  34. package/templates/db/mongoose/mongodb/src/index.ts.hbs +3 -2
  35. package/templates/db/prisma/mongodb/prisma/schema/schema.prisma.hbs +1 -1
  36. package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +1 -1
  37. package/templates/db/prisma/mysql/prisma.config.ts.hbs +16 -16
  38. package/templates/db/prisma/mysql/src/index.ts.hbs +16 -15
  39. package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +1 -1
  40. package/templates/db/prisma/postgres/src/index.ts.hbs +10 -9
  41. package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +1 -1
  42. package/templates/db/prisma/sqlite/src/index.ts.hbs +4 -7
  43. package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +2 -1
  44. package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +2 -1
  45. package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +2 -1
  46. package/templates/examples/ai/web/nuxt/app/pages/ai.vue.hbs +1 -3
  47. package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +4 -3
  48. package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +2 -1
  49. package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +4 -1
  50. package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +4 -1
  51. package/templates/frontend/native/bare/app/_layout.tsx.hbs +4 -2
  52. package/templates/frontend/native/unistyles/app/_layout.tsx.hbs +4 -2
  53. package/templates/frontend/native/uniwind/app/_layout.tsx.hbs +4 -3
  54. package/templates/frontend/nuxt/nuxt.config.ts.hbs +6 -3
  55. package/templates/frontend/react/next/next.config.ts.hbs +9 -8
  56. package/templates/frontend/react/next/src/components/providers.tsx.hbs +4 -1
  57. package/templates/frontend/react/next/tsconfig.json.hbs +2 -2
  58. package/templates/frontend/react/react-router/src/root.tsx.hbs +3 -4
  59. package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +3 -2
  60. package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +100 -108
  61. package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +25 -7
  62. package/templates/packages/config/tsconfig.base.json.hbs +1 -1
  63. package/templates/{deploy/alchemy → packages/env}/env.d.ts.hbs +6 -4
  64. package/templates/packages/env/package.json.hbs +7 -0
  65. package/templates/packages/env/src/native.ts.hbs +21 -0
  66. package/templates/packages/env/src/server.ts.hbs +38 -0
  67. package/templates/packages/env/src/web.ts.hbs +89 -0
  68. package/templates/packages/env/tsconfig.json.hbs +3 -0
  69. package/templates/{deploy/alchemy → packages/infra}/alchemy.run.ts.hbs +86 -82
  70. package/templates/packages/infra/package.json.hbs +10 -0
  71. package/templates/payments/polar/server/base/src/lib/payments.ts.hbs +3 -2
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { t as __reExport } from "./chunk-Dt3mZKp0.mjs";
2
3
  import { autocompleteMultiselect, cancel, confirm, group, groupMultiselect, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
3
4
  import { createRouterClient, os } from "@orpc/server";
4
5
  import pc from "picocolors";
5
6
  import { createCli } from "trpc-cli";
6
7
  import z from "zod";
7
- import path from "node:path";
8
8
  import consola, { consola as consola$1 } from "consola";
9
9
  import fs from "fs-extra";
10
+ import path from "node:path";
10
11
  import { fileURLToPath } from "node:url";
11
- import { APISchema, AddonsSchema, AuthSchema, BackendSchema, DatabaseSchema, DatabaseSetupSchema, DirectoryConflictSchema, ExamplesSchema, FrontendSchema, ORMSchema, PackageManagerSchema, PaymentsSchema, ProjectNameSchema, RuntimeSchema, ServerDeploySchema, TemplateSchema, WebDeploySchema } from "@better-t-stack/types";
12
12
  import gradient from "gradient-string";
13
13
  import * as JSONC from "jsonc-parser";
14
14
  import { $, execa } from "execa";
@@ -64,8 +64,8 @@ function getDefaultConfig() {
64
64
  const DEFAULT_CONFIG = getDefaultConfig();
65
65
  const dependencyVersionMap = {
66
66
  typescript: "^5",
67
- "better-auth": "^1.4.7",
68
- "@better-auth/expo": "^1.4.7",
67
+ "better-auth": "^1.4.9",
68
+ "@better-auth/expo": "^1.4.9",
69
69
  "@clerk/nextjs": "^6.31.5",
70
70
  "@clerk/clerk-react": "^5.45.0",
71
71
  "@clerk/tanstack-react-start": "^0.26.3",
@@ -138,13 +138,14 @@ const dependencyVersionMap = {
138
138
  "convex-svelte": "^0.0.12",
139
139
  "convex-nuxt": "0.1.5",
140
140
  "convex-vue": "^0.1.5",
141
- "@convex-dev/better-auth": "^0.10.6",
141
+ "@convex-dev/better-auth": "^0.10.9",
142
142
  "@tanstack/svelte-query": "^5.85.3",
143
143
  "@tanstack/svelte-query-devtools": "^5.85.3",
144
144
  "@tanstack/vue-query-devtools": "^5.90.2",
145
145
  "@tanstack/vue-query": "^5.90.2",
146
146
  "@tanstack/react-query-devtools": "^5.91.1",
147
147
  "@tanstack/react-query": "^5.90.12",
148
+ "@tanstack/react-router-ssr-query": "^1.142.7",
148
149
  "@tanstack/solid-query": "^5.87.4",
149
150
  "@tanstack/solid-query-devtools": "^5.87.4",
150
151
  "@tanstack/solid-router-devtools": "^1.131.44",
@@ -154,10 +155,13 @@ const dependencyVersionMap = {
154
155
  "nitro-cloudflare-dev": "^0.2.2",
155
156
  "@sveltejs/adapter-cloudflare": "^7.2.4",
156
157
  "@cloudflare/workers-types": "^4.20251213.0",
157
- alchemy: "^0.81.2",
158
+ alchemy: "^0.82.1",
158
159
  dotenv: "^17.2.2",
159
160
  tsdown: "^0.16.5",
160
161
  zod: "^4.1.13",
162
+ "@t3-oss/env-core": "^0.13.1",
163
+ "@t3-oss/env-nextjs": "^0.13.1",
164
+ "@t3-oss/env-nuxt": "^0.13.1",
161
165
  srvx: "0.8.15",
162
166
  "@polar-sh/better-auth": "^1.1.3",
163
167
  "@polar-sh/sdk": "^0.34.16"
@@ -190,6 +194,12 @@ const ADDON_COMPATIBILITY = {
190
194
  none: []
191
195
  };
192
196
 
197
+ //#endregion
198
+ //#region src/types.ts
199
+ var types_exports = {};
200
+ import * as import__better_t_stack_types from "@better-t-stack/types";
201
+ __reExport(types_exports, import__better_t_stack_types);
202
+
193
203
  //#endregion
194
204
  //#region src/utils/compatibility.ts
195
205
  const WEB_FRAMEWORKS = [
@@ -204,24 +214,18 @@ const WEB_FRAMEWORKS = [
204
214
 
205
215
  //#endregion
206
216
  //#region src/utils/errors.ts
207
- function isProgrammatic() {
208
- return process.env.BTS_PROGRAMMATIC === "1";
209
- }
210
217
  function exitWithError(message) {
211
218
  consola.error(pc.red(message));
212
- if (isProgrammatic()) throw new Error(message);
213
- process.exit(1);
219
+ throw new Error(message);
214
220
  }
215
221
  function exitCancelled(message = "Operation cancelled") {
216
222
  cancel(pc.red(message));
217
- if (isProgrammatic()) throw new Error(message);
218
- process.exit(0);
223
+ throw new Error(message);
219
224
  }
220
225
  function handleError(error, fallbackMessage) {
221
226
  const message = error instanceof Error ? error.message : fallbackMessage || String(error);
222
227
  consola.error(pc.red(message));
223
- if (isProgrammatic()) throw new Error(message);
224
- process.exit(1);
228
+ throw new Error(message);
225
229
  }
226
230
 
227
231
  //#endregion
@@ -434,7 +438,7 @@ const ADDON_GROUPS = {
434
438
  };
435
439
  async function getAddonsChoice(addons, frontends, auth) {
436
440
  if (addons !== void 0) return addons;
437
- const allAddons = AddonsSchema.options.filter((addon) => addon !== "none");
441
+ const allAddons = types_exports.AddonsSchema.options.filter((addon) => addon !== "none");
438
442
  const groupedOptions = {
439
443
  Documentation: [],
440
444
  Linting: [],
@@ -480,7 +484,7 @@ async function getAddonsToAdd(frontend, existingAddons = [], auth) {
480
484
  Other: []
481
485
  };
482
486
  const frontendArray = frontend || [];
483
- const compatibleAddons = getCompatibleAddons(AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
487
+ const compatibleAddons = getCompatibleAddons(types_exports.AddonsSchema.options.filter((addon) => addon !== "none"), frontendArray, existingAddons, auth);
484
488
  for (const addon of compatibleAddons) {
485
489
  const { label, hint } = getAddonDisplay(addon);
486
490
  const option = {
@@ -1057,8 +1061,8 @@ async function getRuntimeChoice(runtime, backend) {
1057
1061
  //#endregion
1058
1062
  //#region src/prompts/server-deploy.ts
1059
1063
  function getDeploymentDisplay$1(deployment) {
1060
- if (deployment === "alchemy") return {
1061
- label: "Alchemy",
1064
+ if (deployment === "cloudflare") return {
1065
+ label: "Cloudflare",
1062
1066
  hint: "Deploy to Cloudflare Workers using Alchemy"
1063
1067
  };
1064
1068
  return {
@@ -1070,17 +1074,17 @@ async function getServerDeploymentChoice(deployment, runtime, backend, _webDeplo
1070
1074
  if (deployment !== void 0) return deployment;
1071
1075
  if (backend === "none" || backend === "convex") return "none";
1072
1076
  if (backend !== "hono") return "none";
1073
- if (runtime === "workers") return "alchemy";
1077
+ if (runtime === "workers") return "cloudflare";
1074
1078
  return "none";
1075
1079
  }
1076
1080
  async function getServerDeploymentToAdd(runtime, existingDeployment, backend) {
1077
1081
  if (backend !== "hono") return "none";
1078
1082
  const options = [];
1079
1083
  if (runtime === "workers") {
1080
- if (existingDeployment !== "alchemy") {
1081
- const { label, hint } = getDeploymentDisplay$1("alchemy");
1084
+ if (existingDeployment !== "cloudflare") {
1085
+ const { label, hint } = getDeploymentDisplay$1("cloudflare");
1082
1086
  options.push({
1083
- value: "alchemy",
1087
+ value: "cloudflare",
1084
1088
  label,
1085
1089
  hint
1086
1090
  });
@@ -1103,8 +1107,8 @@ function hasWebFrontend(frontends) {
1103
1107
  return frontends.some((f) => WEB_FRAMEWORKS.includes(f));
1104
1108
  }
1105
1109
  function getDeploymentDisplay(deployment) {
1106
- if (deployment === "alchemy") return {
1107
- label: "Alchemy",
1110
+ if (deployment === "cloudflare") return {
1111
+ label: "Cloudflare",
1108
1112
  hint: "Deploy to Cloudflare Workers using Alchemy"
1109
1113
  };
1110
1114
  return {
@@ -1117,7 +1121,7 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
1117
1121
  if (!hasWebFrontend(frontend)) return "none";
1118
1122
  const response = await select({
1119
1123
  message: "Select web deployment",
1120
- options: ["alchemy", "none"].map((deploy) => {
1124
+ options: ["cloudflare", "none"].map((deploy) => {
1121
1125
  const { label, hint } = getDeploymentDisplay(deploy);
1122
1126
  return {
1123
1127
  value: deploy,
@@ -1133,10 +1137,10 @@ async function getDeploymentChoice(deployment, _runtime, _backend, frontend = []
1133
1137
  async function getDeploymentToAdd(frontend, existingDeployment) {
1134
1138
  if (!hasWebFrontend(frontend)) return "none";
1135
1139
  const options = [];
1136
- if (existingDeployment !== "alchemy") {
1137
- const { label, hint } = getDeploymentDisplay("alchemy");
1140
+ if (existingDeployment !== "cloudflare") {
1141
+ const { label, hint } = getDeploymentDisplay("cloudflare");
1138
1142
  options.push({
1139
- value: "alchemy",
1143
+ value: "cloudflare",
1140
1144
  label,
1141
1145
  hint
1142
1146
  });
@@ -1210,7 +1214,7 @@ function isPathWithinCwd(targetPath) {
1210
1214
  }
1211
1215
  function validateDirectoryName(name) {
1212
1216
  if (name === ".") return void 0;
1213
- const result = ProjectNameSchema.safeParse(name);
1217
+ const result = types_exports.ProjectNameSchema.safeParse(name);
1214
1218
  if (!result.success) return result.error.issues[0]?.message || "Invalid project name";
1215
1219
  }
1216
1220
  async function getProjectName(initialName) {
@@ -1770,7 +1774,7 @@ function validateFullConfig(config, providedFlags, options) {
1770
1774
  validateSelfBackendCompatibility(providedFlags, options, config);
1771
1775
  validateWorkersCompatibility(providedFlags, options, config);
1772
1776
  if (config.runtime === "workers" && config.serverDeploy === "none") exitWithError("Cloudflare Workers runtime requires a server deployment. Please choose 'alchemy' for --server-deploy.");
1773
- if (providedFlags.has("serverDeploy") && config.serverDeploy === "alchemy" && config.runtime !== "workers") exitWithError(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
1777
+ if (providedFlags.has("serverDeploy") && config.serverDeploy === "cloudflare" && config.runtime !== "workers") exitWithError(`Server deployment '${config.serverDeploy}' requires '--runtime workers'. Please use '--runtime workers' or choose a different server deployment.`);
1774
1778
  if (config.addons && config.addons.length > 0) {
1775
1779
  validateAddonsAgainstFrontends(config.addons, config.frontend, config.auth);
1776
1780
  config.addons = [...new Set(config.addons)];
@@ -1795,11 +1799,11 @@ function validateConfigForProgrammaticUse(config) {
1795
1799
  //#endregion
1796
1800
  //#region src/utils/project-name-validation.ts
1797
1801
  function validateProjectName(name) {
1798
- const result = ProjectNameSchema.safeParse(name);
1802
+ const result = types_exports.ProjectNameSchema.safeParse(name);
1799
1803
  if (!result.success) exitWithError(`Invalid project name: ${result.error.issues[0]?.message || "Invalid project name"}`);
1800
1804
  }
1801
1805
  function validateProjectNameThrow(name) {
1802
- const result = ProjectNameSchema.safeParse(name);
1806
+ const result = types_exports.ProjectNameSchema.safeParse(name);
1803
1807
  if (!result.success) throw new Error(`Invalid project name: ${result.error.issues[0]?.message}`);
1804
1808
  }
1805
1809
  function extractAndValidateProjectName(projectName, projectDirectory, throwOnError = false) {
@@ -1997,6 +2001,45 @@ function getPackageExecutionCommand(packageManager, commandWithArgs) {
1997
2001
  default: return `npx ${commandWithArgs}`;
1998
2002
  }
1999
2003
  }
2004
+ /**
2005
+ * Returns the command and arguments as an array for use with execa's $ template syntax.
2006
+ * This avoids the need for shell: true and provides better escaping.
2007
+ *
2008
+ * @param packageManager - The selected package manager (e.g., 'npm', 'yarn', 'pnpm', 'bun').
2009
+ * @param commandWithArgs - The command to run, including arguments (e.g., "prisma generate").
2010
+ * @returns An array of [command, ...args] (e.g., ["npx", "prisma", "generate"]).
2011
+ */
2012
+ function getPackageExecutionArgs(packageManager, commandWithArgs) {
2013
+ const args = commandWithArgs.split(" ");
2014
+ switch (packageManager) {
2015
+ case "pnpm": return [
2016
+ "pnpm",
2017
+ "dlx",
2018
+ ...args
2019
+ ];
2020
+ case "bun": return ["bunx", ...args];
2021
+ default: return ["npx", ...args];
2022
+ }
2023
+ }
2024
+ /**
2025
+ * Returns just the runner prefix as an array, for when you already have args built.
2026
+ * Use this when you have complex arguments that shouldn't be split by spaces.
2027
+ *
2028
+ * @param packageManager - The selected package manager.
2029
+ * @returns The runner prefix as an array (e.g., ["npx"] or ["pnpm", "dlx"]).
2030
+ *
2031
+ * @example
2032
+ * const prefix = getPackageRunnerPrefix("bun");
2033
+ * const args = ["@tauri-apps/cli@latest", "init", "--app-name=foo"];
2034
+ * await $`${[...prefix, ...args]}`;
2035
+ */
2036
+ function getPackageRunnerPrefix(packageManager) {
2037
+ switch (packageManager) {
2038
+ case "pnpm": return ["pnpm", "dlx"];
2039
+ case "bun": return ["bunx"];
2040
+ default: return ["npx"];
2041
+ }
2042
+ }
2000
2043
 
2001
2044
  //#endregion
2002
2045
  //#region src/helpers/addons/fumadocs-setup.ts
@@ -2041,16 +2084,15 @@ async function setupFumadocs(config) {
2041
2084
  initialValue: "next-mdx"
2042
2085
  });
2043
2086
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2044
- const fumadocsInitCommand = getPackageExecutionCommand(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES$2[template].value} --src --pm ${packageManager} --no-git`);
2087
+ const args = getPackageExecutionArgs(packageManager, `create-fumadocs-app@latest fumadocs --template ${TEMPLATES$2[template].value} --src --pm ${packageManager} --no-git`);
2045
2088
  const appsDir = path.join(projectDir, "apps");
2046
2089
  await fs.ensureDir(appsDir);
2047
2090
  const s = spinner();
2048
2091
  s.start("Running Fumadocs create command...");
2049
- await execa(fumadocsInitCommand, {
2092
+ await $({
2050
2093
  cwd: appsDir,
2051
- env: { CI: "true" },
2052
- shell: true
2053
- });
2094
+ env: { CI: "true" }
2095
+ })`${args}`;
2054
2096
  const fumadocsDir = path.join(projectDir, "apps", "fumadocs");
2055
2097
  const packageJsonPath = path.join(fumadocsDir, "package.json");
2056
2098
  if (await fs.pathExists(packageJsonPath)) {
@@ -2083,18 +2125,17 @@ async function setupOxlint(projectDir, packageManager) {
2083
2125
  await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
2084
2126
  }
2085
2127
  const s = spinner();
2086
- const oxlintInitCommand = getPackageExecutionCommand(packageManager, "oxlint@latest --init");
2128
+ const oxlintArgs = getPackageExecutionArgs(packageManager, "oxlint@latest --init");
2087
2129
  s.start("Initializing oxlint and oxfmt...");
2088
- await execa(oxlintInitCommand, {
2130
+ await $({
2089
2131
  cwd: projectDir,
2090
- env: { CI: "true" },
2091
- shell: true
2092
- });
2093
- await execa(getPackageExecutionCommand(packageManager, "oxfmt@latest --init"), {
2132
+ env: { CI: "true" }
2133
+ })`${oxlintArgs}`;
2134
+ const oxfmtArgs = getPackageExecutionArgs(packageManager, "oxfmt@latest --init");
2135
+ await $({
2094
2136
  cwd: projectDir,
2095
- env: { CI: "true" },
2096
- shell: true
2097
- });
2137
+ env: { CI: "true" }
2138
+ })`${oxfmtArgs}`;
2098
2139
  s.stop("oxlint and oxfmt initialized successfully!");
2099
2140
  }
2100
2141
 
@@ -2159,11 +2200,11 @@ async function setupRuler(config) {
2159
2200
  const s = spinner();
2160
2201
  s.start("Applying rules with Ruler...");
2161
2202
  try {
2162
- await execa(getPackageExecutionCommand(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`), {
2203
+ const rulerApplyArgs = getPackageExecutionArgs(packageManager, `@intellectronica/ruler@latest apply --agents ${selectedEditors.join(",")} --local-only`);
2204
+ await $({
2163
2205
  cwd: projectDir,
2164
- env: { CI: "true" },
2165
- shell: true
2166
- });
2206
+ env: { CI: "true" }
2207
+ })`${rulerApplyArgs}`;
2167
2208
  s.stop("Applied rules with Ruler");
2168
2209
  } catch {
2169
2210
  s.stop(pc.red("Failed to apply rules"));
@@ -2193,7 +2234,7 @@ async function setupStarlight(config) {
2193
2234
  const s = spinner();
2194
2235
  try {
2195
2236
  s.start("Setting up Starlight docs...");
2196
- const starlightInitCommand = getPackageExecutionCommand(packageManager, `create-astro@latest ${[
2237
+ const args = getPackageExecutionArgs(packageManager, `create-astro@latest ${[
2197
2238
  "docs",
2198
2239
  "--template",
2199
2240
  "starlight",
@@ -2205,11 +2246,10 @@ async function setupStarlight(config) {
2205
2246
  ].join(" ")}`);
2206
2247
  const appsDir = path.join(projectDir, "apps");
2207
2248
  await fs.ensureDir(appsDir);
2208
- await execa(starlightInitCommand, {
2249
+ await $({
2209
2250
  cwd: appsDir,
2210
- env: { CI: "true" },
2211
- shell: true
2212
- });
2251
+ env: { CI: "true" }
2252
+ })`${args}`;
2213
2253
  s.stop("Starlight docs setup successfully!");
2214
2254
  } catch (error) {
2215
2255
  s.stop(pc.red("Failed to set up Starlight docs"));
@@ -2249,19 +2289,21 @@ async function setupTauri(config) {
2249
2289
  const hasNext = frontend.includes("next");
2250
2290
  const devUrl = hasReactRouter || hasSvelte ? "http://localhost:5173" : hasNext ? "http://localhost:3001" : "http://localhost:3001";
2251
2291
  const frontendDist = hasNuxt ? "../.output/public" : hasSvelte ? "../build" : hasNext ? "../.next" : hasReactRouter ? "../build/client" : "../dist";
2252
- await execa(getPackageExecutionCommand(packageManager, `@tauri-apps/cli@latest ${[
2292
+ const tauriArgs = [
2293
+ "@tauri-apps/cli@latest",
2253
2294
  "init",
2254
2295
  `--app-name=${path.basename(projectDir)}`,
2255
2296
  `--window-title=${path.basename(projectDir)}`,
2256
2297
  `--frontend-dist=${frontendDist}`,
2257
2298
  `--dev-url=${devUrl}`,
2258
- `--before-dev-command="${packageManager} run dev"`,
2259
- `--before-build-command="${packageManager} run build"`
2260
- ].join(" ")}`), {
2299
+ `--before-dev-command=${packageManager} run dev`,
2300
+ `--before-build-command=${packageManager} run build`
2301
+ ];
2302
+ const prefix = getPackageRunnerPrefix(packageManager);
2303
+ await $({
2261
2304
  cwd: clientPackageDir,
2262
- env: { CI: "true" },
2263
- shell: true
2264
- });
2305
+ env: { CI: "true" }
2306
+ })`${[...prefix, ...tauriArgs]}`;
2265
2307
  s.stop("Tauri desktop app support configured successfully!");
2266
2308
  } catch (error) {
2267
2309
  s.stop(pc.red("Failed to set up Tauri"));
@@ -2299,16 +2341,15 @@ async function setupTui(config) {
2299
2341
  initialValue: "core"
2300
2342
  });
2301
2343
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2302
- const tuiInitCommand = getPackageExecutionCommand(packageManager, `create-tui@latest --template ${template} --no-git --no-install tui`);
2344
+ const args = getPackageExecutionArgs(packageManager, `create-tui@latest --template ${template} --no-git --no-install tui`);
2303
2345
  const appsDir = path.join(projectDir, "apps");
2304
2346
  await fs.ensureDir(appsDir);
2305
2347
  const s = spinner();
2306
2348
  s.start("Running OpenTUI create command...");
2307
- await execa(tuiInitCommand, {
2349
+ await $({
2308
2350
  cwd: appsDir,
2309
- env: { CI: "true" },
2310
- shell: true
2311
- });
2351
+ env: { CI: "true" }
2352
+ })`${args}`;
2312
2353
  s.stop("OpenTUI setup complete!");
2313
2354
  } catch (error) {
2314
2355
  log.error(pc.red("Failed to set up OpenTUI"));
@@ -2409,14 +2450,13 @@ async function setupUltracite(config, hasHusky) {
2409
2450
  if (agents.length > 0) ultraciteArgs.push("--agents", ...agents);
2410
2451
  if (hooks.length > 0) ultraciteArgs.push("--hooks", ...hooks);
2411
2452
  if (hasHusky) ultraciteArgs.push("--integrations", "husky", "lint-staged");
2412
- const ultraciteInitCommand = getPackageExecutionCommand(packageManager, `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`);
2453
+ const args = getPackageExecutionArgs(packageManager, `ultracite@latest ${ultraciteArgs.join(" ")} --skip-install`);
2413
2454
  const s = spinner();
2414
2455
  s.start("Running Ultracite init command...");
2415
- await execa(ultraciteInitCommand, {
2456
+ await $({
2416
2457
  cwd: projectDir,
2417
- env: { CI: "true" },
2418
- shell: true
2419
- });
2458
+ env: { CI: "true" }
2459
+ })`${args}`;
2420
2460
  if (hasHusky) await addPackageDependency({
2421
2461
  devDependencies: ["husky", "lint-staged"],
2422
2462
  projectDir
@@ -2428,6 +2468,54 @@ async function setupUltracite(config, hasHusky) {
2428
2468
  }
2429
2469
  }
2430
2470
 
2471
+ //#endregion
2472
+ //#region src/utils/ts-morph.ts
2473
+ const tsProject = new Project({
2474
+ useInMemoryFileSystem: false,
2475
+ skipAddingFilesFromTsConfig: true,
2476
+ manipulationSettings: {
2477
+ quoteKind: QuoteKind.Single,
2478
+ indentationText: IndentationText.TwoSpaces
2479
+ }
2480
+ });
2481
+ function ensureArrayProperty(obj, name) {
2482
+ return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
2483
+ name,
2484
+ initializer: "[]"
2485
+ }).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
2486
+ }
2487
+
2488
+ //#endregion
2489
+ //#region src/helpers/addons/vite-pwa-setup.ts
2490
+ async function addPwaToViteConfig(viteConfigPath, projectName) {
2491
+ const sourceFile = tsProject.addSourceFileAtPathIfExists(viteConfigPath);
2492
+ if (!sourceFile) throw new Error("vite config not found");
2493
+ if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
2494
+ namedImports: ["VitePWA"],
2495
+ moduleSpecifier: "vite-plugin-pwa"
2496
+ });
2497
+ const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
2498
+ const expression = expr.getExpression();
2499
+ return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
2500
+ });
2501
+ if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
2502
+ const configObject = defineCall.getArguments()[0];
2503
+ if (!configObject) throw new Error("defineConfig argument is not an object literal");
2504
+ const pluginsArray = ensureArrayProperty(configObject, "plugins");
2505
+ if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
2506
+ registerType: "autoUpdate",
2507
+ manifest: {
2508
+ name: "${projectName}",
2509
+ short_name: "${projectName}",
2510
+ description: "${projectName} - PWA Application",
2511
+ theme_color: "#0c0c0c",
2512
+ },
2513
+ pwaAssets: { disabled: false, config: true },
2514
+ devOptions: { enabled: true },
2515
+ })`);
2516
+ await tsProject.save();
2517
+ }
2518
+
2431
2519
  //#endregion
2432
2520
  //#region src/helpers/addons/wxt-setup.ts
2433
2521
  const TEMPLATES = {
@@ -2466,16 +2554,15 @@ async function setupWxt(config) {
2466
2554
  initialValue: "react"
2467
2555
  });
2468
2556
  if (isCancel(template)) return exitCancelled("Operation cancelled");
2469
- const wxtInitCommand = getPackageExecutionCommand(packageManager, `wxt@latest init extension --template ${template} --pm ${packageManager}`);
2557
+ const args = getPackageExecutionArgs(packageManager, `wxt@latest init extension --template ${template} --pm ${packageManager}`);
2470
2558
  const appsDir = path.join(projectDir, "apps");
2471
2559
  await fs.ensureDir(appsDir);
2472
2560
  const s = spinner();
2473
2561
  s.start("Running WXT init command...");
2474
- await execa(wxtInitCommand, {
2562
+ await $({
2475
2563
  cwd: appsDir,
2476
- env: { CI: "true" },
2477
- shell: true
2478
- });
2564
+ env: { CI: "true" }
2565
+ })`${args}`;
2479
2566
  const extensionDir = path.join(projectDir, "apps", "extension");
2480
2567
  const packageJsonPath = path.join(extensionDir, "package.json");
2481
2568
  if (await fs.pathExists(packageJsonPath)) {
@@ -2491,54 +2578,6 @@ async function setupWxt(config) {
2491
2578
  }
2492
2579
  }
2493
2580
 
2494
- //#endregion
2495
- //#region src/utils/ts-morph.ts
2496
- const tsProject$1 = new Project({
2497
- useInMemoryFileSystem: false,
2498
- skipAddingFilesFromTsConfig: true,
2499
- manipulationSettings: {
2500
- quoteKind: QuoteKind.Single,
2501
- indentationText: IndentationText.TwoSpaces
2502
- }
2503
- });
2504
- function ensureArrayProperty(obj, name) {
2505
- return obj.getProperty(name)?.getFirstDescendantByKind(SyntaxKind.ArrayLiteralExpression) ?? obj.addPropertyAssignment({
2506
- name,
2507
- initializer: "[]"
2508
- }).getFirstDescendantByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
2509
- }
2510
-
2511
- //#endregion
2512
- //#region src/helpers/addons/vite-pwa-setup.ts
2513
- async function addPwaToViteConfig(viteConfigPath, projectName) {
2514
- const sourceFile = tsProject$1.addSourceFileAtPathIfExists(viteConfigPath);
2515
- if (!sourceFile) throw new Error("vite config not found");
2516
- if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === "vite-plugin-pwa")) sourceFile.insertImportDeclaration(0, {
2517
- namedImports: ["VitePWA"],
2518
- moduleSpecifier: "vite-plugin-pwa"
2519
- });
2520
- const defineCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((expr) => {
2521
- const expression = expr.getExpression();
2522
- return Node.isIdentifier(expression) && expression.getText() === "defineConfig";
2523
- });
2524
- if (!defineCall) throw new Error("Could not find defineConfig call in vite config");
2525
- const configObject = defineCall.getArguments()[0];
2526
- if (!configObject) throw new Error("defineConfig argument is not an object literal");
2527
- const pluginsArray = ensureArrayProperty(configObject, "plugins");
2528
- if (!pluginsArray.getElements().some((el) => el.getText().startsWith("VitePWA("))) pluginsArray.addElement(`VitePWA({
2529
- registerType: "autoUpdate",
2530
- manifest: {
2531
- name: "${projectName}",
2532
- short_name: "${projectName}",
2533
- description: "${projectName} - PWA Application",
2534
- theme_color: "#0c0c0c",
2535
- },
2536
- pwaAssets: { disabled: false, config: true },
2537
- devOptions: { enabled: true },
2538
- })`);
2539
- await tsProject$1.save();
2540
- }
2541
-
2542
2581
  //#endregion
2543
2582
  //#region src/helpers/addons/addons-setup.ts
2544
2583
  async function setupAddons(config, isAddCommand = false) {
@@ -2767,38 +2806,6 @@ handlebars.registerHelper("or", (...args) => {
2767
2806
  });
2768
2807
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2769
2808
 
2770
- //#endregion
2771
- //#region src/helpers/deployment/alchemy/env-dts-setup.ts
2772
- const tsProject = new Project({
2773
- useInMemoryFileSystem: false,
2774
- skipAddingFilesFromTsConfig: true
2775
- });
2776
- function determineImportPath(envDtsPath, projectDir, config) {
2777
- const { webDeploy, serverDeploy, backend } = config;
2778
- const isBackendSelf = backend === "self";
2779
- let alchemyRunPath;
2780
- if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) if (isBackendSelf) alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
2781
- else alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
2782
- else if (webDeploy === "alchemy") alchemyRunPath = path.join(projectDir, "apps/web/alchemy.run.ts");
2783
- else if (serverDeploy === "alchemy") alchemyRunPath = path.join(projectDir, "apps/server/alchemy.run.ts");
2784
- else alchemyRunPath = path.join(projectDir, "alchemy.run.ts");
2785
- const relativePath = path.relative(path.dirname(envDtsPath), alchemyRunPath.replace(/\.ts$/, ""));
2786
- return (relativePath.startsWith(".") ? relativePath : `./${relativePath}`).replace(/\\/g, "/");
2787
- }
2788
- async function setupEnvDtsImport(envDtsPath, projectDir, config) {
2789
- if (!await fs.pathExists(envDtsPath)) return;
2790
- const importPath = determineImportPath(envDtsPath, projectDir, config);
2791
- const sourceFile = tsProject.addSourceFileAtPath(envDtsPath);
2792
- if (!sourceFile.getImportDeclarations().some((imp) => imp.getModuleSpecifierValue() === importPath && imp.getNamedImports().some((named) => named.getName() === "server"))) sourceFile.insertImportDeclaration(0, {
2793
- moduleSpecifier: importPath,
2794
- namedImports: [{
2795
- name: "server",
2796
- isTypeOnly: true
2797
- }]
2798
- });
2799
- await sourceFile.save();
2800
- }
2801
-
2802
2809
  //#endregion
2803
2810
  //#region src/helpers/core/template-manager.ts
2804
2811
  async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, context, overwrite = true, ignorePatterns) {
@@ -2903,7 +2910,7 @@ async function setupFrontendTemplates(projectDir, context) {
2903
2910
  }
2904
2911
  }
2905
2912
  }
2906
- async function setupApiPackage(projectDir, context) {
2913
+ async function setupApiPackage$1(projectDir, context) {
2907
2914
  if (context.api === "none") return;
2908
2915
  const apiPackageDir = path.join(projectDir, "packages/api");
2909
2916
  await fs.ensureDir(apiPackageDir);
@@ -2916,7 +2923,7 @@ async function setupConfigPackage(projectDir, context) {
2916
2923
  const configBaseDir = path.join(PKG_ROOT, "templates/packages/config");
2917
2924
  if (await fs.pathExists(configBaseDir)) await processAndCopyFiles("**/*", configBaseDir, configPackageDir, context);
2918
2925
  }
2919
- async function setupDbPackage(projectDir, context) {
2926
+ async function setupDbPackage$1(projectDir, context) {
2920
2927
  if (context.database === "none" || context.orm === "none") return;
2921
2928
  const dbPackageDir = path.join(projectDir, "packages/db");
2922
2929
  await fs.ensureDir(dbPackageDir);
@@ -2943,21 +2950,78 @@ async function setupServerApp(projectDir, context) {
2943
2950
  const frameworkSrcDir = path.join(PKG_ROOT, `templates/backend/server/${context.backend}`);
2944
2951
  if (await fs.pathExists(frameworkSrcDir)) await processAndCopyFiles("**/*", frameworkSrcDir, serverAppDir, context, true);
2945
2952
  }
2953
+ async function setupEnvPackage$1(projectDir, context) {
2954
+ const hasWebFrontend$1 = context.frontend.some((f) => [
2955
+ "tanstack-router",
2956
+ "react-router",
2957
+ "tanstack-start",
2958
+ "next",
2959
+ "nuxt",
2960
+ "svelte",
2961
+ "solid"
2962
+ ].includes(f));
2963
+ const hasNative = context.frontend.some((f) => [
2964
+ "native-bare",
2965
+ "native-uniwind",
2966
+ "native-unistyles"
2967
+ ].includes(f));
2968
+ if (!hasWebFrontend$1 && !hasNative && context.backend === "none") return;
2969
+ const envPackageDir = path.join(projectDir, "packages/env");
2970
+ await fs.ensureDir(envPackageDir);
2971
+ const envBaseDir = path.join(PKG_ROOT, "templates/packages/env");
2972
+ const packageJsonSrc = path.join(envBaseDir, "package.json.hbs");
2973
+ if (await fs.pathExists(packageJsonSrc)) await processAndCopyFiles("package.json.hbs", envBaseDir, envPackageDir, context);
2974
+ const tsconfigSrc = path.join(envBaseDir, "tsconfig.json.hbs");
2975
+ if (await fs.pathExists(tsconfigSrc)) await processAndCopyFiles("tsconfig.json.hbs", envBaseDir, envPackageDir, context);
2976
+ const needsServerEnv = context.backend !== "none" && context.backend !== "convex";
2977
+ if (needsServerEnv) {
2978
+ const serverSrc = path.join(envBaseDir, "src/server.ts.hbs");
2979
+ if (await fs.pathExists(serverSrc)) {
2980
+ await fs.ensureDir(path.join(envPackageDir, "src"));
2981
+ await processAndCopyFiles("src/server.ts.hbs", envBaseDir, envPackageDir, context);
2982
+ }
2983
+ }
2984
+ if (hasWebFrontend$1) {
2985
+ const webSrc = path.join(envBaseDir, "src/web.ts.hbs");
2986
+ if (await fs.pathExists(webSrc)) {
2987
+ await fs.ensureDir(path.join(envPackageDir, "src"));
2988
+ await processAndCopyFiles("src/web.ts.hbs", envBaseDir, envPackageDir, context);
2989
+ }
2990
+ }
2991
+ if (hasNative) {
2992
+ const nativeSrc = path.join(envBaseDir, "src/native.ts.hbs");
2993
+ if (await fs.pathExists(nativeSrc)) {
2994
+ await fs.ensureDir(path.join(envPackageDir, "src"));
2995
+ await processAndCopyFiles("src/native.ts.hbs", envBaseDir, envPackageDir, context);
2996
+ }
2997
+ }
2998
+ const packageJsonPath = path.join(envPackageDir, "package.json");
2999
+ if (await fs.pathExists(packageJsonPath)) {
3000
+ const packageJson = await fs.readJson(packageJsonPath);
3001
+ const exports = {};
3002
+ if (needsServerEnv) exports["./server"] = "./src/server.ts";
3003
+ if (hasWebFrontend$1) exports["./web"] = "./src/web.ts";
3004
+ if (hasNative) exports["./native"] = "./src/native.ts";
3005
+ packageJson.exports = exports;
3006
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3007
+ }
3008
+ }
2946
3009
  async function setupBackendFramework(projectDir, context) {
2947
3010
  await setupConfigPackage(projectDir, context);
3011
+ await setupEnvPackage$1(projectDir, context);
2948
3012
  if (context.backend === "none") return;
2949
3013
  if (context.backend === "convex") {
2950
3014
  await setupConvexBackend(projectDir, context);
2951
3015
  return;
2952
3016
  }
2953
3017
  if (context.backend === "self") {
2954
- await setupApiPackage(projectDir, context);
2955
- await setupDbPackage(projectDir, context);
3018
+ await setupApiPackage$1(projectDir, context);
3019
+ await setupDbPackage$1(projectDir, context);
2956
3020
  return;
2957
3021
  }
2958
3022
  await setupServerApp(projectDir, context);
2959
- await setupApiPackage(projectDir, context);
2960
- await setupDbPackage(projectDir, context);
3023
+ await setupApiPackage$1(projectDir, context);
3024
+ await setupDbPackage$1(projectDir, context);
2961
3025
  }
2962
3026
  async function setupAuthTemplate(projectDir, context) {
2963
3027
  if (!context.auth || context.auth === "none") return;
@@ -3286,35 +3350,22 @@ async function setupDockerComposeTemplates(projectDir, context) {
3286
3350
  }
3287
3351
  async function setupDeploymentTemplates(projectDir, context) {
3288
3352
  const isBackendSelf = context.backend === "self";
3289
- if (context.webDeploy === "alchemy" || context.serverDeploy === "alchemy") {
3290
- const alchemyTemplateSrc = path.join(PKG_ROOT, "templates/deploy/alchemy");
3291
- if (context.webDeploy === "alchemy" && (context.serverDeploy === "alchemy" || isBackendSelf)) {
3292
- if (await fs.pathExists(alchemyTemplateSrc)) {
3293
- const webAppDir = path.join(projectDir, "apps/web");
3294
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, isBackendSelf && await fs.pathExists(webAppDir) ? webAppDir : projectDir, context);
3295
- if (!isBackendSelf) await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3296
- }
3297
- } else {
3298
- if (context.webDeploy === "alchemy") {
3299
- const webAppDir = path.join(projectDir, "apps/web");
3300
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(webAppDir)) {
3301
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, webAppDir, context);
3302
- if (!isBackendSelf) await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3303
- }
3304
- }
3305
- if (context.serverDeploy === "alchemy" && !isBackendSelf) {
3306
- const serverAppDir = path.join(projectDir, "apps/server");
3307
- if (await fs.pathExists(alchemyTemplateSrc) && await fs.pathExists(serverAppDir)) {
3308
- await processAndCopyFiles("alchemy.run.ts.hbs", alchemyTemplateSrc, serverAppDir, context);
3309
- const envDtsPath = path.join(serverAppDir, "env.d.ts");
3310
- await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
3311
- await setupEnvDtsImport(envDtsPath, projectDir, context);
3312
- await addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc);
3313
- }
3314
- }
3353
+ if (context.webDeploy === "cloudflare" || context.serverDeploy === "cloudflare") {
3354
+ const infraTemplateSrc = path.join(PKG_ROOT, "templates/packages/infra");
3355
+ const infraDir = path.join(projectDir, "packages/infra");
3356
+ if (await fs.pathExists(infraTemplateSrc)) {
3357
+ await fs.ensureDir(infraDir);
3358
+ await processAndCopyFiles("package.json.hbs", infraTemplateSrc, infraDir, context);
3359
+ await processAndCopyFiles("alchemy.run.ts.hbs", infraTemplateSrc, infraDir, context);
3360
+ }
3361
+ if (!isBackendSelf) {
3362
+ const envTemplateSrc = path.join(PKG_ROOT, "templates/packages/env");
3363
+ const envDir = path.join(projectDir, "packages/env");
3364
+ const envDtsTemplatePath = path.join(envTemplateSrc, "env.d.ts.hbs");
3365
+ if (await fs.pathExists(envDtsTemplatePath)) await processTemplate(envDtsTemplatePath, path.join(envDir, "env.d.ts"), context);
3315
3366
  }
3316
3367
  }
3317
- if (context.webDeploy !== "none" && context.webDeploy !== "alchemy") {
3368
+ if (context.webDeploy !== "none" && context.webDeploy !== "cloudflare") {
3318
3369
  const webAppDir = path.join(projectDir, "apps/web");
3319
3370
  if (await fs.pathExists(webAppDir)) {
3320
3371
  const frontends = context.frontend;
@@ -3333,7 +3384,7 @@ async function setupDeploymentTemplates(projectDir, context) {
3333
3384
  }
3334
3385
  }
3335
3386
  }
3336
- if (context.serverDeploy !== "none" && context.serverDeploy !== "alchemy" && !isBackendSelf) {
3387
+ if (context.serverDeploy !== "none" && context.serverDeploy !== "cloudflare" && !isBackendSelf) {
3337
3388
  const serverAppDir = path.join(projectDir, "apps/server");
3338
3389
  if (await fs.pathExists(serverAppDir)) {
3339
3390
  const deployTemplateSrc = path.join(PKG_ROOT, `templates/deploy/${context.serverDeploy}/server`);
@@ -3341,26 +3392,6 @@ async function setupDeploymentTemplates(projectDir, context) {
3341
3392
  }
3342
3393
  }
3343
3394
  }
3344
- async function addEnvDtsToPackages(projectDir, context, alchemyTemplateSrc) {
3345
- for (const packageName of [
3346
- "packages/api",
3347
- "packages/auth",
3348
- "packages/db"
3349
- ]) {
3350
- const packageDir = path.join(projectDir, packageName);
3351
- if (await fs.pathExists(packageDir)) {
3352
- const envDtsPath = path.join(packageDir, "env.d.ts");
3353
- await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
3354
- await setupEnvDtsImport(envDtsPath, projectDir, context);
3355
- }
3356
- }
3357
- const serverAppDir = path.join(projectDir, "apps/server");
3358
- if (await fs.pathExists(serverAppDir)) {
3359
- const envDtsPath = path.join(serverAppDir, "env.d.ts");
3360
- await processTemplate(path.join(alchemyTemplateSrc, "env.d.ts.hbs"), envDtsPath, context);
3361
- await setupEnvDtsImport(envDtsPath, projectDir, context);
3362
- }
3363
- }
3364
3395
 
3365
3396
  //#endregion
3366
3397
  //#region src/helpers/core/add-addons.ts
@@ -3409,50 +3440,9 @@ async function addAddonsToProject(input) {
3409
3440
  }
3410
3441
  }
3411
3442
 
3412
- //#endregion
3413
- //#region src/helpers/deployment/server-deploy-setup.ts
3414
- async function setupServerDeploy(config) {
3415
- const { serverDeploy, webDeploy, projectDir } = config;
3416
- if (serverDeploy === "none") return;
3417
- if (serverDeploy === "alchemy" && webDeploy === "alchemy") return;
3418
- const serverDir = path.join(projectDir, "apps/server");
3419
- if (!await fs.pathExists(serverDir)) return;
3420
- if (serverDeploy === "alchemy") await setupAlchemyServerDeploy(serverDir, projectDir);
3421
- }
3422
- async function setupAlchemyServerDeploy(serverDir, projectDir) {
3423
- if (!await fs.pathExists(serverDir)) return;
3424
- await addPackageDependency({
3425
- devDependencies: [
3426
- "alchemy",
3427
- "wrangler",
3428
- "@types/node",
3429
- "@cloudflare/workers-types"
3430
- ],
3431
- projectDir: serverDir
3432
- });
3433
- if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3434
- const packageJsonPath = path.join(serverDir, "package.json");
3435
- if (await fs.pathExists(packageJsonPath)) {
3436
- const packageJson = await fs.readJson(packageJsonPath);
3437
- packageJson.scripts = {
3438
- ...packageJson.scripts,
3439
- dev: "alchemy dev",
3440
- deploy: "alchemy deploy",
3441
- destroy: "alchemy destroy"
3442
- };
3443
- await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
3444
- }
3445
- }
3446
- async function addAlchemyPackagesDependencies$1(projectDir) {
3447
- await addPackageDependency({
3448
- devDependencies: ["@cloudflare/workers-types"],
3449
- projectDir
3450
- });
3451
- }
3452
-
3453
3443
  //#endregion
3454
3444
  //#region src/helpers/deployment/alchemy/alchemy-next-setup.ts
3455
- async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3445
+ async function setupNextAlchemyDeploy(projectDir, _packageManager, _options) {
3456
3446
  const webAppDir = path.join(projectDir, "apps/web");
3457
3447
  if (!await fs.pathExists(webAppDir)) return;
3458
3448
  await addPackageDependency({
@@ -3464,17 +3454,6 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3464
3454
  ],
3465
3455
  projectDir: webAppDir
3466
3456
  });
3467
- const pkgPath = path.join(webAppDir, "package.json");
3468
- if (await fs.pathExists(pkgPath)) {
3469
- const pkg = await fs.readJson(pkgPath);
3470
- if (!options?.skipAppScripts) pkg.scripts = {
3471
- ...pkg.scripts,
3472
- dev: "alchemy dev",
3473
- deploy: "alchemy deploy",
3474
- destroy: "alchemy destroy"
3475
- };
3476
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3477
- }
3478
3457
  const openNextConfigPath = path.join(webAppDir, "open-next.config.ts");
3479
3458
  await fs.writeFile(openNextConfigPath, `import { defineCloudflareConfig } from "@opennextjs/cloudflare";
3480
3459
 
@@ -3488,7 +3467,7 @@ export default defineCloudflareConfig({});
3488
3467
 
3489
3468
  //#endregion
3490
3469
  //#region src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts
3491
- async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3470
+ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, _options) {
3492
3471
  const webAppDir = path.join(projectDir, "apps/web");
3493
3472
  if (!await fs.pathExists(webAppDir)) return;
3494
3473
  await addPackageDependency({
@@ -3499,17 +3478,6 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3499
3478
  ],
3500
3479
  projectDir: webAppDir
3501
3480
  });
3502
- const pkgPath = path.join(webAppDir, "package.json");
3503
- if (await fs.pathExists(pkgPath)) {
3504
- const pkg = await fs.readJson(pkgPath);
3505
- if (!options?.skipAppScripts) pkg.scripts = {
3506
- ...pkg.scripts,
3507
- dev: "alchemy dev",
3508
- deploy: "alchemy deploy",
3509
- destroy: "alchemy destroy"
3510
- };
3511
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3512
- }
3513
3481
  const nuxtConfigPath = path.join(webAppDir, "nuxt.config.ts");
3514
3482
  if (!await fs.pathExists(nuxtConfigPath)) return;
3515
3483
  try {
@@ -3554,68 +3522,35 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3554
3522
 
3555
3523
  //#endregion
3556
3524
  //#region src/helpers/deployment/alchemy/alchemy-react-router-setup.ts
3557
- async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, options) {
3525
+ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, _options) {
3558
3526
  const webAppDir = path.join(projectDir, "apps/web");
3559
3527
  if (!await fs.pathExists(webAppDir)) return;
3560
3528
  await addPackageDependency({
3561
3529
  devDependencies: ["alchemy"],
3562
3530
  projectDir: webAppDir
3563
3531
  });
3564
- const pkgPath = path.join(webAppDir, "package.json");
3565
- if (await fs.pathExists(pkgPath)) {
3566
- const pkg = await fs.readJson(pkgPath);
3567
- if (!options?.skipAppScripts) pkg.scripts = {
3568
- ...pkg.scripts,
3569
- dev: "alchemy dev",
3570
- deploy: "alchemy deploy",
3571
- destroy: "alchemy destroy"
3572
- };
3573
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3574
- }
3575
3532
  }
3576
3533
 
3577
3534
  //#endregion
3578
3535
  //#region src/helpers/deployment/alchemy/alchemy-solid-setup.ts
3579
- async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3536
+ async function setupSolidAlchemyDeploy(projectDir, _packageManager, _options) {
3580
3537
  const webAppDir = path.join(projectDir, "apps/web");
3581
3538
  if (!await fs.pathExists(webAppDir)) return;
3582
3539
  await addPackageDependency({
3583
3540
  devDependencies: ["alchemy"],
3584
3541
  projectDir: webAppDir
3585
3542
  });
3586
- const pkgPath = path.join(webAppDir, "package.json");
3587
- if (await fs.pathExists(pkgPath)) {
3588
- const pkg = await fs.readJson(pkgPath);
3589
- if (!options?.skipAppScripts) pkg.scripts = {
3590
- ...pkg.scripts,
3591
- dev: "alchemy dev",
3592
- deploy: "alchemy deploy",
3593
- destroy: "alchemy destroy"
3594
- };
3595
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3596
- }
3597
3543
  }
3598
3544
 
3599
3545
  //#endregion
3600
3546
  //#region src/helpers/deployment/alchemy/alchemy-svelte-setup.ts
3601
- async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3547
+ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, _options) {
3602
3548
  const webAppDir = path.join(projectDir, "apps/web");
3603
3549
  if (!await fs.pathExists(webAppDir)) return;
3604
3550
  await addPackageDependency({
3605
3551
  devDependencies: ["alchemy", "@sveltejs/adapter-cloudflare"],
3606
3552
  projectDir: webAppDir
3607
3553
  });
3608
- const pkgPath = path.join(webAppDir, "package.json");
3609
- if (await fs.pathExists(pkgPath)) {
3610
- const pkg = await fs.readJson(pkgPath);
3611
- if (!options?.skipAppScripts) pkg.scripts = {
3612
- ...pkg.scripts,
3613
- dev: "alchemy dev",
3614
- deploy: "alchemy deploy",
3615
- destroy: "alchemy destroy"
3616
- };
3617
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3618
- }
3619
3554
  const svelteConfigPath = path.join(webAppDir, "svelte.config.js");
3620
3555
  if (!await fs.pathExists(svelteConfigPath)) return;
3621
3556
  try {
@@ -3664,46 +3599,24 @@ function updateAdapterInConfig(configObject) {
3664
3599
 
3665
3600
  //#endregion
3666
3601
  //#region src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts
3667
- async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, options) {
3602
+ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, _options) {
3668
3603
  const webAppDir = path.join(projectDir, "apps/web");
3669
3604
  if (!await fs.pathExists(webAppDir)) return;
3670
3605
  await addPackageDependency({
3671
3606
  devDependencies: ["alchemy"],
3672
3607
  projectDir: webAppDir
3673
3608
  });
3674
- const pkgPath = path.join(webAppDir, "package.json");
3675
- if (await fs.pathExists(pkgPath)) {
3676
- const pkg = await fs.readJson(pkgPath);
3677
- if (!options?.skipAppScripts) pkg.scripts = {
3678
- ...pkg.scripts,
3679
- dev: "alchemy dev",
3680
- deploy: "alchemy deploy",
3681
- destroy: "alchemy destroy"
3682
- };
3683
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3684
- }
3685
3609
  }
3686
3610
 
3687
3611
  //#endregion
3688
3612
  //#region src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts
3689
- async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, options) {
3613
+ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, _options) {
3690
3614
  const webAppDir = path.join(projectDir, "apps/web");
3691
3615
  if (!await fs.pathExists(webAppDir)) return;
3692
3616
  await addPackageDependency({
3693
3617
  devDependencies: ["alchemy", "@cloudflare/vite-plugin"],
3694
3618
  projectDir: webAppDir
3695
3619
  });
3696
- const pkgPath = path.join(webAppDir, "package.json");
3697
- if (await fs.pathExists(pkgPath)) {
3698
- const pkg = await fs.readJson(pkgPath);
3699
- if (!options?.skipAppScripts) pkg.scripts = {
3700
- ...pkg.scripts,
3701
- dev: "alchemy dev",
3702
- deploy: "alchemy deploy",
3703
- destroy: "alchemy destroy"
3704
- };
3705
- await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3706
- }
3707
3620
  const viteConfigPath = path.join(webAppDir, "vite.config.ts");
3708
3621
  if (await fs.pathExists(viteConfigPath)) try {
3709
3622
  const project = new Project({ manipulationSettings: {
@@ -3744,22 +3657,16 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3744
3657
 
3745
3658
  //#endregion
3746
3659
  //#region src/helpers/deployment/alchemy/alchemy-combined-setup.ts
3747
- async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3748
- await addPackageDependency({
3749
- devDependencies: ["alchemy"],
3750
- projectDir
3751
- });
3752
- const rootPkgPath = path.join(projectDir, "package.json");
3753
- if (await fs.pathExists(rootPkgPath)) {
3754
- const pkg = await fs.readJson(rootPkgPath);
3755
- pkg.scripts = {
3756
- ...pkg.scripts,
3757
- deploy: "alchemy deploy",
3758
- destroy: "alchemy destroy",
3759
- dev: "alchemy dev"
3760
- };
3761
- await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3660
+ function getInfraFilter(packageManager, hasTurborepo, infraWorkspace) {
3661
+ if (hasTurborepo) return (script) => `turbo -F ${infraWorkspace} ${script}`;
3662
+ switch (packageManager) {
3663
+ case "pnpm": return (script) => `pnpm --filter ${infraWorkspace} ${script}`;
3664
+ case "npm": return (script) => `npm run ${script} --workspace ${infraWorkspace}`;
3665
+ case "bun": return (script) => `bun run --filter ${infraWorkspace} ${script}`;
3762
3666
  }
3667
+ }
3668
+ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3669
+ await setupInfraScripts(projectDir, packageManager, config);
3763
3670
  const serverDir = path.join(projectDir, "apps/server");
3764
3671
  if (await fs.pathExists(serverDir)) await setupAlchemyServerDeploy(serverDir, projectDir);
3765
3672
  const frontend = config.frontend;
@@ -3778,6 +3685,77 @@ async function setupCombinedAlchemyDeploy(projectDir, packageManager, config) {
3778
3685
  else if (isReactRouter) await setupReactRouterAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3779
3686
  else if (isSolid) await setupSolidAlchemyDeploy(projectDir, packageManager, { skipAppScripts: true });
3780
3687
  }
3688
+ async function setupInfraScripts(projectDir, packageManager, config) {
3689
+ const projectName = config.projectName;
3690
+ const hasTurborepo = config.addons.includes("turborepo");
3691
+ const infraWorkspace = `@${projectName}/infra`;
3692
+ const rootPkgPath = path.join(projectDir, "package.json");
3693
+ if (await fs.pathExists(rootPkgPath)) {
3694
+ const pkg = await fs.readJson(rootPkgPath);
3695
+ const filter = getInfraFilter(packageManager, hasTurborepo, infraWorkspace);
3696
+ pkg.scripts = {
3697
+ ...pkg.scripts,
3698
+ deploy: filter("deploy"),
3699
+ destroy: filter("destroy")
3700
+ };
3701
+ await fs.writeJson(rootPkgPath, pkg, { spaces: 2 });
3702
+ }
3703
+ if (config.serverDeploy === "cloudflare") {
3704
+ const serverPkgPath = path.join(projectDir, "apps/server/package.json");
3705
+ if (await fs.pathExists(serverPkgPath)) {
3706
+ const serverPkg = await fs.readJson(serverPkgPath);
3707
+ if (serverPkg.scripts?.dev) {
3708
+ serverPkg.scripts["dev:bare"] = serverPkg.scripts.dev;
3709
+ delete serverPkg.scripts.dev;
3710
+ await fs.writeJson(serverPkgPath, serverPkg, { spaces: 2 });
3711
+ }
3712
+ }
3713
+ }
3714
+ if (config.webDeploy === "cloudflare") {
3715
+ const webPkgPath = path.join(projectDir, "apps/web/package.json");
3716
+ if (await fs.pathExists(webPkgPath)) {
3717
+ const webPkg = await fs.readJson(webPkgPath);
3718
+ if (webPkg.scripts?.dev) {
3719
+ webPkg.scripts["dev:bare"] = webPkg.scripts.dev;
3720
+ delete webPkg.scripts.dev;
3721
+ await fs.writeJson(webPkgPath, webPkg, { spaces: 2 });
3722
+ }
3723
+ }
3724
+ }
3725
+ }
3726
+
3727
+ //#endregion
3728
+ //#region src/helpers/deployment/server-deploy-setup.ts
3729
+ async function setupServerDeploy(config) {
3730
+ const { serverDeploy, webDeploy, projectDir, packageManager } = config;
3731
+ if (serverDeploy === "none") return;
3732
+ if (serverDeploy === "cloudflare" && webDeploy === "cloudflare") return;
3733
+ const serverDir = path.join(projectDir, "apps/server");
3734
+ if (!await fs.pathExists(serverDir)) return;
3735
+ if (serverDeploy === "cloudflare") {
3736
+ await setupInfraScripts(projectDir, packageManager, config);
3737
+ await setupAlchemyServerDeploy(serverDir, projectDir);
3738
+ }
3739
+ }
3740
+ async function setupAlchemyServerDeploy(serverDir, projectDir) {
3741
+ if (!await fs.pathExists(serverDir)) return;
3742
+ await addPackageDependency({
3743
+ devDependencies: [
3744
+ "alchemy",
3745
+ "wrangler",
3746
+ "@types/node",
3747
+ "@cloudflare/workers-types"
3748
+ ],
3749
+ projectDir: serverDir
3750
+ });
3751
+ if (projectDir) await addAlchemyPackagesDependencies$1(projectDir);
3752
+ }
3753
+ async function addAlchemyPackagesDependencies$1(projectDir) {
3754
+ await addPackageDependency({
3755
+ devDependencies: ["@cloudflare/workers-types"],
3756
+ projectDir
3757
+ });
3758
+ }
3781
3759
 
3782
3760
  //#endregion
3783
3761
  //#region src/helpers/deployment/web-deploy-setup.ts
@@ -3785,12 +3763,13 @@ async function setupWebDeploy(config) {
3785
3763
  const { webDeploy, serverDeploy, frontend, projectDir } = config;
3786
3764
  const { packageManager } = config;
3787
3765
  if (webDeploy === "none") return;
3788
- if (webDeploy !== "alchemy") return;
3789
- if (webDeploy === "alchemy" && serverDeploy === "alchemy") {
3766
+ if (webDeploy !== "cloudflare") return;
3767
+ if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") {
3790
3768
  await setupCombinedAlchemyDeploy(projectDir, packageManager, config);
3791
3769
  await addAlchemyPackagesDependencies(projectDir);
3792
3770
  return;
3793
3771
  }
3772
+ await setupInfraScripts(projectDir, packageManager, config);
3794
3773
  const isNext = frontend.includes("next");
3795
3774
  const isNuxt = frontend.includes("nuxt");
3796
3775
  const isSvelte = frontend.includes("svelte");
@@ -3879,7 +3858,9 @@ async function setupCatalogs(projectDir, options) {
3879
3858
  "packages/db",
3880
3859
  "packages/auth",
3881
3860
  "packages/backend",
3882
- "packages/config"
3861
+ "packages/config",
3862
+ "packages/env",
3863
+ "packages/infra"
3883
3864
  ];
3884
3865
  const packagesInfo = [];
3885
3866
  for (const pkgPath of packagePaths) {
@@ -4171,7 +4152,10 @@ function getConvexDependencies(frontend) {
4171
4152
  web: { dependencies: ["convex"] },
4172
4153
  native: { dependencies: ["convex"] }
4173
4154
  };
4174
- if (frontend.includes("tanstack-start")) deps.web.dependencies.push("@convex-dev/react-query");
4155
+ if (frontend.includes("tanstack-start")) {
4156
+ deps.web.dependencies.push("@convex-dev/react-query");
4157
+ deps.web.dependencies.push("@tanstack/react-router-ssr-query");
4158
+ }
4175
4159
  if (frontend.includes("svelte")) deps.web.dependencies.push("convex-svelte");
4176
4160
  if (frontend.includes("nuxt")) deps.web.dependencies.push("convex-nuxt", "convex-vue");
4177
4161
  return deps;
@@ -4294,7 +4278,7 @@ async function setupBackendDependencies(config) {
4294
4278
  //#region src/utils/better-auth-plugin-setup.ts
4295
4279
  async function setupBetterAuthPlugins(projectDir, config) {
4296
4280
  const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
4297
- const authIndexFile = tsProject$1.addSourceFileAtPath(authIndexPath);
4281
+ const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
4298
4282
  if (!authIndexFile) return;
4299
4283
  const pluginsToAdd = [];
4300
4284
  const importsToAdd = [];
@@ -4367,12 +4351,12 @@ async function setupAuth(config) {
4367
4351
  if (convexBackendDirExists) {
4368
4352
  await addPackageDependency({
4369
4353
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4370
- customDependencies: { "better-auth": "1.4.7" },
4354
+ customDependencies: { "better-auth": "1.4.9" },
4371
4355
  projectDir: convexBackendDir
4372
4356
  });
4373
4357
  if (hasNativeForBA) await addPackageDependency({
4374
4358
  dependencies: ["@better-auth/expo"],
4375
- customDependencies: { "@better-auth/expo": "1.4.7" },
4359
+ customDependencies: { "@better-auth/expo": "1.4.9" },
4376
4360
  projectDir: convexBackendDir
4377
4361
  });
4378
4362
  }
@@ -4382,17 +4366,17 @@ async function setupAuth(config) {
4382
4366
  const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
4383
4367
  if (hasNextJs) await addPackageDependency({
4384
4368
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4385
- customDependencies: { "better-auth": "1.4.7" },
4369
+ customDependencies: { "better-auth": "1.4.9" },
4386
4370
  projectDir: clientDir
4387
4371
  });
4388
4372
  else if (hasTanStackStart) await addPackageDependency({
4389
4373
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4390
- customDependencies: { "better-auth": "1.4.7" },
4374
+ customDependencies: { "better-auth": "1.4.9" },
4391
4375
  projectDir: clientDir
4392
4376
  });
4393
4377
  else if (hasViteReactOther) await addPackageDependency({
4394
4378
  dependencies: ["better-auth", "@convex-dev/better-auth"],
4395
- customDependencies: { "better-auth": "1.4.7" },
4379
+ customDependencies: { "better-auth": "1.4.9" },
4396
4380
  projectDir: clientDir
4397
4381
  });
4398
4382
  }
@@ -4406,8 +4390,8 @@ async function setupAuth(config) {
4406
4390
  "@convex-dev/better-auth"
4407
4391
  ],
4408
4392
  customDependencies: {
4409
- "better-auth": "1.4.7",
4410
- "@better-auth/expo": "1.4.7"
4393
+ "better-auth": "1.4.9",
4394
+ "@better-auth/expo": "1.4.9"
4411
4395
  },
4412
4396
  projectDir: nativeDir
4413
4397
  });
@@ -4566,11 +4550,6 @@ async function setupEnvironmentVariables(config) {
4566
4550
  value: backend === "convex" ? "https://<YOUR_CONVEX_URL>" : baseVar.value,
4567
4551
  condition: backend === "convex" ? true : baseVar.write
4568
4552
  }];
4569
- if (hasNextJs) clientVars.push({
4570
- key: "PORT",
4571
- value: "3001",
4572
- condition: true
4573
- });
4574
4553
  if (backend === "convex" && auth === "clerk") {
4575
4554
  if (hasNextJs) clientVars.push({
4576
4555
  key: "NEXT_PUBLIC_CLERK_FRONTEND_API_URL",
@@ -4706,7 +4685,7 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
4706
4685
  databaseUrl = "mongodb://localhost:27017/mydatabase";
4707
4686
  break;
4708
4687
  case "sqlite":
4709
- if (config.runtime === "workers" || webDeploy === "alchemy" || serverDeploy === "alchemy") databaseUrl = "http://127.0.0.1:8080";
4688
+ if (config.runtime === "workers" || webDeploy === "cloudflare" || serverDeploy === "cloudflare") databaseUrl = "http://127.0.0.1:8080";
4710
4689
  else {
4711
4690
  const dbAppDir = backend === "self" ? "apps/web" : "apps/server";
4712
4691
  databaseUrl = `file:${path.join(config.projectDir, dbAppDir, "local.db")}`;
@@ -4754,33 +4733,13 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
4754
4733
  const webDir = path.join(projectDir, "apps/web");
4755
4734
  if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverVars);
4756
4735
  } else if (await fs.pathExists(serverDir)) await addEnvVariablesToFile(path.join(serverDir, ".env"), serverVars);
4757
- const isUnifiedAlchemy = webDeploy === "alchemy" && serverDeploy === "alchemy";
4758
- const isIndividualAlchemy = webDeploy === "alchemy" || serverDeploy === "alchemy";
4759
- if (isUnifiedAlchemy) await addEnvVariablesToFile(path.join(projectDir, ".env"), [{
4760
- key: "ALCHEMY_PASSWORD",
4761
- value: "please-change-this",
4762
- condition: true
4763
- }]);
4764
- else if (isIndividualAlchemy) {
4765
- if (webDeploy === "alchemy") {
4766
- const webDir = path.join(projectDir, "apps/web");
4767
- if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), [{
4768
- key: "ALCHEMY_PASSWORD",
4769
- value: "please-change-this",
4770
- condition: true
4771
- }]);
4772
- }
4773
- if (serverDeploy === "alchemy") {
4774
- const serverAlchemyVars = [{
4775
- key: "ALCHEMY_PASSWORD",
4776
- value: "please-change-this",
4777
- condition: true
4778
- }];
4779
- if (backend === "self") {
4780
- const webDir = path.join(projectDir, "apps/web");
4781
- if (await fs.pathExists(webDir)) await addEnvVariablesToFile(path.join(webDir, ".env"), serverAlchemyVars);
4782
- } else await addEnvVariablesToFile(path.join(serverDir, ".env"), serverAlchemyVars);
4783
- }
4736
+ if (webDeploy === "cloudflare" && serverDeploy === "cloudflare" || webDeploy === "cloudflare" || serverDeploy === "cloudflare") {
4737
+ const infraDir = path.join(projectDir, "packages/infra");
4738
+ if (await fs.pathExists(infraDir)) await addEnvVariablesToFile(path.join(infraDir, ".env"), [{
4739
+ key: "ALCHEMY_PASSWORD",
4740
+ value: "please-change-this",
4741
+ condition: true
4742
+ }]);
4784
4743
  }
4785
4744
  }
4786
4745
 
@@ -4788,7 +4747,7 @@ ${hasWebFrontend$1 ? "# npx convex env set SITE_URL http://localhost:3001\n" : "
4788
4747
  //#region src/helpers/database-providers/d1-setup.ts
4789
4748
  async function setupCloudflareD1(config) {
4790
4749
  const { projectDir, serverDeploy, orm, backend } = config;
4791
- if (serverDeploy === "alchemy" && orm === "prisma") {
4750
+ if (serverDeploy === "cloudflare" && orm === "prisma") {
4792
4751
  const targetApp2 = backend === "self" ? "apps/web" : "apps/server";
4793
4752
  await addEnvVariablesToFile(path.join(projectDir, targetApp2, ".env"), [{
4794
4753
  key: "DATABASE_URL",
@@ -4834,8 +4793,8 @@ function getDatabaseUrl(database, projectName) {
4834
4793
  //#region src/utils/command-exists.ts
4835
4794
  async function commandExists(command) {
4836
4795
  try {
4837
- if (process.platform === "win32") return (await execa("where", [command])).exitCode === 0;
4838
- return (await execa("which", [command])).exitCode === 0;
4796
+ if (process.platform === "win32") return (await $({ reject: false })`where ${command}`).exitCode === 0;
4797
+ return (await $({ reject: false })`which ${command}`).exitCode === 0;
4839
4798
  } catch {
4840
4799
  return false;
4841
4800
  }
@@ -4862,11 +4821,10 @@ async function initMongoDBAtlas(serverDir) {
4862
4821
  return null;
4863
4822
  }
4864
4823
  log.info("Running MongoDB Atlas setup...");
4865
- await execa("atlas", ["deployments", "setup"], {
4824
+ await $({
4866
4825
  cwd: serverDir,
4867
- shell: true,
4868
4826
  stdio: "inherit"
4869
- });
4827
+ })`atlas deployments setup`;
4870
4828
  log.success("MongoDB Atlas deployment ready");
4871
4829
  const connectionString = await text({
4872
4830
  message: "Enter your MongoDB connection string:",
@@ -5004,9 +4962,9 @@ const NEON_REGIONS = [
5004
4962
  async function executeNeonCommand(packageManager, commandArgsString, spinnerText) {
5005
4963
  const s = spinner();
5006
4964
  try {
5007
- const fullCommand = getPackageExecutionCommand(packageManager, commandArgsString);
4965
+ const args = getPackageExecutionArgs(packageManager, commandArgsString);
5008
4966
  if (spinnerText) s.start(spinnerText);
5009
- const result = await execa(fullCommand, { shell: true });
4967
+ const result = await $`${args}`;
5010
4968
  if (spinnerText) s.stop(pc.green(spinnerText.replace("...", "").replace("ing ", "ed ").trim()));
5011
4969
  return result;
5012
4970
  } catch (error) {
@@ -5051,10 +5009,8 @@ async function setupWithNeonDb(projectDir, packageManager, backend) {
5051
5009
  const targetApp = backend === "self" ? "apps/web" : "apps/server";
5052
5010
  const targetDir = path.join(projectDir, targetApp);
5053
5011
  await fs.ensureDir(targetDir);
5054
- await execa(getPackageExecutionCommand(packageManager, "get-db@latest --yes"), {
5055
- shell: true,
5056
- cwd: targetDir
5057
- });
5012
+ const packageArgs = getPackageExecutionArgs(packageManager, "get-db@latest --yes");
5013
+ await $({ cwd: targetDir })`${packageArgs}`;
5058
5014
  s.stop(pc.green("Neon database created successfully!"));
5059
5015
  return true;
5060
5016
  } catch (error) {
@@ -5240,13 +5196,10 @@ async function setupWithCreateDb(serverDir, packageManager) {
5240
5196
  initialValue: "ap-southeast-1"
5241
5197
  });
5242
5198
  if (isCancel(selectedRegion)) return null;
5243
- const createDbCommand = getPackageExecutionCommand(packageManager, `create-db@latest --json --region ${selectedRegion}`);
5199
+ const createDbArgs = getPackageExecutionArgs(packageManager, `create-db@latest --json --region ${selectedRegion}`);
5244
5200
  const s = spinner();
5245
5201
  s.start("Creating Prisma Postgres database...");
5246
- const { stdout } = await execa(createDbCommand, {
5247
- cwd: serverDir,
5248
- shell: true
5249
- });
5202
+ const { stdout } = await $({ cwd: serverDir })`${createDbArgs}`;
5250
5203
  s.stop("Database created successfully!");
5251
5204
  let createDbResponse;
5252
5205
  try {
@@ -5373,11 +5326,11 @@ function extractDbUrl(output) {
5373
5326
  async function initializeSupabase(serverDir, packageManager) {
5374
5327
  log.info("Initializing Supabase project...");
5375
5328
  try {
5376
- await execa(getPackageExecutionCommand(packageManager, "supabase init"), {
5329
+ const supabaseInitArgs = getPackageExecutionArgs(packageManager, "supabase init");
5330
+ await $({
5377
5331
  cwd: serverDir,
5378
- stdio: "inherit",
5379
- shell: true
5380
- });
5332
+ stdio: "inherit"
5333
+ })`${supabaseInitArgs}`;
5381
5334
  log.success("Supabase project initialized");
5382
5335
  return true;
5383
5336
  } catch (error) {
@@ -5393,12 +5346,9 @@ async function initializeSupabase(serverDir, packageManager) {
5393
5346
  }
5394
5347
  async function startSupabase(serverDir, packageManager) {
5395
5348
  log.info("Starting Supabase services (this may take a moment)...");
5396
- const supabaseStartCommand = getPackageExecutionCommand(packageManager, "supabase start");
5349
+ const supabaseStartArgs = getPackageExecutionArgs(packageManager, "supabase start");
5397
5350
  try {
5398
- const subprocess = execa(supabaseStartCommand, {
5399
- cwd: serverDir,
5400
- shell: true
5401
- });
5351
+ const subprocess = execa(supabaseStartArgs[0], supabaseStartArgs.slice(1), { cwd: serverDir });
5402
5352
  let stdoutData = "";
5403
5353
  if (subprocess.stdout) subprocess.stdout.on("data", (data) => {
5404
5354
  const text$1 = data.toString();
@@ -5944,7 +5894,7 @@ ${generateProjectStructure(projectName, frontend, backend, addons, isConvex, api
5944
5894
 
5945
5895
  ## Available Scripts
5946
5896
 
5947
- ${generateScriptsList(packageManagerRunCmd, database, orm, auth, hasNative, addons, backend)}
5897
+ ${generateScriptsList(packageManagerRunCmd, database, orm, auth, hasNative, addons, backend, options.dbSetup)}
5948
5898
  `;
5949
5899
  }
5950
5900
  function generateStackDescription(frontend, backend, api, isConvex) {
@@ -6099,15 +6049,13 @@ function generateDatabaseSetup(database, _auth, packageManagerRunCmd, orm, dbSet
6099
6049
  if (database === "none") return "";
6100
6050
  const isBackendSelf = backend === "self";
6101
6051
  const envPath = isBackendSelf ? "apps/web/.env" : "apps/server/.env";
6102
- const dbLocalPath = "packages/db";
6103
6052
  let setup = "## Database Setup\n\n";
6104
6053
  if (database === "sqlite") setup += `This project uses SQLite${orm === "drizzle" ? " with Drizzle ORM" : orm === "prisma" ? " with Prisma" : ` with ${orm}`}.
6105
6054
 
6106
- 1. Start the local SQLite database:
6055
+ 1. Start the local SQLite database (optional):
6107
6056
  ${dbSetup === "d1" ? "D1 local development and migrations are handled automatically by Alchemy during dev and deploy." : `\`\`\`bash
6108
- cd ${dbLocalPath} && ${packageManagerRunCmd} db:local
6109
- \`\`\`
6110
- `}
6057
+ ${packageManagerRunCmd} db:local
6058
+ \`\`\``}
6111
6059
 
6112
6060
  2. Update your \`.env\` file in the \`${isBackendSelf ? "apps/web" : "apps/server"}\` directory with the appropriate connection details if needed.
6113
6061
  `;
@@ -6140,7 +6088,7 @@ ${packageManagerRunCmd} db:push
6140
6088
  `;
6141
6089
  return setup;
6142
6090
  }
6143
- function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend) {
6091
+ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNative, addons, backend, dbSetup) {
6144
6092
  const isConvex = backend === "convex";
6145
6093
  const isBackendNone = backend === "none";
6146
6094
  const isBackendSelf = backend === "self";
@@ -6160,8 +6108,8 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
6160
6108
  scripts += `
6161
6109
  - \`${packageManagerRunCmd} db:push\`: Push schema changes to database
6162
6110
  - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`;
6163
- if (database === "sqlite" && orm === "drizzle") scripts += `
6164
- - \`cd packages/db && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6111
+ if (database === "sqlite" && dbSetup !== "d1") scripts += `
6112
+ - \`${packageManagerRunCmd} db:local\`: Start the local SQLite database`;
6165
6113
  }
6166
6114
  if (addons.includes("biome")) scripts += `
6167
6115
  - \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
@@ -6198,15 +6146,35 @@ BETTER_AUTH_URL={your-production-server-domain}
6198
6146
  }
6199
6147
  function generateDeploymentCommands(packageManagerRunCmd, webDeploy, serverDeploy) {
6200
6148
  const lines = [];
6201
- if (webDeploy === "alchemy" || serverDeploy === "alchemy") {
6202
- lines.push("## Deployment (Alchemy)");
6203
- if (webDeploy === "alchemy" && serverDeploy !== "alchemy") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
6204
- if (serverDeploy === "alchemy" && webDeploy !== "alchemy") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
6205
- if (webDeploy === "alchemy" && serverDeploy === "alchemy") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
6149
+ if (webDeploy === "cloudflare" || serverDeploy === "cloudflare") {
6150
+ lines.push("## Deployment (Cloudflare via Alchemy)");
6151
+ if (webDeploy === "cloudflare" && serverDeploy !== "cloudflare") lines.push(`- Web dev: cd apps/web && ${packageManagerRunCmd} dev`, `- Web deploy: cd apps/web && ${packageManagerRunCmd} deploy`, `- Web destroy: cd apps/web && ${packageManagerRunCmd} destroy`);
6152
+ if (serverDeploy === "cloudflare" && webDeploy !== "cloudflare") lines.push(`- Server dev: cd apps/server && ${packageManagerRunCmd} dev`, `- Server deploy: cd apps/server && ${packageManagerRunCmd} deploy`, `- Server destroy: cd apps/server && ${packageManagerRunCmd} destroy`);
6153
+ if (webDeploy === "cloudflare" && serverDeploy === "cloudflare") lines.push(`- Dev: ${packageManagerRunCmd} dev`, `- Deploy: ${packageManagerRunCmd} deploy`, `- Destroy: ${packageManagerRunCmd} destroy`);
6206
6154
  }
6207
6155
  return lines.length ? `\n${lines.join("\n")}\n` : "";
6208
6156
  }
6209
6157
 
6158
+ //#endregion
6159
+ //#region src/helpers/core/env-package-setup.ts
6160
+ async function setupEnvPackageDependencies(projectDir, options) {
6161
+ const envDir = path.join(projectDir, "packages/env");
6162
+ if (!await fs.pathExists(envDir)) return;
6163
+ await addPackageDependency({
6164
+ dependencies: getT3EnvDeps(options),
6165
+ projectDir: envDir
6166
+ });
6167
+ }
6168
+ function getT3EnvDeps(options) {
6169
+ const deps = ["zod"];
6170
+ const { frontend, backend, runtime } = options;
6171
+ if (frontend.includes("next")) deps.push("@t3-oss/env-nextjs");
6172
+ else if (frontend.includes("nuxt")) deps.push("@t3-oss/env-nuxt");
6173
+ else deps.push("@t3-oss/env-core");
6174
+ if (backend !== "convex" && backend !== "none" && runtime !== "workers" && !deps.includes("@t3-oss/env-core")) deps.push("@t3-oss/env-core");
6175
+ return deps;
6176
+ }
6177
+
6210
6178
  //#endregion
6211
6179
  //#region src/helpers/core/git.ts
6212
6180
  async function initializeGit(projectDir, useGit) {
@@ -6229,6 +6197,17 @@ async function initializeGit(projectDir, useGit) {
6229
6197
  await $({ cwd: projectDir })`git commit -m ${"initial commit"}`;
6230
6198
  }
6231
6199
 
6200
+ //#endregion
6201
+ //#region src/helpers/core/infra-package-setup.ts
6202
+ async function setupInfraPackageDependencies(projectDir, _options) {
6203
+ const infraDir = path.join(projectDir, "packages/infra");
6204
+ if (!await fs.pathExists(infraDir)) return;
6205
+ await addPackageDependency({
6206
+ devDependencies: ["alchemy"],
6207
+ projectDir: infraDir
6208
+ });
6209
+ }
6210
+
6232
6211
  //#endregion
6233
6212
  //#region src/helpers/core/payments-setup.ts
6234
6213
  async function setupPayments(config) {
@@ -6348,7 +6327,7 @@ async function displayPostInstallInstructions(config) {
6348
6327
  let output = `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd}\n`;
6349
6328
  let stepCounter = 2;
6350
6329
  if (!depsInstalled) output += `${pc.cyan(`${stepCounter++}.`)} ${packageManager} install\n`;
6351
- if (database === "sqlite" && dbSetup === "none" && (serverDeploy === "alchemy" || webDeploy === "alchemy")) output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (starts local SQLite server for Workers compatibility)")}\n`;
6330
+ if (database === "sqlite" && dbSetup !== "d1") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} db:local\n${pc.dim(" (optional - starts local SQLite database)")}\n`;
6352
6331
  if (isConvex) {
6353
6332
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup\n${pc.dim(" (this will guide you through Convex project setup)")}\n`;
6354
6333
  output += `${pc.cyan(`${stepCounter++}.`)} Copy environment variables from\n${pc.white(" packages/backend/.env.local")} to ${pc.white("apps/*/.env")}\n`;
@@ -6415,7 +6394,7 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6415
6394
  instructions.push("");
6416
6395
  }
6417
6396
  }
6418
- if (dbSetup === "d1" && serverDeploy === "alchemy") {
6397
+ if (dbSetup === "d1" && serverDeploy === "cloudflare") {
6419
6398
  if (orm === "drizzle") instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
6420
6399
  else if (orm === "prisma") {
6421
6400
  instructions.push(`${pc.cyan("•")} Generate Prisma client: ${`${runCmd} db:generate`}`);
@@ -6430,15 +6409,15 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
6430
6409
  if (orm === "prisma") {
6431
6410
  if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
6432
6411
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6433
- if (!(dbSetup === "d1" && serverDeploy === "alchemy")) {
6412
+ if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) {
6434
6413
  instructions.push(`${pc.cyan("•")} Generate Prisma Client: ${`${runCmd} db:generate`}`);
6435
6414
  instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6436
6415
  }
6437
- if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6416
+ if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6438
6417
  } else if (orm === "drizzle") {
6439
6418
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6440
6419
  if (dbSetup !== "d1") instructions.push(`${pc.cyan("•")} Apply schema: ${`${runCmd} db:push`}`);
6441
- if (!(dbSetup === "d1" && serverDeploy === "alchemy")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6420
+ if (!(dbSetup === "d1" && serverDeploy === "cloudflare")) instructions.push(`${pc.cyan("•")} Database UI: ${`${runCmd} db:studio`}`);
6442
6421
  } else if (orm === "mongoose") {
6443
6422
  if (dbSetup === "docker") instructions.push(`${pc.cyan("•")} Start docker container: ${`${runCmd} db:start`}`);
6444
6423
  } else if (orm === "none") instructions.push(`${pc.yellow("NOTE:")} Manual database schema setup\n required.`);
@@ -6469,116 +6448,174 @@ function getPolarInstructions(backend) {
6469
6448
  function getAlchemyDeployInstructions(runCmd, webDeploy, serverDeploy, backend) {
6470
6449
  const instructions = [];
6471
6450
  const isBackendSelf = backend === "self";
6472
- if (webDeploy === "alchemy" && serverDeploy !== "alchemy") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
6473
- else if (serverDeploy === "alchemy" && webDeploy !== "alchemy" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6474
- else if (webDeploy === "alchemy" && (serverDeploy === "alchemy" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6451
+ if (webDeploy === "cloudflare" && serverDeploy !== "cloudflare") instructions.push(`${pc.bold("Deploy web with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/web && ${runCmd} alchemy dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/web && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/web && ${runCmd} destroy`}`);
6452
+ else if (serverDeploy === "cloudflare" && webDeploy !== "cloudflare" && !isBackendSelf) instructions.push(`${pc.bold("Deploy server with Alchemy:")}\n${pc.cyan("•")} Dev: ${`cd apps/server && ${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`cd apps/server && ${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`cd apps/server && ${runCmd} destroy`}`);
6453
+ else if (webDeploy === "cloudflare" && (serverDeploy === "cloudflare" || isBackendSelf)) instructions.push(`${pc.bold("Deploy with Alchemy:")}\n${pc.cyan("•")} Dev: ${`${runCmd} dev`}\n${pc.cyan("•")} Deploy: ${`${runCmd} deploy`}\n${pc.cyan("•")} Destroy: ${`${runCmd} destroy`}`);
6475
6454
  return instructions.length ? `\n${instructions.join("\n")}` : "";
6476
6455
  }
6477
6456
 
6478
6457
  //#endregion
6479
6458
  //#region src/helpers/core/workspace-setup.ts
6480
6459
  async function setupWorkspaceDependencies(projectDir, options) {
6481
- const { projectName, packageManager, database, auth, api, runtime, backend } = options;
6460
+ const { projectName, packageManager, runtime, backend } = options;
6482
6461
  const workspaceVersion = packageManager === "npm" ? "*" : "workspace:*";
6483
- const commonDeps = ["dotenv", "zod"];
6484
- const commonDevDeps = ["typescript"];
6485
- const configDir = path.join(projectDir, "packages/config");
6486
- const dbDir = path.join(projectDir, "packages/db");
6487
- const authDir = path.join(projectDir, "packages/auth");
6488
- const apiDir = path.join(projectDir, "packages/api");
6489
- const backendDir = path.join(projectDir, "packages/backend");
6490
- const serverDir = path.join(projectDir, "apps/server");
6491
- const webDir = path.join(projectDir, "apps/web");
6492
- const nativeDir = path.join(projectDir, "apps/native");
6493
- const [configExists, dbExists, authExists, apiExists, backendExists, serverExists, webExists, nativeExists] = await Promise.all([
6494
- fs.pathExists(configDir),
6495
- fs.pathExists(dbDir),
6496
- fs.pathExists(authDir),
6497
- fs.pathExists(apiDir),
6498
- fs.pathExists(backendDir),
6499
- fs.pathExists(serverDir),
6500
- fs.pathExists(webDir),
6501
- fs.pathExists(nativeDir)
6462
+ const packages = await detectPackages(projectDir);
6463
+ const configDep = packages.config.exists ? { [`@${projectName}/config`]: workspaceVersion } : {};
6464
+ const envDep = packages.env.exists ? { [`@${projectName}/env`]: workspaceVersion } : {};
6465
+ const ctx = {
6466
+ projectName,
6467
+ workspaceVersion,
6468
+ options,
6469
+ commonDeps: ["dotenv", "zod"],
6470
+ commonDevDeps: ["typescript"],
6471
+ configDep,
6472
+ envDep
6473
+ };
6474
+ await Promise.all([
6475
+ setupEnvPackage(packages.env, packages.infra, ctx),
6476
+ setupInfraPackage(packages.infra, ctx),
6477
+ setupDbPackage(packages.db, ctx),
6478
+ setupAuthPackage(packages.auth, packages.db, ctx),
6479
+ setupApiPackage(packages.api, packages.auth, packages.db, ctx),
6480
+ setupBackendPackage(packages.backend, ctx),
6481
+ setupServerPackage(packages.server, packages.api, packages.auth, packages.db, ctx),
6482
+ setupWebPackage(packages.web, packages.api, packages.auth, packages.backend, ctx),
6483
+ setupNativePackage(packages.native, packages.api, packages.backend, ctx)
6502
6484
  ]);
6503
- const configDep = configExists ? { [`@${projectName}/config`]: workspaceVersion } : {};
6504
- if (dbExists) await addPackageDependency({
6505
- dependencies: commonDeps,
6506
- devDependencies: commonDevDeps,
6485
+ const runtimeDevDeps = getRuntimeDevDeps(runtime, backend);
6486
+ await addPackageDependency({
6487
+ dependencies: ctx.commonDeps,
6488
+ devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
6489
+ customDependencies: envDep,
6507
6490
  customDevDependencies: configDep,
6508
- projectDir: dbDir
6491
+ projectDir
6509
6492
  });
6510
- if (authExists) {
6511
- const authDeps = {};
6512
- if (database !== "none" && dbExists) authDeps[`@${projectName}/db`] = workspaceVersion;
6513
- await addPackageDependency({
6514
- dependencies: commonDeps,
6515
- devDependencies: commonDevDeps,
6516
- customDependencies: authDeps,
6517
- customDevDependencies: configDep,
6518
- projectDir: authDir
6519
- });
6520
- }
6521
- if (apiExists) {
6522
- const apiDeps = {};
6523
- if (auth !== "none" && authExists) apiDeps[`@${projectName}/auth`] = workspaceVersion;
6524
- if (database !== "none" && dbExists) apiDeps[`@${projectName}/db`] = workspaceVersion;
6525
- await addPackageDependency({
6526
- dependencies: commonDeps,
6527
- devDependencies: commonDevDeps,
6528
- customDependencies: apiDeps,
6529
- customDevDependencies: configDep,
6530
- projectDir: apiDir
6531
- });
6532
- }
6533
- if (backendExists) await addPackageDependency({
6534
- dependencies: commonDeps,
6535
- devDependencies: commonDevDeps,
6536
- customDevDependencies: configDep,
6537
- projectDir: backendDir
6493
+ }
6494
+ async function detectPackages(projectDir) {
6495
+ const entries = await Promise.all(Object.entries({
6496
+ config: "packages/config",
6497
+ env: "packages/env",
6498
+ infra: "packages/infra",
6499
+ db: "packages/db",
6500
+ auth: "packages/auth",
6501
+ api: "packages/api",
6502
+ backend: "packages/backend",
6503
+ server: "apps/server",
6504
+ web: "apps/web",
6505
+ native: "apps/native"
6506
+ }).map(async ([name, relativePath]) => {
6507
+ const dir = path.join(projectDir, relativePath);
6508
+ return [name, {
6509
+ dir,
6510
+ exists: await fs.pathExists(dir)
6511
+ }];
6512
+ }));
6513
+ return Object.fromEntries(entries);
6514
+ }
6515
+ async function setupEnvPackage(pkg, infraPkg, ctx) {
6516
+ if (!pkg.exists) return;
6517
+ const runtimeDevDeps = getRuntimeDevDeps(ctx.options.runtime, ctx.options.backend);
6518
+ const customDevDeps = { ...ctx.configDep };
6519
+ if ((ctx.options.serverDeploy === "cloudflare" || ctx.options.webDeploy === "cloudflare") && infraPkg.exists) customDevDeps[`@${ctx.projectName}/infra`] = ctx.workspaceVersion;
6520
+ await addPackageDependency({
6521
+ dependencies: ctx.commonDeps,
6522
+ devDependencies: [...ctx.commonDevDeps, ...runtimeDevDeps],
6523
+ customDevDependencies: customDevDeps,
6524
+ projectDir: pkg.dir
6538
6525
  });
6539
- if (serverExists) {
6540
- const serverDeps = {};
6541
- if (api !== "none" && apiExists) serverDeps[`@${projectName}/api`] = workspaceVersion;
6542
- if (auth !== "none" && authExists) serverDeps[`@${projectName}/auth`] = workspaceVersion;
6543
- if (database !== "none" && dbExists) serverDeps[`@${projectName}/db`] = workspaceVersion;
6544
- await addPackageDependency({
6545
- dependencies: commonDeps,
6546
- devDependencies: [...commonDevDeps, "tsdown"],
6547
- customDependencies: serverDeps,
6548
- customDevDependencies: configDep,
6549
- projectDir: serverDir
6550
- });
6551
- }
6552
- if (webExists) {
6553
- const webDeps = {};
6554
- if (api !== "none" && apiExists) webDeps[`@${projectName}/api`] = workspaceVersion;
6555
- if (auth !== "none" && authExists) webDeps[`@${projectName}/auth`] = workspaceVersion;
6556
- if (backend === "convex" && backendExists) webDeps[`@${projectName}/backend`] = workspaceVersion;
6557
- await addPackageDependency({
6558
- dependencies: commonDeps,
6559
- devDependencies: commonDevDeps,
6560
- customDependencies: webDeps,
6561
- customDevDependencies: configDep,
6562
- projectDir: webDir
6563
- });
6564
- }
6565
- if (nativeExists) {
6566
- const nativeDeps = {};
6567
- if (api !== "none" && apiExists) nativeDeps[`@${projectName}/api`] = workspaceVersion;
6568
- if (backend === "convex" && backendExists) nativeDeps[`@${projectName}/backend`] = workspaceVersion;
6569
- await addPackageDependency({
6570
- dependencies: commonDeps,
6571
- devDependencies: commonDevDeps,
6572
- customDependencies: nativeDeps,
6573
- customDevDependencies: configDep,
6574
- projectDir: nativeDir
6575
- });
6576
- }
6577
- const runtimeDevDeps = getRuntimeDevDeps(runtime, backend);
6526
+ }
6527
+ async function setupInfraPackage(pkg, ctx) {
6528
+ if (!pkg.exists) return;
6578
6529
  await addPackageDependency({
6579
- dependencies: commonDeps,
6580
- devDependencies: [...commonDevDeps, ...runtimeDevDeps],
6581
- projectDir
6530
+ dependencies: ctx.commonDeps,
6531
+ devDependencies: ctx.commonDevDeps,
6532
+ customDevDependencies: ctx.configDep,
6533
+ projectDir: pkg.dir
6534
+ });
6535
+ }
6536
+ async function setupDbPackage(pkg, ctx) {
6537
+ if (!pkg.exists) return;
6538
+ await addPackageDependency({
6539
+ dependencies: ctx.commonDeps,
6540
+ devDependencies: ctx.commonDevDeps,
6541
+ customDependencies: ctx.envDep,
6542
+ customDevDependencies: ctx.configDep,
6543
+ projectDir: pkg.dir
6544
+ });
6545
+ }
6546
+ async function setupAuthPackage(pkg, dbPkg, ctx) {
6547
+ if (!pkg.exists) return;
6548
+ const deps = { ...ctx.envDep };
6549
+ if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
6550
+ await addPackageDependency({
6551
+ dependencies: ctx.commonDeps,
6552
+ devDependencies: ctx.commonDevDeps,
6553
+ customDependencies: deps,
6554
+ customDevDependencies: ctx.configDep,
6555
+ projectDir: pkg.dir
6556
+ });
6557
+ }
6558
+ async function setupApiPackage(pkg, authPkg, dbPkg, ctx) {
6559
+ if (!pkg.exists) return;
6560
+ const deps = { ...ctx.envDep };
6561
+ if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
6562
+ if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
6563
+ await addPackageDependency({
6564
+ dependencies: ctx.commonDeps,
6565
+ devDependencies: ctx.commonDevDeps,
6566
+ customDependencies: deps,
6567
+ customDevDependencies: ctx.configDep,
6568
+ projectDir: pkg.dir
6569
+ });
6570
+ }
6571
+ async function setupBackendPackage(pkg, ctx) {
6572
+ if (!pkg.exists) return;
6573
+ await addPackageDependency({
6574
+ dependencies: ctx.commonDeps,
6575
+ devDependencies: ctx.commonDevDeps,
6576
+ customDevDependencies: ctx.configDep,
6577
+ projectDir: pkg.dir
6578
+ });
6579
+ }
6580
+ async function setupServerPackage(pkg, apiPkg, authPkg, dbPkg, ctx) {
6581
+ if (!pkg.exists) return;
6582
+ const deps = { ...ctx.envDep };
6583
+ if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
6584
+ if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
6585
+ if (ctx.options.database !== "none" && dbPkg.exists) deps[`@${ctx.projectName}/db`] = ctx.workspaceVersion;
6586
+ await addPackageDependency({
6587
+ dependencies: ctx.commonDeps,
6588
+ devDependencies: [...ctx.commonDevDeps, "tsdown"],
6589
+ customDependencies: deps,
6590
+ customDevDependencies: ctx.configDep,
6591
+ projectDir: pkg.dir
6592
+ });
6593
+ }
6594
+ async function setupWebPackage(pkg, apiPkg, authPkg, backendPkg, ctx) {
6595
+ if (!pkg.exists) return;
6596
+ const deps = { ...ctx.envDep };
6597
+ if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
6598
+ if (ctx.options.auth !== "none" && authPkg.exists) deps[`@${ctx.projectName}/auth`] = ctx.workspaceVersion;
6599
+ if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
6600
+ await addPackageDependency({
6601
+ dependencies: ctx.commonDeps,
6602
+ devDependencies: ctx.commonDevDeps,
6603
+ customDependencies: deps,
6604
+ customDevDependencies: ctx.configDep,
6605
+ projectDir: pkg.dir
6606
+ });
6607
+ }
6608
+ async function setupNativePackage(pkg, apiPkg, backendPkg, ctx) {
6609
+ if (!pkg.exists) return;
6610
+ const deps = { ...ctx.envDep };
6611
+ if (ctx.options.api !== "none" && apiPkg.exists) deps[`@${ctx.projectName}/api`] = ctx.workspaceVersion;
6612
+ if (ctx.options.backend === "convex" && backendPkg.exists) deps[`@${ctx.projectName}/backend`] = ctx.workspaceVersion;
6613
+ await addPackageDependency({
6614
+ dependencies: ctx.commonDeps,
6615
+ devDependencies: ctx.commonDevDeps,
6616
+ customDependencies: deps,
6617
+ customDevDependencies: ctx.configDep,
6618
+ projectDir: pkg.dir
6582
6619
  });
6583
6620
  }
6584
6621
  function getRuntimeDevDeps(runtime, backend) {
@@ -6615,7 +6652,7 @@ async function updateRootPackageJson(projectDir, options) {
6615
6652
  const dbPackageName = `@${projectName}/db`;
6616
6653
  const hasTurborepo = addons.includes("turborepo");
6617
6654
  const needsDbScripts = backend !== "convex" && database !== "none" && orm !== "none" && orm !== "mongoose";
6618
- const isD1Alchemy = dbSetup === "d1" && serverDeploy === "alchemy";
6655
+ const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
6619
6656
  const pmConfig = getPackageManagerConfig(packageManager, hasTurborepo);
6620
6657
  scripts.dev = pmConfig.dev;
6621
6658
  scripts.build = pmConfig.build;
@@ -6635,7 +6672,7 @@ async function updateRootPackageJson(projectDir, options) {
6635
6672
  if (!isD1Alchemy) scripts["db:migrate"] = pmConfig.filter(dbPackageName, "db:migrate");
6636
6673
  }
6637
6674
  }
6638
- if (database === "sqlite" && dbSetup !== "d1" && orm !== "none") scripts["db:local"] = pmConfig.filter(dbPackageName, "db:local");
6675
+ if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = pmConfig.filter(dbPackageName, "db:local");
6639
6676
  if (dbSetup === "docker") {
6640
6677
  scripts["db:start"] = pmConfig.filter(dbPackageName, "db:start");
6641
6678
  scripts["db:watch"] = pmConfig.filter(dbPackageName, "db:watch");
@@ -6643,7 +6680,7 @@ async function updateRootPackageJson(projectDir, options) {
6643
6680
  scripts["db:down"] = pmConfig.filter(dbPackageName, "db:down");
6644
6681
  }
6645
6682
  try {
6646
- const { stdout } = await execa(packageManager, ["-v"], { cwd: projectDir });
6683
+ const { stdout } = await $`${packageManager} -v`;
6647
6684
  packageJson.packageManager = `${packageManager}@${stdout.trim()}`;
6648
6685
  } catch {
6649
6686
  log.warn(`Could not determine ${packageManager} version.`);
@@ -6700,7 +6737,7 @@ async function updateDbPackageJson(projectDir, options) {
6700
6737
  dbPackageJson.scripts = dbPackageJson.scripts || {};
6701
6738
  const scripts = dbPackageJson.scripts;
6702
6739
  const { database, orm, dbSetup, serverDeploy } = options;
6703
- const isD1Alchemy = dbSetup === "d1" && serverDeploy === "alchemy";
6740
+ const isD1Alchemy = dbSetup === "d1" && serverDeploy === "cloudflare";
6704
6741
  if (database !== "none") {
6705
6742
  if (database === "sqlite" && dbSetup !== "d1") scripts["db:local"] = "turso dev --db-file local.db";
6706
6743
  if (orm === "prisma") {
@@ -6750,7 +6787,8 @@ async function updateConvexPackageJson(projectDir, options) {
6750
6787
 
6751
6788
  //#endregion
6752
6789
  //#region src/helpers/core/create-project.ts
6753
- async function createProject(options, cliInput) {
6790
+ async function createProject(options, cliInput = {}) {
6791
+ const { silent = false } = cliInput;
6754
6792
  const projectDir = options.projectDir;
6755
6793
  const isConvex = options.backend === "convex";
6756
6794
  const isSelfBackend = options.backend === "self";
@@ -6766,6 +6804,8 @@ async function createProject(options, cliInput) {
6766
6804
  if (options.examples.length > 0 && options.examples[0] !== "none") await setupExamplesTemplate(projectDir, options);
6767
6805
  await setupAddonsTemplate(projectDir, options);
6768
6806
  await setupDeploymentTemplates(projectDir, options);
6807
+ await setupEnvPackageDependencies(projectDir, options);
6808
+ if (options.serverDeploy === "cloudflare" || options.webDeploy === "cloudflare") await setupInfraPackageDependencies(projectDir, options);
6769
6809
  await setupApi(options);
6770
6810
  if (isConvex || needsServerSetup) await setupBackendDependencies(options);
6771
6811
  if (!isConvex) {
@@ -6784,23 +6824,23 @@ async function createProject(options, cliInput) {
6784
6824
  await setupCatalogs(projectDir, options);
6785
6825
  await createReadme(projectDir, options);
6786
6826
  await writeBtsConfig(options);
6787
- log.success("Project template successfully scaffolded!");
6827
+ if (!silent) log.success("Project template successfully scaffolded!");
6788
6828
  if (options.install) await installDependencies({
6789
6829
  projectDir,
6790
6830
  packageManager: options.packageManager
6791
6831
  });
6792
6832
  await initializeGit(projectDir, options.git);
6793
- await displayPostInstallInstructions({
6833
+ if (!silent) await displayPostInstallInstructions({
6794
6834
  ...options,
6795
6835
  depsInstalled: options.install
6796
6836
  });
6797
6837
  return projectDir;
6798
6838
  } catch (error) {
6799
6839
  if (error instanceof Error) {
6800
- console.error(error.stack);
6840
+ if (!silent) console.error(error.stack);
6801
6841
  exitWithError(`Error during project creation: ${error.message}`);
6802
6842
  } else {
6803
- console.error(error);
6843
+ if (!silent) console.error(error);
6804
6844
  exitWithError(`An unexpected error occurred: ${String(error)}`);
6805
6845
  }
6806
6846
  }
@@ -6808,12 +6848,13 @@ async function createProject(options, cliInput) {
6808
6848
 
6809
6849
  //#endregion
6810
6850
  //#region src/helpers/core/command-handlers.ts
6811
- async function createProjectHandler(input) {
6851
+ async function createProjectHandler(input, options = {}) {
6852
+ const { silent = false } = options;
6812
6853
  const startTime = Date.now();
6813
6854
  const timeScaffolded = (/* @__PURE__ */ new Date()).toISOString();
6814
- if (input.renderTitle !== false) renderTitle();
6815
- intro(pc.magenta("Creating a new Better-T-Stack project"));
6816
- if (input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
6855
+ if (!silent && input.renderTitle !== false) renderTitle();
6856
+ if (!silent) intro(pc.magenta("Creating a new Better-T-Stack project"));
6857
+ if (!silent && input.yolo) consola.fatal("YOLO mode enabled - skipping checks. Things may break!");
6817
6858
  let currentPathInput;
6818
6859
  if (input.yes && input.projectName) currentPathInput = input.projectName;
6819
6860
  else if (input.yes) {
@@ -6882,8 +6923,10 @@ async function createProjectHandler(input) {
6882
6923
  if (templateConfig) {
6883
6924
  const templateName = input.template.toUpperCase();
6884
6925
  const templateDescription = getTemplateDescription(input.template);
6885
- log.message(pc.bold(pc.cyan(`Using template: ${pc.white(templateName)}`)));
6886
- log.message(pc.dim(` ${templateDescription}`));
6926
+ if (!silent) {
6927
+ log.message(pc.bold(pc.cyan(`Using template: ${pc.white(templateName)}`)));
6928
+ log.message(pc.dim(` ${templateDescription}`));
6929
+ }
6887
6930
  const userOverrides = {};
6888
6931
  for (const [key, value] of Object.entries(originalInput)) if (value !== void 0) userOverrides[key] = value;
6889
6932
  cliInput = {
@@ -6905,25 +6948,32 @@ async function createProjectHandler(input) {
6905
6948
  relativePath: finalPathInput
6906
6949
  };
6907
6950
  validateConfigCompatibility(config, providedFlags, cliInput);
6908
- log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
6909
- log.message(displayConfig(config));
6951
+ if (!silent) {
6952
+ log.info(pc.yellow("Using default/flag options (config prompts skipped):"));
6953
+ log.message(displayConfig(config));
6954
+ }
6910
6955
  } else {
6911
6956
  const flagConfig = processAndValidateFlags(cliInput, providedFlags, finalBaseName);
6912
6957
  const { projectName: _projectNameFromFlags, ...otherFlags } = flagConfig;
6913
- if (Object.keys(otherFlags).length > 0) {
6958
+ if (!silent && Object.keys(otherFlags).length > 0) {
6914
6959
  log.info(pc.yellow("Using these pre-selected options:"));
6915
6960
  log.message(displayConfig(otherFlags));
6916
6961
  log.message("");
6917
6962
  }
6918
6963
  config = await gatherConfig(flagConfig, finalBaseName, finalResolvedPath, finalPathInput);
6919
6964
  }
6920
- await createProject(config, { manualDb: cliInput.manualDb ?? input.manualDb });
6965
+ await createProject(config, {
6966
+ manualDb: cliInput.manualDb ?? input.manualDb,
6967
+ silent
6968
+ });
6921
6969
  const reproducibleCommand = generateReproducibleCommand(config);
6922
- log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
6970
+ if (!silent) log.success(pc.blue(`You can reproduce this setup with the following command:\n${reproducibleCommand}`));
6923
6971
  await trackProjectCreation(config, input.disableAnalytics);
6924
6972
  const elapsedTimeMs = Date.now() - startTime;
6925
- const elapsedTimeInSeconds = (elapsedTimeMs / 1e3).toFixed(2);
6926
- outro(pc.magenta(`Project created successfully in ${pc.bold(elapsedTimeInSeconds)} seconds!`));
6973
+ if (!silent) {
6974
+ const elapsedTimeInSeconds = (elapsedTimeMs / 1e3).toFixed(2);
6975
+ outro(pc.magenta(`Project created successfully in ${pc.bold(elapsedTimeInSeconds)} seconds!`));
6976
+ }
6927
6977
  return {
6928
6978
  success: true,
6929
6979
  projectConfig: config,
@@ -7035,25 +7085,12 @@ async function addAddonsHandler(input) {
7035
7085
  //#region src/utils/open-url.ts
7036
7086
  async function openUrl(url) {
7037
7087
  const platform = process.platform;
7038
- let command;
7039
- let args = [];
7040
- if (platform === "darwin") {
7041
- command = "open";
7042
- args = [url];
7043
- } else if (platform === "win32") {
7044
- command = "cmd";
7045
- args = [
7046
- "/c",
7047
- "start",
7048
- "",
7049
- url.replace(/&/g, "^&")
7050
- ];
7051
- } else {
7052
- command = "xdg-open";
7053
- args = [url];
7054
- }
7055
7088
  try {
7056
- await execa(command, args, { stdio: "ignore" });
7089
+ if (platform === "darwin") await $({ stdio: "ignore" })`open ${url}`;
7090
+ else if (platform === "win32") {
7091
+ const escapedUrl = url.replace(/&/g, "^&");
7092
+ await $({ stdio: "ignore" })`cmd /c start "" ${escapedUrl}`;
7093
+ } else await $({ stdio: "ignore" })`xdg-open ${url}`;
7057
7094
  } catch {
7058
7095
  log.message(`Please open ${url} in your browser.`);
7059
7096
  }
@@ -7103,32 +7140,32 @@ function displaySponsorsBox(sponsors$1) {
7103
7140
  //#endregion
7104
7141
  //#region src/index.ts
7105
7142
  const router = os.router({
7106
- init: os.meta({
7143
+ create: os.meta({
7107
7144
  description: "Create a new Better-T-Stack project",
7108
7145
  default: true,
7109
7146
  negateBooleans: true
7110
- }).input(z.tuple([ProjectNameSchema.optional(), z.object({
7111
- template: TemplateSchema.optional().describe("Use a predefined template"),
7147
+ }).input(z.tuple([types_exports.ProjectNameSchema.optional(), z.object({
7148
+ template: types_exports.TemplateSchema.optional().describe("Use a predefined template"),
7112
7149
  yes: z.boolean().optional().default(false).describe("Use default configuration"),
7113
7150
  yolo: z.boolean().optional().default(false).describe("(WARNING - NOT RECOMMENDED) Bypass validations and compatibility checks"),
7114
7151
  verbose: z.boolean().optional().default(false).describe("Show detailed result information"),
7115
- database: DatabaseSchema.optional(),
7116
- orm: ORMSchema.optional(),
7117
- auth: AuthSchema.optional(),
7118
- payments: PaymentsSchema.optional(),
7119
- frontend: z.array(FrontendSchema).optional(),
7120
- addons: z.array(AddonsSchema).optional(),
7121
- examples: z.array(ExamplesSchema).optional(),
7152
+ database: types_exports.DatabaseSchema.optional(),
7153
+ orm: types_exports.ORMSchema.optional(),
7154
+ auth: types_exports.AuthSchema.optional(),
7155
+ payments: types_exports.PaymentsSchema.optional(),
7156
+ frontend: z.array(types_exports.FrontendSchema).optional(),
7157
+ addons: z.array(types_exports.AddonsSchema).optional(),
7158
+ examples: z.array(types_exports.ExamplesSchema).optional(),
7122
7159
  git: z.boolean().optional(),
7123
- packageManager: PackageManagerSchema.optional(),
7160
+ packageManager: types_exports.PackageManagerSchema.optional(),
7124
7161
  install: z.boolean().optional(),
7125
- dbSetup: DatabaseSetupSchema.optional(),
7126
- backend: BackendSchema.optional(),
7127
- runtime: RuntimeSchema.optional(),
7128
- api: APISchema.optional(),
7129
- webDeploy: WebDeploySchema.optional(),
7130
- serverDeploy: ServerDeploySchema.optional(),
7131
- directoryConflict: DirectoryConflictSchema.optional(),
7162
+ dbSetup: types_exports.DatabaseSetupSchema.optional(),
7163
+ backend: types_exports.BackendSchema.optional(),
7164
+ runtime: types_exports.RuntimeSchema.optional(),
7165
+ api: types_exports.APISchema.optional(),
7166
+ webDeploy: types_exports.WebDeploySchema.optional(),
7167
+ serverDeploy: types_exports.ServerDeploySchema.optional(),
7168
+ directoryConflict: types_exports.DirectoryConflictSchema.optional(),
7132
7169
  renderTitle: z.boolean().optional(),
7133
7170
  disableAnalytics: z.boolean().optional().default(false).describe("Disable analytics"),
7134
7171
  manualDb: z.boolean().optional().default(false).describe("Skip automatic/manual database setup prompt and use manual setup")
@@ -7141,12 +7178,12 @@ const router = os.router({
7141
7178
  if (options.verbose) return result;
7142
7179
  }),
7143
7180
  add: os.meta({ description: "Add addons or deployment configurations to an existing Better-T-Stack project" }).input(z.tuple([z.object({
7144
- addons: z.array(AddonsSchema).optional().default([]),
7145
- webDeploy: WebDeploySchema.optional(),
7146
- serverDeploy: ServerDeploySchema.optional(),
7181
+ addons: z.array(types_exports.AddonsSchema).optional().default([]),
7182
+ webDeploy: types_exports.WebDeploySchema.optional(),
7183
+ serverDeploy: types_exports.ServerDeploySchema.optional(),
7147
7184
  projectDir: z.string().optional(),
7148
7185
  install: z.boolean().optional().default(false).describe("Install dependencies after adding addons or deployment"),
7149
- packageManager: PackageManagerSchema.optional()
7186
+ packageManager: types_exports.PackageManagerSchema.optional()
7150
7187
  })])).handler(async ({ input }) => {
7151
7188
  const [options] = input;
7152
7189
  await addAddonsHandler(options);
@@ -7188,49 +7225,49 @@ function createBtsCli() {
7188
7225
  });
7189
7226
  }
7190
7227
  /**
7191
- * Initialize a new Better-T-Stack project
7228
+ * Programmatic API to create a new Better-T-Stack project.
7229
+ * Returns pure JSON - no console output, no interactive prompts.
7192
7230
  *
7193
- * @example CLI usage:
7194
- * ```bash
7195
- * npx create-better-t-stack my-app --yes
7196
- * ```
7197
- *
7198
- * @example Programmatic usage (always returns structured data):
7231
+ * @example
7199
7232
  * ```typescript
7200
- * import { init } from "create-better-t-stack";
7233
+ * import { create } from "create-better-t-stack";
7201
7234
  *
7202
- * const result = await init("my-app", {
7203
- * yes: true,
7235
+ * const result = await create("my-app", {
7204
7236
  * frontend: ["tanstack-router"],
7205
7237
  * backend: "hono",
7238
+ * runtime: "bun",
7206
7239
  * database: "sqlite",
7207
7240
  * orm: "drizzle",
7208
- * auth: "better-auth",
7209
- * addons: ["biome", "turborepo"],
7210
- * packageManager: "bun",
7211
- * install: false,
7212
- * directoryConflict: "increment", // auto-handle conflicts
7213
- * disableAnalytics: true, // disable analytics
7214
7241
  * });
7215
7242
  *
7216
7243
  * if (result.success) {
7217
7244
  * console.log(`Project created at: ${result.projectDirectory}`);
7218
- * console.log(`Reproducible command: ${result.reproducibleCommand}`);
7219
- * console.log(`Time taken: ${result.elapsedTimeMs}ms`);
7220
7245
  * }
7221
7246
  * ```
7222
7247
  */
7223
- async function init(projectName, options) {
7224
- const programmaticOpts = {
7225
- ...options ?? {},
7226
- verbose: true
7248
+ async function create(projectName, options) {
7249
+ const input = {
7250
+ ...options,
7251
+ projectName,
7252
+ renderTitle: false,
7253
+ verbose: true,
7254
+ disableAnalytics: options?.disableAnalytics ?? true,
7255
+ directoryConflict: options?.directoryConflict ?? "error"
7227
7256
  };
7228
- const prev = process.env.BTS_PROGRAMMATIC;
7229
- process.env.BTS_PROGRAMMATIC = "1";
7230
- const result = await caller.init([projectName, programmaticOpts]);
7231
- if (prev === void 0) delete process.env.BTS_PROGRAMMATIC;
7232
- else process.env.BTS_PROGRAMMATIC = prev;
7233
- return result;
7257
+ try {
7258
+ return await createProjectHandler(input, { silent: true });
7259
+ } catch (error) {
7260
+ return {
7261
+ success: false,
7262
+ error: error instanceof Error ? error.message : String(error),
7263
+ projectConfig: {},
7264
+ reproducibleCommand: "",
7265
+ timeScaffolded: (/* @__PURE__ */ new Date()).toISOString(),
7266
+ elapsedTimeMs: 0,
7267
+ projectDirectory: "",
7268
+ relativePath: ""
7269
+ };
7270
+ }
7234
7271
  }
7235
7272
  async function sponsors() {
7236
7273
  return caller.sponsors();
@@ -7243,4 +7280,4 @@ async function builder() {
7243
7280
  }
7244
7281
 
7245
7282
  //#endregion
7246
- export { router as a, init as i, createBtsCli as n, sponsors as o, docs as r, builder as t };
7283
+ export { router as a, docs as i, create as n, sponsors as o, createBtsCli as r, builder as t };