fullstx 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.
Files changed (83) hide show
  1. package/README.md +86 -0
  2. package/bin/create.js +68 -0
  3. package/bin/generate.js +78 -0
  4. package/coverage/clover.xml +1356 -0
  5. package/coverage/coverage-final.json +30 -0
  6. package/coverage/lcov-report/base.css +224 -0
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/favicon.png +0 -0
  9. package/coverage/lcov-report/fullstx/index.html +146 -0
  10. package/coverage/lcov-report/fullstx/index.js.html +379 -0
  11. package/coverage/lcov-report/fullstx/lib/application.js.html +1840 -0
  12. package/coverage/lcov-report/fullstx/lib/browser-sync.js.html +199 -0
  13. package/coverage/lcov-report/fullstx/lib/database.js.html +535 -0
  14. package/coverage/lcov-report/fullstx/lib/errors.js.html +154 -0
  15. package/coverage/lcov-report/fullstx/lib/features/hooks.js.html +151 -0
  16. package/coverage/lcov-report/fullstx/lib/features/index.html +176 -0
  17. package/coverage/lcov-report/fullstx/lib/features/jwt.js.html +214 -0
  18. package/coverage/lcov-report/fullstx/lib/features/plugins.js.html +172 -0
  19. package/coverage/lcov-report/fullstx/lib/features/routing.js.html +211 -0
  20. package/coverage/lcov-report/fullstx/lib/features/utils.js.html +1216 -0
  21. package/coverage/lcov-report/fullstx/lib/index.html +236 -0
  22. package/coverage/lcov-report/fullstx/lib/middlewares/apiKey.js.html +127 -0
  23. package/coverage/lcov-report/fullstx/lib/middlewares/authorize.js.html +115 -0
  24. package/coverage/lcov-report/fullstx/lib/middlewares/basicAuth.js.html +148 -0
  25. package/coverage/lcov-report/fullstx/lib/middlewares/csp.js.html +124 -0
  26. package/coverage/lcov-report/fullstx/lib/middlewares/csrf.js.html +178 -0
  27. package/coverage/lcov-report/fullstx/lib/middlewares/htmx.js.html +157 -0
  28. package/coverage/lcov-report/fullstx/lib/middlewares/i18n.js.html +175 -0
  29. package/coverage/lcov-report/fullstx/lib/middlewares/index.html +281 -0
  30. package/coverage/lcov-report/fullstx/lib/middlewares/rateLimit.js.html +169 -0
  31. package/coverage/lcov-report/fullstx/lib/middlewares/redisCache.js.html +154 -0
  32. package/coverage/lcov-report/fullstx/lib/middlewares/requirePermission.js.html +118 -0
  33. package/coverage/lcov-report/fullstx/lib/middlewares/routeLocalization.js.html +118 -0
  34. package/coverage/lcov-report/fullstx/lib/middlewares/session.js.html +133 -0
  35. package/coverage/lcov-report/fullstx/lib/middlewares.js.html +340 -0
  36. package/coverage/lcov-report/fullstx/lib/radix-router.js.html +418 -0
  37. package/coverage/lcov-report/fullstx/lib/request.js.html +328 -0
  38. package/coverage/lcov-report/fullstx/lib/response.js.html +610 -0
  39. package/coverage/lcov-report/fullstx/lib/websocket.js.html +166 -0
  40. package/coverage/lcov-report/fullstx/router.js.html +226 -0
  41. package/coverage/lcov-report/fullstx/server.js.html +664 -0
  42. package/coverage/lcov-report/index.html +161 -0
  43. package/coverage/lcov-report/prettify.css +1 -0
  44. package/coverage/lcov-report/prettify.js +2 -0
  45. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  46. package/coverage/lcov-report/sorter.js +210 -0
  47. package/coverage/lcov.info +2753 -0
  48. package/dist/index.js +238 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/index.mjs +238 -0
  51. package/dist/index.mjs.map +1 -0
  52. package/index.d.ts +115 -0
  53. package/index.js +98 -0
  54. package/lib/application.js +585 -0
  55. package/lib/browser-sync.js +38 -0
  56. package/lib/database.js +150 -0
  57. package/lib/errors.js +23 -0
  58. package/lib/features/hooks.js +22 -0
  59. package/lib/features/jwt.js +43 -0
  60. package/lib/features/plugins.js +29 -0
  61. package/lib/features/routing.js +42 -0
  62. package/lib/features/utils.js +377 -0
  63. package/lib/middlewares/apiKey.js +14 -0
  64. package/lib/middlewares/authorize.js +10 -0
  65. package/lib/middlewares/basicAuth.js +21 -0
  66. package/lib/middlewares/csp.js +13 -0
  67. package/lib/middlewares/csrf.js +31 -0
  68. package/lib/middlewares/htmx.js +24 -0
  69. package/lib/middlewares/i18n.js +30 -0
  70. package/lib/middlewares/rateLimit.js +28 -0
  71. package/lib/middlewares/redisCache.js +23 -0
  72. package/lib/middlewares/requirePermission.js +11 -0
  73. package/lib/middlewares/routeLocalization.js +11 -0
  74. package/lib/middlewares/session.js +16 -0
  75. package/lib/middlewares.js +85 -0
  76. package/lib/radix-router.js +111 -0
  77. package/lib/request.js +81 -0
  78. package/lib/response.js +175 -0
  79. package/lib/websocket.js +27 -0
  80. package/package.json +61 -0
  81. package/router.js +47 -0
  82. package/server.js +193 -0
  83. package/session.js +109 -0
