{"version":3,"sources":["../src/middleware.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Express.js Middleware for PEAC Receipt Issuance\n *\n * Intercepts responses and adds PEAC receipts automatically.\n *\n * @packageDocumentation\n */\n\nimport type { Request, Response, NextFunction, RequestHandler } from 'express';\nimport {\n  createReceipt,\n  validateConfig,\n  type MiddlewareConfig,\n  type RequestContext,\n} from '@peac/middleware-core';\n\n/**\n * Express-specific middleware configuration\n */\nexport interface ExpressMiddlewareConfig extends MiddlewareConfig {\n  /** Skip receipt generation for certain routes */\n  skip?: (req: Request) => boolean;\n\n  /** Custom audience extraction from request */\n  audienceExtractor?: (req: Request) => string;\n\n  /** Custom subject extraction from request */\n  subjectExtractor?: (req: Request) => string | undefined;\n\n  /** Error handler for receipt generation failures */\n  onError?: (error: Error, req: Request, res: Response) => void;\n}\n\n/**\n * Symbol to store PEAC context on request\n */\nconst PEAC_CONTEXT_KEY = Symbol('peac-context');\n\n/**\n * Request with PEAC context attached\n */\nexport interface RequestWithPeacContext extends Request {\n  [PEAC_CONTEXT_KEY]?: RequestContext;\n}\n\n/**\n * Check if a request has PEAC context\n */\nexport function hasPeacContext(req: Request): req is RequestWithPeacContext {\n  return PEAC_CONTEXT_KEY in req;\n}\n\n/**\n * Get the PEAC receipt from a response (for testing/debugging)\n *\n * @param res - Express response object\n * @returns Receipt JWS if present, undefined otherwise\n */\nexport function getReceiptFromResponse(res: Response): string | undefined {\n  const receipt = res.getHeader('PEAC-Receipt');\n  return typeof receipt === 'string' ? receipt : undefined;\n}\n\n/**\n * Convert Express request to RequestContext\n */\nfunction buildRequestContext(req: Request): RequestContext {\n  return {\n    method: req.method,\n    path: req.path,\n    headers: req.headers as Record<string, string | string[] | undefined>,\n    body: req.body,\n    timestamp: Date.now(),\n  };\n}\n\n/**\n * Express middleware that adds PEAC receipts to responses\n *\n * The middleware intercepts `res.json()` and `res.send()` to inject\n * PEAC receipts into responses. For header transport (default), the\n * receipt is added as a `PEAC-Receipt` header. For body transport,\n * the response is wrapped in a `{ data, peac_receipt }` structure.\n *\n * @param config - Middleware configuration\n * @returns Express request handler\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { peacMiddleware } from '@peac/middleware-express';\n *\n * const app = express();\n *\n * // Add PEAC middleware\n * app.use(peacMiddleware({\n *   issuer: 'https://api.example.com',\n *   signingKey: privateKey,\n *   keyId: 'prod-2026-02',\n * }));\n *\n * app.get('/api/data', (req, res) => {\n *   res.json({ items: [1, 2, 3] });\n *   // PEAC-Receipt header automatically added\n * });\n * ```\n */\nexport function peacMiddleware(config: ExpressMiddlewareConfig): RequestHandler {\n  // Validate configuration at initialization\n  validateConfig(config);\n\n  return async function peacReceiptMiddleware(\n    req: Request,\n    res: Response,\n    next: NextFunction\n  ): Promise<void> {\n    // Check if we should skip this request\n    if (config.skip?.(req)) {\n      next();\n      return;\n    }\n\n    // Build and store request context\n    const requestContext = buildRequestContext(req);\n    (req as RequestWithPeacContext)[PEAC_CONTEXT_KEY] = requestContext;\n\n    // Enhance config with Express-specific extractors\n    const enhancedConfig: MiddlewareConfig = {\n      ...config,\n      claimsGenerator: async (ctx) => {\n        const baseClaims = config.claimsGenerator ? await config.claimsGenerator(ctx) : {};\n\n        return {\n          ...baseClaims,\n          // Override audience if custom extractor provided\n          ...(config.audienceExtractor && { aud: config.audienceExtractor(req) }),\n          // Add subject if extractor provided\n          ...(config.subjectExtractor && { sub: config.subjectExtractor(req) }),\n        };\n      },\n    };\n\n    // Store original json method\n    const originalJson = res.json.bind(res);\n\n    // Override res.json to inject receipt\n    res.json = function jsonWithReceipt(body: unknown): Response {\n      // Generate receipt asynchronously\n      createReceipt(enhancedConfig, requestContext, {\n        statusCode: res.statusCode,\n        headers: res.getHeaders() as Record<string, string | string[] | undefined>,\n        body,\n      })\n        .then((result) => {\n          // Add headers from receipt result\n          for (const [key, value] of Object.entries(result.headers)) {\n            res.setHeader(key, value);\n          }\n\n          // If body transport, use wrapped body\n          if (result.bodyWrapper) {\n            return originalJson(result.bodyWrapper);\n          }\n\n          return originalJson(body);\n        })\n        .catch((error: Error) => {\n          // Handle errors without breaking the response\n          if (config.onError) {\n            config.onError(error, req, res);\n          } else {\n            // Log but don't fail the response\n            console.error('[PEAC] Receipt generation failed:', error.message);\n          }\n\n          // Send original body on error\n          return originalJson(body);\n        });\n\n      // Return response for chaining (synchronous return)\n      // The actual response will be sent by the promise chain\n      return res;\n    };\n\n    next();\n  };\n}\n\n/**\n * Create middleware with synchronous receipt generation (blocking)\n *\n * This variant waits for receipt generation before sending the response.\n * Use this when you need to guarantee the receipt is in the response,\n * but be aware it adds latency.\n *\n * @param config - Middleware configuration\n * @returns Express request handler\n */\nexport function peacMiddlewareSync(config: ExpressMiddlewareConfig): RequestHandler {\n  // Validate configuration at initialization\n  validateConfig(config);\n\n  return async function peacReceiptMiddlewareSync(\n    req: Request,\n    res: Response,\n    next: NextFunction\n  ): Promise<void> {\n    // Check if we should skip this request\n    if (config.skip?.(req)) {\n      next();\n      return;\n    }\n\n    // Build and store request context\n    const requestContext = buildRequestContext(req);\n    (req as RequestWithPeacContext)[PEAC_CONTEXT_KEY] = requestContext;\n\n    // Enhance config with Express-specific extractors\n    const enhancedConfig: MiddlewareConfig = {\n      ...config,\n      claimsGenerator: async (ctx) => {\n        const baseClaims = config.claimsGenerator ? await config.claimsGenerator(ctx) : {};\n\n        return {\n          ...baseClaims,\n          ...(config.audienceExtractor && { aud: config.audienceExtractor(req) }),\n          ...(config.subjectExtractor && { sub: config.subjectExtractor(req) }),\n        };\n      },\n    };\n\n    // Store original json method\n    const originalJson = res.json.bind(res);\n\n    // Override res.json to inject receipt synchronously\n    res.json = function jsonWithReceiptSync(body: unknown): Response {\n      // We can't make this truly sync due to crypto operations,\n      // but we can make it blocking by using a sync wrapper\n      const syncWrapper = async (): Promise<void> => {\n        try {\n          const result = await createReceipt(enhancedConfig, requestContext, {\n            statusCode: res.statusCode,\n            headers: res.getHeaders() as Record<string, string | string[] | undefined>,\n            body,\n          });\n\n          // Add headers\n          for (const [key, value] of Object.entries(result.headers)) {\n            res.setHeader(key, value);\n          }\n\n          // Send response\n          if (result.bodyWrapper) {\n            originalJson(result.bodyWrapper);\n          } else {\n            originalJson(body);\n          }\n        } catch (error) {\n          if (config.onError) {\n            config.onError(error as Error, req, res);\n          } else {\n            console.error('[PEAC] Receipt generation failed:', (error as Error).message);\n          }\n          originalJson(body);\n        }\n      };\n\n      // Execute synchronously by blocking\n      syncWrapper();\n      return res;\n    };\n\n    next();\n  };\n}\n"]}