{"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 function transformReadableStreamWithRouter(\n  router: AnyRouter,\n  routerStream: ReadableStream,\n) {\n  return transformStreamWithRouter(router, routerStream)\n}\n\nexport function transformPipeableStreamWithRouter(\n  router: AnyRouter,\n  routerStream: Readable,\n) {\n  return Readable.fromWeb(\n    transformStreamWithRouter(router, Readable.toWeb(routerStream)),\n  )\n}\n\n// Use string constants for simple indexOf matching\nconst BODY_END_TAG = '</body>'\nconst HTML_END_TAG = '</html>'\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 = 60000\n\n// Module-level encoder (stateless, safe to reuse)\nconst textEncoder = new TextEncoder()\n\n/**\n * Finds the position just after the last valid HTML closing tag in the string.\n *\n * Valid closing tags match the pattern: </[a-zA-Z][\\w:.-]*>\n * Examples: </div>, </my-component>, </slot:name.nested>\n *\n * @returns Position after the last closing tag, or -1 if none found\n */\nfunction findLastClosingTagEnd(str: string): number {\n  const len = str.length\n  if (len < MIN_CLOSING_TAG_LENGTH) return -1\n\n  let i = len - 1\n\n  while (i >= MIN_CLOSING_TAG_LENGTH - 1) {\n    // Look for > (charCode 62)\n    if (str.charCodeAt(i) === 62) {\n      // Look backwards for valid tag name characters\n      let j = i - 1\n\n      // Skip through valid tag name characters\n      while (j >= 1) {\n        const code = str.charCodeAt(j)\n        // Check if it's a valid tag name char: [a-zA-Z0-9_:.-]\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          j--\n        } else {\n          break\n        }\n      }\n\n      // Check if the first char after </ is a valid start char (letter only)\n      const tagNameStart = j + 1\n      if (tagNameStart < i) {\n        const startCode = str.charCodeAt(tagNameStart)\n        // Tag name must start with a letter (a-z or A-Z)\n        if (\n          (startCode >= 97 && startCode <= 122) ||\n          (startCode >= 65 && startCode <= 90)\n        ) {\n          // Check for </ (charCodes: < = 60, / = 47)\n          if (\n            j >= 1 &&\n            str.charCodeAt(j) === 47 &&\n            str.charCodeAt(j - 1) === 60\n          ) {\n            return i + 1 // Return position after the closing >\n          }\n        }\n      }\n    }\n    i--\n  }\n  return -1\n}\n\nexport function transformStreamWithRouter(\n  router: AnyRouter,\n  appStream: ReadableStream,\n  opts?: {\n    /** Timeout for serialization to complete after app render finishes (default: 60000ms) */\n    timeoutMs?: number\n    /** Maximum lifetime of the stream transform (default: 60000ms). Safety net for cleanup. */\n    lifetimeMs?: number\n  },\n) {\n  // Check upfront if serialization already finished synchronously\n  // This is the fast path for routes with no deferred data\n  const serializationAlreadyFinished =\n    router.serverSsr?.isSerializationFinished() ?? false\n\n  // Take any HTML that was buffered before we started listening\n  const initialBufferedHtml = router.serverSsr?.takeBufferedHtml()\n\n  // True passthrough: if serialization already finished and nothing buffered,\n  // we can avoid any decoding/scanning while still honoring cleanup + setRenderFinished.\n  if (serializationAlreadyFinished && !initialBufferedHtml) {\n    let cleanedUp = false\n    let controller: ReadableStreamDefaultController<Uint8Array> | undefined\n    let isStreamClosed = false\n    let lifetimeTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n    const cleanup = () => {\n      if (cleanedUp) return\n      cleanedUp = true\n\n      if (lifetimeTimeoutHandle !== undefined) {\n        clearTimeout(lifetimeTimeoutHandle)\n        lifetimeTimeoutHandle = undefined\n      }\n\n      router.serverSsr?.cleanup()\n    }\n\n    const safeClose = () => {\n      if (isStreamClosed) return\n      isStreamClosed = true\n      try {\n        controller?.close()\n      } catch {\n        // ignore\n      }\n    }\n\n    const safeError = (error: unknown) => {\n      if (isStreamClosed) return\n      isStreamClosed = true\n      try {\n        controller?.error(error)\n      } catch {\n        // ignore\n      }\n    }\n\n    const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS\n    lifetimeTimeoutHandle = setTimeout(() => {\n      if (!cleanedUp && !isStreamClosed) {\n        console.warn(\n          `SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`,\n        )\n        safeError(new Error('Stream lifetime exceeded'))\n        cleanup()\n      }\n    }, lifetimeMs)\n\n    const stream = new ReadableStream<Uint8Array>({\n      start(c: ReadableStreamDefaultController<Uint8Array>) {\n        controller = c\n      },\n      cancel() {\n        isStreamClosed = true\n        cleanup()\n      },\n    })\n\n    ;(async () => {\n      const reader = appStream.getReader()\n      try {\n        while (true) {\n          const { done, value } = await reader.read()\n          if (done) break\n          if (cleanedUp || isStreamClosed) return\n          controller?.enqueue(value as unknown as Uint8Array)\n        }\n\n        if (cleanedUp || isStreamClosed) return\n\n        router.serverSsr?.setRenderFinished()\n        safeClose()\n        cleanup()\n      } catch (error) {\n        if (cleanedUp) return\n        console.error('Error reading appStream:', error)\n        router.serverSsr?.setRenderFinished()\n        safeError(error)\n        cleanup()\n      } finally {\n        reader.releaseLock()\n      }\n    })().catch((error) => {\n      if (cleanedUp) return\n      console.error('Error in stream transform:', error)\n      safeError(error)\n      cleanup()\n    })\n\n    return stream\n  }\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<any>\n  let isStreamClosed = false\n\n  const textDecoder = new TextDecoder()\n\n  // concat'd router HTML; avoids array joins on each flush\n  let pendingRouterHtml = initialBufferedHtml ?? ''\n\n  // between-chunk text buffer; keep bounded to avoid unbounded memory\n  let leftover = ''\n\n  // captured closing tags from </body> onward\n  let pendingClosingTags = ''\n\n  // conservative cap: enough to hold any partial closing tag + a bit\n  const MAX_LEFTOVER_CHARS = 2048\n\n  let isAppRendering = true\n  let streamBarrierLifted = false\n  let serializationFinished = serializationAlreadyFinished\n\n  function safeEnqueue(chunk: string | Uint8Array) {\n    if (isStreamClosed) return\n    if (typeof chunk === 'string') {\n      controller.enqueue(textEncoder.encode(chunk))\n    } else {\n      controller.enqueue(chunk)\n    }\n  }\n\n  function safeClose() {\n    if (isStreamClosed) return\n    isStreamClosed = true\n    try {\n      controller.close()\n    } catch {\n      // ignore\n    }\n  }\n\n  function safeError(error: unknown) {\n    if (isStreamClosed) return\n    isStreamClosed = true\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() {\n    if (cleanedUp) return\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    pendingRouterHtml = ''\n    leftover = ''\n    pendingClosingTags = ''\n\n    router.serverSsr?.cleanup()\n  }\n\n  const stream = new ReadableStream({\n    start(c: ReadableStreamDefaultController<any>) {\n      controller = c\n    },\n    cancel() {\n      isStreamClosed = true\n      cleanup()\n    },\n  })\n\n  function flushPendingRouterHtml() {\n    if (!pendingRouterHtml) return\n    safeEnqueue(pendingRouterHtml)\n    pendingRouterHtml = ''\n  }\n\n  function appendRouterHtml(html: string) {\n    if (!html) return\n    pendingRouterHtml += html\n  }\n\n  /**\n   * Finish only when app done and serialization complete.\n   */\n  function tryFinish() {\n    if (isAppRendering || !serializationFinished) return\n    if (cleanedUp || isStreamClosed) return\n\n    if (serializationTimeoutHandle !== undefined) {\n      clearTimeout(serializationTimeoutHandle)\n      serializationTimeoutHandle = undefined\n    }\n\n    // Flush any remaining bytes in the TextDecoder\n    const decoderRemainder = textDecoder.decode()\n\n    if (leftover) safeEnqueue(leftover)\n    if (decoderRemainder) safeEnqueue(decoderRemainder)\n    flushPendingRouterHtml()\n    if (pendingClosingTags) safeEnqueue(pendingClosingTags)\n\n    safeClose()\n    cleanup()\n  }\n\n  // Safety net: cleanup even if consumer never reads\n  const lifetimeMs = opts?.lifetimeMs ?? DEFAULT_LIFETIME_TIMEOUT_MS\n  lifetimeTimeoutHandle = setTimeout(() => {\n    if (!cleanedUp && !isStreamClosed) {\n      console.warn(\n        `SSR stream transform exceeded maximum lifetime (${lifetimeMs}ms), forcing cleanup`,\n      )\n      safeError(new Error('Stream lifetime exceeded'))\n      cleanup()\n    }\n  }, lifetimeMs)\n\n  if (!serializationAlreadyFinished) {\n    stopListeningToInjectedHtml = router.subscribe('onInjectedHtml', () => {\n      if (cleanedUp || isStreamClosed) return\n      const html = router.serverSsr?.takeBufferedHtml()\n      if (!html) return\n\n      // If we've already captured </body> (pendingClosingTags), we must keep appending\n      // so injection stays before the stored closing tags.\n      if (isAppRendering || leftover || pendingClosingTags) {\n        appendRouterHtml(html)\n      } else {\n        // App is done rendering - flush any pending buffer first to maintain order,\n        // then write the new HTML directly\n        flushPendingRouterHtml()\n        safeEnqueue(html)\n      }\n    })\n\n    stopListeningToSerializationFinished = router.subscribe(\n      'onSerializationFinished',\n      () => {\n        serializationFinished = true\n        tryFinish()\n      },\n    )\n  }\n\n  // Transform the appStream\n  ;(async () => {\n    const reader = appStream.getReader()\n    try {\n      while (true) {\n        const { done, value } = await reader.read()\n        if (done) break\n\n        if (cleanedUp || isStreamClosed) return\n\n        const text =\n          value instanceof Uint8Array\n            ? textDecoder.decode(value, { stream: true })\n            : String(value)\n\n        // Fast path: most chunks have no pending left-over.\n        const chunkString = leftover ? leftover + text : text\n\n        if (!streamBarrierLifted) {\n          if (chunkString.includes(TSR_SCRIPT_BARRIER_ID)) {\n            streamBarrierLifted = true\n            router.serverSsr?.liftScriptBarrier()\n          }\n        }\n\n        // If we already saw </body>, everything else is part of tail; buffer it.\n        if (pendingClosingTags) {\n          pendingClosingTags += chunkString\n          leftover = ''\n          continue\n        }\n\n        const bodyEndIndex = chunkString.indexOf(BODY_END_TAG)\n        const htmlEndIndex = chunkString.indexOf(HTML_END_TAG)\n\n        if (\n          bodyEndIndex !== -1 &&\n          htmlEndIndex !== -1 &&\n          bodyEndIndex < htmlEndIndex\n        ) {\n          pendingClosingTags = chunkString.slice(bodyEndIndex)\n          safeEnqueue(chunkString.slice(0, bodyEndIndex))\n          flushPendingRouterHtml()\n          leftover = ''\n          continue\n        }\n\n        const lastClosingTagEnd = findLastClosingTagEnd(chunkString)\n\n        if (lastClosingTagEnd > 0) {\n          safeEnqueue(chunkString.slice(0, lastClosingTagEnd))\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            safeEnqueue(leftover.slice(0, leftover.length - MAX_LEFTOVER_CHARS))\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            const flushUpto = combined.length - MAX_LEFTOVER_CHARS\n            safeEnqueue(combined.slice(0, flushUpto))\n            leftover = combined.slice(flushUpto)\n          } else {\n            leftover = combined\n          }\n        }\n      }\n\n      if (cleanedUp || isStreamClosed) return\n\n      isAppRendering = false\n      router.serverSsr?.setRenderFinished()\n\n      if (serializationFinished) {\n        tryFinish()\n      } else {\n        const timeoutMs = opts?.timeoutMs ?? DEFAULT_SERIALIZATION_TIMEOUT_MS\n        serializationTimeoutHandle = setTimeout(() => {\n          if (!cleanedUp && !isStreamClosed) {\n            console.error('Serialization timeout after app render finished')\n            safeError(\n              new Error('Serialization timeout after app render finished'),\n            )\n            cleanup()\n          }\n        }, timeoutMs)\n      }\n    } catch (error) {\n      if (cleanedUp) return\n      console.error('Error reading appStream:', error)\n      isAppRendering = false\n      router.serverSsr?.setRenderFinished()\n      safeError(error)\n      cleanup()\n    } finally {\n      reader.releaseLock()\n    }\n  })().catch((error) => {\n    if (cleanedUp) return\n    console.error('Error in stream transform:', error)\n    safeError(error)\n    cleanup()\n  })\n\n  return stream\n}\n"],"mappings":"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"}