@fncts/observable 0.0.25 → 0.0.27

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 (199) hide show
  1. package/Observable/api/raceWith.d.ts +0 -1
  2. package/Observable/api/repeatWhen.d.ts +1 -1
  3. package/Observable/api/share.d.ts +1 -1
  4. package/Observable/api.d.ts +4 -4
  5. package/Observable/definition.d.ts +1 -4
  6. package/Observable.d.ts +16 -16
  7. package/ObservableRef.d.ts +2 -2
  8. package/Operator.d.ts +3 -3
  9. package/Subject.d.ts +16 -7
  10. package/Subscriber.d.ts +23 -8
  11. package/Subscription.d.ts +2 -2
  12. package/_cjs/Action.cjs +2 -3
  13. package/_cjs/Action.cjs.map +1 -1
  14. package/_cjs/AnimationFrameAction.cjs +3 -3
  15. package/_cjs/AnimationFrameAction.cjs.map +1 -1
  16. package/_cjs/AnimationFrameScheduler.cjs +3 -3
  17. package/_cjs/AnimationFrameScheduler.cjs.map +1 -1
  18. package/_cjs/AsyncAction.cjs +5 -5
  19. package/_cjs/AsyncAction.cjs.map +1 -1
  20. package/_cjs/AsyncScheduler.cjs +3 -5
  21. package/_cjs/AsyncScheduler.cjs.map +1 -1
  22. package/_cjs/BehaviorSubject.cjs +3 -3
  23. package/_cjs/BehaviorSubject.cjs.map +1 -1
  24. package/_cjs/Notification.cjs.map +1 -1
  25. package/_cjs/Observable/api/connect.cjs +8 -8
  26. package/_cjs/Observable/api/connect.cjs.map +1 -1
  27. package/_cjs/Observable/api/connectable.cjs +5 -5
  28. package/_cjs/Observable/api/connectable.cjs.map +1 -1
  29. package/_cjs/Observable/api/fromCallback.cjs +3 -3
  30. package/_cjs/Observable/api/fromCallback.cjs.map +1 -1
  31. package/_cjs/Observable/api/fromEvent.cjs +2 -2
  32. package/_cjs/Observable/api/fromEvent.cjs.map +1 -1
  33. package/_cjs/Observable/api/index.cjs.map +1 -1
  34. package/_cjs/Observable/api/race.cjs +5 -5
  35. package/_cjs/Observable/api/race.cjs.map +1 -1
  36. package/_cjs/Observable/api/raceWith.cjs +4 -5
  37. package/_cjs/Observable/api/raceWith.cjs.map +1 -1
  38. package/_cjs/Observable/api/repeatWhen.cjs +10 -9
  39. package/_cjs/Observable/api/repeatWhen.cjs.map +1 -1
  40. package/_cjs/Observable/api/retryWhen.cjs +10 -9
  41. package/_cjs/Observable/api/retryWhen.cjs.map +1 -1
  42. package/_cjs/Observable/api/share.cjs +10 -10
  43. package/_cjs/Observable/api/share.cjs.map +1 -1
  44. package/_cjs/Observable/api/window.cjs +13 -12
  45. package/_cjs/Observable/api/window.cjs.map +1 -1
  46. package/_cjs/Observable/api/windowCount.cjs +13 -11
  47. package/_cjs/Observable/api/windowCount.cjs.map +1 -1
  48. package/_cjs/Observable/api/windowTime.cjs +13 -12
  49. package/_cjs/Observable/api/windowTime.cjs.map +1 -1
  50. package/_cjs/Observable/api/windowToggle.cjs +27 -25
  51. package/_cjs/Observable/api/windowToggle.cjs.map +1 -1
  52. package/_cjs/Observable/api/windowWhen.cjs +18 -16
  53. package/_cjs/Observable/api/windowWhen.cjs.map +1 -1
  54. package/_cjs/Observable/api.cjs +328 -317
  55. package/_cjs/Observable/api.cjs.map +1 -1
  56. package/_cjs/Observable/definition.cjs +8 -15
  57. package/_cjs/Observable/definition.cjs.map +1 -1
  58. package/_cjs/Observable/dom/animationFrames.cjs +4 -4
  59. package/_cjs/Observable/dom/animationFrames.cjs.map +1 -1
  60. package/_cjs/Observable/instances.cjs.map +1 -1
  61. package/_cjs/Observable.cjs +64 -64
  62. package/_cjs/Observable.cjs.map +1 -1
  63. package/_cjs/ObservableRef/api.cjs +3 -3
  64. package/_cjs/ObservableRef/api.cjs.map +1 -1
  65. package/_cjs/ObservableRef/atomic.cjs +3 -3
  66. package/_cjs/ObservableRef/atomic.cjs.map +1 -1
  67. package/_cjs/ObservableRef/definition.cjs +2 -2
  68. package/_cjs/ObservableRef/definition.cjs.map +1 -1
  69. package/_cjs/ObservableRef.cjs +8 -8
  70. package/_cjs/ObservableRef.cjs.map +1 -1
  71. package/_cjs/Observer.cjs.map +1 -1
  72. package/_cjs/Operator.cjs +10 -15
  73. package/_cjs/Operator.cjs.map +1 -1
  74. package/_cjs/Scheduler.cjs +3 -3
  75. package/_cjs/Scheduler.cjs.map +1 -1
  76. package/_cjs/Subject.cjs +70 -44
  77. package/_cjs/Subject.cjs.map +1 -1
  78. package/_cjs/Subscriber.cjs +123 -71
  79. package/_cjs/Subscriber.cjs.map +1 -1
  80. package/_cjs/Subscription.cjs +8 -8
  81. package/_cjs/Subscription.cjs.map +1 -1
  82. package/_cjs/config.cjs +15 -0
  83. package/_cjs/config.cjs.map +1 -0
  84. package/_cjs/global.cjs.map +1 -1
  85. package/_cjs/index.cjs +14 -14
  86. package/_cjs/index.cjs.map +1 -1
  87. package/_cjs/internal/animationFrameProvider.cjs +2 -2
  88. package/_cjs/internal/animationFrameProvider.cjs.map +1 -1
  89. package/_cjs/internal/args.cjs +2 -2
  90. package/_cjs/internal/args.cjs.map +1 -1
  91. package/_cjs/internal/intervalProvider.cjs.map +1 -1
  92. package/_cjs/internal/performanceTimestampProvider.cjs.map +1 -1
  93. package/_cjs/internal/timeoutProvider.cjs.map +1 -1
  94. package/_cjs/internal/timestampProvider.cjs.map +1 -1
  95. package/_cjs/internal/util.cjs +3 -3
  96. package/_cjs/internal/util.cjs.map +1 -1
  97. package/_mjs/Action.mjs +0 -1
  98. package/_mjs/Action.mjs.map +1 -1
  99. package/_mjs/AnimationFrameAction.mjs.map +1 -1
  100. package/_mjs/AnimationFrameScheduler.mjs.map +1 -1
  101. package/_mjs/AsyncAction.mjs +3 -3
  102. package/_mjs/AsyncAction.mjs.map +1 -1
  103. package/_mjs/AsyncScheduler.mjs +0 -2
  104. package/_mjs/AsyncScheduler.mjs.map +1 -1
  105. package/_mjs/BehaviorSubject.mjs +1 -1
  106. package/_mjs/BehaviorSubject.mjs.map +1 -1
  107. package/_mjs/Notification.mjs.map +1 -1
  108. package/_mjs/Observable/api/connect.mjs +6 -6
  109. package/_mjs/Observable/api/connect.mjs.map +1 -1
  110. package/_mjs/Observable/api/connectable.mjs +1 -1
  111. package/_mjs/Observable/api/connectable.mjs.map +1 -1
  112. package/_mjs/Observable/api/fromCallback.mjs.map +1 -1
  113. package/_mjs/Observable/api/fromEvent.mjs.map +1 -1
  114. package/_mjs/Observable/api/index.mjs.map +1 -1
  115. package/_mjs/Observable/api/race.mjs +1 -1
  116. package/_mjs/Observable/api/race.mjs.map +1 -1
  117. package/_mjs/Observable/api/raceWith.mjs +3 -4
  118. package/_mjs/Observable/api/raceWith.mjs.map +1 -1
  119. package/_mjs/Observable/api/repeatWhen.mjs +9 -8
  120. package/_mjs/Observable/api/repeatWhen.mjs.map +1 -1
  121. package/_mjs/Observable/api/retryWhen.mjs +9 -8
  122. package/_mjs/Observable/api/retryWhen.mjs.map +1 -1
  123. package/_mjs/Observable/api/share.mjs +8 -8
  124. package/_mjs/Observable/api/share.mjs.map +1 -1
  125. package/_mjs/Observable/api/window.mjs +12 -11
  126. package/_mjs/Observable/api/window.mjs.map +1 -1
  127. package/_mjs/Observable/api/windowCount.mjs +12 -10
  128. package/_mjs/Observable/api/windowCount.mjs.map +1 -1
  129. package/_mjs/Observable/api/windowTime.mjs +11 -10
  130. package/_mjs/Observable/api/windowTime.mjs.map +1 -1
  131. package/_mjs/Observable/api/windowToggle.mjs +25 -23
  132. package/_mjs/Observable/api/windowToggle.mjs.map +1 -1
  133. package/_mjs/Observable/api/windowWhen.mjs +17 -15
  134. package/_mjs/Observable/api/windowWhen.mjs.map +1 -1
  135. package/_mjs/Observable/api.mjs +306 -295
  136. package/_mjs/Observable/api.mjs.map +1 -1
  137. package/_mjs/Observable/definition.mjs +2 -9
  138. package/_mjs/Observable/definition.mjs.map +1 -1
  139. package/_mjs/Observable/dom/animationFrames.mjs +1 -1
  140. package/_mjs/Observable/dom/animationFrames.mjs.map +1 -1
  141. package/_mjs/Observable/instances.mjs.map +1 -1
  142. package/_mjs/Observable.mjs +16 -19
  143. package/_mjs/Observable.mjs.map +1 -1
  144. package/_mjs/ObservableRef/api.mjs.map +1 -1
  145. package/_mjs/ObservableRef/atomic.mjs.map +1 -1
  146. package/_mjs/ObservableRef/definition.mjs.map +1 -1
  147. package/_mjs/ObservableRef.mjs +2 -3
  148. package/_mjs/ObservableRef.mjs.map +1 -1
  149. package/_mjs/Observer.mjs.map +1 -1
  150. package/_mjs/Operator.mjs +6 -11
  151. package/_mjs/Operator.mjs.map +1 -1
  152. package/_mjs/Scheduler.mjs.map +1 -1
  153. package/_mjs/Subject.mjs +65 -39
  154. package/_mjs/Subject.mjs.map +1 -1
  155. package/_mjs/Subscriber.mjs +119 -67
  156. package/_mjs/Subscriber.mjs.map +1 -1
  157. package/_mjs/Subscription.mjs +6 -6
  158. package/_mjs/Subscription.mjs.map +1 -1
  159. package/_mjs/config.mjs +9 -0
  160. package/_mjs/config.mjs.map +1 -0
  161. package/_mjs/global.mjs.map +1 -1
  162. package/_mjs/index.mjs.map +1 -1
  163. package/_mjs/internal/animationFrameProvider.mjs.map +1 -1
  164. package/_mjs/internal/args.mjs.map +1 -1
  165. package/_mjs/internal/intervalProvider.mjs.map +1 -1
  166. package/_mjs/internal/performanceTimestampProvider.mjs.map +1 -1
  167. package/_mjs/internal/timeoutProvider.mjs.map +1 -1
  168. package/_mjs/internal/timestampProvider.mjs.map +1 -1
  169. package/_mjs/internal/util.mjs.map +1 -1
  170. package/_src/Action.ts +0 -1
  171. package/_src/AsyncAction.ts +3 -3
  172. package/_src/AsyncScheduler.ts +0 -2
  173. package/_src/BehaviorSubject.ts +1 -1
  174. package/_src/Observable/api/connect.ts +2 -2
  175. package/_src/Observable/api/connectable.ts +1 -1
  176. package/_src/Observable/api/race.ts +1 -1
  177. package/_src/Observable/api/raceWith.ts +2 -5
  178. package/_src/Observable/api/repeatWhen.ts +4 -4
  179. package/_src/Observable/api/retryWhen.ts +3 -3
  180. package/_src/Observable/api/share.ts +6 -6
  181. package/_src/Observable/api/window.ts +3 -3
  182. package/_src/Observable/api/windowCount.ts +30 -33
  183. package/_src/Observable/api/windowTime.ts +4 -4
  184. package/_src/Observable/api/windowToggle.ts +19 -21
  185. package/_src/Observable/api/windowWhen.ts +10 -13
  186. package/_src/Observable/api.ts +358 -366
  187. package/_src/Observable/definition.ts +2 -17
  188. package/_src/Observable/dom/animationFrames.ts +1 -1
  189. package/_src/Observable.ts +18 -19
  190. package/_src/ObservableRef.ts +2 -3
  191. package/_src/Operator.ts +9 -21
  192. package/_src/Subject.ts +66 -39
  193. package/_src/Subscriber.ts +134 -59
  194. package/_src/Subscription.ts +8 -8
  195. package/_src/config.ts +40 -0
  196. package/_src/global.ts +1 -1
  197. package/config.d.ts +36 -0
  198. package/global.d.ts +1 -1
  199. package/package.json +2 -2
