@visualprd/mcp-server 1.1.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/README.md +396 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +27 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +243 -0
- package/dist/index.js.map +1 -0
- package/dist/intelligence/context-optimizer.d.ts +93 -0
- package/dist/intelligence/context-optimizer.d.ts.map +1 -0
- package/dist/intelligence/context-optimizer.js +481 -0
- package/dist/intelligence/context-optimizer.js.map +1 -0
- package/dist/intelligence/error-analyzer.d.ts +49 -0
- package/dist/intelligence/error-analyzer.d.ts.map +1 -0
- package/dist/intelligence/error-analyzer.js +765 -0
- package/dist/intelligence/error-analyzer.js.map +1 -0
- package/dist/intelligence/gap-filler.d.ts +56 -0
- package/dist/intelligence/gap-filler.d.ts.map +1 -0
- package/dist/intelligence/gap-filler.js +410 -0
- package/dist/intelligence/gap-filler.js.map +1 -0
- package/dist/intelligence/guidance-generator.d.ts +43 -0
- package/dist/intelligence/guidance-generator.d.ts.map +1 -0
- package/dist/intelligence/guidance-generator.js +314 -0
- package/dist/intelligence/guidance-generator.js.map +1 -0
- package/dist/intelligence/index.d.ts +132 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/index.js +683 -0
- package/dist/intelligence/index.js.map +1 -0
- package/dist/server-http.d.ts +9 -0
- package/dist/server-http.d.ts.map +1 -0
- package/dist/server-http.js +141 -0
- package/dist/server-http.js.map +1 -0
- package/dist/services/api-key-service.d.ts +68 -0
- package/dist/services/api-key-service.d.ts.map +1 -0
- package/dist/services/api-key-service.js +298 -0
- package/dist/services/api-key-service.js.map +1 -0
- package/dist/services/llm-client.d.ts +66 -0
- package/dist/services/llm-client.d.ts.map +1 -0
- package/dist/services/llm-client.js +141 -0
- package/dist/services/llm-client.js.map +1 -0
- package/dist/services/model-registry.d.ts +135 -0
- package/dist/services/model-registry.d.ts.map +1 -0
- package/dist/services/model-registry.js +276 -0
- package/dist/services/model-registry.js.map +1 -0
- package/dist/services/visualprd-client.d.ts +191 -0
- package/dist/services/visualprd-client.d.ts.map +1 -0
- package/dist/services/visualprd-client.js +805 -0
- package/dist/services/visualprd-client.js.map +1 -0
- package/dist/tools/index.d.ts +803 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +570 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +497 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Error Analyzer
|
|
4
|
+
*
|
|
5
|
+
* Intelligently analyzes errors reported during the build process
|
|
6
|
+
* and provides actionable fixes, migrations, or new build steps.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ErrorAnalyzer = void 0;
|
|
10
|
+
class ErrorAnalyzer {
|
|
11
|
+
patterns = [];
|
|
12
|
+
constructor() {
|
|
13
|
+
this.registerPatterns();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Analyze an error and provide diagnosis + fixes
|
|
17
|
+
*/
|
|
18
|
+
async analyzeError(error, context) {
|
|
19
|
+
// Try to match against known patterns
|
|
20
|
+
for (const pattern of this.patterns) {
|
|
21
|
+
const matches = typeof pattern.pattern === 'string'
|
|
22
|
+
? error.errorMessage.includes(pattern.pattern)
|
|
23
|
+
: pattern.pattern.test(error.errorMessage);
|
|
24
|
+
if (matches) {
|
|
25
|
+
const partialResult = await pattern.analyzer(error, context);
|
|
26
|
+
return this.buildFullResult(error, context, partialResult, pattern.type);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Fallback: Generic analysis
|
|
30
|
+
return this.genericAnalysis(error, context);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Register known error patterns
|
|
34
|
+
*/
|
|
35
|
+
registerPatterns() {
|
|
36
|
+
// Null/undefined reference errors
|
|
37
|
+
this.patterns.push({
|
|
38
|
+
pattern: /Cannot read propert(y|ies) .* of (undefined|null)/i,
|
|
39
|
+
type: 'null_reference',
|
|
40
|
+
analyzer: this.analyzeNullReference.bind(this),
|
|
41
|
+
});
|
|
42
|
+
this.patterns.push({
|
|
43
|
+
pattern: /TypeError:.*is (undefined|null)/i,
|
|
44
|
+
type: 'null_reference',
|
|
45
|
+
analyzer: this.analyzeNullReference.bind(this),
|
|
46
|
+
});
|
|
47
|
+
// Module/import errors
|
|
48
|
+
this.patterns.push({
|
|
49
|
+
pattern: /Module not found|Cannot find module/i,
|
|
50
|
+
type: 'missing_module',
|
|
51
|
+
analyzer: this.analyzeMissingModule.bind(this),
|
|
52
|
+
});
|
|
53
|
+
this.patterns.push({
|
|
54
|
+
pattern: /is not exported from/i,
|
|
55
|
+
type: 'export_error',
|
|
56
|
+
analyzer: this.analyzeExportError.bind(this),
|
|
57
|
+
});
|
|
58
|
+
// Schema/field mismatch errors
|
|
59
|
+
this.patterns.push({
|
|
60
|
+
pattern: /field .* does not exist|unknown field|property .* does not exist on type/i,
|
|
61
|
+
type: 'schema_mismatch',
|
|
62
|
+
analyzer: this.analyzeSchemaMismatch.bind(this),
|
|
63
|
+
});
|
|
64
|
+
this.patterns.push({
|
|
65
|
+
pattern: /Expected .* but received|Type .* is not assignable/i,
|
|
66
|
+
type: 'type_mismatch',
|
|
67
|
+
analyzer: this.analyzeTypeMismatch.bind(this),
|
|
68
|
+
});
|
|
69
|
+
// Firebase/Firestore errors
|
|
70
|
+
this.patterns.push({
|
|
71
|
+
pattern: /PERMISSION_DENIED|Missing or insufficient permissions/i,
|
|
72
|
+
type: 'permission_error',
|
|
73
|
+
analyzer: this.analyzePermissionError.bind(this),
|
|
74
|
+
});
|
|
75
|
+
this.patterns.push({
|
|
76
|
+
pattern: /FirebaseError|Firebase:.*Error/i,
|
|
77
|
+
type: 'firebase_error',
|
|
78
|
+
analyzer: this.analyzeFirebaseError.bind(this),
|
|
79
|
+
});
|
|
80
|
+
// API/Network errors
|
|
81
|
+
this.patterns.push({
|
|
82
|
+
pattern: /fetch failed|network error|ECONNREFUSED/i,
|
|
83
|
+
type: 'network_error',
|
|
84
|
+
analyzer: this.analyzeNetworkError.bind(this),
|
|
85
|
+
});
|
|
86
|
+
this.patterns.push({
|
|
87
|
+
pattern: /4\d{2}|5\d{2}.*error|HTTP Error/i,
|
|
88
|
+
type: 'http_error',
|
|
89
|
+
analyzer: this.analyzeHTTPError.bind(this),
|
|
90
|
+
});
|
|
91
|
+
// Test failures
|
|
92
|
+
this.patterns.push({
|
|
93
|
+
pattern: /test failed|assertion.*failed|expect.*to/i,
|
|
94
|
+
type: 'test_failure',
|
|
95
|
+
analyzer: this.analyzeTestFailure.bind(this),
|
|
96
|
+
});
|
|
97
|
+
// Syntax/compilation errors
|
|
98
|
+
this.patterns.push({
|
|
99
|
+
pattern: /SyntaxError|Unexpected token/i,
|
|
100
|
+
type: 'syntax_error',
|
|
101
|
+
analyzer: this.analyzeSyntaxError.bind(this),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// PATTERN-SPECIFIC ANALYZERS
|
|
106
|
+
// ============================================================================
|
|
107
|
+
async analyzeNullReference(error, context) {
|
|
108
|
+
// Extract property name from error
|
|
109
|
+
const propertyMatch = error.errorMessage.match(/Cannot read propert(?:y|ies) ['"]?(\w+)['"]?/i);
|
|
110
|
+
const propertyName = propertyMatch?.[1];
|
|
111
|
+
// Check if property exists in any schema
|
|
112
|
+
let relatedSchema;
|
|
113
|
+
let fieldExists = false;
|
|
114
|
+
if (propertyName) {
|
|
115
|
+
for (const schema of context.projectData.schemas) {
|
|
116
|
+
const field = schema.fields?.find((f) => f.fieldName.toLowerCase() === propertyName.toLowerCase());
|
|
117
|
+
if (field) {
|
|
118
|
+
relatedSchema = schema;
|
|
119
|
+
fieldExists = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const diagnosis = {
|
|
125
|
+
rootCause: fieldExists
|
|
126
|
+
? `The field "${propertyName}" is defined in schema but the object is null/undefined at runtime`
|
|
127
|
+
: `Attempting to access property "${propertyName || 'unknown'}" on a null or undefined object`,
|
|
128
|
+
explanation: fieldExists
|
|
129
|
+
? 'This usually means the data hasn\'t been fetched yet, or the query returned no results. Add null checks or ensure data is loaded before accessing.'
|
|
130
|
+
: 'The code assumes an object exists but it\'s actually null/undefined. Add defensive checks.',
|
|
131
|
+
errorCategory: 'null_reference',
|
|
132
|
+
severity: 'medium',
|
|
133
|
+
};
|
|
134
|
+
const fixes = [
|
|
135
|
+
{
|
|
136
|
+
approach: 'Optional Chaining',
|
|
137
|
+
steps: [
|
|
138
|
+
`Use optional chaining (?.) when accessing ${propertyName || 'the property'}`,
|
|
139
|
+
'Add nullish coalescing (??) for default values',
|
|
140
|
+
],
|
|
141
|
+
code: propertyName
|
|
142
|
+
? {
|
|
143
|
+
filename: 'fix.ts',
|
|
144
|
+
content: `// Instead of:\nconst value = obj.${propertyName};\n\n// Use:\nconst value = obj?.${propertyName} ?? defaultValue;`,
|
|
145
|
+
language: 'typescript',
|
|
146
|
+
}
|
|
147
|
+
: undefined,
|
|
148
|
+
confidence: 0.9,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
approach: 'Early Return Guard',
|
|
152
|
+
steps: [
|
|
153
|
+
'Add a guard clause at the start of the function',
|
|
154
|
+
'Return early or throw a meaningful error if data is missing',
|
|
155
|
+
],
|
|
156
|
+
code: {
|
|
157
|
+
filename: 'fix.ts',
|
|
158
|
+
content: `// Add at the start of your function:\nif (!data) {\n console.warn('Data not loaded yet');\n return null; // or throw new Error('Data required');\n}`,
|
|
159
|
+
language: 'typescript',
|
|
160
|
+
},
|
|
161
|
+
confidence: 0.85,
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
// If it's a schema field issue, suggest checking data loading
|
|
165
|
+
if (fieldExists && relatedSchema) {
|
|
166
|
+
fixes.unshift({
|
|
167
|
+
approach: 'Verify Data Loading',
|
|
168
|
+
steps: [
|
|
169
|
+
`Check that ${relatedSchema.collectionName} data is being fetched correctly`,
|
|
170
|
+
'Verify the document exists in Firestore',
|
|
171
|
+
'Ensure the query is returning the expected document',
|
|
172
|
+
],
|
|
173
|
+
confidence: 0.95,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
diagnosis,
|
|
178
|
+
suggestedFixes: fixes,
|
|
179
|
+
relatedDocs: relatedSchema ? { schemas: [relatedSchema] } : {},
|
|
180
|
+
shouldUncheckStep: false,
|
|
181
|
+
shouldInjectNewStep: false,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async analyzeMissingModule(error, context) {
|
|
185
|
+
// Extract module name
|
|
186
|
+
const moduleMatch = error.errorMessage.match(/Cannot find module ['"]([^'"]+)['"]/i);
|
|
187
|
+
const moduleName = moduleMatch?.[1] || 'unknown';
|
|
188
|
+
const isLocalModule = moduleName.startsWith('.') || moduleName.startsWith('@/');
|
|
189
|
+
const isNodeModule = !isLocalModule;
|
|
190
|
+
const diagnosis = {
|
|
191
|
+
rootCause: isNodeModule
|
|
192
|
+
? `The npm package "${moduleName}" is not installed`
|
|
193
|
+
: `The local module "${moduleName}" doesn't exist or path is incorrect`,
|
|
194
|
+
explanation: isNodeModule
|
|
195
|
+
? 'You need to install this dependency using npm or yarn'
|
|
196
|
+
: 'Check the file path and ensure the file exists at that location',
|
|
197
|
+
errorCategory: 'missing_module',
|
|
198
|
+
severity: 'high',
|
|
199
|
+
};
|
|
200
|
+
const fixes = [];
|
|
201
|
+
if (isNodeModule) {
|
|
202
|
+
// Check if it's in tech stack
|
|
203
|
+
const techItem = context.projectData.techStack.find((t) => moduleName.toLowerCase().includes(t.name.toLowerCase()) ||
|
|
204
|
+
t.name.toLowerCase().includes(moduleName.split('/')[0].replace('@', '')));
|
|
205
|
+
fixes.push({
|
|
206
|
+
approach: 'Install Package',
|
|
207
|
+
steps: [
|
|
208
|
+
`Run: npm install ${moduleName}`,
|
|
209
|
+
'Or with yarn: yarn add ' + moduleName,
|
|
210
|
+
'Restart your development server after installation',
|
|
211
|
+
],
|
|
212
|
+
code: {
|
|
213
|
+
filename: 'terminal',
|
|
214
|
+
content: `npm install ${moduleName}`,
|
|
215
|
+
language: 'bash',
|
|
216
|
+
},
|
|
217
|
+
confidence: 0.95,
|
|
218
|
+
});
|
|
219
|
+
if (techItem) {
|
|
220
|
+
fixes[0].steps.push(`Note: "${techItem.name}" is in your tech stack - ensure version matches: ${techItem.version || 'latest'}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
fixes.push({
|
|
225
|
+
approach: 'Fix Import Path',
|
|
226
|
+
steps: [
|
|
227
|
+
'Verify the file exists at the specified path',
|
|
228
|
+
'Check for typos in the filename or path',
|
|
229
|
+
'Ensure the file extension is correct (.ts, .tsx, .js)',
|
|
230
|
+
'If using path aliases (@/), check tsconfig.json paths configuration',
|
|
231
|
+
],
|
|
232
|
+
confidence: 0.8,
|
|
233
|
+
});
|
|
234
|
+
fixes.push({
|
|
235
|
+
approach: 'Create Missing File',
|
|
236
|
+
steps: [
|
|
237
|
+
`Create the missing file at: ${moduleName}`,
|
|
238
|
+
'Export the required functions/components',
|
|
239
|
+
],
|
|
240
|
+
confidence: 0.7,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
diagnosis,
|
|
245
|
+
suggestedFixes: fixes,
|
|
246
|
+
shouldUncheckStep: false,
|
|
247
|
+
shouldInjectNewStep: false,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
async analyzeExportError(error, context) {
|
|
251
|
+
const exportMatch = error.errorMessage.match(/['"](\w+)['"] is not exported from ['"]([^'"]+)['"]/i);
|
|
252
|
+
const exportName = exportMatch?.[1] || 'unknown';
|
|
253
|
+
const modulePath = exportMatch?.[2] || 'unknown';
|
|
254
|
+
const diagnosis = {
|
|
255
|
+
rootCause: `The export "${exportName}" doesn't exist in module "${modulePath}"`,
|
|
256
|
+
explanation: 'Either the export name is wrong, or the module needs to export this item',
|
|
257
|
+
errorCategory: 'export_error',
|
|
258
|
+
severity: 'medium',
|
|
259
|
+
};
|
|
260
|
+
const fixes = [
|
|
261
|
+
{
|
|
262
|
+
approach: 'Check Export Name',
|
|
263
|
+
steps: [
|
|
264
|
+
`Open ${modulePath} and check available exports`,
|
|
265
|
+
'Look for typos in the export name',
|
|
266
|
+
'Check if it\'s a default vs named export',
|
|
267
|
+
],
|
|
268
|
+
confidence: 0.85,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
approach: 'Add Missing Export',
|
|
272
|
+
steps: [
|
|
273
|
+
`Add the export to ${modulePath}`,
|
|
274
|
+
`export const ${exportName} = ...;`,
|
|
275
|
+
'Or: export { ${exportName} };',
|
|
276
|
+
],
|
|
277
|
+
code: {
|
|
278
|
+
filename: modulePath,
|
|
279
|
+
content: `// Add this export to ${modulePath}:\nexport const ${exportName} = /* your implementation */;`,
|
|
280
|
+
language: 'typescript',
|
|
281
|
+
},
|
|
282
|
+
confidence: 0.8,
|
|
283
|
+
},
|
|
284
|
+
];
|
|
285
|
+
return {
|
|
286
|
+
diagnosis,
|
|
287
|
+
suggestedFixes: fixes,
|
|
288
|
+
shouldUncheckStep: false,
|
|
289
|
+
shouldInjectNewStep: false,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async analyzeSchemaMismatch(error, context) {
|
|
293
|
+
// Extract field name
|
|
294
|
+
const fieldMatch = error.errorMessage.match(/(?:field|property) ['"]?(\w+)['"]?/i);
|
|
295
|
+
const fieldName = fieldMatch?.[1];
|
|
296
|
+
// Find schema that might have this field
|
|
297
|
+
let relatedSchema;
|
|
298
|
+
let fieldDefinedInSchema = false;
|
|
299
|
+
if (fieldName) {
|
|
300
|
+
for (const schema of context.projectData.schemas) {
|
|
301
|
+
const field = schema.fields?.find((f) => f.fieldName.toLowerCase() === fieldName.toLowerCase());
|
|
302
|
+
if (field) {
|
|
303
|
+
relatedSchema = schema;
|
|
304
|
+
fieldDefinedInSchema = true;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const diagnosis = {
|
|
310
|
+
rootCause: fieldDefinedInSchema
|
|
311
|
+
? `Field "${fieldName}" is defined in schema but not present in the actual database document`
|
|
312
|
+
: `Code references field "${fieldName || 'unknown'}" which doesn't exist in any schema`,
|
|
313
|
+
explanation: fieldDefinedInSchema
|
|
314
|
+
? 'The schema definition is ahead of the actual database state. You need to migrate existing documents or add the field to new documents.'
|
|
315
|
+
: 'Either add this field to the appropriate schema, or fix the code to use an existing field.',
|
|
316
|
+
errorCategory: 'schema_mismatch',
|
|
317
|
+
severity: fieldDefinedInSchema ? 'high' : 'medium',
|
|
318
|
+
};
|
|
319
|
+
const fixes = [];
|
|
320
|
+
if (fieldDefinedInSchema && relatedSchema) {
|
|
321
|
+
// Field is in schema but not in DB - need migration
|
|
322
|
+
fixes.push({
|
|
323
|
+
approach: 'Database Migration',
|
|
324
|
+
steps: [
|
|
325
|
+
`Create a migration script for ${relatedSchema.collectionName} collection`,
|
|
326
|
+
`Add default value for "${fieldName}" to existing documents`,
|
|
327
|
+
'Run the migration before continuing',
|
|
328
|
+
],
|
|
329
|
+
code: {
|
|
330
|
+
filename: `migrations/add-${fieldName}-to-${relatedSchema.collectionName}.ts`,
|
|
331
|
+
content: `// Migration script\nimport { getFirestore } from 'firebase-admin/firestore';\n\nconst db = getFirestore();\n\nexport async function migrate() {\n const snapshot = await db.collection('${relatedSchema.collectionName}').get();\n \n const batch = db.batch();\n snapshot.docs.forEach(doc => {\n if (doc.data().${fieldName} === undefined) {\n batch.update(doc.ref, { ${fieldName}: /* default value */ });\n }\n });\n \n await batch.commit();\n console.log('Migration complete');\n}`,
|
|
332
|
+
language: 'typescript',
|
|
333
|
+
},
|
|
334
|
+
confidence: 0.9,
|
|
335
|
+
});
|
|
336
|
+
// Should inject a migration step
|
|
337
|
+
return {
|
|
338
|
+
diagnosis,
|
|
339
|
+
suggestedFixes: fixes,
|
|
340
|
+
relatedDocs: { schemas: [relatedSchema] },
|
|
341
|
+
shouldUncheckStep: true,
|
|
342
|
+
shouldInjectNewStep: true,
|
|
343
|
+
newStep: {
|
|
344
|
+
title: `Database Migration: Add ${fieldName} to ${relatedSchema.collectionName}`,
|
|
345
|
+
category: 'database',
|
|
346
|
+
instruction: `Run a migration to add the "${fieldName}" field to existing documents in the ${relatedSchema.collectionName} collection.\n\nSteps:\n1. Create migration script\n2. Test on a few documents first\n3. Run for all documents\n4. Verify the field exists in documents`,
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
// Field not in schema - add defensive coding
|
|
351
|
+
fixes.push({
|
|
352
|
+
approach: 'Defensive Coding',
|
|
353
|
+
steps: [
|
|
354
|
+
'Add null check before accessing the field',
|
|
355
|
+
'Provide a sensible default value',
|
|
356
|
+
],
|
|
357
|
+
code: fieldName
|
|
358
|
+
? {
|
|
359
|
+
filename: 'fix.ts',
|
|
360
|
+
content: `// Safe access with default:\nconst ${fieldName} = document?.${fieldName} ?? defaultValue;`,
|
|
361
|
+
language: 'typescript',
|
|
362
|
+
}
|
|
363
|
+
: undefined,
|
|
364
|
+
confidence: 0.85,
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
diagnosis,
|
|
368
|
+
suggestedFixes: fixes,
|
|
369
|
+
relatedDocs: relatedSchema ? { schemas: [relatedSchema] } : {},
|
|
370
|
+
shouldUncheckStep: false,
|
|
371
|
+
shouldInjectNewStep: false,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
async analyzeTypeMismatch(error, context) {
|
|
375
|
+
const diagnosis = {
|
|
376
|
+
rootCause: 'Type mismatch between expected and actual values',
|
|
377
|
+
explanation: 'TypeScript is detecting that the types don\'t match. This could be a data transformation issue or incorrect type definition.',
|
|
378
|
+
errorCategory: 'type_mismatch',
|
|
379
|
+
severity: 'medium',
|
|
380
|
+
};
|
|
381
|
+
const fixes = [
|
|
382
|
+
{
|
|
383
|
+
approach: 'Type Assertion',
|
|
384
|
+
steps: [
|
|
385
|
+
'If you\'re sure about the type, use type assertion',
|
|
386
|
+
'But first verify the data actually matches',
|
|
387
|
+
],
|
|
388
|
+
code: {
|
|
389
|
+
filename: 'fix.ts',
|
|
390
|
+
content: `// Type assertion (use carefully):\nconst value = data as ExpectedType;\n\n// Or with validation:\nif (isExpectedType(data)) {\n const value = data;\n}`,
|
|
391
|
+
language: 'typescript',
|
|
392
|
+
},
|
|
393
|
+
confidence: 0.7,
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
approach: 'Add Type Guard',
|
|
397
|
+
steps: [
|
|
398
|
+
'Create a type guard function to validate the data',
|
|
399
|
+
'Use it before processing',
|
|
400
|
+
],
|
|
401
|
+
code: {
|
|
402
|
+
filename: 'fix.ts',
|
|
403
|
+
content: `function isExpectedType(data: unknown): data is ExpectedType {\n return (\n typeof data === 'object' &&\n data !== null &&\n 'requiredField' in data\n );\n}`,
|
|
404
|
+
language: 'typescript',
|
|
405
|
+
},
|
|
406
|
+
confidence: 0.85,
|
|
407
|
+
},
|
|
408
|
+
];
|
|
409
|
+
return {
|
|
410
|
+
diagnosis,
|
|
411
|
+
suggestedFixes: fixes,
|
|
412
|
+
shouldUncheckStep: false,
|
|
413
|
+
shouldInjectNewStep: false,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
async analyzePermissionError(error, context) {
|
|
417
|
+
const diagnosis = {
|
|
418
|
+
rootCause: 'Firestore security rules are blocking this operation',
|
|
419
|
+
explanation: 'The current user doesn\'t have permission to read/write this data. Check your Firestore security rules.',
|
|
420
|
+
errorCategory: 'permission_error',
|
|
421
|
+
severity: 'high',
|
|
422
|
+
};
|
|
423
|
+
// Find relevant tech stack (Firebase)
|
|
424
|
+
const firebaseTech = context.projectData.techStack.find((t) => t.name.toLowerCase().includes('firebase'));
|
|
425
|
+
const fixes = [
|
|
426
|
+
{
|
|
427
|
+
approach: 'Update Security Rules',
|
|
428
|
+
steps: [
|
|
429
|
+
'Review firestore.rules file',
|
|
430
|
+
'Ensure the rule allows the current user to perform this operation',
|
|
431
|
+
'Check if authentication is required and user is logged in',
|
|
432
|
+
],
|
|
433
|
+
code: {
|
|
434
|
+
filename: 'firestore.rules',
|
|
435
|
+
content: `rules_version = '2';\nservice cloud.firestore {\n match /databases/{database}/documents {\n // Example rule - adjust for your needs:\n match /collection/{docId} {\n allow read: if request.auth != null;\n allow write: if request.auth != null && request.auth.uid == resource.data.userId;\n }\n }\n}`,
|
|
436
|
+
language: 'text',
|
|
437
|
+
},
|
|
438
|
+
confidence: 0.85,
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
approach: 'Verify Authentication',
|
|
442
|
+
steps: [
|
|
443
|
+
'Ensure user is authenticated before this operation',
|
|
444
|
+
'Check if auth state is initialized',
|
|
445
|
+
'Verify the auth token hasn\'t expired',
|
|
446
|
+
],
|
|
447
|
+
code: {
|
|
448
|
+
filename: 'auth-check.ts',
|
|
449
|
+
content: `// Verify auth before Firestore operation:\nimport { auth } from './firebase';\n\nconst user = auth.currentUser;\nif (!user) {\n throw new Error('User must be authenticated');\n}\n\n// Now proceed with Firestore operation`,
|
|
450
|
+
language: 'typescript',
|
|
451
|
+
},
|
|
452
|
+
confidence: 0.8,
|
|
453
|
+
},
|
|
454
|
+
];
|
|
455
|
+
return {
|
|
456
|
+
diagnosis,
|
|
457
|
+
suggestedFixes: fixes,
|
|
458
|
+
shouldUncheckStep: false,
|
|
459
|
+
shouldInjectNewStep: false,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
async analyzeFirebaseError(error, context) {
|
|
463
|
+
// Try to extract specific Firebase error code
|
|
464
|
+
const codeMatch = error.errorMessage.match(/auth\/[\w-]+/);
|
|
465
|
+
const errorCode = codeMatch?.[0];
|
|
466
|
+
let specificFix;
|
|
467
|
+
// Common Firebase Auth errors
|
|
468
|
+
const authErrorFixes = {
|
|
469
|
+
'auth/user-not-found': {
|
|
470
|
+
approach: 'Handle User Not Found',
|
|
471
|
+
steps: ['Check if user exists before operation', 'Provide user-friendly error message'],
|
|
472
|
+
confidence: 0.9,
|
|
473
|
+
},
|
|
474
|
+
'auth/wrong-password': {
|
|
475
|
+
approach: 'Handle Wrong Password',
|
|
476
|
+
steps: ['Show generic "invalid credentials" message for security', 'Offer password reset'],
|
|
477
|
+
confidence: 0.9,
|
|
478
|
+
},
|
|
479
|
+
'auth/email-already-in-use': {
|
|
480
|
+
approach: 'Handle Existing Email',
|
|
481
|
+
steps: ['Inform user the email is taken', 'Offer login option instead'],
|
|
482
|
+
confidence: 0.9,
|
|
483
|
+
},
|
|
484
|
+
'auth/weak-password': {
|
|
485
|
+
approach: 'Enforce Password Requirements',
|
|
486
|
+
steps: ['Show password requirements to user', 'Validate before submission'],
|
|
487
|
+
confidence: 0.9,
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
if (errorCode && authErrorFixes[errorCode]) {
|
|
491
|
+
specificFix = authErrorFixes[errorCode];
|
|
492
|
+
}
|
|
493
|
+
const diagnosis = {
|
|
494
|
+
rootCause: errorCode
|
|
495
|
+
? `Firebase error: ${errorCode}`
|
|
496
|
+
: 'Firebase operation failed',
|
|
497
|
+
explanation: 'A Firebase service encountered an error. Check the specific error code for details.',
|
|
498
|
+
errorCategory: 'firebase_error',
|
|
499
|
+
severity: 'medium',
|
|
500
|
+
};
|
|
501
|
+
const fixes = specificFix
|
|
502
|
+
? [specificFix]
|
|
503
|
+
: [
|
|
504
|
+
{
|
|
505
|
+
approach: 'Add Error Handling',
|
|
506
|
+
steps: [
|
|
507
|
+
'Wrap Firebase calls in try-catch',
|
|
508
|
+
'Check error.code for specific handling',
|
|
509
|
+
'Provide user-friendly error messages',
|
|
510
|
+
],
|
|
511
|
+
code: {
|
|
512
|
+
filename: 'firebase-error-handling.ts',
|
|
513
|
+
content: `try {\n await firebaseOperation();\n} catch (error: any) {\n switch (error.code) {\n case 'auth/user-not-found':\n // Handle specific error\n break;\n default:\n console.error('Firebase error:', error);\n }\n}`,
|
|
514
|
+
language: 'typescript',
|
|
515
|
+
},
|
|
516
|
+
confidence: 0.75,
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
return {
|
|
520
|
+
diagnosis,
|
|
521
|
+
suggestedFixes: fixes,
|
|
522
|
+
shouldUncheckStep: false,
|
|
523
|
+
shouldInjectNewStep: false,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
async analyzeNetworkError(error, context) {
|
|
527
|
+
const diagnosis = {
|
|
528
|
+
rootCause: 'Network request failed - server unreachable or connection issue',
|
|
529
|
+
explanation: 'The API endpoint or server is not responding. This could be a server issue, CORS problem, or network connectivity.',
|
|
530
|
+
errorCategory: 'network_error',
|
|
531
|
+
severity: 'high',
|
|
532
|
+
};
|
|
533
|
+
const fixes = [
|
|
534
|
+
{
|
|
535
|
+
approach: 'Check API Server',
|
|
536
|
+
steps: [
|
|
537
|
+
'Verify the API server is running',
|
|
538
|
+
'Check the URL is correct',
|
|
539
|
+
'Try the endpoint directly in browser or Postman',
|
|
540
|
+
],
|
|
541
|
+
confidence: 0.8,
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
approach: 'Handle Network Errors',
|
|
545
|
+
steps: [
|
|
546
|
+
'Add retry logic for transient failures',
|
|
547
|
+
'Show user-friendly offline message',
|
|
548
|
+
'Consider using a library like axios with interceptors',
|
|
549
|
+
],
|
|
550
|
+
code: {
|
|
551
|
+
filename: 'network-retry.ts',
|
|
552
|
+
content: `async function fetchWithRetry(url: string, retries = 3): Promise<Response> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fetch(url);\n } catch (error) {\n if (i === retries - 1) throw error;\n await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n }\n }\n throw new Error('Max retries reached');\n}`,
|
|
553
|
+
language: 'typescript',
|
|
554
|
+
},
|
|
555
|
+
confidence: 0.85,
|
|
556
|
+
},
|
|
557
|
+
];
|
|
558
|
+
return {
|
|
559
|
+
diagnosis,
|
|
560
|
+
suggestedFixes: fixes,
|
|
561
|
+
shouldUncheckStep: false,
|
|
562
|
+
shouldInjectNewStep: false,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
async analyzeHTTPError(error, context) {
|
|
566
|
+
// Extract status code
|
|
567
|
+
const statusMatch = error.errorMessage.match(/\b([45]\d{2})\b/);
|
|
568
|
+
const statusCode = statusMatch ? parseInt(statusMatch[1]) : 500;
|
|
569
|
+
const statusMessages = {
|
|
570
|
+
400: 'Bad request - check your request payload',
|
|
571
|
+
401: 'Unauthorized - authentication required or token expired',
|
|
572
|
+
403: 'Forbidden - user lacks permission',
|
|
573
|
+
404: 'Not found - check the endpoint URL',
|
|
574
|
+
422: 'Validation error - check request data format',
|
|
575
|
+
429: 'Too many requests - implement rate limiting',
|
|
576
|
+
500: 'Server error - check server logs',
|
|
577
|
+
502: 'Bad gateway - upstream server issue',
|
|
578
|
+
503: 'Service unavailable - server may be down',
|
|
579
|
+
};
|
|
580
|
+
const diagnosis = {
|
|
581
|
+
rootCause: `HTTP ${statusCode}: ${statusMessages[statusCode] || 'Request failed'}`,
|
|
582
|
+
explanation: 'The API returned an error status code. Check the response body for details.',
|
|
583
|
+
errorCategory: 'http_error',
|
|
584
|
+
severity: statusCode >= 500 ? 'high' : 'medium',
|
|
585
|
+
};
|
|
586
|
+
const fixes = [];
|
|
587
|
+
if (statusCode === 401) {
|
|
588
|
+
fixes.push({
|
|
589
|
+
approach: 'Refresh Authentication',
|
|
590
|
+
steps: [
|
|
591
|
+
'Check if auth token exists and is valid',
|
|
592
|
+
'Implement token refresh logic',
|
|
593
|
+
'Redirect to login if refresh fails',
|
|
594
|
+
],
|
|
595
|
+
confidence: 0.9,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
else if (statusCode === 404) {
|
|
599
|
+
fixes.push({
|
|
600
|
+
approach: 'Verify Endpoint',
|
|
601
|
+
steps: [
|
|
602
|
+
'Check the API URL is correct',
|
|
603
|
+
'Verify the resource ID exists',
|
|
604
|
+
'Check if the endpoint is deployed',
|
|
605
|
+
],
|
|
606
|
+
confidence: 0.85,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
fixes.push({
|
|
611
|
+
approach: 'Add Error Response Handling',
|
|
612
|
+
steps: [
|
|
613
|
+
'Parse the error response body',
|
|
614
|
+
'Display specific error message to user',
|
|
615
|
+
'Log detailed error for debugging',
|
|
616
|
+
],
|
|
617
|
+
code: {
|
|
618
|
+
filename: 'http-error-handling.ts',
|
|
619
|
+
content: `const response = await fetch(url);\nif (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.message || \`HTTP \${response.status}\`);\n}`,
|
|
620
|
+
language: 'typescript',
|
|
621
|
+
},
|
|
622
|
+
confidence: 0.8,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
diagnosis,
|
|
627
|
+
suggestedFixes: fixes,
|
|
628
|
+
shouldUncheckStep: false,
|
|
629
|
+
shouldInjectNewStep: false,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
async analyzeTestFailure(error, context) {
|
|
633
|
+
const diagnosis = {
|
|
634
|
+
rootCause: 'Test assertion failed',
|
|
635
|
+
explanation: 'A test expectation was not met. Either the test is wrong or the implementation needs fixing.',
|
|
636
|
+
errorCategory: 'test_failure',
|
|
637
|
+
severity: 'medium',
|
|
638
|
+
};
|
|
639
|
+
const fixes = [
|
|
640
|
+
{
|
|
641
|
+
approach: 'Review Test vs Implementation',
|
|
642
|
+
steps: [
|
|
643
|
+
'Read the test to understand expected behavior',
|
|
644
|
+
'Check if implementation matches requirements',
|
|
645
|
+
'Verify test data and mocks are correct',
|
|
646
|
+
],
|
|
647
|
+
confidence: 0.7,
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
approach: 'Debug with Console Logs',
|
|
651
|
+
steps: [
|
|
652
|
+
'Add console.log to see actual values',
|
|
653
|
+
'Compare with expected values',
|
|
654
|
+
'Check for edge cases',
|
|
655
|
+
],
|
|
656
|
+
confidence: 0.75,
|
|
657
|
+
},
|
|
658
|
+
];
|
|
659
|
+
return {
|
|
660
|
+
diagnosis,
|
|
661
|
+
suggestedFixes: fixes,
|
|
662
|
+
shouldUncheckStep: false, // Tests failing doesn't necessarily mean step should be unchecked
|
|
663
|
+
shouldInjectNewStep: false,
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
async analyzeSyntaxError(error, context) {
|
|
667
|
+
// Try to extract line info
|
|
668
|
+
const lineMatch = error.stackTrace?.match(/:(\d+):\d+/);
|
|
669
|
+
const lineNumber = lineMatch?.[1];
|
|
670
|
+
const diagnosis = {
|
|
671
|
+
rootCause: 'Syntax error in code',
|
|
672
|
+
explanation: lineNumber
|
|
673
|
+
? `There's a syntax error around line ${lineNumber}. Check for missing brackets, semicolons, or typos.`
|
|
674
|
+
: 'There\'s a syntax error in the code. Check for missing brackets, semicolons, or typos.',
|
|
675
|
+
errorCategory: 'syntax_error',
|
|
676
|
+
severity: 'high',
|
|
677
|
+
};
|
|
678
|
+
const fixes = [
|
|
679
|
+
{
|
|
680
|
+
approach: 'Check Common Syntax Issues',
|
|
681
|
+
steps: [
|
|
682
|
+
'Look for unmatched brackets: { }, ( ), [ ]',
|
|
683
|
+
'Check for missing commas in objects/arrays',
|
|
684
|
+
'Verify template literal backticks are paired',
|
|
685
|
+
'Check for missing semicolons (if required)',
|
|
686
|
+
],
|
|
687
|
+
confidence: 0.9,
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
approach: 'Use IDE Features',
|
|
691
|
+
steps: [
|
|
692
|
+
'Let your IDE highlight the syntax error',
|
|
693
|
+
'Use "Format Document" to auto-fix some issues',
|
|
694
|
+
'Check the Problems panel for details',
|
|
695
|
+
],
|
|
696
|
+
confidence: 0.85,
|
|
697
|
+
},
|
|
698
|
+
];
|
|
699
|
+
return {
|
|
700
|
+
diagnosis,
|
|
701
|
+
suggestedFixes: fixes,
|
|
702
|
+
shouldUncheckStep: false,
|
|
703
|
+
shouldInjectNewStep: false,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
// ============================================================================
|
|
707
|
+
// GENERIC FALLBACK ANALYZER
|
|
708
|
+
// ============================================================================
|
|
709
|
+
async genericAnalysis(error, context) {
|
|
710
|
+
const diagnosis = {
|
|
711
|
+
rootCause: 'Error encountered during build step',
|
|
712
|
+
explanation: `An error occurred: "${error.errorMessage.substring(0, 200)}". Review the stack trace for more details.`,
|
|
713
|
+
errorCategory: error.errorType,
|
|
714
|
+
severity: 'medium',
|
|
715
|
+
};
|
|
716
|
+
const fixes = [
|
|
717
|
+
{
|
|
718
|
+
approach: 'Debug the Error',
|
|
719
|
+
steps: [
|
|
720
|
+
'Review the full stack trace',
|
|
721
|
+
'Add logging around the failing code',
|
|
722
|
+
'Check recent changes that might have caused this',
|
|
723
|
+
'Search for the error message online',
|
|
724
|
+
],
|
|
725
|
+
confidence: 0.5,
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
approach: 'Isolate the Problem',
|
|
729
|
+
steps: [
|
|
730
|
+
'Comment out recent code changes',
|
|
731
|
+
'Test each piece individually',
|
|
732
|
+
'Narrow down to the specific line causing issues',
|
|
733
|
+
],
|
|
734
|
+
confidence: 0.6,
|
|
735
|
+
},
|
|
736
|
+
];
|
|
737
|
+
return {
|
|
738
|
+
diagnosis,
|
|
739
|
+
suggestedFixes: fixes,
|
|
740
|
+
relatedDocs: {},
|
|
741
|
+
shouldUncheckStep: false,
|
|
742
|
+
shouldInjectNewStep: false,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
// ============================================================================
|
|
746
|
+
// HELPER METHODS
|
|
747
|
+
// ============================================================================
|
|
748
|
+
buildFullResult(error, context, partialResult, errorType) {
|
|
749
|
+
return {
|
|
750
|
+
diagnosis: partialResult.diagnosis || {
|
|
751
|
+
rootCause: 'Unknown error',
|
|
752
|
+
explanation: error.errorMessage,
|
|
753
|
+
errorCategory: errorType,
|
|
754
|
+
severity: 'medium',
|
|
755
|
+
},
|
|
756
|
+
suggestedFixes: partialResult.suggestedFixes || [],
|
|
757
|
+
relatedDocs: partialResult.relatedDocs || {},
|
|
758
|
+
shouldUncheckStep: partialResult.shouldUncheckStep || false,
|
|
759
|
+
shouldInjectNewStep: partialResult.shouldInjectNewStep || false,
|
|
760
|
+
newStep: partialResult.newStep,
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
exports.ErrorAnalyzer = ErrorAnalyzer;
|
|
765
|
+
//# sourceMappingURL=error-analyzer.js.map
|