te.js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "singleQuote": true,
3
+ "printWidth": 80,
4
+ "semi": true
5
+ }
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # IN DEVELOPMENT. DO NOT USE
@@ -0,0 +1,7 @@
1
+ import mongo from './mongo.js';
2
+
3
+ const database = {
4
+ mongodb: mongo,
5
+ };
6
+
7
+ export default database;
@@ -0,0 +1,14 @@
1
+ import mongoose from './../example/node_modules/mongoose';
2
+
3
+ const connect = (uri, options, cb) => {
4
+ mongoose
5
+ .connect(uri, options)
6
+ .then(() => {
7
+ cb(undefined);
8
+ })
9
+ .catch((error) => {
10
+ cb(error);
11
+ });
12
+ };
13
+
14
+ export default connect;
@@ -0,0 +1,6 @@
1
+ import { Tejas } from 'te.js';
2
+ import './routes/index.target.js';
3
+
4
+ const tejas = new Tejas();
5
+ tejas.connectDatabase();
6
+ tejas.takeoff();
@@ -0,0 +1,9 @@
1
+ const auth = (ammo, next) => {
2
+ if (ammo.headers.authorization === "Bearer 123") {
3
+ next();
4
+ } else {
5
+ return ammo.unauthorized();
6
+ }
7
+ };
8
+
9
+ export default auth;
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "example",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node index.js",
9
+ "test": "echo \"Error: no test specified\" && exit 1"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "te.js": "file:..",
16
+ "tej-env": "^1.0.2",
17
+ "mongoose": "^8.3.1"
18
+ }
19
+ }
@@ -0,0 +1,10 @@
1
+ import { Target } from "te.js";
2
+
3
+ const target = new Target();
4
+
5
+ target.register('/hello', (ammo) => {
6
+ ammo.dispatch({
7
+ status: 200,
8
+ body: 'Hello, World!',
9
+ });
10
+ });
@@ -0,0 +1,13 @@
1
+ import {Target} from 'te.js';
2
+
3
+ const target = new Target();
4
+
5
+ const registerMiddleware = (req, res) => {
6
+ console.log("Register user middleware");
7
+ }
8
+
9
+ target.register('/register', registerMiddleware, (req, res) => {
10
+ res.writeHead(200, {'Content-Type': 'text/html'});
11
+ res.write('Hello User');
12
+ res.end();
13
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "port": 1403,
3
+ "log": {
4
+ "http_requests": true,
5
+ "exceptions": true
6
+ },
7
+ "db": {
8
+ "type": "mongodb",
9
+ "uri": "mongodb://localhost:27017/te-js"
10
+ }
11
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "te.js",
3
+ "version": "1.0.0",
4
+ "description": "A nodejs framework",
5
+ "type": "module",
6
+ "main": "te.js",
7
+ "scripts": {
8
+ "start": "node te.js",
9
+ "test": "echo \"Error: no test specified\" && exit 1",
10
+ "prepare": "husky"
11
+ },
12
+ "author": "Hirak",
13
+ "license": "ISC",
14
+ "devDependencies": {
15
+ "@types/node": "^20.12.5",
16
+ "husky": "^9.0.11",
17
+ "lint-staged": "^15.2.2",
18
+ "prettier": "3.2.5"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git@github.com:hirakchhatbar/te.js.git"
23
+ },
24
+ "dependencies": {
25
+ "ansi-colors": "^4.1.3",
26
+ "formidable": "^3.5.1",
27
+ "statuses": "^2.0.1",
28
+ "tej-env": "^1.0.5",
29
+ "tej-logger": "^1.2.1"
30
+ },
31
+ "lint-staged": {
32
+ "**/*.{js,ts}": "prettier --write --ignore-unknown"
33
+ }
34
+ }
@@ -0,0 +1,51 @@
1
+ import formidable from 'formidable';
2
+
3
+ async function parseDataBasedOnContentType(req) {
4
+ // Check if content type is JSON
5
+ if (req.headers['content-type'] === 'application/json') {
6
+ return await parseJSONRequestBody(req);
7
+ }
8
+
9
+ // Check if content type is URL encoded
10
+ if (req.headers['content-type'] === 'application/x-www-form-urlencoded') {
11
+ return await parseUrlEncodedData(req);
12
+ }
13
+
14
+ return null;
15
+ }
16
+
17
+ function parseJSONRequestBody(req) {
18
+ return new Promise((resolve, reject) => {
19
+ let body = '';
20
+ req.on('data', (chunk) => {
21
+ body += chunk.toString();
22
+ });
23
+
24
+ req.on('end', () => {
25
+ try {
26
+ const jsonData = JSON.parse(body);
27
+ resolve(jsonData); // Resolve promise with the parsed JSON
28
+ } catch (err) {
29
+ reject(new Error('Invalid JSON')); // Reject promise if JSON parsing fails
30
+ }
31
+ });
32
+ });
33
+ }
34
+
35
+ function parseUrlEncodedData(req) {
36
+ return new Promise((resolve, reject) => {
37
+ let body = '';
38
+
39
+ req.on('data', (chunk) => {
40
+ body += chunk.toString();
41
+ });
42
+
43
+ req.on('end', () => {
44
+ const data = new URLSearchParams(body);
45
+ const parsedData = Object.fromEntries(data);
46
+ resolve(parsedData);
47
+ });
48
+ });
49
+ }
50
+
51
+ export default parseDataBasedOnContentType;
@@ -0,0 +1,53 @@
1
+ import status from 'statuses';
2
+
3
+ const formattedData = (data) => {
4
+ if (typeof data === 'object') return JSON.stringify(data);
5
+ if (typeof data === 'string') return data;
6
+ if (typeof data === 'number') return status[data];
7
+ return data;
8
+ };
9
+
10
+ const statusAndData = (args) => {
11
+ if (!args || args.length === 0)
12
+ return {
13
+ statusCode: 204,
14
+ data: status(204),
15
+ contentType: 'text/plain',
16
+ };
17
+
18
+ if (args.length === 1 && typeof args[0] === 'number')
19
+ return {
20
+ statusCode: args[0],
21
+ data: status(args[0]),
22
+ contentType: 'text/plain',
23
+ };
24
+
25
+ let statusCode = 200;
26
+ let data = args[0];
27
+ if (args.length > 1) {
28
+ statusCode = args[0];
29
+ data = args[1];
30
+ if (!data) data = status[statusCode];
31
+ }
32
+
33
+ return {
34
+ statusCode,
35
+ data: formattedData(data),
36
+ contentType: contentType(data),
37
+ };
38
+ };
39
+
40
+ const contentType = (data) => {
41
+ switch (typeof data) {
42
+ case 'object':
43
+ return 'application/json';
44
+ case 'string':
45
+ return 'text/html';
46
+ case 'number':
47
+ return 'text/plain';
48
+ default:
49
+ return 'text/plain';
50
+ }
51
+ };
52
+
53
+ export { statusAndData, contentType };
@@ -0,0 +1,53 @@
1
+ import bodyParser from './body-parser.js';
2
+
3
+ function hostname(req) {
4
+ let host = req.headers['X-Forwarded-Host'];
5
+
6
+ if (!host) {
7
+ host = req.headers.host;
8
+ } else if (host.indexOf(',') !== -1) {
9
+ host = host.substring(0, host.indexOf(',')).trimRight();
10
+ }
11
+
12
+ return host;
13
+ }
14
+
15
+ async function generatePayload(req) {
16
+ const obj = {};
17
+
18
+ const searchParams = new URLSearchParams(req.url.split('?')[1]);
19
+ for (const [key, value] of searchParams) {
20
+ obj[key] = value;
21
+ }
22
+
23
+ const body = await bodyParser(req);
24
+ if (body) Object.assign(obj, body);
25
+ return obj;
26
+ }
27
+
28
+ function protocol(req) {
29
+ const proto = req.connection.encrypted ? 'https' : 'http';
30
+
31
+ const header = req.headers['X-Forwarded-Proto'] || proto;
32
+ const index = header.indexOf(',');
33
+
34
+ return index !== -1 ? header.substring(0, index).trim() : header.trim();
35
+ }
36
+
37
+ const enhance = async (ammo) => {
38
+ const req = ammo.req;
39
+
40
+ ammo.ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
41
+ ammo.headers = req.headers;
42
+ ammo.payload = await generatePayload(req);
43
+ ammo.method = req.method;
44
+
45
+ ammo.protocol = protocol(req);
46
+ ammo.hostname = hostname(req);
47
+ ammo.path = req.url;
48
+ ammo.endpoint = req.url.split('?')[0];
49
+
50
+ ammo.fullURL = `${ammo.protocol}://${ammo.hostname}/${ammo.path}`;
51
+ };
52
+
53
+ export default enhance;
package/server/ammo.js ADDED
@@ -0,0 +1,95 @@
1
+ import { statusAndData } from './ammo/dispatch-helper.js';
2
+ import {
3
+ isStatusCode,
4
+ toStatusCode,
5
+ toStatusMessage,
6
+ } from '../utils/status-codes.js';
7
+ import html from '../utils/tejas-entrypoint-html.js';
8
+ import ammoEnhancer from './ammo/enhancer.js';
9
+
10
+ class Ammo {
11
+ constructor(req, res) {
12
+ this.req = req;
13
+ this.res = res;
14
+
15
+ // Request related data
16
+ this.ip = undefined;
17
+ this.headers = undefined;
18
+ this.payload = undefined;
19
+ this.method = undefined;
20
+
21
+ // URL related data
22
+ this.protocol = undefined;
23
+ this.hostname = undefined;
24
+ this.path = undefined;
25
+ this.endpoint = undefined;
26
+
27
+ this.fullURL = undefined;
28
+
29
+ // Response related data
30
+ this.dispatchedData = undefined;
31
+ }
32
+
33
+ async enhance() {
34
+ await ammoEnhancer(this);
35
+ }
36
+
37
+ dispatch() {
38
+ const { statusCode, data, contentType } = statusAndData(arguments);
39
+ const contentTypeHeader = { 'Content-Type': contentType };
40
+
41
+ this.dispatchedData = data;
42
+
43
+ this.res.writeHead(statusCode, contentTypeHeader);
44
+ this.res.write(data ?? '');
45
+ this.res.end();
46
+ }
47
+
48
+ notFound() {
49
+ this.throw(new Error('Not Found'));
50
+ }
51
+
52
+ notAllowed() {
53
+ this.throw(new Error('Method Not Allowed'));
54
+ }
55
+
56
+ unauthorized() {
57
+ this.throw(new Error('Unauthorized'));
58
+ }
59
+
60
+ defaultEntry() {
61
+ this.dispatch(html);
62
+ }
63
+
64
+ throw() {
65
+ const err = arguments[0];
66
+ const errCode = arguments[1];
67
+
68
+ let errMsg = err instanceof Error ? err.message : err.toString();
69
+
70
+ if (errCode && isStatusCode(errCode)) {
71
+ if (!errMsg) errMsg = toStatusMessage(errCode);
72
+ this.dispatch(errCode, errMsg);
73
+ return;
74
+ }
75
+
76
+ if (err instanceof Error) {
77
+ const errMessage = err.message;
78
+
79
+ if (!isNaN(parseInt(errMessage))) {
80
+ // Execute when errMessage is a number. Notice ! in front of isNan
81
+ const message = toStatusMessage(errMessage) ?? toStatusMessage(500);
82
+ this.dispatch(message, message);
83
+ return;
84
+ }
85
+
86
+ const code = toStatusCode(errMsg) ?? 500;
87
+ this.dispatch(code, errMsg);
88
+ return;
89
+ }
90
+
91
+ this.dispatch(err);
92
+ }
93
+ }
94
+
95
+ export default Ammo;
@@ -0,0 +1,70 @@
1
+ import TejLogger from 'tej-logger';
2
+ import { env } from 'tej-env';
3
+
4
+ import Ammo from './ammo.js';
5
+ import TargetRegistry from './targets/registry.js';
6
+ import logHttpRequest from '../utils/request-logger.js';
7
+
8
+ const targetRegistry = new TargetRegistry();
9
+ const errorLogger = new TejLogger('Tejas.Exception');
10
+
11
+ const executeChain = async (target, ammo) => {
12
+ let i = 0;
13
+
14
+ const chain = targetRegistry.globalMiddlewares.concat(target.middlewares);
15
+ chain.push(target.shoot);
16
+
17
+ const next = async () => {
18
+ const middleware = chain[i];
19
+ i++;
20
+
21
+ const args =
22
+ middleware.length === 3 ? [ammo.req, ammo.res, next] : [ammo, next];
23
+
24
+ try {
25
+ await middleware(...args);
26
+ } catch (err) {
27
+ const ammo =
28
+ middleware.length === 2 ? args[0] : new Ammo(args[0], args[1]);
29
+ errorHandler(ammo, err);
30
+ }
31
+ };
32
+
33
+ await next();
34
+ };
35
+
36
+ const errorHandler = (ammo, err, errCode) => {
37
+ if (env('LOG_EXCEPTIONS')) errorLogger.error(err);
38
+
39
+ ammo.throw(err, errCode);
40
+ };
41
+
42
+ const handler = async (req, res) => {
43
+ const target = targetRegistry.aim(req.method, req.url.split('?')[0]);
44
+ const ammo = new Ammo(req, res);
45
+ await ammo.enhance();
46
+
47
+ if (env('LOG_HTTP_REQUESTS')) logHttpRequest(ammo);
48
+
49
+ try {
50
+ if (target) {
51
+ await executeChain(target, ammo);
52
+ } else {
53
+ if (req.url === '/') {
54
+ ammo.defaultEntry();
55
+ } else {
56
+ errorHandler(
57
+ ammo,
58
+ new Error(
59
+ `No target found for URL ${ammo.fullURL} with method ${ammo.method}`,
60
+ ),
61
+ 404,
62
+ );
63
+ }
64
+ }
65
+ } catch (err) {
66
+ errorHandler(ammo, err);
67
+ }
68
+ };
69
+
70
+ export default handler;
@@ -0,0 +1,62 @@
1
+ import isMiddlewareValid from './targets/middleware-validator.js';
2
+ import TargetRegistry from './targets/registry.js';
3
+
4
+ const targetRegistry = new TargetRegistry();
5
+
6
+ const isEndpointValid = (endpoint) => {
7
+ if (typeof endpoint !== 'string') return false;
8
+ if (endpoint.length === 0) return false;
9
+ return endpoint[0] === '/';
10
+ };
11
+
12
+ const isShootValid = (shoot) => typeof shoot === 'function';
13
+
14
+ const validMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
15
+
16
+ class Target {
17
+ constructor(base = '') {
18
+ this.base = base;
19
+ this.targetMiddlewares = [];
20
+ }
21
+
22
+ base(base) {
23
+ if (!base || !base.startsWith('/')) return;
24
+ this.base = base;
25
+ }
26
+
27
+ midair() {
28
+ if (!arguments) return;
29
+ const middlewares = [...arguments];
30
+ const validMiddlewares = middlewares.filter(isMiddlewareValid);
31
+ this.targetMiddlewares = this.targetMiddlewares.concat(validMiddlewares);
32
+ }
33
+
34
+ register() {
35
+ let allowedMethods = validMethods;
36
+ let args = arguments;
37
+ if (!args) return;
38
+
39
+ if (validMethods.includes(args[0])) {
40
+ allowedMethods = [args[0]];
41
+ args = arguments[1];
42
+ }
43
+
44
+ const endpoint = args[0];
45
+ const shoot = args[args.length - 1];
46
+ const middlewares = Array.from(args).slice(1, args.length - 1);
47
+
48
+ if (!isEndpointValid(endpoint)) return;
49
+ if (!isShootValid(shoot)) return;
50
+
51
+ const validMiddlewares = middlewares.filter(isMiddlewareValid);
52
+
53
+ targetRegistry.targets.push({
54
+ allowedMethods: allowedMethods.length > 0 ? allowedMethods : validMethods,
55
+ endpoint: this.base + endpoint,
56
+ middlewares: this.targetMiddlewares.concat(validMiddlewares),
57
+ shoot,
58
+ });
59
+ }
60
+ }
61
+
62
+ export default Target;
@@ -0,0 +1,22 @@
1
+ import TejLogger from 'tej-logger';
2
+
3
+ const logger = new TejLogger('MiddlewareValidator');
4
+
5
+ const isMiddlewareValid = (middleware) => {
6
+ if (typeof middleware !== 'function') {
7
+ logger.error(`Middleware ${middleware} should be a function. Skipping...`);
8
+ return false;
9
+ }
10
+
11
+ const args = middleware.length;
12
+ if (args !== 2 && args !== 3) {
13
+ logger.error(
14
+ `Middleware ${middleware.name} should have 2 arguments (ammo, next) or 3 arguments (req, res, next). Skipping...`,
15
+ );
16
+ return false;
17
+ }
18
+
19
+ return true;
20
+ };
21
+
22
+ export default isMiddlewareValid;
@@ -0,0 +1,44 @@
1
+ import isMiddlewareValid from './middleware-validator.js';
2
+
3
+ class TargetRegistry {
4
+ constructor() {
5
+ if (TargetRegistry.instance) {
6
+ return TargetRegistry.instance;
7
+ }
8
+
9
+ TargetRegistry.instance = this;
10
+
11
+ // TODO - Add a default target
12
+ this.targets = [];
13
+ this.globalMiddlewares = [];
14
+ }
15
+
16
+ addGlobalMiddleware() {
17
+ if (!arguments) return;
18
+
19
+ const middlewares = [...arguments];
20
+ const validMiddlewares = middlewares.filter(isMiddlewareValid);
21
+ this.globalMiddlewares = this.globalMiddlewares.concat(validMiddlewares);
22
+ }
23
+
24
+ /**
25
+ * @param {Array || Object} targets
26
+ */
27
+ register(targets) {
28
+ if (Array.isArray(targets)) {
29
+ this.targets = this.targets.concat(targets);
30
+ } else {
31
+ this.targets.push(targets);
32
+ }
33
+ }
34
+
35
+ aim(method, endpoint) {
36
+ return this.targets.find((target) => {
37
+ return (
38
+ target.endpoint === endpoint && target.allowedMethods.includes(method)
39
+ );
40
+ });
41
+ }
42
+ }
43
+
44
+ export default TargetRegistry;
package/te.js ADDED
@@ -0,0 +1,106 @@
1
+ import { createServer } from 'node:http';
2
+
3
+ import { env, setEnv } from 'tej-env';
4
+ import TejLogger from 'tej-logger';
5
+ import database from './database/index.js';
6
+
7
+ import TargetRegistry from './server/targets/registry.js';
8
+ import Target from './server/target.js';
9
+
10
+ import { loadConfigFile, standardizeObj } from './utils/configuration.js';
11
+
12
+ import targetHandler from './server/handler.js';
13
+
14
+ const logger = new TejLogger('Tejas');
15
+ const targetRegistry = new TargetRegistry();
16
+
17
+ class Tejas {
18
+ /*
19
+ * Constructor for Tejas
20
+ * @param {Object} args - Arguments for Tejas
21
+ * @param {Number} args.port - Port to run Tejas on
22
+ * @param {Boolean} args.log.http_requests - Whether to log incoming HTTP requests
23
+ * @param {Boolean} args.log.exceptions - Whether to log exceptions
24
+ * @param {String} args.db.type - Database type. It can be 'mongodb', 'mysql', 'postgres', 'sqlite'
25
+ * @param {String} args.db.uri - Connection URI string for the database
26
+ */
27
+ constructor(args) {
28
+ if (Tejas.instance) return Tejas.instance;
29
+ Tejas.instance = this;
30
+
31
+ this.generateConfiguration(args);
32
+ }
33
+
34
+ /*
35
+ * Connect to a database
36
+ * @param {Object}
37
+ * @param {String} args.db - Database type. It can be 'mongodb', 'mysql', 'postgres', 'sqlite'
38
+ * @param {String} args.uri - Connection URI string for the database
39
+ * @param {Object} args.options - Options for the database connection
40
+ */
41
+ connectDatabase(args) {
42
+ const db = args?.db ?? env('DB_TYPE');
43
+ const uri = args?.uri ?? env('DB_URI');
44
+ const options = args?.options ?? {};
45
+
46
+ if (!db) return;
47
+ if (!uri) {
48
+ logger.error(
49
+ `Tejas could not connect to ${db} as it couldn't find a connection URI. See our documentation for more information.`,
50
+ false,
51
+ );
52
+ return;
53
+ }
54
+
55
+ const connect = database[db];
56
+ if (!connect) {
57
+ logger.error(
58
+ `Tejas could not connect to ${db} as it is not supported. See our documentation for more information.`,
59
+ false,
60
+ );
61
+ return;
62
+ }
63
+
64
+ connect(uri, options, (error) => {
65
+ if (error) {
66
+ logger.error(
67
+ `Tejas could not connect to ${db}. Error: ${error}`,
68
+ false,
69
+ );
70
+ return;
71
+ }
72
+
73
+ logger.info(`Tejas connected to ${db} successfully.`);
74
+ });
75
+ }
76
+
77
+ generateConfiguration(options) {
78
+ const configVars = standardizeObj(loadConfigFile());
79
+ const envVars = standardizeObj(process.env);
80
+ const userVars = standardizeObj(options);
81
+
82
+ const config = { ...configVars, ...envVars, ...userVars };
83
+ for (const key in config) {
84
+ if (config.hasOwnProperty(key)) {
85
+ setEnv(key, config[key]);
86
+ }
87
+ }
88
+
89
+ // Load defaults
90
+ if (!env('PORT')) setEnv('PORT', 1403);
91
+ }
92
+
93
+ midair() {
94
+ if (!arguments) return;
95
+ targetRegistry.addGlobalMiddleware(...arguments);
96
+ }
97
+
98
+ takeoff() {
99
+ this.engine = createServer(targetHandler);
100
+ this.engine.listen(env('PORT'), () => {
101
+ logger.info(`Tejas took off from port ${env('PORT')}`);
102
+ });
103
+ }
104
+ }
105
+
106
+ export { Tejas, Target };
@@ -0,0 +1,61 @@
1
+ import * as fs from 'fs';
2
+
3
+ const loadConfigFile = () => {
4
+ try {
5
+ const data = fs.readFileSync('tejas.config.json', 'utf8');
6
+ return JSON.parse(data);
7
+ } catch (err) {
8
+ return {};
9
+ }
10
+ };
11
+
12
+ const keysToUpperCase = (obj) => {
13
+ if (!obj) return {};
14
+ const standardObj = {};
15
+
16
+ for (const key in obj) {
17
+ if (obj.hasOwnProperty(key)) {
18
+ const value = obj[key];
19
+ if (
20
+ typeof value === 'object' &&
21
+ value !== null &&
22
+ !Array.isArray(value)
23
+ ) {
24
+ standardObj[key.toUpperCase()] = standardizeObj(value);
25
+ } else {
26
+ standardObj[key.toUpperCase()] = value;
27
+ }
28
+ }
29
+ }
30
+
31
+ return standardObj;
32
+ };
33
+ const flattenObject = (obj, prefix = '') => {
34
+ let flattened = {};
35
+
36
+ for (const key in obj) {
37
+ if (obj.hasOwnProperty(key)) {
38
+ const newKey = prefix.length ? `${prefix}_${key}` : key; // Create a new key
39
+ const value = obj[key];
40
+ if (
41
+ typeof value === 'object' &&
42
+ value !== null &&
43
+ !Array.isArray(value)
44
+ ) {
45
+ Object.assign(flattened, flattenObject(value, newKey));
46
+ } else {
47
+ flattened[newKey] = value;
48
+ }
49
+ }
50
+ }
51
+ return flattened;
52
+ };
53
+
54
+ const standardizeObj = (obj) => {
55
+ let standardObj = obj;
56
+ standardObj = keysToUpperCase(standardObj);
57
+ standardObj = flattenObject(standardObj);
58
+ return standardObj;
59
+ };
60
+
61
+ export { loadConfigFile, standardizeObj };
@@ -0,0 +1,43 @@
1
+ import { env } from 'tej-env';
2
+ import ansi from 'ansi-colors';
3
+ import TejLogger from 'tej-logger';
4
+
5
+ const logger = new TejLogger('Tejas.Request');
6
+ const { italic, bold, blue, white, bgGreen, bgRed, whiteBright } = ansi;
7
+
8
+ function logHttpRequest(ammo, next) {
9
+ if (!env('LOG_HTTP_REQUESTS')) return;
10
+
11
+ const startTime = new Date();
12
+ ammo.res.on('finish', () => {
13
+ const res = ammo.res;
14
+ const method = italic(whiteBright(ammo.method));
15
+ const endpoint = bold(ammo.endpoint.split('?')[0].replace(/\/$/, ''));
16
+ const statusCode =
17
+ res.statusCode >= 400
18
+ ? bgRed(whiteBright(bold(`✖ ${res.statusCode}`)))
19
+ : bgGreen(whiteBright(bold(`✔ ${res.statusCode}`)));
20
+
21
+ const duration = white(`${new Date() - startTime}ms`);
22
+ const payload = `${blue('Request')}: ${white(
23
+ JSON.stringify(ammo.payload),
24
+ )}`;
25
+ const dispatchedData = `${blue('Response')}: ${white(ammo.dispatchedData)}`;
26
+ const nextLine = '\n';
27
+
28
+ logger.log(
29
+ italic(`Incoming request from ${ammo.ip}`),
30
+ nextLine,
31
+ method,
32
+ endpoint,
33
+ statusCode,
34
+ duration,
35
+ nextLine,
36
+ payload,
37
+ nextLine,
38
+ dispatchedData,
39
+ );
40
+ });
41
+ }
42
+
43
+ export default logHttpRequest;
@@ -0,0 +1,82 @@
1
+ const statuses = {
2
+ '100': 'Continue',
3
+ '101': 'Switching Protocols',
4
+ '102': 'Processing',
5
+ '103': 'Early Hints',
6
+ '200': 'OK',
7
+ '201': 'Created',
8
+ '202': 'Accepted',
9
+ '203': 'Non-Authoritative Information',
10
+ '204': 'No Content',
11
+ '205': 'Reset Content',
12
+ '206': 'Partial Content',
13
+ '207': 'Multi-Status',
14
+ '208': 'Already Reported',
15
+ '226': 'IM Used',
16
+ '300': 'Multiple Choices',
17
+ '301': 'Moved Permanently',
18
+ '302': 'Found',
19
+ '303': 'See Other',
20
+ '304': 'Not Modified',
21
+ '305': 'Use Proxy',
22
+ '307': 'Temporary Redirect',
23
+ '308': 'Permanent Redirect',
24
+ '400': 'Bad Request',
25
+ '401': 'Unauthorized',
26
+ '402': 'Payment Required',
27
+ '403': 'Forbidden',
28
+ '404': 'Not Found',
29
+ '405': 'Method Not Allowed',
30
+ '406': 'Not Acceptable',
31
+ '407': 'Proxy Authentication Required',
32
+ '408': 'Request Timeout',
33
+ '409': 'Conflict',
34
+ '410': 'Gone',
35
+ '411': 'Length Required',
36
+ '412': 'Precondition Failed',
37
+ '413': 'Payload Too Large',
38
+ '414': 'URI Too Long',
39
+ '415': 'Unsupported Media Type',
40
+ '416': 'Range Not Satisfiable',
41
+ '417': 'Expectation Failed',
42
+ '418': 'I\'m a Teapot',
43
+ '421': 'Misdirected Request',
44
+ '422': 'Unprocessable Entity',
45
+ '423': 'Locked',
46
+ '424': 'Failed Dependency',
47
+ '425': 'Too Early',
48
+ '426': 'Upgrade Required',
49
+ '428': 'Precondition Required',
50
+ '429': 'Too Many Requests',
51
+ '431': 'Request Header Fields Too Large',
52
+ '451': 'Unavailable For Legal Reasons',
53
+ '500': 'Internal Server Error',
54
+ '501': 'Not Implemented',
55
+ '502': 'Bad Gateway',
56
+ '503': 'Service Unavailable',
57
+ '504': 'Gateway Timeout',
58
+ '505': 'HTTP Version Not Supported',
59
+ '506': 'Variant Also Negotiates',
60
+ '507': 'Insufficient Storage',
61
+ '508': 'Loop Detected',
62
+ '509': 'Bandwidth Limit Exceeded',
63
+ '510': 'Not Extended',
64
+ '511': 'Network Authentication Required',
65
+ };
66
+
67
+ const reverseStatuses = Object.fromEntries(
68
+ Object.entries(statuses).map(([key, value]) => [value, key]));
69
+
70
+ const toStatusCode = (message) => {
71
+ return reverseStatuses[message];
72
+ };
73
+
74
+ const toStatusMessage = (code) => {
75
+ return statuses[code];
76
+ };
77
+
78
+ const isStatusCode = (code) => {
79
+ return statuses[code] !== undefined;
80
+ }
81
+
82
+ export {toStatusCode, toStatusMessage, isStatusCode};
@@ -0,0 +1,18 @@
1
+ // TODO Create a better entrypoint html
2
+
3
+ const html =
4
+ "" +
5
+ "<!DOCTYPE html>" +
6
+ '<html lang="en">' +
7
+ "<head>" +
8
+ '<meta charset="UTF-8">' +
9
+ '<meta http-equiv="X-UA-Compatible" content="IE=edge">' +
10
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0">' +
11
+ "<title>te.js</title>" +
12
+ "</head>" +
13
+ "<body>" +
14
+ "<h1>Tejas is flying</h1>" +
15
+ "</body>" +
16
+ "</html>";
17
+
18
+ export default html;