Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
139 lines
4.6 KiB
Plaintext
139 lines
4.6 KiB
Plaintext
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 };
|