-
- Powered by
-
- AI Gateway
-
-
diff --git a/app/(auth)/login/login-form.tsx b/app/(auth)/login/login-form.tsx
new file mode 100644
index 0000000000..b848c13776
--- /dev/null
+++ b/app/(auth)/login/login-form.tsx
@@ -0,0 +1,49 @@
+"use client";
+
+import { LogIn } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { signIn, useSession } from "next-auth/react";
+import { useEffect, useState } from "react";
+import { Button } from "@/components/ui/button";
+
+export function LoginForm({
+ isEntraConfigured,
+}: {
+ isEntraConfigured: boolean;
+}) {
+ const router = useRouter();
+ const { status } = useSession();
+ const [isRedirecting, setIsRedirecting] = useState(false);
+
+ useEffect(() => {
+ if (status === "authenticated") {
+ router.replace("/");
+ }
+ }, [router, status]);
+
+ return (
+ <>
+
Sign in
+
+ {isEntraConfigured
+ ? "Use your Microsoft Entra ID account to continue"
+ : "Microsoft Entra ID is not configured for this environment."}
+
+
{
+ if (!isEntraConfigured) {
+ return;
+ }
+ setIsRedirecting(true);
+ signIn("microsoft-entra-id", { callbackUrl: "/" });
+ }}
+ type="button"
+ >
+
+ {isRedirecting ? "Redirecting..." : "Continue with Microsoft"}
+
+ >
+ );
+}
diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx
index ea4c602eeb..cd61f0e2e7 100644
--- a/app/(auth)/login/page.tsx
+++ b/app/(auth)/login/page.tsx
@@ -1,66 +1,6 @@
-"use client";
-
-import Link from "next/link";
-import { useRouter } from "next/navigation";
-import { useSession } from "next-auth/react";
-import { useActionState, useEffect, useState } from "react";
-
-import { AuthForm } from "@/components/chat/auth-form";
-import { SubmitButton } from "@/components/chat/submit-button";
-import { toast } from "@/components/chat/toast";
-import { type LoginActionState, login } from "../actions";
+import { hasEntraAuthConfig } from "@/lib/auth-mode";
+import { LoginForm } from "./login-form";
export default function Page() {
- const router = useRouter();
- const [email, setEmail] = useState("");
- const [isSuccessful, setIsSuccessful] = useState(false);
-
- const [state, formAction] = useActionState
(
- login,
- { status: "idle" }
- );
-
- const { update: updateSession } = useSession();
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: router and updateSession are stable refs
- useEffect(() => {
- if (state.status === "failed") {
- toast({ type: "error", description: "Invalid credentials!" });
- } else if (state.status === "invalid_data") {
- toast({
- type: "error",
- description: "Failed validating your submission!",
- });
- } else if (state.status === "success") {
- setIsSuccessful(true);
- updateSession();
- router.refresh();
- }
- }, [state.status]);
-
- const handleSubmit = (formData: FormData) => {
- setEmail(formData.get("email") as string);
- formAction(formData);
- };
-
- return (
- <>
- Welcome back
-
- Sign in to your account to continue
-
-
- Sign in
-
- {"No account? "}
-
- Sign up
-
-
-
- >
- );
+ return ;
}
diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx
index f2abbc8620..63b91e73bf 100644
--- a/app/(auth)/register/page.tsx
+++ b/app/(auth)/register/page.tsx
@@ -1,66 +1,18 @@
-"use client";
-
import Link from "next/link";
-import { useRouter } from "next/navigation";
-import { useSession } from "next-auth/react";
-import { useActionState, useEffect, useState } from "react";
-import { AuthForm } from "@/components/chat/auth-form";
-import { SubmitButton } from "@/components/chat/submit-button";
-import { toast } from "@/components/chat/toast";
-import { type RegisterActionState, register } from "../actions";
+import { Button } from "@/components/ui/button";
export default function Page() {
- const router = useRouter();
- const [email, setEmail] = useState("");
- const [isSuccessful, setIsSuccessful] = useState(false);
-
- const [state, formAction] = useActionState(
- register,
- { status: "idle" }
- );
-
- const { update: updateSession } = useSession();
-
- // biome-ignore lint/correctness/useExhaustiveDependencies: router and updateSession are stable refs
- useEffect(() => {
- if (state.status === "user_exists") {
- toast({ type: "error", description: "Account already exists!" });
- } else if (state.status === "failed") {
- toast({ type: "error", description: "Failed to create account!" });
- } else if (state.status === "invalid_data") {
- toast({
- type: "error",
- description: "Failed validating your submission!",
- });
- } else if (state.status === "success") {
- toast({ type: "success", description: "Account created!" });
- setIsSuccessful(true);
- updateSession();
- router.refresh();
- }
- }, [state.status]);
-
- const handleSubmit = (formData: FormData) => {
- setEmail(formData.get("email") as string);
- formAction(formData);
- };
-
return (
<>
- Create account
- Get started for free
-
- Sign up
-
- {"Have an account? "}
-
- Sign in
-
-
-
+
+ Organization account
+
+
+ Accounts are managed in Microsoft Entra ID.
+
+
+ Sign in with Microsoft
+
>
);
}
diff --git a/app/(chat)/actions.ts b/app/(chat)/actions.ts
deleted file mode 100644
index 2955a53fbc..0000000000
--- a/app/(chat)/actions.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-"use server";
-
-import { generateText, type UIMessage } from "ai";
-import { cookies } from "next/headers";
-import { auth } from "@/app/(auth)/auth";
-import type { VisibilityType } from "@/components/chat/visibility-selector";
-import { titleModel } from "@/lib/ai/models";
-import { titlePrompt } from "@/lib/ai/prompts";
-import { getTitleModel } from "@/lib/ai/providers";
-import {
- deleteMessagesByChatIdAfterTimestamp,
- getChatById,
- getMessageById,
- updateChatVisibilityById,
-} from "@/lib/db/queries";
-import { getTextFromMessage } from "@/lib/utils";
-
-export async function saveChatModelAsCookie(model: string) {
- const cookieStore = await cookies();
- cookieStore.set("chat-model", model);
-}
-
-export async function generateTitleFromUserMessage({
- message,
-}: {
- message: UIMessage;
-}) {
- const { text } = await generateText({
- model: getTitleModel(),
- system: titlePrompt,
- prompt: getTextFromMessage(message),
- providerOptions: {
- gateway: { order: titleModel.gatewayOrder },
- },
- });
- return text
- .replace(/^[#*"\s]+/, "")
- .replace(/["]+$/, "")
- .trim();
-}
-
-export async function deleteTrailingMessages({ id }: { id: string }) {
- const session = await auth();
- if (!session?.user?.id) {
- throw new Error("Unauthorized");
- }
-
- const [message] = await getMessageById({ id });
- if (!message) {
- throw new Error("Message not found");
- }
-
- const chat = await getChatById({ id: message.chatId });
- if (!chat || chat.userId !== session.user.id) {
- throw new Error("Unauthorized");
- }
-
- await deleteMessagesByChatIdAfterTimestamp({
- chatId: message.chatId,
- timestamp: message.createdAt,
- });
-}
-
-export async function updateChatVisibility({
- chatId,
- visibility,
-}: {
- chatId: string;
- visibility: VisibilityType;
-}) {
- const session = await auth();
- if (!session?.user?.id) {
- throw new Error("Unauthorized");
- }
-
- const chat = await getChatById({ id: chatId });
- if (!chat || chat.userId !== session.user.id) {
- throw new Error("Unauthorized");
- }
-
- await updateChatVisibilityById({ chatId, visibility });
-}
diff --git a/app/(chat)/api/chat/[id]/stream/route.ts b/app/(chat)/api/chat/[id]/stream/route.ts
deleted file mode 100644
index 3713ec5dac..0000000000
--- a/app/(chat)/api/chat/[id]/stream/route.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export function GET() {
- return new Response(null, { status: 204 });
-}
diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts
index eb70454bfd..de8e512d73 100644
--- a/app/(chat)/api/chat/route.ts
+++ b/app/(chat)/api/chat/route.ts
@@ -1,61 +1,57 @@
-import { geolocation, ipAddress } from "@vercel/functions";
-import {
- convertToModelMessages,
- createUIMessageStream,
- createUIMessageStreamResponse,
- generateId,
- stepCountIs,
- streamText,
-} from "ai";
-import { checkBotId } from "botid/server";
-import { after } from "next/server";
-import { createResumableStreamContext } from "resumable-stream";
-import { auth, type UserType } from "@/app/(auth)/auth";
-import { entitlementsByUserType } from "@/lib/ai/entitlements";
-import {
- allowedModelIds,
- chatModels,
- DEFAULT_CHAT_MODEL,
- getCapabilities,
-} from "@/lib/ai/models";
-import { type RequestHints, systemPrompt } from "@/lib/ai/prompts";
-import { getLanguageModel } from "@/lib/ai/providers";
-import { createDocument } from "@/lib/ai/tools/create-document";
-import { editDocument } from "@/lib/ai/tools/edit-document";
-import { getWeather } from "@/lib/ai/tools/get-weather";
-import { requestSuggestions } from "@/lib/ai/tools/request-suggestions";
-import { updateDocument } from "@/lib/ai/tools/update-document";
-import { isProductionEnvironment } from "@/lib/constants";
-import {
- createStreamId,
- deleteChatById,
- getChatById,
- getMessageCountByUserId,
- getMessagesByChatId,
- saveChat,
- saveMessages,
- updateChatTitleById,
- updateMessage,
-} from "@/lib/db/queries";
-import type { DBMessage } from "@/lib/db/schema";
+import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
+import { auth } from "@/app/(auth)/auth";
+import { shouldRequireAuth } from "@/lib/auth-mode";
import { ChatbotError } from "@/lib/errors";
-import { checkIpRateLimit } from "@/lib/ratelimit";
-import type { ChatMessage } from "@/lib/types";
-import { convertToUIMessages, generateUUID } from "@/lib/utils";
-import { generateTitleFromUserMessage } from "../../actions";
+import { generateUUID } from "@/lib/utils";
import { type PostRequestBody, postRequestBodySchema } from "./schema";
export const maxDuration = 60;
-function getStreamContext() {
- try {
- return createResumableStreamContext({ waitUntil: after });
- } catch (_) {
- return null;
+type ChatLikeMessage = {
+ role?: string;
+ parts?: Array<{
+ type?: string;
+ text?: unknown;
+ }>;
+};
+
+function extractTextFromParts(parts: ChatLikeMessage["parts"]) {
+ return (
+ parts
+ ?.filter((part) => part.type === "text")
+ .map((part) => (typeof part.text === "string" ? part.text : ""))
+ .join("\n")
+ .trim() ?? ""
+ );
+}
+
+function getUserQuestion({
+ message,
+ messages,
+}: {
+ message?: ChatLikeMessage;
+ messages?: ChatLikeMessage[];
+}) {
+ const directQuestion = extractTextFromParts(message?.parts);
+
+ if (directQuestion) {
+ return directQuestion;
}
+
+ const lastUserMessage = messages
+ ?.filter((currentMessage) => currentMessage.role === "user")
+ .at(-1);
+
+ return extractTextFromParts(lastUserMessage?.parts);
+}
+
+function chunkText(text: string) {
+ return text.match(/\S+\s*/g) ?? [text];
}
-export { getStreamContext };
+function sleep(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
export async function POST(request: Request) {
let requestBody: PostRequestBody;
@@ -67,306 +63,62 @@ export async function POST(request: Request) {
return new ChatbotError("bad_request:api").toResponse();
}
- try {
- const { id, message, messages, selectedChatModel, selectedVisibilityType } =
- requestBody;
-
- const [, session] = await Promise.all([
- checkBotId().catch(() => null),
- auth(),
- ]);
+ if (shouldRequireAuth()) {
+ const session = await auth();
if (!session?.user) {
return new ChatbotError("unauthorized:chat").toResponse();
}
+ }
- const chatModel = allowedModelIds.has(selectedChatModel)
- ? selectedChatModel
- : DEFAULT_CHAT_MODEL;
-
- await checkIpRateLimit(ipAddress(request));
-
- const userType: UserType = session.user.type;
-
- const messageCount = await getMessageCountByUserId({
- id: session.user.id,
- differenceInHours: 1,
- });
-
- if (messageCount > entitlementsByUserType[userType].maxMessagesPerHour) {
- return new ChatbotError("rate_limit:chat").toResponse();
- }
-
- const isToolApprovalFlow = Boolean(messages);
-
- const chat = await getChatById({ id });
- let messagesFromDb: DBMessage[] = [];
- let titlePromise: Promise | null = null;
+ const question = getUserQuestion({
+ message: requestBody.message,
+ messages: requestBody.messages,
+ });
- if (chat) {
- if (chat.userId !== session.user.id) {
- return new ChatbotError("forbidden:chat").toResponse();
- }
- messagesFromDb = await getMessagesByChatId({ id });
- } else if (message?.role === "user") {
- await saveChat({
- id,
- userId: session.user.id,
- title: "New chat",
- visibility: selectedVisibilityType,
- });
- titlePromise = generateTitleFromUserMessage({ message });
- }
+ if (!question) {
+ return new ChatbotError("bad_request:api").toResponse();
+ }
- let uiMessages: ChatMessage[];
+ const stream = createUIMessageStream({
+ generateId: generateUUID,
+ execute: async ({ writer }) => {
+ const textId = generateUUID();
+ const responseText = `UI-only mode is active.
- if (isToolApprovalFlow && messages) {
- const dbMessages = convertToUIMessages(messagesFromDb);
- const approvalStates = new Map(
- messages.flatMap(
- (m) =>
- m.parts
- ?.filter(
- (p: Record) =>
- p.state === "approval-responded" ||
- p.state === "output-denied"
- )
- .map((p: Record) => [
- String(p.toolCallId ?? ""),
- p,
- ]) ?? []
- )
- );
- uiMessages = dbMessages.map((msg) => ({
- ...msg,
- parts: msg.parts.map((part) => {
- if (
- "toolCallId" in part &&
- approvalStates.has(String(part.toolCallId))
- ) {
- return { ...part, ...approvalStates.get(String(part.toolCallId)) };
- }
- return part;
- }),
- })) as ChatMessage[];
- } else {
- uiMessages = [
- ...convertToUIMessages(messagesFromDb),
- message as ChatMessage,
- ];
- }
+I received your message:
- const { longitude, latitude, city, country } = geolocation(request);
+> ${question}
- const requestHints: RequestHints = {
- longitude,
- latitude,
- city,
- country,
- };
+The chat shell, streaming state, markdown rendering, and message layout are working. We will connect this route to your production backend once the backend contract is ready.`;
- if (message?.role === "user") {
- await saveMessages({
- messages: [
- {
- chatId: id,
- id: message.id,
- role: "user",
- parts: message.parts,
- attachments: [],
- createdAt: new Date(),
- },
- ],
+ writer.write({
+ type: "text-start",
+ id: textId,
});
- }
-
- const modelConfig = chatModels.find((m) => m.id === chatModel);
- const modelCapabilities = await getCapabilities();
- const capabilities = modelCapabilities[chatModel];
- const isReasoningModel = capabilities?.reasoning === true;
- const supportsTools = capabilities?.tools === true;
-
- const modelMessages = await convertToModelMessages(uiMessages);
- const stream = createUIMessageStream({
- originalMessages: isToolApprovalFlow ? uiMessages : undefined,
- execute: async ({ writer: dataStream }) => {
- const result = streamText({
- model: getLanguageModel(chatModel),
- system: systemPrompt({ requestHints, supportsTools }),
- messages: modelMessages,
- stopWhen: stepCountIs(5),
- experimental_activeTools:
- isReasoningModel && !supportsTools
- ? []
- : [
- "getWeather",
- "createDocument",
- "editDocument",
- "updateDocument",
- "requestSuggestions",
- ],
- providerOptions: {
- ...(modelConfig?.gatewayOrder && {
- gateway: { order: modelConfig.gatewayOrder },
- }),
- ...(modelConfig?.reasoningEffort && {
- openai: { reasoningEffort: modelConfig.reasoningEffort },
- }),
- },
- tools: {
- getWeather,
- createDocument: createDocument({
- session,
- dataStream,
- modelId: chatModel,
- }),
- editDocument: editDocument({ dataStream, session }),
- updateDocument: updateDocument({
- session,
- dataStream,
- modelId: chatModel,
- }),
- requestSuggestions: requestSuggestions({
- session,
- dataStream,
- modelId: chatModel,
- }),
- },
- experimental_telemetry: {
- isEnabled: isProductionEnvironment,
- functionId: "stream-text",
- },
- });
-
- dataStream.merge(
- result.toUIMessageStream({ sendReasoning: isReasoningModel })
- );
-
- if (titlePromise) {
- try {
- const title = await titlePromise;
- dataStream.write({ type: "data-chat-title", data: title });
- updateChatTitleById({ chatId: id, title });
- } catch (_) {
- /* non-fatal */
- }
- }
- },
- generateId: generateUUID,
- onFinish: async ({ messages: finishedMessages }) => {
- if (isToolApprovalFlow) {
- for (const finishedMsg of finishedMessages) {
- const existingMsg = uiMessages.find((m) => m.id === finishedMsg.id);
- if (existingMsg) {
- await updateMessage({
- id: finishedMsg.id,
- parts: finishedMsg.parts,
- });
- } else {
- await saveMessages({
- messages: [
- {
- id: finishedMsg.id,
- role: finishedMsg.role,
- parts: finishedMsg.parts,
- createdAt: new Date(),
- attachments: [],
- chatId: id,
- },
- ],
- });
- }
- }
- } else if (finishedMessages.length > 0) {
- await saveMessages({
- messages: finishedMessages.map((currentMessage) => ({
- id: currentMessage.id,
- role: currentMessage.role,
- parts: currentMessage.parts,
- createdAt: new Date(),
- attachments: [],
- chatId: id,
- })),
- });
- }
- },
- onError: (error) => {
- if (
- error instanceof Error &&
- error.message?.includes(
- "AI Gateway requires a valid credit card on file to service requests"
- )
- ) {
- return "AI Gateway requires a valid credit card on file to service requests. Please visit https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card to add a card and unlock your free credits.";
- }
- return "Oops, an error occurred!";
- },
- });
-
- return createUIMessageStreamResponse({
- stream,
- async consumeSseStream({ stream: sseStream }) {
- if (!process.env.REDIS_URL) {
- return;
+ for (const delta of chunkText(responseText)) {
+ if (request.signal.aborted) {
+ break;
}
- try {
- const streamContext = getStreamContext();
- if (streamContext) {
- const streamId = generateId();
- await createStreamId({ streamId, chatId: id });
- await streamContext.createNewResumableStream(
- streamId,
- () => sseStream
- );
- }
- } catch (_) {
- /* non-critical */
- }
- },
- });
- } catch (error) {
- const vercelId = request.headers.get("x-vercel-id");
-
- if (error instanceof ChatbotError) {
- return error.toResponse();
- }
-
- if (
- error instanceof Error &&
- error.message?.includes(
- "AI Gateway requires a valid credit card on file to service requests"
- )
- ) {
- return new ChatbotError("bad_request:activate_gateway").toResponse();
- }
-
- console.error("Unhandled error in chat API:", error, { vercelId });
- return new ChatbotError("offline:chat").toResponse();
- }
-}
-
-export async function DELETE(request: Request) {
- const { searchParams } = new URL(request.url);
- const id = searchParams.get("id");
-
- if (!id) {
- return new ChatbotError("bad_request:api").toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:chat").toResponse();
- }
-
- const chat = await getChatById({ id });
-
- if (chat?.userId !== session.user.id) {
- return new ChatbotError("forbidden:chat").toResponse();
- }
- const deletedChat = await deleteChatById({ id });
+ writer.write({
+ type: "text-delta",
+ id: textId,
+ delta,
+ });
+ await sleep(18);
+ }
- return Response.json(deletedChat, { status: 200 });
+ if (!request.signal.aborted) {
+ writer.write({ type: "text-end", id: textId });
+ }
+ },
+ onError: (error) => {
+ console.error("UI-only chat stream error:", error);
+ return "The local UI-only chat stream failed.";
+ },
+ });
+
+ return createUIMessageStreamResponse({ stream });
}
diff --git a/app/(chat)/api/document/route.ts b/app/(chat)/api/document/route.ts
deleted file mode 100644
index ee66e3057f..0000000000
--- a/app/(chat)/api/document/route.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-import { z } from "zod";
-import { auth } from "@/app/(auth)/auth";
-import type { ArtifactKind } from "@/components/chat/artifact";
-import {
- deleteDocumentsByIdAfterTimestamp,
- getDocumentsById,
- saveDocument,
- updateDocumentContent,
-} from "@/lib/db/queries";
-import { ChatbotError } from "@/lib/errors";
-
-const documentSchema = z.object({
- content: z.string(),
- title: z.string(),
- kind: z.enum(["text", "code", "image", "sheet"]),
- isManualEdit: z.boolean().optional(),
-});
-
-export async function GET(request: Request) {
- const { searchParams } = new URL(request.url);
- const id = searchParams.get("id");
-
- if (!id) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter id is missing"
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:document").toResponse();
- }
-
- const documents = await getDocumentsById({ id });
-
- const [document] = documents;
-
- if (!document) {
- return new ChatbotError("not_found:document").toResponse();
- }
-
- if (document.userId !== session.user.id) {
- return new ChatbotError("forbidden:document").toResponse();
- }
-
- return Response.json(documents, { status: 200 });
-}
-
-export async function POST(request: Request) {
- const { searchParams } = new URL(request.url);
- const id = searchParams.get("id");
-
- if (!id) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter id is required."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("not_found:document").toResponse();
- }
-
- let content: string;
- let title: string;
- let kind: ArtifactKind;
- let isManualEdit: boolean | undefined;
-
- try {
- const parsed = documentSchema.parse(await request.json());
- content = parsed.content;
- title = parsed.title;
- kind = parsed.kind;
- isManualEdit = parsed.isManualEdit;
- } catch {
- return new ChatbotError(
- "bad_request:api",
- "Invalid request body."
- ).toResponse();
- }
-
- const documents = await getDocumentsById({ id });
-
- if (documents.length > 0) {
- const [doc] = documents;
-
- if (doc.userId !== session.user.id) {
- return new ChatbotError("forbidden:document").toResponse();
- }
- }
-
- if (isManualEdit && documents.length > 0) {
- const result = await updateDocumentContent({ id, content });
- return Response.json(result, { status: 200 });
- }
-
- const document = await saveDocument({
- id,
- content,
- title,
- kind,
- userId: session.user.id,
- });
-
- return Response.json(document, { status: 200 });
-}
-
-export async function DELETE(request: Request) {
- const { searchParams } = new URL(request.url);
- const id = searchParams.get("id");
- const timestamp = searchParams.get("timestamp");
-
- if (!id) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter id is required."
- ).toResponse();
- }
-
- if (!timestamp) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter timestamp is required."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:document").toResponse();
- }
-
- const documents = await getDocumentsById({ id });
-
- const [document] = documents;
-
- if (document.userId !== session.user.id) {
- return new ChatbotError("forbidden:document").toResponse();
- }
-
- const parsedTimestamp = new Date(timestamp);
-
- if (Number.isNaN(parsedTimestamp.getTime())) {
- return new ChatbotError(
- "bad_request:api",
- "Invalid timestamp."
- ).toResponse();
- }
-
- const documentsDeleted = await deleteDocumentsByIdAfterTimestamp({
- id,
- timestamp: parsedTimestamp,
- });
-
- return Response.json(documentsDeleted, { status: 200 });
-}
diff --git a/app/(chat)/api/files/upload/route.ts b/app/(chat)/api/files/upload/route.ts
deleted file mode 100644
index b2270331e3..0000000000
--- a/app/(chat)/api/files/upload/route.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { put } from "@vercel/blob";
-import { NextResponse } from "next/server";
-import { z } from "zod";
-
-import { auth } from "@/app/(auth)/auth";
-
-const FileSchema = z.object({
- file: z
- .instanceof(Blob)
- .refine((file) => file.size <= 5 * 1024 * 1024, {
- message: "File size should be less than 5MB",
- })
- .refine((file) => ["image/jpeg", "image/png"].includes(file.type), {
- message: "File type should be JPEG or PNG",
- }),
-});
-
-export async function POST(request: Request) {
- const session = await auth();
-
- if (!session) {
- return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
- }
-
- if (request.body === null) {
- return new Response("Request body is empty", { status: 400 });
- }
-
- try {
- const formData = await request.formData();
- const file = formData.get("file") as Blob;
-
- if (!file) {
- return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
- }
-
- const validatedFile = FileSchema.safeParse({ file });
-
- if (!validatedFile.success) {
- const errorMessage = validatedFile.error.errors
- .map((error) => error.message)
- .join(", ");
-
- return NextResponse.json({ error: errorMessage }, { status: 400 });
- }
-
- const filename = (formData.get("file") as File).name;
- const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_");
- const fileBuffer = await file.arrayBuffer();
-
- try {
- const data = await put(`${safeName}`, fileBuffer, {
- access: "public",
- });
-
- return NextResponse.json(data);
- } catch (_error) {
- return NextResponse.json({ error: "Upload failed" }, { status: 500 });
- }
- } catch (_error) {
- return NextResponse.json(
- { error: "Failed to process request" },
- { status: 500 }
- );
- }
-}
diff --git a/app/(chat)/api/history/route.ts b/app/(chat)/api/history/route.ts
deleted file mode 100644
index 064a385473..0000000000
--- a/app/(chat)/api/history/route.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import type { NextRequest } from "next/server";
-import { auth } from "@/app/(auth)/auth";
-import { deleteAllChatsByUserId, getChatsByUserId } from "@/lib/db/queries";
-import { ChatbotError } from "@/lib/errors";
-
-export async function GET(request: NextRequest) {
- const { searchParams } = request.nextUrl;
-
- const limit = Math.min(
- Math.max(Number.parseInt(searchParams.get("limit") || "10", 10), 1),
- 50
- );
- const startingAfter = searchParams.get("starting_after");
- const endingBefore = searchParams.get("ending_before");
-
- if (startingAfter && endingBefore) {
- return new ChatbotError(
- "bad_request:api",
- "Only one of starting_after or ending_before can be provided."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:chat").toResponse();
- }
-
- const chats = await getChatsByUserId({
- id: session.user.id,
- limit,
- startingAfter,
- endingBefore,
- });
-
- return Response.json(chats);
-}
-
-export async function DELETE() {
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:chat").toResponse();
- }
-
- const result = await deleteAllChatsByUserId({ userId: session.user.id });
-
- return Response.json(result, { status: 200 });
-}
diff --git a/app/(chat)/api/messages/route.ts b/app/(chat)/api/messages/route.ts
deleted file mode 100644
index cda98dedb2..0000000000
--- a/app/(chat)/api/messages/route.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { auth } from "@/app/(auth)/auth";
-import { getChatById, getMessagesByChatId } from "@/lib/db/queries";
-import { convertToUIMessages } from "@/lib/utils";
-
-export async function GET(request: Request) {
- const { searchParams } = new URL(request.url);
- const chatId = searchParams.get("chatId");
-
- if (!chatId) {
- return Response.json({ error: "chatId required" }, { status: 400 });
- }
-
- const [session, chat, messages] = await Promise.all([
- auth(),
- getChatById({ id: chatId }),
- getMessagesByChatId({ id: chatId }),
- ]);
-
- if (!chat) {
- return Response.json({
- messages: [],
- visibility: "private",
- userId: null,
- isReadonly: false,
- });
- }
-
- if (
- chat.visibility === "private" &&
- (!session?.user || session.user.id !== chat.userId)
- ) {
- return Response.json({ error: "forbidden" }, { status: 403 });
- }
-
- const isReadonly = !session?.user || session.user.id !== chat.userId;
-
- return Response.json({
- messages: convertToUIMessages(messages),
- visibility: chat.visibility,
- userId: chat.userId,
- isReadonly,
- });
-}
diff --git a/app/(chat)/api/models/route.ts b/app/(chat)/api/models/route.ts
deleted file mode 100644
index de1d12a822..0000000000
--- a/app/(chat)/api/models/route.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { getAllGatewayModels, getCapabilities, isDemo } from "@/lib/ai/models";
-
-export async function GET() {
- const headers = {
- "Cache-Control": "public, max-age=86400, s-maxage=86400",
- };
-
- const curatedCapabilities = await getCapabilities();
-
- if (isDemo) {
- const models = await getAllGatewayModels();
- const capabilities = Object.fromEntries(
- models.map((m) => [m.id, curatedCapabilities[m.id] ?? m.capabilities])
- );
-
- return Response.json({ capabilities, models }, { headers });
- }
-
- return Response.json(curatedCapabilities, { headers });
-}
diff --git a/app/(chat)/api/suggestions/route.ts b/app/(chat)/api/suggestions/route.ts
deleted file mode 100644
index 303f45ed26..0000000000
--- a/app/(chat)/api/suggestions/route.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { auth } from "@/app/(auth)/auth";
-import { getSuggestionsByDocumentId } from "@/lib/db/queries";
-import { ChatbotError } from "@/lib/errors";
-
-export async function GET(request: Request) {
- const { searchParams } = new URL(request.url);
- const documentId = searchParams.get("documentId");
-
- if (!documentId) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter documentId is required."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:suggestions").toResponse();
- }
-
- const suggestions = await getSuggestionsByDocumentId({
- documentId,
- });
-
- const [suggestion] = suggestions;
-
- if (!suggestion) {
- return Response.json([], { status: 200 });
- }
-
- if (suggestion.userId !== session.user.id) {
- return new ChatbotError("forbidden:api").toResponse();
- }
-
- return Response.json(suggestions, { status: 200 });
-}
diff --git a/app/(chat)/api/vote/route.ts b/app/(chat)/api/vote/route.ts
deleted file mode 100644
index 726ba56465..0000000000
--- a/app/(chat)/api/vote/route.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { z } from "zod";
-import { auth } from "@/app/(auth)/auth";
-import { getChatById, getVotesByChatId, voteMessage } from "@/lib/db/queries";
-import { ChatbotError } from "@/lib/errors";
-
-const voteSchema = z.object({
- chatId: z.string(),
- messageId: z.string(),
- type: z.enum(["up", "down"]),
-});
-
-export async function GET(request: Request) {
- const { searchParams } = new URL(request.url);
- const chatId = searchParams.get("chatId");
-
- if (!chatId) {
- return new ChatbotError(
- "bad_request:api",
- "Parameter chatId is required."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:vote").toResponse();
- }
-
- const chat = await getChatById({ id: chatId });
-
- if (!chat) {
- return new ChatbotError("not_found:chat").toResponse();
- }
-
- if (chat.userId !== session.user.id) {
- return new ChatbotError("forbidden:vote").toResponse();
- }
-
- const votes = await getVotesByChatId({ id: chatId });
-
- return Response.json(votes, { status: 200 });
-}
-
-export async function PATCH(request: Request) {
- let chatId: string;
- let messageId: string;
- let type: "up" | "down";
-
- try {
- const parsed = voteSchema.parse(await request.json());
- chatId = parsed.chatId;
- messageId = parsed.messageId;
- type = parsed.type;
- } catch {
- return new ChatbotError(
- "bad_request:api",
- "Parameters chatId, messageId, and type are required."
- ).toResponse();
- }
-
- const session = await auth();
-
- if (!session?.user) {
- return new ChatbotError("unauthorized:vote").toResponse();
- }
-
- const chat = await getChatById({ id: chatId });
-
- if (!chat) {
- return new ChatbotError("not_found:vote").toResponse();
- }
-
- if (chat.userId !== session.user.id) {
- return new ChatbotError("forbidden:vote").toResponse();
- }
-
- await voteMessage({
- chatId,
- messageId,
- type,
- });
-
- return new Response("Message voted", { status: 200 });
-}
diff --git a/artifacts/actions.ts b/artifacts/actions.ts
deleted file mode 100644
index 2000ca1110..0000000000
--- a/artifacts/actions.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-"use server";
-
-import { getSuggestionsByDocumentId } from "@/lib/db/queries";
-
-export async function getSuggestions({ documentId }: { documentId: string }) {
- const suggestions = await getSuggestionsByDocumentId({ documentId });
- return suggestions ?? [];
-}
diff --git a/artifacts/text/client.tsx b/artifacts/text/client.tsx
index a0b056c857..373f9a05c6 100644
--- a/artifacts/text/client.tsx
+++ b/artifacts/text/client.tsx
@@ -11,8 +11,7 @@ import {
UndoIcon,
} from "@/components/chat/icons";
import { Editor } from "@/components/chat/text-editor";
-import type { Suggestion } from "@/lib/db/schema";
-import { getSuggestions } from "../actions";
+import type { Suggestion } from "@/lib/chat/types";
type TextArtifactMetadata = {
suggestions: Suggestion[];
@@ -22,10 +21,10 @@ export const textArtifact = new Artifact<"text", TextArtifactMetadata>({
kind: "text",
description: "Useful for text content, like drafting essays and emails.",
initialize: async ({ documentId, setMetadata }) => {
- const suggestions = await getSuggestions({ documentId });
+ void documentId;
setMetadata({
- suggestions,
+ suggestions: [],
});
},
onStreamPart: ({ streamPart, setMetadata, setArtifact }) => {
diff --git a/components/chat/app-sidebar.tsx b/components/chat/app-sidebar.tsx
index d276842d13..e9f89a50c7 100644
--- a/components/chat/app-sidebar.tsx
+++ b/components/chat/app-sidebar.tsx
@@ -4,19 +4,11 @@ import {
MessageSquareIcon,
PanelLeftIcon,
PenSquareIcon,
- TrashIcon,
} from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import type { User } from "next-auth";
-import { useState } from "react";
-import { toast } from "sonner";
-import { useSWRConfig } from "swr";
-import { unstable_serialize } from "swr/infinite";
-import {
- getChatHistoryPaginationKey,
- SidebarHistory,
-} from "@/components/chat/sidebar-history";
+import { SidebarHistory } from "@/components/chat/sidebar-history";
import { SidebarUserNav } from "@/components/chat/sidebar-user-nav";
import {
Sidebar,
@@ -32,134 +24,73 @@ import {
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar";
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "../ui/alert-dialog";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
export function AppSidebar({ user }: { user: User | undefined }) {
const router = useRouter();
const { setOpenMobile, toggleSidebar } = useSidebar();
- const { mutate } = useSWRConfig();
- const [showDeleteAllDialog, setShowDeleteAllDialog] = useState(false);
-
- const handleDeleteAll = () => {
- setShowDeleteAllDialog(false);
- router.replace("/");
- mutate(unstable_serialize(getChatHistoryPaginationKey), [], {
- revalidate: false,
- });
-
- fetch(`${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history`, {
- method: "DELETE",
- });
-
- toast.success("All chats deleted");
- };
return (
- <>
-
-
-
-
-
-
- setOpenMobile(false)}>
-
-
-
-
-
- toggleSidebar()}
- >
-
-
-
-
- Open sidebar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ setOpenMobile(false)}>
+
+
+
+
+
{
- setOpenMobile(false);
- router.push("/");
- }}
- tooltip="New Chat"
+ className="pointer-events-none absolute inset-0 size-8 opacity-0 group-data-[collapsible=icon]:pointer-events-auto group-data-[collapsible=icon]:group-hover/logo:opacity-100"
+ onClick={() => toggleSidebar()}
>
-
- New chat
+
-
- {user && (
-
- setShowDeleteAllDialog(true)}
- tooltip="Delete All Chats"
- >
-
- Delete all
-
-
- )}
-
-
-
-
-
-
- {user && }
-
-
-
-
-
-
-
- Delete all chats?
-
- This action cannot be undone. This will permanently delete all
- your chats and remove them from our servers.
-
-
-
- Cancel
-
- Delete All
-
-
-
-
- >
+
+
+ Open sidebar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ setOpenMobile(false);
+ router.push("/");
+ }}
+ tooltip="New Chat"
+ >
+
+ New chat
+
+
+
+
+
+
+
+
+ {user && }
+
+
+
);
}
diff --git a/components/chat/artifact-messages.tsx b/components/chat/artifact-messages.tsx
index 3cfa972b3d..2352b30c27 100644
--- a/components/chat/artifact-messages.tsx
+++ b/components/chat/artifact-messages.tsx
@@ -3,7 +3,7 @@ import equal from "fast-deep-equal";
import { AnimatePresence, motion } from "framer-motion";
import { memo } from "react";
import { useMessages } from "@/hooks/use-messages";
-import type { Vote } from "@/lib/db/schema";
+import type { Vote } from "@/lib/chat/types";
import type { ChatMessage } from "@/lib/types";
import type { UIArtifact } from "./artifact";
import { PreviewMessage, ThinkingMessage } from "./message";
diff --git a/components/chat/artifact.tsx b/components/chat/artifact.tsx
index e233effcd0..fb3b4fba1b 100644
--- a/components/chat/artifact.tsx
+++ b/components/chat/artifact.tsx
@@ -1,5 +1,4 @@
import type { UseChatHelpers } from "@ai-sdk/react";
-import { formatDistance } from "date-fns";
import equal from "fast-deep-equal";
import { AnimatePresence, motion } from "framer-motion";
import {
@@ -11,16 +10,14 @@ import {
useRef,
useState,
} from "react";
-import useSWR, { useSWRConfig } from "swr";
import { useWindowSize } from "usehooks-ts";
import { codeArtifact } from "@/artifacts/code/client";
import { imageArtifact } from "@/artifacts/image/client";
import { sheetArtifact } from "@/artifacts/sheet/client";
import { textArtifact } from "@/artifacts/text/client";
import { useArtifact } from "@/hooks/use-artifact";
-import type { Document, Vote } from "@/lib/db/schema";
+import type { Vote } from "@/lib/chat/types";
import type { Attachment, ChatMessage } from "@/lib/types";
-import { fetcher } from "@/lib/utils";
import { useSidebar } from "../ui/sidebar";
import { ArtifactActions } from "./artifact-actions";
import { ArtifactCloseButton } from "./artifact-close-button";
@@ -89,20 +86,8 @@ function PureArtifact({
}) {
const { artifact, setArtifact, metadata, setMetadata } = useArtifact();
- const {
- data: documents,
- isLoading: isDocumentsFetching,
- mutate: mutateDocuments,
- } = useSWR(
- artifact.documentId !== "init" && artifact.status !== "streaming"
- ? `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${artifact.documentId}`
- : null,
- fetcher
- );
-
const [mode, setMode] = useState<"edit" | "diff">("edit");
- const [document, setDocument] = useState(null);
- const [currentVersionIndex, setCurrentVersionIndex] = useState(-1);
+ const [currentVersionIndex, setCurrentVersionIndex] = useState(0);
const { state: sidebarState } = useSidebar();
const artifactContentRef = useRef(null);
@@ -124,81 +109,6 @@ function PureArtifact({
el.scrollTo({ top: el.scrollHeight });
}, [artifact.status]);
- useEffect(() => {
- if (documents && documents.length > 0) {
- const mostRecentDocument = documents.at(-1);
-
- if (mostRecentDocument) {
- setDocument(mostRecentDocument);
- setCurrentVersionIndex(documents.length - 1);
- if (artifact.status === "streaming" || !isContentDirty) {
- setArtifact((currentArtifact) => ({
- ...currentArtifact,
- content: mostRecentDocument.content ?? "",
- }));
- }
- }
- }
- }, [documents, setArtifact, artifact.status, isContentDirty]);
-
- useEffect(() => {
- mutateDocuments();
- }, [mutateDocuments]);
-
- const { mutate } = useSWRConfig();
-
- const handleContentChange = useCallback(
- (updatedContent: string) => {
- if (!artifact) {
- return;
- }
-
- mutate(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${artifact.documentId}`,
- async (currentDocuments) => {
- if (!currentDocuments) {
- return [];
- }
-
- const currentDocument = currentDocuments.at(-1);
-
- if (!currentDocument?.content) {
- setIsContentDirty(false);
- return currentDocuments;
- }
-
- if (currentDocument.content === updatedContent) {
- setIsContentDirty(false);
- return currentDocuments;
- }
-
- await fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${artifact.documentId}`,
- {
- method: "POST",
- body: JSON.stringify({
- title: artifact.title,
- content: updatedContent,
- kind: artifact.kind,
- isManualEdit: true,
- }),
- }
- );
-
- setIsContentDirty(false);
-
- return currentDocuments.map((doc, i) =>
- i === currentDocuments.length - 1
- ? { ...doc, content: updatedContent }
- : doc
- );
- },
- { revalidate: false }
- );
- },
- [artifact, mutate]
- );
-
const latestContentRef = useRef("");
const saveTimerRef = useRef | null>(null);
@@ -212,57 +122,53 @@ function PureArtifact({
saveTimerRef.current = null;
}
+ const commitContent = () => {
+ setArtifact((currentArtifact) => ({
+ ...currentArtifact,
+ content: latestContentRef.current,
+ }));
+ setIsContentDirty(false);
+ };
+
if (debounce) {
saveTimerRef.current = setTimeout(() => {
- handleContentChange(latestContentRef.current);
+ commitContent();
saveTimerRef.current = null;
}, 2000);
} else {
- handleContentChange(updatedContent);
+ commitContent();
}
},
- [handleContentChange]
+ [setArtifact]
+ );
+
+ useEffect(
+ () => () => {
+ if (saveTimerRef.current) {
+ clearTimeout(saveTimerRef.current);
+ }
+ },
+ []
);
function getDocumentContentById(index: number) {
- if (!documents) {
- return "";
- }
- if (!documents[index]) {
- return "";
- }
- return documents[index].content ?? "";
+ return index === 0 ? artifact.content : "";
}
const handleVersionChange = (type: "next" | "prev" | "toggle" | "latest") => {
- if (!documents) {
- return;
- }
-
if (type === "latest") {
- setCurrentVersionIndex(documents.length - 1);
+ setCurrentVersionIndex(0);
setMode("edit");
}
if (type === "toggle") {
setMode((currentMode) => (currentMode === "edit" ? "diff" : "edit"));
}
-
- if (type === "prev") {
- if (currentVersionIndex > 0) {
- setCurrentVersionIndex((index) => index - 1);
- }
- } else if (type === "next" && currentVersionIndex < documents.length - 1) {
- setCurrentVersionIndex((index) => index + 1);
- }
};
const [isToolbarVisible, setIsToolbarVisible] = useState(true);
- const isCurrentVersion =
- documents && documents.length > 0
- ? currentVersionIndex === documents.length - 1
- : true;
+ const isCurrentVersion = true;
const { width: windowWidth, height: windowHeight } = useWindowSize();
const isMobile = windowWidth ? windowWidth < 768 : false;
@@ -321,10 +227,6 @@ function PureArtifact({
Saving...
- ) : document ? (
-
- {`Updated ${formatDistance(new Date(document.createdAt), new Date(), { addSuffix: true })}`}
-
) : artifact.status === "streaming" ? (
@@ -333,11 +235,8 @@ function PureArtifact({
Generating...
) : (
-
- )}
- {documents && documents.length > 1 && (
-
- v{currentVersionIndex + 1}/{documents.length}
+
+ Local draft
)}
@@ -369,7 +268,7 @@ function PureArtifact({
getDocumentContentById={getDocumentContentById}
isCurrentVersion={isCurrentVersion}
isInline={false}
- isLoading={isDocumentsFetching && !artifact.content}
+ isLoading={false}
metadata={metadata}
mode={mode}
onSaveContent={saveContent}
@@ -412,7 +311,7 @@ function PureArtifact({
{!isCurrentVersion && (
-
-
-
-
{!isReadonly && (
)}
-
-
-
-
- Deploy with Vercel
-
-
);
}
diff --git a/components/chat/code-editor.tsx b/components/chat/code-editor.tsx
index 721b908e26..b5a9a4584a 100644
--- a/components/chat/code-editor.tsx
+++ b/components/chat/code-editor.tsx
@@ -6,7 +6,7 @@ import { oneDark } from "@codemirror/theme-one-dark";
import { EditorView } from "@codemirror/view";
import { basicSetup } from "codemirror";
import { memo, useEffect, useRef } from "react";
-import type { Suggestion } from "@/lib/db/schema";
+import type { Suggestion } from "@/lib/chat/types";
type EditorProps = {
content: string;
diff --git a/components/chat/create-artifact.tsx b/components/chat/create-artifact.tsx
index 78b24b1030..5f9349e016 100644
--- a/components/chat/create-artifact.tsx
+++ b/components/chat/create-artifact.tsx
@@ -1,7 +1,7 @@
import type { UseChatHelpers } from "@ai-sdk/react";
import type { DataUIPart } from "ai";
import type { ComponentType, Dispatch, ReactNode, SetStateAction } from "react";
-import type { Suggestion } from "@/lib/db/schema";
+import type { Suggestion } from "@/lib/chat/types";
import type { ChatMessage, CustomUIDataTypes } from "@/lib/types";
import type { UIArtifact } from "./artifact";
diff --git a/components/chat/data-stream-handler.tsx b/components/chat/data-stream-handler.tsx
index 239ade6b7c..05ea1505a4 100644
--- a/components/chat/data-stream-handler.tsx
+++ b/components/chat/data-stream-handler.tsx
@@ -1,16 +1,12 @@
"use client";
import { useEffect } from "react";
-import { useSWRConfig } from "swr";
-import { unstable_serialize } from "swr/infinite";
import { initialArtifactData, useArtifact } from "@/hooks/use-artifact";
import { artifactDefinitions } from "./artifact";
import { useDataStream } from "./data-stream-provider";
-import { getChatHistoryPaginationKey } from "./sidebar-history";
export function DataStreamHandler() {
const { dataStream, setDataStream } = useDataStream();
- const { mutate } = useSWRConfig();
const { artifact, setArtifact, setMetadata } = useArtifact();
@@ -24,7 +20,6 @@ export function DataStreamHandler() {
for (const delta of newDeltas) {
if (delta.type === "data-chat-title") {
- mutate(unstable_serialize(getChatHistoryPaginationKey));
continue;
}
const artifactDefinition = artifactDefinitions.find(
@@ -85,7 +80,7 @@ export function DataStreamHandler() {
}
});
}
- }, [dataStream, setArtifact, setMetadata, artifact, setDataStream, mutate]);
+ }, [dataStream, setArtifact, setMetadata, artifact, setDataStream]);
return null;
}
diff --git a/components/chat/document-preview.tsx b/components/chat/document-preview.tsx
index 747aa58eed..cbe9921a5c 100644
--- a/components/chat/document-preview.tsx
+++ b/components/chat/document-preview.tsx
@@ -9,10 +9,9 @@ import {
useMemo,
useRef,
} from "react";
-import useSWR from "swr";
import { useArtifact } from "@/hooks/use-artifact";
-import type { Document } from "@/lib/db/schema";
-import { cn, fetcher } from "@/lib/utils";
+import type { Document } from "@/lib/chat/types";
+import { cn } from "@/lib/utils";
import type { ArtifactKind, UIArtifact } from "./artifact";
import { CodeEditor } from "./code-editor";
import { InlineDocumentSkeleton } from "./document-skeleton";
@@ -46,17 +45,6 @@ export function DocumentPreview({
args,
}: DocumentPreviewProps) {
const { artifact, setArtifact } = useArtifact();
-
- const { data: documents, isLoading: isDocumentsFetching } = useSWR<
- Document[]
- >(
- result
- ? `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${result.id}`
- : null,
- fetcher
- );
-
- const previewDocument = useMemo(() => documents?.[0], [documents]);
const hitboxRef = useRef
(null);
useEffect(() => {
@@ -75,42 +63,40 @@ export function DocumentPreview({
}
}, [artifact.documentId, setArtifact]);
- if (isDocumentsFetching) {
- const kind = result?.kind ?? args?.kind ?? artifact.kind;
- const title = result?.title ?? args?.title ?? artifact.title;
+ const document = useMemo(() => {
+ if (result?.id && result.title && result.kind) {
+ return {
+ title: result.title,
+ kind: result.kind,
+ content: result.content ?? "",
+ id: result.id,
+ createdAt: new Date(),
+ userId: "ui-only",
+ };
+ }
- return (
-
- {title ? (
-
- ) : (
-
- )}
-
-
-
-
- );
- }
+ if (args?.id && args.title && args.kind) {
+ return {
+ title: args.title,
+ kind: args.kind,
+ content: args.content ?? "",
+ id: args.id,
+ createdAt: new Date(),
+ userId: "ui-only",
+ };
+ }
- const document: Document | null = previewDocument
- ? previewDocument
- : artifact.status === "streaming"
+ return artifact.status === "streaming"
? {
title: artifact.title,
kind: artifact.kind,
content: artifact.content,
id: artifact.documentId,
createdAt: new Date(),
- userId: "noop",
+ userId: "ui-only",
}
: null;
+ }, [result, args, artifact]);
if (!document) {
return ;
@@ -119,8 +105,8 @@ export function DocumentPreview({
return (
(
);
const PureHitboxLayer = ({
+ document,
hitboxRef,
- result,
setArtifact,
}: {
+ document: Document;
hitboxRef: React.RefObject;
- result?: Partial;
setArtifact: (
updaterFn: UIArtifact | ((currentArtifact: UIArtifact) => UIArtifact)
) => void;
@@ -171,9 +157,11 @@ const PureHitboxLayer = ({
setArtifact((artifact) => ({
...artifact,
- ...(result?.id && { documentId: result.id }),
- ...(result?.title && { title: result.title }),
- ...(result?.kind && { kind: result.kind }),
+ documentId: document.id,
+ title: document.title,
+ kind: document.kind,
+ content: document.content ?? "",
+ status: artifact.status === "streaming" ? "streaming" : "idle",
isVisible: true,
boundingBox: {
left: boundingBox.x,
@@ -183,7 +171,7 @@ const PureHitboxLayer = ({
},
}));
},
- [setArtifact, result]
+ [setArtifact, document]
);
return (
@@ -204,7 +192,7 @@ const PureHitboxLayer = ({
};
const HitboxLayer = memo(PureHitboxLayer, (prevProps, nextProps) => {
- if (!equal(prevProps.result, nextProps.result)) {
+ if (!equal(prevProps.document, nextProps.document)) {
return false;
}
return true;
diff --git a/components/chat/message-actions.tsx b/components/chat/message-actions.tsx
index ecb74abc5a..9214c65733 100644
--- a/components/chat/message-actions.tsx
+++ b/components/chat/message-actions.tsx
@@ -1,20 +1,19 @@
import equal from "fast-deep-equal";
import { memo } from "react";
import { toast } from "sonner";
-import { useSWRConfig } from "swr";
import { useCopyToClipboard } from "usehooks-ts";
-import type { Vote } from "@/lib/db/schema";
+import type { Vote } from "@/lib/chat/types";
import type { ChatMessage } from "@/lib/types";
import {
MessageAction as Action,
MessageActions as Actions,
} from "../ai-elements/message";
-import { CopyIcon, PencilEditIcon, ThumbDownIcon, ThumbUpIcon } from "./icons";
+import { CopyIcon, PencilEditIcon } from "./icons";
export function PureMessageActions({
- chatId,
+ chatId: _chatId,
message,
- vote,
+ vote: _vote,
isLoading,
onEdit,
}: {
@@ -24,7 +23,6 @@ export function PureMessageActions({
isLoading: boolean;
onEdit?: () => void;
}) {
- const { mutate } = useSWRConfig();
const [_, copyToClipboard] = useCopyToClipboard();
if (isLoading) {
@@ -82,112 +80,6 @@ export function PureMessageActions({
>
-
- {
- const upvote = fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote`,
- {
- method: "PATCH",
- body: JSON.stringify({
- chatId,
- messageId: message.id,
- type: "up",
- }),
- }
- );
-
- toast.promise(upvote, {
- loading: "Upvoting Response...",
- success: () => {
- mutate(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote?chatId=${chatId}`,
- (currentVotes) => {
- if (!currentVotes) {
- return [];
- }
-
- const votesWithoutCurrent = currentVotes.filter(
- (currentVote) => currentVote.messageId !== message.id
- );
-
- return [
- ...votesWithoutCurrent,
- {
- chatId,
- messageId: message.id,
- isUpvoted: true,
- },
- ];
- },
- { revalidate: false }
- );
-
- return "Upvoted Response!";
- },
- error: "Failed to upvote response.",
- });
- }}
- tooltip="Upvote Response"
- >
-
-
-
- {
- const downvote = fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote`,
- {
- method: "PATCH",
- body: JSON.stringify({
- chatId,
- messageId: message.id,
- type: "down",
- }),
- }
- );
-
- toast.promise(downvote, {
- loading: "Downvoting Response...",
- success: () => {
- mutate(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote?chatId=${chatId}`,
- (currentVotes) => {
- if (!currentVotes) {
- return [];
- }
-
- const votesWithoutCurrent = currentVotes.filter(
- (currentVote) => currentVote.messageId !== message.id
- );
-
- return [
- ...votesWithoutCurrent,
- {
- chatId,
- messageId: message.id,
- isUpvoted: false,
- },
- ];
- },
- { revalidate: false }
- );
-
- return "Downvoted Response!";
- },
- error: "Failed to downvote response.",
- });
- }}
- tooltip="Downvote Response"
- >
-
-
);
}
diff --git a/components/chat/message-editor.tsx b/components/chat/message-editor.tsx
index a867f79df4..420848691e 100644
--- a/components/chat/message-editor.tsx
+++ b/components/chat/message-editor.tsx
@@ -1,7 +1,6 @@
"use client";
import type { UseChatHelpers } from "@ai-sdk/react";
-import { deleteTrailingMessages } from "@/app/(chat)/actions";
import type { ChatMessage } from "@/lib/types";
export async function submitEditedMessage({
@@ -15,8 +14,6 @@ export async function submitEditedMessage({
setMessages: UseChatHelpers["setMessages"];
regenerate: UseChatHelpers["regenerate"];
}) {
- await deleteTrailingMessages({ id: message.id });
-
setMessages((messages) => {
const index = messages.findIndex((m) => m.id === message.id);
if (index === -1) {
diff --git a/components/chat/message.tsx b/components/chat/message.tsx
index b08c5cc130..594eed135f 100644
--- a/components/chat/message.tsx
+++ b/components/chat/message.tsx
@@ -1,6 +1,6 @@
"use client";
import type { UseChatHelpers } from "@ai-sdk/react";
-import type { Vote } from "@/lib/db/schema";
+import type { Vote } from "@/lib/chat/types";
import type { ChatMessage } from "@/lib/types";
import { cn, sanitizeText } from "@/lib/utils";
import { MessageContent, MessageResponse } from "../ai-elements/message";
diff --git a/components/chat/messages.tsx b/components/chat/messages.tsx
index c3b6359cb7..7b96d0c472 100644
--- a/components/chat/messages.tsx
+++ b/components/chat/messages.tsx
@@ -2,7 +2,7 @@ import type { UseChatHelpers } from "@ai-sdk/react";
import { ArrowDownIcon } from "lucide-react";
import { useEffect, useRef } from "react";
import { useMessages } from "@/hooks/use-messages";
-import type { Vote } from "@/lib/db/schema";
+import type { Vote } from "@/lib/chat/types";
import type { ChatMessage } from "@/lib/types";
import { cn } from "@/lib/utils";
import { useDataStream } from "./data-stream-provider";
diff --git a/components/chat/multimodal-input.tsx b/components/chat/multimodal-input.tsx
index bb07f7cc42..8e8bee5c25 100644
--- a/components/chat/multimodal-input.tsx
+++ b/components/chat/multimodal-input.tsx
@@ -23,7 +23,6 @@ import {
useState,
} from "react";
import { toast } from "sonner";
-import useSWR from "swr";
import { useLocalStorage, useWindowSize } from "usehooks-ts";
import {
ModelSelector,
@@ -40,6 +39,7 @@ import {
type ChatModel,
chatModels,
DEFAULT_CHAT_MODEL,
+ getCapabilities,
type ModelCapabilities,
} from "@/lib/ai/models";
import type { Attachment, ChatMessage } from "@/lib/types";
@@ -180,10 +180,7 @@ function PureMultimodalInput({
action: {
label: "Delete",
onClick: () => {
- fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/chat?id=${chatId}`,
- { method: "DELETE" }
- );
+ setMessages(() => []);
router.push("/");
toast.success("Chat deleted");
},
@@ -195,9 +192,7 @@ function PureMultimodalInput({
action: {
label: "Delete all",
onClick: () => {
- fetch(`${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history`, {
- method: "DELETE",
- });
+ setMessages(() => []);
router.push("/");
toast.success("All chats deleted");
},
@@ -210,7 +205,7 @@ function PureMultimodalInput({
};
const fileInputRef = useRef(null);
- const [uploadQueue, setUploadQueue] = useState([]);
+ const [uploadQueue] = useState([]);
const [slashOpen, setSlashOpen] = useState(false);
const [slashQuery, setSlashQuery] = useState("");
const [slashIndex, setSlashIndex] = useState(0);
@@ -256,64 +251,24 @@ function PureMultimodalInput({
chatId,
]);
- const uploadFile = useCallback(async (file: File) => {
- const formData = new FormData();
- formData.append("file", file);
-
- try {
- const response = await fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/files/upload`,
- {
- method: "POST",
- body: formData,
- }
- );
-
- if (response.ok) {
- const data = await response.json();
- const { url, pathname, contentType } = data;
-
- return {
- url,
- name: pathname,
- contentType,
- };
- }
- const { error } = await response.json();
- toast.error(error);
- } catch (_error) {
- toast.error("Failed to upload file, please try again!");
- }
+ const showUploadsDisabled = useCallback(() => {
+ toast.error("File uploads are disabled until your backend is connected.");
}, []);
const handleFileChange = useCallback(
- async (event: ChangeEvent) => {
+ (event: ChangeEvent) => {
const files = Array.from(event.target.files || []);
- setUploadQueue(files.map((file) => file.name));
-
- try {
- const uploadPromises = files.map((file) => uploadFile(file));
- const uploadedAttachments = await Promise.all(uploadPromises);
- const successfullyUploadedAttachments = uploadedAttachments.filter(
- (attachment) => attachment !== undefined
- );
-
- setAttachments((currentAttachments) => [
- ...currentAttachments,
- ...successfullyUploadedAttachments,
- ]);
- } catch (_error) {
- toast.error("Failed to upload files");
- } finally {
- setUploadQueue([]);
+ if (files.length > 0) {
+ showUploadsDisabled();
+ event.target.value = "";
}
},
- [setAttachments, uploadFile]
+ [showUploadsDisabled]
);
const handlePaste = useCallback(
- async (event: ClipboardEvent) => {
+ (event: ClipboardEvent) => {
const items = event.clipboardData?.items;
if (!items) {
return;
@@ -328,34 +283,9 @@ function PureMultimodalInput({
}
event.preventDefault();
-
- setUploadQueue((prev) => [...prev, "Pasted image"]);
-
- try {
- const uploadPromises = imageItems
- .map((item) => item.getAsFile())
- .filter((file): file is File => file !== null)
- .map((file) => uploadFile(file));
-
- const uploadedAttachments = await Promise.all(uploadPromises);
- const successfullyUploadedAttachments = uploadedAttachments.filter(
- (attachment) =>
- attachment !== undefined &&
- attachment.url !== undefined &&
- attachment.contentType !== undefined
- );
-
- setAttachments((curr) => [
- ...curr,
- ...(successfullyUploadedAttachments as Attachment[]),
- ]);
- } catch (_error) {
- toast.error("Failed to upload pasted image(s)");
- } finally {
- setUploadQueue([]);
- }
+ showUploadsDisabled();
},
- [setAttachments, uploadFile]
+ [showUploadsDisabled]
);
useEffect(() => {
@@ -404,6 +334,7 @@ function PureMultimodalInput({
onChange={handleFileChange}
ref={fileInputRef}
tabIndex={-1}
+ accept="image/png,image/jpeg"
type="file"
/>
@@ -593,14 +524,7 @@ function PureAttachmentsButton({
status: UseChatHelpers["status"];
selectedModelId: string;
}) {
- const { data: modelsResponse } = useSWR(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/models`,
- (url: string) => fetch(url).then((r) => r.json()),
- { revalidateOnFocus: false, dedupingInterval: 3_600_000 }
- );
-
- const caps: Record | undefined =
- modelsResponse?.capabilities ?? modelsResponse;
+ const caps: Record = getCapabilities();
const hasVision = caps?.[selectedModelId]?.vision ?? false;
return (
@@ -634,16 +558,9 @@ function PureModelSelectorCompact({
onModelChange?: (modelId: string) => void;
}) {
const [open, setOpen] = useState(false);
- const { data: modelsData } = useSWR(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/models`,
- (url: string) => fetch(url).then((r) => r.json()),
- { revalidateOnFocus: false, dedupingInterval: 3_600_000 }
- );
- const capabilities: Record | undefined =
- modelsData?.capabilities ?? modelsData;
- const dynamicModels: ChatModel[] | undefined = modelsData?.models;
- const activeModels = dynamicModels ?? chatModels;
+ const capabilities: Record = getCapabilities();
+ const activeModels = chatModels;
const selectedModel =
activeModels.find((m: ChatModel) => m.id === selectedModelId) ??
@@ -668,12 +585,7 @@ function PureModelSelectorCompact({
{(() => {
const curatedIds = new Set(chatModels.map((m) => m.id));
- const allModels = dynamicModels
- ? [
- ...chatModels,
- ...dynamicModels.filter((m) => !curatedIds.has(m.id)),
- ]
- : chatModels;
+ const allModels = chatModels;
const grouped: Record<
string,
diff --git a/components/chat/shell.tsx b/components/chat/shell.tsx
index 9327206e82..b071864264 100644
--- a/components/chat/shell.tsx
+++ b/components/chat/shell.tsx
@@ -1,16 +1,6 @@
"use client";
import { useEffect, useRef, useState } from "react";
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "@/components/ui/alert-dialog";
import { useActiveChat } from "@/hooks/use-active-chat";
import {
initialArtifactData,
@@ -44,8 +34,6 @@ export function ChatShell() {
votes,
currentModelId,
setCurrentModelId,
- showCreditCardAlert,
- setShowCreditCardAlert,
} = useActiveChat();
const [editingMessage, setEditingMessage] = useState(
@@ -170,36 +158,6 @@ export function ChatShell() {
-
-
-
-
- Activate AI Gateway
-
- This application requires{" "}
- {process.env.NODE_ENV === "production" ? "the owner" : "you"} to
- activate Vercel AI Gateway.
-
-
-
- Cancel
- {
- window.open(
- "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card",
- "_blank"
- );
- window.location.href = `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/`;
- }}
- >
- Activate
-
-
-
-
>
);
}
diff --git a/components/chat/sidebar-history-item.tsx b/components/chat/sidebar-history-item.tsx
index 1940e5826b..29d0391c76 100644
--- a/components/chat/sidebar-history-item.tsx
+++ b/components/chat/sidebar-history-item.tsx
@@ -1,7 +1,7 @@
import Link from "next/link";
import { memo } from "react";
import { useChatVisibility } from "@/hooks/use-chat-visibility";
-import type { Chat } from "@/lib/db/schema";
+import type { Chat } from "@/lib/chat/types";
import {
DropdownMenu,
DropdownMenuContent,
diff --git a/components/chat/sidebar-history.tsx b/components/chat/sidebar-history.tsx
index fa56db15d5..bde2192cf9 100644
--- a/components/chat/sidebar-history.tsx
+++ b/components/chat/sidebar-history.tsx
@@ -1,373 +1,31 @@
"use client";
-import { isToday, isYesterday, subMonths, subWeeks } from "date-fns";
-import { motion } from "framer-motion";
-import { usePathname, useRouter } from "next/navigation";
import type { User } from "next-auth";
-import { useState } from "react";
-import { toast } from "sonner";
-import useSWRInfinite from "swr/infinite";
-import {
- AlertDialog,
- AlertDialogAction,
- AlertDialogCancel,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogFooter,
- AlertDialogHeader,
- AlertDialogTitle,
-} from "@/components/ui/alert-dialog";
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
- SidebarMenu,
- useSidebar,
} from "@/components/ui/sidebar";
-import type { Chat } from "@/lib/db/schema";
-import { fetcher } from "@/lib/utils";
-import { LoaderIcon } from "./icons";
-import { ChatItem } from "./sidebar-history-item";
-
-type GroupedChats = {
- today: Chat[];
- yesterday: Chat[];
- lastWeek: Chat[];
- lastMonth: Chat[];
- older: Chat[];
-};
+import type { Chat } from "@/lib/chat/types";
export type ChatHistory = {
chats: Chat[];
hasMore: boolean;
};
-const PAGE_SIZE = 20;
-
-const groupChatsByDate = (chats: Chat[]): GroupedChats => {
- const now = new Date();
- const oneWeekAgo = subWeeks(now, 1);
- const oneMonthAgo = subMonths(now, 1);
-
- return chats.reduce(
- (groups, chat) => {
- const chatDate = new Date(chat.createdAt);
-
- if (isToday(chatDate)) {
- groups.today.push(chat);
- } else if (isYesterday(chatDate)) {
- groups.yesterday.push(chat);
- } else if (chatDate > oneWeekAgo) {
- groups.lastWeek.push(chat);
- } else if (chatDate > oneMonthAgo) {
- groups.lastMonth.push(chat);
- } else {
- groups.older.push(chat);
- }
-
- return groups;
- },
- {
- today: [],
- yesterday: [],
- lastWeek: [],
- lastMonth: [],
- older: [],
- } as GroupedChats
- );
-};
-
-export function getChatHistoryPaginationKey(
- pageIndex: number,
- previousPageData: ChatHistory
-) {
- if (previousPageData && previousPageData.hasMore === false) {
- return null;
- }
-
- if (pageIndex === 0) {
- return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history?limit=${PAGE_SIZE}`;
- }
-
- const firstChatFromPage = previousPageData.chats.at(-1);
-
- if (!firstChatFromPage) {
- return null;
- }
-
- return `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history?ending_before=${firstChatFromPage.id}&limit=${PAGE_SIZE}`;
-}
-
export function SidebarHistory({ user }: { user: User | undefined }) {
- const { setOpenMobile } = useSidebar();
- const pathname = usePathname();
- const id = pathname?.startsWith("/chat/") ? pathname.split("/")[2] : null;
-
- const {
- data: paginatedChatHistories,
- setSize,
- isValidating,
- isLoading,
- mutate,
- } = useSWRInfinite(
- user ? getChatHistoryPaginationKey : () => null,
- fetcher,
- { fallbackData: [], revalidateOnFocus: false }
- );
-
- const router = useRouter();
- const [deleteId, setDeleteId] = useState(null);
- const [showDeleteDialog, setShowDeleteDialog] = useState(false);
-
- const hasReachedEnd = paginatedChatHistories
- ? paginatedChatHistories.some((page) => page.hasMore === false)
- : false;
-
- const hasEmptyChatHistory = paginatedChatHistories
- ? paginatedChatHistories.every((page) => page.chats.length === 0)
- : false;
-
- const handleDelete = () => {
- const chatToDelete = deleteId;
- const isCurrentChat = pathname === `/chat/${chatToDelete}`;
-
- setShowDeleteDialog(false);
-
- if (isCurrentChat) {
- router.replace("/");
- }
-
- mutate((chatHistories) => {
- if (chatHistories) {
- return chatHistories.map((chatHistory) => ({
- ...chatHistory,
- chats: chatHistory.chats.filter((chat) => chat.id !== chatToDelete),
- }));
- }
- });
-
- fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/chat?id=${chatToDelete}`,
- { method: "DELETE" }
- );
-
- toast.success("Chat deleted");
- };
-
- if (!user) {
- return (
-
-
-
- Login to save and revisit previous chats!
-
-
-
- );
- }
-
- if (isLoading) {
- return (
-
-
- History
-
-
-
- {[44, 32, 28, 64, 52].map((item) => (
-
- ))}
-
-
-
- );
- }
-
- if (hasEmptyChatHistory) {
- return (
-
-
- History
-
-
-
- Your conversations will appear here once you start chatting!
-
-
-
- );
- }
-
return (
- <>
-
-
- History
-
-
-
- {paginatedChatHistories &&
- (() => {
- const chatsFromHistory = paginatedChatHistories.flatMap(
- (paginatedChatHistory) => paginatedChatHistory.chats
- );
-
- const groupedChats = groupChatsByDate(chatsFromHistory);
-
- return (
-
- {groupedChats.today.length > 0 && (
-
-
- Today
-
- {groupedChats.today.map((chat) => (
-
{
- setDeleteId(chatId);
- setShowDeleteDialog(true);
- }}
- setOpenMobile={setOpenMobile}
- />
- ))}
-
- )}
-
- {groupedChats.yesterday.length > 0 && (
-
-
- Yesterday
-
- {groupedChats.yesterday.map((chat) => (
-
{
- setDeleteId(chatId);
- setShowDeleteDialog(true);
- }}
- setOpenMobile={setOpenMobile}
- />
- ))}
-
- )}
-
- {groupedChats.lastWeek.length > 0 && (
-
-
- Last 7 days
-
- {groupedChats.lastWeek.map((chat) => (
-
{
- setDeleteId(chatId);
- setShowDeleteDialog(true);
- }}
- setOpenMobile={setOpenMobile}
- />
- ))}
-
- )}
-
- {groupedChats.lastMonth.length > 0 && (
-
-
- Last 30 days
-
- {groupedChats.lastMonth.map((chat) => (
-
{
- setDeleteId(chatId);
- setShowDeleteDialog(true);
- }}
- setOpenMobile={setOpenMobile}
- />
- ))}
-
- )}
-
- {groupedChats.older.length > 0 && (
-
-
- Older
-
- {groupedChats.older.map((chat) => (
-
{
- setDeleteId(chatId);
- setShowDeleteDialog(true);
- }}
- setOpenMobile={setOpenMobile}
- />
- ))}
-
- )}
-
- );
- })()}
-
-
- {
- if (!isValidating && !hasReachedEnd) {
- setSize((size) => size + 1);
- }
- }}
- />
-
- {hasReachedEnd ? null : (
-
- )}
-
-
-
-
-
-
- Are you absolutely sure?
-
- This action cannot be undone. This will permanently delete your
- chat and remove it from our servers.
-
-
-
- Cancel
-
- Continue
-
-
-
-
- >
+
+
+ History
+
+
+
+ {user
+ ? "History is disabled until your backend is connected."
+ : "Sign in to use your connected backend history later."}
+
+
+
);
}
diff --git a/components/chat/sidebar-user-nav.tsx b/components/chat/sidebar-user-nav.tsx
index 2a1e83dcad..b572459a13 100644
--- a/components/chat/sidebar-user-nav.tsx
+++ b/components/chat/sidebar-user-nav.tsx
@@ -1,7 +1,6 @@
"use client";
import { ChevronUp } from "lucide-react";
-import { useRouter } from "next/navigation";
import type { User } from "next-auth";
import { signOut, useSession } from "next-auth/react";
import { useTheme } from "next-themes";
@@ -17,7 +16,6 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
-import { guestRegex } from "@/lib/constants";
import { LoaderIcon } from "./icons";
import { toast } from "./toast";
@@ -30,12 +28,9 @@ function emailToHue(email: string): number {
}
export function SidebarUserNav({ user }: { user: User }) {
- const router = useRouter();
const { data, status } = useSession();
const { setTheme, resolvedTheme } = useTheme();
- const isGuest = guestRegex.test(data?.user?.email ?? "");
-
return (
@@ -65,7 +60,7 @@ export function SidebarUserNav({ user }: { user: User }) {
}}
/>
- {isGuest ? "Guest" : user?.email}
+ {data?.user?.email ?? user?.email}
@@ -100,17 +95,13 @@ export function SidebarUserNav({ user }: { user: User }) {
return;
}
- if (isGuest) {
- router.push("/login");
- } else {
- signOut({
- redirectTo: "/",
- });
- }
+ signOut({
+ redirectTo: "/",
+ });
}}
type="button"
>
- {isGuest ? "Login to your account" : "Sign out"}
+ Sign out
diff --git a/components/chat/sign-out-form.tsx b/components/chat/sign-out-form.tsx
deleted file mode 100644
index b56ea41c29..0000000000
--- a/components/chat/sign-out-form.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import Form from "next/form";
-
-import { signOut } from "@/app/(auth)/auth";
-
-export const SignOutForm = () => {
- return (
-
- );
-};
diff --git a/components/chat/text-editor.tsx b/components/chat/text-editor.tsx
index b2af5991b1..bb1af5cf6d 100644
--- a/components/chat/text-editor.tsx
+++ b/components/chat/text-editor.tsx
@@ -7,7 +7,7 @@ import { type Decoration, DecorationSet, EditorView } from "prosemirror-view";
import { memo, useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
-import type { Suggestion } from "@/lib/db/schema";
+import type { Suggestion } from "@/lib/chat/types";
import {
documentSchema,
handleTransaction,
diff --git a/components/chat/version-footer.tsx b/components/chat/version-footer.tsx
index 9da560c99b..067757cce0 100644
--- a/components/chat/version-footer.tsx
+++ b/components/chat/version-footer.tsx
@@ -1,15 +1,10 @@
"use client";
-import { isAfter } from "date-fns";
import { motion } from "framer-motion";
import { ChevronLeftIcon, ChevronRightIcon, DiffIcon } from "lucide-react";
import type { Dispatch, SetStateAction } from "react";
-import { useState } from "react";
-import { useSWRConfig } from "swr";
-import { useArtifact } from "@/hooks/use-artifact";
-import type { Document } from "@/lib/db/schema";
-import { cn, getDocumentTimestampByIndex } from "@/lib/utils";
-import { LoaderIcon } from "./icons";
+import type { Document } from "@/lib/chat/types";
+import { cn } from "@/lib/utils";
type VersionFooterProps = {
handleVersionChange: (type: "next" | "prev" | "toggle" | "latest") => void;
@@ -26,11 +21,6 @@ export const VersionFooter = ({
mode,
setMode,
}: VersionFooterProps) => {
- const { artifact } = useArtifact();
-
- const { mutate } = useSWRConfig();
- const [isMutating, setIsMutating] = useState(false);
-
if (!documents) {
return;
}
@@ -85,52 +75,13 @@ export const VersionFooter = ({
{
- setIsMutating(true);
-
- try {
- await mutate(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${artifact.documentId}`,
- await fetch(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/document?id=${artifact.documentId}×tamp=${getDocumentTimestampByIndex(
- documents,
- currentVersionIndex
- )}`,
- {
- method: "DELETE",
- }
- ),
- {
- optimisticData: documents
- ? [
- ...documents.filter((document) =>
- isAfter(
- new Date(document.createdAt),
- new Date(
- getDocumentTimestampByIndex(
- documents,
- currentVersionIndex
- )
- )
- )
- ),
- ]
- : [],
- }
- );
- } finally {
- setIsMutating(false);
- }
+ onClick={() => {
+ handleVersionChange("latest");
+ setMode("edit");
}}
type="button"
>
Restore
- {isMutating && (
-
-
-
- )}
void;
- showCreditCardAlert: boolean;
- setShowCreditCardAlert: Dispatch>;
};
const ActiveChatContext = createContext(null);
@@ -59,7 +54,6 @@ function extractChatId(pathname: string): string | null {
export function ActiveChatProvider({ children }: { children: ReactNode }) {
const pathname = usePathname();
const { setDataStream } = useDataStream();
- const { mutate } = useSWRConfig();
const chatIdFromUrl = extractChatId(pathname);
const isNewChat = !chatIdFromUrl;
@@ -80,22 +74,8 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
}, [currentModelId]);
const [input, setInput] = useState("");
- const [showCreditCardAlert, setShowCreditCardAlert] = useState(false);
-
- const { data: chatData, isLoading } = useSWR(
- isNewChat
- ? null
- : `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/messages?chatId=${chatId}`,
- fetcher,
- { revalidateOnFocus: false }
- );
-
- const initialMessages: ChatMessage[] = isNewChat
- ? []
- : (chatData?.messages ?? []);
- const visibility: VisibilityType = isNewChat
- ? "private"
- : (chatData?.visibility ?? "private");
+ const initialMessages: ChatMessage[] = [];
+ const visibility: VisibilityType = "private";
const {
messages,
@@ -141,9 +121,8 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
return {
body: {
id: request.id,
- ...(isToolApprovalContinuation
- ? { messages: request.messages }
- : { message: lastMessage }),
+ messages: request.messages,
+ ...(isToolApprovalContinuation ? {} : { message: lastMessage }),
selectedChatModel: currentModelIdRef.current,
selectedVisibilityType: visibility,
...request.body,
@@ -154,13 +133,8 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
onData: (dataPart) => {
setDataStream((ds) => (ds ? [...ds, dataPart] : []));
},
- onFinish: () => {
- mutate(unstable_serialize(getChatHistoryPaginationKey));
- },
onError: (error) => {
- if (error.message?.includes("AI Gateway requires a valid credit card")) {
- setShowCreditCardAlert(true);
- } else if (error instanceof ChatbotError) {
+ if (error instanceof ChatbotError) {
toast({ type: "error", description: error.message });
} else {
toast({
@@ -181,11 +155,8 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
if (loadedChatIds.current.has(chatId)) {
return;
}
- if (chatData?.messages) {
- loadedChatIds.current.add(chatId);
- setMessages(chatData.messages);
- }
- }, [chatId, chatData?.messages, setMessages]);
+ loadedChatIds.current.add(chatId);
+ }, [chatId]);
const prevChatIdRef = useRef(chatId);
useEffect(() => {
@@ -198,16 +169,14 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
}, [chatId, isNewChat, setMessages]);
useEffect(() => {
- if (chatData && !isNewChat) {
- const cookieModel = document.cookie
- .split("; ")
- .find((row) => row.startsWith("chat-model="))
- ?.split("=")[1];
- if (cookieModel) {
- setCurrentModelId(decodeURIComponent(cookieModel));
- }
+ const cookieModel = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("chat-model="))
+ ?.split("=")[1];
+ if (cookieModel) {
+ setCurrentModelId(decodeURIComponent(cookieModel));
}
- }, [chatData, isNewChat]);
+ }, []);
const hasAppendedQueryRef = useRef(false);
useEffect(() => {
@@ -228,21 +197,14 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
}, [sendMessage, chatId]);
useAutoResume({
- autoResume: !isNewChat && !!chatData,
+ autoResume: false,
initialMessages,
resumeStream,
setMessages,
});
- const isReadonly = isNewChat ? false : (chatData?.isReadonly ?? false);
-
- const { data: votes } = useSWR(
- !isReadonly && messages.length >= 2
- ? `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/vote?chatId=${chatId}`
- : null,
- fetcher,
- { revalidateOnFocus: false }
- );
+ const isReadonly = false;
+ const votes: Vote[] = [];
const value = useMemo(
() => ({
@@ -258,12 +220,10 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
setInput,
visibilityType: visibility,
isReadonly,
- isLoading: !isNewChat && isLoading,
+ isLoading: false,
votes,
currentModelId,
setCurrentModelId,
- showCreditCardAlert,
- setShowCreditCardAlert,
}),
[
chatId,
@@ -277,11 +237,8 @@ export function ActiveChatProvider({ children }: { children: ReactNode }) {
input,
visibility,
isReadonly,
- isNewChat,
- isLoading,
votes,
currentModelId,
- showCreditCardAlert,
]
);
diff --git a/hooks/use-chat-visibility.ts b/hooks/use-chat-visibility.ts
index 4d77b4e01c..a6f8f25e48 100644
--- a/hooks/use-chat-visibility.ts
+++ b/hooks/use-chat-visibility.ts
@@ -1,13 +1,6 @@
"use client";
-import { useMemo } from "react";
-import useSWR, { useSWRConfig } from "swr";
-import { unstable_serialize } from "swr/infinite";
-import { updateChatVisibility } from "@/app/(chat)/actions";
-import {
- type ChatHistory,
- getChatHistoryPaginationKey,
-} from "@/components/chat/sidebar-history";
+import { useState } from "react";
import type { VisibilityType } from "@/components/chat/visibility-selector";
export function useChatVisibility({
@@ -17,38 +10,12 @@ export function useChatVisibility({
chatId: string;
initialVisibilityType: VisibilityType;
}) {
- const { mutate, cache } = useSWRConfig();
- const history: ChatHistory = cache.get(
- `${process.env.NEXT_PUBLIC_BASE_PATH ?? ""}/api/history`
- )?.data;
-
- const { data: localVisibility, mutate: setLocalVisibility } = useSWR(
- `${chatId}-visibility`,
- null,
- {
- fallbackData: initialVisibilityType,
- }
- );
-
- const visibilityType = useMemo(() => {
- if (!history) {
- return localVisibility;
- }
- const chat = history.chats.find((currentChat) => currentChat.id === chatId);
- if (!chat) {
- return "private";
- }
- return chat.visibility;
- }, [history, chatId, localVisibility]);
+ void chatId;
+ const [visibilityType, setLocalVisibility] =
+ useState(initialVisibilityType);
const setVisibilityType = (updatedVisibilityType: VisibilityType) => {
setLocalVisibility(updatedVisibilityType);
- mutate(unstable_serialize(getChatHistoryPaginationKey));
-
- updateChatVisibility({
- chatId,
- visibility: updatedVisibilityType,
- });
};
return { visibilityType, setVisibilityType };
diff --git a/instrumentation-client.ts b/instrumentation-client.ts
deleted file mode 100644
index d3ed55a747..0000000000
--- a/instrumentation-client.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { initBotId } from "botid/client/core";
-
-initBotId({
- protect: [
- {
- path: "/api/chat",
- method: "POST",
- },
- ],
-});
diff --git a/instrumentation.ts b/instrumentation.ts
deleted file mode 100644
index f97d49bf7c..0000000000
--- a/instrumentation.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { registerOTel } from "@vercel/otel";
-
-export function register() {
- registerOTel({ serviceName: "chatbot" });
-}
diff --git a/lib/ai/entitlements.ts b/lib/ai/entitlements.ts
index 8f50152685..757ff7c4da 100644
--- a/lib/ai/entitlements.ts
+++ b/lib/ai/entitlements.ts
@@ -5,9 +5,6 @@ type Entitlements = {
};
export const entitlementsByUserType: Record = {
- guest: {
- maxMessagesPerHour: 10,
- },
regular: {
maxMessagesPerHour: 10,
},
diff --git a/lib/ai/models.ts b/lib/ai/models.ts
index eac9514203..0f05a999db 100644
--- a/lib/ai/models.ts
+++ b/lib/ai/models.ts
@@ -1,11 +1,11 @@
-export const DEFAULT_CHAT_MODEL = "moonshotai/kimi-k2.5";
+export const DEFAULT_CHAT_MODEL = "synthetic/ui-preview";
export const titleModel = {
- id: "moonshotai/kimi-k2.5",
- name: "Kimi K2.5",
- provider: "moonshotai",
- description: "Fast model for title generation",
- gatewayOrder: ["fireworks", "bedrock"],
+ id: "synthetic/ui-preview",
+ name: "UI Preview",
+ provider: "synthetic",
+ description: "Local streaming stub for frontend-only development",
+ gatewayOrder: [],
};
export type ModelCapabilities = {
@@ -25,128 +25,33 @@ export type ChatModel = {
export const chatModels: ChatModel[] = [
{
- id: "deepseek/deepseek-v3.2",
- name: "DeepSeek V3.2",
- provider: "deepseek",
- description: "Fast and capable model with tool use",
- gatewayOrder: ["bedrock", "deepinfra"],
- },
- {
- id: "moonshotai/kimi-k2.5",
- name: "Kimi K2.5",
- provider: "moonshotai",
- description: "Moonshot AI flagship model",
- gatewayOrder: ["fireworks", "bedrock"],
- },
- {
- id: "openai/gpt-oss-20b",
- name: "GPT OSS 20B",
- provider: "openai",
- description: "Compact reasoning model",
- gatewayOrder: ["groq", "bedrock"],
- reasoningEffort: "low",
- },
- {
- id: "openai/gpt-oss-120b",
- name: "GPT OSS 120B",
- provider: "openai",
- description: "Open-source 120B parameter model",
- gatewayOrder: ["fireworks", "bedrock"],
- reasoningEffort: "low",
- },
- {
- id: "xai/grok-4.1-fast-non-reasoning",
- name: "Grok 4.1 Fast",
- provider: "xai",
- description: "Fast non-reasoning model with tool use",
- gatewayOrder: ["xai"],
+ id: "synthetic/ui-preview",
+ name: "UI Preview",
+ provider: "synthetic",
+ description: "Local streaming stub for frontend-only development",
},
];
-export async function getCapabilities(): Promise<
- Record
-> {
- const results = await Promise.all(
- chatModels.map(async (model) => {
- try {
- const res = await fetch(
- `https://ai-gateway.vercel.sh/v1/models/${model.id}/endpoints`,
- { next: { revalidate: 86_400 } }
- );
- if (!res.ok) {
- return [model.id, { tools: false, vision: false, reasoning: false }];
- }
-
- const json = await res.json();
- const endpoints = json.data?.endpoints ?? [];
- const params = new Set(
- endpoints.flatMap(
- (e: { supported_parameters?: string[] }) =>
- e.supported_parameters ?? []
- )
- );
- const inputModalities = new Set(
- json.data?.architecture?.input_modalities ?? []
- );
-
- return [
- model.id,
- {
- tools: params.has("tools"),
- vision: inputModalities.has("image"),
- reasoning: params.has("reasoning"),
- },
- ];
- } catch {
- return [model.id, { tools: false, vision: false, reasoning: false }];
- }
- })
+export function getCapabilities(): Record {
+ return Object.fromEntries(
+ chatModels.map((model) => [
+ model.id,
+ { tools: false, vision: false, reasoning: false },
+ ])
);
-
- return Object.fromEntries(results);
}
export const isDemo = process.env.IS_DEMO === "1";
-type GatewayModel = {
- id: string;
- name: string;
- type?: string;
- tags?: string[];
-};
-
export type GatewayModelWithCapabilities = ChatModel & {
capabilities: ModelCapabilities;
};
-export async function getAllGatewayModels(): Promise<
- GatewayModelWithCapabilities[]
-> {
- try {
- const res = await fetch("https://ai-gateway.vercel.sh/v1/models", {
- next: { revalidate: 86_400 },
- });
- if (!res.ok) {
- return [];
- }
-
- const json = await res.json();
- return (json.data ?? [])
- .filter((m: GatewayModel) => m.type === "language")
- .map((m: GatewayModel) => ({
- id: m.id,
- name: m.name,
- provider: m.id.split("/")[0],
- description: "",
- capabilities: {
- tools: m.tags?.includes("tool-use") ?? false,
- vision: m.tags?.includes("vision") ?? false,
- reasoning: m.tags?.includes("reasoning") ?? false,
- },
- }));
- } catch {
- return [];
- }
+export function getAllGatewayModels(): GatewayModelWithCapabilities[] {
+ return chatModels.map((model) => ({
+ ...model,
+ capabilities: { tools: false, vision: false, reasoning: false },
+ }));
}
export function getActiveModels(): ChatModel[] {
diff --git a/lib/ai/prompts.ts b/lib/ai/prompts.ts
index 77d0d280f9..b58104d51e 100644
--- a/lib/ai/prompts.ts
+++ b/lib/ai/prompts.ts
@@ -1,4 +1,3 @@
-import type { Geo } from "@vercel/functions";
import type { ArtifactKind } from "@/components/chat/artifact";
export const artifactsPrompt = `
@@ -49,10 +48,10 @@ export const regularPrompt = `You are a helpful assistant. Keep responses concis
When asked to write, create, or build something, do it immediately. Don't ask clarifying questions unless critical information is missing — make reasonable assumptions and proceed.`;
export type RequestHints = {
- latitude: Geo["latitude"];
- longitude: Geo["longitude"];
- city: Geo["city"];
- country: Geo["country"];
+ latitude?: string;
+ longitude?: string;
+ city?: string;
+ country?: string;
};
export const getRequestPromptFromHints = (requestHints: RequestHints) => `\
diff --git a/lib/ai/tools/create-document.ts b/lib/ai/tools/create-document.ts
index a6a0527559..babae14e54 100644
--- a/lib/ai/tools/create-document.ts
+++ b/lib/ai/tools/create-document.ts
@@ -1,13 +1,11 @@
import { tool, type UIMessageStreamWriter } from "ai";
import type { Session } from "next-auth";
import { z } from "zod";
-import {
- artifactKinds,
- documentHandlersByArtifactKind,
-} from "@/lib/artifacts/server";
import type { ChatMessage } from "@/lib/types";
import { generateUUID } from "@/lib/utils";
+const artifactKinds = ["text", "code", "sheet"] as const;
+
type CreateDocumentProps = {
session: Session;
dataStream: UIMessageStreamWriter;
@@ -15,9 +13,9 @@ type CreateDocumentProps = {
};
export const createDocument = ({
- session,
+ session: _session,
dataStream,
- modelId,
+ modelId: _modelId,
}: CreateDocumentProps) =>
tool({
description:
@@ -57,33 +55,13 @@ export const createDocument = ({
transient: true,
});
- const documentHandler = documentHandlersByArtifactKind.find(
- (documentHandlerByArtifactKind) =>
- documentHandlerByArtifactKind.kind === kind
- );
-
- if (!documentHandler) {
- throw new Error(`No document handler found for kind: ${kind}`);
- }
-
- await documentHandler.onCreateDocument({
- id,
- title,
- dataStream,
- session,
- modelId,
- });
-
dataStream.write({ type: "data-finish", data: null, transient: true });
return {
id,
title,
kind,
- content:
- kind === "code"
- ? "A script was created and is now visible to the user."
- : "A document was created and is now visible to the user.",
+ content: "Document tools are disabled in UI-only mode.",
};
},
});
diff --git a/lib/ai/tools/edit-document.ts b/lib/ai/tools/edit-document.ts
index 014e6c1779..b19f343cd7 100644
--- a/lib/ai/tools/edit-document.ts
+++ b/lib/ai/tools/edit-document.ts
@@ -1,7 +1,6 @@
import { tool, type UIMessageStreamWriter } from "ai";
import type { Session } from "next-auth";
import { z } from "zod";
-import { getDocumentById, saveDocument } from "@/lib/db/queries";
import type { ChatMessage } from "@/lib/types";
type EditDocumentProps = {
@@ -9,7 +8,7 @@ type EditDocumentProps = {
dataStream: UIMessageStreamWriter;
};
-export const editDocument = ({ session, dataStream }: EditDocumentProps) =>
+export const editDocument = ({ session: _session, dataStream }: EditDocumentProps) =>
tool({
description:
"Make a targeted edit to an existing artifact by finding and replacing an exact string. Preferred over updateDocument for small changes. The old_string must match exactly.",
@@ -29,35 +28,9 @@ export const editDocument = ({ session, dataStream }: EditDocumentProps) =>
),
}),
execute: async ({ id, old_string, new_string, replace_all }) => {
- const document = await getDocumentById({ id });
-
- if (!document) {
- return { error: "Document not found" };
- }
-
- if (document.userId !== session.user?.id) {
- return { error: "Forbidden" };
- }
-
- if (!document.content) {
- return { error: "Document has no content" };
- }
-
- if (!document.content.includes(old_string)) {
- return { error: "old_string not found in document" };
- }
-
- const updated = replace_all
- ? document.content.replaceAll(old_string, new_string)
- : document.content.replace(old_string, new_string);
-
- await saveDocument({
- id: document.id,
- title: document.title,
- kind: document.kind,
- content: updated,
- userId: document.userId,
- });
+ void old_string;
+ void new_string;
+ void replace_all;
dataStream.write({
type: "data-clear",
@@ -65,36 +38,13 @@ export const editDocument = ({ session, dataStream }: EditDocumentProps) =>
transient: true,
});
- if (document.kind === "code") {
- dataStream.write({
- type: "data-codeDelta",
- data: updated,
- transient: true,
- });
- } else if (document.kind === "sheet") {
- dataStream.write({
- type: "data-sheetDelta",
- data: updated,
- transient: true,
- });
- } else {
- dataStream.write({
- type: "data-textDelta",
- data: updated,
- transient: true,
- });
- }
-
dataStream.write({ type: "data-finish", data: null, transient: true });
return {
id,
- title: document.title,
- kind: document.kind,
- content:
- document.kind === "code"
- ? "The script has been edited successfully."
- : "The document has been edited successfully.",
+ title: "Document",
+ kind: "text" as const,
+ content: "Document tools are disabled in UI-only mode.",
};
},
});
diff --git a/lib/ai/tools/request-suggestions.ts b/lib/ai/tools/request-suggestions.ts
index 2c6c3cd441..a737b5c7b1 100644
--- a/lib/ai/tools/request-suggestions.ts
+++ b/lib/ai/tools/request-suggestions.ts
@@ -1,11 +1,7 @@
-import { Output, streamText, tool, type UIMessageStreamWriter } from "ai";
+import { tool, type UIMessageStreamWriter } from "ai";
import type { Session } from "next-auth";
import { z } from "zod";
-import { getDocumentById, saveSuggestions } from "@/lib/db/queries";
-import type { Suggestion } from "@/lib/db/schema";
import type { ChatMessage } from "@/lib/types";
-import { generateUUID } from "@/lib/utils";
-import { getLanguageModel } from "../providers";
type RequestSuggestionsProps = {
session: Session;
@@ -14,9 +10,9 @@ type RequestSuggestionsProps = {
};
export const requestSuggestions = ({
- session,
- dataStream,
- modelId,
+ session: _session,
+ dataStream: _dataStream,
+ modelId: _modelId,
}: RequestSuggestionsProps) =>
tool({
description:
@@ -29,93 +25,11 @@ export const requestSuggestions = ({
),
}),
execute: async ({ documentId }) => {
- const document = await getDocumentById({ id: documentId });
-
- if (!document?.content) {
- return {
- error: "Document not found",
- };
- }
-
- if (document.userId !== session.user?.id) {
- return { error: "Forbidden" };
- }
-
- const suggestions: Omit<
- Suggestion,
- "userId" | "createdAt" | "documentCreatedAt"
- >[] = [];
-
- const { partialOutputStream } = streamText({
- model: getLanguageModel(modelId),
- system:
- "You are a writing assistant. Given a piece of writing, offer up to 5 suggestions to improve it. Each suggestion must contain full sentences, not just individual words. Describe what changed and why.",
- prompt: document.content,
- output: Output.array({
- element: z.object({
- originalSentence: z.string().describe("The original sentence"),
- suggestedSentence: z.string().describe("The suggested sentence"),
- description: z
- .string()
- .describe("The description of the suggestion"),
- }),
- }),
- });
-
- let processedCount = 0;
- for await (const partialOutput of partialOutputStream) {
- if (!partialOutput) {
- continue;
- }
-
- for (let i = processedCount; i < partialOutput.length; i++) {
- const element = partialOutput[i];
- if (
- !element?.originalSentence ||
- !element?.suggestedSentence ||
- !element?.description
- ) {
- continue;
- }
-
- const suggestion = {
- originalText: element.originalSentence,
- suggestedText: element.suggestedSentence,
- description: element.description,
- id: generateUUID(),
- documentId,
- isResolved: false,
- };
-
- dataStream.write({
- type: "data-suggestion",
- data: suggestion as Suggestion,
- transient: true,
- });
-
- suggestions.push(suggestion);
- processedCount++;
- }
- }
-
- if (session.user?.id) {
- const userId = session.user.id;
-
- await saveSuggestions({
- suggestions: suggestions.map((suggestion) => ({
- ...suggestion,
- userId,
- createdAt: new Date(),
- documentCreatedAt: document.createdAt,
- })),
- });
- }
-
return {
id: documentId,
- title: document.title,
- kind: document.kind,
- message: "Suggestions have been added to the document",
+ title: "Document",
+ kind: "text" as const,
+ message: "Document suggestions are disabled in UI-only mode.",
};
},
});
diff --git a/lib/ai/tools/update-document.ts b/lib/ai/tools/update-document.ts
index 357a72153d..567101fb07 100644
--- a/lib/ai/tools/update-document.ts
+++ b/lib/ai/tools/update-document.ts
@@ -1,8 +1,6 @@
import { tool, type UIMessageStreamWriter } from "ai";
import type { Session } from "next-auth";
import { z } from "zod";
-import { documentHandlersByArtifactKind } from "@/lib/artifacts/server";
-import { getDocumentById } from "@/lib/db/queries";
import type { ChatMessage } from "@/lib/types";
type UpdateDocumentProps = {
@@ -12,9 +10,9 @@ type UpdateDocumentProps = {
};
export const updateDocument = ({
- session,
+ session: _session,
dataStream,
- modelId,
+ modelId: _modelId,
}: UpdateDocumentProps) =>
tool({
description:
@@ -27,17 +25,7 @@ export const updateDocument = ({
.describe("The description of changes that need to be made"),
}),
execute: async ({ id, description }) => {
- const document = await getDocumentById({ id });
-
- if (!document) {
- return {
- error: "Document not found",
- };
- }
-
- if (document.userId !== session.user?.id) {
- return { error: "Forbidden" };
- }
+ void description;
dataStream.write({
type: "data-clear",
@@ -45,33 +33,13 @@ export const updateDocument = ({
transient: true,
});
- const documentHandler = documentHandlersByArtifactKind.find(
- (documentHandlerByArtifactKind) =>
- documentHandlerByArtifactKind.kind === document.kind
- );
-
- if (!documentHandler) {
- throw new Error(`No document handler found for kind: ${document.kind}`);
- }
-
- await documentHandler.onUpdateDocument({
- document,
- description,
- dataStream,
- session,
- modelId,
- });
-
dataStream.write({ type: "data-finish", data: null, transient: true });
return {
id,
- title: document.title,
- kind: document.kind,
- content:
- document.kind === "code"
- ? "The script has been updated successfully."
- : "The document has been updated successfully.",
+ title: "Document",
+ kind: "text" as const,
+ content: "Document tools are disabled in UI-only mode.",
};
},
});
diff --git a/lib/artifacts/server.ts b/lib/artifacts/server.ts
index 219455bd5c..44b25569f7 100644
--- a/lib/artifacts/server.ts
+++ b/lib/artifacts/server.ts
@@ -3,9 +3,7 @@ import type { Session } from "next-auth";
import { codeDocumentHandler } from "@/artifacts/code/server";
import { sheetDocumentHandler } from "@/artifacts/sheet/server";
import { textDocumentHandler } from "@/artifacts/text/server";
-import type { ArtifactKind } from "@/components/chat/artifact";
-import { saveDocument } from "../db/queries";
-import type { Document } from "../db/schema";
+import type { ArtifactKind, Document } from "../chat/types";
import type { ChatMessage } from "../types";
export type SaveDocumentProps = {
@@ -46,7 +44,7 @@ export function createDocumentHandler(config: {
return {
kind: config.kind,
onCreateDocument: async (args: CreateDocumentCallbackProps) => {
- const draftContent = await config.onCreateDocument({
+ await config.onCreateDocument({
id: args.id,
title: args.title,
dataStream: args.dataStream,
@@ -54,20 +52,10 @@ export function createDocumentHandler(config: {
modelId: args.modelId,
});
- if (args.session?.user?.id) {
- await saveDocument({
- id: args.id,
- title: args.title,
- content: draftContent,
- kind: config.kind,
- userId: args.session.user.id,
- });
- }
-
return;
},
onUpdateDocument: async (args: UpdateDocumentCallbackProps) => {
- const draftContent = await config.onUpdateDocument({
+ await config.onUpdateDocument({
document: args.document,
description: args.description,
dataStream: args.dataStream,
@@ -75,16 +63,6 @@ export function createDocumentHandler(config: {
modelId: args.modelId,
});
- if (args.session?.user?.id) {
- await saveDocument({
- id: args.document.id,
- title: args.document.title,
- content: draftContent,
- kind: config.kind,
- userId: args.session.user.id,
- });
- }
-
return;
},
};
diff --git a/lib/auth-mode.ts b/lib/auth-mode.ts
new file mode 100644
index 0000000000..940d329bf0
--- /dev/null
+++ b/lib/auth-mode.ts
@@ -0,0 +1,14 @@
+export function hasEntraAuthConfig() {
+ return Boolean(
+ process.env.AUTH_MICROSOFT_ENTRA_ID_ID &&
+ process.env.AUTH_MICROSOFT_ENTRA_ID_SECRET
+ );
+}
+
+export function isUiOnlyPreviewMode() {
+ return process.env.AUTH_REQUIRED !== "true";
+}
+
+export function shouldRequireAuth() {
+ return process.env.AUTH_REQUIRED === "true";
+}
diff --git a/lib/chat/types.ts b/lib/chat/types.ts
new file mode 100644
index 0000000000..f7c020aa2d
--- /dev/null
+++ b/lib/chat/types.ts
@@ -0,0 +1,47 @@
+export type VisibilityType = "private" | "public";
+
+export type ArtifactKind = "text" | "code" | "image" | "sheet";
+
+export type Chat = {
+ id: string;
+ createdAt: Date | string;
+ title: string;
+ userId: string;
+ visibility: VisibilityType;
+};
+
+export type DBMessage = {
+ id: string;
+ chatId: string;
+ role: "user" | "assistant" | "system";
+ parts: unknown;
+ attachments: unknown;
+ createdAt: Date | string;
+};
+
+export type Vote = {
+ chatId: string;
+ messageId: string;
+ isUpvoted: boolean;
+};
+
+export type Document = {
+ id: string;
+ createdAt: Date | string;
+ title: string;
+ content: string | null;
+ kind: ArtifactKind;
+ userId: string;
+};
+
+export type Suggestion = {
+ id: string;
+ documentId: string;
+ documentCreatedAt: Date | string;
+ originalText: string;
+ suggestedText: string;
+ description: string | null;
+ isResolved: boolean;
+ userId: string;
+ createdAt: Date | string;
+};
diff --git a/lib/constants.ts b/lib/constants.ts
index 7b72d9be4f..6be16bda3c 100644
--- a/lib/constants.ts
+++ b/lib/constants.ts
@@ -1,5 +1,3 @@
-import { generateDummyPassword } from "./db/utils";
-
export const isProductionEnvironment = process.env.NODE_ENV === "production";
export const isDevelopmentEnvironment = process.env.NODE_ENV === "development";
export const isTestEnvironment = Boolean(
@@ -8,10 +6,6 @@ export const isTestEnvironment = Boolean(
process.env.CI_PLAYWRIGHT
);
-export const guestRegex = /^guest-\d+$/;
-
-export const DUMMY_PASSWORD = generateDummyPassword();
-
export const suggestions = [
"What are the advantages of using Next.js?",
"Write code to demonstrate Dijkstra's algorithm",
diff --git a/lib/db/migrate.ts b/lib/db/migrate.ts
deleted file mode 100644
index 401a2d54cf..0000000000
--- a/lib/db/migrate.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { config } from "dotenv";
-import { drizzle } from "drizzle-orm/postgres-js";
-import { migrate } from "drizzle-orm/postgres-js/migrator";
-import postgres from "postgres";
-
-config({
- path: ".env.local",
-});
-
-const runMigrate = async () => {
- if (!process.env.POSTGRES_URL) {
- console.log("POSTGRES_URL not defined, skipping migrations");
- process.exit(0);
- }
-
- const connection = postgres(process.env.POSTGRES_URL, { max: 1 });
- const db = drizzle(connection);
-
- console.log("Running migrations...");
-
- const start = Date.now();
- await migrate(db, { migrationsFolder: "./lib/db/migrations" });
- const end = Date.now();
-
- console.log("Migrations completed in", end - start, "ms");
- process.exit(0);
-};
-
-runMigrate().catch((err) => {
- console.error("Migration failed");
- console.error(err);
- process.exit(1);
-});
diff --git a/lib/db/migrations/0000_initial.sql b/lib/db/migrations/0000_initial.sql
deleted file mode 100644
index 9af55d657f..0000000000
--- a/lib/db/migrations/0000_initial.sql
+++ /dev/null
@@ -1,67 +0,0 @@
-CREATE TABLE IF NOT EXISTS "User" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "email" varchar(64) NOT NULL,
- "password" varchar(64),
- "name" text,
- "emailVerified" boolean NOT NULL DEFAULT false,
- "image" text,
- "isAnonymous" boolean NOT NULL DEFAULT false,
- "createdAt" timestamp DEFAULT now() NOT NULL,
- "updatedAt" timestamp DEFAULT now() NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS "Chat" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "createdAt" timestamp NOT NULL,
- "title" text NOT NULL,
- "userId" uuid NOT NULL REFERENCES "User"("id"),
- "visibility" varchar NOT NULL DEFAULT 'private'
-);
-
-CREATE TABLE IF NOT EXISTS "Message_v2" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "chatId" uuid NOT NULL REFERENCES "Chat"("id"),
- "role" varchar NOT NULL,
- "parts" json NOT NULL,
- "attachments" json NOT NULL,
- "createdAt" timestamp NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS "Vote_v2" (
- "chatId" uuid NOT NULL REFERENCES "Chat"("id"),
- "messageId" uuid NOT NULL REFERENCES "Message_v2"("id"),
- "isUpvoted" boolean NOT NULL,
- PRIMARY KEY ("chatId", "messageId")
-);
-
-CREATE TABLE IF NOT EXISTS "Document" (
- "id" uuid DEFAULT gen_random_uuid() NOT NULL,
- "createdAt" timestamp NOT NULL,
- "title" text NOT NULL,
- "content" text,
- "text" varchar NOT NULL DEFAULT 'text',
- "userId" uuid NOT NULL REFERENCES "User"("id"),
- PRIMARY KEY ("id", "createdAt")
-);
-
-CREATE TABLE IF NOT EXISTS "Suggestion" (
- "id" uuid DEFAULT gen_random_uuid() NOT NULL,
- "documentId" uuid NOT NULL,
- "documentCreatedAt" timestamp NOT NULL,
- "originalText" text NOT NULL,
- "suggestedText" text NOT NULL,
- "description" text,
- "isResolved" boolean NOT NULL DEFAULT false,
- "userId" uuid NOT NULL REFERENCES "User"("id"),
- "createdAt" timestamp NOT NULL,
- PRIMARY KEY ("id"),
- FOREIGN KEY ("documentId", "documentCreatedAt") REFERENCES "Document"("id", "createdAt")
-);
-
-CREATE TABLE IF NOT EXISTS "Stream" (
- "id" uuid DEFAULT gen_random_uuid() NOT NULL,
- "chatId" uuid NOT NULL,
- "createdAt" timestamp NOT NULL,
- PRIMARY KEY ("id"),
- FOREIGN KEY ("chatId") REFERENCES "Chat"("id")
-);
diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json
deleted file mode 100644
index a39cb79532..0000000000
--- a/lib/db/migrations/meta/_journal.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "version": "7",
- "dialect": "postgresql",
- "entries": [
- {
- "idx": 0,
- "version": "7",
- "when": 1710000000000,
- "tag": "0000_initial",
- "breakpoints": true
- }
- ]
-}
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
deleted file mode 100644
index d4ec3583ca..0000000000
--- a/lib/db/queries.ts
+++ /dev/null
@@ -1,632 +0,0 @@
-import "server-only";
-
-import {
- and,
- asc,
- count,
- desc,
- eq,
- gt,
- gte,
- inArray,
- lt,
- type SQL,
-} from "drizzle-orm";
-import { drizzle } from "drizzle-orm/postgres-js";
-import postgres from "postgres";
-import type { ArtifactKind } from "@/components/chat/artifact";
-import type { VisibilityType } from "@/components/chat/visibility-selector";
-import { ChatbotError } from "../errors";
-import { generateUUID } from "../utils";
-import {
- type Chat,
- chat,
- type DBMessage,
- document,
- message,
- type Suggestion,
- stream,
- suggestion,
- type User,
- user,
- vote,
-} from "./schema";
-import { generateHashedPassword } from "./utils";
-
-const client = postgres(process.env.POSTGRES_URL ?? "");
-const db = drizzle(client);
-
-export async function getUser(email: string): Promise {
- try {
- return await db.select().from(user).where(eq(user.email, email));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get user by email"
- );
- }
-}
-
-export async function createUser(email: string, password: string) {
- const hashedPassword = generateHashedPassword(password);
-
- try {
- return await db.insert(user).values({ email, password: hashedPassword });
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to create user");
- }
-}
-
-export async function createGuestUser() {
- const email = `guest-${Date.now()}`;
- const password = generateHashedPassword(generateUUID());
-
- try {
- return await db.insert(user).values({ email, password }).returning({
- id: user.id,
- email: user.email,
- });
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to create guest user"
- );
- }
-}
-
-export async function saveChat({
- id,
- userId,
- title,
- visibility,
-}: {
- id: string;
- userId: string;
- title: string;
- visibility: VisibilityType;
-}) {
- try {
- return await db.insert(chat).values({
- id,
- createdAt: new Date(),
- userId,
- title,
- visibility,
- });
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to save chat");
- }
-}
-
-export async function deleteChatById({ id }: { id: string }) {
- try {
- await db.delete(vote).where(eq(vote.chatId, id));
- await db.delete(message).where(eq(message.chatId, id));
- await db.delete(stream).where(eq(stream.chatId, id));
-
- const [chatsDeleted] = await db
- .delete(chat)
- .where(eq(chat.id, id))
- .returning();
- return chatsDeleted;
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to delete chat by id"
- );
- }
-}
-
-export async function deleteAllChatsByUserId({ userId }: { userId: string }) {
- try {
- const userChats = await db
- .select({ id: chat.id })
- .from(chat)
- .where(eq(chat.userId, userId));
-
- if (userChats.length === 0) {
- return { deletedCount: 0 };
- }
-
- const chatIds = userChats.map((c) => c.id);
-
- await db.delete(vote).where(inArray(vote.chatId, chatIds));
- await db.delete(message).where(inArray(message.chatId, chatIds));
- await db.delete(stream).where(inArray(stream.chatId, chatIds));
-
- const deletedChats = await db
- .delete(chat)
- .where(eq(chat.userId, userId))
- .returning();
-
- return { deletedCount: deletedChats.length };
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to delete all chats by user id"
- );
- }
-}
-
-export async function getChatsByUserId({
- id,
- limit,
- startingAfter,
- endingBefore,
-}: {
- id: string;
- limit: number;
- startingAfter: string | null;
- endingBefore: string | null;
-}) {
- try {
- const extendedLimit = limit + 1;
-
- const query = (whereCondition?: SQL) =>
- db
- .select()
- .from(chat)
- .where(
- whereCondition
- ? and(whereCondition, eq(chat.userId, id))
- : eq(chat.userId, id)
- )
- .orderBy(desc(chat.createdAt))
- .limit(extendedLimit);
-
- let filteredChats: Chat[] = [];
-
- if (startingAfter) {
- const [selectedChat] = await db
- .select()
- .from(chat)
- .where(eq(chat.id, startingAfter))
- .limit(1);
-
- if (!selectedChat) {
- throw new ChatbotError(
- "not_found:database",
- `Chat with id ${startingAfter} not found`
- );
- }
-
- filteredChats = await query(gt(chat.createdAt, selectedChat.createdAt));
- } else if (endingBefore) {
- const [selectedChat] = await db
- .select()
- .from(chat)
- .where(eq(chat.id, endingBefore))
- .limit(1);
-
- if (!selectedChat) {
- throw new ChatbotError(
- "not_found:database",
- `Chat with id ${endingBefore} not found`
- );
- }
-
- filteredChats = await query(lt(chat.createdAt, selectedChat.createdAt));
- } else {
- filteredChats = await query();
- }
-
- const hasMore = filteredChats.length > limit;
-
- return {
- chats: hasMore ? filteredChats.slice(0, limit) : filteredChats,
- hasMore,
- };
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get chats by user id"
- );
- }
-}
-
-export async function getChatById({ id }: { id: string }) {
- try {
- const [selectedChat] = await db.select().from(chat).where(eq(chat.id, id));
- if (!selectedChat) {
- return null;
- }
-
- return selectedChat;
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to get chat by id");
- }
-}
-
-export async function saveMessages({ messages }: { messages: DBMessage[] }) {
- try {
- return await db.insert(message).values(messages);
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to save messages");
- }
-}
-
-export async function updateMessage({
- id,
- parts,
-}: {
- id: string;
- parts: DBMessage["parts"];
-}) {
- try {
- return await db.update(message).set({ parts }).where(eq(message.id, id));
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to update message");
- }
-}
-
-export async function getMessagesByChatId({ id }: { id: string }) {
- try {
- return await db
- .select()
- .from(message)
- .where(eq(message.chatId, id))
- .orderBy(asc(message.createdAt));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get messages by chat id"
- );
- }
-}
-
-export async function voteMessage({
- chatId,
- messageId,
- type,
-}: {
- chatId: string;
- messageId: string;
- type: "up" | "down";
-}) {
- try {
- const [existingVote] = await db
- .select()
- .from(vote)
- .where(and(eq(vote.messageId, messageId)));
-
- if (existingVote) {
- return await db
- .update(vote)
- .set({ isUpvoted: type === "up" })
- .where(and(eq(vote.messageId, messageId), eq(vote.chatId, chatId)));
- }
- return await db.insert(vote).values({
- chatId,
- messageId,
- isUpvoted: type === "up",
- });
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to vote message");
- }
-}
-
-export async function getVotesByChatId({ id }: { id: string }) {
- try {
- return await db.select().from(vote).where(eq(vote.chatId, id));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get votes by chat id"
- );
- }
-}
-
-export async function saveDocument({
- id,
- title,
- kind,
- content,
- userId,
-}: {
- id: string;
- title: string;
- kind: ArtifactKind;
- content: string;
- userId: string;
-}) {
- try {
- return await db
- .insert(document)
- .values({
- id,
- title,
- kind,
- content,
- userId,
- createdAt: new Date(),
- })
- .returning();
- } catch (_error) {
- throw new ChatbotError("bad_request:database", "Failed to save document");
- }
-}
-
-export async function updateDocumentContent({
- id,
- content,
-}: {
- id: string;
- content: string;
-}) {
- try {
- const docs = await db
- .select()
- .from(document)
- .where(eq(document.id, id))
- .orderBy(desc(document.createdAt))
- .limit(1);
-
- const latest = docs[0];
- if (!latest) {
- throw new ChatbotError("not_found:database", "Document not found");
- }
-
- return await db
- .update(document)
- .set({ content })
- .where(and(eq(document.id, id), eq(document.createdAt, latest.createdAt)))
- .returning();
- } catch (_error) {
- if (_error instanceof ChatbotError) {
- throw _error;
- }
- throw new ChatbotError(
- "bad_request:database",
- "Failed to update document content"
- );
- }
-}
-
-export async function getDocumentsById({ id }: { id: string }) {
- try {
- const documents = await db
- .select()
- .from(document)
- .where(eq(document.id, id))
- .orderBy(asc(document.createdAt));
-
- return documents;
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get documents by id"
- );
- }
-}
-
-export async function getDocumentById({ id }: { id: string }) {
- try {
- const [selectedDocument] = await db
- .select()
- .from(document)
- .where(eq(document.id, id))
- .orderBy(desc(document.createdAt));
-
- return selectedDocument;
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get document by id"
- );
- }
-}
-
-export async function deleteDocumentsByIdAfterTimestamp({
- id,
- timestamp,
-}: {
- id: string;
- timestamp: Date;
-}) {
- try {
- await db
- .delete(suggestion)
- .where(
- and(
- eq(suggestion.documentId, id),
- gt(suggestion.documentCreatedAt, timestamp)
- )
- );
-
- return await db
- .delete(document)
- .where(and(eq(document.id, id), gt(document.createdAt, timestamp)))
- .returning();
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to delete documents by id after timestamp"
- );
- }
-}
-
-export async function saveSuggestions({
- suggestions,
-}: {
- suggestions: Suggestion[];
-}) {
- try {
- return await db.insert(suggestion).values(suggestions);
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to save suggestions"
- );
- }
-}
-
-export async function getSuggestionsByDocumentId({
- documentId,
-}: {
- documentId: string;
-}) {
- try {
- return await db
- .select()
- .from(suggestion)
- .where(eq(suggestion.documentId, documentId));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get suggestions by document id"
- );
- }
-}
-
-export async function getMessageById({ id }: { id: string }) {
- try {
- return await db.select().from(message).where(eq(message.id, id));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get message by id"
- );
- }
-}
-
-export async function deleteMessagesByChatIdAfterTimestamp({
- chatId,
- timestamp,
-}: {
- chatId: string;
- timestamp: Date;
-}) {
- try {
- const messagesToDelete = await db
- .select({ id: message.id })
- .from(message)
- .where(
- and(eq(message.chatId, chatId), gte(message.createdAt, timestamp))
- );
-
- const messageIds = messagesToDelete.map(
- (currentMessage) => currentMessage.id
- );
-
- if (messageIds.length > 0) {
- await db
- .delete(vote)
- .where(
- and(eq(vote.chatId, chatId), inArray(vote.messageId, messageIds))
- );
-
- return await db
- .delete(message)
- .where(
- and(eq(message.chatId, chatId), inArray(message.id, messageIds))
- );
- }
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to delete messages by chat id after timestamp"
- );
- }
-}
-
-export async function updateChatVisibilityById({
- chatId,
- visibility,
-}: {
- chatId: string;
- visibility: "private" | "public";
-}) {
- try {
- return await db.update(chat).set({ visibility }).where(eq(chat.id, chatId));
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to update chat visibility by id"
- );
- }
-}
-
-export async function updateChatTitleById({
- chatId,
- title,
-}: {
- chatId: string;
- title: string;
-}) {
- try {
- return await db.update(chat).set({ title }).where(eq(chat.id, chatId));
- } catch (_error) {
- return;
- }
-}
-
-export async function getMessageCountByUserId({
- id,
- differenceInHours,
-}: {
- id: string;
- differenceInHours: number;
-}) {
- try {
- const cutoffTime = new Date(
- Date.now() - differenceInHours * 60 * 60 * 1000
- );
-
- const [stats] = await db
- .select({ count: count(message.id) })
- .from(message)
- .innerJoin(chat, eq(message.chatId, chat.id))
- .where(
- and(
- eq(chat.userId, id),
- gte(message.createdAt, cutoffTime),
- eq(message.role, "user")
- )
- )
- .execute();
-
- return stats?.count ?? 0;
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get message count by user id"
- );
- }
-}
-
-export async function createStreamId({
- streamId,
- chatId,
-}: {
- streamId: string;
- chatId: string;
-}) {
- try {
- await db
- .insert(stream)
- .values({ id: streamId, chatId, createdAt: new Date() });
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to create stream id"
- );
- }
-}
-
-export async function getStreamIdsByChatId({ chatId }: { chatId: string }) {
- try {
- const streamIds = await db
- .select({ id: stream.id })
- .from(stream)
- .where(eq(stream.chatId, chatId))
- .orderBy(asc(stream.createdAt))
- .execute();
-
- return streamIds.map(({ id }) => id);
- } catch (_error) {
- throw new ChatbotError(
- "bad_request:database",
- "Failed to get stream ids by chat id"
- );
- }
-}
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
deleted file mode 100644
index 33fb26c4f1..0000000000
--- a/lib/db/schema.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import type { InferSelectModel } from "drizzle-orm";
-import {
- boolean,
- foreignKey,
- json,
- pgTable,
- primaryKey,
- text,
- timestamp,
- uuid,
- varchar,
-} from "drizzle-orm/pg-core";
-
-export const user = pgTable("User", {
- id: uuid("id").primaryKey().notNull().defaultRandom(),
- email: varchar("email", { length: 64 }).notNull(),
- password: varchar("password", { length: 64 }),
- name: text("name"),
- emailVerified: boolean("emailVerified").notNull().default(false),
- image: text("image"),
- isAnonymous: boolean("isAnonymous").notNull().default(false),
- createdAt: timestamp("createdAt").notNull().defaultNow(),
- updatedAt: timestamp("updatedAt").notNull().defaultNow(),
-});
-
-export type User = InferSelectModel;
-
-export const chat = pgTable("Chat", {
- id: uuid("id").primaryKey().notNull().defaultRandom(),
- createdAt: timestamp("createdAt").notNull(),
- title: text("title").notNull(),
- userId: uuid("userId")
- .notNull()
- .references(() => user.id),
- visibility: varchar("visibility", { enum: ["public", "private"] })
- .notNull()
- .default("private"),
-});
-
-export type Chat = InferSelectModel;
-
-export const message = pgTable("Message_v2", {
- id: uuid("id").primaryKey().notNull().defaultRandom(),
- chatId: uuid("chatId")
- .notNull()
- .references(() => chat.id),
- role: varchar("role").notNull(),
- parts: json("parts").notNull(),
- attachments: json("attachments").notNull(),
- createdAt: timestamp("createdAt").notNull(),
-});
-
-export type DBMessage = InferSelectModel;
-
-export const vote = pgTable(
- "Vote_v2",
- {
- chatId: uuid("chatId")
- .notNull()
- .references(() => chat.id),
- messageId: uuid("messageId")
- .notNull()
- .references(() => message.id),
- isUpvoted: boolean("isUpvoted").notNull(),
- },
- (table) => ({
- pk: primaryKey({ columns: [table.chatId, table.messageId] }),
- })
-);
-
-export type Vote = InferSelectModel;
-
-export const document = pgTable(
- "Document",
- {
- id: uuid("id").notNull().defaultRandom(),
- createdAt: timestamp("createdAt").notNull(),
- title: text("title").notNull(),
- content: text("content"),
- kind: varchar("text", { enum: ["text", "code", "image", "sheet"] })
- .notNull()
- .default("text"),
- userId: uuid("userId")
- .notNull()
- .references(() => user.id),
- },
- (table) => ({
- pk: primaryKey({ columns: [table.id, table.createdAt] }),
- })
-);
-
-export type Document = InferSelectModel;
-
-export const suggestion = pgTable(
- "Suggestion",
- {
- id: uuid("id").notNull().defaultRandom(),
- documentId: uuid("documentId").notNull(),
- documentCreatedAt: timestamp("documentCreatedAt").notNull(),
- originalText: text("originalText").notNull(),
- suggestedText: text("suggestedText").notNull(),
- description: text("description"),
- isResolved: boolean("isResolved").notNull().default(false),
- userId: uuid("userId")
- .notNull()
- .references(() => user.id),
- createdAt: timestamp("createdAt").notNull(),
- },
- (table) => ({
- pk: primaryKey({ columns: [table.id] }),
- documentRef: foreignKey({
- columns: [table.documentId, table.documentCreatedAt],
- foreignColumns: [document.id, document.createdAt],
- }),
- })
-);
-
-export type Suggestion = InferSelectModel;
-
-export const stream = pgTable(
- "Stream",
- {
- id: uuid("id").notNull().defaultRandom(),
- chatId: uuid("chatId").notNull(),
- createdAt: timestamp("createdAt").notNull(),
- },
- (table) => ({
- pk: primaryKey({ columns: [table.id] }),
- chatRef: foreignKey({
- columns: [table.chatId],
- foreignColumns: [chat.id],
- }),
- })
-);
-
-export type Stream = InferSelectModel;
diff --git a/lib/db/utils.ts b/lib/db/utils.ts
deleted file mode 100644
index 233176c038..0000000000
--- a/lib/db/utils.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { generateId } from "ai";
-import { genSaltSync, hashSync } from "bcrypt-ts";
-
-export function generateHashedPassword(password: string) {
- const salt = genSaltSync(10);
- const hash = hashSync(password, salt);
-
- return hash;
-}
-
-export function generateDummyPassword() {
- const password = generateId();
- const hashedPassword = generateHashedPassword(password);
-
- return hashedPassword;
-}
diff --git a/lib/editor/suggestions.tsx b/lib/editor/suggestions.tsx
index 6f3eab2a38..cf2294ab8b 100644
--- a/lib/editor/suggestions.tsx
+++ b/lib/editor/suggestions.tsx
@@ -1,7 +1,7 @@
import type { Node } from "prosemirror-model";
import { Plugin, PluginKey } from "prosemirror-state";
import { DecorationSet } from "prosemirror-view";
-import type { Suggestion } from "@/lib/db/schema";
+import type { Suggestion } from "@/lib/chat/types";
export interface UISuggestion extends Suggestion {
selectionStart: number;
diff --git a/lib/errors.ts b/lib/errors.ts
index 68f569b916..d65fe1194b 100644
--- a/lib/errors.ts
+++ b/lib/errors.ts
@@ -15,8 +15,7 @@ export type Surface =
| "history"
| "vote"
| "document"
- | "suggestions"
- | "activate_gateway";
+ | "suggestions";
export type ErrorCode = `${ErrorType}:${Surface}`;
@@ -32,7 +31,6 @@ export const visibilityBySurface: Record = {
vote: "response",
document: "response",
suggestions: "response",
- activate_gateway: "response",
};
export class ChatbotError extends Error {
@@ -84,9 +82,6 @@ export function getMessageByErrorCode(errorCode: ErrorCode): string {
case "bad_request:api":
return "The request couldn't be processed. Please check your input and try again.";
- case "bad_request:activate_gateway":
- return "AI Gateway requires a valid credit card on file to service requests. Please visit https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card to add a card and unlock your free credits.";
-
case "unauthorized:auth":
return "You need to sign in before continuing.";
case "forbidden:auth":
diff --git a/lib/ratelimit.ts b/lib/ratelimit.ts
deleted file mode 100644
index ca417e92b4..0000000000
--- a/lib/ratelimit.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { createClient } from "redis";
-
-import { isProductionEnvironment } from "@/lib/constants";
-import { ChatbotError } from "@/lib/errors";
-
-const MAX_MESSAGES = 10;
-const TTL_SECONDS = 60 * 60;
-
-let client: ReturnType | null = null;
-
-function getClient() {
- if (!client && process.env.REDIS_URL) {
- client = createClient({ url: process.env.REDIS_URL });
- client.on("error", () => undefined);
- client.connect().catch(() => {
- client = null;
- });
- }
- return client;
-}
-
-export async function checkIpRateLimit(ip: string | undefined) {
- if (!isProductionEnvironment || !ip) {
- return;
- }
-
- const redis = getClient();
- if (!redis?.isReady) {
- return;
- }
-
- try {
- const key = `ip-rate-limit:${ip}`;
- const [count] = await redis
- .multi()
- .incr(key)
- .expire(key, TTL_SECONDS, "NX")
- .exec();
-
- if (typeof count === "number" && count > MAX_MESSAGES) {
- throw new ChatbotError("rate_limit:chat");
- }
- } catch (error) {
- if (error instanceof ChatbotError) {
- throw error;
- }
- }
-}
diff --git a/lib/types.ts b/lib/types.ts
index b2e45a002e..23cf73f463 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -5,7 +5,7 @@ import type { createDocument } from "./ai/tools/create-document";
import type { getWeather } from "./ai/tools/get-weather";
import type { requestSuggestions } from "./ai/tools/request-suggestions";
import type { updateDocument } from "./ai/tools/update-document";
-import type { Suggestion } from "./db/schema";
+import type { Suggestion } from "./chat/types";
export const messageMetadataSchema = z.object({
createdAt: z.string(),
diff --git a/lib/utils.ts b/lib/utils.ts
index 27dc247d7d..aba3b77612 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -5,7 +5,7 @@ import type {
import { type ClassValue, clsx } from 'clsx';
import { formatISO } from 'date-fns';
import { twMerge } from 'tailwind-merge';
-import type { DBMessage, Document } from '@/lib/db/schema';
+import type { DBMessage } from "@/lib/chat/types";
import { ChatbotError, type ErrorCode } from './errors';
import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types';
@@ -13,17 +13,6 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
-export const fetcher = async (url: string) => {
- const response = await fetch(url);
-
- if (!response.ok) {
- const { code, cause } = await response.json();
- throw new ChatbotError(code as ErrorCode, cause);
- }
-
- return response.json();
-};
-
export async function fetchWithErrorHandlers(
input: RequestInfo | URL,
init?: RequestInit,
@@ -54,16 +43,6 @@ export function generateUUID(): string {
});
}
-export function getDocumentTimestampByIndex(
- documents: Document[],
- index: number,
-) {
- if (!documents) { return new Date(); }
- if (index > documents.length) { return new Date(); }
-
- return documents[index].createdAt;
-}
-
export function sanitizeText(text: string) {
return text.replace('', '');
}
diff --git a/next.config.ts b/next.config.ts
index d066f56356..ab25b3204a 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,4 +1,3 @@
-import { withBotId } from "botid/next/config";
import type { NextConfig } from "next";
const basePath = process.env.IS_DEMO === "1" ? "/demo" : "";
@@ -36,10 +35,6 @@ const nextConfig: NextConfig = {
{
hostname: "avatar.vercel.sh",
},
- {
- protocol: "https",
- hostname: "*.public.blob.vercel-storage.com",
- },
],
},
experimental: {
@@ -51,4 +46,4 @@ const nextConfig: NextConfig = {
},
};
-export default withBotId(nextConfig);
+export default nextConfig;
diff --git a/package.json b/package.json
index 220a564b86..0c6f5e5c88 100644
--- a/package.json
+++ b/package.json
@@ -4,17 +4,12 @@
"private": true,
"scripts": {
"dev": "next dev --turbo",
- "build": "tsx lib/db/migrate && next build",
+ "build": "next build",
"start": "next start",
+ "format": "prettier --write .",
+ "format:check": "prettier --check .",
"check": "ultracite check",
"fix": "ultracite fix",
- "db:generate": "drizzle-kit generate",
- "db:migrate": "npx tsx lib/db/migrate.ts",
- "db:studio": "drizzle-kit studio",
- "db:push": "drizzle-kit push",
- "db:pull": "drizzle-kit pull",
- "db:check": "drizzle-kit check",
- "db:up": "drizzle-kit up",
"test": "export PLAYWRIGHT=True && pnpm exec playwright test"
},
"dependencies": {
@@ -24,20 +19,12 @@
"@codemirror/state": "^6.5.0",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.35.3",
- "@opentelemetry/api": "^1.9.0",
- "@opentelemetry/api-logs": "^0.200.0",
"@radix-ui/react-use-controllable-state": "^1.2.2",
"@streamdown/cjk": "^1.0.2",
"@streamdown/code": "^1.0.3",
"@streamdown/math": "^1.0.2",
"@streamdown/mermaid": "^1.0.2",
- "@vercel/analytics": "^1.3.1",
- "@vercel/blob": "^0.24.1",
- "@vercel/functions": "^2.0.0",
- "@vercel/otel": "^1.12.0",
"ai": "6.0.116",
- "bcrypt-ts": "^5.0.2",
- "botid": "^1.5.11",
"class-variance-authority": "^0.7.1",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
@@ -46,7 +33,6 @@
"date-fns": "^4.1.0",
"diff-match-patch": "^1.0.5",
"dotenv": "^16.4.5",
- "drizzle-orm": "^0.34.0",
"fast-deep-equal": "^3.1.3",
"framer-motion": "^11.3.19",
"katex": "^0.16.28",
@@ -58,7 +44,6 @@
"next-themes": "^0.3.0",
"orderedmap": "^2.1.1",
"papaparse": "^5.5.2",
- "postgres": "^3.4.4",
"prosemirror-example-setup": "^1.2.3",
"prosemirror-inputrules": "^1.4.0",
"prosemirror-markdown": "^1.13.1",
@@ -71,9 +56,6 @@
"react": "19.0.1",
"react-data-grid": "7.0.0-beta.47",
"react-dom": "19.0.1",
- "redis": "^5.0.0",
- "resumable-stream": "^2.2.10",
- "server-only": "^0.0.1",
"shiki": "^3.21.0",
"sonner": "^1.5.0",
"streamdown": "^2.3.0",
@@ -96,8 +78,8 @@
"@types/react": "^18",
"@types/react-dom": "^18",
"babel-plugin-react-compiler": "^1.0.0",
- "drizzle-kit": "^0.25.0",
"postcss": "^8",
+ "prettier": "^3.8.3",
"tailwindcss": "^4.1.13",
"tsx": "^4.19.1",
"typescript": "^5.6.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b58ffa4797..7a4c9e0dd0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,12 +26,6 @@ importers:
'@codemirror/view':
specifier: ^6.35.3
version: 6.36.4
- '@opentelemetry/api':
- specifier: ^1.9.0
- version: 1.9.0
- '@opentelemetry/api-logs':
- specifier: ^0.200.0
- version: 0.200.0
'@radix-ui/react-use-controllable-state':
specifier: ^1.2.2
version: 1.2.2(@types/react@18.3.18)(react@19.0.1)
@@ -47,27 +41,9 @@ importers:
'@streamdown/mermaid':
specifier: ^1.0.2
version: 1.0.2(react@19.0.1)
- '@vercel/analytics':
- specifier: ^1.3.1
- version: 1.5.0(next@16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1)
- '@vercel/blob':
- specifier: ^0.24.1
- version: 0.24.1
- '@vercel/functions':
- specifier: ^2.0.0
- version: 2.2.13
- '@vercel/otel':
- specifier: ^1.12.0
- version: 1.14.0(@opentelemetry/api-logs@0.200.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))
ai:
specifier: 6.0.116
version: 6.0.116(zod@3.25.76)
- bcrypt-ts:
- specifier: ^5.0.2
- version: 5.0.3
- botid:
- specifier: ^1.5.11
- version: 1.5.11(next@16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -92,9 +68,6 @@ importers:
dotenv:
specifier: ^16.4.5
version: 16.4.7
- drizzle-orm:
- specifier: ^0.34.0
- version: 0.34.1(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@18.3.18)(@vercel/postgres@0.10.0)(kysely@0.28.11)(postgres@3.4.5)(react@19.0.1)
fast-deep-equal:
specifier: ^3.1.3
version: 3.1.3
@@ -128,9 +101,6 @@ importers:
papaparse:
specifier: ^5.5.2
version: 5.5.2
- postgres:
- specifier: ^3.4.4
- version: 3.4.5
prosemirror-example-setup:
specifier: ^1.2.3
version: 1.2.3
@@ -167,15 +137,6 @@ importers:
react-dom:
specifier: 19.0.1
version: 19.0.1(react@19.0.1)
- redis:
- specifier: ^5.0.0
- version: 5.9.0
- resumable-stream:
- specifier: ^2.2.10
- version: 2.2.10
- server-only:
- specifier: ^0.0.1
- version: 0.0.1
shiki:
specifier: ^3.21.0
version: 3.21.0
@@ -237,12 +198,12 @@ importers:
babel-plugin-react-compiler:
specifier: ^1.0.0
version: 1.0.0
- drizzle-kit:
- specifier: ^0.25.0
- version: 0.25.0
postcss:
specifier: ^8
version: 8.5.3
+ prettier:
+ specifier: ^3.8.3
+ version: 3.8.3
tailwindcss:
specifier: ^4.1.13
version: 4.1.16
@@ -425,314 +386,105 @@ packages:
'@codemirror/view@6.36.4':
resolution: {integrity: sha512-ZQ0V5ovw/miKEXTvjgzRyjnrk9TwriUB1k4R5p7uNnHR9Hus+D1SXHGdJshijEzPFjU25xea/7nhIeSqYFKdbA==}
- '@drizzle-team/brocli@0.10.2':
- resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
-
'@emnapi/runtime@1.7.1':
resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==}
- '@esbuild-kit/core-utils@3.3.2':
- resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
- deprecated: 'Merged into tsx: https://tsx.is'
-
- '@esbuild-kit/esm-loader@2.6.5':
- resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
- deprecated: 'Merged into tsx: https://tsx.is'
-
- '@esbuild/aix-ppc64@0.19.12':
- resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [aix]
-
'@esbuild/aix-ppc64@0.25.1':
resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
- '@esbuild/android-arm64@0.18.20':
- resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm64@0.19.12':
- resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
-
'@esbuild/android-arm64@0.25.1':
resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
- '@esbuild/android-arm@0.18.20':
- resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-arm@0.19.12':
- resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
-
'@esbuild/android-arm@0.25.1':
resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
- '@esbuild/android-x64@0.18.20':
- resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/android-x64@0.19.12':
- resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
-
'@esbuild/android-x64@0.25.1':
resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
- '@esbuild/darwin-arm64@0.18.20':
- resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-arm64@0.19.12':
- resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
-
'@esbuild/darwin-arm64@0.25.1':
resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
- '@esbuild/darwin-x64@0.18.20':
- resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.19.12':
- resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
-
'@esbuild/darwin-x64@0.25.1':
resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
- '@esbuild/freebsd-arm64@0.18.20':
- resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-arm64@0.19.12':
- resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
-
'@esbuild/freebsd-arm64@0.25.1':
resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
- '@esbuild/freebsd-x64@0.18.20':
- resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.19.12':
- resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
-
'@esbuild/freebsd-x64@0.25.1':
resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
- '@esbuild/linux-arm64@0.18.20':
- resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm64@0.19.12':
- resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
-
'@esbuild/linux-arm64@0.25.1':
resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
- '@esbuild/linux-arm@0.18.20':
- resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-arm@0.19.12':
- resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
-
'@esbuild/linux-arm@0.25.1':
resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
- '@esbuild/linux-ia32@0.18.20':
- resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-ia32@0.19.12':
- resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
-
'@esbuild/linux-ia32@0.25.1':
resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
- '@esbuild/linux-loong64@0.18.20':
- resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-loong64@0.19.12':
- resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
-
'@esbuild/linux-loong64@0.25.1':
resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
- '@esbuild/linux-mips64el@0.18.20':
- resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.19.12':
- resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
-
'@esbuild/linux-mips64el@0.25.1':
resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
- '@esbuild/linux-ppc64@0.18.20':
- resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.19.12':
- resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
-
'@esbuild/linux-ppc64@0.25.1':
resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
- '@esbuild/linux-riscv64@0.18.20':
- resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.19.12':
- resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
-
'@esbuild/linux-riscv64@0.25.1':
resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
- '@esbuild/linux-s390x@0.18.20':
- resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-s390x@0.19.12':
- resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
-
'@esbuild/linux-s390x@0.25.1':
resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
- '@esbuild/linux-x64@0.18.20':
- resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/linux-x64@0.19.12':
- resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
-
'@esbuild/linux-x64@0.25.1':
resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==}
engines: {node: '>=18'}
@@ -745,18 +497,6 @@ packages:
cpu: [arm64]
os: [netbsd]
- '@esbuild/netbsd-x64@0.18.20':
- resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/netbsd-x64@0.19.12':
- resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
-
'@esbuild/netbsd-x64@0.25.1':
resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==}
engines: {node: '>=18'}
@@ -769,100 +509,36 @@ packages:
cpu: [arm64]
os: [openbsd]
- '@esbuild/openbsd-x64@0.18.20':
- resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.19.12':
- resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
-
'@esbuild/openbsd-x64@0.25.1':
resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
- '@esbuild/sunos-x64@0.18.20':
- resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/sunos-x64@0.19.12':
- resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
-
'@esbuild/sunos-x64@0.25.1':
resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
- '@esbuild/win32-arm64@0.18.20':
- resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-arm64@0.19.12':
- resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
-
'@esbuild/win32-arm64@0.25.1':
resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
- '@esbuild/win32-ia32@0.18.20':
- resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-ia32@0.19.12':
- resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
-
'@esbuild/win32-ia32@0.25.1':
resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
- '@esbuild/win32-x64@0.18.20':
- resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
-
- '@esbuild/win32-x64@0.19.12':
- resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
-
'@esbuild/win32-x64@0.25.1':
resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
- '@fastify/busboy@2.1.1':
- resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==}
- engines: {node: '>=14'}
-
'@floating-ui/core@1.6.9':
resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
@@ -1087,9 +763,6 @@ packages:
'@mermaid-js/parser@1.0.0':
resolution: {integrity: sha512-vvK0Hi/VWndxoh03Mmz6wa1KDriSPjS2XMZL/1l19HFwygiObEEoEwSDxOqyLzzAI6J2PU3261JjTMTO7x+BPw==}
- '@neondatabase/serverless@0.9.5':
- resolution: {integrity: sha512-siFas6gItqv6wD/pZnvdu34wEqgG3nSE6zWZdq5j2DEsa+VvX8i/5HXJOo06qrw5axPXn+lGCxeR+NLaSPIXug==}
-
'@next/env@16.2.0':
resolution: {integrity: sha512-OZIbODWWAi0epQRCRjNe1VO45LOFBzgiyqmTLzIqWq6u1wrxKnAyz1HH6tgY/Mc81YzIjRPoYsPAEr4QV4l9TA==}
@@ -1145,58 +818,10 @@ packages:
cpu: [x64]
os: [win32]
- '@opentelemetry/api-logs@0.200.0':
- resolution: {integrity: sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==}
- engines: {node: '>=8.0.0'}
-
- '@opentelemetry/api-logs@0.57.2':
- resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==}
- engines: {node: '>=14'}
-
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
- '@opentelemetry/core@1.30.1':
- resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': '>=1.0.0 <1.10.0'
-
- '@opentelemetry/instrumentation@0.57.2':
- resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': ^1.3.0
-
- '@opentelemetry/resources@1.30.1':
- resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': '>=1.0.0 <1.10.0'
-
- '@opentelemetry/sdk-logs@0.57.2':
- resolution: {integrity: sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': '>=1.4.0 <1.10.0'
-
- '@opentelemetry/sdk-metrics@1.30.1':
- resolution: {integrity: sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': '>=1.3.0 <1.10.0'
-
- '@opentelemetry/sdk-trace-base@1.30.1':
- resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==}
- engines: {node: '>=14'}
- peerDependencies:
- '@opentelemetry/api': '>=1.0.0 <1.10.0'
-
- '@opentelemetry/semantic-conventions@1.28.0':
- resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==}
- engines: {node: '>=14'}
-
'@panva/hkdf@1.2.1':
resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==}
@@ -1895,34 +1520,6 @@ packages:
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
- '@redis/bloom@5.9.0':
- resolution: {integrity: sha512-W9D8yfKTWl4tP8lkC3MRYkMz4OfbuzE/W8iObe0jFgoRmgMfkBV+Vj38gvIqZPImtY0WB34YZkX3amYuQebvRQ==}
- engines: {node: '>= 18'}
- peerDependencies:
- '@redis/client': ^5.9.0
-
- '@redis/client@5.9.0':
- resolution: {integrity: sha512-EI0Ti5pojD2p7TmcS7RRa+AJVahdQvP/urpcSbK/K9Rlk6+dwMJTQ354pCNGCwfke8x4yKr5+iH85wcERSkwLQ==}
- engines: {node: '>= 18'}
-
- '@redis/json@5.9.0':
- resolution: {integrity: sha512-Bm2jjLYaXdUWPb9RaEywxnjmzw7dWKDZI4MS79mTWPV16R982jVWBj6lY2ZGelJbwxHtEVg4/FSVgYDkuO/MxA==}
- engines: {node: '>= 18'}
- peerDependencies:
- '@redis/client': ^5.9.0
-
- '@redis/search@5.9.0':
- resolution: {integrity: sha512-jdk2csmJ29DlpvCIb2ySjix2co14/0iwIT3C0I+7ZaToXgPbgBMB+zfEilSuncI2F9JcVxHki0YtLA0xX3VdpA==}
- engines: {node: '>= 18'}
- peerDependencies:
- '@redis/client': ^5.9.0
-
- '@redis/time-series@5.9.0':
- resolution: {integrity: sha512-W6ILxcyOqhnI7ELKjJXOktIg3w4+aBHugDbVpgVLPZ+YDjObis1M0v7ZzwlpXhlpwsfePfipeSK+KWNuymk52w==}
- engines: {node: '>= 18'}
- peerDependencies:
- '@redis/client': ^5.9.0
-
'@shikijs/core@3.21.0':
resolution: {integrity: sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA==}
@@ -2210,9 +1807,6 @@ packages:
'@types/pdf-parse@1.1.4':
resolution: {integrity: sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==}
- '@types/pg@8.11.6':
- resolution: {integrity: sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==}
-
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
@@ -2224,9 +1818,6 @@ packages:
'@types/react@18.3.18':
resolution: {integrity: sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==}
- '@types/shimmer@1.2.0':
- resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==}
-
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -2239,75 +1830,10 @@ packages:
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
- '@vercel/analytics@1.5.0':
- resolution: {integrity: sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==}
- peerDependencies:
- '@remix-run/react': ^2
- '@sveltejs/kit': ^1 || ^2
- next: '>= 13'
- react: ^18 || ^19 || ^19.0.0-rc
- svelte: '>= 4'
- vue: ^3
- vue-router: ^4
- peerDependenciesMeta:
- '@remix-run/react':
- optional: true
- '@sveltejs/kit':
- optional: true
- next:
- optional: true
- react:
- optional: true
- svelte:
- optional: true
- vue:
- optional: true
- vue-router:
- optional: true
-
- '@vercel/blob@0.24.1':
- resolution: {integrity: sha512-wHzgKzvAuF4tRDoXk3wGBYzQZ9z2fLr4oftiR1hOclPEdA1aj2/0mizvO2l5w91eZlTAaFth0S1DlqrrXqpntg==}
- engines: {node: '>=16.14'}
-
- '@vercel/functions@2.2.13':
- resolution: {integrity: sha512-14ArBSIIcOBx9nrEgaJb4Bw+en1gl6eSoJWh8qjifLl5G3E4dRXCFOT8HP+w66vb9Wqyd1lAQBrmRhRwOj9X9A==}
- engines: {node: '>= 18'}
- peerDependencies:
- '@aws-sdk/credential-provider-web-identity': '*'
- peerDependenciesMeta:
- '@aws-sdk/credential-provider-web-identity':
- optional: true
-
- '@vercel/oidc@2.0.2':
- resolution: {integrity: sha512-59PBFx3T+k5hLTEWa3ggiMpGRz1OVvl9eN8SUai+A43IsqiOuAe7qPBf+cray/Fj6mkgnxm/D7IAtjc8zSHi7g==}
- engines: {node: '>= 18'}
-
'@vercel/oidc@3.1.0':
resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==}
engines: {node: '>= 20'}
- '@vercel/otel@1.14.0':
- resolution: {integrity: sha512-4FXrYvjmHh3ia2v/TN/iiz0oVIw1xSnkW/MzRtsUGpu4jlcfu1qXJIfugQ1iAZnRolyTKn8FxhoWA6EhiAIZBg==}
- engines: {node: '>=18'}
- peerDependencies:
- '@opentelemetry/api': '>=1.7.0 <2.0.0'
- '@opentelemetry/api-logs': '>=0.46.0 <0.200.0'
- '@opentelemetry/instrumentation': '>=0.46.0 <0.200.0'
- '@opentelemetry/resources': '>=1.19.0 <2.0.0'
- '@opentelemetry/sdk-logs': '>=0.46.0 <0.200.0'
- '@opentelemetry/sdk-metrics': '>=1.19.0 <2.0.0'
- '@opentelemetry/sdk-trace-base': '>=1.19.0 <2.0.0'
-
- '@vercel/postgres@0.10.0':
- resolution: {integrity: sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==}
- engines: {node: '>=18.14'}
- deprecated: '@vercel/postgres is deprecated. If you are setting up a new database, you can choose an alternate storage solution from the Vercel Marketplace. If you had an existing Vercel Postgres database, it should have been migrated to Neon as a native Vercel integration. You can find more details and the guide to migrate to Neon''s SDKs here: https://neon.com/docs/guides/vercel-postgres-transition-guide'
-
- acorn-import-attributes@1.9.5:
- resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
- peerDependencies:
- acorn: ^8
-
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
@@ -2326,9 +1852,6 @@ packages:
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
engines: {node: '>=10'}
- async-retry@1.3.3:
- resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
-
babel-plugin-react-compiler@1.0.0:
resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==}
@@ -2340,32 +1863,6 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
- bcrypt-ts@5.0.3:
- resolution: {integrity: sha512-2FcgD12xPbwCoe5i9/HK0jJ1xA1m+QfC1e6htG9Bl/hNOnLyaFmQSlqLKcfe3QdnoMPKpKEGFCbESBTg+SJNOw==}
- engines: {node: '>=18'}
-
- botid@1.5.11:
- resolution: {integrity: sha512-KOO1A3+/vFVJk5aFoG3sNwiogKFPVR+x4aw4gQ1b2e0XoE+i5xp48/EZn+WqR07jRHeDGwHWQUOtV5WVm7xiww==}
- peerDependencies:
- next: '*'
- react: ^18.0.0 || ^19.0.0
- peerDependenciesMeta:
- next:
- optional: true
- react:
- optional: true
-
- buffer-from@1.1.2:
- resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
-
- bufferutil@4.0.9:
- resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
- engines: {node: '>=6.14.2'}
-
- bytes@3.1.2:
- resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
- engines: {node: '>= 0.8'}
-
caniuse-lite@1.0.30001704:
resolution: {integrity: sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew==}
@@ -2395,9 +1892,6 @@ packages:
citty@0.1.6:
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
- cjs-module-lexer@1.4.3:
- resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==}
-
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
@@ -2411,10 +1905,6 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
- cluster-key-slot@1.1.2:
- resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
- engines: {node: '>=0.10.0'}
-
cmdk@1.1.1:
resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==}
peerDependencies:
@@ -2632,15 +2122,6 @@ packages:
dayjs@1.11.19:
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
- debug@4.4.0:
- resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -2684,99 +2165,6 @@ packages:
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
engines: {node: '>=12'}
- drizzle-kit@0.25.0:
- resolution: {integrity: sha512-Rcf0nYCAKizwjWQCY+d3zytyuTbDb81NcaPor+8NebESlUz1+9W3uGl0+r9FhU4Qal5Zv9j/7neXCSCe7DHzjA==}
- hasBin: true
-
- drizzle-orm@0.34.1:
- resolution: {integrity: sha512-t+zCwyWWt8xTqtYV4doE/xYmT7hpv1L8pQ66zddEz+3VWyedBBtctjMAp22mAJPfyWurRQXUJ1nrTtqLq+DqNA==}
- peerDependencies:
- '@aws-sdk/client-rds-data': '>=3'
- '@cloudflare/workers-types': '>=3'
- '@electric-sql/pglite': '>=0.1.1'
- '@libsql/client': '>=0.10.0'
- '@neondatabase/serverless': '>=0.1'
- '@op-engineering/op-sqlite': '>=2'
- '@opentelemetry/api': ^1.4.1
- '@planetscale/database': '>=1'
- '@prisma/client': '*'
- '@tidbcloud/serverless': '*'
- '@types/better-sqlite3': '*'
- '@types/pg': '*'
- '@types/react': '>=18'
- '@types/sql.js': '*'
- '@vercel/postgres': '>=0.8.0'
- '@xata.io/client': '*'
- better-sqlite3: '>=7'
- bun-types: '*'
- expo-sqlite: '>=13.2.0'
- knex: '*'
- kysely: '*'
- mysql2: '>=2'
- pg: '>=8'
- postgres: '>=3'
- prisma: '*'
- react: '>=18'
- sql.js: '>=1'
- sqlite3: '>=5'
- peerDependenciesMeta:
- '@aws-sdk/client-rds-data':
- optional: true
- '@cloudflare/workers-types':
- optional: true
- '@electric-sql/pglite':
- optional: true
- '@libsql/client':
- optional: true
- '@neondatabase/serverless':
- optional: true
- '@op-engineering/op-sqlite':
- optional: true
- '@opentelemetry/api':
- optional: true
- '@planetscale/database':
- optional: true
- '@prisma/client':
- optional: true
- '@tidbcloud/serverless':
- optional: true
- '@types/better-sqlite3':
- optional: true
- '@types/pg':
- optional: true
- '@types/react':
- optional: true
- '@types/sql.js':
- optional: true
- '@vercel/postgres':
- optional: true
- '@xata.io/client':
- optional: true
- better-sqlite3:
- optional: true
- bun-types:
- optional: true
- expo-sqlite:
- optional: true
- knex:
- optional: true
- kysely:
- optional: true
- mysql2:
- optional: true
- pg:
- optional: true
- postgres:
- optional: true
- prisma:
- optional: true
- react:
- optional: true
- sql.js:
- optional: true
- sqlite3:
- optional: true
-
enhanced-resolve@5.18.3:
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
engines: {node: '>=10.13.0'}
@@ -2789,21 +2177,6 @@ packages:
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
engines: {node: '>=0.12'}
- esbuild-register@3.6.0:
- resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
- peerDependencies:
- esbuild: '>=0.12 <1'
-
- esbuild@0.18.20:
- resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
- engines: {node: '>=12'}
- hasBin: true
-
- esbuild@0.19.12:
- resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
- engines: {node: '>=12'}
- hasBin: true
-
esbuild@0.25.1:
resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==}
engines: {node: '>=18'}
@@ -2867,9 +2240,6 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
- function-bind@1.1.2:
- resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-
get-east-asian-width@1.4.0:
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
engines: {node: '>=18'}
@@ -2891,10 +2261,6 @@ packages:
hachure-fill@0.5.2:
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
- hasown@2.0.2:
- resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
- engines: {node: '>= 0.4'}
-
hast-util-from-dom@5.0.1:
resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==}
@@ -2947,9 +2313,6 @@ packages:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
- import-in-the-middle@1.15.0:
- resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==}
-
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
@@ -2966,14 +2329,6 @@ packages:
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
- is-buffer@2.0.5:
- resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
- engines: {node: '>=4'}
-
- is-core-module@2.16.1:
- resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
- engines: {node: '>= 0.4'}
-
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
@@ -3004,10 +2359,6 @@ packages:
khroma@2.1.0:
resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
- kysely@0.28.11:
- resolution: {integrity: sha512-zpGIFg0HuoC893rIjYX1BETkVWdDnzTzF5e0kWXJFg5lE0k1/LfNWBejrcnOFu8Q2Rfq/hTDTU7XLUM8QOrpzg==}
- engines: {node: '>=20.0.0'}
-
langium@4.2.1:
resolution: {integrity: sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==}
engines: {node: '>=20.10.0', npm: '>=10.2.3'}
@@ -3323,9 +2674,6 @@ packages:
mlly@1.8.0:
resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}
- module-details-from-path@1.0.4:
- resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
-
motion-dom@11.18.1:
resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==}
@@ -3413,10 +2761,6 @@ packages:
sass:
optional: true
- node-gyp-build@4.8.4:
- resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
- hasBin: true
-
nypm@0.6.2:
resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==}
engines: {node: ^14.16.0 || >=16.10.0}
@@ -3425,9 +2769,6 @@ packages:
oauth4webapi@3.8.5:
resolution: {integrity: sha512-A8jmyUckVhRJj5lspguklcl90Ydqk61H3dcU0oLhH3Yv13KpAliKTt5hknpGGPZSSfOwGyraNEFmofDYH+1kSg==}
- obuf@1.1.2:
- resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==}
-
oniguruma-parser@0.12.1:
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
@@ -3452,9 +2793,6 @@ packages:
path-data-parser@0.1.0:
resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
- path-parse@1.0.7:
- resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-
path-scurry@2.0.1:
resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==}
engines: {node: 20 || >=22}
@@ -3462,21 +2800,6 @@ packages:
pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
- pg-int8@1.0.1:
- resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
- engines: {node: '>=4.0.0'}
-
- pg-numeric@1.0.2:
- resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
- engines: {node: '>=4'}
-
- pg-protocol@1.8.0:
- resolution: {integrity: sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==}
-
- pg-types@4.0.2:
- resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
- engines: {node: '>=10'}
-
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -3514,29 +2837,6 @@ packages:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
engines: {node: ^10 || ^12 || >=14}
- postgres-array@3.0.4:
- resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==}
- engines: {node: '>=12'}
-
- postgres-bytea@3.0.0:
- resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
- engines: {node: '>= 6'}
-
- postgres-date@2.1.0:
- resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
- engines: {node: '>=12'}
-
- postgres-interval@3.0.0:
- resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
- engines: {node: '>=12'}
-
- postgres-range@1.1.4:
- resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==}
-
- postgres@3.4.5:
- resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
- engines: {node: '>=12'}
-
preact-render-to-string@5.2.3:
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
peerDependencies:
@@ -3545,6 +2845,11 @@ packages:
preact@10.11.3:
resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
+ prettier@3.8.3:
+ resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
+ engines: {node: '>=14'}
+ hasBin: true
+
pretty-format@3.8.0:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
@@ -3661,10 +2966,6 @@ packages:
resolution: {integrity: sha512-nVRaZCuEyvu69sWrkdwjP6QY57C+lY+uMNNMyWUFJb9Z/JlaBOQus7mSMfGYsblv7R691u6SSJA/dX9IRnyyLQ==}
engines: {node: '>=0.10.0'}
- redis@5.9.0:
- resolution: {integrity: sha512-E8dQVLSyH6UE/C9darFuwq4usOPrqfZ1864kI4RFbr5Oj9ioB9qPF0oJMwX7s8mf6sPYrz84x/Dx1PGF3/0EaQ==}
- engines: {node: '>= 18'}
-
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
@@ -3724,25 +3025,9 @@ packages:
remend@1.2.1:
resolution: {integrity: sha512-4wC12bgXsfKAjF1ewwkNIQz5sqewz/z1xgIgjEMb3r1pEytQ37F0Cm6i+OhbTWEvguJD7lhOUJhK5fSasw9f0w==}
- require-in-the-middle@7.5.2:
- resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==}
- engines: {node: '>=8.6.0'}
-
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
- resolve@1.22.10:
- resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
- engines: {node: '>= 0.4'}
- hasBin: true
-
- resumable-stream@2.2.10:
- resolution: {integrity: sha512-pSJtiDVkPgirq4x+e+gu67IEkUVYGu1cPgW5AnTHCfYGRfIUjS3d4pj7VGveXmYWb9hy6yIMFTM4YzK7rqj14A==}
-
- retry@0.13.1:
- resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
- engines: {node: '>= 4'}
-
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
@@ -3766,9 +3051,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
- server-only@0.0.1:
- resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
-
sharp@0.34.5:
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -3776,9 +3058,6 @@ packages:
shiki@3.21.0:
resolution: {integrity: sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w==}
- shimmer@1.2.1:
- resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
-
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@@ -3792,13 +3071,6 @@ packages:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
- source-map-support@0.5.21:
- resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
-
- source-map@0.6.1:
- resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
- engines: {node: '>=0.10.0'}
-
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
@@ -3836,10 +3108,6 @@ packages:
stylis@4.3.6:
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
- supports-preserve-symlinks-flag@1.0.0:
- resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
- engines: {node: '>= 0.4'}
-
swr@2.3.3:
resolution: {integrity: sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==}
peerDependencies:
@@ -3937,10 +3205,6 @@ packages:
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
- undici@5.28.5:
- resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==}
- engines: {node: '>=14.0'}
-
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
@@ -4048,18 +3312,6 @@ packages:
web-namespaces@2.0.1:
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
- ws@8.18.1:
- resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
- engines: {node: '>=10.0.0'}
- peerDependencies:
- bufferutil: ^4.0.1
- utf-8-validate: '>=5.0.2'
- peerDependenciesMeta:
- bufferutil:
- optional: true
- utf-8-validate:
- optional: true
-
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
@@ -4252,237 +3504,88 @@ snapshots:
dependencies:
'@codemirror/state': 6.5.2
style-mod: 4.1.2
- w3c-keyname: 2.2.8
-
- '@drizzle-team/brocli@0.10.2': {}
-
- '@emnapi/runtime@1.7.1':
- dependencies:
- tslib: 2.8.1
- optional: true
-
- '@esbuild-kit/core-utils@3.3.2':
- dependencies:
- esbuild: 0.18.20
- source-map-support: 0.5.21
+ w3c-keyname: 2.2.8
- '@esbuild-kit/esm-loader@2.6.5':
+ '@emnapi/runtime@1.7.1':
dependencies:
- '@esbuild-kit/core-utils': 3.3.2
- get-tsconfig: 4.10.0
-
- '@esbuild/aix-ppc64@0.19.12':
+ tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.25.1':
optional: true
- '@esbuild/android-arm64@0.18.20':
- optional: true
-
- '@esbuild/android-arm64@0.19.12':
- optional: true
-
'@esbuild/android-arm64@0.25.1':
optional: true
- '@esbuild/android-arm@0.18.20':
- optional: true
-
- '@esbuild/android-arm@0.19.12':
- optional: true
-
'@esbuild/android-arm@0.25.1':
optional: true
- '@esbuild/android-x64@0.18.20':
- optional: true
-
- '@esbuild/android-x64@0.19.12':
- optional: true
-
'@esbuild/android-x64@0.25.1':
optional: true
- '@esbuild/darwin-arm64@0.18.20':
- optional: true
-
- '@esbuild/darwin-arm64@0.19.12':
- optional: true
-
'@esbuild/darwin-arm64@0.25.1':
optional: true
- '@esbuild/darwin-x64@0.18.20':
- optional: true
-
- '@esbuild/darwin-x64@0.19.12':
- optional: true
-
'@esbuild/darwin-x64@0.25.1':
optional: true
- '@esbuild/freebsd-arm64@0.18.20':
- optional: true
-
- '@esbuild/freebsd-arm64@0.19.12':
- optional: true
-
'@esbuild/freebsd-arm64@0.25.1':
optional: true
- '@esbuild/freebsd-x64@0.18.20':
- optional: true
-
- '@esbuild/freebsd-x64@0.19.12':
- optional: true
-
'@esbuild/freebsd-x64@0.25.1':
optional: true
- '@esbuild/linux-arm64@0.18.20':
- optional: true
-
- '@esbuild/linux-arm64@0.19.12':
- optional: true
-
'@esbuild/linux-arm64@0.25.1':
optional: true
- '@esbuild/linux-arm@0.18.20':
- optional: true
-
- '@esbuild/linux-arm@0.19.12':
- optional: true
-
'@esbuild/linux-arm@0.25.1':
optional: true
- '@esbuild/linux-ia32@0.18.20':
- optional: true
-
- '@esbuild/linux-ia32@0.19.12':
- optional: true
-
'@esbuild/linux-ia32@0.25.1':
optional: true
- '@esbuild/linux-loong64@0.18.20':
- optional: true
-
- '@esbuild/linux-loong64@0.19.12':
- optional: true
-
'@esbuild/linux-loong64@0.25.1':
optional: true
- '@esbuild/linux-mips64el@0.18.20':
- optional: true
-
- '@esbuild/linux-mips64el@0.19.12':
- optional: true
-
'@esbuild/linux-mips64el@0.25.1':
optional: true
- '@esbuild/linux-ppc64@0.18.20':
- optional: true
-
- '@esbuild/linux-ppc64@0.19.12':
- optional: true
-
'@esbuild/linux-ppc64@0.25.1':
optional: true
- '@esbuild/linux-riscv64@0.18.20':
- optional: true
-
- '@esbuild/linux-riscv64@0.19.12':
- optional: true
-
'@esbuild/linux-riscv64@0.25.1':
optional: true
- '@esbuild/linux-s390x@0.18.20':
- optional: true
-
- '@esbuild/linux-s390x@0.19.12':
- optional: true
-
'@esbuild/linux-s390x@0.25.1':
optional: true
- '@esbuild/linux-x64@0.18.20':
- optional: true
-
- '@esbuild/linux-x64@0.19.12':
- optional: true
-
'@esbuild/linux-x64@0.25.1':
optional: true
'@esbuild/netbsd-arm64@0.25.1':
optional: true
- '@esbuild/netbsd-x64@0.18.20':
- optional: true
-
- '@esbuild/netbsd-x64@0.19.12':
- optional: true
-
'@esbuild/netbsd-x64@0.25.1':
optional: true
'@esbuild/openbsd-arm64@0.25.1':
optional: true
- '@esbuild/openbsd-x64@0.18.20':
- optional: true
-
- '@esbuild/openbsd-x64@0.19.12':
- optional: true
-
'@esbuild/openbsd-x64@0.25.1':
optional: true
- '@esbuild/sunos-x64@0.18.20':
- optional: true
-
- '@esbuild/sunos-x64@0.19.12':
- optional: true
-
'@esbuild/sunos-x64@0.25.1':
optional: true
- '@esbuild/win32-arm64@0.18.20':
- optional: true
-
- '@esbuild/win32-arm64@0.19.12':
- optional: true
-
'@esbuild/win32-arm64@0.25.1':
optional: true
- '@esbuild/win32-ia32@0.18.20':
- optional: true
-
- '@esbuild/win32-ia32@0.19.12':
- optional: true
-
'@esbuild/win32-ia32@0.25.1':
optional: true
- '@esbuild/win32-x64@0.18.20':
- optional: true
-
- '@esbuild/win32-x64@0.19.12':
- optional: true
-
'@esbuild/win32-x64@0.25.1':
optional: true
- '@fastify/busboy@2.1.1': {}
-
'@floating-ui/core@1.6.9':
dependencies:
'@floating-ui/utils': 0.2.9
@@ -4657,11 +3760,6 @@ snapshots:
dependencies:
langium: 4.2.1
- '@neondatabase/serverless@0.9.5':
- dependencies:
- '@types/pg': 8.11.6
- optional: true
-
'@next/env@16.2.0': {}
'@next/swc-darwin-arm64@16.2.0':
@@ -4688,61 +3786,8 @@ snapshots:
'@next/swc-win32-x64-msvc@16.2.0':
optional: true
- '@opentelemetry/api-logs@0.200.0':
- dependencies:
- '@opentelemetry/api': 1.9.0
-
- '@opentelemetry/api-logs@0.57.2':
- dependencies:
- '@opentelemetry/api': 1.9.0
-
'@opentelemetry/api@1.9.0': {}
- '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/semantic-conventions': 1.28.0
-
- '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/api-logs': 0.57.2
- '@types/shimmer': 1.2.0
- import-in-the-middle: 1.15.0
- require-in-the-middle: 7.5.2
- semver: 7.7.3
- shimmer: 1.2.1
- transitivePeerDependencies:
- - supports-color
-
- '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.28.0
-
- '@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/api-logs': 0.57.2
- '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0)
-
- '@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0)
-
- '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/semantic-conventions': 1.28.0
-
- '@opentelemetry/semantic-conventions@1.28.0': {}
-
'@panva/hkdf@1.2.1': {}
'@playwright/test@1.51.0':
@@ -5496,26 +4541,6 @@ snapshots:
'@radix-ui/rect@1.1.1': {}
- '@redis/bloom@5.9.0(@redis/client@5.9.0)':
- dependencies:
- '@redis/client': 5.9.0
-
- '@redis/client@5.9.0':
- dependencies:
- cluster-key-slot: 1.1.2
-
- '@redis/json@5.9.0(@redis/client@5.9.0)':
- dependencies:
- '@redis/client': 5.9.0
-
- '@redis/search@5.9.0(@redis/client@5.9.0)':
- dependencies:
- '@redis/client': 5.9.0
-
- '@redis/time-series@5.9.0(@redis/client@5.9.0)':
- dependencies:
- '@redis/client': 5.9.0
-
'@shikijs/core@3.21.0':
dependencies:
'@shikijs/types': 3.21.0
@@ -5829,13 +4854,6 @@ snapshots:
'@types/pdf-parse@1.1.4': {}
- '@types/pg@8.11.6':
- dependencies:
- '@types/node': 22.13.10
- pg-protocol: 1.8.0
- pg-types: 4.0.2
- optional: true
-
'@types/prop-types@15.7.14': {}
'@types/react-dom@18.3.5(@types/react@18.3.18)':
@@ -5847,8 +4865,6 @@ snapshots:
'@types/prop-types': 15.7.14
csstype: 3.1.3
- '@types/shimmer@1.2.0': {}
-
'@types/trusted-types@2.0.7':
optional: true
@@ -5858,52 +4874,8 @@ snapshots:
'@ungap/structured-clone@1.3.0': {}
- '@vercel/analytics@1.5.0(next@16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1)':
- optionalDependencies:
- next: 16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)
- react: 19.0.1
-
- '@vercel/blob@0.24.1':
- dependencies:
- async-retry: 1.3.3
- bytes: 3.1.2
- is-buffer: 2.0.5
- undici: 5.28.5
-
- '@vercel/functions@2.2.13':
- dependencies:
- '@vercel/oidc': 2.0.2
-
- '@vercel/oidc@2.0.2':
- dependencies:
- '@types/ms': 2.1.0
- ms: 2.1.3
-
'@vercel/oidc@3.1.0': {}
- '@vercel/otel@1.14.0(@opentelemetry/api-logs@0.200.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))':
- dependencies:
- '@opentelemetry/api': 1.9.0
- '@opentelemetry/api-logs': 0.200.0
- '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0)
- '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0)
- '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
-
- '@vercel/postgres@0.10.0':
- dependencies:
- '@neondatabase/serverless': 0.9.5
- bufferutil: 4.0.9
- ws: 8.18.1(bufferutil@4.0.9)
- transitivePeerDependencies:
- - utf-8-validate
- optional: true
-
- acorn-import-attributes@1.9.5(acorn@8.15.0):
- dependencies:
- acorn: 8.15.0
-
acorn@8.15.0: {}
ai@6.0.116(zod@3.25.76):
@@ -5920,10 +4892,6 @@ snapshots:
dependencies:
tslib: 2.8.1
- async-retry@1.3.3:
- dependencies:
- retry: 0.13.1
-
babel-plugin-react-compiler@1.0.0:
dependencies:
'@babel/types': 7.29.0
@@ -5932,22 +4900,6 @@ snapshots:
baseline-browser-mapping@2.10.8: {}
- bcrypt-ts@5.0.3: {}
-
- botid@1.5.11(next@16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1):
- optionalDependencies:
- next: 16.2.0(@opentelemetry/api@1.9.0)(@playwright/test@1.51.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)
- react: 19.0.1
-
- buffer-from@1.1.2: {}
-
- bufferutil@4.0.9:
- dependencies:
- node-gyp-build: 4.8.4
- optional: true
-
- bytes@3.1.2: {}
-
caniuse-lite@1.0.30001704: {}
ccount@2.0.1: {}
@@ -5978,8 +4930,6 @@ snapshots:
dependencies:
consola: 3.4.2
- cjs-module-lexer@1.4.3: {}
-
class-variance-authority@0.7.1:
dependencies:
clsx: 2.1.1
@@ -5990,8 +4940,6 @@ snapshots:
clsx@2.1.1: {}
- cluster-key-slot@1.1.2: {}
-
cmdk@1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@19.0.1(react@19.0.1))(react@19.0.1):
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.18)(react@19.0.1)
@@ -6232,10 +5180,6 @@ snapshots:
dayjs@1.11.19: {}
- debug@4.4.0:
- dependencies:
- ms: 2.1.3
-
debug@4.4.3:
dependencies:
ms: 2.1.3
@@ -6268,26 +5212,6 @@ snapshots:
dotenv@16.4.7: {}
- drizzle-kit@0.25.0:
- dependencies:
- '@drizzle-team/brocli': 0.10.2
- '@esbuild-kit/esm-loader': 2.6.5
- esbuild: 0.19.12
- esbuild-register: 3.6.0(esbuild@0.19.12)
- transitivePeerDependencies:
- - supports-color
-
- drizzle-orm@0.34.1(@neondatabase/serverless@0.9.5)(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@types/react@18.3.18)(@vercel/postgres@0.10.0)(kysely@0.28.11)(postgres@3.4.5)(react@19.0.1):
- optionalDependencies:
- '@neondatabase/serverless': 0.9.5
- '@opentelemetry/api': 1.9.0
- '@types/pg': 8.11.6
- '@types/react': 18.3.18
- '@vercel/postgres': 0.10.0
- kysely: 0.28.11
- postgres: 3.4.5
- react: 19.0.1
-
enhanced-resolve@5.18.3:
dependencies:
graceful-fs: 4.2.11
@@ -6297,64 +5221,6 @@ snapshots:
entities@6.0.1: {}
- esbuild-register@3.6.0(esbuild@0.19.12):
- dependencies:
- debug: 4.4.0
- esbuild: 0.19.12
- transitivePeerDependencies:
- - supports-color
-
- esbuild@0.18.20:
- optionalDependencies:
- '@esbuild/android-arm': 0.18.20
- '@esbuild/android-arm64': 0.18.20
- '@esbuild/android-x64': 0.18.20
- '@esbuild/darwin-arm64': 0.18.20
- '@esbuild/darwin-x64': 0.18.20
- '@esbuild/freebsd-arm64': 0.18.20
- '@esbuild/freebsd-x64': 0.18.20
- '@esbuild/linux-arm': 0.18.20
- '@esbuild/linux-arm64': 0.18.20
- '@esbuild/linux-ia32': 0.18.20
- '@esbuild/linux-loong64': 0.18.20
- '@esbuild/linux-mips64el': 0.18.20
- '@esbuild/linux-ppc64': 0.18.20
- '@esbuild/linux-riscv64': 0.18.20
- '@esbuild/linux-s390x': 0.18.20
- '@esbuild/linux-x64': 0.18.20
- '@esbuild/netbsd-x64': 0.18.20
- '@esbuild/openbsd-x64': 0.18.20
- '@esbuild/sunos-x64': 0.18.20
- '@esbuild/win32-arm64': 0.18.20
- '@esbuild/win32-ia32': 0.18.20
- '@esbuild/win32-x64': 0.18.20
-
- esbuild@0.19.12:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.19.12
- '@esbuild/android-arm': 0.19.12
- '@esbuild/android-arm64': 0.19.12
- '@esbuild/android-x64': 0.19.12
- '@esbuild/darwin-arm64': 0.19.12
- '@esbuild/darwin-x64': 0.19.12
- '@esbuild/freebsd-arm64': 0.19.12
- '@esbuild/freebsd-x64': 0.19.12
- '@esbuild/linux-arm': 0.19.12
- '@esbuild/linux-arm64': 0.19.12
- '@esbuild/linux-ia32': 0.19.12
- '@esbuild/linux-loong64': 0.19.12
- '@esbuild/linux-mips64el': 0.19.12
- '@esbuild/linux-ppc64': 0.19.12
- '@esbuild/linux-riscv64': 0.19.12
- '@esbuild/linux-s390x': 0.19.12
- '@esbuild/linux-x64': 0.19.12
- '@esbuild/netbsd-x64': 0.19.12
- '@esbuild/openbsd-x64': 0.19.12
- '@esbuild/sunos-x64': 0.19.12
- '@esbuild/win32-arm64': 0.19.12
- '@esbuild/win32-ia32': 0.19.12
- '@esbuild/win32-x64': 0.19.12
-
esbuild@0.25.1:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.1
@@ -6419,8 +5285,6 @@ snapshots:
fsevents@2.3.3:
optional: true
- function-bind@1.1.2: {}
-
get-east-asian-width@1.4.0: {}
get-nonce@1.0.1: {}
@@ -6439,10 +5303,6 @@ snapshots:
hachure-fill@0.5.2: {}
- hasown@2.0.2:
- dependencies:
- function-bind: 1.1.2
-
hast-util-from-dom@5.0.1:
dependencies:
'@types/hast': 3.0.4
@@ -6577,13 +5437,6 @@ snapshots:
dependencies:
safer-buffer: 2.1.2
- import-in-the-middle@1.15.0:
- dependencies:
- acorn: 8.15.0
- acorn-import-attributes: 1.9.5(acorn@8.15.0)
- cjs-module-lexer: 1.4.3
- module-details-from-path: 1.0.4
-
inline-style-parser@0.2.4: {}
internmap@1.0.1: {}
@@ -6597,12 +5450,6 @@ snapshots:
is-alphabetical: 2.0.1
is-decimal: 2.0.1
- is-buffer@2.0.5: {}
-
- is-core-module@2.16.1:
- dependencies:
- hasown: 2.0.2
-
is-decimal@2.0.1: {}
is-hexadecimal@2.0.1: {}
@@ -6623,9 +5470,6 @@ snapshots:
khroma@2.1.0: {}
- kysely@0.28.11:
- optional: true
-
langium@4.2.1:
dependencies:
chevrotain: 11.1.1
@@ -7164,8 +6008,6 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.3
- module-details-from-path@1.0.4: {}
-
motion-dom@11.18.1:
dependencies:
motion-utils: 11.18.1
@@ -7232,9 +6074,6 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
- node-gyp-build@4.8.4:
- optional: true
-
nypm@0.6.2:
dependencies:
citty: 0.1.6
@@ -7245,9 +6084,6 @@ snapshots:
oauth4webapi@3.8.5: {}
- obuf@1.1.2:
- optional: true
-
oniguruma-parser@0.12.1: {}
oniguruma-to-es@4.3.4:
@@ -7278,8 +6114,6 @@ snapshots:
path-data-parser@0.1.0: {}
- path-parse@1.0.7: {}
-
path-scurry@2.0.1:
dependencies:
lru-cache: 11.2.4
@@ -7287,26 +6121,6 @@ snapshots:
pathe@2.0.3: {}
- pg-int8@1.0.1:
- optional: true
-
- pg-numeric@1.0.2:
- optional: true
-
- pg-protocol@1.8.0:
- optional: true
-
- pg-types@4.0.2:
- dependencies:
- pg-int8: 1.0.1
- pg-numeric: 1.0.2
- postgres-array: 3.0.4
- postgres-bytea: 3.0.0
- postgres-date: 2.1.0
- postgres-interval: 3.0.0
- postgres-range: 1.1.4
- optional: true
-
picocolors@1.1.1: {}
pkg-types@1.3.1:
@@ -7353,25 +6167,6 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
- postgres-array@3.0.4:
- optional: true
-
- postgres-bytea@3.0.0:
- dependencies:
- obuf: 1.1.2
- optional: true
-
- postgres-date@2.1.0:
- optional: true
-
- postgres-interval@3.0.0:
- optional: true
-
- postgres-range@1.1.4:
- optional: true
-
- postgres@3.4.5: {}
-
preact-render-to-string@5.2.3(preact@10.11.3):
dependencies:
preact: 10.11.3
@@ -7379,6 +6174,8 @@ snapshots:
preact@10.11.3: {}
+ prettier@3.8.3: {}
+
pretty-format@3.8.0: {}
property-information@6.5.0: {}
@@ -7581,14 +6378,6 @@ snapshots:
react@19.0.1: {}
- redis@5.9.0:
- dependencies:
- '@redis/bloom': 5.9.0(@redis/client@5.9.0)
- '@redis/client': 5.9.0
- '@redis/json': 5.9.0(@redis/client@5.9.0)
- '@redis/search': 5.9.0(@redis/client@5.9.0)
- '@redis/time-series': 5.9.0(@redis/client@5.9.0)
-
regex-recursion@6.0.2:
dependencies:
regex-utilities: 2.3.0
@@ -7689,26 +6478,8 @@ snapshots:
remend@1.2.1: {}
- require-in-the-middle@7.5.2:
- dependencies:
- debug: 4.4.3
- module-details-from-path: 1.0.4
- resolve: 1.22.10
- transitivePeerDependencies:
- - supports-color
-
resolve-pkg-maps@1.0.0: {}
- resolve@1.22.10:
- dependencies:
- is-core-module: 2.16.1
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
-
- resumable-stream@2.2.10: {}
-
- retry@0.13.1: {}
-
robust-predicates@3.0.2: {}
rope-sequence@1.3.4: {}
@@ -7726,9 +6497,8 @@ snapshots:
scheduler@0.25.0: {}
- semver@7.7.3: {}
-
- server-only@0.0.1: {}
+ semver@7.7.3:
+ optional: true
sharp@0.34.5:
dependencies:
@@ -7773,8 +6543,6 @@ snapshots:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
- shimmer@1.2.1: {}
-
sisteransi@1.0.5: {}
sonner@1.7.4(react-dom@19.0.1(react@19.0.1))(react@19.0.1):
@@ -7784,13 +6552,6 @@ snapshots:
source-map-js@1.2.1: {}
- source-map-support@0.5.21:
- dependencies:
- buffer-from: 1.1.2
- source-map: 0.6.1
-
- source-map@0.6.1: {}
-
space-separated-tokens@2.0.2: {}
streamdown@2.3.0(react-dom@19.0.1(react@19.0.1))(react@19.0.1):
@@ -7837,8 +6598,6 @@ snapshots:
stylis@4.3.6: {}
- supports-preserve-symlinks-flag@1.0.0: {}
-
swr@2.3.3(react@19.0.1):
dependencies:
dequal: 2.0.3
@@ -7908,10 +6667,6 @@ snapshots:
undici-types@6.20.0: {}
- undici@5.28.5:
- dependencies:
- '@fastify/busboy': 2.1.1
-
unified@11.0.5:
dependencies:
'@types/unist': 3.0.3
@@ -8027,11 +6782,6 @@ snapshots:
web-namespaces@2.0.1: {}
- ws@8.18.1(bufferutil@4.0.9):
- optionalDependencies:
- bufferutil: 4.0.9
- optional: true
-
zod@3.25.76: {}
zod@4.3.5: {}
diff --git a/proxy.ts b/proxy.ts
index 60b97f47fe..e0e605197b 100644
--- a/proxy.ts
+++ b/proxy.ts
@@ -1,6 +1,7 @@
import { type NextRequest, NextResponse } from "next/server";
import { getToken } from "next-auth/jwt";
-import { guestRegex, isDevelopmentEnvironment } from "./lib/constants";
+import { shouldRequireAuth } from "./lib/auth-mode";
+import { isDevelopmentEnvironment } from "./lib/constants";
export async function proxy(request: NextRequest) {
const { pathname } = request.nextUrl;
@@ -20,18 +21,25 @@ export async function proxy(request: NextRequest) {
});
const base = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
+ const authPages = ["/login", "/register"];
if (!token) {
- const redirectUrl = encodeURIComponent(new URL(request.url).pathname);
+ if (!shouldRequireAuth()) {
+ return NextResponse.next();
+ }
- return NextResponse.redirect(
- new URL(`${base}/api/auth/guest?redirectUrl=${redirectUrl}`, request.url)
- );
- }
+ if (authPages.includes(pathname)) {
+ return NextResponse.next();
+ }
+
+ if (pathname.startsWith("/api/")) {
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
+ }
- const isGuest = guestRegex.test(token?.email ?? "");
+ return NextResponse.redirect(new URL(`${base}/login`, request.url));
+ }
- if (token && !isGuest && ["/login", "/register"].includes(pathname)) {
+ if (authPages.includes(pathname)) {
return NextResponse.redirect(new URL(`${base}/`, request.url));
}