@matter/general 0.16.1 → 0.16.2-alpha.0-20260114-b38c6b796

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 (144) hide show
  1. package/dist/cjs/net/Network.d.ts +2 -0
  2. package/dist/cjs/net/Network.d.ts.map +1 -1
  3. package/dist/cjs/net/Network.js +3 -0
  4. package/dist/cjs/net/Network.js.map +1 -1
  5. package/dist/cjs/net/RetrySchedule.d.ts +1 -1
  6. package/dist/cjs/net/ServerAddress.d.ts +52 -14
  7. package/dist/cjs/net/ServerAddress.d.ts.map +1 -1
  8. package/dist/cjs/net/ServerAddress.js +41 -6
  9. package/dist/cjs/net/ServerAddress.js.map +2 -2
  10. package/dist/cjs/net/ServerAddressSet.d.ts +65 -0
  11. package/dist/cjs/net/ServerAddressSet.d.ts.map +1 -0
  12. package/dist/cjs/net/ServerAddressSet.js +144 -0
  13. package/dist/cjs/net/ServerAddressSet.js.map +6 -0
  14. package/dist/cjs/net/dns-sd/MdnsSocket.d.ts +40 -0
  15. package/dist/cjs/net/dns-sd/MdnsSocket.d.ts.map +1 -0
  16. package/dist/cjs/net/dns-sd/MdnsSocket.js +164 -0
  17. package/dist/cjs/net/dns-sd/MdnsSocket.js.map +6 -0
  18. package/dist/cjs/net/dns-sd/index.d.ts +7 -0
  19. package/dist/cjs/net/dns-sd/index.d.ts.map +1 -0
  20. package/dist/cjs/net/dns-sd/index.js +24 -0
  21. package/dist/cjs/net/dns-sd/index.js.map +6 -0
  22. package/dist/cjs/net/index.d.ts +2 -0
  23. package/dist/cjs/net/index.d.ts.map +1 -1
  24. package/dist/cjs/net/index.js +2 -0
  25. package/dist/cjs/net/index.js.map +1 -1
  26. package/dist/cjs/time/TimeUnit.d.ts +4 -0
  27. package/dist/cjs/time/TimeUnit.d.ts.map +1 -1
  28. package/dist/cjs/time/TimeUnit.js +2 -0
  29. package/dist/cjs/time/TimeUnit.js.map +1 -1
  30. package/dist/cjs/time/Timestamp.d.ts +1 -1
  31. package/dist/cjs/time/Timestamp.d.ts.map +1 -1
  32. package/dist/cjs/time/Timestamp.js +1 -1
  33. package/dist/cjs/time/Timestamp.js.map +1 -1
  34. package/dist/cjs/util/Abort.d.ts +9 -0
  35. package/dist/cjs/util/Abort.d.ts.map +1 -1
  36. package/dist/cjs/util/Abort.js +20 -0
  37. package/dist/cjs/util/Abort.js.map +1 -1
  38. package/dist/cjs/util/Heap.d.ts +84 -0
  39. package/dist/cjs/util/Heap.d.ts.map +1 -0
  40. package/dist/cjs/util/Heap.js +286 -0
  41. package/dist/cjs/util/Heap.js.map +6 -0
  42. package/dist/cjs/util/Observable.d.ts +29 -6
  43. package/dist/cjs/util/Observable.d.ts.map +1 -1
  44. package/dist/cjs/util/Observable.js +40 -6
  45. package/dist/cjs/util/Observable.js.map +1 -1
  46. package/dist/cjs/util/Promises.d.ts +3 -0
  47. package/dist/cjs/util/Promises.d.ts.map +1 -1
  48. package/dist/cjs/util/Promises.js +33 -3
  49. package/dist/cjs/util/Promises.js.map +2 -2
  50. package/dist/cjs/util/Scheduler.d.ts +68 -0
  51. package/dist/cjs/util/Scheduler.d.ts.map +1 -0
  52. package/dist/cjs/util/Scheduler.js +207 -0
  53. package/dist/cjs/util/Scheduler.js.map +6 -0
  54. package/dist/cjs/util/Semaphore.d.ts +0 -4
  55. package/dist/cjs/util/Semaphore.d.ts.map +1 -1
  56. package/dist/cjs/util/Semaphore.js.map +1 -1
  57. package/dist/cjs/util/Set.d.ts.map +1 -1
  58. package/dist/cjs/util/Set.js +14 -8
  59. package/dist/cjs/util/Set.js.map +1 -1
  60. package/dist/cjs/util/index.d.ts +2 -0
  61. package/dist/cjs/util/index.d.ts.map +1 -1
  62. package/dist/cjs/util/index.js +2 -0
  63. package/dist/cjs/util/index.js.map +1 -1
  64. package/dist/esm/net/Network.d.ts +2 -0
  65. package/dist/esm/net/Network.d.ts.map +1 -1
  66. package/dist/esm/net/Network.js +3 -0
  67. package/dist/esm/net/Network.js.map +1 -1
  68. package/dist/esm/net/RetrySchedule.d.ts +1 -1
  69. package/dist/esm/net/ServerAddress.d.ts +52 -14
  70. package/dist/esm/net/ServerAddress.d.ts.map +1 -1
  71. package/dist/esm/net/ServerAddress.js +41 -6
  72. package/dist/esm/net/ServerAddress.js.map +2 -2
  73. package/dist/esm/net/ServerAddressSet.d.ts +65 -0
  74. package/dist/esm/net/ServerAddressSet.d.ts.map +1 -0
  75. package/dist/esm/net/ServerAddressSet.js +124 -0
  76. package/dist/esm/net/ServerAddressSet.js.map +6 -0
  77. package/dist/esm/net/dns-sd/MdnsSocket.d.ts +40 -0
  78. package/dist/esm/net/dns-sd/MdnsSocket.d.ts.map +1 -0
  79. package/dist/esm/net/dns-sd/MdnsSocket.js +149 -0
  80. package/dist/esm/net/dns-sd/MdnsSocket.js.map +6 -0
  81. package/dist/esm/net/dns-sd/index.d.ts +7 -0
  82. package/dist/esm/net/dns-sd/index.d.ts.map +1 -0
  83. package/dist/esm/net/dns-sd/index.js +7 -0
  84. package/dist/esm/net/dns-sd/index.js.map +6 -0
  85. package/dist/esm/net/index.d.ts +2 -0
  86. package/dist/esm/net/index.d.ts.map +1 -1
  87. package/dist/esm/net/index.js +2 -0
  88. package/dist/esm/net/index.js.map +1 -1
  89. package/dist/esm/time/TimeUnit.d.ts +4 -0
  90. package/dist/esm/time/TimeUnit.d.ts.map +1 -1
  91. package/dist/esm/time/TimeUnit.js +2 -0
  92. package/dist/esm/time/TimeUnit.js.map +1 -1
  93. package/dist/esm/time/Timestamp.d.ts +1 -1
  94. package/dist/esm/time/Timestamp.d.ts.map +1 -1
  95. package/dist/esm/time/Timestamp.js +1 -1
  96. package/dist/esm/time/Timestamp.js.map +1 -1
  97. package/dist/esm/util/Abort.d.ts +9 -0
  98. package/dist/esm/util/Abort.d.ts.map +1 -1
  99. package/dist/esm/util/Abort.js +20 -0
  100. package/dist/esm/util/Abort.js.map +1 -1
  101. package/dist/esm/util/Heap.d.ts +84 -0
  102. package/dist/esm/util/Heap.d.ts.map +1 -0
  103. package/dist/esm/util/Heap.js +266 -0
  104. package/dist/esm/util/Heap.js.map +6 -0
  105. package/dist/esm/util/Observable.d.ts +29 -6
  106. package/dist/esm/util/Observable.d.ts.map +1 -1
  107. package/dist/esm/util/Observable.js +40 -6
  108. package/dist/esm/util/Observable.js.map +1 -1
  109. package/dist/esm/util/Promises.d.ts +3 -0
  110. package/dist/esm/util/Promises.d.ts.map +1 -1
  111. package/dist/esm/util/Promises.js +33 -3
  112. package/dist/esm/util/Promises.js.map +2 -2
  113. package/dist/esm/util/Scheduler.d.ts +68 -0
  114. package/dist/esm/util/Scheduler.d.ts.map +1 -0
  115. package/dist/esm/util/Scheduler.js +187 -0
  116. package/dist/esm/util/Scheduler.js.map +6 -0
  117. package/dist/esm/util/Semaphore.d.ts +0 -4
  118. package/dist/esm/util/Semaphore.d.ts.map +1 -1
  119. package/dist/esm/util/Semaphore.js.map +1 -1
  120. package/dist/esm/util/Set.d.ts.map +1 -1
  121. package/dist/esm/util/Set.js +14 -8
  122. package/dist/esm/util/Set.js.map +1 -1
  123. package/dist/esm/util/index.d.ts +2 -0
  124. package/dist/esm/util/index.d.ts.map +1 -1
  125. package/dist/esm/util/index.js +2 -0
  126. package/dist/esm/util/index.js.map +1 -1
  127. package/package.json +2 -2
  128. package/src/net/Network.ts +2 -0
  129. package/src/net/RetrySchedule.ts +1 -1
  130. package/src/net/ServerAddress.ts +93 -19
  131. package/src/net/ServerAddressSet.ts +225 -0
  132. package/src/net/dns-sd/MdnsSocket.ts +203 -0
  133. package/src/net/dns-sd/index.ts +7 -0
  134. package/src/net/index.ts +2 -0
  135. package/src/time/TimeUnit.ts +5 -0
  136. package/src/time/Timestamp.ts +1 -1
  137. package/src/util/Abort.ts +25 -0
  138. package/src/util/Heap.ts +332 -0
  139. package/src/util/Observable.ts +74 -10
  140. package/src/util/Promises.ts +61 -4
  141. package/src/util/Scheduler.ts +185 -0
  142. package/src/util/Semaphore.ts +0 -5
  143. package/src/util/Set.ts +15 -8
  144. package/src/util/index.ts +2 -0
