CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tough-cookie

RFC6265 Cookies and Cookie Jar for node.js

Pending
Overview
Eval results
Files

stores.mddocs/

Store Implementations

Pluggable storage system for cookie persistence supporting both synchronous and asynchronous operations with complete CRUD functionality and efficient indexing.

Capabilities

Store Base Class

Abstract base class defining the interface for cookie storage implementations.

/**
 * Base class for cookie storage implementations
 * Provides the interface for pluggable storage backends
 */
class Store {
  // Properties
  synchronous: boolean; // Whether store supports synchronous operations
  
  // Methods that should be implemented by subclasses (throw errors in base class)
  findCookie(
    domain: string | null, 
    path: string | null, 
    key: string | null
  ): Promise<Cookie | undefined>;
  findCookie(
    domain: string | null, 
    path: string | null, 
    key: string | null, 
    callback: Callback<Cookie | undefined>
  ): void;
  
  findCookies(
    domain: string | null, 
    path: string | null, 
    allowSpecialUseDomain?: boolean
  ): Promise<Cookie[]>;
  findCookies(
    domain: string | null, 
    path: string | null, 
    allowSpecialUseDomain?: boolean, 
    callback?: Callback<Cookie[]>
  ): void;
  
  putCookie(cookie: Cookie): Promise<void>;
  putCookie(cookie: Cookie, callback: ErrorCallback): void;
  
  updateCookie(oldCookie: Cookie, newCookie: Cookie): Promise<void>;
  updateCookie(oldCookie: Cookie, newCookie: Cookie, callback: ErrorCallback): void;
  
  removeCookie(domain: string | null, path: string | null, key: string | null): Promise<void>;
  removeCookie(domain: string | null, path: string | null, key: string | null, callback: ErrorCallback): void;
  
  removeCookies(domain: string, path: string | null): Promise<void>;
  removeCookies(domain: string, path: string | null, callback: ErrorCallback): void;
  
  removeAllCookies(): Promise<void>;
  removeAllCookies(callback: ErrorCallback): void;
  
  getAllCookies(): Promise<Cookie[]>;
  getAllCookies(callback: Callback<Cookie[]>): void;
}

Store Method Descriptions

Core CRUD operations that all store implementations must provide.

/**
 * Retrieves a specific cookie by domain, path, and key
 * @param domain - Cookie domain (null matches any domain)
 * @param path - Cookie path (null matches any path)  
 * @param key - Cookie key/name (null matches any key)
 * @returns Promise resolving to found cookie or undefined
 */
findCookie(
  domain: string | null, 
  path: string | null, 
  key: string | null
): Promise<Cookie | undefined>;

/**
 * Finds all cookies matching given domain and path
 * @param domain - Domain to match cookies against
 * @param path - Path to match cookies against (null matches all paths)
 * @param allowSpecialUseDomain - Whether to allow special use domains
 * @returns Promise resolving to array of matching cookies
 */
findCookies(
  domain: string | null, 
  path: string | null, 
  allowSpecialUseDomain?: boolean
): Promise<Cookie[]>;

/**
 * Stores a new cookie, replacing any existing cookie with same domain/path/key
 * @param cookie - Cookie to store
 * @returns Promise that resolves when storage is complete
 */
putCookie(cookie: Cookie): Promise<void>;

/**
 * Updates an existing cookie with new values
 * @param oldCookie - Existing cookie to replace
 * @param newCookie - New cookie data
 * @returns Promise that resolves when update is complete
 */
updateCookie(oldCookie: Cookie, newCookie: Cookie): Promise<void>;

/**
 * Removes a specific cookie by domain, path, and key
 * @param domain - Cookie domain (null matches any domain)
 * @param path - Cookie path (null matches any path)
 * @param key - Cookie key/name (null matches any key)
 * @returns Promise that resolves when removal is complete
 */
removeCookie(domain: string | null, path: string | null, key: string | null): Promise<void>;

/**
 * Removes cookies matching domain and path
 * @param domain - Domain to match
 * @param path - Path to match (null removes all paths in domain)
 * @returns Promise that resolves when removal is complete
 */
removeCookies(domain: string, path: string | null): Promise<void>;

/**
 * Removes all cookies from the store
 * @returns Promise that resolves when cleanup is complete
 */
removeAllCookies(): Promise<void>;

/**
 * Retrieves all cookies from the store
 * @returns Promise resolving to array of all stored cookies
 */
getAllCookies(): Promise<Cookie[]>;

MemoryCookieStore

Default in-memory store implementation with efficient indexing and synchronous operation support.

/**
 * Default in-memory cookie store implementation
 * Provides efficient storage using nested domain/path/key indexing
 */
class MemoryCookieStore extends Store {
  // Properties
  synchronous: boolean; // Always true - supports synchronous operations
  
  // Constructor
  constructor();
  
  // Implements all Store methods with in-memory storage
  findCookie(domain: string | null, path: string | null, key: string | null): Promise<Cookie | undefined>;
  findCookies(domain: string, path: string, allowSpecialUseDomain?: boolean): Promise<Cookie[]>;
  putCookie(cookie: Cookie): Promise<void>;
  updateCookie(oldCookie: Cookie, newCookie: Cookie): Promise<void>;
  removeCookie(domain: string, path: string, key: string): Promise<void>;
  removeCookies(domain: string, path: string): Promise<void>;
  removeAllCookies(): Promise<void>;
  getAllCookies(): Promise<Cookie[]>;
}

Usage Examples:

import { MemoryCookieStore, CookieJar, Cookie } from "tough-cookie";

// Create memory store
const store = new MemoryCookieStore();

// Use with CookieJar
const jar = new CookieJar(store);

// Store is synchronous
console.log(store.synchronous); // true

