diff --git a/.changeset/modern-queens-run.md b/.changeset/modern-queens-run.md index d53b7e5a22..7108a0dde6 100644 --- a/.changeset/modern-queens-run.md +++ b/.changeset/modern-queens-run.md @@ -1,8 +1,6 @@ --- -'@urql/core': minor -'@urql/preact': minor 'urql': minor --- -Add the `stripTypename` export to `@urql/core` to make all `__typename` fields non-enumerable. -In `@urql/preact` and `urql` this is applied to all `data` results. +Add the `maskTypename` export to `@urql/core` to make all `__typename` fields non-enumerable. +Add the possibility of masking `__typename` from result through the `client.maskTypename` option. diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 0ae87d7e8c..a2829c3b08 100755 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -16,6 +16,7 @@ import { switchMap, publish, subscribe, + map, } from 'wonka'; import { @@ -35,7 +36,7 @@ import { PromisifiedSource, } from './types'; -import { createRequest, toSuspenseSource, withPromise } from './utils'; +import { createRequest, toSuspenseSource, withPromise, maskTypename } from './utils'; import { DocumentNode } from 'graphql'; /** Options for configuring the URQL [client]{@link Client}. */ @@ -54,6 +55,8 @@ export interface ClientOptions { requestPolicy?: RequestPolicy; /** Use HTTP GET for queries. */ preferGetMethod?: boolean; + /** Mask __typename from results. */ + maskTypename?: boolean; } interface ActiveOperations { @@ -72,6 +75,7 @@ export class Client { suspense: boolean; preferGetMethod: boolean; requestPolicy: RequestPolicy; + maskTypename: boolean; // These are internals to be used to keep track of operations dispatchOperation: (operation: Operation) => void; @@ -90,6 +94,7 @@ export class Client { this.suspense = !!opts.suspense; this.requestPolicy = opts.requestPolicy || 'cache-first'; this.preferGetMethod = !!opts.preferGetMethod; + this.maskTypename = !!opts.maskTypename; // This subject forms the input of operations; executeOperation may be // called to dispatch a new operation on the subject @@ -182,11 +187,21 @@ export class Client { /** Executes an Operation by sending it through the exchange pipeline It returns an observable that emits all related exchange results and keeps track of this observable's subscribers. A teardown signal will be emitted when no subscribers are listening anymore. */ executeRequestOperation(operation: Operation): Source { const { key, operationName } = operation; - const operationResults$ = pipe( + let operationResults$ = pipe( this.results$, filter((res: OperationResult) => res.operation.key === key) ); + if (this.maskTypename) { + operationResults$ = pipe( + operationResults$, + map(res => { + res.data = maskTypename(res.data); + return res; + }), + ); + } + if (operationName === 'mutation') { // A mutation is always limited to just a single result and is never shared return pipe( diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 43df0e0302..a420bbdd4e 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -4,7 +4,7 @@ export * from './result'; export * from './typenames'; export * from './toSuspenseSource'; export * from './stringifyVariables'; -export * from './stripTypename'; +export * from './maskTypename'; export * from './withPromise'; export const noop = () => { diff --git a/packages/core/src/utils/stripTypename.test.ts b/packages/core/src/utils/maskTypename.test.ts similarity index 83% rename from packages/core/src/utils/stripTypename.test.ts rename to packages/core/src/utils/maskTypename.test.ts index eee14ee4dd..be980a4ad7 100644 --- a/packages/core/src/utils/stripTypename.test.ts +++ b/packages/core/src/utils/maskTypename.test.ts @@ -1,21 +1,21 @@ -import { stripTypename } from './stripTypename'; +import { maskTypename } from './maskTypename'; it('strips typename from flat objects', () => { expect( - stripTypename({ __typename: 'Todo', id: 1 }) + maskTypename({ __typename: 'Todo', id: 1 }) ).toEqual({ id: 1 }); }); it('strips typename from flat objects containing dates', () => { const date = new Date(); expect( - stripTypename({ __typename: 'Todo', id: 1, date }) + maskTypename({ __typename: 'Todo', id: 1, date }) ).toEqual({ id: 1, date }); }); it('strips typename from nested objects', () => { expect( - stripTypename({ + maskTypename({ __typename: 'Todo', id: 1, author: { @@ -28,7 +28,7 @@ it('strips typename from nested objects', () => { it('strips typename from nested objects with arrays', () => { expect( - stripTypename({ + maskTypename({ __typename: 'Todo', id: 1, author: { diff --git a/packages/core/src/utils/stripTypename.ts b/packages/core/src/utils/maskTypename.ts similarity index 77% rename from packages/core/src/utils/stripTypename.ts rename to packages/core/src/utils/maskTypename.ts index cafcda9278..fdfbcae4b1 100644 --- a/packages/core/src/utils/stripTypename.ts +++ b/packages/core/src/utils/maskTypename.ts @@ -1,4 +1,4 @@ -export const stripTypename = (data: any): any => { +export const maskTypename = (data: any): any => { if (!data || typeof data !== 'object') return data; return Object.keys(data).reduce((acc, key: string) => { @@ -9,9 +9,9 @@ export const stripTypename = (data: any): any => { value, }); } else if (Array.isArray(value)) { - acc[key] = value.map(stripTypename); + acc[key] = value.map(maskTypename); } else if (typeof value === 'object' && '__typename' in value) { - acc[key] = stripTypename(value); + acc[key] = maskTypename(value); } else { acc[key] = value; } diff --git a/packages/preact-urql/src/hooks/useMutation.ts b/packages/preact-urql/src/hooks/useMutation.ts index f5c46e19b2..ddce70d5e3 100644 --- a/packages/preact-urql/src/hooks/useMutation.ts +++ b/packages/preact-urql/src/hooks/useMutation.ts @@ -6,7 +6,6 @@ import { OperationContext, CombinedError, createRequest, - stripTypename, } from '@urql/core'; import { useClient } from '../context'; import { useImmediateState } from './useImmediateState'; @@ -54,7 +53,7 @@ export const useMutation = ( setState({ fetching: false, stale: !!result.stale, - data: stripTypename(result.data), + data: result.data, error: result.error, extensions: result.extensions, }); diff --git a/packages/preact-urql/src/hooks/useQuery.ts b/packages/preact-urql/src/hooks/useQuery.ts index 785d28e0fa..a61ff6e42f 100644 --- a/packages/preact-urql/src/hooks/useQuery.ts +++ b/packages/preact-urql/src/hooks/useQuery.ts @@ -1,7 +1,7 @@ import { DocumentNode } from 'graphql'; import { pipe, subscribe, onEnd } from 'wonka'; import { useRef, useCallback } from 'preact/hooks'; -import { OperationContext, RequestPolicy, CombinedError, stripTypename } from '@urql/core'; +import { OperationContext, RequestPolicy, CombinedError } from '@urql/core'; import { useClient } from '../context'; import { useRequest } from './useRequest'; @@ -71,7 +71,7 @@ export const useQuery = ( subscribe(result => { setState({ fetching: false, - data: stripTypename(result.data), + data: result.data, error: result.error, extensions: result.extensions, stale: !!result.stale, diff --git a/packages/preact-urql/src/hooks/useSubscription.ts b/packages/preact-urql/src/hooks/useSubscription.ts index d26cf39ec4..34c0203c1b 100644 --- a/packages/preact-urql/src/hooks/useSubscription.ts +++ b/packages/preact-urql/src/hooks/useSubscription.ts @@ -1,7 +1,7 @@ import { DocumentNode } from 'graphql'; import { useCallback, useRef } from 'preact/hooks'; import { pipe, onEnd, subscribe } from 'wonka'; -import { CombinedError, OperationContext, stripTypename } from '@urql/core'; +import { CombinedError, OperationContext } from '@urql/core'; import { useClient } from '../context'; import { useRequest } from './useRequest'; import { noop, initialState } from './useQuery'; @@ -63,7 +63,6 @@ export const useSubscription = ( }), onEnd(() => setState(s => ({ ...s, fetching: false }))), subscribe(result => { - result.data = stripTypename(result.data); setState(s => ({ fetching: true, data: diff --git a/packages/react-urql/src/hooks/useMutation.ts b/packages/react-urql/src/hooks/useMutation.ts index 6e4d85e546..435cfbe545 100644 --- a/packages/react-urql/src/hooks/useMutation.ts +++ b/packages/react-urql/src/hooks/useMutation.ts @@ -7,7 +7,6 @@ import { OperationContext, CombinedError, createRequest, - stripTypename, } from '@urql/core'; import { useClient } from '../context'; @@ -50,7 +49,7 @@ export const useMutation = ( setState({ fetching: false, stale: !!result.stale, - data: stripTypename(result.data), + data: result.data, error: result.error, extensions: result.extensions, }); diff --git a/packages/react-urql/src/hooks/useQuery.ts b/packages/react-urql/src/hooks/useQuery.ts index 5c14d3465e..6c67f1f049 100644 --- a/packages/react-urql/src/hooks/useQuery.ts +++ b/packages/react-urql/src/hooks/useQuery.ts @@ -1,7 +1,7 @@ import { DocumentNode } from 'graphql'; import { useCallback, useMemo } from 'react'; import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka'; -import { CombinedError, OperationContext, RequestPolicy, stripTypename } from '@urql/core'; +import { CombinedError, OperationContext, RequestPolicy } from '@urql/core'; import { useClient } from '../context'; import { useSource, useBehaviourSubject } from './useSource'; @@ -71,7 +71,7 @@ export const useQuery = ( map(({ stale, data, error, extensions }) => ({ fetching: false, stale: !!stale, - data: stripTypename(data), + data, error, extensions, })) diff --git a/packages/react-urql/src/hooks/useSubscription.ts b/packages/react-urql/src/hooks/useSubscription.ts index b7bec80f9a..d6b6bff442 100644 --- a/packages/react-urql/src/hooks/useSubscription.ts +++ b/packages/react-urql/src/hooks/useSubscription.ts @@ -1,7 +1,7 @@ import { DocumentNode } from 'graphql'; import { useCallback, useRef, useMemo } from 'react'; import { pipe, concat, fromValue, switchMap, map, scan } from 'wonka'; -import { CombinedError, OperationContext, stripTypename } from '@urql/core'; +import { CombinedError, OperationContext } from '@urql/core'; import { useClient } from '../context'; import { useSource, useBehaviourSubject } from './useSource'; @@ -75,7 +75,7 @@ export const useSubscription = ( map(({ stale, data, error, extensions }) => ({ fetching: true, stale: !!stale, - data: stripTypename(data), + data, error, extensions, }))