@dxos/async 0.6.13 → 0.6.14-main.1366248

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.
@@ -0,0 +1,1343 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+
3
+ // packages/common/async/src/callback.ts
4
+ var createPromiseFromCallback = (run) => new Promise((resolve, reject) => {
5
+ run((error, value) => {
6
+ if (error) {
7
+ reject(error);
8
+ } else {
9
+ resolve(value);
10
+ }
11
+ });
12
+ });
13
+
14
+ // packages/common/async/src/chain.ts
15
+ var asyncChain = (chain) => async (elements) => {
16
+ let result = await elements;
17
+ for (const part of chain) {
18
+ result = await Promise.all(result.map(async (element) => await part(element)));
19
+ }
20
+ return result;
21
+ };
22
+
23
+ // packages/common/async/src/debounce.ts
24
+ var debounce = (cb, wait = 100) => {
25
+ let t;
26
+ return (...args) => {
27
+ clearTimeout(t);
28
+ t = setTimeout(() => cb(...args), wait);
29
+ };
30
+ };
31
+
32
+ // packages/common/async/src/errors.ts
33
+ var toError = (err) => err === void 0 || typeof err === "string" ? new Error(err) : err;
34
+ var TimeoutError = class extends Error {
35
+ constructor(timeout, label) {
36
+ super(timeout ? `Timeout [${timeout.toLocaleString()}ms]${label === void 0 ? "" : `: ${label}`}` : "Timeout");
37
+ }
38
+ };
39
+ var observableError = (observable, err) => {
40
+ if (err instanceof TimeoutError) {
41
+ observable.callback.onTimeout?.(err);
42
+ } else {
43
+ observable.callback.onError(toError(err));
44
+ }
45
+ };
46
+
47
+ // packages/common/async/src/timeout.ts
48
+ import { ContextDisposedError } from "@dxos/context";
49
+ var sleep = (ms) => {
50
+ return new Promise((resolve) => {
51
+ const finish = Date.now() + ms;
52
+ const sleeper = () => {
53
+ const delta = finish - Date.now();
54
+ if (delta > 0) {
55
+ setTimeout(sleeper, delta);
56
+ } else {
57
+ resolve();
58
+ }
59
+ };
60
+ sleeper();
61
+ });
62
+ };
63
+ var asyncReturn = () => sleep(0);
64
+ var asyncTimeout = async (promise, timeout, err) => {
65
+ let timeoutId;
66
+ const throwable = err === void 0 || typeof err === "string" ? new TimeoutError(timeout, err) : err;
67
+ const timeoutPromise = new Promise((resolve, reject) => {
68
+ timeoutId = setTimeout(() => {
69
+ reject(throwable);
70
+ }, timeout);
71
+ unrefTimeout(timeoutId);
72
+ });
73
+ const conditionTimeout = typeof promise === "function" ? createPromiseFromCallback(promise) : promise;
74
+ return await Promise.race([
75
+ conditionTimeout,
76
+ timeoutPromise
77
+ ]).finally(() => {
78
+ clearTimeout(timeoutId);
79
+ });
80
+ };
81
+ var unrefTimeout = (timeoutId) => {
82
+ if (typeof timeoutId === "object" && "unref" in timeoutId) {
83
+ timeoutId.unref();
84
+ }
85
+ };
86
+ var sleepWithContext = (ctx, ms) => {
87
+ const error = new ContextDisposedError();
88
+ return new Promise((resolve, reject) => {
89
+ if (ctx.disposed) {
90
+ reject(error);
91
+ return;
92
+ }
93
+ const timeout = setTimeout(() => {
94
+ clearDispose();
95
+ resolve();
96
+ }, ms);
97
+ const clearDispose = ctx.onDispose(() => {
98
+ clearTimeout(timeout);
99
+ reject(error);
100
+ });
101
+ });
102
+ };
103
+
104
+ // packages/common/async/src/event-emitter.ts
105
+ var onEvent = (eventEmitter, eventName, callback) => {
106
+ eventEmitter.on(eventName, callback);
107
+ return () => eventEmitter.off(eventName, callback);
108
+ };
109
+ var addListener = (eventEmitter, eventName, callback) => {
110
+ const off = onEvent(eventEmitter, eventName, callback);
111
+ return {
112
+ remove: () => off()
113
+ };
114
+ };
115
+ var waitForEvent = (eventEmitter, eventName, test, timeout, error) => {
116
+ let off;
117
+ const promise = new Promise((resolve) => {
118
+ off = onEvent(eventEmitter, eventName, (...args) => {
119
+ if (!test || test(...args)) {
120
+ resolve(...args);
121
+ }
122
+ });
123
+ });
124
+ return timeout ? asyncTimeout(promise, timeout, error ?? new Error()).finally(off) : promise.finally(off);
125
+ };
126
+
127
+ // packages/common/async/src/events.ts
128
+ import { Context } from "@dxos/context";
129
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/async/src/events.ts";
130
+ var EventSubscriptions = class {
131
+ constructor() {
132
+ this._listeners = [];
133
+ }
134
+ add(cb) {
135
+ this._listeners.push(cb);
136
+ }
137
+ clear() {
138
+ this._listeners.forEach((cb) => cb());
139
+ this._listeners.length = 0;
140
+ }
141
+ };
142
+ var Event = class _Event {
143
+ constructor() {
144
+ this._listeners = /* @__PURE__ */ new Set();
145
+ this._effects = /* @__PURE__ */ new Set();
146
+ }
147
+ /**
148
+ * Wrap objects that have on/off style event emitters.
149
+ */
150
+ static wrap(emitter, eventName) {
151
+ const event = new _Event();
152
+ event.addEffect(() => {
153
+ const onEvent2 = (data) => event.emit(data);
154
+ emitter.on(eventName, onEvent2);
155
+ return () => emitter.off(eventName, onEvent2);
156
+ });
157
+ return event;
158
+ }
159
+ /**
160
+ * Emit an event.
161
+ * In most cases should only be called by the class or entity containing the event.
162
+ * All listeners are called in order of subscription with persistent ones first.
163
+ * Listeners are called synchronously in the same stack.
164
+ * A thrown exception in the listener will stop the event from being emitted to the rest of the listeners.
165
+ *
166
+ * @param data param that will be passed to all listeners.
167
+ */
168
+ emit(data) {
169
+ for (const listener of this._listeners) {
170
+ void listener.trigger(data);
171
+ if (listener.once) {
172
+ this._listeners.delete(listener);
173
+ }
174
+ }
175
+ }
176
+ on(_ctx, _callback, options) {
177
+ const [ctx, callback] = _ctx instanceof Context ? [
178
+ _ctx,
179
+ _callback
180
+ ] : [
181
+ new Context(void 0, {
182
+ F: __dxlog_file,
183
+ L: 124
184
+ }),
185
+ _ctx
186
+ ];
187
+ const weak = !!options?.weak;
188
+ const once = !!options?.once;
189
+ const listener = new EventListener(this, callback, ctx, once, weak);
190
+ this._addListener(listener);
191
+ return () => {
192
+ this._removeListener(listener);
193
+ };
194
+ }
195
+ /**
196
+ * Unsubscribe this callback from new events. Includes persistent and once-listeners.
197
+ * NOTE: It is recommended to use `Event.on`'s return value instead.
198
+ * If the callback is not subscribed this is no-op.
199
+ *
200
+ * @param callback
201
+ */
202
+ off(callback) {
203
+ for (const listener of this._listeners) {
204
+ if (listener.derefCallback() === callback) {
205
+ this._removeListener(listener);
206
+ }
207
+ }
208
+ }
209
+ once(_ctx, _callback) {
210
+ const [ctx, callback] = _ctx instanceof Context ? [
211
+ _ctx,
212
+ _callback
213
+ ] : [
214
+ new Context(void 0, {
215
+ F: __dxlog_file,
216
+ L: 161
217
+ }),
218
+ _ctx
219
+ ];
220
+ const listener = new EventListener(this, callback, ctx, true, false);
221
+ this._addListener(listener);
222
+ return () => {
223
+ this._removeListener(listener);
224
+ };
225
+ }
226
+ /**
227
+ * An async iterator that iterates over events.
228
+ * This iterator runs indefinitely.
229
+ */
230
+ async *[Symbol.asyncIterator]() {
231
+ while (true) {
232
+ yield await new Promise((resolve) => {
233
+ this.once(resolve);
234
+ });
235
+ }
236
+ }
237
+ /**
238
+ * Returns a promise that resolves with the first event emitted that matches the provided predicate.
239
+ *
240
+ * @param predicate
241
+ */
242
+ waitFor(predicate) {
243
+ return new Promise((resolve) => {
244
+ const unsubscribe = this.on((data) => {
245
+ if (predicate(data)) {
246
+ unsubscribe();
247
+ resolve(data);
248
+ }
249
+ });
250
+ });
251
+ }
252
+ /**
253
+ * Returns a promise that resolves once a specific number of events was emitted since this method was called.
254
+ *
255
+ * @param expectedCount
256
+ */
257
+ waitForCount(expectedCount) {
258
+ let count = 0;
259
+ return this.waitFor(() => ++count === expectedCount);
260
+ }
261
+ /**
262
+ * Similar to waitFor, but the promise resolves immediately if the condition is already true.
263
+ */
264
+ // TODO(burdon): Should pass event property to predicate.
265
+ async waitForCondition(predicate) {
266
+ if (!predicate()) {
267
+ await this.waitFor(predicate);
268
+ }
269
+ }
270
+ /**
271
+ * Returns the number of persistent listeners.
272
+ */
273
+ listenerCount() {
274
+ return this._listeners.size;
275
+ }
276
+ /**
277
+ * Add a side effect that will be activated once the event has at least one subscriber.
278
+ * The provided callback can return a function that will be used to clean up after the last subscriber unsubscribes from the event.
279
+ * The API is similar to `useEffect` from React.
280
+ *
281
+ * ## Example:
282
+ * ```typescript
283
+ * event.addEffect(() => {
284
+ * // do stuff
285
+ * return () => {
286
+ * // clean-up
287
+ * };
288
+ * });
289
+ * ```
290
+ *
291
+ * @returns Callback that will remove this effect once called.
292
+ */
293
+ addEffect(effect) {
294
+ const handle = {
295
+ effect,
296
+ cleanup: void 0
297
+ };
298
+ if (this.listenerCount() > 0) {
299
+ handle.cleanup = handle.effect();
300
+ }
301
+ this._effects.add(handle);
302
+ return () => {
303
+ handle.cleanup?.();
304
+ this._effects.delete(handle);
305
+ };
306
+ }
307
+ /**
308
+ * Triggers an event with at least `timeout` milliseconds between each event.
309
+ * If the event is triggered more often, the event is delayed until the timeout is reached.
310
+ * If event is emitted for the first time or event wasn't fired for `timeout` milliseconds,
311
+ * the event is emitted after `timeout / 8` ms.
312
+ */
313
+ // TODO(burdon): Factor out generic function.
314
+ debounce(timeout = 0) {
315
+ let firing;
316
+ let lastFired;
317
+ const debouncedEvent = new _Event();
318
+ debouncedEvent.addEffect(() => {
319
+ const unsubscribe = this.on(() => {
320
+ if (!firing) {
321
+ const fireIn = !lastFired || Date.now() - lastFired > timeout ? timeout / 8 : timeout;
322
+ firing = setTimeout(() => {
323
+ lastFired = Date.now();
324
+ firing = void 0;
325
+ debouncedEvent.emit();
326
+ }, fireIn);
327
+ }
328
+ });
329
+ return () => {
330
+ unsubscribe();
331
+ clearTimeout(firing);
332
+ };
333
+ });
334
+ return debouncedEvent;
335
+ }
336
+ /**
337
+ * Turn any variant of `Event<T>` into an `Event<void>` discarding the callback parameter.
338
+ */
339
+ discardParameter() {
340
+ return this;
341
+ }
342
+ /**
343
+ * Overridden to not return implementation details.
344
+ */
345
+ toJSON() {
346
+ return {
347
+ listenerCount: this.listenerCount()
348
+ };
349
+ }
350
+ _addListener(listener) {
351
+ this._listeners.add(listener);
352
+ if (this.listenerCount() === 1) {
353
+ this._runEffects();
354
+ }
355
+ }
356
+ /**
357
+ * @internal
358
+ */
359
+ _removeListener(listener) {
360
+ this._listeners.delete(listener);
361
+ listener.remove();
362
+ if (this.listenerCount() === 0) {
363
+ this._cleanupEffects();
364
+ }
365
+ }
366
+ _runEffects() {
367
+ for (const handle of this._effects) {
368
+ handle.cleanup = handle.effect();
369
+ }
370
+ }
371
+ _cleanupEffects() {
372
+ for (const handle of this._effects) {
373
+ handle.cleanup?.();
374
+ handle.cleanup = void 0;
375
+ }
376
+ }
377
+ };
378
+ var EventListener = class {
379
+ constructor(event, listener, ctx, once, weak) {
380
+ this.ctx = ctx;
381
+ this.once = once;
382
+ this.weak = weak;
383
+ this._clearDispose = void 0;
384
+ this._clearDispose = ctx.onDispose(() => {
385
+ event._removeListener(this);
386
+ });
387
+ if (weak) {
388
+ this.callback = new WeakRef(listener);
389
+ weakListeners().registry?.register(listener, {
390
+ event: new WeakRef(event),
391
+ listener: this
392
+ }, this);
393
+ } else {
394
+ this.callback = listener;
395
+ }
396
+ }
397
+ derefCallback() {
398
+ return this.weak ? this.callback.deref() : this.callback;
399
+ }
400
+ async trigger(data) {
401
+ try {
402
+ const callback = this.derefCallback();
403
+ await callback?.(data);
404
+ } catch (err) {
405
+ this.ctx.raise(err);
406
+ }
407
+ }
408
+ remove() {
409
+ this._clearDispose?.();
410
+ weakListeners().registry?.unregister(this);
411
+ }
412
+ };
413
+ var weakListenersState = null;
414
+ var FINALIZATION_REGISTRY_SUPPORTED = !!globalThis.FinalizationRegistry;
415
+ var weakListeners = () => {
416
+ if (!FINALIZATION_REGISTRY_SUPPORTED) {
417
+ return {
418
+ registry: void 0
419
+ };
420
+ }
421
+ weakListenersState ??= new FinalizationRegistry(({ event, listener }) => {
422
+ event.deref()?._removeListener(listener);
423
+ });
424
+ return {
425
+ registry: weakListenersState
426
+ };
427
+ };
428
+
429
+ // packages/common/async/src/latch.ts
430
+ import { invariant } from "@dxos/invariant";
431
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/common/async/src/latch.ts";
432
+ var latch = ({ count = 1, timeout } = {}) => {
433
+ invariant(count >= 0, void 0, {
434
+ F: __dxlog_file2,
435
+ L: 19,
436
+ S: void 0,
437
+ A: [
438
+ "count >= 0",
439
+ ""
440
+ ]
441
+ });
442
+ let t;
443
+ let doResolve;
444
+ let doReject;
445
+ const promise = new Promise((resolve, reject) => {
446
+ doResolve = (value) => {
447
+ clearTimeout(t);
448
+ resolve(value);
449
+ };
450
+ doReject = (err) => {
451
+ clearTimeout(t);
452
+ reject(err);
453
+ };
454
+ });
455
+ if (count === 0) {
456
+ setTimeout(() => {
457
+ doResolve(0);
458
+ });
459
+ } else {
460
+ if (timeout) {
461
+ t = setTimeout(() => {
462
+ doReject(new Error(`Timed out after ${timeout.toLocaleString()}ms`));
463
+ }, timeout);
464
+ }
465
+ }
466
+ let i = 0;
467
+ return [
468
+ async () => await promise,
469
+ () => {
470
+ if (++i === count) {
471
+ doResolve(i);
472
+ }
473
+ return i;
474
+ },
475
+ (err) => doReject(err)
476
+ ];
477
+ };
478
+
479
+ // packages/common/async/src/observable.ts
480
+ import Observable from "zen-observable";
481
+ import PushStream from "zen-push";
482
+
483
+ // packages/common/async/src/trigger.ts
484
+ var trigger = (timeout) => {
485
+ let callback;
486
+ const promise = new Promise((resolve, reject) => {
487
+ if (timeout) {
488
+ setTimeout(() => reject(new Error(`Timed out after ${timeout.toLocaleString()}ms`)), timeout);
489
+ }
490
+ callback = resolve;
491
+ });
492
+ const provider = () => promise;
493
+ const resolver = (value) => callback(value);
494
+ return [
495
+ provider,
496
+ resolver
497
+ ];
498
+ };
499
+ var TriggerState;
500
+ (function(TriggerState2) {
501
+ TriggerState2["WAITING"] = "WAITING";
502
+ TriggerState2["RESOLVED"] = "RESOLVED";
503
+ TriggerState2["REJECTED"] = "REJECTED";
504
+ })(TriggerState || (TriggerState = {}));
505
+ var Trigger = class {
506
+ constructor(_options = {
507
+ autoReset: false
508
+ }) {
509
+ this._options = _options;
510
+ this._state = "WAITING";
511
+ this.reset();
512
+ }
513
+ get state() {
514
+ return this._state;
515
+ }
516
+ /**
517
+ * Wait until wake is called, with optional timeout.
518
+ */
519
+ async wait({ timeout } = {}) {
520
+ if (timeout) {
521
+ return asyncTimeout(this._promise, timeout, new TimeoutError(timeout));
522
+ } else {
523
+ return this._promise;
524
+ }
525
+ }
526
+ /**
527
+ * Wake blocked callers (if any).
528
+ * NOOP if the trigger is already resolved.
529
+ */
530
+ wake(value) {
531
+ if (this._state !== "WAITING") {
532
+ return this;
533
+ }
534
+ this._state = "RESOLVED";
535
+ this._resolve(value);
536
+ if (this._options.autoReset) {
537
+ return this.reset();
538
+ }
539
+ return this;
540
+ }
541
+ /**
542
+ * Reset promise (new waiters will wait).
543
+ */
544
+ reset() {
545
+ this._state = "WAITING";
546
+ this._promise = new Promise((resolve, reject) => {
547
+ this._resolve = resolve;
548
+ this._reject = reject;
549
+ });
550
+ this._promise.catch(() => {
551
+ });
552
+ return this;
553
+ }
554
+ /**
555
+ * Throw error to blocked callers (if any).
556
+ * NOOP if the trigger is already resolved.
557
+ */
558
+ throw(error) {
559
+ if (this._state !== "WAITING") {
560
+ return this;
561
+ }
562
+ this._state = "REJECTED";
563
+ this._reject(error);
564
+ if (this._options.autoReset) {
565
+ return this.reset();
566
+ }
567
+ return this;
568
+ }
569
+ };
570
+
571
+ // packages/common/async/src/observable.ts
572
+ var MulticastObservable = class _MulticastObservable extends Observable {
573
+ constructor(subscriber, _value) {
574
+ super((observer) => this._subscribe(observer));
575
+ this._value = _value;
576
+ this._observers = /* @__PURE__ */ new Set();
577
+ this._completed = new Trigger();
578
+ this._handlers = {
579
+ next: (value) => {
580
+ this._value = value;
581
+ this._observers.forEach((observer) => observer.next?.(value));
582
+ },
583
+ error: (err) => {
584
+ this._observers.forEach((observer) => observer.error?.(err));
585
+ },
586
+ complete: () => {
587
+ this._completed.wake();
588
+ this._observers.forEach((observer) => observer.complete?.());
589
+ }
590
+ };
591
+ this._observable = typeof subscriber === "function" ? new Observable(subscriber) : subscriber;
592
+ this._observable.subscribe(this._handlers);
593
+ }
594
+ static from(value, initialValue) {
595
+ if ("emit" in value) {
596
+ return new _MulticastObservable((observer) => {
597
+ value.on((data) => {
598
+ observer.next(data);
599
+ });
600
+ }, initialValue);
601
+ }
602
+ const observable = Observable.from(value);
603
+ return new _MulticastObservable(observable, initialValue);
604
+ }
605
+ static of(...items) {
606
+ return new _MulticastObservable(Observable.of(...items.slice(1)), items[0]);
607
+ }
608
+ /**
609
+ * @returns Stable reference to an observable that always returns `undefined`.
610
+ */
611
+ static empty() {
612
+ return EMPTY_OBSERVABLE;
613
+ }
614
+ /**
615
+ * Get the current value of the observable.
616
+ */
617
+ get() {
618
+ if (this._value === void 0) {
619
+ throw new Error("MulticastObservable is not initialized.");
620
+ }
621
+ return this._value;
622
+ }
623
+ /**
624
+ * Wait for the observable to complete.
625
+ *
626
+ * @returns Promise that resolves to the value of the observable at the time of completion.
627
+ */
628
+ async wait({ timeout } = {}) {
629
+ await this._completed.wait({
630
+ timeout
631
+ });
632
+ return this.get();
633
+ }
634
+ forEach(callback) {
635
+ return this._observable.forEach(callback);
636
+ }
637
+ map(callback) {
638
+ return new _MulticastObservable(this._observable.map(callback), this._value && callback(this._value));
639
+ }
640
+ filter(callback) {
641
+ return new _MulticastObservable(this._observable.filter(callback), this._value && callback(this._value) ? this._value : void 0);
642
+ }
643
+ reduce(callback, initialValue) {
644
+ return new _MulticastObservable(initialValue ? this._observable.reduce(callback, initialValue) : this._observable.reduce(callback), initialValue ?? this._value);
645
+ }
646
+ flatMap(callback) {
647
+ return new _MulticastObservable(this._observable.flatMap(callback), this._value && callback(this._value).get());
648
+ }
649
+ concat(...observables) {
650
+ return new _MulticastObservable(this._observable.concat(...observables), this._value);
651
+ }
652
+ /**
653
+ * Concatenates multicast observables without losing the current value.
654
+ * @param reducer reduces the values of any multicast observables being concatenated into a single value
655
+ * @param observables observables to concatenate
656
+ * @returns concatenated observable
657
+ */
658
+ losslessConcat(reducer, ...observables) {
659
+ const multicast = observables.filter((observable) => observable instanceof _MulticastObservable);
660
+ const value = reducer(this._value, multicast.map((observable) => observable.get()));
661
+ return new _MulticastObservable(this._observable.concat(...observables), value);
662
+ }
663
+ _subscribe(observer) {
664
+ if (!this._observers.has(observer)) {
665
+ this._observers.add(observer);
666
+ }
667
+ if (this._value !== void 0) {
668
+ observer.next?.(this._value);
669
+ }
670
+ return () => {
671
+ this._observers.delete(observer);
672
+ };
673
+ }
674
+ };
675
+ var EMPTY_OBSERVABLE = MulticastObservable.of(null);
676
+
677
+ // packages/common/async/src/observable-value.ts
678
+ import { createSetDispatch } from "@dxos/util";
679
+ var ObservableProvider = class {
680
+ constructor() {
681
+ this._handlers = /* @__PURE__ */ new Set();
682
+ this._proxy = createSetDispatch({
683
+ handlers: this._handlers
684
+ });
685
+ }
686
+ /**
687
+ * Proxy used to dispatch callbacks to each subscription.
688
+ */
689
+ get callback() {
690
+ return this._proxy;
691
+ }
692
+ get value() {
693
+ return this._value;
694
+ }
695
+ setValue(value) {
696
+ this._value = value;
697
+ }
698
+ subscribe(handler) {
699
+ this._handlers.add(handler);
700
+ return () => {
701
+ this._handlers.delete(handler);
702
+ };
703
+ }
704
+ };
705
+ var CancellableObservableProvider = class extends ObservableProvider {
706
+ constructor(_handleCancel) {
707
+ super();
708
+ this._handleCancel = _handleCancel;
709
+ this._cancelled = false;
710
+ }
711
+ get cancelled() {
712
+ return this._cancelled;
713
+ }
714
+ async cancel() {
715
+ if (this._cancelled) {
716
+ return;
717
+ }
718
+ this._cancelled = true;
719
+ await this._handleCancel?.();
720
+ this.callback.onCancelled?.();
721
+ }
722
+ };
723
+
724
+ // packages/common/async/src/mutex.ts
725
+ import "@dxos/util";
726
+ import { warnAfterTimeout } from "@dxos/debug";
727
+ var Mutex = class {
728
+ constructor() {
729
+ this._queue = Promise.resolve();
730
+ this._queueLength = 0;
731
+ this._tag = null;
732
+ }
733
+ get tag() {
734
+ return this._tag;
735
+ }
736
+ isLocked() {
737
+ return this._queueLength > 0;
738
+ }
739
+ /**
740
+ * Acquires the lock.
741
+ * Caller is responsible for releasing the lock using the returned callback.
742
+ * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
743
+ * @returns Release callback
744
+ */
745
+ async acquire(tag) {
746
+ const prev = this._queue;
747
+ let guard;
748
+ this._queueLength++;
749
+ this._queue = new Promise((resolve) => {
750
+ guard = new MutexGuard(() => {
751
+ this._queueLength--;
752
+ this._tag = null;
753
+ resolve();
754
+ });
755
+ });
756
+ await prev;
757
+ if (tag !== void 0) {
758
+ this._tag = tag;
759
+ }
760
+ return guard;
761
+ }
762
+ /**
763
+ * Waits for all previous executions to complete and then executes a given function.
764
+ * Only a single function can be executed at a time.
765
+ * Function are executed in the same order as `executeSynchronized` is called.
766
+ * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
767
+ */
768
+ async executeSynchronized(fun) {
769
+ const guard = await this.acquire();
770
+ try {
771
+ return await fun();
772
+ } finally {
773
+ guard.release();
774
+ }
775
+ }
776
+ };
777
+ var MutexGuard = class {
778
+ constructor(_release) {
779
+ this._release = _release;
780
+ }
781
+ /**
782
+ * Releases the lock.
783
+ */
784
+ release() {
785
+ this._release();
786
+ }
787
+ [Symbol.dispose]() {
788
+ this.release();
789
+ }
790
+ };
791
+ var classMutexSymbol = Symbol("class-mutex");
792
+ var FORCE_DISABLE_WARNING = false;
793
+ var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
794
+ var synchronized = (target, propertyName, descriptor) => {
795
+ const method = descriptor.value;
796
+ descriptor.value = async function synchronizedMethod(...args) {
797
+ const mutex = this[classMutexSymbol] ??= new Mutex();
798
+ const tag = `${target.constructor.name}.${propertyName}`;
799
+ let guard;
800
+ if (!enableWarning) {
801
+ guard = await mutex.acquire(tag);
802
+ } else {
803
+ guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
804
+ }
805
+ try {
806
+ return await method.apply(this, args);
807
+ } finally {
808
+ guard.release();
809
+ }
810
+ };
811
+ Object.defineProperty(descriptor.value, "name", {
812
+ value: propertyName + "$synchronized"
813
+ });
814
+ };
815
+
816
+ // packages/common/async/src/sink.ts
817
+ var sink = (emitter, event, count = 1) => {
818
+ const [getPromise, resolve] = trigger();
819
+ let counter = 0;
820
+ const listener = () => {
821
+ if (++counter === count) {
822
+ emitter.off(event, listener);
823
+ resolve();
824
+ }
825
+ };
826
+ emitter.on(event, listener);
827
+ return getPromise();
828
+ };
829
+
830
+ // packages/common/async/src/stream-to-array.ts
831
+ var streamToArray = (stream) => {
832
+ let deferred;
833
+ if (!stream.readable) {
834
+ deferred = Promise.resolve([]);
835
+ } else {
836
+ deferred = new Promise((resolve, reject) => {
837
+ if (!stream.readable) {
838
+ return resolve([]);
839
+ }
840
+ let arr = [];
841
+ const onData = (doc) => {
842
+ arr?.push(doc);
843
+ };
844
+ const onEnd = (err) => {
845
+ if (err) {
846
+ reject(err);
847
+ } else {
848
+ resolve(arr);
849
+ }
850
+ cleanup();
851
+ };
852
+ const onClose = () => {
853
+ resolve(arr);
854
+ cleanup();
855
+ };
856
+ const cleanup = () => {
857
+ arr = [];
858
+ stream.removeListener("data", onData);
859
+ stream.removeListener("end", onEnd);
860
+ stream.removeListener("error", onEnd);
861
+ stream.removeListener("close", onClose);
862
+ };
863
+ stream.on("data", onData);
864
+ stream.on("end", onEnd);
865
+ stream.on("error", onEnd);
866
+ stream.on("close", onClose);
867
+ });
868
+ }
869
+ return deferred;
870
+ };
871
+
872
+ // packages/common/async/src/timer.ts
873
+ var Timer = class {
874
+ constructor(_callback) {
875
+ this._callback = _callback;
876
+ this._state = new Event();
877
+ this._count = 0;
878
+ }
879
+ get state() {
880
+ return this._state;
881
+ }
882
+ get running() {
883
+ return !!this._timer;
884
+ }
885
+ start(options, cb) {
886
+ if (isNaN(options.count) || isNaN(options.interval)) {
887
+ throw new Error(`Invalid options: ${JSON.stringify(options)}`);
888
+ }
889
+ if (this.running) {
890
+ this.stop();
891
+ }
892
+ const stop = () => {
893
+ this.stop();
894
+ cb?.();
895
+ };
896
+ const run = () => {
897
+ if (this._count >= (options.count ?? 0)) {
898
+ stop();
899
+ } else {
900
+ const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
901
+ this._timer = setTimeout(async () => {
902
+ await this._callback(this._count++);
903
+ run();
904
+ }, interval);
905
+ }
906
+ };
907
+ this._state.emit(true);
908
+ this._count = 0;
909
+ setTimeout(run);
910
+ return this;
911
+ }
912
+ stop() {
913
+ clearInterval(this._timer);
914
+ this._timer = void 0;
915
+ this._state.emit(false);
916
+ return this;
917
+ }
918
+ };
919
+
920
+ // packages/common/async/src/testing.ts
921
+ var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
922
+ const stopTime = timeout ? Date.now() + timeout : 0;
923
+ const trigger2 = new Trigger();
924
+ const waiter = async () => {
925
+ while (!stopTime || Date.now() < stopTime) {
926
+ try {
927
+ const value = await condition();
928
+ if (value) {
929
+ trigger2.wake(value);
930
+ break;
931
+ }
932
+ } catch (err) {
933
+ }
934
+ await sleep(interval);
935
+ }
936
+ };
937
+ setTimeout(waiter, 0);
938
+ return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
939
+ };
940
+
941
+ // packages/common/async/src/until.ts
942
+ var until = (cb, timeout) => {
943
+ return new Promise((resolve, reject) => {
944
+ const t = timeout && setTimeout(() => {
945
+ reject(new Error(`Timeout after ${t}ms`));
946
+ }, timeout);
947
+ setTimeout(async () => {
948
+ try {
949
+ await cb((value) => {
950
+ t && clearTimeout(t);
951
+ resolve(value);
952
+ }, (error) => {
953
+ t && clearTimeout(t);
954
+ reject(error);
955
+ });
956
+ } catch (err) {
957
+ reject(err);
958
+ }
959
+ });
960
+ });
961
+ };
962
+ var untilPromise = (cb) => cb();
963
+ var untilError = (cb) => {
964
+ return new Promise((resolve, reject) => {
965
+ setTimeout(async () => {
966
+ try {
967
+ await cb();
968
+ reject(new Error("No error was thrown."));
969
+ } catch (err) {
970
+ resolve(err);
971
+ }
972
+ });
973
+ });
974
+ };
975
+
976
+ // packages/common/async/src/task-scheduling.ts
977
+ import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
978
+ import { StackTrace as StackTrace2 } from "@dxos/debug";
979
+
980
+ // packages/common/async/src/track-leaks.ts
981
+ import { StackTrace } from "@dxos/debug";
982
+ import { log } from "@dxos/log";
983
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
984
+ var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
985
+ var openResources = /* @__PURE__ */ new Set();
986
+ var handleSymbol = Symbol("checkLeaksHandle");
987
+ var trackResource = (resourceProvider) => {
988
+ if (!enabled) {
989
+ return () => {
990
+ };
991
+ }
992
+ const resource = resourceProvider();
993
+ openResources.add(resource);
994
+ return () => {
995
+ openResources.delete(resource);
996
+ };
997
+ };
998
+ var trackLeaks = (open, close) => (target) => {
999
+ if (!enabled) {
1000
+ return;
1001
+ }
1002
+ const openMethod = target.prototype[open];
1003
+ const closeMethod = target.prototype[close];
1004
+ if (!openMethod || !closeMethod) {
1005
+ throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
1006
+ }
1007
+ {
1008
+ target.prototype[open] = async function(...args) {
1009
+ this[handleSymbol] = trackResource(() => ({
1010
+ name: target.name,
1011
+ openStack: new StackTrace()
1012
+ }));
1013
+ return openMethod.apply(this, args);
1014
+ };
1015
+ Object.defineProperty(target.prototype[open], "name", {
1016
+ value: open + "$checkLeaks"
1017
+ });
1018
+ }
1019
+ {
1020
+ target.prototype[close] = async function(...args) {
1021
+ this[handleSymbol]?.();
1022
+ return closeMethod.apply(this, args);
1023
+ };
1024
+ Object.defineProperty(target.prototype[close], "name", {
1025
+ value: close + "$checkLeaks"
1026
+ });
1027
+ }
1028
+ };
1029
+ var dumpLeaks = () => {
1030
+ if (!enabled) {
1031
+ return;
1032
+ }
1033
+ log.info(`Leaked resources ${openResources.size}:`, void 0, {
1034
+ F: __dxlog_file3,
1035
+ L: 82,
1036
+ S: void 0,
1037
+ C: (f, a) => f(...a)
1038
+ });
1039
+ for (const resource of openResources) {
1040
+ log.info(`- ${resource.name} at`, void 0, {
1041
+ F: __dxlog_file3,
1042
+ L: 84,
1043
+ S: void 0,
1044
+ C: (f, a) => f(...a)
1045
+ });
1046
+ log.info(resource.openStack.getStack(1), void 0, {
1047
+ F: __dxlog_file3,
1048
+ L: 85,
1049
+ S: void 0,
1050
+ C: (f, a) => f(...a)
1051
+ });
1052
+ log.info("\n", void 0, {
1053
+ F: __dxlog_file3,
1054
+ L: 86,
1055
+ S: void 0,
1056
+ C: (f, a) => f(...a)
1057
+ });
1058
+ }
1059
+ };
1060
+ if (enabled) {
1061
+ global.dxDumpLeaks = dumpLeaks;
1062
+ }
1063
+
1064
+ // packages/common/async/src/task-scheduling.ts
1065
+ var DeferredTask = class {
1066
+ constructor(_ctx, _callback) {
1067
+ this._ctx = _ctx;
1068
+ this._callback = _callback;
1069
+ this._scheduled = false;
1070
+ this._currentTask = null;
1071
+ this._nextTask = new Trigger();
1072
+ }
1073
+ /**
1074
+ * Schedule the task to run asynchronously.
1075
+ */
1076
+ schedule() {
1077
+ if (this._scheduled) {
1078
+ return;
1079
+ }
1080
+ scheduleTask(this._ctx, async () => {
1081
+ await this._currentTask;
1082
+ this._scheduled = false;
1083
+ const completionTrigger = this._nextTask;
1084
+ this._nextTask = new Trigger();
1085
+ this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
1086
+ completionTrigger.wake();
1087
+ });
1088
+ });
1089
+ this._scheduled = true;
1090
+ }
1091
+ /**
1092
+ * Schedule the task to run and wait for it to finish.
1093
+ */
1094
+ async runBlocking() {
1095
+ if (this._ctx.disposed) {
1096
+ throw new ContextDisposedError2();
1097
+ }
1098
+ this.schedule();
1099
+ await this._nextTask.wait();
1100
+ }
1101
+ /**
1102
+ * Waits for the current task to finish if it is running.
1103
+ * Does not schedule a new task.
1104
+ */
1105
+ async join() {
1106
+ await this._currentTask;
1107
+ }
1108
+ };
1109
+ var runInContext = (ctx, fn) => {
1110
+ try {
1111
+ fn();
1112
+ } catch (err) {
1113
+ ctx.raise(err);
1114
+ }
1115
+ };
1116
+ var runInContextAsync = async (ctx, fn) => {
1117
+ try {
1118
+ await fn();
1119
+ } catch (err) {
1120
+ ctx.raise(err);
1121
+ }
1122
+ };
1123
+ var scheduleMicroTask = (ctx, fn) => {
1124
+ queueMicrotask(async () => {
1125
+ if (ctx.disposed) {
1126
+ return;
1127
+ }
1128
+ await runInContextAsync(ctx, fn);
1129
+ });
1130
+ };
1131
+ var scheduleTask = (ctx, fn, afterMs) => {
1132
+ const clearTracking = trackResource(() => ({
1133
+ name: `task (${fn.name || "anonymous"})`,
1134
+ openStack: new StackTrace2()
1135
+ }));
1136
+ const timeout = setTimeout(async () => {
1137
+ clearDispose();
1138
+ await runInContextAsync(ctx, fn);
1139
+ clearTracking();
1140
+ }, afterMs);
1141
+ const clearDispose = ctx.onDispose(() => {
1142
+ clearTracking();
1143
+ clearTimeout(timeout);
1144
+ });
1145
+ };
1146
+ var scheduleTaskInterval = (ctx, task, interval) => {
1147
+ const clearTracking = trackResource(() => ({
1148
+ name: `repeating task (${task.name || "anonymous"})`,
1149
+ openStack: new StackTrace2()
1150
+ }));
1151
+ let timeoutId;
1152
+ const run = async () => {
1153
+ await runInContextAsync(ctx, task);
1154
+ if (ctx.disposed) {
1155
+ return;
1156
+ }
1157
+ timeoutId = setTimeout(run, interval);
1158
+ };
1159
+ timeoutId = setTimeout(run, interval);
1160
+ ctx.onDispose(() => {
1161
+ clearTracking();
1162
+ clearTimeout(timeoutId);
1163
+ });
1164
+ };
1165
+ var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
1166
+ const clearTracking = trackResource(() => ({
1167
+ name: `repeating task (${task.name || "anonymous"})`,
1168
+ openStack: new StackTrace2()
1169
+ }));
1170
+ let timeoutId;
1171
+ let interval = initialInterval;
1172
+ const repeat = async () => {
1173
+ await runInContextAsync(ctx, task);
1174
+ if (ctx.disposed) {
1175
+ return;
1176
+ }
1177
+ interval *= 2;
1178
+ timeoutId = setTimeout(repeat, interval);
1179
+ };
1180
+ timeoutId = setTimeout(repeat, interval);
1181
+ ctx.onDispose(() => {
1182
+ clearTracking();
1183
+ clearTimeout(timeoutId);
1184
+ });
1185
+ };
1186
+
1187
+ // packages/common/async/src/test-stream.ts
1188
+ import { Duplex } from "node:stream";
1189
+ var TestStream = class extends Duplex {
1190
+ constructor() {
1191
+ super(...arguments);
1192
+ this._received = Buffer.alloc(0);
1193
+ this._onWrite = new Event();
1194
+ }
1195
+ static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
1196
+ stream1.push("ping");
1197
+ stream2.push("pong");
1198
+ await Promise.all([
1199
+ stream2.assertReceivedAsync("ping", {
1200
+ timeout
1201
+ }),
1202
+ stream1.assertReceivedAsync("pong", {
1203
+ timeout
1204
+ })
1205
+ ]);
1206
+ }
1207
+ _write(chunk, encoding, callback) {
1208
+ this._received = Buffer.concat([
1209
+ this._received,
1210
+ chunk
1211
+ ]);
1212
+ this._onWrite.emit();
1213
+ callback();
1214
+ }
1215
+ _read(size) {
1216
+ }
1217
+ assertReceivedAsync(data, { timeout = 200 } = {}) {
1218
+ const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
1219
+ return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
1220
+ }
1221
+ };
1222
+
1223
+ // packages/common/async/src/update-scheduler.ts
1224
+ var TIME_PERIOD = 1e3;
1225
+ var UpdateScheduler = class {
1226
+ constructor(_ctx, _callback, _params = {}) {
1227
+ this._ctx = _ctx;
1228
+ this._callback = _callback;
1229
+ this._params = _params;
1230
+ this._promise = null;
1231
+ this._scheduled = false;
1232
+ this._lastUpdateTime = -TIME_PERIOD;
1233
+ _ctx.onDispose(async () => {
1234
+ await this._promise;
1235
+ });
1236
+ }
1237
+ trigger() {
1238
+ if (this._scheduled) {
1239
+ return;
1240
+ }
1241
+ scheduleMicroTask(this._ctx, async () => {
1242
+ await this._promise;
1243
+ if (this._params.maxFrequency) {
1244
+ const now = performance.now();
1245
+ const delay = this._lastUpdateTime + TIME_PERIOD / this._params.maxFrequency - now;
1246
+ if (delay > 0) {
1247
+ await new Promise((resolve) => {
1248
+ const clearContext = this._ctx.onDispose(() => {
1249
+ clearTimeout(timeoutId);
1250
+ resolve();
1251
+ });
1252
+ const timeoutId = setTimeout(() => {
1253
+ clearContext();
1254
+ resolve();
1255
+ }, delay);
1256
+ });
1257
+ }
1258
+ }
1259
+ if (this._ctx.disposed) {
1260
+ return;
1261
+ }
1262
+ this._lastUpdateTime = performance.now();
1263
+ this._scheduled = false;
1264
+ this._promise = this._callback().then(() => {
1265
+ this._promise = null;
1266
+ }, (error) => {
1267
+ this._promise = null;
1268
+ this._ctx.raise(error);
1269
+ });
1270
+ });
1271
+ this._scheduled = true;
1272
+ }
1273
+ forceTrigger() {
1274
+ scheduleMicroTask(this._ctx, async () => {
1275
+ this._callback().catch((err) => this._ctx.raise(err));
1276
+ });
1277
+ }
1278
+ /**
1279
+ * Waits for the current task to finish if it is running.
1280
+ * Does not schedule a new task.
1281
+ */
1282
+ async join() {
1283
+ await this._promise;
1284
+ }
1285
+ /**
1286
+ * Force schedule the task to run and wait for it to finish.
1287
+ */
1288
+ async runBlocking() {
1289
+ await this._promise;
1290
+ this._promise = this._callback();
1291
+ await this._promise;
1292
+ }
1293
+ };
1294
+ export {
1295
+ CancellableObservableProvider,
1296
+ DeferredTask,
1297
+ Event,
1298
+ EventSubscriptions,
1299
+ MulticastObservable,
1300
+ Mutex,
1301
+ MutexGuard,
1302
+ Observable,
1303
+ ObservableProvider,
1304
+ PushStream,
1305
+ TestStream,
1306
+ TimeoutError,
1307
+ Timer,
1308
+ Trigger,
1309
+ TriggerState,
1310
+ UpdateScheduler,
1311
+ addListener,
1312
+ asyncChain,
1313
+ asyncReturn,
1314
+ asyncTimeout,
1315
+ createPromiseFromCallback,
1316
+ debounce,
1317
+ dumpLeaks,
1318
+ latch,
1319
+ observableError,
1320
+ onEvent,
1321
+ runInContext,
1322
+ runInContextAsync,
1323
+ scheduleExponentialBackoffTaskInterval,
1324
+ scheduleMicroTask,
1325
+ scheduleTask,
1326
+ scheduleTaskInterval,
1327
+ sink,
1328
+ sleep,
1329
+ sleepWithContext,
1330
+ streamToArray,
1331
+ synchronized,
1332
+ toError,
1333
+ trackLeaks,
1334
+ trackResource,
1335
+ trigger,
1336
+ unrefTimeout,
1337
+ until,
1338
+ untilError,
1339
+ untilPromise,
1340
+ waitForCondition,
1341
+ waitForEvent
1342
+ };
1343
+ //# sourceMappingURL=index.mjs.map