mastercontroller 1.2.12 → 1.2.13

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/MasterCache.js ADDED
@@ -0,0 +1,400 @@
1
+ // version 1.0.0
2
+ // MasterController Cache System - Runtime Performance Optimization
3
+
4
+ /**
5
+ * Multi-level cache system for MasterController
6
+ * - Event manifest caching
7
+ * - Component render caching
8
+ * - Template caching
9
+ * - LRU eviction
10
+ * - TTL support
11
+ */
12
+
13
+ const { logger } = require('./MasterErrorLogger');
14
+
15
+ /**
16
+ * LRU Cache with TTL support
17
+ */
18
+ class LRUCache {
19
+ constructor(options = {}) {
20
+ this.maxSize = options.maxSize || 100;
21
+ this.ttl = options.ttl || 3600000; // 1 hour default
22
+ this.cache = new Map();
23
+ this.accessOrder = [];
24
+
25
+ // Statistics
26
+ this.hits = 0;
27
+ this.misses = 0;
28
+ this.evictions = 0;
29
+ }
30
+
31
+ /**
32
+ * Get value from cache
33
+ */
34
+ get(key) {
35
+ const entry = this.cache.get(key);
36
+
37
+ if (!entry) {
38
+ this.misses++;
39
+ return null;
40
+ }
41
+
42
+ // Check TTL
43
+ if (Date.now() > entry.expiry) {
44
+ this.cache.delete(key);
45
+ this.accessOrder = this.accessOrder.filter(k => k !== key);
46
+ this.misses++;
47
+ return null;
48
+ }
49
+
50
+ // Update access order (move to end)
51
+ this.accessOrder = this.accessOrder.filter(k => k !== key);
52
+ this.accessOrder.push(key);
53
+
54
+ this.hits++;
55
+ return entry.value;
56
+ }
57
+
58
+ /**
59
+ * Set value in cache
60
+ */
61
+ set(key, value, ttl = null) {
62
+ // Remove if already exists
63
+ if (this.cache.has(key)) {
64
+ this.accessOrder = this.accessOrder.filter(k => k !== key);
65
+ }
66
+
67
+ // Evict if at capacity
68
+ if (this.cache.size >= this.maxSize) {
69
+ const oldestKey = this.accessOrder.shift();
70
+ this.cache.delete(oldestKey);
71
+ this.evictions++;
72
+ }
73
+
74
+ // Add new entry
75
+ this.cache.set(key, {
76
+ value,
77
+ expiry: Date.now() + (ttl || this.ttl)
78
+ });
79
+
80
+ this.accessOrder.push(key);
81
+ }
82
+
83
+ /**
84
+ * Check if key exists
85
+ */
86
+ has(key) {
87
+ return this.get(key) !== null;
88
+ }
89
+
90
+ /**
91
+ * Delete entry
92
+ */
93
+ delete(key) {
94
+ this.cache.delete(key);
95
+ this.accessOrder = this.accessOrder.filter(k => k !== key);
96
+ }
97
+
98
+ /**
99
+ * Clear cache
100
+ */
101
+ clear() {
102
+ this.cache.clear();
103
+ this.accessOrder = [];
104
+ this.hits = 0;
105
+ this.misses = 0;
106
+ this.evictions = 0;
107
+ }
108
+
109
+ /**
110
+ * Get cache statistics
111
+ */
112
+ getStats() {
113
+ const total = this.hits + this.misses;
114
+ const hitRate = total > 0 ? (this.hits / total * 100).toFixed(2) : 0;
115
+
116
+ return {
117
+ size: this.cache.size,
118
+ maxSize: this.maxSize,
119
+ hits: this.hits,
120
+ misses: this.misses,
121
+ evictions: this.evictions,
122
+ hitRate: `${hitRate}%`
123
+ };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * MasterController Cache Manager
129
+ */
130
+ class MasterCache {
131
+ constructor(options = {}) {
132
+ // Event manifest cache
133
+ this.manifestCache = new LRUCache({
134
+ maxSize: options.manifestCacheSize || 50,
135
+ ttl: options.manifestTTL || 3600000 // 1 hour
136
+ });
137
+
138
+ // Component render cache
139
+ this.renderCache = new LRUCache({
140
+ maxSize: options.renderCacheSize || 200,
141
+ ttl: options.renderTTL || 300000 // 5 minutes
142
+ });
143
+
144
+ // Template cache
145
+ this.templateCache = new LRUCache({
146
+ maxSize: options.templateCacheSize || 100,
147
+ ttl: options.templateTTL || 3600000 // 1 hour
148
+ });
149
+
150
+ // Module cache (for require/import)
151
+ this.moduleCache = new Map();
152
+
153
+ // Enabled flag
154
+ this.enabled = options.enabled !== false;
155
+ }
156
+
157
+ /**
158
+ * Cache event manifest
159
+ */
160
+ cacheManifest(componentName, manifest) {
161
+ if (!this.enabled) return;
162
+
163
+ const key = `manifest:${componentName}`;
164
+ this.manifestCache.set(key, manifest);
165
+
166
+ logger.debug({
167
+ code: 'MC_CACHE_MANIFEST',
168
+ message: `Cached manifest for ${componentName}`,
169
+ size: JSON.stringify(manifest).length
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Get cached event manifest
175
+ */
176
+ getManifest(componentName) {
177
+ if (!this.enabled) return null;
178
+
179
+ const key = `manifest:${componentName}`;
180
+ return this.manifestCache.get(key);
181
+ }
182
+
183
+ /**
184
+ * Cache component render output
185
+ */
186
+ cacheRender(componentName, props, html) {
187
+ if (!this.enabled) return;
188
+
189
+ // Create cache key from component name and props
190
+ const propsKey = JSON.stringify(props || {});
191
+ const key = `render:${componentName}:${this.hashString(propsKey)}`;
192
+
193
+ this.renderCache.set(key, html);
194
+
195
+ logger.debug({
196
+ code: 'MC_CACHE_RENDER',
197
+ message: `Cached render for ${componentName}`,
198
+ size: html.length
199
+ });
200
+ }
201
+
202
+ /**
203
+ * Get cached component render
204
+ */
205
+ getCachedRender(componentName, props) {
206
+ if (!this.enabled) return null;
207
+
208
+ const propsKey = JSON.stringify(props || {});
209
+ const key = `render:${componentName}:${this.hashString(propsKey)}`;
210
+
211
+ return this.renderCache.get(key);
212
+ }
213
+
214
+ /**
215
+ * Cache template
216
+ */
217
+ cacheTemplate(templatePath, compiled) {
218
+ if (!this.enabled) return;
219
+
220
+ const key = `template:${templatePath}`;
221
+ this.templateCache.set(key, compiled);
222
+ }
223
+
224
+ /**
225
+ * Get cached template
226
+ */
227
+ getTemplate(templatePath) {
228
+ if (!this.enabled) return null;
229
+
230
+ const key = `template:${templatePath}`;
231
+ return this.templateCache.get(key);
232
+ }
233
+
234
+ /**
235
+ * Cache module (require/import result)
236
+ */
237
+ cacheModule(modulePath, exports) {
238
+ if (!this.enabled) return;
239
+
240
+ this.moduleCache.set(modulePath, exports);
241
+ }
242
+
243
+ /**
244
+ * Get cached module
245
+ */
246
+ getModule(modulePath) {
247
+ if (!this.enabled) return null;
248
+
249
+ return this.moduleCache.get(modulePath);
250
+ }
251
+
252
+ /**
253
+ * Invalidate cache for component
254
+ */
255
+ invalidateComponent(componentName) {
256
+ // Clear manifest
257
+ const manifestKey = `manifest:${componentName}`;
258
+ this.manifestCache.delete(manifestKey);
259
+
260
+ // Clear all renders for this component
261
+ // (We'd need to track which keys belong to which components for this)
262
+ // For now, just clear the entire render cache
263
+ this.renderCache.clear();
264
+
265
+ logger.info({
266
+ code: 'MC_CACHE_INVALIDATE',
267
+ message: `Cache invalidated for ${componentName}`
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Clear all caches
273
+ */
274
+ clearAll() {
275
+ this.manifestCache.clear();
276
+ this.renderCache.clear();
277
+ this.templateCache.clear();
278
+ this.moduleCache.clear();
279
+
280
+ logger.info({
281
+ code: 'MC_CACHE_CLEAR',
282
+ message: 'All caches cleared'
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Get cache statistics
288
+ */
289
+ getStats() {
290
+ return {
291
+ manifest: this.manifestCache.getStats(),
292
+ render: this.renderCache.getStats(),
293
+ template: this.templateCache.getStats(),
294
+ module: {
295
+ size: this.moduleCache.size
296
+ }
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Log cache statistics
302
+ */
303
+ logStats() {
304
+ const stats = this.getStats();
305
+
306
+ console.log('\n═══════════════════════════════════════════════════');
307
+ console.log('📊 MasterController Cache Statistics');
308
+ console.log('═══════════════════════════════════════════════════');
309
+
310
+ console.log('\nManifest Cache:');
311
+ console.log(` Size: ${stats.manifest.size}/${stats.manifest.maxSize}`);
312
+ console.log(` Hits: ${stats.manifest.hits}`);
313
+ console.log(` Misses: ${stats.manifest.misses}`);
314
+ console.log(` Hit Rate: ${stats.manifest.hitRate}`);
315
+ console.log(` Evictions: ${stats.manifest.evictions}`);
316
+
317
+ console.log('\nRender Cache:');
318
+ console.log(` Size: ${stats.render.size}/${stats.render.maxSize}`);
319
+ console.log(` Hits: ${stats.render.hits}`);
320
+ console.log(` Misses: ${stats.render.misses}`);
321
+ console.log(` Hit Rate: ${stats.render.hitRate}`);
322
+ console.log(` Evictions: ${stats.render.evictions}`);
323
+
324
+ console.log('\nTemplate Cache:');
325
+ console.log(` Size: ${stats.template.size}/${stats.template.maxSize}`);
326
+ console.log(` Hits: ${stats.template.hits}`);
327
+ console.log(` Misses: ${stats.template.misses}`);
328
+ console.log(` Hit Rate: ${stats.template.hitRate}`);
329
+ console.log(` Evictions: ${stats.template.evictions}`);
330
+
331
+ console.log('\nModule Cache:');
332
+ console.log(` Size: ${stats.module.size}`);
333
+
334
+ console.log('═══════════════════════════════════════════════════\n');
335
+ }
336
+
337
+ /**
338
+ * Simple string hash function
339
+ */
340
+ hashString(str) {
341
+ let hash = 0;
342
+ for (let i = 0; i < str.length; i++) {
343
+ const char = str.charCodeAt(i);
344
+ hash = ((hash << 5) - hash) + char;
345
+ hash = hash & hash; // Convert to 32bit integer
346
+ }
347
+ return hash.toString(36);
348
+ }
349
+
350
+ /**
351
+ * Enable cache
352
+ */
353
+ enable() {
354
+ this.enabled = true;
355
+ logger.info({
356
+ code: 'MC_CACHE_ENABLED',
357
+ message: 'Cache enabled'
358
+ });
359
+ }
360
+
361
+ /**
362
+ * Disable cache
363
+ */
364
+ disable() {
365
+ this.enabled = false;
366
+ logger.info({
367
+ code: 'MC_CACHE_DISABLED',
368
+ message: 'Cache disabled'
369
+ });
370
+ }
371
+ }
372
+
373
+ // Create singleton instance
374
+ const cache = new MasterCache({
375
+ manifestCacheSize: 50,
376
+ renderCacheSize: 200,
377
+ templateCacheSize: 100,
378
+ manifestTTL: 3600000, // 1 hour
379
+ renderTTL: 300000, // 5 minutes
380
+ templateTTL: 3600000, // 1 hour
381
+ enabled: process.env.NODE_ENV === 'production' || process.env.MC_CACHE_ENABLED === 'true'
382
+ });
383
+
384
+ // Auto-cleanup interval (every 5 minutes)
385
+ setInterval(() => {
386
+ // Force garbage collection of expired entries
387
+ const stats = cache.getStats();
388
+
389
+ logger.debug({
390
+ code: 'MC_CACHE_CLEANUP',
391
+ message: 'Cache cleanup running',
392
+ stats
393
+ });
394
+ }, 300000);
395
+
396
+ module.exports = {
397
+ MasterCache,
398
+ LRUCache,
399
+ cache
400
+ };
package/MasterControl.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // MasterControl - by Alexander rich
2
- // version 1.0.247
2
+ // version 1.0.248
3
3
 
4
4
  var url = require('url');
5
5
  var fileserver = require('fs');
@@ -11,6 +11,47 @@ var url = require('url');
11
11
  var path = require('path');
12
12
  var globSearch = require("glob");
13
13
 
14
+ // Enhanced error handling - setup global handlers
15
+ const { setupGlobalErrorHandlers } = require('./MasterErrorMiddleware');
16
+ const { logger } = require('./MasterErrorLogger');
17
+
18
+ // Security - Initialize security features
19
+ const { security, securityHeaders } = require('./SecurityMiddleware');
20
+ const { csp } = require('./CSPConfig');
21
+ const { session } = require('./SessionSecurity');
22
+
23
+ // Initialize global error handling
24
+ setupGlobalErrorHandlers();
25
+
26
+ // Log framework start
27
+ logger.info({
28
+ code: 'MC_INFO_FRAMEWORK_START',
29
+ message: 'MasterController framework initializing',
30
+ context: {
31
+ version: '1.0.247',
32
+ nodeVersion: process.version,
33
+ platform: process.platform,
34
+ env: process.env.NODE_ENV || 'development'
35
+ }
36
+ });
37
+
38
+ // Log security status
39
+ const isProduction = process.env.NODE_ENV === 'production';
40
+ logger.info({
41
+ code: 'MC_INFO_SECURITY_INITIALIZED',
42
+ message: 'Security features initialized',
43
+ context: {
44
+ environment: isProduction ? 'production' : 'development',
45
+ features: {
46
+ securityHeaders: true,
47
+ csp: csp.enabled,
48
+ csrf: security.csrfEnabled,
49
+ rateLimit: security.rateLimitEnabled,
50
+ sessionSecurity: true
51
+ }
52
+ }
53
+ });
54
+
14
55
 
15
56
  class MasterControl {
16
57
  controllerList = {}
@@ -161,7 +202,7 @@ class MasterControl {
161
202
  var routeFiles = globSearch.sync("**/*routes.js", { cwd: rootFolderLocation, absolute: true });
162
203
  var route = routeFiles && routeFiles.length > 0 ? routeFiles[0] : null;
163
204
  var routeObject = {
164
- isComponent : true,
205
+ isComponent : true,
165
206
  root : rootFolderLocation
166
207
  }
167
208
  this.router.setup(routeObject);
@@ -203,6 +244,26 @@ class MasterControl {
203
244
  setupServer(type, credentials ){
204
245
  try {
205
246
  var $that = this;
247
+ // Auto-load internal master tools so services (request, error, router, etc.) are available
248
+ // before user config initializes them.
249
+ try {
250
+ $that.addInternalTools([
251
+ 'MasterAction',
252
+ 'MasterActionFilters',
253
+ 'MasterRouter',
254
+ 'MasterRequest',
255
+ 'MasterError',
256
+ 'MasterCors',
257
+ 'MasterSession',
258
+ 'MasterSocket',
259
+ 'MasterHtml',
260
+ 'MasterTemplate',
261
+ 'MasterTools',
262
+ 'TemplateOverwrite'
263
+ ]);
264
+ } catch (e) {
265
+ console.error('[MasterControl] Failed to load internal tools:', e && e.message);
266
+ }
206
267
  if(type === "http"){
207
268
  $that.serverProtocol = "http";
208
269
  return http.createServer(async function(req, res) {
@@ -399,16 +460,25 @@ class MasterControl {
399
460
  return;
400
461
  }
401
462
 
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
+ }
471
+
402
472
  // parse URL
403
473
  const parsedUrl = url.parse(req.url);
404
474
  // extract URL path
405
475
  let pathname = `.${parsedUrl.pathname}`;
406
-
476
+
407
477
  // based on the URL path, extract the file extension. e.g. .js, .doc, ...
408
478
  const ext = path.parse(pathname).ext;
409
-
479
+
410
480
  // handle simple preflight configuration - might need a complex approch for all scenarios
411
-
481
+
412
482
 
413
483
  // if extension exist then its a file.
414
484
  if(ext === ""){
@@ -480,7 +550,7 @@ class MasterControl {
480
550
  if(files && files.length > 0){
481
551
  require(files[0]);
482
552
  }else{
483
- master.error.log(`Cannot find routes file under ${rootFolderLocation}`, "error");
553
+ this.error.log(`Cannot find routes file under ${rootFolderLocation}`, "error");
484
554
  }
485
555
  }
486
556