relay-runtime 11.0.2 → 13.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/handlers/RelayDefaultHandlerProvider.js.flow +3 -3
- package/handlers/connection/ConnectionHandler.js.flow +9 -18
- package/handlers/connection/ConnectionInterface.js.flow +1 -1
- package/handlers/connection/MutationHandlers.js.flow +8 -12
- package/index.js +1 -1
- package/index.js.flow +57 -35
- package/lib/handlers/RelayDefaultHandlerProvider.js +1 -1
- package/lib/handlers/connection/ConnectionHandler.js +13 -19
- package/lib/handlers/connection/ConnectionInterface.js +1 -1
- package/lib/handlers/connection/MutationHandlers.js +4 -7
- package/lib/index.js +59 -44
- package/lib/multi-actor-environment/ActorIdentifier.js +12 -2
- package/lib/multi-actor-environment/ActorSpecificEnvironment.js +64 -20
- package/lib/multi-actor-environment/ActorUtils.js +27 -0
- package/lib/multi-actor-environment/MultiActorEnvironment.js +324 -61
- package/lib/multi-actor-environment/MultiActorEnvironmentTypes.js +1 -1
- package/lib/multi-actor-environment/index.js +6 -2
- package/lib/mutations/RelayDeclarativeMutationConfig.js +5 -2
- package/lib/mutations/RelayRecordProxy.js +4 -3
- package/lib/mutations/RelayRecordSourceMutator.js +4 -3
- package/lib/mutations/RelayRecordSourceProxy.js +13 -5
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +19 -6
- package/lib/mutations/applyOptimisticMutation.js +7 -7
- package/lib/mutations/commitLocalUpdate.js +1 -1
- package/lib/mutations/commitMutation.js +15 -11
- package/lib/mutations/readUpdatableQuery_EXPERIMENTAL.js +242 -0
- package/lib/mutations/validateMutation.js +11 -6
- package/lib/network/ConvertToExecuteFunction.js +3 -2
- package/lib/network/RelayNetwork.js +4 -3
- package/lib/network/RelayNetworkTypes.js +1 -1
- package/lib/network/RelayObservable.js +1 -1
- package/lib/network/RelayQueryResponseCache.js +22 -6
- package/lib/network/wrapNetworkWithLogObserver.js +79 -0
- package/lib/query/GraphQLTag.js +3 -2
- package/lib/query/PreloadableQueryRegistry.js +1 -1
- package/lib/query/fetchQuery.js +7 -6
- package/lib/query/fetchQueryInternal.js +1 -1
- package/lib/query/fetchQuery_DEPRECATED.js +3 -2
- package/lib/store/ClientID.js +8 -2
- package/lib/store/DataChecker.js +124 -55
- package/lib/store/OperationExecutor.js +489 -215
- package/lib/store/RelayConcreteVariables.js +27 -9
- package/lib/store/RelayExperimentalGraphResponseHandler.js +153 -0
- package/lib/store/RelayExperimentalGraphResponseTransform.js +391 -0
- package/lib/store/RelayModernEnvironment.js +100 -120
- package/lib/store/RelayModernFragmentSpecResolver.js +53 -27
- package/lib/store/RelayModernOperationDescriptor.js +3 -2
- package/lib/store/RelayModernRecord.js +48 -13
- package/lib/store/RelayModernSelector.js +15 -9
- package/lib/store/RelayModernStore.js +56 -23
- package/lib/store/RelayOperationTracker.js +34 -24
- package/lib/store/RelayOptimisticRecordSource.js +1 -1
- package/lib/store/RelayPublishQueue.js +35 -11
- package/lib/store/RelayReader.js +257 -72
- package/lib/store/RelayRecordSource.js +88 -4
- package/lib/store/RelayRecordState.js +1 -1
- package/lib/store/RelayReferenceMarker.js +34 -22
- package/lib/store/RelayResponseNormalizer.js +172 -96
- package/lib/store/RelayStoreReactFlightUtils.js +5 -11
- package/lib/store/RelayStoreSubscriptions.js +15 -10
- package/lib/store/RelayStoreTypes.js +1 -1
- package/lib/store/RelayStoreUtils.js +13 -8
- package/lib/store/ResolverCache.js +213 -0
- package/lib/store/ResolverFragments.js +10 -6
- package/lib/store/StoreInspector.js +1 -1
- package/lib/store/TypeID.js +1 -1
- package/lib/store/ViewerPattern.js +1 -1
- package/lib/store/cloneRelayHandleSourceField.js +6 -5
- package/lib/store/cloneRelayScalarHandleSourceField.js +6 -5
- package/lib/store/createFragmentSpecResolver.js +1 -1
- package/lib/store/createRelayContext.js +5 -3
- package/lib/store/defaultGetDataID.js +1 -1
- package/lib/store/defaultRequiredFieldLogger.js +1 -1
- package/lib/store/hasOverlappingIDs.js +1 -1
- package/lib/store/isRelayModernEnvironment.js +1 -1
- package/lib/store/normalizeRelayPayload.js +1 -1
- package/lib/store/readInlineData.js +7 -3
- package/lib/subscription/requestSubscription.js +32 -34
- package/lib/util/JSResourceTypes.flow.js +1 -1
- package/lib/util/NormalizationNode.js +1 -1
- package/lib/util/ReaderNode.js +1 -1
- package/lib/util/RelayConcreteNode.js +3 -1
- package/lib/util/RelayDefaultHandleKey.js +1 -1
- package/lib/util/RelayError.js +1 -1
- package/lib/util/RelayFeatureFlags.js +10 -7
- package/lib/util/RelayProfiler.js +1 -1
- package/lib/util/RelayReplaySubject.js +22 -7
- package/lib/util/RelayRuntimeTypes.js +1 -7
- package/lib/util/StringInterner.js +71 -0
- package/lib/util/createPayloadFor3DField.js +1 -1
- package/lib/util/deepFreeze.js +1 -1
- package/lib/util/generateID.js +1 -1
- package/lib/util/getAllRootVariables.js +29 -0
- package/lib/util/getFragmentIdentifier.js +16 -8
- package/lib/util/getOperation.js +3 -2
- package/lib/util/getPaginationMetadata.js +41 -0
- package/lib/util/getPaginationVariables.js +66 -0
- package/lib/util/getPendingOperationsForFragment.js +55 -0
- package/lib/util/getRefetchMetadata.js +36 -0
- package/lib/util/getRelayHandleKey.js +3 -3
- package/lib/util/getRequestIdentifier.js +3 -3
- package/lib/util/getValueAtPath.js +51 -0
- package/lib/util/isEmptyObject.js +2 -2
- package/lib/util/isPromise.js +1 -1
- package/lib/util/isScalarAndEqual.js +1 -1
- package/lib/util/recycleNodesInto.js +1 -1
- package/lib/util/registerEnvironmentWithDevTools.js +26 -0
- package/lib/util/reportMissingRequiredFields.js +1 -1
- package/lib/util/resolveImmediate.js +1 -1
- package/lib/util/stableCopy.js +1 -1
- package/lib/util/withDuration.js +31 -0
- package/multi-actor-environment/ActorIdentifier.js.flow +18 -2
- package/multi-actor-environment/ActorSpecificEnvironment.js.flow +94 -58
- package/multi-actor-environment/ActorUtils.js.flow +33 -0
- package/multi-actor-environment/MultiActorEnvironment.js.flow +366 -93
- package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +88 -23
- package/multi-actor-environment/index.js.flow +3 -1
- package/mutations/RelayDeclarativeMutationConfig.js.flow +33 -27
- package/mutations/RelayRecordProxy.js.flow +5 -6
- package/mutations/RelayRecordSourceMutator.js.flow +5 -7
- package/mutations/RelayRecordSourceProxy.js.flow +20 -11
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +23 -8
- package/mutations/applyOptimisticMutation.js.flow +14 -15
- package/mutations/commitLocalUpdate.js.flow +2 -2
- package/mutations/commitMutation.js.flow +36 -47
- package/mutations/readUpdatableQuery_EXPERIMENTAL.js.flow +318 -0
- package/mutations/validateMutation.js.flow +27 -17
- package/network/ConvertToExecuteFunction.js.flow +3 -3
- package/network/RelayNetwork.js.flow +5 -6
- package/network/RelayNetworkTypes.js.flow +1 -1
- package/network/RelayObservable.js.flow +2 -2
- package/network/RelayQueryResponseCache.js.flow +35 -22
- package/network/wrapNetworkWithLogObserver.js.flow +99 -0
- package/package.json +2 -2
- package/query/GraphQLTag.js.flow +11 -11
- package/query/PreloadableQueryRegistry.js.flow +5 -3
- package/query/fetchQuery.js.flow +19 -19
- package/query/fetchQueryInternal.js.flow +7 -10
- package/query/fetchQuery_DEPRECATED.js.flow +7 -7
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +15 -4
- package/store/DataChecker.js.flow +142 -60
- package/store/OperationExecutor.js.flow +575 -320
- package/store/RelayConcreteVariables.js.flow +28 -9
- package/store/RelayExperimentalGraphResponseHandler.js.flow +121 -0
- package/store/RelayExperimentalGraphResponseTransform.js.flow +470 -0
- package/store/RelayModernEnvironment.js.flow +91 -115
- package/store/RelayModernFragmentSpecResolver.js.flow +56 -32
- package/store/RelayModernOperationDescriptor.js.flow +13 -8
- package/store/RelayModernRecord.js.flow +68 -12
- package/store/RelayModernSelector.js.flow +25 -15
- package/store/RelayModernStore.js.flow +67 -32
- package/store/RelayOperationTracker.js.flow +60 -44
- package/store/RelayOptimisticRecordSource.js.flow +3 -3
- package/store/RelayPublishQueue.js.flow +74 -32
- package/store/RelayReader.js.flow +319 -100
- package/store/RelayRecordSource.js.flow +73 -7
- package/store/RelayRecordState.js.flow +1 -1
- package/store/RelayReferenceMarker.js.flow +41 -27
- package/store/RelayResponseNormalizer.js.flow +204 -86
- package/store/RelayStoreReactFlightUtils.js.flow +5 -12
- package/store/RelayStoreSubscriptions.js.flow +20 -12
- package/store/RelayStoreTypes.js.flow +200 -41
- package/store/RelayStoreUtils.js.flow +25 -12
- package/store/ResolverCache.js.flow +249 -0
- package/store/ResolverFragments.js.flow +16 -20
- package/store/StoreInspector.js.flow +3 -3
- package/store/TypeID.js.flow +2 -2
- package/store/ViewerPattern.js.flow +3 -3
- package/store/cloneRelayHandleSourceField.js.flow +6 -7
- package/store/cloneRelayScalarHandleSourceField.js.flow +6 -7
- package/store/createFragmentSpecResolver.js.flow +4 -5
- package/store/createRelayContext.js.flow +4 -4
- package/store/defaultGetDataID.js.flow +1 -1
- package/store/defaultRequiredFieldLogger.js.flow +1 -1
- package/store/hasOverlappingIDs.js.flow +1 -1
- package/store/isRelayModernEnvironment.js.flow +1 -1
- package/store/normalizeRelayPayload.js.flow +7 -8
- package/store/readInlineData.js.flow +8 -9
- package/subscription/requestSubscription.js.flow +55 -51
- package/util/JSResourceTypes.flow.js.flow +1 -1
- package/util/NormalizationNode.js.flow +11 -4
- package/util/ReaderNode.js.flow +25 -2
- package/util/RelayConcreteNode.js.flow +5 -1
- package/util/RelayDefaultHandleKey.js.flow +1 -1
- package/util/RelayError.js.flow +1 -1
- package/util/RelayFeatureFlags.js.flow +23 -15
- package/util/RelayProfiler.js.flow +1 -1
- package/util/RelayReplaySubject.js.flow +10 -10
- package/util/RelayRuntimeTypes.js.flow +70 -3
- package/util/StringInterner.js.flow +69 -0
- package/util/createPayloadFor3DField.js.flow +4 -4
- package/util/deepFreeze.js.flow +1 -1
- package/util/generateID.js.flow +1 -1
- package/util/getAllRootVariables.js.flow +36 -0
- package/util/getFragmentIdentifier.js.flow +28 -16
- package/util/getOperation.js.flow +3 -3
- package/util/getPaginationMetadata.js.flow +69 -0
- package/util/getPaginationVariables.js.flow +108 -0
- package/util/getPendingOperationsForFragment.js.flow +62 -0
- package/util/getRefetchMetadata.js.flow +76 -0
- package/util/getRelayHandleKey.js.flow +2 -3
- package/util/getRequestIdentifier.js.flow +4 -4
- package/util/getValueAtPath.js.flow +46 -0
- package/util/isEmptyObject.js.flow +2 -1
- package/util/isPromise.js.flow +1 -1
- package/util/isScalarAndEqual.js.flow +1 -1
- package/util/recycleNodesInto.js.flow +1 -1
- package/util/registerEnvironmentWithDevTools.js.flow +33 -0
- package/util/reportMissingRequiredFields.js.flow +1 -1
- package/util/resolveImmediate.js.flow +2 -2
- package/util/stableCopy.js.flow +1 -1
- package/util/withDuration.js.flow +32 -0
- package/lib/store/RelayRecordSourceMapImpl.js +0 -107
- package/lib/store/RelayStoreSubscriptionsUsingMapByID.js +0 -318
- package/store/RelayRecordSourceMapImpl.js.flow +0 -91
- package/store/RelayStoreSubscriptionsUsingMapByID.js.flow +0 -283
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
@@ -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
89
|
+operationTracker: OperationTracker,
|
|
80
|
-
+optimisticConfig: ?OptimisticResponseConfig
|
|
81
|
-
+publishQueue: PublishQueue,
|
|
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
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,19 +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
|
|
248
|
-
this.
|
|
282
|
+
this._runPublishQueue();
|
|
249
283
|
}
|
|
250
284
|
this._incrementalResults.clear();
|
|
251
|
-
this.
|
|
252
|
-
|
|
253
|
-
this.
|
|
254
|
-
this._retainDisposable = null;
|
|
285
|
+
if (this._asyncStoreUpdateDisposable != null) {
|
|
286
|
+
this._asyncStoreUpdateDisposable.dispose();
|
|
287
|
+
this._asyncStoreUpdateDisposable = null;
|
|
255
288
|
}
|
|
289
|
+
this._completeFns = [];
|
|
290
|
+
this._completeOperationTracker();
|
|
291
|
+
this._disposeRetainedData();
|
|
256
292
|
}
|
|
257
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
|
+
|
|
258
314
|
_updateActiveState(): void {
|
|
259
315
|
let activeState;
|
|
260
316
|
switch (this._state) {
|
|
@@ -314,12 +370,21 @@ class Executor {
|
|
|
314
370
|
if (this._subscriptions.size === 0) {
|
|
315
371
|
this.cancel();
|
|
316
372
|
this._sink.complete();
|
|
373
|
+
this._log({
|
|
374
|
+
name: 'execute.complete',
|
|
375
|
+
executeId: this._executeId,
|
|
376
|
+
});
|
|
317
377
|
}
|
|
318
378
|
}
|
|
319
379
|
|
|
320
380
|
_error(error: Error): void {
|
|
321
381
|
this.cancel();
|
|
322
382
|
this._sink.error(error);
|
|
383
|
+
this._log({
|
|
384
|
+
name: 'execute.error',
|
|
385
|
+
executeId: this._executeId,
|
|
386
|
+
error,
|
|
387
|
+
});
|
|
323
388
|
}
|
|
324
389
|
|
|
325
390
|
_start(id: number, subscription: Subscription): void {
|
|
@@ -330,8 +395,16 @@ class Executor {
|
|
|
330
395
|
// Handle a raw GraphQL response.
|
|
331
396
|
_next(_id: number, response: GraphQLResponse): void {
|
|
332
397
|
this._schedule(() => {
|
|
333
|
-
|
|
334
|
-
|
|
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
|
+
});
|
|
335
408
|
});
|
|
336
409
|
}
|
|
337
410
|
|
|
@@ -375,7 +448,8 @@ class Executor {
|
|
|
375
448
|
error.stack;
|
|
376
449
|
throw error;
|
|
377
450
|
} else {
|
|
378
|
-
const responseWithData: GraphQLResponseWithData =
|
|
451
|
+
const responseWithData: GraphQLResponseWithData =
|
|
452
|
+
(response: $FlowFixMe);
|
|
379
453
|
results.push(responseWithData);
|
|
380
454
|
}
|
|
381
455
|
});
|
|
@@ -395,7 +469,10 @@ class Executor {
|
|
|
395
469
|
responsePart => responsePart.extensions?.isOptimistic === true,
|
|
396
470
|
)
|
|
397
471
|
) {
|
|
398
|
-
invariant(
|
|
472
|
+
invariant(
|
|
473
|
+
false,
|
|
474
|
+
'OperationExecutor: Optimistic responses cannot be batched.',
|
|
475
|
+
);
|
|
399
476
|
}
|
|
400
477
|
return false;
|
|
401
478
|
}
|
|
@@ -423,6 +500,7 @@ class Executor {
|
|
|
423
500
|
if (this._state === 'completed') {
|
|
424
501
|
return;
|
|
425
502
|
}
|
|
503
|
+
this._seenActors.clear();
|
|
426
504
|
|
|
427
505
|
const responses = Array.isArray(response) ? response : [response];
|
|
428
506
|
const responsesWithData = this._handleErrorResponse(responses);
|
|
@@ -446,10 +524,8 @@ class Executor {
|
|
|
446
524
|
return;
|
|
447
525
|
}
|
|
448
526
|
|
|
449
|
-
const [
|
|
450
|
-
|
|
451
|
-
incrementalResponses,
|
|
452
|
-
] = partitionGraphQLResponses(responsesWithData);
|
|
527
|
+
const [nonIncrementalResponses, incrementalResponses] =
|
|
528
|
+
partitionGraphQLResponses(responsesWithData);
|
|
453
529
|
const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
|
|
454
530
|
|
|
455
531
|
// In theory this doesn't preserve the ordering of the batch.
|
|
@@ -460,39 +536,38 @@ class Executor {
|
|
|
460
536
|
// with the initial payload followed by some early-to-resolve incremental
|
|
461
537
|
// payloads (although, can that even happen?)
|
|
462
538
|
if (hasNonIncrementalResponses) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
+
};
|
|
468
558
|
}
|
|
469
559
|
|
|
560
|
+
const payloadFollowups = this._processResponses(nonIncrementalResponses);
|
|
470
561
|
this._processPayloadFollowups(payloadFollowups);
|
|
471
|
-
|
|
472
|
-
if (!RelayFeatureFlags.ENABLE_BATCHED_STORE_UPDATES) {
|
|
473
|
-
if (this._incrementalPayloadsPending && !this._retainDisposable) {
|
|
474
|
-
this._retainDisposable = this._store.retain(this._operation);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
562
|
}
|
|
478
563
|
|
|
479
564
|
if (incrementalResponses.length > 0) {
|
|
480
|
-
const payloadFollowups =
|
|
481
|
-
incrementalResponses
|
|
482
|
-
|
|
483
|
-
if (!RelayFeatureFlags.ENABLE_BATCHED_STORE_UPDATES) {
|
|
484
|
-
// For the incremental case, we're only handling follow-up responses
|
|
485
|
-
// for already initiated operation (and we're not passing it to
|
|
486
|
-
// the run(...) call)
|
|
487
|
-
const updatedOwners = this._publishQueue.run();
|
|
488
|
-
this._updateOperationTracker(updatedOwners);
|
|
489
|
-
}
|
|
565
|
+
const payloadFollowups =
|
|
566
|
+
this._processIncrementalResponses(incrementalResponses);
|
|
567
|
+
|
|
490
568
|
this._processPayloadFollowups(payloadFollowups);
|
|
491
569
|
}
|
|
492
|
-
if (
|
|
493
|
-
this._isSubscriptionOperation &&
|
|
494
|
-
RelayFeatureFlags.ENABLE_UNIQUE_SUBSCRIPTION_ROOT
|
|
495
|
-
) {
|
|
570
|
+
if (this._isSubscriptionOperation) {
|
|
496
571
|
// We attach the id to allow the `requestSubscription` to read from the store using
|
|
497
572
|
// the current id in its `onNext` callback
|
|
498
573
|
if (responsesWithData[0].extensions == null) {
|
|
@@ -501,51 +576,55 @@ class Executor {
|
|
|
501
576
|
__relay_subscription_root_id: this._operation.fragment.dataID,
|
|
502
577
|
};
|
|
503
578
|
} else {
|
|
504
|
-
responsesWithData[0].extensions.__relay_subscription_root_id =
|
|
579
|
+
responsesWithData[0].extensions.__relay_subscription_root_id =
|
|
580
|
+
this._operation.fragment.dataID;
|
|
505
581
|
}
|
|
506
582
|
}
|
|
507
583
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
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();
|
|
520
595
|
}
|
|
521
|
-
this._updateOperationTracker(updatedOwners);
|
|
522
596
|
}
|
|
597
|
+
this._updateOperationTracker(updatedOwners);
|
|
523
598
|
this._sink.next(response);
|
|
524
599
|
}
|
|
525
600
|
|
|
526
601
|
_processOptimisticResponse(
|
|
527
602
|
response: ?GraphQLResponseWithData,
|
|
528
|
-
updater: ?SelectorStoreUpdater
|
|
603
|
+
updater: ?SelectorStoreUpdater<TMutation['response']>,
|
|
529
604
|
treatMissingFieldsAsNull: boolean,
|
|
530
605
|
): void {
|
|
531
606
|
invariant(
|
|
532
607
|
this._optimisticUpdates === null,
|
|
533
|
-
'environment.execute: only support one optimistic response per ' +
|
|
608
|
+
'OperationExecutor: environment.execute: only support one optimistic response per ' +
|
|
534
609
|
'execute.',
|
|
535
610
|
);
|
|
536
611
|
if (response == null && updater == null) {
|
|
537
612
|
return;
|
|
538
613
|
}
|
|
539
|
-
const optimisticUpdates: Array<OptimisticUpdate
|
|
614
|
+
const optimisticUpdates: Array<OptimisticUpdate<TMutation>> = [];
|
|
540
615
|
if (response) {
|
|
541
616
|
const payload = normalizeResponse(
|
|
542
617
|
response,
|
|
543
618
|
this._operation.root,
|
|
544
619
|
ROOT_TYPE,
|
|
545
620
|
{
|
|
621
|
+
actorIdentifier: this._actorIdentifier,
|
|
546
622
|
getDataID: this._getDataID,
|
|
547
623
|
path: [],
|
|
548
|
-
reactFlightPayloadDeserializer:
|
|
624
|
+
reactFlightPayloadDeserializer:
|
|
625
|
+
this._reactFlightPayloadDeserializer != null
|
|
626
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
627
|
+
: null,
|
|
549
628
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
550
629
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
551
630
|
treatMissingFieldsAsNull,
|
|
@@ -565,7 +644,7 @@ class Executor {
|
|
|
565
644
|
errors: null,
|
|
566
645
|
fieldPayloads: null,
|
|
567
646
|
incrementalPlaceholders: null,
|
|
568
|
-
|
|
647
|
+
followupPayloads: null,
|
|
569
648
|
source: RelayRecordSource.create(),
|
|
570
649
|
isFinal: false,
|
|
571
650
|
},
|
|
@@ -573,61 +652,90 @@ class Executor {
|
|
|
573
652
|
});
|
|
574
653
|
}
|
|
575
654
|
this._optimisticUpdates = optimisticUpdates;
|
|
576
|
-
optimisticUpdates.forEach(update =>
|
|
655
|
+
optimisticUpdates.forEach(update =>
|
|
656
|
+
this._getPublishQueueAndSaveActor().applyUpdate(update),
|
|
657
|
+
);
|
|
577
658
|
// OK: only called on construction and when receiving an optimistic payload from network,
|
|
578
659
|
// which doesn't fall-through to the regular next() handling
|
|
579
|
-
this.
|
|
660
|
+
this._runPublishQueue();
|
|
580
661
|
}
|
|
581
662
|
|
|
582
663
|
_processOptimisticFollowups(
|
|
583
664
|
payload: RelayResponsePayload,
|
|
584
|
-
optimisticUpdates: Array<OptimisticUpdate
|
|
665
|
+
optimisticUpdates: Array<OptimisticUpdate<TMutation>>,
|
|
585
666
|
): void {
|
|
586
|
-
if (payload.
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
+
);
|
|
609
697
|
}
|
|
610
698
|
}
|
|
611
699
|
}
|
|
612
700
|
}
|
|
613
701
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
702
|
+
/**
|
|
703
|
+
* Normalize Data for @module payload, and actor-specific payload
|
|
704
|
+
*/
|
|
705
|
+
_normalizeFollowupPayload(
|
|
706
|
+
followupPayload: FollowupPayload,
|
|
707
|
+
normalizationNode: NormalizationSelectableNode,
|
|
617
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
|
+
}
|
|
618
722
|
const selector = createNormalizationSelector(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
723
|
+
normalizationNode,
|
|
724
|
+
followupPayload.dataID,
|
|
725
|
+
variables,
|
|
622
726
|
);
|
|
623
727
|
return normalizeResponse(
|
|
624
|
-
{data:
|
|
728
|
+
{data: followupPayload.data},
|
|
625
729
|
selector,
|
|
626
|
-
|
|
730
|
+
followupPayload.typeName,
|
|
627
731
|
{
|
|
732
|
+
actorIdentifier: this._actorIdentifier,
|
|
628
733
|
getDataID: this._getDataID,
|
|
629
|
-
path:
|
|
630
|
-
reactFlightPayloadDeserializer:
|
|
734
|
+
path: followupPayload.path,
|
|
735
|
+
reactFlightPayloadDeserializer:
|
|
736
|
+
this._reactFlightPayloadDeserializer != null
|
|
737
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
738
|
+
: null,
|
|
631
739
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
632
740
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
633
741
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
@@ -638,10 +746,10 @@ class Executor {
|
|
|
638
746
|
_processOptimisticModuleImport(
|
|
639
747
|
normalizationRootNode: NormalizationRootNode,
|
|
640
748
|
moduleImportPayload: ModuleImportPayload,
|
|
641
|
-
): $ReadOnlyArray<OptimisticUpdate
|
|
749
|
+
): $ReadOnlyArray<OptimisticUpdate<TMutation>> {
|
|
642
750
|
const operation = getOperation(normalizationRootNode);
|
|
643
751
|
const optimisticUpdates = [];
|
|
644
|
-
const modulePayload = this.
|
|
752
|
+
const modulePayload = this._normalizeFollowupPayload(
|
|
645
753
|
moduleImportPayload,
|
|
646
754
|
operation,
|
|
647
755
|
);
|
|
@@ -656,21 +764,18 @@ class Executor {
|
|
|
656
764
|
}
|
|
657
765
|
|
|
658
766
|
_processAsyncOptimisticModuleImport(
|
|
659
|
-
operationLoader: OperationLoader,
|
|
660
767
|
moduleImportPayload: ModuleImportPayload,
|
|
661
768
|
): void {
|
|
662
|
-
|
|
769
|
+
this._expectOperationLoader()
|
|
663
770
|
.load(moduleImportPayload.operationReference)
|
|
664
771
|
.then(operation => {
|
|
665
772
|
if (operation == null || this._state !== 'started') {
|
|
666
773
|
return;
|
|
667
774
|
}
|
|
668
|
-
const moduleImportOptimisticUpdates =
|
|
669
|
-
operation,
|
|
670
|
-
moduleImportPayload,
|
|
671
|
-
);
|
|
775
|
+
const moduleImportOptimisticUpdates =
|
|
776
|
+
this._processOptimisticModuleImport(operation, moduleImportPayload);
|
|
672
777
|
moduleImportOptimisticUpdates.forEach(update =>
|
|
673
|
-
this.
|
|
778
|
+
this._getPublishQueueAndSaveActor().applyUpdate(update),
|
|
674
779
|
);
|
|
675
780
|
if (this._optimisticUpdates == null) {
|
|
676
781
|
warning(
|
|
@@ -682,16 +787,18 @@ class Executor {
|
|
|
682
787
|
} else {
|
|
683
788
|
this._optimisticUpdates.push(...moduleImportOptimisticUpdates);
|
|
684
789
|
// OK: always have to run() after an module import resolves async
|
|
685
|
-
this.
|
|
790
|
+
this._runPublishQueue();
|
|
686
791
|
}
|
|
687
792
|
});
|
|
688
793
|
}
|
|
689
794
|
|
|
690
|
-
_processResponses(
|
|
795
|
+
_processResponses(
|
|
796
|
+
responses: $ReadOnlyArray<GraphQLResponseWithData>,
|
|
797
|
+
): $ReadOnlyArray<RelayResponsePayload> {
|
|
691
798
|
if (this._optimisticUpdates !== null) {
|
|
692
|
-
this._optimisticUpdates.forEach(update =>
|
|
693
|
-
this.
|
|
694
|
-
);
|
|
799
|
+
this._optimisticUpdates.forEach(update => {
|
|
800
|
+
this._getPublishQueueAndSaveActor().revertUpdate(update);
|
|
801
|
+
});
|
|
695
802
|
this._optimisticUpdates = null;
|
|
696
803
|
}
|
|
697
804
|
|
|
@@ -704,19 +811,24 @@ class Executor {
|
|
|
704
811
|
this._operation.root,
|
|
705
812
|
ROOT_TYPE,
|
|
706
813
|
{
|
|
814
|
+
actorIdentifier: this._actorIdentifier,
|
|
707
815
|
getDataID: this._getDataID,
|
|
708
816
|
path: [],
|
|
709
|
-
reactFlightPayloadDeserializer:
|
|
817
|
+
reactFlightPayloadDeserializer:
|
|
818
|
+
this._reactFlightPayloadDeserializer != null
|
|
819
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
820
|
+
: null,
|
|
710
821
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
711
822
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
712
823
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
713
824
|
},
|
|
714
825
|
);
|
|
715
|
-
this.
|
|
826
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
716
827
|
this._operation,
|
|
717
828
|
relayPayload,
|
|
718
829
|
this._updater,
|
|
719
830
|
);
|
|
831
|
+
|
|
720
832
|
return relayPayload;
|
|
721
833
|
});
|
|
722
834
|
}
|
|
@@ -732,30 +844,30 @@ class Executor {
|
|
|
732
844
|
return;
|
|
733
845
|
}
|
|
734
846
|
payloads.forEach(payload => {
|
|
735
|
-
const {incrementalPlaceholders,
|
|
847
|
+
const {incrementalPlaceholders, followupPayloads, isFinal} = payload;
|
|
736
848
|
this._state = isFinal ? 'loading_final' : 'loading_incremental';
|
|
737
849
|
this._updateActiveState();
|
|
738
850
|
if (isFinal) {
|
|
739
851
|
this._incrementalPayloadsPending = false;
|
|
740
852
|
}
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
moduleImportPayloads.forEach(moduleImportPayload => {
|
|
749
|
-
this._processModuleImportPayload(
|
|
750
|
-
moduleImportPayload,
|
|
751
|
-
operationLoader,
|
|
752
|
-
);
|
|
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;
|
|
753
860
|
});
|
|
754
861
|
}
|
|
862
|
+
|
|
755
863
|
if (incrementalPlaceholders && incrementalPlaceholders.length !== 0) {
|
|
756
864
|
this._incrementalPayloadsPending = this._state !== 'loading_final';
|
|
757
865
|
incrementalPlaceholders.forEach(incrementalPlaceholder => {
|
|
866
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
867
|
+
this._actorIdentifier =
|
|
868
|
+
incrementalPlaceholder.actorIdentifier ?? this._actorIdentifier;
|
|
758
869
|
this._processIncrementalPlaceholder(payload, incrementalPlaceholder);
|
|
870
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
759
871
|
});
|
|
760
872
|
|
|
761
873
|
if (this._isClientPayload || this._state === 'loading_final') {
|
|
@@ -787,10 +899,6 @@ class Executor {
|
|
|
787
899
|
}
|
|
788
900
|
});
|
|
789
901
|
if (relayPayloads.length > 0) {
|
|
790
|
-
if (!RelayFeatureFlags.ENABLE_BATCHED_STORE_UPDATES) {
|
|
791
|
-
const updatedOwners = this._publishQueue.run();
|
|
792
|
-
this._updateOperationTracker(updatedOwners);
|
|
793
|
-
}
|
|
794
902
|
this._processPayloadFollowups(relayPayloads);
|
|
795
903
|
}
|
|
796
904
|
}
|
|
@@ -808,23 +916,6 @@ class Executor {
|
|
|
808
916
|
) {
|
|
809
917
|
this._completeOperationTracker();
|
|
810
918
|
}
|
|
811
|
-
if (RelayFeatureFlags.ENABLE_UNIQUE_SUBSCRIPTION_ROOT) {
|
|
812
|
-
const nextID = generateUniqueClientID();
|
|
813
|
-
this._operation = {
|
|
814
|
-
request: this._operation.request,
|
|
815
|
-
fragment: createReaderSelector(
|
|
816
|
-
this._operation.fragment.node,
|
|
817
|
-
nextID,
|
|
818
|
-
this._operation.fragment.variables,
|
|
819
|
-
this._operation.fragment.owner,
|
|
820
|
-
),
|
|
821
|
-
root: createNormalizationSelector(
|
|
822
|
-
this._operation.root.node,
|
|
823
|
-
nextID,
|
|
824
|
-
this._operation.root.variables,
|
|
825
|
-
),
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
919
|
}
|
|
829
920
|
|
|
830
921
|
/**
|
|
@@ -834,78 +925,154 @@ class Executor {
|
|
|
834
925
|
* defer, stream, etc); these are handled by calling
|
|
835
926
|
* `_processPayloadFollowups()`.
|
|
836
927
|
*/
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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,
|
|
879
1014
|
});
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
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
|
+
);
|
|
893
1047
|
}
|
|
894
1048
|
}
|
|
895
1049
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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,
|
|
899
1067
|
): void {
|
|
900
|
-
const relayPayload = this.
|
|
901
|
-
|
|
902
|
-
|
|
1068
|
+
const relayPayload = this._normalizeFollowupPayload(
|
|
1069
|
+
followupPayload,
|
|
1070
|
+
normalizationNode,
|
|
1071
|
+
);
|
|
1072
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1073
|
+
this._operation,
|
|
1074
|
+
relayPayload,
|
|
903
1075
|
);
|
|
904
|
-
this._publishQueue.commitPayload(this._operation, relayPayload);
|
|
905
|
-
if (!RelayFeatureFlags.ENABLE_BATCHED_STORE_UPDATES) {
|
|
906
|
-
const updatedOwners = this._publishQueue.run();
|
|
907
|
-
this._updateOperationTracker(updatedOwners);
|
|
908
|
-
}
|
|
909
1076
|
this._processPayloadFollowups([relayPayload]);
|
|
910
1077
|
}
|
|
911
1078
|
|
|
@@ -951,7 +1118,7 @@ class Executor {
|
|
|
951
1118
|
(placeholder: empty);
|
|
952
1119
|
invariant(
|
|
953
1120
|
false,
|
|
954
|
-
'Unsupported incremental placeholder kind `%s`.',
|
|
1121
|
+
'OperationExecutor: Unsupported incremental placeholder kind `%s`.',
|
|
955
1122
|
placeholder.kind,
|
|
956
1123
|
);
|
|
957
1124
|
}
|
|
@@ -975,7 +1142,7 @@ class Executor {
|
|
|
975
1142
|
// exist.
|
|
976
1143
|
invariant(
|
|
977
1144
|
parentRecord != null,
|
|
978
|
-
'
|
|
1145
|
+
'OperationExecutor: Expected record `%s` to exist.',
|
|
979
1146
|
parentID,
|
|
980
1147
|
);
|
|
981
1148
|
let nextParentRecord;
|
|
@@ -1004,16 +1171,12 @@ class Executor {
|
|
|
1004
1171
|
record: nextParentRecord,
|
|
1005
1172
|
fieldPayloads: nextParentPayloads,
|
|
1006
1173
|
});
|
|
1174
|
+
|
|
1007
1175
|
// If there were any queued responses, process them now that placeholders
|
|
1008
1176
|
// are in place
|
|
1009
1177
|
if (pendingResponses != null) {
|
|
1010
|
-
const payloadFollowups =
|
|
1011
|
-
pendingResponses
|
|
1012
|
-
);
|
|
1013
|
-
if (!RelayFeatureFlags.ENABLE_BATCHED_STORE_UPDATES) {
|
|
1014
|
-
const updatedOwners = this._publishQueue.run();
|
|
1015
|
-
this._updateOperationTracker(updatedOwners);
|
|
1016
|
-
}
|
|
1178
|
+
const payloadFollowups =
|
|
1179
|
+
this._processIncrementalResponses(pendingResponses);
|
|
1017
1180
|
this._processPayloadFollowups(payloadFollowups);
|
|
1018
1181
|
}
|
|
1019
1182
|
}
|
|
@@ -1049,7 +1212,7 @@ class Executor {
|
|
|
1049
1212
|
const placeholder = resultForPath.placeholder;
|
|
1050
1213
|
invariant(
|
|
1051
1214
|
placeholder.kind === 'defer',
|
|
1052
|
-
'
|
|
1215
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
1053
1216
|
'to be data for @defer, was `@%s`.',
|
|
1054
1217
|
pathKey,
|
|
1055
1218
|
label,
|
|
@@ -1063,10 +1226,7 @@ class Executor {
|
|
|
1063
1226
|
// but Relay records paths relative to the parent of the stream node:
|
|
1064
1227
|
// therefore we strip the last two elements just to lookup the path
|
|
1065
1228
|
// (the item index is used later to insert the element in the list)
|
|
1066
|
-
const pathKey = path
|
|
1067
|
-
.slice(0, -2)
|
|
1068
|
-
.map(String)
|
|
1069
|
-
.join('.');
|
|
1229
|
+
const pathKey = path.slice(0, -2).map(String).join('.');
|
|
1070
1230
|
let resultForPath = resultForLabel.get(pathKey);
|
|
1071
1231
|
if (resultForPath == null) {
|
|
1072
1232
|
resultForPath = {kind: 'response', responses: [incrementalResponse]};
|
|
@@ -1079,7 +1239,7 @@ class Executor {
|
|
|
1079
1239
|
const placeholder = resultForPath.placeholder;
|
|
1080
1240
|
invariant(
|
|
1081
1241
|
placeholder.kind === 'stream',
|
|
1082
|
-
'
|
|
1242
|
+
'OperationExecutor: Expected data for path `%s` for label `%s` ' +
|
|
1083
1243
|
'to be data for @stream, was `@%s`.',
|
|
1084
1244
|
pathKey,
|
|
1085
1245
|
label,
|
|
@@ -1100,27 +1260,37 @@ class Executor {
|
|
|
1100
1260
|
response: GraphQLResponseWithData,
|
|
1101
1261
|
): RelayResponsePayload {
|
|
1102
1262
|
const {dataID: parentID} = placeholder.selector;
|
|
1263
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1264
|
+
this._actorIdentifier =
|
|
1265
|
+
placeholder.actorIdentifier ?? this._actorIdentifier;
|
|
1103
1266
|
const relayPayload = normalizeResponse(
|
|
1104
1267
|
response,
|
|
1105
1268
|
placeholder.selector,
|
|
1106
1269
|
placeholder.typeName,
|
|
1107
1270
|
{
|
|
1271
|
+
actorIdentifier: this._actorIdentifier,
|
|
1108
1272
|
getDataID: this._getDataID,
|
|
1109
1273
|
path: placeholder.path,
|
|
1110
|
-
reactFlightPayloadDeserializer:
|
|
1274
|
+
reactFlightPayloadDeserializer:
|
|
1275
|
+
this._reactFlightPayloadDeserializer != null
|
|
1276
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1277
|
+
: null,
|
|
1111
1278
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1112
1279
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1113
1280
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
1114
1281
|
},
|
|
1115
1282
|
);
|
|
1116
|
-
this.
|
|
1283
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1284
|
+
this._operation,
|
|
1285
|
+
relayPayload,
|
|
1286
|
+
);
|
|
1117
1287
|
|
|
1118
1288
|
// Load the version of the parent record from which this incremental data
|
|
1119
1289
|
// was derived
|
|
1120
1290
|
const parentEntry = this._source.get(parentID);
|
|
1121
1291
|
invariant(
|
|
1122
1292
|
parentEntry != null,
|
|
1123
|
-
'
|
|
1293
|
+
'OperationExecutor: Expected the parent record `%s` for @defer ' +
|
|
1124
1294
|
'data to exist.',
|
|
1125
1295
|
parentID,
|
|
1126
1296
|
);
|
|
@@ -1130,15 +1300,17 @@ class Executor {
|
|
|
1130
1300
|
errors: null,
|
|
1131
1301
|
fieldPayloads,
|
|
1132
1302
|
incrementalPlaceholders: null,
|
|
1133
|
-
|
|
1303
|
+
followupPayloads: null,
|
|
1134
1304
|
source: RelayRecordSource.create(),
|
|
1135
1305
|
isFinal: response.extensions?.is_final === true,
|
|
1136
1306
|
};
|
|
1137
|
-
this.
|
|
1307
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1138
1308
|
this._operation,
|
|
1139
1309
|
handleFieldsRelayPayload,
|
|
1140
1310
|
);
|
|
1141
1311
|
}
|
|
1312
|
+
|
|
1313
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1142
1314
|
return relayPayload;
|
|
1143
1315
|
}
|
|
1144
1316
|
|
|
@@ -1151,12 +1323,14 @@ class Executor {
|
|
|
1151
1323
|
placeholder: StreamPlaceholder,
|
|
1152
1324
|
response: GraphQLResponseWithData,
|
|
1153
1325
|
): RelayResponsePayload {
|
|
1154
|
-
const {parentID, node, variables} = placeholder;
|
|
1326
|
+
const {parentID, node, variables, actorIdentifier} = placeholder;
|
|
1327
|
+
const prevActorIdentifier = this._actorIdentifier;
|
|
1328
|
+
this._actorIdentifier = actorIdentifier ?? this._actorIdentifier;
|
|
1155
1329
|
// Find the LinkedField where @stream was applied
|
|
1156
1330
|
const field = node.selections[0];
|
|
1157
1331
|
invariant(
|
|
1158
1332
|
field != null && field.kind === 'LinkedField' && field.plural === true,
|
|
1159
|
-
'
|
|
1333
|
+
'OperationExecutor: Expected @stream to be used on a plural field.',
|
|
1160
1334
|
);
|
|
1161
1335
|
const {
|
|
1162
1336
|
fieldPayloads,
|
|
@@ -1176,34 +1350,38 @@ class Executor {
|
|
|
1176
1350
|
// Publish the new item and update the parent record to set
|
|
1177
1351
|
// field[index] = item *if* the parent record hasn't been concurrently
|
|
1178
1352
|
// modified.
|
|
1179
|
-
this.
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
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
|
+
);
|
|
1207
1385
|
|
|
1208
1386
|
// Now that the parent record has been updated to include the new item,
|
|
1209
1387
|
// also update any handle fields that are derived from the parent record.
|
|
@@ -1212,15 +1390,17 @@ class Executor {
|
|
|
1212
1390
|
errors: null,
|
|
1213
1391
|
fieldPayloads,
|
|
1214
1392
|
incrementalPlaceholders: null,
|
|
1215
|
-
|
|
1393
|
+
followupPayloads: null,
|
|
1216
1394
|
source: RelayRecordSource.create(),
|
|
1217
1395
|
isFinal: false,
|
|
1218
1396
|
};
|
|
1219
|
-
this.
|
|
1397
|
+
this._getPublishQueueAndSaveActor().commitPayload(
|
|
1220
1398
|
this._operation,
|
|
1221
1399
|
handleFieldsRelayPayload,
|
|
1222
1400
|
);
|
|
1223
1401
|
}
|
|
1402
|
+
|
|
1403
|
+
this._actorIdentifier = prevActorIdentifier;
|
|
1224
1404
|
return relayPayload;
|
|
1225
1405
|
}
|
|
1226
1406
|
|
|
@@ -1242,7 +1422,7 @@ class Executor {
|
|
|
1242
1422
|
const {data} = response;
|
|
1243
1423
|
invariant(
|
|
1244
1424
|
typeof data === 'object',
|
|
1245
|
-
'
|
|
1425
|
+
'OperationExecutor: Expected the GraphQL @stream payload `data` ' +
|
|
1246
1426
|
'value to be an object.',
|
|
1247
1427
|
);
|
|
1248
1428
|
const responseKey = field.alias ?? field.name;
|
|
@@ -1253,7 +1433,7 @@ class Executor {
|
|
|
1253
1433
|
const parentEntry = this._source.get(parentID);
|
|
1254
1434
|
invariant(
|
|
1255
1435
|
parentEntry != null,
|
|
1256
|
-
'
|
|
1436
|
+
'OperationExecutor: Expected the parent record `%s` for @stream ' +
|
|
1257
1437
|
'data to exist.',
|
|
1258
1438
|
parentID,
|
|
1259
1439
|
);
|
|
@@ -1268,7 +1448,7 @@ class Executor {
|
|
|
1268
1448
|
);
|
|
1269
1449
|
invariant(
|
|
1270
1450
|
prevIDs != null,
|
|
1271
|
-
'
|
|
1451
|
+
'OperationExecutor: Expected record `%s` to have fetched field ' +
|
|
1272
1452
|
'`%s` with @stream.',
|
|
1273
1453
|
parentID,
|
|
1274
1454
|
field.name,
|
|
@@ -1279,7 +1459,7 @@ class Executor {
|
|
|
1279
1459
|
const itemIndex = parseInt(finalPathEntry, 10);
|
|
1280
1460
|
invariant(
|
|
1281
1461
|
itemIndex === finalPathEntry && itemIndex >= 0,
|
|
1282
|
-
'
|
|
1462
|
+
'OperationExecutor: Expected path for @stream to end in a ' +
|
|
1283
1463
|
'positive integer index, got `%s`',
|
|
1284
1464
|
finalPathEntry,
|
|
1285
1465
|
);
|
|
@@ -1287,7 +1467,7 @@ class Executor {
|
|
|
1287
1467
|
const typeName = field.concreteType ?? data[TYPENAME_KEY];
|
|
1288
1468
|
invariant(
|
|
1289
1469
|
typeof typeName === 'string',
|
|
1290
|
-
'
|
|
1470
|
+
'OperationExecutor: Expected @stream field `%s` to have a ' +
|
|
1291
1471
|
'__typename.',
|
|
1292
1472
|
field.name,
|
|
1293
1473
|
);
|
|
@@ -1302,7 +1482,7 @@ class Executor {
|
|
|
1302
1482
|
generateClientID(parentID, storageKey, itemIndex);
|
|
1303
1483
|
invariant(
|
|
1304
1484
|
typeof itemID === 'string',
|
|
1305
|
-
'
|
|
1485
|
+
'OperationExecutor: Expected id of elements of field `%s` to ' +
|
|
1306
1486
|
'be strings.',
|
|
1307
1487
|
storageKey,
|
|
1308
1488
|
);
|
|
@@ -1322,9 +1502,13 @@ class Executor {
|
|
|
1322
1502
|
fieldPayloads,
|
|
1323
1503
|
});
|
|
1324
1504
|
const relayPayload = normalizeResponse(response, selector, typeName, {
|
|
1505
|
+
actorIdentifier: this._actorIdentifier,
|
|
1325
1506
|
getDataID: this._getDataID,
|
|
1326
1507
|
path: [...normalizationPath, responseKey, String(itemIndex)],
|
|
1327
|
-
reactFlightPayloadDeserializer:
|
|
1508
|
+
reactFlightPayloadDeserializer:
|
|
1509
|
+
this._reactFlightPayloadDeserializer != null
|
|
1510
|
+
? this._deserializeReactFlightPayloadWithLogging
|
|
1511
|
+
: null,
|
|
1328
1512
|
reactFlightServerErrorHandler: this._reactFlightServerErrorHandler,
|
|
1329
1513
|
treatMissingFieldsAsNull: this._treatMissingFieldsAsNull,
|
|
1330
1514
|
shouldProcessClientComponents: this._shouldProcessClientComponents,
|
|
@@ -1339,6 +1523,25 @@ class Executor {
|
|
|
1339
1523
|
};
|
|
1340
1524
|
}
|
|
1341
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
|
+
|
|
1342
1545
|
_updateOperationTracker(
|
|
1343
1546
|
updatedOwners: ?$ReadOnlyArray<RequestDescriptor>,
|
|
1344
1547
|
): void {
|
|
@@ -1353,6 +1556,58 @@ class Executor {
|
|
|
1353
1556
|
_completeOperationTracker() {
|
|
1354
1557
|
this._operationTracker.complete(this._operation.request);
|
|
1355
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));
|
|
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
|
+
}
|
|
1356
1611
|
}
|
|
1357
1612
|
|
|
1358
1613
|
function partitionGraphQLResponses(
|