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

523 lines (507 loc) 19.9 kB
import * as _apollo_client from '@apollo/client'; import { ApolloLink, InMemoryCache as InMemoryCache$1, InMemoryCacheConfig, FetchPolicy, ApolloClient as ApolloClient$1, Observable as Observable$1, OperationVariables, DocumentNode, TypedDocumentNode } from '@apollo/client'; import { Observable } from '@apollo/client/utilities'; import * as rxjs from 'rxjs'; import React from 'react'; import { wrapQueryRef } from '@apollo/client/react/internal'; import { QueryRef } from '@apollo/client/react'; 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: ApolloLink.Operation, forward?: ApolloLink.ForwardFunction): Observable<ApolloLink.Result<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: ApolloLink.Operation, forward?: ApolloLink.ForwardFunction): rxjs.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); } type TransportedOptions = { query: string; nextFetchPolicy?: FetchPolicy | undefined; } & Omit<ApolloClient$1.WatchQueryOptions, "query" | "skipPollAttempt" | "nextFetchPolicy">; /** * @internal */ type ReadableStreamLinkEvent = { type: "next"; value: ApolloLink.Result; } | { 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(); } 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: ApolloClient$1.WatchQueryOptions<any, OperationVariables>; }; declare class ApolloClientBase extends ApolloClient$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(options: ApolloClient.Options); setLink(newLink: ApolloLink): void; } declare class ApolloClientClientBaseImpl extends ApolloClientBase { constructor(options: ApolloClient.Options); 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: ApolloClient$1.WatchQueryOptions<any, any>): ReadableStreamDefaultController<ReadableStreamLinkEvent>; watchQuery<TData = any, TVariables extends OperationVariables = OperationVariables>(options: ApolloClient$1.WatchQueryOptions<TData, TVariables>): _apollo_client.ObservableQuery<TData, TVariables>; } declare const ApolloClient_base: typeof ApolloClientBase; /** @public */ declare namespace ApolloClient { /** @public */ interface Options extends Omit<ApolloClient$1.Options, "cache" | "ssrMode" | "ssrForceFetchDelay"> { cache: InMemoryCache; } } /** * 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 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; } } /** * > 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; } & 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>; 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<TData, TVariables extends OperationVariables> = Omit<ApolloClient$1.QueryOptions<TData, TVariables>, "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<TData, NoInfer<TVariables>>): TransportedQueryRef<TData, TVariables>; } /** @public */ declare function createTransportedQueryPreloader(client: ApolloClient$1, { prepareForReuse, notTransportedOptionOverrides, }?: { /** * Set this to `true` to indicate that this `queryRef` will be reused within the same process with the same Apollo Client instance without being dehydrated and hydrated. * In that case, it will already be written to the suspense cache so it doesn't need to be hydrated by re-running the query with a fake network request. */ prepareForReuse?: boolean; /** * Overrides to the options that should happen only in the `preloader` call, but should not be transported/hydrated on the client. */ notTransportedOptionOverrides?: Partial<ApolloClient$1.WatchQueryOptions<any, any>>; }): PreloadTransportedQueryFunction; /** @public */ declare function reviveTransportedQueryRef(queryRef: TransportedQueryRef, client: ApolloClient$1): asserts queryRef is TransportedQueryRef & ReturnType<typeof wrapQueryRef>; /** @public */ declare function isTransportedQueryRef(queryRef: any): queryRef is TransportedQueryRef; /** @public */ declare function useWrapTransportedQueryRef<TData>(queryRef: QueryRef<TData, any, "complete" | "streaming" | "empty" | "partial"> | TransportedQueryRef): QueryRef<TData>; export { ApolloClient, DataTransportContext, type DataTransportProviderImplementation, AccumulateMultipartResponsesLink as DebounceMultipartResponsesLink, InMemoryCache, type PreloadTransportedQueryFunction, type PreloadTransportedQueryOptions, type QueryEvent, ReadFromReadableStreamLink, type ReadableStreamLinkEvent, RemoveMultipartDirectivesLink, SSRMultipartLink, TeeToReadableStreamLink, type TransportedQueryRef, WrapApolloProvider, type WrappedApolloProvider, createTransportedQueryPreloader, isTransportedQueryRef, readFromReadableStream, resetApolloSingletons, reviveTransportedQueryRef, skipDataTransport, teeToReadableStream, useWrapTransportedQueryRef };