1import type {
2 AnyVariables,
3 DocumentInput,
4 RequestExtensions,
5 TypedDocumentNode,
6 FormattedNode,
7 ErrorLike,
8} from '@urql/core';
9
10import type { DocumentNode, FragmentDefinitionNode } from '@0no-co/graphql.web';
11import type { IntrospectionData } from './ast';
12
13/** Nullable GraphQL list types of `T`.
14 *
15 * @remarks
16 * Any GraphQL list of a given type `T` that is nullable is
17 * expected to contain nullable values. Nested lists are
18 * also taken into account in Graphcache.
19 */
20export type NullArray<T> = Array<null | T | NullArray<T>>;
21
22/** Dictionary of GraphQL Fragment definitions by their names.
23 *
24 * @remarks
25 * A map of {@link FragmentDefinitionNode | FragmentDefinitionNodes} by their
26 * fragment names from the original GraphQL document that Graphcache is
27 * executing.
28 */
29export interface Fragments {
30 [fragmentName: string]: void | FormattedNode<FragmentDefinitionNode>;
31}
32
33/** Non-object JSON values as serialized by a GraphQL API
34 * @see {@link https://spec.graphql.org/October2021/#sel-DAPJDHAAEJHAKmzP} for the
35 * GraphQL spec’s serialization format.
36 */
37export type Primitive = null | number | boolean | string;
38
39/** Any GraphQL scalar object
40 *
41 * @remarks
42 * A GraphQL schema may define custom scalars that are resolved
43 * and serialized as objects. These objects could also be turned
44 * on the client-side into a non-JSON object, e.g. a `Date`.
45 *
46 * @see {@link https://spec.graphql.org/October2021/#sec-Scalars} for the
47 * GraphQL spec’s information on custom scalars.
48 */
49export interface ScalarObject {
50 constructor?: Function;
51 [key: string]: any;
52}
53
54/** GraphQL scalar value
55 * @see {@link https://spec.graphql.org/October2021/#sec-Scalars} for the GraphQL
56 * spec’s definition of scalars
57 */
58export type Scalar = Primitive | ScalarObject;
59
60/** Fields that Graphcache expects on GraphQL object (“entity”) results.
61 *
62 * @remarks
63 * Any object that comes back from a GraphQL API will have
64 * a `__typename` field from GraphQL Object types.
65 *
66 * The `__typename` field must be present as Graphcache updates
67 * GraphQL queries with type name introspection.
68 * Furthermore, Graphcache always checks for its default key
69 * fields, `id` and `_id` to be present.
70 */
71export interface SystemFields {
72 /** GraphQL Object type name as returned by Type Name Introspection.
73 * @see {@link https://spec.graphql.org/October2021/#sec-Type-Name-Introspection} for
74 * more information on GraphQL’s Type Name introspection.
75 */
76 __typename: string;
77 _id?: string | number | null;
78 id?: string | number | null;
79}
80
81/** Scalar values are stored separately from relations between entities.
82 * @internal
83 */
84export type EntityField = undefined | Scalar | NullArray<Scalar>;
85
86/** Values on GraphQL object (“entity”) results.
87 *
88 * @remarks
89 * Any field that comes back from a GraphQL API will have
90 * values that are scalars, other objects, or arrays
91 * of scalars or objects.
92 */
93export type DataField = Scalar | Data | NullArray<Scalar> | NullArray<Data>;
94
95/** Definition of GraphQL object (“entity”) fields.
96 *
97 * @remarks
98 * Any object that comes back from a GraphQL API will have
99 * values that are scalars, other objects, or arrays
100 * of scalars or objects, i.e. the {@link DataField} type.
101 */
102export interface DataFields {
103 [fieldName: string]: DataField;
104}
105
106/** Definition of GraphQL variables objects.
107 * @remarks
108 * Variables, as passed to GraphQL queries, can only contain scalar values.
109 *
110 * @see {@link https://spec.graphql.org/October2021/#sec-Coercing-Variable-Values} for the
111 * GraphQL spec’s coercion of GraphQL variables.
112 */
113export interface Variables {
114 [name: string]: Scalar | Scalar[] | Variables | NullArray<Variables>;
115}
116
117/** Definition of GraphQL objects (“entities”).
118 *
119 * @remarks
120 * An entity is expected to consist of a `__typename`
121 * fields, optionally the default `id` or `_id` key
122 * fields, and scalar values or other entities
123 * otherwise.
124 */
125export type Data = SystemFields & DataFields;
126
127/** An entity, a key of an entity, or `null`
128 *
129 * @remarks
130 * When Graphcache accepts a reference to an entity, you may pass it a key of an entity,
131 * as retrieved for instance by {@link Cache.keyOfEntity} or a partial GraphQL object
132 * (i.e. an object with a `__typename` and key field).
133 */
134export type Entity = undefined | null | Data | string;
135
136/** A key of an entity, or `null`; or a list of keys.
137 *
138 * @remarks
139 * When Graphcache accepts a reference to one or more entities, you may pass it a
140 * key, an entity, or a list of entities or keys. This is often passed to {@link Cache.link}
141 * to update a field pointing to other GraphQL objects.
142 */
143export type Link<Key = string> = null | Key | NullArray<Key>;
144
145/** Arguments passed to a Graphcache field resolver.
146 *
147 * @remarks
148 * Arguments a field receives are similar to variables and can
149 * only contain scalars or other arguments objects. This
150 * is equivalent to the {@link Variables} type.
151 *
152 * @see {@link https://spec.graphql.org/October2021/#sec-Coercing-Field-Arguments} for the
153 * GraphQL spec’s coercion of field arguments.
154 */
155export type FieldArgs = Variables | null | undefined;
156
157/** Metadata about an entity’s cached field.
158 *
159 * @remarks
160 * As returned by {@link Cache.inspectFields}, `FieldInfo` specifies an entity’s cached field,
161 * split into the field’s key itself and the field’s original name and arguments.
162 */
163export interface FieldInfo {
164 /** The field’s key which combines `fieldName` and `arguments`. */
165 fieldKey: string;
166 /** The field’s name, as defined on a GraphQL Object type. */
167 fieldName: string;
168 /** The arguments passed to the field as found on the cache. */
169 arguments: Variables | null;
170}
171
172/** A key to an entity field split back into the entity’s key and the field’s key part.
173 * @internal
174 */
175export interface KeyInfo {
176 entityKey: string;
177 fieldKey: string;
178}
179
180/** Abstract type for GraphQL requests.
181 *
182 * @remarks
183 * Similarly to `@urql/core`’s `GraphQLRequest` type, `OperationRequest`
184 * requires the minimum fields that Grapcache requires to execute a
185 * GraphQL operation: its query document and variables.
186 */
187export interface OperationRequest {
188 query: FormattedNode<DocumentNode> | DocumentNode;
189 variables?: any;
190}
191
192/** Metadata object passed to all resolver functions.
193 *
194 * @remarks
195 * `ResolveInfo`, similar to GraphQL.js’ `GraphQLResolveInfo` object,
196 * gives your resolvers a global state of the current GraphQL
197 * document traversal.
198 *
199 * `parent`, `parenTypeName`, `parentKey`, and `parentFieldKey`
200 * are particularly useful to make reusable resolver functions that
201 * must know on which field and type they’re being called on.
202 */
203export interface ResolveInfo {
204 /** The parent GraphQL object.
205 *
206 * @remarks
207 * The GraphQL object that the resolver has been called on. Because this is
208 * a reference to raw GraphQL data, this may be incomplete or contain
209 * aliased fields!
210 */
211 parent: Data;
212 /** The parent object’s typename that the resolver has been called on. */
213 parentTypeName: string;
214 /** The parent object’s entity key that the resolver has been called on. */
215 parentKey: string;
216 /** Current field’s key that the resolver has been called on. */
217 parentFieldKey: string;
218 /** Current field that the resolver has been called on. */
219 fieldName: string;
220 /** Map of fragment definitions from the query document. */
221 fragments: Fragments;
222 /** Full original {@link Variables} object on the {@link OperationRequest}. */
223 variables: Variables;
224 /** Error that occurred for the current field, if any.
225 *
226 * @remarks
227 * If a {@link GraphQLError.path} points at the current field, the error
228 * will be set and provided here. This can be useful to recover from an
229 * error on a specific field.
230 */
231 error: ErrorLike | undefined;
232 /** Flag used to indicate whether the current GraphQL query is only partially cached.
233 *
234 * @remarks
235 * When Graphcache has {@link CacheExchangeOpts.schema} introspection information,
236 * it can automatically generate partial results and trigger a full API request
237 * in the background.
238 * Hence, this field indicates whether any data so far has only been partially
239 * resolved from the cache, and is only in use on {@link Resolver | Resolvers}.
240 *
241 * However, you can also flip this flag to `true` manually to indicate to
242 * the {@link cacheExchange} that it should still make a network request.
243 */
244 partial?: boolean;
245 /** Flag used to indicate whether the current GraphQL mutation is optimistically executed.
246 *
247 * @remarks
248 * An {@link UpdateResolver} is called for both API mutation responses and
249 * optimistic mutation reuslts, as generated by {@link OptimisticMutationResolver}.
250 *
251 * Since an update sometimes needs to perform different actions if it’s run
252 * optimistically, this flag is set to `true` during optimisti cupdates.
253 */
254 optimistic?: boolean;
255 /** Internal state used by Graphcache.
256 * @internal
257 */
258 __internal?: unknown;
259}
260
261/** GraphQL document and variables that should be queried against the cache.
262 *
263 * @remarks
264 * `QueryInput` is a generic GraphQL request that should be executed against
265 * cached data, as accepted by {@link cache.readQuery}.
266 */
267export interface QueryInput<T = Data, V = Variables> {
268 query: DocumentInput<T, V>;
269 variables?: V;
270}
271
272/** Interface to interact with cached data, which resolvers receive. */
273export interface Cache {
274 /** Returns the cache key for a given entity or `null` if it’s unkeyable.
275 *
276 * @param entity - the {@link Entity} to generate a key for.
277 * @returns the entity’s key or `null`.
278 *
279 * @remarks
280 * `cache.keyOfEntity` may be called with a partial GraphQL object (“entity”)
281 * and generates a key for it. It uses your {@link KeyingConfig} and otherwise
282 * defaults to `id` and `_id` fields.
283 *
284 * If it’s passed a `string` or `null`, it will simply return what it’s been passed.
285 * Objects that lack a `__typename` field will return `null`.
286 */
287 keyOfEntity(entity: Entity | undefined): string | null;
288
289 /** Returns the cache key for a field.
290 *
291 * @param fieldName - the field’s name.
292 * @param args - the field’s arguments, if any.
293 * @returns the field key
294 *
295 * @remarks
296 * `cache.keyOfField` is used to create a field’s cache key from a given
297 * field name and its arguments. This is used internally by {@link cache.resolve}
298 * to combine an entity key and a field key into a path that normalized data is
299 * accessed on in Graphcache’s internal data structure.
300 */
301 keyOfField(fieldName: string, args?: FieldArgs): string | null;
302
303 /** Returns a cached value on a given entity’s field.
304 *
305 * @param entity - a GraphQL object (“entity”) or an entity key.
306 * @param fieldName - the field’s name.
307 * @param args - the field’s arguments, if any.
308 * @returns the field’s value or the entity key(s) this field is pointing at.
309 *
310 * @remarks
311 * `cache.resolve` is used to retrieve either the cached value of a field, or
312 * to get the relation of the field (“link”). When a cached field points at
313 * another normalized entity, this method will return the related entity key
314 * (or a list, if it’s pointing at a list of entities).
315 *
316 * As such, if you’re accessing a nested field, you may have to call
317 * `cache.resolve` again and chain its calls.
318 *
319 * Hint: If you have a field key from {@link FieldInfo} or {@link cache.keyOfField},
320 * you may pass it as a second argument.
321 *
322 * @example
323 * ```ts
324 * const authorName = cache.resolve(
325 * cache.resolve({ __typename: 'Book', id }, 'author'),
326 * 'name'
327 * );
328 * ```
329 */
330 resolve(
331 entity: Entity | undefined,
332 fieldName: string,
333 args?: FieldArgs
334 ): DataField | undefined;
335
336 /** Returns a list of cached fields for a given GraphQL object (“entity”).
337 *
338 * @param entity - a GraphQL object (“entity”) or an entity key.
339 * @returns a list of {@link FieldInfo} objects.
340 *
341 * @remarks
342 * `cache.inspectFields` can be used to list out all known fields
343 * of a given entity. This can be useful in an {@link UpdateResolver}
344 * if you have a `Query` field that accepts many different arguments,
345 * for instance a paginated field.
346 *
347 * The returned list of fields are all fields that the cache knows about,
348 * and you may have to filter them by name or arguments to find only which
349 * ones you need.
350 *
351 * Hint: This method is theoretically a slower operation than simple
352 * cache lookups, as it has to decode field keys. It’s only recommended
353 * to be used in updaters.
354 */
355 inspectFields(entity: Entity): FieldInfo[];
356
357 /** Deletes a cached entity or an entity’s field.
358 *
359 * @param entity - a GraphQL object (“entity”) or an entity key.
360 * @param fieldName - optionally, a field name.
361 * @param args - optionally, the field’s arguments, if any.
362 *
363 * @remarks
364 * `cache.invalidate` can be used in updaters to delete data from
365 * the cache. This will cause the {@link cacheExchange} to reexecute
366 * queries that contain the deleted data.
367 *
368 * If you only pass its first argument, the entire entity is deleted.
369 * However, if a field name (and optionally, its arguments) are passed,
370 * only a single field is erased.
371 */
372 invalidate(
373 entity: Entity | undefined,
374 fieldName?: string,
375 args?: FieldArgs
376 ): void;
377
378 /** Updates a GraphQL query‘s cached data.
379 *
380 * @param input - a {@link QueryInput}, which is a GraphQL query request.
381 * @param updater - a function called with the query’s result or `null` in case of a cache miss, which
382 * may return updated data, which is written to the cache using the query.
383 *
384 * @remarks
385 * `cache.updateQuery` can be used to update data for an entire GraphQL query document.
386 * When it's passed a GraphQL query request, it calls the passed `updater` function
387 * with the cached result for this query. You may then modify and update the data and
388 * return it, after which it’s written back to the cache.
389 *
390 * Hint: While this allows for large updates at once, {@link cache.link},
391 * {@link cache.resolve}, and {@link cache.writeFragment} are often better
392 * choices for more granular and compact updater code.
393 *
394 * @example
395 * ```ts
396 * cache.updateQuery({ query: TodoList }, data => {
397 * data.todos.push(newTodo);
398 * return data;
399 * });
400 * ```
401 */
402 updateQuery<T = Data, V = Variables>(
403 input: QueryInput<T, V>,
404 updater: (data: T | null) => T | null
405 ): void;
406
407 /** Returns a GraphQL query‘s cached result.
408 *
409 * @param input - a {@link QueryInput}, which is a GraphQL query request.
410 * @returns the cached data result of the query or `null`, in case of a cache miss.
411 *
412 * @remarks
413 * `cache.readQuery` can be used to read an entire query’s data all at once
414 * from the cache.
415 *
416 * This can be useful when typing out many {@link cache.resolve}
417 * calls is too tedious.
418 *
419 * @example
420 * ```ts
421 * const data = cache.readQuery({
422 * query: TodosQuery,
423 * variables: { from: 0, limit: 10 }
424 * });
425 * ```
426 */
427 readQuery<T = Data, V = Variables>(input: QueryInput<T, V>): T | null;
428
429 /** Returns a GraphQL fragment‘s cached result.
430 *
431 * @param fragment - a {@link DocumentNode} containing a fragment definition.
432 * @param entity - a GraphQL object (“entity”) or an entity key to read the fragment on.
433 * @param variables - optionally, GraphQL variables, if the fragments use any.
434 * @returns the cached data result of the fragment or `null`, in case of a cache miss.
435 *
436 * @remarks
437 * `cache.readFragment` can be used to read an entire query’s data all at once
438 * from the cache.
439 *
440 * It attempts to read the fragment starting from the `entity` that’s passed to it.
441 * If the entity can’t be resolved or has mismatching types, `null` is returned.
442 *
443 * This can be useful when typing out many {@link cache.resolve}
444 * calls is too tedious.
445 *
446 * @example
447 * ```ts
448 * const data = cache.readFragment(
449 * gql`fragment _ on Todo { id, text }`,
450 * { id: '123' }
451 * );
452 * ```
453 */
454 readFragment<T = Data, V = Variables>(
455 fragment: TypedDocumentNode<any, any> | TypedDocumentNode<T, V>,
456 entity: string | Data | T,
457 variables?: V
458 ): T | null;
459
460 /** Writes a GraphQL fragment to the cache.
461 *
462 * @param fragment - a {@link DocumentNode} containing a fragment definition.
463 * @param data - a GraphQL object to be written with the given fragment.
464 * @param variables - optionally, GraphQL variables, if the fragments use any.
465 *
466 * @remarks
467 * `cache.writeFragment` can be used to write an entity to the cache.
468 * The method will generate a key for the `data` it’s passed, and start writing
469 * it using the fragment.
470 *
471 * This method is used when writing scalar values to the cache.
472 * Since it's rare for an updater to write values to the cache, {@link cache.link}
473 * only allows relations (“links”) to be updated, and `cache.writeFragment` is
474 * instead used when writing multiple scalars.
475 *
476 * @example
477 * ```ts
478 * const data = cache.writeFragment(
479 * gql`fragment _ on Todo { id, text }`,
480 * { id: '123', text: 'New Text' }
481 * );
482 * ```
483 */
484 writeFragment<T = Data, V = Variables>(
485 fragment: TypedDocumentNode<any, any> | TypedDocumentNode<T, V>,
486 data: T,
487 variables?: V
488 ): void;
489
490 /** Updates the relation (“link”) from an entity’s field to another entity.
491 *
492 * @param entity - a GraphQL object (“entity”) or an entity key.
493 * @param fieldName - the field’s name.
494 * @param args - optionally, the field’s arguments, if any.
495 * @param link - the GraphQL object(s) that should be set on this field.
496 *
497 * @remarks
498 * The normalized cache stores relations between GraphQL objects separately.
499 * As such, a field can be updated using `cache.link` to point to a new entity,
500 * or a list of entities.
501 *
502 * In other words, `cache.link` is used to set a field to point to another
503 * entity or a list of entities.
504 *
505 * @example
506 * ```ts
507 * const todos = cache.resolve('Query', 'todos');
508 * cache.link('Query', 'todos', [...todos, newTodo]);
509 * ```
510 */
511 link(
512 entity: Entity,
513 field: string,
514 args: FieldArgs,
515 link: Link<Entity>
516 ): void;
517 link(entity: Entity, field: string, value: Link<Entity>): void;
518}
519
520/** Values a {@link Resolver} may return.
521 *
522 * @remarks
523 * A resolver may return any value that a GraphQL object may contain.
524 *
525 * Additionally however, a resolver may return `undefined` to indicate that data
526 * isn’t available from the cache, i.e. to trigger a cache miss.
527 */
528export type ResolverResult =
529 | DataField
530 | (DataFields & { __typename?: string })
531 | null
532 | undefined;
533
534export type Logger = (
535 severity: 'debug' | 'error' | 'warn',
536 message: string
537) => void;
538
539/** Input parameters for the {@link cacheExchange}. */
540export type CacheExchangeOpts = {
541 /** Configure a custom-logger for graphcache, this function wll be called with a severity and a message.
542 *
543 * @remarks
544 * By default we will invoke `console.warn` for warnings during development, however you might want to opt
545 * out of this because you are re-using urql for a different library. This setting allows you to stub the logger
546 * function or filter to only logs you want.
547 */
548 logger?: Logger;
549 /** Configures update functions which are called when the mapped fields are written to the cache.
550 *
551 * @remarks
552 * `updates` are commonly used to define additional changes to the cache for
553 * mutation or subscription fields. It may commonly be used to invalidate
554 * cached data or to modify lists after mutations.
555 * This is a map of types to fields to {@link UpdateResolver} functions.
556 *
557 * @see {@link https://urql.dev/goto/docs/graphcache/cache-updates} for the full updates docs.
558 */
559 updates?: UpdatesConfig;
560 /** Configures resolvers which replace cached reuslts with custom values.
561 *
562 * @remarks
563 * `resolvers` is a map of types to fields to {@link Resolver} functions.
564 * These functions allow us to replace cached field values with a custom
565 * result, either to replace values on GraphQL results, or to resolve
566 * entities from the cache for queries that haven't been sent to the API
567 * yet.
568 *
569 * @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers} for the full resolvers docs.
570 */
571 resolvers?: ResolverConfig;
572 /** Configures directives which can perform custom logic on fields.
573 *
574 * @remarks
575 * A {@link DirectivesConfig} may be passed to allow local directives to be used. For example, when `@_custom` is placed on a field and the configuration contains `custom` then this directive is executed by Graphcache.
576 *
577 * @see {@link https://urql.dev/goto/docs/graphcache/local-directives} for the full directives docs.
578 */
579 directives?: DirectivesConfig;
580 /** Configures optimistic updates to react to mutations instantly before an API response.
581 *
582 * @remarks
583 * `optimistic` is a map of mutation fields to {@link OptimisticMutationResolver} functions.
584 * These functions allow us to return result data for mutations to optimistically apply them.
585 * Optimistic updates are temporary updates to the cache’s data which allow an app to
586 * instantly reflect changes that a mutation will make.
587 *
588 * @see {@link https://urql.dev/goto/docs/graphcache/cache-updates/#optimistic-updates} for the
589 * full optimistic updates docs.
590 */
591 optimistic?: OptimisticMutationConfig;
592 /** Configures keying functions for GraphQL types.
593 *
594 * @remarks
595 * `keys` is a map of GraphQL object type names to {@link KeyGenerator} functions.
596 * If a type in your API has no key field or a key field that isn't the default
597 * `id` or `_id` fields, you may define a custom key generator for the type.
598 *
599 * Hint: Graphcache will log warnings when it finds objects that have no keyable
600 * fields, which will remind you to define these functions gradually for every
601 * type that needs them.
602 *
603 * @see {@link https://urql.dev/goto/docs/graphcache/normalized-caching/#custom-keys-and-non-keyable-entities} for
604 * the full keys docs.
605 */
606 keys?: KeyingConfig;
607 /** Enables global IDs for keying GraphQL types.
608 *
609 * @remarks
610 * When `globalIDs` are enabled, GraphQL object type names will not contribute
611 * to the keys of entities and instead only their ID fields (or `keys` return
612 * values will be used.
613 *
614 * This is useful to overlap types of differing typenames. While this isn’t recommended
615 * it can be necessary to represent more complex interface relationships.
616 *
617 * If this should only be applied to a limited set of type names, a list of
618 * type names may be passed instead.
619 */
620 globalIDs?: string[] | boolean;
621 /** Configures abstract to concrete types mapping for GraphQL types.
622 *
623 * @remarks
624 * This will disable heuristic fragment matching, allowing Graphcache to match
625 * fragment deterministically.
626 *
627 * When both `possibleTypes` and `schema` is set, `possibleTypes` value will be
628 * ignored.
629 */
630 possibleTypes?: PossibleTypesConfig;
631 /** Configures Graphcache with Schema Introspection data.
632 *
633 * @remarks
634 * Passing a `schema` to Graphcache enables it to do non-heuristic fragment
635 * matching, and be certain when a fragment matches against a union or interface
636 * on your schema.
637 *
638 * It also enables a mode called “Schema Awareness”, which allows Graphcache to
639 * return partial GraphQL results, `null`-ing out fields that aren’t in the cache
640 * that are nullable on your schema, while requesting the full API response in
641 * the background.
642 *
643 * @see {@link https://urql.dev/goto/urql/docs/graphcache/schema-awareness} for
644 * the full keys docs on Schema Awareness.
645 */
646 schema?: IntrospectionData;
647 /** Configures an offline storage adapter for Graphcache.
648 *
649 * @remarks
650 * A {@link StorageAdapter} allows Graphcache to write data to an external,
651 * asynchronous storage, and hydrate data from it when it first loads.
652 * This allows you to preserve normalized data between restarts/reloads.
653 *
654 * Hint: If you’re trying to use Graphcache’s Offline Support, you may
655 * want to swap out the `cacheExchange` with the {@link offlineExchange}.
656 *
657 * @see {@link https://urql.dev/goto/docs/graphcache/offline} for the full Offline Support docs.
658 */
659 storage?: StorageAdapter;
660};
661
662/** Cache Resolver, which may resolve or replace data during cache reads.
663 *
664 * @param parent - The GraphQL object that is currently being constructed from cache data.
665 * @param args - This field’s arguments.
666 * @param cache - {@link Cache} interface.
667 * @param info - {@link ResolveInfo} interface.
668 * @returns a {@link ResolverResult}, which is an updated value, partial entity, or entity key
669 *
670 * @remarks
671 * A `Resolver`, as defined on the {@link ResolverConfig}, is called for
672 * a field’s type during cache reads, and can be used to deserialize or replace
673 * scalar values, or to resolve an entity from cached data, even if the
674 * current field hasn’t been cached from an API response yet.
675 *
676 * For instance, if you have a `Query.picture(id: ID!)` field, you may define
677 * a resolver that returns `{ __typename: 'Picture', id: args.id }`, since you
678 * know the key fields of the GraphQL object.
679 *
680 * @example
681 * ```ts
682 * cacheExchange({
683 * resolvers: {
684 * Query: {
685 * // resolvers can be used to resolve cached entities without API requests
686 * todo: (_parent, args) => ({ __typename: 'Todo', id: args.id }),
687 * },
688 * Todo: {
689 * // resolvers can also be used to replace/deserialize scalars
690 * updatedAt: parent => new Date(parent.updatedAt),
691 * },
692 * },
693 * });
694 * ```
695 *
696 * @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers} for the full resolvers docs.
697 */
698export type Resolver<
699 ParentData = DataFields,
700 Args = Variables,
701 Result = ResolverResult,
702> = {
703 bivarianceHack(
704 parent: ParentData,
705 args: Args,
706 cache: Cache,
707 info: ResolveInfo
708 ): Result;
709}['bivarianceHack'];
710
711/** Configures resolvers which replace cached reuslts with custom values.
712 *
713 * @remarks
714 * A `ResolverConfig` is a map of types to fields to {@link Resolver} functions.
715 * These functions allow us to replace cached field values with a custom
716 * result, either to replace values on GraphQL results, or to resolve
717 * entities from the cache for queries that haven't been sent to the API
718 * yet.
719 *
720 * @see {@link https://urql.dev/goto/docs/graphcache/local-resolvers} for the full resolvers docs.
721 */
722export type ResolverConfig = {
723 [typeName: string]: {
724 [fieldName: string]: Resolver | void;
725 } | void;
726};
727
728export type Directive = (
729 directiveArguments: Record<string, unknown> | null
730) => Resolver;
731
732export type DirectivesConfig = {
733 [directiveName: string]: Directive;
734};
735
736/** Cache Updater, which defines additional cache updates after cache writes.
737 *
738 * @param parent - The GraphQL object that is currently being written to the cache.
739 * @param args - This field’s arguments.
740 * @param cache - {@link Cache} interface.
741 * @param info - {@link ResolveInfo} interface.
742 *
743 * @remarks
744 * An `UpdateResolver` (“updater”), as defined on the {@link UpdatesConfig}, is
745 * called for a field’s type during cache writes, and can be used to instruct
746 * the {@link Cache} to perform other cache updates at the same time.
747 *
748 * This is often used, for instance, to update lists or invalidate entities
749 * after a mutation response has come back from the API.
750 *
751 * @example
752 * ```ts
753 * cacheExchange({
754 * updates: {
755 * Mutation: {
756 * // updaters can invalidate data from the cache
757 * deleteAuthor: (_parent, args, cache) => {
758 * cache.invalidate({ __typename: 'Author', id: args.id });
759 * },
760 * },
761 * },
762 * });
763 * ```
764 *
765 * @see {@link https://urql.dev/goto/docs/graphcache/cache-updates} for the
766 * full cache updates docs.
767 */
768export type UpdateResolver<ParentData = DataFields, Args = Variables> = {
769 bivarianceHack(
770 parent: ParentData,
771 args: Args,
772 cache: Cache,
773 info: ResolveInfo
774 ): void;
775}['bivarianceHack'];
776
777/** A key functon, which is called to create a cache key for a GraphQL object (“entity”).
778 *
779 * @param data - The GraphQL object that a key is generated for.
780 * @returns a key `string` or `null` or unkeyable objects.
781 *
782 * @remarks
783 * By default, Graphcache will use an object’s `__typename`, and `id` or `_id` fields
784 * to generate a key for an object. However, not all GraphQL objects will have a unique
785 * field, and some objects don’t have a key at all.
786 *
787 * When one of your GraphQL object types has a different key field, you may define a
788 * function on the {@link KeyingConfig} to return its key field.
789 * You may also have objects that don’t have keys, like “Edge” objects, or scalar-like
790 * objects. For these, you can define a function that returns `null`, which tells
791 * Graphcache that it’s an embedded object, which only occurs on its parent and is
792 * globally unique.
793 *
794 * @see {@link https://urql.dev/goto/docs/graphcache/normalized-caching/#custom-keys-and-non-keyable-entities} for
795 * the full keys docs.
796 *
797 * @example
798 * ```ts
799 * cacheExchange({
800 * keys: {
801 * Image: data => data.url,
802 * LatLng: () => null,
803 * },
804 * });
805 * ```
806 */
807export type KeyGenerator = {
808 bivarianceHack(data: Data): string | null;
809}['bivarianceHack'];
810
811/** Configures update functions which are called when the mapped fields are written to the cache.
812 *
813 * @remarks
814 * `UpdatesConfig` is a map of types to fields to {@link UpdateResolver} functions.
815 * These update functions are defined to instruct the cache to make additional changes
816 * when a field is written to the cache.
817 *
818 * As changes are often made after a mutation or subscription, the `typeName` is
819 * often set to `'Mutation'` or `'Subscription'`.
820 *
821 * @see {@link https://urql.dev/goto/docs/graphcache/cache-updates} for the full updates docs.
822 *
823 * @example
824 * ```ts
825 * const updates = {
826 * Mutation: {
827 * deleteAuthor(_parent, args, cache) {
828 * // Delete the Author from the cache when Mutation.deleteAuthor is sent
829 * cache.invalidate({ __typename: 'Author', id: args.id });
830 * },
831 * },
832 * };
833 */
834export type UpdatesConfig = {
835 [typeName: string | 'Query' | 'Mutation' | 'Subscription']: {
836 [fieldName: string]: UpdateResolver | void;
837 } | void;
838};
839
840/** Remaps result type to allow for nested optimistic mutation resolvers.
841 *
842 * @remarks
843 * An {@link OptimisticMutationResolver} can not only return partial, nested
844 * mutation result data, but may also contain more optimistic mutation resolvers
845 * for nested fields, which allows fields with arguments to optimistically be
846 * resolved to dynamic values.
847 *
848 * @see {@link OptimisticMutationConfig} for more information.
849 */
850export type MakeFunctional<T> = T extends { __typename: string }
851 ? WithTypename<{
852 [P in keyof T]?: MakeFunctional<T[P]>;
853 }>
854 : OptimisticMutationResolver<Variables, T> | T;
855
856/** Optimistic mutation resolver, which may return data that a mutation response will return.
857 *
858 * @param args - This field’s arguments.
859 * @param cache - {@link Cache} interface.
860 * @param info - {@link ResolveInfo} interface.
861 * @returns the field’s optimistic data
862 *
863 * @remarks
864 * Graphcache can update its cache optimistically via the {@link OptimisticMutationConfig}.
865 * An `OptimisticMutationResolver` should return partial data that a mutation will return
866 * once it completes and we receive its result.
867 *
868 * For instance, it could return the data that a deletion mutation may return
869 * optimistically, which might allow an updater to run early and your UI to update
870 * instantly.
871 *
872 * The result that this function returns may miss some fields that your mutation may return,
873 * especially if it contains GraphQL object that are already cached. It may also contain
874 * other, nested resolvers, which allows you to handle fields that accept arguments.
875 */
876export type OptimisticMutationResolver<
877 Args = Variables,
878 Result = Link<Data> | Scalar,
879> = {
880 bivarianceHack(
881 args: Args,
882 cache: Cache,
883 info: ResolveInfo
884 ): MakeFunctional<Result>;
885}['bivarianceHack'];
886
887/** Configures optimistic result functions which are called to get a mutation’s optimistic result.
888 *
889 * @remarks
890 * `OptimisticMutationConfig` is a map of mutation fields to {@link OptimisticMutationResolver}
891 * functions, which return result data for mutations to optimistically apply them.
892 * Optimistic updates are temporary updates to the cache’s data which allow an app to
893 * instantly reflect changes that a mutation will make.
894 *
895 * Hint: Results returned from optimistic functions may be partial, and may contain functions.
896 * If the returned optimistic object contains functions on fields, these are executed as nested
897 * optimistic resolver functions.
898 *
899 * @see {@link https://urql.dev/goto/docs/graphcache/cache-updates/#optimistic-updates} for the
900 * full optimistic updates docs.
901 *
902 * @example
903 * ```ts
904 * const optimistic = {
905 * updateProfile: (args) => ({
906 * __typename: 'UserProfile',
907 * id: args.id,
908 * name: args.newName,
909 * }),
910 * };
911 */
912export type OptimisticMutationConfig = {
913 [mutationFieldName: string]: OptimisticMutationResolver;
914};
915
916/** Configures keying functions for GraphQL types.
917 *
918 * @remarks
919 * `KeyingConfig` is a map of GraphQL object type names to {@link KeyGenerator} functions.
920 * If a type in your API has no key field or a key field that isn't the default
921 * `id` or `_id` fields, you may define a custom key generator for the type.
922 *
923 * Keys are important to a normalized cache, because they’re the identity of the object
924 * that is shared across the cache, and helps the cache recognize shared/normalized data.
925 *
926 * Hint: Graphcache will log warnings when it finds objects that have no keyable
927 * fields, which will remind you to define these functions gradually for every
928 * type that needs them.
929 *
930 * @see {@link https://urql.dev/goto/docs/graphcache/normalized-caching/#custom-keys-and-non-keyable-entities} for
931 * the full keys docs.
932 *
933 * @example
934 * ```ts
935 * const keys = {
936 * Image: data => data.url,
937 * LatLng: () => null,
938 * };
939 * ```
940 */
941export type KeyingConfig = {
942 [typename: string]: KeyGenerator;
943};
944
945export type PossibleTypesConfig = {
946 [abstractType: string]: string[];
947};
948
949/** Serialized normalized caching data. */
950export interface SerializedEntries {
951 [key: string]: string | undefined;
952}
953
954/** A serialized GraphQL request for offline storage. */
955export interface SerializedRequest {
956 query: string;
957 variables: AnyVariables | undefined;
958 extensions?: RequestExtensions | undefined;
959}
960
961/** Interface for a storage adapter, used by the {@link offlineExchange} for Offline Support.
962 * @see {@link https://urql.dev/goto/docs/graphcache/offline} for the full Offline Support docs.
963 * @see `@urql/exchange-graphcache/default-storage` for an example implementation using IndexedDB.
964 */
965export interface StorageAdapter {
966 /** Called to rehydrate data when the {@link cacheExchange} first loads.
967 * @remarks
968 * `readData` is called when Graphcache first starts up, and loads cache entries
969 * using which it'll repopulate its normalized cache data.
970 */
971 readData(): Promise<SerializedEntries>;
972 /** Called by the {@link cacheExchange} to write new data to the offline storage.
973 * @remarks
974 * `writeData` is called when Graphcache updated its cached data and wishes to
975 * persist this data to the offline storage. The data is a partial object and
976 * Graphcache does not write all its data at once.
977 */
978 writeData(delta: SerializedEntries): Promise<any>;
979 /** Called to rehydrate metadata when the {@link offlineExchange} first loads.
980 * @remarks
981 * `readMetadata` is called when Graphcache first starts up, and loads
982 * metadata informing it of pending mutations that failed while the device
983 * was offline.
984 */
985 readMetadata?(): Promise<null | SerializedRequest[]>;
986 /** Called by the {@link offlineExchange} to persist failed mutations.
987 * @remarks
988 * `writeMetadata` is called when a mutation failed to persist a queue
989 * of failed mutations to the offline storage that must be retried when
990 * the application is reloaded.
991 */
992 writeMetadata?(json: SerializedRequest[]): void;
993 /** Called to register a callback called when the device is back online.
994 * @remarks
995 * `onOnline` is called by the {@link offlineExchange} with a callback.
996 * This callback must be called when the device comes back online and
997 * will cause all failed mutations in the queue to be retried.
998 */
999 onOnline?(cb: () => void): any;
1000 /** Called when the cache has been hydrated with the data from `readData` */
1001 onCacheHydrated?(): any;
1002}
1003
1004/** Set of keys that have been modified or accessed.
1005 * @internal
1006 */
1007export type Dependencies = Set<string>;
1008
1009/** The type of cache operation being executed.
1010 * @internal
1011 */
1012export type OperationType = 'read' | 'write';
1013
1014/** Casts a given object type to have a required typename field.
1015 * @internal
1016 */
1017export type WithTypename<T extends { __typename?: any }> = T & {
1018 __typename: NonNullable<T['__typename']>;
1019};