@apollo/client 4.3.0-alpha.2 → 4.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -0
- package/__cjs/cache/index.cjs.map +1 -1
- package/__cjs/cache/index.d.cts +1 -1
- package/__cjs/cache/inmemory/entityStore.cjs +12 -6
- package/__cjs/cache/inmemory/entityStore.cjs.map +1 -1
- package/__cjs/cache/inmemory/entityStore.d.cts +1 -0
- package/__cjs/cache/inmemory/readFromStore.cjs +0 -1
- package/__cjs/cache/inmemory/readFromStore.cjs.map +1 -1
- package/__cjs/cache/inmemory/writeToStore.cjs +6 -2
- package/__cjs/cache/inmemory/writeToStore.cjs.map +1 -1
- package/__cjs/core/ObservableQuery.cjs +1 -3
- package/__cjs/core/ObservableQuery.cjs.map +1 -1
- package/__cjs/core/QueryInfo.cjs +171 -7
- package/__cjs/core/QueryInfo.cjs.map +1 -1
- package/__cjs/core/QueryInfo.d.cts +5 -2
- package/__cjs/core/QueryManager.cjs +10 -17
- package/__cjs/core/QueryManager.cjs.map +1 -1
- package/__cjs/core/QueryManager.d.cts +0 -2
- package/__cjs/utilities/index.cjs.map +1 -1
- package/__cjs/utilities/index.d.cts +1 -0
- package/__cjs/utilities/internal/collectSiblingFields.cjs +61 -0
- package/__cjs/utilities/internal/collectSiblingFields.cjs.map +1 -0
- package/__cjs/utilities/internal/collectSiblingFields.d.cts +21 -0
- package/__cjs/utilities/internal/index.cjs +8 -2
- package/__cjs/utilities/internal/index.cjs.map +1 -1
- package/__cjs/utilities/internal/index.d.cts +4 -0
- package/__cjs/utilities/internal/isDeferredFragment.cjs +23 -0
- package/__cjs/utilities/internal/isDeferredFragment.cjs.map +1 -0
- package/__cjs/utilities/internal/isDeferredFragment.d.cts +4 -0
- package/__cjs/utilities/internal/isTypenameField.cjs +7 -0
- package/__cjs/utilities/internal/isTypenameField.cjs.map +1 -0
- package/__cjs/utilities/internal/isTypenameField.d.cts +3 -0
- package/__cjs/utilities/policies/pagination.cjs.map +1 -1
- package/__cjs/utilities/policies/pagination.d.cts +1 -1
- package/__cjs/utilities/subscriptions/relay/index.cjs +1 -0
- package/__cjs/utilities/subscriptions/relay/index.cjs.map +1 -1
- package/__cjs/version.cjs +1 -1
- package/cache/index.d.ts +1 -1
- package/cache/index.js.map +1 -1
- package/cache/inmemory/entityStore.d.ts +1 -0
- package/cache/inmemory/entityStore.js +12 -6
- package/cache/inmemory/entityStore.js.map +1 -1
- package/cache/inmemory/readFromStore.js +0 -1
- package/cache/inmemory/readFromStore.js.map +1 -1
- package/cache/inmemory/writeToStore.js +7 -3
- package/cache/inmemory/writeToStore.js.map +1 -1
- package/core/ObservableQuery.js +1 -3
- package/core/ObservableQuery.js.map +1 -1
- package/core/QueryInfo.d.ts +5 -2
- package/core/QueryInfo.js +172 -8
- package/core/QueryInfo.js.map +1 -1
- package/core/QueryManager.d.ts +0 -2
- package/core/QueryManager.js +10 -17
- package/core/QueryManager.js.map +1 -1
- package/package.json +1 -1
- package/utilities/index.d.ts +1 -0
- package/utilities/index.js.map +1 -1
- package/utilities/internal/collectSiblingFields.d.ts +21 -0
- package/utilities/internal/collectSiblingFields.js +58 -0
- package/utilities/internal/collectSiblingFields.js.map +1 -0
- package/utilities/internal/index.d.ts +4 -0
- package/utilities/internal/index.js +3 -0
- package/utilities/internal/index.js.map +1 -1
- package/utilities/internal/isDeferredFragment.d.ts +4 -0
- package/utilities/internal/isDeferredFragment.js +20 -0
- package/utilities/internal/isDeferredFragment.js.map +1 -0
- package/utilities/internal/isTypenameField.d.ts +3 -0
- package/utilities/internal/isTypenameField.js +4 -0
- package/utilities/internal/isTypenameField.js.map +1 -0
- package/utilities/policies/pagination.d.ts +1 -1
- package/utilities/policies/pagination.js.map +1 -1
- package/utilities/subscriptions/relay/index.js +1 -0
- package/utilities/subscriptions/relay/index.js.map +1 -1
- package/version.js +1 -1
package/core/QueryInfo.d.ts
CHANGED
|
@@ -24,9 +24,12 @@ interface OperationInfo<TData, TVariables extends OperationVariables, AllowedCac
|
|
|
24
24
|
variables: TVariables;
|
|
25
25
|
errorPolicy: ErrorPolicy;
|
|
26
26
|
cacheWriteBehavior: AllowedCacheWriteBehavior;
|
|
27
|
+
returnPartialData?: boolean | undefined;
|
|
28
|
+
}
|
|
29
|
+
interface MarkQueryResult<TData, TExtensions> extends FormattedExecutionResult<TData, TExtensions> {
|
|
30
|
+
dataState: "empty" | "partial" | "streaming" | "complete";
|
|
27
31
|
}
|
|
28
32
|
export declare class QueryInfo<TData, TVariables extends OperationVariables = OperationVariables, TCache extends Cache.Implementation = Cache.Implementation> {
|
|
29
|
-
lastRequestId: number;
|
|
30
33
|
private cache;
|
|
31
34
|
private queryManager;
|
|
32
35
|
readonly id: string;
|
|
@@ -47,7 +50,7 @@ export declare class QueryInfo<TData, TVariables extends OperationVariables = Op
|
|
|
47
50
|
private shouldWrite;
|
|
48
51
|
get hasNext(): boolean;
|
|
49
52
|
private maybeHandleIncrementalResult;
|
|
50
|
-
markQueryResult(incoming: ApolloLink.Result<TData>, { document: query, variables, errorPolicy, cacheWriteBehavior, }: OperationInfo<TData, TVariables>):
|
|
53
|
+
markQueryResult(incoming: ApolloLink.Result<TData>, { document: query, variables, errorPolicy, cacheWriteBehavior, returnPartialData, }: OperationInfo<TData, TVariables>): MarkQueryResult<DataValue.Complete<TData> | DataValue.Streaming<TData>, ExtensionsWithStreamInfo>;
|
|
51
54
|
markMutationResult(incoming: ApolloLink.Result<TData>, mutation: OperationInfo<TData, TVariables, CacheWriteBehavior.FORBID | CacheWriteBehavior.MERGE> & {
|
|
52
55
|
context?: DefaultContext;
|
|
53
56
|
updateQueries: UpdateQueries<TData>;
|
package/core/QueryInfo.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { equal } from "@wry/equality";
|
|
2
|
-
import { getOperationName, graphQLResultHasError, streamInfoSymbol, } from "@apollo/client/utilities/internal";
|
|
2
|
+
import { collectSiblingFields, createFragmentMap, getFragmentDefinitions, getFragmentFromSelection, getMainDefinition, getOperationName, graphQLResultHasError, hasDirectives, isDeferredFragment, isField, isTypenameField, resultKeyNameFromField, streamInfoSymbol, } from "@apollo/client/utilities/internal";
|
|
3
3
|
import { invariant } from "@apollo/client/utilities/invariant";
|
|
4
4
|
const IGNORE = {};
|
|
5
5
|
const destructiveMethodCounts = new WeakMap();
|
|
@@ -26,8 +26,6 @@ const queryInfoIds = new WeakMap();
|
|
|
26
26
|
// It is responsible for reporting results to the cache, merging and in a no-cache
|
|
27
27
|
// scenario accumulating the response.
|
|
28
28
|
export class QueryInfo {
|
|
29
|
-
// TODO remove soon - this should be able to be handled by cancelling old operations before starting new ones
|
|
30
|
-
lastRequestId = 1;
|
|
31
29
|
cache;
|
|
32
30
|
queryManager;
|
|
33
31
|
id;
|
|
@@ -97,7 +95,7 @@ export class QueryInfo {
|
|
|
97
95
|
}
|
|
98
96
|
return incoming;
|
|
99
97
|
}
|
|
100
|
-
markQueryResult(incoming, { document: query, variables, errorPolicy, cacheWriteBehavior, }) {
|
|
98
|
+
markQueryResult(incoming, { document: query, variables, errorPolicy, cacheWriteBehavior, returnPartialData, }) {
|
|
101
99
|
const diffOptions = {
|
|
102
100
|
query,
|
|
103
101
|
variables,
|
|
@@ -109,9 +107,26 @@ export class QueryInfo {
|
|
|
109
107
|
this.observableQuery?.["resetNotifications"]();
|
|
110
108
|
const skipCache = cacheWriteBehavior === 0 /* CacheWriteBehavior.FORBID */;
|
|
111
109
|
const lastDiff = skipCache ? undefined : this.cache.diff(diffOptions);
|
|
112
|
-
|
|
110
|
+
const incrementalResult = this.maybeHandleIncrementalResult(lastDiff?.result, incoming, query);
|
|
111
|
+
let result = {
|
|
112
|
+
...incrementalResult,
|
|
113
|
+
dataState: incrementalResult.data == null ? "empty"
|
|
114
|
+
: this.hasNext ? "streaming"
|
|
115
|
+
: "complete",
|
|
116
|
+
};
|
|
113
117
|
if (skipCache) {
|
|
114
|
-
return
|
|
118
|
+
return {
|
|
119
|
+
...result,
|
|
120
|
+
dataState: result.data == null ? "empty"
|
|
121
|
+
// Ww can simplify the checks for no-cache queries because the
|
|
122
|
+
// result is purely server driven which means we can assume a
|
|
123
|
+
// well-formed GraphQL response. In this case, `streaming` only
|
|
124
|
+
// makes sense if we are using the `@defer` directive and there are
|
|
125
|
+
// more chunks still streaming (i.e. this.hasNext is true). Purely
|
|
126
|
+
// `@stream` queries should always be complete.
|
|
127
|
+
: this.hasNext && hasDirectives(["defer"], query) ? "streaming"
|
|
128
|
+
: "complete",
|
|
129
|
+
};
|
|
115
130
|
}
|
|
116
131
|
if (shouldWriteResult(result, errorPolicy)) {
|
|
117
132
|
// Using a transaction here so we have a chance to read the result
|
|
@@ -179,7 +194,11 @@ export class QueryInfo {
|
|
|
179
194
|
if (lastDiff && lastDiff.complete) {
|
|
180
195
|
// Reuse data from the last good (complete) diff that we
|
|
181
196
|
// received, when possible.
|
|
182
|
-
result = {
|
|
197
|
+
result = {
|
|
198
|
+
...result,
|
|
199
|
+
data: lastDiff.result,
|
|
200
|
+
dataState: "complete",
|
|
201
|
+
};
|
|
183
202
|
return;
|
|
184
203
|
}
|
|
185
204
|
// If the previous this.diff was incomplete, fall through to
|
|
@@ -192,7 +211,27 @@ export class QueryInfo {
|
|
|
192
211
|
// Set without setDiff to avoid triggering a notify call, since
|
|
193
212
|
// we have other ways of notifying for this result.
|
|
194
213
|
if (diff.complete) {
|
|
195
|
-
result = {
|
|
214
|
+
result = {
|
|
215
|
+
...result,
|
|
216
|
+
data: diff.result,
|
|
217
|
+
dataState: "complete",
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
else if (this.hasNext && diff.result !== null) {
|
|
221
|
+
const isPartial = isStreamingPartial(diff, query, variables);
|
|
222
|
+
// If we tolerate partial results always apply `diff.result` to
|
|
223
|
+
// ensure we return the result of any transforms in cache read
|
|
224
|
+
// functions or custom scalars. If we don't tolerate partial
|
|
225
|
+
// results, we only want to apply the diff result if the only hole
|
|
226
|
+
// in the data is at a defer boundary (e.g.
|
|
227
|
+
// `diff.complete === false && isStreamingPartial === false`)
|
|
228
|
+
if (returnPartialData || !isPartial) {
|
|
229
|
+
result = {
|
|
230
|
+
...result,
|
|
231
|
+
data: diff.result,
|
|
232
|
+
dataState: isPartial ? "partial" : "streaming",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
196
235
|
}
|
|
197
236
|
},
|
|
198
237
|
});
|
|
@@ -404,4 +443,129 @@ function shouldWriteResult(result, errorPolicy = "none") {
|
|
|
404
443
|
}
|
|
405
444
|
return writeWithErrors;
|
|
406
445
|
}
|
|
446
|
+
function isStreamingPartial(diff, document, variables) {
|
|
447
|
+
if (!diff.missing)
|
|
448
|
+
return false;
|
|
449
|
+
const fragmentMap = createFragmentMap(getFragmentDefinitions(document));
|
|
450
|
+
return isPartialInSelectionSet(getMainDefinition(document).selectionSet, diff.missing.missing, { fragmentMap, variables });
|
|
451
|
+
}
|
|
452
|
+
function isPartialInSelectionSet(selectionSet, missingTree, context) {
|
|
453
|
+
if (typeof missingTree === "string")
|
|
454
|
+
return true;
|
|
455
|
+
if (missingTree === undefined)
|
|
456
|
+
return false;
|
|
457
|
+
const missingKeys = Object.keys(missingTree);
|
|
458
|
+
if (missingKeys.length > 0 && missingKeys.every(isArrayIndex)) {
|
|
459
|
+
return missingKeys.some((key) => {
|
|
460
|
+
if (typeof missingTree[key] === "string")
|
|
461
|
+
return true;
|
|
462
|
+
return isPartialInSelectionSet(selectionSet, missingTree[key], context);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
const { fragmentMap, variables } = context;
|
|
466
|
+
for (const selection of selectionSet.selections) {
|
|
467
|
+
if (isField(selection)) {
|
|
468
|
+
if (isTypenameField(selection))
|
|
469
|
+
continue;
|
|
470
|
+
const missing = missingTree[resultKeyNameFromField(selection)];
|
|
471
|
+
// If a missing entry is absent, we have a value for this field
|
|
472
|
+
if (missing === undefined)
|
|
473
|
+
continue;
|
|
474
|
+
if (!selection.selectionSet ||
|
|
475
|
+
isPartialInSelectionSet(selection.selectionSet, missing, context)) {
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
const fragment = getFragmentFromSelection(selection, fragmentMap);
|
|
481
|
+
if (!fragment)
|
|
482
|
+
continue;
|
|
483
|
+
if (isDeferredFragment(selection, variables)) {
|
|
484
|
+
// We need to know the non-deferred fields that might overlap with the
|
|
485
|
+
// deferred selection set so that we accurately report the right
|
|
486
|
+
// dataState when all the non-deferred fields are satisfied and only the
|
|
487
|
+
// selections inside the fragment are missing.
|
|
488
|
+
// For example:
|
|
489
|
+
//
|
|
490
|
+
// {
|
|
491
|
+
// recipient {
|
|
492
|
+
// name
|
|
493
|
+
// }
|
|
494
|
+
// ... @defer {
|
|
495
|
+
// recipient {
|
|
496
|
+
// name
|
|
497
|
+
// email
|
|
498
|
+
// }
|
|
499
|
+
// }
|
|
500
|
+
// }
|
|
501
|
+
//
|
|
502
|
+
// dataState should be `streaming`, not `partial` if `name` is present,
|
|
503
|
+
// but `email` is absent since `name` is not deferred.
|
|
504
|
+
if (isPartialDeferBoundary(fragment.selectionSet, missingTree, collectSiblingFields(selectionSet, {
|
|
505
|
+
...context,
|
|
506
|
+
exclude: selection,
|
|
507
|
+
}), fragmentMap)) {
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else if (isPartialInSelectionSet(fragment.selectionSet, missingTree, context)) {
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
function isPartialDeferBoundary(selectionSet, missing, nonDeferredFields, fragmentMap) {
|
|
519
|
+
// This flag tracks whether all fields in this selection set should contain
|
|
520
|
+
// values or should all be missing. A defer boundary is only partial if some
|
|
521
|
+
// fields are missing. The first selection node will set this value and all
|
|
522
|
+
// sibling fields should match after that. As soon as we get a mismatch, we
|
|
523
|
+
// have a partial defer boundary.
|
|
524
|
+
let shouldContainValues;
|
|
525
|
+
for (const selection of selectionSet.selections) {
|
|
526
|
+
if (isField(selection)) {
|
|
527
|
+
if (isTypenameField(selection))
|
|
528
|
+
continue;
|
|
529
|
+
const name = resultKeyNameFromField(selection);
|
|
530
|
+
const nonDeferredField = nonDeferredFields?.[name];
|
|
531
|
+
// If this field is not exclusive to this selection set, we don't care
|
|
532
|
+
// what the value is as far as the defer boundary is concerned. Ignore it
|
|
533
|
+
// and continue checking other siblings.
|
|
534
|
+
if (nonDeferredField === true)
|
|
535
|
+
continue;
|
|
536
|
+
// The value of missing means different things:
|
|
537
|
+
// - undefined: The field is fully satisfied (i.e. it has a value for the field)
|
|
538
|
+
// - string: the field is fully unsatisfied (no scalar value, or object is
|
|
539
|
+
// missing all fields)
|
|
540
|
+
// - object: The field has a selection set and is partially satisfied.
|
|
541
|
+
const missingField = missing[name];
|
|
542
|
+
if (typeof missingField !== "object") {
|
|
543
|
+
const hasValue = missingField === undefined;
|
|
544
|
+
if (shouldContainValues === undefined) {
|
|
545
|
+
shouldContainValues = hasValue;
|
|
546
|
+
}
|
|
547
|
+
if (hasValue !== shouldContainValues) {
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (selection.selectionSet &&
|
|
553
|
+
isPartialDeferBoundary(selection.selectionSet, missingField, nonDeferredField, fragmentMap)) {
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
const fragment = getFragmentFromSelection(selection, fragmentMap);
|
|
559
|
+
if (!fragment)
|
|
560
|
+
continue;
|
|
561
|
+
if (isPartialDeferBoundary(fragment.selectionSet, missing, nonDeferredFields, fragmentMap)) {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
function isArrayIndex(key) {
|
|
569
|
+
return /^\d+$/.test(key);
|
|
570
|
+
}
|
|
407
571
|
//# sourceMappingURL=QueryInfo.js.map
|