@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 +50 -10
- package/dist/index.cjs +35 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
# @philiprehberger/ts
|
|
1
|
+
# @philiprehberger/middleware-ts
|
|
2
2
|
|
|
3
|
-
[](https://github.com/philiprehberger/ts-middleware/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@philiprehberger/middleware-ts)
|
|
5
|
+
[](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
|
|
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
|
|
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
|
|
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
|
package/dist/index.cjs.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.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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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": "^
|
|
33
|
+
"typescript": "^6.0.2"
|
|
34
34
|
},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"middleware",
|