@dxos/async 0.10.0 → 0.11.0
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.
- package/dist/lib/chunk-events.mjs +384 -0
- package/dist/lib/chunk-events.mjs.map +1 -0
- package/dist/lib/index.mjs +1327 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +30 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/task-scheduling.d.ts.map +1 -1
- package/dist/types/src/update-scheduler.d.ts +40 -6
- package/dist/types/src/update-scheduler.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -12
- package/src/index.ts +0 -1
- package/src/task-scheduling.ts +4 -0
- package/src/update-scheduler.test.ts +192 -2
- package/src/update-scheduler.ts +104 -30
- package/dist/lib/browser/index.mjs +0 -1677
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -1679
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
|
@@ -1,1677 +0,0 @@
|
|
|
1
|
-
// src/callback.ts
|
|
2
|
-
var promiseFromCallback = (run) => new Promise((resolve, reject) => {
|
|
3
|
-
run((error, value) => {
|
|
4
|
-
if (error) {
|
|
5
|
-
reject(error);
|
|
6
|
-
} else {
|
|
7
|
-
resolve(value);
|
|
8
|
-
}
|
|
9
|
-
});
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
// src/chain.ts
|
|
13
|
-
var chain = (chain2) => async (elements) => {
|
|
14
|
-
let result = await elements;
|
|
15
|
-
for (const part of chain2) {
|
|
16
|
-
result = await Promise.all(result.map(async (element) => await part(element)));
|
|
17
|
-
}
|
|
18
|
-
return result;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// src/cleanup.ts
|
|
22
|
-
import { ComplexMap } from "@dxos/util";
|
|
23
|
-
var combine = (...cleanupFns) => {
|
|
24
|
-
return () => {
|
|
25
|
-
cleanupFns.flat().filter((f) => typeof f === "function").forEach((cleanupFn) => cleanupFn());
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
var timeout = (cb, ms = 0) => {
|
|
29
|
-
const t = setTimeout(cb, ms);
|
|
30
|
-
return () => clearTimeout(t);
|
|
31
|
-
};
|
|
32
|
-
var interval = (cb, ms) => {
|
|
33
|
-
const t = setInterval(cb, ms);
|
|
34
|
-
return () => clearInterval(t);
|
|
35
|
-
};
|
|
36
|
-
var addEventListener = (target, type, listener, options) => {
|
|
37
|
-
target.addEventListener(type, listener, options);
|
|
38
|
-
return () => target.removeEventListener(type, listener, options);
|
|
39
|
-
};
|
|
40
|
-
var SubscriptionList = class {
|
|
41
|
-
_cleanups = [];
|
|
42
|
-
add(...cb) {
|
|
43
|
-
this._cleanups.push(...cb);
|
|
44
|
-
return this;
|
|
45
|
-
}
|
|
46
|
-
clear() {
|
|
47
|
-
this._cleanups.forEach((cb) => cb());
|
|
48
|
-
this._cleanups.length = 0;
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
var SubscriptionSet = class {
|
|
52
|
-
_cleanupMap;
|
|
53
|
-
constructor(keyProjection) {
|
|
54
|
-
this._cleanupMap = new ComplexMap(keyProjection);
|
|
55
|
-
}
|
|
56
|
-
set(key, cb) {
|
|
57
|
-
this._cleanupMap.set(key, cb);
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
clear() {
|
|
61
|
-
this._cleanupMap.forEach((cb) => cb());
|
|
62
|
-
this._cleanupMap.clear();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// src/debounce.ts
|
|
67
|
-
var delay = (cb, delay2 = 100) => {
|
|
68
|
-
let pending = false;
|
|
69
|
-
return (...args) => {
|
|
70
|
-
if (pending) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
pending = true;
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
try {
|
|
76
|
-
cb(...args);
|
|
77
|
-
} finally {
|
|
78
|
-
pending = false;
|
|
79
|
-
}
|
|
80
|
-
}, delay2);
|
|
81
|
-
};
|
|
82
|
-
};
|
|
83
|
-
var debounce = (cb, delay2 = 100) => {
|
|
84
|
-
let t;
|
|
85
|
-
return (...args) => {
|
|
86
|
-
clearTimeout(t);
|
|
87
|
-
t = setTimeout(() => cb(...args), delay2);
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
var throttle = (cb, delay2 = 100) => {
|
|
91
|
-
let lastCall = 0;
|
|
92
|
-
return (...args) => {
|
|
93
|
-
const now = Date.now();
|
|
94
|
-
if (now - lastCall >= delay2) {
|
|
95
|
-
cb(...args);
|
|
96
|
-
lastCall = now;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
};
|
|
100
|
-
var debounceAndThrottle = (cb, delay2 = 100) => {
|
|
101
|
-
let timeout2;
|
|
102
|
-
let lastCall = 0;
|
|
103
|
-
return (...args) => {
|
|
104
|
-
const now = Date.now();
|
|
105
|
-
const delta = now - lastCall;
|
|
106
|
-
clearTimeout(timeout2);
|
|
107
|
-
if (delta >= delay2) {
|
|
108
|
-
cb(...args);
|
|
109
|
-
lastCall = now;
|
|
110
|
-
} else {
|
|
111
|
-
timeout2 = setTimeout(() => {
|
|
112
|
-
cb(...args);
|
|
113
|
-
lastCall = Date.now();
|
|
114
|
-
}, delay2 - delta);
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// src/errors.ts
|
|
120
|
-
var toError = (err) => err === void 0 || typeof err === "string" ? new Error(err) : err;
|
|
121
|
-
var TimeoutError = class extends Error {
|
|
122
|
-
constructor(timeout2, label) {
|
|
123
|
-
super(timeout2 ? `Timeout [${timeout2.toLocaleString()}ms]${label === void 0 ? "" : `: ${label}`}` : "Timeout");
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
var observableError = (observable, err) => {
|
|
127
|
-
if (err instanceof TimeoutError) {
|
|
128
|
-
observable.callback.onTimeout?.(err);
|
|
129
|
-
} else {
|
|
130
|
-
observable.callback.onError(toError(err));
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// src/timeout.ts
|
|
135
|
-
import { ContextDisposedError } from "@dxos/context";
|
|
136
|
-
var sleep = (ms) => {
|
|
137
|
-
return new Promise((resolve) => {
|
|
138
|
-
const finish = Date.now() + ms;
|
|
139
|
-
const sleeper = () => {
|
|
140
|
-
const delta = finish - Date.now();
|
|
141
|
-
if (delta > 0) {
|
|
142
|
-
setTimeout(sleeper, delta);
|
|
143
|
-
} else {
|
|
144
|
-
resolve();
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
sleeper();
|
|
148
|
-
});
|
|
149
|
-
};
|
|
150
|
-
var sleepWithContext = (ctx, ms) => {
|
|
151
|
-
const error = new ContextDisposedError();
|
|
152
|
-
return new Promise((resolve, reject) => {
|
|
153
|
-
if (ctx.disposed) {
|
|
154
|
-
reject(error);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const timeout2 = setTimeout(() => {
|
|
158
|
-
clearDispose();
|
|
159
|
-
resolve();
|
|
160
|
-
}, ms);
|
|
161
|
-
const clearDispose = ctx.onDispose(() => {
|
|
162
|
-
clearTimeout(timeout2);
|
|
163
|
-
reject(error);
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
};
|
|
167
|
-
var asyncReturn = () => sleep(0);
|
|
168
|
-
var asyncTimeout = async (promise, timeout2, err) => {
|
|
169
|
-
if (typeof promise === "function") {
|
|
170
|
-
throw new Error("First argument must be a promise.");
|
|
171
|
-
}
|
|
172
|
-
let timeoutId;
|
|
173
|
-
const throwable = err === void 0 || typeof err === "string" ? new TimeoutError(timeout2, err) : err;
|
|
174
|
-
const timeoutPromise = new Promise((resolve, reject) => {
|
|
175
|
-
timeoutId = setTimeout(() => {
|
|
176
|
-
reject(throwable);
|
|
177
|
-
}, timeout2);
|
|
178
|
-
unrefTimeout(timeoutId);
|
|
179
|
-
});
|
|
180
|
-
return await Promise.race([
|
|
181
|
-
promise,
|
|
182
|
-
timeoutPromise
|
|
183
|
-
]).finally(() => {
|
|
184
|
-
clearTimeout(timeoutId);
|
|
185
|
-
});
|
|
186
|
-
};
|
|
187
|
-
var unrefTimeout = (timeoutId) => {
|
|
188
|
-
if (typeof timeoutId === "object" && "unref" in timeoutId) {
|
|
189
|
-
timeoutId.unref();
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// src/event-emitter.ts
|
|
194
|
-
var onEvent = (eventEmitter, eventName, callback) => {
|
|
195
|
-
eventEmitter.on(eventName, callback);
|
|
196
|
-
return () => eventEmitter.off(eventName, callback);
|
|
197
|
-
};
|
|
198
|
-
var addListener = (eventEmitter, eventName, callback) => {
|
|
199
|
-
const off = onEvent(eventEmitter, eventName, callback);
|
|
200
|
-
return {
|
|
201
|
-
remove: () => off()
|
|
202
|
-
};
|
|
203
|
-
};
|
|
204
|
-
var waitForEvent = (eventEmitter, eventName, test, timeout2, error) => {
|
|
205
|
-
let off;
|
|
206
|
-
const promise = new Promise((resolve) => {
|
|
207
|
-
off = onEvent(eventEmitter, eventName, (...args) => {
|
|
208
|
-
if (!test || test(...args)) {
|
|
209
|
-
resolve(...args);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
return timeout2 ? asyncTimeout(promise, timeout2, error ?? new Error()).finally(off) : promise.finally(off);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
// src/events.ts
|
|
217
|
-
import { Context } from "@dxos/context";
|
|
218
|
-
var DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;
|
|
219
|
-
var Event = class _Event {
|
|
220
|
-
/**
|
|
221
|
-
* Wrap objects that have on/off style event emitters.
|
|
222
|
-
*/
|
|
223
|
-
static wrap(emitter, eventName) {
|
|
224
|
-
const event = new _Event();
|
|
225
|
-
event.addEffect(() => {
|
|
226
|
-
const onEvent2 = (data) => event.emit(data);
|
|
227
|
-
emitter.on(eventName, onEvent2);
|
|
228
|
-
return () => emitter.off(eventName, onEvent2);
|
|
229
|
-
});
|
|
230
|
-
return event;
|
|
231
|
-
}
|
|
232
|
-
_listeners = /* @__PURE__ */ new Set();
|
|
233
|
-
_effects = /* @__PURE__ */ new Set();
|
|
234
|
-
/**
|
|
235
|
-
* Emit an event.
|
|
236
|
-
* In most cases should only be called by the class or entity containing the event.
|
|
237
|
-
* All listeners are called in order of subscription with persistent ones first.
|
|
238
|
-
* Listeners are called synchronously in the same stack.
|
|
239
|
-
* A thrown exception in the listener will stop the event from being emitted to the rest of the listeners.
|
|
240
|
-
*
|
|
241
|
-
* @param data param that will be passed to all listeners.
|
|
242
|
-
*/
|
|
243
|
-
emit(data) {
|
|
244
|
-
for (const listener of this._listeners) {
|
|
245
|
-
listener.trigger(data);
|
|
246
|
-
if (listener.once) {
|
|
247
|
-
this._listeners.delete(listener);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Emit an event and wait for async listeners to complete.
|
|
253
|
-
* In most cases should only be called by the class or entity containing the event.
|
|
254
|
-
* All listeners are called in order of subscription with persistent ones first.
|
|
255
|
-
* Listeners are called sequentially.
|
|
256
|
-
*
|
|
257
|
-
* @param data param that will be passed to all listeners.
|
|
258
|
-
*/
|
|
259
|
-
async emitAsync(data) {
|
|
260
|
-
for (const listener of this._listeners) {
|
|
261
|
-
await listener.triggerAsync(data);
|
|
262
|
-
if (listener.once) {
|
|
263
|
-
this._listeners.delete(listener);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
on(_ctx, _callback, options) {
|
|
268
|
-
const [ctx, callback] = _ctx instanceof Context ? [
|
|
269
|
-
_ctx,
|
|
270
|
-
_callback
|
|
271
|
-
] : [
|
|
272
|
-
new Context(),
|
|
273
|
-
_ctx
|
|
274
|
-
];
|
|
275
|
-
const weak = !!options?.weak;
|
|
276
|
-
const once = !!options?.once;
|
|
277
|
-
const listener = new EventListener(this, callback, ctx, once, weak);
|
|
278
|
-
this._addListener(listener);
|
|
279
|
-
return () => {
|
|
280
|
-
this._removeListener(listener);
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Unsubscribe this callback from new events. Includes persistent and once-listeners.
|
|
285
|
-
* NOTE: It is recommended to use `Event.on`'s return value instead.
|
|
286
|
-
* If the callback is not subscribed this is no-op.
|
|
287
|
-
*
|
|
288
|
-
* @param callback
|
|
289
|
-
*/
|
|
290
|
-
off(callback) {
|
|
291
|
-
for (const listener of this._listeners) {
|
|
292
|
-
if (listener.derefCallback() === callback) {
|
|
293
|
-
this._removeListener(listener);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
once(_ctx, _callback) {
|
|
298
|
-
const [ctx, callback] = _ctx instanceof Context ? [
|
|
299
|
-
_ctx,
|
|
300
|
-
_callback
|
|
301
|
-
] : [
|
|
302
|
-
new Context(),
|
|
303
|
-
_ctx
|
|
304
|
-
];
|
|
305
|
-
const listener = new EventListener(this, callback, ctx, true, false);
|
|
306
|
-
this._addListener(listener);
|
|
307
|
-
return () => {
|
|
308
|
-
this._removeListener(listener);
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* An async iterator that iterates over events.
|
|
313
|
-
* This iterator runs indefinitely.
|
|
314
|
-
*/
|
|
315
|
-
async *[Symbol.asyncIterator]() {
|
|
316
|
-
while (true) {
|
|
317
|
-
yield await new Promise((resolve) => {
|
|
318
|
-
this.once(resolve);
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Returns a promise that resolves with the first event emitted that matches the provided predicate.
|
|
324
|
-
*
|
|
325
|
-
* @param predicate
|
|
326
|
-
*/
|
|
327
|
-
waitFor(predicate) {
|
|
328
|
-
return new Promise((resolve) => {
|
|
329
|
-
const unsubscribe = this.on((data) => {
|
|
330
|
-
if (predicate(data)) {
|
|
331
|
-
unsubscribe();
|
|
332
|
-
resolve(data);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Returns a promise that resolves once a specific number of events was emitted since this method was called.
|
|
339
|
-
*
|
|
340
|
-
* @param expectedCount
|
|
341
|
-
*/
|
|
342
|
-
waitForCount(expectedCount) {
|
|
343
|
-
let count = 0;
|
|
344
|
-
return this.waitFor(() => ++count === expectedCount);
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Similar to waitFor, but the promise resolves immediately if the condition is already true.
|
|
348
|
-
*/
|
|
349
|
-
// TODO(burdon): Should pass event property to predicate.
|
|
350
|
-
async waitForCondition(predicate) {
|
|
351
|
-
if (!predicate()) {
|
|
352
|
-
await this.waitFor(predicate);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Returns the number of persistent listeners.
|
|
357
|
-
*/
|
|
358
|
-
listenerCount() {
|
|
359
|
-
return this._listeners.size;
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Add a side effect that will be activated once the event has at least one subscriber.
|
|
363
|
-
* The provided callback can return a function that will be used to clean up after the last subscriber unsubscribes from the event.
|
|
364
|
-
* The API is similar to `useEffect` from React.
|
|
365
|
-
*
|
|
366
|
-
* ## Example:
|
|
367
|
-
* ```typescript
|
|
368
|
-
* event.addEffect(() => {
|
|
369
|
-
* // do stuff
|
|
370
|
-
* return () => {
|
|
371
|
-
* // clean-up
|
|
372
|
-
* };
|
|
373
|
-
* });
|
|
374
|
-
* ```
|
|
375
|
-
*
|
|
376
|
-
* @returns Callback that will remove this effect once called.
|
|
377
|
-
*/
|
|
378
|
-
addEffect(effect) {
|
|
379
|
-
const handle = {
|
|
380
|
-
effect,
|
|
381
|
-
cleanup: void 0
|
|
382
|
-
};
|
|
383
|
-
if (this.listenerCount() > 0) {
|
|
384
|
-
handle.cleanup = handle.effect();
|
|
385
|
-
}
|
|
386
|
-
this._effects.add(handle);
|
|
387
|
-
return () => {
|
|
388
|
-
handle.cleanup?.();
|
|
389
|
-
this._effects.delete(handle);
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Triggers an event with at least `timeout` milliseconds between each event.
|
|
394
|
-
* If the event is triggered more often, the event is delayed until the timeout is reached.
|
|
395
|
-
* If event is emitted for the first time or event wasn't fired for `timeout` milliseconds,
|
|
396
|
-
* the event is emitted after `timeout / 8` ms.
|
|
397
|
-
*/
|
|
398
|
-
// TODO(burdon): Factor out generic function.
|
|
399
|
-
debounce(timeout2 = 0) {
|
|
400
|
-
let firing;
|
|
401
|
-
let lastFired;
|
|
402
|
-
const debouncedEvent = new _Event();
|
|
403
|
-
debouncedEvent.addEffect(() => {
|
|
404
|
-
const unsubscribe = this.on(() => {
|
|
405
|
-
if (!firing) {
|
|
406
|
-
const fireIn = !lastFired || Date.now() - lastFired > timeout2 ? timeout2 / 8 : timeout2;
|
|
407
|
-
firing = setTimeout(() => {
|
|
408
|
-
lastFired = Date.now();
|
|
409
|
-
firing = void 0;
|
|
410
|
-
debouncedEvent.emit();
|
|
411
|
-
}, fireIn);
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
return () => {
|
|
415
|
-
unsubscribe();
|
|
416
|
-
clearTimeout(firing);
|
|
417
|
-
};
|
|
418
|
-
});
|
|
419
|
-
return debouncedEvent;
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Turn any variant of `Event<T>` into an `Event<void>` discarding the callback parameter.
|
|
423
|
-
*/
|
|
424
|
-
discardParameter() {
|
|
425
|
-
return this;
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Pipe the events into another event.
|
|
429
|
-
* @param event
|
|
430
|
-
*/
|
|
431
|
-
pipeInto(event) {
|
|
432
|
-
return this.on((data) => event.emit(data));
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Overridden to not return implementation details.
|
|
436
|
-
*/
|
|
437
|
-
toJSON() {
|
|
438
|
-
return {
|
|
439
|
-
listenerCount: this.listenerCount()
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
_addListener(listener) {
|
|
443
|
-
this._listeners.add(listener);
|
|
444
|
-
if (this.listenerCount() === 1) {
|
|
445
|
-
this._runEffects();
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* @internal
|
|
450
|
-
*/
|
|
451
|
-
_removeListener(listener) {
|
|
452
|
-
this._listeners.delete(listener);
|
|
453
|
-
listener.remove();
|
|
454
|
-
if (this.listenerCount() === 0) {
|
|
455
|
-
this._cleanupEffects();
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
_runEffects() {
|
|
459
|
-
for (const handle of this._effects) {
|
|
460
|
-
handle.cleanup = handle.effect();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
_cleanupEffects() {
|
|
464
|
-
for (const handle of this._effects) {
|
|
465
|
-
handle.cleanup?.();
|
|
466
|
-
handle.cleanup = void 0;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
var EventListener = class {
|
|
471
|
-
ctx;
|
|
472
|
-
once;
|
|
473
|
-
weak;
|
|
474
|
-
callback;
|
|
475
|
-
_clearDispose = void 0;
|
|
476
|
-
constructor(event, listener, ctx, once, weak) {
|
|
477
|
-
this.ctx = ctx;
|
|
478
|
-
this.once = once;
|
|
479
|
-
this.weak = weak;
|
|
480
|
-
this._clearDispose = ctx.onDispose(() => {
|
|
481
|
-
event._removeListener(this);
|
|
482
|
-
});
|
|
483
|
-
if (weak) {
|
|
484
|
-
this.callback = new WeakRef(listener);
|
|
485
|
-
weakListeners().registry?.register(listener, {
|
|
486
|
-
event: new WeakRef(event),
|
|
487
|
-
listener: this
|
|
488
|
-
}, this);
|
|
489
|
-
} else {
|
|
490
|
-
this.callback = listener;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
derefCallback() {
|
|
494
|
-
return this.weak ? this.callback.deref() : this.callback;
|
|
495
|
-
}
|
|
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) {
|
|
511
|
-
try {
|
|
512
|
-
const callback = this.derefCallback();
|
|
513
|
-
await callback?.(data);
|
|
514
|
-
} catch (err) {
|
|
515
|
-
this.ctx.raise(err);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
remove() {
|
|
519
|
-
this._clearDispose?.();
|
|
520
|
-
weakListeners().registry?.unregister(this);
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
var weakListenersState = null;
|
|
524
|
-
var FINALIZATION_REGISTRY_SUPPORTED = !!globalThis.FinalizationRegistry;
|
|
525
|
-
var weakListeners = () => {
|
|
526
|
-
if (!FINALIZATION_REGISTRY_SUPPORTED) {
|
|
527
|
-
return {
|
|
528
|
-
registry: void 0
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
weakListenersState ??= new FinalizationRegistry(({ event, listener }) => {
|
|
532
|
-
event.deref()?._removeListener(listener);
|
|
533
|
-
});
|
|
534
|
-
return {
|
|
535
|
-
registry: weakListenersState
|
|
536
|
-
};
|
|
537
|
-
};
|
|
538
|
-
|
|
539
|
-
// src/mutex.ts
|
|
540
|
-
import "@dxos/util";
|
|
541
|
-
import { warnAfterTimeout } from "@dxos/debug";
|
|
542
|
-
var Mutex = class {
|
|
543
|
-
_queue = Promise.resolve();
|
|
544
|
-
_queueLength = 0;
|
|
545
|
-
_tag = null;
|
|
546
|
-
get tag() {
|
|
547
|
-
return this._tag;
|
|
548
|
-
}
|
|
549
|
-
isLocked() {
|
|
550
|
-
return this._queueLength > 0;
|
|
551
|
-
}
|
|
552
|
-
/**
|
|
553
|
-
* Acquires the lock.
|
|
554
|
-
* Caller is responsible for releasing the lock using the returned callback.
|
|
555
|
-
* NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.
|
|
556
|
-
* @returns Release callback
|
|
557
|
-
*/
|
|
558
|
-
async acquire(tag) {
|
|
559
|
-
const prev = this._queue;
|
|
560
|
-
let guard;
|
|
561
|
-
this._queueLength++;
|
|
562
|
-
this._queue = new Promise((resolve) => {
|
|
563
|
-
guard = new MutexGuard(() => {
|
|
564
|
-
this._queueLength--;
|
|
565
|
-
this._tag = null;
|
|
566
|
-
resolve();
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
await prev;
|
|
570
|
-
if (tag !== void 0) {
|
|
571
|
-
this._tag = tag;
|
|
572
|
-
}
|
|
573
|
-
return guard;
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Waits for all previous executions to complete and then executes a given function.
|
|
577
|
-
* Only a single function can be executed at a time.
|
|
578
|
-
* Function are executed in the same order as `executeSynchronized` is called.
|
|
579
|
-
* WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.
|
|
580
|
-
*/
|
|
581
|
-
async executeSynchronized(fun) {
|
|
582
|
-
const guard = await this.acquire();
|
|
583
|
-
try {
|
|
584
|
-
return await fun();
|
|
585
|
-
} finally {
|
|
586
|
-
guard.release();
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
var MutexGuard = class {
|
|
591
|
-
_release;
|
|
592
|
-
constructor(_release) {
|
|
593
|
-
this._release = _release;
|
|
594
|
-
}
|
|
595
|
-
/**
|
|
596
|
-
* Releases the lock.
|
|
597
|
-
*/
|
|
598
|
-
release() {
|
|
599
|
-
this._release();
|
|
600
|
-
}
|
|
601
|
-
[Symbol.dispose]() {
|
|
602
|
-
this.release();
|
|
603
|
-
}
|
|
604
|
-
};
|
|
605
|
-
var classMutexSymbol = /* @__PURE__ */ Symbol("class-mutex");
|
|
606
|
-
var FORCE_DISABLE_WARNING = false;
|
|
607
|
-
var enableWarning = !FORCE_DISABLE_WARNING && globalThis.mochaExecutor;
|
|
608
|
-
var synchronized = (target, propertyName, descriptor) => {
|
|
609
|
-
const method = descriptor.value;
|
|
610
|
-
descriptor.value = async function synchronizedMethod(...args) {
|
|
611
|
-
const mutex = this[classMutexSymbol] ??= new Mutex();
|
|
612
|
-
const tag = `${target.constructor.name}.${propertyName}`;
|
|
613
|
-
let guard;
|
|
614
|
-
if (!enableWarning) {
|
|
615
|
-
guard = await mutex.acquire(tag);
|
|
616
|
-
} else {
|
|
617
|
-
guard = await warnAfterTimeout(1e4, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));
|
|
618
|
-
}
|
|
619
|
-
try {
|
|
620
|
-
return await method.apply(this, args);
|
|
621
|
-
} finally {
|
|
622
|
-
guard.release();
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
Object.defineProperty(descriptor.value, "name", {
|
|
626
|
-
value: propertyName + "$synchronized"
|
|
627
|
-
});
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
// src/observable.ts
|
|
631
|
-
import Observable from "zen-observable";
|
|
632
|
-
import PushStream from "zen-push";
|
|
633
|
-
|
|
634
|
-
// src/trigger.ts
|
|
635
|
-
import { invariant } from "@dxos/invariant";
|
|
636
|
-
var trigger = (timeout2) => {
|
|
637
|
-
let callback;
|
|
638
|
-
const promise = new Promise((resolve, reject) => {
|
|
639
|
-
if (timeout2) {
|
|
640
|
-
setTimeout(() => reject(new Error(`Timed out after ${timeout2.toLocaleString()}ms`)), timeout2);
|
|
641
|
-
}
|
|
642
|
-
callback = resolve;
|
|
643
|
-
});
|
|
644
|
-
const provider = () => promise;
|
|
645
|
-
const resolver = (value) => callback(value);
|
|
646
|
-
return [
|
|
647
|
-
provider,
|
|
648
|
-
resolver
|
|
649
|
-
];
|
|
650
|
-
};
|
|
651
|
-
var TriggerState = /* @__PURE__ */ (function(TriggerState2) {
|
|
652
|
-
TriggerState2["WAITING"] = "WAITING";
|
|
653
|
-
TriggerState2["RESOLVED"] = "RESOLVED";
|
|
654
|
-
TriggerState2["REJECTED"] = "REJECTED";
|
|
655
|
-
return TriggerState2;
|
|
656
|
-
})({});
|
|
657
|
-
var Trigger = class {
|
|
658
|
-
_options;
|
|
659
|
-
_promise;
|
|
660
|
-
_resolve;
|
|
661
|
-
_reject;
|
|
662
|
-
_state = "WAITING";
|
|
663
|
-
constructor(_options = {
|
|
664
|
-
autoReset: false
|
|
665
|
-
}) {
|
|
666
|
-
this._options = _options;
|
|
667
|
-
this.reset();
|
|
668
|
-
}
|
|
669
|
-
get state() {
|
|
670
|
-
return this._state;
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* Wait until wake is called, with optional timeout.
|
|
674
|
-
*/
|
|
675
|
-
async wait({ timeout: timeout2 } = {}) {
|
|
676
|
-
if (timeout2) {
|
|
677
|
-
return asyncTimeout(this._promise, timeout2, new TimeoutError(timeout2));
|
|
678
|
-
} else {
|
|
679
|
-
return this._promise;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Wake blocked callers (if any).
|
|
684
|
-
* NOOP if the trigger is already resolved.
|
|
685
|
-
*/
|
|
686
|
-
wake(value) {
|
|
687
|
-
if (this._state !== "WAITING") {
|
|
688
|
-
return this;
|
|
689
|
-
}
|
|
690
|
-
this._state = "RESOLVED";
|
|
691
|
-
this._resolve(value);
|
|
692
|
-
if (this._options.autoReset) {
|
|
693
|
-
return this.reset();
|
|
694
|
-
}
|
|
695
|
-
return this;
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Reset promise (new waiters will wait).
|
|
699
|
-
*/
|
|
700
|
-
reset() {
|
|
701
|
-
this._state = "WAITING";
|
|
702
|
-
this._promise = new Promise((resolve, reject) => {
|
|
703
|
-
this._resolve = resolve;
|
|
704
|
-
this._reject = reject;
|
|
705
|
-
});
|
|
706
|
-
this._promise.catch(() => {
|
|
707
|
-
});
|
|
708
|
-
return this;
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Throw error to blocked callers (if any).
|
|
712
|
-
* NOOP if the trigger is already resolved.
|
|
713
|
-
*/
|
|
714
|
-
throw(error) {
|
|
715
|
-
if (this._state !== "WAITING") {
|
|
716
|
-
return this;
|
|
717
|
-
}
|
|
718
|
-
this._state = "REJECTED";
|
|
719
|
-
this._reject(error);
|
|
720
|
-
if (this._options.autoReset) {
|
|
721
|
-
return this.reset();
|
|
722
|
-
}
|
|
723
|
-
return this;
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
var latch = ({ count = 1, timeout: timeout2 } = {}) => {
|
|
727
|
-
invariant(count >= 0);
|
|
728
|
-
let t;
|
|
729
|
-
let doResolve;
|
|
730
|
-
let doReject;
|
|
731
|
-
const promise = new Promise((resolve, reject) => {
|
|
732
|
-
doResolve = (value) => {
|
|
733
|
-
clearTimeout(t);
|
|
734
|
-
resolve(value);
|
|
735
|
-
};
|
|
736
|
-
doReject = (err) => {
|
|
737
|
-
clearTimeout(t);
|
|
738
|
-
reject(err);
|
|
739
|
-
};
|
|
740
|
-
});
|
|
741
|
-
if (count === 0) {
|
|
742
|
-
setTimeout(() => {
|
|
743
|
-
doResolve(0);
|
|
744
|
-
});
|
|
745
|
-
} else {
|
|
746
|
-
if (timeout2) {
|
|
747
|
-
t = setTimeout(() => {
|
|
748
|
-
doReject(new Error(`Timed out after ${timeout2.toLocaleString()}ms`));
|
|
749
|
-
}, timeout2);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
let i = 0;
|
|
753
|
-
return [
|
|
754
|
-
async () => await promise,
|
|
755
|
-
() => {
|
|
756
|
-
if (++i === count) {
|
|
757
|
-
doResolve(i);
|
|
758
|
-
}
|
|
759
|
-
return i;
|
|
760
|
-
},
|
|
761
|
-
(err) => doReject(err)
|
|
762
|
-
];
|
|
763
|
-
};
|
|
764
|
-
|
|
765
|
-
// src/observable.ts
|
|
766
|
-
var MulticastObservable = class _MulticastObservable extends Observable {
|
|
767
|
-
_value;
|
|
768
|
-
_observers = /* @__PURE__ */ new Set();
|
|
769
|
-
_observable;
|
|
770
|
-
_completed = new Trigger();
|
|
771
|
-
constructor(subscriber, _value) {
|
|
772
|
-
super((observer) => this._subscribe(observer)), this._value = _value;
|
|
773
|
-
this._observable = typeof subscriber === "function" ? new Observable(subscriber) : subscriber;
|
|
774
|
-
this._observable.subscribe(this._handlers);
|
|
775
|
-
}
|
|
776
|
-
static from(value, initialValue) {
|
|
777
|
-
if ("emit" in value) {
|
|
778
|
-
return new _MulticastObservable((observer) => {
|
|
779
|
-
value.on((data) => {
|
|
780
|
-
observer.next(data);
|
|
781
|
-
});
|
|
782
|
-
}, initialValue);
|
|
783
|
-
}
|
|
784
|
-
const observable = Observable.from(value);
|
|
785
|
-
return new _MulticastObservable(observable, initialValue);
|
|
786
|
-
}
|
|
787
|
-
static of(...items) {
|
|
788
|
-
return new _MulticastObservable(Observable.of(...items.slice(1)), items[0]);
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* @returns Stable reference to an observable that always returns `undefined`.
|
|
792
|
-
*/
|
|
793
|
-
static empty() {
|
|
794
|
-
return EMPTY_OBSERVABLE;
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* Get the current value of the observable.
|
|
798
|
-
*/
|
|
799
|
-
get() {
|
|
800
|
-
if (this._value === void 0) {
|
|
801
|
-
throw new Error("MulticastObservable is not initialized.");
|
|
802
|
-
}
|
|
803
|
-
return this._value;
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Wait for the observable to complete.
|
|
807
|
-
*
|
|
808
|
-
* @returns Promise that resolves to the value of the observable at the time of completion.
|
|
809
|
-
*/
|
|
810
|
-
async wait({ timeout: timeout2 } = {}) {
|
|
811
|
-
await this._completed.wait({
|
|
812
|
-
timeout: timeout2
|
|
813
|
-
});
|
|
814
|
-
return this.get();
|
|
815
|
-
}
|
|
816
|
-
forEach(callback) {
|
|
817
|
-
return this._observable.forEach(callback);
|
|
818
|
-
}
|
|
819
|
-
map(callback) {
|
|
820
|
-
return new _MulticastObservable(this._observable.map(callback), this._value && callback(this._value));
|
|
821
|
-
}
|
|
822
|
-
filter(callback) {
|
|
823
|
-
return new _MulticastObservable(this._observable.filter(callback), this._value && callback(this._value) ? this._value : void 0);
|
|
824
|
-
}
|
|
825
|
-
reduce(callback, initialValue) {
|
|
826
|
-
return new _MulticastObservable(initialValue ? this._observable.reduce(callback, initialValue) : this._observable.reduce(callback), initialValue ?? this._value);
|
|
827
|
-
}
|
|
828
|
-
flatMap(callback) {
|
|
829
|
-
return new _MulticastObservable(this._observable.flatMap(callback), this._value && callback(this._value).get());
|
|
830
|
-
}
|
|
831
|
-
concat(...observables) {
|
|
832
|
-
return new _MulticastObservable(this._observable.concat(...observables), this._value);
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* Concatenates multicast observables without losing the current value.
|
|
836
|
-
* @param reducer reduces the values of any multicast observables being concatenated into a single value
|
|
837
|
-
* @param observables observables to concatenate
|
|
838
|
-
* @returns concatenated observable
|
|
839
|
-
*/
|
|
840
|
-
losslessConcat(reducer, ...observables) {
|
|
841
|
-
const multicast = observables.filter((observable) => observable instanceof _MulticastObservable);
|
|
842
|
-
const value = reducer(this._value, multicast.map((observable) => observable.get()));
|
|
843
|
-
return new _MulticastObservable(this._observable.concat(...observables), value);
|
|
844
|
-
}
|
|
845
|
-
_subscribe(observer) {
|
|
846
|
-
if (!this._observers.has(observer)) {
|
|
847
|
-
this._observers.add(observer);
|
|
848
|
-
}
|
|
849
|
-
if (this._value !== void 0) {
|
|
850
|
-
observer.next?.(this._value);
|
|
851
|
-
}
|
|
852
|
-
return () => {
|
|
853
|
-
this._observers.delete(observer);
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
_handlers = {
|
|
857
|
-
next: (value) => {
|
|
858
|
-
this._value = value;
|
|
859
|
-
this._observers.forEach((observer) => observer.next?.(value));
|
|
860
|
-
},
|
|
861
|
-
error: (err) => {
|
|
862
|
-
this._observers.forEach((observer) => observer.error?.(err));
|
|
863
|
-
},
|
|
864
|
-
complete: () => {
|
|
865
|
-
this._completed.wake();
|
|
866
|
-
this._observers.forEach((observer) => observer.complete?.());
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
};
|
|
870
|
-
var EMPTY_OBSERVABLE = MulticastObservable.of(null);
|
|
871
|
-
|
|
872
|
-
// src/observable-value.ts
|
|
873
|
-
import { createSetDispatch } from "@dxos/util";
|
|
874
|
-
var ObservableProvider = class {
|
|
875
|
-
_handlers = /* @__PURE__ */ new Set();
|
|
876
|
-
_proxy = createSetDispatch({
|
|
877
|
-
handlers: this._handlers
|
|
878
|
-
});
|
|
879
|
-
_value;
|
|
880
|
-
/**
|
|
881
|
-
* Proxy used to dispatch callbacks to each subscription.
|
|
882
|
-
*/
|
|
883
|
-
get callback() {
|
|
884
|
-
return this._proxy;
|
|
885
|
-
}
|
|
886
|
-
get value() {
|
|
887
|
-
return this._value;
|
|
888
|
-
}
|
|
889
|
-
setValue(value) {
|
|
890
|
-
this._value = value;
|
|
891
|
-
}
|
|
892
|
-
subscribe(handler) {
|
|
893
|
-
this._handlers.add(handler);
|
|
894
|
-
return () => {
|
|
895
|
-
this._handlers.delete(handler);
|
|
896
|
-
};
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
var CancellableObservableProvider = class extends ObservableProvider {
|
|
900
|
-
_handleCancel;
|
|
901
|
-
_cancelled = false;
|
|
902
|
-
constructor(_handleCancel) {
|
|
903
|
-
super(), this._handleCancel = _handleCancel;
|
|
904
|
-
}
|
|
905
|
-
get cancelled() {
|
|
906
|
-
return this._cancelled;
|
|
907
|
-
}
|
|
908
|
-
async cancel() {
|
|
909
|
-
if (this._cancelled) {
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
this._cancelled = true;
|
|
913
|
-
await this._handleCancel?.();
|
|
914
|
-
this.callback.onCancelled?.();
|
|
915
|
-
}
|
|
916
|
-
};
|
|
917
|
-
|
|
918
|
-
// src/persistent-lifecycle.ts
|
|
919
|
-
import { LifecycleState, Resource, cancelWithContext } from "@dxos/context";
|
|
920
|
-
import { warnAfterTimeout as warnAfterTimeout2 } from "@dxos/debug";
|
|
921
|
-
import { log as log2 } from "@dxos/log";
|
|
922
|
-
|
|
923
|
-
// src/task-scheduling.ts
|
|
924
|
-
import { Context as Context2, ContextDisposedError as ContextDisposedError2 } from "@dxos/context";
|
|
925
|
-
import { StackTrace as StackTrace2 } from "@dxos/debug";
|
|
926
|
-
|
|
927
|
-
// src/track-leaks.ts
|
|
928
|
-
import { StackTrace } from "@dxos/debug";
|
|
929
|
-
import { log } from "@dxos/log";
|
|
930
|
-
var enabled = typeof process !== "undefined" && !!process.env.DX_TRACK_LEAKS;
|
|
931
|
-
var openResources = /* @__PURE__ */ new Set();
|
|
932
|
-
var handleSymbol = /* @__PURE__ */ Symbol("checkLeaksHandle");
|
|
933
|
-
var trackResource = (resourceProvider) => {
|
|
934
|
-
if (!enabled) {
|
|
935
|
-
return () => {
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
const resource = resourceProvider();
|
|
939
|
-
openResources.add(resource);
|
|
940
|
-
return () => {
|
|
941
|
-
openResources.delete(resource);
|
|
942
|
-
};
|
|
943
|
-
};
|
|
944
|
-
var trackLeaks = (open, close) => (target) => {
|
|
945
|
-
if (!enabled) {
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
const openMethod = target.prototype[open];
|
|
949
|
-
const closeMethod = target.prototype[close];
|
|
950
|
-
if (!openMethod || !closeMethod) {
|
|
951
|
-
throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);
|
|
952
|
-
}
|
|
953
|
-
{
|
|
954
|
-
target.prototype[open] = async function(...args) {
|
|
955
|
-
this[handleSymbol] = trackResource(() => ({
|
|
956
|
-
name: target.name,
|
|
957
|
-
openStack: new StackTrace()
|
|
958
|
-
}));
|
|
959
|
-
return openMethod.apply(this, args);
|
|
960
|
-
};
|
|
961
|
-
Object.defineProperty(target.prototype[open], "name", {
|
|
962
|
-
value: open + "$checkLeaks"
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
{
|
|
966
|
-
target.prototype[close] = async function(...args) {
|
|
967
|
-
this[handleSymbol]?.();
|
|
968
|
-
return closeMethod.apply(this, args);
|
|
969
|
-
};
|
|
970
|
-
Object.defineProperty(target.prototype[close], "name", {
|
|
971
|
-
value: close + "$checkLeaks"
|
|
972
|
-
});
|
|
973
|
-
}
|
|
974
|
-
};
|
|
975
|
-
var dumpLeaks = () => {
|
|
976
|
-
if (!enabled) {
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
log.info(`Leaked resources ${openResources.size}:`);
|
|
980
|
-
for (const resource of openResources) {
|
|
981
|
-
log.info(`- ${resource.name} at`);
|
|
982
|
-
log.info(resource.openStack.getStack(1));
|
|
983
|
-
log.info("\n");
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
if (enabled) {
|
|
987
|
-
global.dxDumpLeaks = dumpLeaks;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// src/task-scheduling.ts
|
|
991
|
-
var DeferredTask = class {
|
|
992
|
-
_ctx;
|
|
993
|
-
_callback;
|
|
994
|
-
_scheduled = false;
|
|
995
|
-
_currentTask = null;
|
|
996
|
-
_nextTask = new Trigger();
|
|
997
|
-
constructor(_ctx, _callback) {
|
|
998
|
-
this._ctx = _ctx;
|
|
999
|
-
this._callback = _callback;
|
|
1000
|
-
}
|
|
1001
|
-
get scheduled() {
|
|
1002
|
-
return this._scheduled;
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Schedule the task to run asynchronously.
|
|
1006
|
-
*/
|
|
1007
|
-
schedule() {
|
|
1008
|
-
if (this._scheduled) {
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
scheduleTask(this._ctx, async () => {
|
|
1012
|
-
await this._currentTask;
|
|
1013
|
-
this._scheduled = false;
|
|
1014
|
-
const completionTrigger = this._nextTask;
|
|
1015
|
-
this._nextTask = new Trigger();
|
|
1016
|
-
this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {
|
|
1017
|
-
completionTrigger.wake();
|
|
1018
|
-
});
|
|
1019
|
-
});
|
|
1020
|
-
this._scheduled = true;
|
|
1021
|
-
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Schedule the task to run and wait for it to finish.
|
|
1024
|
-
*/
|
|
1025
|
-
async runBlocking() {
|
|
1026
|
-
if (this._ctx.disposed) {
|
|
1027
|
-
throw new ContextDisposedError2();
|
|
1028
|
-
}
|
|
1029
|
-
this.schedule();
|
|
1030
|
-
await this._nextTask.wait();
|
|
1031
|
-
}
|
|
1032
|
-
/**
|
|
1033
|
-
* Waits for the current task to finish if it is running.
|
|
1034
|
-
* Does not schedule a new task.
|
|
1035
|
-
*/
|
|
1036
|
-
async join() {
|
|
1037
|
-
await this._currentTask;
|
|
1038
|
-
}
|
|
1039
|
-
};
|
|
1040
|
-
var AsyncTask = class {
|
|
1041
|
-
#callback;
|
|
1042
|
-
#ctx = void 0;
|
|
1043
|
-
#scheduled = false;
|
|
1044
|
-
#currentTask = null;
|
|
1045
|
-
#nextTask = new Trigger();
|
|
1046
|
-
constructor(callback) {
|
|
1047
|
-
this.#callback = callback;
|
|
1048
|
-
}
|
|
1049
|
-
get scheduled() {
|
|
1050
|
-
return this.#scheduled;
|
|
1051
|
-
}
|
|
1052
|
-
/**
|
|
1053
|
-
* Context of the resource that owns the task.
|
|
1054
|
-
* When the context is disposed, the task is cancelled and cannot be scheduled again.
|
|
1055
|
-
*/
|
|
1056
|
-
open() {
|
|
1057
|
-
this.#ctx = new Context2();
|
|
1058
|
-
}
|
|
1059
|
-
/**
|
|
1060
|
-
* Closes the task and waits for it to finish if it is running.
|
|
1061
|
-
*/
|
|
1062
|
-
async close() {
|
|
1063
|
-
await this.#ctx?.dispose();
|
|
1064
|
-
await this.join();
|
|
1065
|
-
this.#ctx = void 0;
|
|
1066
|
-
}
|
|
1067
|
-
[Symbol.asyncDispose]() {
|
|
1068
|
-
return this.close();
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* Schedule the task to run asynchronously.
|
|
1072
|
-
*/
|
|
1073
|
-
// TODO(dmaretskyi): Add scheduleAt. Where the earlier time will override the later one.
|
|
1074
|
-
schedule() {
|
|
1075
|
-
if (!this.#ctx || this.#ctx.disposed) {
|
|
1076
|
-
throw new Error("AsyncTask not open");
|
|
1077
|
-
}
|
|
1078
|
-
if (this.#scheduled) {
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
scheduleTask(this.#ctx, async () => {
|
|
1082
|
-
await this.#currentTask;
|
|
1083
|
-
if (!this.#ctx || this.#ctx.disposed) {
|
|
1084
|
-
return;
|
|
1085
|
-
}
|
|
1086
|
-
this.#scheduled = false;
|
|
1087
|
-
const completionTrigger = this.#nextTask;
|
|
1088
|
-
this.#nextTask = new Trigger();
|
|
1089
|
-
this.#currentTask = runInContextAsync(this.#ctx, () => this.#callback()).then(() => {
|
|
1090
|
-
completionTrigger.wake();
|
|
1091
|
-
});
|
|
1092
|
-
});
|
|
1093
|
-
this.#scheduled = true;
|
|
1094
|
-
}
|
|
1095
|
-
/**
|
|
1096
|
-
* Schedule the task to run and wait for it to finish.
|
|
1097
|
-
*/
|
|
1098
|
-
async runBlocking() {
|
|
1099
|
-
if (this.#ctx?.disposed) {
|
|
1100
|
-
throw new ContextDisposedError2();
|
|
1101
|
-
}
|
|
1102
|
-
this.schedule();
|
|
1103
|
-
await this.#nextTask.wait();
|
|
1104
|
-
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Waits for the current task to finish if it is running.
|
|
1107
|
-
* Does not schedule a new task.
|
|
1108
|
-
*/
|
|
1109
|
-
async join() {
|
|
1110
|
-
await this.#currentTask;
|
|
1111
|
-
}
|
|
1112
|
-
};
|
|
1113
|
-
var runInContext = (ctx, fn) => {
|
|
1114
|
-
try {
|
|
1115
|
-
fn();
|
|
1116
|
-
} catch (err) {
|
|
1117
|
-
ctx.raise(err);
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
var runInContextAsync = async (ctx, fn) => {
|
|
1121
|
-
try {
|
|
1122
|
-
await fn();
|
|
1123
|
-
} catch (err) {
|
|
1124
|
-
ctx.raise(err);
|
|
1125
|
-
}
|
|
1126
|
-
};
|
|
1127
|
-
var scheduleMicroTask = (ctx, fn) => {
|
|
1128
|
-
queueMicrotask(async () => {
|
|
1129
|
-
if (ctx.disposed) {
|
|
1130
|
-
return;
|
|
1131
|
-
}
|
|
1132
|
-
await runInContextAsync(ctx, fn);
|
|
1133
|
-
});
|
|
1134
|
-
};
|
|
1135
|
-
var scheduleTask = (ctx, fn, afterMs) => {
|
|
1136
|
-
const clearTracking = trackResource(() => ({
|
|
1137
|
-
name: `task (${fn.name || "anonymous"})`,
|
|
1138
|
-
openStack: new StackTrace2()
|
|
1139
|
-
}));
|
|
1140
|
-
const timeout2 = setTimeout(async () => {
|
|
1141
|
-
clearDispose();
|
|
1142
|
-
await runInContextAsync(ctx, fn);
|
|
1143
|
-
clearTracking();
|
|
1144
|
-
}, afterMs);
|
|
1145
|
-
const clearDispose = ctx.onDispose(() => {
|
|
1146
|
-
clearTracking();
|
|
1147
|
-
clearTimeout(timeout2);
|
|
1148
|
-
});
|
|
1149
|
-
};
|
|
1150
|
-
var scheduleTaskInterval = (ctx, task, interval2) => {
|
|
1151
|
-
const clearTracking = trackResource(() => ({
|
|
1152
|
-
name: `repeating task (${task.name || "anonymous"})`,
|
|
1153
|
-
openStack: new StackTrace2()
|
|
1154
|
-
}));
|
|
1155
|
-
let timeoutId;
|
|
1156
|
-
const run = async () => {
|
|
1157
|
-
await runInContextAsync(ctx, task);
|
|
1158
|
-
if (ctx.disposed) {
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
timeoutId = setTimeout(run, interval2);
|
|
1162
|
-
};
|
|
1163
|
-
timeoutId = setTimeout(run, interval2);
|
|
1164
|
-
ctx.onDispose(() => {
|
|
1165
|
-
clearTracking();
|
|
1166
|
-
clearTimeout(timeoutId);
|
|
1167
|
-
});
|
|
1168
|
-
};
|
|
1169
|
-
var scheduleExponentialBackoffTaskInterval = (ctx, task, initialInterval) => {
|
|
1170
|
-
const clearTracking = trackResource(() => ({
|
|
1171
|
-
name: `repeating task (${task.name || "anonymous"})`,
|
|
1172
|
-
openStack: new StackTrace2()
|
|
1173
|
-
}));
|
|
1174
|
-
let timeoutId;
|
|
1175
|
-
let interval2 = initialInterval;
|
|
1176
|
-
const repeat = async () => {
|
|
1177
|
-
await runInContextAsync(ctx, task);
|
|
1178
|
-
if (ctx.disposed) {
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
interval2 *= 2;
|
|
1182
|
-
timeoutId = setTimeout(repeat, interval2);
|
|
1183
|
-
};
|
|
1184
|
-
timeoutId = setTimeout(repeat, interval2);
|
|
1185
|
-
ctx.onDispose(() => {
|
|
1186
|
-
clearTracking();
|
|
1187
|
-
clearTimeout(timeoutId);
|
|
1188
|
-
});
|
|
1189
|
-
};
|
|
1190
|
-
|
|
1191
|
-
// src/persistent-lifecycle.ts
|
|
1192
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
1193
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1194
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1195
|
-
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;
|
|
1196
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1197
|
-
}
|
|
1198
|
-
var INIT_RESTART_DELAY = 100;
|
|
1199
|
-
var DEFAULT_MAX_RESTART_DELAY = 5e3;
|
|
1200
|
-
var STABLE_CONNECTION_THRESHOLD = 5e3;
|
|
1201
|
-
var PersistentLifecycle = class extends Resource {
|
|
1202
|
-
_start;
|
|
1203
|
-
_stop;
|
|
1204
|
-
_onRestart;
|
|
1205
|
-
_maxRestartDelay;
|
|
1206
|
-
_currentState = void 0;
|
|
1207
|
-
_restartTask = void 0;
|
|
1208
|
-
_restartAfter = 0;
|
|
1209
|
-
_connectedAt = void 0;
|
|
1210
|
-
constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
|
|
1211
|
-
super();
|
|
1212
|
-
this._start = start;
|
|
1213
|
-
this._stop = stop;
|
|
1214
|
-
this._onRestart = onRestart;
|
|
1215
|
-
this._maxRestartDelay = maxRestartDelay;
|
|
1216
|
-
}
|
|
1217
|
-
get state() {
|
|
1218
|
-
return this._currentState;
|
|
1219
|
-
}
|
|
1220
|
-
async _open() {
|
|
1221
|
-
this._restartTask = new DeferredTask(this._ctx, async () => {
|
|
1222
|
-
try {
|
|
1223
|
-
await this._restart();
|
|
1224
|
-
} catch (err) {
|
|
1225
|
-
if (this._ctx?.disposed) {
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
log2.warn("Restart failed", {
|
|
1229
|
-
err
|
|
1230
|
-
});
|
|
1231
|
-
this._restartTask?.schedule();
|
|
1232
|
-
}
|
|
1233
|
-
});
|
|
1234
|
-
try {
|
|
1235
|
-
this._currentState = await this._start();
|
|
1236
|
-
this._connectedAt = Date.now();
|
|
1237
|
-
} catch (err) {
|
|
1238
|
-
if (this._ctx?.disposed) {
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
log2.warn("Start failed", {
|
|
1242
|
-
err
|
|
1243
|
-
});
|
|
1244
|
-
this._restartTask?.schedule();
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
async _close() {
|
|
1248
|
-
await this._restartTask?.join();
|
|
1249
|
-
await this._stopCurrentState();
|
|
1250
|
-
this._restartTask = void 0;
|
|
1251
|
-
}
|
|
1252
|
-
async _restart() {
|
|
1253
|
-
log2(`restarting in ${this._restartAfter}ms`, {
|
|
1254
|
-
state: this._lifecycleState
|
|
1255
|
-
});
|
|
1256
|
-
if (this._connectedAt !== void 0 && Date.now() - this._connectedAt >= STABLE_CONNECTION_THRESHOLD) {
|
|
1257
|
-
this._restartAfter = 0;
|
|
1258
|
-
}
|
|
1259
|
-
this._connectedAt = void 0;
|
|
1260
|
-
await this._stopCurrentState();
|
|
1261
|
-
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
1262
|
-
return;
|
|
1263
|
-
}
|
|
1264
|
-
await cancelWithContext(this._ctx, sleep(this._restartAfter));
|
|
1265
|
-
this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
|
|
1266
|
-
await warnAfterTimeout2(5e3, "Connection establishment takes too long", async () => {
|
|
1267
|
-
this._currentState = await this._start();
|
|
1268
|
-
});
|
|
1269
|
-
this._connectedAt = Date.now();
|
|
1270
|
-
await this._onRestart?.();
|
|
1271
|
-
}
|
|
1272
|
-
async _stopCurrentState() {
|
|
1273
|
-
if (this._currentState) {
|
|
1274
|
-
try {
|
|
1275
|
-
await this._stop(this._currentState);
|
|
1276
|
-
} catch (err) {
|
|
1277
|
-
log2.catch(err);
|
|
1278
|
-
}
|
|
1279
|
-
this._currentState = void 0;
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Scheduling restart should be done from outside.
|
|
1284
|
-
*/
|
|
1285
|
-
async scheduleRestart() {
|
|
1286
|
-
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
|
-
this._restartTask.schedule();
|
|
1290
|
-
}
|
|
1291
|
-
};
|
|
1292
|
-
_ts_decorate([
|
|
1293
|
-
synchronized
|
|
1294
|
-
], PersistentLifecycle.prototype, "_open", null);
|
|
1295
|
-
_ts_decorate([
|
|
1296
|
-
synchronized
|
|
1297
|
-
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
1298
|
-
|
|
1299
|
-
// src/push-iterable.ts
|
|
1300
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1301
|
-
var makePushIterable = () => {
|
|
1302
|
-
const buf = [];
|
|
1303
|
-
const trigger2 = new Trigger({
|
|
1304
|
-
autoReset: true
|
|
1305
|
-
});
|
|
1306
|
-
return {
|
|
1307
|
-
[Symbol.asyncIterator]() {
|
|
1308
|
-
return {
|
|
1309
|
-
next: async () => {
|
|
1310
|
-
while (buf.length === 0) {
|
|
1311
|
-
await trigger2.wait();
|
|
1312
|
-
}
|
|
1313
|
-
const item = buf.shift();
|
|
1314
|
-
invariant2(item);
|
|
1315
|
-
switch (item.kind) {
|
|
1316
|
-
case "next":
|
|
1317
|
-
return {
|
|
1318
|
-
value: item.value,
|
|
1319
|
-
done: false
|
|
1320
|
-
};
|
|
1321
|
-
case "return":
|
|
1322
|
-
return {
|
|
1323
|
-
value: item.value,
|
|
1324
|
-
done: true
|
|
1325
|
-
};
|
|
1326
|
-
case "throw":
|
|
1327
|
-
throw item.value;
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
};
|
|
1331
|
-
},
|
|
1332
|
-
next: (value) => {
|
|
1333
|
-
buf.push({
|
|
1334
|
-
kind: "next",
|
|
1335
|
-
value
|
|
1336
|
-
});
|
|
1337
|
-
trigger2.wake();
|
|
1338
|
-
},
|
|
1339
|
-
return: (value) => {
|
|
1340
|
-
buf.push({
|
|
1341
|
-
kind: "return",
|
|
1342
|
-
value
|
|
1343
|
-
});
|
|
1344
|
-
trigger2.wake();
|
|
1345
|
-
},
|
|
1346
|
-
throw: (value) => {
|
|
1347
|
-
buf.push({
|
|
1348
|
-
kind: "throw",
|
|
1349
|
-
value
|
|
1350
|
-
});
|
|
1351
|
-
trigger2.wake();
|
|
1352
|
-
}
|
|
1353
|
-
};
|
|
1354
|
-
};
|
|
1355
|
-
|
|
1356
|
-
// src/stream-to-array.ts
|
|
1357
|
-
var streamToArray = (stream) => {
|
|
1358
|
-
let deferred;
|
|
1359
|
-
if (!stream.readable) {
|
|
1360
|
-
deferred = Promise.resolve([]);
|
|
1361
|
-
} else {
|
|
1362
|
-
deferred = new Promise((resolve, reject) => {
|
|
1363
|
-
if (!stream.readable) {
|
|
1364
|
-
return resolve([]);
|
|
1365
|
-
}
|
|
1366
|
-
let arr = [];
|
|
1367
|
-
const onData = (doc) => {
|
|
1368
|
-
arr?.push(doc);
|
|
1369
|
-
};
|
|
1370
|
-
const onEnd = (err) => {
|
|
1371
|
-
if (err) {
|
|
1372
|
-
reject(err);
|
|
1373
|
-
} else {
|
|
1374
|
-
resolve(arr);
|
|
1375
|
-
}
|
|
1376
|
-
cleanup();
|
|
1377
|
-
};
|
|
1378
|
-
const onClose = () => {
|
|
1379
|
-
resolve(arr);
|
|
1380
|
-
cleanup();
|
|
1381
|
-
};
|
|
1382
|
-
const cleanup = () => {
|
|
1383
|
-
arr = [];
|
|
1384
|
-
stream.removeListener("data", onData);
|
|
1385
|
-
stream.removeListener("end", onEnd);
|
|
1386
|
-
stream.removeListener("error", onEnd);
|
|
1387
|
-
stream.removeListener("close", onClose);
|
|
1388
|
-
};
|
|
1389
|
-
stream.on("data", onData);
|
|
1390
|
-
stream.on("end", onEnd);
|
|
1391
|
-
stream.on("error", onEnd);
|
|
1392
|
-
stream.on("close", onClose);
|
|
1393
|
-
});
|
|
1394
|
-
}
|
|
1395
|
-
return deferred;
|
|
1396
|
-
};
|
|
1397
|
-
|
|
1398
|
-
// src/test-stream.ts
|
|
1399
|
-
import { Duplex } from "@dxos/node-std/stream";
|
|
1400
|
-
var TestStream = class extends Duplex {
|
|
1401
|
-
static async assertConnectivity(stream1, stream2, { timeout: timeout2 = 200 } = {}) {
|
|
1402
|
-
stream1.push("ping");
|
|
1403
|
-
stream2.push("pong");
|
|
1404
|
-
await Promise.all([
|
|
1405
|
-
stream2.assertReceivedAsync("ping", {
|
|
1406
|
-
timeout: timeout2
|
|
1407
|
-
}),
|
|
1408
|
-
stream1.assertReceivedAsync("pong", {
|
|
1409
|
-
timeout: timeout2
|
|
1410
|
-
})
|
|
1411
|
-
]);
|
|
1412
|
-
}
|
|
1413
|
-
_received = Buffer.alloc(0);
|
|
1414
|
-
_onWrite = new Event();
|
|
1415
|
-
_write(chunk, encoding, callback) {
|
|
1416
|
-
this._received = Buffer.concat([
|
|
1417
|
-
this._received,
|
|
1418
|
-
chunk
|
|
1419
|
-
]);
|
|
1420
|
-
this._onWrite.emit();
|
|
1421
|
-
callback();
|
|
1422
|
-
}
|
|
1423
|
-
_read(size) {
|
|
1424
|
-
}
|
|
1425
|
-
assertReceivedAsync(data, { timeout: timeout2 = 200 } = {}) {
|
|
1426
|
-
const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1427
|
-
return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout2);
|
|
1428
|
-
}
|
|
1429
|
-
};
|
|
1430
|
-
|
|
1431
|
-
// src/testing.ts
|
|
1432
|
-
var waitForCondition = ({ condition, timeout: timeout2 = 0, interval: interval2 = 10, error, breakOnError = false }) => {
|
|
1433
|
-
const stopTime = timeout2 ? Date.now() + timeout2 : 0;
|
|
1434
|
-
const trigger2 = new Trigger();
|
|
1435
|
-
const waiter = async () => {
|
|
1436
|
-
while (!stopTime || Date.now() < stopTime) {
|
|
1437
|
-
try {
|
|
1438
|
-
const value = await condition();
|
|
1439
|
-
if (value) {
|
|
1440
|
-
trigger2.wake(value);
|
|
1441
|
-
break;
|
|
1442
|
-
}
|
|
1443
|
-
} catch (err) {
|
|
1444
|
-
if (breakOnError === true) {
|
|
1445
|
-
trigger2.throw(err);
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
await sleep(interval2);
|
|
1449
|
-
}
|
|
1450
|
-
};
|
|
1451
|
-
setTimeout(waiter, 0);
|
|
1452
|
-
return trigger2.wait({
|
|
1453
|
-
timeout: timeout2
|
|
1454
|
-
});
|
|
1455
|
-
};
|
|
1456
|
-
var until = (cb, timeout2) => {
|
|
1457
|
-
return new Promise((resolve, reject) => {
|
|
1458
|
-
const t = timeout2 && setTimeout(() => {
|
|
1459
|
-
reject(new Error(`Timeout after ${t}ms`));
|
|
1460
|
-
}, timeout2);
|
|
1461
|
-
setTimeout(async () => {
|
|
1462
|
-
try {
|
|
1463
|
-
await cb((value) => {
|
|
1464
|
-
t && clearTimeout(t);
|
|
1465
|
-
resolve(value);
|
|
1466
|
-
}, (error) => {
|
|
1467
|
-
t && clearTimeout(t);
|
|
1468
|
-
reject(error);
|
|
1469
|
-
});
|
|
1470
|
-
} catch (err) {
|
|
1471
|
-
reject(err);
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1474
|
-
});
|
|
1475
|
-
};
|
|
1476
|
-
var untilPromise = (cb) => cb();
|
|
1477
|
-
var untilError = (cb) => {
|
|
1478
|
-
return new Promise((resolve, reject) => {
|
|
1479
|
-
setTimeout(async () => {
|
|
1480
|
-
try {
|
|
1481
|
-
await cb();
|
|
1482
|
-
reject(new Error("No error was thrown."));
|
|
1483
|
-
} catch (err) {
|
|
1484
|
-
resolve(err);
|
|
1485
|
-
}
|
|
1486
|
-
});
|
|
1487
|
-
});
|
|
1488
|
-
};
|
|
1489
|
-
|
|
1490
|
-
// src/timer.ts
|
|
1491
|
-
var Timer = class {
|
|
1492
|
-
_callback;
|
|
1493
|
-
_state = new Event();
|
|
1494
|
-
_timer;
|
|
1495
|
-
_count = 0;
|
|
1496
|
-
constructor(_callback) {
|
|
1497
|
-
this._callback = _callback;
|
|
1498
|
-
}
|
|
1499
|
-
get state() {
|
|
1500
|
-
return this._state;
|
|
1501
|
-
}
|
|
1502
|
-
get running() {
|
|
1503
|
-
return !!this._timer;
|
|
1504
|
-
}
|
|
1505
|
-
start(options, cb) {
|
|
1506
|
-
if (isNaN(options.count) || isNaN(options.interval)) {
|
|
1507
|
-
throw new Error(`Invalid options: ${JSON.stringify(options)}`);
|
|
1508
|
-
}
|
|
1509
|
-
if (this.running) {
|
|
1510
|
-
this.stop();
|
|
1511
|
-
}
|
|
1512
|
-
const stop = () => {
|
|
1513
|
-
this.stop();
|
|
1514
|
-
cb?.();
|
|
1515
|
-
};
|
|
1516
|
-
const run = () => {
|
|
1517
|
-
if (this._count >= (options.count ?? 0)) {
|
|
1518
|
-
stop();
|
|
1519
|
-
} else {
|
|
1520
|
-
const interval2 = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);
|
|
1521
|
-
this._timer = setTimeout(async () => {
|
|
1522
|
-
await this._callback(this._count++);
|
|
1523
|
-
run();
|
|
1524
|
-
}, interval2);
|
|
1525
|
-
}
|
|
1526
|
-
};
|
|
1527
|
-
this._state.emit(true);
|
|
1528
|
-
this._count = 0;
|
|
1529
|
-
setTimeout(run);
|
|
1530
|
-
return this;
|
|
1531
|
-
}
|
|
1532
|
-
stop() {
|
|
1533
|
-
clearInterval(this._timer);
|
|
1534
|
-
this._timer = void 0;
|
|
1535
|
-
this._state.emit(false);
|
|
1536
|
-
return this;
|
|
1537
|
-
}
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
|
-
// src/update-scheduler.ts
|
|
1541
|
-
var TIME_PERIOD = 1e3;
|
|
1542
|
-
var UpdateScheduler = class {
|
|
1543
|
-
_ctx;
|
|
1544
|
-
_callback;
|
|
1545
|
-
_params;
|
|
1546
|
-
/**
|
|
1547
|
-
* Promise that resolves when the callback is done.
|
|
1548
|
-
* Never rejects.
|
|
1549
|
-
*/
|
|
1550
|
-
_promise = null;
|
|
1551
|
-
_scheduled = false;
|
|
1552
|
-
_lastUpdateTime = -TIME_PERIOD;
|
|
1553
|
-
constructor(_ctx, _callback, _params = {}) {
|
|
1554
|
-
this._ctx = _ctx;
|
|
1555
|
-
this._callback = _callback;
|
|
1556
|
-
this._params = _params;
|
|
1557
|
-
_ctx.onDispose(async () => {
|
|
1558
|
-
await this._promise;
|
|
1559
|
-
});
|
|
1560
|
-
}
|
|
1561
|
-
trigger() {
|
|
1562
|
-
if (this._scheduled) {
|
|
1563
|
-
return;
|
|
1564
|
-
}
|
|
1565
|
-
scheduleMicroTask(this._ctx, async () => {
|
|
1566
|
-
await this._promise;
|
|
1567
|
-
if (this._params.maxFrequency) {
|
|
1568
|
-
const now = performance.now();
|
|
1569
|
-
const delay2 = this._lastUpdateTime + TIME_PERIOD / this._params.maxFrequency - now;
|
|
1570
|
-
if (delay2 > 0) {
|
|
1571
|
-
await new Promise((resolve) => {
|
|
1572
|
-
const timeoutId = setTimeout(() => {
|
|
1573
|
-
clearContext();
|
|
1574
|
-
resolve();
|
|
1575
|
-
}, delay2);
|
|
1576
|
-
const clearContext = this._ctx.onDispose(() => {
|
|
1577
|
-
clearTimeout(timeoutId);
|
|
1578
|
-
resolve();
|
|
1579
|
-
});
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
if (this._ctx.disposed) {
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
this._lastUpdateTime = performance.now();
|
|
1587
|
-
this._scheduled = false;
|
|
1588
|
-
this._promise = this._callback().then(() => {
|
|
1589
|
-
this._promise = null;
|
|
1590
|
-
}, (error) => {
|
|
1591
|
-
this._promise = null;
|
|
1592
|
-
this._ctx.raise(error);
|
|
1593
|
-
});
|
|
1594
|
-
});
|
|
1595
|
-
this._scheduled = true;
|
|
1596
|
-
}
|
|
1597
|
-
forceTrigger() {
|
|
1598
|
-
scheduleMicroTask(this._ctx, async () => {
|
|
1599
|
-
this._callback().catch((err) => this._ctx.raise(err));
|
|
1600
|
-
});
|
|
1601
|
-
}
|
|
1602
|
-
/**
|
|
1603
|
-
* Waits for the current task to finish if it is running.
|
|
1604
|
-
* Does not schedule a new task.
|
|
1605
|
-
*/
|
|
1606
|
-
async join() {
|
|
1607
|
-
await this._promise;
|
|
1608
|
-
}
|
|
1609
|
-
/**
|
|
1610
|
-
* Force schedule the task to run and wait for it to finish.
|
|
1611
|
-
*/
|
|
1612
|
-
async runBlocking() {
|
|
1613
|
-
await this._promise;
|
|
1614
|
-
this._promise = this._callback();
|
|
1615
|
-
await this._promise;
|
|
1616
|
-
}
|
|
1617
|
-
};
|
|
1618
|
-
export {
|
|
1619
|
-
AsyncTask,
|
|
1620
|
-
CancellableObservableProvider,
|
|
1621
|
-
DeferredTask,
|
|
1622
|
-
Event,
|
|
1623
|
-
MulticastObservable,
|
|
1624
|
-
Mutex,
|
|
1625
|
-
MutexGuard,
|
|
1626
|
-
Observable,
|
|
1627
|
-
ObservableProvider,
|
|
1628
|
-
PersistentLifecycle,
|
|
1629
|
-
PushStream,
|
|
1630
|
-
SubscriptionList,
|
|
1631
|
-
SubscriptionSet,
|
|
1632
|
-
TestStream,
|
|
1633
|
-
TimeoutError,
|
|
1634
|
-
Timer,
|
|
1635
|
-
Trigger,
|
|
1636
|
-
TriggerState,
|
|
1637
|
-
UpdateScheduler,
|
|
1638
|
-
addEventListener,
|
|
1639
|
-
addListener,
|
|
1640
|
-
asyncReturn,
|
|
1641
|
-
asyncTimeout,
|
|
1642
|
-
chain,
|
|
1643
|
-
combine,
|
|
1644
|
-
debounce,
|
|
1645
|
-
debounceAndThrottle,
|
|
1646
|
-
delay,
|
|
1647
|
-
dumpLeaks,
|
|
1648
|
-
interval,
|
|
1649
|
-
latch,
|
|
1650
|
-
makePushIterable,
|
|
1651
|
-
observableError,
|
|
1652
|
-
onEvent,
|
|
1653
|
-
promiseFromCallback,
|
|
1654
|
-
runInContext,
|
|
1655
|
-
runInContextAsync,
|
|
1656
|
-
scheduleExponentialBackoffTaskInterval,
|
|
1657
|
-
scheduleMicroTask,
|
|
1658
|
-
scheduleTask,
|
|
1659
|
-
scheduleTaskInterval,
|
|
1660
|
-
sleep,
|
|
1661
|
-
sleepWithContext,
|
|
1662
|
-
streamToArray,
|
|
1663
|
-
synchronized,
|
|
1664
|
-
throttle,
|
|
1665
|
-
timeout,
|
|
1666
|
-
toError,
|
|
1667
|
-
trackLeaks,
|
|
1668
|
-
trackResource,
|
|
1669
|
-
trigger,
|
|
1670
|
-
unrefTimeout,
|
|
1671
|
-
until,
|
|
1672
|
-
untilError,
|
|
1673
|
-
untilPromise,
|
|
1674
|
-
waitForCondition,
|
|
1675
|
-
waitForEvent
|
|
1676
|
-
};
|
|
1677
|
-
//# sourceMappingURL=index.mjs.map
|