/** * Layer 5: Compute (Server) — Server-side transform runner. * * Handles POST /:space/api/compute/:transformId requests from clients * that can't run a transform locally (e.g. PDF generation, image thumbnailing). * * Usage in server/index.ts: * import { createComputeRouter, registerServerTransform } from './local-first/compute-server'; * const computeRouter = createComputeRouter(); * app.route('/:space/api/compute', computeRouter); */ import { Hono } from 'hono'; // ============================================================================ // TYPES // ============================================================================ export interface ServerTransform { id: string; /** Execute the transform on the server */ execute(input: In, context: TransformContext): Promise; } export interface TransformContext { space: string; /** Auth claims from EncryptID JWT (null if unauthenticated) */ claims: Record | null; } // ============================================================================ // REGISTRY // ============================================================================ const transforms = new Map>(); export function registerServerTransform(transform: ServerTransform): void { transforms.set(transform.id, transform); } export function getServerTransform(id: string): ServerTransform | undefined { return transforms.get(id); } // ============================================================================ // HONO ROUTER // ============================================================================ /** * Create a Hono router that handles compute requests. * Mount at /:space/api/compute */ export function createComputeRouter(): Hono { const router = new Hono(); // List available transforms router.get('/', (c) => { const list = Array.from(transforms.keys()); return c.json({ transforms: list }); }); // Execute a transform router.post('/:transformId', async (c) => { const transformId = c.req.param('transformId'); const transform = transforms.get(transformId); if (!transform) { return c.json({ error: `Transform "${transformId}" not found` }, 404); } try { const body = await c.req.json(); const input = body.input; const context: TransformContext = { space: c.req.param('space') || 'demo', claims: (c as any).get?.('claims') ?? null, }; const output = await transform.execute(input, context); return c.json({ output }); } catch (e) { const message = e instanceof Error ? e.message : 'Transform execution failed'; console.error(`[ComputeServer] Transform "${transformId}" failed:`, e); return c.json({ error: message }, 500); } }); return router; }