···2121} from 'wonka';
22222323import { query, write, writeOptimistic } from './operations';
2424-import { addCacheOutcome, toRequestPolicy } from './helpers/operation';
2424+import { addMetadata, toRequestPolicy } from './helpers/operation';
2525import { filterVariables, getMainOperation } from './ast';
2626import { Store, noopDataState, hydrateData, reserveLayer } from './store';
2727import { Data, Dependencies, CacheExchangeOpts } from './types';
···295295 message: 'The result could not be retrieved from the cache',
296296 operation: res.operation,
297297 });
298298- return addCacheOutcome(res.operation, 'miss');
298298+ return addMetadata(res.operation, { cacheOutcome: 'miss' });
299299 })
300300 );
301301···327327 !reexecutingOperations.has(res.operation.key));
328328329329 const result: OperationResult = {
330330- operation: addCacheOutcome(res.operation, res.outcome),
330330+ operation: addMetadata(res.operation, {
331331+ cacheOutcome: res.outcome,
332332+ }),
331333 data: res.data,
332334 error: res.error,
333335 extensions: res.extensions,
+4-4
exchanges/graphcache/src/helpers/operation.ts
···11import {
22 Operation,
33 RequestPolicy,
44- CacheOutcome,
44+ OperationDebugMeta,
55 makeOperation,
66} from '@urql/core';
7788// Returns the given operation result with added cacheOutcome meta field
99-export const addCacheOutcome = (
99+export const addMetadata = (
1010 operation: Operation,
1111- outcome: CacheOutcome
1111+ meta: OperationDebugMeta
1212): Operation =>
1313 makeOperation(operation.kind, operation, {
1414 ...operation.context,
1515 meta: {
1616 ...operation.context.meta,
1717- cacheOutcome: outcome,
1717+ ...meta,
1818 },
1919 });
2020
+31-41
packages/core/src/gql.ts
···33import { AnyVariables, TypedDocumentNode } from './types';
44import { keyDocument, stringifyDocument } from './utils';
5566-const applyDefinitions = (
77- fragmentNames: Map<string, string>,
88- target: DefinitionNode[],
99- source: Array<DefinitionNode> | ReadonlyArray<DefinitionNode>
1010-) => {
1111- for (const definition of source) {
1212- if (definition.kind === Kind.FRAGMENT_DEFINITION) {
1313- const name = definition.name.value;
1414- const value = stringifyDocument(definition);
1515- // Fragments will be deduplicated according to this Map
1616- if (!fragmentNames.has(name)) {
1717- fragmentNames.set(name, value);
1818- target.push(definition);
1919- } else if (
2020- process.env.NODE_ENV !== 'production' &&
2121- fragmentNames.get(name) !== value
2222- ) {
2323- // Fragments with the same names is expected to have the same contents
2424- console.warn(
2525- '[WARNING: Duplicate Fragment] A fragment with name `' +
2626- name +
2727- '` already exists in this document.\n' +
2828- 'While fragment names may not be unique across your source, each name must be unique per document.'
2929- );
3030- }
3131- } else {
3232- target.push(definition);
3333- }
3434- }
3535-};
3636-376/** A GraphQL parse function, which may be called as a tagged template literal, returning a parsed {@link DocumentNode}.
387 *
398 * @remarks
···8958 string: string
9059): TypedDocumentNode<Data, Variables>;
91609292-function gql(/* arguments */) {
6161+function gql(parts: string | TemplateStringsArray /* arguments */) {
9362 const fragmentNames = new Map<string, string>();
9463 const definitions: DefinitionNode[] = [];
9595- const interpolations: DefinitionNode[] = [];
6464+ const source: DocumentNode[] = [];
96659766 // Apply the entire tagged template body's definitions
9898- let body: string = Array.isArray(arguments[0])
9999- ? arguments[0][0]
100100- : arguments[0] || '';
6767+ let body: string = Array.isArray(parts) ? parts[0] : parts || '';
10168 for (let i = 1; i < arguments.length; i++) {
10269 const value = arguments[i];
10370 if (value && value.definitions) {
104104- interpolations.push(...value.definitions);
7171+ source.push(value);
10572 } else {
10673 body += value;
10774 }
···10976 body += arguments[0][i];
11077 }
11178112112- // Apply the tag's body definitions
113113- applyDefinitions(fragmentNames, definitions, keyDocument(body).definitions);
114114- // Copy over each interpolated document's definitions
115115- applyDefinitions(fragmentNames, definitions, interpolations);
7979+ source.unshift(keyDocument(body));
8080+ for (const document of source) {
8181+ for (const definition of document.definitions) {
8282+ if (definition.kind === Kind.FRAGMENT_DEFINITION) {
8383+ const name = definition.name.value;
8484+ const value = stringifyDocument(definition);
8585+ // Fragments will be deduplicated according to this Map
8686+ if (!fragmentNames.has(name)) {
8787+ fragmentNames.set(name, value);
8888+ definitions.push(definition);
8989+ } else if (
9090+ process.env.NODE_ENV !== 'production' &&
9191+ fragmentNames.get(name) !== value
9292+ ) {
9393+ // Fragments with the same names is expected to have the same contents
9494+ console.warn(
9595+ '[WARNING: Duplicate Fragment] A fragment with name `' +
9696+ name +
9797+ '` already exists in this document.\n' +
9898+ 'While fragment names may not be unique across your source, each name must be unique per document.'
9999+ );
100100+ }
101101+ } else {
102102+ definitions.push(definition);
103103+ }
104104+ }
105105+ }
116106117107 return keyDocument({
118108 kind: Kind.DOCUMENT,
+1
packages/core/src/types.ts
···433433 * `OperationResult`s and is filled in by exchanges across the codebase in development.
434434 *
435435 * This data is not for production use and hence shouldn't be used or relied upon directly.
436436+ * In production, this may not be set by default exchanges.
436437 */
437438 meta?: OperationDebugMeta;
438439 /** Instructs fetch exchanges to use a GET request.
+1-1
packages/core/src/utils/hash.ts
···3030 * @see {@link http://www.cse.yorku.ca/~oz/hash.html#djb2} for a further description of djb2.
3131 */
3232export const phash = (x: string, seed?: HashValue): HashValue => {
3333- let h = typeof seed === 'number' ? seed | 0 : 5381;
3333+ let h = (seed || 5381) | 0;
3434 for (let i = 0, l = x.length | 0; i < l; i++)
3535 h = (h << 5) + h + x.charCodeAt(i);
3636 return h as HashValue;
+7-7
packages/core/src/utils/request.ts
···24242525const SOURCE_NAME = 'gql';
2626const GRAPHQL_STRING_RE = /("{3}[\s\S]*"{3}|"(?:\\.|[^"])*")/g;
2727-const REPLACE_CHAR_RE = /(#[^\n\r]+)?(?:\n|\r\n?|$)+/g;
2727+const REPLACE_CHAR_RE = /(?:#[^\n\r]+)?(?:[\r\n]+|$)/g;
28282929const replaceOutsideStrings = (str: string, idx: number) =>
3030 idx % 2 === 0 ? str.replace(REPLACE_CHAR_RE, '\n') : str;
···9595): HashValue => {
9696 let key = phash(stringifyDocument(node));
9797 // Add the operation name to the produced hash
9898- if (typeof node === 'object' && 'definitions' in node) {
9999- const operationName = getOperationName(node);
9898+ if ((node as DocumentNode).definitions) {
9999+ const operationName = getOperationName(node as DocumentNode);
100100 if (operationName) key = phash(`\n# ${operationName}`, key);
101101 }
102102 return key;
···151151 Data = any,
152152 Variables extends AnyVariables = AnyVariables
153153>(
154154- q: string | DocumentNode | TypedDocumentNode<Data, Variables>,
155155- variables: Variables
154154+ _query: string | DocumentNode | TypedDocumentNode<Data, Variables>,
155155+ _variables: Variables
156156): GraphQLRequest<Data, Variables> => {
157157- if (!variables) variables = {} as Variables;
158158- const query = keyDocument(q);
157157+ const variables = _variables || ({} as Variables);
158158+ const query = keyDocument(_query);
159159 const printedVars = stringifyVariables(variables);
160160 let key = query.__key;
161161 if (printedVars !== '{}') key = phash(printedVars, key);
+3-12
packages/core/src/utils/result.ts
···2828 result: ExecutionResult,
2929 response?: any
3030): OperationResult => {
3131- if (
3232- (!('data' in result) && !('errors' in result)) ||
3333- 'incremental' in result
3434- ) {
3131+ if (!('data' in result) && !('errors' in result)) {
3532 throw new Error('No Content');
3633 }
3734···4542 response,
4643 })
4744 : undefined,
4848- extensions:
4949- (typeof result.extensions === 'object' && result.extensions) || undefined,
4545+ extensions: result.extensions ? { ...result.extensions } : undefined,
5046 hasNext: result.hasNext == null ? defaultHasNext : result.hasNext,
5147 };
5248};
···83798480 // NOTE: We handle the old version of the incremental delivery payloads as well
8581 if ('path' in nextResult) {
8686- incremental = [
8787- {
8888- data: nextResult.data,
8989- path: nextResult.path,
9090- } as IncrementalPayload,
9191- ];
8282+ incremental = [nextResult as IncrementalPayload];
9283 }
93849485 if (incremental) {
+8-16
packages/core/src/utils/streamUtils.ts
···11-import { Source, subscribe, pipe } from 'wonka';
11+import { Source, take, filter, toPromise, pipe } from 'wonka';
22import { OperationResult, PromisifiedSource } from '../types';
3344/** Patches a `toPromise` method onto the `Source` passed to it.
···99export function withPromise<T extends OperationResult>(
1010 source$: Source<T>
1111): PromisifiedSource<T> {
1212- (source$ as PromisifiedSource<T>).toPromise = () => {
1313- return new Promise(resolve => {
1414- const subscription = pipe(
1515- source$,
1616- subscribe(result => {
1717- if (!result.stale && !result.hasNext) {
1818- Promise.resolve().then(() => {
1919- subscription.unsubscribe();
2020- resolve(result);
2121- });
2222- }
2323- })
2424- );
2525- });
2626- };
1212+ (source$ as PromisifiedSource<T>).toPromise = () =>
1313+ pipe(
1414+ source$,
1515+ filter(result => !result.stale && !result.hasNext),
1616+ take(1),
1717+ toPromise
1818+ );
27192820 return source$ as PromisifiedSource<T>;
2921}
+4-6
packages/core/src/utils/stringifyVariables.ts
···1010 return stringify(x.toJSON());
1111 } else if (Array.isArray(x)) {
1212 let out = '[';
1313- for (let value of x) {
1414- if (out !== '[') out += ',';
1515- value = stringify(value);
1616- out += value.length > 0 ? value : 'null';
1313+ for (const value of x) {
1414+ if (out.length > 1) out += ',';
1515+ out += stringify(value) || 'null';
1716 }
1818-1917 out += ']';
2018 return out;
2119 }
···2422 if (!keys.length && x.constructor && x.constructor !== Object) {
2523 const key = cache.get(x) || Math.random().toString(36).slice(2);
2624 cache.set(x, key);
2727- return `{"__key":"${key}"}`;
2525+ return stringify({ __key: key });
2826 }
29273028 seen.add(x);