relay-runtime 10.0.1 → 10.1.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 (70) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +6 -0
  2. package/handlers/connection/MutationHandlers.js.flow +114 -3
  3. package/index.js +1 -1
  4. package/index.js.flow +16 -1
  5. package/lib/handlers/RelayDefaultHandlerProvider.js +9 -0
  6. package/lib/handlers/connection/MutationHandlers.js +138 -12
  7. package/lib/index.js +7 -0
  8. package/lib/mutations/RelayDeclarativeMutationConfig.js +2 -2
  9. package/lib/mutations/commitMutation.js +1 -4
  10. package/lib/mutations/validateMutation.js +27 -7
  11. package/lib/network/RelayQueryResponseCache.js +2 -2
  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 +82 -5
  16. package/lib/store/RelayModernEnvironment.js +18 -6
  17. package/lib/store/RelayModernFragmentSpecResolver.js +10 -1
  18. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  19. package/lib/store/RelayModernQueryExecutor.js +44 -23
  20. package/lib/store/RelayModernStore.js +25 -14
  21. package/lib/store/RelayOperationTracker.js +2 -2
  22. package/lib/store/RelayPublishQueue.js +1 -1
  23. package/lib/store/RelayReader.js +196 -33
  24. package/lib/store/RelayRecordSourceMapImpl.js +2 -2
  25. package/lib/store/RelayReferenceMarker.js +89 -5
  26. package/lib/store/RelayResponseNormalizer.js +119 -19
  27. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  28. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  29. package/lib/store/normalizeRelayPayload.js +1 -1
  30. package/lib/subscription/requestSubscription.js +2 -3
  31. package/lib/util/NormalizationNode.js +1 -5
  32. package/lib/util/RelayConcreteNode.js +2 -0
  33. package/lib/util/RelayFeatureFlags.js +5 -2
  34. package/lib/util/getFragmentIdentifier.js +12 -3
  35. package/lib/util/getOperation.js +33 -0
  36. package/lib/util/isEmptyObject.js +25 -0
  37. package/lib/util/recycleNodesInto.js +4 -1
  38. package/lib/util/reportMissingRequiredFields.js +48 -0
  39. package/mutations/commitMutation.js.flow +1 -2
  40. package/mutations/validateMutation.js.flow +34 -5
  41. package/network/RelayNetworkTypes.js.flow +22 -0
  42. package/package.json +2 -2
  43. package/query/GraphQLTag.js.flow +3 -1
  44. package/query/fetchQuery.js.flow +2 -2
  45. package/query/fetchQueryInternal.js.flow +0 -5
  46. package/relay-runtime.js +2 -2
  47. package/relay-runtime.min.js +2 -2
  48. package/store/DataChecker.js.flow +68 -2
  49. package/store/RelayModernEnvironment.js.flow +29 -9
  50. package/store/RelayModernFragmentSpecResolver.js.flow +13 -1
  51. package/store/RelayModernOperationDescriptor.js.flow +5 -1
  52. package/store/RelayModernQueryExecutor.js.flow +47 -23
  53. package/store/RelayModernStore.js.flow +31 -15
  54. package/store/RelayPublishQueue.js.flow +1 -1
  55. package/store/RelayReader.js.flow +180 -15
  56. package/store/RelayReferenceMarker.js.flow +72 -5
  57. package/store/RelayResponseNormalizer.js.flow +130 -19
  58. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  59. package/store/RelayStoreTypes.js.flow +90 -31
  60. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  61. package/subscription/requestSubscription.js.flow +5 -2
  62. package/util/NormalizationNode.js.flow +17 -2
  63. package/util/ReaderNode.js.flow +20 -1
  64. package/util/RelayConcreteNode.js.flow +6 -0
  65. package/util/RelayFeatureFlags.js.flow +8 -1
  66. package/util/getFragmentIdentifier.js.flow +33 -9
  67. package/util/getOperation.js.flow +40 -0
  68. package/util/isEmptyObject.js.flow +25 -0
  69. package/util/recycleNodesInto.js.flow +11 -0
  70. package/util/reportMissingRequiredFields.js.flow +51 -0
