Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ec7cb04
chore: initiate the refactoring of response object; rearchitecturing
arpan404 Feb 12, 2025
47a3abc
chore: nothing
arpan404 Feb 12, 2025
5c360e8
del: removed husky pre commit push commands while working, will add l…
arpan404 Feb 12, 2025
4449feb
feat: sendFile added in JoorResponse
arpan404 Feb 13, 2025
80d9d21
feat: attachements can now be sent through JoorResponse
arpan404 Feb 13, 2025
9e6f784
refacor: separated errors in same file
arpan404 Feb 13, 2025
c405fbc
del: Remove not needed types
arpan404 Feb 13, 2025
843efad
refactor: extended functionalites of response object from ServerResponse
arpan404 Feb 13, 2025
79709d2
chore: added some methods in response object
arpan404 Feb 13, 2025
3e9844a
add: vary functionality
arpan404 Feb 13, 2025
f4c692d
feat: Added vary package
arpan404 Feb 14, 2025
099f067
chore: Development restarted
arpan404 Apr 3, 2025
d9f8ea3
feat: Redirection feature using .location method on response object
arpan404 Apr 3, 2025
a31fd4a
feat: Add jssert function, alternative to node:assert
arpan404 Apr 3, 2025
d52c76e
feat: Add handleError function which reduce code redundancy while han…
arpan404 Apr 3, 2025
0a21dbd
feat: Add set and get method in Response object to set and get header
arpan404 Apr 3, 2025
284bbaa
refactor: Refactor configuration loading class using jssert
arpan404 Apr 3, 2025
42cce7c
fix: Fixed some methods of joor class
arpan404 Apr 3, 2025
65b3a23
refactor: Rewrote route matcher according to new architecture and in …
arpan404 Apr 3, 2025
1aa286e
refactor: some minor changes
arpan404 Apr 3, 2025
a34a427
refactor: Make the router class more readable
arpan404 Apr 3, 2025
d5bf0ec
chore: Added jsdocs to router class and also deleted unnecessary files
arpan404 Apr 3, 2025
c5d6aef
feat: Add sendStatus method to response object
arpan404 Apr 3, 2025
2921b88
feat: Methods to redirect and send json
arpan404 Apr 3, 2025
88995f2
chore: Add jsdocs to json and redirect methods
arpan404 Apr 3, 2025
8dae4ac
feat: Add cookies setter and header delete method
arpan404 Apr 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
npm run lint:fix && npm run format
1 change: 0 additions & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
npm run test
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Joor

**Joor** is a modern, high-performance backend framework built on **Node.js**, designed for **efficiency, scalability, and simplicity**. With **built-in tools** and a **lightweight core**, Joor minimizes dependencies while maximizing performance.
**Joor** is a modern, high-performance backend framework built on **Node.js** (compatible with runtimes like Bun and Deno), designed for **efficiency, scalability, and simplicity**. Featuring **built-in tools** and a **lightweight core**, Joor minimizes dependencies while maximizing performance.

**Note**: Joor is in early development; documentation and features may be incomplete.

Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "joor",
"version": "0.0.1",
"version": "0.0.0-alpha",
"description": "Joor is a full-fledged backend web framework for small to enterprise-level projects. Joor.js provides blazing fast responsiveness to the web app with many built-in features.",
"author": "Joor",
"license": "MIT",
Expand Down Expand Up @@ -73,8 +73,5 @@
"dependencies": {
"mime-types": "^2.1.35",
"socket.io": "^4.8.1"
},
"exports": {
"./middlewares": "./src/middlewares/index.js"
}
}
54 changes: 18 additions & 36 deletions src/core/config/configuration.ts → src/core/config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import fs from 'node:fs';
import path from 'node:path';

import Jrror from '@/core/error/index';
// import validateConfig from '@/helpers/validateConfig';
import JoorError from '@/core/error/JoorError';
import logger from '@/helpers/joorLogger';
import { handleError, jssert } from '@/core/error';
import validateConfig from '@/helpers/validateConfig';
import JOOR_CONFIG from '@/types/config';

