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.
- package/handlers/RelayDefaultHandlerProvider.js.flow +47 -0
- package/handlers/connection/ConnectionHandler.js.flow +549 -0
- package/handlers/connection/ConnectionInterface.js.flow +92 -0
- package/handlers/connection/MutationHandlers.js.flow +199 -0
- package/index.js +1 -1
- package/index.js.flow +335 -0
- package/lib/handlers/RelayDefaultHandlerProvider.js +20 -0
- package/lib/handlers/connection/ConnectionHandler.js +1 -3
- package/lib/handlers/connection/MutationHandlers.js +212 -0
- package/lib/index.js +14 -2
- package/lib/mutations/RelayDeclarativeMutationConfig.js +22 -45
- package/lib/mutations/RelayRecordProxy.js +1 -3
- package/lib/mutations/RelayRecordSourceMutator.js +1 -3
- package/lib/mutations/RelayRecordSourceProxy.js +1 -3
- package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -3
- package/lib/mutations/commitMutation.js +2 -3
- package/lib/mutations/validateMutation.js +40 -9
- package/lib/network/RelayObservable.js +9 -9
- package/lib/network/RelayQueryResponseCache.js +8 -6
- package/lib/query/GraphQLTag.js +2 -1
- package/lib/query/PreloadableQueryRegistry.js +70 -0
- package/lib/query/fetchQuery.js +2 -3
- package/lib/query/fetchQueryInternal.js +5 -14
- package/lib/store/DataChecker.js +200 -71
- package/lib/store/RelayConcreteVariables.js +6 -2
- package/lib/store/RelayModernEnvironment.js +124 -65
- package/lib/store/RelayModernFragmentSpecResolver.js +19 -14
- package/lib/store/RelayModernOperationDescriptor.js +6 -5
- package/lib/store/RelayModernQueryExecutor.js +122 -73
- package/lib/store/RelayModernRecord.js +14 -9
- package/lib/store/RelayModernSelector.js +6 -2
- package/lib/store/RelayModernStore.js +281 -131
- package/lib/store/RelayOperationTracker.js +35 -78
- package/lib/store/RelayOptimisticRecordSource.js +7 -5
- package/lib/store/RelayPublishQueue.js +2 -4
- package/lib/store/RelayReader.js +304 -52
- package/lib/store/RelayRecordSource.js +1 -3
- package/lib/store/RelayRecordSourceMapImpl.js +13 -18
- package/lib/store/RelayReferenceMarker.js +125 -14
- package/lib/store/RelayResponseNormalizer.js +261 -66
- package/lib/store/RelayStoreReactFlightUtils.js +47 -0
- 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/defaultRequiredFieldLogger.js +18 -0
- package/lib/store/normalizeRelayPayload.js +6 -2
- package/lib/store/readInlineData.js +1 -1
- package/lib/subscription/requestSubscription.js +4 -3
- package/lib/util/NormalizationNode.js +1 -5
- package/lib/util/RelayConcreteNode.js +11 -6
- package/lib/util/RelayError.js +39 -9
- package/lib/util/RelayFeatureFlags.js +6 -3
- package/lib/util/RelayReplaySubject.js +3 -3
- package/lib/util/createPayloadFor3DField.js +7 -2
- package/lib/util/getFragmentIdentifier.js +12 -3
- package/lib/util/getOperation.js +33 -0
- package/lib/util/getRequestIdentifier.js +2 -2
- package/lib/util/isEmptyObject.js +25 -0
- package/lib/util/recycleNodesInto.js +6 -7
- package/lib/util/reportMissingRequiredFields.js +48 -0
- package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
- package/mutations/RelayRecordProxy.js.flow +165 -0
- package/mutations/RelayRecordSourceMutator.js.flow +238 -0
- package/mutations/RelayRecordSourceProxy.js.flow +164 -0
- package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
- package/mutations/applyOptimisticMutation.js.flow +76 -0
- package/mutations/commitLocalUpdate.js.flow +24 -0
- package/mutations/commitMutation.js.flow +181 -0
- package/mutations/validateMutation.js.flow +242 -0
- package/network/ConvertToExecuteFunction.js.flow +49 -0
- package/network/RelayNetwork.js.flow +84 -0
- package/network/RelayNetworkTypes.js.flow +145 -0
- package/network/RelayObservable.js.flow +634 -0
- package/network/RelayQueryResponseCache.js.flow +111 -0
- package/package.json +2 -2
- package/query/GraphQLTag.js.flow +168 -0
- package/query/PreloadableQueryRegistry.js.flow +65 -0
- package/query/fetchQuery.js.flow +47 -0
- package/query/fetchQueryInternal.js.flow +343 -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 +568 -0
- package/store/RelayConcreteVariables.js.flow +96 -0
- package/store/RelayModernEnvironment.js.flow +571 -0
- package/store/RelayModernFragmentSpecResolver.js.flow +438 -0
- package/store/RelayModernOperationDescriptor.js.flow +92 -0
- package/store/RelayModernQueryExecutor.js.flow +1345 -0
- package/store/RelayModernRecord.js.flow +403 -0
- package/store/RelayModernSelector.js.flow +455 -0
- package/store/RelayModernStore.js.flow +858 -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 +638 -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 +324 -0
- package/store/RelayResponseNormalizer.js.flow +791 -0
- package/store/RelayStoreReactFlightUtils.js.flow +64 -0
- package/store/RelayStoreTypes.js.flow +958 -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/defaultRequiredFieldLogger.js.flow +23 -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 +103 -0
- package/util/JSResourceTypes.flow.js.flow +20 -0
- package/util/NormalizationNode.js.flow +213 -0
- package/util/ReaderNode.js.flow +227 -0
- package/util/RelayConcreteNode.js.flow +99 -0
- package/util/RelayDefaultHandleKey.js.flow +17 -0
- package/util/RelayError.js.flow +62 -0
- package/util/RelayFeatureFlags.js.flow +37 -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 +76 -0
- package/util/getOperation.js.flow +40 -0
- package/util/getRelayHandleKey.js.flow +41 -0
- package/util/getRequestIdentifier.js.flow +42 -0
- package/util/isEmptyObject.js.flow +25 -0
- package/util/isPromise.js.flow +21 -0
- package/util/isScalarAndEqual.js.flow +26 -0
- package/util/recycleNodesInto.js.flow +87 -0
- package/util/reportMissingRequiredFields.js.flow +51 -0
- package/util/resolveImmediate.js.flow +30 -0
- package/util/stableCopy.js.flow +35 -0
|
@@ -0,0 +1,634 @@
|
|
|
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 isPromise = require('../util/isPromise');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A Subscription object is returned from .subscribe(), which can be
|
|
19
|
+
* unsubscribed or checked to see if the resulting subscription has closed.
|
|
20
|
+
*/
|
|
21
|
+
export type Subscription = {|
|
|
22
|
+
+unsubscribe: () => void,
|
|
23
|
+
+closed: boolean,
|
|
24
|
+
|};
|
|
25
|
+
|
|
26
|
+
type SubscriptionFn = {
|
|
27
|
+
(): mixed,
|
|
28
|
+
+unsubscribe?: void,
|
|
29
|
+
+closed?: void,
|
|
30
|
+
...
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An Observer is an object of optional callback functions provided to
|
|
35
|
+
* .subscribe(). Each callback function is invoked when that event occurs.
|
|
36
|
+
*/
|
|
37
|
+
export type Observer<-T> = {|
|
|
38
|
+
+start?: ?(Subscription) => mixed,
|
|
39
|
+
+next?: ?(T) => mixed,
|
|
40
|
+
+error?: ?(Error) => mixed,
|
|
41
|
+
+complete?: ?() => mixed,
|
|
42
|
+
+unsubscribe?: ?(Subscription) => mixed,
|
|
43
|
+
|};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A Sink is an object of methods provided by Observable during construction.
|
|
47
|
+
* The methods are to be called to trigger each event. It also contains a closed
|
|
48
|
+
* field to see if the resulting subscription has closed.
|
|
49
|
+
*/
|
|
50
|
+
export type Sink<-T> = {|
|
|
51
|
+
+next: T => void,
|
|
52
|
+
+error: (Error, isUncaughtThrownError?: boolean) => void,
|
|
53
|
+
+complete: () => void,
|
|
54
|
+
+closed: boolean,
|
|
55
|
+
|};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A Source is the required argument when constructing a new Observable. Similar
|
|
59
|
+
* to a Promise constructor, this is a function which is invoked with a Sink,
|
|
60
|
+
* and may return either a cleanup function or a Subscription instance (for use
|
|
61
|
+
* when composing Observables).
|
|
62
|
+
*/
|
|
63
|
+
export type Source<+T> = (Sink<T>) => void | Subscription | SubscriptionFn;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A Subscribable is an interface describing any object which can be subscribed.
|
|
67
|
+
*
|
|
68
|
+
* Note: A sink may be passed directly to .subscribe() as its observer,
|
|
69
|
+
* allowing for easily composing Subscribables.
|
|
70
|
+
*/
|
|
71
|
+
export interface Subscribable<+T> {
|
|
72
|
+
subscribe(observer: Observer<T> | Sink<T>): Subscription;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Note: This should accept Subscribable<T> instead of RelayObservable<T>,
|
|
76
|
+
// however Flow cannot yet distinguish it from T.
|
|
77
|
+
export type ObservableFromValue<+T> = RelayObservable<T> | Promise<T> | T;
|
|
78
|
+
|
|
79
|
+
let hostReportError = swallowError;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Limited implementation of ESObservable, providing the limited set of behavior
|
|
83
|
+
* Relay networking requires.
|
|
84
|
+
*
|
|
85
|
+
* Observables retain the benefit of callbacks which can be called
|
|
86
|
+
* synchronously, avoiding any UI jitter, while providing a compositional API,
|
|
87
|
+
* which simplifies logic and prevents mishandling of errors compared to
|
|
88
|
+
* the direct use of callback functions.
|
|
89
|
+
*
|
|
90
|
+
* ESObservable: https://github.com/tc39/proposal-observable
|
|
91
|
+
*/
|
|
92
|
+
class RelayObservable<+T> implements Subscribable<T> {
|
|
93
|
+
+_source: Source<T>;
|
|
94
|
+
|
|
95
|
+
static create<V>(source: Source<V>): RelayObservable<V> {
|
|
96
|
+
return new RelayObservable((source: any));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Use RelayObservable.create()
|
|
100
|
+
constructor(source: empty): void {
|
|
101
|
+
if (__DEV__) {
|
|
102
|
+
// Early runtime errors for ill-formed sources.
|
|
103
|
+
if (!source || typeof source !== 'function') {
|
|
104
|
+
throw new Error('Source must be a Function: ' + String(source));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
(this: any)._source = source;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* When an emitted error event is not handled by an Observer, it is reported
|
|
112
|
+
* to the host environment (what the ESObservable spec refers to as
|
|
113
|
+
* "HostReportErrors()").
|
|
114
|
+
*
|
|
115
|
+
* The default implementation in development rethrows thrown errors, and
|
|
116
|
+
* logs emitted error events to the console, while in production does nothing
|
|
117
|
+
* (swallowing unhandled errors).
|
|
118
|
+
*
|
|
119
|
+
* Called during application initialization, this method allows
|
|
120
|
+
* application-specific handling of unhandled errors. Allowing, for example,
|
|
121
|
+
* integration with error logging or developer tools.
|
|
122
|
+
*
|
|
123
|
+
* A second parameter `isUncaughtThrownError` is true when the unhandled error
|
|
124
|
+
* was thrown within an Observer handler, and false when the unhandled error
|
|
125
|
+
* was an unhandled emitted event.
|
|
126
|
+
*
|
|
127
|
+
* - Uncaught thrown errors typically represent avoidable errors thrown from
|
|
128
|
+
* application code, which should be handled with a try/catch block, and
|
|
129
|
+
* usually have useful stack traces.
|
|
130
|
+
*
|
|
131
|
+
* - Unhandled emitted event errors typically represent unavoidable events in
|
|
132
|
+
* application flow such as network failure, and may not have useful
|
|
133
|
+
* stack traces.
|
|
134
|
+
*/
|
|
135
|
+
static onUnhandledError(
|
|
136
|
+
callback: (Error, isUncaughtThrownError: boolean) => mixed,
|
|
137
|
+
): void {
|
|
138
|
+
hostReportError = callback;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Accepts various kinds of data sources, and always returns a RelayObservable
|
|
143
|
+
* useful for accepting the result of a user-provided FetchFunction.
|
|
144
|
+
*/
|
|
145
|
+
static from<V>(obj: ObservableFromValue<V>): RelayObservable<V> {
|
|
146
|
+
return isObservable(obj)
|
|
147
|
+
? fromObservable(obj)
|
|
148
|
+
: isPromise(obj)
|
|
149
|
+
? fromPromise(obj)
|
|
150
|
+
: fromValue(obj);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Similar to promise.catch(), observable.catch() handles error events, and
|
|
155
|
+
* provides an alternative observable to use in it's place.
|
|
156
|
+
*
|
|
157
|
+
* If the catch handler throws a new error, it will appear as an error event
|
|
158
|
+
* on the resulting Observable.
|
|
159
|
+
*/
|
|
160
|
+
catch<U>(fn: Error => RelayObservable<U>): RelayObservable<T | U> {
|
|
161
|
+
return RelayObservable.create(sink => {
|
|
162
|
+
let subscription;
|
|
163
|
+
this.subscribe({
|
|
164
|
+
start: sub => {
|
|
165
|
+
subscription = sub;
|
|
166
|
+
},
|
|
167
|
+
next: sink.next,
|
|
168
|
+
complete: sink.complete,
|
|
169
|
+
error(error) {
|
|
170
|
+
try {
|
|
171
|
+
fn(error).subscribe({
|
|
172
|
+
start: sub => {
|
|
173
|
+
subscription = sub;
|
|
174
|
+
},
|
|
175
|
+
next: sink.next,
|
|
176
|
+
complete: sink.complete,
|
|
177
|
+
error: sink.error,
|
|
178
|
+
});
|
|
179
|
+
} catch (error2) {
|
|
180
|
+
sink.error(error2, true /* isUncaughtThrownError */);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
return () => subscription.unsubscribe();
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Returns a new Observable which first yields values from this Observable,
|
|
190
|
+
* then yields values from the next Observable. This is useful for chaining
|
|
191
|
+
* together Observables of finite length.
|
|
192
|
+
*/
|
|
193
|
+
concat<U>(next: RelayObservable<U>): RelayObservable<T | U> {
|
|
194
|
+
return RelayObservable.create(sink => {
|
|
195
|
+
let current;
|
|
196
|
+
this.subscribe({
|
|
197
|
+
start(subscription) {
|
|
198
|
+
current = subscription;
|
|
199
|
+
},
|
|
200
|
+
next: sink.next,
|
|
201
|
+
error: sink.error,
|
|
202
|
+
complete() {
|
|
203
|
+
current = next.subscribe(sink);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
return () => {
|
|
207
|
+
current && current.unsubscribe();
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Returns a new Observable which returns the same values as this one, but
|
|
214
|
+
* modified so that the provided Observer is called to perform a side-effects
|
|
215
|
+
* for all events emitted by the source.
|
|
216
|
+
*
|
|
217
|
+
* Any errors that are thrown in the side-effect Observer are unhandled, and
|
|
218
|
+
* do not affect the source Observable or its Observer.
|
|
219
|
+
*
|
|
220
|
+
* This is useful for when debugging your Observables or performing other
|
|
221
|
+
* side-effects such as logging or performance monitoring.
|
|
222
|
+
*/
|
|
223
|
+
do(observer: Observer<T>): RelayObservable<T> {
|
|
224
|
+
return RelayObservable.create(sink => {
|
|
225
|
+
const both = (action: any) =>
|
|
226
|
+
function() {
|
|
227
|
+
try {
|
|
228
|
+
observer[action] && observer[action].apply(observer, arguments);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
231
|
+
}
|
|
232
|
+
sink[action] && sink[action].apply(sink, arguments);
|
|
233
|
+
};
|
|
234
|
+
return this.subscribe({
|
|
235
|
+
start: both('start'),
|
|
236
|
+
next: both('next'),
|
|
237
|
+
error: both('error'),
|
|
238
|
+
complete: both('complete'),
|
|
239
|
+
unsubscribe: both('unsubscribe'),
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Returns a new Observable which returns the same values as this one, but
|
|
246
|
+
* modified so that the finally callback is performed after completion,
|
|
247
|
+
* whether normal or due to error or unsubscription.
|
|
248
|
+
*
|
|
249
|
+
* This is useful for cleanup such as resource finalization.
|
|
250
|
+
*/
|
|
251
|
+
finally(fn: () => mixed): RelayObservable<T> {
|
|
252
|
+
return RelayObservable.create(sink => {
|
|
253
|
+
const subscription = this.subscribe(sink);
|
|
254
|
+
return () => {
|
|
255
|
+
subscription.unsubscribe();
|
|
256
|
+
fn();
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Returns a new Observable which is identical to this one, unless this
|
|
263
|
+
* Observable completes before yielding any values, in which case the new
|
|
264
|
+
* Observable will yield the values from the alternate Observable.
|
|
265
|
+
*
|
|
266
|
+
* If this Observable does yield values, the alternate is never subscribed to.
|
|
267
|
+
*
|
|
268
|
+
* This is useful for scenarios where values may come from multiple sources
|
|
269
|
+
* which should be tried in order, i.e. from a cache before a network.
|
|
270
|
+
*/
|
|
271
|
+
ifEmpty<U>(alternate: RelayObservable<U>): RelayObservable<T | U> {
|
|
272
|
+
return RelayObservable.create(sink => {
|
|
273
|
+
let hasValue = false;
|
|
274
|
+
let current = this.subscribe({
|
|
275
|
+
next(value) {
|
|
276
|
+
hasValue = true;
|
|
277
|
+
sink.next(value);
|
|
278
|
+
},
|
|
279
|
+
error: sink.error,
|
|
280
|
+
complete() {
|
|
281
|
+
if (hasValue) {
|
|
282
|
+
sink.complete();
|
|
283
|
+
} else {
|
|
284
|
+
current = alternate.subscribe(sink);
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
return () => {
|
|
289
|
+
current.unsubscribe();
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Observable's primary API: returns an unsubscribable Subscription to the
|
|
296
|
+
* source of this Observable.
|
|
297
|
+
*
|
|
298
|
+
* Note: A sink may be passed directly to .subscribe() as its observer,
|
|
299
|
+
* allowing for easily composing Observables.
|
|
300
|
+
*/
|
|
301
|
+
subscribe(observer: Observer<T> | Sink<T>): Subscription {
|
|
302
|
+
if (__DEV__) {
|
|
303
|
+
// Early runtime errors for ill-formed observers.
|
|
304
|
+
if (!observer || typeof observer !== 'object') {
|
|
305
|
+
throw new Error(
|
|
306
|
+
'Observer must be an Object with callbacks: ' + String(observer),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return subscribe(this._source, observer);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Returns a new Observerable where each value has been transformed by
|
|
315
|
+
* the mapping function.
|
|
316
|
+
*/
|
|
317
|
+
map<U>(fn: T => U): RelayObservable<U> {
|
|
318
|
+
return RelayObservable.create(sink => {
|
|
319
|
+
const subscription = this.subscribe({
|
|
320
|
+
complete: sink.complete,
|
|
321
|
+
error: sink.error,
|
|
322
|
+
next: value => {
|
|
323
|
+
try {
|
|
324
|
+
const mapValue = fn(value);
|
|
325
|
+
sink.next(mapValue);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
sink.error(error, true /* isUncaughtThrownError */);
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
return () => {
|
|
332
|
+
subscription.unsubscribe();
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Returns a new Observable where each value is replaced with a new Observable
|
|
339
|
+
* by the mapping function, the results of which returned as a single
|
|
340
|
+
* merged Observable.
|
|
341
|
+
*/
|
|
342
|
+
mergeMap<U>(fn: T => ObservableFromValue<U>): RelayObservable<U> {
|
|
343
|
+
return RelayObservable.create(sink => {
|
|
344
|
+
const subscriptions = [];
|
|
345
|
+
|
|
346
|
+
function start(subscription) {
|
|
347
|
+
this._sub = subscription;
|
|
348
|
+
subscriptions.push(subscription);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function complete() {
|
|
352
|
+
subscriptions.splice(subscriptions.indexOf(this._sub), 1);
|
|
353
|
+
if (subscriptions.length === 0) {
|
|
354
|
+
sink.complete();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this.subscribe({
|
|
359
|
+
start,
|
|
360
|
+
next(value) {
|
|
361
|
+
try {
|
|
362
|
+
if (!sink.closed) {
|
|
363
|
+
RelayObservable.from(fn(value)).subscribe({
|
|
364
|
+
start,
|
|
365
|
+
next: sink.next,
|
|
366
|
+
error: sink.error,
|
|
367
|
+
complete,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
sink.error(error, true /* isUncaughtThrownError */);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
error: sink.error,
|
|
375
|
+
complete,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
return () => {
|
|
379
|
+
subscriptions.forEach(sub => sub.unsubscribe());
|
|
380
|
+
subscriptions.length = 0;
|
|
381
|
+
};
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Returns a new Observable which first mirrors this Observable, then when it
|
|
387
|
+
* completes, waits for `pollInterval` milliseconds before re-subscribing to
|
|
388
|
+
* this Observable again, looping in this manner until unsubscribed.
|
|
389
|
+
*
|
|
390
|
+
* The returned Observable never completes.
|
|
391
|
+
*/
|
|
392
|
+
poll(pollInterval: number): RelayObservable<T> {
|
|
393
|
+
if (__DEV__) {
|
|
394
|
+
if (typeof pollInterval !== 'number' || pollInterval <= 0) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
'RelayObservable: Expected pollInterval to be positive, got: ' +
|
|
397
|
+
pollInterval,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return RelayObservable.create(sink => {
|
|
402
|
+
let subscription;
|
|
403
|
+
let timeout;
|
|
404
|
+
const poll = () => {
|
|
405
|
+
subscription = this.subscribe({
|
|
406
|
+
next: sink.next,
|
|
407
|
+
error: sink.error,
|
|
408
|
+
complete() {
|
|
409
|
+
timeout = setTimeout(poll, pollInterval);
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
poll();
|
|
414
|
+
return () => {
|
|
415
|
+
clearTimeout(timeout);
|
|
416
|
+
subscription.unsubscribe();
|
|
417
|
+
};
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Returns a Promise which resolves when this Observable yields a first value
|
|
423
|
+
* or when it completes with no value.
|
|
424
|
+
*
|
|
425
|
+
* NOTE: The source Observable is *NOT* canceled when the returned Promise
|
|
426
|
+
* resolves. The Observable is always run to completion.
|
|
427
|
+
*/
|
|
428
|
+
toPromise(): Promise<T | void> {
|
|
429
|
+
return new Promise((resolve, reject) => {
|
|
430
|
+
let resolved = false;
|
|
431
|
+
this.subscribe({
|
|
432
|
+
next(val) {
|
|
433
|
+
if (!resolved) {
|
|
434
|
+
resolved = true;
|
|
435
|
+
resolve(val);
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
error: reject,
|
|
439
|
+
complete: resolve,
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Use declarations to teach Flow how to check isObservable.
|
|
446
|
+
declare function isObservable(p: mixed): boolean %checks(p instanceof
|
|
447
|
+
RelayObservable);
|
|
448
|
+
|
|
449
|
+
function isObservable(obj) {
|
|
450
|
+
return (
|
|
451
|
+
typeof obj === 'object' &&
|
|
452
|
+
obj !== null &&
|
|
453
|
+
typeof obj.subscribe === 'function'
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function fromObservable<T>(obj: Subscribable<T>): RelayObservable<T> {
|
|
458
|
+
return obj instanceof RelayObservable
|
|
459
|
+
? obj
|
|
460
|
+
: RelayObservable.create(sink => obj.subscribe(sink));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function fromPromise<T>(promise: Promise<T>): RelayObservable<T> {
|
|
464
|
+
return RelayObservable.create(sink => {
|
|
465
|
+
// Since sink methods do not throw, the resulting Promise can be ignored.
|
|
466
|
+
promise.then(value => {
|
|
467
|
+
sink.next(value);
|
|
468
|
+
sink.complete();
|
|
469
|
+
}, sink.error);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function fromValue<T>(value: T): RelayObservable<T> {
|
|
474
|
+
return RelayObservable.create(sink => {
|
|
475
|
+
sink.next(value);
|
|
476
|
+
sink.complete();
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function subscribe<T>(
|
|
481
|
+
source: Source<T>,
|
|
482
|
+
observer: Observer<T> | Sink<T>,
|
|
483
|
+
): Subscription {
|
|
484
|
+
let closed = false;
|
|
485
|
+
let cleanup;
|
|
486
|
+
|
|
487
|
+
// Ideally we would simply describe a `get closed()` method on the Sink and
|
|
488
|
+
// Subscription objects below, however not all flow environments we expect
|
|
489
|
+
// Relay to be used within will support property getters, and many minifier
|
|
490
|
+
// tools still do not support ES5 syntax. Instead, we can use defineProperty.
|
|
491
|
+
const withClosed: <O>(obj: O) => {|...O, +closed: boolean|} = (obj =>
|
|
492
|
+
Object.defineProperty(obj, 'closed', ({get: () => closed}: any)): any);
|
|
493
|
+
|
|
494
|
+
function doCleanup() {
|
|
495
|
+
if (cleanup) {
|
|
496
|
+
if (cleanup.unsubscribe) {
|
|
497
|
+
cleanup.unsubscribe();
|
|
498
|
+
} else {
|
|
499
|
+
try {
|
|
500
|
+
cleanup();
|
|
501
|
+
} catch (error) {
|
|
502
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
cleanup = undefined;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Create a Subscription.
|
|
510
|
+
const subscription: Subscription = withClosed({
|
|
511
|
+
unsubscribe() {
|
|
512
|
+
if (!closed) {
|
|
513
|
+
closed = true;
|
|
514
|
+
|
|
515
|
+
// Tell Observer that unsubscribe was called.
|
|
516
|
+
try {
|
|
517
|
+
observer.unsubscribe && observer.unsubscribe(subscription);
|
|
518
|
+
} catch (error) {
|
|
519
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
520
|
+
} finally {
|
|
521
|
+
doCleanup();
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// Tell Observer that observation is about to begin.
|
|
528
|
+
try {
|
|
529
|
+
observer.start && observer.start(subscription);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// If closed already, don't bother creating a Sink.
|
|
535
|
+
if (closed) {
|
|
536
|
+
return subscription;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Create a Sink respecting subscription state and cleanup.
|
|
540
|
+
const sink: Sink<T> = withClosed({
|
|
541
|
+
next(value) {
|
|
542
|
+
if (!closed && observer.next) {
|
|
543
|
+
try {
|
|
544
|
+
observer.next(value);
|
|
545
|
+
} catch (error) {
|
|
546
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
error(error, isUncaughtThrownError) {
|
|
551
|
+
if (closed || !observer.error) {
|
|
552
|
+
closed = true;
|
|
553
|
+
hostReportError(error, isUncaughtThrownError || false);
|
|
554
|
+
doCleanup();
|
|
555
|
+
} else {
|
|
556
|
+
closed = true;
|
|
557
|
+
try {
|
|
558
|
+
observer.error(error);
|
|
559
|
+
} catch (error2) {
|
|
560
|
+
hostReportError(error2, true /* isUncaughtThrownError */);
|
|
561
|
+
} finally {
|
|
562
|
+
doCleanup();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
complete() {
|
|
567
|
+
if (!closed) {
|
|
568
|
+
closed = true;
|
|
569
|
+
try {
|
|
570
|
+
observer.complete && observer.complete();
|
|
571
|
+
} catch (error) {
|
|
572
|
+
hostReportError(error, true /* isUncaughtThrownError */);
|
|
573
|
+
} finally {
|
|
574
|
+
doCleanup();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// If anything goes wrong during observing the source, handle the error.
|
|
581
|
+
try {
|
|
582
|
+
cleanup = source(sink);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
sink.error(error, true /* isUncaughtThrownError */);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (__DEV__) {
|
|
588
|
+
// Early runtime errors for ill-formed returned cleanup.
|
|
589
|
+
if (
|
|
590
|
+
cleanup !== undefined &&
|
|
591
|
+
typeof cleanup !== 'function' &&
|
|
592
|
+
(!cleanup || typeof cleanup.unsubscribe !== 'function')
|
|
593
|
+
) {
|
|
594
|
+
throw new Error(
|
|
595
|
+
'Returned cleanup function which cannot be called: ' + String(cleanup),
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// If closed before the source function existed, cleanup now.
|
|
601
|
+
if (closed) {
|
|
602
|
+
doCleanup();
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return subscription;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function swallowError(_error: Error, _isUncaughtThrownError: boolean): void {
|
|
609
|
+
// do nothing.
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (__DEV__) {
|
|
613
|
+
// Default implementation of HostReportErrors() in development builds.
|
|
614
|
+
// Can be replaced by the host application environment.
|
|
615
|
+
RelayObservable.onUnhandledError((error, isUncaughtThrownError) => {
|
|
616
|
+
declare function fail(string): void;
|
|
617
|
+
if (typeof fail === 'function') {
|
|
618
|
+
// In test environments (Jest), fail() immediately fails the current test.
|
|
619
|
+
fail(String(error));
|
|
620
|
+
} else if (isUncaughtThrownError) {
|
|
621
|
+
// Rethrow uncaught thrown errors on the next frame to avoid breaking
|
|
622
|
+
// current logic.
|
|
623
|
+
setTimeout(() => {
|
|
624
|
+
throw error;
|
|
625
|
+
});
|
|
626
|
+
} else if (typeof console !== 'undefined') {
|
|
627
|
+
// Otherwise, log the unhandled error for visibility.
|
|
628
|
+
// eslint-disable-next-line no-console
|
|
629
|
+
console.error('RelayObservable: Unhandled Error', error);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
module.exports = RelayObservable;
|