mastercontroller 1.2.13 → 1.3.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 (38) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/MasterAction.js +7 -7
  3. package/MasterControl.js +192 -122
  4. package/MasterCors.js +29 -0
  5. package/MasterHtml.js +5 -5
  6. package/MasterPipeline.js +344 -0
  7. package/MasterRouter.js +59 -29
  8. package/MasterSession.js +19 -0
  9. package/MasterTemplate.js +3 -3
  10. package/MasterTimeout.js +332 -0
  11. package/README.md +1496 -36
  12. package/docs/timeout-and-error-handling.md +712 -0
  13. package/{MasterError.js → error/MasterError.js} +2 -2
  14. package/{MasterErrorLogger.js → error/MasterErrorLogger.js} +1 -1
  15. package/{MasterErrorMiddleware.js → error/MasterErrorMiddleware.js} +2 -2
  16. package/error/MasterErrorRenderer.js +529 -0
  17. package/{ssr → error}/SSRErrorHandler.js +2 -2
  18. package/{MasterCache.js → monitoring/MasterCache.js} +2 -2
  19. package/{MasterMemoryMonitor.js → monitoring/MasterMemoryMonitor.js} +2 -2
  20. package/{MasterProfiler.js → monitoring/MasterProfiler.js} +2 -2
  21. package/{ssr → monitoring}/PerformanceMonitor.js +2 -2
  22. package/package.json +5 -5
  23. package/{EventHandlerValidator.js → security/EventHandlerValidator.js} +3 -3
  24. package/{MasterSanitizer.js → security/MasterSanitizer.js} +2 -2
  25. package/{MasterValidator.js → security/MasterValidator.js} +2 -2
  26. package/{SecurityMiddleware.js → security/SecurityMiddleware.js} +75 -3
  27. package/{SessionSecurity.js → security/SessionSecurity.js} +2 -2
  28. package/ssr/hydration-client.js +3 -3
  29. package/ssr/runtime-ssr.cjs +9 -9
  30. package/MasterBenchmark.js +0 -89
  31. package/MasterBuildOptimizer.js +0 -376
  32. package/MasterBundleAnalyzer.js +0 -108
  33. package/ssr/HTMLUtils.js +0 -15
  34. /package/{ssr → error}/ErrorBoundary.js +0 -0
  35. /package/{ssr → error}/HydrationMismatch.js +0 -0
  36. /package/{MasterBackendErrorHandler.js → error/MasterBackendErrorHandler.js} +0 -0
  37. /package/{MasterErrorHandler.js → error/MasterErrorHandler.js} +0 -0
  38. /package/{CSPConfig.js → security/CSPConfig.js} +0 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(rm:*)",
5
+ "Bash(mv:*)",
6
+ "Bash(node -e:*)",
7
+ "Bash(find:*)"
8
+ ],
9
+ "deny": [],
10
+ "ask": []
11
+ }
12
+ }
package/MasterAction.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- // version 0.0.22
2
+ // version 0.0.23
3
3
 
4
4
  var master = require('./MasterControl');
5
5
  var fileserver = require('fs');
@@ -16,14 +16,14 @@ var path = require('path');
16
16
  const compileWebComponentsHTML = require('./ssr/runtime-ssr.cjs');
17
17
 
18
18
  // Enhanced error handling
19
- const { handleTemplateError, sendErrorResponse } = require('./MasterBackendErrorHandler');
20
- const { safeReadFile } = require('./MasterErrorMiddleware');
21
- const { logger } = require('./MasterErrorLogger');
19
+ const { handleTemplateError, sendErrorResponse } = require('./error/MasterBackendErrorHandler');
20
+ const { safeReadFile } = require('./error/MasterErrorMiddleware');
21
+ const { logger } = require('./error/MasterErrorLogger');
22
22
 
23
23
  // Security - CSRF, validation, sanitization