// Can be used directly
const cookie = new Cookie({
  key: 'session',
  value: 'abc123',
  domain: 'example.com',
  path: '/'
});

await store.putCookie(cookie);
const found = await store.findCookie('example.com', '/', 'session');
console.log(found?.value); // 'abc123'

Store Internal Structure

The memory store uses an efficient nested indexing structure for fast lookups.

/**
 * Internal structure for MemoryCookieStore indexing
 * Provides O(1) average case lookups by domain/path/key
 */
type MemoryCookieStoreIndex = {
  [domain: string]: {
    [path: string]: {
      [key: string]: Cookie
    }
  }
}

Custom Store Implementation

Creating Custom Stores

To create a custom store, extend the Store base class and implement all abstract methods.

Example: Simple File-based Store

import { Store, Cookie, Callback, ErrorCallback } from "tough-cookie";
import * as fs from 'fs/promises';
import * as path from 'path';

class FileCookieStore extends Store {
  private storePath: string;
  
  constructor(filePath: string) {
    super();
    this.synchronous = false; // Async file operations
    this.storePath = filePath;
  }
  
  async findCookie(
    domain: string | null, 
    path: string | null, 
    key: string | null
  ): Promise<Cookie | undefined> {
    const cookies = await this.getAllCookies();
    return cookies.find(cookie => 
      (domain === null || cookie.domain === domain) &&
      (path === null || cookie.path === path) &&
      (key === null || cookie.key === key)
    );
  }
  
  async findCookies(
    domain: string, 
    path: string, 
    allowSpecialUseDomain?: boolean
  ): Promise<Cookie[]> {
    const cookies = await this.getAllCookies();
    return cookies.filter(cookie => 
      cookie.domain === domain && 
      (cookie.path?.startsWith(path) || false)
    );
  }
  
  async putCookie(cookie: Cookie): Promise<void> {
    const cookies = await this.getAllCookies();
    
    // Remove existing cookie with same domain/path/key
    const filtered = cookies.filter(c => 
      !(c.domain === cookie.domain && 
        c.path === cookie.path && 
        c.key === cookie.key)
    );
    
    filtered.push(cookie);
    await this.saveAllCookies(filtered);
  }
  
  async updateCookie(oldCookie: Cookie, newCookie: Cookie): Promise<void> {
    const cookies = await this.getAllCookies();
    const index = cookies.findIndex(c => 
      c.domain === oldCookie.domain && 
      c.path === oldCookie.path && 
      c.key === oldCookie.key
    );
    
    if (index !== -1) {
      cookies[index] = newCookie;
      await this.saveAllCookies(cookies);
    }
  }
  
  async removeCookie(domain: string, path: string, key: string): Promise<void> {
    const cookies = await this.getAllCookies();
    const filtered = cookies.filter(c => 
      !(c.domain === domain && c.path === path && c.key === key)
    );
    await this.saveAllCookies(filtered);
  }
  
  async removeCookies(domain: string, path: string): Promise<void> {
    const cookies = await this.getAllCookies();
    const filtered = cookies.filter(c => 
      !(c.domain === domain && c.path === path)
    );
    await this.saveAllCookies(filtered);
  }
  
  async removeAllCookies(): Promise<void> {
    await this.saveAllCookies([]);
  }
  
  async getAllCookies(): Promise<Cookie[]> {
    try {
      const data = await fs.readFile(this.storePath, 'utf8');
      const parsed = JSON.parse(data);
      return parsed.map((cookieData: any) => Cookie.fromJSON(cookieData)).filter(Boolean);
    } catch (error) {
      if ((error as any).code === 'ENOENT') {
        return []; // File doesn't exist yet
      }
      throw error;
    }
  }
  
  private async saveAllCookies(cookies: Cookie[]): Promise<void> {
    const serialized = cookies.map(cookie => cookie.toJSON());
    await fs.mkdir(path.dirname(this.storePath), { recursive: true });
    await fs.writeFile(this.storePath, JSON.stringify(serialized, null, 2));
  }
}

// Usage
const fileStore = new FileCookieStore('./cookies.json');
const jar = new CookieJar(fileStore);

Store Callback Pattern

All store methods support both Promise and callback patterns for compatibility.

import { MemoryCookieStore, Cookie } from "tough-cookie";

const store = new MemoryCookieStore();
const cookie = new Cookie({ key: 'test', value: 'value' });

// Promise-based usage
await store.putCookie(cookie);
const found = await store.findCookie('example.com', '/', 'test');

// Callback-based usage
store.putCookie(cookie, (error) => {
  if (error) {
    console.error('Failed to store cookie:', error);
    return;
  }
  
  store.findCookie('example.com', '/', 'test', (error, result) => {
    if (error) {
      console.error('Failed to find cookie:', error);
      return;
    }
    console.log('Found cookie:', result);
  });
});

Store Performance Considerations

MemoryCookieStore Performance:

  • O(1) average case lookup by exact domain/path/key
  • O(n) worst case for domain/path matching operations
  • Efficient for applications with moderate cookie counts
  • Memory usage grows linearly with number of stored cookies

Custom Store Guidelines:

  • Implement efficient indexing for your storage backend
  • Consider async operations for I/O bound stores (files, databases)
  • Handle concurrent access appropriately
  • Implement proper error handling for storage failures
  • Consider cleanup of expired cookies in background operations

Types

interface Callback<T> {
  (error: Error, result?: never): void;
  (error: null, result: T): void;
}

interface ErrorCallback {
  (error: Error | null): void;
}

Install with Tessl CLI

npx tessl i tessl/npm-tough-cookie

docs

cookie-jar.md

cookie-operations.md

index.md

stores.md

utilities.md

tile.json