@bronti/wooter 0.0.0-stage → 6.0.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/DOC.md +289 -0
- package/LICENSE +7 -0
- package/README.md +23 -2
- package/package.json +13 -4
- package/src/Graph.ts +231 -0
- package/src/MiddlewareContext.ts +216 -0
- package/src/RouteContext.ts +312 -0
- package/src/Wooter.ts +318 -0
- package/src/WooterError.ts +26 -0
- package/src/export/error.ts +17 -0
- package/src/export/mod.ts +10 -0
- package/src/export/option.ts +8 -0
- package/src/export/path.ts +13 -0
- package/src/export/response.ts +39 -0
- package/src/export/result.ts +14 -0
- package/src/export/types.ts +1 -0
- package/src/export/use.ts +68 -0
- package/src/path/index.ts +84 -0
- package/src/path/match.ts +82 -0
- package/src/path/matcher.ts +29 -0
- package/src/path/types.ts +39 -0
- package/src/types/internal.ts +41 -0
- package/src/types/types.ts +39 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { EmptyObject, State } from "./types/types.ts";
|
|
2
|
+
import RouteContext, {
|
|
3
|
+
HandlerDidntRespondError,
|
|
4
|
+
type InternalHandler,
|
|
5
|
+
RouteContext__execution,
|
|
6
|
+
RouteContext__respond,
|
|
7
|
+
} from "./RouteContext.ts";
|
|
8
|
+
import WooterError from "./WooterError.ts";
|
|
9
|
+
import { err, ok, type Result } from "./export/result.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when middleware exits without delegating to `next()`.
|
|
13
|
+
*/
|
|
14
|
+
export class MiddlewareHandlerDidntCallUpError extends WooterError {
|
|
15
|
+
/** Error name used for identification. */
|
|
16
|
+
override name: string = "MiddlewareHandlerDidntCallUpError";
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
super("The middleware handler must call ctx.next() before exiting");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Middleware context passed to middleware handlers.
|
|
25
|
+
*
|
|
26
|
+
* Extends {@link RouteContext} with flow-control helpers for composing middleware chains.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TParams Route param shape.
|
|
29
|
+
* @typeParam TState State currently available on the context.
|
|
30
|
+
* @typeParam TNextState State shape that this middleware can pass to the next handler.
|
|
31
|
+
*/
|
|
32
|
+
export default class MiddlewareContext<
|
|
33
|
+
TParams extends Record<string, unknown> | undefined = undefined,
|
|
34
|
+
TState extends State | undefined = undefined,
|
|
35
|
+
TNextState extends State | undefined = undefined,
|
|
36
|
+
> extends RouteContext<TParams, TState> {
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
* Internal marker that tracks whether `next()` or `tryNext()` has been called.
|
|
40
|
+
*/
|
|
41
|
+
private calledNext = false;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @internal
|
|
45
|
+
* Creates a middleware context instance.
|
|
46
|
+
*
|
|
47
|
+
* @param request Current request.
|
|
48
|
+
* @param state Context state.
|
|
49
|
+
* @param params Route params.
|
|
50
|
+
* @param nextHandler Internal continuation handler.
|
|
51
|
+
*/
|
|
52
|
+
constructor(
|
|
53
|
+
override readonly request: Request,
|
|
54
|
+
state: TState extends undefined ? EmptyObject : TState,
|
|
55
|
+
params: TParams extends undefined ? EmptyObject : TParams,
|
|
56
|
+
private readonly nextHandler: InternalHandler,
|
|
57
|
+
reporter: (e: unknown) => void,
|
|
58
|
+
) {
|
|
59
|
+
super(request, state, params, reporter);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Invokes the next handler in the middleware chain.
|
|
64
|
+
*
|
|
65
|
+
* @param state State to merge into downstream context.
|
|
66
|
+
* @param request Optional request override.
|
|
67
|
+
* @returns The downstream response, or throws the downstream error.
|
|
68
|
+
*/
|
|
69
|
+
readonly next = async (
|
|
70
|
+
state: TNextState extends undefined ? EmptyObject : TNextState,
|
|
71
|
+
request?: Request,
|
|
72
|
+
): Promise<Response> => {
|
|
73
|
+
const opt = await this.tryNext(state, request);
|
|
74
|
+
return opt.match(
|
|
75
|
+
(c) => {
|
|
76
|
+
return c;
|
|
77
|
+
},
|
|
78
|
+
(e) => {
|
|
79
|
+
throw e;
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Like {@link next}, but captures failures in a `Result`.
|
|
86
|
+
*
|
|
87
|
+
* @param state State to merge into downstream context.
|
|
88
|
+
* @param request Optional request override.
|
|
89
|
+
* @returns `ok(response)` on success or `err(error)` on failure.
|
|
90
|
+
*/
|
|
91
|
+
readonly tryNext = (
|
|
92
|
+
state: TNextState extends undefined ? EmptyObject : TNextState,
|
|
93
|
+
request?: Request,
|
|
94
|
+
): Promise<Result<Response, unknown>> => {
|
|
95
|
+
const { promise, resolve } =
|
|
96
|
+
Promise.withResolvers<Result<Response, unknown>>();
|
|
97
|
+
this.calledNext = true;
|
|
98
|
+
const ctx = this.nextHandler(state, request || this.request, this.reporter);
|
|
99
|
+
this[RouteContext__respond].then((response) => {
|
|
100
|
+
if (!ctx[RouteContext__respond].resolved) {
|
|
101
|
+
ctx[RouteContext__respond].push(response);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
ctx[RouteContext__respond].then((response) => {
|
|
105
|
+
resolve(ok(response));
|
|
106
|
+
});
|
|
107
|
+
ctx[RouteContext__execution].then((v) => {
|
|
108
|
+
v.inspect((e) => {
|
|
109
|
+
if (
|
|
110
|
+
e instanceof HandlerDidntRespondError &&
|
|
111
|
+
this.respondSoon.resolved
|
|
112
|
+
) {
|
|
113
|
+
this[RouteContext__respond].then((response) => {
|
|
114
|
+
resolve(ok(response));
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
resolve(err(e));
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return promise;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Invokes {@link next} and immediately responds with the downstream response.
|
|
126
|
+
*
|
|
127
|
+
* @param state State to merge into downstream context.
|
|
128
|
+
* @param request Optional request override.
|
|
129
|
+
* @returns The response sent by `resp`.
|
|
130
|
+
*/
|
|
131
|
+
readonly forward = (
|
|
132
|
+
state: TNextState extends undefined ? EmptyObject : TNextState,
|
|
133
|
+
request?: Request,
|
|
134
|
+
): Promise<Response> =>
|
|
135
|
+
this.next(state, request).then((response) =>
|
|
136
|
+
this.respondSoon.resolved ? response : this.resp(response),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Invokes {@link tryNext} and maps successful responses through `resp`.
|
|
141
|
+
*
|
|
142
|
+
* @param state State to merge into downstream context.
|
|
143
|
+
* @param request Optional request override.
|
|
144
|
+
* @returns Result containing the response or captured error.
|
|
145
|
+
*/
|
|
146
|
+
readonly tryForward = (
|
|
147
|
+
state: TNextState extends undefined ? EmptyObject : TNextState,
|
|
148
|
+
request?: Request,
|
|
149
|
+
): Promise<Result<Response, unknown>> =>
|
|
150
|
+
this.tryNext(state, request).then((o) =>
|
|
151
|
+
o.map((response) =>
|
|
152
|
+
this.respondSoon.resolved ? response : this.resp(response),
|
|
153
|
+
),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Adapts a middleware handler into the router's internal handler signature.
|
|
158
|
+
*
|
|
159
|
+
* @param handler User middleware handler.
|
|
160
|
+
* @param params Route params for the current match.
|
|
161
|
+
* @param next Continuation for the next link in the chain.
|
|
162
|
+
* @returns Internal handler that executes middleware and reports lifecycle errors.
|
|
163
|
+
*
|
|
164
|
+
* @internal
|
|
165
|
+
*/
|
|
166
|
+
static useMiddlewareHandler<
|
|
167
|
+
TParams extends Record<string, unknown> = Record<string, unknown>,
|
|
168
|
+
TState extends State | undefined = undefined,
|
|
169
|
+
TNextState extends State | undefined = undefined,
|
|
170
|
+
>(
|
|
171
|
+
handler: MiddlewareHandler<TParams, TState, TNextState>,
|
|
172
|
+
params: Record<string, unknown>,
|
|
173
|
+
next: InternalHandler,
|
|
174
|
+
reporter: (e: unknown) => void,
|
|
175
|
+
): InternalHandler {
|
|
176
|
+
return (state, req) => {
|
|
177
|
+
const ctx = new MiddlewareContext<TParams, TState, TNextState>(
|
|
178
|
+
req,
|
|
179
|
+
state as TState extends undefined ? EmptyObject : TState,
|
|
180
|
+
params as TParams extends undefined ? EmptyObject : TParams,
|
|
181
|
+
next,
|
|
182
|
+
reporter,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
Promise.try(handler, ctx).then(
|
|
186
|
+
() => {
|
|
187
|
+
if (!ctx.respondSoon.resolved) {
|
|
188
|
+
if (!ctx.calledNext)
|
|
189
|
+
return ctx.catchErr(new MiddlewareHandlerDidntCallUpError());
|
|
190
|
+
return ctx.catchErr(new HandlerDidntRespondError());
|
|
191
|
+
}
|
|
192
|
+
ctx.ok();
|
|
193
|
+
},
|
|
194
|
+
(e) => {
|
|
195
|
+
ctx.catchErr(e);
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
return ctx as unknown as MiddlewareContext;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Middleware handler function.
|
|
206
|
+
*
|
|
207
|
+
* @param ctx Middleware context.
|
|
208
|
+
* @returns Optional promise for async middleware.
|
|
209
|
+
*/
|
|
210
|
+
export type MiddlewareHandler<
|
|
211
|
+
TParams extends Record<string, unknown> = Record<string, unknown>,
|
|
212
|
+
TState extends State | undefined = undefined,
|
|
213
|
+
TNextState extends State | undefined = undefined,
|
|
214
|
+
> = (
|
|
215
|
+
ctx: MiddlewareContext<TParams, TState, TNextState>,
|
|
216
|
+
) => Promise<unknown> | unknown;
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { none, type Option, some } from "./export/option.ts";
|
|
2
|
+
import { Soon } from "@bronti/robust/Soon";
|
|
3
|
+
import type { EmptyObject, State } from "./types/types.ts";
|
|
4
|
+
import WooterError, { ControlFlowBreak } from "./WooterError.ts";
|
|
5
|
+
import { TypedMap } from "@bronti/robust/TypedMap";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Error thrown when a handler exits without calling `resp()`.
|
|
9
|
+
*/
|
|
10
|
+
export class HandlerDidntRespondError extends WooterError {
|
|
11
|
+
/** Error name used for identification. */
|
|
12
|
+
override name: string = "HandlerDidntRespondError";
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super("The handler must respond before exiting");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Error thrown when a handler attempts to respond more than once.
|
|
21
|
+
*/
|
|
22
|
+
export class HandlerRespondedTwiceError extends WooterError {
|
|
23
|
+
/** Error name used for identification. */
|
|
24
|
+
override name: string = "HandlerRespondedTwiceError";
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
super("The handler called resp() multiple times");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Internal symbol for reading a context's execution state.
|
|
33
|
+
*/
|
|
34
|
+
export const RouteContext__execution = Symbol("RouteContext__execution");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Internal symbol for reading a context's response state.
|
|
38
|
+
*/
|
|
39
|
+
export const RouteContext__respond = Symbol("RouteContext__respond");
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Internal symbol for reading a context's stray-error reporter.
|
|
43
|
+
*/
|
|
44
|
+
export const RouteContext__reporter = Symbol("RouteContext__reporter");
|
|
45
|
+
/**
|
|
46
|
+
* Context passed to route handlers.
|
|
47
|
+
*
|
|
48
|
+
* @typeParam TParams Route param shape.
|
|
49
|
+
* @typeParam TState State shape accumulated from middleware.
|
|
50
|
+
*/
|
|
51
|
+
export default class RouteContext<
|
|
52
|
+
TParams extends Record<string, unknown> | undefined = undefined,
|
|
53
|
+
TState extends State | undefined = undefined,
|
|
54
|
+
> {
|
|
55
|
+
/**
|
|
56
|
+
* Backing store for context state.
|
|
57
|
+
*/
|
|
58
|
+
private readonly _state: TState extends undefined ? EmptyObject : TState;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Backing store for route params.
|
|
62
|
+
*/
|
|
63
|
+
private readonly _params: TypedMap<
|
|
64
|
+
TParams extends undefined ? EmptyObject : TParams
|
|
65
|
+
>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Middleware state available to the current handler.
|
|
69
|
+
*
|
|
70
|
+
* @returns The typed context state object.
|
|
71
|
+
*/
|
|
72
|
+
get state(): TState extends undefined ? EmptyObject : TState {
|
|
73
|
+
return this._state;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Route params captured from path matching.
|
|
78
|
+
*
|
|
79
|
+
* @returns Typed map of route params.
|
|
80
|
+
*/
|
|
81
|
+
get params(): TypedMap<TParams extends undefined ? EmptyObject : TParams> {
|
|
82
|
+
return this._params;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Tracks handler completion state.
|
|
87
|
+
*
|
|
88
|
+
* `none()` means successful completion, `some(error)` means failure.
|
|
89
|
+
*
|
|
90
|
+
* @internal
|
|
91
|
+
*/
|
|
92
|
+
protected executionSoon: Soon<Option<unknown>> = new Soon();
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Tracks the first response emitted by this context.
|
|
96
|
+
*
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
protected respondSoon: Soon<Response> = new Soon();
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Exposes {@link executionSoon} through a symbol-based internal API.
|
|
103
|
+
*
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
get [RouteContext__execution](): RouteContext["executionSoon"] {
|
|
107
|
+
return this.executionSoon;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Exposes {@link respondSoon} through a symbol-based internal API.
|
|
112
|
+
*
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
get [RouteContext__respond](): RouteContext["respondSoon"] {
|
|
116
|
+
return this.respondSoon;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Exposes the stray-error reporter through a symbol-based internal API.
|
|
121
|
+
*
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
get [RouteContext__reporter](): (e: unknown) => void {
|
|
125
|
+
return this.reporter;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Indicates if a response has already been sent.
|
|
130
|
+
*/
|
|
131
|
+
get responded(): boolean {
|
|
132
|
+
return this.respondSoon.resolved;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Parsed request URL.
|
|
137
|
+
*/
|
|
138
|
+
readonly url: URL;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Creates a route context.
|
|
142
|
+
*
|
|
143
|
+
* @param request Incoming request.
|
|
144
|
+
* @param state Context state from middleware.
|
|
145
|
+
* @param params Route params captured by path matching.
|
|
146
|
+
*
|
|
147
|
+
* @internal
|
|
148
|
+
*/
|
|
149
|
+
constructor(
|
|
150
|
+
/** Request object. */
|
|
151
|
+
readonly request: Request,
|
|
152
|
+
state: TState extends undefined ? EmptyObject : TState,
|
|
153
|
+
params: TParams extends undefined ? EmptyObject : TParams,
|
|
154
|
+
protected reporter: (e: unknown) => void,
|
|
155
|
+
) {
|
|
156
|
+
this.url = new URL(request.url);
|
|
157
|
+
this._state = state;
|
|
158
|
+
this._params = new TypedMap(params);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Marks execution as failed with an error.
|
|
163
|
+
*
|
|
164
|
+
* @param e Error value.
|
|
165
|
+
*
|
|
166
|
+
* @internal
|
|
167
|
+
*/
|
|
168
|
+
protected err(e: unknown) {
|
|
169
|
+
this.executionSoon.push(some(e));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Marks the handler as successfully completed.
|
|
174
|
+
*
|
|
175
|
+
* This is typically called by the internal runtime after handler execution settles.
|
|
176
|
+
*
|
|
177
|
+
* @returns `void`.
|
|
178
|
+
*/
|
|
179
|
+
readonly ok = (): void => {
|
|
180
|
+
if (!this.respondSoon.resolved)
|
|
181
|
+
return this.err(new HandlerDidntRespondError());
|
|
182
|
+
this.executionSoon.push(none());
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Responds to the request.
|
|
187
|
+
*
|
|
188
|
+
* @param response A fully constructed response.
|
|
189
|
+
* @param init Optional response init when the first argument is body-like.
|
|
190
|
+
* @returns The response that was sent.
|
|
191
|
+
* @throws HandlerRespondedTwiceError If called after a previous response.
|
|
192
|
+
*/
|
|
193
|
+
readonly resp: {
|
|
194
|
+
(response: Response): Response;
|
|
195
|
+
(body?: BodyInit | null, init?: ResponseInit): Response;
|
|
196
|
+
} = (responseOrBody, init?: ResponseInit): Response => {
|
|
197
|
+
const response =
|
|
198
|
+
responseOrBody instanceof Response
|
|
199
|
+
? responseOrBody
|
|
200
|
+
: new Response(responseOrBody, init);
|
|
201
|
+
if (this.executionSoon.resolved) {
|
|
202
|
+
console.warn(
|
|
203
|
+
"responding after execution is misuse of the wooter library",
|
|
204
|
+
);
|
|
205
|
+
return response;
|
|
206
|
+
}
|
|
207
|
+
if (this.respondSoon.resolved) {
|
|
208
|
+
throw new HandlerRespondedTwiceError();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.respondSoon.push(response);
|
|
212
|
+
return response;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Responds with a JSON body.
|
|
217
|
+
*
|
|
218
|
+
* @param data JSON-serializable response body.
|
|
219
|
+
* @param init Optional response init.
|
|
220
|
+
* @returns The response that was sent.
|
|
221
|
+
*/
|
|
222
|
+
readonly respJson: typeof Response.json = (data, init) =>
|
|
223
|
+
this.resp(Response.json(data, init));
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Safely exits handler execution without surfacing a framework error.
|
|
227
|
+
*
|
|
228
|
+
* Call this when you intentionally stop processing after sending a response.
|
|
229
|
+
*
|
|
230
|
+
* @throws ControlFlowBreak Always throws to abort current control flow.
|
|
231
|
+
*/
|
|
232
|
+
readonly safeExit = (): never => {
|
|
233
|
+
throw ControlFlowBreak;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Captures an internal handler error and routes it to the appropriate sink.
|
|
238
|
+
*
|
|
239
|
+
* @param e Error value.
|
|
240
|
+
*
|
|
241
|
+
* @internal
|
|
242
|
+
*/
|
|
243
|
+
protected catchErr = (e: unknown): void => {
|
|
244
|
+
if (this.respondSoon.resolved) {
|
|
245
|
+
if (e != ControlFlowBreak) this.reporter(e);
|
|
246
|
+
return this.ok();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this.err(e);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Adapts a user route handler into the internal runtime handler signature.
|
|
254
|
+
*
|
|
255
|
+
* @param handler Route handler to wrap.
|
|
256
|
+
* @param params Route params for the current match.
|
|
257
|
+
* @returns Internal handler that manages lifecycle success/error state.
|
|
258
|
+
*
|
|
259
|
+
* @internal
|
|
260
|
+
*/
|
|
261
|
+
static useRouteHandler<
|
|
262
|
+
TParams extends Record<string, unknown> | undefined = Record<
|
|
263
|
+
string,
|
|
264
|
+
unknown
|
|
265
|
+
>,
|
|
266
|
+
TState extends State | undefined = State,
|
|
267
|
+
>(
|
|
268
|
+
handler: RouteHandler<TParams, TState>,
|
|
269
|
+
params: Record<string, unknown>,
|
|
270
|
+
reporter: (e: unknown) => void,
|
|
271
|
+
): InternalHandler {
|
|
272
|
+
return (state, req) => {
|
|
273
|
+
const ctx = new RouteContext<TParams, TState>(
|
|
274
|
+
req,
|
|
275
|
+
state as TState extends undefined ? EmptyObject : TState,
|
|
276
|
+
params as TParams extends undefined ? EmptyObject : TParams,
|
|
277
|
+
reporter,
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
Promise.try(handler, ctx).then(
|
|
281
|
+
() => {
|
|
282
|
+
ctx.ok();
|
|
283
|
+
},
|
|
284
|
+
(e) => {
|
|
285
|
+
ctx.catchErr(e);
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return ctx as unknown as RouteContext;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Internal route handler signature used by the execution pipeline.
|
|
296
|
+
*/
|
|
297
|
+
export type InternalHandler = (
|
|
298
|
+
state: State,
|
|
299
|
+
request: Request,
|
|
300
|
+
reporter: (e: unknown) => void,
|
|
301
|
+
) => RouteContext;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Route handler function.
|
|
305
|
+
*
|
|
306
|
+
* @param ctx Route context.
|
|
307
|
+
* @returns Optional promise for async handlers.
|
|
308
|
+
*/
|
|
309
|
+
export type RouteHandler<
|
|
310
|
+
TParams extends Record<string, unknown> | undefined = Record<string, unknown>,
|
|
311
|
+
TState extends State | undefined = State,
|
|
312
|
+
> = (ctx: RouteContext<TParams, TState>) => Promise<unknown> | unknown;
|