This repository has been archived on 2025-03-10. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
layc/src/IO/LayResponse.js

245 lines
6.2 KiB
JavaScript

import MimeTypeMap from './MimeTypeMap';
/**
* Represents a response object for handling HTTP responses.
*/
export default class LayResponse {
#routing = undefined;
#body = 'Not Implemented';
#data = undefined;
#statusCode = undefined;
#headerList = new Map();
#mimeType = 'text/plain';
#options = new Map([
['type', 'default'],
['disableFileCheck', false],
['layout', undefined],
]);
getData() {
return this.#data;
}
getBody() {
return this.#body;
}
/**
* Creates a new instance of LayResponse.
* @param {Object} routing - The routing object.
*/
constructor(routing) {
this.#routing = routing;
if(this.#routing.getServer().hasLayout()) {
this.#options.set('layout', this.#routing.getServer().getLayout());
}
}
/**
* Returns the headers of the response.
* @returns {Map} The headers map.
*/
headers() {
if (!this.#headerList.get('Content-Type')) {
this.#headerList.set('Content-Type', `${this.#mimeType}`);
}
return this.#headerList;
}
/**
* Sets or gets the layout for the response.
* @param {string} [layout] - The layout to set. If not provided, returns the current layout.
* @returns {string|LayResponse} The current layout or the LayResponse instance.
*/
layout (layout = undefined) {
if(typeof(layout) == 'undefined') return this.#options.get('layout');
this.#options.set('layout', layout);
return this;
}
/**
* Builds the response based on the specified options.
* @param {Object} [options={}] - The options for building the response.
* @returns {Promise<Response>} A promise that resolves to the built response.
*/
async build(options = {}) {
switch(this.#options.get('type')) {
case 'redirect':
return Response.redirect(this.#body, this.status());
case 'file':
const file = Bun.file(this.#body);
if(await file.exists()) {
return new Response(file);
}
return undefined;
case 'render':
const loader = await this.#routing.getServer().load(this.#body);
if(!loader) {
return new Response('Not Found', { status: 404 });
}
console.log(loader);
const { data, content } = await loader.parse(this.#data);
this.#data = data || this.#data;
this.#body = content;
this.type('text');
break;
default:
case 'default':
break;
}
if(['text/plain', 'text/html'].includes(this.#mimeType)) {
await this.#handleLayout(this.#body, options);
}
if(typeof(this.#statusCode) == 'undefined') this.status(this.#body ? 200 : 500);
return new Response(this.#body, {
status: this.status(),
headers: this.headers(),
});
}
/**
* Handles the layout rendering for the response.
* @param {string} content - The content to render within the layout.
* @param {Object} [data={}] - The data to pass to the layout.
* @returns {Promise<void>}
* @private
*/
async #handleLayout(content, data = {}) {
if(!this.#options.get('layout')) return
const layout = await this.#routing.getServer().load(this.#options.get('layout'));
data = {
...(this.#data || {}),
...data
};
if(layout) {
const rendered = await layout.parse({ content: String(content), ...(data ?? {}) });
this.#body = rendered.content ?? String(content);
this.#mimeType = layout.mimeType;
}
}
/**
* Sets a header for the response.
* @param {string} key - The header key.
* @param {string} value - The header value.
* @returns {LayResponse} The LayResponse instance.
*/
header(key, value) {
this.#headerList.set(key, value);
return this;
}
/**
* Sets the MIME type of the response based on the provided key.
* @param {string} key - The key to determine the MIME type.
* @returns {LayResponse} The LayResponse instance.
*/
type(key) {
if (!MimeTypeMap.has(key)) {
if(this.#mimeType != 'text/plain') this.#mimeType = MimeTypeMap.get('text');
return this;
}
this.mimeType = MimeTypeMap.get(key);
return this;
}
/**
* Sets or gets the status code of the response.
* @param {number} [value] - The status code to set. If not provided, returns the current status code.
* @returns {number|LayResponse} The current status code or the LayResponse instance.
*/
status(value = undefined) {
if (value == undefined) return this.#statusCode;
if (isNaN(value)) throw new TypeError('status has to be a number');
this.#statusCode = value || 502;
return this.#statusCode;
}
/**
* Sends the response with the provided arguments.
* @returns {LayResponse} The LayResponse instance.
*/
send() {
this.text(...arguments);
return this;
}
/**
* Sends a file as the response.
* @returns {LayResponse} The LayResponse instance.
*/
sendFile() {
this.#body = arguments[0];
this.#options.set('type', 'file');
return this;
}
/**
* Renders a template with the provided path and data.
* @returns {LayResponse} The LayResponse instance.
*/
render() {
const [ path, data ] = arguments;
this.#body = path;
this.#data = data;
this.#options.set('type', 'render');
return this;
}
/**
* Sets the response body as plain text.
* @returns {LayResponse} The LayResponse instance.
*/
text() {
this.#body = arguments[0] ?? '';
this.#data = arguments[1] ?? {};
this.#options.set('type', 'default');
return this;
}
/**
* Redirects the response to the specified value with the provided status code.
* @param {string} [value=null] - The value to redirect to. If not provided, returns the current redirect value.
* @param {number} [status=302] - The status code for the redirect.
* @returns {number|null} The redirect status code or null if no redirect is set.
*/
redirect(value = null, status = 302) {
if (value === null) {
if(!this.#options.get('type') == 'redirect') return null;
return this.#body
};
this.#body = value;
this.#options.set('type', 'redirect');
return this.status(status);
}
/**
* Sets the response body as JSON.
* @returns {LayResponse} The LayResponse instance.
*/
json() {
const [ data, options ] = arguments;
let {
replacer = null,
space = 0
} = (options || {});
if (!Array.isArray(replacer) && typeof replacer != 'function') replacer = null;
if (isNaN(space) || Math.floor(space) < 0) space = 0;
this.type('json');
this.#body = JSON.stringify(data, replacer, space);
this.#data = options;
return this;
}
}