hono
Version:
Web framework built on Web Standards
240 lines (239 loc) • 6.98 kB
JavaScript
// 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
};