@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 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>;