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

refactor(graphcache): Refactor internal state and data utilities (#3166)

+4 -3
exchanges/graphcache/src/cacheExchange.ts
···
import { _write } from './operations/write';
import { addMetadata, toRequestPolicy } from './helpers/operation';
import { filterVariables, getMainOperation } from './ast';
+
import { Store } from './store/store';
+
import { Data, Dependencies, CacheExchangeOpts } from './types';
+
import {
-
Store,
initDataState,
clearDataState,
noopDataState,
hydrateData,
reserveLayer,
-
} from './store';
-
import { Data, Dependencies, CacheExchangeOpts } from './types';
+
} from './store/data';
interface OperationResultWithMeta extends Partial<OperationResult> {
operation: Operation;
+1 -1
exchanges/graphcache/src/extras/relayPagination.test.ts
···
import { it, expect } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
import { relayPagination } from './relayPagination';
function itemNode(numItem: number) {
+1 -1
exchanges/graphcache/src/extras/simplePagination.test.ts
···
import { it, expect } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
import { simplePagination } from './simplePagination';
it('works with forward pagination', () => {
+1 -1
exchanges/graphcache/src/index.ts
···
export * from './types';
-
export type { Store } from './store';
+
export { Store } from './store/store';
export { cacheExchange } from './cacheExchange';
export { offlineExchange } from './offlineExchange';
+1 -1
exchanges/graphcache/src/operations/invalidate.ts
···
import * as InMemoryData from '../store/data';
+
import { keyOfField } from '../store/keys';
import { FieldArgs } from '../types';
-
import { keyOfField } from '../store';
interface PartialFieldInfo {
fieldKey: string;
+1 -1
exchanges/graphcache/src/operations/query.test.ts
···
import { minifyIntrospectionQuery } from '@urql/introspection';
import { describe, it, beforeEach, beforeAll, expect } from 'vitest';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
import { __initAnd_write as write } from './write';
import { __initAnd_query as query } from './query';
+34 -29
exchanges/graphcache/src/operations/query.ts
···
Dependencies,
} from '../types';
-
import {
-
Store,
-
getCurrentOperation,
-
getCurrentDependencies,
-
initDataState,
-
clearDataState,
-
joinKeys,
-
keyOfField,
-
makeData,
-
ownsData,
-
} from '../store';
-
+
import { joinKeys, keyOfField } from '../store/keys';
+
import { Store } from '../store/store';
import * as InMemoryData from '../store/data';
import { warn, pushDebugNode, popDebugNode } from '../helpers/help';
···
error?: CombinedError | undefined,
key?: number
): QueryResult => {
-
initDataState('read', store.data, key);
+
InMemoryData.initDataState('read', store.data, key);
const result = _query(store, request, data, error);
-
clearDataState();
+
InMemoryData.clearDataState();
return result;
};
···
getFragments(request.query),
rootKey,
rootKey,
-
false,
error
);
···
// scratch
const data =
rootKey !== ctx.store.rootFields['query']
-
? readRoot(ctx, rootKey, rootSelect, input || makeData())
-
: readSelection(ctx, rootKey, rootSelect, input || makeData());
+
? readRoot(ctx, rootKey, rootSelect, input || InMemoryData.makeData())
+
: readSelection(
+
ctx,
+
rootKey,
+
rootSelect,
+
input || InMemoryData.makeData()
+
);
if (process.env.NODE_ENV !== 'production') {
popDebugNode();
+
InMemoryData.getCurrentDependencies();
}
return {
-
dependencies: getCurrentDependencies(),
+
dependencies: InMemoryData.currentDependencies!,
partial: ctx.partial || !data,
hasNext: ctx.hasNext,
data: data || null,
···
let node: FieldNode | void;
let hasChanged = InMemoryData.currentForeignData;
-
const output = makeData(input);
+
const output = InMemoryData.makeData(input);
while ((node = iterate())) {
const fieldAlias = getFieldAlias(node);
const fieldValue = input[fieldAlias];
···
variables || {},
fragments,
typename,
-
entityKey
+
entityKey,
+
undefined
);
const result =
-
readSelection(ctx, entityKey, getSelectionSet(fragment), makeData()) ||
-
null;
+
readSelection(
+
ctx,
+
entityKey,
+
getSelectionSet(fragment),
+
InMemoryData.makeData()
+
) || null;
if (process.env.NODE_ENV !== 'production') {
popDebugNode();
···
let hasNext = false;
let hasChanged = InMemoryData.currentForeignData;
let node: FieldNode | void;
-
const output = makeData(input);
+
const output = InMemoryData.makeData(input);
while ((node = iterate()) !== undefined) {
// Derive the needed data from our node.
const fieldName = getName(node);
···
// The field is a scalar and can be retrieved directly from the result
dataFieldValue = resultValue;
} else if (
-
getCurrentOperation() === 'read' &&
+
InMemoryData.currentOperation === 'read' &&
resolvers &&
resolvers[fieldName]
) {
···
? output[fieldAlias]
: input[fieldAlias]) as Data,
dataFieldValue,
-
ownsData(input)
+
InMemoryData.ownsData(input)
);
}
···
? output[fieldAlias]
: input[fieldAlias]) as Data,
resultValue,
-
ownsData(input)
+
InMemoryData.ownsData(input)
);
} else {
// Otherwise we attempt to get the missing field from the cache
···
(output[fieldAlias] !== undefined
? output[fieldAlias]
: input[fieldAlias]) as Data,
-
ownsData(input)
+
InMemoryData.ownsData(input)
);
} else if (typeof fieldValue === 'object' && fieldValue !== null) {
// The entity on the field was invalid but can still be recovered
···
} else if (!isOwnedData && prevData === null) {
return null;
} else if (isDataOrKey(result)) {
-
const data = (prevData || makeData()) as Data;
+
const data = (prevData || InMemoryData.makeData()) as Data;
return typeof result === 'string'
? readSelection(ctx, result, select, data)
: readSelection(ctx, key, select, data, result);
···
return null;
}
-
return readSelection(ctx, link, select, (prevData || makeData()) as Data);
+
return readSelection(
+
ctx,
+
link,
+
select,
+
(prevData || InMemoryData.makeData()) as Data
+
);
};
const isDataOrKey = (x: any): x is string | Data =>
+6 -6
exchanges/graphcache/src/operations/shared.ts
···
} from '../ast';
import { warn, pushDebugNode, popDebugNode } from '../helpers/help';
-
import { hasField, isWriting } from '../store/data';
-
import { Store, keyOfField } from '../store';
+
import { hasField, currentOperation, currentOptimistic } from '../store/data';
+
import { keyOfField } from '../store/keys';
+
import { Store } from '../store/store';
import { getFieldArguments, shouldInclude, isInterfaceOfType } from '../ast';
···
fragments: Fragments,
typename: string,
entityKey: string,
-
optimistic?: boolean,
-
error?: CombinedError | undefined
+
error: CombinedError | undefined
): Context => {
const ctx: Context = {
store,
···
error: undefined,
partial: false,
hasNext: false,
-
optimistic: !!optimistic,
+
optimistic: currentOptimistic,
__internal: {
path: [],
errorMap: undefined,
···
);
return (
-
isWriting() ||
+
currentOperation === 'write' ||
!getSelectionSet(node).some(node => {
if (!isFieldNode(node)) return false;
const fieldKey = keyOfField(getName(node), getFieldArguments(node, vars));
+1 -1
exchanges/graphcache/src/operations/write.test.ts
···
import { __initAnd_write as write } from './write';
import * as InMemoryData from '../store/data';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
const TODO_QUERY = gql`
query todos {
+12 -17
exchanges/graphcache/src/operations/write.ts
···
OptimisticMutationResolver,
} from '../types';
-
import {
-
Store,
-
getCurrentDependencies,
-
initDataState,
-
clearDataState,
-
joinKeys,
-
keyOfField,
-
makeData,
-
} from '../store';
-
+
import { joinKeys, keyOfField } from '../store/keys';
+
import { Store } from '../store/store';
import * as InMemoryData from '../store/data';
import {
···
error?: CombinedError | undefined,
key?: number
): WriteResult => {
-
initDataState('write', store.data, key || null);
+
InMemoryData.initDataState('write', store.data, key || null);
const result = _write(store, request, data, error);
-
clearDataState();
+
InMemoryData.clearDataState();
return result;
};
···
);
}
-
initDataState('write', store.data, key, true);
+
InMemoryData.initDataState('write', store.data, key, true);
const result = _write(store, request, {} as Data, undefined);
-
clearDataState();
+
InMemoryData.clearDataState();
return result;
};
···
data?: Data,
error?: CombinedError | undefined
) => {
+
if (process.env.NODE_ENV !== 'production') {
+
InMemoryData.getCurrentDependencies();
+
}
+
const operation = getMainOperation(request.query);
const result: WriteResult = {
-
data: data || makeData(),
-
dependencies: getCurrentDependencies(),
+
data: data || InMemoryData.makeData(),
+
dependencies: InMemoryData.currentDependencies!,
};
const kind = store.rootFields[operation.operation];
···
getFragments(request.query),
kind,
kind,
-
InMemoryData.currentOptimistic,
error
);
+2 -16
exchanges/graphcache/src/store/data.ts
···
let currentOwnership: null | WeakSet<Data> = null;
let currentDataMapping: null | WeakMap<Data, Data> = null;
-
let currentOperation: null | OperationType = null;
let currentData: null | InMemoryData = null;
-
let currentDependencies: null | Dependencies = null;
let currentOptimisticKey: null | number = null;
+
export let currentOperation: null | OperationType = null;
+
export let currentDependencies: null | Dependencies = null;
export let currentForeignData = false;
export let currentOptimistic = false;
···
currentOwnership!.add(newData);
return newData;
};
-
-
export const isWriting = (): boolean => currentOperation === 'write';
export const ownsData = (data?: Data): boolean =>
!!data && currentOwnership!.has(data);
···
if (layerKey && !isOptimistic) data.deferredKeys.delete(layerKey);
initDataState('write', data, layerKey, isOptimistic);
clearDataState();
-
};
-
-
export const getCurrentOperation = (): OperationType => {
-
invariant(
-
currentOperation !== null,
-
'Invalid Cache call: The cache may only be accessed or mutated during' +
-
'operations like write or query, or as part of its resolvers, updaters, ' +
-
'or optimistic configs.',
-
2
-
);
-
-
return currentOperation;
};
/** As we're writing, we keep around all the records and links we've read or have written to */
-14
exchanges/graphcache/src/store/index.ts
···
-
export {
-
initDataState,
-
clearDataState,
-
noopDataState,
-
makeData,
-
ownsData,
-
reserveLayer,
-
getCurrentOperation,
-
getCurrentDependencies,
-
hydrateData,
-
} from './data';
-
-
export * from './keys';
-
export * from './store';
+1 -1
exchanges/graphcache/src/store/store.test.ts
···
},
});
-
context = makeContext(store, {}, {}, 'Query', 'Query');
+
context = makeContext(store, {}, {}, 'Query', 'Query', undefined);
write(store, { query: Todos }, todosData);
InMemoryData.initDataState('read', store.data, null);
});
+1 -1
exchanges/graphcache/src/test-utils/examples-1.test.ts
···
__initAnd_writeOptimistic as writeOptimistic,
} from '../operations/write';
import * as InMemoryData from '../store/data';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
import { Data } from '../types';
const Todos = gql`
+1 -1
exchanges/graphcache/src/test-utils/examples-2.test.ts
···
import { it, afterEach, expect } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
const Item = gql`
{
+1 -1
exchanges/graphcache/src/test-utils/examples-3.test.ts
···
import { it, afterEach, expect } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
afterEach(() => {
expect(console.warn).not.toHaveBeenCalled();
+1 -1
exchanges/graphcache/src/test-utils/suite.test.ts
···
import { it, expect } from 'vitest';
import { __initAnd_query as query } from '../operations/query';
import { __initAnd_write as write } from '../operations/write';
-
import { Store } from '../store';
+
import { Store } from '../store/store';
interface TestCase {
query: DocumentNode;