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

@isdk/sandbox-executor

v0.1.1

Published

Isolated multi-language function executor using WebAssembly (WASM). Supports Python, JavaScript, Ruby, PHP, C/C++ with fine-grained file system permissions and virtual FS.

Readme

Sandbox Executor

npm version License: MIT TypeScript

A powerful, type-safe library for executing functions in isolated sandbox environments across multiple programming languages. Built on top of @runno/sandbox with WebAssembly-based isolation.

✨ Features

  • 🌍 Multi-Language Support - Execute Python, JavaScript (QuickJS), Ruby, PHP, C and C++ code
  • 📦 Function-Level Execution - Call specific functions with args and kwargs, not just run scripts
  • 🔒 Permission Control - Fine-grained file system permissions with glob patterns
  • 📁 Virtual File System - In-memory file operations with optional real filesystem sync
  • 🔄 Change Tracking - Automatic detection and tracking of file changes (Snapshot-based for robustness)
  • 🎯 Smart Signature Inference - Automatically infers function signatures from code
  • 📊 Schema Support - Optional explicit parameter schemas for precise control
  • 🎪 Event System - Rich event hooks for sync operations with abort/skip capabilities
  • 🛡️ Type-Safe - Full TypeScript support with comprehensive type definitions

📦 Installation

npm install @isdk/sandbox-executor
# or
yarn add @isdk/sandbox-executor
# or
pnpm add @isdk/sandbox-executor

🚀 Quick Start

Basic Function Execution

import { createExecutor } from '@isdk/sandbox-executor';

const executor = createExecutor();

// Python
const result = await executor.execute({
  language: 'python',
  code: `
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"
  `,
  functionName: 'greet',
  args: ['World'],
  kwargs: { greeting: 'Hi' },
});

console.log(result.result); // "Hi, World!"

// PHP (Supports automatic <?php tag injection)
const phpResult = await executor.execute({
  language: 'php',
  code: `
function multiply($a, $b) {
    return $a * $b;
}
  `,
  functionName: 'multiply',
  args: [6, 7],
});

console.log(phpResult.result); // 42

// JavaScript (Supports aliases: quickjs, js, javascript)
const jsResult = await executor.execute({
  language: 'js',
  code: `
function calculate(a, b, options = {}) {
  const { multiplier = 1 } = options;
  return (a + b) * multiplier;
}
  `,
  functionName: 'calculate',
  args: [5, 3],
  kwargs: { multiplier: 10 },
});

console.log(jsResult.result); // 80

With Virtual Files

const result = await executor.execute({
  language: 'python',
  code: `
def process(input_path, output_path):
    with open(input_path) as f:
        data = f.read()

    with open(output_path, 'w') as f:
        f.write(data.upper())

    return len(data)
  `,
  functionName: 'process',
  args: ['/workspace/input.txt', '/workspace/output.txt'],
  files: {
    'input.txt': 'hello world',
  },
});

console.log(result.result); // 11
console.log(result.files?.created); // [{ path: '/workspace/output.txt', ... }]

With Real Directory Mount (Node.js)

const result = await executor.execute({
  language: 'python',
  code: `
def process_files(input_dir, output_dir):
    import os
    for name in os.listdir(input_dir):
        with open(f'{input_dir}/{name}') as f:
            data = f.read()
        with open(f'{output_dir}/{name}.out', 'w') as f:
            f.write(data.upper())
    return 'done'
  `,
  functionName: 'process_files',
  args: ['/data/input', '/data/output'],

  mount: {
    dirs: {
      '/data': './my-project/data',
    },
    permissions: {
      default: { read: true, list: true },
      rules: [
        { pattern: 'output/**', allow: ['create', 'modify'] },
      ],
      exclude: ['node_modules', '.git'],
    },
    loading: { mode: 'eager' },
    sync: { mode: 'batch', onError: 'continue' },
  },
});

📖 API Reference

createExecutor(options?)

Creates a new sandbox executor instance.

const executor = createExecutor({
  defaultWorkdir: '/workspace',  // Default: '/workspace'
  syncEventConfig: {
    allowAbort: true,  // Allow beforeSync to abort entire sync
  },
});

