npm.io
2.0.1 • Published 9 months ago

nx-troubleshooting

Licence
ISC
Version
2.0.1
Deps
1
Size
71 kB
Vulns
0
Weekly
0

nx-troubleshooting

Generic troubleshooting engine for matching errors to solutions

npm version License: ISC

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-config2 for enhanced features)

Installation

npm install nx-troubleshooting

Quick 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 link type solutions
  • Code: Optional code example for code type 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:

  1. ErrorContext properties
  2. Custom templateVars passed to constructor
  3. Probe results (via result.*)
  4. 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
  1. Error Logs & Stack Traces

    • Review production error logs
    • Check test failures
    • Analyze user-reported issues
  2. Common Error Patterns

    • Configuration errors
    • Missing dependencies
    • Connection failures
    • Validation errors
  3. Support Tickets

    • Frequently asked questions
    • Common user mistakes
    • Setup issues
  4. 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 undefined
Step 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
  1. Use Descriptive IDs

    • Format: kebab-case
    • Example: missing-connections-config, invalid-api-key
  2. Clear Titles

    • Be specific and concise
    • Example: "Missing Connections Configuration" not "Config Error"
  3. 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."
  4. Multiple Symptoms

    • Use multiple probes for better matching
    • Combine error message keywords with config checks
    • Example: Check both error message AND config state
  5. Actionable Solutions

    • Provide step-by-step guidance
    • Include code examples when relevant
    • Link to documentation
    • Use template variables for dynamic content
  6. 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.json

Or 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: 'support@example.com',
    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:

  1. Error Name Matching - Exact match with narrative ID or title (confidence: 1.0)
  2. Keyword Matching - Keywords from error message matched against narrative title/description (confidence: 0.7-0.9)
  3. Symptom Evaluation - All symptoms evaluated, confidence based on match ratio (confidence: 0.5-0.8)
  4. 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

  • nx-config2 - Configuration management (used for env config)

Made with by nx-intelligence

Keywords