···11+---
22+'@urql/exchange-multipart-fetch': minor
33+'@urql/exchange-graphcache': minor
44+'@urql/exchange-persisted': minor
55+'@urql/exchange-context': minor
66+'@urql/exchange-execute': minor
77+'@urql/exchange-retry': minor
88+'@urql/exchange-auth': minor
99+---
1010+1111+Update exchanges to drop redundant `share` calls, since `@urql/core`’s `composeExchanges` utility now automatically does so for us.
+5
.changeset/five-lies-collect.md
···11+---
22+'@urql/core': patch
33+---
44+55+Update `Exchange` contract and `composeExchanges` utility to remove the need to manually call `share` on either incoming `Source<Operation>` or `forward()`’s `Source<OperationResult>`. This is now taken care of internally in `composeExchanges` and should make it easier for you to create custom exchanges and for us to explain them.
+22-85
docs/advanced/authoring-exchanges.md
···152152});
153153```
154154155155-### Only One Operations Stream
156156-157157-When writing an Exchange we have to be careful not to _split_ the stream into multiple ones by
158158-subscribing multiple times. Streams are lazy and immutable by default. Every time you use them,
159159-a new chain of streaming operators is created; since Exchanges are technically side effects, we don't
160160-want to accidentally have multiple instances of them in parallel.
161161-162162-The `ExchangeIO` function receives an `operations$` stream. It's important to be careful to either only
163163-use it once, or to _share_ its subscription.
164164-165165-```js
166166-import { pipe, filter, merge, share } from 'wonka';
167167-168168-// DON'T: split use operations$ twice
169169-({ forward }) => operations$ => {
170170- // <-- The ExchangeIO function (inline)
171171- const queries = pipe(
172172- operations$,
173173- filter(op => op.kind === 'query')
174174- );
175175- const others = pipe(
176176- operations$,
177177- filter(op => op.kind !== 'query')
178178- );
179179- return forward(merge([queries, others]));
180180-};
181181-182182-// DO: share operations$ if you have to use it twice
183183-({ forward }) => operations$ => {
184184- // <-- The ExchangeIO function (inline)
185185- const shared = pipe(operations$, share);
186186- const queries = pipe(
187187- shared,
188188- filter(op => op.kind === 'query')
189189- );
190190- const others = pipe(
191191- shared,
192192- filter(op => op.kind !== 'query')
193193- );
194194- return forward(merge([queries, others]));
195195-};
196196-197197-// DO: use operations$ only once alternatively
198198-({ forward }) => (
199199- operations$ // <-- The ExchangeIO function (inline)
200200-) =>
201201- pipe(
202202- operations$,
203203- map(op => {
204204- if (op.kind === 'query') {
205205- /* ... */
206206- } else {
207207- /* ... */
208208- }
209209- return op;
210210- }),
211211- forward
212212- );
213213-```
214214-215215-So if you see the `operations$` stream twice in your exchange code, make sure to
216216-use Wonka's [`share`](https://wonka.kitten.sh/api/operators#share) operator, to share the underlying
217217-subscription between all your streams.
218218-219155### How to Avoid Accidentally Dropping Operations
220156221157Typically the `operations$` stream will send you `query`, `mutation`,
···226162not `filter` operations too aggressively.
227163228164```js
229229-import { pipe, filter, merge, share } from 'wonka';
165165+import { pipe, filter, merge } from 'wonka';
230166231167// DON'T: drop unknown operations
232232-({ forward }) => operations$ => {
233233- // This doesn't handle operations that aren't queries
234234- const queries = pipe(
235235- operations$,
236236- filter(op => op.kind === 'query')
237237- );
238238- return forward(queries);
239239-};
168168+({ forward }) =>
169169+ operations$ => {
170170+ // This doesn't handle operations that aren't queries
171171+ const queries = pipe(
172172+ operations$,
173173+ filter(op => op.kind === 'query')
174174+ );
175175+ return forward(queries);
176176+ };
240177241178// DO: forward operations that you don't handle
242242-({ forward }) => operations$ => {
243243- const shared = pipe(operations$, share);
244244- const queries = pipe(
245245- shared,
246246- filter(op => op.kind === 'query')
247247- );
248248- const rest = pipe(
249249- shared,
250250- filter(op => op.kind !== 'query')
251251- );
252252- return forward(merge([queries, rest]));
253253-};
179179+({ forward }) =>
180180+ operations$ => {
181181+ const queries = pipe(
182182+ operations$,
183183+ filter(op => op.kind === 'query')
184184+ );
185185+ const rest = pipe(
186186+ operations$,
187187+ filter(op => op.kind !== 'query')
188188+ );
189189+ return forward(merge([queries, rest]));
190190+ };
254191```
255192256193If operations are grouped and/or filtered by what the exchange is handling, then it's also important to
···11+import { share } from 'wonka';
12import type { ExchangeIO, Exchange, ExchangeInput } from '../types';
2334/** Composes an array of Exchanges into a single one.
···1314 *
1415 * This simply merges all exchanges into one and is used by the {@link Client}
1516 * to merge the `exchanges` option it receives.
1717+ *
1818+ * @throws
1919+ * In development, if {@link ExchangeInput.forward} is called repeatedly
2020+ * by an {@link Exchange} an error is thrown, since `forward()` must only
2121+ * be called once per `Exchange`.
1622 */
1723export const composeExchanges =
1824 (exchanges: Exchange[]): Exchange =>
1925 ({ client, forward, dispatchDebug }: ExchangeInput): ExchangeIO =>
2020- exchanges.reduceRight(
2121- (forward, exchange) =>
2222- exchange({
2323- client,
2424- forward,
2525- dispatchDebug(event) {
2626- dispatchDebug({
2727- timestamp: Date.now(),
2828- source: exchange.name,
2929- ...event,
3030- });
3131- },
3232- }),
3333- forward
3434- );
2626+ exchanges.reduceRight((forward, exchange) => {
2727+ let forwarded = false;
2828+ return exchange({
2929+ client,
3030+ forward(operations$) {
3131+ if (process.env.NODE_ENV !== 'production') {
3232+ if (forwarded)
3333+ throw new Error(
3434+ 'forward() must only be called once in each Exchange.'
3535+ );
3636+ forwarded = true;
3737+ }
3838+ return share(forward(share(operations$)));
3939+ },
4040+ dispatchDebug(event) {
4141+ dispatchDebug({
4242+ timestamp: Date.now(),
4343+ source: exchange.name,
4444+ ...event,
4545+ });
4646+ },
4747+ });
4848+ }, forward);