npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

x-risk-recommendation

v1.0.3

Published

A Risk Recommendation System built on nx-rules for analyzing data exposure risks and producing actionable recommendations

Readme

Risk Recommendation System

A production-grade recommendation system built on nx-rules for analyzing data exposure risks and producing actionable recommendations.

TypeScript License: MIT

Overview

Organizations track data exposure risks through "risk counts"—aggregated metrics describing what sensitive information was found, how much, and its severity. This system determines whether risks can be automatically remediated via existing policies (like Microsoft Information Protection or Google Workspace labels) or require manual review.

┌─────────────────────────────────────────────────────────────────┐
│                    Risk Recommendation System                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌──────────┐     ┌──────────────┐     ┌──────────────────┐   │
│   │  Input   │────▶│   nx-rules   │────▶│     Output       │   │
│   │RiskCount │     │   Engine     │     │ Recommendation   │   │
│   └──────────┘     └──────┬───────┘     └──────────────────┘   │
│                          │                                      │
│                          ▼                                      │
│                   ┌──────────────┐                              │
│                   │  RuleSet     │                              │
│                   │  - route     │                              │
│                   │  - actions   │                              │
│                   │  - templates │                              │
│                   └──────────────┘                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Features

  • 🎯 Simple Decision Logic - Two paths: policy (automated) or recommendation (manual)
  • 📝 Template-Based Output - Configurable recommendation content
  • 🔍 Full Traceability - Track exactly which rule matched and why
  • 📊 JSON-Based Rules - Business logic as data, not code
  • 🔧 Extensible - Add new templates and rules without code changes

Installation

npm install

Quick Start

import { createRecommendationEngine, type RiskCount } from 'risk-recommendation-system';

// Create the engine
const engine = createRecommendationEngine();

// Evaluate a risk with an external label → Policy recommendation
const riskWithLabel: RiskCount = {
  riskType: 'PII_EXPOSURE',
  count: 15,
  contentLabel: 'SSN',
  externalLabel: 'Confidential',  // Has label mapping
  severity: 'high'
};

const result1 = await engine.evaluate(riskWithLabel);
console.log(result1.recommendation.action);   // 'policy'
console.log(result1.recommendation.content);
// "Apply Confidential policy to automatically remediate 15 SSN exposure(s). Severity: high | Risk Type: PII_EXPOSURE"

// Evaluate a risk without an external label → Manual recommendation
const riskWithoutLabel: RiskCount = {
  riskType: 'CREDENTIAL_LEAK',
  count: 8,
  contentLabel: 'API Key',
  severity: 'critical'
  // No externalLabel
};

const result2 = await engine.evaluate(riskWithoutLabel);
console.log(result2.recommendation.action);   // 'recommendation'
console.log(result2.recommendation.content);
// "Review and manually label 8 API Key item(s) flagged as CREDENTIAL_LEAK. Severity: critical. No automated policy available—manual classification required."

Data Structures

Input: RiskCount

interface RiskCount {
  riskType: string;           // Category of risk (e.g., "PII_EXPOSURE")
  count: number;              // Number of instances detected
  contentLabel: string;       // What information is at risk (e.g., "SSN")
  externalLabel?: string;     // Mapped MIP/Google label, if any
  severity: 'low' | 'medium' | 'high' | 'critical';
}

Output: Recommendation

interface Recommendation {
  riskInput: RiskCount;       // Original input (preserved)
  action: 'policy' | 'recommendation';
  content: string;            // Human-readable recommendation
}

Decision Logic

The system implements a simple routing decision:

IF risk has an external label mapped
    THEN action = "policy"
    AND content = policy remediation instructions

IF risk has no external label mapped
    THEN action = "recommendation"  
    AND content = manual review instructions

Rules

Two rules are configured:

1. Policy Path (Priority: 100)

Routes to automated remediation when externalLabel exists:

