enh(Response/Request) Docs; General Improvements; Base Layout
This commit is contained in:
@@ -1,22 +1,49 @@
|
|||||||
import RequestParser from '../Utils/RequestParser';
|
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 {
|
export default class LayRequest extends Request {
|
||||||
body = undefined;
|
body = undefined;
|
||||||
nativeRequest = null;
|
#nativeRequest = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of LayRequest.
|
||||||
|
* @param {*} req - The native request object.
|
||||||
|
*/
|
||||||
constructor(req) {
|
constructor(req) {
|
||||||
super(req);
|
super(req);
|
||||||
|
|
||||||
this.params = new Map();
|
this.params = new Map();
|
||||||
this.query = 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() {
|
async parseBody() {
|
||||||
if(typeof this.body != 'undefined') return this.body;
|
if (typeof this.body !== 'undefined') return this.body;
|
||||||
this.body = await RequestParser(this.nativeReq);
|
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';
|
import MimeTypeMap from './MimeTypeMap';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a response object for handling HTTP responses.
|
||||||
|
*/
|
||||||
export default class LayResponse {
|
export default class LayResponse {
|
||||||
#routing = undefined;
|
#routing = undefined;
|
||||||
#body = 'Not Implemented';
|
#body = 'Not Implemented';
|
||||||
#file = undefined;
|
#data = undefined;
|
||||||
#redirectUrl = undefined;
|
#statusCode = undefined;
|
||||||
#statusCode = 502;
|
|
||||||
#headerList = new Map();
|
#headerList = new Map();
|
||||||
#mimeType = 'text/plain';
|
#mimeType = 'text/plain';
|
||||||
#options = {
|
#options = new Map([
|
||||||
disableFileCheck: false,
|
['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) {
|
constructor(routing) {
|
||||||
this.#routing = routing;
|
this.#routing = routing;
|
||||||
}
|
|
||||||
|
|
||||||
type(key) {
|
if(this.#routing.getServer().hasLayout()) {
|
||||||
if (!MimeTypeMap.has(key)) {
|
this.#options.set('layout', this.#routing.getServer().getLayout());
|
||||||
if(this.#mimeType != 'text/plain') this.#mimeType = MimeTypeMap.get('text');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
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() {
|
headers() {
|
||||||
if (!this.#headerList.get('Content-Type')) {
|
if (!this.#headerList.get('Content-Type')) {
|
||||||
this.#headerList.set('Content-Type', `${this.#mimeType}`);
|
this.#headerList.set('Content-Type', `${this.#mimeType}`);
|
||||||
@@ -45,85 +47,198 @@ export default class LayResponse {
|
|||||||
return this.#headerList;
|
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 = {}) {
|
async build(options = {}) {
|
||||||
if (this.#redirectUrl) {
|
switch(this.#options.get('type')) {
|
||||||
return Response.redirect(this.#redirectUrl, this.status());
|
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', 'text/html'].includes(this.#mimeType)) {
|
||||||
|
await this.#handleLayout(this.#body, options);
|
||||||
if(['text/plain'].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, {
|
return new Response(this.#body, {
|
||||||
status: this.status(),
|
status: this.status(),
|
||||||
headers: this.headers(),
|
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;
|
const layout = await this.#routing.getServer().load(this.#options.get('layout'));
|
||||||
if (this.#options.disableFileCheck) return;
|
data = {
|
||||||
|
...(this.#data || {}),
|
||||||
try {
|
...data
|
||||||
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);
|
|
||||||
if(layout) {
|
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;
|
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