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(exchanges): Add TSDocs to all exchanges #3072

Merged
merged 15 commits into from
Mar 17, 2023
12 changes: 12 additions & 0 deletions .changeset/wise-hairs-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@urql/exchange-request-policy': patch
'@urql/exchange-graphcache': patch
'@urql/exchange-persisted': patch
'@urql/exchange-populate': patch
'@urql/exchange-context': patch
'@urql/exchange-execute': patch
'@urql/exchange-refocus': patch
'@urql/exchange-retry': patch
---

Add TSDocs for all exchanges, documenting API internals.
56 changes: 54 additions & 2 deletions exchanges/context/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,64 @@ import {
} from '@urql/core';
import { fromPromise, fromValue, mergeMap, pipe } from 'wonka';

/** Input parameters for the {@link contextExchange}. */
export interface ContextExchangeArgs {
getContext: (
/** Returns a new {@link OperationContext}, optionally wrapped in a `Promise`.
*
* @remarks
* `getContext` is called for every {@link Operation} the `contextExchange`
* receives and must return a new {@link OperationContext} or a `Promise`
* of it.
*
* The new `OperationContext` will be used to update the `Operation`'s
* context before it's forwarded to the next exchange.
*/
getContext(
operation: Operation
) => OperationContext | Promise<OperationContext>;
): OperationContext | Promise<OperationContext>;
}

/** Exchange factory modifying the {@link OperationContext} per incoming `Operation`.
*
* @param options - A {@link ContextExchangeArgs} configuration object.
* @returns the created context {@link Exchange}.
*
* @remarks
* The `contextExchange` allows the {@link OperationContext` to be easily
* modified per `Operation`. This may be useful to dynamically change the
* `Operation`’s parameters, even when we need to do so asynchronously.
*
* You must define a {@link ContextExchangeArgs.getContext} function,
* which may return a `Promise<OperationContext>` or `OperationContext`.
*
* Hint: If the `getContext` function passed to this exchange returns a
* `Promise` it must be placed _after_ all synchronous exchanges, such as
* a `cacheExchange`.
*
* @example
* ```ts
* import { Client, cacheExchange, fetchExchange } from '@urql/core';
* import { contextExchange } from '@urql/exchange-context';
*
* const client = new Client({
* url: '',
* exchanges: [
* cacheExchange,
* contextExchange({
* async getContext(operation) {
* const url = await loadDynamicUrl();
* return {
* ...operation.context,
* url,
* };
* },
* }),
* fetchExchange,
* ],
* });
* ```
*/

export const contextExchange =
({ getContext }: ContextExchangeArgs): Exchange =>
({ forward }) => {
Expand Down
46 changes: 30 additions & 16 deletions exchanges/execute/src/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@ import {
OperationResult,
} from '@urql/core';

/** Input parameters for the {@link executeExchange}.
* @see {@link ExecutionArgs} which this interface mirrors. */
export interface ExecuteExchangeArgs {
/** GraphQL Schema definition that `Operation`s are execute against. */
schema: GraphQLSchema;
/** Context object or a factory function creating a `context` object.
*
* @remarks
* The `context` that is passed to the `schema` may either be passed
* or created from an incoming `Operation`, which also allows it to
* be recreated per `Operation`.
*/
context?: ((operation: Operation) => any) | any;
rootValue?: any;
context?: ((op: Operation) => any) | any;
fieldResolver?: GraphQLFieldResolver<any, any>;
typeResolver?: GraphQLTypeResolver<any, any>;
subscribeFieldResolver?: GraphQLFieldResolver<any, any>;
Expand Down Expand Up @@ -108,16 +118,18 @@ const makeExecuteSource = (
});
};