@@ -19,7 +19,6 @@ export class Observable<R, E, A> implements Subscribable<E, A>, AsyncIterable<A>
19
19
  readonly [ObservableTypeId]: ObservableTypeId = ObservableTypeId;
20
20
 
21
21
  protected source: Observable<any, any, any> | undefined;
22
- protected operator: Operator<E, A> | undefined;
23
22
  protected environment: Environment<any> = Environment();
24
23
 
25
24
  constructor(
@@ -80,19 +79,11 @@ export class Observable<R, E, A> implements Subscribable<E, A>, AsyncIterable<A>
80
79
  };
81
80
  }
82
81
 
83
- lift<R1, E1, A1>(operator: Operator<E1, A1>): Observable<R1, E1, A1> {
84
- const observable = new Observable<R1, E1, A1>();
85
- observable.source = this;
86
- observable.operator = operator;
87
- return observable;
88
- }
89
-
90
82
  provideEnvironment(environment: Environment<R>): Observable<never, E, A>;
91
83
  provideEnvironment<In>(environment: Environment<In>): Observable<Exclude<R, In>, E, A>;
92
84
  provideEnvironment<In>(environment: Environment<In>): Observable<Exclude<R, In>, E, A> {
93
85
  const observable = new Observable<never, E, A>(this.subscribeInternal);
94
86
  observable.source = this.source;
95
- observable.operator = this.operator;
96
87
  observable.environment = this.environment.union(environment);
97
88
  return observable;
98
89
  }
