npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

als-browser

v1.0.1

Published

Browser polyfill for Node.js AsyncLocalStorage

Readme

als-browser

Browser-compatible polyfill for Node.js's AsyncLocalStorage API. This package enables async context propagation in browser environments by patching common async browser APIs.

Features

  • Full AsyncLocalStorage API compatibility
  • Automatic patching of browser async APIs
  • Zero dependencies (dev dependencies only)
  • TypeScript support with full type definitions
  • ESM and CommonJS builds
  • Comprehensive test coverage

Installation

npm install als-browser
# or
pnpm add als-browser
# or
yarn add als-browser

Usage

import { AsyncLocalStorage } from 'als-browser';

// Create a storage instance
const requestContext = new AsyncLocalStorage<{ userId: string }>();

// Use run() to execute code in a context
requestContext.run({ userId: '123' }, () => {
  console.log(requestContext.getStore()); // { userId: '123' }

  // Context is preserved through setTimeout
  setTimeout(() => {
    console.log(requestContext.getStore()); // { userId: '123' }
  }, 100);
});

API

AsyncLocalStorage<T>

constructor(options?)

const store = new AsyncLocalStorage<T>({
  defaultValue?: T,  // Optional default value
  name?: string      // Optional name for debugging
});

run(data, callback, ...args)

Run a function in a new async context with the given data.

const result = store.run(myData, () => {
  // Your code here
  return store.getStore(); // Returns myData
});

getStore()

Get the current value from this store.

const currentValue = store.getStore();

enterWith(data)

Enter a new async context with the given data (no callback).

store.enterWith(myData);
console.log(store.getStore()); // myData

exit(callback, ...args)

Run a function with the store value set to undefined.

store.exit(() => {
  console.log(store.getStore()); // undefined
});

disable()

Remove this store from the current async context.

store.disable();

Static: bind(fn)

Bind a function to the current async context.

const boundFn = AsyncLocalStorage.bind(() => {
  return store.getStore();
});

Static: snapshot()

Capture the current async context and return a function that can restore it.

const snapshot = AsyncLocalStorage.snapshot();
snapshot(() => {
  // Runs in captured context
});

Manual Context Propagation

For advanced use cases like code transformers or custom async instrumentation, you can manually capture and restore async context around await points.

capture(container, promise)

Capture the current async context frame before an await and store it in a container object.

import { capture, restore, SnapshotContainer } from 'als-browser';

const container: SnapshotContainer = {};
const result = restore(container, await capture(container, promise));

restore(container, value)

Restore the async context frame after an await from the container object.

// Transform: await foo()
// Into: restore(container, await capture(container, foo()))

const container: SnapshotContainer = {};
store.run(myData, async () => {
  // Manually preserve context across await
  restore(container, await capture(container, asyncOperation()));
  console.log(store.getStore()); // myData is preserved
});

These functions are primarily useful for:

  • Code transformers/compilers that automatically instrument async functions
  • Custom async context tracking systems
  • Debugging and understanding async context flow

Note: For normal application code, prefer using the automatic patches or AsyncLocalStorage.bind()/snapshot().

Patched Browser APIs

The following browser APIs are automatically patched to preserve async context:

Timers

  • setTimeout
  • setInterval
  • setImmediate (if available)

Animation

  • requestAnimationFrame
  • requestIdleCallback

Network

  • XMLHttpRequest event handlers (addEventListener and on* properties)

How It Works

This package implements Node.js's AsyncContextFrame model adapted for browsers:

  1. AsyncContextFrame: A Map-based storage for async context, stored in a module-level variable
  2. AsyncLocalStorage: The main API that stores and retrieves values from the current frame
  3. Browser API Patches: Automatically wraps callbacks to preserve context across async boundaries

The implementation replaces Node.js's V8 embedder data APIs with a simple module-level variable, making it work in any JavaScript environment.

Limitations

  • Promise-based APIs: This package does not automatically patch promise-based APIs like fetch(). For those, you need to manually propagate context using AsyncLocalStorage.bind() or AsyncLocalStorage.snapshot().
  • EventTarget.addEventListener: Only XMLHttpRequest is patched. Other event targets may need manual context propagation.
  • Module-level state: The context is stored in a module-level variable, which means it's shared across all code in the same JavaScript realm.

Example: Request Tracing

import { AsyncLocalStorage } from 'als-browser';

const requestId = new AsyncLocalStorage<string>();

function generateId() {
  return Math.random().toString(36).slice(2);
}

function log(message: string) {
  const id = requestId.getStore() || 'no-context';
  console.log(`[${id}] ${message}`);
}

// Start a request
requestId.run(generateId(), async () => {
  log('Request started');

  // Context preserved through setTimeout
  setTimeout(() => {
    log('Async operation 1');
  }, 100);

  // Context preserved through requestAnimationFrame
  requestAnimationFrame(() => {
    log('Animation frame');
  });

  // For fetch, manually bind
  const boundHandler = AsyncLocalStorage.bind(async () => {
    const response = await fetch('/api/data');
    log('Fetch completed');
    return response.json();
  });

  await boundHandler();
});

Testing

# Run tests
pnpm test

# Build
pnpm build

# Type check
pnpm typecheck

License

MIT

Credits

This implementation is based on Node.js's AsyncLocalStorage and AsyncContextFrame APIs:

  • lib/internal/async_context_frame.js
  • lib/internal/async_local_storage/async_context_frame.js