UNPKG

@apollo/client-react-streaming

Version:

This package provides building blocks to create framework-level integration of Apollo Client with React's streaming SSR. See the [@apollo/client-integration-nextjs](https://github.com/apollographql/apollo-client-integrations/tree/main/packages/nextjs) pac

1 lines 22.6 kB
{"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/htmlescape.ts","../src/ManualDataTransport/serialization.ts","../src/ManualDataTransport/index.ts"],"names":["React","stringify","invariant","event"],"mappings":"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","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//\n// copied from\n// https://github.com/vercel/next.js/blob/6bc07792a4462a4bf921a72ab30dc4ab2c4e1bda/packages/next/src/server/htmlescape.ts\n// License: https://github.com/vercel/next.js/blob/6bc07792a4462a4bf921a72ab30dc4ab2c4e1bda/packages/next/license.md\n//\n// --------------------------------------------------------------------------------\n\n// This utility is based on https://github.com/zertosh/htmlescape\n// License: https://github.com/zertosh/htmlescape/blob/0527ca7156a524d256101bb310a9f970f63078ad/LICENSE\n\nconst ESCAPE_LOOKUP: { [match: string]: string } = {\n \"&\": \"\\\\u0026\",\n \">\": \"\\\\u003e\",\n \"<\": \"\\\\u003c\",\n \"\\u2028\": \"\\\\u2028\",\n \"\\u2029\": \"\\\\u2029\",\n};\n\nconst ESCAPE_REGEX = /[&><\\u2028\\u2029]/g;\n\nexport function htmlEscapeJsonString(str: string): string {\n return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);\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"]}