@@ -104,15 +95,9 @@ export class Observable<R, E, A> implements Subscribable<E, A>, AsyncIterable<A>
104
95
  this: Observable<never, E, A>,
105
96
  observer?: Partial<Observer<E, A>> | ((value: A) => void),
106
97
  ): Subscription {
107
- const subscriber: Subscriber<E, A> = isSubscriber(observer) ? observer : new SafeSubscriber(observer);
98
+ const subscriber: Subscriber<E, A> = isSubscriber(observer) ? observer : new Subscriber(observer);
108
99
 
109
- subscriber.add(
110
- this.operator
111
- ? this.operator.call(subscriber, this.source, this.environment)
112
- : this.source
113
- ? this.subscribeInternal(subscriber, this.environment)
114
- : this.trySubscribe(subscriber, this.environment),
115
- );
100
+ subscriber.add(this.trySubscribe(subscriber, this.environment));
116
101
 
117
102
  return subscriber;
118
103
  }
@@ -31,7 +31,7 @@ function animationFramesInternal(timestampProvider?: TimestampProvider): Observa
31
31
  timestamp: timestampProvider ? now : timestamp,
32
32
  elapsed: now - start,
33
33
  });
34
- if (!subscriber.closed) {
34
+ if (!subscriber._closed) {
35
35
  subscription.add(schedule(run));
36
36
  }
37
37
  };