@@ -54,6 +54,16 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
54
54
  */
55
55
  on(observer: Observer<T, R>): void;
56
56
 
57
+ /**
58
+ * Add an observer that may be released via disposal.
59
+ */
60
+ use(observer: Observer<T, R>): Disposable;
61
+
62
+ /**
63
+ * Add a "once" observer that may be released via disposal.
64
+ */
65
+ useOnce(observer: Observer<T, R>): Disposable;
66
+
57
67
  /**
58
68
  * Remove an observer.
59
69
  */
@@ -148,7 +158,13 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
148
158
  */
149
159
  export interface ObservableValue<T extends [any, ...any[]] = [boolean], R extends MaybePromise<void> = void>
150
160
  extends Observable<T, R>, Promise<T[0]> {
161
+ /**
162
+ * The current value.
163
+ *
164
+ * Setting the value will resolve the promise interface but you must use {@link emit} to also emit an event.
165
+ */
151
166
  value: T[0] | undefined;
167
+
152
168
  error?: Error;
153
169
 
154
170
  /**
@@ -166,6 +182,12 @@ export interface ObservableValue<T extends [any, ...any[]] = [boolean], R extend
166
182
  catch<TResult = never>(
167
183
  onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
168
184
  ): Promise<T[0] | TResult>;
185
+
186
+ onError(handler: (cause: Error) => void): void;
187
+
188
+ offError(handler: (cause: Error) => void): void;
189
+
190
+ useError(handler: (cause: Error) => void): Disposable;
169
191
  }
170
192
 
171
193
  /**
@@ -344,7 +366,7 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
344
366
  };
345
367
  }
346
368
 
347
- // Initially emit using a synchronous loop. When we hit the first promies we convert to an async function
369
+ // Initially emit using a synchronous loop. When we hit the first promise we convert to an async function
348
370
  for (; nextObserver < observers.length; nextObserver++) {
349
371
  let result: ReturnType<Observer<T, R>>;
350
372
 
@@ -410,8 +432,27 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
410
432
  this.#observers.add(observer);
411
433
  }
412
434
 
435
+ use(observer: Observer<T, R>) {
436
+ this.on(observer);
437
+ return {
438
+ [Symbol.dispose]: () => {
439
+ this.off(observer);
440
+ },
441
+ };
442
+ }
443
+
444
+ useOnce(observer: Observer<T, R>) {
445
+ this.once(observer);
446
+ return {
447
+ [Symbol.dispose]: () => {
448
+ this.off(observer);
449
+ },
450
+ };
451
+ }
452
+
413
453
  off(observer: Observer<T, R>) {
414
454
  this.#observers?.delete(observer);
455
+ this.#once?.delete(observer);
415
456
  }
416
457
 
417
458
  once(observer: Observer<T, R>) {
@@ -552,9 +593,9 @@ function event<E, N extends string>(emitter: E, name: N) {
552
593
  /**
553
594
  * A concrete {@link ObservableValue} implementation.
554
595
  */
555
- export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
556
- extends BasicObservable<T, void>
557
- implements ObservableValue<T>
596
+ export class BasicObservableValue<T extends [any, ...any[]] = [boolean], R extends MaybePromise<void> = void>
597
+ extends BasicObservable<T, R>
598
+ implements ObservableValue<T, R>
558
599
  {
559
600
  #value: T | undefined;
560
601
  #error?: Error;
@@ -566,14 +607,12 @@ export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
566
607
  constructor(value?: T[0], handleError?: ObserverErrorHandler, asyncConfig?: ObserverPromiseHandler | boolean) {
567
608
  super(handleError, asyncConfig);
568
609
  this.#value = value;
569
- this.on(this.#maybeResolve.bind(this) as unknown as Observer<T, void>);
610
+
611
+ const maybeResolve = this.#maybeResolve.bind(this) as unknown as Observer<T, R>;
612
+ Object.defineProperty(maybeResolve, observant, { value: false });
613
+ this.on(maybeResolve);
570
614
  }
571
615
 
572
- /**
573
- * The current value.
574
- *
575
- * This will resolve the promise interface but you must use {@link emit} to also emit an event..
576
- */
577
616
  get value(): T[0] | undefined {
578
617
  return this.#value;
579
618
  }
@@ -639,6 +678,27 @@ export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
639
678
  return this.then(undefined, onrejected);
640
679
  }
641
680
 
681
+ onError(handler: (cause: Error) => void) {
682
+ if (!this.#awaiters) {
683
+ this.#awaiters = [];
684
+ }
685
+ this.#awaiters?.push({ resolve: undefined, reject: handler });
686
+ }
687
+
688
+ offError(handler: (cause: Error) => void) {
689
+ this.#awaiters = this.#awaiters?.filter(awaiter => awaiter.resolve === undefined && awaiter.reject === handler);
690
+ }
691
+
692
+ useError(handler: (cause: Error) => void) {
693
+ this.onError(handler);
694
+
695
+ return {
696
+ [Symbol.dispose]: () => {
697
+ this.offError(handler);
698
+ },
699
+ };
700
+ }
701
+
642
702
  finally(onfinally?: (() => void) | null): Promise<T> {
643
703
  return Promise.resolve(this).finally(onfinally);
644
704
  }
@@ -924,6 +984,10 @@ export class ObserverGroup {
924
984
  this.#observers.clear();
925
985
  this.#boundObservers.clear();
926
986
  }
987
+
988
+ [Symbol.dispose]() {
989
+ this.close();
990
+ }
927
991
  }
928
992
 
929
993
  /**
@@ -10,6 +10,13 @@ import { Duration } from "#time/Duration.js";
10
10
  import { asError } from "#util/Error.js";
11
11
  import { InternalError, TimeoutError } from "../MatterError.js";
12
12
  import { Time } from "../time/Time.js";
13
+ import type {
14
+ AsyncObservable,
15
+ AsyncObservableValue,
16
+ AsyncObserver,
17
+ Observable,
18
+ ObservableValue,
19
+ } from "./Observable.js";
13
20
 
14
21
  /**
15
22
  * Obtain a promise with functions to resolve and reject.
@@ -413,10 +420,14 @@ export namespace SafePromise {
413
420
  * https://github.com/nodejs/node/issues/17469#issuecomment-685216777
414
421
  *
415
422
  * ...although this isn't an issue specific to Node.
423
+ *
424
+ * We specialize support for {@link Observable} and {@link ObservableValue}. Those contracts are awaitable like a
425
+ * {@link Promise} but we instead register listeners directly so we can unregister using {@link Observable#off}.
416
426
  */
417
427
  export function race<T>(values: Iterable<T>): Promise<Awaited<T>> {
418
428
  let listener!: SettlementListener;
419
429
  let registered: undefined | Set<SettlementListener>[];
430
+ let disposables: undefined | Disposable[];
420
431
 
421
432
  let race = new Promise<Awaited<T>>((resolve, reject) => {
422
433
  listener = { resolve, reject };
@@ -428,6 +439,44 @@ export namespace SafePromise {
428
439
  continue;
429
440
  }
430
441
 
442
+ // If this is an Observable, use on/off so we can reliably unregister listeners
443
+ if (
444
+ "use" in value &&
445
+ "off" in value &&
446
+ typeof value.use === "function" &&
447
+ typeof value.off === "function"
448
+ ) {
449
+ // Further specialize for ObservableValue contract, which behaves as resolved when a truthy value is
450
+ // present
451
+ if ("value" in value && value.value) {
452
+ Promise.resolve(value.value as Awaited<T>).then(resolve, reject);
453
+ continue;
454
+ }
455
+
456
+ if (!disposables) {
457
+ disposables = [];
458
+ }
459
+
460
+ let observer: AsyncObserver<[Awaited<T>]>;
461
+ if ("value" in value) {
462
+ // For observable value, only resolve if value is truthy
463
+ observer = value => {
464
+ if (value) {
465
+ resolve(value);
466
+ }
467
+ };
468
+
469
+ // And handle errors
470
+ disposables.push((value as unknown as AsyncObservableValue).useError(reject));
471
+ } else {
472
+ // Normal observables "resolve" on any emit and do not have an error channel
473
+ observer = resolve;
474
+ }
475
+ disposables.push((value as unknown as AsyncObservable<[Awaited<T>]>).use(observer));
476
+
477
+ continue;
478
+ }
479
+
431
480
  // We only use Promise#then once per promise and dispatch to a set of listeners from there
432
481
  const settlement = settlementOf(value);
433
482
  if (settlement.isSettled) {
@@ -447,11 +496,19 @@ export namespace SafePromise {
447
496
  }
448
497
  });
449
498
 
450
- // If there were any unsettled promises, unregister our listener when settled
451
- if (registered) {
499
+ // Ensure we unregister listeners when settled
500
+ if (registered || disposables) {
452
501
  race = race.finally(() => {
453
- for (const listeners of registered!) {
454
- listeners.delete(listener);
502
+ if (registered) {
503
+ for (const listeners of registered) {
504
+ listeners.delete(listener);
505
+ }
506
+ }
507
+
508
+ if (disposables) {
509
+ for (const disposable of disposables) {
510
+ disposable[Symbol.dispose]();
511
+ }
455
512
  }
456
513
  });
457
514
  }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2026 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Logger } from "#log/Logger.js";
8
+ import { ImplementationError } from "#MatterError.js";
9
+ import { Time } from "#time/Time.js";
10
+ import { Timestamp } from "#time/Timestamp.js";
11
+ import { Abort } from "./Abort.js";
12
+ import { asError } from "./Error.js";
13
+ import { Heap } from "./Heap.js";
14
+ import { Lifetime } from "./Lifetime.js";
15
+ import { BasicMultiplex } from "./Multiplex.js";
16
+ import { MaybePromise } from "./Promises.js";
17
+ import { Semaphore } from "./Semaphore.js";
18
+
19
+ const logger = Logger.get("Scheduler");
20
+
21
+ /**
22
+ * Task scheduler.
23
+ *
24
+ * Runs workers at a designated time.
25
+ */
26
+ export class Scheduler<T> {
27
+ #name: string;
28
+ #lifetime: Lifetime;
29
+ #timeOf: Scheduler.Configuration<T>["timeOf"];
30
+ #run: Scheduler.Configuration<T>["run"];
31
+ #queue: Heap<T>;
32
+ #abort = new Abort();
33
+ #workers = new BasicMultiplex();
34
+ #semaphore?: Semaphore;
35
+
36
+ constructor({
37
+ name = "scheduler",
38
+ lifetime = Lifetime.process,
39
+ timeOf,
40
+ run,
41
+ semaphore,
42
+ }: Scheduler.Configuration<T>) {
43
+ this.#name = name;
44
+ this.#lifetime = lifetime.join(this.#name);
45
+ this.#timeOf = timeOf;
46
+ this.#run = run;
47
+ this.#semaphore = semaphore;
48
+
49
+ this.#queue = new Heap(comparatorFor(timeOf));
50
+ this.#workers.add(this.#schedule());
51
+ }
52
+
53
+ /**
54
+ * Schedule a worker.
55
+ */
56
+ add(worker: T) {
57
+ if (this.#lifetime.isClosing) {
58
+ throw new ImplementationError(`Cannot schedule new work because ${this.#name} is closed`);
59
+ }
60
+ this.#queue.add(worker);
61
+ }
62
+
63
+ /**
64
+ * Unschedule a worker.
65
+ */
66
+ delete(worker: T) {
67
+ this.#queue.delete(worker);
68
+ }
69
+
70
+ /**
71
+ * Update a worker's scheduled run time.
72
+ */
73
+ reschedule(worker: T) {
74
+ this.#queue.delete(worker);
75
+ this.#queue.add(worker);
76
+ }
77
+
78
+ /**
79
+ * Close.
80
+ *
81
+ * Stops accepting workers. Returns when all workers are complete.
82
+ */
83
+ async close() {
84
+ using _closing = this.#lifetime.closing();
85
+ this.#abort();
86
+ await this.#workers;
87
+ }
88
+
89
+ /**
90
+ * Performs actual scheduling of workers.
91
+ */
92
+ async #schedule() {
93
+ while (!this.#abort.aborted) {
94
+ // Activate workers that have no more delay
95
+ const now = Time.nowMs;
96
+ let first = this.#queue.first;
97
+ let nextAt = first ? this.#timeOf(first) : undefined;
98
+ while (nextAt !== undefined && nextAt <= now) {
99
+ if (this.#abort.aborted) {
100
+ return;
101
+ }
102
+
103
+ let slot: undefined | Disposable;
104
+ if (this.#semaphore) {
105
+ slot = await this.#semaphore.obtainSlot(this.#abort);
106
+ if (this.#abort.aborted) {
107
+ return;
108
+ }
109
+ }
110
+
111
+ try {
112
+ this.#queue.shift();
113
+ const promise = this.#run(first!, this.#abort.signal);
114
+ if (promise) {
115
+ let p = Promise.resolve(promise).catch(this.#unhandled.bind(this));
116
+ if (slot) {
117
+ p = p.finally(() => slot[Symbol.dispose]());
118
+ }
119
+ this.#workers.add(p);
120
+ } else {
121
+ slot?.[Symbol.dispose]();
122
+ }
123
+ } catch (e) {
124
+ slot?.[Symbol.dispose]();
125
+ this.#unhandled(e);
126
+ }
127
+
128
+ first = this.#queue.first;
129
+ nextAt = first ? this.#timeOf(first) : undefined;
130
+ }
131
+
132
+ // Wait for delay for next worker, abort or change in head of queue
133
+ using abort = new Abort({
134
+ abort: this.#abort,
135
+ timeout: nextAt === undefined ? undefined : Timestamp.delta(now, nextAt),
136
+ });
137
+ using _abortOnChange = this.#queue.firstChanged.use(newFirst => abort.if(newFirst !== first));
138
+ await abort;
139
+ }
140
+ }
141
+
142
+ #unhandled(e: unknown) {
143
+ logger.error(`Unhandled error in ${this.#name} worker:`, asError(e));
144
+ }
145
+ }
146
+
147
+ export namespace Scheduler {
148
+ export interface Configuration<T> {
149
+ /**
150
+ * Name used for diagnostics.
151
+ *
152
+ * Defaults to "scheduler".
153
+ */
154
+ name?: string;
155
+
156
+ /**
157
+ * Owner lifetime.
158
+ *
159
+ * Defaults to process.
160
+ */
161
+ lifetime?: Lifetime.Owner;
162
+
163
+ /**
164
+ * Identify the scheduled time for a specific worker.
165
+ */
166
+ timeOf(worker: T): Timestamp;
167
+
168
+ /**
169
+ * Execute a worker.
170
+ */
171
+ run(worker: T, abort: AbortSignal): MaybePromise<void>;
172
+
173
+ /**
174
+ * If present, controls worker concurrency.
175
+ *
176
+ * Providing the semaphore here rather than using it within the worker prevents the worker from starting when no
177
+ * queue slot is available.
178
+ */
179
+ semaphore?: Semaphore;
180
+ }
181
+ }
182
+
183
+ function comparatorFor<T>(timeOf: Scheduler.Configuration<T>["timeOf"]) {
184
+ return (a: T, b: T) => timeOf(a) - timeOf(b);
185
+ }
@@ -23,11 +23,6 @@ export interface WorkSlot extends Disposable {
23
23
  * This is called automatically when using `using` syntax.
24
24
  */
25
25
  close(): void;
26
-
27
- /**
28
- * Release the slot automatically when the object is garbage collected.
29
- */
30
- [Symbol.dispose](): void;
31
26
  }
32
27
 
33
28
  /**
package/src/util/Set.ts CHANGED
@@ -61,7 +61,7 @@ export interface IndexedSet<T> {
61
61
  * Unused features have minimal performance impact.
62
62
  */
63
63
  export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, AddT>, ObservableSet<T>, IndexedSet<T> {
64
- #entries = new Set<T>();
64
+ #entries?: Set<T>;
65
65
  #added?: Observable<[T]>;
66
66
  #deleted?: Observable<[T]>;
67
67
  #empty?: ObservableValue;
@@ -79,11 +79,11 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
79
79
  }
80
80
 
81
81
  [Symbol.iterator]() {
82
- return this.#entries[Symbol.iterator]();
82
+ return this.#definedEntries[Symbol.iterator]();
83
83
  }
84
84
 
85
85
  get size() {
86
- return this.#entries.size;
86
+ return this.#entries?.size ?? 0;
87
87
  }
88
88
 
89
89
  map<R>(mapper: (item: T) => R) {
@@ -109,17 +109,17 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
109
109
  }
110
110
 
111
111
  has(item: T) {
112
- return this.#entries.has(item);
112
+ return this.#entries?.has(item) ?? false;
113
113
  }
114
114
 
115
115
  add(item: AddT) {
116
116
  const created = this.create(item);
117
117
 
118
- if (this.#entries.has(item as any)) {
118
+ if (this.#definedEntries.has(item as any)) {
119
119
  return;
120
120
  }
121
121
 
122
- this.#entries.add(item as any);
122
+ this.#definedEntries.add(item as any);
123
123
 
124
124
  if (this.#indices) {
125
125
  for (const field in this.#indices) {
@@ -148,6 +148,13 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
148
148
  return this.#indexOf(field).get(value);
149
149
  }
150
150
 
151
+ get #definedEntries() {
152
+ if (this.#entries === undefined) {
153
+ this.#entries = new Set();
154
+ }
155
+ return this.#entries;
156
+ }
157
+
151
158
  #indexOf<F extends keyof T>(field: F) {
152
159
  if (!this.#indices) {
153
160
  this.#indices = {};
@@ -197,7 +204,7 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
197
204
  }
198
205
  }
199
206
 
200
- if (!this.#entries.delete(item)) {
207
+ if (!this.#entries?.delete(item)) {
201
208
  return false;
202
209
  }
203
210
 
@@ -246,7 +253,7 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
246
253
 
247
254
  get empty() {
248
255
  if (this.#empty === undefined) {
249
- this.#empty = ObservableValue(!this.#entries.size);
256
+ this.#empty = ObservableValue(!this.#entries?.size);
250
257
  }
251
258
  return this.#empty;
252
259
  }
package/src/util/index.ts CHANGED
@@ -23,6 +23,7 @@ export * from "./FormattedText.js";
23
23
  export * from "./Function.js";
24
24
  export * from "./GeneratedClass.js";
25
25
  export * from "./Github.js";
26
+ export * from "./Heap.js";
26
27
  export * from "./identifier-case.js";
27
28
  export * from "./Ip.js";
28
29
  export * from "./Lifecycle.js";
@@ -35,6 +36,7 @@ export * from "./NamedHandler.js";
35
36
  export * from "./Number.js";
36
37
  export * from "./Observable.js";
37
38
  export * from "./Promises.js";
39
+ export * from "./Scheduler.js";
38
40
  export * from "./Semaphore.js";
39
41
  export * from "./serialize.js";
40
42
  export * from "./Set.js";