@apollo/client 4.3.0-alpha.1 → 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.
Files changed (135) hide show
  1. package/CHANGELOG.md +285 -0
  2. package/__cjs/cache/core/Scalar.cjs +36 -0
  3. package/__cjs/cache/core/Scalar.cjs.map +1 -0
  4. package/__cjs/cache/core/Scalar.d.cts +20 -0
  5. package/__cjs/cache/core/cache.cjs +8 -1
  6. package/__cjs/cache/core/cache.cjs.map +1 -1
  7. package/__cjs/cache/core/cache.d.cts +16 -0
  8. package/__cjs/cache/index.cjs +3 -1
  9. package/__cjs/cache/index.cjs.map +1 -1
  10. package/__cjs/cache/index.d.cts +3 -2
  11. package/__cjs/cache/inmemory/entityStore.cjs +45 -2
  12. package/__cjs/cache/inmemory/entityStore.cjs.map +1 -1
  13. package/__cjs/cache/inmemory/entityStore.d.cts +3 -0
  14. package/__cjs/cache/inmemory/helpers.d.cts +9 -1
  15. package/__cjs/cache/inmemory/inMemoryCache.cjs +73 -4
  16. package/__cjs/cache/inmemory/inMemoryCache.cjs.map +1 -1
  17. package/__cjs/cache/inmemory/inMemoryCache.d.cts +35 -3
  18. package/__cjs/cache/inmemory/policies.cjs +8 -1
  19. package/__cjs/cache/inmemory/policies.cjs.map +1 -1
  20. package/__cjs/cache/inmemory/policies.d.cts +4 -1
  21. package/__cjs/cache/inmemory/readFromStore.cjs +16 -3
  22. package/__cjs/cache/inmemory/readFromStore.cjs.map +1 -1
  23. package/__cjs/cache/inmemory/types.d.cts +20 -4
  24. package/__cjs/cache/inmemory/writeToStore.cjs +10 -6
  25. package/__cjs/cache/inmemory/writeToStore.cjs.map +1 -1
  26. package/__cjs/core/ApolloClient.cjs +2 -2
  27. package/__cjs/core/ApolloClient.cjs.map +1 -1
  28. package/__cjs/core/ObservableQuery.cjs +1 -3
  29. package/__cjs/core/ObservableQuery.cjs.map +1 -1
  30. package/__cjs/core/QueryInfo.cjs +211 -37
  31. package/__cjs/core/QueryInfo.cjs.map +1 -1
  32. package/__cjs/core/QueryInfo.d.cts +5 -2
  33. package/__cjs/core/QueryManager.cjs +12 -18
  34. package/__cjs/core/QueryManager.cjs.map +1 -1
  35. package/__cjs/core/QueryManager.d.cts +1 -3
  36. package/__cjs/core/index.cjs +2 -1
  37. package/__cjs/core/index.cjs.map +1 -1
  38. package/__cjs/core/index.d.cts +2 -2
  39. package/__cjs/invariantErrorCodes.cjs +12 -7
  40. package/__cjs/react/hooks/useLazyQuery.cjs.map +1 -1
  41. package/__cjs/react/hooks/useLazyQuery.d.cts +13 -13
  42. package/__cjs/utilities/index.cjs.map +1 -1
  43. package/__cjs/utilities/index.d.cts +1 -0
  44. package/__cjs/utilities/internal/collectSiblingFields.cjs +61 -0
  45. package/__cjs/utilities/internal/collectSiblingFields.cjs.map +1 -0
  46. package/__cjs/utilities/internal/collectSiblingFields.d.cts +21 -0
  47. package/__cjs/utilities/internal/getUnwrappedType.cjs +20 -0
  48. package/__cjs/utilities/internal/getUnwrappedType.cjs.map +1 -0
  49. package/__cjs/utilities/internal/getUnwrappedType.d.cts +8 -0
  50. package/__cjs/utilities/internal/index.cjs +10 -2
  51. package/__cjs/utilities/internal/index.cjs.map +1 -1
  52. package/__cjs/utilities/internal/index.d.cts +6 -0
  53. package/__cjs/utilities/internal/isDeferredFragment.cjs +23 -0
  54. package/__cjs/utilities/internal/isDeferredFragment.cjs.map +1 -0
  55. package/__cjs/utilities/internal/isDeferredFragment.d.cts +4 -0
  56. package/__cjs/utilities/internal/isTypenameField.cjs +7 -0
  57. package/__cjs/utilities/internal/isTypenameField.cjs.map +1 -0
  58. package/__cjs/utilities/internal/isTypenameField.d.cts +3 -0
  59. package/__cjs/utilities/internal/types/IsLooselyEqual.cjs +3 -0
  60. package/__cjs/utilities/internal/types/IsLooselyEqual.cjs.map +1 -0
  61. package/__cjs/utilities/internal/types/IsLooselyEqual.d.cts +10 -0
  62. package/__cjs/utilities/policies/pagination.cjs.map +1 -1
  63. package/__cjs/utilities/policies/pagination.d.cts +1 -1
  64. package/__cjs/utilities/subscriptions/relay/index.cjs +1 -0
  65. package/__cjs/utilities/subscriptions/relay/index.cjs.map +1 -1
  66. package/__cjs/version.cjs +1 -1
  67. package/cache/core/Scalar.d.ts +20 -0
  68. package/cache/core/Scalar.js +32 -0
  69. package/cache/core/Scalar.js.map +1 -0
  70. package/cache/core/cache.d.ts +16 -0
  71. package/cache/core/cache.js +8 -1
  72. package/cache/core/cache.js.map +1 -1
  73. package/cache/index.d.ts +3 -2
  74. package/cache/index.js +1 -0
  75. package/cache/index.js.map +1 -1
  76. package/cache/inmemory/entityStore.d.ts +3 -0
  77. package/cache/inmemory/entityStore.js +46 -3
  78. package/cache/inmemory/entityStore.js.map +1 -1
  79. package/cache/inmemory/helpers.d.ts +9 -1
  80. package/cache/inmemory/inMemoryCache.d.ts +35 -3
  81. package/cache/inmemory/inMemoryCache.js +74 -5
  82. package/cache/inmemory/inMemoryCache.js.map +1 -1
  83. package/cache/inmemory/policies.d.ts +4 -1
  84. package/cache/inmemory/policies.js +8 -1
  85. package/cache/inmemory/policies.js.map +1 -1
  86. package/cache/inmemory/readFromStore.js +11 -2
  87. package/cache/inmemory/readFromStore.js.map +1 -1
  88. package/cache/inmemory/types.d.ts +20 -4
  89. package/cache/inmemory/types.js.map +1 -1
  90. package/cache/inmemory/writeToStore.js +11 -7
  91. package/cache/inmemory/writeToStore.js.map +1 -1
  92. package/core/ApolloClient.js +2 -2
  93. package/core/ApolloClient.js.map +1 -1
  94. package/core/ObservableQuery.js +1 -3
  95. package/core/ObservableQuery.js.map +1 -1
  96. package/core/QueryInfo.d.ts +5 -2
  97. package/core/QueryInfo.js +212 -38
  98. package/core/QueryInfo.js.map +1 -1
  99. package/core/QueryManager.d.ts +1 -3
  100. package/core/QueryManager.js +12 -18
  101. package/core/QueryManager.js.map +1 -1
  102. package/core/index.d.ts +2 -2
  103. package/core/index.js +1 -1
  104. package/core/index.js.map +1 -1
  105. package/invariantErrorCodes.js +12 -7
  106. package/package.json +1 -1
  107. package/react/hooks/useLazyQuery.d.ts +13 -13
  108. package/react/hooks/useLazyQuery.js.map +1 -1
  109. package/react/hooks-compiled/useLazyQuery.d.ts +13 -13
  110. package/react/hooks-compiled/useLazyQuery.js.map +1 -1
  111. package/utilities/index.d.ts +1 -0
  112. package/utilities/index.js.map +1 -1
  113. package/utilities/internal/collectSiblingFields.d.ts +21 -0
  114. package/utilities/internal/collectSiblingFields.js +58 -0
  115. package/utilities/internal/collectSiblingFields.js.map +1 -0
  116. package/utilities/internal/getUnwrappedType.d.ts +8 -0
  117. package/utilities/internal/getUnwrappedType.js +17 -0
  118. package/utilities/internal/getUnwrappedType.js.map +1 -0
  119. package/utilities/internal/index.d.ts +6 -0
  120. package/utilities/internal/index.js +4 -0
  121. package/utilities/internal/index.js.map +1 -1
  122. package/utilities/internal/isDeferredFragment.d.ts +4 -0
  123. package/utilities/internal/isDeferredFragment.js +20 -0
  124. package/utilities/internal/isDeferredFragment.js.map +1 -0
  125. package/utilities/internal/isTypenameField.d.ts +3 -0
  126. package/utilities/internal/isTypenameField.js +4 -0
  127. package/utilities/internal/isTypenameField.js.map +1 -0
  128. package/utilities/internal/types/IsLooselyEqual.d.ts +10 -0
  129. package/utilities/internal/types/IsLooselyEqual.js +2 -0
  130. package/utilities/internal/types/IsLooselyEqual.js.map +1 -0
  131. package/utilities/policies/pagination.d.ts +1 -1
  132. package/utilities/policies/pagination.js.map +1 -1
  133. package/utilities/subscriptions/relay/index.js +1 -0
  134. package/utilities/subscriptions/relay/index.js.map +1 -1
  135. package/version.js +1 -1
