Mirror: The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

authored by

Phil Pluckthun and committed by
GitHub
18bb0274 4cb752bf

+1669 -89
+5
.changeset/giant-tables-help.md
··· 1 + --- 2 + '@urql/core': patch 3 + --- 4 + 5 + Add TSDoc annotations to all external `@urql/core` APIs.
+422 -16
packages/core/src/client.ts
··· 51 51 getOperationType, 52 52 } from './utils'; 53 53 54 - /** Options for configuring the URQL [client]{@link Client}. */ 54 + /** Configuration options passed when creating a new {@link Client}. 55 + * 56 + * @remarks 57 + * The `ClientOptions` are passed when creating a new {@link Client}, and 58 + * are used to instantiate the pipeline of {@link Exchange | Exchanges}, configure 59 + * options used to initialize {@link OperationContext | OperationContexts}, or to 60 + * change the general behaviour of the {@link Client}. 61 + */ 55 62 export interface ClientOptions { 56 - /** Target endpoint URL such as `https://my-target:8080/graphql`. */ 63 + /** Target URL used by fetch exchanges to make GraphQL API requests to. 64 + * 65 + * @remarks 66 + * This is the URL that fetch exchanges will call to make GraphQL API requests. 67 + * This value is copied to {@link OperationContext.url}. 68 + */ 57 69 url: string; 58 - /** Any additional options to pass to fetch. */ 70 + /** Additional options used by fetch exchanges that'll be passed to the `fetch` call on API requests. 71 + * 72 + * @remarks 73 + * The options in this object or an object returned by a callback function will be merged into the 74 + * {@link RequestInit} options passed to the `fetch` call. 75 + * 76 + * Hint: If you're trying to implement more complex changes per {@link Operation}, it's worth considering 77 + * to use the {@link mapExchange} instead, which allows you to change `Operation`s and `OperationResult`s. 78 + * 79 + * Hint: If you're trying to use this as a function for authentication, consider checking out 80 + * `@urql/exchange-auth` instead, which allows you to handle refresh auth flows, and more 81 + * complex auth flows. 82 + * 83 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} for a description of this object. 84 + */ 59 85 fetchOptions?: RequestInit | (() => RequestInit); 60 - /** An alternative fetch implementation. */ 86 + /** A `fetch` function polyfill used by fetch exchanges to make API calls. 87 + * 88 + * @remarks 89 + * This is the fetch polyfill used by any fetch exchange to make an API request. By default, when this 90 + * option isn't set, any fetch exchange will attempt to use the globally available `fetch` function 91 + * to make a request instead. 92 + * 93 + * It's recommended to only pass a polyfill, if any of the environments you're running the {@link Client} 94 + * in don't support the Fetch API natively. 95 + * 96 + * Hint: If you're using the "Incremental Delivery" multipart spec, for instance with `@defer` directives, 97 + * you're better off using the native `fetch` function, or must ensure that your polyfill supports streamed 98 + * results. However, a "Streaming requests unsupported" error will be thrown, to let you know that your `fetch` 99 + * API doesn't support incrementally streamed responses, if this mode is used. 100 + * 101 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} for the Fetch API spec. 102 + */ 61 103 fetch?: typeof fetch; 62 - /** An ordered array of Exchanges. */ 104 + /** A list of `Exchange`s that will be used to create the `Client`'s execution pipeline. 105 + * 106 + * @remarks 107 + * The {@link Client} accepts and composes a list of {@link Exchange | Exchanges} into an “exchange pipeline” 108 + * which receive a stream of {@link Operation | Operations} the `Client` wishes to execute, and return a stream 109 + * of {@link OperationResult | OperationResults}. 110 + * 111 + * This is the basis for how `urql` handles GraphQL operations, and exchanges handle the creation, execution, 112 + * and control flow of exchanges for the `Client`. 113 + * 114 + * When this option is left out, the `Client` defaults to a list of {@link defaultExchanges}. By default, these 115 + * will implement deduping, caching (via a document cache), and fetching (GraphQL over HTTP). 116 + * 117 + * @see {@link https://formidable.com/open-source/urql/docs/architecture/#the-client-and-exchanges} for more information 118 + * on what `Exchange`s are and how they work. 119 + */ 63 120 exchanges?: Exchange[]; 64 - /** Activates support for Suspense. */ 121 + /** A configuration flag indicating whether support for "Suspense" is activated. 122 + * 123 + * @remarks 124 + * This configuration flag is only relevant for using `urql` with the React or Preact bindings. 125 + * When activated it allows `useQuery` to "suspend" instead of returning a loading state, which 126 + * will stop updates in a querying component and instead cascade 127 + * to a higher suspense boundary for a loading state. 128 + * 129 + * Hint: While, when this option is enabled, by default all `useQuery` hooks will suspense, you can 130 + * disable Suspense selectively for each hook. 131 + * 132 + * @see {@link https://beta.reactjs.org/blog/2022/03/29/react-v18#new-suspense-features} for more information on React Suspense. 133 + */ 65 134 suspense?: boolean; 66 - /** The default request policy for requests. */ 135 + /** The request and caching strategy that all `Operation`s on this `Client` will use by default. 136 + * 137 + * @remarks 138 + * The {@link RequestPolicy} instructs cache exchanges how to use and treat their cached results. 139 + * By default `cache-first` is set and used, which will use cache results, and only make an API request 140 + * on a cache miss. 141 + * 142 + * The `requestPolicy` can be overriden per operation, since it's added to the {@link OperationContext}, 143 + * which allows you to change the policy per `Operation`, rather than changing it by default here. 144 + * 145 + * Hint: We don’t recommend changing this from the default `cache-first` option, unless you know what 146 + * you‘re doing. Setting this to `cache-and-network` is not recommend and may not lead to the behaviour 147 + * you expect. If you’re looking to always update your cache frequently, use `@urql/exchange-request-policy` 148 + * instead. 149 + */ 67 150 requestPolicy?: RequestPolicy; 68 - /** Use HTTP GET for queries. */ 151 + /** Instructs fetch exchanges to use a GET request. 152 + * 153 + * @remarks 154 + * This changes the {@link OperationContext.preferGetMethod} option, which tells fetch exchanges 155 + * to use GET requests for queries instead of POST requests. 156 + * 157 + * When set to `true` or `'within-url-limit'`, built-in fetch exchanges will always attempt to send query 158 + * operations as GET requests, unless the resulting URL exceeds a length of 2,048 characters. 159 + * If you want to bypass this restriction, set this option to `'force'` instead, to always send GET. 160 + * requests for queries. 161 + */ 69 162 preferGetMethod?: boolean | 'force' | 'within-url-limit'; 70 - /** Mask __typename from results. */ 163 + /** Instructs the `Client` to remove `__typename` properties on all results. 164 + * 165 + * @remarks 166 + * By default, cache exchanges will alter your GraphQL documents to request `__typename` fields 167 + * for all selections. However, this means that your GraphQL data will now contain `__typename` fields you 168 + * didn't ask for. This is why the {@link Client} supports “masking” this field by marking it 169 + * as non-enumerable via this option. 170 + * 171 + * Only use this option if you absolutely have to. It's popular to model mutation inputs in 172 + * GraphQL schemas after the object types they modify, and if you're using this option to make 173 + * it possible to directly pass objects from results as inputs to your mutation variables, it's 174 + * more performant and idomatic to instead create a new input object. 175 + * 176 + * Hint: With `@urql/exchange-graphcache` you will never need this option, as it selects fields on 177 + * the client-side according to which fields you specified, rather than the fields it modified. 178 + * 179 + * @see {@link https://spec.graphql.org/October2021/#sec-Type-Name-Introspection} for more information 180 + * on typename introspection via the `__typename` field. 181 + */ 71 182 maskTypename?: boolean; 72 183 } 73 184 185 + /** The `Client` is the central hub for your GraphQL operations and holds `urql`'s state. 186 + * 187 + * @remarks 188 + * The `Client` manages your active GraphQL operations and their state, and contains the 189 + * {@link Exchange} pipeline to execute your GraphQL operations. 190 + * 191 + * It contains methods that allow you to execute GraphQL operations manually, but the `Client` 192 + * is also interacted with by bindings (for React, Preact, Vue, Svelte, etc) to execute GraphQL 193 + * operations. 194 + * 195 + * While {@link Exchange | Exchanges} are ultimately responsible for the control flow of operations, 196 + * sending API requests, and caching, the `Client` still has the important responsibility for 197 + * creating operations, managing consumers of active operations, sharing results for operations, 198 + * and more tasks as a “central hub”. 199 + * 200 + * @see {@link https://formidable.com/open-source/urql/docs/architecture/#requests-and-operations-on-the-client} for more information 201 + * on what the `Client` is and does. 202 + */ 74 203 export interface Client { 75 204 new (options: ClientOptions): Client; 76 205 206 + /** Exposes the stream of `Operation`s that is passed to the `Exchange` pipeline. 207 + * 208 + * @remarks 209 + * This is a Wonka {@link Source} that issues the {@link Operation | Operations} going into 210 + * the exchange pipeline. 211 + * @internal 212 + */ 77 213 operations$: Source<Operation>; 214 + 215 + /** Flag indicating whether support for “Suspense” is activated. 216 + * 217 + * @remarks 218 + * This flag indicates whether support for “Suspense” has been activated via the 219 + * {@link ClientOptions.suspense} flag. 220 + * 221 + * When this is enabled, the {@link Client} itself doesn’t function any differently, and the flag 222 + * only serves as an instructions for the React/Preact bindings to change their behaviour. 223 + * 224 + * @see {@link ClientOptions.suspense} for more information. 225 + * @internal 226 + */ 78 227 suspense: boolean; 79 228 80 - /** Start an operation from an exchange */ 81 - reexecuteOperation: (operation: Operation) => void; 82 - /** Event target for monitoring, e.g. for @urql/devtools */ 83 - subscribeToDebugTarget?: (onEvent: (e: DebugEvent) => void) => Subscription; 229 + /** Dispatches an `Operation` to the `Exchange` pipeline, if this `Operation` is active. 230 + * 231 + * @remarks 232 + * This method is frequently used in {@link Exchange | Exchanges}, for instance caches, to reexecute 233 + * an operation. It’s often either called because an `Operation` will need to be queried against the 234 + * cache again, if a cache result has changed or been invalidated, or it’s called with an {@link Operation}'s 235 + * {@link RequestPolicy} set to `network-only` to issue a network request. 236 + * 237 + * This method will only dispatch an {@link Operation} if it has active consumers, meaning, 238 + * active subscribers to the sources of {@link OperationResult}. For instance, if no bindings 239 + * (e.g. `useQuery`) is subscribed to the `Operation`, then `reexecuteOperation` will do nothing. 240 + * 241 + * All operations are put onto a queue and executed after a micro-tick. The queue of operations is 242 + * emptied eagerly and synchronously, similar to a trampoline scheduler. 243 + */ 244 + reexecuteOperation(operation: Operation): void; 84 245 246 + /** Subscribe method to add an event listener to debug events. 247 + * 248 + * @param onEvent - A callback called with new debug events, each time an `Exchange` issues them. 249 + * @returns A Wonka {@link Subscription} which is used to optionally terminate the event listener. 250 + * 251 + * @remarks 252 + * This is a method that's only available in development, and allows the `urql-devtools` to receive 253 + * to debug events that are issued by exchanges, giving the devtools more information about the flow 254 + * and execution of {@link Operation | Operations}. 255 + * 256 + * @see {@link DebugEventTypes} for a description of all debug events. 257 + * @internal 258 + */ 259 + subscribeToDebugTarget?(onEvent: (event: DebugEvent) => void): Subscription; 260 + 261 + /** Creates an `Operation` from a `GraphQLRequest` and optionally, overriding `OperationContext` options. 262 + * 263 + * @param kind - The {@link OperationType} of GraphQL operation, i.e. `query`, `mutation`, or `subscription`. 264 + * @param request - A {@link GraphQLRequest} created prior to calling this method. 265 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 266 + * @returns An {@link Operation} created from the parameters. 267 + * 268 + * @remarks 269 + * This method is expected to be called with a `kind` set to the `OperationType` of the GraphQL operation. 270 + * In development, this is enforced by checking that the GraphQL document's operation matches this `kind`. 271 + * 272 + * Hint: While bindings will use this method combined with {@link Client.executeRequestOperation}, if 273 + * you’re executing operations manually, you can use one of the other convenience methods instead. 274 + * 275 + * @see {@link Client.executeRequestOperation} for the method used to execute operations. 276 + * @see {@link createRequest} which creates a `GraphQLRequest` from a `DocumentNode` and variables. 277 + */ 85 278 createRequestOperation< 86 279 Data = any, 87 280 Variables extends AnyVariables = AnyVariables ··· 91 284 opts?: Partial<OperationContext> | undefined 92 285 ): Operation<Data, Variables>; 93 286 94 - /** Executes an Operation by sending it through the exchange pipeline It returns an observable that emits all related exchange results and keeps track of this observable's subscribers. A teardown signal will be emitted when no subscribers are listening anymore. */ 287 + /** Creates a `Source` that executes the `Operation` and issues `OperationResult`s for this `Operation`. 288 + * 289 + * @param operation - {@link Operation} that will be executed. 290 + * @returns A Wonka {@link Source} of {@link OperationResult | OperationResults} for the passed `Operation`. 291 + * 292 + * @remarks 293 + * The {@link Operation} will be dispatched to the pipeline of {@link Exchange | Exchanges} when 294 + * subscribing to the returned {@link Source}, which issues {@link OperationResult | OperationResults} 295 + * belonging to this `Operation`. 296 + * 297 + * Internally, {@link OperationResult | OperationResults} are filtered and deliverd to this source by 298 + * comparing the {@link Operation.key} on the operation and the {@link OperationResult.operation}. 299 + * For mutations, the {@link OperationContext._instance | `OperationContext._instance`} will additionally be compared, since two mutations 300 + * with, even given the same variables, will have two distinct results and will be executed separately. 301 + * 302 + * The {@link Client} dispatches the {@link Operation} when we subscribe to the returned {@link Source} 303 + * and will from then on consider the `Operation` as “active” until we unsubscribe. When all consumers unsubscribe 304 + * from an `Operation` and it becomes “inactive” a `teardown` signal will be dispatched to the 305 + * {@link Exchange | Exchanges}. 306 + * 307 + * Hint: While bindings will use this method, if you’re executing operations manually, you can use one 308 + * of the other convenience methods instead, like {@link Client.executeQuery} et al. 309 + */ 95 310 executeRequestOperation< 96 311 Data = any, 97 312 Variables extends AnyVariables = AnyVariables ··· 99 314 operation: Operation<Data, Variables> 100 315 ): Source<OperationResult<Data, Variables>>; 101 316 317 + /** Creates a `Source` that executes the GraphQL query operation created from the passed parameters. 318 + * 319 + * @param query - a GraphQL document containing the query operation that will be executed. 320 + * @param variables - the variables used to execute the operation. 321 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 322 + * @returns A {@link PromisifiedSource} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 323 + * 324 + * @remarks 325 + * The `Client.query` method is useful to programmatically create and issue a GraphQL query operation. 326 + * It automatically calls {@link createRequest}, {@link client.createRequestOperation}, and 327 + * {@link client.executeRequestOperation} for you, and is a convenience method. 328 + * 329 + * Since it returns a {@link PromisifiedSource} it may be chained with a `toPromise()` call to only 330 + * await a single result in an async function. 331 + * 332 + * Hint: This is the recommended way to create queries programmatically when not using the bindings, 333 + * or when you’re trying to get a single, promisified result. 334 + * 335 + * @example 336 + * ```ts 337 + * const getBookQuery = gql` 338 + * query GetBook($id: ID!) { 339 + * book(id: $id) { 340 + * id 341 + * name 342 + * author { 343 + * name 344 + * } 345 + * } 346 + * } 347 + * `; 348 + * 349 + * async function getBook(id) { 350 + * const result = await client.query(getBookQuery, { id }).toPromise(); 351 + * if (result.error) { 352 + * throw result.error; 353 + * } 354 + * 355 + * return result.data.book; 356 + * } 357 + * ``` 358 + */ 102 359 query<Data = any, Variables extends AnyVariables = AnyVariables>( 103 360 query: DocumentNode | TypedDocumentNode<Data, Variables> | string, 104 361 variables: Variables, 105 362 context?: Partial<OperationContext> 106 363 ): PromisifiedSource<OperationResult<Data, Variables>>; 107 364 365 + /** Returns the first synchronous result a `Client` provides for a given operation. 366 + * 367 + * @param query - a GraphQL document containing the query operation that will be executed. 368 + * @param variables - the variables used to execute the operation. 369 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 370 + * @returns An {@link OperationResult} if one became available synchronously or `null`. 371 + * 372 + * @remarks 373 + * The `Client.readQuery` method returns a result synchronously or defaults to `null`. This is useful 374 + * as it limits the result for a query operation to whatever the cache {@link Exchange} of a {@link Client} 375 + * had stored and available at that moment. 376 + * 377 + * In `urql`, it's expected that cache exchanges return their results synchronously. The bindings 378 + * and this method exploit this by using synchronous results, like these, to check what data is already 379 + * in the cache. 380 + * 381 + * This method is similar to what all bindings do to synchronously provide the initial state for queries, 382 + * regardless of whether effects afterwards that subscribe to the query operation update this state synchronously 383 + * or asynchronously. 384 + */ 108 385 readQuery<Data = any, Variables extends AnyVariables = AnyVariables>( 109 386 query: DocumentNode | TypedDocumentNode<Data, Variables> | string, 110 387 variables: Variables, 111 388 context?: Partial<OperationContext> 112 389 ): OperationResult<Data, Variables> | null; 113 390 391 + /** Creates a `Source` that executes the GraphQL query operation for the passed `GraphQLRequest`. 392 + * 393 + * @param query - a {@link GraphQLRequest} 394 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 395 + * @returns A {@link PromisifiedSource} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 396 + * 397 + * @remarks 398 + * The `Client.executeQuery` method is used to programmatically issue a GraphQL query operation. 399 + * It automatically calls {@link client.createRequestOperation} and {@link client.executeRequestOperation} for you, 400 + * but requires you to create a {@link GraphQLRequest} using {@link createRequest} yourself first. 401 + * 402 + * @see {@link Client.query} for a method that doesn't require calling {@link createRequest} yourself. 403 + */ 114 404 executeQuery<Data = any, Variables extends AnyVariables = AnyVariables>( 115 405 query: GraphQLRequest<Data, Variables>, 116 406 opts?: Partial<OperationContext> | undefined 117 407 ): Source<OperationResult<Data, Variables>>; 118 408 409 + /** Creates a `Source` that executes the GraphQL subscription operation created from the passed parameters. 410 + * 411 + * @param query - a GraphQL document containing the subscription operation that will be executed. 412 + * @param variables - the variables used to execute the operation. 413 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 414 + * @returns A Wonka {@link Source} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 415 + * 416 + * @remarks 417 + * The `Client.subscription` method is useful to programmatically create and issue a GraphQL subscription operation. 418 + * It automatically calls {@link createRequest}, {@link client.createRequestOperation}, and 419 + * {@link client.executeRequestOperation} for you, and is a convenience method. 420 + * 421 + * Hint: This is the recommended way to create subscriptions programmatically when not using the bindings. 422 + * 423 + * @example 424 + * ```ts 425 + * import { pipe, subscribe } from 'wonka'; 426 + * 427 + * const getNewsSubscription = gql` 428 + * subscription GetNews { 429 + * breakingNews { 430 + * id 431 + * text 432 + * createdAt 433 + * } 434 + * } 435 + * `; 436 + * 437 + * function subscribeToBreakingNews() { 438 + * const subscription = pipe( 439 + * client.subscription(getNewsSubscription, {}), 440 + * subscribe(result => { 441 + * if (result.data) { 442 + * console.log(result.data.breakingNews.text); 443 + * } 444 + * }) 445 + * ); 446 + * 447 + * return subscription.unsubscribe; 448 + * } 449 + * ``` 450 + */ 119 451 subscription<Data = any, Variables extends AnyVariables = AnyVariables>( 120 452 query: DocumentNode | TypedDocumentNode<Data, Variables> | string, 121 453 variables: Variables, 122 454 context?: Partial<OperationContext> 123 455 ): Source<OperationResult<Data, Variables>>; 124 456 457 + /** Creates a `Source` that executes the GraphQL subscription operation for the passed `GraphQLRequest`. 458 + * 459 + * @param query - a {@link GraphQLRequest} 460 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 461 + * @returns A {@link PromisifiedSource} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 462 + * 463 + * @remarks 464 + * The `Client.executeSubscription` method is used to programmatically issue a GraphQL subscription operation. 465 + * It automatically calls {@link client.createRequestOperation} and {@link client.executeRequestOperation} for you, 466 + * but requires you to create a {@link GraphQLRequest} using {@link createRequest} yourself first. 467 + * 468 + * @see {@link Client.subscription} for a method that doesn't require calling {@link createRequest} yourself. 469 + */ 125 470 executeSubscription< 126 471 Data = any, 127 472 Variables extends AnyVariables = AnyVariables ··· 130 475 opts?: Partial<OperationContext> | undefined 131 476 ): Source<OperationResult<Data, Variables>>; 132 477 478 + /** Creates a `Source` that executes the GraphQL mutation operation created from the passed parameters. 479 + * 480 + * @param query - a GraphQL document containing the mutation operation that will be executed. 481 + * @param variables - the variables used to execute the operation. 482 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 483 + * @returns A {@link PromisifiedSource} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 484 + * 485 + * @remarks 486 + * The `Client.mutation` method is useful to programmatically create and issue a GraphQL mutation operation. 487 + * It automatically calls {@link createRequest}, {@link client.createRequestOperation}, and 488 + * {@link client.executeRequestOperation} for you, and is a convenience method. 489 + * 490 + * Since it returns a {@link PromisifiedSource} it may be chained with a `toPromise()` call to only 491 + * await a single result in an async function. Since mutations will only typically issue one result, 492 + * using this method is recommended. 493 + * 494 + * Hint: This is the recommended way to create mutations programmatically when not using the bindings, 495 + * or when you’re trying to get a single, promisified result. 496 + * 497 + * @example 498 + * ```ts 499 + * const createPostMutation = gql` 500 + * mutation CreatePost($text: String!) { 501 + * createPost(text: $text) { 502 + * id 503 + * text 504 + * } 505 + * } 506 + * `; 507 + * 508 + * async function createPost(text) { 509 + * const result = await client.mutation(createPostMutation, { 510 + * text, 511 + * }).toPromise(); 512 + * if (result.error) { 513 + * throw result.error; 514 + * } 515 + * 516 + * return result.data.createPost; 517 + * } 518 + * ``` 519 + */ 133 520 mutation<Data = any, Variables extends AnyVariables = AnyVariables>( 134 521 query: DocumentNode | TypedDocumentNode<Data, Variables> | string, 135 522 variables: Variables, 136 523 context?: Partial<OperationContext> 137 524 ): PromisifiedSource<OperationResult<Data, Variables>>; 138 525 526 + /** Creates a `Source` that executes the GraphQL mutation operation for the passed `GraphQLRequest`. 527 + * 528 + * @param query - a {@link GraphQLRequest} 529 + * @param opts - {@link OperationContext} options that'll override and be merged with options from the {@link ClientOptions}. 530 + * @returns A {@link PromisifiedSource} issuing the {@link OperationResult | OperationResults} for the GraphQL operation. 531 + * 532 + * @remarks 533 + * The `Client.executeMutation` method is used to programmatically issue a GraphQL mutation operation. 534 + * It automatically calls {@link client.createRequestOperation} and {@link client.executeRequestOperation} for you, 535 + * but requires you to create a {@link GraphQLRequest} using {@link createRequest} yourself first. 536 + * 537 + * @see {@link Client.mutation} for a method that doesn't require calling {@link createRequest} yourself. 538 + */ 139 539 executeMutation<Data = any, Variables extends AnyVariables = AnyVariables>( 140 540 query: GraphQLRequest<Data, Variables>, 141 541 opts?: Partial<OperationContext> | undefined ··· 278 678 279 679 createRequestOperation(kind, request, opts) { 280 680 if (!opts) opts = {}; 281 - const requestOperationType = getOperationType(request.query); 681 + 682 + let requestOperationType: string | undefined; 282 683 if ( 283 684 process.env.NODE_ENV !== 'production' && 284 685 kind !== 'teardown' && 285 - requestOperationType !== kind 686 + (requestOperationType = getOperationType(request.query)) !== kind 286 687 ) { 287 688 throw new Error( 288 689 `Expected operation of type "${kind}" but found "${requestOperationType}"` 289 690 ); 290 691 } 692 + 291 693 return makeOperation(kind, request, { 292 694 _instance: 293 695 kind === 'mutation' ··· 436 838 return client; 437 839 } as any; 438 840 841 + /** Accepts `ClientOptions` and creates a `Client`. 842 + * @param opts - A {@link ClientOptions} objects with options for the `Client`. 843 + * @returns A {@link Client} instantiated with `opts`. 844 + */ 439 845 export const createClient = (Client as any) as (opts: ClientOptions) => Client;
+23 -1
packages/core/src/exchanges/cache.ts
··· 17 17 const shouldSkip = ({ kind }: Operation) => 18 18 kind !== 'mutation' && kind !== 'query'; 19 19 20 + /** Default document cache exchange. 21 + * 22 + * @remarks 23 + * The default document cache in `urql` avoids sending the same GraphQL request 24 + * multiple times by caching it using the {@link Operation.key}. It will invalidate 25 + * query results automatically whenever it sees a mutation responses with matching 26 + * `__typename`s in their responses. 27 + * 28 + * The document cache will get the introspected `__typename` fields by modifying 29 + * your GraphQL operation documents using the {@link formatDocument} utility. 30 + * 31 + * This automatic invalidation strategy can fail if your query or mutation don’t 32 + * contain matching typenames, for instance, because the query contained an 33 + * empty list. 34 + * You can manually add hints for this exchange by specifying a list of 35 + * {@link OperationContext.additionalTypenames} for queries and mutations that 36 + * should invalidate one another. 37 + * 38 + * @see {@link https://formidable.com/open-source/urql/docs/basics/document-caching/} for more information on this cache. 39 + */ 20 40 export const cacheExchange: Exchange = ({ forward, client, dispatchDebug }) => { 21 41 const resultCache: ResultCache = new Map(); 22 42 const operationCache: OperationCache = new Map(); ··· 148 168 }; 149 169 }; 150 170 151 - // Reexecutes a given operation with the default requestPolicy 171 + /** Reexecutes an `Operation` with the `network-only` request policy. 172 + * @internal 173 + */ 152 174 export const reexecuteOperation = (client: Client, operation: Operation) => { 153 175 return client.reexecuteOperation( 154 176 makeOperation(operation.kind, operation, {
+17 -4
packages/core/src/exchanges/compose.ts
··· 1 - import { Exchange, ExchangeInput } from '../types'; 1 + import type { ExchangeIO, Exchange, ExchangeInput } from '../types'; 2 2 3 - /** This composes an array of Exchanges into a single ExchangeIO function */ 4 - export const composeExchanges = (exchanges: Exchange[]) => ({ 3 + /** Composes an array of Exchanges into a single one. 4 + * 5 + * @param exchanges - An array of {@link Exchange | Exchanges}. 6 + * @returns - A composed {@link Exchange}. 7 + * 8 + * @remarks 9 + * `composeExchanges` returns an {@link Exchange} that when instantiated 10 + * composes the array of passed `Exchange`s into one, calling them from 11 + * right to left, with the prior `Exchange`’s {@link ExchangeIO} function 12 + * as the {@link ExchangeInput.forward} input. 13 + * 14 + * This simply merges all exchanges into one and is used by the {@link Client} 15 + * to merge the `exchanges` option it receives. 16 + */ 17 + export const composeExchanges = (exchanges: Exchange[]): Exchange => ({ 5 18 client, 6 19 forward, 7 20 dispatchDebug, 8 - }: ExchangeInput) => 21 + }: ExchangeInput): ExchangeIO => 9 22 exchanges.reduceRight( 10 23 (forward, exchange) => 11 24 exchange({
+14
packages/core/src/exchanges/debug.ts
··· 1 1 import { pipe, tap } from 'wonka'; 2 2 import { Exchange } from '../types'; 3 3 4 + /** Simple log debugger exchange. 5 + * 6 + * @remarks 7 + * An exchange that logs incoming {@link Operation | Operations} and 8 + * {@link OperationResult | OperationResults} in development. 9 + * 10 + * This exchange is a no-op in production and often used in issue reporting 11 + * to understand certain usage patterns of `urql` without having access to 12 + * the original source code. 13 + * 14 + * Hint: When you report an issue you’re having with `urql`, adding 15 + * this as your first exchange and posting its output can speed up 16 + * issue triaging a lot! 17 + */ 4 18 export const debugExchange: Exchange = ({ forward }) => { 5 19 if (process.env.NODE_ENV === 'production') { 6 20 return ops$ => forward(ops$);
+16 -1
packages/core/src/exchanges/dedup.ts
··· 1 1 import { filter, pipe, tap } from 'wonka'; 2 2 import { Exchange, Operation, OperationResult } from '../types'; 3 3 4 - /** A default exchange for debouncing GraphQL requests. */ 4 + /** Default deduplication exchange. 5 + * 6 + * @remarks 7 + * The `dedupExchange` deduplicates queries and subscriptions that are 8 + * started with identical documents and variables by deduplicating by 9 + * their {@link Operation.key}. 10 + * This can prevent duplicate requests from being sent to your GraphQL API. 11 + * 12 + * Because this is a very safe exchange to add to any GraphQL setup, it’s 13 + * not only the default, but we also recommend you to always keep this 14 + * exchange added and included in your setup. 15 + * 16 + * Hint: In React and Vue, some common usage patterns can trigger duplicate 17 + * operations. For instance, in React a single render will actually 18 + * trigger two phases that execute an {@link Operation}. 19 + */ 5 20 export const dedupExchange: Exchange = ({ forward, dispatchDebug }) => { 6 21 const inFlightKeys = new Set<number>(); 7 22
+10 -7
packages/core/src/exchanges/fallback.ts
··· 1 1 import { filter, pipe, tap } from 'wonka'; 2 2 import { Operation, ExchangeIO, ExchangeInput } from '../types'; 3 - import { noop } from '../utils'; 4 3 5 - /** This is always the last exchange in the chain; No operation should ever reach it */ 4 + /** Used by the `Client` as the last exchange to warn about unhandled operations. 5 + * 6 + * @remarks 7 + * In a normal setup, some operations may go unhandled when a {@link Client} isn’t set up 8 + * with the right exchanges. 9 + * For instance, a `Client` may be missing a fetch exchange, or an exchange handling subscriptions. 10 + * This {@link Exchange} is added by the `Client` automatically to log warnings about unhandled 11 + * {@link Operaiton | Operations} in development. 12 + */ 6 13 export const fallbackExchange: ({ 7 14 dispatchDebug, 8 15 }: Pick<ExchangeInput, 'dispatchDebug'>) => ExchangeIO = ({ ··· 25 32 console.warn(message); 26 33 } 27 34 }), 28 - /* All operations that skipped through the entire exchange chain should be filtered from the output */ 35 + // All operations that skipped through the entire exchange chain should be filtered from the output 29 36 filter<any>(() => false) 30 37 ); 31 - 32 - export const fallbackExchangeIO: ExchangeIO = fallbackExchange({ 33 - dispatchDebug: noop, 34 - });
+17 -1
packages/core/src/exchanges/fetch.ts
··· 9 9 makeFetchSource, 10 10 } from '../internal'; 11 11 12 - /** A default exchange for fetching GraphQL requests. */ 12 + /** Default GraphQL over HTTP fetch exchange. 13 + * 14 + * @remarks 15 + * The default fetch exchange in `urql` supports sending GraphQL over HTTP 16 + * requests, can optionally send GraphQL queries as GET requests, and 17 + * handles incremental multipart responses. 18 + * 19 + * This exchange does not handle persisted queries or multipart uploads. 20 + * Support for the former can be added using `@urql/exchange-persisted-fetch` 21 + * and the latter using `@urql/exchange-multipart-fetch`. 22 + * 23 + * Hint: The `fetchExchange` and the two other exchanges all use the built-in fetch 24 + * utilities in `@urql/core/internal`, which you can also use to implement 25 + * a customized fetch exchange. 26 + * 27 + * @see {@link makeFetchSource} for the shared utility calling the Fetch API. 28 + */ 13 29 export const fetchExchange: Exchange = ({ forward, dispatchDebug }) => { 14 30 return ops$ => { 15 31 const sharedOps$ = share(ops$);
+9 -1
packages/core/src/exchanges/index.ts
··· 4 4 export { debugExchange } from './debug'; 5 5 export { dedupExchange } from './dedup'; 6 6 export { fetchExchange } from './fetch'; 7 - export { fallbackExchangeIO } from './fallback'; 8 7 export { composeExchanges } from './compose'; 9 8 10 9 export type { ··· 20 19 import { dedupExchange } from './dedup'; 21 20 import { fetchExchange } from './fetch'; 22 21 22 + /** The default list of exchanges a `Client` falls back to. 23 + * 24 + * @remarks 25 + * When {@link ClientOptions.exchanges} isn’s passed, a {@link Client} is automatically 26 + * created using this list of default exchanges. 27 + * 28 + * By default, this adds deduplication of operations, a basic document cache, 29 + * and the built-in fetch exchange for GraphQL over HTTP. 30 + */ 23 31 export const defaultExchanges = [dedupExchange, cacheExchange, fetchExchange];
+56
packages/core/src/exchanges/map.ts
··· 2 2 import { Operation, OperationResult, Exchange } from '../types'; 3 3 import { CombinedError } from '../utils'; 4 4 5 + /** Options for the `mapExchange` allowing it to react to incoming operations, results, or errors. */ 5 6 export interface MapExchangeOpts { 7 + /** Accepts a callback for incoming `Operation`s. 8 + * 9 + * @param operation - An {@link Operation} that the {@link mapExchange} received. 10 + * @returns optionally a new {@link Operation} replacing the original. 11 + * 12 + * @remarks 13 + * You may return new {@link Operation | Operations} from this function replacing 14 + * the original that the {@link mapExchange} received. 15 + * It’s recommended that you use the {@link makeOperation} utility to create a copy 16 + * of the original when you do this. (However, this isn’t required) 17 + * 18 + * Hint: The callback may also be promisified and return a new {@link Operation} asynchronously, 19 + * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, 20 + * like after your `cacheExchange`. 21 + */ 6 22 onOperation?(operation: Operation): Promise<Operation> | Operation | void; 23 + /** Accepts a callback for incoming `OperationResult`s. 24 + * 25 + * @param result - An {@link OperationResult} that the {@link mapExchange} received. 26 + * @returns optionally a new {@link OperationResult} replacing the original. 27 + * 28 + * @remarks 29 + * This callback may optionally return a new {@link OperationResult} that replaces the original, 30 + * which you can use to modify incoming API results. 31 + * 32 + * Hint: The callback may also be promisified and return a new {@link Operation} asynchronously, 33 + * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, 34 + * like after your `cacheExchange`. 35 + */ 7 36 onResult?( 8 37 result: OperationResult 9 38 ): Promise<OperationResult> | OperationResult | void; 39 + /** Accepts a callback for incoming `CombinedError`s. 40 + * 41 + * @param error - A {@link CombinedError} that an incoming {@link OperationResult} contained. 42 + * @param operation - The {@link Operation} of the incoming {@link OperationResult}. 43 + * 44 + * @remarks 45 + * The callback may also be promisified and return a new {@link Operation} asynchronously, 46 + * provided you place your {@link mapExchange} after all synchronous {@link Exchange | Exchanges}, 47 + * like after your `cacheExchange`. 48 + */ 10 49 onError?(error: CombinedError, operation: Operation): void; 11 50 } 12 51 52 + /** Creates an `Exchange` mapping over incoming operations, results, and/or errors. 53 + * 54 + * @param opts - A {@link MapExchangeOpts} configuration object, containing the callbacks the `mapExchange` will use. 55 + * @returns the created {@link Exchange} 56 + * 57 + * @remarks 58 + * The `mapExchange` may be used to react to or modify incoming {@link Operation | Operations} 59 + * and {@link OperationResult | OperationResults}. Optionally, it can also modify these 60 + * asynchronously, when a promise is returned from the callbacks. 61 + * 62 + * This is useful to, for instance, add an authentication token to a given request, when 63 + * the `@urql/exchange-auth` package would be overkill. 64 + * 65 + * It can also accept an `onError` callback, which can be used to react to incoming 66 + * {@link CombinedError | CombinedErrors} on results, and trigger side-effects. 67 + * 68 + */ 13 69 export const mapExchange = ({ 14 70 onOperation, 15 71 onResult,
+93 -5
packages/core/src/exchanges/ssr.ts
··· 4 4 import { addMetadata, CombinedError } from '../utils'; 5 5 import { reexecuteOperation } from './cache'; 6 6 7 + /** A serialized version of an {@link OperationResult}. 8 + * 9 + * @remarks 10 + * All properties are serialized separately as JSON strings, except for the 11 + * {@link CombinedError} to speed up JS parsing speed, even if a result doesn’t 12 + * end up being used. 13 + * 14 + * @internal 15 + */ 7 16 export interface SerializedResult { 8 17 hasNext?: boolean; 18 + /** JSON-serialized version of {@link OperationResult.data}. */ 9 19 data?: string | undefined; // JSON string of data 10 - extensions?: string | undefined; // JSON string of data 20 + /** JSON-serialized version of {@link OperationResult.extensions}. */ 21 + extensions?: string | undefined; 22 + /** JSON version of {@link CombinedError}. */ 11 23 error?: { 12 24 graphQLErrors: Array<Partial<GraphQLError> | string>; 13 25 networkError?: string; 14 26 }; 15 27 } 16 28 29 + /** A dictionary of {@link Operation.key} keys to serializable {@link SerializedResult} objects. 30 + * 31 + * @remarks 32 + * It’s not recommended to modify the serialized data manually, however, multiple payloads of 33 + * this dictionary may safely be merged and combined. 34 + */ 17 35 export interface SSRData { 18 36 [key: string]: SerializedResult; 19 37 } 20 38 39 + /** Options for the `ssrExchange` allowing it to either operate on the server- or client-side. */ 21 40 export interface SSRExchangeParams { 41 + /** Indicates to the {@link SSRExchange} whether it's currently in server-side or client-side mode. 42 + * 43 + * @remarks 44 + * Depending on this option, the {@link SSRExchange} will either capture or replay results. 45 + * When `true`, it’s in client-side mode and results will be serialized. When `false`, it’ll 46 + * use its deserialized data and replay results from it. 47 + */ 22 48 isClient?: boolean; 49 + /** May be used on the client-side to pass the {@link SSRExchange} serialized data from the server-side. 50 + * 51 + * @remarks 52 + * Alternatively, {@link SSRExchange.restoreData} may be called to imperatively add serialized data to 53 + * the exchange. 54 + * 55 + * Hint: This method also works on the server-side to add to the initial serialized data, which enables 56 + * you to combine multiple {@link SSRExchange} results, as needed. 57 + */ 23 58 initialState?: SSRData; 59 + /** Forces a new API request to be sent in the background after replaying the deserialized result. 60 + * 61 + * @remarks 62 + * Similarly to the `cache-and-network` {@link RequestPolicy}, this option tells the {@link SSRExchange} 63 + * to send a new API request for the {@link Operation} after replaying a serialized result. 64 + * 65 + * Hint: This is useful when you're caching SSR results and need the client-side to update itself after 66 + * rendering the initial serialized SSR results. 67 + */ 24 68 staleWhileRevalidate?: boolean; 69 + /** Forces {@link OperationResult.extensions} to be serialized alongside the rest of a result. 70 + * 71 + * @remarks 72 + * Entries in the `extension` object of a GraphQL result are often non-standard metdata, and many 73 + * APIs use it for data that changes between every request. As such, the {@link SSRExchange} will 74 + * not serialize this data by default, unless this flag is set. 75 + */ 25 76 includeExtensions?: boolean; 26 77 } 27 78 79 + /** An `SSRExchange` either in server-side mode, serializing results, or client-side mode, deserializing and replaying results.. 80 + * 81 + * @remarks 82 + * This same {@link Exchange} is used in your code both for the client-side and server-side as it’s “universal” 83 + * and can be put into either client-side or server-side mode using the {@link SSRExchangeParams.isClient} flag. 84 + * 85 + * In server-side mode, the `ssrExchange` will “record” results it sees from your API and provide them for you 86 + * to send to the client-side using the {@link SSRExchange.extractData} method. 87 + * 88 + * In client-side mode, the `ssrExchange` will use these serialized results, rehydrated either using 89 + * {@link SSRExchange.restoreData} or {@link SSRexchangeParams.initialState}, to replay results the 90 + * server-side has seen and sent before. 91 + * 92 + * Each serialized result will only be replayed once, as it’s assumed that your cache exchange will have the 93 + * results cached afterwards. 94 + */ 28 95 export interface SSRExchange extends Exchange { 29 - /** Rehydrates cached data */ 96 + /** Client-side method to add serialized results to the {@link SSRExchange}. 97 + * @param data - {@link SSRData}, 98 + */ 30 99 restoreData(data: SSRData): void; 31 - /** Extracts cached data */ 100 + /** Server-side method to get all serialized results the {@link SSRExchange} has captured. 101 + * @returns an {@link SSRData} dictionary. 102 + */ 32 103 extractData(): SSRData; 33 104 } 34 105 ··· 65 136 return result; 66 137 }; 67 138 68 - /** Deserialize plain JSON to an OperationResult */ 139 + /** Deserialize plain JSON to an OperationResult 140 + * @internal 141 + */ 69 142 const deserializeResult = ( 70 143 operation: Operation, 71 144 result: SerializedResult, ··· 90 163 91 164 const revalidated = new Set<number>(); 92 165 93 - /** The ssrExchange can be created to capture data during SSR and also to rehydrate it on the client */ 166 + /** Creates a server-side rendering `Exchange` that either captures responses on the server-side or replays them on the client-side. 167 + * 168 + * @param params - An {@link SSRExchangeParams} configuration object. 169 + * @returns the created {@link SSRExchange} 170 + * 171 + * @remarks 172 + * When dealing with server-side rendering, we essentially have two {@link Client | Clients} making requests, 173 + * the server-side client, and the client-side one. The `ssrExchange` helps implementing a tiny cache on both 174 + * sides that: 175 + * 176 + * - captures results on the server-side which it can serialize, 177 + * - replays results on the client-side that it deserialized from the server-side. 178 + * 179 + * Hint: The `ssrExchange` is basically an exchange that acts like a replacement for any fetch exchange 180 + * temporarily. As such, you should place it after your cache exchange but in front of any fetch exchange. 181 + */ 94 182 export const ssrExchange = (params: SSRExchangeParams = {}): SSRExchange => { 95 183 const staleWhileRevalidate = !!params.staleWhileRevalidate; 96 184 const includeExtensions = !!params.includeExtensions;
+81 -4
packages/core/src/exchanges/subscription.ts
··· 25 25 OperationResult, 26 26 } from '../types'; 27 27 28 + /** An abstract observer-like interface. 29 + * 30 + * @remarks 31 + * Observer-like interfaces are passed to {@link ObservableLike.subscribe} to provide them 32 + * with callbacks for their events. 33 + * 34 + * @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal. 35 + */ 28 36 export interface ObserverLike<T> { 37 + /** Callback for values an {@link ObservableLike} emits. */ 29 38 next: (value: T) => void; 39 + /** Callback for an error an {@link ObservableLike} emits, which ends the subscription. */ 30 40 error: (err: any) => void; 41 + /** Callback for the completion of an {@link ObservableLike}, which ends the subscription. */ 31 42 complete: () => void; 32 43 } 33 44 34 - /** An abstract observable interface conforming to: https://github.com/tc39/proposal-observable */ 45 + /** An abstract observable-like interface. 46 + * 47 + * @remarks 48 + * Observable, or Observable-like interfaces, are often used by GraphQL transports to abstract 49 + * how they send {@link ExecutionResult | ExecutionResults} to consumers. These generally contain 50 + * a `subscribe` method accepting an {@link ObserverLike} structure. 51 + * 52 + * @see {@link https://github.com/tc39/proposal-observable} for the full TC39 Observable proposal. 53 + */ 35 54 export interface ObservableLike<T> { 55 + /** Start the Observable-like subscription and returns a subscription handle. 56 + * 57 + * @param observer - an {@link ObserverLike} object with result, error, and completion callbacks. 58 + * @returns a subscription handle providing an `unsubscribe` method to stop the subscription. 59 + */ 36 60 subscribe( 37 61 observer: ObserverLike<T> 38 62 ): { ··· 40 64 }; 41 65 } 42 66 67 + /** A more cross-compatible version of the {@link Operation} structure. 68 + * 69 + * @remarks 70 + * When the `subscriptionExchange` was first created, some transports needed a specific shape 71 + * of {@link GraphQLRequest} objects to be passed to them. This is a shim that is as compatible 72 + * with most transports out of the box as possible. 73 + */ 43 74 export interface SubscriptionOperation { 44 75 query: string; 45 76 variables: Record<string, unknown> | undefined; ··· 47 78 context: OperationContext; 48 79 } 49 80 81 + /** A subscription forwarding function, which must accept a {@link SubscriptionOperation}. 82 + * 83 + * @param operation - A {@link SubscriptionOperation} 84 + * @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}. 85 + */ 50 86 export type SubscriptionForwarder = ( 51 87 operation: SubscriptionOperation 52 88 ) => ObservableLike<ExecutionResult>; 53 89 54 90 /** This is called to create a subscription and needs to be hooked up to a transport client. */ 55 91 export interface SubscriptionExchangeOpts { 56 - // This has been modelled to work with subscription-transport-ws 57 - // See: https://github.com/apollographql/subscriptions-transport-ws#requestoptions--observableexecutionresult-returns-observable-to-execute-the-operation 92 + /** A subscription forwarding function, which must accept a {@link SubscriptionOperation}. 93 + * 94 + * @param operation - A {@link SubscriptionOperation} 95 + * @returns An {@link ObservableLike} object issuing {@link ExecutionResult | ExecutionResults}. 96 + * 97 + * @remarks 98 + * This callback is called for each {@link Operation} that this `subscriptionExchange` will 99 + * handle. It receives the {@link SubscriptionOperation}, which is a more compatible version 100 + * of the raw {@link Operation} objects and must return an {@link ObservableLike} of results. 101 + */ 58 102 forwardSubscription: SubscriptionForwarder; 59 103 60 - /** This flag may be turned on to allow your subscriptions-transport to handle all operation types */ 104 + /** Flag to enable this exchange to handle all types of GraphQL operations. 105 + * 106 + * @remarks 107 + * When you aren’t using fetch exchanges and GraphQL over HTTP as a transport for your GraphQL requests, 108 + * or you have a third-party GraphQL transport implementation, which must also be used for queries and 109 + * mutations, this flag may be used to allow this exchange to handle all kinds of GraphQL operations. 110 + * 111 + * By default, this flag is `false` and the exchange will only handle GraphQL subscription operations. 112 + */ 61 113 enableAllOperations?: boolean; 114 + 115 + /** A predicate function that causes an operation to be handled by this `subscriptionExchange` if `true` is returned. 116 + * 117 + * @param operation - an {@link Operation} 118 + * @returns true when the operation is handled by this exchange. 119 + * 120 + * @remarks 121 + * In some cases, a `subscriptionExchange` will be used to only handle some {@link Operation | Operations}, 122 + * e.g. all that contain `@live` directive. For these cases, this function may be passed to precisely 123 + * determine which `Operation`s this exchange should handle, instead of forwarding. 124 + * 125 + * When specified, the {@link SubscriptionExchangeOpts.enableAllOperations} flag is disregarded. 126 + */ 62 127 isSubscriptionOperation?: (operation: Operation) => boolean; 63 128 } 64 129 130 + /** Generic subscription exchange factory used to either create an exchange handling just subscriptions or all operation kinds. 131 + * 132 + * @remarks 133 + * `subscriptionExchange` can be used to create an {@link Exchange} that either 134 + * handles just GraphQL subscription operations, or optionally all operations, 135 + * when the {@link SubscriptionExchangeOpts.enableAllOperations} flag is passed. 136 + * 137 + * The {@link SubscriptionExchangeOpts.forwardSubscription} function must 138 + * be provided and provides a generic input that's based on {@link Operation} 139 + * but is compatible with many libraries implementing GraphQL request or 140 + * subscription interfaces. 141 + */ 65 142 export const subscriptionExchange = ({ 66 143 forwardSubscription, 67 144 enableAllOperations,
+46
packages/core/src/gql.ts
··· 34 34 } 35 35 }; 36 36 37 + /** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}. 38 + * 39 + * @remarks 40 + * The `gql` tag or function is used to parse a GraphQL query document into a {@link DocumentNode}. 41 + * 42 + * When used as a tagged template, `gql` will automatically merge fragment definitions into the resulting 43 + * document and deduplicate them. 44 + * 45 + * It enforces that all fragments have a unique name. When fragments with different definitions share a name, 46 + * it will log a warning in development. 47 + * 48 + * Hint: It’s recommended to use this `gql` function over other GraphQL parse functions, since it puts the parsed 49 + * results directly into `@urql/core`’s internal caches and prevents further unnecessary work. 50 + * 51 + * @example 52 + * ```ts 53 + * const AuthorFragment = gql` 54 + * fragment AuthorDisplayComponent on Author { 55 + * id 56 + * name 57 + * } 58 + * `; 59 + * 60 + * const BookFragment = gql` 61 + * fragment ListBookComponent on Book { 62 + * id 63 + * title 64 + * author { 65 + * ...AuthorDisplayComponent 66 + * } 67 + * } 68 + * 69 + * ${AuthorFragment} 70 + * `; 71 + * 72 + * const BookQuery = gql` 73 + * query Book($id: ID!) { 74 + * book(id: $id) { 75 + * ...BookFragment 76 + * } 77 + * } 78 + * 79 + * ${BookFragment} 80 + * `; 81 + * ``` 82 + */ 37 83 function gql<Data = any, Variables extends AnyVariables = AnyVariables>( 38 84 strings: TemplateStringsArray, 39 85 ...interpolations: Array<TypedDocumentNode | DocumentNode | string>
+30
packages/core/src/internal/fetchOptions.ts
··· 5 5 } from '../utils'; 6 6 import { AnyVariables, GraphQLRequest, Operation } from '../types'; 7 7 8 + /** Abstract definition of the JSON data sent during GraphQL HTTP POST requests. */ 8 9 export interface FetchBody { 9 10 query?: string; 10 11 operationName: string | undefined; ··· 12 13 extensions: undefined | Record<string, any>; 13 14 } 14 15 16 + /** Creates a GraphQL over HTTP compliant JSON request body. 17 + * @param request - An object containing a `query` document and `variables`. 18 + * @returns A {@link FetchBody} 19 + * @see {@link https://github.com/graphql/graphql-over-http} for the GraphQL over HTTP spec. 20 + */ 15 21 export function makeFetchBody< 16 22 Data = any, 17 23 Variables extends AnyVariables = AnyVariables ··· 24 30 }; 25 31 } 26 32 33 + /** Creates a URL that will be called for a GraphQL HTTP request. 34 + * 35 + * @param operation - An {@link Operation} for which to make the request. 36 + * @param body - A {@link FetchBody} which may be replaced with a URL. 37 + * 38 + * @remarks 39 + * Creates the URL that’ll be called as part of a GraphQL HTTP request. 40 + * Built-in fetch exchanges support sending GET requests, even for 41 + * non-persisted full requests, which this function supports by being 42 + * able to serialize GraphQL requests into the URL. 43 + */ 27 44 export const makeFetchURL = ( 28 45 operation: Operation, 29 46 body?: FetchBody ··· 50 67 return finalUrl; 51 68 }; 52 69 70 + /** Creates a `RequestInit` object for a given `Operation`. 71 + * 72 + * @param operation - An {@link Operation} for which to make the request. 73 + * @param body - A {@link FetchBody} which is added to the options, if the request isn’t a GET request. 74 + * 75 + * @remarks 76 + * Creates the fetch options {@link RequestInit} object that’ll be passed to the Fetch API 77 + * as part of a GraphQL over HTTP request. It automatically sets a default `Content-Type` 78 + * header. 79 + * 80 + * @see {@link https://github.com/graphql/graphql-over-http} for the GraphQL over HTTP spec. 81 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} for the Fetch API spec. 82 + */ 53 83 export const makeFetchOptions = ( 54 84 operation: Operation, 55 85 body?: FetchBody
+27
packages/core/src/internal/fetchSource.ts
··· 15 15 ? (input as Buffer).toString() 16 16 : decoder!.decode(input as ArrayBuffer); 17 17 18 + /** Makes a GraphQL HTTP request to a given API by wrapping around the Fetch API. 19 + * 20 + * @param operation - The {@link Operation} that should be sent via GraphQL over HTTP. 21 + * @param url - The endpoint URL for the GraphQL HTTP API. 22 + * @param fetchOptions - The {@link RequestInit} fetch options for the request. 23 + * @returns A Wonka {@link Source} of {@link OperationResult | OperationResults}. 24 + * 25 + * @remarks 26 + * This utility defines how all built-in fetch exchanges make GraphQL HTTP requests, 27 + * supporting multipart incremental responses, cancellation and other smaller 28 + * implementation details. 29 + * 30 + * If you’re implementing a modified fetch exchange for a GraphQL over HTTP API 31 + * it’s recommended you use this utility. 32 + * 33 + * Hint: This function does not use the passed `operation` to create or modify the 34 + * `fetchOptions` and instead expects that the options have already been created 35 + * using {@link makeFetchOptions} and modified as needed. 36 + * 37 + * @throws 38 + * If the `fetch` polyfill or globally available `fetch` function doesn’t support 39 + * streamed multipart responses while trying to handle a `multipart/mixed` GraphQL response, 40 + * the source will throw “Streaming requests unsupported”. 41 + * This shouldn’t happen in modern browsers and Node.js. 42 + * 43 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} for the Fetch API spec. 44 + */ 18 45 export const makeFetchSource = ( 19 46 operation: Operation, 20 47 url: string,
+535 -39
packages/core/src/types.ts
··· 3 3 import { Client } from './client'; 4 4 import { CombinedError } from './utils/error'; 5 5 6 - // NOTE: This is mirrored from @graphql-typed-document-node/core and must match this type exactly. 7 - // It has been copied here to avoid tooling problems where build systems get confused about the type-only nature of this package. 8 - // See for original: https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10 6 + /** A GraphQL `DocumentNode` with attached generics for its result data and variables. 7 + * 8 + * @remarks 9 + * A GraphQL {@link DocumentNode} defines both the variables it accepts on request and the `data` 10 + * shape it delivers on a response in the GraphQL query language. 11 + * 12 + * To bridge the gap to TypeScript, tools may be used to generate TypeScript types that define the shape 13 + * of `data` and `variables` ahead of time. These types are then attached to GraphQL documents using this 14 + * `TypedDocumentNode` type. 15 + * 16 + * Using a `DocumentNode` that is typed like this will cause any `urql` API to type its input `variables` 17 + * and resulting `data` using the types provided. 18 + * 19 + * @privateRemarks 20 + * For compatibility reasons this type has been copied and internalized from: 21 + * https://github.com/dotansimha/graphql-typed-document-node/blob/3711b12/packages/core/src/index.ts#L3-L10 22 + * 23 + * @see {@link https://github.com/dotansimha/graphql-typed-document-node} for more information. 24 + */ 9 25 export interface TypedDocumentNode< 10 26 Result = { [key: string]: any }, 11 27 Variables = { [key: string]: any } 12 28 > extends DocumentNode { 13 - /** 14 - * This type is used to ensure that the variables you pass in to the query are assignable to Variables 15 - * and that the Result is assignable to whatever you pass your result to. The method is never actually 16 - * implemented, but the type is valid because we list it as optional 29 + /** Type to check whether `Variables` and `Result` are assignable types. 30 + * @internal 17 31 */ 18 32 __apiType?: (variables: Variables) => Result; 19 33 } 20 34 35 + /** A list of errors on {@link ExecutionResult | ExecutionResults}. 36 + * @see {@link https://spec.graphql.org/draft/#sec-Errors.Error-Result-Format} for the GraphQL Error Result format spec. 37 + */ 21 38 type ErrorLike = Partial<GraphQLError> | Error; 39 + /** Extensions which may be placed on {@link ExecutionResult | ExecutionResults}. 40 + * @see {@link https://spec.graphql.org/draft/#sel-EAPHJCAACCoGu9J} for the GraphQL Error Result format spec. 41 + */ 22 42 type Extensions = Record<string, any>; 23 43 44 + /** Incremental Payloads sent as part of "Incremental Delivery" patching prior result data. 45 + * 46 + * @remarks 47 + * "Incremental Delivery" works by allowing APIs to stream patches to the client, whih update 48 + * prior results at the specified `path`. 49 + * 50 + * @see {@link https://github.com/graphql/graphql-spec/blob/94363c9/spec/Section%207%20--%20Response.md#incremental} for the incremental payload spec 51 + */ 24 52 export interface IncrementalPayload { 53 + /** Optional label for the incremental payload that corresponds to directives' labels. 54 + * 55 + * @remarks 56 + * All incremental payloads are labelled by the label that `@stream` or `@defer` directives 57 + * specified, to identify which directive they originally belonged to. 58 + */ 25 59 label?: string | null; 60 + /** JSON patch at which to apply the `data` patch or append the `items`. 61 + * 62 + * @remarks 63 + * The `path` indicates the JSON path of a prior result’s `data` structure at which 64 + * to insert the patch’s data. 65 + * When `items` is set instead, which represents a list of items to insert, the last 66 + * entry of the `path` will be an index number at which to start setting the range of 67 + * items. 68 + */ 26 69 path: readonly (string | number)[]; 70 + /** Data to patch into the result data at the given `path`. 71 + * 72 + * @remarks 73 + * This `data`, when set, is merged into the object at the given `path` of the last 74 + * result that has been delivered. 75 + * This isn't set when `items` is set. 76 + */ 27 77 data?: Record<string, unknown> | null; 78 + /** List of items to patch into the result data at the given `path`. 79 + * 80 + * @remarks 81 + * The `items`, when provided, is set onto a range in an array, at the given JSON 82 + * `path`. The start index is the last entry of the `path` and the end index is 83 + * the length of the `items` list added to this index. 84 + * This isn't set when `data` is set. 85 + */ 28 86 items?: readonly unknown[] | null; 87 + /** Contains a list of errors raised by incremental payloads. 88 + * 89 + * @remarks 90 + * The list of `errors` on `incremental` payloads is merged into the list of prior 91 + * results’ errors. 92 + * 93 + * @see {@link https://spec.graphql.org/October2021/#sec-Errors} for the GraphQL Errors Response spec 94 + */ 29 95 errors?: ErrorLike[] | readonly ErrorLike[]; 96 + /** Additional metadata that a GraphQL API may choose to send that is out of spec. 97 + * @see {@link https://spec.graphql.org/October2021/#sel-EAPHJCAACCoGu9J} for the GraphQL Response spec 98 + */ 30 99 extensions?: Extensions; 31 100 } 32 101 33 102 export interface ExecutionResult { 103 + /** Incremental patches to be applied to a previous result as part of "Incremental Delivery". 104 + * 105 + * @remarks 106 + * When this is set `data` and `errors` is typically not set on the result. Instead, the incremental payloads 107 + * are applied as patches to a prior result's `data`. 108 + * 109 + * @see {@link https://github.com/graphql/graphql-spec/blob/94363c9/spec/Section%207%20--%20Response.md#incremental} for the incremental payload spec 110 + */ 34 111 incremental?: IncrementalPayload[]; 112 + /** The result of the execution of the GraphQL operation. 113 + * @see {@link https://spec.graphql.org/October2021/#sec-Data} for the GraphQL Data Response spec 114 + */ 35 115 data?: null | Record<string, any>; 116 + /** Contains a list of errors raised by fields or the request itself. 117 + * @see {@link https://spec.graphql.org/October2021/#sec-Errors} for the GraphQL Errors Response spec 118 + */ 36 119 errors?: ErrorLike[] | readonly ErrorLike[]; 120 + /** Additional metadata that a GraphQL API may choose to send that is out of spec. 121 + * @see {@link https://spec.graphql.org/October2021/#sel-EAPHJCAACCoGu9J} for the GraphQL Response spec 122 + */ 37 123 extensions?: Extensions; 124 + /** Flag indicating whether a future, incremental response may update this response. 125 + * @see {@link https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#payload-format} for the DeferStream spec 126 + */ 38 127 hasNext?: boolean; 39 128 } 40 129 130 + /** A `Source` with a `PromisifiedSource.toPromise` helper method, to promisify a single result. 131 + * 132 + * @remarks 133 + * The {@link Client} will often return a `PromisifiedSource` to provide the `toPromise` method. When called, this returns 134 + * a promise of the source that resolves on the first {@link OperationResult} of the `Source` that doesn't have `stale: true` 135 + * nor `hasNext: true` set, meaning, it'll resolve to the first result that is stable and complete. 136 + */ 41 137 export type PromisifiedSource<T = any> = Source<T> & { 42 138 toPromise: () => Promise<T>; 43 139 }; 44 140 45 - /** The type of GraphQL operation being executed. */ 141 + /** A type of Operation, either a GraphQL `query`, `mutation`, or `subscription`; or a `teardown` signal. 142 + * 143 + * @remarks 144 + * Internally, {@link Operation | Operations} instruct the {@link Client} to perform a certain action on its exchanges. 145 + * Any of the three GraphQL operations tell it to execute these operations, and the `teardown` signal instructs it that 146 + * the operations are cancelled and/or have ended. 147 + * 148 + * The `teardown` signal is sent when nothing is subscribed to an operation anymore and no longer interested in its results 149 + * or any updates. 150 + */ 46 151 export type OperationType = 'subscription' | 'query' | 'mutation' | 'teardown'; 47 152 48 - /** The strategy that is used to request results from network and/or the cache. */ 153 + /** The request and caching strategy that is used by exchanges to retrive cached results. 154 + * 155 + * @remarks 156 + * The `RequestPolicy` is used by cache exchanges to decide how a query operation may be resolved with cached results.h 157 + * A cache exchange may behave differently depending on which policy is returned. 158 + * 159 + * - `cache-first` (the default) prefers cached results and falls back to sending an API request. 160 + * - `cache-and-network` returns cached results but also always sends an API request in the background. 161 + * - `network-only` will ignore any cached results and send an API request. 162 + * - `cache-only` will always return cached results and prevent API requests. 163 + */ 49 164 export type RequestPolicy = 50 165 | 'cache-first' 51 - | 'cache-only' 166 + | 'cache-and-network' 52 167 | 'network-only' 53 - | 'cache-and-network'; 168 + | 'cache-only'; 54 169 55 - /** How the operation has */ 170 + /** A metadata flag set by cache exchanges to indicate whether a cache miss, a cache hit, or a partial cache hit has occurred. 171 + * 172 + * @remarks 173 + * A cache exchange may update {@link OperationDebugMeta.cacheOutcome} on {@link OperationContext.meta} to indicate whether 174 + * an operation has been resolved from the cache. 175 + * 176 + * A cache hit is considered a result that has fully come from a cache. A partial result is a result that has come from a cache 177 + * but is incomplete, which may trigger another API request. A cache miss means a result must be requested from the API as no 178 + * cache result has been delivered. 179 + */ 56 180 export type CacheOutcome = 'miss' | 'partial' | 'hit'; 57 181 58 - /** A default type for variables */ 182 + /** A default type for variables. 183 + * 184 + * @remarks 185 + * While {@link TypedDocumentNode} can be used by generators to add TypeScript types for a GraphQL operation’s 186 + * variables and result, when this isn’t the case this type is used as a fallback for the `Variables` generic. 187 + */ 59 188 export type AnyVariables = { [prop: string]: any } | void | undefined; 60 189 61 - /** A Graphql query, mutation, or subscription. */ 190 + /** A GraphQL request representing a single execution in GraphQL. 191 + * 192 + * @remarks 193 + * A `GraphQLRequest` is a single executable request that may be used by a cache or a GraphQL API to deliver a result. 194 + * A request contains a `DocumentNode` for the query document of a GraphQL operation and the `variables` for the given 195 + * request. 196 + * 197 + * A unique `key` is generated to identify the request internally by `urql`. Two requests with the same query and 198 + * variables will share the same `key`. 199 + * 200 + * The `Data` and `Variables` generics may be provided by a {@link TypedDocumentNode}, adding TypeScript types for what 201 + * the result shape and variables shape are. 202 + * 203 + * @see {@link https://spec.graphql.org/October2021/#sec-Executing-Requests} for more information on GraphQL reuqests. 204 + */ 62 205 export interface GraphQLRequest< 63 206 Data = any, 64 207 Variables extends AnyVariables = AnyVariables 65 208 > { 66 - /** Unique identifier of the request. */ 209 + /** Unique identifier for the `GraphQLRequest`. 210 + * 211 + * @remarks 212 + * This is a key that combines the unique key of the `query` and the `variables` into a unique 213 + * `key` for the `GraphQLRequest`. Any request with the same query and variables will have a unique 214 + * `key` by which results and requests can be identified as identical. 215 + * 216 + * Internally, a stable, cached `key` is generated for the `DocumentNode` and for the `variables` and 217 + * both will be combined into a combined `key` which is set here, based on a DJB2 hash, 218 + * 219 + * The `variables` will change the key even if they contain a non-JSON reference. If you pass a custom 220 + * class instance to `variables` that doesn't contain a `toString` or `toJSON` method, a stable but random 221 + * identifier will replace this class to generate a key. 222 + */ 67 223 key: number; 224 + /** A GraphQL document to execute against a cache or API. 225 + * 226 + * @remarks 227 + * A `GraphQLRequest` is executed against an operation in a GraphQL document. 228 + * In `urql`, we expect a document to only contain a single operation that is executed rather than 229 + * multiple ones by convention. 230 + */ 68 231 query: DocumentNode | TypedDocumentNode<Data, Variables>; 232 + /** Variables used to execute the `query` document. 233 + * 234 + * @remarks 235 + * The `variables`, based either on the {@link AnyVariables} type or the {@link TypedDocumentNode}'s provided 236 + * generic, are sent to the GraphQL API to execute a request. 237 + */ 69 238 variables: Variables; 70 239 } 71 240 72 - /** Metadata that is only available in development for devtools. */ 241 + /** Metadata used to annotate an `Operation` in development for the `urql-devtools`. 242 + * 243 + * @remarks 244 + * The `OperationDebugMeta` is found on {@link OperationContext.meta} only in development, 245 + * and is used to send additional metadata to the `urql-devtools` about the {@link Operation}. 246 + * 247 + * In production, most of this metadata will be missing, and it must not be used outside 248 + * of development, and should only be used by the `urql-devtools`. 249 + */ 73 250 export interface OperationDebugMeta { 251 + /** A label for the source of the `Operation`. 252 + * 253 + * @remarks 254 + * The `source` string indicates a human readable originator for the `Operation`. 255 + * This may be set to a component name or function name to indicate what originally 256 + * triggered the `Operation`. 257 + */ 74 258 source?: string; 259 + /** A type of caching outcome set by cache exchanges on `OperationResult`s. 260 + * 261 + * @remarks 262 + * The `cacheOutcome` flag is set to a {@link CacheOutcome} on {@link Operation | Operations} 263 + * after they passed through the cache exchange. This flag indicates whether a cache hit, miss, 264 + * or partial cache hit has occurred. 265 + */ 75 266 cacheOutcome?: CacheOutcome; 267 + /** Reserved to indicate the time it took for a GraphQL request to receive a response from a GraphQL API. 268 + * 269 + * @remarks 270 + * The `networkLatency` may be set to the time it took (in ms) for a GraphQL API to respond to a request 271 + * and deliver a result. 272 + * @internal 273 + */ 76 274 networkLatency?: number; 275 + /** Reserved to indicate the timestamp at which a GraphQL request was sent to a GraphQL API. 276 + * 277 + * @remarks 278 + * The `startTime` is set to an epoch timestamp (in ms) at which a GraphQL request was started 279 + * and sent to a GraphQL API. 280 + * @internal 281 + */ 77 282 startTime?: number; 78 283 } 79 284 80 - /** A unique marker that marks the operation's identity when multiple mutation operations with identical keys are issued. */ 81 - export type OperationInstance = number & { readonly _opaque: unique symbol }; 285 + /** A unique identity for GraphQL mutations. 286 + * 287 + * @remarks 288 + * GraphQL mutations not only use {@link GraphQLRequest.key} to identify a result, but instead use 289 + * an identity to mark which result belongs to them. 290 + * 291 + * While two GraphQL queries and subscriptions sharing the same variables and the same operation 292 + * (i.e. `DocumentNode`) are considered identical, two mutations are not. 293 + * Two GraphQL queries or subscription results with the same {@link GraphQLRequest.key} can be used 294 + * to resolve any {@link GraphQLRequest} with this same `key`. 295 + * This is because identical queries and subscriptions are idempotent. 296 + * 297 + * However, two mutations with the same variables may receive different results from a GraphQL API, 298 + * since they may trigger side-effects. 299 + * This means that `urql` needs an additional identifier to differentiate between two mutations with 300 + * the same `DocumentNode`s and `variables`. 301 + */ 302 + export type OperationInstance = number & { 303 + /** Marker to indicate that an `OperationInstance` may not be created by a user. 304 + * 305 + * @remarks 306 + * The {@link Client} creates `OperationInstance` indentities automatically and uses them internally 307 + * to identify mutations results as belonging to mutation operations. These are just integers (numbers), 308 + * however, they're used as if they are objects (e.g. `{}`). However, since instances of arrays and 309 + * objects are not serialisable numbers are used instead. 310 + * 311 + * Because these are internal, the TypeScript type is marked using a `unique symbol` because they're 312 + * created opaquely and privately. 313 + * 314 + * @internal 315 + */ 316 + readonly _opaque: unique symbol; 317 + }; 82 318 83 - /** Additional metadata passed to [exchange]{@link Exchange} functions. */ 319 + /** Additional metadata for an `Operation` used to execute it. 320 + * 321 + * @remarks 322 + * The `OperationContext` is found on {@link Operation.context} and gives exchanges additional metadata 323 + * and options used to execute the operation. 324 + * 325 + * The context can often be changed on a per-operation basis, meaning, APIs on the {@link Client} or 326 + * bindings can pass a partial context that alters these options for a single operation. 327 + * 328 + * The `OperationContext` is populated mostly from the initial options passed to the `Client` at its 329 + * time of creation, but may also be modified by exchanges when an {@link Operation} is passed through 330 + * to the next exchange or when a result is returned. 331 + */ 84 332 export interface OperationContext { 85 - [key: string]: any; 333 + /** A unique identity for GraphQL mutations. 334 + * 335 + * @remarks 336 + * This is an internal property set by the `Client` to an identity of type {@link OperationInstance}. 337 + * An `OperationInstance` is an identifier that's used to tell two mutation operations with identical 338 + * `query` documents and `variables` apart from one another. 339 + * @internal 340 + */ 86 341 readonly _instance?: OperationInstance | undefined; 342 + /** Additional cache tags for `@urql/core`'s document `cacheExchange`. 343 + * 344 + * @remarks 345 + * The built-in {@link cacheExchange} in `@urql/core` is a document cache that uses `__typename`s in 346 + * mutation results to invalidate past, cached queries. 347 + * The `additionalTypenames` array may be set to the list of custom typenames whenever a result may 348 + * not deliver `__typename` properties, e.g. when an empty array may be sent. 349 + * 350 + * By providing a list of custom typenames you may "tag" a result as containing a certain type, which 351 + * helps the document cache associate mutations with queries when either don't actually contain a 352 + * `__typename` in the JSON result. 353 + */ 87 354 additionalTypenames?: string[]; 355 + /** The `fetch` function used to make API calls. 356 + * 357 + * @remarks 358 + * This is the fetch polyfill used by any fetch exchange to make an API request. By default, when this 359 + * option isn't set, any fetch exchange will attempt to use the globally available `fetch` function 360 + * to make a request instead. 361 + * 362 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} for the Fetch API spec. 363 + */ 88 364 fetch?: typeof fetch; 365 + /** The `url` passed to the `fetch` call on API requests. 366 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} for a description of the `fetch` calls. 367 + */ 368 + url: string; 369 + /** Additional options passed to the `fetch` call on API requests. 370 + * 371 + * @remarks 372 + * The options in this object or an object returned by a callback function will be merged into the 373 + * {@link RequestInit} options passed to the `fetch` call. 374 + * 375 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} for a description of this object. 376 + */ 89 377 fetchOptions?: RequestInit | (() => RequestInit); 378 + /** The request and caching strategy instructing cache exchanges how to treat cached results. 379 + * 380 + * @remarks 381 + * The {@link RequestPolicy} instructing cache exchanges how to use and treat their cached results. 382 + * By default `cache-first` is set and used, which will use cache results, and only make an API request 383 + * on a cache miss. 384 + */ 90 385 requestPolicy: RequestPolicy; 91 - url: string; 386 + /** Metadata that annotates an `Operation` in development for the `urql-devtools`. 387 + * 388 + * @remarks 389 + * This is metadata that is used by the `urql-devtools` to get more information about `Operation`s and 390 + * `OperationResult`s and is filled in by exchanges across the codebase in development. 391 + * 392 + * This data is not for production use and hence shouldn't be used or relied upon directly. 393 + */ 92 394 meta?: OperationDebugMeta; 93 - suspense?: boolean; 94 395 /** Instructs fetch exchanges to use a GET request. 95 - * When true or 'within-url-limit' is passed, GET won't be used when the resulting URL exceeds a length of 2048. */ 396 + * 397 + * @remarks 398 + * By default, GraphQL over HTTP requests are always sent as POST requests with a JSON body. 399 + * However, sometimes it's preferable to send a GET request instead, for instance, for caching with or 400 + * without persisted queries. 401 + * 402 + * When set to `true`, the `preferGetMethod` instructs fetch exchanges to instead send a GET request 403 + * for query operations. 404 + * 405 + * Additionally, `urql`'s built-in fetch exchanges will default to `'within-url-limit'` and not send a GET request 406 + * when the resulting URL would be 2,048 characters or longer. This can be forced and circumvented by setting 407 + * this option to `'force'`. 408 + */ 96 409 preferGetMethod?: boolean | 'force' | 'within-url-limit'; 410 + /** A configuration flag indicating whether this operation may trigger "Suspense". 411 + * 412 + * @remarks 413 + * This configuration flag is reserved for `urql` (`react-urql`) and `@urql/preact` to activate or 414 + * deactivate support for Suspense, and is ignored in other bindings. 415 + * When activated here and on {@link `Client.suspense`} it allows the bindings to "suspend" instead 416 + * of returning a loading state, which will stop updates in a querying component and instead cascade 417 + * to a higher suspense boundary for a loading state. 418 + * 419 + * @see {@link https://beta.reactjs.org/blog/2022/03/29/react-v18#new-suspense-features} for more information on React Suspense. 420 + */ 421 + suspense?: boolean; 422 + [key: string]: any; 97 423 } 98 424 99 - /** A [query]{@link Query} or [mutation]{@link Mutation} with additional metadata for use during transmission. */ 425 + /** The inputs to `urql`'s Exchange pipeline to instruct them to execute a GraphQL operation. 426 + * 427 + * @remarks 428 + * An `Operation`, in `urql`, starts a {@link GraphQLRequest} and are events. The `kind` of an `Operation` can 429 + * be set to any operation kind of GraphQL, namely `query`, `mutation`, or `subscription`. To terminate an 430 + * operation, once it's cancelled, a `teardown` kind event is sent. 431 + * 432 + * The {@link ExchangeIO} type describes how {@link Exchange | Exchanges} receive `Operation`s and return 433 + * `OperationResults`, using `teardown` `Operation`s to cancel ongoing operations. 434 + * 435 + * @see {@link https://formidable.com/open-source/urql/docs/architecture/#the-client-and-exchanges} for more information 436 + * on the flow of Exchanges. 437 + */ 100 438 export interface Operation< 101 439 Data = any, 102 440 Variables extends AnyVariables = AnyVariables 103 441 > extends GraphQLRequest<Data, Variables> { 442 + /** The `OperationType` describing the kind of `Operation`. 443 + * 444 + * @remarks 445 + * This is used to describe what to do with the {@link GraphQLRequest} of an {@link Operation} and is set 446 + * to a GraphQL operation type (`query`, `mutation`, or `subscription`) to start an `Operation`; and to 447 + * `teardown` to cancel an operation, which either terminates it early or lets exchanges know that no 448 + * consumer is interested in this operation any longer. 449 + */ 104 450 readonly kind: OperationType; 451 + /** Holds additional metadata for an `Operation` used to execute it. 452 + * 453 + * @remarks 454 + * The {@link OperationContext} is created by the {@link Client} but may also be modified by 455 + * {@link Exchange | Exchanges} and is used as metadata by them. 456 + */ 105 457 context: OperationContext; 106 458 } 107 459 108 - /** Resulting data from an [operation]{@link Operation}. */ 460 + /** A result for an `Operation` carrying a full GraphQL response. 461 + * 462 + * @remarks 463 + * An `OperationResult` is the result of an {@link Operation} and carry a description of the full response 464 + * on them. The {@link OperationResult.operation} is set to the `Operation` that this result fulfils. 465 + * 466 + * Unlike {@link ExecutionResult}, an `OperationResult` will never be an incremental result and will 467 + * always match the fully merged type of a GraphQL request. It essentially is a postprocessed version 468 + * of a GraphQL API response. 469 + */ 109 470 export interface OperationResult< 110 471 Data = any, 111 472 Variables extends AnyVariables = AnyVariables 112 473 > { 113 474 /** The [operation]{@link Operation} which has been executed. */ 475 + /** The `Operation` which this `OperationResult` is for. 476 + * 477 + * @remarks 478 + * The `operation` property is set to the {@link Operation} that this result is. At the time the 479 + * {@link OperationResult} is constructed (either from the cache or an API response) the original 480 + * `Operation` that the exchange delivering this result has received will be added. 481 + * 482 + * The {@link Client} uses this to identify which {@link Operation} this {@link OperationResult} is 483 + * for and to filter and deliver this result to the right place and consumers. 484 + */ 114 485 operation: Operation<Data, Variables>; 115 - /** The data returned from the Graphql server. */ 486 + /** The result of the execution of the GraphQL operation. 487 + * @see {@link https://spec.graphql.org/October2021/#sec-Data} for the GraphQL Data Response spec 488 + */ 116 489 data?: Data; 117 - /** Any errors resulting from the operation. */ 490 + /** Contains a description of errors raised by GraphQL fields or the request itself by the API. 491 + * 492 + * @remarks 493 + * The `error` of an `OperationResult` is set to a {@link CombinedError} if the GraphQL API response 494 + * contained any GraphQL errors. 495 + * 496 + * GraphQL errors occur when either a GraphQL request was prevented from executing entirely 497 + * (at which point `data: undefined` is set) or when one or more fields of a GraphQL request 498 + * failed to execute. Due to the latter, you may receive partial data when a GraphQL request 499 + * partially failed. 500 + */ 118 501 error?: CombinedError; 119 - /** Optional extensions return by the Graphql server. */ 502 + /** Additional metadata that a GraphQL API may choose to send that is out of spec. 503 + * @see {@link https://spec.graphql.org/October2021/#sel-EAPHJCAACCoGu9J} for the GraphQL Response spec 504 + */ 120 505 extensions?: Record<string, any>; 121 - /** Optional stale flag added by exchanges that return stale results. */ 506 + /** Indicates that an `OperationResult` is not fresh and a new result will follow. 507 + * 508 + * @remarks 509 + * The `stale` flag indicates whether a result is expected to be superseded by a new result soon. 510 + * This flag is set whenever a new result is being awaited and will be deliverd as soon as the API responds. 511 + * 512 + * It may be set by the {@link Client} when the `Operation` was already active, at which point 513 + * the {@link Client} asks the {@link Exchange | Exchanges} to request a new API response, or 514 + * by cache exchanges when a temporary, incomplete, or initial cache result has been deliverd, and 515 + * a new API request has been started in the background. (For partial cache results) 516 + * 517 + * Most commonly, this flag is set for a cached result when the operation is executed using the 518 + * `cache-and-network` {@link RequestPolicy}. 519 + */ 122 520 stale?: boolean; 123 - /** Optional hasNext flag indicating deferred/streamed results are following. */ 521 + /** Indicates that the GraphQL response is streamed and updated results will follow. 522 + * 523 + * @remarks 524 + * Due to incremental delivery, an API may deliver multiple {@link ExecutionResult | ExecutionResults} for a 525 + * single GraphQL request. This can happen for `@defer`, `@stream`, or `@live` queries, which allow an API 526 + * to update an initial GraphQL response over time, like a subscription. 527 + * 528 + * For GraphQL subscriptions, this flag will always be set to `true`. 529 + */ 124 530 hasNext?: boolean; 125 531 } 126 532 127 - /** Input parameters for to an Exchange factory function. */ 533 + /** The input parameters a `Client` passes to an `Exchange` when it's created. 534 + * 535 + * @remarks 536 + * When instantiated, a {@link Client} passes these inputs parameters to an {@link Exchange}. 537 + * 538 + * This input contains the `Client` itself, a `dispatchDebug` function for the `urql-devtools`, and 539 + * `forward`, which is set to the next exchange's {@link ExchangeIO} function in the exchange pipeline. 540 + */ 128 541 export interface ExchangeInput { 542 + /** The `Client` that is using this `Exchange`. 543 + * 544 + * @remarks 545 + * The {@link Client} instantiating the {@link Exchange} will call it with the `ExchangeInput` object, 546 + * while setting `client` to itself. 547 + * 548 + * Exchanges use methods like {@link Client.reexecuteOperation} to issue {@link Operation | Operations} 549 + * themselves, and communicate with the exchange pipeline as a whole. 550 + */ 129 551 client: Client; 552 + /** The next `Exchange`'s {@link ExchangeIO} function in the pipeline. 553 + * 554 + * @remarks 555 + * `Exchange`s are like middleware function, and are henced composed like a recursive pipeline. 556 + * Each `Exchange` will receive the next `Exchange`'s {@link ExchangeIO} function which they 557 + * then call to compose each other. 558 + * 559 + * Since each `Exchange` calls the next, this creates a pipeline where operations are forwarded 560 + * in sequence and `OperationResult`s from the next `Exchange` are combined with the current. 561 + */ 130 562 forward: ExchangeIO; 131 - dispatchDebug: <T extends keyof DebugEventTypes | string>( 563 + /** Issues a debug event to the `urql-devtools`. 564 + * 565 + * @remarks 566 + * If `@urql/devtools` are set up, this dispatch function issues events to the `urql-devtools`. 567 + * These events give the devtools more granular insights on what's going on in exchanges asynchronously, 568 + * since `Operation`s and `OperationResult`s only signify the “start” and “end” of a request. 569 + */ 570 + dispatchDebug<T extends keyof DebugEventTypes | string>( 132 571 t: DebugEventArg<T> 133 - ) => void; 572 + ): void; 134 573 } 135 574 136 - /** Function responsible for listening for streamed [operations]{@link Operation}. */ 575 + /** `Exchange`s are both extensions for a `Client` and part of the control-flow executing `Operation`s. 576 + * 577 + * @remarks 578 + * `Exchange`s are responsible for the pipeline in `urql` that accepts {@link Operation | Operations} and 579 + * returns {@link OperationResult | OperationResults}. They take care of adding functionality to a {@link Client}, 580 + * like deduplication, caching, and fetching (i.e. making GraphQL requests). 581 + * 582 + * When passed to the `Client`, they're instantiated with the {@link ExchangeInput} object and return an {@link ExchangeIO} 583 + * function, which is a mapping function that receives a stream of `Operation`s and returns a stream of `OperationResult`s. 584 + * 585 + * Like middleware, exchanges are composed, calling each other in a pipeline-like fashion, which is facilitated by exchanges 586 + * calling {@link ExchangeInput.forward}, which is set to the next exchange's {@link ExchangeIO} function in the pipeline. 587 + * 588 + * @see {@link https://formidable.com/open-source/urql/docs/architecture/#the-client-and-exchanges} for more information on Exchanges. 589 + * @see {@link https://formidable.com/open-source/urql/docs/advanced/authoring-exchanges/} on how Exchanges are authored. 590 + */ 137 591 export type Exchange = (input: ExchangeInput) => ExchangeIO; 138 592 139 - /** Function responsible for receiving an observable [operation]{@link Operation} and returning a [result]{@link OperationResult}. */ 593 + /** Returned by `Exchange`s, the `ExchangeIO` function are the composed pipeline functions. 594 + * 595 + * @remarks 596 + * An {@link Exchange} must return an `ExchangeIO` function, which accepts a stream of {@link Operation | Operations} which 597 + * this exchange handles and returns a stream of {@link OperationResult | OperationResults}. These streams are Wonka {@link Source | Sources}. 598 + * 599 + * An exchange may enhance the incoming stream of `Operation`s to add, filter, map (change), or remove `Operation`s, before 600 + * forwarding those to {@link ExchangeInput.forward}, using Wonka's operators, and may add or remove `OperationResult`s from 601 + * the returned stream. 602 + * 603 + * Generally, the stream of `OperationResult` returned by {@link ExchangeInput.forward} is always merged and combined with 604 + * the `Exchange`'s own stream of results if the `Exchange` creates and delivers results of its own. 605 + * 606 + * @see {@link https://formidable.com/open-source/urql/docs/advanced/authoring-exchanges/} on how Exchanges are authored. 607 + */ 140 608 export type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>; 141 609 142 - /** Debug event types (interfaced for declaration merging). */ 610 + /** A mapping type of debug event types to their payloads. 611 + * 612 + * @remarks 613 + * These are the debug events that {@link ExchangeInput.dispatchDebug} accepts mapped to the payloads these events carry. 614 + * Debug events are only used in development and only consumed by the `urql-devtools`. 615 + */ 143 616 export interface DebugEventTypes { 144 - // Cache exchange 617 + /** Signals to the devtools that a cache exchange will deliver a cached result. */ 145 618 cacheHit: { value: any }; 619 + /** Signals to the devtools that a cache exchange will invalidate a cached result. */ 146 620 cacheInvalidation: { 147 621 typenames: string[]; 148 622 response: OperationResult; 149 623 }; 150 - // Fetch exchange 624 + /** Signals to the devtools that a fetch exchange will make a GraphQL API request. */ 151 625 fetchRequest: { 152 626 url: string; 153 627 fetchOptions: RequestInit; 154 628 }; 629 + /** Signals to the devtools that a fetch exchange has received a GraphQL API response successfully. */ 155 630 fetchSuccess: { 156 631 url: string; 157 632 fetchOptions: RequestInit; 158 633 value: object; 159 634 }; 635 + /** Signals to the devtools that a fetch exchange has failed to execute a GraphQL API request. */ 160 636 fetchError: { 161 637 url: string; 162 638 fetchOptions: RequestInit; 163 639 value: Error; 164 640 }; 165 - // Retry exchange 641 + /** Signals to the devtools that a retry exchange will retry an Operation. */ 166 642 retryRetrying: { 167 643 retryCount: number; 168 644 }; 169 645 } 170 646 647 + /** Utility type that maps a debug event type to its payload. 648 + * 649 + * @remarks 650 + * This is a utility type that determines the required payload for a given debug event, which is 651 + * sent to the `urql-devtools`. 652 + * 653 + * The payloads for known debug events are defined by the {@link DebugEventTypes} type, and 654 + * each event additionally carries a human readable `message` with it, and the {@link Operation} 655 + * for which the event is. 656 + * 657 + * @internal 658 + */ 171 659 export type DebugEventArg<T extends keyof DebugEventTypes | string> = { 172 660 type: T; 173 661 message: string; ··· 176 664 ? { data: DebugEventTypes[T] } 177 665 : { data?: any }); 178 666 667 + /** Utility type of the full payload that is sent to the `urql-devtools`. 668 + * 669 + * @remarks 670 + * While the {@link DebugEventArg} defines the payload that {@link ExchangeInput.dispatchDebug} 671 + * accepts, each debug event then receives additional properties which are sent to the `urql-devtools`, 672 + * which this type defines. 673 + * @internal 674 + */ 179 675 export type DebugEvent< 180 676 T extends keyof DebugEventTypes | string = string 181 677 > = DebugEventArg<T> & {
+47 -1
packages/core/src/utils/error.ts
··· 33 33 } 34 34 }; 35 35 36 - /** An error which can consist of GraphQL errors and Network errors. */ 36 + /** An abstracted `Error` that provides either a `networkError` or `graphQLErrors`. 37 + * 38 + * @remarks 39 + * During a GraphQL request, either the request can fail entirely, causing a network error, 40 + * or the GraphQL execution or fields can fail, which will cause an {@link ExecutionResult} 41 + * to contain an array of GraphQL errors. 42 + * 43 + * The `CombinedError` abstracts and normalizes both failure cases. When {@link OperationResult.error} 44 + * is set to this error, the `CombinedError` abstracts all errors, making it easier to handle only 45 + * a subset of error cases. 46 + * 47 + * @see {@link https://formidable.com/open-source/urql/docs/basics/errors/} for more information on handling 48 + * GraphQL errors and the `CombinedError`. 49 + */ 37 50 export class CombinedError extends Error { 38 51 public name: string; 39 52 public message: string; 53 + 54 + /** A list of GraphQL errors rehydrated from a {@link ExecutionResult}. 55 + * 56 + * @remarks 57 + * If an {@link ExecutionResult} received from the API contains a list of errors, 58 + * the `CombinedError` will rehydrate them, normalize them to 59 + * {@link GraphQLError | GraphQLErrors} and list them here. 60 + * An empty list indicates that no GraphQL error has been sent by the API. 61 + */ 40 62 public graphQLErrors: GraphQLError[]; 63 + 64 + /** Set to an error, if a GraphQL request has failed outright. 65 + * 66 + * @remarks 67 + * A GraphQL over HTTP request may fail and not reach the API. Any error that 68 + * prevents a GraphQl request outright, will be considered a “network error” and 69 + * set here. 70 + */ 41 71 public networkError?: Error; 72 + 73 + /** Set to the {@link Response} object a fetch exchange received. 74 + * 75 + * @remarks 76 + * If a built-in fetch {@link Exchange} is used in `urql`, this may 77 + * be set to the {@link Response} object of the Fetch API response. 78 + * However, since `urql` doesn’t assume that all users will use HTTP 79 + * as the only or exclusive transport for GraphQL this property is 80 + * neither typed nor guaranteed and may be re-used for other purposes 81 + * by non-fetch exchanges. 82 + * 83 + * Hint: It can be useful to use `response.status` here, however, if 84 + * you plan on relying on this being a {@link Response} in your app, 85 + * which it is by default, then make sure you add some extra checks 86 + * before blindly assuming so! 87 + */ 42 88 public response?: any; 43 89 44 90 constructor(input: {
+30 -4
packages/core/src/utils/hash.ts
··· 1 - export type HashValue = number & { readonly _opaque: unique symbol }; 1 + /** A hash value as computed by {@link phash}. 2 + * 3 + * @remarks 4 + * Typically `HashValue`s are used as hashes and keys of GraphQL documents, 5 + * variables, and combined, for GraphQL requests. 6 + */ 7 + export type HashValue = number & { 8 + /** Marker to indicate that a `HashValue` may not be created by a user. 9 + * 10 + * @remarks 11 + * `HashValue`s are created by {@link phash} and are marked as such to not mix them 12 + * up with other numbers and prevent them from being created or used outside of this 13 + * hashing function. 14 + * 15 + * @internal 16 + */ 17 + readonly _opaque: unique symbol; 18 + }; 2 19 3 - // When we have separate strings it's useful to run a progressive 4 - // version of djb2 where we pretend that we're still looping over 5 - // the same string 20 + /** Computes a djb2 hash of the given string. 21 + * 22 + * @param x - the string to be hashed 23 + * @param seed - optionally a prior hash for progressive hashing 24 + * @returns a hash value, i.e. a number 25 + * 26 + * @remark 27 + * This is the hashing function used throughout `urql`, primarily to compute 28 + * {@link Operation.key}. 29 + * 30 + * @see {@link http://www.cse.yorku.ca/~oz/hash.html#djb2} for a further description of djb2. 31 + */ 6 32 export const phash = (x: string, seed?: HashValue): HashValue => { 7 33 let h = typeof seed === 'number' ? seed | 0 : 5381; 8 34 for (let i = 0, l = x.length | 0; i < l; i++)
+12
packages/core/src/utils/maskTypename.ts
··· 1 + /** Used to recursively mark `__typename` fields in data as non-enumerable. 2 + * 3 + * @remarks 4 + * This utility can be used to recursively copy GraphQl response data and hide 5 + * all `__typename` fields present on it. 6 + * 7 + * Hint: It’s not recommended to do this, unless it's absolutely necessary as 8 + * cloning and modifying all data of a response can be unnecessarily slow, when 9 + * a manual and more specific copy/mask is more efficient. 10 + * 11 + * @see {@link ClientOptions.maskTypename} for a description of how the `Client` uses this utility. 12 + */ 1 13 export const maskTypename = (data: any, isRoot?: boolean): any => { 2 14 if (!data || typeof data !== 'object') { 3 15 return data;
+26 -1
packages/core/src/utils/operation.ts
··· 6 6 OperationType, 7 7 } from '../types'; 8 8 9 + /** Creates a {@link Operation} from the given parameters. 10 + * 11 + * @param kind - The {@link OperationType} of GraphQL operation, i.e. `query`, `mutation`, or `subscription`. 12 + * @param request - The {@link GraphQLRequest} used as a template for the `Operation`. 13 + * @param context - The {@link OperationContext} `context` data for the `Operation`. 14 + * @returns An {@link Operation}. 15 + * 16 + * @remarks 17 + * This method is both used to create new {@link Operation | Operations} as well as copy and modify existing 18 + * operations. While it’s not required to use this function to copy an `Operation`, it is recommended, in case 19 + * additional dynamic logic is added to them in the future. 20 + * 21 + * @example 22 + * An example of copying an existing `Operation` to modify its `context`: 23 + * 24 + * ```ts 25 + * makeOperation( 26 + * operation.kind, 27 + * operation, 28 + * { ...operation.context, requestPolicy: 'cache-first' }, 29 + * ); 30 + * ``` 31 + */ 9 32 function makeOperation< 10 33 Data = any, 11 34 Variables extends AnyVariables = AnyVariables ··· 38 61 39 62 export { makeOperation }; 40 63 41 - /** Spreads the provided metadata to the source operation's meta property in context. */ 64 + /** Adds additional metadata to an `Operation`'s `context.meta` property while copying it. 65 + * @see {@link OperationDebugMeta} for more information on the {@link OperationContext.meta} property. 66 + */ 42 67 export const addMetadata = ( 43 68 operation: Operation, 44 69 meta: OperationContext['meta']
+62 -4
packages/core/src/utils/request.ts
··· 15 15 loc: Location | undefined; 16 16 } 17 17 18 + /** A `DocumentNode` annotated with its hashed key. 19 + * @internal 20 + */ 18 21 export interface KeyedDocumentNode extends DocumentNode { 19 22 __key: HashValue; 20 23 } ··· 26 29 const replaceOutsideStrings = (str: string, idx: number) => 27 30 idx % 2 === 0 ? str.replace(REPLACE_CHAR_RE, '\n') : str; 28 31 32 + /** Sanitizes a GraphQL document string by replacing comments and redundant newlines in it. */ 29 33 const sanitizeDocument = (node: string): string => 30 34 node.split(GRAPHQL_STRING_RE).map(replaceOutsideStrings).join('').trim(); 31 35 32 36 const prints = new Map<DocumentNode | DefinitionNode, string>(); 33 37 const docs = new Map<HashValue, KeyedDocumentNode>(); 34 38 39 + /** A cached printing function for GraphQL documents. 40 + * 41 + * @param node - A string of a document or a {@link DocumentNode} 42 + * @returns A normalized printed string of the passed GraphQL document. 43 + * 44 + * @remarks 45 + * This function accepts a GraphQL query string or {@link DocumentNode}, 46 + * then prints and sanitizes it. The sanitizer takes care of removing 47 + * comments, which otherwise alter the key of the document although the 48 + * document is otherwise equivalent to another. 49 + * 50 + * When a {@link DocumentNode} is passed to this function, it caches its 51 + * output by modifying the `loc.source.body` property on the GraphQL node. 52 + */ 35 53 export const stringifyDocument = ( 36 54 node: string | DefinitionNode | DocumentNode 37 55 ): string => { ··· 60 78 return printed; 61 79 }; 62 80 81 + /** Computes the hash for a document's string using {@link stringifyDocument}'s output. 82 + * 83 + * @param node - A string of a document or a {@link DocumentNode} 84 + * @returns A {@link HashValue} 85 + * 86 + * @privateRemarks 87 + * This function adds the operation name of the document to the hash, since sometimes 88 + * a merged document with multiple operations may be used. Although `urql` requires a 89 + * `DocumentNode` to only contain a single operation, when the cached `loc.source.body` 90 + * of a `DocumentNode` is used, this string may still contain multiple operations and 91 + * the resulting hash should account for only one at a time. 92 + */ 63 93 const hashDocument = ( 64 94 node: string | DefinitionNode | DocumentNode 65 95 ): HashValue => { ··· 72 102 return key; 73 103 }; 74 104 105 + /** Returns a canonical version of the passed `DocumentNode` with an added hash key. 106 + * 107 + * @param node - A string of a document or a {@link DocumentNode} 108 + * @returns A {@link KeyedDocumentNode} 109 + * 110 + * @remarks 111 + * `urql` will always avoid unnecessary work, no matter whether a user passes `DocumentNode`s 112 + * or strings of GraphQL documents to its APIs. 113 + * 114 + * This function will return a canonical version of a {@link KeyedDocumentNode} no matter 115 + * which kind of input is passed, avoiding parsing or hashing of passed data as needed. 116 + */ 75 117 export const keyDocument = (node: string | DocumentNode): KeyedDocumentNode => { 76 118 let key: HashValue; 77 119 let query: DocumentNode; ··· 91 133 return query as KeyedDocumentNode; 92 134 }; 93 135 136 + /** Creates a `GraphQLRequest` from the passed parameters. 137 + * 138 + * @param q - A string of a document or a {@link DocumentNode} 139 + * @param variables - A variables object for the defined GraphQL operation. 140 + * @returns A {@link GraphQLRequest} 141 + * 142 + * @remarks 143 + * `createRequest` creates a {@link GraphQLRequest} from the passed parameters, 144 + * while replacing the document as needed with a canonical version of itself, 145 + * to avoid parsing, printing, or hashing the same input multiple times. 146 + * 147 + * If no variables are passed, canonically it'll default to an empty object, 148 + * which is removed from the resulting hash key. 149 + */ 94 150 export const createRequest = < 95 151 Data = any, 96 152 Variables extends AnyVariables = AnyVariables ··· 106 162 return { key, query, variables }; 107 163 }; 108 164 109 - /** 110 - * Finds the Name value from the OperationDefinition of a Document 165 + /** Returns the name of the `DocumentNode`'s operation, if any. 166 + * @param query - A {@link DocumentNode} 167 + * @returns the operation's name contained within the document, or `undefined` 111 168 */ 112 169 export const getOperationName = (query: DocumentNode): string | undefined => { 113 170 for (const node of query.definitions) { ··· 117 174 } 118 175 }; 119 176 120 - /** 121 - * Finds the operation-type 177 + /** Returns the type of the `DocumentNode`'s operation, if any. 178 + * @param query - A {@link DocumentNode} 179 + * @returns the operation's type contained within the document, or `undefined` 122 180 */ 123 181 export const getOperationType = (query: DocumentNode): string | undefined => { 124 182 for (const node of query.definitions) {
+48
packages/core/src/utils/result.ts
··· 6 6 } from '../types'; 7 7 import { CombinedError } from './error'; 8 8 9 + /** Converts the `ExecutionResult` received for a given `Operation` to an `OperationResult`. 10 + * 11 + * @param operation - The {@link Operation} for which the API’s result is for. 12 + * @param result - The GraphQL API’s {@link ExecutionResult}. 13 + * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). 14 + * @returns An {@link OperationResult}. 15 + * 16 + * @remarks 17 + * This utility can be used to create {@link OperationResult | OperationResults} in the shape 18 + * that `urql` expects and defines, and should be used rather than creating the results manually. 19 + * 20 + * @throws 21 + * If no data, or errors are contained within the result, or the result is instead an incremental 22 + * response containing a `path` property, a “No Content” error is thrown. 23 + * 24 + * @see {@link ExecutionResult} for the type definition of GraphQL API results. 25 + */ 9 26 export const makeResult = ( 10 27 operation: Operation, 11 28 result: ExecutionResult, ··· 34 51 }; 35 52 }; 36 53 54 + /** Merges an incrementally delivered `ExecutionResult` into a previous `OperationResult`. 55 + * 56 + * @param prevResult - The {@link OperationResult} that preceded this result. 57 + * @param path - The GraphQL API’s {@link ExecutionResult} that should be patching the `prevResult`. 58 + * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). 59 + * @returns A new {@link OperationResult} patched with the incremental result. 60 + * 61 + * @remarks 62 + * This utility should be used to merge subsequent {@link ExecutionResult | ExecutionResults} of 63 + * incremental responses into a prior {@link OperationResult}. 64 + * 65 + * When directives like `@defer`, `@stream`, and `@live` are used, GraphQL may deliver new 66 + * results that modify previous results. In these cases, it'll set a `path` property to modify 67 + * the result it sent last. This utility is built to handle these cases and merge these payloads 68 + * into existing {@link OperationResult | OperationResults}. 69 + * 70 + * @see {@link ExecutionResult} for the type definition of GraphQL API results. 71 + */ 37 72 export const mergeResultPatch = ( 38 73 prevResult: OperationResult, 39 74 nextResult: ExecutionResult, ··· 102 137 }; 103 138 }; 104 139 140 + /** Creates an `OperationResult` containing a network error for requests that encountered unexpected errors. 141 + * 142 + * @param operation - The {@link Operation} for which the API’s result is for. 143 + * @param error - The network-like error that prevented an API result from being delivered. 144 + * @param response - Optionally, a raw object representing the API’s result (Typically a {@link Response}). 145 + * @returns An {@link OperationResult} containing only a {@link CombinedError}. 146 + * 147 + * @remarks 148 + * This utility can be used to create {@link OperationResult | OperationResults} in the shape 149 + * that `urql` expects and defines, and should be used rather than creating the results manually. 150 + * This function should be used for when the {@link CombinedError.networkError} property is 151 + * populated and no GraphQL execution actually occurred. 152 + */ 105 153 export const makeErrorResult = ( 106 154 operation: Operation, 107 155 error: Error,
+5
packages/core/src/utils/streamUtils.ts
··· 1 1 import { Source, subscribe, pipe } from 'wonka'; 2 2 import { OperationResult, PromisifiedSource } from '../types'; 3 3 4 + /** Patches a `toPromise` method onto the `Source` passed to it. 5 + * @param source$ - the Wonka {@link Source} to patch. 6 + * @returns The passed `source$` with a patched `toPromise` method as a {@link PromisifiedSource}. 7 + * @internal 8 + */ 4 9 export function withPromise<T extends OperationResult>( 5 10 source$: Source<T> 6 11 ): PromisifiedSource<T> {
+14
packages/core/src/utils/stringifyVariables.ts
··· 42 42 return out; 43 43 }; 44 44 45 + /** A stable stringifier for GraphQL variables objects. 46 + * 47 + * @param x - any JSON-like data. 48 + * @return A JSON string. 49 + * 50 + * @remarks 51 + * This utility creates a stable JSON string from any passed data, 52 + * and protects itself from throwing. 53 + * 54 + * The JSON string is stable insofar as objects’ keys are sorted, 55 + * and instances of non-plain objects are replaced with random keys 56 + * replacing their values, which remain stable for the objects’ 57 + * instance. 58 + */ 45 59 export const stringifyVariables = (x: any): string => { 46 60 seen.clear(); 47 61 return stringify(x);
+24
packages/core/src/utils/typenames.ts
··· 29 29 return types; 30 30 }; 31 31 32 + /** Finds and returns a list of `__typename` fields found in response data. 33 + * 34 + * @privateRemarks 35 + * This is used by `@urql/core`’s document `cacheExchange` to find typenames 36 + * in a given GraphQL response’s data. 37 + */ 32 38 export const collectTypesFromResponse = (response: object): string[] => [ 33 39 ...collectTypes(response as EntityLike, new Set()), 34 40 ]; ··· 63 69 64 70 const formattedDocs = new Map<number, KeyedDocumentNode>(); 65 71 72 + /** Adds `__typename` fields to a GraphQL `DocumentNode`. 73 + * 74 + * @param node - a {@link DocumentNode}. 75 + * @returns a copy of the passed {@link DocumentNode} with added `__typename` introspection fields. 76 + * 77 + * @remarks 78 + * Cache {@link Exchange | Exchanges} will require typename introspection to 79 + * recognize types in a GraphQL response. To retrieve these typenames, 80 + * this function is used to add the `__typename` fields to non-root 81 + * selection sets of a GraphQL document. 82 + * 83 + * This utility also preserves the internally computed key of the 84 + * document as created by {@link createRequest} to avoid any 85 + * formatting from being duplicated. 86 + * 87 + * @see {@link https://spec.graphql.org/October2021/#sec-Type-Name-Introspection} for more information 88 + * on typename introspection via the `__typename` field. 89 + */ 66 90 export const formatDocument = <T extends DocumentNode>(node: T): T => { 67 91 const query = keyDocument(node); 68 92