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.
Files changed (157) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +34 -0
  2. package/handlers/connection/ConnectionHandler.js.flow +549 -0
  3. package/handlers/connection/ConnectionInterface.js.flow +92 -0
  4. package/index.js +1 -1
  5. package/index.js.flow +314 -0
  6. package/lib/handlers/RelayDefaultHandlerProvider.js +3 -2
  7. package/lib/handlers/connection/{RelayConnectionHandler.js → ConnectionHandler.js} +34 -35
  8. package/lib/handlers/connection/{RelayConnectionInterface.js → ConnectionInterface.js} +3 -30
  9. package/lib/index.js +29 -27
  10. package/lib/mutations/RelayDeclarativeMutationConfig.js +30 -52
  11. package/lib/mutations/RelayRecordProxy.js +6 -3
  12. package/lib/mutations/RelayRecordSourceMutator.js +3 -9
  13. package/lib/mutations/RelayRecordSourceProxy.js +21 -24
  14. package/lib/mutations/RelayRecordSourceSelectorProxy.js +18 -14
  15. package/lib/mutations/applyOptimisticMutation.js +2 -1
  16. package/lib/mutations/commitLocalUpdate.js +1 -0
  17. package/lib/mutations/commitMutation.js +26 -8
  18. package/lib/mutations/validateMutation.js +21 -11
  19. package/lib/network/ConvertToExecuteFunction.js +1 -0
  20. package/lib/network/RelayNetwork.js +1 -0
  21. package/lib/network/RelayNetworkTypes.js +1 -0
  22. package/lib/network/RelayObservable.js +10 -9
  23. package/lib/network/RelayQueryResponseCache.js +9 -7
  24. package/lib/query/{RelayModernGraphQLTag.js → GraphQLTag.js} +15 -8
  25. package/lib/query/fetchQuery.js +2 -1
  26. package/lib/query/fetchQueryInternal.js +30 -20
  27. package/lib/store/ClientID.js +1 -0
  28. package/lib/store/DataChecker.js +47 -97
  29. package/lib/store/RelayConcreteVariables.js +7 -2
  30. package/lib/store/RelayModernEnvironment.js +82 -41
  31. package/lib/store/RelayModernFragmentSpecResolver.js +61 -21
  32. package/lib/store/RelayModernOperationDescriptor.js +2 -1
  33. package/lib/store/RelayModernQueryExecutor.js +476 -333
  34. package/lib/store/RelayModernRecord.js +39 -9
  35. package/lib/store/RelayModernSelector.js +2 -1
  36. package/lib/store/RelayModernStore.js +359 -371
  37. package/lib/store/RelayOperationTracker.js +36 -78
  38. package/lib/store/RelayOptimisticRecordSource.js +8 -5
  39. package/lib/store/RelayPublishQueue.js +66 -53
  40. package/lib/store/RelayReader.js +2 -24
  41. package/lib/store/RelayRecordSource.js +3 -9
  42. package/lib/store/RelayRecordSourceMapImpl.js +14 -18
  43. package/lib/store/RelayRecordState.js +1 -0
  44. package/lib/store/RelayReferenceMarker.js +8 -58
  45. package/lib/store/RelayResponseNormalizer.js +15 -144
  46. package/lib/store/RelayStoreTypes.js +1 -0
  47. package/lib/store/RelayStoreUtils.js +34 -10
  48. package/lib/store/StoreInspector.js +11 -5
  49. package/lib/store/ViewerPattern.js +1 -0
  50. package/lib/store/cloneRelayHandleSourceField.js +1 -0
  51. package/lib/store/createFragmentSpecResolver.js +1 -0
  52. package/lib/store/createRelayContext.js +1 -0
  53. package/lib/store/defaultGetDataID.js +1 -0
  54. package/lib/store/hasOverlappingIDs.js +1 -0
  55. package/lib/store/isRelayModernEnvironment.js +1 -0
  56. package/lib/store/normalizeRelayPayload.js +8 -4
  57. package/lib/store/readInlineData.js +2 -1
  58. package/lib/subscription/requestSubscription.js +6 -3
  59. package/lib/util/JSResourceTypes.flow.js +12 -0
  60. package/lib/util/NormalizationNode.js +1 -0
  61. package/lib/util/ReaderNode.js +1 -0
  62. package/lib/util/RelayConcreteNode.js +3 -0
  63. package/lib/util/RelayDefaultHandleKey.js +1 -0
  64. package/lib/util/RelayError.js +2 -1
  65. package/lib/util/RelayFeatureFlags.js +3 -2
  66. package/lib/util/RelayProfiler.js +1 -0
  67. package/lib/util/RelayReplaySubject.js +2 -3
  68. package/lib/util/RelayRuntimeTypes.js +1 -0
  69. package/lib/util/createPayloadFor3DField.js +34 -0
  70. package/lib/util/deepFreeze.js +1 -0
  71. package/lib/util/generateID.js +1 -0
  72. package/lib/util/getFragmentIdentifier.js +1 -0
  73. package/lib/util/getRelayHandleKey.js +1 -0
  74. package/lib/util/getRequestIdentifier.js +1 -0
  75. package/lib/util/isPromise.js +1 -0
  76. package/lib/util/isScalarAndEqual.js +1 -0
  77. package/lib/util/recycleNodesInto.js +1 -0
  78. package/lib/util/resolveImmediate.js +1 -0
  79. package/lib/util/stableCopy.js +1 -0
  80. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  81. package/mutations/RelayRecordProxy.js.flow +165 -0
  82. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  83. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  84. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  85. package/mutations/applyOptimisticMutation.js.flow +76 -0
  86. package/mutations/commitLocalUpdate.js.flow +24 -0
  87. package/mutations/commitMutation.js.flow +184 -0
  88. package/mutations/validateMutation.js.flow +211 -0
  89. package/network/ConvertToExecuteFunction.js.flow +49 -0
  90. package/network/RelayNetwork.js.flow +84 -0
  91. package/network/RelayNetworkTypes.js.flow +123 -0
  92. package/network/RelayObservable.js.flow +634 -0
  93. package/network/RelayQueryResponseCache.js.flow +111 -0
  94. package/package.json +1 -1
  95. package/query/GraphQLTag.js.flow +166 -0
  96. package/query/fetchQuery.js.flow +47 -0
  97. package/query/fetchQueryInternal.js.flow +349 -0
  98. package/relay-runtime.js +2 -2
  99. package/relay-runtime.min.js +2 -2
  100. package/store/ClientID.js.flow +43 -0
  101. package/store/DataChecker.js.flow +426 -0
  102. package/store/RelayConcreteVariables.js.flow +96 -0
  103. package/store/RelayModernEnvironment.js.flow +526 -0
  104. package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
  105. package/store/RelayModernOperationDescriptor.js.flow +88 -0
  106. package/store/RelayModernQueryExecutor.js.flow +1327 -0
  107. package/store/RelayModernRecord.js.flow +403 -0
  108. package/store/RelayModernSelector.js.flow +444 -0
  109. package/store/RelayModernStore.js.flow +757 -0
  110. package/store/RelayOperationTracker.js.flow +164 -0
  111. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  112. package/store/RelayPublishQueue.js.flow +401 -0
  113. package/store/RelayReader.js.flow +376 -0
  114. package/store/RelayRecordSource.js.flow +29 -0
  115. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  116. package/store/RelayRecordState.js.flow +37 -0
  117. package/store/RelayReferenceMarker.js.flow +236 -0
  118. package/store/RelayResponseNormalizer.js.flow +556 -0
  119. package/store/RelayStoreTypes.js.flow +873 -0
  120. package/store/RelayStoreUtils.js.flow +218 -0
  121. package/store/StoreInspector.js.flow +173 -0
  122. package/store/ViewerPattern.js.flow +26 -0
  123. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  124. package/store/createFragmentSpecResolver.js.flow +55 -0
  125. package/store/createRelayContext.js.flow +44 -0
  126. package/store/defaultGetDataID.js.flow +27 -0
  127. package/store/hasOverlappingIDs.js.flow +34 -0
  128. package/store/isRelayModernEnvironment.js.flow +27 -0
  129. package/store/normalizeRelayPayload.js.flow +51 -0
  130. package/store/readInlineData.js.flow +75 -0
  131. package/subscription/requestSubscription.js.flow +100 -0
  132. package/util/JSResourceTypes.flow.js.flow +20 -0
  133. package/util/NormalizationNode.js.flow +191 -0
  134. package/util/ReaderNode.js.flow +208 -0
  135. package/util/RelayConcreteNode.js.flow +80 -0
  136. package/util/RelayDefaultHandleKey.js.flow +17 -0
  137. package/util/RelayError.js.flow +33 -0
  138. package/util/RelayFeatureFlags.js.flow +30 -0
  139. package/util/RelayProfiler.js.flow +284 -0
  140. package/util/RelayReplaySubject.js.flow +134 -0
  141. package/util/RelayRuntimeTypes.js.flow +70 -0
  142. package/util/createPayloadFor3DField.js.flow +43 -0
  143. package/util/deepFreeze.js.flow +36 -0
  144. package/util/generateID.js.flow +21 -0
  145. package/util/getFragmentIdentifier.js.flow +52 -0
  146. package/util/getRelayHandleKey.js.flow +41 -0
  147. package/util/getRequestIdentifier.js.flow +41 -0
  148. package/util/isPromise.js.flow +21 -0
  149. package/util/isScalarAndEqual.js.flow +26 -0
  150. package/util/recycleNodesInto.js.flow +80 -0
  151. package/util/resolveImmediate.js.flow +30 -0
  152. package/util/stableCopy.js.flow +35 -0
  153. package/lib/handlers/RelayDefaultMissingFieldHandlers.js +0 -26
  154. package/lib/store/RelayConnection.js +0 -36
  155. package/lib/store/RelayConnectionResolver.js +0 -177
  156. package/lib/store/RelayRecordSourceObjectImpl.js +0 -78
  157. 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 strict-local
