91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
/**
|
|
* 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<In = unknown, Out = unknown> {
|
|
id: string;
|
|
/** Execute the transform on the server */
|
|
execute(input: In, context: TransformContext): Promise<Out>;
|
|
}
|
|
|
|
export interface TransformContext {
|
|
space: string;
|
|
/** Auth claims from EncryptID JWT (null if unauthenticated) */
|
|
claims: Record<string, unknown> | null;
|
|
}
|
|
|
|
// ============================================================================
|
|
// REGISTRY
|
|
// ============================================================================
|
|
|
|
const transforms = new Map<string, ServerTransform<any, any>>();
|
|
|
|
export function registerServerTransform<In, Out>(transform: ServerTransform<In, Out>): 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;
|
|
}
|