UNPKG

@layercode/node-server-sdk

Version:

Layercode Node.js Server Side SDK

77 lines (73 loc) 2.85 kB
import { createHmac } from 'crypto'; /** * Creates a server-sent events (SSE) stream response for the Layercode pipeline. * @param requestBody - The full request body (parsed JSON) from the webhook route. * @param handler - Async function that receives helpers for writing to the stream. * @returns Response object */ function streamResponse(requestBody, handler) { let streamController; const encoder = new TextEncoder(); const body = requestBody || {}; // Helper to format and enqueue SSE events, merging turn_id if present function sendEvent(eventType, content) { const merged = { ...content }; if (body.turn_id) merged.turn_id = body.turn_id; const sse = `data: ${JSON.stringify({ type: eventType, ...merged })}\n\n`; streamController.enqueue(encoder.encode(sse)); } // Write helpers const stream = { tts: (content) => sendEvent('response.tts', { content }), ttsTextStream: async (textStream) => { for await (const chunk of textStream) { stream.tts(chunk); } }, data: (content) => sendEvent('response.data', { content }), // other: (type: string, payload: any) => sendEvent(type, payload), end: () => { sendEvent('response.end', {}); streamController.close(); }, }; const readable = new ReadableStream({ async start(controller) { streamController = controller; try { await handler({ stream }); } catch (err) { sendEvent('response.error', { error: err.message }); controller.close(); } }, }); return new Response(readable, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache, no-transform', Connection: 'keep-alive', 'Content-Encoding': 'none', }, }); } // timestampTolerance is the number of seconds that the timestamp can be off by. Default is 300 seconds (5 minutes). function verifySignature({ payload, signature, secret, timestampTolerance = 300 }) { // signature is expected to be in the format: t=timestamp,v1=signature const match = signature.match(/t=(\d+),v1=([a-fA-F0-9]+)/); if (!match) return false; const timestamp = parseInt(match[1], 10); const v1 = match[2]; const now = Math.floor(Date.now() / 1000); // current time in seconds if (Math.abs(now - timestamp) > timestampTolerance) { return false; } const signedPayload = `${timestamp}.${payload}`; const expected = createHmac('sha256', secret).update(signedPayload).digest('hex'); return expected === v1; } export { streamResponse, verifySignature }; //# sourceMappingURL=index.js.map