/**
Expand All @@ -27,16 +25,12 @@ class Configuration {
*/
private async loadConfig(): Promise<void> {
// Check if the configuration data is already loaded
if (Configuration.configData !== null) {
throw new Jrror({
code: 'config-loaded-already',
docsPath: '/configuration',
message:
'The configuration data is already loaded. Attempting to load it again is not recommended',
type: 'warn',
});
}

jssert(
Configuration.configData === null,
'Configuration data is already loaded. Attempting to load it again is not recommended.',
'/configuration',
'warn'
);
try {
// Default config file name is joor.config.js or else fallback to joor.config.ts
let configFile = 'joor.config.js';
Expand All @@ -45,29 +39,21 @@ class Configuration {
configFile = 'joor.config.ts';
}

if (!fs.existsSync(path.resolve(process.cwd(), configFile))) {
throw new Jrror({
code: 'config-file-missing',
docsPath: '/configuration',
message:
'The configuration file (joor.config.js or joor.config.ts) is missing in the root directory.',
type: 'error',
});
}
// Check if the configuration file exists
jssert(
fs.existsSync(path.resolve(process.cwd(), configFile)),
'The configuration file (joor.config.js or joor.config.ts) is missing in the root directory.',
'/configuration',
'error'
);

const configPath = path.resolve(process.cwd(), configFile);
// Dynamically import the configuration file
const configData = (await import(configPath)).config as JOOR_CONFIG;
// Configuration.configData = validateConfig(configData);
Configuration.configData = configData;
Configuration.configData = validateConfig(configData);
this.setConfigToEnv();
} catch (error) {
throw new Jrror({
code: 'config-load-failed',
message: `Error occured while loading the configuration file. ${error}`,
type: 'panic',
docsPath: '/configuration',
});
handleError(error);
}
}

Expand Down Expand Up @@ -107,11 +93,7 @@ class Configuration {
try {
await this.loadConfig();
} catch (error: unknown) {
if (error instanceof Jrror || error instanceof JoorError) {
error.handle();
} else {
logger.error(error);
}
handleError(error);
}
}

Expand Down
2 changes: 0 additions & 2 deletions src/core/config/index.ts

This file was deleted.

241 changes: 241 additions & 0 deletions src/core/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import joorData from '@/data/joor';
import logger from '@/helpers/joorLogger';
import marker from '@/packages/marker';
import { JOOR_ERROR } from '@/types/error';

/**
* Custom class with additional metadata such as error code, message and type.
* Extends the native `Error` class to support structured error handling
*/
class JoorError extends Error {
/**
* The unique code indentifying the error
* @type number
*/
public errorCode: JOOR_ERROR['code'];

/**
* The type of error
* @type "warn"|"error"|"panic"
*/
public type: JOOR_ERROR['type'];

/**
* The path to the documentation for the error
* @type string
*/
public docsPath: JOOR_ERROR['docsPath'];

/**
* The stack trace of the error, captured at the point of instantiation.
* @type {string | undefined}
*/
public stackTrace: string | undefined;

/**
* Constructs a new `JoorError` instance.
*
* @param {Object} params - The parameters for creating the error.
* @param {JOOR_ERROR["message"]} params.message - A descriptive message for the error.
* @param {JOOR_ERROR["errorCode"]} params.errorCode - A unique identifier for the error.
* @param {JOOR_ERROR["type"]} params.type - The type of the error (e.g., "warn", "panic").
*/

constructor({
message,
errorCode,
type,
docsPath,
}: {
message: JOOR_ERROR['message'];
errorCode: JOOR_ERROR['code'];
type: JOOR_ERROR['type'];
docsPath?: JOOR_ERROR['docsPath'];
}) {
super(message);
this.name = this.constructor.name;
this.errorCode = errorCode;
this.type = type;
this.docsPath = docsPath;
// Capture stack trace if the environment supports it
if (Error.captureStackTrace) {
Error.captureStackTrace(this, JoorError);
}
this.stackTrace = this.stack;
// Set the prototype chain correctly for proper inheritance
Object.setPrototypeOf(this, JoorError.prototype);
}
/**
* Method to handle the error by logging it to the console
* If the type of error thrown is `panic`, using `handle` method will cause the program to stop
* @example
* ```typescript
* try{
* ...
* }
* catch(error:unknown){
* if (error instanceof Jrror){
* error.handle()
* }
* }
*
*/
public handle(): void {
const errorMessage = this.formatMessage();

if (this.type === 'warn') {
logger.warn(errorMessage);
} else if (this.type === 'error') {
logger.error(`${errorMessage}\nStack Trace:\n${this.stackTrace}`);
} else if (this.type === 'panic') {
logger.error(`${errorMessage}\nStack Trace:\n${this.stackTrace}`);
process.exit(1);
}
}

/**
* Method to reject the error by not logging it console
* @example
* ```typescript
* try{
* ...
* }
* catch(error:unknown){
* if (error instanceof Jrror){
* error.reject()
* }
* }
*
*/
public reject(): void {
return;
}
/**
* Formats the error message for user-friendly display, including the error code, message, and a link to documentation.
*
* @returns {string} The formatted error message.
*/
private formatMessage(): string {
const docLink = `${joorData.docs}/${joorData.docsVersion}${this.docsPath}?error=${this.errorCode}`;

return `
Error Code: ${this.errorCode}
Message: ${this.message}
${marker.greenBright(
'For more information, visit:'
)} ${marker.bgGreenBright.whiteBright(docLink)}
`;
}
}

