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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@memberjunction/code-execution

v2.123.1

Published

Sandboxed code execution service for MemberJunction actions and agents

Readme

@memberjunction/code-execution

Sandboxed JavaScript code execution service for MemberJunction AI agents and workflows.

Overview

This package provides secure, isolated execution of JavaScript code in a sandboxed environment using isolated-vm with worker process isolation. It's designed to enable AI agents to generate and run code for data analysis, transformations, and calculations without compromising system security or stability.

⚠️ Security: This package handles untrusted code execution. Please review security-research.md for comprehensive security analysis, threat model, and implementation details.

Features

  • Multi-Layer Security: 5 independent defense layers (process isolation, V8 isolates, module blocking, resource limits, library allowlist)
  • Fault Isolation: Workers run in separate processes - crashes don't affect main application
  • Automatic Recovery: Crashed workers automatically restart with circuit breaker protection
  • Timeout Protection: Configurable execution timeouts (default: 30 seconds)
  • Memory Limits: Configurable per-execution memory limits (default: 128MB)
  • Safe Library Access: Curated allowlist of npm packages with inline implementations
  • Console Logging: Captures console output for debugging
  • Type Safety: Full TypeScript support with comprehensive interfaces
  • Audit Trail: Integrated with MemberJunction action logging

Installation

npm install @memberjunction/code-execution

Usage

Direct TypeScript Usage

import { CodeExecutionService } from '@memberjunction/code-execution';

// Create service with optional worker pool configuration
const service = new CodeExecutionService({
  poolSize: 2,              // Number of worker processes (default: 2)
  maxQueueSize: 100,        // Max queued requests (default: 100)
  maxCrashesPerWorker: 3,   // Crashes before marking unhealthy (default: 3)
  crashTimeWindow: 60000    // Time window for crash counting (default: 60s)
});

// Initialize the worker pool (can also be done automatically on first execute)
await service.initialize();

// Execute code
const result = await service.execute({
  code: `
    const sum = input.values.reduce((a, b) => a + b, 0);
    const average = sum / input.values.length;
    output = { sum, average };
  `,
  language: 'javascript',
  inputData: { values: [10, 20, 30, 40, 50] },
  timeoutSeconds: 30,       // Optional: execution timeout (default: 30)
  memoryLimitMB: 128        // Optional: memory limit (default: 128)
});

if (result.success) {
  console.log(result.output); // { sum: 150, average: 30 }
  console.log(result.logs);   // Any console.log output from the code
} else {
  console.error(result.error);
  console.error(result.errorType); // 'TIMEOUT' | 'MEMORY_LIMIT' | 'SYNTAX_ERROR' | etc.
}

// Check worker pool health
const stats = service.getStats();
console.log('Active workers:', stats.activeWorkers);
console.log('Busy workers:', stats.busyWorkers);
console.log('Queue length:', stats.queueLength);

// Graceful shutdown (important for clean application exit)
await service.shutdown();

With Actions (for AI Agents)

Agents use the "Execute Code" action:

{
  "type": "Action",
  "action": {
    "name": "Execute Code",
    "params": {
      "code": "const total = input.prices.reduce((sum, p) => sum + p, 0); output = total;",
      "language": "javascript",
      "inputData": "{\"prices\": [10, 20, 30]}"
    }
  }
}

Supported Libraries

The sandbox includes these pre-approved npm packages:

  • lodash - Data manipulation and utilities
  • date-fns - Modern date library
  • mathjs - Advanced mathematics
  • papaparse - CSV parsing
  • uuid - UUID generation
  • validator - String validation and sanitization

Using Libraries in Code

const result = await service.execute({
  code: `
    const _ = require('lodash');
    const { format, addDays } = require('date-fns');
    const math = require('mathjs');

    // Group data by category
    const grouped = _.groupBy(input.data, 'category');

    // Calculate statistics
    const stats = {};
    for (const [category, items] of Object.entries(grouped)) {
      const values = items.map(i => i.value);
      stats[category] = {
        mean: math.mean(values),
        median: math.median(values),
        total: math.sum(values)
      };
    }

    output = stats;
  `,
  language: 'javascript',
  inputData: {
    data: [
      { category: 'A', value: 10 },
      { category: 'B', value: 20 },
      { category: 'A', value: 15 }
    ]
  }
});

Security Model

For detailed security analysis, see security-research.md

This package uses a defense-in-depth approach with five independent security layers:

  1. Process Isolation - Workers run in separate OS processes
  2. V8 Isolates - Each execution runs in isolated V8 context (via isolated-vm)
  3. Module Blocking - Dangerous Node.js modules are blocked
  4. Resource Limits - Enforced timeout and memory limits
  5. Library Allowlist - Only pre-vetted libraries available

What Code CAN Do

  • Access input data via input variable
  • Set output data via output variable
  • Use console methods (log, error, warn, info)
  • Require allowed npm packages (lodash, date-fns, uuid, validator)
  • Perform calculations and data transformations
  • Use safe built-in objects (JSON, Math, Date, Array, Object, String, Number, Boolean)

What Code CANNOT Do

  • Access filesystem (fs, path modules blocked)
  • Make network requests (http, https, net, axios modules blocked, fetch() API disabled)
  • Spawn processes (child_process, cluster blocked)
  • Access system information (os, process blocked)
  • Access environment variables
  • Use eval() or Function constructor (for user code)
  • Run indefinitely (timeout enforced)
  • Exceed memory limits (128MB default)

API Reference

CodeExecutionService

execute(params: CodeExecutionParams): Promise<CodeExecutionResult>

Executes code in a sandboxed environment.

Parameters:

