create-better-t-stack 3.0.12 → 3.1.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/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-91MwhQvg.js → src-3Gylwm4_.js} +97 -38
- package/package.json +1 -1
- package/templates/api/orpc/fullstack/next/src/app/api/rpc/[[...rest]]/route.ts.hbs +1 -1
- package/templates/api/orpc/fullstack/tanstack-start/src/routes/api/rpc/$.ts.hbs +58 -0
- package/templates/api/orpc/server/src/context.ts.hbs +18 -0
- package/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs +36 -0
- package/templates/api/trpc/fullstack/tanstack-start/src/routes/api/trpc/$.ts.hbs +22 -0
- package/templates/api/trpc/server/src/context.ts.hbs +21 -0
- package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +15 -0
- package/templates/auth/better-auth/server/base/src/index.ts.hbs +9 -48
- package/templates/backend/server/elysia/src/index.ts.hbs +1 -1
- package/templates/backend/server/express/src/index.ts.hbs +1 -1
- package/templates/backend/server/fastify/src/index.ts.hbs +1 -1
- package/templates/backend/server/hono/src/index.ts.hbs +1 -1
- package/templates/examples/ai/fullstack/tanstack-start/src/routes/api/ai/$.ts.hbs +31 -0
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +1 -1
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +1 -1
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -347,17 +347,17 @@ function ensureSingleWebAndNative(frontends) {
|
|
|
347
347
|
if (web.length > 1) exitWithError("Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid");
|
|
348
348
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
349
349
|
}
|
|
350
|
-
const FULLSTACK_FRONTENDS$1 = ["next"];
|
|
350
|
+
const FULLSTACK_FRONTENDS$1 = ["next", "tanstack-start"];
|
|
351
351
|
function validateSelfBackendCompatibility(providedFlags, options, config) {
|
|
352
352
|
const backend = config.backend || options.backend;
|
|
353
353
|
const frontends = config.frontend || options.frontend || [];
|
|
354
354
|
if (backend === "self") {
|
|
355
355
|
const { web, native } = splitFrontends(frontends);
|
|
356
|
-
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js
|
|
356
|
+
if (!(web.length === 1 && FULLSTACK_FRONTENDS$1.includes(web[0]))) exitWithError("Backend 'self' (fullstack) currently only supports Next.js and TanStack Start frontends. Please use --frontend next or --frontend tanstack-start. Support for Nuxt and SvelteKit will be added in a future update.");
|
|
357
357
|
if (native.length > 1) exitWithError("Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles");
|
|
358
358
|
}
|
|
359
359
|
const hasFullstackFrontend = frontends.some((f) => FULLSTACK_FRONTENDS$1.includes(f));
|
|
360
|
-
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js
|
|
360
|
+
if (providedFlags.has("backend") && !hasFullstackFrontend && backend === "self") exitWithError("Backend 'self' (fullstack) currently only supports Next.js and TanStack Start frontends. Please use --frontend next or --frontend tanstack-start or choose a different backend. Support for Nuxt and SvelteKit will be added in a future update.");
|
|
361
361
|
}
|
|
362
362
|
function validateWorkersCompatibility(providedFlags, options, config) {
|
|
363
363
|
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.`);
|
|
@@ -680,7 +680,7 @@ async function getAuthChoice(auth, hasDatabase, backend, frontend) {
|
|
|
680
680
|
|
|
681
681
|
//#endregion
|
|
682
682
|
//#region src/prompts/backend.ts
|
|
683
|
-
const FULLSTACK_FRONTENDS = ["next"];
|
|
683
|
+
const FULLSTACK_FRONTENDS = ["next", "tanstack-start"];
|
|
684
684
|
async function getBackendFrameworkChoice(backendFramework, frontends) {
|
|
685
685
|
if (backendFramework !== void 0) return backendFramework;
|
|
686
686
|
const hasIncompatibleFrontend = frontends?.some((f) => f === "solid");
|
|
@@ -2757,8 +2757,8 @@ async function setupFrontendTemplates(projectDir, context) {
|
|
|
2757
2757
|
const apiWebBaseDir = path.join(PKG_ROOT, `templates/api/${context.api}/web/react/base`);
|
|
2758
2758
|
if (await fs.pathExists(apiWebBaseDir)) await processAndCopyFiles("**/*", apiWebBaseDir, webAppDir, context);
|
|
2759
2759
|
}
|
|
2760
|
-
if (context.backend === "self" && reactFramework === "next" && context.api !== "none") {
|
|
2761
|
-
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack
|
|
2760
|
+
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start") && context.api !== "none") {
|
|
2761
|
+
const apiFullstackDir = path.join(PKG_ROOT, `templates/api/${context.api}/fullstack/${reactFramework}`);
|
|
2762
2762
|
if (await fs.pathExists(apiFullstackDir)) await processAndCopyFiles("**/*", apiFullstackDir, webAppDir, context);
|
|
2763
2763
|
}
|
|
2764
2764
|
}
|
|
@@ -2956,8 +2956,8 @@ async function setupAuthTemplate(projectDir, context) {
|
|
|
2956
2956
|
if (reactFramework) {
|
|
2957
2957
|
const authWebFrameworkSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/web/react/${reactFramework}`);
|
|
2958
2958
|
if (await fs.pathExists(authWebFrameworkSrc)) await processAndCopyFiles("**/*", authWebFrameworkSrc, webAppDir, context);
|
|
2959
|
-
if (context.backend === "self" && reactFramework === "next") {
|
|
2960
|
-
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack
|
|
2959
|
+
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start")) {
|
|
2960
|
+
const authFullstackSrc = path.join(PKG_ROOT, `templates/auth/${authProvider}/fullstack/${reactFramework}`);
|
|
2961
2961
|
if (await fs.pathExists(authFullstackSrc)) await processAndCopyFiles("**/*", authFullstackSrc, webAppDir, context);
|
|
2962
2962
|
}
|
|
2963
2963
|
}
|
|
@@ -3102,8 +3102,8 @@ async function setupExamplesTemplate(projectDir, context) {
|
|
|
3102
3102
|
if (reactFramework) {
|
|
3103
3103
|
const exampleWebFrameworkSrc = path.join(exampleWebSrc, reactFramework);
|
|
3104
3104
|
if (await fs.pathExists(exampleWebFrameworkSrc)) await processAndCopyFiles("**/*", exampleWebFrameworkSrc, webAppDir, context, false);
|
|
3105
|
-
if (context.backend === "self" && reactFramework === "next") {
|
|
3106
|
-
const exampleFullstackSrc = path.join(exampleBaseDir,
|
|
3105
|
+
if (context.backend === "self" && (reactFramework === "next" || reactFramework === "tanstack-start")) {
|
|
3106
|
+
const exampleFullstackSrc = path.join(exampleBaseDir, `fullstack/${reactFramework}`);
|
|
3107
3107
|
if (await fs.pathExists(exampleFullstackSrc)) await processAndCopyFiles("**/*", exampleFullstackSrc, webAppDir, context, false);
|
|
3108
3108
|
}
|
|
3109
3109
|
}
|
|
@@ -3995,6 +3995,46 @@ async function addDeploymentToProject(input) {
|
|
|
3995
3995
|
}
|
|
3996
3996
|
}
|
|
3997
3997
|
|
|
3998
|
+
//#endregion
|
|
3999
|
+
//#region src/utils/better-auth-plugin-setup.ts
|
|
4000
|
+
async function setupBetterAuthPlugins(projectDir, config) {
|
|
4001
|
+
const authIndexPath = `${projectDir}/packages/auth/src/index.ts`;
|
|
4002
|
+
const authIndexFile = tsProject.addSourceFileAtPath(authIndexPath);
|
|
4003
|
+
if (!authIndexFile) {
|
|
4004
|
+
console.warn("Better Auth index file not found, skipping plugin setup");
|
|
4005
|
+
return;
|
|
4006
|
+
}
|
|
4007
|
+
const pluginsToAdd = [];
|
|
4008
|
+
const importsToAdd = [];
|
|
4009
|
+
if (config.backend === "self" && config.frontend?.includes("tanstack-start")) {
|
|
4010
|
+
pluginsToAdd.push("reactStartCookies()");
|
|
4011
|
+
importsToAdd.push("import { reactStartCookies } from \"better-auth/react-start\";");
|
|
4012
|
+
}
|
|
4013
|
+
if (config.frontend?.includes("native-nativewind") || config.frontend?.includes("native-unistyles")) {
|
|
4014
|
+
pluginsToAdd.push("expo()");
|
|
4015
|
+
importsToAdd.push("import { expo } from \"@better-auth/expo\";");
|
|
4016
|
+
}
|
|
4017
|
+
if (pluginsToAdd.length === 0) return;
|
|
4018
|
+
importsToAdd.forEach((importStatement) => {
|
|
4019
|
+
if (!authIndexFile.getImportDeclaration((declaration) => declaration.getModuleSpecifierValue().includes(importStatement.split("\"")[1]))) authIndexFile.insertImportDeclaration(0, {
|
|
4020
|
+
moduleSpecifier: importStatement.split("\"")[1],
|
|
4021
|
+
namedImports: [importStatement.split("{")[1].split("}")[0].trim()]
|
|
4022
|
+
});
|
|
4023
|
+
});
|
|
4024
|
+
const betterAuthCall = authIndexFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((call) => call.getExpression().getText() === "betterAuth");
|
|
4025
|
+
if (betterAuthCall) {
|
|
4026
|
+
const configObject = betterAuthCall.getArguments()[0];
|
|
4027
|
+
if (configObject && configObject.getKind() === SyntaxKind.ObjectLiteralExpression) {
|
|
4028
|
+
const objLiteral = configObject.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
4029
|
+
const pluginsArray = ensureArrayProperty(objLiteral, "plugins");
|
|
4030
|
+
pluginsToAdd.forEach((plugin) => {
|
|
4031
|
+
pluginsArray.addElement(plugin);
|
|
4032
|
+
});
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
authIndexFile.save();
|
|
4036
|
+
}
|
|
4037
|
+
|
|
3998
4038
|
//#endregion
|
|
3999
4039
|
//#region src/utils/setup-catalogs.ts
|
|
4000
4040
|
async function setupCatalogs(projectDir, options) {
|
|
@@ -4194,7 +4234,11 @@ function getApiDependencies(api, frontendType) {
|
|
|
4194
4234
|
] };
|
|
4195
4235
|
else if (api === "trpc") deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
|
|
4196
4236
|
if (frontendType.hasReactWeb) {
|
|
4197
|
-
if (api === "orpc") deps.web = { dependencies: [
|
|
4237
|
+
if (api === "orpc") deps.web = { dependencies: [
|
|
4238
|
+
"@orpc/tanstack-query",
|
|
4239
|
+
"@orpc/client",
|
|
4240
|
+
"@orpc/server"
|
|
4241
|
+
] };
|
|
4198
4242
|
else if (api === "trpc") deps.web = { dependencies: [
|
|
4199
4243
|
"@trpc/tanstack-react-query",
|
|
4200
4244
|
"@trpc/client",
|
|
@@ -4204,7 +4248,8 @@ function getApiDependencies(api, frontendType) {
|
|
|
4204
4248
|
dependencies: [
|
|
4205
4249
|
"@tanstack/vue-query",
|
|
4206
4250
|
"@orpc/tanstack-query",
|
|
4207
|
-
"@orpc/client"
|
|
4251
|
+
"@orpc/client",
|
|
4252
|
+
"@orpc/server"
|
|
4208
4253
|
],
|
|
4209
4254
|
devDependencies: ["@tanstack/vue-query-devtools"]
|
|
4210
4255
|
};
|
|
@@ -4212,6 +4257,7 @@ function getApiDependencies(api, frontendType) {
|
|
|
4212
4257
|
dependencies: [
|
|
4213
4258
|
"@orpc/tanstack-query",
|
|
4214
4259
|
"@orpc/client",
|
|
4260
|
+
"@orpc/server",
|
|
4215
4261
|
"@tanstack/svelte-query"
|
|
4216
4262
|
],
|
|
4217
4263
|
devDependencies: ["@tanstack/svelte-query-devtools"]
|
|
@@ -4220,6 +4266,7 @@ function getApiDependencies(api, frontendType) {
|
|
|
4220
4266
|
dependencies: [
|
|
4221
4267
|
"@orpc/tanstack-query",
|
|
4222
4268
|
"@orpc/client",
|
|
4269
|
+
"@orpc/server",
|
|
4223
4270
|
"@tanstack/solid-query"
|
|
4224
4271
|
],
|
|
4225
4272
|
devDependencies: ["@tanstack/solid-query-devtools", "@tanstack/solid-router-devtools"]
|
|
@@ -4489,6 +4536,7 @@ function getClientServerVar(frontend, backend) {
|
|
|
4489
4536
|
const hasNextJs = frontend.includes("next");
|
|
4490
4537
|
const hasNuxt = frontend.includes("nuxt");
|
|
4491
4538
|
const hasSvelte = frontend.includes("svelte");
|
|
4539
|
+
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
4492
4540
|
if (backend === "self") return {
|
|
4493
4541
|
key: "",
|
|
4494
4542
|
value: "",
|
|
@@ -4498,6 +4546,7 @@ function getClientServerVar(frontend, backend) {
|
|
|
4498
4546
|
if (hasNextJs) key = "NEXT_PUBLIC_SERVER_URL";
|
|
4499
4547
|
else if (hasNuxt) key = "NUXT_PUBLIC_SERVER_URL";
|
|
4500
4548
|
else if (hasSvelte) key = "PUBLIC_SERVER_URL";
|
|
4549
|
+
else if (hasTanstackStart) key = "VITE_SERVER_URL";
|
|
4501
4550
|
return {
|
|
4502
4551
|
key,
|
|
4503
4552
|
value: "http://localhost:3000",
|
|
@@ -4508,9 +4557,11 @@ function getConvexVar(frontend) {
|
|
|
4508
4557
|
const hasNextJs = frontend.includes("next");
|
|
4509
4558
|
const hasNuxt = frontend.includes("nuxt");
|
|
4510
4559
|
const hasSvelte = frontend.includes("svelte");
|
|
4560
|
+
const hasTanstackStart = frontend.includes("tanstack-start");
|
|
4511
4561
|
if (hasNextJs) return "NEXT_PUBLIC_CONVEX_URL";
|
|
4512
4562
|
if (hasNuxt) return "NUXT_PUBLIC_CONVEX_URL";
|
|
4513
4563
|
if (hasSvelte) return "PUBLIC_CONVEX_URL";
|
|
4564
|
+
if (hasTanstackStart) return "VITE_CONVEX_URL";
|
|
4514
4565
|
return "VITE_CONVEX_URL";
|
|
4515
4566
|
}
|
|
4516
4567
|
async function addEnvVariablesToFile(filePath, variables) {
|
|
@@ -4625,7 +4676,8 @@ async function setupEnvironmentVariables(config) {
|
|
|
4625
4676
|
const nativeDir = path.join(projectDir, "apps/native");
|
|
4626
4677
|
if (await fs.pathExists(nativeDir)) {
|
|
4627
4678
|
let envVarName = "EXPO_PUBLIC_SERVER_URL";
|
|
4628
|
-
let serverUrl =
|
|
4679
|
+
let serverUrl = "http://localhost:3000";
|
|
4680
|
+
if (backend === "self") serverUrl = "http://localhost:3001";
|
|
4629
4681
|
if (backend === "convex") {
|
|
4630
4682
|
envVarName = "EXPO_PUBLIC_CONVEX_URL";
|
|
4631
4683
|
serverUrl = "https://<YOUR_CONVEX_URL>";
|
|
@@ -6397,7 +6449,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6397
6449
|
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6398
6450
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6399
6451
|
const lintingInstructions = hasHuskyOrBiome ? getLintingInstructions(runCmd) : "";
|
|
6400
|
-
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf) : "";
|
|
6452
|
+
const nativeInstructions = frontend?.includes("native-nativewind") || frontend?.includes("native-unistyles") ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
|
|
6401
6453
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6402
6454
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
6403
6455
|
const clerkInstructions = isConvex && config.auth === "clerk" ? getClerkInstructions() : "";
|
|
@@ -6441,9 +6493,9 @@ async function displayPostInstallInstructions(config) {
|
|
|
6441
6493
|
else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
|
|
6442
6494
|
if (!isConvex && !isBackendSelf) {
|
|
6443
6495
|
output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
|
|
6444
|
-
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
|
|
6496
|
+
if (api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api-reference\n`;
|
|
6445
6497
|
}
|
|
6446
|
-
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/rpc/api\n`;
|
|
6498
|
+
if (isBackendSelf && api === "orpc") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:${webPort}/api/rpc/api-reference\n`;
|
|
6447
6499
|
if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
|
|
6448
6500
|
if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
|
|
6449
6501
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
@@ -6463,7 +6515,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6463
6515
|
output += pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack");
|
|
6464
6516
|
consola$1.box(output);
|
|
6465
6517
|
}
|
|
6466
|
-
function getNativeInstructions(isConvex, isBackendSelf) {
|
|
6518
|
+
function getNativeInstructions(isConvex, isBackendSelf, _frontend) {
|
|
6467
6519
|
const envVar = isConvex ? "EXPO_PUBLIC_CONVEX_URL" : "EXPO_PUBLIC_SERVER_URL";
|
|
6468
6520
|
const exampleUrl = isConvex ? "https://<YOUR_CONVEX_URL>" : isBackendSelf ? "http://<YOUR_LOCAL_IP>:3001" : "http://<YOUR_LOCAL_IP>:3000";
|
|
6469
6521
|
const envFileName = ".env";
|
|
@@ -6578,28 +6630,34 @@ async function setupWorkspaceDependencies(projectDir, options) {
|
|
|
6578
6630
|
projectDir: dbPackageDir
|
|
6579
6631
|
});
|
|
6580
6632
|
const authPackageDir = path.join(projectDir, "packages/auth");
|
|
6581
|
-
if (await fs.pathExists(authPackageDir))
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6633
|
+
if (await fs.pathExists(authPackageDir)) {
|
|
6634
|
+
const authDeps = {};
|
|
6635
|
+
if (options.database !== "none" && await fs.pathExists(dbPackageDir)) authDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6636
|
+
await addPackageDependency({
|
|
6637
|
+
dependencies: commonDeps,
|
|
6638
|
+
devDependencies: commonDevDeps,
|
|
6639
|
+
customDependencies: authDeps,
|
|
6640
|
+
projectDir: authPackageDir
|
|
6641
|
+
});
|
|
6642
|
+
}
|
|
6587
6643
|
const apiPackageDir = path.join(projectDir, "packages/api");
|
|
6588
|
-
if (await fs.pathExists(apiPackageDir))
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6644
|
+
if (await fs.pathExists(apiPackageDir)) {
|
|
6645
|
+
const apiDeps = {};
|
|
6646
|
+
if (options.auth !== "none" && await fs.pathExists(authPackageDir)) apiDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6647
|
+
if (options.database !== "none" && await fs.pathExists(dbPackageDir)) apiDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6648
|
+
await addPackageDependency({
|
|
6649
|
+
dependencies: commonDeps,
|
|
6650
|
+
devDependencies: commonDevDeps,
|
|
6651
|
+
customDependencies: apiDeps,
|
|
6652
|
+
projectDir: apiPackageDir
|
|
6653
|
+
});
|
|
6654
|
+
}
|
|
6597
6655
|
const serverPackageDir = path.join(projectDir, "apps/server");
|
|
6598
6656
|
if (await fs.pathExists(serverPackageDir)) {
|
|
6599
6657
|
const serverDeps = {};
|
|
6600
|
-
if (await fs.pathExists(apiPackageDir)) serverDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6601
|
-
if (await fs.pathExists(authPackageDir)) serverDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6602
|
-
if (await fs.pathExists(dbPackageDir)) serverDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6658
|
+
if (options.api !== "none" && await fs.pathExists(apiPackageDir)) serverDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6659
|
+
if (options.auth !== "none" && await fs.pathExists(authPackageDir)) serverDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6660
|
+
if (options.database !== "none" && await fs.pathExists(dbPackageDir)) serverDeps[`@${projectName}/db`] = workspaceVersion;
|
|
6603
6661
|
await addPackageDependency({
|
|
6604
6662
|
dependencies: commonDeps,
|
|
6605
6663
|
devDependencies: commonDevDeps,
|
|
@@ -6610,8 +6668,8 @@ async function setupWorkspaceDependencies(projectDir, options) {
|
|
|
6610
6668
|
const webPackageDir = path.join(projectDir, "apps/web");
|
|
6611
6669
|
if (await fs.pathExists(webPackageDir)) {
|
|
6612
6670
|
const webDeps = {};
|
|
6613
|
-
if (await fs.pathExists(apiPackageDir)) webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6614
|
-
if (await fs.pathExists(authPackageDir)) webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6671
|
+
if (options.api !== "none" && await fs.pathExists(apiPackageDir)) webDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6672
|
+
if (options.auth !== "none" && await fs.pathExists(authPackageDir)) webDeps[`@${projectName}/auth`] = workspaceVersion;
|
|
6615
6673
|
if (Object.keys(webDeps).length > 0) await addPackageDependency({
|
|
6616
6674
|
customDependencies: webDeps,
|
|
6617
6675
|
projectDir: webPackageDir
|
|
@@ -6620,7 +6678,7 @@ async function setupWorkspaceDependencies(projectDir, options) {
|
|
|
6620
6678
|
const nativePackageDir = path.join(projectDir, "apps/native");
|
|
6621
6679
|
if (await fs.pathExists(nativePackageDir)) {
|
|
6622
6680
|
const nativeDeps = {};
|
|
6623
|
-
if (await fs.pathExists(apiPackageDir)) nativeDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6681
|
+
if (options.api !== "none" && await fs.pathExists(apiPackageDir)) nativeDeps[`@${projectName}/api`] = workspaceVersion;
|
|
6624
6682
|
if (Object.keys(nativeDeps).length > 0) await addPackageDependency({
|
|
6625
6683
|
customDependencies: nativeDeps,
|
|
6626
6684
|
projectDir: nativePackageDir
|
|
@@ -6885,6 +6943,7 @@ async function createProject(options, cliInput) {
|
|
|
6885
6943
|
}
|
|
6886
6944
|
if (options.addons.length > 0 && options.addons[0] !== "none") await setupAddons(options);
|
|
6887
6945
|
if (options.auth && options.auth !== "none") await setupAuth(options);
|
|
6946
|
+
await setupBetterAuthPlugins(projectDir, options);
|
|
6888
6947
|
if (options.payments && options.payments !== "none") await setupPayments(options);
|
|
6889
6948
|
await handleExtras(projectDir, options);
|
|
6890
6949
|
await setupEnvironmentVariables(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.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",
|
|
@@ -35,7 +35,7 @@ async function handleRequest(req: NextRequest) {
|
|
|
35
35
|
if (rpcResult.response) return rpcResult.response;
|
|
36
36
|
|
|
37
37
|
const apiResult = await apiHandler.handle(req, {
|
|
38
|
-
prefix: "/api/rpc/api",
|
|
38
|
+
prefix: "/api/rpc/api-reference",
|
|
39
39
|
context: await createContext(req),
|
|
40
40
|
});
|
|
41
41
|
if (apiResult.response) return apiResult.response;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createContext } from "@{{projectName}}/api/context";
|
|
2
|
+
import { appRouter } from "@{{projectName}}/api/routers/index";
|
|
3
|
+
import { OpenAPIHandler } from "@orpc/openapi/fetch";
|
|
4
|
+
import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
|
|
5
|
+
import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
|
|
6
|
+
import { RPCHandler } from "@orpc/server/fetch";
|
|
7
|
+
import { onError } from "@orpc/server";
|
|
8
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
9
|
+
|
|
10
|
+
const rpcHandler = new RPCHandler(appRouter, {
|
|
11
|
+
interceptors: [
|
|
12
|
+
onError((error) => {
|
|
13
|
+
console.error(error);
|
|
14
|
+
}),
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const apiHandler = new OpenAPIHandler(appRouter, {
|
|
19
|
+
plugins: [
|
|
20
|
+
new OpenAPIReferencePlugin({
|
|
21
|
+
schemaConverters: [new ZodToJsonSchemaConverter()],
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
interceptors: [
|
|
25
|
+
onError((error) => {
|
|
26
|
+
console.error(error);
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function handle({ request }: { request: Request }) {
|
|
32
|
+
const rpcResult = await rpcHandler.handle(request, {
|
|
33
|
+
prefix: "/api/rpc",
|
|
34
|
+
context: await createContext({ req: request }),
|
|
35
|
+
});
|
|
36
|
+
if (rpcResult.response) return rpcResult.response;
|
|
37
|
+
|
|
38
|
+
const apiResult = await apiHandler.handle(request, {
|
|
39
|
+
prefix: "/api/rpc/api-reference",
|
|
40
|
+
context: await createContext({ req: request }),
|
|
41
|
+
});
|
|
42
|
+
if (apiResult.response) return apiResult.response;
|
|
43
|
+
|
|
44
|
+
return new Response("Not found", { status: 404 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Route = createFileRoute('/api/rpc/$')({
|
|
48
|
+
server: {
|
|
49
|
+
handlers: {
|
|
50
|
+
HEAD: handle,
|
|
51
|
+
GET: handle,
|
|
52
|
+
POST: handle,
|
|
53
|
+
PUT: handle,
|
|
54
|
+
PATCH: handle,
|
|
55
|
+
DELETE: handle,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
})
|
|
@@ -17,6 +17,24 @@ export async function createContext(req: NextRequest) {
|
|
|
17
17
|
{{/if}}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
{{else if (and (eq backend 'self') (includes frontend "tanstack-start"))}}
|
|
21
|
+
{{#if (eq auth "better-auth")}}
|
|
22
|
+
import { auth } from "@{{projectName}}/auth";
|
|
23
|
+
{{/if}}
|
|
24
|
+
|
|
25
|
+
export async function createContext({ req }: { req: Request }) {
|
|
26
|
+
{{#if (eq auth "better-auth")}}
|
|
27
|
+
const session = await auth.api.getSession({
|
|
28
|
+
headers: req.headers,
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
session,
|
|
32
|
+
};
|
|
33
|
+
{{else}}
|
|
34
|
+
return {};
|
|
35
|
+
{{/if}}
|
|
36
|
+
}
|
|
37
|
+
|
|
20
38
|
{{else if (eq backend 'hono')}}
|
|
21
39
|
import type { Context as HonoContext } from "hono";
|
|
22
40
|
{{#if (eq auth "better-auth")}}
|
|
@@ -3,7 +3,15 @@ import { RPCLink } from "@orpc/client/fetch";
|
|
|
3
3
|
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
|
|
4
4
|
import { QueryCache, QueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { toast } from "sonner";
|
|
6
|
+
{{#if (includes frontend "tanstack-start")}}
|
|
7
|
+
import { createRouterClient } from "@orpc/server";
|
|
8
|
+
import type { RouterClient } from "@orpc/server";
|
|
9
|
+
import { createIsomorphicFn } from "@tanstack/react-start";
|
|
10
|
+
import { appRouter } from "@{{projectName}}/api/routers/index";
|
|
11
|
+
import { createContext } from "@{{projectName}}/api/context";
|
|
12
|
+
{{else}}
|
|
6
13
|
import type { AppRouterClient } from "@{{projectName}}/api/routers/index";
|
|
14
|
+
{{/if}}
|
|
7
15
|
|
|
8
16
|
export const queryClient = new QueryClient({
|
|
9
17
|
queryCache: new QueryCache({
|
|
@@ -20,6 +28,33 @@ export const queryClient = new QueryClient({
|
|
|
20
28
|
}),
|
|
21
29
|
});
|
|
22
30
|
|
|
31
|
+
{{#if (includes frontend "tanstack-start")}}
|
|
32
|
+
const getORPCClient = createIsomorphicFn()
|
|
33
|
+
.server(() =>
|
|
34
|
+
createRouterClient(appRouter, {
|
|
35
|
+
context: async ({ req }) => {
|
|
36
|
+
return createContext({ req });
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
.client((): RouterClient<typeof appRouter> => {
|
|
41
|
+
const link = new RPCLink({
|
|
42
|
+
url: {{#if (eq backend "self")}}`${window.location.origin}/api/rpc`{{else}}`${import.meta.env.VITE_SERVER_URL}/rpc`{{/if}},
|
|
43
|
+
{{#if (eq auth "better-auth")}}
|
|
44
|
+
fetch(url, options) {
|
|
45
|
+
return fetch(url, {
|
|
46
|
+
...options,
|
|
47
|
+
credentials: "include",
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
{{/if}}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return createORPCClient(link);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const client: RouterClient<typeof appRouter> = getORPCClient();
|
|
57
|
+
{{else}}
|
|
23
58
|
export const link = new RPCLink({
|
|
24
59
|
{{#if (and (eq backend "self") (includes frontend "next"))}}
|
|
25
60
|
url: `${typeof window !== "undefined" ? window.location.origin : "http://localhost:3001"}/api/rpc`,
|
|
@@ -49,5 +84,6 @@ export const link = new RPCLink({
|
|
|
49
84
|
});
|
|
50
85
|
|
|
51
86
|
export const client: AppRouterClient = createORPCClient(link)
|
|
87
|
+
{{/if}}
|
|
52
88
|
|
|
53
89
|
export const orpc = createTanstackQueryUtils(client)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
|
|
2
|
+
import { appRouter } from '@{{projectName}}/api/routers/index'
|
|
3
|
+
import { createContext } from '@{{projectName}}/api/context'
|
|
4
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
5
|
+
|
|
6
|
+
function handler({ request }: { request: Request }) {
|
|
7
|
+
return fetchRequestHandler({
|
|
8
|
+
req: request,
|
|
9
|
+
router: appRouter,
|
|
10
|
+
createContext,
|
|
11
|
+
endpoint: '/api/trpc',
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Route = createFileRoute('/api/trpc/$')({
|
|
16
|
+
server: {
|
|
17
|
+
handlers: {
|
|
18
|
+
GET: handler,
|
|
19
|
+
POST: handler,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
@@ -20,6 +20,27 @@ export async function createContext(req: NextRequest) {
|
|
|
20
20
|
{{/if}}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
{{else if (and (eq backend 'self') (includes frontend "tanstack-start"))}}
|
|
24
|
+
{{#if (eq auth "better-auth")}}
|
|
25
|
+
import { auth } from "@{{projectName}}/auth";
|
|
26
|
+
{{/if}}
|
|
27
|
+
|
|
28
|
+
export async function createContext({ req }: { req: Request }) {
|
|
29
|
+
{{#if (eq auth "better-auth")}}
|
|
30
|
+
const session = await auth.api.getSession({
|
|
31
|
+
headers: req.headers,
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
session,
|
|
35
|
+
};
|
|
36
|
+
{{else}}
|
|
37
|
+
// No auth configured
|
|
38
|
+
return {
|
|
39
|
+
session: null,
|
|
40
|
+
};
|
|
41
|
+
{{/if}}
|
|
42
|
+
}
|
|
43
|
+
|
|
23
44
|
{{else if (eq backend 'hono')}}
|
|
24
45
|
import type { Context as HonoContext } from "hono";
|
|
25
46
|
{{#if (eq auth "better-auth")}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { auth } from '@{{projectName}}/auth'
|
|
2
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/api/auth/$')({
|
|
5
|
+
server: {
|
|
6
|
+
handlers: {
|
|
7
|
+
GET: ({ request }) => {
|
|
8
|
+
return auth.handler(request)
|
|
9
|
+
},
|
|
10
|
+
POST: ({ request }) => {
|
|
11
|
+
return auth.handler(request)
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
})
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
{{#if (eq orm "prisma")}}
|
|
2
2
|
import { betterAuth } from "better-auth";
|
|
3
3
|
import { prismaAdapter } from "better-auth/adapters/prisma";
|
|
4
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
5
|
-
import { expo } from "@better-auth/expo";
|
|
6
|
-
{{/if}}
|
|
7
4
|
{{#if (eq payments "polar")}}
|
|
8
5
|
import { polar, checkout, portal } from "@polar-sh/better-auth";
|
|
9
6
|
import { polarClient } from "./lib/payments";
|
|
@@ -26,6 +23,7 @@ export const auth = betterAuth({
|
|
|
26
23
|
emailAndPassword: {
|
|
27
24
|
enabled: true,
|
|
28
25
|
},
|
|
26
|
+
{{#if (ne backend "self")}}
|
|
29
27
|
advanced: {
|
|
30
28
|
defaultCookieAttributes: {
|
|
31
29
|
sameSite: "none",
|
|
@@ -33,6 +31,7 @@ export const auth = betterAuth({
|
|
|
33
31
|
httpOnly: true,
|
|
34
32
|
},
|
|
35
33
|
},
|
|
34
|
+
{{/if}}
|
|
36
35
|
{{#if (eq payments "polar")}}
|
|
37
36
|
plugins: [
|
|
38
37
|
polar({
|
|
@@ -53,14 +52,7 @@ export const auth = betterAuth({
|
|
|
53
52
|
portal(),
|
|
54
53
|
],
|
|
55
54
|
}),
|
|
56
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
57
|
-
expo(),
|
|
58
|
-
{{/if}}
|
|
59
55
|
],
|
|
60
|
-
{{else}}
|
|
61
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
62
|
-
plugins: [expo()],
|
|
63
|
-
{{/if}}
|
|
64
56
|
{{/if}}
|
|
65
57
|
});
|
|
66
58
|
{{/if}}
|
|
@@ -69,9 +61,6 @@ export const auth = betterAuth({
|
|
|
69
61
|
{{#if (or (eq runtime "bun") (eq runtime "node") (eq runtime "none"))}}
|
|
70
62
|
import { betterAuth } from "better-auth";
|
|
71
63
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
72
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
73
|
-
import { expo } from "@better-auth/expo";
|
|
74
|
-
{{/if}}
|
|
75
64
|
{{#if (eq payments "polar")}}
|
|
76
65
|
import { polar, checkout, portal } from "@polar-sh/better-auth";
|
|
77
66
|
import { polarClient } from "./lib/payments";
|
|
@@ -95,6 +84,7 @@ export const auth = betterAuth({
|
|
|
95
84
|
emailAndPassword: {
|
|
96
85
|
enabled: true,
|
|
97
86
|
},
|
|
87
|
+
{{#if (ne backend "self")}}
|
|
98
88
|
advanced: {
|
|
99
89
|
defaultCookieAttributes: {
|
|
100
90
|
sameSite: "none",
|
|
@@ -102,6 +92,7 @@ export const auth = betterAuth({
|
|
|
102
92
|
httpOnly: true,
|
|
103
93
|
},
|
|
104
94
|
},
|
|
95
|
+
{{/if}}
|
|
105
96
|
{{#if (eq payments "polar")}}
|
|
106
97
|
plugins: [
|
|
107
98
|
polar({
|
|
@@ -122,14 +113,7 @@ export const auth = betterAuth({
|
|
|
122
113
|
portal(),
|
|
123
114
|
],
|
|
124
115
|
}),
|
|
125
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
126
|
-
expo(),
|
|
127
|
-
{{/if}}
|
|
128
116
|
],
|
|
129
|
-
{{else}}
|
|
130
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
131
|
-
plugins: [expo()],
|
|
132
|
-
{{/if}}
|
|
133
117
|
{{/if}}
|
|
134
118
|
});
|
|
135
119
|
{{/if}}
|
|
@@ -137,9 +121,6 @@ export const auth = betterAuth({
|
|
|
137
121
|
{{#if (eq runtime "workers")}}
|
|
138
122
|
import { betterAuth } from "better-auth";
|
|
139
123
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
140
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
141
|
-
import { expo } from "@better-auth/expo";
|
|
142
|
-
{{/if}}
|
|
143
124
|
{{#if (eq payments "polar")}}
|
|
144
125
|
import { polar, checkout, portal } from "@polar-sh/better-auth";
|
|
145
126
|
import { polarClient } from "./lib/payments";
|
|
@@ -207,10 +188,6 @@ export const auth = betterAuth({
|
|
|
207
188
|
],
|
|
208
189
|
}),
|
|
209
190
|
],
|
|
210
|
-
{{else}}
|
|
211
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
212
|
-
plugins: [expo()],
|
|
213
|
-
{{/if}}
|
|
214
191
|
{{/if}}
|
|
215
192
|
});
|
|
216
193
|
{{/if}}
|
|
@@ -219,9 +196,6 @@ export const auth = betterAuth({
|
|
|
219
196
|
{{#if (eq orm "mongoose")}}
|
|
220
197
|
import { betterAuth } from "better-auth";
|
|
221
198
|
import { mongodbAdapter } from "better-auth/adapters/mongodb";
|
|
222
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
223
|
-
import { expo } from "@better-auth/expo";
|
|
224
|
-
{{/if}}
|
|
225
199
|
{{#if (eq payments "polar")}}
|
|
226
200
|
import { polar, checkout, portal } from "@polar-sh/better-auth";
|
|
227
201
|
import { polarClient } from "./lib/payments";
|
|
@@ -239,6 +213,7 @@ export const auth = betterAuth({
|
|
|
239
213
|
emailAndPassword: {
|
|
240
214
|
enabled: true,
|
|
241
215
|
},
|
|
216
|
+
{{#if (ne backend "self")}}
|
|
242
217
|
advanced: {
|
|
243
218
|
defaultCookieAttributes: {
|
|
244
219
|
sameSite: "none",
|
|
@@ -246,6 +221,7 @@ export const auth = betterAuth({
|
|
|
246
221
|
httpOnly: true,
|
|
247
222
|
},
|
|
248
223
|
},
|
|
224
|
+
{{/if}}
|
|
249
225
|
{{#if (eq payments "polar")}}
|
|
250
226
|
plugins: [
|
|
251
227
|
polar({
|
|
@@ -266,23 +242,13 @@ export const auth = betterAuth({
|
|
|
266
242
|
portal(),
|
|
267
243
|
],
|
|
268
244
|
}),
|
|
269
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
270
|
-
expo(),
|
|
271
|
-
{{/if}}
|
|
272
245
|
],
|
|
273
|
-
{{else}}
|
|
274
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
275
|
-
plugins: [expo()],
|
|
276
|
-
{{/if}}
|
|
277
246
|
{{/if}}
|
|
278
247
|
});
|
|
279
248
|
{{/if}}
|
|
280
249
|
|
|
281
250
|
{{#if (eq orm "none")}}
|
|
282
251
|
import { betterAuth } from "better-auth";
|
|
283
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
284
|
-
import { expo } from "@better-auth/expo";
|
|
285
|
-
{{/if}}
|
|
286
252
|
{{#if (eq payments "polar")}}
|
|
287
253
|
import { polar, checkout, portal } from "@polar-sh/better-auth";
|
|
288
254
|
import { polarClient } from "./lib/payments";
|
|
@@ -299,6 +265,7 @@ export const auth = betterAuth({
|
|
|
299
265
|
emailAndPassword: {
|
|
300
266
|
enabled: true,
|
|
301
267
|
},
|
|
268
|
+
{{#if (ne backend "self")}}
|
|
302
269
|
advanced: {
|
|
303
270
|
defaultCookieAttributes: {
|
|
304
271
|
sameSite: "none",
|
|
@@ -306,6 +273,7 @@ export const auth = betterAuth({
|
|
|
306
273
|
httpOnly: true,
|
|
307
274
|
},
|
|
308
275
|
},
|
|
276
|
+
{{/if}}
|
|
309
277
|
{{#if (eq payments "polar")}}
|
|
310
278
|
plugins: [
|
|
311
279
|
polar({
|
|
@@ -326,14 +294,7 @@ export const auth = betterAuth({
|
|
|
326
294
|
portal(),
|
|
327
295
|
],
|
|
328
296
|
}),
|
|
329
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
330
|
-
expo(),
|
|
331
|
-
{{/if}}
|
|
332
297
|
],
|
|
333
|
-
{{else}}
|
|
334
|
-
{{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
|
|
335
|
-
plugins: [expo()],
|
|
336
|
-
{{/if}}
|
|
337
298
|
{{/if}}
|
|
338
299
|
});
|
|
339
|
-
{{/if}}
|
|
300
|
+
{{/if}}
|
|
@@ -82,7 +82,7 @@ const app = new Elysia()
|
|
|
82
82
|
})
|
|
83
83
|
.all('/api*', async (context) => {
|
|
84
84
|
const { response } = await apiHandler.handle(context.request, {
|
|
85
|
-
prefix: '/api',
|
|
85
|
+
prefix: '/api-reference',
|
|
86
86
|
context: await createContext({ context })
|
|
87
87
|
})
|
|
88
88
|
return response ?? new Response('Not Found', { status: 404 })
|
|
@@ -86,7 +86,7 @@ app.use(async (req, res, next) => {
|
|
|
86
86
|
if (rpcResult.matched) return;
|
|
87
87
|
|
|
88
88
|
const apiResult = await apiHandler.handle(req, res, {
|
|
89
|
-
prefix: "/api",
|
|
89
|
+
prefix: "/api-reference",
|
|
90
90
|
{{#if (eq auth "better-auth")}}
|
|
91
91
|
context: await createContext({ req }),
|
|
92
92
|
{{else}}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { google } from "@ai-sdk/google";
|
|
3
|
+
import { streamText, type UIMessage, convertToModelMessages } from "ai";
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute("/api/ai/$")({
|
|
6
|
+
server: {
|
|
7
|
+
handlers: {
|
|
8
|
+
POST: async ({ request }) => {
|
|
9
|
+
try {
|
|
10
|
+
const { messages }: { messages: UIMessage[] } = await request.json();
|
|
11
|
+
|
|
12
|
+
const result = streamText({
|
|
13
|
+
model: google("gemini-2.5-flash"),
|
|
14
|
+
messages: convertToModelMessages(messages),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return result.toUIMessageStreamResponse();
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("AI API error:", error);
|
|
20
|
+
return new Response(
|
|
21
|
+
JSON.stringify({ error: "Failed to process AI request" }),
|
|
22
|
+
{
|
|
23
|
+
status: 500,
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
@@ -15,7 +15,7 @@ function RouteComponent() {
|
|
|
15
15
|
const [input, setInput] = useState("");
|
|
16
16
|
const { messages, sendMessage } = useChat({
|
|
17
17
|
transport: new DefaultChatTransport({
|
|
18
|
-
api: `${import.meta.env.VITE_SERVER_URL}/ai
|
|
18
|
+
api: {{#if (eq backend "self")}}"/api/ai"{{else}}`${import.meta.env.VITE_SERVER_URL}/ai`{{/if}},
|
|
19
19
|
}),
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -85,7 +85,7 @@ export const queryClient = new QueryClient({
|
|
|
85
85
|
const trpcClient = createTRPCClient<AppRouter>({
|
|
86
86
|
links: [
|
|
87
87
|
httpBatchLink({
|
|
88
|
-
url: `${import.meta.env.VITE_SERVER_URL}/trpc
|
|
88
|
+
url: {{#if (eq backend "self")}}"/api/trpc"{{else}}`${import.meta.env.VITE_SERVER_URL}/trpc`{{/if}},
|
|
89
89
|
{{#if (eq auth "better-auth")}}
|
|
90
90
|
fetch(url, options) {
|
|
91
91
|
return fetch(url, {
|