@trifrost/core
Version:
Blazingly fast, runtime-agnostic server framework for modern edge and node environments
174 lines (173 loc) • 5.19 kB
JavaScript
"use strict";
/// <reference types="bun-types" />
Object.defineProperty(exports, "__esModule", { value: true });
exports.BunContext = void 0;
const Context_1 = require("../../Context");
const constants_1 = require("../../types/constants");
const Request_1 = require("../../utils/BodyParser/Request");
const Http_1 = require("../../utils/Http");
const types_1 = require("../../utils/BodyParser/types");
const Stream_1 = require("../../utils/Stream");
const encoder = new TextEncoder();
class BunContext extends Context_1.Context {
/* Bun Apis */
bun;
/* Bun Request instance */
bun_req;
/* Internal Response instance */
res = null;
constructor(cfg, logger, bunApis, req) {
/* Extract path and query */
const { path, query } = (0, Http_1.extractPartsFromUrl)(req.url);
/* Hydrate headers */
const headers = {};
/* eslint-disable-next-line */
/* @ts-ignore */
req.headers.forEach((value, key) => {
headers[key.toLowerCase()] = value;
});
super(logger, cfg, {
path,
method: constants_1.HttpMethodToNormal[req.method],
headers,
query,
});
this.bun = bunApis;
this.bun_req = req;
}
/**
* Getter for the final response
*/
get response() {
return this.res;
}
/**
* Initializes the context, this happens when a route is matched and tied to this context.
*/
async init(val) {
await super.init(val, async () => (0, Request_1.parseBody)(this, this.bun_req, val.route.bodyParser || types_1.DEFAULT_BODY_PARSER_OPTIONS));
}
/**
* Get a stream for a particular path
*
* @param {string} path - Path to the file
*/
async getStream(path) {
try {
const file_obj = this.bun.file(path);
if (!file_obj) {
this.logger.warn('BunContext@getStream: File not found', { path });
return null;
}
return {
stream: file_obj.stream(),
size: file_obj.size,
};
}
catch (err) {
this.logger.error('BunContext@getStream: Failed to create stream', { msg: err.message, path });
return null;
}
}
/**
* Stream a file-like response in Bun
*
* @param {unknown} stream - Stream to respond with
* @param {number|null} size - Size of the stream
*/
stream(stream, size = null) {
/* If already locked do nothing */
if (this.isLocked)
return;
(0, Stream_1.verifyFileStream)(stream);
super.stream(stream, size);
/* Set response with stream */
this.res = new Response(stream, {
status: this.res_code,
headers: this.res_headers,
});
/* Write cookies */
this.writeCookies();
}
/**
* Abort the request
*
* @param {HttpStatusCode?} status - Status to abort with (defaults to 503)
*/
abort(status) {
if (this.isLocked)
return;
super.abort(status);
/* Set response */
this.res = new Response(null, {
status: this.res_code,
headers: this.res_headers,
});
/* Write cookies */
this.writeCookies();
}
/**
* End the request and respond to callee
*/
end() {
if (this.isLocked)
return;
super.end();
switch (this.method) {
case constants_1.HttpMethods.HEAD: {
this.res_headers['content-length'] = typeof this.res_body === 'string' ? '' + encoder.encode(this.res_body).length : '0';
this.res = new Response(null, {
status: this.res_code,
headers: this.res_headers,
});
/* Write cookies */
this.writeCookies();
break;
}
default:
/* Set response */
this.res = new Response(this.res_body, {
status: this.res_code,
headers: this.res_headers,
});
/* Write cookies */
this.writeCookies();
break;
}
}
/**
* Run jobs after the response has gone out
*/
runAfter() {
const hooks = this.afterHooks;
if (!hooks.length)
return;
queueMicrotask(() => {
for (let i = 0; i < hooks.length; i++) {
try {
hooks[i](this);
}
catch {
/* No-Op */
}
}
});
}
/**
* MARK: Protected
*/
getIP() {
return this.bun_req.socket?.remoteAddress ?? null;
}
/**
* MARK: Private
*/
writeCookies() {
if (!this.$cookies)
return;
const outgoing = this.$cookies.outgoing;
for (let i = 0; i < outgoing.length; i++)
this.res.headers.append('set-cookie', outgoing[i]);
}
}
exports.BunContext = BunContext;