-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapplication.ts
141 lines (123 loc) · 3.94 KB
/
application.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { serve, ServeInit } from "https://deno.land/std@0.119.0/http/server.ts";
import { Handler } from "./handler.ts";
import { CatchFunc, HandleFunc, Matcher } from "./types.ts";
import { Request as RoarterRequest } from "./request.ts";
export class Application {
private handlers: Handler[] = [];
private catchFunc: CatchFunc = () => {
throw new Error("Not Implemented");
};
match(custom: Matcher): Handler {
const handler = new Handler().match(custom);
this.handlers.push(handler);
return handler;
}
get get(): Handler {
const handler = new Handler().get;
this.handlers.push(handler);
return handler;
}
get post(): Handler {
const handler = new Handler().post;
this.handlers.push(handler);
return handler;
}
get put(): Handler {
const handler = new Handler().put;
this.handlers.push(handler);
return handler;
}
get delete(): Handler {
const handler = new Handler().delete;
this.handlers.push(handler);
return handler;
}
get patch(): Handler {
const handler = new Handler().patch;
this.handlers.push(handler);
return handler;
}
path(path: string): Handler {
const handler = new Handler().path(path);
this.handlers.push(handler);
return handler;
}
queries(queries: string[]): Handler {
const handler = new Handler().queries(queries);
this.handlers.push(handler);
return handler;
}
handle(handler: HandleFunc | Application): Handler {
const h = new Handler().handle(handler);
this.handlers.push(h);
return h;
}
catch(fn: CatchFunc): void {
this.catchFunc = fn;
}
private async runAllUntilResponse(req: RoarterRequest, runFromIndex: number) {
for (runFromIndex; runFromIndex < this.handlers.length; runFromIndex++) {
const handler = this.handlers[runFromIndex];
const response = await handler["run"](req);
if (response) {
return {
response,
handler,
index: runFromIndex,
};
}
}
}
private async runMiddlewaresIgnoreResponse(
req: RoarterRequest,
runFromIndex: number,
) {
for (runFromIndex; runFromIndex < this.handlers.length; runFromIndex++) {
const handler = this.handlers[runFromIndex];
if (handler["isMiddleware"]()) {
await handler["run"](req);
}
}
}
// Handler runs all the registered handlers and returns the first
// response it finds. After returning the first response, it runs
// the remaining handlers. The purpose of this is to support middleware
// after a response has been sent.
private async runHandlers(req: RoarterRequest): Promise<Response> {
let matchedHandler: Handler | null = null;
let matchedIndex: number = 0;
try {
const result = await this.runAllUntilResponse(req, matchedIndex);
if (!result) {
// If after running all the handlers there is no response, throw the error
throw new Error(
`No Response was sent for ${req.method} ${req.url}`,
);
}
const { response, handler, index } = result;
matchedHandler = handler;
matchedIndex = index;
return response;
} catch (e) {
// If an error occurs, we wan't to skip all handlers and run the catchFunc
return this.catchFunc(req, e);
} finally {
// If the handler that returned a Response is a Route, run all the remaining middleware
if (matchedHandler && matchedHandler["isRoute"]()) {
matchedIndex++;
// We run the remaining middleware asynchronously in order for the return <Response> to go through
this.runMiddlewaresIgnoreResponse(req, matchedIndex).catch((e) =>
this.catchFunc(req, e)
);
}
}
}
private async handler(domReq: Request): Promise<Response> {
const req = new RoarterRequest(domReq);
return this.runHandlers(req);
}
async serve(options?: ServeInit) {
this.handler = this.handler.bind(this);
await serve(this.handler, options);
}
}