relay-runtime 10.0.1 → 10.1.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 (82) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +6 -0
  2. package/handlers/connection/MutationHandlers.js.flow +152 -20
  3. package/index.js +1 -1
  4. package/index.js.flow +17 -1
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
  6. package/lib/handlers/connection/MutationHandlers.js +185 -21
  7. package/lib/index.js +7 -0
  8. package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -7
  9. package/lib/mutations/commitMutation.js +1 -4
  10. package/lib/mutations/validateMutation.js +28 -12
  11. package/lib/network/RelayQueryResponseCache.js +3 -7
  12. package/lib/query/GraphQLTag.js +2 -1
  13. package/lib/query/fetchQuery.js +2 -3
  14. package/lib/query/fetchQueryInternal.js +2 -3
  15. package/lib/store/DataChecker.js +85 -10
  16. package/lib/store/RelayConcreteVariables.js +2 -6
  17. package/lib/store/RelayModernEnvironment.js +81 -72
  18. package/lib/store/RelayModernFragmentSpecResolver.js +14 -7
  19. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  20. package/lib/store/RelayModernQueryExecutor.js +46 -33
  21. package/lib/store/RelayModernRecord.js +3 -7
  22. package/lib/store/RelayModernStore.js +39 -137
  23. package/lib/store/RelayOperationTracker.js +7 -9
  24. package/lib/store/RelayOptimisticRecordSource.js +2 -6
  25. package/lib/store/RelayPublishQueue.js +1 -1
  26. package/lib/store/RelayReader.js +196 -33
  27. package/lib/store/RelayRecordSourceMapImpl.js +3 -5
  28. package/lib/store/RelayReferenceMarker.js +87 -5
  29. package/lib/store/RelayResponseNormalizer.js +115 -19
  30. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  31. package/lib/store/RelayStoreSubscriptions.js +162 -0
  32. package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +258 -0
  33. package/lib/store/StoreInspector.js +2 -6
  34. package/lib/store/createRelayContext.js +5 -0
  35. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  36. package/lib/store/normalizeRelayPayload.js +2 -6
  37. package/lib/subscription/requestSubscription.js +2 -3
  38. package/lib/util/NormalizationNode.js +1 -5
  39. package/lib/util/RelayConcreteNode.js +2 -0
  40. package/lib/util/RelayFeatureFlags.js +7 -2
  41. package/lib/util/createPayloadFor3DField.js +2 -7
  42. package/lib/util/getFragmentIdentifier.js +12 -3
  43. package/lib/util/getOperation.js +33 -0
  44. package/lib/util/isEmptyObject.js +25 -0
  45. package/lib/util/recycleNodesInto.js +4 -1
  46. package/lib/util/reportMissingRequiredFields.js +48 -0
  47. package/mutations/commitMutation.js.flow +1 -2
  48. package/mutations/validateMutation.js.flow +34 -5
  49. package/network/RelayNetworkTypes.js.flow +22 -0
  50. package/package.json +2 -2
  51. package/query/GraphQLTag.js.flow +3 -1
  52. package/query/fetchQuery.js.flow +2 -2
  53. package/query/fetchQueryInternal.js.flow +0 -5
  54. package/relay-runtime.js +2 -2
  55. package/relay-runtime.min.js +2 -2
  56. package/store/DataChecker.js.flow +68 -2
  57. package/store/RelayModernEnvironment.js.flow +107 -87
  58. package/store/RelayModernFragmentSpecResolver.js.flow +13 -1
  59. package/store/RelayModernOperationDescriptor.js.flow +5 -1
  60. package/store/RelayModernQueryExecutor.js.flow +47 -23
  61. package/store/RelayModernStore.js.flow +33 -107
  62. package/store/RelayPublishQueue.js.flow +1 -1
  63. package/store/RelayReader.js.flow +180 -15
  64. package/store/RelayReferenceMarker.js.flow +72 -5
  65. package/store/RelayResponseNormalizer.js.flow +130 -19
  66. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  67. package/store/RelayStoreSubscriptions.js.flow +168 -0
  68. package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +259 -0
  69. package/store/RelayStoreTypes.js.flow +130 -37
  70. package/store/createRelayContext.js.flow +3 -0
  71. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  72. package/subscription/requestSubscription.js.flow +5 -2
  73. package/util/NormalizationNode.js.flow +17 -2
  74. package/util/ReaderNode.js.flow +20 -1
  75. package/util/RelayConcreteNode.js.flow +6 -0
  76. package/util/RelayFeatureFlags.js.flow +12 -1
  77. package/util/getFragmentIdentifier.js.flow +33 -9
  78. package/util/getOperation.js.flow +40 -0
  79. package/util/getRequestIdentifier.js.flow +1 -1
  80. package/util/isEmptyObject.js.flow +25 -0
  81. package/util/recycleNodesInto.js.flow +11 -0
  82. package/util/reportMissingRequiredFields.js.flow +51 -0
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const RelayReader = require('./RelayReader');
16
+
17
+ const deepFreeze = require('../util/deepFreeze');
18
+ const recycleNodesInto = require('../util/recycleNodesInto');
19
+
20
+ import type {DataID, Disposable} from '../util/RelayRuntimeTypes';
21
+ import type {
22
+ RecordMap,
23
+ RecordSource,
24
+ RequestDescriptor,
25
+ Snapshot,
26
+ StoreSubscriptions,
27
+ UpdatedRecords,
28
+ } from './RelayStoreTypes';
29
+
30
+ type Subscription = {|
31
+ backup: ?Snapshot,
32
+ callback: (snapshot: Snapshot) => void,
33
+ notifiedRevision: number,
34
+ snapshot: Snapshot,
35
+ snapshotRevision: number,
36
+ |};
37
+
38
+ class RelayStoreSubscriptionsUsingMapByID implements StoreSubscriptions {
39
+ _notifiedRevision: number;
40
+ _snapshotRevision: number;
41
+ _subscriptionsByDataId: Map<DataID, Set<Subscription>>;
42
+ _staleSubscriptions: Set<Subscription>;
43
+
44
+ constructor() {
45
+ this._notifiedRevision = 0;
46
+ this._snapshotRevision = 0;
47
+ this._subscriptionsByDataId = new Map();
48
+ this._staleSubscriptions = new Set();
49
+ }
50
+
51
+ subscribe(
52
+ snapshot: Snapshot,
53
+ callback: (snapshot: Snapshot) => void,
54
+ ): Disposable {
55
+ const subscription = {
56
+ backup: null,
57
+ callback,
58
+ notifiedRevision: this._notifiedRevision,
59
+ snapshotRevision: this._snapshotRevision,
60
+ snapshot,
61
+ };
62
+ const dispose = () => {
63
+ for (const dataId in snapshot.seenRecords) {
64
+ const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
65
+ if (subscriptionsForDataId != null) {
66
+ subscriptionsForDataId.delete(subscription);
67
+ if (subscriptionsForDataId.size === 0) {
68
+ this._subscriptionsByDataId.delete(dataId);
69
+ }
70
+ }
71
+ }
72
+ };
73
+
74
+ for (const dataId in snapshot.seenRecords) {
75
+ const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
76
+ if (subscriptionsForDataId != null) {
77
+ subscriptionsForDataId.add(subscription);
78
+ } else {
79
+ this._subscriptionsByDataId.set(dataId, new Set([subscription]));
80
+ }
81
+ }
82
+
83
+ return {dispose};
84
+ }
85
+
86
+ snapshotSubscriptions(source: RecordSource) {
87
+ this._snapshotRevision++;
88
+ this._subscriptionsByDataId.forEach(subscriptions => {
89
+ subscriptions.forEach(subscription => {
90
+ if (subscription.snapshotRevision === this._snapshotRevision) {
91
+ return;
92
+ }
93
+ subscription.snapshotRevision = this._snapshotRevision;
94
+
95
+ // Backup occurs after writing a new "final" payload(s) and before (re)applying
96
+ // optimistic changes. Each subscription's `snapshot` represents what was *last
97
+ // published to the subscriber*, which notably may include previous optimistic
98
+ // updates. Therefore a subscription can be in any of the following states:
99
+ // - stale=true: This subscription was restored to a different value than
100
+ // `snapshot`. That means this subscription has changes relative to its base,
101
+ // but its base has changed (we just applied a final payload): recompute
102
+ // a backup so that we can later restore to the state the subscription
103
+ // should be in.
104
+ // - stale=false: This subscription was restored to the same value than
105
+ // `snapshot`. That means this subscription does *not* have changes relative
106
+ // to its base, so the current `snapshot` is valid to use as a backup.
107
+ if (!this._staleSubscriptions.has(subscription)) {
108
+ subscription.backup = subscription.snapshot;
109
+ return;
110
+ }
111
+ const snapshot = subscription.snapshot;
112
+ const backup = RelayReader.read(source, snapshot.selector);
113
+ const nextData = recycleNodesInto(snapshot.data, backup.data);
114
+ (backup: $FlowFixMe).data = nextData; // backup owns the snapshot and can safely mutate
115
+ subscription.backup = backup;
116
+ });
117
+ });
118
+ }
119
+
120
+ restoreSubscriptions() {
121
+ this._snapshotRevision++;
122
+ this._subscriptionsByDataId.forEach(subscriptions => {
123
+ subscriptions.forEach(subscription => {
124
+ if (subscription.snapshotRevision === this._snapshotRevision) {
125
+ return;
126
+ }
127
+ subscription.snapshotRevision = this._snapshotRevision;
128
+
129
+ const backup = subscription.backup;
130
+ subscription.backup = null;
131
+ if (backup) {
132
+ if (backup.data !== subscription.snapshot.data) {
133
+ this._staleSubscriptions.add(subscription);
134
+ }
135
+ const prevSeenRecords = subscription.snapshot.seenRecords;
136
+ subscription.snapshot = {
137
+ data: subscription.snapshot.data,
138
+ isMissingData: backup.isMissingData,
139
+ seenRecords: backup.seenRecords,
140
+ selector: backup.selector,
141
+ missingRequiredFields: backup.missingRequiredFields,
142
+ };
143
+ this._updateSubscriptionsMap(subscription, prevSeenRecords);
144
+ } else {
145
+ this._staleSubscriptions.add(subscription);
146
+ }
147
+ });
148
+ });
149
+ }
150
+
151
+ updateSubscriptions(
152
+ source: RecordSource,
153
+ updatedRecordIDs: UpdatedRecords,
154
+ updatedOwners: Array<RequestDescriptor>,
155
+ ) {
156
+ this._notifiedRevision++;
157
+ Object.keys(updatedRecordIDs).forEach(updatedRecordId => {
158
+ const subcriptionsForDataId = this._subscriptionsByDataId.get(
159
+ updatedRecordId,
160
+ );
161
+ if (subcriptionsForDataId == null) {
162
+ return;
163
+ }
164
+ subcriptionsForDataId.forEach(subscription => {
165
+ if (subscription.notifiedRevision === this._notifiedRevision) {
166
+ return;
167
+ }
168
+ const owner = this._updateSubscription(source, subscription, false);
169
+ if (owner != null) {
170
+ updatedOwners.push(owner);
171
+ }
172
+ });
173
+ });
174
+ this._staleSubscriptions.forEach(subscription => {
175
+ if (subscription.notifiedRevision === this._notifiedRevision) {
176
+ return;
177
+ }
178
+ const owner = this._updateSubscription(source, subscription, true);
179
+ if (owner != null) {
180
+ updatedOwners.push(owner);
181
+ }
182
+ });
183
+ this._staleSubscriptions.clear();
184
+ }
185
+
186
+ /**
187
+ * Notifies the callback for the subscription if the data for the associated
188
+ * snapshot has changed.
189
+ * Additionally, updates the subscription snapshot with the latest snapshot,
190
+ * amarks it as not stale, and updates the subscription tracking for any
191
+ * any new ids observed in the latest data snapshot.
192
+ * Returns the owner (RequestDescriptor) if the subscription was affected by the
193
+ * latest update, or null if it was not affected.
194
+ */
195
+ _updateSubscription(
196
+ source: RecordSource,
197
+ subscription: Subscription,
198
+ stale: boolean,
199
+ ): ?RequestDescriptor {
200
+ const {backup, callback, snapshot} = subscription;
201
+ let nextSnapshot: Snapshot =
202
+ stale && backup != null
203
+ ? backup
204
+ : RelayReader.read(source, snapshot.selector);
205
+ const nextData = recycleNodesInto(snapshot.data, nextSnapshot.data);
206
+ nextSnapshot = ({
207
+ data: nextData,
208
+ isMissingData: nextSnapshot.isMissingData,
209
+ seenRecords: nextSnapshot.seenRecords,
210
+ selector: nextSnapshot.selector,
211
+ missingRequiredFields: nextSnapshot.missingRequiredFields,
212
+ }: Snapshot);
213
+ if (__DEV__) {
214
+ deepFreeze(nextSnapshot);
215
+ }
216
+
217
+ const prevSeenRecords = subscription.snapshot.seenRecords;
218
+ subscription.snapshot = nextSnapshot;
219
+ subscription.notifiedRevision = this._notifiedRevision;
220
+ this._updateSubscriptionsMap(subscription, prevSeenRecords);
221
+
222
+ if (nextSnapshot.data !== snapshot.data) {
223
+ callback(nextSnapshot);
224
+ return snapshot.selector.owner;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Updates the Map that tracks subscriptions by id.
230
+ * Given an updated subscription and the records that where seen
231
+ * on the previous subscription snapshot, updates our tracking
232
+ * to track the subscription for the newly and no longer seen ids.
233
+ */
234
+ _updateSubscriptionsMap(
235
+ subscription: Subscription,
236
+ prevSeenRecords: RecordMap,
237
+ ) {
238
+ for (const dataId in prevSeenRecords) {
239
+ const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
240
+ if (subscriptionsForDataId != null) {
241
+ subscriptionsForDataId.delete(subscription);
242
+ if (subscriptionsForDataId.size === 0) {
243
+ this._subscriptionsByDataId.delete(dataId);
244
+ }
245
+ }
246
+ }
247
+
248
+ for (const dataId in subscription.snapshot.seenRecords) {
249
+ const subscriptionsForDataId = this._subscriptionsByDataId.get(dataId);
250
+ if (subscriptionsForDataId != null) {
251
+ subscriptionsForDataId.add(subscription);
252
+ } else {
253
+ this._subscriptionsByDataId.set(dataId, new Set([subscription]));
254
+ }
255
+ }
256
+ }
257
+ }
258
+
259
+ module.exports = RelayStoreSubscriptionsUsingMapByID;
@@ -17,14 +17,15 @@ import type {
17
17
  INetwork,
18
18
  PayloadData,
19
19
  PayloadError,
20
+ ReactFlightServerTree,
20
21
  UploadableMap,
21
22
  } from '../network/RelayNetworkTypes';
22
23
  import type RelayObservable from '../network/RelayObservable';
23
24
  import type {
24
25
  NormalizationLinkedField,
26
+ NormalizationRootNode,
25
27
  NormalizationScalarField,
26
28
  NormalizationSelectableNode,
27
- NormalizationSplitOperation,
28
29
  } from '../util/NormalizationNode';
29
30
  import type {ReaderFragment} from '../util/ReaderNode';
30
31
  import type {
@@ -83,6 +84,7 @@ export type RequestDescriptor = {|
83
84
  +identifier: RequestIdentifier,
84
85
  +node: ConcreteRequest,
85
86
  +variables: Variables,
87
+ +cacheConfig: ?CacheConfig,
86
88
  |};
87
89
 
88
90
  /**
@@ -95,16 +97,25 @@ export type NormalizationSelector = {|
95
97
  +variables: Variables,
96
98
  |};
97
99
 
100
+ type MissingRequiredField = {|
101
+ path: string,
102
+ owner: string,
103
+ |};
104
+
105
+ export type MissingRequiredFields =
106
+ | {|action: 'THROW', field: MissingRequiredField|}
107
+ | {|action: 'LOG', fields: Array<MissingRequiredField>|};
108
+
98
109
  /**
99
110
  * A representation of a selector and its results at a particular point in time.
100
111
  */
101
- export type TypedSnapshot<TData> = {|
102
- +data: TData,
112
+ export type Snapshot = {|
113
+ +data: ?SelectorData,
103
114
  +isMissingData: boolean,
104
115
  +seenRecords: RecordMap,
105
116
  +selector: SingularReaderSelector,
117
+ +missingRequiredFields: ?MissingRequiredFields,
106
118
  |};
107
- export type Snapshot = TypedSnapshot<?SelectorData>;
108
119
 
109
120
  /**
110
121
  * An operation selector describes a specific instance of a GraphQL operation
@@ -236,8 +247,6 @@ export interface Store {
236
247
 
237
248
  /**
238
249
  * Read the results of a selector from in-memory records in the store.
239
- * Optionally takes an owner, corresponding to the operation that
240
- * owns this selector (fragment).
241
250
  */
242
251
  lookup(selector: SingularReaderSelector): Snapshot;
243
252
 
@@ -247,7 +256,7 @@ export interface Store {
247
256
  * Optionally provide an OperationDescriptor indicating the source operation
248
257
  * that was being processed to produce this run.
249
258
  *
250
- * This method should return an array of the affected fragment owners
259
+ * This method should return an array of the affected fragment owners.
251
260
  */
252
261
  notify(
253
262
  sourceOperation?: OperationDescriptor,
@@ -263,7 +272,7 @@ export interface Store {
263
272
 
264
273
  /**
265
274
  * Ensure that all the records necessary to fulfill the given selector are
266
- * retained in-memory. The records will not be eligible for garbage collection
275
+ * retained in memory. The records will not be eligible for garbage collection
267
276
  * until the returned reference is disposed.
268
277
  */
269
278
  retain(operation: OperationDescriptor): Disposable;
@@ -324,6 +333,40 @@ export interface Store {
324
333
  ): Disposable;
325
334
  }
326
335
 
336
+ export interface StoreSubscriptions {
337
+ /**
338
+ * Subscribe to changes to the results of a selector. The callback is called
339
+ * when `updateSubscriptions()` is called *and* records have been published that affect the
340
+ * selector results relative to the last update.
341
+ */
342
+ subscribe(
343
+ snapshot: Snapshot,
344
+ callback: (snapshot: Snapshot) => void,
345
+ ): Disposable;
346
+
347
+ /**
348
+ * Record a backup/snapshot of the current state of the subscriptions.
349
+ * This state can be restored with restore().
350
+ */
351
+ snapshotSubscriptions(source: RecordSource): void;
352
+
353
+ /**
354
+ * Reset the state of the subscriptions to the point that snapshot() was last called.
355
+ */
356
+ restoreSubscriptions(): void;
357
+
358
+ /**
359
+ * Notifies each subscription if the snapshot for the subscription selector has changed.
360
+ * Mutates the updatedOwners array with any owners (RequestDescriptors) associated
361
+ * with the subscriptions that were notifed; i.e. the owners affected by the changes.
362
+ */
363
+ updateSubscriptions(
364
+ source: RecordSource,
365
+ updatedRecordIDs: UpdatedRecords,
366
+ updatedOwners: Array<RequestDescriptor>,
367
+ ): void;
368
+ }
369
+
327
370
  /**
328
371
  * A type that accepts a callback and schedules it to run at some future time.
329
372
  * By convention, implementations should not execute the callback immediately.
@@ -422,32 +465,32 @@ export type LogEvent =
422
465
  +profilerContext: mixed,
423
466
  |}
424
467
  | {|
425
- +name: 'execute.info',
468
+ +name: 'network.info',
426
469
  +transactionID: number,
427
470
  +info: mixed,
428
471
  |}
429
472
  | {|
430
- +name: 'execute.start',
473
+ +name: 'network.start',
431
474
  +transactionID: number,
432
475
  +params: RequestParameters,
433
476
  +variables: Variables,
434
477
  |}
435
478
  | {|
436
- +name: 'execute.next',
479
+ +name: 'network.next',
437
480
  +transactionID: number,
438
481
  +response: GraphQLResponse,
439
482
  |}
440
483
  | {|
441
- +name: 'execute.error',
484
+ +name: 'network.error',
442
485
  +transactionID: number,
443
486
  +error: Error,
444
487
  |}
445
488
  | {|
446
- +name: 'execute.complete',
489
+ +name: 'network.complete',
447
490
  +transactionID: number,
448
491
  |}
449
492
  | {|
450
- +name: 'execute.unsubscribe',
493
+ +name: 'network.unsubscribe',
451
494
  +transactionID: number,
452
495
  |}
453
496
  | {|
@@ -472,7 +515,13 @@ export type LogEvent =
472
515
  +name: 'store.notify.complete',
473
516
  +updatedRecordIDs: UpdatedRecords,
474
517
  +invalidatedRecordIDs: Set<DataID>,
518
+ |}
519
+ | {|
520
+ +name: 'entrypoint.root.consume',
521
+ +profilerContext: mixed,
522
+ +rootModuleID: string,
475
523
  |};
524
+
476
525
  export type LogFunction = LogEvent => void;
477
526
  export type LogRequestInfoFunction = mixed => void;
478
527
 
@@ -563,14 +612,12 @@ export interface IEnvironment {
563
612
  /**
564
613
  * EXPERIMENTAL
565
614
  * Returns the default render policy to use when rendering a query
566
- * that uses Relay Hooks
615
+ * that uses Relay Hooks.
567
616
  */
568
617
  UNSTABLE_getDefaultRenderPolicy(): RenderPolicy;
569
618
 
570
619
  /**
571
620
  * Read the results of a selector from in-memory records in the store.
572
- * Optionally takes an owner, corresponding to the operation that
573
- * owns this selector (fragment).
574
621
  */
575
622
  lookup(selector: SingularReaderSelector): Snapshot;
576
623
 
@@ -587,7 +634,6 @@ export interface IEnvironment {
587
634
  */
588
635
  execute(config: {|
589
636
  operation: OperationDescriptor,
590
- cacheConfig?: ?CacheConfig,
591
637
  updater?: ?SelectorStoreUpdater,
592
638
  |}): RelayObservable<GraphQLResponse>;
593
639
 
@@ -602,7 +648,6 @@ export interface IEnvironment {
602
648
  * environment.executeMutation({...}).subscribe({...}).
603
649
  */
604
650
  executeMutation({|
605
- cacheConfig?: ?CacheConfig,
606
651
  operation: OperationDescriptor,
607
652
  optimisticUpdater?: ?SelectorStoreUpdater,
608
653
  optimisticResponse?: ?Object,
@@ -639,18 +684,13 @@ export interface IEnvironment {
639
684
  * whether we need to set up certain caches and timeout's.
640
685
  */
641
686
  isServer(): boolean;
642
- }
643
687
 
644
- /**
645
- * The results of reading data for a fragment. This is similar to a `Selector`,
646
- * but references the (fragment) node by name rather than by value.
647
- */
648
- export type FragmentPointer = {
649
- __id: DataID,
650
- __fragments: {[fragmentName: string]: Variables, ...},
651
- __fragmentOwner: RequestDescriptor,
652
- ...
653
- };
688
+ /**
689
+ * Called by Relay when it encounters a missing field that has been annotated
690
+ * with `@required(action: LOG)`.
691
+ */
692
+ requiredFieldLogger: RequiredFieldLogger;
693
+ }
654
694
 
655
695
  /**
656
696
  * The partial shape of an object with a '...Fragment @module(name: "...")'
@@ -699,7 +739,9 @@ export type HandleFieldPayload = {|
699
739
 
700
740
  /**
701
741
  * A payload that represents data necessary to process the results of an object
702
- * with a `@module` fragment spread:
742
+ * with a `@module` fragment spread, or a Flight field's:
743
+ *
744
+ * ## @module Fragment Spread
703
745
  * - data: The GraphQL response value for the @match field.
704
746
  * - dataID: The ID of the store object linked to by the @match field.
705
747
  * - operationReference: A reference to a generated module containing the
@@ -710,6 +752,21 @@ export type HandleFieldPayload = {|
710
752
  * The dataID, variables, and fragmentName can be used to create a Selector
711
753
  * which can in turn be used to normalize and publish the data. The dataID and
712
754
  * typeName can also be used to construct a root record for normalization.
755
+ *
756
+ * ## Flight fields
757
+ * In Flight, data for additional components rendered by the requested server
758
+ * component are included in the response returned by a Flight compliant server.
759
+ *
760
+ * - data: Data used by additional components rendered by the server component
761
+ * being requested.
762
+ * - dataID: For Flight fields, this should always be ROOT_ID. This is because
763
+ * the query data isn't relative to the parent record–it's root data.
764
+ * - operationReference: The query's module that will be later used by an
765
+ * operation loader.
766
+ * - variables: The query's variables.
767
+ * - typeName: For Flight fields, this should always be ROOT_TYPE. This is
768
+ * because the query data isn't relative to the parent record–it's
769
+ * root data.
713
770
  */
714
771
  export type ModuleImportPayload = {|
715
772
  +data: PayloadData,
@@ -744,22 +801,22 @@ export type StreamPlaceholder = {|
744
801
  export type IncrementalDataPlaceholder = DeferPlaceholder | StreamPlaceholder;
745
802
 
746
803
  /**
747
- * A user-supplied object to load a generated operation (SplitOperation) AST
748
- * by a module reference. The exact format of a module reference is left to
749
- * the application, but it must be a plain JavaScript value (string, number,
750
- * or object/array of same).
804
+ * A user-supplied object to load a generated operation (SplitOperation or
805
+ * ConcreteRequest) AST by a module reference. The exact format of a module
806
+ * reference is left to the application, but it must be a plain JavaScript value
807
+ * (string, number, or object/array of same).
751
808
  */
752
809
  export type OperationLoader = {|
753
810
  /**
754
811
  * Synchronously load an operation, returning either the node or null if it
755
812
  * cannot be resolved synchronously.
756
813
  */
757
- get(reference: mixed): ?NormalizationSplitOperation,
814
+ get(reference: mixed): ?NormalizationRootNode,
758
815
 
759
816
  /**
760
817
  * Asynchronously load an operation.
761
818
  */
762
- load(reference: mixed): Promise<?NormalizationSplitOperation>,
819
+ load(reference: mixed): Promise<?NormalizationRootNode>,
763
820
  |};
764
821
 
765
822
  /**
@@ -837,6 +894,23 @@ export type MissingFieldHandler =
837
894
  ) => ?Array<?DataID>,
838
895
  |};
839
896
 
897
+ /**
898
+ * A handler for events related to @required fields. Currently reports missing
899
+ * fields with either `action: LOG` or `action: THROW`.
900
+ */
901
+ export type RequiredFieldLogger = (
902
+ | {|
903
+ +kind: 'missing_field.log',
904
+ +owner: string,
905
+ +fieldPath: string,
906
+ |}
907
+ | {|
908
+ +kind: 'missing_field.throw',
909
+ +owner: string,
910
+ +fieldPath: string,
911
+ |},
912
+ ) => void;
913
+
840
914
  /**
841
915
  * The results of normalizing a query.
842
916
  */
@@ -897,3 +971,22 @@ export interface PublishQueue {
897
971
  */
898
972
  run(sourceOperation?: OperationDescriptor): $ReadOnlyArray<RequestDescriptor>;
899
973
  }
974
+
975
+ /**
976
+ * ReactFlightDOMRelayClient processes a ReactFlightServerTree into a
977
+ * ReactFlightClientResponse object. readRoot() can suspend.
978
+ */
979
+ export type ReactFlightClientResponse = {readRoot: () => mixed, ...};
980
+
981
+ export type ReactFlightReachableQuery = {|
982
+ +module: mixed,
983
+ +variables: Variables,
984
+ |};
985
+
986
+ /**
987
+ * A user-supplied function that takes a ReactFlightServerTree, and deserializes
988
+ * it into a ReactFlightClientResponse object.
989
+ */
990
+ export type ReactFlightPayloadDeserializer = (
991
+ tree: ReactFlightServerTree,
992
+ ) => ReactFlightClientResponse;
@@ -31,6 +31,9 @@ let firstReact: ?React;
31
31
  function createRelayContext(react: React): React$Context<RelayContext | null> {
32
32
  if (!relayContext) {
33
33
  relayContext = react.createContext(null);
34
+ if (__DEV__) {
35
+ relayContext.displayName = 'RelayContext';
36
+ }
34
37
  firstReact = react;
35
38
  }
36
39
  invariant(
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ import type {RequiredFieldLogger} from './RelayStoreTypes';
14
+
15
+ const defaultRequiredFieldLogger: RequiredFieldLogger = event => {
16
+ if (__DEV__ && event.kind === 'missing_field.log') {
17
+ throw new Error(
18
+ 'Relay Environment Configuration Error (dev only): `@required(action: LOG)` requires that the Relay Environment be configured with a `requiredFieldLogger`.',
19
+ );
20
+ }
21
+ };
22
+
23
+ module.exports = defaultRequiredFieldLogger;
@@ -60,7 +60,11 @@ function requestSubscription<TSubscriptionPayload>(
60
60
  variables,
61
61
  cacheConfig,
62
62
  } = config;
63
- const operation = createOperationDescriptor(subscription, variables);
63
+ const operation = createOperationDescriptor(
64
+ subscription,
65
+ variables,
66
+ cacheConfig,
67
+ );
64
68
 
65
69
  warning(
66
70
  !(config.updater && configs),
@@ -80,7 +84,6 @@ function requestSubscription<TSubscriptionPayload>(
80
84
  .execute({
81
85
  operation,
82
86
  updater,
83
- cacheConfig,
84
87
  })
85
88
  .map(() => {
86
89
  const data = environment.lookup(operation.fragment).data;