chore: remove swap files

This commit is contained in:
gh0sTedBuddy
2024-04-01 22:19:31 +01:00
parent 788957ed56
commit ed766c9951
5 changed files with 0 additions and 629 deletions

View File

@@ -1,308 +0,0 @@
import { readdir } from 'fs/promises';
import { resolve as resolvePath } from 'path';
import LayRouting from './Routing/LayRouting';
import HtmlEngine from './Views/Html';
import DefaultLoader from './Views/DefaultLoader';
import BaseRouter from './Routing/BaseRouter';
import Logger from './Utils/Logger';
/**
* Represents a LayServer instance that handles routing and server configuration.
* @todo add multi domain support
* @class
*/
export default class LayServer extends BaseRouter {
controllers = new Map();
loaders = new Map();
proc = null;
publicFiles;
routing = null;
options = new Map([
['loaders', undefined],
['publicDir', undefined],
['viewsPath', undefined],
['preferIndexFiles', false],
['discoverPublicFiles', false],
['enforceTrailingSlash', false],
['laycLoading', false],
['allowRouteOverwrite', true],
]);
/**
* Creates a new instance of LayServer.
* @param {Object} options - The options for configuring the LayServer.
*/
constructor(options = {}) {
super();
if (typeof options == 'object') {
const optionKeys = Object.getOwnPropertyNames(options);
for (const key of optionKeys) {
if (!this.options.has(key)) continue;
if (key == 'loaders') continue;
const valueType = typeof this.options.get(key);
if (
typeof options[key] !== 'undefined' &&
(typeof options[key] == valueType ||
valueType == 'undefined')
)
this.options.set(key, options[key]);
}
if (options.hasOwnProperty('loaders')) {
if (options.loaders instanceof Map) {
this.loaders = options.loaders;
}
}
}
this.routing = new LayRouting(this);
if(options.router) {
this.routing.addRouter(options.router);
}
if (options.hasOwnProperty('controllers')) {
this.registerControllers(options.controllers);
}
if (options.publicFiles) this.addPublicFileRoutes(options.publicFiles);
if (
!options.publicFiles &&
this.options.discoverPublicFiles &&
this.options.publicDir
)
this.indexPublicFiles(this.options.publicDir);
if (!this.options.viewsEngine && this.options.viewsPath) {
this.options.viewsEngine = new HtmlEngine();
}
}
/**
* Retrieves or sets the value of an option.
* @param {string} key - The key of the option.
* @param {*} [value] - The value to set for the option. If not provided, the current value of the option is returned.
* @returns {*} - The current value of the option, or undefined if the option does not exist.
*/
option(key, value = undefined) {
if (!this.options.has(key)) return undefined;
if (value !== undefined) this.option.set(key, value);
return this.options.get(key);
}
/**
* Adds public file routes to the LayServer instance.
* @param {Array<string>} filePaths - An array of file paths to be added as public file routes.
* @throws {Error} Throws an error if filePaths is not an array or undefined.
*/
addPublicFileRoutes(filePaths) {
if (!filePaths) return;
if (!Array.isArray(filePaths))
throw new Error(
'publicFiles option has to be an array or undefined'
);
if (!this.publicFiles || !Array.isArray(this.publicFiles))
this.publicFiles = [];
for (let path of filePaths) {
if (typeof path != 'string') continue;
this.publicFiles.push(path);
}
}
/**
* Indexes all public files stored as static routes.
* @todo Implement this method.
* @param {string} publicDir - The directory where the public files are stored.
*/
indexPublicFiles(publicDir) {
Logger.error('@todo implement method `indexPublicFiles`')
// this method is meant to try to iterate over all public files which are stored as static routes
}
/**
* Registers the given controllers.
*
* @param {Function|Function[]} controllers - The controllers to register.
*/
registerControllers(controllers) {
if (!Array.isArray(controllers)) controllers = [controllers];
for (let controller of controllers) {
if (typeof controller != 'function') {
Logger(`ignored ${controller} for not being a function`);
continue;
}
// if the given controller is just an anonymous function defining the routes itself
if (!controller?.prototype?.constructor?.name) {
controller(this.routing);
continue;
}
this.controllers.set(
controller?.name,
this.instantiateController(controller)
);
}
}
/**
* Instantiates a controller and sets its namespace in the route control.
* @param {Object} controller - The controller to be instantiated.
* @returns {Object} - The instantiated controller.
*/
instantiateController(controller) {
this.routing.setNamespace(controller.namespace);
const _temp = new controller(this.routing);
if (controller.namespace) this.routing.setNamespace(null);
return _temp;
}
/**
* Checks if the server is running.
* @returns {boolean} True if the server is running, false otherwise.
*/
isRunning() {
proc != null;
}
/**
* Handles an incoming request.
*
* @param {Request} req - The incoming request object.
* @returns {Promise<Response>} - A promise that resolves to a response object.
*/
async handleRequest(req) {
const url = new URL(req.url);
if (
!url.pathname.endsWith('/') &&
Array.isArray(this.publicFiles) &&
this.publicFiles.length > 0
) {
let path = this.publicFiles.find(
(v) => url.pathname.substring(1) === v
);
if (path) {
try {
return new Response(
Bun.file(
resolvePath(
this.options.get('publicDir') ??
`${import.meta.dir}/public`,
path
)
)
);
} catch (err) {
Logger(err, import.meta.dir, 'public', path);
return new Response('Internal Server Error', {
status: 500,
});
}
}
}
if (this.options.enforceTrailingSlash && !url.pathname.endsWith('/')) {
return Response.redirect(`${url.toString()}/`, 302);
}
let resp = await this.routing.handle(req);
if (resp instanceof Response) return resp;
return new Response('Not Found', { status: 404 });
}
/**
* Starts the server and listens for incoming requests on the specified port.
* @param {number} [port=8080] - The port number to listen on.
*/
async listen(port = 8080, cb = () => {}) {
try {
this.proc = await Bun.serve({
port,
development: Bun.env.BUN_ENV != 'production',
fetch: this.handleRequest.bind(this),
error(err) {
Logger('where', err.stack);
return new Response(`<pre>${err}\n${err.stack}</pre>`, {
headers: {
'Content-Type': 'text/html',
},
});
},
});
if (!this.proc) {
throw new Error('something went wrong');
}
if (!!cb && cb.constructor?.name.endsWith('Function')) {
cb(this.proc);
}
} catch (err) {
Logger.error('where', err.stack);
}
}
/**
* Retrieves the path of a view file based on the given filepath.
* @param {string} filepath - The filepath of the view file.
* @returns {Promise<string|null>} - The path of the view file, or null if not found.
*/
async getViewPath(filepath) {
let path = this.options.get('viewsPath');
if (!path.endsWith('/')) path += '/';
const filename = filepath.split('/').pop().toString();
const basedir = filepath.substring(0, filepath.indexOf(filename));
path += basedir;
if (!path.endsWith('/')) path += '/';
const files = await readdir(path);
for (let file of files) {
console.log(
file,
file.substring(0, file.indexOf('.')),
filename.substring(0, filename.indexOf('.')) || filename
);
if (
(file.substring(0, file.indexOf('.')) || file) !=
(filename.substring(0, filename.indexOf('.')) || filename)
)
continue;
return `${path}${file}`;
}
return null;
}
/**
* Loads a file from the specified filepath.
*
* @param {string} filepath - The path of the file to load.
* @returns {DefaultLoader} - The loader for the file.
*/
async load(filepath) {
const fullFilePath = await this.getViewPath(filepath);
if (!fullFilePath) return new DefaultLoader(filepath);
const filename = fullFilePath.split('/').pop().toString();
const ext = filename.substring(filename.indexOf('.') + 1);
if (this.loaders.size > 0) {
for (const keys of this.loaders.keys()) {
if (!keys.includes(ext)) continue;
return this.loaders.get(keys).handle(fullFilePath);
}
}
return new DefaultLoader(filepath);
}
async add(path, cb) {
this.routing.add(...arguments)
}
}

