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

Part 2: New Documentation (#556)

* Fix index page missing from sidebar

* Hide legend for empty headings

* Fix legend item spacing

* add short chapter about the way we send errors back

* Rename a couple of routes and files to match titles

* add note about graphql error

* Edit "Getting Started" guide (1st pass)

* Adjust after meeting with Joe

* correct some typo's

* add missing punctuation

* change mutation wording

* fix lint error

* Improve line breaks and legend spacing

* Edit "Queries"

* rewrite computed-queries

* rewrite custom-updaes

* Edit "Mutations"

* add reading-on sections

* edit schema-awareness

* Add reference to request policies to "Document Caching" doc

* core-package.md update

* Edit "Errors"

* Edit "Basics" and "Concepts" index pages

* Add index page for "Advanced"

* update exchanges.md grammar

* update philosophy.md & stream-patterns.md

* move help.md

* Update basics code examples and mutation errors

* Remove "Common Questions" page

* Fix homepage styling

* Expand "Server-side Rendering" page with more content

* Update intro section of "Testing"

* Add disclaimer and intro to "Auto-populate Mutations"

* Add table styling in mdx.js, remove required column(s)

* Add "Graphcache" index page content

* Fix typo in server-side rendering doc

* Add special styling for inline-code table cells

* adjust computed-queries

* remove spacing from api docs

* Add more explanation to "Normalized Caching"

* elaborate on partial results

* Edit "Custom Updates"

* Add initial "Showcase" content

* Upgrade react-static-plugin-md-pages (fixes raw HTML)

* Adjust "Showcase" styling

* Edit "API @urql/core" docs

* Edit "urql (React)" docs

* Fix links edge cases (sidebar and markdown links with hashes)

* Resolve TODO comments in the docs pages

* Add initial "@urql/exchange-graphcache" API docs

* Add finalised "@urql/exchange-graphcache API" content

* Remove frontmatter from help.md

* Update yarn.lock file

* Remove "Common Questions" reference from docs index page

* Add new installation instructions to "Auto-populate Mutations"

* Fix table formatting on Graphcache API page

* Add deployment stages to Circle CI

Add surge CLI

Exclude private packages from build

Add deployment tasks to docs

Add deploy scripts

Set threading limit for react-static

Call surge with yarn

Publish to surge using commit hash in subdomain

* basic loader implementation

* Fix mobile menu max width

* Deploy to PR_NUM instead of COMMIT_HASH

* add scroll to table on small vw

* add white-space pre to Pre code blocks

Co-authored-by: Jovi De Croock <decroockjovi@gmail.com>
Co-authored-by: Will Golledge <will.golledge@formidable.com>

authored by kitten.sh

Jovi De Croock
Will Golledge
and committed by
GitHub
e6710d29 eb33052a

+2857 -1082
+52 -8
.circleci/config.yml
··· 1 - version: 2 1 + version: 2.1 2 + 3 + executors: 4 + node: 5 + docker: 6 + - image: circleci/node:12-buster 2 7 3 8 aliases: 4 - - &docker 5 - - image: circleci/node:12-buster 6 9 - &yarn_cache 7 10 restore_cache: 8 11 name: Restore Yarn cache ··· 16 19 17 20 jobs: 18 21 setup: 19 - docker: *docker 22 + executor: node 20 23 steps: 21 24 - checkout 22 25 - *yarn_cache ··· 28 31 - ~/.cache/yarn 29 32 30 33 lint: 31 - docker: *docker 34 + executor: node 32 35 steps: 33 36 - checkout 34 37 - *yarn_cache ··· 38 41 command: yarn run lint 39 42 40 43 typescript: 41 - docker: *docker 44 + executor: node 42 45 steps: 43 46 - checkout 44 47 - *yarn_cache ··· 48 51 command: yarn run check 49 52 50 53 test: 51 - docker: *docker 54 + executor: node 52 55 steps: 53 56 - checkout 54 57 - *yarn_cache ··· 58 61 command: yarn run test --maxWorkers=2 59 62 60 63 build: 61 - docker: *docker 64 + executor: node 62 65 parallelism: 4 63 66 steps: 64 67 - checkout ··· 68 71 name: Build 69 72 command: yarn run build 70 73 74 + staging_site: 75 + executor: node 76 + steps: 77 + - checkout 78 + - *yarn_cache 79 + - *yarn 80 + - run: 81 + name: Deploy Staging Site 82 + command: | 83 + cd packages/site 84 + yarn run build --staging 85 + node scripts/deploy/surge.js 86 + 87 + production_site: 88 + executor: node 89 + steps: 90 + - run: 91 + name: Install AWS CLI 92 + command: sudo apt-get -y -qq install awscli 93 + - checkout 94 + - *yarn_cache 95 + - *yarn 96 + - run: 97 + name: Deploy Production Site 98 + command: | 99 + cd packages/site 100 + yarn run build 101 + node scripts/deploy/aws.js 102 + 71 103 workflows: 72 104 version: 2 73 105 stable: ··· 85 117 - build: 86 118 requires: 87 119 - setup 120 + - staging_site: 121 + filters: 122 + branches: 123 + ignore: master 124 + requires: 125 + - setup 126 + - production_site: 127 + filters: 128 + branches: 129 + only: master 130 + requires: 131 + - setup
+4 -10
docs/README.md
··· 16 16 ## Constituent Parts 17 17 18 18 `urql` can be understood as a collection of connected parts and packages. When you [get 19 - started](./basics/setting-up-the-client.md) you'll only need to install a single package for your 19 + started](./basics/getting-started.md) you'll only need to install a single package for your 20 20 framework of choice. We're then able to declaratively send GraphQL requests to our API. 21 21 22 22 All framework packages — like `urql` and `@urql/preact` — wrap the core package, which we can ··· 31 31 interest. 32 32 33 33 - **Basics** is the section where we find the ["Getting Started" 34 - guide](./basics/setting-up-the-client.md) and usage patterns for our framework of choice. 34 + guide](./basics/getting-started.md) and usage patterns for our framework of choice. 35 35 - **Main Concepts** then explains more about how `urql` functions, what it's made up of, and covers 36 36 the main aspects of the `Client` and GraphQL clients in general, on the ["Philosophy" 37 37 page](./concepts/philosophy.md) ··· 40 40 - **Graphcache** documents one of the most important addons to `urql`, which adds ["Normalized 41 41 Caching" support](./graphcache/normalized-caching.md) to the `Client` and enables more complex 42 42 use-cases, smarter caching, and more dynamic apps to function. 43 + - **Showcase** aims to list some companies that use `urql`, third-party packages, and other helpful 44 + resources, like tutorials or guides. 43 45 - **API** contains a detailed list of all helpers, utilities, components, and other parts of each of 44 46 `urql`'s packages, which may contain all details of each part and package. 45 - 46 - Apart from these main sections there is also some additional content that doesn't fit into any of 47 - the other sections. 48 - 49 - - **Showcase** aims to list some companies that use `urql`, third-party packages, and other helpful 50 - resources. 51 - - **Common Questions** lists frequently asked questions and problems that we may encounter 52 - infrequently (but shall answer nonetheless.) 53 47 54 48 We hope you grow to love `urql`!
+11
docs/advanced/README.md
··· 4 4 --- 5 5 6 6 # Advanced 7 + 8 + In this chapter we'll dive into various topics of "advanced" `urql` usage. This is admittedly a 9 + catch-all chapter of various use-cases that can only be covered after [the "Concepts" 10 + chapter.](../coconcepts/README.md) 11 + 12 + - **Subscriptions** covers how to use `useSubscription` and how to set up GraphQL subscriptions with 13 + `urql`. 14 + - **Server-side Rendering** guides us through how to set up server-side rendering and rehydration. 15 + - **Auto-populate Mutations** presents the `populateExchange` addon which can make it easier to 16 + update normalized data after mutations. 17 + - **Testing** covers how to test components that use `urql` particularly in React.
+33 -10
docs/advanced/auto-populate-mutations.md
··· 5 5 6 6 # Automatically populating Mutations 7 7 8 - `populate` is an exchange for auto-populating fields in your mutations. 8 + The `populateExchange` allows you to auto-populate selection sets in your mutations using the 9 + `@populate` directive. In combination with [Graphcache](../graphcache/README.md) this is a useful 10 + tool to update the data in your application automatically following a mutation, when your app grows 11 + and it becomes harder to track all fields that have been queried before. 9 12 10 - ## How to use 13 + > **NOTE:** The `populateExchange` is currently _experimental_! Certain patterns and usage paths 14 + > like GraphQL field arguments aren't covered yet, and the exchange hasn't been extensively used 15 + > yet. 11 16 12 - As with any exchange, add this to your client. 17 + ## Installation and Setup 13 18 14 - > Note: Populate needs to be declared before the cache in order to ensure the `populate` directive is replaced with the respective attributes. 19 + The `populateExchange` can be installed via the `@urql/exchange-populate` package. 20 + 21 + ```sh 22 + yarn add @urql/exchange-populate 23 + # or 24 + npm install --save @urql/exchange-populate 25 + ``` 26 + 27 + Afterwards we can set the `populateExchange` up by adding it to our list of `exchanges` in the 28 + client options. 15 29 16 - ```tsx 30 + ```ts 31 + import { createClient, dedupExchange, populateExchange, fetchExchange } from '@urql/core'; 17 32 import { populateExchange } from '@urql/exchange-graphcache'; 18 33 19 34 const client = createClient({ 20 35 // ... 21 36 exchanges: [dedupExchange, populateExchange, cacheExchange, fetchExchange], 22 - }) 37 + }); 23 38 ``` 39 + 40 + The `populateExchange` should be placed in front of the `cacheExchange`, especially if you're using 41 + [Graphcache](../graphcache/README.md), since it won't understand the `@populate` directive on its 42 + own. It should also be placed after the `dedupExchange` to avoid unnecessary work. 43 + 44 + Adding the `populateExchange` now enables us to use the `@populate` directive in our mutations. 24 45 25 46 ## Example usage 26 47 27 48 Consider the following queries which have been requested in other parts of your application: 28 49 29 - 30 50 ```graphql 31 51 # Query 1 32 52 { ··· 67 87 } 68 88 ``` 69 89 70 - > Note: The above two mutations produce an identical GraphQL request. 71 - 72 90 ### Choosing when to populate 73 91 74 92 You may not want to populate your whole mutation response. In order to reduce your payload, pass populate lower in your query. ··· 84 102 85 103 ### Using aliases 86 104 87 - If you find yourself using multiple queries with variables, it may be necessary to [use aliases](https://graphql.org/learn/queries/#aliases) in order to allow merging of queries. 105 + If you find yourself using multiple queries with variables, it may be necessary to 106 + [use aliases](https://graphql.org/learn/queries/#aliases) in order to allow merging of queries. 107 + 108 + > **Note:** This caveat may change in the future or this restriction may be lifted. 88 109 89 110 **Invalid usage** 111 + 90 112 ```graphql 91 113 # Query 1 92 114 { ··· 106 128 ``` 107 129 108 130 **Usage with aliases** 131 + 109 132 ```graphql 110 133 # Query 1 111 134 {
+217 -22
docs/advanced/server-side-rendering.md
··· 5 5 6 6 # Server-side Rendering 7 7 8 - `urql` supports server-side rendering through the `ssrExchange`, this exchange 9 - will gather all results that are fetched during the server side render and store 10 - them. This data can then be used on the client when the application is being 11 - hydrated. 8 + In server-side rendered applications we often need to set our application up so that data will be 9 + fetched on the server-side and later sent down to the client for hydration. `urql` supports this 10 + through the `ssrExchange.` 11 + 12 + ## The SSR Exchange 12 13 13 - ## Setting up 14 + The `ssrExchange` has two functions. On the server-side it's able to gather all results as they're 15 + being fetched, which can then be serialised and sent to the client. On the client-side it's able to 16 + use these serialised results to rehydrate and render the application without refetching this data. 14 17 15 - To start out with the `ssrExchange` we have to add the exchange 18 + To start out with the `ssrExchange` we have to add the exchange to our `Client`: 16 19 17 20 ```js 18 - const isClient = typeof window !== 'undefined'; 21 + import { 22 + createClient, 23 + dedupExchange, 24 + cacheExchange, 25 + fetchExchange, 26 + ssrExchange 27 + } from '@urql/core'; 28 + 29 + const isServerSide = typeof window === 'undefined'; 30 + 31 + // The `ssrExchange` must be initialised with `isClient` and `initialState` 19 32 const ssr = ssrExchange({ 20 - initialData: isClient ? window.URQL_DATA : undefined, 21 - // This will need to be passed explicitly to ssrExchange: 22 - isClient: !!isClient 33 + isClient: !isServerSide, 34 + initialState: !isServerSide ? window.__URQL_DATA__ : undefined, 23 35 }); 24 36 25 - const client = createClient({ 37 + const client createClient({ 26 38 exchanges: [ 27 39 dedupExchange, 28 40 cacheExchange, 29 - ssr, 41 + ssr, // Add `ssr` in front of the `fetchExchange` 30 42 fetchExchange, 31 43 ] 32 - }) 44 + }); 45 + ``` 46 + 47 + The `ssrExchange` must be initialised with the `isClient` and `initialState` options. The `isClient` 48 + option tells the exchange whether it's on the server- or client-side. In our example we use `typeof window` to determine this, but in Webpack environments you may also be able to use `process.browser`. 49 + 50 + The `initialState` option should be set to the serialised data you retrieve on your server-side. 51 + This data may be retrieved using methods on `ssrExchange()`. You can retrive the serialised data 52 + after server-side rendering using `ssr.extractData()`: 53 + 54 + ```js 55 + // Extract and serialise the data like so from the `ssr` instance 56 + // we've previously created by calling `ssrExchange()` 57 + const data = JSON.stringify(ssr.extractData()); 58 + 59 + const markup = ''; // The render code for our framework goes here 60 + 61 + const html = ` 62 + <html> 63 + <body> 64 + <div id="root">${markup}</div> 65 + <script> 66 + window.__URQL_DATA__ = JSON.parse(${data}); 67 + </script> 68 + </body> 69 + </html> 70 + `; 71 + ``` 72 + 73 + This will provide `__URQL_DATA__` globally which we've used in our first example to inject data into 74 + the `ssrExchange` on the client-side. 75 + 76 + Alternatively you can also call `restoreData` as long as this call happens synchronously before the 77 + `client` starts receiving queries. 78 + 79 + ```js 80 + const isServerSide = typeof window === 'undefined'; 81 + const ssr = ssrExchange({ isClient: !isServerSide }); 82 + 83 + if (!isServerSide) { 84 + ssr.restoreData(window.__URQL_DATA__); 85 + } 86 + ``` 87 + 88 + ## Using `react-ssr-prepass` 89 + 90 + In the previous examples we've set up the `ssrExchange`, however with React this still requires us 91 + to manually execute our queries before rendering a server-side React app [using `renderToString` 92 + or `renderToNodeStream`](https://reactjs.org/docs/react-dom-server.html#rendertostring). 93 + 94 + For React, `urql` has a "Suspense mode" that [allows data fetching to interrupt 95 + rendering](https://reactjs.org/docs/concurrent-mode-suspense.html). However, suspense is currently 96 + not supported by React during server-side rendering. 97 + 98 + Using [the `react-ssr-prepass` package](https://github.com/FormidableLabs/react-ssr-prepass) however, 99 + we can implement a prerendering step before we let React server-side render, which allows us to 100 + automatically fetch all data that the app requires with Suspense. This technique is commonly 101 + referred to as a "two-pass approach", since our React element is traversed twice. 102 + 103 + To set this up, first we'll install `react-ssr-prepass`. It has a peer dependency on `react-is` 104 + and `react`. 105 + 106 + ```sh 107 + yarn add react-ssr-prepass react-is react-dom 108 + # or 109 + npm install --save react-ssr-prepass react-is react-dom 110 + ``` 111 + 112 + Next, we'll modify our server-side code and add `react-ssr-prepass` in front of `renderToString`. 113 + 114 + ```jsx 115 + import { renderToString } from 'react-dom/server'; 116 + import prepass from 'react-ssr-prepass'; 117 + 118 + import { 119 + createClient, 120 + dedupExchange, 121 + cacheExchange, 122 + fetchExchange, 123 + ssrExchange 124 + } from 'urql'; 125 + 126 + const handleRequest = async (req, res) => { 127 + // ... 128 + const ssr = ssrExchange({ isClient: false }); 129 + 130 + const client createClient({ 131 + suspense: true, // This activates urql's Suspense mode on the server-side 132 + exchanges: [dedupExchange, cacheExchange, ssr, fetchExchange] 133 + }); 134 + 135 + const element = ( 136 + <Provider value={client}> 137 + <App /> 138 + </Provider> 139 + ); 140 + 141 + // Using `react-ssr-prepass` this prefetches all data 142 + await prepass(element); 143 + // This is the usual React SSR rendering code 144 + const markup = renderToString(element); 145 + // Extract the data after prepass and rendering 146 + const data = JSON.stringify(ssr.extractData()); 147 + 148 + res.status(200).send(` 149 + <html> 150 + <body> 151 + <div id="root">${markup}</div> 152 + <script> 153 + window.__URQL_DATA__ = JSON.parse(${data}); 154 + </script> 155 + </body> 156 + </html> 157 + `); 158 + }; 33 159 ``` 34 160 35 - The `ssrExchange` allows you to pass in an object with two options, one being `isClient`, 36 - this option tells the exchange you're in the browser rather than in the process of a server-side 37 - render. The other option is called `initialState`, this being a mapping of `operationKey` to `operationResult`, 38 - this could for instance be `window.__URQL_DATA__`. 161 + It's important to set enable the `suspense` option on the `Client`, which switches it to support 162 + React suspense. 39 163 40 - The returned exchange will have two methods available on it, `restoreData` and `extractData`, during your ssr 41 - we extract the gathered data and serialize it into a `<script>` tag inside head, this should bind it to a unique 42 - property on `window` for instance `window.__URQL_DATA__`. 164 + ### With Preact 43 165 44 - When `isClient` is true it will use the `initialState` to restore the gathered data. 166 + If you're using Preact instead of React, there's a drop-in replacement package for 167 + `react-ssr-prepass`, which is called `preact-ssr-prepass`. It only has a peer dependency on Preact 168 + and we can install it like so: 45 169 46 - ## Next 170 + ```sh 171 + yarn add preact-ssr-prepass preact 172 + # or 173 + npm install --save preact-ssr-prepass preact 174 + ``` 175 + 176 + All above examples for `react-ssr-prepass` will still be the exact same, except that instead of 177 + using the `urql` package we'll have to import from `@urql/preact`, and instead of `react-ssr-prepass` 178 + we'll have to import from. `preact-ssr-prepass`. 179 + 180 + ## Next.js 181 + 182 + If you're using [Next.js](https://nextjs.org/) you can save yourself a lot of work by using 183 + `next-urql`. The `next-urql` package includes setup for `react-ssr-prepass` already, which automates 184 + a lot of the complexity of setting up server-side rendering with `urql`. 47 185 48 186 We have a custom integration with [`Next.js`](https://nextjs.org/), being [`next-urql`](https://github.com/FormidableLabs/next-urql) 49 187 this integration contains convenience methods specifically for `Next.js`. 50 188 These will simplify the above setup for SSR. 189 + 190 + To setup `next-urql`, first we'll install `next-urql` with `react-is` and `isomorphic-unfetch` as 191 + peer dependencies: 192 + 193 + ```sh 194 + yarn add next-urql react-is isomorphic-unfetch 195 + # or 196 + npm install --save next-urql react-is isomorphic-unfetch 197 + ``` 198 + 199 + The peer dependency on `react-is` is inherited from `react-ssr-prepass` requiring it, and the peer 200 + dependency on `isomorphic-unfetch` exists, since `next-urql` automatically injects it as a `fetch` 201 + polyfill. 202 + 203 + We're now able to wrap any page or `_app.js` using the `withUrqlClient` higher-order component. If 204 + we wrap `_app.js` we won't have to wrap any individual page, but we also won't be able to make use 205 + of Next's ["Automatic Static 206 + Optimization"](https://nextjs.org/docs/advanced-features/automatic-static-optimization). 207 + 208 + ```js 209 + // pages/index.js 210 + import React from 'react'; 211 + import Head from 'next/head'; 212 + import { withUrqlClient } from 'next-urql'; 213 + 214 + const Index = () => { 215 + const [result] = useQuery({ 216 + query: '{ test }', 217 + }); 218 + 219 + // ... 220 + }; 221 + 222 + export default withUrqlClient(ctx => ({ 223 + // ...add your Client options here 224 + url: 'http://localhost:3000/graphql', 225 + }))(Index); 226 + ``` 227 + 228 + This will automatically set up server-side rendering on the page. The `withUrqlClient` higher-order 229 + component function accepts the usual `Client` options as an argument. This may either just be an 230 + object or a function that receives the Next.js' `getInitialProps` context. 231 + 232 + One added caveat is that these options may not include the `exchanges` option because `next-urql` 233 + injects the `ssrExchange` automatically at the right location. If you're setting up custom exchanges 234 + you'll need to instead provide them in a custom `mergeExchanges` function as the second argument: 235 + 236 + ```js 237 + import { dedupExchange, cacheExchange, fetchExchange } from '@urql/core'; 238 + 239 + import { withUrqlClient } from 'next-urql'; 240 + 241 + // Modify this array to include custom exchanges: 242 + const mergeExchanges = ssrExchange => [dedupExchange, cacheExchange, ssrExchange, fetchExchange]; 243 + 244 + export default withUrqlClient({ url: 'http://localhost:3000/graphql' }, mergeExchanges)(Index); 245 + ```
+6 -9
docs/advanced/testing.md
··· 3 3 order: 4 4 4 --- 5 5 6 - 7 6 # Testing 8 7 9 - When testing your components, you're likely going to want to check arguments and force different states for your components using Urql. 8 + Testing with `urql` can be done in a multitude of ways. The most effective and straightforward 9 + method is to mock the `Client` to force your components into a fixed state during testing. 10 10 11 - > **Note:** Examples demonstrate the _React hooks_ version of Urql being used but underlying patterns apply to all implementations. 11 + The following examples demonstrate this method of testing for React and the `urql` package only, 12 + however the pattern itself can be adapted for any framework-bindings of `urql`. 12 13 13 14 ## Mocking the client 14 15 ··· 70 71 name: 'Carla', 71 72 }; 72 73 73 - wrapper 74 - .find('input') 75 - .simulate('change', { currentTarget: { value: variables.name } }); 74 + wrapper.find('input').simulate('change', { currentTarget: { value: variables.name } }); 76 75 wrapper.find('button').simulate('click'); 77 76 78 77 expect(mockClient.executeMutation).toBeCalledTimes(1); 79 - expect(mockClient.executeMutation).toBeCalledWith( 80 - expect.objectContaining({ variables }) 81 - ); 78 + expect(mockClient.executeMutation).toBeCalledWith(expect.objectContaining({ variables })); 82 79 }); 83 80 ``` 84 81
+10 -1
docs/api/README.md
··· 5 5 6 6 # API 7 7 8 - <!-- List of packages and subpages --> 8 + `urql` is a collection of multiple packages. You'll likely be using one of the framework bindings 9 + package or exchange packages, which are all listed in this section. 10 + 11 + Most of these packages will refer to or use utilities and types from the `@urql/core` package. [Read 12 + more about the core package on the "Core Package" page.](../concepts/core-package.md) 13 + 14 + - [`@urql/core` API docs](./core.md) 15 + - [`urql` React API docs](./urql.md) 16 + - [`@urql/preact` Preact API docs](./preact.md) 17 + - [`@urql/exchange-graphcache` API docs](./graphcache.md)
+265 -180
docs/api/core.md
··· 5 5 6 6 # @urql/core 7 7 8 - ## The Client and related types 8 + The `@urql/core` package is the basis of all framework bindings. Every bindings package, 9 + like [`urql` for React](./urql.md) or [`@urql/preact`](./preact.md) will reuse the core logic and 10 + reexport all exports from `@urql/core`. 11 + Therefore if you're not accessing utilities directly, aren't in a Node.js environment, and are using 12 + framework bindings, you'll likely want to import from your framework bindings package directly. 9 13 10 - ### Client (class) 14 + [Read more about `urql`'s core on the "Core Package" page.](../concepts/core-package.md) 11 15 12 - The client manages all operations and ongoing requests to the exchange pipeline. 13 - It accepts a bunch of inputs when it's created 16 + ## Client 14 17 15 - | Input | Type | Description | 16 - | ------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | 17 - | url | `string` | The GraphQL API URL as used by `fetchExchange` | 18 - | fetchOptions | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request | 19 - | fetch | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` | 20 - | suspense | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data | 21 - | exchanges | `Exchange[]` | An array of `Exchange`s that the client should use instead of the list of `defaultExchanges` | 18 + The `Client` manages all operations and ongoing requests to the exchange pipeline. 19 + It accepts several options on creation. 22 20 23 - `urql` also exposes `createClient()` that is just a convenient alternative to calling `new Client()`. 21 + `@urql/core` also exposes `createClient()` that is just a convenient alternative to calling `new Client()`. 24 22 25 - #### .executeQuery(), .executeSubscription(), and .executeMutation() 23 + | Input | Type | Description | 24 + | --------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 25 + | exchanges | `Exchange[]` | An array of `Exchange`s that the client should use instead of the list of `defaultExchanges` | 26 + | url | `string` | The GraphQL API URL as used by `fetchExchange` | 27 + | fetchOptions | `RequestInit \| () => RequestInit` | Additional `fetchOptions` that `fetch` in `fetchExchange` should use to make a request | 28 + | fetch | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` | 29 + | suspense | `?boolean` | Activates the experimental React suspense mode, which can be used during server-side rendering to prefetch data | 30 + | requestPolicy | `?RequestPolicy` | Changes the default request policy that will be used. By default this will be `cache-first`. | 31 + | preferGetMethod | `?boolean` | This is picked up by the `fetchExchange` and will force all queries (not mutations) to be sent using the HTTP GET method instead of POST. | 32 + | maskTypename | `?boolean` | Enables the `Client` to automatically apply the `maskTypename` utility to all `data` on [`OperationResult`s](#operationresult). This makes the `__typename` properties non-enumerable. | 26 33 27 - These methods are used by `<Query>` & `useQuery()`, `<Subscription>` & `useSubscription()`, 28 - and `<Mutation>` & `useMutation()` respectively. 34 + ### client.executeQuery 29 35 30 - They accept a `GraphQLRequest` object as their first argument and optionally 31 - a partial `OperationContext` as their second. 36 + Accepts a [`GraphQLRequest`](#graphqlrequest) and optionally `Partial<OperationContext>`, and returns a 37 + [`Source<OperationResult>`](#operationresult) — a stream of query results that can be subscribed to. 32 38 33 - Internally they then create an `Operation` and call `.executeRequestOperation()` with 34 - the `Operation`. This then returns a `Source<OperationResult>`, i.e. a stream of 35 - `OperationResult`s. 39 + Internally, subscribing to the returned source will create an [`Operation`](#operation), with 40 + `operationName` set to `'query'`, and dispatch it on the 41 + exchanges pipeline. If no subscribers are listening to this operation anymore and unsubscribe from 42 + the query sources, the `Client` will dispatch a "teardown" operation. 36 43 37 - #### .query and .mutation 44 + - [Instead of using this method directly, you may want to use the `client.query` shortcut 45 + instead.](#clientquery) 46 + - [See `createRequest` for a utility that creates `GraphQLRequest` objects.](#createrequest) 38 47 39 - These two methods accept a `query`, `variables` and a `context`, these two methods 40 - are really similar to the above in the sense that they return you a `Source<OperationResult>` 41 - you can subscribe to. The difference is that this returned value has a method on it called 42 - `toPromise`, when invoked it will convert the `Source` to a one-time promise. These methods 43 - are ideal for SSR, like for example the `getInitialProps` method in [Next.js](https://nextjs.org/). 48 + A feature that is specific to `client.executeQuery` and isn't supported by 49 + `client.executeSubscription` and `client.executeMutation` is polling. You may optionally pass a 50 + `pollInterval` option on the `OperationContext` object, which will instruct the query to reexecute 51 + repeatedly in the interval you pass. 44 52 45 - #### .executeRequestOperation() 53 + ### client.executeSubscription 46 54 47 - This method accepts an `Operation` and handles the flow of said `Operation`. Every `Operation` 48 - that is executed must pass through this method. 55 + This is functionally the same as `client.executeQuery`, but creates operations for subscriptions 56 + instead, with `operationName` set to `'mutation'`. 49 57 50 - It creates a filtered `Source<OperationResult>` that only contains the `OperationResult`s 51 - relevant to this `Operation` by filtering by the operation `key` and track the subscriptions 52 - to this `Source`. 58 + ### client.executeMutation 53 59 54 - This is important as a cache exchange can call `reexecuteOperation` to inform the 55 - client about an invalidation. Whenever an operation needs to be updated with new 56 - network data, it's important to know whether any component is still interested in 57 - this operation. 60 + This is functionally the same as `client.executeQuery`, but creates operations for mutations 61 + instead, with `operationName` set to `'mutation'`. 62 + 63 + A mutation source is always guaranteed to only respond with a single [`OperationResult`](#operationresult) and then complete. 64 + 65 + ### client.query 66 + 67 + This is a shorthand method for [`client.executeQuery`](#clientexecutequery), which accepts a query 68 + (`DocumentNode | string`) and variables separately and creates a [`GraphQLRequest`](#graphqlrequest) [`createRequest`](#createrequest) automatically. 69 + 70 + The returned `Source<OperationResult>` will also have an added `toPromise` method so the stream can 71 + be conveniently converted to a promise. 72 + 73 + ```js 74 + import { pipe, subscribe } from 'wonka'; 75 + 76 + const { subscribe } = pipe( 77 + client.query('{ test }', { 78 + /* vars */ 79 + }), 80 + subscribe(result => { 81 + console.log(result); // OperationResult 82 + }) 83 + ); 84 + 85 + // or with toPromise, which also limits this to one result 86 + client 87 + .query('{ test }', { 88 + /* vars */ 89 + }) 90 + .then(result => { 91 + console.log(result); // OperationResult 92 + }); 93 + ``` 94 + 95 + [Read more about how to use this API on the "Core Package" 96 + page.](../concepts/core-package.md#one-off-queries-and-mutations) 97 + 98 + ### client.mutation 99 + 100 + This is similar to [`client.query`](#clientquery), but dispatches mutations instead. 58 101 59 - To track this, this method ensures that a mapping is updated that counts up 60 - for each subscription to the `Source` and counts down for each unsubscription. 102 + [Read more about how to use this API on the "Core Package" 103 + page.](../concepts/core-package.md#one-off-queries-and-mutations) 61 104 62 - The `Operation` that has been passed to this method will be dispatched 63 - when the first subscription is started. When the last subscription unsubscribes 64 - from the returned source, this method will ensure that a `teardown` operation 65 - is dispatched. 105 + #### client.reexecuteOperation 66 106 67 - > _Note:_ This does not apply to mutations, which are one-off calls and 68 - > hence aren't shared, cancelled, or tracked in the cache. 107 + This method is commonly used in _Exchanges_ to reexecute an [`Operation`](#operation) on the 108 + `Client`. It will only reexecute when there are still subscribers for the given 109 + [`Operation`](#operation). 69 110 70 - The return value is the filtered `Source<OperationResult>`. 111 + For an example, this method is used by the `cacheExchange` when an 112 + [`OperationResult`](#operationresult) is invalidated in the cache and needs to be refetched. 71 113 72 - #### .reexecuteOperation() 114 + ## CombinedError 73 115 74 - This method accepts an `Operation` and will dispatch this `Operation` if there 75 - are any subscriptions from `executeRequestOperation`'s `Source<OperationResult>` 76 - to this particular `Operation`. 116 + The `CominedError` is used in `urql` to normalize network errors and `GraphQLError`s if anything 117 + goes wrong during a GraphQL request. 77 118 78 - This is called by `cacheExchange` when an `Operation`'s `OperationResult` is 79 - invalidated in the cache. 119 + | Input | Type | Description | 120 + | ------------- | -------------------------------- | --------------------------------------------------------------------------------- | 121 + | networkError | `?Error` | An unexpected error that might've occured when trying to send the GraphQL request | 122 + | graphQLErrors | `?Array<string \| GraphQLError>` | GraphQL Errors (if any) that were returned by the GraphQL API | 123 + | response | `?any` | The raw response object (if any) from the `fetch` call | 80 124 81 - #### .createRequestOperation() 125 + [Read more about errors in `urql` on the "Error" page.](../basics/errors.md) 82 126 83 - This is called by the `executeQuery`, `executeSubscription` and `executeMutation` 84 - methods to create `Operation`s. It accepts: 127 + ## Types 85 128 86 - - `OperationType` 87 - - `GraphQLRequest` 88 - - and; the optional partial `OperationContext` (`Partial<OperationContext>`) 129 + ### GraphQLRequest 89 130 90 - It returns an `Operation`. 131 + This often comes up as the **input** for every GraphQL request. 132 + It consists of `query` and optionally `variables`. 91 133 92 - #### .dispatchOperation() 134 + | Prop | Type | Description | 135 + | --------- | -------------- | --------------------------------------------------------------------------------------------------------------------- | 136 + | key | `number` | A unique key that identifies this exact combination of `query` and `variables`, which is derived using a stable hash. | 137 + | query | `DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 138 + | variables | `?object` | The variables to be used with the GraphQL request. | 93 139 94 - This method dispatches an `Operation` to the exchange pipeline. This is only 95 - used directly by the Client and shouldn't normally be called externally, except 96 - when the tracking logic of active `Operation`s needs to be bypassed. 140 + The `key` property is a hash of both the `query` and the `variables`, to uniquely 141 + identify the request. When `variables` are passed it is ensured that they're stabily stringified so 142 + that the same variables in a different order will result in the same `key`, since variables are 143 + order-independent in GraphQL. 97 144 98 - These `Operation`s are streamed from the `operations$: Source<Operation>` stream. 99 - The results of all exchanges are similarly output to `results$: Source<OperationResult>`. 145 + [A `GraphQLRequest` may be manually created using the `createRequest` helper.](#createrequest) 100 146 101 - ### OperationType (type) 147 + ### OperationType 102 148 103 149 This determines what _kind of operation_ the exchanges need to perform. 104 150 This is one of: ··· 112 158 any ongoing operations with the same key as the `'teardown'` operation that is 113 159 received. 114 160 115 - ### RequestPolicy (type) 161 + ### Operation 162 + 163 + The input for every exchange that informs GraphQL requests. 164 + It extends the [GraphQLRequest](#graphqlrequest) type and contains these additional properties: 165 + 166 + | Prop | Type | Description | 167 + | ------------- | ------------------ | --------------------------------------------- | 168 + | operationName | `OperationType` | The type of GraphQL operation being executed. | 169 + | context | `OperationContext` | Additional metadata passed to exchange. | 170 + 171 + > **Note:** In `urql` the `operationName` on the `Operation` isn't the actual name of an operation 172 + > and dervied from the GraphQL `DocumentNode`, but instead a type of operation, like `'query'` or 173 + > `'teardown'` 174 + 175 + ### RequestPolicy 116 176 117 177 This determines the strategy that a cache exchange should use to fulfill an operation. 118 178 When you implement a custom cache exchange it's recommended that these policies are ··· 123 183 - `'network-only'` 124 184 - `'cache-and-network'` 125 185 126 - ### GraphQLRequest (type) 186 + [Read more about request policies on the "Queries" page.](../basics/queries.md#request-policies) 127 187 128 - This often comes up as the **input** for every GraphQL request. 129 - It consists of `query` and optional `variables`. 188 + ### OperationContext 130 189 131 - | Prop | Type | Description | Required | | 132 - | --------- | -------------- | --------------------------------------------------------------------------------------------------------------------- | -------- | --- | 133 - | key | `number` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 134 - | query | `DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 135 - | variables | `object` | The variables to be used with the GraphQL request. | No | 190 + The context often carries options or metadata for individual exchanges, but may also contain custom 191 + data that can be passed from almost all API methods in `urql` that deal with 192 + [`Operation`s](#operation). 136 193 137 - &nbsp; 194 + Some of these options are set when the `Client` is initialised, so in the following list of 195 + properties you'll likely see some options that exist on the `Client` as well. 138 196 139 - The `key` property is a hash of both the `query` and the `variables`, to uniquely 140 - identify the request. 141 - 142 - ### OperationContext (type) 197 + | Prop | Type | Description | 198 + | --------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | 199 + | fetchOptions | `?RequestInit \| (() => RequestInit)` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | 200 + | fetch | `typeof fetch` | An alternative implementation of `fetch` that will be used by the `fetchExchange` instead of `window.fetch` | 201 + | requestPolicy | `RequestPolicy` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | 202 + | url | `string` | The GraphQL endpoint | 203 + | pollInterval | `?number` | Every `pollInterval` milliseconds the query will be refetched. | 204 + | meta | `?OperationDebugMeta` | Metadata that is only available in development for devtools. | 205 + | suspense | `?boolean` | Whether suspense is enabled. | 206 + | preferGetMethod | `?number` | Instructs the `fetchExchange` to use HTTP GET for queries. | 143 207 144 - This type is used to give an operation additional metadata and information. 145 - 146 - | Prop | Type | Description | Always Present | | 147 - | --------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | -------------- | --- | 148 - | fetchOptions | `RequestInit \| (() => RequestInit)` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 149 - | requestPolicy | `RequestPolicy` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | Yes | 150 - | url | `string` | The GraphQL endpoint | Yes | 151 - | pollInterval | `number` | Every `pollInterval` milliseconds the query will be refetched. | No | 152 - | meta | `OperationDebugMeta` | Metadata that is only available in development for devtools. | No | 153 - | suspense | `boolean` | Whether suspense is enabled. | No | 154 - | preferGetMethod | `number` | Whether to use HTTP GET for queries. | No | 155 - 156 - &nbsp; 157 - 158 - It contains a lot of the above mentioned Client options and also `requestPolicy`. 159 - It accepts additional, untyped parameters that can be used to send more 208 + It also accepts additional, untyped parameters that can be used to send more 160 209 information to custom exchanges. 161 210 162 - ### Operation (type) 211 + ### OperationResult 163 212 164 - The input for every exchange that informs GraphQL requests. 165 - It contains all properties in the [GraphQLRequest](#graphqlrequest-type) type, as well as the additional properties below. 213 + The result of every GraphQL request, i.e. an `Operation`. It's very similar to what comes back from 214 + a typical GraphQL API, but slightly enriched and normalized. 166 215 167 - | Prop | Type | Description | Required | | 168 - | ------------- | ------------------ | --------------------------------------------- | -------- | --- | 169 - | operationName | `OperationType` | The type of GraphQL operation being executed. | Yes | 170 - | context | `OperationContext` | Additional metadata passed to exchange. | Yes | 216 + | Prop | Type | Description | 217 + | ---------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | 218 + | operation | `Operation` | The operation that this is a result for | 219 + | data | `?any` | Data returned by the specified query | 220 + | error | `?CombinedError` | A [`CombinedError`](#combinederror) instances that wraps network or `GraphQLError`s (if any) | 221 + | extensions | `?Record<string, any>` | Extensions that the GraphQL server may have returned. | 222 + | stale | `?boolean` | A flag that may be set to `true` by exchanges to indicate that the `data` is incomplete or out-of-date, and that the result will be updated soon. | 171 223 172 - ### OperationResult (type) 224 + ### ExchangeInput 173 225 174 - The result of every GraphQL request, i.e. an `Operation`. 175 - It's very similar to what comes back from a typical GraphQL API, but 176 - slightly enriched. 226 + This is the input that an [`Exchange`](#exchange) receives when it's initialized by the 227 + [`Client`](#client) 177 228 178 - | Prop | Type | Description | Always Present | 179 - | ---------- | --------------------- | ----------------------------------------------------- | -------------- | 180 - | operation | `Operation` | The operation that this is a result for | Yes | 181 - | data | Generic | Data returned by the specified query | No | 182 - | error | `CombinedError` | The query error | No | 183 - | extensions | `Record<string, any>` | Extensions that the GraphQL server may have returned. | No | 229 + | Input | Type | Description | 230 + | ------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | 231 + | forward | `ExchangeIO` | The unction responsible for receiving an observable operation and returning a result | 232 + | client | `Client` | The URQL application-wide client library. Each execute method starts a GraphQL request and returns a stream of results. | 184 233 185 - ### CombinedError (class) 234 + ### Exchange 186 235 187 - | Input | Type | Description | Required | 188 - | ------------- | ------------------------------- | --------------------------------------------------------------------------------- | -------- | 189 - | networkError | `Error` | An unexpected error that might've occured when trying to send the GraphQL request | No | 190 - | graphQLErrors | `Array<string \| GraphQLError>` | GraphQL Errors (if any) that were returned by the GraphQL API | No | 191 - | response | `any` | The raw response object (if any) from the `fetch` call | No | 236 + An exchange represents abstractions of small chunks of logic in `urql`. 237 + They're small building blocks and similar to "middleware". 192 238 193 - These are both inputs and properties on the `CombinedError`. Additionally it exposes a default `message` 194 - that combines all errors it has received. 195 - 196 - This is on every `OperationResult` that has one or more errors and groups the usual `errors` property 197 - that a GraphQL result might have normally. 198 - 199 - ## Exchanges and their utilities 200 - 201 - ### ExchangeInput (type) 202 - 203 - | Input | Type | Description | Required | 204 - | ------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | -------- | 205 - | forward | `ExchangeIO` | The unction responsible for receiving an observable operation and returning a result | Yes | 206 - | client | `Client` | The URQL application-wide client library. Each execute method starts a GraphQL request and returns a stream of results. | Yes | 239 + [Read more about _Exchanges_ on the "Exchanges" page.](../concepts/exchanges.md) 207 240 208 - ### ExchangeIO (type) 209 - 210 - A function that receives a stream of operations and must return a stream 211 - of results. 241 + An exchange is defined to be a function that receives [`ExchangeInput`](#exchangeinput) and returns 242 + an `ExchangeIO` function. The `ExchangeIO` function in turn will receive a stream of operations, and 243 + must return a stream of results. If the exchange is purely transforming data, like the 244 + `dedupExchange` for instance, it'll call `forward`, which is the next Exchange's `ExchangeIO` 245 + function to get a stream of results. 212 246 213 247 ```js 214 248 type ExchangeIO = (Source<Operation>) => Source<OperationResult>; 215 - ``` 216 - 217 - ### Exchange (type) 218 - 219 - Similar to `redux-observable`'s epics, kind of related to Apollo's links, 220 - also somehow similar to Express' middleware. 221 - 222 - ```js 223 249 type Exchange = ExchangeInput => ExchangeIO; 224 250 ``` 225 251 226 - This works since every exchange receives `forward` with the `ExchangeInput`. 227 - Exchanges can therefore be chained. They can alter and filter `Operation`s 228 - that go into the next exchange, and they can alter, filter, or return 229 - `OperationResult`s that are returned. 252 + [If you haven't yet seen `Source`, read more about "Stream 253 + Patterns".](../concepts/stream-patterns.md) 230 254 231 - ### composeExchanges (function) 255 + ## Exchanges 232 256 233 - This utility accepts multiple exchanges and composes them into a single one. 234 - It chains them in the order that they're given, left to right. 235 - 236 - ```js 237 - function composeExchanges(Exchange[]): Exchange; 238 - ``` 239 - 240 - This can be used to combine some exchanges and is also used by `Client` 241 - to handle the `exchanges` input. 242 - 243 - ### cacheExchange (Exchange) 257 + ### cacheExchange 244 258 245 - The `cacheExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#cacheexchange). 246 - It's of type `Exchange`. 259 + The `cacheExchange` as [described on the "Document Caching" page.](../basics/document-caching.md). It's of type `Exchange`. 247 260 248 - ### subscriptionExchange (Exchange factory) 261 + ### subscriptionExchange 249 262 250 - The `subscriptionExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#subscriptions). 251 - It's of type `Options => Exchange`. 263 + The `subscriptionExchange` as [described on the "Subscriptions" page.](../advanced/subscriptions.md). It's of type `Options => Exchange`. 252 264 253 265 It accepts a single input: `{ forwardSubscription }`. This is a function that 254 266 receives an enriched operation and must return an Observable-like object that 255 267 streams `GraphQLResult`s with `data` and `errors`. 256 268 257 - ### ssrExchange (Exchange factory) 269 + The `forwardSubscription` function is commonly connected to the [`subscriptions-transport-ws` 270 + package](https://github.com/apollographql/subscriptions-transport-ws). 258 271 259 - The `ssrExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#server-side-rendering). 272 + ### ssrExchange 273 + 274 + The `ssrExchange` as [described on the "Server-side Rendering" 275 + page.](../advanced/server-side-rendering.md). 260 276 It's of type `Options => Exchange`. 261 277 262 278 It accepts two inputs, `initialState` which is completely ··· 294 310 `cacheExchange`, but before any _asynchronous_ Exchange like 295 311 the `fetchExchange`. 296 312 297 - ### debugExchange (Exchange) 313 + ### debugExchange 298 314 299 315 An exchange that writes incoming `Operation`s to `console.log` and 300 316 writes completed `OperationResult`s to `console.log`. 301 317 302 - ### dedupExchange (Exchange) 318 + ### dedupExchange 303 319 304 320 An exchange that keeps track of ongoing `Operation`s that haven't returned had 305 321 a corresponding `OperationResult` yet. Any duplicate `Operation` that it 306 322 receives is filtered out if the same `Operation` has already been received 307 323 and is still waiting for a result. 308 324 309 - ### fallbackExchangeIO (ExchangeIO) 325 + ### fetchExchange 326 + 327 + The `fetchExchange` of type `Exchange` is responsible for sending operations of type `'query'` and 328 + `'mutation'` to a GraphQL API using `fetch`. 329 + 330 + ## Utilities 331 + 332 + ### stringifyVariables 333 + 334 + This function is a variation of `JSON.stringify` that sorts any object's keys that is being 335 + stringified to ensure that two objects with a different order of keys will be stabily stringified to 336 + the same string. 337 + 338 + ```js 339 + stringifyVariables({ a: 1, b: 2 }); // {"a":1,"b":2} 340 + stringifyVariables({ b: 2, a: 1 }); // {"a":1,"b":2} 341 + ``` 342 + 343 + ### createRequest 344 + 345 + This utility accepts a GraphQL query of type `string | DocumentNode` and optionally an object of 346 + variables, and returns a [`GraphQLRequest` object](#graphqlrequest). 347 + 348 + Since the [`client.executeQuery`](#clientexecutequery) and other execute methods only accept 349 + [`GraphQLRequest`s](#graphqlrequest), this helper is commonly used to create that request first. The 350 + [`client.query`](#clientquery) and [`client.mutation`](#clientmutation) methods use this helper as 351 + well to create requests. 352 + 353 + The helper takes are of creating a unique `key` for the `GraphQLRequest`. This is a hash of the 354 + `query` and `variables` if they're passed. The `variables` will be stringified using 355 + [`stringifyVariables`](#stringifyvariables), which outputs a stable JSON string. 356 + 357 + Additionally, this utility will ensure that the `query` reference will remain stable. This means 358 + that if the same `query` will be passed in as a string or as a fresh `DocumentNode`, then the output 359 + will always have the same `DocumentNode` reference. 360 + 361 + ### makeResult 310 362 311 - This is an `ExchangeIO` function that the `Client` adds on after all 312 - exchanges. This function is responsible from filtering `teardown` operations 313 - out of the output and also warns you of unhandled `operationName`s which 314 - can occur when a subscription is used without adding a `subscriptionExchange`. 363 + This is a helper function that converts a GraphQL API result to an 364 + [`OperationResult`](#operationresult). 365 + 366 + It accepts an [`Operation`](#operation), the API result, and optionally the original `FetchResponse` 367 + for debugging as arguments, in that order. 368 + 369 + ### makeErrorResult 370 + 371 + This is a helper function that creates an [`OperationResult`](#operationresult) for GraphQL API 372 + requests that failed with a generic or network error. 373 + 374 + It accepts an [`Operation`](#operation), the error, and optionally the original `FetchResponse` 375 + for debugging as arguments, in that order. 376 + 377 + ### formatDocument 378 + 379 + This utility is used by the [`cacheExchange`](#cacheexchange) and by 380 + [Graphcache](../graphcache/README.md) to add `__typename` fields to GraphQL `DocumentNode`s. 381 + 382 + ### maskTypename 383 + 384 + This utility accepts a GraphQL `data` object, like `data` on [`OperationResult`s](#operationresult) 385 + and marks every `__typename` property as non-enumerable. 315 386 316 - ### fetchExchange (Exchange) 387 + The [`formatDocument`](#formatdocument) is often used by `urql` automatically and adds `__typename` 388 + fields to all results. However, this means that data can often not be passed back into variables or 389 + inputs on mutations, which is a common use-case. This utility hides these fields which can solves 390 + this problem. 317 391 318 - The `fetchExchange` as [described in the Basics section](https://formidable.com/open-source/urql/docs/basics#fetchexchange). 319 - It's of type `Exchange`. 392 + It's used by the [`Client`](#client) when the `maskTypename` option is enabled. 320 393 321 - ### defaultExchanges (Exchange[]) 394 + ### defaultExchanges 322 395 323 - An array of the default exchanges that the `Client` uses when it wasn't passed 324 - an `exchanges` option. 396 + This is an array of the default `Exchange`s that the `Client` uses when the `exchanges` option isn't 397 + passed. 325 398 326 399 ```js 327 400 const defaultExchanges = [dedupExchange, cacheExchange, fetchExchange]; 328 401 ``` 402 + 403 + ### composeExchanges 404 + 405 + This utility accepts an array of `Exchange`s and composes them into a single one. 406 + It chains them in the order that they're given, left to right. 407 + 408 + ```js 409 + function composeExchanges(Exchange[]): Exchange; 410 + ``` 411 + 412 + This can be used to combine some exchanges and is also used by [`Client`](#client) 413 + to handle the `exchanges` input.
+424
docs/api/graphcache.md
··· 5 5 6 6 # @urql/exchange-graphcache 7 7 8 + The `@urql/exchange-graphcache` package contains an addon `cacheExchange` for `urql` that may be 9 + used to replace the default [`cacheExchange`](./core.md#cacheexchange), which switches `urql` from 10 + using ["Document Caching"](../basics/document-caching.md) to ["Normalized 11 + Caching"](../graphcache/normalized-caching.md). 12 + 13 + [Read more about how to use and configure _Graphcache_ in the "Graphcache" 14 + section](../graphcache/README.md) 15 + 16 + ## cacheExchange 17 + 18 + The `cacheExchange` function, as exported by `@urql/exchange-graphcache`, accepts a single object of 19 + options and returns an [`Exchange`](./core.md#exchange). 20 + 21 + | Input | Description | 22 + | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 23 + | _keys_ | A mapping of key generator functions for types that are used to override the default key generation that _Graphcache_ uses to normalize data for given types. | 24 + | _resolvers_ | A nested mapping of resolvers, which are used to override the record or entity that _Graphcache_ resolves for a given field for a type. | 25 + | _updates_ | A nested mapping of updater functions for mutation and subscription fields, which may be used to add side-effects that update other parts of the cache when the given subscription or mutation field is written to the cache. | 26 + | _optimistic_ | A mapping of mutation fields to resolvers that may be used to provide _Graphcache_ with an optimistic result for a given mutation field that should be applied to the cached data temporarily. | 27 + | _schema_ | A serialized GraphQL schema that is used by _Graphcache_ to resolve partial data, to resolve interfaces and enums, and to provide helpful warnings. | 28 + 29 + ### `keys` option 30 + 31 + This is a mapping of typenames to `KeyGenerator` functions. 32 + 33 + ```ts 34 + interface KeyingConfig { 35 + [typename: string]: (data: Data) => null | string; 36 + } 37 + ``` 38 + 39 + It may be used to alter how _Graphcache_ generates the key it uses for normalization for individual 40 + types. The key generator function may also always return `null` when a type should always be 41 + embedded. 42 + 43 + [Read more about how to set up `keys` in the "Key Generation" section of the "Normalized Caching" 44 + page.](../graphcache/normalized-caching.md#key-generation) 45 + 46 + ### `resolvers` option 47 + 48 + This configuration is a mapping of typenames to field names to `Resolver` functions. 49 + A resolver may be defined to override the entity or record that a given field on a type should 50 + resolve on the cache. 51 + 52 + ```ts 53 + interface ResolverConfig { 54 + [typeName: string]: { 55 + [fieldName: string]: Resolver; 56 + }; 57 + } 58 + ``` 59 + 60 + A `Resolver` receives four arguments when it's called: `parent`, `args`, `cache`, and 61 + `info`. 62 + 63 + | Argument | Type | Description | 64 + | -------- | -------- | ----------------------------------------------------------------------------------------------------------- | 65 + | parent | `Data` | The parent entity that the given field is on. | 66 + | args | `object` | The arguments for the given field the updater is executed on. | 67 + | cache | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 68 + | info | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 69 + 70 + [Read more about how to set up `resolvers` on the "Computed Queries" 71 + page.](../graphcache/computed-queries.md) 72 + 73 + ### `updates` option 74 + 75 + The `updates` configuration is a mapping of `'Mutation' | 'Subscription'` to field names to 76 + `UpdateResolver` functions. An update resolver may be defined to add side-effects that run when a 77 + given mutation field or subscription field is written to the cache. These side-effects are helpful 78 + to update data in the cache that is implicitly changed on the GraphQL API, that _Graphcache_ can't 79 + know about automatically. 80 + 81 + ```ts 82 + interface UpdatesConfig { 83 + Mutation: { 84 + [fieldName: string]: UpdateResolver; 85 + }; 86 + Subscription: { 87 + [fieldName: string]: UpdateResolver; 88 + }; 89 + } 90 + ``` 91 + 92 + An `UpdateResolver` receives four arguments when it's called: `result`, `args`, `cache`, and 93 + `info`. 94 + 95 + | Argument | Type | Description | 96 + | -------- | -------- | ----------------------------------------------------------------------------------------------------------- | 97 + | result | `any` | Always the entire `data` object from the mutation or subscription. | 98 + | args | `object` | The arguments for the given field the updater is executed on. | 99 + | cache | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 100 + | info | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 101 + 102 + [Read more about how to set up `updates` on the "Custom Updates" 103 + page.](../graphcache/custom-updates.md) 104 + 105 + ### `optimistic` option 106 + 107 + The `optimistic` configuration is a mapping of Mutation field names to `OptimisticMutationResolver` 108 + functions, which return optimistic mutation results for given fields. These results are used by 109 + _Graphcache_ to optimistically update the cache data, which provides an immediate and temporary 110 + change to its data before a mutation completes. 111 + 112 + ```ts 113 + interface OptimisticMutationConfig { 114 + [mutationFieldName: string]: OptimisticMutationResolver; 115 + } 116 + ``` 117 + 118 + A `OptimisticMutationResolver` receives three arguments when it's called: `variables`, `cache`, and 119 + `info`. 120 + 121 + | Argument | Type | Description | 122 + | --------- | -------- | ----------------------------------------------------------------------------------------------------------- | 123 + | variables | `object` | The variables that the given mutation received. | 124 + | cache | `Cache` | The cache using which data can be read or written. [See `Cache`.](#cache) | 125 + | info | `Info` | Additional metadata and information about the current operation and the current field. [See `Info`.](#info) | 126 + 127 + [Read more about how to set up `optimistic` on the "Custom Updates" 128 + page.](../graphcache/custom-updates.md) 129 + 130 + ### `schema` option 131 + 132 + The `schema` option may be used to pass a `IntrospectionQuery` data to _Graphcache_, in other words 133 + it's used to provide schema information to it. This schema is then used to resolve and return 134 + partial results when querying, which are results that the cache can partially resolve as long as no 135 + required fields are missing. 136 + 137 + [Read more about how to use the `schema` option on the "Schema Awareness" 138 + page.](../graphcache/schema-awareness.md) 139 + 140 + ## Cache 141 + 142 + An instance of the `Cache` interface is passed to every resolvers and updater function. It may be 143 + used to read cached data or write cached data, which may be used in combination with the 144 + [`cacheExchange` configuration](#cacheexchange) to alter the default behaviour of _Graphcache_. 145 + 146 + ### keyOfEntity 147 + 148 + The `cache.keyOfEntity` method may be called with a partial `Data` object and will return the key 149 + for that object, or `null` if it's not keyable. 150 + 151 + An object may not be keyable if it's missing the `__typename` or `id` (which falls back to `_id`) 152 + fields. This method does take the [`keys` configuration](#keys-option) into account. 153 + 154 + ```js 155 + cache.keyOfEntity({ __typename: 'Todo', id: 1 }); // 'Todo:1' 156 + cache.keyOfEntity({ __typename: 'Query' }); // 'Query' 157 + cache.keyOfEntity({ __typename: 'Unknown' }); // null 158 + ``` 159 + 160 + There's an alternative method, `cache.keyOfField` which generates a key for a given field. This is 161 + only rarely needed but similar to `cache.keyOfEntity`. This method accepts a field name and 162 + optionally a field's arguments. 163 + 164 + ```js 165 + cache.keyOfField('todo'); // 'todo' 166 + cache.keyOfField('todo', { id: 1 }); // 'todo({"id":1})' 167 + ``` 168 + 169 + Internally, these are the keys that records and links are stored on per entity. 170 + 171 + ### resolve 172 + 173 + This method retrieves a value or link for a given field, given a partially keyable `Data` object or 174 + entity, a field name, and optionally the field's arguments. Internally this method accesses the 175 + cache by using `cache.keyOfEntity` and `cache.keyOfField`. 176 + 177 + ```js 178 + // This may resolve a link: 179 + cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }); // 'Todo:1' 180 + 181 + // This may also resolve records / scalar values: 182 + cache.resolve({ __typename: 'Todo', id: 1 }, 'id'); // 1 183 + 184 + // You can also chain multiple calls to `cache.resolve`! 185 + cache.resolve(cache.resolve({ __typename: 'Query' }, 'todo', { id: 1 }), 'id'); // 1 186 + ``` 187 + 188 + As you can see in the last example of this code snippet, the `Data` object can also be replaced by 189 + an entity key, which makes it possible to pass a key from `cache.keyOfEntity` or another call to 190 + `cache.resolve` instead of the partial entity. 191 + 192 + > **Note:** Because `cache.resolve` may return either a scalar value or another entity key, it may 193 + > be dangerous to use in some cases. It's a good idea to make sure first whether the field you're 194 + > reading will be a key or a value. 195 + 196 + There's an alternative method, `cache.resolveFieldByKey` which accepts a field key that may be 197 + generated using `cache.keyOfField`. 198 + 199 + ```js 200 + cache.resolveFieldByKey({ __typename: 'Query' }, cache.keyOfField('todo', { id: 1 })); // 'Todo:1' 201 + ``` 202 + 203 + This specialised method is likely only going to be useful in combination with 204 + [`cache.inspectFields`](#inspectfields). 205 + 206 + ### inspectFields 207 + 208 + The `cache.inspectFields` method may be used to interrogate the cache about all available fields on 209 + a specific entity. It accepts a partial entity or an entity key, like [`cache.resolve`](#resolve)'s 210 + first argument. 211 + 212 + When calling the method this returns an array of `FieldInfo` objects, one per field (including 213 + differing arguments) that is known to the cache. The `FieldInfo` interface has three properties: 214 + `fieldKey`, `fieldName`, and `arguments`: 215 + 216 + | Argument | Type | Description | 217 + | --------- | ---------------- | ------------------------------------------------------------------------------- | 218 + | fieldName | `string` | The field's name (without any arguments, just the name) | 219 + | arguments | `object \| null` | The field's arguments, or `null` if the field doesn't have any arguments | 220 + | fieldKey | `string` | The field's cache key, which is similar to what `cache.keyOfField` would return | 221 + 222 + This works on any given entity. When calling this method the cache works in reverse on its data 223 + structure, by parsing the entity's individual field keys. 224 + 225 + ```js 226 + cache.inspectFields({ __typename: 'Query' }); 227 + 228 + /* 229 + [ 230 + { fieldName: 'todo', arguments: { id: 1 }, fieldKey: 'id({"id":1})' }, 231 + { fieldName: 'todo', arguments: { id: 2 }, fieldKey: 'id({"id":2})' }, 232 + ... 233 + ] 234 + */ 235 + ``` 236 + 237 + ### readFragment 238 + 239 + `cache.readFragment` accepts a GraphQL `DocumentNode` as the first argument and a partial entity or 240 + an entity key as the second, like [`cache.resolve`](#resolve)'s first argument. 241 + 242 + The method will then attempt to read the entity according to the fragment entirely from the cached 243 + data. If any data is uncached and missing it'll return `null`. 244 + 245 + ```js 246 + import gql from 'graphql-tag'; 247 + 248 + cache.readFragment( 249 + gql` 250 + fragment _ on Todo { 251 + id 252 + text 253 + } 254 + `, 255 + { id: 1 } 256 + ); // Data or null 257 + ``` 258 + 259 + Note that the `__typename` may be left out on the partial entity, since the `__typename` is already 260 + present on the fragment itself. 261 + 262 + [Read more about using `readFragment` on the ["Computed Queries" 263 + page.](../graphcache/computed-queries.md#reading-a-fragment) 264 + 265 + ### readQuery 266 + 267 + The `cache.readQuery` method is similar to `cache.readFragment`, but instead of reading a fragment 268 + from cache, it reads an entire query. The only difference between how these two methods are used is 269 + `cache.readQuery`'s input, which is an object instead of two arguments. 270 + 271 + The method accepts a `{ query, variables }` object as the first argument, where `query` may either 272 + be a `DocumentNode` or a `string` and variables may optionally be an object. 273 + 274 + ```js 275 + cache.readQuery({ 276 + query: ` 277 + query ($id: ID!) { 278 + todo(id: $id) { id, text } 279 + } 280 + `, 281 + variables: { 282 + id: 1 283 + } 284 + ); // Data or null 285 + ``` 286 + 287 + [Read more about using `readQuery` on the ["Computed Queries" 288 + page.](../graphcache/computed-queries.md#reading-a-query) 289 + 290 + ### writeFragment 291 + 292 + Corresponding to [`cache.readFragment`](#readfragments), the `cache.writeFragment` method allows 293 + data in the cache to be updated. 294 + 295 + The arguments for `cache.writeFragment` are identical to [`cache.readFragment`](#readfragment), 296 + however the second argument, `data`, should not only contain properties that are necessary to derive 297 + an entity key from the given data, but also the fields that will be written: 298 + 299 + ```js 300 + import gql from 'graphql-tag'; 301 + 302 + cache.writeFragment( 303 + gql` 304 + fragment _ on Todo { 305 + text 306 + } 307 + `, 308 + { id: 1, text: 'New Todo Text' } 309 + ); 310 + ``` 311 + 312 + In the example we can see that the `writeFragment` method returns `undefined`. Furthermore we pass 313 + `id` in our `data` object so that an entity key can be written, but the fragment itself doesn't have 314 + to include these fields. 315 + 316 + [Read more about using `writeFragment` on the ["Custom Updates" 317 + page.](../graphcache/custom-updates.md#cachewritefragment) 318 + 319 + ### updateQuery 320 + 321 + Similarly to [`cache.writeFragment`](#writefragment), there's an analogous method for 322 + [`cache.readQuery`](#readquery) that may be used to update query data. 323 + 324 + The `cache.updateQuery` method accepts the same `{ query, variables }` object input as its first 325 + argument, which is the query we'd like to write to the cache. As a second argument the method 326 + accepts an updater function. This function will be called with the query data that is already in the 327 + cache (which may be `null` if the data is uncached) and must return the new data that should be 328 + written to the cache. 329 + 330 + ```js 331 + const TodoQuery = ` 332 + query ($id: ID!) { 333 + todo(id: $id) { id, text } 334 + } 335 + `; 336 + 337 + cache.updateQuery({ query: TodoQuery, variables: { id: 1 } }, data => { 338 + if (!data) return null; 339 + data.todo.text = 'New Todo Text'; 340 + return data; 341 + }); 342 + ``` 343 + 344 + As we can see, our updater may return `null` to cancel updating any data, which we do in case the 345 + query data is uncached. 346 + 347 + We can also see that data can simply be mutated and doesn't have to be altered immutably. This is 348 + because all data from the cache is already a deep copy and hence we can do to it whatever we want. 349 + 350 + [Read more about using `updateQuery` on the "Custom Updates" 351 + page.](../graphcache/custom-updates.md#cacheupdatequery) 352 + 353 + ### invalidate 354 + 355 + The `cache.invalidate` method can be used to delete (i.e. "evict") an entity from the cache 356 + entirely. This will cause it to disappear from all queries in _Graphcache_. 357 + 358 + Since deleting an entity will lead to some queries containing missing and uncached data, calling 359 + `invalidate` may lead to additional GraphQL requests being sent, unless you're using [_Graphcache_'s 360 + "Schema Awareness" feature](../graphcache/schema-awareness.md), which takes optional fields into 361 + account. 362 + 363 + This method accepts a partial entity or an entity key as its only argument, similar to 364 + [`cache.resolve`](#resolve)'s first argument. 365 + 366 + ```js 367 + cache.invalidate({ __typename: 'Todo', id: 1 }); // Invalidates Todo:1 368 + ``` 369 + 370 + ## Info 371 + 372 + This is a metadata object that is passed to every resolver and updater function. It contains basic 373 + information about the current GraphQL document and query, and also some information on the current 374 + field that a given resolver or updater is called on. 375 + 376 + | Argument | Type | Description | 377 + | -------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | 378 + | parentTypeName | `string` | The field's parent entity's typename | 379 + | parentKey | `string` | The field's parent entity's cache key (if any) | 380 + | parentFieldKey | `string` | The current key's cache key, which is the parent entity's key combined with the current field's key (This is mostly obsolete) | 381 + | fieldName | `string` | The current field's name | 382 + | fragments | `{ [name: string]: FragmentDefinitionNode }` | A dictionary of fragments from the current GraphQL document | 383 + | variables | `object` | The current GraphQL operation's variables (may be an empty object) | 384 + | partial | `?boolean` | This may be set to `true` at any point in time (by your custom resolver or by _Graphcache_) to indicate that some data is uncached and missing | 385 + | optimistic | `?boolean` | This is only `true` when an optimistic mutation update is running | 386 + 387 + > **Note:** Using `info` is regarded as a last resort. Please only use information from it if 388 + > there's no other solution to get to the metadata you need. We don't regard the `Info` API as 389 + > stable and may change it with a simple minor version bump. 390 + 391 + ## The `/extras` import 392 + 393 + The `extras` subpackage is published with _Graphcache_ and contains helpers and utilities that don't 394 + have to be included in every app or aren't needed by all users of _Graphcache_. 395 + All utilities from extras may be imported from `@urql/exchange-graphcache/extras`. 396 + 397 + Currently the `extras` subpackage only contains the [pagination resolvers that have been mentioned 398 + on the "Computed Queries" page.](../graphcache/computed-queries.md#pagination) 399 + 400 + ### simplePagination 401 + 402 + Accepts a single object of optional options and returns a resolver that can be inserted into the 403 + [`cacheExchange`'s](#cacheexchange) [`resolvers` configuration.](#resolvers-option) 404 + 405 + | Argument | Type | Description | 406 + | -------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 407 + | offsetArgument | `?string` | The field arguments's property, as passed to the resolver, that contains the current offset, i.e. the number of items to be skipped. Defaults to `'skip'`. | 408 + | limitArgument | `?string` | The field arguments's property, as passed to the resolver, that contains the current page size limit, i.e. the number of items on each page. Defaults to `'limit'`. | 409 + 410 + Once set up, the resulting resolver is able to automatically concatenate all pages of a given field 411 + automatically. Queries to this resolvers will from then on only return the infinite, combined list 412 + of all pages. 413 + 414 + [Read more about `simplePagination` on the "Computed Queries" 415 + page.](../graphcache/computed-queries.md#simple-pagination) 416 + 417 + ### relayPagination 418 + 419 + Accepts a single object of optional options and returns a resolver that can be inserted into the 420 + [`cacheExchange`'s](#cacheexchange) [`resolvers` configuration.](#resolvers-option) 421 + 422 + | Argument | Type | Description | 423 + | --------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 424 + | mergeMode | `'outwards' \| 'inwards'` | With Relay pagination, pages can be queried forwards and backwards using `after` and `before` cursors. This option defines whether pages that have been quiered backwards should be concatenated before (outwards) or after (inwards) all pages that have been queried forwards. | 425 + 426 + Once set up, the resulting resolver is able to automatically concatenate all pages of a given field 427 + automatically. Queries to this resolvers will from then on only return the infinite, combined list 428 + of all pages. 429 + 430 + [Read more about `relayPagnation` on the "Computed Queries" 431 + page.](../graphcache/computed-queries.md#relay-pagination)
+2 -1
docs/api/preact.md
··· 5 5 6 6 # @urql/preact 7 7 8 - Please refer to [the React API section](/api/urql) for details of the Preact API. 8 + The `@urql/preact` API is the same as the React `urql` API. 9 + Please refer to [the "urql" API docs](./urql.md) for details on the Preact API.
+97 -128
docs/api/urql.md
··· 5 5 6 6 # React API 7 7 8 - ## Hooks 8 + ## useQuery 9 9 10 - ### useQuery 10 + Accepts a single required options object as an input with the following properties: 11 11 12 - #### useQuery Parameters 13 - Accepts a single required `options` object as an input with the following properties: 12 + | Prop | Type | Description | 13 + | ------------- | ------------------------ | -------------------------------------------------------------------------------------------------------- | 14 + | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 15 + | variables | `?object` | The variables to be used with the GraphQL request. | 16 + | requestPolicy | `?RequestPolicy` | An optional [request policy](./core.md#requestpolicy) that should be used specifying the cache strategy. | 17 + | pause | `?boolean` | A boolean flag instructing [execution to be paused](../basics/queries.md#pausing-usequery). | 18 + | pollInterval | `?number` | Every `pollInterval` milliseconds the query will be reexecuted. | 19 + | context | `?object` | Holds the contextual information for the query. | 14 20 15 - | Prop | Type | Description | Required | | 16 - | ------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------- | -------- | 17 - | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 18 - | variables | `object` | The variables to be used with the GraphQL request. | No | 19 - | requestPolicy | `RequestPolicy` | An optional [request policy](/basics/querying-data#request-policy) that should be used specifying the cache strategy. | No | 20 - | pause | `boolean` | A boolean flag instructing `Query` to pause execution of the subsequent query operation. | No | 21 - | pollInterval | `number` | Every `pollInterval` milliseconds the query will be refetched. | No | 22 - | context | `object` | Holds the contextual information for the query. | No | 21 + This hook returns a tuple of the shape `[result, executeQuery]`. 23 22 24 - #### useQuery Returned Data 23 + - The `result` is an object with the shape of an [`OperationResult`](./core.md#operationresult) with 24 + an added `fetching: boolean` property, indicating whether the query is currently being fetched. 25 + - The `executeQuery` function optionally accepts 26 + [`Partial<OperationContext>`](./core.md#operationcontext) and reexecutes the current query when 27 + it's called. When `pause` is set to `true` this executes the query, overriding the otherwise 28 + paused hook. 25 29 26 - A tuple is returned with item one being the current query's state object and item two being an `executeQuery` function. 30 + [Read more about how to use the `useQuery` API on the "Queries" page.](../basics/queries.md) 27 31 28 - The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) 32 + ## useMutation 29 33 30 - &nbsp; 34 + Accepts a single `query` argument of type `string | DocumentNode` and returns a tuple of the shape 35 + `[result, executeMutation]`. 31 36 32 - The `executeQuery` function optionally accepts a partial `OperationContext`. 37 + - The `result` is an object with the shape of an [`OperationResult`](./core.md#operationresult) with 38 + an added `fetching: boolean` property, indicating whether the mutation is currently being executed. 39 + - The `executeMutation` function accepts variables and optionally 40 + [`Partial<OperationContext>`](./core.md#operationcontext) and may be used to start executing a 41 + mutation. It returns a `Promise` resolving to an [`OperationResult`](./core.md#operationresult). 33 42 34 - [More information on how to use this hook can be found in the Basics section.](/basics/querying-data#queries) 43 + [Read more about how to use the `useMutation` API on the "Mutations" page.](../basics/mutations.md) 35 44 36 - ### useMutation 45 + ## useSubscription 37 46 38 - #### useMutation Parameters 47 + Accepts a single required options object as an input with the following properties: 39 48 40 - Accepts a single `query` argument of type `string`. 49 + | Prop | Type | Description | 50 + | --------- | ------------------------ | ------------------------------------------------------------------------------------------- | 51 + | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | 52 + | variables | `?object` | The variables to be used with the GraphQL request. | 53 + | pause | `?boolean` | A boolean flag instructing [execution to be paused](../basics/queries.md#pausing-usequery). | 54 + | context | `?object` | Holds the contextual information for the query. | 41 55 42 - #### useMutation Returned Data 43 - 44 - A tuple is returned with item one being the current query's state object and item two being an `executeQuery` function. 45 - 46 - The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) 47 - &nbsp; 48 - 49 - The `executeQuery` function optionally accepts a partial `OperationContext`. 50 - 51 - [More information on how to use this hook can be found in the Basics section.](/basics/mutating-data#mutations) 52 - 53 - ### useSubscription 54 - 55 - #### useSubscription Parameters 56 - Accepts an `options` object as the required first parameter, and a second optional parameter that is the subscription's handler function. 57 - 58 - The `options` object's property breakdown: 59 - 60 - | Prop | Type | Description | Required | | 61 - | --------- | ------------------------ | ---------------------------------------------------------------------------------- | -------- | 62 - | query | `string \| DocumentNode` | The query to be executed. Accepts as a plain string query or GraphQL DocumentNode. | Yes | 63 - | variables | `object` | The variables to be used with the GraphQL request. | No | 64 - | context | `object` | Holds the contextual information for the query. | No | 65 - 66 - &nbsp; 67 - 68 - The subscription handler's type signature: 56 + The hook optionally accepts a second argument, which may be a handler function with a type signature 57 + of: 69 58 70 59 ```js 71 - type SubscriptionHandler<T, R> = (prev: R | undefined, data: T) => R; 60 + type SubscriptionHandler<T, R> = (previousData: R | undefined, data: T) => R; 72 61 ``` 73 62 74 - This means that the subscription handler receives the previous data or undefined 75 - and the current, incoming subscription event data. 63 + This function will be called with the previous data (or `undefined`) and the new data that's 64 + incoming from a subscription event, and may be used to "reduce" the data over time, altering the 65 + value of `result.data`. 76 66 77 - #### useSubscription Returned Data 67 + This hook returns a tuple of the shape `[result, executeQuery]`. 78 68 79 - The shape of the current state is an [OperationResult Type](/api/core#operationresult-type) without the first `operation` prop. 69 + - The `result` is an object with the shape of an [`OperationResult`](./core.md#operationresult). 70 + - The `executeSubscription` function optionally accepts 71 + [`Partial<OperationContext>`](./core.md#operationcontext) and restarts the current subscription when 72 + it's called. When `pause` is set to `true` this starts the subscription, overriding the otherwise 73 + paused hook. 80 74 81 - More information can be found in the [Subscriptions](/advanced/subscriptions) section. 75 + Since a subscription may proactively closed by the server, the additional `fetching: boolean` 76 + property on the `result` may update to `false` when the server ends the subscription. 77 + By default `urql` is not able to start subscriptions, since this requires some additional setup. 82 78 83 - ## Components 79 + [Read more about how to use the `useSubscription` API on the "Subscriptions" 80 + page.](../advanced/subscriptions.md) 84 81 85 - ### Query 82 + ## Query Component 86 83 87 - #### Props 84 + This component is a wrapper around [`useQuery`](#usequery), exposing a [render prop 85 + API](https://reactjs.org/docs/render-props.html) for cases where hooks aren't desirable. 88 86 89 - | Prop | Type | Description | Required | 90 - | ------------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | -------- | 91 - | query | `string` | The GraphQL request's query | Yes | 92 - | variables | `object` | The GraphQL request's variables | Yes | 93 - | context | `?object` | The GraphQL request's context | No | 94 - | requestPolicy | `?RequestPolicy` | An optional request policy that should be used | No | 95 - | pause | `?boolean` | A boolean flag instructing `Query` to pause execution of the subsequent query operation | No | 96 - | pollInterval | `?number` | Every `pollInterval` milliseconds the query will be refetched | No | 97 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | N/A | 98 - 99 - #### Render Props 100 - 101 - | Prop | Type | Description | 102 - | ------------ | ----------------------------------- | ------------------------------------------------------------------------------------------------------- | 103 - | fetching | `boolean` | Whether the `Query` is currently waiting for a GraphQL result | 104 - | data | `?any` | The GraphQL request's result | 105 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 106 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 107 - | executeQuery | `Partial<OperationContext> => void` | A function that can force the operation to be sent again with the given context (Useful for refetching) | 108 - 109 - &nbsp; 110 - 111 - [More information on how to use this hook can be found in the Basics section.](/basics/querying-data#queries) 87 + The API of the `Query` component mirrors the API of [`useQuery`](#usequery). The props that `<Query>` 88 + accepts are the same as `useQuery`'s options object. 112 89 113 - ### Mutation 114 - 115 - #### Props 90 + A function callback must be passed to `children` that receives the query result and must return a 91 + React element. The second argument of the hook's tuple, `executeQuery` is passed as an added property 92 + on the query result. 116 93 117 - | Prop | Type | Description | 118 - | -------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | 119 - | query | `string` | The GraphQL request's query | 120 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 94 + ## Mutation Component 121 95 122 - #### Render Props 96 + This component is a wrapper around [`useMutation`](#usemutation), exposing a [render prop 97 + API](https://reactjs.org/docs/render-props.html) for cases where hooks aren't desirable. 123 98 124 - | Prop | Type | Description | 125 - | --------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------- | 126 - | fetching | `boolean` | Whether the `Mutation` is currently waiting for a GraphQL result | 127 - | data | `?any` | The GraphQL request's result | 128 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 129 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 130 - | executeMutation | `(variables: object, context?: Partial<OperationContext>) => void` | A function that accepts variables and starts the mutation | 99 + The `Mutation` component accepts a `query` prop and a function callback must be passed to `children` 100 + that receives the mutation result and must return a React element. The second argument of 101 + `useMutation`'s returned tuple, `executeMutation` is passed as an added property on the mutation 102 + result object. 131 103 132 - [More information on how to use this hook can be found in the Basics section.](/basics/mutating-data#mutations) 104 + ## Subscription Component 133 105 134 - ### Subscription 106 + This component is a wrapper around [`useSubscription`](#usesubscription), exposing a [render prop 107 + API](https://reactjs.org/docs/render-props.html) for cases where hooks aren't desirable. 135 108 136 - [More information on how to use this component can be found in the Basics section.](https://formidable.com/open-source/urql/docs/basics#subscriptions) 109 + The API of the `Subscription` component mirrors the API of [`useSubscription`](#usesubscription). 110 + The props that `<Mutation>` accepts are the same as `useSubscription`'s options object, with an 111 + added, optional `handler` prop that may be passed, which for the `useSubscription` hook is instead 112 + the second argument. 137 113 138 - #### Props 114 + A function callback must be passed to `children` that receives the subscription result and must 115 + return a React element. The second argument of the hook's tuple, `executeSubscription` is passed as 116 + an added property on the subscription result. 139 117 140 - | Prop | Type | Description | 141 - | --------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | 142 - | query | `string` | The GraphQL subscription's query | 143 - | variables | `object` | The GraphQL subscriptions' variables | 144 - | context | `?Partial<OperationContext>` | The GraphQL subscriptions' context | 145 - | handler | `undefined \| (prev: R \| undefined, data: T) => R` | The handler that should combine/update the subscription's data with incoming data | 146 - | children | `RenderProps => ReactNode` | A function that follows the typical render props pattern. The shape of the render props is as follows | 118 + ## Context 147 119 148 - #### Render Props 120 + `urql` is used in React by adding a provider around where the [`Client`](./core.md#client) is 121 + supposed to be used. Internally this means that `urql` creates a 122 + [React Context](https://reactjs.org/docs/context.html). 149 123 150 - | Prop | Type | Description | 151 - | ---------- | ---------------------- | --------------------------------------------------------------- | 152 - | fetching | `boolean` | Whether the `Subscription` is currently ongoing | 153 - | data | `?any` | The GraphQL subscription's data | 154 - | error | `?CombinedError` | The `CombinedError` containing any errors that might've occured | 155 - | extensions | `?Record<string, any>` | Optional extensions that the GraphQL server may have returned. | 124 + All created parts of this context are exported by `urql`, namely: 156 125 157 - More information can be found in the [Subscriptions](/advanced/subscriptions) section. 126 + - `Context` 127 + - `Provider` 128 + - `Consumer` 158 129 159 - ### Context 130 + To keep examples brief, `urql` creates a default client with the `url` set to `'/graphql'`. This 131 + client will be used when no `Provider` wraps any of `urql`'s hooks. However, to prevent this default 132 + client from being used accidentally, a warning is output in the console for the default client. 160 133 161 - `urql` comes with the two context components `Consumer` and `Provider` as returned 162 - by React's `createContext` utility. It also exports the `Context` itself which can 163 - be used in combination with the `useContext` hook. 134 + ### useClient 164 135 165 - E.g. 136 + `urql` also exports a `useClient` hook, which is a convenience wrapper like the following: 166 137 167 138 ```js 168 - <App> 169 - <UrqlProvider> 170 - <UrqlConsumer> 171 - {urqlData => ( 172 - <MyComponent data={urqlData} /> 173 - )} 174 - </UrqlConsumer> 175 - </UrqlProvider> 176 - </App> 139 + import React from 'react'; 140 + import { Context } from 'urql'; 141 + 142 + const useClient = () => React.useContext(Context); 177 143 ``` 144 + 145 + However, this hook is also responsible for outputting the default client warning that's mentioned 146 + above, and should thus be preferred over manually using `useContext` with `urql`'s `Context`.
docs/assets/logos/egghead.png

This is a binary file and will not be displayed.

docs/assets/logos/github.png

This is a binary file and will not be displayed.

docs/assets/logos/tripadvisor.png

This is a binary file and will not be displayed.

+12 -4
docs/basics/README.md
··· 5 5 6 6 # Basics 7 7 8 - In this chapter we'll explain the basics of `urql`, you'll learn 9 - [how to set up your client](./setting-up-the-client.md). You'll 10 - see how you [query data](./querying-data.md) and find out how we 11 - can alter that data by [mutating it](./mutating-data.md). 8 + In this chapter we'll explain the basics of `urql` and how to get started with using it without any 9 + prior knowledge. It's split into multiple pages that are best read in order. 12 10 11 + - **Getting started** covers how to install the package and set it up. 12 + - **Queries** explains how to write your first GraphQL queries with `urql`. 13 + - **Mutations** follows up with how to define mutations. 14 + - **Document Caching** explains the default cache mechanism of `urql`, as opposed to the [Normalized 15 + Cache](../graphcache/normalized-caching.md) which is not the default. 16 + - **Errors** contains information on error handling in `urql`. 17 + 18 + After reading "Basics" you may want to [read the "Concepts" chapter of the 19 + documentation](../concepts/README.md) as it explains the motivation and architecture that drives 20 + `urql`.
+11
docs/basics/document-caching.md
··· 34 34 This is an aggressive form of cache invalidation. However, it works well for content-driven sites, 35 35 although it doesn't deal with normalized data or IDs. 36 36 37 + ## Request Policies 38 + 39 + [We previously covered request policies on the "Queries" page.](./queries.md) 40 + 41 + The _request policy_ that is defined will alter what the default document cache does. By default the 42 + cache will prefer cached results and will otherwise send a request, which is called `cache-first`, 43 + but there's also `cache-and-network`, `cache-only`, and `network-only`. 44 + 45 + [Read more about which request policies are available in the API 46 + docs.](../api/core.md#requestpolicy-type) 47 + 37 48 ## Document Cache Gotchas 38 49 39 50 This cache has a small trade-off! If we request a list of data and the API returns an empty list,
+29
docs/basics/errors.md
··· 1 + --- 2 + title: Errors 3 + order: 4 4 + --- 5 + 6 + # Error handling 7 + 8 + When we use a GraphQL API there are two kinds of errors we may encounter: Network Errors and GraphQL 9 + Errors from the API. Since it's common to encounter either of them in development, there's a 10 + [`CombinedError`](../api/core.md#combinederror-class) class that can hold and abstract either. 11 + 12 + We may encounter a `CombinedError` when using `urql` wherever an `error` may be returned, typically 13 + in results from the API. The `CombinedError` can have one of two properties that describe what went 14 + wrong. 15 + 16 + - The `networkError` property will contain any error that stopped `urql` from making a network 17 + request. 18 + - The `graphQLErrors` property may be an array that contains [normalized `GraphQLError`s as they 19 + were returned in the `errors` array from a GraphQL API.](https://graphql.org/graphql-js/error/) 20 + 21 + Additionally, the `message` of the error will be generated and combined from the errors for 22 + debugging purposes. 23 + 24 + ![Combined errors](../assets/urql-combined-error.png) 25 + 26 + It's worth noting that an `error` can coexit and be returned in a successful request alongside 27 + `data`. This is because in GraphQL a query can have partially failed but still contain some data. 28 + In that case `CombinedError` will be passed to us with `graphQLErrors`, while `data` may still be 29 + set.
+97
docs/basics/getting-started.md
··· 1 + --- 2 + title: Getting started 3 + order: 0 4 + --- 5 + 6 + # Getting started 7 + 8 + In the ["Introduction"](./README.md) we read how `urql` consists of multiple constituent parts that 9 + make up a GraphQL client. Hence there are multiple packages — one for each framework we officially 10 + support — that you'll get started with. 11 + 12 + ## React & Preact 13 + 14 + This "Getting Started" guide covers how to install and set up `urql` and the Client, for React and 15 + Preact. Since the `urql` and `@urql/preact` packages share most of their API one-to-one, when 16 + reading the documentation on React, all examples are essentially the same, except that we'll use the 17 + `@urql/preact` package instead of the `urql` package for Preact. 18 + 19 + ### Installation 20 + 21 + Installing `urql` is as quick as you'd expect and you won't need any other packages to get started 22 + with at first. We'll install the package with our package manager of choice. 23 + 24 + ```sh 25 + yarn add urql graphql 26 + # or 27 + npm install --save urql graphql 28 + ``` 29 + 30 + To use urql with Preact, we have to install `@urql/preact` instead of `urql` and import from 31 + that package instead. Otherwise all examples for Preact will be the same. 32 + 33 + Most libraries related to GraphQL also need the `graphql` package to be installed as a peer 34 + dependency, so that they can adapt to your specific versioning requirements, which is why we'll need 35 + to install `graphql` as well. 36 + 37 + Both the `urql` packages and `graphql` follow [semantic versioning](https://semver.org) and `urql` 38 + packages will define a range of compatible versions of `graphql`. Watch out for breaking changes in 39 + the future however, in which case your package manager may warn you about `graphql` being out of the 40 + defined peer dependency range. 41 + 42 + ### Setting up the `Client` 43 + 44 + The `urql` and `@urql/preact` packages export a method called `createClient` which we can use to 45 + create the GraphQL client. This central `Client` manages all of our GraphQL requests and results. 46 + 47 + ```js 48 + import { createClient } from 'urql'; 49 + 50 + const client = createClient({ 51 + url: 'http://localhost:3000/graphql', 52 + }); 53 + ``` 54 + 55 + At the bare minimum we'll need to pass an API's `url` when we create a `Client` to get started. 56 + 57 + Another common option is `fetchOptions`. This option allows us to customize the options that will be 58 + passed to `fetch` when a request is sent to the given API `url`. We may pass in an object or a 59 + function returning an object to this option. 60 + 61 + In the following example we'll add a token to each `fetch` request that our `Client` sends to our 62 + GraphQL API. 63 + 64 + ```js 65 + const client = createClient({ 66 + url: 'http://localhost:3000/graphql', 67 + fetchOptions: () => { 68 + const token = getToken(); 69 + return { 70 + headers: { authorization: token ? `Bearer ${token}` : '' }, 71 + }; 72 + }, 73 + }); 74 + ``` 75 + 76 + ### Providing the `Client` 77 + 78 + To make use of the `Client` in React & Preact we will have to provide it via the 79 + [Context API](https://reactjs.org/docs/context.html). This may be done with the help of 80 + the `Provider` export. 81 + 82 + ```jsx 83 + import { createClient, Provider } from 'urql'; 84 + 85 + const client = createClient({ 86 + url: 'http://localhost:3000/graphql', 87 + }); 88 + 89 + const App = () => ( 90 + <Provider value={client}> 91 + <YourRoutes /> 92 + </Provider> 93 + ); 94 + ``` 95 + 96 + Now every component and element inside and under the `Provider` are able to use GraphQL queries that 97 + will be sent to our API.
-61
docs/basics/mutating-data.md
··· 1 - --- 2 - title: Mutations 3 - order: 2 4 - --- 5 - 6 - # Mutations 7 - 8 - Now that we know how to query our data we'll also need to know 9 - how to mutate that data. 10 - We'll see how we can dispatch mutations to our back-end and view 11 - the result of these mutations. 12 - 13 - ## React/Preact 14 - 15 - `urql` exposes the `useMutation` hook and the `Mutation` component to send out mutations. 16 - 17 - ### Sending a mutation 18 - 19 - Let's set up a mutation allowing us to change the name of our todo. 20 - 21 - ```jsx 22 - const Todo = ({ id, title }) => { 23 - const [updateTodoResult, updateTodo] = useMutation(` 24 - mutation ($id: ID!, $title: String!) { 25 - updateTodo (id: $id, title: $title) { 26 - id 27 - } 28 - } 29 - `); 30 - } 31 - ``` 32 - 33 - Similar to the `useQuery` output, `useMutation` returns a tuple. The first item in the tuple being our `result` 34 - containing: `fetching`, `error`, and `data`. At this point in time, no mutation has been performed. 35 - To mutate the data we first have to invoke the second item in the tuple - the function here named `updateTodo`. 36 - 37 - ### Using the mutation result 38 - 39 - When calling this `updateTodo` function we have two ways of getting the response from the server, 40 - we can get it from `updateTodoResult` or we can await the promise returned from our mutation trigger function. 41 - 42 - ```jsx 43 - const Todo = ({ id, title }) => { 44 - const [updateTodoResult, updateTodo] = useMutation(` 45 - mutation ($id: ID!, $title: String!) { 46 - updateTodo (id: $id, title: $title) { 47 - id 48 - } 49 - } 50 - `); 51 - 52 - const submit = (newTitle) => { 53 - updateTodo({ variables: { id, title: newTitle } }).then((data) => { 54 - // this data variable will be the same as updateTodoResult.data 55 - }); 56 - } 57 - } 58 - ``` 59 - 60 - This means that we can react to a completed todo in the body of the `.then` or 61 - with a `useEffect`.
+101
docs/basics/mutations.md
··· 1 + --- 2 + title: Mutations 3 + order: 2 4 + --- 5 + 6 + # Mutations 7 + 8 + In this chapter we'll learn how to execute mutations and view their results. 9 + Sending mutations to our GraphQL API is similar to what we've learned about sending queries to our 10 + API [previously on the "Queries" page.](./queries.md) 11 + 12 + ## React & Preact 13 + 14 + This guide covers how to query data with React and Preact, which share almost the same API. 15 + 16 + Both libraries offer a `useMutation` hook and a `Mutation` component. The latter accepts the same 17 + parameters but we won't cover it in this guide. [Look it up in the API docs if you prefer 18 + render-props components.](../api/urql.md#components) 19 + 20 + ### Sending a mutation 21 + 22 + Let's pick up an example in an imaginary todo items GraphQL API and dive into an example! 23 + We'll set up a mutation that _updates_ a todo item. 24 + 25 + ```jsx 26 + const UpdateTodo = ` 27 + mutation ($id: ID!, $title: String!) { 28 + updateTodo (id: $id, title: $title) { 29 + id 30 + } 31 + } 32 + `; 33 + 34 + const Todo = ({ id, title }) => { 35 + const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 36 + }; 37 + ``` 38 + 39 + Similar to the `useQuery` output, `useMutation` returns a tuple. The first item in the tuple again 40 + contains `fetching`, `error`, and `data` — it's identical since this is a common pattern of how 41 + `urql` presents _operation results_. 42 + 43 + Unlike the `useQuery` hook, the `useMutation` hook doesn't execute automatically. At this point in 44 + our example, no mutation will be performed. To execute our mutation we instead have to call the 45 + execute function — `updateTodo` in our example — which is the second item in the tuple. 46 + 47 + ### Using the mutation result 48 + 49 + When calling our `updateTodo` function we have two ways of getting to the result as it comes back 50 + from our API. We can either use the first value of the returned tuple — our `updateTodoResult` — or 51 + we can use the promise that `updateTodo` returns. 52 + 53 + ```jsx 54 + const Todo = ({ id, title }) => { 55 + const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 56 + 57 + const submit = newTitle => { 58 + const variables = { id, title: newTitle || '' }; 59 + updateTodo(variables).then(result => { 60 + // The result is almost identical to `updateTodoResult` with the exception 61 + // of `result.fetching` not being set. 62 + }); 63 + }; 64 + }; 65 + ``` 66 + 67 + This is useful when your UI has to display progress or results on the mutation, and the returned 68 + promise is particularly useful when you're adding side-effects that run after the mutation has 69 + completed. 70 + 71 + ### Handling mutation errors 72 + 73 + It's worth noting that the promise we receive when calling the execute function will never 74 + reject. Instead it will always return a promise that resolves to a result. 75 + 76 + If you're checking for errors, you should use `result.error` instead, which will be set 77 + to a `CombinedError` when any kind of errors occurred while executing your mutation. 78 + [Read more about errors on our "Errors" page.](./errors.md) 79 + 80 + ```jsx 81 + const Todo = ({ id, title }) => { 82 + const [updateTodoResult, updateTodo] = useMutation(UpdateTodo); 83 + 84 + const submit = newTitle => { 85 + const variables = { id, title: newTitle || '' }; 86 + updateTodo(variables).then(result => { 87 + if (result.error) { 88 + console.error('Oh no!', result.error); 89 + } 90 + }); 91 + }; 92 + }; 93 + ``` 94 + 95 + ### Reading on 96 + 97 + There are some more tricks we can use with `useMutation`. [Read more about its API in the API docs for 98 + it.](../api/urql.md#usemutation) 99 + 100 + [On the next page we'll learn about "Document Caching", `urql`'s default caching 101 + mechanism.](./document-caching.md)
+189
docs/basics/queries.md
··· 1 + --- 2 + title: Queries 3 + order: 1 4 + --- 5 + 6 + # Queries 7 + 8 + Let's get to querying our data! This page will teach us how we can retrieve our data declaratively 9 + from our GraphQL API with the help of `urql`. 10 + 11 + ## React & Preact 12 + 13 + This guide covers how to query data with React and Preact, which share almost the same API. 14 + 15 + Both libraries offer a `useQuery` hook and a `Query` component. The latter accepts the same 16 + parameters but we won't cover it in this guide. [Look it up in the API docs if you prefer 17 + render-props components.](../api/urql.md#components) 18 + 19 + ### Run a first query 20 + 21 + For the following examples imagine we are querying data from a GraphQL API that contains todo items 22 + Let's dive right into an example! 23 + 24 + ```jsx 25 + import { useQuery } from 'urql'; 26 + 27 + const TodosQuery = ` 28 + query { 29 + todos { 30 + id 31 + title 32 + } 33 + } 34 + `; 35 + 36 + const Todos = () => { 37 + const [result, reexecuteQuery] = useQuery({ 38 + query: TodosQuery, 39 + }); 40 + 41 + const { data, fetching, error } = result; 42 + 43 + if (fetching) return <p>Loading...</p>; 44 + if (error) return <p>Oh no... {error.message}</p>; 45 + 46 + return ( 47 + <ul> 48 + {data.todos.map(todo => ( 49 + <li key={todo.id}>{todo.title}</li> 50 + ))} 51 + </ul> 52 + ); 53 + }; 54 + ``` 55 + 56 + Here we have implemented our first GraphQL query to fetch todos. We see that using `useQuery` 57 + accepts options — in this case we've set `query` to our GraphQL query — and returns a tuple — an 58 + array that contains a result and a reexecute function. 59 + 60 + The result contains several properties. The `fetching` field indicates whether we're currently loading 61 + data, `data` contains the actual `data` from the API's result, and `error` is set when either the 62 + request to the API has failed or when our result contained some `GraphQLError`s, which 63 + we'll get into later on the ["Errors" page](./errors.md). 64 + 65 + ### Variables 66 + 67 + Typically we'll also need to pass variables to our queries, for instance What if we are dealing with 68 + pagination? For this purpose the `useQuery` hook also accepts a `variables` option, which we can use 69 + to supply variables to our query. 70 + 71 + ```jsx 72 + const TodosListQuery = ` 73 + query ($from: Int!, $limit: Int!) { 74 + todos (from: $from, limit: $limit) { 75 + id 76 + title 77 + } 78 + } 79 + `; 80 + 81 + const Todos = ({ from, limit }) => { 82 + const [result, reexecuteQuery] = useQuery({ 83 + query: TodosListQuery, 84 + variables: { from, limit }, 85 + }); 86 + 87 + // ... 88 + }; 89 + ``` 90 + 91 + As when we're sending GraphQL queries manually using `fetch`, the variables will be attached to the 92 + `POST` request that is sent to our GraphQL API. 93 + 94 + Whenever the `variables` (or the `query`) option on 95 + the `useQuery` hook changes `fetching` will return to being `true` and a new request will be sent to 96 + our API, unless a result has already been cached previously. 97 + 98 + ### Pausing `useQuery` 99 + 100 + In some cases we may want `useQuery` to execute a query when a precondition has been met. For 101 + instance, we may be building a form and want validation to only take place when a field has been 102 + filled out. 103 + 104 + In the previous example we've defined a query with mandatory arguments. The `$from` and `$limit` 105 + variables have been defined to be non-nullable `Int!` values. 106 + 107 + Let's pause the query we've just 108 + written to not execute when these variables are empty, to prevent `null` variables from being 109 + executed. We can do this by means of the `pause` option. 110 + 111 + ```jsx 112 + const Todos = ({ from, limit }) => { 113 + const [result, reexecuteQuery] = useQuery({ 114 + query: TodosListQuery, 115 + variables: { from, limit }, 116 + pause: !from || !limit, 117 + }); 118 + 119 + // ... 120 + }; 121 + ``` 122 + 123 + Now whenever the mandatory `$from` or `$limit` variables aren't supplied the query won't be executed. 124 + This also means that `result.data` won't change, which means we'll still have access to our old data 125 + even though the variables may have changed. 126 + 127 + ### Request Policies 128 + 129 + As has become clear in the previous sections of this page, the `useQuery` hook accepts more options 130 + than just `query` and `variables`. Another option we should touch on is `requestPolicy`. 131 + 132 + The `requestPolicy` option determines how results are retrieved from our `Client`'s cache. By 133 + default this is set to `cache-first`, which means that we prefer to get results from our cache, but 134 + are falling back to sending an API request. 135 + 136 + In total there are four different policies that we can use: 137 + 138 + - `cache-first` (the default) prefers cached results and falls back to sending an API request when 139 + no prior result is cached. 140 + - `cache-and-network` returns cached results but also always sends an API request, which is perfect 141 + for displaying data quickly while keeping it up-to-date. 142 + - `network-only` will always send an API request and will ignore cached results. 143 + - `cache-only` will always return cached results or `null`. 144 + 145 + The `cache-and-network` policy is particularly useful, since it allows us to display data instantly 146 + if it has been cached, but also refreshes data in our cache in the background. This means though 147 + that `fetching` will be `false` for cached results although an API request may still be ongoing in 148 + the background. 149 + 150 + For this reason there's another field on results, `result.stale`, which indicates that the cached 151 + result is either outdated or that another request is being sent in the background. 152 + 153 + ### Reexecuting Queries 154 + 155 + The `useQuery` hook updates and executes queries whenever its inputs, like the `query` or 156 + `variables` change, but in some cases we may find that we need to programmatically trigger a new 157 + query. This is the purpose of the `reexecuteQuery` function which is the second item in the tuple 158 + that `useQuery` returns. 159 + 160 + Triggering a query programmatically may be useful in a couple of cases. It can for instance be used 161 + to refresh data that is currently being displayed. In these cases we may also override the 162 + `requestPolicy` of our query just once and set it to `network-only` to skip the cache. 163 + 164 + ```jsx 165 + const Todos = ({ from, limit }) => { 166 + const [result, reexecuteQuery] = useQuery({ 167 + query: TodosListQuery, 168 + variables: { from, limit }, 169 + }); 170 + 171 + const refresh = () => { 172 + // Refetch the query and skip the cache 173 + reexecuteQuery({ requestPolicy: 'network-only' }); 174 + }; 175 + }; 176 + ``` 177 + 178 + Calling `refresh` in the above example will execute the query again forcefully, and will skip the 179 + cache, since we're passing `requestPolicy: 'network-only'`. 180 + 181 + Furthermore the `reexecuteQuery` function can also be used to programmatically start a query even 182 + when `pause` is set to `true`, which would usually stop all automatic queries. 183 + 184 + ### Reading on 185 + 186 + There are some more tricks we can use with `useQuery`. [Read more about its API in the API docs for 187 + it.](../api/urql.md#usequery) 188 + 189 + [On the next page we'll learn about "Mutations" rather than Queries.](./mutations.md)
-107
docs/basics/querying-data.md
··· 1 - --- 2 - title: Queries 3 - order: 1 4 - --- 5 - 6 - # Queries 7 - 8 - Let's get to querying our data! This section will teach us how we can 9 - retrieve our data from the server with the help of `urql`. 10 - 11 - ## React/Preact 12 - 13 - Let's get to querying our first piece of data, we offer both a 14 - `render-props` component named `Query` and a hook named `useQuery` as 15 - a means to query data. 16 - 17 - The examples will show the hooks-version but it will be the same for the component. 18 - 19 - ### Run your first query 20 - 21 - For the following examples imagine we are querying a server offering us todo's, let's 22 - dive right into it! 23 - 24 - ```jsx 25 - const Todos = () => { 26 - const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 27 - query: ` 28 - query { 29 - todos { 30 - id 31 - title 32 - } 33 - } 34 - `, 35 - }); 36 - 37 - if (fetching) return <p>Loading...</p> 38 - if (error) return <p>Oh no... {error.message}</p> 39 - 40 - return ( 41 - <ul> 42 - {data.todos.map(todo => ( 43 - <li key={todo.id}> 44 - {todo.title} 45 - </li> 46 - ))} 47 - </ul> 48 - ); 49 - } 50 - ``` 51 - 52 - ### Variables 53 - 54 - We have fetched our first set of todos. We can see the `useQuery` hook returns a tuple, 55 - the first being the result indicating whether it's fetching, it has errored and the result. 56 - The second can be used to refetch the query forcefully. 57 - 58 - What if we are dealing with pagination? We'd need a way to pass that right? 59 - We have the `variables` property to supply the variables to our query. 60 - 61 - ```jsx 62 - const Todos = ({ from, limit }) => { 63 - const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 64 - query: ` 65 - query ($from: Int!, $limit: Int!) { 66 - todos (from: $from, limit: $limit) { 67 - id 68 - title 69 - } 70 - } 71 - `, 72 - variables: { from, limit }, 73 - }); 74 - ... 75 - } 76 - ``` 77 - 78 - ### Skipping queries 79 - 80 - As you can see we are enforcing `from` and `limit` as mandatory (notice the "!" after the `Int` type) 81 - this means that if we don't supply them this query will fail, we need some way of pausing this query 82 - when we don't have these. We can do exactly this by means of the `skip` property. 83 - 84 - ```jsx 85 - const Todos = ({ from, limit }) => { 86 - const [{ data, fetching, error }, reexecuteFetch] = useQuery({ 87 - ... 88 - skip: (!from || !limit) 89 - }); 90 - ... 91 - } 92 - ``` 93 - 94 - Now whenever one of these two mandatory variables isn't supplied the query won't be executed. 95 - 96 - ### Request policy 97 - 98 - We're almost there, there's one last thing we should touch on and that's the `requestPolicy`, 99 - this property tells the client how you want to get the result for your query, there are four values: 100 - 101 - - `cache-first` (default), this means we want to first look in our cache and see if the result is there, if not 102 - we fetch it and update our cache with the result. 103 - - `cache-and-network`, here we'll go to the cache and see if there's a result if there's not we fetch it, if there 104 - is a result we return it to the `query` and dispatch another operation to refresh this data. 105 - - `network-only`, this policy bypasses the cache and will just query your server. 106 - - `cache-only`, here we'll look for your data in the cache, if it's there you'll get the data returned, if not 107 - there will be a `null` return.
-81
docs/basics/setting-up-the-client.md
··· 1 - --- 2 - title: Getting started 3 - order: 0 4 - --- 5 - 6 - # Getting started 7 - 8 - ## React/Preact 9 - 10 - ### Installation 11 - 12 - Installing `urql` is as quick as you'd expect. Firstly, install it 13 - with your package manager of choice, Note: this installation is specific for React: 14 - 15 - ```sh 16 - yarn add urql graphql 17 - # or 18 - npm install --save urql graphql 19 - ``` 20 - 21 - To use urql with Preact, you have to install `@urql/preact` instead of urql and import from 22 - that package instead. 23 - 24 - > _Note:_ Most libraries related to GraphQL specify `graphql` as their peer 25 - > dependency so that they can adapt to your specific versioning 26 - > requirements. 27 - > The library is updated frequently and remains very backwards compatible, 28 - > but make sure it will work with other GraphQL tooling you might have installed. 29 - 30 - ### Setting up the client 31 - 32 - The package will export a method called `createClient` we can use this to create the 33 - client that will be used to dispatch our queries, mutations, etc. 34 - ```js 35 - import { createClient } from 'urql'; 36 - 37 - const client = createClient({ 38 - url: 'http://localhost:3000/graphql', 39 - }); 40 - ``` 41 - 42 - This is the bare minimum you need to get started with your client. 43 - 44 - One option you will most likely need in most applications is the `fetchOptions`, 45 - this option allows you to customize the `fetch` request sent to the given url. 46 - 47 - This is a function or an object, in the following example we tell our client a token 48 - should be added whenever it's present. 49 - 50 - ```js 51 - const client = createClient({ 52 - url: 'http://localhost:3000/graphql', 53 - fetchOptions: () => { 54 - const token = getToken(); 55 - return { 56 - headers: { authorization: token ? `Bearer ${token}` : '' }, 57 - }; 58 - }, 59 - }); 60 - ``` 61 - 62 - ### Providing the client 63 - 64 - To make use of this client in (P)React we will have to provide the client through 65 - the context API. This is done with the help of the `Provider` export. 66 - 67 - ```jsx 68 - import { createClient, Provider } from 'urql'; 69 - 70 - const client = createClient({ 71 - url: 'http://localhost:3000/graphql', 72 - }); 73 - 74 - const App = () => ( 75 - <Provider value={client}> 76 - <YourRoutes /> 77 - </Provider> 78 - ); 79 - ``` 80 - 81 - Now all the children of `<App />` will have access to the client we declared.
-6
docs/common-questions.md
··· 1 - --- 2 - title: Common Questions 3 - order: 6 4 - --- 5 - 6 - # Common Questions
+20
docs/concepts/README.md
··· 4 4 --- 5 5 6 6 # Main Concepts 7 + 8 + In this chapter we'll learn about the motivation behind `urql`, the architecture that drives it, the 9 + inner workings of the `Client`, and how to write extensions and addons, also known as _Exchanges_. 10 + 11 + Each page goes a little further in explaning a core concept of `urql`. 12 + 13 + - **Philosophy** gives a quick overview of the different aspects of GraphQL clients and `urql` in 14 + particular, which shines a light on why you may want to use `urql`. 15 + - **Stream Pattern** explains the inner working of `urql`, which is _stream-based_, also known as 16 + Observable patterns in JS. 17 + - **Core Package** defines why a shared package exists that contains the main logic of `urql`, and 18 + how we can use it directly in Node.js. 19 + - **Exchanges** finally introduces _Exchanges_ and how to write extensions or addons and use them 20 + in `urql`. 21 + 22 + Finally, some _Exchanges_ are covered in different sections of the documentation, like 23 + ["Subscriptions"](../advanced/subscriptions.md), ["Server-side 24 + Rendering"](../advanced/server-side-rendering.md), or ["Normalized 25 + Caching"](../graphcache/normalized-caching.md). It's advisable to read this chapter before moving on 26 + to using _Exchanges_.
+30 -34
docs/concepts/core-package.md
··· 5 5 6 6 # The Core Package 7 7 8 - Previously, [the "Philosophy" page](./philosophy.md) explained how `urql` solves different aspects 8 + The `@urql/core` package contains `urql`'s `Client`, some common utilities, and some default 9 + _Exchanges_. These are the shared, default parts of `urql` that we will be using no matter which 10 + framework we're interacting with. 11 + 12 + Therefore those are also the parts of `urql` that contain its most important logic — like the 13 + [`Client`](../api/core.md#client) — and the package that we need to know about if we're either integrating `urql` with a new 14 + framework, or if we're using the "raw" `Client` in Node.js. 15 + 16 + ## Background 17 + 18 + The ["Philosophy"](./philosophy.md) page explains how `urql` solves some of problems encountered when different aspects 9 19 of having a GraphQL client handle declarative querying and being a central point of extensibiliy. 10 20 11 21 By extension there are three parts of `urql` you'll come in contact with when you add it to your 12 22 app: 13 23 14 - <!-- TODO: Add more package links --> 15 - 16 24 - the framework integration that allows you to declaratively write queries and mutations in your 17 - preferred framework, which are currently the `urql` or `@urql/preact` packages 18 - - the `Client` that manages the operation lifecycle and results 19 - - and; exchanges that may either be some default exchanges or some from external packages 20 - 21 - On this page we'll learn about the latter two points, which is shared logic that isn't specific to 22 - any framework, like React code or Preact code. 23 - 24 - [We'll learn more about _Exchanges_ on the next page.](./exchanges.md) 25 - 26 - ## Contents of Core 25 + preferred framework, which are currently the [`urql`](../api/urql.md) or 26 + [`@urql/preact`](../api/preact.md) packages 27 + - the [`Client`](../api/core.md#client) that manages the operation lifecycle and results 28 + - and, exchanges that may either be some default exchanges or some from external packages 27 29 28 - The `@urql/core` package contains `urql`'s `Client`, some common utilities, and some default 29 - _Exchanges_. These are the shared, default parts of `urql` that we will be using no matter which 30 - framework we're interacting with. 30 + On this page we'll learn about the latter two points - shared logic that isn't specific to 31 + a particular library or framework (such as React or Preact code). 31 32 32 - Therefore those are also the parts of `urql` that contain its most important logic — like the 33 - `Client` — and the package that we need to know about if we're either integrating `urql` with a new 34 - framework, or if we're using the "raw" `Client` in Node.js. 33 + _Exchanges_ are discussed in more detail on the [next page](./exchanges.md). 35 34 36 35 ## Usage with Node.js 37 36 38 37 The largest part of `urql` itself and the core package is the aforementioned `Client`. It's often 39 - used directly if you're just using `urql` in Node.js without any other integration. 38 + used directly when using `urql` in Node.js without any other integration. 40 39 41 - [We've previously seen how we can use the `Client`'s stream methods directly, in "Stream 42 - Patterns".](./stream-patterns.md) However, the `Client` also has plenty of convenience methods that 43 - make interacting with the `Client` directly a lot easier. 40 + [We've previously seen how we can use the `Client`'s stream methods directly, in [Stream 41 + Patterns](./stream-patterns.md). However, the [`Client`](../api/core.md#client) also has plenty of 42 + convenience methods that make interacting with the `Client` directly a lot easier. 44 43 45 44 ### One-off Queries and Mutations 46 45 ··· 58 57 } 59 58 `; 60 59 61 - client.query(QUERY, { id: 'test' }) 60 + client 61 + .query(QUERY, { id: 'test' }) 62 62 .toPromise() 63 63 .then(result => { 64 64 console.log(result); // { data: ... } ··· 88 88 ``` 89 89 90 90 Since the streams in `urql` operate synchronously, internally this method subscribes to 91 - `client.executeQuery` but unsubscribes immediately. If a result is available in the cache it will be 92 - resolved synchronosuly before we immediately unsubscribe, otherwise this will cause no request to be 93 - sent to the GraphQL API. 91 + `client.executeQuery` and unsubscribes immediately. If a result is available in the cache it will be 92 + resolved synchronosuly prior to the unsubscribe. If not, the query is cancelled and no request will be sent to the GraphQL API. 94 93 95 94 ## Common Utilities in Core 96 95 97 96 The `@urql/core` package contains other utilities that are shared between multiple addon packages. 98 97 This is a short but non-exhaustive list. It contains, 99 98 100 - <!-- TODO: Add links to other docs pages where appropriate --> 101 - 102 - - `CombinedError`, our abstraction to combine `GraphQLError`s and a `NetworkError` 103 - - `makeResult` and `makeErrorResult`, utilities to create _Operation Results_ 104 - - `createRequest`, a utility function to create a request from a query and some variables (which 99 + - [`CombinedError`](../api/core.md#combinederror) - our abstraction to combine one or more `GraphQLError`(s) and a `NetworkError` 100 + - `makeResult` and `makeErrorResult` - utilities to create _Operation Results_ 101 + - [`createRequest`](../api/core.md#createrequest) - a utility function to create a request from a query and some variables (which 105 102 generates a stable _Operation Key_) 106 103 107 - There are more utilities. [Read more about the `@urql/core` API in the API docs for 108 - it.](../api/core.md) 104 + There are other utilities not mentioned here. Read more about the `@urql/core` API in the [API docs](../api/core.md).
+65 -67
docs/concepts/exchanges.md
··· 5 5 6 6 # Exchanges 7 7 8 - As we've learned [on the "Stream Patterns" page](./stream-patterns.md), `urql`'s `Client` structures 8 + As we've learned on the [Stream Patterns](./stream-patterns.md) page, `urql`'s `Client` structures 9 9 its data as an event hub. We have an input stream of operations, which are instructions for the 10 - `Client` to provide a result. These results come from an output stream of operation results. 10 + `Client` to provide a result. These results then come from an output stream of operation results. 11 11 12 - The important part how we get from the operations stream to the results stream, which is the 13 - responsibility of _Exchanges_. _Exchanges_ are handler functions that deal with these input and 14 - output streams. They're the main construct of `urql` to implement every piece of logic, like 15 - caching, fetching, deduplicating requests, and more. In other words, _Exchanges_ are handlers that 12 + _Exchanges_ are responsible for performing the important transform from the operations (input) stream to the results stream. Exchanges are handler functions that deal with these input and 13 + output streams. They're one of `urql`'s key components, and are needed to implement vital pieces of logic such as 14 + caching, fetching, deduplicating requests, and more. In other words, Exchanges are handlers that 16 15 fulfill our GraphQL requests and can change the stream of operations or results. 17 16 18 - The default exchanges that `@urql/core` contains and applies by default to a `Client` without custom 19 - exchanges are the: 17 + The default set of exchanges that `@urql/core` contains and applies to a `Client` are: 20 18 21 19 - `dedupExchange`: Deduplicates pending operations (pending = waiting for a result) 22 20 - `cacheExchange`: The default caching logic with ["Document Caching"](../basics/document-caching.md) 23 21 - `fetchExchange`: Sends an operation to the API using `fetch` and adds results to the output stream 24 22 23 + It is also possible to apply custom exchanges to override the default logic. 24 + 25 25 ## An Exchange Signature 26 26 27 - Because of how _Exchanges_ work they're akin to [middleware in 28 - Redux](https://redux.js.org/advanced/middleware). 27 + Exchanges are akin to [middleware in 28 + Redux](https://redux.js.org/advanced/middleware) due to the way that they apply transforms. 29 29 30 30 ```ts 31 31 import { Client, Operation, OperationResult } from '@urql/core'; 32 32 33 - type ExchangeInput = { forward: ExchangeIO, client: Client }; 33 + type ExchangeInput = { forward: ExchangeIO; client: Client }; 34 34 type Exchange = (input: ExchangeInput) => ExchangeIO; 35 35 type ExchangeIO = (ops$: Source<Operation>) => Source<OperationResult>; 36 36 ``` 37 37 38 - Their signature is a function that receives a `forward` function, which is the next _Exchange_ in a 39 - chain of them and returns the `ExchangeIO` function, which accepts the source of _Operations_ and 40 - returns a source of _Operation Results_: 38 + The first parameter to an exchange is a `forward` function that refers to the next Exchange in the 39 + chain. The second second parameter is the `Client` being used. Exchanges always return an `ExchangeIO` 40 + function (this applies to the `forward` funtion as well), which accepts the source of 41 + [_Operations_](../api/core.md#operation) and returns a source of [_Operation 42 + Results_](../api/core.md#operationresult). 43 + 44 + - [Read more about streams on the "Stream Patterns" page.](../concepts/stream-patterns.md) 45 + - [Read more about the _Exchange_ type signature on the API docs.](../api/core.md#exchange) 41 46 42 47 ## Using Exchanges 43 48 44 - The `Client` accepts an `exchanges` option which is by default the list of default exchanges, as 45 - discussed above. When we pass a custom list of exchanges the `Client` uses the `composeExchanges` 49 + The `Client` accepts an `exchanges` option that defaults to the three default exchanges mentioned above. When we pass a custom list of exchanges the `Client` uses the `composeExchanges` 46 50 utiliy, which starts chaining these exchanges. 47 51 48 52 In essence these exchanges build a pipeline that runs in the order they're passed; _Operations_ flow 49 - in from the start to the end, and _Results_ come back in reverse, through this chain. 53 + in from the start to the end, and _Results_ are returned through the chain in reverse. 50 54 51 55 If we look at our list of default exchanges — `dedupExchange`, `cacheExchange`, and then 52 56 `fetchExchange` — an incoming operation is treated as follows: 53 57 54 58 **First,** ongoing operations are deduplicated. It wouldn't make sense to send the 55 - same operation / request twice at the same time. 59 + same operation / request twice in parralel. 56 60 57 - **Second,** operations are checked against the cache. Depending on the `requestPolicy` 61 + **Second,** operations are checked against the cache. Depending on the `requestPolicy`, 58 62 cached results can be resolved instead and results from network requests are cached. 59 63 60 - **Third,** operations are sent to the API and the result is normalized. The result then travels 61 - backwards through the returned stream of results. 64 + **Third,** operations are sent to the API and the result is normalized. The normalized result then travels 65 + backwards through the stream. 62 66 63 67 ## The Rules of Exchanges 64 68 65 - Before we can start writing some exchanges, there are a couple of patterns and limitations that 66 - always remain the same when writing an exchange. We call these the "rules of _Exchanges_", which 67 - also come in useful when trying to learn what _Exchanges_ actually are. 69 + Before we can start writing some exchanges, there are a couple of consistent patterns and limitations that 70 + must be adhered to when writing an exchange. We call these the "rules of Exchanges", which 71 + also come in useful when trying to learn what Exchanges actually are. 68 72 69 73 For reference, this is a basic template for an exchange: 70 74 71 75 ```js 72 76 const noopExchange = ({ client, forward }) => { 73 - return operation$ => { // <-- The ExchangeIO function 77 + return operation$ => { 78 + // <-- The ExchangeIO function 74 79 const operationResult$ = forward(operations$); 75 80 return operationResult$; 76 81 }; ··· 78 83 ``` 79 84 80 85 This exchange does nothing else than forward all operations and return all results. Hence, it's 81 - called a `noopExchange`, an exchange that doesn't do anything. 86 + called a `noopExchange` - an exchange that doesn't do anything. 82 87 83 88 ### Forward and Return Composition 84 89 85 90 When you create a `Client` and pass it an array of exchanges, `urql` composes them left-to-right. 86 - If we look at our previous `noopExchange` example in context, we can track what it does if it sat 87 - in-between the `dedupExchange` and the `fetchExchange`. 91 + If we look at our previous `noopExchange` example in context, we can track what it does if it is located between the `dedupExchange` and the `fetchExchange`. 88 92 89 93 ```js 90 94 import { Client, dedupExchange, fetchExchange } from 'urql'; 91 95 92 96 const noopExchange = ({ client, forward }) => { 93 - return operation$ => { // <-- The ExchangeIO function 97 + return operation$ => { 98 + // <-- The ExchangeIO function 94 99 // We receive a stream of Operations from `dedupExchange` which 95 100 // we can modify before... 96 101 const forwardOperations$ = operations$; ··· 112 117 }); 113 118 ``` 114 119 115 - ### One operations stream only 120 + ### Only One Operations Stream 116 121 117 - When writing an _Exchange_ we have to be careful not to "split" the stream into multiple ones by 118 - subscribing multiple times. Streams are lazy and immutable by default. Every time you use them, you 119 - create a new chain of streaming operators, but since _Exchanges_ are side-effects, we don't want to 122 + When writing an Exchange we have to be careful not to _split_ the stream into multiple ones by 123 + subscribing multiple times. Streams are lazy and immutable by default. Every time you use them, a new chain of streaming operators is created; since Exchanges are technically side-effects, we don't want to 120 124 accidentally have multiple instances of them in parallel. 121 125 122 - Your `ExchangeIO` function receives an `operations$` stream, and you must be careful to either only 126 + The `ExchangeIO` function receives an `operations$` stream. It's important to be careful to either only 123 127 use it once, or to _share_ its subscription. 124 128 125 129 ```js 126 130 import { pipe, filter, merge, share } from 'wonka'; 127 131 128 132 // DON'T: split use operations$ twice 129 - ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 133 + ({ forward }) => operations$ => { 134 + // <-- The ExchangeIO function (inline) 130 135 const queries = pipe( 131 136 operations$, 132 137 filter(op => op.operationName === 'query') ··· 139 144 }; 140 145 141 146 // DO: share operations$ if you have to use it twice 142 - ({ forward }) => operations$ => { // <-- The ExchangeIO function (inline) 143 - const shared = pipe( 144 - operations$, 145 - share 146 - ); 147 + ({ forward }) => operations$ => { 148 + // <-- The ExchangeIO function (inline) 149 + const shared = pipe(operations$, share); 147 150 const queries = pipe( 148 151 shared, 149 152 filter(op => op.operationName === 'query') ··· 156 159 }; 157 160 158 161 // DO: use operations$ only once alternatively 159 - ({ forward }) => operations$ => // <-- The ExchangeIO function (inline) 162 + ({ forward }) => ( 163 + operations$ // <-- The ExchangeIO function (inline) 164 + ) => 160 165 pipe( 161 166 operations$, 162 167 map(op => { ··· 174 179 use Wonka's [`share`](https://wonka.kitten.sh/api/operators#share) operator, to share the underlying 175 180 subscription between all your streams. 176 181 177 - ### Don't accidentally drop operations 182 + ### How to Avoid Accidentally Dropping Operations 178 183 179 184 Typically the `operations$` stream will send you `query`, `mutation`, 180 185 `subscription`, and `teardown`. There is no constraint for new operations ··· 198 203 199 204 // DO: forward operations that you don't handle 200 205 ({ forward }) => operations$ => { 201 - const shared = pipe( 202 - operations$, 203 - share 204 - ); 206 + const shared = pipe(operations$, share); 205 207 const queries = pipe( 206 208 shared, 207 209 filter(op => op.operationName === 'query') ··· 214 216 }; 215 217 ``` 216 218 217 - If you group and or filter operations by what your exchange is handling, 218 - also make sure that you have a stream of operations that it's not handling, 219 - which you should also forward. 219 + If operations are grouped and/or filtered by what the exchange is handling, then it's also important to 220 + make that any streams of operations not handled by the exchange should also be forwarded. 220 221 221 222 ### Synchronous first, Asynchronous last 222 223 223 224 By default exchanges and Wonka streams are as predictable as possible. 224 - Every operator in Wonka runs synchronously until you actually introduce 225 - asynchronicity. 225 + Every operator in Wonka runs synchronously until asynchronicity is introduced. 226 226 227 - This may happen when you use a timing utility from Wonka, like 227 + This may happen when using a timing utility from Wonka, like 228 228 [`delay`](https://wonka.kitten.sh/api/operators#delay) or 229 229 [`throttle`](https://wonka.kitten.sh/api/operators#throttle) 230 - Or this could happen because your exchange inherently does something asynchronous, like fetching some 231 - data or use a promise. 230 + This can also happen because the exchange inherently does something asynchronous, like fetching some 231 + data or using a promise. 232 232 233 - When you write exchanges, some will inevitably be asynchronous, if 233 + When writing exchanges, some will inevitably be asynchronous. For example if 234 234 they're fetching results, performing authentication, or other tasks 235 235 that you have to wait for. 236 236 237 237 This can cause problems, because the behavior in `urql` is built 238 - to be _synchronous_ first. This helps us build our suspense mode, 239 - and it helps your components receive cached data on their initial 238 + to be _synchronous_ first. This is very helpful for suspense mode and allowing components receive cached data on their initial 240 239 mount without rerendering. 241 240 242 241 This why **all exchanges should be ordered synchronous first and ··· 245 244 The default order of exchanges is: 246 245 247 246 ```js 248 - import { dedupExchange, cacheExchange, fetchExchange } from 'wonka'; 247 + import { dedupExchange, cacheExchange, fetchExchange } from 'urql'; 249 248 250 249 // Also exported as `defaultExchanges`: 251 250 [dedupExchange, cacheExchange, fetchExchange]; 252 251 ``` 253 252 254 253 Both the `dedupExchange` and `cacheExchange` are completely 255 - synchronous and only the `fetchExchange` is asynchronous since 254 + synchronous. The `fetchExchange` is asynchronous since 256 255 it makes a `fetch` request and waits for a server response. 257 256 258 - When you're adding more exchanges you obviously have a reason 259 - to put them in a specific order. For instance, an authentication exchange 260 - needs to go before the `fetchExchange`. And a secondary cache would 261 - maybe go in front of the default cache exchange. 257 + When you're adding more exchanges it's often crucial 258 + to put them in a specific order. For instance - an authentication exchange 259 + will need to go before the `fetchExchange`, a secondary cache will probably have to 260 + go in front of the default cache exchange. 262 261 263 - But to ensure the correct behavior of suspense mode and 264 - the initialization of our hooks, it's vital to order your exchanges 265 - so that synchronous exchanges come first and asynchronous ones 266 - come last. 262 + To ensure the correct behavior of suspense mode and 263 + the initialization of our hooks, it's vital to order exchanges 264 + so that synchronous ones come before asynchronous ones.
+29 -33
docs/concepts/philosophy.md
··· 9 9 [core behavior in the core package](./core-package.md). 10 10 11 11 By default, we aim to provide features that allow you to build your app quickly with minimal 12 - configuration. `urql` is a client that grows with you. As you go from building your first 13 - GraphQL app to a full experience, we give you he tools to extend and customize `urql` based on 12 + configuration. `urql` is designed to be a client that grows with you. As you go from building your first 13 + GraphQL app to a utilising the full functionality, the toopls are available to extend and customize `urql` based on 14 14 your needs. 15 15 16 - In this guide, we will walkthrough how `urql` is set up internally and how all pieces of the puzzle 16 + In this guide, we will walk through how `urql` is set up internally and how all pieces of the puzzle 17 17 — the building blocks of `urql` — interact with one another. 18 18 19 19 ## Hello World 20 20 21 - [We previously read about how to set up a `Client` in "Getting 22 - Started".](../basics/setting-up-the-client.md) 21 + We previously read about how to set up a `Client` in [Getting 22 + Started](../basics/getting-started.md). 23 23 24 - When you use `urql` you will always create and set up a `Client` for which a `createClient` 25 - convenience helper exists. 24 + When you use `urql` you will always create and set up a `Client`. There is a `createClient` 25 + convenience helper to do just that. 26 26 27 27 ```js 28 28 import { Client } from 'urql'; ··· 36 36 37 37 ## Using GraphQL Clients 38 38 39 - You may have worked on a GraphQL API previously and noticed that using GraphQL in your app can be 39 + You may have worked with a GraphQL API previously and noticed that using GraphQL in your app can be 40 40 as straightforward as sending a plain HTTP request with your query to fetch some data. 41 41 42 - But GraphQL provides an opportunity to abstract away a lot of the manual work that goes with 43 - sending these queries and managing the data. This ultimately lets you focus on building 44 - your app without handling the technical details of state management in detail. 42 + GraphQL also provides an opportunity to abstract away a lot of the manual work that goes with 43 + sending these queries and managing the data. Ultimately, this lets you focus on building 44 + your app without having to handle the technical details of state management in detail. 45 45 46 - Specifically `urql` simplifies three common aspects of using GraphQL easily: 46 + Specifically, `urql` simplifies three common aspects of using GraphQL: 47 47 48 48 - Sending queries and mutations and receiving results _declaratively_ 49 49 - Abstracting _caching_ and state management internally ··· 65 65 66 66 ![Operations and Results](../assets/urql-event-hub.png) 67 67 68 - This all happens in the background while you just declare that you'd like to have data for a given 68 + This all happens in the background, allowing you to simply declare that you'd like to have data for a given 69 69 query. 70 70 71 71 ## Caching and State 72 72 73 73 When we use GraphQL queries and mutations declaratively with `urql`, we expect them to interact 74 - and update automaticaly. 74 + and update automatically. 75 75 76 - Furthermore, we don't wish to send more requests for a query, when we've done so before. We'd like 77 - to instead cache results in-memory and notify other parts of an app when these results chane or 78 - are invalidated by mutations or subscriptions. 76 + Furthermore, when we've already received the results from a query, we may not wish to send another request. To solve this, results can be cached in-memory and notifications can be sent to other parts of an app when the results change or 77 + are invalidated by mutations/subscriptions. 79 78 80 - GraphQL clients have access to some amount of type information for any GraphQL API and can hence 79 + GraphQL clients have access to some type information for any GraphQL API and hence can 81 80 cache the results of queries automatically. In `urql` the `Client` can be extended with several 82 - cache implementations, but all of them mean that you'll never mix your declarative query or mutation 83 - code with cache-implementation details, which mostly happen behind the scenes. 81 + cache implementations; all of them allow you to prevent mixing your declarative query or mutation 82 + code with cache-implementation details, as they mostly happen behind the scenes. 84 83 85 - [We previously read about the default "Document Caching".](../basics/document-caching.md) 84 + We previously read about the default [Document Caching](../basics/document-caching.md). 86 85 87 86 Some GraphQL clients also resort to caching data in a normalized format. This is similar to 88 87 [how you may store data in Redux.](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/) ··· 90 89 in the cache and structures it in a graph, which leads to more shared data, and hence more shared 91 90 updates in your UI! 92 91 93 - [Read more about how to add "Normalized Caching" to an app.](../graphcache/normalized-caching.md) 92 + [Read more](../graphcache/normalized-caching.md) on how to add Normalized Caching to an app. 94 93 95 94 ## Extensibility and Integration 96 95 97 - With any kind of API come other concerns apart from caching and state mangagement that concern 98 - the global behavior or business logic of your application. 99 - 100 - For instance, you may want to add authentication, retry-logic for failed requests, or a global 96 + With any kind of API there can be concerns outside of caching and state mangagement. For example, 97 + the global behavior or business logic of your application. For instance, you may want to add authentication, retry-logic for failed requests, or a global 101 98 error handler. 102 99 103 - `urql` provides a concept of _Exchanges_ to abstract the details of how the `Client` interacts with 100 + `urql` introduces the concept of _Exchanges_ in order to abstract the details of how the `Client` interacts with 104 101 your framework of choice, your app, and your GraphQL API. They are akin to 105 102 [middleware in Redux](https://redux.js.org/advanced/middleware) and have access to all operations 106 103 and all results. 107 104 108 - [Read more about _Exchanges_ in a later page of the documentation.](./exchanges.md) 105 + Read more about [Exchanges](./exchanges.md) later on in the documentation. 109 106 110 107 All default behavior in the [core package](./core-package.md) is implemented using 111 - _Exchanges_, which is possible because all operations and all results are treated as a stream 112 - of events. We call these events "Operations". 108 + Exchanges. This is possible as all operations and all results are treated as a stream 109 + of events; we call these events "Operations". 113 110 114 111 ![Operation Signature](../assets/urql-signals.png) 115 112 116 - From our perspective, thinking about your GraphQL queries and results in terms of 117 - streams of operations and results allows us to implement any given complex behaviour, 118 - which we'll learn more about in the next section. 113 + Thinking about GraphQL queries and results in 114 + streams of operations and results allow us to implement complex behaviour in addition to allowing deep customisation over how the operations/results are handled. We'll learn more about this in the next section - [the Core Package](./core-package.md).
+29 -25
docs/concepts/stream-patterns.md
··· 5 5 6 6 # Stream Patterns 7 7 8 - As we've learned [on the last page](./philosophy.md), `urql`'s main way of handling GraphQL requests 9 - is by abstracting them as streams of operations and results 8 + As we've learned in the previous section on [philosophy](./philosophy.md), `urql`'s main way of handling GraphQL requests 9 + is by abstracting them as streams of operations and results. 10 10 11 11 ## Streams on the Client 12 12 13 - Mainly, the client abstracts GraphQL requests as _Operations_, descriptions of the GraphQL request, 14 - its query and variables, and also additional information that is configured on the `Client`, like 15 - the `url` and `fetchOptions`. 13 + The client abstracts GraphQL requests in a number of ways: 14 + 15 + - as _Operations_ 16 + - descriptions of the GraphQL request 17 + - the query and related variables 18 + - additional information that is configured on the `Client`, such as 19 + the `url` and `fetchOptions`. 16 20 17 21 ![Operations stream and results stream](../assets/urql-client-architecture.png) 18 22 19 23 Internally the `Client` is an event hub. It defines a stream of operations as inputs, sends them 20 - through a layer that will ultimately send GraphQL requests to an API, and then sends the results 21 - onto another stream. 24 + through a layer that will ultimately send GraphQL requests to an API, and then send the corresponding results 25 + to another stream. 22 26 23 - As a user, in framework code, we never interact with these streams directly, but they describe 24 - every interaction between the declarative queries we write and how `urql` fulfills them. 27 + As a user working with framewrk code we never interact with these streams directly, but it's helpful to know that they describe 28 + every interaction between the declarative queries we write and the way that `urql` fulfills them. 25 29 26 30 ## Streams in JavaScript 27 31 28 32 Generally we refer to _streams_ as abstractions that allow us to program with asynchronous streams of 29 - events over time, but more specifically in JavaScript, we're thinking specifically of 33 + events over time. Within the JavaScript context we're thinking specifically in terms of of 30 34 [Observables](https://github.com/tc39/proposal-observable) 31 35 and [Reactive Programming with Observables.](http://reactivex.io/documentation/observable.html) 32 36 ··· 39 43 methods on arrays, so you're likely to see `map` and `filter` — amongst other utlities — in those 40 44 libraries. 41 45 42 - [Read this Gist for a more in-depth 43 - explanation.](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) 46 + Read [this Gist](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) for a more in-depth 47 + explanation. 44 48 45 49 ## The Wonka library 46 50 47 51 `urql` utilises the [Wonka](https://github.com/kitten/wonka) library for its streams. It has a 48 - couple of advantages that are specifically tailored for the `urql` library and ecosystem. 52 + few advantages that are specifically tailored for the `urql` library and ecosystem: 49 53 50 - - It is extremely lightweight and treeshakeable, weighing around 3.7kB minzipped. 54 + - It is extremely lightweight and treeshakeable, with a size of around 3.7kB minzipped. 51 55 - It's cross-platform and cross-language compatible, having been written in 52 - [Reason](https://reasonml.github.io/) and providing support for [Flow](https://flow.org/) 56 + [Reason](https://reasonml.github.io/) and provides support for [Flow](https://flow.org/) 53 57 and [TypeScript](typescriptlang.org/). 54 - - It's predictable and also an iterable toolchain, emitting synchronous events whenever possible. 58 + - It's a predictable and iterable toolchain, emitting synchronous events whenever possible. 55 59 56 60 Typical usage of Wonka will involve creating a _source_ of some values and a _sink_. 57 61 ··· 62 66 fromArray([1, 2, 3]), 63 67 map(x => x * 2), 64 68 subscribe(x => { 65 - console.log(x); // 1, 2, 3 69 + console.log(x); // 2, 4, 6 66 70 }) 67 71 ); 68 72 ``` 69 73 70 - In Wonka, like with Observables, streams are cancellable by calling `unsubscribe` that a 74 + In Wonka, like with Observables, streams are cancellable by calling the `unsubscribe` method that a 71 75 subscription returns. 72 76 73 - [Read more about Wonka in its documentation.](https://wonka.kitten.sh/basics/background) 77 + Read more about Wonka in its [documentation](https://wonka.kitten.sh/basics/background). 74 78 75 - ## The Client's query streams 79 + ## Client Query Streams 76 80 77 81 Internally the `Client` has methods that may be used to execute queries, mutations, and 78 - subscriptions. These methods typically return `Wonka` streams that when subscribed to will 82 + subscriptions. These methods typically return `Wonka` streams that, when subscribed to, will 79 83 emit results for a given query. 80 84 81 85 When a result can be retrieved from an in-memory cache, the stream may even emit the result 82 86 synchronously — rather than asynchronously. 83 87 84 - There are three methods for each different type of operation that GraphQL supports, there's an 85 - `executeQuery`, `executeMutation`, and `executeSubscription` method. All these methods are 88 + There are three methods for each type of operation that GraphQL supports; 89 + `executeQuery`, `executeMutation`, and `executeSubscription`. All these methods are 86 90 convenience wrappers around `executeRequestOperation` that create an operation and return a stream. 87 91 88 92 There are also convenience wrappers around the "execute" methods that are useful when using `urql` 89 - in a Node.js environment. Those are `query`, `mutation`, and `subscription`. 93 + in a Node.js environment. They are the `query`, `mutation`, and `subscription` methods. 90 94 91 95 ```js 92 96 import { pipe, subscribe } from 'wonka'; ··· 113 117 There are several of these convenience methods in `urql` that make it easier to work with the 114 118 concept of GraphQL operation and result streams. 115 119 116 - [Read more about the available APIs on the `Client` in the Core API docs.](../api/core.md) 120 + Read more about the available APIs on the `Client` in the [Core API docs](../api/core.md).
+77 -1
docs/graphcache/README.md
··· 5 5 6 6 # Graphcache 7 7 8 - <!-- TODO: Link to subpages --> 8 + In `urql`, caching is fully configurable via [_Exchanges_](../concepts/exchanges.md) and the default 9 + `cacheExchange` in `urql` offers a ["Document Cache"](../basics/document-caching.md), which is 10 + sufficient for sites that heavily rely and render static content. However as an app grows more 11 + complex it's likely that the data and state that `urql` manages, will also grow more complex and 12 + introduce interdependencies between data. 13 + 14 + To solve this problem most GraphQL clients resort to caching data in a normalized format, similar to 15 + how [data is often structured in 16 + Redux.](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/) 17 + 18 + In `urql`, normalized caching is an opt-in feature which is provided by the 19 + `@urql/exchange-graphcache` package, _Graphcache_ for short. 20 + 21 + ## Features 22 + 23 + The following pages introduce different features in _Graphcache_ which together make it a compelling 24 + alternative to the standard [document cache](../basics/document-caching.md) that `urql` uses by 25 + default. 26 + 27 + - 🔁 [**Fully reactive, normalized caching.**](./normalized-caching.md) _Graphcache_ stores data in 28 + a normalized data structure. Query, mutation, and subscription results may update one another if 29 + they share data, and the app will rerender or refetch data accordingly. This often allows your app 30 + to make less API requests, since data may already be in the cache. 31 + - 💾 [**Custom cache resolvers**](./computed-queries.md) Since all queries are fully resolved in the 32 + cache before and after they're sent, you can add custom resolvers that enable you to format data, 33 + implement pagination, or implement cache redirects. 34 + - 💭 [**Subscription and Mutation updates**](./custom-updates.md) You can implement update functions 35 + that tell _Graphcache_ how to update its data after a mutation has been executed, or whenever a 36 + subscription sends a new event. This allows the cache to reactively update itself without queries 37 + having to perform a refetch. 38 + - 🏃 [**Optimistic mutation updates**](./custom-updates.md) When implemented, optimistic updates can 39 + provide the data that the GraphQL API is expected to send back before the request succeeds, which 40 + allows the app to instantly render an update while the GraphQL mutation is executed in the 41 + background. 42 + - 🧠 [**Opt-in schema awareness**](./schema-awareness.md) _Graphcache_ also optionally accepts your 43 + entire schema, which allows it to resolve _partial data_ before making a request to the GraphQL 44 + API, allowing an app to render everything that's cached before receiving all missing data. It also 45 + allows _Graphcache_ to output more helpful warnings and to handle interfaces and enums correctly 46 + without heuristics. 47 + - 📡 **Offline support** (work in progress) _Graphcache_ can persist and rehydrate its entire state, 48 + allowing an offline application to be built that is able to execute queries against the cache 49 + although the device is offline. 50 + 51 + ## Installation and Setup 52 + 53 + We can add _Graphcache_ by installing the `@urql/exchange-graphcache` package. 54 + Using the package won't increase your bundle size by as much as platforms like 55 + [Bundlephobia](https://bundlephobia.com/result?p=@urql/exchange-graphcache) may suggest, since it 56 + shares the dependency on `wonka` and `@urql/core` with the framework bindings package, e.g. `urql` 57 + or `@urql/preact`, that you're already using. 58 + 59 + ```sh 60 + yarn add @urql/exchange-graphcache 61 + # or 62 + npm install --save @urql/exchange-graphcache 63 + ``` 64 + 65 + The package exports the `cacheExchange` which replaces the default `cacheExchange` in `@urql/core`. 66 + This new `cacheExchange` must be instantiated using some options, which are used to customise 67 + _Graphcache_ as introduced in the ["Features" section above.](#features) Howver, you can get started 68 + without passing any options. 69 + 70 + ```js 71 + import { createClient, dedupExchange, fetchExchange } from 'urql'; 72 + import { cacheExchange } from '@urql/exchange-graphcache'; 73 + 74 + const client = createClient({ 75 + url: 'http://localhost:3000/graphql', 76 + exchanges: [dedupExchange, cacheExchange({}), fetchExchange], 77 + }); 78 + ``` 79 + 80 + This will automatically enable normalized caching, and you may find that in a lot of cases, 81 + _Graphcache_ already does what you'd expect it to do without any additional configuration. We'll 82 + explore how to customize and set up different parts of _Graphcache_ on the following pages. 83 + 84 + [Read more about "Normalized Caching" on the next page.](./normalized-caching.md)
+58 -29
docs/graphcache/computed-queries.md
··· 1 1 --- 2 - title: Computed-queries 2 + title: Computed Queries 3 3 order: 2 4 4 --- 5 5 6 - # Resolvers 6 + # Computed Queries 7 7 8 - `resolvers` are a way to alter the response you'll receive from the cache. 9 - Let's look at an example to get a better understanding. 8 + When dealing with data we could have special cases where we want to format a date 9 + or if we for instance have a list of a certain entity in cache and next we want 10 + to query a specific entity, chances are this will already be (partially) available 11 + in that list. 12 + 13 + These cases can be solved with the concept of `resolvers`. 14 + 15 + ## Resolvers 16 + 17 + Let's look at how we can introduce these `resolvers` to our `cacheExchange`. 10 18 11 19 ```js 12 20 const cache = cacheExchange({ 13 21 resolvers: { 14 - Todo: { text: () => 'Secret' }, 22 + Todo: { updatedAt: ({ date }) => Date.format(date) }, 15 23 }, 16 24 }); 17 25 ``` 18 26 19 27 Now when we query our `todos` every time we encounter an object with `Todo` 20 - as the `__typename` it will change the `text` to `'Secret'`. In this way we 28 + as the `__typename` it will format the `updatedAt` property. This way we 21 29 can effectively change how we handle a certain property on an entity. 22 30 23 - This may seem pretty useless right now, but let's look at the arguments 24 - passed to `resolvers` to get a better sense of how powerful they are. 25 - 26 - A `resolver` gets four arguments: 31 + Let's look at the arguments passed to `resolvers` to get a better sense of 32 + what we can do, there are four arguments (these are in order): 27 33 28 34 - `parent` – The original entity in the cache. In the example above, this 29 35 would be the full `Todo` object. 30 36 - `arguments` – The arguments used in this field. 31 - - `cache` – This is the normalized cache. The cache provides us with `resolve`, `readQuery` and `readFragment` methods; 32 - see more about this [below](#cache.resolve). 37 + - `cache` – This is the normalized cache. The cache provides us with `resolve`, `readQuery` and `readFragment` methods, 38 + read more about this [below](#cache.resolve). 33 39 - `info` – This contains the fragments used in the query and the field arguments in the query. 34 40 35 41 ## Cache parameter 42 + 43 + This is the main point of communication with the cache, it will give us access to 44 + all cached data. 36 45 37 46 ### resolve 38 47 ··· 44 53 - `field` – The field you want data for. This can be a relation or a single property. 45 54 - `arguments` – The arguments to include on the field. 46 55 47 - To get a better grasp let's look at a few examples. 48 - Consider the following data structure: 56 + To get a better grasp let's look at a few examples, 57 + consider the following data structure: 49 58 50 59 ```js 51 60 todos: [ ··· 64 73 ]; 65 74 ``` 66 75 67 - Using `cache.resolve` to get the author would look like this: 76 + Let's get the `author` for a todo. 68 77 69 78 ```js 70 79 const parent = { ··· 74 83 author: undefined, 75 84 __typename: 'Todo', 76 85 }; 77 - const result = cache.resolve(parent, 'author'); 78 - console.log(result); // 'Author:2' 86 + 87 + console.log(cache.resolve(parent, 'author')); // 'Author:2' 79 88 ``` 80 89 81 90 Now we have a stringed key that identifies our author. We ··· 100 109 }); 101 110 ``` 102 111 103 - will do the trick. 112 + Returning a `__typename` and `key` (`id`/`_id`/custom key) is sufficient to make the 113 + cache resolve this to the full entity. 114 + 115 + Note that resolving from a list to details can lead to partial data, this will result in 116 + a network-request to get the full data when fields are missing. 117 + When graphcache isn't [aware of your schema](./schema-awareness.md) it won't show partial data. 104 118 105 119 ### Reading a query 106 120 ··· 108 122 accepts an object of `query` and optionally `variables`. 109 123 110 124 ```js 111 - const data = cache.readQuery({ query: Todos, variables: { from: 0, limit: 10 } })` 125 + cache.readQuery({ query: Todos, variables: { from: 0, limit: 10 } })` 112 126 ``` 113 127 114 - This way we'll get the stored data for the `TodosQuery` with given variables. 128 + This way we'll get the stored data for the `TodosQuery` for the given `variables`. 115 129 116 130 ### Reading a fragment 117 131 118 132 The store allows the user to also read a fragment for a certain entity, this function 119 133 accepts a `fragment` and an `id`. This looks like the following. 120 134 121 - ```js 122 - const data = cache.readFragment(gql` 123 - fragment _ on Todo { 124 - id 125 - text 126 - } 127 - `, '1'); 135 + ```js 136 + import gql from 'graphql-tag'; 137 + 138 + const data = cache.readFragment( 139 + gql` 140 + fragment _ on Todo { 141 + id 142 + text 143 + } 144 + `, 145 + '1' 146 + ); 128 147 ``` 129 148 149 + > **Note:** In the above example, we've used 150 + > [graphql-tag](https://github.com/apollographql/graphql-tag) because `writeFragment` only accepts 151 + > GraphQL `DocumentNode`s as inputs, and not strings. 152 + 130 153 This way we'll get the Todo with id 1 and the relevant data we are askng for in the 131 154 fragment. 132 155 133 156 ## Pagination 134 157 135 - ### Simple 158 + `Graphcache` offers some preset `resolvers` to help us out with endless scrolling pagination. 159 + 160 + ### Simple Pagination 136 161 137 162 Given you have a schema that uses some form of `offset` and `limit` based pagination you can use the 138 163 `simplePagination` exported from `@urql/exchange-graphcache/extras` to achieve an endless scroller. ··· 157 182 and `skip` respectively. This way you can use the keywords that you are using in 158 183 your queries. 159 184 160 - ### Relay 185 + ### Relay Pagination 161 186 162 187 Given you have a [relay-compatible schema](https://facebook.github.io/relay/graphql/connections.htm) 163 188 on your backend we offer the possibility of endless data resolving. ··· 201 226 202 227 With inwards merging the nodes will be in this order: `[1, 2, ..., 89, 99]` 203 228 And with outwards merging: `[..., 89, 99, 1, 2, ...]` 229 + 230 + ### Reading on 231 + 232 + [On the next page we'll learn about "Custom updates".](./custom-updates.md)
+147 -90
docs/graphcache/custom-updates.md
··· 1 1 --- 2 - title: Custom-updates 2 + title: Custom Updates 3 3 order: 3 4 4 --- 5 5 6 - # Custom Updates (on Mutations or Subscriptions), 6 + # Custom Updates 7 7 8 - ## Data-updates 8 + _Graphcache_ will attempt to automatically react to your mutations' and subscriptions' results 9 + but sometimes this isn't possible. While it will update all normalized entities that it finds in 10 + those results, it can't for instance tell whether a new item should be appended or removed from a 11 + list. 9 12 10 - When the cache receives a response it will try and do its best to 11 - incorporate that response into the current cache. However, for adding and 12 - deleting entities it can't really make assumptions. 13 + Specifically, a normalized cache can't automatically assume that unrelated links have changed due to 14 + a mutation, since this is server-side specific logic. Instead, we may use the `updates` 15 + configuration to set up manual updates that react to mutations or subscriptions. 13 16 14 - That's where updates come into play. Analogous to our `resolvers`, 15 - `updates` get arguments, but instead of the `parent` argument we get the 16 - result given from the server due to a subscription trigger or a mutation. 17 + ## Data Updates 17 18 18 - > Note that this result will look like result.data, this means that in 19 - > the example of us adding a todo by means of `addTodo` it will look like 20 - > `{ addTodo: addedTodo }`. 19 + The `updates` configuration is similar to our `resolvers` configuration that we've [previously looked 20 + at on the "Computed Queries" page.](./computed-queries.md) We pass a resolver-like function into the 21 + configuration that receives cache-specific arguments. Instead of the `parent` argument we'll however 22 + receive the subscription's or mutation's `data` instead. 21 23 22 - Let's look at three additional methods provided by the cache to enable 23 - updates. 24 + ```js 25 + const cache = cacheExchange({ 26 + updates: { 27 + Mutation: { 28 + addTodo: (result, args, cache, info) => { 29 + // ... 30 + }, 31 + }, 32 + Subscription: { 33 + newTodo: (result, args, cache, info) => { 34 + // ... 35 + }, 36 + }, 37 + }, 38 + }); 39 + ``` 24 40 25 - The first we'll look at is `updateQuery`. This method, given an object 26 - containg the query and optionally some variables as first argument and 27 - a callback with the result from this query as the second, will update the 28 - cache. 41 + Inside these update functions, apart from the `cache` methods that we've seen in ["Computed 42 + Query"](./computed-queries.md) to read from the cached data, we can also use other `cache` methods to 43 + write to the cached data. 44 + 45 + ### cache.updateQuery 46 + 47 + The first we'll look at is `cache.updateQuery`. This method accepts a `{ query, variables }` object 48 + as the first argument and an updater callback as the second. The updater function receives the query 49 + data as its' only argument and must return the updated version of said data. 50 + 51 + Note that we don't have to update the query data immutably. _Graphcache_ never returns raw data and 52 + will instead always return copies of data. This means that we can also mutate query data inside the 53 + `updateQuery` callback. 29 54 30 55 ```js 31 - const Todos = gql` 56 + const TodosQuery = gql` 32 57 query { 33 58 __typename 34 59 todos { ··· 40 65 } 41 66 `; 42 67 43 - cache.updateQuery({ query: Todos, variables: { page: 1 } }, data => { 44 - data.todos.push({ 45 - id: '2', 46 - text: 'Learn updates and resolvers', 47 - complete: false, 48 - __typename: 'Todo', 49 - }); 50 - return data; 68 + const cache = cacheExchange({ 69 + updates: { 70 + Mutation: { 71 + addTodo: (result, args, cache, info) => { 72 + cache.updateQuery({ query: TodosQuery }, data => { 73 + data.todos.push(result.addTodo); 74 + return data; 75 + }); 76 + }, 77 + }, 78 + }, 51 79 }); 52 80 ``` 53 81 54 - In the above code sample, we start by supplying a query to the `cache`. 55 - This allows it to fetch the required data. This data is passed as the 56 - second argument to the `updater`, which allows you to alter it before 57 - returning. When you have returned to the cache it will update the relevant 58 - query with your given input. 82 + In the above example, we add an updater configuration for `Mutation.addTodo`. Whenever a mutation's 83 + result contains `addTodo` our new updater will be called. 84 + 85 + Inside the updater we use `cache.updateQuery` to update a list of todos with the new todo that has 86 + been created by `addTodo`, which we can access using `result.addTodo`. 87 + We could also alter this todo before returning our updated `data`. 88 + 89 + With this configuration any query that requests `Query.todos` will automatically update and contain 90 + our new todo, when a mutation with `Mutation.addTodo` completes. 91 + 92 + It's worth noting that it doesn't matter whether the `TodosQuery` is the same one that you use in 93 + your application code. We're simply updating the normalized data of `Query.todos` across the 94 + normalized store, which will be reflected in any query that depends on `Query.todos`. 95 + 96 + ### cache.writeFragment 59 97 60 - > Note that you have to supply \_\_typename. 98 + Similar to `cache.updateQuery`, we can also update data for a fragment using `cache.writeFragment`, 99 + instead of an entire query. This method accepts a GraphQL fragment instead of an entire GraphQL 100 + query. It's also not an updater method but a direct write method instead. 61 101 62 - The second method we have available on the `cache` is `updateFragment`. 63 - This method allows you to supply a GraphQL fragment to update, such that you 64 - don't have to supply the full object entity (in this case `Todo`) if you just 65 - want to update specific fields. 102 + The first argument for `cache.writeFragment`, similarly to `readFragment`, must be a GraphQL 103 + fragment. The second argument is the data that should be written to the cache. This data object must 104 + contain `id` or another field if the type has a custom `keys` configuration. 66 105 67 106 ```js 68 - cache.updateFragment( 107 + import gql from 'graphql-tag'; 108 + 109 + cache.writeFragment( 69 110 gql` 70 111 fragment _ on Todo { 71 112 id ··· 79 120 ); 80 121 ``` 81 122 82 - In the example above, we supply a fragment on `Todo` to `updateFragment`. You'll 83 - notice we include the `id` property such that the cache knows which entity to update, 84 - as well as the some fields we want to update. So in this case we update `Todo` with `id` 85 - `'1'` to have text `'update'`. The rest of the properties (complete and \_\_typename) 86 - will stay untouched. 123 + > **Note:** In the above example, we've used 124 + > [graphql-tag](https://github.com/apollographql/graphql-tag) because `writeFragment` only accepts 125 + > GraphQL `DocumentNode`s as inputs, and not strings. 126 + 127 + This can be useful for instance if we have a mutation that doesn't return the type that the GraphQL 128 + API will alter in the background. Suppose we had a `updateTodoText` mutation that doesn't allow us 129 + to access the updated `Todo`. In such a case `cache.writeFragment` allows us to make the change 130 + manually: 87 131 88 132 ```js 133 + import gql from 'graphql-tag'; 134 + 89 135 const cache = cacheExchange({ 90 136 updates: { 91 137 Mutation: { 92 - addTodo: (result, args, cache, info) => { 93 - cache.updateQuery({ query: Todos }, data => { 94 - data.todos.push(result); 95 - return data; 96 - }); 97 - }, 98 - }, 99 - Subscription: { 100 - newTodo: (result, args, cache) => { 101 - cache.updateQuery({ query: Todos }, data => { 102 - data.todos.push(result); 103 - return data; 104 - }); 138 + updateTodoText: (result, args, cache, info) => { 139 + cache.writeFragment( 140 + gql` 141 + fragment _ on Todo { 142 + id 143 + text 144 + } 145 + `, 146 + { id: args.id, text: args.text } 147 + ); 105 148 }, 106 149 }, 107 150 }, 108 151 }); 109 152 ``` 110 153 111 - The last method we'll look at is essentially an escape hatch 112 - this is called `invalidateQuery` and accepts a `query` as 113 - the first argument and variables for that query as the second. 154 + In this example we haven't used `result` at all, but have written to a `Todo` fragment using the 155 + arguments (`args`) that have been supplied to `Mutation.updateTodoText`. This can also be used in 156 + combination with `cache.readFragment` or `cache.resolve` if we need to retrieve arbitrary data from 157 + the cache, before using `cache.writeFragment` to update some data. 158 + 159 + ## cache.invalidate 114 160 115 - This method should only be needed when a mutation has some sort 116 - of side-effect, let's say when a user subscribes to a certain subject 117 - that user gets an additional agenda. 161 + The `cache.invalidate` method is useful for evicting a single entity from the cache. When a user 162 + logs out or a mutation deletes an item from a list it can be tedious to update every link in our 163 + normalized data to said entity, instead the `cache.invalidate` method can be used to quickly remove 164 + the entity itself. 118 165 119 - This can't really be derived from the mutation response so we opt 120 - to invalidate our agenda's as followed: 166 + Note that this may lead to an additional request to the GraphQL API, unless you're making use of the 167 + ["Schema Awareness" feature](./schema-awareness.md), since deleting an entity may cause cache 168 + misses for all queries that depend on this entity. 121 169 122 170 ```js 123 171 const cache = cacheExchange({ 124 172 updates: { 125 173 Mutation: { 126 - subscribeToSubject: (result, args, cache, info) => { 127 - cache.invalidateQuery(AgendasForUser, { userId: args.userId }); 174 + deleteTodo: (result, args, cache, info) => { 175 + cache.invalidate({ __typename: 'Todo', id: args.id }); 128 176 }, 129 177 }, 130 178 }, 131 179 }); 132 180 ``` 133 181 134 - Next time we hit the query for agendas this will be refetched. 182 + The above example deletes a `Todo` with a given `id` from the arguments, when `Mutation.deleteTodo` 183 + is executed. This will cause all queries that reference this `Todo` to automatically update. 135 184 136 185 ## Optimistic updates 137 186 138 - Let's say we want to work offline or we don't want to wait for 139 - responses from the server. 187 + If we know what result a mutation may return, why wait for the GraphQL API to fulfill our mutations? 188 + The _Optimistic Updates_ configuration allows us to set up "temporary" results for mutations, which 189 + will be applied immediately. This is a great solution to reduce the waiting time for the user. 140 190 141 - Optimistic responses can be a great solution to this problem. Optimisitc 142 - responses are simply a mapping of the name of a mutation to a function. 143 - This function gets three 144 - arguments: 191 + This technique is often used with one-off mutations that are assumed to succeed, like starring a 192 + repository, or liking a tweet. In such cases it's often desirable to make the interaction feel 193 + as instant as possible. 194 + 195 + The `optimistic` configurations are similar to `resolvers` as well. We supply an function to 196 + `Mutation` fields that must return some data. When said mutation is then executed, _Graphcache_ 197 + applies a temporary update using the supplied optimistic data that is reverted when the real 198 + mutation completes and an actual result comes back from the API. The temporary update is also 199 + reverted if the GraphQL mutation fails. 200 + 201 + The `optimistic` functions receive the same arguments as `resolvers` functions, except for `parent`: 145 202 146 203 - `variables` – The variables used to execute the mutation. 147 - - `cache` – The cache we've already seen in [resolvers](./resolvers.md) and 148 - [updates](./updates.md). This can be used to get a certain entity/property 149 - from the cache. 204 + - `cache` – The cache we've already seen in [resolvers](./computed-queries.md) and in the previous 205 + examples on this page. In optimistic updates it's useful to retrieve more data from the cache. 150 206 - `info` – Contains the used fragments and field arguments. 151 207 152 - Let's see an example. 208 + In the following example we assume that we'd like to implement an optimistic result for a 209 + `favoriteTodo` mutation, which sets `favorite` on a `Todo` to `true`: 153 210 154 211 ```js 155 - const myGraphCache = cacheExchange({ 212 + const cache = cacheExchange({ 156 213 optimistic: { 157 - addTodo: (variables, cache, info) => { 158 - console.log(variables); // { id: '1', text: 'optimistic' } 159 - return { 160 - ...variables, 161 - __typename: 'Todo', // We still have to let the cache know what entity we are on. 162 - }; 163 - }, 214 + favoriteTodo: (variables, cache, info) => ({ 215 + __typename: 'Todo', 216 + id: variables.id, 217 + favorite: true, 218 + }), 164 219 }, 165 220 }); 166 221 ``` 167 222 168 - Now that we return `variables` our `Todo:1` will be updated to have 169 - the new `text` property. In our cache this will form a layer above 170 - the property. When a response from the server comes in this layer 171 - will be removed and the response from the server will be used to 172 - replace the original properties. 223 + Since we return an assumed result in our `optimistic` configuration, `Mutation.favoriteTodo` will be 224 + automatically applied and `favorite` will seemingly flip to `true` instantly for the queries that 225 + observe `Todo.query`. 226 + 227 + ### Reading on 228 + 229 + [On the next page we'll learn about "Schema-awareness".](./schema-awareness.md)
+1 -6
docs/graphcache/help.md exchanges/graphcache/help.md
··· 1 - --- 2 - title: Help 3 - order: 6 4 - --- 5 - 6 1 # Help! 7 2 8 3 **This document lists out all errors and warnings in `@urql/exchange-graphcache`.** ··· 271 266 ## (19) Can't generate a key for invalidate(...) 272 267 273 268 > Can't generate a key for invalidate(...). 274 - > You need to pass in a valid key (__typename:id) or an object with the "__typename" property and an "id" or "_id" property. 269 + > You need to pass in a valid key (**typename:id) or an object with the "**typename" property and an "id" or "\_id" property. 275 270 276 271 You probably have called `cache.invalidate` with data that the cache can't generate a key for. 277 272
+75 -33
docs/graphcache/normalized-caching.md
··· 5 5 6 6 # Normalized Caching 7 7 8 - In urql we have the option to utilize a normalized caching mechanism, 9 - this opens up a world of new features ranging from automatic updates 10 - to offline capabilities. 8 + With _Graphcache_ all data is stored in a normalized data structure. It automatically uses 9 + `__typename` information and `id` fields on entities to create a normalized table of data. Since 10 + GraphQL deals with connected data in a tree structure, each entity may link to other entities or 11 + even lists of entities, which we call "links". The scalar fields on entities like numbers, strings, 12 + etc is what we call "records." 11 13 12 - Instead of storing a query by its `operationKey` we'll store the entities 13 - we get back and normalize them so for instance: 14 + Instead of storing query results whole documents, like `urql` does with [its default "Document 15 + Caching"](../basics/document-caching.md), _Graphcache_ flattens all data it receives automatically. 16 + If we looked at doing this manually on the following piece of data, we'd separate each object into a 17 + list of key-value entries per entity. 14 18 15 - ```js 16 - todo: { 17 - __typename: 'Todo', 18 - id: 1, 19 - title: 'implement graphcache', 20 - author: { 21 - __typename: 'Author', 22 - id: 1, 23 - name: 'urql-team', 19 + ```json 20 + { 21 + "__typename": "Query", 22 + "todo": { 23 + "__typename": "Todo", 24 + "id": 1, 25 + "title": "implement graphcache", 26 + "author": { 27 + "__typename": "Author", 28 + "id": 1, 29 + "name": "urql-team" 30 + } 24 31 } 25 32 } 26 33 ``` 27 34 28 - will become 35 + The above would look like the following once we normalized the data: 29 36 30 - ```js 37 + ```json 31 38 { 32 - "Todo:1.title": 'implement graphcache', 33 - "Todo:1.author": 1, 34 - "Author:1.name": 'urql-team', 39 + "Query": { 40 + "todo": "Todo:1" // link 41 + }, 42 + "Todo:1": { 43 + "__typename": "Todo", 44 + "id": "1", // record 45 + "title": "implement graphcache", // record 46 + "author": "Author:1" // link 47 + }, 48 + "Author:1": { 49 + "__typename": "Todo", 50 + "id": "1", // record 51 + "name": "urql-team" // record 52 + } 35 53 } 36 54 ``` 37 55 38 - This allows us to for instance take the result of an update mutation to 39 - `Todo:1` and automatcally update altered properties, this also allows us to 40 - reuse entities. We will always try to create a key with the `__typename` and the 41 - `id` or `_id` whichever is present. 56 + This is vewry similar to how we'd go about creating a state management store manually, except that 57 + _Graphcache_ can use the GraphQL document and the `__typename` field to perform this normalization 58 + automatically. 59 + 60 + The interesting part of normalization starts when we read from the cache instead of writing to it. 61 + Multiple results may refer to the same piece of data — a mutation for instance may update `"Todo:1"` 62 + later on in the app. This would automatically cause _Graphcache_ to update any related queries in 63 + the entire app, because all references to each each entity are shared. 64 + 65 + ## Key Generation 66 + 67 + As we saw in the previous example, by default _Graphcache_ will attempt to generate a key by 68 + combining the `__typename` of a piece of data with the `id` or `_id` fields, if they're present. For 69 + instance, `{ __typename: 'Author', id: 1 }` becomes `"Author:1"`. 42 70 43 - The custom `keys` property comes into play when we don't have an `id` or `_id`, 44 - in this scenario graphcache will warn us and ask to create a key for said entity. 71 + _Graphcache_ will log a warning when these fields weren't requested as part of a query's selection 72 + set or aren't present in the data. This can be useful if we forget to include them in our queries. 73 + In general, _Graphcache_ will always output warnings in development when it assumes that something 74 + went wrong. 75 + 76 + However, in your schema you may have types that don't have an `id` or `_id` field, say maybe some 77 + types have a `key` field instead. In such cases the custom `keys` configuration comes into play 45 78 46 79 Let's look at an example. Say we have a set of todos each with a `__typename` 47 80 of `Todo`, but instead of identifying on `id` or `_id` we want to identify 48 - each record by its `name`. 81 + each record by its `name`: 49 82 50 83 ```js 51 - import { cacheExchange } from '@urql/exchange-graphcache'; 84 + import { cacheExchange } from '@urql/exchange-graphcache'; 52 85 53 - const myGraphCache = cacheExchange({ 86 + const cache = cacheExchange({ 54 87 keys: { 55 88 Todo: data => data.name, 56 89 }, 57 90 }); 58 91 ``` 59 92 60 - Now when we query or write a Todo it will use `name` to identify the record 61 - in the cache. All other records will be resolved the traditional way. 93 + This will cause our cache to generate a key from `__typename` and `name` instead if an entity's type 94 + is `Todo`. 95 + 96 + Similarly some pieces of data shouldn't be normalized at all. If _Graphcache_ can't find the `id` or 97 + `_id` fields it will log a warning and _embed the data_ instead. Embedding the data means that it 98 + won't be normalized because the generated key is `null` and will instead only be referenced by the 99 + parent entity. 62 100 63 - In the same way, you could say that a Todo meant only for admin access is 64 - prefixed with `admin`. 101 + You can force this behaviour and silence the warning by making a `keys` function that returns `null` 102 + immediately. This can be useful for types that aren't globally unique, like a `GeoPoint`: 65 103 66 104 ```js 67 105 const myGraphCache = cacheExchange({ 68 106 keys: { 69 - Todo: data => (data.isAdminOnly ? `admin-${data.name}` : data.name), 107 + GeoPoint: () => null, 70 108 }, 71 109 }); 72 110 ``` 111 + 112 + ### Reading on 113 + 114 + [On the next page we'll learn about "Computed queries".](./computed-queries.md)
+16 -2
docs/graphcache/schema-awareness.md
··· 1 1 --- 2 - title: Schema-awareness 2 + title: Schema Awareness 3 3 order: 4 4 4 --- 5 5 6 - # Schema-awareness 6 + # Schema Awareness 7 7 8 8 As mentioned in the docs we allow for the schema to be passed 9 9 to the `cacheExchange` this allows for partial results and deterministic ··· 11 11 With deterministic fragment matching we mean that if you use an interface 12 12 or a union we will be 100% sure you're allowed to do so, we'll check if the 13 13 type you request can actually be returned from this union/interface. 14 + 15 + ## Getting your schema 14 16 15 17 But how do you get this schema? Well let's consider some steps, first 16 18 make sure `introspection` is turned on on your server. This is very crucial ··· 42 44 }); 43 45 ``` 44 46 47 + ## Integrating 48 + 45 49 next up we can just import this schema and add it to the cacheExchange: 46 50 47 51 ```js ··· 49 53 50 54 const cache = cacheExchange({ schema }); 51 55 ``` 56 + 57 + So what benefits do we have now that graphCache is aware of the shape of our schema? 58 + 59 + ### Partial results 60 + 61 + Let's approach this with the example from [Computed queries](./computed-queries.md#resolve) we have 62 + our `TodosQuery` result (a list) and now we want to get a specific `Todo` we wire these up through 63 + `resolve` but we are missing an optional field for this, without a schema we don't know this is optional 64 + and we will not show you the partial result. Now that we have a schema we can check if this is allowed to 65 + be left out, we'll return you the entity and fetch the missing properties in the background.
+30
docs/showcase.md
··· 5 5 6 6 # Showcase 7 7 8 + `urql` wouldn't be the same without our growing and loving community of users, 9 + maintainers, and supporters. This page is specifically dedicated to all of you! 8 10 11 + ## Used by folks at 12 + 13 + <a href="https://tripadvisor.com"> 14 + <img alt="TripAdvisor" height="60" src="./assets/logos/tripadvisor.png" /> 15 + </a> 16 + 17 + <a href="https://github.com"> 18 + <img alt="GitHub" height="60" src="./assets/logos/github.png" /> 19 + </a> 20 + 21 + <a href="https://egghead.io"> 22 + <img alt="Egghead" height="60" src="./assets/logos/egghead.png" /> 23 + </a> 24 + 25 + ## Articles & Tutorials 26 + 27 + - [Egghead Course](https://egghead.io/lessons/graphql-set-up-an-urql-graphql-provider-in-react?pl=introduction-to-urql-a-react-graphql-client-faaa2bf5) 28 + by [Ian Jones](https://twitter.com/_jonesian). 29 + - [How To GraphQL: React + urql](https://www.howtographql.com/react-urql/0-introduction/) 30 + 31 + ## Community packages 32 + 33 + - [`reason-urql`](https://github.com/FormidableLabs/reason-urql): The official Reason bindings for 34 + `urql`. 35 + - [`urql-persisted-queries`](https://github.com/Daniel15/urql-persisted-queries): Support for 36 + Apollo-style persisted queries. 37 + - [`urql-computed-queries`](https://github.com/Drawbotics/urql-computed-exchange): An exchange to 38 + compute fields on-the-fly using the `@computed` directive.
+1 -1
exchanges/shared/src/helpers/help.ts
··· 28 28 type DebugNode = ExecutableDefinitionNode | InlineFragmentNode; 29 29 30 30 const helpUrl = 31 - '\nhttps://github.com/FormidableLabs/urql/blob/master/docs/graphcache/help.md#'; 31 + '\nhttps://github.com/FormidableLabs/urql/tree/master/exchanges/graphcache/help.md#'; 32 32 const cache = new Set<string>(); 33 33 34 34 export const currentDebugStack: string[] = [];
+8 -4
packages/site/package.json
··· 8 8 "build": "react-static build", 9 9 "lint": "eslint --ext=js,jsx .", 10 10 "clean": "rimraf dist", 11 - "prepublishOnly": "run-s clean build" 11 + "prepublishOnly": "run-s clean build", 12 + "stage:build": "react-static build --staging", 13 + "stage:deploy": "node scripts/deploy/surge.js", 14 + "prod:build": "react-static build", 15 + "prod:deploy": "node scripts/deploy/aws.js" 12 16 }, 13 17 "babel": { 14 18 "presets": [ ··· 42 46 "react-router-ga": "^1.0.0", 43 47 "react-scroll": "^1.7.15", 44 48 "react-static": "^7.2.3", 45 - "react-static-plugin-md-pages": "^0.1.1", 49 + "react-static-plugin-md-pages": "^0.1.3", 46 50 "styled-components": "^5.0.1" 47 51 }, 48 52 "devDependencies": { ··· 50 54 "@mdx-js/mdx": "^1.5.5", 51 55 "config": "^3.0.0", 52 56 "lodash": "^4.17.11", 53 - "react-static-plugin-mdx": "^7.2.2", 54 57 "react-static-plugin-react-router": "^7.2.3", 55 58 "react-static-plugin-sitemap": "^7.0.0", 56 - "react-static-plugin-styled-components": "^7.2.2" 59 + "react-static-plugin-styled-components": "^7.2.2", 60 + "surge": "^0.21.3" 57 61 } 58 62 }
+60
packages/site/scripts/deploy/aws.js
··· 1 + // TODO: Currently disabled to prevent oopsies 2 + process.exit(0); 3 + 4 + /** 5 + * Upload docs to appropriate s3 subdirectory. 6 + */ 7 + const path = require('path'); 8 + const chalk = require('chalk'); 9 + const execa = require('execa'); 10 + 11 + const PROJECT = 'urql'; 12 + const DOCS_PATH = `open-source/${PROJECT}`; 13 + 14 + const SRC = path.resolve(__dirname, '../../dist'); 15 + const BUCKET_NAME = 'formidable.com'; 16 + const DEST = `s3://${path.join(BUCKET_NAME, DOCS_PATH)}`; 17 + 18 + const AWS_DRY_RUN_FLAG = '--dryrun'; 19 + const AWS_EXCLUDES = ['*.DS_Store*']; 20 + 21 + // Cache values (in seconds) 22 + const CACHE_MAX_AGE_DEFAULT = 10 * 60; // eslint-disable-line no-magic-numbers 23 + 24 + const EXECA_OPTS = { 25 + stdio: 'inherit', 26 + }; 27 + 28 + const { log } = console; 29 + const logMsg = msg => log(chalk`[{cyan deploy/aws}] ${msg}`); 30 + 31 + const main = async ({ isDryRun }) => { 32 + logMsg(chalk`Uploading files to {cyan ${DEST}}`); 33 + await execa( 34 + 'aws', 35 + [ 36 + 's3', 37 + 'sync', 38 + isDryRun ? AWS_DRY_RUN_FLAG : '', 39 + '--cache-control', 40 + `max-age=${CACHE_MAX_AGE_DEFAULT},public`, 41 + '--delete', 42 + ...AWS_EXCLUDES.reduce( 43 + (memo, exc) => memo.concat(['--exclude', exc]), 44 + [] 45 + ), 46 + SRC, 47 + DEST, 48 + ].filter(Boolean), 49 + EXECA_OPTS 50 + ); 51 + }; 52 + 53 + if (require.main === module) { 54 + main({ 55 + isDryRun: process.argv.indexOf('--dryrun') > -1, 56 + }).catch(err => { 57 + console.error(err); // eslint-disable-line no-console 58 + process.exit(1); // eslint-disable-line no-process-exit 59 + }); 60 + }
+42
packages/site/scripts/deploy/surge.js
··· 1 + /** 2 + * Upload docs to surge. 3 + */ 4 + const path = require('path'); 5 + const chalk = require('chalk'); 6 + const execa = require('execa'); 7 + 8 + if (!process.env.SURGE_LOGIN) { 9 + console.warn('No SURGE_* env variables received. Skipping.'); 10 + process.exit(0); 11 + } 12 + 13 + const { CI_PULL_REQUEST = '' } = process.env; 14 + const parts = CI_PULL_REQUEST.split('/'); 15 + const PR_NUM = parts[parts.length - 1]; 16 + if (!PR_NUM) { 17 + console.warn('Not a pull request. Skipping'); 18 + process.exit(0); 19 + } 20 + 21 + const PROJECT = 'urql'; 22 + const SRC = path.resolve(__dirname, '../../dist'); 23 + const DOMAIN = `formidable-com-${PROJECT}-staging-${PR_NUM}.surge.sh`; 24 + 25 + const EXECA_OPTS = { 26 + stdio: 'inherit', 27 + }; 28 + 29 + const { log } = console; 30 + const logMsg = msg => log(chalk`[{cyan deploy/surge}] ${msg}`); 31 + 32 + const main = async () => { 33 + logMsg(chalk`Uploading files to {cyan ${DOMAIN}}`); 34 + await execa('yarn', ['run', 'surge', '--project', SRC, '--domain', DOMAIN], EXECA_OPTS); 35 + }; 36 + 37 + if (require.main === module) { 38 + main().catch(err => { 39 + console.error(err); // eslint-disable-line no-console 40 + process.exit(1); // eslint-disable-line no-process-exit 41 + }); 42 + }
+6 -7
packages/site/src/app.js
··· 8 8 import { GlobalStyle } from './styles/global'; 9 9 import * as theme from './styles/theme'; 10 10 import Analytics from './google-analytics'; 11 - 11 + import { Loading } from './components/loading'; 12 12 // TODO: import NotFound from './screens/404'; 13 13 14 14 const App = () => { 15 15 return ( 16 16 <Root> 17 - {/* TODO: create a better fallback component */} 18 - <React.Suspense fallback={<h1>Loading</h1>}> 19 - <ThemeProvider theme={theme}> 20 - <GlobalStyle /> 17 + <ThemeProvider theme={theme}> 18 + <GlobalStyle /> 19 + <React.Suspense fallback={<Loading />}> 21 20 <Analytics id={constants.googleAnalyticsId}> 22 21 <Routes /> 23 22 </Analytics> 24 - </ThemeProvider> 25 - </React.Suspense> 23 + </React.Suspense> 24 + </ThemeProvider> 26 25 </Root> 27 26 ); 28 27 };
+5 -3
packages/site/src/components/footer.js
··· 17 17 const FooterDescription = styled.p` 18 18 flex: 2; 19 19 font-size: 1.4rem; 20 - letter-spacing: 0.05rem; 21 20 line-height: 1.6; 22 21 margin: 2rem 0 0; 23 22 max-width: 56rem; ··· 29 28 } 30 29 & a { 31 30 color: white; 32 - letter-spacing: 0.05em; 33 31 transition: opacity 0.4s; 34 32 } 35 33 & a:hover { ··· 56 54 list-style: none; 57 55 padding: 0px 8px; 58 56 text-transform: uppercase; 57 + 59 58 & li { 60 - margin-bottom: 1.4rem; 59 + margin: 0.2rem 0; 61 60 } 61 + 62 62 & a { 63 63 color: white; 64 64 letter-spacing: 0.05em; 65 65 transition: opacity 0.4s; 66 66 } 67 + 67 68 & a:hover { 68 69 opacity: 0.7; 69 70 } 71 + 70 72 & a:visited { 71 73 color: white; 72 74 }
+2 -2
packages/site/src/components/header.js
··· 32 32 width: 12rem; 33 33 flex-direction: column; 34 34 color: #ffffff; 35 - p { 36 - } 35 + text-decoration: none; 37 36 `; 38 37 39 38 const HeaderText = styled.p` 40 39 text-transform: uppercase; 40 + font-size: 1.5rem; 41 41 margin-left: 14px; 42 42 line-height: 1.9rem; 43 43 margin-bottom: 0;
+85
packages/site/src/components/loading.js
··· 1 + import React from 'react'; 2 + import styled, { keyframes } from 'styled-components'; 3 + import { Container as DocsContainer } from '../screens/docs'; 4 + import Header from '../screens/docs/header'; 5 + 6 + const Container = styled.div` 7 + height: 100vh; 8 + width: 100%; 9 + `; 10 + const Loader = styled.div` 11 + position: relative; 12 + margin: 0 auto; 13 + width: ${p => p.theme.spacing.xl}; 14 + top: calc(50% - ${p => p.theme.spacing.xl}); 15 + &:before { 16 + content: ''; 17 + display: block; 18 + padding-top: 100%; 19 + } 20 + `; 21 + 22 + const rotate = keyframes` 23 + 100% { 24 + transform: rotate(360deg); 25 + } 26 + `; 27 + 28 + const dash = keyframes` 29 + 0% { 30 + stroke-dasharray: 1, 200; 31 + stroke-dashoffset: 0; 32 + } 33 + 50% { 34 + stroke-dasharray: 89, 200; 35 + stroke-dashoffset: -35px; 36 + } 37 + 100% { 38 + stroke-dasharray: 89, 200; 39 + stroke-dashoffset: -124px; 40 + } 41 + `; 42 + 43 + const Svg = styled.svg` 44 + animation: ${rotate} 2s linear infinite; 45 + height: 100%; 46 + transform-origin: center center; 47 + width: 100%; 48 + position: absolute; 49 + top: 0; 50 + bottom: 0; 51 + left: 0; 52 + right: 0; 53 + margin: auto; 54 + `; 55 + 56 + const Circle = styled.circle` 57 + stroke: ${p => p.theme.colors.accent}; 58 + stroke-dasharray: 1, 200; 59 + stroke-dashoffset: 0; 60 + animation: ${dash} 1.5s ease-in-out infinite; 61 + stroke-linecap: round; 62 + `; 63 + 64 + export const Loading = () => { 65 + return ( 66 + <DocsContainer> 67 + <Container> 68 + <Header /> 69 + <Loader> 70 + <Svg className="circular" viewBox="25 25 50 50"> 71 + <Circle 72 + className="path" 73 + cx="50" 74 + cy="50" 75 + r="20" 76 + fill="none" 77 + strokeWidth="2" 78 + strokeMiterlimit="10" 79 + /> 80 + </Svg> 81 + </Loader> 82 + </Container> 83 + </DocsContainer> 84 + ); 85 + };
+106 -16
packages/site/src/components/mdx.js
··· 1 1 import React from 'react'; 2 - import styled from 'styled-components'; 2 + import styled, { css } from 'styled-components'; 3 3 import { MDXProvider } from '@mdx-js/react'; 4 4 5 5 import Highlight, { Prism } from 'prism-react-renderer'; ··· 11 11 }; 12 12 13 13 const Pre = styled.pre` 14 - background: ${p => p.theme.colors.passiveBg}; 14 + background: ${p => p.theme.colors.codeBg}; 15 15 border: 1px solid ${p => p.theme.colors.border}; 16 - line-height: ${p => p.theme.lineHeights.code}; 17 - font-size: ${p => p.theme.fontSizes.code}; 18 - padding: ${p => p.theme.spacing.sm}; 19 16 border-radius: ${p => p.theme.spacing.xs}; 20 - -webkit-overflow-scrolling: touch; 17 + 18 + font-size: ${p => p.theme.fontSizes.code}; 19 + line-height: ${p => p.theme.lineHeights.code}; 20 + 21 + max-width: 100%; 21 22 overflow-x: auto; 23 + -webkit-overflow-scrolling: touch; 24 + padding: ${p => p.theme.spacing.sm}; 22 25 position: relative; 23 - max-width: 100%; 26 + white-space: pre; 24 27 `; 25 28 26 29 const Code = styled.code` ··· 32 35 white-space: pre; 33 36 `; 34 37 35 - const InlineCode = styled.code` 36 - background: ${p => p.theme.colors.passiveBg}; 38 + const InlineCode = styled(props => { 39 + const children = props.children.replace(/\\\|/g, '|'); 40 + return <code {...props}>{children}</code>; 41 + })` 42 + background: ${p => p.theme.colors.codeBg}; 37 43 color: ${p => p.theme.colors.code}; 38 44 font-family: ${p => p.theme.fonts.code}; 39 45 font-size: ${p => p.theme.fontSizes.small}; ··· 45 51 font-feature-settings: normal; 46 52 padding: 0 0.2em; 47 53 margin: 0; 54 + 55 + a > & { 56 + text-decoration: underline; 57 + } 58 + `; 59 + 60 + const InlineImage = styled.img` 61 + display: inline-block; 62 + margin: 0 ${p => p.theme.spacing.sm} ${p => p.theme.spacing.md} 0; 63 + padding: ${p => p.theme.spacing.xs} ${p => p.theme.spacing.sm}; 64 + border: 1px solid ${p => p.theme.colors.border}; 65 + border-radius: ${p => p.theme.spacing.xs}; 48 66 `; 49 67 50 68 const ImageWrapper = styled.div` ··· 69 87 display: block; 70 88 padding: ${p => p.theme.spacing.xs} ${p => p.theme.spacing.sm}; 71 89 border-top: 1px solid ${p => p.theme.colors.border}; 72 - background: ${p => p.theme.colors.passiveBg}; 90 + background: ${p => p.theme.colors.codeBg}; 73 91 font-size: ${p => p.theme.fontSizes.small}; 74 92 `; 75 93 76 - const Image = ({ alt, src }) => ( 77 - <ImageWrapper> 78 - <img alt={alt} src={src} /> 79 - <ImageAlt>{alt}</ImageAlt> 80 - </ImageWrapper> 81 - ); 94 + const Image = props => { 95 + const { height, width, alt, src } = props; 96 + if (height || width) return <InlineImage {...props} />; 97 + 98 + return ( 99 + <ImageWrapper> 100 + <img alt={alt} src={src} /> 101 + <ImageAlt>{alt}</ImageAlt> 102 + </ImageWrapper> 103 + ); 104 + }; 82 105 83 106 const HighlightCode = ({ className = '', children }) => { 84 107 const language = getLanguage(className); ··· 119 142 } 120 143 `; 121 144 145 + const sharedTableCellStyling = css` 146 + padding: ${p => p.theme.spacing.xs} ${p => p.theme.spacing.sm}; 147 + border-left: 1px solid ${p => p.theme.colors.passiveBg}; 148 + border-bottom: 1px solid ${p => p.theme.colors.passiveBg}; 149 + `; 150 + 151 + const TableHeader = styled.th` 152 + text-align: left; 153 + white-space: nowrap; 154 + ${sharedTableCellStyling} 155 + `; 156 + 157 + const TableCell = styled.td` 158 + width: min-content; 159 + ${sharedTableCellStyling} 160 + 161 + ${p => { 162 + const isCodeOnly = React.Children.toArray(p.children).every( 163 + x => x.props && x.props.mdxType === 'inlineCode' 164 + ); 165 + return ( 166 + isCodeOnly && 167 + css` 168 + background-color: ${p.theme.colors.codeBg}; 169 + 170 + & > ${InlineCode} { 171 + background: none; 172 + padding: 0; 173 + margin: 0; 174 + } 175 + ` 176 + ); 177 + }} 178 + 179 + &:last-child { 180 + min-width: 20rem; 181 + width: max-content; 182 + } 183 + 184 + &:first-child { 185 + white-space: nowrap; 186 + } 187 + 188 + &:nth-child(2) { 189 + overflow-wrap: break-word; 190 + min-width: 20rem; 191 + } 192 + `; 193 + 194 + const TableOverflow = styled.div` 195 + overflow-x: scroll; 196 + `; 197 + 198 + const Table = styled.table` 199 + border: 1px solid ${p => p.theme.colors.passiveBg}; 200 + border-collapse: collapse; 201 + `; 202 + 203 + const TableWithOverflow = props => ( 204 + <TableOverflow> 205 + <Table {...props} /> 206 + </TableOverflow> 207 + ); 208 + 122 209 const components = { 123 210 pre: Pre, 124 211 img: Image, 125 212 blockquote: Blockquote, 126 213 inlineCode: InlineCode, 127 214 code: HighlightCode, 215 + table: TableWithOverflow, 216 + th: TableHeader, 217 + td: TableCell, 128 218 }; 129 219 130 220 export const MDXComponents = ({ children }) => (
+13 -7
packages/site/src/components/navigation.js
··· 4 4 export const SidebarContainer = styled.div` 5 5 display: ${p => (p.hidden ? 'none' : 'block')}; 6 6 position: absolute; 7 + 7 8 @media ${({ theme }) => theme.media.sm} { 8 9 display: block; 9 - position: static; 10 + position: relative; 10 11 width: ${p => p.theme.layout.sidebar}; 11 12 } 12 13 `; ··· 14 15 export const SideBarStripes = styled.div` 15 16 border-left: ${p => p.theme.layout.stripes} solid #8196ff; 16 17 border-right: ${p => p.theme.layout.stripes} solid #bcc6fa; 18 + position: absolute; 17 19 height: 100%; 18 20 width: 0; 19 - position: fixed; 20 21 left: 0; 21 22 top: 0; 22 23 bottom: 0; ··· 34 35 z-index: 1; 35 36 overflow-y: scroll; 36 37 min-height: 100%; 37 - width: 100%; 38 - padding: ${p => p.theme.spacing.md}; 39 - padding-right: ${p => p.theme.spacing.sm}; 40 - background: ${p => p.theme.colors.bg}; 41 38 line-height: ${p => p.theme.lineHeights.body}; 42 39 font-size: ${p => p.theme.fontSizes.small}; 43 40 41 + padding: ${p => p.theme.spacing.md}; 42 + padding-right: ${p => p.theme.spacing.sm}; 43 + 44 + background-color: ${p => p.theme.colors.bg}; 45 + border-right: 1px solid ${p => p.theme.colors.border}; 46 + border-top: 1px solid ${p => p.theme.colors.border}; 47 + 44 48 @media ${({ theme }) => theme.media.sm} { 49 + border: none; 50 + background: none; 51 + padding-top: ${p => p.theme.spacing.lg}; 45 52 width: ${p => p.theme.layout.sidebar}; 46 53 } 47 54 `; ··· 57 64 58 65 export const SidebarNavSubItemWrapper = styled.div` 59 66 padding-left: ${p => p.theme.spacing.sm}; 60 - border-left: 1px solid ${p => p.theme.colors.activeBorder}; 61 67 margin-bottom: ${p => p.theme.spacing.xs}; 62 68 `; 63 69
+1 -1
packages/site/src/components/section-title.js
··· 2 2 3 3 export const SectionTitle = styled.h2` 4 4 color: #fff; 5 - font-size: 2.5rem; 5 + font-size: 4.5rem; 6 6 flex: auto; 7 7 line-height: 1.3; 8 8 margin: 2rem 0 3rem;
+10 -1
packages/site/src/components/sidebar.js
··· 24 24 width: ${p => p.theme.layout.logo}; 25 25 margin-bottom: ${p => p.theme.spacing.sm}; 26 26 align-self: center; 27 + 27 28 @media ${p => p.theme.media.sm} { 28 29 display: block; 29 30 } ··· 34 35 flex-direction: column; 35 36 padding-top: ${p => p.theme.spacing.xs}; 36 37 padding-bottom: ${p => p.theme.spacing.lg}; 38 + padding-left: ${p => p.theme.spacing.sm}; 37 39 `; 38 40 39 41 const relative = (from, to) => { 40 42 if (!from || !to) return null; 41 43 let pathname = path.relative(path.dirname(from), to); 44 + if (!pathname) 45 + pathname = path.join(path.relative(from, to), path.basename(to)); 42 46 if (from.endsWith('/')) pathname = '../' + pathname; 43 47 return { pathname }; 44 48 }; ··· 53 57 return null; 54 58 } 55 59 56 - return tree.children.map(page => { 60 + let children = tree.children; 61 + if (tree.frontmatter && tree.originalPath) { 62 + children = [{ ...tree, children: undefined }, ...children]; 63 + } 64 + 65 + return children.map(page => { 57 66 return ( 58 67 <Fragment key={page.key}> 59 68 <SidebarNavItem to={relative(pathname, page.path)}>
+34 -21
packages/site/src/screens/docs/article.js
··· 10 10 flex: 1; 11 11 width: 100%; 12 12 position: sticky; 13 - 14 - display: ${p => (p.sidebarOpen ? 'none' : 'flex')}; 13 + display: flex; 15 14 flex-direction: row-reverse; 16 15 `; 17 16 ··· 20 19 }))` 21 20 flex: 1; 22 21 min-height: 100vh; 23 - margin: 0 ${p => p.theme.spacing.md}; 24 - padding: ${p => p.theme.spacing.md} 0; 22 + background: ${p => p.theme.colors.bg}; 23 + padding: ${p => p.theme.spacing.md}; 24 + 25 + @media ${p => p.theme.media.lg} { 26 + padding: ${p => p.theme.spacing.lg}; 27 + } 28 + 29 + overflow-wrap: break-word; 30 + word-wrap: break-word; 31 + word-break: break-word; 32 + hyphens: auto; 25 33 `; 26 34 27 35 const Legend = styled.aside` ··· 32 40 position: sticky; 33 41 top: ${p => p.theme.layout.header}; 34 42 max-height: 100vh; 35 - width: ${p => p.theme.layout.legend}; 36 - max-width: ${p => p.theme.layout.legendMaxWidth}; 37 - padding: ${p => p.theme.spacing.md} 0; 38 - margin: ${p => p.theme.spacing.sm} ${p => p.theme.spacing.md}; 43 + width: 100%; 44 + max-width: ${p => p.theme.layout.legend}; 45 + margin: 0 ${p => p.theme.spacing.md}; 46 + padding: ${p => p.theme.spacing.lg} 0; 39 47 } 40 48 `; 41 49 ··· 48 56 const HeadingList = styled.ul` 49 57 list-style-type: none; 50 58 margin: 0; 51 - padding-left: ${p => p.theme.spacing.sm}; 52 - border-left: 1px solid ${p => p.theme.colors.border}; 59 + padding: 0; 53 60 `; 54 61 55 - const HeadingItem = styled.a` 56 - font-size: ${p => p.theme.fontSizes.small}; 57 - font-weight: ${p => p.theme.fontWeights.body}; 58 - color: ${p => p.theme.colors.heading}; 59 - text-decoration: none; 60 - opacity: 0.7; 62 + const HeadingItem = styled.li` 63 + line-height: ${p => p.theme.lineHeights.heading}; 64 + margin-bottom: ${p => p.theme.spacing.xs}; 65 + 66 + > a { 67 + font-size: ${p => p.theme.fontSizes.small}; 68 + font-weight: ${p => p.theme.fontWeights.body}; 69 + color: ${p => p.theme.colors.heading}; 70 + text-decoration: none; 71 + opacity: 0.7; 72 + } 61 73 `; 62 74 63 75 const SectionList = () => { ··· 65 77 if (!page) return null; 66 78 67 79 const headings = page.headings.filter(x => x.depth > 1); 80 + if (headings.length === 0) return null; 68 81 69 82 return ( 70 83 <> 71 84 <LegendTitle>In this section</LegendTitle> 72 85 <HeadingList> 73 86 {headings.map(heading => ( 74 - <li key={heading.slug}> 75 - <HeadingItem href={`#${heading.slug}`}>{heading.value}</HeadingItem> 76 - </li> 87 + <HeadingItem key={heading.slug}> 88 + <a href={`#${heading.slug}`}>{heading.value}</a> 89 + </HeadingItem> 77 90 ))} 78 91 </HeadingList> 79 92 </> 80 93 ); 81 94 }; 82 95 83 - const Article = ({ children, sidebarOpen }) => ( 84 - <Container sidebarOpen={sidebarOpen}> 96 + const Article = ({ children }) => ( 97 + <Container> 85 98 <Legend> 86 99 <SectionList /> 87 100 </Legend>
+7 -10
packages/site/src/screens/docs/header.js
··· 4 4 5 5 import formidableLogo from '../../assets/logos/logo-formidable.svg'; 6 6 7 - const Fixed = styled.div` 7 + const Fixed = styled.header` 8 8 position: fixed; 9 9 top: 0; 10 10 left: 0; 11 11 right: 0; 12 12 width: 100%; 13 13 z-index: 1; 14 + 15 + background: ${p => p.theme.colors.bg}; 16 + border-bottom: 1px solid ${p => p.theme.colors.border}; 17 + padding: 0 ${p => p.theme.spacing.md}; 18 + box-shadow: ${p => p.theme.shadows.header}; 14 19 `; 15 20 16 - const Wrapper = styled.header` 21 + const Wrapper = styled.div` 17 22 width: 100%; 18 23 max-width: ${p => p.theme.layout.page}; 19 24 margin: 0 auto; 20 - background: ${p => p.theme.colors.bg}; 21 - 22 25 height: ${p => p.theme.layout.header}; 23 - padding: 0 ${p => p.theme.spacing.md}; 24 26 padding-top: 2px; 25 - 26 - border-bottom: 1px solid ${p => p.theme.colors.border}; 27 - border-left: 1px solid ${p => p.theme.colors.border}; 28 - border-right: 1px solid ${p => p.theme.colors.border}; 29 - 30 27 display: flex; 31 28 flex-direction: row; 32 29 align-items: center;
+4 -8
packages/site/src/screens/docs/index.js
··· 10 10 import burger from '../../assets/burger.svg'; 11 11 import closeButton from '../../assets/close.svg'; 12 12 13 - const Container = styled.div` 13 + export const Container = styled.div` 14 14 display: flex; 15 15 flex-direction: row; 16 16 17 17 width: 100%; 18 18 max-width: ${p => p.theme.layout.page}; 19 19 margin: 0 auto; 20 - 21 - background: ${p => p.theme.colors.bg}; 22 - border-left: 1px solid ${p => p.theme.colors.border}; 23 - border-right: 1px solid ${p => p.theme.colors.border}; 24 - 25 20 margin-top: ${p => p.theme.layout.header}; 26 21 `; 27 22 ··· 31 26 cursor: pointer; 32 27 display: block; 33 28 margin: ${p => p.theme.spacing.sm} ${p => p.theme.spacing.md}; 34 - position: absolute; 29 + position: fixed; 35 30 right: 0; 36 31 top: 0; 37 32 z-index: 1; 33 + 38 34 @media ${p => p.theme.media.sm} { 39 35 display: none; 40 36 } ··· 52 48 onClick={() => setSidebarOpen(prev => !prev)} 53 49 /> 54 50 <Sidebar sidebarOpen={sidebarOpen} /> 55 - <Article sidebarOpen={sidebarOpen}>{props.children}</Article> 51 + <Article>{props.children}</Article> 56 52 </Container> 57 53 </> 58 54 );
+3 -7
packages/site/src/screens/home/hero.js
··· 28 28 29 29 const HeroTitle = styled.h1` 30 30 font-size: 5rem; 31 - letter-spacing: 0.15em; 32 31 margin: 0 0 2rem; 33 32 text-align: center; 34 33 text-transform: uppercase; 35 34 width: 100%; 35 + color: #fff; 36 + 36 37 @media (min-width: 768px) { 37 38 font-size: 5.8rem; 38 39 margin: 4rem 0 2rem; ··· 41 42 `; 42 43 43 44 const HeroBody = styled.p` 44 - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 45 - letter-spacing: 0.08em; 46 45 font-size: 2rem; 47 46 line-height: 3rem; 48 47 text-align: left; ··· 115 114 padding: 0.33rem 1.5rem; 116 115 line-height: 3.44rem; 117 116 font-size: 14px; 118 - letter-spacing: 0.2px; 119 117 margin: 0; 120 118 `; 121 119 const HeroNPMButton = styled.button` ··· 127 125 font-style: normal; 128 126 font-stretch: normal; 129 127 line-height: normal; 130 - letter-spacing: 1px; 131 128 color: #383838; 132 129 border: 0; 133 130 text-transform: uppercase; ··· 146 143 line-height: 4rem; 147 144 text-align: center; 148 145 text-transform: uppercase; 149 - letter-spacing: 1px; 150 146 color: #383838; 151 147 border: 0; 152 148 margin-top: 5rem; ··· 186 182 color: white; 187 183 display: inline-block; 188 184 font-size: 1.7rem; 189 - letter-spacing: 0.05em; 190 185 transition: opacity 0.4s; 191 186 text-transform: uppercase; 187 + text-decoration: none; 192 188 } 193 189 & li a:hover { 194 190 color: #8196ff;
+1 -1
packages/site/src/styles/global.js
··· 44 44 font-weight: ${p => p.theme.fontWeights.links}; 45 45 } 46 46 47 - p, h1, h2, h3 { 47 + table, pre, p, h1, h2, h3 { 48 48 margin: 0 0 ${p => p.theme.spacing.md} 0; 49 49 } 50 50
+5 -4
packages/site/src/styles/theme.js
··· 14 14 ]; 15 15 16 16 export const colors = { 17 - passiveBg: '#f6f6f6', 17 + passiveBg: '#f2f2f2', 18 + codeBg: '#f0f7fb', 18 19 bg: '#ffffff', 19 20 border: '#ececec', 20 21 activeBorder: '#a2b1ff', ··· 28 29 export const layout = { 29 30 page: '144rem', 30 31 header: '4.8rem', 31 - stripes: '0.9rem', 32 + stripes: '1rem', 32 33 sidebar: '28rem', 33 - legend: '100%', 34 - legendMaxWidth: '25rem', 34 + legend: '22rem', 35 35 logo: '12rem', 36 36 }; 37 37 ··· 63 63 }; 64 64 65 65 export const shadows = { 66 + header: 'rgba(0, 0, 0, 0.09) 0px 2px 10px -3px', 66 67 input: 'rgba(0, 0, 0, 0.09) 0px 2px 10px -3px', 67 68 }; 68 69
+7 -1
packages/site/static.config.js
··· 1 + import * as os from 'os'; 1 2 import { resolve } from 'path'; 2 3 import constants from './src/constants'; 3 4 import Document from './src/html'; 5 + 6 + const isStaging = process.env.REACT_STATIC_STAGING === 'true'; 7 + const basePath = 'open-source/urql'; 4 8 5 9 export default { 6 10 plugins: [ ··· 22 26 23 27 paths: { 24 28 src: 'src', 25 - dist: 'dist', 29 + dist: isStaging ? `dist/${basePath}` : 'dist', 26 30 buildArtifacts: 'node_modules/.cache/react-static/artifacts/', 27 31 devDist: 'node_modules/.cache/react-static/dist/', 28 32 temp: 'node_modules/.cache/react-static/temp/', ··· 37 41 getSiteData: () => ({ 38 42 title: constants.docsTitle, 39 43 }), 44 + 45 + maxThreads: Math.min(8, os.cpus().length / 2), 40 46 41 47 getRoutes: async () => [ 42 48 {
+3 -3
scripts/rollup/build.js
··· 6 6 7 7 const workspaceRoot = path.resolve(__dirname, '../../'); 8 8 9 - let packages = glob('{packages,exchanges}/*/package.json').map(pkg => { 10 - return path.resolve(pkg, '../'); 11 - }); 9 + let packages = glob('{packages,exchanges}/*/package.json') 10 + .filter(pkg => !require(path.join(workspaceRoot, pkg)).private) 11 + .map(pkg => path.resolve(pkg, '../')); 12 12 13 13 // CircleCI parallelism 14 14 // See: https://github.com/facebook/react/blob/901d76bc5c8dcd0fa15bb32d1dfe05709aa5d273/scripts/rollup/build.js#L705-L710
+317 -37
yarn.lock
··· 1448 1448 version "2.0.0" 1449 1449 resolved "https://registry.yarnpkg.com/@kristoferbaxter/estree-walker/-/estree-walker-2.0.0.tgz#52ee40f9f8b59bbf00cac477a777aeb830ea5952" 1450 1450 1451 - "@mdx-js/loader@^1.3.0": 1452 - version "1.5.5" 1453 - resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.5.5.tgz#b658534153b3faab8f93ffc790c868dacc5b43d3" 1454 - integrity sha512-2/2WX73qj79Kv2cYk14kQsN/aypAH3RPzuNMx1gxwZjj77G0N6tzhM9WFkEDM/SXjasWep03ZmSRb9d//b2D8w== 1455 - dependencies: 1456 - "@mdx-js/mdx" "^1.5.5" 1457 - "@mdx-js/react" "^1.5.5" 1458 - loader-utils "1.2.3" 1459 - 1460 1451 "@mdx-js/mdx@^1.5.5": 1461 1452 version "1.5.5" 1462 1453 resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.5.5.tgz#09dc8932af84e5baf5add2625ad0250a117c3363" ··· 2407 2398 resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.0.0.tgz#9e22d6783f0ab66a1cf330e21a905e39b3b3a975" 2408 2399 integrity sha512-zyCMBDl4m71feawrxYcVbHxv/UUkqm4nKJiLu3+l9lfiQha6jQ/9dxhrXLnzzBXVFqCTDwiUkZOz9XFbdEGQsg== 2409 2400 2401 + async@0.2.x, async@~0.2.9: 2402 + version "0.2.10" 2403 + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" 2404 + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= 2405 + 2410 2406 async@^2.6.2: 2411 2407 version "2.6.3" 2412 2408 resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" ··· 2705 2701 blob@0.0.5: 2706 2702 version "0.0.5" 2707 2703 resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" 2704 + 2705 + block-stream@*: 2706 + version "0.0.9" 2707 + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" 2708 + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= 2709 + dependencies: 2710 + inherits "~2.0.0" 2708 2711 2709 2712 bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: 2710 2713 version "3.7.2" ··· 3315 3318 resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.2.0.tgz#e8b988d9206c692302d8ee834e7a85c0144d8f77" 3316 3319 integrity sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ== 3317 3320 3321 + cli-table3@^0.5.1: 3322 + version "0.5.1" 3323 + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" 3324 + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== 3325 + dependencies: 3326 + object-assign "^4.1.0" 3327 + string-width "^2.1.1" 3328 + optionalDependencies: 3329 + colors "^1.1.2" 3330 + 3318 3331 cli-truncate@^0.2.1: 3319 3332 version "0.2.1" 3320 3333 resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" ··· 3453 3466 color-convert "^1.9.1" 3454 3467 color-string "^1.5.2" 3455 3468 3469 + colors@0.6.x: 3470 + version "0.6.2" 3471 + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" 3472 + integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= 3473 + 3456 3474 colors@1.1.2: 3457 3475 version "1.1.2" 3458 3476 resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" 3459 3477 integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= 3478 + 3479 + colors@^1.1.2: 3480 + version "1.4.0" 3481 + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 3482 + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== 3460 3483 3461 3484 combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: 3462 3485 version "1.0.8" ··· 4067 4090 dependencies: 4068 4091 array-find-index "^1.0.1" 4069 4092 4093 + cycle@1.0.x: 4094 + version "1.0.3" 4095 + resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" 4096 + integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= 4097 + 4070 4098 cyclist@^1.0.1: 4071 4099 version "1.0.1" 4072 4100 resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" ··· 4197 4225 dedent@^0.7.0: 4198 4226 version "0.7.0" 4199 4227 resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" 4228 + 4229 + deep-equal@*: 4230 + version "2.0.1" 4231 + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.1.tgz#fc12bbd6850e93212f21344748682ccc5a8813cf" 4232 + integrity sha512-7Et6r6XfNW61CPPCIYfm1YPGSmh6+CliYeL4km7GWJcpX5LTAflGF8drLLR+MZX+2P3NZfAfSduutBbSWqER4g== 4233 + dependencies: 4234 + es-abstract "^1.16.3" 4235 + es-get-iterator "^1.0.1" 4236 + is-arguments "^1.0.4" 4237 + is-date-object "^1.0.1" 4238 + is-regex "^1.0.4" 4239 + isarray "^2.0.5" 4240 + object-is "^1.0.1" 4241 + object-keys "^1.1.1" 4242 + regexp.prototype.flags "^1.2.0" 4243 + side-channel "^1.0.1" 4244 + which-boxed-primitive "^1.0.1" 4245 + which-collection "^1.0.0" 4200 4246 4201 4247 deep-equal@^1.0.1: 4202 4248 version "1.1.1" ··· 4737 4783 dependencies: 4738 4784 is-arrayish "^0.2.1" 4739 4785 4740 - es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: 4786 + es-abstract@^1.16.3, es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: 4741 4787 version "1.17.4" 4742 4788 resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" 4743 4789 dependencies: ··· 4753 4799 string.prototype.trimleft "^2.1.1" 4754 4800 string.prototype.trimright "^2.1.1" 4755 4801 4802 + es-get-iterator@^1.0.1: 4803 + version "1.1.0" 4804 + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" 4805 + integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== 4806 + dependencies: 4807 + es-abstract "^1.17.4" 4808 + has-symbols "^1.0.1" 4809 + is-arguments "^1.0.4" 4810 + is-map "^2.0.1" 4811 + is-set "^2.0.1" 4812 + is-string "^1.0.5" 4813 + isarray "^2.0.5" 4814 + 4756 4815 es-to-primitive@^1.2.1: 4757 4816 version "1.2.1" 4758 4817 resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" ··· 5224 5283 version "1.4.0" 5225 5284 resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" 5226 5285 5286 + eyes@0.1.x: 5287 + version "0.1.8" 5288 + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" 5289 + integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= 5290 + 5227 5291 fast-deep-equal@^2.0.1: 5228 5292 version "2.0.1" 5229 5293 resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" ··· 5618 5682 version "2.1.2" 5619 5683 resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" 5620 5684 5685 + fstream@>=1.0.12: 5686 + version "1.0.12" 5687 + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" 5688 + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== 5689 + dependencies: 5690 + graceful-fs "^4.1.2" 5691 + inherits "~2.0.0" 5692 + mkdirp ">=0.5 0" 5693 + rimraf "2" 5694 + 5621 5695 function-bind@^1.1.1: 5622 5696 version "1.1.1" 5623 5697 resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" ··· 6297 6371 slash "^3.0.0" 6298 6372 which-pm-runs "^1.0.0" 6299 6373 6374 + i@0.3.x: 6375 + version "0.3.6" 6376 + resolved "https://registry.yarnpkg.com/i/-/i-0.3.6.tgz#d96c92732076f072711b6b10fd7d4f65ad8ee23d" 6377 + integrity sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0= 6378 + 6300 6379 iconv-lite@0.4.24, iconv-lite@^0.4.24: 6301 6380 version "0.4.24" 6302 6381 resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" ··· 6397 6476 once "^1.3.0" 6398 6477 wrappy "1" 6399 6478 6400 - inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: 6479 + inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: 6401 6480 version "2.0.4" 6402 6481 resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 6403 6482 ··· 6427 6506 figures "^2.0.0" 6428 6507 run-async "^2.3.0" 6429 6508 6430 - inquirer@^6.5.1: 6509 + inquirer@^6.2.2, inquirer@^6.5.1: 6431 6510 version "6.5.2" 6432 6511 resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" 6433 6512 dependencies: ··· 6567 6646 version "0.3.2" 6568 6647 resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" 6569 6648 6649 + is-bigint@^1.0.0: 6650 + version "1.0.0" 6651 + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" 6652 + integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== 6653 + 6570 6654 is-binary-path@^1.0.0: 6571 6655 version "1.0.1" 6572 6656 resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" ··· 6579 6663 dependencies: 6580 6664 binary-extensions "^2.0.0" 6581 6665 6582 - is-boolean-object@^1.0.1: 6666 + is-boolean-object@^1.0.0, is-boolean-object@^1.0.1: 6583 6667 version "1.0.1" 6584 6668 resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" 6585 6669 integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== ··· 6662 6746 resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" 6663 6747 integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== 6664 6748 6749 + is-domain@0.0.1: 6750 + version "0.0.1" 6751 + resolved "https://registry.yarnpkg.com/is-domain/-/is-domain-0.0.1.tgz#7ffb288d5cced6b07c4f2df91c9be9153511348e" 6752 + integrity sha1-f/sojVzO1rB8Ty35HJvpFTURNI4= 6753 + 6665 6754 is-extendable@^0.1.0, is-extendable@^0.1.1: 6666 6755 version "0.1.1" 6667 6756 resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" ··· 6718 6807 version "1.0.4" 6719 6808 resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" 6720 6809 6810 + is-map@^2.0.1: 6811 + version "2.0.1" 6812 + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" 6813 + integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== 6814 + 6721 6815 is-module@^1.0.0: 6722 6816 version "1.0.0" 6723 6817 resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" ··· 6732 6826 version "4.0.1" 6733 6827 resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" 6734 6828 6735 - is-number-object@^1.0.4: 6829 + is-number-object@^1.0.3, is-number-object@^1.0.4: 6736 6830 version "1.0.4" 6737 6831 resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" 6738 6832 integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== ··· 6843 6937 version "1.2.0" 6844 6938 resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" 6845 6939 6940 + is-set@^2.0.1: 6941 + version "2.0.1" 6942 + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" 6943 + integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== 6944 + 6846 6945 is-stream@^1.1.0: 6847 6946 version "1.1.0" 6848 6947 resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" ··· 6851 6950 version "2.0.0" 6852 6951 resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" 6853 6952 6854 - is-string@^1.0.5: 6953 + is-string@^1.0.4, is-string@^1.0.5: 6855 6954 version "1.0.5" 6856 6955 resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" 6857 6956 ··· 6876 6975 version "1.0.0" 6877 6976 resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 6878 6977 6978 + is-weakmap@^2.0.1: 6979 + version "2.0.1" 6980 + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" 6981 + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== 6982 + 6983 + is-weakset@^2.0.1: 6984 + version "2.0.1" 6985 + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" 6986 + integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== 6987 + 6879 6988 is-whitespace-character@^1.0.0: 6880 6989 version "1.0.4" 6881 6990 resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" ··· 6908 7017 version "2.0.1" 6909 7018 resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" 6910 7019 7020 + isarray@^2.0.5: 7021 + version "2.0.5" 7022 + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" 7023 + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== 7024 + 6911 7025 isexe@^2.0.0: 6912 7026 version "2.0.0" 6913 7027 resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" ··· 6930 7044 node-fetch "^2.2.0" 6931 7045 unfetch "^4.0.0" 6932 7046 6933 - isstream@~0.1.2: 7047 + isstream@0.1.x, isstream@~0.1.2: 6934 7048 version "0.1.2" 6935 7049 resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 6936 7050 ··· 7662 7776 loader-utils@1.2.3: 7663 7777 version "1.2.3" 7664 7778 resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" 7779 + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== 7665 7780 dependencies: 7666 7781 big.js "^5.2.2" 7667 7782 emojis-list "^2.0.0" ··· 8211 8326 version "1.0.1" 8212 8327 resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" 8213 8328 8214 - minimatch@3.0.4, minimatch@^3.0.4: 8329 + minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.4: 8215 8330 version "3.0.4" 8216 8331 resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 8217 8332 dependencies: ··· 8227 8342 minimist@0.0.8: 8228 8343 version "0.0.8" 8229 8344 resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 8345 + 8346 + minimist@1.1.1: 8347 + version "1.1.1" 8348 + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.1.tgz#1bc2bc71658cdca5712475684363615b0b4f695b" 8349 + integrity sha1-G8K8cWWM3KVxJHVoQ2NhWwtPaVs= 8230 8350 8231 8351 minimist@1.x, minimist@^1.1.1, minimist@^1.2.0: 8232 8352 version "1.2.0" ··· 8258 8378 version "0.3.5" 8259 8379 resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.3.5.tgz#304652cdaf24a3df0487205e61ac6162c6906ddd" 8260 8380 8261 - mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.1, mkdirp@~0.5.1: 8381 + mkdirp@0.5.1, mkdirp@0.x, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1: 8262 8382 version "0.5.1" 8263 8383 resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 8264 8384 dependencies: 8265 8385 minimist "0.0.8" 8386 + 8387 + moniker@0.1.2: 8388 + version "0.1.2" 8389 + resolved "https://registry.yarnpkg.com/moniker/-/moniker-0.1.2.tgz#872dfba575dcea8fa04a5135b13d5f24beccc97e" 8390 + integrity sha1-hy37pXXc6o+gSlE1sT1fJL7MyX4= 8266 8391 8267 8392 moo@^0.5.0: 8268 8393 version "0.5.1" ··· 8311 8436 version "0.0.7" 8312 8437 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 8313 8438 8314 - mute-stream@0.0.8: 8439 + mute-stream@0.0.8, mute-stream@~0.0.4: 8315 8440 version "0.0.8" 8316 8441 resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" 8317 8442 ··· 8347 8472 version "1.4.0" 8348 8473 resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 8349 8474 8475 + ncp@0.4.x: 8476 + version "0.4.2" 8477 + resolved "https://registry.yarnpkg.com/ncp/-/ncp-0.4.2.tgz#abcc6cbd3ec2ed2a729ff6e7c1fa8f01784a8574" 8478 + integrity sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ= 8479 + 8350 8480 nearley@^2.7.10: 8351 8481 version "2.19.1" 8352 8482 resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.1.tgz#4af4006e16645ff800e9f993c3af039857d9dbdc" ··· 8365 8495 neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: 8366 8496 version "2.6.1" 8367 8497 resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" 8498 + 8499 + netrc@0.1.4: 8500 + version "0.1.4" 8501 + resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" 8502 + integrity sha1-a+lPysqNd63gqWcNxGCRTJRHJEQ= 8368 8503 8369 8504 next-tick@~1.0.0: 8370 8505 version "1.0.0" ··· 9219 9354 dependencies: 9220 9355 find-up "^3.0.0" 9221 9356 9357 + pkginfo@0.3.x: 9358 + version "0.3.1" 9359 + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.3.1.tgz#5b29f6a81f70717142e09e765bbeab97b4f81e21" 9360 + integrity sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE= 9361 + 9362 + pkginfo@0.x.x: 9363 + version "0.4.1" 9364 + resolved "https://registry.yarnpkg.com/pkginfo/-/pkginfo-0.4.1.tgz#b5418ef0439de5425fc4995042dced14fb2a84ff" 9365 + integrity sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8= 9366 + 9222 9367 please-upgrade-node@^3.2.0: 9223 9368 version "3.2.0" 9224 9369 resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" ··· 9939 10084 version "0.11.10" 9940 10085 resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" 9941 10086 10087 + progress@1.1.8: 10088 + version "1.1.8" 10089 + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" 10090 + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= 10091 + 9942 10092 progress@^2.0.0, progress@^2.0.3: 9943 10093 version "2.0.3" 9944 10094 resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" ··· 9953 10103 integrity sha1-SJZUxpJha4qlWwck+oCbt9tJxb8= 9954 10104 dependencies: 9955 10105 asap "~2.0.3" 10106 + 10107 + prompt@~0.2.14: 10108 + version "0.2.14" 10109 + resolved "https://registry.yarnpkg.com/prompt/-/prompt-0.2.14.tgz#57754f64f543fd7b0845707c818ece618f05ffdc" 10110 + integrity sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w= 10111 + dependencies: 10112 + pkginfo "0.x.x" 10113 + read "1.0.x" 10114 + revalidator "0.1.x" 10115 + utile "0.2.x" 10116 + winston "0.8.x" 9956 10117 9957 10118 prompts@^1.1.1: 9958 10119 version "1.2.1" ··· 10301 10462 dependencies: 10302 10463 object-is "^1.0.2" 10303 10464 10304 - react-static-plugin-md-pages@^0.1.1: 10305 - version "0.1.1" 10306 - resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.1.tgz#7cb19dad439eb81e820d17f48e11a948b248bb93" 10307 - integrity sha512-g374YtRDgPK6dIMRXaRbFuOYwXta/kASUVAY3rc+z7/YIiCqyjkS1Dkt/pyBAaTaXGsrXI4wZcIUxgN5Z0Fz3w== 10465 + react-static-plugin-md-pages@^0.1.3: 10466 + version "0.1.3" 10467 + resolved "https://registry.yarnpkg.com/react-static-plugin-md-pages/-/react-static-plugin-md-pages-0.1.3.tgz#2c019e9e5e407b5d7701669b9b4581d506662ab5" 10468 + integrity sha512-aONsWlmeZECJgAz5Er7Jw83RlGLXgf08OFMmn2e3+BADhbheKp7ZWvC50l3AP8jwRr6wslxCO/2kwx99GC0u6A== 10308 10469 dependencies: 10309 10470 "@mdx-js/mdx" "^1.5.5" 10310 10471 "@mdx-js/react" "^1.5.5" ··· 10320 10481 unist-util-select "^3.0.1" 10321 10482 unist-util-visit "^2.0.2" 10322 10483 yaml "^1.7.2" 10323 - 10324 - react-static-plugin-mdx@^7.2.2: 10325 - version "7.2.2" 10326 - resolved "https://registry.yarnpkg.com/react-static-plugin-mdx/-/react-static-plugin-mdx-7.2.2.tgz#c90884103ea1c6007a502bf0efadd18e4be4f4a7" 10327 - integrity sha512-f0T/vq5cryIRvztt2tjpp3AjNdvR1krxl9ADR27AVK2StnFk2T20HraVsTdFDa1FUDrYwRP07JczUZV9gcFtzw== 10328 - dependencies: 10329 - "@mdx-js/loader" "^1.3.0" 10330 10484 10331 10485 react-static-plugin-react-router@^7.2.3: 10332 10486 version "7.2.3" ··· 10496 10650 pify "^4.0.1" 10497 10651 strip-bom "^3.0.0" 10498 10652 10653 + read@1.0.5: 10654 + version "1.0.5" 10655 + resolved "https://registry.yarnpkg.com/read/-/read-1.0.5.tgz#007a3d169478aa710a491727e453effb92e76203" 10656 + integrity sha1-AHo9FpR4qnEKSRcn5FPv+5LnYgM= 10657 + dependencies: 10658 + mute-stream "~0.0.4" 10659 + 10660 + read@1.0.x: 10661 + version "1.0.7" 10662 + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" 10663 + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= 10664 + dependencies: 10665 + mute-stream "~0.0.4" 10666 + 10499 10667 "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: 10500 10668 version "2.3.7" 10501 10669 resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" ··· 10902 11070 version "0.12.0" 10903 11071 resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" 10904 11072 11073 + revalidator@0.1.x: 11074 + version "0.1.8" 11075 + resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" 11076 + integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs= 11077 + 10905 11078 rework-visit@1.0.0: 10906 11079 version "1.0.0" 10907 11080 resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" ··· 10927 11100 version "1.0.0" 10928 11101 resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" 10929 11102 10930 - rimraf@2.6.3: 10931 - version "2.6.3" 10932 - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 11103 + rimraf@2, rimraf@2.x.x, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: 11104 + version "2.7.1" 11105 + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 10933 11106 dependencies: 10934 11107 glob "^7.1.3" 10935 11108 10936 - rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: 10937 - version "2.7.1" 10938 - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 11109 + rimraf@2.6.3: 11110 + version "2.6.3" 11111 + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 10939 11112 dependencies: 10940 11113 glob "^7.1.3" 10941 11114 ··· 11308 11481 version "0.0.2" 11309 11482 resolved "https://registry.yarnpkg.com/shorthash/-/shorthash-0.0.2.tgz#59b268eecbde59038b30da202bcfbddeb2c4a4eb" 11310 11483 11311 - side-channel@^1.0.2: 11484 + side-channel@^1.0.1, side-channel@^1.0.2: 11312 11485 version "1.0.2" 11313 11486 resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" 11314 11487 dependencies: ··· 11571 11744 dependencies: 11572 11745 extend-shallow "^3.0.0" 11573 11746 11747 + split@0.3.1: 11748 + version "0.3.1" 11749 + resolved "https://registry.yarnpkg.com/split/-/split-0.3.1.tgz#cebcf142bf61bbb64b141628e6db482a2914654c" 11750 + integrity sha1-zrzxQr9hu7ZLFBYo5ttIKikUZUw= 11751 + dependencies: 11752 + through "2" 11753 + 11574 11754 split@^1.0.1: 11575 11755 version "1.0.1" 11576 11756 resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" ··· 11605 11785 version "0.1.8" 11606 11786 resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" 11607 11787 11788 + stack-trace@0.0.x: 11789 + version "0.0.10" 11790 + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" 11791 + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= 11792 + 11608 11793 stack-utils@^1.0.1: 11609 11794 version "1.0.2" 11610 11795 resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" ··· 11964 12149 has-flag "^4.0.0" 11965 12150 supports-color "^7.0.0" 11966 12151 12152 + surge-fstream-ignore@^1.0.6: 12153 + version "1.0.6" 12154 + resolved "https://registry.yarnpkg.com/surge-fstream-ignore/-/surge-fstream-ignore-1.0.6.tgz#e30efce95574b91fd571b5b02f32a611ea829731" 12155 + integrity sha512-hNN52cz2fYCAzhlHmWPn4aE3bFbpBt01AkWFLljrtSzFvxlipLAeLuLtQ3t4f0RKoUkjzXWCAFK13WoET2iM1A== 12156 + dependencies: 12157 + fstream ">=1.0.12" 12158 + inherits "2" 12159 + minimatch "^3.0.0" 12160 + 12161 + surge-ignore@0.2.0: 12162 + version "0.2.0" 12163 + resolved "https://registry.yarnpkg.com/surge-ignore/-/surge-ignore-0.2.0.tgz#5a7f8a20a71188cf9e75a2cfe8eb182de90daf3b" 12164 + integrity sha1-Wn+KIKcRiM+edaLP6OsYLekNrzs= 12165 + 12166 + surge@^0.21.3: 12167 + version "0.21.3" 12168 + resolved "https://registry.yarnpkg.com/surge/-/surge-0.21.3.tgz#8abfba77bac551d0db9bae0fc14a450874fd9e8d" 12169 + integrity sha512-2rTlyC6ku3alrMAyI/8xexCZeiQu0X11rc3Sg8k2SC+9+V+X2dsohCnsTgFbDANZzlL2WojzxmnzLsJFEu/WIQ== 12170 + dependencies: 12171 + cli-table3 "^0.5.1" 12172 + inquirer "^6.2.2" 12173 + is-domain "0.0.1" 12174 + minimist "1.1.1" 12175 + moniker "0.1.2" 12176 + netrc "0.1.4" 12177 + progress "1.1.8" 12178 + prompt "~0.2.14" 12179 + read "1.0.5" 12180 + request "^2.88.0" 12181 + split "0.3.1" 12182 + surge-fstream-ignore "^1.0.6" 12183 + surge-ignore "0.2.0" 12184 + tarr "1.1.0" 12185 + url-parse-as-address "1.0.0" 12186 + 11967 12187 svelte@^3.16.7: 11968 12188 version "3.18.1" 11969 12189 resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.18.1.tgz#db0f82cc46394ca8c9a9d183995e1ebfeea3bdd0" ··· 12043 12263 fs-constants "^1.0.0" 12044 12264 inherits "^2.0.3" 12045 12265 readable-stream "^3.1.1" 12266 + 12267 + tarr@1.1.0: 12268 + version "1.1.0" 12269 + resolved "https://registry.yarnpkg.com/tarr/-/tarr-1.1.0.tgz#d7a9532ce97f08f5200b78ae0a82a6883173c8c8" 12270 + integrity sha512-tENbQ43IQckay71stp1p1lljRhoEZpZk10FzEZKW2tJcMcnLwV3CfZdxBAERlH6nwnFvnHMS9eJOJl6IzSsG0g== 12271 + dependencies: 12272 + block-stream "*" 12273 + fstream ">=1.0.12" 12274 + inherits "2" 12046 12275 12047 12276 term-size@^1.2.0: 12048 12277 version "1.2.0" ··· 12655 12884 mime "^2.4.4" 12656 12885 schema-utils "^2.5.0" 12657 12886 12887 + url-parse-as-address@1.0.0: 12888 + version "1.0.0" 12889 + resolved "https://registry.yarnpkg.com/url-parse-as-address/-/url-parse-as-address-1.0.0.tgz#fb80901883f338b3cbed3538f5faa26adaf7f2e7" 12890 + integrity sha1-+4CQGIPzOLPL7TU49fqiatr38uc= 12891 + 12658 12892 url-parse-lax@^3.0.0: 12659 12893 version "3.0.0" 12660 12894 resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" ··· 12753 12987 version "0.4.0" 12754 12988 resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" 12755 12989 12990 + utile@0.2.x: 12991 + version "0.2.1" 12992 + resolved "https://registry.yarnpkg.com/utile/-/utile-0.2.1.tgz#930c88e99098d6220834c356cbd9a770522d90d7" 12993 + integrity sha1-kwyI6ZCY1iIINMNWy9mncFItkNc= 12994 + dependencies: 12995 + async "~0.2.9" 12996 + deep-equal "*" 12997 + i "0.3.x" 12998 + mkdirp "0.x.x" 12999 + ncp "0.4.x" 13000 + rimraf "2.x.x" 13001 + 12756 13002 utils-merge@1.0.1: 12757 13003 version "1.0.1" 12758 13004 resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" ··· 13132 13378 tr46 "^1.0.1" 13133 13379 webidl-conversions "^4.0.2" 13134 13380 13381 + which-boxed-primitive@^1.0.1: 13382 + version "1.0.1" 13383 + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" 13384 + integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== 13385 + dependencies: 13386 + is-bigint "^1.0.0" 13387 + is-boolean-object "^1.0.0" 13388 + is-number-object "^1.0.3" 13389 + is-string "^1.0.4" 13390 + is-symbol "^1.0.2" 13391 + 13392 + which-collection@^1.0.0: 13393 + version "1.0.1" 13394 + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" 13395 + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== 13396 + dependencies: 13397 + is-map "^2.0.1" 13398 + is-set "^2.0.1" 13399 + is-weakmap "^2.0.1" 13400 + is-weakset "^2.0.1" 13401 + 13135 13402 which-module@^2.0.0: 13136 13403 version "2.0.0" 13137 13404 resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" ··· 13157 13424 resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" 13158 13425 dependencies: 13159 13426 string-width "^2.1.1" 13427 + 13428 + winston@0.8.x: 13429 + version "0.8.3" 13430 + resolved "https://registry.yarnpkg.com/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0" 13431 + integrity sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA= 13432 + dependencies: 13433 + async "0.2.x" 13434 + colors "0.6.x" 13435 + cycle "1.0.x" 13436 + eyes "0.1.x" 13437 + isstream "0.1.x" 13438 + pkginfo "0.3.x" 13439 + stack-trace "0.0.x" 13160 13440 13161 13441 "wonka@^3.2.1 || ^4.0.0", wonka@^4.0.6, wonka@^4.0.7: 13162 13442 version "4.0.7"