{"version":3,"sources":["../src/ManualDataTransport/ManualDataTransport.tsx","../src/ManualDataTransport/RehydrationContext.tsx","../src/ManualDataTransport/ApolloRehydrateSymbols.tsx","../src/ManualDataTransport/lateInitializingQueue.ts","../src/ManualDataTransport/dataTransport.ts","../src/ManualDataTransport/serialization.ts","../src/ManualDataTransport/index.ts"],"names":["React","revive","invariant","useStaticValueRef"],"mappings":";AAAA,OAAOA,UAAS,aAAa,WAAW,OAAO,SAAS,cAAc;AAEtE,SAAS,4BAA4B;;;ACFrC,OAAO,WAAW;;;ACSX,IAAM,yBAAuC,uBAAO;AAAA,EACzD;AACF;AAEO,IAAM,6BAA2C,uBAAO;AAAA,EAC7D;AACF;;;ACCO,SAAS,8BACd,KACA,UACA;AACA,QAAM,eAAe,OAAO,GAAG,KAAK,CAAC;AACrC,MAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,WAAO,GAAG,IAAI;AAAA,MACZ,MAAM,IAAI,SAAgB;AACxB,mBAAW,SAAS,MAAM;AACxB,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO,GAAG,EAAE,KAAK,GAAG,YAAY;AAAA,EAClC;AACF;;;AC5BA,SAAS,iBAAiB;AA0BnB,SAAS,sBAAsB;AAAA,EACpC;AAAA,EACA;AAAA,EACA,QAAAC;AACF,GAIG;AACD,gCAA8B,wBAAwB,CAAC,SAAS;AAC9D,UAAM,SAASA,QAAO,IAAI;AAC1B,cAAU,MAAM,kCAAkC,MAAM;AACxD,gBAAY,OAAO,SAAS;AAC5B,eAAW,UAAU,OAAO,QAAQ;AAClC,mBAAa,MAAM;AAAA,IACrB;AAAA,EACF,CAAC;AACH;;;AH3CA,SAAS,aAAAC,kBAAiB;;;AIYnB,SAAS,OAAO,OAAiB;AACtC,SAAO;AACT;;;ALiFA,IAAM,sCAAsC,CAAC;AAAA,EAC3C,mBAAmB;AACrB,MACE,SAAS,+BAA+B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,GAAG;AACD,QAAM,uBAAuB;AAAA,IAC1B,OAAO,0BAA0B,MAAM,CAAC;AAAA,EAC3C;AACA,wBAAsB;AAAA,IACpB;AAAA,IACA,YAAY,WAAW;AACrB,aAAO,OAAO,qBAAqB,SAAS,SAAS;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,YAAU,MAAM;AACd,QAAI,SAAS,eAAe,YAAY;AAGtC,aAAO,iBAAiB,QAAQ,uBAAwB;AAAA,QACtD,MAAM;AAAA,MACR,CAAC;AACD,aAAO,MAAM,OAAO,oBAAoB,QAAQ,qBAAsB;AAAA,IACxE,OAAO;AACL,4BAAuB;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAM,oBAAoB,YAAY,SAASC,mBAAqB,GAAM;AACxE,UAAM,KAAK,MAAM;AACjB,UAAM,QAAQ,qBAAqB;AACnC,UAAM,UAAU,OAAO,aAAkB;AACzC,QAAI,QAAQ,YAAY,eAAe;AACrC,UAAI,SAAS,MAAM,OAAO;AACxB,gBAAQ,UAAU,MAAM,EAAE;AAC1B,eAAO,MAAM,EAAE;AAAA,MACjB,OAAO;AACL,gBAAQ,UAAU;AAAA,MACpB;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAH,OAAA;AAAA,IAAC,qBAAqB;AAAA,IAArB;AAAA,MACC,OAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,QACF;AAAA,QACA,CAAC,iBAAiB;AAAA,MACpB;AAAA;AAAA,IAEC;AAAA,EACH;AAEJ;AAEF,IAAM,gBAAgB,CAAC;AAkDhB,IAAM,2BAGX,QACI,kCACA;;;AM9MN,SAAS,6BAA6B;AAc/B,SAAS,iCAAiC;AAC/C,wBAAsB;AACtB,SAAO,OAAO,0BAA0B;AACxC,SAAO,OAAO,sBAAsB;AACtC","sourcesContent":["import React, { useCallback, useEffect, useId, useMemo, useRef } from \"react\";\nimport type { DataTransportProviderImplementation } from \"@apollo/client-react-streaming\";\nimport { DataTransportContext } from \"@apollo/client-react-streaming\";\nimport type { RehydrationCache, RehydrationContextValue } from \"./types.js\";\nimport type { HydrationContextOptions } from \"./RehydrationContext.js\";\nimport { buildApolloRehydrationContext } from \"./RehydrationContext.js\";\nimport { registerDataTransport } from \"./dataTransport.js\";\nimport { revive, stringify } from \"./serialization.js\";\nimport { ApolloHookRehydrationCache } from \"./ApolloRehydrateSymbols.js\";\n\ninterface ManualDataTransportOptions {\n  /**\n   * A hook that allows for insertion into the stream.\n   * Will only be called during SSR, doesn't need to actiually return something otherwise.\n   */\n  useInsertHtml(): (callbacks: () => React.ReactNode) => void;\n  /**\n   * Prepare data for injecting into the stream by converting it into a string that can be parsed as JavaScript by the browser.\n   * Could e.g. be `SuperJSON.stringify` or `serialize-javascript`.\n   * The default implementation act like a JSON.stringify that preserves `undefined`, but not do much on top of that.\n   */\n  stringifyForStream?: (value: any) => string;\n  /**\n   * If necessary, additional deserialization steps that need to be applied on top of executing the result of `stringifyForStream` in the browser.\n   * Could e.g. be `SuperJSON.deserialize`. (Not needed in the case of using `serialize-javascript`)\n   */\n  reviveFromStream?: (value: any) => any;\n  /**\n   * **Read the whole comment before using this option!**\n   *\n   * If `true`, the `useStaticValueRef` hook will not transport values over to the client.\n   * This hook is used to transport the values of hook calls during SSR to the client, to ensure that\n   * the client will rehydrate with the exact same values as it rendered on the server.\n   *\n   * This mechanism is in place to prevent hydration mismatches as described in\n   * https://github.com/apollographql/apollo-client-integrations/blob/pr/RFC-2/RFC.md#challenges-of-a-client-side-cache-in-streaming-ssr\n   * (first graph of the \"Challenges of a client-side cache in streaming SSR\" section).\n   *\n   * Setting this value to `true` will save on data transported over the wire, but comes with the risk\n   * of hydration mismatches.\n   * Strongly discouraged with older React versions, as hydration mismatches there will likely crash\n   * the application, setting this to `true` might be okay with React 19, which is much better at recovering\n   * from hydration mismatches - but it still comes with a risk.\n   * When enabling this, you should closely monitor error reporting and user feedback.\n   */\n  dangerous_disableHookValueTransportation?: boolean;\n}\n\nconst buildManualDataTransportSSRImpl = ({\n  useInsertHtml,\n  stringifyForStream = stringify,\n  dangerous_disableHookValueTransportation: disableHookValueTransportation,\n}: ManualDataTransportOptions): DataTransportProviderImplementation<HydrationContextOptions> =>\n  function ManualDataTransportSSRImpl({\n    extraScriptProps,\n    children,\n    registerDispatchRequestStarted,\n  }) {\n    const insertHtml = useInsertHtml();\n\n    const rehydrationContext = useRef<RehydrationContextValue>(undefined);\n    if (!rehydrationContext.current) {\n      rehydrationContext.current = buildApolloRehydrationContext({\n        insertHtml,\n        extraScriptProps,\n        stringify: stringifyForStream,\n      });\n    }\n\n    registerDispatchRequestStarted!(({ event, observable }) => {\n      rehydrationContext.current!.incomingEvents.push(event);\n      observable.subscribe({\n        next(event) {\n          rehydrationContext.current!.incomingEvents.push(event);\n        },\n      });\n    });\n\n    const contextValue = useMemo(\n      () => ({\n        useStaticValueRef: function useStaticValueRef<T>(value: T) {\n          const id = useId();\n          if (!disableHookValueTransportation) {\n            rehydrationContext.current!.transportValueData[id] = value;\n          }\n          return { current: value };\n        },\n      }),\n      []\n    );\n\n    return (\n      <DataTransportContext.Provider value={contextValue}>\n        {children}\n      </DataTransportContext.Provider>\n    );\n  };\n\nconst buildManualDataTransportBrowserImpl = ({\n  reviveFromStream = revive,\n}: ManualDataTransportOptions): DataTransportProviderImplementation<HydrationContextOptions> =>\n  function ManualDataTransportBrowserImpl({\n    children,\n    onQueryEvent,\n    rerunSimulatedQueries,\n  }) {\n    const hookRehydrationCache = useRef<RehydrationCache>(\n      (window[ApolloHookRehydrationCache] ??= {})\n    );\n    registerDataTransport({\n      onQueryEvent: onQueryEvent!,\n      onRehydrate(rehydrate) {\n        Object.assign(hookRehydrationCache.current, rehydrate);\n      },\n      revive: reviveFromStream,\n    });\n\n    useEffect(() => {\n      if (document.readyState !== \"complete\") {\n        // happens simulatenously to `readyState` changing to `\"complete\"`, see\n        // https://html.spec.whatwg.org/multipage/parsing.html#the-end (step 9.1 and 9.5)\n        window.addEventListener(\"load\", rerunSimulatedQueries!, {\n          once: true,\n        });\n        return () => window.removeEventListener(\"load\", rerunSimulatedQueries!);\n      } else {\n        rerunSimulatedQueries!();\n      }\n    }, [rerunSimulatedQueries]);\n\n    const useStaticValueRef = useCallback(function useStaticValueRef<T>(v: T) {\n      const id = useId();\n      const store = hookRehydrationCache.current;\n      const dataRef = useRef(UNINITIALIZED as T);\n      if (dataRef.current === UNINITIALIZED) {\n        if (store && id in store) {\n          dataRef.current = store[id] as T;\n          delete store[id];\n        } else {\n          dataRef.current = v;\n        }\n      }\n      return dataRef;\n    }, []);\n\n    return (\n      <DataTransportContext.Provider\n        value={useMemo(\n          () => ({\n            useStaticValueRef,\n          }),\n          [useStaticValueRef]\n        )}\n      >\n        {children}\n      </DataTransportContext.Provider>\n    );\n  };\n\nconst UNINITIALIZED = {};\n\n/**\n * > This export is only available in React Client Components\n *\n * Creates a \"manual\" Data Transport, to be used with `WrapApolloProvider`.\n *\n * @remarks\n *\n * ### Drawbacks\n *\n * While this Data Transport enables streaming SSR, it has some conceptual drawbacks:\n *\n * - It does not have a way of keeping your connection open if your application already finished, but there are still ongoing queries that might need to be transported over.\n *   - This can happen if a component renders `useBackgroundQuery`, but does not read the `queryRef` with `useReadQuery`\n *   - These \"cut off\" queries will be restarted in the browser once the browser's `load` event happens\n * - If the `useInsertHtml` doesn't immediately flush data to the browser, the browser might already attain \"newer\" data through queries triggered by user interaction.\n *   - This delayed behaviour is the case with the Next.js `ServerInsertedHTMLContext` and in the example Vite implementation.\n *   - In this, case, older data from the server might overwrite newer data in the browser. This is minimized by simulating ongoing queries in the browser once the information of a started query is transported over.\n *     If the browser would try to trigger the exact same query, query deduplication would make the browser wait for the server query to resolve instead.\n * - For more timing-related details, see https://github.com/apollographql/apollo-client-integrations/pull/9\n *\n * To fully work around these drawbacks, React needs to add \"data injection into the stream\" to it's public API, which is not the case today.\n * We provide an [example with a patched React version](https://github.com/apollographql/apollo-client-integrations/blob/main/integration-test/experimental-react) to showcase how that could look.\n *\n * @example\n * For usage examples, see the implementation of the `@apollo/client-integration-nextjs`\n * [`ApolloNextAppProvider`](https://github.com/apollographql/apollo-client-integrations/blob/c0715a05cf8ca29a3cbb9ce294cdcbc5ce251b2e/packages/experimental-nextjs-app-support/src/ApolloNextAppProvider.ts)\n *\n * ```tsx\n * export const ApolloNextAppProvider = WrapApolloProvider(\n *   buildManualDataTransport({\n *     useInsertHtml() {\n *       const insertHtml = useContext(ServerInsertedHTMLContext);\n *       if (!insertHtml) {\n *         throw new Error(\n *           \"ApolloNextAppProvider cannot be used outside of the Next App Router!\"\n *         );\n *       }\n *       return insertHtml;\n *     },\n *   })\n * );\n * ```\n *\n * @example\n * Another usage example is our integration example with Vite and streaming SSR, which you can find at https://github.com/apollographql/apollo-client-integrations/tree/main/integration-test/vite-streaming\n *\n * @public\n */\nexport const buildManualDataTransport: (\n  args: ManualDataTransportOptions\n) => DataTransportProviderImplementation<HydrationContextOptions> =\n  process.env.REACT_ENV === \"ssr\"\n    ? buildManualDataTransportSSRImpl\n    : buildManualDataTransportBrowserImpl;\n","import React from \"react\";\nimport type { RehydrationContextValue } from \"./types.js\";\nimport { transportDataToJS } from \"./dataTransport.js\";\nimport { invariant } from \"@apollo/client/utilities/invariant\";\nimport type { Stringify } from \"./serialization.js\";\n\n/**\n * @public\n */\nexport interface HydrationContextOptions {\n  /**\n   * Props that will be passed down to `script` tags that will be used to transport\n   * data to the browser.\n   * Can e.g. be used to add a `nonce`.\n   */\n  extraScriptProps?: ScriptProps;\n}\n\ntype SerializableProps<T> = Pick<\n  T,\n  {\n    [K in keyof T]: T[K] extends string | number | boolean | undefined | null\n      ? K\n      : never;\n  }[keyof T]\n>;\n\ntype ScriptProps = SerializableProps<\n  React.ScriptHTMLAttributes<HTMLScriptElement>\n>;\n\nexport function buildApolloRehydrationContext({\n  insertHtml,\n  stringify,\n  extraScriptProps,\n}: HydrationContextOptions & {\n  insertHtml: (callbacks: () => React.ReactNode) => void;\n  stringify: Stringify;\n}): RehydrationContextValue {\n  function ensureInserted() {\n    if (!rehydrationContext.currentlyInjected) {\n      rehydrationContext.currentlyInjected = true;\n      insertHtml(() => <rehydrationContext.RehydrateOnClient />);\n    }\n  }\n\n  const rehydrationContext: RehydrationContextValue = {\n    currentlyInjected: false,\n    transportValueData: getTransportObject(ensureInserted),\n    transportedValues: {},\n    incomingEvents: getTransportArray(ensureInserted),\n    RehydrateOnClient() {\n      rehydrationContext.currentlyInjected = false;\n      if (\n        !Object.keys(rehydrationContext.transportValueData).length &&\n        !Object.keys(rehydrationContext.incomingEvents).length\n      )\n        return <></>;\n      invariant.debug(\n        \"transporting data\",\n        rehydrationContext.transportValueData\n      );\n      invariant.debug(\"transporting events\", rehydrationContext.incomingEvents);\n\n      const __html = transportDataToJS(\n        {\n          rehydrate: Object.fromEntries(\n            Object.entries(rehydrationContext.transportValueData).filter(\n              ([key, value]) =>\n                rehydrationContext.transportedValues[key] !== value\n            )\n          ),\n          events: rehydrationContext.incomingEvents,\n        },\n        stringify\n      );\n      Object.assign(\n        rehydrationContext.transportedValues,\n        rehydrationContext.transportValueData\n      );\n      rehydrationContext.transportValueData =\n        getTransportObject(ensureInserted);\n      rehydrationContext.incomingEvents = getTransportArray(ensureInserted);\n      return (\n        <script\n          {...extraScriptProps}\n          dangerouslySetInnerHTML={{\n            __html,\n          }}\n        />\n      );\n    },\n  };\n  return rehydrationContext;\n}\n\nfunction getTransportObject(ensureInserted: () => void) {\n  return new Proxy(\n    {},\n    {\n      set(...args) {\n        ensureInserted();\n        return Reflect.set(...args);\n      },\n    }\n  );\n}\nfunction getTransportArray(ensureInserted: () => void) {\n  return new Proxy<any[]>([], {\n    get(...args) {\n      if (args[1] === \"push\") {\n        return (...values: any[]) => {\n          ensureInserted();\n          return args[0].push(...values);\n        };\n      }\n      return Reflect.get(...args);\n    },\n  });\n}\n","import type { DataTransport } from \"./dataTransport.js\";\nimport type { RehydrationCache } from \"./types.js\";\n\ndeclare global {\n  interface Window {\n    [ApolloSSRDataTransport]?: DataTransport<unknown>;\n    [ApolloHookRehydrationCache]?: RehydrationCache;\n  }\n}\nexport const ApolloSSRDataTransport = /*#__PURE__*/ Symbol.for(\n  \"ApolloSSRDataTransport\"\n);\n\nexport const ApolloHookRehydrationCache = /*#__PURE__*/ Symbol.for(\n  \"apollo.hookRehydrationCache\"\n);\n","type ValidQueueKeys = {\n  [K in keyof Window]-?: NonNullable<Window[K]> extends {\n    push(...args: any[]): any;\n  }\n    ? K\n    : never;\n}[keyof Window];\n\n/**\n * Registers a queue that can be filled with data before it has actually been initialized with this function.\n * Before calling this function, `window[key]` can just be handled as an array of data.\n * When calling this funcation, all accumulated data will be passed to the callback.\n * After calling this function, `window[key]` will be an object with a `push` method that will call the callback with the data.\n *\n * @public\n */\nexport function registerLateInitializingQueue<K extends ValidQueueKeys>(\n  key: K,\n  callback: (data: Parameters<NonNullable<Window[K]>[\"push\"]>[0]) => void\n) {\n  const previousData = window[key] || [];\n  if (Array.isArray(previousData)) {\n    window[key] = {\n      push: (...data: any[]) => {\n        for (const value of data) {\n          callback(value);\n        }\n      },\n    };\n    window[key].push(...previousData);\n  }\n}\n","import { ApolloSSRDataTransport } from \"./ApolloRehydrateSymbols.js\";\nimport type { RehydrationCache } from \"./types.js\";\nimport { registerLateInitializingQueue } from \"./lateInitializingQueue.js\";\nimport { invariant } from \"@apollo/client/utilities/invariant\";\nimport { htmlEscapeJsonString } from \"./htmlescape.js\";\nimport type { QueryEvent } from \"@apollo/client-react-streaming\";\nimport type { Revive, Stringify } from \"./serialization.js\";\n\nexport type DataTransport<T> = Array<T> | { push(...args: T[]): void };\n\ntype DataToTransport = {\n  rehydrate: RehydrationCache;\n  events: QueryEvent[];\n};\n\n/**\n * Returns a string of JavaScript that can be used to transport data to the client.\n */\nexport function transportDataToJS(data: DataToTransport, stringify: Stringify) {\n  const key = Symbol.keyFor(ApolloSSRDataTransport);\n  return `(window[Symbol.for(\"${key}\")] ??= []).push(${htmlEscapeJsonString(\n    stringify(data)\n  )})`;\n}\n\n/**\n * Registers a lazy queue that will be filled with data by `transportDataToJS`.\n * All incoming data will be added either to the rehydration cache or the result cache.\n */\nexport function registerDataTransport({\n  onQueryEvent,\n  onRehydrate,\n  revive,\n}: {\n  onQueryEvent(event: QueryEvent): void;\n  onRehydrate(rehydrate: RehydrationCache): void;\n  revive: Revive;\n}) {\n  registerLateInitializingQueue(ApolloSSRDataTransport, (data) => {\n    const parsed = revive(data) as DataToTransport;\n    invariant.debug(`received data from the server:`, parsed);\n    onRehydrate(parsed.rehydrate);\n    for (const result of parsed.events) {\n      onQueryEvent(result);\n    }\n  });\n}\n","/**\n * Stringifies a value to be injected into JavaScript \"text\" - preserves `undefined` values.\n */\nexport function stringify(value: any) {\n  let undefinedPlaceholder = \"$apollo.undefined$\";\n\n  const stringified = JSON.stringify(value);\n  while (stringified.includes(JSON.stringify(undefinedPlaceholder))) {\n    undefinedPlaceholder = \"$\" + undefinedPlaceholder;\n  }\n  return JSON.stringify(value, (_, v) =>\n    v === undefined ? undefinedPlaceholder : v\n  ).replaceAll(JSON.stringify(undefinedPlaceholder), \"undefined\");\n}\n\nexport function revive(value: any): any {\n  return value;\n}\n\nexport type Stringify = typeof stringify;\nexport type Revive = typeof revive;\n","export { buildManualDataTransport } from \"./ManualDataTransport.js\";\nexport { registerLateInitializingQueue } from \"./lateInitializingQueue.js\";\nexport type { HydrationContextOptions } from \"./RehydrationContext.js\";\n\nimport {\n  ApolloHookRehydrationCache,\n  ApolloSSRDataTransport,\n} from \"./ApolloRehydrateSymbols.js\";\nimport { resetApolloSingletons } from \"@apollo/client-react-streaming\";\n\n/**\n * > This export is only available in React Client Components\n *\n * Resets the singleton instances created for the Apollo SSR data transport and caches.\n *\n * To be used in testing only, like\n * ```ts\n * afterEach(resetManualSSRApolloSingletons);\n * ```\n *\n * @public\n */\nexport function resetManualSSRApolloSingletons() {\n  resetApolloSingletons();\n  delete window[ApolloHookRehydrationCache];\n  delete window[ApolloSSRDataTransport];\n}\n"]}