executor.execute(request)

Executes a function in the sandbox.

interface FunctionCallRequest {
  // Required
  language: 'python' | 'ruby' | 'quickjs' | 'php' | 'js' | 'javascript' | 'c' | 'cpp';
  code: string;
  functionName: string;

  // Optional
  args?: unknown[];
  kwargs?: Record<string, unknown>;
  schema?: FunctionSchema;
  mount?: MountConfig;
  files?: Record<string, string | Uint8Array>;
  workdir?: string;
  resultOptions?: {
    includeChanges?: boolean;
    includeContents?: boolean;
    includeDenied?: boolean;
  };
}

executor.syncFiles(changes, mount, options?)

Manually sync file changes to real filesystem.

const syncResult = await executor.syncFiles(
  result.files?.created ?? [],
  { dirs: { '/workspace': './output' } },
  { onError: 'continue' }
);

Execution Result

interface ExecutionResult<T> {
  status: 'success' | 'error' | 'crash' | 'timeout' | 'terminated';
  success: boolean;
  result?: T;
  error?: {
    message: string;
    type?: string;
    stack?: string;
  };
  stdout: string;
  stderr: string;
  exitCode: number;
  files?: FileChangeSummary;
  meta?: {
    duration: number;
    signatureSource: 'schema' | 'inferred' | 'convention';
  };
}

⚙️ Configuration

Mount Configuration

interface MountConfig {
  // Directory mappings: { virtualPath: realPath }
  dirs: Record<string, string>;

  // Permission configuration
  permissions?: {
    default?: {
      read?: boolean;    // Default: true
      list?: boolean;    // Default: true
      create?: boolean;  // Default: false
      modify?: boolean;  // Default: false
      delete?: boolean;  // Default: false
    };
    rules?: Array<{
      pattern: string;      // Glob pattern
      allow?: Permission[] | '*';
      deny?: Permission[] | '*';
      priority?: number;    // Higher = matched first
    }>;
    exclude?: string[];     // Shorthand for high-priority deny rules
  };

  // Loading strategy
  loading?: {
    mode: 'eager' | 'lazy' | 'explicit';
    include?: string[];
    maxFileSize?: number;
    maxTotalSize?: number;
  };

  // Sync strategy
  sync?: {
    mode: 'batch' | 'manual';
    onError?: 'rollback' | 'continue' | 'abort';
  };

  // Security options
  security?: {
    followSymlinks?: boolean | 'restricted';
  };

  // Permission denied behavior
  onPermissionDenied?: 'throw' | 'ignore' | 'virtual';
}

Permission Patterns

The library uses minimatch for glob pattern matching:

| Pattern | Description | Example Matches | |---------|-------------|-----------------| | * | Match any characters in a segment | *.txtfile.txt | | ** | Match any directory depth | src/**/*.tssrc/a/b/c.ts | | ? | Match exactly one character | file?.txtfile1.txt | | [abc] | Match any character in set | file[123].txtfile1.txt | | [!abc] | Match any character not in set | file[!0-9].txtfileA.txt | | {a,b} | Match any of the patterns | *.{js,ts}app.js, app.ts |

Function Schema

Provide explicit parameter schemas for precise control:

await executor.execute({
  language: 'python',
  code: '...',
  functionName: 'process',
  args: [1, 2],
  kwargs: { c: 3 },
  schema: {
    params: [
      { name: 'a', type: 'number', required: true },
      { name: 'b', type: 'number', required: true },
      { name: 'c', type: 'number', required: false, default: 0 },
    ],
    variadic: false,
    acceptsKwargs: false,
  },
});

🎪 Events

Use event listeners to intercept and control sync operations:

import { createExecutor, SyncStates } from '@isdk/sandbox-executor';

const executor = createExecutor();

// Before sync - can skip or abort
executor.on('beforeSync', function(change) {
  console.log(`Syncing: ${change.path}`);

  // Skip specific files
  if (change.path.endsWith('.tmp')) {
    this.result = { state: SyncStates.SKIP, reason: 'Temporary file' };
    return;
  }

  // Abort entire sync
  if (change.path.includes('/secrets/')) {
    this.result = { state: SyncStates.ABORT };
    this.stopped = true;
  }
});

