diff --git a/apps/cli/package.json b/apps/cli/package.json index d2317aa..ec695ba 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,88 +1,88 @@ { - "name": "create-better-t-stack", - "version": "2.42.0", - "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations", - "type": "module", - "license": "MIT", - "author": "Aman Varshney", - "bin": { - "create-better-t-stack": "dist/cli.js" - }, - "files": [ - "templates", - "dist" - ], - "keywords": [ - "better-t-stack", - "typescript", - "boilerplate", - "starter", - "cli", - "turborepo", - "trpc", - "better-auth", - "monorepo", - "fullstack", - "type-safety", - "react", - "react-native", - "expo", - "hono", - "elysia", - "drizzle", - "prisma", - "tanstack", - "tailwind", - "shadcn", - "pwa", - "tauri", - "biome" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/AmanVarshney01/create-better-t-stack.git", - "directory": "apps/cli" - }, - "publishConfig": { - "access": "public" - }, - "homepage": "https://better-t-stack.dev/", - "scripts": { - "build": "tsdown", - "dev": "tsdown --watch", - "check-types": "tsc --noEmit", - "check": "biome check --write .", - "test": "bun run build && vitest run", - "test:ui": "bun run build && vitest --ui", - "test:with-build": "bun run build && WITH_BUILD=1 vitest --ui", - "prepublishOnly": "npm run build" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js" - } - }, - "dependencies": { - "@clack/prompts": "^1.0.0-alpha.4", - "consola": "^3.4.2", - "execa": "^9.6.0", - "fs-extra": "^11.3.1", - "gradient-string": "^3.0.0", - "handlebars": "^4.7.8", - "jsonc-parser": "^3.3.1", - "picocolors": "^1.1.1", - "tinyglobby": "^0.2.15", - "trpc-cli": "^0.10.2", - "ts-morph": "^27.0.0", - "zod": "^4.1.5" - }, - "devDependencies": { - "@types/fs-extra": "^11.0.4", - "@types/node": "^24.3.1", - "@vitest/ui": "^3.2.4", - "tsdown": "^0.14.2", - "typescript": "^5.9.2", - "vitest": "^3.2.4" - } + "name": "create-better-t-stack", + "version": "2.42.0", + "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations", + "type": "module", + "license": "MIT", + "author": "Aman Varshney", + "bin": { + "create-better-t-stack": "dist/cli.js" + }, + "files": [ + "templates", + "dist" + ], + "keywords": [ + "better-t-stack", + "typescript", + "boilerplate", + "starter", + "cli", + "turborepo", + "trpc", + "better-auth", + "monorepo", + "fullstack", + "type-safety", + "react", + "react-native", + "expo", + "hono", + "elysia", + "drizzle", + "prisma", + "tanstack", + "tailwind", + "shadcn", + "pwa", + "tauri", + "biome" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/AmanVarshney01/create-better-t-stack.git", + "directory": "apps/cli" + }, + "publishConfig": { + "access": "public" + }, + "homepage": "https://better-t-stack.dev/", + "scripts": { + "build": "tsdown", + "dev": "tsdown --watch", + "check-types": "tsc --noEmit", + "check": "biome check --write .", + "test": "bun run build && vitest run", + "test:ui": "bun run build && vitest --ui", + "test:with-build": "bun run build && WITH_BUILD=1 vitest --ui", + "prepublishOnly": "npm run build" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "dependencies": { + "@clack/prompts": "^1.0.0-alpha.4", + "consola": "^3.4.2", + "execa": "^9.6.0", + "fs-extra": "^11.3.1", + "gradient-string": "^3.0.0", + "handlebars": "^4.7.8", + "jsonc-parser": "^3.3.1", + "picocolors": "^1.1.1", + "tinyglobby": "^0.2.15", + "trpc-cli": "^0.10.2", + "ts-morph": "^27.0.0", + "zod": "^4.1.5" + }, + "devDependencies": { + "@types/fs-extra": "^11.0.4", + "@types/node": "^24.3.1", + "@vitest/ui": "^3.2.4", + "tsdown": "^0.14.2", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } } diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 0dc1392..4dc2f07 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -92,7 +92,7 @@ export const dependencyVersionMap = { "@elysiajs/cors": "^1.3.3", "@elysiajs/trpc": "^1.1.0", - elysia: "^1.3.20", + "elysia": "^1.3.21", "@hono/node-server": "^1.14.4", "@hono/trpc-server": "^0.4.0", @@ -108,12 +108,12 @@ export const dependencyVersionMap = { turbo: "^2.5.4", - ai: "^5.0.9", - "@ai-sdk/google": "^2.0.3", - "@ai-sdk/vue": "^2.0.9", - "@ai-sdk/svelte": "^3.0.9", - "@ai-sdk/react": "^2.0.9", - streamdown: "^1.1.6", + "ai": "^5.0.39", + "@ai-sdk/google": "^2.0.13", + "@ai-sdk/vue": "^2.0.39", + "@ai-sdk/svelte": "^3.0.39", + "@ai-sdk/react": "^2.0.39", + streamdown: "^1.2.0", "@orpc/server": "^1.8.6", "@orpc/client": "^1.8.6", diff --git a/apps/cli/src/prompts/examples.ts b/apps/cli/src/prompts/examples.ts index 4206af4..bd0113a 100644 --- a/apps/cli/src/prompts/examples.ts +++ b/apps/cli/src/prompts/examples.ts @@ -33,10 +33,6 @@ export async function getExamplesChoice( if (database === "none") return []; - const noFrontendSelected = !frontends || frontends.length === 0; - - if (noFrontendSelected) return []; - let response: Examples[] | symbol = []; const options: { value: Examples; label: string; hint: string }[] = []; diff --git a/apps/cli/src/utils/compatibility-rules.ts b/apps/cli/src/utils/compatibility-rules.ts index 2387e9a..3186db1 100644 --- a/apps/cli/src/utils/compatibility-rules.ts +++ b/apps/cli/src/utils/compatibility-rules.ts @@ -157,11 +157,10 @@ export function isExampleTodoAllowed( } export function isExampleAIAllowed( - backend?: ProjectConfig["backend"], + _backend?: ProjectConfig["backend"], frontends: Frontend[] = [], ) { const includesSolid = frontends.includes("solid"); - if (backend === "elysia") return false; if (includesSolid) return false; return true; } @@ -226,11 +225,6 @@ export function validateExamplesCompatibility( "The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.", ); } - if (examplesArr.includes("ai") && backend === "elysia") { - exitWithError( - "The 'ai' example is not compatible with the Elysia backend.", - ); - } if (examplesArr.includes("ai") && (frontend ?? []).includes("solid")) { exitWithError( "The 'ai' example is not compatible with the Solid frontend.", diff --git a/apps/cli/templates/backend/server/elysia/src/index.ts.hbs b/apps/cli/templates/backend/server/elysia/src/index.ts.hbs index 853b0c2..21b1fef 100644 --- a/apps/cli/templates/backend/server/elysia/src/index.ts.hbs +++ b/apps/cli/templates/backend/server/elysia/src/index.ts.hbs @@ -4,6 +4,10 @@ import { node } from "@elysiajs/node"; {{/if}} import { Elysia } from "elysia"; import { cors } from "@elysiajs/cors"; +{{#if (includes examples "ai")}} +import { google } from "@ai-sdk/google"; +import { convertToModelMessages, streamText } from "ai"; +{{/if}} {{#if (eq api "trpc")}} import { createContext } from "./lib/context"; import { appRouter } from "./routers/index"; @@ -94,6 +98,18 @@ const app = new Elysia() }); return res; }) +{{/if}} +{{#if (includes examples "ai")}} + .post("/ai", async (context) => { + const body = await context.request.json(); + const uiMessages = body.messages || []; + const result = streamText({ + model: google("gemini-2.0-flash"), + messages: convertToModelMessages(uiMessages) + }); + + return result.toUIMessageStreamResponse(); + }) {{/if}} .get("/", () => "OK") .listen(3000, () => { diff --git a/apps/cli/templates/backend/server/fastify/src/index.ts.hbs b/apps/cli/templates/backend/server/fastify/src/index.ts.hbs index b247f27..79c88c1 100644 --- a/apps/cli/templates/backend/server/fastify/src/index.ts.hbs +++ b/apps/cli/templates/backend/server/fastify/src/index.ts.hbs @@ -158,15 +158,14 @@ interface AiRequestBody { messages: UIMessage[]; } -fastify.post('/ai', async function (request, reply) { - // there are some issues with the ai sdk and fastify, docs: https://ai-sdk.dev/cookbook/api-servers/fastify +fastify.post('/ai', async function (request) { const { messages } = request.body as AiRequestBody; const result = streamText({ model: google('gemini-1.5-flash'), messages: convertToModelMessages(messages), }); - return result.pipeUIMessageStreamToResponse(reply.raw); + return result.toUIMessageStreamResponse(); }); {{/if}} diff --git a/apps/web/src/app/(home)/new/_components/utils.ts b/apps/web/src/app/(home)/new/_components/utils.ts index df0db3a..7f970cc 100644 --- a/apps/web/src/app/(home)/new/_components/utils.ts +++ b/apps/web/src/app/(home)/new/_components/utils.ts @@ -912,13 +912,6 @@ export const analyzeStackCompatibility = ( "Todo example removed (requires a database but 'None' was selected)", }); } - if (nextStack.backend === "elysia" && nextStack.examples.includes("ai")) { - incompatibleExamples.push("ai"); - changes.push({ - category: "examples", - message: "AI example removed (not compatible with Elysia backend)", - }); - } if (isSolid && nextStack.examples.includes("ai")) { incompatibleExamples.push("ai"); changes.push({ @@ -942,19 +935,6 @@ export const analyzeStackCompatibility = ( notes.database.hasIssue = true; notes.examples.hasIssue = true; } - if ( - nextStack.backend === "elysia" && - uniqueIncompatibleExamples.includes("ai") - ) { - notes.backend.notes.push( - "AI example is not compatible with Elysia. It will be removed.", - ); - notes.examples.notes.push( - "AI example is not compatible with Elysia. It will be removed.", - ); - notes.backend.hasIssue = true; - notes.examples.hasIssue = true; - } if (isSolid && uniqueIncompatibleExamples.includes("ai")) { notes.webFrontend.notes.push( "AI example is not compatible with Solid. It will be removed.", @@ -1551,9 +1531,6 @@ export const getDisabledReason = ( } if (category === "examples" && optionId === "ai") { - if (finalStack.backend === "elysia") { - return "AI example is not compatible with Elysia backend. Try Hono, Express, or Fastify."; - } if (finalStack.webFrontend.includes("solid")) { return "AI example is not compatible with Solid frontend. Try React-based frontends."; }