interface CodeExecutionParams {
  code: string;              // JavaScript code to execute
  language: 'javascript';    // Currently only 'javascript' supported
  inputData?: any;           // Data available as 'input' variable
  timeoutSeconds?: number;   // Execution timeout (default: 30)
  memoryLimitMB?: number;    // Memory limit (default: 128)
}

Returns:

interface CodeExecutionResult {
  success: boolean;           // Whether execution succeeded
  output?: any;              // Value of 'output' variable
  logs?: string[];           // Console output
  error?: string;            // Error message if failed
  errorType?: 'TIMEOUT' | 'MEMORY_LIMIT' | 'SYNTAX_ERROR' | 'RUNTIME_ERROR' | 'SECURITY_ERROR';
  executionTimeMs?: number;  // Execution duration
}

Error Handling

The service handles various error conditions gracefully:

const result = await service.execute({
  code: 'invalid syntax here',
  language: 'javascript'
});

if (!result.success) {
  switch (result.errorType) {
    case 'SYNTAX_ERROR':
      console.error('Code has syntax errors:', result.error);
      break;
    case 'TIMEOUT':
      console.error('Code execution timed out');
      break;
    case 'RUNTIME_ERROR':
      console.error('Runtime error:', result.error);
      break;
    case 'SECURITY_ERROR':
      console.error('Security violation:', result.error);
      break;
  }
}

Examples

Data Analysis

const result = await service.execute({
  code: `
    const _ = require('lodash');

    // Calculate key metrics
    const metrics = {
      totalSales: _.sumBy(input.sales, 'amount'),
      avgSale: _.meanBy(input.sales, 'amount'),
      topProducts: _.chain(input.sales)
        .groupBy('product')
        .map((items, product) => ({
          product,
          revenue: _.sumBy(items, 'amount')
        }))
        .orderBy('revenue', 'desc')
        .take(5)
        .value()
    };

    output = metrics;
  `,
  language: 'javascript',
  inputData: {
    sales: [
      { product: 'Widget', amount: 100 },
      { product: 'Gadget', amount: 200 },
      // ... more sales
    ]
  }
});

Date Manipulation

const result = await service.execute({
  code: `
    const { format, addDays, differenceInDays } = require('date-fns');

    const start = new Date(input.startDate);
    const end = new Date(input.endDate);

    output = {
      duration: differenceInDays(end, start),
      milestones: [
        format(addDays(start, 30), 'yyyy-MM-dd'),
        format(addDays(start, 60), 'yyyy-MM-dd'),
        format(addDays(start, 90), 'yyyy-MM-dd')
      ]
    };
  `,
  language: 'javascript',
  inputData: {
    startDate: '2025-01-01',
    endDate: '2025-12-31'
  }
});

CSV Processing

const result = await service.execute({
  code: `
    const Papa = require('papaparse');

    const parsed = Papa.parse(input.csvData, {
      header: true,
      dynamicTyping: true
    });

    // Filter and transform
    const processed = parsed.data
      .filter(row => row.status === 'active')
      .map(row => ({
        id: row.id,
        name: row.name,
        value: row.value * 1.1 // 10% markup
      }));

    output = processed;
  `,
  language: 'javascript',
  inputData: {
    csvData: 'id,name,value,status\n1,Item A,100,active\n2,Item B,200,inactive'
  }
});

Best Practices

  1. Initialize Once: Create one CodeExecutionService instance and reuse it (worker pool is shared)
  2. Shutdown Gracefully: Call service.shutdown() during application shutdown to clean up workers
  3. Set Appropriate Timeouts: Adjust timeoutSeconds based on expected workload
  4. Validate Input Data: Ensure input data is in expected format before execution
  5. Handle Errors Gracefully: Always check result.success before using result.output
  6. Monitor Pool Health: Use service.getStats() to monitor worker health and queue depth
  7. Use Console Logging: Add console.log() statements for debugging
  8. Leverage Libraries: Use lodash, date-fns, etc. instead of reinventing the wheel
  9. Keep Code Focused: Break complex logic into smaller execution chunks
  10. Review Security Docs: Read security-research.md for threat model and security considerations

Architecture

Worker Process Pool

The service maintains a pool of worker processes for fault isolation:

┌─────────────────────────────────────┐
│   Main Application Process          │
│                                      │
│  ┌────────────────────────────┐    │
│  │  CodeExecutionService       │    │
│  │                              │    │
│  │  ┌──────────────────────┐   │    │
│  │  │   WorkerPool         │   │    │
│  │  │   - Queue Requests   │   │    │
│  │  │   - Monitor Health   │   │    │
│  │  │   - Auto Restart     │   │    │
│  │  └──────────────────────┘   │    │
│  └────────────────────────────┘    │
│          │          │               │
└──────────┼──────────┼───────────────┘
           │          │ IPC (JSON)
    ┌──────▼───┐  ┌───▼──────┐
    │ Worker 1 │  │ Worker 2 │  (Separate OS Processes)
    │          │  │          │
    │ isolated │  │ isolated │
    │   -vm    │  │   -vm    │
    │          │  │          │
    └──────────┘  └──────────┘

Benefits of Process Isolation

  • Fault Tolerance: Worker crashes don't affect main app or other workers
  • Resource Isolation: Per-process memory limits and cleanup
  • Automatic Recovery: Crashed workers restart automatically
  • Circuit Breaker: Too many crashes → worker disabled to prevent crash loops

Limitations

  • Language Support: Currently only JavaScript (Python may be added in future)
  • Async Code: No support for async/await or Promises (synchronous code only)
  • Library Expansion: New libraries require code changes (not configurable at runtime)
  • Throughput: ~100-200 executions/sec per worker (horizontal scaling recommended for higher loads)

Contributing

This package is part of the MemberJunction project. See the main repository for contribution guidelines.

License

ISC