@matter/general 0.13.0-alpha.0-20250318-c1aa38b08 → 0.13.0-alpha.0-20250322-f085fa576

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 (65) hide show
  1. package/dist/cjs/codec/DnsCodec.js +1 -1
  2. package/dist/cjs/codec/DnsCodec.js.map +1 -1
  3. package/dist/cjs/log/Logger.d.ts +11 -6
  4. package/dist/cjs/log/Logger.d.ts.map +1 -1
  5. package/dist/cjs/log/Logger.js +19 -8
  6. package/dist/cjs/log/Logger.js.map +1 -1
  7. package/dist/cjs/time/Time.d.ts +1 -0
  8. package/dist/cjs/time/Time.d.ts.map +1 -1
  9. package/dist/cjs/time/Time.js +3 -0
  10. package/dist/cjs/time/Time.js.map +1 -1
  11. package/dist/cjs/transaction/Participant.d.ts +3 -3
  12. package/dist/cjs/transaction/Participant.d.ts.map +1 -1
  13. package/dist/cjs/transaction/Transaction.d.ts +20 -20
  14. package/dist/cjs/transaction/Tx.js +3 -3
  15. package/dist/cjs/transaction/Tx.js.map +1 -1
  16. package/dist/cjs/util/Construction.js +2 -2
  17. package/dist/cjs/util/Construction.js.map +1 -1
  18. package/dist/cjs/util/DataReadQueue.d.ts +4 -0
  19. package/dist/cjs/util/DataReadQueue.d.ts.map +1 -1
  20. package/dist/cjs/util/DataReadQueue.js +6 -0
  21. package/dist/cjs/util/DataReadQueue.js.map +1 -1
  22. package/dist/cjs/util/FormattedText.js +4 -2
  23. package/dist/cjs/util/FormattedText.js.map +1 -1
  24. package/dist/cjs/util/Observable.d.ts +137 -12
  25. package/dist/cjs/util/Observable.d.ts.map +1 -1
  26. package/dist/cjs/util/Observable.js +297 -35
  27. package/dist/cjs/util/Observable.js.map +2 -2
  28. package/dist/esm/codec/DnsCodec.js +1 -1
  29. package/dist/esm/codec/DnsCodec.js.map +1 -1
  30. package/dist/esm/log/Logger.d.ts +11 -6
  31. package/dist/esm/log/Logger.d.ts.map +1 -1
  32. package/dist/esm/log/Logger.js +19 -8
  33. package/dist/esm/log/Logger.js.map +1 -1
  34. package/dist/esm/time/Time.d.ts +1 -0
  35. package/dist/esm/time/Time.d.ts.map +1 -1
  36. package/dist/esm/time/Time.js +3 -0
  37. package/dist/esm/time/Time.js.map +1 -1
  38. package/dist/esm/transaction/Participant.d.ts +3 -3
  39. package/dist/esm/transaction/Participant.d.ts.map +1 -1
  40. package/dist/esm/transaction/Transaction.d.ts +20 -20
  41. package/dist/esm/transaction/Tx.js +3 -3
  42. package/dist/esm/transaction/Tx.js.map +1 -1
  43. package/dist/esm/util/Construction.js +2 -2
  44. package/dist/esm/util/Construction.js.map +1 -1
  45. package/dist/esm/util/DataReadQueue.d.ts +4 -0
  46. package/dist/esm/util/DataReadQueue.d.ts.map +1 -1
  47. package/dist/esm/util/DataReadQueue.js +6 -0
  48. package/dist/esm/util/DataReadQueue.js.map +1 -1
  49. package/dist/esm/util/FormattedText.js +4 -2
  50. package/dist/esm/util/FormattedText.js.map +1 -1
  51. package/dist/esm/util/Observable.d.ts +137 -12
  52. package/dist/esm/util/Observable.d.ts.map +1 -1
  53. package/dist/esm/util/Observable.js +297 -35
  54. package/dist/esm/util/Observable.js.map +2 -2
  55. package/package.json +2 -2
  56. package/src/codec/DnsCodec.ts +1 -1
  57. package/src/log/Logger.ts +26 -11
  58. package/src/time/Time.ts +4 -0
  59. package/src/transaction/Participant.ts +3 -3
  60. package/src/transaction/Transaction.ts +1 -1
  61. package/src/transaction/Tx.ts +3 -3
  62. package/src/util/Construction.ts +2 -2
  63. package/src/util/DataReadQueue.ts +7 -0
  64. package/src/util/FormattedText.ts +4 -2
  65. package/src/util/Observable.ts +453 -47
