or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

Continuation Local Storage

Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads. It allows developers to set and get values that are scoped to the lifetime of chains of function calls, making it particularly useful for maintaining context across asynchronous operations without explicitly passing values through callback parameters.

Package Information

  • Package Name: continuation-local-storage
  • Package Type: npm
  • Language: JavaScript (Node.js)
  • Installation: npm install continuation-local-storage

Core Imports

const cls = require('continuation-local-storage');
// or
const { createNamespace, getNamespace, destroyNamespace, reset } = require('continuation-local-storage');

Basic Usage

const cls = require('continuation-local-storage');

// Create a namespace
const session = cls.createNamespace('my-session');

// Use in callback chains
function handleRequest(req, res) {
  session.run(function() {
    // Set values in the context
    session.set('user', { id: 123, name: 'Alice' });
    session.set('requestId', req.headers['x-request-id']);
    
    // Call other functions - they inherit the context
    processRequest();
  });
}

function processRequest() {
  // Retrieve values from context - no need to pass as parameters
  const user = session.get('user');
  const requestId = session.get('requestId');
  
  console.log(`Processing request ${requestId} for user ${user.name}`);
}

Architecture

Continuation-local storage is built around several key concepts:

  • Namespaces: Application-specific contexts that group related values
  • Contexts: Plain objects that store key-value pairs within a namespace
  • Async Listener Integration: Uses Node.js async listeners to propagate context across asynchronous boundaries
  • Context Inheritance: Child contexts inherit from parent contexts, allowing nested scoping
  • EventEmitter Binding: Special support for binding EventEmitters to namespace contexts

Capabilities

Namespace Management

Core functions for creating, retrieving, and managing continuation-local storage namespaces.

/**
 * Create a new namespace for continuation-local storage
 * @param name - Unique name for the namespace
 * @returns Namespace instance
 * @throws Error if name is not provided
 */
function createNamespace(name: string): Namespace;

/**
 * Look up an existing namespace by name
 * @param name - Name of the namespace to retrieve
 * @returns Namespace instance or undefined if not found
 */
function getNamespace(name: string): Namespace | undefined;

/**
 * Dispose of an existing namespace and remove async listeners
 * @param name - Name of the namespace to destroy
 * @throws Error if namespace doesn't exist or has no async listener ID
 */
function destroyNamespace(name: string): void;

/**
 * Completely reset all continuation-local storage namespaces
 * WARNING: Existing namespace references will no longer propagate context
 */
function reset(): void;

Context Operations

Methods for setting and getting values within a namespace context.

/**
 * Set a value on the current continuation context
 * Must be called within an active context created by run() or bind()
 * @param key - Key to store the value under
 * @param value - Value to store
 * @returns The stored value
 * @throws Error if no context is available
 */
Namespace.prototype.set(key: string, value: any): any;

/**
 * Look up a value on the current continuation context
 * Recursively searches from innermost to outermost nested context
 * @param key - Key to retrieve the value for
 * @returns The stored value or undefined if not found
 */
Namespace.prototype.get(key: string): any;

Context Execution

Methods for creating and running code within namespace contexts.

/**
 * Create a new context and run function within its scope
 * All functions called from the callback inherit this context
 * @param fn - Callback function that receives the context as argument
 * @returns The context object that was created
 * @throws Error if exception occurs in callback (context attached to exception)
 */
Namespace.prototype.run(fn: (context: object) => void): object;

/**
 * Same as run() but returns the return value of callback instead of context
 * @param fn - Callback function that receives the context as argument
 * @returns The return value of the callback function
 */
Namespace.prototype.runAndReturn(fn: (context: object) => any): any;

/**
 * Bind a function to the namespace context for deferred execution
 * @param fn - Function to bind to the context
 * @param context - Optional context to bind to (defaults to active or new context)
 * @returns Wrapped function that runs in the bound context
 */
Namespace.prototype.bind(fn: Function, context?: object): Function;

Context Management

Advanced methods for creating and managing contexts directly.

/**
 * Create a new context cloned from the currently active context
 * Useful with bind() for fresh context at invocation time vs binding time
 * @returns Context object using active context as prototype
 */
Namespace.prototype.createContext(): object;

/**
 * Enter a specific context (internal method)
 * @param context - Context object to enter
 * @throws Error if context is not provided
 */
Namespace.prototype.enter(context: object): void;

/**
 * Exit a specific context (internal method)
 * @param context - Context object to exit
 * @throws Error if context not provided or not found in context stack
 */
Namespace.prototype.exit(context: object): void;

EventEmitter Integration

Methods for binding EventEmitters to namespace contexts.

/**
 * Bind an EventEmitter to the namespace
 * Similar to domain.add but for continuation-local storage
 * @param emitter - EventEmitter instance to bind
 * @throws Error if emitter doesn't have required methods (on, addListener, emit)
 */
Namespace.prototype.bindEmitter(emitter: EventEmitter): void;

Usage Example:

const http = require('http');
const cls = require('continuation-local-storage');

const session = cls.createNamespace('http-session');

http.createServer(function(req, res) {
  session.run(function() {
    // Bind request and response to the session context
    session.bindEmitter(req);
    session.bindEmitter(res);
    
    session.set('startTime', Date.now());
    
    // Event handlers will maintain context
    req.on('data', function(chunk) {
      const startTime = session.get('startTime');
      console.log(`Received data ${Date.now() - startTime}ms after start`);
    });
    
    // Continue with request handling...
  });
});

Error Handling

Methods for extracting context information from errors.

/**
 * Extract context from an error that was thrown within a namespace
 * Errors thrown in namespace contexts have the context attached
 * @param exception - Error object that may have attached context
 * @returns Context object or undefined if no context attached
 */
Namespace.prototype.fromException(exception: Error): object | undefined;

Global State

Process Namespaces

/**
 * Dictionary of all active Namespace objects, keyed by name
 * Available globally after the module is first loaded
 * @type {Object.<string, Namespace>}
 */
process.namespaces: { [name: string]: Namespace };

Types

/**
 * Namespace class representing an application-specific context
 */
class Namespace {
  /** The name of the namespace */
  name: string;
  
  /** The currently active context in the namespace */
  active: object | null;
  
  /** Async listener ID assigned by process.addAsyncListener */
  id: number;
  
  /** Internal context stack for enter/exit operations */
  _set: object[];
}

Error Conditions

  • No active context: Calling set() or get() outside of run() or bind() throws an error
  • Missing namespace name: createNamespace() without a name throws an error
  • Invalid emitter: bindEmitter() with object lacking required methods throws an error
  • Context stack errors: Improper enter()/exit() calls throw assertion errors
  • Nonexistent namespace: destroyNamespace() on non-existent namespace throws an error

Common Patterns

Request Context in Web Applications:

const session = cls.createNamespace('request');

app.use(function(req, res, next) {
  session.run(function() {
    session.set('requestId', req.id);
    session.set('userId', req.user?.id);
    next();
  });
});

// Later in any middleware or route handler
function logActivity(action) {
  const requestId = session.get('requestId');
  const userId = session.get('userId');
  console.log(`User ${userId} performed ${action} in request ${requestId}`);
}

Nested Contexts:

const tracer = cls.createNamespace('tracer');

tracer.run(function() {
  tracer.set('operation', 'parent');
  
  tracer.run(function() {
    tracer.set('operation', 'child');
    console.log(tracer.get('operation')); // 'child'
  });
  
  console.log(tracer.get('operation')); // 'parent'
});