'use client'; import React, { useState, useEffect } from 'react'; import MonacoEditor from '@monaco-editor/react'; import { ServerFunction } from '@/lib/vfs/types'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Loader2, AlertCircle, Info } from 'lucide-react'; import { useTheme } from 'next-themes'; interface ServerFunctionEditorProps { function: ServerFunction | null; isOpen: boolean; onClose: () => void; onSave: (data: Partial) => Promise; } const DEFAULT_CODE = `// Server functions receive arguments via the 'args' array // and have access to 'db' and 'fetch' // Example: Validate an API key const [apiKey] = args; if (!apiKey) { return { valid: false, error: 'No API key provided' }; } const users = db.query( 'SELECT id, name FROM users WHERE api_key = ?', [apiKey] ); if (users.length === 0) { return { valid: false, error: 'Invalid API key' }; } return { valid: true, user: users[0] }; `; export function ServerFunctionEditor({ function: fn, isOpen, onClose, onSave, }: ServerFunctionEditorProps) { const [name, setName] = useState(fn?.name || ''); const [description, setDescription] = useState(fn?.description || ''); const [code, setCode] = useState(fn?.code || DEFAULT_CODE); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const { resolvedTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); useEffect(() => { if (isOpen) { setName(fn?.name || ''); setDescription(fn?.description || ''); setCode(fn?.code || DEFAULT_CODE); setError(null); } }, [fn, isOpen]); const handleSave = async () => { setError(null); // Basic validation if (!name.trim()) { setError('Function name is required'); return; } // Validate JS identifier (camelCase or snake_case) if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) { setError('Name must be a valid identifier (letters, numbers, underscores; cannot start with number)'); return; } // Check reserved names const reserved = ['db', 'fetch', 'console', 'args', 'request', 'Response', 'server']; if (reserved.includes(name)) { setError(`"${name}" is reserved and cannot be used`); return; } if (!code.trim()) { setError('Function code is required'); return; } setSaving(true); try { await onSave({ name: name.trim(), description: description.trim() || undefined, code, enabled: fn?.enabled ?? true, }); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save server function'); } finally { setSaving(false); } }; if (!mounted) return null; return ( {fn ? 'Edit Server Function' : 'Create Server Function'} Define a reusable helper function that edge functions can call via server.{name || 'name'}(args).
{/* Name */}
setName(e.target.value.replace(/[^a-zA-Z0-9_]/g, ''))} placeholder="validateAuth" disabled={!!fn} />

Usage in edge functions: server.{name || 'name'}(arg1, arg2, ...)

{/* Description */}
setDescription(e.target.value)} placeholder="What does this helper do?" />
{/* Code Editor */}
setCode(value || '')} options={{ minimap: { enabled: false }, fontSize: 13, scrollBeyondLastLine: false, automaticLayout: true, tabSize: 2, }} />
{/* API Reference */}
Available in Server Functions
args - Array of arguments passed from edge function
db.query(sql, params), .run(sql, params), .all(sql, params)
fetch(url, options) - External HTTP requests
console.log(), .error(), .warn() - Logging

Note: Return a value to send data back to the calling edge function. Server functions are synchronous and share the timeout with the parent edge function.

{/* Example */}
Example: Using in Edge Function
{`// Edge function code
const auth = server.${name || 'validateAuth'}(request.headers['x-api-key']);
if (!auth.valid) {
  Response.error(auth.error, 401);
  return;
}

// User is authenticated
const products = db.query('SELECT * FROM products WHERE user_id = ?', [auth.user.id]);
Response.json({ products });`}
            
{/* Error */} {error && (
{error}
)}
{/* Footer */}
); }