@nhtio/middleware 0.1.0-master-d3ec5be7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +9 -0
- package/README.md +5 -0
- package/index.cjs +2 -0
- package/index.cjs.map +1 -0
- package/index.d.ts +37 -0
- package/index.mjs +264 -0
- package/index.mjs.map +1 -0
- package/package.json +21 -0
- package/private/middleware.d.ts +94 -0
- package/private/runner.d.ts +107 -0
- package/private/types.d.ts +36 -0
package/LICENSE.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 New Horizon Technology LTD (nht.io)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# @nhtio/middleware
|
2
|
+
|
3
|
+
A cross-environment (browser/node) implementation of the chain of responsibility design pattern based on [@poppinss/middleware](https://github.com/poppinss/middleware)
|
4
|
+
|
5
|
+
For more information, see the [official documentation](https://middleware.nht.io)
|
package/index.cjs
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
"use strict";var H=t=>{throw TypeError(t)};var M=(t,r,e)=>r.has(t)||H("Cannot "+e);var n=(t,r,e)=>(M(t,r,"read from private field"),e?e.call(t):r.get(t)),a=(t,r,e)=>r.has(t)?H("Cannot add the same private member more than once"):r instanceof WeakSet?r.add(t):r.set(t,e),s=(t,r,e,i)=>(M(t,r,"write to private field"),i?i.call(t,e):r.set(t,e),e),f=(t,r,e)=>(M(t,r,"access private method"),e);var z=(t,r,e,i)=>({set _(l){s(t,r,l,e)},get _(){return n(t,r,i)}});Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function y(t,r){function e(){if(!e.called)return e.called=!0,r(t)}return e.called=!1,e}const C=()=>Promise.resolve();var u,v,w,m,c,h,k,g;class A{constructor(r){a(this,h);a(this,u);a(this,v,0);a(this,w);a(this,m,C);a(this,c);s(this,u,r)}finalHandler(r){return s(this,m,r),this}errorHandler(r){return s(this,c,r),this}async run(r){return s(this,w,r),n(this,c)?f(this,h,g).call(this,this):f(this,h,k).call(this,this)}}u=new WeakMap,v=new WeakMap,w=new WeakMap,m=new WeakMap,c=new WeakMap,h=new WeakSet,k=function(r){var i,l;const e=n(r,u)[z(r,v)._++];return e?n(l=r,w).call(l,e,y(r,f(r,h,k))):n(i=r,m).call(i)},g=function(r){var i,l;const e=n(r,u)[z(r,v)._++];return e?n(l=r,w).call(l,e,y(r,f(r,h,g))).catch(n(r,c)):n(i=r,m).call(i).catch(n(r,c))};var o,E,d;class b{constructor(){a(this,o,new Set);a(this,E);a(this,d,!1)}all(){return n(this,o)}has(r){return n(this,o).has(r)}add(r){if(n(this,d))throw new Error("Middleware stack is frozen. Cannot add new middleware");return n(this,o).add(r),this}remove(r){if(n(this,d))throw new Error("Middleware stack is frozen. Cannot remove middleware");return n(this,o).delete(r)}clear(){if(n(this,d))throw new Error("Middleware stack is frozen. Cannot clear middleware");n(this,o).clear()}merge(r){if(n(this,d))throw new Error("Middleware stack is frozen. Cannot merge middleware");r.all().forEach(e=>{this.add(e)})}freeze(){n(this,d)||(s(this,d,!0),s(this,E,[...this.all()]))}runner(){return this.freeze(),new A(n(this,E))}}o=new WeakMap,E=new WeakMap,d=new WeakMap;const x="0.1.0-master-d3ec5be7";exports.Middleware=b;exports.Runner=A;exports.version=x;
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
package/index.cjs.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/private/runner.ts","../src/private/middleware.ts","../src/index.ts"],"sourcesContent":["import type { ErrorHandler, Executor, FinalHandler } from './types'\n\n/**\n * Ensures a function is executed only once by maintaining a called state.\n *\n * This utility function is tightly coupled with the Runner class and is used\n * to ensure the `next` callback is not invoked multiple times in the middleware chain.\n *\n * @param scope - The Runner instance scope\n * @param callback - The function to execute only once\n * @returns A wrapped function that tracks its execution state\n * @internal\n */\nfunction once(scope: Runner<any>, callback: (scope: Runner<any>) => Promise<void> | void) {\n function next(): Promise<void> | void {\n if (next.called) {\n return\n }\n\n next.called = true\n return callback(scope)\n }\n next.called = false\n\n return next\n}\n\n/**\n * Default final handler that resolves immediately when the middleware chain completes.\n *\n * This handler is used when no custom final handler is specified via `finalHandler()`.\n *\n * @internal\n */\nconst DEFAULT_FINAL_HANDLER = () => Promise.resolve()\n\n/**\n * Runner executes an array of middleware functions in sequence.\n *\n * The middleware pipeline advances only when the current function calls `next`,\n * implementing the chain of responsibility pattern. You control how to execute\n * the underlying middleware functions by providing an executor function.\n *\n * @template MiddlewareFn - The type of the middleware function/handler\n *\n * @example\n * ```ts\n * const context = { stack: [] }\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n * middleware.add((ctx, next) => {\n * ctx.stack.push('fn1')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .finalHandler(() => {\n * context.stack.push('final handler')\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\nexport class Runner<MiddlewareFn extends any> {\n /**\n * The array of middleware handlers to execute in sequence.\n */\n #middleware: MiddlewareFn[]\n\n /**\n * The current index position in the middleware pipeline.\n */\n #currentIndex = 0\n\n /**\n * The executor function responsible for invoking each middleware handler.\n */\n #executor!: Executor<MiddlewareFn>\n\n /**\n * The final handler to execute when the middleware chain completes successfully.\n */\n #finalHandler: FinalHandler = DEFAULT_FINAL_HANDLER\n\n /**\n * Optional error handler to catch and handle exceptions in the middleware pipeline.\n */\n #errorHandler?: ErrorHandler\n\n constructor(middleware: MiddlewareFn[]) {\n this.#middleware = middleware\n }\n\n /**\n * Invokes middleware handlers one at a time.\n *\n * Middleware functions are executed recursively until `next` is called.\n * If a middleware doesn't call `next`, the chain will finish automatically\n * without executing remaining handlers.\n *\n * @param self - The Runner instance scope\n * @returns A Promise that resolves when the middleware chain completes\n * @internal\n */\n #invoke(self: Runner<MiddlewareFn>): Promise<void> | void {\n const middleware = self.#middleware[self.#currentIndex++]\n\n /**\n * Empty stack\n */\n if (!middleware) {\n return self.#finalHandler()\n }\n\n return self.#executor(middleware, once(self, self.#invoke))\n }\n\n /**\n * Invokes middleware handlers with error handling.\n *\n * Similar to `#invoke`, but catches exceptions and passes them to the error handler.\n * When an exception is raised, the middleware downstream logic will not run unless\n * the error handler allows the chain to continue.\n *\n * @param self - The Runner instance scope\n * @returns A Promise that resolves when the middleware chain completes\n * @internal\n */\n #invokeWithErrorManagement(self: Runner<MiddlewareFn>): Promise<void> | void {\n const middleware = self.#middleware[self.#currentIndex++]\n\n /**\n * Empty stack\n */\n if (!middleware) {\n return self.#finalHandler().catch(self.#errorHandler)\n }\n\n return self\n .#executor(middleware, once(self, self.#invokeWithErrorManagement))\n .catch(self.#errorHandler)\n }\n\n /**\n * Sets a custom final handler to execute when the middleware chain completes successfully.\n *\n * The final handler is called when the entire middleware pipeline reaches the end\n * by calling `next` through all handlers. This makes it easier to execute custom\n * logic that is not part of the chain but must run when the chain ends.\n *\n * @param finalHandler - The function to execute when the chain completes\n * @returns The Runner instance for method chaining\n *\n * @example\n * ```ts\n * await middleware\n * .runner()\n * .finalHandler(() => {\n * console.log('All middleware completed')\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\n finalHandler(finalHandler: FinalHandler): this {\n this.#finalHandler = finalHandler\n return this\n }\n\n /**\n * Sets a custom error handler to catch exceptions in the middleware pipeline.\n *\n * By default, exceptions raised in the middleware pipeline bubble up to the `run`\n * method and can be captured using a try/catch block. When an exception is raised,\n * the middleware downstream logic will not run.\n *\n * Defining an error handler changes this behavior:\n * - The `run` method will not throw exceptions\n * - Errors are caught and passed to the error handler\n * - Middleware upstream logic (after `next`) can still execute\n *\n * @param errorHandler - The function to handle errors\n * @returns The Runner instance for method chaining\n *\n * @example\n * ```ts\n * await middleware\n * .runner()\n * .errorHandler((error) => {\n * console.error('Middleware error:', error)\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\n errorHandler(errorHandler: ErrorHandler): this {\n this.#errorHandler = errorHandler\n return this\n }\n\n /**\n * Executes the middleware pipeline using the provided executor function.\n *\n * The executor function is responsible for invoking each middleware handler with\n * the appropriate context and the `next` callback. Since you control the executor,\n * you can pass any data you want to the middleware.\n *\n * @param cb - The executor function that invokes each middleware handler\n * @returns A Promise that resolves when the middleware pipeline completes\n *\n * @example\n * ```ts\n * const context = { user: null }\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n * middleware.add(async (ctx, next) => {\n * ctx.user = await authenticate()\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\n async run(cb: Executor<MiddlewareFn>): Promise<void> {\n this.#executor = cb\n\n if (this.#errorHandler) {\n return this.#invokeWithErrorManagement(this)\n }\n\n return this.#invoke(this)\n }\n}\n","import { Runner } from './runner'\n\n/**\n * The Middleware class implements the chain of responsibility design pattern\n * and allows executing handlers in series.\n *\n * The middleware handlers can be represented as any value you wish, such as:\n * - A function: `middleware.add(function () { console.log('called') })`\n * - An object with a `handle` method: `middleware.add({ name: 'auth', handle: authenticate })`\n *\n * @template MiddlewareFn - The type of the middleware function/handler\n *\n * @example\n * ```ts\n * const context = {}\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n *\n * middleware.add((ctx, next) => {\n * console.log('executing middleware')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\nexport class Middleware<MiddlewareFn extends any> {\n #middleware: Set<MiddlewareFn> = new Set()\n #middlewareArray?: MiddlewareFn[]\n #isFrozen: boolean = false\n\n /**\n * Returns all registered middleware handlers.\n *\n * @returns A Set containing all registered middleware handlers\n */\n all() {\n return this.#middleware\n }\n\n /**\n * Checks if a specific handler has already been registered as middleware.\n *\n * @param handler - The middleware handler to check for\n * @returns `true` if the handler is registered, `false` otherwise\n */\n has(handler: MiddlewareFn): boolean {\n return this.#middleware.has(handler)\n }\n\n /**\n * Registers a new middleware handler to the pipeline.\n *\n * Adding the same middleware handler multiple times will result in a no-op,\n * as handlers are stored in a Set to prevent duplicates.\n *\n * @param handler - The middleware handler to register\n * @returns The Middleware instance for method chaining\n * @throws {Error} If the middleware stack is frozen\n */\n add(handler: MiddlewareFn): this {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot add new middleware')\n }\n\n this.#middleware.add(handler)\n return this\n }\n\n /**\n * Removes a specific middleware handler from the pipeline.\n *\n * @param handler - The middleware handler to remove\n * @returns `true` if the handler was removed, `false` if it was not found\n * @throws {Error} If the middleware stack is frozen\n */\n remove(handler: MiddlewareFn): boolean {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot remove middleware')\n }\n\n return this.#middleware.delete(handler)\n }\n\n /**\n * Removes all registered middleware handlers from the pipeline.\n *\n * @throws {Error} If the middleware stack is frozen\n */\n clear(): void {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot clear middleware')\n }\n\n this.#middleware.clear()\n }\n\n /**\n * Merges middleware handlers from another Middleware instance.\n *\n * The middleware from the source instance are appended to the current instance.\n *\n * @param hooks - The source Middleware instance to merge from\n * @throws {Error} If the middleware stack is frozen\n */\n merge(hooks: Middleware<MiddlewareFn>) {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot merge middleware')\n }\n\n hooks.all().forEach((handler) => {\n this.add(handler)\n })\n }\n\n /**\n * Freezes the middleware stack to prevent further modifications.\n *\n * Once frozen, the middleware array is cached and no new handlers can be added,\n * removed, or modified. This method is automatically called when creating a runner.\n */\n freeze() {\n if (this.#isFrozen) {\n return\n }\n\n this.#isFrozen = true\n this.#middlewareArray = [...this.all()]\n }\n\n /**\n * Creates and returns a Runner instance to execute the middleware pipeline.\n *\n * This method automatically freezes the middleware stack to prevent modifications\n * during execution.\n *\n * @returns A new Runner instance configured with the current middleware handlers\n */\n runner(): Runner<MiddlewareFn> {\n this.freeze()\n return new Runner(this.#middlewareArray!)\n }\n}\n","/**\n * @module @nhtio/middleware\n *\n * A cross-environment (browser/node) implementation of the chain of responsibility design pattern,\n * also known as the middleware pipeline. This package is based on\n * [@poppinss/middleware](https://github.com/poppinss/middleware) and provides a zero-dependency\n * implementation for executing handlers in series.\n *\n * @example\n * ```ts\n * import { Middleware } from '@nhtio/middleware'\n * import type { NextFn } from '@nhtio/middleware'\n *\n * const context = {}\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n *\n * middleware.add((ctx, next) => {\n * console.log('executing fn1')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\n\n/**\n * The current version of the package.\n *\n * This constant is replaced during the build process with the actual version from package.json.\n */\nexport const version = __VERSION__\n\nexport { Middleware } from './private/middleware'\nexport { Runner } from './private/runner'\nexport * from './private/types'\n"],"names":["once","scope","callback","next","DEFAULT_FINAL_HANDLER","Runner","middleware","__privateAdd","_Runner_instances","_middleware","_currentIndex","_executor","_finalHandler","_errorHandler","__privateSet","finalHandler","errorHandler","cb","__privateGet","__privateMethod","invokeWithErrorManagement_fn","invoke_fn","self","__privateWrapper","_b","_a","Middleware","_middlewareArray","_isFrozen","handler","hooks","version"],"mappings":"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"}
|
package/index.d.ts
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
/**
|
2
|
+
* @module @nhtio/middleware
|
3
|
+
*
|
4
|
+
* A cross-environment (browser/node) implementation of the chain of responsibility design pattern,
|
5
|
+
* also known as the middleware pipeline. This package is based on
|
6
|
+
* [@poppinss/middleware](https://github.com/poppinss/middleware) and provides a zero-dependency
|
7
|
+
* implementation for executing handlers in series.
|
8
|
+
*
|
9
|
+
* @example
|
10
|
+
* ```ts
|
11
|
+
* import { Middleware } from '@nhtio/middleware'
|
12
|
+
* import type { NextFn } from '@nhtio/middleware'
|
13
|
+
*
|
14
|
+
* const context = {}
|
15
|
+
* type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
|
16
|
+
*
|
17
|
+
* const middleware = new Middleware<MiddlewareFn>()
|
18
|
+
*
|
19
|
+
* middleware.add((ctx, next) => {
|
20
|
+
* console.log('executing fn1')
|
21
|
+
* await next()
|
22
|
+
* })
|
23
|
+
*
|
24
|
+
* await middleware
|
25
|
+
* .runner()
|
26
|
+
* .run((fn, next) => fn(context, next))
|
27
|
+
* ```
|
28
|
+
*/
|
29
|
+
/**
|
30
|
+
* The current version of the package.
|
31
|
+
*
|
32
|
+
* This constant is replaced during the build process with the actual version from package.json.
|
33
|
+
*/
|
34
|
+
export declare const version: string;
|
35
|
+
export { Middleware } from './private/middleware';
|
36
|
+
export { Runner } from './private/runner';
|
37
|
+
export * from './private/types';
|
package/index.mjs
ADDED
@@ -0,0 +1,264 @@
|
|
1
|
+
var x = (t) => {
|
2
|
+
throw TypeError(t);
|
3
|
+
};
|
4
|
+
var k = (t, r, e) => r.has(t) || x("Cannot " + e);
|
5
|
+
var n = (t, r, e) => (k(t, r, "read from private field"), e ? e.call(t) : r.get(t)), a = (t, r, e) => r.has(t) ? x("Cannot add the same private member more than once") : r instanceof WeakSet ? r.add(t) : r.set(t, e), s = (t, r, e, i) => (k(t, r, "write to private field"), i ? i.call(t, e) : r.set(t, e), e), f = (t, r, e) => (k(t, r, "access private method"), e);
|
6
|
+
var v = (t, r, e, i) => ({
|
7
|
+
set _(c) {
|
8
|
+
s(t, r, c, e);
|
9
|
+
},
|
10
|
+
get _() {
|
11
|
+
return n(t, r, i);
|
12
|
+
}
|
13
|
+
});
|
14
|
+
function A(t, r) {
|
15
|
+
function e() {
|
16
|
+
if (!e.called)
|
17
|
+
return e.called = !0, r(t);
|
18
|
+
}
|
19
|
+
return e.called = !1, e;
|
20
|
+
}
|
21
|
+
const C = () => Promise.resolve();
|
22
|
+
var u, E, w, m, l, h, M, H;
|
23
|
+
class g {
|
24
|
+
constructor(r) {
|
25
|
+
a(this, h);
|
26
|
+
/**
|
27
|
+
* The array of middleware handlers to execute in sequence.
|
28
|
+
*/
|
29
|
+
a(this, u);
|
30
|
+
/**
|
31
|
+
* The current index position in the middleware pipeline.
|
32
|
+
*/
|
33
|
+
a(this, E, 0);
|
34
|
+
/**
|
35
|
+
* The executor function responsible for invoking each middleware handler.
|
36
|
+
*/
|
37
|
+
a(this, w);
|
38
|
+
/**
|
39
|
+
* The final handler to execute when the middleware chain completes successfully.
|
40
|
+
*/
|
41
|
+
a(this, m, C);
|
42
|
+
/**
|
43
|
+
* Optional error handler to catch and handle exceptions in the middleware pipeline.
|
44
|
+
*/
|
45
|
+
a(this, l);
|
46
|
+
s(this, u, r);
|
47
|
+
}
|
48
|
+
/**
|
49
|
+
* Sets a custom final handler to execute when the middleware chain completes successfully.
|
50
|
+
*
|
51
|
+
* The final handler is called when the entire middleware pipeline reaches the end
|
52
|
+
* by calling `next` through all handlers. This makes it easier to execute custom
|
53
|
+
* logic that is not part of the chain but must run when the chain ends.
|
54
|
+
*
|
55
|
+
* @param finalHandler - The function to execute when the chain completes
|
56
|
+
* @returns The Runner instance for method chaining
|
57
|
+
*
|
58
|
+
* @example
|
59
|
+
* ```ts
|
60
|
+
* await middleware
|
61
|
+
* .runner()
|
62
|
+
* .finalHandler(() => {
|
63
|
+
* console.log('All middleware completed')
|
64
|
+
* })
|
65
|
+
* .run((fn, next) => fn(context, next))
|
66
|
+
* ```
|
67
|
+
*/
|
68
|
+
finalHandler(r) {
|
69
|
+
return s(this, m, r), this;
|
70
|
+
}
|
71
|
+
/**
|
72
|
+
* Sets a custom error handler to catch exceptions in the middleware pipeline.
|
73
|
+
*
|
74
|
+
* By default, exceptions raised in the middleware pipeline bubble up to the `run`
|
75
|
+
* method and can be captured using a try/catch block. When an exception is raised,
|
76
|
+
* the middleware downstream logic will not run.
|
77
|
+
*
|
78
|
+
* Defining an error handler changes this behavior:
|
79
|
+
* - The `run` method will not throw exceptions
|
80
|
+
* - Errors are caught and passed to the error handler
|
81
|
+
* - Middleware upstream logic (after `next`) can still execute
|
82
|
+
*
|
83
|
+
* @param errorHandler - The function to handle errors
|
84
|
+
* @returns The Runner instance for method chaining
|
85
|
+
*
|
86
|
+
* @example
|
87
|
+
* ```ts
|
88
|
+
* await middleware
|
89
|
+
* .runner()
|
90
|
+
* .errorHandler((error) => {
|
91
|
+
* console.error('Middleware error:', error)
|
92
|
+
* })
|
93
|
+
* .run((fn, next) => fn(context, next))
|
94
|
+
* ```
|
95
|
+
*/
|
96
|
+
errorHandler(r) {
|
97
|
+
return s(this, l, r), this;
|
98
|
+
}
|
99
|
+
/**
|
100
|
+
* Executes the middleware pipeline using the provided executor function.
|
101
|
+
*
|
102
|
+
* The executor function is responsible for invoking each middleware handler with
|
103
|
+
* the appropriate context and the `next` callback. Since you control the executor,
|
104
|
+
* you can pass any data you want to the middleware.
|
105
|
+
*
|
106
|
+
* @param cb - The executor function that invokes each middleware handler
|
107
|
+
* @returns A Promise that resolves when the middleware pipeline completes
|
108
|
+
*
|
109
|
+
* @example
|
110
|
+
* ```ts
|
111
|
+
* const context = { user: null }
|
112
|
+
* type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
|
113
|
+
*
|
114
|
+
* const middleware = new Middleware<MiddlewareFn>()
|
115
|
+
* middleware.add(async (ctx, next) => {
|
116
|
+
* ctx.user = await authenticate()
|
117
|
+
* await next()
|
118
|
+
* })
|
119
|
+
*
|
120
|
+
* await middleware
|
121
|
+
* .runner()
|
122
|
+
* .run((fn, next) => fn(context, next))
|
123
|
+
* ```
|
124
|
+
*/
|
125
|
+
async run(r) {
|
126
|
+
return s(this, w, r), n(this, l) ? f(this, h, H).call(this, this) : f(this, h, M).call(this, this);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
u = new WeakMap(), E = new WeakMap(), w = new WeakMap(), m = new WeakMap(), l = new WeakMap(), h = new WeakSet(), /**
|
130
|
+
* Invokes middleware handlers one at a time.
|
131
|
+
*
|
132
|
+
* Middleware functions are executed recursively until `next` is called.
|
133
|
+
* If a middleware doesn't call `next`, the chain will finish automatically
|
134
|
+
* without executing remaining handlers.
|
135
|
+
*
|
136
|
+
* @param self - The Runner instance scope
|
137
|
+
* @returns A Promise that resolves when the middleware chain completes
|
138
|
+
* @internal
|
139
|
+
*/
|
140
|
+
M = function(r) {
|
141
|
+
var i, c;
|
142
|
+
const e = n(r, u)[v(r, E)._++];
|
143
|
+
return e ? n(c = r, w).call(c, e, A(r, f(r, h, M))) : n(i = r, m).call(i);
|
144
|
+
}, /**
|
145
|
+
* Invokes middleware handlers with error handling.
|
146
|
+
*
|
147
|
+
* Similar to `#invoke`, but catches exceptions and passes them to the error handler.
|
148
|
+
* When an exception is raised, the middleware downstream logic will not run unless
|
149
|
+
* the error handler allows the chain to continue.
|
150
|
+
*
|
151
|
+
* @param self - The Runner instance scope
|
152
|
+
* @returns A Promise that resolves when the middleware chain completes
|
153
|
+
* @internal
|
154
|
+
*/
|
155
|
+
H = function(r) {
|
156
|
+
var i, c;
|
157
|
+
const e = n(r, u)[v(r, E)._++];
|
158
|
+
return e ? n(c = r, w).call(c, e, A(r, f(r, h, H))).catch(n(r, l)) : n(i = r, m).call(i).catch(n(r, l));
|
159
|
+
};
|
160
|
+
var o, z, d;
|
161
|
+
class L {
|
162
|
+
constructor() {
|
163
|
+
a(this, o, /* @__PURE__ */ new Set());
|
164
|
+
a(this, z);
|
165
|
+
a(this, d, !1);
|
166
|
+
}
|
167
|
+
/**
|
168
|
+
* Returns all registered middleware handlers.
|
169
|
+
*
|
170
|
+
* @returns A Set containing all registered middleware handlers
|
171
|
+
*/
|
172
|
+
all() {
|
173
|
+
return n(this, o);
|
174
|
+
}
|
175
|
+
/**
|
176
|
+
* Checks if a specific handler has already been registered as middleware.
|
177
|
+
*
|
178
|
+
* @param handler - The middleware handler to check for
|
179
|
+
* @returns `true` if the handler is registered, `false` otherwise
|
180
|
+
*/
|
181
|
+
has(r) {
|
182
|
+
return n(this, o).has(r);
|
183
|
+
}
|
184
|
+
/**
|
185
|
+
* Registers a new middleware handler to the pipeline.
|
186
|
+
*
|
187
|
+
* Adding the same middleware handler multiple times will result in a no-op,
|
188
|
+
* as handlers are stored in a Set to prevent duplicates.
|
189
|
+
*
|
190
|
+
* @param handler - The middleware handler to register
|
191
|
+
* @returns The Middleware instance for method chaining
|
192
|
+
* @throws {Error} If the middleware stack is frozen
|
193
|
+
*/
|
194
|
+
add(r) {
|
195
|
+
if (n(this, d))
|
196
|
+
throw new Error("Middleware stack is frozen. Cannot add new middleware");
|
197
|
+
return n(this, o).add(r), this;
|
198
|
+
}
|
199
|
+
/**
|
200
|
+
* Removes a specific middleware handler from the pipeline.
|
201
|
+
*
|
202
|
+
* @param handler - The middleware handler to remove
|
203
|
+
* @returns `true` if the handler was removed, `false` if it was not found
|
204
|
+
* @throws {Error} If the middleware stack is frozen
|
205
|
+
*/
|
206
|
+
remove(r) {
|
207
|
+
if (n(this, d))
|
208
|
+
throw new Error("Middleware stack is frozen. Cannot remove middleware");
|
209
|
+
return n(this, o).delete(r);
|
210
|
+
}
|
211
|
+
/**
|
212
|
+
* Removes all registered middleware handlers from the pipeline.
|
213
|
+
*
|
214
|
+
* @throws {Error} If the middleware stack is frozen
|
215
|
+
*/
|
216
|
+
clear() {
|
217
|
+
if (n(this, d))
|
218
|
+
throw new Error("Middleware stack is frozen. Cannot clear middleware");
|
219
|
+
n(this, o).clear();
|
220
|
+
}
|
221
|
+
/**
|
222
|
+
* Merges middleware handlers from another Middleware instance.
|
223
|
+
*
|
224
|
+
* The middleware from the source instance are appended to the current instance.
|
225
|
+
*
|
226
|
+
* @param hooks - The source Middleware instance to merge from
|
227
|
+
* @throws {Error} If the middleware stack is frozen
|
228
|
+
*/
|
229
|
+
merge(r) {
|
230
|
+
if (n(this, d))
|
231
|
+
throw new Error("Middleware stack is frozen. Cannot merge middleware");
|
232
|
+
r.all().forEach((e) => {
|
233
|
+
this.add(e);
|
234
|
+
});
|
235
|
+
}
|
236
|
+
/**
|
237
|
+
* Freezes the middleware stack to prevent further modifications.
|
238
|
+
*
|
239
|
+
* Once frozen, the middleware array is cached and no new handlers can be added,
|
240
|
+
* removed, or modified. This method is automatically called when creating a runner.
|
241
|
+
*/
|
242
|
+
freeze() {
|
243
|
+
n(this, d) || (s(this, d, !0), s(this, z, [...this.all()]));
|
244
|
+
}
|
245
|
+
/**
|
246
|
+
* Creates and returns a Runner instance to execute the middleware pipeline.
|
247
|
+
*
|
248
|
+
* This method automatically freezes the middleware stack to prevent modifications
|
249
|
+
* during execution.
|
250
|
+
*
|
251
|
+
* @returns A new Runner instance configured with the current middleware handlers
|
252
|
+
*/
|
253
|
+
runner() {
|
254
|
+
return this.freeze(), new g(n(this, z));
|
255
|
+
}
|
256
|
+
}
|
257
|
+
o = new WeakMap(), z = new WeakMap(), d = new WeakMap();
|
258
|
+
const y = "0.1.0-master-d3ec5be7";
|
259
|
+
export {
|
260
|
+
L as Middleware,
|
261
|
+
g as Runner,
|
262
|
+
y as version
|
263
|
+
};
|
264
|
+
//# sourceMappingURL=index.mjs.map
|
package/index.mjs.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/private/runner.ts","../src/private/middleware.ts","../src/index.ts"],"sourcesContent":["import type { ErrorHandler, Executor, FinalHandler } from './types'\n\n/**\n * Ensures a function is executed only once by maintaining a called state.\n *\n * This utility function is tightly coupled with the Runner class and is used\n * to ensure the `next` callback is not invoked multiple times in the middleware chain.\n *\n * @param scope - The Runner instance scope\n * @param callback - The function to execute only once\n * @returns A wrapped function that tracks its execution state\n * @internal\n */\nfunction once(scope: Runner<any>, callback: (scope: Runner<any>) => Promise<void> | void) {\n function next(): Promise<void> | void {\n if (next.called) {\n return\n }\n\n next.called = true\n return callback(scope)\n }\n next.called = false\n\n return next\n}\n\n/**\n * Default final handler that resolves immediately when the middleware chain completes.\n *\n * This handler is used when no custom final handler is specified via `finalHandler()`.\n *\n * @internal\n */\nconst DEFAULT_FINAL_HANDLER = () => Promise.resolve()\n\n/**\n * Runner executes an array of middleware functions in sequence.\n *\n * The middleware pipeline advances only when the current function calls `next`,\n * implementing the chain of responsibility pattern. You control how to execute\n * the underlying middleware functions by providing an executor function.\n *\n * @template MiddlewareFn - The type of the middleware function/handler\n *\n * @example\n * ```ts\n * const context = { stack: [] }\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n * middleware.add((ctx, next) => {\n * ctx.stack.push('fn1')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .finalHandler(() => {\n * context.stack.push('final handler')\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\nexport class Runner<MiddlewareFn extends any> {\n /**\n * The array of middleware handlers to execute in sequence.\n */\n #middleware: MiddlewareFn[]\n\n /**\n * The current index position in the middleware pipeline.\n */\n #currentIndex = 0\n\n /**\n * The executor function responsible for invoking each middleware handler.\n */\n #executor!: Executor<MiddlewareFn>\n\n /**\n * The final handler to execute when the middleware chain completes successfully.\n */\n #finalHandler: FinalHandler = DEFAULT_FINAL_HANDLER\n\n /**\n * Optional error handler to catch and handle exceptions in the middleware pipeline.\n */\n #errorHandler?: ErrorHandler\n\n constructor(middleware: MiddlewareFn[]) {\n this.#middleware = middleware\n }\n\n /**\n * Invokes middleware handlers one at a time.\n *\n * Middleware functions are executed recursively until `next` is called.\n * If a middleware doesn't call `next`, the chain will finish automatically\n * without executing remaining handlers.\n *\n * @param self - The Runner instance scope\n * @returns A Promise that resolves when the middleware chain completes\n * @internal\n */\n #invoke(self: Runner<MiddlewareFn>): Promise<void> | void {\n const middleware = self.#middleware[self.#currentIndex++]\n\n /**\n * Empty stack\n */\n if (!middleware) {\n return self.#finalHandler()\n }\n\n return self.#executor(middleware, once(self, self.#invoke))\n }\n\n /**\n * Invokes middleware handlers with error handling.\n *\n * Similar to `#invoke`, but catches exceptions and passes them to the error handler.\n * When an exception is raised, the middleware downstream logic will not run unless\n * the error handler allows the chain to continue.\n *\n * @param self - The Runner instance scope\n * @returns A Promise that resolves when the middleware chain completes\n * @internal\n */\n #invokeWithErrorManagement(self: Runner<MiddlewareFn>): Promise<void> | void {\n const middleware = self.#middleware[self.#currentIndex++]\n\n /**\n * Empty stack\n */\n if (!middleware) {\n return self.#finalHandler().catch(self.#errorHandler)\n }\n\n return self\n .#executor(middleware, once(self, self.#invokeWithErrorManagement))\n .catch(self.#errorHandler)\n }\n\n /**\n * Sets a custom final handler to execute when the middleware chain completes successfully.\n *\n * The final handler is called when the entire middleware pipeline reaches the end\n * by calling `next` through all handlers. This makes it easier to execute custom\n * logic that is not part of the chain but must run when the chain ends.\n *\n * @param finalHandler - The function to execute when the chain completes\n * @returns The Runner instance for method chaining\n *\n * @example\n * ```ts\n * await middleware\n * .runner()\n * .finalHandler(() => {\n * console.log('All middleware completed')\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\n finalHandler(finalHandler: FinalHandler): this {\n this.#finalHandler = finalHandler\n return this\n }\n\n /**\n * Sets a custom error handler to catch exceptions in the middleware pipeline.\n *\n * By default, exceptions raised in the middleware pipeline bubble up to the `run`\n * method and can be captured using a try/catch block. When an exception is raised,\n * the middleware downstream logic will not run.\n *\n * Defining an error handler changes this behavior:\n * - The `run` method will not throw exceptions\n * - Errors are caught and passed to the error handler\n * - Middleware upstream logic (after `next`) can still execute\n *\n * @param errorHandler - The function to handle errors\n * @returns The Runner instance for method chaining\n *\n * @example\n * ```ts\n * await middleware\n * .runner()\n * .errorHandler((error) => {\n * console.error('Middleware error:', error)\n * })\n * .run((fn, next) => fn(context, next))\n * ```\n */\n errorHandler(errorHandler: ErrorHandler): this {\n this.#errorHandler = errorHandler\n return this\n }\n\n /**\n * Executes the middleware pipeline using the provided executor function.\n *\n * The executor function is responsible for invoking each middleware handler with\n * the appropriate context and the `next` callback. Since you control the executor,\n * you can pass any data you want to the middleware.\n *\n * @param cb - The executor function that invokes each middleware handler\n * @returns A Promise that resolves when the middleware pipeline completes\n *\n * @example\n * ```ts\n * const context = { user: null }\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n * middleware.add(async (ctx, next) => {\n * ctx.user = await authenticate()\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\n async run(cb: Executor<MiddlewareFn>): Promise<void> {\n this.#executor = cb\n\n if (this.#errorHandler) {\n return this.#invokeWithErrorManagement(this)\n }\n\n return this.#invoke(this)\n }\n}\n","import { Runner } from './runner'\n\n/**\n * The Middleware class implements the chain of responsibility design pattern\n * and allows executing handlers in series.\n *\n * The middleware handlers can be represented as any value you wish, such as:\n * - A function: `middleware.add(function () { console.log('called') })`\n * - An object with a `handle` method: `middleware.add({ name: 'auth', handle: authenticate })`\n *\n * @template MiddlewareFn - The type of the middleware function/handler\n *\n * @example\n * ```ts\n * const context = {}\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n *\n * middleware.add((ctx, next) => {\n * console.log('executing middleware')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\nexport class Middleware<MiddlewareFn extends any> {\n #middleware: Set<MiddlewareFn> = new Set()\n #middlewareArray?: MiddlewareFn[]\n #isFrozen: boolean = false\n\n /**\n * Returns all registered middleware handlers.\n *\n * @returns A Set containing all registered middleware handlers\n */\n all() {\n return this.#middleware\n }\n\n /**\n * Checks if a specific handler has already been registered as middleware.\n *\n * @param handler - The middleware handler to check for\n * @returns `true` if the handler is registered, `false` otherwise\n */\n has(handler: MiddlewareFn): boolean {\n return this.#middleware.has(handler)\n }\n\n /**\n * Registers a new middleware handler to the pipeline.\n *\n * Adding the same middleware handler multiple times will result in a no-op,\n * as handlers are stored in a Set to prevent duplicates.\n *\n * @param handler - The middleware handler to register\n * @returns The Middleware instance for method chaining\n * @throws {Error} If the middleware stack is frozen\n */\n add(handler: MiddlewareFn): this {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot add new middleware')\n }\n\n this.#middleware.add(handler)\n return this\n }\n\n /**\n * Removes a specific middleware handler from the pipeline.\n *\n * @param handler - The middleware handler to remove\n * @returns `true` if the handler was removed, `false` if it was not found\n * @throws {Error} If the middleware stack is frozen\n */\n remove(handler: MiddlewareFn): boolean {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot remove middleware')\n }\n\n return this.#middleware.delete(handler)\n }\n\n /**\n * Removes all registered middleware handlers from the pipeline.\n *\n * @throws {Error} If the middleware stack is frozen\n */\n clear(): void {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot clear middleware')\n }\n\n this.#middleware.clear()\n }\n\n /**\n * Merges middleware handlers from another Middleware instance.\n *\n * The middleware from the source instance are appended to the current instance.\n *\n * @param hooks - The source Middleware instance to merge from\n * @throws {Error} If the middleware stack is frozen\n */\n merge(hooks: Middleware<MiddlewareFn>) {\n if (this.#isFrozen) {\n throw new Error('Middleware stack is frozen. Cannot merge middleware')\n }\n\n hooks.all().forEach((handler) => {\n this.add(handler)\n })\n }\n\n /**\n * Freezes the middleware stack to prevent further modifications.\n *\n * Once frozen, the middleware array is cached and no new handlers can be added,\n * removed, or modified. This method is automatically called when creating a runner.\n */\n freeze() {\n if (this.#isFrozen) {\n return\n }\n\n this.#isFrozen = true\n this.#middlewareArray = [...this.all()]\n }\n\n /**\n * Creates and returns a Runner instance to execute the middleware pipeline.\n *\n * This method automatically freezes the middleware stack to prevent modifications\n * during execution.\n *\n * @returns A new Runner instance configured with the current middleware handlers\n */\n runner(): Runner<MiddlewareFn> {\n this.freeze()\n return new Runner(this.#middlewareArray!)\n }\n}\n","/**\n * @module @nhtio/middleware\n *\n * A cross-environment (browser/node) implementation of the chain of responsibility design pattern,\n * also known as the middleware pipeline. This package is based on\n * [@poppinss/middleware](https://github.com/poppinss/middleware) and provides a zero-dependency\n * implementation for executing handlers in series.\n *\n * @example\n * ```ts\n * import { Middleware } from '@nhtio/middleware'\n * import type { NextFn } from '@nhtio/middleware'\n *\n * const context = {}\n * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>\n *\n * const middleware = new Middleware<MiddlewareFn>()\n *\n * middleware.add((ctx, next) => {\n * console.log('executing fn1')\n * await next()\n * })\n *\n * await middleware\n * .runner()\n * .run((fn, next) => fn(context, next))\n * ```\n */\n\n/**\n * The current version of the package.\n *\n * This constant is replaced during the build process with the actual version from package.json.\n */\nexport const version = __VERSION__\n\nexport { Middleware } from './private/middleware'\nexport { Runner } from './private/runner'\nexport * from './private/types'\n"],"names":["once","scope","callback","next","DEFAULT_FINAL_HANDLER","_middleware","_currentIndex","_executor","_finalHandler","_errorHandler","_Runner_instances","invoke_fn","invokeWithErrorManagement_fn","Runner","middleware","__privateAdd","__privateSet","finalHandler","errorHandler","cb","__privateGet","__privateMethod","self","_a","_b","__privateWrapper","_middlewareArray","_isFrozen","Middleware","handler","hooks","version"],"mappings":"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"}
|
package/package.json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"name": "@nhtio/middleware",
|
3
|
+
"version": "0.1.0-master-d3ec5be7",
|
4
|
+
"description": "A cross-environment (browser/node) implementation of the chain of responsibility design pattern based on @poppinss/middleware",
|
5
|
+
"keywords": [],
|
6
|
+
"author": "Jak Giveon <jak@nht.io>",
|
7
|
+
"copyright": "© 2025-present New Horizon Technology LTD",
|
8
|
+
"license": "MIT",
|
9
|
+
"module": "./index.mjs",
|
10
|
+
"main": "./index.cjs",
|
11
|
+
"exports": {
|
12
|
+
".": {
|
13
|
+
"import": "./index.mjs",
|
14
|
+
"require": "./index.cjs",
|
15
|
+
"types": "./index.d.ts"
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"packageManager": "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971",
|
19
|
+
"type": "module",
|
20
|
+
"dependencies": {}
|
21
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import { Runner } from './runner';
|
2
|
+
/**
|
3
|
+
* The Middleware class implements the chain of responsibility design pattern
|
4
|
+
* and allows executing handlers in series.
|
5
|
+
*
|
6
|
+
* The middleware handlers can be represented as any value you wish, such as:
|
7
|
+
* - A function: `middleware.add(function () { console.log('called') })`
|
8
|
+
* - An object with a `handle` method: `middleware.add({ name: 'auth', handle: authenticate })`
|
9
|
+
*
|
10
|
+
* @template MiddlewareFn - The type of the middleware function/handler
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```ts
|
14
|
+
* const context = {}
|
15
|
+
* type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
|
16
|
+
*
|
17
|
+
* const middleware = new Middleware<MiddlewareFn>()
|
18
|
+
*
|
19
|
+
* middleware.add((ctx, next) => {
|
20
|
+
* console.log('executing middleware')
|
21
|
+
* await next()
|
22
|
+
* })
|
23
|
+
*
|
24
|
+
* await middleware
|
25
|
+
* .runner()
|
26
|
+
* .run((fn, next) => fn(context, next))
|
27
|
+
* ```
|
28
|
+
*/
|
29
|
+
export declare class Middleware<MiddlewareFn extends any> {
|
30
|
+
#private;
|
31
|
+
/**
|
32
|
+
* Returns all registered middleware handlers.
|
33
|
+
*
|
34
|
+
* @returns A Set containing all registered middleware handlers
|
35
|
+
*/
|
36
|
+
all(): Set<MiddlewareFn>;
|
37
|
+
/**
|
38
|
+
* Checks if a specific handler has already been registered as middleware.
|
39
|
+
*
|
40
|
+
* @param handler - The middleware handler to check for
|
41
|
+
* @returns `true` if the handler is registered, `false` otherwise
|
42
|
+
*/
|
43
|
+
has(handler: MiddlewareFn): boolean;
|
44
|
+
/**
|
45
|
+
* Registers a new middleware handler to the pipeline.
|
46
|
+
*
|
47
|
+
* Adding the same middleware handler multiple times will result in a no-op,
|
48
|
+
* as handlers are stored in a Set to prevent duplicates.
|
49
|
+
*
|
50
|
+
* @param handler - The middleware handler to register
|
51
|
+
* @returns The Middleware instance for method chaining
|
52
|
+
* @throws {Error} If the middleware stack is frozen
|
53
|
+
*/
|
54
|
+
add(handler: MiddlewareFn): this;
|
55
|
+
/**
|
56
|
+
* Removes a specific middleware handler from the pipeline.
|
57
|
+
*
|
58
|
+
* @param handler - The middleware handler to remove
|
59
|
+
* @returns `true` if the handler was removed, `false` if it was not found
|
60
|
+
* @throws {Error} If the middleware stack is frozen
|
61
|
+
*/
|
62
|
+
remove(handler: MiddlewareFn): boolean;
|
63
|
+
/**
|
64
|
+
* Removes all registered middleware handlers from the pipeline.
|
65
|
+
*
|
66
|
+
* @throws {Error} If the middleware stack is frozen
|
67
|
+
*/
|
68
|
+
clear(): void;
|
69
|
+
/**
|
70
|
+
* Merges middleware handlers from another Middleware instance.
|
71
|
+
*
|
72
|
+
* The middleware from the source instance are appended to the current instance.
|
73
|
+
*
|
74
|
+
* @param hooks - The source Middleware instance to merge from
|
75
|
+
* @throws {Error} If the middleware stack is frozen
|
76
|
+
*/
|
77
|
+
merge(hooks: Middleware<MiddlewareFn>): void;
|
78
|
+
/**
|
79
|
+
* Freezes the middleware stack to prevent further modifications.
|
80
|
+
*
|
81
|
+
* Once frozen, the middleware array is cached and no new handlers can be added,
|
82
|
+
* removed, or modified. This method is automatically called when creating a runner.
|
83
|
+
*/
|
84
|
+
freeze(): void;
|
85
|
+
/**
|
86
|
+
* Creates and returns a Runner instance to execute the middleware pipeline.
|
87
|
+
*
|
88
|
+
* This method automatically freezes the middleware stack to prevent modifications
|
89
|
+
* during execution.
|
90
|
+
*
|
91
|
+
* @returns A new Runner instance configured with the current middleware handlers
|
92
|
+
*/
|
93
|
+
runner(): Runner<MiddlewareFn>;
|
94
|
+
}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
import type { ErrorHandler, Executor, FinalHandler } from './types';
|
2
|
+
/**
|
3
|
+
* Runner executes an array of middleware functions in sequence.
|
4
|
+
*
|
5
|
+
* The middleware pipeline advances only when the current function calls `next`,
|
6
|
+
* implementing the chain of responsibility pattern. You control how to execute
|
7
|
+
* the underlying middleware functions by providing an executor function.
|
8
|
+
*
|
9
|
+
* @template MiddlewareFn - The type of the middleware function/handler
|
10
|
+
*
|
11
|
+
* @example
|
12
|
+
* ```ts
|
13
|
+
* const context = { stack: [] }
|
14
|
+
* type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
|
15
|
+
*
|
16
|
+
* const middleware = new Middleware<MiddlewareFn>()
|
17
|
+
* middleware.add((ctx, next) => {
|
18
|
+
* ctx.stack.push('fn1')
|
19
|
+
* await next()
|
20
|
+
* })
|
21
|
+
*
|
22
|
+
* await middleware
|
23
|
+
* .runner()
|
24
|
+
* .finalHandler(() => {
|
25
|
+
* context.stack.push('final handler')
|
26
|
+
* })
|
27
|
+
* .run((fn, next) => fn(context, next))
|
28
|
+
* ```
|
29
|
+
*/
|
30
|
+
export declare class Runner<MiddlewareFn extends any> {
|
31
|
+
#private;
|
32
|
+
constructor(middleware: MiddlewareFn[]);
|
33
|
+
/**
|
34
|
+
* Sets a custom final handler to execute when the middleware chain completes successfully.
|
35
|
+
*
|
36
|
+
* The final handler is called when the entire middleware pipeline reaches the end
|
37
|
+
* by calling `next` through all handlers. This makes it easier to execute custom
|
38
|
+
* logic that is not part of the chain but must run when the chain ends.
|
39
|
+
*
|
40
|
+
* @param finalHandler - The function to execute when the chain completes
|
41
|
+
* @returns The Runner instance for method chaining
|
42
|
+
*
|
43
|
+
* @example
|
44
|
+
* ```ts
|
45
|
+
* await middleware
|
46
|
+
* .runner()
|
47
|
+
* .finalHandler(() => {
|
48
|
+
* console.log('All middleware completed')
|
49
|
+
* })
|
50
|
+
* .run((fn, next) => fn(context, next))
|
51
|
+
* ```
|
52
|
+
*/
|
53
|
+
finalHandler(finalHandler: FinalHandler): this;
|
54
|
+
/**
|
55
|
+
* Sets a custom error handler to catch exceptions in the middleware pipeline.
|
56
|
+
*
|
57
|
+
* By default, exceptions raised in the middleware pipeline bubble up to the `run`
|
58
|
+
* method and can be captured using a try/catch block. When an exception is raised,
|
59
|
+
* the middleware downstream logic will not run.
|
60
|
+
*
|
61
|
+
* Defining an error handler changes this behavior:
|
62
|
+
* - The `run` method will not throw exceptions
|
63
|
+
* - Errors are caught and passed to the error handler
|
64
|
+
* - Middleware upstream logic (after `next`) can still execute
|
65
|
+
*
|
66
|
+
* @param errorHandler - The function to handle errors
|
67
|
+
* @returns The Runner instance for method chaining
|
68
|
+
*
|
69
|
+
* @example
|
70
|
+
* ```ts
|
71
|
+
* await middleware
|
72
|
+
* .runner()
|
73
|
+
* .errorHandler((error) => {
|
74
|
+
* console.error('Middleware error:', error)
|
75
|
+
* })
|
76
|
+
* .run((fn, next) => fn(context, next))
|
77
|
+
* ```
|
78
|
+
*/
|
79
|
+
errorHandler(errorHandler: ErrorHandler): this;
|
80
|
+
/**
|
81
|
+
* Executes the middleware pipeline using the provided executor function.
|
82
|
+
*
|
83
|
+
* The executor function is responsible for invoking each middleware handler with
|
84
|
+
* the appropriate context and the `next` callback. Since you control the executor,
|
85
|
+
* you can pass any data you want to the middleware.
|
86
|
+
*
|
87
|
+
* @param cb - The executor function that invokes each middleware handler
|
88
|
+
* @returns A Promise that resolves when the middleware pipeline completes
|
89
|
+
*
|
90
|
+
* @example
|
91
|
+
* ```ts
|
92
|
+
* const context = { user: null }
|
93
|
+
* type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
|
94
|
+
*
|
95
|
+
* const middleware = new Middleware<MiddlewareFn>()
|
96
|
+
* middleware.add(async (ctx, next) => {
|
97
|
+
* ctx.user = await authenticate()
|
98
|
+
* await next()
|
99
|
+
* })
|
100
|
+
*
|
101
|
+
* await middleware
|
102
|
+
* .runner()
|
103
|
+
* .run((fn, next) => fn(context, next))
|
104
|
+
* ```
|
105
|
+
*/
|
106
|
+
run(cb: Executor<MiddlewareFn>): Promise<void>;
|
107
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
/**
|
2
|
+
* The next function used to advance the middleware pipeline.
|
3
|
+
*
|
4
|
+
* When called, it triggers the execution of the next middleware in the chain.
|
5
|
+
* If not called, the middleware chain stops at the current handler.
|
6
|
+
*/
|
7
|
+
export type NextFn = () => Promise<any> | any;
|
8
|
+
/**
|
9
|
+
* Final handler called when the entire middleware chain completes successfully.
|
10
|
+
*
|
11
|
+
* The final handler is executed after all middleware have called `next` and the
|
12
|
+
* pipeline reaches its end. It provides a way to execute custom logic outside
|
13
|
+
* the middleware chain when the chain completes.
|
14
|
+
*/
|
15
|
+
export type FinalHandler = () => Promise<any>;
|
16
|
+
/**
|
17
|
+
* Error handler called when any middleware in the pipeline raises an exception.
|
18
|
+
*
|
19
|
+
* When defined, errors are caught and passed to this handler instead of bubbling
|
20
|
+
* up to the `run` method. This allows you to implement custom error handling logic
|
21
|
+
* and optionally resume the downstream flow of middleware.
|
22
|
+
*
|
23
|
+
* @param error - The error thrown by the middleware
|
24
|
+
*/
|
25
|
+
export type ErrorHandler = (error: any) => Promise<any>;
|
26
|
+
/**
|
27
|
+
* The executor function responsible for invoking each middleware handler.
|
28
|
+
*
|
29
|
+
* The executor gives you control over how middleware functions are called,
|
30
|
+
* allowing you to pass custom context and parameters to each handler.
|
31
|
+
*
|
32
|
+
* @template MiddlewareFn - The type of the middleware function/handler
|
33
|
+
* @param middleware - The current middleware handler to execute
|
34
|
+
* @param next - The next function to advance the pipeline
|
35
|
+
*/
|
36
|
+
export type Executor<MiddlewareFn extends any> = (middleware: MiddlewareFn, next: NextFn) => Promise<any>;
|