nx-troubleshooting
v2.0.1
Published
Generic troubleshooting engine for matching errors to solutions
Maintainers
Readme
nx-troubleshooting
Generic troubleshooting engine for matching errors to solutions
nx-troubleshooting is a generic, reusable npm package that provides intelligent error-to-solution matching for any Node.js/TypeScript project. It loads troubleshooting narratives from JSON files, matches errors to relevant solutions based on configurable symptoms, and formats helpful guidance for developers.
Features
- 🎯 Intelligent Error Matching - Matches errors to solutions using multiple strategies (error name, keywords, symptom evaluation)
- 🔍 Flexible Probe System - Built-in probes plus extensible custom probes for project-specific checks
- 📝 Template Variables - Dynamic solution messages with
{{variable}}syntax and nested access - 📊 Multiple Output Formats - Markdown, JSON, and plain text formatting
- 🛠️ Generic Probe Toolbox - Additional probes for common scenarios (file system, environment, config types)
- 🔌 Framework Agnostic - Works with any Node.js/TypeScript project
- 📦 Zero Dependencies - Only uses Node.js built-ins (plus optional
nx-config2for enhanced features)
Installation
npm install nx-troubleshootingQuick Start
import { TroubleshootingEngine } from 'nx-troubleshooting';
// Initialize engine
const engine = new TroubleshootingEngine({
narrativesPath: './metadata/troubleshooting.json'
});
// Load narratives
await engine.loadNarratives();
// Match an error
const error = new Error('Missing connections configuration');
const matches = await engine.matchError(error, {
error,
config: { /* your config */ },
query: { /* your query */ }
});
// Format results
const markdown = engine.formatMatches(matches, 'markdown');
console.log(markdown);Core Concepts
Troubleshooting Narrative
A troubleshooting narrative is a structured description of a problem and its solution:
{
id: "missing-connections-config",
title: "Missing Connections Configuration",
description: "The application config is missing the required 'connections' object...",
symptoms: [
{
probe: "config-check",
params: { field: "connections" },
condition: "result.exists == false"
}
],
solution: [
{
type: "code",
message: "Add a 'connections' object to your config:",
code: "{\n \"connections\": { ... }\n}"
}
]
}Symptoms
Symptoms define how to detect a problem using probes:
- Probe: A function that checks a condition (e.g.,
config-check,file-exists) - Params: Parameters passed to the probe
- Condition: JavaScript expression evaluated against the probe result
Solutions
Solutions provide guidance on how to resolve the issue:
- Type: Solution type (e.g.,
collaborate,link,code) - Message: Solution message (supports template variables)
- URL: Optional URL for
linktype solutions - Code: Optional code example for
codetype solutions
API Reference
TroubleshootingEngine
Main class that handles loading narratives and matching errors.
class TroubleshootingEngine {
constructor(options?: {
narrativesPath?: string; // Path to JSON file or directory
customProbes?: ProbeRegistry; // Custom probe functions
templateVars?: Record<string, any>; // Default template variables
envPrefix?: string; // Environment variable prefix
});
// Load narratives from JSON file(s)
loadNarratives(path?: string): Promise<void>;
// Load narratives from in-memory array
loadNarrativesFromArray(narratives: TroubleshootingNarrative[]): void;
// Match an error to relevant narratives
matchError(error: Error, context?: ErrorContext): Promise<TroubleshootingMatch[]>;
// Match based on custom criteria
match(context: MatchContext): Promise<TroubleshootingMatch[]>;
// Format matched narratives for display
formatMatches(matches: TroubleshootingMatch[], format?: 'markdown' | 'json' | 'text'): string;
// Get all loaded narratives
getNarratives(): TroubleshootingNarrative[];
// Get narrative by ID
getNarrative(id: string): TroubleshootingNarrative | undefined;
// Register a custom probe
registerProbe(name: string, probe: ProbeFunction): void;
// Get current probe registry
getProbeRegistry(): ProbeRegistry;
}Built-in Probes
The package provides several built-in probes:
error-message
Check if error message contains keywords:
{
probe: "error-message",
params: { keywords: ["missing", "connection"] },
condition: "result.matches == true"
}error-name
Check if error name matches:
{
probe: "error-name",
params: { name: "ValidationError" },
condition: "result.matches == true"
}config-check
Check if config field exists:
{
probe: "config-check",
params: { field: "connections.database", required: true },
condition: "result.exists == false"
}file-exists
Check if file exists:
{
probe: "file-exists",
params: { path: "./config.json" },
condition: "result.exists == false"
}Generic Probe Toolbox
Additional probes available via probeToolbox:
fs-path-status
Check file system path status:
import { probeToolbox } from 'nx-troubleshooting';
engine.registerProbe('fs-path-status', probeToolbox['fs-path-status']);
// Usage in narrative:
{
probe: "fs-path-status",
params: { targetPath: "./config.json" },
condition: "result.exists == false || result.isDirectory == true"
}env-runtime-info
Get Node.js runtime information:
{
probe: "env-runtime-info",
params: {},
condition: "result.nodeVersion.startsWith('v18') == false"
}env-var-check
Check environment variable:
{
probe: "env-var-check",
params: { name: "DATABASE_URL", requireNonEmpty: true },
condition: "result.exists == false || result.nonEmpty == false"
}config-field-type
Check config field type:
{
probe: "config-field-type",
params: { field: "port", type: "number" },
condition: "result.exists == false || result.typeMatches == false"
}Custom Probes
Register project-specific probes:
const engine = new TroubleshootingEngine({
customProbes: {
'mongodb-connect': async (params, context) => {
try {
const client = await MongoClient.connect(params.uri);
await client.close();
return { ok: true };
} catch (error) {
return { ok: false, error: error.message };
}
},
'binding-check': (params, context) => {
const bindings = context.config?.bindings || [];
const found = bindings.some(b => b.connection === params.bindingConnection);
return { found };
}
}
});
// Or register after construction:
engine.registerProbe('custom-probe', (params, context) => {
// Your custom logic
return { result: 'value' };
});Template Variables
Solution messages support template variables using {{variable}} syntax:
{
"message": "Your config is missing '{{query.binding.connection}}'. Available: {{availableConnections}}"
}Template variables are resolved from:
ErrorContextproperties- Custom
templateVarspassed to constructor - Probe results (via
result.*) - Nested object access (e.g.,
query.binding.connection)
Fallback Values
Use || for fallbacks:
{
"message": "Port: {{config.port||3000}}"
}Finding and Creating Troubleshooting Narratives
Where to Find Existing Narratives
Error Logs & Stack Traces
- Review production error logs
- Check test failures
- Analyze user-reported issues
Common Error Patterns
- Configuration errors
- Missing dependencies
- Connection failures
- Validation errors
Support Tickets
- Frequently asked questions
- Common user mistakes
- Setup issues
Code Review
- Error handling patterns
- Validation logic
- Configuration requirements
How to Create a Troubleshooting Narrative
Step 1: Identify the Problem
Start with a real error scenario:
// Error: "Missing connections configuration"
// Stack trace shows: ConfigError at line 42
// Context: config.connections is undefinedStep 2: Define Symptoms
Think about what conditions indicate this problem:
{
"symptoms": [
{
"probe": "config-check",
"params": { "field": "connections" },
"condition": "result.exists == false"
},
{
"probe": "error-message",
"params": { "keywords": ["missing", "connections", "configuration"] },
"condition": "result.matches == true"
}
]
}Step 3: Write the Solution
Provide clear, actionable steps:
{
"solution": [
{
"type": "code",
"message": "Add a 'connections' object to your configuration file:",
"code": "{\n \"connections\": {\n \"database\": {\n \"mongo\": [{\n \"alias\": \"my-mongo\",\n \"uri\": \"mongodb://localhost:27017\",\n \"db\": \"mydb\"\n }]\n }\n }\n}"
},
{
"type": "link",
"message": "See the configuration documentation for more details:",
"url": "https://docs.example.com/configuration"
}
]
}Step 4: Complete the Narrative
{
"id": "missing-connections-config",
"title": "Missing Connections Configuration",
"description": "The application configuration is missing the required 'connections' object. This object defines database connections and other external service endpoints.",
"possibleSolution": "Add a 'connections' object to your config file with at least one database connection.",
"symptoms": [
{
"probe": "config-check",
"params": { "field": "connections" },
"condition": "result.exists == false"
}
],
"solution": [
{
"type": "code",
"message": "Add a 'connections' object to your configuration:",
"code": "{\n \"connections\": {\n \"database\": { ... }\n }\n}"
}
]
}Best Practices for Narratives
Use Descriptive IDs
- Format:
kebab-case - Example:
missing-connections-config,invalid-api-key
- Format:
Clear Titles
- Be specific and concise
- Example: "Missing Connections Configuration" not "Config Error"
Detailed Descriptions
- Explain what the problem is
- Include context about when it occurs
- Example: "The application configuration is missing the required 'connections' object. This typically occurs during initial setup or when the config file is incomplete."
Multiple Symptoms
- Use multiple probes for better matching
- Combine error message keywords with config checks
- Example: Check both error message AND config state
Actionable Solutions
- Provide step-by-step guidance
- Include code examples when relevant
- Link to documentation
- Use template variables for dynamic content
Test Your Narratives
const testError = new Error('Missing connections configuration'); const matches = await engine.matchError(testError, { error: testError, config: {} // Missing connections }); console.assert(matches.length > 0, 'Narrative should match'); console.assert(matches[0].confidence > 0.5, 'Should have reasonable confidence');
Example Troubleshooting JSON
See examples/troubleshooting.json for a complete example with multiple narratives demonstrating best practices.
Environment Configuration
The engine supports environment-based configuration via nx-config2:
# Set narratives path
export NX_TROUBLE_NARRATIVES_PATH=./metadata/troubleshooting.jsonOr configure programmatically:
const engine = new TroubleshootingEngine({
envPrefix: 'MY_APP',
narrativesPath: './troubleshooting.json'
});Integration Examples
In a Test Framework
async function generateBugReport(error: Error, context: ErrorContext) {
const engine = new TroubleshootingEngine({
narrativesPath: './metadata/troubleshooting.json'
});
await engine.loadNarratives();
const matches = await engine.matchError(error, context);
const troubleshooting = engine.formatMatches(matches, 'markdown');
return {
error: {
name: error.name,
message: error.message,
stack: error.stack
},
context,
troubleshooting
};
}In an Express Error Handler
import { TroubleshootingEngine } from 'nx-troubleshooting';
const engine = new TroubleshootingEngine({
narrativesPath: './troubleshooting.json'
});
await engine.loadNarratives();
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
engine.matchError(err, {
error: err,
config: app.get('config'),
query: req.query,
operation: req.method
}).then(matches => {
const troubleshooting = engine.formatMatches(matches, 'markdown');
res.status(500).json({
error: err.message,
troubleshooting
});
});
});With Custom Probes and Template Variables
const engine = new TroubleshootingEngine({
narrativesPath: './troubleshooting.json',
customProbes: {
'database-connection': async (params, context) => {
// Check database connection
return { connected: true, latency: 42 };
}
},
templateVars: {
supportEmail: '[email protected]',
documentationUrl: 'https://docs.example.com'
}
});
// Narratives can use {{supportEmail}} and {{documentationUrl}}Output Formats
Markdown
## 🔧 Troubleshooting Help
### 1. Missing Connections Configuration
The application configuration is missing the required 'connections' object...
**Quick hint**: Add a 'connections' object to your config file
**Suggested steps:**
- (code) Add a 'connections' object to your configuration:
```json
{
"connections": {
"database": { ... }
}
}
### JSON
```json
{
"matches": [
{
"narrative": { /* full narrative */ },
"confidence": 0.9,
"matchedSymptoms": [ /* matched symptoms */ ],
"context": { /* matching context */ }
}
]
}Text
(1) Missing Connections Configuration [90%]
The application configuration is missing the required 'connections' object...
Hint: Add a 'connections' object to your config file
- code: Add a 'connections' object to your configuration:Matching Algorithm
The engine matches errors using:
- Error Name Matching - Exact match with narrative ID or title (confidence: 1.0)
- Keyword Matching - Keywords from error message matched against narrative title/description (confidence: 0.7-0.9)
- Symptom Evaluation - All symptoms evaluated, confidence based on match ratio (confidence: 0.5-0.8)
- Sorting - Results sorted by confidence (highest first)
TypeScript Support
Full TypeScript definitions are included:
import {
TroubleshootingEngine,
TroubleshootingNarrative,
ErrorContext,
MatchContext,
TroubleshootingMatch,
ProbeFunction,
ProbeRegistry
} from 'nx-troubleshooting';Contributing
Contributions are welcome! Please see the contributing guide for details.
License
ISC
Related Packages
nx-config2- Configuration management (used for env config)
Made with ❤️ by nx-intelligence
