Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(core): Add TSDocs annotations to @urql/core exported APIs #2962

Merged
merged 18 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/giant-tables-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/core': patch
---

Add TSDoc annotations to all external `@urql/core` APIs.
440 changes: 423 additions & 17 deletions packages/core/src/client.ts

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion packages/core/src/exchanges/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ type OperationCache = Map<string, Set<number>>;
const shouldSkip = ({ kind }: Operation) =>
kind !== 'mutation' && kind !== 'query';

/** 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://formidable.com/open-source/urql/docs/basics/document-caching/} for more information on this cache.
*/
export const cacheExchange: Exchange = ({ forward, client, dispatchDebug }) => {
const resultCache: ResultCache = new Map();
const operationCache: OperationCache = new Map();
Expand Down Expand Up @@ -148,7 +168,9 @@ export const cacheExchange: Exchange = ({ forward, client, dispatchDebug }) => {
};
};

// Reexecutes a given operation with the default requestPolicy
/** Reexecutes an `Operation` with the `network-only` request policy.
* @internal
*/
export const reexecuteOperation = (client: Client, operation: Operation) => {
return client.reexecuteOperation(
makeOperation(operation.kind, operation, {
Expand Down
21 changes: 17 additions & 4 deletions packages/core/src/exchanges/compose.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { Exchange, ExchangeInput } from '../types';
import type { ExchangeIO, Exchange, ExchangeInput } from '../types';

/** This composes an array of Exchanges into a single ExchangeIO function */
export const composeExchanges = (exchanges: 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.
*/
export const composeExchanges = (exchanges: Exchange[]): Exchange => ({
client,
forward,
dispatchDebug,
}: ExchangeInput) =>
}: ExchangeInput): ExchangeIO =>
exchanges.reduceRight(
(forward, exchange) =>
exchange({
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/exchanges/debug.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { pipe, tap } from 'wonka';
import { Exchange } from '../types';

/** 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!
*/
export const debugExchange: Exchange = ({ forward }) => {
if (process.env.NODE_ENV === 'production') {
return ops$ => forward(ops$);
Expand Down
17 changes: 16 additions & 1 deletion packages/core/src/exchanges/dedup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { filter, pipe, tap } from 'wonka';
import { Exchange, Operation, OperationResult } from '../types';

/** A default exchange for debouncing GraphQL requests. */
/** Default deduplication exchange.
*
* @remarks
* The `dedupExchange` deduplicates queries and subscriptions that are
* started with identical documents and variables by deduplicating by
* their {@link Operation.key}.
* This can prevent duplicate requests from being sent to your GraphQL API.
*
* Because this is a very safe exchange to add to any GraphQL setup, it’s
* not only the default, but we also recommend you to always keep this
* exchange added and included in your setup.
*
* Hint: In React and Vue, some common usage patterns can trigger duplicate
* operations. For instance, in React a single render will actually
* trigger two phases that execute an {@link Operation}.
*/
export const dedupExchange: Exchange = ({ forward, dispatchDebug }) => {
const inFlightKeys = new Set<number>();

Expand Down
17 changes: 10 additions & 7 deletions packages/core/src/exchanges/fallback.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { filter, pipe, tap } from 'wonka';
import { Operation, ExchangeIO, ExchangeInput } from '../types';
import { noop } from '../utils';

/** This is always the last exchange in the chain; No operation should ever reach it */
/** Used by the `Client` as the last exchange to warn about unhandled operations.
*
* @remarks
* In a normal setup, some operations may go unhandled when a {@link Client} isn’t set up
* with the right exchanges.
* For instance, a `Client` may be missing a fetch exchange, or an exchange handling subscriptions.
* This {@link Exchange} is added by the `Client` automatically to log warnings about unhandled
* {@link Operaiton | Operations} in development.
*/
export const fallbackExchange: ({
dispatchDebug,
}: Pick<ExchangeInput, 'dispatchDebug'>) => ExchangeIO = ({
Expand All @@ -25,10 +32,6 @@ export const fallbackExchange: ({
console.warn(message);
}
}),
/* All operations that skipped through the entire exchange chain should be filtered from the output */
// All operations that skipped through the entire exchange chain should be filtered from the output
filter<any>(() => false)
);

export const fallbackExchangeIO: ExchangeIO = fallbackExchange({
dispatchDebug: noop,
});
18 changes: 17 additions & 1 deletion packages/core/src/exchanges/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,23 @@ import {
makeFetchSource,
} from '../internal';

/** A default exchange for fetching GraphQL requests. */
/** 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.
*/
export const fetchExchange: Exchange = ({ forward, dispatchDebug }) => {
return ops$ => {
const sharedOps$ = share(ops$);
Expand Down
10 changes: 9 additions & 1 deletion packages/core/src/exchanges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export { subscriptionExchange } from './subscription';
export { debugExchange } from './debug';
export { dedupExchange } from './dedup';
export { fetchExchange } from './fetch';
export { fallbackExchangeIO } from './fallback';
export { composeExchanges } from './compose';

export type {
Expand All @@ -20,4 +19,13 @@ import { cacheExchange } from './cache';
import { dedupExchange } from './dedup';
import { fetchExchange } from './fetch';

/** The default list of exchanges a `Client` falls back to.
*
* @remarks
* When {@link ClientOptions.exchanges} isn’s passed, a {@link Client} is automatically
* created using this list of default exchanges.
*
* By default, this adds deduplication of operations, a basic document cache,
* and the built-in fetch exchange for GraphQL over HTTP.
*/
export const defaultExchanges = [dedupExchange, cacheExchange, fetchExchange];
56 changes: 56 additions & 0 deletions packages/core/src/exchanges/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,70 @@ import { mergeMap, fromValue, fromPromise, pipe } from 'wonka';
import { Operation, OperationResult, Exchange } from '../types';
import { CombinedError } from '../utils';

/** Options for the `mapExchange` allowing it to react to incoming operations, results, or errors. */
export 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.
*
*/
export const mapExchange = ({
onOperation,
onResult,
Expand Down
Loading