relay-runtime 13.0.3 → 13.2.0

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 (50) hide show
  1. package/index.js +1 -1
  2. package/index.js.flow +4 -2
  3. package/lib/index.js +3 -3
  4. package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +3 -4
  5. package/lib/query/GraphQLTag.js +13 -0
  6. package/lib/query/fetchQuery.js +2 -5
  7. package/lib/store/RelayModernEnvironment.js +3 -3
  8. package/lib/store/RelayModernFragmentSpecResolver.js +6 -6
  9. package/lib/store/RelayModernSelector.js +16 -2
  10. package/lib/store/RelayReader.js +71 -6
  11. package/lib/store/RelayStoreSubscriptions.js +4 -2
  12. package/lib/store/RelayStoreUtils.js +1 -0
  13. package/lib/store/ResolverCache.js +25 -13
  14. package/lib/store/experimental-live-resolvers/LiveResolverCache.js +316 -0
  15. package/lib/store/experimental-live-resolvers/LiveResolverStore.js +787 -0
  16. package/lib/store/hasOverlappingIDs.js +1 -1
  17. package/lib/util/RelayConcreteNode.js +2 -0
  18. package/lib/util/RelayFeatureFlags.js +0 -3
  19. package/lib/util/handlePotentialSnapshotErrors.js +73 -0
  20. package/mutations/RelayRecordSourceProxy.js.flow +7 -3
  21. package/mutations/RelayRecordSourceSelectorProxy.js.flow +7 -3
  22. package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +14 -12
  23. package/network/RelayNetworkTypes.js.flow +1 -1
  24. package/package.json +1 -1
  25. package/query/GraphQLTag.js.flow +38 -3
  26. package/query/fetchQuery.js.flow +6 -4
  27. package/relay-runtime.js +2 -2
  28. package/relay-runtime.min.js +2 -2
  29. package/store/RelayModernEnvironment.js.flow +3 -3
  30. package/store/RelayModernFragmentSpecResolver.js.flow +11 -7
  31. package/store/RelayModernSelector.js.flow +32 -8
  32. package/store/RelayReader.js.flow +65 -23
  33. package/store/RelayResponseNormalizer.js.flow +1 -1
  34. package/store/RelayStoreSubscriptions.js.flow +2 -0
  35. package/store/RelayStoreTypes.js.flow +22 -8
  36. package/store/RelayStoreUtils.js.flow +2 -1
  37. package/store/ResolverCache.js.flow +45 -10
  38. package/store/defaultGetDataID.js.flow +1 -1
  39. package/store/experimental-live-resolvers/LiveResolverCache.js.flow +410 -0
  40. package/store/experimental-live-resolvers/LiveResolverStore.js.flow +822 -0
  41. package/store/hasOverlappingIDs.js.flow +1 -1
  42. package/util/ReaderNode.js.flow +17 -1
  43. package/util/RelayConcreteNode.js.flow +9 -1
  44. package/util/RelayFeatureFlags.js.flow +0 -6
  45. package/util/RelayRuntimeTypes.js.flow +20 -1
  46. package/util/deepFreeze.js.flow +1 -1
  47. package/util/handlePotentialSnapshotErrors.js.flow +63 -0
  48. package/util/isEmptyObject.js.flow +1 -1
  49. package/lib/util/reportMissingRequiredFields.js +0 -48
  50. package/util/reportMissingRequiredFields.js.flow +0 -51
