@dxos/async 0.9.1-staging.ee54ba693a → 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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/callback.ts","../../src/chain.ts","../../src/cleanup.ts","../../src/debounce.ts","../../src/event-emitter.ts","../../src/mutex.ts","../../src/trigger.ts","../../src/observable.ts","../../src/observable-value.ts","../../src/track-leaks.ts","../../src/task-scheduling.ts","../../src/persistent-lifecycle.ts","../../src/push-iterable.ts","../../src/stream-to-array.ts","../../src/testing.ts","../../src/timer.ts","../../src/update-scheduler.ts"],"sourcesContent":["//\n// Copyright 2020 DXOS.org\n//\n\nexport type Awaited<T> = T extends Promise<infer U> ? U : T;\n\n/**\n * Helper to convert a callback based API into a promise based API.\n */\nexport const promiseFromCallback = <T = void>(run: (cb: (error?: Error, value?: T) => void) => void): Promise<T> =>\n new Promise((resolve, reject) => {\n run((error, value) => {\n if (error) {\n reject(error);\n } else {\n resolve(value!);\n }\n });\n });\n","//\n// Copyright 2022 DXOS.org\n//\n\ntype Transform = (...args: any) => Promise<any>;\n\n/**\n * Async reducer iteratively applies functions to the given array of elements.\n */\nexport const chain =\n <T>(chain: Transform[]) =>\n async (elements: Promise<T[]>) => {\n let result = await elements;\n for (const part of chain) {\n result = await Promise.all(result.map(async (element) => await part(element)));\n }\n\n return result;\n };\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { ComplexMap, type PrimitiveProjection } from '@dxos/util';\n\nexport type CleanupFn = () => void;\n\n/**\n * Combine multiple cleanup functions into a single cleanup function.\n * Can be used in effect hooks in conjunction with `addEventListener`.\n */\nexport const combine = (...cleanupFns: (boolean | undefined | CleanupFn | CleanupFn[])[]): CleanupFn => {\n return () => {\n cleanupFns\n .flat()\n .filter((f): f is CleanupFn => typeof f === 'function')\n .forEach((cleanupFn) => cleanupFn());\n };\n};\n\nexport const timeout = (cb: Function, ms = 0): CleanupFn => {\n const t = setTimeout(cb, ms);\n return () => clearTimeout(t);\n};\n\nexport const interval = (cb: Function, ms: number): CleanupFn => {\n const t = setInterval(cb, ms);\n return () => clearInterval(t);\n};\n\ntype EventMap<T> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : Record<string, Event>;\n\n/**\n * Add the event listener and return a cleanup function.\n * Can be used in effect hooks in conjunction with `combine`.\n */\nexport const addEventListener = <T extends EventTarget, K extends keyof EventMap<T>>(\n target: T,\n type: K,\n listener: (this: T, ev: EventMap<T>[K]) => any,\n options?: boolean | AddEventListenerOptions,\n): CleanupFn => {\n target.addEventListener(type as string, listener as EventListener, options);\n return () => target.removeEventListener(type as string, listener as EventListener, options);\n};\n\nexport class SubscriptionList {\n private readonly _cleanups: CleanupFn[] = [];\n\n add(...cb: CleanupFn[]): this {\n this._cleanups.push(...cb);\n return this;\n }\n\n clear(): void {\n this._cleanups.forEach((cb) => cb());\n this._cleanups.length = 0;\n }\n}\n\nexport class SubscriptionSet<T = any> {\n private readonly _cleanupMap: ComplexMap<T, CleanupFn>;\n\n constructor(keyProjection: PrimitiveProjection<T>) {\n this._cleanupMap = new ComplexMap<T, CleanupFn>(keyProjection);\n }\n\n set(key: T, cb: CleanupFn): this {\n this._cleanupMap.set(key, cb);\n return this;\n }\n\n clear(): void {\n this._cleanupMap.forEach((cb) => cb());\n this._cleanupMap.clear();\n }\n}\n","//\n// Copyright 2023 DXOS.org\n//\n\ntype Callback = (...args: any[]) => void;\n\n/**\n * Delay callback execution by a specified time.\n * Unlike debounce, subsequent calls during the delay period are ignored.\n *\n * @param cb Callback to invoke.\n * @param delay Time to wait before invoking the callback.\n * @returns A new function that schedules the callback once and ignores subsequent calls until executed.\n */\nexport const delay = <F extends Callback>(cb: F, delay = 100): F => {\n let pending = false;\n return ((...args: any[]) => {\n if (pending) {\n return;\n }\n\n pending = true;\n setTimeout(() => {\n try {\n cb(...args);\n } finally {\n pending = false;\n }\n }, delay);\n }) as F;\n};\n\n/**\n * Debounce callback: delays execution until calls stop.\n * Each new call resets the timer, so the callback fires only after the delay elapses with no further calls (trailing-edge).\n * Use when you want to react to the end of a burst of events (e.g. user stops typing).\n *\n * @param cb Callback to invoke.\n * @param delay Idle time (ms) to wait after the last call before invoking.\n * @returns Wrapped function that postpones invocation until activity ceases.\n */\nexport const debounce = <F extends Callback>(cb: F, delay = 100): F => {\n let t: ReturnType<typeof setTimeout>;\n return ((...args: any[]) => {\n clearTimeout(t);\n t = setTimeout(() => cb(...args), delay);\n }) as F;\n};\n\n/**\n * Throttle callback: limits execution to at most once per interval.\n * The callback fires immediately on the first call;\n * subsequent calls within the same interval are dropped (leading-edge).\n * Use when you need regular updates at a bounded rate (e.g. scroll or resize handlers).\n *\n * @param cb Callback to invoke.\n * @param delay Minimum interval (ms) between successive invocations.\n * @returns Wrapped function that rate-limits invocations.\n */\nexport const throttle = <F extends Callback>(cb: F, delay = 100): F => {\n let lastCall = 0;\n return ((...args: any[]) => {\n const now = Date.now();\n if (now - lastCall >= delay) {\n cb(...args);\n lastCall = now;\n }\n }) as F;\n};\n\n/**\n * Debounce and throttle callback.\n * Executes immediately on the first call (throttle), prevents calls during the throttle window,\n * and ensures a final call happens after activity stops (debounce).\n *\n * @param cb Callback to invoke.\n * @param delay Time window for both throttle and debounce.\n * @returns A new function that combines throttle and debounce behavior.\n */\nexport const debounceAndThrottle = <F extends Callback>(cb: F, delay = 100): F => {\n let timeout: ReturnType<typeof setTimeout>;\n let lastCall = 0;\n\n return ((...args: any[]) => {\n const now = Date.now();\n const delta = now - lastCall;\n\n // Clear any pending debounced call.\n clearTimeout(timeout);\n\n // Throttle: execute immediately if enough time has passed.\n if (delta >= delay) {\n cb(...args);\n lastCall = now;\n } else {\n // Debounce: schedule to execute after the remaining time.\n timeout = setTimeout(() => {\n cb(...args);\n lastCall = Date.now();\n }, delay - delta);\n }\n }) as F;\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { type EventEmitter } from 'node:events';\n\nimport { asyncTimeout } from './timeout';\n\n/**\n * Adds the listener and returns a function to remove it.\n * Promotes removing listeners when cleaning up objects (to prevent leaks).\n */\nexport const onEvent = (eventEmitter: EventEmitter, eventName: string, callback: (args: any) => void) => {\n eventEmitter.on(eventName, callback);\n return () => eventEmitter.off(eventName, callback);\n};\n\n/**\n * @deprecated\n */\nexport const addListener = (eventEmitter: EventEmitter, eventName: string, callback: () => void) => {\n const off = onEvent(eventEmitter, eventName, callback);\n return {\n remove: () => off(),\n };\n};\n\n/**\n * Waits for an event with an optional test condition.\n */\nexport const waitForEvent = (\n eventEmitter: EventEmitter,\n eventName: string,\n test?: (args: any) => boolean,\n timeout?: number,\n error?: Error | string,\n): Promise<any> => {\n let off;\n\n const promise = new Promise((resolve) => {\n off = onEvent(eventEmitter, eventName, (...args) => {\n if (!test || test(...args)) {\n resolve(...args);\n }\n });\n });\n\n return timeout ? asyncTimeout(promise, timeout, error ?? new Error()).finally(off) : promise.finally(off);\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\n// Import explicit resource management polyfill.\nimport '@dxos/util';\nimport { warnAfterTimeout } from '@dxos/debug';\n\n/**\n * A locking mechanism to ensure that a given section of the code is executed by only one single \"thread\" at a time.\n *\n * Functions are chained in a structure similar to a linked list.\n * `_lastPromise` always contains the function that will finish executing last.\n *\n * Initially it is set to `Promise.resolve()` -- a promise that resolves immediately.\n * Enqueuing is done by attaching provided function to the `_lastPromise` via a `.then()` call\n * then updating the `_lastPromise` variable.\n *\n * It is important that enqueuing is done atomically:\n * there are no `await`s in `executeSynchronized` and it's not async while still returning a promise.\n *\n * Java docs reference on synchronized sections:\n * https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html\n */\nexport class Mutex {\n private _queue = Promise.resolve();\n private _queueLength = 0;\n\n private _tag: string | null = null;\n\n get tag() {\n return this._tag;\n }\n\n isLocked(): boolean {\n return this._queueLength > 0;\n }\n\n /**\n * Acquires the lock.\n * Caller is responsible for releasing the lock using the returned callback.\n * NOTE: Using `executeSynchronized` is preferred over using `acquire` directly.\n * @returns Release callback\n */\n async acquire(tag?: string): Promise<MutexGuard> {\n const prev = this._queue;\n\n // Immediately update the promise before invoking any async actions so that next invocation waits for our task to complete.\n let guard!: MutexGuard;\n this._queueLength++;\n this._queue = new Promise((resolve) => {\n guard = new MutexGuard(() => {\n this._queueLength--;\n this._tag = null;\n resolve();\n });\n });\n\n await prev;\n\n if (tag !== undefined) {\n this._tag = tag;\n }\n return guard;\n }\n\n /**\n * Waits for all previous executions to complete and then executes a given function.\n * Only a single function can be executed at a time.\n * Function are executed in the same order as `executeSynchronized` is called.\n * WARNING: Calling `executeSynchronized` inside of `executeSynchronized` on the same lock instance is a deadlock.\n */\n async executeSynchronized<T>(fun: () => Promise<T>): Promise<T> {\n const guard = await this.acquire();\n\n try {\n return await fun();\n } finally {\n guard.release();\n }\n }\n}\n\nexport class MutexGuard {\n constructor(private readonly _release: () => void) {}\n\n /**\n * Releases the lock.\n */\n release(): void {\n this._release();\n }\n\n [Symbol.dispose](): void {\n this.release();\n }\n}\n\nconst classMutexSymbol = Symbol('class-mutex');\n\ninterface LockableClass {\n [classMutexSymbol]?: Mutex;\n}\n\nconst FORCE_DISABLE_WARNING = false;\n\n// Enabled only in tests by default.\nconst enableWarning = !FORCE_DISABLE_WARNING && (globalThis as any).mochaExecutor;\n\n/**\n * Same as `synchronized` in Java.\n * Uses a lock global to the current class instance.\n * This way every synchronized method on the same instance will share a single lock.\n */\nexport const synchronized = (\n target: any,\n propertyName: string,\n descriptor: TypedPropertyDescriptor<(...args: any) => Promise<any>>,\n) => {\n const method = descriptor.value!;\n descriptor.value = async function synchronizedMethod(this: any & LockableClass, ...args: any) {\n const mutex: Mutex = (this[classMutexSymbol] ??= new Mutex());\n\n const tag = `${target.constructor.name}.${propertyName}`;\n\n // Disable warning in prod to avoid performance penalty.\n let guard;\n if (!enableWarning) {\n guard = await mutex.acquire(tag);\n } else {\n guard = await warnAfterTimeout(10_000, `lock on ${tag} (taken by ${mutex.tag})`, () => mutex.acquire(tag));\n }\n\n try {\n return await method.apply(this, args);\n } finally {\n guard.release();\n }\n };\n Object.defineProperty(descriptor.value, 'name', { value: propertyName + '$synchronized' });\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nimport { TimeoutError } from './errors';\nimport { asyncTimeout } from './timeout';\n\n/**\n * Returns a tuple containing a Promise that will be resolved when the resolver function is called.\n * @deprecated Use `Trigger` instead.\n */\nexport const trigger = <T = void>(timeout?: number): [() => Promise<T>, (arg: T) => void] => {\n // eslint-disable-line prefer-arrow-functions/prefer-arrow-functions\n let callback: (arg: T) => void;\n\n const promise = new Promise<T>((resolve, reject) => {\n if (timeout) {\n setTimeout(() => reject(new Error(`Timed out after ${timeout.toLocaleString()}ms`)), timeout);\n }\n\n callback = resolve;\n });\n\n const provider = () => promise;\n const resolver = (value: T) => callback(value);\n\n return [provider, resolver];\n};\n\nexport type TriggerOptions = {\n autoReset: boolean;\n};\n\nexport enum TriggerState {\n WAITING = 'WAITING',\n RESOLVED = 'RESOLVED',\n REJECTED = 'REJECTED',\n}\n\n/**\n * Enables blocked listeners to be awakened with optional timeouts.\n *\n * Has two states:\n * - WAITING: promise is in pending state and will be resolved once `wake()` is called.\n * - RESOLVED: promise is already resolved, and all calls to `wait()` resolve immediately.\n * - REJECTED: promise is rejected, and all calls to `wait()` return rejected promise.\n *\n * Trigger starts in WAITING state initially.\n * Use `reset()` to switch resolved trigger back to WAITING state.\n */\nexport class Trigger<T = void> {\n private _promise!: Promise<T>;\n private _resolve!: (value: T | PromiseLike<T>) => void;\n private _reject!: (error: Error) => void;\n private _state: TriggerState = TriggerState.WAITING;\n\n constructor(private _options: TriggerOptions = { autoReset: false }) {\n this.reset();\n }\n\n get state() {\n return this._state;\n }\n\n /**\n * Wait until wake is called, with optional timeout.\n */\n async wait({ timeout }: { timeout?: number } = {}): Promise<T> {\n if (timeout) {\n return asyncTimeout(this._promise, timeout, new TimeoutError(timeout));\n } else {\n return this._promise;\n }\n }\n\n /**\n * Wake blocked callers (if any).\n * NOOP if the trigger is already resolved.\n */\n wake(value: T): this {\n if (this._state !== TriggerState.WAITING) {\n return this;\n }\n this._state = TriggerState.RESOLVED;\n this._resolve(value);\n if (this._options.autoReset) {\n return this.reset();\n }\n\n return this;\n }\n\n /**\n * Reset promise (new waiters will wait).\n */\n reset(): this {\n this._state = TriggerState.WAITING;\n this._promise = new Promise<T>((resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n });\n\n this._promise.catch(() => {}); // Prevent unhandled promise rejections.\n return this;\n }\n\n /**\n * Throw error to blocked callers (if any).\n * NOOP if the trigger is already resolved.\n */\n throw(error: Error): this {\n if (this._state !== TriggerState.WAITING) {\n return this;\n }\n this._state = TriggerState.REJECTED;\n this._reject(error);\n if (this._options.autoReset) {\n return this.reset();\n }\n\n return this;\n }\n}\n\ntype LatchProps = {\n count?: number;\n timeout?: number;\n};\n\ntype LatchResult = [() => Promise<number>, () => number, (err: Error) => void];\n\n/**\n * Returns a callback and a promise that's resolved when the callback is called n times.\n * @deprecated Use `Trigger` instead.\n */\nexport const latch = ({ count = 1, timeout }: LatchProps = {}): LatchResult => {\n invariant(count >= 0);\n\n let t: ReturnType<typeof setTimeout>;\n let doResolve: (value: number) => void;\n let doReject: (err: Error) => void;\n const promise = new Promise<number>((resolve, reject) => {\n doResolve = (value) => {\n clearTimeout(t);\n resolve(value);\n };\n\n doReject = (err) => {\n clearTimeout(t);\n reject(err);\n };\n });\n\n if (count === 0) {\n setTimeout(() => {\n doResolve(0);\n });\n } else {\n if (timeout) {\n t = setTimeout(() => {\n doReject(new Error(`Timed out after ${timeout.toLocaleString()}ms`));\n }, timeout);\n }\n }\n\n let i = 0;\n return [\n async () => await promise,\n () => {\n if (++i === count) {\n doResolve(i);\n }\n\n return i;\n },\n (err: Error) => doReject(err),\n ];\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport Observable from 'zen-observable';\nimport type { ObservableLike, Observer, Subscriber } from 'zen-observable/esm';\nimport PushStream from 'zen-push';\n\nimport { type Event } from './events';\nimport { Trigger } from './trigger';\n\nexport { Observable, PushStream, type Subscriber };\n\n// Inspired by:\n// https://github.com/zenparsing/zen-push/blob/39949f1/index.js#L93\n// https://github.com/apollographql/apollo-client/blob/a0eb4d6/src/utilities/observables/Concast.ts\n\n/**\n * Observable which supports multiple subscribers and stores the current value.\n *\n * The current value is emitted to new subscribers on subscription.\n */\nexport class MulticastObservable<T> extends Observable<T> {\n private readonly _observers = new Set<Observer<T>>();\n private readonly _observable: Observable<T>;\n private readonly _completed = new Trigger();\n\n constructor(\n subscriber: Observable<T> | Subscriber<T>,\n protected _value?: T,\n ) {\n super((observer) => this._subscribe(observer));\n\n this._observable = typeof subscriber === 'function' ? new Observable(subscriber) : subscriber;\n // Automatically subscribe to source observable.\n // Ensures that the current value is always up to date.\n // TODO(wittjosiah): Does this subscription need to be cleaned up? Where should that happen?\n this._observable.subscribe(this._handlers);\n }\n\n static override from<T>(\n value: Observable<T> | ObservableLike<T> | ArrayLike<T> | Event<T>,\n initialValue?: T,\n ): MulticastObservable<T> {\n if ('emit' in value) {\n return new MulticastObservable((observer) => {\n // TODO(wittjosiah): Do error/complete matter for events?\n value.on((data) => {\n observer.next(data);\n });\n }, initialValue);\n }\n\n const observable = Observable.from(value);\n return new MulticastObservable(observable, initialValue);\n }\n\n static override of<T>(...items: T[]): MulticastObservable<T> {\n return new MulticastObservable(Observable.of(...items.slice(1)), items[0]);\n }\n\n /**\n * @returns Stable reference to an observable that always returns `undefined`.\n */\n static empty(): MulticastObservable<null> {\n return EMPTY_OBSERVABLE;\n }\n\n /**\n * Get the current value of the observable.\n */\n get(): T {\n // TODO(wittjosiah): Is there a better way to handle this?\n // `this._value` is not guaranteed to be set for compatibility with `Observable` base class.\n // `get()` should always return `T` to avoid having to sprinkle conditional logic.\n if (this._value === undefined) {\n throw new Error('MulticastObservable is not initialized.');\n }\n\n return this._value;\n }\n\n /**\n * Wait for the observable to complete.\n *\n * @returns Promise that resolves to the value of the observable at the time of completion.\n */\n async wait({ timeout }: { timeout?: number } = {}): Promise<T> {\n await this._completed.wait({ timeout });\n return this.get();\n }\n\n override forEach(callback: (value: T) => void): Promise<void> {\n return this._observable.forEach(callback);\n }\n\n override map<R>(callback: (value: T) => R): MulticastObservable<R> {\n return new MulticastObservable(this._observable.map(callback), this._value && callback(this._value));\n }\n\n override filter(callback: (value: T) => boolean): MulticastObservable<T> {\n return new MulticastObservable(\n this._observable.filter(callback),\n this._value && callback(this._value) ? this._value : undefined,\n );\n }\n\n override reduce<R = T>(callback: (previousValue: R, currentValue: T) => R, initialValue?: R): MulticastObservable<R> {\n return new MulticastObservable(\n initialValue ? this._observable.reduce(callback, initialValue) : this._observable.reduce(callback),\n initialValue ?? (this._value as R),\n );\n }\n\n override flatMap<R>(callback: (value: T) => MulticastObservable<R>): MulticastObservable<R> {\n return new MulticastObservable(this._observable.flatMap(callback), this._value && callback(this._value).get());\n }\n\n override concat<R>(...observables: Array<Observable<R>>): MulticastObservable<R> {\n return new MulticastObservable(this._observable.concat(...observables), this._value as R);\n }\n\n /**\n * Concatenates multicast observables without losing the current value.\n * @param reducer reduces the values of any multicast observables being concatenated into a single value\n * @param observables observables to concatenate\n * @returns concatenated observable\n */\n losslessConcat<R>(\n reducer: (currentValue: R, newValues: R[]) => R,\n ...observables: Array<Observable<R>>\n ): MulticastObservable<R> {\n const multicast = observables.filter(\n (observable): observable is MulticastObservable<R> => observable instanceof MulticastObservable,\n );\n const value = reducer(\n this._value as R,\n multicast.map((observable) => observable.get()),\n );\n return new MulticastObservable(this._observable.concat(...observables), value);\n }\n\n private _subscribe(observer: Observer<T>): () => void {\n if (!this._observers.has(observer)) {\n this._observers.add(observer);\n }\n\n if (this._value !== undefined) {\n observer.next?.(this._value);\n }\n\n return () => {\n this._observers.delete(observer);\n };\n }\n\n private _handlers: Observer<T> = {\n next: (value) => {\n this._value = value;\n this._observers.forEach((observer) => observer.next?.(value));\n },\n error: (err) => {\n this._observers.forEach((observer) => observer.error?.(err));\n },\n complete: () => {\n this._completed.wake();\n this._observers.forEach((observer) => observer.complete?.());\n },\n };\n}\n\nconst EMPTY_OBSERVABLE = MulticastObservable.of(null);\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { createSetDispatch } from '@dxos/util';\n\nimport { type CleanupFn } from './cleanup';\n\n/**\n * Return type for processes that support cancellable subscriptions.\n * The handler object implements the observable events.\n * @deprecated\n */\nexport interface ObservableValue<Events, Value = unknown> {\n value?: Value;\n setValue(value: Value): void;\n subscribe(handler: Events): CleanupFn;\n}\n\n/**\n * Provider that manages a set of subscriptions.\n * @deprecated\n */\n// TODO(burdon): Support multiple subscribers.\n// https://betterprogramming.pub/compare-leading-javascript-functional-reactive-stream-libraries-544163c1ded6\n// https://github.com/apollographql/apollo-client/tree/main/src/utilities/observables\n// https://github.com/mostjs/core\nexport class ObservableProvider<Events extends {}, Value = unknown> implements ObservableValue<Events, Value> {\n protected readonly _handlers = new Set<Events>();\n private readonly _proxy = createSetDispatch<Events>({\n handlers: this._handlers,\n });\n\n private _value?: Value;\n\n /**\n * Proxy used to dispatch callbacks to each subscription.\n */\n get callback(): Events {\n return this._proxy;\n }\n\n get value() {\n return this._value;\n }\n\n setValue(value: Value): void {\n this._value = value;\n }\n\n subscribe(handler: Events): CleanupFn {\n this._handlers.add(handler);\n return () => {\n this._handlers.delete(handler);\n };\n }\n}\n\n/**\n * @deprecated\n */\nexport interface CancellableObservableEvents {\n onCancelled?(): void;\n}\n\n/**\n * @deprecated\n */\nexport interface CancellableObservable<\n Events extends CancellableObservableEvents,\n Value = unknown,\n> extends ObservableValue<Events, Value> {\n cancel(): Promise<void>;\n}\n\n/**\n * Implements subscriptions with ability to be cancelled.\n * @deprecated\n */\nexport class CancellableObservableProvider<\n Events extends CancellableObservableEvents,\n> extends ObservableProvider<Events> {\n private _cancelled = false;\n\n constructor(private readonly _handleCancel?: () => Promise<void>) {\n super();\n }\n\n get cancelled() {\n return this._cancelled;\n }\n\n async cancel(): Promise<void> {\n if (this._cancelled) {\n return;\n }\n\n this._cancelled = true;\n await this._handleCancel?.();\n this.callback.onCancelled?.();\n }\n}\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { StackTrace } from '@dxos/debug';\nimport { log } from '@dxos/log';\n\nconst enabled = typeof process !== 'undefined' && !!process.env.DX_TRACK_LEAKS;\n\ntype OpenResource = {\n name: string;\n openStack: StackTrace;\n};\n\nconst openResources = new Set<OpenResource>();\n\nconst handleSymbol = Symbol('checkLeaksHandle');\n\nexport const trackResource = (resourceProvider: () => OpenResource): (() => void) => {\n if (!enabled) {\n return () => {};\n }\n const resource = resourceProvider();\n\n openResources.add(resource);\n\n return () => {\n openResources.delete(resource);\n };\n};\n\n/**\n * Makes sure that the resource is at the end of the test.\n *\n * Example:\n *\n * ```typescript\n * @trackLeaks('open', 'close')\n * class Resource {}\n * ```\n */\nexport const trackLeaks =\n (open: string, close: string): ClassDecorator =>\n (target: any) => {\n if (!enabled) {\n return;\n }\n\n const openMethod = target.prototype[open];\n const closeMethod = target.prototype[close];\n if (!openMethod || !closeMethod) {\n throw new Error(`Cannot find ${open} or ${close} method in ${target.name}`);\n }\n\n {\n target.prototype[open] = async function (this: any, ...args: any) {\n this[handleSymbol] = trackResource(() => ({\n name: target.name,\n openStack: new StackTrace(),\n }));\n\n return openMethod.apply(this, args);\n };\n Object.defineProperty(target.prototype[open], 'name', { value: open + '$checkLeaks' });\n }\n\n {\n target.prototype[close] = async function (this: any, ...args: any) {\n this[handleSymbol]?.();\n\n return closeMethod.apply(this, args);\n };\n Object.defineProperty(target.prototype[close], 'name', { value: close + '$checkLeaks' });\n }\n };\n\nexport const dumpLeaks = () => {\n if (!enabled) {\n return;\n }\n\n log.info(`Leaked resources ${openResources.size}:`);\n for (const resource of openResources) {\n log.info(`- ${resource.name} at`);\n log.info(resource.openStack.getStack(1));\n log.info('\\n');\n }\n};\n\nif (enabled) {\n (global as any).dxDumpLeaks = dumpLeaks;\n}\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { Context, ContextDisposedError } from '@dxos/context';\nimport { StackTrace } from '@dxos/debug';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { trackResource } from './track-leaks';\nimport { Trigger } from './trigger';\n\n/**\n * A task that can be scheduled to run in the next event loop iteration.\n * Could be triggered multiple times, but only runs once.\n * If a new task is triggered while a previous one is running, the next run would occur immediately after the current run has finished.\n */\n// TODO(dmaretskyi): Consider calling `join` on context dispose.\n// TODO(burdon): Add throttling support.\nexport class DeferredTask {\n private _scheduled = false;\n private _currentTask: Promise<void> | null = null; // Can't be rejected.\n private _nextTask = new Trigger();\n\n constructor(\n private readonly _ctx: Context,\n private readonly _callback: () => Promise<void>,\n ) {}\n\n get scheduled() {\n return this._scheduled;\n }\n\n /**\n * Schedule the task to run asynchronously.\n */\n schedule(): void {\n if (this._scheduled) {\n return; // Already scheduled.\n }\n\n scheduleTask(this._ctx, async () => {\n // The previous task might still be running, so we need to wait for it to finish.\n //\n // A single await suffices only because this is the one claim site and `_scheduled` collapses\n // schedules into one waiter. A second path claiming `_currentTask` directly would let two\n // callbacks run at once — see dxos/edge#758.\n await this._currentTask; // Can't be rejected.\n\n // Reset the flag. New tasks can now be scheduled. They would wait for the callback to finish.\n this._scheduled = false;\n const completionTrigger = this._nextTask;\n this._nextTask = new Trigger(); // Re-create the trigger as opposed to resetting it since there might be listeners waiting for it.\n\n // Store the promise so that new tasks could wait for this one to finish.\n this._currentTask = runInContextAsync(this._ctx, () => this._callback()).then(() => {\n completionTrigger.wake();\n });\n });\n\n this._scheduled = true;\n }\n\n /**\n * Schedule the task to run and wait for it to finish.\n */\n async runBlocking(): Promise<void> {\n if (this._ctx.disposed) {\n throw new ContextDisposedError();\n }\n\n this.schedule();\n await this._nextTask.wait();\n }\n\n /**\n * Waits for the current task to finish if it is running.\n * Does not schedule a new task.\n */\n async join(): Promise<void> {\n await this._currentTask;\n }\n}\n\n// TODO(dmaretskyi): Protyping this an alternative API to DeferredTask.\nexport class AsyncTask {\n #callback: () => Promise<void>;\n #ctx?: Context = undefined;\n\n #scheduled = false;\n #currentTask: Promise<void> | null = null; // Can't be rejected.\n #nextTask = new Trigger();\n\n constructor(callback: () => Promise<void>) {\n this.#callback = callback;\n }\n\n get scheduled() {\n return this.#scheduled;\n }\n\n /**\n * Context of the resource that owns the task.\n * When the context is disposed, the task is cancelled and cannot be scheduled again.\n */\n open(): void {\n this.#ctx = new Context();\n }\n\n /**\n * Closes the task and waits for it to finish if it is running.\n */\n async close(): Promise<void> {\n await this.#ctx?.dispose();\n await this.join();\n this.#ctx = undefined;\n }\n\n [Symbol.asyncDispose](): Promise<void> {\n return this.close();\n }\n\n /**\n * Schedule the task to run asynchronously.\n */\n // TODO(dmaretskyi): Add scheduleAt. Where the earlier time will override the later one.\n schedule(): void {\n if (!this.#ctx || this.#ctx.disposed) {\n throw new Error('AsyncTask not open');\n }\n\n if (this.#scheduled) {\n return; // Already scheduled.\n }\n\n scheduleTask(this.#ctx, async () => {\n // The previous task might still be running, so we need to wait for it to finish.\n await this.#currentTask; // Can't be rejected.\n\n if (!this.#ctx || this.#ctx.disposed) {\n return;\n }\n\n // Reset the flag. New tasks can now be scheduled. They would wait for the callback to finish.\n this.#scheduled = false;\n const completionTrigger = this.#nextTask;\n this.#nextTask = new Trigger(); // Re-create the trigger as opposed to resetting it since there might be listeners waiting for it.\n\n // Store the promise so that new tasks could wait for this one to finish.\n this.#currentTask = runInContextAsync(this.#ctx, () => this.#callback()).then(() => {\n completionTrigger.wake();\n });\n });\n\n this.#scheduled = true;\n }\n\n /**\n * Schedule the task to run and wait for it to finish.\n */\n async runBlocking(): Promise<void> {\n if (this.#ctx?.disposed) {\n throw new ContextDisposedError();\n }\n\n this.schedule();\n await this.#nextTask.wait();\n }\n\n /**\n * Waits for the current task to finish if it is running.\n * Does not schedule a new task.\n */\n async join(): Promise<void> {\n await this.#currentTask;\n }\n}\n\nexport const runInContext = (ctx: Context, fn: () => void) => {\n try {\n fn();\n } catch (err: any) {\n ctx.raise(err);\n }\n};\n\nexport const runInContextAsync = async (ctx: Context, fn: () => MaybePromise<void>) => {\n try {\n await fn();\n } catch (err: any) {\n ctx.raise(err);\n }\n};\n\nexport const scheduleMicroTask = (ctx: Context, fn: () => MaybePromise<void>) => {\n queueMicrotask(async () => {\n if (ctx.disposed) {\n return;\n }\n await runInContextAsync(ctx, fn);\n });\n};\n\nexport const scheduleTask = (ctx: Context, fn: () => MaybePromise<void>, afterMs?: number) => {\n const clearTracking = trackResource(() => ({\n name: `task (${fn.name || 'anonymous'})`,\n openStack: new StackTrace(),\n }));\n\n const timeout = setTimeout(async () => {\n clearDispose();\n await runInContextAsync(ctx, fn);\n clearTracking();\n }, afterMs);\n\n const clearDispose = ctx.onDispose(() => {\n clearTracking();\n clearTimeout(timeout);\n });\n};\n\n/**\n * Run the task in the next event loop iteration, and then repeat in `interval` ms after the previous iteration completes.\n */\nexport const scheduleTaskInterval = (ctx: Context, task: () => Promise<void>, interval: number) => {\n const clearTracking = trackResource(() => ({\n name: `repeating task (${task.name || 'anonymous'})`,\n openStack: new StackTrace(),\n }));\n\n let timeoutId: NodeJS.Timeout;\n\n const run = async () => {\n await runInContextAsync(ctx, task);\n if (ctx.disposed) {\n return;\n }\n timeoutId = setTimeout(run, interval);\n };\n\n timeoutId = setTimeout(run, interval);\n ctx.onDispose(() => {\n clearTracking();\n clearTimeout(timeoutId);\n });\n};\n\nexport const scheduleExponentialBackoffTaskInterval = (\n ctx: Context,\n task: () => Promise<void>,\n initialInterval: number,\n) => {\n const clearTracking = trackResource(() => ({\n name: `repeating task (${task.name || 'anonymous'})`,\n openStack: new StackTrace(),\n }));\n\n let timeoutId: NodeJS.Timeout;\n\n let interval = initialInterval;\n const repeat = async () => {\n await runInContextAsync(ctx, task);\n if (ctx.disposed) {\n return;\n }\n interval *= 2;\n timeoutId = setTimeout(repeat, interval);\n };\n\n timeoutId = setTimeout(repeat, interval);\n ctx.onDispose(() => {\n clearTracking();\n clearTimeout(timeoutId);\n });\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { LifecycleState, Resource, cancelWithContext } from '@dxos/context';\nimport { warnAfterTimeout } from '@dxos/debug';\nimport { log } from '@dxos/log';\n\nimport { synchronized } from './mutex';\nimport { DeferredTask } from './task-scheduling';\nimport { sleep } from './timeout';\n\nconst INIT_RESTART_DELAY = 100;\nconst DEFAULT_MAX_RESTART_DELAY = 5000;\n\n/**\n * Minimum duration a connection must stay up before it is considered stable and the backoff is\n * reset. A connection that drops sooner keeps escalating the delay, so an endpoint that accepts\n * then immediately closes the connection cannot produce a zero-delay reconnect loop.\n */\nconst STABLE_CONNECTION_THRESHOLD = 5000;\n\nexport type PersistentLifecycleProps<T> = {\n /**\n * Create connection.\n * If promise resolves successfully, connection is considered established.\n */\n start: () => Promise<T | undefined>;\n\n /**\n * Reset connection to initial state.\n */\n stop: (state: T) => Promise<void>;\n\n /**\n * Called after successful start.\n */\n onRestart?: () => Promise<void>;\n\n /**\n * Maximum delay between restartion attempts.\n * Default: 5000ms\n */\n maxRestartDelay?: number;\n};\n\n/**\n * Handles restarts (e.g. persists connection).\n * Restarts are scheduled with exponential backoff.\n */\nexport class PersistentLifecycle<T> extends Resource {\n private readonly _start: () => Promise<T | undefined>;\n private readonly _stop: (state: T) => Promise<void>;\n private readonly _onRestart?: () => Promise<void>;\n private readonly _maxRestartDelay: number;\n\n private _currentState: T | undefined = undefined;\n private _restartTask?: DeferredTask = undefined;\n private _restartAfter = 0;\n private _connectedAt: number | undefined = undefined;\n\n constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }: PersistentLifecycleProps<T>) {\n super();\n this._start = start;\n this._stop = stop;\n this._onRestart = onRestart;\n this._maxRestartDelay = maxRestartDelay;\n }\n\n get state() {\n return this._currentState;\n }\n\n @synchronized\n protected override async _open(): Promise<void> {\n this._restartTask = new DeferredTask(this._ctx, async () => {\n try {\n await this._restart();\n } catch (err) {\n // Suppress noise from restarts that race with shutdown.\n if (this._ctx?.disposed) {\n return;\n }\n log.warn('Restart failed', { err });\n this._restartTask?.schedule();\n }\n });\n\n try {\n this._currentState = await this._start();\n this._connectedAt = Date.now();\n } catch (err) {\n // Suppress noise when shutdown was requested while the initial start was in flight.\n if (this._ctx?.disposed) {\n return;\n }\n log.warn('Start failed', { err });\n this._restartTask?.schedule();\n }\n }\n\n protected override async _close(): Promise<void> {\n await this._restartTask?.join();\n await this._stopCurrentState();\n this._restartTask = undefined;\n }\n\n private async _restart(): Promise<void> {\n log(`restarting in ${this._restartAfter}ms`, { state: this._lifecycleState });\n\n // Reset the backoff only if the previous connection stayed up long enough to be considered\n // stable. A connection that drops shortly after starting must keep escalating the delay,\n // otherwise reconnects degenerate into a hot loop.\n if (this._connectedAt !== undefined && Date.now() - this._connectedAt >= STABLE_CONNECTION_THRESHOLD) {\n this._restartAfter = 0;\n }\n this._connectedAt = undefined;\n\n await this._stopCurrentState();\n if (this._lifecycleState !== LifecycleState.OPEN) {\n return;\n }\n await cancelWithContext(this._ctx!, sleep(this._restartAfter));\n this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);\n\n // May fail if the connection is not established.\n await warnAfterTimeout(5_000, 'Connection establishment takes too long', async () => {\n this._currentState = await this._start();\n });\n\n this._connectedAt = Date.now();\n await this._onRestart?.();\n }\n\n private async _stopCurrentState(): Promise<void> {\n if (this._currentState) {\n try {\n await this._stop(this._currentState);\n } catch (err) {\n log.catch(err);\n }\n this._currentState = undefined;\n }\n }\n\n /**\n * Scheduling restart should be done from outside.\n */\n @synchronized\n async scheduleRestart(): Promise<void> {\n if (this._lifecycleState !== LifecycleState.OPEN) {\n return;\n }\n this._restartTask!.schedule();\n }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\n\nimport { Trigger } from './trigger';\n\ninterface PushAsyncIterable<T, TReturn = any> extends AsyncIterable<T, TReturn> {\n /**\n * Push a value into the iterable.\n */\n next(value: T): void;\n\n /**\n * Return a value from the iterable.\n */\n return(value?: TReturn): void;\n\n /**\n * Throw an error from the iterable.\n */\n throw(value?: any): void;\n}\n\n/**\n * Creates an async iterable where the data is pushed by calling methods on the iterable.\n */\nexport const makePushIterable = <T, TReturn = any>(): PushAsyncIterable<T, TReturn> => {\n const buf: ({ kind: 'next'; value: T } | { kind: 'return'; value: TReturn } | { kind: 'throw'; value?: any })[] = [];\n const trigger = new Trigger({ autoReset: true });\n\n return {\n [Symbol.asyncIterator]() {\n return {\n next: async (): Promise<IteratorResult<T, TReturn>> => {\n while (buf.length === 0) {\n await trigger.wait();\n }\n\n const item = buf.shift();\n invariant(item);\n\n switch (item.kind) {\n case 'next':\n return { value: item.value, done: false };\n case 'return':\n return { value: item.value, done: true };\n case 'throw':\n throw item.value;\n }\n },\n };\n },\n next: (value: T) => {\n buf.push({ kind: 'next', value });\n trigger.wake();\n },\n return: (value: TReturn) => {\n buf.push({ kind: 'return', value });\n trigger.wake();\n },\n throw: (value?: any) => {\n buf.push({ kind: 'throw', value });\n trigger.wake();\n },\n };\n};\n","//\n// Copyright 2022 DXOS.org\n//\n\n/**\n * Concatenate a readable stream's data into a single array.\n *\n * From https://github.com/stream-utils/stream-to-array, but does not depend on\n * any-promise which does not work in web workers due to using window in its\n * browser shim.\n */\nexport const streamToArray = (stream: NodeJS.ReadableStream): Promise<any[]> => {\n let deferred: Promise<any[]>;\n\n if (!stream.readable) {\n deferred = Promise.resolve([]);\n } else {\n deferred = new Promise((resolve, reject) => {\n // stream is already ended\n if (!stream.readable) {\n return resolve([]);\n }\n\n let arr: any[] = [];\n\n const onData = (doc: any) => {\n arr?.push(doc);\n };\n\n const onEnd = (err: any) => {\n if (err) {\n reject(err);\n } else {\n resolve(arr);\n }\n cleanup();\n };\n\n const onClose = () => {\n resolve(arr);\n cleanup();\n };\n\n const cleanup = () => {\n arr = [];\n stream.removeListener('data', onData);\n stream.removeListener('end', onEnd);\n stream.removeListener('error', onEnd);\n stream.removeListener('close', onClose);\n };\n\n stream.on('data', onData);\n stream.on('end', onEnd);\n stream.on('error', onEnd);\n stream.on('close', onClose);\n });\n }\n\n return deferred;\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { sleep } from './timeout';\nimport { Trigger } from './trigger';\n\n/**\n * NOTE: THIS SHOULD ONLY BE USED IN TESTS.\n *\n * Returns a Promise which resolves when `condFn` returns truthy.\n * The value returned by `condFn` is used to resolve the Promise.\n * @param condition Function to call.\n * @param [timeout] How long to wait, in milliseconds (0 = no timeout).\n * @param [interval=10] How frequently to check, in milliseconds.\n */\nexport const waitForCondition = <FunctionType extends (...args: any) => any>({\n condition,\n timeout = 0,\n interval = 10,\n error,\n breakOnError = false,\n}: {\n condition: FunctionType;\n timeout?: number;\n interval?: number;\n error?: Error;\n breakOnError?: boolean;\n}) => {\n const stopTime = timeout ? Date.now() + timeout : 0;\n const trigger = new Trigger<ReturnType<FunctionType>>();\n const waiter = async () => {\n // eslint-disable-next-line no-unmodified-loop-condition\n while (!stopTime || Date.now() < stopTime) {\n try {\n // eslint-disable-next-line no-await-in-loop\n const value = await condition();\n if (value) {\n trigger.wake(value);\n break;\n }\n } catch (err: any) {\n if (breakOnError === true) {\n trigger.throw(err);\n }\n }\n\n // eslint-disable-next-line no-await-in-loop\n await sleep(interval);\n }\n };\n\n setTimeout(waiter, 0);\n\n return trigger.wait({ timeout });\n};\n\nexport type UntilCallback<T> = (resolve: (value: T) => void, reject: (error: Error) => void) => Promise<T> | void;\n\n/**\n * Awaits promise.\n */\nexport const until = <T = void>(cb: UntilCallback<T>, timeout?: number): Promise<T> => {\n return new Promise((resolve, reject) => {\n const t =\n timeout &&\n setTimeout(() => {\n reject(new Error(`Timeout after ${t}ms`));\n }, timeout);\n\n setTimeout(async () => {\n try {\n await cb(\n (value: T) => {\n t && clearTimeout(t);\n resolve(value);\n },\n (error: Error) => {\n t && clearTimeout(t);\n reject(error);\n },\n );\n } catch (err) {\n reject(err);\n }\n });\n });\n};\n\n/**\n * Wait until promise resolves.\n */\nexport const untilPromise = <T = void>(cb: () => Promise<T>) => cb();\n\n/**\n * Wait until error is thrown.\n */\nexport const untilError = (cb: () => Promise<any>) => {\n return new Promise((resolve, reject) => {\n setTimeout(async () => {\n try {\n await cb();\n reject(new Error('No error was thrown.'));\n } catch (err) {\n resolve(err);\n }\n });\n });\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { Event, type ReadOnlyEvent } from './events';\n\nexport type TimerOptions = { count: number; interval: number; jitter?: number };\n\nexport type TimerCallback = (i: number) => Promise<boolean | void>;\n\n/**\n * Manages callback invocations at a interval with a possible jitter.\n * Note: The interval excludes the running time of the callback.\n */\nexport class Timer {\n private readonly _state = new Event<boolean>();\n private _timer?: NodeJS.Timeout;\n private _count = 0;\n\n constructor(private readonly _callback: TimerCallback) {}\n\n get state(): ReadOnlyEvent<boolean> {\n return this._state;\n }\n\n get running() {\n return !!this._timer;\n }\n\n start(options: TimerOptions, cb?: () => void): this {\n if (isNaN(options.count) || isNaN(options.interval)) {\n throw new Error(`Invalid options: ${JSON.stringify(options)}`);\n }\n\n if (this.running) {\n this.stop();\n }\n\n const stop = () => {\n this.stop();\n cb?.();\n };\n\n const run = () => {\n if (this._count >= (options.count ?? 0)) {\n stop();\n } else {\n const interval = (options.interval ?? 0) + Math.random() * (options.jitter ?? 0);\n this._timer = setTimeout(async () => {\n await this._callback(this._count++);\n run();\n }, interval);\n }\n };\n\n this._state.emit(true);\n this._count = 0;\n\n // Start asynchronously (give caller chance to register event listener).\n setTimeout(run);\n return this;\n }\n\n stop(): this {\n clearInterval(this._timer);\n this._timer = undefined;\n this._state.emit(false);\n return this;\n }\n}\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Context } from '@dxos/context';\n\nimport { scheduleMicroTask } from './task-scheduling';\nimport { Trigger } from './trigger';\n\nexport type UpdateSchedulerOptions = {\n /**\n * Maximum frequency of updates. If not specified, updates will be scheduled on every change.\n */\n maxFrequency?: number;\n};\n\n/**\n * Time period for update counting.\n */\nconst TIME_PERIOD = 1_000;\n\n/** A claimed run's error (or `undefined`). Resolves, never rejects — `runBlocking` re-throws it. */\ntype RunResult = Promise<Error | undefined>;\n\n/**\n * Runs a non-reentrant callback at most once at a time, coalescing triggers (optionally rate-limited\n * via {@link UpdateSchedulerOptions.maxFrequency}).\n *\n * The runner inside {@link trigger} is the only place the callback starts; `runBlocking` and\n * `forceTrigger` funnel into it. That, plus `_scheduled` collapsing triggers into one pending\n * runner, is what makes mutual exclusion hold by construction (dxos/edge#758).\n */\nexport class UpdateScheduler {\n /** The running pass; never rejects, non-null exactly while a run is executing. */\n private _currentTask: Promise<void> | null = null;\n private _scheduled = false;\n\n /**\n * Armed by the `trigger` that schedules a runner, woken by that runner when it claims — with the\n * run's promise, which `Trigger` adopts, so waiters resume when the run *finishes*. Lets\n * `runBlocking` await a run that does not exist in `_currentTask` yet.\n *\n * `reset()` abandons parked waiters, so it may only be armed in `trigger`'s non-coalesced branch:\n * waiters park only while `_scheduled` is true, and that branch requires it false.\n */\n private _runOutcome = new Trigger<RunResult>();\n\n /** Woken to make the pending runner skip its throttle delay. Replaced at claim time. */\n private _skipDelay = new Trigger();\n\n /**\n * `runBlocking` callers in flight, incremented synchronously before triggering. A failing run uses\n * it to tell whether someone will throw the error; counting microtask hops to a waiter's resume\n * would be a race.\n */\n private _observers = 0;\n\n private _lastUpdateTime = -TIME_PERIOD;\n\n constructor(\n private readonly _ctx: Context,\n private readonly _callback: () => Promise<void>,\n private readonly _params: UpdateSchedulerOptions = {},\n ) {\n _ctx.onDispose(async () => {\n await this._currentTask; // Context waits for callback to finish.\n // Release `runBlocking` callers parked on a runner that never claimed. NOOP if already woken.\n this._runOutcome.wake(Promise.resolve(undefined));\n });\n }\n\n get scheduled() {\n return this._scheduled;\n }\n\n /**\n * Schedule the callback to run asynchronously. Triggers issued while a run is pending or in flight\n * coalesce into the next run.\n */\n trigger(): void {\n if (this._scheduled) {\n return;\n }\n this._scheduled = true;\n this._runOutcome.reset(); // Only here — never on a coalesced trigger (see `_runOutcome`).\n\n // The only claim site. A single await suffices: nothing else claims, so this is the only waiter.\n scheduleMicroTask(this._ctx, async () => {\n await this._currentTask; // Never rejects.\n\n // Rate limiting. Suspending before the claim is safe only because no other claimant exists.\n if (this._params.maxFrequency) {\n const now = performance.now();\n const delay = this._lastUpdateTime + TIME_PERIOD / this._params.maxFrequency - now;\n if (delay > 0) {\n const skipDelay = this._skipDelay;\n await new Promise<void>((resolve) => {\n const timeoutId = setTimeout(() => {\n clearContext();\n resolve();\n }, delay);\n\n const clearContext = this._ctx.onDispose(() => {\n clearTimeout(timeoutId);\n resolve();\n });\n\n // `runBlocking`/`forceTrigger` cut the delay short.\n void skipDelay.wait().then(() => {\n clearTimeout(timeoutId);\n clearContext();\n resolve();\n });\n });\n }\n }\n\n if (this._ctx.disposed) {\n this._runOutcome.wake(Promise.resolve(undefined));\n return;\n }\n\n // The claim: one synchronous slice from here to the `wake` below.\n this._scheduled = false;\n this._skipDelay = new Trigger();\n this._lastUpdateTime = performance.now();\n const run: RunResult = this._callback().then(\n () => undefined,\n (error): Error => {\n // A thrown non-Error is typed away here, matching `Context.raise`'s signature.\n const failure = error as Error;\n // Unobserved failures go to the context, as trigger-path failures always have.\n if (this._observers === 0) {\n this._ctx.raise(failure);\n }\n return failure;\n },\n );\n const task: Promise<void> = run.then(() => {\n if (this._currentTask === task) {\n this._currentTask = null;\n }\n });\n this._currentTask = task;\n this._runOutcome.wake(run);\n });\n }\n\n /**\n * Run as soon as possible: schedules (coalescing with any pending run) and skips the throttle\n * delay. Does not start a concurrent callback.\n */\n forceTrigger(): void {\n this.trigger();\n this._skipDelay.wake();\n }\n\n /**\n * Waits for the current task to finish if it is running.\n * Does not schedule a new task, and does not wait for one that is merely scheduled.\n */\n async join(): Promise<void> {\n await this._currentTask;\n }\n\n /**\n * Ensure a run that observes everything enqueued so far, and wait for it to finish. Waits for the\n * pending run to claim, then for its result; that run necessarily claims after this call, so its\n * drain includes everything enqueued before it. Rejects with the run's error so a flush barrier can\n * see its batch was not handed off. No-op once disposed.\n */\n async runBlocking(): Promise<void> {\n if (this._ctx.disposed) {\n return;\n }\n this._observers++;\n try {\n this.trigger();\n this._skipDelay.wake();\n const error = await this._runOutcome.wait();\n if (error !== undefined) {\n throw error;\n }\n } finally {\n this._observers--;\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { r as asyncTimeout, t as Event } from "./chunk-events.mjs";
2
+ import { Duplex } from "@dxos/node-std/stream";
3
+ //#region src/test-stream.ts
4
+ /**
5
+ * Duplex stream for testing.
6
+ * If this stream is piped into another stream, use `push` to send data, and `assertReceivedAsync` to assert the received data.
7
+ */
8
+ var TestStream = class extends Duplex {
9
+ static async assertConnectivity(stream1, stream2, { timeout = 200 } = {}) {
10
+ stream1.push("ping");
11
+ stream2.push("pong");
12
+ await Promise.all([stream2.assertReceivedAsync("ping", { timeout }), stream1.assertReceivedAsync("pong", { timeout })]);
13
+ }
14
+ _received = Buffer.alloc(0);
15
+ _onWrite = new Event();
16
+ _write(chunk, encoding, callback) {
17
+ this._received = Buffer.concat([this._received, chunk]);
18
+ this._onWrite.emit();
19
+ callback();
20
+ }
21
+ _read(size) {}
22
+ assertReceivedAsync(data, { timeout = 200 } = {}) {
23
+ const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
24
+ return asyncTimeout(this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)), timeout);
25
+ }
26
+ };
27
+ //#endregion
28
+ export { TestStream };
29
+
30
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../../src/test-stream.ts"],"sourcesContent":["//\n// Copyright 2022 DXOS.org\n//\n\nimport { Duplex } from 'node:stream';\n\nimport { Event } from './events';\nimport { asyncTimeout } from './timeout';\n\n/**\n * Duplex stream for testing.\n * If this stream is piped into another stream, use `push` to send data, and `assertReceivedAsync` to assert the received data.\n */\nexport class TestStream extends Duplex {\n static async assertConnectivity(\n stream1: TestStream,\n stream2: TestStream,\n { timeout = 200 }: { timeout?: number } = {},\n ): Promise<void> {\n stream1.push('ping');\n stream2.push('pong');\n\n await Promise.all([\n stream2.assertReceivedAsync('ping', { timeout }),\n stream1.assertReceivedAsync('pong', { timeout }),\n ]);\n }\n\n private _received = Buffer.alloc(0);\n private _onWrite = new Event();\n\n override _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {\n this._received = Buffer.concat([this._received, chunk]);\n this._onWrite.emit();\n callback();\n }\n\n override _read(size: number): void {\n // noop\n }\n\n assertReceivedAsync(data: Buffer | string, { timeout = 200 }: { timeout?: number } = {}): Promise<void> {\n const dataBuffer = Buffer.isBuffer(data) ? data : Buffer.from(data);\n return asyncTimeout(\n this._onWrite.waitForCondition(() => this._received.equals(dataBuffer)),\n timeout,\n );\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAa,aAAb,cAAgC,OAAO;CACrC,aAAa,mBACX,SACA,SACA,EAAE,UAAU,QAA8B,CAAC,GAC5B;EACf,QAAQ,KAAK,MAAM;EACnB,QAAQ,KAAK,MAAM;EAEnB,MAAM,QAAQ,IAAI,CAChB,QAAQ,oBAAoB,QAAQ,EAAE,QAAQ,CAAC,GAC/C,QAAQ,oBAAoB,QAAQ,EAAE,QAAQ,CAAC,CACjD,CAAC;CACH;CAEA,YAAoB,OAAO,MAAM,CAAC;CAClC,WAAmB,IAAI,MAAM;CAE7B,OAAgB,OAAY,UAA0B,UAAgD;EACpG,KAAK,YAAY,OAAO,OAAO,CAAC,KAAK,WAAW,KAAK,CAAC;EACtD,KAAK,SAAS,KAAK;EACnB,SAAS;CACX;CAEA,MAAe,MAAoB,CAEnC;CAEA,oBAAoB,MAAuB,EAAE,UAAU,QAA8B,CAAC,GAAkB;EACtG,MAAM,aAAa,OAAO,SAAS,IAAI,IAAI,OAAO,OAAO,KAAK,IAAI;EAClE,OAAO,aACL,KAAK,SAAS,uBAAuB,KAAK,UAAU,OAAO,UAAU,CAAC,GACtE,OACF;CACF;AACF"}
@@ -12,7 +12,6 @@ export * from './persistent-lifecycle';
12
12
  export * from './push-iterable';
