create-better-t-stack 2.40.3-canary.0e4f85b3 → 2.40.3-canary.34eb1c18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-D59j3XG5.js";
2
+ import { createBtsCli } from "./src-5v1dq5ez.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-D59j3XG5.js";
2
+ import { builder, createBtsCli, docs, init, router, sponsors } from "./src-5v1dq5ez.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -110,6 +110,8 @@ const dependencyVersionMap = {
110
110
  streamdown: "^1.1.6",
111
111
  "@orpc/server": "^1.8.6",
112
112
  "@orpc/client": "^1.8.6",
113
+ "@orpc/openapi": "^1.8.6",
114
+ "@orpc/zod": "^1.8.6",
113
115
  "@orpc/tanstack-query": "^1.8.6",
114
116
  "@trpc/tanstack-react-query": "^11.5.0",
115
117
  "@trpc/server": "^11.5.0",
@@ -496,6 +498,8 @@ function ensureSingleWebAndNative(frontends) {
496
498
  function validateWorkersCompatibility(providedFlags, options, config) {
497
499
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.backend && config.backend !== "hono") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Hono backend (--backend hono). Current backend: ${config.backend}. Please use '--backend hono' or choose a different runtime.`);
498
500
  if (providedFlags.has("backend") && config.backend && config.backend !== "hono" && config.runtime === "workers") exitWithError(`Backend '${config.backend}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Hono backend. Please use '--backend hono' or choose a different runtime.`);
501
+ if (providedFlags.has("runtime") && options.runtime === "workers" && config.orm && config.orm !== "drizzle" && config.orm !== "none") exitWithError(`Cloudflare Workers runtime (--runtime workers) is only supported with Drizzle ORM (--orm drizzle) or no ORM (--orm none). Current ORM: ${config.orm}. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
502
+ if (providedFlags.has("orm") && config.orm && config.orm !== "drizzle" && config.orm !== "none" && config.runtime === "workers") exitWithError(`ORM '${config.orm}' is not compatible with Cloudflare Workers runtime. Cloudflare Workers runtime is only supported with Drizzle ORM or no ORM. Please use '--orm drizzle', '--orm none', or choose a different runtime.`);
499
503
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.database === "mongodb") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with MongoDB database. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
500
504
  if (providedFlags.has("runtime") && options.runtime === "workers" && config.dbSetup === "docker") exitWithError("Cloudflare Workers runtime (--runtime workers) is not compatible with Docker setup. Workers runtime uses serverless databases (D1) and doesn't support local Docker containers. Please use '--db-setup d1' for SQLite or choose a different runtime.");
501
505
  if (providedFlags.has("database") && config.database === "mongodb" && config.runtime === "workers") exitWithError("MongoDB database is not compatible with Cloudflare Workers runtime. MongoDB requires Prisma or Mongoose ORM, but Workers runtime only supports Drizzle ORM. Please use a different database or runtime.");
@@ -993,11 +997,12 @@ async function getORMChoice(orm, hasDatabase, database, backend, runtime) {
993
997
  if (backend === "convex") return "none";
994
998
  if (!hasDatabase) return "none";
995
999
  if (orm !== void 0) return orm;
1000
+ if (runtime === "workers") return "drizzle";
996
1001
  const options = [...database === "mongodb" ? [ormOptions.prisma, ormOptions.mongoose] : [ormOptions.drizzle, ormOptions.prisma]];
997
1002
  const response = await select({
998
1003
  message: "Select ORM",
999
1004
  options,
1000
- initialValue: database === "mongodb" ? "prisma" : runtime === "workers" ? "drizzle" : DEFAULT_CONFIG.orm
1005
+ initialValue: database === "mongodb" ? "prisma" : DEFAULT_CONFIG.orm
1001
1006
  });
1002
1007
  if (isCancel(response)) return exitCancelled("Operation cancelled");
1003
1008
  return response;
@@ -3093,8 +3098,7 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3093
3098
  if (!options?.skipAppScripts) pkg.scripts = {
3094
3099
  ...pkg.scripts,
3095
3100
  deploy: "alchemy deploy",
3096
- destroy: "alchemy destroy",
3097
- dev: "alchemy dev"
3101
+ destroy: "alchemy destroy"
3098
3102
  };
3099
3103
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3100
3104
  }
@@ -3119,8 +3123,7 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3119
3123
  if (!options?.skipAppScripts) pkg.scripts = {
3120
3124
  ...pkg.scripts,
3121
3125
  deploy: "alchemy deploy",
3122
- destroy: "alchemy destroy",
3123
- dev: "alchemy dev"
3126
+ destroy: "alchemy destroy"
3124
3127
  };
3125
3128
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3126
3129
  }
@@ -3183,8 +3186,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3183
3186
  if (!options?.skipAppScripts) pkg.scripts = {
3184
3187
  ...pkg.scripts,
3185
3188
  deploy: "alchemy deploy",
3186
- destroy: "alchemy destroy",
3187
- dev: "alchemy dev"
3189
+ destroy: "alchemy destroy"
3188
3190
  };
3189
3191
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3190
3192
  }
@@ -3205,8 +3207,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3205
3207
  if (!options?.skipAppScripts) pkg.scripts = {
3206
3208
  ...pkg.scripts,
3207
3209
  deploy: "alchemy deploy",
3208
- destroy: "alchemy destroy",
3209
- dev: "alchemy dev"
3210
+ destroy: "alchemy destroy"
3210
3211
  };
3211
3212
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3212
3213
  }
@@ -3231,8 +3232,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3231
3232
  if (!options?.skipAppScripts) pkg.scripts = {
3232
3233
  ...pkg.scripts,
3233
3234
  deploy: "alchemy deploy",
3234
- destroy: "alchemy destroy",
3235
- dev: "alchemy dev"
3235
+ destroy: "alchemy destroy"
3236
3236
  };
3237
3237
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3238
3238
  }
@@ -3298,8 +3298,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3298
3298
  if (!options?.skipAppScripts) pkg.scripts = {
3299
3299
  ...pkg.scripts,
3300
3300
  deploy: "alchemy deploy",
3301
- destroy: "alchemy destroy",
3302
- dev: "alchemy dev"
3301
+ destroy: "alchemy destroy"
3303
3302
  };
3304
3303
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3305
3304
  }
@@ -3324,8 +3323,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3324
3323
  if (!options?.skipAppScripts) pkg.scripts = {
3325
3324
  ...pkg.scripts,
3326
3325
  deploy: "alchemy deploy",
3327
- destroy: "alchemy destroy",
3328
- dev: "alchemy dev"
3326
+ destroy: "alchemy destroy"
3329
3327
  };
3330
3328
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3331
3329
  }
@@ -3889,7 +3887,12 @@ function getFrontendType(frontend) {
3889
3887
  }
3890
3888
  function getApiDependencies(api, frontendType) {
3891
3889
  const deps = {};
3892
- if (api === "orpc") deps.server = { dependencies: ["@orpc/server", "@orpc/client"] };
3890
+ if (api === "orpc") deps.server = { dependencies: [
3891
+ "@orpc/server",
3892
+ "@orpc/client",
3893
+ "@orpc/openapi",
3894
+ "@orpc/zod"
3895
+ ] };
3893
3896
  else if (api === "trpc") deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
3894
3897
  if (frontendType.hasReactWeb) {
3895
3898
  if (api === "orpc") deps.web = { dependencies: ["@orpc/tanstack-query", "@orpc/client"] };
@@ -5737,7 +5740,7 @@ async function getDockerStatus(database) {
5737
5740
  //#endregion
5738
5741
  //#region src/helpers/core/post-installation.ts
5739
5742
  async function displayPostInstallInstructions(config) {
5740
- const { database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
5743
+ const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
5741
5744
  const isConvex = backend === "convex";
5742
5745
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
5743
5746
  const cdCmd = `cd ${relativePath}`;
@@ -5785,7 +5788,11 @@ async function displayPostInstallInstructions(config) {
5785
5788
  output += `${pc.bold("Your project will be available at:")}\n`;
5786
5789
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
5787
5790
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
5788
- if (!isConvex) output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
5791
+ if (!isConvex) {
5792
+ output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
5793
+ if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
5794
+ else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
5795
+ }
5789
5796
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
5790
5797
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
5791
5798
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.40.3-canary.0e4f85b3",
3
+ "version": "2.40.3-canary.34eb1c18",
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",
@@ -2,18 +2,35 @@
2
2
  import { createContext } from '@/lib/context'
3
3
  {{/if}}
4
4
  import { appRouter } from '@/routers'
5
+ import { OpenAPIHandler } from '@orpc/openapi/fetch'
6
+ import { OpenAPIReferencePlugin } from '@orpc/openapi/plugins'
7
+ import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
5
8
  import { RPCHandler } from '@orpc/server/fetch'
6
9
  import { NextRequest } from 'next/server'
7
10
 
8
- const handler = new RPCHandler(appRouter)
11
+ const rpcHandler = new RPCHandler(appRouter)
12
+ const apiHandler = new OpenAPIHandler(appRouter, {
13
+ plugins: [
14
+ new OpenAPIReferencePlugin({
15
+ schemaConverters: [new ZodToJsonSchemaConverter()],
16
+ }),
17
+ ],
18
+ })
9
19
 
10
20
  async function handleRequest(req: NextRequest) {
11
- const { response } = await handler.handle(req, {
21
+ const rpcResult = await rpcHandler.handle(req, {
12
22
  prefix: '/rpc',
13
23
  context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
14
24
  })
25
+ if (rpcResult.response) return rpcResult.response
15
26
 
16
- return response ?? new Response('Not found', { status: 404 })
27
+ const apiResult = await apiHandler.handle(req, {
28
+ prefix: '/rpc/api',
29
+ context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
30
+ })
31
+ if (apiResult.response) return apiResult.response
32
+
33
+ return new Response('Not found', { status: 404 })
17
34
  }
18
35
 
19
36
  export const GET = handleRequest
@@ -10,6 +10,9 @@ import { appRouter } from "./routers/index";
10
10
  import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
11
11
  {{/if}}
12
12
  {{#if (eq api "orpc")}}
13
+ import { OpenAPIHandler } from "@orpc/openapi/fetch";
14
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
15
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
13
16
  import { RPCHandler } from "@orpc/server/fetch";
14
17
  import { appRouter } from "./routers";
15
18
  import { createContext } from "./lib/context";
@@ -19,7 +22,14 @@ import { auth } from "./lib/auth";
19
22
  {{/if}}
20
23
 
21
24
  {{#if (eq api "orpc")}}
22
- const handler = new RPCHandler(appRouter);
25
+ const rpcHandler = new RPCHandler(appRouter);
26
+ const apiHandler = new OpenAPIHandler(appRouter, {
27
+ plugins: [
28
+ new OpenAPIReferencePlugin({
29
+ schemaConverters: [new ZodToJsonSchemaConverter()],
30
+ }),
31
+ ],
32
+ });
23
33
  {{/if}}
24
34
 
25
35
  {{#if (eq runtime "node")}}
@@ -48,12 +58,19 @@ const app = new Elysia()
48
58
  {{/if}}
49
59
  {{#if (eq api "orpc")}}
50
60
  .all('/rpc*', async (context) => {
51
- const { response } = await handler.handle(context.request, {
61
+ const { response } = await rpcHandler.handle(context.request, {
52
62
  prefix: '/rpc',
53
63
  context: await createContext({ context })
54
64
  })
55
65
  return response ?? new Response('Not Found', { status: 404 })
56
66
  })
67
+ .all('/api*', async (context) => {
68
+ const { response } = await apiHandler.handle(context.request, {
69
+ prefix: '/api',
70
+ context: await createContext({ context })
71
+ })
72
+ return response ?? new Response('Not Found', { status: 404 })
73
+ })
57
74
  {{/if}}
58
75
  {{#if (eq api "trpc")}}
59
76
  .all("/trpc/*", async (context) => {
@@ -5,6 +5,9 @@ import { createContext } from "./lib/context";
5
5
  import { appRouter } from "./routers/index";
6
6
  {{/if}}
7
7
  {{#if (eq api "orpc")}}
8
+ import { OpenAPIHandler } from "@orpc/openapi/node";
9
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
10
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
8
11
  import { RPCHandler } from "@orpc/server/node";
9
12
  import { appRouter } from "./routers";
10
13
  {{#if (eq auth "better-auth")}}
@@ -50,9 +53,17 @@ app.use(
50
53
  {{/if}}
51
54
 
52
55
  {{#if (eq api "orpc")}}
53
- const handler = new RPCHandler(appRouter);
54
- app.use("/rpc{*path}", async (req, res, next) => {
55
- const { matched } = await handler.handle(req, res, {
56
+ const rpcHandler = new RPCHandler(appRouter);
57
+ const apiHandler = new OpenAPIHandler(appRouter, {
58
+ plugins: [
59
+ new OpenAPIReferencePlugin({
60
+ schemaConverters: [new ZodToJsonSchemaConverter()],
61
+ }),
62
+ ],
63
+ });
64
+
65
+ app.use(async (req, res, next) => {
66
+ const rpcResult = await rpcHandler.handle(req, res, {
56
67
  prefix: "/rpc",
57
68
  {{#if (eq auth "better-auth")}}
58
69
  context: await createContext({ req }),
@@ -60,7 +71,18 @@ app.use("/rpc{*path}", async (req, res, next) => {
60
71
  context: {},
61
72
  {{/if}}
62
73
  });
63
- if (matched) return;
74
+ if (rpcResult.matched) return;
75
+
76
+ const apiResult = await apiHandler.handle(req, res, {
77
+ prefix: "/api",
78
+ {{#if (eq auth "better-auth")}}
79
+ context: await createContext({ req }),
80
+ {{else}}
81
+ context: {},
82
+ {{/if}}
83
+ });
84
+ if (apiResult.matched) return;
85
+
64
86
  next();
65
87
  });
66
88
  {{/if}}
@@ -9,6 +9,9 @@ import { appRouter, type AppRouter } from "./routers/index";
9
9
  {{/if}}
10
10
 
11
11
  {{#if (eq api "orpc")}}
12
+ import { OpenAPIHandler } from "@orpc/openapi/node";
13
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
14
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
12
15
  import { RPCHandler } from "@orpc/server/node";
13
16
  import { CORSPlugin } from "@orpc/server/plugins";
14
17
  import { appRouter } from "./routers/index";
@@ -40,7 +43,7 @@ const baseCorsConfig = {
40
43
  };
41
44
 
42
45
  {{#if (eq api "orpc")}}
43
- const handler = new RPCHandler(appRouter, {
46
+ const rpcHandler = new RPCHandler(appRouter, {
44
47
  plugins: [
45
48
  new CORSPlugin({
46
49
  origin: process.env.CORS_ORIGIN,
@@ -50,11 +53,19 @@ const handler = new RPCHandler(appRouter, {
50
53
  ],
51
54
  });
52
55
 
56
+ const apiHandler = new OpenAPIHandler(appRouter, {
57
+ plugins: [
58
+ new OpenAPIReferencePlugin({
59
+ schemaConverters: [new ZodToJsonSchemaConverter()],
60
+ }),
61
+ ],
62
+ });
63
+
53
64
  const fastify = Fastify({
54
65
  logger: true,
55
66
  serverFactory: (fastifyHandler) => {
56
67
  const server = createServer(async (req, res) => {
57
- const { matched } = await handler.handle(req, res, {
68
+ const { matched } = await rpcHandler.handle(req, res, {
58
69
  context: await createContext(req.headers),
59
70
  prefix: "/rpc",
60
71
  });
@@ -63,6 +74,15 @@ const fastify = Fastify({
63
74
  return;
64
75
  }
65
76
 
77
+ const apiResult = await apiHandler.handle(req, res, {
78
+ context: await createContext(req.headers),
79
+ prefix: "/api",
80
+ });
81
+
82
+ if (apiResult.matched) {
83
+ return;
84
+ }
85
+
66
86
  fastifyHandler(req, res);
67
87
  });
68
88
 
@@ -5,6 +5,9 @@ import "dotenv/config";
5
5
  import { env } from "cloudflare:workers";
6
6
  {{/if}}
7
7
  {{#if (eq api "orpc")}}
8
+ import { OpenAPIHandler } from "@orpc/openapi/fetch";
9
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
10
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
8
11
  import { RPCHandler } from "@orpc/server/fetch";
9
12
  import { createContext } from "./lib/context";
10
13
  import { appRouter } from "./routers/index";
@@ -54,17 +57,37 @@ app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
54
57
  {{/if}}
55
58
 
56
59
  {{#if (eq api "orpc")}}
57
- const handler = new RPCHandler(appRouter);
58
- app.use("/rpc/*", async (c, next) => {
60
+ export const apiHandler = new OpenAPIHandler(appRouter, {
61
+ plugins: [
62
+ new OpenAPIReferencePlugin({
63
+ schemaConverters: [new ZodToJsonSchemaConverter()],
64
+ }),
65
+ ],
66
+ });
67
+
68
+ export const rpcHandler = new RPCHandler(appRouter);
69
+
70
+ app.use("/*", async (c, next) => {
59
71
  const context = await createContext({ context: c });
60
- const { matched, response } = await handler.handle(c.req.raw, {
72
+
73
+ const rpcResult = await rpcHandler.handle(c.req.raw, {
61
74
  prefix: "/rpc",
62
75
  context: context,
63
76
  });
64
77
 
65
- if (matched) {
66
- return c.newResponse(response.body, response);
78
+ if (rpcResult.matched) {
79
+ return c.newResponse(rpcResult.response.body, rpcResult.response);
80
+ }
81
+
82
+ const apiResult = await apiHandler.handle(c.req.raw, {
83
+ prefix: "/api",
84
+ context: context,
85
+ });
86
+
87
+ if (apiResult.matched) {
88
+ return c.newResponse(apiResult.response.body, apiResult.response);
67
89
  }
90
+
68
91
  await next();
69
92
  });
70
93
  {{/if}}