Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit af0730c

Browse files
committed
Improve server context
1 parent 57b811d commit af0730c

File tree

4 files changed

+59
-31
lines changed

4 files changed

+59
-31
lines changed

framework/core/router.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ export function matchRoutes(url: URL, router: Router): RouteMatch[] {
7070
}
7171
}
7272
}
73+
// find index route if no direct match
7374
if (matches.length === 0) {
74-
// find index route
7575
for (const [p, m] of routes) {
7676
if (m.pattern.pathname.endsWith("/index")) {
7777
const ret = p.exec({ host: url.host, pathname: pathname + "/index" });

server/context.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,29 @@ import { computeHash, hmacSign, splitBy } from "../shared/util.ts";
22
import { SessionImpl } from "./session.ts";
33
import type { ConnInfo, Context, HTMLRewriterHandlers, Session, SessionOptions } from "./types.ts";
44

5-
type ContextOptions = {
6-
connInfo?: ConnInfo;
7-
session?: SessionOptions;
5+
export const NEXT = Symbol();
6+
export const CUSTOM_HTML_REWRITER = Symbol();
7+
8+
export type ContextInit = {
9+
req: Request;
10+
connInfo: ConnInfo;
11+
sessionOptions?: SessionOptions;
812
};
913

1014
/** create a context object */
11-
export function createContext(
12-
req: Request,
13-
next: () => Promise<Response> | Response,
14-
options?: ContextOptions,
15-
): Context {
15+
export function createContext(next: () => Promise<Response> | Response, init: ContextInit): Context {
1616
let cookies: Map<string, string> | null = null;
1717
let session: Session<Record<string, unknown>> | null = null;
18+
const customHtmlRewriter: [string, HTMLRewriterHandlers][] = [];
19+
const extension = {};
1820
const ctx: Context = {
19-
connInfo: options?.connInfo,
21+
connInfo: init.connInfo,
2022
params: {},
21-
headers: new Headers(),
2223
cookies: {
2324
get(name: string) {
24-
if (cookies === null) {
25+
if (!cookies) {
2526
cookies = new Map<string, string>();
26-
const cookieHeader = req.headers.get("Cookie");
27+
const cookieHeader = init.req.headers.get("Cookie");
2728
if (cookieHeader) {
2829
for (const cookie of cookieHeader.split(";")) {
2930
const [key, value] = splitBy(cookie, "=");
@@ -37,12 +38,13 @@ export function createContext(
3738
// deno-lint-ignore ban-ts-comment
3839
// @ts-ignore
3940
async getSession(): Promise<Session<Record<string, unknown>>> {
40-
if (session !== null) {
41+
if (session) {
4142
return session;
4243
}
4344

44-
const cookieName = options?.session?.cookie?.name ?? "session";
45-
const secret = options?.session?.secret ?? "-";
45+
const { sessionOptions } = init;
46+
const cookieName = sessionOptions?.cookie?.name ?? "session";
47+
const secret = sessionOptions?.secret ?? "-";
4648
let sid = ctx.cookies.get(cookieName);
4749
let skipInit = false;
4850
if (sid) {
@@ -57,23 +59,44 @@ export function createContext(
5759
skipInit = true;
5860
}
5961

60-
const sessionImpl = new SessionImpl<Record<string, unknown>>(
61-
sid,
62-
options?.session,
63-
);
62+
const sessionImpl = new SessionImpl<Record<string, unknown>>(sid, sessionOptions);
6463
session = sessionImpl;
6564
if (!skipInit) {
6665
await sessionImpl.init();
6766
}
6867
return session;
6968
},
70-
__htmlRewriterHandlers: [],
7169
htmlRewriter: {
7270
on: (selector: string, handlers: HTMLRewriterHandlers) => {
73-
(ctx.__htmlRewriterHandlers as unknown[]).push([selector, handlers]);
71+
customHtmlRewriter.push([selector, handlers]);
7472
},
7573
},
7674
next,
7775
};
78-
return ctx;
76+
return new Proxy(Object.create({}), {
77+
get(_target, prop) {
78+
if (prop === CUSTOM_HTML_REWRITER) {
79+
return customHtmlRewriter;
80+
}
81+
if (Reflect.has(ctx, prop)) {
82+
return Reflect.get(ctx, prop);
83+
}
84+
return Reflect.get(extension, prop);
85+
},
86+
set(_target, prop, value) {
87+
if (prop === NEXT) {
88+
Reflect.set(ctx, "next", value);
89+
}
90+
if (!Reflect.has(ctx, prop)) {
91+
return Reflect.set(extension, prop, value);
92+
}
93+
return false;
94+
},
95+
deleteProperty(_target, prop) {
96+
if (!Reflect.has(ctx, prop)) {
97+
return Reflect.deleteProperty(extension, prop);
98+
}
99+
return false;
100+
},
101+
});
79102
}

server/handler.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { generateErrorHtml, TransformError } from "../framework/core/error.ts";
22
import type { Router } from "../framework/core/router.ts";
33
import { isPlainObject, trimSuffix } from "../shared/util.ts";
4-
import { createContext } from "./context.ts";
4+
import { createContext, NEXT } from "./context.ts";
55
import { handleHMR } from "./dev.ts";
66
import { HTMLRewriter, path } from "./deps.ts";
77
import {
@@ -25,10 +25,10 @@ import { getContentType } from "./media_type.ts";
2525
import renderer from "./renderer.ts";
2626
import { fetchRoute, importRouteModule, initRouter } from "./router.ts";
2727
import transformer from "./transformer.ts";
28-
import type { AlephConfig, ConnInfo, ModuleLoader } from "./types.ts";
28+
import type { AlephConfig, ConnInfo, Context, ModuleLoader } from "./types.ts";
2929

3030
export function createHandler(config: AlephConfig) {
31-
const { loaders, middlewares, onError, build, router: routerConfig, session, ssr } = config;
31+
const { loaders, middlewares, onError, build, router: routerConfig, ssr } = config;
3232
const buildMode = Deno.args.includes("--build");
3333
const isDev = Deno.args.includes("--dev");
3434
const appDir = getAppDir();
@@ -72,7 +72,7 @@ export function createHandler(config: AlephConfig) {
7272
// check if the `out` directory exists
7373
const outDir = await globalIt("__ALEPH_OUT_DIR", async () => {
7474
if (!isDev && !buildMode) {
75-
const outDir = path.join(appDir, build?.outputDir ?? "./output");
75+
const outDir = path.join(appDir, build?.outputDir ?? "output");
7676
if (await existsDir(outDir)) {
7777
return outDir;
7878
}
@@ -352,11 +352,16 @@ export function createHandler(config: AlephConfig) {
352352

353353
// the deno http server handler
354354
return (req: Request, connInfo: ConnInfo): Promise<Response> | Response => {
355+
const ctx = createContext(() => Promise.resolve(new Response(null)), {
356+
req,
357+
connInfo,
358+
sessionOptions: config.session,
359+
});
355360
const next = (i: number): Promise<Response> | Response => {
356361
if (Array.isArray(middlewares) && i < middlewares.length) {
357362
const mw = middlewares[i];
358-
const ctx = createContext(req, next.bind(null, i + 1), { connInfo, session });
359363
try {
364+
Reflect.set(ctx, NEXT, next.bind(null, i + 1));
360365
return mw.fetch(req, ctx);
361366
} catch (err) {
362367
const res = onError?.(err, "middleware", req, ctx);
@@ -370,7 +375,6 @@ export function createHandler(config: AlephConfig) {
370375
});
371376
}
372377
}
373-
const ctx = createContext(req, () => Promise.resolve(new Response(null)), { connInfo, session });
374378
return handler(req, ctx);
375379
};
376380
return next(0);

server/renderer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { FetchError } from "../framework/core/error.ts";
22
import { matchRoutes, type RouteModule, type Router } from "../framework/core/router.ts";
33
import { cleanPath, isFilledString, isPlainObject, utf8Enc } from "../shared/util.ts";
4+
import { CUSTOM_HTML_REWRITER } from "./context.ts";
45
import { HTMLRewriter, path } from "./deps.ts";
56
import depGraph from "./graph.ts";
67
import { getAlephConfig, getAppDir, getDeploymentId, getFiles, regJsxFile, toLocalPath } from "./helpers.ts";
@@ -18,13 +19,13 @@ export type RenderOptions = {
1819
export default {
1920
async fetch(req: Request, ctx: Record<string, unknown>, options: RenderOptions): Promise<Response> {
2021
const { indexHtml, router, ssr, isDev } = options;
21-
const headers = new Headers(ctx.headers as Headers);
22+
const headers = new Headers();
2223
const isFn = typeof ssr === "function";
2324
const CSP = isFn ? undefined : ssr.CSP;
2425
const render = isFn ? ssr : ssr.render;
2526
const [url, modules, deferedData] = await initSSR(req, ctx, router);
2627
const headCollection: string[] = [];
27-
const customHTMLRewriter = ctx.__htmlRewriterHandlers as [string, HTMLRewriterHandlers][];
28+
const customHTMLRewriter = Reflect.get(ctx, CUSTOM_HTML_REWRITER) as [string, HTMLRewriterHandlers][];
2829

2930
let status = 200;
3031
let suspenseMarker: SuspenseMarker | undefined;

0 commit comments

Comments
 (0)