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

Configure Feed

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

fix(vue): fix variables typing (#3734)

authored by

Julien Hauseux and committed by
GitHub
b981b388 d3b019d5

+88 -185
+5
.changeset/short-crews-retire.md
··· 1 + --- 2 + '@urql/vue': minor 3 + --- 4 + 5 + Fix regression breaking `variables` typing
+7 -4
packages/vue-urql/src/useMutation.ts
··· 15 15 } from '@urql/core'; 16 16 17 17 import { useClient } from './useClient'; 18 - import type { MaybeRef } from './utils'; 19 - import { createRequestWithArgs, useRequestState } from './utils'; 18 + import { 19 + createRequestWithArgs, 20 + type MaybeRefOrGetter, 21 + useRequestState, 22 + } from './utils'; 20 23 21 24 /** State of the last mutation executed by {@link useMutation}. 22 25 * ··· 132 135 } 133 136 134 137 export function callUseMutation<T = any, V extends AnyVariables = AnyVariables>( 135 - query: MaybeRef<DocumentInput<T, V>>, 138 + query: MaybeRefOrGetter<DocumentInput<T, V>>, 136 139 client: Ref<Client> = useClient() 137 140 ): UseMutationResponse<T, V> { 138 141 const data: Ref<T | undefined> = shallowRef(); ··· 156 159 157 160 return pipe( 158 161 client.value.executeMutation<T, V>( 159 - createRequestWithArgs({ query, variables }), 162 + createRequestWithArgs<T, V>({ query, variables }), 160 163 context || {} 161 164 ), 162 165 onPush(result => {
+39 -99
packages/vue-urql/src/useQuery.test.ts
··· 120 120 ); 121 121 }); 122 122 123 - it('runs a query with different variables', async () => { 124 - const simpleVariables = { 125 - null: null, 126 - NaN: NaN, 127 - empty: '', 128 - bool: false, 129 - int: 1, 130 - float: 1.1, 131 - string: 'string', 132 - blob: new Blob(), 133 - date: new Date(), 134 - }; 135 - 136 - const variablesSet = { 137 - func: () => 'func', 138 - ref: ref('ref'), 139 - computed: computed(() => 'computed'), 140 - ...simpleVariables, 141 - }; 142 - 143 - const variablesSetUnwrapped = { 144 - func: 'func', 145 - ref: 'ref', 146 - computed: 'computed', 147 - ...simpleVariables, 148 - }; 149 - 150 - const { query$ } = createQuery({ 151 - query: ref('{ test }'), 152 - variables: { 153 - ...variablesSet, 154 - nested: variablesSet, 155 - array: [variablesSet], 156 - }, 157 - }); 158 - 159 - await query$; 160 - 161 - expect(query$.operation.value?.variables).toStrictEqual({ 162 - ...variablesSetUnwrapped, 163 - nested: variablesSetUnwrapped, 164 - array: [variablesSetUnwrapped], 165 - }); 166 - }); 167 - 168 123 it('reacts to ref variables changing', async () => { 169 124 const variables = ref({ prop: 1 }); 170 125 ··· 188 143 expect(query$.operation.value).toHaveProperty('variables.prop', 3); 189 144 }); 190 145 191 - it('reacts to nested ref variables changing', async () => { 192 - const prop = ref(1); 146 + it('reacts to ref variables changing', async () => { 147 + const foo = ref(1); 148 + const bar = ref(1); 193 149 194 150 const { executeQuery, query$ } = createQuery({ 195 151 query: ref('{ test }'), 196 - variables: { prop }, 152 + variables: ref({ 153 + prop: foo, 154 + nested: { 155 + prop: bar, 156 + }, 157 + }), 197 158 }); 198 159 199 160 await query$; 200 161 expect(executeQuery).toHaveBeenCalledTimes(1); 201 162 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 202 163 203 - prop.value++; 164 + foo.value++; 204 165 await query$; 205 166 expect(executeQuery).toHaveBeenCalledTimes(2); 206 167 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 168 + 169 + bar.value++; 170 + await query$; 171 + expect(executeQuery).toHaveBeenCalledTimes(3); 172 + expect(query$.operation.value).toHaveProperty('variables.nested.prop', 2); 207 173 }); 208 174 209 - it('reacts to deep nested ref variables changing', async () => { 210 - const prop = ref(1); 175 + it('reacts to getter variables changing', async () => { 176 + const foo = ref(1); 177 + const bar = ref(1); 211 178 212 179 const { executeQuery, query$ } = createQuery({ 213 180 query: ref('{ test }'), 214 - variables: { deep: { nested: { prop } } }, 181 + variables: () => ({ 182 + prop: foo.value, 183 + nested: { 184 + prop: bar.value, 185 + }, 186 + }), 215 187 }); 216 188 217 189 await query$; 218 190 expect(executeQuery).toHaveBeenCalledTimes(1); 219 - expect(query$.operation.value).toHaveProperty( 220 - 'variables.deep.nested.prop', 221 - 1 222 - ); 191 + expect(query$.operation.value).toHaveProperty('variables.prop', 1); 223 192 224 - prop.value++; 193 + foo.value++; 225 194 await query$; 226 195 expect(executeQuery).toHaveBeenCalledTimes(2); 227 - expect(query$.operation.value).toHaveProperty( 228 - 'variables.deep.nested.prop', 229 - 2 230 - ); 196 + expect(query$.operation.value).toHaveProperty('variables.prop', 2); 197 + 198 + bar.value++; 199 + await query$; 200 + expect(executeQuery).toHaveBeenCalledTimes(3); 201 + expect(query$.operation.value).toHaveProperty('variables.nested.prop', 2); 231 202 }); 232 203 233 204 it('reacts to reactive variables changing', async () => { ··· 258 229 }); 259 230 260 231 it('reacts to computed variables changing', async () => { 261 - const prop = ref(1); 262 - const prop2 = ref(1); 232 + const foo = ref(1); 233 + const bar = ref(1); 263 234 const variables = computed(() => ({ 264 - prop: prop.value, 265 - deep: { nested: { prop2 } }, 235 + prop: foo.value, 236 + deep: { nested: { prop: bar.value } }, 266 237 })); 267 238 268 239 const { executeQuery, query$ } = createQuery({ ··· 274 245 expect(executeQuery).toHaveBeenCalledTimes(1); 275 246 expect(query$.operation.value).toHaveProperty('variables.prop', 1); 276 247 277 - prop.value++; 248 + foo.value++; 278 249 await query$; 279 250 expect(executeQuery).toHaveBeenCalledTimes(2); 280 251 expect(query$.operation.value).toHaveProperty('variables.prop', 2); 281 252 282 - prop2.value++; 253 + bar.value++; 283 254 await query$; 284 255 expect(executeQuery).toHaveBeenCalledTimes(3); 285 256 expect(query$.operation.value).toHaveProperty( 286 - 'variables.deep.nested.prop2', 287 - 2 288 - ); 289 - }); 290 - 291 - it('reacts to callback variables changing', async () => { 292 - const prop = ref(1); 293 - const prop2 = ref(1); 294 - const variables = () => ({ 295 - prop: prop.value, 296 - deep: { nested: { prop2 } }, 297 - }); 298 - 299 - const { executeQuery, query$ } = createQuery({ 300 - query: ref('{ test }'), 301 - variables, 302 - }); 303 - 304 - await query$; 305 - expect(executeQuery).toHaveBeenCalledTimes(1); 306 - expect(query$.operation.value).toHaveProperty('variables.prop', 1); 307 - 308 - prop.value++; 309 - await query$; 310 - expect(executeQuery).toHaveBeenCalledTimes(2); 311 - expect(query$.operation.value).toHaveProperty('variables.prop', 2); 312 - 313 - prop2.value++; 314 - await query$; 315 - expect(executeQuery).toHaveBeenCalledTimes(3); 316 - expect(query$.operation.value).toHaveProperty( 317 - 'variables.deep.nested.prop2', 257 + 'variables.deep.nested.prop', 318 258 2 319 259 ); 320 260 });
+9 -10
packages/vue-urql/src/useQuery.ts
··· 18 18 19 19 import { useClient } from './useClient'; 20 20 21 - import type { MaybeRef, MaybeRefObj } from './utils'; 21 + import type { MaybeRefOrGetter, MaybeRefOrGetterObj } from './utils'; 22 22 import { useRequestState, useClientState } from './utils'; 23 23 24 24 /** Input arguments for the {@link useQuery} function. ··· 42 42 * 43 43 * @see {@link OperationContext.requestPolicy} for where this value is set. 44 44 */ 45 - requestPolicy?: MaybeRef<RequestPolicy>; 45 + requestPolicy?: MaybeRefOrGetter<RequestPolicy>; 46 46 /** Updates the {@link OperationContext} for the executed GraphQL query operation. 47 47 * 48 48 * @remarks ··· 60 60 * }); 61 61 * ``` 62 62 */ 63 - context?: MaybeRef<Partial<OperationContext>>; 63 + context?: MaybeRefOrGetter<Partial<OperationContext>>; 64 64 /** Prevents {@link useQuery} from automatically executing GraphQL query operations. 65 65 * 66 66 * @remarks ··· 72 72 * @see {@link https://urql.dev/goto/docs/basics/vue#pausing-usequery} for 73 73 * documentation on the `pause` option. 74 74 */ 75 - pause?: MaybeRef<boolean>; 76 - } & MaybeRefObj<GraphQLRequestParams<Data, MaybeRefObj<Variables>>>; 75 + pause?: MaybeRefOrGetter<boolean>; 76 + } & MaybeRefOrGetterObj<GraphQLRequestParams<Data, Variables>>; 77 77 78 78 /** State of the current query, your {@link useQuery} function is executing. 79 79 * ··· 246 246 const { fetching, operation, extensions, stale, error, hasNext } = 247 247 useRequestState<T, V>(); 248 248 249 - const { isPaused, source, pause, resume, execute, teardown } = useClientState( 250 - args, 251 - client, 252 - 'executeQuery' 253 - ); 249 + const { isPaused, source, pause, resume, execute, teardown } = useClientState< 250 + T, 251 + V 252 + >(args, client, 'executeQuery'); 254 253 255 254 const teardownQuery = watchEffect( 256 255 onInvalidate => {
+8 -9
packages/vue-urql/src/useSubscription.ts
··· 16 16 17 17 import { useClient } from './useClient'; 18 18 19 - import type { MaybeRef, MaybeRefObj } from './utils'; 19 + import type { MaybeRefOrGetter, MaybeRefOrGetterObj } from './utils'; 20 20 import { useRequestState, useClientState } from './utils'; 21 21 22 22 /** Input arguments for the {@link useSubscription} function. ··· 36 36 * {@link UseSubscriptionResponse.resume} is called, or, if `pause` is a reactive 37 37 * ref of a boolean, until this ref changes to `true`. 38 38 */ 39 - pause?: MaybeRef<boolean>; 39 + pause?: MaybeRefOrGetter<boolean>; 40 40 /** Updates the {@link OperationContext} for the executed GraphQL subscription operation. 41 41 * 42 42 * @remarks ··· 54 54 * }); 55 55 * ``` 56 56 */ 57 - context?: MaybeRef<Partial<OperationContext>>; 58 - } & MaybeRefObj<GraphQLRequestParams<Data, MaybeRefObj<Variables>>>; 57 + context?: MaybeRefOrGetter<Partial<OperationContext>>; 58 + } & MaybeRefOrGetterObj<GraphQLRequestParams<Data, Variables>>; 59 59 60 60 /** Combines previous data with an incoming subscription result’s data. 61 61 * ··· 246 246 V 247 247 >(); 248 248 249 - const { isPaused, source, pause, resume, execute, teardown } = useClientState( 250 - args, 251 - client, 252 - 'executeSubscription' 253 - ); 249 + const { isPaused, source, pause, resume, execute, teardown } = useClientState< 250 + T, 251 + V 252 + >(args, client, 'executeSubscription'); 254 253 255 254 const teardownSubscription = watchEffect(onInvalidate => { 256 255 if (source.value) {
+20 -63
packages/vue-urql/src/utils.ts
··· 3 3 Client, 4 4 CombinedError, 5 5 DocumentInput, 6 + GraphQLRequest, 6 7 Operation, 7 8 OperationContext, 8 9 OperationResult, 9 10 OperationResultSource, 10 11 } from '@urql/core'; 11 12 import { createRequest } from '@urql/core'; 12 - import type { Ref } from 'vue'; 13 + import { type Ref, unref } from 'vue'; 13 14 import { watchEffect, isReadonly, computed, ref, shallowRef, isRef } from 'vue'; 14 15 import type { UseSubscriptionArgs } from './useSubscription'; 15 16 import type { UseQueryArgs } from './useQuery'; 16 17 17 - export type MaybeRef<T> = T | (() => T) | Ref<T>; 18 - export type MaybeRefObj<T> = T extends {} 19 - ? { [K in keyof T]: MaybeRef<T[K]> } 20 - : T; 21 - 22 - const unwrap = <T>(maybeRef: MaybeRef<T>): T => 23 - typeof maybeRef === 'function' 24 - ? (maybeRef as () => T)() 25 - : maybeRef != null && isRef(maybeRef) 26 - ? maybeRef.value 27 - : maybeRef; 28 - 29 - const isPlainObject = (value: any): boolean => { 30 - if (typeof value !== 'object' || value === null) return false; 31 - return ( 32 - value.constructor && 33 - Object.getPrototypeOf(value).constructor === Object.prototype.constructor 34 - ); 35 - }; 36 - export const isArray = Array.isArray; 18 + export type MaybeRefOrGetter<T> = T | (() => T) | Ref<T>; 19 + export type MaybeRefOrGetterObj<T extends {}> = 20 + T extends Record<string, never> 21 + ? T 22 + : { [K in keyof T]: MaybeRefOrGetter<T[K]> }; 37 23 38 - const unwrapDeeply = <T>(input: T): T => { 39 - input = isRef(input) ? (input.value as T) : input; 24 + const isFunction = <T>(val: MaybeRefOrGetter<T>): val is () => T => 25 + typeof val === 'function'; 40 26 41 - if (typeof input === 'function') { 42 - return unwrapDeeply(input()) as T; 43 - } 44 - 45 - if (input && typeof input === 'object') { 46 - if (isArray(input)) { 47 - const length = input.length; 48 - const out = new Array(length) as T; 49 - let i = 0; 50 - for (; i < length; i++) { 51 - out[i] = unwrapDeeply(input[i]); 52 - } 53 - 54 - return out; 55 - } else if (isPlainObject(input)) { 56 - const keys = Object.keys(input); 57 - const length = keys.length; 58 - let i = 0; 59 - let key: string; 60 - const out = {} as T; 61 - 62 - for (; i < length; i++) { 63 - key = keys[i]; 64 - out[key] = unwrapDeeply(input[key]); 65 - } 66 - 67 - return out; 68 - } 69 - } 70 - 71 - return input; 72 - }; 27 + const toValue = <T>(source: MaybeRefOrGetter<T>): T => 28 + isFunction(source) ? source() : unref(source); 73 29 74 30 export const createRequestWithArgs = < 75 31 T = any, ··· 78 34 args: 79 35 | UseQueryArgs<T, V> 80 36 | UseSubscriptionArgs<T, V> 81 - | { query: MaybeRef<DocumentInput<T, V>>; variables: V } 82 - ) => { 37 + | { query: MaybeRefOrGetter<DocumentInput<T, V>>; variables: V } 38 + ): GraphQLRequest<T, V> => { 39 + const _args = toValue(args); 83 40 return createRequest<T, V>( 84 - unwrap(args.query), 85 - unwrapDeeply(args.variables) as V 41 + toValue(_args.query), 42 + toValue(_args.variables as MaybeRefOrGetter<V>) 86 43 ); 87 44 }; 88 45 ··· 120 77 ? computed(args.pause) 121 78 : ref(!!args.pause); 122 79 123 - const request = computed(() => createRequestWithArgs(args)); 80 + const request = computed(() => createRequestWithArgs<T, V>(args)); 124 81 125 82 const requestOptions = computed(() => { 126 83 return 'requestPolicy' in args 127 84 ? { 128 - requestPolicy: unwrap(args.requestPolicy), 129 - ...unwrap(args.context), 85 + requestPolicy: toValue(args.requestPolicy), 86 + ...toValue(args.context), 130 87 } 131 88 : { 132 - ...unwrap(args.context), 89 + ...toValue(args.context), 133 90 }; 134 91 }); 135 92