UNPKG

@urql/core

Version:

The shared core for the highly customizable and versatile GraphQL client

564 lines (546 loc) 27.8 kB
import { AnyVariables, TypedDocumentNode, DocumentNode, GraphQLError, Exchange, FetchBody, Operation, ExecutionResult, DefinitionNode, DocumentInput, RequestExtensions, GraphQLRequest, OperationResult, FormattedNode, OperationType, OperationContext, CombinedError } from './urql-core-chunk.js'; export { CacheOutcome, Client, ClientOptions, DebugEvent, DebugEventArg, DebugEventTypes, ErrorLike, ExchangeIO, ExchangeInput, GraphQLRequestParams, IncrementalPayload, OperationDebugMeta, OperationInstance, OperationResultSource, PersistedDocument, PersistedRequestExtensions, RequestPolicy, createClient } from './urql-core-chunk.js'; /** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}. * * @remarks * The `gql` tag or function is used to parse a GraphQL query document into a {@link DocumentNode}. * * When used as a tagged template, `gql` will automatically merge fragment definitions into the resulting * document and deduplicate them. * * It enforces that all fragments have a unique name. When fragments with different definitions share a name, * it will log a warning in development. * * Hint: It’s recommended to use this `gql` function over other GraphQL parse functions, since it puts the parsed * results directly into `@urql/core`’s internal caches and prevents further unnecessary work. * * @example * ```ts * const AuthorFragment = gql` * fragment AuthorDisplayComponent on Author { * id * name * } * `; * * const BookFragment = gql` * fragment ListBookComponent on Book { * id * title * author { * ...AuthorDisplayComponent * } * } * * ${AuthorFragment} * `; * * const BookQuery = gql` * query Book($id: ID!) { * book(id: $id) { * ...BookFragment * } * } * * ${BookFragment} * `; * ``` */ declare function gql<Data = any, Variables extends AnyVariables = AnyVariables>(strings: TemplateStringsArray, ...interpolations: Array<TypedDocumentNode | DocumentNode | string>): TypedDocumentNode<Data, Variables>; declare function gql<Data = any, Variables extends AnyVariables = AnyVariables>(string: string): TypedDocumentNode<Data, Variables>; /** A serialized version of an {@link OperationResult}. * * @remarks * All properties are serialized separately as JSON strings, except for the * {@link CombinedError} to speed up JS parsing speed, even if a result doesn’t * end up being used. * * @internal */ interface SerializedResult { hasNext?: boolean; /** JSON-serialized version of {@link OperationResult.data}. */ data?: string | undefined; /** JSON-serialized version of {@link OperationResult.extensions}. */ extensions?: string | undefined; /** JSON version of {@link CombinedError}. */ error?: { graphQLErrors: Array<Partial<GraphQLError> | string>; networkError?: string; }; } /** A dictionary of {@link Operation.key} keys to serializable {@link SerializedResult} objects. * * @remarks * It’s not recommended to modify the serialized data manually, however, multiple payloads of * this dictionary may safely be merged and combined. */ interface SSRData { [key: string]: SerializedResult; } /** Options for the `ssrExchange` allowing it to either operate on the server- or client-side. */ interface SSRExchangeParams { /** Indicates to the {@link SSRExchange} whether it's currently in server-side or client-side mode. * * @remarks * Depending on this option, the {@link SSRExchange} will either capture or replay results. * When `true`, it’s in client-side mode and results will be serialized. When `false`, it’ll * use its deserialized data and replay results from it. */ isClient?: boolean; /** May be used on the client-side to pass the {@link SSRExchange} serialized data from the server-side. * * @remarks * Alternatively, {@link SSRExchange.restoreData} may be called to imperatively add serialized data to * the exchange. * * Hint: This method also works on the server-side to add to the initial serialized data, which enables * you to combine multiple {@link SSRExchange} results, as needed. */ initialState?: SSRData; /** Forces a new API request to be sent in the background after replaying the deserialized result. * * @remarks * Similarly to the `cache-and-network` {@link RequestPolicy}, this option tells the {@link SSRExchange} * to send a new API request for the {@link Operation} after replaying a serialized result. * * Hint: This is useful when you're caching SSR results and need the client-side to update itself after * rendering the initial serialized SSR results. */ staleWhileRevalidate?: boolean; /** Forces {@link OperationResult.extensions} to be serialized alongside the rest of a result. * * @remarks * Entries in the `extension` object of a GraphQL result are often non-standard metdata, and many * APIs use it for data that changes between every request. As such, the {@link SSRExchange} will * not serialize this data by default, unless this flag is set. */ includeExtensions?: boolean; } /** An `SSRExchange` either in server-side mode, serializing results, or client-side mode, deserializing and replaying results.. * * @remarks * This same {@link Exchange} is used in your code both for the client-side and server-side as it’s “universal” * and can be put into either client-side or server-side mode using the {@link SSRExchangeParams.isClient} flag. * * In server-side mode, the `ssrExchange` will “record” results it sees from your API and provide them for you * to send to the client-side using the {@link SSRExchange.extractData} method. * * In client-side mode, the `ssrExchange` will use these serialized results, rehydrated either using * {@link SSRExchange.restoreData} or {@link SSRexchangeParams.initialState}, to replay results the * server-side has seen and sent before. * * Each serialized result will only be replayed once, as it’s assumed that your cache exchange will have the * results cached afterwards. */ interface SSRExchange extends Exchange { /** Client-side method to add serialized results to the {@link SSRExchange}. * @param data - {@link SSRData}, */ restoreData(data: SSRData): void; /** Server-side method to get all serialized results the {@link SSRExchange} has captured. * @returns an {@link SSRData} dictionary. */ extractData(): SSRData; } /** Creates a server-side rendering `Exchange` that either captures responses on the server-side or replays them on the client-side. * * @param params - An {@link SSRExchangeParams} configuration object. * @returns the created {@link SSRExchange} * * @remarks * When dealing with server-side rendering, we essentially have two {@link Client | Clients} making requests, * the server-side client, and the client-side one. The `ssrExchange` helps implementing a tiny cache on both * sides that: * * - captures results on the server-side which it can serialize, * - replays results on the client-side that it deserialized from the server-side. * * Hint: The `ssrExchange` is basically an exchange that acts like a replacement for any fetch exchange * temporarily. As such, you should place it after your cache exchange but in front of any fetch exchange. */ declare const ssrExchange: (params?: SSRExchangeParams) => SSRExchange; /** Default document cache exchange. * * @remarks * The default document cache in `urql` avoids sending the same GraphQL request * multiple times by caching it using the {@link Operation.key}. It will invalidate * query results automatically whenever it sees a mutation responses with matching * `__typename`s in their responses. * * The document cache will get the introspected `__typename` fields by modifying * your GraphQL operation documents using the {@link formatDocument} utility. * * This automatic invalidation strategy can fail if your query or mutation don’t * contain matching typenames, for instance, because the query contained an * empty list. * You can manually add hints for this exchange by specifying a list of * {@link OperationContext.additionalTypenames} for queries and mutations that * should invalidate one another. * * @see {@link https://urql.dev/goto/docs/basics/document-caching} for more information on this cache. */ declare const cacheExchange: Exchange; /** An abstract observer-like interface. * * @remarks * Observer-like interfaces are passed to {@link ObservableLike.subscribe} to provide them * with callbacks for their events. * * @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal. */ interface ObserverLike<T> { /** Callback for values an {@link ObservableLike} emits. */ next: (value: T) => void; /** Callback for an error an {@link ObservableLike} emits, which ends the subscription. */ error: (err: any) => void; /** Callback for the completion of an {@link ObservableLike}, which ends the subscription. */ complete: () => void; } /** An abstract observable-like interface. * * @remarks * Observable, or Observable-like interfaces, are often used by GraphQL transports to abstract * how they send {@link ExecutionResult | ExecutionResults} to consumers. These generally contain * a `subscribe` method accepting an {@link ObserverLike} structure. * * @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal. */ interface ObservableLike<T> { /** Start the Observable-like subscription and returns a subscription handle. * * @param observer - an {@link ObserverLike} object with result, error, and completion callbacks. * @returns a subscription handle providing an `unsubscribe` method to stop the subscription. */ subscribe(observer: ObserverLike<T>): { unsubscribe: () => void; }; } /** A more cross-compatible version of the {@link GraphQLRequest} structure. * {@link FetchBody} for more details */ type SubscriptionOperation = FetchBody; /** A subscription forwarding function, which must accept a {@link SubscriptionOperation}. * * @param operation - A {@link SubscriptionOperation} * @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}. */ type SubscriptionForwarder = (request: FetchBody, operation: Operation) => ObservableLike<ExecutionResult>; /** This is called to create a subscription and needs to be hooked up to a transport client. */ interface SubscriptionExchangeOpts { /** A subscription forwarding function, which must accept a {@link SubscriptionOperation}. * * @param operation - A {@link SubscriptionOperation} * @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}. * * @remarks * This callback is called for each {@link Operation} that this `subscriptionExchange` will * handle. It receives the {@link SubscriptionOperation}, which is a more compatible version * of the raw {@link Operation} objects and must return an {@link ObservableLike} of results. */ forwardSubscription: SubscriptionForwarder; /** Flag to enable this exchange to handle all types of GraphQL operations. * * @remarks * When you aren’t using fetch exchanges and GraphQL over HTTP as a transport for your GraphQL requests, * or you have a third-party GraphQL transport implementation, which must also be used for queries and * mutations, this flag may be used to allow this exchange to handle all kinds of GraphQL operations. * * By default, this flag is `false` and the exchange will only handle GraphQL subscription operations. */ enableAllOperations?: boolean; /** A predicate function that causes an operation to be handled by this `subscriptionExchange` if `true` is returned. * * @param operation - an {@link Operation} * @returns true when the operation is handled by this exchange. * * @remarks * In some cases, a `subscriptionExchange` will be used to only handle some {@link Operation | Operations}, * e.g. all that contain `@live` directive. For these cases, this function may be passed to precisely * determine which `Operation`s this exchange should handle, instead of forwarding. * * When specified, the {@link SubscriptionExchangeOpts.enableAllOperations} flag is disregarded. */ isSubscriptionOperation?: (operation: Operation) => boolean; } /** Generic subscription exchange factory used to either create an exchange handling just subscriptions or all operation kinds. * * @remarks * `subscriptionExchange` can be used to create an {@link Exchange} that either * handles just GraphQL subscription operations, or optionally all operations, * when the {@link SubscriptionExchangeOpts.enableAllOperations} flag is passed. * * The {@link SubscriptionExchangeOpts.forwardSubscription} function must * be provided and provides a generic input that's based on {@link Operation} * but is compatible with many libraries implementing GraphQL request or * subscription interfaces. */ declare const subscriptionExchange: ({ forwardSubscription, enableAllOperations, isSubscriptionOperation, }: SubscriptionExchangeOpts) => Exchange; /** Simple log debugger exchange. * * @remarks * An exchange that logs incoming {@link Operation | Operations} and * {@link OperationResult | OperationResults} in development. * * This exchange is a no-op in production and often used in issue reporting * to understand certain usage patterns of `urql` without having access to * the original source code. * * Hint: When you report an issue you’re having with `urql`, adding * this as your first exchange and posting its output can speed up * issue triaging a lot! */ declare const debugExchange: Exchange; /** Default GraphQL over HTTP fetch exchange. * * @remarks * The default fetch exchange in `urql` supports sending GraphQL over HTTP * requests, can optionally send GraphQL queries as GET requests, and * handles incremental multipart responses. * * This exchange does not handle persisted queries or multipart uploads. * Support for the former can be added using `@urql/exchange-persisted-fetch` * and the latter using `@urql/exchange-multipart-fetch`. * * Hint: The `fetchExchange` and the two other exchanges all use the built-in fetch * utilities in `@urql/core/internal`, which you can also use to implement * a customized fetch exchange. * * @see {@link makeFetchSource} for the shared utility calling the Fetch API. */ declare const fetchExchange: Exchange; /** Composes an array of Exchanges into a single one. * * @param exchanges - An array of {@link Exchange | Exchanges}. * @returns - A composed {@link Exchange}. * * @remarks * `composeExchanges` returns an {@link Exchange} that when instantiated * composes the array of passed `Exchange`s into one, calling them from * right to left, with the prior `Exchange`’s {@link ExchangeIO} function * as the {@link ExchangeInput.forward} input. * * This simply merges all exchanges into one and is used by the {@link Client} * to merge the `exchanges` option it receives. * * @throws * In development, if {@link ExchangeInput.forward} is called repeatedly * by an {@link Exchange} an error is thrown, since `forward()` must only * be called once per `Exchange`. */ declare const composeExchanges: (exchanges: Exchange[]) => Exchange; /** A cached printing function for GraphQL documents. * * @param node - A string of a document or a {@link DocumentNode} * @returns A normalized printed string of the passed GraphQL document. * * @remarks * This function accepts a GraphQL query string or {@link DocumentNode}, * then prints and sanitizes it. The sanitizer takes care of removing * comments, which otherwise alter the key of the document although the * document is otherwise equivalent to another. * * When a {@link DocumentNode} is passed to this function, it caches its * output by modifying the `loc.source.body` property on the GraphQL node. */ declare const stringifyDocument: (node: string | DefinitionNode | DocumentNode) => string; /** Creates a `GraphQLRequest` from the passed parameters. * * @param q - A string of a document or a {@link DocumentNode} * @param variables - A variables object for the defined GraphQL operation. * @returns A {@link GraphQLRequest} * * @remarks * `createRequest` creates a {@link GraphQLRequest} from the passed parameters, * while replacing the document as needed with a canonical version of itself, * to avoid parsing, printing, or hashing the same input multiple times. * * If no variables are passed, canonically it'll default to an empty object, * which is removed from the resulting hash key. */ declare const createRequest: <Data = any, Variables extends AnyVariables = AnyVariables>(_query: DocumentInput<Data, Variables>, _variables: Variables, extensions?: RequestExtensions | undefined) => GraphQLRequest<Data, Variables>; /** Returns the name of the `DocumentNode`'s operation, if any. * @param query - A {@link DocumentNode} * @returns the operation's name contained within the document, or `undefined` */ declare const getOperationName: (query: DocumentNode) => string | undefined; /** Converts the `ExecutionResult` received for a given `Operation` to an `OperationResult`. * * @param operation - The {@link Operation} for which the API’s result is for. * @param result - The GraphQL API’s {@link ExecutionResult}. * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). * @returns An {@link OperationResult}. * * @remarks * This utility can be used to create {@link OperationResult | OperationResults} in the shape * that `urql` expects and defines, and should be used rather than creating the results manually. * * @throws * If no data, or errors are contained within the result, or the result is instead an incremental * response containing a `path` property, a “No Content” error is thrown. * * @see {@link ExecutionResult} for the type definition of GraphQL API results. */ declare const makeResult: (operation: Operation, result: ExecutionResult, response?: any) => OperationResult; /** Merges an incrementally delivered `ExecutionResult` into a previous `OperationResult`. * * @param prevResult - The {@link OperationResult} that preceded this result. * @param path - The GraphQL API’s {@link ExecutionResult} that should be patching the `prevResult`. * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). * @returns A new {@link OperationResult} patched with the incremental result. * * @remarks * This utility should be used to merge subsequent {@link ExecutionResult | ExecutionResults} of * incremental responses into a prior {@link OperationResult}. * * When directives like `@defer`, `@stream`, and `@live` are used, GraphQL may deliver new * results that modify previous results. In these cases, it'll set a `path` property to modify * the result it sent last. This utility is built to handle these cases and merge these payloads * into existing {@link OperationResult | OperationResults}. * * @see {@link ExecutionResult} for the type definition of GraphQL API results. */ declare const mergeResultPatch: (prevResult: OperationResult, nextResult: ExecutionResult, response?: any, pending?: ExecutionResult["pending"]) => OperationResult; /** Creates an `OperationResult` containing a network error for requests that encountered unexpected errors. * * @param operation - The {@link Operation} for which the API’s result is for. * @param error - The network-like error that prevented an API result from being delivered. * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). * @returns An {@link OperationResult} containing only a {@link CombinedError}. * * @remarks * This utility can be used to create {@link OperationResult | OperationResults} in the shape * that `urql` expects and defines, and should be used rather than creating the results manually. * This function should be used for when the {@link CombinedError.networkError} property is * populated and no GraphQL execution actually occurred. */ declare const makeErrorResult: (operation: Operation, error: Error, response?: any) => OperationResult; /** A stable stringifier for GraphQL variables objects. * * @param x - any JSON-like data. * @return A JSON string. * * @remarks * This utility creates a stable JSON string from any passed data, * and protects itself from throwing. * * The JSON string is stable insofar as objects’ keys are sorted, * and instances of non-plain objects are replaced with random keys * replacing their values, which remain stable for the objects’ * instance. */ declare const stringifyVariables: (x: any, includeFiles?: boolean) => string; /** Formats a GraphQL document to add `__typename` fields and process client-side directives. * * @param node - a {@link DocumentNode}. * @returns a {@link FormattedDocument} * * @remarks * Cache {@link Exchange | Exchanges} will require typename introspection to * recognize types in a GraphQL response. To retrieve these typenames, * this function is used to add the `__typename` fields to non-root * selection sets of a GraphQL document. * * Additionally, this utility will process directives, filter out client-side * directives starting with an `_` underscore, and place a `_directives` dictionary * on selection nodes. * * This utility also preserves the internally computed key of the * document as created by {@link createRequest} to avoid any * formatting from being duplicated. * * @see {@link https://spec.graphql.org/October2021/#sec-Type-Name-Introspection} for more information * on typename introspection via the `__typename` field. */ declare const formatDocument: <T extends TypedDocumentNode<any, any>>(node: T) => FormattedNode<T>; /** Creates a {@link Operation} from the given parameters. * * @param kind - The {@link OperationType} of GraphQL operation, i.e. `query`, `mutation`, or `subscription`. * @param request - The {@link GraphQLRequest} or {@link Operation} used as a template for the new `Operation`. * @param context - The {@link OperationContext} `context` data for the `Operation`. * @returns A new {@link Operation}. * * @remarks * This method is both used to create new {@link Operation | Operations} as well as copy and modify existing * operations. While it’s not required to use this function to copy an `Operation`, it is recommended, in case * additional dynamic logic is added to them in the future. * * Hint: When an {@link Operation} is passed to the `request` argument, the `context` argument does not have to be * a complete {@link OperationContext} and will instead be combined with passed {@link Operation.context}. * * @example * An example of copying an existing `Operation` to modify its `context`: * * ```ts * makeOperation( * operation.kind, * operation, * { requestPolicy: 'cache-first' }, * ); * ``` */ declare function makeOperation<Data = any, Variables extends AnyVariables = AnyVariables>(kind: OperationType, request: GraphQLRequest<Data, Variables>, context: OperationContext): Operation<Data, Variables>; declare function makeOperation<Data = any, Variables extends AnyVariables = AnyVariables>(kind: OperationType, request: Operation<Data, Variables>, context?: Partial<OperationContext>): Operation<Data, Variables>; /** Options for the `mapExchange` allowing it to react to incoming operations, results, or errors. */ interface MapExchangeOpts { /** Accepts a callback for incoming `Operation`s. * * @param operation - An {@link Operation} that the {@link mapExchange} received. * @returns optionally a new {@link Operation} replacing the original. * * @remarks * You may return new {@link Operation | Operations} from this function replacing * the original that the {@link mapExchange} received. * It’s recommended that you use the {@link makeOperation} utility to create a copy * of the original when you do this. (However, this isn’t required) * * Hint: The callback may also be promisified and return a new {@link Operation} asynchronously, * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, * like after your `cacheExchange`. */ onOperation?(operation: Operation): Promise<Operation> | Operation | void; /** Accepts a callback for incoming `OperationResult`s. * * @param result - An {@link OperationResult} that the {@link mapExchange} received. * @returns optionally a new {@link OperationResult} replacing the original. * * @remarks * This callback may optionally return a new {@link OperationResult} that replaces the original, * which you can use to modify incoming API results. * * Hint: The callback may also be promisified and return a new {@link Operation} asynchronously, * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, * like after your `cacheExchange`. */ onResult?(result: OperationResult): Promise<OperationResult> | OperationResult | void; /** Accepts a callback for incoming `CombinedError`s. * * @param error - A {@link CombinedError} that an incoming {@link OperationResult} contained. * @param operation - The {@link Operation} of the incoming {@link OperationResult}. * * @remarks * The callback may also be promisified and return a new {@link Operation} asynchronously, * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, * like after your `cacheExchange`. */ onError?(error: CombinedError, operation: Operation): void; } /** Creates an `Exchange` mapping over incoming operations, results, and/or errors. * * @param opts - A {@link MapExchangeOpts} configuration object, containing the callbacks the `mapExchange` will use. * @returns the created {@link Exchange} * * @remarks * The `mapExchange` may be used to react to or modify incoming {@link Operation | Operations} * and {@link OperationResult | OperationResults}. Optionally, it can also modify these * asynchronously, when a promise is returned from the callbacks. * * This is useful to, for instance, add an authentication token to a given request, when * the `@urql/exchange-auth` package would be overkill. * * It can also accept an `onError` callback, which can be used to react to incoming * {@link CombinedError | CombinedErrors} on results, and trigger side-effects. * */ declare const mapExchange: ({ onOperation, onResult, onError, }: MapExchangeOpts) => Exchange; export { AnyVariables, CombinedError, DocumentInput, Exchange, ExecutionResult, FormattedNode, GraphQLRequest, MapExchangeOpts, Operation, OperationContext, OperationResult, OperationType, RequestExtensions, SSRData, SSRExchange, SSRExchangeParams, SerializedResult, SubscriptionExchangeOpts, SubscriptionForwarder, SubscriptionOperation, TypedDocumentNode, cacheExchange, composeExchanges, createRequest, debugExchange, mapExchange as errorExchange, fetchExchange, formatDocument, getOperationName, gql, makeErrorResult, makeOperation, makeResult, mapExchange, mergeResultPatch, ssrExchange, stringifyDocument, stringifyVariables, subscriptionExchange };