@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
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { Context, ContextDisposedError } from "@dxos/context";
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
var toError = (err) => err === void 0 || typeof err === "string" ? new Error(err) : err;
|
|
4
|
+
var TimeoutError = class extends Error {
|
|
5
|
+
constructor(timeout, label) {
|
|
6
|
+
super(timeout ? `Timeout [${timeout.toLocaleString()}ms]${label === void 0 ? "" : `: ${label}`}` : "Timeout");
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var observableError = (observable, err) => {
|
|
10
|
+
if (err instanceof TimeoutError) observable.callback.onTimeout?.(err);
|
|
11
|
+
else observable.callback.onError(toError(err));
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/timeout.ts
|
|
15
|
+
/**
|
|
16
|
+
* Times out after delay.
|
|
17
|
+
*/
|
|
18
|
+
var sleep = (ms) => {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const finish = Date.now() + ms;
|
|
21
|
+
const sleeper = () => {
|
|
22
|
+
const delta = finish - Date.now();
|
|
23
|
+
if (delta > 0) setTimeout(sleeper, delta);
|
|
24
|
+
else resolve();
|
|
25
|
+
};
|
|
26
|
+
sleeper();
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
var sleepWithContext = (ctx, ms) => {
|
|
30
|
+
const error = new ContextDisposedError();
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
if (ctx.disposed) {
|
|
33
|
+
reject(error);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const timeout = setTimeout(() => {
|
|
37
|
+
clearDispose();
|
|
38
|
+
resolve();
|
|
39
|
+
}, ms);
|
|
40
|
+
const clearDispose = ctx.onDispose(() => {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
reject(error);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Can be used in long-running tasks to let other callbacks be invoked.
|
|
48
|
+
*/
|
|
49
|
+
var asyncReturn = () => sleep(0);
|
|
50
|
+
/**
|
|
51
|
+
* Wait for promise or throw error.
|
|
52
|
+
*/
|
|
53
|
+
var asyncTimeout = async (promise, timeout, err) => {
|
|
54
|
+
if (typeof promise === "function") throw new Error("First argument must be a promise.");
|
|
55
|
+
let timeoutId;
|
|
56
|
+
const throwable = err === void 0 || typeof err === "string" ? new TimeoutError(timeout, err) : err;
|
|
57
|
+
const timeoutPromise = new Promise((resolve, reject) => {
|
|
58
|
+
timeoutId = setTimeout(() => {
|
|
59
|
+
reject(throwable);
|
|
60
|
+
}, timeout);
|
|
61
|
+
unrefTimeout(timeoutId);
|
|
62
|
+
});
|
|
63
|
+
return await Promise.race([promise, timeoutPromise]).finally(() => {
|
|
64
|
+
clearTimeout(timeoutId);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* In Node.JS, `unref` prevents the timeout from blocking the process from exiting. Not available in browsers.
|
|
69
|
+
* https://nodejs.org/api/timers.html#timeoutunref
|
|
70
|
+
*/
|
|
71
|
+
var unrefTimeout = (timeoutId) => {
|
|
72
|
+
if (typeof timeoutId === "object" && "unref" in timeoutId) timeoutId.unref();
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/events.ts
|
|
76
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/common/async/src/events.ts";
|
|
77
|
+
/**
|
|
78
|
+
* An EventEmitter variant that does not do event multiplexing and represents a single event.
|
|
79
|
+
*
|
|
80
|
+
* ## Typical usage:
|
|
81
|
+
* ```javascript
|
|
82
|
+
* class Model {
|
|
83
|
+
* public readonly update = new Event<UpdateData>();
|
|
84
|
+
* private readonly privateEvent = new Event<void>();
|
|
85
|
+
*
|
|
86
|
+
* onUpdate(data: UpdateData) {
|
|
87
|
+
* ...
|
|
88
|
+
* this.update.emit(data);
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
*
|
|
92
|
+
* model.update.on(data => {
|
|
93
|
+
* ...
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* ## Advantages over traditional EventEmitter:
|
|
98
|
+
* 1. User describes explicitly what events a class has as they are defined as class fields.
|
|
99
|
+
* 2. User can explicitly define event visibility (public, protected, private).
|
|
100
|
+
* 3. Having events as class fields allows the compiler to check for correct event usage.
|
|
101
|
+
* 4. User can type the data that event will be emitting.
|
|
102
|
+
* 5. Uses composition instead of inheritance.
|
|
103
|
+
* 6. Removes the cases where event names intersect when used in cases with inheritance.
|
|
104
|
+
* 7. Remove the need to namespace events when developing a class with events that will be used as a base-class.
|
|
105
|
+
*/
|
|
106
|
+
var Event = class Event {
|
|
107
|
+
/**
|
|
108
|
+
* Wrap objects that have on/off style event emitters.
|
|
109
|
+
*/
|
|
110
|
+
static wrap(emitter, eventName) {
|
|
111
|
+
const event = new Event();
|
|
112
|
+
event.addEffect(() => {
|
|
113
|
+
const onEvent = (data) => event.emit(data);
|
|
114
|
+
emitter.on(eventName, onEvent);
|
|
115
|
+
return () => emitter.off(eventName, onEvent);
|
|
116
|
+
});
|
|
117
|
+
return event;
|
|
118
|
+
}
|
|
119
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
120
|
+
_effects = /* @__PURE__ */ new Set();
|
|
121
|
+
/**
|
|
122
|
+
* Emit an event.
|
|
123
|
+
* In most cases should only be called by the class or entity containing the event.
|
|
124
|
+
* All listeners are called in order of subscription with persistent ones first.
|
|
125
|
+
* Listeners are called synchronously in the same stack.
|
|
126
|
+
* A thrown exception in the listener will stop the event from being emitted to the rest of the listeners.
|
|
127
|
+
*
|
|
128
|
+
* @param data param that will be passed to all listeners.
|
|
129
|
+
*/
|
|
130
|
+
emit(data) {
|
|
131
|
+
for (const listener of this._listeners) {
|
|
132
|
+
listener.trigger(data);
|
|
133
|
+
if (listener.once) this._listeners.delete(listener);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Emit an event and wait for async listeners to complete.
|
|
138
|
+
* In most cases should only be called by the class or entity containing the event.
|
|
139
|
+
* All listeners are called in order of subscription with persistent ones first.
|
|
140
|
+
* Listeners are called sequentially.
|
|
141
|
+
*
|
|
142
|
+
* @param data param that will be passed to all listeners.
|
|
143
|
+
*/
|
|
144
|
+
async emitAsync(data) {
|
|
145
|
+
for (const listener of this._listeners) {
|
|
146
|
+
await listener.triggerAsync(data);
|
|
147
|
+
if (listener.once) this._listeners.delete(listener);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
on(_ctx, _callback, options) {
|
|
151
|
+
const [ctx, callback] = _ctx instanceof Context ? [_ctx, _callback] : [new Context(void 0, {
|
|
152
|
+
"~LogMeta": "~LogMeta",
|
|
153
|
+
F: __dxlog_file,
|
|
154
|
+
L: 132
|
|
155
|
+
}), _ctx];
|
|
156
|
+
const weak = !!options?.weak;
|
|
157
|
+
const once = !!options?.once;
|
|
158
|
+
const listener = new EventListener(this, callback, ctx, once, weak);
|
|
159
|
+
this._addListener(listener);
|
|
160
|
+
return () => {
|
|
161
|
+
this._removeListener(listener);
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Unsubscribe this callback from new events. Includes persistent and once-listeners.
|
|
166
|
+
* NOTE: It is recommended to use `Event.on`'s return value instead.
|
|
167
|
+
* If the callback is not subscribed this is no-op.
|
|
168
|
+
*
|
|
169
|
+
* @param callback
|
|
170
|
+
*/
|
|
171
|
+
off(callback) {
|
|
172
|
+
for (const listener of this._listeners) if (listener.derefCallback() === callback) this._removeListener(listener);
|
|
173
|
+
}
|
|
174
|
+
once(_ctx, _callback) {
|
|
175
|
+
const [ctx, callback] = _ctx instanceof Context ? [_ctx, _callback] : [new Context(void 0, {
|
|
176
|
+
"~LogMeta": "~LogMeta",
|
|
177
|
+
F: __dxlog_file,
|
|
178
|
+
L: 169
|
|
179
|
+
}), _ctx];
|
|
180
|
+
const listener = new EventListener(this, callback, ctx, true, false);
|
|
181
|
+
this._addListener(listener);
|
|
182
|
+
return () => {
|
|
183
|
+
this._removeListener(listener);
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* An async iterator that iterates over events.
|
|
188
|
+
* This iterator runs indefinitely.
|
|
189
|
+
*/
|
|
190
|
+
async *[Symbol.asyncIterator]() {
|
|
191
|
+
while (true) yield await new Promise((resolve) => {
|
|
192
|
+
this.once(resolve);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns a promise that resolves with the first event emitted that matches the provided predicate.
|
|
197
|
+
*
|
|
198
|
+
* @param predicate
|
|
199
|
+
*/
|
|
200
|
+
waitFor(predicate) {
|
|
201
|
+
return new Promise((resolve) => {
|
|
202
|
+
const unsubscribe = this.on((data) => {
|
|
203
|
+
if (predicate(data)) {
|
|
204
|
+
unsubscribe();
|
|
205
|
+
resolve(data);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Returns a promise that resolves once a specific number of events was emitted since this method was called.
|
|
212
|
+
*
|
|
213
|
+
* @param expectedCount
|
|
214
|
+
*/
|
|
215
|
+
waitForCount(expectedCount) {
|
|
216
|
+
let count = 0;
|
|
217
|
+
return this.waitFor(() => ++count === expectedCount);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Similar to waitFor, but the promise resolves immediately if the condition is already true.
|
|
221
|
+
*/
|
|
222
|
+
async waitForCondition(predicate) {
|
|
223
|
+
if (!predicate()) await this.waitFor(predicate);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Returns the number of persistent listeners.
|
|
227
|
+
*/
|
|
228
|
+
listenerCount() {
|
|
229
|
+
return this._listeners.size;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Add a side effect that will be activated once the event has at least one subscriber.
|
|
233
|
+
* The provided callback can return a function that will be used to clean up after the last subscriber unsubscribes from the event.
|
|
234
|
+
* The API is similar to `useEffect` from React.
|
|
235
|
+
*
|
|
236
|
+
* ## Example:
|
|
237
|
+
* ```typescript
|
|
238
|
+
* event.addEffect(() => {
|
|
239
|
+
* // do stuff
|
|
240
|
+
* return () => {
|
|
241
|
+
* // clean-up
|
|
242
|
+
* };
|
|
243
|
+
* });
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @returns Callback that will remove this effect once called.
|
|
247
|
+
*/
|
|
248
|
+
addEffect(effect) {
|
|
249
|
+
const handle = {
|
|
250
|
+
effect,
|
|
251
|
+
cleanup: void 0
|
|
252
|
+
};
|
|
253
|
+
if (this.listenerCount() > 0) handle.cleanup = handle.effect();
|
|
254
|
+
this._effects.add(handle);
|
|
255
|
+
return () => {
|
|
256
|
+
handle.cleanup?.();
|
|
257
|
+
this._effects.delete(handle);
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Triggers an event with at least `timeout` milliseconds between each event.
|
|
262
|
+
* If the event is triggered more often, the event is delayed until the timeout is reached.
|
|
263
|
+
* If event is emitted for the first time or event wasn't fired for `timeout` milliseconds,
|
|
264
|
+
* the event is emitted after `timeout / 8` ms.
|
|
265
|
+
*/
|
|
266
|
+
debounce(timeout = 0) {
|
|
267
|
+
let firing;
|
|
268
|
+
let lastFired;
|
|
269
|
+
const debouncedEvent = new Event();
|
|
270
|
+
debouncedEvent.addEffect(() => {
|
|
271
|
+
const unsubscribe = this.on(() => {
|
|
272
|
+
if (!firing) {
|
|
273
|
+
const fireIn = !lastFired || Date.now() - lastFired > timeout ? timeout / 8 : timeout;
|
|
274
|
+
firing = setTimeout(() => {
|
|
275
|
+
lastFired = Date.now();
|
|
276
|
+
firing = void 0;
|
|
277
|
+
debouncedEvent.emit();
|
|
278
|
+
}, fireIn);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
return () => {
|
|
282
|
+
unsubscribe();
|
|
283
|
+
clearTimeout(firing);
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
return debouncedEvent;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Turn any variant of `Event<T>` into an `Event<void>` discarding the callback parameter.
|
|
290
|
+
*/
|
|
291
|
+
discardParameter() {
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Pipe the events into another event.
|
|
296
|
+
* @param event
|
|
297
|
+
*/
|
|
298
|
+
pipeInto(event) {
|
|
299
|
+
return this.on((data) => event.emit(data));
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Overridden to not return implementation details.
|
|
303
|
+
*/
|
|
304
|
+
toJSON() {
|
|
305
|
+
return { listenerCount: this.listenerCount() };
|
|
306
|
+
}
|
|
307
|
+
_addListener(listener) {
|
|
308
|
+
this._listeners.add(listener);
|
|
309
|
+
if (this.listenerCount() === 1) this._runEffects();
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* @internal
|
|
313
|
+
*/
|
|
314
|
+
_removeListener(listener) {
|
|
315
|
+
this._listeners.delete(listener);
|
|
316
|
+
listener.remove();
|
|
317
|
+
if (this.listenerCount() === 0) this._cleanupEffects();
|
|
318
|
+
}
|
|
319
|
+
_runEffects() {
|
|
320
|
+
for (const handle of this._effects) handle.cleanup = handle.effect();
|
|
321
|
+
}
|
|
322
|
+
_cleanupEffects() {
|
|
323
|
+
for (const handle of this._effects) {
|
|
324
|
+
handle.cleanup?.();
|
|
325
|
+
handle.cleanup = void 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
var EventListener = class {
|
|
330
|
+
ctx;
|
|
331
|
+
once;
|
|
332
|
+
weak;
|
|
333
|
+
callback;
|
|
334
|
+
_clearDispose = void 0;
|
|
335
|
+
constructor(event, listener, ctx, once, weak) {
|
|
336
|
+
this.ctx = ctx;
|
|
337
|
+
this.once = once;
|
|
338
|
+
this.weak = weak;
|
|
339
|
+
this._clearDispose = ctx.onDispose(() => {
|
|
340
|
+
event._removeListener(this);
|
|
341
|
+
});
|
|
342
|
+
if (weak) {
|
|
343
|
+
this.callback = new WeakRef(listener);
|
|
344
|
+
weakListeners().registry?.register(listener, {
|
|
345
|
+
event: new WeakRef(event),
|
|
346
|
+
listener: this
|
|
347
|
+
}, this);
|
|
348
|
+
} else this.callback = listener;
|
|
349
|
+
}
|
|
350
|
+
derefCallback() {
|
|
351
|
+
return this.weak ? this.callback.deref() : this.callback;
|
|
352
|
+
}
|
|
353
|
+
trigger(data) {
|
|
354
|
+
try {
|
|
355
|
+
this.derefCallback()?.(data);
|
|
356
|
+
} catch (err) {
|
|
357
|
+
this.ctx.raise(err);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
async triggerAsync(data) {
|
|
361
|
+
try {
|
|
362
|
+
await this.derefCallback()?.(data);
|
|
363
|
+
} catch (err) {
|
|
364
|
+
this.ctx.raise(err);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
remove() {
|
|
368
|
+
this._clearDispose?.();
|
|
369
|
+
weakListeners().registry?.unregister(this);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var weakListenersState = null;
|
|
373
|
+
var FINALIZATION_REGISTRY_SUPPORTED = !!globalThis.FinalizationRegistry;
|
|
374
|
+
var weakListeners = () => {
|
|
375
|
+
if (!FINALIZATION_REGISTRY_SUPPORTED) return { registry: void 0 };
|
|
376
|
+
weakListenersState ??= new FinalizationRegistry(({ event, listener }) => {
|
|
377
|
+
event.deref()?._removeListener(listener);
|
|
378
|
+
});
|
|
379
|
+
return { registry: weakListenersState };
|
|
380
|
+
};
|
|
381
|
+
//#endregion
|
|
382
|
+
export { sleepWithContext as a, observableError as c, sleep as i, toError as l, asyncReturn as n, unrefTimeout as o, asyncTimeout as r, TimeoutError as s, Event as t };
|
|
383
|
+
|
|
384
|
+
//# sourceMappingURL=chunk-events.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-events.mjs","names":[],"sources":["../../src/errors.ts","../../src/timeout.ts","../../src/events.ts"],"sourcesContent":["//\n// Copyright 2020 DXOS.org\n//\n\nimport { type ObservableProvider } from './observable-value';\n\n// TODO(burdon): Move to debug.\n// Use: https://nodejs.org/api/errors.html\nexport const toError = (err: any) => (err === undefined || typeof err === 'string' ? new Error(err) : err);\n\nexport class TimeoutError extends Error {\n constructor(timeout?: number, label?: string) {\n super(timeout ? `Timeout [${timeout.toLocaleString()}ms]${label === undefined ? '' : `: ${label}`}` : 'Timeout');\n }\n}\n\nexport interface AsyncEvents<T = any> {\n onSuccess?(result: T): void;\n onTimeout?(err: TimeoutError): void;\n onError(err: any): void;\n}\n\nexport const observableError = (observable: ObservableProvider<AsyncEvents>, err: any) => {\n if (err instanceof TimeoutError) {\n observable.callback.onTimeout?.(err);\n } else {\n observable.callback.onError(toError(err));\n }\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { type Context, ContextDisposedError } from '@dxos/context';\n\nimport { TimeoutError } from './errors';\n\n/**\n * Times out after delay.\n */\nexport const sleep = (ms: number) => {\n return new Promise<void>((resolve) => {\n const finish = Date.now() + ms;\n\n // `setTimeout` does not guarantee execution at >= the scheduled time and may execute slightly early.\n const sleeper = () => {\n const delta = finish - Date.now();\n if (delta > 0) {\n setTimeout(sleeper, delta);\n } else {\n resolve();\n }\n };\n\n sleeper();\n });\n};\n\n// TODO(burdon): Reconcile with sleep.\nexport const sleepWithContext = (ctx: Context, ms: number) => {\n const error = new ContextDisposedError();\n return new Promise<void>((resolve, reject) => {\n if (ctx.disposed) {\n reject(error);\n return;\n }\n\n const timeout = setTimeout(() => {\n clearDispose();\n resolve();\n }, ms);\n\n const clearDispose = ctx.onDispose(() => {\n clearTimeout(timeout);\n reject(error);\n });\n });\n};\n\n/**\n * Can be used in long-running tasks to let other callbacks be invoked.\n */\nexport const asyncReturn = () => sleep(0);\n\n/**\n * Wait for promise or throw error.\n */\nexport const asyncTimeout = async <T>(promise: Promise<T>, timeout: number, err?: Error | string): Promise<T> => {\n if (typeof promise === 'function') {\n throw new Error('First argument must be a promise.');\n }\n\n let timeoutId: NodeJS.Timeout;\n const throwable = err === undefined || typeof err === 'string' ? new TimeoutError(timeout, err) : err;\n const timeoutPromise = new Promise<T>((resolve, reject) => {\n timeoutId = setTimeout(() => {\n reject(throwable);\n }, timeout);\n\n unrefTimeout(timeoutId);\n });\n\n return await Promise.race([promise, timeoutPromise]).finally(() => {\n clearTimeout(timeoutId);\n });\n};\n\n/**\n * In Node.JS, `unref` prevents the timeout from blocking the process from exiting. Not available in browsers.\n * https://nodejs.org/api/timers.html#timeoutunref\n */\nexport const unrefTimeout = (timeoutId: NodeJS.Timeout) => {\n if (typeof timeoutId === 'object' && 'unref' in timeoutId) {\n timeoutId.unref();\n }\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { Context } from '@dxos/context';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { type CleanupFn } from './cleanup';\n\nexport type Effect = () => CleanupFn | undefined;\n\n/**\n * Effect that's been added to a specific Event.\n */\ninterface MaterializedEffect {\n effect: Effect;\n cleanup: CleanupFn | undefined;\n}\n\ninterface EventEmitterLike {\n on(event: string, cb: (data?: any) => void): void;\n off(event: string, cb: (data?: any) => void): void;\n}\n\nexport type ListenerOptions = {\n weak?: boolean;\n once?: boolean;\n};\n\ntype EventCallback<T> = (data: T) => MaybePromise<void>;\n\n// TODO(dmaretskyi): Remove this once the code is cleaned up.\nconst DO_NOT_ERROR_ON_ASYNC_CALLBACK = true;\n\n/**\n * An EventEmitter variant that does not do event multiplexing and represents a single event.\n *\n * ## Typical usage:\n * ```javascript\n * class Model {\n * public readonly update = new Event<UpdateData>();\n * private readonly privateEvent = new Event<void>();\n *\n * onUpdate(data: UpdateData) {\n * ...\n * this.update.emit(data);\n * }\n * }\n *\n * model.update.on(data => {\n * ...\n * });\n * ```\n *\n * ## Advantages over traditional EventEmitter:\n * 1. User describes explicitly what events a class has as they are defined as class fields.\n * 2. User can explicitly define event visibility (public, protected, private).\n * 3. Having events as class fields allows the compiler to check for correct event usage.\n * 4. User can type the data that event will be emitting.\n * 5. Uses composition instead of inheritance.\n * 6. Removes the cases where event names intersect when used in cases with inheritance.\n * 7. Remove the need to namespace events when developing a class with events that will be used as a base-class.\n */\n// TODO(burdon): Rename EventSink? (Event is a built-in type).\nexport class Event<T = void> implements ReadOnlyEvent<T> {\n /**\n * Wrap objects that have on/off style event emitters.\n */\n static wrap<T>(emitter: EventEmitterLike, eventName: string): Event<T> {\n const event = new Event<T>();\n\n event.addEffect(() => {\n const onEvent = (data: any) => event.emit(data);\n emitter.on(eventName, onEvent);\n return () => emitter.off(eventName, onEvent);\n });\n\n return event;\n }\n\n private readonly _listeners = new Set<EventListener<T>>();\n private readonly _effects = new Set<MaterializedEffect>();\n\n /**\n * Emit an event.\n * In most cases should only be called by the class or entity containing the event.\n * All listeners are called in order of subscription with persistent ones first.\n * Listeners are called synchronously in the same stack.\n * A thrown exception in the listener will stop the event from being emitted to the rest of the listeners.\n *\n * @param data param that will be passed to all listeners.\n */\n emit(data: T): void {\n for (const listener of this._listeners) {\n listener.trigger(data);\n\n if (listener.once) {\n this._listeners.delete(listener);\n }\n }\n }\n\n /**\n * Emit an event and wait for async listeners to complete.\n * In most cases should only be called by the class or entity containing the event.\n * All listeners are called in order of subscription with persistent ones first.\n * Listeners are called sequentially.\n *\n * @param data param that will be passed to all listeners.\n */\n async emitAsync(data: T): Promise<void> {\n for (const listener of this._listeners) {\n await listener.triggerAsync(data);\n\n if (listener.once) {\n this._listeners.delete(listener);\n }\n }\n }\n\n /**\n * Register an event listener.\n * If provided callback was already registered as once-listener, it is made permanent.\n *\n * @param callback\n * @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist.\n * @returns function that unsubscribes this event listener\n */\n on(callback: EventCallback<T>): CleanupFn;\n on(ctx: Context, callback: EventCallback<T>, options?: ListenerOptions): CleanupFn;\n on(_ctx: any, _callback?: EventCallback<T>, options?: ListenerOptions): CleanupFn {\n const [ctx, callback] = _ctx instanceof Context ? [_ctx, _callback] : [new Context(), _ctx];\n const weak = !!options?.weak;\n const once = !!options?.once;\n\n const listener = new EventListener(this, callback, ctx, once, weak);\n\n this._addListener(listener);\n\n return () => {\n this._removeListener(listener);\n };\n }\n\n /**\n * Unsubscribe this callback from new events. Includes persistent and once-listeners.\n * NOTE: It is recommended to use `Event.on`'s return value instead.\n * If the callback is not subscribed this is no-op.\n *\n * @param callback\n */\n off(callback: (data: T) => void): void {\n for (const listener of this._listeners) {\n if (listener.derefCallback() === callback) {\n this._removeListener(listener);\n }\n }\n }\n\n /**\n * Register a callback to be called only once when the next event is emitted.\n * If this callback is already registered as permanent listener, this is no-op.\n *\n * @param callback\n */\n once(callback: (data: T) => void): CleanupFn;\n once(ctx: Context, callback: (data: T) => void): CleanupFn;\n once(_ctx: any, _callback?: (data: T) => void): CleanupFn {\n const [ctx, callback] = _ctx instanceof Context ? [_ctx, _callback] : [new Context(), _ctx];\n\n const listener = new EventListener(this, callback, ctx, true, false);\n\n this._addListener(listener);\n\n return () => {\n this._removeListener(listener);\n };\n }\n\n /**\n * An async iterator that iterates over events.\n * This iterator runs indefinitely.\n */\n async *[Symbol.asyncIterator](): AsyncIterator<T> {\n while (true) {\n yield await new Promise((resolve) => {\n this.once(resolve);\n });\n }\n }\n\n /**\n * Returns a promise that resolves with the first event emitted that matches the provided predicate.\n *\n * @param predicate\n */\n waitFor(predicate: (data: T) => boolean): Promise<T> {\n return new Promise((resolve) => {\n const unsubscribe = this.on((data) => {\n if (predicate(data)) {\n unsubscribe();\n resolve(data);\n }\n });\n });\n }\n\n /**\n * Returns a promise that resolves once a specific number of events was emitted since this method was called.\n *\n * @param expectedCount\n */\n waitForCount(expectedCount: number): Promise<T> {\n let count = 0;\n return this.waitFor(() => ++count === expectedCount);\n }\n\n /**\n * Similar to waitFor, but the promise resolves immediately if the condition is already true.\n */\n // TODO(burdon): Should pass event property to predicate.\n async waitForCondition(predicate: () => boolean): Promise<void> {\n if (!predicate()) {\n await this.waitFor(predicate);\n }\n }\n\n /**\n * Returns the number of persistent listeners.\n */\n listenerCount(): number {\n return this._listeners.size;\n }\n\n /**\n * Add a side effect that will be activated once the event has at least one subscriber.\n * The provided callback can return a function that will be used to clean up after the last subscriber unsubscribes from the event.\n * The API is similar to `useEffect` from React.\n *\n * ## Example:\n * ```typescript\n * event.addEffect(() => {\n * // do stuff\n * return () => {\n * // clean-up\n * };\n * });\n * ```\n *\n * @returns Callback that will remove this effect once called.\n */\n addEffect(effect: Effect): CleanupFn {\n const handle: MaterializedEffect = { effect, cleanup: undefined };\n\n if (this.listenerCount() > 0) {\n handle.cleanup = handle.effect();\n }\n\n this._effects.add(handle);\n return () => {\n // eslint-disable-next-line no-unused-expressions\n handle.cleanup?.();\n this._effects.delete(handle);\n };\n }\n\n /**\n * Triggers an event with at least `timeout` milliseconds between each event.\n * If the event is triggered more often, the event is delayed until the timeout is reached.\n * If event is emitted for the first time or event wasn't fired for `timeout` milliseconds,\n * the event is emitted after `timeout / 8` ms.\n */\n // TODO(burdon): Factor out generic function.\n debounce(timeout = 0): Event<void> {\n let firing: NodeJS.Timeout | undefined;\n let lastFired: number | undefined;\n\n const debouncedEvent = new Event<void>();\n debouncedEvent.addEffect(() => {\n const unsubscribe = this.on(() => {\n if (!firing) {\n const fireIn = !lastFired || Date.now() - lastFired > timeout ? timeout / 8 : timeout;\n firing = setTimeout(() => {\n lastFired = Date.now();\n firing = undefined;\n debouncedEvent.emit();\n }, fireIn);\n }\n });\n\n return () => {\n unsubscribe();\n clearTimeout(firing);\n };\n });\n\n return debouncedEvent;\n }\n\n /**\n * Turn any variant of `Event<T>` into an `Event<void>` discarding the callback parameter.\n */\n discardParameter(): Event<void> {\n return this as any;\n }\n\n /**\n * Pipe the events into another event.\n * @param event\n */\n pipeInto(event: Event<T>): CleanupFn {\n return this.on((data) => event.emit(data));\n }\n\n /**\n * Overridden to not return implementation details.\n */\n toJSON(): { listenerCount: number } {\n return {\n listenerCount: this.listenerCount(),\n };\n }\n\n private _addListener(listener: EventListener<T>): void {\n this._listeners.add(listener);\n\n if (this.listenerCount() === 1) {\n this._runEffects();\n }\n }\n\n /**\n * @internal\n */\n _removeListener(listener: EventListener<T>): void {\n this._listeners.delete(listener);\n listener.remove();\n\n if (this.listenerCount() === 0) {\n this._cleanupEffects();\n }\n }\n\n private _runEffects(): void {\n for (const handle of this._effects) {\n handle.cleanup = handle.effect();\n }\n }\n\n private _cleanupEffects(): void {\n for (const handle of this._effects) {\n // eslint-disable-next-line no-unused-expressions\n handle.cleanup?.();\n handle.cleanup = undefined;\n }\n }\n}\n\n/**\n * A version of Event class which only has subscribe methods.\n * Useful in cases where you want to explicitly prohibit calling `emit` method.\n */\nexport interface ReadOnlyEvent<T = void> {\n /**\n * Register an event listener.\n * If provided callback was already registered as once-listener, it is made permanent.\n *\n * @param callback\n * @param options.weak If true, the callback will be weakly referenced and will be garbage collected if no other references to it exist.\n * @returns function that unsubscribes this event listener\n */\n on(callback: (data: T) => void): CleanupFn;\n on(ctx: Context, callback: (data: T) => void, options?: ListenerOptions): CleanupFn;\n\n /**\n * Unsubscribes this callback from new events. Includes persistent and once-listeners.\n * NOTE: It is recommended to us `Event.on`'s return value.\n * If the callback is not subscribed this is no-op.\n *\n * @param callback\n */\n off(callback: (data: T) => void): void;\n\n /**\n * Register a callback to be called only once when the next event is emitted.\n * If this callback is already registered as permanent listener, this is no-op.\n *\n * @param callback\n */\n once(callback: (data: T) => void): CleanupFn;\n\n /**\n * An async iterator that iterates over events.\n * This iterator runs indefinitely.\n */\n [Symbol.asyncIterator](): AsyncIterator<T>;\n\n /**\n * Returns a promise that resolves with the first event emitted that matches the provided predicate.\n *\n * @param predicate\n */\n waitFor(predicate: (data: T) => boolean): Promise<T>;\n\n /**\n * Returns a promise that resolves once a specific number of events was emitted since this method was called.\n *\n * @param expectedCount\n */\n waitForCount(expectedCount: number): Promise<T>;\n\n /**\n * Turn any variant of `Event<T>` into an `Event<void>` discarding the callback parameter.\n */\n discardParameter(): Event<void>;\n\n /**\n * Triggers an event with at least `timeout` milliseconds between each event.\n * If the event is triggered more often, the event is delayed until the timeout is reached.\n * If event is emitted for the first time or event wasn't fired for `timeout` milliseconds,\n * the event is emitted after `timeout / 8` ms.\n */\n debounce(timeout?: number): Event<void>;\n}\n\nclass EventListener<T> {\n public readonly callback: EventCallback<T> | WeakRef<EventCallback<T>>;\n\n private readonly _clearDispose?: () => void = undefined;\n\n constructor(\n event: Event<T>,\n listener: EventCallback<T>,\n public readonly ctx: Context,\n public readonly once: boolean,\n public readonly weak: boolean,\n ) {\n this._clearDispose = ctx.onDispose(() => {\n event._removeListener(this);\n });\n\n if (weak) {\n this.callback = new WeakRef(listener);\n weakListeners().registry?.register(\n listener,\n {\n event: new WeakRef(event),\n listener: this,\n },\n this,\n );\n } else {\n this.callback = listener;\n }\n }\n\n derefCallback(): EventCallback<T> | undefined {\n return this.weak ? (this.callback as WeakRef<EventCallback<T>>).deref() : (this.callback as EventCallback<T>);\n }\n\n trigger(data: T): void {\n let result!: MaybePromise<void>;\n try {\n const callback = this.derefCallback();\n result = callback?.(data);\n } catch (err: any) {\n this.ctx.raise(err);\n }\n\n if (!DO_NOT_ERROR_ON_ASYNC_CALLBACK) {\n if (result instanceof Promise) {\n throw new TypeError('Event has async callbacks, use emitAsync instead');\n }\n }\n }\n\n async triggerAsync(data: T): Promise<void> {\n try {\n const callback = this.derefCallback();\n await callback?.(data);\n } catch (err: any) {\n this.ctx.raise(err);\n }\n }\n\n remove(): void {\n this._clearDispose?.();\n weakListeners().registry?.unregister(this);\n }\n}\n\ntype HeldValue = {\n event: WeakRef<Event<any>>;\n listener: EventListener<any>;\n};\n\nlet weakListenersState: FinalizationRegistry<HeldValue> | null = null;\n\ntype WeakListeners = {\n registry: FinalizationRegistry<HeldValue> | undefined;\n};\n\nconst FINALIZATION_REGISTRY_SUPPORTED = !!globalThis.FinalizationRegistry;\n\nconst weakListeners = (): WeakListeners => {\n if (!FINALIZATION_REGISTRY_SUPPORTED) {\n return { registry: undefined };\n }\n weakListenersState ??= new FinalizationRegistry(({ event, listener }) => {\n event.deref()?._removeListener(listener);\n });\n return { registry: weakListenersState };\n};\n"],"mappings":"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"}
|