context-router 0.0.1
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/LICENSE +7 -0
- package/README.md +7 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +21 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +71 -0
- package/dist/router.js.map +1 -0
- package/dist/router.test.d.ts +2 -0
- package/dist/router.test.d.ts.map +1 -0
- package/dist/router.test.js +669 -0
- package/dist/router.test.js.map +1 -0
- package/package.json +40 -0
- package/src/router.test.ts +880 -0
- package/src/router.ts +102 -0
- package/tsconfig.json +26 -0
package/src/router.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
type HttpMethod = "GET" | "POST" | "DELETE" | "PATCH" | "PUT";
|
|
2
|
+
|
|
3
|
+
// Request handler parameters: request data and context
|
|
4
|
+
export type HandlerParams<Context extends Record<string, any> = {}> = {
|
|
5
|
+
body: string;
|
|
6
|
+
urlParams: Record<string, string | undefined>;
|
|
7
|
+
searchParams: URLSearchParams;
|
|
8
|
+
} & Context;
|
|
9
|
+
|
|
10
|
+
// Route handler: accepts request data and context, returns sync or async response
|
|
11
|
+
export type Handler<Context extends Record<string, any> = {}> = (
|
|
12
|
+
params: HandlerParams<Context>,
|
|
13
|
+
) => Response | Promise<Response>;
|
|
14
|
+
|
|
15
|
+
// HTTP router with URLPattern-based matching and context injection
|
|
16
|
+
export class Router<Context extends Record<string, any> = {}> {
|
|
17
|
+
private routes: Record<
|
|
18
|
+
HttpMethod,
|
|
19
|
+
{ pattern: URLPattern; handler: Handler<Context> }[]
|
|
20
|
+
> = {
|
|
21
|
+
GET: [],
|
|
22
|
+
POST: [],
|
|
23
|
+
DELETE: [],
|
|
24
|
+
PATCH: [],
|
|
25
|
+
PUT: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
private notFoundHandler: Handler<Context> = (
|
|
29
|
+
_params: HandlerParams<Context>,
|
|
30
|
+
) => new Response("Not Found", { status: 404 });
|
|
31
|
+
|
|
32
|
+
// Override the not found handler
|
|
33
|
+
setNotFoundHandler = (handler: Handler<Context>) => {
|
|
34
|
+
this.notFoundHandler = handler;
|
|
35
|
+
return this;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Register route: creates URLPattern, stores handler, enables chaining
|
|
39
|
+
add = (
|
|
40
|
+
method: HttpMethod,
|
|
41
|
+
pathname: string,
|
|
42
|
+
handler: Handler<Context>,
|
|
43
|
+
): Router<Context> => {
|
|
44
|
+
this.routes[method].push({
|
|
45
|
+
pattern: new URLPattern({ pathname }),
|
|
46
|
+
handler,
|
|
47
|
+
});
|
|
48
|
+
return this;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Match request: iterate routes by method, invoke first match
|
|
52
|
+
match = async (request: Request, context: Context): Promise<Response> => {
|
|
53
|
+
const method = request.method.toUpperCase() as HttpMethod;
|
|
54
|
+
const possibleRoutes = this.routes[method] || [];
|
|
55
|
+
const body = await request.text();
|
|
56
|
+
const { searchParams, pathname } = new URL(request.url);
|
|
57
|
+
|
|
58
|
+
for (const route of possibleRoutes) {
|
|
59
|
+
const result = route.pattern.exec({ pathname });
|
|
60
|
+
if (result !== null) {
|
|
61
|
+
return route.handler({
|
|
62
|
+
body,
|
|
63
|
+
urlParams: result.pathname.groups,
|
|
64
|
+
searchParams,
|
|
65
|
+
...context,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return this.notFoundHandler({
|
|
71
|
+
body,
|
|
72
|
+
urlParams: {},
|
|
73
|
+
searchParams,
|
|
74
|
+
...context,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Helper method for GET requests
|
|
79
|
+
get = (pathname: string, handler: Handler<Context>): Router<Context> => {
|
|
80
|
+
return this.add("GET", pathname, handler);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Helper method for POST requests
|
|
84
|
+
post = (pathname: string, handler: Handler<Context>): Router<Context> => {
|
|
85
|
+
return this.add("POST", pathname, handler);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Helper method for PUT requests
|
|
89
|
+
put = (pathname: string, handler: Handler<Context>): Router<Context> => {
|
|
90
|
+
return this.add("PUT", pathname, handler);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Helper method for PATCH requests
|
|
94
|
+
patch = (pathname: string, handler: Handler<Context>): Router<Context> => {
|
|
95
|
+
return this.add("PATCH", pathname, handler);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Helper method for DELETE requests
|
|
99
|
+
delete = (pathname: string, handler: Handler<Context>): Router<Context> => {
|
|
100
|
+
return this.add("DELETE", pathname, handler);
|
|
101
|
+
};
|
|
102
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2020",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"outDir": "./dist",
|
|
10
|
+
"rootDir": "./src",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"sourceMap": true,
|
|
17
|
+
"types": ["node"]
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"src"
|
|
21
|
+
],
|
|
22
|
+
"exclude": [
|
|
23
|
+
"node_modules",
|
|
24
|
+
"dist"
|
|
25
|
+
]
|
|
26
|
+
}
|