{"version":3,"sources":["../src/stream-utils/JSONTransformStreams.tsx","../src/stream-utils/createInjectionTransformStream.tsx","../src/stream-utils/pipeReaderToResponse.ts"],"names":[],"mappings":"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","sourcesContent":["export class JSONEncodeStream<T> extends TransformStream<T, JsonString<T>> {\n  constructor() {\n    super({\n      transform(chunk, controller) {\n        controller.enqueue(JSON.stringify(chunk));\n      },\n    });\n  }\n}\n\nexport class JSONDecodeStream<T> extends TransformStream<\n  JsonString<T> | AllowSharedBufferSource,\n  T\n> {\n  constructor() {\n    super({\n      transform(chunk, controller) {\n        if (typeof chunk !== \"string\") {\n          chunk = new TextDecoder().decode(chunk);\n        }\n        controller.enqueue(JSON.parse(chunk));\n      },\n    });\n  }\n}\n\nexport type JsonString<Encoded> = string & { __jsonString?: [Encoded] };\n","/**\n * The logic for `createInjectionTransformStream` was strongly inspired by `createHeadInsertionTransformStream`\n * from https://github.com/vercel/next.js/blob/6481c92038cce43056005c07f80f2938faf29c29/packages/next/src/server/node-web-streams-helper.ts\n *\n * released under a MIT license (https://github.com/vercel/next.js/blob/6481c92038cce43056005c07f80f2938faf29c29/packages/next/license.md)\n * by Vercel, Inc., marked Copyright (c) 2023 Vercel, Inc.\n */\n\nimport { renderToString } from \"react-dom/server\";\nimport * as React from \"react\";\n\n/**\n * > This export is only available in streaming SSR Server environments\n *\n * Used to create a `TransformStream` that can be used for piping a React stream rendered by\n * `renderToReadableStream` and using the callback to insert chunks of HTML between React Chunks.\n */\nexport function createInjectionTransformStream(): {\n  /**\n   * @example\n   * ```js\n   * const { injectIntoStream, transformStream } = createInjectionTransformStream();\n   * const App = render({ assets, injectIntoStream });\n   * const reactStream = await renderToReadableStream(App, { bootstrapModules }));\n   * await pipeReaderToResponse(\n   *   reactStream.pipeThrough(transformStream).getReader(),\n   *   response\n   * );\n   *  ```\n   */\n  transformStream: TransformStream;\n  /**\n   * `injectIntoStream` method that can be injected into your React application, to be made available to\n   *\n   * @example\n   * ```js title=\"setup\"\n   * // create a Context for injection of `injectIntoStream`\n   * const InjectionContext = React.createContext<\n   *   (callback: () => React.ReactNode) => void\n   * >(() => {});\n   * // to be used in your application\n   * export const InjectionContextProvider = InjectionContext.Provider;\n   * // make it accessible to `WrapApolloProvider`\n   * export const WrappedApolloProvider = WrapApolloProvider(\n   *   buildManualDataTransport({\n   *     useInsertHtml() {\n   *       return React.useContext(InjectionContext);\n   *     },\n   *   })\n   * );\n   * ```\n   * Then in your applications SSR render, pass this function to `InjectionContextProvider`:\n   * ```js\n   * <InjectionContextProvider value={injectIntoStream}>\n   * ```\n   */\n  injectIntoStream: (callback: () => React.ReactNode) => void;\n} {\n  let queuedInjections: Array<() => React.ReactNode> = [];\n\n  async function renderInjectedHtml() {\n    const injections = [...queuedInjections];\n    queuedInjections = [];\n    return renderToString(\n      <>\n        {injections.map((callback, i) => (\n          <React.Fragment key={i}>{callback()}</React.Fragment>\n        ))}\n      </>\n    );\n  }\n\n  let headInserted = false;\n  let currentlyStreaming = false;\n  let tailOfLastChunk = \"\";\n  const textDecoder = new TextDecoder();\n  const textEncoder = new TextEncoder();\n  const HEAD_END = \"</head>\";\n  // while the head has not fully been inserted, always move the last few\n  // bytes of a chunk into the next chunk, so we can ensure that `</head>`\n  // is not chopped into e.g. `</he` and `ad>`.\n  const KEEP_BYTES = HEAD_END.length;\n\n  const transformStream = new TransformStream({\n    async transform(chunk, controller) {\n      // While react is flushing chunks, we don't apply insertions.\n      // Injecting during streaming could break HTML by inserting content\n      // at arbitrary chunk boundaries (e.g., inside partial tags like <di${injected}v>).\n      if (currentlyStreaming) {\n        controller.enqueue(chunk);\n        return;\n      }\n\n      if (!headInserted) {\n        const content =\n          tailOfLastChunk + textDecoder.decode(chunk, { stream: true });\n        const index = content.indexOf(HEAD_END);\n        if (index !== -1) {\n          const insertedHeadContent =\n            content.slice(0, index) +\n            (await renderInjectedHtml()) +\n            content.slice(index);\n          controller.enqueue(textEncoder.encode(insertedHeadContent));\n          currentlyStreaming = true;\n          setImmediate(() => {\n            currentlyStreaming = false;\n          });\n          headInserted = true;\n        } else {\n          tailOfLastChunk = content.slice(-KEEP_BYTES);\n          controller.enqueue(textEncoder.encode(content.slice(0, -KEEP_BYTES)));\n        }\n      } else {\n        if (queuedInjections.length > 0) {\n          controller.enqueue(textEncoder.encode(await renderInjectedHtml()));\n        }\n        controller.enqueue(chunk);\n        currentlyStreaming = true;\n        setImmediate(() => {\n          currentlyStreaming = false;\n        });\n      }\n    },\n    async flush(controller) {\n      if (queuedInjections.length > 0) {\n        controller.enqueue(textEncoder.encode(await renderInjectedHtml()));\n      }\n    },\n  });\n\n  return {\n    transformStream,\n    injectIntoStream: (callback) => queuedInjections.push(callback),\n  };\n}\n","/**\n * > This export is only available in streaming SSR Server environments\n *\n * Used to pipe a `ReadableStreamDefaultReader` to a `ServerResponse`.\n *\n * @example\n * ```js\n * const { injectIntoStream, transformStream } = createInjectionTransformStream();\n * const App = render({ assets, injectIntoStream });\n * const reactStream = await renderToReadableStream(App, { bootstrapModules }));\n * await pipeReaderToResponse(\n *   reactStream.pipeThrough(transformStream).getReader(),\n *   response\n * );\n *  ```\n */\nexport async function pipeReaderToResponse(\n  reader: ReadableStreamDefaultReader<any>,\n  res: {\n    write: (chunk: any) => void;\n    end: () => void;\n    destroy: (e: Error) => void;\n  }\n) {\n  try {\n    // eslint-disable-next-line no-constant-condition\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) {\n        res.end();\n        return;\n      } else {\n        res.write(value);\n      }\n    }\n  } catch (e: any) {\n    res.destroy(e);\n  }\n}\n"]}