@rupertsworld/disposable
v0.2.0
Published
Disposable base class and mixin: idempotent dispose() with Symbol.dispose support
Readme
@rupertsworld/disposable
A Disposable base class and mixin for objects that own resources: dispose() is made idempotent (the second call is a no-op), a protected disposed flag tracks state, and Symbol.dispose is implemented so instances work with using declarations.
npm install @rupertsworld/disposableimport { Disposable, withDisposable } from "@rupertsworld/disposable";
class Watcher extends Disposable {
#handle = fs.watch(path, onChange);
override dispose() { this.#handle.close(); }
}
const w = new Watcher();
w.dispose();
w.dispose(); // no-op
// or with `using`:
{
using w2 = new Watcher();
} // disposed at block exitDisposable— abstract base. Overridedispose()(sync or async); the base wraps it so repeated calls run it once, exposesprotected get disposed, and implements[Symbol.dispose].withDisposable(Base)— the same behavior as a mixin, for classes that already extend something. The subclass must implementdispose().DisposableLike— the interface both satisfy:dispose(): void | Promise<void>plus[Symbol.dispose]().@rupertsworld/disposable/polyfill— side-effect import that installsSymbol.dispose/DisposableStackglobals (viadisposablestack) for runtimes that lack them. Import it once at your entry point if needed.
[Symbol.dispose] does not await an async dispose() — using is synchronous disposal; await dispose() yourself when cleanup must complete before continuing.
Changes in 0.2.0
Replaces the 2026-03 0.1.x package (a Disposable interface plus DisposableGroup) with the base-class/mixin design above. For grouped cleanup, use the standard DisposableStack (the /polyfill subpath provides it where missing).