24
- const { generateCSRFToken, validateCSRFToken } = require('./SecurityMiddleware');
25
- const { validator, validateRequestBody, sanitizeObject } = require('./MasterValidator');
26
- const { sanitizeUserHTML, escapeHTML } = require('./MasterSanitizer');
24
+ const { generateCSRFToken, validateCSRFToken } = require('./security/SecurityMiddleware');
25
+ const { validator, validateRequestBody, sanitizeObject } = require('./security/MasterValidator');
26
+ const { sanitizeUserHTML, escapeHTML } = require('./security/MasterSanitizer');
27
27
 
28
28
  class MasterAction{
29
29
 
package/MasterControl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // MasterControl - by Alexander rich
2
- // version 1.0.248
2
+ // version 1.0.252
3
3
 
4
4
  var url = require('url');
5
5
  var fileserver = require('fs');
@@ -12,13 +12,13 @@ var path = require('path');
12
12
  var globSearch = require("glob");
13
13
 
14
14
  // Enhanced error handling - setup global handlers
15
- const { setupGlobalErrorHandlers } = require('./MasterErrorMiddleware');
16
- const { logger } = require('./MasterErrorLogger');
15
+ const { setupGlobalErrorHandlers } = require('./error/MasterErrorMiddleware');
16
+ const { logger } = require('./error/MasterErrorLogger');
17
17
 
18
18
  // Security - Initialize security features
19
- const { security, securityHeaders } = require('./SecurityMiddleware');
20
- const { csp } = require('./CSPConfig');
21
- const { session } = require('./SessionSecurity');
19
+ const { security, securityHeaders } = require('./security/SecurityMiddleware');
20
+ const { csp } = require('./security/CSPConfig');
21
+ const { session } = require('./security/SessionSecurity');
22
22
 
23
23
  // Initialize global error handling
24
24
  setupGlobalErrorHandlers();
@@ -192,24 +192,36 @@ class MasterControl {
192
192
 
193
193
  component(folderLocation, innerFolder){
194
194
 
195
- var rootFolderLocation = path.join(this.root, folderLocation, innerFolder);
196
- var files = globSearch.sync("**/*config.js", { cwd: rootFolderLocation, absolute: true });
197
- if(files && files.length > 0){
198
- require(files[0]);
195
+ // Enhanced: Support both relative (to master.root) and absolute paths
196
+ // If folderLocation is absolute, use it directly; otherwise join with master.root
197
+ var rootFolderLocation;
198
+ if (path.isAbsolute(folderLocation)) {
199
+ // Absolute path provided - use it directly
200
+ rootFolderLocation = path.join(folderLocation, innerFolder);
201
+ } else {
202
+ // Relative path - join with master.root (original behavior)
203
+ rootFolderLocation = path.join(this.root, folderLocation, innerFolder);
204
+ }
205
+
206
+ // Structure is always: {rootFolderLocation}/config/initializers/config.js
207
+ var configPath = path.join(rootFolderLocation, 'config', 'initializers', 'config.js');
208
+ if(fs.existsSync(configPath)){
209
+ require(configPath);
199
210
  }else{
200
- this.error.log(`Cannot find config file under ${rootFolderLocation}`, "error");
211
+ this.error.log(`Cannot find config file at ${configPath}`, "error");
201
212
  }
202
- var routeFiles = globSearch.sync("**/*routes.js", { cwd: rootFolderLocation, absolute: true });
203
- var route = routeFiles && routeFiles.length > 0 ? routeFiles[0] : null;
213
+
214
+ // Structure is always: {rootFolderLocation}/config/routes.js
215
+ var routePath = path.join(rootFolderLocation, 'config', 'routes.js');
204
216
  var routeObject = {
205
- isComponent : true,
217
+ isComponent : true,
206
218
  root : rootFolderLocation
207
219
  }
208
220
  this.router.setup(routeObject);
209
- if(route){
210
- require(route);
221
+ if(fs.existsSync(routePath)){
222
+ require(routePath);
211
223
  }else{
212
- this.error.log(`Cannot find routes file under ${rootFolderLocation}`, "error");
224
+ this.error.log(`Cannot find routes file at ${routePath}`, "error");
213
225
  }
214
226
  }
215
227
 
@@ -248,6 +260,9 @@ class MasterControl {
248
260
  // before user config initializes them.
249
261
  try {
250
262
  $that.addInternalTools([
263
+ 'MasterPipeline',
264
+ 'MasterTimeout',
265
+ 'MasterErrorRenderer',
251
266
  'MasterAction',
252
267
  'MasterActionFilters',
253
268
  'MasterRouter',
@@ -264,6 +279,10 @@ class MasterControl {
264
279
  } catch (e) {
265
280
  console.error('[MasterControl] Failed to load internal tools:', e && e.message);
266
281
  }
282
+
283
+ // Register core middleware that must run for framework to function
284
+ $that._registerCoreMiddleware();
285
+
267
286
  if(type === "http"){
268
287
  $that.serverProtocol = "http";
269
288
  return http.createServer(async function(req, res) {
@@ -420,120 +439,148 @@ class MasterControl {
420
439
  });
421
440
  }
422
441
 
423
- async serverRun(req, res){
442
+ /**
443
+ * Register core middleware that must run for the framework to function
444
+ * This includes: static files, body parsing, scoped services, routing, error handling
445
+ */
446
+ _registerCoreMiddleware(){
424
447
  var $that = this;
425
- console.log("path", `${req.method} ${req.url}`);
426
448
 
427
- // Handle CORS preflight (OPTIONS) requests early and positively
428
- if (req.method === 'OPTIONS') {
429
- try {
430
- if (this.cors && typeof this.cors.load === 'function') {
431
- if (!this.cors.options) {
432
- this.cors.init({
433
- origin: true,
434
- methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
435
- allowedHeaders: true,
436
- credentials: false,
437
- maxAge: 86400
438
- });
449
+ // 1. Static File Serving
450
+ $that.pipeline.use(async (ctx, next) => {
451
+ if (ctx.isStatic) {
452
+ // Serve static files
453
+ let pathname = `.${ctx.request.url}`;
454
+
455
+ fs.exists(pathname, function (exist) {
456
+ if (!exist) {
457
+ ctx.response.statusCode = 404;
458
+ ctx.response.end(`File ${pathname} not found!`);
459
+ return;
439
460
  }
440
- this.cors.load({ request: req, response: res });
441
- } else {
442
- res.setHeader('access-control-allow-origin', '*');
443
- res.setHeader('access-control-allow-methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
444
- if (req.headers['access-control-request-headers']) {
445
- res.setHeader('access-control-allow-headers', req.headers['access-control-request-headers']);
461
+
462
+ if (fs.statSync(pathname).isDirectory()) {
463
+ pathname += '/index' + path.parse(pathname).ext;
446
464
  }
447
- res.setHeader('access-control-max-age', '86400');
448
- }
449
- } catch (e) {
450
- res.setHeader('access-control-allow-origin', '*');
451
- res.setHeader('access-control-allow-methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
452
- if (req.headers['access-control-request-headers']) {
453
- res.setHeader('access-control-allow-headers', req.headers['access-control-request-headers']);
454
- }
455
- res.setHeader('access-control-max-age', '86400');
456
- }
457
- res.statusCode = 204;
458
- res.setHeader('content-length', '0');
459
- res.end();
460
- return;
461
- }
462
465
 
463
- // Apply CORS headers to ALL non-OPTIONS requests
464
- try {
465
- if (this.cors && typeof this.cors.load === 'function') {
466
- this.cors.load({ request: req, response: res });
467
- }
468
- } catch (e) {
469
- console.warn('CORS load failed for non-OPTIONS request:', e.message);
470
- }
466
+ fs.readFile(pathname, function(err, data) {
467
+ if (err) {
468
+ ctx.response.statusCode = 500;
469
+ ctx.response.end(`Error getting the file: ${err}.`);
470
+ } else {
471
+ const mimeType = $that.router.findMimeType(path.parse(pathname).ext);
472
+ ctx.response.setHeader('Content-type', mimeType || 'text/plain');
473
+ ctx.response.end(data);
474
+ }
475
+ });
476
+ });
471
477
 
472
- // parse URL
473
- const parsedUrl = url.parse(req.url);
474
- // extract URL path
475
- let pathname = `.${parsedUrl.pathname}`;
478
+ return; // Terminal - don't call next()
479
+ }
476
480
 
477
- // based on the URL path, extract the file extension. e.g. .js, .doc, ...
478
- const ext = path.parse(pathname).ext;
481
+ await next(); // Not static, continue pipeline
482
+ });
479
483
 
480
- // handle simple preflight configuration - might need a complex approch for all scenarios
484
+ // 2. Timeout Tracking (optional - disabled by default until init)
485
+ // Will be configured by user in config.js with master.timeout.init()
486
+ // This is just a placeholder registration - actual timeout is set in user config
481
487
 
488
+ // 3. Request Body Parsing (always needed)
489
+ $that.pipeline.use(async (ctx, next) => {
490
+ // Parse body using MasterRequest
491
+ const params = await $that.request.getRequestParam(ctx.request, ctx.response);
482
492
 
483
- // if extension exist then its a file.
484
- if(ext === ""){
485
- var requestObject = await this.middleware(req, res);
486
- if(requestObject !== -1){
487
- // HSTS header if enabled
488
- if(this.serverProtocol === 'https' && this._hstsEnabled){
489
- res.setHeader('strict-transport-security', `max-age=${this._hstsMaxAge}; includeSubDomains`);
493
+ // Merge parsed params into context
494
+ if (params && params.query) {
495
+ ctx.params.query = params.query;
490
496
  }
491
- var loadedDone = false;
492
- if (typeof $that._loadedFunc === 'function') {
493
- loadedDone = $that._loadedFunc(requestObject);
494
- if (loadedDone){
495
- require(`${this.root}/config/load`)(requestObject);
496
- }
497
+ if (params && params.formData) {
498
+ ctx.params.formData = params.formData;
497
499
  }
498
- else{
499
- require(`${this.root}/config/load`)(requestObject);
500
+
501
+ await next();
502
+ });
503
+
504
+ // 4. Load Scoped Services (per request - always needed)
505
+ $that.pipeline.use(async (ctx, next) => {
506
+ for (var key in $that._scopedList) {
507
+ var className = $that._scopedList[key];
508
+ $that.requestList[key] = new className();
500
509
  }
501
-
502
-
503
- }
504
- }
505
- else{
506
-
507
- fs.exists(pathname, function (exist) {
508
-
509
- if(!exist) {
510
- // if the file is not found, return 404
511
- res.statusCode = 404;
512
- res.end(`File ${pathname} not found!`);
513
- return;
514
- }
515
-
516
- // if is a directory search for index file matching the extension
517
- if (fs.statSync(pathname).isDirectory()) pathname += '/index' + ext;
518
-
519
- // read file from file system
520
- fs.readFile(pathname, function(err, data){
521
- if(err){
522
- res.statusCode = 500;
523
- res.end(`Error getting the file: ${err}.`);
524
- } else {
525
- const mimeType = $that.router.findMimeType(ext);
526
-
527
- // if the file is found, set Content-type and send data
528
- res.setHeader('Content-type', mimeType || 'text/plain' );
529
- res.end(data);
530
- }
531
- });
532
-
510
+ await next();
511
+ });
512
+
513
+ // 4. HSTS Header (if enabled for HTTPS)
514
+ $that.pipeline.use(async (ctx, next) => {
515
+ if ($that.serverProtocol === 'https' && $that._hstsEnabled) {
516
+ ctx.response.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
517
+ }
518
+ await next();
519
+ });
520
+
521
+ // 5. Routing (TERMINAL - always needed)
522
+ $that.pipeline.run(async (ctx) => {
523
+ // Load config/load which triggers routing
524
+ require(`${$that.root}/config/load`)(ctx);
525
+ });
526
+
527
+ // 6. Global Error Handler
528
+ $that.pipeline.useError(async (error, ctx, next) => {
529
+ logger.error({
530
+ code: 'MC_ERR_PIPELINE',
531
+ message: 'Error in middleware pipeline',
532
+ error: error.message,
533
+ stack: error.stack,
534
+ path: ctx.request.url,
535
+ method: ctx.type
533
536
  });
537
+
538
+ if (!ctx.response.headersSent) {
539
+ ctx.response.statusCode = 500;
540
+ ctx.response.setHeader('Content-Type', 'application/json');
541
+ ctx.response.end(JSON.stringify({
542
+ error: 'Internal Server Error',
543
+ message: process.env.NODE_ENV === 'production'
544
+ ? 'An error occurred'
545
+ : error.message
546
+ }));
547
+ }
548
+ });
549
+ }
550
+
551
+ async serverRun(req, res){
552
+ var $that = this;
553
+ console.log("path", `${req.method} ${req.url}`);
554
+
555
+ // Create request context for middleware pipeline
556
+ const parsedUrl = url.parse(req.url);
557
+ const pathname = parsedUrl.pathname;
558
+ const ext = path.parse(pathname).ext;
559
+
560
+ const context = {
561
+ request: req,
562
+ response: res,
563
+ requrl: url.parse(req.url, true),
564
+ pathName: pathname.replace(/^\/|\/$/g, '').toLowerCase(),
565
+ type: req.method.toLowerCase(),
566
+ params: {},
567
+ state: {}, // User-defined state shared across middleware
568
+ master: $that, // Access to framework instance
569
+ isStatic: ext !== '' // Is this a static file request?
570
+ };
571
+
572
+ // Execute middleware pipeline
573
+ try {
574
+ await $that.pipeline.execute(context);
575
+ } catch (error) {
576
+ console.error('Pipeline execution failed:', error);
577
+ if (!res.headersSent) {
578
+ res.statusCode = 500;
579
+ res.end('Internal Server Error');
580
+ }
534
581
  }
535
-
536
- } // end server()
582
+
583
+ } // end serverRun()
537
584
 
538
585
  start(server){
539
586
  this.server = server;
@@ -541,16 +588,18 @@ class MasterControl {
541
588
 
542
589
  startMVC(foldername){
543
590
  var rootFolderLocation = path.join(this.root, foldername);
544
- var files = globSearch.sync("**/*routes.js", { cwd: rootFolderLocation, absolute: true });
591
+
592
+ // Structure is always: {rootFolderLocation}/routes.js
593
+ var routePath = path.join(rootFolderLocation, 'routes.js');
545
594
  var route = {
546
- isComponent : false,
595
+ isComponent : false,
547
596
  root : `${this.root}`
548
597
  }
549
598
  this.router.setup(route);
550
- if(files && files.length > 0){
551
- require(files[0]);
599
+ if(fs.existsSync(routePath)){
600
+ require(routePath);
552
601
  }else{
553
- this.error.log(`Cannot find routes file under ${rootFolderLocation}`, "error");
602
+ this.error.log(`Cannot find routes file at ${routePath}`, "error");
554
603
  }
555
604
  }
556
605
 
@@ -558,8 +607,29 @@ class MasterControl {
558
607
  // builds and calls all the required tools to have master running completely
559
608
  addInternalTools(requiredList){
560
609
  if(requiredList.constructor === Array){
610
+ // Map module names to their new organized paths
611
+ const modulePathMap = {
612
+ 'MasterPipeline': './MasterPipeline',
613
+ 'MasterTimeout': './MasterTimeout',
614
+ 'MasterErrorRenderer': './error/MasterErrorRenderer',
615
+ 'MasterError': './error/MasterError',
616
+ 'MasterAction': './MasterAction',
617
+ 'MasterActionFilters': './MasterActionFilters',
618
+ 'MasterRouter': './MasterRouter',
619
+ 'MasterRequest': './MasterRequest',
620
+ 'MasterCors': './MasterCors',
621
+ 'MasterSession': './MasterSession',
622
+ 'MasterSocket': './MasterSocket',
623
+ 'MasterHtml': './MasterHtml',
624
+ 'MasterTemplate': './MasterTemplate',
625
+ 'MasterTools': './MasterTools',
626
+ 'TemplateOverwrite': './TemplateOverwrite'
627
+ };
628
+
561
629
  for(var i = 0; i < requiredList.length; i++){
562
- require('./' + requiredList[i]);
630
+ const moduleName = requiredList[i];
631
+ const modulePath = modulePathMap[moduleName] || './' + moduleName;
632
+ require(modulePath);
563
633
  }
564
634
  }
565
635
  }
package/MasterCors.js CHANGED
@@ -11,6 +11,13 @@ class MasterCors{
11
11
  else{
12
12
  master.error.log("cors options missing", "warn");
13
13
  }
14
+
15
+ // Auto-register with pipeline if available
16
+ if (master.pipeline) {
17
+ master.pipeline.use(this.middleware());
18
+ }
19
+
20
+ return this; // Chainable
14
21
  }
15
22
 
16
23
  load(params){
@@ -167,6 +174,28 @@ class MasterCors{
167
174
  }
168
175
  }
169
176
  }
177
+
178
+ /**
179
+ * Get CORS middleware for the pipeline
180
+ * Handles both preflight OPTIONS requests and regular requests
181
+ */
182
+ middleware() {
183
+ var $that = this;
184
+
185
+ return async (ctx, next) => {
186
+ // Handle preflight OPTIONS request
187
+ if (ctx.type === 'options') {
188
+ $that.load({ request: ctx.request, response: ctx.response });
189
+ ctx.response.statusCode = 204;
190
+ ctx.response.end();
191
+ return; // Terminal - don't call next()
192
+ }
193
+
194
+ // Regular request - apply CORS headers
195
+ $that.load({ request: ctx.request, response: ctx.response });
196
+ await next();
197
+ };
198
+ }
170
199
  }
171
200
 
172
201
  master.extend("cors", MasterCors);
package/MasterHtml.js CHANGED
@@ -1,4 +1,4 @@
1
- // version 0.0.24
1
+ // version 0.0.25
2
2
 
3
3
  var master = require('./MasterControl');
4
4
  var fs = require('fs');
@@ -8,12 +8,12 @@ var temp = new tempClass();
8
8
  var tools = new toolClass();
9
9
 
10
10
  // Enhanced error handling
11
- const { handleTemplateError } = require('./MasterBackendErrorHandler');
12
- const { safeReadFile, safeFileExists } = require('./MasterErrorMiddleware');
13
- const { logger } = require('./MasterErrorLogger');
11
+ const { handleTemplateError } = require('./error/MasterBackendErrorHandler');
12
+ const { safeReadFile, safeFileExists } = require('./error/MasterErrorMiddleware');
13
+ const { logger } = require('./error/MasterErrorLogger');
14
14
 
15
15
  // Security - Sanitization
16
- const { sanitizeTemplateHTML, sanitizeUserHTML, escapeHTML } = require('./MasterSanitizer');
16
+ const { sanitizeTemplateHTML, sanitizeUserHTML, escapeHTML } = require('./security/MasterSanitizer');
17
17
 
18
18
  class html {
19
19