relay-runtime 9.0.0 → 10.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 (142) hide show
  1. package/handlers/RelayDefaultHandlerProvider.js.flow +47 -0
  2. package/handlers/connection/ConnectionHandler.js.flow +549 -0
  3. package/handlers/connection/ConnectionInterface.js.flow +92 -0
  4. package/handlers/connection/MutationHandlers.js.flow +199 -0
  5. package/index.js +1 -1
  6. package/index.js.flow +335 -0
  7. package/lib/handlers/RelayDefaultHandlerProvider.js +20 -0
  8. package/lib/handlers/connection/ConnectionHandler.js +1 -3
  9. package/lib/handlers/connection/MutationHandlers.js +212 -0
  10. package/lib/index.js +14 -2
  11. package/lib/mutations/RelayDeclarativeMutationConfig.js +22 -45
  12. package/lib/mutations/RelayRecordProxy.js +1 -3
  13. package/lib/mutations/RelayRecordSourceMutator.js +1 -3
  14. package/lib/mutations/RelayRecordSourceProxy.js +1 -3
  15. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -3
  16. package/lib/mutations/commitMutation.js +2 -3
  17. package/lib/mutations/validateMutation.js +40 -9
  18. package/lib/network/RelayObservable.js +9 -9
  19. package/lib/network/RelayQueryResponseCache.js +8 -6
  20. package/lib/query/GraphQLTag.js +2 -1
  21. package/lib/query/PreloadableQueryRegistry.js +70 -0
  22. package/lib/query/fetchQuery.js +2 -3
  23. package/lib/query/fetchQueryInternal.js +5 -14
  24. package/lib/store/DataChecker.js +200 -71
  25. package/lib/store/RelayConcreteVariables.js +6 -2
  26. package/lib/store/RelayModernEnvironment.js +124 -65
  27. package/lib/store/RelayModernFragmentSpecResolver.js +19 -14
  28. package/lib/store/RelayModernOperationDescriptor.js +6 -5
  29. package/lib/store/RelayModernQueryExecutor.js +122 -73
  30. package/lib/store/RelayModernRecord.js +14 -9
  31. package/lib/store/RelayModernSelector.js +6 -2
  32. package/lib/store/RelayModernStore.js +281 -131
  33. package/lib/store/RelayOperationTracker.js +35 -78
  34. package/lib/store/RelayOptimisticRecordSource.js +7 -5
  35. package/lib/store/RelayPublishQueue.js +2 -4
  36. package/lib/store/RelayReader.js +304 -52
  37. package/lib/store/RelayRecordSource.js +1 -3
  38. package/lib/store/RelayRecordSourceMapImpl.js +13 -18
  39. package/lib/store/RelayReferenceMarker.js +125 -14
  40. package/lib/store/RelayResponseNormalizer.js +261 -66
  41. package/lib/store/RelayStoreReactFlightUtils.js +47 -0
  42. package/lib/store/RelayStoreUtils.js +1 -0
  43. package/lib/store/StoreInspector.js +8 -8
  44. package/lib/store/TypeID.js +28 -0
  45. package/lib/store/cloneRelayScalarHandleSourceField.js +44 -0
  46. package/lib/store/defaultRequiredFieldLogger.js +18 -0
  47. package/lib/store/normalizeRelayPayload.js +6 -2
  48. package/lib/store/readInlineData.js +1 -1
  49. package/lib/subscription/requestSubscription.js +4 -3
  50. package/lib/util/NormalizationNode.js +1 -5
  51. package/lib/util/RelayConcreteNode.js +11 -6
  52. package/lib/util/RelayError.js +39 -9
  53. package/lib/util/RelayFeatureFlags.js +6 -3
  54. package/lib/util/RelayReplaySubject.js +3 -3
  55. package/lib/util/createPayloadFor3DField.js +7 -2
  56. package/lib/util/getFragmentIdentifier.js +12 -3
  57. package/lib/util/getOperation.js +33 -0
  58. package/lib/util/getRequestIdentifier.js +2 -2
  59. package/lib/util/isEmptyObject.js +25 -0
  60. package/lib/util/recycleNodesInto.js +6 -7
  61. package/lib/util/reportMissingRequiredFields.js +48 -0
  62. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  63. package/mutations/RelayRecordProxy.js.flow +165 -0
  64. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  65. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  66. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  67. package/mutations/applyOptimisticMutation.js.flow +76 -0
  68. package/mutations/commitLocalUpdate.js.flow +24 -0
  69. package/mutations/commitMutation.js.flow +181 -0
  70. package/mutations/validateMutation.js.flow +242 -0
  71. package/network/ConvertToExecuteFunction.js.flow +49 -0
  72. package/network/RelayNetwork.js.flow +84 -0
  73. package/network/RelayNetworkTypes.js.flow +145 -0
  74. package/network/RelayObservable.js.flow +634 -0
  75. package/network/RelayQueryResponseCache.js.flow +111 -0
  76. package/package.json +2 -2
  77. package/query/GraphQLTag.js.flow +168 -0
  78. package/query/PreloadableQueryRegistry.js.flow +65 -0
  79. package/query/fetchQuery.js.flow +47 -0
  80. package/query/fetchQueryInternal.js.flow +343 -0
  81. package/relay-runtime.js +2 -2
  82. package/relay-runtime.min.js +2 -2
  83. package/store/ClientID.js.flow +43 -0
  84. package/store/DataChecker.js.flow +568 -0
  85. package/store/RelayConcreteVariables.js.flow +96 -0
  86. package/store/RelayModernEnvironment.js.flow +571 -0
  87. package/store/RelayModernFragmentSpecResolver.js.flow +438 -0
  88. package/store/RelayModernOperationDescriptor.js.flow +92 -0
  89. package/store/RelayModernQueryExecutor.js.flow +1345 -0
  90. package/store/RelayModernRecord.js.flow +403 -0
  91. package/store/RelayModernSelector.js.flow +455 -0
  92. package/store/RelayModernStore.js.flow +858 -0
  93. package/store/RelayOperationTracker.js.flow +164 -0
  94. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  95. package/store/RelayPublishQueue.js.flow +401 -0
  96. package/store/RelayReader.js.flow +638 -0
  97. package/store/RelayRecordSource.js.flow +29 -0
  98. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  99. package/store/RelayRecordState.js.flow +37 -0
  100. package/store/RelayReferenceMarker.js.flow +324 -0
  101. package/store/RelayResponseNormalizer.js.flow +791 -0
  102. package/store/RelayStoreReactFlightUtils.js.flow +64 -0
  103. package/store/RelayStoreTypes.js.flow +958 -0
  104. package/store/RelayStoreUtils.js.flow +219 -0
  105. package/store/StoreInspector.js.flow +171 -0
  106. package/store/TypeID.js.flow +28 -0
  107. package/store/ViewerPattern.js.flow +26 -0
  108. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  109. package/store/cloneRelayScalarHandleSourceField.js.flow +62 -0
  110. package/store/createFragmentSpecResolver.js.flow +55 -0
  111. package/store/createRelayContext.js.flow +44 -0
  112. package/store/defaultGetDataID.js.flow +27 -0
  113. package/store/defaultRequiredFieldLogger.js.flow +23 -0
  114. package/store/hasOverlappingIDs.js.flow +34 -0
  115. package/store/isRelayModernEnvironment.js.flow +27 -0
  116. package/store/normalizeRelayPayload.js.flow +51 -0
  117. package/store/readInlineData.js.flow +75 -0
  118. package/subscription/requestSubscription.js.flow +103 -0
  119. package/util/JSResourceTypes.flow.js.flow +20 -0
  120. package/util/NormalizationNode.js.flow +213 -0
  121. package/util/ReaderNode.js.flow +227 -0
  122. package/util/RelayConcreteNode.js.flow +99 -0
  123. package/util/RelayDefaultHandleKey.js.flow +17 -0
  124. package/util/RelayError.js.flow +62 -0
  125. package/util/RelayFeatureFlags.js.flow +37 -0
  126. package/util/RelayProfiler.js.flow +284 -0
  127. package/util/RelayReplaySubject.js.flow +135 -0
  128. package/util/RelayRuntimeTypes.js.flow +72 -0
  129. package/util/createPayloadFor3DField.js.flow +43 -0
  130. package/util/deepFreeze.js.flow +36 -0
  131. package/util/generateID.js.flow +21 -0
  132. package/util/getFragmentIdentifier.js.flow +76 -0
  133. package/util/getOperation.js.flow +40 -0
  134. package/util/getRelayHandleKey.js.flow +41 -0
  135. package/util/getRequestIdentifier.js.flow +42 -0
  136. package/util/isEmptyObject.js.flow +25 -0
  137. package/util/isPromise.js.flow +21 -0
  138. package/util/isScalarAndEqual.js.flow +26 -0
  139. package/util/recycleNodesInto.js.flow +87 -0
  140. package/util/reportMissingRequiredFields.js.flow +51 -0
  141. package/util/resolveImmediate.js.flow +30 -0
  142. package/util/stableCopy.js.flow +35 -0