{
  "ruleId": "risk-with-label-to-policy",
  "priority": 100,
  "route": {
    "exists": { "path": "input.externalLabel" }
  },
  "actions": {
    "onMatch": [{
      "type": "transform",
      "args": { "action": "policy", "template": "policy-recommendation" }
    }]
  }
}

2. Recommendation Path (Priority: 90)

Routes to manual review when no externalLabel:

{
  "ruleId": "risk-without-label-to-recommendation",
  "priority": 90,
  "route": {
    "not": { "exists": { "path": "input.externalLabel" } }
  },
  "actions": {
    "onMatch": [{
      "type": "transform",
      "args": { "action": "recommendation", "template": "manual-recommendation" }
    }]
  }
}

Templates

Policy Recommendation

Apply {{externalLabel}} policy to automatically remediate {{count}} {{contentLabel}} exposure(s). 
Severity: {{severity}} | Risk Type: {{riskType}}

Manual Recommendation

Review and manually label {{count}} {{contentLabel}} item(s) flagged as {{riskType}}. 
Severity: {{severity}}. No automated policy available—manual classification required.

With Scope

const result = await engine.evaluate(risk, {
  tenantId: 'tenant-123',
  environment: 'production'
});
// Only rules matching this scope (or global rules) will be evaluated

Configuration

const engine = createRecommendationEngine({
  matchPolicy: 'FIRST_MATCH',   // Stop after first rule matches
  conflictPolicy: 'ERROR',      // Only one rule should match
  trace: true                   // Enable for audit trail
});

Tracing

Every evaluation includes trace events for debugging and auditing:

const result = await engine.evaluate(risk);

if (result.trace) {
  result.trace.forEach(event => {
    console.log(event.type, event.ruleId, event.timestamp);
  });
}

Trace event types:

  • evaluation_start / evaluation_end
  • route_end
  • condition_end
  • action_start / action_end
  • write
  • error

API Reference

createRecommendationEngine(config?)

Creates a new recommendation engine.

const engine = createRecommendationEngine({
  matchPolicy?: 'FIRST_MATCH' | 'ALL_MATCH',
  conflictPolicy?: 'FIRST_WRITE_WINS' | 'LAST_WRITE_WINS' | 'MERGE' | 'ERROR',
  trace?: boolean
});

engine.evaluate(riskCount)

Evaluates a risk and produces a recommendation.

const result = await engine.evaluate(riskCount);
// Returns: RecommendationResult

engine.getRules()

Returns all configured rules.

engine.addRule(rule)

Adds a new rule to the engine.

evaluateRisk(riskCount)

Quick helper function for one-time evaluations.

import { evaluateRisk } from 'risk-recommendation-system';

const result = await evaluateRisk(riskCount);

Testing

npm test

Development

# Run demo
npm run dev

# Build
npm run build

# Watch tests
npm run test:watch

Project Structure

risk-recommendation-system/
├── src/
│   ├── index.ts              # Main exports
│   ├── types.ts              # TypeScript interfaces
│   ├── engine.ts             # Rule engine implementation
│   ├── demo.ts               # Demo script
│   ├── templates/
│   │   └── index.ts          # Template registry
│   ├── functions/
│   │   └── render.ts         # Render function
│   └── rules/
│       └── index.ts          # Rule definitions
├── tests/
│   └── recommendation.test.ts
├── package.json
├── tsconfig.json
└── vitest.config.ts

Extension Points

| To Add... | Configure... | |-----------|--------------| | New risk type | No change needed (generic) | | New severity level | No change needed (pass-through) | | New recommendation path | Add new rule with route condition | | New template | Register template, reference in rule action | | Custom content logic | Register new function, reference in action |

Error Handling

| Condition | Behavior | |-----------|----------| | No rule matches | Returns error: "No recommendation path for input" | | Template not found | Returns error: "Template {id} not registered" | | Missing required field | Validation error before evaluation |

License

MIT

Based On

  • nx-rules - Production-grade rules engine for Node.js/TypeScript