/** Exchange for executing queries locally on a schema using graphql-js. */
/** Exchange factory that executes operations against a GraphQL schema.
*
* @param options - A {@link ExecuteExchangeArgs} configuration object.
* @returns the created execute {@link Exchange}.
*
* @remarks
* The `executeExchange` executes GraphQL operations against the `schema`
* that it’s passed. As such, its options mirror the options that GraphQL.js’
* {@link execute} function accepts.
*/
export const executeExchange =
({
schema,
rootValue,
context,
fieldResolver,
typeResolver,
subscribeFieldResolver,
}: ExecuteExchangeArgs): Exchange =>
(options: ExecuteExchangeArgs): Exchange =>
({ forward }) => {
return ops$ => {
const sharedOps$ = share(ops$);
Expand All @@ -139,7 +151,9 @@ export const executeExchange =
);

const contextValue =
typeof context === 'function' ? context(operation) : context;
typeof options.context === 'function'
? options.context(operation)
: options.context;

// Filter undefined values from variables before calling execute()
// to support default values within directives.
Expand All @@ -162,15 +176,15 @@ export const executeExchange =

return pipe(
makeExecuteSource(operation, {
schema,
schema: options.schema,
document: operation.query,
rootValue,
rootValue: options.rootValue,
contextValue,
variableValues,
operationName,
fieldResolver,
typeResolver,
subscribeFieldResolver,
fieldResolver: options.fieldResolver,
typeResolver: options.typeResolver,
subscribeFieldResolver: options.subscribeFieldResolver,
}),
takeUntil(teardown$)
);
Expand Down
17 changes: 17 additions & 0 deletions exchanges/graphcache/src/cacheExchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ type ResultMap = Map<number, Data | null>;
type OptimisticDependencies = Map<number, Dependencies>;
type DependentOperations = Map<string, Operations>;

/** Exchange factory that creates a normalized cache exchange.
*
* @param opts - A {@link CacheExchangeOpts} configuration object.
* @returns the created normalized cache {@link Exchange}.
*
* @remarks
* Graphcache is a normalized cache, enabled by using the `cacheExchange`
* in place of `@urql/core`’s. A normalized GraphQL cache uses typenames
* and key fields in the result to share a single copy for each unique
* entity across all queries.
*
* The `cacheExchange` may be passed a {@link CacheExchangeOpts} object
* to define custom resolvers, custom updates for mutations,
* optimistic updates, or to add custom key fields per type.
*
* @see {@link https://urql.dev/goto/docs/graphcache} for the full Graphcache docs.
*/
export const cacheExchange =
<C extends Partial<CacheExchangeOpts>>(opts?: C): Exchange =>
({ forward, client, dispatchDebug }) => {
Expand Down
20 changes: 20 additions & 0 deletions exchanges/graphcache/src/default-storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,34 @@ const getTransactionPromise = (transaction: IDBTransaction): Promise<any> => {
};

export interface StorageOptions {
/** Name of the IndexedDB database that will be used.
* @defaultValue `'graphcache-v4'`
*/
idbName?: string;
/** Maximum age of cache entries (in days) after which data is discarded.
* @defaultValue `7` days
*/
maxAge?: number;
}

/** Sample storage adapter persisting to IndexedDB. */
export interface DefaultStorage extends StorageAdapter {
/** Clears the entire IndexedDB storage. */
clear(): Promise<any>;
}

/** Creates a default {@link StorageAdapter} which uses IndexedDB for storage.
*
* @param opts - A {@link StorageOptions} configuration object.
* @returns the created {@link StorageAdapter}.
*
* @remarks
* The default storage uses IndexedDB to persist the normalized cache for
* offline use. It demonstrates that the cache can be chunked by timestamps.
*
* Note: We have no data on stability of this storage and our Offline Support
* for large APIs or longterm use. Proceed with caution.
*/
export const makeDefaultStorage = (opts?: StorageOptions): DefaultStorage => {
if (!opts) opts = {};

Expand Down
29 changes: 29 additions & 0 deletions exchanges/graphcache/src/extras/relayPagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@ import { Cache, Resolver, Variables, NullArray } from '../types';

export type MergeMode = 'outwards' | 'inwards';

/** Input parameters for the {@link relayPagination} factory. */
export interface PaginationParams {
/** Flip between inwards and outwards pagination.
*
* @remarks
* This is only relevant if you’re querying pages using forwards and
* backwards pagination at the same time.
* When set to `'inwards'`, its default, pages that have been queried
* forward are placed in front of all pages that were queried backwards.
* When set to `'outwards'`, the two sets are merged in reverse.
*/
mergeMode?: MergeMode;
}

Expand Down Expand Up @@ -182,6 +192,25 @@ const getPage = (
return page;
};

/** Creates a {@link Resolver} that combines pages that comply to the Relay pagination spec.
*
* @param params - A {@link PaginationParams} configuration object.
* @returns the created Relay pagination {@link Resolver}.
*
* @remarks
* `relayPagination` is a factory that creates a {@link Resolver} that can combine
* multiple pages on a field that complies to the Relay pagination spec into a single,
* combined list for infinite scrolling.
*
* This resolver will only work on fields that return a `Connection` GraphQL object
* type, according to the Relay pagination spec.
*
* Hint: It's not recommended to use this when you can handle infinite scrolling
* in your UI code instead.
*
* @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers#relay-pagination} for more information.
* @see {@link https://urql.dev/goto/docs/basics/ui-patterns/#infinite-scrolling} for an alternate approach.
*/
export const relayPagination = (
params: PaginationParams = {}
): Resolver<any, any, any> => {
Expand Down
26 changes: 26 additions & 0 deletions exchanges/graphcache/src/extras/simplePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,38 @@ import { Resolver, Variables, NullArray } from '../types';

export type MergeMode = 'before' | 'after';

/** Input parameters for the {@link simplePagination} factory. */
export interface PaginationParams {
/** The name of the field argument used to define the page’s offset. */
offsetArgument?: string;
/** The name of the field argument used to define the page’s length. */
limitArgument?: string;
/** Flip between forward and backwards pagination.
*
* @remarks
* When set to `'after'`, its default, pages are merged forwards and in order.
* When set to `'before'`, pages are merged in reverse, putting later pages
* in front of earlier ones.
*/
mergeMode?: MergeMode;
}

/** Creates a {@link Resolver} that combines pages of a primitive pagination field.
*
* @param options - A {@link PaginationParams} configuration object.
* @returns the created pagination {@link Resolver}.
*
* @remarks
* `simplePagination` is a factory that creates a {@link Resolver} that can combine
* multiple lists on a paginated field into a single, combined list for infinite
* scrolling.
*
* Hint: It's not recommended to use this when you can handle infinite scrolling
* in your UI code instead.
*
* @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers#simple-pagination} for more information.
* @see {@link https://urql.dev/goto/docs/basics/ui-patterns/#infinite-scrolling} for an alternate approach.
*/
export const simplePagination = ({
offsetArgument = 'skip',
limitArgument = 'limit',
Expand Down
37 changes: 37 additions & 0 deletions exchanges/graphcache/src/offlineExchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
OptimisticMutationConfig,
Variables,
CacheExchangeOpts,
StorageAdapter,
} from './types';

import { cacheExchange } from './cacheExchange';
Expand Down Expand Up @@ -57,13 +58,49 @@ const isOptimisticMutation = <T extends OptimisticMutationConfig>(
return false;
};

/** Input parameters for the {@link offlineExchange}.
* @remarks
* This configuration object extends the {@link CacheExchangeOpts}
* as the `offlineExchange` extends the regular {@link cacheExchange}.
*/
export interface OfflineExchangeOpts extends CacheExchangeOpts {
/** Configures an offline storage adapter for Graphcache.
*
* @remarks
* A {@link StorageAdapter} allows Graphcache to write data to an external,
* asynchronous storage, and hydrate data from it when it first loads.
* This allows you to preserve normalized data between restarts/reloads.
*
* @see {@link https://urql.dev/goto/docs/graphcache/offline} for the full Offline Support docs.
*/
storage: StorageAdapter;
/** Predicate function to determine whether a {@link CombinedError} hints at a network error.
*
* @remarks
* Not ever {@link CombinedError} means that the device is offline and by default
* the `offlineExchange` will check for common network error messages and check
* `navigator.onLine`. However, when `isOfflineError` is passed it can replace
* the default offline detection.
*/
isOfflineError?(
error: undefined | CombinedError,
result: OperationResult
): boolean;
}

/** Exchange factory that creates a normalized cache exchange in Offline Support mode.
*
* @param opts - A {@link OfflineExchangeOpts} configuration object.
* @returns the created normalized, offline cache {@link Exchange}.
*
* @remarks
* The `offlineExchange` is a wrapper around the regular {@link cacheExchange}
* which adds logic via the {@link OfflineExchangeOpts.storage} adapter to
* recognize when it’s offline, when to retry failed mutations, and how
* to handle longer periods of being offline.
*
* @see {@link https://urql.dev/goto/docs/graphcache/offline} for the full Offline Support docs.
*/
export const offlineExchange =
<C extends OfflineExchangeOpts>(opts: C): Exchange =>
input => {
Expand Down
3 changes: 3 additions & 0 deletions exchanges/graphcache/src/operations/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export interface QueryResult {
data: null | Data;
}

/** Reads a GraphQL query from the cache.
* @internal
*/
export const query = (
store: Store,
request: OperationRequest,
Expand Down
4 changes: 3 additions & 1 deletion exchanges/graphcache/src/operations/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ export interface WriteResult {
dependencies: Dependencies;
}

/** Writes a request given its response to the store */
/** Writes a GraphQL response to the cache.
* @internal
*/
export const write = (
store: Store,
request: OperationRequest,
Expand Down
3 changes: 3 additions & 0 deletions exchanges/graphcache/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import {

type RootField = 'query' | 'mutation' | 'subscription';

/** Implementation of the {@link Cache} interface as created internally by the {@link cacheExchange}.
* @internal
*/
export class Store<
C extends Partial<CacheExchangeOpts> = Partial<CacheExchangeOpts>
> implements Cache
Expand Down
Loading