import type { AppRouteHandler } from "@/lib/types.ts"; import type * as Routes from "./memory.routes.ts"; import { Memory, memory } from "../memory.ts"; import * as Codec from "@commontools/memory/codec"; import { createSpan } from "@/middlewares/opentelemetry.ts"; export const transact: AppRouteHandler = async (c) => { return await createSpan("memory.transact", async (span) => { try { const ucan = (await c.req.valid("json")) as Memory.UCAN< Memory.ConsumerInvocationFor<"/memory/transact", Memory.Protocol> >; span.setAttribute("memory.operation", "transact"); const result = await createSpan("memory.invoke", async (invokeSpan) => { invokeSpan.setAttribute("memory.operation_type", "transact"); return await memory.invoke(ucan); }); if (result.ok) { span.setAttribute("memory.status", "success"); return c.json(result, 200); } else { // This is ugly but without this TS inference is failing to infer that // types are correct const { error } = result; span.setAttribute("memory.status", "error"); span.setAttribute("memory.error_type", error.name); if (error.name === "ConflictError") { return c.json({ error }, 409); } else if (error.name === "AuthorizationError") { return c.json({ error }, 401); } else { return c.json({ error }, 503); } } } catch (cause) { const { message, stack, name } = (cause ?? new Error()) as Error; span.setAttribute("memory.status", "exception"); span.setAttribute("error.message", message); span.setAttribute("error.type", name); return c.json({ error: { message, name, stack } }, 500); } }); }; export const query: AppRouteHandler = async (c) => { return await createSpan("memory.query", async (span) => { try { const ucan = (await c.req.valid("json")) as Memory.UCAN< Memory.ConsumerInvocationFor<"/memory/query", Memory.Protocol> >; span.setAttribute("memory.operation", "query"); const result = await createSpan("memory.invoke", async (invokeSpan) => { invokeSpan.setAttribute("memory.operation_type", "query"); return await memory.invoke(ucan); }); if (result.ok) { span.setAttribute("memory.status", "success"); return c.json({ ok: result.ok }, 200); } else { span.setAttribute("memory.status", "error"); span.setAttribute("memory.error_type", result.error.name); if (result.error.name === "AuthorizationError") { return c.json({ error: result.error }, 401); } else { return c.json({ error: result.error }, 503); } } } catch (cause) { const { message, stack, name } = (cause ?? new Error()) as Error; span.setAttribute("memory.status", "exception"); span.setAttribute("error.message", message); span.setAttribute("error.type", name); return c.json({ error: { message, name, stack } }, 500); } }); }; export const subscribe: AppRouteHandler = (c) => { return createSpan("memory.subscribe", (span) => { try { span.setAttribute("memory.operation", "subscribe"); const { socket, response } = Deno.upgradeWebSocket(c.req.raw); span.setAttribute("websocket.upgrade", "success"); createSpan("memory.socket.setup", (setupSpan) => { const { readable, writable } = Memory.Socket.from( socket, ); setupSpan.setAttribute("socket.setup", "complete"); // We can't await the pipeline in a WebSocket handler, // so we just record that we've set it up readable .pipeThrough(Codec.UCAN.fromStringStream()) .pipeThrough(memory.session()) .pipeThrough(Codec.Receipt.toStringStream()) .pipeTo(writable); }); return response; } catch (error) { span.setAttribute("memory.status", "exception"); span.setAttribute( "error.message", error instanceof Error ? error.message : String(error), ); span.setAttribute( "error.type", error instanceof Error ? error.name : "UnknownError", ); throw error; } }); };