@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 +269 -1
- package/index.cjs.map +1 -1
- package/index.d.mts +249 -0
- package/index.mjs +116 -110
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/index.d.ts +0 -37
- package/private/middleware.d.ts +0 -94
- package/private/runner.d.ts +0 -107
- package/private/types.d.ts +0 -36
package/index.cjs
CHANGED
|
@@ -1,2 +1,270 @@
|
|
|
1
|
-
"use strict";
|
|
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":"4gBAaA,SAASA,EAAKC,EAAoBC,EAAwD,CACxF,SAASC,GAA6B,CACpC,GAAI,CAAAA,EAAK,OAIT,OAAAA,EAAK,OAAS,GACPD,EAASD,CAAK,CACvB,CACA,OAAAE,EAAK,OAAS,GAEPA,CACT,CASA,MAAMC,EAAwB,IAAM,QAAQ,QAAA,sBA8BrC,MAAMC,CAAiC,CA0B5C,YAAYC,EAA4B,CA1BnCC,EAAA,KAAAC,GAILD,EAAA,KAAAE,GAKAF,EAAA,KAAAG,EAAgB,GAKhBH,EAAA,KAAAI,GAKAJ,EAAA,KAAAK,EAA8BR,GAK9BG,EAAA,KAAAM,GAGEC,EAAA,KAAKL,EAAcH,EACrB,CAwEA,aAAaS,EAAkC,CAC7C,OAAAD,EAAA,KAAKF,EAAgBG,GACd,IACT,CA2BA,aAAaC,EAAkC,CAC7C,OAAAF,EAAA,KAAKD,EAAgBG,GACd,IACT,CA4BA,MAAM,IAAIC,EAA2C,CAGnD,OAFAH,EAAA,KAAKH,EAAYM,GAEbC,EAAA,KAAKL,GACAM,EAAA,KAAKX,EAAAY,GAAL,UAAgC,MAGlCD,EAAA,KAAKX,EAAAa,GAAL,UAAa,KACtB,CACF,CAtKEZ,EAAA,YAKAC,EAAA,YAKAC,EAAA,YAKAC,EAAA,YAKAC,EAAA,YAxBKL,EAAA,YAyCLa,WAAQC,EAAkD,SACxD,MAAMhB,EAAaY,EAAAI,EAAKb,GAAYc,EAAAD,EAAKZ,GAAL,GAAoB,EAKxD,OAAKJ,EAIEY,EAAAM,EAAAF,EAAKX,GAAL,KAAAa,EAAelB,EAAYN,EAAKsB,EAAMH,EAAAG,EAAKd,EAAAa,EAAO,GAHhDH,EAAAO,EAAAH,EAAKV,GAAL,KAAAa,EAIX,EAaAL,WAA2BE,EAAkD,SAC3E,MAAMhB,EAAaY,EAAAI,EAAKb,GAAYc,EAAAD,EAAKZ,GAAL,GAAoB,EAKxD,OAAKJ,EAIEY,EAAAM,EAAAF,EACJX,GADI,KAAAa,EACMlB,EAAYN,EAAKsB,EAAMH,EAAAG,EAAKd,EAAAY,EAA0B,GAChE,MAAMF,EAAAI,EAAKT,EAAa,EALlBK,EAAAO,EAAAH,EAAKV,GAAL,KAAAa,GAAqB,MAAMP,EAAAI,EAAKT,EAAa,CAMxD,YCjHK,MAAMa,CAAqC,CAA3C,cACLnB,EAAA,KAAAE,MAAqC,KACrCF,EAAA,KAAAoB,GACApB,EAAA,KAAAqB,EAAqB,IAOrB,KAAM,CACJ,OAAOV,EAAA,KAAKT,EACd,CAQA,IAAIoB,EAAgC,CAClC,OAAOX,EAAA,KAAKT,GAAY,IAAIoB,CAAO,CACrC,CAYA,IAAIA,EAA6B,CAC/B,GAAIX,EAAA,KAAKU,GACP,MAAM,IAAI,MAAM,uDAAuD,EAGzE,OAAAV,EAAA,KAAKT,GAAY,IAAIoB,CAAO,EACrB,IACT,CASA,OAAOA,EAAgC,CACrC,GAAIX,EAAA,KAAKU,GACP,MAAM,IAAI,MAAM,sDAAsD,EAGxE,OAAOV,EAAA,KAAKT,GAAY,OAAOoB,CAAO,CACxC,CAOA,OAAc,CACZ,GAAIX,EAAA,KAAKU,GACP,MAAM,IAAI,MAAM,qDAAqD,EAGvEV,EAAA,KAAKT,GAAY,MAAA,CACnB,CAUA,MAAMqB,EAAiC,CACrC,GAAIZ,EAAA,KAAKU,GACP,MAAM,IAAI,MAAM,qDAAqD,EAGvEE,EAAM,IAAA,EAAM,QAASD,GAAY,CAC/B,KAAK,IAAIA,CAAO,CAClB,CAAC,CACH,CAQA,QAAS,CACHX,EAAA,KAAKU,KAITd,EAAA,KAAKc,EAAY,IACjBd,EAAA,KAAKa,EAAmB,CAAC,GAAG,KAAK,KAAK,GACxC,CAUA,QAA+B,CAC7B,YAAK,OAAA,EACE,IAAItB,EAAOa,EAAA,KAAKS,EAAiB,CAC1C,CACF,CAnHElB,EAAA,YACAkB,EAAA,YACAC,EAAA,YCEK,MAAMG,EAAU"}
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
9
|
+
next.called = false;
|
|
10
|
+
return next;
|
|
20
11
|
}
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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(
|
|
69
|
-
|
|
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(
|
|
97
|
-
|
|
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(
|
|
126
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
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(
|
|
182
|
-
return
|
|
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(
|
|
195
|
-
if (
|
|
191
|
+
add(handler) {
|
|
192
|
+
if (this.#isFrozen) {
|
|
196
193
|
throw new Error("Middleware stack is frozen. Cannot add new middleware");
|
|
197
|
-
|
|
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(
|
|
207
|
-
if (
|
|
205
|
+
remove(handler) {
|
|
206
|
+
if (this.#isFrozen) {
|
|
208
207
|
throw new Error("Middleware stack is frozen. Cannot remove middleware");
|
|
209
|
-
|
|
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 (
|
|
217
|
+
if (this.#isFrozen) {
|
|
218
218
|
throw new Error("Middleware stack is frozen. Cannot clear middleware");
|
|
219
|
-
|
|
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(
|
|
230
|
-
if (
|
|
230
|
+
merge(hooks) {
|
|
231
|
+
if (this.#isFrozen) {
|
|
231
232
|
throw new Error("Middleware stack is frozen. Cannot merge middleware");
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
260
|
+
this.freeze();
|
|
261
|
+
return new Runner(this.#middlewareArray);
|
|
255
262
|
}
|
|
256
263
|
}
|
|
257
|
-
|
|
258
|
-
const y = "1.20251013.0";
|
|
264
|
+
const version = "1.20251213.0";
|
|
259
265
|
export {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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":"AAaA,SAAS,KAAK,OAAoB,UAAwD;AACxF,WAAS,OAA6B;AACpC,QAAI,KAAK,QAAQ;AACf;AAAA,IACF;AAEA,SAAK,SAAS;AACd,WAAO,SAAS,KAAK;AAAA,EACvB;AACA,OAAK,SAAS;AAEd,SAAO;AACT;AASA,MAAM,wBAAwB,MAAM,QAAQ,QAAA;AA8BrC,MAAM,OAAiC;AAAA;AAAA;AAAA;AAAA,EAI5C;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB;AAAA;AAAA;AAAA;AAAA,EAKhB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAA8B;AAAA;AAAA;AAAA;AAAA,EAK9B;AAAA,EAEA,YAAY,YAA4B;AACtC,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,QAAQ,MAAkD;AACxD,UAAM,aAAa,KAAK,YAAY,KAAK,eAAe;AAKxD,QAAI,CAAC,YAAY;AACf,aAAO,KAAK,cAAA;AAAA,IACd;AAEA,WAAO,KAAK,UAAU,YAAY,KAAK,MAAM,KAAK,OAAO,CAAC;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,2BAA2B,MAAkD;AAC3E,UAAM,aAAa,KAAK,YAAY,KAAK,eAAe;AAKxD,QAAI,CAAC,YAAY;AACf,aAAO,KAAK,cAAA,EAAgB,MAAM,KAAK,aAAa;AAAA,IACtD;AAEA,WAAO,KACJ,UAAU,YAAY,KAAK,MAAM,KAAK,0BAA0B,CAAC,EACjE,MAAM,KAAK,aAAa;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,aAAa,cAAkC;AAC7C,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,aAAa,cAAkC;AAC7C,SAAK,gBAAgB;AACrB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,IAAI,IAA2C;AACnD,SAAK,YAAY;AAEjB,QAAI,KAAK,eAAe;AACtB,aAAO,KAAK,2BAA2B,IAAI;AAAA,IAC7C;AAEA,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AACF;AC7MO,MAAM,WAAqC;AAAA,EAChD,kCAAqC,IAAA;AAAA,EACrC;AAAA,EACA,YAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,MAAM;AACJ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,SAAgC;AAClC,WAAO,KAAK,YAAY,IAAI,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,IAAI,SAA6B;AAC/B,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,uDAAuD;AAAA,IACzE;AAEA,SAAK,YAAY,IAAI,OAAO;AAC5B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,SAAgC;AACrC,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAEA,WAAO,KAAK,YAAY,OAAO,OAAO;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAc;AACZ,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAEA,SAAK,YAAY,MAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAiC;AACrC,QAAI,KAAK,WAAW;AAClB,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAEA,UAAM,IAAA,EAAM,QAAQ,CAAC,YAAY;AAC/B,WAAK,IAAI,OAAO;AAAA,IAClB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS;AACP,QAAI,KAAK,WAAW;AAClB;AAAA,IACF;AAEA,SAAK,YAAY;AACjB,SAAK,mBAAmB,CAAC,GAAG,KAAK,KAAK;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,SAA+B;AAC7B,SAAK,OAAA;AACL,WAAO,IAAI,OAAO,KAAK,gBAAiB;AAAA,EAC1C;AACF;AC/GO,MAAM,UAAU;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nhtio/middleware",
|
|
3
|
-
"version": "1.
|
|
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';
|
package/private/middleware.d.ts
DELETED
|
@@ -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
|
-
}
|
package/private/runner.d.ts
DELETED
|
@@ -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
|
-
}
|
package/private/types.d.ts
DELETED
|
@@ -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>;
|