@dotsetlabs/bellwether 0.10.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/CHANGELOG.md +291 -0
- package/LICENSE +21 -0
- package/README.md +739 -0
- package/dist/auth/credentials.d.ts +64 -0
- package/dist/auth/credentials.js +218 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/keychain.d.ts +64 -0
- package/dist/auth/keychain.js +268 -0
- package/dist/baseline/ab-testing.d.ts +80 -0
- package/dist/baseline/ab-testing.js +236 -0
- package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
- package/dist/baseline/ai-compatibility-scorer.js +606 -0
- package/dist/baseline/calibration.d.ts +77 -0
- package/dist/baseline/calibration.js +136 -0
- package/dist/baseline/category-matching.d.ts +85 -0
- package/dist/baseline/category-matching.js +289 -0
- package/dist/baseline/change-impact-analyzer.d.ts +98 -0
- package/dist/baseline/change-impact-analyzer.js +592 -0
- package/dist/baseline/comparator.d.ts +64 -0
- package/dist/baseline/comparator.js +916 -0
- package/dist/baseline/confidence.d.ts +55 -0
- package/dist/baseline/confidence.js +122 -0
- package/dist/baseline/converter.d.ts +61 -0
- package/dist/baseline/converter.js +585 -0
- package/dist/baseline/dependency-analyzer.d.ts +89 -0
- package/dist/baseline/dependency-analyzer.js +567 -0
- package/dist/baseline/deprecation-tracker.d.ts +133 -0
- package/dist/baseline/deprecation-tracker.js +322 -0
- package/dist/baseline/diff.d.ts +55 -0
- package/dist/baseline/diff.js +1584 -0
- package/dist/baseline/documentation-scorer.d.ts +205 -0
- package/dist/baseline/documentation-scorer.js +466 -0
- package/dist/baseline/embeddings.d.ts +118 -0
- package/dist/baseline/embeddings.js +251 -0
- package/dist/baseline/error-analyzer.d.ts +198 -0
- package/dist/baseline/error-analyzer.js +721 -0
- package/dist/baseline/evaluation/evaluator.d.ts +42 -0
- package/dist/baseline/evaluation/evaluator.js +323 -0
- package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
- package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
- package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
- package/dist/baseline/evaluation/golden-dataset.js +717 -0
- package/dist/baseline/evaluation/index.d.ts +15 -0
- package/dist/baseline/evaluation/index.js +15 -0
- package/dist/baseline/evaluation/types.d.ts +186 -0
- package/dist/baseline/evaluation/types.js +8 -0
- package/dist/baseline/external-dependency-detector.d.ts +181 -0
- package/dist/baseline/external-dependency-detector.js +524 -0
- package/dist/baseline/golden-output.d.ts +162 -0
- package/dist/baseline/golden-output.js +636 -0
- package/dist/baseline/health-scorer.d.ts +174 -0
- package/dist/baseline/health-scorer.js +451 -0
- package/dist/baseline/incremental-checker.d.ts +97 -0
- package/dist/baseline/incremental-checker.js +174 -0
- package/dist/baseline/index.d.ts +31 -0
- package/dist/baseline/index.js +42 -0
- package/dist/baseline/migration-generator.d.ts +137 -0
- package/dist/baseline/migration-generator.js +554 -0
- package/dist/baseline/migrations.d.ts +60 -0
- package/dist/baseline/migrations.js +197 -0
- package/dist/baseline/performance-tracker.d.ts +214 -0
- package/dist/baseline/performance-tracker.js +577 -0
- package/dist/baseline/pr-comment-generator.d.ts +117 -0
- package/dist/baseline/pr-comment-generator.js +546 -0
- package/dist/baseline/response-fingerprint.d.ts +127 -0
- package/dist/baseline/response-fingerprint.js +728 -0
- package/dist/baseline/response-schema-tracker.d.ts +129 -0
- package/dist/baseline/response-schema-tracker.js +420 -0
- package/dist/baseline/risk-scorer.d.ts +54 -0
- package/dist/baseline/risk-scorer.js +434 -0
- package/dist/baseline/saver.d.ts +89 -0
- package/dist/baseline/saver.js +554 -0
- package/dist/baseline/scenario-generator.d.ts +151 -0
- package/dist/baseline/scenario-generator.js +905 -0
- package/dist/baseline/schema-compare.d.ts +86 -0
- package/dist/baseline/schema-compare.js +557 -0
- package/dist/baseline/schema-evolution.d.ts +189 -0
- package/dist/baseline/schema-evolution.js +467 -0
- package/dist/baseline/semantic.d.ts +203 -0
- package/dist/baseline/semantic.js +908 -0
- package/dist/baseline/synonyms.d.ts +60 -0
- package/dist/baseline/synonyms.js +386 -0
- package/dist/baseline/telemetry.d.ts +165 -0
- package/dist/baseline/telemetry.js +294 -0
- package/dist/baseline/test-pruner.d.ts +120 -0
- package/dist/baseline/test-pruner.js +387 -0
- package/dist/baseline/types.d.ts +449 -0
- package/dist/baseline/types.js +5 -0
- package/dist/baseline/version.d.ts +138 -0
- package/dist/baseline/version.js +206 -0
- package/dist/cache/index.d.ts +5 -0
- package/dist/cache/index.js +5 -0
- package/dist/cache/response-cache.d.ts +151 -0
- package/dist/cache/response-cache.js +287 -0
- package/dist/ci/index.d.ts +60 -0
- package/dist/ci/index.js +342 -0
- package/dist/cli/commands/auth.d.ts +12 -0
- package/dist/cli/commands/auth.js +352 -0
- package/dist/cli/commands/badge.d.ts +3 -0
- package/dist/cli/commands/badge.js +74 -0
- package/dist/cli/commands/baseline-accept.d.ts +15 -0
- package/dist/cli/commands/baseline-accept.js +178 -0
- package/dist/cli/commands/baseline-migrate.d.ts +12 -0
- package/dist/cli/commands/baseline-migrate.js +164 -0
- package/dist/cli/commands/baseline.d.ts +14 -0
- package/dist/cli/commands/baseline.js +449 -0
- package/dist/cli/commands/beta.d.ts +10 -0
- package/dist/cli/commands/beta.js +231 -0
- package/dist/cli/commands/check.d.ts +11 -0
- package/dist/cli/commands/check.js +820 -0
- package/dist/cli/commands/cloud/badge.d.ts +3 -0
- package/dist/cli/commands/cloud/badge.js +74 -0
- package/dist/cli/commands/cloud/diff.d.ts +6 -0
- package/dist/cli/commands/cloud/diff.js +79 -0
- package/dist/cli/commands/cloud/history.d.ts +6 -0
- package/dist/cli/commands/cloud/history.js +102 -0
- package/dist/cli/commands/cloud/link.d.ts +9 -0
- package/dist/cli/commands/cloud/link.js +119 -0
- package/dist/cli/commands/cloud/login.d.ts +7 -0
- package/dist/cli/commands/cloud/login.js +499 -0
- package/dist/cli/commands/cloud/projects.d.ts +6 -0
- package/dist/cli/commands/cloud/projects.js +44 -0
- package/dist/cli/commands/cloud/shared.d.ts +7 -0
- package/dist/cli/commands/cloud/shared.js +42 -0
- package/dist/cli/commands/cloud/teams.d.ts +8 -0
- package/dist/cli/commands/cloud/teams.js +169 -0
- package/dist/cli/commands/cloud/upload.d.ts +8 -0
- package/dist/cli/commands/cloud/upload.js +181 -0
- package/dist/cli/commands/contract.d.ts +11 -0
- package/dist/cli/commands/contract.js +280 -0
- package/dist/cli/commands/discover.d.ts +3 -0
- package/dist/cli/commands/discover.js +82 -0
- package/dist/cli/commands/eval.d.ts +9 -0
- package/dist/cli/commands/eval.js +187 -0
- package/dist/cli/commands/explore.d.ts +11 -0
- package/dist/cli/commands/explore.js +437 -0
- package/dist/cli/commands/feedback.d.ts +9 -0
- package/dist/cli/commands/feedback.js +174 -0
- package/dist/cli/commands/golden.d.ts +12 -0
- package/dist/cli/commands/golden.js +407 -0
- package/dist/cli/commands/history.d.ts +10 -0
- package/dist/cli/commands/history.js +202 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +219 -0
- package/dist/cli/commands/interview.d.ts +3 -0
- package/dist/cli/commands/interview.js +903 -0
- package/dist/cli/commands/link.d.ts +10 -0
- package/dist/cli/commands/link.js +169 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +499 -0
- package/dist/cli/commands/preset.d.ts +33 -0
- package/dist/cli/commands/preset.js +297 -0
- package/dist/cli/commands/profile.d.ts +33 -0
- package/dist/cli/commands/profile.js +286 -0
- package/dist/cli/commands/registry.d.ts +11 -0
- package/dist/cli/commands/registry.js +146 -0
- package/dist/cli/commands/shared.d.ts +79 -0
- package/dist/cli/commands/shared.js +196 -0
- package/dist/cli/commands/teams.d.ts +8 -0
- package/dist/cli/commands/teams.js +169 -0
- package/dist/cli/commands/test.d.ts +9 -0
- package/dist/cli/commands/test.js +500 -0
- package/dist/cli/commands/upload.d.ts +8 -0
- package/dist/cli/commands/upload.js +223 -0
- package/dist/cli/commands/validate-config.d.ts +6 -0
- package/dist/cli/commands/validate-config.js +35 -0
- package/dist/cli/commands/verify.d.ts +11 -0
- package/dist/cli/commands/verify.js +283 -0
- package/dist/cli/commands/watch.d.ts +12 -0
- package/dist/cli/commands/watch.js +253 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +178 -0
- package/dist/cli/interactive.d.ts +47 -0
- package/dist/cli/interactive.js +216 -0
- package/dist/cli/output/terminal-reporter.d.ts +19 -0
- package/dist/cli/output/terminal-reporter.js +104 -0
- package/dist/cli/output.d.ts +226 -0
- package/dist/cli/output.js +438 -0
- package/dist/cli/utils/env.d.ts +5 -0
- package/dist/cli/utils/env.js +14 -0
- package/dist/cli/utils/progress.d.ts +59 -0
- package/dist/cli/utils/progress.js +206 -0
- package/dist/cli/utils/server-context.d.ts +10 -0
- package/dist/cli/utils/server-context.js +36 -0
- package/dist/cloud/auth.d.ts +144 -0
- package/dist/cloud/auth.js +374 -0
- package/dist/cloud/client.d.ts +24 -0
- package/dist/cloud/client.js +65 -0
- package/dist/cloud/http-client.d.ts +38 -0
- package/dist/cloud/http-client.js +215 -0
- package/dist/cloud/index.d.ts +23 -0
- package/dist/cloud/index.js +25 -0
- package/dist/cloud/mock-client.d.ts +107 -0
- package/dist/cloud/mock-client.js +545 -0
- package/dist/cloud/types.d.ts +515 -0
- package/dist/cloud/types.js +15 -0
- package/dist/config/defaults.d.ts +160 -0
- package/dist/config/defaults.js +169 -0
- package/dist/config/loader.d.ts +24 -0
- package/dist/config/loader.js +122 -0
- package/dist/config/template.d.ts +42 -0
- package/dist/config/template.js +647 -0
- package/dist/config/validator.d.ts +2112 -0
- package/dist/config/validator.js +658 -0
- package/dist/constants/cloud.d.ts +107 -0
- package/dist/constants/cloud.js +110 -0
- package/dist/constants/core.d.ts +521 -0
- package/dist/constants/core.js +556 -0
- package/dist/constants/testing.d.ts +1283 -0
- package/dist/constants/testing.js +1568 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.js +10 -0
- package/dist/contract/index.d.ts +6 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/validator.d.ts +177 -0
- package/dist/contract/validator.js +574 -0
- package/dist/cost/index.d.ts +6 -0
- package/dist/cost/index.js +5 -0
- package/dist/cost/tracker.d.ts +134 -0
- package/dist/cost/tracker.js +313 -0
- package/dist/discovery/discovery.d.ts +16 -0
- package/dist/discovery/discovery.js +173 -0
- package/dist/discovery/types.d.ts +51 -0
- package/dist/discovery/types.js +2 -0
- package/dist/docs/agents.d.ts +3 -0
- package/dist/docs/agents.js +995 -0
- package/dist/docs/contract.d.ts +51 -0
- package/dist/docs/contract.js +1681 -0
- package/dist/docs/generator.d.ts +4 -0
- package/dist/docs/generator.js +4 -0
- package/dist/docs/html-reporter.d.ts +9 -0
- package/dist/docs/html-reporter.js +757 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +11 -0
- package/dist/docs/junit-reporter.d.ts +18 -0
- package/dist/docs/junit-reporter.js +210 -0
- package/dist/docs/report.d.ts +14 -0
- package/dist/docs/report.js +44 -0
- package/dist/docs/sarif-reporter.d.ts +19 -0
- package/dist/docs/sarif-reporter.js +335 -0
- package/dist/docs/shared.d.ts +35 -0
- package/dist/docs/shared.js +162 -0
- package/dist/docs/templates.d.ts +12 -0
- package/dist/docs/templates.js +76 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +6 -0
- package/dist/errors/retry.d.ts +92 -0
- package/dist/errors/retry.js +323 -0
- package/dist/errors/types.d.ts +321 -0
- package/dist/errors/types.js +584 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +32 -0
- package/dist/interview/dependency-resolver.d.ts +11 -0
- package/dist/interview/dependency-resolver.js +32 -0
- package/dist/interview/interviewer.d.ts +232 -0
- package/dist/interview/interviewer.js +1939 -0
- package/dist/interview/mock-response-generator.d.ts +7 -0
- package/dist/interview/mock-response-generator.js +102 -0
- package/dist/interview/orchestrator.d.ts +237 -0
- package/dist/interview/orchestrator.js +1296 -0
- package/dist/interview/rate-limiter.d.ts +15 -0
- package/dist/interview/rate-limiter.js +55 -0
- package/dist/interview/response-validator.d.ts +10 -0
- package/dist/interview/response-validator.js +132 -0
- package/dist/interview/schema-inferrer.d.ts +8 -0
- package/dist/interview/schema-inferrer.js +71 -0
- package/dist/interview/schema-test-generator.d.ts +71 -0
- package/dist/interview/schema-test-generator.js +834 -0
- package/dist/interview/smart-value-generator.d.ts +155 -0
- package/dist/interview/smart-value-generator.js +554 -0
- package/dist/interview/stateful-test-runner.d.ts +19 -0
- package/dist/interview/stateful-test-runner.js +106 -0
- package/dist/interview/types.d.ts +561 -0
- package/dist/interview/types.js +2 -0
- package/dist/llm/anthropic.d.ts +41 -0
- package/dist/llm/anthropic.js +355 -0
- package/dist/llm/client.d.ts +123 -0
- package/dist/llm/client.js +42 -0
- package/dist/llm/factory.d.ts +38 -0
- package/dist/llm/factory.js +145 -0
- package/dist/llm/fallback.d.ts +140 -0
- package/dist/llm/fallback.js +379 -0
- package/dist/llm/index.d.ts +18 -0
- package/dist/llm/index.js +15 -0
- package/dist/llm/ollama.d.ts +37 -0
- package/dist/llm/ollama.js +330 -0
- package/dist/llm/openai.d.ts +25 -0
- package/dist/llm/openai.js +320 -0
- package/dist/llm/token-budget.d.ts +161 -0
- package/dist/llm/token-budget.js +395 -0
- package/dist/logging/logger.d.ts +70 -0
- package/dist/logging/logger.js +130 -0
- package/dist/metrics/collector.d.ts +106 -0
- package/dist/metrics/collector.js +547 -0
- package/dist/metrics/index.d.ts +7 -0
- package/dist/metrics/index.js +7 -0
- package/dist/metrics/prometheus.d.ts +20 -0
- package/dist/metrics/prometheus.js +241 -0
- package/dist/metrics/types.d.ts +209 -0
- package/dist/metrics/types.js +5 -0
- package/dist/persona/builtins.d.ts +54 -0
- package/dist/persona/builtins.js +219 -0
- package/dist/persona/index.d.ts +8 -0
- package/dist/persona/index.js +8 -0
- package/dist/persona/loader.d.ts +30 -0
- package/dist/persona/loader.js +190 -0
- package/dist/persona/types.d.ts +144 -0
- package/dist/persona/types.js +5 -0
- package/dist/persona/validation.d.ts +94 -0
- package/dist/persona/validation.js +332 -0
- package/dist/prompts/index.d.ts +5 -0
- package/dist/prompts/index.js +5 -0
- package/dist/prompts/templates.d.ts +180 -0
- package/dist/prompts/templates.js +431 -0
- package/dist/registry/client.d.ts +49 -0
- package/dist/registry/client.js +191 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.js +6 -0
- package/dist/registry/types.d.ts +140 -0
- package/dist/registry/types.js +6 -0
- package/dist/scenarios/evaluator.d.ts +43 -0
- package/dist/scenarios/evaluator.js +206 -0
- package/dist/scenarios/index.d.ts +10 -0
- package/dist/scenarios/index.js +9 -0
- package/dist/scenarios/loader.d.ts +20 -0
- package/dist/scenarios/loader.js +285 -0
- package/dist/scenarios/types.d.ts +153 -0
- package/dist/scenarios/types.js +8 -0
- package/dist/security/index.d.ts +17 -0
- package/dist/security/index.js +18 -0
- package/dist/security/payloads.d.ts +61 -0
- package/dist/security/payloads.js +268 -0
- package/dist/security/security-tester.d.ts +42 -0
- package/dist/security/security-tester.js +582 -0
- package/dist/security/types.d.ts +166 -0
- package/dist/security/types.js +8 -0
- package/dist/transport/base-transport.d.ts +59 -0
- package/dist/transport/base-transport.js +38 -0
- package/dist/transport/http-transport.d.ts +67 -0
- package/dist/transport/http-transport.js +238 -0
- package/dist/transport/mcp-client.d.ts +141 -0
- package/dist/transport/mcp-client.js +496 -0
- package/dist/transport/sse-transport.d.ts +88 -0
- package/dist/transport/sse-transport.js +316 -0
- package/dist/transport/stdio-transport.d.ts +43 -0
- package/dist/transport/stdio-transport.js +238 -0
- package/dist/transport/types.d.ts +125 -0
- package/dist/transport/types.js +16 -0
- package/dist/utils/concurrency.d.ts +123 -0
- package/dist/utils/concurrency.js +213 -0
- package/dist/utils/formatters.d.ts +16 -0
- package/dist/utils/formatters.js +37 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jsonpath.d.ts +87 -0
- package/dist/utils/jsonpath.js +326 -0
- package/dist/utils/markdown.d.ts +113 -0
- package/dist/utils/markdown.js +265 -0
- package/dist/utils/network.d.ts +14 -0
- package/dist/utils/network.js +17 -0
- package/dist/utils/sanitize.d.ts +92 -0
- package/dist/utils/sanitize.js +191 -0
- package/dist/utils/semantic.d.ts +194 -0
- package/dist/utils/semantic.js +1051 -0
- package/dist/utils/smart-truncate.d.ts +94 -0
- package/dist/utils/smart-truncate.js +361 -0
- package/dist/utils/timeout.d.ts +153 -0
- package/dist/utils/timeout.js +205 -0
- package/dist/utils/yaml-parser.d.ts +58 -0
- package/dist/utils/yaml-parser.js +86 -0
- package/dist/validation/index.d.ts +32 -0
- package/dist/validation/index.js +32 -0
- package/dist/validation/semantic-test-generator.d.ts +50 -0
- package/dist/validation/semantic-test-generator.js +176 -0
- package/dist/validation/semantic-types.d.ts +66 -0
- package/dist/validation/semantic-types.js +94 -0
- package/dist/validation/semantic-validator.d.ts +38 -0
- package/dist/validation/semantic-validator.js +340 -0
- package/dist/verification/index.d.ts +6 -0
- package/dist/verification/index.js +5 -0
- package/dist/verification/types.d.ts +133 -0
- package/dist/verification/types.js +5 -0
- package/dist/verification/verifier.d.ts +30 -0
- package/dist/verification/verifier.js +309 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +48 -0
- package/dist/workflow/auto-generator.d.ts +27 -0
- package/dist/workflow/auto-generator.js +513 -0
- package/dist/workflow/discovery.d.ts +40 -0
- package/dist/workflow/discovery.js +195 -0
- package/dist/workflow/executor.d.ts +82 -0
- package/dist/workflow/executor.js +611 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/loader.d.ts +24 -0
- package/dist/workflow/loader.js +194 -0
- package/dist/workflow/state-tracker.d.ts +98 -0
- package/dist/workflow/state-tracker.js +424 -0
- package/dist/workflow/types.d.ts +337 -0
- package/dist/workflow/types.js +5 -0
- package/package.json +94 -0
- package/schemas/bellwether-check.schema.json +651 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rich error analysis with remediation suggestions.
|
|
3
|
+
*
|
|
4
|
+
* This module provides enhanced error analysis capabilities:
|
|
5
|
+
* - HTTP status code parsing and categorization
|
|
6
|
+
* - Root cause inference from error messages
|
|
7
|
+
* - Remediation suggestion generation
|
|
8
|
+
* - Related parameter extraction
|
|
9
|
+
* - Transient error detection
|
|
10
|
+
* - Error trend analysis across baselines
|
|
11
|
+
*/
|
|
12
|
+
// ==================== Analysis Functions ====================
|
|
13
|
+
/**
|
|
14
|
+
* Analyze an error message for enhanced information.
|
|
15
|
+
*
|
|
16
|
+
* @param errorMessage - The error message to analyze
|
|
17
|
+
* @param context - Optional context about the test that generated the error
|
|
18
|
+
* @returns Enhanced error analysis with root cause and remediation
|
|
19
|
+
*/
|
|
20
|
+
export function analyzeError(errorMessage, context) {
|
|
21
|
+
const httpStatus = extractHttpStatus(errorMessage);
|
|
22
|
+
let statusCategory = categorizeHttpStatus(httpStatus);
|
|
23
|
+
const wasExpected = context?.wasExpected ?? context?.expectedOutcome === 'error';
|
|
24
|
+
// If the error was expected (validation test), recategorize it
|
|
25
|
+
if (wasExpected && (statusCategory === 'client_error_validation' || statusCategory === 'unknown')) {
|
|
26
|
+
statusCategory = 'validation_expected';
|
|
27
|
+
}
|
|
28
|
+
const rootCause = wasExpected
|
|
29
|
+
? 'Expected validation error from intentional test'
|
|
30
|
+
: inferRootCause(errorMessage, statusCategory);
|
|
31
|
+
const remediation = wasExpected
|
|
32
|
+
? 'No action needed - this error was intentional to verify input validation'
|
|
33
|
+
: generateRemediation(statusCategory, errorMessage);
|
|
34
|
+
const relatedParameters = extractRelatedParameters(errorMessage);
|
|
35
|
+
const transient = isTransientError(statusCategory, errorMessage);
|
|
36
|
+
// Expected errors have 'info' severity - they're not problems
|
|
37
|
+
const severity = wasExpected ? 'info' : assessErrorSeverity(statusCategory, errorMessage);
|
|
38
|
+
return {
|
|
39
|
+
pattern: {
|
|
40
|
+
category: mapStatusToErrorCategory(statusCategory),
|
|
41
|
+
patternHash: '',
|
|
42
|
+
example: errorMessage,
|
|
43
|
+
count: 1,
|
|
44
|
+
},
|
|
45
|
+
httpStatus,
|
|
46
|
+
statusCategory,
|
|
47
|
+
rootCause,
|
|
48
|
+
remediation,
|
|
49
|
+
relatedParameters,
|
|
50
|
+
transient,
|
|
51
|
+
severity,
|
|
52
|
+
wasExpected,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Analyze multiple error patterns and return enhanced analyses.
|
|
57
|
+
*
|
|
58
|
+
* @param patterns - Array of error patterns to analyze
|
|
59
|
+
* @param defaultContext - Default context to apply to all patterns without individual context
|
|
60
|
+
* @returns Array of enhanced error analyses
|
|
61
|
+
*/
|
|
62
|
+
export function analyzeErrorPatterns(patterns, defaultContext) {
|
|
63
|
+
return patterns.map((item) => {
|
|
64
|
+
// Support both plain patterns and patterns with context
|
|
65
|
+
const pattern = 'pattern' in item ? item.pattern : item;
|
|
66
|
+
const context = 'context' in item ? (item.context ?? defaultContext) : defaultContext;
|
|
67
|
+
const analysis = analyzeError(pattern.example, context);
|
|
68
|
+
return {
|
|
69
|
+
...analysis,
|
|
70
|
+
pattern,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate an error analysis summary for a tool.
|
|
76
|
+
*
|
|
77
|
+
* @param toolName - Name of the tool
|
|
78
|
+
* @param patterns - Error patterns for the tool
|
|
79
|
+
* @returns Error analysis summary
|
|
80
|
+
*/
|
|
81
|
+
export function generateErrorSummary(toolName, patterns) {
|
|
82
|
+
const analyses = analyzeErrorPatterns(patterns);
|
|
83
|
+
const totalErrors = patterns.reduce((sum, p) => sum + p.count, 0);
|
|
84
|
+
// Find dominant category and build category counts
|
|
85
|
+
const categoryCounts = new Map();
|
|
86
|
+
for (const analysis of analyses) {
|
|
87
|
+
const count = categoryCounts.get(analysis.statusCategory) ?? 0;
|
|
88
|
+
categoryCounts.set(analysis.statusCategory, count + analysis.pattern.count);
|
|
89
|
+
}
|
|
90
|
+
let dominantCategory = 'unknown';
|
|
91
|
+
let maxCount = 0;
|
|
92
|
+
for (const [category, count] of categoryCounts) {
|
|
93
|
+
if (count > maxCount) {
|
|
94
|
+
maxCount = count;
|
|
95
|
+
dominantCategory = category;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Count transient and actionable errors
|
|
99
|
+
const transientErrors = analyses.filter((a) => a.transient).reduce((sum, a) => sum + a.pattern.count, 0);
|
|
100
|
+
const actionableCount = analyses.filter((a) => a.remediation && !a.remediation.includes('Review')).length;
|
|
101
|
+
// Collect unique remediations with frequency
|
|
102
|
+
const remediationCounts = new Map();
|
|
103
|
+
for (const analysis of analyses) {
|
|
104
|
+
if (analysis.remediation) {
|
|
105
|
+
const count = remediationCounts.get(analysis.remediation) ?? 0;
|
|
106
|
+
remediationCounts.set(analysis.remediation, count + analysis.pattern.count);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Get top remediations sorted by frequency
|
|
110
|
+
const topRemediations = Array.from(remediationCounts.entries())
|
|
111
|
+
.sort((a, b) => b[1] - a[1])
|
|
112
|
+
.slice(0, 5)
|
|
113
|
+
.map(([remediation]) => remediation);
|
|
114
|
+
// Collect root causes with frequency
|
|
115
|
+
const rootCauseCounts = new Map();
|
|
116
|
+
for (const analysis of analyses) {
|
|
117
|
+
if (analysis.rootCause) {
|
|
118
|
+
const count = rootCauseCounts.get(analysis.rootCause) ?? 0;
|
|
119
|
+
rootCauseCounts.set(analysis.rootCause, count + analysis.pattern.count);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// Get top root causes sorted by frequency
|
|
123
|
+
const topRootCauses = Array.from(rootCauseCounts.entries())
|
|
124
|
+
.sort((a, b) => b[1] - a[1])
|
|
125
|
+
.slice(0, 5)
|
|
126
|
+
.map(([cause]) => cause);
|
|
127
|
+
// Collect all related parameters
|
|
128
|
+
const relatedParamsSet = new Set();
|
|
129
|
+
for (const analysis of analyses) {
|
|
130
|
+
for (const param of analysis.relatedParameters) {
|
|
131
|
+
relatedParamsSet.add(param);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
tool: toolName,
|
|
136
|
+
totalErrors,
|
|
137
|
+
analyses,
|
|
138
|
+
dominantCategory,
|
|
139
|
+
transientErrors,
|
|
140
|
+
actionableCount,
|
|
141
|
+
remediations: Array.from(remediationCounts.keys()),
|
|
142
|
+
categoryCounts,
|
|
143
|
+
topRootCauses,
|
|
144
|
+
topRemediations,
|
|
145
|
+
relatedParameters: Array.from(relatedParamsSet).slice(0, 10),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Compare error patterns and identify trends.
|
|
150
|
+
*
|
|
151
|
+
* @param previous - Error patterns from previous baseline
|
|
152
|
+
* @param current - Error patterns from current baseline
|
|
153
|
+
* @returns Error trend report
|
|
154
|
+
*/
|
|
155
|
+
export function analyzeErrorTrends(previous, current) {
|
|
156
|
+
const trends = [];
|
|
157
|
+
const prevByCategory = new Map();
|
|
158
|
+
const currByCategory = new Map();
|
|
159
|
+
for (const p of previous) {
|
|
160
|
+
prevByCategory.set(p.category, (prevByCategory.get(p.category) ?? 0) + p.count);
|
|
161
|
+
}
|
|
162
|
+
for (const c of current) {
|
|
163
|
+
currByCategory.set(c.category, (currByCategory.get(c.category) ?? 0) + c.count);
|
|
164
|
+
}
|
|
165
|
+
const allCategories = new Set([...prevByCategory.keys(), ...currByCategory.keys()]);
|
|
166
|
+
const increasingCategories = [];
|
|
167
|
+
const decreasingCategories = [];
|
|
168
|
+
const newCategories = [];
|
|
169
|
+
const resolvedCategories = [];
|
|
170
|
+
for (const category of allCategories) {
|
|
171
|
+
const prevCount = prevByCategory.get(category) ?? 0;
|
|
172
|
+
const currCount = currByCategory.get(category) ?? 0;
|
|
173
|
+
let trend;
|
|
174
|
+
let significance;
|
|
175
|
+
let changePercent = 0;
|
|
176
|
+
if (prevCount === 0 && currCount > 0) {
|
|
177
|
+
trend = 'new';
|
|
178
|
+
significance = 'high';
|
|
179
|
+
changePercent = 100;
|
|
180
|
+
newCategories.push(category);
|
|
181
|
+
}
|
|
182
|
+
else if (currCount === 0 && prevCount > 0) {
|
|
183
|
+
trend = 'resolved';
|
|
184
|
+
significance = 'medium';
|
|
185
|
+
changePercent = -100;
|
|
186
|
+
resolvedCategories.push(category);
|
|
187
|
+
}
|
|
188
|
+
else if (prevCount > 0) {
|
|
189
|
+
changePercent = ((currCount - prevCount) / prevCount) * 100;
|
|
190
|
+
if (currCount > prevCount * 1.5) {
|
|
191
|
+
trend = 'increasing';
|
|
192
|
+
significance = 'high';
|
|
193
|
+
increasingCategories.push(category);
|
|
194
|
+
}
|
|
195
|
+
else if (currCount < prevCount * 0.5) {
|
|
196
|
+
trend = 'decreasing';
|
|
197
|
+
significance = 'low';
|
|
198
|
+
decreasingCategories.push(category);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
trend = 'stable';
|
|
202
|
+
significance = 'low';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
trend = 'stable';
|
|
207
|
+
significance = 'low';
|
|
208
|
+
}
|
|
209
|
+
trends.push({
|
|
210
|
+
category,
|
|
211
|
+
previousCount: prevCount,
|
|
212
|
+
currentCount: currCount,
|
|
213
|
+
trend,
|
|
214
|
+
significance,
|
|
215
|
+
changePercent: Math.round(changePercent),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// Filter out stable trends with zero counts
|
|
219
|
+
const significantTrends = trends.filter((t) => t.trend !== 'stable' || t.currentCount > 0);
|
|
220
|
+
// Determine if there's a significant change
|
|
221
|
+
const significantChange = newCategories.length > 0 ||
|
|
222
|
+
increasingCategories.length > 0 ||
|
|
223
|
+
trends.some((t) => t.significance === 'high');
|
|
224
|
+
// Generate summary
|
|
225
|
+
const summary = generateTrendSummary(newCategories, resolvedCategories, increasingCategories, decreasingCategories);
|
|
226
|
+
return {
|
|
227
|
+
trends: significantTrends,
|
|
228
|
+
significantChange,
|
|
229
|
+
summary,
|
|
230
|
+
increasingCategories,
|
|
231
|
+
decreasingCategories,
|
|
232
|
+
newCategories,
|
|
233
|
+
resolvedCategories,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// ==================== HTTP Status Parsing ====================
|
|
237
|
+
/**
|
|
238
|
+
* Extract HTTP status code from error message.
|
|
239
|
+
*
|
|
240
|
+
* @param message - Error message to parse
|
|
241
|
+
* @returns HTTP status code if found, undefined otherwise
|
|
242
|
+
*/
|
|
243
|
+
export function extractHttpStatus(message) {
|
|
244
|
+
// Match patterns like "status code 400", "HTTP 404", "Error 500", "(404)", "[500]"
|
|
245
|
+
const patterns = [
|
|
246
|
+
/status\s*(?:code)?\s*[:\s]?\s*(\d{3})/i,
|
|
247
|
+
/HTTP\s*(?:\/\d\.\d)?\s*(\d{3})/i,
|
|
248
|
+
/Error\s*(\d{3})/i,
|
|
249
|
+
/\[(\d{3})\]/,
|
|
250
|
+
/\((\d{3})\)/,
|
|
251
|
+
/\b([45]\d{2})\b(?:\s+(?:error|bad|not|forbidden|unauthorized|internal))/i,
|
|
252
|
+
];
|
|
253
|
+
for (const pattern of patterns) {
|
|
254
|
+
const match = message.match(pattern);
|
|
255
|
+
if (match) {
|
|
256
|
+
const status = parseInt(match[1], 10);
|
|
257
|
+
// Validate it's a reasonable HTTP status
|
|
258
|
+
if (status >= 100 && status < 600) {
|
|
259
|
+
return status;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Categorize HTTP status code into a category.
|
|
267
|
+
*
|
|
268
|
+
* @param status - HTTP status code
|
|
269
|
+
* @returns HTTP status category
|
|
270
|
+
*/
|
|
271
|
+
export function categorizeHttpStatus(status) {
|
|
272
|
+
if (!status)
|
|
273
|
+
return 'unknown';
|
|
274
|
+
if (status === 400)
|
|
275
|
+
return 'client_error_validation';
|
|
276
|
+
if (status === 401 || status === 403)
|
|
277
|
+
return 'client_error_auth';
|
|
278
|
+
if (status === 404)
|
|
279
|
+
return 'client_error_not_found';
|
|
280
|
+
if (status === 409)
|
|
281
|
+
return 'client_error_conflict';
|
|
282
|
+
if (status === 429)
|
|
283
|
+
return 'client_error_rate_limit';
|
|
284
|
+
if (status >= 500)
|
|
285
|
+
return 'server_error';
|
|
286
|
+
if (status >= 400)
|
|
287
|
+
return 'client_error_validation';
|
|
288
|
+
return 'unknown';
|
|
289
|
+
}
|
|
290
|
+
// ==================== Root Cause Analysis ====================
|
|
291
|
+
/**
|
|
292
|
+
* Infer root cause from error message and category.
|
|
293
|
+
*
|
|
294
|
+
* @param message - Error message
|
|
295
|
+
* @param category - HTTP status category
|
|
296
|
+
* @returns Root cause description
|
|
297
|
+
*/
|
|
298
|
+
export function inferRootCause(message, category) {
|
|
299
|
+
const lower = message.toLowerCase();
|
|
300
|
+
// Check for specific patterns first (most specific)
|
|
301
|
+
if (lower.includes('required') && lower.includes('missing')) {
|
|
302
|
+
return 'Missing required parameter or field';
|
|
303
|
+
}
|
|
304
|
+
if (lower.includes('required')) {
|
|
305
|
+
const paramMatch = message.match(/['"`](\w+)['"`].*(?:is )?required/i);
|
|
306
|
+
if (paramMatch) {
|
|
307
|
+
return `Required parameter "${paramMatch[1]}" is missing`;
|
|
308
|
+
}
|
|
309
|
+
return 'Missing required parameter or field';
|
|
310
|
+
}
|
|
311
|
+
if (lower.includes('missing')) {
|
|
312
|
+
const fieldMatch = message.match(/missing\s+(?:required\s+)?(?:field|parameter|property)?\s*['"`]?(\w+)['"`]?/i);
|
|
313
|
+
if (fieldMatch) {
|
|
314
|
+
return `Missing required field "${fieldMatch[1]}"`;
|
|
315
|
+
}
|
|
316
|
+
return 'Missing required parameter or field';
|
|
317
|
+
}
|
|
318
|
+
if (lower.includes('invalid') && lower.includes('format')) {
|
|
319
|
+
return 'Invalid input format - value does not match expected format';
|
|
320
|
+
}
|
|
321
|
+
if (lower.includes('invalid') && lower.includes('type')) {
|
|
322
|
+
return 'Invalid input type - value type does not match expected type';
|
|
323
|
+
}
|
|
324
|
+
if (lower.includes('invalid') || lower.includes('malformed')) {
|
|
325
|
+
return 'Invalid input format or value';
|
|
326
|
+
}
|
|
327
|
+
if (lower.includes('not found') || lower.includes('does not exist') || lower.includes("doesn't exist")) {
|
|
328
|
+
return 'Referenced resource does not exist';
|
|
329
|
+
}
|
|
330
|
+
if (lower.includes('already exists') || lower.includes('duplicate')) {
|
|
331
|
+
return 'Resource already exists - duplicate creation attempted';
|
|
332
|
+
}
|
|
333
|
+
if (lower.includes('unauthorized') || lower.includes('authentication')) {
|
|
334
|
+
return 'Authentication credentials missing or invalid';
|
|
335
|
+
}
|
|
336
|
+
if (lower.includes('forbidden') || lower.includes('permission') || lower.includes('access denied')) {
|
|
337
|
+
return 'Insufficient permissions for this operation';
|
|
338
|
+
}
|
|
339
|
+
if (lower.includes('rate') || lower.includes('throttl') || lower.includes('too many')) {
|
|
340
|
+
return 'Request rate limit exceeded';
|
|
341
|
+
}
|
|
342
|
+
if (lower.includes('timeout') || lower.includes('timed out')) {
|
|
343
|
+
return 'Operation timed out';
|
|
344
|
+
}
|
|
345
|
+
if (lower.includes('conflict') || lower.includes('concurrent')) {
|
|
346
|
+
return 'Resource state conflict (concurrent modification)';
|
|
347
|
+
}
|
|
348
|
+
if (lower.includes('connection') && (lower.includes('refused') || lower.includes('failed'))) {
|
|
349
|
+
return 'Connection to external service failed';
|
|
350
|
+
}
|
|
351
|
+
if (lower.includes('network') || lower.includes('dns')) {
|
|
352
|
+
return 'Network connectivity issue';
|
|
353
|
+
}
|
|
354
|
+
if (lower.includes('internal') && lower.includes('error')) {
|
|
355
|
+
return 'Internal server error occurred';
|
|
356
|
+
}
|
|
357
|
+
if (lower.includes('unavailable') || lower.includes('maintenance')) {
|
|
358
|
+
return 'Service temporarily unavailable';
|
|
359
|
+
}
|
|
360
|
+
// Fall back to category-based inference
|
|
361
|
+
switch (category) {
|
|
362
|
+
case 'client_error_validation':
|
|
363
|
+
return 'Client request validation failed';
|
|
364
|
+
case 'client_error_auth':
|
|
365
|
+
return 'Authentication or authorization failure';
|
|
366
|
+
case 'client_error_not_found':
|
|
367
|
+
return 'Requested resource not found';
|
|
368
|
+
case 'client_error_conflict':
|
|
369
|
+
return 'Resource conflict detected';
|
|
370
|
+
case 'client_error_rate_limit':
|
|
371
|
+
return 'Rate limit exceeded';
|
|
372
|
+
case 'server_error':
|
|
373
|
+
return 'Server-side error occurred';
|
|
374
|
+
default:
|
|
375
|
+
return 'Unknown error cause';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// ==================== Remediation Generation ====================
|
|
379
|
+
/**
|
|
380
|
+
* Generate remediation suggestion based on category and message.
|
|
381
|
+
*
|
|
382
|
+
* @param category - HTTP status category
|
|
383
|
+
* @param message - Error message
|
|
384
|
+
* @returns Remediation suggestion
|
|
385
|
+
*/
|
|
386
|
+
export function generateRemediation(category, message) {
|
|
387
|
+
const lower = message.toLowerCase();
|
|
388
|
+
// Specific remediations based on message content
|
|
389
|
+
if (lower.includes('required')) {
|
|
390
|
+
const paramMatch = message.match(/['"`](\w+)['"`]/);
|
|
391
|
+
if (paramMatch) {
|
|
392
|
+
return `Ensure the "${paramMatch[1]}" parameter is provided with a valid value`;
|
|
393
|
+
}
|
|
394
|
+
return 'Ensure all required parameters are provided';
|
|
395
|
+
}
|
|
396
|
+
if (lower.includes('invalid') && lower.includes('format')) {
|
|
397
|
+
return 'Verify input format matches the expected pattern (check documentation for format requirements)';
|
|
398
|
+
}
|
|
399
|
+
if (lower.includes('invalid') && lower.includes('type')) {
|
|
400
|
+
return 'Check that parameter types match the schema (string, number, boolean, etc.)';
|
|
401
|
+
}
|
|
402
|
+
if (lower.includes('not found')) {
|
|
403
|
+
const resourceMatch = message.match(/['"`]?(\w+)['"`]?\s+(?:not found|does not exist)/i);
|
|
404
|
+
if (resourceMatch) {
|
|
405
|
+
return `Verify the ${resourceMatch[1]} exists before accessing it`;
|
|
406
|
+
}
|
|
407
|
+
return 'Verify the resource exists before accessing';
|
|
408
|
+
}
|
|
409
|
+
if (lower.includes('already exists')) {
|
|
410
|
+
return 'Check if resource exists before creation, or use upsert if available';
|
|
411
|
+
}
|
|
412
|
+
if (lower.includes('timeout')) {
|
|
413
|
+
return 'Consider increasing timeout or breaking operation into smaller chunks';
|
|
414
|
+
}
|
|
415
|
+
if (lower.includes('rate') || lower.includes('throttl')) {
|
|
416
|
+
return 'Implement exponential backoff and respect rate limits (Retry-After header)';
|
|
417
|
+
}
|
|
418
|
+
// Category-based remediations
|
|
419
|
+
switch (category) {
|
|
420
|
+
case 'client_error_validation':
|
|
421
|
+
return 'Check input parameters against the schema requirements';
|
|
422
|
+
case 'client_error_auth':
|
|
423
|
+
return 'Verify authentication credentials and ensure proper permissions are granted';
|
|
424
|
+
case 'client_error_not_found':
|
|
425
|
+
return 'Verify the resource exists before accessing (check IDs and paths)';
|
|
426
|
+
case 'client_error_conflict':
|
|
427
|
+
return 'Implement optimistic locking or retry with conflict resolution';
|
|
428
|
+
case 'client_error_rate_limit':
|
|
429
|
+
return 'Implement exponential backoff and rate limiting';
|
|
430
|
+
case 'server_error':
|
|
431
|
+
return 'Retry with exponential backoff; contact server administrator if persistent';
|
|
432
|
+
default:
|
|
433
|
+
return 'Review error message and API documentation';
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// ==================== Parameter Extraction ====================
|
|
437
|
+
/**
|
|
438
|
+
* Extract parameter names mentioned in error message.
|
|
439
|
+
*
|
|
440
|
+
* @param message - Error message
|
|
441
|
+
* @returns Array of parameter names
|
|
442
|
+
*/
|
|
443
|
+
export function extractRelatedParameters(message) {
|
|
444
|
+
const params = [];
|
|
445
|
+
const seen = new Set();
|
|
446
|
+
// Match quoted strings that look like parameter names
|
|
447
|
+
const quotedMatches = message.matchAll(/['"`](\w+)['"`]/g);
|
|
448
|
+
for (const match of quotedMatches) {
|
|
449
|
+
const param = match[1];
|
|
450
|
+
if (param.length > 1 && param.length < 30 && !seen.has(param)) {
|
|
451
|
+
// Filter out common non-parameter words
|
|
452
|
+
if (!isCommonWord(param)) {
|
|
453
|
+
params.push(param);
|
|
454
|
+
seen.add(param);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Match "parameter X" or "field X" patterns
|
|
459
|
+
const paramMatches = message.matchAll(/(?:parameter|field|property|argument|key)\s+['"`]?(\w+)['"`]?/gi);
|
|
460
|
+
for (const match of paramMatches) {
|
|
461
|
+
const param = match[1];
|
|
462
|
+
if (!seen.has(param) && !isCommonWord(param)) {
|
|
463
|
+
params.push(param);
|
|
464
|
+
seen.add(param);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// Match "X is required" or "missing X" patterns
|
|
468
|
+
const requiredMatches = message.matchAll(/(\w+)\s+(?:is\s+)?(?:required|missing|invalid)/gi);
|
|
469
|
+
for (const match of requiredMatches) {
|
|
470
|
+
const param = match[1].toLowerCase();
|
|
471
|
+
if (param.length > 2 && !seen.has(param) && !isCommonWord(param)) {
|
|
472
|
+
params.push(param);
|
|
473
|
+
seen.add(param);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return params;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Check if a word is a common English word (not a parameter).
|
|
480
|
+
*/
|
|
481
|
+
function isCommonWord(word) {
|
|
482
|
+
const commonWords = new Set([
|
|
483
|
+
'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
484
|
+
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
|
|
485
|
+
'could', 'should', 'may', 'might', 'must', 'shall', 'can',
|
|
486
|
+
'need', 'not', 'and', 'but', 'or', 'if', 'then', 'else',
|
|
487
|
+
'for', 'with', 'from', 'this', 'that', 'these', 'those',
|
|
488
|
+
'error', 'message', 'failed', 'invalid', 'missing', 'required',
|
|
489
|
+
'found', 'exist', 'exists', 'value', 'input', 'output', 'type',
|
|
490
|
+
'string', 'number', 'boolean', 'object', 'array', 'null', 'undefined',
|
|
491
|
+
'field', 'parameter', 'property', 'argument', 'key',
|
|
492
|
+
]);
|
|
493
|
+
return commonWords.has(word.toLowerCase());
|
|
494
|
+
}
|
|
495
|
+
// ==================== Transient Error Detection ====================
|
|
496
|
+
/**
|
|
497
|
+
* Determine if error is likely transient (temporary).
|
|
498
|
+
*
|
|
499
|
+
* @param category - HTTP status category
|
|
500
|
+
* @param message - Error message
|
|
501
|
+
* @returns Whether the error is likely transient
|
|
502
|
+
*/
|
|
503
|
+
export function isTransientError(category, message) {
|
|
504
|
+
const lower = message.toLowerCase();
|
|
505
|
+
// Rate limiting is always transient
|
|
506
|
+
if (category === 'client_error_rate_limit')
|
|
507
|
+
return true;
|
|
508
|
+
// Server errors are usually transient
|
|
509
|
+
if (category === 'server_error')
|
|
510
|
+
return true;
|
|
511
|
+
// Check for transient keywords
|
|
512
|
+
const transientKeywords = [
|
|
513
|
+
'timeout', 'timed out', 'temporarily', 'retry', 'unavailable',
|
|
514
|
+
'connection', 'network', 'service unavailable', 'too many requests',
|
|
515
|
+
'try again', 'overloaded', 'busy', 'maintenance',
|
|
516
|
+
];
|
|
517
|
+
return transientKeywords.some((keyword) => lower.includes(keyword));
|
|
518
|
+
}
|
|
519
|
+
// ==================== Severity Assessment ====================
|
|
520
|
+
/**
|
|
521
|
+
* Assess error severity based on category and message.
|
|
522
|
+
*
|
|
523
|
+
* @param category - HTTP status category
|
|
524
|
+
* @param message - Error message
|
|
525
|
+
* @returns Error severity level
|
|
526
|
+
*/
|
|
527
|
+
export function assessErrorSeverity(category, message) {
|
|
528
|
+
const lower = message.toLowerCase();
|
|
529
|
+
// Critical severity indicators
|
|
530
|
+
if (lower.includes('fatal') || lower.includes('crash') || lower.includes('corrupt')) {
|
|
531
|
+
return 'critical';
|
|
532
|
+
}
|
|
533
|
+
// High severity
|
|
534
|
+
if (category === 'server_error') {
|
|
535
|
+
return 'high';
|
|
536
|
+
}
|
|
537
|
+
if (category === 'client_error_auth') {
|
|
538
|
+
return 'high';
|
|
539
|
+
}
|
|
540
|
+
// Medium severity
|
|
541
|
+
if (category === 'client_error_validation') {
|
|
542
|
+
return 'medium';
|
|
543
|
+
}
|
|
544
|
+
if (category === 'client_error_conflict') {
|
|
545
|
+
return 'medium';
|
|
546
|
+
}
|
|
547
|
+
// Low severity
|
|
548
|
+
if (category === 'client_error_not_found') {
|
|
549
|
+
return 'low';
|
|
550
|
+
}
|
|
551
|
+
if (category === 'client_error_rate_limit') {
|
|
552
|
+
return 'low';
|
|
553
|
+
}
|
|
554
|
+
// Default
|
|
555
|
+
return 'info';
|
|
556
|
+
}
|
|
557
|
+
// ==================== Helper Functions ====================
|
|
558
|
+
/**
|
|
559
|
+
* Map HTTP status category to ErrorPattern category.
|
|
560
|
+
*/
|
|
561
|
+
export function mapStatusToErrorCategory(category) {
|
|
562
|
+
switch (category) {
|
|
563
|
+
case 'client_error_validation':
|
|
564
|
+
case 'client_error_conflict':
|
|
565
|
+
case 'client_error_rate_limit':
|
|
566
|
+
return 'validation';
|
|
567
|
+
case 'client_error_not_found':
|
|
568
|
+
return 'not_found';
|
|
569
|
+
case 'client_error_auth':
|
|
570
|
+
return 'permission';
|
|
571
|
+
case 'server_error':
|
|
572
|
+
return 'internal';
|
|
573
|
+
default:
|
|
574
|
+
return 'unknown';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Generate a summary of error trends.
|
|
579
|
+
*/
|
|
580
|
+
function generateTrendSummary(newCategories, resolvedCategories, increasingCategories, decreasingCategories) {
|
|
581
|
+
const parts = [];
|
|
582
|
+
if (newCategories.length > 0) {
|
|
583
|
+
parts.push(`${newCategories.length} new error type(s): ${newCategories.join(', ')}`);
|
|
584
|
+
}
|
|
585
|
+
if (resolvedCategories.length > 0) {
|
|
586
|
+
parts.push(`${resolvedCategories.length} resolved: ${resolvedCategories.join(', ')}`);
|
|
587
|
+
}
|
|
588
|
+
if (increasingCategories.length > 0) {
|
|
589
|
+
parts.push(`${increasingCategories.length} increasing: ${increasingCategories.join(', ')}`);
|
|
590
|
+
}
|
|
591
|
+
if (decreasingCategories.length > 0) {
|
|
592
|
+
parts.push(`${decreasingCategories.length} decreasing: ${decreasingCategories.join(', ')}`);
|
|
593
|
+
}
|
|
594
|
+
if (parts.length === 0) {
|
|
595
|
+
return 'Error patterns stable';
|
|
596
|
+
}
|
|
597
|
+
return parts.join('; ');
|
|
598
|
+
}
|
|
599
|
+
// ==================== Formatting Functions ====================
|
|
600
|
+
/**
|
|
601
|
+
* Format enhanced error analysis for display.
|
|
602
|
+
*
|
|
603
|
+
* @param analysis - Enhanced error analysis
|
|
604
|
+
* @param useColors - Whether to use ANSI colors
|
|
605
|
+
* @returns Formatted string
|
|
606
|
+
*/
|
|
607
|
+
export function formatEnhancedError(analysis, useColors = false) {
|
|
608
|
+
const lines = [];
|
|
609
|
+
const { yellow, cyan, dim } = useColors ? getColors() : getNoColors();
|
|
610
|
+
// Category and status
|
|
611
|
+
const statusText = analysis.httpStatus ? `HTTP ${analysis.httpStatus}` : 'Unknown status';
|
|
612
|
+
lines.push(`${cyan(formatCategoryName(analysis.statusCategory))} (${statusText})`);
|
|
613
|
+
// Root cause
|
|
614
|
+
lines.push(` ${dim('Cause:')} ${analysis.rootCause}`);
|
|
615
|
+
// Remediation
|
|
616
|
+
lines.push(` ${dim('Fix:')} ${analysis.remediation}`);
|
|
617
|
+
// Related parameters
|
|
618
|
+
if (analysis.relatedParameters.length > 0) {
|
|
619
|
+
lines.push(` ${dim('Parameters:')} ${analysis.relatedParameters.join(', ')}`);
|
|
620
|
+
}
|
|
621
|
+
// Transient indicator
|
|
622
|
+
if (analysis.transient) {
|
|
623
|
+
lines.push(` ${yellow('Transient - may resolve with retry')}`);
|
|
624
|
+
}
|
|
625
|
+
return lines.join('\n');
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Format error trend report for display.
|
|
629
|
+
*
|
|
630
|
+
* @param report - Error trend report
|
|
631
|
+
* @param useColors - Whether to use ANSI colors
|
|
632
|
+
* @returns Formatted string
|
|
633
|
+
*/
|
|
634
|
+
export function formatErrorTrendReport(report, useColors = false) {
|
|
635
|
+
const lines = [];
|
|
636
|
+
const { red, green, yellow, cyan, dim } = useColors ? getColors() : getNoColors();
|
|
637
|
+
lines.push(cyan('Error Trend Analysis'));
|
|
638
|
+
lines.push('');
|
|
639
|
+
if (!report.significantChange) {
|
|
640
|
+
lines.push(green(' No significant changes in error patterns'));
|
|
641
|
+
return lines.join('\n');
|
|
642
|
+
}
|
|
643
|
+
// New errors (high priority)
|
|
644
|
+
if (report.newCategories.length > 0) {
|
|
645
|
+
lines.push(red(` New error types: ${report.newCategories.join(', ')}`));
|
|
646
|
+
}
|
|
647
|
+
// Resolved errors
|
|
648
|
+
if (report.resolvedCategories.length > 0) {
|
|
649
|
+
lines.push(green(` Resolved: ${report.resolvedCategories.join(', ')}`));
|
|
650
|
+
}
|
|
651
|
+
// Increasing errors
|
|
652
|
+
if (report.increasingCategories.length > 0) {
|
|
653
|
+
lines.push(yellow(` Increasing: ${report.increasingCategories.join(', ')}`));
|
|
654
|
+
}
|
|
655
|
+
// Decreasing errors
|
|
656
|
+
if (report.decreasingCategories.length > 0) {
|
|
657
|
+
lines.push(dim(` Decreasing: ${report.decreasingCategories.join(', ')}`));
|
|
658
|
+
}
|
|
659
|
+
// Trend details
|
|
660
|
+
lines.push('');
|
|
661
|
+
lines.push(' Trend details:');
|
|
662
|
+
for (const trend of report.trends.filter((t) => t.trend !== 'stable')) {
|
|
663
|
+
const arrow = getTrendArrow(trend.trend);
|
|
664
|
+
const changeText = trend.changePercent !== 0 ? ` (${trend.changePercent > 0 ? '+' : ''}${trend.changePercent}%)` : '';
|
|
665
|
+
lines.push(` ${arrow} ${trend.category}: ${trend.previousCount} → ${trend.currentCount}${changeText}`);
|
|
666
|
+
}
|
|
667
|
+
return lines.join('\n');
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Format category name for display.
|
|
671
|
+
*/
|
|
672
|
+
export function formatCategoryName(category) {
|
|
673
|
+
const names = {
|
|
674
|
+
client_error_validation: 'Validation Error',
|
|
675
|
+
client_error_auth: 'Authentication Error',
|
|
676
|
+
client_error_not_found: 'Not Found',
|
|
677
|
+
client_error_conflict: 'Conflict',
|
|
678
|
+
client_error_rate_limit: 'Rate Limited',
|
|
679
|
+
server_error: 'Server Error',
|
|
680
|
+
validation_expected: 'Expected Validation (Test)',
|
|
681
|
+
unknown: 'Unknown Error',
|
|
682
|
+
};
|
|
683
|
+
return names[category] ?? category;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Get trend arrow for display.
|
|
687
|
+
*/
|
|
688
|
+
function getTrendArrow(trend) {
|
|
689
|
+
switch (trend) {
|
|
690
|
+
case 'increasing':
|
|
691
|
+
return '↑';
|
|
692
|
+
case 'decreasing':
|
|
693
|
+
return '↓';
|
|
694
|
+
case 'new':
|
|
695
|
+
return '+';
|
|
696
|
+
case 'resolved':
|
|
697
|
+
return '✓';
|
|
698
|
+
case 'stable':
|
|
699
|
+
return '→';
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function getColors() {
|
|
703
|
+
return {
|
|
704
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
705
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
706
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
707
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
708
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
function getNoColors() {
|
|
712
|
+
const identity = (s) => s;
|
|
713
|
+
return {
|
|
714
|
+
red: identity,
|
|
715
|
+
green: identity,
|
|
716
|
+
yellow: identity,
|
|
717
|
+
cyan: identity,
|
|
718
|
+
dim: identity,
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
//# sourceMappingURL=error-analyzer.js.map
|