//#region src/middleware/cors/index.ts /** * CORS Middleware for Hono. * * @see {@link https://hono.dev/docs/middleware/builtin/cors} * * @param {CORSOptions} [options] - The options for the CORS middleware. * @param {string | string[] | ((origin: string, c: Context) => Promise | string | undefined | null)} [options.origin='*'] - The value of "Access-Control-Allow-Origin" CORS header. * @param {string[] | ((origin: string, c: Context) => Promise | string[])} [options.allowMethods=['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH', 'QUERY']] - The value of "Access-Control-Allow-Methods" CORS header. * @param {string[]} [options.allowHeaders=[]] - The value of "Access-Control-Allow-Headers" CORS header. * @param {number} [options.maxAge] - The value of "Access-Control-Max-Age" CORS header. * @param {boolean} [options.credentials] - The value of "Access-Control-Allow-Credentials" CORS header. * @param {string[]} [options.exposeHeaders=[]] - The value of "Access-Control-Expose-Headers" CORS header. * @returns {MiddlewareHandler} The middleware handler function. * * @example * ```ts * const app = new Hono() * * app.use('/api/*', cors()) * app.use( * '/api2/*', * cors({ * origin: 'http://example.com', * allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'], * allowMethods: ['POST', 'GET', 'OPTIONS'], * exposeHeaders: ['Content-Length', 'X-Kuma-Revision'], * maxAge: 600, * credentials: true, * }) * ) * * app.all('/api/abc', (c) => { * return c.json({ success: true }) * }) * app.all('/api2/abc', (c) => { * return c.json({ success: true }) * }) * ``` */ const cors = (options) => { const opts = { origin: "*", allowMethods: [ "GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "QUERY" ], allowHeaders: [], exposeHeaders: [], ...options }; const exposeHeadersStr = opts.exposeHeaders?.length ? opts.exposeHeaders.join(",") : void 0; const allowHeadersStr = opts.allowHeaders?.length ? opts.allowHeaders.join(",") : void 0; const findAllowOrigin = ((optsOrigin) => { if (typeof optsOrigin === "string") { if (optsOrigin === "*") return () => optsOrigin; else return (origin) => optsOrigin === origin ? origin : null; } else if (typeof optsOrigin === "function") return optsOrigin; else return (origin) => optsOrigin.includes(origin) ? origin : null; })(opts.origin); const findAllowMethods = ((optsAllowMethods) => { if (typeof optsAllowMethods === "function") return async (origin, c) => (await optsAllowMethods(origin, c)).join(","); else if (Array.isArray(optsAllowMethods)) { const methodsStr = optsAllowMethods.join(","); return () => methodsStr; } else return () => ""; })(opts.allowMethods); return async function cors(c, next) { function set(key, value) { c.res.headers.set(key, value); } const allowOrigin = await findAllowOrigin(c.req.header("origin") || "", c); if (allowOrigin) set("Access-Control-Allow-Origin", allowOrigin); if (opts.credentials) set("Access-Control-Allow-Credentials", "true"); if (exposeHeadersStr) set("Access-Control-Expose-Headers", exposeHeadersStr); if (c.req.method === "OPTIONS") { if (opts.origin !== "*") c.res.headers.append("Vary", "Origin"); if (opts.maxAge != null) set("Access-Control-Max-Age", opts.maxAge.toString()); const allowMethods = await findAllowMethods(c.req.header("origin") || "", c); if (allowMethods) set("Access-Control-Allow-Methods", allowMethods); let headersStr = allowHeadersStr; if (!headersStr) { const requestHeaders = c.req.header("Access-Control-Request-Headers"); if (requestHeaders) headersStr = requestHeaders.split(",").map((h) => h.trim()).join(","); } if (headersStr) { set("Access-Control-Allow-Headers", headersStr); c.res.headers.append("Vary", "Access-Control-Request-Headers"); } c.res.headers.delete("Content-Length"); c.res.headers.delete("Content-Type"); return new Response(null, { headers: c.res.headers, status: 204, statusText: "No Content" }); } await next(); if (opts.origin !== "*") c.header("Vary", "Origin", { append: true }); }; }; //#endregion export { cors };