@dxos/async 0.7.5-main.9d26e3a → 0.7.5-main.b19bfc8

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.
@@ -37,6 +37,7 @@ __export(node_exports, {
37
37
  MutexGuard: () => MutexGuard,
38
38
  Observable: () => import_zen_observable.default,
39
39
  ObservableProvider: () => ObservableProvider,
40
+ PersistentLifecycle: () => PersistentLifecycle,
40
41
  PushStream: () => import_zen_push.default,
41
42
  TestStream: () => TestStream,
42
43
  TimeoutError: () => TimeoutError,
@@ -80,15 +81,18 @@ module.exports = __toCommonJS(node_exports);
80
81
  var import_context = require("@dxos/context");
81
82
  var import_context2 = require("@dxos/context");
82
83
  var import_invariant = require("@dxos/invariant");
84
+ var import_util = require("@dxos/util");
85
+ var import_debug = require("@dxos/debug");
83
86
  var import_zen_observable = __toESM(require("zen-observable"));
84
87
  var import_zen_push = __toESM(require("zen-push"));
85
- var import_util = require("@dxos/util");
86
88
  var import_util2 = require("@dxos/util");
87
- var import_debug = require("@dxos/debug");
88
89
  var import_context3 = require("@dxos/context");
89
90
  var import_debug2 = require("@dxos/debug");
90
- var import_debug3 = require("@dxos/debug");
91
91
  var import_log = require("@dxos/log");
92
+ var import_context4 = require("@dxos/context");
93
+ var import_debug3 = require("@dxos/debug");
94
+ var import_debug4 = require("@dxos/debug");
95
+ var import_log2 = require("@dxos/log");
92
96
  var import_node_stream = require("node:stream");
93
97
  var createPromiseFromCallback = (run) => new Promise((resolve, reject) => {
94
98
  run((error, value) => {
@@ -214,6 +218,7 @@ var EventSubscriptions = class {
214
218
  this._listeners.length = 0;
215
219
  }
216
220
  };
221
+ var DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;
217
222
  var Event = class _Event {
218
223
  constructor() {
219
224
  this._listeners = /* @__PURE__ */ new Set();
@@ -242,7 +247,23 @@ var Event = class _Event {
242
247
  */
243
248
  emit(data) {
244
249
  for (const listener of this._listeners) {
245
- void listener.trigger(data);
250
+ listener.trigger(data);
251
+ if (listener.once) {
252
+ this._listeners.delete(listener);
253
+ }
254
+ }
255
+ }
256
+ /**
257
+ * Emit an event and wait for async listeners to complete.
258
+ * In most cases should only be called by the class or entity containing the event.
259
+ * All listeners are called in order of subscription with persistent ones first.
260
+ * Listeners are called sequentially.
261
+ *
262
+ * @param data param that will be passed to all listeners.
263
+ */
264
+ async emitAsync(data) {
265
+ for (const listener of this._listeners) {
266
+ await listener.triggerAsync(data);
246
267
  if (listener.once) {
247
268
  this._listeners.delete(listener);
248
269
  }
@@ -255,7 +276,7 @@ var Event = class _Event {
255
276
  ] : [
256
277
  new import_context2.Context(void 0, {
257
278
  F: __dxlog_file,
258
- L: 124
279
+ L: 148
259
280
  }),
260
281
  _ctx
261
282
  ];
@@ -288,7 +309,7 @@ var Event = class _Event {
288
309
  ] : [
289
310
  new import_context2.Context(void 0, {
290
311
  F: __dxlog_file,
291
- L: 161
312
+ L: 185
292
313
  }),
293
314
  _ctx
294
315
  ];
@@ -472,7 +493,21 @@ var EventListener = class {
472
493
  derefCallback() {
473
494
  return this.weak ? this.callback.deref() : this.callback;
474
495
  }
475
- async trigger(data) {
496
+ trigger(data) {
497
+ let result;
498
+ try {
499
+ const callback = this.derefCallback();
500
+ result = callback?.(data);
501
+ } catch (err) {
502
+ this.ctx.raise(err);
503
+ }
504
+ if (!DO_NOT_ERROR_ON_ASYNC_CALLBACK) {
505
+ if (result instanceof Promise) {
506
+ throw new TypeError("Event has async callbacks, use emitAsync instead");
507
+ }
508
+ }
509
+ }
510
+ async triggerAsync(data) {
476
511
  try {
477
512
  const callback = this.derefCallback();
478
513
  await callback?.(data);
@@ -547,6 +582,94 @@ var latch = ({ count = 1, timeout } = {}) => {
547
582
  (err) => doReject(err)
548
583
  ];
549
584
  };
585
+ var Mutex = class {
586
+ constructor() {
587
+ this._queue = Promise.resolve();
588
+ this._queueLength = 0;
589
+ this._tag = null;
590
+ }
591
+ get tag() {
592
+ return this._tag;
593
+ }
594
+ isLocked() {
595
+ return this._queueLength > 0;
596
+ }
597
+ /**
598
+ * Acquires the lock.
599
+ * Caller is responsible for releasing the lock using the returned callback.
600
+ * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
601
+ * @returns Release callback
602
+ */
603
+ async acquire(tag) {
604
+ const prev = this._queue;
605
+ let guard;
606
+ this._queueLength++;
607
+ this._queue = new Promise((resolve) => {
608
+ guard = new MutexGuard(() => {
609
+ this._queueLength--;
610
+ this._tag = null;
611
+ resolve();
612
+ });
613
+ });
614
+ await prev;
615
+ if (tag !== void 0) {
616
+ this._tag = tag;
617
+ }
618
+ return guard;
619
+ }
620
+ /**
621
+ * Waits for all previous executions to complete and then executes a given function.
622
+ * Only a single function can be executed at a time.
623
+ * Function are executed in the same order as `executeSynchronized` is called.
624
+ * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
625
+ */
626
+ async executeSynchronized(fun) {
627
+ const guard = await this.acquire();
628
+ try {
629
+ return await fun();
630
+ } finally {
631
+ guard.release();
632
+ }
633
+ }
634
+ };
635
+ var MutexGuard = class {
636
+ constructor(_release) {
637
+ this._release = _release;
638
+ }
639
+ /**
640
+ * Releases the lock.
641
+ */
642
+ release() {
643
+ this._release();
644
+ }
645
+ [Symbol.dispose]() {
646
+ this.release();
647
+ }
648
+ };
649
+ var classMutexSymbol = Symbol("class-mutex");
650
+ var FORCE_DISABLE_WARNING = false;
651
+ var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
652
+ var synchronized = (target, propertyName, descriptor) => {
653
+ const method = descriptor.value;
654
+ descriptor.value = async function synchronizedMethod(...args) {
655
+ const mutex = this[classMutexSymbol] ??= new Mutex();
656
+ const tag = `${target.constructor.name}.${propertyName}`;
657
+ let guard;
658
+ if (!enableWarning) {
659
+ guard = await mutex.acquire(tag);
660
+ } else {
661
+ guard = await (0, import_debug.warnAfterTimeout)(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
662
+ }
663
+ try {
664
+ return await method.apply(this, args);
665
+ } finally {
666
+ guard.release();
667
+ }
668
+ };
669
+ Object.defineProperty(descriptor.value, "name", {
670
+ value: propertyName + "$synchronized"
671
+ });
672
+ };
550
673
  var trigger = (timeout) => {
551
674
  let callback;
552
675
  const promise = new Promise((resolve, reject) => {
@@ -740,7 +863,7 @@ var EMPTY_OBSERVABLE = MulticastObservable.of(null);
740
863
  var ObservableProvider = class {
741
864
  constructor() {
742
865
  this._handlers = /* @__PURE__ */ new Set();
743
- this._proxy = (0, import_util.createSetDispatch)({
866
+ this._proxy = (0, import_util2.createSetDispatch)({
744
867
  handlers: this._handlers
745
868
  });
746
869
  }
@@ -781,94 +904,319 @@ var CancellableObservableProvider = class extends ObservableProvider {
781
904
  this.callback.onCancelled?.();
782
905
  }
783
906
  };
784
- var Mutex = class {
785
- constructor() {
786
- this._queue = Promise.resolve();
787
- this._queueLength = 0;
788
- this._tag = null;
907
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
908
+ var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
909
+ var openResources = /* @__PURE__ */ new Set();
910
+ var handleSymbol = Symbol("checkLeaksHandle");
911
+ var trackResource = (resourceProvider) => {
912
+ if (!enabled) {
913
+ return () => {
914
+ };
789
915
  }
790
- get tag() {
791
- return this._tag;
916
+ const resource = resourceProvider();
917
+ openResources.add(resource);
918
+ return () => {
919
+ openResources.delete(resource);
920
+ };
921
+ };
922
+ var trackLeaks = (open, close) => (target) => {
923
+ if (!enabled) {
924
+ return;
792
925
  }
793
- isLocked() {
794
- return this._queueLength > 0;
926
+ const openMethod = target.prototype[open];
927
+ const closeMethod = target.prototype[close];
928
+ if (!openMethod || !closeMethod) {
929
+ throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
930
+ }
931
+ {
932
+ target.prototype[open] = async function(...args) {
933
+ this[handleSymbol] = trackResource(() => ({
934
+ name: target.name,
935
+ openStack: new import_debug4.StackTrace()
936
+ }));
937
+ return openMethod.apply(this, args);
938
+ };
939
+ Object.defineProperty(target.prototype[open], "name", {
940
+ value: open + "$checkLeaks"
941
+ });
942
+ }
943
+ {
944
+ target.prototype[close] = async function(...args) {
945
+ this[handleSymbol]?.();
946
+ return closeMethod.apply(this, args);
947
+ };
948
+ Object.defineProperty(target.prototype[close], "name", {
949
+ value: close + "$checkLeaks"
950
+ });
951
+ }
952
+ };
953
+ var dumpLeaks = () => {
954
+ if (!enabled) {
955
+ return;
956
+ }
957
+ import_log2.log.info(`Leaked resources ${openResources.size}:`, void 0, {
958
+ F: __dxlog_file3,
959
+ L: 82,
960
+ S: void 0,
961
+ C: (f, a) => f(...a)
962
+ });
963
+ for (const resource of openResources) {
964
+ import_log2.log.info(`- ${resource.name} at`, void 0, {
965
+ F: __dxlog_file3,
966
+ L: 84,
967
+ S: void 0,
968
+ C: (f, a) => f(...a)
969
+ });
970
+ import_log2.log.info(resource.openStack.getStack(1), void 0, {
971
+ F: __dxlog_file3,
972
+ L: 85,
973
+ S: void 0,
974
+ C: (f, a) => f(...a)
975
+ });
976
+ import_log2.log.info("\n", void 0, {
977
+ F: __dxlog_file3,
978
+ L: 86,
979
+ S: void 0,
980
+ C: (f, a) => f(...a)
981
+ });
982
+ }
983
+ };
984
+ if (enabled) {
985
+ global.dxDumpLeaks = dumpLeaks;
986
+ }
987
+ var DeferredTask = class {
988
+ constructor(_ctx, _callback) {
989
+ this._ctx = _ctx;
990
+ this._callback = _callback;
991
+ this._scheduled = false;
992
+ this._currentTask = null;
993
+ this._nextTask = new Trigger();
994
+ }
995
+ get scheduled() {
996
+ return this._scheduled;
795
997
  }
796
998
  /**
797
- * Acquires the lock.
798
- * Caller is responsible for releasing the lock using the returned callback.
799
- * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
800
- * @returns Release callback
999
+ * Schedule the task to run asynchronously.
801
1000
  */
802
- async acquire(tag) {
803
- const prev = this._queue;
804
- let guard;
805
- this._queueLength++;
806
- this._queue = new Promise((resolve) => {
807
- guard = new MutexGuard(() => {
808
- this._queueLength--;
809
- this._tag = null;
810
- resolve();
1001
+ schedule() {
1002
+ if (this._scheduled) {
1003
+ return;
1004
+ }
1005
+ scheduleTask(this._ctx, async () => {
1006
+ await this._currentTask;
1007
+ this._scheduled = false;
1008
+ const completionTrigger = this._nextTask;
1009
+ this._nextTask = new Trigger();
1010
+ this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
1011
+ completionTrigger.wake();
811
1012
  });
812
1013
  });
813
- await prev;
814
- if (tag !== void 0) {
815
- this._tag = tag;
816
- }
817
- return guard;
1014
+ this._scheduled = true;
818
1015
  }
819
1016
  /**
820
- * Waits for all previous executions to complete and then executes a given function.
821
- * Only a single function can be executed at a time.
822
- * Function are executed in the same order as `executeSynchronized` is called.
823
- * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
1017
+ * Schedule the task to run and wait for it to finish.
824
1018
  */
825
- async executeSynchronized(fun) {
826
- const guard = await this.acquire();
827
- try {
828
- return await fun();
829
- } finally {
830
- guard.release();
1019
+ async runBlocking() {
1020
+ if (this._ctx.disposed) {
1021
+ throw new import_context4.ContextDisposedError();
831
1022
  }
832
- }
833
- };
834
- var MutexGuard = class {
835
- constructor(_release) {
836
- this._release = _release;
1023
+ this.schedule();
1024
+ await this._nextTask.wait();
837
1025
  }
838
1026
  /**
839
- * Releases the lock.
1027
+ * Waits for the current task to finish if it is running.
1028
+ * Does not schedule a new task.
840
1029
  */
841
- release() {
842
- this._release();
1030
+ async join() {
1031
+ await this._currentTask;
843
1032
  }
844
- [Symbol.dispose]() {
845
- this.release();
1033
+ };
1034
+ var runInContext = (ctx, fn) => {
1035
+ try {
1036
+ fn();
1037
+ } catch (err) {
1038
+ ctx.raise(err);
846
1039
  }
847
1040
  };
848
- var classMutexSymbol = Symbol("class-mutex");
849
- var FORCE_DISABLE_WARNING = false;
850
- var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
851
- var synchronized = (target, propertyName, descriptor) => {
852
- const method = descriptor.value;
853
- descriptor.value = async function synchronizedMethod(...args) {
854
- const mutex = this[classMutexSymbol] ??= new Mutex();
855
- const tag = `${target.constructor.name}.${propertyName}`;
856
- let guard;
857
- if (!enableWarning) {
858
- guard = await mutex.acquire(tag);
859
- } else {
860
- guard = await (0, import_debug.warnAfterTimeout)(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
1041
+ var runInContextAsync = async (ctx, fn) => {
1042
+ try {
1043
+ await fn();
1044
+ } catch (err) {
1045
+ ctx.raise(err);
1046
+ }
1047
+ };
1048
+ var scheduleMicroTask = (ctx, fn) => {
1049
+ queueMicrotask(async () => {
1050
+ if (ctx.disposed) {
1051
+ return;
861
1052
  }
862
- try {
863
- return await method.apply(this, args);
864
- } finally {
865
- guard.release();
1053
+ await runInContextAsync(ctx, fn);
1054
+ });
1055
+ };
1056
+ var scheduleTask = (ctx, fn, afterMs) => {
1057
+ const clearTracking = trackResource(() => ({
1058
+ name: `task (${fn.name || "anonymous"})`,
1059
+ openStack: new import_debug3.StackTrace()
1060
+ }));
1061
+ const timeout = setTimeout(async () => {
1062
+ clearDispose();
1063
+ await runInContextAsync(ctx, fn);
1064
+ clearTracking();
1065
+ }, afterMs);
1066
+ const clearDispose = ctx.onDispose(() => {
1067
+ clearTracking();
1068
+ clearTimeout(timeout);
1069
+ });
1070
+ };
1071
+ var scheduleTaskInterval = (ctx, task, interval) => {
1072
+ const clearTracking = trackResource(() => ({
1073
+ name: `repeating task (${task.name || "anonymous"})`,
1074
+ openStack: new import_debug3.StackTrace()
1075
+ }));
1076
+ let timeoutId;
1077
+ const run = async () => {
1078
+ await runInContextAsync(ctx, task);
1079
+ if (ctx.disposed) {
1080
+ return;
866
1081
  }
1082
+ timeoutId = setTimeout(run, interval);
867
1083
  };
868
- Object.defineProperty(descriptor.value, "name", {
869
- value: propertyName + "$synchronized"
1084
+ timeoutId = setTimeout(run, interval);
1085
+ ctx.onDispose(() => {
1086
+ clearTracking();
1087
+ clearTimeout(timeoutId);
870
1088
  });
871
1089
  };
1090
+ var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
1091
+ const clearTracking = trackResource(() => ({
1092
+ name: `repeating task (${task.name || "anonymous"})`,
1093
+ openStack: new import_debug3.StackTrace()
1094
+ }));
1095
+ let timeoutId;
1096
+ let interval = initialInterval;
1097
+ const repeat = async () => {
1098
+ await runInContextAsync(ctx, task);
1099
+ if (ctx.disposed) {
1100
+ return;
1101
+ }
1102
+ interval *= 2;
1103
+ timeoutId = setTimeout(repeat, interval);
1104
+ };
1105
+ timeoutId = setTimeout(repeat, interval);
1106
+ ctx.onDispose(() => {
1107
+ clearTracking();
1108
+ clearTimeout(timeoutId);
1109
+ });
1110
+ };
1111
+ function _ts_decorate(decorators, target, key, desc) {
1112
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1113
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1114
+ 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;
1115
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1116
+ }
1117
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/common/async/src/persistent-lifecycle.ts";
1118
+ var INIT_RESTART_DELAY = 100;
1119
+ var DEFAULT_MAX_RESTART_DELAY = 5e3;
1120
+ var PersistentLifecycle = class extends import_context3.Resource {
1121
+ constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
1122
+ super();
1123
+ this._currentState = void 0;
1124
+ this._restartTask = void 0;
1125
+ this._restartAfter = 0;
1126
+ this._start = start;
1127
+ this._stop = stop;
1128
+ this._onRestart = onRestart;
1129
+ this._maxRestartDelay = maxRestartDelay;
1130
+ }
1131
+ get state() {
1132
+ return this._currentState;
1133
+ }
1134
+ async _open() {
1135
+ this._restartTask = new DeferredTask(this._ctx, async () => {
1136
+ try {
1137
+ await this._restart();
1138
+ } catch (err) {
1139
+ import_log.log.warn("Restart failed", {
1140
+ err
1141
+ }, {
1142
+ F: __dxlog_file4,
1143
+ L: 72,
1144
+ S: this,
1145
+ C: (f, a) => f(...a)
1146
+ });
1147
+ this._restartTask?.schedule();
1148
+ }
1149
+ });
1150
+ this._currentState = await this._start().catch((err) => {
1151
+ import_log.log.warn("Start failed", {
1152
+ err
1153
+ }, {
1154
+ F: __dxlog_file4,
1155
+ L: 77,
1156
+ S: this,
1157
+ C: (f, a) => f(...a)
1158
+ });
1159
+ this._restartTask?.schedule();
1160
+ return void 0;
1161
+ });
1162
+ }
1163
+ async _close() {
1164
+ await this._restartTask?.join();
1165
+ await this._stopCurrentState();
1166
+ this._restartTask = void 0;
1167
+ }
1168
+ async _restart() {
1169
+ (0, import_log.log)(`restarting in ${this._restartAfter}ms`, {
1170
+ state: this._lifecycleState
1171
+ }, {
1172
+ F: __dxlog_file4,
1173
+ L: 90,
1174
+ S: this,
1175
+ C: (f, a) => f(...a)
1176
+ });
1177
+ await this._stopCurrentState();
1178
+ if (this._lifecycleState !== import_context3.LifecycleState.OPEN) {
1179
+ return;
1180
+ }
1181
+ await (0, import_context3.cancelWithContext)(this._ctx, sleep(this._restartAfter));
1182
+ this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
1183
+ await (0, import_debug2.warnAfterTimeout)(5e3, "Connection establishment takes too long", async () => {
1184
+ this._currentState = await this._start();
1185
+ });
1186
+ this._restartAfter = 0;
1187
+ await this._onRestart?.();
1188
+ }
1189
+ async _stopCurrentState() {
1190
+ if (this._currentState) {
1191
+ try {
1192
+ await this._stop(this._currentState);
1193
+ } catch (err) {
1194
+ import_log.log.catch(err, void 0, {
1195
+ F: __dxlog_file4,
1196
+ L: 112,
1197
+ S: this,
1198
+ C: (f, a) => f(...a)
1199
+ });
1200
+ }
1201
+ this._currentState = void 0;
1202
+ }
1203
+ }
1204
+ /**
1205
+ * Scheduling restart should be done from outside.
1206
+ */
1207
+ scheduleRestart() {
1208
+ if (this._lifecycleState !== import_context3.LifecycleState.OPEN) {
1209
+ return;
1210
+ }
1211
+ this._restartTask.schedule();
1212
+ }
1213
+ };
1214
+ _ts_decorate([
1215
+ synchronized
1216
+ ], PersistentLifecycle.prototype, "_open", null);
1217
+ _ts_decorate([
1218
+ synchronized
1219
+ ], PersistentLifecycle.prototype, "scheduleRestart", null);
872
1220
  var sink = (emitter, event, count = 1) => {
873
1221
  const [getPromise, resolve] = trigger();
874
1222
  let counter = 0;
@@ -921,6 +1269,58 @@ var streamToArray = (stream) => {
921
1269
  }
922
1270
  return deferred;
923
1271
  };
1272
+ var TestStream = class extends import_node_stream.Duplex {
1273
+ constructor() {
1274
+ super(...arguments);
1275
+ this._received = Buffer.alloc(0);
1276
+ this._onWrite = new Event();
1277
+ }
1278
+ static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
1279
+ stream1.push("ping");
1280
+ stream2.push("pong");
1281
+ await Promise.all([
1282
+ stream2.assertReceivedAsync("ping", {
1283
+ timeout
1284
+ }),
1285
+ stream1.assertReceivedAsync("pong", {
1286
+ timeout
1287
+ })
1288
+ ]);
1289
+ }
1290
+ _write(chunk, encoding, callback) {
1291
+ this._received = Buffer.concat([
1292
+ this._received,
1293
+ chunk
1294
+ ]);
1295
+ this._onWrite.emit();
1296
+ callback();
1297
+ }
1298
+ _read(size) {
1299
+ }
1300
+ assertReceivedAsync(data, { timeout = 200 } = {}) {
1301
+ const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
1302
+ return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
1303
+ }
1304
+ };
1305
+ var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
1306
+ const stopTime = timeout ? Date.now() + timeout : 0;
1307
+ const trigger2 = new Trigger();
1308
+ const waiter = async () => {
1309
+ while (!stopTime || Date.now() < stopTime) {
1310
+ try {
1311
+ const value = await condition();
1312
+ if (value) {
1313
+ trigger2.wake(value);
1314
+ break;
1315
+ }
1316
+ } catch (err) {
1317
+ }
1318
+ await sleep(interval);
1319
+ }
1320
+ };
1321
+ setTimeout(waiter, 0);
1322
+ return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
1323
+ };
924
1324
  var Timer = class {
925
1325
  constructor(_callback) {
926
1326
  this._callback = _callback;
@@ -967,25 +1367,6 @@ var Timer = class {
967
1367
  return this;
968
1368
  }
969
1369
  };
970
- var waitForCondition = ({ condition, timeout = 0, interval = 10, error }) => {
971
- const stopTime = timeout ? Date.now() + timeout : 0;
972
- const trigger2 = new Trigger();
973
- const waiter = async () => {
974
- while (!stopTime || Date.now() < stopTime) {
975
- try {
976
- const value = await condition();
977
- if (value) {
978
- trigger2.wake(value);
979
- break;
980
- }
981
- } catch (err) {
982
- }
983
- await sleep(interval);
984
- }
985
- };
986
- setTimeout(waiter, 0);
987
- return timeout ? asyncTimeout(trigger2.wait(), timeout, error ?? new Error("Timeout")) : trigger2.wait();
988
- };
989
1370
  var until = (cb, timeout) => {
990
1371
  return new Promise((resolve, reject) => {
991
1372
  const t = timeout && setTimeout(() => {
@@ -1019,240 +1400,6 @@ var untilError = (cb) => {
1019
1400
  });
1020
1401
  });
1021
1402
  };
1022
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/async/src/track-leaks.ts";
1023
- var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
1024
- var openResources = /* @__PURE__ */ new Set();
1025
- var handleSymbol = Symbol("checkLeaksHandle");
1026
- var trackResource = (resourceProvider) => {
1027
- if (!enabled) {
1028
- return () => {
1029
- };
1030
- }
1031
- const resource = resourceProvider();
1032
- openResources.add(resource);
1033
- return () => {
1034
- openResources.delete(resource);
1035
- };
1036
- };
1037
- var trackLeaks = (open, close) => (target) => {
1038
- if (!enabled) {
1039
- return;
1040
- }
1041
- const openMethod = target.prototype[open];
1042
- const closeMethod = target.prototype[close];
1043
- if (!openMethod || !closeMethod) {
1044
- throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
1045
- }
1046
- {
1047
- target.prototype[open] = async function(...args) {
1048
- this[handleSymbol] = trackResource(() => ({
1049
- name: target.name,
1050
- openStack: new import_debug3.StackTrace()
1051
- }));
1052
- return openMethod.apply(this, args);
1053
- };
1054
- Object.defineProperty(target.prototype[open], "name", {
1055
- value: open + "$checkLeaks"
1056
- });
1057
- }
1058
- {
1059
- target.prototype[close] = async function(...args) {
1060
- this[handleSymbol]?.();
1061
- return closeMethod.apply(this, args);
1062
- };
1063
- Object.defineProperty(target.prototype[close], "name", {
1064
- value: close + "$checkLeaks"
1065
- });
1066
- }
1067
- };
1068
- var dumpLeaks = () => {
1069
- if (!enabled) {
1070
- return;
1071
- }
1072
- import_log.log.info(`Leaked resources ${openResources.size}:`, void 0, {
1073
- F: __dxlog_file3,
1074
- L: 82,
1075
- S: void 0,
1076
- C: (f, a) => f(...a)
1077
- });
1078
- for (const resource of openResources) {
1079
- import_log.log.info(`- ${resource.name} at`, void 0, {
1080
- F: __dxlog_file3,
1081
- L: 84,
1082
- S: void 0,
1083
- C: (f, a) => f(...a)
1084
- });
1085
- import_log.log.info(resource.openStack.getStack(1), void 0, {
1086
- F: __dxlog_file3,
1087
- L: 85,
1088
- S: void 0,
1089
- C: (f, a) => f(...a)
1090
- });
1091
- import_log.log.info("\n", void 0, {
1092
- F: __dxlog_file3,
1093
- L: 86,
1094
- S: void 0,
1095
- C: (f, a) => f(...a)
1096
- });
1097
- }
1098
- };
1099
- if (enabled) {
1100
- global.dxDumpLeaks = dumpLeaks;
1101
- }
1102
- var DeferredTask = class {
1103
- constructor(_ctx, _callback) {
1104
- this._ctx = _ctx;
1105
- this._callback = _callback;
1106
- this._scheduled = false;
1107
- this._currentTask = null;
1108
- this._nextTask = new Trigger();
1109
- }
1110
- /**
1111
- * Schedule the task to run asynchronously.
1112
- */
1113
- schedule() {
1114
- if (this._scheduled) {
1115
- return;
1116
- }
1117
- scheduleTask(this._ctx, async () => {
1118
- await this._currentTask;
1119
- this._scheduled = false;
1120
- const completionTrigger = this._nextTask;
1121
- this._nextTask = new Trigger();
1122
- this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
1123
- completionTrigger.wake();
1124
- });
1125
- });
1126
- this._scheduled = true;
1127
- }
1128
- /**
1129
- * Schedule the task to run and wait for it to finish.
1130
- */
1131
- async runBlocking() {
1132
- if (this._ctx.disposed) {
1133
- throw new import_context3.ContextDisposedError();
1134
- }
1135
- this.schedule();
1136
- await this._nextTask.wait();
1137
- }
1138
- /**
1139
- * Waits for the current task to finish if it is running.
1140
- * Does not schedule a new task.
1141
- */
1142
- async join() {
1143
- await this._currentTask;
1144
- }
1145
- };
1146
- var runInContext = (ctx, fn) => {
1147
- try {
1148
- fn();
1149
- } catch (err) {
1150
- ctx.raise(err);
1151
- }
1152
- };
1153
- var runInContextAsync = async (ctx, fn) => {
1154
- try {
1155
- await fn();
1156
- } catch (err) {
1157
- ctx.raise(err);
1158
- }
1159
- };
1160
- var scheduleMicroTask = (ctx, fn) => {
1161
- queueMicrotask(async () => {
1162
- if (ctx.disposed) {
1163
- return;
1164
- }
1165
- await runInContextAsync(ctx, fn);
1166
- });
1167
- };
1168
- var scheduleTask = (ctx, fn, afterMs) => {
1169
- const clearTracking = trackResource(() => ({
1170
- name: `task (${fn.name || "anonymous"})`,
1171
- openStack: new import_debug2.StackTrace()
1172
- }));
1173
- const timeout = setTimeout(async () => {
1174
- clearDispose();
1175
- await runInContextAsync(ctx, fn);
1176
- clearTracking();
1177
- }, afterMs);
1178
- const clearDispose = ctx.onDispose(() => {
1179
- clearTracking();
1180
- clearTimeout(timeout);
1181
- });
1182
- };
1183
- var scheduleTaskInterval = (ctx, task, interval) => {
1184
- const clearTracking = trackResource(() => ({
1185
- name: `repeating task (${task.name || "anonymous"})`,
1186
- openStack: new import_debug2.StackTrace()
1187
- }));
1188
- let timeoutId;
1189
- const run = async () => {
1190
- await runInContextAsync(ctx, task);
1191
- if (ctx.disposed) {
1192
- return;
1193
- }
1194
- timeoutId = setTimeout(run, interval);
1195
- };
1196
- timeoutId = setTimeout(run, interval);
1197
- ctx.onDispose(() => {
1198
- clearTracking();
1199
- clearTimeout(timeoutId);
1200
- });
1201
- };
1202
- var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
1203
- const clearTracking = trackResource(() => ({
1204
- name: `repeating task (${task.name || "anonymous"})`,
1205
- openStack: new import_debug2.StackTrace()
1206
- }));
1207
- let timeoutId;
1208
- let interval = initialInterval;
1209
- const repeat = async () => {
1210
- await runInContextAsync(ctx, task);
1211
- if (ctx.disposed) {
1212
- return;
1213
- }
1214
- interval *= 2;
1215
- timeoutId = setTimeout(repeat, interval);
1216
- };
1217
- timeoutId = setTimeout(repeat, interval);
1218
- ctx.onDispose(() => {
1219
- clearTracking();
1220
- clearTimeout(timeoutId);
1221
- });
1222
- };
1223
- var TestStream = class extends import_node_stream.Duplex {
1224
- constructor() {
1225
- super(...arguments);
1226
- this._received = Buffer.alloc(0);
1227
- this._onWrite = new Event();
1228
- }
1229
- static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
1230
- stream1.push("ping");
1231
- stream2.push("pong");
1232
- await Promise.all([
1233
- stream2.assertReceivedAsync("ping", {
1234
- timeout
1235
- }),
1236
- stream1.assertReceivedAsync("pong", {
1237
- timeout
1238
- })
1239
- ]);
1240
- }
1241
- _write(chunk, encoding, callback) {
1242
- this._received = Buffer.concat([
1243
- this._received,
1244
- chunk
1245
- ]);
1246
- this._onWrite.emit();
1247
- callback();
1248
- }
1249
- _read(size) {
1250
- }
1251
- assertReceivedAsync(data, { timeout = 200 } = {}) {
1252
- const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
1253
- return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
1254
- }
1255
- };
1256
1403
  var TIME_PERIOD = 1e3;
1257
1404
  var UpdateScheduler = class {
1258
1405
  constructor(_ctx, _callback, _params = {}) {
@@ -1334,6 +1481,7 @@ var UpdateScheduler = class {
1334
1481
  MutexGuard,
1335
1482
  Observable,
1336
1483
  ObservableProvider,
1484
+ PersistentLifecycle,
1337
1485
  PushStream,
1338
1486
  TestStream,
1339
1487
  TimeoutError,