@tanstack/router-core
Version:
Modern and scalable routing for React applications
1 lines • 37 kB
Source Map (JSON)
{"version":3,"file":"transformStreamWithRouter.cjs","names":[],"sources":["../../../src/ssr/transformStreamWithRouter.ts"],"sourcesContent":["import { ReadableStream } from 'node:stream/web'\nimport { Readable } from 'node:stream'\nimport { TSR_SCRIPT_BARRIER_ID } from './constants'\nimport type { AnyRouter } from '../router'\n\nexport type TransformStreamWithRouterOptions = {\n /** Timeout for serialization to complete after app render finishes (default: 60000ms) */\n timeoutMs?: number\n /** Maximum lifetime of the stream transform (default: 120000ms). Safety net for cleanup. */\n lifetimeMs?: number\n /**\n * Called exactly once when the stream is torn down due to abort/error/\n * cancel/timeout — NOT on natural successful completion. Use this to\n * abort a hidden producer upstream of any PassThrough you passed in\n * (e.g. React `renderToPipeableStream`'s `abort()`).\n * Errors thrown from this callback are swallowed.\n */\n onAbort?: (reason?: unknown) => void\n}\n\nexport function transformReadableStreamWithRouter(\n router: AnyRouter,\n routerStream: ReadableStream,\n opts?: TransformStreamWithRouterOptions,\n) {\n return transformStreamWithRouter(router, routerStream, opts)\n}\n\nexport function transformPipeableStreamWithRouter(\n router: AnyRouter,\n routerStream: Readable,\n opts?: TransformStreamWithRouterOptions,\n) {\n return Readable.fromWeb(\n transformStreamWithRouter(router, Readable.toWeb(routerStream), opts),\n )\n}\n\n// Minimum length of a valid closing tag: </a> = 4 characters\nconst MIN_CLOSING_TAG_LENGTH = 4\n\n// Default timeout values (in milliseconds)\nconst DEFAULT_SERIALIZATION_TIMEOUT_MS = 60000\nconst DEFAULT_LIFETIME_TIMEOUT_MS = DEFAULT_SERIALIZATION_TIMEOUT_MS * 2\nconst MAX_LEFTOVER_CHARS = 2048\nconst MAX_TAIL_CHARS = 64 * 1024\nconst MAX_ROUTER_HTML_CHARS = 16 * 1024 * 1024\nconst MAX_PENDING_WRITE_CHARS = 16 * 1024 * 1024\n\n// Merge lifecycle: body bytes can stream, router HTML must precede tail,\n// terminal states own close/error/cleanup exactly once.\nconst MergeState = {\n ReadingBody: 0,\n HoldingTail: 1,\n AppDone: 2,\n Draining: 3,\n Done: 4,\n} as const\n\ntype MergeState = (typeof MergeState)[keyof typeof MergeState]\n\n// Module-level encoder (stateless, safe to reuse)\nconst textEncoder = new TextEncoder()\n\nconst noop = () => {}\nconst resolvedPromise = Promise.resolve()\n\n// Returns -bodyEndIndex - 2 when </body> is found; otherwise returns\n// the position after the last valid closing tag, or -1 when none exists.\nfunction findHtmlBoundary(str: string): number {\n let lastClosingTagEnd = -1\n let searchFrom = str.length - MIN_CLOSING_TAG_LENGTH\n\n while (searchFrom >= 0) {\n const openSlash = str.lastIndexOf('</', searchFrom)\n if (openSlash === -1) break\n\n // Fast case-insensitive match for </body>. Negative return encodes the\n // body start index without allocating a result object.\n if (\n (str.charCodeAt(openSlash + 2) | 32) === 98 &&\n (str.charCodeAt(openSlash + 3) | 32) === 111 &&\n (str.charCodeAt(openSlash + 4) | 32) === 100 &&\n (str.charCodeAt(openSlash + 5) | 32) === 121 &&\n str.charCodeAt(openSlash + 6) === 62\n ) {\n return -openSlash - 2\n }\n\n if (lastClosingTagEnd === -1) {\n let i = openSlash + 2\n const startCode = str.charCodeAt(i)\n if (\n (startCode >= 97 && startCode <= 122) ||\n (startCode >= 65 && startCode <= 90)\n ) {\n i++\n while (i < str.length) {\n const code = str.charCodeAt(i)\n if (\n (code >= 97 && code <= 122) || // a-z\n (code >= 65 && code <= 90) || // A-Z\n (code >= 48 && code <= 57) || // 0-9\n code === 95 || // _\n code === 58 || // :\n code === 46 || // .\n code === 45 // -\n ) {\n i++\n } else {\n break\n }\n }\n\n if (str.charCodeAt(i) === 62) {\n lastClosingTagEnd = i + 1\n }\n }\n }\n\n searchFrom = openSlash - 1\n }\n\n return lastClosingTagEnd\n}\n\n/**\n * Releasing the lock can throw if a pending read is still settling or if the\n * lock was already released.\n */\ntype ReaderOps = {\n cancel: (reason?: unknown) => Promise<unknown>\n releaseLock: () => void\n}\n\nfunction safeReleaseReader(reader: ReaderOps) {\n try {\n reader.releaseLock()\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Cancel a reader without producing an unhandled rejection. `reader.cancel()`\n * can reject (e.g. when the underlying source's cancel() throws), and\n * downstream cancel() should still wait for upstream teardown when possible.\n */\nfunction safeCancelReader(reader: ReaderOps, reason?: unknown): Promise<void> {\n let cancelPromise: Promise<unknown> | undefined\n try {\n cancelPromise = reader.cancel(reason)\n } catch {\n // ignore\n }\n\n if (!safeReleaseReader(reader) && cancelPromise) {\n return cancelPromise.then(noop, noop).then(() => {\n safeReleaseReader(reader)\n })\n }\n\n return cancelPromise ? cancelPromise.then(noop, noop) : resolvedPromise\n}\n\nfunction createReaderState<T>(appStream: ReadableStream<T>) {\n const reader = appStream.getReader()\n let released = false\n\n return {\n reader,\n cancel: (reason?: unknown) => {\n if (released) return resolvedPromise\n released = true\n return safeCancelReader(reader, reason)\n },\n release: () => {\n if (released) return\n released = true\n safeReleaseReader(reader)\n },\n }\n}\n\nfunction createAbortNotifier(opts?: TransformStreamWithRouterOptions) {\n let abortNotified = false\n return (reason?: unknown) => {\n if (abortNotified) return\n abortNotified = true\n try {\n opts?.onAbort?.(reason)\n } catch {\n // swallow user errors\n }\n }\n}\n\nexport function transformStreamWithRouter(\n router: AnyRouter,\n appStream: ReadableStream,\n opts?: TransformStreamWithRouterOptions,\n) {\n const serverSsr = router.serverSsr\n if (!serverSsr) {\n throw new Error('Invariant failed: router.serverSsr is required')\n }\n if (serverSsr.reserveStreamFastPath()) {\n return makeFastPathStream(appStream, opts, serverSsr)\n }\n\n return makeMainStream(serverSsr, appStream, opts)\n}\n\n// =====================================================================\n// Fast path: passthrough with cleanup + backpressure on app reads.\n// =====================================================================\nfunction makeFastPathStream(\n appStream: ReadableStream<Uint8Array>,\n opts?: TransformStreamWithRouterOptions,\n serverSsr?: NonNullable<AnyRouter['serverSsr']>,\n) {\n let cleanedUp = false\n let controller: ReadableStreamDefaultController<Uint8Array> | undefined\n let state: MergeState = MergeState.ReadingBody\n let lifetimeTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n let stopListeningToInjectedHtml: (() => void) | undefined\n const readerState = createReaderState(appStream)\n const notifyAbort = createAbortNotifier(opts)\n const isDone = () => state === MergeState.Done\n let renderFinished = false\n\n const finishSsrRendering = () => {\n if (!serverSsr || renderFinished) return true\n renderFinished = true\n try {\n serverSsr.setRenderFinished()\n return true\n } catch (error) {\n safeError(error)\n cleanup(error)\n return false\n }\n }\n\n const cleanup = (reason?: unknown, cancelReader = true) => {\n if (cleanedUp) return resolvedPromise\n cleanedUp = true\n\n if (lifetimeTimeoutHandle !== undefined) {\n clearTimeout(lifetimeTimeoutHandle)\n lifetimeTimeoutHandle = undefined\n }\n try {\n stopListeningToInjectedHtml?.()\n } catch {\n // ignore\n }\n stopListeningToInjectedHtml = undefined\n\n if (cancelReader) {\n // Notify the producer immediately. Reader cancellation may take time to\n // settle, and upstream renderers must tolerate abort + cancel overlap.\n notifyAbort(reason)\n }\n const readerDone = cancelReader\n ? readerState.cancel(reason)\n : (readerState.release(), resolvedPromise)\n if (serverSsr) {\n try {\n serverSsr.cleanup()\n } catch (error) {\n console.error('Error in SSR cleanup:', error)\n }\n }\n return readerDone\n }\n\n const safeClose = () => {\n if (isDone()) return\n state = MergeState.Done\n try {\n controller?.close()\n } catch {\n // ignore\n }\n }\n\n const safeError = (error: unknown) => {\n if (isDone()) return\n state = MergeState.Done\n try {\n controller?.error(error)\n } catch {\n // ignore\n }\n }\n\n if (serverSsr) {\n stopListeningToInjectedHtml = serverSsr.onInjectedHtml(() => {\n const err = new Error('SSR router HTML injected during fast path')\n safeError(err)\n cleanup(err)\n })\n }\n\n const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS\n lifetimeTimeoutHandle = setTimeout(() => {\n if (!cleanedUp && !isDone()) {\n const err = new Error('Stream lifetime exceeded')\n console.warn(\n `SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`,\n )\n safeError(err)\n cleanup(err)\n }\n }, lifetimeMs)\n\n const stream = new ReadableStream<Uint8Array>({\n start(c) {\n controller = c\n },\n async pull(c) {\n if (cleanedUp || isDone()) return\n try {\n const { done, value } = await readerState.reader.read()\n if (!done) {\n if (!cleanedUp && !isDone()) {\n c.enqueue(value)\n }\n return\n }\n\n if (cleanedUp || isDone()) return\n\n if (!finishSsrRendering()) return\n safeClose()\n return cleanup(undefined, false)\n } catch (error) {\n if (cleanedUp) return\n console.error('Error reading appStream:', error)\n if (state < MergeState.AppDone) {\n try {\n serverSsr?.setRenderFinished()\n } catch {\n // ignore\n }\n }\n safeError(error)\n return cleanup(error)\n } finally {\n if (cleanedUp || isDone()) {\n readerState.release()\n }\n }\n },\n cancel(reason) {\n state = MergeState.Done\n return cleanup(reason)\n },\n })\n\n return stream\n}\n\n// =====================================================================\n// Main path: scan + inject router HTML/scripts with full backpressure.\n//\n// ALL output (app chunks AND router-injected HTML/scripts) flows through a\n// single pendingWrites queue and is only enqueued onto the downstream\n// controller when desiredSize > 0. This prevents native-memory growth of\n// queued Uint8Arrays under slow HTTP consumers.\n// =====================================================================\nfunction makeMainStream(\n serverSsr: NonNullable<AnyRouter['serverSsr']>,\n appStream: ReadableStream,\n opts?: TransformStreamWithRouterOptions,\n) {\n let stopListeningToInjectedHtml: (() => void) | undefined\n let stopListeningToSerializationFinished: (() => void) | undefined\n let serializationTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n let lifetimeTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n let cleanedUp = false\n\n let controller: ReadableStreamDefaultController<Uint8Array> | undefined\n let closeWhenDrained = false\n let state: MergeState = MergeState.ReadingBody\n\n const readerState = createReaderState(appStream)\n const notifyAbort = createAbortNotifier(opts)\n\n // Single output queue: app chunks + router-injected HTML/scripts.\n // Stored as STRINGS to avoid holding native-backed Uint8Arrays in our queue\n // while waiting for downstream capacity. Encoding happens at enqueue time\n // (drainPending) so the bytes live only inside the controller's internal\n // queue, not in two places.\n //\n // Uses an index pointer instead of Array.prototype.shift() (which is O(n))\n // so many small router-injected script chunks stay O(1) per chunk.\n const pendingWrites: Array<string> = []\n let pendingWriteHead = 0\n let pendingWriteChars = 0\n\n function clearPending() {\n pendingWrites.length = 0\n pendingWriteHead = 0\n pendingWriteChars = 0\n }\n\n // Backpressure: pull() resolves drainResolve to let the read loop advance.\n let drainResolve: (() => void) | null = null\n const waitForDrain = () =>\n new Promise<void>((r) => {\n drainResolve = r\n })\n const signalDrain = () => {\n if (drainResolve) {\n const r = drainResolve\n drainResolve = null\n r()\n }\n }\n\n const isDone = () => state === MergeState.Done\n\n function drainPending() {\n if (!controller || isDone()) return\n while (pendingWriteHead < pendingWrites.length) {\n const ds = controller.desiredSize\n if (ds !== null && ds <= 0) return\n const next = pendingWrites[pendingWriteHead]!\n // Release reference for GC; compact when fully drained.\n pendingWrites[pendingWriteHead] = ''\n pendingWriteHead++\n pendingWriteChars -= next.length\n try {\n controller.enqueue(textEncoder.encode(next))\n } catch (error) {\n safeError(error)\n cleanup(error)\n return\n }\n }\n // Fully drained: reset array so it doesn't grow unbounded across SSR.\n if (pendingWriteHead >= pendingWrites.length) {\n pendingWrites.length = 0\n pendingWriteHead = 0\n }\n // If we've flushed everything and tryFinish requested close, close now.\n if (closeWhenDrained && pendingWriteHead >= pendingWrites.length) {\n closeWhenDrained = false\n safeClose()\n cleanup(undefined, false)\n }\n }\n\n /**\n * Enqueue a string chunk through the backpressure queue. Stored as a\n * string and encoded only when the downstream actually accepts the chunk\n * — keeps native-memory pressure inside the controller's queue (which\n * honors desiredSize) rather than ours.\n */\n function writeChunk(chunk: string) {\n if (cleanedUp || isDone()) return\n if (!chunk.length) return\n if (pendingWriteChars + chunk.length > MAX_PENDING_WRITE_CHARS) {\n const err = new Error('SSR stream pending output exceeded maximum buffer')\n safeError(err)\n cleanup(err)\n return\n }\n pendingWrites.push(chunk)\n pendingWriteChars += chunk.length\n drainPending()\n }\n\n function safeClose() {\n if (isDone()) return\n state = MergeState.Done\n try {\n controller?.close()\n } catch {\n // ignore\n }\n }\n\n function safeError(error: unknown) {\n if (isDone()) return\n state = MergeState.Done\n try {\n controller?.error(error)\n } catch {\n // ignore\n }\n }\n\n /**\n * Cleanup with guards; must be idempotent.\n */\n function cleanup(reason?: unknown, cancelReader = true) {\n if (cleanedUp) return resolvedPromise\n cleanedUp = true\n\n try {\n stopListeningToInjectedHtml?.()\n stopListeningToSerializationFinished?.()\n } catch {\n // ignore\n }\n stopListeningToInjectedHtml = undefined\n stopListeningToSerializationFinished = undefined\n\n if (serializationTimeoutHandle !== undefined) {\n clearTimeout(serializationTimeoutHandle)\n serializationTimeoutHandle = undefined\n }\n if (lifetimeTimeoutHandle !== undefined) {\n clearTimeout(lifetimeTimeoutHandle)\n lifetimeTimeoutHandle = undefined\n }\n\n clearPendingRouterHtml()\n leftover = ''\n pendingTail = ''\n clearPending()\n\n if (cancelReader) {\n // Notify the producer immediately. Reader cancellation may take time to\n // settle, and upstream renderers must tolerate abort + cancel overlap.\n notifyAbort(reason)\n }\n const readerDone = cancelReader\n ? readerState.cancel(reason)\n : (readerState.release(), resolvedPromise)\n signalDrain()\n try {\n serverSsr.cleanup()\n } catch (error) {\n console.error('Error in SSR cleanup:', error)\n }\n return readerDone\n }\n\n const textDecoder = new TextDecoder()\n\n // Router-injected scripts/HTML waiting for the next safe body boundary.\n // Keep chunks separate so flushing does not flatten a large rope string.\n const pendingRouterHtml: Array<string> = []\n let pendingRouterHtmlChars = 0\n\n // between-chunk text buffer; keep bounded to avoid unbounded memory\n let leftover = ''\n\n // captured bytes from </body> onward; must stay behind router scripts.\n let pendingTail = ''\n\n let streamBarrierLifted = false\n let streamBarrierMarkerSeen = false\n let serializationFinished = false\n\n function noteBarrierMarker(chunk: string) {\n if (streamBarrierMarkerSeen) return\n if (chunk.includes(TSR_SCRIPT_BARRIER_ID)) {\n streamBarrierMarkerSeen = true\n }\n }\n\n function liftBarrierAfterBoundary() {\n if (streamBarrierLifted) return\n if (!streamBarrierMarkerSeen) return\n streamBarrierLifted = true\n serverSsr.liftScriptBarrier()\n }\n\n const stream = new ReadableStream<Uint8Array>({\n start(c) {\n controller = c\n // If anything queued before start (shouldn't happen but be safe), drain.\n drainPending()\n },\n pull() {\n // Consumer has capacity; flush queue then unblock read loop.\n drainPending()\n signalDrain()\n },\n cancel(reason) {\n state = MergeState.Done\n return cleanup(reason)\n },\n })\n\n function drainRouterHtml() {\n if (cleanedUp || isDone()) return\n let html: string | undefined\n try {\n html = serverSsr.takeBufferedHtml()\n } catch (error) {\n safeError(error)\n cleanup(error)\n return\n }\n if (!html) return\n if (state >= MergeState.Draining) {\n // At this point final tail/close has already been queued. Emitting late\n // router HTML would put scripts after </body> or drop them silently.\n const err = new Error(\n 'SSR router HTML injected after stream finalization',\n )\n safeError(err)\n cleanup(err)\n return\n }\n if (state === MergeState.HoldingTail) {\n flushPendingRouterHtml()\n writeChunk(html)\n } else {\n if (pendingRouterHtmlChars + html.length > MAX_ROUTER_HTML_CHARS) {\n const err = new Error('SSR router HTML exceeded maximum buffer')\n safeError(err)\n cleanup(err)\n return\n }\n pendingRouterHtml.push(html)\n pendingRouterHtmlChars += html.length\n }\n }\n\n function flushPendingRouterHtml() {\n if (!pendingRouterHtml.length) return\n for (const html of pendingRouterHtml) {\n writeChunk(html)\n }\n clearPendingRouterHtml()\n }\n\n function clearPendingRouterHtml() {\n pendingRouterHtml.length = 0\n pendingRouterHtmlChars = 0\n }\n\n function appendTail(chunk: string) {\n pendingTail += chunk\n if (pendingTail.length > MAX_TAIL_CHARS) {\n throw new Error('SSR stream tail exceeded maximum buffer')\n }\n }\n\n function waitForBackpressure() {\n return !!(\n controller &&\n controller.desiredSize !== null &&\n controller.desiredSize <= 0\n )\n }\n\n function startSerializationTimeout() {\n if (cleanedUp || isDone()) return\n if (serializationTimeoutHandle !== undefined) return\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_SERIALIZATION_TIMEOUT_MS\n serializationTimeoutHandle = setTimeout(() => {\n if (!cleanedUp && !isDone()) {\n const err = new Error('Serialization timeout after app render finished')\n console.error('Serialization timeout after app render finished')\n safeError(err)\n cleanup(err)\n }\n }, timeoutMs)\n }\n\n /**\n * Finish only when app done and serialization complete. Queues final\n * output and requests close-when-drained so we don't close ahead of\n * pending writes still waiting on downstream capacity.\n */\n function tryFinish() {\n if (state !== MergeState.AppDone || !serializationFinished) return\n if (cleanedUp || isDone()) return\n\n if (serializationTimeoutHandle !== undefined) {\n clearTimeout(serializationTimeoutHandle)\n serializationTimeoutHandle = undefined\n }\n\n drainRouterHtml()\n if (cleanedUp || isDone()) return\n\n // Flush any remaining bytes in the TextDecoder\n const decoderRemainder = textDecoder.decode()\n\n if (leftover) writeChunk(leftover)\n if (cleanedUp || isDone()) return\n if (decoderRemainder) writeChunk(decoderRemainder)\n if (cleanedUp || isDone()) return\n flushPendingRouterHtml()\n if (cleanedUp || isDone()) return\n if (pendingTail) writeChunk(pendingTail)\n if (cleanedUp || isDone()) return\n\n leftover = ''\n pendingTail = ''\n\n state = MergeState.Draining\n closeWhenDrained = true\n // Try immediately; if queue not drained yet, pull() will retry.\n drainPending()\n }\n\n function finishAppRendering() {\n if (state >= MergeState.AppDone) return\n state = MergeState.AppDone\n try {\n serverSsr.setRenderFinished()\n } catch (error) {\n safeError(error)\n cleanup(error)\n return\n }\n drainRouterHtml()\n if (cleanedUp || isDone()) return\n serializationFinished =\n serializationFinished || serverSsr.isSerializationFinished()\n if (serializationFinished) {\n tryFinish()\n } else {\n startSerializationTimeout()\n }\n }\n\n // Safety net: cleanup even if consumer never reads\n const timeoutMs = opts?.timeoutMs ?? DEFAULT_SERIALIZATION_TIMEOUT_MS\n const lifetimeMs = opts?.lifetimeMs ?? timeoutMs * 2\n lifetimeTimeoutHandle = setTimeout(() => {\n if (!cleanedUp && !isDone()) {\n const err = new Error('Stream lifetime exceeded')\n console.warn(\n `SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`,\n )\n safeError(err)\n cleanup(err)\n }\n }, lifetimeMs)\n\n stopListeningToInjectedHtml = serverSsr.onInjectedHtml(() => {\n drainRouterHtml()\n })\n\n stopListeningToSerializationFinished = serverSsr.onSerializationFinished(\n () => {\n serializationFinished = true\n drainRouterHtml()\n tryFinish()\n },\n )\n\n // Subscriptions are installed before snapshots, so missed events are\n // recovered by these synchronous drains/rechecks.\n drainRouterHtml()\n if (cleanedUp || isDone()) return stream\n serializationFinished =\n serializationFinished || serverSsr.isSerializationFinished()\n if (serializationFinished) {\n drainRouterHtml()\n if (cleanedUp || isDone()) return stream\n }\n\n // Transform the appStream\n ;(async () => {\n try {\n while (true) {\n // Backpressure: pause upstream reads while downstream is full.\n if (waitForBackpressure()) {\n await waitForDrain()\n if (cleanedUp || isDone()) return\n }\n\n const { done, value } = await readerState.reader.read()\n if (done) break\n\n if (cleanedUp || isDone()) return\n\n const text =\n typeof value === 'string'\n ? value\n : textDecoder.decode(value as ArrayBufferView, { stream: true })\n\n const chunkString = leftover ? leftover + text : text\n\n // If we already saw </body>, everything else is tail. Keep it bounded\n // and held until router scripts are ready so injection remains before </body>.\n if (state >= MergeState.HoldingTail) {\n appendTail(chunkString)\n leftover = ''\n continue\n }\n\n const boundary = findHtmlBoundary(chunkString)\n if (boundary < -1) {\n const bodyEndIndex = -boundary - 2\n state = MergeState.HoldingTail\n appendTail(chunkString.slice(bodyEndIndex))\n const bodyChunk = chunkString.slice(0, bodyEndIndex)\n writeChunk(bodyChunk)\n if (cleanedUp || isDone()) return\n noteBarrierMarker(bodyChunk)\n liftBarrierAfterBoundary()\n if (cleanedUp || isDone()) return\n flushPendingRouterHtml()\n leftover = ''\n continue\n }\n\n const lastClosingTagEnd = boundary\n\n if (lastClosingTagEnd > 0) {\n const safeChunk = chunkString.slice(0, lastClosingTagEnd)\n writeChunk(safeChunk)\n if (cleanedUp || isDone()) return\n noteBarrierMarker(safeChunk)\n liftBarrierAfterBoundary()\n if (cleanedUp || isDone()) return\n flushPendingRouterHtml()\n\n leftover = chunkString.slice(lastClosingTagEnd)\n if (leftover.length > MAX_LEFTOVER_CHARS) {\n // Ensure bounded memory even if a consumer streams long text sequences\n // without any closing tags. This may reduce injection granularity but is correct.\n noteBarrierMarker(leftover)\n const flushed = leftover.slice(\n 0,\n leftover.length - MAX_LEFTOVER_CHARS,\n )\n writeChunk(flushed)\n leftover = leftover.slice(-MAX_LEFTOVER_CHARS)\n }\n } else {\n // No closing tag found; keep small tail to handle split closing tags,\n // but stream older bytes to prevent unbounded buffering.\n const combined = chunkString\n if (combined.length > MAX_LEFTOVER_CHARS) {\n noteBarrierMarker(combined)\n const flushUpto = combined.length - MAX_LEFTOVER_CHARS\n const flushed = combined.slice(0, flushUpto)\n writeChunk(flushed)\n leftover = combined.slice(flushUpto)\n } else {\n leftover = combined\n }\n }\n }\n\n if (cleanedUp || isDone()) return\n\n finishAppRendering()\n } catch (error) {\n if (cleanedUp) return\n console.error('Error reading appStream:', error)\n if (state < MergeState.AppDone) {\n try {\n serverSsr.setRenderFinished()\n } catch {\n // ignore\n }\n }\n safeError(error)\n cleanup(error)\n } finally {\n readerState.release()\n }\n })().catch((error) => {\n if (cleanedUp) return\n console.error('Error in stream transform:', error)\n safeError(error)\n cleanup(error)\n })\n\n return stream\n}\n"],"mappings":"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"}