Skip to content

Commit c98a78c

Browse files
committed
v1.2.0
2 parents d608a55 + ca58fb0 commit c98a78c

File tree

11 files changed

+211
-39
lines changed

11 files changed

+211
-39
lines changed

deno.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

dev.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./server/dev.ts"

http/error.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export class HTTPError {
4646
error: Error,
4747
statusCode: number | StatusCode = StatusCode.INTERNAL_ERR,
4848
): HTTPError {
49-
console.error(color.red`[HTTP ${statusCode}] ` + error.message);
49+
// console.error(color.red`[HTTP ${statusCode}] ` + error.message);
50+
console.error(color.red`[HTTP ${statusCode}]`,error);
5051

5152
return new HTTPError(statusCode, error.message)
5253
.withName(error.name)

http/request.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ type Accumulator = {
77
};
88

99
export class HTTPRequest {
10-
readonly raw: Request;
10+
// readonly raw: Request;
1111
readonly method: HTTPMethodStr;
1212
readonly headers: Headers;
13+
readonly reqHeaders: Headers;
1314
readonly cookies: Cookies;
1415

1516
readonly url: URL;
@@ -26,13 +27,13 @@ export class HTTPRequest {
2627

2728
private _prepared = false;
2829

29-
constructor(readonly event: Deno.RequestEvent) {
30-
this.raw = event.request;
31-
this.method = this.raw.method as HTTPMethodStr;
30+
constructor(readonly raw: Request) {
31+
this.method = raw.method as HTTPMethodStr;
3232
this.headers = new Headers();
33-
this.cookies = new Cookies(this.raw.headers, this.headers);
33+
this.reqHeaders = raw.headers;
34+
this.cookies = new Cookies(raw.headers, this.headers);
3435

35-
this.url = new URL(this.raw.url);
36+
this.url = new URL(raw.url);
3637
this.pathname = this.url.pathname;
3738

3839
// By Parts

http/response.ts

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,60 @@
1-
// import { PrimitiveObject, StatusCode } from "../types.ts";
2-
// import { DynamicHtmlTree } from "../dynamic-html/DynamicHtmlTree.ts";
1+
import { HTTPRequest } from "./request.ts";
2+
import { HTTPError } from "./error.ts";
33

44
export class HTTPResponse {
5-
// static viewsTree: DynamicHtmlTree;
65
static viewsPath: string;
76

8-
// constructor(readonly event: Deno.RequestEvent) {}
9-
107
static async view(
118
path: string,
129
data?: unknown,
1310
init?: ResponseInit,
1411
): Promise<Response> {
15-
const view = await import(this.viewsPath + '/' + path + '.ts');
12+
// const view = await import(this.viewsPath + "/" + path + ".ts");
13+
14+
return new Response(
15+
JSON.stringify({
16+
view_path: path,
17+
data: data,
18+
}),
19+
{
20+
status: 200,
21+
...init,
22+
headers: {
23+
"Content-Type": "text/html",
24+
...init?.headers,
25+
},
26+
},
27+
);
28+
}
29+
30+
static toResponse(
31+
req: HTTPRequest,
32+
response: Response | HTTPError | Error | void,
33+
): Response {
34+
if (response instanceof Error) {
35+
response = HTTPError.fromError(response);
36+
}
37+
38+
if (response instanceof HTTPError) {
39+
response = response.toResponse();
40+
}
41+
42+
if (response instanceof Response) {
43+
console.log(req.headers, response.headers);
44+
console.log(Object.fromEntries([
45+
...req.headers.entries(),
46+
...response.headers.entries(),
47+
]));
48+
return new Response(response.body, {
49+
status: response.status,
50+
statusText: response.statusText,
51+
headers: Object.fromEntries([
52+
...req.headers.entries(),
53+
...response.headers.entries(),
54+
]),
55+
});
56+
}
1657

17-
return new Response(view.default(data), {
18-
status: 200,
19-
...init,
20-
headers: {
21-
"Content-Type": "text/html",
22-
...init?.headers
23-
}
24-
});
25-
// if (!this.viewsTree) {
26-
// throw new Error(
27-
// "You're trying to use views without its config. Please set 'viewsPath' config.",
28-
// );
29-
// }
30-
//
31-
// const viewNode = await this.viewsTree.getNode(path);
32-
//
33-
// return viewNode.toResponse(data, init);
58+
throw new Error("Unreachable code");
3459
}
3560
}

server/BaseServer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
// import type { Promisable } from "../common.ts";
21
import { HTTPError } from "../http/error.ts";
32
import version from "../version.ts";
43
import { Globals } from "../global.ts";
54
import color from "../colors.ts";
65
import { Promisable } from "../types.ts";
76

8-
import { log } from "../log.ts";
7+
import { HTTPRequest } from "../http/request.ts";
98

109
export type BaseServerOptions = Parameters<typeof Deno.listen>[0] & {
1110
verbose?: boolean;
@@ -48,8 +47,8 @@ export abstract class BaseServer {
4847

4948
async start() {
5049
for await (const conn of this.server) {
51-
log("New Connection", "HTTP");
52-
this.handleConnection(conn).catch((_) => conn.close());
50+
// log("New Connection", "HTTP");
51+
this.handleConnection(conn).finally(() => conn.close()).catch((_) => {});
5352
}
5453
}
5554

@@ -63,7 +62,8 @@ export abstract class BaseServer {
6362
async handleConnection(conn: Deno.Conn): Promise<void> {
6463
for await (const request of Deno.serveHttp(conn)) {
6564
try {
66-
const handled = this.handleRequest(request, conn);
65+
const req = new HTTPRequest(request.request);
66+
const handled = this.handleRequest(req, conn);
6767

6868
// Handle Async
6969
if (typeof handled === "object" && "catch" in handled) {
@@ -85,7 +85,7 @@ export abstract class BaseServer {
8585
}
8686

8787
abstract handleRequest(
88-
request: Deno.RequestEvent,
88+
request: HTTPRequest,
8989
conn: Deno.Conn,
9090
): Promisable<Response>;
9191
}

server/dev.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { relative as relativePath } from "https://deno.land/std@0.190.0/path/mod.ts";
2+
import { Globals } from "../global.ts";
3+
import { stripPrefix } from "../utils.ts";
4+
import { log, log_error } from "../log.ts";
5+
import { HTTPRequest, HTTPResponse } from "../runtime.ts";
6+
import type { CompileOptions, EntryController } from "../types.ts";
7+
import { BaseServer, BaseServerOptions } from "./BaseServer.ts";
8+
import { join } from "https://deno.land/std@0.190.0/path/posix.ts";
9+
10+
type ControllerResolved = {
11+
middlewares: Array<string>;
12+
fallbacks: Array<string>;
13+
controller: string;
14+
};
15+
type ManifestResolver = (req: HTTPRequest) => ControllerResolved | null;
16+
17+
export class DevServer extends BaseServer {
18+
lastId = 0;
19+
waitingRequests = new Map<number, Deno.RequestEvent>();
20+
21+
manifestResolver: ManifestResolver | null = null;
22+
cache = new Map<string, EntryController>();
23+
24+
constructor(
25+
options: BaseServerOptions,
26+
compileOptions: CompileOptions,
27+
) {
28+
super(options);
29+
if (compileOptions.viewsPath) {
30+
HTTPResponse.viewsPath = join(Globals.cwd, "/.densky/views");
31+
}
32+
}
33+
34+
importWithoutCache(url: string): Promise<unknown> {
35+
log("./" + stripPrefix(url, Globals.cwd), "CACHE", "import");
36+
return import(
37+
"file://" + url + "?k=" +
38+
(Math.random() * 16000 | 0).toString(32)
39+
);
40+
}
41+
42+
async importController(url: string): Promise<EntryController | null> {
43+
if (this.cache.has(url)) {
44+
return this.cache.get(url)!;
45+
}
46+
47+
try {
48+
const controller = await this.importWithoutCache(url) as EntryController;
49+
this.cache.set(url, controller);
50+
51+
return controller;
52+
} catch (e) {
53+
log_error(e);
54+
return null;
55+
}
56+
}
57+
58+
async handleRequest(req: HTTPRequest) {
59+
if (!this.manifestResolver) {
60+
type ManifestResolverModule = { default: ManifestResolver };
61+
62+
this.manifestResolver = await this.importWithoutCache(
63+
join(Globals.cwd, ".densky/manifest.ts"),
64+
).then((m) => (m as ManifestResolverModule).default);
65+
}
66+
67+
if (req.pathname === "/$/dev") {
68+
this.manifestResolver = null;
69+
for (const entry of await req.raw.json()) {
70+
this.cache.delete(entry[1]);
71+
log(
72+
relativePath(Globals.cwd, entry[1]),
73+
"WATCHER",
74+
entry[0].toUpperCase(),
75+
);
76+
}
77+
78+
return (new Response("Updated!", { status: 200 }));
79+
}
80+
81+
const resolvedController = this.manifestResolver!(req);
82+
if (resolvedController == null) {
83+
return new Response("Not Found", { status: 404 });
84+
}
85+
86+
for (const middleware of resolvedController.middlewares) {
87+
const out = await this.handleController(middleware, req);
88+
if (out) return out;
89+
}
90+
91+
for (const fallback of resolvedController.fallbacks) {
92+
const out = await this.handleController(fallback, req);
93+
if (out) return out;
94+
}
95+
96+
const out = await this.handleController(resolvedController.controller, req);
97+
if (out) return out;
98+
99+
return new Response("Not found", { status: 404 });
100+
}
101+
102+
async handleController(
103+
path: string,
104+
req: HTTPRequest,
105+
): Promise<Response | null> {
106+
const controller = await this.importController(path);
107+
if (!controller) {
108+
return new Response("Controller doesn't exist: " + path, {
109+
status: 500,
110+
});
111+
}
112+
113+
const entry = controller[req.method as keyof EntryController] ??
114+
controller.default;
115+
if (!entry) return new Response("Method not implemented", { status: 402 });
116+
117+
await req.prepare();
118+
const out = await entry(req);
119+
if (out == null) return null;
120+
121+
return HTTPResponse.toResponse(req, out);
122+
}
123+
}

server/server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { HTTPRequest } from "../mod.ts";
12
import { Promisable } from "../types.ts";
23
import { BaseServer, BaseServerOptions } from "./BaseServer.ts";
34

45
type RequestHandler = (
5-
request: Deno.RequestEvent,
6+
request: HTTPRequest,
67
conn: Deno.Conn,
78
) => Promisable<Response>;
89

@@ -14,7 +15,7 @@ export class Server extends BaseServer {
1415
super(options);
1516
}
1617

17-
async handleRequest(req: Deno.RequestEvent, conn: Deno.Conn) {
18+
async handleRequest(req: HTTPRequest, conn: Deno.Conn) {
1819
return await this.requestHandler(req, conn);
1920
}
2021
}

types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { HTTPRequest } from "densky/mod.ts";
12
export type { BaseServerOptions } from "./server/BaseServer.ts";
23

34
export type CompileOptions = {
@@ -147,3 +148,12 @@ export type HTTPMethodStr =
147148
| "PATH"
148149
| "OPTIONS"
149150
| "ANY";
151+
152+
export type Entry = (req: HTTPRequest) => Promisable<Response | undefined>;
153+
export interface EntryController {
154+
default?: Entry;
155+
GET?: Entry;
156+
POST?: Entry;
157+
DELETE?: Entry;
158+
PATCH?: Entry;
159+
}

utils.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function stripPrefix(target: string, prefix: string): string {
2+
if (target.startsWith(prefix)) {
3+
return target.slice(prefix.length);
4+
} else {
5+
return target;
6+
}
7+
}
8+
9+

0 commit comments

Comments
 (0)