{"version":3,"sources":["../../../src/frameworks/cors/cors.framework.ts"],"sourcesContent":["//#region Imports\n\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport cors, { type CorsOptions } from 'cors';\nimport type { FrameworkContract } from '../../contracts';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The options to customize {@link CorsFramework}\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport type CorsFrameworkOptions = CorsOptions & {\n  /**\n   * Send error 403 when cors is invalid. From what I read in `cors`, `fastify/cors` and [this problem](https://stackoverflow.com/questions/57212248/why-is-http-request-been-processed-in-action-even-when-cors-is-not-enabled)\n   * it is normal to process the request even if the origin is invalid.\n   * So this option will respond with error if this method was called from an invalid origin (or not allowed method) like [access control lib](https://github.com/primus/access-control/blob/master/index.js#L95-L115) .\n   *\n   * @defaultValue true\n   */\n  forbiddenOnInvalidOriginOrMethod?: boolean;\n};\n\n/**\n * The framework that handles cors for your api without relying on internals of the framework\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { ServerlessAdapter } from '@h4ad/serverless-adapter';\n * import { ExpressFramework } from '@h4ad/serverless-adapter/lib/frameworks/express';\n * import { CorsFramework } from '@h4ad/serverless-adapter/lib/frameworks/cors';\n *\n * const expressFramework = new ExpressFramework();\n * const options: CorsOptions = {}; // customize the options\n * const framework = new CorsFramework(expressFramework, options);\n *\n * export const handler = ServerlessAdapter.new(null)\n *   .setFramework(framework)\n *   // set other configurations and then build\n *   .build();\n * ```\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport class CorsFramework<TApp> implements FrameworkContract<TApp> {\n  //#region Constructor\n\n  /**\n   * Default Constructor\n   */\n  constructor(\n    protected readonly framework: FrameworkContract<TApp>,\n    protected readonly options?: CorsFrameworkOptions,\n  ) {\n    this.cachedCorsInstance = cors(this.options);\n  }\n\n  //#endregion\n\n  /**\n   * All cors headers that can be added by cors package\n   */\n  protected readonly corsHeaders: string[] = [\n    'Access-Control-Max-Age',\n    'Access-Control-Expose-Headers',\n    'Access-Control-Allow-Headers',\n    'Access-Control-Request-Headers',\n    'Access-Control-Allow-Credentials',\n    'Access-Control-Allow-Methods',\n    'Access-Control-Allow-Origin',\n  ];\n\n  /**\n   * The cached instance of cors\n   */\n  protected readonly cachedCorsInstance: ReturnType<typeof cors>;\n\n  //#region Public Methods\n\n  /**\n   * {@inheritDoc}\n   */\n  public sendRequest(\n    app: TApp,\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): void {\n    this.cachedCorsInstance(\n      request,\n      response,\n      this.onCorsNext(app, request, response),\n    );\n  }\n\n  //#endregion\n\n  //#region Protected Methods\n\n  /**\n   * Handle next execution called by the cors package\n   */\n  protected onCorsNext(\n    app: TApp,\n    request: IncomingMessage,\n    response: ServerResponse,\n  ): () => void {\n    return () => {\n      this.formatHeaderValuesAddedByCorsPackage(response);\n\n      const errorOnInvalidOrigin = getDefaultIfUndefined(\n        this.options?.forbiddenOnInvalidOriginOrMethod,\n        true,\n      );\n\n      if (errorOnInvalidOrigin) {\n        const allowedOrigin = response.getHeader('access-control-allow-origin');\n        const isInvalidOrigin = this.isInvalidOriginOrMethodIsNotAllowed(\n          request,\n          allowedOrigin,\n        );\n\n        if (isInvalidOrigin) {\n          response.statusCode = 403;\n          response.setHeader('Content-Type', 'text/plain');\n          response.end(\n            [\n              'Invalid HTTP Access Control (CORS) request:',\n              `  Origin: ${request.headers.origin}`,\n              `  Method: ${request.method}`,\n            ].join('\\n'),\n          );\n\n          return;\n        }\n      }\n\n      this.framework.sendRequest(app, request, response);\n    };\n  }\n\n  /**\n   * Format the headers to be standardized with the rest of the library, such as ApiGatewayV2.\n   * Also, some frameworks don't support headers as an array, so we need to format the values.\n   */\n  protected formatHeaderValuesAddedByCorsPackage(\n    response: ServerResponse,\n  ): void {\n    for (const corsHeader of this.corsHeaders) {\n      const value = response.getHeader(corsHeader);\n\n      if (value === undefined) continue;\n\n      response.removeHeader(corsHeader);\n      response.setHeader(\n        corsHeader.toLowerCase(),\n        Array.isArray(value) ? value.join(',') : value,\n      );\n    }\n  }\n\n  /**\n   * Check if the origin is invalid or if the method is not allowed.\n   * Highly inspired by [access-control](https://github.com/primus/access-control/blob/master/index.js#L95-L115)\n   */\n  protected isInvalidOriginOrMethodIsNotAllowed(\n    request: IncomingMessage,\n    allowedOrigin: number | string | string[] | undefined,\n  ): boolean {\n    if (!allowedOrigin) return true;\n\n    if (\n      !!request.headers.origin &&\n      allowedOrigin !== '*' &&\n      request.headers.origin !== allowedOrigin\n    )\n      return true;\n\n    const notPermitedInMethods =\n      this.options &&\n      Array.isArray(this.options.methods) &&\n      this.options.methods.every(\n        m => m.toLowerCase() !== request.method?.toLowerCase(),\n      );\n    const differentMethod =\n      this.options &&\n      typeof this.options.methods === 'string' &&\n      this.options.methods\n        .split(',')\n        .every(m => m.trim().toLowerCase() !== request.method?.toLowerCase());\n\n    if (this.options?.methods && (notPermitedInMethods || differentMethod))\n      return true;\n\n    return false;\n  }\n\n  //#endregion\n}\n"],"mappings":"0FAGA,OAAOA,MAAgC,OA8ChC,IAAMC,EAAN,KAA6D,CAMlE,YACqBC,EACAC,EACnB,CAFmB,eAAAD,EACA,aAAAC,EAEnB,KAAK,mBAAqBC,EAAK,KAAK,OAAO,CAC7C,CA5DF,MAiDoE,CAAAC,EAAA,sBAkB/C,YAAwB,CACzC,yBACA,gCACA,+BACA,iCACA,mCACA,+BACA,6BACF,EAKmB,mBAOZ,YACLC,EACAC,EACAC,EACM,CACN,KAAK,mBACHD,EACAC,EACA,KAAK,WAAWF,EAAKC,EAASC,CAAQ,CACxC,CACF,CASU,WACRF,EACAC,EACAC,EACY,CACZ,MAAO,IAAM,CAQX,GAPA,KAAK,qCAAqCA,CAAQ,EAErBC,EAC3B,KAAK,SAAS,iCACd,EACF,EAE0B,CACxB,IAAMC,EAAgBF,EAAS,UAAU,6BAA6B,EAMtE,GALwB,KAAK,oCAC3BD,EACAG,CACF,EAEqB,CACnBF,EAAS,WAAa,IACtBA,EAAS,UAAU,eAAgB,YAAY,EAC/CA,EAAS,IACP,CACE,8CACA,aAAaD,EAAQ,QAAQ,MAAM,GACnC,aAAaA,EAAQ,MAAM,EAC7B,EAAE,KAAK;AAAA,CAAI,CACb,EAEA,MACF,CACF,CAEA,KAAK,UAAU,YAAYD,EAAKC,EAASC,CAAQ,CACnD,CACF,CAMU,qCACRA,EACM,CACN,QAAWG,KAAc,KAAK,YAAa,CACzC,IAAMC,EAAQJ,EAAS,UAAUG,CAAU,EAEvCC,IAAU,SAEdJ,EAAS,aAAaG,CAAU,EAChCH,EAAS,UACPG,EAAW,YAAY,EACvB,MAAM,QAAQC,CAAK,EAAIA,EAAM,KAAK,GAAG,EAAIA,CAC3C,EACF,CACF,CAMU,oCACRL,EACAG,EACS,CAGT,GAFI,CAACA,GAGDH,EAAQ,QAAQ,QAClBG,IAAkB,KAClBH,EAAQ,QAAQ,SAAWG,EAE3B,MAAO,GAET,IAAMG,EACJ,KAAK,SACL,MAAM,QAAQ,KAAK,QAAQ,OAAO,GAClC,KAAK,QAAQ,QAAQ,MACnBC,GAAKA,EAAE,YAAY,IAAMP,EAAQ,QAAQ,YAAY,CACvD,EACIQ,EACJ,KAAK,SACL,OAAO,KAAK,QAAQ,SAAY,UAChC,KAAK,QAAQ,QACV,MAAM,GAAG,EACT,MAAMD,GAAKA,EAAE,KAAK,EAAE,YAAY,IAAMP,EAAQ,QAAQ,YAAY,CAAC,EAExE,MAAI,QAAK,SAAS,UAAYM,GAAwBE,GAIxD,CAGF","names":["cors","CorsFramework","framework","options","cors","__name","app","request","response","getDefaultIfUndefined","allowedOrigin","corsHeader","value","notPermitedInMethods","m","differentMethod"]}