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.
- package/.claude/settings.local.json +12 -0
- package/MasterAction.js +7 -7
- package/MasterControl.js +192 -122
- package/MasterCors.js +29 -0
- package/MasterHtml.js +5 -5
- package/MasterPipeline.js +344 -0
- package/MasterRouter.js +59 -29
- package/MasterSession.js +19 -0
- package/MasterTemplate.js +3 -3
- package/MasterTimeout.js +332 -0
- package/README.md +1496 -36
- package/docs/timeout-and-error-handling.md +712 -0
- package/{MasterError.js → error/MasterError.js} +2 -2
- package/{MasterErrorLogger.js → error/MasterErrorLogger.js} +1 -1
- package/{MasterErrorMiddleware.js → error/MasterErrorMiddleware.js} +2 -2
- package/error/MasterErrorRenderer.js +529 -0
- package/{ssr → error}/SSRErrorHandler.js +2 -2
- package/{MasterCache.js → monitoring/MasterCache.js} +2 -2
- package/{MasterMemoryMonitor.js → monitoring/MasterMemoryMonitor.js} +2 -2
- package/{MasterProfiler.js → monitoring/MasterProfiler.js} +2 -2
- package/{ssr → monitoring}/PerformanceMonitor.js +2 -2
- package/package.json +5 -5
- package/{EventHandlerValidator.js → security/EventHandlerValidator.js} +3 -3
- package/{MasterSanitizer.js → security/MasterSanitizer.js} +2 -2
- package/{MasterValidator.js → security/MasterValidator.js} +2 -2
- package/{SecurityMiddleware.js → security/SecurityMiddleware.js} +75 -3
- package/{SessionSecurity.js → security/SessionSecurity.js} +2 -2
- package/ssr/hydration-client.js +3 -3
- package/ssr/runtime-ssr.cjs +9 -9
- package/MasterBenchmark.js +0 -89
- package/MasterBuildOptimizer.js +0 -376
- package/MasterBundleAnalyzer.js +0 -108
- package/ssr/HTMLUtils.js +0 -15
- /package/{ssr → error}/ErrorBoundary.js +0 -0
- /package/{ssr → error}/HydrationMismatch.js +0 -0
- /package/{MasterBackendErrorHandler.js → error/MasterBackendErrorHandler.js} +0 -0
- /package/{MasterErrorHandler.js → error/MasterErrorHandler.js} +0 -0
- /package/{CSPConfig.js → security/CSPConfig.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// version 1.0.
|
|
1
|
+
// version 1.0.1
|
|
2
2
|
// MasterController Security Middleware - CSRF, Headers, Rate Limiting, CORS
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const crypto = require('crypto');
|
|
10
|
-
const { logger } = require('
|
|
10
|
+
const { logger } = require('../error/MasterErrorLogger');
|
|
11
11
|
|
|
12
12
|
// Rate limiting store
|
|
13
13
|
const rateLimitStore = new Map();
|
|
@@ -473,6 +473,73 @@ function validateCSRFToken(token) {
|
|
|
473
473
|
return security.validateCSRFToken(token);
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Pipeline-compatible middleware wrappers
|
|
478
|
+
* These adapt from (ctx, next) format to (req, res, next) format
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
function pipelineSecurityHeaders(options = {}) {
|
|
482
|
+
const instance = options.instance || security;
|
|
483
|
+
return async (ctx, next) => {
|
|
484
|
+
// Create next callback for old-style middleware
|
|
485
|
+
let nextCalled = false;
|
|
486
|
+
const oldNext = () => { nextCalled = true; };
|
|
487
|
+
|
|
488
|
+
// Call old middleware
|
|
489
|
+
instance.securityHeadersMiddleware(ctx.request, ctx.response, oldNext);
|
|
490
|
+
|
|
491
|
+
// Continue pipeline if next was called
|
|
492
|
+
if (nextCalled) {
|
|
493
|
+
await next();
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function pipelineCors(options = {}) {
|
|
499
|
+
const instance = new SecurityMiddleware({ ...options, headers: false, csrf: false, rateLimit: false });
|
|
500
|
+
return async (ctx, next) => {
|
|
501
|
+
let nextCalled = false;
|
|
502
|
+
const oldNext = () => { nextCalled = true; };
|
|
503
|
+
|
|
504
|
+
instance.corsMiddleware(ctx.request, ctx.response, oldNext);
|
|
505
|
+
|
|
506
|
+
// CORS might terminate for OPTIONS - check if response ended
|
|
507
|
+
if (!ctx.response.writableEnded && nextCalled) {
|
|
508
|
+
await next();
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function pipelineRateLimit(options = {}) {
|
|
514
|
+
const instance = new SecurityMiddleware({ ...options, headers: false, csrf: false, cors: false });
|
|
515
|
+
return async (ctx, next) => {
|
|
516
|
+
let nextCalled = false;
|
|
517
|
+
const oldNext = () => { nextCalled = true; };
|
|
518
|
+
|
|
519
|
+
instance.rateLimitMiddleware(ctx.request, ctx.response, oldNext);
|
|
520
|
+
|
|
521
|
+
// Rate limit might terminate - check if response ended
|
|
522
|
+
if (!ctx.response.writableEnded && nextCalled) {
|
|
523
|
+
await next();
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function pipelineCsrf(options = {}) {
|
|
529
|
+
const instance = new SecurityMiddleware({ ...options, headers: false, cors: false, rateLimit: false });
|
|
530
|
+
return async (ctx, next) => {
|
|
531
|
+
let nextCalled = false;
|
|
532
|
+
const oldNext = () => { nextCalled = true; };
|
|
533
|
+
|
|
534
|
+
instance.csrfMiddleware(ctx.request, ctx.response, oldNext);
|
|
535
|
+
|
|
536
|
+
// CSRF might terminate - check if response ended
|
|
537
|
+
if (!ctx.response.writableEnded && nextCalled) {
|
|
538
|
+
await next();
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
476
543
|
module.exports = {
|
|
477
544
|
SecurityMiddleware,
|
|
478
545
|
security,
|
|
@@ -482,5 +549,10 @@ module.exports = {
|
|
|
482
549
|
csrf,
|
|
483
550
|
generateCSRFToken,
|
|
484
551
|
validateCSRFToken,
|
|
485
|
-
SECURITY_HEADERS
|
|
552
|
+
SECURITY_HEADERS,
|
|
553
|
+
// Pipeline-compatible exports
|
|
554
|
+
pipelineSecurityHeaders,
|
|
555
|
+
pipelineCors,
|
|
556
|
+
pipelineRateLimit,
|
|
557
|
+
pipelineCsrf
|
|
486
558
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// version 1.0.
|
|
1
|
+
// version 1.0.1
|
|
2
2
|
// MasterController Session Security - Secure cookie handling, session fixation prevention
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const crypto = require('crypto');
|
|
10
|
-
const { logger } = require('
|
|
10
|
+
const { logger } = require('../error/MasterErrorLogger');
|
|
11
11
|
|
|
12
12
|
// Session store (use Redis in production)
|
|
13
13
|
const sessionStore = new Map();
|
package/ssr/hydration-client.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MasterController Client-Side Hydration Runtime
|
|
3
3
|
* Handles error boundaries and hydration mismatch detection
|
|
4
|
-
* Version: 2.0.
|
|
4
|
+
* Version: 2.0.1
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Import error boundary
|
|
8
|
-
import { ErrorBoundary } from '
|
|
8
|
+
import { ErrorBoundary } from '../error/ErrorBoundary.js';
|
|
9
9
|
|
|
10
10
|
// Import hydration mismatch detection
|
|
11
11
|
const isDevelopment = window.location.hostname === 'localhost' ||
|
|
@@ -13,7 +13,7 @@ const isDevelopment = window.location.hostname === 'localhost' ||
|
|
|
13
13
|
|
|
14
14
|
if (isDevelopment && typeof require !== 'undefined') {
|
|
15
15
|
try {
|
|
16
|
-
const { enableHydrationMismatchDetection } = require('
|
|
16
|
+
const { enableHydrationMismatchDetection } = require('../error/HydrationMismatch.js');
|
|
17
17
|
enableHydrationMismatchDetection({
|
|
18
18
|
verbose: localStorage.getItem('mc-hydration-debug') === 'true',
|
|
19
19
|
delay: 1000
|
package/ssr/runtime-ssr.cjs
CHANGED
|
@@ -13,18 +13,18 @@ const vm = require('vm');
|
|
|
13
13
|
const moduleCache = new Map();
|
|
14
14
|
|
|
15
15
|
// Error handling and monitoring
|
|
16
|
-
const { MasterControllerError, findSimilarStrings } = require('../MasterErrorHandler');
|
|
17
|
-
const { safeRenderComponent, validateSSRComponent, wrapConnectedCallback } = require('
|
|
18
|
-
const { monitor } = require('
|
|
19
|
-
const { logger } = require('../MasterErrorLogger');
|
|
16
|
+
const { MasterControllerError, findSimilarStrings } = require('../error/MasterErrorHandler');
|
|
17
|
+
const { safeRenderComponent, validateSSRComponent, wrapConnectedCallback } = require('../error/SSRErrorHandler');
|
|
18
|
+
const { monitor } = require('../monitoring/PerformanceMonitor');
|
|
19
|
+
const { logger } = require('../error/MasterErrorLogger');
|
|
20
20
|
|
|
21
21
|
// Security - Sanitization and validation
|
|
22
|
-
const { sanitizer, sanitizeTemplateHTML, sanitizeProps } = require('../MasterSanitizer');
|
|
23
|
-
const { validateEventAttribute } = require('../EventHandlerValidator');
|
|
22
|
+
const { sanitizer, sanitizeTemplateHTML, sanitizeProps } = require('../security/MasterSanitizer');
|
|
23
|
+
const { validateEventAttribute } = require('../security/EventHandlerValidator');
|
|
24
24
|
|
|
25
25
|
// Performance - Caching and profiling
|
|
26
|
-
const { cache } = require('../MasterCache');
|
|
27
|
-
const { profiler } = require('../MasterProfiler');
|
|
26
|
+
const { cache } = require('../monitoring/MasterCache');
|
|
27
|
+
const { profiler } = require('../monitoring/MasterProfiler');
|
|
28
28
|
|
|
29
29
|
// Track registered custom elements to detect duplicates
|
|
30
30
|
const registeredElements = new Map();
|
|
@@ -462,7 +462,7 @@ module.exports = async function compileWebComponentsHTML(inputHTML, preloadModul
|
|
|
462
462
|
if (isDevelopment) {
|
|
463
463
|
el.innerHTML = error.toHTML();
|
|
464
464
|
} else {
|
|
465
|
-
const { renderFallback } = require('
|
|
465
|
+
const { renderFallback } = require('../error/SSRErrorHandler');
|
|
466
466
|
el.innerHTML = renderFallback(componentName);
|
|
467
467
|
}
|
|
468
468
|
}
|
package/MasterBenchmark.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// version 1.0.0
|
|
2
|
-
// MasterController Benchmark - Performance Benchmarking
|
|
3
|
-
|
|
4
|
-
const { profiler } = require('./MasterProfiler');
|
|
5
|
-
const { logger } = require('./MasterErrorLogger');
|
|
6
|
-
|
|
7
|
-
class MasterBenchmark {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.results = [];
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Benchmark SSR render time
|
|
14
|
-
*/
|
|
15
|
-
async benchmarkSSR(component, iterations = 100) {
|
|
16
|
-
const times = [];
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < iterations; i++) {
|
|
19
|
-
const start = Date.now();
|
|
20
|
-
|
|
21
|
-
// Render component (would call actual SSR here)
|
|
22
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
23
|
-
|
|
24
|
-
const duration = Date.now() - start;
|
|
25
|
-
times.push(duration);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return this.calculateStats(times, 'SSR Render');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Benchmark hydration time
|
|
33
|
-
*/
|
|
34
|
-
async benchmarkHydration(iterations = 100) {
|
|
35
|
-
const times = [];
|
|
36
|
-
|
|
37
|
-
for (let i = 0; i < iterations; i++) {
|
|
38
|
-
const start = Date.now();
|
|
39
|
-
|
|
40
|
-
// Simulate hydration
|
|
41
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
42
|
-
|
|
43
|
-
const duration = Date.now() - start;
|
|
44
|
-
times.push(duration);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return this.calculateStats(times, 'Hydration');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Calculate statistics
|
|
52
|
-
*/
|
|
53
|
-
calculateStats(times, name) {
|
|
54
|
-
const sorted = times.sort((a, b) => a - b);
|
|
55
|
-
const sum = times.reduce((a, b) => a + b, 0);
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
name,
|
|
59
|
-
iterations: times.length,
|
|
60
|
-
min: sorted[0],
|
|
61
|
-
max: sorted[sorted.length - 1],
|
|
62
|
-
mean: sum / times.length,
|
|
63
|
-
median: sorted[Math.floor(times.length / 2)],
|
|
64
|
-
p95: sorted[Math.floor(times.length * 0.95)],
|
|
65
|
-
p99: sorted[Math.floor(times.length * 0.99)]
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Print benchmark results
|
|
71
|
-
*/
|
|
72
|
-
printResults(stats) {
|
|
73
|
-
console.log('\n═══════════════════════════════════════════════════');
|
|
74
|
-
console.log('⚡ MasterController Benchmark Results');
|
|
75
|
-
console.log('═══════════════════════════════════════════════════');
|
|
76
|
-
|
|
77
|
-
console.log(`\n${stats.name} (${stats.iterations} iterations):`);
|
|
78
|
-
console.log(` Mean: ${stats.mean.toFixed(2)}ms`);
|
|
79
|
-
console.log(` Median: ${stats.median.toFixed(2)}ms`);
|
|
80
|
-
console.log(` Min: ${stats.min.toFixed(2)}ms`);
|
|
81
|
-
console.log(` Max: ${stats.max.toFixed(2)}ms`);
|
|
82
|
-
console.log(` P95: ${stats.p95.toFixed(2)}ms`);
|
|
83
|
-
console.log(` P99: ${stats.p99.toFixed(2)}ms`);
|
|
84
|
-
|
|
85
|
-
console.log('═══════════════════════════════════════════════════\n');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
module.exports = { MasterBenchmark };
|
package/MasterBuildOptimizer.js
DELETED
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
// version 1.0.0
|
|
2
|
-
// MasterController Build Optimizer - Minification, Tree Shaking, Dead Code Elimination
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Build-time optimizations for MasterController
|
|
6
|
-
* - Minifies event manifests
|
|
7
|
-
* - Eliminates dead code
|
|
8
|
-
* - Tree shaking for unused components
|
|
9
|
-
* - Optimizes bundle size
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const path = require('path');
|
|
14
|
-
const { logger } = require('./MasterErrorLogger');
|
|
15
|
-
|
|
16
|
-
class MasterBuildOptimizer {
|
|
17
|
-
constructor(options = {}) {
|
|
18
|
-
this.rootDir = options.rootDir || process.cwd();
|
|
19
|
-
this.outputDir = options.outputDir || path.join(this.rootDir, 'public', '__compiled__');
|
|
20
|
-
this.minify = options.minify !== false;
|
|
21
|
-
this.treeShake = options.treeShake !== false;
|
|
22
|
-
this.deadCodeElimination = options.deadCodeElimination !== false;
|
|
23
|
-
this.sourceMaps = options.sourceMaps || false;
|
|
24
|
-
|
|
25
|
-
// Statistics
|
|
26
|
-
this.stats = {
|
|
27
|
-
filesProcessed: 0,
|
|
28
|
-
bytesOriginal: 0,
|
|
29
|
-
bytesOptimized: 0,
|
|
30
|
-
timeTaken: 0,
|
|
31
|
-
componentsFound: 0,
|
|
32
|
-
componentsUsed: 0,
|
|
33
|
-
componentsRemoved: 0
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Optimize entire build
|
|
39
|
-
*/
|
|
40
|
-
async optimize() {
|
|
41
|
-
const startTime = Date.now();
|
|
42
|
-
|
|
43
|
-
logger.info({
|
|
44
|
-
code: 'MC_PERF_BUILD_START',
|
|
45
|
-
message: 'Build optimization started'
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
// 1. Analyze component usage
|
|
50
|
-
const usageMap = await this.analyzeComponentUsage();
|
|
51
|
-
|
|
52
|
-
// 2. Tree shake unused components
|
|
53
|
-
if (this.treeShake) {
|
|
54
|
-
await this.treeShakeComponents(usageMap);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 3. Minify event manifests
|
|
58
|
-
if (this.minify) {
|
|
59
|
-
await this.minifyEventManifests();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 4. Eliminate dead code
|
|
63
|
-
if (this.deadCodeElimination) {
|
|
64
|
-
await this.eliminateDeadCode();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 5. Optimize bundle
|
|
68
|
-
await this.optimizeBundle();
|
|
69
|
-
|
|
70
|
-
this.stats.timeTaken = Date.now() - startTime;
|
|
71
|
-
|
|
72
|
-
// Log results
|
|
73
|
-
this.logOptimizationResults();
|
|
74
|
-
|
|
75
|
-
return this.stats;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
logger.error({
|
|
78
|
-
code: 'MC_PERF_BUILD_ERROR',
|
|
79
|
-
message: 'Build optimization failed',
|
|
80
|
-
error: error.message
|
|
81
|
-
});
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Analyze which components are actually used in the application
|
|
88
|
-
*/
|
|
89
|
-
async analyzeComponentUsage() {
|
|
90
|
-
const usageMap = new Map();
|
|
91
|
-
|
|
92
|
-
// Scan all view files
|
|
93
|
-
const viewsDir = path.join(this.rootDir, 'app', 'views');
|
|
94
|
-
const viewFiles = this.findFiles(viewsDir, ['.html', '.js']);
|
|
95
|
-
|
|
96
|
-
for (const file of viewFiles) {
|
|
97
|
-
const content = fs.readFileSync(file, 'utf8');
|
|
98
|
-
|
|
99
|
-
// Find custom element usages (<ui-button>, <ui-calendar>, etc.)
|
|
100
|
-
const customElements = content.match(/<([a-z]+-[a-z-]+)/g) || [];
|
|
101
|
-
|
|
102
|
-
for (const match of customElements) {
|
|
103
|
-
const tagName = match.substring(1); // Remove <
|
|
104
|
-
usageMap.set(tagName, (usageMap.get(tagName) || 0) + 1);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
this.stats.componentsFound = usageMap.size;
|
|
109
|
-
|
|
110
|
-
logger.info({
|
|
111
|
-
code: 'MC_PERF_USAGE_ANALYSIS',
|
|
112
|
-
message: 'Component usage analysis complete',
|
|
113
|
-
componentsFound: usageMap.size
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
return usageMap;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Tree shake unused components from the bundle
|
|
121
|
-
*/
|
|
122
|
-
async treeShakeComponents(usageMap) {
|
|
123
|
-
const componentsDir = path.join(this.rootDir, 'app', 'assets', 'javascripts', 'shad-web-components', 'components');
|
|
124
|
-
|
|
125
|
-
if (!fs.existsSync(componentsDir)) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const componentFiles = this.findFiles(componentsDir, ['.js']);
|
|
130
|
-
let removedCount = 0;
|
|
131
|
-
|
|
132
|
-
for (const file of componentFiles) {
|
|
133
|
-
const content = fs.readFileSync(file, 'utf8');
|
|
134
|
-
|
|
135
|
-
// Extract component tag name from customElements.define call
|
|
136
|
-
const defineMatch = content.match(/customElements\.define\(['"]([^'"]+)['"]/);
|
|
137
|
-
|
|
138
|
-
if (defineMatch) {
|
|
139
|
-
const tagName = defineMatch[1];
|
|
140
|
-
|
|
141
|
-
// If component is not used, mark for removal
|
|
142
|
-
if (!usageMap.has(tagName)) {
|
|
143
|
-
removedCount++;
|
|
144
|
-
logger.info({
|
|
145
|
-
code: 'MC_PERF_TREE_SHAKE',
|
|
146
|
-
message: `Unused component detected: ${tagName}`,
|
|
147
|
-
file: path.basename(file)
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// In production, we'd actually remove or exclude this from the bundle
|
|
151
|
-
// For now, just log it
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
this.stats.componentsUsed = usageMap.size;
|
|
157
|
-
this.stats.componentsRemoved = removedCount;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Minify event manifest JSON files
|
|
162
|
-
*/
|
|
163
|
-
async minifyEventManifests() {
|
|
164
|
-
const manifestsDir = path.join(this.outputDir, 'event-manifests');
|
|
165
|
-
|
|
166
|
-
if (!fs.existsSync(manifestsDir)) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const manifestFiles = fs.readdirSync(manifestsDir).filter(f => f.endsWith('.json'));
|
|
171
|
-
|
|
172
|
-
for (const file of manifestFiles) {
|
|
173
|
-
const filePath = path.join(manifestsDir, file);
|
|
174
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
175
|
-
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
176
|
-
|
|
177
|
-
// Parse and re-stringify without whitespace
|
|
178
|
-
const parsed = JSON.parse(content);
|
|
179
|
-
const minified = JSON.stringify(parsed);
|
|
180
|
-
|
|
181
|
-
const minifiedSize = Buffer.byteLength(minified, 'utf8');
|
|
182
|
-
|
|
183
|
-
// Write minified version
|
|
184
|
-
fs.writeFileSync(filePath, minified, 'utf8');
|
|
185
|
-
|
|
186
|
-
this.stats.filesProcessed++;
|
|
187
|
-
this.stats.bytesOriginal += originalSize;
|
|
188
|
-
this.stats.bytesOptimized += minifiedSize;
|
|
189
|
-
|
|
190
|
-
logger.info({
|
|
191
|
-
code: 'MC_PERF_MINIFY',
|
|
192
|
-
message: `Minified ${file}`,
|
|
193
|
-
originalSize: originalSize,
|
|
194
|
-
minifiedSize: minifiedSize,
|
|
195
|
-
savings: `${((1 - minifiedSize / originalSize) * 100).toFixed(1)}%`
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Eliminate dead code from JavaScript bundles
|
|
202
|
-
*/
|
|
203
|
-
async eliminateDeadCode() {
|
|
204
|
-
const jsDir = path.join(this.outputDir);
|
|
205
|
-
|
|
206
|
-
if (!fs.existsSync(jsDir)) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const jsFiles = fs.readdirSync(jsDir).filter(f => f.endsWith('.js'));
|
|
211
|
-
|
|
212
|
-
for (const file of jsFiles) {
|
|
213
|
-
const filePath = path.join(jsDir, file);
|
|
214
|
-
let content = fs.readFileSync(filePath, 'utf8');
|
|
215
|
-
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
216
|
-
|
|
217
|
-
// Remove console.log statements in production
|
|
218
|
-
if (process.env.NODE_ENV === 'production') {
|
|
219
|
-
content = content.replace(/console\.log\([^)]*\);?/g, '');
|
|
220
|
-
content = content.replace(/console\.debug\([^)]*\);?/g, '');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Remove comments
|
|
224
|
-
content = content.replace(/\/\*[\s\S]*?\*\//g, ''); // Block comments
|
|
225
|
-
content = content.replace(/\/\/.*/g, ''); // Line comments
|
|
226
|
-
|
|
227
|
-
// Remove empty lines
|
|
228
|
-
content = content.replace(/^\s*[\r\n]/gm, '');
|
|
229
|
-
|
|
230
|
-
const optimizedSize = Buffer.byteLength(content, 'utf8');
|
|
231
|
-
|
|
232
|
-
// Write optimized version
|
|
233
|
-
fs.writeFileSync(filePath, content, 'utf8');
|
|
234
|
-
|
|
235
|
-
this.stats.bytesOriginal += originalSize;
|
|
236
|
-
this.stats.bytesOptimized += optimizedSize;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Optimize bundle size
|
|
242
|
-
*/
|
|
243
|
-
async optimizeBundle() {
|
|
244
|
-
// Analyze and report bundle sizes
|
|
245
|
-
const compiledDir = this.outputDir;
|
|
246
|
-
|
|
247
|
-
if (!fs.existsSync(compiledDir)) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const files = fs.readdirSync(compiledDir);
|
|
252
|
-
const bundles = [];
|
|
253
|
-
|
|
254
|
-
for (const file of files) {
|
|
255
|
-
const filePath = path.join(compiledDir, file);
|
|
256
|
-
const stats = fs.statSync(filePath);
|
|
257
|
-
|
|
258
|
-
if (stats.isFile() && file.endsWith('.js')) {
|
|
259
|
-
bundles.push({
|
|
260
|
-
name: file,
|
|
261
|
-
size: stats.size,
|
|
262
|
-
gzipSize: this.estimateGzipSize(stats.size)
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Sort by size
|
|
268
|
-
bundles.sort((a, b) => b.size - a.size);
|
|
269
|
-
|
|
270
|
-
// Log largest bundles
|
|
271
|
-
const topBundles = bundles.slice(0, 5);
|
|
272
|
-
for (const bundle of topBundles) {
|
|
273
|
-
logger.info({
|
|
274
|
-
code: 'MC_PERF_BUNDLE_SIZE',
|
|
275
|
-
message: `Bundle: ${bundle.name}`,
|
|
276
|
-
size: `${(bundle.size / 1024).toFixed(2)} KB`,
|
|
277
|
-
gzipSize: `${(bundle.gzipSize / 1024).toFixed(2)} KB`
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return bundles;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Estimate gzip size (approximation: ~30% of original)
|
|
286
|
-
*/
|
|
287
|
-
estimateGzipSize(size) {
|
|
288
|
-
return Math.round(size * 0.3);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Find files recursively
|
|
293
|
-
*/
|
|
294
|
-
findFiles(dir, extensions) {
|
|
295
|
-
const files = [];
|
|
296
|
-
|
|
297
|
-
if (!fs.existsSync(dir)) {
|
|
298
|
-
return files;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const items = fs.readdirSync(dir);
|
|
302
|
-
|
|
303
|
-
for (const item of items) {
|
|
304
|
-
const fullPath = path.join(dir, item);
|
|
305
|
-
const stat = fs.statSync(fullPath);
|
|
306
|
-
|
|
307
|
-
if (stat.isDirectory()) {
|
|
308
|
-
files.push(...this.findFiles(fullPath, extensions));
|
|
309
|
-
} else if (extensions.some(ext => item.endsWith(ext))) {
|
|
310
|
-
files.push(fullPath);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return files;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Log optimization results
|
|
319
|
-
*/
|
|
320
|
-
logOptimizationResults() {
|
|
321
|
-
const savingsPercent = this.stats.bytesOriginal > 0
|
|
322
|
-
? ((1 - this.stats.bytesOptimized / this.stats.bytesOriginal) * 100).toFixed(1)
|
|
323
|
-
: 0;
|
|
324
|
-
|
|
325
|
-
logger.info({
|
|
326
|
-
code: 'MC_PERF_BUILD_COMPLETE',
|
|
327
|
-
message: 'Build optimization complete',
|
|
328
|
-
context: {
|
|
329
|
-
filesProcessed: this.stats.filesProcessed,
|
|
330
|
-
originalSize: `${(this.stats.bytesOriginal / 1024).toFixed(2)} KB`,
|
|
331
|
-
optimizedSize: `${(this.stats.bytesOptimized / 1024).toFixed(2)} KB`,
|
|
332
|
-
savings: `${savingsPercent}%`,
|
|
333
|
-
timeTaken: `${this.stats.timeTaken}ms`,
|
|
334
|
-
componentsFound: this.stats.componentsFound,
|
|
335
|
-
componentsUsed: this.stats.componentsUsed,
|
|
336
|
-
componentsRemoved: this.stats.componentsRemoved
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
// Print summary
|
|
341
|
-
console.log('\n═══════════════════════════════════════════════════');
|
|
342
|
-
console.log('🚀 MasterController Build Optimization Complete');
|
|
343
|
-
console.log('═══════════════════════════════════════════════════');
|
|
344
|
-
console.log(`Files Processed: ${this.stats.filesProcessed}`);
|
|
345
|
-
console.log(`Original Size: ${(this.stats.bytesOriginal / 1024).toFixed(2)} KB`);
|
|
346
|
-
console.log(`Optimized Size: ${(this.stats.bytesOptimized / 1024).toFixed(2)} KB`);
|
|
347
|
-
console.log(`Savings: ${savingsPercent}%`);
|
|
348
|
-
console.log(`Time Taken: ${this.stats.timeTaken}ms`);
|
|
349
|
-
console.log(`Components Found: ${this.stats.componentsFound}`);
|
|
350
|
-
console.log(`Components Used: ${this.stats.componentsUsed}`);
|
|
351
|
-
console.log(`Unused Components: ${this.stats.componentsRemoved}`);
|
|
352
|
-
console.log('═══════════════════════════════════════════════════\n');
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// CLI usage
|
|
357
|
-
if (require.main === module) {
|
|
358
|
-
const optimizer = new MasterBuildOptimizer({
|
|
359
|
-
rootDir: process.cwd(),
|
|
360
|
-
minify: true,
|
|
361
|
-
treeShake: true,
|
|
362
|
-
deadCodeElimination: true
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
optimizer.optimize()
|
|
366
|
-
.then(stats => {
|
|
367
|
-
console.log('✅ Build optimization successful');
|
|
368
|
-
process.exit(0);
|
|
369
|
-
})
|
|
370
|
-
.catch(error => {
|
|
371
|
-
console.error('❌ Build optimization failed:', error.message);
|
|
372
|
-
process.exit(1);
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
module.exports = { MasterBuildOptimizer };
|