Skip to content

Commit 57bca28

Browse files
authored
refactor!: explicit use of handlers or middleware (#1128)
1 parent 01d4506 commit 57bca28

File tree

7 files changed

+46
-83
lines changed

7 files changed

+46
-83
lines changed

src/h3.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,14 @@ import { toResponse, kNotFound } from "./response.ts";
44
import { callMiddleware, normalizeMiddleware } from "./middleware.ts";
55

66
import type { RouterContext, MatchedRoute } from "rou3";
7-
import type {
8-
FetchHandler,
9-
H3Config,
10-
H3Plugin,
11-
H3RouteMeta,
12-
} from "./types/h3.ts";
7+
import type { FetchHandler, H3Config, H3Plugin } from "./types/h3.ts";
138
import type { H3EventContext } from "./types/context.ts";
149
import type { EventHandler, Middleware } from "./types/handler.ts";
1510
import type {
1611
H3Route,
1712
HTTPMethod,
1813
H3 as H3Type,
1914
RouteOptions,
20-
RouteHandler,
2115
MiddlewareOptions,
2216
} from "./types/h3.ts";
2317
import type { ServerRequest } from "srvx";
@@ -120,32 +114,24 @@ export const H3Core = /* @__PURE__ */ (() => {
120114
return this as unknown as H3Type;
121115
}
122116

123-
all(route: string, handler: RouteHandler, opts?: RouteOptions): H3Type {
117+
all(route: string, handler: EventHandler, opts?: RouteOptions): H3Type {
124118
return this.on("", route, handler, opts);
125119
}
126120

127121
on(
128122
method: HTTPMethod | Lowercase<HTTPMethod> | "",
129123
route: string,
130-
handler: RouteHandler,
124+
handler: EventHandler,
131125
opts?: RouteOptions,
132126
): H3Type {
133127
const _method = (method || "").toUpperCase();
134-
let _handler: EventHandler;
135-
let meta: H3RouteMeta | undefined = opts?.meta;
136-
if ((handler as H3Type).handler) {
137-
_handler = (handler as H3Type).handler;
138-
} else {
139-
_handler = handler as EventHandler;
140-
meta = { ...(handler as EventHandler).meta, ...meta };
141-
}
142128
route = new URL(route, "h://_").pathname;
143129
this._addRoute({
144130
method: _method as HTTPMethod,
145131
route,
146-
handler: _handler,
132+
handler,
147133
middleware: opts?.middleware,
148-
meta,
134+
meta: { ...(handler as EventHandler).meta, ...opts?.meta },
149135
});
150136
return this as unknown as H3Type;
151137
}
@@ -176,7 +162,7 @@ export const H3Core = /* @__PURE__ */ (() => {
176162
(H3Core as any).prototype[method.toLowerCase()] = function (
177163
this: H3Type,
178164
route: string,
179-
handler: EventHandler | H3Type,
165+
handler: EventHandler,
180166
opts?: RouteOptions,
181167
) {
182168
return this.on(method, route, handler, opts);

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export type {
88
HTTPMethod,
99
PreparedResponse,
1010
RouteOptions,
11-
RouteHandler,
1211
MiddlewareOptions,
1312
FetchHandler,
1413
} from "./types/h3.ts";

src/middleware.ts

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { routeToRegExp } from "rou3";
22
import { kNotFound } from "./response.ts";
33

4-
import type { H3 } from "./h3.ts";
54
import type { H3Event } from "./event.ts";
65
import type { MiddlewareOptions } from "./types/h3.ts";
76
import type { EventHandler, Middleware } from "./types/handler.ts";
@@ -11,44 +10,23 @@ export function defineMiddleware(input: Middleware): Middleware {
1110
}
1211

1312
export function normalizeMiddleware(
14-
input: Middleware | H3,
13+
input: Middleware,
1514
opts: MiddlewareOptions & { route?: string } = {},
1615
): Middleware {
1716
const matcher = createMatcher(opts);
18-
19-
if (typeof input === "function") {
20-
if (
21-
!matcher &&
22-
(input.length > 1 || input.constructor?.name === "AsyncFunction")
23-
) {
24-
return input; // Fast path: async or with explicit next() and no matcher filters
25-
}
26-
return (event, next) => {
27-
if (matcher && !matcher(event)) {
28-
return next();
29-
}
30-
const res = input(event, next);
31-
return res === undefined ? next() : res;
32-
};
33-
}
34-
// H3 sub-app
35-
if (typeof (input as H3).handler === "function") {
36-
return (event, next) => {
37-
if (matcher && !matcher(event)) {
38-
return next();
39-
}
40-
const res = (input as H3).handler(event);
41-
if (res === kNotFound) {
42-
return next();
43-
} else if (res instanceof Promise) {
44-
return res.then((resolved) =>
45-
resolved === kNotFound ? next() : resolved,
46-
);
47-
}
48-
return res === undefined ? next() : res;
49-
};
17+
if (
18+
!matcher &&
19+
(input.length > 1 || input.constructor?.name === "AsyncFunction")
20+
) {
21+
return input; // Fast path: async or with explicit next() and no matcher filters
5022
}
51-
throw new Error(`Invalid middleware: ${input}`);
23+
return (event, next) => {
24+
if (matcher && !matcher(event)) {
25+
return next();
26+
}
27+
const res = input(event, next);
28+
return res === undefined || res === kNotFound ? next() : res;
29+
};
5230
}
5331

5432
function createMatcher(opts: MiddlewareOptions & { route?: string }) {
@@ -93,9 +71,11 @@ export function callMiddleware(
9371
const fn = middleware[index];
9472
const next = () => callMiddleware(event, middleware, handler, index + 1);
9573
const ret = fn(event, next);
96-
return ret === undefined
74+
return ret === undefined || ret === kNotFound
9775
? next()
9876
: ret instanceof Promise
99-
? ret.then((resolved) => (resolved === undefined ? next() : resolved))
77+
? ret.then((resolved) =>
78+
resolved === undefined || resolved === kNotFound ? next() : resolved,
79+
)
10080
: ret;
10181
}

src/types/h3.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ export function definePlugin<T = unknown>(
4848

4949
// --- H3 App ---
5050

51-
export type RouteHandler = EventHandler | { handler: EventHandler };
52-
5351
export type FetchHandler = (req: ServerRequest) => Response | Promise<Response>;
5452

5553
export type RouteOptions = {
@@ -130,31 +128,31 @@ export declare class H3 {
130128
/**
131129
* Register a global middleware.
132130
*/
133-
use(route: string, handler: Middleware | H3, opts?: MiddlewareOptions): this;
134-
use(handler: Middleware | H3, opts?: MiddlewareOptions): this;
131+
use(route: string, handler: Middleware, opts?: MiddlewareOptions): this;
132+
use(handler: Middleware, opts?: MiddlewareOptions): this;
135133

136134
/**
137135
* Register a route handler for the specified HTTP method and route.
138136
*/
139137
on(
140138
method: HTTPMethod | Lowercase<HTTPMethod> | "",
141139
route: string,
142-
handler: RouteHandler,
140+
handler: EventHandler,
143141
opts?: RouteOptions,
144142
): this;
145143

146144
/**
147145
* Register a route handler for all HTTP methods.
148146
*/
149-
all(route: string, handler: RouteHandler, opts?: RouteOptions): this;
150-
151-
get(route: string, handler: RouteHandler, opts?: RouteOptions): this;
152-
post(route: string, handler: RouteHandler, opts?: RouteOptions): this;
153-
put(route: string, handler: RouteHandler, opts?: RouteOptions): this;
154-
delete(route: string, handler: RouteHandler, opts?: RouteOptions): this;
155-
patch(route: string, handler: RouteHandler, opts?: RouteOptions): this;
156-
head(route: string, handler: RouteHandler, opts?: RouteOptions): this;
157-
options(route: string, handler: RouteHandler, opts?: RouteOptions): this;
158-
connect(route: string, handler: RouteHandler, opts?: RouteOptions): this;
159-
trace(route: string, handler: RouteHandler, opts?: RouteOptions): this;
147+
all(route: string, handler: EventHandler, opts?: RouteOptions): this;
148+
149+
get(route: string, handler: EventHandler, opts?: RouteOptions): this;
150+
post(route: string, handler: EventHandler, opts?: RouteOptions): this;
151+
put(route: string, handler: EventHandler, opts?: RouteOptions): this;
152+
delete(route: string, handler: EventHandler, opts?: RouteOptions): this;
153+
patch(route: string, handler: EventHandler, opts?: RouteOptions): this;
154+
head(route: string, handler: EventHandler, opts?: RouteOptions): this;
155+
options(route: string, handler: EventHandler, opts?: RouteOptions): this;
156+
connect(route: string, handler: EventHandler, opts?: RouteOptions): this;
157+
trace(route: string, handler: EventHandler, opts?: RouteOptions): this;
160158
}

test/middleware.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describeMatrix("middleware", (t, { it, expect }) => {
3535
event.req.headers.has("x-async")
3636
? Promise.resolve("Hello World!")
3737
: "Hello World!",
38-
),
38+
).handler,
3939
{
4040
method: "GET",
4141
match: (event) => !event.req.headers.has("x-skip"),

test/router.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
2020
it("Multiple Routers", async () => {
2121
const secondRouter = new H3().get("/router2", () => "router2");
2222

23-
t.app.use(secondRouter);
23+
t.app.use(secondRouter.handler);
2424

2525
const res1 = await t.fetch("/");
2626
expect(await res1.text()).toEqual("Hello");
@@ -84,7 +84,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
8484
router = new H3()
8585
.get("/preemptive/test", () => "Test")
8686
.get("/preemptive/undefined", () => undefined);
87-
t.app.all("/**", router);
87+
t.app.all("/**", router.handler);
8888
});
8989

9090
it("Handle /test", async () => {
@@ -120,7 +120,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
120120
expect(getRouterParams(event)).toMatchObject({ name: "string" });
121121
return "200";
122122
});
123-
t.app.use(router);
123+
t.app.use(router.handler);
124124
const result = await t.fetch("/test/params/string");
125125

126126
expect(await result.text()).toBe("200");
@@ -133,7 +133,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
133133
});
134134
return "200";
135135
});
136-
t.app.use(router);
136+
t.app.use(router.handler);
137137
const result = await t.fetch("/test/params/string with space");
138138

139139
expect(await result.text()).toBe("200");
@@ -160,7 +160,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
160160
expect(getRouterParam(event, "name")).toEqual("string");
161161
return "200";
162162
});
163-
t.app.use(router);
163+
t.app.use(router.handler);
164164
const result = await t.fetch("/test/params/string");
165165

166166
expect(await result.text()).toBe("200");
@@ -173,7 +173,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
173173
);
174174
return "200";
175175
});
176-
t.app.use(router);
176+
t.app.use(router.handler);
177177
const result = await t.fetch("/test/params/string with space");
178178

179179
expect(await result.text()).toBe("200");
@@ -204,7 +204,7 @@ describeMatrix("router", (t, { it, expect, describe }) => {
204204
});
205205
return "200";
206206
});
207-
t.app.use(router);
207+
t.app.use(router.handler);
208208
const result = await t.fetch("/test/path");
209209

210210
expect(await result.text()).toBe("200");

test/session.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describeMatrix("session", (t, { it, expect }) => {
2424
}
2525
return { session };
2626
});
27-
t.app.use(app);
27+
t.app.use(app.handler);
2828
});
2929

3030
it("initiates session", async () => {

0 commit comments

Comments
 (0)