import { compose } from "../../compose.js"; import "../../router.js"; import { TrieRouter } from "../../router/trie-router/router.js"; import "../../router/trie-router/index.js"; //#region src/middleware/combine/index.ts /** * @module * Combine Middleware for Hono. */ /** * Create a composed middleware that runs the first middleware that returns true. * * @param middleware - An array of MiddlewareHandler or Condition functions. * Middleware is applied in the order it is passed, and if any middleware exits without returning * an exception first, subsequent middleware will not be executed. * You can also pass a condition function that returns a boolean value. If returns true * the evaluation will be halted, and rest of the middleware will not be executed. * @returns A composed middleware. * * @example * ```ts * import { some } from 'hono/combine' * import { bearerAuth } from 'hono/bearer-auth' * import { myRateLimit } from '@/rate-limit' * * // If client has a valid token, then skip rate limiting. * // Otherwise, apply rate limiting. * app.use('/api/*', some( * bearerAuth({ token }), * myRateLimit({ limit: 100 }), * )); * ``` */ const some = (...middleware) => { return async function some(c, next) { let isNextCalled = false; const wrappedNext = () => { isNextCalled = true; return next(); }; let lastError; let response; for (const handler of middleware) try { const result = await handler(c, wrappedNext); if (result === true && !c.finalized) await wrappedNext(); else if (result === false) { lastError = /* @__PURE__ */ new Error("No successful middleware found"); continue; } else if (result && result !== true) response = result; lastError = void 0; break; } catch (error) { lastError = error; if (isNextCalled) break; } if (lastError) throw lastError; return response; }; }; /** * Create a composed middleware that runs all middleware and throws an error if any of them fail. * * @param middleware - An array of MiddlewareHandler or Condition functions. * Middleware is applied in the order it is passed, and if any middleware throws an error, * subsequent middleware will not be executed. * You can also pass a condition function that returns a boolean value. If returns false * the evaluation will be halted, and rest of the middleware will not be executed. * @returns A composed middleware. * * @example * ```ts * import { some, every } from 'hono/combine' * import { bearerAuth } from 'hono/bearer-auth' * import { myCheckLocalNetwork } from '@/check-local-network' * import { myRateLimit } from '@/rate-limit' * * // If client is in local network, then skip authentication and rate limiting. * // Otherwise, apply authentication and rate limiting. * app.use('/api/*', some( * myCheckLocalNetwork(), * every( * bearerAuth({ token }), * myRateLimit({ limit: 100 }), * ), * )); * ``` */ const every = (...middleware) => { return async function every(c, next) { const currentRouteIndex = c.req.routeIndex; await compose(middleware.map((m) => [[async (c, next) => { c.req.routeIndex = currentRouteIndex; const res = await m(c, next); if (res === false) throw new Error("Unmet condition"); return res; }]]))(c, next); }; }; /** * Create a composed middleware that runs all middleware except when the condition is met. * * @param condition - A string or Condition function. * If there are multiple targets to match any of them, they can be passed as an array. * If a string is passed, it will be treated as a path pattern to match. * If a Condition function is passed, it will be evaluated against the request context. * @param middleware - Middleware to run when the condition is not met. * Multiple middleware can be passed, and they are applied in the order they are passed. * @returns A composed middleware. * * @example * ```ts * import { except } from 'hono/combine' * import { bearerAuth } from 'hono/bearer-auth' * * // If client is accessing public API, then skip authentication. * // Otherwise, require a valid token. * app.use('/api/*', except( * '/api/public/*', * bearerAuth({ token }), * )); * ``` */ const except = (condition, ...middleware) => { let router = void 0; const conditions = (Array.isArray(condition) ? condition : [condition]).map((condition) => { if (typeof condition === "string") { router ||= new TrieRouter(); router.add("ALL", condition, true); } else return condition; }).filter(Boolean); if (router) conditions.unshift((c) => !!router?.match("ALL", c.req.path)?.[0]?.[0]?.[0]); const handler = some((c) => conditions.some((cond) => cond(c)), every(...middleware)); return async function except(c, next) { await handler(c, next); }; }; //#endregion export { every, except, some };