enh(Response/Request) Docs; General Improvements; Base Layout
This commit is contained in:
@@ -1,22 +1,49 @@
|
||||
import RequestParser from '../Utils/RequestParser';
|
||||
|
||||
/**
|
||||
* Represents a custom request class that extends the built-in Request class.
|
||||
* @extends Request
|
||||
*/
|
||||
export default class LayRequest extends Request {
|
||||
body = undefined;
|
||||
nativeRequest = null;
|
||||
#nativeRequest = undefined;
|
||||
|
||||
/**
|
||||
* Creates an instance of LayRequest.
|
||||
* @param {*} req - The native request object.
|
||||
*/
|
||||
constructor(req) {
|
||||
super(req);
|
||||
|
||||
this.params = new Map();
|
||||
this.query = new Map();
|
||||
this.nativeReq = req;
|
||||
this.#nativeRequest = req;
|
||||
}
|
||||
|
||||
header(key) {
|
||||
this.headers?.get(key);
|
||||
/**
|
||||
* Retrieves the value of a specific header from the request.
|
||||
* @param {string} key - The header key.
|
||||
* @returns {string|undefined} The value of the header, or undefined if not found.
|
||||
*/
|
||||
header(key, value = undefined) {
|
||||
if(typeof(value) != 'undefined') {
|
||||
if(!this.headers) this.headers = new Headers();
|
||||
this.headers.set(key, value);
|
||||
}
|
||||
return this.headers.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the request body asynchronously.
|
||||
* @returns {Promise<*>} A promise that resolves to the parsed request body.
|
||||
*/
|
||||
async parseBody() {
|
||||
if(typeof this.body != 'undefined') return this.body;
|
||||
this.body = await RequestParser(this.nativeReq);
|
||||
if (typeof this.body !== 'undefined') return this.body;
|
||||
try {
|
||||
this.body = await RequestParser(this);
|
||||
return this.body;
|
||||
} catch (error) {
|
||||
console.error('Error parsing request body:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,45 @@
|
||||
import MimeTypeMap from './MimeTypeMap';
|
||||
|
||||
/**
|
||||
* Represents a response object for handling HTTP responses.
|
||||
*/
|
||||
export default class LayResponse {
|
||||
#routing = undefined;
|
||||
#body = 'Not Implemented';
|
||||
#file = undefined;
|
||||
#redirectUrl = undefined;
|
||||
#statusCode = 502;
|
||||
#data = undefined;
|
||||
#statusCode = undefined;
|
||||
#headerList = new Map();
|
||||
#mimeType = 'text/plain';
|
||||
#options = {
|
||||
disableFileCheck: false,
|
||||
};
|
||||
#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;
|
||||
}
|
||||
|
||||
type(key) {
|
||||
if (!MimeTypeMap.has(key)) {
|
||||
if(this.#mimeType != 'text/plain') this.#mimeType = MimeTypeMap.get('text');
|
||||
return;
|
||||
if(this.#routing.getServer().hasLayout()) {
|
||||
this.#options.set('layout', this.#routing.getServer().getLayout());
|
||||
}
|
||||
this.mimeType = MimeTypeMap.get(key);
|
||||
}
|
||||
|
||||
status(value = null) {
|
||||
if (isNaN(value)) throw new TypeError('status has to be a number');
|
||||
this.#statusCode = value || 502;
|
||||
}
|
||||
|
||||
redirect(value = null, status = 302) {
|
||||
if (value === null) {
|
||||
return this.#redirectUrl;
|
||||
}
|
||||
|
||||
this.#redirectUrl = value;
|
||||
this.status(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}`);
|
||||
@@ -45,85 +47,198 @@ export default class LayResponse {
|
||||
return this.#headerList;
|
||||
}
|
||||
|
||||
header(key, value) {
|
||||
this.#headerList.set(key, value);
|
||||
/**
|
||||
* 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 = {}) {
|
||||
if (this.#redirectUrl) {
|
||||
return Response.redirect(this.#redirectUrl, this.status());
|
||||
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(this.#file) return new Response(this.#file);
|
||||
|
||||
if(['text/plain'].includes(this.#mimeType)) {
|
||||
await this.handleLayout(this.#body, options);
|
||||
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(),
|
||||
});
|
||||
}
|
||||
|
||||
async send() {
|
||||
await this.text(...arguments);
|
||||
/**
|
||||
* 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
|
||||
|
||||
if (typeof(arguments[0]) != 'string' || !arguments[0]) return;
|
||||
if (this.#options.disableFileCheck) return;
|
||||
|
||||
try {
|
||||
const temp = Bun.file(arguments[0]);
|
||||
|
||||
if (await temp.exists()) {
|
||||
this.#file = temp;
|
||||
}
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
async text() {
|
||||
let [value, ...rest] = arguments;
|
||||
if (typeof value != 'string') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
this.type('text');
|
||||
await this.handleLayout(value, ...rest);
|
||||
}
|
||||
|
||||
json(data, options = {}) {
|
||||
let { replacer, space } = 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);
|
||||
}
|
||||
|
||||
async render(path, data = {}) {
|
||||
const loader = await this.#routing.getServer().load(path);
|
||||
const content = await loader.parse(data);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
async handleLayout(content, options = {}) {
|
||||
this.#body = String(content);
|
||||
if(!options.hasOwnProperty('layout') || typeof options.layout !== 'string') {
|
||||
if(this.#routing.getServer().hasLayout()) {
|
||||
options.layout = this.#routing.getServer().getLayout();
|
||||
}
|
||||
}
|
||||
|
||||
if(!options.layout) return
|
||||
|
||||
const layout = await this.#routing.getServer().load(options.layout);
|
||||
const layout = await this.#routing.getServer().load(this.#options.get('layout'));
|
||||
data = {
|
||||
...(this.#data || {}),
|
||||
...data
|
||||
};
|
||||
if(layout) {
|
||||
this.#body = await layout.parse({ ...(options?.data || {}), content: String(content) }, options);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user