relay-runtime 11.0.2 → 13.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/handlers/RelayDefaultHandlerProvider.js.flow +3 -3
- package/handlers/connection/ConnectionHandler.js.flow +9 -18
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/handlers/connection/MutationHandlers.js.flow +8 -12
- package/index.js +1 -1
- package/index.js.flow +57 -35
- package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
- package/lib/handlers/connection/ConnectionHandler.js +13 -19
- package/lib/handlers/connection/ConnectionInterface.js +1 -1
- package/lib/handlers/connection/MutationHandlers.js +4 -7
- package/lib/index.js +59 -44
- package/lib/multi-actor-environment/ActorIdentifier.js +12 -2
- package/lib/multi-actor-environment/ActorSpecificEnvironment.js +64 -20
- package/lib/multi-actor-environment/ActorUtils.js +27 -0
- package/lib/multi-actor-environment/MultiActorEnvironment.js +324 -61
- package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +1 -1
- package/lib/multi-actor-environment/index.js +6 -2
- package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -2
- package/lib/mutations/RelayRecordProxy.js +4 -3
- package/lib/mutations/RelayRecordSourceMutator.js +4 -3
- package/lib/mutations/RelayRecordSourceProxy.js +13 -5
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +19 -6
- package/lib/mutations/applyOptimisticMutation.js +7 -7
- package/lib/mutations/commitLocalUpdate.js +1 -1
- package/lib/mutations/commitMutation.js +15 -11
- package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +242 -0
- package/lib/mutations/validateMutation.js +11 -6
- package/lib/network/ConvertToExecuteFunction.js +3 -2
- package/lib/network/RelayNetwork.js +4 -3
- package/lib/network/RelayNetworkTypes.js +1 -1
- package/lib/network/RelayObservable.js +1 -1
- package/lib/network/RelayQueryResponseCache.js +22 -6
- package/lib/network/wrapNetworkWithLogObserver.js +79 -0
- package/lib/query/GraphQLTag.js +3 -2
- package/lib/query/PreloadableQueryRegistry.js +1 -1
- package/lib/query/fetchQuery.js +7 -6
- package/lib/query/fetchQueryInternal.js +1 -1
- package/lib/query/fetchQuery_DEPRECATED.js +3 -2
- package/lib/store/ClientID.js +8 -2
- package/lib/store/DataChecker.js +124 -55
- package/lib/store/OperationExecutor.js +489 -215
- package/lib/store/RelayConcreteVariables.js +27 -9
- package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
- package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
- package/lib/store/RelayModernEnvironment.js +100 -120
- package/lib/store/RelayModernFragmentSpecResolver.js +53 -27
- package/lib/store/RelayModernOperationDescriptor.js +3 -2
- package/lib/store/RelayModernRecord.js +48 -13
- package/lib/store/RelayModernSelector.js +15 -9
- package/lib/store/RelayModernStore.js +56 -23
- package/lib/store/RelayOperationTracker.js +34 -24
- package/lib/store/RelayOptimisticRecordSource.js +1 -1
- package/lib/store/RelayPublishQueue.js +35 -11
- package/lib/store/RelayReader.js +257 -72
- package/lib/store/RelayRecordSource.js +88 -4
- package/lib/store/RelayRecordState.js +1 -1
- package/lib/store/RelayReferenceMarker.js +34 -22
- package/lib/store/RelayResponseNormalizer.js +172 -96
- package/lib/store/RelayStoreReactFlightUtils.js +5 -11
- package/lib/store/RelayStoreSubscriptions.js +15 -10
- package/lib/store/RelayStoreTypes.js +1 -1
- package/lib/store/RelayStoreUtils.js +13 -8
- package/lib/store/ResolverCache.js +213 -0
- package/lib/store/ResolverFragments.js +10 -6
- package/lib/store/StoreInspector.js +1 -1
- package/lib/store/TypeID.js +1 -1
- package/lib/store/ViewerPattern.js +1 -1
- package/lib/store/cloneRelayHandleSourceField.js +6 -5
- package/lib/store/cloneRelayScalarHandleSourceField.js +6 -5
- package/lib/store/createFragmentSpecResolver.js +1 -1
- package/lib/store/createRelayContext.js +5 -3
- package/lib/store/defaultGetDataID.js +1 -1
- package/lib/store/defaultRequiredFieldLogger.js +1 -1
- package/lib/store/hasOverlappingIDs.js +1 -1
- package/lib/store/isRelayModernEnvironment.js +1 -1
- package/lib/store/normalizeRelayPayload.js +1 -1
- package/lib/store/readInlineData.js +7 -3
- package/lib/subscription/requestSubscription.js +32 -34
- package/lib/util/JSResourceTypes.flow.js +1 -1
- package/lib/util/NormalizationNode.js +1 -1
- package/lib/util/ReaderNode.js +1 -1
- package/lib/util/RelayConcreteNode.js +3 -1
- package/lib/util/RelayDefaultHandleKey.js +1 -1
- package/lib/util/RelayError.js +1 -1
- package/lib/util/RelayFeatureFlags.js +10 -7
- package/lib/util/RelayProfiler.js +1 -1
- package/lib/util/RelayReplaySubject.js +22 -7
- package/lib/util/RelayRuntimeTypes.js +1 -7
- package/lib/util/StringInterner.js +71 -0
- package/lib/util/createPayloadFor3DField.js +1 -1
- package/lib/util/deepFreeze.js +1 -1
- package/lib/util/generateID.js +1 -1
- package/lib/util/getAllRootVariables.js +29 -0
- package/lib/util/getFragmentIdentifier.js +16 -8
- package/lib/util/getOperation.js +3 -2
- package/lib/util/getPaginationMetadata.js +41 -0
- package/lib/util/getPaginationVariables.js +66 -0
- package/lib/util/getPendingOperationsForFragment.js +55 -0
- package/lib/util/getRefetchMetadata.js +36 -0
- package/lib/util/getRelayHandleKey.js +3 -3
- package/lib/util/getRequestIdentifier.js +3 -3
- package/lib/util/getValueAtPath.js +51 -0
- package/lib/util/isEmptyObject.js +2 -2
- package/lib/util/isPromise.js +1 -1
- package/lib/util/isScalarAndEqual.js +1 -1
- package/lib/util/recycleNodesInto.js +1 -1
- package/lib/util/registerEnvironmentWithDevTools.js +26 -0
- package/lib/util/reportMissingRequiredFields.js +1 -1
- package/lib/util/resolveImmediate.js +1 -1
- package/lib/util/stableCopy.js +1 -1
- package/lib/util/withDuration.js +31 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +18 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +94 -58
- package/multi-actor-environment/ActorUtils.js.flow +33 -0
- package/multi-actor-environment/MultiActorEnvironment.js.flow +366 -93
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +88 -23
- package/multi-actor-environment/index.js.flow +3 -1
- package/mutations/RelayDeclarativeMutationConfig.js.flow +33 -27
- package/mutations/RelayRecordProxy.js.flow +5 -6
- package/mutations/RelayRecordSourceMutator.js.flow +5 -7
- package/mutations/RelayRecordSourceProxy.js.flow +20 -11
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +23 -8
- package/mutations/applyOptimisticMutation.js.flow +14 -15
- package/mutations/commitLocalUpdate.js.flow +2 -2
- package/mutations/commitMutation.js.flow +36 -47
- package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +318 -0
- package/mutations/validateMutation.js.flow +27 -17
- package/network/ConvertToExecuteFunction.js.flow +3 -3
- package/network/RelayNetwork.js.flow +5 -6
- package/network/RelayNetworkTypes.js.flow +1 -1
- package/network/RelayObservable.js.flow +2 -2
- package/network/RelayQueryResponseCache.js.flow +35 -22
- package/network/wrapNetworkWithLogObserver.js.flow +99 -0
- package/package.json +2 -2
- package/query/GraphQLTag.js.flow +11 -11
- package/query/PreloadableQueryRegistry.js.flow +5 -3
- package/query/fetchQuery.js.flow +19 -19
- package/query/fetchQueryInternal.js.flow +7 -10
- package/query/fetchQuery_DEPRECATED.js.flow +7 -7
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +15 -4
- package/store/DataChecker.js.flow +142 -60
- package/store/OperationExecutor.js.flow +575 -320
- package/store/RelayConcreteVariables.js.flow +28 -9
- package/store/RelayExperimentalGraphResponseHandler.js.flow +121 -0
- package/store/RelayExperimentalGraphResponseTransform.js.flow +470 -0
- package/store/RelayModernEnvironment.js.flow +91 -115
- package/store/RelayModernFragmentSpecResolver.js.flow +56 -32
- package/store/RelayModernOperationDescriptor.js.flow +13 -8
- package/store/RelayModernRecord.js.flow +68 -12
- package/store/RelayModernSelector.js.flow +25 -15
- package/store/RelayModernStore.js.flow +67 -32
- package/store/RelayOperationTracker.js.flow +60 -44
- package/store/RelayOptimisticRecordSource.js.flow +3 -3
- package/store/RelayPublishQueue.js.flow +74 -32
- package/store/RelayReader.js.flow +319 -100
- package/store/RelayRecordSource.js.flow +73 -7
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +41 -27
- package/store/RelayResponseNormalizer.js.flow +204 -86
- package/store/RelayStoreReactFlightUtils.js.flow +5 -12
- package/store/RelayStoreSubscriptions.js.flow +20 -12
- package/store/RelayStoreTypes.js.flow +200 -41
- package/store/RelayStoreUtils.js.flow +25 -12
- package/store/ResolverCache.js.flow +249 -0
- package/store/ResolverFragments.js.flow +16 -20
- package/store/StoreInspector.js.flow +3 -3
- package/store/TypeID.js.flow +2 -2
- package/store/ViewerPattern.js.flow +3 -3
- package/store/cloneRelayHandleSourceField.js.flow +6 -7
- package/store/cloneRelayScalarHandleSourceField.js.flow +6 -7
- package/store/createFragmentSpecResolver.js.flow +4 -5
- package/store/createRelayContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +1 -1
- package/store/defaultRequiredFieldLogger.js.flow +1 -1
- package/store/hasOverlappingIDs.js.flow +1 -1
- package/store/isRelayModernEnvironment.js.flow +1 -1
- package/store/normalizeRelayPayload.js.flow +7 -8
- package/store/readInlineData.js.flow +8 -9
- package/subscription/requestSubscription.js.flow +55 -51
- package/util/JSResourceTypes.flow.js.flow +1 -1
- package/util/NormalizationNode.js.flow +11 -4
- package/util/ReaderNode.js.flow +25 -2
- package/util/RelayConcreteNode.js.flow +5 -1
- package/util/RelayDefaultHandleKey.js.flow +1 -1
- package/util/RelayError.js.flow +1 -1
- package/util/RelayFeatureFlags.js.flow +23 -15
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.js.flow +10 -10
- package/util/RelayRuntimeTypes.js.flow +70 -3
- package/util/StringInterner.js.flow +69 -0
- package/util/createPayloadFor3DField.js.flow +4 -4
- package/util/deepFreeze.js.flow +1 -1
- package/util/generateID.js.flow +1 -1
- package/util/getAllRootVariables.js.flow +36 -0
- package/util/getFragmentIdentifier.js.flow +28 -16
- package/util/getOperation.js.flow +3 -3
- package/util/getPaginationMetadata.js.flow +69 -0
- package/util/getPaginationVariables.js.flow +108 -0
- package/util/getPendingOperationsForFragment.js.flow +62 -0
- package/util/getRefetchMetadata.js.flow +76 -0
- package/util/getRelayHandleKey.js.flow +2 -3
- package/util/getRequestIdentifier.js.flow +4 -4
- package/util/getValueAtPath.js.flow +46 -0
- package/util/isEmptyObject.js.flow +2 -1
- package/util/isPromise.js.flow +1 -1
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.js.flow +1 -1
- package/util/registerEnvironmentWithDevTools.js.flow +33 -0
- package/util/reportMissingRequiredFields.js.flow +1 -1
- package/util/resolveImmediate.js.flow +2 -2
- package/util/stableCopy.js.flow +1 -1
- package/util/withDuration.js.flow +32 -0
- package/lib/store/RelayRecordSourceMapImpl.js +0 -107
- package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
- package/store/RelayRecordSourceMapImpl.js.flow +0 -91
- package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -12,12 +12,39 @@
|
|
|
12
12
|
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import type {
|
|
16
|
+
ReaderActorChange,
|
|
17
|
+
ReaderClientEdge,
|
|
18
|
+
ReaderFlightField,
|
|
19
|
+
ReaderFragment,
|
|
20
|
+
ReaderFragmentSpread,
|
|
21
|
+
ReaderInlineDataFragmentSpread,
|
|
22
|
+
ReaderLinkedField,
|
|
23
|
+
ReaderModuleImport,
|
|
24
|
+
ReaderNode,
|
|
25
|
+
ReaderRelayResolver,
|
|
26
|
+
ReaderRequiredField,
|
|
27
|
+
ReaderScalarField,
|
|
28
|
+
ReaderSelection,
|
|
29
|
+
} from '../util/ReaderNode';
|
|
30
|
+
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
31
|
+
import type {
|
|
32
|
+
ClientEdgeTraversalInfo,
|
|
33
|
+
DataIDSet,
|
|
34
|
+
MissingClientEdgeRequestInfo,
|
|
35
|
+
MissingRequiredFields,
|
|
36
|
+
Record,
|
|
37
|
+
RecordSource,
|
|
38
|
+
RequestDescriptor,
|
|
39
|
+
SelectorData,
|
|
40
|
+
SingularReaderSelector,
|
|
41
|
+
Snapshot,
|
|
42
|
+
} from './RelayStoreTypes';
|
|
43
|
+
import type {ResolverCache} from './ResolverCache';
|
|
19
44
|
|
|
20
45
|
const {
|
|
46
|
+
ACTOR_CHANGE,
|
|
47
|
+
CLIENT_EDGE,
|
|
21
48
|
CLIENT_EXTENSION,
|
|
22
49
|
CONDITION,
|
|
23
50
|
DEFER,
|
|
@@ -27,57 +54,43 @@ const {
|
|
|
27
54
|
INLINE_FRAGMENT,
|
|
28
55
|
LINKED_FIELD,
|
|
29
56
|
MODULE_IMPORT,
|
|
30
|
-
REQUIRED_FIELD,
|
|
31
57
|
RELAY_RESOLVER,
|
|
58
|
+
REQUIRED_FIELD,
|
|
32
59
|
SCALAR_FIELD,
|
|
33
60
|
STREAM,
|
|
34
61
|
} = require('../util/RelayConcreteNode');
|
|
62
|
+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
63
|
+
const ClientID = require('./ClientID');
|
|
64
|
+
const RelayModernRecord = require('./RelayModernRecord');
|
|
35
65
|
const {getReactFlightClientResponse} = require('./RelayStoreReactFlightUtils');
|
|
36
66
|
const {
|
|
37
|
-
|
|
67
|
+
CLIENT_EDGE_TRAVERSAL_PATH,
|
|
38
68
|
FRAGMENT_OWNER_KEY,
|
|
39
69
|
FRAGMENT_PROP_NAME_KEY,
|
|
70
|
+
FRAGMENTS_KEY,
|
|
40
71
|
ID_KEY,
|
|
41
72
|
IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
|
|
42
73
|
MODULE_COMPONENT_KEY,
|
|
43
74
|
ROOT_ID,
|
|
44
75
|
getArgumentValues,
|
|
45
|
-
getStorageKey,
|
|
46
76
|
getModuleComponentKey,
|
|
77
|
+
getStorageKey,
|
|
47
78
|
} = require('./RelayStoreUtils');
|
|
79
|
+
const {NoopResolverCache} = require('./ResolverCache');
|
|
48
80
|
const {withResolverContext} = require('./ResolverFragments');
|
|
49
81
|
const {generateTypeID} = require('./TypeID');
|
|
50
|
-
|
|
51
|
-
import type {
|
|
52
|
-
ReaderFlightField,
|
|
53
|
-
ReaderFragment,
|
|
54
|
-
ReaderFragmentSpread,
|
|
55
|
-
ReaderInlineDataFragmentSpread,
|
|
56
|
-
ReaderLinkedField,
|
|
57
|
-
ReaderModuleImport,
|
|
58
|
-
ReaderNode,
|
|
59
|
-
ReaderRelayResolver,
|
|
60
|
-
ReaderRequiredField,
|
|
61
|
-
ReaderScalarField,
|
|
62
|
-
ReaderSelection,
|
|
63
|
-
} from '../util/ReaderNode';
|
|
64
|
-
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
65
|
-
import type {
|
|
66
|
-
Record,
|
|
67
|
-
RecordSource,
|
|
68
|
-
RequestDescriptor,
|
|
69
|
-
SelectorData,
|
|
70
|
-
SingularReaderSelector,
|
|
71
|
-
Snapshot,
|
|
72
|
-
MissingRequiredFields,
|
|
73
|
-
DataIDSet,
|
|
74
|
-
} from './RelayStoreTypes';
|
|
82
|
+
const invariant = require('invariant');
|
|
75
83
|
|
|
76
84
|
function read(
|
|
77
85
|
recordSource: RecordSource,
|
|
78
86
|
selector: SingularReaderSelector,
|
|
87
|
+
resolverCache?: ResolverCache,
|
|
79
88
|
): Snapshot {
|
|
80
|
-
const reader = new RelayReader(
|
|
89
|
+
const reader = new RelayReader(
|
|
90
|
+
recordSource,
|
|
91
|
+
selector,
|
|
92
|
+
resolverCache ?? new NoopResolverCache(),
|
|
93
|
+
);
|
|
81
94
|
return reader.read();
|
|
82
95
|
}
|
|
83
96
|
|
|
@@ -85,7 +98,9 @@ function read(
|
|
|
85
98
|
* @private
|
|
86
99
|
*/
|
|
87
100
|
class RelayReader {
|
|
101
|
+
_clientEdgeTraversalPath: Array<ClientEdgeTraversalInfo | null>;
|
|
88
102
|
_isMissingData: boolean;
|
|
103
|
+
_missingClientEdges: Array<MissingClientEdgeRequestInfo>;
|
|
89
104
|
_isWithinUnmatchedTypeRefinement: boolean;
|
|
90
105
|
_missingRequiredFields: ?MissingRequiredFields;
|
|
91
106
|
_owner: RequestDescriptor;
|
|
@@ -93,8 +108,19 @@ class RelayReader {
|
|
|
93
108
|
_seenRecords: DataIDSet;
|
|
94
109
|
_selector: SingularReaderSelector;
|
|
95
110
|
_variables: Variables;
|
|
111
|
+
_resolverCache: ResolverCache;
|
|
96
112
|
|
|
97
|
-
constructor(
|
|
113
|
+
constructor(
|
|
114
|
+
recordSource: RecordSource,
|
|
115
|
+
selector: SingularReaderSelector,
|
|
116
|
+
resolverCache: ResolverCache,
|
|
117
|
+
) {
|
|
118
|
+
this._clientEdgeTraversalPath =
|
|
119
|
+
RelayFeatureFlags.ENABLE_CLIENT_EDGES &&
|
|
120
|
+
selector.clientEdgeTraversalPath?.length
|
|
121
|
+
? [...selector.clientEdgeTraversalPath]
|
|
122
|
+
: [];
|
|
123
|
+
this._missingClientEdges = [];
|
|
98
124
|
this._isMissingData = false;
|
|
99
125
|
this._isWithinUnmatchedTypeRefinement = false;
|
|
100
126
|
this._missingRequiredFields = null;
|
|
@@ -103,6 +129,7 @@ class RelayReader {
|
|
|
103
129
|
this._seenRecords = new Set();
|
|
104
130
|
this._selector = selector;
|
|
105
131
|
this._variables = selector.variables;
|
|
132
|
+
this._resolverCache = resolverCache;
|
|
106
133
|
}
|
|
107
134
|
|
|
108
135
|
read(): Snapshot {
|
|
@@ -126,7 +153,18 @@ class RelayReader {
|
|
|
126
153
|
// match, then no data is expected to be present.
|
|
127
154
|
if (isDataExpectedToBePresent && abstractKey == null && record != null) {
|
|
128
155
|
const recordType = RelayModernRecord.getType(record);
|
|
129
|
-
if (
|
|
156
|
+
if (
|
|
157
|
+
recordType !== node.type &&
|
|
158
|
+
// The root record type is a special `__Root` type and may not match the
|
|
159
|
+
// type on the ast, so ignore type mismatches at the root.
|
|
160
|
+
// We currently detect whether we're at the root by checking against ROOT_ID,
|
|
161
|
+
// but this does not work for mutations/subscriptions which generate unique
|
|
162
|
+
// root ids. This is acceptable in practice as we don't read data for mutations/
|
|
163
|
+
// subscriptions in a situation where we would use isMissingData to decide whether
|
|
164
|
+
// to suspend or not.
|
|
165
|
+
// TODO T96653810: Correctly detect reading from root of mutation/subscription
|
|
166
|
+
dataID !== ROOT_ID
|
|
167
|
+
) {
|
|
130
168
|
isDataExpectedToBePresent = false;
|
|
131
169
|
}
|
|
132
170
|
}
|
|
@@ -135,12 +173,7 @@ class RelayReader {
|
|
|
135
173
|
// then data is only expected to be present if the record type is known to
|
|
136
174
|
// implement the interface. If we aren't sure whether the record implements
|
|
137
175
|
// the interface, that itself constitutes "expected" data being missing.
|
|
138
|
-
if (
|
|
139
|
-
isDataExpectedToBePresent &&
|
|
140
|
-
abstractKey != null &&
|
|
141
|
-
record != null &&
|
|
142
|
-
RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT
|
|
143
|
-
) {
|
|
176
|
+
if (isDataExpectedToBePresent && abstractKey != null && record != null) {
|
|
144
177
|
const recordType = RelayModernRecord.getType(record);
|
|
145
178
|
const typeID = generateTypeID(recordType);
|
|
146
179
|
const typeRecord = this._recordSource.get(typeID);
|
|
@@ -162,12 +195,36 @@ class RelayReader {
|
|
|
162
195
|
return {
|
|
163
196
|
data,
|
|
164
197
|
isMissingData: this._isMissingData && isDataExpectedToBePresent,
|
|
198
|
+
missingClientEdges:
|
|
199
|
+
RelayFeatureFlags.ENABLE_CLIENT_EDGES && this._missingClientEdges.length
|
|
200
|
+
? this._missingClientEdges
|
|
201
|
+
: null,
|
|
165
202
|
seenRecords: this._seenRecords,
|
|
166
203
|
selector: this._selector,
|
|
167
204
|
missingRequiredFields: this._missingRequiredFields,
|
|
168
205
|
};
|
|
169
206
|
}
|
|
170
207
|
|
|
208
|
+
_markDataAsMissing(): void {
|
|
209
|
+
this._isMissingData = true;
|
|
210
|
+
if (
|
|
211
|
+
RelayFeatureFlags.ENABLE_CLIENT_EDGES &&
|
|
212
|
+
this._clientEdgeTraversalPath.length
|
|
213
|
+
) {
|
|
214
|
+
const top =
|
|
215
|
+
this._clientEdgeTraversalPath[this._clientEdgeTraversalPath.length - 1];
|
|
216
|
+
// Top can be null if we've traversed past a client edge into an ordinary
|
|
217
|
+
// client extension field; we never want to fetch in response to missing
|
|
218
|
+
// data off of a client extension field.
|
|
219
|
+
if (top !== null) {
|
|
220
|
+
this._missingClientEdges.push({
|
|
221
|
+
request: top.readerClientEdge.operation,
|
|
222
|
+
clientEdgeDestinationID: top.clientEdgeDestinationID,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
171
228
|
_traverse(
|
|
172
229
|
node: ReaderNode,
|
|
173
230
|
dataID: DataID,
|
|
@@ -177,7 +234,7 @@ class RelayReader {
|
|
|
177
234
|
this._seenRecords.add(dataID);
|
|
178
235
|
if (record == null) {
|
|
179
236
|
if (record === undefined) {
|
|
180
|
-
this.
|
|
237
|
+
this._markDataAsMissing();
|
|
181
238
|
}
|
|
182
239
|
return record;
|
|
183
240
|
}
|
|
@@ -196,7 +253,6 @@ class RelayReader {
|
|
|
196
253
|
'RelayReader(): Undefined variable `%s`.',
|
|
197
254
|
name,
|
|
198
255
|
);
|
|
199
|
-
// $FlowFixMe[cannot-write]
|
|
200
256
|
return this._variables[name];
|
|
201
257
|
}
|
|
202
258
|
|
|
@@ -237,13 +293,6 @@ class RelayReader {
|
|
|
237
293
|
const selection = selections[i];
|
|
238
294
|
switch (selection.kind) {
|
|
239
295
|
case REQUIRED_FIELD:
|
|
240
|
-
invariant(
|
|
241
|
-
RelayFeatureFlags.ENABLE_REQUIRED_DIRECTIVES,
|
|
242
|
-
'RelayReader(): Encountered a `@required` directive at path "%s" in `%s` without the `ENABLE_REQUIRED_DIRECTIVES` feature flag enabled.',
|
|
243
|
-
selection.path,
|
|
244
|
-
this._selector.node.name,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
296
|
const fieldValue = this._readRequiredField(selection, record, data);
|
|
248
297
|
if (fieldValue == null) {
|
|
249
298
|
const {action} = selection;
|
|
@@ -267,7 +316,9 @@ class RelayReader {
|
|
|
267
316
|
}
|
|
268
317
|
break;
|
|
269
318
|
case CONDITION:
|
|
270
|
-
const conditionValue =
|
|
319
|
+
const conditionValue = Boolean(
|
|
320
|
+
this._getVariableValue(selection.condition),
|
|
321
|
+
);
|
|
271
322
|
if (conditionValue === selection.passingValue) {
|
|
272
323
|
const hasExpectedData = this._traverseSelections(
|
|
273
324
|
selection.selections,
|
|
@@ -294,15 +345,15 @@ class RelayReader {
|
|
|
294
345
|
return false;
|
|
295
346
|
}
|
|
296
347
|
}
|
|
297
|
-
} else
|
|
348
|
+
} else {
|
|
298
349
|
// Similar to the logic in read(): data is only expected to be present
|
|
299
350
|
// if the record is known to conform to the interface. If we don't know
|
|
300
351
|
// whether the type conforms or not, that constitutes missing data.
|
|
301
352
|
|
|
302
353
|
// store flags to reset after reading
|
|
303
354
|
const parentIsMissingData = this._isMissingData;
|
|
304
|
-
const parentIsWithinUnmatchedTypeRefinement =
|
|
305
|
-
._isWithinUnmatchedTypeRefinement;
|
|
355
|
+
const parentIsWithinUnmatchedTypeRefinement =
|
|
356
|
+
this._isWithinUnmatchedTypeRefinement;
|
|
306
357
|
|
|
307
358
|
const typeName = RelayModernRecord.getType(record);
|
|
308
359
|
const typeID = generateTypeID(typeName);
|
|
@@ -315,19 +366,16 @@ class RelayReader {
|
|
|
315
366
|
parentIsWithinUnmatchedTypeRefinement ||
|
|
316
367
|
implementsInterface === false;
|
|
317
368
|
this._traverseSelections(selection.selections, record, data);
|
|
318
|
-
this._isWithinUnmatchedTypeRefinement =
|
|
369
|
+
this._isWithinUnmatchedTypeRefinement =
|
|
370
|
+
parentIsWithinUnmatchedTypeRefinement;
|
|
319
371
|
|
|
320
372
|
if (implementsInterface === false) {
|
|
321
373
|
// Type known to not implement the interface, no data expected
|
|
322
374
|
this._isMissingData = parentIsMissingData;
|
|
323
375
|
} else if (implementsInterface == null) {
|
|
324
376
|
// Don't know if the type implements the interface or not
|
|
325
|
-
this.
|
|
377
|
+
this._markDataAsMissing();
|
|
326
378
|
}
|
|
327
|
-
} else {
|
|
328
|
-
// legacy behavior for abstract refinements: always read even
|
|
329
|
-
// if the type doesn't conform and don't reset isMissingData
|
|
330
|
-
this._traverseSelections(selection.selections, record, data);
|
|
331
379
|
}
|
|
332
380
|
break;
|
|
333
381
|
}
|
|
@@ -354,12 +402,24 @@ class RelayReader {
|
|
|
354
402
|
case DEFER:
|
|
355
403
|
case CLIENT_EXTENSION: {
|
|
356
404
|
const isMissingData = this._isMissingData;
|
|
405
|
+
const alreadyMissingClientEdges = this._missingClientEdges.length;
|
|
406
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
407
|
+
this._clientEdgeTraversalPath.push(null);
|
|
408
|
+
}
|
|
357
409
|
const hasExpectedData = this._traverseSelections(
|
|
358
410
|
selection.selections,
|
|
359
411
|
record,
|
|
360
412
|
data,
|
|
361
413
|
);
|
|
362
|
-
|
|
414
|
+
// The only case where we want to suspend due to missing data off of
|
|
415
|
+
// a client extension is if we reached a client edge that we might be
|
|
416
|
+
// able to fetch:
|
|
417
|
+
this._isMissingData =
|
|
418
|
+
isMissingData ||
|
|
419
|
+
this._missingClientEdges.length > alreadyMissingClientEdges;
|
|
420
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
421
|
+
this._clientEdgeTraversalPath.pop();
|
|
422
|
+
}
|
|
363
423
|
if (!hasExpectedData) {
|
|
364
424
|
return false;
|
|
365
425
|
}
|
|
@@ -383,6 +443,16 @@ class RelayReader {
|
|
|
383
443
|
throw new Error('Flight fields are not yet supported.');
|
|
384
444
|
}
|
|
385
445
|
break;
|
|
446
|
+
case ACTOR_CHANGE:
|
|
447
|
+
this._readActorChange(selection, record, data);
|
|
448
|
+
break;
|
|
449
|
+
case CLIENT_EDGE:
|
|
450
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
451
|
+
this._readClientEdge(selection, record, data);
|
|
452
|
+
} else {
|
|
453
|
+
throw new Error('Client edges are not yet supported.');
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
386
456
|
default:
|
|
387
457
|
(selection: empty);
|
|
388
458
|
invariant(
|
|
@@ -409,6 +479,12 @@ class RelayReader {
|
|
|
409
479
|
} else {
|
|
410
480
|
return this._readLink(selection.field, record, data);
|
|
411
481
|
}
|
|
482
|
+
case RELAY_RESOLVER:
|
|
483
|
+
if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
|
|
484
|
+
throw new Error('Relay Resolver fields are not yet supported.');
|
|
485
|
+
}
|
|
486
|
+
this._readResolverField(selection.field, record, data);
|
|
487
|
+
break;
|
|
412
488
|
default:
|
|
413
489
|
(selection.field.kind: empty);
|
|
414
490
|
invariant(
|
|
@@ -420,40 +496,142 @@ class RelayReader {
|
|
|
420
496
|
}
|
|
421
497
|
|
|
422
498
|
_readResolverField(
|
|
423
|
-
|
|
499
|
+
field: ReaderRelayResolver,
|
|
424
500
|
record: Record,
|
|
425
501
|
data: SelectorData,
|
|
426
|
-
):
|
|
427
|
-
const {
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
502
|
+
): void {
|
|
503
|
+
const {resolverModule, fragment} = field;
|
|
504
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
505
|
+
const resolverID = ClientID.generateClientID(
|
|
506
|
+
RelayModernRecord.getDataID(record),
|
|
507
|
+
storageKey,
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
// Found when reading the resolver fragment, which can happen either when
|
|
511
|
+
// evaluating the resolver and it calls readFragment, or when checking if the
|
|
512
|
+
// inputs have changed since a previous evaluation:
|
|
513
|
+
let fragmentValue;
|
|
514
|
+
let fragmentReaderSelector;
|
|
515
|
+
const fragmentSeenRecordIDs = new Set();
|
|
516
|
+
|
|
517
|
+
const getDataForResolverFragment = singularReaderSelector => {
|
|
518
|
+
if (fragmentValue != null) {
|
|
519
|
+
// It was already read when checking for input staleness; no need to read it again.
|
|
520
|
+
// Note that the variables like fragmentSeenRecordIDs in the outer closure will have
|
|
521
|
+
// already been set and will still be used in this case.
|
|
522
|
+
return fragmentValue;
|
|
523
|
+
}
|
|
524
|
+
fragmentReaderSelector = singularReaderSelector;
|
|
525
|
+
const existingSeenRecords = this._seenRecords;
|
|
526
|
+
try {
|
|
527
|
+
this._seenRecords = fragmentSeenRecordIDs;
|
|
437
528
|
const resolverFragmentData = {};
|
|
438
529
|
this._createInlineDataOrResolverFragmentPointer(
|
|
439
530
|
singularReaderSelector.node,
|
|
440
531
|
record,
|
|
441
532
|
resolverFragmentData,
|
|
442
533
|
);
|
|
443
|
-
|
|
534
|
+
fragmentValue = resolverFragmentData[FRAGMENTS_KEY]?.[fragment.name];
|
|
444
535
|
invariant(
|
|
445
|
-
typeof
|
|
536
|
+
typeof fragmentValue === 'object' && fragmentValue !== null,
|
|
446
537
|
`Expected reader data to contain a __fragments property with a property for the fragment named ${fragment.name}, but it is missing.`,
|
|
447
538
|
);
|
|
448
|
-
return
|
|
449
|
-
}
|
|
539
|
+
return fragmentValue;
|
|
540
|
+
} finally {
|
|
541
|
+
this._seenRecords = existingSeenRecords;
|
|
542
|
+
}
|
|
450
543
|
};
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
544
|
+
const resolverContext = {getDataForResolverFragment};
|
|
545
|
+
|
|
546
|
+
const [result, seenRecord] = this._resolverCache.readFromCacheOrEvaluate(
|
|
547
|
+
record,
|
|
548
|
+
field,
|
|
549
|
+
this._variables,
|
|
550
|
+
() => {
|
|
551
|
+
const key = {
|
|
552
|
+
__id: RelayModernRecord.getDataID(record),
|
|
553
|
+
__fragmentOwner: this._owner,
|
|
554
|
+
__fragments: {
|
|
555
|
+
[fragment.name]: {}, // Arguments to this fragment; not yet supported.
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
return withResolverContext(resolverContext, () => {
|
|
559
|
+
// $FlowFixMe[prop-missing] - resolver module's type signature is a lie
|
|
560
|
+
const resolverResult = resolverModule(key);
|
|
561
|
+
return {
|
|
562
|
+
resolverResult,
|
|
563
|
+
fragmentValue,
|
|
564
|
+
resolverID,
|
|
565
|
+
seenRecordIDs: fragmentSeenRecordIDs,
|
|
566
|
+
readerSelector: fragmentReaderSelector,
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
},
|
|
570
|
+
getDataForResolverFragment,
|
|
571
|
+
);
|
|
572
|
+
if (seenRecord != null) {
|
|
573
|
+
this._seenRecords.add(seenRecord);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const applicationName = field.alias ?? field.name;
|
|
577
|
+
data[applicationName] = result;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
_readClientEdge(
|
|
581
|
+
field: ReaderClientEdge,
|
|
582
|
+
record: Record,
|
|
583
|
+
data: SelectorData,
|
|
584
|
+
): void {
|
|
585
|
+
const backingField = field.backingField;
|
|
586
|
+
|
|
587
|
+
// Because ReaderClientExtension doesn't have `alias` or `name` and so I don't know
|
|
588
|
+
// how to get its applicationName or storageKey yet:
|
|
589
|
+
invariant(
|
|
590
|
+
backingField.kind !== 'ClientExtension',
|
|
591
|
+
'Client extension client edges are not yet implemented.',
|
|
592
|
+
);
|
|
593
|
+
|
|
594
|
+
const applicationName = backingField.alias ?? backingField.name;
|
|
595
|
+
|
|
596
|
+
const backingFieldData = {};
|
|
597
|
+
this._traverseSelections([backingField], record, backingFieldData);
|
|
598
|
+
const destinationDataID = backingFieldData[applicationName];
|
|
599
|
+
|
|
600
|
+
if (destinationDataID == null) {
|
|
601
|
+
data[applicationName] = destinationDataID;
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
invariant(
|
|
606
|
+
typeof destinationDataID === 'string',
|
|
607
|
+
'Plural client edges not are yet implemented',
|
|
608
|
+
); // FIXME support plural
|
|
609
|
+
|
|
610
|
+
// Not wrapping the push/pop in a try/finally because if we throw, the
|
|
611
|
+
// Reader object is not usable after that anyway.
|
|
612
|
+
this._clientEdgeTraversalPath.push({
|
|
613
|
+
readerClientEdge: field,
|
|
614
|
+
clientEdgeDestinationID: destinationDataID,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const prevData = data[applicationName];
|
|
618
|
+
invariant(
|
|
619
|
+
prevData == null || typeof prevData === 'object',
|
|
620
|
+
'RelayReader(): Expected data for field `%s` on record `%s` ' +
|
|
621
|
+
'to be an object, got `%s`.',
|
|
622
|
+
applicationName,
|
|
623
|
+
RelayModernRecord.getDataID(record),
|
|
624
|
+
prevData,
|
|
454
625
|
);
|
|
455
|
-
|
|
456
|
-
|
|
626
|
+
const value = this._traverse(
|
|
627
|
+
field.linkedField,
|
|
628
|
+
destinationDataID,
|
|
629
|
+
// $FlowFixMe[incompatible-variance]
|
|
630
|
+
prevData,
|
|
631
|
+
);
|
|
632
|
+
data[applicationName] = value;
|
|
633
|
+
|
|
634
|
+
this._clientEdgeTraversalPath.pop();
|
|
457
635
|
}
|
|
458
636
|
|
|
459
637
|
_readFlightField(
|
|
@@ -463,14 +641,12 @@ class RelayReader {
|
|
|
463
641
|
): ?mixed {
|
|
464
642
|
const applicationName = field.alias ?? field.name;
|
|
465
643
|
const storageKey = getStorageKey(field, this._variables);
|
|
466
|
-
const reactFlightClientResponseRecordID =
|
|
467
|
-
record,
|
|
468
|
-
storageKey,
|
|
469
|
-
);
|
|
644
|
+
const reactFlightClientResponseRecordID =
|
|
645
|
+
RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
470
646
|
if (reactFlightClientResponseRecordID == null) {
|
|
471
647
|
data[applicationName] = reactFlightClientResponseRecordID;
|
|
472
648
|
if (reactFlightClientResponseRecordID === undefined) {
|
|
473
|
-
this.
|
|
649
|
+
this._markDataAsMissing();
|
|
474
650
|
}
|
|
475
651
|
return reactFlightClientResponseRecordID;
|
|
476
652
|
}
|
|
@@ -481,7 +657,7 @@ class RelayReader {
|
|
|
481
657
|
if (reactFlightClientResponseRecord == null) {
|
|
482
658
|
data[applicationName] = reactFlightClientResponseRecord;
|
|
483
659
|
if (reactFlightClientResponseRecord === undefined) {
|
|
484
|
-
this.
|
|
660
|
+
this._markDataAsMissing();
|
|
485
661
|
}
|
|
486
662
|
return reactFlightClientResponseRecord;
|
|
487
663
|
}
|
|
@@ -501,7 +677,7 @@ class RelayReader {
|
|
|
501
677
|
const storageKey = getStorageKey(field, this._variables);
|
|
502
678
|
const value = RelayModernRecord.getValue(record, storageKey);
|
|
503
679
|
if (value === undefined) {
|
|
504
|
-
this.
|
|
680
|
+
this._markDataAsMissing();
|
|
505
681
|
}
|
|
506
682
|
data[applicationName] = value;
|
|
507
683
|
return value;
|
|
@@ -518,7 +694,7 @@ class RelayReader {
|
|
|
518
694
|
if (linkedID == null) {
|
|
519
695
|
data[applicationName] = linkedID;
|
|
520
696
|
if (linkedID === undefined) {
|
|
521
|
-
this.
|
|
697
|
+
this._markDataAsMissing();
|
|
522
698
|
}
|
|
523
699
|
return linkedID;
|
|
524
700
|
}
|
|
@@ -538,6 +714,42 @@ class RelayReader {
|
|
|
538
714
|
return value;
|
|
539
715
|
}
|
|
540
716
|
|
|
717
|
+
_readActorChange(
|
|
718
|
+
field: ReaderActorChange,
|
|
719
|
+
record: Record,
|
|
720
|
+
data: SelectorData,
|
|
721
|
+
): ?mixed {
|
|
722
|
+
const applicationName = field.alias ?? field.name;
|
|
723
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
724
|
+
const externalRef = RelayModernRecord.getActorLinkedRecordID(
|
|
725
|
+
record,
|
|
726
|
+
storageKey,
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
if (externalRef == null) {
|
|
730
|
+
data[applicationName] = externalRef;
|
|
731
|
+
if (externalRef === undefined) {
|
|
732
|
+
this._markDataAsMissing();
|
|
733
|
+
}
|
|
734
|
+
return data[applicationName];
|
|
735
|
+
}
|
|
736
|
+
const [actorIdentifier, dataID] = externalRef;
|
|
737
|
+
|
|
738
|
+
const fragmentRef = {};
|
|
739
|
+
this._createFragmentPointer(
|
|
740
|
+
field.fragmentSpread,
|
|
741
|
+
{
|
|
742
|
+
__id: dataID,
|
|
743
|
+
},
|
|
744
|
+
fragmentRef,
|
|
745
|
+
);
|
|
746
|
+
data[applicationName] = {
|
|
747
|
+
__fragmentRef: fragmentRef,
|
|
748
|
+
__viewer: actorIdentifier,
|
|
749
|
+
};
|
|
750
|
+
return data[applicationName];
|
|
751
|
+
}
|
|
752
|
+
|
|
541
753
|
_readPluralLink(
|
|
542
754
|
field: ReaderLinkedField,
|
|
543
755
|
record: Record,
|
|
@@ -550,7 +762,7 @@ class RelayReader {
|
|
|
550
762
|
if (linkedIDs == null) {
|
|
551
763
|
data[applicationName] = linkedIDs;
|
|
552
764
|
if (linkedIDs === undefined) {
|
|
553
|
-
this.
|
|
765
|
+
this._markDataAsMissing();
|
|
554
766
|
}
|
|
555
767
|
return linkedIDs;
|
|
556
768
|
}
|
|
@@ -568,7 +780,7 @@ class RelayReader {
|
|
|
568
780
|
linkedIDs.forEach((linkedID, nextIndex) => {
|
|
569
781
|
if (linkedID == null) {
|
|
570
782
|
if (linkedID === undefined) {
|
|
571
|
-
this.
|
|
783
|
+
this._markDataAsMissing();
|
|
572
784
|
}
|
|
573
785
|
// $FlowFixMe[cannot-write]
|
|
574
786
|
linkedArray[nextIndex] = linkedID;
|
|
@@ -606,7 +818,7 @@ class RelayReader {
|
|
|
606
818
|
const component = RelayModernRecord.getValue(record, componentKey);
|
|
607
819
|
if (component == null) {
|
|
608
820
|
if (component === undefined) {
|
|
609
|
-
this.
|
|
821
|
+
this._markDataAsMissing();
|
|
610
822
|
}
|
|
611
823
|
return;
|
|
612
824
|
}
|
|
@@ -620,7 +832,7 @@ class RelayReader {
|
|
|
620
832
|
{
|
|
621
833
|
kind: 'FragmentSpread',
|
|
622
834
|
name: moduleImport.fragmentName,
|
|
623
|
-
args:
|
|
835
|
+
args: moduleImport.args,
|
|
624
836
|
},
|
|
625
837
|
record,
|
|
626
838
|
data,
|
|
@@ -651,11 +863,18 @@ class RelayReader {
|
|
|
651
863
|
? getArgumentValues(fragmentSpread.args, this._variables)
|
|
652
864
|
: {};
|
|
653
865
|
data[FRAGMENT_OWNER_KEY] = this._owner;
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
866
|
+
data[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] =
|
|
867
|
+
this._isWithinUnmatchedTypeRefinement;
|
|
868
|
+
|
|
869
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
870
|
+
if (
|
|
871
|
+
this._clientEdgeTraversalPath.length > 0 &&
|
|
872
|
+
this._clientEdgeTraversalPath[
|
|
873
|
+
this._clientEdgeTraversalPath.length - 1
|
|
874
|
+
] !== null
|
|
875
|
+
) {
|
|
876
|
+
data[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
|
|
877
|
+
}
|
|
659
878
|
}
|
|
660
879
|
}
|
|
661
880
|
|