// After sync - notification only
executor.on('afterSync', function(data) {
  if (data.success) {
    console.log(`✅ Synced: ${data.path}`);
  } else {
    console.log(`❌ Failed: ${data.path}`, data.error);
  }
});

// Error handling
executor.on('syncError', function(change, error) {
  console.error(`Sync error for ${change.path}:`, error);
});

// Permission denied
executor.on('permissionDenied', function(record) {
  console.warn(`🚫 Permission denied: ${record.operation} on ${record.path}`);
});

🔄 Execution Modes

Pure Virtual Mode

No real filesystem access, everything runs in memory:

const result = await executor.execute({
  language: 'python',
  code: 'def add(a, b): return a + b',
  functionName: 'add',
  args: [1, 2],
});
// No mount config = full permissions in virtual FS

Virtual Files Mode

Provide virtual files without syncing to real filesystem:

const result = await executor.execute({
  language: 'python',
  code: '...',
  functionName: 'process',
  files: {
    'input.txt': 'content',
    'config.json': '{"key": "value"}',
  },
});
// Files exist only in memory

Real Directory Mode

Mount real directories with permission control and sync:

const result = await executor.execute({
  language: 'python',
  code: '...',
  functionName: 'process',
  mount: {
    dirs: { '/workspace': './real-dir' },
    sync: { mode: 'batch' },
  },
});
// Changes synced to real filesystem

Manual Sync Mode

Control exactly which changes to sync:

const result = await executor.execute({
  language: 'python',
  code: '...',
  functionName: 'generate',
  mount: {
    dirs: { '/workspace': './output' },
    sync: { mode: 'manual' },
  },
});

// Inspect changes first
console.log('Created:', result.files?.created);

// Sync only selected files
const toSync = result.files?.created.filter(f => f.path.endsWith('.json')) ?? [];
await executor.syncFiles(toSync, { dirs: { '/workspace': './output' } });

🧪 Signature Inference

The library automatically infers function signatures with a three-tier priority:

  1. Schema (highest) - User-provided explicit schema
  2. Inferred - Parsed from code using AST/regex
  3. Convention (lowest) - Language-specific defaults

Python Convention

def func(*args, **kwargs):  # Supports variadic and kwargs
    pass

JavaScript Convention

function func(arg1, arg2, options = {}) {  // Last param as options object
}

Ruby Convention

def func(*args, **kwargs)  # Supports variadic and kwargs
end

PHP Convention

function func(...$args) {  // Supports variadic and kwargs (via associative arrays)
}

📊 Result Status

| Status | Description | Exit Code | |--------|-------------|-----------| | success | Function executed successfully | 0 | | error | Function threw an exception | 1 | | crash | WASM runtime crashed | 1 | | timeout | Execution timed out | 124 | | terminated | Execution was terminated | 143 |

🔧 Advanced Usage

Custom Workdir

const executor = createExecutor({
  defaultWorkdir: '/app',
});

// Or per-request
await executor.execute({
  // ...
  workdir: '/custom/path',
});

Type-Safe Results

interface UserData {
  name: string;
  age: number;
}

const result = await executor.execute<UserData>({
  language: 'python',
  code: `
def get_user():
    return {"name": "Alice", "age": 30}
  `,
  functionName: 'get_user',
});

if (result.success) {
  console.log(result.result?.name); // TypeScript knows this is string
}

Error Handling

const result = await executor.execute({
  language: 'python',
  code: 'def divide(a, b): return a / b',
  functionName: 'divide',
  args: [1, 0],
});

switch (result.status) {
  case 'success':
    console.log('Result:', result.result);
    break;
  case 'error':
    console.log('Function error:', result.error?.message);
    console.log('Error type:', result.error?.type); // e.g., 'ZeroDivisionError'
    break;
  case 'crash':
    console.log('Runtime crashed:', result.error?.message);
    break;
  case 'timeout':
    console.log('Execution timed out');
    break;
  case 'terminated':
    console.log('Execution was terminated');
    break;
}

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide to learn about our architecture, how to add new languages, and development workflow.

📄 License

MIT © Riceball LEE

🙏 Acknowledgments