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
|
@@ -13,33 +13,22 @@
|
|
|
13
13
|
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const RelayModernRecord = require('./RelayModernRecord');
|
|
18
|
-
const RelayObservable = require('../network/RelayObservable');
|
|
19
|
-
const RelayRecordSource = require('./RelayRecordSource');
|
|
20
|
-
const RelayResponseNormalizer = require('./RelayResponseNormalizer');
|
|
21
|
-
|
|
22
|
-
const getOperation = require('../util/getOperation');
|
|
23
|
-
const invariant = require('invariant');
|
|
24
|
-
const stableCopy = require('../util/stableCopy');
|
|
25
|
-
const warning = require('warning');
|
|
26
|
-
|
|
27
|
-
const {generateClientID} = require('./ClientID');
|
|
28
|
-
const {createNormalizationSelector} = require('./RelayModernSelector');
|
|
29
|
-
const {ROOT_TYPE, TYPENAME_KEY, getStorageKey} = require('./RelayStoreUtils');
|
|
30
|
-
|
|
16
|
+
import type {ActorIdentifier} from '../multi-actor-environment/ActorIdentifier';
|
|
31
17
|
import type {
|
|
32
18
|
GraphQLResponse,
|
|
33
|
-
GraphQLSingularResponse,
|
|
34
19
|
GraphQLResponseWithData,
|
|
20
|
+
GraphQLSingularResponse,
|
|
21
|
+
ReactFlightServerTree,
|
|
35
22
|
} from '../network/RelayNetworkTypes';
|
|
36
23
|
import type {Sink, Subscription} from '../network/RelayObservable';
|
|
37
24
|
import type {
|
|
38
25
|
DeferPlaceholder,
|
|
39
|
-
|
|
26
|
+
FollowupPayload,
|
|
40
27
|
HandleFieldPayload,
|
|
41
28
|
IncrementalDataPlaceholder,
|
|
29
|
+
LogFunction,
|
|
42
30
|
ModuleImportPayload,
|
|
31
|
+
MutationParameters,
|
|
43
32
|
NormalizationSelector,
|
|
44
33
|
OperationDescriptor,
|
|
45
34
|
OperationLoader,
|
|
@@ -47,10 +36,12 @@ import type {
|
|
|
47
36
|
OptimisticResponseConfig,
|
|
48
37
|
OptimisticUpdate,
|
|
49
38
|
PublishQueue,
|
|
39
|
+
ReactFlightClientResponse,
|
|
50
40
|
ReactFlightPayloadDeserializer,
|
|
51
41
|
ReactFlightServerErrorHandler,
|
|
52
42
|
Record,
|
|
53
43
|
RelayResponsePayload,
|
|
44
|
+
RequestDescriptor,
|
|
54
45
|
SelectorStoreUpdater,
|
|
55
46
|
Store,
|
|
56
47
|
StreamPlaceholder,
|
|
@@ -62,27 +53,50 @@ import type {
|
|
|
62
53
|
NormalizationSelectableNode,
|
|
63
54
|
NormalizationSplitOperation,
|
|
64
55
|
} from '../util/NormalizationNode';
|
|
65
|
-
import type {DataID,
|
|
56
|
+
import type {DataID, Disposable, Variables} from '../util/RelayRuntimeTypes';
|
|
66
57
|
import type {GetDataID} from './RelayResponseNormalizer';
|
|
67
58
|
import type {NormalizationOptions} from './RelayResponseNormalizer';
|
|
68
59
|
|
|
69
|
-
|
|
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,
|
|
70
82
|
+getDataID: GetDataID,
|
|
71
|
-
+
|
|
83
|
+
+getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue,
|
|
84
|
+
+getStore: (actorIdentifier: ActorIdentifier) => Store,
|
|
85
|
+
+isClientPayload?: boolean,
|
|
72
86
|
+operation: OperationDescriptor,
|
|
73
87
|
+operationExecutions: Map<string, ActiveState>,
|
|
74
88
|
+operationLoader: ?OperationLoader,
|
|
75
|
-
+operationTracker
|
|
76
|
-
+optimisticConfig: ?OptimisticResponseConfig
|
|
77
|
-
+publishQueue: PublishQueue,
|
|
89
|
+
+operationTracker: OperationTracker,
|
|
90
|
+
+optimisticConfig: ?OptimisticResponseConfig<TMutation>,
|
|
78
91
|
+reactFlightPayloadDeserializer?: ?ReactFlightPayloadDeserializer,
|
|
79
92
|
+reactFlightServerErrorHandler?: ?ReactFlightServerErrorHandler,
|
|
80
93
|
+scheduler?: ?TaskScheduler,
|
|
94
|
+
+shouldProcessClientComponents?: ?boolean,
|
|
81
95
|
+sink: Sink<GraphQLResponse>,
|
|
82
96
|
+source: RelayObservable<GraphQLResponse>,
|
|
83
|
-
+
|
|
84
|
-
+updater?: ?SelectorStoreUpdater
|
|
85
|
-
+
|
|
97
|
+
+treatMissingFieldsAsNull: boolean,
|
|
98
|
+
+updater?: ?SelectorStoreUpdater<TMutation['response']>,
|
|
99
|
+
+log: LogFunction,
|
|
86
100
|
|};
|
|
87
101
|
|
|
88
102
|
export type ActiveState = 'active' | 'inactive';
|
|
@@ -110,7 +124,9 @@ type IncrementalGraphQLResponse = {|
|
|
|
110
124
|
response: GraphQLResponseWithData,
|
|
111
125
|
|};
|
|
112
126
|
|
|
113
|
-
function execute
|
|
127
|
+
function execute<TMutation: MutationParameters>(
|
|
128
|
+
config: ExecuteConfig<TMutation>,
|
|
129
|
+
): Executor<TMutation> {
|
|
114
130
|
return new Executor(config);
|
|
115
131
|
}
|
|
116
132
|
|
|
@@ -119,22 +135,26 @@ function execute(config: ExecuteConfig): Executor {
|
|
|
119
135
|
* including optimistic payloads, standard payloads, resolution of match
|
|
120
136
|
* dependencies, etc.
|
|
121
137
|
*/
|
|
122
|
-
class Executor {
|
|
138
|
+
class Executor<TMutation: MutationParameters> {
|
|
139
|
+
_actorIdentifier: ActorIdentifier;
|
|
123
140
|
_getDataID: GetDataID;
|
|
124
141
|
_treatMissingFieldsAsNull: boolean;
|
|
125
142
|
_incrementalPayloadsPending: boolean;
|
|
126
143
|
_incrementalResults: Map<Label, Map<PathKey, IncrementalResults>>;
|
|
144
|
+
_log: LogFunction;
|
|
145
|
+
_executeId: number;
|
|
127
146
|
_nextSubscriptionId: number;
|
|
128
147
|
_operation: OperationDescriptor;
|
|
129
148
|
_operationExecutions: Map<string, ActiveState>;
|
|
130
149
|
_operationLoader: ?OperationLoader;
|
|
131
|
-
_operationTracker:
|
|
150
|
+
_operationTracker: OperationTracker;
|
|
132
151
|
_operationUpdateEpochs: Map<string, number>;
|
|
133
|
-
_optimisticUpdates: null | Array<OptimisticUpdate
|
|
152
|
+
_optimisticUpdates: null | Array<OptimisticUpdate<TMutation>>;
|
|
134
153
|
_pendingModulePayloadsCount: number;
|
|
135
|
-
|
|
154
|
+
+_getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue;
|
|
136
155
|
_reactFlightPayloadDeserializer: ?ReactFlightPayloadDeserializer;
|
|
137
156
|
_reactFlightServerErrorHandler: ?ReactFlightServerErrorHandler;
|
|
157
|
+
_shouldProcessClientComponents: ?boolean;
|
|
138
158
|
_scheduler: ?TaskScheduler;
|
|
139
159
|
_sink: Sink<GraphQLResponse>;
|
|
140
160
|
_source: Map<
|
|
@@ -142,34 +162,44 @@ class Executor {
|
|
|
142
162
|
{|+record: Record, +fieldPayloads: Array<HandleFieldPayload>|},
|
|
143
163
|
>;
|
|
144
164
|
_state: 'started' | 'loading_incremental' | 'loading_final' | 'completed';
|
|
145
|
-
|
|
165
|
+
+_getStore: (actorIdentifier: ActorIdentifier) => Store;
|
|
146
166
|
_subscriptions: Map<number, Subscription>;
|
|
147
|
-
_updater: ?SelectorStoreUpdater
|
|
148
|
-
|
|
167
|
+
_updater: ?SelectorStoreUpdater<TMutation['response']>;
|
|
168
|
+
_asyncStoreUpdateDisposable: ?Disposable;
|
|
169
|
+
_completeFns: Array<() => void>;
|
|
170
|
+
+_retainDisposables: Map<ActorIdentifier, Disposable>;
|
|
149
171
|
+_isClientPayload: boolean;
|
|
172
|
+
+_isSubscriptionOperation: boolean;
|
|
173
|
+
+_seenActors: Set<ActorIdentifier>;
|
|
150
174
|
|
|
151
175
|
constructor({
|
|
176
|
+
actorIdentifier,
|
|
177
|
+
getDataID,
|
|
178
|
+
getPublishQueue,
|
|
179
|
+
getStore,
|
|
180
|
+
isClientPayload,
|
|
152
181
|
operation,
|
|
153
182
|
operationExecutions,
|
|
154
183
|
operationLoader,
|
|
184
|
+
operationTracker,
|
|
155
185
|
optimisticConfig,
|
|
156
|
-
|
|
186
|
+
reactFlightPayloadDeserializer,
|
|
187
|
+
reactFlightServerErrorHandler,
|
|
157
188
|
scheduler,
|
|
189
|
+
shouldProcessClientComponents,
|
|
158
190
|
sink,
|
|
159
191
|
source,
|
|
160
|
-
store,
|
|
161
|
-
updater,
|
|
162
|
-
operationTracker,
|
|
163
192
|
treatMissingFieldsAsNull,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}: ExecuteConfig): void {
|
|
193
|
+
updater,
|
|
194
|
+
log,
|
|
195
|
+
}: ExecuteConfig<TMutation>): void {
|
|
196
|
+
this._actorIdentifier = actorIdentifier;
|
|
169
197
|
this._getDataID = getDataID;
|
|
170
198
|
this._treatMissingFieldsAsNull = treatMissingFieldsAsNull;
|
|
171
199
|
this._incrementalPayloadsPending = false;
|
|
172
200
|
this._incrementalResults = new Map();
|
|
201
|
+
this._log = log;
|
|
202
|
+
this._executeId = generateID();
|
|
173
203
|
this._nextSubscriptionId = 0;
|
|
174
204
|
this._operation = operation;
|
|
175
205
|
this._operationExecutions = operationExecutions;
|
|
@@ -178,17 +208,23 @@ class Executor {
|
|
|
178
208
|
this._operationUpdateEpochs = new Map();
|
|
179
209
|
this._optimisticUpdates = null;
|
|
180
210
|
this._pendingModulePayloadsCount = 0;
|
|
181
|
-
this.
|
|
211
|
+
this._getPublishQueue = getPublishQueue;
|
|
182
212
|
this._scheduler = scheduler;
|
|
183
213
|
this._sink = sink;
|
|
184
214
|
this._source = new Map();
|
|
185
215
|
this._state = 'started';
|
|
186
|
-
this.
|
|
216
|
+
this._getStore = getStore;
|
|
187
217
|
this._subscriptions = new Map();
|
|
188
218
|
this._updater = updater;
|
|
189
219
|
this._isClientPayload = isClientPayload === true;
|
|
190
220
|
this._reactFlightPayloadDeserializer = reactFlightPayloadDeserializer;
|
|
191
221
|
this._reactFlightServerErrorHandler = reactFlightServerErrorHandler;
|
|
222
|
+
this._isSubscriptionOperation =
|
|
223
|
+
this._operation.request.node.params.operationKind === 'subscription';
|
|
224
|
+
this._shouldProcessClientComponents = shouldProcessClientComponents;
|
|
225
|
+
this._retainDisposables = new Map();
|
|
226
|
+
this._seenActors = new Set();
|
|
227
|
+
this._completeFns = [];
|
|
192
228
|
|
|
193
229
|
const id = this._nextSubscriptionId++;
|
|
194
230
|
source.subscribe({
|
|
@@ -201,7 +237,16 @@ class Executor {
|
|
|
201
237
|
sink.error(error);
|
|
202
238
|
}
|
|
203
239
|
},
|
|
204
|
-
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
|
+
},
|
|
205
250
|
});
|
|
206
251
|
|
|
207
252
|
if (optimisticConfig != null) {
|
|
@@ -231,18 +276,41 @@ class Executor {
|
|
|
231
276
|
if (optimisticUpdates !== null) {
|
|
232
277
|
this._optimisticUpdates = null;
|
|
233
278
|
optimisticUpdates.forEach(update =>
|
|
234
|
-
this.
|
|
279
|
+
this._getPublishQueueAndSaveActor().revertUpdate(update),
|
|
235
280
|
);
|
|
236
|
-
|
|
281
|
+
// OK: run revert on cancel
|
|
282
|
+
this._runPublishQueue();
|
|
237
283
|
}
|
|
238
284
|
this._incrementalResults.clear();
|
|
239
|
-
this.
|
|
240
|
-
|
|
241
|
-
this.
|
|
242
|
-
this._retainDisposable = null;
|
|
285
|
+
if (this._asyncStoreUpdateDisposable != null) {
|
|
286
|
+
this._asyncStoreUpdateDisposable.dispose();
|
|
287
|
+
this._asyncStoreUpdateDisposable = null;
|
|
243
288
|
}
|
|
289
|
+
this._completeFns = [];
|
|
290
|
+
this._completeOperationTracker();
|
|
291
|
+
this._disposeRetainedData();
|
|
244
292
|
}
|
|
245
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
|
+
|
|
246
314
|
_updateActiveState(): void {
|
|
247
315
|
let activeState;
|
|
248
316
|
switch (this._state) {
|
|
@@ -265,7 +333,7 @@ class Executor {
|
|
|
265
333
|
}
|
|
266
334
|
default:
|
|
267
335
|
(this._state: empty);
|
|
268
|
-
invariant(false, '
|
|
336
|
+
invariant(false, 'OperationExecutor: invalid executor state.');
|
|
269
337
|
}
|
|
270
338
|
this._operationExecutions.set(
|
|
271
339
|
this._operation.request.identifier,
|
|
@@ -302,12 +370,21 @@ class Executor {
|
|
|
302
370
|
if (this._subscriptions.size === 0) {
|
|
303
371
|
this.cancel();
|
|
304
372
|
this._sink.complete();
|
|
373
|
+
this._log({
|
|
374
|
+
name: 'execute.complete',
|
|
375
|
+
executeId: this._executeId,
|
|
376
|
+
});
|
|
305
377
|
}
|
|
306
378
|
}
|
|
307
379
|
|
|
308
380
|
_error(error: Error): void {
|
|
309
381
|
this.cancel();
|
|
310
382
|
this._sink.error(error);
|
|
383
|
+
this._log({
|
|
384
|
+
name: 'execute.error',
|
|
385
|
+
executeId: this._executeId,
|
|
386
|
+
error,
|
|
387
|
+
});
|
|
311
388
|
}
|
|
312
389
|
|
|
313
390
|
_start(id: number, subscription: Subscription): void {
|
|
@@ -318,8 +395,16 @@ class Executor {
|
|
|
318
395
|
// Handle a raw GraphQL response.
|
|
319
396
|
_next(_id: number, response: GraphQLResponse): void {
|
|
320
397
|
this._schedule(() => {
|
|
321
|
-
|
|
322
|
-
|
|
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
|
+
});
|
|
323
408
|
});
|
|
324
409
|
}
|
|
325
410
|
|
|
@@ -363,7 +448,8 @@ class Executor {
|
|
|
363
448
|
error.stack;
|
|
364
449
|
throw error;
|
|
365
450
|
} else {
|
|
366
|
-
const responseWithData: GraphQLResponseWithData =
|
|
451
|
+
const responseWithData: GraphQLResponseWithData =
|
|
452
|
+
(response: $FlowFixMe);
|
|
367
453
|
results.push(responseWithData);
|
|
368
454
|
}
|
|
369
455
|
});
|
|
@@ -383,7 +469,10 @@ class Executor {
|
|
|
383
469
|
responsePart => responsePart.extensions?.isOptimistic === true,
|
|
384
470
|
)
|
|
385
471
|
) {
|
|
386
|
-
invariant(
|
|
472
|
+
invariant(
|
|
473
|
+
false,
|
|
474
|
+
'OperationExecutor: Optimistic responses cannot be batched.',
|
|
475
|
+
);
|
|
387
476
|
}
|
|
388
477
|
return false;
|
|
389
478
|
}
|
|
@@ -392,7 +481,7 @@ class Executor {
|
|
|
392
481
|
if (isOptimistic && this._state !== 'started') {
|
|
393
482
|
invariant(
|
|
394
483
|
false,
|
|
395
|
-
'
|
|
484
|
+
'OperationExecutor: optimistic payload received after server payload.',
|
|
396
485
|
);
|
|
397
486
|
}
|
|
398
487
|
if (isOptimistic) {
|
|
@@ -411,6 +500,7 @@ class Executor {
|
|
|
411
500
|
if (this._state === 'completed') {
|
|
412
501
|
return;
|
|
413
502
|
}
|
|
503
|
+
this._seenActors.clear();
|
|
414
504
|
|
|
415
505
|
const responses = Array.isArray(response) ? response : [response];
|
|
416
506
|
const responsesWithData = this._handleErrorResponse(responses);
|
|
@@ -434,10 +524,9 @@ class Executor {
|
|
|
434
524
|
return;
|
|
435
525
|
}
|
|
436
526
|
|
|
437
|
-
const [
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
] = partitionGraphQLResponses(responsesWithData);
|
|
527
|
+
const [nonIncrementalResponses, incrementalResponses] =
|
|
528
|
+
partitionGraphQLResponses(responsesWithData);
|
|
529
|
+
const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
|
|
441
530
|
|
|
442
531
|
// In theory this doesn't preserve the ordering of the batch.
|
|
443
532
|
// The idea is that a batch is always:
|
|
@@ -446,57 +535,98 @@ class Executor {
|
|
|
446
535
|
// The non-incremental payload can appear if the server sends a batch
|
|
447
536
|
// with the initial payload followed by some early-to-resolve incremental
|
|
448
537
|
// payloads (although, can that even happen?)
|
|
449
|
-
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
|
+
|
|
450
560
|
const payloadFollowups = this._processResponses(nonIncrementalResponses);
|
|
451
|
-
// Please note that we're passing `this._operation` to the publish
|
|
452
|
-
// queue here, which will later passed to the store (via notify)
|
|
453
|
-
// to indicate that this is an operation that caused the store to update
|
|
454
|
-
const updatedOwners = this._publishQueue.run(this._operation);
|
|
455
|
-
this._updateOperationTracker(updatedOwners);
|
|
456
561
|
this._processPayloadFollowups(payloadFollowups);
|
|
457
|
-
if (this._incrementalPayloadsPending && !this._retainDisposable) {
|
|
458
|
-
this._retainDisposable = this._store.retain(this._operation);
|
|
459
|
-
}
|
|
460
562
|
}
|
|
461
563
|
|
|
462
564
|
if (incrementalResponses.length > 0) {
|
|
463
|
-
const payloadFollowups =
|
|
464
|
-
incrementalResponses
|
|
465
|
-
|
|
466
|
-
// For the incremental case, we're only handling follow-up responses
|
|
467
|
-
// for already initiated operation (and we're not passing it to
|
|
468
|
-
// the run(...) call)
|
|
469
|
-
const updatedOwners = this._publishQueue.run();
|
|
470
|
-
this._updateOperationTracker(updatedOwners);
|
|
565
|
+
const payloadFollowups =
|
|
566
|
+
this._processIncrementalResponses(incrementalResponses);
|
|
567
|
+
|
|
471
568
|
this._processPayloadFollowups(payloadFollowups);
|
|
472
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);
|
|
473
598
|
this._sink.next(response);
|
|
474
599
|
}
|
|
475
600
|
|
|
476
601
|
_processOptimisticResponse(
|
|
477
602
|
response: ?GraphQLResponseWithData,
|
|
478
|
-
updater: ?SelectorStoreUpdater
|
|
603
|
+
updater: ?SelectorStoreUpdater<TMutation['response']>,
|
|
479
604
|
treatMissingFieldsAsNull: boolean,
|
|
480
605
|
): void {
|
|
481
606
|
invariant(
|
|
482
607
|
this._optimisticUpdates === null,
|
|
483
|
-
'environment.execute: only support one optimistic response per ' +
|
|
608
|
+
'OperationExecutor: environment.execute: only support one optimistic response per ' +
|
|
484
609
|
'execute.',
|
|
485
610
|
);
|
|
486
611
|
if (response == null && updater == null) {
|
|
487
612
|
return;
|
|
488
613
|
}
|
|
489
|
-
const optimisticUpdates: Array<OptimisticUpdate
|
|
614
|
+
const optimisticUpdates: Array<OptimisticUpdate<TMutation>> = [];
|
|
490
615
|
if (response) {
|
|
491
616
|
const payload = normalizeResponse(
|
|
492
617
|
response,
|
|
493
618
|
this._operation.root,
|
|
494
619
|
ROOT_TYPE,
|
|
495
620
|
{
|
|
621
|
+
actorIdentifier: this._actorIdentifier,
|
|
496
622
|
getDataID: this._getDataID,
|
|
497
623
|
path: [],
|
|
498
|
-
reactFlightPayloadDeserializer:
|
|
624
|
+
reactFlightPayloadDeserializer:
|
|
625
|
+
this._reactFlightPayloadDeserializer != null
|
|
626
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
627
|
+
: null,
|
|
499
628
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
629
|
+
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
500
630
|
treatMissingFieldsAsNull,
|
|
501
631
|
},
|
|
502
632
|
);
|
|
@@ -514,7 +644,7 @@ class Executor {
|
|
|
514
644
|
errors: null,
|
|
515
645
|
fieldPayloads: null,
|
|
516
646
|
incrementalPlaceholders: null,
|
|
517
|
-
|
|
647
|
+
followupPayloads: null,
|
|
518
648
|
source: RelayRecordSource.create(),
|
|
519
649
|
isFinal: false,
|
|
520
650
|
},
|
|
@@ -522,61 +652,93 @@ class Executor {
|
|
|
522
652
|
});
|
|
523
653
|
}
|
|
524
654
|
this._optimisticUpdates = optimisticUpdates;
|
|
525
|
-
optimisticUpdates.forEach(update =>
|
|
526
|
-
|
|
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();
|
|
527
661
|
}
|
|
528
662
|
|
|
529
663
|
_processOptimisticFollowups(
|
|
530
664
|
payload: RelayResponsePayload,
|
|
531
|
-
optimisticUpdates: Array<OptimisticUpdate
|
|
665
|
+
optimisticUpdates: Array<OptimisticUpdate<TMutation>>,
|
|
532
666
|
): void {
|
|
533
|
-
if (payload.
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
+
);
|
|
556
697
|
}
|
|
557
698
|
}
|
|
558
699
|
}
|
|
559
700
|
}
|
|
560
701
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
702
|
+
/**
|
|
703
|
+
* Normalize Data for @module payload, and actor-specific payload
|
|
704
|
+
*/
|
|
705
|
+
_normalizeFollowupPayload(
|
|
706
|
+
followupPayload: FollowupPayload,
|
|
707
|
+
normalizationNode: NormalizationSelectableNode,
|
|
564
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
|
+
}
|
|
565
722
|
const selector = createNormalizationSelector(
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
723
|
+
normalizationNode,
|
|
724
|
+
followupPayload.dataID,
|
|
725
|
+
variables,
|
|
569
726
|
);
|
|
570
727
|
return normalizeResponse(
|
|
571
|
-
{data:
|
|
728
|
+
{data: followupPayload.data},
|
|
572
729
|
selector,
|
|
573
|
-
|
|
730
|
+
followupPayload.typeName,
|
|
574
731
|
{
|
|
732
|
+
actorIdentifier: this._actorIdentifier,
|
|
575
733
|
getDataID: this._getDataID,
|
|
576
|
-
path:
|
|
577
|
-
reactFlightPayloadDeserializer:
|
|
734
|
+
path: followupPayload.path,
|
|
735
|
+
reactFlightPayloadDeserializer:
|
|
736
|
+
this._reactFlightPayloadDeserializer != null
|
|
737
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
738
|
+
: null,
|
|
578
739
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
579
740
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
741
|
+
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
580
742
|
},
|
|
581
743
|
);
|
|
582
744
|
}
|
|
@@ -584,10 +746,10 @@ class Executor {
|
|
|
584
746
|
_processOptimisticModuleImport(
|
|
585
747
|
normalizationRootNode: NormalizationRootNode,
|
|
586
748
|
moduleImportPayload: ModuleImportPayload,
|
|
587
|
-
): $ReadOnlyArray<OptimisticUpdate
|
|
749
|
+
): $ReadOnlyArray<OptimisticUpdate<TMutation>> {
|
|
588
750
|
const operation = getOperation(normalizationRootNode);
|
|
589
751
|
const optimisticUpdates = [];
|
|
590
|
-
const modulePayload = this.
|
|
752
|
+
const modulePayload = this._normalizeFollowupPayload(
|
|
591
753
|
moduleImportPayload,
|
|
592
754
|
operation,
|
|
593
755
|
);
|
|
@@ -602,41 +764,41 @@ class Executor {
|
|
|
602
764
|
}
|
|
603
765
|
|
|
604
766
|
_processAsyncOptimisticModuleImport(
|
|
605
|
-
operationLoader: OperationLoader,
|
|
606
767
|
moduleImportPayload: ModuleImportPayload,
|
|
607
768
|
): void {
|
|
608
|
-
|
|
769
|
+
this._expectOperationLoader()
|
|
609
770
|
.load(moduleImportPayload.operationReference)
|
|
610
771
|
.then(operation => {
|
|
611
772
|
if (operation == null || this._state !== 'started') {
|
|
612
773
|
return;
|
|
613
774
|
}
|
|
614
|
-
const moduleImportOptimisticUpdates =
|
|
615
|
-
operation,
|
|
616
|
-
moduleImportPayload,
|
|
617
|
-
);
|
|
775
|
+
const moduleImportOptimisticUpdates =
|
|
776
|
+
this._processOptimisticModuleImport(operation, moduleImportPayload);
|
|
618
777
|
moduleImportOptimisticUpdates.forEach(update =>
|
|
619
|
-
this.
|
|
778
|
+
this._getPublishQueueAndSaveActor().applyUpdate(update),
|
|
620
779
|
);
|
|
621
780
|
if (this._optimisticUpdates == null) {
|
|
622
781
|
warning(
|
|
623
782
|
false,
|
|
624
|
-
'
|
|
783
|
+
'OperationExecutor: Unexpected ModuleImport optimistic ' +
|
|
625
784
|
'update in operation %s.' +
|
|
626
785
|
this._operation.request.node.params.name,
|
|
627
786
|
);
|
|
628
787
|
} else {
|
|
629
788
|
this._optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
630
|
-
|
|
789
|
+
// OK: always have to run() after an module import resolves async
|
|
790
|
+
this._runPublishQueue();
|
|
631
791
|
}
|
|
632
792
|
});
|
|
633
793
|
}
|
|
634
794
|
|
|
635
|
-
_processResponses(
|
|
795
|
+
_processResponses(
|
|
796
|
+
responses: $ReadOnlyArray<GraphQLResponseWithData>,
|
|
797
|
+
): $ReadOnlyArray<RelayResponsePayload> {
|
|
636
798
|
if (this._optimisticUpdates !== null) {
|
|
637
|
-
this._optimisticUpdates.forEach(update =>
|
|
638
|
-
this.
|
|
639
|
-
);
|
|
799
|
+
this._optimisticUpdates.forEach(update => {
|
|
800
|
+
this._getPublishQueueAndSaveActor().revertUpdate(update);
|
|
801
|
+
});
|
|
640
802
|
this._optimisticUpdates = null;
|
|
641
803
|
}
|
|
642
804
|
|
|
@@ -649,18 +811,24 @@ class Executor {
|
|
|
649
811
|
this._operation.root,
|
|
650
812
|
ROOT_TYPE,
|
|
651
813
|
{
|
|
814
|
+
actorIdentifier: this._actorIdentifier,
|
|
652
815
|
getDataID: this._getDataID,
|
|
653
816
|
path: [],
|
|
654
|
-
reactFlightPayloadDeserializer:
|
|
817
|
+
reactFlightPayloadDeserializer:
|
|
818
|
+
this._reactFlightPayloadDeserializer != null
|
|
819
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
820
|
+
: null,
|
|
655
821
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
656
822
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
823
|
+
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
657
824
|
},
|
|
658
825
|
);
|
|
659
|
-
this.
|
|
826
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
660
827
|
this._operation,
|
|
661
828
|
relayPayload,
|
|
662
829
|
this._updater,
|
|
663
830
|
);
|
|
831
|
+
|
|
664
832
|
return relayPayload;
|
|
665
833
|
});
|
|
666
834
|
}
|
|
@@ -676,30 +844,30 @@ class Executor {
|
|
|
676
844
|
return;
|
|
677
845
|
}
|
|
678
846
|
payloads.forEach(payload => {
|
|
679
|
-
const {incrementalPlaceholders,
|
|
847
|
+
const {incrementalPlaceholders, followupPayloads, isFinal} = payload;
|
|
680
848
|
this._state = isFinal ? 'loading_final' : 'loading_incremental';
|
|
681
849
|
this._updateActiveState();
|
|
682
850
|
if (isFinal) {
|
|
683
851
|
this._incrementalPayloadsPending = false;
|
|
684
852
|
}
|
|
685
|
-
if (
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
moduleImportPayloads.forEach(moduleImportPayload => {
|
|
693
|
-
this._processModuleImportPayload(
|
|
694
|
-
moduleImportPayload,
|
|
695
|
-
operationLoader,
|
|
696
|
-
);
|
|
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;
|
|
697
860
|
});
|
|
698
861
|
}
|
|
862
|
+
|
|
699
863
|
if (incrementalPlaceholders && incrementalPlaceholders.length !== 0) {
|
|
700
864
|
this._incrementalPayloadsPending = this._state !== 'loading_final';
|
|
701
865
|
incrementalPlaceholders.forEach(incrementalPlaceholder => {
|
|
866
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
867
|
+
this._actorIdentifier =
|
|
868
|
+
incrementalPlaceholder.actorIdentifier ?? this._actorIdentifier;
|
|
702
869
|
this._processIncrementalPlaceholder(payload, incrementalPlaceholder);
|
|
870
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
703
871
|
});
|
|
704
872
|
|
|
705
873
|
if (this._isClientPayload || this._state === 'loading_final') {
|
|
@@ -731,8 +899,6 @@ class Executor {
|
|
|
731
899
|
}
|
|
732
900
|
});
|
|
733
901
|
if (relayPayloads.length > 0) {
|
|
734
|
-
const updatedOwners = this._publishQueue.run();
|
|
735
|
-
this._updateOperationTracker(updatedOwners);
|
|
736
902
|
this._processPayloadFollowups(relayPayloads);
|
|
737
903
|
}
|
|
738
904
|
}
|
|
@@ -741,9 +907,7 @@ class Executor {
|
|
|
741
907
|
}
|
|
742
908
|
|
|
743
909
|
_maybeCompleteSubscriptionOperationTracking() {
|
|
744
|
-
|
|
745
|
-
this._operation.request.node.params.operationKind === 'subscription';
|
|
746
|
-
if (!isSubscriptionOperation) {
|
|
910
|
+
if (!this._isSubscriptionOperation) {
|
|
747
911
|
return;
|
|
748
912
|
}
|
|
749
913
|
if (
|
|
@@ -761,73 +925,154 @@ class Executor {
|
|
|
761
925
|
* defer, stream, etc); these are handled by calling
|
|
762
926
|
* `_processPayloadFollowups()`.
|
|
763
927
|
*/
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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,
|
|
803
1014
|
});
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
+
);
|
|
817
1047
|
}
|
|
818
1048
|
}
|
|
819
1049
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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,
|
|
823
1067
|
): void {
|
|
824
|
-
const relayPayload = this.
|
|
825
|
-
|
|
826
|
-
|
|
1068
|
+
const relayPayload = this._normalizeFollowupPayload(
|
|
1069
|
+
followupPayload,
|
|
1070
|
+
normalizationNode,
|
|
1071
|
+
);
|
|
1072
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1073
|
+
this._operation,
|
|
1074
|
+
relayPayload,
|
|
827
1075
|
);
|
|
828
|
-
this._publishQueue.commitPayload(this._operation, relayPayload);
|
|
829
|
-
const updatedOwners = this._publishQueue.run();
|
|
830
|
-
this._updateOperationTracker(updatedOwners);
|
|
831
1076
|
this._processPayloadFollowups([relayPayload]);
|
|
832
1077
|
}
|
|
833
1078
|
|
|
@@ -873,7 +1118,7 @@ class Executor {
|
|
|
873
1118
|
(placeholder: empty);
|
|
874
1119
|
invariant(
|
|
875
1120
|
false,
|
|
876
|
-
'Unsupported incremental placeholder kind `%s`.',
|
|
1121
|
+
'OperationExecutor: Unsupported incremental placeholder kind `%s`.',
|
|
877
1122
|
placeholder.kind,
|
|
878
1123
|
);
|
|
879
1124
|
}
|
|
@@ -897,7 +1142,7 @@ class Executor {
|
|
|
897
1142
|
// exist.
|
|
898
1143
|
invariant(
|
|
899
1144
|
parentRecord != null,
|
|
900
|
-
'
|
|
1145
|
+
'OperationExecutor: Expected record `%s` to exist.',
|
|
901
1146
|
parentID,
|
|
902
1147
|
);
|
|
903
1148
|
let nextParentRecord;
|
|
@@ -926,14 +1171,12 @@ class Executor {
|
|
|
926
1171
|
record: nextParentRecord,
|
|
927
1172
|
fieldPayloads: nextParentPayloads,
|
|
928
1173
|
});
|
|
1174
|
+
|
|
929
1175
|
// If there were any queued responses, process them now that placeholders
|
|
930
1176
|
// are in place
|
|
931
1177
|
if (pendingResponses != null) {
|
|
932
|
-
const payloadFollowups =
|
|
933
|
-
pendingResponses
|
|
934
|
-
);
|
|
935
|
-
const updatedOwners = this._publishQueue.run();
|
|
936
|
-
this._updateOperationTracker(updatedOwners);
|
|
1178
|
+
const payloadFollowups =
|
|
1179
|
+
this._processIncrementalResponses(pendingResponses);
|
|
937
1180
|
this._processPayloadFollowups(payloadFollowups);
|
|
938
1181
|
}
|
|
939
1182
|
}
|
|
@@ -969,7 +1212,7 @@ class Executor {
|
|
|
969
1212
|
const placeholder = resultForPath.placeholder;
|
|
970
1213
|
invariant(
|
|
971
1214
|
placeholder.kind === 'defer',
|
|
972
|
-
'
|
|
1215
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
973
1216
|
'to be data for @defer, was `@%s`.',
|
|
974
1217
|
pathKey,
|
|
975
1218
|
label,
|
|
@@ -983,10 +1226,7 @@ class Executor {
|
|
|
983
1226
|
// but Relay records paths relative to the parent of the stream node:
|
|
984
1227
|
// therefore we strip the last two elements just to lookup the path
|
|
985
1228
|
// (the item index is used later to insert the element in the list)
|
|
986
|
-
const pathKey = path
|
|
987
|
-
.slice(0, -2)
|
|
988
|
-
.map(String)
|
|
989
|
-
.join('.');
|
|
1229
|
+
const pathKey = path.slice(0, -2).map(String).join('.');
|
|
990
1230
|
let resultForPath = resultForLabel.get(pathKey);
|
|
991
1231
|
if (resultForPath == null) {
|
|
992
1232
|
resultForPath = {kind: 'response', responses: [incrementalResponse]};
|
|
@@ -999,7 +1239,7 @@ class Executor {
|
|
|
999
1239
|
const placeholder = resultForPath.placeholder;
|
|
1000
1240
|
invariant(
|
|
1001
1241
|
placeholder.kind === 'stream',
|
|
1002
|
-
'
|
|
1242
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
1003
1243
|
'to be data for @stream, was `@%s`.',
|
|
1004
1244
|
pathKey,
|
|
1005
1245
|
label,
|
|
@@ -1020,26 +1260,37 @@ class Executor {
|
|
|
1020
1260
|
response: GraphQLResponseWithData,
|
|
1021
1261
|
): RelayResponsePayload {
|
|
1022
1262
|
const {dataID: parentID} = placeholder.selector;
|
|
1263
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1264
|
+
this._actorIdentifier =
|
|
1265
|
+
placeholder.actorIdentifier ?? this._actorIdentifier;
|
|
1023
1266
|
const relayPayload = normalizeResponse(
|
|
1024
1267
|
response,
|
|
1025
1268
|
placeholder.selector,
|
|
1026
1269
|
placeholder.typeName,
|
|
1027
1270
|
{
|
|
1271
|
+
actorIdentifier: this._actorIdentifier,
|
|
1028
1272
|
getDataID: this._getDataID,
|
|
1029
1273
|
path: placeholder.path,
|
|
1030
|
-
reactFlightPayloadDeserializer:
|
|
1274
|
+
reactFlightPayloadDeserializer:
|
|
1275
|
+
this._reactFlightPayloadDeserializer != null
|
|
1276
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1277
|
+
: null,
|
|
1031
1278
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1032
1279
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1280
|
+
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
1033
1281
|
},
|
|
1034
1282
|
);
|
|
1035
|
-
this.
|
|
1283
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1284
|
+
this._operation,
|
|
1285
|
+
relayPayload,
|
|
1286
|
+
);
|
|
1036
1287
|
|
|
1037
1288
|
// Load the version of the parent record from which this incremental data
|
|
1038
1289
|
// was derived
|
|
1039
1290
|
const parentEntry = this._source.get(parentID);
|
|
1040
1291
|
invariant(
|
|
1041
1292
|
parentEntry != null,
|
|
1042
|
-
'
|
|
1293
|
+
'OperationExecutor: Expected the parent record `%s` for @defer ' +
|
|
1043
1294
|
'data to exist.',
|
|
1044
1295
|
parentID,
|
|
1045
1296
|
);
|
|
@@ -1049,15 +1300,17 @@ class Executor {
|
|
|
1049
1300
|
errors: null,
|
|
1050
1301
|
fieldPayloads,
|
|
1051
1302
|
incrementalPlaceholders: null,
|
|
1052
|
-
|
|
1303
|
+
followupPayloads: null,
|
|
1053
1304
|
source: RelayRecordSource.create(),
|
|
1054
1305
|
isFinal: response.extensions?.is_final === true,
|
|
1055
1306
|
};
|
|
1056
|
-
this.
|
|
1307
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1057
1308
|
this._operation,
|
|
1058
1309
|
handleFieldsRelayPayload,
|
|
1059
1310
|
);
|
|
1060
1311
|
}
|
|
1312
|
+
|
|
1313
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1061
1314
|
return relayPayload;
|
|
1062
1315
|
}
|
|
1063
1316
|
|
|
@@ -1070,12 +1323,14 @@ class Executor {
|
|
|
1070
1323
|
placeholder: StreamPlaceholder,
|
|
1071
1324
|
response: GraphQLResponseWithData,
|
|
1072
1325
|
): RelayResponsePayload {
|
|
1073
|
-
const {parentID, node, variables} = placeholder;
|
|
1326
|
+
const {parentID, node, variables, actorIdentifier} = placeholder;
|
|
1327
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1328
|
+
this._actorIdentifier = actorIdentifier ?? this._actorIdentifier;
|
|
1074
1329
|
// Find the LinkedField where @stream was applied
|
|
1075
1330
|
const field = node.selections[0];
|
|
1076
1331
|
invariant(
|
|
1077
1332
|
field != null && field.kind === 'LinkedField' && field.plural === true,
|
|
1078
|
-
'
|
|
1333
|
+
'OperationExecutor: Expected @stream to be used on a plural field.',
|
|
1079
1334
|
);
|
|
1080
1335
|
const {
|
|
1081
1336
|
fieldPayloads,
|
|
@@ -1095,34 +1350,38 @@ class Executor {
|
|
|
1095
1350
|
// Publish the new item and update the parent record to set
|
|
1096
1351
|
// field[index] = item *if* the parent record hasn't been concurrently
|
|
1097
1352
|
// modified.
|
|
1098
|
-
this.
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
+
);
|
|
1126
1385
|
|
|
1127
1386
|
// Now that the parent record has been updated to include the new item,
|
|
1128
1387
|
// also update any handle fields that are derived from the parent record.
|
|
@@ -1131,15 +1390,17 @@ class Executor {
|
|
|
1131
1390
|
errors: null,
|
|
1132
1391
|
fieldPayloads,
|
|
1133
1392
|
incrementalPlaceholders: null,
|
|
1134
|
-
|
|
1393
|
+
followupPayloads: null,
|
|
1135
1394
|
source: RelayRecordSource.create(),
|
|
1136
1395
|
isFinal: false,
|
|
1137
1396
|
};
|
|
1138
|
-
this.
|
|
1397
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1139
1398
|
this._operation,
|
|
1140
1399
|
handleFieldsRelayPayload,
|
|
1141
1400
|
);
|
|
1142
1401
|
}
|
|
1402
|
+
|
|
1403
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1143
1404
|
return relayPayload;
|
|
1144
1405
|
}
|
|
1145
1406
|
|
|
@@ -1161,7 +1422,7 @@ class Executor {
|
|
|
1161
1422
|
const {data} = response;
|
|
1162
1423
|
invariant(
|
|
1163
1424
|
typeof data === 'object',
|
|
1164
|
-
'
|
|
1425
|
+
'OperationExecutor: Expected the GraphQL @stream payload `data` ' +
|
|
1165
1426
|
'value to be an object.',
|
|
1166
1427
|
);
|
|
1167
1428
|
const responseKey = field.alias ?? field.name;
|
|
@@ -1172,7 +1433,7 @@ class Executor {
|
|
|
1172
1433
|
const parentEntry = this._source.get(parentID);
|
|
1173
1434
|
invariant(
|
|
1174
1435
|
parentEntry != null,
|
|
1175
|
-
'
|
|
1436
|
+
'OperationExecutor: Expected the parent record `%s` for @stream ' +
|
|
1176
1437
|
'data to exist.',
|
|
1177
1438
|
parentID,
|
|
1178
1439
|
);
|
|
@@ -1187,7 +1448,7 @@ class Executor {
|
|
|
1187
1448
|
);
|
|
1188
1449
|
invariant(
|
|
1189
1450
|
prevIDs != null,
|
|
1190
|
-
'
|
|
1451
|
+
'OperationExecutor: Expected record `%s` to have fetched field ' +
|
|
1191
1452
|
'`%s` with @stream.',
|
|
1192
1453
|
parentID,
|
|
1193
1454
|
field.name,
|
|
@@ -1198,7 +1459,7 @@ class Executor {
|
|
|
1198
1459
|
const itemIndex = parseInt(finalPathEntry, 10);
|
|
1199
1460
|
invariant(
|
|
1200
1461
|
itemIndex === finalPathEntry && itemIndex >= 0,
|
|
1201
|
-
'
|
|
1462
|
+
'OperationExecutor: Expected path for @stream to end in a ' +
|
|
1202
1463
|
'positive integer index, got `%s`',
|
|
1203
1464
|
finalPathEntry,
|
|
1204
1465
|
);
|
|
@@ -1206,7 +1467,7 @@ class Executor {
|
|
|
1206
1467
|
const typeName = field.concreteType ?? data[TYPENAME_KEY];
|
|
1207
1468
|
invariant(
|
|
1208
1469
|
typeof typeName === 'string',
|
|
1209
|
-
'
|
|
1470
|
+
'OperationExecutor: Expected @stream field `%s` to have a ' +
|
|
1210
1471
|
'__typename.',
|
|
1211
1472
|
field.name,
|
|
1212
1473
|
);
|
|
@@ -1221,7 +1482,7 @@ class Executor {
|
|
|
1221
1482
|
generateClientID(parentID, storageKey, itemIndex);
|
|
1222
1483
|
invariant(
|
|
1223
1484
|
typeof itemID === 'string',
|
|
1224
|
-
'
|
|
1485
|
+
'OperationExecutor: Expected id of elements of field `%s` to ' +
|
|
1225
1486
|
'be strings.',
|
|
1226
1487
|
storageKey,
|
|
1227
1488
|
);
|
|
@@ -1241,11 +1502,16 @@ class Executor {
|
|
|
1241
1502
|
fieldPayloads,
|
|
1242
1503
|
});
|
|
1243
1504
|
const relayPayload = normalizeResponse(response, selector, typeName, {
|
|
1505
|
+
actorIdentifier: this._actorIdentifier,
|
|
1244
1506
|
getDataID: this._getDataID,
|
|
1245
1507
|
path: [...normalizationPath, responseKey, String(itemIndex)],
|
|
1246
|
-
reactFlightPayloadDeserializer:
|
|
1508
|
+
reactFlightPayloadDeserializer:
|
|
1509
|
+
this._reactFlightPayloadDeserializer != null
|
|
1510
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1511
|
+
: null,
|
|
1247
1512
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1248
1513
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1514
|
+
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
1249
1515
|
});
|
|
1250
1516
|
return {
|
|
1251
1517
|
fieldPayloads,
|
|
@@ -1257,14 +1523,29 @@ class Executor {
|
|
|
1257
1523
|
};
|
|
1258
1524
|
}
|
|
1259
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
|
+
|
|
1260
1545
|
_updateOperationTracker(
|
|
1261
1546
|
updatedOwners: ?$ReadOnlyArray<RequestDescriptor>,
|
|
1262
1547
|
): void {
|
|
1263
|
-
if (
|
|
1264
|
-
this._operationTracker != null &&
|
|
1265
|
-
updatedOwners != null &&
|
|
1266
|
-
updatedOwners.length > 0
|
|
1267
|
-
) {
|
|
1548
|
+
if (updatedOwners != null && updatedOwners.length > 0) {
|
|
1268
1549
|
this._operationTracker.update(
|
|
1269
1550
|
this._operation.request,
|
|
1270
1551
|
new Set(updatedOwners),
|
|
@@ -1273,9 +1554,59 @@ class Executor {
|
|
|
1273
1554
|
}
|
|
1274
1555
|
|
|
1275
1556
|
_completeOperationTracker() {
|
|
1276
|
-
|
|
1277
|
-
|
|
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;
|
|
1570
|
+
}
|
|
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));
|
|
1278
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;
|
|
1279
1610
|
}
|
|
1280
1611
|
}
|
|
1281
1612
|
|
|
@@ -1293,7 +1624,7 @@ function partitionGraphQLResponses(
|
|
|
1293
1624
|
if (label == null || path == null) {
|
|
1294
1625
|
invariant(
|
|
1295
1626
|
false,
|
|
1296
|
-
'
|
|
1627
|
+
'OperationExecutor: invalid incremental payload, expected ' +
|
|
1297
1628
|
'`path` and `label` to either both be null/undefined, or ' +
|
|
1298
1629
|
'`path` to be an `Array<string | number>` and `label` to be a ' +
|
|
1299
1630
|
'`string`.',
|
|
@@ -1345,11 +1676,13 @@ function validateOptimisticResponsePayload(
|
|
|
1345
1676
|
if (incrementalPlaceholders != null && incrementalPlaceholders.length !== 0) {
|
|
1346
1677
|
invariant(
|
|
1347
1678
|
false,
|
|
1348
|
-
'
|
|
1679
|
+
'OperationExecutor: optimistic responses cannot be returned ' +
|
|
1349
1680
|
'for operations that use incremental data delivery (@defer, ' +
|
|
1350
1681
|
'@stream, and @stream_connection).',
|
|
1351
1682
|
);
|
|
1352
1683
|
}
|
|
1353
1684
|
}
|
|
1354
1685
|
|
|
1355
|
-
module.exports = {
|
|
1686
|
+
module.exports = {
|
|
1687
|
+
execute,
|
|
1688
|
+
};
|