View File

@@ -1,50 +0,0 @@
import Logger from '../Utils/Logger';
export default class BaseRouter {
add(path, cb, options = {}) {
Logger.error('add method not implemented');
}
any(path, cb) {
this.all(path, cb);
}
all(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'all' });
return this;
}
get(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'get' });
return this;
}
post(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'post' });
return this;
}
put(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'put' });
return this;
}
patch(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'patch' });
return this;
}
delete(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'delete' });
return this;
}
head(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'head' });
return this;
}
options(path, cb) {
this.add(path, cb, { ...(arguments[2] || {}), method: 'options' });
return this;
}
}

View File

@@ -1,100 +0,0 @@
import Router from './Router';
import BaseRouter from './BaseRouter';
import LayRequest from '../IO/LayRequest';
import LayResponse from '../IO/LayResponse';
import Logger from '../Utils/Logger';
export default class LayRouting extends BaseRouter {
routers;
namespace = null;
server;
constructor(server, namespace = null) {
super();
this.server = server;
this.routers = new Map([
['$default', new Router(this.server)]
]);
}
getServer() {
return this.server;
}
setNamespace(ns) {
if (!ns || typeof ns != 'string') this.namespace = null;
this.namespace = ns;
}
routes() {
return this.routers.get('$default').routes;
}
addRouter(router) {
let routerInstance;
if(typeof router == 'undefined' || router === null) {
routerInstance = new Router(this.server);
} else {
routerInstance = new router(this.server);
}
this.routers.set(router.domain || '$default', routerInstance);
}
route(config = {}) {
if (typeof config != 'object')
throw new Error('route config is not an object');
const { path, handle, method = 'get' } = config;
if (typeof path != 'string')
throw new Error('route path is not a string');
if (typeof handle != 'function')
throw new Error('route handle is not a function');
this.add(path, handle, method);
return this;
}
async handle(req) {
const url = new URL(req.url);
const reqContext = {
request: null,
response: null,
};
try {
reqContext.request = new LayRequest(req);
reqContext.response = new LayResponse(this);
} catch (err) {
Logger.error(err);
}
let domain = url.hostname;
if([...this.routers.keys()].indexOf(domain) < 0) domain = '$default';
return await this.routers.get(domain).handle(reqContext);
}
add(path, cb) {
let options = arguments[2] ?? { method: 'get', domain: '$default' };
if(typeof options.domain != 'string') options.domain = '$default';
if (this.namespace) {
if (!path.startsWith('/')) path = '/' + path;
path = this.namespace + path;
}
Logger(path, options)
if(options.domain === '$default') {
let domains = this.routers.keys();
for(let domain of [...domains]) {
this.routers.get(domain).add(path, cb, options);
}
} else {
if(!this.routers.has(options.domain)) {
const domainRouter = Router;
domainRouter.domain = options.domain;
this.addRouter(domainRouter);
Logger('added new router for domain', options.domain)
}
this.routers.get(options.domain).add (path, cb, options);
}
}
}

