create-better-t-stack 2.18.6 → 2.19.1

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
@@ -741,7 +742,8 @@ async function setupEnvironmentVariables(config) {
741
742
  databaseUrl = "mongodb://localhost:27017/mydatabase";
742
743
  break;
743
744
  case "sqlite":
744
- databaseUrl = "file:./local.db";
745
+ if (config.runtime === "workers") databaseUrl = "http://127.0.0.1:8080";
746
+ else databaseUrl = "file:./local.db";
745
747
  break;
746
748
  }
747
749
  const serverVars = [
@@ -772,6 +774,12 @@ async function setupEnvironmentVariables(config) {
772
774
  }
773
775
  ];
774
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
+ }
775
783
  }
776
784
 
777
785
  //#endregion
@@ -1586,6 +1594,7 @@ async function setupRuntime(config) {
1586
1594
  if (!await fs.pathExists(serverDir)) return;
1587
1595
  if (runtime === "bun") await setupBunRuntime(serverDir, backend);
1588
1596
  else if (runtime === "node") await setupNodeRuntime(serverDir, backend);
1597
+ else if (runtime === "workers") await setupWorkersRuntime(serverDir);
1589
1598
  }
1590
1599
  async function setupBunRuntime(serverDir, _backend) {
1591
1600
  const packageJsonPath = path.join(serverDir, "package.json");
@@ -1625,6 +1634,24 @@ async function setupNodeRuntime(serverDir, backend) {
1625
1634
  projectDir: serverDir
1626
1635
  });
1627
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
+ }
1628
1655
 
1629
1656
  //#endregion
1630
1657
  //#region src/helpers/project-generation/create-readme.ts
@@ -1873,7 +1900,13 @@ function displayPostInstallInstructions(config) {
1873
1900
  if (isConvex) {
1874
1901
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev:setup ${pc.dim("(this will guide you through Convex project setup)")}\n`;
1875
1902
  output += `${pc.cyan(`${stepCounter++}.`)} ${runCmd} dev\n\n`;
1876
- } 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
+ }
1877
1910
  output += `${pc.bold("Your project will be available at:")}\n`;
1878
1911
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
1879
1912
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n`;
@@ -2128,8 +2161,9 @@ async function processTemplate(srcPath, destPath, context) {
2128
2161
  throw new Error(`Failed to process template ${srcPath}`);
2129
2162
  }
2130
2163
  }
2131
- handlebars.registerHelper("or", (a, b) => a || b);
2132
2164
  handlebars.registerHelper("eq", (a, b) => a === b);
2165
+ handlebars.registerHelper("and", (a, b) => a && b);
2166
+ handlebars.registerHelper("or", (a, b) => a || b);
2133
2167
  handlebars.registerHelper("includes", (array, value) => Array.isArray(array) && array.includes(value));
2134
2168
 
2135
2169
  //#endregion
@@ -2468,6 +2502,10 @@ async function handleExtras(projectDir, context) {
2468
2502
  const npmrcDest = path.join(projectDir, ".npmrc");
2469
2503
  if (await fs.pathExists(npmrcTemplateSrc)) await processTemplate(npmrcTemplateSrc, npmrcDest, context);
2470
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
+ }
2471
2509
  }
2472
2510
 
2473
2511
  //#endregion
