relay-runtime 7.0.0 → 9.1.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 +34 -0
- package/handlers/connection/ConnectionHandler.js.flow +549 -0
- package/handlers/connection/ConnectionInterface.js.flow +92 -0
- package/index.js +1 -1
- package/index.js.flow +314 -0
- package/lib/handlers/RelayDefaultHandlerProvider.js +3 -2
- package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +34 -35
- package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +3 -30
- package/lib/index.js +29 -27
- package/lib/mutations/RelayDeclarativeMutationConfig.js +30 -52
- package/lib/mutations/RelayRecordProxy.js +6 -3
- package/lib/mutations/RelayRecordSourceMutator.js +3 -9
- package/lib/mutations/RelayRecordSourceProxy.js +21 -24
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -14
- package/lib/mutations/applyOptimisticMutation.js +2 -1
- package/lib/mutations/commitLocalUpdate.js +1 -0
- package/lib/mutations/commitMutation.js +26 -8
- package/lib/mutations/validateMutation.js +21 -11
- package/lib/network/ConvertToExecuteFunction.js +1 -0
- package/lib/network/RelayNetwork.js +1 -0
- package/lib/network/RelayNetworkTypes.js +1 -0
- package/lib/network/RelayObservable.js +10 -9
- package/lib/network/RelayQueryResponseCache.js +9 -7
- package/lib/query/{RelayModernGraphQLTag.js → GraphQLTag.js} +15 -8
- package/lib/query/fetchQuery.js +2 -1
- package/lib/query/fetchQueryInternal.js +30 -20
- package/lib/store/ClientID.js +1 -0
- package/lib/store/DataChecker.js +47 -97
- package/lib/store/RelayConcreteVariables.js +7 -2
- package/lib/store/RelayModernEnvironment.js +82 -41
- package/lib/store/RelayModernFragmentSpecResolver.js +61 -21
- package/lib/store/RelayModernOperationDescriptor.js +2 -1
- package/lib/store/RelayModernQueryExecutor.js +476 -333
- package/lib/store/RelayModernRecord.js +39 -9
- package/lib/store/RelayModernSelector.js +2 -1
- package/lib/store/RelayModernStore.js +359 -371
- package/lib/store/RelayOperationTracker.js +36 -78
- package/lib/store/RelayOptimisticRecordSource.js +8 -5
- package/lib/store/RelayPublishQueue.js +66 -53
- package/lib/store/RelayReader.js +2 -24
- package/lib/store/RelayRecordSource.js +3 -9
- package/lib/store/RelayRecordSourceMapImpl.js +14 -18
- package/lib/store/RelayRecordState.js +1 -0
- package/lib/store/RelayReferenceMarker.js +8 -58
- package/lib/store/RelayResponseNormalizer.js +15 -144
- package/lib/store/RelayStoreTypes.js +1 -0
- package/lib/store/RelayStoreUtils.js +34 -10
- package/lib/store/StoreInspector.js +11 -5
- package/lib/store/ViewerPattern.js +1 -0
- package/lib/store/cloneRelayHandleSourceField.js +1 -0
- package/lib/store/createFragmentSpecResolver.js +1 -0
- package/lib/store/createRelayContext.js +1 -0
- package/lib/store/defaultGetDataID.js +1 -0
- package/lib/store/hasOverlappingIDs.js +1 -0
- package/lib/store/isRelayModernEnvironment.js +1 -0
- package/lib/store/normalizeRelayPayload.js +8 -4
- package/lib/store/readInlineData.js +2 -1
- package/lib/subscription/requestSubscription.js +6 -3
- package/lib/util/JSResourceTypes.flow.js +12 -0
- package/lib/util/NormalizationNode.js +1 -0
- package/lib/util/ReaderNode.js +1 -0
- package/lib/util/RelayConcreteNode.js +3 -0
- package/lib/util/RelayDefaultHandleKey.js +1 -0
- package/lib/util/RelayError.js +2 -1
- package/lib/util/RelayFeatureFlags.js +3 -2
- package/lib/util/RelayProfiler.js +1 -0
- package/lib/util/RelayReplaySubject.js +2 -3
- package/lib/util/RelayRuntimeTypes.js +1 -0
- package/lib/util/createPayloadFor3DField.js +34 -0
- package/lib/util/deepFreeze.js +1 -0
- package/lib/util/generateID.js +1 -0
- package/lib/util/getFragmentIdentifier.js +1 -0
- package/lib/util/getRelayHandleKey.js +1 -0
- package/lib/util/getRequestIdentifier.js +1 -0
- package/lib/util/isPromise.js +1 -0
- package/lib/util/isScalarAndEqual.js +1 -0
- package/lib/util/recycleNodesInto.js +1 -0
- package/lib/util/resolveImmediate.js +1 -0
- package/lib/util/stableCopy.js +1 -0
- package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
- package/mutations/RelayRecordProxy.js.flow +165 -0
- package/mutations/RelayRecordSourceMutator.js.flow +238 -0
- package/mutations/RelayRecordSourceProxy.js.flow +164 -0
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
- package/mutations/applyOptimisticMutation.js.flow +76 -0
- package/mutations/commitLocalUpdate.js.flow +24 -0
- package/mutations/commitMutation.js.flow +184 -0
- package/mutations/validateMutation.js.flow +211 -0
- package/network/ConvertToExecuteFunction.js.flow +49 -0
- package/network/RelayNetwork.js.flow +84 -0
- package/network/RelayNetworkTypes.js.flow +123 -0
- package/network/RelayObservable.js.flow +634 -0
- package/network/RelayQueryResponseCache.js.flow +111 -0
- package/package.json +1 -1
- package/query/GraphQLTag.js.flow +166 -0
- package/query/fetchQuery.js.flow +47 -0
- package/query/fetchQueryInternal.js.flow +349 -0
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/ClientID.js.flow +43 -0
- package/store/DataChecker.js.flow +426 -0
- package/store/RelayConcreteVariables.js.flow +96 -0
- package/store/RelayModernEnvironment.js.flow +526 -0
- package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
- package/store/RelayModernOperationDescriptor.js.flow +88 -0
- package/store/RelayModernQueryExecutor.js.flow +1327 -0
- package/store/RelayModernRecord.js.flow +403 -0
- package/store/RelayModernSelector.js.flow +444 -0
- package/store/RelayModernStore.js.flow +757 -0
- package/store/RelayOperationTracker.js.flow +164 -0
- package/store/RelayOptimisticRecordSource.js.flow +119 -0
- package/store/RelayPublishQueue.js.flow +401 -0
- package/store/RelayReader.js.flow +376 -0
- package/store/RelayRecordSource.js.flow +29 -0
- package/store/RelayRecordSourceMapImpl.js.flow +87 -0
- package/store/RelayRecordState.js.flow +37 -0
- package/store/RelayReferenceMarker.js.flow +236 -0
- package/store/RelayResponseNormalizer.js.flow +556 -0
- package/store/RelayStoreTypes.js.flow +873 -0
- package/store/RelayStoreUtils.js.flow +218 -0
- package/store/StoreInspector.js.flow +173 -0
- package/store/ViewerPattern.js.flow +26 -0
- package/store/cloneRelayHandleSourceField.js.flow +66 -0
- package/store/createFragmentSpecResolver.js.flow +55 -0
- package/store/createRelayContext.js.flow +44 -0
- package/store/defaultGetDataID.js.flow +27 -0
- package/store/hasOverlappingIDs.js.flow +34 -0
- package/store/isRelayModernEnvironment.js.flow +27 -0
- package/store/normalizeRelayPayload.js.flow +51 -0
- package/store/readInlineData.js.flow +75 -0
- package/subscription/requestSubscription.js.flow +100 -0
- package/util/JSResourceTypes.flow.js.flow +20 -0
- package/util/NormalizationNode.js.flow +191 -0
- package/util/ReaderNode.js.flow +208 -0
- package/util/RelayConcreteNode.js.flow +80 -0
- package/util/RelayDefaultHandleKey.js.flow +17 -0
- package/util/RelayError.js.flow +33 -0
- package/util/RelayFeatureFlags.js.flow +30 -0
- package/util/RelayProfiler.js.flow +284 -0
- package/util/RelayReplaySubject.js.flow +134 -0
- package/util/RelayRuntimeTypes.js.flow +70 -0
- package/util/createPayloadFor3DField.js.flow +43 -0
- package/util/deepFreeze.js.flow +36 -0
- package/util/generateID.js.flow +21 -0
- package/util/getFragmentIdentifier.js.flow +52 -0
- package/util/getRelayHandleKey.js.flow +41 -0
- package/util/getRequestIdentifier.js.flow +41 -0
- package/util/isPromise.js.flow +21 -0
- package/util/isScalarAndEqual.js.flow +26 -0
- package/util/recycleNodesInto.js.flow +80 -0
- package/util/resolveImmediate.js.flow +30 -0
- package/util/stableCopy.js.flow +35 -0
- package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
- package/lib/store/RelayConnection.js +0 -36
- package/lib/store/RelayConnectionResolver.js +0 -177
- package/lib/store/RelayRecordSourceObjectImpl.js +0 -78
- package/lib/util/getFragmentSpecIdentifier.js +0 -26
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// flowlint ambiguous-object-type:error
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
|
|
16
|
+
|
|
17
|
+
const areEqual = require('areEqual');
|
|
18
|
+
const invariant = require('invariant');
|
|
19
|
+
const isScalarAndEqual = require('../util/isScalarAndEqual');
|
|
20
|
+
const warning = require('warning');
|
|
21
|
+
|
|
22
|
+
const {getPromiseForActiveRequest} = require('../query/fetchQueryInternal');
|
|
23
|
+
const {createRequestDescriptor} = require('./RelayModernOperationDescriptor');
|
|
24
|
+
const {
|
|
25
|
+
areEqualSelectors,
|
|
26
|
+
createReaderSelector,
|
|
27
|
+
getSelectorsFromObject,
|
|
28
|
+
} = require('./RelayModernSelector');
|
|
29
|
+
|
|
30
|
+
import type {ConcreteRequest} from '../util/RelayConcreteNode';
|
|
31
|
+
import type {Disposable, Variables} from '../util/RelayRuntimeTypes';
|
|
32
|
+
import type {
|
|
33
|
+
IEnvironment,
|
|
34
|
+
FragmentMap,
|
|
35
|
+
FragmentSpecResolver,
|
|
36
|
+
FragmentSpecResults,
|
|
37
|
+
PluralReaderSelector,
|
|
38
|
+
RelayContext,
|
|
39
|
+
SelectorData,
|
|
40
|
+
SingularReaderSelector,
|
|
41
|
+
Snapshot,
|
|
42
|
+
} from './RelayStoreTypes';
|
|
43
|
+
|
|
44
|
+
type Props = {[key: string]: mixed, ...};
|
|
45
|
+
type Resolvers = {
|
|
46
|
+
[key: string]: ?(SelectorListResolver | SelectorResolver),
|
|
47
|
+
...,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A utility for resolving and subscribing to the results of a fragment spec
|
|
52
|
+
* (key -> fragment mapping) given some "props" that determine the root ID
|
|
53
|
+
* and variables to use when reading each fragment. When props are changed via
|
|
54
|
+
* `setProps()`, the resolver will update its results and subscriptions
|
|
55
|
+
* accordingly. Internally, the resolver:
|
|
56
|
+
* - Converts the fragment map & props map into a map of `Selector`s.
|
|
57
|
+
* - Removes any resolvers for any props that became null.
|
|
58
|
+
* - Creates resolvers for any props that became non-null.
|
|
59
|
+
* - Updates resolvers with the latest props.
|
|
60
|
+
*
|
|
61
|
+
* This utility is implemented as an imperative, stateful API for performance
|
|
62
|
+
* reasons: reusing previous resolvers, callback functions, and subscriptions
|
|
63
|
+
* all helps to reduce object allocation and thereby decrease GC time.
|
|
64
|
+
*
|
|
65
|
+
* The `resolve()` function is also lazy and memoized: changes in the store mark
|
|
66
|
+
* the resolver as stale and notify the caller, and the actual results are
|
|
67
|
+
* recomputed the first time `resolve()` is called.
|
|
68
|
+
*/
|
|
69
|
+
class RelayModernFragmentSpecResolver implements FragmentSpecResolver {
|
|
70
|
+
_callback: ?() => void;
|
|
71
|
+
_context: RelayContext;
|
|
72
|
+
_data: Object;
|
|
73
|
+
_fragments: FragmentMap;
|
|
74
|
+
_props: Props;
|
|
75
|
+
_resolvers: Resolvers;
|
|
76
|
+
_stale: boolean;
|
|
77
|
+
|
|
78
|
+
constructor(
|
|
79
|
+
context: RelayContext,
|
|
80
|
+
fragments: FragmentMap,
|
|
81
|
+
props: Props,
|
|
82
|
+
callback?: () => void,
|
|
83
|
+
) {
|
|
84
|
+
this._callback = callback;
|
|
85
|
+
this._context = context;
|
|
86
|
+
this._data = {};
|
|
87
|
+
this._fragments = fragments;
|
|
88
|
+
this._props = {};
|
|
89
|
+
this._resolvers = {};
|
|
90
|
+
this._stale = false;
|
|
91
|
+
|
|
92
|
+
this.setProps(props);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
dispose(): void {
|
|
96
|
+
for (const key in this._resolvers) {
|
|
97
|
+
if (this._resolvers.hasOwnProperty(key)) {
|
|
98
|
+
disposeCallback(this._resolvers[key]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
resolve(): FragmentSpecResults {
|
|
104
|
+
if (this._stale) {
|
|
105
|
+
// Avoid mapping the object multiple times, which could occur if data for
|
|
106
|
+
// multiple keys changes in the same event loop.
|
|
107
|
+
const prevData = this._data;
|
|
108
|
+
let nextData;
|
|
109
|
+
for (const key in this._resolvers) {
|
|
110
|
+
if (this._resolvers.hasOwnProperty(key)) {
|
|
111
|
+
const resolver = this._resolvers[key];
|
|
112
|
+
const prevItem = prevData[key];
|
|
113
|
+
if (resolver) {
|
|
114
|
+
const nextItem = resolver.resolve();
|
|
115
|
+
if (nextData || nextItem !== prevItem) {
|
|
116
|
+
nextData = nextData || {...prevData};
|
|
117
|
+
nextData[key] = nextItem;
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
const prop = this._props[key];
|
|
121
|
+
const nextItem = prop !== undefined ? prop : null;
|
|
122
|
+
if (nextData || !isScalarAndEqual(nextItem, prevItem)) {
|
|
123
|
+
nextData = nextData || {...prevData};
|
|
124
|
+
nextData[key] = nextItem;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this._data = nextData || prevData;
|
|
130
|
+
this._stale = false;
|
|
131
|
+
}
|
|
132
|
+
return this._data;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setCallback(callback: () => void): void {
|
|
136
|
+
this._callback = callback;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
setProps(props: Props): void {
|
|
140
|
+
const ownedSelectors = getSelectorsFromObject(this._fragments, props);
|
|
141
|
+
this._props = {};
|
|
142
|
+
|
|
143
|
+
for (const key in ownedSelectors) {
|
|
144
|
+
if (ownedSelectors.hasOwnProperty(key)) {
|
|
145
|
+
const ownedSelector = ownedSelectors[key];
|
|
146
|
+
let resolver = this._resolvers[key];
|
|
147
|
+
if (ownedSelector == null) {
|
|
148
|
+
if (resolver != null) {
|
|
149
|
+
resolver.dispose();
|
|
150
|
+
}
|
|
151
|
+
resolver = null;
|
|
152
|
+
} else if (ownedSelector.kind === 'PluralReaderSelector') {
|
|
153
|
+
if (resolver == null) {
|
|
154
|
+
resolver = new SelectorListResolver(
|
|
155
|
+
this._context.environment,
|
|
156
|
+
ownedSelector,
|
|
157
|
+
this._onChange,
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
invariant(
|
|
161
|
+
resolver instanceof SelectorListResolver,
|
|
162
|
+
'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an array.',
|
|
163
|
+
key,
|
|
164
|
+
);
|
|
165
|
+
resolver.setSelector(ownedSelector);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
if (resolver == null) {
|
|
169
|
+
resolver = new SelectorResolver(
|
|
170
|
+
this._context.environment,
|
|
171
|
+
ownedSelector,
|
|
172
|
+
this._onChange,
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
invariant(
|
|
176
|
+
resolver instanceof SelectorResolver,
|
|
177
|
+
'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an object.',
|
|
178
|
+
key,
|
|
179
|
+
);
|
|
180
|
+
resolver.setSelector(ownedSelector);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
this._props[key] = props[key];
|
|
184
|
+
this._resolvers[key] = resolver;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this._stale = true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
setVariables(variables: Variables, request: ConcreteRequest): void {
|
|
191
|
+
for (const key in this._resolvers) {
|
|
192
|
+
if (this._resolvers.hasOwnProperty(key)) {
|
|
193
|
+
const resolver = this._resolvers[key];
|
|
194
|
+
if (resolver) {
|
|
195
|
+
resolver.setVariables(variables, request);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this._stale = true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
_onChange = (): void => {
|
|
203
|
+
this._stale = true;
|
|
204
|
+
|
|
205
|
+
if (typeof this._callback === 'function') {
|
|
206
|
+
this._callback();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* A resolver for a single Selector.
|
|
213
|
+
*/
|
|
214
|
+
class SelectorResolver {
|
|
215
|
+
_callback: () => void;
|
|
216
|
+
_data: ?SelectorData;
|
|
217
|
+
_environment: IEnvironment;
|
|
218
|
+
_isMissingData: boolean;
|
|
219
|
+
_selector: SingularReaderSelector;
|
|
220
|
+
_subscription: ?Disposable;
|
|
221
|
+
|
|
222
|
+
constructor(
|
|
223
|
+
environment: IEnvironment,
|
|
224
|
+
selector: SingularReaderSelector,
|
|
225
|
+
callback: () => void,
|
|
226
|
+
) {
|
|
227
|
+
const snapshot = environment.lookup(selector);
|
|
228
|
+
this._callback = callback;
|
|
229
|
+
this._data = snapshot.data;
|
|
230
|
+
this._isMissingData = snapshot.isMissingData;
|
|
231
|
+
this._environment = environment;
|
|
232
|
+
this._selector = selector;
|
|
233
|
+
this._subscription = environment.subscribe(snapshot, this._onChange);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
dispose(): void {
|
|
237
|
+
if (this._subscription) {
|
|
238
|
+
this._subscription.dispose();
|
|
239
|
+
this._subscription = null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
resolve(): ?Object {
|
|
244
|
+
if (
|
|
245
|
+
RelayFeatureFlags.ENABLE_RELAY_CONTAINERS_SUSPENSE === true &&
|
|
246
|
+
this._isMissingData === true
|
|
247
|
+
) {
|
|
248
|
+
// NOTE: This branch exists to handle the case in which:
|
|
249
|
+
// - A RelayModern container is rendered as a descendant of a Relay Hook
|
|
250
|
+
// root using a "partial" renderPolicy (this means that eargerly
|
|
251
|
+
// reading any cached data that is available instead of blocking
|
|
252
|
+
// at the root until the whole query is fetched).
|
|
253
|
+
// - A parent Relay Hook didnt' suspend earlier on data being fetched,
|
|
254
|
+
// either because the fragment data for the parent was available, or
|
|
255
|
+
// the parent fragment didn't have any data dependencies.
|
|
256
|
+
// Even though our Flow types reflect the possiblity of null data, there
|
|
257
|
+
// might still be cases where it's not handled at runtime becuase the
|
|
258
|
+
// Flow types are being ignored, or simply not being used (for example,
|
|
259
|
+
// the case reported here: https://fburl.com/srnbucf8, was due to
|
|
260
|
+
// misuse of Flow types here: https://fburl.com/g3m0mqqh).
|
|
261
|
+
// Additionally, even though the null data might be handled without a
|
|
262
|
+
// runtime error, we might not suspend when we intended to if a parent
|
|
263
|
+
// Relay Hook (e.g. that is using @defer) decided not to suspend becuase
|
|
264
|
+
// it's immediate data was already available (even if it was deferred),
|
|
265
|
+
// or it didn't actually need any data (was just spreading other fragments).
|
|
266
|
+
// This should eventually go away with something like @optional, where we only
|
|
267
|
+
// suspend at specific boundaries depending on whether the boundary
|
|
268
|
+
// can be fulfilled or not.
|
|
269
|
+
const promise =
|
|
270
|
+
getPromiseForActiveRequest(this._environment, this._selector.owner) ??
|
|
271
|
+
this._environment
|
|
272
|
+
.getOperationTracker()
|
|
273
|
+
.getPromiseForPendingOperationsAffectingOwner(this._selector.owner);
|
|
274
|
+
if (promise != null) {
|
|
275
|
+
warning(
|
|
276
|
+
false,
|
|
277
|
+
'Relay: Relay Container for fragment `%s` suspended. When using ' +
|
|
278
|
+
'features such as @defer or @module, use `useFragment` instead ' +
|
|
279
|
+
'of a Relay Container.',
|
|
280
|
+
this._selector.node.name,
|
|
281
|
+
);
|
|
282
|
+
throw promise;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return this._data;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
setSelector(selector: SingularReaderSelector): void {
|
|
289
|
+
if (
|
|
290
|
+
this._subscription != null &&
|
|
291
|
+
areEqualSelectors(selector, this._selector)
|
|
292
|
+
) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
this.dispose();
|
|
296
|
+
const snapshot = this._environment.lookup(selector);
|
|
297
|
+
this._data = snapshot.data;
|
|
298
|
+
this._isMissingData = snapshot.isMissingData;
|
|
299
|
+
this._selector = selector;
|
|
300
|
+
this._subscription = this._environment.subscribe(snapshot, this._onChange);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
setVariables(variables: Variables, request: ConcreteRequest): void {
|
|
304
|
+
if (areEqual(variables, this._selector.variables)) {
|
|
305
|
+
// If we're not actually setting new variables, we don't actually want
|
|
306
|
+
// to create a new fragment owner, since areEqualSelectors relies on
|
|
307
|
+
// owner identity.
|
|
308
|
+
// In fact, we don't even need to try to attempt to set a new selector.
|
|
309
|
+
// When fragment ownership is not enabled, setSelector will also bail
|
|
310
|
+
// out since the selector doesn't really change, so we're doing it here
|
|
311
|
+
// earlier.
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// NOTE: We manually create the request descriptor here instead of
|
|
315
|
+
// calling createOperationDescriptor() because we want to set a
|
|
316
|
+
// descriptor with *unaltered* variables as the fragment owner.
|
|
317
|
+
// This is a hack that allows us to preserve exisiting (broken)
|
|
318
|
+
// behavior of RelayModern containers while using fragment ownership
|
|
319
|
+
// to propagate variables instead of Context.
|
|
320
|
+
// For more details, see the summary of D13999308
|
|
321
|
+
const requestDescriptor = createRequestDescriptor(request, variables);
|
|
322
|
+
const selector = createReaderSelector(
|
|
323
|
+
this._selector.node,
|
|
324
|
+
this._selector.dataID,
|
|
325
|
+
variables,
|
|
326
|
+
requestDescriptor,
|
|
327
|
+
);
|
|
328
|
+
this.setSelector(selector);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
_onChange = (snapshot: Snapshot): void => {
|
|
332
|
+
this._data = snapshot.data;
|
|
333
|
+
this._isMissingData = snapshot.isMissingData;
|
|
334
|
+
this._callback();
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* A resolver for an array of Selectors.
|
|
340
|
+
*/
|
|
341
|
+
class SelectorListResolver {
|
|
342
|
+
_callback: () => void;
|
|
343
|
+
_data: Array<?SelectorData>;
|
|
344
|
+
_environment: IEnvironment;
|
|
345
|
+
_resolvers: Array<SelectorResolver>;
|
|
346
|
+
_stale: boolean;
|
|
347
|
+
|
|
348
|
+
constructor(
|
|
349
|
+
environment: IEnvironment,
|
|
350
|
+
selector: PluralReaderSelector,
|
|
351
|
+
callback: () => void,
|
|
352
|
+
) {
|
|
353
|
+
this._callback = callback;
|
|
354
|
+
this._data = [];
|
|
355
|
+
this._environment = environment;
|
|
356
|
+
this._resolvers = [];
|
|
357
|
+
this._stale = true;
|
|
358
|
+
|
|
359
|
+
this.setSelector(selector);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
dispose(): void {
|
|
363
|
+
this._resolvers.forEach(disposeCallback);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
resolve(): Array<?Object> {
|
|
367
|
+
if (this._stale) {
|
|
368
|
+
// Avoid mapping the array multiple times, which could occur if data for
|
|
369
|
+
// multiple indices changes in the same event loop.
|
|
370
|
+
const prevData = this._data;
|
|
371
|
+
let nextData;
|
|
372
|
+
for (let ii = 0; ii < this._resolvers.length; ii++) {
|
|
373
|
+
const prevItem = prevData[ii];
|
|
374
|
+
const nextItem = this._resolvers[ii].resolve();
|
|
375
|
+
if (nextData || nextItem !== prevItem) {
|
|
376
|
+
nextData = nextData || prevData.slice(0, ii);
|
|
377
|
+
nextData.push(nextItem);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (!nextData && this._resolvers.length !== prevData.length) {
|
|
381
|
+
nextData = prevData.slice(0, this._resolvers.length);
|
|
382
|
+
}
|
|
383
|
+
this._data = nextData || prevData;
|
|
384
|
+
this._stale = false;
|
|
385
|
+
}
|
|
386
|
+
return this._data;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
setSelector(selector: PluralReaderSelector): void {
|
|
390
|
+
const {selectors} = selector;
|
|
391
|
+
while (this._resolvers.length > selectors.length) {
|
|
392
|
+
const resolver = this._resolvers.pop();
|
|
393
|
+
resolver.dispose();
|
|
394
|
+
}
|
|
395
|
+
for (let ii = 0; ii < selectors.length; ii++) {
|
|
396
|
+
if (ii < this._resolvers.length) {
|
|
397
|
+
this._resolvers[ii].setSelector(selectors[ii]);
|
|
398
|
+
} else {
|
|
399
|
+
this._resolvers[ii] = new SelectorResolver(
|
|
400
|
+
this._environment,
|
|
401
|
+
selectors[ii],
|
|
402
|
+
this._onChange,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
this._stale = true;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
setVariables(variables: Variables, request: ConcreteRequest): void {
|
|
410
|
+
this._resolvers.forEach(resolver =>
|
|
411
|
+
resolver.setVariables(variables, request),
|
|
412
|
+
);
|
|
413
|
+
this._stale = true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
_onChange = (data: ?Object): void => {
|
|
417
|
+
this._stale = true;
|
|
418
|
+
this._callback();
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function disposeCallback(disposable: ?Disposable): void {
|
|
423
|
+
disposable && disposable.dispose();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
module.exports = RelayModernFragmentSpecResolver;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
* @flow strict-local
|
|
8
|
+
* @format
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// flowlint ambiguous-object-type:error
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const deepFreeze = require('../util/deepFreeze');
|
|
16
|
+
const getRequestIdentifier = require('../util/getRequestIdentifier');
|
|
17
|
+
|
|
18
|
+
const {getOperationVariables} = require('./RelayConcreteVariables');
|
|
19
|
+
const {
|
|
20
|
+
createNormalizationSelector,
|
|
21
|
+
createReaderSelector,
|
|
22
|
+
} = require('./RelayModernSelector');
|
|
23
|
+
const {ROOT_ID} = require('./RelayStoreUtils');
|
|
24
|
+
|
|
25
|
+
import type {ConcreteRequest} from '../util/RelayConcreteNode';
|
|
26
|
+
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
27
|
+
import type {OperationDescriptor, RequestDescriptor} from './RelayStoreTypes';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates an instance of the `OperationDescriptor` type defined in
|
|
31
|
+
* `RelayStoreTypes` given an operation and some variables. The input variables
|
|
32
|
+
* are filtered to exclude variables that do not match defined arguments on the
|
|
33
|
+
* operation, and default values are populated for null values.
|
|
34
|
+
*/
|
|
35
|
+
function createOperationDescriptor(
|
|
36
|
+
request: ConcreteRequest,
|
|
37
|
+
variables: Variables,
|
|
38
|
+
dataID?: DataID = ROOT_ID,
|
|
39
|
+
): OperationDescriptor {
|
|
40
|
+
const operation = request.operation;
|
|
41
|
+
const operationVariables = getOperationVariables(operation, variables);
|
|
42
|
+
const requestDescriptor = createRequestDescriptor(
|
|
43
|
+
request,
|
|
44
|
+
operationVariables,
|
|
45
|
+
);
|
|
46
|
+
const operationDescriptor = {
|
|
47
|
+
fragment: createReaderSelector(
|
|
48
|
+
request.fragment,
|
|
49
|
+
dataID,
|
|
50
|
+
operationVariables,
|
|
51
|
+
requestDescriptor,
|
|
52
|
+
),
|
|
53
|
+
request: requestDescriptor,
|
|
54
|
+
root: createNormalizationSelector(operation, dataID, operationVariables),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
if (__DEV__) {
|
|
58
|
+
// Freezing properties short-circuits a deepFreeze of snapshots that contain
|
|
59
|
+
// an OperationDescriptor via their selector's owner, avoiding stack
|
|
60
|
+
// overflow on larger queries.
|
|
61
|
+
Object.freeze(operationDescriptor.fragment);
|
|
62
|
+
Object.freeze(operationDescriptor.root);
|
|
63
|
+
Object.freeze(operationDescriptor);
|
|
64
|
+
}
|
|
65
|
+
return operationDescriptor;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function createRequestDescriptor(
|
|
69
|
+
request: ConcreteRequest,
|
|
70
|
+
variables: Variables,
|
|
71
|
+
): RequestDescriptor {
|
|
72
|
+
const requestDescriptor = {
|
|
73
|
+
identifier: getRequestIdentifier(request.params, variables),
|
|
74
|
+
node: request,
|
|
75
|
+
variables: variables,
|
|
76
|
+
};
|
|
77
|
+
if (__DEV__) {
|
|
78
|
+
deepFreeze(variables);
|
|
79
|
+
Object.freeze(request);
|
|
80
|
+
Object.freeze(requestDescriptor);
|
|
81
|
+
}
|
|
82
|
+
return requestDescriptor;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
createOperationDescriptor,
|
|
87
|
+
createRequestDescriptor,
|
|
88
|
+
};
|