From acfe1523df58908a73a532d41e8f392ba9b37bc1 Mon Sep 17 00:00:00 2001 From: kenneth Date: Sat, 8 Nov 2025 17:56:28 +0000 Subject: [PATCH] refactor: wrap all errors in ConvexError - Update error() helper to throw ConvexError instead of plain objects - Add isApplicationConvexError() type guard for client-side error checking - Fix Vite config to include convex/values in optimizeDeps for proper instanceof checks - Update error handling to check ConvexError wrapper and extract data property This ensures all application errors are properly typed and can be identified using instanceof ConvexError on the client side. Co-authored-by: Ona --- apps/drive-web/src/lib/error.ts | 24 ++++++++++++++----- apps/drive-web/vite.config.ts | 4 ++-- packages/convex/shared/error.ts | 42 ++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/apps/drive-web/src/lib/error.ts b/apps/drive-web/src/lib/error.ts index 4e3590b..0cf5564 100644 --- a/apps/drive-web/src/lib/error.ts +++ b/apps/drive-web/src/lib/error.ts @@ -1,7 +1,9 @@ import { - Code as ErrorCode, + type ApplicationErrorData, + ErrorCode, isApplicationError, } from "@fileone/convex/error" +import { ConvexError } from "convex/values" import { toast } from "sonner" const ERROR_MESSAGE = { @@ -9,13 +11,19 @@ const ERROR_MESSAGE = { [ErrorCode.FileExists]: "File already exists", [ErrorCode.Internal]: "Internal application error", [ErrorCode.Conflict]: "Conflict", - [ErrorCode.DirectoryNotFound]: "Directory not found", - [ErrorCode.FileNotFound]: "File not found", [ErrorCode.Unauthenticated]: "Unauthenticated", + [ErrorCode.NotFound]: "Not found", + [ErrorCode.StorageQuotaExceeded]: "Storage is full", } as const +export function isApplicationConvexError( + error: unknown, +): error is ConvexError { + return error instanceof ConvexError && isApplicationError(error.data) +} + export function formatError(error: unknown): string { - if (isApplicationError(error)) { + if (isApplicationConvexError(error)) { return ERROR_MESSAGE[error.data.code] } if (error instanceof Error) { @@ -25,8 +33,12 @@ export function formatError(error: unknown): string { } export function defaultOnError(error: unknown) { - console.log(error) - toast.error(formatError(error)) + if (isApplicationConvexError(error)) { + toast.error(formatError(error)) + } else { + console.error("Catastrophic error:", error) + toast.error("An unexpected error occurred") + } } export function withDefaultOnError(fn: (error: unknown) => void) { diff --git a/apps/drive-web/vite.config.ts b/apps/drive-web/vite.config.ts index b9fcc6c..313dc18 100644 --- a/apps/drive-web/vite.config.ts +++ b/apps/drive-web/vite.config.ts @@ -1,7 +1,7 @@ +import path from "node:path" import tailwindcss from "@tailwindcss/vite" import { TanStackRouterVite } from "@tanstack/router-plugin/vite" import react from "@vitejs/plugin-react" -import path from "path" import { defineConfig } from "vite" export default defineConfig({ @@ -19,7 +19,7 @@ export default defineConfig({ }, }, optimizeDeps: { - include: ["convex/react", "convex-helpers"], + include: ["convex/react", "convex/values", "convex-helpers"], // Workaround for better-auth bug: https://github.com/better-auth/better-auth/issues/4457 // Vite's esbuild incorrectly transpiles better-call dependency causing 'super' keyword errors exclude: ["better-auth", "@convex-dev/better-auth"], diff --git a/packages/convex/shared/error.ts b/packages/convex/shared/error.ts index 5c49098..f33379e 100644 --- a/packages/convex/shared/error.ts +++ b/packages/convex/shared/error.ts @@ -1,36 +1,44 @@ import { ConvexError } from "convex/values" -export enum Code { +export enum ErrorCode { Conflict = "Conflict", DirectoryExists = "DirectoryExists", - DirectoryNotFound = "DirectoryNotFound", FileExists = "FileExists", - FileNotFound = "FileNotFound", Internal = "Internal", Unauthenticated = "Unauthenticated", NotFound = "NotFound", StorageQuotaExceeded = "StorageQuotaExceeded", } -export type ApplicationErrorData = { code: Code; message?: string } -export type ApplicationError = ConvexError +export type ApplicationErrorData = { code: ErrorCode; message?: string } -export function isApplicationError(error: unknown): error is ApplicationError { - return error instanceof ConvexError && "code" in error.data +export function isApplicationError( + error: unknown, +): error is ApplicationErrorData { + return ( + error !== null && + typeof error === "object" && + "code" in error && + "message" in error && + Object.values(ErrorCode).includes( + (error as { code: string }).code as ErrorCode, + ) + ) } -export function create(code: Code, message?: string): ApplicationError { - return new ConvexError({ - code, - message: - code === Code.Internal ? "Internal application error" : message, - }) -} - -export function createJson(code: Code, message?: string): ApplicationErrorData { +export function createErrorData( + code: ErrorCode, + message?: string, +): ApplicationErrorData { return { code, message: - code === Code.Internal ? "Internal application error" : message, + code === ErrorCode.Internal + ? "Internal application error" + : message, } } + +export function error(data: ApplicationErrorData): never { + throw new ConvexError(data) +}