relay-runtime 11.0.0 → 13.0.0-rc.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.
- package/handlers/RelayDefaultHandlerProvider.js.flow +2 -2
- package/handlers/connection/ConnectionHandler.js.flow +8 -10
- package/handlers/connection/MutationHandlers.js.flow +31 -7
- package/index.js +1 -1
- package/index.js.flow +60 -36
- package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
- package/lib/handlers/connection/ConnectionHandler.js +8 -8
- package/lib/handlers/connection/MutationHandlers.js +61 -5
- package/lib/index.js +58 -43
- package/lib/multi-actor-environment/ActorIdentifier.js +33 -0
- package/lib/multi-actor-environment/ActorSpecificEnvironment.js +152 -0
- package/lib/multi-actor-environment/ActorUtils.js +27 -0
- package/lib/multi-actor-environment/MultiActorEnvironment.js +419 -0
- package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +11 -0
- package/lib/multi-actor-environment/index.js +21 -0
- package/lib/mutations/RelayDeclarativeMutationConfig.js +4 -1
- package/lib/mutations/RelayRecordProxy.js +3 -2
- package/lib/mutations/RelayRecordSourceMutator.js +3 -2
- package/lib/mutations/RelayRecordSourceProxy.js +12 -4
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -5
- package/lib/mutations/applyOptimisticMutation.js +6 -6
- package/lib/mutations/commitMutation.js +14 -10
- package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +238 -0
- package/lib/mutations/validateMutation.js +12 -5
- package/lib/network/ConvertToExecuteFunction.js +2 -1
- package/lib/network/RelayNetwork.js +3 -2
- package/lib/network/RelayQueryResponseCache.js +21 -4
- package/lib/network/wrapNetworkWithLogObserver.js +79 -0
- package/lib/query/GraphQLTag.js +3 -2
- package/lib/query/fetchQuery.js +6 -5
- package/lib/query/fetchQueryInternal.js +1 -1
- package/lib/query/fetchQuery_DEPRECATED.js +2 -1
- package/lib/store/ClientID.js +7 -1
- package/lib/store/DataChecker.js +141 -60
- package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +532 -195
- package/lib/store/RelayConcreteVariables.js +24 -4
- package/lib/store/RelayModernEnvironment.js +175 -234
- package/lib/store/RelayModernFragmentSpecResolver.js +52 -26
- package/lib/store/RelayModernOperationDescriptor.js +2 -1
- package/lib/store/RelayModernRecord.js +47 -12
- package/lib/store/RelayModernSelector.js +14 -8
- package/lib/store/RelayModernStore.js +58 -29
- package/lib/store/RelayOperationTracker.js +34 -24
- package/lib/store/RelayPublishQueue.js +41 -13
- package/lib/store/RelayReader.js +287 -46
- package/lib/store/RelayRecordSource.js +87 -3
- package/lib/store/RelayReferenceMarker.js +55 -31
- package/lib/store/RelayResponseNormalizer.js +250 -108
- package/lib/store/RelayStoreReactFlightUtils.js +8 -12
- package/lib/store/RelayStoreSubscriptions.js +14 -9
- package/lib/store/RelayStoreUtils.js +11 -5
- package/lib/store/ResolverCache.js +213 -0
- package/lib/store/ResolverFragments.js +61 -0
- package/lib/store/cloneRelayHandleSourceField.js +5 -4
- package/lib/store/cloneRelayScalarHandleSourceField.js +5 -4
- package/lib/store/createRelayContext.js +4 -2
- package/lib/store/defaultGetDataID.js +3 -1
- package/lib/store/readInlineData.js +6 -2
- package/lib/subscription/requestSubscription.js +35 -9
- package/lib/util/RelayConcreteNode.js +4 -0
- package/lib/util/RelayFeatureFlags.js +11 -4
- package/lib/util/RelayProfiler.js +17 -187
- package/lib/util/RelayReplaySubject.js +22 -7
- package/lib/util/RelayRuntimeTypes.js +0 -6
- package/lib/util/StringInterner.js +71 -0
- package/lib/util/deepFreeze.js +1 -0
- package/lib/util/getFragmentIdentifier.js +15 -7
- package/lib/util/getOperation.js +2 -1
- 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 +2 -2
- package/lib/util/getRequestIdentifier.js +2 -2
- package/lib/util/getValueAtPath.js +51 -0
- package/lib/util/isEmptyObject.js +1 -1
- package/lib/util/registerEnvironmentWithDevTools.js +26 -0
- package/lib/util/withDuration.js +31 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +43 -0
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +225 -0
- package/multi-actor-environment/ActorUtils.js.flow +33 -0
- package/multi-actor-environment/MultiActorEnvironment.js.flow +506 -0
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +261 -0
- package/multi-actor-environment/index.js.flow +26 -0
- package/mutations/RelayDeclarativeMutationConfig.js.flow +32 -26
- package/mutations/RelayRecordProxy.js.flow +4 -5
- package/mutations/RelayRecordSourceMutator.js.flow +4 -6
- package/mutations/RelayRecordSourceProxy.js.flow +19 -10
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +22 -7
- package/mutations/applyOptimisticMutation.js.flow +13 -14
- package/mutations/commitLocalUpdate.js.flow +1 -1
- package/mutations/commitMutation.js.flow +35 -46
- package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +309 -0
- package/mutations/validateMutation.js.flow +28 -16
- package/network/ConvertToExecuteFunction.js.flow +2 -2
- package/network/RelayNetwork.js.flow +4 -5
- package/network/RelayNetworkTypes.js.flow +17 -8
- package/network/RelayObservable.js.flow +1 -1
- package/network/RelayQueryResponseCache.js.flow +34 -20
- package/network/wrapNetworkWithLogObserver.js.flow +100 -0
- package/package.json +3 -2
- package/query/GraphQLTag.js.flow +9 -9
- package/query/PreloadableQueryRegistry.js.flow +2 -1
- package/query/fetchQuery.js.flow +11 -13
- package/query/fetchQueryInternal.js.flow +6 -9
- package/query/fetchQuery_DEPRECATED.js.flow +6 -6
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +14 -3
- package/store/DataChecker.js.flow +162 -67
- package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +616 -283
- package/store/RelayConcreteVariables.js.flow +27 -5
- package/store/RelayModernEnvironment.js.flow +176 -235
- package/store/RelayModernFragmentSpecResolver.js.flow +55 -31
- package/store/RelayModernOperationDescriptor.js.flow +12 -7
- package/store/RelayModernRecord.js.flow +67 -11
- package/store/RelayModernSelector.js.flow +24 -14
- package/store/RelayModernStore.js.flow +72 -36
- package/store/RelayOperationTracker.js.flow +59 -43
- package/store/RelayOptimisticRecordSource.js.flow +2 -2
- package/store/RelayPublishQueue.js.flow +79 -34
- package/store/RelayReader.js.flow +351 -72
- package/store/RelayRecordSource.js.flow +72 -6
- package/store/RelayReferenceMarker.js.flow +60 -33
- package/store/RelayResponseNormalizer.js.flow +288 -102
- package/store/RelayStoreReactFlightUtils.js.flow +9 -13
- package/store/RelayStoreSubscriptions.js.flow +19 -11
- package/store/RelayStoreTypes.js.flow +210 -44
- package/store/RelayStoreUtils.js.flow +25 -11
- package/store/ResolverCache.js.flow +249 -0
- package/store/ResolverFragments.js.flow +121 -0
- package/store/StoreInspector.js.flow +2 -2
- package/store/TypeID.js.flow +1 -1
- package/store/ViewerPattern.js.flow +2 -2
- package/store/cloneRelayHandleSourceField.js.flow +5 -6
- package/store/cloneRelayScalarHandleSourceField.js.flow +5 -6
- package/store/createFragmentSpecResolver.js.flow +3 -4
- package/store/createRelayContext.js.flow +3 -3
- package/store/defaultGetDataID.js.flow +3 -1
- package/store/normalizeRelayPayload.js.flow +6 -7
- package/store/readInlineData.js.flow +7 -8
- package/subscription/requestSubscription.js.flow +54 -27
- package/util/NormalizationNode.js.flow +16 -3
- package/util/ReaderNode.js.flow +38 -2
- package/util/RelayConcreteNode.js.flow +4 -0
- package/util/RelayFeatureFlags.js.flow +24 -8
- package/util/RelayProfiler.js.flow +22 -194
- package/util/RelayReplaySubject.js.flow +9 -9
- package/util/RelayRuntimeTypes.js.flow +73 -4
- package/util/StringInterner.js.flow +69 -0
- package/util/createPayloadFor3DField.js.flow +3 -3
- package/util/deepFreeze.js.flow +2 -1
- package/util/getFragmentIdentifier.js.flow +27 -15
- package/util/getOperation.js.flow +2 -2
- package/util/getPaginationMetadata.js.flow +72 -0
- package/util/getPaginationVariables.js.flow +108 -0
- package/util/getPendingOperationsForFragment.js.flow +62 -0
- package/util/getRefetchMetadata.js.flow +79 -0
- package/util/getRelayHandleKey.js.flow +1 -2
- package/util/getRequestIdentifier.js.flow +3 -3
- package/util/getValueAtPath.js.flow +46 -0
- package/util/isEmptyObject.js.flow +2 -1
- package/util/registerEnvironmentWithDevTools.js.flow +33 -0
- package/util/resolveImmediate.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
|
@@ -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,53 +54,43 @@ const {
|
|
|
27
54
|
INLINE_FRAGMENT,
|
|
28
55
|
LINKED_FIELD,
|
|
29
56
|
MODULE_IMPORT,
|
|
57
|
+
RELAY_RESOLVER,
|
|
30
58
|
REQUIRED_FIELD,
|
|
31
59
|
SCALAR_FIELD,
|
|
32
60
|
STREAM,
|
|
33
61
|
} = require('../util/RelayConcreteNode');
|
|
62
|
+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
63
|
+
const ClientID = require('./ClientID');
|
|
64
|
+
const RelayModernRecord = require('./RelayModernRecord');
|
|
34
65
|
const {getReactFlightClientResponse} = require('./RelayStoreReactFlightUtils');
|
|
35
66
|
const {
|
|
36
|
-
|
|
67
|
+
CLIENT_EDGE_TRAVERSAL_PATH,
|
|
37
68
|
FRAGMENT_OWNER_KEY,
|
|
38
69
|
FRAGMENT_PROP_NAME_KEY,
|
|
70
|
+
FRAGMENTS_KEY,
|
|
39
71
|
ID_KEY,
|
|
40
72
|
IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
|
|
41
73
|
MODULE_COMPONENT_KEY,
|
|
42
74
|
ROOT_ID,
|
|
43
75
|
getArgumentValues,
|
|
44
|
-
getStorageKey,
|
|
45
76
|
getModuleComponentKey,
|
|
77
|
+
getStorageKey,
|
|
46
78
|
} = require('./RelayStoreUtils');
|
|
79
|
+
const {NoopResolverCache} = require('./ResolverCache');
|
|
80
|
+
const {withResolverContext} = require('./ResolverFragments');
|
|
47
81
|
const {generateTypeID} = require('./TypeID');
|
|
48
|
-
|
|
49
|
-
import type {
|
|
50
|
-
ReaderFlightField,
|
|
51
|
-
ReaderFragmentSpread,
|
|
52
|
-
ReaderInlineDataFragmentSpread,
|
|
53
|
-
ReaderLinkedField,
|
|
54
|
-
ReaderModuleImport,
|
|
55
|
-
ReaderNode,
|
|
56
|
-
ReaderRequiredField,
|
|
57
|
-
ReaderScalarField,
|
|
58
|
-
ReaderSelection,
|
|
59
|
-
} from '../util/ReaderNode';
|
|
60
|
-
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
61
|
-
import type {
|
|
62
|
-
Record,
|
|
63
|
-
RecordSource,
|
|
64
|
-
RequestDescriptor,
|
|
65
|
-
SelectorData,
|
|
66
|
-
SingularReaderSelector,
|
|
67
|
-
Snapshot,
|
|
68
|
-
MissingRequiredFields,
|
|
69
|
-
DataIDSet,
|
|
70
|
-
} from './RelayStoreTypes';
|
|
82
|
+
const invariant = require('invariant');
|
|
71
83
|
|
|
72
84
|
function read(
|
|
73
85
|
recordSource: RecordSource,
|
|
74
86
|
selector: SingularReaderSelector,
|
|
87
|
+
resolverCache?: ResolverCache,
|
|
75
88
|
): Snapshot {
|
|
76
|
-
const reader = new RelayReader(
|
|
89
|
+
const reader = new RelayReader(
|
|
90
|
+
recordSource,
|
|
91
|
+
selector,
|
|
92
|
+
resolverCache ?? new NoopResolverCache(),
|
|
93
|
+
);
|
|
77
94
|
return reader.read();
|
|
78
95
|
}
|
|
79
96
|
|
|
@@ -81,7 +98,9 @@ function read(
|
|
|
81
98
|
* @private
|
|
82
99
|
*/
|
|
83
100
|
class RelayReader {
|
|
101
|
+
_clientEdgeTraversalPath: Array<ClientEdgeTraversalInfo | null>;
|
|
84
102
|
_isMissingData: boolean;
|
|
103
|
+
_missingClientEdges: Array<MissingClientEdgeRequestInfo>;
|
|
85
104
|
_isWithinUnmatchedTypeRefinement: boolean;
|
|
86
105
|
_missingRequiredFields: ?MissingRequiredFields;
|
|
87
106
|
_owner: RequestDescriptor;
|
|
@@ -89,8 +108,19 @@ class RelayReader {
|
|
|
89
108
|
_seenRecords: DataIDSet;
|
|
90
109
|
_selector: SingularReaderSelector;
|
|
91
110
|
_variables: Variables;
|
|
111
|
+
_resolverCache: ResolverCache;
|
|
92
112
|
|
|
93
|
-
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 = [];
|
|
94
124
|
this._isMissingData = false;
|
|
95
125
|
this._isWithinUnmatchedTypeRefinement = false;
|
|
96
126
|
this._missingRequiredFields = null;
|
|
@@ -99,6 +129,7 @@ class RelayReader {
|
|
|
99
129
|
this._seenRecords = new Set();
|
|
100
130
|
this._selector = selector;
|
|
101
131
|
this._variables = selector.variables;
|
|
132
|
+
this._resolverCache = resolverCache;
|
|
102
133
|
}
|
|
103
134
|
|
|
104
135
|
read(): Snapshot {
|
|
@@ -122,7 +153,18 @@ class RelayReader {
|
|
|
122
153
|
// match, then no data is expected to be present.
|
|
123
154
|
if (isDataExpectedToBePresent && abstractKey == null && record != null) {
|
|
124
155
|
const recordType = RelayModernRecord.getType(record);
|
|
125
|
-
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
|
+
) {
|
|
126
168
|
isDataExpectedToBePresent = false;
|
|
127
169
|
}
|
|
128
170
|
}
|
|
@@ -131,12 +173,7 @@ class RelayReader {
|
|
|
131
173
|
// then data is only expected to be present if the record type is known to
|
|
132
174
|
// implement the interface. If we aren't sure whether the record implements
|
|
133
175
|
// the interface, that itself constitutes "expected" data being missing.
|
|
134
|
-
if (
|
|
135
|
-
isDataExpectedToBePresent &&
|
|
136
|
-
abstractKey != null &&
|
|
137
|
-
record != null &&
|
|
138
|
-
RelayFeatureFlags.ENABLE_PRECISE_TYPE_REFINEMENT
|
|
139
|
-
) {
|
|
176
|
+
if (isDataExpectedToBePresent && abstractKey != null && record != null) {
|
|
140
177
|
const recordType = RelayModernRecord.getType(record);
|
|
141
178
|
const typeID = generateTypeID(recordType);
|
|
142
179
|
const typeRecord = this._recordSource.get(typeID);
|
|
@@ -158,12 +195,36 @@ class RelayReader {
|
|
|
158
195
|
return {
|
|
159
196
|
data,
|
|
160
197
|
isMissingData: this._isMissingData && isDataExpectedToBePresent,
|
|
198
|
+
missingClientEdges:
|
|
199
|
+
RelayFeatureFlags.ENABLE_CLIENT_EDGES && this._missingClientEdges.length
|
|
200
|
+
? this._missingClientEdges
|
|
201
|
+
: null,
|
|
161
202
|
seenRecords: this._seenRecords,
|
|
162
203
|
selector: this._selector,
|
|
163
204
|
missingRequiredFields: this._missingRequiredFields,
|
|
164
205
|
};
|
|
165
206
|
}
|
|
166
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
|
+
|
|
167
228
|
_traverse(
|
|
168
229
|
node: ReaderNode,
|
|
169
230
|
dataID: DataID,
|
|
@@ -173,7 +234,7 @@ class RelayReader {
|
|
|
173
234
|
this._seenRecords.add(dataID);
|
|
174
235
|
if (record == null) {
|
|
175
236
|
if (record === undefined) {
|
|
176
|
-
this.
|
|
237
|
+
this._markDataAsMissing();
|
|
177
238
|
}
|
|
178
239
|
return record;
|
|
179
240
|
}
|
|
@@ -262,7 +323,9 @@ class RelayReader {
|
|
|
262
323
|
}
|
|
263
324
|
break;
|
|
264
325
|
case CONDITION:
|
|
265
|
-
const conditionValue =
|
|
326
|
+
const conditionValue = Boolean(
|
|
327
|
+
this._getVariableValue(selection.condition),
|
|
328
|
+
);
|
|
266
329
|
if (conditionValue === selection.passingValue) {
|
|
267
330
|
const hasExpectedData = this._traverseSelections(
|
|
268
331
|
selection.selections,
|
|
@@ -289,15 +352,15 @@ class RelayReader {
|
|
|
289
352
|
return false;
|
|
290
353
|
}
|
|
291
354
|
}
|
|
292
|
-
} else
|
|
355
|
+
} else {
|
|
293
356
|
// Similar to the logic in read(): data is only expected to be present
|
|
294
357
|
// if the record is known to conform to the interface. If we don't know
|
|
295
358
|
// whether the type conforms or not, that constitutes missing data.
|
|
296
359
|
|
|
297
360
|
// store flags to reset after reading
|
|
298
361
|
const parentIsMissingData = this._isMissingData;
|
|
299
|
-
const parentIsWithinUnmatchedTypeRefinement =
|
|
300
|
-
._isWithinUnmatchedTypeRefinement;
|
|
362
|
+
const parentIsWithinUnmatchedTypeRefinement =
|
|
363
|
+
this._isWithinUnmatchedTypeRefinement;
|
|
301
364
|
|
|
302
365
|
const typeName = RelayModernRecord.getType(record);
|
|
303
366
|
const typeID = generateTypeID(typeName);
|
|
@@ -310,22 +373,26 @@ class RelayReader {
|
|
|
310
373
|
parentIsWithinUnmatchedTypeRefinement ||
|
|
311
374
|
implementsInterface === false;
|
|
312
375
|
this._traverseSelections(selection.selections, record, data);
|
|
313
|
-
this._isWithinUnmatchedTypeRefinement =
|
|
376
|
+
this._isWithinUnmatchedTypeRefinement =
|
|
377
|
+
parentIsWithinUnmatchedTypeRefinement;
|
|
314
378
|
|
|
315
379
|
if (implementsInterface === false) {
|
|
316
380
|
// Type known to not implement the interface, no data expected
|
|
317
381
|
this._isMissingData = parentIsMissingData;
|
|
318
382
|
} else if (implementsInterface == null) {
|
|
319
383
|
// Don't know if the type implements the interface or not
|
|
320
|
-
this.
|
|
384
|
+
this._markDataAsMissing();
|
|
321
385
|
}
|
|
322
|
-
} else {
|
|
323
|
-
// legacy behavior for abstract refinements: always read even
|
|
324
|
-
// if the type doesn't conform and don't reset isMissingData
|
|
325
|
-
this._traverseSelections(selection.selections, record, data);
|
|
326
386
|
}
|
|
327
387
|
break;
|
|
328
388
|
}
|
|
389
|
+
case RELAY_RESOLVER: {
|
|
390
|
+
if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
|
|
391
|
+
throw new Error('Relay Resolver fields are not yet supported.');
|
|
392
|
+
}
|
|
393
|
+
this._readResolverField(selection, record, data);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
329
396
|
case FRAGMENT_SPREAD:
|
|
330
397
|
this._createFragmentPointer(selection, record, data);
|
|
331
398
|
break;
|
|
@@ -333,17 +400,33 @@ class RelayReader {
|
|
|
333
400
|
this._readModuleImport(selection, record, data);
|
|
334
401
|
break;
|
|
335
402
|
case INLINE_DATA_FRAGMENT_SPREAD:
|
|
336
|
-
this.
|
|
403
|
+
this._createInlineDataOrResolverFragmentPointer(
|
|
404
|
+
selection,
|
|
405
|
+
record,
|
|
406
|
+
data,
|
|
407
|
+
);
|
|
337
408
|
break;
|
|
338
409
|
case DEFER:
|
|
339
410
|
case CLIENT_EXTENSION: {
|
|
340
411
|
const isMissingData = this._isMissingData;
|
|
412
|
+
const alreadyMissingClientEdges = this._missingClientEdges.length;
|
|
413
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
414
|
+
this._clientEdgeTraversalPath.push(null);
|
|
415
|
+
}
|
|
341
416
|
const hasExpectedData = this._traverseSelections(
|
|
342
417
|
selection.selections,
|
|
343
418
|
record,
|
|
344
419
|
data,
|
|
345
420
|
);
|
|
346
|
-
|
|
421
|
+
// The only case where we want to suspend due to missing data off of
|
|
422
|
+
// a client extension is if we reached a client edge that we might be
|
|
423
|
+
// able to fetch:
|
|
424
|
+
this._isMissingData =
|
|
425
|
+
isMissingData ||
|
|
426
|
+
this._missingClientEdges.length > alreadyMissingClientEdges;
|
|
427
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
428
|
+
this._clientEdgeTraversalPath.pop();
|
|
429
|
+
}
|
|
347
430
|
if (!hasExpectedData) {
|
|
348
431
|
return false;
|
|
349
432
|
}
|
|
@@ -367,6 +450,16 @@ class RelayReader {
|
|
|
367
450
|
throw new Error('Flight fields are not yet supported.');
|
|
368
451
|
}
|
|
369
452
|
break;
|
|
453
|
+
case ACTOR_CHANGE:
|
|
454
|
+
this._readActorChange(selection, record, data);
|
|
455
|
+
break;
|
|
456
|
+
case CLIENT_EDGE:
|
|
457
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
458
|
+
this._readClientEdge(selection, record, data);
|
|
459
|
+
} else {
|
|
460
|
+
throw new Error('Client edges are not yet supported.');
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
370
463
|
default:
|
|
371
464
|
(selection: empty);
|
|
372
465
|
invariant(
|
|
@@ -393,6 +486,12 @@ class RelayReader {
|
|
|
393
486
|
} else {
|
|
394
487
|
return this._readLink(selection.field, record, data);
|
|
395
488
|
}
|
|
489
|
+
case RELAY_RESOLVER:
|
|
490
|
+
if (!RelayFeatureFlags.ENABLE_RELAY_RESOLVERS) {
|
|
491
|
+
throw new Error('Relay Resolver fields are not yet supported.');
|
|
492
|
+
}
|
|
493
|
+
this._readResolverField(selection.field, record, data);
|
|
494
|
+
break;
|
|
396
495
|
default:
|
|
397
496
|
(selection.field.kind: empty);
|
|
398
497
|
invariant(
|
|
@@ -403,6 +502,145 @@ class RelayReader {
|
|
|
403
502
|
}
|
|
404
503
|
}
|
|
405
504
|
|
|
505
|
+
_readResolverField(
|
|
506
|
+
field: ReaderRelayResolver,
|
|
507
|
+
record: Record,
|
|
508
|
+
data: SelectorData,
|
|
509
|
+
): void {
|
|
510
|
+
const {resolverModule, fragment} = field;
|
|
511
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
512
|
+
const resolverID = ClientID.generateClientID(
|
|
513
|
+
RelayModernRecord.getDataID(record),
|
|
514
|
+
storageKey,
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
// Found when reading the resolver fragment, which can happen either when
|
|
518
|
+
// evaluating the resolver and it calls readFragment, or when checking if the
|
|
519
|
+
// inputs have changed since a previous evaluation:
|
|
520
|
+
let fragmentValue;
|
|
521
|
+
let fragmentReaderSelector;
|
|
522
|
+
const fragmentSeenRecordIDs = new Set();
|
|
523
|
+
|
|
524
|
+
const getDataForResolverFragment = singularReaderSelector => {
|
|
525
|
+
if (fragmentValue != null) {
|
|
526
|
+
// It was already read when checking for input staleness; no need to read it again.
|
|
527
|
+
// Note that the variables like fragmentSeenRecordIDs in the outer closure will have
|
|
528
|
+
// already been set and will still be used in this case.
|
|
529
|
+
return fragmentValue;
|
|
530
|
+
}
|
|
531
|
+
fragmentReaderSelector = singularReaderSelector;
|
|
532
|
+
const existingSeenRecords = this._seenRecords;
|
|
533
|
+
try {
|
|
534
|
+
this._seenRecords = fragmentSeenRecordIDs;
|
|
535
|
+
const resolverFragmentData = {};
|
|
536
|
+
this._createInlineDataOrResolverFragmentPointer(
|
|
537
|
+
singularReaderSelector.node,
|
|
538
|
+
record,
|
|
539
|
+
resolverFragmentData,
|
|
540
|
+
);
|
|
541
|
+
fragmentValue = resolverFragmentData[FRAGMENTS_KEY]?.[fragment.name];
|
|
542
|
+
invariant(
|
|
543
|
+
typeof fragmentValue === 'object' && fragmentValue !== null,
|
|
544
|
+
`Expected reader data to contain a __fragments property with a property for the fragment named ${fragment.name}, but it is missing.`,
|
|
545
|
+
);
|
|
546
|
+
return fragmentValue;
|
|
547
|
+
} finally {
|
|
548
|
+
this._seenRecords = existingSeenRecords;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
const resolverContext = {getDataForResolverFragment};
|
|
552
|
+
|
|
553
|
+
const [result, seenRecord] = this._resolverCache.readFromCacheOrEvaluate(
|
|
554
|
+
record,
|
|
555
|
+
field,
|
|
556
|
+
this._variables,
|
|
557
|
+
() => {
|
|
558
|
+
const key = {
|
|
559
|
+
__id: RelayModernRecord.getDataID(record),
|
|
560
|
+
__fragmentOwner: this._owner,
|
|
561
|
+
__fragments: {
|
|
562
|
+
[fragment.name]: {}, // Arguments to this fragment; not yet supported.
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
return withResolverContext(resolverContext, () => {
|
|
566
|
+
// $FlowFixMe[prop-missing] - resolver module's type signature is a lie
|
|
567
|
+
const resolverResult = resolverModule(key);
|
|
568
|
+
return {
|
|
569
|
+
resolverResult,
|
|
570
|
+
fragmentValue,
|
|
571
|
+
resolverID,
|
|
572
|
+
seenRecordIDs: fragmentSeenRecordIDs,
|
|
573
|
+
readerSelector: fragmentReaderSelector,
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
},
|
|
577
|
+
getDataForResolverFragment,
|
|
578
|
+
);
|
|
579
|
+
if (seenRecord != null) {
|
|
580
|
+
this._seenRecords.add(seenRecord);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const applicationName = field.alias ?? field.name;
|
|
584
|
+
data[applicationName] = result;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
_readClientEdge(
|
|
588
|
+
field: ReaderClientEdge,
|
|
589
|
+
record: Record,
|
|
590
|
+
data: SelectorData,
|
|
591
|
+
): void {
|
|
592
|
+
const backingField = field.backingField;
|
|
593
|
+
|
|
594
|
+
// Because ReaderClientExtension doesn't have `alias` or `name` and so I don't know
|
|
595
|
+
// how to get its applicationName or storageKey yet:
|
|
596
|
+
invariant(
|
|
597
|
+
backingField.kind !== 'ClientExtension',
|
|
598
|
+
'Client extension client edges are not yet implemented.',
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
const applicationName = backingField.alias ?? backingField.name;
|
|
602
|
+
|
|
603
|
+
const backingFieldData = {};
|
|
604
|
+
this._traverseSelections([backingField], record, backingFieldData);
|
|
605
|
+
const destinationDataID = backingFieldData[applicationName];
|
|
606
|
+
|
|
607
|
+
if (destinationDataID == null) {
|
|
608
|
+
data[applicationName] = destinationDataID;
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
invariant(
|
|
613
|
+
typeof destinationDataID === 'string',
|
|
614
|
+
'Plural client edges not are yet implemented',
|
|
615
|
+
); // FIXME support plural
|
|
616
|
+
|
|
617
|
+
// Not wrapping the push/pop in a try/finally because if we throw, the
|
|
618
|
+
// Reader object is not usable after that anyway.
|
|
619
|
+
this._clientEdgeTraversalPath.push({
|
|
620
|
+
readerClientEdge: field,
|
|
621
|
+
clientEdgeDestinationID: destinationDataID,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const prevData = data[applicationName];
|
|
625
|
+
invariant(
|
|
626
|
+
prevData == null || typeof prevData === 'object',
|
|
627
|
+
'RelayReader(): Expected data for field `%s` on record `%s` ' +
|
|
628
|
+
'to be an object, got `%s`.',
|
|
629
|
+
applicationName,
|
|
630
|
+
RelayModernRecord.getDataID(record),
|
|
631
|
+
prevData,
|
|
632
|
+
);
|
|
633
|
+
const value = this._traverse(
|
|
634
|
+
field.linkedField,
|
|
635
|
+
destinationDataID,
|
|
636
|
+
// $FlowFixMe[incompatible-variance]
|
|
637
|
+
prevData,
|
|
638
|
+
);
|
|
639
|
+
data[applicationName] = value;
|
|
640
|
+
|
|
641
|
+
this._clientEdgeTraversalPath.pop();
|
|
642
|
+
}
|
|
643
|
+
|
|
406
644
|
_readFlightField(
|
|
407
645
|
field: ReaderFlightField,
|
|
408
646
|
record: Record,
|
|
@@ -410,14 +648,12 @@ class RelayReader {
|
|
|
410
648
|
): ?mixed {
|
|
411
649
|
const applicationName = field.alias ?? field.name;
|
|
412
650
|
const storageKey = getStorageKey(field, this._variables);
|
|
413
|
-
const reactFlightClientResponseRecordID =
|
|
414
|
-
record,
|
|
415
|
-
storageKey,
|
|
416
|
-
);
|
|
651
|
+
const reactFlightClientResponseRecordID =
|
|
652
|
+
RelayModernRecord.getLinkedRecordID(record, storageKey);
|
|
417
653
|
if (reactFlightClientResponseRecordID == null) {
|
|
418
654
|
data[applicationName] = reactFlightClientResponseRecordID;
|
|
419
655
|
if (reactFlightClientResponseRecordID === undefined) {
|
|
420
|
-
this.
|
|
656
|
+
this._markDataAsMissing();
|
|
421
657
|
}
|
|
422
658
|
return reactFlightClientResponseRecordID;
|
|
423
659
|
}
|
|
@@ -428,7 +664,7 @@ class RelayReader {
|
|
|
428
664
|
if (reactFlightClientResponseRecord == null) {
|
|
429
665
|
data[applicationName] = reactFlightClientResponseRecord;
|
|
430
666
|
if (reactFlightClientResponseRecord === undefined) {
|
|
431
|
-
this.
|
|
667
|
+
this._markDataAsMissing();
|
|
432
668
|
}
|
|
433
669
|
return reactFlightClientResponseRecord;
|
|
434
670
|
}
|
|
@@ -448,7 +684,7 @@ class RelayReader {
|
|
|
448
684
|
const storageKey = getStorageKey(field, this._variables);
|
|
449
685
|
const value = RelayModernRecord.getValue(record, storageKey);
|
|
450
686
|
if (value === undefined) {
|
|
451
|
-
this.
|
|
687
|
+
this._markDataAsMissing();
|
|
452
688
|
}
|
|
453
689
|
data[applicationName] = value;
|
|
454
690
|
return value;
|
|
@@ -465,7 +701,7 @@ class RelayReader {
|
|
|
465
701
|
if (linkedID == null) {
|
|
466
702
|
data[applicationName] = linkedID;
|
|
467
703
|
if (linkedID === undefined) {
|
|
468
|
-
this.
|
|
704
|
+
this._markDataAsMissing();
|
|
469
705
|
}
|
|
470
706
|
return linkedID;
|
|
471
707
|
}
|
|
@@ -485,6 +721,42 @@ class RelayReader {
|
|
|
485
721
|
return value;
|
|
486
722
|
}
|
|
487
723
|
|
|
724
|
+
_readActorChange(
|
|
725
|
+
field: ReaderActorChange,
|
|
726
|
+
record: Record,
|
|
727
|
+
data: SelectorData,
|
|
728
|
+
): ?mixed {
|
|
729
|
+
const applicationName = field.alias ?? field.name;
|
|
730
|
+
const storageKey = getStorageKey(field, this._variables);
|
|
731
|
+
const externalRef = RelayModernRecord.getActorLinkedRecordID(
|
|
732
|
+
record,
|
|
733
|
+
storageKey,
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
if (externalRef == null) {
|
|
737
|
+
data[applicationName] = externalRef;
|
|
738
|
+
if (externalRef === undefined) {
|
|
739
|
+
this._markDataAsMissing();
|
|
740
|
+
}
|
|
741
|
+
return data[applicationName];
|
|
742
|
+
}
|
|
743
|
+
const [actorIdentifier, dataID] = externalRef;
|
|
744
|
+
|
|
745
|
+
const fragmentRef = {};
|
|
746
|
+
this._createFragmentPointer(
|
|
747
|
+
field.fragmentSpread,
|
|
748
|
+
{
|
|
749
|
+
__id: dataID,
|
|
750
|
+
},
|
|
751
|
+
fragmentRef,
|
|
752
|
+
);
|
|
753
|
+
data[applicationName] = {
|
|
754
|
+
__fragmentRef: fragmentRef,
|
|
755
|
+
__viewer: actorIdentifier,
|
|
756
|
+
};
|
|
757
|
+
return data[applicationName];
|
|
758
|
+
}
|
|
759
|
+
|
|
488
760
|
_readPluralLink(
|
|
489
761
|
field: ReaderLinkedField,
|
|
490
762
|
record: Record,
|
|
@@ -497,7 +769,7 @@ class RelayReader {
|
|
|
497
769
|
if (linkedIDs == null) {
|
|
498
770
|
data[applicationName] = linkedIDs;
|
|
499
771
|
if (linkedIDs === undefined) {
|
|
500
|
-
this.
|
|
772
|
+
this._markDataAsMissing();
|
|
501
773
|
}
|
|
502
774
|
return linkedIDs;
|
|
503
775
|
}
|
|
@@ -515,7 +787,7 @@ class RelayReader {
|
|
|
515
787
|
linkedIDs.forEach((linkedID, nextIndex) => {
|
|
516
788
|
if (linkedID == null) {
|
|
517
789
|
if (linkedID === undefined) {
|
|
518
|
-
this.
|
|
790
|
+
this._markDataAsMissing();
|
|
519
791
|
}
|
|
520
792
|
// $FlowFixMe[cannot-write]
|
|
521
793
|
linkedArray[nextIndex] = linkedID;
|
|
@@ -553,7 +825,7 @@ class RelayReader {
|
|
|
553
825
|
const component = RelayModernRecord.getValue(record, componentKey);
|
|
554
826
|
if (component == null) {
|
|
555
827
|
if (component === undefined) {
|
|
556
|
-
this.
|
|
828
|
+
this._markDataAsMissing();
|
|
557
829
|
}
|
|
558
830
|
return;
|
|
559
831
|
}
|
|
@@ -567,7 +839,7 @@ class RelayReader {
|
|
|
567
839
|
{
|
|
568
840
|
kind: 'FragmentSpread',
|
|
569
841
|
name: moduleImport.fragmentName,
|
|
570
|
-
args:
|
|
842
|
+
args: moduleImport.args,
|
|
571
843
|
},
|
|
572
844
|
record,
|
|
573
845
|
data,
|
|
@@ -598,16 +870,23 @@ class RelayReader {
|
|
|
598
870
|
? getArgumentValues(fragmentSpread.args, this._variables)
|
|
599
871
|
: {};
|
|
600
872
|
data[FRAGMENT_OWNER_KEY] = this._owner;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
873
|
+
data[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] =
|
|
874
|
+
this._isWithinUnmatchedTypeRefinement;
|
|
875
|
+
|
|
876
|
+
if (RelayFeatureFlags.ENABLE_CLIENT_EDGES) {
|
|
877
|
+
if (
|
|
878
|
+
this._clientEdgeTraversalPath.length > 0 &&
|
|
879
|
+
this._clientEdgeTraversalPath[
|
|
880
|
+
this._clientEdgeTraversalPath.length - 1
|
|
881
|
+
] !== null
|
|
882
|
+
) {
|
|
883
|
+
data[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath];
|
|
884
|
+
}
|
|
606
885
|
}
|
|
607
886
|
}
|
|
608
887
|
|
|
609
|
-
|
|
610
|
-
|
|
888
|
+
_createInlineDataOrResolverFragmentPointer(
|
|
889
|
+
fragmentSpreadOrFragment: ReaderInlineDataFragmentSpread | ReaderFragment,
|
|
611
890
|
record: Record,
|
|
612
891
|
data: SelectorData,
|
|
613
892
|
): void {
|
|
@@ -625,12 +904,12 @@ class RelayReader {
|
|
|
625
904
|
}
|
|
626
905
|
const inlineData = {};
|
|
627
906
|
this._traverseSelections(
|
|
628
|
-
|
|
907
|
+
fragmentSpreadOrFragment.selections,
|
|
629
908
|
record,
|
|
630
909
|
inlineData,
|
|
631
910
|
);
|
|
632
911
|
// $FlowFixMe[cannot-write] - writing into read-only field
|
|
633
|
-
fragmentPointers[
|
|
912
|
+
fragmentPointers[fragmentSpreadOrFragment.name] = inlineData;
|
|
634
913
|
}
|
|
635
914
|
}
|
|
636
915
|
|