UNPKG

brahma-firelight

Version:

A blazing-fast, fire-and-forget orchestrator built with Rust and JavaScript, designed for ultra-low-latency task routing, message triggering, and heavyweight logic execution — all without blocking. A native Rust AddOn for NodeJS, BunJS and DenoJS.

405 lines (360 loc) 15.4 kB
// Created by Shyam M (https://github.com/Shyam20001) // License: MIT // BrahmaJS — Ultra-fast Node.js framework powered by Rust (via NAPI-RS) const { startServer, registerJsCallback, respond, getJsResponseTimeout, getMaxBodyBytes, setJsResponseTimeout, setMaxBodyBytes, registerRustHotpath, listRustHotpaths, removeRustHotpath, clearRustHotpaths } = require('./brahma'); // native addon function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&'); } function compilePath(path) { const names = []; if (!path || path === '*') return { regex: /^.*$/, names }; const segs = (path || '/').split('/').filter(Boolean); if (segs.length === 0) return { regex: /^\/$/, names }; const parts = segs.map(seg => { if (seg.startsWith(':')) { names.push(seg.slice(1)); return '([^/]+)'; } if (seg === '*') return '(.*)'; return escapeRegExp(seg); }).join('\\/'); return { regex: new RegExp('^\\/' + parts + '\\/?$'), names }; } function matchPrefix(mountPath, reqPath) { if (!mountPath || mountPath === '/' || mountPath === '*') return true; const n = mountPath.endsWith('/') ? mountPath.slice(0, -1) : mountPath; return reqPath === n || reqPath.startsWith(n + '/'); } function createApp() { const middleware = []; const routes = []; // routeMap per method: exact map and param array const routeMap = {}; // e.g. routeMap["GET"] = Map of exact, routeMap["GET_param"] = [] let shuttingDown = false, pending = 0; let nativeAttached = false, listening = false; function ensureRouteBuckets(method) { if (!routeMap[method]) routeMap[method] = new Map(); if (!routeMap[method + '_param']) routeMap[method + '_param'] = []; } function use(pathOrFn, ...fns) { if (typeof pathOrFn === 'function') { middleware.push({ path: '/', fn: pathOrFn }); return app; } const path = pathOrFn || '/'; if (fns.length === 0) throw new TypeError('middleware must be a function'); for (const fn of fns) { if (typeof fn !== 'function') throw new TypeError('middleware must be a function'); middleware.push({ path, fn }); } return app; } function addRouteImpl(method, path, ...fns) { if (fns.length === 0) throw new Error('route needs handler'); const handler = fns.length === 1 ? fns[0] : (req, res, next) => { let i = 0; function _n(err) { if (err) return res.text(err.message || String(err), 500); const fn = fns[i++]; if (!fn) return; try { fn(req, res, _n); } catch (e) { _n(e); } } _n(); }; const route = { method: method.toUpperCase(), path, handler, pathInfo: compilePath(path) }; routes.push(route); // maintain routeMap const m = route.method; ensureRouteBuckets(m); // static path: no params and no wildcard const isParam = route.pathInfo.names.length > 0 || path.includes('*'); if (!isParam) { // normalize key: ensure leading slash const key = (path === '' || path === '/') ? '/' : (path.startsWith('/') ? path : '/' + path); routeMap[m].set(key, route); } else { routeMap[m + '_param'].push(route); } return app; } function createRes(reqId) { let sent = false; const headers = {}; return { _sent: false, _sendObj(obj) { if (sent) return; sent = true; // Normalize cookies into array of strings let cookies = []; if (obj.cookies) { if (Array.isArray(obj.cookies)) cookies = obj.cookies.map(String); else if (typeof obj.cookies === 'object') { // object map -> ["k=v", ...] cookies = Object.keys(obj.cookies).map(k => `${k}=${obj.cookies[k]}`); } else cookies = [String(obj.cookies)]; } // Normalize headers: we keep values as-is (string or array) const headersOut = Object.assign({}, headers, obj.headers || {}); // Normalize body into Buffer (preserve Buffer if provided) let bodyVal = obj.body ?? ''; let bodyBuf; if (Buffer.isBuffer(bodyVal)) bodyBuf = bodyVal; else if (typeof bodyVal === 'string') bodyBuf = Buffer.from(bodyVal, 'utf8'); else if (typeof bodyVal === 'object') { try { bodyBuf = Buffer.from(JSON.stringify(bodyVal), 'utf8'); } catch { bodyBuf = Buffer.from(String(bodyVal), 'utf8'); } } else { bodyBuf = Buffer.from(String(bodyVal), 'utf8'); } // If headersOut contains Set-Cookie but cookies array is provided, remove duplicates if (cookies.length > 0) { for (const k of Object.keys(headersOut)) { if (k.toLowerCase() === 'set-cookie') { delete headersOut[k]; break; } } } // Prepare JSON strings or null for native call const headersJson = Object.keys(headersOut).length ? JSON.stringify(headersOut) : null; const cookiesJson = cookies.length ? JSON.stringify(cookies) : null; try { // call new native signature respond(String(reqId), Number(obj.status ?? 200) || 200, headersJson, cookiesJson, bodyBuf); } catch (e) { console.error('respond failed', e); } this._sent = true; }, send(a = 200, b = {}, c = [], d = '') { if (a && typeof a === 'object' && !Array.isArray(a)) return this._sendObj(a); return this._sendObj({ status: a, headers: b, cookies: Array.isArray(c) ? c : (c ? [c] : []), body: d }); }, text(t, status = 200, cookies = []) { this._sendObj({ status, headers: { 'Content-Type': 'text/plain' }, cookies, body: String(t) }); }, html(h, status = 200, cookies = []) { this._sendObj({ status, headers: { 'Content-Type': 'text/html' }, cookies, body: String(h) }); }, json(o, status = 200, cookies = []) { this._sendObj({ status, headers: { 'Content-Type': 'application/json' }, cookies, body: JSON.stringify(o) }); }, redirect(loc, status = 302) { this._sendObj({ status, headers: { Location: loc }, cookies: [], body: 'Redirecting' }); }, setHeader(k, v) { headers[k] = v; }, appendHeader(k, v) { const cur = headers[k]; if (cur === undefined) headers[k] = v; else if (Array.isArray(cur)) headers[k] = cur.concat(v); else headers[k] = [cur].concat(v); } }; } // fast robust header parser: accepts object or array-of-pairs function normalizeHeadersCandidate(parsed) { if (!parsed) return {}; if (!Array.isArray(parsed) && typeof parsed === 'object') { const out = {}; for (const k of Object.keys(parsed)) { out[String(k).toLowerCase()] = parsed[k]; } return out; } if (Array.isArray(parsed)) { const out = {}; for (let i = 0; i < parsed.length; i++) { const pair = parsed[i]; if (!pair || pair.length === 0) continue; const key = String(pair[0]).toLowerCase(); const val = pair.length > 1 ? pair[1] : ''; const cur = out[key]; if (cur === undefined) out[key] = val; else if (Array.isArray(cur)) cur.push(val); else out[key] = [cur, val]; } return out; } return {}; } function attachNative() { if (nativeAttached) return; nativeAttached = true; // callback shape expected from Rust: // [ reqId, path, method, query, headersJson, body, peerAddr, cookieHeader ] registerJsCallback((_, rawParts) => { if (!rawParts || !Array.isArray(rawParts)) return ''; const [ reqIdRaw, pathRaw, methodRaw, rawQueryRaw, headersJsonRaw, bodyRaw, peerAddrRaw, cookieHeaderRaw ] = rawParts; const reqId = reqIdRaw != null ? String(reqIdRaw) : ''; const path = pathRaw ?? '/'; const method = (methodRaw ?? 'GET').toString().toUpperCase(); const rawQuery = rawQueryRaw ?? ''; // headersJsonRaw may be a string or object/array already — parse only if string let headersRaw = headersJsonRaw; if (typeof headersJsonRaw === 'string' && headersJsonRaw.length > 0) { try { headersRaw = JSON.parse(headersJsonRaw); } catch (e) { headersRaw = []; } } const headers = normalizeHeadersCandidate(headersRaw); // Normalize body into Raw-Buffer (no parsing here; leave it to handler) const body = bodyRaw || Buffer.alloc(0); // parse cookie header cheaply (prefers cookieHeaderRaw) const cookies = {}; const cookieStr = (typeof cookieHeaderRaw === 'string' && cookieHeaderRaw) ? cookieHeaderRaw : (headers['cookie'] || ''); if (cookieStr) { const parts = cookieStr.split(/; */); for (let i = 0; i < parts.length; i++) { const pair = parts[i]; if (!pair) continue; const idx = pair.indexOf('='); if (idx > 0) { const k = pair.slice(0, idx).trim(); const v = pair.slice(idx + 1).trim(); if (k) cookies[k] = v; } } } // parse peerAddr into ip/port (IPv6-aware by splitting at last colon) const peerAddr = (typeof peerAddrRaw === 'string') ? peerAddrRaw : (peerAddrRaw ? String(peerAddrRaw) : ''); let remoteIp = '', remotePort = '', remoteAddr = ''; if (peerAddr) { remoteAddr = peerAddr; const lastColon = peerAddr.lastIndexOf(':'); if (lastColon > 0) { remotePort = peerAddr.slice(lastColon + 1); remoteIp = peerAddr.slice(0, lastColon); if (remoteIp.startsWith('[') && remoteIp.endsWith(']')) remoteIp = remoteIp.slice(1, -1); } else { remoteIp = peerAddr; } } if (shuttingDown) { try { // use new native signature for shutdown response respond(String(reqId), 503, JSON.stringify({ 'Content-Type': 'text/plain' }), null, Buffer.from('Server shutting down')); } catch (_) { } return ''; } pending++; // parse query (faster small parser, behavior-preserving for simple queries) const query = {}; if (rawQuery) { const parts = String(rawQuery).split('&'); for (let i = 0; i < parts.length; i++) { const p = parts[i]; if (!p) continue; const idx = p.indexOf('='); if (idx === -1) { try { query[decodeURIComponent(p)] = ''; } catch (e) { query[p] = ''; } } else { const key = p.slice(0, idx); const val = p.slice(idx + 1); try { query[decodeURIComponent(key)] = decodeURIComponent(val); } catch (e) { query[key] = val; } } } } const req = { reqId: String(reqId), path, method, headers, query, body, cookies, rawCookies: cookieStr, remoteAddr, remoteIp, remotePort, params: {} }; const res = createRes(String(reqId)); // routing: fast exact-map then param arrays let matched = null; // fast exact match for method const methodMap = routeMap[req.method]; if (methodMap && methodMap.has(path)) { matched = methodMap.get(path); } else { // check method param routes then ALL param routes const methodParam = routeMap[req.method + '_param'] || []; const allParam = routeMap['ALL_param'] || []; const paramLists = methodParam.length ? methodParam : []; // concat with ALL_param without allocating a new array when possible let found = false; for (let i = 0; i < methodParam.length && !found; i++) { const r = methodParam[i]; const m = r.pathInfo.regex.exec(path); if (!m) continue; req.params = {}; for (let j = 0; j < r.pathInfo.names.length; j++) req.params[r.pathInfo.names[j]] = decodeURIComponent(m[j + 1] || ''); matched = r; found = true; break; } if (!matched) { for (let i = 0; i < allParam.length; i++) { const r = allParam[i]; const m = r.pathInfo.regex.exec(path); if (!m) continue; req.params = {}; for (let j = 0; j < r.pathInfo.names.length; j++) req.params[r.pathInfo.names[j]] = decodeURIComponent(m[j + 1] || ''); matched = r; break; } } } // build chain without intermediate arrays const chain = []; for (let i = 0; i < middleware.length; i++) { const mw = middleware[i]; if (matchPrefix(mw.path, path)) chain.push(mw.fn); } chain.push(matched ? matched.handler : ((rq, rs) => rs.text('Not Found', 404))); let idx = 0; function next(err) { if (err) { if (!res._sent) res.text('Error: ' + (err && err.message ? err.message : String(err)), 500); return finish(); } const fn = chain[idx++]; if (!fn) return finish(); try { const out = fn(req, res, next); if (out && typeof out.then === 'function') { out.then(resObj => { if (!res._sent && resObj && typeof resObj === 'object' && ('status' in resObj || 'body' in resObj || 'headers' in resObj || 'cookies' in resObj)) { res._sendObj(resObj); } }).catch(err => next(err)); } else if (out && typeof out === 'object' && ('status' in out || 'body' in out || 'headers' in out || 'cookies' in out)) { res._sendObj(out); } } catch (e) { next(e); } } function finish() { if (pending > 0) pending--; /* shutdown handled elsewhere */ } try { next(); } catch (e) { next(e); } return ''; // napi callback string return }); } // Enable Rust's Tokio-Multi-threaded Runtime optional false by default function listen(host, port, cb, multicore = false) { if (host && port) startServer(host, port, multicore); else startServer(); listening = true; attachNative(); if (cb) cb(); return app; } function close(timeoutMs = 10000) { if (!listening) return Promise.resolve(); shuttingDown = true; return new Promise(resolve => { const t = setTimeout(() => { resolve(); }, timeoutMs); // simple graceful: resolve immediately if no pending if (pending === 0) { clearTimeout(t); resolve(); } }); } function del(path, ...fns) { return addRouteImpl('DELETE', path, ...fns); } const app = { use, get: (p, ...f) => addRouteImpl('GET', p, ...f), post: (p, ...f) => addRouteImpl('POST', p, ...f), put: (p, ...f) => addRouteImpl('PUT', p, ...f), del, delete: del, listen, close, _internal: { middleware, routes, routeMap } }; return app; } module.exports = { createApp, getJsResponseTimeout, getMaxBodyBytes, setMaxBodyBytes, setJsResponseTimeout, registerRustHotpath, listRustHotpaths, removeRustHotpath, clearRustHotpaths };