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