@philiprehberger/middleware-ts 0.1.3 → 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,21 +1,21 @@
1
- # @philiprehberger/ts-middleware
1
+ # @philiprehberger/middleware-ts
2
2
 
3
- [![CI](https://github.com/philiprehberger/ts-middleware/actions/workflows/publish.yml/badge.svg)](https://github.com/philiprehberger/ts-middleware/actions/workflows/publish.yml)
4
- [![npm version](https://img.shields.io/npm/v/@philiprehberger/ts-middleware.svg)](https://www.npmjs.com/package/@philiprehberger/ts-middleware)
5
- [![License](https://img.shields.io/github/license/philiprehberger/ts-middleware)](LICENSE)
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
+ [![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/ts-middleware)](https://github.com/philiprehberger/ts-middleware/commits/main)
6
6
 
7
- Framework-agnostic middleware composition engine.
7
+ Framework-agnostic middleware composition engine
8
8
 
9
9
  ## Installation
10
10
 
11
11
  ```bash
12
- npm install @philiprehberger/ts-middleware
12
+ npm install @philiprehberger/middleware-ts
13
13
  ```
14
14
 
15
15
  ## Usage
16
16
 
17
17
  ```ts
18
- import { compose, createPipeline } from '@philiprehberger/ts-middleware';
18
+ import { compose, createPipeline } from '@philiprehberger/middleware-ts';
19
19
 
20
20
  type Ctx = { req: Request; user?: User };
21
21
 
@@ -31,7 +31,7 @@ await app({ req: new Request('/api') });
31
31
  ### Conditional Branching
32
32
 
33
33
  ```ts
34
- import { branch } from '@philiprehberger/ts-middleware';
34
+ import { branch } from '@philiprehberger/middleware-ts';
35
35
 
36
36
  const authBranch = branch(
37
37
  (ctx) => ctx.req.url.startsWith('/api'),
@@ -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,7 +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 |
51
-
72
+ | `withTimeout(mw, ms)` | Reject with `MiddlewareTimeoutError` if `mw` exceeds `ms` |
73
+ | `tap(fn)` | Run a side-effect and continue the pipeline |
52
74
 
53
75
  ## Development
54
76
 
@@ -58,6 +80,24 @@ npm run build
58
80
  npm test
59
81
  ```
60
82
 
83
+ ## Support
84
+
85
+ If you find this project useful:
86
+
87
+ ⭐ [Star the repo](https://github.com/philiprehberger/ts-middleware)
88
+
89
+ 🐛 [Report issues](https://github.com/philiprehberger/ts-middleware/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
90
+
91
+ 💡 [Suggest features](https://github.com/philiprehberger/ts-middleware/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
92
+
93
+ ❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)
94
+
95
+ 🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)
96
+
97
+ 💻 [GitHub Profile](https://github.com/philiprehberger)
98
+
99
+ 🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)
100
+
61
101
  ## License
62
102
 
63
- MIT
103
+ [MIT](LICENSE)
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.3",
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",