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,556 @@
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 RelayModernRecord = require('./RelayModernRecord');
16
+ const RelayProfiler = require('../util/RelayProfiler');
17
+
18
+ const invariant = require('invariant');
19
+ const warning = require('warning');
20
+
21
+ const {
22
+ CONDITION,
23
+ CLIENT_EXTENSION,
24
+ DEFER,
25
+ INLINE_FRAGMENT,
26
+ LINKED_FIELD,
27
+ LINKED_HANDLE,
28
+ MODULE_IMPORT,
29
+ SCALAR_FIELD,
30
+ SCALAR_HANDLE,
31
+ STREAM,
32
+ } = require('../util/RelayConcreteNode');
33
+ const {generateClientID, isClientID} = require('./ClientID');
34
+ const {createNormalizationSelector} = require('./RelayModernSelector');
35
+ const {
36
+ getArgumentValues,
37
+ getHandleStorageKey,
38
+ getModuleComponentKey,
39
+ getModuleOperationKey,
40
+ getStorageKey,
41
+ TYPENAME_KEY,
42
+ ROOT_ID,
43
+ } = require('./RelayStoreUtils');
44
+
45
+ import type {PayloadData} from '../network/RelayNetworkTypes';
46
+ import type {
47
+ NormalizationDefer,
48
+ NormalizationLinkedField,
49
+ NormalizationModuleImport,
50
+ NormalizationNode,
51
+ NormalizationScalarField,
52
+ NormalizationStream,
53
+ } from '../util/NormalizationNode';
54
+ import type {DataID, Variables} from '../util/RelayRuntimeTypes';
55
+ import type {
56
+ HandleFieldPayload,
57
+ IncrementalDataPlaceholder,
58
+ ModuleImportPayload,
59
+ MutableRecordSource,
60
+ NormalizationSelector,
61
+ Record,
62
+ RelayResponsePayload,
63
+ RequestDescriptor,
64
+ } from './RelayStoreTypes';
65
+
66
+ export type GetDataID = (
67
+ fieldValue: {[string]: mixed, ...},
68
+ typeName: string,
69
+ ) => mixed;
70
+
71
+ export type NormalizationOptions = {|
72
+ +getDataID: GetDataID,
73
+ +treatMissingFieldsAsNull: boolean,
74
+ +path?: $ReadOnlyArray<string>,
75
+ +request: RequestDescriptor,
76
+ |};
77
+
78
+ /**
79
+ * Normalizes the results of a query and standard GraphQL response, writing the
80
+ * normalized records/fields into the given MutableRecordSource.
81
+ */
82
+ function normalize(
83
+ recordSource: MutableRecordSource,
84
+ selector: NormalizationSelector,
85
+ response: PayloadData,
86
+ options: NormalizationOptions,
87
+ ): RelayResponsePayload {
88
+ const {dataID, node, variables} = selector;
89
+ const normalizer = new RelayResponseNormalizer(
90
+ recordSource,
91
+ variables,
92
+ options,
93
+ );
94
+ return normalizer.normalizeResponse(node, dataID, response);
95
+ }
96
+
97
+ /**
98
+ * @private
99
+ *
100
+ * Helper for handling payloads.
101
+ */
102
+ class RelayResponseNormalizer {
103
+ _getDataId: GetDataID;
104
+ _handleFieldPayloads: Array<HandleFieldPayload>;
105
+ _treatMissingFieldsAsNull: boolean;
106
+ _incrementalPlaceholders: Array<IncrementalDataPlaceholder>;
107
+ _isClientExtension: boolean;
108
+ _moduleImportPayloads: Array<ModuleImportPayload>;
109
+ _path: Array<string>;
110
+ _recordSource: MutableRecordSource;
111
+ _request: RequestDescriptor;
112
+ _variables: Variables;
113
+
114
+ constructor(
115
+ recordSource: MutableRecordSource,
116
+ variables: Variables,
117
+ options: NormalizationOptions,
118
+ ) {
119
+ this._getDataId = options.getDataID;
120
+ this._handleFieldPayloads = [];
121
+ this._treatMissingFieldsAsNull = options.treatMissingFieldsAsNull;
122
+ this._incrementalPlaceholders = [];
123
+ this._isClientExtension = false;
124
+ this._moduleImportPayloads = [];
125
+ this._path = options.path ? [...options.path] : [];
126
+ this._recordSource = recordSource;
127
+ this._request = options.request;
128
+ this._variables = variables;
129
+ }
130
+
131
+ normalizeResponse(
132
+ node: NormalizationNode,
133
+ dataID: DataID,
134
+ data: PayloadData,
135
+ ): RelayResponsePayload {
136
+ const record = this._recordSource.get(dataID);
137
+ invariant(
138
+ record,
139
+ 'RelayResponseNormalizer(): Expected root record `%s` to exist.',
140
+ dataID,
141
+ );
142
+ this._traverseSelections(node, record, data);
143
+ return {
144
+ errors: null,
145
+ fieldPayloads: this._handleFieldPayloads,
146
+ incrementalPlaceholders: this._incrementalPlaceholders,
147
+ moduleImportPayloads: this._moduleImportPayloads,
148
+ source: this._recordSource,
149
+ isFinal: false,
150
+ };
151
+ }
152
+
153
+ _getVariableValue(name: string): mixed {
154
+ invariant(
155
+ this._variables.hasOwnProperty(name),
156
+ 'RelayResponseNormalizer(): Undefined variable `%s`.',
157
+ name,
158
+ );
159
+ return this._variables[name];
160
+ }
161
+
162
+ _getRecordType(data: PayloadData): string {
163
+ const typeName = (data: any)[TYPENAME_KEY];
164
+ invariant(
165
+ typeName != null,
166
+ 'RelayResponseNormalizer(): Expected a typename for record `%s`.',
167
+ JSON.stringify(data, null, 2),
168
+ );
169
+ return typeName;
170
+ }
171
+
172
+ _traverseSelections(
173
+ node: NormalizationNode,
174
+ record: Record,
175
+ data: PayloadData,
176
+ ): void {
177
+ for (let i = 0; i < node.selections.length; i++) {
178
+ const selection = node.selections[i];
179
+ switch (selection.kind) {
180
+ case SCALAR_FIELD:
181
+ case LINKED_FIELD:
182
+ this._normalizeField(node, selection, record, data);
183
+ break;
184
+ case CONDITION:
185
+ const conditionValue = this._getVariableValue(selection.condition);
186
+ if (conditionValue === selection.passingValue) {
187
+ this._traverseSelections(selection, record, data);
188
+ }
189
+ break;
190
+ case INLINE_FRAGMENT:
191
+ const typeName = RelayModernRecord.getType(record);
192
+ if (typeName === selection.type) {
193
+ this._traverseSelections(selection, record, data);
194
+ }
195
+ break;
196
+ case LINKED_HANDLE:
197
+ case SCALAR_HANDLE:
198
+ const args = selection.args
199
+ ? getArgumentValues(selection.args, this._variables)
200
+ : {};
201
+ const fieldKey = getStorageKey(selection, this._variables);
202
+ const handleKey = getHandleStorageKey(selection, this._variables);
203
+ this._handleFieldPayloads.push({
204
+ args,
205
+ dataID: RelayModernRecord.getDataID(record),
206
+ fieldKey,
207
+ handle: selection.handle,
208
+ handleKey,
209
+ });
210
+ break;
211
+ case MODULE_IMPORT:
212
+ this._normalizeModuleImport(node, selection, record, data);
213
+ break;
214
+ case DEFER:
215
+ this._normalizeDefer(selection, record, data);
216
+ break;
217
+ case STREAM:
218
+ this._normalizeStream(selection, record, data);
219
+ break;
220
+ case CLIENT_EXTENSION:
221
+ const isClientExtension = this._isClientExtension;
222
+ this._isClientExtension = true;
223
+ this._traverseSelections(selection, record, data);
224
+ this._isClientExtension = isClientExtension;
225
+ break;
226
+ default:
227
+ (selection: empty);
228
+ invariant(
229
+ false,
230
+ 'RelayResponseNormalizer(): Unexpected ast kind `%s`.',
231
+ selection.kind,
232
+ );
233
+ }
234
+ }
235
+ }
236
+
237
+ _normalizeDefer(
238
+ defer: NormalizationDefer,
239
+ record: Record,
240
+ data: PayloadData,
241
+ ) {
242
+ const isDeferred = defer.if === null || this._getVariableValue(defer.if);
243
+ if (__DEV__) {
244
+ warning(
245
+ typeof isDeferred === 'boolean',
246
+ 'RelayResponseNormalizer: Expected value for @defer `if` argument to ' +
247
+ 'be a boolean, got `%s`.',
248
+ isDeferred,
249
+ );
250
+ }
251
+ if (isDeferred === false) {
252
+ // If defer is disabled there will be no additional response chunk:
253
+ // normalize the data already present.
254
+ this._traverseSelections(defer, record, data);
255
+ } else {
256
+ // Otherwise data *for this selection* should not be present: enqueue
257
+ // metadata to process the subsequent response chunk.
258
+ this._incrementalPlaceholders.push({
259
+ kind: 'defer',
260
+ data,
261
+ label: defer.label,
262
+ path: [...this._path],
263
+ selector: createNormalizationSelector(
264
+ defer,
265
+ RelayModernRecord.getDataID(record),
266
+ this._variables,
267
+ ),
268
+ typeName: RelayModernRecord.getType(record),
269
+ });
270
+ }
271
+ }
272
+
273
+ _normalizeStream(
274
+ stream: NormalizationStream,
275
+ record: Record,
276
+ data: PayloadData,
277
+ ) {
278
+ // Always normalize regardless of whether streaming is enabled or not,
279
+ // this populates the initial array value (including any items when
280
+ // initial_count > 0).
281
+ this._traverseSelections(stream, record, data);
282
+ const isStreamed = stream.if === null || this._getVariableValue(stream.if);
283
+ if (__DEV__) {
284
+ warning(
285
+ typeof isStreamed === 'boolean',
286
+ 'RelayResponseNormalizer: Expected value for @stream `if` argument ' +
287
+ 'to be a boolean, got `%s`.',
288
+ isStreamed,
289
+ );
290
+ }
291
+ if (isStreamed === true) {
292
+ // If streaming is enabled, *also* emit metadata to process any
293
+ // response chunks that may be delivered.
294
+ this._incrementalPlaceholders.push({
295
+ kind: 'stream',
296
+ label: stream.label,
297
+ path: [...this._path],
298
+ parentID: RelayModernRecord.getDataID(record),
299
+ node: stream,
300
+ variables: this._variables,
301
+ });
302
+ }
303
+ }
304
+
305
+ _normalizeModuleImport(
306
+ parent: NormalizationNode,
307
+ moduleImport: NormalizationModuleImport,
308
+ record: Record,
309
+ data: PayloadData,
310
+ ) {
311
+ invariant(
312
+ typeof data === 'object' && data,
313
+ 'RelayResponseNormalizer: Expected data for @module to be an object.',
314
+ );
315
+ const typeName: string = RelayModernRecord.getType(record);
316
+ const componentKey = getModuleComponentKey(moduleImport.documentName);
317
+ const componentReference = data[componentKey];
318
+ RelayModernRecord.setValue(
319
+ record,
320
+ componentKey,
321
+ componentReference ?? null,
322
+ );
323
+ const operationKey = getModuleOperationKey(moduleImport.documentName);
324
+ const operationReference = data[operationKey];
325
+ RelayModernRecord.setValue(
326
+ record,
327
+ operationKey,
328
+ operationReference ?? null,
329
+ );
330
+ if (operationReference != null) {
331
+ this._moduleImportPayloads.push({
332
+ data,
333
+ dataID: RelayModernRecord.getDataID(record),
334
+ operationReference,
335
+ path: [...this._path],
336
+ typeName,
337
+ variables: this._variables,
338
+ });
339
+ }
340
+ }
341
+
342
+ _normalizeField(
343
+ parent: NormalizationNode,
344
+ selection: NormalizationLinkedField | NormalizationScalarField,
345
+ record: Record,
346
+ data: PayloadData,
347
+ ) {
348
+ invariant(
349
+ typeof data === 'object' && data,
350
+ 'writeField(): Expected data for field `%s` to be an object.',
351
+ selection.name,
352
+ );
353
+ const responseKey = selection.alias || selection.name;
354
+ const storageKey = getStorageKey(selection, this._variables);
355
+ const fieldValue = data[responseKey];
356
+ if (fieldValue == null) {
357
+ if (!this._treatMissingFieldsAsNull && fieldValue === undefined) {
358
+ // Fields that are missing in the response are not set on the record.
359
+ // There are three main cases where this can occur:
360
+ // - Inside a client extension: the server will not generally return
361
+ // values for these fields, but a local update may provide them.
362
+ // - Fields on abstract types: these may be missing if the concrete
363
+ // response type does not match the abstract type.
364
+ //
365
+ // Otherwise, missing fields usually indicate a server or user error (
366
+ // the latter for manually constructed payloads).
367
+ if (__DEV__) {
368
+ warning(
369
+ this._isClientExtension ||
370
+ (parent.kind === LINKED_FIELD && parent.concreteType == null)
371
+ ? true
372
+ : Object.prototype.hasOwnProperty.call(data, responseKey),
373
+ 'RelayResponseNormalizer: Payload did not contain a value ' +
374
+ 'for field `%s: %s`. Check that you are parsing with the same ' +
375
+ 'query that was used to fetch the payload.',
376
+ responseKey,
377
+ storageKey,
378
+ );
379
+ }
380
+ return;
381
+ }
382
+ RelayModernRecord.setValue(record, storageKey, null);
383
+ return;
384
+ }
385
+
386
+ if (selection.kind === SCALAR_FIELD) {
387
+ RelayModernRecord.setValue(record, storageKey, fieldValue);
388
+ } else if (selection.kind === LINKED_FIELD) {
389
+ this._path.push(responseKey);
390
+ if (selection.plural) {
391
+ this._normalizePluralLink(selection, record, storageKey, fieldValue);
392
+ } else {
393
+ this._normalizeLink(selection, record, storageKey, fieldValue);
394
+ }
395
+ this._path.pop();
396
+ } else {
397
+ (selection: empty);
398
+ invariant(
399
+ false,
400
+ 'RelayResponseNormalizer(): Unexpected ast kind `%s` during normalization.',
401
+ selection.kind,
402
+ );
403
+ }
404
+ }
405
+
406
+ _normalizeLink(
407
+ field: NormalizationLinkedField,
408
+ record: Record,
409
+ storageKey: string,
410
+ fieldValue: mixed,
411
+ ): void {
412
+ invariant(
413
+ typeof fieldValue === 'object' && fieldValue,
414
+ 'RelayResponseNormalizer: Expected data for field `%s` to be an object.',
415
+ storageKey,
416
+ );
417
+ const nextID =
418
+ this._getDataId(
419
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
420
+ * suppresses an error found when Flow v0.98 was deployed. To see the
421
+ * error delete this comment and run Flow. */
422
+ fieldValue,
423
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
424
+ * suppresses an error found when Flow v0.98 was deployed. To see the
425
+ * error delete this comment and run Flow. */
426
+ field.concreteType ?? this._getRecordType(fieldValue),
427
+ ) ||
428
+ // Reuse previously generated client IDs
429
+ RelayModernRecord.getLinkedRecordID(record, storageKey) ||
430
+ generateClientID(RelayModernRecord.getDataID(record), storageKey);
431
+ invariant(
432
+ typeof nextID === 'string',
433
+ 'RelayResponseNormalizer: Expected id on field `%s` to be a string.',
434
+ storageKey,
435
+ );
436
+ RelayModernRecord.setLinkedRecordID(record, storageKey, nextID);
437
+ let nextRecord = this._recordSource.get(nextID);
438
+ if (!nextRecord) {
439
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
440
+ * suppresses an error found when Flow v0.98 was deployed. To see the
441
+ * error delete this comment and run Flow. */
442
+ const typeName = field.concreteType || this._getRecordType(fieldValue);
443
+ nextRecord = RelayModernRecord.create(nextID, typeName);
444
+ this._recordSource.set(nextID, nextRecord);
445
+ } else if (__DEV__) {
446
+ this._validateRecordType(nextRecord, field, fieldValue);
447
+ }
448
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
449
+ * suppresses an error found when Flow v0.98 was deployed. To see the error
450
+ * delete this comment and run Flow. */
451
+ this._traverseSelections(field, nextRecord, fieldValue);
452
+ }
453
+
454
+ _normalizePluralLink(
455
+ field: NormalizationLinkedField,
456
+ record: Record,
457
+ storageKey: string,
458
+ fieldValue: mixed,
459
+ ): void {
460
+ invariant(
461
+ Array.isArray(fieldValue),
462
+ 'RelayResponseNormalizer: Expected data for field `%s` to be an array ' +
463
+ 'of objects.',
464
+ storageKey,
465
+ );
466
+ const prevIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey);
467
+ const nextIDs = [];
468
+ fieldValue.forEach((item, nextIndex) => {
469
+ // validate response data
470
+ if (item == null) {
471
+ nextIDs.push(item);
472
+ return;
473
+ }
474
+ this._path.push(String(nextIndex));
475
+ invariant(
476
+ typeof item === 'object',
477
+ 'RelayResponseNormalizer: Expected elements for field `%s` to be ' +
478
+ 'objects.',
479
+ storageKey,
480
+ );
481
+ const nextID =
482
+ this._getDataId(
483
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
484
+ * suppresses an error found when Flow v0.98 was deployed. To see the
485
+ * error delete this comment and run Flow. */
486
+ item,
487
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
488
+ * suppresses an error found when Flow v0.98 was deployed. To see the
489
+ * error delete this comment and run Flow. */
490
+ field.concreteType ?? this._getRecordType(item),
491
+ ) ||
492
+ (prevIDs && prevIDs[nextIndex]) || // Reuse previously generated client IDs:
493
+ generateClientID(
494
+ RelayModernRecord.getDataID(record),
495
+ storageKey,
496
+ nextIndex,
497
+ );
498
+ invariant(
499
+ typeof nextID === 'string',
500
+ 'RelayResponseNormalizer: Expected id of elements of field `%s` to ' +
501
+ 'be strings.',
502
+ storageKey,
503
+ );
504
+
505
+ nextIDs.push(nextID);
506
+ let nextRecord = this._recordSource.get(nextID);
507
+ if (!nextRecord) {
508
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
509
+ * suppresses an error found when Flow v0.98 was deployed. To see the
510
+ * error delete this comment and run Flow. */
511
+ const typeName = field.concreteType || this._getRecordType(item);
512
+ nextRecord = RelayModernRecord.create(nextID, typeName);
513
+ this._recordSource.set(nextID, nextRecord);
514
+ } else if (__DEV__) {
515
+ this._validateRecordType(nextRecord, field, item);
516
+ }
517
+ /* $FlowFixMe(>=0.98.0 site=www,mobile,react_native_fb,oss) This comment
518
+ * suppresses an error found when Flow v0.98 was deployed. To see the
519
+ * error delete this comment and run Flow. */
520
+ this._traverseSelections(field, nextRecord, item);
521
+ this._path.pop();
522
+ });
523
+ RelayModernRecord.setLinkedRecordIDs(record, storageKey, nextIDs);
524
+ }
525
+
526
+ /**
527
+ * Warns if the type of the record does not match the type of the field/payload.
528
+ */
529
+ _validateRecordType(
530
+ record: Record,
531
+ field: NormalizationLinkedField,
532
+ payload: Object,
533
+ ): void {
534
+ const typeName = field.concreteType ?? this._getRecordType(payload);
535
+ const dataID = RelayModernRecord.getDataID(record);
536
+ warning(
537
+ (isClientID(dataID) && dataID !== ROOT_ID) ||
538
+ RelayModernRecord.getType(record) === typeName,
539
+ 'RelayResponseNormalizer: Invalid record `%s`. Expected %s to be ' +
540
+ 'consistent, but the record was assigned conflicting types `%s` ' +
541
+ 'and `%s`. The GraphQL server likely violated the globally unique ' +
542
+ 'id requirement by returning the same id for different objects.',
543
+ dataID,
544
+ TYPENAME_KEY,
545
+ RelayModernRecord.getType(record),
546
+ typeName,
547
+ );
548
+ }
549
+ }
550
+
551
+ const instrumentedNormalize: typeof normalize = RelayProfiler.instrument(
552
+ 'RelayResponseNormalizer.normalize',
553
+ normalize,
554
+ );
555
+
556
+ module.exports = {normalize: instrumentedNormalize};