@@ -1,28 +1,27 @@
1
- /* eslint-disable simple-import-sort/exports */
2
1
  // codegen:start { preset: barrel, include: Observable/*.ts }
3
- export * from "./Observable/instances.js";
4
- export * from "./Observable/definition.js";
5
2
  export * from "./Observable/api.js";
3
+ export * from "./Observable/definition.js";
4
+ export * from "./Observable/instances.js";
6
5
  // codegen:end
7
- /* eslint-disable simple-import-sort/exports */
6
+
8
7
  // codegen:start { preset: barrel, include: Observable/api/*.ts }
9
- export * from "./Observable/api/windowWhen.js";
10
- export * from "./Observable/api/windowToggle.js";
11
- export * from "./Observable/api/windowTime.js";
12
- export * from "./Observable/api/windowCount.js";
13
- export * from "./Observable/api/window.js";
14
- export * from "./Observable/api/share.js";
15
- export * from "./Observable/api/retryWhen.js";
16
- export * from "./Observable/api/repeatWhen.js";
17
- export * from "./Observable/api/raceWith.js";
18
- export * from "./Observable/api/race.js";
19
- export * from "./Observable/api/index.js";
20
- export * from "./Observable/api/fromEvent.js";
21
- export * from "./Observable/api/fromCallback.js";
22
- export * from "./Observable/api/connectable.js";
23
8
  export * from "./Observable/api/connect.js";
9
+ export * from "./Observable/api/connectable.js";
10
+ export * from "./Observable/api/fromCallback.js";
11
+ export * from "./Observable/api/fromEvent.js";
12
+ export * from "./Observable/api/index.js";
13
+ export * from "./Observable/api/race.js";
14
+ export * from "./Observable/api/raceWith.js";
15
+ export * from "./Observable/api/repeatWhen.js";
16
+ export * from "./Observable/api/retryWhen.js";
17
+ export * from "./Observable/api/share.js";
18
+ export * from "./Observable/api/window.js";
19
+ export * from "./Observable/api/windowCount.js";
20
+ export * from "./Observable/api/windowTime.js";
21
+ export * from "./Observable/api/windowToggle.js";
22
+ export * from "./Observable/api/windowWhen.js";
24
23
  // codegen:end
25
- /* eslint-disable simple-import-sort/exports */
24
+
26
25
  // codegen:start { preset: barrel, include: Observable/dom/*.ts }
27
26
  export * from "./Observable/dom/animationFrames.js";
28
27
  // codegen:end
@@ -1,6 +1,5 @@
1
- /* eslint-disable simple-import-sort/exports */
2
1
  // codegen:start { preset: barrel, include: ./ObservableRef/*.ts }
3
- export * from "./ObservableRef/definition.js";
4
- export * from "./ObservableRef/atomic.js";
5
2
  export * from "./ObservableRef/api.js";
3
+ export * from "./ObservableRef/atomic.js";
4
+ export * from "./ObservableRef/definition.js";
6
5
  // codegen:end
package/_src/Operator.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { SubscriberOverrides } from "./Subscriber.js";
2
+
1
3
  export interface Operator<E, A> {
2
4
  call(subscriber: Subscriber<E, A>, source: any, environment: Environment<any>): Finalizer;
3
5
  }
@@ -44,7 +46,7 @@ export class OperatorSubscriber<E, A> extends Subscriber<E, A> {
44
46
  : super.complete;
45
47
  }
46
48
  unsubscribe() {
47
- const { closed } = this;
49
+ const { _closed: closed } = this;
48
50
  super.unsubscribe();
49
51
  !closed && this.onFinalize?.();
50
52
  }
@@ -56,27 +58,13 @@ export function operatorSubscriber<E, A, E1, A1>(
56
58
  ): OperatorSubscriber<E, A> {
57
59
  return new OperatorSubscriber(destination, observer, onFinalize);
58
60
  }
61
+
59
62
  /**
60
- * @tsplus pipeable fncts.observable.Observable operate
63
+ * @tsplus pipeable fncts.observable.Subscriber operate
61
64
  */