@@ -0,0 +1,14 @@
1
+ module.exports = function apiKey(key) {
2
+ const crypto = require('crypto');
3
+ return (req, res, next) => {
4
+ const reqKey = req.headers['x-api-key'] || (req.query && req.query.api_key);
5
+ const a = Buffer.from(reqKey || '');
6
+ const b = Buffer.from(key || '');
7
+ if (reqKey && a.length === b.length && crypto.timingSafeEqual(a, b)) {
8
+ return next();
9
+ }
10
+ if (res.json) return res.json({ error: 'Invalid API Key' }, 401);
11
+ res.writeHead(401, { 'Content-Type': 'application/json' });
12
+ res.end(JSON.stringify({ error: 'Invalid API Key' }));
13
+ };
14
+ };
@@ -0,0 +1,10 @@
1
+ module.exports = function authorize(roles = []) {
2
+ return (req, res, next) => {
3
+ if (!req.user || !req.user.role || !roles.includes(req.user.role)) {
4
+ if (res.json) return res.json({ error: 'Forbidden: Insufficient role privileges' }, 403);
5
+ res.writeHead(403, { 'Content-Type': 'application/json' });
6
+ return res.end(JSON.stringify({ error: 'Forbidden: Insufficient role privileges' }));
7
+ }
8
+ next();
9
+ };
10
+ };
@@ -0,0 +1,21 @@
1
+ module.exports = function basicAuth(credentials) {
2
+ const crypto = require('crypto');
3
+ return (req, res, next) => {
4
+ const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
5
+ const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');
6
+ const reqPass = Buffer.from(password || '');
7
+ const expectedPass = Buffer.from(credentials[login] || '');
8
+ if (
9
+ login &&
10
+ password &&
11
+ credentials[login] &&
12
+ reqPass.length === expectedPass.length &&
13
+ crypto.timingSafeEqual(reqPass, expectedPass)
14
+ ) {
15
+ req.user = { username: login };
16
+ return next();
17
+ }
18
+ res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Protected"' });
19
+ res.end('Authentication required.');
20
+ };
21
+ };
@@ -0,0 +1,13 @@
1
+ module.exports = function csp(options = {}) {
2
+ return (req, res, next) => {
3
+ const policies = [];
4
+ for (const [key, value] of Object.entries(options)) {
5
+ const directive = Array.isArray(value) ? value.join(' ') : value;
6
+ policies.push(`${key} ${directive}`);
7
+ }
8
+ if (policies.length > 0) {
9
+ res.setHeader('Content-Security-Policy', policies.join('; '));
10
+ }
11
+ next();
12
+ };
13
+ };
@@ -0,0 +1,31 @@
1
+ module.exports = function csrf() {
2
+ const crypto = require('crypto');
3
+ return (req, res, next) => {
4
+ if (!req.session) {
5
+ console.warn('[!] CSRF middleware requires Session middleware to be registered before it.');
6
+ return next();
7
+ }
8
+
9
+ if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
10
+ if (!req.session._csrfToken) {
11
+ req.session._csrfToken = crypto.randomBytes(16).toString('hex');
12
+ }
13
+ res.locals = res.locals || {};
14
+ res.locals.csrfToken = req.session._csrfToken;
15
+ req.csrfToken = () => req.session._csrfToken;
16
+ return next();
17
+ }
18
+
19
+ const token = (req.body && req.body._csrf) || req.headers['x-csrf-token'] || req.headers['x-xsrf-token'];
20
+ const tokenBuf = Buffer.from(token || '');
21
+ const sessionBuf = Buffer.from(req.session._csrfToken || '');
22
+
23
+ if (!token || tokenBuf.length !== sessionBuf.length || !crypto.timingSafeEqual(tokenBuf, sessionBuf)) {
24
+ if (res.json) return res.json({ error: 'Invalid CSRF Token' }, 403);
25
+ res.writeHead(403, { 'Content-Type': 'application/json' });
26
+ return res.end(JSON.stringify({ error: 'Invalid CSRF Token' }));
27
+ }
28
+
29
+ next();
30
+ };
31
+ };
@@ -0,0 +1,24 @@
1
+ module.exports = function htmx() {
2
+ return (req, res, next) => {
3
+ req.htmx = req.headers['hx-request'] === 'true';
4
+
5
+ res.hxTrigger = (event) => res.setHeader('HX-Trigger', event);
6
+ res.hxRedirect = (url) => {
7
+ res.setHeader('HX-Redirect', url);
8
+ res.writeHead(200);
9
+ res.end();
10
+ };
11
+ res.hxRefresh = () => {
12
+ res.setHeader('HX-Refresh', 'true');
13
+ res.writeHead(200);
14
+ res.end();
15
+ };
16
+ res.hxLocation = (url) => res.setHeader('HX-Location', url);
17
+ res.hxPushUrl = (url) => res.setHeader('HX-Push-Url', url);
18
+ res.hxReplaceUrl = (url) => res.setHeader('HX-Replace-Url', url);
19
+ res.hxReswap = (swapType) => res.setHeader('HX-Reswap', swapType);
20
+ res.hxRetarget = (target) => res.setHeader('HX-Retarget', target);
21
+
22
+ next();
23
+ };
24
+ };
@@ -0,0 +1,30 @@
1
+ module.exports = function i18n(config = { defaultLocale: 'en', locales: {} }) {
2
+ return (req, res, next) => {
3
+ const langHeader = req.headers['accept-language'];
4
+ let locale = config.defaultLocale;
5
+ if (req.query && req.query.lang && config.locales[req.query.lang]) {
6
+ locale = req.query.lang;
7
+ } else if (langHeader) {
8
+ const parsed = langHeader.split(',')[0].split('-')[0];
9
+ if (config.locales[parsed]) {
10
+ locale = parsed;
11
+ }
12
+ }
13
+
14
+ req.locale = locale;
15
+ res.t = function (key, params = {}) {
16
+ const dict = config.locales[locale] || {};
17
+ let str = dict[key] || key;
18
+ for (const [k, v] of Object.entries(params)) {
19
+ str = str.replace(new RegExp(`{${k}}`, 'g'), v);
20
+ }
21
+ return str;
22
+ };
23
+
24
+ res.locals = res.locals || {};
25
+ res.locals.t = res.t;
26
+ res.locals.locale = locale;
27
+
28
+ next();
29
+ };
30
+ };
@@ -0,0 +1,28 @@
1
+ module.exports = function rateLimit({
2
+ windowMs = 60000,
3
+ max = 100,
4
+ message = 'Too many requests, please try again later.'
5
+ } = {}) {
6
+ const hits = new Map();
7
+ return (req, res, next) => {
8
+ const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || 'unknown';
9
+ const now = Date.now();
10
+ if (!hits.has(ip)) {
11
+ hits.set(ip, { count: 1, resetTime: now + windowMs });
12
+ } else {
13
+ const record = hits.get(ip);
14
+ if (now > record.resetTime) {
15
+ record.count = 1;
16
+ record.resetTime = now + windowMs;
17
+ } else {
18
+ record.count++;
19
+ if (record.count > max) {
20
+ if (res.json) return res.json({ error: message }, 429);
21
+ res.writeHead(429, { 'Content-Type': 'application/json' });
22
+ return res.end(JSON.stringify({ error: message }));
23
+ }
24
+ }
25
+ }
26
+ next();
27
+ };
28
+ };
@@ -0,0 +1,23 @@
1
+ module.exports = function redisCache(redisClient, { prefix = 'cache:', expire = 3600 } = {}) {
2
+ return async (req, res, next) => {
3
+ if (req.method !== 'GET') return next();
4
+ const key = prefix + req.url;
5
+ try {
6
+ const cached = await redisClient.get(key);
7
+ if (cached) {
8
+ res.writeHead(200, { 'Content-Type': 'application/json' });
9
+ return res.end(cached);
10
+ }
11
+ const originalEnd = res.end;
12
+ res.end = function (chunk, encoding, callback) {
13
+ if (res.statusCode === 200 && chunk) {
14
+ redisClient.set(key, chunk.toString(), { EX: expire }).catch(console.error);
15
+ }
16
+ originalEnd.call(res, chunk, encoding, callback);
17
+ };
18
+ next();
19
+ } catch {
20
+ next();
21
+ }
22
+ };
23
+ };
@@ -0,0 +1,11 @@
1
+ module.exports = function requirePermission(permission) {
2
+ return (req, res, next) => {
3
+ const perms = req.user && req.user.permissions ? req.user.permissions : [];
4
+ if (!perms.includes(permission)) {
5
+ if (res.json) return res.json({ error: `Forbidden: Missing required permission '${permission}'` }, 403);
6
+ res.writeHead(403, { 'Content-Type': 'application/json' });
7
+ return res.end(JSON.stringify({ error: `Forbidden: Missing required permission '${permission}'` }));
8
+ }
9
+ next();
10
+ };
11
+ };
@@ -0,0 +1,11 @@
1
+ module.exports = function routeLocalization(supportedLocales = ['en']) {
2
+ return (req, res, next) => {
3
+ const parts = req.url.split('/');
4
+ if (parts.length > 1 && supportedLocales.includes(parts[1])) {
5
+ req.locale = parts[1];
6
+ req.url = '/' + parts.slice(2).join('/');
7
+ if (req.url === '') req.url = '/';
8
+ }
9
+ next();
10
+ };
11
+ };
@@ -0,0 +1,16 @@
1
+ module.exports = function session({ cookieName = 'session_id', maxAge = 86400000 } = {}) {
2
+ const crypto = require('crypto');
3
+ const store = new Map();
4
+ return (req, res, next) => {
5
+ let sessionId = req.cookies && req.cookies[cookieName];
6
+ if (!sessionId || !store.has(sessionId)) {
7
+ sessionId = crypto.randomUUID();
8
+ store.set(sessionId, { _createdAt: Date.now() });
9
+ if (res.cookie) {
10
+ res.cookie(cookieName, sessionId, { maxAge: maxAge / 1000, httpOnly: true });
11
+ }
12
+ }
13
+ req.session = store.get(sessionId);
14
+ next();
15
+ };
16
+ };
@@ -0,0 +1,85 @@
1
+ module.exports = function setupMiddlewares(opts, router, sanitizeHtml) {
2
+ if (opts.healthCheck) {
3
+ router.get('/health', (req, res) => {
4
+ const data = { status: 'ok', uptime: process.uptime(), timestamp: Date.now() };
5
+ if (res.json) {
6
+ return res.json(data);
7
+ }
8
+ res.writeHead(200, { 'Content-Type': 'application/json' });
9
+ res.end(JSON.stringify(data));
10
+ });
11
+ }
12
+
13
+ if (opts.logger) {
14
+ router.middlewares.unshift((req, res, next) => {
15
+ const start = Date.now();
16
+ res.on('finish', () => {
17
+ const duration = Date.now() - start;
18
+ console.log(`[${req.method}] ${req.url} - ${res.statusCode} (${duration}ms)`);
19
+ });
20
+ next();
21
+ });
22
+ }
23
+
24
+ if (opts.cors) {
25
+ router.middlewares.unshift((req, res, next) => {
26
+ const corsOpts = typeof opts.cors === 'object' ? opts.cors : {};
27
+ res.setHeader('Access-Control-Allow-Origin', corsOpts.origin || '*');
28
+ res.setHeader('Access-Control-Allow-Methods', corsOpts.methods || 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
29
+ res.setHeader('Access-Control-Allow-Headers', corsOpts.headers || 'Content-Type, Authorization');
30
+
31
+ if (req.method === 'OPTIONS') {
32
+ res.writeHead(204);
33
+ return res.end();
34
+ }
35
+ next();
36
+ });
37
+ }
38
+
39
+ if (opts.helmet) {
40
+ router.middlewares.unshift((req, res, next) => {
41
+ res.setHeader('X-Content-Type-Options', 'nosniff');
42
+ res.setHeader('X-Frame-Options', 'SAMEORIGIN');
43
+ res.setHeader('X-XSS-Protection', '1; mode=block');
44
+ res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
45
+ next();
46
+ });
47
+ }
48
+
49
+ if (opts.xss) {
50
+ if (!sanitizeHtml) throw new Error('Please install sanitize-html to use xss middleware');
51
+ router.middlewares.unshift((req, res, next) => {
52
+ const clean = (obj) => {
53
+ for (let key in obj) {
54
+ if (typeof obj[key] === 'string') {
55
+ obj[key] = sanitizeHtml(obj[key]);
56
+ } else if (typeof obj[key] === 'object' && obj[key] !== null) {
57
+ clean(obj[key]);
58
+ }
59
+ }
60
+ };
61
+ if (req.body) clean(req.body);
62
+ if (req.query) clean(req.query);
63
+ if (req.params) clean(req.params);
64
+ next();
65
+ });
66
+ }
67
+
68
+ if (opts.session) {
69
+ const createSessionMiddleware = require('../session');
70
+ const sessionMiddleware = createSessionMiddleware(opts);
71
+ if (sessionMiddleware) {
72
+ router.middlewares.push(sessionMiddleware);
73
+ }
74
+ }
75
+
76
+ if (opts.compression) {
77
+ const compression = require('compression');
78
+ router.middlewares.unshift(compression(typeof opts.compression === 'object' ? opts.compression : {}));
79
+ }
80
+
81
+ if (opts.htmx) {
82
+ const htmxMiddleware = require('./middlewares/htmx');
83
+ router.middlewares.unshift(htmxMiddleware());
84
+ }
85
+ };
@@ -0,0 +1,111 @@
1
+ class TrieNode {
2
+ constructor() {
3
+ this.children = new Map();
4
+ this.paramChild = null;
5
+ this.paramName = null;
6
+ this.wildcardChild = null;
7
+ this.wildcardName = null;
8
+ this.handlers = {};
9
+ }
10
+ }
11
+
12
+ class RadixRouter {
13
+ constructor() {
14
+ this.root = new TrieNode();
15
+ this.staticRoutes = new Map();
16
+ }
17
+
18
+ insert(method, path, routeConfig) {
19
+ if (typeof path !== 'string') return;
20
+
21
+ if (typeof routeConfig === 'function') {
22
+ routeConfig = { handler: routeConfig };
23
+ }
24
+
25
+ let serializer = null;
26
+ if (routeConfig.schema && routeConfig.schema.response) {
27
+ try {
28
+ const fastJson = require('fast-json-stringify');
29
+ serializer = fastJson(routeConfig.schema.response);
30
+ } catch (e) {
31
+ console.error('[fullstx] Schema serializer compilation error:', e);
32
+ }
33
+ }
34
+
35
+ const finalConfig = { ...routeConfig, serializer };
36
+
37
+ if (!path.includes(':') && !path.includes('*')) {
38
+ this.staticRoutes.set(method + path, finalConfig);
39
+ return;
40
+ }
41
+
42
+ let current = this.root;
43
+ const segments = path.split('/').filter(Boolean);
44
+
45
+ for (let i = 0; i < segments.length; i++) {
46
+ const segment = segments[i];
47
+
48
+ if (segment.startsWith('*')) {
49
+ if (!current.wildcardChild) {
50
+ current.wildcardChild = new TrieNode();
51
+ current.wildcardChild.wildcardName = segment.length > 1 ? segment.slice(1) : 'wildcard';
52
+ }
53
+ current = current.wildcardChild;
54
+
55
+ break;
56
+ } else if (segment.startsWith(':')) {
57
+ if (!current.paramChild) {
58
+ current.paramChild = new TrieNode();
59
+ current.paramChild.paramName = segment.slice(1);
60
+ }
61
+ current = current.paramChild;
62
+ } else {
63
+ if (!current.children.has(segment)) {
64
+ current.children.set(segment, new TrieNode());
65
+ }
66
+ current = current.children.get(segment);
67
+ }
68
+ }
69
+ current.handlers[method] = finalConfig;
70
+ }
71
+
72
+ lookup(method, path) {
73
+ if (typeof path !== 'string') return null;
74
+
75
+ const staticKey = method + path;
76
+ if (this.staticRoutes.has(staticKey)) {
77
+ const finalConfig = this.staticRoutes.get(staticKey);
78
+ return { routeConfig: finalConfig, handler: finalConfig.handler, params: {} };
79
+ }
80
+
81
+ const segments = path.split('/').filter(Boolean);
82
+ const params = {};
83
+
84
+ let current = this.root;
85
+
86
+ for (let i = 0; i < segments.length; i++) {
87
+ const segment = segments[i];
88
+
89
+ if (current.children.has(segment)) {
90
+ current = current.children.get(segment);
91
+ } else if (current.paramChild) {
92
+ current = current.paramChild;
93
+ params[current.paramName] = decodeURIComponent(segment);
94
+ } else if (current.wildcardChild) {
95
+ current = current.wildcardChild;
96
+ params[current.wildcardName] = decodeURIComponent(segments.slice(i).join('/'));
97
+ break;
98
+ } else {
99
+ return null;
100
+ }
101
+ }
102
+
103
+ if (current.handlers[method]) {
104
+ const finalConfig = current.handlers[method];
105
+ return { routeConfig: finalConfig, handler: finalConfig.handler, params };
106
+ }
107
+ return null;
108
+ }
109
+ }
110
+
111
+ module.exports = RadixRouter;
package/lib/request.js ADDED
@@ -0,0 +1,81 @@
1
+ const url = require('url');
2
+
3
+ const REQUEST_POOL = [];
4
+ const POOL_SIZE = 1000;
5
+
6
+ class FullstxRequest {
7
+ constructor() {
8
+ this.raw = null;
9
+ this.body = {};
10
+ this.query = {};
11
+ this.params = {};
12
+ this.cookies = {};
13
+ this.pathname = '';
14
+ this.route = null;
15
+ }
16
+
17
+ init(rawReq) {
18
+ this.raw = rawReq;
19
+ this.body = {};
20
+ this.query = {};
21
+ this.params = {};
22
+ this.cookies = {};
23
+ this.route = null;
24
+
25
+ const parsedUrl = url.parse(this.raw.url, true);
26
+
27
+ this.query = parsedUrl.query;
28
+ this.pathname = parsedUrl.pathname;
29
+
30
+ const rc = this.raw.headers.cookie;
31
+ if (rc) {
32
+ rc.split(';').forEach((cookie) => {
33
+ const parts = cookie.split('=');
34
+ this.cookies[parts.shift().trim()] = decodeURI(parts.join('='));
35
+ });
36
+ }
37
+ }
38
+
39
+ get url() {
40
+ return this.raw.url;
41
+ }
42
+ get method() {
43
+ return this.raw.method;
44
+ }
45
+ get headers() {
46
+ return this.raw.headers;
47
+ }
48
+ get socket() {
49
+ return this.raw.socket;
50
+ }
51
+
52
+ on(event, listener) {
53
+ this.raw.on(event, listener);
54
+ return this;
55
+ }
56
+ }
57
+
58
+ function getRequest(rawReq) {
59
+ if (REQUEST_POOL.length > 0) {
60
+ const req = REQUEST_POOL.pop();
61
+ req.init(rawReq);
62
+ return req;
63
+ }
64
+ const req = new FullstxRequest();
65
+ req.init(rawReq);
66
+ return req;
67
+ }
68
+
69
+ function releaseRequest(req) {
70
+ req.raw = null;
71
+ req.route = null;
72
+ if (REQUEST_POOL.length < POOL_SIZE) {
73
+ REQUEST_POOL.push(req);
74
+ }
75
+ }
76
+
77
+ module.exports = {
78
+ FullstxRequest,
79
+ getRequest,
80
+ releaseRequest
81
+ };