@noxfly/noxus
Version:
Simulate lightweight HTTP-like requests between renderer and main process in Electron applications with MessagePort, with structured and modular design.
1,291 lines (1,273 loc) • 46.7 kB
JavaScript
/**
* @copyright 2025 NoxFly
* @license MIT
* @author NoxFly
*/
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/DI/app-injector.ts
import "reflect-metadata";
// src/exceptions.ts
var _ResponseException = class _ResponseException extends Error {
constructor(statusOrMessage, message) {
let statusCode;
if (typeof statusOrMessage === "number") {
statusCode = statusOrMessage;
} else if (typeof statusOrMessage === "string") {
message = statusOrMessage;
}
super(message ?? "");
__publicField(this, "status", 0);
if (statusCode !== void 0) {
this.status = statusCode;
}
this.name = this.constructor.name.replace(/([A-Z])/g, " $1");
}
};
__name(_ResponseException, "ResponseException");
var ResponseException = _ResponseException;
var _BadRequestException = class _BadRequestException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 400);
}
};
__name(_BadRequestException, "BadRequestException");
var BadRequestException = _BadRequestException;
var _UnauthorizedException = class _UnauthorizedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 401);
}
};
__name(_UnauthorizedException, "UnauthorizedException");
var UnauthorizedException = _UnauthorizedException;
var _PaymentRequiredException = class _PaymentRequiredException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 402);
}
};
__name(_PaymentRequiredException, "PaymentRequiredException");
var PaymentRequiredException = _PaymentRequiredException;
var _ForbiddenException = class _ForbiddenException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 403);
}
};
__name(_ForbiddenException, "ForbiddenException");
var ForbiddenException = _ForbiddenException;
var _NotFoundException = class _NotFoundException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 404);
}
};
__name(_NotFoundException, "NotFoundException");
var NotFoundException = _NotFoundException;
var _MethodNotAllowedException = class _MethodNotAllowedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 405);
}
};
__name(_MethodNotAllowedException, "MethodNotAllowedException");
var MethodNotAllowedException = _MethodNotAllowedException;
var _NotAcceptableException = class _NotAcceptableException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 406);
}
};
__name(_NotAcceptableException, "NotAcceptableException");
var NotAcceptableException = _NotAcceptableException;
var _RequestTimeoutException = class _RequestTimeoutException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 408);
}
};
__name(_RequestTimeoutException, "RequestTimeoutException");
var RequestTimeoutException = _RequestTimeoutException;
var _ConflictException = class _ConflictException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 409);
}
};
__name(_ConflictException, "ConflictException");
var ConflictException = _ConflictException;
var _UpgradeRequiredException = class _UpgradeRequiredException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 426);
}
};
__name(_UpgradeRequiredException, "UpgradeRequiredException");
var UpgradeRequiredException = _UpgradeRequiredException;
var _TooManyRequestsException = class _TooManyRequestsException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 429);
}
};
__name(_TooManyRequestsException, "TooManyRequestsException");
var TooManyRequestsException = _TooManyRequestsException;
var _InternalServerException = class _InternalServerException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 500);
}
};
__name(_InternalServerException, "InternalServerException");
var InternalServerException = _InternalServerException;
var _NotImplementedException = class _NotImplementedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 501);
}
};
__name(_NotImplementedException, "NotImplementedException");
var NotImplementedException = _NotImplementedException;
var _BadGatewayException = class _BadGatewayException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 502);
}
};
__name(_BadGatewayException, "BadGatewayException");
var BadGatewayException = _BadGatewayException;
var _ServiceUnavailableException = class _ServiceUnavailableException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 503);
}
};
__name(_ServiceUnavailableException, "ServiceUnavailableException");
var ServiceUnavailableException = _ServiceUnavailableException;
var _GatewayTimeoutException = class _GatewayTimeoutException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 504);
}
};
__name(_GatewayTimeoutException, "GatewayTimeoutException");
var GatewayTimeoutException = _GatewayTimeoutException;
var _HttpVersionNotSupportedException = class _HttpVersionNotSupportedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 505);
}
};
__name(_HttpVersionNotSupportedException, "HttpVersionNotSupportedException");
var HttpVersionNotSupportedException = _HttpVersionNotSupportedException;
var _VariantAlsoNegotiatesException = class _VariantAlsoNegotiatesException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 506);
}
};
__name(_VariantAlsoNegotiatesException, "VariantAlsoNegotiatesException");
var VariantAlsoNegotiatesException = _VariantAlsoNegotiatesException;
var _InsufficientStorageException = class _InsufficientStorageException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 507);
}
};
__name(_InsufficientStorageException, "InsufficientStorageException");
var InsufficientStorageException = _InsufficientStorageException;
var _LoopDetectedException = class _LoopDetectedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 508);
}
};
__name(_LoopDetectedException, "LoopDetectedException");
var LoopDetectedException = _LoopDetectedException;
var _NotExtendedException = class _NotExtendedException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 510);
}
};
__name(_NotExtendedException, "NotExtendedException");
var NotExtendedException = _NotExtendedException;
var _NetworkAuthenticationRequiredException = class _NetworkAuthenticationRequiredException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 511);
}
};
__name(_NetworkAuthenticationRequiredException, "NetworkAuthenticationRequiredException");
var NetworkAuthenticationRequiredException = _NetworkAuthenticationRequiredException;
var _NetworkConnectTimeoutException = class _NetworkConnectTimeoutException extends ResponseException {
constructor() {
super(...arguments);
__publicField(this, "status", 599);
}
};
__name(_NetworkConnectTimeoutException, "NetworkConnectTimeoutException");
var NetworkConnectTimeoutException = _NetworkConnectTimeoutException;
// src/DI/app-injector.ts
var _AppInjector = class _AppInjector {
constructor(name = null) {
__publicField(this, "name");
__publicField(this, "bindings", /* @__PURE__ */ new Map());
__publicField(this, "singletons", /* @__PURE__ */ new Map());
__publicField(this, "scoped", /* @__PURE__ */ new Map());
this.name = name;
}
/**
* Typically used to create a dependency injection scope
* at the "scope" level (i.e., per-request lifetime).
*
* SHOULD NOT BE USED by anything else than the framework itself.
*/
createScope() {
const scope = new _AppInjector();
scope.bindings = this.bindings;
scope.singletons = this.singletons;
return scope;
}
/**
* Called when resolving a dependency,
* i.e., retrieving the instance of a given class.
*/
resolve(target) {
const binding = this.bindings.get(target);
if (!binding) throw new InternalServerException(`Failed to resolve a dependency injection : No binding for type ${target.name}.
Did you forget to use @Injectable() decorator ?`);
switch (binding.lifetime) {
case "transient":
return this.instantiate(binding.implementation);
case "scope": {
if (this.scoped.has(target)) {
return this.scoped.get(target);
}
const instance = this.instantiate(binding.implementation);
this.scoped.set(target, instance);
return instance;
}
case "singleton": {
if (binding.instance === void 0 && this.name === "root") {
binding.instance = this.instantiate(binding.implementation);
this.singletons.set(target, binding.instance);
}
return binding.instance;
}
}
}
/**
*
*/
instantiate(target) {
const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
const params = paramTypes.map((p) => this.resolve(p));
return new target(...params);
}
};
__name(_AppInjector, "AppInjector");
var AppInjector = _AppInjector;
function inject(t) {
return RootInjector.resolve(t);
}
__name(inject, "inject");
var RootInjector = new AppInjector("root");
// src/router.ts
import "reflect-metadata";
// src/decorators/guards.decorator.ts
function Authorize(...guardClasses) {
return (target, propertyKey) => {
let key;
if (propertyKey) {
const ctrlName = target.constructor.name;
const actionName = propertyKey;
key = `${ctrlName}.${actionName}`;
} else {
const ctrlName = target.name;
key = `${ctrlName}`;
}
if (authorizations.has(key)) {
throw new Error(`Guard(s) already registered for ${key}`);
}
authorizations.set(key, guardClasses);
};
}
__name(Authorize, "Authorize");
function getGuardForController(controllerName) {
const key = `${controllerName}`;
return authorizations.get(key) ?? [];
}
__name(getGuardForController, "getGuardForController");
function getGuardForControllerAction(controllerName, actionName) {
const key = `${controllerName}.${actionName}`;
return authorizations.get(key) ?? [];
}
__name(getGuardForControllerAction, "getGuardForControllerAction");
var authorizations = /* @__PURE__ */ new Map();
// src/decorators/method.decorator.ts
function createRouteDecorator(verb) {
return (path) => {
return (target, propertyKey) => {
const existingRoutes = Reflect.getMetadata(ROUTE_METADATA_KEY, target.constructor) || [];
const metadata = {
method: verb,
path: path.trim().replace(/^\/|\/$/g, ""),
handler: propertyKey,
guards: getGuardForControllerAction(target.constructor.__controllerName, propertyKey)
};
existingRoutes.push(metadata);
Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target.constructor);
};
};
}
__name(createRouteDecorator, "createRouteDecorator");
function getRouteMetadata(target) {
return Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
}
__name(getRouteMetadata, "getRouteMetadata");
var Get = createRouteDecorator("GET");
var Post = createRouteDecorator("POST");
var Put = createRouteDecorator("PUT");
var Patch = createRouteDecorator("PATCH");
var Delete = createRouteDecorator("DELETE");
var ROUTE_METADATA_KEY = Symbol("ROUTE_METADATA_KEY");
// src/decorators/module.decorator.ts
function Module(metadata) {
return (target) => {
const checkModule = /* @__PURE__ */ __name((arr, arrName) => {
if (!arr) return;
for (const clazz of arr) {
if (!Reflect.getMetadata(MODULE_METADATA_KEY, clazz)) {
throw new Error(`Class ${clazz.name} in ${arrName} must be decorated with @Module`);
}
}
}, "checkModule");
const checkInjectable = /* @__PURE__ */ __name((arr) => {
if (!arr) return;
for (const clazz of arr) {
if (!Reflect.getMetadata(INJECTABLE_METADATA_KEY, clazz)) {
throw new Error(`Class ${clazz.name} in providers must be decorated with @Injectable`);
}
}
}, "checkInjectable");
const checkController = /* @__PURE__ */ __name((arr) => {
if (!arr) return;
for (const clazz of arr) {
if (!Reflect.getMetadata(CONTROLLER_METADATA_KEY, clazz)) {
throw new Error(`Class ${clazz.name} in controllers must be decorated with @Controller`);
}
}
}, "checkController");
checkModule(metadata.imports, "imports");
checkModule(metadata.exports, "exports");
checkInjectable(metadata.providers);
checkController(metadata.controllers);
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, target);
Injectable("singleton")(target);
};
}
__name(Module, "Module");
function getModuleMetadata(target) {
return Reflect.getMetadata(MODULE_METADATA_KEY, target);
}
__name(getModuleMetadata, "getModuleMetadata");
var MODULE_METADATA_KEY = Symbol("MODULE_METADATA_KEY");
// src/utils/logger.ts
function getPrettyTimestamp() {
const now = /* @__PURE__ */ new Date();
return `${now.getDate().toString().padStart(2, "0")}/${(now.getMonth() + 1).toString().padStart(2, "0")}/${now.getFullYear()} ${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}`;
}
__name(getPrettyTimestamp, "getPrettyTimestamp");
function getLogPrefix(callee, messageType, color) {
const timestamp = getPrettyTimestamp();
const spaces = " ".repeat(10 - messageType.length);
return `${color}[APP] ${process.pid} - ${Logger.colors.initial}${timestamp}${spaces}${color}${messageType.toUpperCase()}${Logger.colors.initial} ${Logger.colors.yellow}[${callee}]${Logger.colors.initial}`;
}
__name(getLogPrefix, "getLogPrefix");
function formatObject(prefix, arg) {
const json = JSON.stringify(arg, null, 2);
const prefixedJson = json.split("\n").map((line, idx) => idx === 0 ? `${Logger.colors.darkGrey}${line}` : `${prefix} ${Logger.colors.grey}${line}`).join("\n") + Logger.colors.initial;
return prefixedJson;
}
__name(formatObject, "formatObject");
function formattedArgs(prefix, args, color) {
return args.map((arg) => {
if (typeof arg === "string") {
return `${color}${arg}${Logger.colors.initial}`;
} else if (typeof arg === "object") {
return formatObject(prefix, arg);
}
return arg;
});
}
__name(formattedArgs, "formattedArgs");
function getCallee() {
const stack = new Error().stack?.split("\n") ?? [];
const caller = stack[3]?.trim().match(/at (.+?)(?:\..+)? .+$/)?.[1]?.replace("Object", "").replace(/^_/, "") || "App";
return caller;
}
__name(getCallee, "getCallee");
function canLog(level) {
return logLevelRank[level] >= logLevelRank[logLevel];
}
__name(canLog, "canLog");
var logLevel = "debug";
var logLevelRank = {
debug: 0,
comment: 1,
log: 2,
info: 3,
warn: 4,
error: 5
};
(function(Logger2) {
function setLogLevel(level) {
logLevel = level;
}
__name(setLogLevel, "setLogLevel");
Logger2.setLogLevel = setLogLevel;
function log(...args) {
if (!canLog("log")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "log", Logger2.colors.green);
console.log(prefix, ...formattedArgs(prefix, args, Logger2.colors.green));
}
__name(log, "log");
Logger2.log = log;
function info(...args) {
if (!canLog("info")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "info", Logger2.colors.blue);
console.info(prefix, ...formattedArgs(prefix, args, Logger2.colors.blue));
}
__name(info, "info");
Logger2.info = info;
function warn(...args) {
if (!canLog("warn")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "warn", Logger2.colors.brown);
console.warn(prefix, ...formattedArgs(prefix, args, Logger2.colors.brown));
}
__name(warn, "warn");
Logger2.warn = warn;
function error(...args) {
if (!canLog("error")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "error", Logger2.colors.red);
console.error(prefix, ...formattedArgs(prefix, args, Logger2.colors.red));
}
__name(error, "error");
Logger2.error = error;
function errorStack(...args) {
if (!canLog("error")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "error", Logger2.colors.grey);
console.error(prefix, ...formattedArgs(prefix, args, Logger2.colors.grey));
}
__name(errorStack, "errorStack");
Logger2.errorStack = errorStack;
function debug(...args) {
if (!canLog("debug")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "debug", Logger2.colors.purple);
console.debug(prefix, ...formattedArgs(prefix, args, Logger2.colors.purple));
}
__name(debug, "debug");
Logger2.debug = debug;
function comment(...args) {
if (!canLog("comment")) return;
const callee = getCallee();
const prefix = getLogPrefix(callee, "comment", Logger2.colors.grey);
console.debug(prefix, ...formattedArgs(prefix, args, Logger2.colors.grey));
}
__name(comment, "comment");
Logger2.comment = comment;
Logger2.colors = {
black: "\x1B[0;30m",
grey: "\x1B[0;37m",
red: "\x1B[0;31m",
green: "\x1B[0;32m",
brown: "\x1B[0;33m",
blue: "\x1B[0;34m",
purple: "\x1B[0;35m",
darkGrey: "\x1B[1;30m",
lightRed: "\x1B[1;31m",
lightGreen: "\x1B[1;32m",
yellow: "\x1B[1;33m",
lightBlue: "\x1B[1;34m",
magenta: "\x1B[1;35m",
cyan: "\x1B[1;36m",
white: "\x1B[1;37m",
initial: "\x1B[0m"
};
})(Logger || (Logger = {}));
var Logger;
// src/DI/injector-explorer.ts
var _InjectorExplorer = class _InjectorExplorer {
/**
* Registers the class as injectable.
* When a class is instantiated, if it has dependencies and those dependencies
* are listed using this method, they will be injected into the class constructor.
*/
static register(target, lifetime) {
if (RootInjector.bindings.has(target)) return RootInjector;
RootInjector.bindings.set(target, {
implementation: target,
lifetime
});
if (lifetime === "singleton") {
RootInjector.resolve(target);
}
if (getModuleMetadata(target)) {
Logger.log(`${target.name} dependencies initialized`);
return RootInjector;
}
const controllerMeta = getControllerMetadata(target);
if (controllerMeta) {
const router = RootInjector.resolve(Router);
router?.registerController(target);
return RootInjector;
}
const routeMeta = getRouteMetadata(target);
if (routeMeta) {
return RootInjector;
}
if (getInjectableMetadata(target)) {
Logger.log(`Registered ${target.name} as ${lifetime}`);
return RootInjector;
}
return RootInjector;
}
};
__name(_InjectorExplorer, "InjectorExplorer");
var InjectorExplorer = _InjectorExplorer;
// src/decorators/injectable.decorator.ts
function Injectable(lifetime = "scope") {
return (target) => {
if (typeof target !== "function" || !target.prototype) {
throw new Error(`@Injectable can only be used on classes, not on ${typeof target}`);
}
Reflect.defineMetadata(INJECTABLE_METADATA_KEY, lifetime, target);
InjectorExplorer.register(target, lifetime);
};
}
__name(Injectable, "Injectable");
function getInjectableMetadata(target) {
return Reflect.getMetadata(INJECTABLE_METADATA_KEY, target);
}
__name(getInjectableMetadata, "getInjectableMetadata");
var INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_METADATA_KEY");
// src/decorators/controller.decorator.ts
function Controller(path) {
return (target) => {
const data = {
path,
guards: getGuardForController(target.name)
};
Reflect.defineMetadata(CONTROLLER_METADATA_KEY, data, target);
Injectable("scope")(target);
};
}
__name(Controller, "Controller");
function getControllerMetadata(target) {
return Reflect.getMetadata(CONTROLLER_METADATA_KEY, target);
}
__name(getControllerMetadata, "getControllerMetadata");
var CONTROLLER_METADATA_KEY = Symbol("CONTROLLER_METADATA_KEY");
// src/decorators/middleware.decorator.ts
function UseMiddlewares(mdlw) {
return (target, propertyKey) => {
let key;
if (propertyKey) {
const ctrlName = target.constructor.name;
const actionName = propertyKey;
key = `${ctrlName}.${actionName}`;
} else {
const ctrlName = target.name;
key = `${ctrlName}`;
}
if (middlewares.has(key)) {
throw new Error(`Middlewares(s) already registered for ${key}`);
}
middlewares.set(key, mdlw);
};
}
__name(UseMiddlewares, "UseMiddlewares");
function getMiddlewaresForController(controllerName) {
const key = `${controllerName}`;
return middlewares.get(key) ?? [];
}
__name(getMiddlewaresForController, "getMiddlewaresForController");
function getMiddlewaresForControllerAction(controllerName, actionName) {
const key = `${controllerName}.${actionName}`;
return middlewares.get(key) ?? [];
}
__name(getMiddlewaresForControllerAction, "getMiddlewaresForControllerAction");
var middlewares = /* @__PURE__ */ new Map();
// src/utils/radix-tree.ts
var _a;
var RadixNode = (_a = class {
/**
* Creates a new RadixNode.
* @param segment - The segment of the path this node represents.
*/
constructor(segment) {
__publicField(this, "segment");
__publicField(this, "children", []);
__publicField(this, "value");
__publicField(this, "isParam");
__publicField(this, "paramName");
this.segment = segment;
this.isParam = segment.startsWith(":");
if (this.isParam) {
this.paramName = segment.slice(1);
}
}
/**
* Matches a child node against a given segment.
* This method checks if the segment matches any of the children nodes.
* @param segment - The segment to match against the children of this node.
* @returns A child node that matches the segment, or undefined if no match is found.
*/
matchChild(segment) {
for (const child of this.children) {
if (child.isParam || segment.startsWith(child.segment)) return child;
}
return void 0;
}
/**
* Finds a child node that matches the segment exactly.
* This method checks if there is a child node that matches the segment exactly.
* @param segment - The segment to find an exact match for among the children of this node.
* @returns A child node that matches the segment exactly, or undefined if no match is found.
*/
findExactChild(segment) {
return this.children.find((c) => c.segment === segment);
}
/**
* Adds a child node to this node's children.
* This method adds a new child node to the list of children for this node.
* @param node - The child node to add to this node's children.
*/
addChild(node) {
this.children.push(node);
}
}, __name(_a, "RadixNode"), _a);
var _RadixTree = class _RadixTree {
constructor() {
__publicField(this, "root", new RadixNode(""));
}
/**
* Inserts a path and its associated value into the Radix Tree.
* This method normalizes the path and inserts it into the tree, associating it with
* @param path - The path to insert into the tree.
* @param value - The value to associate with the path.
*/
insert(path, value) {
const segments = this.normalize(path);
this.insertRecursive(this.root, segments, value);
}
/**
* Recursively inserts a path into the Radix Tree.
* This method traverses the tree and inserts the segments of the path, creating new nodes
* @param node - The node to start inserting from.
* @param segments - The segments of the path to insert.
* @param value - The value to associate with the path.
*/
insertRecursive(node, segments, value) {
if (segments.length === 0) {
node.value = value;
return;
}
const segment = segments[0] ?? "";
let child = node.children.find((c) => c.isParam === segment.startsWith(":") && (c.isParam || c.segment === segment));
if (!child) {
child = new RadixNode(segment);
node.addChild(child);
}
this.insertRecursive(child, segments.slice(1), value);
}
/**
* Searches for a path in the Radix Tree.
* This method normalizes the path and searches for it in the tree, returning the node
* @param path - The path to search for in the Radix Tree.
* @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
*/
search(path) {
const segments = this.normalize(path);
return this.searchRecursive(this.root, segments, {});
}
/**
* Recursively searches for a path in the Radix Tree.
* This method traverses the tree and searches for the segments of the path, collecting parameters
* @param node - The node to start searching from.
* @param segments - The segments of the path to search for.
* @param params - The parameters collected during the search.
* @returns An ISearchResult containing the node and parameters if a match is found, otherwise undefined.
*/
searchRecursive(node, segments, params) {
if (segments.length === 0) {
if (node.value !== void 0) {
return {
node,
params
};
}
return void 0;
}
const [segment, ...rest] = segments;
for (const child of node.children) {
if (child.isParam) {
const paramName = child.paramName;
const childParams = {
...params,
[paramName]: segment ?? ""
};
if (rest.length === 0) {
return {
node: child,
params: childParams
};
}
const result = this.searchRecursive(child, rest, childParams);
if (result) return result;
} else if (segment === child.segment) {
if (rest.length === 0) {
return {
node: child,
params
};
}
const result = this.searchRecursive(child, rest, params);
if (result) return result;
}
}
return void 0;
}
/**
* Normalizes a path into an array of segments.
* This method removes leading and trailing slashes, splits the path by slashes, and
* @param path - The path to normalize.
* @returns An array of normalized path segments.
*/
normalize(path) {
const segments = path.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
return [
"",
...segments
];
}
};
__name(_RadixTree, "RadixTree");
var RadixTree = _RadixTree;
// src/router.ts
function _ts_decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
__name(_ts_decorate, "_ts_decorate");
var _Router = class _Router {
constructor() {
__publicField(this, "routes", new RadixTree());
__publicField(this, "rootMiddlewares", []);
}
/**
* Registers a controller class with the router.
* This method extracts the route metadata from the controller class and registers it in the routing tree.
* It also handles the guards and middlewares associated with the controller.
* @param controllerClass - The controller class to register.
*/
registerController(controllerClass) {
const controllerMeta = getControllerMetadata(controllerClass);
const controllerGuards = getGuardForController(controllerClass.name);
const controllerMiddlewares = getMiddlewaresForController(controllerClass.name);
if (!controllerMeta) throw new Error(`Missing @Controller decorator on ${controllerClass.name}`);
const routeMetadata = getRouteMetadata(controllerClass);
for (const def of routeMetadata) {
const fullPath = `${controllerMeta.path}/${def.path}`.replace(/\/+/g, "/");
const routeGuards = getGuardForControllerAction(controllerClass.name, def.handler);
const routeMiddlewares = getMiddlewaresForControllerAction(controllerClass.name, def.handler);
const guards = /* @__PURE__ */ new Set([
...controllerGuards,
...routeGuards
]);
const middlewares2 = /* @__PURE__ */ new Set([
...controllerMiddlewares,
...routeMiddlewares
]);
const routeDef = {
method: def.method,
path: fullPath,
controller: controllerClass,
handler: def.handler,
guards: [
...guards
],
middlewares: [
...middlewares2
]
};
this.routes.insert(fullPath + "/" + def.method, routeDef);
const hasActionGuards = routeDef.guards.length > 0;
const actionGuardsInfo = hasActionGuards ? "<" + routeDef.guards.map((g) => g.name).join("|") + ">" : "";
Logger.log(`Mapped {${routeDef.method} /${fullPath}}${actionGuardsInfo} route`);
}
const hasCtrlGuards = controllerMeta.guards.length > 0;
const controllerGuardsInfo = hasCtrlGuards ? "<" + controllerMeta.guards.map((g) => g.name).join("|") + ">" : "";
Logger.log(`Mapped ${controllerClass.name}${controllerGuardsInfo} controller's routes`);
return this;
}
/**
* Defines a middleware for the root of the application.
* This method allows you to register a middleware that will be applied to all requests
* to the application, regardless of the controller or action.
* @param middleware - The middleware class to register.
*/
defineRootMiddleware(middleware) {
this.rootMiddlewares.push(middleware);
return this;
}
/**
* Shuts down the message channel for a specific sender ID.
* This method closes the IPC channel for the specified sender ID and
* removes it from the messagePorts map.
* @param channelSenderId - The ID of the sender channel to shut down.
*/
async handle(request) {
Logger.comment(`> ${request.method} /${request.path}`);
const t0 = performance.now();
const response = {
requestId: request.id,
status: 200,
body: null
};
try {
const routeDef = this.findRoute(request);
await this.resolveController(request, response, routeDef);
if (response.status > 400) {
throw new ResponseException(response.status, response.error);
}
} catch (error) {
response.body = void 0;
if (error instanceof ResponseException) {
response.status = error.status;
response.error = error.message;
response.stack = error.stack;
} else if (error instanceof Error) {
response.status = 500;
response.error = error.message || "Internal Server Error";
response.stack = error.stack || "No stack trace available";
} else {
response.status = 500;
response.error = "Unknown error occurred";
response.stack = "No stack trace available";
}
} finally {
const t1 = performance.now();
const message = `< ${response.status} ${request.method} /${request.path} ${Logger.colors.yellow}${Math.round(t1 - t0)}ms${Logger.colors.initial}`;
if (response.status < 400) Logger.log(message);
else if (response.status < 500) Logger.warn(message);
else Logger.error(message);
if (response.error !== void 0) {
Logger.error(response.error);
if (response.stack !== void 0) {
Logger.errorStack(response.stack);
}
}
return response;
}
}
/**
* Finds the route definition for a given request.
* This method searches the routing tree for a matching route based on the request's path and method.
* If no matching route is found, it throws a NotFoundException.
* @param request - The Request object containing the method and path to search for.
* @returns The IRouteDefinition for the matched route.
*/
findRoute(request) {
const matchedRoutes = this.routes.search(request.path);
if (matchedRoutes?.node === void 0 || matchedRoutes.node.children.length === 0) {
throw new NotFoundException(`No route matches ${request.method} ${request.path}`);
}
const routeDef = matchedRoutes.node.findExactChild(request.method);
if (routeDef?.value === void 0) {
throw new MethodNotAllowedException(`Method Not Allowed for ${request.method} ${request.path}`);
}
return routeDef.value;
}
/**
* Resolves the controller for a given route definition.
* This method creates an instance of the controller class and prepares the request parameters.
* It also runs the request pipeline, which includes executing middlewares and guards.
* @param request - The Request object containing the request data.
* @param response - The IResponse object to populate with the response data.
* @param routeDef - The IRouteDefinition for the matched route.
* @return A Promise that resolves when the controller action has been executed.
* @throws UnauthorizedException if the request is not authorized by the guards.
*/
async resolveController(request, response, routeDef) {
const controllerInstance = request.context.resolve(routeDef.controller);
Object.assign(request.params, this.extractParams(request.path, routeDef.path));
await this.runRequestPipeline(request, response, routeDef, controllerInstance);
}
/**
* Runs the request pipeline for a given request.
* This method executes the middlewares and guards associated with the route,
* and finally calls the controller action.
* @param request - The Request object containing the request data.
* @param response - The IResponse object to populate with the response data.
* @param routeDef - The IRouteDefinition for the matched route.
* @param controllerInstance - The instance of the controller class.
* @return A Promise that resolves when the request pipeline has been executed.
* @throws ResponseException if the response status is not successful.
*/
async runRequestPipeline(request, response, routeDef, controllerInstance) {
const middlewares2 = [
.../* @__PURE__ */ new Set([
...this.rootMiddlewares,
...routeDef.middlewares
])
];
const middlewareMaxIndex = middlewares2.length - 1;
const guardsMaxIndex = middlewareMaxIndex + routeDef.guards.length;
let index = -1;
const dispatch = /* @__PURE__ */ __name(async (i) => {
if (i <= index) throw new Error("next() called multiple times");
index = i;
if (i <= middlewareMaxIndex) {
const nextFn = dispatch.bind(null, i + 1);
await this.runMiddleware(request, response, nextFn, middlewares2[i]);
if (response.status >= 400) {
throw new ResponseException(response.status, response.error);
}
return;
}
if (i <= guardsMaxIndex) {
const guardIndex = i - middlewares2.length;
const guardType = routeDef.guards[guardIndex];
await this.runGuard(request, guardType);
await dispatch(i + 1);
return;
}
const action = controllerInstance[routeDef.handler];
response.body = await action.call(controllerInstance, request, response);
if (response.body === void 0) {
response.body = {};
}
}, "dispatch");
await dispatch(0);
}
/**
* Runs a middleware function in the request pipeline.
* This method creates an instance of the middleware and invokes its `invoke` method,
* passing the request, response, and next function.
* @param request - The Request object containing the request data.
* @param response - The IResponse object to populate with the response data.
* @param next - The NextFunction to call to continue the middleware chain.
* @param middlewareType - The type of the middleware to run.
* @return A Promise that resolves when the middleware has been executed.
*/
async runMiddleware(request, response, next, middlewareType) {
const middleware = request.context.resolve(middlewareType);
await middleware.invoke(request, response, next);
}
/**
* Runs a guard to check if the request is authorized.
* This method creates an instance of the guard and calls its `canActivate` method.
* If the guard returns false, it throws an UnauthorizedException.
* @param request - The Request object containing the request data.
* @param guardType - The type of the guard to run.
* @return A Promise that resolves if the guard allows the request, or throws an UnauthorizedException if not.
* @throws UnauthorizedException if the guard denies access to the request.
*/
async runGuard(request, guardType) {
const guard = request.context.resolve(guardType);
const allowed = await guard.canActivate(request);
if (!allowed) throw new UnauthorizedException(`Unauthorized for ${request.method} ${request.path}`);
}
/**
* Extracts parameters from the actual request path based on the template path.
* This method splits the actual path and the template path into segments,
* then maps the segments to parameters based on the template.
* @param actual - The actual request path.
* @param template - The template path to extract parameters from.
* @returns An object containing the extracted parameters.
*/
extractParams(actual, template) {
const aParts = actual.split("/");
const tParts = template.split("/");
const params = {};
tParts.forEach((part, i) => {
if (part.startsWith(":")) {
params[part.slice(1)] = aParts[i] ?? "";
}
});
return params;
}
};
__name(_Router, "Router");
var Router = _Router;
Router = _ts_decorate([
Injectable("singleton")
], Router);
// src/app.ts
import { app, BrowserWindow, ipcMain, MessageChannelMain } from "electron/main";
// src/request.ts
import "reflect-metadata";
var _Request = class _Request {
constructor(event, id, method, path, body) {
__publicField(this, "event");
__publicField(this, "id");
__publicField(this, "method");
__publicField(this, "path");
__publicField(this, "body");
__publicField(this, "context", RootInjector.createScope());
__publicField(this, "params", {});
this.event = event;
this.id = id;
this.method = method;
this.path = path;
this.body = body;
this.path = path.replace(/^\/|\/$/g, "");
}
};
__name(_Request, "Request");
var Request = _Request;
// src/app.ts
function _ts_decorate2(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
__name(_ts_decorate2, "_ts_decorate");
function _ts_metadata(k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
}
__name(_ts_metadata, "_ts_metadata");
var _NoxApp = class _NoxApp {
constructor(router) {
__publicField(this, "router");
__publicField(this, "messagePorts", /* @__PURE__ */ new Map());
__publicField(this, "app");
this.router = router;
}
/**
* Initializes the NoxApp instance.
* This method sets up the IPC communication, registers event listeners,
* and prepares the application for use.
*/
async init() {
ipcMain.on("gimme-my-port", this.giveTheRendererAPort.bind(this));
app.once("activate", this.onAppActivated.bind(this));
app.once("window-all-closed", this.onAllWindowsClosed.bind(this));
console.log("");
return this;
}
/**
* Handles the request from the renderer process.
* This method creates a Request object from the IPC event data,
* processes it through the Router, and sends the response back
* to the renderer process using the MessageChannel.
*/
giveTheRendererAPort(event) {
const senderId = event.sender.id;
if (this.messagePorts.has(senderId)) {
this.shutdownChannel(senderId);
}
const channel = new MessageChannelMain();
this.messagePorts.set(senderId, channel);
channel.port1.on("message", this.onRendererMessage.bind(this));
channel.port1.start();
event.sender.postMessage("port", {
senderId
}, [
channel.port2
]);
}
/**
* Electron specific message handling.
* Replaces HTTP calls by using Electron's IPC mechanism.
*/
async onRendererMessage(event) {
const { senderId, requestId, path, method, body } = event.data;
const channel = this.messagePorts.get(senderId);
if (!channel) {
Logger.error(`No message channel found for sender ID: ${senderId}`);
return;
}
try {
const request = new Request(event, requestId, method, path, body);
const response = await this.router.handle(request);
channel.port1.postMessage(response);
} catch (err) {
const response = {
requestId,
status: 500,
body: null,
error: err.message || "Internal Server Error"
};
channel.port1.postMessage(response);
}
}
/**
* MacOS specific behavior.
*/
onAppActivated() {
if (process.platform === "darwin" && BrowserWindow.getAllWindows().length === 0) {
this.app?.onActivated();
}
}
/**
* Shuts down the message channel for a specific sender ID.
* This method closes the IPC channel for the specified sender ID and
* removes it from the messagePorts map.
* @param channelSenderId - The ID of the sender channel to shut down.
* @param remove - Whether to remove the channel from the messagePorts map.
*/
shutdownChannel(channelSenderId) {
const channel = this.messagePorts.get(channelSenderId);
if (!channel) {
Logger.warn(`No message channel found for sender ID: ${channelSenderId}`);
return;
}
channel.port1.off("message", this.onRendererMessage.bind(this));
channel.port1.close();
channel.port2.close();
this.messagePorts.delete(channelSenderId);
}
/**
* Handles the application shutdown process.
* This method is called when all windows are closed, and it cleans up the message channels
*/
async onAllWindowsClosed() {
this.messagePorts.forEach((channel, senderId) => {
this.shutdownChannel(senderId);
});
this.messagePorts.clear();
Logger.info("All windows closed, shutting down application...");
await this.app?.dispose();
if (process.platform !== "darwin") {
app.quit();
}
}
// ---
/**
* Configures the NoxApp instance with the provided application class.
* This method allows you to set the application class that will handle lifecycle events.
* @param app - The application class to configure.
* @returns NoxApp instance for method chaining.
*/
configure(app3) {
this.app = inject(app3);
return this;
}
/**
* Registers a middleware for the root of the application.
* This method allows you to define a middleware that will be applied to all requests
* @param middleware - The middleware class to register.
* @returns NoxApp instance for method chaining.
*/
use(middleware) {
this.router.defineRootMiddleware(middleware);
return this;
}
/**
* Should be called after the bootstrapApplication function is called.
* @returns NoxApp instance for method chaining.
*/
start() {
this.app?.onReady();
return this;
}
};
__name(_NoxApp, "NoxApp");
var NoxApp = _NoxApp;
NoxApp = _ts_decorate2([
Injectable("singleton"),
_ts_metadata("design:type", Function),
_ts_metadata("design:paramtypes", [
typeof Router === "undefined" ? Object : Router
])
], NoxApp);
// src/bootstrap.ts
import { app as app2 } from "electron/main";
async function bootstrapApplication(rootModule) {
if (!getModuleMetadata(rootModule)) {
throw new Error(`Root module must be decorated with @Module`);
}
await app2.whenReady();
const noxApp = inject(NoxApp);
await noxApp.init();
return noxApp;
}
__name(bootstrapApplication, "bootstrapApplication");
export {
AppInjector,
Authorize,
BadGatewayException,
BadRequestException,
CONTROLLER_METADATA_KEY,
ConflictException,
Controller,
Delete,
ForbiddenException,
GatewayTimeoutException,
Get,
HttpVersionNotSupportedException,
INJECTABLE_METADATA_KEY,
Injectable,
InsufficientStorageException,
InternalServerException,
Logger,
LoopDetectedException,
MODULE_METADATA_KEY,
MethodNotAllowedException,
Module,
NetworkAuthenticationRequiredException,
NetworkConnectTimeoutException,
NotAcceptableException,
NotExtendedException,
NotFoundException,
NotImplementedException,
NoxApp,
Patch,
PaymentRequiredException,
Post,
Put,
ROUTE_METADATA_KEY,
Request,
RequestTimeoutException,
ResponseException,
RootInjector,
Router,
ServiceUnavailableException,
TooManyRequestsException,
UnauthorizedException,
UpgradeRequiredException,
UseMiddlewares,
VariantAlsoNegotiatesException,
bootstrapApplication,
getControllerMetadata,
getGuardForController,
getGuardForControllerAction,
getInjectableMetadata,
getMiddlewaresForController,
getMiddlewaresForControllerAction,
getModuleMetadata,
getRouteMetadata,
inject
};
/**
* @copyright 2025 NoxFly
* @license MIT
* @author NoxFly
*/
//# sourceMappingURL=noxus.mjs.map