mastercontroller 1.3.9 → 1.3.12

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 (47) hide show
  1. package/.claude/settings.local.json +6 -1
  2. package/.eslintrc.json +50 -0
  3. package/.github/workflows/ci.yml +317 -0
  4. package/.prettierrc +10 -0
  5. package/CHANGES.md +296 -0
  6. package/DEPLOYMENT.md +956 -0
  7. package/FIXES_APPLIED.md +378 -0
  8. package/FORTUNE_500_UPGRADE.md +863 -0
  9. package/MasterAction.js +10 -263
  10. package/MasterControl.js +226 -43
  11. package/MasterRequest.js +42 -1
  12. package/MasterRouter.js +42 -37
  13. package/PERFORMANCE_SECURITY_AUDIT.md +677 -0
  14. package/README.md +602 -71
  15. package/SENIOR_ENGINEER_AUDIT.md +2477 -0
  16. package/VERIFICATION_CHECKLIST.md +726 -0
  17. package/error/README.md +2452 -0
  18. package/monitoring/HealthCheck.js +347 -0
  19. package/monitoring/PrometheusExporter.js +416 -0
  20. package/monitoring/README.md +3112 -0
  21. package/package.json +64 -11
  22. package/security/MasterValidator.js +140 -10
  23. package/security/README.md +1805 -0
  24. package/security/adapters/RedisCSRFStore.js +428 -0
  25. package/security/adapters/RedisRateLimiter.js +462 -0
  26. package/security/adapters/RedisSessionStore.js +476 -0
  27. package/MasterCors.js.tmp +0 -0
  28. package/MasterHtml.js +0 -649
  29. package/MasterPipeline.js.tmp +0 -0
  30. package/MasterRequest.js.tmp +0 -0
  31. package/MasterRouter.js.tmp +0 -0
  32. package/MasterSocket.js.tmp +0 -0
  33. package/MasterTemp.js.tmp +0 -0
  34. package/MasterTemplate.js +0 -230
  35. package/MasterTimeout.js.tmp +0 -0
  36. package/TemplateOverwrite.js +0 -41
  37. package/TemplateOverwrite.js.tmp +0 -0
  38. package/error/ErrorBoundary.js +0 -353
  39. package/error/HydrationMismatch.js +0 -265
  40. package/error/MasterError.js +0 -240
  41. package/error/MasterError.js.tmp +0 -0
  42. package/error/MasterErrorRenderer.js +0 -536
  43. package/error/MasterErrorRenderer.js.tmp +0 -0
  44. package/error/SSRErrorHandler.js +0 -273
  45. package/ssr/hydration-client.js +0 -93
  46. package/ssr/runtime-ssr.cjs +0 -553
  47. package/ssr/ssr-shims.js +0 -73
