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

624 lines (606 loc) 22.7 kB
import * as _apollo_client_index_js from '@apollo/client/index.js'; import { FetchResult, ApolloLink, WatchQueryOptions, QueryOptions, OperationVariables, DocumentNode, TypedDocumentNode, ApolloClient as ApolloClient$1, Operation, NextLink, InMemoryCache as InMemoryCache$1, InMemoryCacheConfig, Observable as Observable$1, NormalizedCacheObject, ApolloClientOptions } from '@apollo/client/index.js'; import React, { ReactNode } from 'react'; import { DocumentNode as DocumentNode$1, TypedDocumentNode as TypedDocumentNode$1 } from '@apollo/client'; import { wrapQueryRef } from '@apollo/client/react/internal/index.js'; import { QueryRef } from '@apollo/client/react/index.js'; import { Observable } from '@apollo/client/utilities/index.js'; /** * @internal */ type ReadableStreamLinkEvent = { type: "next"; value: FetchResult; } | { type: "completed"; } | { type: "error"; }; /** * Called when the link is hit, before the request is forwarded. * * Should return the controller for the readable stream. * * This is useful because when starting a query, it's not always * clear if the query will hit the network or will be served from * cache, deduplicated etc. * This allows to inject the "start event" into the stream only * when we know that more chunks will actually follow. */ type OnLinkHitFunction = () => ReadableStreamDefaultController<ReadableStreamLinkEvent>; interface InternalContext$1 { [teeToReadableStreamKey]?: OnLinkHitFunction; [readFromReadableStreamKey]?: ReadableStream<ReadableStreamLinkEvent>; } declare const teeToReadableStreamKey: unique symbol; declare const readFromReadableStreamKey: unique symbol; /** * Apply to a context that will be passed to a link chain containing `TeeToReadableStreamLink`. * @public */ declare function teeToReadableStream<T extends Record<string, any>>(onLinkHit: OnLinkHitFunction, context: T): T & InternalContext$1; /** * Apply to a context that will be passed to a link chain containing `ReadFromReadableStreamLink`. * @public */ declare function readFromReadableStream<T extends Record<string, any>>(readableStream: ReadableStream<ReadableStreamLinkEvent>, context: T): T & InternalContext$1; /** * A link that allows the request to be cloned into a readable stream, e.g. for * transport of multipart responses from RSC or a server loader to the browser. * @public */ declare class TeeToReadableStreamLink extends ApolloLink { constructor(); } /** * A link that allows the response to be read from a readable stream, e.g. for * hydration of a multipart response from RSC or a server loader in the browser. * @public */ declare class ReadFromReadableStreamLink extends ApolloLink { constructor(); } type TransportedOptions = { query: string; } & Omit<WatchQueryOptions, "query">; type JsonString<Encoded> = string & { __jsonString?: [Encoded]; }; type RestrictedPreloadOptions = { fetchPolicy?: "network-only" | "cache-and-network" | "cache-first"; returnPartialData?: false; nextFetchPolicy?: undefined; pollInterval?: undefined; }; /** @public */ type PreloadTransportedQueryOptions<TVariables, TData> = Omit<QueryOptions<TVariables, TData>, "query"> & RestrictedPreloadOptions; type TransportedQueryRefOptions = TransportedOptions & RestrictedPreloadOptions; /** * A `TransportedQueryRef` is an opaque object accessible via renderProp within `PreloadQuery`. * * A child client component reading the `TransportedQueryRef` via useReadQuery will suspend until the promise resolves. * * @public */ interface TransportedQueryRef<TData = unknown, TVariables extends OperationVariables = OperationVariables> extends QueryRef<TData, TVariables> { /** * Temporarily disabled - see https://github.com/apollographql/apollo-client-integrations/issues/332 * * Will now be be `undefined` both in React Server Components and Client Components until we can find a better resolution. */ toPromise?: undefined; /** @internal */ $__apollo_queryRef: { options: TransportedQueryRefOptions; stream: ReadableStream<JsonString<ReadableStreamLinkEvent>>; /** * A unique key for this query, to ensure it is only hydrated once, * even if it should get transported over the wire in a way that results * in multiple objects describing the same queryRef. * This key will be used to store the queryRef in the suspence cache. * * The chances of this happening should be slim (it is handled within * React thanks to https://github.com/facebook/react/pull/28996), but * as we use transported queryRefs with multiple frameworks with distinct * transport mechanisms, this seems like a safe option. */ queryKey: string; }; } /** @public */ interface PreloadTransportedQueryFunction { <TData = unknown, TVariables extends OperationVariables = OperationVariables>(query: DocumentNode | TypedDocumentNode<TData, TVariables>, options?: PreloadTransportedQueryOptions<NoInfer<TVariables>, TData>): TransportedQueryRef<TData, TVariables>; } /** @public */ declare function createTransportedQueryPreloader(client: ApolloClient$1<any>): PreloadTransportedQueryFunction; /** @public */ declare function reviveTransportedQueryRef(queryRef: TransportedQueryRef, client: ApolloClient$1<any>): asserts queryRef is TransportedQueryRef & ReturnType<typeof wrapQueryRef<any, any>>; /** @public */ declare function isTransportedQueryRef(queryRef: any): queryRef is TransportedQueryRef; /** @public */ declare function useWrapTransportedQueryRef<TData, TVariables>(queryRef: QueryRef<TData, TVariables> | TransportedQueryRef): QueryRef<TData, TVariables>; type PreloadQueryOptions<TVariables, TData> = PreloadTransportedQueryOptions<TVariables, TData> & { query: DocumentNode$1 | TypedDocumentNode$1<TData, TVariables>; }; /** * > This export is only available in React Server Components * * Ensures that you can always access the same instance of ApolloClient * during RSC for an ongoing request, while always returning * a new instance for different requests. * * @example * ```ts * export const { getClient, query, PreloadQuery } = registerApolloClient(() => { * return new ApolloClient({ * cache: new InMemoryCache(), * link: new HttpLink({ * uri: "http://example.com/api/graphql", * }), * }); * }); * ``` * * @public */ declare function registerApolloClient<ApolloClientOrPromise extends Promise<ApolloClient$1<any>> | ApolloClient$1<any>>(makeClient: () => ApolloClientOrPromise): { getClient: () => ApolloClientOrPromise; query: Awaited<ApolloClientOrPromise>["query"]; /** * Preloads data in React Server Components to be hydrated * in Client Components. * * ### Example with `queryRef` * `ClientChild` would call `useReadQuery` with the `queryRef`, the `Suspense` boundary is optional: * ```jsx * <PreloadQuery * query={QUERY} * variables={{ * foo: 1 * }} * > * {(queryRef) => ( * <Suspense fallback={<>loading</>}> * <ClientChild queryRef={queryRef} /> * </Suspense> * )} * </PreloadQuery> * ``` * * ### Example for `useSuspenseQuery` * `ClientChild` would call the same query with `useSuspenseQuery`, the `Suspense` boundary is optional: * ```jsx * <PreloadQuery * query={QUERY} * variables={{ * foo: 1 * }} * > * <Suspense fallback={<>loading</>}> * <ClientChild /> * </Suspense> * </PreloadQuery> * ``` */ PreloadQuery: PreloadQueryComponent; }; /** * Props for `PreloadQueryComponent` * @see {@link PreloadQueryComponent} * @public */ interface PreloadQueryProps<TData, TVariables extends OperationVariables> extends PreloadQueryOptions<TVariables, TData> { children: ReactNode | ((queryRef: TransportedQueryRef<NoInfer<TData>, NoInfer<TVariables>>) => ReactNode); } /** * Preloads data in React Server Components to be hydrated * in Client Components. * * ### Example with `queryRef` * `ClientChild` would call `useReadQuery` with the `queryRef`, the `Suspense` boundary is optional: * ```jsx * <PreloadQuery * query={QUERY} * variables={{ * foo: 1 * }} * > * {(queryRef) => ( * <Suspense fallback={<>loading</>}> * <ClientChild queryRef={queryRef} /> * </Suspense> * )} * </PreloadQuery> * ``` * * ### Example for `useSuspenseQuery` * `ClientChild` would call the same query with `useSuspenseQuery`, the `Suspense` boundary is optional: * ```jsx * <PreloadQuery * query={QUERY} * variables={{ * foo: 1 * }} * > * <Suspense fallback={<>loading</>}> * <ClientChild /> * </Suspense> * </PreloadQuery> * ``` * * @public */ interface PreloadQueryComponent { <TData, TVariables extends OperationVariables>(props: PreloadQueryProps<TData, TVariables>): React.ReactElement; } interface AccumulateMultipartResponsesConfig { /** * The maximum delay in milliseconds * from receiving the first response * until the accumulated data will be flushed * and the connection will be closed. */ cutoffDelay: number; } /** * * This link can be used to "debounce" the initial response of a multipart request. Any incremental data received during the `cutoffDelay` time will be merged into the initial response. * * After `cutoffDelay`, the link will return the initial response, even if there is still incremental data pending, and close the network connection. * * If `cutoffDelay` is `0`, the link will immediately return data as soon as it is received, without waiting for incremental data, and immediately close the network connection. * * @example * ```ts * new AccumulateMultipartResponsesLink({ * // The maximum delay in milliseconds * // from receiving the first response * // until the accumulated data will be flushed * // and the connection will be closed. * cutoffDelay: 100, * }); * ``` * * @public */ declare class AccumulateMultipartResponsesLink extends ApolloLink { private maxDelay; constructor(config: AccumulateMultipartResponsesConfig); request(operation: Operation, forward?: NextLink): Observable<FetchResult<Record<string, any>, Record<string, any>, Record<string, any>>>; } interface RemoveMultipartDirectivesConfig { /** * Whether to strip fragments with `@defer` directives * from queries before sending them to the server. * * Defaults to `true`. * * Can be overwritten by adding a label starting * with either `"SsrDontStrip"` or `"SsrStrip"` to the * directive. */ stripDefer?: boolean; } /** * This link will (if called with `stripDefer: true`) strip all `@defer` fragments from your query. * * This is used to prevent the server from doing additional work in SSR scenarios where multipart responses cannot be handled anyways. * * You can exclude certain fragments from this behavior by giving them a label starting with `"SsrDontStrip"`. * The "starting with" is important, because labels have to be unique per operation. So if you have multiple directives where you want to override the default stipping behaviour, * you can do this by annotating them like * ```graphql * query myQuery { * fastField * ... @defer(label: "SsrDontStrip1") { * slowField1 * } * ... @defer(label: "SsrDontStrip2") { * slowField2 * } * } * ``` * * You can also use the link with `stripDefer: false` and mark certain fragments to be stripped by giving them a label starting with `"SsrStrip"`. * * @example * ```ts * new RemoveMultipartDirectivesLink({ * // Whether to strip fragments with `@defer` directives * // from queries before sending them to the server. * // * // Defaults to `true`. * // * // Can be overwritten by adding a label starting * // with either `"SsrDontStrip"` or `"SsrStrip"` to the * // directive. * stripDefer: true, * }); * ``` * * @public */ declare class RemoveMultipartDirectivesLink extends ApolloLink { private stripDirectives; constructor(config: RemoveMultipartDirectivesConfig); request(operation: Operation, forward?: NextLink): Observable<{}>; } interface SSRMultipartLinkConfig { /** * Whether to strip fragments with `@defer` directives * from queries before sending them to the server. * * Defaults to `true`. * * Can be overwritten by adding a label starting * with either `"SsrDontStrip"` or `"SsrStrip"` to the * directive. */ stripDefer?: boolean; /** * The maximum delay in milliseconds * from receiving the first response * until the accumulated data will be flushed * and the connection will be closed. * * Defaults to `0`. */ cutoffDelay?: number; } /** * A convenient combination of `RemoveMultipartDirectivesLink` and `AccumulateMultipartResponsesLink`. * * @example * ```ts * new SSRMultipartLink({ * // Whether to strip fragments with `@defer` directives * // from queries before sending them to the server. * // * // Defaults to `true`. * // * // Can be overwritten by adding a label starting * // with either `"SsrDontStrip"` or `"SsrStrip"` to the * // directive. * stripDefer: true, * // The maximum delay in milliseconds * // from receiving the first response * // until the accumulated data will be flushed * // and the connection will be closed. * // * // Defaults to `0`. * // * cutoffDelay: 100, * }); * ``` * * @public */ declare class SSRMultipartLink extends ApolloLink { constructor(config?: SSRMultipartLinkConfig); } declare const sourceSymbol: unique symbol; /** * A version of `InMemoryCache` to be used with streaming SSR. * * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/cache/InMemoryCache | the Apollo Client API documentation}. * * @public */ declare class InMemoryCache extends InMemoryCache$1 { /** * Information about the current package and it's export names, for use in error messages. * * @internal */ static readonly info: { pkg: string; }; [sourceSymbol]: string; constructor(config?: InMemoryCacheConfig | undefined); } interface DataTransportAbstraction { /** * This hook should always return the first value it was called with. * * If used in the browser and SSR happened, it should return the value passed to it on the server. */ useStaticValueRef<T>(value: T): { current: T; }; } /** * > This export is only available in React Client Components * * If you create a custom data transport, you need to wrap the child tree in a * `DataTransportContext.Provider` and provide the `DataTransportAbstraction` to it. * * See for example * https://github.com/apollographql/apollo-client-integrations/blob/37feeaa9aea69b90a974eb9cd0fbd636b62d841a/integration-test/experimental-react/src/WrappedApolloProvider.tsx * * @public */ declare const DataTransportContext: React.Context<DataTransportAbstraction | null>; /** * Interface to be implemented by a custom data transport component, * for usage with `WrapApolloProvider`. * * This component needs to provide a `DataTransportContext` to it's children. * * See for example * https://github.com/apollographql/apollo-client-integrations/blob/37feeaa9aea69b90a974eb9cd0fbd636b62d841a/integration-test/experimental-react/src/WrappedApolloProvider.tsx * * @public */ type DataTransportProviderImplementation<ExtraProps = {}> = React.FC<{ /** will be present in the Browser */ onQueryEvent?: (event: QueryEvent) => void; /** will be present in the Browser */ rerunSimulatedQueries?: () => void; /** will be present during SSR */ registerDispatchRequestStarted?: (callback: (query: { event: Extract<QueryEvent, { type: "started"; }>; observable: Observable$1<Exclude<QueryEvent, { type: "started"; }>>; }) => void) => void; /** will always be present */ children: React.ReactNode; } & ExtraProps>; type TransportIdentifier = string & { __transportIdentifier: true; }; /** * Events that will be emitted by a wrapped ApolloClient instance during * SSR on `DataTransportProviderImplementation.registerDispatchRequestStarted`, * to be transported to the browser and replayed there using * `DataTransportProviderImplementation.onQueryEvent`. * * @public */ type QueryEvent = { type: "started"; options: TransportedOptions; id: TransportIdentifier; } | (ReadableStreamLinkEvent & { id: TransportIdentifier; }); type ProgressEvent = Exclude<QueryEvent, { type: "started"; }>; type SimulatedQueryInfo = { controller: ReadableStreamDefaultController<ReadableStreamLinkEvent>; options: WatchQueryOptions<OperationVariables, any>; }; interface WrappedApolloClientOptions extends Omit<ApolloClientOptions<NormalizedCacheObject>, "cache" | "ssrMode" | "ssrForceFetchDelay"> { cache: InMemoryCache; } declare class ApolloClientBase extends ApolloClient$1<NormalizedCacheObject> { /** * Information about the current package and it's export names, for use in error messages. * * @internal */ static readonly info: { pkg: string; }; [sourceSymbol]: string; constructor(options: WrappedApolloClientOptions); setLink(newLink: ApolloLink): void; } declare class ApolloClientClientBaseImpl extends ApolloClientBase { constructor(options: WrappedApolloClientOptions); private simulatedStreamingQueries; onQueryStarted({ options, id }: Extract<QueryEvent, { type: "started"; }>): void; onQueryProgress: (event: ProgressEvent) => void; /** * Can be called when the stream closed unexpectedly while there might still be unresolved * simulated server-side queries going on. * Those queries will be cancelled and then re-run in the browser. */ rerunSimulatedQueries: () => void; rerunSimulatedQuery: (queryInfo: SimulatedQueryInfo) => void; } declare const skipDataTransportKey: unique symbol; interface InternalContext { [skipDataTransportKey]?: boolean; } /** * Apply to a context to prevent this operation from being transported over the SSR data transport mechanism. * @public */ declare function skipDataTransport<T extends Record<string, any>>(context: T): T & InternalContext; declare class ApolloClientSSRImpl extends ApolloClientClientBaseImpl { watchQueryQueue: { push: (value: { event: Extract<QueryEvent, { type: "started"; }>; observable: Observable$1<Exclude<QueryEvent, { type: "started"; }>>; }) => void; register: (callback: ((value: { event: Extract<QueryEvent, { type: "started"; }>; observable: Observable$1<Exclude<QueryEvent, { type: "started"; }>>; }) => void) | null) => void; }; pushEventStream(options: WatchQueryOptions<any, any>): ReadableStreamDefaultController<ReadableStreamLinkEvent>; watchQuery<T = any, TVariables extends OperationVariables = OperationVariables>(options: WatchQueryOptions<TVariables, T>): _apollo_client_index_js.ObservableQuery<T, TVariables>; } declare const ApolloClient_base: typeof ApolloClientBase; /** * A version of `ApolloClient` to be used with streaming SSR or in React Server Components. * * For more documentation, please see {@link https://www.apollographql.com/docs/react/api/core/ApolloClient | the Apollo Client API documentation}. * * @public */ declare class ApolloClient<Ignored = NormalizedCacheObject> extends ApolloClient_base implements Partial<ApolloClientSSRImpl> { /** @internal */ onQueryStarted?: ApolloClientSSRImpl["onQueryStarted"]; /** @internal */ onQueryProgress?: ApolloClientSSRImpl["onQueryProgress"]; /** @internal */ rerunSimulatedQueries?: ApolloClientSSRImpl["rerunSimulatedQueries"]; /** @internal */ rerunSimulatedQuery?: ApolloClientSSRImpl["rerunSimulatedQuery"]; /** @internal */ watchQueryQueue?: ApolloClientSSRImpl["watchQueryQueue"]; } /** * > This export is only available in React Client Components * * Resets the singleton instances created for the Apollo SSR data transport and caches. * * To be used in testing only, like * ```ts * afterEach(resetApolloSingletons); * ``` * * @public */ declare function resetApolloSingletons(): void; declare const ApolloClientSingleton: unique symbol; declare global { interface Window { [ApolloClientSingleton]?: ApolloClient<any>; } } /** * > This is only available in React Client Components * * A version of `ApolloProvider` particularly suited for React's streaming SSR. * * @public */ interface WrappedApolloProvider<ExtraProps> { ({ makeClient, children, ...extraProps }: React.PropsWithChildren<{ makeClient: () => ApolloClient<any>; } & ExtraProps>): React.JSX.Element; /** * Information about the current package and it's export names, for use in error messages. */ info: { pkg: string; }; } /** * > This export is only available in React Client Components * * Creates an ApolloProvider for streaming SSR. * * @param TransportProvider - The transport provider to be used. * This could e.g. be a `ManualDataTransport` created by `buildManualDataTransport`, * or a fully custom implementation of `DataTransportProviderImplementation`. * @public */ declare function WrapApolloProvider<ExtraProps>(TransportProvider: DataTransportProviderImplementation<ExtraProps>): WrappedApolloProvider<ExtraProps>; export { ApolloClient, DataTransportContext, type DataTransportProviderImplementation, AccumulateMultipartResponsesLink as DebounceMultipartResponsesLink, InMemoryCache, type PreloadQueryComponent, type PreloadQueryProps, type PreloadTransportedQueryFunction, type PreloadTransportedQueryOptions, type QueryEvent, ReadFromReadableStreamLink, type ReadableStreamLinkEvent, RemoveMultipartDirectivesLink, SSRMultipartLink, TeeToReadableStreamLink, type TransportedQueryRef, WrapApolloProvider, type WrappedApolloProvider, createTransportedQueryPreloader, isTransportedQueryRef, readFromReadableStream, registerApolloClient, resetApolloSingletons, reviveTransportedQueryRef, skipDataTransport, teeToReadableStream, useWrapTransportedQueryRef };