Concise, clear polyfills for the TC39 Explicit Resource Management proposal, providing DisposableStack, AsyncDisposableStack, and SuppressedError.
npm install @jsxtools/explicit-resource-managementimport { DisposableStack, AsyncDisposableStack, SuppressedError } from "@jsxtools/explicit-resource-management";
// Sync disposal
const stack = new DisposableStack();
const file = stack.use(openFile("example.txt"));
stack.defer(() => console.log("Cleanup complete"));
// Resources are disposed in reverse order
stack.dispose();
// Async disposal
const asyncStack = new AsyncDisposableStack();
const connection = asyncStack.use(await openConnection());
asyncStack.defer(async () => console.log("Async cleanup complete"));
await asyncStack.disposeAsync();A stack-based container for synchronous disposable resources.
use<T>(value: T): T- Adds a disposable resource (withSymbol.dispose) to the stackadopt<T>(value: T, onDispose: (value: T) => void): T- Adds a non-disposable resource with a cleanup callbackdefer(onDispose: () => void): void- Adds a cleanup callback to be called on disposalmove(): DisposableStack- Moves all resources to a new stack and marks this one as disposeddispose(): void- Disposes all resources in reverse order
disposed: boolean- Whether the stack has been disposed
A stack-based container for asynchronous disposable resources.
use<T>(value: T): T- Adds a disposable resource (withSymbol.asyncDisposeorSymbol.dispose)adopt<T>(value: T, onDisposeAsync: (value: T) => Promise<void>): T- Adds a resource with async cleanupdefer(onDisposeAsync: () => Promise<void>): void- Adds an async cleanup callbackmove(): AsyncDisposableStack- Moves all resources to a new stackdisposeAsync(): Promise<void>- Disposes all resources asynchronously in reverse order
disposed: boolean- Whether the stack has been disposed
An error type used to chain multiple errors that occur during resource disposal.
const error = new SuppressedError(new Error("disposal error"), new Error("suppressed error"), "optional message");
console.log(error.error); // Error: disposal error
console.log(error.suppressed); // Error: suppressed error- ✅ Spec-compliant implementation of
DisposableStackandAsyncDisposableStack - ✅ Proper error aggregation with
SuppressedError - ✅ TypeScript support with full type definitions
- ✅ Zero dependencies (assumes
Symbol.disposeandSymbol.asyncDisposeexist) - ✅ Concise, readable implementation (~230 lines total)
This polyfill assumes that Symbol.dispose and Symbol.asyncDispose are already available in your environment. If they are not, you'll need to polyfill them first:
Symbol.dispose ??= Symbol("Symbol.dispose");
Symbol.asyncDispose ??= Symbol("Symbol.asyncDispose");MIT-0