@@ -16,6 +16,7 @@ const RelayFeatureFlags = require('../util/RelayFeatureFlags');
16
16
  const RelayModernRecord = require('./RelayModernRecord');
17
17
  const RelayProfiler = require('../util/RelayProfiler');
18
18
 
19
+ const areEqual = require('areEqual');
19
20
  const invariant = require('invariant');
20
21
  const warning = require('warning');
21
22
 
@@ -23,6 +24,7 @@ const {
23
24
  CONDITION,
24
25
  CLIENT_EXTENSION,
25
26
  DEFER,
27
+ FLIGHT_FIELD,
26
28
  INLINE_FRAGMENT,
27
29
  LINKED_FIELD,
28
30
  LINKED_HANDLE,
@@ -34,6 +36,12 @@ const {
34
36
  } = require('../util/RelayConcreteNode');
35
37
  const {generateClientID, isClientID} = require('./ClientID');
36
38
  const {createNormalizationSelector} = require('./RelayModernSelector');
39
+ const {
40
+ refineToReactFlightPayloadData,
41
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
42
+ REACT_FLIGHT_TREE_STORAGE_KEY,
43
+ REACT_FLIGHT_TYPE_NAME,
44
+ } = require('./RelayStoreReactFlightUtils');
37
45
  const {
38
46
  getArgumentValues,
39
47
  getHandleStorageKey,
@@ -42,12 +50,14 @@ const {
42
50
  getStorageKey,
43
51
  TYPENAME_KEY,
44
52
  ROOT_ID,
53
+ ROOT_TYPE,
45
54
  } = require('./RelayStoreUtils');
46
55
  const {generateTypeID, TYPE_SCHEMA_TYPE} = require('./TypeID');
47
56
 
48
57
  import type {PayloadData} from '../network/RelayNetworkTypes';
49
58
  import type {
50
59
  NormalizationDefer,
60
+ NormalizationFlightField,
51
61
  NormalizationLinkedField,
52
62
  NormalizationModuleImport,
53
63
  NormalizationNode,
@@ -61,6 +71,8 @@ import type {
61
71
  ModuleImportPayload,
62
72
  MutableRecordSource,
63
73
  NormalizationSelector,
74
+ ReactFlightReachableQuery,
75
+ ReactFlightPayloadDeserializer,
64
76
  Record,
65
77
  RelayResponsePayload,
66
78
  } from './RelayStoreTypes';
@@ -74,6 +86,7 @@ export type NormalizationOptions = {|
74
86
  +getDataID: GetDataID,
75
87
  +treatMissingFieldsAsNull: boolean,
76
88
  +path?: $ReadOnlyArray<string>,
89
+ +reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
77
90
  |};
78
91
 
79
92
  /**
@@ -111,6 +124,7 @@ class RelayResponseNormalizer {
111
124
  _path: Array<string>;
112
125
  _recordSource: MutableRecordSource;
113
126
  _variables: Variables;
127
+ _reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
114
128
 
115
129
  constructor(
116
130
  recordSource: MutableRecordSource,
@@ -127,6 +141,8 @@ class RelayResponseNormalizer {
127
141
  this._path = options.path ? [...options.path] : [];
128
142
  this._recordSource = recordSource;
129
143
  this._variables = variables;
144
+ this._reactFlightPayloadDeserializer =
145
+ options.reactFlightPayloadDeserializer;
130
146
  }
131
147
 
132
148
  normalizeResponse(
@@ -277,6 +293,13 @@ class RelayResponseNormalizer {
277
293
  this._traverseSelections(selection, record, data);
278
294
  this._isClientExtension = isClientExtension;
279
295
  break;
296
+ case FLIGHT_FIELD:
297
+ if (RelayFeatureFlags.ENABLE_REACT_FLIGHT_COMPONENT_FIELD) {
298
+ this._normalizeFlightField(node, selection, record, data);
299
+ } else {
300
+ throw new Error('Flight fields are not yet supported.');
301
+ }
302
+ break;
280
303
  default:
281
304
  (selection: empty);
282
305
  invariant(
@@ -441,23 +464,27 @@ class RelayResponseNormalizer {
441
464
  return;
442
465
  }
443
466
  }
444
- if (selection.kind === SCALAR_FIELD && __DEV__) {
445
- this._validateConflictingFieldsWithIdenticalId(
446
- record,
447
- storageKey,
448
- fieldValue,
449
- );
467
+ if (__DEV__) {
468
+ if (selection.kind === SCALAR_FIELD) {
469
+ this._validateConflictingFieldsWithIdenticalId(
470
+ record,
471
+ storageKey,
472
+ fieldValue,
473
+ );
474
+ }
450
475
  }
451
476
  RelayModernRecord.setValue(record, storageKey, null);
452
477
  return;
453
478
  }
454
479
 
455
480
  if (selection.kind === SCALAR_FIELD) {
456
- this._validateConflictingFieldsWithIdenticalId(
457
- record,
458
- storageKey,
459
- fieldValue,
460
- );
481
+ if (__DEV__) {
482
+ this._validateConflictingFieldsWithIdenticalId(
483
+ record,
484
+ storageKey,
485
+ fieldValue,
486
+ );
487
+ }
461
488
  RelayModernRecord.setValue(record, storageKey, fieldValue);
462
489
  } else if (selection.kind === LINKED_FIELD) {
463
490
  this._path.push(responseKey);
@@ -477,6 +504,84 @@ class RelayResponseNormalizer {
477
504
  }
478
505
  }
479
506
 
507
+ _normalizeFlightField(
508
+ parent: NormalizationNode,
509
+ selection: NormalizationFlightField,
510
+ record: Record,
511
+ data: PayloadData,
512
+ ) {
513
+ const responseKey = selection.alias || selection.name;
514
+ const storageKey = getStorageKey(selection, this._variables);
515
+ const fieldValue = data[responseKey];
516
+
517
+ if (fieldValue == null) {
518
+ RelayModernRecord.setValue(record, storageKey, null);
519
+ return;
520
+ }
521
+
522
+ const reactFlightPayload = refineToReactFlightPayloadData(fieldValue);
523
+
524
+ invariant(
525
+ reactFlightPayload != null,
526
+ 'RelayResponseNormalizer(): Expected React Flight payload data ' +
527
+ 'to be an object with `tree` and `queries` properties, got `%s`.',
528
+ fieldValue,
529
+ );
530
+ invariant(
531
+ typeof this._reactFlightPayloadDeserializer === 'function',
532
+ 'RelayResponseNormalizer: Expected reactFlightPayloadDeserializer to ' +
533
+ 'be a function, got `%s`.',
534
+ this._reactFlightPayloadDeserializer,
535
+ );
536
+
537
+ // We store the deserialized reactFlightClientResponse in a separate
538
+ // record and link it to the parent record. This is so we can GC the Flight
539
+ // tree later even if the parent record is still reachable.
540
+ const reactFlightClientResponse = this._reactFlightPayloadDeserializer(
541
+ reactFlightPayload.tree,
542
+ );
543
+ const reactFlightID = generateClientID(
544
+ RelayModernRecord.getDataID(record),
545
+ getStorageKey(selection, this._variables),
546
+ );
547
+ let reactFlightClientResponseRecord = this._recordSource.get(reactFlightID);
548
+ if (reactFlightClientResponseRecord == null) {
549
+ reactFlightClientResponseRecord = RelayModernRecord.create(
550
+ reactFlightID,
551
+ REACT_FLIGHT_TYPE_NAME,
552
+ );
553
+ this._recordSource.set(reactFlightID, reactFlightClientResponseRecord);
554
+ }
555
+ RelayModernRecord.setValue(
556
+ reactFlightClientResponseRecord,
557
+ REACT_FLIGHT_TREE_STORAGE_KEY,
558
+ reactFlightClientResponse,
559
+ );
560
+ const reachableQueries: Array<ReactFlightReachableQuery> = [];
561
+ for (const query of reactFlightPayload.queries) {
562
+ if (query.response.data != null) {
563
+ this._moduleImportPayloads.push({
564
+ data: query.response.data,
565
+ dataID: ROOT_ID,
566
+ operationReference: query.module,
567
+ path: [],
568
+ typeName: ROOT_TYPE,
569
+ variables: query.variables,
570
+ });
571
+ }
572
+ reachableQueries.push({
573
+ module: query.module,
574
+ variables: query.variables,
575
+ });
576
+ }
577
+ RelayModernRecord.setValue(
578
+ reactFlightClientResponseRecord,
579
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
580
+ reachableQueries,
581
+ );
582
+ RelayModernRecord.setLinkedRecordID(record, storageKey, reactFlightID);
583
+ }
584
+
480
585
  _normalizeLink(
481
586
  field: NormalizationLinkedField,
482
587
  record: Record,
@@ -582,13 +687,17 @@ class RelayResponseNormalizer {
582
687
  } else if (__DEV__) {
583
688
  this._validateRecordType(nextRecord, field, item);
584
689
  }
585
- if (prevIDs && __DEV__) {
586
- this._validateConflictingLinkedFieldsWithIdenticalId(
587
- record,
588
- prevIDs[nextIndex],
589
- nextID,
590
- storageKey,
591
- );
690
+ // NOTE: the check to strip __DEV__ code only works for simple
691
+ // `if (__DEV__)`
692
+ if (__DEV__) {
693
+ if (prevIDs) {
694
+ this._validateConflictingLinkedFieldsWithIdenticalId(
695
+ record,
696
+ prevIDs[nextIndex],
697
+ nextID,
698
+ storageKey,
699
+ );
700
+ }
592
701
  }
593
702
  // $FlowFixMe[incompatible-variance]
594
703
  this._traverseSelections(field, nextRecord, item);
@@ -629,13 +738,14 @@ class RelayResponseNormalizer {
629
738
  storageKey: string,
630
739
  fieldValue: mixed,
631
740
  ): void {
741
+ // NOTE: Only call this function in DEV
632
742
  if (__DEV__) {
633
743
  const dataID = RelayModernRecord.getDataID(record);
634
744
  var previousValue = RelayModernRecord.getValue(record, storageKey);
635
745
  warning(
636
746
  storageKey === TYPENAME_KEY ||
637
747
  previousValue === undefined ||
638
- previousValue === fieldValue,
748
+ areEqual(previousValue, fieldValue),
639
749
  'RelayResponseNormalizer: Invalid record. The record contains two ' +
640
750
  'instances of the same id: `%s` with conflicting field, %s and its values: %s and %s. ' +
641
751
  'If two fields are different but share ' +
@@ -657,6 +767,7 @@ class RelayResponseNormalizer {
657
767
  nextID: DataID,
658
768
  storageKey: string,
659
769
  ): void {
770
+ // NOTE: Only call this function in DEV
660
771
  if (__DEV__) {
661
772
  warning(
662
773
  prevID === undefined || prevID === nextID,
@@ -0,0 +1,64 @@
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
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const invariant = require('invariant');
16
+
17
+ const {getType} = require('./RelayModernRecord');
18
+
19
+ import type {ReactFlightPayloadData} from '../network/RelayNetworkTypes';
20
+ import type {ReactFlightClientResponse, Record} from './RelayStoreTypes';
21
+
22
+ const REACT_FLIGHT_QUERIES_STORAGE_KEY = 'queries';
23
+ const REACT_FLIGHT_TREE_STORAGE_KEY = 'tree';
24
+ const REACT_FLIGHT_TYPE_NAME = 'ReactFlightComponent';
25
+
26
+ function refineToReactFlightPayloadData(
27
+ payload: mixed,
28
+ ): ?ReactFlightPayloadData {
29
+ if (
30
+ payload == null ||
31
+ typeof payload !== 'object' ||
32
+ !Array.isArray(payload.tree) ||
33
+ !Array.isArray(payload.queries)
34
+ ) {
35
+ return null;
36
+ }
37
+ return (payload: $FlowFixMe);
38
+ }
39
+
40
+ function getReactFlightClientResponse(
41
+ record: Record,
42
+ ): ?ReactFlightClientResponse {
43
+ invariant(
44
+ getType(record) === REACT_FLIGHT_TYPE_NAME,
45
+ 'getReactFlightClientResponse(): Expected a ReactFlightComponentRecord, ' +
46
+ 'got %s.',
47
+ record,
48
+ );
49
+ const response: ?ReactFlightClientResponse = (record[
50
+ REACT_FLIGHT_TREE_STORAGE_KEY
51
+ ]: $FlowFixMe);
52
+ if (response != null) {
53
+ return response;
54
+ }
55
+ return null;
56
+ }
57
+
58
+ module.exports = {
59
+ REACT_FLIGHT_QUERIES_STORAGE_KEY,
60
+ REACT_FLIGHT_TREE_STORAGE_KEY,
61
+ REACT_FLIGHT_TYPE_NAME,
62
+ getReactFlightClientResponse,
63
+ refineToReactFlightPayloadData,
64
+ };
@@ -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;
@@ -472,7 +481,13 @@ export type LogEvent =
472
481
  +name: 'store.notify.complete',
473
482
  +updatedRecordIDs: UpdatedRecords,
474
483
  +invalidatedRecordIDs: Set<DataID>,
484
+ |}
485
+ | {|
486
+ +name: 'entrypoint.root.consume',
487
+ +profilerContext: mixed,
488
+ +rootModuleID: string,
475
489
  |};
490
+
476
491
  export type LogFunction = LogEvent => void;
477
492
  export type LogRequestInfoFunction = mixed => void;
478
493
 
@@ -563,14 +578,12 @@ export interface IEnvironment {
563
578
  /**
564
579
  * EXPERIMENTAL
565
580
  * Returns the default render policy to use when rendering a query
566
- * that uses Relay Hooks
581
+ * that uses Relay Hooks.
567
582
  */
568
583
  UNSTABLE_getDefaultRenderPolicy(): RenderPolicy;
569
584
 
570
585
  /**
571
586
  * 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
587
  */
575
588
  lookup(selector: SingularReaderSelector): Snapshot;
576
589
 
@@ -587,7 +600,6 @@ export interface IEnvironment {
587
600
  */
588
601
  execute(config: {|
589
602
  operation: OperationDescriptor,
590
- cacheConfig?: ?CacheConfig,
591
603
  updater?: ?SelectorStoreUpdater,
592
604
  |}): RelayObservable<GraphQLResponse>;
593
605
 
@@ -602,7 +614,6 @@ export interface IEnvironment {
602
614
  * environment.executeMutation({...}).subscribe({...}).
603
615
  */
604
616
  executeMutation({|
605
- cacheConfig?: ?CacheConfig,
606
617
  operation: OperationDescriptor,
607
618
  optimisticUpdater?: ?SelectorStoreUpdater,
608
619
  optimisticResponse?: ?Object,
@@ -639,18 +650,13 @@ export interface IEnvironment {
639
650
  * whether we need to set up certain caches and timeout's.
640
651
  */
641
652
  isServer(): boolean;
642
- }
643
653
 
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
- };
654
+ /**
655
+ * Called by Relay when it encounters a missing field that has been annotated
656
+ * with `@required(action: LOG)`.
657
+ */
658
+ requiredFieldLogger: RequiredFieldLogger;
659
+ }
654
660
 
655
661
  /**
656
662
  * The partial shape of an object with a '...Fragment @module(name: "...")'
@@ -699,7 +705,9 @@ export type HandleFieldPayload = {|
699
705
 
700
706
  /**
701
707
  * A payload that represents data necessary to process the results of an object
702
- * with a `@module` fragment spread:
708
+ * with a `@module` fragment spread, or a Flight field's:
709
+ *
710
+ * ## @module Fragment Spread
703
711
  * - data: The GraphQL response value for the @match field.
704
712
  * - dataID: The ID of the store object linked to by the @match field.
705
713
  * - operationReference: A reference to a generated module containing the
@@ -710,6 +718,21 @@ export type HandleFieldPayload = {|
710
718
  * The dataID, variables, and fragmentName can be used to create a Selector
711
719
  * which can in turn be used to normalize and publish the data. The dataID and
712
720
  * typeName can also be used to construct a root record for normalization.
721
+ *
722
+ * ## Flight fields
723
+ * In Flight, data for additional components rendered by the requested server
724
+ * component are included in the response returned by a Flight compliant server.
725
+ *
726
+ * - data: Data used by additional components rendered by the server component
727
+ * being requested.
728
+ * - dataID: For Flight fields, this should always be ROOT_ID. This is because
729
+ * the query data isn't relative to the parent record–it's root data.
730
+ * - operationReference: The query's module that will be later used by an
731
+ * operation loader.
732
+ * - variables: The query's variables.
733
+ * - typeName: For Flight fields, this should always be ROOT_TYPE. This is
734
+ * because the query data isn't relative to the parent record–it's
735
+ * root data.
713
736
  */
714
737
  export type ModuleImportPayload = {|
715
738
  +data: PayloadData,
@@ -744,22 +767,22 @@ export type StreamPlaceholder = {|
744
767
  export type IncrementalDataPlaceholder = DeferPlaceholder | StreamPlaceholder;
745
768
 
746
769
  /**
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).
770
+ * A user-supplied object to load a generated operation (SplitOperation or
771
+ * ConcreteRequest) AST by a module reference. The exact format of a module
772
+ * reference is left to the application, but it must be a plain JavaScript value
773
+ * (string, number, or object/array of same).
751
774
  */
752
775
  export type OperationLoader = {|
753
776
  /**
754
777
  * Synchronously load an operation, returning either the node or null if it
755
778
  * cannot be resolved synchronously.
756
779
  */
757
- get(reference: mixed): ?NormalizationSplitOperation,
780
+ get(reference: mixed): ?NormalizationRootNode,
758
781
 
759
782
  /**
760
783
  * Asynchronously load an operation.
761
784
  */
762
- load(reference: mixed): Promise<?NormalizationSplitOperation>,
785
+ load(reference: mixed): Promise<?NormalizationRootNode>,
763
786
  |};
764
787
 
765
788
  /**
@@ -837,6 +860,23 @@ export type MissingFieldHandler =
837
860
  ) => ?Array<?DataID>,
838
861
  |};
839
862
 
863
+ /**
864
+ * A handler for events related to @required fields. Currently reports missing
865
+ * fields with either `action: LOG` or `action: THROW`.
866
+ */
867
+ export type RequiredFieldLogger = (
868
+ | {|
869
+ +kind: 'missing_field.log',
870
+ +owner: string,
871
+ +fieldPath: string,
872
+ |}
873
+ | {|
874
+ +kind: 'missing_field.throw',
875
+ +owner: string,
876
+ +fieldPath: string,
877
+ |},
878
+ ) => void;
879
+
840
880
  /**
841
881
  * The results of normalizing a query.
842
882
  */
@@ -897,3 +937,22 @@ export interface PublishQueue {
897
937
  */
898
938
  run(sourceOperation?: OperationDescriptor): $ReadOnlyArray<RequestDescriptor>;
899
939
  }
940
+
941
+ /**
942
+ * ReactFlightDOMRelayClient processes a ReactFlightServerTree into a
943
+ * ReactFlightClientResponse object. readRoot() can suspend.
944
+ */
945
+ export type ReactFlightClientResponse = {readRoot: () => mixed, ...};
946
+
947
+ export type ReactFlightReachableQuery = {|
948
+ +module: mixed,
949
+ +variables: Variables,
950
+ |};
951
+
952
+ /**
953
+ * A user-supplied function that takes a ReactFlightServerTree, and deserializes
954
+ * it into a ReactFlightClientResponse object.
955
+ */
956
+ export type ReactFlightPayloadDeserializer = (
957
+ tree: ReactFlightServerTree,
958
+ ) => ReactFlightClientResponse;
@@ -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;
@@ -12,6 +12,8 @@
12
12
 
13
13
  'use strict';
14
14
 
15
+ import type {ConcreteRequest} from './RelayConcreteNode';
16
+
15
17
  /**
16
18
  * Represents a single operation used to processing and normalize runtime
17
19
  * request results.
@@ -34,7 +36,6 @@ export type NormalizationLinkedHandle = {|
34
36
  +args: ?$ReadOnlyArray<NormalizationArgument>,
35
37
  +handle: string,
36
38
  +key: string,
37
- // T45504512: new connection model
38
39
  // NOTE: this property is optional because it's expected to be rarely used
39
40
  +dynamicKey?: ?NormalizationArgument,
40
41
  +filters: ?$ReadOnlyArray<string>,
@@ -48,10 +49,10 @@ export type NormalizationScalarHandle = {|
48
49
  +args: ?$ReadOnlyArray<NormalizationArgument>,
49
50
  +handle: string,
50
51
  +key: string,
51
- // T45504512: new connection model
52
52
  // NOTE: this property is optional because it's expected to be rarely used
53
53
  +dynamicKey?: ?NormalizationArgument,
54
54
  +filters: ?$ReadOnlyArray<string>,
55
+ +handleArgs?: $ReadOnlyArray<NormalizationArgument>,
55
56
  |};
56
57
 
57
58
  export type NormalizationArgument =
@@ -73,6 +74,7 @@ export type NormalizationClientExtension = {|
73
74
  |};
74
75
 
75
76
  export type NormalizationField =
77
+ | NormalizationFlightField
76
78
  | NormalizationScalarField
77
79
  | NormalizationLinkedField;
78
80
 
@@ -138,6 +140,14 @@ export type NormalizationScalarField = {|
138
140
  +storageKey: ?string,
139
141
  |};
140
142
 
143
+ export type NormalizationFlightField = {|
144
+ +kind: 'FlightField',
145
+ +alias: ?string,
146
+ +name: string,
147
+ +args: ?$ReadOnlyArray<NormalizationArgument>,
148
+ +storageKey: ?string,
149
+ |};
150
+
141
151
  export type NormalizationTypeDiscriminator = {|
142
152
  +kind: 'TypeDiscriminator',
143
153
  +abstractKey: string,
@@ -148,6 +158,7 @@ export type NormalizationSelection =
148
158
  | NormalizationClientExtension
149
159
  | NormalizationDefer
150
160
  | NormalizationField
161
+ | NormalizationFlightField
151
162
  | NormalizationHandle
152
163
  | NormalizationInlineFragment
153
164
  | NormalizationModuleImport
@@ -196,3 +207,7 @@ export type NormalizationSelectableNode =
196
207
  | NormalizationOperation
197
208
  | NormalizationSplitOperation
198
209
  | NormalizationStream;
210
+
211
+ export type NormalizationRootNode =
212
+ | ConcreteRequest
213
+ | NormalizationSplitOperation;