@dxos/async 0.7.5-main.e9bb01b → 0.7.5-staging.2ff1350

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.
@@ -137,6 +137,7 @@ var EventSubscriptions = class {
137
137
  this._listeners.length = 0;
138
138
  }
139
139
  };
140
+ var DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;
140
141
  var Event = class _Event {
141
142
  constructor() {
142
143
  this._listeners = /* @__PURE__ */ new Set();
@@ -165,7 +166,23 @@ var Event = class _Event {
165
166
  */
166
167
  emit(data) {
167
168
  for (const listener of this._listeners) {
168
- void listener.trigger(data);
169
+ listener.trigger(data);
170
+ if (listener.once) {
171
+ this._listeners.delete(listener);
172
+ }
173
+ }
174
+ }
175
+ /**
176
+ * Emit an event and wait for async listeners to complete.
177
+ * In most cases should only be called by the class or entity containing the event.
178
+ * All listeners are called in order of subscription with persistent ones first.
179
+ * Listeners are called sequentially.
180
+ *
181
+ * @param data param that will be passed to all listeners.
182
+ */
183
+ async emitAsync(data) {
184
+ for (const listener of this._listeners) {
185
+ await listener.triggerAsync(data);
169
186
  if (listener.once) {
170
187
  this._listeners.delete(listener);
171
188
  }
@@ -178,7 +195,7 @@ var Event = class _Event {
178
195
  ] : [
179
196
  new Context(void 0, {
180
197
  F: __dxlog_file,
181
- L: 124
198
+ L: 148
182
199
  }),
183
200
  _ctx
184
201
  ];
@@ -211,7 +228,7 @@ var Event = class _Event {
211
228
  ] : [
212
229
  new Context(void 0, {
213
230
  F: __dxlog_file,
214
- L: 161
231
+ L: 185
215
232
  }),
216
233
  _ctx
217
234
  ];
@@ -395,7 +412,21 @@ var EventListener = class {
395
412
  derefCallback() {
396
413
  return this.weak ? this.callback.deref() : this.callback;
397
414
  }
398
- async trigger(data) {
415
+ trigger(data) {
416
+ let result;
417
+ try {
418
+ const callback = this.derefCallback();
419
+ result = callback?.(data);
420
+ } catch (err) {
421
+ this.ctx.raise(err);
422
+ }
423
+ if (!DO_NOT_ERROR_ON_ASYNC_CALLBACK) {
424
+ if (result instanceof Promise) {
425
+ throw new TypeError("Event has async callbacks, use emitAsync instead");
426
+ }
427
+ }
428
+ }
429
+ async triggerAsync(data) {
399
430
  try {
400
431
  const callback = this.derefCallback();
401
432
  await callback?.(data);
@@ -474,6 +505,98 @@ var latch = ({ count = 1, timeout } = {}) => {
474
505
  ];
475
506
  };
476
507
 
508
+ // packages/common/async/src/mutex.ts
509
+ import "@dxos/util";
510
+ import { warnAfterTimeout } from "@dxos/debug";
511
+ var Mutex = class {
512
+ constructor() {
513
+ this._queue = Promise.resolve();
514
+ this._queueLength = 0;
515
+ this._tag = null;
516
+ }
517
+ get tag() {
518
+ return this._tag;
519
+ }
520
+ isLocked() {
521
+ return this._queueLength > 0;
522
+ }
523
+ /**
524
+ * Acquires the lock.
525
+ * Caller is responsible for releasing the lock using the returned callback.
526
+ * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
527
+ * @returns Release callback
528
+ */
529
+ async acquire(tag) {
530
+ const prev = this._queue;
531
+ let guard;
532
+ this._queueLength++;
533
+ this._queue = new Promise((resolve) => {
534
+ guard = new MutexGuard(() => {
535
+ this._queueLength--;
536
+ this._tag = null;
537
+ resolve();
538
+ });
539
+ });
540
+ await prev;
541
+ if (tag !== void 0) {
542
+ this._tag = tag;
543
+ }
544
+ return guard;
545
+ }
546
+ /**
547
+ * Waits for all previous executions to complete and then executes a given function.
548
+ * Only a single function can be executed at a time.
549
+ * Function are executed in the same order as `executeSynchronized` is called.
550
+ * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
551
+ */
552
+ async executeSynchronized(fun) {
553
+ const guard = await this.acquire();
554
+ try {
555
+ return await fun();
556
+ } finally {
557
+ guard.release();
558
+ }
559
+ }
560
+ };
561
+ var MutexGuard = class {
562
+ constructor(_release) {
563
+ this._release = _release;
564
+ }
565
+ /**
566
+ * Releases the lock.
567
+ */
568
+ release() {
569
+ this._release();
570
+ }
571
+ [Symbol.dispose]() {
572
+ this.release();
573
+ }
574
+ };
575
+ var classMutexSymbol = Symbol("class-mutex");
576
+ var FORCE_DISABLE_WARNING = false;
577
+ var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
578
+ var synchronized = (target, propertyName, descriptor) => {
579
+ const method = descriptor.value;
580
+ descriptor.value = async function synchronizedMethod(...args) {
581
+ const mutex = this[classMutexSymbol] ??= new Mutex();
582
+ const tag = `${target.constructor.name}.${propertyName}`;
583
+ let guard;
584
+ if (!enableWarning) {
585
+ guard = await mutex.acquire(tag);
586
+ } else {
587
+ guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
588
+ }
589
+ try {
590
+ return await method.apply(this, args);
591
+ } finally {
592
+ guard.release();
593
+ }
594
+ };
595
+ Object.defineProperty(descriptor.value, "name", {
596
+ value: propertyName + "$synchronized"
597
+ });
598
+ };
599
+
477
600
  // packages/common/async/src/observable.ts
478
601
  import Observable from "zen-observable";
479
602
  import PushStream from "zen-push";
@@ -719,340 +842,93 @@ var CancellableObservableProvider = class extends ObservableProvider {
719
842
  }
720
843
  };
721
844
 
722
- // packages/common/async/src/mutex.ts
723
- import "@dxos/util";
724
- import { warnAfterTimeout } from "@dxos/debug";
725
- var Mutex = class {
726
- constructor() {
727
- this._queue = Promise.resolve();
728
- this._queueLength = 0;
729
- this._tag = null;
845
+ // packages/common/async/src/persistent-lifecycle.ts
846
+ import { LifecycleState, Resource, cancelWithContext } from "@dxos/context";
847
+ import { warnAfterTimeout as warnAfterTimeout2 } from "@dxos/debug";
848
+ import { log as log2 } from "@dxos/log";
849
+
850
+ // packages/common/async/src/task-scheduling.ts
851
+ import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
852
+ import { StackTrace as StackTrace2 } from "@dxos/debug";
853
+
854
+ // packages/common/async/src/track-leaks.ts
855
+ import { StackTrace } from "@dxos/debug";
856
+ import { log } from "@dxos/log";
857
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
858
+ var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
859
+ var openResources = /* @__PURE__ */ new Set();
860
+ var handleSymbol = Symbol("checkLeaksHandle");
861
+ var trackResource = (resourceProvider) => {
862
+ if (!enabled) {
863
+ return () => {
864
+ };
730
865
  }
731
- get tag() {
732
- return this._tag;
866
+ const resource = resourceProvider();
867
+ openResources.add(resource);
868
+ return () => {
869
+ openResources.delete(resource);
870
+ };
871
+ };
872
+ var trackLeaks = (open, close) => (target) => {
873
+ if (!enabled) {
874
+ return;
733
875
  }
734
- isLocked() {
735
- return this._queueLength > 0;
876
+ const openMethod = target.prototype[open];
877
+ const closeMethod = target.prototype[close];
878
+ if (!openMethod || !closeMethod) {
879
+ throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
736
880
  }
737
- /**
738
- * Acquires the lock.
739
- * Caller is responsible for releasing the lock using the returned callback.
740
- * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
741
- * @returns Release callback
742
- */
743
- async acquire(tag) {
744
- const prev = this._queue;
745
- let guard;
746
- this._queueLength++;
747
- this._queue = new Promise((resolve) => {
748
- guard = new MutexGuard(() => {
749
- this._queueLength--;
750
- this._tag = null;
751
- resolve();
752
- });
881
+ {
882
+ target.prototype[open] = async function(...args) {
883
+ this[handleSymbol] = trackResource(() => ({
884
+ name: target.name,
885
+ openStack: new StackTrace()
886
+ }));
887
+ return openMethod.apply(this, args);
888
+ };
889
+ Object.defineProperty(target.prototype[open], "name", {
890
+ value: open + "$checkLeaks"
753
891
  });
754
- await prev;
755
- if (tag !== void 0) {
756
- this._tag = tag;
757
- }
758
- return guard;
759
892
  }
760
- /**
761
- * Waits for all previous executions to complete and then executes a given function.
762
- * Only a single function can be executed at a time.
763
- * Function are executed in the same order as `executeSynchronized` is called.
764
- * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
765
- */
766
- async executeSynchronized(fun) {
767
- const guard = await this.acquire();
768
- try {
769
- return await fun();
770
- } finally {
771
- guard.release();
772
- }
893
+ {
894
+ target.prototype[close] = async function(...args) {
895
+ this[handleSymbol]?.();
896
+ return closeMethod.apply(this, args);
897
+ };
898
+ Object.defineProperty(target.prototype[close], "name", {
899
+ value: close + "$checkLeaks"
900
+ });
773
901
  }
774
902
  };
775
- var MutexGuard = class {
776
- constructor(_release) {
777
- this._release = _release;
778
- }
779
- /**
780
- * Releases the lock.
781
- */
782
- release() {
783
- this._release();
903
+ var dumpLeaks = () => {
904
+ if (!enabled) {
905
+ return;
784
906
  }
785
- [Symbol.dispose]() {
786
- this.release();
787
- }
788
- };
789
- var classMutexSymbol = Symbol("class-mutex");
790
- var FORCE_DISABLE_WARNING = false;
791
- var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
792
- var synchronized = (target, propertyName, descriptor) => {
793
- const method = descriptor.value;
794
- descriptor.value = async function synchronizedMethod(...args) {
795
- const mutex = this[classMutexSymbol] ??= new Mutex();
796
- const tag = `${target.constructor.name}.${propertyName}`;
797
- let guard;
798
- if (!enableWarning) {
799
- guard = await mutex.acquire(tag);
800
- } else {
801
- guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
802
- }
803
- try {
804
- return await method.apply(this, args);
805
- } finally {
806
- guard.release();
807
- }
808
- };
809
- Object.defineProperty(descriptor.value, "name", {
810
- value: propertyName + "$synchronized"
811
- });
812
- };
813
-
814
- // packages/common/async/src/sink.ts
815
- var sink = (emitter, event, count = 1) => {
816
- const [getPromise, resolve] = trigger();
817
- let counter = 0;
818
- const listener = () => {
819
- if (++counter === count) {
820
- emitter.off(event, listener);
821
- resolve();
822
- }
823
- };
824
- emitter.on(event, listener);
825
- return getPromise();
826
- };
827
-
828
- // packages/common/async/src/stream-to-array.ts
829
- var streamToArray = (stream) => {
830
- let deferred;
831
- if (!stream.readable) {
832
- deferred = Promise.resolve([]);
833
- } else {
834
- deferred = new Promise((resolve, reject) => {
835
- if (!stream.readable) {
836
- return resolve([]);
837
- }
838
- let arr = [];
839
- const onData = (doc) => {
840
- arr?.push(doc);
841
- };
842
- const onEnd = (err) => {
843
- if (err) {
844
- reject(err);
845
- } else {
846
- resolve(arr);
847
- }
848
- cleanup();
849
- };
850
- const onClose = () => {
851
- resolve(arr);
852
- cleanup();
853
- };
854
- const cleanup = () => {
855
- arr = [];
856
- stream.removeListener("data", onData);
857
- stream.removeListener("end", onEnd);
858
- stream.removeListener("error", onEnd);
859
- stream.removeListener("close", onClose);
860
- };
861
- stream.on("data", onData);
862
- stream.on("end", onEnd);
863
- stream.on("error", onEnd);
864
- stream.on("close", onClose);
865
- });
866
- }
867
- return deferred;
868
- };
869
-
870
- // packages/common/async/src/timer.ts
871
- var Timer = class {
872
- constructor(_callback) {
873
- this._callback = _callback;
874
- this._state = new Event();
875
- this._count = 0;
876
- }
877
- get state() {
878
- return this._state;
879
- }
880
- get running() {
881
- return !!this._timer;
882
- }
883
- start(options, cb) {
884
- if (isNaN(options.count) || isNaN(options.interval)) {
885
- throw new Error(`Invalid options: ${JSON.stringify(options)}`);
886
- }
887
- if (this.running) {
888
- this.stop();
889
- }
890
- const stop = () => {
891
- this.stop();
892
- cb?.();
893
- };
894
- const run = () => {
895
- if (this._count >= (options.count ?? 0)) {
896
- stop();
897
- } else {
898
- const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
899
- this._timer = setTimeout(async () => {
900
- await this._callback(this._count++);
901
- run();
902
- }, interval);
903
- }
904
- };
905
- this._state.emit(true);
906
- this._count = 0;
907
- setTimeout(run);
908
- return this;
909
- }
910
- stop() {
911
- clearInterval(this._timer);
912
- this._timer = void 0;
913
- this._state.emit(false);
914
- return this;
915
- }
916
- };
917
-
918
- // packages/common/async/src/testing.ts
919
- var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
920
- const stopTime = timeout ? Date.now() + timeout : 0;
921
- const trigger2 = new Trigger();
922
- const waiter = async () => {
923
- while (!stopTime || Date.now() < stopTime) {
924
- try {
925
- const value = await condition();
926
- if (value) {
927
- trigger2.wake(value);
928
- break;
929
- }
930
- } catch (err) {
931
- }
932
- await sleep(interval);
933
- }
934
- };
935
- setTimeout(waiter, 0);
936
- return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
937
- };
938
-
939
- // packages/common/async/src/until.ts
940
- var until = (cb, timeout) => {
941
- return new Promise((resolve, reject) => {
942
- const t = timeout && setTimeout(() => {
943
- reject(new Error(`Timeout after ${t}ms`));
944
- }, timeout);
945
- setTimeout(async () => {
946
- try {
947
- await cb((value) => {
948
- t && clearTimeout(t);
949
- resolve(value);
950
- }, (error) => {
951
- t && clearTimeout(t);
952
- reject(error);
953
- });
954
- } catch (err) {
955
- reject(err);
956
- }
957
- });
958
- });
959
- };
960
- var untilPromise = (cb) => cb();
961
- var untilError = (cb) => {
962
- return new Promise((resolve, reject) => {
963
- setTimeout(async () => {
964
- try {
965
- await cb();
966
- reject(new Error("No error was thrown."));
967
- } catch (err) {
968
- resolve(err);
969
- }
970
- });
971
- });
972
- };
973
-
974
- // packages/common/async/src/task-scheduling.ts
975
- import { ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
976
- import { StackTrace as StackTrace2 } from "@dxos/debug";
977
-
978
- // packages/common/async/src/track-leaks.ts
979
- import { StackTrace } from "@dxos/debug";
980
- import { log } from "@dxos/log";
981
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
982
- var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
983
- var openResources = /* @__PURE__ */ new Set();
984
- var handleSymbol = Symbol("checkLeaksHandle");
985
- var trackResource = (resourceProvider) => {
986
- if (!enabled) {
987
- return () => {
988
- };
989
- }
990
- const resource = resourceProvider();
991
- openResources.add(resource);
992
- return () => {
993
- openResources.delete(resource);
994
- };
995
- };
996
- var trackLeaks = (open, close) => (target) => {
997
- if (!enabled) {
998
- return;
999
- }
1000
- const openMethod = target.prototype[open];
1001
- const closeMethod = target.prototype[close];
1002
- if (!openMethod || !closeMethod) {
1003
- throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
1004
- }
1005
- {
1006
- target.prototype[open] = async function(...args) {
1007
- this[handleSymbol] = trackResource(() => ({
1008
- name: target.name,
1009
- openStack: new StackTrace()
1010
- }));
1011
- return openMethod.apply(this, args);
1012
- };
1013
- Object.defineProperty(target.prototype[open], "name", {
1014
- value: open + "$checkLeaks"
1015
- });
1016
- }
1017
- {
1018
- target.prototype[close] = async function(...args) {
1019
- this[handleSymbol]?.();
1020
- return closeMethod.apply(this, args);
1021
- };
1022
- Object.defineProperty(target.prototype[close], "name", {
1023
- value: close + "$checkLeaks"
1024
- });
1025
- }
1026
- };
1027
- var dumpLeaks = () => {
1028
- if (!enabled) {
1029
- return;
1030
- }
1031
- log.info(`Leaked resources ${openResources.size}:`, void 0, {
1032
- F: __dxlog_file3,
1033
- L: 82,
1034
- S: void 0,
1035
- C: (f, a) => f(...a)
1036
- });
1037
- for (const resource of openResources) {
1038
- log.info(`- ${resource.name} at`, void 0, {
1039
- F: __dxlog_file3,
1040
- L: 84,
1041
- S: void 0,
1042
- C: (f, a) => f(...a)
1043
- });
1044
- log.info(resource.openStack.getStack(1), void 0, {
1045
- F: __dxlog_file3,
1046
- L: 85,
1047
- S: void 0,
1048
- C: (f, a) => f(...a)
1049
- });
1050
- log.info("\n", void 0, {
1051
- F: __dxlog_file3,
1052
- L: 86,
1053
- S: void 0,
1054
- C: (f, a) => f(...a)
1055
- });
907
+ log.info(`Leaked resources ${openResources.size}:`, void 0, {
908
+ F: __dxlog_file3,
909
+ L: 82,
910
+ S: void 0,
911
+ C: (f, a) => f(...a)
912
+ });
913
+ for (const resource of openResources) {
914
+ log.info(`- ${resource.name} at`, void 0, {
915
+ F: __dxlog_file3,
916
+ L: 84,
917
+ S: void 0,
918
+ C: (f, a) => f(...a)
919
+ });
920
+ log.info(resource.openStack.getStack(1), void 0, {
921
+ F: __dxlog_file3,
922
+ L: 85,
923
+ S: void 0,
924
+ C: (f, a) => f(...a)
925
+ });
926
+ log.info("\n", void 0, {
927
+ F: __dxlog_file3,
928
+ L: 86,
929
+ S: void 0,
930
+ C: (f, a) => f(...a)
931
+ });
1056
932
  }
1057
933
  };
1058
934
  if (enabled) {
@@ -1068,6 +944,9 @@ var DeferredTask = class {
1068
944
  this._currentTask = null;
1069
945
  this._nextTask = new Trigger();
1070
946
  }
947
+ get scheduled() {
948
+ return this._scheduled;
949
+ }
1071
950
  /**
1072
951
  * Schedule the task to run asynchronously.
1073
952
  */
@@ -1182,6 +1061,173 @@ var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
1182
1061
  });
1183
1062
  };
1184
1063
 
1064
+ // packages/common/async/src/persistent-lifecycle.ts
1065
+ function _ts_decorate(decorators, target, key, desc) {
1066
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1067
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1068
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1069
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1070
+ }
1071
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/common/async/src/persistent-lifecycle.ts";
1072
+ var INIT_RESTART_DELAY = 100;
1073
+ var DEFAULT_MAX_RESTART_DELAY = 5e3;
1074
+ var PersistentLifecycle = class extends Resource {
1075
+ constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
1076
+ super();
1077
+ this._currentState = void 0;
1078
+ this._restartTask = void 0;
1079
+ this._restartAfter = 0;
1080
+ this._start = start;
1081
+ this._stop = stop;
1082
+ this._onRestart = onRestart;
1083
+ this._maxRestartDelay = maxRestartDelay;
1084
+ }
1085
+ get state() {
1086
+ return this._currentState;
1087
+ }
1088
+ async _open() {
1089
+ this._restartTask = new DeferredTask(this._ctx, async () => {
1090
+ try {
1091
+ await this._restart();
1092
+ } catch (err) {
1093
+ log2.warn("Restart failed", {
1094
+ err
1095
+ }, {
1096
+ F: __dxlog_file4,
1097
+ L: 72,
1098
+ S: this,
1099
+ C: (f, a) => f(...a)
1100
+ });
1101
+ this._restartTask?.schedule();
1102
+ }
1103
+ });
1104
+ this._currentState = await this._start().catch((err) => {
1105
+ log2.warn("Start failed", {
1106
+ err
1107
+ }, {
1108
+ F: __dxlog_file4,
1109
+ L: 77,
1110
+ S: this,
1111
+ C: (f, a) => f(...a)
1112
+ });
1113
+ this._restartTask?.schedule();
1114
+ return void 0;
1115
+ });
1116
+ }
1117
+ async _close() {
1118
+ await this._restartTask?.join();
1119
+ await this._stopCurrentState();
1120
+ this._restartTask = void 0;
1121
+ }
1122
+ async _restart() {
1123
+ log2(`restarting in ${this._restartAfter}ms`, {
1124
+ state: this._lifecycleState
1125
+ }, {
1126
+ F: __dxlog_file4,
1127
+ L: 90,
1128
+ S: this,
1129
+ C: (f, a) => f(...a)
1130
+ });
1131
+ await this._stopCurrentState();
1132
+ if (this._lifecycleState !== LifecycleState.OPEN) {
1133
+ return;
1134
+ }
1135
+ await cancelWithContext(this._ctx, sleep(this._restartAfter));
1136
+ this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
1137
+ await warnAfterTimeout2(5e3, "Connection establishment takes too long", async () => {
1138
+ this._currentState = await this._start();
1139
+ });
1140
+ this._restartAfter = 0;
1141
+ await this._onRestart?.();
1142
+ }
1143
+ async _stopCurrentState() {
1144
+ if (this._currentState) {
1145
+ try {
1146
+ await this._stop(this._currentState);
1147
+ } catch (err) {
1148
+ log2.catch(err, void 0, {
1149
+ F: __dxlog_file4,
1150
+ L: 112,
1151
+ S: this,
1152
+ C: (f, a) => f(...a)
1153
+ });
1154
+ }
1155
+ this._currentState = void 0;
1156
+ }
1157
+ }
1158
+ /**
1159
+ * Scheduling restart should be done from outside.
1160
+ */
1161
+ scheduleRestart() {
1162
+ if (this._lifecycleState !== LifecycleState.OPEN) {
1163
+ return;
1164
+ }
1165
+ this._restartTask.schedule();
1166
+ }
1167
+ };
1168
+ _ts_decorate([
1169
+ synchronized
1170
+ ], PersistentLifecycle.prototype, "_open", null);
1171
+ _ts_decorate([
1172
+ synchronized
1173
+ ], PersistentLifecycle.prototype, "scheduleRestart", null);
1174
+
1175
+ // packages/common/async/src/sink.ts
1176
+ var sink = (emitter, event, count = 1) => {
1177
+ const [getPromise, resolve] = trigger();
1178
+ let counter = 0;
1179
+ const listener = () => {
1180
+ if (++counter === count) {
1181
+ emitter.off(event, listener);
1182
+ resolve();
1183
+ }
1184
+ };
1185
+ emitter.on(event, listener);
1186
+ return getPromise();
1187
+ };
1188
+
1189
+ // packages/common/async/src/stream-to-array.ts
1190
+ var streamToArray = (stream) => {
1191
+ let deferred;
1192
+ if (!stream.readable) {
1193
+ deferred = Promise.resolve([]);
1194
+ } else {
1195
+ deferred = new Promise((resolve, reject) => {
1196
+ if (!stream.readable) {
1197
+ return resolve([]);
1198
+ }
1199
+ let arr = [];
1200
+ const onData = (doc) => {
1201
+ arr?.push(doc);
1202
+ };
1203
+ const onEnd = (err) => {
1204
+ if (err) {
1205
+ reject(err);
1206
+ } else {
1207
+ resolve(arr);
1208
+ }
1209
+ cleanup();
1210
+ };
1211
+ const onClose = () => {
1212
+ resolve(arr);
1213
+ cleanup();
1214
+ };
1215
+ const cleanup = () => {
1216
+ arr = [];
1217
+ stream.removeListener("data", onData);
1218
+ stream.removeListener("end", onEnd);
1219
+ stream.removeListener("error", onEnd);
1220
+ stream.removeListener("close", onClose);
1221
+ };
1222
+ stream.on("data", onData);
1223
+ stream.on("end", onEnd);
1224
+ stream.on("error", onEnd);
1225
+ stream.on("close", onClose);
1226
+ });
1227
+ }
1228
+ return deferred;
1229
+ };
1230
+
1185
1231
  // packages/common/async/src/test-stream.ts
1186
1232
  import { Duplex } from "@dxos/node-std/stream";
1187
1233
  var TestStream = class extends Duplex {
@@ -1218,6 +1264,110 @@ var TestStream = class extends Duplex {
1218
1264
  }
1219
1265
  };
1220
1266
 
1267
+ // packages/common/async/src/testing.ts
1268
+ var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
1269
+ const stopTime = timeout ? Date.now() + timeout : 0;
1270
+ const trigger2 = new Trigger();
1271
+ const waiter = async () => {
1272
+ while (!stopTime || Date.now() < stopTime) {
1273
+ try {
1274
+ const value = await condition();
1275
+ if (value) {
1276
+ trigger2.wake(value);
1277
+ break;
1278
+ }
1279
+ } catch (err) {
1280
+ }
1281
+ await sleep(interval);
1282
+ }
1283
+ };
1284
+ setTimeout(waiter, 0);
1285
+ return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
1286
+ };
1287
+
1288
+ // packages/common/async/src/timer.ts
1289
+ var Timer = class {
1290
+ constructor(_callback) {
1291
+ this._callback = _callback;
1292
+ this._state = new Event();
1293
+ this._count = 0;
1294
+ }
1295
+ get state() {
1296
+ return this._state;
1297
+ }
1298
+ get running() {
1299
+ return !!this._timer;
1300
+ }
1301
+ start(options, cb) {
1302
+ if (isNaN(options.count) || isNaN(options.interval)) {
1303
+ throw new Error(`Invalid options: ${JSON.stringify(options)}`);
1304
+ }
1305
+ if (this.running) {
1306
+ this.stop();
1307
+ }
1308
+ const stop = () => {
1309
+ this.stop();
1310
+ cb?.();
1311
+ };
1312
+ const run = () => {
1313
+ if (this._count >= (options.count ?? 0)) {
1314
+ stop();
1315
+ } else {
1316
+ const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
1317
+ this._timer = setTimeout(async () => {
1318
+ await this._callback(this._count++);
1319
+ run();
1320
+ }, interval);
1321
+ }
1322
+ };
1323
+ this._state.emit(true);
1324
+ this._count = 0;
1325
+ setTimeout(run);
1326
+ return this;
1327
+ }
1328
+ stop() {
1329
+ clearInterval(this._timer);
1330
+ this._timer = void 0;
1331
+ this._state.emit(false);
1332
+ return this;
1333
+ }
1334
+ };
1335
+
1336
+ // packages/common/async/src/until.ts
1337
+ var until = (cb, timeout) => {
1338
+ return new Promise((resolve, reject) => {
1339
+ const t = timeout && setTimeout(() => {
1340
+ reject(new Error(`Timeout after ${t}ms`));
1341
+ }, timeout);
1342
+ setTimeout(async () => {
1343
+ try {
1344
+ await cb((value) => {
1345
+ t && clearTimeout(t);
1346
+ resolve(value);
1347
+ }, (error) => {
1348
+ t && clearTimeout(t);
1349
+ reject(error);
1350
+ });
1351
+ } catch (err) {
1352
+ reject(err);
1353
+ }
1354
+ });
1355
+ });
1356
+ };
1357
+ var untilPromise = (cb) => cb();
1358
+ var untilError = (cb) => {
1359
+ return new Promise((resolve, reject) => {
1360
+ setTimeout(async () => {
1361
+ try {
1362
+ await cb();
1363
+ reject(new Error("No error was thrown."));
1364
+ } catch (err) {
1365
+ resolve(err);
1366
+ }
1367
+ });
1368
+ });
1369
+ };
1370
+
1221
1371
  // packages/common/async/src/update-scheduler.ts
1222
1372
  var TIME_PERIOD = 1e3;
1223
1373
  var UpdateScheduler = class {
@@ -1299,6 +1449,7 @@ export {
1299
1449
  MutexGuard,
1300
1450
  Observable,
1301
1451
  ObservableProvider,
1452
+ PersistentLifecycle,
1302
1453
  PushStream,
1303
1454
  TestStream,
1304
1455
  TimeoutError,