@@ -2702,38 +2740,39 @@ async function getBackendFrameworkChoice(backendFramework, frontends) {
2702
2740
 
2703
2741
  //#endregion
2704
2742
  //#region src/prompts/database.ts
2705
- async function getDatabaseChoice(database, backend) {
2743
+ async function getDatabaseChoice(database, backend, runtime) {
2706
2744
  if (backend === "convex" || backend === "none") return "none";
2707
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
+ });
2708
2773
  const response = await select({
2709
2774
  message: "Select database",
2710
- options: [
2711
- {
2712
- value: "none",
2713
- label: "None",
2714
- hint: "No database setup"
2715
- },
2716
- {
2717
- value: "sqlite",
2718
- label: "SQLite",
2719
- hint: "lightweight, server-less, embedded relational database"
2720
- },
2721
- {
2722
- value: "postgres",
2723
- label: "PostgreSQL",
2724
- hint: "powerful, open source object-relational database system"
2725
- },
2726
- {
2727
- value: "mysql",
2728
- label: "MySQL",
2729
- hint: "popular open-source relational database system"
2730
- },
2731
- {
2732
- value: "mongodb",
2733
- label: "MongoDB",
2734
- hint: "open-source NoSQL database that stores data in JSON-like documents called BSON"
2735
- }
2736
- ],
2775
+ options: databaseOptions,
2737
2776
  initialValue: DEFAULT_CONFIG.database
2738
2777
  });
2739
2778
  if (isCancel(response)) {
@@ -2986,10 +3025,11 @@ const ormOptions = {
2986
3025
  hint: "Lightweight and performant TypeScript ORM"
2987
3026
  }
2988
3027
  };
2989
- async function getORMChoice(orm, hasDatabase, database, backend) {
3028
+ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
2990
3029
  if (backend === "convex") return "none";
2991
3030
  if (!hasDatabase) return "none";
2992
3031
  if (orm !== void 0) return orm;
3032
+ if (runtime === "workers") return "drizzle";
2993
3033
  const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
2994
3034
  const response = await select({
2995
3035
  message: "Select ORM",
@@ -3042,17 +3082,23 @@ async function getRuntimeChoice(runtime, backend) {
3042
3082
  if (backend === "convex" || backend === "none") return "none";
3043
3083
  if (runtime !== void 0) return runtime;
3044
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
+ });
3045
3099
  const response = await select({
3046
3100
  message: "Select runtime",
3047
- options: [{
3048
- value: "bun",
3049
- label: "Bun",
3050
- hint: "Fast all-in-one JavaScript runtime"
3051
- }, {
3052
- value: "node",
3053
- label: "Node.js",
3054
- hint: "Traditional Node.js runtime"
3055
- }],
3101
+ options: runtimeOptions,
3056
3102
  initialValue: DEFAULT_CONFIG.runtime
3057
3103
  });
3058
3104
  if (isCancel(response)) {
@@ -3069,8 +3115,8 @@ async function gatherConfig(flags, projectName, projectDir, relativePath) {
3069
3115
  frontend: () => getFrontendChoice(flags.frontend, flags.backend),
3070
3116
  backend: ({ results }) => getBackendFrameworkChoice(flags.backend, results.frontend),
3071
3117
  runtime: ({ results }) => getRuntimeChoice(flags.runtime, results.backend),
3072
- database: ({ results }) => getDatabaseChoice(flags.database, results.backend),
3073
- 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),
3074
3120
  api: ({ results }) => getApiChoice(flags.api, results.frontend, results.backend),
3075
3121
  auth: ({ results }) => getAuthChoice(flags.auth, results.database !== "none", results.backend),
3076
3122
  addons: ({ results }) => getAddonsChoice(flags.addons, results.frontend),
@@ -3148,8 +3194,9 @@ const BackendSchema = z.enum([
3148
3194
  const RuntimeSchema = z.enum([
3149
3195
  "bun",
3150
3196
  "node",
3197
+ "workers",
3151
3198
  "none"
3152
- ]).describe("Runtime environment");
3199
+ ]).describe("Runtime environment (workers only available with hono backend and drizzle orm)");
3153
3200
  const FrontendSchema = z.enum([
3154
3201
  "tanstack-router",
3155
3202
  "react-router",
@@ -3655,6 +3702,30 @@ function processAndValidateFlags(options, providedFlags, projectName) {
3655
3702
  consola$1.fatal("Supabase setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup.");
3656
3703
  process.exit(1);
3657
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
+ }
3658
3729
  return config;
3659
3730
  }
3660
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.6",
3
+ "version": "2.19.1",
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
+ {
2
+ "name": "{{projectName}}-server",
3
+ "main": "src/index.ts",
4
+ "compatibility_date": "2025-06-15",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "vars": {
7
+ "NODE_ENV": "production"
8
+ // Non-sensitive environment variables (visible in dashboard)
9
+ // "CORS_ORIGIN": "https://your-frontend-domain.com",
10
+ // "BETTER_AUTH_URL": "https://your-worker-domain.workers.dev"
11
+ }
12
+ // ⚠️ SENSITIVE DATA: Use `wrangler secret put` instead of adding here
13
+ // Don't put these in "vars" - they'll be visible in the dashboard!
14
+ // - DATABASE_URL
15
+ // - DATABASE_AUTH_TOKEN
16
+ // - GOOGLE_GENERATIVE_AI_API_KEY
17
+ // - BETTER_AUTH_SECRET
18
+ }
@@ -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 });