create-better-t-stack 2.18.5 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -99,7 +99,8 @@ const dependencyVersionMap = {
99
99
  "@tanstack/react-query-devtools": "^5.80.5",
100
100
  "@tanstack/react-query": "^5.80.5",
101
101
  "@tanstack/solid-query": "^5.75.0",
102
- "@tanstack/solid-query-devtools": "^5.75.0"
102
+ "@tanstack/solid-query-devtools": "^5.75.0",
103
+ wrangler: "^4.20.0"
103
104
  };
104
105
 
105
106
  //#endregion
@@ -539,7 +540,8 @@ async function setupAuth(config) {
539
540
  "tanstack-start",
540
541
  "next",
541
542
  "nuxt",
542
- "svelte"
543
+ "svelte",
544
+ "solid"
543
545
  ].includes(f));
544
546
  if (hasWebFrontend && clientDirExists) await addPackageDependency({
545
547
  dependencies: ["better-auth"],
@@ -740,7 +742,8 @@ async function setupEnvironmentVariables(config) {
740
742
  databaseUrl = "mongodb://localhost:27017/mydatabase";
741
743
  break;
742
744
  case "sqlite":
743
- databaseUrl = "file:./local.db";
745
+ if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
746
+ else databaseUrl = "file:./local.db";
744
747
  break;
745
748
  }
746
749
  const serverVars = [
@@ -771,6 +774,12 @@ async function setupEnvironmentVariables(config) {
771
774
  }
772
775
  ];
773
776
  await addEnvVariablesToFile(envPath, serverVars);
777
+ if (config.runtime === "workers") {
778
+ const devVarsPath = path.join(serverDir, ".dev.vars");
779
+ try {
780
+ await fs.copy(envPath, devVarsPath);
781
+ } catch (_err) {}
782
+ }
774
783
  }
775
784
 
776
785
  //#endregion
@@ -1585,6 +1594,7 @@ async function setupRuntime(config) {
1585
1594
  if (!await fs.pathExists(serverDir)) return;
1586
1595
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
1587
1596
  else if (runtime === "node") await setupNodeRuntime(serverDir, backend);
1597
+ else if (runtime === "workers") await setupWorkersRuntime(serverDir);
1588
1598
  }
1589
1599
  async function setupBunRuntime(serverDir, _backend) {
1590
1600
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -1624,6 +1634,24 @@ async function setupNodeRuntime(serverDir, backend) {
1624
1634
  projectDir: serverDir
1625
1635
  });
1626
1636
  }
1637
+ async function setupWorkersRuntime(serverDir) {
1638
+ const packageJsonPath = path.join(serverDir, "package.json");
1639
+ if (!await fs.pathExists(packageJsonPath)) return;
1640
+ const packageJson = await fs.readJson(packageJsonPath);
1641
+ packageJson.scripts = {
1642
+ ...packageJson.scripts,
1643
+ dev: "wrangler dev --port=3000",
1644
+ start: "wrangler dev",
1645
+ deploy: "wrangler deploy",
1646
+ build: "wrangler deploy --dry-run",
1647
+ "cf-typegen": "wrangler types --env-interface CloudflareBindings"
1648
+ };
1649
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
1650
+ await addPackageDependency({
1651
+ devDependencies: ["wrangler"],
1652
+ projectDir: serverDir
1653
+ });
1654
+ }
1627
1655
 
1628
1656
  //#endregion
1629
1657
  //#region src/helpers/project-generation/create-readme.ts
@@ -1872,7 +1900,13 @@ function displayPostInstallInstructions(config) {
1872
1900
  if (isConvex) {
1873
1901
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim("(this will guide you through Convex project setup)")}\n`;
1874
1902
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
1875
- } else output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
1903
+ } else {
1904
+ if (runtime !== "workers") output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n`;
1905
+ if (runtime === "workers") {
1906
+ output += `${pc.cyan(`${stepCounter++}.`)} bun dev\n`;
1907
+ output += `${pc.cyan(`${stepCounter++}.`)} cd apps/server && bun run cf-typegen\n\n`;
1908
+ } else output += "\n";
1909
+ }
1876
1910
  output += `${pc.bold("Your project will be available at:")}\n`;
