{"version":3,"file":"renderRouterToStream.cjs","names":[],"sources":["../../../src/ssr/renderRouterToStream.tsx"],"sourcesContent":["import * as Solid from '@solidjs/web'\nimport { isbot } from 'isbot'\nimport {\n  createSsrStreamResponse,\n  transformReadableStreamWithRouter,\n} from '@tanstack/router-core/ssr/server'\nimport { makeSsrSerovalPlugin } from '@tanstack/router-core'\nimport clientAssetsManifest from './clientAssetsManifest'\nimport type { ReadableStream } from 'node:stream/web'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type { JSX } from '@solidjs/web'\n\nconst noop = () => {}\n\n// Bot responses wait for the server renderer before streaming. If the request\n// disconnects during that wait, unblock so the pipe can abort and clean up.\nasync function waitForReadyOrAbort(\n  ready: Promise<unknown>,\n  signal: AbortSignal,\n) {\n  let cleanup = noop\n  try {\n    await Promise.race([\n      ready,\n      new Promise<void>((resolve) => {\n        const onAbort = () => resolve()\n        cleanup = () => signal.removeEventListener('abort', onAbort)\n        signal.addEventListener('abort', onAbort, { once: true })\n        if (signal.aborted) resolve()\n      }),\n    ])\n  } finally {\n    cleanup()\n  }\n}\n\nexport const renderRouterToStream = async ({\n  request,\n  router,\n  responseHeaders,\n  children,\n  manifest,\n}: {\n  request: Request\n  router: AnyRouter\n  responseHeaders: Headers\n  children: () => JSX.Element\n  manifest?: unknown\n}) => {\n  const { writable, readable } = new TransformStream()\n\n  const serializationAdapters =\n    (router.options as any)?.serializationAdapters ||\n    (router.options.ssr as any)?.serializationAdapters\n  const serovalPlugins = serializationAdapters?.map((adapter: any) => {\n    const plugin = makeSsrSerovalPlugin(adapter, { didRun: false })\n    return plugin\n  })\n\n  const stream = Solid.renderToStream(() => children, {\n    nonce: router.options.ssr?.nonce,\n    plugins: serovalPlugins,\n    // Prefer the bundler-provided client-assets bridge (module-keyed, the\n    // shape Solid resolves lazy() assets from); the router's route-keyed\n    // manifest is a last resort that cannot answer lazy module lookups.\n    manifest: manifest ?? clientAssetsManifest ?? router.ssr?.manifest,\n  } as any)\n\n  // Solid's `pipeTo(w)` takes a single arg (no signal overload) and locks\n  // `w` via `w.getWriter()`. To still own the lifecycle we hand Solid a\n  // proxy WritableStream that forwards into an inner writer we control on\n  // the real TransformStream writable. Aborting the inner writer errors\n  // the underlying readable (which our router transform reads from),\n  // surfacing the cancel through the response pipeline.\n  //\n  // RESIDUAL RISK: solid-js@1.x does NOT expose a disposal hook on\n  // `renderToStream`, and its internal write loop swallows writer\n  // rejections (`writer.write(...).catch(() => {})` in\n  // solid-js/web/dist/server.js). So aborting the inner writer stops\n  // outbound bytes but does not terminate Solid's render continuation\n  // if a Suspense/resource never resolves — those pending promise\n  // continuations remain scheduled and can retain children/context/\n  // request references via captured closures until natural completion\n  // or process exit. The request-scoped router graph itself is released\n  // by ServerSsr.cleanup() through the router stream lifecycle, so the leak\n  // is bounded to whatever the user's Suspense/resource closures capture.\n  // A hard upstream-abort guarantee would require a disposal API in solid-js.\n  const innerWriter = writable.getWriter()\n  let writerDone = false\n  const releaseWriter = () => {\n    try {\n      innerWriter.releaseLock()\n    } catch {\n      // already released / errored\n    }\n  }\n  const abortSolidPipe = (reason?: unknown) => {\n    if (writerDone) return\n    writerDone = true\n    void innerWriter\n      .abort(reason)\n      .catch(() => {})\n      .finally(releaseWriter)\n  }\n\n  const onRequestAbort = () => {\n    abortSolidPipe(request.signal.reason)\n  }\n\n  // Wire request abort before the bot all-ready wait. Otherwise a disconnect\n  // during `await stream` can leave this callback pending forever.\n  if (request.signal.aborted) {\n    onRequestAbort()\n  } else {\n    request.signal.addEventListener('abort', onRequestAbort, { once: true })\n    router.serverSsr?.onCleanup(() => {\n      request.signal.removeEventListener('abort', onRequestAbort)\n    })\n  }\n\n  if (isbot(request.headers.get('User-Agent'))) {\n    await waitForReadyOrAbort(\n      Promise.resolve(stream as unknown),\n      request.signal,\n    )\n  }\n\n  const doctype = new TextEncoder().encode('<!DOCTYPE html>')\n  let doctypeWritten = false\n\n  const solidWritable = new WritableStream({\n    write(chunk) {\n      if (!doctypeWritten) {\n        doctypeWritten = true\n        if (ArrayBuffer.isView(chunk)) {\n          const bytes = chunk as Uint8Array\n          const out = new Uint8Array(doctype.length + bytes.length)\n          out.set(doctype, 0)\n          out.set(bytes, doctype.length)\n          return innerWriter.write(out)\n        }\n      }\n      return innerWriter.write(chunk)\n    },\n    close() {\n      writerDone = true\n      return innerWriter.close().finally(releaseWriter)\n    },\n    abort(reason) {\n      writerDone = true\n      return innerWriter.abort(reason).finally(releaseWriter)\n    },\n  })\n\n  if (!request.signal.aborted) {\n    try {\n      void Promise.resolve(stream.pipeTo(solidWritable) as unknown).catch(\n        (err: any) => {\n          if (\n            writerDone ||\n            err?.name === 'AbortError' ||\n            err?.code === 'ABORT_ERR'\n          )\n            return\n          console.error('Error in Solid render stream:', err)\n          abortSolidPipe(err)\n        },\n      )\n    } catch (err: any) {\n      if (err?.name !== 'AbortError' && err?.code !== 'ABORT_ERR') {\n        console.error('Error in Solid render stream:', err)\n      }\n      abortSolidPipe(err)\n    }\n  }\n\n  const responseStream = transformReadableStreamWithRouter(\n    router,\n    readable as unknown as ReadableStream,\n    { signal: request.signal, onAbort: abortSolidPipe },\n  )\n  return createSsrStreamResponse(\n    router,\n    new Response(responseStream as any, {\n      status:\n        router._serverResult?.type === 'render'\n          ? router._serverResult.status\n          : 200,\n      headers: responseHeaders,\n    }),\n  )\n}\n"],"mappings":"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"}