relay-runtime 9.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 (111) 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/connection/ConnectionHandler.js +1 -3
  7. package/lib/index.js +1 -2
  8. package/lib/mutations/RelayDeclarativeMutationConfig.js +22 -45
  9. package/lib/mutations/RelayRecordProxy.js +1 -3
  10. package/lib/mutations/RelayRecordSourceMutator.js +1 -3
  11. package/lib/mutations/RelayRecordSourceProxy.js +1 -3
  12. package/lib/mutations/RelayRecordSourceSelectorProxy.js +1 -3
  13. package/lib/mutations/commitMutation.js +2 -0
  14. package/lib/mutations/validateMutation.js +13 -4
  15. package/lib/network/RelayObservable.js +9 -9
  16. package/lib/network/RelayQueryResponseCache.js +8 -6
  17. package/lib/query/fetchQueryInternal.js +1 -8
  18. package/lib/store/DataChecker.js +23 -51
  19. package/lib/store/RelayConcreteVariables.js +6 -2
  20. package/lib/store/RelayModernEnvironment.js +30 -12
  21. package/lib/store/RelayModernFragmentSpecResolver.js +9 -13
  22. package/lib/store/RelayModernQueryExecutor.js +73 -37
  23. package/lib/store/RelayModernRecord.js +14 -9
  24. package/lib/store/RelayModernStore.js +107 -70
  25. package/lib/store/RelayOperationTracker.js +35 -78
  26. package/lib/store/RelayOptimisticRecordSource.js +7 -5
  27. package/lib/store/RelayPublishQueue.js +1 -3
  28. package/lib/store/RelayReader.js +1 -3
  29. package/lib/store/RelayRecordSource.js +1 -3
  30. package/lib/store/RelayRecordSourceMapImpl.js +13 -18
  31. package/lib/store/RelayReferenceMarker.js +2 -6
  32. package/lib/store/RelayResponseNormalizer.js +9 -10
  33. package/lib/store/StoreInspector.js +7 -5
  34. package/lib/store/normalizeRelayPayload.js +6 -2
  35. package/lib/subscription/requestSubscription.js +4 -2
  36. package/lib/util/RelayFeatureFlags.js +1 -1
  37. package/lib/util/RelayReplaySubject.js +1 -3
  38. package/lib/util/createPayloadFor3DField.js +7 -2
  39. package/mutations/RelayDeclarativeMutationConfig.js.flow +380 -0
  40. package/mutations/RelayRecordProxy.js.flow +165 -0
  41. package/mutations/RelayRecordSourceMutator.js.flow +238 -0
  42. package/mutations/RelayRecordSourceProxy.js.flow +164 -0
  43. package/mutations/RelayRecordSourceSelectorProxy.js.flow +119 -0
  44. package/mutations/applyOptimisticMutation.js.flow +76 -0
  45. package/mutations/commitLocalUpdate.js.flow +24 -0
  46. package/mutations/commitMutation.js.flow +184 -0
  47. package/mutations/validateMutation.js.flow +211 -0
  48. package/network/ConvertToExecuteFunction.js.flow +49 -0
  49. package/network/RelayNetwork.js.flow +84 -0
  50. package/network/RelayNetworkTypes.js.flow +123 -0
  51. package/network/RelayObservable.js.flow +634 -0
  52. package/network/RelayQueryResponseCache.js.flow +111 -0
  53. package/package.json +1 -1
  54. package/query/GraphQLTag.js.flow +166 -0
  55. package/query/fetchQuery.js.flow +47 -0
  56. package/query/fetchQueryInternal.js.flow +349 -0
  57. package/relay-runtime.js +2 -2
  58. package/relay-runtime.min.js +2 -2
  59. package/store/ClientID.js.flow +43 -0
  60. package/store/DataChecker.js.flow +426 -0
  61. package/store/RelayConcreteVariables.js.flow +96 -0
  62. package/store/RelayModernEnvironment.js.flow +526 -0
  63. package/store/RelayModernFragmentSpecResolver.js.flow +426 -0
  64. package/store/RelayModernOperationDescriptor.js.flow +88 -0
  65. package/store/RelayModernQueryExecutor.js.flow +1327 -0
  66. package/store/RelayModernRecord.js.flow +403 -0
  67. package/store/RelayModernSelector.js.flow +444 -0
  68. package/store/RelayModernStore.js.flow +757 -0
  69. package/store/RelayOperationTracker.js.flow +164 -0
  70. package/store/RelayOptimisticRecordSource.js.flow +119 -0
  71. package/store/RelayPublishQueue.js.flow +401 -0
  72. package/store/RelayReader.js.flow +376 -0
  73. package/store/RelayRecordSource.js.flow +29 -0
  74. package/store/RelayRecordSourceMapImpl.js.flow +87 -0
  75. package/store/RelayRecordState.js.flow +37 -0
  76. package/store/RelayReferenceMarker.js.flow +236 -0
  77. package/store/RelayResponseNormalizer.js.flow +556 -0
  78. package/store/RelayStoreTypes.js.flow +873 -0
  79. package/store/RelayStoreUtils.js.flow +218 -0
  80. package/store/StoreInspector.js.flow +173 -0
  81. package/store/ViewerPattern.js.flow +26 -0
  82. package/store/cloneRelayHandleSourceField.js.flow +66 -0
  83. package/store/createFragmentSpecResolver.js.flow +55 -0
  84. package/store/createRelayContext.js.flow +44 -0
  85. package/store/defaultGetDataID.js.flow +27 -0
  86. package/store/hasOverlappingIDs.js.flow +34 -0
  87. package/store/isRelayModernEnvironment.js.flow +27 -0
  88. package/store/normalizeRelayPayload.js.flow +51 -0
  89. package/store/readInlineData.js.flow +75 -0
  90. package/subscription/requestSubscription.js.flow +100 -0
  91. package/util/JSResourceTypes.flow.js.flow +20 -0
  92. package/util/NormalizationNode.js.flow +191 -0
  93. package/util/ReaderNode.js.flow +208 -0
  94. package/util/RelayConcreteNode.js.flow +80 -0
  95. package/util/RelayDefaultHandleKey.js.flow +17 -0
  96. package/util/RelayError.js.flow +33 -0
  97. package/util/RelayFeatureFlags.js.flow +30 -0
  98. package/util/RelayProfiler.js.flow +284 -0
  99. package/util/RelayReplaySubject.js.flow +134 -0
  100. package/util/RelayRuntimeTypes.js.flow +70 -0
  101. package/util/createPayloadFor3DField.js.flow +43 -0
  102. package/util/deepFreeze.js.flow +36 -0
  103. package/util/generateID.js.flow +21 -0
  104. package/util/getFragmentIdentifier.js.flow +52 -0
  105. package/util/getRelayHandleKey.js.flow +41 -0
  106. package/util/getRequestIdentifier.js.flow +41 -0
  107. package/util/isPromise.js.flow +21 -0
  108. package/util/isScalarAndEqual.js.flow +26 -0
  109. package/util/recycleNodesInto.js.flow +80 -0
  110. package/util/resolveImmediate.js.flow +30 -0
  111. 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;