@nhtio/middleware 1.20251013.0 → 1.20251213.0

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/index.cjs CHANGED
@@ -1,2 +1,270 @@
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 x{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 F="1.20251013.0";exports.Middleware=x;exports.Runner=A;exports.version=F;
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function once(scope, callback) {
4
+ function next() {
5
+ if (next.called) {
6
+ return;
7
+ }
8
+ next.called = true;
9
+ return callback(scope);
10
+ }
11
+ next.called = false;
12
+ return next;
13
+ }
14
+ const DEFAULT_FINAL_HANDLER = () => Promise.resolve();
15
+ class Runner {
16
+ /**
17
+ * The array of middleware handlers to execute in sequence.
18
+ */
19
+ #middleware;
20
+ /**
21
+ * The current index position in the middleware pipeline.
22
+ */
23
+ #currentIndex = 0;
24
+ /**
25
+ * The executor function responsible for invoking each middleware handler.
26
+ */
27
+ #executor;
28
+ /**
29
+ * The final handler to execute when the middleware chain completes successfully.
30
+ */
31
+ #finalHandler = DEFAULT_FINAL_HANDLER;
32
+ /**
33
+ * Optional error handler to catch and handle exceptions in the middleware pipeline.
34
+ */
35
+ #errorHandler;
36
+ constructor(middleware) {
37
+ this.#middleware = middleware;
38
+ }
39
+ /**
40
+ * Invokes middleware handlers one at a time.
41
+ *
42
+ * Middleware functions are executed recursively until `next` is called.
43
+ * If a middleware doesn't call `next`, the chain will finish automatically
44
+ * without executing remaining handlers.
45
+ *
46
+ * @param self - The Runner instance scope
47
+ * @returns A Promise that resolves when the middleware chain completes
48
+ * @internal
49
+ */
50
+ #invoke(self) {
51
+ const middleware = self.#middleware[self.#currentIndex++];
52
+ if (!middleware) {
53
+ return self.#finalHandler();
54
+ }
55
+ return self.#executor(middleware, once(self, self.#invoke));
56
+ }
57
+ /**
58
+ * Invokes middleware handlers with error handling.
59
+ *
60
+ * Similar to `#invoke`, but catches exceptions and passes them to the error handler.
61
+ * When an exception is raised, the middleware downstream logic will not run unless
62
+ * the error handler allows the chain to continue.
63
+ *
64
+ * @param self - The Runner instance scope
65
+ * @returns A Promise that resolves when the middleware chain completes
66
+ * @internal
67
+ */
68
+ #invokeWithErrorManagement(self) {
69
+ const middleware = self.#middleware[self.#currentIndex++];
70
+ if (!middleware) {
71
+ return self.#finalHandler().catch(self.#errorHandler);
72
+ }
73
+ return self.#executor(middleware, once(self, self.#invokeWithErrorManagement)).catch(self.#errorHandler);
74
+ }
75
+ /**
76
+ * Sets a custom final handler to execute when the middleware chain completes successfully.
77
+ *
78
+ * The final handler is called when the entire middleware pipeline reaches the end
79
+ * by calling `next` through all handlers. This makes it easier to execute custom
80
+ * logic that is not part of the chain but must run when the chain ends.
81
+ *
82
+ * @param finalHandler - The function to execute when the chain completes
83
+ * @returns The Runner instance for method chaining
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * await middleware
88
+ * .runner()
89
+ * .finalHandler(() => {
90
+ * console.log('All middleware completed')
91
+ * })
92
+ * .run((fn, next) => fn(context, next))
93
+ * ```
94
+ */
95
+ finalHandler(finalHandler) {
96
+ this.#finalHandler = finalHandler;
97
+ return this;
98
+ }
99
+ /**
100
+ * Sets a custom error handler to catch exceptions in the middleware pipeline.
101
+ *
102
+ * By default, exceptions raised in the middleware pipeline bubble up to the `run`
103
+ * method and can be captured using a try/catch block. When an exception is raised,
104
+ * the middleware downstream logic will not run.
105
+ *
106
+ * Defining an error handler changes this behavior:
107
+ * - The `run` method will not throw exceptions
108
+ * - Errors are caught and passed to the error handler
109
+ * - Middleware upstream logic (after `next`) can still execute
110
+ *
111
+ * @param errorHandler - The function to handle errors
112
+ * @returns The Runner instance for method chaining
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * await middleware
117
+ * .runner()
118
+ * .errorHandler((error) => {
119
+ * console.error('Middleware error:', error)
120
+ * })
121
+ * .run((fn, next) => fn(context, next))
122
+ * ```
123
+ */
124
+ errorHandler(errorHandler) {
125
+ this.#errorHandler = errorHandler;
126
+ return this;
127
+ }
128
+ /**
129
+ * Executes the middleware pipeline using the provided executor function.
130
+ *
131
+ * The executor function is responsible for invoking each middleware handler with
132
+ * the appropriate context and the `next` callback. Since you control the executor,
133
+ * you can pass any data you want to the middleware.
134
+ *
135
+ * @param cb - The executor function that invokes each middleware handler
136
+ * @returns A Promise that resolves when the middleware pipeline completes
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * const context = { user: null }
141
+ * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
142
+ *
143
+ * const middleware = new Middleware<MiddlewareFn>()
144
+ * middleware.add(async (ctx, next) => {
145
+ * ctx.user = await authenticate()
146
+ * await next()
147
+ * })
148
+ *
149
+ * await middleware
150
+ * .runner()
151
+ * .run((fn, next) => fn(context, next))
152
+ * ```
153
+ */
154
+ async run(cb) {
155
+ this.#executor = cb;
156
+ if (this.#errorHandler) {
157
+ return this.#invokeWithErrorManagement(this);
158
+ }
159
+ return this.#invoke(this);
160
+ }
161
+ }
162
+ class Middleware {
163
+ #middleware = /* @__PURE__ */ new Set();
164
+ #middlewareArray;
165
+ #isFrozen = false;
166
+ /**
167
+ * Returns all registered middleware handlers.
168
+ *
169
+ * @returns A Set containing all registered middleware handlers
170
+ */
171
+ all() {
172
+ return this.#middleware;
173
+ }
174
+ /**
175
+ * Checks if a specific handler has already been registered as middleware.
176
+ *
177
+ * @param handler - The middleware handler to check for
178
+ * @returns `true` if the handler is registered, `false` otherwise
179
+ */
180
+ has(handler) {
181
+ return this.#middleware.has(handler);
182
+ }
183
+ /**
184
+ * Registers a new middleware handler to the pipeline.
185
+ *
186
+ * Adding the same middleware handler multiple times will result in a no-op,
187
+ * as handlers are stored in a Set to prevent duplicates.
188
+ *
189
+ * @param handler - The middleware handler to register
190
+ * @returns The Middleware instance for method chaining
191
+ * @throws {Error} If the middleware stack is frozen
192
+ */
193
+ add(handler) {
194
+ if (this.#isFrozen) {
195
+ throw new Error("Middleware stack is frozen. Cannot add new middleware");
196
+ }
197
+ this.#middleware.add(handler);
198
+ return this;
199
+ }
200
+ /**
201
+ * Removes a specific middleware handler from the pipeline.
202
+ *
203
+ * @param handler - The middleware handler to remove
204
+ * @returns `true` if the handler was removed, `false` if it was not found
205
+ * @throws {Error} If the middleware stack is frozen
206
+ */
207
+ remove(handler) {
208
+ if (this.#isFrozen) {
209
+ throw new Error("Middleware stack is frozen. Cannot remove middleware");
210
+ }
211
+ return this.#middleware.delete(handler);
212
+ }
213
+ /**
214
+ * Removes all registered middleware handlers from the pipeline.
215
+ *
216
+ * @throws {Error} If the middleware stack is frozen
217
+ */
218
+ clear() {
219
+ if (this.#isFrozen) {
220
+ throw new Error("Middleware stack is frozen. Cannot clear middleware");
221
+ }
222
+ this.#middleware.clear();
223
+ }
224
+ /**
225
+ * Merges middleware handlers from another Middleware instance.
226
+ *
227
+ * The middleware from the source instance are appended to the current instance.
228
+ *
229
+ * @param hooks - The source Middleware instance to merge from
230
+ * @throws {Error} If the middleware stack is frozen
231
+ */
232
+ merge(hooks) {
233
+ if (this.#isFrozen) {
234
+ throw new Error("Middleware stack is frozen. Cannot merge middleware");
235
+ }
236
+ hooks.all().forEach((handler) => {
237
+ this.add(handler);
238
+ });
239
+ }
240
+ /**
241
+ * Freezes the middleware stack to prevent further modifications.
242
+ *
243
+ * Once frozen, the middleware array is cached and no new handlers can be added,
244
+ * removed, or modified. This method is automatically called when creating a runner.
245
+ */
246
+ freeze() {
247
+ if (this.#isFrozen) {
248
+ return;
249
+ }
250
+ this.#isFrozen = true;
251
+ this.#middlewareArray = [...this.all()];
252
+ }
253
+ /**
254
+ * Creates and returns a Runner instance to execute the middleware pipeline.
255
+ *
256
+ * This method automatically freezes the middleware stack to prevent modifications
257
+ * during execution.
258
+ *
259
+ * @returns A new Runner instance configured with the current middleware handlers
260
+ */
261
+ runner() {
262
+ this.freeze();
263
+ return new Runner(this.#middlewareArray);
264
+ }
265
+ }
266
+ const version = "1.20251213.0";
267
+ exports.Middleware = Middleware;
268
+ exports.Runner = Runner;
269
+ exports.version = version;
2
270
  //# sourceMappingURL=index.cjs.map
package/index.cjs.map CHANGED
@@ -1 +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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"}
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":[],"mappings":"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"}
package/index.d.mts ADDED
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Error handler called when any middleware in the pipeline raises an exception.
3
+ *
4
+ * When defined, errors are caught and passed to this handler instead of bubbling
5
+ * up to the `run` method. This allows you to implement custom error handling logic
6
+ * and optionally resume the downstream flow of middleware.
7
+ *
8
+ * @param error - The error thrown by the middleware
9
+ */
10
+ export declare type ErrorHandler = (error: any) => Promise<any>;
11
+
12
+ /**
13
+ * The executor function responsible for invoking each middleware handler.
14
+ *
15
+ * The executor gives you control over how middleware functions are called,
16
+ * allowing you to pass custom context and parameters to each handler.
17
+ *
18
+ * @template MiddlewareFn - The type of the middleware function/handler
19
+ * @param middleware - The current middleware handler to execute
20
+ * @param next - The next function to advance the pipeline
21
+ */
22
+ export declare type Executor<MiddlewareFn extends any> = (middleware: MiddlewareFn, next: NextFn) => Promise<any>;
23
+
24
+ /**
25
+ * Final handler called when the entire middleware chain completes successfully.
26
+ *
27
+ * The final handler is executed after all middleware have called `next` and the
28
+ * pipeline reaches its end. It provides a way to execute custom logic outside
29
+ * the middleware chain when the chain completes.
30
+ */
31
+ export declare type FinalHandler = () => Promise<any>;
32
+
33
+ /**
34
+ * The Middleware class implements the chain of responsibility design pattern
35
+ * and allows executing handlers in series.
36
+ *
37
+ * The middleware handlers can be represented as any value you wish, such as:
38
+ * - A function: `middleware.add(function () { console.log('called') })`
39
+ * - An object with a `handle` method: `middleware.add({ name: 'auth', handle: authenticate })`
40
+ *
41
+ * @template MiddlewareFn - The type of the middleware function/handler
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const context = {}
46
+ * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
47
+ *
48
+ * const middleware = new Middleware<MiddlewareFn>()
49
+ *
50
+ * middleware.add((ctx, next) => {
51
+ * console.log('executing middleware')
52
+ * await next()
53
+ * })
54
+ *
55
+ * await middleware
56
+ * .runner()
57
+ * .run((fn, next) => fn(context, next))
58
+ * ```
59
+ */
60
+ export declare class Middleware<MiddlewareFn extends any> {
61
+ #private;
62
+ /**
63
+ * Returns all registered middleware handlers.
64
+ *
65
+ * @returns A Set containing all registered middleware handlers
66
+ */
67
+ all(): Set<MiddlewareFn>;
68
+ /**
69
+ * Checks if a specific handler has already been registered as middleware.
70
+ *
71
+ * @param handler - The middleware handler to check for
72
+ * @returns `true` if the handler is registered, `false` otherwise
73
+ */
74
+ has(handler: MiddlewareFn): boolean;
75
+ /**
76
+ * Registers a new middleware handler to the pipeline.
77
+ *
78
+ * Adding the same middleware handler multiple times will result in a no-op,
79
+ * as handlers are stored in a Set to prevent duplicates.
80
+ *
81
+ * @param handler - The middleware handler to register
82
+ * @returns The Middleware instance for method chaining
83
+ * @throws {Error} If the middleware stack is frozen
84
+ */
85
+ add(handler: MiddlewareFn): this;
86
+ /**
87
+ * Removes a specific middleware handler from the pipeline.
88
+ *
89
+ * @param handler - The middleware handler to remove
90
+ * @returns `true` if the handler was removed, `false` if it was not found
91
+ * @throws {Error} If the middleware stack is frozen
92
+ */
93
+ remove(handler: MiddlewareFn): boolean;
94
+ /**
95
+ * Removes all registered middleware handlers from the pipeline.
96
+ *
97
+ * @throws {Error} If the middleware stack is frozen
98
+ */
99
+ clear(): void;
100
+ /**
101
+ * Merges middleware handlers from another Middleware instance.
102
+ *
103
+ * The middleware from the source instance are appended to the current instance.
104
+ *
105
+ * @param hooks - The source Middleware instance to merge from
106
+ * @throws {Error} If the middleware stack is frozen
107
+ */
108
+ merge(hooks: Middleware<MiddlewareFn>): void;
109
+ /**
110
+ * Freezes the middleware stack to prevent further modifications.
111
+ *
112
+ * Once frozen, the middleware array is cached and no new handlers can be added,
113
+ * removed, or modified. This method is automatically called when creating a runner.
114
+ */
115
+ freeze(): void;
116
+ /**
117
+ * Creates and returns a Runner instance to execute the middleware pipeline.
118
+ *
119
+ * This method automatically freezes the middleware stack to prevent modifications
120
+ * during execution.
121
+ *
122
+ * @returns A new Runner instance configured with the current middleware handlers
123
+ */
124
+ runner(): Runner<MiddlewareFn>;
125
+ }
126
+
127
+ /**
128
+ * The next function used to advance the middleware pipeline.
129
+ *
130
+ * When called, it triggers the execution of the next middleware in the chain.
131
+ * If not called, the middleware chain stops at the current handler.
132
+ */
133
+ export declare type NextFn = () => Promise<any> | any;
134
+
135
+ /**
136
+ * Runner executes an array of middleware functions in sequence.
137
+ *
138
+ * The middleware pipeline advances only when the current function calls `next`,
139
+ * implementing the chain of responsibility pattern. You control how to execute
140
+ * the underlying middleware functions by providing an executor function.
141
+ *
142
+ * @template MiddlewareFn - The type of the middleware function/handler
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * const context = { stack: [] }
147
+ * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
148
+ *
149
+ * const middleware = new Middleware<MiddlewareFn>()
150
+ * middleware.add((ctx, next) => {
151
+ * ctx.stack.push('fn1')
152
+ * await next()
153
+ * })
154
+ *
155
+ * await middleware
156
+ * .runner()
157
+ * .finalHandler(() => {
158
+ * context.stack.push('final handler')
159
+ * })
160
+ * .run((fn, next) => fn(context, next))
161
+ * ```
162
+ */
163
+ export declare class Runner<MiddlewareFn extends any> {
164
+ #private;
165
+ constructor(middleware: MiddlewareFn[]);
166
+ /**
167
+ * Sets a custom final handler to execute when the middleware chain completes successfully.
168
+ *
169
+ * The final handler is called when the entire middleware pipeline reaches the end
170
+ * by calling `next` through all handlers. This makes it easier to execute custom
171
+ * logic that is not part of the chain but must run when the chain ends.
172
+ *
173
+ * @param finalHandler - The function to execute when the chain completes
174
+ * @returns The Runner instance for method chaining
175
+ *
176
+ * @example
177
+ * ```ts
178
+ * await middleware
179
+ * .runner()
180
+ * .finalHandler(() => {
181
+ * console.log('All middleware completed')
182
+ * })
183
+ * .run((fn, next) => fn(context, next))
184
+ * ```
185
+ */
186
+ finalHandler(finalHandler: FinalHandler): this;
187
+ /**
188
+ * Sets a custom error handler to catch exceptions in the middleware pipeline.
189
+ *
190
+ * By default, exceptions raised in the middleware pipeline bubble up to the `run`
191
+ * method and can be captured using a try/catch block. When an exception is raised,
192
+ * the middleware downstream logic will not run.
193
+ *
194
+ * Defining an error handler changes this behavior:
195
+ * - The `run` method will not throw exceptions
196
+ * - Errors are caught and passed to the error handler
197
+ * - Middleware upstream logic (after `next`) can still execute
198
+ *
199
+ * @param errorHandler - The function to handle errors
200
+ * @returns The Runner instance for method chaining
201
+ *
202
+ * @example
203
+ * ```ts
204
+ * await middleware
205
+ * .runner()
206
+ * .errorHandler((error) => {
207
+ * console.error('Middleware error:', error)
208
+ * })
209
+ * .run((fn, next) => fn(context, next))
210
+ * ```
211
+ */
212
+ errorHandler(errorHandler: ErrorHandler): this;
213
+ /**
214
+ * Executes the middleware pipeline using the provided executor function.
215
+ *
216
+ * The executor function is responsible for invoking each middleware handler with
217
+ * the appropriate context and the `next` callback. Since you control the executor,
218
+ * you can pass any data you want to the middleware.
219
+ *
220
+ * @param cb - The executor function that invokes each middleware handler
221
+ * @returns A Promise that resolves when the middleware pipeline completes
222
+ *
223
+ * @example
224
+ * ```ts
225
+ * const context = { user: null }
226
+ * type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
227
+ *
228
+ * const middleware = new Middleware<MiddlewareFn>()
229
+ * middleware.add(async (ctx, next) => {
230
+ * ctx.user = await authenticate()
231
+ * await next()
232
+ * })
233
+ *
234
+ * await middleware
235
+ * .runner()
236
+ * .run((fn, next) => fn(context, next))
237
+ * ```
238
+ */
239
+ run(cb: Executor<MiddlewareFn>): Promise<void>;
240
+ }
241
+
242
+ /**
243
+ * The current version of the package.
244
+ *
245
+ * This constant is replaced during the build process with the actual version from package.json.
246
+ */
247
+ export declare const version: string;
248
+
249
+ export { }
package/index.mjs CHANGED
@@ -1,49 +1,74 @@
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);
1
+ function once(scope, callback) {
2
+ function next() {
3
+ if (next.called) {
4
+ return;
5
+ }
6
+ next.called = true;
7
+ return callback(scope);
18
8
  }
19
- return e.called = !1, e;
9
+ next.called = false;
10
+ return next;
20
11
  }
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);
12
+ const DEFAULT_FINAL_HANDLER = () => Promise.resolve();
13
+ class Runner {
14
+ /**
15
+ * The array of middleware handlers to execute in sequence.
16
+ */
17
+ #middleware;
18
+ /**
19
+ * The current index position in the middleware pipeline.
20
+ */
21
+ #currentIndex = 0;
22
+ /**
23
+ * The executor function responsible for invoking each middleware handler.
24
+ */
25
+ #executor;
26
+ /**
27
+ * The final handler to execute when the middleware chain completes successfully.
28
+ */
29
+ #finalHandler = DEFAULT_FINAL_HANDLER;
30
+ /**
31
+ * Optional error handler to catch and handle exceptions in the middleware pipeline.
32
+ */
33
+ #errorHandler;
34
+ constructor(middleware) {
35
+ this.#middleware = middleware;
36
+ }
37
+ /**
38
+ * Invokes middleware handlers one at a time.
39
+ *
40
+ * Middleware functions are executed recursively until `next` is called.
41
+ * If a middleware doesn't call `next`, the chain will finish automatically
42
+ * without executing remaining handlers.
43
+ *
44
+ * @param self - The Runner instance scope
45
+ * @returns A Promise that resolves when the middleware chain completes
46
+ * @internal
47
+ */
48
+ #invoke(self) {
49
+ const middleware = self.#middleware[self.#currentIndex++];
50
+ if (!middleware) {
51
+ return self.#finalHandler();
52
+ }
53
+ return self.#executor(middleware, once(self, self.#invoke));
54
+ }
55
+ /**
56
+ * Invokes middleware handlers with error handling.
57
+ *
58
+ * Similar to `#invoke`, but catches exceptions and passes them to the error handler.
59
+ * When an exception is raised, the middleware downstream logic will not run unless
60
+ * the error handler allows the chain to continue.
61
+ *
62
+ * @param self - The Runner instance scope
63
+ * @returns A Promise that resolves when the middleware chain completes
64
+ * @internal
65
+ */
66
+ #invokeWithErrorManagement(self) {
67
+ const middleware = self.#middleware[self.#currentIndex++];
68
+ if (!middleware) {
69
+ return self.#finalHandler().catch(self.#errorHandler);
70
+ }
71
+ return self.#executor(middleware, once(self, self.#invokeWithErrorManagement)).catch(self.#errorHandler);
47
72
  }
