mastercontroller 1.2.12 → 1.2.14

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.
@@ -0,0 +1,407 @@
1
+ /**
2
+ * MasterErrorMiddleware - Request/Response error handling middleware
3
+ * Version: 1.0.1
4
+ */
5
+
6
+ const { handleControllerError, handleRoutingError, sendErrorResponse } = require('./MasterBackendErrorHandler');
7
+ const { logger } = require('./MasterErrorLogger');
8
+
9
+ const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.master === 'development';
10
+
11
+ /**
12
+ * Global error handler middleware
13
+ * Wrap all controller actions with this
14
+ */
15
+ function errorHandlerMiddleware(handler, controllerName, actionName) {
16
+ return async function wrappedHandler(requestObject) {
17
+ const startTime = Date.now();
18
+
19
+ try {
20
+ // Execute the actual handler
21
+ const result = await Promise.resolve(handler.call(this, requestObject));
22
+
23
+ // Log successful request in development
24
+ if (isDevelopment) {
25
+ const duration = Date.now() - startTime;
26
+ logger.info({
27
+ code: 'MC_INFO_REQUEST_SUCCESS',
28
+ message: `${controllerName}#${actionName} completed`,
29
+ context: {
30
+ duration,
31
+ path: requestObject.pathName,
32
+ method: requestObject.type
33
+ }
34
+ });
35
+ }
36
+
37
+ return result;
38
+
39
+ } catch (error) {
40
+ const duration = Date.now() - startTime;
41
+
42
+ // Handle the error
43
+ const mcError = handleControllerError(
44
+ error,
45
+ controllerName,
46
+ actionName,
47
+ requestObject.pathName
48
+ );
49
+
50
+ // Send error response
51
+ sendErrorResponse(
52
+ requestObject.response,
53
+ mcError,
54
+ requestObject.pathName
55
+ );
56
+
57
+ // Log to monitoring
58
+ logger.error({
59
+ code: mcError.code,
60
+ message: mcError.message,
61
+ controller: controllerName,
62
+ action: actionName,
63
+ route: requestObject.pathName,
64
+ method: requestObject.type,
65
+ duration,
66
+ originalError: error,
67
+ stack: error.stack
68
+ });
69
+
70
+ // Don't re-throw - error has been handled
71
+ return null;
72
+ }
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Request logging middleware
78
+ */
79
+ function requestLoggerMiddleware() {
80
+ return function(requestObject, next) {
81
+ const startTime = Date.now();
82
+
83
+ logger.info({
84
+ code: 'MC_INFO_REQUEST_START',
85
+ message: `${requestObject.type} ${requestObject.pathName}`,
86
+ context: {
87
+ method: requestObject.type,
88
+ path: requestObject.pathName,
89
+ params: requestObject.params,
90
+ query: requestObject.query,
91
+ ip: requestObject.request.connection?.remoteAddress
92
+ }
93
+ });
94
+
95
+ // Continue to next middleware
96
+ if (typeof next === 'function') {
97
+ next();
98
+ }
99
+ };
100
+ }
101
+
102
+ /**
103
+ * 404 handler middleware
104
+ */
105
+ function notFoundMiddleware(requestObject) {
106
+ const mcError = handleRoutingError(
107
+ requestObject.pathName,
108
+ [] // Would need to pass available routes here
109
+ );
110
+
111
+ sendErrorResponse(
112
+ requestObject.response,
113
+ mcError,
114
+ requestObject.pathName
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Extract user code context from stack trace
120
+ */
121
+ function extractUserCodeContext(stack) {
122
+ if (!stack) return null;
123
+
124
+ const lines = stack.split('\n');
125
+ const userFiles = [];
126
+ const frameworkFiles = [];
127
+
128
+ for (const line of lines) {
129
+ // Skip the error message line
130
+ if (!line.trim().startsWith('at ')) continue;
131
+
132
+ // Extract file path from stack line
133
+ const match = line.match(/\((.+?):(\d+):(\d+)\)|at (.+?):(\d+):(\d+)/);
134
+ if (!match) continue;
135
+
136
+ const filePath = match[1] || match[4];
137
+ const lineNum = match[2] || match[5];
138
+ const colNum = match[3] || match[6];
139
+
140
+ if (!filePath) continue;
141
+
142
+ // Categorize as user code or framework code
143
+ const isFramework = filePath.includes('node_modules/mastercontroller');
144
+ const isNodeInternal = filePath.includes('node:internal') || filePath.includes('/lib/internal/');
145
+
146
+ if (isNodeInternal) continue;
147
+
148
+ const fileInfo = {
149
+ file: filePath,
150
+ line: lineNum,
151
+ column: colNum,
152
+ location: `${filePath}:${lineNum}:${colNum}`
153
+ };
154
+
155
+ if (isFramework) {
156
+ frameworkFiles.push(fileInfo);
157
+ } else {
158
+ userFiles.push(fileInfo);
159
+ }
160
+ }
161
+
162
+ return {
163
+ userFiles,
164
+ frameworkFiles,
165
+ triggeringFile: userFiles[0] || frameworkFiles[0] || null
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Uncaught exception handler
171
+ */
172
+ function setupGlobalErrorHandlers() {
173
+ // Handle uncaught exceptions
174
+ process.on('uncaughtException', (error) => {
175
+ console.error('[MasterController] Uncaught Exception:', error);
176
+
177
+ // Extract context from stack trace
178
+ const context = extractUserCodeContext(error.stack);
179
+
180
+ // Build enhanced error message
181
+ let enhancedMessage = `Uncaught exception: ${error.message}`;
182
+
183
+ if (context && context.triggeringFile) {
184
+ enhancedMessage += `\n\n🔍 Error Location: ${context.triggeringFile.location}`;
185
+ }
186
+
187
+ if (context && context.userFiles.length > 0) {
188
+ enhancedMessage += `\n\n📂 Your Code Involved:`;
189
+ context.userFiles.forEach((file, i) => {
190
+ if (i < 3) { // Show first 3 user files
191
+ enhancedMessage += `\n ${i + 1}. ${file.location}`;
192
+ }
193
+ });
194
+ }
195
+
196
+ if (context && context.frameworkFiles.length > 0) {
197
+ enhancedMessage += `\n\n🔧 Framework Files Involved:`;
198
+ context.frameworkFiles.forEach((file, i) => {
199
+ if (i < 2) { // Show first 2 framework files
200
+ enhancedMessage += `\n ${i + 1}. ${file.location}`;
201
+ }
202
+ });
203
+ }
204
+
205
+ console.error(enhancedMessage);
206
+
207
+ logger.fatal({
208
+ code: 'MC_ERR_UNCAUGHT_EXCEPTION',
209
+ message: enhancedMessage,
210
+ originalError: error,
211
+ stack: error.stack,
212
+ context: context
213
+ });
214
+
215
+ // Give logger time to write, then exit
216
+ setTimeout(() => {
217
+ process.exit(1);
218
+ }, 1000);
219
+ });
220
+
221
+ // Handle unhandled promise rejections
222
+ process.on('unhandledRejection', (reason, promise) => {
223
+ console.error('[MasterController] Unhandled Rejection:', reason);
224
+
225
+ // Extract context from stack trace if available
226
+ const context = reason?.stack ? extractUserCodeContext(reason.stack) : null;
227
+
228
+ // Build enhanced error message
229
+ let enhancedMessage = `Unhandled promise rejection: ${reason}`;
230
+
231
+ if (context && context.triggeringFile) {
232
+ enhancedMessage += `\n\n🔍 Error Location: ${context.triggeringFile.location}`;
233
+ }
234
+
235
+ if (context && context.userFiles.length > 0) {
236
+ enhancedMessage += `\n\n📂 Your Code Involved:`;
237
+ context.userFiles.forEach((file, i) => {
238
+ if (i < 3) { // Show first 3 user files
239
+ enhancedMessage += `\n ${i + 1}. ${file.location}`;
240
+ }
241
+ });
242
+ }
243
+
244
+ if (enhancedMessage !== `Unhandled promise rejection: ${reason}`) {
245
+ console.error(enhancedMessage);
246
+ }
247
+
248
+ logger.error({
249
+ code: 'MC_ERR_UNHANDLED_REJECTION',
250
+ message: enhancedMessage,
251
+ originalError: reason,
252
+ stack: reason?.stack,
253
+ context: context
254
+ });
255
+ });
256
+
257
+ // Handle warnings
258
+ process.on('warning', (warning) => {
259
+ if (isDevelopment) {
260
+ console.warn('[MasterController] Warning:', warning);
261
+ }
262
+
263
+ logger.warn({
264
+ code: 'MC_WARN_PROCESS_WARNING',
265
+ message: warning.message,
266
+ context: {
267
+ name: warning.name,
268
+ stack: warning.stack
269
+ }
270
+ });
271
+ });
272
+ }
273
+
274
+ /**
275
+ * Safe file reader with error handling
276
+ */
277
+ function safeReadFile(fs, filePath, encoding = 'utf8') {
278
+ try {
279
+ return {
280
+ success: true,
281
+ content: fs.readFileSync(filePath, encoding),
282
+ error: null
283
+ };
284
+ } catch (error) {
285
+ const { handleFileReadError } = require('./MasterBackendErrorHandler');
286
+ const mcError = handleFileReadError(error, filePath);
287
+
288
+ return {
289
+ success: false,
290
+ content: null,
291
+ error: mcError
292
+ };
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Safe file existence check
298
+ */
299
+ function safeFileExists(fs, filePath) {
300
+ try {
301
+ return fs.existsSync(filePath);
302
+ } catch (error) {
303
+ logger.warn({
304
+ code: 'MC_WARN_FILE_CHECK',
305
+ message: `Could not check if file exists: ${filePath}`,
306
+ originalError: error
307
+ });
308
+ return false;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Wrap controller class with error handling
314
+ */
315
+ function wrapController(ControllerClass, controllerName) {
316
+ const wrappedMethods = {};
317
+
318
+ // Get all methods from the controller
319
+ const methodNames = Object.getOwnPropertyNames(ControllerClass.prototype);
320
+
321
+ methodNames.forEach(methodName => {
322
+ if (methodName === 'constructor') return;
323
+
324
+ const originalMethod = ControllerClass.prototype[methodName];
325
+
326
+ if (typeof originalMethod === 'function') {
327
+ // Wrap each method with error handling
328
+ wrappedMethods[methodName] = errorHandlerMiddleware(
329
+ originalMethod,
330
+ controllerName,
331
+ methodName
332
+ );
333
+ }
334
+ });
335
+
336
+ // Create new class with wrapped methods
337
+ const WrappedController = class extends ControllerClass {
338
+ constructor(...args) {
339
+ super(...args);
340
+
341
+ // Apply wrapped methods
342
+ Object.keys(wrappedMethods).forEach(methodName => {
343
+ this[methodName] = wrappedMethods[methodName].bind(this);
344
+ });
345
+ }
346
+ };
347
+
348
+ return WrappedController;
349
+ }
350
+
351
+ /**
352
+ * Performance tracking middleware
353
+ */
354
+ function performanceMiddleware() {
355
+ const requests = new Map();
356
+
357
+ return {
358
+ start(requestId, requestObject) {
359
+ requests.set(requestId, {
360
+ startTime: Date.now(),
361
+ path: requestObject.pathName,
362
+ method: requestObject.type
363
+ });
364
+ },
365
+
366
+ end(requestId) {
367
+ const req = requests.get(requestId);
368
+ if (!req) return;
369
+
370
+ const duration = Date.now() - req.startTime;
371
+
372
+ if (duration > 1000) {
373
+ logger.warn({
374
+ code: 'MC_WARN_SLOW_REQUEST',
375
+ message: `Slow request detected (${duration}ms)`,
376
+ context: {
377
+ duration,
378
+ path: req.path,
379
+ method: req.method
380
+ }
381
+ });
382
+ }
383
+
384
+ requests.delete(requestId);
385
+ },
386
+
387
+ getStats() {
388
+ return {
389
+ activeRequests: requests.size,
390
+ requests: Array.from(requests.values())
391
+ };
392
+ }
393
+ };
394
+ }
395
+
396
+ const performanceTracker = performanceMiddleware();
397
+
398
+ module.exports = {
399
+ errorHandlerMiddleware,
400
+ requestLoggerMiddleware,
401
+ notFoundMiddleware,
402
+ setupGlobalErrorHandlers,
403
+ safeReadFile,
404
+ safeFileExists,
405
+ wrapController,
406
+ performanceTracker
407
+ };
@@ -0,0 +1,273 @@
1
+ /**
2
+ * SSRErrorHandler - Server-side rendering error handling
3
+ * Handles component render failures with graceful fallbacks
4
+ * Version: 1.0.1
5
+ */
6
+
7
+ const { MasterControllerError } = require('./MasterErrorHandler');
8
+
9
+ const isDevelopment = process.env.NODE_ENV !== 'production' && process.env.master === 'development';
10
+
11
+ /**
12
+ * Render error component for development mode
13
+ */
14
+ function renderErrorComponent(options = {}) {
15
+ const {
16
+ component,
17
+ error,
18
+ stack,
19
+ file,
20
+ line,
21
+ details
22
+ } = options;
23
+
24
+ const errorObj = new MasterControllerError({
25
+ code: 'MC_ERR_COMPONENT_RENDER_FAILED',
26
+ message: error || 'Component failed to render on server',
27
+ component,
28
+ file,
29
+ line,
30
+ details: details || stack,
31
+ originalError: options.originalError
32
+ });
33
+
34
+ // Return full HTML error page in development
35
+ return errorObj.toHTML();
36
+ }
37
+
38
+ /**
39
+ * Render fallback component for production mode
40
+ */
41
+ function renderFallback(componentName, options = {}) {
42
+ const { showSkeleton = true, customMessage } = options;
43
+
44
+ if (showSkeleton) {
45
+ return `
46
+ <div class="mc-fallback" data-component="${componentName}" style="
47
+ padding: 20px;
48
+ background: #f9fafb;
49
+ border-radius: 8px;
50
+ border: 1px dashed #d1d5db;
51
+ min-height: 100px;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ color: #6b7280;
56
+ ">
57
+ <div class="mc-fallback-content">
58
+ ${customMessage || ''}
59
+ <div class="skeleton-loader" style="
60
+ width: 100%;
61
+ height: 20px;
62
+ background: linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%);
63
+ background-size: 200% 100%;
64
+ animation: skeleton-loading 1.5s ease-in-out infinite;
65
+ border-radius: 4px;
66
+ "></div>
67
+ </div>
68
+ <style>
69
+ @keyframes skeleton-loading {
70
+ 0% { background-position: 200% 0; }
71
+ 100% { background-position: -200% 0; }
72
+ }
73
+ </style>
74
+ </div>
75
+ `;
76
+ }
77
+
78
+ // Minimal fallback - just an empty div
79
+ return `<div class="mc-fallback" data-component="${componentName}"></div>`;
80
+ }
81
+
82
+ /**
83
+ * Safe component render wrapper
84
+ * Catches errors and returns either error page (dev) or fallback (prod)
85
+ */
86
+ function safeRenderComponent(component, componentName, filePath) {
87
+ try {
88
+ // Try tempRender first
89
+ if (typeof component.tempRender === 'function') {
90
+ const startTime = Date.now();
91
+ const result = component.tempRender();
92
+ const renderTime = Date.now() - startTime;
93
+
94
+ // Warn about slow renders in development
95
+ if (isDevelopment && renderTime > 100) {
96
+ console.warn(
97
+ new MasterControllerError({
98
+ code: 'MC_ERR_SLOW_RENDER',
99
+ message: `Component rendering slowly on server (${renderTime}ms)`,
100
+ component: componentName,
101
+ file: filePath,
102
+ details: `Render time exceeded 100ms threshold. Consider optimizing this component.`
103
+ }).format()
104
+ );
105
+ }
106
+
107
+ return { success: true, html: result, renderTime };
108
+ }
109
+
110
+ // Fallback to connectedCallback
111
+ if (typeof component.connectedCallback === 'function') {
112
+ const startTime = Date.now();
113
+ component.connectedCallback();
114
+ const renderTime = Date.now() - startTime;
115
+
116
+ // Get the rendered HTML
117
+ const html = component.innerHTML || '';
118
+ return { success: true, html, renderTime };
119
+ }
120
+
121
+ // No render method found
122
+ throw new Error('No tempRender() or connectedCallback() method found');
123
+
124
+ } catch (error) {
125
+ console.error(
126
+ new MasterControllerError({
127
+ code: 'MC_ERR_COMPONENT_RENDER_FAILED',
128
+ message: `Failed to render component: ${error.message}`,
129
+ component: componentName,
130
+ file: filePath,
131
+ originalError: error
132
+ }).format()
133
+ );
134
+
135
+ if (isDevelopment) {
136
+ return {
137
+ success: false,
138
+ html: renderErrorComponent({
139
+ component: componentName,
140
+ error: error.message,
141
+ stack: error.stack,
142
+ file: filePath,
143
+ originalError: error
144
+ }),
145
+ renderTime: 0
146
+ };
147
+ } else {
148
+ // Log error for monitoring
149
+ logProductionError(error, componentName, filePath);
150
+
151
+ return {
152
+ success: false,
153
+ html: renderFallback(componentName, { showSkeleton: true }),
154
+ renderTime: 0
155
+ };
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Log production errors for monitoring
162
+ */
163
+ function logProductionError(error, component, file) {
164
+ const errorData = {
165
+ timestamp: new Date().toISOString(),
166
+ component,
167
+ file,
168
+ message: error.message,
169
+ stack: error.stack,
170
+ environment: 'production'
171
+ };
172
+
173
+ // Log to console (can be captured by monitoring services)
174
+ console.error('[MasterController Production Error]', JSON.stringify(errorData, null, 2));
175
+
176
+ // Hook for external logging services (Sentry, LogRocket, etc.)
177
+ if (global.masterControllerErrorHook) {
178
+ try {
179
+ global.masterControllerErrorHook(errorData);
180
+ } catch (hookError) {
181
+ console.error('[MasterController] Error hook failed:', hookError.message);
182
+ }
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Wrap connectedCallback with try-catch
188
+ */
189
+ function wrapConnectedCallback(element, componentName, filePath) {
190
+ if (!element || typeof element.connectedCallback !== 'function') {
191
+ return;
192
+ }
193
+
194
+ const originalCallback = element.connectedCallback;
195
+
196
+ element.connectedCallback = function(...args) {
197
+ try {
198
+ return originalCallback.apply(this, args);
199
+ } catch (error) {
200
+ console.error(
201
+ new MasterControllerError({
202
+ code: 'MC_ERR_COMPONENT_RENDER_FAILED',
203
+ message: `connectedCallback failed: ${error.message}`,
204
+ component: componentName,
205
+ file: filePath,
206
+ originalError: error
207
+ }).format()
208
+ );
209
+
210
+ if (isDevelopment) {
211
+ this.innerHTML = renderErrorComponent({
212
+ component: componentName,
213
+ error: error.message,
214
+ stack: error.stack,
215
+ file: filePath,
216
+ originalError: error
217
+ });
218
+ } else {
219
+ logProductionError(error, componentName, filePath);
220
+ this.innerHTML = renderFallback(componentName);
221
+ }
222
+ }
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Check if component has required SSR methods
228
+ */
229
+ function validateSSRComponent(componentClass, componentName, filePath) {
230
+ const warnings = [];
231
+
232
+ // Check for tempRender method
233
+ if (!componentClass.prototype.tempRender && !componentClass.prototype.connectedCallback) {
234
+ warnings.push(
235
+ new MasterControllerError({
236
+ code: 'MC_ERR_TEMPRENDER_MISSING',
237
+ message: 'Component missing tempRender() method for SSR',
238
+ component: componentName,
239
+ file: filePath,
240
+ details: `Add a tempRender() method to enable server-side rendering:\n\n tempRender() {\n return \`<div>Your HTML here</div>\`;\n }`
241
+ })
242
+ );
243
+ }
244
+
245
+ // Warn if only render() exists (client-only)
246
+ if (componentClass.prototype.render && !componentClass.prototype.tempRender) {
247
+ warnings.push(
248
+ new MasterControllerError({
249
+ code: 'MC_ERR_TEMPRENDER_MISSING',
250
+ message: 'Component has render() but no tempRender() - will not SSR',
251
+ component: componentName,
252
+ file: filePath,
253
+ details: 'For SSR, rename render() to tempRender() or add a separate tempRender() method'
254
+ })
255
+ );
256
+ }
257
+
258
+ if (warnings.length > 0 && isDevelopment) {
259
+ warnings.forEach(warning => console.warn(warning.format()));
260
+ }
261
+
262
+ return warnings.length === 0;
263
+ }
264
+
265
+ module.exports = {
266
+ renderErrorComponent,
267
+ renderFallback,
268
+ safeRenderComponent,
269
+ wrapConnectedCallback,
270
+ validateSSRComponent,
271
+ logProductionError,
272
+ isDevelopment
273
+ };