Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .cursor/skills/next-v16/error-handling.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Error Handling

Handle errors in Next.js with **Sentry reporting** (`@repo/sentry/*`) and **structured logging** (`@repo/utils/logger/server` or `@repo/utils/logger/client`).
Handle errors in Next.js with **Sentry reporting** (`@repo/error/*`) and **structured logging** (`@repo/utils/logger/server` or `@repo/utils/logger/client`).

Reference: https://nextjs.org/docs/app/getting-started/error-handling

## Observability (Required)

- Report unexpected errors with `captureError` from `@repo/sentry/nextjs`
- Report unexpected errors with `captureError` from `@repo/error/nextjs`
- Log operational context with `logger` from `@repo/utils/logger/server` or `@repo/utils/logger/client` (never `console.*`)

## Error Boundaries
Expand All @@ -18,7 +18,7 @@ Catches errors in a route segment and its children:
```tsx
'use client'

import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import { logger } from '@repo/utils/logger/client'
import { useEffect } from 'react'

Expand Down Expand Up @@ -58,7 +58,7 @@ Catches errors in the root layout. Same capture pattern as `error.tsx`, but it *
```tsx
'use client'

import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import { logger } from '@repo/utils/logger/client'
import { useEffect } from 'react'

Expand Down Expand Up @@ -97,7 +97,7 @@ Reference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior
'use server'

import { redirect, unstable_rethrow } from 'next/navigation'
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import { logger } from '@repo/utils/logger/server'