1877
1911
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
1878
1912
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`;
@@ -2127,8 +2161,9 @@ async function processTemplate(srcPath, destPath, context) {
2127
2161
  throw new Error(`Failed to process template ${srcPath}`);
2128
2162
  }
2129
2163
  }
2130
- handlebars.registerHelper("or", (a, b) => a || b);
2131
2164
  handlebars.registerHelper("eq", (a, b) => a === b);
2165
+ handlebars.registerHelper("and", (a, b) => a && b);
2166
+ handlebars.registerHelper("or", (a, b) => a || b);
2132
2167
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2133
2168
 
2134
2169
  //#endregion
@@ -2467,6 +2502,10 @@ async function handleExtras(projectDir, context) {
2467
2502
  const npmrcDest = path.join(projectDir, ".npmrc");
2468
2503
  if (await fs.pathExists(npmrcTemplateSrc)) await processTemplate(npmrcTemplateSrc, npmrcDest, context);
2469
2504
  }
2505
+ if (context.runtime === "workers") {
2506
+ const runtimeWorkersDir = path.join(PKG_ROOT, "templates/runtime/workers");
2507
+ if (await fs.pathExists(runtimeWorkersDir)) await processAndCopyFiles("**/*", runtimeWorkersDir, projectDir, context, false);
2508
+ }
2470
2509
  }
2471
2510
 
2472
2511
  //#endregion
@@ -2701,38 +2740,39 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
2701
2740
 
2702
2741
  //#endregion
2703
2742
  //#region src/prompts/database.ts
2704
- async function getDatabaseChoice(database, backend) {
2743
+ async function getDatabaseChoice(database, backend, runtime) {
2705
2744
  if (backend === "convex" || backend === "none") return "none";
2706
2745
  if (database !== void 0) return database;
2746
+ const databaseOptions = [
2747
+ {
2748
+ value: "none",
2749
+ label: "None",
2750
+ hint: "No database setup"
2751
+ },
2752
+ {
2753
+ value: "sqlite",
2754
+ label: "SQLite",
2755
+ hint: "lightweight, server-less, embedded relational database"
2756
+ },
2757
+ {
2758
+ value: "postgres",
2759
+ label: "PostgreSQL",
2760
+ hint: "powerful, open source object-relational database system"
2761
+ },
2762
+ {
2763
+ value: "mysql",
2764
+ label: "MySQL",
2765
+ hint: "popular open-source relational database system"
2766
+ }
2767
+ ];
2768
+ if (runtime !== "workers") databaseOptions.push({
2769
+ value: "mongodb",
2770
+ label: "MongoDB",
2771
+ hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
2772
+ });
2707
2773
  const response = await select({
2708
2774
  message: "Select database",
2709
- options: [
2710
- {
2711
- value: "none",
2712
- label: "None",
2713
- hint: "No database setup"
2714
- },
2715
- {
2716
- value: "sqlite",
2717
- label: "SQLite",
2718
- hint: "lightweight, server-less, embedded relational database"
2719
- },
2720
- {
2721
- value: "postgres",
2722
- label: "PostgreSQL",
2723
- hint: "powerful, open source object-relational database system"
2724
- },
2725
- {
2726
- value: "mysql",
2727
- label: "MySQL",
2728
- hint: "popular open-source relational database system"
2729
- },
2730
- {
2731
- value: "mongodb",
2732
- label: "MongoDB",
2733
- hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
2734
- }
2735
- ],
2775
+ options: databaseOptions,
2736
2776
  initialValue: DEFAULT_CONFIG.database
2737
2777
  });
2738
2778
  if (isCancel(response)) {
@@ -2985,10 +3025,11 @@ const ormOptions = {
2985
3025
  hint: "Lightweight and performant TypeScript ORM"
2986
3026
  }
2987
3027
  };
2988
- async function getORMChoice(orm, hasDatabase, database, backend) {
3028
+ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
2989
3029
  if (backend === "convex") return "none";
2990
3030
  if (!hasDatabase) return "none";
2991
3031
  if (orm !== void 0) return orm;
3032
+ if (runtime === "workers") return "drizzle";
2992
3033
  const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
2993
3034
  const response = await select({
2994
3035
  message: "Select ORM",
@@ -3041,17 +3082,23 @@ async function getRuntimeChoice(runtime, backend) {
3041
3082
  if (backend === "convex" || backend === "none") return "none";
3042
3083
  if (runtime !== void 0) return runtime;
3043
3084
  if (backend === "next") return "node";
3085
+ const runtimeOptions = [{
3086
+ value: "bun",
3087
+ label: "Bun",
3088
+ hint: "Fast all-in-one JavaScript runtime"
3089
+ }, {
3090
+ value: "node",
3091
+ label: "Node.js",
3092
+ hint: "Traditional Node.js runtime"
3093
+ }];
3094
+ if (backend === "hono") runtimeOptions.push({
3095
+ value: "workers",
3096
+ label: "Cloudflare Workers (beta)",
3097
+ hint: "Edge runtime on Cloudflare's global network"
3098
+ });
3044
3099
  const response = await select({
3045
3100
  message: "Select runtime",
3046
- options: [{
3047
- value: "bun",
3048
- label: "Bun",
3049
- hint: "Fast all-in-one JavaScript runtime"
3050
- }, {
3051
- value: "node",
3052
- label: "Node.js",
3053
- hint: "Traditional Node.js runtime"
3054
- }],
3101
+ options: runtimeOptions,
3055
3102
  initialValue: DEFAULT_CONFIG.runtime
3056
3103
  });
3057
3104
  if (isCancel(response)) {
@@ -3068,8 +3115,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
3068
3115
  frontend: () => getFrontendChoice(flags.frontend, flags.backend),
3069
3116
  backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
3070
3117
  runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
3071
- database: ({ results }) => getDatabaseChoice(flags.database, results.backend),
3072
- orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend),
3118
+ database: ({ results }) => getDatabaseChoice(flags.database, results.backend, results.runtime),
3119
+ orm: ({ results }) => getORMChoice(flags.orm, results.database !== "none", results.database, results.backend, results.runtime),
3073
3120
  api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
3074
3121
  auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
3075
3122
  addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
@@ -3147,8 +3194,9 @@ const BackendSchema = z.enum([
3147
3194
  const RuntimeSchema = z.enum([
3148
3195
  "bun",
3149
3196
  "node",
3197
+ "workers",
3150
3198
  "none"
3151
- ]).describe("Runtime environment");
3199
+ ]).describe("Runtime environment (workers only available with hono backend and drizzle orm)");
3152
3200
  const FrontendSchema = z.enum([
3153
3201
  "tanstack-router",
3154
3202
  "react-router",
@@ -3654,6 +3702,30 @@ function processAndValidateFlags(options, providedFlags, projectName) {
3654
3702
  consola$1.fatal("Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.");
3655
3703
  process.exit(1);
3656
3704
  }
3705
+ if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") {
3706
+ consola$1.fatal(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
3707
+ process.exit(1);
3708
+ }
3709
+ if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") {
3710
+ consola$1.fatal(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
3711
+ process.exit(1);
3712
+ }
3713
+ if (providedFlags.has("runtime") && options.runtime === "workers" && config.orm && config.orm !== "drizzle" && config.orm !== "none") {
3714
+ consola$1.fatal(`Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
3715
+ process.exit(1);
3716
+ }
3717
+ if (providedFlags.has("orm") && config.orm && config.orm !== "drizzle" && config.orm !== "none" && config.runtime === "workers") {
3718
+ consola$1.fatal(`ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
3719
+ process.exit(1);
3720
+ }
3721
+ if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") {
3722
+ consola$1.fatal("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
3723
+ process.exit(1);
3724
+ }
3725
+ if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") {
3726
+ consola$1.fatal("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
3727
+ process.exit(1);
3728
+ }
3657
3729
  return config;
3658
3730
  }
3659
3731
  function getProvidedFlags(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.18.5",
3
+ "version": "2.19.0",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,21 +14,22 @@ export const auth = betterAuth({
14
14
  {{#if (eq database "mongodb")}}provider: "mongodb"{{/if}}
15
15
  }),
16
16
  trustedOrigins: [
17
- process.env.CORS_ORIGIN || "",{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
18
- "my-better-t-app://",{{/if}}
17
+ process.env.CORS_ORIGIN || "",
18
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
19
+ "my-better-t-app://",
20
+ {{/if}}
19
21
  ],
20
22
  emailAndPassword: {
21
23
  enabled: true,
22
24
  }
23
-
24
25
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
25
- ,
26
- plugins: [expo()]
26
+ , plugins: [expo()]
27
27
  {{/if}}
28
28
  });
29
29
  {{/if}}
30
30
 
31
31
  {{#if (eq orm "drizzle")}}
32
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
32
33
  import { betterAuth } from "better-auth";
33
34
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
34
35
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
@@ -45,19 +46,51 @@ export const auth = betterAuth({
45
46
  schema: schema,
46
47
  }),
47
48
  trustedOrigins: [
48
- process.env.CORS_ORIGIN || "",{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
49
- "my-better-t-app://",{{/if}}
49
+ process.env.CORS_ORIGIN || "",
50
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
51
+ "my-better-t-app://",
52
+ {{/if}}
50
53
  ],
51
54
  emailAndPassword: {
52
55
  enabled: true,
53
- }
56
+ },
57
+ secret: process.env.BETTER_AUTH_SECRET,
58
+ baseURL: process.env.BETTER_AUTH_URL,
59
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
60
+ plugins: [expo()],
61
+ {{/if}}
62
+ });
63
+ {{/if}}
64
+
65
+ {{#if (eq runtime "workers")}}
66
+ import { betterAuth } from "better-auth";
67
+ import { drizzleAdapter } from "better-auth/adapters/drizzle";
68
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
69
+ import { expo } from "@better-auth/expo";
70
+ {{/if}}
71
+ import { db } from "@/db";
72
+ import * as schema from "../db/schema/auth";
73
+ import { env } from "cloudflare:workers";
54
74
 
75
+ export const auth = betterAuth({
76
+ database: drizzleAdapter(db, {
77
+ {{#if (eq database "postgres")}}provider: "pg",{{/if}}
78
+ {{#if (eq database "sqlite")}}provider: "sqlite",{{/if}}
79
+ {{#if (eq database "mysql")}}provider: "mysql",{{/if}}
80
+ schema: schema,
81
+ }),
82
+ trustedOrigins: [env.CORS_ORIGIN],
83
+ emailAndPassword: {
84
+ enabled: true,
85
+ },
86
+ secret: env.BETTER_AUTH_SECRET,
87
+ baseURL: env.BETTER_AUTH_URL,
55
88
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
56
- ,
57
- plugins: [expo()]
89
+ plugins: [expo()],
58
90
  {{/if}}
59
91
  });
60
92
  {{/if}}
93
+ {{/if}}
61
94
 
62
95
  {{#if (eq orm "mongoose")}}
63
96
  import { betterAuth } from "better-auth";
@@ -70,16 +103,16 @@ import { client } from "../db";
70
103
  export const auth = betterAuth({
71
104
  database: mongodbAdapter(client),
72
105
  trustedOrigins: [
73
- process.env.CORS_ORIGIN || "",{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
74
- "my-better-t-app://",{{/if}}
106
+ process.env.CORS_ORIGIN || "",
107
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
108
+ "my-better-t-app://",
109
+ {{/if}}
75
110
  ],
76
111
  emailAndPassword: {
77
112
  enabled: true,
78
113
  }
79
-
80
114
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
81
- ,
82
- plugins: [expo()]
115
+ , plugins: [expo()]
83
116
  {{/if}}
84
117
  });
85
118
  {{/if}}
@@ -93,16 +126,16 @@ import { expo } from "@better-auth/expo";
93
126
  export const auth = betterAuth({
94
127
  database: "", // Invalid configuration
95
128
  trustedOrigins: [
96
- process.env.CORS_ORIGIN || "",{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
97
- "my-better-t-app://",{{/if}}
129
+ process.env.CORS_ORIGIN || "",
130
+ {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
131
+ "my-better-t-app://",
132
+ {{/if}}
98
133
  ],
99
134
  emailAndPassword: {
100
135
  enabled: true,
101
136
  }
102
-
103
137
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
104
- ,
105
- plugins: [expo()]
138
+ , plugins: [expo()]
106
139
  {{/if}}
107
140
  });
108
141
  {{/if}}
@@ -1,4 +1,9 @@
1
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
1
2
  import "dotenv/config";
3
+ {{/if}}
4
+ {{#if (eq runtime "workers")}}
5
+ import { env } from "cloudflare:workers";
6
+ {{/if}}
2
7
  {{#if (eq api "orpc")}}
3
8
  import { RPCHandler } from "@orpc/server/fetch";
4
9
  import { createContext } from "./lib/context";
@@ -15,26 +20,33 @@ import { auth } from "./lib/auth";
15
20
  import { Hono } from "hono";
16
21
  import { cors } from "hono/cors";
17
22
  import { logger } from "hono/logger";
18
- {{#if (includes examples "ai")}}
23
+ {{#if (and (includes examples "ai") (or (eq runtime "bun") (eq runtime "node")))}}
19
24
  import { streamText } from "ai";
20
25
  import { google } from "@ai-sdk/google";
21
26
  import { stream } from "hono/streaming";
22
27
  {{/if}}
28
+ {{#if (and (includes examples "ai") (eq runtime "workers"))}}
29
+ import { streamText } from "ai";
30
+ import { stream } from "hono/streaming";
31
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
32
+ {{/if}}
23
33
 
24
34
  const app = new Hono();
25
35
 
26
36
  app.use(logger());
27
- app.use(
28
- "/*",
29
- cors({
30
- origin: process.env.CORS_ORIGIN || "",
31
- allowMethods: ["GET", "POST", "OPTIONS"],
32
- {{#if auth}}
33
- allowHeaders: ["Content-Type", "Authorization"],
34
- credentials: true,
35
- {{/if}}
36
- })
37
- );
37
+ app.use("/*", cors({
38
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
39
+ origin: process.env.CORS_ORIGIN || "",
40
+ {{/if}}
41
+ {{#if (eq runtime "workers")}}
42
+ origin: env.CORS_ORIGIN || "",
43
+ {{/if}}
44
+ allowMethods: ["GET", "POST", "OPTIONS"],
45
+ {{#if auth}}
46
+ allowHeaders: ["Content-Type", "Authorization"],
47
+ credentials: true,
48
+ {{/if}}
49
+ }));
38
50
 
39
51
  {{#if auth}}
40
52
  app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
@@ -48,6 +60,7 @@ app.use("/rpc/*", async (c, next) => {
48
60
  prefix: "/rpc",
49
61
  context: context,
50
62
  });
63
+
51
64
  if (matched) {
52
65
  return c.newResponse(response.body, response);
53
66
  }
@@ -64,11 +77,10 @@ app.use("/trpc/*", trpcServer({
64
77
  }));
65
78
  {{/if}}
66
79
 
67
- {{#if (includes examples "ai")}}
80
+ {{#if (and (includes examples "ai") (or (eq runtime "bun") (eq runtime "node")))}}
68
81
  app.post("/ai", async (c) => {
69
82
  const body = await c.req.json();
70
83
  const messages = body.messages || [];
71
-
72
84
  const result = streamText({
73
85
  model: google("gemini-1.5-flash"),
74
86
  messages,
@@ -76,7 +88,24 @@ app.post("/ai", async (c) => {
76
88
 
77
89
  c.header("X-Vercel-AI-Data-Stream", "v1");
78
90
  c.header("Content-Type", "text/plain; charset=utf-8");
91
+ return stream(c, (stream) => stream.pipe(result.toDataStream()));
92
+ });
93
+ {{/if}}
79
94
 
95
+ {{#if (and (includes examples "ai") (eq runtime "workers"))}}
96
+ app.post("/ai", async (c) => {
97
+ const body = await c.req.json();
98
+ const messages = body.messages || [];
99
+ const google = createGoogleGenerativeAI({
100
+ apiKey: env.GOOGLE_GENERATIVE_AI_API_KEY,
101
+ });
102
+ const result = streamText({
103
+ model: google("gemini-1.5-flash"),
104
+ messages,
105
+ });
106
+
107
+ c.header("X-Vercel-AI-Data-Stream", "v1");
108
+ c.header("Content-Type", "text/plain; charset=utf-8");
80
109
  return stream(c, (stream) => stream.pipe(result.toDataStream()));
81
110
  });
82
111
  {{/if}}
@@ -95,5 +124,10 @@ serve({
95
124
  console.log(`Server is running on http://localhost:${info.port}`);
96
125
  });
