From 0682dd569393ccb0b0f810db7e55f1eb28181303 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Thu, 8 Jan 2026 11:42:40 -0500 Subject: [PATCH 01/69] refactor(biome, tsconfig): streamline import formatting and update configuration files - Reformatted import arrays in biome.jsonc and tsconfig.json for improved readability and consistency. - Consolidated multiple lines into single lines where applicable to enhance clarity. - Added new Sentry configuration documentation in sentry.mdc for better guidance on exception handling and tracing. - Updated VSCode settings to maintain consistent formatting and organization. --- .cursor/rules/sentry.mdc | 130 +++++++++++++++++++++++++++++++++++++++ .vscode/extensions.json | 2 +- .vscode/settings.json | 21 ++----- biome.jsonc | 44 +++---------- tsconfig.json | 32 +++------- 5 files changed, 153 insertions(+), 76 deletions(-) create mode 100644 .cursor/rules/sentry.mdc diff --git a/.cursor/rules/sentry.mdc b/.cursor/rules/sentry.mdc new file mode 100644 index 0000000..39ef703 --- /dev/null +++ b/.cursor/rules/sentry.mdc @@ -0,0 +1,130 @@ +--- +alwaysApply: true +--- + +These examples should be used as guidance when configuring Sentry functionality within a project. + +# Exception Catching + +Use `Sentry.captureException(error)` to capture an exception and log the error in Sentry. +Use this in try catch blocks or areas where exceptions are expected + +# Tracing Examples + +Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls +Use the `Sentry.startSpan` function to create a span +Child spans can exist within a parent span + +## Custom Span instrumentation in component actions + +The `name` and `op` properties should be meaninful for the activities in the call. +Attach attributes based on relevant information and metrics from the request + +```javascript +function TestComponent() { + const handleTestButtonClick = () => { + // Create a transaction/span to measure performance + Sentry.startSpan( + { + op: "ui.click", + name: "Test Button Click", + }, + (span) => { + const value = "some config"; + const metric = "some metric"; + + // Metrics can be added to the span + span.setAttribute("config", value); + span.setAttribute("metric", metric); + + doSomething(); + }, + ); + }; + + return ( + + ); +} +``` + +## Custom span instrumentation in API calls + +The `name` and `op` properties should be meaninful for the activities in the call. +Attach attributes based on relevant information and metrics from the request + +```javascript +async function fetchUserData(userId) { + return Sentry.startSpan( + { + op: "http.client", + name: `GET /api/users/${userId}`, + }, + async () => { + const response = await fetch(`/api/users/${userId}`); + const data = await response.json(); + return data; + }, + ); +} +``` + +# Logs + +Where logs are used, ensure Sentry is imported using `import * as Sentry from "@sentry/nextjs"` +Enable logging in Sentry using `Sentry.init({ enableLogs: true })` +Reference the logger using `const { logger } = Sentry` +Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls + +## Configuration + +In NextJS the client side Sentry initialization is in `instrumentation-client.(js|ts)`, the server initialization is in `sentry.server.config.ts` and the edge initialization is in `sentry.edge.config.ts` +Initialization does not need to be repeated in other files, it only needs to happen the files mentioned above. You should use `import * as Sentry from "@sentry/nextjs"` to reference Sentry functionality + +### Baseline + +```javascript +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: "https://b8a2d1e96d478cdd0cbedd89f23f33e4@o4506955434885120.ingest.us.sentry.io/4510675394822144", + + enableLogs: true, +}); +``` + +### Logger Integration + +```javascript +Sentry.init({ + dsn: "https://b8a2d1e96d478cdd0cbedd89f23f33e4@o4506955434885120.ingest.us.sentry.io/4510675394822144", + integrations: [ + // send console.log, console.warn, and console.error calls as logs to Sentry + Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }), + ], +}); +``` + +## Logger Examples + +`logger.fmt` is a template literal function that should be used to bring variables into the structured logs. + +```javascript +logger.trace("Starting database connection", { database: "users" }); +logger.debug(logger.fmt`Cache miss for user: ${userId}`); +logger.info("Updated profile", { profileId: 345 }); +logger.warn("Rate limit reached for endpoint", { + endpoint: "/api/results/", + isEnterprise: false, +}); +logger.error("Failed to process payment", { + orderId: "order_123", + amount: 99.99, +}); +logger.fatal("Database connection pool exhausted", { + database: "users", + activeConnections: 100, +}); +``` diff --git a/.vscode/extensions.json b/.vscode/extensions.json index ab1193e..f51763c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -7,4 +7,4 @@ "usernamehw.errorlens", "bradlc.vscode-tailwindcss" ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 9db5bda..62c55fe 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,13 +49,8 @@ "source.fixAll.biome": "explicit", "source.organizeImports.biome": "explicit" }, - "i18n-ally.localesPaths": [ - "locale", - "src/i18n", - ], - "i18n-ally.enabledParsers": [ - "json" - ], + "i18n-ally.localesPaths": ["locale", "src/i18n"], + "i18n-ally.enabledParsers": ["json"], "i18n-ally.namespace": true, // Path matcher: locale/en/common.json -> namespace "common" // Matches: {locale}/{namespaces}.json @@ -70,12 +65,6 @@ "i18n-ally.indent": 2, "i18n-ally.tabStyle": "space", "i18n-ally.encoding": "utf-8", - "i18n-ally.enabledFrameworks": [ - "next-intl" - ], - "i18n-ally.usage.scanningIgnore": [ - "node_modules/**", - ".next/**", - "dist/**" - ], -} \ No newline at end of file + "i18n-ally.enabledFrameworks": ["next-intl"], + "i18n-ally.usage.scanningIgnore": ["node_modules/**", ".next/**", "dist/**"] +} diff --git a/biome.jsonc b/biome.jsonc index 274ed05..7f5eb5a 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -34,46 +34,20 @@ "class-variance-authority" ], // Next.js specific - [ - "next", - "next/**", - "@next/**" - ], + ["next", "next/**", "@next/**"], // Form and validation - [ - "react-hook-form", - "@hookform/resolvers", - "zod" - ], + ["react-hook-form", "@hookform/resolvers", "zod"], // Other packages ":PACKAGE:", // Blank line separator ":BLANK_LINE:", // Internal aliases by specificity - [ - "@/components/ui", - "@/components/ui/**" - ], - [ - "@/components", - "@/components/**" - ], - [ - "@/hooks", - "@/hooks/**" - ], - [ - "@/lib", - "@/lib/**" - ], - [ - "@/auth", - "@/auth/**" - ], - [ - "@/db", - "@/db/**" - ], + ["@/components/ui", "@/components/ui/**"], + ["@/components", "@/components/**"], + ["@/hooks", "@/hooks/**"], + ["@/lib", "@/lib/**"], + ["@/auth", "@/auth/**"], + ["@/db", "@/db/**"], // Relative imports last ":PATH:" ], @@ -91,4 +65,4 @@ "!drizzle" ] } -} \ No newline at end of file +} diff --git a/tsconfig.json b/tsconfig.json index 9c5893f..03254ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ESNext", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], + "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -23,24 +19,12 @@ } ], "paths": { - "@/auth/*": [ - "./src/lib/auth/*" - ], - "@/auth": [ - "./src/lib/auth" - ], - "@/db": [ - "./src/lib/db" - ], - "@/config": [ - "./src/lib/config" - ], - "@/ui/*": [ - "./src/components/ui/*" - ], - "@/*": [ - "./src/*" - ] + "@/auth/*": ["./src/lib/auth/*"], + "@/auth": ["./src/lib/auth"], + "@/db": ["./src/lib/db"], + "@/config": ["./src/lib/config"], + "@/ui/*": ["./src/components/ui/*"], + "@/*": ["./src/*"] } }, "include": [ @@ -57,4 +41,4 @@ "src/components/ui/**", "src/lib/db/auth-schema.ts" ] -} \ No newline at end of file +} From 243e3d9f01b690f6ee13a29ce8a91876743d3b3d Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Thu, 8 Jan 2026 11:42:52 -0500 Subject: [PATCH 02/69] feat(sentry): integrate Sentry for error logging and tracing - Updated error handling in Error and GlobalError components to log exceptions to Sentry. - Enhanced metadata generation in layout to include Sentry trace data for improved distributed tracing. - Initialized Sentry on the client-side in providers for comprehensive observability. --- src/app/(dashboard)/app/(overview)/page.tsx | 4 +++- src/app/error.tsx | 5 ++-- src/app/global-error.tsx | 5 ++-- src/app/layout.tsx | 26 ++++++++++++++++++++- src/app/providers.tsx | 5 ++++ 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/app/(dashboard)/app/(overview)/page.tsx b/src/app/(dashboard)/app/(overview)/page.tsx index 874e260..5e49747 100644 --- a/src/app/(dashboard)/app/(overview)/page.tsx +++ b/src/app/(dashboard)/app/(overview)/page.tsx @@ -40,7 +40,9 @@ export default async function AppPage() { description={t("routes.dashboard.ui.description")} pathname="/app" resolver={resolver} - title={t("routes.dashboard.ui.title", { name: user.name || user.email })} + title={t("routes.dashboard.ui.title", { + name: user.name || user.email, + })} /> {/* Dashboard content will be added here */} diff --git a/src/app/error.tsx b/src/app/error.tsx index e52abdc..abb5b2a 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -24,8 +24,9 @@ export default function Error({ const t = useTranslations(); useEffect(() => { - // Log error to error reporting service - console.error("Root error boundary caught:", error); + // Log the error to Sentry + const { captureException } = require("@sentry/nextjs"); + captureException(error); }, [error]); return ( diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx index 87e0096..b2f04d8 100644 --- a/src/app/global-error.tsx +++ b/src/app/global-error.tsx @@ -37,8 +37,9 @@ export default function GlobalError({ const t = useTranslations(); useEffect(() => { - // Log error to error reporting service - console.error("Global error boundary caught:", error); + // Log the error to Sentry + const { captureException } = require("@sentry/nextjs"); + captureException(error); }, [error]); return ( diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 28deba0..49816ba 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -11,7 +11,31 @@ import "@/styles/globals.css"; import { defaultMetadata } from "@/lib/seo"; import { geistMono, geistSans, inter } from "./fonts"; -export const metadata: Metadata = defaultMetadata; +export async function generateMetadata(): Promise { + // Add Sentry trace data for distributed tracing + const { getTraceData } = await import("@sentry/nextjs"); + const traceData = getTraceData(); + + // Filter out undefined values to avoid TypeScript errors + const validTraceData: Record = {}; + if (traceData["sentry-trace"]) { + validTraceData["sentry-trace"] = traceData["sentry-trace"]; + } + if (traceData.baggage) { + validTraceData.baggage = traceData.baggage; + } + if (traceData.traceparent) { + validTraceData.traceparent = traceData.traceparent; + } + + return { + ...defaultMetadata, + other: { + ...defaultMetadata.other, + ...validTraceData, + }, + }; +} export default async function RootLayout({ children, diff --git a/src/app/providers.tsx b/src/app/providers.tsx index bc98f76..3bee813 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,5 +1,10 @@ "use client"; +import { initializeSentry } from "@/lib/observability/client"; + +// Initialize Sentry on client-side +initializeSentry(); + import type { ReactNode } from "react"; import { Toaster } from "sonner"; import Link from "next/link"; From 4940b15b43576a5b8056ecf4d1fded9a060bb8bd Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Thu, 8 Jan 2026 11:43:06 -0500 Subject: [PATCH 03/69] fix(navigation): update Link href type casting for improved type safety - Modified Link component href prop in NavCollapsible, NavItem, and SidebarUserSection to ensure proper type casting using Parameters for enhanced TypeScript type safety. - This change helps prevent potential runtime errors related to incorrect path types. --- src/components/layout/navigation/nav-collapsible.tsx | 2 +- src/components/layout/navigation/nav-item.tsx | 2 +- src/components/layout/sidebar/sidebar-user-section.tsx | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/layout/navigation/nav-collapsible.tsx b/src/components/layout/navigation/nav-collapsible.tsx index fc1694e..a67c9f5 100644 --- a/src/components/layout/navigation/nav-collapsible.tsx +++ b/src/components/layout/navigation/nav-collapsible.tsx @@ -57,7 +57,7 @@ export function NavCollapsible({ route }: NavCollapsibleProps) { {children.map((child) => ( - + [0]["href"]}> {child.label} diff --git a/src/components/layout/navigation/nav-item.tsx b/src/components/layout/navigation/nav-item.tsx index b5a6a4e..3fd4ea9 100644 --- a/src/components/layout/navigation/nav-item.tsx +++ b/src/components/layout/navigation/nav-item.tsx @@ -33,7 +33,7 @@ export function NavItem({ route }: NavItemProps) { return ( - + [0]["href"]}> {route.icon && } {route.label} {route.navigation?.badge && ( diff --git a/src/components/layout/sidebar/sidebar-user-section.tsx b/src/components/layout/sidebar/sidebar-user-section.tsx index afc6bb0..6a3c2a2 100644 --- a/src/components/layout/sidebar/sidebar-user-section.tsx +++ b/src/components/layout/sidebar/sidebar-user-section.tsx @@ -81,7 +81,9 @@ export function SidebarUserSection({ actions }: SidebarUserSectionProps) { ) : ( action.path && ( - + [0]["href"]} + > {action.label} From 364804a82aa28ef45204f8a113381a81b9ee6674 Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Thu, 8 Jan 2026 11:43:11 -0500 Subject: [PATCH 04/69] fix(navigation): enhance type safety in router path handling - Updated the type casting for the router.push method in CommandMenu to use Parameters for improved TypeScript type safety. - This change ensures that the path argument is correctly typed, reducing the risk of runtime errors. --- src/components/command-menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/command-menu.tsx b/src/components/command-menu.tsx index 9aeadc2..3454c01 100644 --- a/src/components/command-menu.tsx +++ b/src/components/command-menu.tsx @@ -53,7 +53,7 @@ export function CommandMenu() { const handleSelect = (path: string) => { setOpen(false); - router.push(path); + router.push(path as Parameters[0]); }; return ( From 9e061715a3e41508b1c24b687737b54875565eaa Mon Sep 17 00:00:00 2001 From: Logan Honeycutt Date: Thu, 8 Jan 2026 11:43:23 -0500 Subject: [PATCH 05/69] feat(seo): add JsonLd component for structured data and enhance metadata merging - Introduced a new JsonLd component to render JSON-LD structured data in a script tag, ensuring proper escaping for HTML. - Updated createPageMetadata function to use lodash.merge for deep merging of metadata, improving the handling of nested properties. --- src/lib/seo/json-ld.tsx | 25 +++++++++++++++++++++++++ src/lib/seo/metadata.ts | 15 +++------------ 2 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 src/lib/seo/json-ld.tsx diff --git a/src/lib/seo/json-ld.tsx b/src/lib/seo/json-ld.tsx new file mode 100644 index 0000000..79b5a67 --- /dev/null +++ b/src/lib/seo/json-ld.tsx @@ -0,0 +1,25 @@ +import type { Thing, WithContext } from "schema-dts"; + +interface JsonLdProps { + readonly code: WithContext; +} + +const escapeJsonForHtml = (json: string): string => + json + .replace(//g, "\\u003e") + .replace(/&/g, "\\u0026") + .replace(/\u2028/g, "\\u2028") + .replace(/\u2029/g, "\\u2029"); + +export function JsonLd({ code }: JsonLdProps) { + return ( +