13
13
  export * from './stream-to-array';
14
14
  export * from './task-scheduling';
15
- export * from './test-stream';
16
15
  export * from './testing';
17
16
  export * from './timeout';
18
17
  export * from './timer';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAwB,MAAM,eAAe,CAAC;AAE9D,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C;;;;GAIG;AAGH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;IAElC,YACmB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7C;IAEJ,IAAI,SAAS,YAEZ;IAED;;OAEG;IACH,QAAQ,IAAI,IAAI,CAqBf;IAED;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAOjC;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACF;AAGD,qBAAa,SAAS;;IAQpB,YAAY,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAExC;IAED,IAAI,SAAS,YAEZ;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,CAEX;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3B;IAED,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAErC;IAED;;OAEG;IAEH,QAAQ,IAAI,IAAI,CA6Bf;IAED;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAOjC;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACF;AAED,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,IAAI,SAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,kBAMjF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,SAO3E,CAAC;AAEF,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,YAAY,MAAM,SAgBxF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAS,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,MAAM,SAqB7F,CAAC;AAEF,eAAO,MAAM,sCAAsC,QAC5C,OAAO,QACN,MAAM,OAAO,CAAC,IAAI,CAAC,mBACR,MAAM,SAwBxB,CAAC"}
1
+ {"version":3,"file":"task-scheduling.d.ts","sourceRoot":"","sources":["../../../src/task-scheduling.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAwB,MAAM,eAAe,CAAC;AAE9D,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C;;;;GAIG;AAGH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;IAElC,YACmB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7C;IAEJ,IAAI,SAAS,YAEZ;IAED;;OAEG;IACH,QAAQ,IAAI,IAAI,CAyBf;IAED;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAOjC;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACF;AAGD,qBAAa,SAAS;;IAQpB,YAAY,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAExC;IAED,IAAI,SAAS,YAEZ;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,CAEX;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAI3B;IAED,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAErC;IAED;;OAEG;IAEH,QAAQ,IAAI,IAAI,CA6Bf;IAED;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAOjC;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACF;AAED,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,IAAI,SAMxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAe,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,kBAMjF,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,SAO3E,CAAC;AAEF,eAAO,MAAM,YAAY,QAAS,OAAO,MAAM,MAAM,YAAY,CAAC,IAAI,CAAC,YAAY,MAAM,SAgBxF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAS,OAAO,QAAQ,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,MAAM,SAqB7F,CAAC;AAEF,eAAO,MAAM,sCAAsC,QAC5C,OAAO,QACN,MAAM,OAAO,CAAC,IAAI,CAAC,mBACR,MAAM,SAwBxB,CAAC"}
@@ -5,27 +5,61 @@ export type UpdateSchedulerOptions = {
5
5
  */
6
6
  maxFrequency?: number;
7
7
  };
8
+ /**
9
+ * Runs a non-reentrant callback at most once at a time, coalescing triggers (optionally rate-limited
10
+ * via {@link UpdateSchedulerOptions.maxFrequency}).
11
+ *
12
+ * The runner inside {@link trigger} is the only place the callback starts; `runBlocking` and
13
+ * `forceTrigger` funnel into it. That, plus `_scheduled` collapsing triggers into one pending
14
+ * runner, is what makes mutual exclusion hold by construction (dxos/edge#758).
15
+ */
8
16
  export declare class UpdateScheduler {
9
17
  private readonly _ctx;
10
18
  private readonly _callback;
11
19
  private readonly _params;
20
+ /** The running pass; never rejects, non-null exactly while a run is executing. */
21
+ private _currentTask;
22
+ private _scheduled;
12
23
  /**
13
- * Promise that resolves when the callback is done.
14
- * Never rejects.
24
+ * Armed by the `trigger` that schedules a runner, woken by that runner when it claims — with the
25
+ * run's promise, which `Trigger` adopts, so waiters resume when the run *finishes*. Lets
26
+ * `runBlocking` await a run that does not exist in `_currentTask` yet.
27
+ *
28
+ * `reset()` abandons parked waiters, so it may only be armed in `trigger`'s non-coalesced branch:
29
+ * waiters park only while `_scheduled` is true, and that branch requires it false.
15
30
  */
16
- private _promise;
17
- private _scheduled;
31
+ private _runOutcome;
32
+ /** Woken to make the pending runner skip its throttle delay. Replaced at claim time. */
33
+ private _skipDelay;
34
+ /**
35
+ * `runBlocking` callers in flight, incremented synchronously before triggering. A failing run uses
36
+ * it to tell whether someone will throw the error; counting microtask hops to a waiter's resume
37
+ * would be a race.
38
+ */
39
+ private _observers;
18
40
  private _lastUpdateTime;
19
41
  constructor(_ctx: Context, _callback: () => Promise<void>, _params?: UpdateSchedulerOptions);
42
+ get scheduled(): boolean;
43
+ /**
44
+ * Schedule the callback to run asynchronously. Triggers issued while a run is pending or in flight
45
+ * coalesce into the next run.
46
+ */
20
47
  trigger(): void;
48
+ /**
49
+ * Run as soon as possible: schedules (coalescing with any pending run) and skips the throttle
50
+ * delay. Does not start a concurrent callback.
51
+ */
21
52
  forceTrigger(): void;
22
53
  /**
23
54
  * Waits for the current task to finish if it is running.
24
- * Does not schedule a new task.
55
+ * Does not schedule a new task, and does not wait for one that is merely scheduled.
25
56
  */
26
57
  join(): Promise<void>;
27
58
  /**
28
- * Force schedule the task to run and wait for it to finish.
59
+ * Ensure a run that observes everything enqueued so far, and wait for it to finish. Waits for the
60
+ * pending run to claim, then for its result; that run necessarily claims after this call, so its
61
+ * drain includes everything enqueued before it. Rejects with the run's error so a flush barrier can
62
+ * see its batch was not handed off. No-op once disposed.
29
63
  */
30
64
  runBlocking(): Promise<void>;
31
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update-scheduler.d.ts","sourceRoot":"","sources":["../../../src/update-scheduler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAI7C,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAOF,qBAAa,eAAe;IAWxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAZ1B;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,eAAe,CAAgB;IAEvC,YACmB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9B,OAAO,GAAE,sBAA2B,EAKtD;IAED,OAAO,IAAI,IAAI,CAgDd;IAED,YAAY,IAAI,IAAI,CAInB;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;IAED;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAKjC;CACF"}
1
+ {"version":3,"file":"update-scheduler.d.ts","sourceRoot":"","sources":["../../../src/update-scheduler.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAK7C,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAUF;;;;;;;GAOG;AACH,qBAAa,eAAe;IA4BxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IA7B1B,kFAAkF;IAClF,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW,CAA4B;IAE/C,wFAAwF;IACxF,OAAO,CAAC,UAAU,CAAiB;IAEnC;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,eAAe,CAAgB;IAEvC,YACmB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC9B,OAAO,GAAE,sBAA2B,EAOtD;IAED,IAAI,SAAS,YAEZ;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAmEd;IAED;;;OAGG;IACH,YAAY,IAAI,IAAI,CAGnB;IAED;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;IAED;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAejC;CACF"}