Skip to content

A secure, lightweight sandbox for executing AI-generated JavaScript code in isolation. Available as both a React component and an imperative JavaScript API. Perfect for building Claude Artifacts-style experiences in your application.

Notifications You must be signed in to change notification settings

rawmahn/micro-ai-sandbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Micro-ai-sandbox

A secure, lightweight sandbox for executing AI-generated JavaScript code in isolation. Available as both a React component and an imperative JavaScript API. Perfect for building Claude Artifacts-style experiences.

Features

  • Secure Execution - Runs untrusted AI code without XSS vulnerabilities using blob URLs and strict CSP
  • Two-Way Bridge - Bidirectional communication for both events and function calls between parent and sandbox
  • Self-Healing - Catches runtime errors with line numbers for AI feedback loops
  • Chart.js Included - Pre-loaded Chart.js v4 for instant data visualization
  • Zero Backend - Purely client-side, no Node.js runtime required
  • TypeScript - Full type definitions included
  • ~5KB gzipped - Minimal bundle size

Quick concept illustration

Sample Demo Screenshot

Installation

# This package is not published yet, but when it is:
# npm install micro-ai-sandbox

Quick Start

React Component

import { AgentCanvas } from 'micro-ai-sandbox';
import { useState } from 'react';

function App() {
  const [code, setCode] = useState(`
    const canvas = document.createElement('canvas');
    canvas.width = 800;
    canvas.height = 600;
    document.body.appendChild(canvas);

    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#4CAF50';
    ctx.fillRect(100, 100, 200, 150);
  `);

  return (
    <div style={{ width: '800px', height: '600px' }}>
      <AgentCanvas
        code={code}
        allowNetwork={false}
        allowCookies={false}
        onError={(error) => {
          console.error(`${error.type} at line ${error.line}: ${error.message}`);
        }}
        onMessage={(data) => {
          console.log('Sandbox emitted:', data);
        }}
      />
    </div>
  );
}

Imperative API (no React required)

import { Sandbox } from 'micro-ai-sandbox';

const sandbox = new Sandbox({
  code: `
    const canvas = document.createElement('canvas');
    canvas.width = 800;
    canvas.height = 600;
    document.body.appendChild(canvas);

    const ctx = canvas.getContext('2d');
    ctx.fillStyle = 'blue';
    ctx.fillRect(50, 50, 200, 100);
  `,
  onMessage: (data) => console.log('Message:', data),
  onError: (error) => console.error('Error:', error)
});

// Mount to DOM
document.getElementById('container').appendChild(sandbox.getIframe());

// Clean up when done
sandbox.destroy();

API Reference

AgentCanvas Props (React)

Prop Type Default Description
code string required JavaScript or HTML code to execute
contentType 'javascript' | 'html' 'javascript' Type of content
allowNetwork boolean false Allow fetch/XHR requests
allowCookies boolean false Allow cookie access
csp string optional Custom CSP (overrides allow flags)
onError (error: SandboxError) => void optional Error callback
onMessage (data: any) => void optional Message callback
onReady () => void optional Ready callback
hostFunctions Record<string, Function> optional Functions callable from sandbox
sandboxRef React.RefObject<SandboxHandle> optional Ref to sandbox instance
className string optional CSS class for container
style React.CSSProperties optional Inline styles for container
iframeStyle React.CSSProperties optional Inline styles for iframe
developerMode boolean false Show errors in UI overlay

Sandbox Class (Imperative)

constructor(options: SandboxOptions)

interface SandboxOptions {
  code: string;
  contentType?: 'javascript' | 'html';
  allowNetwork?: boolean;
  allowCookies?: boolean;
  csp?: string;
  onError?: (error: SandboxError) => void;
  onMessage?: (data: any) => void;
  onReady?: () => void;
  hostFunctions?: Record<string, (...args: any[]) => any | Promise<any>>;
  container?: HTMLElement;
  iframeStyle?: Partial<CSSStyleDeclaration>;
}

Methods:

  • sendEvent(eventName: string, data?: any): void - Send event to sandbox
  • callFunction(functionName: string, args: any[]): Promise<any> - Call sandbox function
  • updateCode(code: string): void - Update sandbox code
  • getIframe(): HTMLIFrameElement | null - Get iframe element
  • getError(): SandboxError | null - Get current error
  • destroy(): void - Cleanup and destroy sandbox

Sandbox Environment

Bidirectional Communication

Two-Way Bridge for both events and function calls - The sandbox provides four communication channels between parent and sandbox:

