relay-runtime 8.0.0 → 10.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/handlers/RelayDefaultHandlerProvider.js.flow +41 -0
- package/handlers/connection/ConnectionHandler.js.flow +549 -0
- package/handlers/connection/ConnectionInterface.js.flow +92 -0
- package/handlers/connection/MutationHandlers.js.flow +88 -0
- package/index.js +1 -1
- package/index.js.flow +320 -0
- package/lib/handlers/RelayDefaultHandlerProvider.js +13 -2
- package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +33 -35
- package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +2 -2
- package/lib/handlers/connection/MutationHandlers.js +86 -0
- package/lib/index.js +15 -19
- package/lib/mutations/RelayDeclarativeMutationConfig.js +29 -52
- package/lib/mutations/RelayRecordProxy.js +1 -3
- package/lib/mutations/RelayRecordSourceMutator.js +2 -9
- package/lib/mutations/RelayRecordSourceProxy.js +2 -4
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -13
- package/lib/mutations/commitMutation.js +13 -3
- package/lib/mutations/validateMutation.js +16 -9
- package/lib/network/RelayObservable.js +9 -9
- package/lib/network/RelayQueryResponseCache.js +8 -6
- package/lib/query/PreloadableQueryRegistry.js +70 -0
- package/lib/query/fetchQueryInternal.js +31 -23
- package/lib/store/DataChecker.js +122 -110
- package/lib/store/RelayConcreteVariables.js +6 -2
- package/lib/store/RelayModernEnvironment.js +121 -67
- package/lib/store/RelayModernFragmentSpecResolver.js +12 -16
- package/lib/store/RelayModernQueryExecutor.js +389 -314
- package/lib/store/RelayModernRecord.js +14 -9
- package/lib/store/RelayModernSelector.js +7 -3
- package/lib/store/RelayModernStore.js +289 -484
- package/lib/store/RelayOperationTracker.js +35 -78
- package/lib/store/RelayOptimisticRecordSource.js +7 -5
- package/lib/store/RelayPublishQueue.js +6 -33
- package/lib/store/RelayReader.js +113 -45
- package/lib/store/RelayRecordSource.js +2 -9
- package/lib/store/RelayRecordSourceMapImpl.js +13 -18
- package/lib/store/RelayReferenceMarker.js +40 -60
- package/lib/store/RelayResponseNormalizer.js +158 -193
- package/lib/store/RelayStoreUtils.js +1 -0
- package/lib/store/StoreInspector.js +8 -8
- package/lib/store/TypeID.js +28 -0
- package/lib/store/cloneRelayScalarHandleSourceField.js +44 -0
- package/lib/store/normalizeRelayPayload.js +6 -2
- package/lib/store/readInlineData.js +1 -1
- package/lib/subscription/requestSubscription.js +5 -3
- package/lib/util/RelayConcreteNode.js +9 -6
- package/lib/util/RelayError.js +39 -9
- package/lib/util/RelayFeatureFlags.js +2 -5
- package/lib/util/RelayReplaySubject.js +3 -3
- package/lib/util/createPayloadFor3DField.js +7 -2
- package/lib/util/getRequestIdentifier.js +2 -2
- package/lib/util/recycleNodesInto.js +2 -6
- 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 +182 -0
- package/mutations/validateMutation.js.flow +213 -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/PreloadableQueryRegistry.js.flow +65 -0
- package/query/fetchQuery.js.flow +47 -0
- package/query/fetchQueryInternal.js.flow +348 -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 +502 -0
- package/store/RelayConcreteVariables.js.flow +96 -0
- package/store/RelayModernEnvironment.js.flow +551 -0
- package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
- package/store/RelayModernOperationDescriptor.js.flow +88 -0
- package/store/RelayModernQueryExecutor.js.flow +1321 -0
- package/store/RelayModernRecord.js.flow +403 -0
- package/store/RelayModernSelector.js.flow +455 -0
- package/store/RelayModernStore.js.flow +842 -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 +473 -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 +257 -0
- package/store/RelayResponseNormalizer.js.flow +680 -0
- package/store/RelayStoreTypes.js.flow +899 -0
- package/store/RelayStoreUtils.js.flow +219 -0
- package/store/StoreInspector.js.flow +171 -0
- package/store/TypeID.js.flow +28 -0
- package/store/ViewerPattern.js.flow +26 -0
- package/store/cloneRelayHandleSourceField.js.flow +66 -0
- package/store/cloneRelayScalarHandleSourceField.js.flow +62 -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 +198 -0
- package/util/ReaderNode.js.flow +208 -0
- package/util/RelayConcreteNode.js.flow +93 -0
- package/util/RelayDefaultHandleKey.js.flow +17 -0
- package/util/RelayError.js.flow +62 -0
- package/util/RelayFeatureFlags.js.flow +30 -0
- package/util/RelayProfiler.js.flow +284 -0
- package/util/RelayReplaySubject.js.flow +135 -0
- package/util/RelayRuntimeTypes.js.flow +72 -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 +42 -0
- package/util/isPromise.js.flow +21 -0
- package/util/isScalarAndEqual.js.flow +26 -0
- package/util/recycleNodesInto.js.flow +76 -0
- package/util/resolveImmediate.js.flow +30 -0
- package/util/stableCopy.js.flow +35 -0
- package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
- package/lib/handlers/getRelayDefaultMissingFieldHandlers.js +0 -36
- package/lib/query/RelayModernGraphQLTag.js +0 -104
- package/lib/store/RelayConnection.js +0 -37
- package/lib/store/RelayConnectionResolver.js +0 -178
- package/lib/store/RelayRecordSourceObjectImpl.js +0 -79
- package/lib/util/getFragmentSpecIdentifier.js +0 -27
|
@@ -0,0 +1,455 @@
|
|
|
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 areEqual = require('areEqual');
|
|
16
|
+
const invariant = require('invariant');
|
|
17
|
+
const warning = require('warning');
|
|
18
|
+
|
|
19
|
+
const {getFragmentVariables} = require('./RelayConcreteVariables');
|
|
20
|
+
const {
|
|
21
|
+
FRAGMENT_OWNER_KEY,
|
|
22
|
+
FRAGMENTS_KEY,
|
|
23
|
+
ID_KEY,
|
|
24
|
+
IS_WITHIN_UNMATCHED_TYPE_REFINEMENT,
|
|
25
|
+
} = require('./RelayStoreUtils');
|
|
26
|
+
|
|
27
|
+
import type {NormalizationSelectableNode} from '../util/NormalizationNode';
|
|
28
|
+
import type {ReaderFragment} from '../util/ReaderNode';
|
|
29
|
+
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
30
|
+
import type {
|
|
31
|
+
NormalizationSelector,
|
|
32
|
+
PluralReaderSelector,
|
|
33
|
+
ReaderSelector,
|
|
34
|
+
RequestDescriptor,
|
|
35
|
+
SingularReaderSelector,
|
|
36
|
+
} from './RelayStoreTypes';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @public
|
|
40
|
+
*
|
|
41
|
+
* Given the result `item` from a parent that fetched `fragment`, creates a
|
|
42
|
+
* selector that can be used to read the results of that fragment for that item.
|
|
43
|
+
*
|
|
44
|
+
* Example:
|
|
45
|
+
*
|
|
46
|
+
* Given two fragments as follows:
|
|
47
|
+
*
|
|
48
|
+
* ```
|
|
49
|
+
* fragment Parent on User {
|
|
50
|
+
* id
|
|
51
|
+
* ...Child
|
|
52
|
+
* }
|
|
53
|
+
* fragment Child on User {
|
|
54
|
+
* name
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* And given some object `parent` that is the results of `Parent` for id "4",
|
|
59
|
+
* the results of `Child` can be accessed by first getting a selector and then
|
|
60
|
+
* using that selector to `lookup()` the results against the environment:
|
|
61
|
+
*
|
|
62
|
+
* ```
|
|
63
|
+
* const childSelector = getSingularSelector(queryVariables, Child, parent);
|
|
64
|
+
* const childData = environment.lookup(childSelector).data;
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
function getSingularSelector(
|
|
68
|
+
fragment: ReaderFragment,
|
|
69
|
+
item: mixed,
|
|
70
|
+
): ?SingularReaderSelector {
|
|
71
|
+
invariant(
|
|
72
|
+
typeof item === 'object' && item !== null && !Array.isArray(item),
|
|
73
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an object, got ' +
|
|
74
|
+
'`%s`.',
|
|
75
|
+
fragment.name,
|
|
76
|
+
JSON.stringify(item),
|
|
77
|
+
);
|
|
78
|
+
const dataID = item[ID_KEY];
|
|
79
|
+
const fragments = item[FRAGMENTS_KEY];
|
|
80
|
+
const mixedOwner = item[FRAGMENT_OWNER_KEY];
|
|
81
|
+
const isWithinUnmatchedTypeRefinement =
|
|
82
|
+
item[IS_WITHIN_UNMATCHED_TYPE_REFINEMENT] === true;
|
|
83
|
+
if (
|
|
84
|
+
typeof dataID === 'string' &&
|
|
85
|
+
typeof fragments === 'object' &&
|
|
86
|
+
fragments !== null &&
|
|
87
|
+
typeof fragments[fragment.name] === 'object' &&
|
|
88
|
+
fragments[fragment.name] !== null &&
|
|
89
|
+
typeof mixedOwner === 'object' &&
|
|
90
|
+
mixedOwner !== null
|
|
91
|
+
) {
|
|
92
|
+
const owner: RequestDescriptor = (mixedOwner: $FlowFixMe);
|
|
93
|
+
const argumentVariables = fragments[fragment.name];
|
|
94
|
+
|
|
95
|
+
const fragmentVariables = getFragmentVariables(
|
|
96
|
+
fragment,
|
|
97
|
+
owner.variables,
|
|
98
|
+
argumentVariables,
|
|
99
|
+
);
|
|
100
|
+
return createReaderSelector(
|
|
101
|
+
fragment,
|
|
102
|
+
dataID,
|
|
103
|
+
fragmentVariables,
|
|
104
|
+
owner,
|
|
105
|
+
isWithinUnmatchedTypeRefinement,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (__DEV__) {
|
|
110
|
+
let stringifiedItem = JSON.stringify(item);
|
|
111
|
+
if (stringifiedItem.length > 499) {
|
|
112
|
+
stringifiedItem = stringifiedItem.substr(0, 498) + '\u2026';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
warning(
|
|
116
|
+
false,
|
|
117
|
+
'RelayModernSelector: Expected object to contain data for fragment `%s`, got ' +
|
|
118
|
+
'`%s`. Make sure that the parent operation/fragment included fragment ' +
|
|
119
|
+
'`...%s` without `@relay(mask: false)`.',
|
|
120
|
+
fragment.name,
|
|
121
|
+
stringifiedItem,
|
|
122
|
+
fragment.name,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @public
|
|
131
|
+
*
|
|
132
|
+
* Given the result `items` from a parent that fetched `fragment`, creates a
|
|
133
|
+
* selector that can be used to read the results of that fragment on those
|
|
134
|
+
* items. This is similar to `getSingularSelector` but for "plural" fragments that
|
|
135
|
+
* expect an array of results and therefore return an array of selectors.
|
|
136
|
+
*/
|
|
137
|
+
function getPluralSelector(
|
|
138
|
+
fragment: ReaderFragment,
|
|
139
|
+
items: $ReadOnlyArray<mixed>,
|
|
140
|
+
): ?PluralReaderSelector {
|
|
141
|
+
let selectors = null;
|
|
142
|
+
items.forEach((item, ii) => {
|
|
143
|
+
const selector = item != null ? getSingularSelector(fragment, item) : null;
|
|
144
|
+
if (selector != null) {
|
|
145
|
+
selectors = selectors || [];
|
|
146
|
+
selectors.push(selector);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
if (selectors == null) {
|
|
150
|
+
return null;
|
|
151
|
+
} else {
|
|
152
|
+
return {
|
|
153
|
+
kind: 'PluralReaderSelector',
|
|
154
|
+
selectors,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getSelector(
|
|
160
|
+
fragment: ReaderFragment,
|
|
161
|
+
item: mixed | Array<mixed>,
|
|
162
|
+
): ?ReaderSelector {
|
|
163
|
+
if (item == null) {
|
|
164
|
+
return item;
|
|
165
|
+
} else if (fragment.metadata && fragment.metadata.plural === true) {
|
|
166
|
+
invariant(
|
|
167
|
+
Array.isArray(item),
|
|
168
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an array, got `%s`. ' +
|
|
169
|
+
'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.',
|
|
170
|
+
fragment.name,
|
|
171
|
+
JSON.stringify(item),
|
|
172
|
+
fragment.name,
|
|
173
|
+
);
|
|
174
|
+
return getPluralSelector(fragment, item);
|
|
175
|
+
} else {
|
|
176
|
+
invariant(
|
|
177
|
+
!Array.isArray(item),
|
|
178
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an object, got `%s`. ' +
|
|
179
|
+
'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.',
|
|
180
|
+
fragment.name,
|
|
181
|
+
JSON.stringify(item),
|
|
182
|
+
fragment.name,
|
|
183
|
+
);
|
|
184
|
+
return getSingularSelector(fragment, item);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @public
|
|
190
|
+
*
|
|
191
|
+
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
192
|
+
* extracts the selectors for those fragments from the results.
|
|
193
|
+
*
|
|
194
|
+
* The canonical use-case for this function is ReactRelayFragmentContainer, which
|
|
195
|
+
* uses this function to convert (props, fragments) into selectors so that it
|
|
196
|
+
* can read the results to pass to the inner component.
|
|
197
|
+
*/
|
|
198
|
+
function getSelectorsFromObject(
|
|
199
|
+
fragments: {[key: string]: ReaderFragment, ...},
|
|
200
|
+
object: {[key: string]: mixed, ...},
|
|
201
|
+
): {[key: string]: ?ReaderSelector, ...} {
|
|
202
|
+
const selectors = {};
|
|
203
|
+
for (const key in fragments) {
|
|
204
|
+
if (fragments.hasOwnProperty(key)) {
|
|
205
|
+
const fragment = fragments[key];
|
|
206
|
+
const item = object[key];
|
|
207
|
+
selectors[key] = getSelector(fragment, item);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return selectors;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @public
|
|
215
|
+
*
|
|
216
|
+
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
217
|
+
* extracts a mapping of keys -> id(s) of the results.
|
|
218
|
+
*
|
|
219
|
+
* Similar to `getSelectorsFromObject()`, this function can be useful in
|
|
220
|
+
* determining the "identity" of the props passed to a component.
|
|
221
|
+
*/
|
|
222
|
+
function getDataIDsFromObject(
|
|
223
|
+
fragments: {[key: string]: ReaderFragment, ...},
|
|
224
|
+
object: {[key: string]: mixed, ...},
|
|
225
|
+
): {[key: string]: ?(DataID | Array<DataID>), ...} {
|
|
226
|
+
const ids = {};
|
|
227
|
+
for (const key in fragments) {
|
|
228
|
+
if (fragments.hasOwnProperty(key)) {
|
|
229
|
+
const fragment = fragments[key];
|
|
230
|
+
const item = object[key];
|
|
231
|
+
ids[key] = getDataIDsFromFragment(fragment, item);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return ids;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getDataIDsFromFragment(
|
|
238
|
+
fragment: ReaderFragment,
|
|
239
|
+
item: mixed | Array<mixed>,
|
|
240
|
+
): ?DataID | ?$ReadOnlyArray<DataID> {
|
|
241
|
+
if (item == null) {
|
|
242
|
+
return item;
|
|
243
|
+
} else if (fragment.metadata && fragment.metadata.plural === true) {
|
|
244
|
+
invariant(
|
|
245
|
+
Array.isArray(item),
|
|
246
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an array, got `%s`. ' +
|
|
247
|
+
'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.',
|
|
248
|
+
fragment.name,
|
|
249
|
+
JSON.stringify(item),
|
|
250
|
+
fragment.name,
|
|
251
|
+
);
|
|
252
|
+
return getDataIDs(fragment, item);
|
|
253
|
+
} else {
|
|
254
|
+
invariant(
|
|
255
|
+
!Array.isArray(item),
|
|
256
|
+
'RelayModernFragmentSpecResolver: Expected value for fragment `%s` to be an object, got `%s`. ' +
|
|
257
|
+
'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.',
|
|
258
|
+
fragment.name,
|
|
259
|
+
JSON.stringify(item),
|
|
260
|
+
fragment.name,
|
|
261
|
+
);
|
|
262
|
+
return getDataID(fragment, item);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @internal
|
|
268
|
+
*/
|
|
269
|
+
function getDataIDs(
|
|
270
|
+
fragment: ReaderFragment,
|
|
271
|
+
items: $ReadOnlyArray<mixed>,
|
|
272
|
+
): ?$ReadOnlyArray<DataID> {
|
|
273
|
+
let ids = null;
|
|
274
|
+
items.forEach(item => {
|
|
275
|
+
const id = item != null ? getDataID(fragment, item) : null;
|
|
276
|
+
if (id != null) {
|
|
277
|
+
ids = ids || [];
|
|
278
|
+
ids.push(id);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
return ids;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
function getDataID(fragment: ReaderFragment, item: mixed): ?DataID {
|
|
288
|
+
invariant(
|
|
289
|
+
typeof item === 'object' && item !== null && !Array.isArray(item),
|
|
290
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an object, got ' +
|
|
291
|
+
'`%s`.',
|
|
292
|
+
fragment.name,
|
|
293
|
+
JSON.stringify(item),
|
|
294
|
+
);
|
|
295
|
+
const dataID = item[ID_KEY];
|
|
296
|
+
if (typeof dataID === 'string') {
|
|
297
|
+
return dataID;
|
|
298
|
+
}
|
|
299
|
+
warning(
|
|
300
|
+
false,
|
|
301
|
+
'RelayModernSelector: Expected object to contain data for fragment `%s`, got ' +
|
|
302
|
+
'`%s`. Make sure that the parent operation/fragment included fragment ' +
|
|
303
|
+
'`...%s` without `@relay(mask: false)`, or `null` is passed as the fragment ' +
|
|
304
|
+
"reference for `%s` if it's conditonally included and the condition isn't met.",
|
|
305
|
+
fragment.name,
|
|
306
|
+
JSON.stringify(item),
|
|
307
|
+
fragment.name,
|
|
308
|
+
fragment.name,
|
|
309
|
+
);
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @public
|
|
315
|
+
*
|
|
316
|
+
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
317
|
+
* extracts the merged variables that would be in scope for those
|
|
318
|
+
* fragments/results.
|
|
319
|
+
*
|
|
320
|
+
* This can be useful in determing what varaibles were used to fetch the data
|
|
321
|
+
* for a Relay container, for example.
|
|
322
|
+
*/
|
|
323
|
+
function getVariablesFromObject(
|
|
324
|
+
fragments: {[key: string]: ReaderFragment, ...},
|
|
325
|
+
object: {[key: string]: mixed, ...},
|
|
326
|
+
): Variables {
|
|
327
|
+
const variables = {};
|
|
328
|
+
for (const key in fragments) {
|
|
329
|
+
if (fragments.hasOwnProperty(key)) {
|
|
330
|
+
const fragment = fragments[key];
|
|
331
|
+
const item = object[key];
|
|
332
|
+
const itemVariables = getVariablesFromFragment(fragment, item);
|
|
333
|
+
Object.assign(variables, itemVariables);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return variables;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function getVariablesFromFragment(
|
|
340
|
+
fragment: ReaderFragment,
|
|
341
|
+
item: mixed | $ReadOnlyArray<mixed>,
|
|
342
|
+
): Variables {
|
|
343
|
+
if (item == null) {
|
|
344
|
+
return {};
|
|
345
|
+
} else if (fragment.metadata?.plural === true) {
|
|
346
|
+
invariant(
|
|
347
|
+
Array.isArray(item),
|
|
348
|
+
'RelayModernSelector: Expected value for fragment `%s` to be an array, got `%s`. ' +
|
|
349
|
+
'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.',
|
|
350
|
+
fragment.name,
|
|
351
|
+
JSON.stringify(item),
|
|
352
|
+
fragment.name,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
return getVariablesFromPluralFragment(fragment, item);
|
|
356
|
+
} else {
|
|
357
|
+
invariant(
|
|
358
|
+
!Array.isArray(item),
|
|
359
|
+
'RelayModernFragmentSpecResolver: Expected value for fragment `%s` to be an object, got `%s`. ' +
|
|
360
|
+
'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.',
|
|
361
|
+
fragment.name,
|
|
362
|
+
JSON.stringify(item),
|
|
363
|
+
fragment.name,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
return getVariablesFromSingularFragment(fragment, item) || {};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function getVariablesFromSingularFragment(
|
|
371
|
+
fragment: ReaderFragment,
|
|
372
|
+
item: mixed,
|
|
373
|
+
): ?Variables {
|
|
374
|
+
const selector = getSingularSelector(fragment, item);
|
|
375
|
+
if (!selector) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
return selector.variables;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function getVariablesFromPluralFragment(
|
|
382
|
+
fragment: ReaderFragment,
|
|
383
|
+
items: $ReadOnlyArray<mixed>,
|
|
384
|
+
): Variables {
|
|
385
|
+
const variables = {};
|
|
386
|
+
items.forEach((value, ii) => {
|
|
387
|
+
if (value != null) {
|
|
388
|
+
const itemVariables = getVariablesFromSingularFragment(fragment, value);
|
|
389
|
+
if (itemVariables != null) {
|
|
390
|
+
Object.assign(variables, itemVariables);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
return variables;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* @public
|
|
399
|
+
*
|
|
400
|
+
* Determine if two selectors are equal (represent the same selection). Note
|
|
401
|
+
* that this function returns `false` when the two queries/fragments are
|
|
402
|
+
* different objects, even if they select the same fields.
|
|
403
|
+
*/
|
|
404
|
+
function areEqualSelectors(
|
|
405
|
+
thisSelector: SingularReaderSelector,
|
|
406
|
+
thatSelector: SingularReaderSelector,
|
|
407
|
+
): boolean {
|
|
408
|
+
return (
|
|
409
|
+
thisSelector.owner === thatSelector.owner &&
|
|
410
|
+
thisSelector.dataID === thatSelector.dataID &&
|
|
411
|
+
thisSelector.node === thatSelector.node &&
|
|
412
|
+
areEqual(thisSelector.variables, thatSelector.variables)
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function createReaderSelector(
|
|
417
|
+
fragment: ReaderFragment,
|
|
418
|
+
dataID: DataID,
|
|
419
|
+
variables: Variables,
|
|
420
|
+
request: RequestDescriptor,
|
|
421
|
+
isWithinUnmatchedTypeRefinement: boolean = false,
|
|
422
|
+
): SingularReaderSelector {
|
|
423
|
+
return {
|
|
424
|
+
kind: 'SingularReaderSelector',
|
|
425
|
+
dataID,
|
|
426
|
+
isWithinUnmatchedTypeRefinement,
|
|
427
|
+
node: fragment,
|
|
428
|
+
variables,
|
|
429
|
+
owner: request,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function createNormalizationSelector(
|
|
434
|
+
node: NormalizationSelectableNode,
|
|
435
|
+
dataID: DataID,
|
|
436
|
+
variables: Variables,
|
|
437
|
+
): NormalizationSelector {
|
|
438
|
+
return {dataID, node, variables};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
module.exports = {
|
|
442
|
+
areEqualSelectors,
|
|
443
|
+
createReaderSelector,
|
|
444
|
+
createNormalizationSelector,
|
|
445
|
+
getDataIDsFromFragment,
|
|
446
|
+
getDataIDsFromObject,
|
|
447
|
+
getSingularSelector,
|
|
448
|
+
getPluralSelector,
|
|
449
|
+
getSelector,
|
|
450
|
+
getSelectorsFromObject,
|
|
451
|
+
getVariablesFromSingularFragment,
|
|
452
|
+
getVariablesFromPluralFragment,
|
|
453
|
+
getVariablesFromFragment,
|
|
454
|
+
getVariablesFromObject,
|
|
455
|
+
};
|