UNPKG

@trifrost/core

Version:

Blazingly fast, runtime-agnostic server framework for modern edge and node environments

174 lines (173 loc) 5.19 kB
"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;