8
+ * @format
9
+ * @emails oncall+relay
10
+ */
11
+
12
+ // flowlint ambiguous-object-type:error
13
+
14
+ 'use strict';
15
+
16
+ const RelayConcreteNode = require('../util/RelayConcreteNode');
17
+ const RelayModernRecord = require('./RelayModernRecord');
18
+ const RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator');
19
+ const RelayRecordSourceProxy = require('../mutations/RelayRecordSourceProxy');
20
+ const RelayStoreUtils = require('./RelayStoreUtils');
21
+
22
+ const cloneRelayHandleSourceField = require('./cloneRelayHandleSourceField');
23
+ const invariant = require('invariant');
24
+
25
+ const {isClientID} = require('./ClientID');
26
+ const {EXISTENT, UNKNOWN} = require('./RelayRecordState');
27
+
28
+ import type {
29
+ NormalizationField,
30
+ NormalizationLinkedField,
31
+ NormalizationModuleImport,
32
+ NormalizationNode,
33
+ NormalizationScalarField,
34
+ NormalizationSelection,
35
+ } from '../util/NormalizationNode';
36
+ import type {DataID, Variables} from '../util/RelayRuntimeTypes';
37
+ import type {GetDataID} from './RelayResponseNormalizer';
38
+ import type {
39
+ MissingFieldHandler,
40
+ MutableRecordSource,
41
+ NormalizationSelector,
42
+ OperationLoader,
43
+ Record,
44
+ RecordSource,
45
+ } from './RelayStoreTypes';
46
+
47
+ export type Availability = {|
48
+ +status: 'available' | 'missing',
49
+ +mostRecentlyInvalidatedAt: ?number,
50
+ |};
51
+
52
+ const {
53
+ CONDITION,
54
+ CLIENT_EXTENSION,
55
+ DEFER,
56
+ FRAGMENT_SPREAD,
57
+ INLINE_FRAGMENT,
58
+ LINKED_FIELD,
59
+ LINKED_HANDLE,
60
+ MODULE_IMPORT,
61
+ SCALAR_FIELD,
62
+ SCALAR_HANDLE,
63
+ STREAM,
64
+ } = RelayConcreteNode;
65
+ const {
66
+ getModuleOperationKey,
67
+ getStorageKey,
68
+ getArgumentValues,
69
+ } = RelayStoreUtils;
70
+
71
+ /**
72
+ * Synchronously check whether the records required to fulfill the given
73
+ * `selector` are present in `source`.
74
+ *
75
+ * If a field is missing, it uses the provided handlers to attempt to substitute
76
+ * data. The `target` will store all records that are modified because of a
77
+ * successful substitution.
78
+ *
79
+ * If all records are present, returns `true`, otherwise `false`.
80
+ */
81
+ function check(
82
+ source: RecordSource,
83
+ target: MutableRecordSource,
84
+ selector: NormalizationSelector,
85
+ handlers: $ReadOnlyArray<MissingFieldHandler>,
86
+ operationLoader: ?OperationLoader,
87
+ getDataID: GetDataID,
88
+ ): Availability {
89
+ const {dataID, node, variables} = selector;
90
+ const checker = new DataChecker(
91
+ source,
92
+ target,
93
+ variables,
94
+ handlers,
95
+ operationLoader,
96
+ getDataID,
97
+ );
98
+ return checker.check(node, dataID);
99
+ }
100
+
101
+ /**
102
+ * @private
103
+ */
104
+ class DataChecker {
105
+ _handlers: $ReadOnlyArray<MissingFieldHandler>;
106
+ _mostRecentlyInvalidatedAt: number | null;
107
+ _mutator: RelayRecordSourceMutator;
108
+ _operationLoader: OperationLoader | null;
109
+ _operationLastWrittenAt: ?number;
110
+ _recordSourceProxy: RelayRecordSourceProxy;
111
+ _recordWasMissing: boolean;
112
+ _source: RecordSource;
113
+ _variables: Variables;
114
+
115
+ constructor(
116
+ source: RecordSource,
117
+ target: MutableRecordSource,
118
+ variables: Variables,
119
+ handlers: $ReadOnlyArray<MissingFieldHandler>,
120
+ operationLoader: ?OperationLoader,
121
+ getDataID: GetDataID,
122
+ ) {
123
+ const mutator = new RelayRecordSourceMutator(source, target);
124
+ this._mostRecentlyInvalidatedAt = null;
125
+ this._handlers = handlers;
126
+ this._mutator = mutator;
127
+ this._operationLoader = operationLoader ?? null;
128
+ this._recordSourceProxy = new RelayRecordSourceProxy(mutator, getDataID);
129
+ this._recordWasMissing = false;
130
+ this._source = source;
131
+ this._variables = variables;
132
+ }
133
+
134
+ check(node: NormalizationNode, dataID: DataID): Availability {
135
+ this._traverse(node, dataID);
136
+
137
+ return this._recordWasMissing === true
138
+ ? {
139
+ status: 'missing',
140
+ mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt,
141
+ }
142
+ : {
143
+ status: 'available',
144
+ mostRecentlyInvalidatedAt: this._mostRecentlyInvalidatedAt,
145
+ };
146
+ }
147
+
148
+ _getVariableValue(name: string): mixed {
149
+ invariant(
150
+ this._variables.hasOwnProperty(name),
151
+ 'RelayAsyncLoader(): Undefined variable `%s`.',
152
+ name,
153
+ );
154
+ return this._variables[name];
155
+ }
156
+
157
+ _handleMissing(): void {
158
+ this._recordWasMissing = true;
159
+ }
160
+
161
+ _getDataForHandlers(
162
+ field: NormalizationField,
163
+ dataID: DataID,
164
+ ): {
165
+ args: Variables,
166
+ record: ?Record,
167
+ ...
168
+ } {
169
+ return {
170
+ args: field.args ? getArgumentValues(field.args, this._variables) : {},
171
+ // Getting a snapshot of the record state is potentially expensive since
172
+ // we will need to merge the sink and source records. Since we do not create
173
+ // any new records in this process, it is probably reasonable to provide
174
+ // handlers with a copy of the source record.
175
+ // The only thing that the provided record will not contain is fields
176
+ // added by previous handlers.
177
+ record: this._source.get(dataID),
178
+ };
179
+ }
180
+
181
+ _handleMissingScalarField(
182
+ field: NormalizationScalarField,
183
+ dataID: DataID,
184
+ ): mixed {
185
+ if (field.name === 'id' && field.alias == null && isClientID(dataID)) {
186
+ return undefined;
187
+ }
188
+ const {args, record} = this._getDataForHandlers(field, dataID);
189
+ for (const handler of this._handlers) {
190
+ if (handler.kind === 'scalar') {
191
+ const newValue = handler.handle(
192
+ field,
193
+ record,
194
+ args,
195
+ this._recordSourceProxy,
196
+ );
197
+ if (newValue !== undefined) {
198
+ return newValue;
199
+ }
200
+ }
201
+ }
202
+ this._handleMissing();
203
+ }
204
+
205
+ _handleMissingLinkField(
206
+ field: NormalizationLinkedField,
207
+ dataID: DataID,
208
+ ): ?DataID {
209
+ const {args, record} = this._getDataForHandlers(field, dataID);
210
+ for (const handler of this._handlers) {
211
+ if (handler.kind === 'linked') {
212
+ const newValue = handler.handle(
213
+ field,
214
+ record,
215
+ args,
216
+ this._recordSourceProxy,
217
+ );
218
+ if (
219
+ newValue != null &&
220
+ this._mutator.getStatus(newValue) === EXISTENT
221
+ ) {
222
+ return newValue;
223
+ }
224
+ }
225
+ }
226
+ this._handleMissing();
227
+ }
228
+
229
+ _handleMissingPluralLinkField(
230
+ field: NormalizationLinkedField,
231
+ dataID: DataID,
232
+ ): ?Array<?DataID> {
233
+ const {args, record} = this._getDataForHandlers(field, dataID);
234
+ for (const handler of this._handlers) {
235
+ if (handler.kind === 'pluralLinked') {
236
+ const newValue = handler.handle(
237
+ field,
238
+ record,
239
+ args,
240
+ this._recordSourceProxy,
241
+ );
242
+ if (newValue != null) {
243
+ const allItemsKnown = newValue.every(
244
+ linkedID =>
245
+ linkedID != null &&
246
+ this._mutator.getStatus(linkedID) === EXISTENT,
247
+ );
248
+ if (allItemsKnown) {
249
+ return newValue;
250
+ }
251
+ }
252
+ }
253
+ }
254
+ this._handleMissing();
255
+ }
256
+
257
+ _traverse(node: NormalizationNode, dataID: DataID): void {
258
+ const status = this._mutator.getStatus(dataID);
259
+ if (status === UNKNOWN) {
260
+ this._handleMissing();
261
+ }
262
+
263
+ if (status === EXISTENT) {
264
+ const record = this._source.get(dataID);
265
+ const invalidatedAt = RelayModernRecord.getInvalidationEpoch(record);
266
+ if (invalidatedAt != null) {
267
+ this._mostRecentlyInvalidatedAt =
268
+ this._mostRecentlyInvalidatedAt != null
269
+ ? Math.max(this._mostRecentlyInvalidatedAt, invalidatedAt)
270
+ : invalidatedAt;
271
+ }
272
+
273
+ this._traverseSelections(node.selections, dataID);
274
+ }
275
+ }
276
+
277
+ _traverseSelections(
278
+ selections: $ReadOnlyArray<NormalizationSelection>,
279
+ dataID: DataID,
280
+ ): void {
281
+ selections.forEach(selection => {
282
+ switch (selection.kind) {
283
+ case SCALAR_FIELD:
284
+ this._checkScalar(selection, dataID);
285
+ break;
286
+ case LINKED_FIELD:
287
+ if (selection.plural) {
288
+ this._checkPluralLink(selection, dataID);
289
+ } else {
290
+ this._checkLink(selection, dataID);
291
+ }
292
+ break;
293
+ case CONDITION:
294
+ const conditionValue = this._getVariableValue(selection.condition);
295
+ if (conditionValue === selection.passingValue) {
296
+ this._traverseSelections(selection.selections, dataID);
297
+ }
298
+ break;
299
+ case INLINE_FRAGMENT:
300
+ const typeName = this._mutator.getType(dataID);
301
+ if (typeName != null && typeName === selection.type) {
302
+ this._traverseSelections(selection.selections, dataID);
303
+ }
304
+ break;
305
+ case LINKED_HANDLE:
306
+ // Handles have no selections themselves; traverse the original field
307
+ // where the handle was set-up instead.
308
+ const handleField = cloneRelayHandleSourceField(
309
+ selection,
310
+ selections,
311
+ this._variables,
312
+ );
313
+ if (handleField.plural) {
314
+ this._checkPluralLink(handleField, dataID);
315
+ } else {
316
+ this._checkLink(handleField, dataID);
317
+ }
318
+ break;
319
+ case MODULE_IMPORT:
320
+ this._checkModuleImport(selection, dataID);
321
+ break;
322
+ case DEFER:
323
+ case STREAM:
324
+ this._traverseSelections(selection.selections, dataID);
325
+ break;
326
+ case SCALAR_HANDLE:
327
+ case FRAGMENT_SPREAD:
328
+ invariant(
329
+ false,
330
+ 'RelayAsyncLoader(): Unexpected ast kind `%s`.',
331
+ selection.kind,
332
+ );
333
+ // $FlowExpectedError - we need the break; for OSS linter
334
+ break;
335
+ case CLIENT_EXTENSION:
336
+ const recordWasMissing = this._recordWasMissing;
337
+ this._traverseSelections(selection.selections, dataID);
338
+ this._recordWasMissing = recordWasMissing;
339
+ break;
340
+ default:
341
+ (selection: empty);
342
+ invariant(
343
+ false,
344
+ 'RelayAsyncLoader(): Unexpected ast kind `%s`.',
345
+ selection.kind,
346
+ );
347
+ }
348
+ });
349
+ }
350
+
351
+ _checkModuleImport(
352
+ moduleImport: NormalizationModuleImport,
353
+ dataID: DataID,
354
+ ): void {
355
+ const operationLoader = this._operationLoader;
356
+ invariant(
357
+ operationLoader !== null,
358
+ 'DataChecker: Expected an operationLoader to be configured when using `@module`.',
359
+ );
360
+ const operationKey = getModuleOperationKey(moduleImport.documentName);
361
+ const operationReference = this._mutator.getValue(dataID, operationKey);
362
+ if (operationReference == null) {
363
+ if (operationReference === undefined) {
364
+ this._handleMissing();
365
+ }
366
+ return;
367
+ }
368
+ const operation = operationLoader.get(operationReference);
369
+ if (operation != null) {
370
+ this._traverse(operation, dataID);
371
+ } else {
372
+ // If the fragment is not available, we assume that the data cannot have been
373
+ // processed yet and must therefore be missing.
374
+ this._handleMissing();
375
+ }
376
+ }
377
+
378
+ _checkScalar(field: NormalizationScalarField, dataID: DataID): void {
379
+ const storageKey = getStorageKey(field, this._variables);
380
+ let fieldValue = this._mutator.getValue(dataID, storageKey);
381
+ if (fieldValue === undefined) {
382
+ fieldValue = this._handleMissingScalarField(field, dataID);
383
+ if (fieldValue !== undefined) {
384
+ this._mutator.setValue(dataID, storageKey, fieldValue);
385
+ }
386
+ }
387
+ }
388
+
389
+ _checkLink(field: NormalizationLinkedField, dataID: DataID): void {
390
+ const storageKey = getStorageKey(field, this._variables);
391
+ let linkedID = this._mutator.getLinkedRecordID(dataID, storageKey);
392
+
393
+ if (linkedID === undefined) {
394
+ linkedID = this._handleMissingLinkField(field, dataID);
395
+ if (linkedID != null) {
396
+ this._mutator.setLinkedRecordID(dataID, storageKey, linkedID);
397
+ }
398
+ }
399
+ if (linkedID != null) {
400
+ this._traverse(field, linkedID);
401
+ }
402
+ }
403
+
404
+ _checkPluralLink(field: NormalizationLinkedField, dataID: DataID): void {
405
+ const storageKey = getStorageKey(field, this._variables);
406
+ let linkedIDs = this._mutator.getLinkedRecordIDs(dataID, storageKey);
407
+
408
+ if (linkedIDs === undefined) {
409
+ linkedIDs = this._handleMissingPluralLinkField(field, dataID);
410
+ if (linkedIDs != null) {
411
+ this._mutator.setLinkedRecordIDs(dataID, storageKey, linkedIDs);
412
+ }
413
+ }
414
+ if (linkedIDs) {
415
+ linkedIDs.forEach(linkedID => {
416
+ if (linkedID != null) {
417
+ this._traverse(field, linkedID);
418
+ }
419
+ });
420
+ }
421
+ }
422
+ }
423
+
424
+ module.exports = {
425
+ check,
426
+ };
@@ -0,0 +1,96 @@
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 invariant = require('invariant');
16
+
17
+ import type {NormalizationOperation} from '../util/NormalizationNode';
18
+ import type {ReaderFragment} from '../util/ReaderNode';
19
+ import type {Variables} from '../util/RelayRuntimeTypes';
20
+
21
+ /**
22
+ * Determines the variables that are in scope for a fragment given the variables
23
+ * in scope at the root query as well as any arguments applied at the fragment
24
+ * spread via `@arguments`.
25
+ *
26
+ * Note that this is analagous to determining function arguments given a function call.
27
+ */
28
+ function getFragmentVariables(
29
+ fragment: ReaderFragment,
30
+ rootVariables: Variables,
31
+ argumentVariables: Variables,
32
+ ): Variables {
33
+ let variables;
34
+ fragment.argumentDefinitions.forEach(definition => {
35
+ if (argumentVariables.hasOwnProperty(definition.name)) {
36
+ return;
37
+ }
38
+ variables = variables || {...argumentVariables};
39
+ switch (definition.kind) {
40
+ case 'LocalArgument':
41
+ variables[definition.name] = definition.defaultValue;
42
+ break;
43
+ case 'RootArgument':
44
+ if (!rootVariables.hasOwnProperty(definition.name)) {
45
+ /*
46
+ * Global variables passed as values of @arguments are not required to
47
+ * be declared unless they are used by the callee fragment or a
48
+ * descendant. In this case, the root variable may not be defined when
49
+ * resolving the callee's variables. The value is explicitly set to
50
+ * undefined to conform to the check in
51
+ * RelayStoreUtils.getStableVariableValue() that variable keys are all
52
+ * present.
53
+ */
54
+ variables[definition.name] = undefined;
55
+ break;
56
+ }
57
+ variables[definition.name] = rootVariables[definition.name];
58
+ break;
59
+ default:
60
+ (definition: empty);
61
+ invariant(
62
+ false,
63
+ 'RelayConcreteVariables: Unexpected node kind `%s` in fragment `%s`.',
64
+ definition.kind,
65
+ fragment.name,
66
+ );
67
+ }
68
+ });
69
+ return variables || argumentVariables;
70
+ }
71
+
72
+ /**
73
+ * Determines the variables that are in scope for a given operation given values
74
+ * for some/all of its arguments. Extraneous input variables are filtered from
75
+ * the output, and missing variables are set to default values (if given in the
76
+ * operation's definition).
77
+ */
78
+ function getOperationVariables(
79
+ operation: NormalizationOperation,
80
+ variables: Variables,
81
+ ): Variables {
82
+ const operationVariables = {};
83
+ operation.argumentDefinitions.forEach(def => {
84
+ let value = def.defaultValue;
85
+ if (variables[def.name] != null) {
86
+ value = variables[def.name];
87
+ }
88
+ operationVariables[def.name] = value;
89
+ });
90
+ return operationVariables;
91
+ }
92
+
93
+ module.exports = {
94
+ getFragmentVariables,
95
+ getOperationVariables,
96
+ };