@spfn/core 0.2.0-beta.53 → 0.2.0-beta.55

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.
Files changed (41) hide show
  1. package/dist/{boss-Cxqc-Oiw.d.ts → boss-CEik0yq-.d.ts} +7 -0
  2. package/dist/cache/index.js +10 -1
  3. package/dist/cache/index.js.map +1 -1
  4. package/dist/config/index.d.ts +213 -0
  5. package/dist/config/index.js +43 -1
  6. package/dist/config/index.js.map +1 -1
  7. package/dist/db/index.d.ts +57 -0
  8. package/dist/db/index.js +55 -8
  9. package/dist/db/index.js.map +1 -1
  10. package/dist/define-middleware-DuXD8Hvu.d.ts +167 -0
  11. package/dist/env/index.js +4 -3
  12. package/dist/env/index.js.map +1 -1
  13. package/dist/errors/index.d.ts +10 -0
  14. package/dist/errors/index.js +20 -2
  15. package/dist/errors/index.js.map +1 -1
  16. package/dist/event/index.d.ts +3 -3
  17. package/dist/event/index.js.map +1 -1
  18. package/dist/event/sse/client.d.ts +2 -2
  19. package/dist/event/sse/index.d.ts +4 -4
  20. package/dist/event/sse/index.js +41 -16
  21. package/dist/event/sse/index.js.map +1 -1
  22. package/dist/event/ws/client.d.ts +2 -2
  23. package/dist/event/ws/index.d.ts +3 -3
  24. package/dist/event/ws/index.js +75 -16
  25. package/dist/event/ws/index.js.map +1 -1
  26. package/dist/job/index.d.ts +2 -2
  27. package/dist/job/index.js +4 -4
  28. package/dist/job/index.js.map +1 -1
  29. package/dist/middleware/index.d.ts +64 -2
  30. package/dist/middleware/index.js +1030 -96
  31. package/dist/middleware/index.js.map +1 -1
  32. package/dist/nextjs/server.js +17 -0
  33. package/dist/nextjs/server.js.map +1 -1
  34. package/dist/route/index.d.ts +3 -165
  35. package/dist/server/index.d.ts +4 -4
  36. package/dist/server/index.js +125 -39
  37. package/dist/server/index.js.map +1 -1
  38. package/dist/{token-manager-C2Ag5-s8.d.ts → token-manager-jKD_EsSE.d.ts} +0 -1
  39. package/dist/{types-VpVQIsyB.d.ts → types-BFB72jbM.d.ts} +1 -1
  40. package/dist/{types-CKsmzaB8.d.ts → types-DVjf37yO.d.ts} +37 -1
  41. package/package.json +6 -6
@@ -8,6 +8,7 @@ var PROXY_SIGNATURE_HEADER = "x-spfn-proxy-signature";
8
8
  var PROXY_TIMESTAMP_HEADER = "x-spfn-proxy-timestamp";
9
9
  var PROXY_NONCE_HEADER = "x-spfn-proxy-nonce";
10
10
  var PROXY_KEY_ID_HEADER = "x-spfn-proxy-key-id";
11
+ var PROXY_CLIENT_IP_HEADER = "x-spfn-proxy-client-ip";
11
12
  var DEFAULT_KEY_ID = "default";