export async function action() {
Expand All @@ -119,7 +119,7 @@ Capture, log, then return a safe result. If navigation APIs may be involved, cal

```tsx
import { unstable_rethrow } from 'next/navigation'
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import { logger } from '@repo/utils/logger/server'

async function action() {
Expand Down
2 changes: 1 addition & 1 deletion .cursor/skills/wagmi-v3/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ Comprehensive error handling with user-friendly messages:
```tsx
import { useWriteContract } from 'wagmi'
import { BaseError } from 'viem'
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import type { WriteContractParameters } from 'wagmi/actions'

function useContractWriteWithErrorHandling() {
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Fastify • OpenAPI • Next.js • Expo — one stack, multiple platforms.
- ⚙️ **Preconfigured dev tools** — Biome, Git workflows, hooks, and security checks
- 🛡️ **Security & quality** — Automated checks in CI (e.g. Gitleaks, OSV)
- ⛓️ **Multichain** — EVM, Solana; shared validation and chain-specific tooling
- 📐 **Conventions** — Cursor rules per domain, @repo/sentry, Pino logging, shared TS and style
- 📐 **Conventions** — Cursor rules per domain, @repo/error, Pino logging, shared TS and style
- 🧑‍💻 **TypeScript-first** — End-to-end types from database to frontend

## Technology stack
Expand All @@ -39,7 +39,7 @@ Fastify • OpenAPI • Next.js • Expo — one stack, multiple platforms.
- **[@repo/react](packages/react/README.md)** — React Query hooks for `@repo/core` API functions
- **[@repo/ui](packages/ui/README.md)** — Shared UI component library (Shadcn/ui, Tailwind)
- **[@repo/utils](packages/utils/README.md)** — Shared utilities (async, data, debug, error, logger, web3)
- **[@repo/sentry](packages/sentry/README.md)** — Common `captureError` interface for error reporting
- **[@repo/error](packages/error/README.md)** — Error reporting and utilities (`captureError`, `getErrorMessage`)
- **[@repo/email](packages/email/README.md)** — Email template library built with React Email
- **[@repo/notif](packages/notif/README.md)** — Notification service (email, activity) with type-safe schemas

Expand Down
60 changes: 30 additions & 30 deletions apps/docu/content/docs/architecture/error-handling.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
title: "Error Handling"
description: "Error handling with Sentry integration and app-level error catalogs."
description: "Error handling with error reporting and app-level error catalogs."
---

Complete guide for error handling across the monorepo: **`@repo/sentry`** for reporting to Sentry, **`@repo/utils/error`** for message extraction, and **app-level catalogs** for safe API responses.
Complete guide for error handling across the monorepo: **`@repo/error`** for error reporting and message extraction, and **app-level catalogs** for safe API responses.

## Overview

- **`@repo/sentry`** – `captureError(options)` sends errors to Sentry (void; does not return a catalog). Use platform subpaths: `node`, `nextjs`, `browser`, `react`.
- **`@repo/utils/error`** – `getErrorMessage(error)` and related utilities for type-safe message extraction.
- **Sentry initialization** – Not in `@repo/sentry`. Initialize per your platform’s [Sentry docs](https://docs.sentry.io/platforms/) (Node, Next.js, Browser).
- **`@repo/error`** – `captureError(options)` sends errors to the reporting backend (void; does not return a catalog). Use platform subpaths: `node`, `nextjs`, `browser`, `react`.
- **`@repo/error`** – `getErrorMessage(error)` and related utilities for type-safe message extraction (base export or `@repo/error/nextjs`).
- **Initialization** – Use `initErrorReporting` from `@repo/error/node` or `@repo/error/nextjs`. [GlitchTip](https://glitchtip.com/sdkdocs/javascript/) is recommended; Sentry also works (same DSN format).
- **Error catalogs and safe responses** – Implemented in each app (e.g. Fastify’s `apps/fastify/src/lib/catalogs/` with `getError`, `mapHttpStatusToErrorCode`). Apps build the safe `{ code, message }` response themselves.

**Key principles:**
Expand All @@ -23,10 +23,10 @@ Complete guide for error handling across the monorepo: **`@repo/sentry`** for re

```typescript
// For Next.js apps
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'

// For Node.js/Fastify apps
// import { captureError } from '@repo/sentry/node'
// import { captureError } from '@repo/error/node'

captureError({
error,
Expand All @@ -41,14 +41,14 @@ captureError({
### Extract Error Message

```typescript
import { getErrorMessage } from '@repo/utils/error'
import { getErrorMessage } from '@repo/error/nextjs'

const message = getErrorMessage(error) // Type-safe
```

### Initialize Sentry

Sentry is not initialized by `@repo/sentry`. Follow your platform’s setup:
Sentry is not initialized by `@repo/error`. Follow your platform’s setup:

- **Next.js**: [Sentry Next.js guide](https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/) – `withSentryConfig`, `instrumentation.ts`, etc.
- **Node/Fastify**: [Sentry Node guide](https://docs.sentry.io/platforms/javascript/guides/node/)
Expand Down Expand Up @@ -96,7 +96,7 @@ reply.status(500).send(catalogError)

### App-level error catalogs

Error catalogs and safe responses are implemented **per app**, not in `@repo/sentry`. For example, the Fastify app defines catalogs in `apps/fastify/src/lib/catalogs/` (e.g. `server.ts`, `client.ts`, `common.ts`) and exposes `getError(code)`, `mapHttpStatusToErrorCode(statusCode)`. The error handler calls `captureError` from `@repo/sentry/node` then builds the response using the app’s `getError`.
Error catalogs and safe responses are implemented **per app**, not in `@repo/error`. For example, the Fastify app defines catalogs in `apps/fastify/src/lib/catalogs/` (e.g. `server.ts`, `client.ts`, `common.ts`) and exposes `getError(code)`, `mapHttpStatusToErrorCode(statusCode)`. The error handler calls `captureError` from `@repo/error/node` then builds the response using the app’s `getError`.

**Adding new error codes:** Add them to your app’s catalog (e.g. `apps/fastify/src/lib/catalogs/`), then use `getError(yourCode)` when building responses. Use `UPPER_SNAKE_CASE` (e.g. `NETWORK_ERROR`, `USER_NOT_FOUND`).

Expand All @@ -121,11 +121,11 @@ Error catalogs and safe responses are implemented **per app**, not in `@repo/sen

#### Global Error Handler Plugin

Use `captureError` from `@repo/sentry/node` (void); build the response from the app’s catalog (`getError`, `mapHttpStatusToErrorCode` from `apps/fastify/src/lib/catalogs/`):
Use `captureError` from `@repo/error/node` (void); build the response from the app’s catalog (`getError`, `mapHttpStatusToErrorCode` from `apps/fastify/src/lib/catalogs/`):

```typescript
// apps/fastify/src/plugins/error-handler.ts
import { captureError } from '@repo/sentry/node'
import { captureError } from '@repo/error/node'
import { getError, mapHttpStatusToErrorCode } from '../lib/catalogs/mapper.js'
// ... route extraction, redaction ...

Expand Down Expand Up @@ -158,8 +158,8 @@ fastify.setErrorHandler((error, request, reply) => {
// apps/next/app/layout.tsx
'use client'

import { AppErrorBoundary } from '@repo/sentry/react'
import { captureError } from '@repo/sentry/nextjs'
import { AppErrorBoundary } from '@repo/error/react'
import { captureError } from '@repo/error/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
Expand All @@ -170,13 +170,13 @@ export default function RootLayout({ children }: { children: React.ReactNode })
}
```

**Custom implementation:** Use `captureError` from `@repo/sentry/nextjs` in `onError`; it returns void. Show user-facing message via your fallback (e.g. `getErrorMessage(error)` from `@repo/utils/error`).
**Custom implementation:** Use `captureError` from `@repo/error/nextjs` in `onError`; it returns void. Show user-facing message via your fallback (e.g. `getErrorMessage(error)` from `@repo/error/nextjs`).

#### Event Handlers

```typescript
import { captureError } from '@repo/sentry/nextjs'
import { getErrorMessage } from '@repo/utils/error'
import { captureError } from '@repo/error/nextjs'
import { getErrorMessage } from '@repo/error/nextjs'
import { toast } from 'sonner'

async function handleSubmit() {
Expand All @@ -200,7 +200,7 @@ async function handleSubmit() {
```typescript
// app/error.tsx
'use client'
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'
import { useEffect } from 'react'

export default function ErrorPage({
Expand Down Expand Up @@ -237,8 +237,8 @@ export default function ErrorPage({

```typescript
'use server'
import { captureError } from '@repo/sentry/nextjs'
import { getErrorMessage } from '@repo/utils/error'
import { captureError } from '@repo/error/nextjs'
import { getErrorMessage } from '@repo/error/nextjs'

export async function serverAction() {
try {
Expand All @@ -255,7 +255,7 @@ export async function serverAction() {

```typescript
'use server'
import { captureError } from '@repo/sentry/nextjs'
import { captureError } from '@repo/error/nextjs'

export async function serverAction() {
try {
Expand All @@ -267,16 +267,16 @@ export async function serverAction() {
}
```

**Initialization:** Sentry is not initialized by `@repo/sentry`. Use [Sentry’s Next.js setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/) in `next.config`, `instrumentation.ts`, etc.
**Initialization:** Sentry is not initialized by `@repo/error`. Use [Sentry’s Next.js setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/) in `next.config`, `instrumentation.ts`, etc.

## API Reference

### `captureError(options): void` (`@repo/sentry/*`)
### `captureError(options): void` (`@repo/error/*`)

Sends the error to Sentry asynchronously. Does not return a catalog; each app builds its own response.

```typescript
import { captureError } from '@repo/sentry/nextjs' // or /node, /browser
import { captureError } from '@repo/error/nextjs' // or /node, /browser

captureError({
error: unknown, // Converted to Error if needed; full stack sent to Sentry
Expand All @@ -290,27 +290,27 @@ captureError({
})
```

See [packages/sentry/README.md](https://github.com/blockmatic/basilic/blob/main/packages/sentry/README.md) for full options. **Serverless:** Consider `Sentry.flush()` before function exit if the process terminates quickly.
See [@repo/error](https://github.com/blockmatic/basilic/blob/main/packages/error/README.md) for full options. **Serverless:** Consider `Sentry.flush()` before function exit if the process terminates quickly.

### `getErrorMessage(error)` (`@repo/utils/error`)
### `getErrorMessage(error)` (`@repo/error` or `@repo/error/nextjs`)

```typescript
import { getErrorMessage } from '@repo/utils/error'
import { getErrorMessage } from '@repo/error/nextjs'

const message = getErrorMessage(error) // Type-safe extraction from unknown
```

### App-level: `getError`, `mapHttpStatusToErrorCode`

These live in each app (e.g. Fastify’s `apps/fastify/src/lib/catalogs/mapper.ts`). `mapHttpStatusToErrorCode(statusCode)` maps HTTP status to a catalog code; `getError(code)` returns `{ code, message }` for the response. Not part of `@repo/sentry`.
These live in each app (e.g. Fastify’s `apps/fastify/src/lib/catalogs/mapper.ts`). `mapHttpStatusToErrorCode(statusCode)` maps HTTP status to a catalog code; `getError(code)` returns `{ code, message }` for the response. Not part of `@repo/error`.

## Best Practices

### Common Patterns

```typescript
import { getErrorMessage } from '@repo/utils/error'
import { captureError } from '@repo/sentry/nextjs'
import { getErrorMessage } from '@repo/error/nextjs'
import { captureError } from '@repo/error/nextjs'

// Extract message
const message = getErrorMessage(error)
Expand Down
4 changes: 2 additions & 2 deletions apps/docu/content/docs/architecture/esm-strategy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Packages expose TypeScript source for workspace consumption. Two patterns exist:
}
```

**Pattern B** – `source` condition for bundlers, `node`/`import` for Node (packages like `@repo/utils`, `@repo/sentry`, `@repo/email`, `@repo/notif`):
**Pattern B** – `source` condition for bundlers, `node`/`import` for Node (packages like `@repo/utils`, `@repo/error`, `@repo/email`, `@repo/notif`):

```json
{
Expand Down Expand Up @@ -113,7 +113,7 @@ Next.js apps import TypeScript source directly using **package interpolation**:
```js
// next.config.mjs
export default {
transpilePackages: ['@repo/ui', '@repo/core', '@repo/react', '@repo/sentry', '@repo/utils'],
transpilePackages: ['@repo/ui', '@repo/core', '@repo/react', '@repo/error', '@repo/utils'],
serverExternalPackages: ['import-in-the-middle', 'require-in-the-middle'],
webpack: config => {
// Resolve "source" condition so Next.js uses TypeScript source from workspace packages
Expand Down
6 changes: 3 additions & 3 deletions apps/docu/content/docs/architecture/logging.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,10 @@ try {
}
```

**5. Integration with `@repo/sentry`** - When using `captureError` in Fastify, pass `request.log` to use Fastify's native logger. The catalog and response are built in the app (e.g. from `apps/fastify/src/lib/catalogs/`):
**5. Integration with `@repo/error`** - When using `captureError` in Fastify, pass `request.log` to use Fastify's native logger. The catalog and response are built in the app (e.g. from `apps/fastify/src/lib/catalogs/`):

```typescript
import { captureError } from '@repo/sentry/node'
import { captureError } from '@repo/error/node'

fastify.setErrorHandler((error, request, reply) => {
captureError({
Expand Down Expand Up @@ -251,4 +251,4 @@ fastify.setErrorHandler((error, request, reply) => {
## Related Documentation

- [Installation](/docs/development) - Environment variable configuration
- [Error Handling](/docs/architecture/error-handling) - Error logging with `@repo/sentry`
- [Error Handling](/docs/architecture/error-handling) - Error logging with `@repo/error`
4 changes: 2 additions & 2 deletions apps/docu/content/docs/deployment/vercel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ OLLAMA_BASE_URL=https://ollama.yourdomain.com
RESEND_API_KEY=re_...
PORT=3001
NODE_ENV=production
SENTRY_DSN=https://...
ERROR_REPORTING_DSN=https://...
```

**For Web (`apps/next`):**
```bash
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
NEXT_PUBLIC_SENTRY_DSN=https://...
NEXT_PUBLIC_ERROR_REPORTING_DSN=https://...
```

**For Docs (`apps/docu`):**
Expand Down
4 changes: 2 additions & 2 deletions apps/docu/content/docs/development/dev-tooling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ Tasks are configured in `turbo.json` with:

- **`@repo/core`** - Watches OpenAPI spec and regenerates API client
- **`@repo/react`** - Watches OpenAPI spec and regenerates React hooks, watches TypeScript for rebuilds
- **`@repo/sentry`** - Watches TypeScript for rebuilds
- **`@repo/error`** - Watches TypeScript for rebuilds
- **`@repo/utils`** - Watches TypeScript for rebuilds
- **`@repo/fastify`** - Starts Fastify API server with OpenAPI generation watcher
- **`@repo/next`** - Starts Next.js development server
Expand All @@ -156,7 +156,7 @@ You can run apps directly, but build dependencies first:

```bash
# Build required packages
pnpm build --filter=@repo/core --filter=@repo/react --filter=@repo/sentry --filter=@repo/utils
pnpm build --filter=@repo/core --filter=@repo/react --filter=@repo/error --filter=@repo/utils

# Then run the app
cd apps/next
Expand Down
2 changes: 1 addition & 1 deletion apps/docu/content/docs/development/package-conventions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ For `@repo/utils`, prefer subpath imports even though a root barrel exists:
```ts
import { delay } from '@repo/utils/async'
import { logger } from '@repo/utils/logger/server'
import { getErrorMessage } from '@repo/utils/error'
import { getErrorMessage } from '@repo/error'
```

## Dependency management strategy
Expand Down
20 changes: 11 additions & 9 deletions apps/docu/content/docs/development/packages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Only import from **exported entrypoints** (package root or documented subpaths).
- `@repo/react`
- **Subpath-only packages** (no root export):
- `@repo/ui/*`
- `@repo/sentry/*`
- `@repo/error/*`
- `@repo/email/*`
- `@repo/notif/*`
- **Mixed**:
Expand All @@ -40,7 +40,7 @@ Examples:
| `@repo/react` | React Query hooks + React utilities | `@repo/react` |
| `@repo/utils` | Cross-runtime utilities | `@repo/utils/*` (prefer subpaths) |
| `@repo/ui` | Shared shadcn/ui components | `@repo/ui/components/*`, `@repo/ui/lib/*`, `@repo/ui/radix` |
| `@repo/sentry` | Error reporting interface | `@repo/sentry/node`, `/nextjs`, `/browser`, `/react` |
| `@repo/error` | Error reporting interface | `@repo/error/node`, `/nextjs`, `/browser`, `/react` |
| `@repo/email` | Email templates + renderer | `@repo/email/emails/*`, `@repo/email/render` |
| `@repo/notif` | Notification env/types (no root export) | `@repo/notif/node`, `@repo/notif/types/*` |

Expand Down Expand Up @@ -139,7 +139,6 @@ export function HealthStatus() {
Prefer subpath imports:

- `@repo/utils/async` — async helpers (`delay`, `fetchWithTimeout`, …)
- `@repo/utils/error` — error normalization (`getErrorMessage`, `tryCatch`, …)
- `@repo/utils/web3` — chain metadata + helpers
- `@repo/utils/logger/server`, `@repo/utils/logger/client` — Pino (server) and console (client)
- `@repo/utils/debug` — client-only debug hooks (`useDevtools`, `useNuqsDebug`, `useVconsole`)
Expand Down Expand Up @@ -168,19 +167,22 @@ import { cn } from '@repo/ui/lib/utils'

## Error reporting

### `@repo/sentry` — capture interface
### `@repo/error` — capture interface + error utilities

Subpath-only exports (choose the platform path):

- `@repo/sentry/node`
- `@repo/sentry/nextjs`
- `@repo/sentry/browser`
- `@repo/sentry/react`
- `@repo/error` — error utilities (`getErrorMessage`, `tryCatch`, `toErrorWithMessage`, …)
- `@repo/error/node`
- `@repo/error/nextjs`
- `@repo/error/browser`
- `@repo/error/react`

```ts
import { captureError } from '@repo/sentry/node' // or /nextjs, /browser
import { captureError } from '@repo/error/node' // or /nextjs, /browser
import { getErrorMessage, tryCatch } from '@repo/error' // or @repo/error/nextjs

captureError({ error, label: 'API Call', tags: { app: 'api' } })
const message = getErrorMessage(error)
```

## Email
Expand Down
Loading
Loading