Direction Method Description
Sandbox → Parent window.emit(event, data) Send events/data from sandbox to parent (fire-and-forget)
Sandbox → Parent host.functionName(args) Call parent functions from sandbox (returns Promise)
Parent → Sandbox sandbox.sendEvent(event, data) Send events to sandbox (received as CustomEvent)
Parent → Sandbox sandbox.callFunction(name, args) Call sandbox functions (returns Promise)

Available APIs

Inside the sandbox, code has access to:

1. window.emit() - Send events to parent (one-way)

Use this to notify the parent of events or send data without expecting a response.

// In sandbox
window.emit('dataProcessed', { result: 42 });
window.emit('chartClicked', { bar: 3, value: 150 });

// In parent (React)
<AgentCanvas
  onMessage={(data) => {
    console.log(data.event); // 'dataProcessed'
    console.log(data.data);  // { result: 42 }
  }}
/>

// In parent (Imperative)
const sandbox = new Sandbox({
  code: '...',
  onMessage: (data) => {
    console.log(data.event, data.data);
  }
});

2. host.functionName() or window.host.functionName() - Call parent functions (with response)

Use this when sandbox needs data or services from the parent. Returns a Promise.

// Parent provides host functions
const hostFunctions = {
  fetchWeather: async (params) => {
    const response = await fetch('/api/weather', {
      method: 'POST',
      body: JSON.stringify(params)
    });
    return response.json();
  },
  saveData: async (data) => {
    localStorage.setItem('myData', JSON.stringify(data));
    return { success: true };
  }
};

// Sandbox code calls them (note: 'window.' is optional)
const weather = await host.fetchWeather({
  location: 'New York',
  startDate: '2024-12-01',
  endDate: '2024-12-10'
});

const result = await window.host.saveData({ foo: 'bar' });

3. Parent → Sandbox Communication

The parent can also send events and call functions in the sandbox.

sendEvent() - Send events to sandbox
// Imperative API
const sandbox = new Sandbox({ code: '...' });
sandbox.sendEvent('updateTheme', { theme: 'dark' });

// React API
const sandboxRef = useRef<SandboxHandle>(null);
sandboxRef.current?.sendEvent('updateTheme', { theme: 'dark' });

// Sandbox receives as CustomEvent
window.addEventListener('updateTheme', (event) => {
  console.log('Theme changed:', event.detail); // { theme: 'dark' }
  document.body.style.background = event.detail.theme === 'dark' ? '#000' : '#fff';
});
callFunction() - Call sandbox functions
// Sandbox defines functions on window
window.getData = () => {
  return { temperature: 72, humidity: 65 };
};

window.processData = async (input) => {
  // Simulate async work
  await new Promise(r => setTimeout(r, 100));
  return { result: input * 2 };
};

// Imperative API - Parent calls these functions
const sandbox = new Sandbox({ code: '...' });
const data = await sandbox.callFunction('getData', []);
console.log(data); // { temperature: 72, humidity: 65 }

const result = await sandbox.callFunction('processData', [42]);
console.log(result); // { result: 84 }

// React API - Using sandboxRef
const sandboxRef = useRef<SandboxHandle>(null);
const data = await sandboxRef.current?.callFunction('getData', []);

4. Async/Await Support

All code is wrapped in an async context, enabling top-level await:

// ✅ This works!
const response = await fetch('https://api.example.com/data');
const data = await response.json();

// ✅ Call host functions with await
const result = await host.myFunction(arg1, arg2);

// ✅ Proper error handling
try {
  const data = await someAsyncOperation();
} catch (error) {
  console.error('Async error:', error);
}

Examples

Host Functions

function WeatherApp() {
  const hostFunctions = {
    fetchWeather: async (params) => {
      const response = await fetch('/api/weather', {
        method: 'POST',
        body: JSON.stringify(params)
      });
      return response.json();
    }
  };

  const code = `
    // Call host function from sandbox
    const weather = await host.fetchWeather({
      location: 'New York',
      startDate: '2024-12-01',
      endDate: '2024-12-10',
      variables: ['temperature_2m_max']
    });

    // Create visualization
    const canvas = document.createElement('canvas');
    canvas.width = 800;
    canvas.height = 600;
    document.body.appendChild(canvas);

    const ctx = canvas.getContext('2d');
    weather.data.daily.temperature_2m_max.forEach((temp, i) => {
      ctx.fillRect(50 + i * 20, 500 - temp * 3, 15, temp * 3);
    });
  `;

  return <AgentCanvas code={code} hostFunctions={hostFunctions} />;
}

Interactive Visualization

