Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix lith awslambda shim — streaming API endpoints (ask, keep-mint, etc.) were broken

The shim used `streamifyResponse: (fn) => fn` which returned the Netlify
stream() wrapper as-is. That wrapper expects 3 args (event, responseStream,
context) but lith calls handlers with 2 args (event, context), so pipeline()
crashed trying to pipe AI responses to the context object. Now provides a
proper PassThrough stream and converts to Web ReadableStream for lith.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+36 -4
+36 -4
lith/server.mjs
··· 3 3 4 4 // Shim awslambda before anything imports @netlify/functions. 5 5 // Netlify's stream() calls awslambda.streamifyResponse() at wrap time, 6 - // which doesn't exist outside AWS Lambda. This shim makes the wrapped 7 - // handler just call the original function and return its result. 6 + // which doesn't exist outside AWS Lambda. This shim adapts the 3-arg 7 + // streaming function (event, responseStream, context) back to a normal 8 + // 2-arg handler (event, context) that returns {statusCode, headers, body}. 9 + import { PassThrough } from "stream"; 10 + import { Readable } from "stream"; 11 + 8 12 if (typeof globalThis.awslambda === "undefined") { 9 13 globalThis.awslambda = { 10 - streamifyResponse: (fn) => fn, 14 + streamifyResponse: (wrappedFn) => { 15 + // wrappedFn expects (event, responseStream, context). 16 + // It calls the real handler(event, context) internally, then pipes 17 + // the body to responseStream via pipeline(). We provide a PassThrough 18 + // as the responseStream and return it as the response body. 19 + return async (event, context) => { 20 + const pt = new PassThrough(); 21 + 22 + // Promise that resolves when HttpResponseStream.from() is called 23 + // inside wrappedFn, giving us the response metadata (statusCode, headers). 24 + let resolveMetadata; 25 + const metadataPromise = new Promise((r) => { resolveMetadata = r; }); 26 + pt._resolveMetadata = resolveMetadata; 27 + 28 + // Start the pipeline (don't await — data streams to pt asynchronously) 29 + wrappedFn(event, pt, context).catch((err) => { 30 + if (!pt.destroyed) pt.destroy(err); 31 + }); 32 + 33 + const metadata = await metadataPromise; 34 + const webStream = Readable.toWeb(pt); 35 + 36 + return { ...metadata, body: webStream }; 37 + }; 38 + }, 11 39 HttpResponseStream: { 12 - from: (stream, _metadata) => stream, 40 + from: (stream, metadata) => { 41 + // Signal metadata to the adapter above 42 + if (stream._resolveMetadata) stream._resolveMetadata(metadata || {}); 43 + return stream; 44 + }, 13 45 }, 14 46 }; 15 47 }