# Redirects — `@sonordev/site-kit/redirects`

Sonor-managed 301/302/307/308 redirect rules. Used by `createProxy()` or standalone.

## Usage

Automatically handled when using `createProxy()`:

```ts
// proxy.ts
import { createProxy } from '@sonordev/site-kit/proxy'

export default createProxy()  // redirects: true by default

// Inlined: an imported `config.matcher` is a build error. See the proxy README.
export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon\\.ico|.*\\.(?:ico|png|jpg|jpeg|gif|webp|svg|woff2?)$).*)',
  ],
}
```

For standalone use:

```ts
import { handleManagedRedirects } from '@sonordev/site-kit/redirects'

export async function middleware(request: NextRequest) {
  const redirect = await handleManagedRedirects(request, {})
  if (redirect) return redirect
  return NextResponse.next()
}
```

## API

```ts
handleManagedRedirects(request: NextRequest, config: RedirectConfig): Promise<NextResponse | undefined>
fetchRedirectRules(config: RedirectConfig): Promise<RedirectRule[]>
generateNextRedirects(config: RedirectConfig): Promise<Redirect[]>  // For next.config.js
clearRedirectCache(): void  // Dev helper
```

## Config

```ts
interface RedirectConfig {
  domain?: string              // Resolved from Sonor when using apiKey
  apiKey?: string              // Project API key
  site?: string                // Multi-site host, sent as ?site= and cached per host (default: NEXT_PUBLIC_SITE_URL host)
  portalApiUrl?: string        // Default: https://api.sonor.io
  cacheSeconds?: number        // Default: 300 (5 minutes)
}
```

## Behavior

1. Checks in-memory cache first
2. Fetches rules from Sonor API if expired
3. Matches pathname (exact or trailing-slash variant)
4. Preserves query parameters on redirect
5. Tracks redirect hit (fire-and-forget)
6. Skips static assets and API routes

## Types

```ts
interface RedirectRule {
  from_path: string
  to_path: string
  redirect_type: '301' | '302' | '307' | '308'
  is_enabled: boolean
}
```