@@ -0,0 +1,343 @@
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 Observable = require('../network/RelayObservable');
16
+ const RelayReplaySubject = require('../util/RelayReplaySubject');
17
+
18
+ const invariant = require('invariant');
19
+
20
+ import type {GraphQLResponse} from '../network/RelayNetworkTypes';
21
+ import type {Subscription} from '../network/RelayObservable';
22
+ import type {
23
+ IEnvironment,
24
+ OperationDescriptor,
25
+ RequestDescriptor,
26
+ } from '../store/RelayStoreTypes';
27
+ import type {RequestIdentifier} from '../util/getRequestIdentifier';
28
+
29
+ type RequestCacheEntry = {|
30
+ +identifier: RequestIdentifier,
31
+ +subject: RelayReplaySubject<GraphQLResponse>,
32
+ +subjectForInFlightStatus: RelayReplaySubject<GraphQLResponse>,
33
+ +subscription: Subscription,
34
+ |};
35
+
36
+ const WEAKMAP_SUPPORTED = typeof WeakMap === 'function';
37
+
38
+ const requestCachesByEnvironment = WEAKMAP_SUPPORTED
39
+ ? new WeakMap()
40
+ : new Map();
41
+
42
+ /**
43
+ * Fetches the given query and variables on the provided environment,
44
+ * and de-dupes identical in-flight requests.
45
+ *
46
+ * Observing a request:
47
+ * ====================
48
+ * fetchQuery returns an Observable which you can call .subscribe()
49
+ * on. subscribe() takes an Observer, which you can provide to
50
+ * observe network events:
51
+ *
52
+ * ```
53
+ * fetchQuery(environment, query, variables).subscribe({
54
+ * // Called when network requests starts
55
+ * start: (subscription) => {},
56
+ *
57
+ * // Called after a payload is received and written to the local store
58
+ * next: (payload) => {},
59
+ *
60
+ * // Called when network requests errors
61
+ * error: (error) => {},
62
+ *
63
+ * // Called when network requests fully completes
64
+ * complete: () => {},
65
+ *
66
+ * // Called when network request is unsubscribed
67
+ * unsubscribe: (subscription) => {},
68
+ * });
69
+ * ```
70
+ *
71
+ * In-flight request de-duping:
72
+ * ============================
73
+ * By default, calling fetchQuery multiple times with the same
74
+ * environment, query and variables will not initiate a new request if a request
75
+ * for those same parameters is already in flight.
76
+ *
77
+ * A request is marked in-flight from the moment it starts until the moment it
78
+ * fully completes, regardless of error or successful completion.
79
+ *
80
+ * NOTE: If the request completes _synchronously_, calling fetchQuery
81
+ * a second time with the same arguments in the same tick will _NOT_ de-dupe
82
+ * the request given that it will no longer be in-flight.
83
+ *
84
+ *
85
+ * Data Retention:
86
+ * ===============
87
+ * This function will not retain any query data outside the scope of the
88
+ * request, which means it is not guaranteed that it won't be garbage
89
+ * collected after the request completes.
90
+ * If you need to retain data, you can do so manually with environment.retain().
91
+ *
92
+ * Cancelling requests:
93
+ * ====================
94
+ * If the subscription returned by subscribe is called while the
95
+ * request is in-flight, apart from releasing retained data, the request will
96
+ * also be cancelled.
97
+ *
98
+ * ```
99
+ * const subscription = fetchQuery(...).subscribe(...);
100
+ *
101
+ * // This will cancel the request if it is in-flight.
102
+ * subscription.unsubscribe();
103
+ * ```
104
+ */
105
+ function fetchQuery(
106
+ environment: IEnvironment,
107
+ operation: OperationDescriptor,
108
+ ): Observable<GraphQLResponse> {
109
+ return fetchQueryDeduped(environment, operation.request.identifier, () =>
110
+ environment.execute({
111
+ operation,
112
+ }),
113
+ );
114
+ }
115
+
116
+ /**
117
+ * Low-level implementation details of `fetchQuery`.
118
+ *
119
+ * `fetchQueryDeduped` can also be used to share a single cache for
120
+ * requests that aren't using `fetchQuery` directly (e.g. because they don't
121
+ * have an `OperationDescriptor` when they are called).
122
+ */
123
+ function fetchQueryDeduped(
124
+ environment: IEnvironment,
125
+ identifier: RequestIdentifier,
126
+ fetchFn: () => Observable<GraphQLResponse>,
127
+ ): Observable<GraphQLResponse> {
128
+ return Observable.create(sink => {
129
+ const requestCache = getRequestCache(environment);
130
+ let cachedRequest = requestCache.get(identifier);
131
+
132
+ if (!cachedRequest) {
133
+ fetchFn()
134
+ .finally(() => requestCache.delete(identifier))
135
+ .subscribe({
136
+ start: subscription => {
137
+ cachedRequest = {
138
+ identifier,
139
+ subject: new RelayReplaySubject(),
140
+ subjectForInFlightStatus: new RelayReplaySubject(),
141
+ subscription: subscription,
142
+ };
143
+ requestCache.set(identifier, cachedRequest);
144
+ },
145
+ next: response => {
146
+ const cachedReq = getCachedRequest(requestCache, identifier);
147
+ cachedReq.subject.next(response);
148
+ cachedReq.subjectForInFlightStatus.next(response);
149
+ },
150
+ error: error => {
151
+ const cachedReq = getCachedRequest(requestCache, identifier);
152
+ cachedReq.subject.error(error);
153
+ cachedReq.subjectForInFlightStatus.error(error);
154
+ },
155
+ complete: () => {
156
+ const cachedReq = getCachedRequest(requestCache, identifier);
157
+ cachedReq.subject.complete();
158
+ cachedReq.subjectForInFlightStatus.complete();
159
+ },
160
+ unsubscribe: subscription => {
161
+ const cachedReq = getCachedRequest(requestCache, identifier);
162
+ cachedReq.subject.unsubscribe();
163
+ cachedReq.subjectForInFlightStatus.unsubscribe();
164
+ },
165
+ });
166
+ }
167
+
168
+ invariant(
169
+ cachedRequest != null,
170
+ '[fetchQueryInternal] fetchQueryDeduped: Expected `start` to be ' +
171
+ 'called synchronously',
172
+ );
173
+ return getObservableForCachedRequest(requestCache, cachedRequest).subscribe(
174
+ sink,
175
+ );
176
+ });
177
+ }
178
+
179
+ /**
180
+ * @private
181
+ */
182
+ function getObservableForCachedRequest(
183
+ requestCache: Map<RequestIdentifier, RequestCacheEntry>,
184
+ cachedRequest: RequestCacheEntry,
185
+ ): Observable<GraphQLResponse> {
186
+ return Observable.create(sink => {
187
+ const subscription = cachedRequest.subject.subscribe(sink);
188
+
189
+ return () => {
190
+ subscription.unsubscribe();
191
+ const cachedRequestInstance = requestCache.get(cachedRequest.identifier);
192
+ if (cachedRequestInstance) {
193
+ const requestSubscription = cachedRequestInstance.subscription;
194
+ if (
195
+ requestSubscription != null &&
196
+ cachedRequestInstance.subject.getObserverCount() === 0
197
+ ) {
198
+ requestSubscription.unsubscribe();
199
+ requestCache.delete(cachedRequest.identifier);
200
+ }
201
+ }
202
+ };
203
+ });
204
+ }
205
+
206
+ /**
207
+ * @private
208
+ */
209
+ function getActiveStatusObservableForCachedRequest(
210
+ environment: IEnvironment,
211
+ requestCache: Map<RequestIdentifier, RequestCacheEntry>,
212
+ cachedRequest: RequestCacheEntry,
213
+ ): Observable<void> {
214
+ return Observable.create(sink => {
215
+ const subscription = cachedRequest.subjectForInFlightStatus.subscribe({
216
+ error: sink.error,
217
+ next: response => {
218
+ if (!environment.isRequestActive(cachedRequest.identifier)) {
219
+ sink.complete();
220
+ return;
221
+ }
222
+ sink.next();
223
+ },
224
+ complete: sink.complete,
225
+ unsubscribe: sink.complete,
226
+ });
227
+
228
+ return () => {
229
+ subscription.unsubscribe();
230
+ };
231
+ });
232
+ }
233
+
234
+ /**
235
+ * If a request is active for the given query, variables and environment,
236
+ * this function will return a Promise that will resolve when that request has
237
+ * stops being active (receives a final payload), and the data has been saved
238
+ * to the store.
239
+ * If no request is active, null will be returned
240
+ */
241
+ function getPromiseForActiveRequest(
242
+ environment: IEnvironment,
243
+ request: RequestDescriptor,
244
+ ): Promise<void> | null {
245
+ const requestCache = getRequestCache(environment);
246
+ const cachedRequest = requestCache.get(request.identifier);
247
+ if (!cachedRequest) {
248
+ return null;
249
+ }
250
+ if (!environment.isRequestActive(cachedRequest.identifier)) {
251
+ return null;
252
+ }
253
+
254
+ return new Promise((resolve, reject) => {
255
+ let resolveOnNext = false;
256
+ getActiveStatusObservableForCachedRequest(
257
+ environment,
258
+ requestCache,
259
+ cachedRequest,
260
+ ).subscribe({
261
+ complete: resolve,
262
+ error: reject,
263
+ next: response => {
264
+ /*
265
+ * The underlying `RelayReplaySubject` will synchronously replay events
266
+ * as soon as we subscribe, but since we want the *next* asynchronous
267
+ * one, we'll ignore them until the replay finishes.
268
+ */
269
+ if (resolveOnNext) {
270
+ resolve(response);
271
+ }
272
+ },
273
+ });
274
+ resolveOnNext = true;
275
+ });
276
+ }
277
+
278
+ /**
279
+ * If there is a pending request for the given query, returns an Observable of
280
+ * *all* its responses. Existing responses are published synchronously and
281
+ * subsequent responses are published asynchronously. Returns null if there is
282
+ * no pending request. This is similar to fetchQuery() except that it will not
283
+ * issue a fetch if there isn't already one pending.
284
+ */
285
+ function getObservableForActiveRequest(
286
+ environment: IEnvironment,
287
+ request: RequestDescriptor,
288
+ ): Observable<void> | null {
289
+ const requestCache = getRequestCache(environment);
290
+ const cachedRequest = requestCache.get(request.identifier);
291
+ if (!cachedRequest) {
292
+ return null;
293
+ }
294
+ if (!environment.isRequestActive(cachedRequest.identifier)) {
295
+ return null;
296
+ }
297
+
298
+ return getActiveStatusObservableForCachedRequest(
299
+ environment,
300
+ requestCache,
301
+ cachedRequest,
302
+ );
303
+ }
304
+
305
+ /**
306
+ * @private
307
+ */
308
+ function getRequestCache(
309
+ environment: IEnvironment,
310
+ ): Map<RequestIdentifier, RequestCacheEntry> {
311
+ const cached: ?Map<
312
+ RequestIdentifier,
313
+ RequestCacheEntry,
314
+ > = requestCachesByEnvironment.get(environment);
315
+ if (cached != null) {
316
+ return cached;
317
+ }
318
+ const requestCache: Map<RequestIdentifier, RequestCacheEntry> = new Map();
319
+ requestCachesByEnvironment.set(environment, requestCache);
320
+ return requestCache;
321
+ }
322
+
323
+ /**
324
+ * @private
325
+ */
326
+ function getCachedRequest(
327
+ requestCache: Map<RequestIdentifier, RequestCacheEntry>,
328
+ identifier: RequestIdentifier,
329
+ ) {
330
+ const cached = requestCache.get(identifier);
331
+ invariant(
332
+ cached != null,
333
+ '[fetchQueryInternal] getCachedRequest: Expected request to be cached',
334
+ );
335
+ return cached;
336
+ }
337
+
338
+ module.exports = {
339
+ fetchQuery,
340
+ fetchQueryDeduped,
341
+ getPromiseForActiveRequest,
342
+ getObservableForActiveRequest,
343
+ };