@@ -342,7 +342,7 @@ class RelayModernEnvironment implements IEnvironment {
342
342
  |}): RelayObservable<GraphQLResponse> {
343
343
  return this._execute({
344
344
  createSource: () =>
345
- this._network.execute(
345
+ this.getNetwork().execute(
346
346
  operation.request.node.params,
347
347
  operation.request.variables,
348
348
  operation.request.cacheConfig || {},
@@ -372,7 +372,7 @@ class RelayModernEnvironment implements IEnvironment {
372
372
  |}): RelayObservable<GraphQLResponse> {
373
373
  return this._execute({
374
374
  createSource: () =>
375
- this._network.execute(
375
+ this.getNetwork().execute(
376
376
  operation.request.node.params,
377
377
  operation.request.variables,
378
378
  operation.request.cacheConfig || {},
@@ -412,7 +412,7 @@ class RelayModernEnvironment implements IEnvironment {
412
412
  }
413
413
  return this._execute({
414
414
  createSource: () =>
415
- this._network.execute(
415
+ this.getNetwork().execute(
416
416
  operation.request.node.params,
417
417
  operation.request.variables,
418
418
  {
@@ -22,16 +22,17 @@ import type {
22
22
  MissingRequiredFields,
23
23
  PluralReaderSelector,
24
24
  RelayContext,
25
+ RelayResolverErrors,
25
26
  SelectorData,
26
27
  SingularReaderSelector,
27
28
  Snapshot,
28
29
  } from './RelayStoreTypes';
29
30
 
30
31
  const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment');
32
+ const handlePotentialSnapshotErrors = require('../util/handlePotentialSnapshotErrors');
31
33
  const isScalarAndEqual = require('../util/isScalarAndEqual');
32
34
  const recycleNodesInto = require('../util/recycleNodesInto');
33
35
  const RelayFeatureFlags = require('../util/RelayFeatureFlags');
34
- const reportMissingRequiredFields = require('../util/reportMissingRequiredFields');
35
36
  const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
36
37
  const {
37
38
  areEqualSelectors,
@@ -228,6 +229,7 @@ class SelectorResolver {
228
229
  _environment: IEnvironment;
229
230
  _isMissingData: boolean;
230
231
  _missingRequiredFields: ?MissingRequiredFields;
232
+ _relayResolverErrors: RelayResolverErrors;
231
233
  _rootIsQueryRenderer: boolean;
232
234
  _selector: SingularReaderSelector;
233
235
  _subscription: ?Disposable;
@@ -244,6 +246,7 @@ class SelectorResolver {
244
246
  this._data = snapshot.data;
245
247
  this._isMissingData = snapshot.isMissingData;
246
248
  this._missingRequiredFields = snapshot.missingRequiredFields;
249
+ this._relayResolverErrors = snapshot.relayResolverErrors;
247
250
  this._environment = environment;
248
251
  this._rootIsQueryRenderer = rootIsQueryRenderer;
249
252
  this._selector = selector;
@@ -324,12 +327,11 @@ class SelectorResolver {
324
327
  }
325
328
  }
326
329
  }
327
- if (this._missingRequiredFields != null) {
328
- reportMissingRequiredFields(
329
- this._environment,
330
- this._missingRequiredFields,
331
- );
332
- }
330
+ handlePotentialSnapshotErrors(
331
+ this._environment,
332
+ this._missingRequiredFields,
333
+ this._relayResolverErrors,
334
+ );
333
335
  return this._data;
334
336
  }
335
337
 
@@ -345,6 +347,7 @@ class SelectorResolver {
345
347
  this._data = recycleNodesInto(this._data, snapshot.data);
346
348
  this._isMissingData = snapshot.isMissingData;
347
349
  this._missingRequiredFields = snapshot.missingRequiredFields;
350
+ this._relayResolverErrors = snapshot.relayResolverErrors;
348
351
  this._selector = selector;
349
352
  this._subscription = this._environment.subscribe(snapshot, this._onChange);
350
353
  }
@@ -381,6 +384,7 @@ class SelectorResolver {
381
384
  this._data = snapshot.data;
382
385
  this._isMissingData = snapshot.isMissingData;
383
386
  this._missingRequiredFields = snapshot.missingRequiredFields;
387
+ this._relayResolverErrors = snapshot.relayResolverErrors;
384
388
  this._callback();
385
389
  };
386
390
  }
@@ -402,14 +402,7 @@ function getVariablesFromPluralFragment(
402
402
  return variables;
403
403
  }
404
404
 
405
- /**
406
- * @public
407
- *
408
- * Determine if two selectors are equal (represent the same selection). Note
409
- * that this function returns `false` when the two queries/fragments are
410
- * different objects, even if they select the same fields.
411
- */
412
- function areEqualSelectors(
405
+ function areEqualSingularSelectors(
413
406
  thisSelector: SingularReaderSelector,
414
407
  thatSelector: SingularReaderSelector,
415
408
  ): boolean {
@@ -421,6 +414,37 @@ function areEqualSelectors(
421
414
  );
422
415
  }
423
416
 
417
+ /**
418
+ * @public
419
+ *
420
+ * Determine if two selectors are equal (represent the same selection). Note
421
+ * that this function returns `false` when the two queries/fragments are
422
+ * different objects, even if they select the same fields.
423
+ */
424
+ function areEqualSelectors(
425
+ a: ?(SingularReaderSelector | PluralReaderSelector),
426
+ b: ?(SingularReaderSelector | PluralReaderSelector),
427
+ ): boolean {
428
+ if (
429
+ a?.kind === 'SingularReaderSelector' &&
430
+ b?.kind === 'SingularReaderSelector'
431
+ ) {
432
+ return areEqualSingularSelectors(a, b);
433
+ } else if (
434
+ a?.kind === 'PluralReaderSelector' &&
435
+ b?.kind === 'PluralReaderSelector'
436
+ ) {
437
+ return (
438
+ a.selectors.length === b.selectors.length &&
439
+ a.selectors.every((s, i) => areEqualSingularSelectors(s, b.selectors[i]))
440
+ );
441
+ } else if (a == null && b == null) {
442
+ return true;
443
+ } else {
444
+ return false;
445
+ }
446
+ }
447
+
424
448
  function createReaderSelector(
425
449
  fragment: ReaderFragment,
426
450
  dataID: DataID,
@@ -22,6 +22,7 @@ import type {
22
22
  ReaderLinkedField,
23
23
  ReaderModuleImport,
24
24
  ReaderNode,
25
+ ReaderRelayLiveResolver,
25
26
  ReaderRelayResolver,
26
27
  ReaderRequiredField,
27
28
  ReaderScalarField,
@@ -35,6 +36,7 @@ import type {
35
36
  MissingRequiredFields,
36
37
  Record,
37
38
  RecordSource,
39
+ RelayResolverErrors,
38
40
  RequestDescriptor,
39
41
  SelectorData,
40
42
  SingularReaderSelector,
@@ -54,6 +56,7 @@ const {
54
56
  INLINE_FRAGMENT,
55
57
  LINKED_FIELD,
56
58
  MODULE_IMPORT,
59
+ RELAY_LIVE_RESOLVER,
57
60
  RELAY_RESOLVER,
58
61
  REQUIRED_FIELD,
59
62
  SCALAR_FIELD,
@@ -109,6 +112,7 @@ class RelayReader {
109
112
  _selector: SingularReaderSelector;
110
113
  _variables: Variables;
111
114
  _resolverCache: ResolverCache;
115
+ _resolverErrors: RelayResolverErrors;
112
116
  _fragmentName: string;
113
117
 
114
118
  constructor(
@@ -131,6 +135,7 @@ class RelayReader {
131
135
  this._selector = selector;
132
136
  this._variables = selector.variables;
133
137
  this._resolverCache = resolverCache;
138
+ this._resolverErrors = [];
134
139
  this._fragmentName = selector.node.name;
135
140
  }
136
141
 
@@ -204,6 +209,7 @@ class RelayReader {
204
209
  seenRecords: this._seenRecords,
205
210
  selector: this._selector,
206
211
  missingRequiredFields: this._missingRequiredFields,
212
+ relayResolverErrors: this._resolverErrors,
207
213
  };
208
214
  }
209
215
 
@@ -391,6 +397,13 @@ class RelayReader {
391
397
  }
392
398
  break;
393
399
  }
400
+ case RELAY_LIVE_RESOLVER: {
401
+ if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
402
+ throw new Error('Relay Resolver fields are not yet supported.');
403
+ }
404
+ this._readResolverField(selection, record, data);
405
+ break;
406
+ }
394
407
  case RELAY_RESOLVER: {
395
408
  if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
396
409
  throw new Error('Relay Resolver fields are not yet supported.');
@@ -496,6 +509,11 @@ class RelayReader {
496
509
  throw new Error('Relay Resolver fields are not yet supported.');
497
510
  }
498
511
  return this._readResolverField(selection.field, record, data);
512
+ case RELAY_LIVE_RESOLVER:
513
+ if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
514
+ throw new Error('Relay Resolver fields are not yet supported.');
515
+ }
516
+ return this._readResolverField(selection.field, record, data);
499
517
  default:
500
518
  (selection.field.kind: empty);
501
519
  invariant(
@@ -507,7 +525,7 @@ class RelayReader {
507
525
  }
508
526
 
509
527
  _readResolverField(
510
- field: ReaderRelayResolver,
528
+ field: ReaderRelayResolver | ReaderRelayLiveResolver,
511
529
  record: Record,
512
530
  data: SelectorData,
513
531
  ): mixed {
@@ -525,6 +543,9 @@ class RelayReader {
525
543
  let fragmentReaderSelector;
526
544
  let fragmentMissingRequiredFields: ?MissingRequiredFields;
527
545
  let previousMissingRequriedFields: ?MissingRequiredFields;
546
+
547
+ let currentResolverErrors: RelayResolverErrors;
548
+ let previousResolverErrors: RelayResolverErrors;
528
549
  const fragmentSeenRecordIDs = new Set();
529
550
 
530
551
  const getDataForResolverFragment = singularReaderSelector => {
@@ -541,12 +562,16 @@ class RelayReader {
541
562
  const resolverFragmentData = {};
542
563
  previousMissingRequriedFields = this._missingRequiredFields;
543
564
  this._missingRequiredFields = null;
565
+
566
+ previousResolverErrors = this._resolverErrors;
567
+ this._resolverErrors = [];
544
568
  this._createInlineDataOrResolverFragmentPointer(
545
569
  singularReaderSelector.node,
546
570
  record,
547
571
  resolverFragmentData,
548
572
  );
549
573
  fragmentMissingRequiredFields = this._missingRequiredFields;
574
+ currentResolverErrors = this._resolverErrors;
550
575
  fragmentValue = resolverFragmentData[FRAGMENTS_KEY]?.[fragment.name];
551
576
  invariant(
552
577
  typeof fragmentValue === 'object' && fragmentValue !== null,
@@ -556,39 +581,56 @@ class RelayReader {
556
581
  } finally {
557
582
  this._seenRecords = existingSeenRecords;
558
583
  this._missingRequiredFields = previousMissingRequriedFields;
584
+ this._resolverErrors = previousResolverErrors;
559
585
  }
560
586
  };
561
587
  const resolverContext = {getDataForResolverFragment};
562
588
 
563
- const [result, seenRecord, missingRequiredFields] =
589
+ const evaluate = () => {
590
+ const key = {
591
+ __id: RelayModernRecord.getDataID(record),
592
+ __fragmentOwner: this._owner,
593
+ __fragments: {
594
+ [fragment.name]: {}, // Arguments to this fragment; not yet supported.
595
+ },
596
+ };
597
+ return withResolverContext(resolverContext, () => {
598
+ let resolverResult = null;
599
+ try {
600
+ // $FlowFixMe[prop-missing] - resolver module's type signature is a lie
601
+ resolverResult = resolverModule(key);
602
+ } catch (e) {
603
+ // `field.path` is typed as nullable while we rollout compiler changes.
604
+ const path = field.path ?? '[UNKNOWN]';
605
+ currentResolverErrors.push({
606
+ field: {path, owner: this._fragmentName},
607
+ error: e,
608
+ });
609
+ }
610
+ return {
611
+ resolverResult,
612
+ errors: currentResolverErrors,
613
+ fragmentValue,
614
+ resolverID,
615
+ seenRecordIDs: fragmentSeenRecordIDs,
616
+ readerSelector: fragmentReaderSelector,
617
+ missingRequiredFields: fragmentMissingRequiredFields,
618
+ };
619
+ });
620
+ };
621
+
622
+ const [result, seenRecord, resolverErrors, missingRequiredFields] =
564
623
  this._resolverCache.readFromCacheOrEvaluate(
565
624
  record,
566
625
  field,
567
626
  this._variables,
568
- () => {
569
- const key = {
570
- __id: RelayModernRecord.getDataID(record),
571
- __fragmentOwner: this._owner,
572
- __fragments: {
573
- [fragment.name]: {}, // Arguments to this fragment; not yet supported.
574
- },
575
- };
576
- return withResolverContext(resolverContext, () => {
577
- // $FlowFixMe[prop-missing] - resolver module's type signature is a lie
578
- const resolverResult = resolverModule(key);
579
- return {
580
- resolverResult,
581
- fragmentValue,
582
- resolverID,
583
- seenRecordIDs: fragmentSeenRecordIDs,
584
- readerSelector: fragmentReaderSelector,
585
- missingRequiredFields: fragmentMissingRequiredFields,
586
- };
587
- });
588
- },
627
+ evaluate,
589
628
  getDataForResolverFragment,
590
629
  );
591
630
 
631
+ for (const resolverError of resolverErrors) {
632
+ this._resolverErrors.push(resolverError);
633
+ }
592
634
  if (missingRequiredFields != null) {
593
635
  this._addMissingRequiredFields(missingRequiredFields);
594
636
  }
@@ -86,7 +86,7 @@ const invariant = require('invariant');
86
86
  const warning = require('warning');
87
87
 
88
88
  export type GetDataID = (
89
- fieldValue: interface {[string]: mixed},
89
+ fieldValue: {[string]: mixed},
90
90
  typeName: string,
91
91
  ) => mixed;
92
92
 
@@ -105,6 +105,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
105
105
  seenRecords: backup.seenRecords,
106
106
  selector: backup.selector,
107
107
  missingRequiredFields: backup.missingRequiredFields,
108
+ relayResolverErrors: backup.relayResolverErrors,
108
109
  };
109
110
  } else {
110
111
  subscription.stale = true;
@@ -167,6 +168,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
167
168
  seenRecords: nextSnapshot.seenRecords,
168
169
  selector: nextSnapshot.selector,
169
170
  missingRequiredFields: nextSnapshot.missingRequiredFields,
171
+ relayResolverErrors: nextSnapshot.relayResolverErrors,
170
172
  }: Snapshot);
171
173
  if (__DEV__) {
172
174
  deepFreeze(nextSnapshot);
@@ -26,7 +26,6 @@ import type {
26
26
  UploadableMap,
27
27
  } from '../network/RelayNetworkTypes';
28
28
  import type RelayObservable from '../network/RelayObservable';
29
- import type {GraphQLTaggedNode} from '../query/GraphQLTag';
30
29
  import type {RequestIdentifier} from '../util/getRequestIdentifier';
31
30
  import type {
32
31
  NormalizationArgument,
@@ -44,8 +43,9 @@ import type {
44
43
  CacheConfig,
45
44
  DataID,
46
45
  Disposable,
47
- OperationType,
48
46
  RenderPolicy,
47
+ UpdatableQuery,
48
+ UpdatableQueryType,
49
49
  Variables,
50
50
  } from '../util/RelayRuntimeTypes';
51
51
  import type {InvalidationState} from './RelayModernStore';
@@ -57,7 +57,7 @@ export type OperationTracker = RelayOperationTracker;
57
57
 
58
58
  export type MutationParameters = {|
59
59
  +response: {...},
60
- +variables: interface {},
60
+ +variables: {...},
61
61
  +rawResponse?: {...},
62
62
  |};
63
63
 
@@ -112,14 +112,14 @@ export type NormalizationSelector = {|
112
112
  +variables: Variables,
113
113
  |};
114
114
 
115
- type MissingRequiredField = {|
115
+ type FieldLocation = {|
116
116
  path: string,
117
117
  owner: string,
118
118
  |};
119
119
 
120
120
  export type MissingRequiredFields = $ReadOnly<
121
- | {|action: 'THROW', field: MissingRequiredField|}
122
- | {|action: 'LOG', fields: Array<MissingRequiredField>|},
121
+ | {|action: 'THROW', field: FieldLocation|}
122
+ | {|action: 'LOG', fields: Array<FieldLocation>|},
123
123
  >;
124
124
 
125
125
  export type ClientEdgeTraversalInfo = {|
@@ -135,6 +135,13 @@ export type MissingClientEdgeRequestInfo = {|
135
135
  +clientEdgeDestinationID: DataID,
136
136
  |};
137
137
 
138
+ export type RelayResolverError = {|
139
+ field: FieldLocation,
140
+ error: Error,
141
+ |};
142
+
143
+ export type RelayResolverErrors = Array<RelayResolverError>;
144
+
138
145
  /**
139
146
  * A representation of a selector and its results at a particular point in time.
140
147
  */
@@ -145,6 +152,7 @@ export type Snapshot = {|
145
152
  +seenRecords: DataIDSet,
146
153
  +selector: SingularReaderSelector,
147
154
  +missingRequiredFields: ?MissingRequiredFields,
155
+ +relayResolverErrors: RelayResolverErrors,
148
156
  |};
149
157
 
150
158
  /**
@@ -466,8 +474,8 @@ export interface RecordSourceProxy {
466
474
  get(dataID: DataID): ?RecordProxy;
467
475
  getRoot(): RecordProxy;
468
476
  invalidateStore(): void;
469
- readUpdatableQuery_EXPERIMENTAL<TQuery: OperationType>(
470
- query: GraphQLTaggedNode,
477
+ readUpdatableQuery_EXPERIMENTAL<TQuery: UpdatableQueryType>(
478
+ query: UpdatableQuery<TQuery['variables'], TQuery['response']>,
471
479
  variables: TQuery['variables'],
472
480
  ): TQuery['response'];
473
481
  }
@@ -1072,6 +1080,12 @@ export type RequiredFieldLogger = (
1072
1080
  +kind: 'missing_field.throw',
1073
1081
  +owner: string,
1074
1082
  +fieldPath: string,
1083
+ |}
1084
+ | {|
1085
+ +kind: 'relay_resolver.error',
1086
+ +owner: string,
1087
+ +fieldPath: string,
1088
+ +error: Error,
1075
1089
  |},
1076
1090
  ) => void;
1077
1091
 
@@ -29,7 +29,7 @@ const RelayConcreteNode = require('../util/RelayConcreteNode');
29
29
  const stableCopy = require('../util/stableCopy');
30
30
  const invariant = require('invariant');
31
31
 
32
- export type Arguments = interface {+[string]: mixed};
32
+ export type Arguments = {+[string]: mixed};
33
33
 
34
34
  const {VARIABLE, LITERAL, OBJECT_VALUE, LIST_VALUE} = RelayConcreteNode;
35
35
 
@@ -220,6 +220,7 @@ const RelayStoreUtils = {
220
220
  RELAY_RESOLVER_INPUTS_KEY: '__resolverInputValues',
221
221
  RELAY_RESOLVER_READER_SELECTOR_KEY: '__resolverReaderSelector',
222
222
  RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY: '__resolverMissingRequiredFields',
223
+ RELAY_RESOLVER_ERROR_KEY: '__resolverError',
223
224
 
224
225
  formatStorageKey,
225
226
  getArgumentValue,
@@ -12,19 +12,25 @@
12
12
 
13
13
  'use strict';
14
14
 
15
- import type {ReaderRelayResolver} from '../util/ReaderNode';
15
+ import type {
16
+ ReaderRelayLiveResolver,
17
+ ReaderRelayResolver,
18
+ } from '../util/ReaderNode';
16
19
  import type {DataID, Variables} from '../util/RelayRuntimeTypes';
17
20
  import type {
18
21
  MissingRequiredFields,
19
22
  MutableRecordSource,
20
23
  Record,
24
+ RelayResolverErrors,
21
25
  SingularReaderSelector,
22
26
  } from './RelayStoreTypes';
23
27
 
24
28
  const recycleNodesInto = require('../util/recycleNodesInto');
29
+ const {RELAY_LIVE_RESOLVER} = require('../util/RelayConcreteNode');
25
30
  const {generateClientID} = require('./ClientID');
26
31
  const RelayModernRecord = require('./RelayModernRecord');
27
32
  const {
33
+ RELAY_RESOLVER_ERROR_KEY,
28
34
  RELAY_RESOLVER_INPUTS_KEY,
29
35
  RELAY_RESOLVER_INVALIDATION_KEY,
30
36
  RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY,
@@ -32,6 +38,7 @@ const {
32
38
  RELAY_RESOLVER_VALUE_KEY,
33
39
  getStorageKey,
34
40
  } = require('./RelayStoreUtils');
41
+ const invariant = require('invariant');
35
42
  const warning = require('warning');
36
43
 
37
44
  type ResolverID = string;
@@ -42,17 +49,23 @@ type EvaluationResult<T> = {|
42
49
  resolverID: ResolverID,
43
50
  seenRecordIDs: Set<DataID>,
44
51
  readerSelector: SingularReaderSelector,
52
+ errors: RelayResolverErrors,
45
53
  missingRequiredFields: ?MissingRequiredFields,
46
54
  |};
47
55
 
48
56
  export interface ResolverCache {
49
57
  readFromCacheOrEvaluate<T>(
50
58
  record: Record,
51
- field: ReaderRelayResolver,
59
+ field: ReaderRelayResolver | ReaderRelayLiveResolver,
52
60
  variables: Variables,
53
61
  evaluate: () => EvaluationResult<T>,
54
62
  getDataForResolverFragment: (SingularReaderSelector) => mixed,
55
- ): [T /* Answer */, ?DataID /* Seen record */, ?MissingRequiredFields];
63
+ ): [
64
+ T /* Answer */,
65
+ ?DataID /* Seen record */,
66
+ RelayResolverErrors,
67
+ ?MissingRequiredFields,
68
+ ];
56
69
  invalidateDataIDs(
57
70
  updatedDataIDs: Set<DataID>, // Mutated in place
58
71
  ): void;
@@ -64,13 +77,22 @@ const emptySet: $ReadOnlySet<any> = new Set();
64
77
  class NoopResolverCache implements ResolverCache {
65
78
  readFromCacheOrEvaluate<T>(
66
79
  record: Record,
67
- field: ReaderRelayResolver,
80
+ field: ReaderRelayResolver | ReaderRelayLiveResolver,
68
81
  variables: Variables,
69
82
  evaluate: () => EvaluationResult<T>,
70
83
  getDataForResolverFragment: SingularReaderSelector => mixed,
71
- ): [T /* Answer */, ?DataID /* Seen record */, ?MissingRequiredFields] {
72
- const {resolverResult, missingRequiredFields} = evaluate();
73
- return [resolverResult, undefined, missingRequiredFields];
84
+ ): [
85
+ T /* Answer */,
86
+ ?DataID /* Seen record */,
87
+ RelayResolverErrors,
88
+ ?MissingRequiredFields,
89
+ ] {
90
+ invariant(
91
+ field.kind !== RELAY_LIVE_RESOLVER,
92
+ 'This store does not support Live Resolvers',
93
+ );
94
+ const {resolverResult, missingRequiredFields, errors} = evaluate();
95
+ return [resolverResult, undefined, errors, missingRequiredFields];
74
96
  }
75
97
  invalidateDataIDs(updatedDataIDs: Set<DataID>): void {}
76
98
  }
@@ -102,11 +124,16 @@ class RecordResolverCache implements ResolverCache {
102
124
 
103
125
  readFromCacheOrEvaluate<T>(
104
126
  record: Record,
105
- field: ReaderRelayResolver,
127
+ field: ReaderRelayResolver | ReaderRelayLiveResolver,
106
128
  variables: Variables,
107
129
  evaluate: () => EvaluationResult<T>,
108
130
  getDataForResolverFragment: SingularReaderSelector => mixed,
109
- ): [T /* Answer */, ?DataID /* Seen record */, ?MissingRequiredFields] {
131
+ ): [
132
+ T /* Answer */,
133
+ ?DataID /* Seen record */,
134
+ RelayResolverErrors,
135
+ ?MissingRequiredFields,
136
+ ] {
110
137
  const recordSource = this._getRecordSource();
111
138
  const recordID = RelayModernRecord.getDataID(record);
112
139
 
@@ -142,6 +169,11 @@ class RecordResolverCache implements ResolverCache {
142
169
  RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY,
143
170
  evaluationResult.missingRequiredFields,
144
171
  );
172
+ RelayModernRecord.setValue(
173
+ linkedRecord,
174
+ RELAY_RESOLVER_ERROR_KEY,
175
+ evaluationResult.errors,
176
+ );
145
177
  recordSource.set(linkedID, linkedRecord);
146
178
 
147
179
  // Link the resolver value record to the resolver field of the record being read:
@@ -168,7 +200,10 @@ class RecordResolverCache implements ResolverCache {
168
200
  const missingRequiredFields: ?MissingRequiredFields =
169
201
  // $FlowFixMe[incompatible-type] - casting mixed
170
202
  linkedRecord[RELAY_RESOLVER_MISSING_REQUIRED_FIELDS_KEY];
171
- return [answer, linkedID, missingRequiredFields];
203
+
204
+ // $FlowFixMe[incompatible-type] - casting mixed
205
+ const errors: RelayResolverErrors = linkedRecord[RELAY_RESOLVER_ERROR_KEY];
206
+ return [answer, linkedID, errors, missingRequiredFields];
172
207
  }
173
208
 
174
209
  invalidateDataIDs(
@@ -15,7 +15,7 @@
15
15
  const {VIEWER_ID, VIEWER_TYPE} = require('./ViewerPattern');
16
16
 
17
17
  function defaultGetDataID(
18
- fieldValue: interface {[string]: mixed},
18
+ fieldValue: {[string]: mixed},
19
19
  typeName: string,
20
20
  ): mixed {
21
21
  if (typeName === VIEWER_TYPE) {