2023-12-08 02:05:23 +00:00
|
|
|
import MimeTypeMap from './MimeTypeMap';
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* Represents a response object for handling HTTP responses.
|
|
|
|
|
*/
|
2023-12-08 02:05:23 +00:00
|
|
|
export default class LayResponse {
|
2024-03-28 07:07:35 +00:00
|
|
|
#routing = undefined;
|
|
|
|
|
#body = 'Not Implemented';
|
2024-04-01 22:47:45 +01:00
|
|
|
#data = undefined;
|
|
|
|
|
#statusCode = undefined;
|
2024-03-28 07:07:35 +00:00
|
|
|
#headerList = new Map();
|
|
|
|
|
#mimeType = 'text/plain';
|
2024-04-01 22:47:45 +01:00
|
|
|
#options = new Map([
|
|
|
|
|
['type', 'default'],
|
|
|
|
|
['disableFileCheck', false],
|
|
|
|
|
['layout', undefined],
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
getData() {
|
|
|
|
|
return this.#data;
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
getBody() {
|
|
|
|
|
return this.#body;
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* Creates a new instance of LayResponse.
|
|
|
|
|
* @param {Object} routing - The routing object.
|
|
|
|
|
*/
|
|
|
|
|
constructor(routing) {
|
|
|
|
|
this.#routing = routing;
|
2023-12-08 02:05:23 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
if(this.#routing.getServer().hasLayout()) {
|
|
|
|
|
this.#options.set('layout', this.#routing.getServer().getLayout());
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* Returns the headers of the response.
|
|
|
|
|
* @returns {Map} The headers map.
|
|
|
|
|
*/
|
2023-12-08 02:05:23 +00:00
|
|
|
headers() {
|
2024-03-28 07:07:35 +00:00
|
|
|
if (!this.#headerList.get('Content-Type')) {
|
|
|
|
|
this.#headerList.set('Content-Type', `${this.#mimeType}`);
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
return this.#headerList;
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2024-03-28 07:07:35 +00:00
|
|
|
async build(options = {}) {
|
2024-04-01 22:47:45 +01:00
|
|
|
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;
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
if(['text/plain', 'text/html'].includes(this.#mimeType)) {
|
|
|
|
|
await this.#handleLayout(this.#body, options);
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
if(typeof(this.#statusCode) == 'undefined') this.status(this.#body ? 200 : 500);
|
|
|
|
|
|
2024-03-28 07:07:35 +00:00
|
|
|
return new Response(this.#body, {
|
2023-12-08 02:05:23 +00:00
|
|
|
status: this.status(),
|
|
|
|
|
headers: this.headers(),
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-01-20 04:59:20 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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);
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
return this;
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
2024-04-01 22:47:45 +01:00
|
|
|
this.mimeType = MimeTypeMap.get(key);
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
return this;
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* Sends the response with the provided arguments.
|
|
|
|
|
* @returns {LayResponse} The LayResponse instance.
|
|
|
|
|
*/
|
|
|
|
|
send() {
|
|
|
|
|
this.text(...arguments);
|
|
|
|
|
return this;
|
|
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* Sends a file as the response.
|
|
|
|
|
* @returns {LayResponse} The LayResponse instance.
|
|
|
|
|
*/
|
|
|
|
|
sendFile() {
|
|
|
|
|
this.#body = arguments[0];
|
|
|
|
|
this.#options.set('type', 'file');
|
|
|
|
|
return this;
|
2024-01-20 04:59:20 +00:00
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
2024-03-28 07:07:35 +00:00
|
|
|
|
2024-04-01 22:47:45 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
2024-03-28 07:07:35 +00:00
|
|
|
}
|
2023-12-08 02:05:23 +00:00
|
|
|
}
|