62
- export function operate_<R, E, A, R1, E1, A1>(
63
- f: (
64
- source: Observable<R, E, A>,
65
- subscriber: Subscriber<E1, A1>,
66
- environment: Environment<R | R1>,
67
- ) => (() => void) | void,
68
- ) {
69
- return (source: Observable<R, E, A>): Observable<R1, E1, A1> => {
70
- return source.lift(function (
71
- this: Subscriber<E1, A1>,
72
- liftedSource: Observable<R, E, A>,
73
- environment: Environment<R | R1>,
74
- ) {
75
- try {
76
- f(liftedSource, this, environment);
77
- } catch (err) {
78
- this.error(Cause.halt(err));
79
- }
80
- });
65
+ export function operate_<E1, A1>(config: SubscriberOverrides<E1, A1>) {
66
+ return <E, A>(destination: Subscriber<E, A>): Subscriber<E1, A1> => {
67
+ // @ts-expect-error
68
+ return new Subscriber<E1, A1>(destination, config);
81
69
  };
82
70
  }
package/_src/Subject.ts CHANGED
@@ -1,11 +1,19 @@
1
- import { arrayRemove } from "@fncts/observable/internal/util";
2
-
3
1
  export interface SubjectLike<E, A> extends Observer<E, A>, Subscribable<E, A> {}
4
2
 
3
+ /**
4
+ * A Subject is a special type of Observable that allows values to be
5
+ * multicasted to many Observers. Subjects are like EventEmitters.
6
+ *
7
+ * Every Subject is an Observable and an Observer. You can subscribe to a
8
+ * Subject, and you can call next to feed values as well as error and complete.
9
+ */
5
10
  export class Subject<R, E, A> extends Observable<R, E, A> implements SubscriptionLike {
6
- closed = false;
7
- protected observers: Array<Observer<E, A>> = [];
8
- protected isStopped = false;
11
+ _closed = false;
12
+
13
+ protected currentObservers = new Map<number, Observer<E, A>>();
14
+ private observersCount = 0;
15
+ private observerSnapshot: Array<Observer<E, A>> | undefined;
16
+
9
17
  protected hasError = false;
10
18
  protected thrownError: Cause<E> = null!;
11
19
 
@@ -13,83 +21,102 @@ export class Subject<R, E, A> extends Observable<R, E, A> implements Subscriptio
13
21
  super();
14
22
  }
15
23
 
16
- lift<R1, E1, B>(operator: Operator<E1, B>): Observable<R1, E1, B> {
17
- const subject = new AnonymousSubject(this, this);
18
- subject.operator = operator as any;
19
- return subject as any;
24
+ get closed() {
25
+ return this._closed;
26
+ }
27
+
28
+ get observers(): Array<Observer<E, A>> {
29
+ return (this.observerSnapshot ??= Array.from(this.currentObservers.values()));
20
30
  }
21
31
 
22
32
  next(value: A) {
23
- this.throwIfClosed();
24
- if (!this.isStopped) {
25
- const copy = this.observers.slice();
26
- for (const observer of copy) {
27
- observer.next(value);
33
+ if (!this._closed) {
34
+ const { observers } = this;
35
+ const len = observers.length;
36
+ for (let i = 0; i < len; i++) {
37
+ observers[i].next(value);
28
38
  }
29
39
  }
30
40
  }
31
41
 
32
42
  error(err: Cause<E>) {
33
- this.throwIfClosed();
34
- if (!this.isStopped) {
35
- this.hasError = this.isStopped = true;
43
+ if (!this._closed) {
44
+ this.hasError = this._closed = true;
36
45
  this.thrownError = err;
37
46
  const { observers } = this;
38
- while (observers.length) {
39
- observers.shift()!.error(err);
47
+ const len = observers.length;
48
+ for (let i = 0; i < len; i++) {
49
+ observers[i].error(err);
40
50
  }
51
+ this.clearObservers();
41
52
  }
42
53
  }
43
54
 
44
55
  complete() {
45
- this.throwIfClosed();
46
- if (!this.isStopped) {
47
- this.isStopped = true;
56
+ if (!this._closed) {
57
+ this._closed = true;
48
58
  const { observers } = this;
49
- while (observers.length) {
50
- observers.shift()!.complete();
59
+ const len = observers.length;
60
+ for (let i = 0; i < len; i++) {
61
+ observers[i].complete();
51
62
  }
63
+ this.clearObservers();
52
64
  }
53
65
  }
54
66
 
55
67
  unsubscribe() {
56
- this.isStopped = this.closed = true;
57
- this.observers = null!;
68
+ this._closed = true;
69
+ this.clearObservers();
58
70
  }
59
71
 
60
72
  get observed() {
61
- return this.observers?.length > 0;
73
+ return this.currentObservers.size > 0;
62
74
  }
63
75
 
64
76
  protected throwIfClosed() {
65
- if (this.closed) {
77
+ if (this._closed) {
66
78
  throw new Error("Object Unsubscribed");
67
79
  }
68
80
  }
69
81
 
82
+ protected clearObservers() {
83
+ this.currentObservers.clear();
84
+ this.observerSnapshot = undefined;
85
+ }
86
+
70
87
  protected trySubscribe(subscriber: Subscriber<E, A>, environment: Environment<R>): Finalizer {
71
88
  this.throwIfClosed();
72
89
  return super.trySubscribe(subscriber, environment);
73
90
  }
74
91
 
75
92
  protected subscribeInternal(subscriber: Subscriber<E, A>): Subscription {
76
- this.throwIfClosed();
77
93
  this.checkFinalizedStatuses(subscriber);
78
94
  return this.innerSubscribe(subscriber);
79
95
  }
80
96
 
81
97
  protected innerSubscribe(subscriber: Subscriber<E, A>): Subscription {
82
- const { hasError, isStopped, observers } = this;
83
- return hasError || isStopped
84
- ? Subscription.empty
85
- : (observers.push(subscriber), new Subscription(() => arrayRemove(observers, subscriber)));
98
+ const { hasError, _closed: closed } = this;
99
+ if (hasError || closed) {
100
+ return Subscription.empty;
101
+ }
102
+
103
+ const { currentObservers } = this;
104
+
105
+ const observerId = this.observersCount++;
106
+ currentObservers.set(observerId, subscriber);
107
+ this.observerSnapshot = undefined;
108
+ subscriber.add(() => {
109
+ currentObservers.delete(observerId);
110
+ this.observerSnapshot = undefined;
111
+ });
112
+ return subscriber;
86
113
  }
87
114
 
88
115
  protected checkFinalizedStatuses(subscriber: Subscriber<any, any>) {
89
- const { hasError, thrownError, isStopped } = this;
116
+ const { hasError, thrownError, _closed: closed } = this;
90
117
  if (hasError) {
91
118
  subscriber.error(thrownError);
92
- } else if (isStopped) {
119
+ } else if (closed) {
93
120
  subscriber.complete();
94
121
  }
95
122
  }
@@ -132,10 +159,10 @@ export class AsyncSubject<R, E, A> extends Subject<R, E, A> {
132
159
 
133
160
  /** @internal */
134
161
  protected checkFinalizedStatuses(subscriber: Subscriber<E, A>) {
135
- const { hasError, hasValue, value, thrownError, isStopped, isComplete } = this;
162
+ const { hasError, hasValue, value, thrownError, _closed: closed, isComplete } = this;
136
163
  if (hasError) {
137
164
  subscriber.error(thrownError);
138
- } else if (isStopped || isComplete) {
165
+ } else if (closed || isComplete) {
139
166
  hasValue &&
140
167
  value!.match(
141
168
  (e) => subscriber.error(e),
@@ -146,14 +173,14 @@ export class AsyncSubject<R, E, A> extends Subject<R, E, A> {
146
173
  }
147
174
 
148
175
  next(value: A) {
149
- if (!this.isStopped) {
176
+ if (!this._closed) {
150
177
  this.value = Either.right(value);
151
178
  this.hasValue = true;
152
179
  }
153
180
  }
154
181
 
155
182
  error(err: Cause<E>) {
156
- if (!this.isStopped) {
183
+ if (!this._closed) {
157
184
  this.value = Either.left(err);
158
185
  this.hasValue = true;
159
186
  }
@@ -1,55 +1,92 @@
1
+ import { Cause } from "@fncts/base/data/Cause";
2
+ import { isFunction } from "@fncts/base/util/predicates";
3
+
4
+ import { config } from "./config.js";
5
+ import { reportUnhandledError } from "./internal/util.js";
6
+ import { Notification } from "./Notification.js";
7
+
1
8
  export const SubscriberTypeId = Symbol.for("fncts.observable.Subscriber");
2
9
  export type SubscriberTypeId = typeof SubscriberTypeId;
3
10
 
11
+ export interface SubscriberOverrides<E, A> {
12
+ next?: (value: A) => void;
13
+ error?: (error: Cause<E>) => void;
14
+ complete?: () => void;
15
+ finalize?: () => void;
16
+ }
17
+
18
+ /**
19
+ * @tsplus type fncts.observable.Subscriber
20
+ */
4
21
  export class Subscriber<E, A> extends Subscription implements Observer<E, A> {
5
22
  readonly [SubscriberTypeId]: SubscriberTypeId = SubscriberTypeId;
6
23
 
7
24
  private isStopped = false;
8
- protected observer: Subscriber<E, A> | Observer<E, A> | null;
9
-
10
- constructor(observer?: Subscriber<E, A> | Observer<E, A>) {
11
- super();
12
- if (observer) {
13
- this.observer = observer;
14
- if (isSubscription(observer)) {
15
- observer.add(this);
16
- }
17
- } else {
18
- this.observer = EMPTY_OBSERVER;
25
+ protected destination: Subscriber<E, A> | Observer<E, A> | null;
26
+
27
+ protected readonly nextOverride: ((value: A) => void) | null = null;
28
+ protected readonly errorOverride: ((err: Cause<E>) => void) | null = null;
29
+ protected readonly completeOverride: (() => void) | null = null;
30
+
31
+ constructor(
32
+ destination?: Subscriber<E, A> | Partial<Observer<E, A>> | ((value: A) => void) | null,
33
+ overrides?: SubscriberOverrides<E, A>,
34
+ ) {
35
+ super(overrides?.finalize);
36
+
37
+ this.destination = destination instanceof Subscriber ? destination : createSafeObserver(destination);
38
+
39
+ this.nextOverride = overrides?.next ?? null;
40
+ this.errorOverride = overrides?.error ?? null;
41
+ this.completeOverride = overrides?.complete ?? null;
42
+
43
+ this.next = this.nextOverride ? overrideNext : this.next;
44
+ this.error = this.errorOverride ? overrideError : this.error;
45
+ this.complete = this.completeOverride ? overrideComplete : this.complete;
46
+
47
+ if (hasAddAndUnsubscribe(destination)) {
48
+ destination.add(this);
19
49
  }
20
50
  }
51
+
21
52
  next(value: A) {
22
- if (!this.isStopped) {
53
+ if (this.isStopped) {
54
+ handleStoppedNotification(Notification.next(value), this);
55
+ } else {
23
56
  this._next(value);
24
57
  }
25
58
  }
26
59
  error(err: Cause<E>) {
27
- if (!this.isStopped) {
60
+ if (this.isStopped) {
61
+ handleStoppedNotification(Notification.error(err), this);
62
+ } else {
28
63
  this.isStopped = true;
29
64
  this._error(err);
30
65
  }
31
66
  }
32
67
  complete() {
33
- if (!this.isStopped) {
68
+ if (this.isStopped) {
69
+ handleStoppedNotification(Notification.complete(), this);
70
+ } else {
34
71
  this.isStopped = true;
35
72
  this._complete();
36
73
  }
37
74
  }
38
75
  unsubscribe(): void {
39
- if (!this.closed) {
76
+ if (!this._closed) {
40
77
  this.isStopped = true;
41
78
  super.unsubscribe();
42
- this.observer = null;
79
+ this.destination = null;
43
80
  }
44
81
  }
45
82
 
46
83
  _next(value: A) {
47
- this.observer!.next(value);
84
+ this.destination!.next(value);
48
85
  }
49
86
 
50
87
  _error(err: Cause<E>) {
51
88
  try {
52
- this.observer!.error(err);
89
+ this.destination!.error(err);
53
90
  } finally {
54
91
  this.unsubscribe();
55
92
  }
@@ -57,60 +94,89 @@ export class Subscriber<E, A> extends Subscription implements Observer<E, A> {
57
94
 
58
95
  _complete() {
59
96
  try {
60
- this.observer!.complete();
97
+ this.destination!.complete();
61
98
  } finally {
62
99
  this.unsubscribe();
63
100
  }
64
101
  }
65
102
  }
66
- export class SafeSubscriber<E, A> extends Subscriber<E, A> {
67
- constructor(observer?: Partial<Observer<E, A>> | ((value: A) => void)) {
68
- super();
69
- let next: ((value: A) => void) | undefined = undefined;
70
- let error: ((err: Cause<E>) => void) | undefined = undefined;
71
- let complete: (() => void) | undefined = undefined;
72
- if (isFunction(observer)) {
73
- next = observer;
74
- } else if (observer) {
75
- ({ next, error, complete } = observer);
76
- next = next?.bind(observer);
77
- error = error?.bind(observer);
78
- complete = complete?.bind(observer);
103
+
104
+ function createSafeObserver<E, A>(
105
+ observerOrNext?: Partial<Observer<E, A>> | ((value: A) => void) | null,
106
+ ): Observer<E, A> {
107
+ return new ConsumerObserver(
108
+ !observerOrNext || isFunction(observerOrNext) ? { next: observerOrNext ?? undefined } : observerOrNext,
109
+ );
110
+ }
111
+
112
+ export class ConsumerObserver<E, A> implements Observer<E, A> {
113
+ private onError: (error: Cause<E>) => void = reportUnhandledError;
114
+ constructor(private partialObserver: Partial<Observer<E, A>>) {
115
+ this.onError = partialObserver.error ?? reportUnhandledError;
116
+ }
117
+
118
+ next(value: A): void {
119
+ const { partialObserver } = this;
120
+ if (partialObserver.next) {
121
+ try {
122
+ partialObserver.next(value);
123
+ } catch (error) {
124
+ this.onError(Cause.halt(error));
125
+ }
79
126
  }
80
- if (error) {
81
- this.observer = {
82
- next: next ? wrapDefectHandler(next, error) : noop,
83
- error: error ? wrapDefectHandler(error, error) : noop,
84
- complete: complete ? wrapDefectHandler(complete, error) : noop,
85
- };
127
+ }
128
+
129
+ error(err: Cause<E>): void {
130
+ const { partialObserver } = this;
131
+ if (partialObserver.error) {
132
+ try {
133
+ partialObserver.error(err);
134
+ } catch (error) {
135
+ this.onError(Cause.halt(error));
136
+ }
86
137
  } else {
87
- this.observer = {
88
- next: next ? wrapThrowHandler(next) : noop,
89
- error: wrapThrowHandler(error ?? defaultErrorHandler),
90
- complete: complete ? wrapThrowHandler(complete) : noop,
91
- };
138
+ this.onError(err);
92
139
  }
93
140
  }
94
- }
95
141
 
96
- function wrapDefectHandler<E>(handler: (arg?: any) => void, onDefect: (err: Cause<E>) => void) {
97
- return (...args: any[]) => {
98
- try {
99
- handler(...args);
100
- } catch (err) {
101
- onDefect(Cause.halt(err));
142
+ complete(): void {
143
+ const { partialObserver } = this;
144
+ if (partialObserver.complete) {
145
+ try {
146
+ partialObserver.complete();
147
+ } catch (error) {
148
+ this.onError(Cause.halt(error));
149
+ }
102
150
  }
103
- };
151
+ }
104
152
  }
105
153
 
106
- function wrapThrowHandler(handler: (arg?: any) => void) {
107
- return (...args: any[]) => {
108
- try {
109
- handler(...args);
110
- } catch (err) {
111
- reportUnhandledError(err);
112
- }
113
- };
154
+ function overrideNext<E, A>(this: Subscriber<E, A>, value: A): void {
155
+ try {
156
+ this.nextOverride!(value);
157
+ } catch (error) {
158
+ this.destination!.error(Cause.halt(error));
159
+ }
160
+ }
161
+
162
+ function overrideError<E, A>(this: Subscriber<E, A>, err: Cause<E>): void {
163
+ try {
164
+ this.errorOverride!(err);
165
+ } catch (error) {
166
+ this.destination!.error(Cause.halt(error));
167
+ } finally {
168
+ this.unsubscribe();
169
+ }
170
+ }
171
+
172
+ function overrideComplete<E, A>(this: Subscriber<E, A>): void {
173
+ try {
174
+ this.completeOverride!();
175
+ } catch (error) {
176
+ this.destination!.error(Cause.halt(error));
177
+ } finally {
178
+ this.unsubscribe();
179
+ }
114
180
  }
115
181
 
116
182
  function defaultErrorHandler(error: any) {
@@ -126,6 +192,15 @@ export const EMPTY_OBSERVER: Readonly<Observer<any, any>> & {
126
192
  complete: noop,
127
193
  };
128
194
 
195
+ function hasAddAndUnsubscribe(value: any): value is Subscription {
196
+ return value && isFunction(value.unsubscribe) && isFunction(value.add);
197
+ }
198
+
129
199
  export function isSubscriber(u: unknown): u is Subscriber<any, any> {
130
200
  return isObject(u) && SubscriberTypeId in u;
131
201
  }
202
+
203
+ function handleStoppedNotification<E, A>(notification: Notification<E, A>, subscriber: Subscriber<E, A>) {
204
+ const { onStoppedNotification } = config;
205
+ onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));
206
+ }
@@ -3,7 +3,7 @@ export interface Unsubscribable {
3
3
  }
4
4
 
5
5
  export interface SubscriptionLike extends Unsubscribable {
6
- readonly closed: boolean;
6
+ readonly _closed: boolean;
7
7
  }
8
8
 
9
9
  export type Finalizer = Unsubscribable | (() => void) | void;
@@ -18,7 +18,7 @@ export type SubscriptionTypeId = typeof SubscriptionTypeId;
18
18
  export class Subscription implements SubscriptionLike {
19
19
  readonly [SubscriptionTypeId]: SubscriptionTypeId = SubscriptionTypeId;
20
20
 
21
- public closed = false;
21
+ public _closed = false;
22
22
  private finalizers: Set<Finalizer> | null = null;
23
23
  private parents: Set<Subscription> | null = null;
24
24
 
@@ -27,8 +27,8 @@ export class Subscription implements SubscriptionLike {
27
27
  unsubscribe(): void {
28
28
  let errors: unknown[] | undefined;
29
29
 
30
- if (!this.closed) {
31
- this.closed = true;
30
+ if (!this._closed) {
31
+ this._closed = true;
32
32
 
33
33
  const { parents, initialFinalizer, finalizers } = this;
34
34
 
@@ -71,11 +71,11 @@ export class Subscription implements SubscriptionLike {
71
71
 
72
72
  add(finalizer: Finalizer): void {
73
73
  if (finalizer && finalizer !== this) {
74
- if (this.closed) {
74
+ if (this._closed) {
75
75
  executeFinalizer(finalizer);
76
76
  } else {
77
77
  if (isSubscription(finalizer)) {
78
- if (finalizer.closed || finalizer.hasParent(this)) {
78
+ if (finalizer._closed || finalizer.hasParent(this)) {
79
79
  return;
80
80
  }
81
81
  finalizer.addParent(this);
@@ -125,8 +125,8 @@ function executeFinalizer(finalizer: Finalizer): void {
125
125
  * @tsplus static fncts.observable.SubscriptionOps empty
126
126
  */
127
127
  export const EMPTY_SUBSCRIPTION = (() => {
128
- const empty = new Subscription();
129
- empty.closed = true;
128
+ const empty = new Subscription();
129
+ empty._closed = true;
130
130
  return empty;
131
131
  })();
132
132