Skip to main content

Vibe Compiler

The Vibe Compiler validates generated code before execution, blocking dangerous patterns and enforcing security constraints. It’s used internally by VibeFrame but can also be used directly for pre-validation.

Usage

import { VibeCompiler } from 'genkit'

const compiler = new VibeCompiler()

try {
  const compiled = await compiler.compile(generatedCode)
  console.log('Code is valid:', compiled.hash)
} catch (error) {
  console.error('Validation failed:', error.message)
}

Configuration

const compiler = new VibeCompiler({
  // Allowed import sources (default: ['react'])
  allowedImports: ['react', 'recharts', 'date-fns'],

  // Maximum code size in bytes (default: 100KB)
  maxCodeSize: 200_000,

  // Enable caching of compiled results (default: true)
  cache: true
})

Security Checks

Blocked Patterns

The compiler rejects code containing:
// Dynamic code execution
eval('...')           // ❌ Blocked
Function('...')       // ❌ Blocked
new Function('...')   // ❌ Blocked

// Dynamic imports
import('...')         // ❌ Blocked
require('...')        // ❌ Blocked

// Global access
process.env           // ❌ Blocked
global.anything       // ❌ Blocked
window.anything       // ❌ Blocked
document.anything     // ❌ Blocked

// Prototype pollution
obj.__proto__         // ❌ Blocked
obj.constructor[...]  // ❌ Blocked

Import Validation

Only whitelisted imports are allowed:
// With default config (allowedImports: ['react'])
import React from 'react'           // ✅ Allowed
import { useState } from 'react'    // ✅ Allowed
import Chart from 'recharts'        // ❌ Blocked

// With custom config
const compiler = new VibeCompiler({
  allowedImports: ['react', 'recharts']
})

import { BarChart } from 'recharts' // ✅ Now allowed

Size Limits

const compiler = new VibeCompiler({
  maxCodeSize: 50_000  // 50KB limit
})

// Large code will be rejected
await compiler.compile(hugeCodeString)
// Error: Code size (150000 bytes) exceeds maximum (50000 bytes)

Compiled Output

interface CompiledComponent {
  // Original source code
  code: string

  // Hash for caching
  hash: string

  // Render function (in full implementation)
  render(data?: Record<string, unknown>): ReactNode
}

Pre-validation Pattern

Validate before saving to database:
import { VibeCompiler, createVibeApi } from 'genkit'

const compiler = new VibeCompiler()
const vibe = createVibeApi({ storage })

async function saveComponent(input: { userId: string; name: string; code: string }) {
  // Validate first
  try {
    await compiler.compile(input.code)
  } catch (error) {
    throw new Error(`Invalid code: ${error.message}`)
  }

  // Save if valid
  return vibe.save(input)
}

API Integration

// app/api/vibe/validate/route.ts
import { VibeCompiler } from 'genkit'

const compiler = new VibeCompiler({
  allowedImports: ['react', 'recharts', 'lucide-react']
})

export async function POST(request: Request) {
  const { code } = await request.json()

  try {
    const compiled = await compiler.compile(code)
    return Response.json({
      valid: true,
      hash: compiled.hash
    })
  } catch (error) {
    return Response.json({
      valid: false,
      error: error.message
    }, { status: 400 })
  }
}

Caching

Compiled results are cached by code hash:
const compiler = new VibeCompiler({ cache: true })

// First compile - validates code
await compiler.compile(code)  // ~10ms

// Second compile - returns cached result
await compiler.compile(code)  // ~0.1ms

// Clear cache if needed
compiler.clearCache()

Custom Validation

Extend the compiler for additional checks:
class CustomCompiler extends VibeCompiler {
  async compile(code: string) {
    // Run base validation
    const result = await super.compile(code)

    // Add custom checks
    if (code.includes('alert(')) {
      throw new Error('alert() is not allowed')
    }

    if (code.length < 50) {
      throw new Error('Code is too short to be valid')
    }

    return result
  }
}

Error Messages

The compiler provides detailed error messages:
try {
  await compiler.compile(code)
} catch (error) {
  // Possible error messages:
  // "Code size (150000 bytes) exceeds maximum allowed (100000 bytes)"
  // "Code contains potentially dangerous pattern: eval\\s*\\("
  // "Import not allowed: axios"
}

Best Practices

1. Validate on Both Client and Server

// Client-side (quick feedback)
const clientCompiler = new VibeCompiler()
try {
  await clientCompiler.compile(code)
  setError(null)
} catch (e) {
  setError(e.message)
  return
}

// Server-side (authoritative)
const response = await fetch('/api/vibe/validate', {
  method: 'POST',
  body: JSON.stringify({ code })
})

2. Configure Imports for Your Use Case

// Dashboard app - allow charting libraries
const dashboardCompiler = new VibeCompiler({
  allowedImports: ['react', 'recharts', 'd3', 'date-fns']
})

// Form builder - allow form libraries
const formCompiler = new VibeCompiler({
  allowedImports: ['react', 'react-hook-form', 'zod']
})

3. Set Reasonable Size Limits

// Widgets should be small
const widgetCompiler = new VibeCompiler({
  maxCodeSize: 20_000  // 20KB
})

// Full pages can be larger
const pageCompiler = new VibeCompiler({
  maxCodeSize: 100_000  // 100KB
})