48
73
  /**
49
74
  * Sets a custom final handler to execute when the middleware chain completes successfully.
@@ -65,8 +90,9 @@ class g {
65
90
  * .run((fn, next) => fn(context, next))
66
91
  * ```
67
92
  */
68
- finalHandler(r) {
69
- return s(this, m, r), this;
93
+ finalHandler(finalHandler) {
94
+ this.#finalHandler = finalHandler;
95
+ return this;
70
96
  }
71
97
  /**
72
98
  * Sets a custom error handler to catch exceptions in the middleware pipeline.
@@ -93,8 +119,9 @@ class g {
93
119
  * .run((fn, next) => fn(context, next))
94
120
  * ```
95
121
  */
96
- errorHandler(r) {
97
- return s(this, l, r), this;
122
+ errorHandler(errorHandler) {
123
+ this.#errorHandler = errorHandler;
124
+ return this;
98
125
  }
99
126
  /**
100
127
  * Executes the middleware pipeline using the provided executor function.
@@ -122,55 +149,25 @@ class g {
122
149
  * .run((fn, next) => fn(context, next))
123
150
  * ```
124
151
  */
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);
152
+ async run(cb) {
153
+ this.#executor = cb;
154
+ if (this.#errorHandler) {
155
+ return this.#invokeWithErrorManagement(this);
156
+ }
157
+ return this.#invoke(this);
127
158
  }
128
159
  }
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
- }
160
+ class Middleware {
161
+ #middleware = /* @__PURE__ */ new Set();
162
+ #middlewareArray;
163
+ #isFrozen = false;
167
164
  /**
168
165
  * Returns all registered middleware handlers.
169
166
  *
170
167
  * @returns A Set containing all registered middleware handlers
171
168
  */
172
169
  all() {
173
- return n(this, o);
170
+ return this.#middleware;
174
171
  }
175
172
  /**
176
173
  * Checks if a specific handler has already been registered as middleware.
@@ -178,8 +175,8 @@ class L {
178
175
  * @param handler - The middleware handler to check for
179
176
  * @returns `true` if the handler is registered, `false` otherwise
180
177
  */
181
- has(r) {
182
- return n(this, o).has(r);
178
+ has(handler) {
179
+ return this.#middleware.has(handler);
183
180
  }
184
181
  /**
185
182
  * Registers a new middleware handler to the pipeline.
@@ -191,10 +188,12 @@ class L {
191
188
  * @returns The Middleware instance for method chaining
192
189
  * @throws {Error} If the middleware stack is frozen
193
190
  */
194
- add(r) {
195
- if (n(this, d))
191
+ add(handler) {
192
+ if (this.#isFrozen) {
196
193
  throw new Error("Middleware stack is frozen. Cannot add new middleware");
197
- return n(this, o).add(r), this;
194
+ }
195
+ this.#middleware.add(handler);
196
+ return this;
198
197
  }
199
198
  /**
200
199
  * Removes a specific middleware handler from the pipeline.
@@ -203,10 +202,11 @@ class L {
203
202
  * @returns `true` if the handler was removed, `false` if it was not found
204
203
  * @throws {Error} If the middleware stack is frozen
205
204
  */
206
- remove(r) {
207
- if (n(this, d))
205
+ remove(handler) {
206
+ if (this.#isFrozen) {
208
207
  throw new Error("Middleware stack is frozen. Cannot remove middleware");
209
- return n(this, o).delete(r);
208
+ }
209
+ return this.#middleware.delete(handler);
210
210
  }
211
211
  /**
212
212
  * Removes all registered middleware handlers from the pipeline.
@@ -214,9 +214,10 @@ class L {
214
214
  * @throws {Error} If the middleware stack is frozen
215
215
  */
216
216
  clear() {
217
- if (n(this, d))
217
+ if (this.#isFrozen) {
218
218
  throw new Error("Middleware stack is frozen. Cannot clear middleware");
219
- n(this, o).clear();
219
+ }
220
+ this.#middleware.clear();
220
221
  }
221
222
  /**
222
223
  * Merges middleware handlers from another Middleware instance.
@@ -226,11 +227,12 @@ class L {
226
227
  * @param hooks - The source Middleware instance to merge from
227
228
  * @throws {Error} If the middleware stack is frozen
228
229
  */
229
- merge(r) {
230
- if (n(this, d))
230
+ merge(hooks) {
231
+ if (this.#isFrozen) {
231
232
  throw new Error("Middleware stack is frozen. Cannot merge middleware");
232
- r.all().forEach((e) => {
233
- this.add(e);
233
+ }
234
+ hooks.all().forEach((handler) => {
235
+ this.add(handler);
234
236
  });
235
237
  }
236
238
  /**
@@ -240,7 +242,11 @@ class L {
240
242
  * removed, or modified. This method is automatically called when creating a runner.
241
243
  */
242
244
  freeze() {
243
- n(this, d) || (s(this, d, !0), s(this, z, [...this.all()]));
245
+ if (this.#isFrozen) {
246
+ return;
247
+ }
248
+ this.#isFrozen = true;
249
+ this.#middlewareArray = [...this.all()];
244
250
  }
245
251
  /**
246
252
  * Creates and returns a Runner instance to execute the middleware pipeline.
@@ -251,14 +257,14 @@ class L {
251
257
  * @returns A new Runner instance configured with the current middleware handlers
252
258
  */
253
259
  runner() {
254
- return this.freeze(), new g(n(this, z));
260
+ this.freeze();
261
+ return new Runner(this.#middlewareArray);
255
262
  }
256
263
  }
257
- o = new WeakMap(), z = new WeakMap(), d = new WeakMap();
258
- const y = "1.20251013.0";
264
+ const version = "1.20251213.0";
259
265
  export {
260
- L as Middleware,
261
- g as Runner,
262
- y as version
266
+ Middleware,
267
+ Runner,
268
+ version
263
269
  };
264
270
  //# sourceMappingURL=index.mjs.map
package/index.mjs.map CHANGED
@@ -1 +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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"}
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":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nhtio/middleware",
3
- "version": "1.20251013.0",
3
+ "version": "1.20251213.0",
4
4
  "description": "A cross-environment (browser/node) implementation of the chain of responsibility design pattern based on @poppinss/middleware",
5
5
  "keywords": [],
6
6
  "author": "Jak Giveon <jak@nht.io>",
package/index.d.ts DELETED
@@ -1,37 +0,0 @@
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';
@@ -1,94 +0,0 @@
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
- }
@@ -1,107 +0,0 @@
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
- }
@@ -1,36 +0,0 @@
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>;