97
126
  {{else}}
127
+ {{#if (eq runtime "bun")}}
128
+ export default app;
129
+ {{/if}}
130
+ {{#if (eq runtime "workers")}}
98
131
  export default app;
132
+ {{/if}}
99
133
  {{/if}}
@@ -1,35 +1,39 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "verbatimModuleSyntax": true,
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "baseUrl": "./",
10
- "paths": {
11
- "@/*": ["./src/*"]
12
- {{#if (eq orm 'prisma')}},
13
- "prisma": ["node_modules/prisma"]
14
- {{/if}}
15
- },
16
- "outDir": "./dist",
17
- "types": [
18
- {{#if (eq runtime 'node')}}
19
- "node"
20
- {{else if (eq runtime 'bun')}}
21
- "bun"
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "verbatimModuleSyntax": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "baseUrl": "./",
10
+ "paths": {
11
+ "@/*": ["./src/*"]
12
+ {{#if (eq orm "prisma")}},
13
+ "prisma": ["node_modules/prisma"]
14
+ {{/if}}
15
+ },
16
+ "outDir": "./dist",
17
+ "types": [
18
+ {{#if (eq runtime "node")}}
19
+ "node"
20
+ {{else if (eq runtime "bun")}}
21
+ "bun"
22
+ {{else if (eq runtime "workers")}}
23
+ "./worker-configuration",
24
+ "node"
22
25
  {{else}}
23
- "node", "bun"
26
+ "node",
27
+ "bun"
24
28
  {{/if}}
25
29
  ],
26
- {{#unless (or (eq backend "convex") (eq backend "none"))}}
27
- "composite": true,
28
- {{/unless}}
29
- "jsx": "react-jsx"{{#if (eq backend 'hono')}},
30
- "jsxImportSource": "hono/jsx"{{/if}}
31
- },
32
- "tsc-alias": {
33
- "resolveFullPaths": true
34
- }
30
+ {{#unless (or (eq backend "convex") (eq backend "none"))}}
31
+ "composite": true,
32
+ {{/unless}}
33
+ "jsx": "react-jsx"{{#if (eq backend "hono")}},
34
+ "jsxImportSource": "hono/jsx"{{/if}}
35
+ },
36
+ "tsc-alias": {
37
+ "resolveFullPaths": true
38
+ }
35
39
  }
@@ -0,0 +1,20 @@
1
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
2
+ import { drizzle } from "drizzle-orm/mysql2";
3
+
4
+ export const db = drizzle({
5
+ connection: {
6
+ uri: process.env.DATABASE_URL,
7
+ },
8
+ });
9
+ {{/if}}
10
+
11
+ {{#if (eq runtime "workers")}}
12
+ import { drizzle } from "drizzle-orm/mysql2";
13
+ import { env } from "cloudflare:workers";
14
+
15
+ export const db = drizzle({
16
+ connection: {
17
+ uri: env.DATABASE_URL,
18
+ },
19
+ });
20
+ {{/if}}
@@ -0,0 +1,12 @@
1
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
2
+ import { drizzle } from "drizzle-orm/node-postgres";
3
+
4
+ export const db = drizzle(process.env.DATABASE_URL || "");
5
+ {{/if}}
6
+
7
+ {{#if (eq runtime "workers")}}
8
+ import { drizzle } from "drizzle-orm/node-postgres";
9
+ import { env } from "cloudflare:workers";
10
+
11
+ export const db = drizzle(env.DATABASE_URL || "");
12
+ {{/if}}
@@ -6,6 +6,8 @@ export default defineConfig({
6
6
  dialect: "turso",
7
7
  dbCredentials: {
8
8
  url: process.env.DATABASE_URL || "",
9
+ {{#if (eq dbSetup "turso")}}
9
10
  authToken: process.env.DATABASE_AUTH_TOKEN,
11
+ {{/if}}
10
12
  },
11
13
  });
@@ -0,0 +1,28 @@
1
+ {{#if (or (eq runtime "bun") (eq runtime "node"))}}
2
+ import { drizzle } from "drizzle-orm/libsql";
3
+ import { createClient } from "@libsql/client";
4
+
5
+ const client = createClient({
6
+ url: process.env.DATABASE_URL || "",
7
+ {{#if (eq dbSetup "turso")}}
8
+ authToken: process.env.DATABASE_AUTH_TOKEN,
9
+ {{/if}}
10
+ });
11
+
12
+ export const db = drizzle({ client });
13
+ {{/if}}
14
+
15
+ {{#if (eq runtime "workers")}}
16
+ import { drizzle } from "drizzle-orm/libsql";
17
+ import { env } from "cloudflare:workers";
18
+ import { createClient } from "@libsql/client";
19
+
20
+ const client = createClient({
21
+ url: env.DATABASE_URL || "",
22
+ {{#if (eq dbSetup "turso")}}
23
+ authToken: env.DATABASE_AUTH_TOKEN,
24
+ {{/if}}
25
+ });
26
+
27
+ export const db = drizzle({ client });
28
+ {{/if}}
@@ -1,4 +1,8 @@
1
+ {{#if (eq dbSetup "prisma-postgres")}}
1
2
  // import "dotenv/config"; uncomment this to load .env
3
+ {{else}}
4
+ import "dotenv/config";
5
+ {{/if}}
2
6
  import path from "node:path";
3
7
  import type { PrismaConfig } from "prisma";
4
8
 
@@ -0,0 +1,18 @@
1
+ name = "{{projectName}}-server"
2
+ main = "src/index.ts"
3
+ compatibility_date = "2025-06-15"
4
+ compatibility_flags = ["nodejs_compat"]
5
+
6
+ [vars]
7
+ NODE_ENV = "production"
8
+
9
+ # Non-sensitive environment variables (visible in dashboard)
10
+ # CORS_ORIGIN = "https://your-frontend-domain.com"
11
+ # BETTER_AUTH_URL = "https://your-worker-domain.workers.dev"
12
+
13
+ # ⚠️ SENSITIVE DATA: Use `wrangler secret put` instead of adding here
14
+ # Don't put these in [vars] - they'll be visible in the dashboard!
15
+ # - DATABASE_URL
16
+ # - DATABASE_AUTH_TOKEN
17
+ # - GOOGLE_GENERATIVE_AI_API_KEY
18
+ # - BETTER_AUTH_SECRET
@@ -1,3 +0,0 @@
1
- import { drizzle } from "drizzle-orm/mysql2";
2
-
3
- export const db = drizzle({ connection: { uri: process.env.DATABASE_URL } });
@@ -1,3 +0,0 @@
1
- import { drizzle } from "drizzle-orm/node-postgres";
2
-
3
- export const db = drizzle(process.env.DATABASE_URL || "");
@@ -1,9 +0,0 @@
1
- import { drizzle } from "drizzle-orm/libsql";
2
- import { createClient } from "@libsql/client";
3
-
4
- const client = createClient({
5
- url: process.env.DATABASE_URL || "",
6
- authToken: process.env.DATABASE_AUTH_TOKEN ,
7
- });
8
-
9
- export const db = drizzle({ client });