@@ -1,265 +0,0 @@
1
- /**
2
- * HydrationMismatch - Detect and report hydration mismatches
3
- * Compares server-rendered HTML with client-rendered HTML
4
- * Version: 1.0.0
5
- */
6
-
7
- const isDevelopment = typeof process !== 'undefined'
8
- ? (process.env.NODE_ENV !== 'production')
9
- : (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
10
-
11
- /**
12
- * Simple diff algorithm for HTML comparison
13
- */
14
- function generateDiff(serverHTML, clientHTML) {
15
- const serverLines = serverHTML.split('\n').map(l => l.trim()).filter(Boolean);
16
- const clientLines = clientHTML.split('\n').map(l => l.trim()).filter(Boolean);
17
-
18
- const diff = [];
19
- const maxLines = Math.max(serverLines.length, clientLines.length);
20
-
21
- for (let i = 0; i < maxLines; i++) {
22
- const serverLine = serverLines[i] || '';
23
- const clientLine = clientLines[i] || '';
24
-
25
- if (serverLine !== clientLine) {
26
- diff.push({
27
- line: i + 1,
28
- server: serverLine,
29
- client: clientLine,
30
- type: !serverLine ? 'added' : !clientLine ? 'removed' : 'modified'
31
- });
32
- }
33
- }
34
-
35
- return diff;
36
- }
37
-
38
- /**
39
- * Format diff for console output
40
- */
41
- function formatDiffForConsole(diff) {
42
- let output = '\n';
43
-
44
- diff.slice(0, 10).forEach(change => { // Show first 10 differences
45
- output += `Line ${change.line}:\n`;
46
-
47
- if (change.type === 'removed') {
48
- output += ` \x1b[31m- ${change.server}\x1b[0m\n`;
49
- } else if (change.type === 'added') {
50
- output += ` \x1b[32m+ ${change.client}\x1b[0m\n`;
51
- } else {
52
- output += ` \x1b[31m- ${change.server}\x1b[0m\n`;
53
- output += ` \x1b[32m+ ${change.client}\x1b[0m\n`;
54
- }
55
- });
56
-
57
- if (diff.length > 10) {
58
- output += `\n... and ${diff.length - 10} more differences\n`;
59
- }
60
-
61
- return output;
62
- }
63
-
64
- /**
65
- * Compare attributes between two elements
66
- */
67
- function compareAttributes(serverEl, clientEl) {
68
- const mismatches = [];
69
-
70
- // Check server attributes
71
- if (serverEl.attributes) {
72
- for (const attr of serverEl.attributes) {
73
- const serverValue = attr.value;
74
- const clientValue = clientEl.getAttribute(attr.name);
75
-
76
- if (serverValue !== clientValue) {
77
- mismatches.push({
78
- attribute: attr.name,
79
- server: serverValue,
80
- client: clientValue || '(missing)'
81
- });
82
- }
83
- }
84
- }
85
-
86
- // Check for client attributes missing on server
87
- if (clientEl.attributes) {
88
- for (const attr of clientEl.attributes) {
89
- if (!serverEl.hasAttribute(attr.name)) {
90
- mismatches.push({
91
- attribute: attr.name,
92
- server: '(missing)',
93
- client: attr.value
94
- });
95
- }
96
- }
97
- }
98
-
99
- return mismatches;
100
- }
101
-
102
- /**
103
- * Detect hydration mismatch between server and client HTML
104
- */
105
- function detectHydrationMismatch(element, componentName, options = {}) {
106
- if (!element || !element.hasAttribute('data-ssr')) {
107
- return null; // Not server-rendered
108
- }
109
-
110
- // Store server HTML before hydration
111
- const serverHTML = element.innerHTML;
112
-
113
- // Create a clone to test client rendering
114
- const testElement = element.cloneNode(false);
115
- testElement.removeAttribute('data-ssr');
116
-
117
- // Simulate client render
118
- if (typeof element.connectedCallback === 'function') {
119
- try {
120
- // Call connectedCallback to trigger client render
121
- const originalCallback = element.constructor.prototype.connectedCallback;
122
- if (originalCallback) {
123
- originalCallback.call(testElement);
124
- }
125
- } catch (error) {
126
- console.warn('[HydrationMismatch] Could not simulate client render:', error);
127
- return null;
128
- }
129
- }
130
-
131
- const clientHTML = testElement.innerHTML;
132
-
133
- // Compare HTML
134
- if (serverHTML.trim() === clientHTML.trim()) {
135
- return null; // No mismatch
136
- }
137
-
138
- // Generate diff
139
- const diff = generateDiff(serverHTML, clientHTML);
140
-
141
- // Compare attributes
142
- const attrMismatches = compareAttributes(element, testElement);
143
-
144
- return {
145
- component: componentName || element.tagName.toLowerCase(),
146
- serverHTML,
147
- clientHTML,
148
- diff,
149
- attrMismatches,
150
- element
151
- };
152
- }
153
-
154
- /**
155
- * Report hydration mismatch to console
156
- */
157
- function reportHydrationMismatch(mismatch, options = {}) {
158
- if (!mismatch) return;
159
-
160
- const { component, diff, attrMismatches } = mismatch;
161
-
162
- console.group('\x1b[33m⚠️ MasterController Hydration Mismatch\x1b[0m');
163
- console.log(`\x1b[36mComponent:\x1b[0m ${component}`);
164
-
165
- // Attribute mismatches
166
- if (attrMismatches.length > 0) {
167
- console.log('\n\x1b[33mAttribute Mismatches:\x1b[0m');
168
- attrMismatches.forEach(attr => {
169
- console.log(` ${attr.attribute}:`);
170
- console.log(` \x1b[31mServer: ${attr.server}\x1b[0m`);
171
- console.log(` \x1b[32mClient: ${attr.client}\x1b[0m`);
172
- });
173
- }
174
-
175
- // HTML content mismatches
176
- if (diff.length > 0) {
177
- console.log('\n\x1b[33mHTML Diff:\x1b[0m');
178
- console.log(formatDiffForConsole(diff));
179
- }
180
-
181
- // Suggestions
182
- console.log('\n\x1b[36mPossible Causes:\x1b[0m');
183
- console.log(' 1. Component state differs between server and client');
184
- console.log(' 2. Conditional rendering based on client-only APIs (window, navigator, etc.)');
185
- console.log(' 3. Missing or incorrect attributes in client-side render');
186
- console.log(' 4. Random values or timestamps generated during render');
187
- console.log(' 5. Missing data-ssr guard in connectedCallback');
188
-
189
- console.log('\n\x1b[36mSuggestions:\x1b[0m');
190
- console.log(' • Ensure server and client render with same props/state');
191
- console.log(' • Use typeof window !== "undefined" checks for browser APIs');
192
- console.log(' • Avoid random values or Date.now() in render logic');
193
- console.log(' • Verify data-ssr attribute is present on server-rendered elements');
194
-
195
- console.log('\n\x1b[34mLearn more:\x1b[0m https://mastercontroller.dev/docs/hydration#mismatches');
196
- console.groupEnd();
197
-
198
- // Log to monitoring service
199
- if (typeof window !== 'undefined' && window.masterControllerErrorReporter) {
200
- window.masterControllerErrorReporter({
201
- type: 'hydration-mismatch',
202
- component: mismatch.component,
203
- diffCount: diff.length,
204
- attrMismatchCount: attrMismatches.length
205
- });
206
- }
207
- }
208
-
209
- /**
210
- * Scan all SSR components for hydration mismatches
211
- */
212
- function scanForHydrationMismatches(options = {}) {
213
- if (!isDevelopment) return;
214
-
215
- const ssrElements = document.querySelectorAll('[data-ssr]');
216
- const mismatches = [];
217
-
218
- ssrElements.forEach(element => {
219
- const componentName = element.tagName.toLowerCase();
220
- const mismatch = detectHydrationMismatch(element, componentName, options);
221
-
222
- if (mismatch) {
223
- mismatches.push(mismatch);
224
- reportHydrationMismatch(mismatch, options);
225
- }
226
- });
227
-
228
- if (mismatches.length === 0 && options.verbose) {
229
- console.log('\x1b[32m✓ No hydration mismatches detected\x1b[0m');
230
- }
231
-
232
- return mismatches;
233
- }
234
-
235
- /**
236
- * Enable automatic hydration mismatch detection
237
- */
238
- function enableHydrationMismatchDetection(options = {}) {
239
- if (!isDevelopment) return;
240
-
241
- // Run check after hydration completes
242
- if (typeof window !== 'undefined') {
243
- window.addEventListener('load', () => {
244
- setTimeout(() => {
245
- scanForHydrationMismatches(options);
246
- }, options.delay || 1000);
247
- });
248
- }
249
- }
250
-
251
- // Auto-enable in development
252
- if (typeof window !== 'undefined' && isDevelopment) {
253
- enableHydrationMismatchDetection({
254
- verbose: localStorage.getItem('mc-hydration-debug') === 'true'
255
- });
256
- }
257
-
258
- module.exports = {
259
- detectHydrationMismatch,
260
- reportHydrationMismatch,
261
- scanForHydrationMismatches,
262
- enableHydrationMismatchDetection,
263
- generateDiff,
264
- compareAttributes
265
- };
@@ -1,240 +0,0 @@
1
-
2
- // version 1.0.21 - improved console/error logging with syntax error code frames
3
- var winston = require('winston');
4
- var fileserver = require('fs');
5
- const { request } = require('http');
6
-
7
- class MasterError{
8
- logger = "";
9
- statuses = [];
10
-
11
- // Lazy-load master to avoid circular dependency (Google-style lazy initialization)
12
- get _master() {
13
- if (!this.__masterCache) {
14
- this.__masterCache = require('../MasterControl');
15
- }
16
- return this.__masterCache;
17
- }
18
-
19
- init(error){
20
- var that = this;
21
- var stat = error;
22
- this.addStatuses(stat);
23
-
24
- this.logger = winston.createLogger({
25
- format: winston.format.json(),
26
- transports: [
27
- new winston.transports.Console(),
28
- new winston.transports.File({ filename: `${this._master.root}/log/${this._master.environmentType}.log` })
29
- ]
30
- });
31
-
32
- // Global error handlers with better diagnostics (stack and code frame)
33
- process.on('uncaughtException', function (err) {
34
- that._reportError(err, 'uncaughtException');
35
- });
36
-
37
- // will catch all promise exceptions
38
- process.on('unhandledRejection', function (reason, promise) {
39
- that._reportError(reason instanceof Error ? reason : new Error(String(reason)), 'unhandledRejection');
40
- });
41
-
42
- process.on('rejectionHandled', function (reason, promise) {
43
- that._reportError(reason instanceof Error ? reason : new Error(String(reason)), 'rejectionHandled');
44
- });
45
-
46
- process.on('warning', function (warning) {
47
- that._reportError(warning instanceof Error ? warning : new Error(String(warning)), 'warning');
48
- });
49
-
50
- }
51
-
52
- clearStatuses(){
53
- this.statuses = [];
54
- }
55
-
56
- // add error status functions
57
- addStatuses(status){
58
- // loop through object and add all codes
59
- for (var code in status) {
60
- // skip loop if the property is from prototype
61
- if (!status.hasOwnProperty(code)) continue;
62
-
63
- this.statuses.push({
64
- code: code,
65
- route : status[code]
66
- });
67
-
68
- }
69
- }
70
-
71
- // call error status by status code
72
- callHttpStatus(statusCode, response){
73
- try{
74
-
75
- var status = this.statuses;
76
- var res = response;
77
- if(status.length !== 0){
78
- if(Number.isInteger(statusCode) ){
79
- for (var i = 0; i < status.length; i++) {
80
- if(parseInt(status[i].code) === statusCode){
81
- var location = status[i].route;
82
- var html = fileserver.readFileSync(`${this._master.root}/${status[i].route.replace(/^\/|\/$/g, '')}`, 'utf8' );
83
- if (!res.headersSent) {
84
- res.writeHead(200, {'Content-Type': 'text/html'});
85
- res.write(html, 'utf8');
86
- res.end();
87
- }
88
- }
89
- };
90
- }
91
- else{
92
- this.log("The HTTP status added is not a number","error");
93
- throw "The HTTP status added is not a number";
94
- }
95
- }
96
- else{
97
- this.log("No error http statuses have been added", "error");
98
- throw "No error http statuses have been added";
99
- }
100
- }
101
- catch(err){
102
- throw err;
103
- }
104
- }
105
-
106
- // log your error
107
- log(msg, level){
108
-
109
- var level = level === undefined ? "info": level;
110
- if(msg === undefined){
111
- throw "error has been thrown with no message.";
112
- };
113
- if(this.logger === ""){
114
- throw "error init function has not been called.";
115
- };
116
-
117
- var message = msg.message === undefined ? msg : msg.message;
118
-
119
- this.logger.log({
120
- level: level,
121
- message: message,
122
- stack : msg.stack
123
- });
124
- }
125
-
126
- // Enhanced error reporter: logs formatted stack and nearby source code
127
- _reportError(err, tag){
128
- try{
129
- const name = err && err.name ? err.name : 'Error';
130
- const message = err && err.message ? err.message : String(err);
131
- const stack = err && err.stack ? String(err.stack) : '';
132
- const header = `[${tag}] ${name}: ${message}`;
133
- console.error('\u001b[31m' + header + '\u001b[0m');
134
- if (stack) {
135
- console.error(stack);
136
- const loc = this._extractTopFrame(stack);
137
- if (loc && loc.file && loc.line) {
138
- const frame = this._buildCodeFrame(loc.file, loc.line, loc.column);
139
- if (frame) {
140
- console.error('\n\u001b[33mCode frame:\u001b[0m');
141
- console.error(frame);
142
- }
143
- }
144
- }
145
- this.log(err, 'error');
146
- } catch(e){
147
- try { console.error('Error while reporting error:', e); } catch(_) {}
148
- }
149
- }
150
-
151
- _extractTopFrame(stack){
152
- try{
153
- const lines = stack.split('\n');
154
- for (let i = 0; i < lines.length; i++) {
155
- const m = lines[i].match(/\((.*):(\d+):(\d+)\)/) || lines[i].match(/at ([^\s]+):(\d+):(\d+)/);
156
- if (m) {
157
- return { file: m[1], line: parseInt(m[2]), column: parseInt(m[3]||'0') };
158
- }
159
- }
160
- return null;
161
- } catch(_) { return null; }
162
- }
163
-
164
- _buildCodeFrame(file, line, column){
165
- try{
166
- if (!fileserver.existsSync(file)) return null;
167
- const src = fileserver.readFileSync(file, 'utf8').split(/\r?\n/);
168
- const start = Math.max(1, line - 3);
169
- const end = Math.min(src.length, line + 3);
170
- const digits = String(end).length;
171
- let out = '';
172
- for (let i = start; i <= end; i++) {
173
- const prefix = (i === line ? '>' : ' ') + String(i).padStart(digits, ' ') + ' | ';
174
- out += prefix + src[i - 1] + '\n';
175
- if (i === line && column && column > 0) {
176
- out += ' '.repeat(prefix.length + column - 1) + '^\n';
177
- }
178
- }
179
- return out;
180
- } catch(_) { return null; }
181
- }
182
- }
183
-
184
- module.exports = { MasterError };
185
-
186
- // ================ CODES YOU CAN USE ================ //
187
- // ACCEPTED 202 Accepted
188
- // BAD_GATEWAY 502 Bad Gateway
189
- // BAD_REQUEST 400 Bad Request
190
- // CONFLICT 409 Conflict
191
- // CONTINUE 100 Continue
192
- // CREATED 201 Created
193
- // EXPECTATION_FAILED 417 Expectation Failed
194
- // FAILED_DEPENDENCY 424 Failed Dependency
195
- // FORBIDDEN 403 Forbidden
196
- // GATEWAY_TIMEOUT 504 Gateway Timeout
197
- // GONE 410 Gone
198
- // HTTP_VERSION_NOT_SUPPORTED 505 HTTP Version Not Supported
199
- // IM_A_TEAPOT 418 I'm a teapot
200
- // INSUFFICIENT_SPACE_ON_RESOURCE 419 Insufficient Space on Resource
201
- // INSUFFICIENT_STORAGE 507 Insufficient Storage
202
- // INTERNAL_SERVER_ERROR 500 Server Error
203
- // LENGTH_REQUIRED 411 Length Required
204
- // LOCKED 423 Locked
205
- // METHOD_FAILURE 420 Method Failure
206
- // METHOD_NOT_ALLOWED 405 Method Not Allowed
207
- // MOVED_PERMANENTLY 301 Moved Permanently
208
- // MOVED_TEMPORARILY 302 Moved Temporarily
209
- // MULTI_STATUS 207 Multi-Status
210
- // MULTIPLE_CHOICES 300 Multiple Choices
211
- // NETWORK_AUTHENTICATION_REQUIRED 511 Network Authentication Required
212
- // NO_CONTENT 204 No Content
213
- // NON_AUTHORITATIVE_INFORMATION 203 Non Authoritative Information
214
- // NOT_ACCEPTABLE 406 Not Acceptable
215
- // NOT_FOUND 404 Not Found
216
- // NOT_IMPLEMENTED 501 Not Implemented
217
- // NOT_MODIFIED 304 Not Modified
218
- // OK 200 OK
219
- // PARTIAL_CONTENT 206 Partial Content
220
- // PAYMENT_REQUIRED 402 Payment Required
221
- // PERMANENT_REDIRECT 308 Permanent Redirect
222
- // PRECONDITION_FAILED 412 Precondition Failed
223
- // PRECONDITION_REQUIRED 428 Precondition Required
224
- // PROCESSING 102 Processing
225
- // PROXY_AUTHENTICATION_REQUIRED 407 Proxy Authentication Required
226
- // REQUEST_HEADER_FIELDS_TOO_LARGE 431 Request Header Fields Too Large
227
- // REQUEST_TIMEOUT 408 Request Timeout
228
- // REQUEST_TOO_LONG 413 Request Entity Too Large
229
- // REQUEST_URI_TOO_LONG 414 Request-URI Too Long
230
- // REQUESTED_RANGE_NOT_SATISFIABLE 416 Requested Range Not Satisfiable
231
- // RESET_CONTENT 205 Reset Content
232
- // SEE_OTHER 303 See Other
233
- // SERVICE_UNAVAILABLE 503 Service Unavailable
234
- // SWITCHING_PROTOCOLS 101 Switching Protocols
235
- // TEMPORARY_REDIRECT 307 Temporary Redirect
236
- // TOO_MANY_REQUESTS 429 Too Many Requests
237
- // UNAUTHORIZED 401 Unauthorized
238
- // UNPROCESSABLE_ENTITY 422 Unprocessable Entity
239
- // UNSUPPORTED_MEDIA_TYPE 415 Unsupported Media Type
240
- // USE_PROXY 305 Use Proxy
File without changes