UNPKG

hono

Version:

Web framework built on Web Standards

240 lines (239 loc) 6.98 kB
// src/context.ts import { HonoRequest } from "./request.js"; import { HtmlEscapedCallbackPhase, resolveCallback } from "./utils/html.js"; var TEXT_PLAIN = "text/plain; charset=UTF-8"; var setHeaders = (headers, map = {}) => { Object.entries(map).forEach(([key, value]) => headers.set(key, value)); return headers; }; var Context = class { #rawRequest; #req; env = {}; #var; finalized = false; error; #status = 200; #executionCtx; #headers; #preparedHeaders; #res; #isFresh = true; #layout; #renderer; #notFoundHandler; #matchResult; #path; constructor(req, options) { this.#rawRequest = req; if (options) { this.#executionCtx = options.executionCtx; this.env = options.env; this.#notFoundHandler = options.notFoundHandler; this.#path = options.path; this.#matchResult = options.matchResult; } } get req() { this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult); return this.#req; } get event() { if (this.#executionCtx && "respondWith" in this.#executionCtx) { return this.#executionCtx; } else { throw Error("This context has no FetchEvent"); } } get executionCtx() { if (this.#executionCtx) { return this.#executionCtx; } else { throw Error("This context has no ExecutionContext"); } } get res() { this.#isFresh = false; return this.#res ||= new Response("404 Not Found", { status: 404 }); } set res(_res) { this.#isFresh = false; if (this.#res && _res) { this.#res.headers.delete("content-type"); for (const [k, v] of this.#res.headers.entries()) { if (k === "set-cookie") { const cookies = this.#res.headers.getSetCookie(); _res.headers.delete("set-cookie"); for (const cookie of cookies) { _res.headers.append("set-cookie", cookie); } } else { _res.headers.set(k, v); } } } this.#res = _res; this.finalized = true; } render = (...args) => { this.#renderer ??= (content) => this.html(content); return this.#renderer(...args); }; setLayout = (layout) => this.#layout = layout; getLayout = () => this.#layout; setRenderer = (renderer) => { this.#renderer = renderer; }; header = (name, value, options) => { if (value === void 0) { if (this.#headers) { this.#headers.delete(name); } else if (this.#preparedHeaders) { delete this.#preparedHeaders[name.toLocaleLowerCase()]; } if (this.finalized) { this.res.headers.delete(name); } return; } if (options?.append) { if (!this.#headers) { this.#isFresh = false; this.#headers = new Headers(this.#preparedHeaders); this.#preparedHeaders = {}; } this.#headers.append(name, value); } else { if (this.#headers) { this.#headers.set(name, value); } else { this.#preparedHeaders ??= {}; this.#preparedHeaders[name.toLowerCase()] = value; } } if (this.finalized) { if (options?.append) { this.res.headers.append(name, value); } else { this.res.headers.set(name, value); } } }; status = (status) => { this.#isFresh = false; this.#status = status; }; set = (key, value) => { this.#var ??= /* @__PURE__ */ new Map(); this.#var.set(key, value); }; get = (key) => { return this.#var ? this.#var.get(key) : void 0; }; get var() { if (!this.#var) { return {}; } return Object.fromEntries(this.#var); } newResponse = (data, arg, headers) => { if (this.#isFresh && !headers && !arg && this.#status === 200) { return new Response(data, { headers: this.#preparedHeaders }); } if (arg && typeof arg !== "number") { const header = new Headers(arg.headers); if (this.#headers) { this.#headers.forEach((v, k) => { if (k === "set-cookie") { header.append(k, v); } else { header.set(k, v); } }); } const headers2 = setHeaders(header, this.#preparedHeaders); return new Response(data, { headers: headers2, status: arg.status ?? this.#status }); } const status = typeof arg === "number" ? arg : this.#status; this.#preparedHeaders ??= {}; this.#headers ??= new Headers(); setHeaders(this.#headers, this.#preparedHeaders); if (this.#res) { this.#res.headers.forEach((v, k) => { if (k === "set-cookie") { this.#headers?.append(k, v); } else { this.#headers?.set(k, v); } }); setHeaders(this.#headers, this.#preparedHeaders); } headers ??= {}; for (const [k, v] of Object.entries(headers)) { if (typeof v === "string") { this.#headers.set(k, v); } else { this.#headers.delete(k); for (const v2 of v) { this.#headers.append(k, v2); } } } return new Response(data, { status, headers: this.#headers }); }; body = (data, arg, headers) => { return typeof arg === "number" ? this.newResponse(data, arg, headers) : this.newResponse(data, arg); }; text = (text, arg, headers) => { if (!this.#preparedHeaders) { if (this.#isFresh && !headers && !arg) { return new Response(text); } this.#preparedHeaders = {}; } this.#preparedHeaders["content-type"] = TEXT_PLAIN; return typeof arg === "number" ? this.newResponse(text, arg, headers) : this.newResponse(text, arg); }; json = (object, arg, headers) => { const body = JSON.stringify(object); this.#preparedHeaders ??= {}; this.#preparedHeaders["content-type"] = "application/json; charset=UTF-8"; return typeof arg === "number" ? this.newResponse(body, arg, headers) : this.newResponse(body, arg); }; html = (html, arg, headers) => { this.#preparedHeaders ??= {}; this.#preparedHeaders["content-type"] = "text/html; charset=UTF-8"; if (typeof html === "object") { if (!(html instanceof Promise)) { html = html.toString(); } if (html instanceof Promise) { return html.then((html2) => resolveCallback(html2, HtmlEscapedCallbackPhase.Stringify, false, {})).then((html2) => { return typeof arg === "number" ? this.newResponse(html2, arg, headers) : this.newResponse(html2, arg); }); } } return typeof arg === "number" ? this.newResponse(html, arg, headers) : this.newResponse(html, arg); }; redirect = (location, status) => { this.#headers ??= new Headers(); this.#headers.set("Location", location); return this.newResponse(null, status ?? 302); }; notFound = () => { this.#notFoundHandler ??= () => new Response(); return this.#notFoundHandler(this); }; }; export { Context, TEXT_PLAIN };