/**
* Class to work with errors
*
* @example
* ```typescript
* // Throwing an error
* throw new Jrror({code:"config-p1",
* message: `Error: The configuration file '${joorData.configFile}' for Joor app is not found.\nMake sure the file is in the root directory of your project.`,
* type: "panic"
* })
*
* // Handling the thrown error
* try{
* ...
* }
* catch(error:unknown){
* if (error instanceof Jrror){
* if (error.type !=="warn"){
* error.handle() // Call this method to log and handle the error based on its type
* }
* else{
* error.reject() // Call this method to reject the error
* }
* }
* }
*
*/

class Jrror extends JoorError {
constructor(errorData: JOOR_ERROR) {
// Validate the error data provided when creating the instance of Jrror class
if (!errorData?.code || !errorData.message || !errorData.type) {
// Throws error code joor-e1 if errorData is not provided, e2 if code is not provided, e3 if message is not provided, e4 if type is not provided
throw new Jrror({
message: `Instance of Jrror has been created without passing required data.
Missing: ${
!errorData
? 'errorData'
: !errorData.code
? 'error code'
: !errorData.message
? 'message'
: 'type'
}`,
code: `jrror-${
!errorData
? 'e1'
: !errorData.code
? 'e2'
: !errorData.message
? 'e3'
: 'e4'
}`,
type: 'error',
});
}
super({
errorCode: errorData.code,
message: errorData.message,
type: errorData.type,
docsPath: errorData.docsPath ?? '/',
});
}
}

/**
* Handles errors by checking their type and calling the appropriate method.
* If the error is not an instance of Jrror or JoorError, it logs the error.
*
* @param {unknown} error - The error to handle.
*
* Meant to reduce code duplication while handling errors in the codebase.
*/
function handleError(error: unknown): void {
if (error instanceof Jrror || error instanceof JoorError) {
error.handle();
} else {
logger.error(error);
}
}

/**
* Implicitly asserts that a condition is true. If the condition is false, it throws an error with the provided message and documentation path.
* Alternative to `assert(condition, message)` from the `node:assert` module.
*
* For naming convention, `jssert` is used to avoid confusion with the `assert` function from the `node:assert` module.
*
* @param {boolean} condition - The condition to assert.
* @param {string} message - The error message to throw if the assertion fails.
* @param {string} docsPath - The documentation path for the error.
* @param {JOOR_ERROR['type']} type - The type of error to throw ["warn" | "error" | panic]
*
*/
function jssert(
condition: boolean,
message: string,
docsPath: string = '/assertion',
type: JOOR_ERROR['type'] = 'error'
): asserts condition {
if (!condition) {
throw new Jrror({
code: 'assertion-failed',
message,
type: type,
docsPath: docsPath,
});
}
}

export { JoorError, jssert, handleError };
export default Jrror;
Loading