'use client'; import React, { useState, useEffect } from 'react'; import MonacoEditor from '@monaco-editor/react'; import { EdgeFunction } 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Loader2, AlertCircle, Info } from 'lucide-react'; import { useTheme } from 'next-themes'; interface FunctionEditorProps { deploymentId: string; function: EdgeFunction | null; isOpen: boolean; onClose: () => void; onSave: (data: Partial) => Promise; } const DEFAULT_CODE = `// Access the request object // request.method - HTTP method // request.body - Parsed request body // request.query - Query string parameters // request.headers - Request headers // Use the database // db.query(sql, params) - Execute SELECT query // db.run(sql, params) - Execute INSERT/UPDATE/DELETE // db.all(sql, params) - Alias for query // Return a response // Response.json(data, status) - Return JSON // Response.text(text, status) - Return text // Response.error(message, status) - Return error // Example: List items const items = db.all('SELECT * FROM items LIMIT 10'); Response.json({ items }); `; export function FunctionEditor({ deploymentId, function: fn, isOpen, onClose, onSave, }: FunctionEditorProps) { const [name, setName] = useState(fn?.name || ''); const [description, setDescription] = useState(fn?.description || ''); const [method, setMethod] = useState(fn?.method || 'ANY'); const [code, setCode] = useState(fn?.code || DEFAULT_CODE); const [timeoutMs, setTimeoutMs] = useState(fn?.timeoutMs || 5000); 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 || ''); setMethod(fn?.method || 'ANY'); setCode(fn?.code || DEFAULT_CODE); setTimeoutMs(fn?.timeoutMs || 5000); setError(null); } }, [fn, isOpen]); const handleSave = async () => { setError(null); // Basic validation if (!name.trim()) { setError('Function name is required'); return; } if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(name)) { setError('Name must be lowercase letters, numbers, and hyphens only'); return; } if (!code.trim()) { setError('Function code is required'); return; } setSaving(true); try { await onSave({ name: name.trim(), description: description.trim() || undefined, method, code, timeoutMs, enabled: fn?.enabled ?? true, }); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to save function'); } finally { setSaving(false); } }; if (!mounted) return null; return ( {fn ? 'Edit Function' : 'Create Function'} Define an HTTP endpoint that can access your deployment database.
{/* Name & Method */}
setName(e.target.value.toLowerCase())} placeholder="my-function" disabled={!!fn} /> {deploymentId && (

URL: /api/deployments/{deploymentId}/functions/{name || 'name'}

)}
{/* Description */}
setDescription(e.target.value)} placeholder="What does this function do?" />
{/* Timeout */}
setTimeoutMs(Math.min(30, Math.max(1, parseInt(e.target.value) || 5)) * 1000)} className="w-24" /> 1-30 seconds
{/* Code Editor */}
setCode(value || '')} options={{ minimap: { enabled: false }, fontSize: 13, scrollBeyondLastLine: false, automaticLayout: true, tabSize: 2, }} />
{/* API Reference */}
Available APIs
request.method, .body, .query, .headers, .params, .path
db.query(sql, params), .run(sql, params), .all(sql, params)
Response.json(data, status), .text(text, status), .error(msg, status)
fetch(url, options) - External HTTP requests
{/* Error */} {error && (
{error}
)}
{/* Footer */}
); }