@@ -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>): FormattedExecutionResult<DataValue.Complete<TData> | DataValue.Streaming<TData>, ExtensionsWithStreamInfo>;
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
- let result = this.maybeHandleIncrementalResult(lastDiff?.result, incoming, query);
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 result;
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 = { ...result, data: lastDiff.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 = { ...result, data: diff.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
  });
@@ -289,38 +328,34 @@ export class QueryInfo {
289
328
  const { update } = mutation;
290
329
  // Determine whether result is a SingleExecutionResult,
291
330
  // or the final ExecutionPatchResult.
292
- if (update) {
293
- if (!skipCache) {
294
- // Re-read the ROOT_MUTATION data we just wrote into the cache
295
- // (the first cache.write call in the cacheWrites.forEach loop
296
- // above), so field read functions have a chance to run for
297
- // fields within mutation result objects.
298
- const diff = cache.diff({
299
- id: "ROOT_MUTATION",
300
- // The cache complains if passed a mutation where it expects a
301
- // query, so we transform mutations and subscriptions to queries
302
- // (only once, thanks to this.transformCache).
303
- query: this.queryManager.getDocumentInfo(mutation.document)
304
- .asQuery,
305
- variables: mutation.variables,
306
- optimistic: false,
307
- returnPartialData: true,
308
- });
309
- if (diff.complete) {
310
- result = {
311
- ...result,
312
- data: diff.result,
313
- };
314
- }
315
- }
316
- // If we've received the whole response, call the update function.
317
- if (!this.hasNext) {
318
- update(cache, result, {
319
- context: mutation.context,
320
- variables: mutation.variables,
321
- });
331
+ // Re-read from the cache after writing to it to update `result`
332
+ // with any parsed scalar values that might have been written.
333
+ if (!skipCache) {
334
+ const diff = cache.diff({
335
+ id: "ROOT_MUTATION",
336
+ // The cache complains if passed a mutation where it expects a
337
+ // query, so we transform mutations and subscriptions to queries
338
+ // (only once, thanks to this.transformCache).
339
+ query: this.queryManager.getDocumentInfo(mutation.document)
340
+ .asQuery,
341
+ variables: mutation.variables,
342
+ optimistic: false,
343
+ returnPartialData: true,
344
+ });
345
+ if (diff.complete) {
346
+ result = {
347
+ ...result,
348
+ data: diff.result,
349
+ };
322
350
  }
323
351
  }
352
+ // If we've received the whole response, call the update function.
353
+ if (update && !this.hasNext) {
354
+ update(cache, result, {
355
+ context: mutation.context,
356
+ variables: mutation.variables,
357
+ });
358
+ }
324
359
  // TODO Do this with cache.evict({ id: 'ROOT_MUTATION' }) but make it
325
360
  // shallow to allow rolling back optimistic evictions.
326
361
  if (!skipCache && !mutation.keepRootFields && !this.hasNext) {
@@ -381,6 +416,20 @@ export class QueryInfo {
381
416
  variables: variables,
382
417
  extensions: result.extensions,
383
418
  });
419
+ // Re-read from the cache to get parsed scalar values
420
+ const diff = this.cache.diff({
421
+ // The cache complains if passed a mutation where it expects a
422
+ // query, so we transform mutations and subscriptions to queries
423
+ // (only once, thanks to this.transformCache).
424
+ query: this.queryManager.getDocumentInfo(document).asQuery,
425
+ id: "ROOT_SUBSCRIPTION",
426
+ variables,
427
+ optimistic: false,
428
+ returnPartialData: true,
429
+ });
430
+ if (diff.complete) {
431
+ result.data = diff.result;
432
+ }
384
433
  }
385
434
  this.queryManager.broadcastQueries();
386
435
  }
@@ -394,4 +443,129 @@ function shouldWriteResult(result, errorPolicy = "none") {
394
443
  }
395
444
  return writeWithErrors;
396
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
+ }
397
571
  //# sourceMappingURL=QueryInfo.js.map