Skip to content

Commit

Permalink
docs(exchanges): Add TSDocs to all exchanges (#3072)
Browse files Browse the repository at this point in the history
  • Loading branch information
kitten authored Mar 17, 2023
1 parent d375de2 commit 59f43ca
Show file tree
Hide file tree
Showing 21 changed files with 1,217 additions and 91 deletions.
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

0 comments on commit 59f43ca

Please sign in to comment.