/** * Minimal NextRequest type compatible with next/server. * We define this locally to avoid module resolution issues during build. */ interface NextRequest extends Request { readonly nextUrl: URL; readonly cookies: { get(name: string): { name: string; value: string; } | undefined; getAll(): Array<{ name: string; value: string; }>; has(name: string): boolean; }; readonly geo?: { city?: string; country?: string; region?: string; }; readonly ip?: string; } /** * Context object passed through the handler chain. * Accumulates data from each utility. */ interface HandlerContext { /** The original Next.js request */ request: NextRequest; /** Parsed and validated request body */ body?: unknown; /** User information from authentication */ user?: unknown; /** Custom data added by utilities */ [key: string]: unknown; } /** * A route handler function that receives context and returns a Response. */ type RouteHandler = (context: TContext) => Response | Promise; /** * A utility function that wraps a route handler. * Can modify context, short-circuit with a Response, or pass to the next handler. */ type HandlerUtility = (handler: RouteHandler) => RouteHandler; /** * Options for the compose function. */ interface ComposeOptions { /** Custom error handler for uncaught errors */ onError?: (error: unknown, context: HandlerContext) => Response | Promise; } /** * CORS utility options. */ interface CorsOptions { /** Allowed origins. Use "*" for any origin, or specify allowed origins. */ origin?: string | string[] | ((origin: string) => boolean); /** Allowed HTTP methods */ methods?: string[]; /** Allowed headers */ allowedHeaders?: string[]; /** Exposed headers */ exposedHeaders?: string[]; /** Allow credentials */ credentials?: boolean; /** Max age for preflight cache (seconds) */ maxAge?: number; } /** * Rate limit utility options. */ interface RateLimitOptions { /** Maximum number of requests */ limit: number; /** Time window in seconds */ window: number; /** Function to generate a unique key for the request (default: IP-based) */ keyGenerator?: (context: HandlerContext) => string | Promise; /** Custom handler when rate limit is exceeded */ onRateLimited?: (context: HandlerContext) => Response | Promise; } /** * Authentication utility options. */ interface AuthOptions { /** Function to verify and extract user from request */ verify: (context: HandlerContext) => TUser | null | Promise; /** Custom handler for unauthorized requests */ onUnauthorized?: (context: HandlerContext) => Response | Promise; } /** * Logging utility options. */ interface LoggingOptions { /** Log level */ level?: "debug" | "info" | "warn" | "error"; /** Custom log function */ logger?: (message: string, data: Record) => void; /** Include request body in logs */ includeBody?: boolean; /** Include response body in logs */ includeResponse?: boolean; } /** * Authentication utility for verifying user identity. * * @example * ```ts * import { compose, withAuth } from "next-route-compose"; * * const verifyToken = async (ctx) => { * const token = ctx.request.headers.get("Authorization")?.replace("Bearer ", ""); * if (!token) return null; * return await decodeToken(token); * }; * * export const GET = compose( * withAuth({ verify: verifyToken }) * )(async (ctx) => { * // ctx.user is typed and available * return Response.json({ user: ctx.user }); * }); * ``` */ declare function withAuth(options: AuthOptions): HandlerUtility; /** * Optional authentication utility - doesn't fail if user is not found, * just sets ctx.user to null. * * @example * ```ts * export const GET = compose( * withOptionalAuth({ verify: verifyToken }) * )(async (ctx) => { * if (ctx.user) { * return Response.json({ message: `Hello, ${ctx.user.name}!` }); * } * return Response.json({ message: "Hello, guest!" }); * }); * ``` */ declare function withOptionalAuth(options: Omit, "onUnauthorized">): HandlerUtility; /** * CORS utility for handling Cross-Origin Resource Sharing. * * @example * ```ts * import { compose, withCors } from "next-route-compose"; * * export const GET = compose( * withCors({ origin: "https://example.com" }) * )(handler); * * // Allow any origin * export const POST = compose( * withCors({ origin: "*", credentials: true }) * )(handler); * ``` */ declare function withCors(options?: CorsOptions): HandlerUtility; /** * Logging utility for request/response logging. * * @example * ```ts * import { compose, withLogging } from "next-route-compose"; * * export const GET = compose( * withLogging({ level: "info" }) * )(handler); * * // With custom logger (e.g., Pino, Winston) * export const POST = compose( * withLogging({ * logger: (msg, data) => pino.info(data, msg) * }) * )(handler); * ``` */ declare function withLogging(options?: LoggingOptions): HandlerUtility; /** * Timing utility to measure handler execution time. * * @example * ```ts * export const GET = compose( * withTiming() * )(handler); * // Response will include X-Response-Time header * ``` */ declare function withTiming(): HandlerUtility; /** * Rate limiting utility to prevent abuse. * * @example * ```ts * import { compose, withRateLimit } from "next-route-compose"; * * // 100 requests per minute * export const GET = compose( * withRateLimit({ limit: 100, window: 60 }) * )(handler); * * // Custom key generator (e.g., by user ID) * export const POST = compose( * withRateLimit({ * limit: 10, * window: 60, * keyGenerator: (ctx) => `user:${ctx.user?.id ?? 'anonymous'}` * }) * )(handler); * ``` */ declare function withRateLimit(options: RateLimitOptions): HandlerUtility; /** * Create a custom rate limiter with a different store. * Useful for distributed systems using Redis or other backends. * * @example * ```ts * const redisRateLimit = createRateLimiter({ * get: async (key) => await redis.get(key), * set: async (key, value, ttl) => await redis.setex(key, ttl, value), * increment: async (key) => await redis.incr(key), * }); * ``` */ interface RateLimitStore { get: (key: string) => Promise<{ count: number; resetAt: number; } | null>; set: (key: string, value: { count: number; resetAt: number; }) => Promise; } declare function createRateLimiter(customStore: RateLimitStore): (options: RateLimitOptions) => HandlerUtility; /** * Generic schema interface compatible with Zod and other validation libraries. */ interface Schema { parse: (data: unknown) => T; safeParse: (data: unknown) => { success: true; data: T; } | { success: false; error: unknown; }; } /** * Options for the validation utility. */ interface ValidateOptions { /** Schema to validate against (Zod schema or compatible) */ schema: Schema; /** Source of data to validate */ source?: "body" | "query" | "params"; /** Custom error formatter */ formatError?: (error: unknown) => unknown; } /** * Validation utility using Zod or compatible schema libraries. * * @example * ```ts * import { compose, withValidation } from "next-route-compose"; * import { z } from "zod"; * * const CreateUserSchema = z.object({ * name: z.string().min(1), * email: z.string().email(), * }); * * export const POST = compose( * withValidation({ schema: CreateUserSchema }) * )(async (ctx) => { * // ctx.body is typed as { name: string; email: string } * const { name, email } = ctx.body; * return Response.json({ user: { name, email } }); * }); * ``` */ declare function withValidation(options: ValidateOptions): HandlerUtility; /** * Validate multiple sources at once. * * @example * ```ts * export const POST = compose( * withMultiValidation({ * body: CreateUserSchema, * query: PaginationSchema, * }) * )(handler); * ``` */ declare function withMultiValidation(schemas: { body?: Schema; query?: Schema; params?: Schema; }): HandlerUtility; export { type AuthOptions as A, type ComposeOptions as C, type HandlerContext as H, type LoggingOptions as L, type NextRequest as N, type RouteHandler as R, type Schema as S, type ValidateOptions as V, type HandlerUtility as a, type CorsOptions as b, type RateLimitOptions as c, createRateLimiter as d, withCors as e, withLogging as f, withMultiValidation as g, withOptionalAuth as h, withRateLimit as i, withTiming as j, withValidation as k, type RateLimitStore as l, withAuth as w };