@philiprehberger/middleware-ts 0.1.4 → 0.2.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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @philiprehberger/middleware-ts
2
2
 
3
- [![CI](https://github.com/philiprehberger/middleware-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/middleware-ts/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/philiprehberger/ts-middleware/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/ts-middleware/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/@philiprehberger/middleware-ts.svg)](https://www.npmjs.com/package/@philiprehberger/middleware-ts)
5
- [![Last updated](https://img.shields.io/github/last-commit/philiprehberger/middleware-ts)](https://github.com/philiprehberger/middleware-ts/commits/main)
5
+ [![Last updated](https://img.shields.io/github/last-commit/philiprehberger/ts-middleware)](https://github.com/philiprehberger/ts-middleware/commits/main)
6
6
 
7
7
  Framework-agnostic middleware composition engine
8
8
 
@@ -40,6 +40,27 @@ const authBranch = branch(
40
40
  );
41
41
  ```
42
42
 
43
+ ### Timeout
44
+
45
+ ```ts
46
+ import { withTimeout, MiddlewareTimeoutError } from '@philiprehberger/middleware-ts';
47
+
48
+ const slowOp = withTimeout(async (ctx, next) => {
49
+ await fetchSomethingSlow(ctx);
50
+ await next();
51
+ }, 5000);
52
+ ```
53
+
54
+ ### Tap (Side Effects)
55
+
56
+ ```ts
57
+ import { tap } from '@philiprehberger/middleware-ts';
58
+
59
+ const logger = tap<Ctx>((ctx) => {
60
+ console.log(`[req] ${ctx.req.url}`);
61
+ });
62
+ ```
63
+
43
64
  ## API
44
65
 
45
66
  | Function | Description |
@@ -48,6 +69,8 @@ const authBranch = branch(
48
69
  | `createPipeline<Ctx>()` | Builder with `.use()` and `.useIf()` |
49
70
  | `branch(condition, trueMw, falseMw?)` | Conditional middleware |
50
71
  | `withErrorHandler(mw, handler)` | Wrap with error catching |
72
+ | `withTimeout(mw, ms)` | Reject with `MiddlewareTimeoutError` if `mw` exceeds `ms` |
73
+ | `tap(fn)` | Run a side-effect and continue the pipeline |
51
74
 
52
75
  ## Development
53
76
 
@@ -61,11 +84,11 @@ npm test
61
84
 
62
85
  If you find this project useful:
63
86
 
64
- ⭐ [Star the repo](https://github.com/philiprehberger/middleware-ts)
87
+ ⭐ [Star the repo](https://github.com/philiprehberger/ts-middleware)
65
88
 
66
- 🐛 [Report issues](https://github.com/philiprehberger/middleware-ts/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
89
+ 🐛 [Report issues](https://github.com/philiprehberger/ts-middleware/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
67
90
 
68
- 💡 [Suggest features](https://github.com/philiprehberger/middleware-ts/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
91
+ 💡 [Suggest features](https://github.com/philiprehberger/ts-middleware/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
69
92
 
70
93
  ❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)
71
94
 
package/dist/index.cjs CHANGED
@@ -72,9 +72,44 @@ function withErrorHandler(mw, handler) {
72
72
  };
73
73
  }
74
74
 
75
+ // src/timeout.ts
76
+ var MiddlewareTimeoutError = class extends Error {
77
+ constructor(timeoutMs) {
78
+ super(`Middleware timed out after ${timeoutMs}ms`);
79
+ this.name = "MiddlewareTimeoutError";
80
+ this.timeoutMs = timeoutMs;
81
+ }
82
+ };
83
+ function withTimeout(mw, ms) {
84
+ return async (ctx, next) => {
85
+ let timer;
86
+ try {
87
+ await Promise.race([
88
+ mw(ctx, next),
89
+ new Promise((_, reject) => {
90
+ timer = setTimeout(() => reject(new MiddlewareTimeoutError(ms)), ms);
91
+ })
92
+ ]);
93
+ } finally {
94
+ if (timer) clearTimeout(timer);
95
+ }
96
+ };
97
+ }
98
+
99
+ // src/tap.ts
100
+ function tap(fn) {
101
+ return async (ctx, next) => {
102
+ await fn(ctx);
103
+ await next();
104
+ };
105
+ }
106
+
107
+ exports.MiddlewareTimeoutError = MiddlewareTimeoutError;
75
108
  exports.branch = branch;
76
109
  exports.compose = compose;
77
110
  exports.createPipeline = createPipeline;
111
+ exports.tap = tap;
78
112
  exports.withErrorHandler = withErrorHandler;
113
+ exports.withTimeout = withTimeout;
79
114
  //# sourceMappingURL=index.cjs.map
80
115
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/compose.ts","../src/pipeline.ts","../src/branch.ts","../src/error.ts"],"names":[],"mappings":";;;AAEO,SAAS,WAAgB,WAAA,EAAiD;AAC/E,EAAA,OAAO,OAAO,KAAU,IAAA,KAA8B;AACpD,IAAA,IAAI,KAAA,GAAQ,EAAA;AAEZ,IAAA,eAAe,SAAS,CAAA,EAA0B;AAChD,MAAA,IAAI,CAAA,IAAK,KAAA,EAAO,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAC9D,MAAA,KAAA,GAAQ,CAAA;AAER,MAAA,MAAM,KAAK,CAAA,GAAI,WAAA,CAAY,MAAA,GAAS,WAAA,CAAY,CAAC,CAAA,GAAI,IAAA;AACrD,MAAA,IAAI,CAAC,EAAA,EAAI;AAET,MAAA,MAAM,GAAG,GAAA,EAAK,MAAM,QAAA,CAAS,CAAA,GAAI,CAAC,CAAC,CAAA;AAAA,IACrC;AAEA,IAAA,MAAM,SAAS,CAAC,CAAA;AAAA,EAClB,CAAA;AACF;;;ACTO,SAAS,cAAA,GAA+C;AAC7D,EAAA,MAAM,QAA2B,EAAC;AAElC,EAAA,MAAM,QAAA,GAAoC;AAAA,IACxC,IAAI,EAAA,EAAqB;AACvB,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAA,CAAM,WAA8C,EAAA,EAAqB;AACvE,MAAA,IAAI,OAAO,cAAc,SAAA,EAAW;AAClC,QAAA,IAAI,SAAA,EAAW,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AAAA,MAC9B,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,IAAA,CAAK,OAAO,GAAA,EAAK,IAAA,KAAS;AAC9B,UAAA,IAAI,SAAA,CAAU,GAAG,CAAA,EAAG;AAClB,YAAA,MAAM,EAAA,CAAG,KAAK,IAAI,CAAA;AAAA,UACpB,CAAA,MAAO;AACL,YAAA,MAAM,IAAA,EAAK;AAAA,UACb;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AACA,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAA,GAAQ;AACN,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAG,KAAK,CAAA;AACjC,MAAA,OAAO,OAAO,GAAA,KAAa;AACzB,QAAA,MAAM,QAAA,CAAS,KAAK,YAAY;AAAA,QAAC,CAAC,CAAA;AAAA,MACpC,CAAA;AAAA,IACF;AAAA,GACF;AAEA,EAAA,OAAO,QAAA;AACT;;;ACxCO,SAAS,MAAA,CACd,SAAA,EACA,MAAA,EACA,OAAA,EACiB;AACjB,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,IAAI,SAAA,CAAU,GAAG,CAAA,EAAG;AAClB,MAAA,MAAM,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,IACxB,WAAW,OAAA,EAAS;AAClB,MAAA,MAAM,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,IACzB,CAAA,MAAO;AACL,MAAA,MAAM,IAAA,EAAK;AAAA,IACb;AAAA,EACF,CAAA;AACF;;;ACdO,SAAS,gBAAA,CACd,IACA,OAAA,EACiB;AACjB,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,CAAG,KAAK,IAAI,CAAA;AAAA,IACpB,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["import type { Middleware } from './types';\n\nexport function compose<Ctx>(...middlewares: Middleware<Ctx>[]): Middleware<Ctx> {\n return async (ctx: Ctx, next: () => Promise<void>) => {\n let index = -1;\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) throw new Error('next() called multiple times');\n index = i;\n\n const mw = i < middlewares.length ? middlewares[i] : next;\n if (!mw) return;\n\n await mw(ctx, () => dispatch(i + 1));\n }\n\n await dispatch(0);\n };\n}\n","import type { Middleware } from './types';\nimport { compose } from './compose';\n\nexport interface MiddlewarePipeline<Ctx> {\n use(mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n build(): (ctx: Ctx) => Promise<void>;\n}\n\nexport function createPipeline<Ctx>(): MiddlewarePipeline<Ctx> {\n const stack: Middleware<Ctx>[] = [];\n\n const pipeline: MiddlewarePipeline<Ctx> = {\n use(mw: Middleware<Ctx>) {\n stack.push(mw);\n return pipeline;\n },\n\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>) {\n if (typeof condition === 'boolean') {\n if (condition) stack.push(mw);\n } else {\n stack.push(async (ctx, next) => {\n if (condition(ctx)) {\n await mw(ctx, next);\n } else {\n await next();\n }\n });\n }\n return pipeline;\n },\n\n build() {\n const composed = compose(...stack);\n return async (ctx: Ctx) => {\n await composed(ctx, async () => {});\n };\n },\n };\n\n return pipeline;\n}\n","import type { Middleware } from './types';\n\nexport function branch<Ctx>(\n condition: (ctx: Ctx) => boolean,\n trueMw: Middleware<Ctx>,\n falseMw?: Middleware<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n if (condition(ctx)) {\n await trueMw(ctx, next);\n } else if (falseMw) {\n await falseMw(ctx, next);\n } else {\n await next();\n }\n };\n}\n","import type { Middleware, ErrorHandler } from './types';\n\nexport function withErrorHandler<Ctx>(\n mw: Middleware<Ctx>,\n handler: ErrorHandler<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n try {\n await mw(ctx, next);\n } catch (err) {\n await handler(err, ctx);\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/compose.ts","../src/pipeline.ts","../src/branch.ts","../src/error.ts","../src/timeout.ts","../src/tap.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Middleware } from './types';\n\nexport function compose<Ctx>(...middlewares: Middleware<Ctx>[]): Middleware<Ctx> {\n return async (ctx: Ctx, next: () => Promise<void>) => {\n let index = -1;\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) throw new Error('next() called multiple times');\n index = i;\n\n const mw = i < middlewares.length ? middlewares[i] : next;\n if (!mw) return;\n\n await mw(ctx, () => dispatch(i + 1));\n }\n\n await dispatch(0);\n };\n}\n","import type { Middleware } from './types';\nimport { compose } from './compose';\n\nexport interface MiddlewarePipeline<Ctx> {\n use(mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n build(): (ctx: Ctx) => Promise<void>;\n}\n\nexport function createPipeline<Ctx>(): MiddlewarePipeline<Ctx> {\n const stack: Middleware<Ctx>[] = [];\n\n const pipeline: MiddlewarePipeline<Ctx> = {\n use(mw: Middleware<Ctx>) {\n stack.push(mw);\n return pipeline;\n },\n\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>) {\n if (typeof condition === 'boolean') {\n if (condition) stack.push(mw);\n } else {\n stack.push(async (ctx, next) => {\n if (condition(ctx)) {\n await mw(ctx, next);\n } else {\n await next();\n }\n });\n }\n return pipeline;\n },\n\n build() {\n const composed = compose(...stack);\n return async (ctx: Ctx) => {\n await composed(ctx, async () => {});\n };\n },\n };\n\n return pipeline;\n}\n","import type { Middleware } from './types';\n\nexport function branch<Ctx>(\n condition: (ctx: Ctx) => boolean,\n trueMw: Middleware<Ctx>,\n falseMw?: Middleware<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n if (condition(ctx)) {\n await trueMw(ctx, next);\n } else if (falseMw) {\n await falseMw(ctx, next);\n } else {\n await next();\n }\n };\n}\n","import type { Middleware, ErrorHandler } from './types';\n\nexport function withErrorHandler<Ctx>(\n mw: Middleware<Ctx>,\n handler: ErrorHandler<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n try {\n await mw(ctx, next);\n } catch (err) {\n await handler(err, ctx);\n }\n };\n}\n","import type { Middleware } from './types';\n\nexport class MiddlewareTimeoutError extends Error {\n public readonly timeoutMs: number;\n constructor(timeoutMs: number) {\n super(`Middleware timed out after ${timeoutMs}ms`);\n this.name = 'MiddlewareTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport function withTimeout<Ctx>(mw: Middleware<Ctx>, ms: number): Middleware<Ctx> {\n return async (ctx, next) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n mw(ctx, next),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new MiddlewareTimeoutError(ms)), ms);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n}\n","import type { Middleware } from './types';\n\nexport function tap<Ctx>(fn: (ctx: Ctx) => void | Promise<void>): Middleware<Ctx> {\n return async (ctx, next) => {\n await fn(ctx);\n await next();\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -14,4 +14,12 @@ declare function branch<Ctx>(condition: (ctx: Ctx) => boolean, trueMw: Middlewar
14
14
 
15
15
  declare function withErrorHandler<Ctx>(mw: Middleware<Ctx>, handler: ErrorHandler<Ctx>): Middleware<Ctx>;
16
16
 
17
- export { type ErrorHandler, type Middleware, type MiddlewarePipeline, branch, compose, createPipeline, withErrorHandler };
17
+ declare class MiddlewareTimeoutError extends Error {
18
+ readonly timeoutMs: number;
19
+ constructor(timeoutMs: number);
20
+ }
21
+ declare function withTimeout<Ctx>(mw: Middleware<Ctx>, ms: number): Middleware<Ctx>;
22
+
23
+ declare function tap<Ctx>(fn: (ctx: Ctx) => void | Promise<void>): Middleware<Ctx>;
24
+
25
+ export { type ErrorHandler, type Middleware, type MiddlewarePipeline, MiddlewareTimeoutError, branch, compose, createPipeline, tap, withErrorHandler, withTimeout };
package/dist/index.d.ts CHANGED
@@ -14,4 +14,12 @@ declare function branch<Ctx>(condition: (ctx: Ctx) => boolean, trueMw: Middlewar
14
14
 
15
15
  declare function withErrorHandler<Ctx>(mw: Middleware<Ctx>, handler: ErrorHandler<Ctx>): Middleware<Ctx>;
16
16
 
17
- export { type ErrorHandler, type Middleware, type MiddlewarePipeline, branch, compose, createPipeline, withErrorHandler };
17
+ declare class MiddlewareTimeoutError extends Error {
18
+ readonly timeoutMs: number;
19
+ constructor(timeoutMs: number);
20
+ }
21
+ declare function withTimeout<Ctx>(mw: Middleware<Ctx>, ms: number): Middleware<Ctx>;
22
+
23
+ declare function tap<Ctx>(fn: (ctx: Ctx) => void | Promise<void>): Middleware<Ctx>;
24
+
25
+ export { type ErrorHandler, type Middleware, type MiddlewarePipeline, MiddlewareTimeoutError, branch, compose, createPipeline, tap, withErrorHandler, withTimeout };
package/dist/index.js CHANGED
@@ -70,6 +70,38 @@ function withErrorHandler(mw, handler) {
70
70
  };
71
71
  }
72
72
 
73
- export { branch, compose, createPipeline, withErrorHandler };
73
+ // src/timeout.ts
74
+ var MiddlewareTimeoutError = class extends Error {
75
+ constructor(timeoutMs) {
76
+ super(`Middleware timed out after ${timeoutMs}ms`);
77
+ this.name = "MiddlewareTimeoutError";
78
+ this.timeoutMs = timeoutMs;
79
+ }
80
+ };
81
+ function withTimeout(mw, ms) {
82
+ return async (ctx, next) => {
83
+ let timer;
84
+ try {
85
+ await Promise.race([
86
+ mw(ctx, next),
87
+ new Promise((_, reject) => {
88
+ timer = setTimeout(() => reject(new MiddlewareTimeoutError(ms)), ms);
89
+ })
90
+ ]);
91
+ } finally {
92
+ if (timer) clearTimeout(timer);
93
+ }
94
+ };
95
+ }
96
+
97
+ // src/tap.ts
98
+ function tap(fn) {
99
+ return async (ctx, next) => {
100
+ await fn(ctx);
101
+ await next();
102
+ };
103
+ }
104
+
105
+ export { MiddlewareTimeoutError, branch, compose, createPipeline, tap, withErrorHandler, withTimeout };
74
106
  //# sourceMappingURL=index.js.map
75
107
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/compose.ts","../src/pipeline.ts","../src/branch.ts","../src/error.ts"],"names":[],"mappings":";AAEO,SAAS,WAAgB,WAAA,EAAiD;AAC/E,EAAA,OAAO,OAAO,KAAU,IAAA,KAA8B;AACpD,IAAA,IAAI,KAAA,GAAQ,EAAA;AAEZ,IAAA,eAAe,SAAS,CAAA,EAA0B;AAChD,MAAA,IAAI,CAAA,IAAK,KAAA,EAAO,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAC9D,MAAA,KAAA,GAAQ,CAAA;AAER,MAAA,MAAM,KAAK,CAAA,GAAI,WAAA,CAAY,MAAA,GAAS,WAAA,CAAY,CAAC,CAAA,GAAI,IAAA;AACrD,MAAA,IAAI,CAAC,EAAA,EAAI;AAET,MAAA,MAAM,GAAG,GAAA,EAAK,MAAM,QAAA,CAAS,CAAA,GAAI,CAAC,CAAC,CAAA;AAAA,IACrC;AAEA,IAAA,MAAM,SAAS,CAAC,CAAA;AAAA,EAClB,CAAA;AACF;;;ACTO,SAAS,cAAA,GAA+C;AAC7D,EAAA,MAAM,QAA2B,EAAC;AAElC,EAAA,MAAM,QAAA,GAAoC;AAAA,IACxC,IAAI,EAAA,EAAqB;AACvB,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAA,CAAM,WAA8C,EAAA,EAAqB;AACvE,MAAA,IAAI,OAAO,cAAc,SAAA,EAAW;AAClC,QAAA,IAAI,SAAA,EAAW,KAAA,CAAM,IAAA,CAAK,EAAE,CAAA;AAAA,MAC9B,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,IAAA,CAAK,OAAO,GAAA,EAAK,IAAA,KAAS;AAC9B,UAAA,IAAI,SAAA,CAAU,GAAG,CAAA,EAAG;AAClB,YAAA,MAAM,EAAA,CAAG,KAAK,IAAI,CAAA;AAAA,UACpB,CAAA,MAAO;AACL,YAAA,MAAM,IAAA,EAAK;AAAA,UACb;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AACA,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAA,GAAQ;AACN,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAG,KAAK,CAAA;AACjC,MAAA,OAAO,OAAO,GAAA,KAAa;AACzB,QAAA,MAAM,QAAA,CAAS,KAAK,YAAY;AAAA,QAAC,CAAC,CAAA;AAAA,MACpC,CAAA;AAAA,IACF;AAAA,GACF;AAEA,EAAA,OAAO,QAAA;AACT;;;ACxCO,SAAS,MAAA,CACd,SAAA,EACA,MAAA,EACA,OAAA,EACiB;AACjB,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,IAAI,SAAA,CAAU,GAAG,CAAA,EAAG;AAClB,MAAA,MAAM,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,IACxB,WAAW,OAAA,EAAS;AAClB,MAAA,MAAM,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,IACzB,CAAA,MAAO;AACL,MAAA,MAAM,IAAA,EAAK;AAAA,IACb;AAAA,EACF,CAAA;AACF;;;ACdO,SAAS,gBAAA,CACd,IACA,OAAA,EACiB;AACjB,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,EAAA,CAAG,KAAK,IAAI,CAAA;AAAA,IACpB,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AACF","file":"index.js","sourcesContent":["import type { Middleware } from './types';\n\nexport function compose<Ctx>(...middlewares: Middleware<Ctx>[]): Middleware<Ctx> {\n return async (ctx: Ctx, next: () => Promise<void>) => {\n let index = -1;\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) throw new Error('next() called multiple times');\n index = i;\n\n const mw = i < middlewares.length ? middlewares[i] : next;\n if (!mw) return;\n\n await mw(ctx, () => dispatch(i + 1));\n }\n\n await dispatch(0);\n };\n}\n","import type { Middleware } from './types';\nimport { compose } from './compose';\n\nexport interface MiddlewarePipeline<Ctx> {\n use(mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n build(): (ctx: Ctx) => Promise<void>;\n}\n\nexport function createPipeline<Ctx>(): MiddlewarePipeline<Ctx> {\n const stack: Middleware<Ctx>[] = [];\n\n const pipeline: MiddlewarePipeline<Ctx> = {\n use(mw: Middleware<Ctx>) {\n stack.push(mw);\n return pipeline;\n },\n\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>) {\n if (typeof condition === 'boolean') {\n if (condition) stack.push(mw);\n } else {\n stack.push(async (ctx, next) => {\n if (condition(ctx)) {\n await mw(ctx, next);\n } else {\n await next();\n }\n });\n }\n return pipeline;\n },\n\n build() {\n const composed = compose(...stack);\n return async (ctx: Ctx) => {\n await composed(ctx, async () => {});\n };\n },\n };\n\n return pipeline;\n}\n","import type { Middleware } from './types';\n\nexport function branch<Ctx>(\n condition: (ctx: Ctx) => boolean,\n trueMw: Middleware<Ctx>,\n falseMw?: Middleware<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n if (condition(ctx)) {\n await trueMw(ctx, next);\n } else if (falseMw) {\n await falseMw(ctx, next);\n } else {\n await next();\n }\n };\n}\n","import type { Middleware, ErrorHandler } from './types';\n\nexport function withErrorHandler<Ctx>(\n mw: Middleware<Ctx>,\n handler: ErrorHandler<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n try {\n await mw(ctx, next);\n } catch (err) {\n await handler(err, ctx);\n }\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/compose.ts","../src/pipeline.ts","../src/branch.ts","../src/error.ts","../src/timeout.ts","../src/tap.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Middleware } from './types';\n\nexport function compose<Ctx>(...middlewares: Middleware<Ctx>[]): Middleware<Ctx> {\n return async (ctx: Ctx, next: () => Promise<void>) => {\n let index = -1;\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) throw new Error('next() called multiple times');\n index = i;\n\n const mw = i < middlewares.length ? middlewares[i] : next;\n if (!mw) return;\n\n await mw(ctx, () => dispatch(i + 1));\n }\n\n await dispatch(0);\n };\n}\n","import type { Middleware } from './types';\nimport { compose } from './compose';\n\nexport interface MiddlewarePipeline<Ctx> {\n use(mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>): MiddlewarePipeline<Ctx>;\n build(): (ctx: Ctx) => Promise<void>;\n}\n\nexport function createPipeline<Ctx>(): MiddlewarePipeline<Ctx> {\n const stack: Middleware<Ctx>[] = [];\n\n const pipeline: MiddlewarePipeline<Ctx> = {\n use(mw: Middleware<Ctx>) {\n stack.push(mw);\n return pipeline;\n },\n\n useIf(condition: boolean | ((ctx: Ctx) => boolean), mw: Middleware<Ctx>) {\n if (typeof condition === 'boolean') {\n if (condition) stack.push(mw);\n } else {\n stack.push(async (ctx, next) => {\n if (condition(ctx)) {\n await mw(ctx, next);\n } else {\n await next();\n }\n });\n }\n return pipeline;\n },\n\n build() {\n const composed = compose(...stack);\n return async (ctx: Ctx) => {\n await composed(ctx, async () => {});\n };\n },\n };\n\n return pipeline;\n}\n","import type { Middleware } from './types';\n\nexport function branch<Ctx>(\n condition: (ctx: Ctx) => boolean,\n trueMw: Middleware<Ctx>,\n falseMw?: Middleware<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n if (condition(ctx)) {\n await trueMw(ctx, next);\n } else if (falseMw) {\n await falseMw(ctx, next);\n } else {\n await next();\n }\n };\n}\n","import type { Middleware, ErrorHandler } from './types';\n\nexport function withErrorHandler<Ctx>(\n mw: Middleware<Ctx>,\n handler: ErrorHandler<Ctx>,\n): Middleware<Ctx> {\n return async (ctx, next) => {\n try {\n await mw(ctx, next);\n } catch (err) {\n await handler(err, ctx);\n }\n };\n}\n","import type { Middleware } from './types';\n\nexport class MiddlewareTimeoutError extends Error {\n public readonly timeoutMs: number;\n constructor(timeoutMs: number) {\n super(`Middleware timed out after ${timeoutMs}ms`);\n this.name = 'MiddlewareTimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport function withTimeout<Ctx>(mw: Middleware<Ctx>, ms: number): Middleware<Ctx> {\n return async (ctx, next) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n await Promise.race([\n mw(ctx, next),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new MiddlewareTimeoutError(ms)), ms);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n };\n}\n","import type { Middleware } from './types';\n\nexport function tap<Ctx>(fn: (ctx: Ctx) => void | Promise<void>): Middleware<Ctx> {\n return async (ctx, next) => {\n await fn(ctx);\n await next();\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@philiprehberger/middleware-ts",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic middleware composition engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "tsup": "^8.0.0",
33
- "typescript": "^5.0.0"
33
+ "typescript": "^6.0.2"
34
34
  },
35
35
  "keywords": [
36
36
  "middleware",