RFC6265 Cookies and Cookie Jar for node.js
—
Pluggable storage system for cookie persistence supporting both synchronous and asynchronous operations with complete CRUD functionality and efficient indexing.
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;
}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[]>;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'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
}
}
}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);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);
});
});MemoryCookieStore Performance:
Custom Store Guidelines:
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