function InteractiveChart() {
  const [selectedItem, setSelectedItem] = useState<string | null>(null);

  const code = `
    const canvas = document.createElement('canvas');
    document.body.appendChild(canvas);

    const chart = new Chart(canvas, {
      type: 'bar',
      data: {
        labels: ['Product A', 'Product B', 'Product C'],
        datasets: [{
          label: 'Sales',
          data: [65, 59, 80],
          backgroundColor: 'rgba(75, 192, 192, 0.5)'
        }]
      },
      options: {
        onClick: (event, elements) => {
          if (elements.length > 0) {
            const index = elements[0].index;
            const label = chart.data.labels[index];
            window.emit('barClicked', { product: label });
          }
        }
      }
    });
  `;

  return (
    <div>
      {selectedItem && <p>You clicked: {selectedItem}</p>}
      <AgentCanvas
        code={code}
        onMessage={(data) => {
          if (data.event === 'barClicked') {
            setSelectedItem(data.data.product);
          }
        }}
      />
    </div>
  );
}

HTML Content

const htmlCode = `
  <!DOCTYPE html>
  <html>
    <head>
      <style>
        body {
          font-family: Arial;
          background: linear-gradient(135deg, #667eea, #764ba2);
          color: white;
          padding: 40px;
        }
        .card {
          background: rgba(255,255,255,0.1);
          padding: 20px;
          border-radius: 10px;
        }
      </style>
    </head>
    <body>
      <div class="card">
        <h1>Dashboard</h1>
        <button onclick="window.emit('buttonClicked', {time: Date.now()})">
          Click Me
        </button>
      </div>
    </body>
  </html>
`;

<AgentCanvas
  contentType="html"
  code={htmlCode}
  onMessage={(data) => console.log('Button clicked:', data.time)}
/>

AI Integration

Sample System Prompt

Here's a sample system prompt for AI models generating code for micro-ai-sandbox:

You are a visualization assistant that generates JavaScript code to run in a sandbox.

When generating visualizations:
- Use vanilla JavaScript and Canvas API or Chart.js (pre-loaded)
- Code runs in a sandboxed environment with access to standard Web APIs
- All code supports top-level await (wrapped in async context)

Available sandbox APIs:
1. host.functionName(args) - Call parent functions (returns Promise)
   Example: const data = await host.fetchData({ query: 'sales' });

2. window.emit(eventName, data) - Send events to parent (fire-and-forget)
   Example: window.emit('chartClicked', { bar: 3, value: 150 });

3. Standard Web APIs - Canvas, DOM, console, setTimeout, fetch (if enabled)

Example code structure:
```javascript
// Fetch data from parent if needed
const data = await host.fetchData({ type: 'sales', range: '2024' });

// Create canvas
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
document.body.appendChild(canvas);

const ctx = canvas.getContext('2d');

// Draw visualization
data.values.forEach((value, i) => {
  ctx.fillStyle = '#4CAF50';
  ctx.fillRect(50 + i * 30, 500 - value * 2, 25, value * 2);
});

// Send event when user interacts
canvas.addEventListener('click', (e) => {
  const barIndex = Math.floor((e.offsetX - 50) / 30);
  window.emit('barClicked', { index: barIndex, value: data.values[barIndex] });
});

Security

Defense in Depth

  • Blob URL Isolation - Each execution runs in a unique origin
  • Content Security Policy - Strict CSP headers block unauthorized access
  • Iframe Sandbox - Restrictive sandbox attributes prevent dangerous operations
  • Cookie Blocking - Optional document.cookie access prevention
  • Network Blocking - Optional fetch/XHR blocking
  • Message Validation - postMessage origin validation

Custom CSP

For fine-grained control:

const sandbox = new Sandbox({
  csp: [
    "default-src 'none'",
    "script-src 'unsafe-inline' 'unsafe-eval'",
    "connect-src https://api.example.com",
    "img-src * data: blob:",
    "style-src 'unsafe-inline'"
  ].join('; '),
  code: yourCode
});

Note: When csp is provided, allowNetwork and allowCookies are ignored.

Architecture

Built on the "Micro-Sandbox" pattern using blob URLs for maximum security with minimal overhead:

  • Zero runtime dependencies (React is peer dependency)
  • ~5KB gzipped
  • No build step required for consumers
  • Chart.js loaded from CDN on demand

Development

# Install dependencies
npm install

# Build for production
npm run build

# Type checking
npm run typecheck

# Development mode with watch
npm run dev

Examples

See examples/ directory for complete working examples including:

  • React integration
  • Weather data visualization with host functions
  • AI agent integration
  • Interactive charts

License

MIT


Made for building Claude Artifacts-style experiences. Perfect for AI-powered data visualization, interactive demos, and secure code execution.

About

A secure, lightweight sandbox for executing AI-generated JavaScript code in isolation. Available as both a React component and an imperative JavaScript API. Perfect for building Claude Artifacts-style experiences in your application.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published