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

520 lines (503 loc) 18.8 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, NormalizedCacheObject, Observable as Observable$1, 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 { [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; /** * 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; /** * 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; 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); } 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 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"]; } declare const ApolloClientSingleton: unique symbol; declare global { interface Window { [ApolloClientSingleton]?: ApolloClient<any>; } } export { ApolloClient, AccumulateMultipartResponsesLink as DebounceMultipartResponsesLink, InMemoryCache, type PreloadQueryComponent, type PreloadQueryProps, type PreloadTransportedQueryFunction, type PreloadTransportedQueryOptions, ReadFromReadableStreamLink, type ReadableStreamLinkEvent, RemoveMultipartDirectivesLink, SSRMultipartLink, TeeToReadableStreamLink, type TransportedQueryRef, createTransportedQueryPreloader, isTransportedQueryRef, readFromReadableStream, registerApolloClient, reviveTransportedQueryRef, teeToReadableStream };