@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 +28 -5
- 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,8 +1,8 @@
|
|
|
1
1
|
# @philiprehberger/middleware-ts
|
|
2
2
|
|
|
3
|
-
[](https://github.com/philiprehberger/ts-middleware/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/@philiprehberger/middleware-ts)
|
|
5
|
-
[](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
|
|
87
|
+
⭐ [Star the repo](https://github.com/philiprehberger/ts-middleware)
|
|
65
88
|
|
|
66
|
-
🐛 [Report issues](https://github.com/philiprehberger/middleware
|
|
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
|
|
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
|
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":";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;;;ACXO,IAAM,sBAAA,GAAN,cAAqC,KAAA,CAAM;AAAA,EAEhD,YAAY,SAAA,EAAmB;AAC7B,IAAA,KAAA,CAAM,CAAA,2BAAA,EAA8B,SAAS,CAAA,EAAA,CAAI,CAAA;AACjD,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAEO,SAAS,WAAA,CAAiB,IAAqB,EAAA,EAA6B;AACjF,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAM,QAAQ,IAAA,CAAK;AAAA,QACjB,EAAA,CAAG,KAAK,IAAI,CAAA;AAAA,QACZ,IAAI,OAAA,CAAe,CAAC,CAAA,EAAG,MAAA,KAAW;AAChC,UAAA,KAAA,GAAQ,UAAA,CAAW,MAAM,MAAA,CAAO,IAAI,uBAAuB,EAAE,CAAC,GAAG,EAAE,CAAA;AAAA,QACrE,CAAC;AAAA,OACF,CAAA;AAAA,IACH,CAAA,SAAE;AACA,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA;AACF;;;ACvBO,SAAS,IAAS,EAAA,EAAyD;AAChF,EAAA,OAAO,OAAO,KAAK,IAAA,KAAS;AAC1B,IAAA,MAAM,GAAG,GAAG,CAAA;AACZ,IAAA,MAAM,IAAA,EAAK;AAAA,EACb,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","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",
|