@@ -239,7 +239,7 @@ export function Construction<const T extends Constructable>(
239
239
  let change: Observable<[status: Lifecycle.Status, subject: T]> | undefined;
240
240
 
241
241
  const self: Construction<any> = {
242
- [Symbol.toStringTag]: "AsyncConstruction",
242
+ [Symbol.toStringTag]: "Construction",
243
243
 
244
244
  get error() {
245
245
  return error;
@@ -663,7 +663,7 @@ export function Construction<const T extends Constructable>(
663
663
 
664
664
  function createErrorHandler(name: string) {
665
665
  return (e: any) => {
666
- unhandledError(`Unhandled error in ${self} ${name}:`, e);
666
+ unhandledError(`Unhandled error in ${subject} ${name}:`, e);
667
667
  };
668
668
  }
669
669
  }
@@ -35,6 +35,13 @@ export class DataReadQueue<T> implements Stream<T> {
35
35
  }
36
36
 
37
37
  async write(data: T) {
38
+ this.push(data);
39
+ }
40
+
41
+ /**
42
+ * Same as write but doesn't require the await required to satisfy {@link Stream#write}.
43
+ */
44
+ push(data: T) {
38
45
  if (this.#closed) throw new EndOfStreamError();
39
46
  if (this.#pendingRead !== undefined) {
40
47
  this.#pendingRead.timeoutTimer?.stop();
@@ -144,7 +144,7 @@ function detectStructure(text: string): TextStructure {
144
144
  }
145
145
 
146
146
  function wrapParagraph(input: string, into: string[], wrapWidth: number, padding: number, prefixWidth: number) {
147
- const segments = input.match(/\s+/g);
147
+ const segments = input.split(/\s+/);
148
148
  if (!segments) {
149
149
  return;
150
150
  }
@@ -205,10 +205,12 @@ function wrapParagraph(input: string, into: string[], wrapWidth: number, padding
205
205
 
206
206
  // Add to the line
207
207
  line.push(s);
208
- length += segmentLength;
208
+ line.push(" ");
209
+ length += segmentLength + 1;
209
210
  }
210
211
 
211
212
  // If there is a remaining line, add it
213
+ line.length = line.length - 1; // Remove ending space
212
214
  if (line.length) {
213
215
  addLine();
214
216
  }
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { Time, Timer } from "#time/Time.js";
7
8
  import { ImplementationError } from "../MatterError.js";
8
9
  import { Logger } from "../log/Logger.js";
9
10
  import "../polyfills/disposable.js";
@@ -69,11 +70,38 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
69
70
  isObservedBy(observer: Observer<T, R>): boolean;
70
71
 
71
72
  /**
72
- * This flag indicates whether the observable is asynchronous. Any observable that accepts promise returns may
73
- * be asynchronous but this information is not available at runtime unless you specify here, typically via
74
- * {@link AsyncObservable}.
73
+ * Errors throw by observers will interrupt emitters unless an error handler is installed here and the handler does
74
+ * not rethrow.
75
+ *
76
+ * The only exception to this is if {@link handlePromise} is false and an observer is asynchronous. In this case
77
+ * the emitter cannot be made aware of the exception.
75
78
  */
76
- isAsync?: boolean;
79
+ handleError: ObserverErrorHandler;
80
+
81
+ /**
82
+ * We allow emitters to be async, but we do not want to overburden either the emitter or the observer with promise
83
+ * tracking if the lifetime of the observer is not relevant to the emitter.
84
+ *
85
+ * To facilitate this we allow observables to be configured in one of three promise handling modes:
86
+ *
87
+ * * If you set handlePromise, isAsync is true; the handler is invoked for any observer promise
88
+ *
89
+ * * If isAsync is true but you do not set handlePromise, any observer promise is returned to the emitter which must
90
+ * handle the promise
91
+ *
92
+ * * If isAsync is false, we log observer promise errors but the promise is otherwise untracked
93
+ *
94
+ * If the promiseHandler returns a promise or is true and the emitter returns a promise, the observable will emit to
95
+ * successive observers only after the promise resolves.
96
+ */
97
+ isAsync: boolean;
98
+
99
+ /**
100
+ * A promise handler.
101
+ *
102
+ * If you set {@link isAsync} (either true or false) the promise handler is set by the Observable.
103
+ */
104
+ handlePromise: ObserverPromiseHandler | boolean;
77
105
 
78
106
  /**
79
107
  * Observable supports standard "for await (const value of observable").
@@ -98,9 +126,7 @@ export const observant = Symbol("consider-observed");
98
126
  /**
99
127
  * An {@link Observable} that explicitly supports asynchronous observers.
100
128
  */
101
- export interface AsyncObservable<T extends any[] = any[], R = void> extends Observable<T, MaybePromise<R>> {
102
- isAsync: true;
103
- }
129
+ export interface AsyncObservable<T extends any[] = any[], R = void> extends Observable<T, MaybePromise<R>> {}
104
130
 
105
131
  function defaultErrorHandler(error: Error) {
106
132
  throw error;
@@ -108,22 +134,29 @@ function defaultErrorHandler(error: Error) {
108
134
 
109
135
  export type ObserverErrorHandler = (error: Error, observer: Observer<any[], any>) => void;
110
136
 
137
+ export type ObserverPromiseHandler = (promise: Promise<unknown>, observer: Observer<any[], any>) => unknown;
138
+
111
139
  /**
112
140
  * A concrete {@link Observable} implementation.
113
141
  */
114
142
  export class BasicObservable<T extends any[] = any[], R = void> implements Observable<T, R> {
115
- #errorHandler: ObserverErrorHandler;
143
+ #handleError!: ObserverErrorHandler;
144
+ #isAsync!: boolean;
145
+ #handlePromise!: ObserverPromiseHandler;
116
146
  #observers?: Set<Observer<T, R>>;
117
147
  #once?: Set<Observer<T, R>>;
118
- #isAsync?: boolean;
119
148
 
120
149
  #joinIteration?: () => Promise<Next<T>>;
121
150
  #removeIterator?: () => void;
122
151
  #stopIteration?: () => void;
123
152
 
124
- constructor(errorHandler?: ObserverErrorHandler, isAsync?: boolean) {
125
- this.#errorHandler = errorHandler ?? defaultErrorHandler;
126
- this.#isAsync = isAsync;
153
+ constructor(handleError?: ObserverErrorHandler, asyncConfig?: ObserverPromiseHandler | boolean) {
154
+ this.handleError = handleError ?? defaultErrorHandler;
155
+ if (typeof asyncConfig === "function") {
156
+ this.handlePromise = asyncConfig;
157
+ } else {
158
+ this.isAsync = asyncConfig ?? false;
159
+ }
127
160
  }
128
161
 
129
162
  [Symbol.dispose]() {
@@ -132,12 +165,51 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
132
165
  this.#stopIteration?.();
133
166
  }
134
167
 
168
+ set handleError(handleError: ObserverErrorHandler) {
169
+ this.#handleError = handleError;
170
+ }
171
+
172
+ get handleError() {
173
+ return this.#handleError;
174
+ }
175
+
176
+ set isAsync(isAsync: boolean) {
177
+ this.#isAsync = isAsync;
178
+ if (isAsync) {
179
+ // Promises handled by emitter
180
+ this.#handlePromise = promise => promise;
181
+ } else {
182
+ // We log promise errors but do not otherwise track
183
+ this.#handlePromise = (promise, observer) => {
184
+ promise.catch(error => {
185
+ let identity: string;
186
+ if (observer.name) {
187
+ identity = ` "${observer.name}"`;
188
+ } else {
189
+ identity = "";
190
+ }
191
+
192
+ if (this.#handleError === defaultErrorHandler) {
193
+ logger.error(`Unhandled error in async observer${identity}:`, error);
194
+ } else {
195
+ this.#handleError(error, observer);
196
+ }
197
+ });
198
+ };
199
+ }
200
+ }
201
+
135
202
  get isAsync() {
136
203
  return this.#isAsync;
137
204
  }
138
205
 
139
- set isAsync(isAsync: boolean | undefined) {
140
- this.#isAsync = isAsync;
206
+ set handlePromise(handlePromise: ObserverPromiseHandler) {
207
+ this.isAsync = true;
208
+ this.#handlePromise = handlePromise;
209
+ }
210
+
211
+ get handlePromise() {
212
+ return this.#handlePromise;
141
213
  }
142
214
 
143
215
  get isObserved() {
@@ -185,7 +257,7 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
185
257
  try {
186
258
  result = observer(...payload);
187
259
  } catch (e) {
188
- this.#errorHandler(asError(e), observer);
260
+ this.#handleError(asError(e), observer);
189
261
  }
190
262
 
191
263
  if (this.#once?.has(observer)) {
@@ -198,27 +270,20 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
198
270
  }
199
271
 
200
272
  if (MaybePromise.is(result)) {
201
- if (!this.isAsync) {
202
- let identity: string;
203
- if (observer.name) {
204
- identity = ` "${observer.name}"`;
205
- } else {
206
- identity = "";
207
- }
208
-
209
- result.then(undefined, error =>
210
- logger.error(`Unhandled error in async observer${identity}:`, error),
211
- );
273
+ result = this.#handlePromise(Promise.resolve(result), observer as Observer) as R | undefined;
274
+
275
+ if (MaybePromise.is(result)) {
276
+ return result.then(result => {
277
+ if (result === undefined) {
278
+ return emitNext();
279
+ }
280
+ return result;
281
+ }) as R;
282
+ }
212
283
 
284
+ if (result === undefined) {
213
285
  continue;
214
286
  }
215
-
216
- return result.then(result => {
217
- if (result === undefined) {
218
- return emitNext();
219
- }
220
- return result;
221
- }) as R;
222
287
  }
223
288
 
224
289
  return result;
@@ -318,8 +383,8 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
318
383
 
319
384
  type Next<T> = undefined | { value: T; promise: Promise<Next<T>> };
320
385
 
321
- function constructObservable(errorHandler?: ObserverErrorHandler) {
322
- return new BasicObservable(errorHandler);
386
+ function constructObservable(handleError?: ObserverErrorHandler) {
387
+ return new BasicObservable(handleError);
323
388
  }
324
389
 
325
390
  /**
@@ -330,16 +395,16 @@ export const Observable = constructObservable as unknown as {
330
395
  <T extends any[], R = void>(errorHandler?: ObserverErrorHandler): Observable<T, R>;
331
396
  };
332
397
 
333
- function constructAsyncObservable(errorHandler?: ObserverErrorHandler) {
334
- return new BasicObservable(errorHandler, true);
398
+ function constructAsyncObservable(handleError?: ObserverErrorHandler) {
399
+ return new BasicObservable(handleError, true);
335
400
  }
336
401
 
337
402
  /**
338
403
  * Create an {@link AsyncObservable} that explicitly supports asynchronous results
339
404
  */
340
405
  export const AsyncObservable = constructAsyncObservable as unknown as {
341
- new <T extends any[], R = void>(errorHandler?: ObserverErrorHandler): AsyncObservable<T, R>;
342
- <T extends any[], R = void>(errorHandler?: ObserverErrorHandler): AsyncObservable<T, R>;
406
+ new <T extends any[], R = void>(handleError?: ObserverErrorHandler): AsyncObservable<T, R>;
407
+ <T extends any[], R = void>(handleError?: ObserverErrorHandler): AsyncObservable<T, R>;
343
408
  };
344
409
 
345
410
  function event<E, N extends string>(emitter: E, name: N) {
@@ -357,6 +422,9 @@ function event<E, N extends string>(emitter: E, name: N) {
357
422
  * To maintain type safety, implementers define events as observable child properties.
358
423
  */
359
424
  export class EventEmitter {
425
+ // True private screws up TS types
426
+ private events?: Record<string, Observable | undefined>;
427
+
360
428
  emit<This, N extends EventEmitter.NamesOf<This>>(this: This, name: N, ...payload: EventEmitter.PayloadOf<This, N>) {
361
429
  event(this, name).emit(...payload);
362
430
  }
@@ -377,14 +445,40 @@ export class EventEmitter {
377
445
  event(this, name).off(handler as any);
378
446
  }
379
447
 
448
+ addEvent(name: string, event?: Observable) {
449
+ if (!this.events) {
450
+ this.events = {};
451
+ }
452
+
453
+ this.events[name] = event;
454
+ }
455
+
456
+ getEvent(name: string) {
457
+ if (!this.events || !(name in this.events)) {
458
+ throw new ImplementationError(`No such event ${name}`);
459
+ }
460
+
461
+ return this.events[name] ?? (this.events[name] = Observable());
462
+ }
463
+
464
+ hasEvent(name: string, onlyIfInitialized = false) {
465
+ return this.events && (onlyIfInitialized ? this.events[name] : name in this.events);
466
+ }
467
+
380
468
  get eventNames() {
381
- return Object.keys(this).filter(k => typeof (this as any)[k]?.on === "function");
469
+ return this.events ? Object.keys(this.events) : [];
382
470
  }
383
471
 
384
472
  [Symbol.dispose]() {
385
- for (const name of this.eventNames) {
386
- (this as unknown as Record<string, Observable>)[name][Symbol.dispose]?.();
473
+ if (!this.events) {
474
+ return;
387
475
  }
476
+
477
+ for (const event of Object.values(this.events)) {
478
+ event?.[Symbol.dispose]?.();
479
+ }
480
+
481
+ this.events = undefined;
388
482
  }
389
483
  }
390
484
 
@@ -418,7 +512,7 @@ export namespace EventEmitter {
418
512
  /**
419
513
  * An {@link Observable} that proxies to another {@link Observable}.
420
514
  *
421
- * Emits emitted here instead emit on the target {@link Observable}. Events emitted on the target emit locally via
515
+ * Events emitted here instead emit on the target {@link Observable}. Events emitted on the target emit locally via
422
516
  * a listener installed by the proxy.
423
517
  *
424
518
  * This is useful for managing a subset of {@link Observer}s for an {@link Observable}.
@@ -448,15 +542,15 @@ export class ObservableProxy extends BasicObservable {
448
542
  super[Symbol.dispose]();
449
543
  }
450
544
 
451
- override get isAsync() {
452
- return this.#target.isAsync;
453
- }
454
-
455
545
  override get isObserved(): boolean {
456
546
  return this.#target.isObserved;
457
547
  }
458
548
 
459
549
  override emit: (...payload: any) => any | undefined;
550
+
551
+ protected get target() {
552
+ return this.#target;
553
+ }
460
554
  }
461
555
 
462
556
  /**
@@ -553,3 +647,315 @@ export namespace ObserverGroup {
553
647
  */
554
648
  export type VarArgs<T extends any[]> = T extends [...infer R, infer A] ? [...R, A] : T extends [infer A] ? A : [];
555
649
  }
650
+
651
+ /**
652
+ * An {@link Observable} that emits an algorithmically-reduced number of events.
653
+ */
654
+ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T> implements QuietObservable.State<T> {
655
+ #emitAutomatically = QuietObservable.defaults.emitAutomatically;
656
+ #suppressionEnabled = QuietObservable.defaults.suppressionEnabled;
657
+ #minimumEmitIntervalMs = QuietObservable.defaults.minimumEmitIntervalMs;
658
+ #shouldEmit?: QuietObservable.EmitPredicate<T>;
659
+ #source?: Observable<T>;
660
+ #sink?: Observable<T>;
661
+ #sourceObserver?: Observer<T>;
662
+ #sinkObserver?: Observer<T>;
663
+ #deferredPayload?: T;
664
+ #lastEmitAt?: number;
665
+ #emitTimer?: Timer;
666
+
667
+ constructor(config?: QuietObservable.Configuration<T>) {
668
+ super();
669
+ if (config) {
670
+ this.config = config;
671
+ }
672
+ }
673
+
674
+ get config() {
675
+ return this;
676
+ }
677
+
678
+ set config(config: QuietObservable.Configuration<T>) {
679
+ const { suppressionEnabled, minimumEmitIntervalMs, emitAutomatically } = config;
680
+ if (emitAutomatically !== undefined) {
681
+ this.emitAutomatically = emitAutomatically;
682
+ }
683
+ if (suppressionEnabled !== undefined) {
684
+ this.suppressionEnabled = suppressionEnabled;
685
+ }
686
+ if (minimumEmitIntervalMs !== undefined) {
687
+ this.minimumEmitIntervalMs = minimumEmitIntervalMs;
688
+ }
689
+ if ("shouldEmit" in config) {
690
+ this.shouldEmit = config.shouldEmit;
691
+ }
692
+ if ("source" in config) {
693
+ this.source = config.source;
694
+ }
695
+ if ("sink" in config) {
696
+ this.sink = config.sink;
697
+ }
698
+ if ("handleError" in config) {
699
+ this.handleError = config.handleError ?? defaultErrorHandler;
700
+ }
701
+ if ("handlePromise" in config && config.handlePromise) {
702
+ this.handlePromise = config.handlePromise;
703
+ } else {
704
+ this.isAsync = config.isAsync ?? false;
705
+ }
706
+ }
707
+
708
+ get emitAutomatically() {
709
+ return this.#emitAutomatically;
710
+ }
711
+
712
+ set emitAutomatically(value: boolean) {
713
+ this.#emitAutomatically = value;
714
+ if (value) {
715
+ this.emitSoon();
716
+ } else if (this.#emitTimer) {
717
+ this.#stop();
718
+ }
719
+ }
720
+
721
+ get suppressionEnabled() {
722
+ return this.#suppressionEnabled;
723
+ }
724
+
725
+ set suppressionEnabled(value: boolean) {
726
+ this.#suppressionEnabled = value;
727
+ }
728
+
729
+ get minimumEmitIntervalMs() {
730
+ return this.#minimumEmitIntervalMs;
731
+ }
732
+
733
+ set minimumEmitIntervalMs(value: number) {
734
+ if (this.#minimumEmitIntervalMs === value) {
735
+ return;
736
+ }
737
+ const needStart = this.#emitTimer !== undefined;
738
+ if (needStart) {
739
+ this.#stop();
740
+ }
741
+ this.#minimumEmitIntervalMs = value;
742
+ if (needStart) {
743
+ this.#start();
744
+ }
745
+ }
746
+
747
+ get source() {
748
+ return this.#source;
749
+ }
750
+
751
+ set source(source: Observable<T> | undefined) {
752
+ if (this.#source === source) {
753
+ return;
754
+ }
755
+ if (this.#source && this.#sourceObserver) {
756
+ this.#source.off(this.#sourceObserver);
757
+ } else if (this.#sourceObserver === undefined) {
758
+ this.#sourceObserver = (...payload) => this.emit(...payload);
759
+ }
760
+ this.#source = source;
761
+ if (source) {
762
+ source.on(this.#sourceObserver);
763
+ }
764
+ }
765
+
766
+ get sink() {
767
+ return this.#sink;
768
+ }
769
+
770
+ set sink(sink: Observable<T> | undefined) {
771
+ if (this.#sink === sink) {
772
+ return;
773
+ }
774
+ if (this.#sink && this.#sinkObserver) {
775
+ this.off(this.#sinkObserver);
776
+ }
777
+ this.#sink = sink;
778
+ if (sink) {
779
+ this.#sinkObserver = (...payload) => sink.emit(...payload);
780
+ this.#sinkObserver[observant] = false;
781
+ this.on(this.#sinkObserver);
782
+ }
783
+ }
784
+
785
+ get shouldEmit() {
786
+ return this.#shouldEmit;
787
+ }
788
+
789
+ set shouldEmit(shouldEmit: QuietObservable.EmitPredicate<T> | undefined) {
790
+ this.#shouldEmit = shouldEmit;
791
+ if (this.#deferredPayload && shouldEmit?.(...this.#deferredPayload) === false) {
792
+ this.#deferredPayload = undefined;
793
+ this.#stop();
794
+ }
795
+ }
796
+
797
+ override get isObserved() {
798
+ return super.isObserved || this.#sink?.isObserved || false;
799
+ }
800
+
801
+ override isObservedBy(observer: Observer<T>): boolean {
802
+ return this.#sink?.isObservedBy(observer) || this.isObservedBy(observer) || false;
803
+ }
804
+
805
+ override emit(...payload: T) {
806
+ const shouldEmit = this.#shouldEmit?.(...payload);
807
+ if (shouldEmit === false) {
808
+ return;
809
+ }
810
+ const immediate = shouldEmit === "now";
811
+ if (!immediate && !this.#emitAutomatically) {
812
+ this.#deferredPayload = payload;
813
+ return;
814
+ }
815
+ const now = Time.nowMs();
816
+ if (
817
+ immediate ||
818
+ !this.#suppressionEnabled ||
819
+ this.#lastEmitAt === undefined ||
820
+ this.#lastEmitAt + this.#minimumEmitIntervalMs < now
821
+ ) {
822
+ return this.#emit(payload, now);
823
+ }
824
+ this.#deferredPayload = payload;
825
+ this.#start(now);
826
+ }
827
+
828
+ /**
829
+ * Emit immediately, regardless of suppression configuration.
830
+ */
831
+ emitNow() {
832
+ this.#stop();
833
+ if (this.#deferredPayload) {
834
+ this.#emit(this.#deferredPayload);
835
+ this.#deferredPayload = undefined;
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Emit as soon as allowed by suppression.
841
+ */
842
+ emitSoon() {
843
+ if (this.#deferredPayload && this.#emitTimer === undefined) {
844
+ this.#start();
845
+ }
846
+ }
847
+
848
+ override [Symbol.dispose]() {
849
+ this.#stop();
850
+ }
851
+
852
+ #emit(payload: T, now?: number) {
853
+ this.#deferredPayload = undefined;
854
+ this.#lastEmitAt = now ?? Time.nowMs();
855
+ this.#stop();
856
+ super.emit(...payload);
857
+ }
858
+
859
+ #start(now?: number) {
860
+ if (this.#emitTimer || this.#deferredPayload === undefined) {
861
+ return;
862
+ }
863
+
864
+ let timeout;
865
+ if (this.#lastEmitAt === undefined) {
866
+ timeout = 0;
867
+ } else {
868
+ timeout = this.#minimumEmitIntervalMs - ((now ?? Time.nowMs()) - this.#lastEmitAt);
869
+ }
870
+
871
+ if (timeout <= 0) {
872
+ this.emitNow();
873
+ } else {
874
+ this.#emitTimer = Time.getTimer("delayed emit", timeout, this.emitNow.bind(this));
875
+ this.#emitTimer.start();
876
+ }
877
+ }
878
+
879
+ #stop() {
880
+ if (this.#emitTimer) {
881
+ this.#emitTimer.stop();
882
+ this.#emitTimer = undefined;
883
+ }
884
+ }
885
+ }
886
+
887
+ export namespace QuietObservable {
888
+ export interface State<T extends any[] = any[]> {
889
+ /**
890
+ * If true this observable will emit within the suppression constraints. If false it will only emit after calls
891
+ * to {@link emitSoon} or {@link emitNow}.
892
+ */
893
+ emitAutomatically: boolean;
894
+
895
+ /**
896
+ * If true then emit rate is constrained. If false emits will occur immediately.
897
+ */
898
+ suppressionEnabled: boolean;
899
+
900
+ /**
901
+ * The minimum time between emits in milliseconds.
902
+ */
903
+ minimumEmitIntervalMs: number;
904
+
905
+ /**
906
+ * An input observable this observable will automatically observe to produce events.
907
+ */
908
+ source?: Observable<T>;
909
+
910
+ /**
911
+ * An output observable this observable will automatically emit to whenever it emits.
912
+ */
913
+ sink?: Observable<T>;
914
+
915
+ /**
916
+ * A predicate that determine whether a payload should emit.
917
+ */
918
+ shouldEmit?: EmitPredicate<T>;
919
+
920
+ /**
921
+ * Handler for errors returned by observers.
922
+ */
923
+ handleError?: ObserverErrorHandler;
924
+
925
+ /**
926
+ * Designates async support (overridden if you supply {@link handlePromise}).
927
+ */
928
+ isAsync?: boolean;
929
+
930
+ /**
931
+ * Handler for promises returned by observers.
932
+ */
933
+ handlePromise?: ObserverPromiseHandler;
934
+ }
935
+
936
+ /**
937
+ * An emit predicate may emit this value to force immediate emit.
938
+ */
939
+ export const now = "now";
940
+
941
+ /**
942
+ * The return value of an emit predicate. "true" allows the event to emit as normal, "false" prevents the event
943
+ * from emitting, and {@link now} forces immediate emit regardless of interval configuration.
944
+ */
945
+ export type EmitDirective = true | false | typeof now;
946
+
947
+ /**
948
+ * A predicate that may filter emits manually.
949
+ */
950
+ export interface EmitPredicate<T extends any[] = any[]> {
951
+ (...payload: T): EmitDirective;
952
+ }
953
+
954
+ export interface Configuration<T extends any[] = any[]> extends Partial<State<T>> {}
955
+
956
+ export const defaults: State = {
957
+ emitAutomatically: true,
958
+ suppressionEnabled: true,
959
+ minimumEmitIntervalMs: 1000,
960
+ };
961
+ }