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

353 lines (336 loc) 11.2 kB
"use client"; // src/SimulatePreloadedQuery.cc.ts import { useApolloClient as useApolloClient2, useBackgroundQuery } from "@apollo/client/react"; import { useMemo as useMemo2 } from "react"; // src/transportedQueryRef.ts import { unwrapQueryRef, wrapQueryRef } from "@apollo/client/react/internal"; // src/ReadableStreamLink.tsx import { ApolloLink, Observable } from "@apollo/client"; var teeToReadableStreamKey = Symbol.for( "apollo.tee.readableStreamController" ); var readFromReadableStreamKey = Symbol.for("apollo.read.readableStream"); function readFromReadableStream(readableStream, context) { return Object.assign(context, { [readFromReadableStreamKey]: readableStream }); } var TeeToReadableStreamLink = class extends ApolloLink { constructor() { super((operation, forward) => { const context = operation.getContext(); const onLinkHit = context[teeToReadableStreamKey]; if (onLinkHit) { const controller = onLinkHit(); const tryClose = () => { try { controller.close(); } catch { } }; return new Observable((observer) => { let subscribed = true; forward(operation).subscribe({ next(result) { controller.enqueue({ type: "next", value: result }); if (subscribed) { observer.next(result); } }, error(error) { controller.enqueue({ type: "error" }); tryClose(); if (subscribed) { observer.error(error); } }, complete() { controller.enqueue({ type: "completed" }); tryClose(); if (subscribed) { observer.complete(); } } }); return () => { subscribed = false; }; }); } return forward(operation); }); } }; var ReadFromReadableStreamLink = class extends ApolloLink { constructor() { super((operation, forward) => { const context = operation.getContext(); const eventSteam = context[readFromReadableStreamKey]; if (eventSteam) { return new Observable((observer) => { let aborted = false; const reader = (() => { try { return eventSteam.getReader(); } catch { } })(); if (!reader) { const subscription = forward(operation).subscribe(observer); return () => subscription.unsubscribe(); } let forwarded = false; consume(reader).finally(() => { if (!observer.closed && !forwarded) { observer.complete(); } }); let onAbort = () => { aborted = true; reader.cancel(); }; return () => onAbort(); async function consume(reader2) { let event = void 0; while (!aborted && !event?.done) { event = await reader2.read(); if (aborted) break; if (event.value) { switch (event.value.type) { case "next": observer.next(event.value.value); break; case "completed": observer.complete(); break; case "error": if (false) { observer.error( new Error( "Error from event stream. Redacted for security concerns." ) ); } else { onAbort(); forwarded = true; const subscription = forward(operation).subscribe(observer); onAbort = () => subscription.unsubscribe(); } break; } } } } }); } return forward(operation); }); } }; // src/DataTransportAbstraction/WrappedInMemoryCache.tsx import { InMemoryCache as OrigInMemoryCache } from "@apollo/client"; // src/bundleInfo.ts var bundle = { pkg: "@apollo/client-react-streaming" }; var sourceSymbol = Symbol.for("apollo.source_package"); // src/DataTransportAbstraction/WrappedInMemoryCache.tsx var InMemoryCache = class extends OrigInMemoryCache { /** * Information about the current package and it's export names, for use in error messages. * * @internal */ static info = bundle; [sourceSymbol]; constructor(config) { super(config); const info = this.constructor.info; this[sourceSymbol] = `${info.pkg}:InMemoryCache`; } }; // src/DataTransportAbstraction/WrappedApolloClient.tsx import { ApolloLink as ApolloLink2, ApolloClient as OrigApolloClient, Observable as Observable2 } from "@apollo/client"; import { invariant as invariant2 } from "@apollo/client/utilities/invariant"; // src/DataTransportAbstraction/useTransportValue.tsx import { useContext, useSyncExternalStore } from "react"; // src/DataTransportAbstraction/DataTransportAbstraction.ts import { createContext } from "react"; // src/DataTransportAbstraction/useTransportValue.tsx import { equal } from "@wry/equality"; // src/DataTransportAbstraction/hooks.ts import { useMemo } from "react"; // src/DataTransportAbstraction/transportedOptions.ts import { gql } from "@apollo/client"; import { print } from "@apollo/client/utilities"; import { invariant } from "@apollo/client/utilities/invariant"; import { stripIgnoredCharacters } from "graphql"; function deserializeOptions(options) { return { ...options, // `gql` memoizes results, but based on the input string. // We parse-stringify-parse here to ensure that our minified query // has the best chance of being the referential same query as the one used in // client-side code. query: gql(print(gql(options.query))) }; } // src/assertInstance.ts function assertInstance(value, info, name) { if (value[sourceSymbol] !== `${info.pkg}:${name}`) { throw new Error( `When using \`${name}\` in streaming SSR, you must use the \`${name}\` export provided by \`"${info.pkg}"\`.` ); } } // src/DataTransportAbstraction/WrappedApolloClient.tsx var wrappers = Symbol.for("apollo.hook.wrappers"); var ApolloClientBase = class extends OrigApolloClient { /** * Information about the current package and it's export names, for use in error messages. * * @internal */ static info = bundle; [sourceSymbol]; constructor(options) { const warnings = []; if ("ssrMode" in options) { delete options.ssrMode; warnings.push( "The `ssrMode` option is not supported in %s. Please remove it from your %s constructor options." ); } if ("ssrForceFetchDelay" in options) { delete options.ssrForceFetchDelay; warnings.push( "The `ssrForceFetchDelay` option is not supported in %s. Please remove it from your %s constructor options." ); } super( false ? { devtools: { enabled: false, ...options.devtools }, ...options } : options ); const info = this.constructor.info; this[sourceSymbol] = `${info.pkg}:ApolloClient`; for (const warning of warnings) { console.warn(warning, info.pkg, "ApolloClient"); } assertInstance( this.cache, info, "InMemoryCache" ); this.setLink(this.link); } setLink(newLink) { super.setLink.call( this, ApolloLink2.from([ new ReadFromReadableStreamLink(), new TeeToReadableStreamLink(), newLink ]) ); } }; var skipDataTransportKey = Symbol.for("apollo.dataTransport.skip"); function skipDataTransport(context) { return Object.assign(context, { [skipDataTransportKey]: true }); } // src/DataTransportAbstraction/WrapApolloProvider.tsx import React from "react"; import { useRef } from "react"; import { ApolloProvider } from "@apollo/client/react"; // src/transportedQueryRef.ts import { useApolloClient } from "@apollo/client/react"; import { useEffect } from "react"; import { JSONDecodeStream, JSONEncodeStream } from "@apollo/client-react-streaming/stream-utils"; import { InternalQueryReference } from "@apollo/client/react/internal"; import { invariant as invariant3 } from "@apollo/client/utilities/invariant"; import { __DEV__ } from "@apollo/client/utilities/environment"; function reviveTransportedQueryRef(queryRef, client) { if (unwrapQueryRef(queryRef)) return; const { $__apollo_queryRef: { options, stream } } = queryRef; const hydratedOptions = deserializeOptions(options); const internalQueryRef = getInternalQueryRef(client, hydratedOptions, { ...hydratedOptions, fetchPolicy: "network-only", context: skipDataTransport( readFromReadableStream(stream.pipeThrough(new JSONDecodeStream()), { ...hydratedOptions.context, queryDeduplication: true }) ) }); Object.assign(queryRef, wrapQueryRef(internalQueryRef)); } function getInternalQueryRef(client, userOptions, initialFetchOptions) { if (__DEV__) { invariant3( userOptions.nextFetchPolicy === initialFetchOptions.nextFetchPolicy, "Encountered an unexpected bug in @apollo/client-react-streaming. Please file an issue." ); } const observable = client.watchQuery(userOptions); const optionsAfterCreation = { // context might still be `undefined`, so we need to make sure the property is at least present // `undefined` won't merge in as `applyOptions` uses `compact`, so we use an empty object instead context: {}, ...observable.options }; observable.applyOptions(initialFetchOptions); const internalQueryRef = new InternalQueryReference(observable, { autoDisposeTimeoutMs: client.defaultOptions.react?.suspense?.autoDisposeTimeoutMs }); observable.applyOptions({ ...optionsAfterCreation, fetchPolicy: observable.options.fetchPolicy === initialFetchOptions.fetchPolicy ? ( // restore `userOptions.fetchPolicy` for future fetches optionsAfterCreation.fetchPolicy ) : ( // otherwise `fetchPolicy` was changed from `initialFetchOptions`, `nextFetchPolicy` has been applied and we're not going to touch it observable.options.fetchPolicy ) }); return internalQueryRef; } // src/SimulatePreloadedQuery.cc.ts function SimulatePreloadedQuery({ queryRef, children }) { const client = useApolloClient2(); reviveTransportedQueryRef(queryRef, client); const bgQueryArgs = useMemo2(() => { const { query, ...hydratedOptions } = deserializeOptions( queryRef.$__apollo_queryRef.options ); return [ query, { ...hydratedOptions, queryKey: queryRef.$__apollo_queryRef.queryKey } ]; }, [queryRef.$__apollo_queryRef]); useBackgroundQuery(...bgQueryArgs); return children; } export { SimulatePreloadedQuery as default }; export const built_for_browser = true; //# sourceMappingURL=SimulatePreloadedQuery.cc.js.map