UNPKG

hono

Version:

Web framework built on Web Standards

183 lines (182 loc) 5.32 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 setDefaultContentType = (contentType, headers) => { return { "Content-Type": contentType, ...headers }; }; var Context = class { #rawRequest; #req; env = {}; #var; finalized = false; error; #status; #executionCtx; #res; #layout; #renderer; #notFoundHandler; #preparedHeaders; #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() { return this.#res ||= new Response(null, { headers: this.#preparedHeaders ??= new Headers() }); } set res(_res) { if (this.#res && _res) { _res = new Response(_res.body, _res); for (const [k, v] of this.#res.headers.entries()) { if (k === "content-type") { continue; } 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 (this.finalized) { this.#res = new Response(this.#res.body, this.#res); } const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers(); if (value === void 0) { headers.delete(name); } else if (options?.append) { headers.append(name, value); } else { headers.set(name, value); } }; status = (status) => { 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) { const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers(); if (typeof arg === "object" && "headers" in arg) { const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers); for (const [key, value] of argHeaders) { if (key.toLowerCase() === "set-cookie") { responseHeaders.append(key, value); } else { responseHeaders.set(key, value); } } } if (headers) { for (const [k, v] of Object.entries(headers)) { if (typeof v === "string") { responseHeaders.set(k, v); } else { responseHeaders.delete(k); for (const v2 of v) { responseHeaders.append(k, v2); } } } } const status = typeof arg === "number" ? arg : arg?.status ?? this.#status; return new Response(data, { status, headers: responseHeaders }); } newResponse = (...args) => this.#newResponse(...args); body = (data, arg, headers) => this.#newResponse(data, arg, headers); text = (text, arg, headers) => { return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse( text, arg, setDefaultContentType(TEXT_PLAIN, headers) ); }; json = (object, arg, headers) => { return this.#newResponse( JSON.stringify(object), arg, setDefaultContentType("application/json", headers) ); }; html = (html, arg, headers) => { const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType("text/html; charset=UTF-8", headers)); return typeof html === "object" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html); }; redirect = (location, status) => { const locationString = String(location); this.header( "Location", !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString) ); return this.newResponse(null, status ?? 302); }; notFound = () => { this.#notFoundHandler ??= () => new Response(); return this.#notFoundHandler(this); }; }; export { Context, TEXT_PLAIN };