relay-runtime 11.0.1 → 13.0.0-rc.1
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 -17
- package/handlers/connection/MutationHandlers.js.flow +7 -11
- package/index.js +1 -1
- package/index.js.flow +60 -36
- package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
- package/lib/handlers/connection/ConnectionHandler.js +13 -19
- package/lib/handlers/connection/MutationHandlers.js +4 -7
- 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 +10 -5
- package/lib/network/ConvertToExecuteFunction.js +2 -1
- package/lib/network/RelayNetwork.js +3 -2
- package/lib/network/RelayQueryResponseCache.js +21 -5
- 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 +123 -54
- package/lib/store/{RelayModernQueryExecutor.js → OperationExecutor.js} +518 -200
- package/lib/store/RelayConcreteVariables.js +26 -8
- package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
- package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
- package/lib/store/RelayModernEnvironment.js +175 -240
- 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 +56 -28
- package/lib/store/RelayOperationTracker.js +34 -24
- package/lib/store/RelayPublishQueue.js +41 -13
- package/lib/store/RelayReader.js +288 -48
- package/lib/store/RelayRecordSource.js +87 -3
- package/lib/store/RelayReferenceMarker.js +34 -22
- package/lib/store/RelayResponseNormalizer.js +211 -110
- package/lib/store/RelayStoreReactFlightUtils.js +4 -10
- package/lib/store/RelayStoreSubscriptions.js +14 -9
- package/lib/store/RelayStoreUtils.js +12 -7
- 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/readInlineData.js +6 -2
- package/lib/subscription/requestSubscription.js +34 -25
- package/lib/util/RelayConcreteNode.js +3 -0
- package/lib/util/RelayFeatureFlags.js +10 -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/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 +26 -16
- package/network/ConvertToExecuteFunction.js.flow +2 -2
- package/network/RelayNetwork.js.flow +4 -5
- package/network/RelayNetworkTypes.js.flow +5 -4
- package/network/RelayObservable.js.flow +1 -1
- package/network/RelayQueryResponseCache.js.flow +34 -21
- 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 +141 -59
- package/store/{RelayModernQueryExecutor.js.flow → OperationExecutor.js.flow} +605 -303
- package/store/RelayConcreteVariables.js.flow +27 -8
- package/store/RelayExperimentalGraphResponseHandler.js.flow +124 -0
- package/store/RelayExperimentalGraphResponseTransform.js.flow +475 -0
- package/store/RelayModernEnvironment.js.flow +173 -240
- 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 +66 -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 -73
- package/store/RelayRecordSource.js.flow +72 -6
- package/store/RelayReferenceMarker.js.flow +40 -26
- package/store/RelayResponseNormalizer.js.flow +258 -99
- package/store/RelayStoreReactFlightUtils.js.flow +4 -11
- package/store/RelayStoreSubscriptions.js.flow +19 -11
- package/store/RelayStoreTypes.js.flow +209 -43
- package/store/RelayStoreUtils.js.flow +24 -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/normalizeRelayPayload.js.flow +6 -7
- package/store/readInlineData.js.flow +7 -8
- package/subscription/requestSubscription.js.flow +53 -41
- package/util/NormalizationNode.js.flow +10 -3
- package/util/ReaderNode.js.flow +38 -2
- package/util/RelayConcreteNode.js.flow +5 -0
- package/util/RelayFeatureFlags.js.flow +24 -10
- package/util/RelayProfiler.js.flow +22 -194
- package/util/RelayReplaySubject.js.flow +9 -9
- package/util/RelayRuntimeTypes.js.flow +72 -3
- package/util/StringInterner.js.flow +69 -0
- package/util/createPayloadFor3DField.js.flow +3 -3
- 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 +1 -0
- 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
|
@@ -13,37 +13,22 @@
|
|
|
13
13
|
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
18
|
-
const RelayModernRecord = require('./RelayModernRecord');
|
|
19
|
-
const RelayObservable = require('../network/RelayObservable');
|
|
20
|
-
const RelayRecordSource = require('./RelayRecordSource');
|
|
21
|
-
const RelayResponseNormalizer = require('./RelayResponseNormalizer');
|
|
22
|
-
|
|
23
|
-
const getOperation = require('../util/getOperation');
|
|
24
|
-
const invariant = require('invariant');
|
|
25
|
-
const stableCopy = require('../util/stableCopy');
|
|
26
|
-
const warning = require('warning');
|
|
27
|
-
|
|
28
|
-
const {generateClientID, generateUniqueClientID} = require('./ClientID');
|
|
29
|
-
const {
|
|
30
|
-
createNormalizationSelector,
|
|
31
|
-
createReaderSelector,
|
|
32
|
-
} = require('./RelayModernSelector');
|
|
33
|
-
const {ROOT_TYPE, TYPENAME_KEY, getStorageKey} = require('./RelayStoreUtils');
|
|
34
|
-
|
|
16
|
+
import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
|
|
35
17
|
import type {
|
|
36
18
|
GraphQLResponse,
|
|
37
|
-
GraphQLSingularResponse,
|
|
38
19
|
GraphQLResponseWithData,
|
|
20
|
+
GraphQLSingularResponse,
|
|
21
|
+
ReactFlightServerTree,
|
|
39
22
|
} from '../network/RelayNetworkTypes';
|
|
40
23
|
import type {Sink, Subscription} from '../network/RelayObservable';
|
|
41
24
|
import type {
|
|
42
25
|
DeferPlaceholder,
|
|
43
|
-
|
|
26
|
+
FollowupPayload,
|
|
44
27
|
HandleFieldPayload,
|
|
45
28
|
IncrementalDataPlaceholder,
|
|
29
|
+
LogFunction,
|
|
46
30
|
ModuleImportPayload,
|
|
31
|
+
MutationParameters,
|
|
47
32
|
NormalizationSelector,
|
|
48
33
|
OperationDescriptor,
|
|
49
34
|
OperationLoader,
|
|
@@ -51,10 +36,12 @@ import type {
|
|
|
51
36
|
OptimisticResponseConfig,
|
|
52
37
|
OptimisticUpdate,
|
|
53
38
|
PublishQueue,
|
|
39
|
+
ReactFlightClientResponse,
|
|
54
40
|
ReactFlightPayloadDeserializer,
|
|
55
41
|
ReactFlightServerErrorHandler,
|
|
56
42
|
Record,
|
|
57
43
|
RelayResponsePayload,
|
|
44
|
+
RequestDescriptor,
|
|
58
45
|
SelectorStoreUpdater,
|
|
59
46
|
Store,
|
|
60
47
|
StreamPlaceholder,
|
|
@@ -66,28 +53,50 @@ import type {
|
|
|
66
53
|
NormalizationSelectableNode,
|
|
67
54
|
NormalizationSplitOperation,
|
|
68
55
|
} from '../util/NormalizationNode';
|
|
69
|
-
import type {DataID,
|
|
56
|
+
import type {DataID, Disposable, Variables} from '../util/RelayRuntimeTypes';
|
|
70
57
|
import type {GetDataID} from './RelayResponseNormalizer';
|
|
71
58
|
import type {NormalizationOptions} from './RelayResponseNormalizer';
|
|
72
59
|
|
|
73
|
-
|
|
60
|
+
const RelayObservable = require('../network/RelayObservable');
|
|
61
|
+
const generateID = require('../util/generateID');
|
|
62
|
+
const getOperation = require('../util/getOperation');
|
|
63
|
+
const RelayError = require('../util/RelayError');
|
|
64
|
+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
65
|
+
const stableCopy = require('../util/stableCopy');
|
|
66
|
+
const withDuration = require('../util/withDuration');
|
|
67
|
+
const {generateClientID, generateUniqueClientID} = require('./ClientID');
|
|
68
|
+
const {getLocalVariables} = require('./RelayConcreteVariables');
|
|
69
|
+
const RelayModernRecord = require('./RelayModernRecord');
|
|
70
|
+
const {
|
|
71
|
+
createNormalizationSelector,
|
|
72
|
+
createReaderSelector,
|
|
73
|
+
} = require('./RelayModernSelector');
|
|
74
|
+
const RelayRecordSource = require('./RelayRecordSource');
|
|
75
|
+
const RelayResponseNormalizer = require('./RelayResponseNormalizer');
|
|
76
|
+
const {ROOT_TYPE, TYPENAME_KEY, getStorageKey} = require('./RelayStoreUtils');
|
|
77
|
+
const invariant = require('invariant');
|
|
78
|
+
const warning = require('warning');
|
|
79
|
+
|
|
80
|
+
export type ExecuteConfig<TMutation: MutationParameters> = {|
|
|
81
|
+
+actorIdentifier: ActorIdentifier,
|
|
74
82
|
+getDataID: GetDataID,
|
|
75
|
-
+
|
|
83
|
+
+getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue,
|
|
84
|
+
+getStore: (actorIdentifier: ActorIdentifier) => Store,
|
|
85
|
+
+isClientPayload?: boolean,
|
|
76
86
|
+operation: OperationDescriptor,
|
|
77
87
|
+operationExecutions: Map<string, ActiveState>,
|
|
78
88
|
+operationLoader: ?OperationLoader,
|
|
79
|
-
+operationTracker
|
|
80
|
-
+optimisticConfig: ?OptimisticResponseConfig
|
|
81
|
-
+publishQueue: PublishQueue,
|
|
89
|
+
+operationTracker: OperationTracker,
|
|
90
|
+
+optimisticConfig: ?OptimisticResponseConfig<TMutation>,
|
|
82
91
|
+reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
|
|
83
92
|
+reactFlightServerErrorHandler?: ?ReactFlightServerErrorHandler,
|
|
84
93
|
+scheduler?: ?TaskScheduler,
|
|
94
|
+
+shouldProcessClientComponents?: ?boolean,
|
|
85
95
|
+sink: Sink<GraphQLResponse>,
|
|
86
96
|
+source: RelayObservable<GraphQLResponse>,
|
|
87
|
-
+
|
|
88
|
-
+updater?: ?SelectorStoreUpdater
|
|
89
|
-
+
|
|
90
|
-
+shouldProcessClientComponents?: ?boolean,
|
|
97
|
+
+treatMissingFieldsAsNull: boolean,
|
|
98
|
+
+updater?: ?SelectorStoreUpdater<TMutation['response']>,
|
|
99
|
+
+log: LogFunction,
|
|
91
100
|
|};
|
|
92
101
|
|
|
93
102
|
export type ActiveState = 'active' | 'inactive';
|
|
@@ -115,7 +124,9 @@ type IncrementalGraphQLResponse = {|
|
|
|
115
124
|
response: GraphQLResponseWithData,
|
|
116
125
|
|};
|
|
117
126
|
|
|
118
|
-
function execute
|
|
127
|
+
function execute<TMutation: MutationParameters>(
|
|
128
|
+
config: ExecuteConfig<TMutation>,
|
|
129
|
+
): Executor<TMutation> {
|
|
119
130
|
return new Executor(config);
|
|
120
131
|
}
|
|
121
132
|
|
|
@@ -124,20 +135,23 @@ function execute(config: ExecuteConfig): Executor {
|
|
|
124
135
|
* including optimistic payloads, standard payloads, resolution of match
|
|
125
136
|
* dependencies, etc.
|
|
126
137
|
*/
|
|
127
|
-
class Executor {
|
|
138
|
+
class Executor<TMutation: MutationParameters> {
|
|
139
|
+
_actorIdentifier: ActorIdentifier;
|
|
128
140
|
_getDataID: GetDataID;
|
|
129
141
|
_treatMissingFieldsAsNull: boolean;
|
|
130
142
|
_incrementalPayloadsPending: boolean;
|
|
131
143
|
_incrementalResults: Map<Label, Map<PathKey, IncrementalResults>>;
|
|
144
|
+
_log: LogFunction;
|
|
145
|
+
_executeId: number;
|
|
132
146
|
_nextSubscriptionId: number;
|
|
133
147
|
_operation: OperationDescriptor;
|
|
134
148
|
_operationExecutions: Map<string, ActiveState>;
|
|
135
149
|
_operationLoader: ?OperationLoader;
|
|
136
|
-
_operationTracker:
|
|
150
|
+
_operationTracker: OperationTracker;
|
|
137
151
|
_operationUpdateEpochs: Map<string, number>;
|
|
138
|
-
_optimisticUpdates: null | Array<OptimisticUpdate
|
|
152
|
+
_optimisticUpdates: null | Array<OptimisticUpdate<TMutation>>;
|
|
139
153
|
_pendingModulePayloadsCount: number;
|
|
140
|
-
|
|
154
|
+
+_getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue;
|
|
141
155
|
_reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
|
|
142
156
|
_reactFlightServerErrorHandler: ?ReactFlightServerErrorHandler;
|
|
143
157
|
_shouldProcessClientComponents: ?boolean;
|
|
@@ -148,36 +162,44 @@ class Executor {
|
|
|
148
162
|
{|+record: Record, +fieldPayloads: Array<HandleFieldPayload>|},
|
|
149
163
|
>;
|
|
150
164
|
_state: 'started' | 'loading_incremental' | 'loading_final' | 'completed';
|
|
151
|
-
|
|
165
|
+
+_getStore: (actorIdentifier: ActorIdentifier) => Store;
|
|
152
166
|
_subscriptions: Map<number, Subscription>;
|
|
153
|
-
_updater: ?SelectorStoreUpdater
|
|
154
|
-
|
|
167
|
+
_updater: ?SelectorStoreUpdater<TMutation['response']>;
|
|
168
|
+
_asyncStoreUpdateDisposable: ?Disposable;
|
|
169
|
+
_completeFns: Array<() => void>;
|
|
170
|
+
+_retainDisposables: Map<ActorIdentifier, Disposable>;
|
|
155
171
|
+_isClientPayload: boolean;
|
|
156
172
|
+_isSubscriptionOperation: boolean;
|
|
173
|
+
+_seenActors: Set<ActorIdentifier>;
|
|
157
174
|
|
|
158
175
|
constructor({
|
|
176
|
+
actorIdentifier,
|
|
177
|
+
getDataID,
|
|
178
|
+
getPublishQueue,
|
|
179
|
+
getStore,
|
|
180
|
+
isClientPayload,
|
|
159
181
|
operation,
|
|
160
182
|
operationExecutions,
|
|
161
183
|
operationLoader,
|
|
184
|
+
operationTracker,
|
|
162
185
|
optimisticConfig,
|
|
163
|
-
|
|
186
|
+
reactFlightPayloadDeserializer,
|
|
187
|
+
reactFlightServerErrorHandler,
|
|
164
188
|
scheduler,
|
|
189
|
+
shouldProcessClientComponents,
|
|
165
190
|
sink,
|
|
166
191
|
source,
|
|
167
|
-
store,
|
|
168
|
-
updater,
|
|
169
|
-
operationTracker,
|
|
170
192
|
treatMissingFieldsAsNull,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
shouldProcessClientComponents,
|
|
176
|
-
}: ExecuteConfig): void {
|
|
193
|
+
updater,
|
|
194
|
+
log,
|
|
195
|
+
}: ExecuteConfig<TMutation>): void {
|
|
196
|
+
this._actorIdentifier = actorIdentifier;
|
|
177
197
|
this._getDataID = getDataID;
|
|
178
198
|
this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
|
|
179
199
|
this._incrementalPayloadsPending = false;
|
|
180
200
|
this._incrementalResults = new Map();
|
|
201
|
+
this._log = log;
|
|
202
|
+
this._executeId = generateID();
|
|
181
203
|
this._nextSubscriptionId = 0;
|
|
182
204
|
this._operation = operation;
|
|
183
205
|
this._operationExecutions = operationExecutions;
|
|
@@ -186,12 +208,12 @@ class Executor {
|
|
|
186
208
|
this._operationUpdateEpochs = new Map();
|
|
187
209
|
this._optimisticUpdates = null;
|
|
188
210
|
this._pendingModulePayloadsCount = 0;
|
|
189
|
-
this.
|
|
211
|
+
this._getPublishQueue = getPublishQueue;
|
|
190
212
|
this._scheduler = scheduler;
|
|
191
213
|
this._sink = sink;
|
|
192
214
|
this._source = new Map();
|
|
193
215
|
this._state = 'started';
|
|
194
|
-
this.
|
|
216
|
+
this._getStore = getStore;
|
|
195
217
|
this._subscriptions = new Map();
|
|
196
218
|
this._updater = updater;
|
|
197
219
|
this._isClientPayload = isClientPayload === true;
|
|
@@ -200,6 +222,9 @@ class Executor {
|
|
|
200
222
|
this._isSubscriptionOperation =
|
|
201
223
|
this._operation.request.node.params.operationKind === 'subscription';
|
|
202
224
|
this._shouldProcessClientComponents = shouldProcessClientComponents;
|
|
225
|
+
this._retainDisposables = new Map();
|
|
226
|
+
this._seenActors = new Set();
|
|
227
|
+
this._completeFns = [];
|
|
203
228
|
|
|
204
229
|
const id = this._nextSubscriptionId++;
|
|
205
230
|
source.subscribe({
|
|
@@ -212,7 +237,16 @@ class Executor {
|
|
|
212
237
|
sink.error(error);
|
|
213
238
|
}
|
|
214
239
|
},
|
|
215
|
-
start: subscription =>
|
|
240
|
+
start: subscription => {
|
|
241
|
+
this._start(id, subscription);
|
|
242
|
+
this._log({
|
|
243
|
+
name: 'execute.start',
|
|
244
|
+
executeId: this._executeId,
|
|
245
|
+
params: this._operation.request.node.params,
|
|
246
|
+
variables: this._operation.request.variables,
|
|
247
|
+
cacheConfig: this._operation.request.cacheConfig ?? {},
|
|
248
|
+
});
|
|
249
|
+
},
|
|
216
250
|
});
|
|
217
251
|
|
|
218
252
|
if (optimisticConfig != null) {
|
|
@@ -242,18 +276,41 @@ class Executor {
|
|
|
242
276
|
if (optimisticUpdates !== null) {
|
|
243
277
|
this._optimisticUpdates = null;
|
|
244
278
|
optimisticUpdates.forEach(update =>
|
|
245
|
-
this.
|
|
279
|
+
this._getPublishQueueAndSaveActor().revertUpdate(update),
|
|
246
280
|
);
|
|
247
|
-
|
|
281
|
+
// OK: run revert on cancel
|
|
282
|
+
this._runPublishQueue();
|
|
248
283
|
}
|
|
249
284
|
this._incrementalResults.clear();
|
|
250
|
-
this.
|
|
251
|
-
|
|
252
|
-
this.
|
|
253
|
-
this._retainDisposable = null;
|
|
285
|
+
if (this._asyncStoreUpdateDisposable != null) {
|
|
286
|
+
this._asyncStoreUpdateDisposable.dispose();
|
|
287
|
+
this._asyncStoreUpdateDisposable = null;
|
|
254
288
|
}
|
|
289
|
+
this._completeFns = [];
|
|
290
|
+
this._completeOperationTracker();
|
|
291
|
+
this._disposeRetainedData();
|
|
255
292
|
}
|
|
256
293
|
|
|
294
|
+
_deserializeReactFlightPayloadWithLogging = (
|
|
295
|
+
tree: ReactFlightServerTree,
|
|
296
|
+
): ReactFlightClientResponse => {
|
|
297
|
+
const reactFlightPayloadDeserializer = this._reactFlightPayloadDeserializer;
|
|
298
|
+
invariant(
|
|
299
|
+
typeof reactFlightPayloadDeserializer === 'function',
|
|
300
|
+
'OperationExecutor: Expected reactFlightPayloadDeserializer to be available when calling _deserializeReactFlightPayloadWithLogging.',
|
|
301
|
+
);
|
|
302
|
+
const [duration, result] = withDuration(() => {
|
|
303
|
+
return reactFlightPayloadDeserializer(tree);
|
|
304
|
+
});
|
|
305
|
+
this._log({
|
|
306
|
+
name: 'execute.flight.payload_deserialize',
|
|
307
|
+
executeId: this._executeId,
|
|
308
|
+
operationName: this._operation.request.node.params.name,
|
|
309
|
+
duration,
|
|
310
|
+
});
|
|
311
|
+
return result;
|
|
312
|
+
};
|
|
313
|
+
|
|
257
314
|
_updateActiveState(): void {
|
|
258
315
|
let activeState;
|
|
259
316
|
switch (this._state) {
|
|
@@ -276,7 +333,7 @@ class Executor {
|
|
|
276
333
|
}
|
|
277
334
|
default:
|
|
278
335
|
(this._state: empty);
|
|
279
|
-
invariant(false, '
|
|
336
|
+
invariant(false, 'OperationExecutor: invalid executor state.');
|
|
280
337
|
}
|
|
281
338
|
this._operationExecutions.set(
|
|
282
339
|
this._operation.request.identifier,
|
|
@@ -313,12 +370,21 @@ class Executor {
|
|
|
313
370
|
if (this._subscriptions.size === 0) {
|
|
314
371
|
this.cancel();
|
|
315
372
|
this._sink.complete();
|
|
373
|
+
this._log({
|
|
374
|
+
name: 'execute.complete',
|
|
375
|
+
executeId: this._executeId,
|
|
376
|
+
});
|
|
316
377
|
}
|
|
317
378
|
}
|
|
318
379
|
|
|
319
380
|
_error(error: Error): void {
|
|
320
381
|
this.cancel();
|
|
321
382
|
this._sink.error(error);
|
|
383
|
+
this._log({
|
|
384
|
+
name: 'execute.error',
|
|
385
|
+
executeId: this._executeId,
|
|
386
|
+
error,
|
|
387
|
+
});
|
|
322
388
|
}
|
|
323
389
|
|
|
324
390
|
_start(id: number, subscription: Subscription): void {
|
|
@@ -329,8 +395,16 @@ class Executor {
|
|
|
329
395
|
// Handle a raw GraphQL response.
|
|
330
396
|
_next(_id: number, response: GraphQLResponse): void {
|
|
331
397
|
this._schedule(() => {
|
|
332
|
-
|
|
333
|
-
|
|
398
|
+
const [duration] = withDuration(() => {
|
|
399
|
+
this._handleNext(response);
|
|
400
|
+
this._maybeCompleteSubscriptionOperationTracking();
|
|
401
|
+
});
|
|
402
|
+
this._log({
|
|
403
|
+
name: 'execute.next',
|
|
404
|
+
executeId: this._executeId,
|
|
405
|
+
response,
|
|
406
|
+
duration,
|
|
407
|
+
});
|
|
334
408
|
});
|
|
335
409
|
}
|
|
336
410
|
|
|
@@ -374,7 +448,8 @@ class Executor {
|
|
|
374
448
|
error.stack;
|
|
375
449
|
throw error;
|
|
376
450
|
} else {
|
|
377
|
-
const responseWithData: GraphQLResponseWithData =
|
|
451
|
+
const responseWithData: GraphQLResponseWithData =
|
|
452
|
+
(response: $FlowFixMe);
|
|
378
453
|
results.push(responseWithData);
|
|
379
454
|
}
|
|
380
455
|
});
|
|
@@ -394,7 +469,10 @@ class Executor {
|
|
|
394
469
|
responsePart => responsePart.extensions?.isOptimistic === true,
|
|
395
470
|
)
|
|
396
471
|
) {
|
|
397
|
-
invariant(
|
|
472
|
+
invariant(
|
|
473
|
+
false,
|
|
474
|
+
'OperationExecutor: Optimistic responses cannot be batched.',
|
|
475
|
+
);
|
|
398
476
|
}
|
|
399
477
|
return false;
|
|
400
478
|
}
|
|
@@ -403,7 +481,7 @@ class Executor {
|
|
|
403
481
|
if (isOptimistic && this._state !== 'started') {
|
|
404
482
|
invariant(
|
|
405
483
|
false,
|
|
406
|
-
'
|
|
484
|
+
'OperationExecutor: optimistic payload received after server payload.',
|
|
407
485
|
);
|
|
408
486
|
}
|
|
409
487
|
if (isOptimistic) {
|
|
@@ -422,6 +500,7 @@ class Executor {
|
|
|
422
500
|
if (this._state === 'completed') {
|
|
423
501
|
return;
|
|
424
502
|
}
|
|
503
|
+
this._seenActors.clear();
|
|
425
504
|
|
|
426
505
|
const responses = Array.isArray(response) ? response : [response];
|
|
427
506
|
const responsesWithData = this._handleErrorResponse(responses);
|
|
@@ -445,10 +524,9 @@ class Executor {
|
|
|
445
524
|
return;
|
|
446
525
|
}
|
|
447
526
|
|
|
448
|
-
const [
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
] = partitionGraphQLResponses(responsesWithData);
|
|
527
|
+
const [nonIncrementalResponses, incrementalResponses] =
|
|
528
|
+
partitionGraphQLResponses(responsesWithData);
|
|
529
|
+
const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
|
|
452
530
|
|
|
453
531
|
// In theory this doesn't preserve the ordering of the batch.
|
|
454
532
|
// The idea is that a batch is always:
|
|
@@ -457,56 +535,96 @@ class Executor {
|
|
|
457
535
|
// The non-incremental payload can appear if the server sends a batch
|
|
458
536
|
// with the initial payload followed by some early-to-resolve incremental
|
|
459
537
|
// payloads (although, can that even happen?)
|
|
460
|
-
if (
|
|
538
|
+
if (hasNonIncrementalResponses) {
|
|
539
|
+
// For subscriptions, to avoid every new payload from overwriting existing
|
|
540
|
+
// data from previous payloads, assign a unique rootID for every new
|
|
541
|
+
// non-incremental payload.
|
|
542
|
+
if (this._isSubscriptionOperation) {
|
|
543
|
+
const nextID = generateUniqueClientID();
|
|
544
|
+
this._operation = {
|
|
545
|
+
request: this._operation.request,
|
|
546
|
+
fragment: createReaderSelector(
|
|
547
|
+
this._operation.fragment.node,
|
|
548
|
+
nextID,
|
|
549
|
+
this._operation.fragment.variables,
|
|
550
|
+
this._operation.fragment.owner,
|
|
551
|
+
),
|
|
552
|
+
root: createNormalizationSelector(
|
|
553
|
+
this._operation.root.node,
|
|
554
|
+
nextID,
|
|
555
|
+
this._operation.root.variables,
|
|
556
|
+
),
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
461
560
|
const payloadFollowups = this._processResponses(nonIncrementalResponses);
|
|
462
|
-
// Please note that we're passing `this._operation` to the publish
|
|
463
|
-
// queue here, which will later passed to the store (via notify)
|
|
464
|
-
// to indicate that this is an operation that caused the store to update
|
|
465
|
-
const updatedOwners = this._publishQueue.run(this._operation);
|
|
466
|
-
this._updateOperationTracker(updatedOwners);
|
|
467
561
|
this._processPayloadFollowups(payloadFollowups);
|
|
468
|
-
if (this._incrementalPayloadsPending && !this._retainDisposable) {
|
|
469
|
-
this._retainDisposable = this._store.retain(this._operation);
|
|
470
|
-
}
|
|
471
562
|
}
|
|
472
563
|
|
|
473
564
|
if (incrementalResponses.length > 0) {
|
|
474
|
-
const payloadFollowups =
|
|
475
|
-
incrementalResponses
|
|
476
|
-
|
|
477
|
-
// For the incremental case, we're only handling follow-up responses
|
|
478
|
-
// for already initiated operation (and we're not passing it to
|
|
479
|
-
// the run(...) call)
|
|
480
|
-
const updatedOwners = this._publishQueue.run();
|
|
481
|
-
this._updateOperationTracker(updatedOwners);
|
|
565
|
+
const payloadFollowups =
|
|
566
|
+
this._processIncrementalResponses(incrementalResponses);
|
|
567
|
+
|
|
482
568
|
this._processPayloadFollowups(payloadFollowups);
|
|
483
569
|
}
|
|
570
|
+
if (this._isSubscriptionOperation) {
|
|
571
|
+
// We attach the id to allow the `requestSubscription` to read from the store using
|
|
572
|
+
// the current id in its `onNext` callback
|
|
573
|
+
if (responsesWithData[0].extensions == null) {
|
|
574
|
+
// $FlowFixMe[cannot-write]
|
|
575
|
+
responsesWithData[0].extensions = {
|
|
576
|
+
__relay_subscription_root_id: this._operation.fragment.dataID,
|
|
577
|
+
};
|
|
578
|
+
} else {
|
|
579
|
+
responsesWithData[0].extensions.__relay_subscription_root_id =
|
|
580
|
+
this._operation.fragment.dataID;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// OK: run once after each new payload
|
|
585
|
+
// If we have non-incremental responses, we passing `this._operation` to
|
|
586
|
+
// the publish queue here, which will later be passed to the store (via
|
|
587
|
+
// notify) to indicate that this operation caused the store to update
|
|
588
|
+
const updatedOwners = this._runPublishQueue(
|
|
589
|
+
hasNonIncrementalResponses ? this._operation : undefined,
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
if (hasNonIncrementalResponses) {
|
|
593
|
+
if (this._incrementalPayloadsPending) {
|
|
594
|
+
this._retainData();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
this._updateOperationTracker(updatedOwners);
|
|
484
598
|
this._sink.next(response);
|
|
485
599
|
}
|
|
486
600
|
|
|
487
601
|
_processOptimisticResponse(
|
|
488
602
|
response: ?GraphQLResponseWithData,
|
|
489
|
-
updater: ?SelectorStoreUpdater
|
|
603
|
+
updater: ?SelectorStoreUpdater<TMutation['response']>,
|
|
490
604
|
treatMissingFieldsAsNull: boolean,
|
|
491
605
|
): void {
|
|
492
606
|
invariant(
|
|
493
607
|
this._optimisticUpdates === null,
|
|
494
|
-
'environment.execute: only support one optimistic response per ' +
|
|
608
|
+
'OperationExecutor: environment.execute: only support one optimistic response per ' +
|
|
495
609
|
'execute.',
|
|
496
610
|
);
|
|
497
611
|
if (response == null && updater == null) {
|
|
498
612
|
return;
|
|
499
613
|
}
|
|
500
|
-
const optimisticUpdates: Array<OptimisticUpdate
|
|
614
|
+
const optimisticUpdates: Array<OptimisticUpdate<TMutation>> = [];
|
|
501
615
|
if (response) {
|
|
502
616
|
const payload = normalizeResponse(
|
|
503
617
|
response,
|
|
504
618
|
this._operation.root,
|
|
505
619
|
ROOT_TYPE,
|
|
506
620
|
{
|
|
621
|
+
actorIdentifier: this._actorIdentifier,
|
|
507
622
|
getDataID: this._getDataID,
|
|
508
623
|
path: [],
|
|
509
|
-
reactFlightPayloadDeserializer:
|
|
624
|
+
reactFlightPayloadDeserializer:
|
|
625
|
+
this._reactFlightPayloadDeserializer != null
|
|
626
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
627
|
+
: null,
|
|
510
628
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
511
629
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
512
630
|
treatMissingFieldsAsNull,
|
|
@@ -526,7 +644,7 @@ class Executor {
|
|
|
526
644
|
errors: null,
|
|
527
645
|
fieldPayloads: null,
|
|
528
646
|
incrementalPlaceholders: null,
|
|
529
|
-
|
|
647
|
+
followupPayloads: null,
|
|
530
648
|
source: RelayRecordSource.create(),
|
|
531
649
|
isFinal: false,
|
|
532
650
|
},
|
|
@@ -534,59 +652,90 @@ class Executor {
|
|
|
534
652
|
});
|
|
535
653
|
}
|
|
536
654
|
this._optimisticUpdates = optimisticUpdates;
|
|
537
|
-
optimisticUpdates.forEach(update =>
|
|
538
|
-
|
|
655
|
+
optimisticUpdates.forEach(update =>
|
|
656
|
+
this._getPublishQueueAndSaveActor().applyUpdate(update),
|
|
657
|
+
);
|
|
658
|
+
// OK: only called on construction and when receiving an optimistic payload from network,
|
|
659
|
+
// which doesn't fall-through to the regular next() handling
|
|
660
|
+
this._runPublishQueue();
|
|
539
661
|
}
|
|
540
662
|
|
|
541
663
|
_processOptimisticFollowups(
|
|
542
664
|
payload: RelayResponsePayload,
|
|
543
|
-
optimisticUpdates: Array<OptimisticUpdate
|
|
665
|
+
optimisticUpdates: Array<OptimisticUpdate<TMutation>>,
|
|
544
666
|
): void {
|
|
545
|
-
if (payload.
|
|
546
|
-
const
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
667
|
+
if (payload.followupPayloads && payload.followupPayloads.length) {
|
|
668
|
+
const followupPayloads = payload.followupPayloads;
|
|
669
|
+
for (const followupPayload of followupPayloads) {
|
|
670
|
+
switch (followupPayload.kind) {
|
|
671
|
+
case 'ModuleImportPayload':
|
|
672
|
+
const operationLoader = this._expectOperationLoader();
|
|
673
|
+
const operation = operationLoader.get(
|
|
674
|
+
followupPayload.operationReference,
|
|
675
|
+
);
|
|
676
|
+
if (operation == null) {
|
|
677
|
+
this._processAsyncOptimisticModuleImport(followupPayload);
|
|
678
|
+
} else {
|
|
679
|
+
const moduleImportOptimisticUpdates =
|
|
680
|
+
this._processOptimisticModuleImport(operation, followupPayload);
|
|
681
|
+
optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
682
|
+
}
|
|
683
|
+
break;
|
|
684
|
+
case 'ActorPayload':
|
|
685
|
+
warning(
|
|
686
|
+
false,
|
|
687
|
+
'OperationExecutor: Unexpected optimistic ActorPayload. These updates are not supported.',
|
|
688
|
+
);
|
|
689
|
+
break;
|
|
690
|
+
default:
|
|
691
|
+
(followupPayload: empty);
|
|
692
|
+
invariant(
|
|
693
|
+
false,
|
|
694
|
+
'OperationExecutor: Unexpected followup kind `%s`. when processing optimistic updates.',
|
|
695
|
+
followupPayload.kind,
|
|
696
|
+
);
|
|
568
697
|
}
|
|
569
698
|
}
|
|
570
699
|
}
|
|
571
700
|
}
|
|
572
701
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
702
|
+
/**
|
|
703
|
+
* Normalize Data for @module payload, and actor-specific payload
|
|
704
|
+
*/
|
|
705
|
+
_normalizeFollowupPayload(
|
|
706
|
+
followupPayload: FollowupPayload,
|
|
707
|
+
normalizationNode: NormalizationSelectableNode,
|
|
576
708
|
) {
|
|
709
|
+
let variables;
|
|
710
|
+
if (
|
|
711
|
+
normalizationNode.kind === 'SplitOperation' &&
|
|
712
|
+
followupPayload.kind === 'ModuleImportPayload'
|
|
713
|
+
) {
|
|
714
|
+
variables = getLocalVariables(
|
|
715
|
+
followupPayload.variables,
|
|
716
|
+
normalizationNode.argumentDefinitions,
|
|
717
|
+
followupPayload.args,
|
|
718
|
+
);
|
|
719
|
+
} else {
|
|
720
|
+
variables = followupPayload.variables;
|
|
721
|
+
}
|
|
577
722
|
const selector = createNormalizationSelector(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
723
|
+
normalizationNode,
|
|
724
|
+
followupPayload.dataID,
|
|
725
|
+
variables,
|
|
581
726
|
);
|
|
582
727
|
return normalizeResponse(
|
|
583
|
-
{data:
|
|
728
|
+
{data: followupPayload.data},
|
|
584
729
|
selector,
|
|
585
|
-
|
|
730
|
+
followupPayload.typeName,
|
|
586
731
|
{
|
|
732
|
+
actorIdentifier: this._actorIdentifier,
|
|
587
733
|
getDataID: this._getDataID,
|
|
588
|
-
path:
|
|
589
|
-
reactFlightPayloadDeserializer:
|
|
734
|
+
path: followupPayload.path,
|
|
735
|
+
reactFlightPayloadDeserializer:
|
|
736
|
+
this._reactFlightPayloadDeserializer != null
|
|
737
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
738
|
+
: null,
|
|
590
739
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
591
740
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
592
741
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
@@ -597,10 +746,10 @@ class Executor {
|
|
|
597
746
|
_processOptimisticModuleImport(
|
|
598
747
|
normalizationRootNode: NormalizationRootNode,
|
|
599
748
|
moduleImportPayload: ModuleImportPayload,
|
|
600
|
-
): $ReadOnlyArray<OptimisticUpdate
|
|
749
|
+
): $ReadOnlyArray<OptimisticUpdate<TMutation>> {
|
|
601
750
|
const operation = getOperation(normalizationRootNode);
|
|
602
751
|
const optimisticUpdates = [];
|
|
603
|
-
const modulePayload = this.
|
|
752
|
+
const modulePayload = this._normalizeFollowupPayload(
|
|
604
753
|
moduleImportPayload,
|
|
605
754
|
operation,
|
|
606
755
|
);
|
|
@@ -615,41 +764,41 @@ class Executor {
|
|
|
615
764
|
}
|
|
616
765
|
|
|
617
766
|
_processAsyncOptimisticModuleImport(
|
|
618
|
-
operationLoader: OperationLoader,
|
|
619
767
|
moduleImportPayload: ModuleImportPayload,
|
|
620
768
|
): void {
|
|
621
|
-
|
|
769
|
+
this._expectOperationLoader()
|
|
622
770
|
.load(moduleImportPayload.operationReference)
|
|
623
771
|
.then(operation => {
|
|
624
772
|
if (operation == null || this._state !== 'started') {
|
|
625
773
|
return;
|
|
626
774
|
}
|
|
627
|
-
const moduleImportOptimisticUpdates =
|
|
628
|
-
operation,
|
|
629
|
-
moduleImportPayload,
|
|
630
|
-
);
|
|
775
|
+
const moduleImportOptimisticUpdates =
|
|
776
|
+
this._processOptimisticModuleImport(operation, moduleImportPayload);
|
|
631
777
|
moduleImportOptimisticUpdates.forEach(update =>
|
|
632
|
-
this.
|
|
778
|
+
this._getPublishQueueAndSaveActor().applyUpdate(update),
|
|
633
779
|
);
|
|
634
780
|
if (this._optimisticUpdates == null) {
|
|
635
781
|
warning(
|
|
636
782
|
false,
|
|
637
|
-
'
|
|
783
|
+
'OperationExecutor: Unexpected ModuleImport optimistic ' +
|
|
638
784
|
'update in operation %s.' +
|
|
639
785
|
this._operation.request.node.params.name,
|
|
640
786
|
);
|
|
641
787
|
} else {
|
|
642
788
|
this._optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
643
|
-
|
|
789
|
+
// OK: always have to run() after an module import resolves async
|
|
790
|
+
this._runPublishQueue();
|
|
644
791
|
}
|
|
645
792
|
});
|
|
646
793
|
}
|
|
647
794
|
|
|
648
|
-
_processResponses(
|
|
795
|
+
_processResponses(
|
|
796
|
+
responses: $ReadOnlyArray<GraphQLResponseWithData>,
|
|
797
|
+
): $ReadOnlyArray<RelayResponsePayload> {
|
|
649
798
|
if (this._optimisticUpdates !== null) {
|
|
650
|
-
this._optimisticUpdates.forEach(update =>
|
|
651
|
-
this.
|
|
652
|
-
);
|
|
799
|
+
this._optimisticUpdates.forEach(update => {
|
|
800
|
+
this._getPublishQueueAndSaveActor().revertUpdate(update);
|
|
801
|
+
});
|
|
653
802
|
this._optimisticUpdates = null;
|
|
654
803
|
}
|
|
655
804
|
|
|
@@ -662,19 +811,24 @@ class Executor {
|
|
|
662
811
|
this._operation.root,
|
|
663
812
|
ROOT_TYPE,
|
|
664
813
|
{
|
|
814
|
+
actorIdentifier: this._actorIdentifier,
|
|
665
815
|
getDataID: this._getDataID,
|
|
666
816
|
path: [],
|
|
667
|
-
reactFlightPayloadDeserializer:
|
|
817
|
+
reactFlightPayloadDeserializer:
|
|
818
|
+
this._reactFlightPayloadDeserializer != null
|
|
819
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
820
|
+
: null,
|
|
668
821
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
669
822
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
670
823
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
671
824
|
},
|
|
672
825
|
);
|
|
673
|
-
this.
|
|
826
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
674
827
|
this._operation,
|
|
675
828
|
relayPayload,
|
|
676
829
|
this._updater,
|
|
677
830
|
);
|
|
831
|
+
|
|
678
832
|
return relayPayload;
|
|
679
833
|
});
|
|
680
834
|
}
|
|
@@ -690,30 +844,30 @@ class Executor {
|
|
|
690
844
|
return;
|
|
691
845
|
}
|
|
692
846
|
payloads.forEach(payload => {
|
|
693
|
-
const {incrementalPlaceholders,
|
|
847
|
+
const {incrementalPlaceholders, followupPayloads, isFinal} = payload;
|
|
694
848
|
this._state = isFinal ? 'loading_final' : 'loading_incremental';
|
|
695
849
|
this._updateActiveState();
|
|
696
850
|
if (isFinal) {
|
|
697
851
|
this._incrementalPayloadsPending = false;
|
|
698
852
|
}
|
|
699
|
-
if (
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
moduleImportPayloads.forEach(moduleImportPayload => {
|
|
707
|
-
this._processModuleImportPayload(
|
|
708
|
-
moduleImportPayload,
|
|
709
|
-
operationLoader,
|
|
710
|
-
);
|
|
853
|
+
if (followupPayloads && followupPayloads.length !== 0) {
|
|
854
|
+
followupPayloads.forEach(followupPayload => {
|
|
855
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
856
|
+
this._actorIdentifier =
|
|
857
|
+
followupPayload.actorIdentifier ?? this._actorIdentifier;
|
|
858
|
+
this._processFollowupPayload(followupPayload);
|
|
859
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
711
860
|
});
|
|
712
861
|
}
|
|
862
|
+
|
|
713
863
|
if (incrementalPlaceholders && incrementalPlaceholders.length !== 0) {
|
|
714
864
|
this._incrementalPayloadsPending = this._state !== 'loading_final';
|
|
715
865
|
incrementalPlaceholders.forEach(incrementalPlaceholder => {
|
|
866
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
867
|
+
this._actorIdentifier =
|
|
868
|
+
incrementalPlaceholder.actorIdentifier ?? this._actorIdentifier;
|
|
716
869
|
this._processIncrementalPlaceholder(payload, incrementalPlaceholder);
|
|
870
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
717
871
|
});
|
|
718
872
|
|
|
719
873
|
if (this._isClientPayload || this._state === 'loading_final') {
|
|
@@ -745,8 +899,6 @@ class Executor {
|
|
|
745
899
|
}
|
|
746
900
|
});
|
|
747
901
|
if (relayPayloads.length > 0) {
|
|
748
|
-
const updatedOwners = this._publishQueue.run();
|
|
749
|
-
this._updateOperationTracker(updatedOwners);
|
|
750
902
|
this._processPayloadFollowups(relayPayloads);
|
|
751
903
|
}
|
|
752
904
|
}
|
|
@@ -764,23 +916,6 @@ class Executor {
|
|
|
764
916
|
) {
|
|
765
917
|
this._completeOperationTracker();
|
|
766
918
|
}
|
|
767
|
-
if (RelayFeatureFlags.ENABLE_UNIQUE_SUBSCRIPTION_ROOT) {
|
|
768
|
-
const nextID = generateUniqueClientID();
|
|
769
|
-
this._operation = {
|
|
770
|
-
request: this._operation.request,
|
|
771
|
-
fragment: createReaderSelector(
|
|
772
|
-
this._operation.fragment.node,
|
|
773
|
-
nextID,
|
|
774
|
-
this._operation.fragment.variables,
|
|
775
|
-
this._operation.fragment.owner,
|
|
776
|
-
),
|
|
777
|
-
root: createNormalizationSelector(
|
|
778
|
-
this._operation.root.node,
|
|
779
|
-
nextID,
|
|
780
|
-
this._operation.root.variables,
|
|
781
|
-
),
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
919
|
}
|
|
785
920
|
|
|
786
921
|
/**
|
|
@@ -790,73 +925,154 @@ class Executor {
|
|
|
790
925
|
* defer, stream, etc); these are handled by calling
|
|
791
926
|
* `_processPayloadFollowups()`.
|
|
792
927
|
*/
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
928
|
+
_processFollowupPayload(followupPayload: FollowupPayload): void {
|
|
929
|
+
switch (followupPayload.kind) {
|
|
930
|
+
case 'ModuleImportPayload':
|
|
931
|
+
const operationLoader = this._expectOperationLoader();
|
|
932
|
+
const node = operationLoader.get(followupPayload.operationReference);
|
|
933
|
+
if (node != null) {
|
|
934
|
+
// If the operation module is available synchronously, normalize the
|
|
935
|
+
// data synchronously.
|
|
936
|
+
this._processFollowupPayloadWithNormalizationNode(
|
|
937
|
+
followupPayload,
|
|
938
|
+
getOperation(node),
|
|
939
|
+
);
|
|
940
|
+
} else {
|
|
941
|
+
// Otherwise load the operation module and schedule a task to normalize
|
|
942
|
+
// the data when the module is available.
|
|
943
|
+
const id = this._nextSubscriptionId++;
|
|
944
|
+
this._pendingModulePayloadsCount++;
|
|
945
|
+
|
|
946
|
+
const decrementPendingCount = () => {
|
|
947
|
+
this._pendingModulePayloadsCount--;
|
|
948
|
+
this._maybeCompleteSubscriptionOperationTracking();
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
// Observable.from(operationLoader.load()) wouldn't catch synchronous
|
|
952
|
+
// errors thrown by the load function, which is user-defined. Guard
|
|
953
|
+
// against that with Observable.from(new Promise(<work>)).
|
|
954
|
+
const networkObservable = RelayObservable.from(
|
|
955
|
+
new Promise((resolve, reject) => {
|
|
956
|
+
operationLoader
|
|
957
|
+
.load(followupPayload.operationReference)
|
|
958
|
+
.then(resolve, reject);
|
|
959
|
+
}),
|
|
960
|
+
);
|
|
961
|
+
RelayObservable.create(sink => {
|
|
962
|
+
let cancellationToken;
|
|
963
|
+
const subscription = networkObservable.subscribe({
|
|
964
|
+
next: (loadedNode: ?NormalizationRootNode) => {
|
|
965
|
+
if (loadedNode != null) {
|
|
966
|
+
const publishModuleImportPayload = () => {
|
|
967
|
+
try {
|
|
968
|
+
const operation = getOperation(loadedNode);
|
|
969
|
+
const batchAsyncModuleUpdatesFN =
|
|
970
|
+
RelayFeatureFlags.BATCH_ASYNC_MODULE_UPDATES_FN;
|
|
971
|
+
const shouldScheduleAsyncStoreUpdate =
|
|
972
|
+
batchAsyncModuleUpdatesFN != null &&
|
|
973
|
+
this._pendingModulePayloadsCount > 1;
|
|
974
|
+
const [duration] = withDuration(() => {
|
|
975
|
+
this._handleFollowupPayload(followupPayload, operation);
|
|
976
|
+
// OK: always have to run after an async module import resolves
|
|
977
|
+
if (shouldScheduleAsyncStoreUpdate) {
|
|
978
|
+
this._scheduleAsyncStoreUpdate(
|
|
979
|
+
// $FlowFixMe[incompatible-call] `shouldScheduleAsyncStoreUpdate` check should cover `null` case
|
|
980
|
+
batchAsyncModuleUpdatesFN,
|
|
981
|
+
sink.complete,
|
|
982
|
+
);
|
|
983
|
+
} else {
|
|
984
|
+
const updatedOwners = this._runPublishQueue();
|
|
985
|
+
this._updateOperationTracker(updatedOwners);
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
this._log({
|
|
989
|
+
name: 'execute.async.module',
|
|
990
|
+
executeId: this._executeId,
|
|
991
|
+
operationName: operation.name,
|
|
992
|
+
duration,
|
|
993
|
+
});
|
|
994
|
+
if (!shouldScheduleAsyncStoreUpdate) {
|
|
995
|
+
sink.complete();
|
|
996
|
+
}
|
|
997
|
+
} catch (error) {
|
|
998
|
+
sink.error(error);
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
const scheduler = this._scheduler;
|
|
1002
|
+
if (scheduler == null) {
|
|
1003
|
+
publishModuleImportPayload();
|
|
1004
|
+
} else {
|
|
1005
|
+
cancellationToken = scheduler.schedule(
|
|
1006
|
+
publishModuleImportPayload,
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
sink.complete();
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
error: sink.error,
|
|
832
1014
|
});
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1015
|
+
return () => {
|
|
1016
|
+
subscription.unsubscribe();
|
|
1017
|
+
if (this._scheduler != null && cancellationToken != null) {
|
|
1018
|
+
this._scheduler.cancel(cancellationToken);
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
}).subscribe({
|
|
1022
|
+
complete: () => {
|
|
1023
|
+
this._complete(id);
|
|
1024
|
+
decrementPendingCount();
|
|
1025
|
+
},
|
|
1026
|
+
error: error => {
|
|
1027
|
+
this._error(error);
|
|
1028
|
+
decrementPendingCount();
|
|
1029
|
+
},
|
|
1030
|
+
start: subscription => this._start(id, subscription),
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
break;
|
|
1034
|
+
case 'ActorPayload':
|
|
1035
|
+
this._processFollowupPayloadWithNormalizationNode(
|
|
1036
|
+
followupPayload,
|
|
1037
|
+
followupPayload.node,
|
|
1038
|
+
);
|
|
1039
|
+
break;
|
|
1040
|
+
default:
|
|
1041
|
+
(followupPayload: empty);
|
|
1042
|
+
invariant(
|
|
1043
|
+
false,
|
|
1044
|
+
'OperationExecutor: Unexpected followup kind `%s`.',
|
|
1045
|
+
followupPayload.kind,
|
|
1046
|
+
);
|
|
846
1047
|
}
|
|
847
1048
|
}
|
|
848
1049
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1050
|
+
_processFollowupPayloadWithNormalizationNode(
|
|
1051
|
+
followupPayload: FollowupPayload,
|
|
1052
|
+
normalizationNode:
|
|
1053
|
+
| NormalizationSplitOperation
|
|
1054
|
+
| NormalizationOperation
|
|
1055
|
+
| NormalizationLinkedField,
|
|
1056
|
+
) {
|
|
1057
|
+
this._handleFollowupPayload(followupPayload, normalizationNode);
|
|
1058
|
+
this._maybeCompleteSubscriptionOperationTracking();
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
_handleFollowupPayload(
|
|
1062
|
+
followupPayload: FollowupPayload,
|
|
1063
|
+
normalizationNode:
|
|
1064
|
+
| NormalizationSplitOperation
|
|
1065
|
+
| NormalizationOperation
|
|
1066
|
+
| NormalizationLinkedField,
|
|
852
1067
|
): void {
|
|
853
|
-
const relayPayload = this.
|
|
854
|
-
|
|
855
|
-
|
|
1068
|
+
const relayPayload = this._normalizeFollowupPayload(
|
|
1069
|
+
followupPayload,
|
|
1070
|
+
normalizationNode,
|
|
1071
|
+
);
|
|
1072
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1073
|
+
this._operation,
|
|
1074
|
+
relayPayload,
|
|
856
1075
|
);
|
|
857
|
-
this._publishQueue.commitPayload(this._operation, relayPayload);
|
|
858
|
-
const updatedOwners = this._publishQueue.run();
|
|
859
|
-
this._updateOperationTracker(updatedOwners);
|
|
860
1076
|
this._processPayloadFollowups([relayPayload]);
|
|
861
1077
|
}
|
|
862
1078
|
|
|
@@ -902,7 +1118,7 @@ class Executor {
|
|
|
902
1118
|
(placeholder: empty);
|
|
903
1119
|
invariant(
|
|
904
1120
|
false,
|
|
905
|
-
'Unsupported incremental placeholder kind `%s`.',
|
|
1121
|
+
'OperationExecutor: Unsupported incremental placeholder kind `%s`.',
|
|
906
1122
|
placeholder.kind,
|
|
907
1123
|
);
|
|
908
1124
|
}
|
|
@@ -926,7 +1142,7 @@ class Executor {
|
|
|
926
1142
|
// exist.
|
|
927
1143
|
invariant(
|
|
928
1144
|
parentRecord != null,
|
|
929
|
-
'
|
|
1145
|
+
'OperationExecutor: Expected record `%s` to exist.',
|
|
930
1146
|
parentID,
|
|
931
1147
|
);
|
|
932
1148
|
let nextParentRecord;
|
|
@@ -955,14 +1171,12 @@ class Executor {
|
|
|
955
1171
|
record: nextParentRecord,
|
|
956
1172
|
fieldPayloads: nextParentPayloads,
|
|
957
1173
|
});
|
|
1174
|
+
|
|
958
1175
|
// If there were any queued responses, process them now that placeholders
|
|
959
1176
|
// are in place
|
|
960
1177
|
if (pendingResponses != null) {
|
|
961
|
-
const payloadFollowups =
|
|
962
|
-
pendingResponses
|
|
963
|
-
);
|
|
964
|
-
const updatedOwners = this._publishQueue.run();
|
|
965
|
-
this._updateOperationTracker(updatedOwners);
|
|
1178
|
+
const payloadFollowups =
|
|
1179
|
+
this._processIncrementalResponses(pendingResponses);
|
|
966
1180
|
this._processPayloadFollowups(payloadFollowups);
|
|
967
1181
|
}
|
|
968
1182
|
}
|
|
@@ -998,7 +1212,7 @@ class Executor {
|
|
|
998
1212
|
const placeholder = resultForPath.placeholder;
|
|
999
1213
|
invariant(
|
|
1000
1214
|
placeholder.kind === 'defer',
|
|
1001
|
-
'
|
|
1215
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
1002
1216
|
'to be data for @defer, was `@%s`.',
|
|
1003
1217
|
pathKey,
|
|
1004
1218
|
label,
|
|
@@ -1012,10 +1226,7 @@ class Executor {
|
|
|
1012
1226
|
// but Relay records paths relative to the parent of the stream node:
|
|
1013
1227
|
// therefore we strip the last two elements just to lookup the path
|
|
1014
1228
|
// (the item index is used later to insert the element in the list)
|
|
1015
|
-
const pathKey = path
|
|
1016
|
-
.slice(0, -2)
|
|
1017
|
-
.map(String)
|
|
1018
|
-
.join('.');
|
|
1229
|
+
const pathKey = path.slice(0, -2).map(String).join('.');
|
|
1019
1230
|
let resultForPath = resultForLabel.get(pathKey);
|
|
1020
1231
|
if (resultForPath == null) {
|
|
1021
1232
|
resultForPath = {kind: 'response', responses: [incrementalResponse]};
|
|
@@ -1028,7 +1239,7 @@ class Executor {
|
|
|
1028
1239
|
const placeholder = resultForPath.placeholder;
|
|
1029
1240
|
invariant(
|
|
1030
1241
|
placeholder.kind === 'stream',
|
|
1031
|
-
'
|
|
1242
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
1032
1243
|
'to be data for @stream, was `@%s`.',
|
|
1033
1244
|
pathKey,
|
|
1034
1245
|
label,
|
|
@@ -1049,27 +1260,37 @@ class Executor {
|
|
|
1049
1260
|
response: GraphQLResponseWithData,
|
|
1050
1261
|
): RelayResponsePayload {
|
|
1051
1262
|
const {dataID: parentID} = placeholder.selector;
|
|
1263
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1264
|
+
this._actorIdentifier =
|
|
1265
|
+
placeholder.actorIdentifier ?? this._actorIdentifier;
|
|
1052
1266
|
const relayPayload = normalizeResponse(
|
|
1053
1267
|
response,
|
|
1054
1268
|
placeholder.selector,
|
|
1055
1269
|
placeholder.typeName,
|
|
1056
1270
|
{
|
|
1271
|
+
actorIdentifier: this._actorIdentifier,
|
|
1057
1272
|
getDataID: this._getDataID,
|
|
1058
1273
|
path: placeholder.path,
|
|
1059
|
-
reactFlightPayloadDeserializer:
|
|
1274
|
+
reactFlightPayloadDeserializer:
|
|
1275
|
+
this._reactFlightPayloadDeserializer != null
|
|
1276
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1277
|
+
: null,
|
|
1060
1278
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1061
1279
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1062
1280
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
1063
1281
|
},
|
|
1064
1282
|
);
|
|
1065
|
-
this.
|
|
1283
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1284
|
+
this._operation,
|
|
1285
|
+
relayPayload,
|
|
1286
|
+
);
|
|
1066
1287
|
|
|
1067
1288
|
// Load the version of the parent record from which this incremental data
|
|
1068
1289
|
// was derived
|
|
1069
1290
|
const parentEntry = this._source.get(parentID);
|
|
1070
1291
|
invariant(
|
|
1071
1292
|
parentEntry != null,
|
|
1072
|
-
'
|
|
1293
|
+
'OperationExecutor: Expected the parent record `%s` for @defer ' +
|
|
1073
1294
|
'data to exist.',
|
|
1074
1295
|
parentID,
|
|
1075
1296
|
);
|
|
@@ -1079,15 +1300,17 @@ class Executor {
|
|
|
1079
1300
|
errors: null,
|
|
1080
1301
|
fieldPayloads,
|
|
1081
1302
|
incrementalPlaceholders: null,
|
|
1082
|
-
|
|
1303
|
+
followupPayloads: null,
|
|
1083
1304
|
source: RelayRecordSource.create(),
|
|
1084
1305
|
isFinal: response.extensions?.is_final === true,
|
|
1085
1306
|
};
|
|
1086
|
-
this.
|
|
1307
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1087
1308
|
this._operation,
|
|
1088
1309
|
handleFieldsRelayPayload,
|
|
1089
1310
|
);
|
|
1090
1311
|
}
|
|
1312
|
+
|
|
1313
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1091
1314
|
return relayPayload;
|
|
1092
1315
|
}
|
|
1093
1316
|
|
|
@@ -1100,12 +1323,14 @@ class Executor {
|
|
|
1100
1323
|
placeholder: StreamPlaceholder,
|
|
1101
1324
|
response: GraphQLResponseWithData,
|
|
1102
1325
|
): RelayResponsePayload {
|
|
1103
|
-
const {parentID, node, variables} = placeholder;
|
|
1326
|
+
const {parentID, node, variables, actorIdentifier} = placeholder;
|
|
1327
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1328
|
+
this._actorIdentifier = actorIdentifier ?? this._actorIdentifier;
|
|
1104
1329
|
// Find the LinkedField where @stream was applied
|
|
1105
1330
|
const field = node.selections[0];
|
|
1106
1331
|
invariant(
|
|
1107
1332
|
field != null && field.kind === 'LinkedField' && field.plural === true,
|
|
1108
|
-
'
|
|
1333
|
+
'OperationExecutor: Expected @stream to be used on a plural field.',
|
|
1109
1334
|
);
|
|
1110
1335
|
const {
|
|
1111
1336
|
fieldPayloads,
|
|
@@ -1125,34 +1350,38 @@ class Executor {
|
|
|
1125
1350
|
// Publish the new item and update the parent record to set
|
|
1126
1351
|
// field[index] = item *if* the parent record hasn't been concurrently
|
|
1127
1352
|
// modified.
|
|
1128
|
-
this.
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1353
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1354
|
+
this._operation,
|
|
1355
|
+
relayPayload,
|
|
1356
|
+
store => {
|
|
1357
|
+
const currentParentRecord = store.get(parentID);
|
|
1358
|
+
if (currentParentRecord == null) {
|
|
1359
|
+
// parent has since been deleted, stream data is stale
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
const currentItems = currentParentRecord.getLinkedRecords(storageKey);
|
|
1363
|
+
if (currentItems == null) {
|
|
1364
|
+
// field has since been deleted, stream data is stale
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
if (
|
|
1368
|
+
currentItems.length !== prevIDs.length ||
|
|
1369
|
+
currentItems.some(
|
|
1370
|
+
(currentItem, index) =>
|
|
1371
|
+
prevIDs[index] !== (currentItem && currentItem.getDataID()),
|
|
1372
|
+
)
|
|
1373
|
+
) {
|
|
1374
|
+
// field has been modified by something other than this query,
|
|
1375
|
+
// stream data is stale
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
// parent.field has not been concurrently modified:
|
|
1379
|
+
// update `parent.field[index] = item`
|
|
1380
|
+
const nextItems = [...currentItems];
|
|
1381
|
+
nextItems[itemIndex] = store.get(itemID);
|
|
1382
|
+
currentParentRecord.setLinkedRecords(nextItems, storageKey);
|
|
1383
|
+
},
|
|
1384
|
+
);
|
|
1156
1385
|
|
|
1157
1386
|
// Now that the parent record has been updated to include the new item,
|
|
1158
1387
|
// also update any handle fields that are derived from the parent record.
|
|
@@ -1161,15 +1390,17 @@ class Executor {
|
|
|
1161
1390
|
errors: null,
|
|
1162
1391
|
fieldPayloads,
|
|
1163
1392
|
incrementalPlaceholders: null,
|
|
1164
|
-
|
|
1393
|
+
followupPayloads: null,
|
|
1165
1394
|
source: RelayRecordSource.create(),
|
|
1166
1395
|
isFinal: false,
|
|
1167
1396
|
};
|
|
1168
|
-
this.
|
|
1397
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1169
1398
|
this._operation,
|
|
1170
1399
|
handleFieldsRelayPayload,
|
|
1171
1400
|
);
|
|
1172
1401
|
}
|
|
1402
|
+
|
|
1403
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1173
1404
|
return relayPayload;
|
|
1174
1405
|
}
|
|
1175
1406
|
|
|
@@ -1191,7 +1422,7 @@ class Executor {
|
|
|
1191
1422
|
const {data} = response;
|
|
1192
1423
|
invariant(
|
|
1193
1424
|
typeof data === 'object',
|
|
1194
|
-
'
|
|
1425
|
+
'OperationExecutor: Expected the GraphQL @stream payload `data` ' +
|
|
1195
1426
|
'value to be an object.',
|
|
1196
1427
|
);
|
|
1197
1428
|
const responseKey = field.alias ?? field.name;
|
|
@@ -1202,7 +1433,7 @@ class Executor {
|
|
|
1202
1433
|
const parentEntry = this._source.get(parentID);
|
|
1203
1434
|
invariant(
|
|
1204
1435
|
parentEntry != null,
|
|
1205
|
-
'
|
|
1436
|
+
'OperationExecutor: Expected the parent record `%s` for @stream ' +
|
|
1206
1437
|
'data to exist.',
|
|
1207
1438
|
parentID,
|
|
1208
1439
|
);
|
|
@@ -1217,7 +1448,7 @@ class Executor {
|
|
|
1217
1448
|
);
|
|
1218
1449
|
invariant(
|
|
1219
1450
|
prevIDs != null,
|
|
1220
|
-
'
|
|
1451
|
+
'OperationExecutor: Expected record `%s` to have fetched field ' +
|
|
1221
1452
|
'`%s` with @stream.',
|
|
1222
1453
|
parentID,
|
|
1223
1454
|
field.name,
|
|
@@ -1228,7 +1459,7 @@ class Executor {
|
|
|
1228
1459
|
const itemIndex = parseInt(finalPathEntry, 10);
|
|
1229
1460
|
invariant(
|
|
1230
1461
|
itemIndex === finalPathEntry && itemIndex >= 0,
|
|
1231
|
-
'
|
|
1462
|
+
'OperationExecutor: Expected path for @stream to end in a ' +
|
|
1232
1463
|
'positive integer index, got `%s`',
|
|
1233
1464
|
finalPathEntry,
|
|
1234
1465
|
);
|
|
@@ -1236,7 +1467,7 @@ class Executor {
|
|
|
1236
1467
|
const typeName = field.concreteType ?? data[TYPENAME_KEY];
|
|
1237
1468
|
invariant(
|
|
1238
1469
|
typeof typeName === 'string',
|
|
1239
|
-
'
|
|
1470
|
+
'OperationExecutor: Expected @stream field `%s` to have a ' +
|
|
1240
1471
|
'__typename.',
|
|
1241
1472
|
field.name,
|
|
1242
1473
|
);
|
|
@@ -1251,7 +1482,7 @@ class Executor {
|
|
|
1251
1482
|
generateClientID(parentID, storageKey, itemIndex);
|
|
1252
1483
|
invariant(
|
|
1253
1484
|
typeof itemID === 'string',
|
|
1254
|
-
'
|
|
1485
|
+
'OperationExecutor: Expected id of elements of field `%s` to ' +
|
|
1255
1486
|
'be strings.',
|
|
1256
1487
|
storageKey,
|
|
1257
1488
|
);
|
|
@@ -1271,9 +1502,13 @@ class Executor {
|
|
|
1271
1502
|
fieldPayloads,
|
|
1272
1503
|
});
|
|
1273
1504
|
const relayPayload = normalizeResponse(response, selector, typeName, {
|
|
1505
|
+
actorIdentifier: this._actorIdentifier,
|
|
1274
1506
|
getDataID: this._getDataID,
|
|
1275
1507
|
path: [...normalizationPath, responseKey, String(itemIndex)],
|
|
1276
|
-
reactFlightPayloadDeserializer:
|
|
1508
|
+
reactFlightPayloadDeserializer:
|
|
1509
|
+
this._reactFlightPayloadDeserializer != null
|
|
1510
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1511
|
+
: null,
|
|
1277
1512
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1278
1513
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1279
1514
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
@@ -1288,14 +1523,29 @@ class Executor {
|
|
|
1288
1523
|
};
|
|
1289
1524
|
}
|
|
1290
1525
|
|
|
1526
|
+
_scheduleAsyncStoreUpdate(
|
|
1527
|
+
scheduleFn: (() => void) => Disposable,
|
|
1528
|
+
completeFn: () => void,
|
|
1529
|
+
): void {
|
|
1530
|
+
this._completeFns.push(completeFn);
|
|
1531
|
+
if (this._asyncStoreUpdateDisposable != null) {
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
this._asyncStoreUpdateDisposable = scheduleFn(() => {
|
|
1535
|
+
this._asyncStoreUpdateDisposable = null;
|
|
1536
|
+
const updatedOwners = this._runPublishQueue();
|
|
1537
|
+
this._updateOperationTracker(updatedOwners);
|
|
1538
|
+
for (const complete of this._completeFns) {
|
|
1539
|
+
complete();
|
|
1540
|
+
}
|
|
1541
|
+
this._completeFns = [];
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1291
1545
|
_updateOperationTracker(
|
|
1292
1546
|
updatedOwners: ?$ReadOnlyArray<RequestDescriptor>,
|
|
1293
1547
|
): void {
|
|
1294
|
-
if (
|
|
1295
|
-
this._operationTracker != null &&
|
|
1296
|
-
updatedOwners != null &&
|
|
1297
|
-
updatedOwners.length > 0
|
|
1298
|
-
) {
|
|
1548
|
+
if (updatedOwners != null && updatedOwners.length > 0) {
|
|
1299
1549
|
this._operationTracker.update(
|
|
1300
1550
|
this._operation.request,
|
|
1301
1551
|
new Set(updatedOwners),
|
|
@@ -1304,10 +1554,60 @@ class Executor {
|
|
|
1304
1554
|
}
|
|
1305
1555
|
|
|
1306
1556
|
_completeOperationTracker() {
|
|
1307
|
-
|
|
1308
|
-
|
|
1557
|
+
this._operationTracker.complete(this._operation.request);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
_getPublishQueueAndSaveActor(): PublishQueue {
|
|
1561
|
+
this._seenActors.add(this._actorIdentifier);
|
|
1562
|
+
return this._getPublishQueue(this._actorIdentifier);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
_getActorsToVisit(): $ReadOnlySet<ActorIdentifier> {
|
|
1566
|
+
if (this._seenActors.size === 0) {
|
|
1567
|
+
return new Set([this._actorIdentifier]);
|
|
1568
|
+
} else {
|
|
1569
|
+
return this._seenActors;
|
|
1309
1570
|
}
|
|
1310
1571
|
}
|
|
1572
|
+
|
|
1573
|
+
_runPublishQueue(
|
|
1574
|
+
operation?: OperationDescriptor,
|
|
1575
|
+
): $ReadOnlyArray<RequestDescriptor> {
|
|
1576
|
+
const updatedOwners = new Set();
|
|
1577
|
+
for (const actorIdentifier of this._getActorsToVisit()) {
|
|
1578
|
+
const owners = this._getPublishQueue(actorIdentifier).run(operation);
|
|
1579
|
+
owners.forEach(owner => updatedOwners.add(owner));
|
|
1580
|
+
}
|
|
1581
|
+
return Array.from(updatedOwners);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
_retainData() {
|
|
1585
|
+
for (const actorIdentifier of this._getActorsToVisit()) {
|
|
1586
|
+
if (!this._retainDisposables.has(actorIdentifier)) {
|
|
1587
|
+
this._retainDisposables.set(
|
|
1588
|
+
actorIdentifier,
|
|
1589
|
+
this._getStore(actorIdentifier).retain(this._operation),
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
_disposeRetainedData() {
|
|
1596
|
+
for (const disposable of this._retainDisposables.values()) {
|
|
1597
|
+
disposable.dispose();
|
|
1598
|
+
}
|
|
1599
|
+
this._retainDisposables.clear();
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
_expectOperationLoader(): OperationLoader {
|
|
1603
|
+
const operationLoader = this._operationLoader;
|
|
1604
|
+
invariant(
|
|
1605
|
+
operationLoader,
|
|
1606
|
+
'OperationExecutor: Expected an operationLoader to be ' +
|
|
1607
|
+
'configured when using `@match`.',
|
|
1608
|
+
);
|
|
1609
|
+
return operationLoader;
|
|
1610
|
+
}
|
|
1311
1611
|
}
|
|
1312
1612
|
|
|
1313
1613
|
function partitionGraphQLResponses(
|
|
@@ -1324,7 +1624,7 @@ function partitionGraphQLResponses(
|
|
|
1324
1624
|
if (label == null || path == null) {
|
|
1325
1625
|
invariant(
|
|
1326
1626
|
false,
|
|
1327
|
-
'
|
|
1627
|
+
'OperationExecutor: invalid incremental payload, expected ' +
|
|
1328
1628
|
'`path` and `label` to either both be null/undefined, or ' +
|
|
1329
1629
|
'`path` to be an `Array<string | number>` and `label` to be a ' +
|
|
1330
1630
|
'`string`.',
|
|
@@ -1376,11 +1676,13 @@ function validateOptimisticResponsePayload(
|
|
|
1376
1676
|
if (incrementalPlaceholders != null && incrementalPlaceholders.length !== 0) {
|
|
1377
1677
|
invariant(
|
|
1378
1678
|
false,
|
|
1379
|
-
'
|
|
1679
|
+
'OperationExecutor: optimistic responses cannot be returned ' +
|
|
1380
1680
|
'for operations that use incremental data delivery (@defer, ' +
|
|
1381
1681
|
'@stream, and @stream_connection).',
|
|
1382
1682
|
);
|
|
1383
1683
|
}
|
|
1384
1684
|
}
|
|
1385
1685
|
|
|
1386
|
-
module.exports = {
|
|
1686
|
+
module.exports = {
|
|
1687
|
+
execute,
|
|
1688
|
+
};
|