12
13
  function parseProxyKey(raw) {
13
14
  const idx = raw.indexOf(":");
@@ -256,6 +257,17 @@ function registerInterceptors(packageName, interceptors) {
256
257
  }
257
258
 
258
259
  // src/nextjs/proxy/helpers.ts
260
+ function clientIpFromForwardedChain(xff, trustedHops) {
261
+ if (!xff) return void 0;
262
+ const parts = xff.split(",").map((s) => s.trim()).filter(Boolean);
263
+ if (parts.length === 0) return void 0;
264
+ const idx = parts.length - Math.max(1, trustedHops);
265
+ return parts[idx] ?? parts[0];
266
+ }
267
+ function trustedProxyHops() {
268
+ const raw = Number.parseInt(process.env.TRUSTED_PROXY_HOPS ?? "", 10);
269
+ return Number.isFinite(raw) && raw > 0 ? raw : 1;
270
+ }
259
271
  function buildProxyHeaders(sourceHeaders, defaultHeaders) {
260
272
  const headers = new Headers();
261
273
  const headersToForward2 = [
@@ -275,6 +287,11 @@ function buildProxyHeaders(sourceHeaders, defaultHeaders) {
275
287
  headers.set(header, value);
276
288
  }
277
289
  }
290
+ const xff = sourceHeaders instanceof Headers ? sourceHeaders.get("x-forwarded-for") : sourceHeaders["x-forwarded-for"];
291
+ const clientIp = clientIpFromForwardedChain(xff, trustedProxyHops());
292
+ if (clientIp) {
293
+ headers.set(PROXY_CLIENT_IP_HEADER, clientIp);
294
+ }
278
295
  for (const [key, value] of Object.entries(defaultHeaders)) {
279
296
  headers.set(key, value);
280
297
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/security/proxy-signature.ts","../../src/nextjs/shared.ts","../../src/nextjs/proxy/interceptors/helpers.ts","../../src/nextjs/proxy/interceptors/registry.ts","../../src/nextjs/proxy/helpers.ts","../../src/nextjs/proxy/rpc.ts"],"names":["headersToForward"],"mappings":"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","file":"server.js","sourcesContent":["/**\n * Proxy → Backend request signing (HMAC-SHA256) with key rotation\n *\n * Shared between the Next.js RPC proxy (which signs outbound requests) and the\n * backend proxy-guard middleware (which verifies them). Keeping the canonical\n * string and HMAC logic in one pure module guarantees both sides agree.\n *\n * The signature proves a request passed through a trusted proxy that holds the\n * shared secret. It does NOT authenticate the end user — that is the JWT layer's\n * job — and it cannot stop a client from calling the proxy itself. Its role is to\n * stop direct-to-backend calls that bypass the proxy.\n *\n * Keys are identified by a short keyId so secrets can be rotated without downtime:\n * the proxy signs with the active key and stamps its keyId in a header; the\n * backend keeps a set of accepted keys (current + previous) and selects by keyId.\n * Roll a new key in, let both sides pick it up, switch the proxy to it, then drop\n * the old one after a grace period — no request is ever rejected mid-rotation.\n *\n * @module security/proxy-signature\n */\nimport { createHmac, randomBytes, timingSafeEqual, createHash } from 'node:crypto';\n\n// ============================================================================\n// Header names\n// ============================================================================\n\nexport const PROXY_SIGNATURE_HEADER = 'x-spfn-proxy-signature';\nexport const PROXY_TIMESTAMP_HEADER = 'x-spfn-proxy-timestamp';\nexport const PROXY_NONCE_HEADER = 'x-spfn-proxy-nonce';\nexport const PROXY_KEY_ID_HEADER = 'x-spfn-proxy-key-id';\n\n/** keyId used when a raw secret carries no `keyId:` prefix (back-compat). */\nexport const DEFAULT_KEY_ID = 'default';\n\n// ============================================================================\n// Keys\n// ============================================================================\n\n/**\n * A signing key: a short identifier plus its secret. Encoded in env as\n * `<keyId>:<secret>` (e.g. `v2:9f3c...`); a bare secret gets `DEFAULT_KEY_ID`.\n */\nexport interface ProxyKey\n{\n keyId: string;\n secret: string;\n}\n\n/**\n * Parse one `<keyId>:<secret>` (or bare `<secret>`) string into a ProxyKey.\n * keyId is everything before the FIRST colon; the secret keeps any later colons.\n */\nexport function parseProxyKey(raw: string): ProxyKey\n{\n const idx = raw.indexOf(':');\n if (idx === -1)\n {\n return { keyId: DEFAULT_KEY_ID, secret: raw };\n }\n\n // A leading colon (':secret') yields an empty keyId; the backend treats an empty\n // keyId header as \"missing\" and rejects, so normalize it to DEFAULT_KEY_ID.\n return { keyId: raw.slice(0, idx) || DEFAULT_KEY_ID, secret: raw.slice(idx + 1) };\n}\n\n/**\n * Build the backend's accepted key set from raw env values. Each input may be a\n * single key or a comma-separated list (for multiple grace keys). Earlier inputs\n * win on keyId collision, so pass the active secret first.\n */\nexport function parseProxyKeySet(raws: Array<string | undefined | null>): ProxyKey[]\n{\n const keys: ProxyKey[] = [];\n const seen = new Set<string>();\n\n for (const raw of raws)\n {\n if (!raw)\n {\n continue;\n }\n\n for (const part of raw.split(','))\n {\n const trimmed = part.trim();\n if (!trimmed)\n {\n continue;\n }\n\n const key = parseProxyKey(trimmed);\n if (!key.secret || seen.has(key.keyId))\n {\n continue;\n }\n\n seen.add(key.keyId);\n keys.push(key);\n }\n }\n\n return keys;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * The parts of a request that are bound into the signature.\n */\nexport interface SignatureParts\n{\n /** Resolved backend HTTP method (GET/POST/PUT/...) */\n method: string;\n\n /**\n * Raw (wire) backend path, percent-encoding preserved, leading slash, NO query.\n * Proxy signs its outbound request-target path; the backend reconstructs the\n * SAME bytes from `new URL(c.req.url).pathname` (NOT the decoded `c.req.path`).\n */\n path: string;\n\n /**\n * Raw (wire) query string including leading `?`, or '' when there is none.\n * Both sides use the verbatim search string so query params are authenticated.\n */\n query: string;\n\n /** Unix epoch milliseconds, as a string */\n timestamp: string;\n\n /** Random per-request nonce */\n nonce: string;\n\n /**\n * SHA-256 hex of the raw request body, or empty string when there is no body\n * (GET/HEAD, or multipart uploads which are excluded by design). Bound for ANY\n * non-multipart content-type, not just application/json.\n */\n bodyHash: string;\n}\n\n/**\n * Headers produced by signing — ready to set on the outbound fetch.\n */\nexport interface SignatureHeaders\n{\n [PROXY_SIGNATURE_HEADER]: string;\n [PROXY_TIMESTAMP_HEADER]: string;\n [PROXY_NONCE_HEADER]: string;\n [PROXY_KEY_ID_HEADER]: string;\n}\n\n// ============================================================================\n// Canonical string + HMAC\n// ============================================================================\n\n/**\n * Build the canonical string that gets signed. Field order is fixed and each\n * field is newline-separated, so a captured signature cannot be replayed against\n * a different method, path, or body.\n */\nexport function buildCanonicalString(parts: SignatureParts): string\n{\n return [parts.method.toUpperCase(), parts.path, parts.query, parts.timestamp, parts.nonce, parts.bodyHash].join('\\n');\n}\n\n/**\n * Hash a request body. Accepts a string (proxy side, from JSON.stringify) or a\n * Buffer (backend side, straight from the stream — avoids a bytes→string→bytes\n * round-trip). Returns '' for empty/no body. A utf8 string and its Buffer hash\n * identically, so both sides agree.\n */\nexport function hashBody(body: string | Buffer | undefined | null): string\n{\n if (!body || body.length === 0)\n {\n return '';\n }\n\n return typeof body === 'string'\n ? createHash('sha256').update(body, 'utf8').digest('hex')\n : createHash('sha256').update(body).digest('hex');\n}\n\n/**\n * Compute the HMAC-SHA256 signature (hex) for the given parts.\n */\nexport function computeSignature(secret: string, parts: SignatureParts): string\n{\n return createHmac('sha256', secret).update(buildCanonicalString(parts)).digest('hex');\n}\n\n/**\n * Generate a fresh random nonce (hex).\n */\nexport function generateNonce(): string\n{\n return randomBytes(16).toString('hex');\n}\n\n// ============================================================================\n// Sign (proxy side)\n// ============================================================================\n\nexport interface SignInput\n{\n /** The active signing key. */\n key: ProxyKey;\n method: string;\n /** Raw (wire) path, leading slash, percent-encoding preserved, NO query. */\n path: string;\n /** Raw (wire) query string including leading `?`, or '' / omitted for none. */\n query?: string;\n /** Raw request body string, if any. Omit for GET / multipart uploads. */\n body?: string | null;\n /** Override the timestamp (testing). Defaults to Date.now(). */\n timestamp?: string;\n /** Override the nonce (testing). Defaults to a random nonce. */\n nonce?: string;\n}\n\n/**\n * Sign an outbound proxy request and return the headers to attach (including the\n * keyId so the backend knows which key to verify against).\n */\nexport function signProxyRequest(input: SignInput): SignatureHeaders\n{\n const timestamp = input.timestamp ?? String(Date.now());\n const nonce = input.nonce ?? generateNonce();\n const signature = computeSignature(input.key.secret, {\n method: input.method,\n path: input.path,\n query: input.query ?? '',\n timestamp,\n nonce,\n bodyHash: hashBody(input.body),\n });\n\n return {\n [PROXY_SIGNATURE_HEADER]: signature,\n [PROXY_TIMESTAMP_HEADER]: timestamp,\n [PROXY_NONCE_HEADER]: nonce,\n [PROXY_KEY_ID_HEADER]: input.key.keyId,\n };\n}\n\n// ============================================================================\n// Verify (backend side)\n// ============================================================================\n\nexport type VerifyFailureReason =\n | 'missing-headers'\n | 'stale-timestamp'\n | 'bad-timestamp'\n | 'unknown-key'\n | 'signature-mismatch';\n\n// ('body-read-error' / 'origin-not-allowed' / 'nonce-replay' are guard-level\n// reasons carried separately by the middleware, not produced by verify itself.)\n\nexport interface VerifyResult\n{\n valid: boolean;\n reason?: VerifyFailureReason;\n /** The verified nonce, for optional replay tracking by the caller. */\n nonce?: string;\n /** The keyId the request was verified against (for logging/metrics). */\n keyId?: string;\n}\n\nexport interface VerifyInput\n{\n /** Accepted keys (active + grace). Selected by the request's keyId header. */\n keys: ProxyKey[];\n method: string;\n /** Raw (wire) path from `new URL(c.req.url).pathname` — NOT decoded `c.req.path`. */\n path: string;\n /** Raw (wire) query string from `new URL(c.req.url).search`, or '' for none. */\n query?: string;\n /** Raw request body (string or Buffer) as received, if any. */\n body?: string | Buffer | null;\n signature: string | null | undefined;\n timestamp: string | null | undefined;\n nonce: string | null | undefined;\n keyId: string | null | undefined;\n /** Allowed clock skew / replay window in ms. Default 30s. */\n windowMs?: number;\n /** Current time (testing). Defaults to Date.now(). */\n now?: number;\n}\n\n/**\n * Constant-time hex string comparison. Returns false on any length diff.\n *\n * Buffer.from(_, 'hex') does NOT throw on invalid hex — it silently truncates at\n * the first bad char — so a malformed signature yields a shorter buffer that the\n * length check rejects. No try/catch needed.\n */\nfunction safeEqualHex(a: string, b: string): boolean\n{\n const bufA = Buffer.from(a, 'hex');\n const bufB = Buffer.from(b, 'hex');\n\n if (bufA.length === 0 || bufA.length !== bufB.length)\n {\n return false;\n }\n\n return timingSafeEqual(bufA, bufB);\n}\n\n/**\n * Verify an inbound request's proxy signature against the accepted key set.\n *\n * Checks, in order: headers present, timestamp within window (replay guard),\n * keyId known, and HMAC match. Nonce-level replay rejection is left to the caller\n * (optional, Redis-backed) since not every deployment has a shared store.\n */\nexport function verifyProxyRequest(input: VerifyInput): VerifyResult\n{\n const { signature, timestamp, nonce, keyId } = input;\n\n if (!signature || !timestamp || !nonce || !keyId)\n {\n return { valid: false, reason: 'missing-headers' };\n }\n\n const ts = Number(timestamp);\n if (!Number.isFinite(ts))\n {\n return { valid: false, reason: 'bad-timestamp' };\n }\n\n const now = input.now ?? Date.now();\n const windowMs = input.windowMs ?? 30_000;\n if (Math.abs(now - ts) > windowMs)\n {\n return { valid: false, reason: 'stale-timestamp' };\n }\n\n const key = input.keys.find(k => k.keyId === keyId);\n if (!key)\n {\n return { valid: false, reason: 'unknown-key' };\n }\n\n const expected = computeSignature(key.secret, {\n method: input.method,\n path: input.path,\n query: input.query ?? '',\n timestamp,\n nonce,\n bodyHash: hashBody(input.body),\n });\n\n if (!safeEqualHex(signature, expected))\n {\n return { valid: false, reason: 'signature-mismatch' };\n }\n\n return { valid: true, nonce, keyId };\n}\n","/**\n * Shared utilities for Next.js client and proxy modules\n *\n * Contains common functions used by both client and proxy to avoid code duplication.\n */\n\n/**\n * Build URL with path parameters replaced\n *\n * @example\n * buildUrlWithParams('/users/:id/posts/:postId', { id: '123', postId: '456' })\n * // Returns: '/users/123/posts/456'\n */\nexport function buildUrlWithParams(path: string, params: Record<string, any>): string\n{\n let url = path;\n for (const [key, value] of Object.entries(params))\n {\n url = url.replace(`:${key}`, encodeURIComponent(String(value)));\n }\n\n return url;\n}\n\n/**\n * Build query string from object\n *\n * @example\n * buildQueryString({ page: '1', limit: '10', tags: ['foo', 'bar'] })\n * // Returns: '?page=1&limit=10&tags=foo&tags=bar'\n */\nexport function buildQueryString(query: Record<string, any>): string\n{\n if (Object.keys(query).length === 0)\n {\n return '';\n }\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query))\n {\n if (Array.isArray(value))\n {\n value.forEach((v) => searchParams.append(key, String(v)));\n }\n else\n {\n searchParams.append(key, String(value));\n }\n }\n\n return `?${searchParams.toString()}`;\n}\n\n/**\n * Build Cookie header string from cookies object\n *\n * @example\n * buildCookieHeader({ session: 'abc123', theme: 'dark' })\n * // Returns: 'session=abc123; theme=dark'\n */\nexport function buildCookieHeader(cookies: Record<string, string>): string\n{\n return Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join('; ');\n}\n\n/**\n * Parse response body based on content type\n *\n * Handles:\n * - 204 No Content: returns null (no body expected)\n * - application/json: parses JSON body\n * - Other content types: returns raw text\n */\nexport async function parseResponseBody(response: Response): Promise<any>\n{\n // 204 No Content has no body\n if (response.status === 204)\n {\n return null;\n }\n\n const contentType = response.headers.get('content-type');\n\n if (contentType?.includes('application/json'))\n {\n const text = await response.text();\n\n return text ? JSON.parse(text) : null;\n }\n else\n {\n return await response.text();\n }\n}\n","/**\n * SPFN Next.js Proxy Interceptor Execution Engine\n */\n\nimport type {\n InterceptorRule,\n RequestInterceptor,\n ResponseInterceptor,\n RequestInterceptorContext,\n ResponseInterceptorContext,\n} from './types';\n\n/**\n * Check if path matches pattern\n *\n * Supports:\n * - Wildcards: '/_auth/*' matches '/_auth/login'\n * - Path params: '/users/:id' matches '/users/123'\n * - RegExp: /^\\/_auth\\/.+$/ matches '/_auth/login'\n * - Exact match: '/_auth/login' matches '/_auth/login'\n * - All: '*' matches any path\n *\n * @param path - Request path to test\n * @param pattern - Pattern to match against\n * @returns True if path matches pattern\n */\nexport function matchPath(path: string, pattern: string | RegExp): boolean\n{\n // Match all\n if (pattern === '*')\n {\n return true;\n }\n\n // RegExp pattern\n if (pattern instanceof RegExp)\n {\n return pattern.test(path);\n }\n\n // String pattern\n // Convert wildcard pattern to RegExp\n // '/_auth/*' -> /^\\/_auth\\/.*/\n // '/users/:id' -> /^\\/users\\/[^/]+$/\n const regexPattern = pattern\n .replace(/\\*/g, '.*')\n .replace(/:[^/]+/g, '[^/]+')\n .replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n\n return regex.test(path);\n}\n\n/**\n * Check if method matches pattern\n *\n * @param method - Request method (e.g., 'POST')\n * @param pattern - Method pattern (e.g., 'POST' or ['POST', 'PUT'])\n * @returns True if method matches pattern\n */\nexport function matchMethod(\n method: string,\n pattern?: string | string[],\n): boolean\n{\n // No method filter = match all\n if (!pattern)\n {\n return true;\n }\n\n // Single method\n if (typeof pattern === 'string')\n {\n return method.toUpperCase() === pattern.toUpperCase();\n }\n\n // Multiple methods\n return pattern.some((m) => m.toUpperCase() === method.toUpperCase());\n}\n\n/**\n * Filter interceptors that match the request\n *\n * @param rules - All interceptor rules\n * @param path - Request path\n * @param method - Request method\n * @returns Matched interceptors\n */\nexport function filterMatchingInterceptors(\n rules: InterceptorRule[],\n path: string,\n method: string,\n): InterceptorRule[]\n{\n return rules.filter((rule) => \n {\n return matchPath(path, rule.pathPattern) && matchMethod(method, rule.method);\n });\n}\n\n/**\n * Execute request interceptors in chain\n *\n * Interceptors are executed in order:\n * 1. First registered interceptor\n * 2. Second registered interceptor\n * 3. ... and so on\n *\n * Each interceptor must call next() to continue the chain.\n * If next() is not called, the chain stops and remaining interceptors are skipped.\n *\n * @param context - Request interceptor context\n * @param interceptors - Interceptors to execute\n */\nexport async function executeRequestInterceptors(\n context: RequestInterceptorContext,\n interceptors: RequestInterceptor[],\n): Promise<void>\n{\n let index = 0;\n\n const next = async (): Promise<void> => \n {\n if (index >= interceptors.length)\n {\n return;\n }\n\n const interceptor = interceptors[index];\n index++;\n\n await interceptor(context, next);\n };\n\n await next();\n}\n\n/**\n * Execute response interceptors in chain\n *\n * Interceptors are executed in order:\n * 1. First registered interceptor\n * 2. Second registered interceptor\n * 3. ... and so on\n *\n * Each interceptor must call next() to continue the chain.\n * If next() is not called, the chain stops and remaining interceptors are skipped.\n *\n * @param context - Response interceptor context\n * @param interceptors - Interceptors to execute\n */\nexport async function executeResponseInterceptors(\n context: ResponseInterceptorContext,\n interceptors: ResponseInterceptor[],\n): Promise<void>\n{\n let index = 0;\n\n const next = async (): Promise<void> => \n {\n if (index >= interceptors.length)\n {\n return;\n }\n\n const interceptor = interceptors[index];\n index++;\n\n await interceptor(context, next);\n };\n\n await next();\n}\n","/**\n * Global Interceptor Registry\n *\n * Allows packages to automatically register their interceptors\n * for Next.js proxy without manual configuration.\n *\n * Uses globalThis for persistence across module reloads (HMR).\n */\nimport type { InterceptorRule } from './types';\n\n// ============================================================================\n// Global Type Declarations\n// ============================================================================\n\n/**\n * Extend globalThis with interceptor registry\n *\n * Using globalThis allows the registry to persist across module reloads (HMR).\n * preventing duplicate registrations during development with HMR.\n */\ndeclare global\n{\n var __SPFN_INTERCEPTOR_REGISTRY__: InterceptorRegistry | undefined;\n}\n\n/**\n * Global interceptor registry\n *\n * Packages register their interceptors on import,\n * and proxy automatically discovers and applies them.\n */\nexport class InterceptorRegistry\n{\n private interceptors = new Map<string, InterceptorRule[]>();\n\n /**\n * Register interceptors for a package\n *\n * @param packageName - Unique package identifier (e.g., 'auth', 'storage')\n * @param interceptors - Array of interceptor rules\n *\n * @example\n * ```typescript\n * registerInterceptors('auth', [\n * {\n * pathPattern: '/_auth/*',\n * request: async (ctx, next) => { ... }\n * }\n * ]);\n * ```\n */\n register(packageName: string, interceptors: InterceptorRule[]): void\n {\n if (!this.interceptors.has(packageName))\n {\n this.interceptors.set(packageName, interceptors);\n }\n }\n\n /**\n * Get all registered interceptors\n *\n * @param exclude - Package names to exclude\n * @returns Flat array of all interceptor rules\n */\n getAll(exclude: string[] = []): InterceptorRule[]\n {\n const all: InterceptorRule[] = [];\n\n for (const [packageName, interceptors] of this.interceptors.entries())\n {\n if (!exclude.includes(packageName))\n {\n all.push(...interceptors);\n }\n }\n\n return all;\n }\n\n /**\n * Get interceptors for specific package\n *\n * @param packageName - Package identifier\n * @returns Interceptor rules or undefined\n */\n get(packageName: string): InterceptorRule[] | undefined\n {\n return this.interceptors.get(packageName);\n }\n\n /**\n * Get list of registered package names\n */\n getPackageNames(): string[]\n {\n return Array.from(this.interceptors.keys());\n }\n\n /**\n * Check if package has registered interceptors\n */\n has(packageName: string): boolean\n {\n return this.interceptors.has(packageName);\n }\n\n /**\n * Unregister interceptors for a package\n *\n * @param packageName - Package identifier\n */\n unregister(packageName: string): void\n {\n this.interceptors.delete(packageName);\n }\n\n /**\n * Clear all registered interceptors\n *\n * Useful for testing\n */\n clear(): void\n {\n this.interceptors.clear();\n }\n\n /**\n * Get total count of registered interceptors\n */\n count(): number\n {\n let total = 0;\n for (const interceptors of this.interceptors.values())\n {\n total += interceptors.length;\n }\n\n return total;\n }\n}\n\n/**\n * Global singleton registry instance\n *\n * Uses globalThis to persist across module reloads (HMR).\n * This prevents duplicate registrations during development.\n */\nexport const interceptorRegistry = (() =>\n{\n if (!globalThis.__SPFN_INTERCEPTOR_REGISTRY__)\n {\n globalThis.__SPFN_INTERCEPTOR_REGISTRY__ = new InterceptorRegistry();\n }\n\n return globalThis.__SPFN_INTERCEPTOR_REGISTRY__;\n})();\n\n/**\n * Register interceptors for a package\n *\n * This should be called during package initialization (on import).\n * The interceptors will be automatically applied by the Next.js proxy.\n *\n * @param packageName - Unique package identifier (e.g., 'auth', 'storage')\n * @param interceptors - Array of interceptor rules\n *\n * @example\n * ```typescript\n * // packages/auth/src/adapters/nextjs/interceptors/index.ts\n * import { registerInterceptors } from '@spfn/core/nextjs';\n *\n * const authInterceptors = [\n * {\n * pathPattern: '/_auth/*',\n * request: async (ctx, next) => {\n * // Add JWT token\n * ctx.headers['Authorization'] = 'Bearer token';\n * await next();\n * }\n * }\n * ];\n *\n * // Auto-register on import\n * registerInterceptors('auth', authInterceptors);\n * ```\n */\nexport function registerInterceptors(\n packageName: string,\n interceptors: InterceptorRule[],\n): void\n{\n interceptorRegistry.register(packageName, interceptors);\n}\n","/**\n * Helper functions for proxy handler\n * Separates utility logic from main proxy handler for better maintainability\n */\nimport { NextRequest } from 'next/server';\nimport type { CookieOptions, SetCookie } from '../client';\nimport type { InterceptorRule, RequestInterceptorContext, ResponseInterceptorContext } from './interceptors/types';\nimport type { InterceptorRegistry } from './interceptors';\n\n// Re-export from shared\nexport { parseResponseBody } from '../shared';\n\n/**\n * Build request headers for proxying\n * Forwards important headers from source and adds default headers\n *\n * @param sourceHeaders - Source headers (can be Headers object or Record)\n * @param defaultHeaders - Default headers to add\n */\nexport function buildProxyHeaders(\n sourceHeaders: Headers | Record<string, string>,\n defaultHeaders: Record<string, string>,\n): Headers\n{\n const headers = new Headers();\n\n // Forward important headers from source\n const headersToForward = [\n 'content-type',\n 'authorization',\n 'cookie',\n 'user-agent',\n 'accept',\n 'accept-language',\n // Forwarded so the backend proxy-guard can enforce its origin allowlist on\n // the real browser Origin; without this allowedOrigins would be a no-op.\n 'origin',\n ];\n\n for (const header of headersToForward)\n {\n const value = sourceHeaders instanceof Headers\n ? sourceHeaders.get(header)\n : sourceHeaders[header];\n\n if (value)\n {\n headers.set(header, value);\n }\n }\n\n // Add default headers\n for (const [key, value] of Object.entries(defaultHeaders))\n {\n headers.set(key, value);\n }\n\n return headers;\n}\n\n/**\n * Parse cookies from Cookie header string\n *\n * @param cookieHeader - Cookie header string (e.g., \"session=abc; theme=dark\")\n * @returns Map of cookie name-value pairs\n */\nexport function parseCookies(cookieHeader: string | null | undefined): Map<string, string>\n{\n const cookiesMap = new Map<string, string>();\n\n if (!cookieHeader)\n {\n return cookiesMap;\n }\n\n const cookiePairs = cookieHeader.split(';').map(c => c.trim());\n for (const pair of cookiePairs)\n {\n const [name, ...valueParts] = pair.split('=');\n if (name && valueParts.length > 0)\n {\n const value = valueParts.join('='); // Handle = in cookie value\n cookiesMap.set(name.trim(), value.trim());\n }\n }\n\n return cookiesMap;\n}\n\n/**\n * Parse cookies from NextRequest (Next.js specific helper)\n * Combines cookies from both NextRequest.cookies and Cookie header\n */\nexport function parseCookiesFromNextRequest(request: NextRequest): Map<string, string>\n{\n const cookiesMap = new Map<string, string>();\n\n // Add cookies from NextRequest (browser cookies)\n for (const cookie of request.cookies.getAll())\n {\n cookiesMap.set(cookie.name, cookie.value);\n }\n\n // Add cookies from Cookie header (server-side forwarded cookies)\n const cookieHeader = request.headers.get('cookie');\n if (cookieHeader)\n {\n const parsed = parseCookies(cookieHeader);\n for (const [name, value] of parsed.entries())\n {\n cookiesMap.set(name, value);\n }\n }\n\n return cookiesMap;\n}\n\n// Mapping of option names to Set-Cookie attribute formats\nconst optionMappings: Array<{\n key: keyof CookieOptions;\n format: (value: any) => string | null;\n}> = [\n { key: 'httpOnly', format: (v) => v ? 'HttpOnly' : null },\n { key: 'secure', format: (v) => v ? 'Secure' : null },\n { key: 'sameSite', format: (v) => v ? `SameSite=${v}` : null },\n { key: 'maxAge', format: (v) => v !== undefined ? `Max-Age=${v}` : null },\n { key: 'path', format: (v) => v ? `Path=${v}` : null },\n { key: 'domain', format: (v) => v ? `Domain=${v}` : null },\n];\n\n/**\n * Build Set-Cookie header string from cookie options\n */\nexport function buildSetCookieHeader(cookie: SetCookie): string\n{\n const parts = [`${cookie.name}=${cookie.value}`];\n const options = cookie.options || {};\n\n for (const { key, format } of optionMappings)\n {\n const value = options[key];\n if (value !== undefined && value !== false)\n {\n const formatted = format(value);\n if (formatted)\n {\n parts.push(formatted);\n }\n }\n }\n\n return parts.join('; ');\n}\n\n/**\n * Build error response JSON\n */\nexport function buildErrorResponse(\n errorType: string,\n message: string,\n debug: boolean,\n error?: Error,\n): any\n{\n return {\n error: errorType,\n message,\n ...(debug && error?.stack && { stack: error.stack }),\n };\n}\n\nconst headersToForward = [\n 'content-type',\n 'cache-control',\n 'set-cookie',\n 'etag',\n 'last-modified',\n];\n\n/**\n * Forward response headers back to client\n */\nexport function forwardResponseHeaders(\n sourceHeaders: Headers,\n targetHeaders: Headers,\n): void\n{\n for (const header of headersToForward)\n {\n const value = sourceHeaders.get(header);\n if (value)\n {\n targetHeaders.set(header, value);\n }\n }\n}\n\n/**\n * Collect all interceptors (auto-discovered + config)\n */\nexport function collectInterceptors(\n autoDiscoverInterceptors: boolean,\n disableAutoInterceptors: string[] | undefined,\n configInterceptors: InterceptorRule[] | undefined,\n registry: InterceptorRegistry,\n): InterceptorRule[]\n{\n const allInterceptors: InterceptorRule[] = [];\n\n // Auto-discover from registry\n if (autoDiscoverInterceptors)\n {\n const registeredInterceptors = registry.getAll(disableAutoInterceptors || []);\n allInterceptors.push(...registeredInterceptors);\n }\n\n // Add config interceptors\n if (configInterceptors)\n {\n allInterceptors.push(...configInterceptors);\n }\n\n return allInterceptors;\n}\n\n/**\n * Build RequestInterceptorContext\n */\nexport function buildRequestContext(\n path: string,\n method: string,\n headers: Headers,\n body: any,\n searchParams: URLSearchParams,\n cookiesMap: Map<string, string>,\n request: NextRequest,\n): RequestInterceptorContext\n{\n return {\n path: `/${path}`,\n method,\n headers: Object.fromEntries(headers.entries()),\n body,\n query: Object.fromEntries(searchParams.entries()),\n cookies: cookiesMap,\n request,\n metadata: {},\n };\n}\n\n/**\n * Build ResponseInterceptorContext\n */\nexport function buildResponseContext(\n path: string,\n method: string,\n requestHeaders: Headers,\n requestBody: any,\n response: Response,\n responseBody: any,\n requestMetadata: Record<string, any>,\n cookies: Map<string, string>,\n): ResponseInterceptorContext\n{\n return {\n path: `/${path}`,\n method,\n request: {\n headers: Object.fromEntries(requestHeaders.entries()),\n body: requestBody,\n },\n response: {\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n body: responseBody,\n },\n cookies,\n setCookies: [],\n metadata: requestMetadata,\n };\n}\n","/**\n * RPC-Style Proxy for define-route System\n *\n * Next.js API Route handler that resolves routeName to method/path\n * and forwards requests to SPFN backend.\n *\n * @example\n * ```typescript\n * // app/api/rpc/[routeName]/route.ts\n * import { createRpcProxy } from '@spfn/core/nextjs/server';\n * import { authRouteMap } from '@spfn/auth';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\nimport { NextRequest, NextResponse } from 'next/server';\n\nimport { env } from '@spfn/core/config';\nimport { logger } from '@spfn/core/logger';\nimport type { HttpMethod } from '@spfn/core/route';\n\nimport { signProxyRequest, parseProxyKeySet } from '../../security/proxy-signature';\nimport { buildUrlWithParams, buildQueryString } from '../shared';\nimport { interceptorRegistry } from './interceptors';\nimport { executeRequestInterceptors, executeResponseInterceptors, filterMatchingInterceptors } from './interceptors';\nimport {\n buildProxyHeaders,\n parseCookiesFromNextRequest,\n buildSetCookieHeader,\n buildErrorResponse,\n forwardResponseHeaders,\n parseResponseBody,\n collectInterceptors,\n buildRequestContext,\n buildResponseContext,\n} from './helpers';\nimport type { TypedProxyConfig } from './types';\n\nconst rpcLogger = logger.child('@spfn/core:rpc-proxy');\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Route info from generated route map\n */\nexport interface RouteMapEntry\n{\n method: HttpMethod;\n path: string;\n}\n\n/**\n * Generated route map type\n */\nexport type RouteMap = Record<string, RouteMapEntry>;\n\n/**\n * RPC proxy configuration\n */\nexport interface RpcProxyConfig extends Omit<TypedProxyConfig, 'onRequest' | 'onResponse'>\n{\n /**\n * Route map containing routeName → {method, path} mappings\n *\n * Merge generated route map with package route maps (auth, events, etc.)\n *\n * @example\n * ```typescript\n * import { authRouteMap } from '@spfn/auth';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\n routeMap: RouteMap;\n\n /**\n * Shared secret for signing proxy→backend requests (HMAC-SHA256).\n *\n * When set, every forwarded request carries an HMAC signature the backend's\n * proxy-guard middleware verifies, letting it reject direct-to-backend calls\n * that bypass this proxy. Set the SAME value here and on the backend.\n *\n * @default process.env.SPFN_PROXY_SECRET (undefined → signing disabled)\n */\n proxySecret?: string;\n}\n\n// ============================================================================\n// RPC Proxy Handler\n// ============================================================================\n\n/**\n * Create RPC proxy handler for Next.js API Route\n *\n * Handles requests in the format:\n * - GET /api/rpc/{routeName}?input={...}\n * - POST /api/rpc/{routeName} with body\n *\n * Resolves routeName to actual HTTP method and path from routeMap,\n * then forwards to SPFN backend.\n */\nexport function createRpcProxy(config: RpcProxyConfig)\n{\n const {\n apiUrl = env.SPFN_API_URL || 'http://localhost:8790',\n debug = env.NODE_ENV === 'development',\n timeout = env.RPC_PROXY_TIMEOUT,\n headers: defaultHeaders = {},\n interceptors,\n autoDiscoverInterceptors = true,\n disableAutoInterceptors,\n proxySecret = env.SPFN_PROXY_SECRET,\n routeMap,\n } = config;\n\n // Parse the active signing key once — it's fixed for the proxy's lifetime.\n // Use the SAME parser the backend uses (comma key set) and take the active key,\n // so a `v2:new,v1:old` secret is interpreted identically on both sides.\n const proxyKey = proxySecret ? (parseProxyKeySet([proxySecret])[0] ?? null) : null;\n\n // proxy-guard signs route-relative paths; if SPFN_API_URL carries a base path the\n // backend keeps (no ingress strip), the signed path won't match c.req.url and\n // strict mode 403s everything. Warn loudly so it's diagnosable.\n if (proxyKey)\n {\n try\n {\n const basePath = new URL(apiUrl).pathname;\n if (basePath !== '/' && basePath !== '')\n {\n rpcLogger.warn(\n `SPFN_API_URL has a base path (\"${basePath}\"). proxy-guard signs route-relative paths, `\n + 'so the backend must receive paths WITHOUT this prefix (e.g. ingress strip) or strict '\n + 'mode will reject all signed requests with 403.',\n );\n }\n }\n catch\n {\n // invalid apiUrl is surfaced elsewhere\n }\n }\n\n /**\n * Resolve route info from routeMap\n */\n function resolveRoute(routeName: string): { method: string; path: string } | null\n {\n const entry = routeMap[routeName];\n if (entry)\n {\n return { method: entry.method, path: entry.path };\n }\n\n return null;\n }\n\n /**\n * Handle RPC request\n */\n async function handleRpc(\n request: NextRequest,\n context: { params: Promise<{ routeName?: string }> },\n ): Promise<NextResponse>\n {\n const startTime = Date.now();\n const params = await context.params;\n\n try\n {\n const routeName = params.routeName;\n\n if (!routeName)\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Missing routeName parameter', debug),\n { status: 400 },\n );\n }\n\n // Parse input from query string (GET) or body (POST)\n let input: {\n params?: Record<string, any>;\n query?: Record<string, any>;\n body?: Record<string, any>;\n formData?: Record<string, any>;\n headers?: Record<string, any>;\n cookies?: Record<string, any>;\n } = {};\n let rawFormData: FormData | null = null;\n\n if (request.method === 'GET')\n {\n const inputParam = request.nextUrl.searchParams.get('input');\n if (inputParam)\n {\n try\n {\n input = JSON.parse(decodeURIComponent(inputParam));\n }\n catch\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid input parameter', debug),\n { status: 400 },\n );\n }\n }\n }\n else\n {\n // POST - check Content-Type for formData vs JSON\n const contentType = request.headers.get('content-type') || '';\n\n if (contentType.includes('multipart/form-data'))\n {\n // Parse multipart/form-data\n try\n {\n rawFormData = await request.formData();\n\n // Extract __metadata if present (contains params, query, etc.)\n const metadataStr = rawFormData.get('__metadata');\n if (metadataStr && typeof metadataStr === 'string')\n {\n const metadata = JSON.parse(metadataStr);\n input.params = metadata.params;\n input.query = metadata.query;\n input.headers = metadata.headers;\n input.cookies = metadata.cookies;\n }\n\n // Collect formData fields (excluding __metadata)\n input.formData = {};\n rawFormData.forEach((value, key) =>\n {\n if (key === '__metadata') return;\n\n const existing = input.formData![key];\n if (existing !== undefined)\n {\n // Multiple values with same key\n if (Array.isArray(existing))\n {\n existing.push(value);\n }\n else\n {\n input.formData![key] = [existing, value];\n }\n }\n else\n {\n input.formData![key] = value;\n }\n });\n }\n catch (error)\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid form data', debug),\n { status: 400 },\n );\n }\n }\n else\n {\n // Parse JSON body\n try\n {\n input = await request.json();\n }\n catch\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid JSON body', debug),\n { status: 400 },\n );\n }\n }\n }\n\n // Resolve route info from routeMap\n const routeInfo = resolveRoute(routeName);\n\n if (!routeInfo)\n {\n rpcLogger.warn(`Route not found: ${routeName}`);\n\n return NextResponse.json(\n buildErrorResponse('Not Found', `Route \"${routeName}\" not found`, debug),\n { status: 404 },\n );\n }\n\n const { method: targetMethod, path: targetPath } = routeInfo;\n\n // Build target URL with params and query\n const inputParams = input.params || {};\n const inputQuery = input.query || {};\n const inputBody = input.body;\n const inputFormData = input.formData;\n const hasFormData = rawFormData !== null && inputFormData && Object.keys(inputFormData).length > 0;\n\n const resolvedPath = buildUrlWithParams(targetPath, inputParams);\n const queryString = buildQueryString(inputQuery);\n const targetUrl = `${apiUrl}${resolvedPath}${queryString}`;\n\n if (debug)\n {\n rpcLogger.debug('→ RPC request', {\n routeName,\n targetMethod,\n targetPath: resolvedPath,\n targetUrl,\n hasBody: !!inputBody,\n hasFormData,\n });\n }\n\n // Build headers\n const headers = buildProxyHeaders(request.headers, defaultHeaders);\n\n // Remove Content-Type for formData (let fetch set it with boundary)\n if (hasFormData)\n {\n headers.delete('content-type');\n }\n\n // Build fetch options\n const fetchOptions: RequestInit = {\n method: targetMethod,\n headers,\n };\n\n // Add body for POST/PUT/PATCH\n if (['POST', 'PUT', 'PATCH'].includes(targetMethod))\n {\n if (hasFormData && rawFormData)\n {\n // Forward formData to backend (rebuild without __metadata)\n const forwardFormData = new FormData();\n rawFormData.forEach((value, key) =>\n {\n if (key !== '__metadata')\n {\n forwardFormData.append(key, value);\n }\n });\n fetchOptions.body = forwardFormData;\n }\n else if (inputBody)\n {\n fetchOptions.body = JSON.stringify(inputBody);\n }\n }\n\n // ============================================================\n // Advanced Interceptors - BEFORE FETCH\n // ============================================================\n\n // Collect and filter interceptors\n const allInterceptors = collectInterceptors(autoDiscoverInterceptors, disableAutoInterceptors, interceptors, interceptorRegistry);\n const matchingInterceptors = filterMatchingInterceptors(allInterceptors, resolvedPath, targetMethod);\n\n if (debug && matchingInterceptors.length > 0)\n {\n rpcLogger.debug(`Found ${matchingInterceptors.length} matching interceptors for ${targetMethod} ${resolvedPath}`);\n }\n\n // Create RequestInterceptorContext\n const cookiesMap = parseCookiesFromNextRequest(request);\n const requestCtx = buildRequestContext(\n resolvedPath.slice(1), // Remove leading slash\n targetMethod,\n headers,\n inputBody,\n new URLSearchParams(queryString.slice(1)), // Remove leading ?\n cookiesMap,\n request,\n );\n\n // Execute request interceptors\n const requestInterceptorsToRun = matchingInterceptors.map(r => r.request).filter((i): i is NonNullable<typeof i> => !!i);\n if (requestInterceptorsToRun.length > 0)\n {\n await executeRequestInterceptors(requestCtx, requestInterceptorsToRun);\n\n // Apply modified headers\n for (const [key, value] of Object.entries(requestCtx.headers))\n {\n headers.set(key, value);\n }\n\n // Apply modified body\n if (requestCtx.body)\n {\n fetchOptions.body = JSON.stringify(requestCtx.body);\n }\n }\n\n // ============================================================\n // Proxy → Backend signature (proxy-guard)\n // ============================================================\n // Sign the FINAL request (after interceptors settled body/headers) so\n // the backend can prove it came through this trusted proxy. JSON body\n // is hashed into the signature; large multipart uploads are excluded.\n if (proxyKey)\n {\n const signedBody = typeof fetchOptions.body === 'string' ? fetchOptions.body : undefined;\n // Sign the route-relative path (no SPFN_API_URL base prefix) so it\n // matches the backend's `new URL(c.req.url).pathname` whether the\n // prefix is absent or stripped by an ingress. resolvedPath is already\n // the percent-encoded wire form; queryString is the raw search.\n const signatureHeaders = signProxyRequest({\n key: proxyKey,\n method: targetMethod,\n path: resolvedPath,\n query: queryString,\n body: signedBody,\n });\n\n for (const [headerName, value] of Object.entries(signatureHeaders))\n {\n headers.set(headerName, value);\n }\n }\n\n // Execute fetch with timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try\n {\n const response = await fetch(targetUrl, {\n ...fetchOptions,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n let body = await parseResponseBody(response);\n\n // ============================================================\n // Advanced Interceptors - AFTER FETCH\n // ============================================================\n\n // Create ResponseInterceptorContext\n const responseCtx = buildResponseContext(\n resolvedPath.slice(1),\n targetMethod,\n headers,\n inputBody,\n response,\n body,\n requestCtx.metadata,\n requestCtx.cookies,\n );\n\n // Execute response interceptors\n const responseInterceptorsToRun = matchingInterceptors.map(r => r.response).filter((i): i is NonNullable<typeof i> => !!i);\n if (responseInterceptorsToRun.length > 0)\n {\n await executeResponseInterceptors(responseCtx, responseInterceptorsToRun);\n body = responseCtx.response.body;\n }\n\n const duration = Date.now() - startTime;\n\n if (debug)\n {\n rpcLogger.debug('← RPC response', {\n routeName,\n status: responseCtx.response.status,\n duration: `${duration}ms`,\n });\n }\n\n // Build Next.js response\n // 204 No Content should use NextResponse directly, not NextResponse.json()\n const nextResponse = responseCtx.response.status === 204\n ? new NextResponse(null, {\n status: 204,\n statusText: responseCtx.response.statusText,\n })\n : NextResponse.json(body, {\n status: responseCtx.response.status,\n statusText: responseCtx.response.statusText,\n });\n\n // Forward response headers\n forwardResponseHeaders(response.headers, nextResponse.headers);\n\n // Apply setCookies from interceptors\n for (const cookie of responseCtx.setCookies)\n {\n const setCookieHeader = buildSetCookieHeader(cookie);\n nextResponse.headers.append('Set-Cookie', setCookieHeader);\n\n if (debug)\n {\n rpcLogger.debug('Set-Cookie header added', {\n name: cookie.name,\n });\n }\n }\n\n return nextResponse;\n }\n catch (error)\n {\n clearTimeout(timeoutId);\n\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError')\n {\n rpcLogger.error('Request timeout', {\n routeName,\n targetUrl,\n timeout,\n });\n\n return NextResponse.json(\n buildErrorResponse('Gateway Timeout', `Request timed out after ${timeout}ms`, debug, error),\n { status: 504 },\n );\n }\n\n // Handle other fetch errors\n const fetchErr = error as Error;\n rpcLogger.error('Fetch error', {\n routeName,\n targetUrl,\n error: fetchErr.message,\n });\n\n return NextResponse.json(\n buildErrorResponse('Bad Gateway', fetchErr.message || 'Failed to connect to backend', debug, fetchErr),\n { status: 502 },\n );\n }\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n const err = error as Error;\n\n rpcLogger.error('RPC proxy error', {\n error: err.message,\n stack: err.stack,\n duration: `${duration}ms`,\n });\n\n return NextResponse.json(\n buildErrorResponse('Internal Server Error', err.message || 'Unknown error', debug, err),\n { status: 500 },\n );\n }\n }\n\n // Return route handlers\n return {\n GET: (req: NextRequest, context: { params: Promise<{ routeName?: string }> }) =>\n handleRpc(req, context),\n POST: (req: NextRequest, context: { params: Promise<{ routeName?: string }> }) =>\n handleRpc(req, context),\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/security/proxy-signature.ts","../../src/nextjs/shared.ts","../../src/nextjs/proxy/interceptors/helpers.ts","../../src/nextjs/proxy/interceptors/registry.ts","../../src/nextjs/proxy/helpers.ts","../../src/nextjs/proxy/rpc.ts"],"names":["headersToForward"],"mappings":"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","file":"server.js","sourcesContent":["/**\n * Proxy → Backend request signing (HMAC-SHA256) with key rotation\n *\n * Shared between the Next.js RPC proxy (which signs outbound requests) and the\n * backend proxy-guard middleware (which verifies them). Keeping the canonical\n * string and HMAC logic in one pure module guarantees both sides agree.\n *\n * The signature proves a request passed through a trusted proxy that holds the\n * shared secret. It does NOT authenticate the end user — that is the JWT layer's\n * job — and it cannot stop a client from calling the proxy itself. Its role is to\n * stop direct-to-backend calls that bypass the proxy.\n *\n * Keys are identified by a short keyId so secrets can be rotated without downtime:\n * the proxy signs with the active key and stamps its keyId in a header; the\n * backend keeps a set of accepted keys (current + previous) and selects by keyId.\n * Roll a new key in, let both sides pick it up, switch the proxy to it, then drop\n * the old one after a grace period — no request is ever rejected mid-rotation.\n *\n * @module security/proxy-signature\n */\nimport { createHmac, randomBytes, timingSafeEqual, createHash } from 'node:crypto';\n\n// ============================================================================\n// Header names\n// ============================================================================\n\nexport const PROXY_SIGNATURE_HEADER = 'x-spfn-proxy-signature';\nexport const PROXY_TIMESTAMP_HEADER = 'x-spfn-proxy-timestamp';\nexport const PROXY_NONCE_HEADER = 'x-spfn-proxy-nonce';\nexport const PROXY_KEY_ID_HEADER = 'x-spfn-proxy-key-id';\n/**\n * The real client IP, forwarded by the proxy. The backend trusts this only when\n * the request is proxy-verified (proxy-guard `clientType` ≠ untrusted) — the proxy\n * itself sees no spoofable hop, so a verified request's value is the true client.\n */\nexport const PROXY_CLIENT_IP_HEADER = 'x-spfn-proxy-client-ip';\n\n/** keyId used when a raw secret carries no `keyId:` prefix (back-compat). */\nexport const DEFAULT_KEY_ID = 'default';\n\n// ============================================================================\n// Keys\n// ============================================================================\n\n/**\n * A signing key: a short identifier plus its secret. Encoded in env as\n * `<keyId>:<secret>` (e.g. `v2:9f3c...`); a bare secret gets `DEFAULT_KEY_ID`.\n */\nexport interface ProxyKey\n{\n keyId: string;\n secret: string;\n}\n\n/**\n * Parse one `<keyId>:<secret>` (or bare `<secret>`) string into a ProxyKey.\n * keyId is everything before the FIRST colon; the secret keeps any later colons.\n */\nexport function parseProxyKey(raw: string): ProxyKey\n{\n const idx = raw.indexOf(':');\n if (idx === -1)\n {\n return { keyId: DEFAULT_KEY_ID, secret: raw };\n }\n\n // A leading colon (':secret') yields an empty keyId; the backend treats an empty\n // keyId header as \"missing\" and rejects, so normalize it to DEFAULT_KEY_ID.\n return { keyId: raw.slice(0, idx) || DEFAULT_KEY_ID, secret: raw.slice(idx + 1) };\n}\n\n/**\n * Build the backend's accepted key set from raw env values. Each input may be a\n * single key or a comma-separated list (for multiple grace keys). Earlier inputs\n * win on keyId collision, so pass the active secret first.\n */\nexport function parseProxyKeySet(raws: Array<string | undefined | null>): ProxyKey[]\n{\n const keys: ProxyKey[] = [];\n const seen = new Set<string>();\n\n for (const raw of raws)\n {\n if (!raw)\n {\n continue;\n }\n\n for (const part of raw.split(','))\n {\n const trimmed = part.trim();\n if (!trimmed)\n {\n continue;\n }\n\n const key = parseProxyKey(trimmed);\n if (!key.secret || seen.has(key.keyId))\n {\n continue;\n }\n\n seen.add(key.keyId);\n keys.push(key);\n }\n }\n\n return keys;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * The parts of a request that are bound into the signature.\n */\nexport interface SignatureParts\n{\n /** Resolved backend HTTP method (GET/POST/PUT/...) */\n method: string;\n\n /**\n * Raw (wire) backend path, percent-encoding preserved, leading slash, NO query.\n * Proxy signs its outbound request-target path; the backend reconstructs the\n * SAME bytes from `new URL(c.req.url).pathname` (NOT the decoded `c.req.path`).\n */\n path: string;\n\n /**\n * Raw (wire) query string including leading `?`, or '' when there is none.\n * Both sides use the verbatim search string so query params are authenticated.\n */\n query: string;\n\n /** Unix epoch milliseconds, as a string */\n timestamp: string;\n\n /** Random per-request nonce */\n nonce: string;\n\n /**\n * SHA-256 hex of the raw request body, or empty string when there is no body\n * (GET/HEAD, or multipart uploads which are excluded by design). Bound for ANY\n * non-multipart content-type, not just application/json.\n *\n * Multipart is excluded so the proxy can stream large uploads without buffering\n * them to hash. The signature therefore proves *provenance* (came through the\n * trusted proxy) and binds method/path/query/timestamp for a multipart request,\n * but NOT the body bytes. Two consequences, by design:\n * - Multipart *content* integrity/validation is the application's job (size,\n * type, checksum, business rules) — not the transport signature's.\n * - If you don't trust the proxy→backend hop itself, secure it with TLS/mTLS;\n * signing a prefix of the stream would be a half-measure (the tail stays\n * unprotected) and is intentionally not done.\n */\n bodyHash: string;\n}\n\n/**\n * Headers produced by signing — ready to set on the outbound fetch.\n */\nexport interface SignatureHeaders\n{\n [PROXY_SIGNATURE_HEADER]: string;\n [PROXY_TIMESTAMP_HEADER]: string;\n [PROXY_NONCE_HEADER]: string;\n [PROXY_KEY_ID_HEADER]: string;\n}\n\n// ============================================================================\n// Canonical string + HMAC\n// ============================================================================\n\n/**\n * Build the canonical string that gets signed. Field order is fixed and each\n * field is newline-separated, so a captured signature cannot be replayed against\n * a different method, path, or body.\n */\nexport function buildCanonicalString(parts: SignatureParts): string\n{\n return [parts.method.toUpperCase(), parts.path, parts.query, parts.timestamp, parts.nonce, parts.bodyHash].join('\\n');\n}\n\n/**\n * Hash a request body. Accepts a string (proxy side, from JSON.stringify) or a\n * Buffer (backend side, straight from the stream — avoids a bytes→string→bytes\n * round-trip). Returns '' for empty/no body. A utf8 string and its Buffer hash\n * identically, so both sides agree.\n */\nexport function hashBody(body: string | Buffer | undefined | null): string\n{\n if (!body || body.length === 0)\n {\n return '';\n }\n\n return typeof body === 'string'\n ? createHash('sha256').update(body, 'utf8').digest('hex')\n : createHash('sha256').update(body).digest('hex');\n}\n\n/**\n * Compute the HMAC-SHA256 signature (hex) for the given parts.\n */\nexport function computeSignature(secret: string, parts: SignatureParts): string\n{\n return createHmac('sha256', secret).update(buildCanonicalString(parts)).digest('hex');\n}\n\n/**\n * Generate a fresh random nonce (hex).\n */\nexport function generateNonce(): string\n{\n return randomBytes(16).toString('hex');\n}\n\n// ============================================================================\n// Sign (proxy side)\n// ============================================================================\n\nexport interface SignInput\n{\n /** The active signing key. */\n key: ProxyKey;\n method: string;\n /** Raw (wire) path, leading slash, percent-encoding preserved, NO query. */\n path: string;\n /** Raw (wire) query string including leading `?`, or '' / omitted for none. */\n query?: string;\n /** Raw request body string, if any. Omit for GET / multipart uploads. */\n body?: string | null;\n /** Override the timestamp (testing). Defaults to Date.now(). */\n timestamp?: string;\n /** Override the nonce (testing). Defaults to a random nonce. */\n nonce?: string;\n}\n\n/**\n * Sign an outbound proxy request and return the headers to attach (including the\n * keyId so the backend knows which key to verify against).\n */\nexport function signProxyRequest(input: SignInput): SignatureHeaders\n{\n const timestamp = input.timestamp ?? String(Date.now());\n const nonce = input.nonce ?? generateNonce();\n const signature = computeSignature(input.key.secret, {\n method: input.method,\n path: input.path,\n query: input.query ?? '',\n timestamp,\n nonce,\n bodyHash: hashBody(input.body),\n });\n\n return {\n [PROXY_SIGNATURE_HEADER]: signature,\n [PROXY_TIMESTAMP_HEADER]: timestamp,\n [PROXY_NONCE_HEADER]: nonce,\n [PROXY_KEY_ID_HEADER]: input.key.keyId,\n };\n}\n\n// ============================================================================\n// Verify (backend side)\n// ============================================================================\n\nexport type VerifyFailureReason =\n | 'missing-headers'\n | 'stale-timestamp'\n | 'bad-timestamp'\n | 'unknown-key'\n | 'signature-mismatch';\n\n// ('body-read-error' / 'origin-not-allowed' / 'nonce-replay' are guard-level\n// reasons carried separately by the middleware, not produced by verify itself.)\n\nexport interface VerifyResult\n{\n valid: boolean;\n reason?: VerifyFailureReason;\n /** The verified nonce, for optional replay tracking by the caller. */\n nonce?: string;\n /** The keyId the request was verified against (for logging/metrics). */\n keyId?: string;\n}\n\nexport interface VerifyInput\n{\n /** Accepted keys (active + grace). Selected by the request's keyId header. */\n keys: ProxyKey[];\n method: string;\n /** Raw (wire) path from `new URL(c.req.url).pathname` — NOT decoded `c.req.path`. */\n path: string;\n /** Raw (wire) query string from `new URL(c.req.url).search`, or '' for none. */\n query?: string;\n /** Raw request body (string or Buffer) as received, if any. */\n body?: string | Buffer | null;\n signature: string | null | undefined;\n timestamp: string | null | undefined;\n nonce: string | null | undefined;\n keyId: string | null | undefined;\n /** Allowed clock skew / replay window in ms. Default 30s. */\n windowMs?: number;\n /** Current time (testing). Defaults to Date.now(). */\n now?: number;\n}\n\n/**\n * Constant-time hex string comparison. Returns false on any length diff.\n *\n * Buffer.from(_, 'hex') does NOT throw on invalid hex — it silently truncates at\n * the first bad char — so a malformed signature yields a shorter buffer that the\n * length check rejects. No try/catch needed.\n */\nfunction safeEqualHex(a: string, b: string): boolean\n{\n const bufA = Buffer.from(a, 'hex');\n const bufB = Buffer.from(b, 'hex');\n\n if (bufA.length === 0 || bufA.length !== bufB.length)\n {\n return false;\n }\n\n return timingSafeEqual(bufA, bufB);\n}\n\n/**\n * Verify an inbound request's proxy signature against the accepted key set.\n *\n * Checks, in order: headers present, timestamp within window (replay guard),\n * keyId known, and HMAC match. Nonce-level replay rejection is left to the caller\n * (optional, Redis-backed) since not every deployment has a shared store.\n */\nexport function verifyProxyRequest(input: VerifyInput): VerifyResult\n{\n const { signature, timestamp, nonce, keyId } = input;\n\n if (!signature || !timestamp || !nonce || !keyId)\n {\n return { valid: false, reason: 'missing-headers' };\n }\n\n const ts = Number(timestamp);\n if (!Number.isFinite(ts))\n {\n return { valid: false, reason: 'bad-timestamp' };\n }\n\n const now = input.now ?? Date.now();\n const windowMs = input.windowMs ?? 30_000;\n if (Math.abs(now - ts) > windowMs)\n {\n return { valid: false, reason: 'stale-timestamp' };\n }\n\n const key = input.keys.find(k => k.keyId === keyId);\n if (!key)\n {\n return { valid: false, reason: 'unknown-key' };\n }\n\n const expected = computeSignature(key.secret, {\n method: input.method,\n path: input.path,\n query: input.query ?? '',\n timestamp,\n nonce,\n bodyHash: hashBody(input.body),\n });\n\n if (!safeEqualHex(signature, expected))\n {\n return { valid: false, reason: 'signature-mismatch' };\n }\n\n return { valid: true, nonce, keyId };\n}\n","/**\n * Shared utilities for Next.js client and proxy modules\n *\n * Contains common functions used by both client and proxy to avoid code duplication.\n */\n\n/**\n * Build URL with path parameters replaced\n *\n * @example\n * buildUrlWithParams('/users/:id/posts/:postId', { id: '123', postId: '456' })\n * // Returns: '/users/123/posts/456'\n */\nexport function buildUrlWithParams(path: string, params: Record<string, any>): string\n{\n let url = path;\n for (const [key, value] of Object.entries(params))\n {\n url = url.replace(`:${key}`, encodeURIComponent(String(value)));\n }\n\n return url;\n}\n\n/**\n * Build query string from object\n *\n * @example\n * buildQueryString({ page: '1', limit: '10', tags: ['foo', 'bar'] })\n * // Returns: '?page=1&limit=10&tags=foo&tags=bar'\n */\nexport function buildQueryString(query: Record<string, any>): string\n{\n if (Object.keys(query).length === 0)\n {\n return '';\n }\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query))\n {\n if (Array.isArray(value))\n {\n value.forEach((v) => searchParams.append(key, String(v)));\n }\n else\n {\n searchParams.append(key, String(value));\n }\n }\n\n return `?${searchParams.toString()}`;\n}\n\n/**\n * Build Cookie header string from cookies object\n *\n * @example\n * buildCookieHeader({ session: 'abc123', theme: 'dark' })\n * // Returns: 'session=abc123; theme=dark'\n */\nexport function buildCookieHeader(cookies: Record<string, string>): string\n{\n return Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join('; ');\n}\n\n/**\n * Parse response body based on content type\n *\n * Handles:\n * - 204 No Content: returns null (no body expected)\n * - application/json: parses JSON body\n * - Other content types: returns raw text\n */\nexport async function parseResponseBody(response: Response): Promise<any>\n{\n // 204 No Content has no body\n if (response.status === 204)\n {\n return null;\n }\n\n const contentType = response.headers.get('content-type');\n\n if (contentType?.includes('application/json'))\n {\n const text = await response.text();\n\n return text ? JSON.parse(text) : null;\n }\n else\n {\n return await response.text();\n }\n}\n","/**\n * SPFN Next.js Proxy Interceptor Execution Engine\n */\n\nimport type {\n InterceptorRule,\n RequestInterceptor,\n ResponseInterceptor,\n RequestInterceptorContext,\n ResponseInterceptorContext,\n} from './types';\n\n/**\n * Check if path matches pattern\n *\n * Supports:\n * - Wildcards: '/_auth/*' matches '/_auth/login'\n * - Path params: '/users/:id' matches '/users/123'\n * - RegExp: /^\\/_auth\\/.+$/ matches '/_auth/login'\n * - Exact match: '/_auth/login' matches '/_auth/login'\n * - All: '*' matches any path\n *\n * @param path - Request path to test\n * @param pattern - Pattern to match against\n * @returns True if path matches pattern\n */\nexport function matchPath(path: string, pattern: string | RegExp): boolean\n{\n // Match all\n if (pattern === '*')\n {\n return true;\n }\n\n // RegExp pattern\n if (pattern instanceof RegExp)\n {\n return pattern.test(path);\n }\n\n // String pattern\n // Convert wildcard pattern to RegExp\n // '/_auth/*' -> /^\\/_auth\\/.*/\n // '/users/:id' -> /^\\/users\\/[^/]+$/\n const regexPattern = pattern\n .replace(/\\*/g, '.*')\n .replace(/:[^/]+/g, '[^/]+')\n .replace(/\\//g, '\\\\/');\n\n const regex = new RegExp(`^${regexPattern}$`);\n\n return regex.test(path);\n}\n\n/**\n * Check if method matches pattern\n *\n * @param method - Request method (e.g., 'POST')\n * @param pattern - Method pattern (e.g., 'POST' or ['POST', 'PUT'])\n * @returns True if method matches pattern\n */\nexport function matchMethod(\n method: string,\n pattern?: string | string[],\n): boolean\n{\n // No method filter = match all\n if (!pattern)\n {\n return true;\n }\n\n // Single method\n if (typeof pattern === 'string')\n {\n return method.toUpperCase() === pattern.toUpperCase();\n }\n\n // Multiple methods\n return pattern.some((m) => m.toUpperCase() === method.toUpperCase());\n}\n\n/**\n * Filter interceptors that match the request\n *\n * @param rules - All interceptor rules\n * @param path - Request path\n * @param method - Request method\n * @returns Matched interceptors\n */\nexport function filterMatchingInterceptors(\n rules: InterceptorRule[],\n path: string,\n method: string,\n): InterceptorRule[]\n{\n return rules.filter((rule) => \n {\n return matchPath(path, rule.pathPattern) && matchMethod(method, rule.method);\n });\n}\n\n/**\n * Execute request interceptors in chain\n *\n * Interceptors are executed in order:\n * 1. First registered interceptor\n * 2. Second registered interceptor\n * 3. ... and so on\n *\n * Each interceptor must call next() to continue the chain.\n * If next() is not called, the chain stops and remaining interceptors are skipped.\n *\n * @param context - Request interceptor context\n * @param interceptors - Interceptors to execute\n */\nexport async function executeRequestInterceptors(\n context: RequestInterceptorContext,\n interceptors: RequestInterceptor[],\n): Promise<void>\n{\n let index = 0;\n\n const next = async (): Promise<void> => \n {\n if (index >= interceptors.length)\n {\n return;\n }\n\n const interceptor = interceptors[index];\n index++;\n\n await interceptor(context, next);\n };\n\n await next();\n}\n\n/**\n * Execute response interceptors in chain\n *\n * Interceptors are executed in order:\n * 1. First registered interceptor\n * 2. Second registered interceptor\n * 3. ... and so on\n *\n * Each interceptor must call next() to continue the chain.\n * If next() is not called, the chain stops and remaining interceptors are skipped.\n *\n * @param context - Response interceptor context\n * @param interceptors - Interceptors to execute\n */\nexport async function executeResponseInterceptors(\n context: ResponseInterceptorContext,\n interceptors: ResponseInterceptor[],\n): Promise<void>\n{\n let index = 0;\n\n const next = async (): Promise<void> => \n {\n if (index >= interceptors.length)\n {\n return;\n }\n\n const interceptor = interceptors[index];\n index++;\n\n await interceptor(context, next);\n };\n\n await next();\n}\n","/**\n * Global Interceptor Registry\n *\n * Allows packages to automatically register their interceptors\n * for Next.js proxy without manual configuration.\n *\n * Uses globalThis for persistence across module reloads (HMR).\n */\nimport type { InterceptorRule } from './types';\n\n// ============================================================================\n// Global Type Declarations\n// ============================================================================\n\n/**\n * Extend globalThis with interceptor registry\n *\n * Using globalThis allows the registry to persist across module reloads (HMR).\n * preventing duplicate registrations during development with HMR.\n */\ndeclare global\n{\n var __SPFN_INTERCEPTOR_REGISTRY__: InterceptorRegistry | undefined;\n}\n\n/**\n * Global interceptor registry\n *\n * Packages register their interceptors on import,\n * and proxy automatically discovers and applies them.\n */\nexport class InterceptorRegistry\n{\n private interceptors = new Map<string, InterceptorRule[]>();\n\n /**\n * Register interceptors for a package\n *\n * @param packageName - Unique package identifier (e.g., 'auth', 'storage')\n * @param interceptors - Array of interceptor rules\n *\n * @example\n * ```typescript\n * registerInterceptors('auth', [\n * {\n * pathPattern: '/_auth/*',\n * request: async (ctx, next) => { ... }\n * }\n * ]);\n * ```\n */\n register(packageName: string, interceptors: InterceptorRule[]): void\n {\n if (!this.interceptors.has(packageName))\n {\n this.interceptors.set(packageName, interceptors);\n }\n }\n\n /**\n * Get all registered interceptors\n *\n * @param exclude - Package names to exclude\n * @returns Flat array of all interceptor rules\n */\n getAll(exclude: string[] = []): InterceptorRule[]\n {\n const all: InterceptorRule[] = [];\n\n for (const [packageName, interceptors] of this.interceptors.entries())\n {\n if (!exclude.includes(packageName))\n {\n all.push(...interceptors);\n }\n }\n\n return all;\n }\n\n /**\n * Get interceptors for specific package\n *\n * @param packageName - Package identifier\n * @returns Interceptor rules or undefined\n */\n get(packageName: string): InterceptorRule[] | undefined\n {\n return this.interceptors.get(packageName);\n }\n\n /**\n * Get list of registered package names\n */\n getPackageNames(): string[]\n {\n return Array.from(this.interceptors.keys());\n }\n\n /**\n * Check if package has registered interceptors\n */\n has(packageName: string): boolean\n {\n return this.interceptors.has(packageName);\n }\n\n /**\n * Unregister interceptors for a package\n *\n * @param packageName - Package identifier\n */\n unregister(packageName: string): void\n {\n this.interceptors.delete(packageName);\n }\n\n /**\n * Clear all registered interceptors\n *\n * Useful for testing\n */\n clear(): void\n {\n this.interceptors.clear();\n }\n\n /**\n * Get total count of registered interceptors\n */\n count(): number\n {\n let total = 0;\n for (const interceptors of this.interceptors.values())\n {\n total += interceptors.length;\n }\n\n return total;\n }\n}\n\n/**\n * Global singleton registry instance\n *\n * Uses globalThis to persist across module reloads (HMR).\n * This prevents duplicate registrations during development.\n */\nexport const interceptorRegistry = (() =>\n{\n if (!globalThis.__SPFN_INTERCEPTOR_REGISTRY__)\n {\n globalThis.__SPFN_INTERCEPTOR_REGISTRY__ = new InterceptorRegistry();\n }\n\n return globalThis.__SPFN_INTERCEPTOR_REGISTRY__;\n})();\n\n/**\n * Register interceptors for a package\n *\n * This should be called during package initialization (on import).\n * The interceptors will be automatically applied by the Next.js proxy.\n *\n * @param packageName - Unique package identifier (e.g., 'auth', 'storage')\n * @param interceptors - Array of interceptor rules\n *\n * @example\n * ```typescript\n * // packages/auth/src/adapters/nextjs/interceptors/index.ts\n * import { registerInterceptors } from '@spfn/core/nextjs';\n *\n * const authInterceptors = [\n * {\n * pathPattern: '/_auth/*',\n * request: async (ctx, next) => {\n * // Add JWT token\n * ctx.headers['Authorization'] = 'Bearer token';\n * await next();\n * }\n * }\n * ];\n *\n * // Auto-register on import\n * registerInterceptors('auth', authInterceptors);\n * ```\n */\nexport function registerInterceptors(\n packageName: string,\n interceptors: InterceptorRule[],\n): void\n{\n interceptorRegistry.register(packageName, interceptors);\n}\n","/**\n * Helper functions for proxy handler\n * Separates utility logic from main proxy handler for better maintainability\n */\nimport { NextRequest } from 'next/server';\nimport type { CookieOptions, SetCookie } from '../client';\nimport type { InterceptorRule, RequestInterceptorContext, ResponseInterceptorContext } from './interceptors/types';\nimport type { InterceptorRegistry } from './interceptors';\nimport { PROXY_CLIENT_IP_HEADER } from '../../security/proxy-signature';\n\n// Re-export from shared\nexport { parseResponseBody } from '../shared';\n\n/**\n * Resolve the real client IP from a forwarded chain.\n *\n * `X-Forwarded-For` is `client, proxy1, proxy2, …` — each trusted hop appends the\n * address it received the connection from, so the rightmost `trustedHops` entries\n * are added by our own infra and can't be spoofed. The client as the outermost\n * trusted hop saw it is `parts[len - trustedHops]`. A client that prepends a fake\n * value just lengthens the chain to the left and is ignored.\n *\n * @param xff - raw X-Forwarded-For header value\n * @param trustedHops - number of trusted proxies in front (e.g. LB + nginx = 2)\n */\nexport function clientIpFromForwardedChain(\n xff: string | null | undefined,\n trustedHops: number,\n): string | undefined\n{\n if (!xff) return undefined;\n\n const parts = xff.split(',').map(s => s.trim()).filter(Boolean);\n if (parts.length === 0) return undefined;\n\n const idx = parts.length - Math.max(1, trustedHops);\n\n // Fewer entries than configured hops (misconfig / fewer real hops): best-effort\n // to the leftmost rather than returning nothing.\n return parts[idx] ?? parts[0];\n}\n\nfunction trustedProxyHops(): number\n{\n const raw = Number.parseInt(process.env.TRUSTED_PROXY_HOPS ?? '', 10);\n\n return Number.isFinite(raw) && raw > 0 ? raw : 1;\n}\n\n/**\n * Build request headers for proxying\n * Forwards important headers from source and adds default headers\n *\n * @param sourceHeaders - Source headers (can be Headers object or Record)\n * @param defaultHeaders - Default headers to add\n */\nexport function buildProxyHeaders(\n sourceHeaders: Headers | Record<string, string>,\n defaultHeaders: Record<string, string>,\n): Headers\n{\n const headers = new Headers();\n\n // Forward important headers from source\n const headersToForward = [\n 'content-type',\n 'authorization',\n 'cookie',\n 'user-agent',\n 'accept',\n 'accept-language',\n // Forwarded so the backend proxy-guard can enforce its origin allowlist on\n // the real browser Origin; without this allowedOrigins would be a no-op.\n 'origin',\n ];\n\n for (const header of headersToForward)\n {\n const value = sourceHeaders instanceof Headers\n ? sourceHeaders.get(header)\n : sourceHeaders[header];\n\n if (value)\n {\n headers.set(header, value);\n }\n }\n\n // Forward the real client IP so the backend can rate-limit per user. The\n // backend trusts this header only on proxy-verified requests, so a direct\n // caller can't spoof it. Resolved hop-aware from the inbound X-Forwarded-For.\n const xff = sourceHeaders instanceof Headers\n ? sourceHeaders.get('x-forwarded-for')\n : sourceHeaders['x-forwarded-for'];\n const clientIp = clientIpFromForwardedChain(xff, trustedProxyHops());\n if (clientIp)\n {\n headers.set(PROXY_CLIENT_IP_HEADER, clientIp);\n }\n\n // Add default headers\n for (const [key, value] of Object.entries(defaultHeaders))\n {\n headers.set(key, value);\n }\n\n return headers;\n}\n\n/**\n * Parse cookies from Cookie header string\n *\n * @param cookieHeader - Cookie header string (e.g., \"session=abc; theme=dark\")\n * @returns Map of cookie name-value pairs\n */\nexport function parseCookies(cookieHeader: string | null | undefined): Map<string, string>\n{\n const cookiesMap = new Map<string, string>();\n\n if (!cookieHeader)\n {\n return cookiesMap;\n }\n\n const cookiePairs = cookieHeader.split(';').map(c => c.trim());\n for (const pair of cookiePairs)\n {\n const [name, ...valueParts] = pair.split('=');\n if (name && valueParts.length > 0)\n {\n const value = valueParts.join('='); // Handle = in cookie value\n cookiesMap.set(name.trim(), value.trim());\n }\n }\n\n return cookiesMap;\n}\n\n/**\n * Parse cookies from NextRequest (Next.js specific helper)\n * Combines cookies from both NextRequest.cookies and Cookie header\n */\nexport function parseCookiesFromNextRequest(request: NextRequest): Map<string, string>\n{\n const cookiesMap = new Map<string, string>();\n\n // Add cookies from NextRequest (browser cookies)\n for (const cookie of request.cookies.getAll())\n {\n cookiesMap.set(cookie.name, cookie.value);\n }\n\n // Add cookies from Cookie header (server-side forwarded cookies)\n const cookieHeader = request.headers.get('cookie');\n if (cookieHeader)\n {\n const parsed = parseCookies(cookieHeader);\n for (const [name, value] of parsed.entries())\n {\n cookiesMap.set(name, value);\n }\n }\n\n return cookiesMap;\n}\n\n// Mapping of option names to Set-Cookie attribute formats\nconst optionMappings: Array<{\n key: keyof CookieOptions;\n format: (value: any) => string | null;\n}> = [\n { key: 'httpOnly', format: (v) => v ? 'HttpOnly' : null },\n { key: 'secure', format: (v) => v ? 'Secure' : null },\n { key: 'sameSite', format: (v) => v ? `SameSite=${v}` : null },\n { key: 'maxAge', format: (v) => v !== undefined ? `Max-Age=${v}` : null },\n { key: 'path', format: (v) => v ? `Path=${v}` : null },\n { key: 'domain', format: (v) => v ? `Domain=${v}` : null },\n];\n\n/**\n * Build Set-Cookie header string from cookie options\n */\nexport function buildSetCookieHeader(cookie: SetCookie): string\n{\n const parts = [`${cookie.name}=${cookie.value}`];\n const options = cookie.options || {};\n\n for (const { key, format } of optionMappings)\n {\n const value = options[key];\n if (value !== undefined && value !== false)\n {\n const formatted = format(value);\n if (formatted)\n {\n parts.push(formatted);\n }\n }\n }\n\n return parts.join('; ');\n}\n\n/**\n * Build error response JSON\n */\nexport function buildErrorResponse(\n errorType: string,\n message: string,\n debug: boolean,\n error?: Error,\n): any\n{\n return {\n error: errorType,\n message,\n ...(debug && error?.stack && { stack: error.stack }),\n };\n}\n\nconst headersToForward = [\n 'content-type',\n 'cache-control',\n 'set-cookie',\n 'etag',\n 'last-modified',\n];\n\n/**\n * Forward response headers back to client\n */\nexport function forwardResponseHeaders(\n sourceHeaders: Headers,\n targetHeaders: Headers,\n): void\n{\n for (const header of headersToForward)\n {\n const value = sourceHeaders.get(header);\n if (value)\n {\n targetHeaders.set(header, value);\n }\n }\n}\n\n/**\n * Collect all interceptors (auto-discovered + config)\n */\nexport function collectInterceptors(\n autoDiscoverInterceptors: boolean,\n disableAutoInterceptors: string[] | undefined,\n configInterceptors: InterceptorRule[] | undefined,\n registry: InterceptorRegistry,\n): InterceptorRule[]\n{\n const allInterceptors: InterceptorRule[] = [];\n\n // Auto-discover from registry\n if (autoDiscoverInterceptors)\n {\n const registeredInterceptors = registry.getAll(disableAutoInterceptors || []);\n allInterceptors.push(...registeredInterceptors);\n }\n\n // Add config interceptors\n if (configInterceptors)\n {\n allInterceptors.push(...configInterceptors);\n }\n\n return allInterceptors;\n}\n\n/**\n * Build RequestInterceptorContext\n */\nexport function buildRequestContext(\n path: string,\n method: string,\n headers: Headers,\n body: any,\n searchParams: URLSearchParams,\n cookiesMap: Map<string, string>,\n request: NextRequest,\n): RequestInterceptorContext\n{\n return {\n path: `/${path}`,\n method,\n headers: Object.fromEntries(headers.entries()),\n body,\n query: Object.fromEntries(searchParams.entries()),\n cookies: cookiesMap,\n request,\n metadata: {},\n };\n}\n\n/**\n * Build ResponseInterceptorContext\n */\nexport function buildResponseContext(\n path: string,\n method: string,\n requestHeaders: Headers,\n requestBody: any,\n response: Response,\n responseBody: any,\n requestMetadata: Record<string, any>,\n cookies: Map<string, string>,\n): ResponseInterceptorContext\n{\n return {\n path: `/${path}`,\n method,\n request: {\n headers: Object.fromEntries(requestHeaders.entries()),\n body: requestBody,\n },\n response: {\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n body: responseBody,\n },\n cookies,\n setCookies: [],\n metadata: requestMetadata,\n };\n}\n","/**\n * RPC-Style Proxy for define-route System\n *\n * Next.js API Route handler that resolves routeName to method/path\n * and forwards requests to SPFN backend.\n *\n * @example\n * ```typescript\n * // app/api/rpc/[routeName]/route.ts\n * import { createRpcProxy } from '@spfn/core/nextjs/server';\n * import { authRouteMap } from '@spfn/auth';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\nimport { NextRequest, NextResponse } from 'next/server';\n\nimport { env } from '@spfn/core/config';\nimport { logger } from '@spfn/core/logger';\nimport type { HttpMethod } from '@spfn/core/route';\n\nimport { signProxyRequest, parseProxyKeySet } from '../../security/proxy-signature';\nimport { buildUrlWithParams, buildQueryString } from '../shared';\nimport { interceptorRegistry } from './interceptors';\nimport { executeRequestInterceptors, executeResponseInterceptors, filterMatchingInterceptors } from './interceptors';\nimport {\n buildProxyHeaders,\n parseCookiesFromNextRequest,\n buildSetCookieHeader,\n buildErrorResponse,\n forwardResponseHeaders,\n parseResponseBody,\n collectInterceptors,\n buildRequestContext,\n buildResponseContext,\n} from './helpers';\nimport type { TypedProxyConfig } from './types';\n\nconst rpcLogger = logger.child('@spfn/core:rpc-proxy');\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Route info from generated route map\n */\nexport interface RouteMapEntry\n{\n method: HttpMethod;\n path: string;\n}\n\n/**\n * Generated route map type\n */\nexport type RouteMap = Record<string, RouteMapEntry>;\n\n/**\n * RPC proxy configuration\n */\nexport interface RpcProxyConfig extends Omit<TypedProxyConfig, 'onRequest' | 'onResponse'>\n{\n /**\n * Route map containing routeName → {method, path} mappings\n *\n * Merge generated route map with package route maps (auth, events, etc.)\n *\n * @example\n * ```typescript\n * import { authRouteMap } from '@spfn/auth';\n * import { eventRouteMap } from '@spfn/core/event';\n * import { routeMap } from '@/generated/route-map';\n *\n * export const { GET, POST } = createRpcProxy({\n * routeMap: { ...routeMap, ...authRouteMap, ...eventRouteMap },\n * });\n * ```\n */\n routeMap: RouteMap;\n\n /**\n * Shared secret for signing proxy→backend requests (HMAC-SHA256).\n *\n * When set, every forwarded request carries an HMAC signature the backend's\n * proxy-guard middleware verifies, letting it reject direct-to-backend calls\n * that bypass this proxy. Set the SAME value here and on the backend.\n *\n * @default process.env.SPFN_PROXY_SECRET (undefined → signing disabled)\n */\n proxySecret?: string;\n}\n\n// ============================================================================\n// RPC Proxy Handler\n// ============================================================================\n\n/**\n * Create RPC proxy handler for Next.js API Route\n *\n * Handles requests in the format:\n * - GET /api/rpc/{routeName}?input={...}\n * - POST /api/rpc/{routeName} with body\n *\n * Resolves routeName to actual HTTP method and path from routeMap,\n * then forwards to SPFN backend.\n */\nexport function createRpcProxy(config: RpcProxyConfig)\n{\n const {\n apiUrl = env.SPFN_API_URL || 'http://localhost:8790',\n debug = env.NODE_ENV === 'development',\n timeout = env.RPC_PROXY_TIMEOUT,\n headers: defaultHeaders = {},\n interceptors,\n autoDiscoverInterceptors = true,\n disableAutoInterceptors,\n proxySecret = env.SPFN_PROXY_SECRET,\n routeMap,\n } = config;\n\n // Parse the active signing key once — it's fixed for the proxy's lifetime.\n // Use the SAME parser the backend uses (comma key set) and take the active key,\n // so a `v2:new,v1:old` secret is interpreted identically on both sides.\n const proxyKey = proxySecret ? (parseProxyKeySet([proxySecret])[0] ?? null) : null;\n\n // proxy-guard signs route-relative paths; if SPFN_API_URL carries a base path the\n // backend keeps (no ingress strip), the signed path won't match c.req.url and\n // strict mode 403s everything. Warn loudly so it's diagnosable.\n if (proxyKey)\n {\n try\n {\n const basePath = new URL(apiUrl).pathname;\n if (basePath !== '/' && basePath !== '')\n {\n rpcLogger.warn(\n `SPFN_API_URL has a base path (\"${basePath}\"). proxy-guard signs route-relative paths, `\n + 'so the backend must receive paths WITHOUT this prefix (e.g. ingress strip) or strict '\n + 'mode will reject all signed requests with 403.',\n );\n }\n }\n catch\n {\n // invalid apiUrl is surfaced elsewhere\n }\n }\n\n /**\n * Resolve route info from routeMap\n */\n function resolveRoute(routeName: string): { method: string; path: string } | null\n {\n const entry = routeMap[routeName];\n if (entry)\n {\n return { method: entry.method, path: entry.path };\n }\n\n return null;\n }\n\n /**\n * Handle RPC request\n */\n async function handleRpc(\n request: NextRequest,\n context: { params: Promise<{ routeName?: string }> },\n ): Promise<NextResponse>\n {\n const startTime = Date.now();\n const params = await context.params;\n\n try\n {\n const routeName = params.routeName;\n\n if (!routeName)\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Missing routeName parameter', debug),\n { status: 400 },\n );\n }\n\n // Parse input from query string (GET) or body (POST)\n let input: {\n params?: Record<string, any>;\n query?: Record<string, any>;\n body?: Record<string, any>;\n formData?: Record<string, any>;\n headers?: Record<string, any>;\n cookies?: Record<string, any>;\n } = {};\n let rawFormData: FormData | null = null;\n\n if (request.method === 'GET')\n {\n const inputParam = request.nextUrl.searchParams.get('input');\n if (inputParam)\n {\n try\n {\n input = JSON.parse(decodeURIComponent(inputParam));\n }\n catch\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid input parameter', debug),\n { status: 400 },\n );\n }\n }\n }\n else\n {\n // POST - check Content-Type for formData vs JSON\n const contentType = request.headers.get('content-type') || '';\n\n if (contentType.includes('multipart/form-data'))\n {\n // Parse multipart/form-data\n try\n {\n rawFormData = await request.formData();\n\n // Extract __metadata if present (contains params, query, etc.)\n const metadataStr = rawFormData.get('__metadata');\n if (metadataStr && typeof metadataStr === 'string')\n {\n const metadata = JSON.parse(metadataStr);\n input.params = metadata.params;\n input.query = metadata.query;\n input.headers = metadata.headers;\n input.cookies = metadata.cookies;\n }\n\n // Collect formData fields (excluding __metadata)\n input.formData = {};\n rawFormData.forEach((value, key) =>\n {\n if (key === '__metadata') return;\n\n const existing = input.formData![key];\n if (existing !== undefined)\n {\n // Multiple values with same key\n if (Array.isArray(existing))\n {\n existing.push(value);\n }\n else\n {\n input.formData![key] = [existing, value];\n }\n }\n else\n {\n input.formData![key] = value;\n }\n });\n }\n catch (error)\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid form data', debug),\n { status: 400 },\n );\n }\n }\n else\n {\n // Parse JSON body\n try\n {\n input = await request.json();\n }\n catch\n {\n return NextResponse.json(\n buildErrorResponse('Bad Request', 'Invalid JSON body', debug),\n { status: 400 },\n );\n }\n }\n }\n\n // Resolve route info from routeMap\n const routeInfo = resolveRoute(routeName);\n\n if (!routeInfo)\n {\n rpcLogger.warn(`Route not found: ${routeName}`);\n\n return NextResponse.json(\n buildErrorResponse('Not Found', `Route \"${routeName}\" not found`, debug),\n { status: 404 },\n );\n }\n\n const { method: targetMethod, path: targetPath } = routeInfo;\n\n // Build target URL with params and query\n const inputParams = input.params || {};\n const inputQuery = input.query || {};\n const inputBody = input.body;\n const inputFormData = input.formData;\n const hasFormData = rawFormData !== null && inputFormData && Object.keys(inputFormData).length > 0;\n\n const resolvedPath = buildUrlWithParams(targetPath, inputParams);\n const queryString = buildQueryString(inputQuery);\n const targetUrl = `${apiUrl}${resolvedPath}${queryString}`;\n\n if (debug)\n {\n rpcLogger.debug('→ RPC request', {\n routeName,\n targetMethod,\n targetPath: resolvedPath,\n targetUrl,\n hasBody: !!inputBody,\n hasFormData,\n });\n }\n\n // Build headers\n const headers = buildProxyHeaders(request.headers, defaultHeaders);\n\n // Remove Content-Type for formData (let fetch set it with boundary)\n if (hasFormData)\n {\n headers.delete('content-type');\n }\n\n // Build fetch options\n const fetchOptions: RequestInit = {\n method: targetMethod,\n headers,\n };\n\n // Add body for POST/PUT/PATCH\n if (['POST', 'PUT', 'PATCH'].includes(targetMethod))\n {\n if (hasFormData && rawFormData)\n {\n // Forward formData to backend (rebuild without __metadata)\n const forwardFormData = new FormData();\n rawFormData.forEach((value, key) =>\n {\n if (key !== '__metadata')\n {\n forwardFormData.append(key, value);\n }\n });\n fetchOptions.body = forwardFormData;\n }\n else if (inputBody)\n {\n fetchOptions.body = JSON.stringify(inputBody);\n }\n }\n\n // ============================================================\n // Advanced Interceptors - BEFORE FETCH\n // ============================================================\n\n // Collect and filter interceptors\n const allInterceptors = collectInterceptors(autoDiscoverInterceptors, disableAutoInterceptors, interceptors, interceptorRegistry);\n const matchingInterceptors = filterMatchingInterceptors(allInterceptors, resolvedPath, targetMethod);\n\n if (debug && matchingInterceptors.length > 0)\n {\n rpcLogger.debug(`Found ${matchingInterceptors.length} matching interceptors for ${targetMethod} ${resolvedPath}`);\n }\n\n // Create RequestInterceptorContext\n const cookiesMap = parseCookiesFromNextRequest(request);\n const requestCtx = buildRequestContext(\n resolvedPath.slice(1), // Remove leading slash\n targetMethod,\n headers,\n inputBody,\n new URLSearchParams(queryString.slice(1)), // Remove leading ?\n cookiesMap,\n request,\n );\n\n // Execute request interceptors\n const requestInterceptorsToRun = matchingInterceptors.map(r => r.request).filter((i): i is NonNullable<typeof i> => !!i);\n if (requestInterceptorsToRun.length > 0)\n {\n await executeRequestInterceptors(requestCtx, requestInterceptorsToRun);\n\n // Apply modified headers\n for (const [key, value] of Object.entries(requestCtx.headers))\n {\n headers.set(key, value);\n }\n\n // Apply modified body\n if (requestCtx.body)\n {\n fetchOptions.body = JSON.stringify(requestCtx.body);\n }\n }\n\n // ============================================================\n // Proxy → Backend signature (proxy-guard)\n // ============================================================\n // Sign the FINAL request (after interceptors settled body/headers) so\n // the backend can prove it came through this trusted proxy. JSON body\n // is hashed into the signature; large multipart uploads are excluded.\n if (proxyKey)\n {\n const signedBody = typeof fetchOptions.body === 'string' ? fetchOptions.body : undefined;\n // Sign the route-relative path (no SPFN_API_URL base prefix) so it\n // matches the backend's `new URL(c.req.url).pathname` whether the\n // prefix is absent or stripped by an ingress. resolvedPath is already\n // the percent-encoded wire form; queryString is the raw search.\n const signatureHeaders = signProxyRequest({\n key: proxyKey,\n method: targetMethod,\n path: resolvedPath,\n query: queryString,\n body: signedBody,\n });\n\n for (const [headerName, value] of Object.entries(signatureHeaders))\n {\n headers.set(headerName, value);\n }\n }\n\n // Execute fetch with timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try\n {\n const response = await fetch(targetUrl, {\n ...fetchOptions,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n let body = await parseResponseBody(response);\n\n // ============================================================\n // Advanced Interceptors - AFTER FETCH\n // ============================================================\n\n // Create ResponseInterceptorContext\n const responseCtx = buildResponseContext(\n resolvedPath.slice(1),\n targetMethod,\n headers,\n inputBody,\n response,\n body,\n requestCtx.metadata,\n requestCtx.cookies,\n );\n\n // Execute response interceptors\n const responseInterceptorsToRun = matchingInterceptors.map(r => r.response).filter((i): i is NonNullable<typeof i> => !!i);\n if (responseInterceptorsToRun.length > 0)\n {\n await executeResponseInterceptors(responseCtx, responseInterceptorsToRun);\n body = responseCtx.response.body;\n }\n\n const duration = Date.now() - startTime;\n\n if (debug)\n {\n rpcLogger.debug('← RPC response', {\n routeName,\n status: responseCtx.response.status,\n duration: `${duration}ms`,\n });\n }\n\n // Build Next.js response\n // 204 No Content should use NextResponse directly, not NextResponse.json()\n const nextResponse = responseCtx.response.status === 204\n ? new NextResponse(null, {\n status: 204,\n statusText: responseCtx.response.statusText,\n })\n : NextResponse.json(body, {\n status: responseCtx.response.status,\n statusText: responseCtx.response.statusText,\n });\n\n // Forward response headers\n forwardResponseHeaders(response.headers, nextResponse.headers);\n\n // Apply setCookies from interceptors\n for (const cookie of responseCtx.setCookies)\n {\n const setCookieHeader = buildSetCookieHeader(cookie);\n nextResponse.headers.append('Set-Cookie', setCookieHeader);\n\n if (debug)\n {\n rpcLogger.debug('Set-Cookie header added', {\n name: cookie.name,\n });\n }\n }\n\n return nextResponse;\n }\n catch (error)\n {\n clearTimeout(timeoutId);\n\n // Handle timeout\n if (error instanceof Error && error.name === 'AbortError')\n {\n rpcLogger.error('Request timeout', {\n routeName,\n targetUrl,\n timeout,\n });\n\n return NextResponse.json(\n buildErrorResponse('Gateway Timeout', `Request timed out after ${timeout}ms`, debug, error),\n { status: 504 },\n );\n }\n\n // Handle other fetch errors\n const fetchErr = error as Error;\n rpcLogger.error('Fetch error', {\n routeName,\n targetUrl,\n error: fetchErr.message,\n });\n\n return NextResponse.json(\n buildErrorResponse('Bad Gateway', fetchErr.message || 'Failed to connect to backend', debug, fetchErr),\n { status: 502 },\n );\n }\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n const err = error as Error;\n\n rpcLogger.error('RPC proxy error', {\n error: err.message,\n stack: err.stack,\n duration: `${duration}ms`,\n });\n\n return NextResponse.json(\n buildErrorResponse('Internal Server Error', err.message || 'Unknown error', debug, err),\n { status: 500 },\n );\n }\n }\n\n // Return route handlers\n return {\n GET: (req: NextRequest, context: { params: Promise<{ routeName?: string }> }) =>\n handleRpc(req, context),\n POST: (req: NextRequest, context: { params: Promise<{ routeName?: string }> }) =>\n handleRpc(req, context),\n };\n}\n"]}
@@ -2,6 +2,8 @@ import * as _sinclair_typebox from '@sinclair/typebox';
2
2
  import { TSchema, Static, Kind } from '@sinclair/typebox';
3
3
  import { Context, MiddlewareHandler, Hono } from 'hono';
4
4
  import { ContentfulStatusCode, RedirectStatusCode } from 'hono/utils/http-status';
5
+ import { N as NamedMiddleware } from '../define-middleware-DuXD8Hvu.js';
6
+ export { E as ExtractMiddlewareNames, b as NamedMiddlewareFactory, d as defineMiddleware, a as defineMiddlewareFactory } from '../define-middleware-DuXD8Hvu.js';
5
7
  import { HttpMethod } from './types.js';
6
8
 
7
9
  /**
@@ -209,170 +211,6 @@ type RouteBuilderContext<TInput extends RouteInput = RouteInput, TInterceptor ex
209
211
  raw: Context;
210
212
  };
211
213
 
212
- /**
213
- * Middleware Definition Helper
214
- *
215
- * Provides type-safe middleware definition with name inference
216
- */
217
-
218
- /**
219
- * Named middleware with type inference
220
- *
221
- * @example
222
- * ```ts
223
- * export const authMiddleware = defineMiddleware('auth', async (c, next) => {
224
- * // auth logic
225
- * c.set('user', { id: 1 });
226
- * await next();
227
- * });
228
- *
229
- * export const rateLimitMiddleware = defineMiddleware('rateLimit', async (c, next) => {
230
- * // rate limit logic
231
- * await next();
232
- * });
233
- * ```
234
- */
235
- type NamedMiddleware<TName extends string = string> = {
236
- name: TName;
237
- handler: MiddlewareHandler;
238
- _name: TName;
239
- skips?: string[];
240
- };
241
- /**
242
- * Named middleware factory with type inference
243
- *
244
- * Factory function that creates middleware instances with parameters
245
- *
246
- * @example
247
- * ```ts
248
- * export const requirePermissions = defineMiddleware('permission',
249
- * (...permissions: string[]) => async (c, next) => {
250
- * // permission check logic
251
- * await next();
252
- * }
253
- * );
254
- * ```
255
- */
256
- type NamedMiddlewareFactory<TName extends string = string, TArgs extends any[] = any[]> = {
257
- name: TName;
258
- _name: TName;
259
- } & ((...args: TArgs) => MiddlewareHandler);
260
- /**
261
- * Define a named middleware
262
- *
263
- * Creates a middleware with a unique name that can be referenced
264
- * in route-level skip() calls with full type safety.
265
- *
266
- * Supports two patterns:
267
- * 1. Regular middleware: Direct middleware handler
268
- * 2. Factory middleware: Function that creates middleware with parameters
269
- *
270
- * @param name - Unique middleware name
271
- * @param handler - Middleware handler function
272
- * @returns Named middleware with type inference
273
- *
274
- * @example
275
- * ```ts
276
- * // Regular middleware
277
- * export const authMiddleware = defineMiddleware('auth', async (c, next) => {
278
- * const token = c.req.header('authorization');
279
- * if (!token) {
280
- * return c.json({ error: 'Unauthorized' }, 401);
281
- * }
282
- * c.set('user', await verifyToken(token));
283
- * await next();
284
- * });
285
- *
286
- * // Factory middleware
287
- * export const requirePermissions = defineMiddleware('permission',
288
- * (...permissions: string[]) => async (c, next) => {
289
- * const user = c.get('user');
290
- * if (!hasPermissions(user, permissions)) {
291
- * return c.json({ error: 'Forbidden' }, 403);
292
- * }
293
- * await next();
294
- * }
295
- * );
296
- *
297
- * // server.config.ts
298
- * export default defineServerConfig()
299
- * .middlewares([authMiddleware])
300
- * .routes(appRouter)
301
- * .build();
302
- *
303
- * // routes.ts - skip by name
304
- * export const publicRoute = route.get('/health')
305
- * .skip(['auth']) // ✅ Type-safe! Autocomplete!
306
- * .handler(async (c) => c.success({ status: 'ok' }));
307
- *
308
- * // Use factory middleware inline
309
- * export const protectedRoute = route.get('/admin')
310
- * .use([requirePermissions('admin:write')])
311
- * .handler(async (c) => { ... });
312
- * ```
313
- */
314
- /**
315
- * Options for defineMiddleware
316
- */
317
- interface DefineMiddlewareOptions {
318
- /**
319
- * Server-level middleware names to auto-skip when this middleware is used at route level.
320
- *
321
- * @example
322
- * ```ts
323
- * // optionalAuth auto-skips the global 'auth' middleware
324
- * export const optionalAuth = defineMiddleware('optionalAuth', handler, {
325
- * skips: ['auth']
326
- * });
327
- *
328
- * // Usage: .use([optionalAuth]) — no need for .skip(['auth'])
329
- * ```
330
- */
331
- skips?: string[];
332
- }
333
- declare function defineMiddleware<TName extends string>(name: TName, handler: MiddlewareHandler, options?: DefineMiddlewareOptions): NamedMiddleware<TName>;
334
- declare function defineMiddleware<TName extends string, TArgs extends any[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler, options?: DefineMiddlewareOptions): NamedMiddlewareFactory<TName, TArgs>;
335
- /**
336
- * Define a middleware factory explicitly
337
- *
338
- * Use this when your factory function has exactly 2 parameters,
339
- * which would be incorrectly detected as a regular middleware handler.
340
- *
341
- * @param name - Unique middleware name
342
- * @param factory - Factory function that returns a middleware handler
343
- * @returns Named middleware factory with type inference
344
- *
345
- * @example
346
- * ```ts
347
- * // Factory with 2 params (would be misdetected by defineMiddleware)
348
- * export const rateLimiter = defineMiddlewareFactory('rateLimit',
349
- * (limit: number, window: number) => async (c, next) => {
350
- * // rate limit logic using limit and window
351
- * await next();
352
- * }
353
- * );
354
- *
355
- * // Usage
356
- * route.get('/api')
357
- * .use([rateLimiter(100, 60000)]) // 100 requests per minute
358
- * .handler(...)
359
- * ```
360
- */
361
- declare function defineMiddlewareFactory<TName extends string, TArgs extends unknown[]>(name: TName, factory: (...args: TArgs) => MiddlewareHandler): NamedMiddlewareFactory<TName, TArgs>;
362
- /**
363
- * Extract middleware names from an array of named middlewares
364
- *
365
- * @example
366
- * ```ts
367
- * type Middlewares = [
368
- * NamedMiddleware<'auth'>,
369
- * NamedMiddleware<'rateLimit'>
370
- * ];
371
- * type Names = ExtractMiddlewareNames<Middlewares>; // 'auth' | 'rateLimit'
372
- * ```
373
- */
374
- type ExtractMiddlewareNames<T extends readonly NamedMiddleware<string>[]> = T[number]['_name'];
375
-
376
214
  /**
377
215
  * Route Builder
378
216
  *
@@ -911,4 +749,4 @@ declare function getFileOptions(schema: TSchema): FileSchemaOptions | FileArrayS
911
749
  */
912
750
  declare function formatFileSize(bytes: number): string;
913
751
 
914
- export { type ExtractMiddlewareNames, FileArraySchema, type FileArraySchemaOptions, type FileArraySchemaType, FileSchema, type FileSchemaOptions, type FileSchemaType, HttpMethod, type MergedInput, type NamedMiddleware, type NamedMiddlewareFactory, Nullable, OptionalFileSchema, OptionalNullable, type PaginatedResult, type RegisteredRoute, type RouteBuilderContext, type RouteDef, type RouteHandlerFn, type RouteInput, type Router, defineMiddleware, defineMiddlewareFactory, defineRouter, formatFileSize, getFileOptions, isFileArraySchema, isFileSchema, isHttpMethod, registerRoutes, route };
752
+ export { FileArraySchema, type FileArraySchemaOptions, type FileArraySchemaType, FileSchema, type FileSchemaOptions, type FileSchemaType, HttpMethod, type MergedInput, NamedMiddleware, Nullable, OptionalFileSchema, OptionalNullable, type PaginatedResult, type RegisteredRoute, type RouteBuilderContext, type RouteDef, type RouteHandlerFn, type RouteInput, type Router, defineRouter, formatFileSize, getFileOptions, isFileArraySchema, isFileSchema, isHttpMethod, registerRoutes, route };
@@ -4,10 +4,10 @@ import { cors } from 'hono/cors';
4
4
  import { serve } from '@hono/node-server';
5
5
  import { NamedMiddleware, Router } from '@spfn/core/route';
6
6
  import { OnErrorContext, ProxyGuardConfig } from '@spfn/core/middleware';
7
- import { J as JobRouter, B as BossOptions } from '../boss-Cxqc-Oiw.js';
8
- import { E as EventRouterDef } from '../token-manager-C2Ag5-s8.js';
9
- import { S as SSEHandlerConfig, a as SSEAuthConfig } from '../types-VpVQIsyB.js';
10
- import { W as WSRouterDef, a as WSHandlerConfig, b as WSMessageHandlers, c as WSAuthConfig } from '../types-CKsmzaB8.js';
7
+ import { J as JobRouter, B as BossOptions } from '../boss-CEik0yq-.js';
8
+ import { E as EventRouterDef } from '../token-manager-jKD_EsSE.js';
9
+ import { S as SSEHandlerConfig, a as SSEAuthConfig } from '../types-BFB72jbM.js';
10
+ import { W as WSRouterDef, a as WSHandlerConfig, b as WSMessageHandlers, c as WSAuthConfig } from '../types-DVjf37yO.js';
11
11
  import '@sinclair/typebox';
12
12
  import 'pg-boss';
13
13