@fresho/router 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +401 -28
- package/dist/index.d.mts +377 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1046 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware/index.d.mts +396 -0
- package/dist/middleware/index.d.mts.map +1 -0
- package/dist/middleware/index.mjs +675 -0
- package/dist/middleware/index.mjs.map +1 -0
- package/dist/middleware-BpFh0XrR.mjs +50 -0
- package/dist/middleware-BpFh0XrR.mjs.map +1 -0
- package/dist/middleware-CqtYOc6D.d.mts +382 -0
- package/dist/middleware-CqtYOc6D.d.mts.map +1 -0
- package/docs/authentication.md +385 -0
- package/docs/middleware.md +441 -0
- package/package.json +63 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["ALGORITHM_MAP: Record<JwtAlgorithm, string>","key: CryptoKey","finalPayload: JwtPayload","status: number","info: LogInfo","windowMs: number","contentType","config: Required<CorsOptions>","headers: Record<string, string>","response","headers"],"sources":["../../src/middleware/auth.ts","../../src/middleware/common.ts","../../src/middleware/cors.ts"],"sourcesContent":["/**\n * @fileoverview Authentication middleware for @fresho/router.\n *\n * Provides Basic HTTP and JWT authentication middleware.\n */\n\nimport type { Middleware, MiddlewareContext } from '../middleware.js';\n\n// =============================================================================\n// Basic Auth\n// =============================================================================\n\n/** Basic authentication middleware configuration. */\nexport interface BasicAuthOptions {\n /** Realm name for the authentication challenge. */\n realm?: string;\n /** Function to validate credentials. Returns claims to merge into context, or null to reject. */\n verify: (\n username: string,\n password: string,\n context: MiddlewareContext,\n ) => Promise<Record<string, unknown> | null> | Record<string, unknown> | null;\n}\n\n/**\n * Creates a Basic HTTP authentication middleware.\n *\n * @example\n * ```typescript\n * const adminRouter = router('/admin', routes,\n * basicAuth({\n * verify: async (username, password) => {\n * if (username === 'admin' && password === 'secret') {\n * return { user: { name: username } };\n * }\n * return null;\n * }\n * })\n * );\n * ```\n */\nexport function basicAuth(options: BasicAuthOptions): Middleware {\n const realm = options.realm || 'Secure Area';\n\n return async (context, next) => {\n const { request } = context;\n\n const authHeader = request.headers.get('Authorization');\n if (!authHeader?.startsWith('Basic ')) {\n return new Response('Unauthorized', {\n status: 401,\n headers: {\n 'WWW-Authenticate': `Basic realm=\"${realm}\", charset=\"UTF-8\"`,\n },\n });\n }\n\n try {\n const credentials = atob(authHeader.slice(6));\n const colonIndex = credentials.indexOf(':');\n if (colonIndex === -1) {\n throw new Error('Invalid credentials format');\n }\n const username = credentials.substring(0, colonIndex);\n const password = credentials.substring(colonIndex + 1);\n\n const claims = await options.verify(username, password, context);\n if (claims === null) {\n return new Response('Invalid credentials', {\n status: 401,\n headers: {\n 'WWW-Authenticate': `Basic realm=\"${realm}\", charset=\"UTF-8\"`,\n },\n });\n }\n\n Object.assign(context, claims);\n return next();\n } catch (_error) {\n return new Response('Invalid credentials', {\n status: 401,\n headers: {\n 'WWW-Authenticate': `Basic realm=\"${realm}\", charset=\"UTF-8\"`,\n },\n });\n }\n };\n}\n\n// =============================================================================\n// JWT Auth\n// =============================================================================\n\n/** JWT payload type. */\nexport interface JwtPayload {\n /** Subject (user ID). */\n sub?: string;\n /** Issued at timestamp. */\n iat?: number;\n /** Expiration timestamp. */\n exp?: number;\n /** Not before timestamp. */\n nbf?: number;\n /** Issuer. */\n iss?: string;\n /** Audience. */\n aud?: string | string[];\n /** Additional claims. */\n [key: string]: unknown;\n}\n\n/** Supported HMAC algorithms for JWT signing/verification. */\nexport type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';\n\n/** Maps JWT algorithm names to Web Crypto hash names. */\nconst ALGORITHM_MAP: Record<JwtAlgorithm, string> = {\n HS256: 'SHA-256',\n HS384: 'SHA-384',\n HS512: 'SHA-512',\n};\n\n/** Options for signing a JWT. */\nexport interface SignJwtOptions {\n /** Signing algorithm (default: 'HS256'). */\n algorithm?: JwtAlgorithm;\n /** Expiration time (e.g., '1h', '7d', '30m', or seconds as number). */\n expiresIn?: string | number;\n /** Not before time (e.g., '5m', or seconds as number). */\n notBefore?: string | number;\n /** Issuer claim. */\n issuer?: string;\n /** Audience claim. */\n audience?: string | string[];\n /** Subject claim. */\n subject?: string;\n /** Custom issued-at timestamp (defaults to now). */\n issuedAt?: number | Date;\n}\n\n/** JWT secret value. */\ntype JwtSecret = string | ArrayBuffer | CryptoKey;\n\n/** Options for verifying a JWT. */\nexport interface VerifyJwtOptions {\n /** Allowed algorithms (default: ['HS256']). */\n algorithms?: JwtAlgorithm[];\n}\n\n/** JWT authentication middleware configuration. */\nexport interface JwtAuthOptions<Ctx = {}> {\n /** Secret key or function to get the secret from context. */\n secret: JwtSecret | ((context: MiddlewareContext<Ctx>) => JwtSecret);\n /**\n * Map JWT payload to context properties. Return null to reject the token.\n * The returned object is merged into the middleware context.\n */\n claims: (payload: JwtPayload) => Record<string, unknown> | null;\n /** Allowed algorithms (default: ['HS256']). */\n algorithms?: JwtAlgorithm[];\n /** Function to extract token from request (default: Authorization header or 'token' cookie). */\n getToken?: (request: Request) => string | null;\n}\n\n/**\n * Verifies a JWT token using Web Crypto API.\n *\n * Uses only Web Crypto API, compatible with Cloudflare Workers, Deno, and browsers.\n *\n * @param token - The JWT token string to verify\n * @param secret - The secret used to sign the token (string, ArrayBuffer, or CryptoKey)\n * @param options - Optional verification options\n * @returns The decoded JWT payload\n * @throws Error if the token is invalid, expired, or not yet valid\n *\n * @example\n * ```typescript\n * import { jwtVerify } from '@fresho/router/middleware';\n *\n * try {\n * const payload = await jwtVerify(token, 'your-secret-key');\n * console.log(payload.sub); // user ID\n * } catch (error) {\n * console.error('Invalid token:', error.message);\n * }\n *\n * // With specific algorithms\n * const payload = await jwtVerify(token, secret, { algorithms: ['HS512'] });\n * ```\n */\nexport async function jwtVerify(\n token: string,\n secret: string | ArrayBuffer | CryptoKey,\n options: VerifyJwtOptions = {},\n): Promise<JwtPayload> {\n const algorithms = options.algorithms || ['HS256'];\n\n const parts = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid JWT format');\n }\n\n const [headerB64, payloadB64, signatureB64] = parts;\n\n // Decode header.\n const header = JSON.parse(atob(headerB64.replace(/-/g, '+').replace(/_/g, '/')));\n if (!algorithms.includes(header.alg)) {\n throw new Error(`Unsupported algorithm: ${header.alg}`);\n }\n\n // Get the hash algorithm for the JWT algorithm.\n const hashAlg = ALGORITHM_MAP[header.alg as JwtAlgorithm];\n if (!hashAlg) {\n throw new Error(`Unknown algorithm: ${header.alg}`);\n }\n\n // Decode payload.\n const payload = JSON.parse(atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/')));\n\n // Verify signature using Web Crypto API.\n const data = `${headerB64}.${payloadB64}`;\n const signature = Uint8Array.from(atob(signatureB64.replace(/-/g, '+').replace(/_/g, '/')), (c) =>\n c.charCodeAt(0),\n );\n\n let key: CryptoKey;\n if (secret instanceof CryptoKey) {\n key = secret;\n } else {\n const keyData = typeof secret === 'string' ? new TextEncoder().encode(secret) : secret;\n key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: hashAlg }, false, [\n 'verify',\n ]);\n }\n\n const valid = await crypto.subtle.verify('HMAC', key, signature, new TextEncoder().encode(data));\n\n if (!valid) {\n throw new Error('Invalid signature');\n }\n\n // Check expiration.\n if (payload.exp && payload.exp < Date.now() / 1000) {\n throw new Error('Token expired');\n }\n\n // Check not before.\n if (payload.nbf && payload.nbf > Date.now() / 1000) {\n throw new Error('Token not yet valid');\n }\n\n return payload as JwtPayload;\n}\n\n/**\n * Parses a duration string (e.g., '1h', '7d', '30m') to seconds.\n */\nfunction parseDuration(duration: string | number): number {\n if (typeof duration === 'number') return duration;\n\n const match = duration.match(/^(\\d+)\\s*(s|m|h|d|w)$/i);\n if (!match) {\n throw new Error(\n `Invalid duration format: ${duration}. Use formats like '1h', '7d', '30m', '60s'.`,\n );\n }\n\n const value = parseInt(match[1], 10);\n const unit = match[2].toLowerCase();\n\n switch (unit) {\n case 's':\n return value;\n case 'm':\n return value * 60;\n case 'h':\n return value * 60 * 60;\n case 'd':\n return value * 60 * 60 * 24;\n case 'w':\n return value * 60 * 60 * 24 * 7;\n default:\n throw new Error(`Unknown duration unit: ${unit}`);\n }\n}\n\n/**\n * Base64url encodes a string or Uint8Array.\n */\nfunction base64urlEncode(data: string | Uint8Array): string {\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;\n const binary = String.fromCharCode(...bytes);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Signs a JWT token using Web Crypto API.\n *\n * Uses only Web Crypto API, compatible with Cloudflare Workers, Deno, and browsers.\n *\n * @param payload - The JWT payload (custom claims)\n * @param secret - The signing secret (string, ArrayBuffer, or CryptoKey)\n * @param options - Optional signing options (algorithm, expiresIn, issuer, etc.)\n * @returns The signed JWT token string\n *\n * @example\n * ```typescript\n * // Basic usage (HS256 by default)\n * const token = await jwtSign(\n * { uid: 'user-123', role: 'admin' },\n * 'your-secret-key',\n * { expiresIn: '1h' }\n * );\n *\n * // With HS512 algorithm\n * const token = await jwtSign(\n * { uid: 'user-123' },\n * process.env.JWT_SECRET,\n * { algorithm: 'HS512', expiresIn: '7d' }\n * );\n * ```\n */\nexport async function jwtSign(\n payload: Record<string, unknown>,\n secret: JwtSecret,\n options: SignJwtOptions = {},\n): Promise<string> {\n const algorithm = options.algorithm || 'HS256';\n const hashAlg = ALGORITHM_MAP[algorithm];\n\n const now = options.issuedAt\n ? options.issuedAt instanceof Date\n ? Math.floor(options.issuedAt.getTime() / 1000)\n : options.issuedAt\n : Math.floor(Date.now() / 1000);\n\n // Build the final payload with registered claims.\n const finalPayload: JwtPayload = {\n ...payload,\n iat: now,\n };\n\n if (options.expiresIn !== undefined) {\n finalPayload.exp = now + parseDuration(options.expiresIn);\n }\n\n if (options.notBefore !== undefined) {\n finalPayload.nbf = now + parseDuration(options.notBefore);\n }\n\n if (options.issuer !== undefined) {\n finalPayload.iss = options.issuer;\n }\n\n if (options.audience !== undefined) {\n finalPayload.aud = options.audience;\n }\n\n if (options.subject !== undefined) {\n finalPayload.sub = options.subject;\n }\n\n // Create header.\n const header = { alg: algorithm, typ: 'JWT' };\n\n // Encode header and payload.\n const headerB64 = base64urlEncode(JSON.stringify(header));\n const payloadB64 = base64urlEncode(JSON.stringify(finalPayload));\n const data = `${headerB64}.${payloadB64}`;\n\n // Import key for signing.\n let key: CryptoKey;\n if (secret instanceof CryptoKey) {\n key = secret;\n } else {\n const keyData = typeof secret === 'string' ? new TextEncoder().encode(secret) : secret;\n key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: hashAlg }, false, [\n 'sign',\n ]);\n }\n\n // Sign the data.\n const signatureBuffer = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(data));\n\n const signatureB64 = base64urlEncode(new Uint8Array(signatureBuffer));\n\n return `${data}.${signatureB64}`;\n}\n\n/**\n * Creates a JWT authentication middleware.\n *\n * @example\n * ```typescript\n * interface AppContext {\n * env: { JWT_SECRET: string };\n * user: { id: string; email: string };\n * }\n *\n * jwtAuth<AppContext>({\n * secret: (ctx) => ctx.env.JWT_SECRET, // ctx.env is typed\n * claims: (payload) => ({\n * user: { id: payload.sub, email: payload.email },\n * }),\n * })\n *\n * // In your route handler:\n * route.ctx<AppContext>()({\n * handler: async (c) => {\n * return { email: c.user.email }; // Fully typed\n * },\n * })\n * ```\n */\nexport function jwtAuth<Ctx = {}>(options: JwtAuthOptions<Ctx>): Middleware<Ctx> {\n const getToken =\n options.getToken ||\n ((req) => {\n const auth = req.headers.get('Authorization');\n if (auth?.startsWith('Bearer ')) {\n return auth.slice(7);\n }\n // Also check for token in cookie\n const cookie = req.headers.get('Cookie');\n if (cookie) {\n const match = cookie.match(/(?:^|;\\s*)token=([^;]*)/);\n if (match) {\n return match[1];\n }\n }\n return null;\n });\n\n return async (context, next) => {\n const { request } = context;\n\n const token = getToken(request);\n if (!token) {\n return new Response('Missing token', {\n status: 401,\n headers: { 'WWW-Authenticate': 'Bearer' },\n });\n }\n\n try {\n const secret =\n typeof options.secret === 'function' ? options.secret(context) : options.secret;\n\n const payload = await jwtVerify(token, secret, { algorithms: options.algorithms });\n\n // Map payload to context properties\n const claims = options.claims(payload);\n if (claims === null) {\n throw new Error('Token validation failed');\n }\n Object.assign(context, claims);\n\n return next();\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Invalid token';\n return new Response(message, {\n status: 401,\n headers: {\n 'WWW-Authenticate': `Bearer error=\"invalid_token\", error_description=\"${message}\"`,\n },\n });\n }\n };\n}\n\n// =============================================================================\n// Bearer Token Auth (Simple)\n// =============================================================================\n\n/** Bearer token authentication options. */\nexport interface BearerAuthOptions {\n /** Function to validate the token. Returns claims to merge into context, or null to reject. */\n verify: (\n token: string,\n context: MiddlewareContext,\n ) => Promise<Record<string, unknown> | null> | Record<string, unknown> | null;\n}\n\n/**\n * Creates a simple bearer token authentication middleware.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes,\n * bearerAuth({\n * verify: async (token) => {\n * if (token === process.env.API_TOKEN) {\n * return { apiClient: 'trusted' };\n * }\n * return null;\n * }\n * })\n * );\n * ```\n */\nexport function bearerAuth(options: BearerAuthOptions): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n const authHeader = request.headers.get('Authorization');\n if (!authHeader?.startsWith('Bearer ')) {\n return new Response('Missing token', {\n status: 401,\n headers: { 'WWW-Authenticate': 'Bearer' },\n });\n }\n\n const token = authHeader.slice(7);\n const claims = await options.verify(token, context);\n\n if (claims === null) {\n return new Response('Invalid token', {\n status: 401,\n headers: { 'WWW-Authenticate': 'Bearer error=\"invalid_token\"' },\n });\n }\n\n Object.assign(context, claims);\n return next();\n };\n}\n","/**\n * @fileoverview Common middleware utilities for @fresho/router.\n *\n * Provides error handling, logging, rate limiting, and other utilities.\n */\n\nimport type { Middleware, MiddlewareContext } from '../middleware.js';\n\n// =============================================================================\n// Error Handler\n// =============================================================================\n\n/**\n * HTTP error with status code for use with errorHandler middleware.\n *\n * Throw this in route handlers to return a specific HTTP status code.\n * The errorHandler middleware will catch it and format the response.\n *\n * @example\n * ```typescript\n * import { HttpError } from '@fresho/router/middleware';\n *\n * const getUser = route({\n * method: 'get',\n * path: '/users/:id',\n * handler: async (c) => {\n * const user = await db.findUser(c.path.id);\n * if (!user) throw new HttpError('User not found', 404);\n * return user;\n * },\n * });\n * ```\n */\nexport class HttpError extends Error {\n constructor(\n message: string,\n public status: number,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\n/**\n * Extracts HTTP status code from an error object.\n *\n * Supports errors with `status` or `statusCode` properties (common patterns\n * in frameworks like Express, Koa, Hono, and custom HTTP error classes).\n */\nfunction getErrorStatus(error: unknown): number {\n if (typeof error !== 'object' || error === null) {\n return 500;\n }\n if ('status' in error && typeof error.status === 'number') {\n return error.status;\n }\n if ('statusCode' in error && typeof error.statusCode === 'number') {\n return error.statusCode;\n }\n return 500;\n}\n\n/** Error handler middleware configuration. */\nexport interface ErrorHandlerOptions {\n /** Custom error logger. */\n log?: (error: Error, context: MiddlewareContext) => void | Promise<void>;\n /** Expose error details in response (dangerous in production). */\n expose?: boolean;\n /** Custom error response formatter. */\n formatter?: (error: Error, context: MiddlewareContext) => Response | Promise<Response>;\n}\n\n/**\n * Creates an error handling middleware that catches and formats errors.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, errorHandler({ expose: false }));\n * ```\n */\nexport function errorHandler(options: ErrorHandlerOptions = {}): Middleware {\n return async (context, next) => {\n try {\n return await next();\n } catch (error) {\n const err = error as Error;\n\n // Log error if logger provided\n if (options.log) {\n await options.log(err, context);\n } else {\n console.error(`Error in ${context.request.method} ${context.request.url}:`, err);\n }\n\n // Use custom formatter if provided\n if (options.formatter) {\n return await options.formatter(err, context);\n }\n\n // Default error response\n const message = options.expose ? err.message : 'Internal Server Error';\n const status = getErrorStatus(error);\n\n const errorBody = {\n error: message,\n ...(options.expose && {\n stack: err.stack,\n name: err.name,\n }),\n };\n\n return new Response(JSON.stringify(errorBody), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n };\n}\n\n// =============================================================================\n// Logger\n// =============================================================================\n\n/** Logger middleware configuration. */\nexport interface LoggerOptions {\n /** Custom logger function. */\n log?: (message: string) => void;\n /** Include request headers in log. */\n includeHeaders?: boolean;\n /** Include request body in log. */\n includeBody?: boolean;\n /** Custom log formatter. */\n formatter?: (info: LogInfo) => string;\n}\n\n/** Information logged for each request. */\nexport interface LogInfo {\n method: string;\n url: string;\n status: number;\n duration: number;\n headers?: Record<string, string>;\n body?: unknown;\n error?: Error;\n}\n\n/**\n * Creates a request/response logging middleware.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, logger({ includeHeaders: true }));\n * ```\n */\nexport function logger(options: LoggerOptions = {}): Middleware {\n const log = options.log || console.log;\n\n return async (context, next) => {\n const { request } = context;\n const start = Date.now();\n const url = new URL(request.url);\n\n // Collect request info\n const info: LogInfo = {\n method: request.method,\n url: `${url.pathname}${url.search}`,\n status: 0,\n duration: 0,\n };\n\n if (options.includeHeaders) {\n info.headers = Object.fromEntries(request.headers.entries());\n }\n\n if (options.includeBody && request.body) {\n try {\n const clonedRequest = request.clone();\n const text = await clonedRequest.text();\n try {\n info.body = JSON.parse(text);\n } catch {\n info.body = text;\n }\n } catch {\n info.body = '[Body not readable]';\n }\n }\n\n // Log request\n const requestMessage = options.formatter\n ? options.formatter({ ...info, status: 0, duration: 0 })\n : `→ ${info.method} ${info.url}`;\n log(requestMessage);\n\n try {\n // Process request\n const response = await next();\n\n // Log response\n info.status = response.status;\n info.duration = Date.now() - start;\n\n const responseMessage = options.formatter\n ? options.formatter(info)\n : `← ${info.status} ${response.statusText || 'OK'} (${info.duration}ms)`;\n log(responseMessage);\n\n return response;\n } catch (error) {\n // Log error\n info.status = 500;\n info.duration = Date.now() - start;\n info.error = error as Error;\n\n const errorMessage = options.formatter\n ? options.formatter(info)\n : `✗ ${info.status} Error: ${(error as Error).message} (${info.duration}ms)`;\n log(errorMessage);\n\n throw error;\n }\n };\n}\n\n// =============================================================================\n// Rate Limiter\n// =============================================================================\n\n/** Rate limit store interface. */\nexport interface RateLimitStore {\n increment(key: string): Promise<number>;\n decrement(key: string): Promise<void>;\n reset(key: string): Promise<void>;\n}\n\n/** In-memory rate limit store with periodic cleanup of expired entries. */\nexport class MemoryRateLimitStore implements RateLimitStore {\n private counts = new Map<string, { count: number; resetAt: number }>();\n private lastCleanup = Date.now();\n\n /** Interval between cleanups (default: 60 seconds). */\n private readonly cleanupIntervalMs: number;\n\n constructor(\n private windowMs: number,\n options?: { cleanupIntervalMs?: number },\n ) {\n // Default cleanup interval is the window duration or 60 seconds, whichever is larger.\n this.cleanupIntervalMs = options?.cleanupIntervalMs ?? Math.max(windowMs, 60_000);\n }\n\n async increment(key: string): Promise<number> {\n const now = Date.now();\n\n // Periodic cleanup to prevent memory leaks.\n if (now - this.lastCleanup > this.cleanupIntervalMs) {\n this.cleanup(now);\n }\n\n const record = this.counts.get(key);\n\n if (!record || now > record.resetAt) {\n this.counts.set(key, { count: 1, resetAt: now + this.windowMs });\n return 1;\n }\n\n record.count++;\n return record.count;\n }\n\n async decrement(key: string): Promise<void> {\n const record = this.counts.get(key);\n if (record && record.count > 0) {\n record.count--;\n }\n }\n\n async reset(key: string): Promise<void> {\n this.counts.delete(key);\n }\n\n /** Removes all expired entries from the store. */\n private cleanup(now: number): void {\n for (const [key, record] of this.counts) {\n if (now > record.resetAt) {\n this.counts.delete(key);\n }\n }\n this.lastCleanup = now;\n }\n\n /** Returns the current number of tracked keys (useful for testing). */\n get size(): number {\n return this.counts.size;\n }\n}\n\n/** Rate limiter middleware configuration. */\nexport interface RateLimitOptions {\n /** Time window in milliseconds. */\n windowMs?: number;\n /** Maximum requests per window. */\n max?: number;\n /** Function to generate a unique key for each client. */\n keyGenerator?: (context: MiddlewareContext) => string;\n /** Store for rate limit data. */\n store?: RateLimitStore;\n /** Skip successful requests from rate limit. */\n skipSuccessfulRequests?: boolean;\n /** Skip failed requests from rate limit. */\n skipFailedRequests?: boolean;\n /** Custom handler for rate limited requests. */\n handler?: (context: MiddlewareContext) => Response | Promise<Response>;\n}\n\n/**\n * Creates a rate limiting middleware.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, rateLimit({ max: 100, windowMs: 60000 }));\n * ```\n */\nexport function rateLimit(options: RateLimitOptions = {}): Middleware {\n const config = {\n windowMs: 60 * 1000, // 1 minute\n max: 100,\n keyGenerator: (ctx: MiddlewareContext) => {\n // Use IP address as default key\n const cfIp = ctx.request.headers.get('CF-Connecting-IP');\n const xForwardedFor = ctx.request.headers.get('X-Forwarded-For');\n const xRealIp = ctx.request.headers.get('X-Real-IP');\n return cfIp || xForwardedFor?.split(',')[0] || xRealIp || 'unknown';\n },\n ...options,\n };\n\n const store = config.store || new MemoryRateLimitStore(config.windowMs);\n\n return async (context, next) => {\n const key = config.keyGenerator(context);\n const current = await store.increment(key);\n\n if (current > config.max) {\n if (config.handler) {\n return await config.handler(context);\n }\n\n return new Response('Too many requests', {\n status: 429,\n headers: {\n 'Retry-After': String(Math.ceil(config.windowMs / 1000)),\n 'X-RateLimit-Limit': String(config.max),\n 'X-RateLimit-Remaining': '0',\n 'X-RateLimit-Reset': new Date(Date.now() + config.windowMs).toISOString(),\n },\n });\n }\n\n const response = await next();\n\n // Optionally don't count successful/failed requests\n if (\n (config.skipSuccessfulRequests && response.status < 400) ||\n (config.skipFailedRequests && response.status >= 400)\n ) {\n await store.decrement(key);\n }\n\n // Add rate limit headers\n const headers = new Headers(response.headers);\n headers.set('X-RateLimit-Limit', String(config.max));\n headers.set('X-RateLimit-Remaining', String(Math.max(0, config.max - current)));\n headers.set('X-RateLimit-Reset', new Date(Date.now() + config.windowMs).toISOString());\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n\n// =============================================================================\n// Request ID\n// =============================================================================\n\n/** Request ID middleware configuration. */\nexport interface RequestIdOptions {\n /** Header name for request ID. */\n headerName?: string;\n /** Function to generate request ID. */\n generator?: () => string;\n}\n\n/**\n * Creates a middleware that adds a unique request ID to each request.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, requestId({ headerName: 'X-Request-ID' }));\n * ```\n */\nexport function requestId(options: RequestIdOptions = {}): Middleware {\n const headerName = options.headerName || 'X-Request-ID';\n const generator = options.generator || (() => crypto.randomUUID());\n\n return async (context, next) => {\n // Check if request already has an ID\n let id = context.request.headers.get(headerName);\n if (!id) {\n id = generator();\n }\n\n // Add to context\n context.requestId = id;\n\n // Process request\n const response = await next();\n\n // Add request ID to response headers\n const headers = new Headers(response.headers);\n headers.set(headerName, id);\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n\n// =============================================================================\n// Timeout\n// =============================================================================\n\n/** Timeout middleware configuration. */\nexport interface TimeoutOptions {\n /** Timeout in milliseconds. */\n timeout: number;\n /** Custom timeout response. */\n message?: string;\n}\n\n/**\n * Creates a middleware that times out requests after a specified duration.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, timeout({ timeout: 5000 }));\n * ```\n */\nexport function timeout(options: TimeoutOptions): Middleware {\n return async (context, next) => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), options.timeout);\n\n try {\n const responsePromise = next();\n const timeoutPromise = new Promise<Response>((_, reject) => {\n controller.signal.addEventListener('abort', () => {\n reject(new Error('Request timeout'));\n });\n });\n\n const response = await Promise.race([responsePromise, timeoutPromise]);\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if ((error as Error).message === 'Request timeout') {\n return new Response(options.message || 'Request timeout', {\n status: 408,\n });\n }\n throw error;\n }\n };\n}\n\n// =============================================================================\n// Content Type Validation\n// =============================================================================\n\n/** Content type validation options. */\nexport interface ContentTypeOptions {\n /** Expected content types. */\n types: string[];\n /** Skip validation for these methods. */\n skipMethods?: string[];\n /** Custom error message. */\n message?: string;\n}\n\n/**\n * Creates a middleware that validates request content type.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, contentType({ types: ['application/json'] }));\n * ```\n */\nexport function contentType(options: ContentTypeOptions): Middleware {\n const skipMethods = new Set(\n options.skipMethods?.map((m) => m.toUpperCase()) || ['GET', 'HEAD', 'OPTIONS'],\n );\n\n return async (context, next) => {\n if (skipMethods.has(context.request.method.toUpperCase())) {\n return next();\n }\n\n const contentType = context.request.headers.get('Content-Type');\n if (!contentType) {\n return new Response(options.message || 'Missing Content-Type header', {\n status: 415,\n });\n }\n\n const matches = options.types.some((type) => contentType.includes(type));\n if (!matches) {\n return new Response(\n options.message || `Unsupported Media Type. Expected: ${options.types.join(', ')}`,\n { status: 415 },\n );\n }\n\n return next();\n };\n}\n","/**\n * @fileoverview CORS middleware for @fresho/router.\n *\n * Provides Cross-Origin Resource Sharing support with configurable options.\n */\n\nimport type { Middleware } from '../middleware.js';\n\n/** CORS middleware configuration options. */\nexport interface CorsOptions {\n /** Allowed origins. Can be string, array, regex, or function. */\n origin?: string | string[] | RegExp | ((origin: string) => boolean);\n /** Allowed HTTP methods. */\n methods?: string[];\n /** Allowed request headers. */\n allowedHeaders?: string[];\n /** Headers to expose to the browser. */\n exposedHeaders?: string[];\n /** Allow credentials (cookies, authorization headers). */\n credentials?: boolean;\n /** Max age for preflight cache (seconds). */\n maxAge?: number;\n /** Pass the CORS preflight response to the next handler. */\n preflightContinue?: boolean;\n}\n\n/**\n * Determines if an origin is allowed based on the configuration.\n */\nfunction getAllowedOrigin(\n requestOrigin: string | null,\n configOrigin: CorsOptions['origin'],\n): string {\n if (!requestOrigin) {\n return '*';\n }\n\n if (!configOrigin || configOrigin === '*') {\n return '*';\n }\n\n if (typeof configOrigin === 'string') {\n return configOrigin === requestOrigin ? configOrigin : 'false';\n }\n\n if (Array.isArray(configOrigin)) {\n return configOrigin.includes(requestOrigin) ? requestOrigin : 'false';\n }\n\n if (configOrigin instanceof RegExp) {\n return configOrigin.test(requestOrigin) ? requestOrigin : 'false';\n }\n\n if (typeof configOrigin === 'function') {\n return configOrigin(requestOrigin) ? requestOrigin : 'false';\n }\n\n return 'false';\n}\n\n/**\n * Creates a CORS middleware with the specified options.\n *\n * @example\n * ```typescript\n * const apiRouter = router('/api', routes, cors({ origin: 'https://example.com', credentials: true }));\n * ```\n */\nexport function cors(options: CorsOptions = {}): Middleware {\n const config: Required<CorsOptions> = {\n origin: '*',\n methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'],\n allowedHeaders: ['Content-Type', 'Authorization'],\n exposedHeaders: [],\n credentials: false,\n maxAge: 86400,\n preflightContinue: false,\n ...options,\n };\n\n return async (context, next) => {\n const { request } = context;\n const requestOrigin = request.headers.get('Origin');\n const allowedOrigin = getAllowedOrigin(requestOrigin, config.origin);\n\n // Handle preflight request\n if (request.method === 'OPTIONS') {\n const headers: Record<string, string> = {\n 'Access-Control-Allow-Origin': allowedOrigin,\n 'Access-Control-Allow-Methods': config.methods.join(', '),\n Vary: 'Origin',\n };\n\n if (config.allowedHeaders.length > 0) {\n const requestHeaders = request.headers.get('Access-Control-Request-Headers');\n if (requestHeaders) {\n // Allow requested headers if they're in the allowed list\n const requested = requestHeaders.split(',').map((h) => h.trim().toLowerCase());\n const allowed = config.allowedHeaders.map((h) => h.toLowerCase());\n const toAllow = requested.filter((h) => allowed.includes(h));\n if (toAllow.length > 0) {\n headers['Access-Control-Allow-Headers'] = toAllow.join(', ');\n }\n } else {\n headers['Access-Control-Allow-Headers'] = config.allowedHeaders.join(', ');\n }\n }\n\n if (config.exposedHeaders.length > 0) {\n headers['Access-Control-Expose-Headers'] = config.exposedHeaders.join(', ');\n }\n\n if (config.credentials) {\n headers['Access-Control-Allow-Credentials'] = 'true';\n }\n\n if (config.maxAge) {\n headers['Access-Control-Max-Age'] = String(config.maxAge);\n }\n\n const response = new Response(null, {\n status: 204,\n headers,\n });\n\n if (config.preflightContinue) {\n // Continue to next middleware/handler even for preflight\n const nextResponse = await next();\n // Merge headers from next response\n const mergedHeaders = new Headers(nextResponse.headers);\n Object.entries(headers).forEach(([key, value]) => {\n mergedHeaders.set(key, value);\n });\n return new Response(nextResponse.body, {\n status: nextResponse.status,\n statusText: nextResponse.statusText,\n headers: mergedHeaders,\n });\n }\n\n return response;\n }\n\n // Handle actual request\n const response = await next();\n\n // Add CORS headers to response\n const headers = new Headers(response.headers);\n headers.set('Access-Control-Allow-Origin', allowedOrigin);\n headers.set('Vary', 'Origin');\n\n if (config.credentials && allowedOrigin !== '*') {\n headers.set('Access-Control-Allow-Credentials', 'true');\n }\n\n if (config.exposedHeaders.length > 0) {\n headers.set('Access-Control-Expose-Headers', config.exposedHeaders.join(', '));\n }\n\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers,\n });\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/middleware.ts
|
|
2
|
+
/**
|
|
3
|
+
* Runs a chain of middleware with a final handler.
|
|
4
|
+
*
|
|
5
|
+
* Middleware are executed in order. Each middleware receives the context and
|
|
6
|
+
* a `next` function. Calling `next()` continues to the next middleware or the
|
|
7
|
+
* final handler. Returning without calling `next()` short-circuits the chain.
|
|
8
|
+
*
|
|
9
|
+
* @param middleware - Array of middleware functions to run
|
|
10
|
+
* @param context - The request context
|
|
11
|
+
* @param finalHandler - Handler to call after all middleware complete
|
|
12
|
+
* @returns The response from the middleware chain or final handler
|
|
13
|
+
*/
|
|
14
|
+
async function runMiddleware(middleware, context, finalHandler) {
|
|
15
|
+
let index = 0;
|
|
16
|
+
const next = async () => {
|
|
17
|
+
if (index >= middleware.length) return finalHandler();
|
|
18
|
+
const currentMiddleware = middleware[index++];
|
|
19
|
+
return await currentMiddleware(context, next);
|
|
20
|
+
};
|
|
21
|
+
return next();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Composes multiple middleware into a single middleware.
|
|
25
|
+
*
|
|
26
|
+
* Useful for grouping related middleware together.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const security = compose(
|
|
31
|
+
* cors(),
|
|
32
|
+
* rateLimit({ max: 100 }),
|
|
33
|
+
* errorHandler(),
|
|
34
|
+
* );
|
|
35
|
+
*
|
|
36
|
+
* const api = router('/api', routes, security);
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param middleware - Middleware functions to compose
|
|
40
|
+
* @returns A single middleware that runs all provided middleware in order
|
|
41
|
+
*/
|
|
42
|
+
function compose(...middleware) {
|
|
43
|
+
return async (context, next) => {
|
|
44
|
+
return runMiddleware(middleware, context, next);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { runMiddleware as n, compose as t };
|
|
50
|
+
//# sourceMappingURL=middleware-BpFh0XrR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-BpFh0XrR.mjs","names":["next: MiddlewareNext"],"sources":["../src/middleware.ts"],"sourcesContent":["/**\n * @fileoverview Core middleware types and utilities.\n *\n * Provides middleware support for @fresho/router with a simple, composable API.\n *\n * @example Type-hinting context properties\n * ```typescript\n * // Define your context type (env is required, other properties are optional in middleware)\n * interface AppContext {\n * env: { JWT_SECRET: string; DB: D1Database };\n * user: { id: string; name: string };\n * }\n *\n * // Middleware receives env as required, other properties as optional\n * const authMiddleware: Middleware<AppContext> = async (context, next) => {\n * context.env.JWT_SECRET; // typed, required\n * context.user; // typed as optional (may not exist yet)\n * context.user = await validateToken(context.request);\n * return next();\n * };\n * ```\n */\n\nimport type { ExecutionContext } from './types.js';\n\n/**\n * Base context properties always available to middleware.\n */\ninterface BaseMiddlewareContext {\n request: Request;\n path: Record<string, string>;\n query: unknown;\n body: unknown;\n executionCtx?: ExecutionContext;\n}\n\n/**\n * Context passed to middleware functions.\n *\n * The `env` property is always required (extracted from Ctx if provided).\n * Other properties from Ctx are optional since middleware builds them up.\n *\n * @typeParam Ctx - Context type including env and middleware-added properties\n *\n * @example\n * ```typescript\n * interface AppContext {\n * env: { JWT_SECRET: string };\n * user: { id: string };\n * }\n *\n * const middleware: Middleware<AppContext> = async (context, next) => {\n * context.env.JWT_SECRET; // typed, required\n * context.user; // typed as { id: string } | undefined\n * context.user = { id: '123' };\n * return next();\n * };\n * ```\n */\nexport type MiddlewareContext<Ctx = {}> = BaseMiddlewareContext & {\n env: Ctx extends { env: infer E } ? E : unknown;\n} & Partial<Omit<Ctx, 'env'>> & {\n [key: string]: unknown;\n };\n\n/**\n * Function to continue to the next middleware or handler.\n */\nexport type MiddlewareNext = () => Promise<Response>;\n\n/**\n * Middleware function signature.\n *\n * @typeParam Ctx - Context type (default: {})\n *\n * @example\n * ```typescript\n * // Simple middleware without typed context\n * const logger: Middleware = async (context, next) => {\n * console.log(context.request.url);\n * return next();\n * };\n *\n * // Middleware with typed context\n * interface AppContext {\n * env: { JWT_SECRET: string };\n * user: { id: string };\n * }\n * const auth: Middleware<AppContext> = async (context, next) => {\n * const secret = context.env.JWT_SECRET; // typed, required\n * context.user = { id: '123' }; // adds to context\n * return next();\n * };\n * ```\n */\nexport type Middleware<Ctx = {}> = (\n context: MiddlewareContext<Ctx>,\n next: MiddlewareNext,\n) => Promise<Response> | Response;\n\n/**\n * Runs a chain of middleware with a final handler.\n *\n * Middleware are executed in order. Each middleware receives the context and\n * a `next` function. Calling `next()` continues to the next middleware or the\n * final handler. Returning without calling `next()` short-circuits the chain.\n *\n * @param middleware - Array of middleware functions to run\n * @param context - The request context\n * @param finalHandler - Handler to call after all middleware complete\n * @returns The response from the middleware chain or final handler\n */\nexport async function runMiddleware(\n middleware: Middleware[],\n context: MiddlewareContext,\n finalHandler: () => Promise<Response>,\n): Promise<Response> {\n let index = 0;\n\n const next: MiddlewareNext = async () => {\n if (index >= middleware.length) {\n return finalHandler();\n }\n\n const currentMiddleware = middleware[index++];\n return await currentMiddleware(context, next);\n };\n\n return next();\n}\n\n/**\n * Composes multiple middleware into a single middleware.\n *\n * Useful for grouping related middleware together.\n *\n * @example\n * ```typescript\n * const security = compose(\n * cors(),\n * rateLimit({ max: 100 }),\n * errorHandler(),\n * );\n *\n * const api = router('/api', routes, security);\n * ```\n *\n * @param middleware - Middleware functions to compose\n * @returns A single middleware that runs all provided middleware in order\n */\nexport function compose(...middleware: Middleware[]): Middleware {\n return async (context, next) => {\n return runMiddleware(middleware, context, next);\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAgHA,eAAsB,cACpB,YACA,SACA,cACmB;CACnB,IAAI,QAAQ;CAEZ,MAAMA,OAAuB,YAAY;AACvC,MAAI,SAAS,WAAW,OACtB,QAAO,cAAc;EAGvB,MAAM,oBAAoB,WAAW;AACrC,SAAO,MAAM,kBAAkB,SAAS,KAAK;;AAG/C,QAAO,MAAM;;;;;;;;;;;;;;;;;;;;;AAsBf,SAAgB,QAAQ,GAAG,YAAsC;AAC/D,QAAO,OAAO,SAAS,SAAS;AAC9B,SAAO,cAAc,YAAY,SAAS,KAAK"}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
//#region src/schema.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Schema types and validation utilities.
|
|
4
|
+
*
|
|
5
|
+
* Provides shorthand schema definitions for validating query parameters and request bodies.
|
|
6
|
+
* No external dependencies - uses simple string-based type definitions.
|
|
7
|
+
*
|
|
8
|
+
* Supported types:
|
|
9
|
+
* - Primitives: `'string'`, `'number'`, `'boolean'`
|
|
10
|
+
* - Optional: `'string?'`, `'number?'`, `'boolean?'`
|
|
11
|
+
* - Arrays: `'string[]'`, `'number[]'`, `'boolean[]'`
|
|
12
|
+
* - Optional arrays: `'string[]?'`, `'number[]?'`, `'boolean[]?'`
|
|
13
|
+
* - Nested objects: `{ field: { nested: 'string' } }`
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // In route definitions
|
|
18
|
+
* const api = router({
|
|
19
|
+
* users: router({
|
|
20
|
+
* post: route({
|
|
21
|
+
* query: { page: 'number?', limit: 'number?' },
|
|
22
|
+
* body: {
|
|
23
|
+
* name: 'string',
|
|
24
|
+
* age: 'number',
|
|
25
|
+
* tags: 'string[]?',
|
|
26
|
+
* address: { city: 'string', zip: 'string?' },
|
|
27
|
+
* },
|
|
28
|
+
* handler: async (c) => {
|
|
29
|
+
* c.query.page; // number | undefined
|
|
30
|
+
* c.body.name; // string
|
|
31
|
+
* c.body.address.city // string
|
|
32
|
+
* },
|
|
33
|
+
* }),
|
|
34
|
+
* }),
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
/** Primitive type strings. */
|
|
39
|
+
type PrimitiveType = 'string' | 'number' | 'boolean' | 'unknown';
|
|
40
|
+
/** Optional primitive type strings. */
|
|
41
|
+
type OptionalPrimitiveType = 'string?' | 'number?' | 'boolean?' | 'unknown?';
|
|
42
|
+
/** Array type strings. */
|
|
43
|
+
type ArrayType = 'string[]' | 'number[]' | 'boolean[]' | 'unknown[]';
|
|
44
|
+
/** Optional array type strings. */
|
|
45
|
+
type OptionalArrayType = 'string[]?' | 'number[]?' | 'boolean[]?' | 'unknown[]?';
|
|
46
|
+
/** All supported type strings. */
|
|
47
|
+
type SchemaType = PrimitiveType | OptionalPrimitiveType | ArrayType | OptionalArrayType;
|
|
48
|
+
/** Schema definition using shorthand syntax. Supports nested objects. */
|
|
49
|
+
interface SchemaDefinition {
|
|
50
|
+
[key: string]: SchemaType | SchemaDefinition;
|
|
51
|
+
}
|
|
52
|
+
/** Maps primitive type strings to TypeScript types. */
|
|
53
|
+
type SchemaTypeMap = {
|
|
54
|
+
string: string;
|
|
55
|
+
'string?': string | undefined;
|
|
56
|
+
number: number;
|
|
57
|
+
'number?': number | undefined;
|
|
58
|
+
boolean: boolean;
|
|
59
|
+
'boolean?': boolean | undefined;
|
|
60
|
+
unknown: unknown;
|
|
61
|
+
'unknown?': unknown | undefined;
|
|
62
|
+
'string[]': string[];
|
|
63
|
+
'string[]?': string[] | undefined;
|
|
64
|
+
'number[]': number[];
|
|
65
|
+
'number[]?': number[] | undefined;
|
|
66
|
+
'boolean[]': boolean[];
|
|
67
|
+
'boolean[]?': boolean[] | undefined;
|
|
68
|
+
'unknown[]': unknown[];
|
|
69
|
+
'unknown[]?': unknown[] | undefined;
|
|
70
|
+
};
|
|
71
|
+
/** Checks if a schema field type is optional (ends with ?). */
|
|
72
|
+
type IsOptionalField<T> = T extends `${string}?` ? true : false;
|
|
73
|
+
/** Infers TypeScript type from a schema field (type string or nested object). */
|
|
74
|
+
type InferSchemaField<T> = T extends SchemaType ? SchemaTypeMap[T] : T extends SchemaDefinition ? InferSchema<T> : never;
|
|
75
|
+
/** Gets keys of required fields (non-optional types). */
|
|
76
|
+
type RequiredKeys<T extends SchemaDefinition> = { [K in keyof T]: IsOptionalField<T[K]> extends true ? never : K }[keyof T];
|
|
77
|
+
/** Gets keys of optional fields (types ending with ?). */
|
|
78
|
+
type OptionalKeys<T extends SchemaDefinition> = { [K in keyof T]: IsOptionalField<T[K]> extends true ? K : never }[keyof T];
|
|
79
|
+
/**
|
|
80
|
+
* Infers TypeScript type from schema definition.
|
|
81
|
+
*
|
|
82
|
+
* Required fields become required properties, optional fields (ending with ?)
|
|
83
|
+
* become optional properties with `| undefined`.
|
|
84
|
+
*/
|
|
85
|
+
type InferSchema<T extends SchemaDefinition> = { [K in RequiredKeys<T>]: InferSchemaField<T[K]> } & { [K in OptionalKeys<T>]?: InferSchemaField<T[K]> };
|
|
86
|
+
/** Error object returned when validation fails. */
|
|
87
|
+
interface ValidationError {
|
|
88
|
+
flatten: () => {
|
|
89
|
+
fieldErrors: Record<string, string[]>;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Validation result matching Zod's safeParse API for compatibility. */
|
|
93
|
+
type ValidationResult<T> = {
|
|
94
|
+
success: true;
|
|
95
|
+
data: T;
|
|
96
|
+
error?: undefined;
|
|
97
|
+
} | {
|
|
98
|
+
success: false;
|
|
99
|
+
data?: undefined;
|
|
100
|
+
error: ValidationError;
|
|
101
|
+
};
|
|
102
|
+
/** Compiled schema with validate method. */
|
|
103
|
+
interface CompiledSchema<T> {
|
|
104
|
+
safeParse: (data: object) => ValidationResult<T>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Compiles a schema definition into a validator.
|
|
108
|
+
*
|
|
109
|
+
* Used internally by the router to validate query parameters and request bodies.
|
|
110
|
+
* Provides a Zod-compatible `safeParse` API for consistency.
|
|
111
|
+
*
|
|
112
|
+
* @param schema - The schema definition object
|
|
113
|
+
* @returns A compiled schema with `safeParse` method
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const userSchema = compileSchema({
|
|
118
|
+
* name: 'string',
|
|
119
|
+
* age: 'number',
|
|
120
|
+
* email: 'string?',
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* const result = userSchema.safeParse({ name: 'Alice', age: 30 });
|
|
124
|
+
* if (result.success) {
|
|
125
|
+
* console.log(result.data.name); // 'Alice'
|
|
126
|
+
* } else {
|
|
127
|
+
* console.log(result.error.flatten().fieldErrors);
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
declare function compileSchema<T extends SchemaDefinition>(schema: T): CompiledSchema<InferSchema<T>>;
|
|
132
|
+
//#endregion
|
|
133
|
+
//#region src/types.d.ts
|
|
134
|
+
/** Standard HTTP methods. */
|
|
135
|
+
type Method = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options' | 'head';
|
|
136
|
+
/**
|
|
137
|
+
* Brand symbol for Router type identification across module boundaries.
|
|
138
|
+
* This enables TypeScript to recognize Router types even when generic
|
|
139
|
+
* type information is lost during cross-package imports.
|
|
140
|
+
*/
|
|
141
|
+
declare const ROUTER_BRAND: unique symbol;
|
|
142
|
+
/** Brand type for Router identification in conditional types. */
|
|
143
|
+
type RouterBrand = {
|
|
144
|
+
readonly [ROUTER_BRAND]: true;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Collects path params from a property path array.
|
|
148
|
+
*/
|
|
149
|
+
type CollectPathParams<Path extends string[]> = Path extends [infer Head extends string, ...infer Rest extends string[]] ? (Head extends `$${infer Param}` ? { [K in Param]: string } : {}) & CollectPathParams<Rest> : {};
|
|
150
|
+
/** Execution context for background tasks (Cloudflare Workers compatible). */
|
|
151
|
+
interface ExecutionContext {
|
|
152
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
153
|
+
passThroughOnException(): void;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Unified context object passed to handlers.
|
|
157
|
+
*/
|
|
158
|
+
type Context<Q = unknown, B = unknown, P extends Record<string, string> = Record<string, string>, Ctx = {}> = {
|
|
159
|
+
/** The original Request. */
|
|
160
|
+
request: Request;
|
|
161
|
+
/** URL path parameters from `$param` segments. */
|
|
162
|
+
path: P;
|
|
163
|
+
/** Query string parameters. */
|
|
164
|
+
query: Q;
|
|
165
|
+
/** Request body. */
|
|
166
|
+
body: B;
|
|
167
|
+
/** Execution context for background tasks. */
|
|
168
|
+
executionCtx?: ExecutionContext;
|
|
169
|
+
/** Environment bindings. */
|
|
170
|
+
env: Ctx extends {
|
|
171
|
+
env: infer E;
|
|
172
|
+
} ? E : unknown;
|
|
173
|
+
} & Omit<Ctx, 'env'>;
|
|
174
|
+
/**
|
|
175
|
+
* Typed response wrapper that carries the response body type.
|
|
176
|
+
*/
|
|
177
|
+
type TypedResponse<T> = Response & {
|
|
178
|
+
__responseType?: T;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Handler function type.
|
|
182
|
+
*/
|
|
183
|
+
type TypedHandler<Q = unknown, B = unknown, P extends Record<string, string> = Record<string, string>, R = unknown, Ctx = {}> = (context: Context<Q, B, P, Ctx>) => R | Response | TypedResponse<R> | Promise<R | Response | TypedResponse<R>>;
|
|
184
|
+
/**
|
|
185
|
+
* Route definition - a single HTTP method handler with optional schemas.
|
|
186
|
+
*
|
|
187
|
+
* Use `route()` when you need query or body validation schemas.
|
|
188
|
+
* For simple handlers without schemas, use bare functions in the router.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* // With schemas
|
|
193
|
+
* get: route({
|
|
194
|
+
* query: { limit: 'number?' },
|
|
195
|
+
* handler: async (c) => ({ items: [], limit: c.query.limit }),
|
|
196
|
+
* })
|
|
197
|
+
*
|
|
198
|
+
* // Without schemas (bare function shorthand)
|
|
199
|
+
* get: async (c) => ({ items: [] })
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
interface RouteDefinition<Q extends SchemaDefinition = {}, B extends SchemaDefinition = {}, R = unknown, P extends Record<string, string> = Record<string, string>, Ctx = {}> {
|
|
203
|
+
/** Query parameter schema for validation. */
|
|
204
|
+
query?: Q;
|
|
205
|
+
/** Request body schema for validation. */
|
|
206
|
+
body?: B;
|
|
207
|
+
/** Optional description for documentation. */
|
|
208
|
+
description?: string;
|
|
209
|
+
/** The request handler function. */
|
|
210
|
+
handler: TypedHandler<InferSchema<Q>, InferSchema<B>, P, R, Ctx>;
|
|
211
|
+
}
|
|
212
|
+
/** A bare handler function (shorthand for route without schemas). */
|
|
213
|
+
type BareHandler = (context: Context<unknown, unknown, Record<string, string>, unknown>) => unknown;
|
|
214
|
+
/** Checks if a value is a function. */
|
|
215
|
+
declare function isFunction(value: unknown): value is BareHandler;
|
|
216
|
+
/** Checks if entry is a Route (marked with ROUTE_MARKER). */
|
|
217
|
+
declare function isRoute(entry: unknown): entry is RouteDefinition;
|
|
218
|
+
/** Checks if entry is a Router (marked with ROUTER_MARKER). */
|
|
219
|
+
declare function isRouter(entry: unknown): entry is Router<RouterRoutes>;
|
|
220
|
+
/**
|
|
221
|
+
* A method handler - either a route with schemas or a bare function.
|
|
222
|
+
*/
|
|
223
|
+
type MethodEntry = RouteDefinition<any, any, any, any, any> | BareHandler;
|
|
224
|
+
/**
|
|
225
|
+
* Router routes record.
|
|
226
|
+
*
|
|
227
|
+
* Can contain:
|
|
228
|
+
* - `get`, `post`, `put`, `patch`, `delete` - method handlers (route or function)
|
|
229
|
+
* - Other property names - nested routers (path segments)
|
|
230
|
+
* - `$param` properties - dynamic path segments
|
|
231
|
+
*/
|
|
232
|
+
type RouterRoutes = {
|
|
233
|
+
get?: MethodEntry;
|
|
234
|
+
post?: MethodEntry;
|
|
235
|
+
put?: MethodEntry;
|
|
236
|
+
patch?: MethodEntry;
|
|
237
|
+
delete?: MethodEntry;
|
|
238
|
+
options?: MethodEntry;
|
|
239
|
+
head?: MethodEntry;
|
|
240
|
+
} & {
|
|
241
|
+
[key: string]: MethodEntry | Router<RouterRoutes>;
|
|
242
|
+
};
|
|
243
|
+
/** Handler function signature for the standalone router. */
|
|
244
|
+
type FetchHandler = (request: Request, env?: unknown, ctx?: ExecutionContext) => Response | Promise<Response>;
|
|
245
|
+
/**
|
|
246
|
+
* Router - groups paths and can have middleware.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* const api = router({
|
|
251
|
+
* // Method handlers for this path
|
|
252
|
+
* get: async (c) => [...],
|
|
253
|
+
* post: route({ body: { name: 'string' }, handler: async (c) => c.body }),
|
|
254
|
+
*
|
|
255
|
+
* // Nested path segments
|
|
256
|
+
* users: router({
|
|
257
|
+
* get: async (c) => [...],
|
|
258
|
+
* $id: router({
|
|
259
|
+
* get: async (c) => ({ id: c.path.id }),
|
|
260
|
+
* }),
|
|
261
|
+
* }),
|
|
262
|
+
* });
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
interface Router<T extends RouterRoutes> extends RouterBrand {
|
|
266
|
+
/** Route definitions and nested routers. */
|
|
267
|
+
readonly routes: T;
|
|
268
|
+
/** Middleware applied to all routes in this router. */
|
|
269
|
+
readonly middleware?: Middleware[];
|
|
270
|
+
/**
|
|
271
|
+
* Creates a fetch handler for use with Cloudflare Workers, Deno, Bun, etc.
|
|
272
|
+
*/
|
|
273
|
+
handler(): FetchHandler;
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/middleware.d.ts
|
|
277
|
+
/**
|
|
278
|
+
* Base context properties always available to middleware.
|
|
279
|
+
*/
|
|
280
|
+
interface BaseMiddlewareContext {
|
|
281
|
+
request: Request;
|
|
282
|
+
path: Record<string, string>;
|
|
283
|
+
query: unknown;
|
|
284
|
+
body: unknown;
|
|
285
|
+
executionCtx?: ExecutionContext;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Context passed to middleware functions.
|
|
289
|
+
*
|
|
290
|
+
* The `env` property is always required (extracted from Ctx if provided).
|
|
291
|
+
* Other properties from Ctx are optional since middleware builds them up.
|
|
292
|
+
*
|
|
293
|
+
* @typeParam Ctx - Context type including env and middleware-added properties
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* interface AppContext {
|
|
298
|
+
* env: { JWT_SECRET: string };
|
|
299
|
+
* user: { id: string };
|
|
300
|
+
* }
|
|
301
|
+
*
|
|
302
|
+
* const middleware: Middleware<AppContext> = async (context, next) => {
|
|
303
|
+
* context.env.JWT_SECRET; // typed, required
|
|
304
|
+
* context.user; // typed as { id: string } | undefined
|
|
305
|
+
* context.user = { id: '123' };
|
|
306
|
+
* return next();
|
|
307
|
+
* };
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
type MiddlewareContext<Ctx = {}> = BaseMiddlewareContext & {
|
|
311
|
+
env: Ctx extends {
|
|
312
|
+
env: infer E;
|
|
313
|
+
} ? E : unknown;
|
|
314
|
+
} & Partial<Omit<Ctx, 'env'>> & {
|
|
315
|
+
[key: string]: unknown;
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Function to continue to the next middleware or handler.
|
|
319
|
+
*/
|
|
320
|
+
type MiddlewareNext = () => Promise<Response>;
|
|
321
|
+
/**
|
|
322
|
+
* Middleware function signature.
|
|
323
|
+
*
|
|
324
|
+
* @typeParam Ctx - Context type (default: {})
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* // Simple middleware without typed context
|
|
329
|
+
* const logger: Middleware = async (context, next) => {
|
|
330
|
+
* console.log(context.request.url);
|
|
331
|
+
* return next();
|
|
332
|
+
* };
|
|
333
|
+
*
|
|
334
|
+
* // Middleware with typed context
|
|
335
|
+
* interface AppContext {
|
|
336
|
+
* env: { JWT_SECRET: string };
|
|
337
|
+
* user: { id: string };
|
|
338
|
+
* }
|
|
339
|
+
* const auth: Middleware<AppContext> = async (context, next) => {
|
|
340
|
+
* const secret = context.env.JWT_SECRET; // typed, required
|
|
341
|
+
* context.user = { id: '123' }; // adds to context
|
|
342
|
+
* return next();
|
|
343
|
+
* };
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
type Middleware<Ctx = {}> = (context: MiddlewareContext<Ctx>, next: MiddlewareNext) => Promise<Response> | Response;
|
|
347
|
+
/**
|
|
348
|
+
* Runs a chain of middleware with a final handler.
|
|
349
|
+
*
|
|
350
|
+
* Middleware are executed in order. Each middleware receives the context and
|
|
351
|
+
* a `next` function. Calling `next()` continues to the next middleware or the
|
|
352
|
+
* final handler. Returning without calling `next()` short-circuits the chain.
|
|
353
|
+
*
|
|
354
|
+
* @param middleware - Array of middleware functions to run
|
|
355
|
+
* @param context - The request context
|
|
356
|
+
* @param finalHandler - Handler to call after all middleware complete
|
|
357
|
+
* @returns The response from the middleware chain or final handler
|
|
358
|
+
*/
|
|
359
|
+
declare function runMiddleware(middleware: Middleware[], context: MiddlewareContext, finalHandler: () => Promise<Response>): Promise<Response>;
|
|
360
|
+
/**
|
|
361
|
+
* Composes multiple middleware into a single middleware.
|
|
362
|
+
*
|
|
363
|
+
* Useful for grouping related middleware together.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```typescript
|
|
367
|
+
* const security = compose(
|
|
368
|
+
* cors(),
|
|
369
|
+
* rateLimit({ max: 100 }),
|
|
370
|
+
* errorHandler(),
|
|
371
|
+
* );
|
|
372
|
+
*
|
|
373
|
+
* const api = router('/api', routes, security);
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* @param middleware - Middleware functions to compose
|
|
377
|
+
* @returns A single middleware that runs all provided middleware in order
|
|
378
|
+
*/
|
|
379
|
+
declare function compose(...middleware: Middleware[]): Middleware;
|
|
380
|
+
//#endregion
|
|
381
|
+
export { SchemaDefinition as C, compileSchema as D, ValidationResult as E, InferSchema as S, SchemaTypeMap as T, TypedResponse as _, runMiddleware as a, isRouter as b, ExecutionContext as c, MethodEntry as d, RouteDefinition as f, TypedHandler as g, RouterRoutes as h, compose as i, FetchHandler as l, RouterBrand as m, MiddlewareContext as n, CollectPathParams as o, Router as p, MiddlewareNext as r, Context as s, Middleware as t, Method as u, isFunction as v, SchemaType as w, CompiledSchema as x, isRoute as y };
|
|
382
|
+
//# sourceMappingURL=middleware-CqtYOc6D.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-CqtYOc6D.d.mts","names":[],"sources":["../src/schema.ts","../src/types.ts","../src/middleware.ts"],"sourcesContent":[],"mappings":"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"}
|