221 lines
6.3 KiB
JavaScript
221 lines
6.3 KiB
JavaScript
// src/context.ts
|
|
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 {
|
|
req;
|
|
env = {};
|
|
_var = {};
|
|
finalized = false;
|
|
error = void 0;
|
|
#status = 200;
|
|
#executionCtx;
|
|
#headers = void 0;
|
|
#preparedHeaders = void 0;
|
|
#res;
|
|
#isFresh = true;
|
|
layout = void 0;
|
|
renderer = (content) => this.html(content);
|
|
notFoundHandler = () => new Response();
|
|
constructor(req, options) {
|
|
this.req = req;
|
|
if (options) {
|
|
this.#executionCtx = options.executionCtx;
|
|
this.env = options.env;
|
|
if (options.notFoundHandler) {
|
|
this.notFoundHandler = options.notFoundHandler;
|
|
}
|
|
}
|
|
}
|
|
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(...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 ??= {};
|
|
this._var[key] = value;
|
|
};
|
|
get = (key) => {
|
|
return this._var ? this._var[key] : void 0;
|
|
};
|
|
get var() {
|
|
return { ...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) => {
|
|
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 = 302) => {
|
|
this.#headers ??= new Headers();
|
|
this.#headers.set("Location", location);
|
|
return this.newResponse(null, status);
|
|
};
|
|
notFound = () => {
|
|
return this.notFoundHandler(this);
|
|
};
|
|
};
|
|
export {
|
|
Context,
|
|
TEXT_PLAIN
|
|
};
|