1import { pipe, map, scan, subscribe } from 'wonka';
2import { derived, writable } from 'svelte/store';
3
4import type {
5 AnyVariables,
6 GraphQLRequestParams,
7 Client,
8 OperationContext,
9} from '@urql/core';
10import { createRequest } from '@urql/core';
11
12import type { OperationResultState, OperationResultStore } from './common';
13import { initialResult } from './common';
14
15/** Input arguments for the {@link mutationStore} function.
16 *
17 * @param query - The GraphQL mutation that the `mutationStore` executes.
18 * @param variables - The variables for the GraphQL mutation that `mutationStore` executes.
19 */
20export type MutationArgs<
21 Data = any,
22 Variables extends AnyVariables = AnyVariables,
23> = {
24 /** The {@link Client} using which the subscription will be started.
25 *
26 * @remarks
27 * If you’ve previously provided a {@link Client} on Svelte’s context
28 * this can be set to {@link getContextClient}’s return value.
29 */
30 client: Client;
31 /** Updates the {@link OperationContext} for the GraphQL mutation operation.
32 *
33 * @remarks
34 * `context` may be passed to {@link mutationStore}, to update the
35 * {@link OperationContext} of a mutation operation. This may be used to update
36 * the `context` that exchanges will receive for a single hook.
37 *
38 * @example
39 * ```ts
40 * mutationStore({
41 * query,
42 * context: {
43 * additionalTypenames: ['Item'],
44 * },
45 * });
46 * ```
47 */
48 context?: Partial<OperationContext>;
49} & GraphQLRequestParams<Data, Variables>;
50
51/** Function to create a `mutationStore` that runs a GraphQL mutation and updates with a GraphQL result.
52 *
53 * @param args - a {@link MutationArgs} object, to pass a `query`, `variables`, and options.
54 * @returns a {@link OperationResultStore} of the mutation’s result.
55 *
56 * @remarks
57 * `mutationStore` allows a GraphQL mutation to be defined as a Svelte store.
58 * Given {@link MutationArgs.query}, it executes the GraphQL mutation on the
59 * {@link MutationArgs.client}.
60 *
61 * The returned store updates with an {@link OperationResult} when
62 * the `Client` returns a result for the mutation.
63 *
64 * Hint: It’s often easier to use {@link Client.mutation} if you’re
65 * creating a mutation imperatively and don’t need a store.
66 *
67 * @see {@link https://urql.dev/goto/docs/basics/svelte#mutations} for
68 * `mutationStore` docs.
69 *
70 * @example
71 * ```ts
72 * import { mutationStore, gql, getContextClient } from '@urql/svelte';
73 *
74 * const client = getContextClient();
75 *
76 * let result;
77 * function updateTodo({ id, title }) {
78 * result = queryStore({
79 * client,
80 * query: gql`
81 * mutation($id: ID!, $title: String!) {
82 * updateTodo(id: $id, title: $title) { id, title }
83 * }
84 * `,
85 * variables: { id, title },
86 * });
87 * }
88 * ```
89 */
90export function mutationStore<
91 Data = any,
92 Variables extends AnyVariables = AnyVariables,
93>(args: MutationArgs<Data, Variables>): OperationResultStore<Data, Variables> {
94 const request = createRequest(args.query, args.variables as Variables);
95 const operation = args.client.createRequestOperation(
96 'mutation',
97 request,
98 args.context
99 );
100 const initialState: OperationResultState<Data, Variables> = {
101 ...initialResult,
102 operation,
103 fetching: true,
104 };
105 const result$ = writable(initialState);
106
107 const subscription = pipe(
108 pipe(
109 args.client.executeRequestOperation(operation),
110 map(({ stale, data, error, extensions, operation, hasNext }) => ({
111 fetching: false,
112 stale,
113 data,
114 error,
115 operation,
116 extensions,
117 hasNext,
118 }))
119 ),
120 scan(
121 (result: OperationResultState<Data, Variables>, partial) => ({
122 ...result,
123 ...partial,
124 }),
125 initialState
126 ),
127 subscribe(result => {
128 result$.set(result);
129 })
130 );
131
132 return derived(result$, (result, set) => {
133 set(result);
134 return subscription.unsubscribe;
135 });
136}