/** * Cloudflare-specific types for sandbox runner */ import type { D1Database, R2Bucket } from "@cloudflare/workers-types"; /** * Environment bindings required for sandbox runner. * These must be configured in wrangler.jsonc. */ export interface CloudflareSandboxEnv { /** Worker Loader binding for spawning plugin isolates */ LOADER?: WorkerLoader; /** D1 database for plugin storage and bridge operations */ DB: D1Database; /** R2 bucket for plugin code storage (optional if loading from config) */ PLUGINS?: R2Bucket; } /** * Worker Loader binding type. * This is the API provided by Cloudflare's Worker Loader feature. */ export interface WorkerLoader { /** * Get or create a dynamic worker instance. * * @param name - Unique identifier for this worker instance * @param config - Configuration function returning worker setup * @returns A stub to interact with the dynamic worker */ get(name: string, config: () => WorkerLoaderConfig | Promise): WorkerStub; } /** * Configuration for a dynamically loaded worker. */ export interface WorkerLoaderConfig { /** Compatibility date for the worker */ compatibilityDate?: string; /** Name of the main module (must be in modules) */ mainModule: string; /** Map of module names to their code */ modules: Record; /** Environment bindings to pass to the worker */ env?: Record; /** * Outbound fetch handler. * Set to null to block all network access. * Set to a service binding to intercept/proxy requests. */ globalOutbound?: null | object; /** * Resource limits enforced at the V8 isolate level. * Analogous to Workers for Platforms custom limits. */ limits?: WorkerLoaderLimits; } /** * Resource limits for a dynamically loaded worker. * Enforced by the Worker Loader runtime at the V8 isolate level. */ export interface WorkerLoaderLimits { /** Maximum CPU time in milliseconds per invocation */ cpuMs?: number; /** Maximum number of subrequests (fetch/service-binding calls) per invocation */ subRequests?: number; } /** * Stub returned by Worker Loader for interacting with dynamic workers. */ export interface WorkerStub { /** * Get the default entrypoint (fetch handler). */ fetch(request: Request): Promise; /** * Get a named entrypoint class instance for RPC. */ getEntrypoint(name?: string): T; } /** * Plugin manifest - loaded from manifest.json in plugin bundle. */ export interface LoadedPluginManifest { id: string; version: string; capabilities: string[]; allowedHosts: string[]; storage: Record }>; hooks: string[]; routes: string[]; } /** * Content item shape returned by bridge content operations. * Matches core's ContentItem from plugins/types.ts. */ interface BridgeContentItem { id: string; type: string; data: Record; createdAt: string; updatedAt: string; } /** * Media item shape returned by bridge media operations. * Matches core's MediaItem from plugins/types.ts. */ interface BridgeMediaItem { id: string; filename: string; mimeType: string; size: number | null; url: string; createdAt: string; } /** * Type for the PluginBridge binding passed to sandboxed workers. * This is the RPC interface exposed by PluginBridge WorkerEntrypoint. */ export interface PluginBridgeBinding { // KV kvGet(key: string): Promise; kvSet(key: string, value: unknown): Promise; kvDelete(key: string): Promise; kvList(prefix?: string): Promise>; // Storage storageGet(collection: string, id: string): Promise; storagePut(collection: string, id: string, data: unknown): Promise; storageDelete(collection: string, id: string): Promise; storageQuery( collection: string, opts?: { limit?: number; cursor?: string }, ): Promise<{ items: Array<{ id: string; data: unknown }>; hasMore: boolean; cursor?: string }>; storageCount(collection: string): Promise; storageGetMany(collection: string, ids: string[]): Promise>; storagePutMany(collection: string, items: Array<{ id: string; data: unknown }>): Promise; storageDeleteMany(collection: string, ids: string[]): Promise; // Content contentGet(collection: string, id: string): Promise; contentList( collection: string, opts?: { limit?: number; cursor?: string }, ): Promise<{ items: BridgeContentItem[]; cursor?: string; hasMore: boolean }>; contentCreate(collection: string, data: Record): Promise; contentUpdate( collection: string, id: string, data: Record, ): Promise; contentDelete(collection: string, id: string): Promise; // Media mediaGet(id: string): Promise; mediaList(opts?: { limit?: number; cursor?: string; mimeType?: string; }): Promise<{ items: BridgeMediaItem[]; cursor?: string; hasMore: boolean }>; mediaUpload( filename: string, contentType: string, bytes: ArrayBuffer, ): Promise<{ mediaId: string; storageKey: string; url: string }>; mediaDelete(id: string): Promise; // Network httpFetch( url: string, init?: RequestInit, ): Promise<{ status: number; headers: Record; text: string }>; // Email emailSend(message: { to: string; subject: string; text: string; html?: string }): Promise; // Logging log(level: "debug" | "info" | "warn" | "error", msg: string, data?: unknown): void; }