183 lines
5.2 KiB
JavaScript
183 lines
5.2 KiB
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 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
|
|
};
|