View File

@@ -1,135 +0,0 @@
import Logger from './Logger';
export default class Placekeeper {
static init(content) {
const sections = Array();
const variables = new Map();
const currentSection = new Map();
for (let idx = 0; idx < content.length; idx++) {
const char = content[idx];
if (char == '{') {
if (content[idx + 1] == '{') {
if (currentSection.has('start')) currentSection.clear(); // everything before this was not a section then
// begins a new section meant to be for a variable but still can be an operational section
// @example: {{#if variableName}} {{#for variableName}} {{#while variableName}}
currentSection.set('start', idx);
}
} else if (currentSection.has('start')) {
if (char == '}') {
if (content[idx + 1] == '}') {
// ends a section
currentSection.set('end', idx + 2);
currentSection.set(
'content',
content
.substring(
currentSection.get('start'),
currentSection.get('end')
)
.toString()
.trim()
);
currentSection.set(
'variables',
Placekeeper.parseVariables(
currentSection.get('content')
)
);
sections.push(new Map(currentSection));
currentSection.clear();
idx++;
}
}
}
}
Logger('content to array', Placekeeper.contentToArray(content));
return {
variables,
sections,
};
}
static contentToArray(content) {
const parts = content.split(/\{\{([^}]+)\}\}|\{(#[a-zA-Z]+)\s*([^}]+)\}/);
const result = [];
let stack = [];
function processStack() {
const block = stack.pop();
result.push(new Function('data', 'with(data){return `'+ block.content.join('') + '`;}'));
}
// Process each part of the split content
for (let i = 0; i < parts.length; i++) {
if (parts[i] === '#' && ['for', 'if', 'else', 'elseif', 'while'].includes(parts[i + 1])) {
// Start of control structure
stack.push({ type: parts[i + 1], content: [] });
i += 2; // Skip the control structure identifier
} else if (parts[i] === '/#' && ['for', 'if', 'while'].includes(stack[stack.length - 1].type)) {
// End of control structure
i++;
processStack();
} else if (stack.length > 0) {
// Inside a control structure
stack[stack.length - 1].content.push(parts[i]);
} else if (parts[i] !== undefined && parts[i] !== '#end') {
// Directly executable expression or static string
result.push(parts[i].startsWith('\n') ? '\n' + parts[i].trimStart() : parts[i].trimEnd());
}
}
return result;
}
static variableRegex = new RegExp(/[\w\d]/, 'i');
static parseVariables(content) {
// iterates over the content character by character to extract variables and returns a list of them
const variables = new Map();
let currentVar;
for (let idx = 0; idx < content.length; idx++) {
const char = content[idx];
if (Placekeeper.variableRegex.test(char)) {
if (!(currentVar instanceof Map))
currentVar = new Map([
['name', ''],
['start', idx],
['end', -1],
]);
currentVar.set('name', currentVar.get('name') + char);
} else if (currentVar instanceof Map) {
currentVar.set('end', idx);
if (currentVar.get('name').length > 0) {
variables.set(currentVar.get('name'), currentVar);
}
currentVar = null;
}
}
}
static parse(content, sections) {
// replace all sections with an empty string
if (!Array.isArray(sections)) {
if (!sections instanceof Map) return content;
sections = [...sections.values()];
}
let parts = [];
let offset = 0;
for (const section of sections) {
if (!(section instanceof Map)) {
if (!Array.isArray(section)) continue;
if (section.length < 3) continue;
}
parts.push(content.substring(offset, section.get('start')));
offset = section.get('end');
}
parts.push(content.substring(offset));
return parts.join('');
}
}

View File

@@ -1,36 +0,0 @@
import Logger from './Logger';
export async function parseFormData(req, multipart = false) {
const data = await req.formData();
for (const key of data.keys()) {
const value = data.get(key);
if (value.constructor.name == 'Blob' && multipart) {
let tempFile = Bun.file(`/tmp/${Date.now().toString(36)}`, { type: value.type });
data.set(key, await Bun.write(tempFile, value));
}
}
return data;
}
export default async function RequestParser(req) {
try {
const contentType = req.headers.get('Content-Type')?.toLowerCase();
let val = await req.text();
console.log(val);
return {};
if (!contentType) return await req.text();
if (contentType.indexOf('multipart') >= 0)
return await parseFormData(req, true);
if (contentType == 'application/x-www-form-urlencoded')
return await parseFormData(req);
if (contentType == 'application/json') return await req.json();
return await req.text();
} catch (err) {
console.log(err, typeof req, req.constructor.name);
}
}