edgerender
Version:
Render at the edge
147 lines • 5.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EdgeRender = void 0;
const response_1 = require("./response");
const sentry_1 = require("./sentry");
const assets_1 = require("./assets");
const request_1 = require("./request");
const csp_1 = require("./csp");
class EdgeRender {
views;
page;
log;
show_error_details;
sentry;
assets;
security_headers;
constructor(config) {
this.views = Object.entries(config.views).map(request_1.as_path_view);
this.page = config.page;
this.log = config.log || false;
this.show_error_details = config.show_error_details || false;
this.security_headers = config.security_headers || response_1.default_security_headers;
const assets_config = config.assets || {};
const AssetsClass = assets_config.asset_class || assets_1.Assets;
this.assets = new AssetsClass(assets_config, this.security_headers, this.log);
if (config.sentry_dsn) {
this.sentry = sentry_1.setupSentry(config.sentry_dsn, config.sentry_environment, config.sentry_release);
}
if (config.csp) {
csp_1.addCspHeader(this.security_headers, config.csp, this.sentry);
}
this.handler = this.handler.bind(this);
}
handler(event) {
event.respondWith(this.handle(event));
}
async handle(event) {
const { request } = event;
try {
const r = await this.route(request);
if (r instanceof Response) {
return r;
}
else {
return this.prepare_response(r);
}
}
catch (exc) {
if (exc instanceof response_1.HttpError) {
return this.on_http_error(request, exc);
}
console.error('error handling request:', request, exc);
if (this.sentry) {
this.sentry.captureException(event, exc);
}
const body = this.show_error_details
? `\nError occurred on the edge:\n\n${exc.message}\n${exc.stack}\n`
: 'Edge Server Error';
return this.prepare_response({
body,
status: 500,
mime_type: response_1.MimeTypes.plaintext,
});
}
}
async route(request) {
const url = new URL(request.url);
const { pathname } = url;
const cleaned_path = request_1.clean_path(pathname);
const is_hx_request = request.headers.get('hx-request') == 'true';
if (this.log) {
console.debug(`${request.method} ${cleaned_path}`);
}
if (this.assets && this.assets.is_static_path(pathname)) {
return await this.assets.response(request, pathname);
}
const method = request.method;
for (const view of this.views) {
let match = {};
if (typeof view.path == 'string') {
if (view.path != cleaned_path) {
continue;
}
}
else {
const m = cleaned_path.match(view.path);
if (!m) {
continue;
}
match = m.groups;
}
if (!view.allow.has(method)) {
const allow = [...view.allow].join(',');
const msg = `"${method}" Method Not Allowed (allowed: ${allow})`;
throw new response_1.HttpError(405, msg, { allow });
}
const context = {
request,
method,
url,
match,
is_hx_request,
edge_render: this,
assets: this.assets,
};
let result = await Promise.resolve(view.view(context));
if ('children' in result) {
if (this.page) {
result = await Promise.resolve(this.page(result, context));
}
return {
body: await result.render(),
mime_type: response_1.MimeTypes.html,
};
}
else if (result instanceof response_1.HttpError) {
return result.response(request);
}
else {
return result;
}
}
return await this.on_404({
request,
method,
url,
match: {},
is_hx_request,
edge_render: this,
assets: this.assets,
});
}
async on_404(context) {
throw new response_1.HttpError(404, `Page not found for "${context.url.pathname}"`);
}
async on_http_error(request, exc) {
console.warn(exc.message);
return this.prepare_response(exc.response(request));
}
prepare_response(r) {
const status = r.status || 200;
const headers = { 'Content-Type': r.mime_type, ...this.security_headers, ...(r.headers || {}) };
return new Response(r.body, { status, headers });
}
}
exports.EdgeRender = EdgeRender;
//# sourceMappingURL=handle.js.map