scutivion 0.0.5 → 0.0.6
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/Core/ContextPool.js +1 -0
- package/Core/CoreEngine.js +1 -0
- package/Core/HookSystem.js +1 -0
- package/Plugins/ContentParsingPlugin.js +1 -0
- package/Plugins/DecoratorPlugin.js +1 -0
- package/Plugins/ErrorHandlingPlugin.js +1 -0
- package/Plugins/FileStreamingPlugin.js +1 -0
- package/Plugins/FrameworkPlugin.js +1 -0
- package/Plugins/JSONValidationPlugin.js +1 -0
- package/Plugins/ObservabilityPlugin.js +1 -0
- package/Plugins/ResponseCompressionPlugin.js +1 -0
- package/Plugins/SecurityPlugin.js +1 -0
- package/Plugins/StructuredLoggingPlugin.js +1 -0
- package/Protocols/HTTP2ProtocolAdapter.js +1 -0
- package/Protocols/HTTPProtocolAdapter.js +1 -0
- package/Protocols/TCPProtocolAdapter.js +1 -0
- package/Protocols/UDPProtocolAdapter.js +1 -0
- package/Protocols/WebSocketProtocolAdapter.js +1 -0
- package/Router/TrieRouter.js +1 -0
- package/package.json +1 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class ContextObjectPool{constructor(size=1e3){this.pool=[];this.bufferPool=[];this.size=size;for(let i=0;i<size;i++){this.pool.push(this.createContext());this.bufferPool.push(Buffer.alloc(4096))}}createContext(){return{req:null,res:null,params:[],query:{},body:null,socket:null,protocol:null,method:null,url:null,headers:{},statusCode:200,responseHeaders:{},responseBody:null,error:null,custom:{}}}acquire(){return this.pool.pop()||this.createContext()}release(ctx){ctx.req=null;ctx.res=null;ctx.params.length=0;ctx.query={};ctx.body=null;ctx.socket=null;ctx.protocol=null;ctx.method=null;ctx.url=null;ctx.headers={};ctx.statusCode=200;ctx.responseHeaders={};ctx.responseBody=null;ctx.error=null;ctx.custom={};if(this.pool.length<this.size){this.pool.push(ctx)}}acquireBuffer(){return this.bufferPool.pop()||Buffer.alloc(4096)}releaseBuffer(buf){if(this.bufferPool.length<this.size){this.bufferPool.push(buf)}}}module.exports=ContextObjectPool;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const{EventEmitter:EventEmitter}=require("events");const TrieRouter=require("../Router/TrieRouter");class UnifiedEngineCore extends EventEmitter{constructor(options={}){super();this.router=new TrieRouter;this.hooks=new(require("./HookSystem"));this.contextPool=new(require("./ContextPool"))(options.poolSize||1e3);this.services={};this.protocols={};this.decorators={context:{},request:{},response:{}};this.circuitBreaker={failures:0,threshold:5,timeout:1e4,lastFailure:0};this.isShuttingDown=false;this.cluster=options.cluster||false;process.on("SIGTERM",()=>this.shutdown());process.on("SIGINT",()=>this.shutdown())}registerService(name,service){this.services[name]=service}decorate(type,name,value){if(this.decorators[type]){this.decorators[type][name]=value}}route(method,path,handlerOrOptions){let handler=handlerOrOptions;let schema=null;if(typeof handlerOrOptions==="object"&&handlerOrOptions.handler){handler=handlerOrOptions.handler;schema=handlerOrOptions.schema}this.router.insert(method,path,{handler:handler,schema:schema})}hook(hookName,fn){this.hooks.register(hookName,fn)}async handleRequest(protocol,rawReq,rawRes,socket=null){if(this.isShuttingDown)return;try{this.checkCircuitBreaker();const ctx=this.contextPool.acquire();ctx.protocol=protocol;ctx.req=rawReq;ctx.res=rawRes;ctx.socket=socket;Object.assign(ctx,this.decorators.context);if(ctx.req)Object.assign(ctx.req,this.decorators.request);if(ctx.res)Object.assign(ctx.res,this.decorators.response);if(protocol==="http"||protocol==="http2"){ctx.method=rawReq.method;ctx.url=rawReq.url;ctx.headers=rawReq.headers}this.hooks.execute("onRequest",ctx);if(ctx.method&&ctx.url){const path=ctx.url.split("?")[0];const route=this.router.lookup(ctx.method,path,ctx.params);if(route&&route.handler){ctx.schema=route.schema;try{route.handler(ctx)}catch(err){ctx.error=err;this.hooks.execute("onError",ctx)}}else{ctx.statusCode=404}}this.hooks.execute("onResponse",ctx);this.emit("response",ctx);this.contextPool.release(ctx)}catch(err){console.error("Request handling error:",err);this.recordFailure()}}listen(protocol,port,options={}){const adapter=this.protocols[protocol];if(!adapter)throw new Error(`Protocol ${protocol} not registered`);adapter.listen(this,port,options)}registerProtocol(name,adapter){this.protocols[name]=adapter}checkCircuitBreaker(){if(this.circuitBreaker.failures>=this.circuitBreaker.threshold){const now=Date.now();if(now-this.circuitBreaker.lastFailure<this.circuitBreaker.timeout){throw new Error("Circuit breaker open")}else{this.circuitBreaker.failures=0}}}recordFailure(){this.circuitBreaker.failures++;this.circuitBreaker.lastFailure=Date.now()}async shutdown(){if(this.isShuttingDown)return;this.isShuttingDown=true;console.log("Shutting down gracefully...");for(const adapter of Object.values(this.protocols)){if(adapter.close)adapter.close()}this.contextPool=null;process.exit(0)}}module.exports=UnifiedEngineCore;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class LifecycleHookSystem{constructor(){this.onRequest=[];this.onResponse=[];this.onError=[]}register(hookName,fn){if(this[hookName]){this[hookName].push(fn)}}execute(hookName,ctx){const hooks=this[hookName];if(!hooks)return;for(const hook of hooks){try{hook(ctx)}catch(err){if(hookName!=="onError"){this.execute("onError",{...ctx,error:err})}}}}async executeInline(hookName,ctx){const hooks=this[hookName];if(!hooks)return;for(const hook of hooks){await hook(ctx)}}}module.exports=LifecycleHookSystem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const{parse:parseQuery}=require("querystring");const FrameworkPlugin=require("./FrameworkPlugin");class ContentParsingPlugin extends FrameworkPlugin{constructor(options={}){super("content-parsing");this.options={json:true,form:true,multipart:false,...options}}apply(core){this.registerHook("onRequest",ctx=>{if(!ctx.req||!ctx.req.headers)return;const contentType=ctx.req.headers["content-type"]||"";const method=ctx.req.method;if(method==="GET"||method==="HEAD")return;if(this.options.json&&contentType.includes("application/json")){this.parseJSON(ctx)}else if(this.options.form&&contentType.includes("application/x-www-form-urlencoded")){this.parseForm(ctx)}})}parseJSON(ctx){if(!ctx.rawBody)return;try{ctx.requestBody=JSON.parse(ctx.rawBody.toString())}catch(e){throw new Error("Invalid JSON")}}parseForm(ctx){if(!ctx.rawBody)return;ctx.requestBody=parseQuery(ctx.rawBody.toString())}}module.exports=ContentParsingPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class DecoratorPlugin extends FrameworkPlugin{constructor(){super("decorator")}apply(core){if(!core.decorators){core.decorators={context:{},request:{},response:{}}}}}module.exports=DecoratorPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class ErrorHandlingPlugin extends FrameworkPlugin{constructor(){super("error-handling")}apply(core){this.registerHook("onError",ctx=>{if(ctx.error){const error=ctx.error;ctx.responseBody={status:ctx.statusCode||500,code:error.code||"INTERNAL_ERROR",message:error.message||"Internal server error",meta:error.meta||{}};ctx.statusCode=ctx.statusCode||500}})}}module.exports=ErrorHandlingPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");const fs=require("fs");class FileStreamingPlugin extends FrameworkPlugin{constructor(){super("streaming")}async apply(core){core.route("POST","/upload",async ctx=>{ctx.responseBody=JSON.stringify({uploaded:true})})}}module.exports=FileStreamingPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class FrameworkPlugin{constructor(name){this.name=name;this.services={};this.hooks={}}registerService(name,service){this.services[name]=service}registerHook(hookName,fn){if(!this.hooks[hookName])this.hooks[hookName]=[];this.hooks[hookName].push(fn)}apply(core){for(const[name,service]of Object.entries(this.services)){core.registerService(name,service)}for(const[hookName,fns]of Object.entries(this.hooks)){for(const fn of fns){core.hook(hookName,fn)}}}}module.exports=FrameworkPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class JSONValidationPlugin extends FrameworkPlugin{constructor(){super("json-validation")}apply(core){this.registerHook("onRequest",ctx=>{if(ctx.schema?.body&&ctx.requestBody){this.validate(ctx.requestBody,ctx.schema.body)}})}validate(data,schema){if(schema.type==="object"&&typeof data!=="object")throw new Error("Invalid type");if(schema.required){for(const req of schema.required){if(!(req in data))throw new Error(`Missing required field: ${req}`)}}if(schema.properties){for(const[key,prop]of Object.entries(schema.properties)){if(prop.type==="string"&&data[key]&&typeof data[key]!=="string"){throw new Error(`Invalid type for ${key}: expected string`)}if(prop.minLength&&data[key]&&data[key].length<prop.minLength){throw new Error(`${key} too short`)}}}}}module.exports=JSONValidationPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class ObservabilityPlugin extends FrameworkPlugin{constructor(){super("observability");this.metrics={requests:0,errors:0}}async apply(core){core.route("GET","/health",async ctx=>{ctx.responseBody=JSON.stringify({status:"ok",uptime:process.uptime()})});core.route("GET","/metrics",async ctx=>{ctx.responseBody=JSON.stringify(this.metrics)});core.hook("onRequest",()=>this.metrics.requests++);core.hook("onError",()=>this.metrics.errors++)}}module.exports=ObservabilityPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");const zlib=require("zlib");class ResponseCompressionPlugin extends FrameworkPlugin{constructor(){super("compression")}async apply(core){core.hook("onResponse",async ctx=>{if(ctx.responseBody&&ctx.headers["accept-encoding"]?.includes("gzip")){ctx.responseBody=await new Promise((resolve,reject)=>{zlib.gzip(Buffer.from(ctx.responseBody),(err,result)=>{if(err)reject(err);else resolve(result)})});ctx.headers["content-encoding"]="gzip"}})}}module.exports=ResponseCompressionPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class SecurityPlugin extends FrameworkPlugin{constructor(options={}){super("security");this.rateLimit=options.rateLimit||1e3;this.requests=new Map}async apply(core){core.hook("onRequest",async ctx=>{const ip=ctx.socket?.remoteAddress||"unknown";const now=Date.now();if(!this.requests.has(ip))this.requests.set(ip,[]);const times=this.requests.get(ip).filter(t=>now-t<6e4);if(times.length>=this.rateLimit){ctx.statusCode=429;ctx.responseBody="Rate limit exceeded";return}times.push(now);this.requests.set(ip,times)})}}module.exports=SecurityPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const FrameworkPlugin=require("./FrameworkPlugin");class StructuredLoggingPlugin extends FrameworkPlugin{constructor(logger=console){super("logging");this.logger=logger;this.registerHook("onRequest",ctx=>{this.logger.log(`${ctx.method} ${ctx.url}`)});this.registerHook("onResponse",ctx=>{this.logger.log(`Response ${ctx.statusCode}`)});this.registerHook("onError",ctx=>{this.logger.error(`Error: ${ctx.error.message}`)})}}module.exports=StructuredLoggingPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const http2=require("http2");class HTTP2ProtocolAdapter{constructor(options={}){this.server=http2.createServer(options)}async listen(core,port){this.server.on("stream",async(stream,headers)=>{const ctx=core.pool.acquire();ctx.stream=stream;ctx.pushStream=(pushHeaders,callback)=>stream.pushStream(pushHeaders,callback);await core.handleRequest("http2",{method:headers[":method"],url:headers[":path"],headers:headers},{respond:resHeaders=>stream.respond(resHeaders),end:data=>stream.end(data)})})}close(){this.server.close()}}module.exports=HTTP2ProtocolAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const http=require("http");const http2=require("http2");class HTTPProtocolAdapter{constructor(options={}){this.http1Server=null;this.http2Server=null;this.options=options}listen(core,port,opts={}){if(opts.http2){this.http2Server=http2.createServer(opts,(req,res)=>{core.handleRequest("http2",req,res)});this.http2Server.listen(port)}else{this.http1Server=http.createServer((req,res)=>{core.handleRequest("http",req,res)});this.http1Server.listen(port)}}close(){if(this.http1Server)this.http1Server.close();if(this.http2Server)this.http2Server.close()}}module.exports=HTTPProtocolAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const net=require("net");class TCPProtocolAdapter{constructor(options={}){this.server=null;this.options=options}listen(core,port){this.server=net.createServer(socket=>{let buffer=Buffer.alloc(0);socket.on("data",data=>{buffer=Buffer.concat([buffer,data]);while(buffer.length>=4){const length=buffer.readUInt32BE(0);if(buffer.length<4+length)break;const message=buffer.slice(4,4+length);buffer=buffer.slice(4+length);const req={method:"TCP",url:"/",headers:{},body:message};const res={write:data=>socket.write(data),end:()=>socket.end(),writeHead:()=>{}};core.handleRequest("tcp",req,res,socket)}});socket.on("error",err=>{console.error("TCP socket error:",err)})});this.server.listen(port)}close(){if(this.server)this.server.close()}}module.exports=TCPProtocolAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const dgram=require("dgram");class UDPProtocolAdapter{constructor(options={}){this.server=dgram.createSocket("udp4")}async listen(core,port){this.server.on("message",async(msg,rinfo)=>{const ctx=core.pool.acquire();ctx.rinfo=rinfo;await core.handleRequest("udp",{method:"UDP",url:"/",headers:{},body:msg},{send:data=>this.server.send(data,rinfo.port,rinfo.address)})});this.server.on("error",err=>{console.error("UDP error:",err)});this.server.bind(port)}close(){this.server.close()}}module.exports=UDPProtocolAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const crypto=require("crypto");class WebSocketProtocolAdapter{constructor(options={}){this.options=options}listen(core,port){const http=require("http");this.server=http.createServer((req,res)=>{if(req.headers.upgrade&&req.headers.upgrade.toLowerCase()==="websocket"){this.handleUpgrade(req,res,core)}else{core.handleRequest("http",req,res)}});this.server.listen(port)}handleUpgrade(req,res,core){const key=req.headers["sec-websocket-key"];const accept=crypto.createHash("sha1").update(key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");res.writeHead(101,{Upgrade:"websocket",Connection:"Upgrade","Sec-WebSocket-Accept":accept});res.end();const socket=req.socket;let buffer=Buffer.alloc(0);socket.on("data",data=>{buffer=Buffer.concat([buffer,data]);buffer=this.parseFrames(buffer,frame=>{if(frame.opcode===1||frame.opcode===2){const req={method:"WS",url:"/",headers:{},body:frame.payload};const res={send:data=>process.nextTick(()=>this.sendFrame(socket,frame.opcode===2?2:1,data)),close:()=>socket.end()};core.handleRequest("ws",req,res,socket)}})})}parseFrames(buffer,onFrame){let offset=0;while(offset<buffer.length){if(buffer.length-offset<2)break;const byte1=buffer[offset++];const byte2=buffer[offset++];const fin=(byte1&128)!==0;const opcode=byte1&15;const masked=(byte2&128)!==0;let length=byte2&127;if(length===126){if(buffer.length-offset<2)break;length=buffer.readUInt16BE(offset);offset+=2}else if(length===127){if(buffer.length-offset<8)break;length=buffer.readUInt32BE(offset)*4294967296+buffer.readUInt32BE(offset+4);offset+=8}if(masked){if(buffer.length-offset<4)break;const mask=buffer.slice(offset,offset+4);offset+=4}if(buffer.length-offset<length)break;let payload=buffer.slice(offset,offset+length);offset+=length;if(masked){for(let i=0;i<payload.length;i++){payload[i]^=mask[i%4]}}onFrame({opcode:opcode,payload:payload,fin:fin})}return buffer.slice(offset)}sendFrame(socket,opcode,payload){const frame=Buffer.alloc(2+payload.length);frame[0]=128|opcode;frame[1]=payload.length;payload.copy(frame,2);socket.write(frame)}close(){if(this.server)this.server.close()}}module.exports=WebSocketProtocolAdapter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class RadixNode{constructor(){this.children={};this.handlers={};this.paramName=null;this.isWildcard=false}}class TrieRouter{constructor(){this.root=new RadixNode}insert(method,path,handler){let node=this.root;const segments=path.split("/").filter(s=>s.length>0);for(const segment of segments){let child=node.children[segment];if(!child){child=new RadixNode;if(segment.startsWith(":")){child.paramName=segment.slice(1)}else if(segment==="*"){child.isWildcard=true}node.children[segment]=child}node=child}node.handlers[method]=handler}lookup(method,path,params){let node=this.root;const segments=path.split("/").filter(s=>s.length>0);let paramIndex=0;for(const segment of segments){let child=node.children[segment];if(!child){for(const key in node.children){const c=node.children[key];if(c.paramName){params[paramIndex++]=segment;child=c;break}else if(c.isWildcard){child=c;break}}}if(!child)return null;node=child}return node.handlers[method]||null}}module.exports=TrieRouter;
|
package/package.json
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scutivion",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "Scutivion: Futuristic high-performance Node.js framework inspired by UY Scuti, the largest supergiant star. Shield-like protection for hyperscale applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
-
"files": [
|
|
8
|
-
"index.d.ts"
|
|
9
|
-
],
|
|
10
7
|
"author": "Muhammad Imam Rozali <muh.imamrozali@gmail.com>",
|
|
11
8
|
"contributors": [],
|
|
12
9
|
"license": "MIT",
|